@babylonjs/core 9.12.0 → 9.12.1

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.
Files changed (90) hide show
  1. package/Behaviors/Cameras/interpolatingBehavior.d.ts +17 -2
  2. package/Behaviors/Cameras/interpolatingBehavior.js +53 -23
  3. package/Behaviors/Cameras/interpolatingBehavior.js.map +1 -1
  4. package/Cameras/Inputs/flyCameraKeyboardInput.js +9 -1
  5. package/Cameras/Inputs/flyCameraKeyboardInput.js.map +1 -1
  6. package/Cameras/Inputs/flyCameraMouseInput.d.ts +2 -0
  7. package/Cameras/Inputs/flyCameraMouseInput.js +14 -2
  8. package/Cameras/Inputs/flyCameraMouseInput.js.map +1 -1
  9. package/Cameras/Inputs/freeCameraKeyboardMoveInput.js +25 -5
  10. package/Cameras/Inputs/freeCameraKeyboardMoveInput.js.map +1 -1
  11. package/Cameras/Inputs/freeCameraMouseInput.d.ts +14 -0
  12. package/Cameras/Inputs/freeCameraMouseInput.js +26 -4
  13. package/Cameras/Inputs/freeCameraMouseInput.js.map +1 -1
  14. package/Cameras/Inputs/freeCameraTouchInput.js +16 -5
  15. package/Cameras/Inputs/freeCameraTouchInput.js.map +1 -1
  16. package/Cameras/Inputs/geospatialCameraKeyboardInput.js +8 -1
  17. package/Cameras/Inputs/geospatialCameraKeyboardInput.js.map +1 -1
  18. package/Cameras/arcRotateCamera.pure.js.map +1 -1
  19. package/Cameras/flyCamera.pure.d.ts +8 -0
  20. package/Cameras/flyCamera.pure.js.map +1 -1
  21. package/Cameras/freeCamera.pure.d.ts +8 -0
  22. package/Cameras/freeCamera.pure.js.map +1 -1
  23. package/Cameras/geospatialCamera.pure.d.ts +30 -7
  24. package/Cameras/geospatialCamera.pure.js +39 -14
  25. package/Cameras/geospatialCamera.pure.js.map +1 -1
  26. package/Cameras/geospatialCameraMovement.js +2 -2
  27. package/Cameras/geospatialCameraMovement.js.map +1 -1
  28. package/Cameras/index.d.ts +1 -0
  29. package/Cameras/index.js +1 -0
  30. package/Cameras/index.js.map +1 -1
  31. package/Cameras/pure.d.ts +1 -0
  32. package/Cameras/pure.js +1 -0
  33. package/Cameras/pure.js.map +1 -1
  34. package/Cameras/targetCamera.pure.d.ts +25 -0
  35. package/Cameras/targetCamera.pure.js +83 -25
  36. package/Cameras/targetCamera.pure.js.map +1 -1
  37. package/Cameras/targetCameraMovement.d.ts +56 -0
  38. package/Cameras/targetCameraMovement.js +63 -0
  39. package/Cameras/targetCameraMovement.js.map +1 -0
  40. package/Engines/AbstractEngine/abstractEngine.scissor.d.ts +18 -0
  41. package/Engines/AbstractEngine/abstractEngine.scissor.js +2 -0
  42. package/Engines/AbstractEngine/abstractEngine.scissor.js.map +1 -0
  43. package/Engines/abstractEngine.pure.js +2 -2
  44. package/Engines/abstractEngine.pure.js.map +1 -1
  45. package/Engines/engine.d.ts +1 -0
  46. package/Engines/engine.js +1 -0
  47. package/Engines/engine.js.map +1 -1
  48. package/Engines/engine.pure.d.ts +0 -12
  49. package/Engines/engine.pure.js +0 -20
  50. package/Engines/engine.pure.js.map +1 -1
  51. package/Engines/nullEngine.pure.d.ts +66 -0
  52. package/Engines/nullEngine.pure.js +93 -0
  53. package/Engines/nullEngine.pure.js.map +1 -1
  54. package/Engines/pure.d.ts +1 -0
  55. package/Engines/pure.js +1 -0
  56. package/Engines/pure.js.map +1 -1
  57. package/Engines/thinEngine.scissor.d.ts +6 -0
  58. package/Engines/thinEngine.scissor.js +9 -0
  59. package/Engines/thinEngine.scissor.js.map +1 -0
  60. package/Engines/thinEngine.scissor.pure.d.ts +6 -0
  61. package/Engines/thinEngine.scissor.pure.js +24 -0
  62. package/Engines/thinEngine.scissor.pure.js.map +1 -0
  63. package/Engines/thinNativeEngine.d.ts +0 -1
  64. package/Engines/thinNativeEngine.js +0 -1
  65. package/Engines/thinNativeEngine.js.map +1 -1
  66. package/Engines/thinNativeEngine.pure.js +7 -0
  67. package/Engines/thinNativeEngine.pure.js.map +1 -1
  68. package/Engines/webgpuEngine.pure.js.map +1 -1
  69. package/Materials/GreasedLine/greasedLinePluginMaterialShadersGLSL.js +10 -3
  70. package/Materials/GreasedLine/greasedLinePluginMaterialShadersGLSL.js.map +1 -1
  71. package/Meshes/GaussianSplatting/gaussianSplattingMesh.pure.js +18 -8
  72. package/Meshes/GaussianSplatting/gaussianSplattingMesh.pure.js.map +1 -1
  73. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.d.ts +50 -1
  74. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.js +199 -104
  75. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.js.map +1 -1
  76. package/Meshes/GaussianSplatting/gaussianSplattingSortWorker.d.ts +37 -0
  77. package/Meshes/GaussianSplatting/gaussianSplattingSortWorker.js +160 -0
  78. package/Meshes/GaussianSplatting/gaussianSplattingSortWorker.js.map +1 -0
  79. package/Meshes/geometry.js +30 -15
  80. package/Meshes/geometry.js.map +1 -1
  81. package/Meshes/mesh.pure.js +26 -14
  82. package/Meshes/mesh.pure.js.map +1 -1
  83. package/Misc/tools.pure.js +1 -1
  84. package/Misc/tools.pure.js.map +1 -1
  85. package/Shaders/greasedLine.vertex.js +19 -3
  86. package/Shaders/greasedLine.vertex.js.map +1 -1
  87. package/package.json +3 -2
  88. package/scene.pure.d.ts +12 -0
  89. package/scene.pure.js +36 -0
  90. package/scene.pure.js.map +1 -1
