UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

189 lines (188 loc) 6.59 kB
import { GSplatManager } from "./gsplat-manager.js"; import { SetUtils } from "../../core/set-utils.js"; import { GSPLAT_FORWARD, GSPLAT_SHADOW } from "../constants.js"; import { GSplatResourceCleanup } from "../gsplat/gsplat-resource-cleanup.js"; const tempLayersToRemove = []; class GSplatLayerData { gsplatManager = null; gsplatManagerShadow = null; constructor(device, director, layer, camera) { this.updateConfiguration(device, director, layer, camera); } createManager(device, director, layer, cameraNode, camera, renderMode) { const manager = new GSplatManager(device, director, layer, cameraNode); manager.setRenderMode(renderMode); if (director.eventHandler) { director.eventHandler.fire("material:created", manager.material, camera, layer); } return manager; } updateConfiguration(device, director, layer, camera) { const cameraNode = camera.node; const hasNormalPlacements = layer.gsplatPlacements.length > 0; const hasShadowCasters = layer.gsplatShadowCasters.length > 0; const setsEqual = SetUtils.equals(layer.gsplatPlacementsSet, layer.gsplatShadowCastersSet); const useSharedManager = setsEqual && hasNormalPlacements; const desiredMainMode = useSharedManager ? GSPLAT_FORWARD | GSPLAT_SHADOW : hasNormalPlacements ? GSPLAT_FORWARD : 0; const desiredShadowMode = useSharedManager ? 0 : hasShadowCasters ? GSPLAT_SHADOW : 0; if (desiredMainMode) { if (this.gsplatManager) { this.gsplatManager.setRenderMode(desiredMainMode); } else { this.gsplatManager = this.createManager(device, director, layer, cameraNode, camera, desiredMainMode); } } else if (this.gsplatManager) { this.gsplatManager.destroy(); this.gsplatManager = null; } if (desiredShadowMode) { if (this.gsplatManagerShadow) { this.gsplatManagerShadow.setRenderMode(desiredShadowMode); } else { this.gsplatManagerShadow = this.createManager(device, director, layer, cameraNode, camera, desiredShadowMode); } } else if (this.gsplatManagerShadow) { this.gsplatManagerShadow.destroy(); this.gsplatManagerShadow = null; } } destroy() { this.gsplatManager?.destroy(); this.gsplatManager = null; this.gsplatManagerShadow?.destroy(); this.gsplatManagerShadow = null; } } class GSplatCameraData { layersMap = /* @__PURE__ */ new Map(); 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, camera) { let layerData = this.layersMap.get(layer); if (!layerData) { layerData = new GSplatLayerData(device, director, layer, camera); this.layersMap.set(layer, layerData); } return layerData; } } class GSplatDirector { device; camerasMap = /* @__PURE__ */ new Map(); scene; eventHandler; constructor(device, renderer, scene, eventHandler) { this.device = device; this.renderer = renderer; this.scene = scene; this.eventHandler = eventHandler; } destroy() { 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; } prepareForPicking(camera, width, height, layer) { const cameraData = this.camerasMap.get(camera); if (!cameraData) return null; const layerData = cameraData.layersMap.get(layer); if (!layerData?.gsplatManager) return null; return layerData.gsplatManager.prepareForPicking(camera, width, height); } update(comp) { GSplatResourceCleanup.process(this.device); this.camerasMap.forEach((cameraData, camera) => { if (!comp.camerasSet.has(camera)) { cameraData.destroy(); this.camerasMap.delete(camera); } else { cameraData.layersMap.forEach((layerData, layer) => { if (!camera.layersSet.has(layer.id) || !layer.enabled) { tempLayersToRemove.push(layer); } }); 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); } } tempLayersToRemove.length = 0; } }); let gsplatCount = 0; let bufferCopyUploaded = 0; let bufferCopyTotal = 0; const camerasComponents = comp.cameras; for (let i = 0; i < camerasComponents.length; i++) { const camera = camerasComponents[i].camera; let cameraData = this.camerasMap.get(camera); const layerIds = camera.layers; for (let j = 0; j < layerIds.length; j++) { const layer = comp.getLayerById(layerIds[j]); if (layer?.enabled) { if (layer.gsplatPlacementsDirty || !cameraData) { const hasNormalPlacements = layer.gsplatPlacements.length > 0; const hasShadowCasters = layer.gsplatShadowCasters.length > 0; if (!hasNormalPlacements && !hasShadowCasters) { if (cameraData) { cameraData.removeLayerData(layer); } } else { cameraData ?? (cameraData = this.getCameraData(camera)); const layerData = cameraData.getLayerData(this.device, this, layer, camera); layerData.updateConfiguration(this.device, this, layer, camera); if (layerData.gsplatManager) { layerData.gsplatManager.reconcile(layer.gsplatPlacements); } if (layerData.gsplatManagerShadow) { layerData.gsplatManagerShadow.reconcile(layer.gsplatShadowCasters); } } } } } if (cameraData) { for (const layerData of cameraData.layersMap.values()) { if (layerData.gsplatManager) { gsplatCount += layerData.gsplatManager.update(); bufferCopyUploaded += layerData.gsplatManager.bufferCopyUploaded; bufferCopyTotal += layerData.gsplatManager.bufferCopyTotal; } if (layerData.gsplatManagerShadow) { gsplatCount += layerData.gsplatManagerShadow.update(); bufferCopyUploaded += layerData.gsplatManagerShadow.bufferCopyUploaded; bufferCopyTotal += layerData.gsplatManagerShadow.bufferCopyTotal; } } } } this.renderer._gsplatCount = gsplatCount; this.renderer._gsplatBufferCopy = bufferCopyTotal > 0 ? bufferCopyUploaded / bufferCopyTotal * 100 : 0; this.scene.gsplat.frameEnd(); for (let i = 0; i < comp.layerList.length; i++) { comp.layerList[i].gsplatPlacementsDirty = false; } } } export { GSplatDirector };