UNPKG

playcanvas

Version:

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

159 lines (156 loc) 6.67 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 { 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(undefined, undefined, subDrawCount); } constructor(device, workBuffer, colorOnly = false){ super(device), this.splats = [], this.colorsByLod = undefined, this.cameraNode = null, this._modelScaleData = new Float32Array(3), this._modelRotationData = new Float32Array(4), this._textureSize = new Int32Array(2), this._partialData = []; this.workBuffer = workBuffer; this.colorOnly = colorOnly; this._subDrawTexture = Texture.createDataTexture2D(device, 'GsplatSubDrawData', 1, 1, PIXELFORMAT_RGBA32U); } } export { GSplatWorkBufferRenderPass };