@@ -0,0 +1,37 @@
1
+ /** This file must only contain pure code and pure imports */
2
+ /**
3
+ * Message commands exchanged with the Gaussian Splatting depth-sort worker. Every message carries a
4
+ * `command` field naming the intended work, instead of inferring it from which payload is present.
5
+ */
6
+ export declare const GaussianSplattingSortWorkerCommand: {
7
+ /** Main -> worker: set the source splat centers (stride 4: xyz + 1). */
8
+ readonly POSITIONS: "positions";
9
+ /** Main -> worker: set the compound-mesh rig node matrices. */
10
+ readonly PART_MATRICES: "partMatrices";
11
+ /** Main -> worker: set the compound-mesh per-splat rig node indices. */
12
+ readonly PART_INDICES: "partIndices";
13
+ /** Main -> worker: set the active source-splat ranges (flat [start0, count0, ...]). */
14
+ readonly INTERVALS: "intervals";
15
+ /** Main -> worker: sort the active splats for a camera view. */
16
+ readonly SORT: "sort";
17
+ /** Worker -> main: a completed sort result. */
18
+ readonly SORTED: "sorted";
19
+ };
20
+ /**
21
+ * Depth-sort web worker body for Gaussian Splatting meshes.
22
+ *
23
+ * The function is self-contained: it is serialized with `Function.prototype.toString()` and run
24
+ * inside a `Blob`-backed `Worker`, so it must not reference anything from its enclosing module
25
+ * (including {@link GaussianSplattingSortWorkerCommand} — the command literals are duplicated here).
26
+ *
27
+ * The intended work for each message is selected explicitly via its `command` field:
28
+ * - `positions` set source splat centers (stride 4: xyz + 1).
29
+ * - `partMatrices` set compound-mesh rig node matrices.
30
+ * - `partIndices` set compound-mesh per-splat rig node indices.
31
+ * - `intervals` set active source-splat ranges (flat [start0, count0, ...]); persisted across sorts.
32
+ * - `sort` sort the active splats for `{ worldMatrix, cameraForward, cameraPosition, depthMix,
33
+ * cameraId, sortRequestId }` and post back a `sorted` result.
34
+ *
35
+ * @param self - the worker global scope
36
+ */
37
+ export declare const GaussianSplattingSortWorker: (self: Worker) => void;
@@ -0,0 +1,160 @@
1
+ /** This file must only contain pure code and pure imports */
2
+ /**
3
+ * Message commands exchanged with the Gaussian Splatting depth-sort worker. Every message carries a
4
+ * `command` field naming the intended work, instead of inferring it from which payload is present.
5
+ */
6
+ export const GaussianSplattingSortWorkerCommand = {
7
+ /** Main -> worker: set the source splat centers (stride 4: xyz + 1). */
8
+ POSITIONS: "positions",
9
+ /** Main -> worker: set the compound-mesh rig node matrices. */
10
+ PART_MATRICES: "partMatrices",
11
+ /** Main -> worker: set the compound-mesh per-splat rig node indices. */
12
+ PART_INDICES: "partIndices",
13
+ /** Main -> worker: set the active source-splat ranges (flat [start0, count0, ...]). */
14
+ INTERVALS: "intervals",
15
+ /** Main -> worker: sort the active splats for a camera view. */
16
+ SORT: "sort",
17
+ /** Worker -> main: a completed sort result. */
18
+ SORTED: "sorted",
19
+ };
20
+ /**
21
+ * Depth-sort web worker body for Gaussian Splatting meshes.
22
+ *
23
+ * The function is self-contained: it is serialized with `Function.prototype.toString()` and run
24
+ * inside a `Blob`-backed `Worker`, so it must not reference anything from its enclosing module
25
+ * (including {@link GaussianSplattingSortWorkerCommand} — the command literals are duplicated here).
26
+ *
27
+ * The intended work for each message is selected explicitly via its `command` field:
28
+ * - `positions` set source splat centers (stride 4: xyz + 1).
29
+ * - `partMatrices` set compound-mesh rig node matrices.
30
+ * - `partIndices` set compound-mesh per-splat rig node indices.
31
+ * - `intervals` set active source-splat ranges (flat [start0, count0, ...]); persisted across sorts.
32
+ * - `sort` sort the active splats for `{ worldMatrix, cameraForward, cameraPosition, depthMix,
33
+ * cameraId, sortRequestId }` and post back a `sorted` result.
34
+ *
35
+ * @param self - the worker global scope
36
+ */
37
+ export const GaussianSplattingSortWorker = function (self) {
38
+ let positions;
39
+ let depthMix;
40
+ let indices;
41
+ let floatMix;
42
+ let partIndices;
43
+ let partMatrices;
44
+ // Active source-splat intervals as flat [start0, count0, start1, count1, ...]. Persisted between
45
+ // sort requests and refreshed by the main thread via the `intervals` message.
46
+ let intervals = null;
47
+ self.onmessage = (e) => {
48
+ // The intended work is selected explicitly via the message `command` field. These string
49
+ // literals must match GaussianSplattingSortWorkerCommand above (this body is serialized in
50
+ // isolation and cannot reference the shared constant).
51
+ const command = e.data.command;
52
+ if (command === "positions") {
53
+ positions = e.data.positions;
54
+ }
55
+ else if (command === "partMatrices") {
56
+ partMatrices = e.data.partMatrices;
57
+ }
58
+ else if (command === "partIndices") {
59
+ partIndices = e.data.partIndices;
60
+ }
61
+ else if (command === "intervals") {
62
+ intervals = e.data.intervals;
63
+ }
64
+ else if (command === "sort") {
65
+ const cameraId = e.data.cameraId;
66
+ const sortRequestId = e.data.sortRequestId;
67
+ const rangeVersion = e.data.rangeVersion;
68
+ const globalWorldMatrix = e.data.worldMatrix;
69
+ const cameraForward = e.data.cameraForward;
70
+ const cameraPosition = e.data.cameraPosition;
71
+ depthMix = e.data.depthMix;
72
+ if (!positions || !cameraForward) {
73
+ // Sort request arrived before positions were initialized — return the buffer unchanged so the main thread can unlock _canPostToWorker.
74
+ self.postMessage({ command: "sorted", depthMix, cameraId, sortRequestId, rangeVersion }, [depthMix.buffer]);
75
+ return;
76
+ }
77
+ // Resolve the active interval set. The main thread always sends an `intervals` message,
78
+ // but fall back to "all source splats" if a sort somehow arrives before it.
79
+ const activeIntervals = intervals;
80
+ let renderSplatCount = positions.length / 4;
81
+ if (activeIntervals) {
82
+ renderSplatCount = 0;
83
+ for (let rangeIndex = 1; rangeIndex < activeIntervals.length; rangeIndex += 2) {
84
+ renderSplatCount += activeIntervals[rangeIndex];
85
+ }
86
+ }
87
+ const vertexCountPadded = Math.max((renderSplatCount + 15) & ~0xf, 16);
88
+ indices = new Uint32Array(depthMix.buffer);
89
+ floatMix = new Float32Array(depthMix.buffer);
90
+ // Build the compact list of source-splat indices to sort.
91
+ if (activeIntervals) {
92
+ let writeIndex = 0;
93
+ for (let rangeIndex = 0; rangeIndex < activeIntervals.length; rangeIndex += 2) {
94
+ const start = activeIntervals[rangeIndex];
95
+ const count = activeIntervals[rangeIndex + 1];
96
+ for (let sourceIndex = start; sourceIndex < start + count; sourceIndex++) {
97
+ indices[2 * writeIndex++] = sourceIndex;
98
+ }
99
+ }
100
+ // Pad up to a multiple of 16 with an existing (back-to-front-harmless) source index.
101
+ for (; writeIndex < vertexCountPadded; writeIndex++) {
102
+ indices[2 * writeIndex] = 0;
103
+ }
104
+ }
105
+ else {
106
+ for (let j = 0; j < vertexCountPadded; j++) {
107
+ indices[2 * j] = j;
108
+ }
109
+ }
110
+ // depth = dot(cameraForward, worldPos - cameraPos)
111
+ const camDot = cameraForward[0] * cameraPosition[0] + cameraForward[1] * cameraPosition[1] + cameraForward[2] * cameraPosition[2];
112
+ const computeDepthCoeffs = (m) => {
113
+ return [
114
+ cameraForward[0] * m[0] + cameraForward[1] * m[1] + cameraForward[2] * m[2],
115
+ cameraForward[0] * m[4] + cameraForward[1] * m[5] + cameraForward[2] * m[6],
116
+ cameraForward[0] * m[8] + cameraForward[1] * m[9] + cameraForward[2] * m[10],
117
+ cameraForward[0] * m[12] + cameraForward[1] * m[13] + cameraForward[2] * m[14] - camDot,
118
+ ];
119
+ };
120
+ try {
121
+ if (partMatrices && partIndices) {
122
+ // Precompute depth coefficients for each rig node
123
+ const depthCoeffs = partMatrices.map((m) => computeDepthCoeffs(m));
124
+ // NB: For performance reasons, we assume that part indices are valid
125
+ const length = partIndices.length;
126
+ for (let j = 0; j < vertexCountPadded; j++) {
127
+ const sourceIndex = indices[2 * j];
128
+ // NB: We need this 'min' because vertex array is padded, not partIndices
129
+ const partIndex = partIndices[Math.min(sourceIndex, length - 1)];
130
+ const coeff = depthCoeffs[partIndex];
131
+ floatMix[2 * j + 1] =
132
+ coeff[0] * positions[4 * sourceIndex + 0] + coeff[1] * positions[4 * sourceIndex + 1] + coeff[2] * positions[4 * sourceIndex + 2] + coeff[3];
133
+ // instead of using minus to sort back to front, we use bitwise not operator to invert the order of indices
134
+ // might not be faster but a minus sign implies a reference value that may not be enough and will decrease floatting precision
135
+ indices[2 * j + 1] = ~indices[2 * j + 1];
136
+ }
137
+ }
138
+ else {
139
+ // Compute depth coefficients from global world matrix
140
+ const [a, b, c, d] = computeDepthCoeffs(globalWorldMatrix);
141
+ for (let j = 0; j < vertexCountPadded; j++) {
142
+ const sourceIndex = indices[2 * j];
143
+ floatMix[2 * j + 1] = a * positions[4 * sourceIndex + 0] + b * positions[4 * sourceIndex + 1] + c * positions[4 * sourceIndex + 2] + d;
144
+ indices[2 * j + 1] = ~indices[2 * j + 1];
145
+ }
146
+ }
147
+ depthMix.sort();
148
+ }
149
+ catch (sortError) {
150
+ // Transient data inconsistency (e.g. partIndices/partMatrices mismatch during addPart/removePart rebuild).
151
+ // Return the buffer unsorted so the main thread can unlock _canPostToWorker and retry next frame.
152
+ // Logger is unavailable inside the worker — console is the only option.
153
+ // eslint-disable-next-line no-console
154
+ console.error("Gaussian splat sort worker encountered an error (will retry next frame):", sortError);
155
+ }
156
+ self.postMessage({ command: "sorted", depthMix, cameraId, sortRequestId, rangeVersion }, [depthMix.buffer]);
157
+ }
158
+ };
159
+ };
160
+ //# sourceMappingURL=gaussianSplattingSortWorker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gaussianSplattingSortWorker.js","sourceRoot":"","sources":["../../../../../dev/core/src/Meshes/GaussianSplatting/gaussianSplattingSortWorker.ts"],"names":[],"mappings":"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","sourcesContent":["/** This file must only contain pure code and pure imports */\r\n\r\nimport { type Nullable } from \"core/types\";\r\n\r\n/**\r\n * Message commands exchanged with the Gaussian Splatting depth-sort worker. Every message carries a\r\n * `command` field naming the intended work, instead of inferring it from which payload is present.\r\n */\r\nexport const GaussianSplattingSortWorkerCommand = {\r\n /** Main -> worker: set the source splat centers (stride 4: xyz + 1). */\r\n POSITIONS: \"positions\",\r\n /** Main -> worker: set the compound-mesh rig node matrices. */\r\n PART_MATRICES: \"partMatrices\",\r\n /** Main -> worker: set the compound-mesh per-splat rig node indices. */\r\n PART_INDICES: \"partIndices\",\r\n /** Main -> worker: set the active source-splat ranges (flat [start0, count0, ...]). */\r\n INTERVALS: \"intervals\",\r\n /** Main -> worker: sort the active splats for a camera view. */\r\n SORT: \"sort\",\r\n /** Worker -> main: a completed sort result. */\r\n SORTED: \"sorted\",\r\n} as const;\r\n\r\n/**\r\n * Depth-sort web worker body for Gaussian Splatting meshes.\r\n *\r\n * The function is self-contained: it is serialized with `Function.prototype.toString()` and run\r\n * inside a `Blob`-backed `Worker`, so it must not reference anything from its enclosing module\r\n * (including {@link GaussianSplattingSortWorkerCommand} — the command literals are duplicated here).\r\n *\r\n * The intended work for each message is selected explicitly via its `command` field:\r\n * - `positions` set source splat centers (stride 4: xyz + 1).\r\n * - `partMatrices` set compound-mesh rig node matrices.\r\n * - `partIndices` set compound-mesh per-splat rig node indices.\r\n * - `intervals` set active source-splat ranges (flat [start0, count0, ...]); persisted across sorts.\r\n * - `sort` sort the active splats for `{ worldMatrix, cameraForward, cameraPosition, depthMix,\r\n * cameraId, sortRequestId }` and post back a `sorted` result.\r\n *\r\n * @param self - the worker global scope\r\n */\r\nexport const GaussianSplattingSortWorker = function (self: Worker) {\r\n let positions: Float32Array;\r\n let depthMix: BigInt64Array;\r\n let indices: Uint32Array;\r\n let floatMix: Float32Array;\r\n let partIndices: Uint8Array;\r\n let partMatrices: Float32Array[];\r\n // Active source-splat intervals as flat [start0, count0, start1, count1, ...]. Persisted between\r\n // sort requests and refreshed by the main thread via the `intervals` message.\r\n let intervals: Nullable<Uint32Array> = null;\r\n\r\n self.onmessage = (e: any) => {\r\n // The intended work is selected explicitly via the message `command` field. These string\r\n // literals must match GaussianSplattingSortWorkerCommand above (this body is serialized in\r\n // isolation and cannot reference the shared constant).\r\n const command = e.data.command;\r\n if (command === \"positions\") {\r\n positions = e.data.positions;\r\n } else if (command === \"partMatrices\") {\r\n partMatrices = e.data.partMatrices;\r\n } else if (command === \"partIndices\") {\r\n partIndices = e.data.partIndices;\r\n } else if (command === \"intervals\") {\r\n intervals = e.data.intervals;\r\n } else if (command === \"sort\") {\r\n const cameraId = e.data.cameraId;\r\n const sortRequestId = e.data.sortRequestId;\r\n const rangeVersion = e.data.rangeVersion;\r\n const globalWorldMatrix = e.data.worldMatrix;\r\n const cameraForward = e.data.cameraForward;\r\n const cameraPosition = e.data.cameraPosition;\r\n\r\n depthMix = e.data.depthMix;\r\n\r\n if (!positions || !cameraForward) {\r\n // Sort request arrived before positions were initialized — return the buffer unchanged so the main thread can unlock _canPostToWorker.\r\n self.postMessage({ command: \"sorted\", depthMix, cameraId, sortRequestId, rangeVersion }, [depthMix.buffer]);\r\n return;\r\n }\r\n\r\n // Resolve the active interval set. The main thread always sends an `intervals` message,\r\n // but fall back to \"all source splats\" if a sort somehow arrives before it.\r\n const activeIntervals = intervals;\r\n let renderSplatCount = positions.length / 4;\r\n if (activeIntervals) {\r\n renderSplatCount = 0;\r\n for (let rangeIndex = 1; rangeIndex < activeIntervals.length; rangeIndex += 2) {\r\n renderSplatCount += activeIntervals[rangeIndex];\r\n }\r\n }\r\n const vertexCountPadded = Math.max((renderSplatCount + 15) & ~0xf, 16);\r\n\r\n indices = new Uint32Array(depthMix.buffer);\r\n floatMix = new Float32Array(depthMix.buffer);\r\n\r\n // Build the compact list of source-splat indices to sort.\r\n if (activeIntervals) {\r\n let writeIndex = 0;\r\n for (let rangeIndex = 0; rangeIndex < activeIntervals.length; rangeIndex += 2) {\r\n const start = activeIntervals[rangeIndex];\r\n const count = activeIntervals[rangeIndex + 1];\r\n for (let sourceIndex = start; sourceIndex < start + count; sourceIndex++) {\r\n indices[2 * writeIndex++] = sourceIndex;\r\n }\r\n }\r\n // Pad up to a multiple of 16 with an existing (back-to-front-harmless) source index.\r\n for (; writeIndex < vertexCountPadded; writeIndex++) {\r\n indices[2 * writeIndex] = 0;\r\n }\r\n } else {\r\n for (let j = 0; j < vertexCountPadded; j++) {\r\n indices[2 * j] = j;\r\n }\r\n }\r\n\r\n // depth = dot(cameraForward, worldPos - cameraPos)\r\n const camDot = cameraForward[0] * cameraPosition[0] + cameraForward[1] * cameraPosition[1] + cameraForward[2] * cameraPosition[2];\r\n\r\n const computeDepthCoeffs = (m: Float32Array): number[] => {\r\n return [\r\n cameraForward[0] * m[0] + cameraForward[1] * m[1] + cameraForward[2] * m[2],\r\n cameraForward[0] * m[4] + cameraForward[1] * m[5] + cameraForward[2] * m[6],\r\n cameraForward[0] * m[8] + cameraForward[1] * m[9] + cameraForward[2] * m[10],\r\n cameraForward[0] * m[12] + cameraForward[1] * m[13] + cameraForward[2] * m[14] - camDot,\r\n ];\r\n };\r\n\r\n try {\r\n if (partMatrices && partIndices) {\r\n // Precompute depth coefficients for each rig node\r\n const depthCoeffs = partMatrices.map((m) => computeDepthCoeffs(m));\r\n\r\n // NB: For performance reasons, we assume that part indices are valid\r\n const length = partIndices.length;\r\n for (let j = 0; j < vertexCountPadded; j++) {\r\n const sourceIndex = indices[2 * j];\r\n // NB: We need this 'min' because vertex array is padded, not partIndices\r\n const partIndex = partIndices[Math.min(sourceIndex, length - 1)];\r\n const coeff = depthCoeffs[partIndex];\r\n floatMix[2 * j + 1] =\r\n coeff[0] * positions[4 * sourceIndex + 0] + coeff[1] * positions[4 * sourceIndex + 1] + coeff[2] * positions[4 * sourceIndex + 2] + coeff[3];\r\n // instead of using minus to sort back to front, we use bitwise not operator to invert the order of indices\r\n // might not be faster but a minus sign implies a reference value that may not be enough and will decrease floatting precision\r\n indices[2 * j + 1] = ~indices[2 * j + 1];\r\n }\r\n } else {\r\n // Compute depth coefficients from global world matrix\r\n const [a, b, c, d] = computeDepthCoeffs(globalWorldMatrix);\r\n for (let j = 0; j < vertexCountPadded; j++) {\r\n const sourceIndex = indices[2 * j];\r\n floatMix[2 * j + 1] = a * positions[4 * sourceIndex + 0] + b * positions[4 * sourceIndex + 1] + c * positions[4 * sourceIndex + 2] + d;\r\n indices[2 * j + 1] = ~indices[2 * j + 1];\r\n }\r\n }\r\n\r\n depthMix.sort();\r\n } catch (sortError) {\r\n // Transient data inconsistency (e.g. partIndices/partMatrices mismatch during addPart/removePart rebuild).\r\n // Return the buffer unsorted so the main thread can unlock _canPostToWorker and retry next frame.\r\n // Logger is unavailable inside the worker — console is the only option.\r\n // eslint-disable-next-line no-console\r\n console.error(\"Gaussian splat sort worker encountered an error (will retry next frame):\", sortError);\r\n }\r\n\r\n self.postMessage({ command: \"sorted\", depthMix, cameraId, sortRequestId, rangeVersion }, [depthMix.buffer]);\r\n }\r\n };\r\n};\r\n"]}
@@ -7,6 +7,7 @@ import { SceneLoaderFlags } from "../Loading/sceneLoaderFlags.js";
7
7
  import { BoundingInfo } from "../Culling/boundingInfo.js";
