@loaders.gl/splats 4.5.0-alpha.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 (121) hide show
  1. package/LICENSE +41 -0
  2. package/README.md +3 -0
  3. package/dist/bundled.d.ts +2 -0
  4. package/dist/bundled.d.ts.map +1 -0
  5. package/dist/bundled.js +5 -0
  6. package/dist/bundled.js.map +1 -0
  7. package/dist/dist.dev.js +33741 -0
  8. package/dist/dist.min.js +82 -0
  9. package/dist/index.cjs +1786 -0
  10. package/dist/index.cjs.map +7 -0
  11. package/dist/index.d.ts +10 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +10 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/ksplat-loader-types.d.ts +19 -0
  16. package/dist/ksplat-loader-types.d.ts.map +1 -0
  17. package/dist/ksplat-loader-types.js +20 -0
  18. package/dist/ksplat-loader-types.js.map +1 -0
  19. package/dist/ksplat-loader.d.ts +21 -0
  20. package/dist/ksplat-loader.d.ts.map +1 -0
  21. package/dist/ksplat-loader.js +12 -0
  22. package/dist/ksplat-loader.js.map +1 -0
  23. package/dist/lib/parse-ksplat.d.ts +7 -0
  24. package/dist/lib/parse-ksplat.d.ts.map +1 -0
  25. package/dist/lib/parse-ksplat.js +312 -0
  26. package/dist/lib/parse-ksplat.js.map +1 -0
  27. package/dist/lib/parse-rad-chunk.d.ts +19 -0
  28. package/dist/lib/parse-rad-chunk.d.ts.map +1 -0
  29. package/dist/lib/parse-rad-chunk.js +514 -0
  30. package/dist/lib/parse-rad-chunk.js.map +1 -0
  31. package/dist/lib/parse-rad.d.ts +136 -0
  32. package/dist/lib/parse-rad.d.ts.map +1 -0
  33. package/dist/lib/parse-rad.js +110 -0
  34. package/dist/lib/parse-rad.js.map +1 -0
  35. package/dist/lib/parse-splat.d.ts +7 -0
  36. package/dist/lib/parse-splat.d.ts.map +1 -0
  37. package/dist/lib/parse-splat.js +58 -0
  38. package/dist/lib/parse-splat.js.map +1 -0
  39. package/dist/lib/parse-spz.d.ts +7 -0
  40. package/dist/lib/parse-spz.d.ts.map +1 -0
  41. package/dist/lib/parse-spz.js +268 -0
  42. package/dist/lib/parse-spz.js.map +1 -0
  43. package/dist/lib/splat-utils.d.ts +12 -0
  44. package/dist/lib/splat-utils.d.ts.map +1 -0
  45. package/dist/lib/splat-utils.js +38 -0
  46. package/dist/lib/splat-utils.js.map +1 -0
  47. package/dist/lib/splats-arrow-table.d.ts +5 -0
  48. package/dist/lib/splats-arrow-table.d.ts.map +1 -0
  49. package/dist/lib/splats-arrow-table.js +133 -0
  50. package/dist/lib/splats-arrow-table.js.map +1 -0
  51. package/dist/rad-loader-types.d.ts +16 -0
  52. package/dist/rad-loader-types.d.ts.map +1 -0
  53. package/dist/rad-loader-types.js +16 -0
  54. package/dist/rad-loader-types.js.map +1 -0
  55. package/dist/rad-loader.d.ts +20 -0
  56. package/dist/rad-loader.d.ts.map +1 -0
  57. package/dist/rad-loader.js +14 -0
  58. package/dist/rad-loader.js.map +1 -0
  59. package/dist/rad-metadata.schema.json +133 -0
  60. package/dist/rad-source-loader.d.ts +112 -0
  61. package/dist/rad-source-loader.d.ts.map +1 -0
  62. package/dist/rad-source-loader.js +329 -0
  63. package/dist/rad-source-loader.js.map +1 -0
  64. package/dist/rad-zod-schema.cjs +81 -0
  65. package/dist/rad-zod-schema.cjs.map +7 -0
  66. package/dist/rad-zod-schema.d.ts +88 -0
  67. package/dist/rad-zod-schema.d.ts.map +1 -0
  68. package/dist/rad-zod-schema.js +70 -0
  69. package/dist/rad-zod-schema.js.map +1 -0
  70. package/dist/radc-metadata.schema.json +112 -0
  71. package/dist/splat-loader-types.d.ts +19 -0
  72. package/dist/splat-loader-types.d.ts.map +1 -0
  73. package/dist/splat-loader-types.js +20 -0
  74. package/dist/splat-loader-types.js.map +1 -0
  75. package/dist/splat-loader.d.ts +21 -0
  76. package/dist/splat-loader.d.ts.map +1 -0
  77. package/dist/splat-loader.js +12 -0
  78. package/dist/splat-loader.js.map +1 -0
  79. package/dist/splats-format.d.ts +39 -0
  80. package/dist/splats-format.d.ts.map +1 -0
  81. package/dist/splats-format.js +42 -0
  82. package/dist/splats-format.js.map +1 -0
  83. package/dist/spz-loader-types.d.ts +20 -0
  84. package/dist/spz-loader-types.d.ts.map +1 -0
  85. package/dist/spz-loader-types.js +20 -0
  86. package/dist/spz-loader-types.js.map +1 -0
  87. package/dist/spz-loader.d.ts +22 -0
  88. package/dist/spz-loader.d.ts.map +1 -0
  89. package/dist/spz-loader.js +11 -0
  90. package/dist/spz-loader.js.map +1 -0
  91. package/dist/types.d.ts +34 -0
  92. package/dist/types.d.ts.map +1 -0
  93. package/dist/types.js +5 -0
  94. package/dist/types.js.map +1 -0
  95. package/dist/unbundled.d.ts +2 -0
  96. package/dist/unbundled.d.ts.map +1 -0
  97. package/dist/unbundled.js +5 -0
  98. package/dist/unbundled.js.map +1 -0
  99. package/package.json +94 -0
  100. package/src/bundled.ts +5 -0
  101. package/src/index.ts +28 -0
  102. package/src/ksplat-loader-types.ts +25 -0
  103. package/src/ksplat-loader.ts +16 -0
  104. package/src/lib/parse-ksplat.ts +540 -0
  105. package/src/lib/parse-rad-chunk.ts +721 -0
  106. package/src/lib/parse-rad.ts +318 -0
  107. package/src/lib/parse-splat.ts +77 -0
  108. package/src/lib/parse-spz.ts +368 -0
  109. package/src/lib/splat-utils.ts +49 -0
  110. package/src/lib/splats-arrow-table.ts +190 -0
  111. package/src/rad-loader-types.ts +20 -0
  112. package/src/rad-loader.ts +22 -0
  113. package/src/rad-source-loader.ts +491 -0
  114. package/src/rad-zod-schema.ts +83 -0
  115. package/src/splat-loader-types.ts +25 -0
  116. package/src/splat-loader.ts +16 -0
  117. package/src/splats-format.ts +47 -0
  118. package/src/spz-loader-types.ts +25 -0
  119. package/src/spz-loader.ts +16 -0
  120. package/src/types.ts +39 -0
  121. package/src/unbundled.ts +5 -0
