playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
117 lines (116 loc) • 4.18 kB
JavaScript
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
};