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