UNPKG

playcanvas

Version:

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

201 lines (200 loc) 5.6 kB
import { BoundingBox } from "../../core/shape/bounding-box.js"; import { GSPLATDATA_COMPACT } from "../constants.js"; import { Mesh } from "../mesh.js"; import { ShaderMaterial } from "../materials/shader-material.js"; import { WorkBufferRenderInfo } from "../gsplat-unified/gsplat-work-buffer.js"; import { GSplatStreams } from "./gsplat-streams.js"; import { GSplatResourceCleanup } from "./gsplat-resource-cleanup.js"; let id = 0; const tempMap = /* @__PURE__ */ new Map(); class GSplatResourceBase { device; gsplatData; set centers(value) { this._centers = value; } get centers() { return this._centers; } get hasCenters() { return this._centers != null; } _centers = null; centersVersion = 0; aabb; mesh = null; id = id++; workBufferRenderInfos = /* @__PURE__ */ new Map(); _format = null; streams; parameters = /* @__PURE__ */ new Map(); _refCount = 0; _meshRefCount = 0; constructor(device, gsplatData, options = {}) { this.device = device; this.gsplatData = gsplatData; this.streams = new GSplatStreams(device); if (options.prepareCenters !== false) { this._centers = gsplatData.getCenters(); } else { this._centers = null; } this.aabb = new BoundingBox(); gsplatData.calcAabb(this.aabb); } destroy() { if (this.refCount > 0) { GSplatResourceCleanup.queueDestroy(this.device, this); return; } this._actualDestroy(); } _actualDestroy() { this.streams.destroy(); this.mesh?.destroy(); this.workBufferRenderInfos.forEach((info) => info.destroy()); this.workBufferRenderInfos.clear(); } incRefCount() { this._refCount++; } decRefCount() { this._refCount--; } get refCount() { return this._refCount; } ensureMesh() { if (!this.mesh) { this.mesh = GSplatResourceBase.createMesh(this.device); this.mesh.aabb.copy(this.aabb); } this._meshRefCount++; } releaseMesh() { this._meshRefCount--; if (this._meshRefCount < 1) { this.mesh = null; } } get supportsWorkBufferGeometry() { return false; } getWorkBufferRenderInfo(colorOnly, workBufferModifier, formatHash, formatDeclarations, workBufferFormat) { this.configureMaterialDefines(tempMap); tempMap.set("GSPLAT_LOD", ""); if (colorOnly) { tempMap.set("GSPLAT_COLOR_ONLY", ""); if (this.supportsWorkBufferGeometry) { tempMap.set("GSPLAT_WORKBUFFER_GEOMETRY", ""); if (workBufferFormat.dataFormat === GSPLATDATA_COMPACT) { tempMap.set("GSPLAT_WORKBUFFER_COMPACT", ""); } } } let definesKey = ""; for (const [k, v] of tempMap) { if (definesKey) definesKey += ";"; definesKey += `${k}=${v}`; } const key = `${formatHash};${workBufferFormat.hash};${workBufferModifier?.hash ?? 0};${definesKey}`; let info = this.workBufferRenderInfos.get(key); if (!info) { const material = new ShaderMaterial(); this.configureMaterial(material, workBufferModifier, formatDeclarations); const chunks = this.device.isWebGPU ? material.shaderChunks.wgsl : material.shaderChunks.glsl; const outputStreams = colorOnly ? [workBufferFormat.getStream("dataColor")] : [...workBufferFormat.streams, ...workBufferFormat.extraStreams]; let outputCode = workBufferFormat.getOutputDeclarations(outputStreams); if (colorOnly && workBufferFormat.extraStreams.length > 0) { outputCode += ` ${workBufferFormat.getOutputStubs(workBufferFormat.extraStreams)}`; } chunks.set("gsplatWorkBufferOutputVS", outputCode); const writeCode = workBufferFormat.getWriteCode(); if (writeCode) { chunks.set("gsplatWriteVS", writeCode); } tempMap.forEach((v, k) => material.setDefine(k, v)); info = new WorkBufferRenderInfo(this.device, key, material, colorOnly, workBufferFormat); this.workBufferRenderInfos.set(key, info); } tempMap.clear(); return info; } static createMesh(device) { const splatInstanceSize = GSplatResourceBase.instanceSize; const meshPositions = new Float32Array(12 * splatInstanceSize); const meshIndices = new Uint32Array(6 * splatInstanceSize); for (let i = 0; i < splatInstanceSize; ++i) { meshPositions.set([ -1, -1, i, 1, -1, i, 1, 1, i, -1, 1, i ], i * 12); const b = i * 4; meshIndices.set([ 0 + b, 1 + b, 2 + b, 0 + b, 2 + b, 3 + b ], i * 6); } const mesh = new Mesh(device); mesh.setPositions(meshPositions, 3); mesh.setIndices(meshIndices); mesh.update(); return mesh; } static get instanceSize() { return 128; } get numSplats() { return this.gsplatData.numSplats; } get format() { return this._format; } getTexture(name) { return this.streams.getTexture(name) ?? null; } get textureDimensions() { return this.streams.textureDimensions; } configureMaterial(material, workBufferModifier, formatDeclarations) { this.configureMaterialDefines(material.defines); this.streams.syncWithFormat(this.format); const chunks = this.device.isWebGPU ? material.shaderChunks.wgsl : material.shaderChunks.glsl; chunks.set("gsplatDeclarationsVS", formatDeclarations); chunks.set("gsplatReadVS", this.format.getReadCode()); if (workBufferModifier?.code) { chunks.set("gsplatModifyVS", workBufferModifier.code); } for (const [name, texture] of this.streams.textures) { material.setParameter(name, texture); } for (const [name, value] of this.parameters) { material.setParameter(name, value); } if (this.textureDimensions.x > 0) { material.setParameter("splatTextureSize", this.textureDimensions.x); } } configureMaterialDefines(defines) { } instantiate() { } } export { GSplatResourceBase };