@myned-ai/gsplat-flame-avatar-renderer 1.0.2 → 1.0.4

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 (66) hide show
  1. package/README.md +1 -21
  2. package/dist/gsplat-flame-avatar-renderer.cjs.js +12875 -0
  3. package/dist/{gsplat-flame-avatar-renderer.umd.js.map → gsplat-flame-avatar-renderer.cjs.js.map} +1 -1
  4. package/dist/gsplat-flame-avatar-renderer.esm.js +1 -1
  5. package/package.json +5 -3
  6. package/src/api/index.js +7 -0
  7. package/src/buffers/SplatBuffer.js +1394 -0
  8. package/src/buffers/SplatBufferGenerator.js +41 -0
  9. package/src/buffers/SplatPartitioner.js +110 -0
  10. package/src/buffers/UncompressedSplatArray.js +106 -0
  11. package/src/buffers/index.js +11 -0
  12. package/src/core/SplatGeometry.js +48 -0
  13. package/src/core/SplatMesh.js +2620 -0
  14. package/src/core/SplatScene.js +43 -0
  15. package/src/core/SplatTree.js +200 -0
  16. package/src/core/Viewer.js +2895 -0
  17. package/src/core/index.js +13 -0
  18. package/src/enums/EngineConstants.js +58 -0
  19. package/src/enums/LogLevel.js +13 -0
  20. package/src/enums/RenderMode.js +11 -0
  21. package/src/enums/SceneFormat.js +21 -0
  22. package/src/enums/SceneRevealMode.js +11 -0
  23. package/src/enums/SplatRenderMode.js +10 -0
  24. package/src/enums/index.js +13 -0
  25. package/src/flame/FlameAnimator.js +271 -0
  26. package/src/flame/FlameConstants.js +21 -0
  27. package/src/flame/FlameTextureManager.js +293 -0
  28. package/src/flame/index.js +22 -0
  29. package/src/flame/utils.js +50 -0
  30. package/src/index.js +39 -0
  31. package/src/loaders/DirectLoadError.js +14 -0
  32. package/src/loaders/INRIAV1PlyParser.js +223 -0
  33. package/src/loaders/PlyLoader.js +261 -0
  34. package/src/loaders/PlyParser.js +19 -0
  35. package/src/loaders/PlyParserUtils.js +311 -0
  36. package/src/loaders/index.js +13 -0
  37. package/src/materials/SplatMaterial.js +1065 -0
  38. package/src/materials/SplatMaterial2D.js +358 -0
  39. package/src/materials/SplatMaterial3D.js +278 -0
  40. package/src/materials/index.js +11 -0
  41. package/src/raycaster/Hit.js +37 -0
  42. package/src/raycaster/Ray.js +123 -0
  43. package/src/raycaster/Raycaster.js +175 -0
  44. package/src/raycaster/index.js +10 -0
  45. package/src/renderer/AnimationManager.js +574 -0
  46. package/src/renderer/AppConstants.js +101 -0
  47. package/src/renderer/GaussianSplatRenderer.js +695 -0
  48. package/src/renderer/index.js +24 -0
  49. package/src/utils/LoaderUtils.js +65 -0
  50. package/src/utils/Util.js +375 -0
  51. package/src/utils/index.js +9 -0
  52. package/src/worker/SortWorker.js +284 -0
  53. package/src/worker/index.js +8 -0
  54. package/dist/gsplat-flame-avatar-renderer.esm.min.js +0 -2
  55. package/dist/gsplat-flame-avatar-renderer.esm.min.js.map +0 -1
  56. package/dist/gsplat-flame-avatar-renderer.umd.js +0 -12876
  57. package/dist/gsplat-flame-avatar-renderer.umd.min.js +0 -2
  58. package/dist/gsplat-flame-avatar-renderer.umd.min.js.map +0 -1
  59. package/dist/gsplat-flame-avatar.esm.js +0 -12755
  60. package/dist/gsplat-flame-avatar.esm.js.map +0 -1
  61. package/dist/gsplat-flame-avatar.esm.min.js +0 -2
  62. package/dist/gsplat-flame-avatar.esm.min.js.map +0 -1
  63. package/dist/gsplat-flame-avatar.umd.js +0 -12876
  64. package/dist/gsplat-flame-avatar.umd.js.map +0 -1
  65. package/dist/gsplat-flame-avatar.umd.min.js +0 -2
  66. package/dist/gsplat-flame-avatar.umd.min.js.map +0 -1
