@myned-ai/gsplat-flame-avatar-renderer 1.0.6 → 1.0.7

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 (64) hide show
  1. package/README.md +30 -0
  2. package/dist/gsplat-flame-avatar-renderer.cjs.js +38 -33
  3. package/dist/gsplat-flame-avatar-renderer.cjs.min.js +1 -1
  4. package/dist/gsplat-flame-avatar-renderer.cjs.min.js.map +1 -1
  5. package/dist/gsplat-flame-avatar-renderer.esm.js +38 -33
  6. package/dist/gsplat-flame-avatar-renderer.esm.min.js +1 -1
  7. package/dist/gsplat-flame-avatar-renderer.esm.min.js.map +1 -1
  8. package/package.json +5 -2
  9. package/src/api/index.js +0 -7
  10. package/src/buffers/SplatBuffer.js +0 -1394
  11. package/src/buffers/SplatBufferGenerator.js +0 -41
  12. package/src/buffers/SplatPartitioner.js +0 -110
  13. package/src/buffers/UncompressedSplatArray.js +0 -106
  14. package/src/buffers/index.js +0 -11
  15. package/src/core/SplatGeometry.js +0 -48
  16. package/src/core/SplatMesh.js +0 -2627
  17. package/src/core/SplatScene.js +0 -43
  18. package/src/core/SplatTree.js +0 -200
  19. package/src/core/Viewer.js +0 -2746
  20. package/src/core/index.js +0 -13
  21. package/src/enums/EngineConstants.js +0 -58
  22. package/src/enums/LogLevel.js +0 -13
  23. package/src/enums/RenderMode.js +0 -11
  24. package/src/enums/SceneFormat.js +0 -21
  25. package/src/enums/SceneRevealMode.js +0 -11
  26. package/src/enums/SplatRenderMode.js +0 -10
  27. package/src/enums/index.js +0 -13
  28. package/src/errors/ApplicationError.js +0 -185
  29. package/src/errors/index.js +0 -17
  30. package/src/flame/FlameAnimator.js +0 -496
  31. package/src/flame/FlameConstants.js +0 -21
  32. package/src/flame/FlameTextureManager.js +0 -293
  33. package/src/flame/index.js +0 -22
  34. package/src/flame/utils.js +0 -50
  35. package/src/index.js +0 -39
  36. package/src/loaders/DirectLoadError.js +0 -14
  37. package/src/loaders/INRIAV1PlyParser.js +0 -223
  38. package/src/loaders/PlyLoader.js +0 -519
  39. package/src/loaders/PlyParser.js +0 -19
  40. package/src/loaders/PlyParserUtils.js +0 -311
  41. package/src/loaders/index.js +0 -13
  42. package/src/materials/SplatMaterial.js +0 -1068
  43. package/src/materials/SplatMaterial2D.js +0 -358
  44. package/src/materials/SplatMaterial3D.js +0 -323
  45. package/src/materials/index.js +0 -11
  46. package/src/raycaster/Hit.js +0 -37
  47. package/src/raycaster/Ray.js +0 -123
  48. package/src/raycaster/Raycaster.js +0 -175
  49. package/src/raycaster/index.js +0 -10
  50. package/src/renderer/AnimationManager.js +0 -577
  51. package/src/renderer/AppConstants.js +0 -101
  52. package/src/renderer/GaussianSplatRenderer.js +0 -1146
  53. package/src/renderer/index.js +0 -24
  54. package/src/utils/BlobUrlManager.js +0 -294
  55. package/src/utils/EventEmitter.js +0 -349
  56. package/src/utils/LoaderUtils.js +0 -66
  57. package/src/utils/Logger.js +0 -171
  58. package/src/utils/ObjectPool.js +0 -248
  59. package/src/utils/RenderLoop.js +0 -306
  60. package/src/utils/Util.js +0 -416
  61. package/src/utils/ValidationUtils.js +0 -331
  62. package/src/utils/index.js +0 -18
  63. package/src/worker/SortWorker.js +0 -284
  64. package/src/worker/index.js +0 -8
