@babylonjs/core 7.31.1 → 7.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Engines/abstractEngine.js +2 -2
- package/Engines/abstractEngine.js.map +1 -1
- package/Materials/standardMaterial.js +1 -3
- package/Materials/standardMaterial.js.map +1 -1
- package/Maths/math.functions.d.ts +6 -0
- package/Maths/math.functions.js +42 -0
- package/Maths/math.functions.js.map +1 -1
- package/Meshes/GaussianSplatting/gaussianSplattingMesh.d.ts +144 -4
- package/Meshes/GaussianSplatting/gaussianSplattingMesh.js +574 -309
- package/Meshes/GaussianSplatting/gaussianSplattingMesh.js.map +1 -1
- package/Meshes/Node/Blocks/cleanGeometryBlock.d.ts +39 -0
- package/Meshes/Node/Blocks/cleanGeometryBlock.js +87 -0
- package/Meshes/Node/Blocks/cleanGeometryBlock.js.map +1 -0
- package/Meshes/Node/Blocks/geometryTransformBlock.js +4 -4
- package/Meshes/Node/Blocks/geometryTransformBlock.js.map +1 -1
- package/Meshes/Node/index.d.ts +1 -0
- package/Meshes/Node/index.js +1 -0
- package/Meshes/Node/index.js.map +1 -1
- package/Meshes/Node/nodeGeometry.js +7 -3
- package/Meshes/Node/nodeGeometry.js.map +1 -1
- package/Meshes/Node/nodeGeometryBlock.js +0 -9
- package/Meshes/Node/nodeGeometryBlock.js.map +1 -1
- package/Shaders/ShadersInclude/defaultFragmentDeclaration.js +1 -5
- package/Shaders/ShadersInclude/defaultFragmentDeclaration.js.map +1 -1
- package/Shaders/default.fragment.js +1 -7
- package/Shaders/default.fragment.js.map +1 -1
- package/ShadersWGSL/default.fragment.js +1 -8
- package/ShadersWGSL/default.fragment.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SubMesh } from "../subMesh.js";
|
|
2
2
|
import { Mesh } from "../mesh.js";
|
|
3
3
|
import { VertexData } from "../mesh.vertexData.js";
|
|
4
|
-
import { Matrix,
|
|
4
|
+
import { Matrix, TmpVectors, Vector2, Vector3 } from "../../Maths/math.vector.js";
|
|
5
5
|
import { Logger } from "../../Misc/logger.js";
|
|
6
6
|
import { GaussianSplattingMaterial } from "../../Materials/GaussianSplatting/gaussianSplattingMaterial.js";
|
|
7
7
|
import { RawTexture } from "../../Materials/Textures/rawTexture.js";
|
|
@@ -10,6 +10,101 @@ import { Tools } from "../../Misc/tools.js";
|
|
|
10
10
|
import "../thinInstanceMesh.js";
|
|
11
11
|
import { ToHalfFloat } from "../../Misc/textureTools.js";
|
|
12
12
|
import { Scalar } from "../../Maths/math.scalar.js";
|
|
13
|
+
import { runCoroutineSync, runCoroutineAsync, createYieldingScheduler } from "../../Misc/coroutine.js";
|
|
14
|
+
// @internal
|
|
15
|
+
const unpackUnorm = (value, bits) => {
|
|
16
|
+
const t = (1 << bits) - 1;
|
|
17
|
+
return (value & t) / t;
|
|
18
|
+
};
|
|
19
|
+
// @internal
|
|
20
|
+
const unpack111011 = (value, result) => {
|
|
21
|
+
result.x = unpackUnorm(value >>> 21, 11);
|
|
22
|
+
result.y = unpackUnorm(value >>> 11, 10);
|
|
23
|
+
result.z = unpackUnorm(value, 11);
|
|
24
|
+
};
|
|
25
|
+
// @internal
|
|
26
|
+
const unpack8888 = (value, result) => {
|
|
27
|
+
result[0] = unpackUnorm(value >>> 24, 8) * 255;
|
|
28
|
+
result[1] = unpackUnorm(value >>> 16, 8) * 255;
|
|
29
|
+
result[2] = unpackUnorm(value >>> 8, 8) * 255;
|
|
30
|
+
result[3] = unpackUnorm(value, 8) * 255;
|
|
31
|
+
};
|
|
32
|
+
// @internal
|
|
33
|
+
// unpack quaternion with 2,10,10,10 format (largest element, 3x10bit element)
|
|
34
|
+
const unpackRot = (value, result) => {
|
|
35
|
+
const norm = 1.0 / (Math.sqrt(2) * 0.5);
|
|
36
|
+
const a = (unpackUnorm(value >>> 20, 10) - 0.5) * norm;
|
|
37
|
+
const b = (unpackUnorm(value >>> 10, 10) - 0.5) * norm;
|
|
38
|
+
const c = (unpackUnorm(value, 10) - 0.5) * norm;
|
|
39
|
+
const m = Math.sqrt(1.0 - (a * a + b * b + c * c));
|
|
40
|
+
switch (value >>> 30) {
|
|
41
|
+
case 0:
|
|
42
|
+
result.set(m, a, b, c);
|
|
43
|
+
break;
|
|
44
|
+
case 1:
|
|
45
|
+
result.set(a, m, b, c);
|
|
46
|
+
break;
|
|
47
|
+
case 2:
|
|
48
|
+
result.set(a, b, m, c);
|
|
49
|
+
break;
|
|
50
|
+
case 3:
|
|
51
|
+
result.set(a, b, c, m);
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Representation of the types
|
|
57
|
+
*/
|
|
58
|
+
var PLYType;
|
|
59
|
+
(function (PLYType) {
|
|
60
|
+
PLYType[PLYType["FLOAT"] = 0] = "FLOAT";
|
|
61
|
+
PLYType[PLYType["INT"] = 1] = "INT";
|
|
62
|
+
PLYType[PLYType["UINT"] = 2] = "UINT";
|
|
63
|
+
PLYType[PLYType["DOUBLE"] = 3] = "DOUBLE";
|
|
64
|
+
PLYType[PLYType["UCHAR"] = 4] = "UCHAR";
|
|
65
|
+
PLYType[PLYType["UNDEFINED"] = 5] = "UNDEFINED";
|
|
66
|
+
})(PLYType || (PLYType = {}));
|
|
67
|
+
/**
|
|
68
|
+
* Usage types of the PLY values
|
|
69
|
+
*/
|
|
70
|
+
var PLYValue;
|
|
71
|
+
(function (PLYValue) {
|
|
72
|
+
PLYValue[PLYValue["MIN_X"] = 0] = "MIN_X";
|
|
73
|
+
PLYValue[PLYValue["MIN_Y"] = 1] = "MIN_Y";
|
|
74
|
+
PLYValue[PLYValue["MIN_Z"] = 2] = "MIN_Z";
|
|
75
|
+
PLYValue[PLYValue["MAX_X"] = 3] = "MAX_X";
|
|
76
|
+
PLYValue[PLYValue["MAX_Y"] = 4] = "MAX_Y";
|
|
77
|
+
PLYValue[PLYValue["MAX_Z"] = 5] = "MAX_Z";
|
|
78
|
+
PLYValue[PLYValue["MIN_SCALE_X"] = 6] = "MIN_SCALE_X";
|
|
79
|
+
PLYValue[PLYValue["MIN_SCALE_Y"] = 7] = "MIN_SCALE_Y";
|
|
80
|
+
PLYValue[PLYValue["MIN_SCALE_Z"] = 8] = "MIN_SCALE_Z";
|
|
81
|
+
PLYValue[PLYValue["MAX_SCALE_X"] = 9] = "MAX_SCALE_X";
|
|
82
|
+
PLYValue[PLYValue["MAX_SCALE_Y"] = 10] = "MAX_SCALE_Y";
|
|
83
|
+
PLYValue[PLYValue["MAX_SCALE_Z"] = 11] = "MAX_SCALE_Z";
|
|
84
|
+
PLYValue[PLYValue["PACKED_POSITION"] = 12] = "PACKED_POSITION";
|
|
85
|
+
PLYValue[PLYValue["PACKED_ROTATION"] = 13] = "PACKED_ROTATION";
|
|
86
|
+
PLYValue[PLYValue["PACKED_SCALE"] = 14] = "PACKED_SCALE";
|
|
87
|
+
PLYValue[PLYValue["PACKED_COLOR"] = 15] = "PACKED_COLOR";
|
|
88
|
+
PLYValue[PLYValue["X"] = 16] = "X";
|
|
89
|
+
PLYValue[PLYValue["Y"] = 17] = "Y";
|
|
90
|
+
PLYValue[PLYValue["Z"] = 18] = "Z";
|
|
91
|
+
PLYValue[PLYValue["SCALE_0"] = 19] = "SCALE_0";
|
|
92
|
+
PLYValue[PLYValue["SCALE_1"] = 20] = "SCALE_1";
|
|
93
|
+
PLYValue[PLYValue["SCALE_2"] = 21] = "SCALE_2";
|
|
94
|
+
PLYValue[PLYValue["DIFFUSE_RED"] = 22] = "DIFFUSE_RED";
|
|
95
|
+
PLYValue[PLYValue["DIFFUSE_GREEN"] = 23] = "DIFFUSE_GREEN";
|
|
96
|
+
PLYValue[PLYValue["DIFFUSE_BLUE"] = 24] = "DIFFUSE_BLUE";
|
|
97
|
+
PLYValue[PLYValue["OPACITY"] = 25] = "OPACITY";
|
|
98
|
+
PLYValue[PLYValue["F_DC_0"] = 26] = "F_DC_0";
|
|
99
|
+
PLYValue[PLYValue["F_DC_1"] = 27] = "F_DC_1";
|
|
100
|
+
PLYValue[PLYValue["F_DC_2"] = 28] = "F_DC_2";
|
|
101
|
+
PLYValue[PLYValue["F_DC_3"] = 29] = "F_DC_3";
|
|
102
|
+
PLYValue[PLYValue["ROT_0"] = 30] = "ROT_0";
|
|
103
|
+
PLYValue[PLYValue["ROT_1"] = 31] = "ROT_1";
|
|
104
|
+
PLYValue[PLYValue["ROT_2"] = 32] = "ROT_2";
|
|
105
|
+
PLYValue[PLYValue["ROT_3"] = 33] = "ROT_3";
|
|
106
|
+
PLYValue[PLYValue["UNDEFINED"] = 34] = "UNDEFINED";
|
|
107
|
+
})(PLYValue || (PLYValue = {}));
|
|
13
108
|
/**
|
|
14
109
|
* Class used to render a gaussian splatting mesh
|
|
15
110
|
*/
|
|
@@ -45,6 +140,7 @@ export class GaussianSplattingMesh extends Mesh {
|
|
|
45
140
|
this._material = value;
|
|
46
141
|
this._material.backFaceCulling = true;
|
|
47
142
|
this._material.cullBackFaces = false;
|
|
143
|
+
value.resetDrawCache();
|
|
48
144
|
}
|
|
49
145
|
/**
|
|
50
146
|
* get rendering material
|
|
@@ -84,12 +180,21 @@ export class GaussianSplattingMesh extends Mesh {
|
|
|
84
180
|
this._oldDirection = new Vector3();
|
|
85
181
|
this._useRGBACovariants = false;
|
|
86
182
|
this._material = null;
|
|
183
|
+
this._tmpCovariances = [0, 0, 0, 0, 0, 0];
|
|
184
|
+
this._sortIsDirty = false;
|
|
87
185
|
const vertexData = new VertexData();
|
|
88
|
-
|
|
89
|
-
|
|
186
|
+
// Use an intanced quad or triangle. Triangle might be a bit faster because of less shader invocation but I didn't see any difference.
|
|
187
|
+
// Keeping both and use triangle for now.
|
|
188
|
+
// for quad, use following lines
|
|
189
|
+
//vertexData.positions = [-2, -2, 0, 2, -2, 0, 2, 2, 0, -2, 2, 0];
|
|
190
|
+
//vertexData.indices = [0, 1, 2, 0, 2, 3];
|
|
191
|
+
vertexData.positions = [-3, -2, 0, 3, -2, 0, 0, 4, 0];
|
|
192
|
+
vertexData.indices = [0, 1, 2];
|
|
90
193
|
vertexData.applyToMesh(this);
|
|
91
194
|
this.subMeshes = [];
|
|
92
|
-
|
|
195
|
+
// for quad, use following line
|
|
196
|
+
//new SubMesh(0, 0, 4, 0, 6, this);
|
|
197
|
+
new SubMesh(0, 0, 3, 0, 3, this);
|
|
93
198
|
this.setEnabled(false);
|
|
94
199
|
// webGL2 and webGPU support for RG texture with float16 is fine. not webGL1
|
|
95
200
|
this._useRGBACovariants = !this.getEngine().isWebGPU && this.getEngine().version === 1.0;
|
|
@@ -131,7 +236,7 @@ export class GaussianSplattingMesh extends Mesh {
|
|
|
131
236
|
}
|
|
132
237
|
_postToWorker(forced = false) {
|
|
133
238
|
const frameId = this.getScene().getFrameId();
|
|
134
|
-
if (frameId !== this._frameIdLastUpdate && this._worker && this._scene.activeCamera && this._canPostToWorker) {
|
|
239
|
+
if ((forced || frameId !== this._frameIdLastUpdate) && this._worker && this._scene.activeCamera && this._canPostToWorker) {
|
|
135
240
|
const cameraMatrix = this._scene.activeCamera.getViewMatrix();
|
|
136
241
|
this.getWorldMatrix().multiplyToRef(cameraMatrix, this._modelViewMatrix);
|
|
137
242
|
cameraMatrix.invertToRef(TmpVectors.Matrix[0]);
|
|
@@ -160,22 +265,111 @@ export class GaussianSplattingMesh extends Mesh {
|
|
|
160
265
|
this._postToWorker();
|
|
161
266
|
return super.render(subMesh, enableAlphaMode, effectiveMeshReplacement);
|
|
162
267
|
}
|
|
268
|
+
static _TypeNameToEnum(name) {
|
|
269
|
+
switch (name) {
|
|
270
|
+
case "float":
|
|
271
|
+
return 0 /* PLYType.FLOAT */;
|
|
272
|
+
case "int":
|
|
273
|
+
return 1 /* PLYType.INT */;
|
|
274
|
+
break;
|
|
275
|
+
case "uint":
|
|
276
|
+
return 2 /* PLYType.UINT */;
|
|
277
|
+
case "double":
|
|
278
|
+
return 3 /* PLYType.DOUBLE */;
|
|
279
|
+
case "uchar":
|
|
280
|
+
return 4 /* PLYType.UCHAR */;
|
|
281
|
+
}
|
|
282
|
+
return 5 /* PLYType.UNDEFINED */;
|
|
283
|
+
}
|
|
284
|
+
static _ValueNameToEnum(name) {
|
|
285
|
+
switch (name) {
|
|
286
|
+
case "min_x":
|
|
287
|
+
return 0 /* PLYValue.MIN_X */;
|
|
288
|
+
case "min_y":
|
|
289
|
+
return 1 /* PLYValue.MIN_Y */;
|
|
290
|
+
case "min_z":
|
|
291
|
+
return 2 /* PLYValue.MIN_Z */;
|
|
292
|
+
case "max_x":
|
|
293
|
+
return 3 /* PLYValue.MAX_X */;
|
|
294
|
+
case "max_y":
|
|
295
|
+
return 4 /* PLYValue.MAX_Y */;
|
|
296
|
+
case "max_z":
|
|
297
|
+
return 5 /* PLYValue.MAX_Z */;
|
|
298
|
+
case "min_scale_x":
|
|
299
|
+
return 6 /* PLYValue.MIN_SCALE_X */;
|
|
300
|
+
case "min_scale_y":
|
|
301
|
+
return 7 /* PLYValue.MIN_SCALE_Y */;
|
|
302
|
+
case "min_scale_z":
|
|
303
|
+
return 8 /* PLYValue.MIN_SCALE_Z */;
|
|
304
|
+
case "max_scale_x":
|
|
305
|
+
return 9 /* PLYValue.MAX_SCALE_X */;
|
|
306
|
+
case "max_scale_y":
|
|
307
|
+
return 10 /* PLYValue.MAX_SCALE_Y */;
|
|
308
|
+
case "max_scale_z":
|
|
309
|
+
return 11 /* PLYValue.MAX_SCALE_Z */;
|
|
310
|
+
case "packed_position":
|
|
311
|
+
return 12 /* PLYValue.PACKED_POSITION */;
|
|
312
|
+
case "packed_rotation":
|
|
313
|
+
return 13 /* PLYValue.PACKED_ROTATION */;
|
|
314
|
+
case "packed_scale":
|
|
315
|
+
return 14 /* PLYValue.PACKED_SCALE */;
|
|
316
|
+
case "packed_color":
|
|
317
|
+
return 15 /* PLYValue.PACKED_COLOR */;
|
|
318
|
+
case "x":
|
|
319
|
+
return 16 /* PLYValue.X */;
|
|
320
|
+
case "y":
|
|
321
|
+
return 17 /* PLYValue.Y */;
|
|
322
|
+
case "z":
|
|
323
|
+
return 18 /* PLYValue.Z */;
|
|
324
|
+
case "scale_0":
|
|
325
|
+
return 19 /* PLYValue.SCALE_0 */;
|
|
326
|
+
case "scale_1":
|
|
327
|
+
return 20 /* PLYValue.SCALE_1 */;
|
|
328
|
+
case "scale_2":
|
|
329
|
+
return 21 /* PLYValue.SCALE_2 */;
|
|
330
|
+
case "diffuse_red":
|
|
331
|
+
case "red":
|
|
332
|
+
return 22 /* PLYValue.DIFFUSE_RED */;
|
|
333
|
+
case "diffuse_green":
|
|
334
|
+
case "green":
|
|
335
|
+
return 23 /* PLYValue.DIFFUSE_GREEN */;
|
|
336
|
+
case "diffuse_blue":
|
|
337
|
+
case "blue":
|
|
338
|
+
return 24 /* PLYValue.DIFFUSE_BLUE */;
|
|
339
|
+
case "f_dc_0":
|
|
340
|
+
return 26 /* PLYValue.F_DC_0 */;
|
|
341
|
+
case "f_dc_1":
|
|
342
|
+
return 27 /* PLYValue.F_DC_1 */;
|
|
343
|
+
case "f_dc_2":
|
|
344
|
+
return 28 /* PLYValue.F_DC_2 */;
|
|
345
|
+
case "f_dc_3":
|
|
346
|
+
return 29 /* PLYValue.F_DC_3 */;
|
|
347
|
+
case "opacity":
|
|
348
|
+
return 25 /* PLYValue.OPACITY */;
|
|
349
|
+
case "rot_0":
|
|
350
|
+
return 30 /* PLYValue.ROT_0 */;
|
|
351
|
+
case "rot_1":
|
|
352
|
+
return 31 /* PLYValue.ROT_1 */;
|
|
353
|
+
case "rot_2":
|
|
354
|
+
return 32 /* PLYValue.ROT_2 */;
|
|
355
|
+
case "rot_3":
|
|
356
|
+
return 33 /* PLYValue.ROT_3 */;
|
|
357
|
+
}
|
|
358
|
+
return 34 /* PLYValue.UNDEFINED */;
|
|
359
|
+
}
|
|
163
360
|
/**
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
* @param data the .ply data to load
|
|
168
|
-
* @returns the loaded splat buffer
|
|
169
|
-
* @deprecated Please use SceneLoader.ImportMeshAsync instead
|
|
361
|
+
* Parse a PLY file header and returns metas infos on splats and chunks
|
|
362
|
+
* @param data the loaded buffer
|
|
363
|
+
* @returns a PLYHeader
|
|
170
364
|
*/
|
|
171
|
-
static
|
|
365
|
+
static ParseHeader(data) {
|
|
172
366
|
const ubuf = new Uint8Array(data);
|
|
173
367
|
const header = new TextDecoder().decode(ubuf.slice(0, 1024 * 10));
|
|
174
368
|
const headerEnd = "end_header\n";
|
|
175
369
|
const headerEndIndex = header.indexOf(headerEnd);
|
|
176
370
|
if (headerEndIndex < 0 || !header) {
|
|
177
371
|
// standard splat
|
|
178
|
-
return
|
|
372
|
+
return null;
|
|
179
373
|
}
|
|
180
374
|
const vertexCount = parseInt(/element vertex (\d+)\n/.exec(header)[1]);
|
|
181
375
|
const chunkElement = /element chunk (\d+)\n/.exec(header);
|
|
@@ -206,17 +400,19 @@ export class GaussianSplattingMesh extends Mesh {
|
|
|
206
400
|
const filtered = header.slice(0, headerEndIndex).split("\n");
|
|
207
401
|
for (const prop of filtered) {
|
|
208
402
|
if (prop.startsWith("property ")) {
|
|
209
|
-
const [,
|
|
403
|
+
const [, typeName, name] = prop.split(" ");
|
|
404
|
+
const value = GaussianSplattingMesh._ValueNameToEnum(name);
|
|
405
|
+
const type = GaussianSplattingMesh._TypeNameToEnum(typeName);
|
|
210
406
|
if (chunkMode == 1 /* ElementMode.Chunk */) {
|
|
211
|
-
chunkProperties.push({
|
|
212
|
-
rowChunkOffset += offsets[
|
|
407
|
+
chunkProperties.push({ value, type, offset: rowChunkOffset });
|
|
408
|
+
rowChunkOffset += offsets[typeName];
|
|
213
409
|
}
|
|
214
410
|
else if (chunkMode == 0 /* ElementMode.Vertex */) {
|
|
215
|
-
vertexProperties.push({
|
|
216
|
-
rowVertexOffset += offsets[
|
|
411
|
+
vertexProperties.push({ value, type, offset: rowVertexOffset });
|
|
412
|
+
rowVertexOffset += offsets[typeName];
|
|
217
413
|
}
|
|
218
|
-
if (!offsets[
|
|
219
|
-
Logger.Warn(`Unsupported property type: ${
|
|
414
|
+
if (!offsets[typeName]) {
|
|
415
|
+
Logger.Warn(`Unsupported property type: ${typeName}.`);
|
|
220
416
|
}
|
|
221
417
|
}
|
|
222
418
|
else if (prop.startsWith("element ")) {
|
|
@@ -229,239 +425,243 @@ export class GaussianSplattingMesh extends Mesh {
|
|
|
229
425
|
}
|
|
230
426
|
}
|
|
231
427
|
}
|
|
232
|
-
const rowVertexLength = rowVertexOffset;
|
|
233
|
-
const rowChunkLength = rowChunkOffset;
|
|
234
|
-
const rowOutputLength = 3 * 4 + 3 * 4 + 4 + 4; // Vector3 position, Vector3 scale, 1 u8 quaternion, 1 color with alpha
|
|
235
|
-
const SH_C0 = 0.28209479177387814;
|
|
236
|
-
let offset = 0;
|
|
237
428
|
const dataView = new DataView(data, headerEndIndex + headerEnd.length);
|
|
238
|
-
const buffer = new ArrayBuffer(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
result.z = unpackUnorm(value, 11);
|
|
429
|
+
const buffer = new ArrayBuffer(GaussianSplattingMesh._RowOutputLength * vertexCount);
|
|
430
|
+
return {
|
|
431
|
+
vertexCount: vertexCount,
|
|
432
|
+
chunkCount: chunkCount,
|
|
433
|
+
rowVertexLength: rowVertexOffset,
|
|
434
|
+
rowChunkLength: rowChunkOffset,
|
|
435
|
+
vertexProperties: vertexProperties,
|
|
436
|
+
chunkProperties: chunkProperties,
|
|
437
|
+
dataView: dataView,
|
|
438
|
+
buffer: buffer,
|
|
249
439
|
};
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const norm = 1.0 / (Math.sqrt(2) * 0.5);
|
|
259
|
-
const a = (unpackUnorm(value >>> 20, 10) - 0.5) * norm;
|
|
260
|
-
const b = (unpackUnorm(value >>> 10, 10) - 0.5) * norm;
|
|
261
|
-
const c = (unpackUnorm(value, 10) - 0.5) * norm;
|
|
262
|
-
const m = Math.sqrt(1.0 - (a * a + b * b + c * c));
|
|
263
|
-
switch (value >>> 30) {
|
|
264
|
-
case 0:
|
|
265
|
-
result.set(m, a, b, c);
|
|
266
|
-
break;
|
|
267
|
-
case 1:
|
|
268
|
-
result.set(a, m, b, c);
|
|
269
|
-
break;
|
|
270
|
-
case 2:
|
|
271
|
-
result.set(a, b, m, c);
|
|
272
|
-
break;
|
|
273
|
-
case 3:
|
|
274
|
-
result.set(a, b, c, m);
|
|
275
|
-
break;
|
|
276
|
-
}
|
|
277
|
-
};
|
|
278
|
-
const compressedChunks = new Array(chunkCount);
|
|
279
|
-
for (let i = 0; i < chunkCount; i++) {
|
|
440
|
+
}
|
|
441
|
+
static _GetCompressedChunks(header, offset) {
|
|
442
|
+
if (!header.chunkCount) {
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
const dataView = header.dataView;
|
|
446
|
+
const compressedChunks = new Array(header.chunkCount);
|
|
447
|
+
for (let i = 0; i < header.chunkCount; i++) {
|
|
280
448
|
const currentChunk = { min: new Vector3(), max: new Vector3(), minScale: new Vector3(), maxScale: new Vector3() };
|
|
281
449
|
compressedChunks[i] = currentChunk;
|
|
282
|
-
for (let propertyIndex = 0; propertyIndex < chunkProperties.length; propertyIndex++) {
|
|
283
|
-
const property = chunkProperties[propertyIndex];
|
|
450
|
+
for (let propertyIndex = 0; propertyIndex < header.chunkProperties.length; propertyIndex++) {
|
|
451
|
+
const property = header.chunkProperties[propertyIndex];
|
|
284
452
|
let value;
|
|
285
453
|
switch (property.type) {
|
|
286
|
-
case
|
|
287
|
-
value = dataView.getFloat32(property.offset + offset, true);
|
|
454
|
+
case 0 /* PLYType.FLOAT */:
|
|
455
|
+
value = dataView.getFloat32(property.offset + offset.value, true);
|
|
288
456
|
break;
|
|
289
457
|
default:
|
|
290
458
|
continue;
|
|
291
459
|
}
|
|
292
|
-
switch (property.
|
|
293
|
-
case
|
|
460
|
+
switch (property.value) {
|
|
461
|
+
case 0 /* PLYValue.MIN_X */:
|
|
294
462
|
currentChunk.min.x = value;
|
|
295
463
|
break;
|
|
296
|
-
case
|
|
464
|
+
case 1 /* PLYValue.MIN_Y */:
|
|
297
465
|
currentChunk.min.y = value;
|
|
298
466
|
break;
|
|
299
|
-
case
|
|
467
|
+
case 2 /* PLYValue.MIN_Z */:
|
|
300
468
|
currentChunk.min.z = value;
|
|
301
469
|
break;
|
|
302
|
-
case
|
|
470
|
+
case 3 /* PLYValue.MAX_X */:
|
|
303
471
|
currentChunk.max.x = value;
|
|
304
472
|
break;
|
|
305
|
-
case
|
|
473
|
+
case 4 /* PLYValue.MAX_Y */:
|
|
306
474
|
currentChunk.max.y = value;
|
|
307
475
|
break;
|
|
308
|
-
case
|
|
476
|
+
case 5 /* PLYValue.MAX_Z */:
|
|
309
477
|
currentChunk.max.z = value;
|
|
310
478
|
break;
|
|
311
|
-
case
|
|
479
|
+
case 6 /* PLYValue.MIN_SCALE_X */:
|
|
312
480
|
currentChunk.minScale.x = value;
|
|
313
481
|
break;
|
|
314
|
-
case
|
|
482
|
+
case 7 /* PLYValue.MIN_SCALE_Y */:
|
|
315
483
|
currentChunk.minScale.y = value;
|
|
316
484
|
break;
|
|
317
|
-
case
|
|
485
|
+
case 8 /* PLYValue.MIN_SCALE_Z */:
|
|
318
486
|
currentChunk.minScale.z = value;
|
|
319
487
|
break;
|
|
320
|
-
case
|
|
488
|
+
case 9 /* PLYValue.MAX_SCALE_X */:
|
|
321
489
|
currentChunk.maxScale.x = value;
|
|
322
490
|
break;
|
|
323
|
-
case
|
|
491
|
+
case 10 /* PLYValue.MAX_SCALE_Y */:
|
|
324
492
|
currentChunk.maxScale.y = value;
|
|
325
493
|
break;
|
|
326
|
-
case
|
|
494
|
+
case 11 /* PLYValue.MAX_SCALE_Z */:
|
|
327
495
|
currentChunk.maxScale.z = value;
|
|
328
496
|
break;
|
|
329
497
|
}
|
|
330
498
|
}
|
|
331
|
-
offset += rowChunkLength;
|
|
499
|
+
offset.value += header.rowChunkLength;
|
|
332
500
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
501
|
+
return compressedChunks;
|
|
502
|
+
}
|
|
503
|
+
static _GetSplat(header, index, compressedChunks, offset) {
|
|
504
|
+
const q = TmpVectors.Quaternion[0];
|
|
505
|
+
const temp3 = TmpVectors.Vector3[0];
|
|
506
|
+
const rowOutputLength = GaussianSplattingMesh._RowOutputLength;
|
|
507
|
+
const buffer = header.buffer;
|
|
508
|
+
const dataView = header.dataView;
|
|
509
|
+
const position = new Float32Array(buffer, index * rowOutputLength, 3);
|
|
510
|
+
const scale = new Float32Array(buffer, index * rowOutputLength + 12, 3);
|
|
511
|
+
const rgba = new Uint8ClampedArray(buffer, index * rowOutputLength + 24, 4);
|
|
512
|
+
const rot = new Uint8ClampedArray(buffer, index * rowOutputLength + 28, 4);
|
|
513
|
+
const chunkIndex = index >> 8;
|
|
514
|
+
let r0 = 255;
|
|
515
|
+
let r1 = 0;
|
|
516
|
+
let r2 = 0;
|
|
517
|
+
let r3 = 0;
|
|
518
|
+
for (let propertyIndex = 0; propertyIndex < header.vertexProperties.length; propertyIndex++) {
|
|
519
|
+
const property = header.vertexProperties[propertyIndex];
|
|
520
|
+
let value;
|
|
521
|
+
switch (property.type) {
|
|
522
|
+
case 0 /* PLYType.FLOAT */:
|
|
523
|
+
value = dataView.getFloat32(offset.value + property.offset, true);
|
|
524
|
+
break;
|
|
525
|
+
case 1 /* PLYType.INT */:
|
|
526
|
+
value = dataView.getInt32(offset.value + property.offset, true);
|
|
527
|
+
break;
|
|
528
|
+
case 2 /* PLYType.UINT */:
|
|
529
|
+
value = dataView.getUint32(offset.value + property.offset, true);
|
|
530
|
+
break;
|
|
531
|
+
case 3 /* PLYType.DOUBLE */:
|
|
532
|
+
value = dataView.getFloat64(offset.value + property.offset, true);
|
|
533
|
+
break;
|
|
534
|
+
case 4 /* PLYType.UCHAR */:
|
|
535
|
+
value = dataView.getUint8(offset.value + property.offset);
|
|
536
|
+
break;
|
|
537
|
+
default:
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
switch (property.value) {
|
|
541
|
+
case 12 /* PLYValue.PACKED_POSITION */:
|
|
542
|
+
{
|
|
543
|
+
const compressedChunk = compressedChunks[chunkIndex];
|
|
544
|
+
unpack111011(value, temp3);
|
|
545
|
+
position[0] = Scalar.Lerp(compressedChunk.min.x, compressedChunk.max.x, temp3.x);
|
|
546
|
+
position[1] = -Scalar.Lerp(compressedChunk.min.y, compressedChunk.max.y, temp3.y);
|
|
547
|
+
position[2] = Scalar.Lerp(compressedChunk.min.z, compressedChunk.max.z, temp3.z);
|
|
548
|
+
}
|
|
549
|
+
break;
|
|
550
|
+
case 13 /* PLYValue.PACKED_ROTATION */:
|
|
551
|
+
{
|
|
552
|
+
unpackRot(value, q);
|
|
553
|
+
r0 = q.w;
|
|
554
|
+
r1 = q.z;
|
|
555
|
+
r2 = q.y;
|
|
556
|
+
r3 = q.x;
|
|
557
|
+
}
|
|
558
|
+
break;
|
|
559
|
+
case 14 /* PLYValue.PACKED_SCALE */:
|
|
560
|
+
{
|
|
561
|
+
const compressedChunk = compressedChunks[chunkIndex];
|
|
562
|
+
unpack111011(value, temp3);
|
|
563
|
+
scale[0] = Math.exp(Scalar.Lerp(compressedChunk.minScale.x, compressedChunk.maxScale.x, temp3.x));
|
|
564
|
+
scale[1] = Math.exp(Scalar.Lerp(compressedChunk.minScale.y, compressedChunk.maxScale.y, temp3.y));
|
|
565
|
+
scale[2] = Math.exp(Scalar.Lerp(compressedChunk.minScale.z, compressedChunk.maxScale.z, temp3.z));
|
|
566
|
+
}
|
|
567
|
+
break;
|
|
568
|
+
case 15 /* PLYValue.PACKED_COLOR */:
|
|
569
|
+
unpack8888(value, rgba);
|
|
570
|
+
break;
|
|
571
|
+
case 16 /* PLYValue.X */:
|
|
572
|
+
position[0] = value;
|
|
573
|
+
break;
|
|
574
|
+
case 17 /* PLYValue.Y */:
|
|
575
|
+
position[1] = value;
|
|
576
|
+
break;
|
|
577
|
+
case 18 /* PLYValue.Z */:
|
|
578
|
+
position[2] = value;
|
|
579
|
+
break;
|
|
580
|
+
case 19 /* PLYValue.SCALE_0 */:
|
|
581
|
+
scale[0] = Math.exp(value);
|
|
582
|
+
break;
|
|
583
|
+
case 20 /* PLYValue.SCALE_1 */:
|
|
584
|
+
scale[1] = Math.exp(value);
|
|
585
|
+
break;
|
|
586
|
+
case 21 /* PLYValue.SCALE_2 */:
|
|
587
|
+
scale[2] = Math.exp(value);
|
|
588
|
+
break;
|
|
589
|
+
case 22 /* PLYValue.DIFFUSE_RED */:
|
|
590
|
+
rgba[0] = value;
|
|
591
|
+
break;
|
|
592
|
+
case 23 /* PLYValue.DIFFUSE_GREEN */:
|
|
593
|
+
rgba[1] = value;
|
|
594
|
+
break;
|
|
595
|
+
case 24 /* PLYValue.DIFFUSE_BLUE */:
|
|
596
|
+
rgba[2] = value;
|
|
597
|
+
break;
|
|
598
|
+
case 26 /* PLYValue.F_DC_0 */:
|
|
599
|
+
rgba[0] = (0.5 + GaussianSplattingMesh._SH_C0 * value) * 255;
|
|
600
|
+
break;
|
|
601
|
+
case 27 /* PLYValue.F_DC_1 */:
|
|
602
|
+
rgba[1] = (0.5 + GaussianSplattingMesh._SH_C0 * value) * 255;
|
|
603
|
+
break;
|
|
604
|
+
case 28 /* PLYValue.F_DC_2 */:
|
|
605
|
+
rgba[2] = (0.5 + GaussianSplattingMesh._SH_C0 * value) * 255;
|
|
606
|
+
break;
|
|
607
|
+
case 29 /* PLYValue.F_DC_3 */:
|
|
608
|
+
rgba[3] = (0.5 + GaussianSplattingMesh._SH_C0 * value) * 255;
|
|
609
|
+
break;
|
|
610
|
+
case 25 /* PLYValue.OPACITY */:
|
|
611
|
+
rgba[3] = (1 / (1 + Math.exp(-value))) * 255;
|
|
612
|
+
break;
|
|
613
|
+
case 30 /* PLYValue.ROT_0 */:
|
|
614
|
+
r0 = value;
|
|
615
|
+
break;
|
|
616
|
+
case 31 /* PLYValue.ROT_1 */:
|
|
617
|
+
r1 = value;
|
|
618
|
+
break;
|
|
619
|
+
case 32 /* PLYValue.ROT_2 */:
|
|
620
|
+
r2 = value;
|
|
621
|
+
break;
|
|
622
|
+
case 33 /* PLYValue.ROT_3 */:
|
|
623
|
+
r3 = value;
|
|
624
|
+
break;
|
|
455
625
|
}
|
|
456
|
-
q.set(r1, r2, r3, r0);
|
|
457
|
-
q.normalize();
|
|
458
|
-
rot[0] = q.w * 128 + 128;
|
|
459
|
-
rot[1] = q.x * 128 + 128;
|
|
460
|
-
rot[2] = q.y * 128 + 128;
|
|
461
|
-
rot[3] = q.z * 128 + 128;
|
|
462
|
-
offset += rowVertexLength;
|
|
463
626
|
}
|
|
464
|
-
|
|
627
|
+
q.set(r1, r2, r3, r0);
|
|
628
|
+
q.normalize();
|
|
629
|
+
rot[0] = q.w * 128 + 128;
|
|
630
|
+
rot[1] = q.x * 128 + 128;
|
|
631
|
+
rot[2] = q.y * 128 + 128;
|
|
632
|
+
rot[3] = q.z * 128 + 128;
|
|
633
|
+
offset.value += header.rowVertexLength;
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Converts a .ply data array buffer to splat
|
|
637
|
+
* if data array buffer is not ply, returns the original buffer
|
|
638
|
+
* @param data the .ply data to load
|
|
639
|
+
* @param useCoroutine use coroutine and yield
|
|
640
|
+
* @returns the loaded splat buffer
|
|
641
|
+
*/
|
|
642
|
+
static *ConvertPLYToSplat(data, useCoroutine = false) {
|
|
643
|
+
const header = GaussianSplattingMesh.ParseHeader(data);
|
|
644
|
+
if (!header) {
|
|
645
|
+
return data;
|
|
646
|
+
}
|
|
647
|
+
const offset = { value: 0 };
|
|
648
|
+
const compressedChunks = GaussianSplattingMesh._GetCompressedChunks(header, offset);
|
|
649
|
+
for (let i = 0; i < header.vertexCount; i++) {
|
|
650
|
+
GaussianSplattingMesh._GetSplat(header, i, compressedChunks, offset);
|
|
651
|
+
if (i % 30000 === 0 && useCoroutine) {
|
|
652
|
+
yield;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return header.buffer;
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Converts a .ply data array buffer to splat
|
|
659
|
+
* if data array buffer is not ply, returns the original buffer
|
|
660
|
+
* @param data the .ply data to load
|
|
661
|
+
* @returns the loaded splat buffer
|
|
662
|
+
*/
|
|
663
|
+
static async ConvertPLYToSplatAsync(data) {
|
|
664
|
+
return runCoroutineAsync(GaussianSplattingMesh.ConvertPLYToSplat(data, true), createYieldingScheduler());
|
|
465
665
|
}
|
|
466
666
|
/**
|
|
467
667
|
* Loads a .splat Gaussian Splatting array buffer asynchronously
|
|
@@ -469,7 +669,7 @@ export class GaussianSplattingMesh extends Mesh {
|
|
|
469
669
|
* @returns a promise that resolves when the operation is complete
|
|
470
670
|
*/
|
|
471
671
|
loadDataAsync(data) {
|
|
472
|
-
return
|
|
672
|
+
return this.updateDataAsync(data);
|
|
473
673
|
}
|
|
474
674
|
/**
|
|
475
675
|
* Loads a .splat Gaussian or .ply Splatting file asynchronously
|
|
@@ -478,8 +678,10 @@ export class GaussianSplattingMesh extends Mesh {
|
|
|
478
678
|
* @deprecated Please use SceneLoader.ImportMeshAsync instead
|
|
479
679
|
*/
|
|
480
680
|
loadFileAsync(url) {
|
|
481
|
-
return Tools.LoadFileAsync(url, true).then((
|
|
482
|
-
|
|
681
|
+
return Tools.LoadFileAsync(url, true).then(async (plyBuffer) => {
|
|
682
|
+
GaussianSplattingMesh.ConvertPLYToSplatAsync(plyBuffer).then((splatsData) => {
|
|
683
|
+
this.updateDataAsync(splatsData);
|
|
684
|
+
});
|
|
483
685
|
});
|
|
484
686
|
}
|
|
485
687
|
/**
|
|
@@ -526,78 +728,52 @@ export class GaussianSplattingMesh extends Mesh {
|
|
|
526
728
|
newGS.setEnabled(true);
|
|
527
729
|
return newGS;
|
|
528
730
|
}
|
|
529
|
-
|
|
530
|
-
* @experimental
|
|
531
|
-
* Update data from GS (position, orientation, color, scaling)
|
|
532
|
-
* @param data array that contain all the datas
|
|
533
|
-
*/
|
|
534
|
-
updateData(data) {
|
|
535
|
-
if (!data.byteLength) {
|
|
536
|
-
return;
|
|
537
|
-
}
|
|
538
|
-
// if a covariance texture is present, then it's not a creation but an update
|
|
539
|
-
if (!this._covariancesATexture) {
|
|
540
|
-
this._readyToDisplay = false;
|
|
541
|
-
}
|
|
542
|
-
// Parse the data
|
|
543
|
-
const uBuffer = new Uint8Array(data);
|
|
544
|
-
const fBuffer = new Float32Array(uBuffer.buffer);
|
|
545
|
-
const rowLength = 3 * 4 + 3 * 4 + 4 + 4;
|
|
546
|
-
const vertexCount = uBuffer.length / rowLength;
|
|
547
|
-
if (vertexCount != this._vertexCount) {
|
|
548
|
-
this._updateSplatIndexBuffer(vertexCount);
|
|
549
|
-
}
|
|
550
|
-
this._vertexCount = vertexCount;
|
|
551
|
-
const textureSize = this._getTextureSize(vertexCount);
|
|
552
|
-
const textureLength = textureSize.x * textureSize.y;
|
|
553
|
-
this._splatPositions = new Float32Array(4 * textureLength);
|
|
554
|
-
const covA = new Uint16Array(4 * textureLength);
|
|
555
|
-
const covB = new Uint16Array((this._useRGBACovariants ? 4 : 2) * textureLength);
|
|
731
|
+
_makeSplat(sourceIndex, destinationIndex, fBuffer, uBuffer, covA, covB, colorArray, minimum, maximum) {
|
|
556
732
|
const matrixRotation = TmpVectors.Matrix[0];
|
|
557
733
|
const matrixScale = TmpVectors.Matrix[1];
|
|
558
734
|
const quaternion = TmpVectors.Quaternion[0];
|
|
559
|
-
const
|
|
560
|
-
const
|
|
561
|
-
const
|
|
562
|
-
const
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
let factor = -10000;
|
|
584
|
-
for (let covIndex = 0; covIndex < 6; covIndex++) {
|
|
585
|
-
factor = Math.max(factor, Math.abs(covariances[covIndex]));
|
|
586
|
-
}
|
|
587
|
-
this._splatPositions[4 * i + 3] = factor;
|
|
588
|
-
const transform = factor;
|
|
589
|
-
covA[i * 4 + 0] = ToHalfFloat(covariances[0] / transform);
|
|
590
|
-
covA[i * 4 + 1] = ToHalfFloat(covariances[1] / transform);
|
|
591
|
-
covA[i * 4 + 2] = ToHalfFloat(covariances[2] / transform);
|
|
592
|
-
covA[i * 4 + 3] = ToHalfFloat(covariances[3] / transform);
|
|
593
|
-
covB[i * covBSplatSize + 0] = ToHalfFloat(covariances[4] / transform);
|
|
594
|
-
covB[i * covBSplatSize + 1] = ToHalfFloat(covariances[5] / transform);
|
|
735
|
+
const covBSItemSize = this._useRGBACovariants ? 4 : 2;
|
|
736
|
+
const x = fBuffer[8 * sourceIndex + 0];
|
|
737
|
+
const y = -fBuffer[8 * sourceIndex + 1];
|
|
738
|
+
const z = fBuffer[8 * sourceIndex + 2];
|
|
739
|
+
this._splatPositions[4 * sourceIndex + 0] = x;
|
|
740
|
+
this._splatPositions[4 * sourceIndex + 1] = y;
|
|
741
|
+
this._splatPositions[4 * sourceIndex + 2] = z;
|
|
742
|
+
minimum.minimizeInPlaceFromFloats(x, y, z);
|
|
743
|
+
maximum.maximizeInPlaceFromFloats(x, y, z);
|
|
744
|
+
quaternion.set((uBuffer[32 * sourceIndex + 28 + 1] - 128) / 128, (uBuffer[32 * sourceIndex + 28 + 2] - 128) / 128, (uBuffer[32 * sourceIndex + 28 + 3] - 128) / 128, -(uBuffer[32 * sourceIndex + 28 + 0] - 128) / 128);
|
|
745
|
+
quaternion.toRotationMatrix(matrixRotation);
|
|
746
|
+
Matrix.ScalingToRef(fBuffer[8 * sourceIndex + 3 + 0] * 2, fBuffer[8 * sourceIndex + 3 + 1] * 2, fBuffer[8 * sourceIndex + 3 + 2] * 2, matrixScale);
|
|
747
|
+
const M = matrixRotation.multiplyToRef(matrixScale, TmpVectors.Matrix[0]).m;
|
|
748
|
+
const covariances = this._tmpCovariances;
|
|
749
|
+
covariances[0] = M[0] * M[0] + M[1] * M[1] + M[2] * M[2];
|
|
750
|
+
covariances[1] = M[0] * M[4] + M[1] * M[5] + M[2] * M[6];
|
|
751
|
+
covariances[2] = M[0] * M[8] + M[1] * M[9] + M[2] * M[10];
|
|
752
|
+
covariances[3] = M[4] * M[4] + M[5] * M[5] + M[6] * M[6];
|
|
753
|
+
covariances[4] = M[4] * M[8] + M[5] * M[9] + M[6] * M[10];
|
|
754
|
+
covariances[5] = M[8] * M[8] + M[9] * M[9] + M[10] * M[10];
|
|
755
|
+
// normalize covA, covB
|
|
756
|
+
let factor = -10000;
|
|
757
|
+
for (let covIndex = 0; covIndex < 6; covIndex++) {
|
|
758
|
+
factor = Math.max(factor, Math.abs(covariances[covIndex]));
|
|
595
759
|
}
|
|
596
|
-
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
760
|
+
this._splatPositions[4 * sourceIndex + 3] = factor;
|
|
761
|
+
const transform = factor;
|
|
762
|
+
covA[destinationIndex * 4 + 0] = ToHalfFloat(covariances[0] / transform);
|
|
763
|
+
covA[destinationIndex * 4 + 1] = ToHalfFloat(covariances[1] / transform);
|
|
764
|
+
covA[destinationIndex * 4 + 2] = ToHalfFloat(covariances[2] / transform);
|
|
765
|
+
covA[destinationIndex * 4 + 3] = ToHalfFloat(covariances[3] / transform);
|
|
766
|
+
covB[destinationIndex * covBSItemSize + 0] = ToHalfFloat(covariances[4] / transform);
|
|
767
|
+
covB[destinationIndex * covBSItemSize + 1] = ToHalfFloat(covariances[5] / transform);
|
|
768
|
+
// colors
|
|
769
|
+
colorArray[destinationIndex * 4 + 0] = uBuffer[32 * sourceIndex + 24 + 0];
|
|
770
|
+
colorArray[destinationIndex * 4 + 1] = uBuffer[32 * sourceIndex + 24 + 1];
|
|
771
|
+
colorArray[destinationIndex * 4 + 2] = uBuffer[32 * sourceIndex + 24 + 2];
|
|
772
|
+
colorArray[destinationIndex * 4 + 3] = uBuffer[32 * sourceIndex + 24 + 3];
|
|
773
|
+
}
|
|
774
|
+
_updateTextures(covA, covB, colorArray) {
|
|
775
|
+
const textureSize = this._getTextureSize(this._vertexCount);
|
|
776
|
+
// Update the textures
|
|
601
777
|
const createTextureFromData = (data, width, height, format) => {
|
|
602
778
|
return new RawTexture(data, width, height, format, this._scene, false, false, 2, 1);
|
|
603
779
|
};
|
|
@@ -607,13 +783,6 @@ export class GaussianSplattingMesh extends Mesh {
|
|
|
607
783
|
const createTextureFromDataF16 = (data, width, height, format) => {
|
|
608
784
|
return new RawTexture(data, width, height, format, this._scene, false, false, 2, 2);
|
|
609
785
|
};
|
|
610
|
-
const colorArray = new Uint8Array(textureSize.x * textureSize.y * 4);
|
|
611
|
-
for (let i = 0; i < this._vertexCount; ++i) {
|
|
612
|
-
colorArray[i * 4 + 0] = uBuffer[32 * i + 24 + 0];
|
|
613
|
-
colorArray[i * 4 + 1] = uBuffer[32 * i + 24 + 1];
|
|
614
|
-
colorArray[i * 4 + 2] = uBuffer[32 * i + 24 + 2];
|
|
615
|
-
colorArray[i * 4 + 3] = uBuffer[32 * i + 24 + 3];
|
|
616
|
-
}
|
|
617
786
|
if (this._keepInRam) {
|
|
618
787
|
this._covariancesA = covA;
|
|
619
788
|
this._covariancesB = covB;
|
|
@@ -634,11 +803,83 @@ export class GaussianSplattingMesh extends Mesh {
|
|
|
634
803
|
this._instanciateWorker();
|
|
635
804
|
}
|
|
636
805
|
}
|
|
637
|
-
|
|
638
|
-
if
|
|
639
|
-
|
|
806
|
+
*_updateData(data, isAsync) {
|
|
807
|
+
// if a covariance texture is present, then it's not a creation but an update
|
|
808
|
+
if (!this._covariancesATexture) {
|
|
809
|
+
this._readyToDisplay = false;
|
|
810
|
+
}
|
|
811
|
+
// Parse the data
|
|
812
|
+
const uBuffer = new Uint8Array(data);
|
|
813
|
+
const fBuffer = new Float32Array(uBuffer.buffer);
|
|
814
|
+
const vertexCount = uBuffer.length / GaussianSplattingMesh._RowOutputLength;
|
|
815
|
+
if (vertexCount != this._vertexCount) {
|
|
816
|
+
this._updateSplatIndexBuffer(vertexCount);
|
|
817
|
+
}
|
|
818
|
+
this._vertexCount = vertexCount;
|
|
819
|
+
const textureSize = this._getTextureSize(vertexCount);
|
|
820
|
+
const textureLength = textureSize.x * textureSize.y;
|
|
821
|
+
const lineCountUpdate = GaussianSplattingMesh.ProgressiveUpdateAmount ?? textureSize.y;
|
|
822
|
+
const textureLengthPerUpdate = textureSize.x * lineCountUpdate;
|
|
823
|
+
this._splatPositions = new Float32Array(4 * textureLength);
|
|
824
|
+
const covA = new Uint16Array(textureLength * 4);
|
|
825
|
+
const covB = new Uint16Array((this._useRGBACovariants ? 4 : 2) * textureLength);
|
|
826
|
+
const colorArray = new Uint8Array(textureLength * 4);
|
|
827
|
+
const minimum = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
|
|
828
|
+
const maximum = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
|
|
829
|
+
if (GaussianSplattingMesh.ProgressiveUpdateAmount) {
|
|
830
|
+
// create textures with not filled-yet array, then update directly portions of it
|
|
831
|
+
this._updateTextures(covA, covB, colorArray);
|
|
832
|
+
this.setEnabled(true);
|
|
833
|
+
const partCount = Math.ceil(textureSize.y / lineCountUpdate);
|
|
834
|
+
for (let partIndex = 0; partIndex < partCount; partIndex++) {
|
|
835
|
+
const updateLine = partIndex * lineCountUpdate;
|
|
836
|
+
const splatIndexBase = updateLine * textureSize.x;
|
|
837
|
+
for (let i = 0; i < textureLengthPerUpdate; i++) {
|
|
838
|
+
this._makeSplat(splatIndexBase + i, splatIndexBase + i, fBuffer, uBuffer, covA, covB, colorArray, minimum, maximum);
|
|
839
|
+
}
|
|
840
|
+
this._updateSubTextures(this._splatPositions, covA, covB, colorArray, updateLine, Math.min(lineCountUpdate, textureSize.y - updateLine));
|
|
841
|
+
// Update the binfo
|
|
842
|
+
this.getBoundingInfo().reConstruct(minimum, maximum, this.getWorldMatrix());
|
|
843
|
+
if (isAsync) {
|
|
844
|
+
yield;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
// sort will be dirty here as just finished filled positions will not be sorted
|
|
848
|
+
const positions = Float32Array.from(this._splatPositions);
|
|
849
|
+
const vertexCount = this._vertexCount;
|
|
850
|
+
this._worker.postMessage({ positions, vertexCount }, [positions.buffer]);
|
|
851
|
+
this._sortIsDirty = true;
|
|
852
|
+
}
|
|
853
|
+
else {
|
|
854
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
855
|
+
this._makeSplat(i, i, fBuffer, uBuffer, covA, covB, colorArray, minimum, maximum);
|
|
856
|
+
if (isAsync && i % 327680 === 0) {
|
|
857
|
+
yield;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
// textures
|
|
861
|
+
this._updateTextures(covA, covB, colorArray);
|
|
862
|
+
// Update the binfo
|
|
863
|
+
this.getBoundingInfo().reConstruct(minimum, maximum, this.getWorldMatrix());
|
|
864
|
+
this.setEnabled(true);
|
|
640
865
|
}
|
|
641
|
-
this.
|
|
866
|
+
this._postToWorker(true);
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Update asynchronously the buffer
|
|
870
|
+
* @param data array buffer containing center, color, orientation and scale of splats
|
|
871
|
+
* @returns a promise
|
|
872
|
+
*/
|
|
873
|
+
async updateDataAsync(data) {
|
|
874
|
+
return runCoroutineAsync(this._updateData(data, true), createYieldingScheduler());
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* @experimental
|
|
878
|
+
* Update data from GS (position, orientation, color, scaling)
|
|
879
|
+
* @param data array that contain all the datas
|
|
880
|
+
*/
|
|
881
|
+
updateData(data) {
|
|
882
|
+
runCoroutineSync(this._updateData(data, false));
|
|
642
883
|
}
|
|
643
884
|
// in case size is different
|
|
644
885
|
_updateSplatIndexBuffer(vertexCount) {
|
|
@@ -648,6 +889,29 @@ export class GaussianSplattingMesh extends Mesh {
|
|
|
648
889
|
}
|
|
649
890
|
this.forcedInstanceCount = vertexCount;
|
|
650
891
|
}
|
|
892
|
+
_updateSubTextures(centers, covA, covB, colors, lineStart, lineCount) {
|
|
893
|
+
const updateTextureFromData = (texture, data, width, lineStart, lineCount) => {
|
|
894
|
+
this.getEngine().updateTextureData(texture.getInternalTexture(), data, 0, lineStart, width, lineCount, 0, 0, false);
|
|
895
|
+
};
|
|
896
|
+
const updateTextureFromDataU8 = (texture, data, width, lineStart, lineCount) => {
|
|
897
|
+
this.getEngine().updateTextureData(texture.getInternalTexture(), data, 0, lineStart, width, lineCount, 0, 0, false);
|
|
898
|
+
};
|
|
899
|
+
const updateTextureFromDataF16 = (texture, data, width, lineStart, lineCount) => {
|
|
900
|
+
this.getEngine().updateTextureData(texture.getInternalTexture(), data, 0, lineStart, width, lineCount, 0, 0, false);
|
|
901
|
+
};
|
|
902
|
+
const textureSize = this._getTextureSize(this._vertexCount);
|
|
903
|
+
const covBSItemSize = this._useRGBACovariants ? 4 : 2;
|
|
904
|
+
const texelStart = lineStart * textureSize.x;
|
|
905
|
+
const texelCount = lineCount * textureSize.x;
|
|
906
|
+
const covAView = new Uint16Array(covA.buffer, texelStart * 4 * Uint16Array.BYTES_PER_ELEMENT, texelCount * 4);
|
|
907
|
+
const covBView = new Uint16Array(covB.buffer, texelStart * covBSItemSize * Uint16Array.BYTES_PER_ELEMENT, texelCount * covBSItemSize);
|
|
908
|
+
const colorsView = new Uint8Array(colors.buffer, texelStart * 4, texelCount * 4);
|
|
909
|
+
const centersView = new Float32Array(centers.buffer, texelStart * 4 * Float32Array.BYTES_PER_ELEMENT, texelCount * 4);
|
|
910
|
+
updateTextureFromDataF16(this._covariancesATexture, covAView, textureSize.x, lineStart, lineCount);
|
|
911
|
+
updateTextureFromDataF16(this._covariancesBTexture, covBView, textureSize.x, lineStart, lineCount);
|
|
912
|
+
updateTextureFromData(this._centersTexture, centersView, textureSize.x, lineStart, lineCount);
|
|
913
|
+
updateTextureFromDataU8(this._colorsTexture, colorsView, textureSize.x, lineStart, lineCount);
|
|
914
|
+
}
|
|
651
915
|
_instanciateWorker() {
|
|
652
916
|
if (!this._vertexCount) {
|
|
653
917
|
return;
|
|
@@ -671,25 +935,19 @@ export class GaussianSplattingMesh extends Mesh {
|
|
|
671
935
|
}
|
|
672
936
|
}
|
|
673
937
|
if (this._delayedTextureUpdate) {
|
|
674
|
-
const updateTextureFromData = (texture, data, width, height) => {
|
|
675
|
-
this.getEngine().updateTextureData(texture.getInternalTexture(), data, 0, 0, width, height, 0, 0, false);
|
|
676
|
-
};
|
|
677
|
-
const updateTextureFromDataU8 = (texture, data, width, height) => {
|
|
678
|
-
this.getEngine().updateTextureData(texture.getInternalTexture(), data, 0, 0, width, height, 0, 0, false);
|
|
679
|
-
};
|
|
680
|
-
const updateTextureFromDataF16 = (texture, data, width, height) => {
|
|
681
|
-
this.getEngine().updateTextureData(texture.getInternalTexture(), data, 0, 0, width, height, 0, 0, false);
|
|
682
|
-
};
|
|
683
938
|
const textureSize = this._getTextureSize(vertexCount);
|
|
684
|
-
|
|
685
|
-
updateTextureFromDataF16(this._covariancesBTexture, this._delayedTextureUpdate.covB, textureSize.x, textureSize.y);
|
|
686
|
-
updateTextureFromData(this._centersTexture, this._delayedTextureUpdate.centers, textureSize.x, textureSize.y);
|
|
687
|
-
updateTextureFromDataU8(this._colorsTexture, this._delayedTextureUpdate.colors, textureSize.x, textureSize.y);
|
|
939
|
+
this._updateSubTextures(this._delayedTextureUpdate.centers, this._delayedTextureUpdate.covA, this._delayedTextureUpdate.covB, this._delayedTextureUpdate.colors, 0, textureSize.y);
|
|
688
940
|
this._delayedTextureUpdate = null;
|
|
689
941
|
}
|
|
690
942
|
this.thinInstanceBufferUpdated("splatIndex");
|
|
691
943
|
this._canPostToWorker = true;
|
|
692
944
|
this._readyToDisplay = true;
|
|
945
|
+
// sort is dirty when GS is visible for progressive update with a this message arriving but positions were partially filled
|
|
946
|
+
// another update needs to be kicked. The kick can't happen just when the position buffer is ready because _canPostToWorker might be false.
|
|
947
|
+
if (this._sortIsDirty) {
|
|
948
|
+
this._postToWorker(true);
|
|
949
|
+
this._sortIsDirty = false;
|
|
950
|
+
}
|
|
693
951
|
};
|
|
694
952
|
}
|
|
695
953
|
_getTextureSize(length) {
|
|
@@ -711,6 +969,13 @@ export class GaussianSplattingMesh extends Mesh {
|
|
|
711
969
|
return new Vector2(width, height);
|
|
712
970
|
}
|
|
713
971
|
}
|
|
972
|
+
GaussianSplattingMesh._RowOutputLength = 3 * 4 + 3 * 4 + 4 + 4; // Vector3 position, Vector3 scale, 1 u8 quaternion, 1 color with alpha
|
|
973
|
+
GaussianSplattingMesh._SH_C0 = 0.28209479177387814;
|
|
974
|
+
/**
|
|
975
|
+
* Set the number of batch (a batch is 16384 splats) after which a display update is performed
|
|
976
|
+
* A value of 0 (default) means display update will not happens before splat is ready.
|
|
977
|
+
*/
|
|
978
|
+
GaussianSplattingMesh.ProgressiveUpdateAmount = 0;
|
|
714
979
|
GaussianSplattingMesh._CreateWorker = function (self) {
|
|
715
980
|
let vertexCount = 0;
|
|
716
981
|
let positions;
|