playcanvas
Version:
PlayCanvas WebGL game engine
258 lines (255 loc) • 7.84 kB
JavaScript
import { Quat } from '../../core/math/quat.js';
import { Vec3 } from '../../core/math/vec3.js';
import { Vec4 } from '../../core/math/vec4.js';
import { GSplatData } from './gsplat-data.js';
var SH_C0 = 0.28209479177387814;
class SplatCompressedIterator {
constructor(gsplatData, p, r, s, c, sh){
var unpackUnorm = (value, bits)=>{
var t = (1 << bits) - 1;
return (value & t) / t;
};
var unpack111011 = (result, value)=>{
result.x = unpackUnorm(value >>> 21, 11);
result.y = unpackUnorm(value >>> 11, 10);
result.z = unpackUnorm(value, 11);
};
var unpack8888 = (result, value)=>{
result.x = unpackUnorm(value >>> 24, 8);
result.y = unpackUnorm(value >>> 16, 8);
result.z = unpackUnorm(value >>> 8, 8);
result.w = unpackUnorm(value, 8);
};
var unpackRot = (result, value)=>{
var norm = 1.0 / (Math.sqrt(2) * 0.5);
var a = (unpackUnorm(value >>> 20, 10) - 0.5) * norm;
var b = (unpackUnorm(value >>> 10, 10) - 0.5) * norm;
var c = (unpackUnorm(value, 10) - 0.5) * norm;
var m = Math.sqrt(1.0 - (a * a + b * b + c * c));
switch(value >>> 30){
case 0:
result.set(a, b, c, m);
break;
case 1:
result.set(m, b, c, a);
break;
case 2:
result.set(b, m, c, a);
break;
case 3:
result.set(b, c, m, a);
break;
}
};
var lerp = (a, b, t)=>a * (1 - t) + b * t;
var { chunkData, chunkSize, vertexData, shData0, shData1, shData2, shBands } = gsplatData;
var shCoeffs = [
3,
8,
15
][shBands - 1];
this.read = (i)=>{
var ci = Math.floor(i / 256) * chunkSize;
if (p) {
unpack111011(p, vertexData[i * 4 + 0]);
p.x = lerp(chunkData[ci + 0], chunkData[ci + 3], p.x);
p.y = lerp(chunkData[ci + 1], chunkData[ci + 4], p.y);
p.z = lerp(chunkData[ci + 2], chunkData[ci + 5], p.z);
}
if (r) {
unpackRot(r, vertexData[i * 4 + 1]);
}
if (s) {
unpack111011(s, vertexData[i * 4 + 2]);
s.x = lerp(chunkData[ci + 6], chunkData[ci + 9], s.x);
s.y = lerp(chunkData[ci + 7], chunkData[ci + 10], s.y);
s.z = lerp(chunkData[ci + 8], chunkData[ci + 11], s.z);
}
if (c) {
unpack8888(c, vertexData[i * 4 + 3]);
if (chunkSize > 12) {
c.x = lerp(chunkData[ci + 12], chunkData[ci + 15], c.x);
c.y = lerp(chunkData[ci + 13], chunkData[ci + 16], c.y);
c.z = lerp(chunkData[ci + 14], chunkData[ci + 17], c.z);
}
}
if (sh && shBands > 0) {
var shData = [
shData0,
shData1,
shData2
];
for(var j = 0; j < 3; ++j){
for(var k = 0; k < 15; ++k){
sh[j * 15 + k] = k < shCoeffs ? shData[j][i * 16 + k] * (8 / 255) - 4 : 0;
}
}
}
};
}
}
class GSplatCompressedData {
createIter(p, r, s, c, sh) {
return new SplatCompressedIterator(this, p, r, s, c, sh);
}
calcAabb(result) {
var { chunkData, numChunks, chunkSize } = this;
var s = Math.exp(Math.max(chunkData[9], chunkData[10], chunkData[11]));
var mx = chunkData[0] - s;
var my = chunkData[1] - s;
var mz = chunkData[2] - s;
var Mx = chunkData[3] + s;
var My = chunkData[4] + s;
var Mz = chunkData[5] + s;
for(var i = 1; i < numChunks; ++i){
var off = i * chunkSize;
s = Math.exp(Math.max(chunkData[off + 9], chunkData[off + 10], chunkData[off + 11]));
mx = Math.min(mx, chunkData[off + 0] - s);
my = Math.min(my, chunkData[off + 1] - s);
mz = Math.min(mz, chunkData[off + 2] - s);
Mx = Math.max(Mx, chunkData[off + 3] + s);
My = Math.max(My, chunkData[off + 4] + s);
Mz = Math.max(Mz, chunkData[off + 5] + s);
}
result.center.set((mx + Mx) * 0.5, (my + My) * 0.5, (mz + Mz) * 0.5);
result.halfExtents.set((Mx - mx) * 0.5, (My - my) * 0.5, (Mz - mz) * 0.5);
return true;
}
getCenters(result) {
var { vertexData, chunkData, numChunks, chunkSize } = this;
var mx, my, mz, Mx, My, Mz;
for(var c = 0; c < numChunks; ++c){
var off = c * chunkSize;
mx = chunkData[off + 0];
my = chunkData[off + 1];
mz = chunkData[off + 2];
Mx = chunkData[off + 3];
My = chunkData[off + 4];
Mz = chunkData[off + 5];
var end = Math.min(this.numSplats, (c + 1) * 256);
for(var i = c * 256; i < end; ++i){
var p = vertexData[i * 4];
var px = (p >>> 21) / 2047;
var py = (p >>> 11 & 0x3ff) / 1023;
var pz = (p & 0x7ff) / 2047;
result[i * 3 + 0] = (1 - px) * mx + px * Mx;
result[i * 3 + 1] = (1 - py) * my + py * My;
result[i * 3 + 2] = (1 - pz) * mz + pz * Mz;
}
}
}
getChunks(result) {
var { chunkData, numChunks, chunkSize } = this;
var mx, my, mz, Mx, My, Mz;
for(var c = 0; c < numChunks; ++c){
var off = c * chunkSize;
mx = chunkData[off + 0];
my = chunkData[off + 1];
mz = chunkData[off + 2];
Mx = chunkData[off + 3];
My = chunkData[off + 4];
Mz = chunkData[off + 5];
result[c * 6 + 0] = mx;
result[c * 6 + 1] = my;
result[c * 6 + 2] = mz;
result[c * 6 + 3] = Mx;
result[c * 6 + 4] = My;
result[c * 6 + 5] = Mz;
}
}
calcFocalPoint(result) {
var { chunkData, numChunks, chunkSize } = this;
result.x = 0;
result.y = 0;
result.z = 0;
for(var i = 0; i < numChunks; ++i){
var off = i * chunkSize;
result.x += chunkData[off + 0] + chunkData[off + 3];
result.y += chunkData[off + 1] + chunkData[off + 4];
result.z += chunkData[off + 2] + chunkData[off + 5];
}
result.mulScalar(0.5 / numChunks);
}
get isCompressed() {
return true;
}
get numChunks() {
return Math.ceil(this.numSplats / 256);
}
get chunkSize() {
return this.chunkData.length / this.numChunks;
}
decompress() {
var members = [
'x',
'y',
'z',
'f_dc_0',
'f_dc_1',
'f_dc_2',
'opacity',
'scale_0',
'scale_1',
'scale_2',
'rot_0',
'rot_1',
'rot_2',
'rot_3'
];
var { shBands } = this;
if (shBands > 0) {
var shMembers = [];
for(var i = 0; i < 45; ++i){
shMembers.push("f_rest_" + i);
}
members.splice(members.indexOf('f_dc_0') + 1, 0, ...shMembers);
}
var data = {};
members.forEach((name)=>{
data[name] = new Float32Array(this.numSplats);
});
var p = new Vec3();
var r = new Quat();
var s = new Vec3();
var c = new Vec4();
var sh = shBands > 0 ? new Float32Array(45) : null;
var iter = this.createIter(p, r, s, c, sh);
for(var i1 = 0; i1 < this.numSplats; ++i1){
iter.read(i1);
data.x[i1] = p.x;
data.y[i1] = p.y;
data.z[i1] = p.z;
data.rot_1[i1] = r.x;
data.rot_2[i1] = r.y;
data.rot_3[i1] = r.z;
data.rot_0[i1] = r.w;
data.scale_0[i1] = s.x;
data.scale_1[i1] = s.y;
data.scale_2[i1] = s.z;
data.f_dc_0[i1] = (c.x - 0.5) / SH_C0;
data.f_dc_1[i1] = (c.y - 0.5) / SH_C0;
data.f_dc_2[i1] = (c.z - 0.5) / SH_C0;
data.opacity[i1] = c.w <= 0 ? -40 : c.w >= 1 ? 40 : -Math.log(1 / c.w - 1);
if (sh) {
for(var c1 = 0; c1 < 45; ++c1){
data["f_rest_" + c1][i1] = sh[c1];
}
}
}
return new GSplatData([
{
name: 'vertex',
count: this.numSplats,
properties: members.map((name)=>{
return {
name: name,
type: 'float',
byteSize: 4,
storage: data[name]
};
})
}
]);
}
}
export { GSplatCompressedData };