gl-react
Version:
Universal React library, write and compose WebGL shaders, implement complex effects using a descriptive paradigm
781 lines (772 loc) • 26.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _invariant = _interopRequireDefault(require("invariant"));
var _react = _interopRequireWildcard(require("react"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _typedarrayPool = _interopRequireDefault(require("typedarray-pool"));
var _ndarray = _interopRequireDefault(require("ndarray"));
var _Uniform = _interopRequireDefault(require("./Uniform"));
var _Bus = _interopRequireDefault(require("./Bus"));
var _Shaders = _interopRequireWildcard(require("./Shaders"));
var _invariantNoDependentsLoop = _interopRequireDefault(require("./helpers/invariantNoDependentsLoop"));
var _genId = _interopRequireDefault(require("./genId"));
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 }; }
const blendFuncAliases = {
zero: "ZERO",
one: "ONE",
"src color": "SRC_COLOR",
"one minus src color": "ONE_MINUS_SRC_COLOR",
"src alpha": "SRC_ALPHA",
"one minus src alpha": "ONE_MINUS_SRC_ALPHA",
"dst color": "DST_COLOR",
"one minus dst color": "ONE_MINUS_DST_COLOR",
"dst alpha": "DST_ALPHA",
"one minus dst alpha": "ONE_MINUS_DST_ALPHA",
"constant color": "CONSTANT_COLOR",
"one minus constant color": "ONE_MINUS_CONSTANT_COLOR",
"constant alpha": "CONSTANT_ALPHA",
"one minus constant alpha": "ONE_MINUS_CONSTANT_ALPHA",
"src alpha saturate": "SRC_ALPHA_SATURATE"
};
const isBackbuffer = obj => {
if (obj === "Backbuffer") {
console.warn('Backbuffer is deprecated, use Uniform.Backbuffer instead: `import {Uniform} from "gl-react"`');
return true;
}
return obj === _Uniform.default.Backbuffer;
};
const isBackbufferFrom = obj => obj && typeof obj === "object" && obj.type === "BackbufferFrom";
const isTextureSizeGetter = obj => obj && typeof obj === "object" && obj.type === "TextureSize";
const nodeWidthHeight = ({
width,
height
}, {
glSizable
}) => {
if (width && height) return [width, height];
const [cw, ch] = glSizable.getGLSize();
return [width || cw, height || ch];
};
const mapBlendFunc = (gl, name) => {
if (name in gl) return gl[name];
if (name in blendFuncAliases) {
const id = blendFuncAliases[name];
if (id in gl) return gl[id];
}
console.warn("Invalid blendFunc. Got:", name);
};
const parseWrap = (gl, w) => {
switch (w) {
case "clamp to edge":
return gl.CLAMP_TO_EDGE;
case "repeat":
return gl.REPEAT;
case "mirrored repeat":
return gl.MIRRORED_REPEAT;
default:
console.warn("Invalid wrap. Got:", w);
return gl.CLAMP_TO_EDGE;
}
};
const mergeArrays = (a, b) => {
const t = [];
const length = Math.max(a.length, b.length);
for (let i = 0; i < length; i++) {
t[i] = b[i] || a[i];
}
return t;
};
const parseInterpolation = (gl, i) => {
switch (i) {
case "linear":
return gl.LINEAR;
case "nearest":
return gl.NEAREST;
default:
console.warn("Invalid interpolation. Got:", i);
return gl.LINEAR;
}
};
// minimal version of gl-fbo
const createFBO = (gl, width, height) => {
var handle = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, handle);
var color = gl.createTexture();
if (!color) throw new Error("createTexture returned null");
gl.bindTexture(gl.TEXTURE_2D, color);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, color, 0);
return {
handle,
color,
bind: () => {
gl.bindFramebuffer(gl.FRAMEBUFFER, handle);
gl.viewport(0, 0, width, height);
},
syncSize: (w, h) => {
if (w !== width || h !== height) {
width = w;
height = h;
gl.bindTexture(gl.TEXTURE_2D, color);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
}
},
dispose: () => {
gl.deleteFramebuffer(handle);
gl.deleteTexture(color);
}
};
};
const defaultTextureOptions = {
interpolation: "linear",
wrap: ["clamp to edge", "clamp to edge"]
};
const applyTextureOptions = (gl, partialOpts) => {
const opts = {
...defaultTextureOptions,
...partialOpts
};
let filter = parseInterpolation(gl, opts.interpolation);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);
let wrapS, wrapT;
if (Array.isArray(opts.wrap)) {
if (opts.wrap.length !== 2) {
console.warn("textureOptions wrap: should be an array of 2 values. Got:", opts.wrap);
wrapS = wrapT = gl.CLAMP_TO_EDGE;
} else {
wrapS = parseWrap(gl, opts.wrap[0]);
wrapT = parseWrap(gl, opts.wrap[1]);
}
} else {
wrapS = wrapT = parseWrap(gl, opts.wrap);
}
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT);
};
const NodePropTypes = {
shader: _propTypes.default.object.isRequired,
uniformsOptions: _propTypes.default.object,
uniforms: _propTypes.default.object,
ignoreUnusedUniforms: _propTypes.default.any,
sync: _propTypes.default.bool,
width: _propTypes.default.number,
height: _propTypes.default.number,
children: _propTypes.default.any,
backbuffering: _propTypes.default.bool,
blendFunc: _propTypes.default.object,
clear: _propTypes.default.object,
onDraw: _propTypes.default.func
};
/**
* `<Node>` is the primitive that renders a shader program into a Framebuffer.
* It can be composed with other `Node` via using a sampler2D uniforms.
*/
class Node extends _react.Component {
drawProps = this.props;
framebuffer;
backbuffer;
_needsRedraw = false;
capturePixelsArray;
id = (0, _genId.default)();
uniformsBus = {};
dependencies = []; // Node this instance depends on
dependents = []; // Node/Surface that depends on this instance
static propTypes = NodePropTypes;
static defaultProps = {
uniformsOptions: {},
uniforms: {},
blendFunc: {
src: "src alpha",
dst: "one minus src alpha"
},
clear: {
color: [0, 0, 0, 0]
}
};
static contextType = _GLContext.default;
componentDidMount() {
const {
glSurface: {
gl
}
} = this.context;
if (gl) this._prepareGLObjects(gl);
this.context.glParent._addGLNodeChild(this);
this.redraw();
if (this.props.sync) this.flush();
}
componentWillUnmount() {
const {
capturePixelsArray
} = this;
this._destroyGLObjects();
if (capturePixelsArray) {
_typedarrayPool.default.freeUint8(capturePixelsArray);
}
this._needsRedraw = false;
this.context.glParent._removeGLNodeChild(this);
this.dependencies.forEach(d => d._removeDependent(this));
}
_syncNextDrawProps(nextProps, nextContext) {
const nextWidthHeight = nodeWidthHeight(nextProps, nextContext);
if (this.framebuffer) {
this.framebuffer.syncSize(...nextWidthHeight);
}
if (this.backbuffer) {
this.backbuffer.syncSize(...nextWidthHeight);
}
(0, _invariant.default)(nextProps.backbuffering === this.drawProps.backbuffering, "Node backbuffering prop must not changed. (not yet supported)");
this.drawProps = nextProps;
}
_resolveElement = (uniform, value, index) => {
if (! /*#__PURE__*/_react.default.isValidElement(value)) {
if (typeof value === "function") {
value = value(this.redraw);
if (! /*#__PURE__*/_react.default.isValidElement(value)) {
return; // the function don't return an Element, skip
}
} else {
return; // the value isn't an Element, skip
}
}
return /*#__PURE__*/_react.default.createElement(_Bus.default, {
key: uniform + (index ? "." + index : ""),
uniform: uniform,
index: index
}, value);
};
_renderUniformElement = key => {
const {
uniforms
} = this.props;
let value = uniforms[key];
return Array.isArray(value) ? value.map((v, i) => this._resolveElement(key, v, i)) : this._resolveElement(key, value, 0);
};
render() {
const {
children,
uniforms
} = this.props;
const {
glSurface: {
RenderLessElement
}
} = this.context;
return /*#__PURE__*/_react.default.createElement(_GLContext.default.Provider, {
value: {
glParent: this,
glSurface: this.context.glSurface,
glSizable: this
}
}, /*#__PURE__*/_react.default.createElement(RenderLessElement, null, children, Object.keys(uniforms).map(this._renderUniformElement)));
}
componentDidUpdate() {
this._syncNextDrawProps(this.props, this.context);
this.redraw();
if (this.props.sync) this.flush();
}
getGLShortName() {
const {
shader
} = this.drawProps;
const shaderName = (0, _Shaders.isShaderIdentifier)(shader) ? _Shaders.default.getShortName(shader) : "<inline>";
return `Node(${shaderName})`;
}
getGLName() {
const {
shader
} = this.drawProps;
const shaderName = (0, _Shaders.isShaderIdentifier)(shader) ? _Shaders.default.getName(shader) : "<inline>";
return `Node#${this.id}(${shaderName})`;
}
getGLSize() {
return nodeWidthHeight(this.drawProps, this.context);
}
getGLOutput() {
const {
framebuffer
} = this;
(0, _invariant.default)(framebuffer, "Node#getGLOutput: framebuffer is not defined. It cannot be called on a root Node");
return framebuffer.color;
}
getGLBackbufferOutput() {
const {
backbuffer
} = this;
(0, _invariant.default)(backbuffer, "Node#getGLBackbufferOutput: backbuffer is not defined. Make sure `backbuffering` prop is defined");
return backbuffer.color;
}
/**
* Imperatively set the props with a partial subset of props to apply.
*/
setDrawProps(patch) {
const nextProps = {
...this.drawProps,
...patch
};
this._syncNextDrawProps(nextProps, this.context);
this.redraw();
if (nextProps.sync) this.flush();
}
/**
* Capture the node pixels.
*/
capture(x, y, w, h) {
const [width, height] = this.getGLSize();
const {
gl
} = this.context.glSurface;
(0, _invariant.default)(gl, "gl is no longer available");
if (x === undefined) x = 0;
if (y === undefined) y = 0;
if (w === undefined) w = width - x;
if (h === undefined) h = height - y;
(0, _invariant.default)(x >= 0 && x + w <= width && y >= 0 && y + h <= height, "capture(%s,%s,%s,%s): requested rectangle is out of bounds (%s,%s)", x, y, w, h, width, height);
const size = w * h * 4;
const pixels = this._captureAlloc(size);
this._bind();
gl.readPixels(x, y, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
return (0, _ndarray.default)(pixels, [h, w, 4]).step(-1, 1, 1).transpose(1, 0, 2);
}
/**
* Schedule a redraw of this node and all dependent nodes.
*/
redraw = () => {
if (!this._needsRedraw) {
this._needsRedraw = true;
this.dependents.forEach(d => d.redraw());
}
};
/**
* Force the redraw (if any) to happen now, synchronously.
*/
flush = () => {
this.context.glSurface._draw();
};
_destroyGLObjects() {
const {
glSurface
} = this.context;
if (glSurface.glIsAvailable()) {
const {
framebuffer,
backbuffer,
_shader
} = this;
if (_shader) {
_shader.dispose();
}
if (framebuffer) {
framebuffer.dispose();
}
if (backbuffer) {
backbuffer.dispose();
}
}
this._shader = undefined;
this.framebuffer = undefined;
this.backbuffer = undefined;
}
_prepareGLObjects(gl) {
const [width, height] = this.getGLSize();
const {
glParent,
glSurface
} = this.context;
if (glParent === glSurface) {
(0, _invariant.default)(!this.drawProps.backbuffering, "`backbuffering` is currently not supported for a Root Node. " + "Try to wrap %s in a <LinearCopy> or <NearestCopy>.", this.getGLName());
} else {
const fbo = createFBO(gl, width, height);
this.framebuffer = fbo;
if (this.drawProps.backbuffering) {
const fbo = createFBO(gl, width, height);
this.backbuffer = fbo;
}
}
}
_onContextLost() {
this.dependencies.forEach(d => d._onContextLost());
this._destroyGLObjects();
}
_onContextRestored(gl) {
this._prepareGLObjects(gl);
this.dependencies.forEach(d => d._onContextRestored(gl));
this._needsRedraw = true;
}
_addGLNodeChild(node) {}
_removeGLNodeChild(node) {}
_addUniformBus(uniformBus, uniformName, index) {
const array = this.uniformsBus[uniformName] || (this.uniformsBus[uniformName] = []);
array[index] = uniformBus;
}
_removeUniformBus(uniformBus, uniformName, index) {
const array = this.uniformsBus[uniformName] || (this.uniformsBus[uniformName] = []);
if (array[index] === uniformBus) {
array[index] = null;
}
}
_addDependent(node) {
const i = this.dependents.indexOf(node);
if (i === -1) {
(0, _invariantNoDependentsLoop.default)(this, node);
this.dependents.push(node);
}
}
_removeDependent(node) {
const i = this.dependents.indexOf(node);
if (i !== -1) {
this.dependents.splice(i, 1);
}
}
_syncDependencies(newdeps) {
const olddeps = this.dependencies;
const additions = newdeps.filter(node => olddeps.indexOf(node) === -1);
const deletions = olddeps.filter(node => newdeps.indexOf(node) === -1);
olddeps.forEach(d => d._removeDependent(this));
newdeps.forEach(d => d._addDependent(this));
this.dependencies = newdeps;
return [additions, deletions];
}
_bind() {
if (this.framebuffer) {
this.framebuffer.bind();
} else {
this.context.glSurface._bindRootNode();
}
}
_captureAlloc(size) {
let {
capturePixelsArray
} = this;
if (capturePixelsArray && size !== capturePixelsArray.length) {
_typedarrayPool.default.freeUint8(capturePixelsArray);
capturePixelsArray = undefined;
}
const pixels = capturePixelsArray || _typedarrayPool.default.mallocUint8(size);
this.capturePixelsArray = pixels;
return pixels;
}
_latestShaderInfo;
_shader;
_getShader(shaderProp) {
const {
glSurface
} = this.context;
const nodeName = this.getGLName();
(0, _invariant.default)(shaderProp, nodeName + ": shader prop must be provided");
if ((0, _Shaders.isShaderIdentifier)(shaderProp)) {
return glSurface._getShader(shaderProp);
}
const shaderInfo = (0, _Shaders.shaderDefinitionToShaderInfo)((0, _Shaders.ensureShaderDefinition)(shaderProp, " in " + nodeName), nodeName);
const latestShaderInfo = this._latestShaderInfo;
let shader = this._shader;
if (!shader || !latestShaderInfo || !(0, _Shaders.shaderInfoEquals)(latestShaderInfo, shaderInfo)) {
if (shader) {
shader.dispose();
this._shader = undefined;
}
shader = glSurface._makeShader(shaderInfo);
this._latestShaderInfo = shaderInfo;
this._shader = shader;
}
return shader;
}
_draw() {
const {
glSurface
} = this.context;
const {
gl
} = glSurface;
const visitors = glSurface.getVisitors();
const nodeName = this.getGLName();
if (!gl || !this._needsRedraw) {
visitors.forEach(v => v.onNodeDrawSkipped(this));
return;
}
const {
backbuffering,
uniforms,
uniformsOptions,
shader: shaderProp,
blendFunc,
clear,
onDraw,
ignoreUnusedUniforms
} = this.drawProps;
//~ PREPARE phase
if (!this.framebuffer) {
const {
glSizable
} = this.context;
const [width, height] = glSizable.getGLSize();
const [nw, nh] = this.getGLSize();
(0, _invariant.default)(nw === width && nh === height, nodeName + " is root but have overrided {width=%s,height=%s} which doesn't match Surface size {width=%s,height=%s}. " + "Try to wrap your Node in a <NearestCopy> or <LinearCopy>", nw, nh, width, height);
}
const shader = this._getShader(shaderProp);
this._needsRedraw = false;
const {
types
} = shader;
const glRedrawableDependencies = [];
const pendingTextures = [];
let units = 0;
const usedUniforms = Object.keys(types.uniforms);
const providedUniforms = Object.keys(uniforms);
const {
uniformsBus
} = this;
for (let k in uniformsBus) {
if (!(k in uniforms)) {
providedUniforms.push(k);
}
}
const textureUnits = new Map();
const prepareTexture = (initialObj, uniformOptions, uniformKeyName) => {
let obj = initialObj,
dependency = null,
result = null;
if (typeof obj === "function") {
obj = obj(this.redraw);
}
if (!obj) {
if (obj === undefined) {
console.warn(`${nodeName}, uniform '${uniformKeyName}' is undefined.` + "If you explicitely want to clear a texture, set it to null.");
}
} else if (isBackbuffer(obj)) {
if (!this.drawProps.backbuffering) {
console.warn(`${nodeName}, uniform ${uniformKeyName}: you must set \`backbuffering\` on Node when using Backbuffer`);
}
result = {
glNode: this,
glNodePickBackbuffer: true
};
} else if (isBackbufferFrom(obj)) {
(0, _invariant.default)(typeof obj === "object", "invalid backbufferFromNode. Got: %s", obj);
let node = obj.node;
if (node instanceof _Bus.default) {
node = node.getGLRenderableNode();
(0, _invariant.default)(node, "backbufferFromNode(bus) but bus.getGLRenderableNode() is %s", node);
}
(0, _invariant.default)(node instanceof Node, "invalid backbufferFromNode(obj): obj must be an instanceof Node or Bus. Got: %s", obj);
if (!node.drawProps.backbuffering) {
console.warn(`${nodeName}, uniform ${uniformKeyName}: you must set \`backbuffering\` on the Node referenced in backbufferFrom(${node.getGLName()})`);
}
result = {
glNode: node,
glNodePickBackbuffer: true
};
} else if (obj instanceof Node) {
dependency = obj;
result = {
glNode: obj
};
} else if (obj instanceof _Bus.default) {
const node = obj.getGLRenderableNode();
if (node) {
dependency = node;
result = {
glNode: node
};
} else {
dependency = obj;
const renderable = obj.getGLRenderableContent();
if (!renderable) {
console.warn(`${nodeName}, uniform ${uniformKeyName}: child is not renderable. Got:`, renderable);
result = {
directTexture: null
};
} else {
obj = renderable;
}
}
}
if (!result && obj) {
const {
loader,
input
} = glSurface._resolveTextureLoader(obj);
if (!loader) {
console.warn(`${nodeName}, uniform ${uniformKeyName}: no loader found for value`, input, obj);
} else {
const t = loader.get(input);
if (t) {
loader.update(input);
result = {
directTexture: t.texture,
directTextureSize: [t.width, t.height]
};
} else {
const p = loader.load(input);
pendingTextures.push(p);
}
}
}
if (dependency) glRedrawableDependencies.push(dependency);
const textureOptions = result ? uniformOptions : null;
const getMetaInfo = () => ({
initialObj,
obj,
dependency,
textureOptions
});
const getSize = () => {
const fallback = [2, 2];
return result ? "directTextureSize" in result ? result.directTextureSize || fallback : result.glNode ? result.glNode.getGLSize() : fallback : fallback;
};
const prepare = () => {
const texture = result && (result.directTexture || result.glNode && (result.glNodePickBackbuffer ? result.glNode.getGLBackbufferOutput() : result.glNode.getGLOutput())) || glSurface.getEmptyTexture();
if (textureUnits.has(texture)) {
return textureUnits.get(texture);
}
const value = units++;
gl.activeTexture(gl.TEXTURE0 + value);
gl.bindTexture(gl.TEXTURE_2D, texture);
applyTextureOptions(gl, textureOptions);
textureUnits.set(texture, value);
return value;
};
return {
getMetaInfo,
getSize,
prepare
};
};
const prepareUniform = key => {
const uniformType = types.uniforms[key];
if (!uniformType) {
const ignoredWarn = ignoreUnusedUniforms === true || ignoreUnusedUniforms instanceof Array && ignoreUnusedUniforms.includes(key);
if (!ignoredWarn) {
console.warn(`${nodeName} uniform '${key}' is not declared, nor used, in your shader code`);
}
return {
key,
value: undefined
};
}
const uniformValue = uniforms[key];
usedUniforms.splice(usedUniforms.indexOf(key), 1);
if (uniformType === "sampler2D") {
const uniformBus = uniformsBus[key];
const {
getMetaInfo,
prepare
} = prepareTexture(uniformBus && uniformBus[0] || uniformValue, uniformsOptions[key], key);
return {
key,
type: uniformType,
getMetaInfo,
prepare
};
} else if (uniformValue === _Uniform.default.Resolution) {
return {
key,
type: uniformType,
value: this.getGLSize()
};
} else if (isTextureSizeGetter(uniformValue)) {
(0, _invariant.default)(uniformValue && typeof uniformValue === "object", "unexpected textureSize object. Got: %s", uniformValue);
const {
getSize
} = prepareTexture(uniformValue.obj, null, key);
const size = getSize();
if (!size) {
console.warn(`${nodeName}, uniform ${key}: texture size is undetermined`);
}
const value = uniformValue.ratio ? size ? size[0] / size[1] : 1 : size || [0, 0];
return {
key,
type: uniformType,
value
};
} else if (Array.isArray(uniformType) && uniformType[0] === "sampler2D") {
let values;
const uniformBus = uniformsBus[key];
const v = mergeArrays(Array.isArray(uniformValue) ? uniformValue : [], Array.isArray(uniformBus) ? uniformBus : []);
if (!v.length) {
console.warn(`${nodeName}, uniform '${key}' should be an array of textures.`);
values = uniformType.map(() => null);
} else if (v.length !== uniformType.length) {
console.warn(`${nodeName}, uniform '${key}' should be an array of exactly ${uniformType.length} textures (not ${v.length}).`);
values = uniformType.map(() => null);
} else {
values = v;
}
const uniformOptions = uniformsOptions[key];
const all = values.map((value, i) => prepareTexture(value, uniformOptions, key + "[" + i + "]"));
return {
key,
type: uniformType,
getMetaInfo: () => all.reduce((acc, o) => acc.concat(o.getMetaInfo()), []),
prepare: () => all.map(o => o.prepare())
};
} else {
if (uniformValue === undefined) {
console.warn(`${nodeName}, uniform '${key}' is undefined.`);
}
return {
key,
type: uniformType,
value: uniformValue
};
}
};
const preparedUniforms = providedUniforms.map(prepareUniform);
if (usedUniforms.length !== 0) {
console.warn(nodeName + ": Missing uniforms: " + usedUniforms.map(u => `'${u}'`).join(", ") + "\n" + "all uniforms must be provided " + "because implementations might share and reuse a Shader Program");
}
if (pendingTextures.length > 0) {
Promise.all(pendingTextures).then(this.redraw);
visitors.forEach(v => v.onNodeDrawSkipped(this));
return;
}
visitors.forEach(v => v.onNodeDrawStart(this));
const [additions, deletions] = this._syncDependencies(glRedrawableDependencies);
visitors.forEach(v => v.onNodeSyncDeps(this, additions, deletions));
if (backbuffering) {
const {
backbuffer,
framebuffer
} = this;
this.backbuffer = framebuffer;
if (backbuffer) {
this.framebuffer = backbuffer;
}
}
const drawDep = d => d._draw();
this.dependencies.forEach(drawDep);
visitors.forEach(v => v.onNodeDraw(this, preparedUniforms));
shader.bind();
this._bind();
preparedUniforms.forEach(obj => {
const value = obj.prepare ? obj.prepare() : obj.value;
if (value !== undefined) {
shader.uniforms[obj.key] = value;
}
});
if (blendFunc) {
const src = mapBlendFunc(gl, blendFunc.src);
const dst = mapBlendFunc(gl, blendFunc.dst);
if (src && dst) gl.blendFunc(src, dst);
}
if (clear) {
gl.clearColor(...clear.color);
gl.clear(gl.COLOR_BUFFER_BIT);
}
gl.drawArrays(gl.TRIANGLES, 0, 3);
if (onDraw) onDraw();
visitors.forEach(v => v.onNodeDrawEnd(this));
}
}
exports.default = Node;
//# sourceMappingURL=Node.js.map