UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

179 lines (176 loc) 6.97 kB
import { GSplatManager } from './gsplat-manager.js'; /** * @import { LayerComposition } from '../composition/layer-composition.js' * @import { Camera } from '../camera.js' * @import { Layer } from '../layer.js' * @import { GraphicsDevice } from '../../platform/graphics/graphics-device.js' * @import { GraphNode } from '../graph-node.js' * @import { Scene } from '../scene.js' * @import { Renderer } from '../renderer/renderer.js' * @import { EventHandler } from '../../core/event-handler.js' */ const tempLayersToRemove = []; /** * Per layer data the director keeps track of. * * @ignore */ class GSplatLayerData { /** * @param {GraphicsDevice} device - The graphics device. * @param {GSplatDirector} director - The director. * @param {Layer} layer - The layer. * @param {GraphNode} cameraNode - The camera node. */ constructor(device, director, layer, cameraNode){ this.gsplatManager = new GSplatManager(device, director, layer, cameraNode); } destroy() { this.gsplatManager.destroy(); } } /** * Per camera data the director keeps track of. * * @ignore */ class GSplatCameraData { destroy() { this.layersMap.forEach((layerData)=>layerData.destroy()); this.layersMap.clear(); } removeLayerData(layer) { const layerData = this.layersMap.get(layer); if (layerData) { layerData.destroy(); this.layersMap.delete(layer); } } getLayerData(device, director, layer, cameraNode) { let layerData = this.layersMap.get(layer); if (!layerData) { layerData = new GSplatLayerData(device, director, layer, cameraNode); this.layersMap.set(layer, layerData); // Fire event that material was created const material = layerData.gsplatManager.material; if (material && director.eventHandler) { director.eventHandler.fire('material:created', material, cameraNode.camera, layer); } } return layerData; } constructor(){ /** * @type {Map<Layer, GSplatLayerData>} */ this.layersMap = new Map(); } } /** * Class responsible for managing {@link GSplatManager} instances for Cameras and their Layers. * * @ignore */ class GSplatDirector { /** * @param {GraphicsDevice} device - The graphics device. * @param {Renderer} renderer - The renderer. * @param {Scene} scene - The scene. * @param {EventHandler} eventHandler - Event handler for firing events. */ constructor(device, renderer, scene, eventHandler){ /** * Per camera data. * * @type {Map<Camera, GSplatCameraData>} */ this.camerasMap = new Map(); this.device = device; this.renderer = renderer; this.scene = scene; this.eventHandler = eventHandler; } destroy() { // destroy all gsplat managers this.camerasMap.forEach((cameraData)=>cameraData.destroy()); this.camerasMap.clear(); } getCameraData(camera) { let cameraData = this.camerasMap.get(camera); if (!cameraData) { cameraData = new GSplatCameraData(); this.camerasMap.set(camera, cameraData); } return cameraData; } /** * Updates the director for the given layer composition cameras and layers. * * @param {LayerComposition} comp - The layer composition. */ update(comp) { // remove camera / layer entires for cameras / layers no longer in the composition this.camerasMap.forEach((cameraData, camera)=>{ // camera is no longer in the composition if (!comp.camerasSet.has(camera)) { cameraData.destroy(); this.camerasMap.delete(camera); } else { // remove all layerdata for removed / disabled layers of this camera // Collect layers to remove (don't modify map during iteration) cameraData.layersMap.forEach((layerData, layer)=>{ if (!camera.layersSet.has(layer.id) || !layer.enabled) { tempLayersToRemove.push(layer); } }); // Now safely remove them for(let i = 0; i < tempLayersToRemove.length; i++){ const layer = tempLayersToRemove[i]; const layerData = cameraData.layersMap.get(layer); if (layerData) { layerData.destroy(); cameraData.layersMap.delete(layer); } } // Clear to avoid dangling references tempLayersToRemove.length = 0; } }); let gsplatCount = 0; // for all cameras in the composition const camerasComponents = comp.cameras; for(let i = 0; i < camerasComponents.length; i++){ const camera = camerasComponents[i].camera; let cameraData = this.camerasMap.get(camera); // for all of its layers const layerIds = camera.layers; for(let j = 0; j < layerIds.length; j++){ const layer = comp.getLayerById(layerIds[j]); if (layer?.enabled) { // if layer's splat placements were modified, or new camera if (layer.gsplatPlacementsDirty || !cameraData) { // no splats on layer const placements = layer.gsplatPlacements; if (placements.length === 0) { // remove gsplat manager if it exists if (cameraData) { cameraData.removeLayerData(layer); } } else { // update gsplat manager with modified placements cameraData ??= this.getCameraData(camera); const layerData = cameraData.getLayerData(this.device, this, layer, camera.node); layerData.gsplatManager.reconcile(placements); } } } } // update gsplat managers if (cameraData) { for (const layerData of cameraData.layersMap.values()){ gsplatCount += layerData.gsplatManager.update(); } } } // update stats this.renderer._gsplatCount = gsplatCount; // clear global gsplat params dirty flag after all updates for this camera this.scene.gsplat.dirty = false; // clear dirty flags on all layers of the composition for(let i = 0; i < comp.layerList.length; i++){ comp.layerList[i].gsplatPlacementsDirty = false; } } } export { GSplatDirector };