UNPKG

playcanvas

Version:

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

117 lines (116 loc) 4.18 kB
import { BlendState } from "../../platform/graphics/blend-state.js"; import { RenderPass } from "../../platform/graphics/render-pass.js"; import { UNIFORMTYPE_MAT4 } from "../../platform/graphics/constants.js"; import { UniformBufferFormat, UniformFormat } from "../../platform/graphics/uniform-buffer-format.js"; import { SHADER_PICK, SHADER_DEPTH_PICK } from "../../scene/constants.js"; const tempMeshInstances = []; const lights = [[], [], []]; class RenderPassPicker extends RenderPass { blendState = BlendState.NOBLEND; camera; scene; layers; mapping; depth; _qualifiedLayerIndices = []; _pickMeshInstances = /* @__PURE__ */ new Map(); _viewUniformFormat = null; constructor(device, renderer) { super(device); this.renderer = renderer; } getViewUniformFormat() { if (!this._viewUniformFormat) { this._viewUniformFormat = new UniformBufferFormat(this.device, [ new UniformFormat("matrix_viewProjection", UNIFORMTYPE_MAT4), new UniformFormat("matrix_view", UNIFORMTYPE_MAT4) ]); } return this._viewUniformFormat; } update(camera, scene, layers, mapping, depth) { this.camera = camera; this.scene = scene; this.layers = layers; this.mapping = mapping; this.depth = depth; if (scene.clusteredLightingEnabled) { this.emptyWorldClusters = this.renderer.worldClustersAllocator.empty; } } // Filter qualifying layers and prepare gsplat pick mesh instances for the compute-based // renderer. The execute() loop iterates the pre-built list instead of re-filtering. before() { this._qualifiedLayerIndices.length = 0; this._pickMeshInstances.clear(); const { camera, scene, layers, renderer } = this; const srcLayers = scene.layers.layerList; const gsplatDirector = renderer.gsplatDirector; const pickerWidth = this.renderTarget?.width ?? 1; const pickerHeight = this.renderTarget?.height ?? 1; for (let i = 0; i < srcLayers.length; i++) { const srcLayer = srcLayers[i]; if (layers && layers.indexOf(srcLayer) < 0) continue; if (!scene.layers.isSubLayerRenderedByCamera(i, camera.camera)) continue; this._qualifiedLayerIndices.push(i); renderer.culler.requestMeshInstanceCull(camera.camera, srcLayer); if (gsplatDirector) { const pickMI = gsplatDirector.prepareForPicking(camera.camera, pickerWidth, pickerHeight, srcLayer); if (pickMI) { this._pickMeshInstances.set(i, pickMI); } } } renderer.culler.executeMeshInstanceCull(); } execute() { const device = this.device; const { renderer, camera, scene, mapping, renderTarget } = this; const srcLayers = scene.layers.layerList; const isTransparent = scene.layers.subLayerList; for (const i of this._qualifiedLayerIndices) { const srcLayer = srcLayers[i]; const transparent = isTransparent[i]; if (srcLayer._clearDepthBuffer) { renderer.clear(camera.camera, false, true, false); } const culledInstances = srcLayer.getCulledInstances(camera.camera); const meshInstances = transparent ? culledInstances.transparent : culledInstances.opaque; for (let j = 0; j < meshInstances.length; j++) { const meshInstance = meshInstances[j]; if (meshInstance.pick) { tempMeshInstances.push(meshInstance); mapping.set(meshInstance.id, meshInstance); } } const pickMI = this._pickMeshInstances.get(i); if (pickMI) { tempMeshInstances.push(pickMI); } if (scene.gsplat.enableIds) { const placements = srcLayer.gsplatPlacements; for (let j = 0; j < placements.length; j++) { const placement = placements[j]; const component = placement.node?.gsplat; if (component) { mapping.set(placement.id, component); } } } if (tempMeshInstances.length > 0) { const shaderPass = this.depth ? SHADER_DEPTH_PICK : SHADER_PICK; renderer.renderForwardLayer(camera.camera, renderTarget, null, void 0, shaderPass, { meshInstances: tempMeshInstances, splitLights: lights, lightClusters: this.emptyWorldClusters, viewUniformFormat: this.getViewUniformFormat(), drawCallback: () => device.setBlendState(this.blendState) }); tempMeshInstances.length = 0; } } } } export { RenderPassPicker };