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
JavaScript
"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