UNPKG

@openhps/core

Version:

Open Hybrid Positioning System - Core component

578 lines (535 loc) 19.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _DataMap = _interopRequireDefault(require("../DataMap.js")); var _ChainMap = _interopRequireDefault(require("../ChainMap.js")); var _NodeBuilderState = _interopRequireDefault(require("./NodeBuilderState.js")); var _CubeMapNode = require("../../../nodes/utils/CubeMapNode.js"); var _Nodes = require("../../../nodes/Nodes.js"); var _TSL = require("../../../nodes/TSL.js"); var _constants = require("../../../constants.js"); var _NodeUtils = require("../../../nodes/core/NodeUtils.js"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const _outputNodeMap = new WeakMap(); const _chainKeys = []; const _cacheKeyValues = []; /** * This renderer module manages node-related objects and is the * primary interface between the renderer and the node system. * * @private * @augments DataMap */ class Nodes extends _DataMap.default { /** * Constructs a new nodes management component. * * @param {Renderer} renderer - The renderer. * @param {Backend} backend - The renderer's backend. */ constructor(renderer, backend) { super(); /** * The renderer. * * @type {Renderer} */ this.renderer = renderer; /** * The renderer's backend. * * @type {Backend} */ this.backend = backend; /** * The node frame. * * @type {Renderer} */ this.nodeFrame = new _Nodes.NodeFrame(); /** * A cache for managing node builder states. * * @type {Map<number,NodeBuilderState>} */ this.nodeBuilderCache = new Map(); /** * A cache for managing data cache key data. * * @type {ChainMap} */ this.callHashCache = new _ChainMap.default(); /** * A cache for managing node uniforms group data. * * @type {ChainMap} */ this.groupsData = new _ChainMap.default(); /** * A cache for managing node objects of * scene properties like fog or environments. * * @type {Object<string,WeakMap>} */ this.cacheLib = {}; } /** * Returns `true` if the given node uniforms group must be updated or not. * * @param {NodeUniformsGroup} nodeUniformsGroup - The node uniforms group. * @return {boolean} Whether the node uniforms group requires an update or not. */ updateGroup(nodeUniformsGroup) { const groupNode = nodeUniformsGroup.groupNode; const name = groupNode.name; // objectGroup is always updated if (name === _TSL.objectGroup.name) return true; // renderGroup is updated once per render/compute call if (name === _TSL.renderGroup.name) { const uniformsGroupData = this.get(nodeUniformsGroup); const renderId = this.nodeFrame.renderId; if (uniformsGroupData.renderId !== renderId) { uniformsGroupData.renderId = renderId; return true; } return false; } // frameGroup is updated once per frame if (name === _TSL.frameGroup.name) { const uniformsGroupData = this.get(nodeUniformsGroup); const frameId = this.nodeFrame.frameId; if (uniformsGroupData.frameId !== frameId) { uniformsGroupData.frameId = frameId; return true; } return false; } // other groups are updated just when groupNode.needsUpdate is true _chainKeys[0] = groupNode; _chainKeys[1] = nodeUniformsGroup; let groupData = this.groupsData.get(_chainKeys); if (groupData === undefined) this.groupsData.set(_chainKeys, groupData = {}); _chainKeys.length = 0; if (groupData.version !== groupNode.version) { groupData.version = groupNode.version; return true; } return false; } /** * Returns the cache key for the given render object. * * @param {RenderObject} renderObject - The render object. * @return {number} The cache key. */ getForRenderCacheKey(renderObject) { return renderObject.initialCacheKey; } /** * Returns a node builder state for the given render object. * * @param {RenderObject} renderObject - The render object. * @return {NodeBuilderState} The node builder state. */ getForRender(renderObject) { const renderObjectData = this.get(renderObject); let nodeBuilderState = renderObjectData.nodeBuilderState; if (nodeBuilderState === undefined) { const { nodeBuilderCache } = this; const cacheKey = this.getForRenderCacheKey(renderObject); nodeBuilderState = nodeBuilderCache.get(cacheKey); if (nodeBuilderState === undefined) { const nodeBuilder = this.backend.createNodeBuilder(renderObject.object, this.renderer); nodeBuilder.scene = renderObject.scene; nodeBuilder.material = renderObject.material; nodeBuilder.camera = renderObject.camera; nodeBuilder.context.material = renderObject.material; nodeBuilder.lightsNode = renderObject.lightsNode; nodeBuilder.environmentNode = this.getEnvironmentNode(renderObject.scene); nodeBuilder.fogNode = this.getFogNode(renderObject.scene); nodeBuilder.clippingContext = renderObject.clippingContext; nodeBuilder.build(); nodeBuilderState = this._createNodeBuilderState(nodeBuilder); nodeBuilderCache.set(cacheKey, nodeBuilderState); } nodeBuilderState.usedTimes++; renderObjectData.nodeBuilderState = nodeBuilderState; } return nodeBuilderState; } /** * Deletes the given object from the internal data map * * @param {any} object - The object to delete. * @return {?Object} The deleted dictionary. */ delete(object) { if (object.isRenderObject) { const nodeBuilderState = this.get(object).nodeBuilderState; nodeBuilderState.usedTimes--; if (nodeBuilderState.usedTimes === 0) { this.nodeBuilderCache.delete(this.getForRenderCacheKey(object)); } } return super.delete(object); } /** * Returns a node builder state for the given compute node. * * @param {Node} computeNode - The compute node. * @return {NodeBuilderState} The node builder state. */ getForCompute(computeNode) { const computeData = this.get(computeNode); let nodeBuilderState = computeData.nodeBuilderState; if (nodeBuilderState === undefined) { const nodeBuilder = this.backend.createNodeBuilder(computeNode, this.renderer); nodeBuilder.build(); nodeBuilderState = this._createNodeBuilderState(nodeBuilder); computeData.nodeBuilderState = nodeBuilderState; } return nodeBuilderState; } /** * Creates a node builder state for the given node builder. * * @private * @param {NodeBuilder} nodeBuilder - The node builder. * @return {NodeBuilderState} The node builder state. */ _createNodeBuilderState(nodeBuilder) { return new _NodeBuilderState.default(nodeBuilder.vertexShader, nodeBuilder.fragmentShader, nodeBuilder.computeShader, nodeBuilder.getAttributesArray(), nodeBuilder.getBindings(), nodeBuilder.updateNodes, nodeBuilder.updateBeforeNodes, nodeBuilder.updateAfterNodes, nodeBuilder.observer, nodeBuilder.transforms); } /** * Returns an environment node for the current configured * scene environment. * * @param {Scene} scene - The scene. * @return {Node} A node representing the current scene environment. */ getEnvironmentNode(scene) { this.updateEnvironment(scene); let environmentNode = null; if (scene.environmentNode && scene.environmentNode.isNode) { environmentNode = scene.environmentNode; } else { const sceneData = this.get(scene); if (sceneData.environmentNode) { environmentNode = sceneData.environmentNode; } } return environmentNode; } /** * Returns a background node for the current configured * scene background. * * @param {Scene} scene - The scene. * @return {Node} A node representing the current scene background. */ getBackgroundNode(scene) { this.updateBackground(scene); let backgroundNode = null; if (scene.backgroundNode && scene.backgroundNode.isNode) { backgroundNode = scene.backgroundNode; } else { const sceneData = this.get(scene); if (sceneData.backgroundNode) { backgroundNode = sceneData.backgroundNode; } } return backgroundNode; } /** * Returns a fog node for the current configured scene fog. * * @param {Scene} scene - The scene. * @return {Node} A node representing the current scene fog. */ getFogNode(scene) { this.updateFog(scene); return scene.fogNode || this.get(scene).fogNode || null; } /** * Returns a cache key for the given scene and lights node. * This key is used by `RenderObject` as a part of the dynamic * cache key (a key that must be checked every time the render * objects is drawn). * * @param {Scene} scene - The scene. * @param {LightsNode} lightsNode - The lights node. * @return {number} The cache key. */ getCacheKey(scene, lightsNode) { _chainKeys[0] = scene; _chainKeys[1] = lightsNode; const callId = this.renderer.info.calls; const cacheKeyData = this.callHashCache.get(_chainKeys) || {}; if (cacheKeyData.callId !== callId) { const environmentNode = this.getEnvironmentNode(scene); const fogNode = this.getFogNode(scene); if (lightsNode) _cacheKeyValues.push(lightsNode.getCacheKey(true)); if (environmentNode) _cacheKeyValues.push(environmentNode.getCacheKey()); if (fogNode) _cacheKeyValues.push(fogNode.getCacheKey()); _cacheKeyValues.push(this.renderer.shadowMap.enabled ? 1 : 0); cacheKeyData.callId = callId; cacheKeyData.cacheKey = (0, _NodeUtils.hashArray)(_cacheKeyValues); this.callHashCache.set(_chainKeys, cacheKeyData); _cacheKeyValues.length = 0; } _chainKeys.length = 0; return cacheKeyData.cacheKey; } /** * A boolean that indicates whether tone mapping should be enabled * or not. * * @type {boolean} */ get isToneMappingState() { return this.renderer.getRenderTarget() ? false : true; } /** * If a scene background is configured, this method makes sure to * represent the background with a corresponding node-based implementation. * * @param {Scene} scene - The scene. */ updateBackground(scene) { const sceneData = this.get(scene); const background = scene.background; if (background) { const forceUpdate = scene.backgroundBlurriness === 0 && sceneData.backgroundBlurriness > 0 || scene.backgroundBlurriness > 0 && sceneData.backgroundBlurriness === 0; if (sceneData.background !== background || forceUpdate) { const backgroundNode = this.getCacheNode('background', background, () => { if (background.isCubeTexture === true || background.mapping === _constants.EquirectangularReflectionMapping || background.mapping === _constants.EquirectangularRefractionMapping || background.mapping === _constants.CubeUVReflectionMapping) { if (scene.backgroundBlurriness > 0 || background.mapping === _constants.CubeUVReflectionMapping) { return (0, _TSL.pmremTexture)(background); } else { let envMap; if (background.isCubeTexture === true) { envMap = (0, _TSL.cubeTexture)(background); } else { envMap = (0, _TSL.texture)(background); } return (0, _CubeMapNode.cubeMapNode)(envMap); } } else if (background.isTexture === true) { return (0, _TSL.texture)(background, _TSL.screenUV.flipY()).setUpdateMatrix(true); } else if (background.isColor !== true) { console.error('WebGPUNodes: Unsupported background configuration.', background); } }, forceUpdate); sceneData.backgroundNode = backgroundNode; sceneData.background = background; sceneData.backgroundBlurriness = scene.backgroundBlurriness; } } else if (sceneData.backgroundNode) { delete sceneData.backgroundNode; delete sceneData.background; } } /** * This method is part of the caching of nodes which are used to represents the * scene's background, fog or environment. * * @param {string} type - The type of object to cache. * @param {Object} object - The object. * @param {Function} callback - A callback that produces a node representation for the given object. * @param {boolean} [forceUpdate=false] - Whether an update should be enforced or not. * @return {Node} The node representation. */ getCacheNode(type, object, callback, forceUpdate = false) { const nodeCache = this.cacheLib[type] || (this.cacheLib[type] = new WeakMap()); let node = nodeCache.get(object); if (node === undefined || forceUpdate) { node = callback(); nodeCache.set(object, node); } return node; } /** * If a scene fog is configured, this method makes sure to * represent the fog with a corresponding node-based implementation. * * @param {Scene} scene - The scene. */ updateFog(scene) { const sceneData = this.get(scene); const sceneFog = scene.fog; if (sceneFog) { if (sceneData.fog !== sceneFog) { const fogNode = this.getCacheNode('fog', sceneFog, () => { if (sceneFog.isFogExp2) { const color = (0, _TSL.reference)('color', 'color', sceneFog).setGroup(_TSL.renderGroup); const density = (0, _TSL.reference)('density', 'float', sceneFog).setGroup(_TSL.renderGroup); return (0, _TSL.fog)(color, (0, _TSL.densityFogFactor)(density)); } else if (sceneFog.isFog) { const color = (0, _TSL.reference)('color', 'color', sceneFog).setGroup(_TSL.renderGroup); const near = (0, _TSL.reference)('near', 'float', sceneFog).setGroup(_TSL.renderGroup); const far = (0, _TSL.reference)('far', 'float', sceneFog).setGroup(_TSL.renderGroup); return (0, _TSL.fog)(color, (0, _TSL.rangeFogFactor)(near, far)); } else { console.error('THREE.Renderer: Unsupported fog configuration.', sceneFog); } }); sceneData.fogNode = fogNode; sceneData.fog = sceneFog; } } else { delete sceneData.fogNode; delete sceneData.fog; } } /** * If a scene environment is configured, this method makes sure to * represent the environment with a corresponding node-based implementation. * * @param {Scene} scene - The scene. */ updateEnvironment(scene) { const sceneData = this.get(scene); const environment = scene.environment; if (environment) { if (sceneData.environment !== environment) { const environmentNode = this.getCacheNode('environment', environment, () => { if (environment.isCubeTexture === true) { return (0, _TSL.cubeTexture)(environment); } else if (environment.isTexture === true) { return (0, _TSL.texture)(environment); } else { console.error('Nodes: Unsupported environment configuration.', environment); } }); sceneData.environmentNode = environmentNode; sceneData.environment = environment; } } else if (sceneData.environmentNode) { delete sceneData.environmentNode; delete sceneData.environment; } } getNodeFrame(renderer = this.renderer, scene = null, object = null, camera = null, material = null) { const nodeFrame = this.nodeFrame; nodeFrame.renderer = renderer; nodeFrame.scene = scene; nodeFrame.object = object; nodeFrame.camera = camera; nodeFrame.material = material; return nodeFrame; } getNodeFrameForRender(renderObject) { return this.getNodeFrame(renderObject.renderer, renderObject.scene, renderObject.object, renderObject.camera, renderObject.material); } /** * Returns the current output cache key. * * @return {string} The output cache key. */ getOutputCacheKey() { const renderer = this.renderer; return renderer.toneMapping + ',' + renderer.currentColorSpace; } /** * Checks if the output configuration (tone mapping and color space) for * the given target has changed. * * @param {Texture} outputTarget - The output target. * @return {boolean} Whether the output configuration has changed or not. */ hasOutputChange(outputTarget) { const cacheKey = _outputNodeMap.get(outputTarget); return cacheKey !== this.getOutputCacheKey(); } /** * Returns a node that represents the output configuration (tone mapping and * color space) for the current target. * * @param {Texture} outputTarget - The output target. * @return {Node} The output node. */ getOutputNode(outputTarget) { const renderer = this.renderer; const cacheKey = this.getOutputCacheKey(); const output = (0, _TSL.texture)(outputTarget, _TSL.screenUV).renderOutput(renderer.toneMapping, renderer.currentColorSpace); _outputNodeMap.set(outputTarget, cacheKey); return output; } /** * Triggers the call of `updateBefore()` methods * for all nodes of the given render object. * * @param {RenderObject} renderObject - The render object. */ updateBefore(renderObject) { const nodeBuilder = renderObject.getNodeBuilderState(); for (const node of nodeBuilder.updateBeforeNodes) { // update frame state for each node this.getNodeFrameForRender(renderObject).updateBeforeNode(node); } } /** * Triggers the call of `updateAfter()` methods * for all nodes of the given render object. * * @param {RenderObject} renderObject - The render object. */ updateAfter(renderObject) { const nodeBuilder = renderObject.getNodeBuilderState(); for (const node of nodeBuilder.updateAfterNodes) { // update frame state for each node this.getNodeFrameForRender(renderObject).updateAfterNode(node); } } /** * Triggers the call of `update()` methods * for all nodes of the given compute node. * * @param {Node} computeNode - The compute node. */ updateForCompute(computeNode) { const nodeFrame = this.getNodeFrame(); const nodeBuilder = this.getForCompute(computeNode); for (const node of nodeBuilder.updateNodes) { nodeFrame.updateNode(node); } } /** * Triggers the call of `update()` methods * for all nodes of the given compute node. * * @param {RenderObject} renderObject - The render object. */ updateForRender(renderObject) { const nodeFrame = this.getNodeFrameForRender(renderObject); const nodeBuilder = renderObject.getNodeBuilderState(); for (const node of nodeBuilder.updateNodes) { nodeFrame.updateNode(node); } } /** * Returns `true` if the given render object requires a refresh. * * @param {RenderObject} renderObject - The render object. * @return {boolean} Whether the given render object requires a refresh or not. */ needsRefresh(renderObject) { const nodeFrame = this.getNodeFrameForRender(renderObject); const monitor = renderObject.getMonitor(); return monitor.needsRefresh(renderObject, nodeFrame); } /** * Frees the internal resources. */ dispose() { super.dispose(); this.nodeFrame = new _Nodes.NodeFrame(); this.nodeBuilderCache = new Map(); this.cacheLib = {}; } } var _default = exports.default = Nodes;