playcanvas
Version:
PlayCanvas WebGL game engine
104 lines (101 loc) • 3.98 kB
JavaScript
import { SEMANTIC_ATTR13, SEMANTIC_POSITION, CULLFACE_NONE } from '../../platform/graphics/constants.js';
import { BLEND_PREMULTIPLIED } from '../constants.js';
import { ShaderMaterial } from '../materials/shader-material.js';
import { GSplatResourceBase } from '../gsplat/gsplat-resource-base.js';
import { MeshInstance } from '../mesh-instance.js';
import { math } from '../../core/math/math.js';
class GSplatRenderer {
constructor(device, node, cameraNode, layer, workBuffer){
this.instanceIndices = null;
this.instanceIndicesCount = 0;
this.viewportParams = [
0,
0
];
this.device = device;
this.node = node;
this.cameraNode = cameraNode;
this.layer = layer;
this.workBuffer = workBuffer;
this._material = new ShaderMaterial({
uniqueName: 'UnifiedSplatMaterial',
vertexGLSL: '#include "gsplatVS"',
fragmentGLSL: '#include "gsplatPS"',
vertexWGSL: '#include "gsplatVS"',
fragmentWGSL: '#include "gsplatPS"',
attributes: {
vertex_position: SEMANTIC_POSITION,
vertex_id_attrib: SEMANTIC_ATTR13
}
});
this._material.setDefine('GSPLAT_WORKBUFFER_DATA', true);
this._material.setParameter('splatColor', workBuffer.colorTexture);
this._material.setParameter('covA', workBuffer.covATexture);
this._material.setParameter('covB', workBuffer.covBTexture);
this._material.setParameter('center', workBuffer.centerTexture);
this._material.setDefine('SH_BANDS', '0');
this._material.setParameter('numSplats', 0);
this._material.setParameter('splatOrder', workBuffer.orderTexture);
this._material.setParameter('alphaClip', 0.3);
this._material.setDefine(`DITHER_${'NONE'}`, '');
this._material.cull = CULLFACE_NONE;
this._material.blendType = BLEND_PREMULTIPLIED;
this._material.depthWrite = false;
this._material.update();
this.meshInstance = this.createMeshInstance();
layer.addMeshInstances([
this.meshInstance
]);
}
destroy() {
this.layer.removeMeshInstances([
this.meshInstance
]);
this._material.destroy();
this.meshInstance.destroy();
}
setNumSplats(count) {
this.meshInstance.instancingCount = Math.ceil(count / GSplatResourceBase.instanceSize);
this._material.setParameter('numSplats', count);
this.meshInstance.visible = count > 0;
}
setMaxNumSplats(numSplats) {
const roundedNumSplats = math.roundUp(numSplats, GSplatResourceBase.instanceSize);
if (this.instanceIndicesCount < roundedNumSplats) {
this.instanceIndicesCount = roundedNumSplats;
this.instanceIndices?.destroy();
this.instanceIndices = GSplatResourceBase.createInstanceIndices(this.device, numSplats);
this.meshInstance.setInstancing(this.instanceIndices, true);
}
}
createMeshInstance() {
const mesh = GSplatResourceBase.createMesh(this.device);
const textureSize = this.workBuffer.textureSize;
const instanceIndices = GSplatResourceBase.createInstanceIndices(this.device, textureSize * textureSize);
const meshInstance = new MeshInstance(mesh, this._material);
meshInstance.node = this.node;
meshInstance.setInstancing(instanceIndices, true);
meshInstance.instancingCount = 0;
const thisCamera = this.cameraNode.camera;
meshInstance.isVisibleFunc = (camera)=>{
const vis = thisCamera.camera === camera;
return vis;
};
return meshInstance;
}
updateViewport(cameraNode) {
const camera = cameraNode.camera;
const cameraRect = camera.rect;
const renderTarget = camera?.renderTarget;
const { width, height } = renderTarget ?? this.device;
const viewport = this.viewportParams;
viewport[0] = width * cameraRect.z;
viewport[1] = height * cameraRect.w;
const xr = camera?.camera?.xr;
if (xr?.active && xr.views.list.length === 2) {
viewport[0] *= 0.5;
}
this._material.setParameter('viewport', viewport);
}
}
export { GSplatRenderer };