@@ -0,0 +1,24 @@
1
+ /**
2
+ * gsplat-flame-avatar - Renderer Module
3
+ * Main exports for the renderer classes
4
+ */
5
+
6
+ // Constants
7
+ export * from './AppConstants.js';
8
+
9
+ // Animation - includes state classes
10
+ export {
11
+ AnimationManager,
12
+ State,
13
+ Hello,
14
+ Idle,
15
+ Listen,
16
+ Think,
17
+ Speak
18
+ } from './AnimationManager.js';
19
+
20
+ // Main Renderer
21
+ export { GaussianSplatRenderer } from './GaussianSplatRenderer.js';
22
+
23
+ // Default export
24
+ export { GaussianSplatRenderer as default } from './GaussianSplatRenderer.js';
@@ -0,0 +1,65 @@
1
+ /**
2
+ * LoaderUtils
3
+ *
4
+ * Derived from @mkkellogg/gaussian-splats-3d (MIT License)
5
+ * https://github.com/mkkellogg/GaussianSplats3D
6
+ *
7
+ * Loader utilities for decoding text and resolving URLs.
8
+ */
9
+
10
+ export class LoaderUtils {
11
+
12
+ /**
13
+ * @deprecated Use TextDecoder instead
14
+ */
15
+ static decodeText(array) {
16
+ if (typeof TextDecoder !== 'undefined') {
17
+ return new TextDecoder().decode(array);
18
+ }
19
+
20
+ // Fallback for environments without TextDecoder
21
+ let s = '';
22
+
23
+ for (let i = 0, il = array.length; i < il; i++) {
24
+ // Implicitly assumes little-endian.
25
+ s += String.fromCharCode(array[i]);
26
+ }
27
+
28
+ try {
29
+ // merges multi-byte utf-8 characters.
30
+ return decodeURIComponent(escape(s));
31
+ } catch (e) { // see #16358
32
+ return s;
33
+ }
34
+ }
35
+
36
+ static extractUrlBase(url) {
37
+ const index = url.lastIndexOf('/');
38
+
39
+ if (index === -1) return './';
40
+
41
+ return url.slice(0, index + 1);
42
+ }
43
+
44
+ static resolveURL(url, path) {
45
+ // Invalid URL
46
+ if (typeof url !== 'string' || url === '') return '';
47
+
48
+ // Host Relative URL
49
+ if (/^https?:\/\//i.test(path) && /^\//.test(url)) {
50
+ path = path.replace(/(^https?:\/\/[^/]+).*/i, '$1');
51
+ }
52
+
53
+ // Absolute URL http://,https://,//
54
+ if (/^(https?:)?\/\//i.test(url)) return url;
55
+
56
+ // Data URI
57
+ if (/^data:.*,.*$/i.test(url)) return url;
58
+
59
+ // Blob URL
60
+ if (/^blob:.*$/i.test(url)) return url;
61
+
62
+ // Relative URL
63
+ return path + url;
64
+ }
65
+ }
@@ -0,0 +1,375 @@
1
+ /**
2
+ * Core utility functions for Gaussian Splat rendering
3
+ *
4
+ * Derived from @mkkellogg/gaussian-splats-3d (MIT License)
5
+ * https://github.com/mkkellogg/GaussianSplats3D
6
+ *
7
+ * Import paths adjusted for gsplat-flame-avatar package structure.
8
+ */
9
+
10
+ import { DataUtils } from 'three';
11
+
12
+ /**
13
+ * Custom error for aborted operations
14
+ */
15
+ export class AbortedPromiseError extends Error {
16
+ constructor(msg) {
17
+ super(msg);
18
+ this.name = 'AbortedPromiseError';
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Fetch with progress tracking using standard AbortController
24
+ * Returns a Promise with an attached `abort()` method and `abortController`
25
+ */
26
+ export const fetchWithProgress = function(path, onProgress, saveChunks = true, headers) {
27
+
28
+ const abortController = new AbortController();
29
+ const signal = abortController.signal;
30
+ let aborted = false;
31
+
32
+ let onProgressCalledAtComplete = false;
33
+ const localOnProgress = (percent, percentLabel, chunk, fileSize) => {
34
+ if (onProgress && !onProgressCalledAtComplete) {
35
+ onProgress(percent, percentLabel, chunk, fileSize);
36
+ if (percent === 100) {
37
+ onProgressCalledAtComplete = true;
38
+ }
39
+ }
40
+ };
41
+
42
+ const promise = new Promise((resolve, reject) => {
43
+ const fetchOptions = { signal };
44
+ if (headers) fetchOptions.headers = headers;
45
+
46
+ fetch(path, fetchOptions)
47
+ .then(async (data) => {
48
+ // Handle error conditions where data is still returned
49
+ if (!data.ok) {
50
+ const errorText = await data.text();
51
+ reject(new Error(`Fetch failed: ${data.status} ${data.statusText} ${errorText}`));
52
+ return;
53
+ }
54
+
55
+ const reader = data.body.getReader();
56
+ let bytesDownloaded = 0;
57
+ let _fileSize = data.headers.get('Content-Length');
58
+ let fileSize = _fileSize ? parseInt(_fileSize) : undefined;
59
+
60
+ const chunks = [];
61
+
62
+ while (!aborted) {
63
+ try {
64
+ const { value: chunk, done } = await reader.read();
65
+ if (done) {
66
+ localOnProgress(100, '100%', chunk, fileSize);
67
+ if (saveChunks) {
68
+ const buffer = new Blob(chunks).arrayBuffer();
69
+ resolve(buffer);
70
+ } else {
71
+ resolve();
72
+ }
73
+ break;
74
+ }
75
+ bytesDownloaded += chunk.length;
76
+ let percent;
77
+ let percentLabel;
78
+ if (fileSize !== undefined) {
79
+ percent = bytesDownloaded / fileSize * 100;
80
+ percentLabel = `${percent.toFixed(2)}%`;
81
+ }
82
+ if (saveChunks) {
83
+ chunks.push(chunk);
84
+ }
85
+ localOnProgress(percent, percentLabel, chunk, fileSize);
86
+ } catch (error) {
87
+ reject(error);
88
+ return;
89
+ }
90
+ }
91
+ })
92
+ .catch((error) => {
93
+ if (error.name === 'AbortError') {
94
+ reject(new AbortedPromiseError('Fetch aborted'));
95
+ } else {
96
+ reject(new AbortedPromiseError(error.message || error));
97
+ }
98
+ });
99
+ });
100
+
101
+ // Attach abort functionality to the promise
102
+ promise.abort = (reason) => {
103
+ aborted = true;
104
+ abortController.abort(reason);
105
+ };
106
+ promise.abortController = abortController;
107
+
108
+ return promise;
109
+ };
110
+
111
+ // Clamp value between min and max
112
+ export const clamp = function(val, min, max) {
113
+ return Math.max(Math.min(val, max), min);
114
+ };
115
+
116
+ // Get current time in seconds
117
+ export const getCurrentTime = function() {
118
+ return performance.now() / 1000;
119
+ };
120
+
121
+ // Dispose all meshes in a scene graph
122
+ export const disposeAllMeshes = (object3D) => {
123
+ if (object3D.geometry) {
124
+ object3D.geometry.dispose();
125
+ object3D.geometry = null;
126
+ }
127
+ if (object3D.material) {
128
+ object3D.material.dispose();
129
+ object3D.material = null;
130
+ }
131
+ if (object3D.children) {
132
+ for (let child of object3D.children) {
133
+ disposeAllMeshes(child);
134
+ }
135
+ }
136
+ };
137
+
138
+ // Delayed execution helper
139
+ export const delayedExecute = (func, fast) => {
140
+ return new Promise((resolve) => {
141
+ window.setTimeout(() => {
142
+ resolve(func());
143
+ }, fast ? 1 : 50);
144
+ });
145
+ };
146
+
147
+ // Get spherical harmonics component count for degree
148
+ export const getSphericalHarmonicsComponentCountForDegree = (sphericalHarmonicsDegree = 0) => {
149
+ switch (sphericalHarmonicsDegree) {
150
+ case 1:
151
+ return 9;
152
+ case 2:
153
+ return 24;
154
+ }
155
+ return 0;
156
+ };
157
+
158
+ // Create native promise with extracted components
159
+ export const nativePromiseWithExtractedComponents = () => {
160
+ let resolver;
161
+ let rejecter;
162
+ const promise = new Promise((resolve, reject) => {
163
+ resolver = resolve;
164
+ rejecter = reject;
165
+ });
166
+ return {
167
+ 'promise': promise,
168
+ 'resolve': resolver,
169
+ 'reject': rejecter
170
+ };
171
+ };
172
+
173
+ /**
174
+ * Create a promise with extracted resolve/reject functions and optional abort capability
175
+ * Uses standard Promise with attached abort method
176
+ */
177
+ export const abortablePromiseWithExtractedComponents = (abortHandler) => {
178
+ let resolver;
179
+ let rejecter;
180
+ const promise = new Promise((resolve, reject) => {
181
+ resolver = resolve;
182
+ rejecter = reject;
183
+ });
184
+
185
+ // Attach abort method to promise
186
+ promise.abort = abortHandler || (() => {});
187
+
188
+ return {
189
+ 'promise': promise,
190
+ 'resolve': resolver,
191
+ 'reject': rejecter
192
+ };
193
+ };
194
+
195
+ // Semver class for version handling
196
+ export class Semver {
197
+ constructor(major, minor, patch) {
198
+ this.major = major;
199
+ this.minor = minor;
200
+ this.patch = patch;
201
+ }
202
+
203
+ toString() {
204
+ return `${this.major}_${this.minor}_${this.patch}`;
205
+ }
206
+ }
207
+
208
+ // iOS detection
209
+ export function isIOS() {
210
+ const ua = navigator.userAgent;
211
+ return ua.indexOf('iPhone') > 0 || ua.indexOf('iPad') > 0;
212
+ }
213
+
214
+ export function getIOSSemever() {
215
+ if (isIOS()) {
216
+ const extract = navigator.userAgent.match(/OS (\d+)_(\d+)_?(\d+)?/);
217
+ return new Semver(
218
+ parseInt(extract[1] || 0, 10),
219
+ parseInt(extract[2] || 0, 10),
220
+ parseInt(extract[3] || 0, 10)
221
+ );
222
+ }
223
+ return null;
224
+ }
225
+
226
+ // Half float conversion utilities
227
+ export const toHalfFloat = DataUtils.toHalfFloat.bind(DataUtils);
228
+ export const fromHalfFloat = DataUtils.fromHalfFloat.bind(DataUtils);
229
+
230
+ // Default spherical harmonics compression range (imported from enums)
231
+ import { DefaultSphericalHarmonics8BitCompressionRange } from '../enums/EngineConstants.js';
232
+ export { DefaultSphericalHarmonics8BitCompressionRange };
233
+ export const DefaultSphericalHarmonics8BitCompressionHalfRange = DefaultSphericalHarmonics8BitCompressionRange / 2.0;
234
+
235
+ // Uncompress float based on compression level
236
+ export const toUncompressedFloat = (f, compressionLevel, isSH = false, range8BitMin, range8BitMax) => {
237
+ if (compressionLevel === 0) {
238
+ return f;
239
+ } else if (compressionLevel === 1 || (compressionLevel === 2 && !isSH)) {
240
+ return DataUtils.fromHalfFloat(f);
241
+ } else if (compressionLevel === 2) {
242
+ return fromUint8(f, range8BitMin, range8BitMax);
243
+ }
244
+ };
245
+
246
+ // Convert to uint8
247
+ export const toUint8 = (v, rangeMin, rangeMax) => {
248
+ v = clamp(v, rangeMin, rangeMax);
249
+ const range = (rangeMax - rangeMin);
250
+ return clamp(Math.floor((v - rangeMin) / range * 255), 0, 255);
251
+ };
252
+
253
+ // Convert from uint8
254
+ export const fromUint8 = (v, rangeMin, rangeMax) => {
255
+ const range = (rangeMax - rangeMin);
256
+ return (v / 255 * range + rangeMin);
257
+ };
258
+
259
+ // Half float to uint8
260
+ export const fromHalfFloatToUint8 = (v, rangeMin, rangeMax) => {
261
+ return toUint8(fromHalfFloat(v), rangeMin, rangeMax);
262
+ };
263
+
264
+ // Uint8 to half float
265
+ export const fromUint8ToHalfFloat = (v, rangeMin, rangeMax) => {
266
+ return toHalfFloat(fromUint8(v, rangeMin, rangeMax));
267
+ };
268
+
269
+ // Read float from DataView based on compression level
270
+ export const dataViewFloatForCompressionLevel = (dataView, floatIndex, compressionLevel, isSH = false) => {
271
+ if (compressionLevel === 0) {
272
+ return dataView.getFloat32(floatIndex * 4, true);
273
+ } else if (compressionLevel === 1 || (compressionLevel === 2 && !isSH)) {
274
+ return dataView.getUint16(floatIndex * 2, true);
275
+ } else {
276
+ return dataView.getUint8(floatIndex, true);
277
+ }
278
+ };
279
+
280
+ // Convert between compression levels
281
+ export const convertBetweenCompressionLevels = function() {
282
+ const noop = (v) => v;
283
+
284
+ return function(val, fromLevel, toLevel, isSH = false) {
285
+ if (fromLevel === toLevel) return val;
286
+ let outputConversionFunc = noop;
287
+
288
+ if (fromLevel === 2 && isSH) {
289
+ if (toLevel === 1) outputConversionFunc = fromUint8ToHalfFloat;
290
+ else if (toLevel === 0) {
291
+ outputConversionFunc = fromUint8;
292
+ }
293
+ } else if (fromLevel === 2 || fromLevel === 1) {
294
+ if (toLevel === 0) outputConversionFunc = fromHalfFloat;
295
+ else if (toLevel === 2) {
296
+ if (!isSH) outputConversionFunc = noop;
297
+ else outputConversionFunc = fromHalfFloatToUint8;
298
+ }
299
+ } else if (fromLevel === 0) {
300
+ if (toLevel === 1) outputConversionFunc = toHalfFloat;
301
+ else if (toLevel === 2) {
302
+ if (!isSH) outputConversionFunc = toHalfFloat;
303
+ else outputConversionFunc = toUint8;
304
+ }
305
+ }
306
+
307
+ return outputConversionFunc(val);
308
+ };
309
+ }();
310
+
311
+ // Copy between buffers
312
+ export const copyBetweenBuffers = (srcBuffer, srcOffset, destBuffer, destOffset, byteCount = 0) => {
313
+ const src = new Uint8Array(srcBuffer, srcOffset);
314
+ const dest = new Uint8Array(destBuffer, destOffset);
315
+ for (let i = 0; i < byteCount; i++) {
316
+ dest[i] = src[i];
317
+ }
318
+ };
319
+
320
+ // Float to half float conversion
321
+ export const floatToHalf = function() {
322
+ const floatView = new Float32Array(1);
323
+ const int32View = new Int32Array(floatView.buffer);
324
+
325
+ return function(val) {
326
+ floatView[0] = val;
327
+ const x = int32View[0];
328
+
329
+ let bits = (x >> 16) & 0x8000;
330
+ let m = (x >> 12) & 0x07ff;
331
+ const e = (x >> 23) & 0xff;
332
+
333
+ if (e < 103) return bits;
334
+
335
+ if (e > 142) {
336
+ bits |= 0x7c00;
337
+ bits |= ((e == 255) ? 0 : 1) && (x & 0x007fffff);
338
+ return bits;
339
+ }
340
+
341
+ if (e < 113) {
342
+ m |= 0x0800;
343
+ bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1);
344
+ return bits;
345
+ }
346
+
347
+ bits |= ((e - 112) << 10) | (m >> 1);
348
+ bits += m & 1;
349
+ return bits;
350
+ };
351
+ }();
352
+
353
+ // Encode float as uint
354
+ export const uintEncodedFloat = function() {
355
+ const floatView = new Float32Array(1);
356
+ const int32View = new Int32Array(floatView.buffer);
357
+
358
+ return function(f) {
359
+ floatView[0] = f;
360
+ return int32View[0];
361
+ };
362
+ }();
363
+
364
+ // RGBA to integer
365
+ export const rgbaToInteger = function(r, g, b, a) {
366
+ return r + (g << 8) + (b << 16) + (a << 24);
367
+ };
368
+
369
+ // RGBA array to integer
370
+ export const rgbaArrayToInteger = function(arr, offset) {
371
+ return arr[offset] + (arr[offset + 1] << 8) + (arr[offset + 2] << 16) + (arr[offset + 3] << 24);
372
+ };
373
+
374
+ // BASE_COMPONENT_COUNT for UncompressedSplatArray
375
+ export const BASE_COMPONENT_COUNT = 14;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * gsplat-flame-avatar - Utils Module
3
+ * Shared utility functions.
4
+ *
5
+ * Derived from @mkkellogg/gaussian-splats-3d (MIT License)
6
+ */
7
+
8
+ export * from './LoaderUtils.js';
9
+ export * from './Util.js';