@@ -0,0 +1,318 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {RADChunkMetadataJSONSchema, RADMetadataJSONSchema} from '../rad-zod-schema';
6
+
7
+ const RAD_MAGIC = 0x30444152;
8
+ const RAD_CHUNK_MAGIC = 0x43444152;
9
+ const RAD_HEADER_BYTE_LENGTH = 8;
10
+ const RAD_CHUNK_PAYLOAD_LENGTH_BYTE_LENGTH = 8;
11
+ const TEXT_DECODER = new TextDecoder();
12
+
13
+ /** Spark RAD splat quantization and shader decode range metadata. */
14
+ export type RADSplatEncoding = {
15
+ /** Minimum RGB value encoded into quantized color properties. */
16
+ rgbMin?: number;
17
+ /** Maximum RGB value encoded into quantized color properties. */
18
+ rgbMax?: number;
19
+ /** Minimum natural log scale encoded into quantized scale properties. */
20
+ lnScaleMin?: number;
21
+ /** Maximum natural log scale encoded into quantized scale properties. */
22
+ lnScaleMax?: number;
23
+ /** Maximum absolute SH degree-1 coefficient value. */
24
+ sh1Max?: number;
25
+ /** Maximum absolute SH degree-2 coefficient value. */
26
+ sh2Max?: number;
27
+ /** Maximum absolute SH degree-3 coefficient value. */
28
+ sh3Max?: number;
29
+ /** Whether opacity is encoded for LoD blending. */
30
+ lodOpacity?: boolean;
31
+ /** Additional splat-encoding properties are preserved verbatim. */
32
+ [key: string]: unknown;
33
+ };
34
+
35
+ /** One RAD chunk location in the top-level RAD chunk table. */
36
+ export type RADChunkRange = {
37
+ /** Chunk byte offset relative to the top-level RAD chunk payload area. */
38
+ offset: number;
39
+ /** Chunk byte length. */
40
+ bytes: number;
41
+ /** Optional first splat index represented by this chunk. */
42
+ base?: number;
43
+ /** Optional number of splats represented by this chunk. */
44
+ count?: number;
45
+ /** Optional sidecar `.radc` filename relative to the RAD file URL. */
46
+ filename?: string;
47
+ /** Additional chunk-range properties are preserved verbatim. */
48
+ [key: string]: unknown;
49
+ };
50
+
51
+ /** Handwritten type for the JSON metadata stored in a Spark RAD header. */
52
+ export type RADMetadataJSON = {
53
+ /** RAD container version. Version 1 is currently supported. */
54
+ version: 1;
55
+ /** RAD payload type. Spark currently writes `gsplat`. */
56
+ type: 'gsplat';
57
+ /** Total splat count represented by the RAD source. */
58
+ count: number;
59
+ /** Maximum spherical harmonics degree present in the chunks. */
60
+ maxSh?: number;
61
+ /** Whether the source includes LoD tree child-count and child-start properties. */
62
+ lodTree?: boolean;
63
+ /** Nominal number of splats per chunk. */
64
+ chunkSize?: number;
65
+ /** Total inline chunk byte length when present in the RAD metadata. */
66
+ allChunkBytes?: number;
67
+ /** RAD chunk table. */
68
+ chunks: RADChunkRange[];
69
+ /** Optional shared splat encoding metadata for chunk decoding. */
70
+ splatEncoding?: RADSplatEncoding;
71
+ /** Optional spherical harmonics codebook count. */
72
+ shCodeCount?: number;
73
+ /** Optional RAD writer comment. */
74
+ comment?: string;
75
+ /** Additional RAD metadata properties are preserved verbatim. */
76
+ [key: string]: unknown;
77
+ };
78
+
79
+ /** Parsed Spark RAD top-level metadata with loader-derived byte offsets. */
80
+ export type RADMetadata = RADMetadataJSON & {
81
+ /** Byte length of the JSON metadata block. */
82
+ headerJsonByteLength: number;
83
+ /** Byte offset where inline RAD chunks begin. */
84
+ chunksByteOffset: number;
85
+ };
86
+
87
+ /** Known RAD chunk property names. */
88
+ export type RADChunkPropertyName =
89
+ | 'center'
90
+ | 'alpha'
91
+ | 'rgb'
92
+ | 'scales'
93
+ | 'orientation'
94
+ | 'sh1'
95
+ | 'sh2'
96
+ | 'sh3'
97
+ | 'child_count'
98
+ | 'child_start'
99
+ | 'sh1_code'
100
+ | 'sh2_code'
101
+ | 'sh3_code'
102
+ | 'sh_label';
103
+
104
+ /** Known RAD chunk property encodings. */
105
+ export type RADChunkPropertyEncoding =
106
+ | 'f32'
107
+ | 'f16'
108
+ | 'f32_lebytes'
109
+ | 'f16_lebytes'
110
+ | 'r8'
111
+ | 'r8_delta'
112
+ | 's8'
113
+ | 's8_delta'
114
+ | 'ln_0r8'
115
+ | 'ln_f16'
116
+ | 'oct88r8'
117
+ | 'u16'
118
+ | 'u32';
119
+
120
+ /** Known RAD chunk property compression modes. */
121
+ export type RADChunkPropertyCompression = 'gz';
122
+
123
+ /** One property payload entry inside a RAD chunk. */
124
+ export type RADChunkProperty = {
125
+ /** Property byte offset relative to the chunk payload area. */
126
+ offset: number;
127
+ /** Property byte length before 8-byte payload padding. */
128
+ bytes: number;
129
+ /** Property semantic name. */
130
+ property: RADChunkPropertyName | string;
131
+ /** Property encoding name. */
132
+ encoding: RADChunkPropertyEncoding | string;
133
+ /** Optional property compression mode. */
134
+ compression?: RADChunkPropertyCompression | string;
135
+ /** Optional decode minimum for quantized properties. */
136
+ min?: number;
137
+ /** Optional decode maximum for quantized properties. */
138
+ max?: number;
139
+ /** Additional chunk-property metadata is preserved verbatim. */
140
+ [key: string]: unknown;
141
+ };
142
+
143
+ /** Handwritten type for the JSON metadata stored in a Spark RADC chunk header. */
144
+ export type RADChunkMetadataJSON = {
145
+ /** RAD chunk version. Version 1 is currently supported. */
146
+ version: 1;
147
+ /** First global splat index represented by this chunk. */
148
+ base: number;
149
+ /** Number of splats represented by this chunk. */
150
+ count: number;
151
+ /** Byte length of the padded property payload. */
152
+ payloadBytes: number;
153
+ /** Maximum spherical harmonics degree present in this chunk. */
154
+ maxSh?: number;
155
+ /** Whether this chunk contains LoD tree properties. */
156
+ lodTree?: boolean;
157
+ /** Optional chunk-local splat encoding metadata. */
158
+ splatEncoding?: RADSplatEncoding;
159
+ /** Property table for the chunk payload. */
160
+ properties: RADChunkProperty[];
161
+ /** Additional RADC metadata properties are preserved verbatim. */
162
+ [key: string]: unknown;
163
+ };
164
+
165
+ /** Parsed Spark RADC chunk metadata with loader-derived byte offsets. */
166
+ export type RADChunkMetadata = RADChunkMetadataJSON & {
167
+ /** Byte length of the chunk JSON metadata block. */
168
+ headerJsonByteLength: number;
169
+ /** Byte offset where the chunk payload begins. */
170
+ payloadByteOffset: number;
171
+ /** Byte length of the RADC header and payload. */
172
+ chunkByteLength: number;
173
+ };
174
+
175
+ /** Returns true when the input begins with the Spark RAD magic bytes. */
176
+ export function isRAD(data: ArrayBuffer | ArrayBufferView): boolean {
177
+ const bytes = getUint8Array(data);
178
+ if (bytes.byteLength < 4) {
179
+ return false;
180
+ }
181
+ return (
182
+ new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(0, true) === RAD_MAGIC
183
+ );
184
+ }
185
+
186
+ /** Parses a complete or header-prefix Spark `.rad` buffer. */
187
+ export function parseRADHeader(data: ArrayBuffer | ArrayBufferView): RADMetadata {
188
+ const metadata = tryParseRADHeader(data);
189
+ if (!metadata) {
190
+ throw new Error('RADLoader: file must contain a complete RAD metadata header.');
191
+ }
192
+ return metadata;
193
+ }
194
+
195
+ /** Parses a Spark `.rad` header when enough bytes are available. */
196
+ export function tryParseRADHeader(data: ArrayBuffer | ArrayBufferView): RADMetadata | null {
197
+ const bytes = getUint8Array(data);
198
+ if (bytes.byteLength < RAD_HEADER_BYTE_LENGTH) {
199
+ return null;
200
+ }
201
+
202
+ const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
203
+ const magic = dataView.getUint32(0, true);
204
+ if (magic !== RAD_MAGIC) {
205
+ throw new Error(`RADLoader: RAD0 magic header not found, received 0x${magic.toString(16)}.`);
206
+ }
207
+
208
+ const headerJsonByteLength = dataView.getUint32(4, true);
209
+ const headerJsonEnd = RAD_HEADER_BYTE_LENGTH + headerJsonByteLength;
210
+ if (bytes.byteLength < headerJsonEnd) {
211
+ return null;
212
+ }
213
+
214
+ const rawMetadata = RADMetadataJSONSchema.parse(
215
+ parseJSON(
216
+ bytes.subarray(RAD_HEADER_BYTE_LENGTH, headerJsonEnd),
217
+ 'RADLoader: failed to parse RAD metadata JSON.'
218
+ )
219
+ );
220
+ return normalizeRADMetadata(rawMetadata, headerJsonByteLength);
221
+ }
222
+
223
+ /** Parses the metadata header from one Spark `.radc` chunk buffer. */
224
+ export function parseRADChunkHeader(data: ArrayBuffer | ArrayBufferView): RADChunkMetadata {
225
+ const bytes = getUint8Array(data);
226
+ if (bytes.byteLength < RAD_HEADER_BYTE_LENGTH) {
227
+ throw new Error('RADLoader: RADC chunk must contain an 8-byte metadata header.');
228
+ }
229
+
230
+ const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
231
+ const magic = dataView.getUint32(0, true);
232
+ if (magic !== RAD_CHUNK_MAGIC) {
233
+ throw new Error(`RADLoader: RADC magic header not found, received 0x${magic.toString(16)}.`);
234
+ }
235
+
236
+ const headerJsonByteLength = dataView.getUint32(4, true);
237
+ const headerJsonEnd = RAD_HEADER_BYTE_LENGTH + headerJsonByteLength;
238
+ const payloadByteLengthOffset = RAD_HEADER_BYTE_LENGTH + roundUpToEight(headerJsonByteLength);
239
+ const payloadByteOffset = payloadByteLengthOffset + RAD_CHUNK_PAYLOAD_LENGTH_BYTE_LENGTH;
240
+ if (bytes.byteLength < payloadByteOffset) {
241
+ throw new Error('RADLoader: RADC chunk must contain a complete metadata header.');
242
+ }
243
+
244
+ const rawMetadata = RADChunkMetadataJSONSchema.parse(
245
+ parseJSON(
246
+ bytes.subarray(RAD_HEADER_BYTE_LENGTH, headerJsonEnd),
247
+ 'RADLoader: failed to parse RADC metadata JSON.'
248
+ )
249
+ );
250
+ const payloadBytes = readSafeUint64(
251
+ dataView,
252
+ payloadByteLengthOffset,
253
+ 'RADC payload byte length'
254
+ );
255
+ return normalizeRADChunkMetadata(
256
+ rawMetadata,
257
+ headerJsonByteLength,
258
+ payloadByteOffset,
259
+ payloadBytes
260
+ );
261
+ }
262
+
263
+ /** Rounds a byte length to Spark RAD's 8-byte alignment. */
264
+ export function roundUpToEight(byteLength: number): number {
265
+ return (byteLength + 7) & ~7;
266
+ }
267
+
268
+ function normalizeRADMetadata(
269
+ rawMetadata: RADMetadataJSON,
270
+ headerJsonByteLength: number
271
+ ): RADMetadata {
272
+ return {
273
+ ...rawMetadata,
274
+ headerJsonByteLength,
275
+ chunksByteOffset: RAD_HEADER_BYTE_LENGTH + roundUpToEight(headerJsonByteLength)
276
+ };
277
+ }
278
+
279
+ function normalizeRADChunkMetadata(
280
+ rawMetadata: RADChunkMetadataJSON,
281
+ headerJsonByteLength: number,
282
+ payloadByteOffset: number,
283
+ payloadBytes: number
284
+ ): RADChunkMetadata {
285
+ if (rawMetadata.payloadBytes !== payloadBytes) {
286
+ throw new Error('RADLoader: RADC metadata payload byte length does not match binary header.');
287
+ }
288
+
289
+ return {
290
+ ...rawMetadata,
291
+ payloadBytes,
292
+ headerJsonByteLength,
293
+ payloadByteOffset,
294
+ chunkByteLength: payloadByteOffset + payloadBytes
295
+ };
296
+ }
297
+
298
+ function parseJSON(bytes: Uint8Array, message: string): unknown {
299
+ try {
300
+ return JSON.parse(TEXT_DECODER.decode(bytes));
301
+ } catch {
302
+ throw new Error(message);
303
+ }
304
+ }
305
+
306
+ function readSafeUint64(dataView: DataView, byteOffset: number, fieldName: string): number {
307
+ const value = dataView.getBigUint64(byteOffset, true);
308
+ if (value > BigInt(Number.MAX_SAFE_INTEGER)) {
309
+ throw new Error(`RADLoader: ${fieldName} exceeds Number.MAX_SAFE_INTEGER.`);
310
+ }
311
+ return Number(value);
312
+ }
313
+
314
+ function getUint8Array(data: ArrayBuffer | ArrayBufferView): Uint8Array {
315
+ return data instanceof ArrayBuffer
316
+ ? new Uint8Array(data)
317
+ : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
318
+ }
@@ -0,0 +1,77 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import type {MeshArrowTable} from '@loaders.gl/schema';
6
+ import type {GaussianSplats} from '../types';
7
+ import {makeGaussianSplatsArrowTable} from './splats-arrow-table';
8
+ import {
9
+ convertAlphaByteToLinearOpacity,
10
+ decodeQuaternionByte,
11
+ normalizeQuaternion
12
+ } from './splat-utils';
13
+
14
+ const SPLAT_ROW_BYTE_LENGTH = 32;
15
+
16
+ /** Parses a raw `.splat` ArrayBuffer into a Mesh Arrow table. */
17
+ export function parseSPLAT(data: ArrayBuffer): MeshArrowTable {
18
+ return makeGaussianSplatsArrowTable(parseSPLATToGaussianSplats(data));
19
+ }
20
+
21
+ /** Parses a raw `.splat` ArrayBuffer into decoded Gaussian splat values. */
22
+ export function parseSPLATToGaussianSplats(data: ArrayBuffer): GaussianSplats {
23
+ if (data.byteLength % SPLAT_ROW_BYTE_LENGTH !== 0) {
24
+ throw new Error(`SPLATLoader: byte length must be a multiple of ${SPLAT_ROW_BYTE_LENGTH}.`);
25
+ }
26
+
27
+ const splatCount = data.byteLength / SPLAT_ROW_BYTE_LENGTH;
28
+ const dataView = new DataView(data);
29
+ const positions = new Float32Array(splatCount * 3);
30
+ const scales = new Float32Array(splatCount * 3);
31
+ const rotations = new Float32Array(splatCount * 4);
32
+ const colors = new Uint8Array(splatCount * 3);
33
+ const opacities = new Float32Array(splatCount);
34
+
35
+ for (let splatIndex = 0; splatIndex < splatCount; splatIndex++) {
36
+ const byteOffset = splatIndex * SPLAT_ROW_BYTE_LENGTH;
37
+ const positionOffset = splatIndex * 3;
38
+ const rotationOffset = splatIndex * 4;
39
+
40
+ positions[positionOffset + 0] = dataView.getFloat32(byteOffset + 0, true);
41
+ positions[positionOffset + 1] = dataView.getFloat32(byteOffset + 4, true);
42
+ positions[positionOffset + 2] = dataView.getFloat32(byteOffset + 8, true);
43
+ scales[positionOffset + 0] = dataView.getFloat32(byteOffset + 12, true);
44
+ scales[positionOffset + 1] = dataView.getFloat32(byteOffset + 16, true);
45
+ scales[positionOffset + 2] = dataView.getFloat32(byteOffset + 20, true);
46
+ colors[positionOffset + 0] = dataView.getUint8(byteOffset + 24);
47
+ colors[positionOffset + 1] = dataView.getUint8(byteOffset + 25);
48
+ colors[positionOffset + 2] = dataView.getUint8(byteOffset + 26);
49
+ opacities[splatIndex] = convertAlphaByteToLinearOpacity(dataView.getUint8(byteOffset + 27));
50
+
51
+ const [w, x, y, z] = normalizeQuaternion(
52
+ decodeQuaternionByte(dataView.getUint8(byteOffset + 28)),
53
+ decodeQuaternionByte(dataView.getUint8(byteOffset + 29)),
54
+ decodeQuaternionByte(dataView.getUint8(byteOffset + 30)),
55
+ decodeQuaternionByte(dataView.getUint8(byteOffset + 31))
56
+ );
57
+ rotations[rotationOffset + 0] = w;
58
+ rotations[rotationOffset + 1] = x;
59
+ rotations[rotationOffset + 2] = y;
60
+ rotations[rotationOffset + 3] = z;
61
+ }
62
+
63
+ return {
64
+ format: 'splat',
65
+ splatCount,
66
+ positions,
67
+ scales,
68
+ rotations,
69
+ colors,
70
+ opacities,
71
+ loaderData: {
72
+ format: 'splat',
73
+ splatCount,
74
+ rowByteLength: SPLAT_ROW_BYTE_LENGTH
75
+ }
76
+ };
77
+ }