UNPKG

gl-react

Version:

Universal React library, write and compose WebGL shaders, implement complex effects using a descriptive paradigm

396 lines (394 loc) 12.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.list = exports.default = void 0; var _invariant = _interopRequireDefault(require("invariant")); var _react = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _glShader = _interopRequireDefault(require("gl-shader")); var _Shaders = _interopRequireDefault(require("./Shaders")); var _Visitors = _interopRequireDefault(require("./Visitors")); var _webgltextureLoader = require("webgltexture-loader"); var _GLContext = _interopRequireDefault(require("./GLContext")); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } const __DEV__ = process.env.NODE_ENV === "development"; const SurfacePropTypes = { children: _propTypes.default.any.isRequired, style: _propTypes.default.any, preload: _propTypes.default.array, onLoad: _propTypes.default.func, onLoadError: _propTypes.default.func, onContextLost: _propTypes.default.func, onContextRestored: _propTypes.default.func, visitor: _propTypes.default.object }; let surfaceId = 0; const _instances = []; const list = () => _instances.slice(0); exports.list = list; const allSurfaceProps = Object.keys(SurfacePropTypes); var _default = ({ GLView, RenderLessElement, mapRenderableContent, requestFrame, cancelFrame }) => { return class Surface extends _react.Component { id = ++surfaceId; gl = null; buffer; loaderResolver = null; glView; root = null; shaders = {}; _preparingGL = []; _needsRedraw = false; state = { ready: false, rebootId: 0, debug: false }; RenderLessElement = RenderLessElement; mapRenderableContent = mapRenderableContent; static propTypes = SurfacePropTypes; componentDidMount() { _instances.push(this); this.getVisitors().forEach(v => v.onSurfaceMount(this)); } componentWillUnmount() { this._stopLoop(); this._destroyGL(); const i = _instances.indexOf(this); if (i !== -1) _instances.splice(i, 1); this.getVisitors().forEach(v => v.onSurfaceUnmount(this)); } componentDidUpdate() { this.redraw(); } render() { const { props, state: { ready, rebootId, debug } } = this; const { children, style } = props; // We allow to pass-in all props we don't know so you can hook to DOM events. const rest = {}; Object.keys(props).forEach(key => { if (allSurfaceProps.indexOf(key) === -1) { rest[key] = props[key]; } }); return /*#__PURE__*/_react.default.createElement(_GLContext.default.Provider, { value: { glParent: this, glSurface: this, glSizable: this } }, /*#__PURE__*/_react.default.createElement(GLView, _extends({ key: rebootId, debug: debug, ref: this._onRef, onContextCreate: this._onContextCreate, onContextFailure: this._onContextFailure, onContextLost: this._onContextLost, onContextRestored: this._onContextRestored, style: style }, rest), ready ? children : null)); } rebootForDebug() { this._stopLoop(); this._destroyGL(); this.setState(({ rebootId }) => ({ rebootId: rebootId + 1, ready: false, debug: true })); } getVisitors() { return _Visitors.default.get().concat(this.props.visitor || []); } getGLSize() { const { gl } = this; return [gl ? gl.drawingBufferWidth : 0, gl ? gl.drawingBufferHeight : 0]; } getGLName() { return `Surface#${this.id}`; } getGLShortName() { return "Surface"; } captureAsDataURL(...args) { const { glView } = this; (0, _invariant.default)(glView, "GLView is mounted"); (0, _invariant.default)(glView.captureAsDataURL, "captureAsDataURL is not defined in %s", GLView.displayName || GLView.name); return glView.captureAsDataURL(...args); } captureAsBlob(...args) { const { glView } = this; (0, _invariant.default)(glView, "GLView is mounted"); (0, _invariant.default)(glView.captureAsBlob, "captureAsBlob is not defined in %s", GLView.displayName || GLView.name); return glView.captureAsBlob(...args); } capture(x, y, w, h) { (0, _invariant.default)(this.root, "Surface#capture: surface is not yet ready or don't have any root Node"); return this.root.capture(x, y, w, h); } redraw = () => { this._needsRedraw = true; }; flush = () => { this._draw(); }; glIsAvailable() { return !!this.gl; } _emptyTexture = null; getEmptyTexture() { let { gl, _emptyTexture } = this; (0, _invariant.default)(gl, "getEmptyTexture called while gl was not defined"); if (!_emptyTexture) { this._emptyTexture = _emptyTexture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, _emptyTexture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])); } return _emptyTexture; } _onContextCreate = gl => { const onSuccess = () => { this.setState({ ready: true }, () => { try { this._handleLoad(); } catch (e) { this._handleError(e); } }); }; this._prepareGL(gl, onSuccess, this._handleError); }; _onContextFailure = e => { this._handleError(e); }; _onContextLost = () => { if (this.props.onContextLost) this.props.onContextLost(); this._stopLoop(); this._destroyGL(); if (this.root) this.root._onContextLost(); }; _onContextRestored = gl => { if (this.root) this.root._onContextRestored(gl); this._prepareGL(gl, this._handleRestoredSuccess, this._handleRestoredFailure); }; _destroyGL() { const { gl } = this; if (gl) { this.gl = null; if (this._emptyTexture) { gl.deleteTexture(this._emptyTexture); this._emptyTexture = null; } if (this.loaderResolver) { this.loaderResolver.dispose(); } for (let k in this.shaders) { this.shaders[k].dispose(); } this.shaders = {}; gl.deleteBuffer(this.buffer); this.getVisitors().map(v => v.onSurfaceGLContextChange(this, null)); } } _prepareGL(gl, onSuccess, onError) { this.gl = gl; this.getVisitors().map(v => v.onSurfaceGLContextChange(this, gl)); this.loaderResolver = new _webgltextureLoader.LoaderResolver(gl); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); const buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, -1, 4, 4, -1]), // see a-big-triangle gl.STATIC_DRAW); this.buffer = buffer; const { preload } = this.props; const all = []; (preload || []).forEach(raw => { if (!raw) { console.warn("Can't preload value", raw); return; } const { loader, input } = this._resolveTextureLoader(raw); if (!loader) { console.warn("Can't preload input", raw, input); return; } const loadedAlready = loader.get(input); if (loadedAlready) return; all.push(loader.load(input)); }); this._preparingGL = all; if (all.length > 0) { Promise.all(all).then(onSuccess, onError); } else { onSuccess(); } } _onRef = ref => { this.glView = ref; }; _addGLNodeChild(node) { (0, _invariant.default)(!this.root, "Surface can only contains a single root. Got: %s", this.root && this.root.getGLName()); this.root = node; node._addDependent(this); this.redraw(); } _removeGLNodeChild(node) { this.root = null; this.redraw(); } _handleError = e => { const { onLoadError } = this.props; if (onLoadError) onLoadError(e);else { console.error(e); } }; _handleRestoredFailure = () => { // there is nothing we can do. it's a dead end. }; _handleRestoredSuccess = () => { this.redraw(); this.flush(); this._startLoop(); if (this.props.onContextRestored) this.props.onContextRestored(); }; _handleLoad = () => { if (!this.root) { console.warn(this.getGLName() + " children does not contain any discoverable Node"); } const { onLoad } = this.props; this.redraw(); this.flush(); this._startLoop(); if (onLoad) onLoad(); }; _resolveTextureLoader(raw) { let input = raw; let loader = this.loaderResolver && this.loaderResolver.resolve(input) || null; return { loader, input }; } _makeShader({ frag, vert }, name) { const { gl } = this; (0, _invariant.default)(gl, "gl is not available"); const shader = (0, _glShader.default)(gl, vert, frag); for (let key in shader.attributes) { shader.attributes[key].pointer(); } return shader; } _getShader(shaderId) { const { shaders } = this; return shaders[shaderId.id] || (shaders[shaderId.id] = this._makeShader(_Shaders.default.get(shaderId), _Shaders.default.getName(shaderId))); } _bindRootNode() { const { gl } = this; (0, _invariant.default)(gl, "gl context not available"); gl.bindFramebuffer(gl.FRAMEBUFFER, null); const [width, height] = this.getGLSize(); gl.viewport(0, 0, width, height); } _loopRaf; _startLoop() { cancelFrame(this._loopRaf); const loop = () => { this._loopRaf = requestFrame(loop); if (this._needsRedraw) this._draw(); }; this._loopRaf = requestFrame(loop); } _stopLoop() { cancelFrame(this._loopRaf); } _draw() { const { gl, root, glView } = this; (0, _invariant.default)(glView, "GLView is mounted"); const visitors = this.getVisitors(); if (!gl || !root || !this._needsRedraw) { visitors.forEach(v => v.onSurfaceDrawSkipped(this)); return; } this._needsRedraw = false; visitors.forEach(v => v.onSurfaceDrawStart(this)); if (glView.beforeDraw) glView.beforeDraw(gl); try { root._draw(); } catch (e) { let silent = false; visitors.forEach(v => { silent = v.onSurfaceDrawError(e) || silent; }); if (!silent) { if (__DEV__ && glView.debugError && e.longMessage /* duck typing an "interesting" GLError (from lib gl-shader) */) { glView.debugError(e); } else { console.warn(e); throw e; } } return; } if (glView.afterDraw) glView.afterDraw(gl); visitors.forEach(v => v.onSurfaceDrawEnd(this)); } }; }; exports.default = _default; //# sourceMappingURL=createSurface.js.map