8
8
 
9
9
  import { Tools } from "../Misc/tools.pure.js";
10
+ import { Logger } from "../Misc/logger.js";
10
11
  import { Tags } from "../Misc/tags.js";
11
12
  import { extractMinAndMax } from "../Maths/math.functions.js";
12
13
  import { EngineStore } from "../Engines/engineStore.js";
@@ -682,23 +683,37 @@ export class Geometry {
682
683
  return;
683
684
  }
684
685
  scene.addPendingData(this);
685
- scene._loadFile(this.delayLoadingFile, (data) => {
686
- if (!this._delayLoadingFunction) {
687
- return;
688
- }
689
- this._delayLoadingFunction(JSON.parse(data), this);
690
- this.delayLoadState = 1;
691
- this._delayInfo = [];
692
- scene.removePendingData(this);
693
- const meshes = this._meshes;
694
- const numOfMeshes = meshes.length;
695
- for (let index = 0; index < numOfMeshes; index++) {
696
- this._applyToMesh(meshes[index]);
686
+ void (async () => {
687
+ try {
688
+ const data = await scene._loadDelayedFileAsync(this.delayLoadingFile, false, true);
689
+ if (!this._delayLoadingFunction) {
690
+ // The geometry was disposed while the load was in flight; still release the pending data so the scene can become ready.
691
+ scene.removePendingData(this);
692
+ return;
693
+ }
694
+ this._delayLoadingFunction(JSON.parse(data), this);
695
+ this.delayLoadState = 1;
696
+ this._delayInfo = [];
697
+ scene.removePendingData(this);
698
+ const meshes = this._meshes;
699
+ const numOfMeshes = meshes.length;
700
+ for (let index = 0; index < numOfMeshes; index++) {
701
+ this._applyToMesh(meshes[index]);
702
+ }
703
+ if (onLoaded) {
704
+ onLoaded();
705
+ }
697
706
  }
698
- if (onLoaded) {
699
- onLoaded();
707
+ catch (error) {
708
+ // Remove the pending data so the scene can still become ready.
709
+ // The state is intentionally left as LOADING so a persistently failing file is not re-fetched every frame.
710
+ scene.removePendingData(this);
711
+ // Don't log when the scene is being disposed: in-flight requests are aborted as part of normal teardown.
712
+ if (!scene.isDisposed) {
713
+ Logger.Error(`Unable to delay load geometry "${this.id}" from "${this.delayLoadingFile}": ${error}`);
714
+ }
700
715
  }
701
- }, undefined, true);
716
+ })();
702
717
  }
703
718
  /**
704
719
  * Invert the geometry to move from a right handed system to a left handed one.