@babylonjs/core 7.31.1 → 7.31.2

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.
@@ -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, Quaternion, TmpVectors, Vector2, Vector3 } from "../../Maths/math.vector.js";
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
- vertexData.positions = [-2, -2, 0, 2, -2, 0, 2, 2, 0, -2, 2, 0];
89
- vertexData.indices = [0, 1, 2, 0, 2, 3];
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
- new SubMesh(0, 0, 4, 0, 6, this);
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
- * Code from https://github.com/dylanebert/gsplat.js/blob/main/src/loaders/PLYLoader.ts Under MIT license
165
- * Converts a .ply data array buffer to splat
166
- * if data array buffer is not ply, returns the original buffer
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 ConvertPLYToSplat(data) {
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 data;
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 [, type, name] = prop.split(" ");
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({ name, type, offset: rowChunkOffset });
212
- rowChunkOffset += offsets[type];
407
+ chunkProperties.push({ value, type, offset: rowChunkOffset });
408
+ rowChunkOffset += offsets[typeName];
213
409
  }
214
410
  else if (chunkMode == 0 /* ElementMode.Vertex */) {
215
- vertexProperties.push({ name, type, offset: rowVertexOffset });
216
- rowVertexOffset += offsets[type];
411
+ vertexProperties.push({ value, type, offset: rowVertexOffset });
412
+ rowVertexOffset += offsets[typeName];
217
413
  }