@@ -1,519 +0,0 @@
1
- /**
2
- * PlyLoader - Loads and parses PLY format Gaussian Splat files
3
- *
4
- * Derived from @mkkellogg/gaussian-splats-3d (MIT License)
5
- * https://github.com/mkkellogg/GaussianSplats3D
6
- *
7
- * Simplified for FLAME avatar - only supports INRIAV1 PLY format.
8
- * Provides both progressive streaming and file-based loading.
9
- */
10
-
11
- import { Vector3 } from 'three';
12
- import { SplatBuffer } from '../buffers/SplatBuffer.js';
13
- import { UncompressedSplatArray } from '../buffers/UncompressedSplatArray.js';
14
- import { SplatBufferGenerator } from '../buffers/SplatBufferGenerator.js';
15
- import { PlyParser } from './PlyParser.js';
16
- import { PlyParserUtils } from './PlyParserUtils.js';
17
- import { INRIAV1PlyParser } from './INRIAV1PlyParser.js';
18
- import { Constants, InternalLoadType, LoaderStatus } from '../enums/EngineConstants.js';
19
- import { fetchWithProgress, delayedExecute, nativePromiseWithExtractedComponents } from '../utils/Util.js';
20
- import { getLogger } from '../utils/Logger.js';
21
- import { ValidationError, NetworkError, ParseError, AssetLoadError } from '../errors/index.js';
22
- import { validateUrl, validateCallback, validateArrayBuffer } from '../utils/ValidationUtils.js';
23
-
24
- const logger = getLogger('PlyLoader');
25
-
26
- /**
27
- * Store data chunks into a single ArrayBuffer
28
- *
29
- * Combines multiple downloaded chunks into a contiguous buffer for parsing.
30
- * Reallocates buffer if needed to fit all chunks.
31
- *
32
- * @private
33
- * @param {Array<{data: Uint8Array, sizeBytes: number}>} chunks - Array of data chunks
34
- * @param {ArrayBuffer} [buffer] - Existing buffer to reuse (will reallocate if too small)
35
- * @returns {ArrayBuffer} Buffer containing all chunk data
36
- */
37
- function storeChunksInBuffer(chunks, buffer) {
38
- let inBytes = 0;
39
- for (let chunk of chunks) {
40
- inBytes += chunk.sizeBytes;
41
- }
42
-
43
- // Reallocate if buffer doesn't exist or is too small
44
- if (!buffer || buffer.byteLength < inBytes) {
45
- buffer = new ArrayBuffer(inBytes);
46
- }
47
-
48
- // Copy all chunks into buffer sequentially
49
- let offset = 0;
50
- for (let chunk of chunks) {
51
- new Uint8Array(buffer, offset, chunk.sizeBytes).set(chunk.data);
52
- offset += chunk.sizeBytes;
53
- }
54
-
55
- return buffer;
56
- }
57
-
58
- /**
59
- * Finalize splat data into a SplatBuffer
60
- *
61
- * Converts UncompressedSplatArray into final optimized SplatBuffer format.
62
- * Applies compression and optimization if requested.
63
- *
64
- * @private
65
- * @param {UncompressedSplatArray} splatData - Parsed splat data
66
- * @param {boolean} optimizeSplatData - Whether to optimize/compress the data
67
- * @param {number} minimumAlpha - Minimum alpha threshold for splat culling
68
- * @param {number} compressionLevel - Compression level (0-2)
69
- * @param {number} sectionSize - Section size for partitioning
70
- * @param {Vector3} sceneCenter - Center point of the scene
71
- * @param {number} blockSize - Block size for spatial partitioning
72
- * @param {number} bucketSize - Bucket size for sorting
73
- * @returns {SplatBuffer} Finalized splat buffer ready for rendering
74
- * @throws {ParseError} If splat data is invalid or finalization fails
75
- */
76
- function finalizeSplatData(splatData, optimizeSplatData, minimumAlpha, compressionLevel, sectionSize, sceneCenter, blockSize, bucketSize) {
77
- try {
78
- if (optimizeSplatData) {
79
- const splatBufferGenerator = SplatBufferGenerator.getStandardGenerator(
80
- minimumAlpha,
81
- compressionLevel,
82
- sectionSize,
83
- sceneCenter,
84
- blockSize,
85
- bucketSize
86
- );
87
- return splatBufferGenerator.generateFromUncompressedSplatArray(splatData);
88
- } else {
89
- return SplatBuffer.generateFromUncompressedSplatArrays([splatData], minimumAlpha, 0, new Vector3());
90
- }
91
- } catch (error) {
92
- throw new ParseError(
93
- `Failed to finalize splat data: ${error.message}`,
94
- 'splatData',
95
- error
96
- );
97
- }
98
- }
99
-
100
- /**
101
- * PlyLoader - Loads and parses PLY format Gaussian Splat files
102
- *
103
- * Supports both progressive streaming and complete file loading.
104
- * Optimized for INRIAV1 PLY format used in FLAME avatars.
105
- */
106
- export class PlyLoader {
107
-
108
- /**
109
- * Load PLY file from URL with progressive streaming support
110
- *
111
- * Downloads and parses PLY data progressively, enabling render during load.
112
- * Supports both direct-to-buffer and array-based loading modes.
113
- *
114
- * @static
115
- * @param {string} fileName - URL to the PLY file
116
- * @param {Function} [onProgress] - Progress callback (percent, percentLabel, status)
117
- * @param {boolean} [loadDirectoToSplatBuffer=false] - Load directly to SplatBuffer (faster but less flexible)
118
- * @param {Function} [onProgressiveLoadSectionProgress] - Callback for progressive section updates
119
- * @param {number} [minimumAlpha=1] - Minimum alpha threshold for splat culling
120
- * @param {number} [compressionLevel=0] - Compression level (0=none, 1=medium, 2=high)
121
- * @param {boolean} [optimizeSplatData=true] - Whether to optimize/compress splat data
122
- * @param {number} [outSphericalHarmonicsDegree=0] - Spherical harmonics degree (0-3)
123
- * @param {Object} [headers] - HTTP headers for fetch request
124
- * @param {number} [sectionSize] - Section size for partitioning
125
- * @param {Vector3} [sceneCenter] - Center point of the scene
126
- * @param {number} [blockSize] - Block size for spatial partitioning
127
- * @param {number} [bucketSize] - Bucket size for sorting
128
- * @returns {Promise<SplatBuffer>} Loaded and parsed splat buffer
129
- * @throws {ValidationError} If parameters are invalid
130
- * @throws {NetworkError} If file download fails
131
- * @throws {ParseError} If PLY parsing fails
132
- * @throws {AssetLoadError} If asset loading fails
133
- */
134
- static loadFromURL(fileName, onProgress, loadDirectoToSplatBuffer, onProgressiveLoadSectionProgress,
135
- minimumAlpha, compressionLevel, optimizeSplatData = true, outSphericalHarmonicsDegree = 0,
136
- headers, sectionSize, sceneCenter, blockSize, bucketSize) {
137
-
138
- // Validate required parameters
139
- try {
140
- validateUrl(fileName);
141
- } catch (error) {
142
- logger.error('Invalid URL provided to loadFromURL', { fileName, error });
143
- throw error;
144
- }
145
-
146
- // Validate optional callbacks
147
- if (onProgress) {
148
- validateCallback(onProgress, 'onProgress', false);
149
- }
150
- if (onProgressiveLoadSectionProgress) {
151
- validateCallback(onProgressiveLoadSectionProgress, 'onProgressiveLoadSectionProgress', false);
152
- }
153
-
154
- logger.info('Loading PLY from URL', { fileName, optimizeSplatData, outSphericalHarmonicsDegree });
155
-
156
- let internalLoadType = loadDirectoToSplatBuffer ? InternalLoadType.DirectToSplatBuffer : InternalLoadType.DirectToSplatArray;
157
- if (optimizeSplatData) internalLoadType = InternalLoadType.DirectToSplatArray;
158
-
159
- const directLoadSectionSizeBytes = Constants.ProgressiveLoadSectionSize;
160
- const splatDataOffsetBytes = SplatBuffer.HeaderSizeBytes + SplatBuffer.SectionHeaderSizeBytes;
161
- const sectionCount = 1;
162
-
163
- let directLoadBufferIn;
164
- let directLoadBufferOut;
165
- let directLoadSplatBuffer;
166
- let maxSplatCount = 0;
167
- let splatCount = 0;
168
-
169
- let headerLoaded = false;
170
- let readyToLoadSplatData = false;
171
-
172
- const loadPromise = nativePromiseWithExtractedComponents();
173
-
174
- let numBytesStreamed = 0;
175
- let numBytesParsed = 0;
176
- let numBytesDownloaded = 0;
177
- let headerText = '';
178
- let header = null;
179
- let chunks = [];
180
-
181
- let standardLoadUncompressedSplatArray;
182
-
183
- const textDecoder = new TextDecoder();
184
- const inriaV1PlyParser = new INRIAV1PlyParser();
185
-
186
- const localOnProgress = (percent, percentLabel, chunkData) => {
187
- const loadComplete = percent >= 100;
188
-
189
- if (chunkData) {
190
- chunks.push({
191
- 'data': chunkData,
192
- 'sizeBytes': chunkData.byteLength,
193
- 'startBytes': numBytesDownloaded,
194
- 'endBytes': numBytesDownloaded + chunkData.byteLength
195
- });
196
- numBytesDownloaded += chunkData.byteLength;
197
- }
198
-
199
- if (internalLoadType === InternalLoadType.DownloadBeforeProcessing) {
200
- if (loadComplete) {
201
- loadPromise.resolve(chunks);
202
- }
203
- } else {
204
- if (!headerLoaded) {
205
- headerText += textDecoder.decode(chunkData);
206
- if (PlyParserUtils.checkTextForEndHeader(headerText)) {
207
- // FLAME avatars use INRIAV1 format - parse header
208
- try {
209
- header = inriaV1PlyParser.decodeHeaderText(headerText);
210
- maxSplatCount = header.splatCount;
211
- readyToLoadSplatData = true;
212
-
213
- logger.debug('PLY header decoded', {
214
- splatCount: maxSplatCount,
215
- sphericalHarmonicsDegree: header.sphericalHarmonicsDegree
216
- });
217
-
218
- outSphericalHarmonicsDegree = Math.min(outSphericalHarmonicsDegree, header.sphericalHarmonicsDegree);
219
- } catch (error) {
220
- const parseError = new ParseError(
221
- `Failed to decode PLY header: ${error.message}`,
222
- 'headerText',
223
- error
224
- );
225
- logger.error('Header parsing failed', parseError);
226
- loadPromise.reject(parseError);
227
- return;
228
- }
229
-
230
- const shDescriptor = SplatBuffer.CompressionLevels[0].SphericalHarmonicsDegrees[outSphericalHarmonicsDegree];
231
- const splatBufferSizeBytes = splatDataOffsetBytes + shDescriptor.BytesPerSplat * maxSplatCount;
232
-
233
- if (internalLoadType === InternalLoadType.DirectToSplatBuffer) {
234
- directLoadBufferOut = new ArrayBuffer(splatBufferSizeBytes);
235
- SplatBuffer.writeHeaderToBuffer({
236
- versionMajor: SplatBuffer.CurrentMajorVersion,
237
- versionMinor: SplatBuffer.CurrentMinorVersion,
238
- maxSectionCount: sectionCount,
239
- sectionCount: sectionCount,
240
- maxSplatCount: maxSplatCount,
241
- splatCount: splatCount,
242
- compressionLevel: 0,
243
- sceneCenter: new Vector3()
244
- }, directLoadBufferOut);
245
- } else {
246
- standardLoadUncompressedSplatArray = new UncompressedSplatArray(outSphericalHarmonicsDegree);
247
- }
248
-
249
- numBytesStreamed = header.headerSizeBytes;
250
- numBytesParsed = header.headerSizeBytes;
251
- headerLoaded = true;
252
- }
253
- }
254
-
255
- if (headerLoaded && readyToLoadSplatData) {
256
-
257
- if (chunks.length > 0) {
258
-
259
- directLoadBufferIn = storeChunksInBuffer(chunks, directLoadBufferIn);
260
-
261
- const bytesLoadedSinceLastStreamedSection = numBytesDownloaded - numBytesStreamed;
262
- if (bytesLoadedSinceLastStreamedSection > directLoadSectionSizeBytes || loadComplete) {
263
- const numBytesToProcess = numBytesDownloaded - numBytesParsed;
264
- const addedSplatCount = Math.floor(numBytesToProcess / header.bytesPerSplat);
265
- const numBytesToParse = addedSplatCount * header.bytesPerSplat;
266
- const numBytesLeftOver = numBytesToProcess - numBytesToParse;
267
- const newSplatCount = splatCount + addedSplatCount;
268
- const parsedDataViewOffset = numBytesParsed - chunks[0].startBytes;
269
- const dataToParse = new DataView(directLoadBufferIn, parsedDataViewOffset, numBytesToParse);
270
-
271
- const shDescriptor = SplatBuffer.CompressionLevels[0].SphericalHarmonicsDegrees[outSphericalHarmonicsDegree];
272
- const outOffset = splatCount * shDescriptor.BytesPerSplat + splatDataOffsetBytes;
273
-
274
- // Parse splat data with error handling
275
- try {
276
- if (internalLoadType === InternalLoadType.DirectToSplatBuffer) {
277
- inriaV1PlyParser.parseToUncompressedSplatBufferSection(
278
- header, 0, addedSplatCount - 1, dataToParse,
279
- 0, directLoadBufferOut, outOffset,
280
- outSphericalHarmonicsDegree
281
- );
282
- } else {
283
- inriaV1PlyParser.parseToUncompressedSplatArraySection(
284
- header, 0, addedSplatCount - 1, dataToParse,
285
- 0, standardLoadUncompressedSplatArray,
286
- outSphericalHarmonicsDegree
287
- );
288
- }
289
- } catch (error) {
290
- const parseError = new ParseError(
291
- `Failed to parse splat data section: ${error.message}`,
292
- 'splatData',
293
- error
294
- );
295
- logger.error('Splat data parsing failed', { splatCount, addedSplatCount, error });
296
- loadPromise.reject(parseError);
297
- return;
298
- }
299
-
300
- splatCount = newSplatCount;
301
-
302
- if (internalLoadType === InternalLoadType.DirectToSplatBuffer) {
303
- if (!directLoadSplatBuffer) {
304
- SplatBuffer.writeSectionHeaderToBuffer({
305
- maxSplatCount: maxSplatCount,
306
- splatCount: splatCount,
307
- bucketSize: 0,
308
- bucketCount: 0,
309
- bucketBlockSize: 0,
310
- compressionScaleRange: 0,
311
- storageSizeBytes: 0,
312
- fullBucketCount: 0,
313
- partiallyFilledBucketCount: 0,
314
- sphericalHarmonicsDegree: outSphericalHarmonicsDegree
315
- }, 0, directLoadBufferOut, SplatBuffer.HeaderSizeBytes);
316
- directLoadSplatBuffer = new SplatBuffer(directLoadBufferOut, false);
317
- }
318
- directLoadSplatBuffer.updateLoadedCounts(1, splatCount);
319
- if (onProgressiveLoadSectionProgress) {
320
- onProgressiveLoadSectionProgress(directLoadSplatBuffer, loadComplete);
321
- }
322
- }
323
-
324
- numBytesStreamed += directLoadSectionSizeBytes;
325
- numBytesParsed += numBytesToParse;
326
-
327
- if (numBytesLeftOver === 0) {
328
- chunks = [];
329
- } else {
330
- let keepChunks = [];
331
- let keepSize = 0;
332
- for (let i = chunks.length - 1; i >= 0; i--) {
333
- const chunk = chunks[i];
334
- keepSize += chunk.sizeBytes;
335
- keepChunks.unshift(chunk);
336
- if (keepSize >= numBytesLeftOver) break;
337
- }
338
- chunks = keepChunks;
339
- }
340
- }
341
- }
342
-
343
- if (loadComplete) {
344
- if (internalLoadType === InternalLoadType.DirectToSplatBuffer) {
345
- loadPromise.resolve(directLoadSplatBuffer);
346
- } else {
347
- loadPromise.resolve(standardLoadUncompressedSplatArray);
348
- }
349
- }
350
- }
351
- }
352
-
353
- // Progress callback with error isolation
354
- if (onProgress) {
355
- try {
356
- onProgress(percent, percentLabel, LoaderStatus.Downloading);
357
- } catch (error) {
358
- logger.warn('Error in onProgress callback', error);
359
- }
360
- }
361
- };
362
-
363
- // Initial progress callback
364
- if (onProgress) {
365
- try {
366
- onProgress(0, '0%', LoaderStatus.Downloading);
367
- } catch (error) {
368
- logger.warn('Error in onProgress callback', error);
369
- }
370
- }
371
-
372
- // Fetch and process the PLY file
373
- return fetchWithProgress(fileName, localOnProgress, false, headers)
374
- .then(() => {
375
- if (onProgress) {
376
- try {
377
- onProgress(0, '0%', LoaderStatus.Processing);
378
- } catch (error) {
379
- logger.warn('Error in onProgress callback', error);
380
- }
381
- }
382
- return loadPromise.promise;
383
- })
384
- .then((splatData) => {
385
- if (onProgress) {
386
- try {
387
- onProgress(100, '100%', LoaderStatus.Done);
388
- } catch (error) {
389
- logger.warn('Error in onProgress callback', error);
390
- }
391
- }
392
-
393
- logger.debug('PLY data loaded successfully', {
394
- internalLoadType,
395
- splatCount: splatData?.splatCount || 'unknown'
396
- });
397
-
398
- // Process based on load type
399
- if (internalLoadType === InternalLoadType.DownloadBeforeProcessing) {
400
- const chunkDatas = chunks.map((chunk) => chunk.data);
401
- return new Blob(chunkDatas).arrayBuffer()
402
- .then((plyFileData) => {
403
- return PlyLoader.loadFromFileData(
404
- plyFileData, minimumAlpha, compressionLevel, optimizeSplatData,
405
- outSphericalHarmonicsDegree, sectionSize, sceneCenter, blockSize, bucketSize
406
- );
407
- })
408
- .catch((error) => {
409
- throw new AssetLoadError(
410
- `Failed to process downloaded PLY data: ${error.message}`,
411
- fileName,
412
- error
413
- );
414
- });
415
- } else if (internalLoadType === InternalLoadType.DirectToSplatBuffer) {
416
- return splatData;
417
- } else {
418
- return delayedExecute(() => {
419
- return finalizeSplatData(
420
- splatData, optimizeSplatData, minimumAlpha, compressionLevel,
421
- sectionSize, sceneCenter, blockSize, bucketSize
422
- );
423
- });
424
- }
425
- })
426
- .catch((error) => {
427
- // Re-throw custom errors as-is
428
- if (error instanceof ValidationError ||
429
- error instanceof NetworkError ||
430
- error instanceof ParseError ||
431
- error instanceof AssetLoadError) {
432
- logger.error('PLY loading failed', { fileName, errorCode: error.code });
433
- throw error;
434
- }
435
-
436
- // Wrap unexpected errors
437
- logger.error('Unexpected error loading PLY', { fileName, error });
438
- throw new AssetLoadError(
439
- `Unexpected error loading PLY file: ${error.message}`,
440
- fileName,
441
- error
442
- );
443
- });
444
- }
445
-
446
- /**
447
- * Load PLY file from raw ArrayBuffer data
448
- *
449
- * Parses PLY data that has already been downloaded. Useful for loading
450
- * from local files or when data is provided directly.
451
- *
452
- * @static
453
- * @param {ArrayBuffer} plyFileData - Raw PLY file data as ArrayBuffer
454
- * @param {number} [minimumAlpha=1] - Minimum alpha threshold for splat culling
455
- * @param {number} [compressionLevel=0] - Compression level (0=none, 1=medium, 2=high)
456
- * @param {boolean} [optimizeSplatData=true] - Whether to optimize/compress splat data
457
- * @param {number} [outSphericalHarmonicsDegree=0] - Spherical harmonics degree (0-3)
458
- * @param {number} [sectionSize] - Section size for partitioning
459
- * @param {Vector3} [sceneCenter] - Center point of the scene
460
- * @param {number} [blockSize] - Block size for spatial partitioning
461
- * @param {number} [bucketSize] - Bucket size for sorting
462
- * @returns {Promise<SplatBuffer>} Parsed and finalized splat buffer
463
- * @throws {ValidationError} If plyFileData is invalid
464
- * @throws {ParseError} If parsing fails
465
- */
466
- static loadFromFileData(plyFileData, minimumAlpha, compressionLevel, optimizeSplatData, outSphericalHarmonicsDegree = 0,
467
- sectionSize, sceneCenter, blockSize, bucketSize) {
468
- // Validate input
469
- try {
470
- validateArrayBuffer(plyFileData, 'plyFileData');
471
- } catch (error) {
472
- logger.error('Invalid PLY file data', error);
473
- return Promise.reject(error);
474
- }
475
-
476
- logger.info('Loading PLY from file data', {
477
- sizeBytes: plyFileData.byteLength,
478
- optimizeSplatData,
479
- outSphericalHarmonicsDegree
480
- });
481
-
482
- return delayedExecute(() => {
483
- try {
484
- return PlyParser.parseToUncompressedSplatArray(plyFileData, outSphericalHarmonicsDegree);
485
- } catch (error) {
486
- throw new ParseError(
487
- `Failed to parse PLY file data: ${error.message}`,
488
- 'plyFileData',
489
- error
490
- );
491
- }
492
- })
493
- .then((splatArray) => {
494
- logger.debug('PLY parsed successfully', {
495
- splatCount: splatArray?.splatCount || 'unknown'
496
- });
497
-
498
- return finalizeSplatData(
499
- splatArray, optimizeSplatData, minimumAlpha, compressionLevel,
500
- sectionSize, sceneCenter, blockSize, bucketSize
501
- );
502
- })
503
- .catch((error) => {
504
- // Re-throw custom errors as-is
505
- if (error instanceof ValidationError || error instanceof ParseError) {
506
- logger.error('PLY file data loading failed', { errorCode: error.code });
507
- throw error;
508
- }
509
-
510
- // Wrap unexpected errors
511
- logger.error('Unexpected error loading PLY from file data', error);
512
- throw new ParseError(
513
- `Unexpected error parsing PLY data: ${error.message}`,
514
- 'plyFileData',
515
- error
516
- );
517
- });
518
- }
519
- }
@@ -1,19 +0,0 @@
1
- /**
2
- * PlyParser
3
- *
4
- * Derived from @mkkellogg/gaussian-splats-3d (MIT License)
5
- * https://github.com/mkkellogg/GaussianSplats3D
6
- *
7
- * Simplified for FLAME avatar - only supports INRIAV1 PLY format.
8
- */
9
-
10
- import { INRIAV1PlyParser } from './INRIAV1PlyParser.js';
11
-
12
- export class PlyParser {
13
-
14
- static parseToUncompressedSplatArray(plyBuffer, outSphericalHarmonicsDegree = 0) {
15
- // FLAME avatars use INRIAV1 PLY format
16
- return new INRIAV1PlyParser().parseToUncompressedSplatArray(plyBuffer, outSphericalHarmonicsDegree);
17
- }
18
-
19
- }