UNPKG

playcanvas

Version:

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

178 lines (177 loc) 6.37 kB
import { Vec2 } from "../../core/math/vec2.js"; import { Mat4 } from "../../core/math/mat4.js"; import { Vec3 } from "../../core/math/vec3.js"; import { Quat } from "../../core/math/quat.js"; import { RenderPass } from "../../platform/graphics/render-pass.js"; import { PIXELFORMAT_RGBA32U } from "../../platform/graphics/constants.js"; import { Texture } from "../../platform/graphics/texture.js"; import { TextureUtils } from "../../platform/graphics/texture-utils.js"; const _viewMat = new Mat4(); const _modelScale = new Vec3(); const _modelRotation = new Quat(); const _tmpSize = new Vec2(); const _whiteColor = [1, 1, 1]; class GSplatWorkBufferRenderPass extends RenderPass { splats = []; colorsByLod = void 0; cameraNode = null; workBuffer; colorOnly; _modelScaleData = new Float32Array(3); _modelRotationData = new Float32Array(4); _textureSize = new Int32Array(2); _subDrawTexture; _partialData = []; constructor(device, workBuffer, colorOnly = false) { super(device); this.workBuffer = workBuffer; this.colorOnly = colorOnly; this._subDrawTexture = Texture.createDataTexture2D(device, "GsplatSubDrawData", 1, 1, PIXELFORMAT_RGBA32U); } destroy() { this.splats.length = 0; this._subDrawTexture.destroy(); super.destroy(); } init(renderTarget) { super.init(renderTarget); this.colorOps.clear = false; this.depthStencilOps.clearDepth = false; } update(splats, cameraNode, colorsByLod, changedAllocIds = null) { this.splats.length = 0; this._partialData.length = 0; this.colorsByLod = colorsByLod; const textureWidth = this.workBuffer.textureSize; if (changedAllocIds) { const requiredCapacity = changedAllocIds.size * 3; if (this._subDrawTexture.width * this._subDrawTexture.height < requiredCapacity) { TextureUtils.calcTextureSize(requiredCapacity, _tmpSize); this._subDrawTexture.resize(_tmpSize.x, _tmpSize.y); } const texData = this._subDrawTexture.lock(); let writeOffset = 0; for (let i = 0; i < splats.length; i++) { const splatInfo = splats[i]; if (splatInfo.activeSplats <= 0) continue; const intervals = splatInfo.intervals; const numIntervals = intervals.length / 2; if (numIntervals === 0) { if (changedAllocIds.has(splatInfo.allocId)) { this.splats.push(splatInfo); this._partialData.push(0, 0); } } else { const baseOffset = writeOffset; const allocIds = splatInfo.intervalAllocIds; for (let j = 0; j < numIntervals; j++) { if (changedAllocIds.has(allocIds[j])) { writeOffset = splatInfo.appendSubDraws( texData, writeOffset, intervals[j * 2], intervals[j * 2 + 1] - intervals[j * 2], splatInfo.intervalOffsets[j], textureWidth ); } } const count = writeOffset - baseOffset; if (count > 0) { this.splats.push(splatInfo); this._partialData.push(baseOffset, count); } } } this._subDrawTexture.unlock(); } else { for (let i = 0; i < splats.length; i++) { const splatInfo = splats[i]; if (splatInfo.activeSplats > 0) { this.splats.push(splatInfo); this._partialData.push(0, 0); } } } for (let i = 0; i < this.splats.length; i++) { if (this._partialData[i * 2 + 1] === 0) { this.splats[i].ensureSubDrawTexture(textureWidth); } } this.cameraNode = cameraNode; return this.splats.length > 0; } execute() { const { device, splats, cameraNode, _partialData } = this; device.setDrawStates(); const viewInvMat = cameraNode.getWorldTransform(); const viewMat = _viewMat.copy(viewInvMat).invert(); device.scope.resolve("matrix_view").setValue(viewMat.data); for (let i = 0; i < splats.length; i++) { const count = _partialData[i * 2 + 1]; if (count > 0) { this.renderSplat(splats[i], this._subDrawTexture, count, _partialData[i * 2]); } else { this.renderSplat(splats[i]); } } } renderSplat(splatInfo, overrideSubDrawTexture, overrideSubDrawCount, subDrawBase = 0) { const { device, resource } = splatInfo; const scope = device.scope; const subDrawTexture = overrideSubDrawTexture ?? splatInfo.subDrawTexture; const subDrawCount = overrideSubDrawCount ?? splatInfo.subDrawCount; const workBufferModifier = splatInfo.getWorkBufferModifier?.() ?? null; const formatHash = resource.format.hash; const formatDeclarations = resource.format.getInputDeclarations(); const workBufferRenderInfo = resource.getWorkBufferRenderInfo( this.colorOnly, workBufferModifier, formatHash, formatDeclarations, this.workBuffer.format ); workBufferRenderInfo.material.setParameters(device); const color = this.colorsByLod?.[splatInfo.lodIndex] ?? this.colorsByLod?.[0] ?? _whiteColor; scope.resolve("uColorMultiply").setValue(color); const worldTransform = splatInfo.node.getWorldTransform(); worldTransform.getScale(_modelScale); _modelRotation.setFromMat4(worldTransform); if (_modelRotation.w < 0) { _modelRotation.mulScalar(-1); } this._modelScaleData[0] = _modelScale.x; this._modelScaleData[1] = _modelScale.y; this._modelScaleData[2] = _modelScale.z; this._modelRotationData[0] = _modelRotation.x; this._modelRotationData[1] = _modelRotation.y; this._modelRotationData[2] = _modelRotation.z; this._modelRotationData[3] = _modelRotation.w; scope.resolve("matrix_model").setValue(worldTransform.data); scope.resolve("model_scale").setValue(this._modelScaleData); scope.resolve("model_rotation").setValue(this._modelRotationData); scope.resolve("uId").setValue(splatInfo.placementId); if (splatInfo.parameters) { for (const param of splatInfo.parameters.values()) { param.scopeId.setValue(param.data); } } const instanceStreams = splatInfo.getInstanceStreams?.(); if (instanceStreams) { instanceStreams.syncWithFormat(splatInfo.resource.format); for (const [name, texture] of instanceStreams.textures) { scope.resolve(name).setValue(texture); } } scope.resolve("uSubDrawData").setValue(subDrawTexture); scope.resolve("uSubDrawBase").setValue(subDrawBase); const ts = this.workBuffer.textureSize; this._textureSize[0] = ts; this._textureSize[1] = ts; scope.resolve("uTextureSize").setValue(this._textureSize); workBufferRenderInfo.quadRender.render(void 0, void 0, subDrawCount); } } export { GSplatWorkBufferRenderPass };