218
- if (!offsets[type]) {
219
- Logger.Warn(`Unsupported property type: ${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(rowOutputLength * vertexCount);
239
- const q = new Quaternion();
240
- const temp3 = TmpVectors.Vector3[0];
241
- const unpackUnorm = (value, bits) => {
242
- const t = (1 << bits) - 1;
243
- return (value & t) / t;
244
- };
245
- const unpack111011 = (value, result) => {
246
- result.x = unpackUnorm(value >>> 21, 11);
247
- result.y = unpackUnorm(value >>> 11, 10);
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
- const unpack8888 = (value, result) => {
251
- result[0] = unpackUnorm(value >>> 24, 8) * 255;
252
- result[1] = unpackUnorm(value >>> 16, 8) * 255;
253
- result[2] = unpackUnorm(value >>> 8, 8) * 255;
254
- result[3] = unpackUnorm(value, 8) * 255;
255
- };
256
- // unpack quaternion with 2,10,10,10 format (largest element, 3x10bit element)
257
- const unpackRot = (value, result) => {
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 "float":
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.name) {
293
- case "min_x":
460
+ switch (property.value) {
461
+ case 0 /* PLYValue.MIN_X */:
294
462
  currentChunk.min.x = value;
295
463
  break;
296
- case "min_y":
464
+ case 1 /* PLYValue.MIN_Y */:
297
465
  currentChunk.min.y = value;
298
466
  break;
299
- case "min_z":
467
+ case 2 /* PLYValue.MIN_Z */:
300
468
  currentChunk.min.z = value;
301
469
  break;
302
- case "max_x":
470
+ case 3 /* PLYValue.MAX_X */:
303
471
  currentChunk.max.x = value;
304
472
  break;
305
- case "max_y":
473
+ case 4 /* PLYValue.MAX_Y */:
306
474
  currentChunk.max.y = value;
307
475
  break;
308
- case "max_z":
476
+ case 5 /* PLYValue.MAX_Z */:
309
477
  currentChunk.max.z = value;
310
478
  break;
311
- case "min_scale_x":
479
+ case 6 /* PLYValue.MIN_SCALE_X */:
312
480
  currentChunk.minScale.x = value;
313
481
  break;
314
- case "min_scale_y":
482
+ case 7 /* PLYValue.MIN_SCALE_Y */:
315
483
  currentChunk.minScale.y = value;
316
484
  break;
317
- case "min_scale_z":
485
+ case 8 /* PLYValue.MIN_SCALE_Z */:
318
486
  currentChunk.minScale.z = value;
319
487
  break;
320
- case "max_scale_x":
488
+ case 9 /* PLYValue.MAX_SCALE_X */:
321
489
  currentChunk.maxScale.x = value;
322
490
  break;
323
- case "max_scale_y":
491
+ case 10 /* PLYValue.MAX_SCALE_Y */:
324
492
  currentChunk.maxScale.y = value;
325
493
  break;
326
- case "max_scale_z":
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
- for (let i = 0; i < vertexCount; i++) {
334
- const position = new Float32Array(buffer, i * rowOutputLength, 3);
335
- const scale = new Float32Array(buffer, i * rowOutputLength + 12, 3);
336
- const rgba = new Uint8ClampedArray(buffer, i * rowOutputLength + 24, 4);
337
- const rot = new Uint8ClampedArray(buffer, i * rowOutputLength + 28, 4);
338
- const chunkIndex = i >> 8;
339
- let r0 = 255;
340
- let r1 = 0;
341
- let r2 = 0;
342
- let r3 = 0;
343
- for (let propertyIndex = 0; propertyIndex < vertexProperties.length; propertyIndex++) {
344
- const property = vertexProperties[propertyIndex];
345
- let value;
346
- switch (property.type) {
347
- case "float":
348
- value = dataView.getFloat32(offset + property.offset, true);
349
- break;
350
- case "int":
351
- value = dataView.getInt32(offset + property.offset, true);
352
- break;
353
- case "uint":
354
- value = dataView.getUint32(offset + property.offset, true);
355
- break;
356
- case "double":
357
- value = dataView.getFloat64(offset + property.offset, true);
358
- break;
359
- case "uchar":
360
- value = dataView.getUint8(offset + property.offset);
361
- break;
362
- default:
363
- //throw new Error(`Unsupported property type: ${property.type}`);
364
- continue;
365
- }
366
- switch (property.name) {
367
- case "packed_position":
368
- {
369
- const compressedChunk = compressedChunks[chunkIndex];
370
- unpack111011(value, temp3);
371
- position[0] = Scalar.Lerp(compressedChunk.min.x, compressedChunk.max.x, temp3.x);
372
- position[1] = -Scalar.Lerp(compressedChunk.min.y, compressedChunk.max.y, temp3.y);
373
- position[2] = Scalar.Lerp(compressedChunk.min.z, compressedChunk.max.z, temp3.z);
374
- }
375
- break;
376
- case "packed_rotation":
377
- {
378
- unpackRot(value, q);
379
- r0 = q.w;
380
- r1 = q.z;
381
- r2 = q.y;
382
- r3 = q.x;
383
- }
384
- break;
385
- case "packed_scale":
386
- {
387
- const compressedChunk = compressedChunks[chunkIndex];
388
- unpack111011(value, temp3);
389
- scale[0] = Math.exp(Scalar.Lerp(compressedChunk.minScale.x, compressedChunk.maxScale.x, temp3.x));
390
- scale[1] = Math.exp(Scalar.Lerp(compressedChunk.minScale.y, compressedChunk.maxScale.y, temp3.y));
391
- scale[2] = Math.exp(Scalar.Lerp(compressedChunk.minScale.z, compressedChunk.maxScale.z, temp3.z));
392
- }
393
- break;
394
- case "packed_color":
395
- unpack8888(value, rgba);
396
- break;
397
- case "x":
398
- position[0] = value;
399
- break;
400
- case "y":
401
- position[1] = value;
402
- break;
403
- case "z":
404
- position[2] = value;
405
- break;
406
- case "scale_0":
407
- scale[0] = Math.exp(value);
408
- break;
409
- case "scale_1":
410
- scale[1] = Math.exp(value);
411
- break;
412
- case "scale_2":
413
- scale[2] = Math.exp(value);
414
- break;
415
- case "diffuse_red":
416
- case "red":
417
- rgba[0] = value;
418
- break;
419
- case "diffuse_green":
420
- case "green":
421
- rgba[1] = value;
422
- break;
423
- case "diffuse_blue":
424
- case "blue":
425
- rgba[2] = value;
426
- break;
427
- case "f_dc_0":
428
- rgba[0] = (0.5 + SH_C0 * value) * 255;
429
- break;
430
- case "f_dc_1":
431
- rgba[1] = (0.5 + SH_C0 * value) * 255;
432
- break;
433
- case "f_dc_2":
434
- rgba[2] = (0.5 + SH_C0 * value) * 255;
435
- break;
436
- case "f_dc_3":
437
- rgba[3] = (0.5 + SH_C0 * value) * 255;
438
- break;
439
- case "opacity":
440
- rgba[3] = (1 / (1 + Math.exp(-value))) * 255;
441
- break;
442
- case "rot_0":
443
- r0 = value;
444
- break;
445
- case "rot_1":
446
- r1 = value;
447
- break;
448
- case "rot_2":
449
- r2 = value;
450
- break;
451
- case "rot_3":
452
- r3 = value;
453
- break;
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
- return buffer;
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 Promise.resolve(this._loadData(data));
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((data) => {
482
- this._loadData(GaussianSplattingMesh.ConvertPLYToSplat(data));
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 minimum = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
560
- const maximum = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
561
- const covariances = [0, 0, 0, 0, 0, 0];
562
- const covBSplatSize = this._useRGBACovariants ? 4 : 2;
563
- for (let i = 0; i < vertexCount; i++) {
564
- const x = fBuffer[8 * i + 0];
565
- const y = -fBuffer[8 * i + 1];
566
- const z = fBuffer[8 * i + 2];
567
- this._splatPositions[4 * i + 0] = x;
568
- this._splatPositions[4 * i + 1] = y;
569
- this._splatPositions[4 * i + 2] = z;
570
- minimum.minimizeInPlaceFromFloats(x, y, z);
571
- maximum.maximizeInPlaceFromFloats(x, y, z);
572
- quaternion.set((uBuffer[32 * i + 28 + 1] - 128) / 128, (uBuffer[32 * i + 28 + 2] - 128) / 128, (uBuffer[32 * i + 28 + 3] - 128) / 128, -(uBuffer[32 * i + 28 + 0] - 128) / 128);
573
- quaternion.toRotationMatrix(matrixRotation);
574
- Matrix.ScalingToRef(fBuffer[8 * i + 3 + 0] * 2, fBuffer[8 * i + 3 + 1] * 2, fBuffer[8 * i + 3 + 2] * 2, matrixScale);
575
- const M = matrixRotation.multiplyToRef(matrixScale, TmpVectors.Matrix[0]).m;
576
- covariances[0] = M[0] * M[0] + M[1] * M[1] + M[2] * M[2];
577
- covariances[1] = M[0] * M[4] + M[1] * M[5] + M[2] * M[6];
578
- covariances[2] = M[0] * M[8] + M[1] * M[9] + M[2] * M[10];
579
- covariances[3] = M[4] * M[4] + M[5] * M[5] + M[6] * M[6];
580
- covariances[4] = M[4] * M[8] + M[5] * M[9] + M[6] * M[10];
581
- covariances[5] = M[8] * M[8] + M[9] * M[9] + M[10] * M[10];
582
- // normalize covA, covB
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
- // Update the mesh
597
- const binfo = this.getBoundingInfo();
598
- binfo.reConstruct(minimum, maximum, this.getWorldMatrix());
599
- this.setEnabled(true);
600
- // Update the material
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
- _loadData(data) {
638
- if (!data.byteLength) {
639
- return;
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.updateData(data);
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
- updateTextureFromDataF16(this._covariancesATexture, this._delayedTextureUpdate.covA, textureSize.x, textureSize.y);
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;