@codyjasonbennett/react-ogl
Version:
A barebones react renderer for OGL.
296 lines (295 loc) • 9.15 kB
JavaScript
var _a;
import Reconciler from "react-reconciler";
import { DefaultEventPriority } from "react-reconciler/constants";
import * as OGL from "ogl";
import * as React from "react";
import { toPascalCase, detach, classExtends, attach, applyProps } from "./utils.mjs";
import { RESERVED_PROPS } from "./constants.mjs";
const catalogue = { ...OGL };
const catalogueGL = [
OGL.Camera,
OGL.Geometry,
OGL.Mesh,
OGL.Program,
OGL.RenderTarget,
OGL.Texture,
OGL.Flowmap,
OGL.GPGPU,
OGL.NormalProgram,
OGL.Polyline,
OGL.Post,
OGL.Shadow,
OGL.AxesHelper,
OGL.GridHelper,
OGL.WireMesh
];
function extend(objects, gl = false) {
for (const key in objects) {
const value = objects[key];
catalogue[key] = value;
if (gl)
catalogueGL.push(value);
}
}
function createInstance(type, { object = null, args = [], ...props }, root) {
const name = toPascalCase(type);
const target = catalogue[name];
if (type !== "primitive" && !target)
throw `${type} is not a part of the OGL catalogue! Did you forget to extend?`;
if (type === "primitive" && !object)
throw `"object" must be set when using primitives.`;
const instance = {
root,
parent: null,
children: [],
type,
props: { ...props, args },
object
};
return instance;
}
const appendChild = (parent, child) => {
child.parent = parent;
parent.children.push(child);
};
function removeChild(parent, child) {
var _a2, _b;
child.parent = null;
const childIndex = parent.children.indexOf(child);
if (childIndex !== -1)
parent.children.splice(childIndex, 1);
if (child.props.attach)
detach(parent, child);
else if (child.object instanceof OGL.Transform)
parent.object.removeChild(child.object);
if (child.props.dispose !== null)
(_b = (_a2 = child.object).dispose) == null ? void 0 : _b.call(_a2);
child.object = null;
}
function commitInstance(instance) {
var _a2;
if (!instance.parent)
return;
if (instance.type !== "primitive" && !instance.object) {
const name = toPascalCase(instance.type);
const target = catalogue[name];
const { args = [], ...props } = instance.props;
const isGLInstance = Object.values(catalogueGL).some((elem) => classExtends(elem, target));
if (isGLInstance) {
const { gl } = instance.root.getState();
const filtered = args.filter((arg) => arg !== gl);
if (instance.type === "program" || instance.type === "geometry") {
const attrs = Object.entries(props).reduce((acc, [key, value]) => {
if (instance.type === "geometry" && !(value == null ? void 0 : value.data))
return acc;
if (!key.includes("-"))
acc[key] = value;
return acc;
}, (_a2 = filtered[0]) != null ? _a2 : {});
instance.object = new target(gl, attrs);
} else {
instance.object = new target(gl, ...filtered);
}
} else {
instance.object = new target(...args);
}
}
if (!instance.props.attach) {
if (instance.object instanceof OGL.Geometry)
instance.props.attach = "geometry";
else if (instance.object instanceof OGL.Program)
instance.props.attach = "program";
}
for (const child of instance.children) {
if (child.props.attach)
attach(instance, child);
else if (child.object instanceof OGL.Transform)
child.object.setParent(instance.object);
}
if (!instance.parent.parent) {
if (instance.props.attach)
attach(instance.parent, instance);
else if (instance.object instanceof OGL.Transform)
instance.object.setParent(instance.parent.object);
}
applyProps(instance.object, instance.props);
}
function switchInstance(instance, type, props, root) {
const newInstance = createInstance(type, props, instance.root);
const parent = instance.parent;
removeChild(parent, instance);
appendChild(parent, newInstance);
commitInstance(newInstance);
if (parent.parent) {
if (newInstance.props.attach)
attach(parent, newInstance);
else if (newInstance.object instanceof OGL.Transform)
newInstance.object.setParent(parent.object);
}
for (const child of instance.children) {
appendChild(newInstance, child);
if (child.props.attach) {
detach(instance, child);
attach(newInstance, child);
}
}
instance.children = [];
[root, root.alternate].forEach((fiber) => {
if (fiber !== null) {
fiber.stateNode = newInstance;
if (fiber.ref) {
if (typeof fiber.ref === "function")
fiber.ref(newInstance.object);
else
fiber.ref.current = newInstance.object;
}
}
});
return newInstance;
}
function checkShallow(a, b) {
if (Array.isArray(a)) {
if (!Array.isArray(b))
return false;
if (a == b)
return true;
if (a.every((v, i) => v === b[i]))
return true;
}
if (a === b)
return true;
return false;
}
function diffProps(instance, newProps, oldProps) {
const changedProps = {};
for (const key in newProps) {
if (RESERVED_PROPS.includes(key))
continue;
if (instance.type === "primitive" && key === "object")
continue;
if (checkShallow(newProps[key], oldProps[key]))
continue;
changedProps[key] = newProps[key];
}
return changedProps;
}
function insertBefore(parent, child, beforeChild) {
if (!child)
return;
child.parent = parent;
parent.children.splice(parent.children.indexOf(beforeChild), 0, child);
}
const reconciler = Reconciler({
isPrimaryRenderer: false,
supportsMutation: true,
supportsHydration: false,
supportsPersistence: false,
scheduleTimeout: () => typeof setTimeout !== "undefined" ? setTimeout : void 0,
cancelTimeout: () => typeof clearTimeout !== "undefined" ? clearTimeout : void 0,
noTimeout: -1,
shouldSetTextContent: () => false,
resetTextContent: () => {
},
createTextInstance() {
throw new Error("Text is not allowed in the OGL scene-graph!");
},
hideTextInstance() {
throw new Error("Text is not allowed in the OGL scene-graph!");
},
unhideTextInstance: () => {
},
getPublicInstance: (instance) => instance.object,
getRootHostContext: () => null,
getChildHostContext: (parentHostContext) => parentHostContext,
preparePortalMount: (container) => container,
prepareForCommit: () => null,
resetAfterCommit: () => {
},
clearContainer: () => false,
createInstance,
appendChild,
appendInitialChild: appendChild,
appendChildToContainer: () => {
},
removeChild,
removeChildFromContainer: () => {
},
insertBefore,
insertInContainerBefore: () => {
},
prepareUpdate(instance, type, oldProps, newProps) {
var _a2, _b, _c;
if (instance.type === "primitive" && oldProps.object !== newProps.object)
return [true];
if (type === "program") {
if (oldProps.vertex !== newProps.vertex)
return [true];
if (oldProps.fragment !== newProps.fragment)
return [true];
}
if (type === "geometry") {
for (const key in oldProps) {
const isAttribute = ((_a2 = oldProps[key]) == null ? void 0 : _a2.data) || ((_b = newProps[key]) == null ? void 0 : _b.data);
if (isAttribute && oldProps[key] !== newProps[key])
return [true];
}
}
if ((_c = newProps.args) == null ? void 0 : _c.some((value, index) => {
var _a3;
return value !== ((_a3 = oldProps.args) == null ? void 0 : _a3[index]);
}))
return [true];
const changedProps = diffProps(instance, newProps, oldProps);
if (Object.keys(changedProps).length)
return [false, changedProps];
return null;
},
commitUpdate(instance, payload, type, oldProps, newProps, root) {
const [reconstruct, changedProps] = payload;
if (reconstruct)
return switchInstance(instance, type, newProps, root);
if (changedProps == null ? void 0 : changedProps.attach) {
if (oldProps.attach)
detach(instance.parent, instance);
instance.props.attach = newProps.attach;
if (newProps.attach)
attach(instance.parent, instance);
}
Object.assign(instance.props, newProps);
applyProps(instance.object, newProps);
},
hideInstance(instance) {
if (instance.object instanceof OGL.Transform) {
instance.object.visible = false;
}
},
unhideInstance(instance) {
var _a2;
if (instance.object instanceof OGL.Transform) {
instance.object.visible = (_a2 = instance.props.visible) != null ? _a2 : true;
}
},
finalizeInitialChildren: () => true,
commitMount: commitInstance,
getCurrentEventPriority: () => DefaultEventPriority,
beforeActiveInstanceBlur: () => {
},
afterActiveInstanceBlur: () => {
},
detachDeletedInstance: () => {
}
});
const act = React.unstable_act;
const isProd = typeof process === "undefined" || ((_a = process.env) == null ? void 0 : _a["NODE_ENV"]) === "production";
reconciler.injectIntoDevTools({
findFiberByHostInstance: (instance) => instance.root,
bundleType: isProd ? 0 : 1,
version: React.version,
rendererPackageName: "react-ogl"
});
export {
act,
extend,
reconciler
};
//# sourceMappingURL=reconciler.mjs.map