@driftengine/drft 3.61.0

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 (67) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +9 -0
  3. package/README.md +9 -0
  4. package/dist/animationData.d.ts +53 -0
  5. package/dist/animationData.js +13 -0
  6. package/dist/coarseFirst.d.ts +27 -0
  7. package/dist/coarseFirst.js +118 -0
  8. package/dist/drftColliders.d.ts +53 -0
  9. package/dist/drftColliders.js +137 -0
  10. package/dist/drftFormat.d.ts +454 -0
  11. package/dist/drftFormat.js +350 -0
  12. package/dist/drftRead.d.ts +121 -0
  13. package/dist/drftRead.js +487 -0
  14. package/dist/drftSkin.d.ts +40 -0
  15. package/dist/drftSkin.js +270 -0
  16. package/dist/drftStream.d.ts +170 -0
  17. package/dist/drftStream.js +315 -0
  18. package/dist/drftSubs.d.ts +18 -0
  19. package/dist/drftSubs.js +70 -0
  20. package/dist/drftWrite.d.ts +103 -0
  21. package/dist/drftWrite.js +478 -0
  22. package/dist/fixtures/v1-0.d.ts +9 -0
  23. package/dist/fixtures/v1-0.js +9 -0
  24. package/dist/fixtures/v1-1.d.ts +16 -0
  25. package/dist/fixtures/v1-1.js +16 -0
  26. package/dist/fixtures/v1-11.d.ts +14 -0
  27. package/dist/fixtures/v1-11.js +14 -0
  28. package/dist/fixtures/v1-2.d.ts +19 -0
  29. package/dist/fixtures/v1-2.js +19 -0
  30. package/dist/fixtures/v1-3.d.ts +18 -0
  31. package/dist/fixtures/v1-3.js +18 -0
  32. package/dist/fixtures/v1-4.d.ts +13 -0
  33. package/dist/fixtures/v1-4.js +13 -0
  34. package/dist/fixtures/v1-5.d.ts +14 -0
  35. package/dist/fixtures/v1-5.js +14 -0
  36. package/dist/fixtures/v1-6.d.ts +9 -0
  37. package/dist/fixtures/v1-6.js +9 -0
  38. package/dist/fixtures/v1-7.d.ts +9 -0
  39. package/dist/fixtures/v1-7.js +9 -0
  40. package/dist/fixtures/v1-9.d.ts +9 -0
  41. package/dist/fixtures/v1-9.js +9 -0
  42. package/dist/index.d.ts +26 -0
  43. package/dist/index.js +29 -0
  44. package/dist/meshData.d.ts +210 -0
  45. package/dist/meshData.js +105 -0
  46. package/package.json +57 -0
  47. package/src/animationData.ts +58 -0
  48. package/src/coarseFirst.ts +113 -0
  49. package/src/drftColliders.ts +153 -0
  50. package/src/drftFormat.ts +537 -0
  51. package/src/drftRead.ts +652 -0
  52. package/src/drftSkin.ts +326 -0
  53. package/src/drftStream.ts +436 -0
  54. package/src/drftSubs.ts +86 -0
  55. package/src/drftWrite.ts +613 -0
  56. package/src/fixtures/v1-0.ts +10 -0
  57. package/src/fixtures/v1-1.ts +17 -0
  58. package/src/fixtures/v1-11.ts +15 -0
  59. package/src/fixtures/v1-2.ts +20 -0
  60. package/src/fixtures/v1-3.ts +19 -0
  61. package/src/fixtures/v1-4.ts +14 -0
  62. package/src/fixtures/v1-5.ts +15 -0
  63. package/src/fixtures/v1-6.ts +10 -0
  64. package/src/fixtures/v1-7.ts +10 -0
  65. package/src/fixtures/v1-9.ts +10 -0
  66. package/src/index.ts +56 -0
  67. package/src/meshData.ts +301 -0
@@ -0,0 +1,613 @@
1
+ /**
2
+ * Writing a `.drft`.
3
+ *
4
+ * The writer is the smaller half and the stricter one: it validates what it is given
5
+ * before anything reaches a file, because a malformed asset caught here costs a failed
6
+ * bake, and the same asset caught later costs somebody a night on a device they cannot
7
+ * attach a debugger to. That is not hypothetical — see `validateMeshData`.
8
+ *
9
+ * Runs offline in the baker, so it may allocate freely. Nothing here is on a frame path.
10
+ */
11
+
12
+ import type { AnimationClip, DrftSkin } from './animationData.ts';
13
+ import type { DrftNode } from './drftSkin.ts';
14
+ import { buildClip, buildMorph, buildNodes, buildSkin } from './drftSkin.ts';
15
+ import type { MeshData } from './meshData.ts';
16
+ import { validateMeshData } from './meshData.ts';
17
+ import {
18
+ ALIGNMENT,
19
+ ATTR_EMISSIVE_COLOR,
20
+ ATTR_GRAIN,
21
+ ATTR_JOINTS,
22
+ ATTR_RELIEF,
23
+ ATTR_TANGENT,
24
+ ATTR_ROUGHNESS,
25
+ ATTR_SPECULAR,
26
+ ATTR_UVS,
27
+ ATTR_WEIGHTS,
28
+ CHUNK_ENTRY_BYTES,
29
+ CHUNK_HEAD,
30
+ CHUNK_ANIM,
31
+ CHUNK_LODM,
32
+ CHUNK_LODF,
33
+ CHUNK_MORP,
34
+ CHUNK_NODE,
35
+ CHUNK_SKIN,
36
+ CHUNK_SPLT,
37
+ SPLAT_BLOCK_PREFIX,
38
+ CHUNK_MATL,
39
+ CHUNK_MESH,
40
+ CHUNK_REQUIRED,
41
+ CHUNK_COLL,
42
+ CHUNK_SUBS,
43
+ CHUNK_TEXS,
44
+ MATERIAL_ENTRY_BYTES,
45
+ DRFT_MAGIC,
46
+ DRFT_VERSION_MAJOR,
47
+ DRFT_VERSION_MINOR,
48
+ DrftError,
49
+ FLAG_LITTLE_ENDIAN,
50
+ HEADER_BYTES,
51
+ align,
52
+ } from './drftFormat.ts';
53
+ import type { DrftHead, DrftMaterial, DrftSplats } from './drftFormat.ts';
54
+ import { coarseFirstOrder } from './coarseFirst.ts';
55
+ import { type DrftSubstanceEntry, buildSubs } from './drftSubs.ts';
56
+ import { buildColliders } from './drftColliders.ts';
57
+
58
+ /** An image to embed, already compressed, with what its own header said about it. */
59
+ export interface DrftTextureSource {
60
+ /**
61
+ * What the source model called this image, normally its declared relative path.
62
+ *
63
+ * Carried so a texture can be addressed by name instead of by an ordinal. An ordinal is a
64
+ * position in whatever order a reader happened to meet its records, which is not something
65
+ * a consumer should have to know or a format should ask it to depend on.
66
+ */
67
+ readonly name: string;
68
+ readonly codec: number;
69
+ readonly width: number;
70
+ readonly height: number;
71
+ readonly bytes: Uint8Array;
72
+ }
73
+
74
+ /** What a caller hands over to be baked. */
75
+ export interface DrftSource {
76
+ readonly head?: Partial<DrftHead>;
77
+ readonly meshes: readonly MeshData[];
78
+ /**
79
+ * One material per mesh, by ordinal, or absent for an asset that names none.
80
+ *
81
+ * All or nothing: a partial list would make material *n* mean a different mesh depending
82
+ * on how many came before it, which is the kind of off-by-one that shows up as one wrong
83
+ * surface in a scene and is looked for everywhere except here.
84
+ */
85
+ readonly materials?: readonly DrftMaterial[];
86
+ readonly textures?: readonly DrftTextureSource[];
87
+ /**
88
+ * Which substance each material is made of, or absent for a file that labels none.
89
+ *
90
+ * Written as one `SUBS` chunk at 1.8. It pairs by the material ordinal carried per entry rather
91
+ * than by position, so a file labelling only its fourth material says so.
92
+ */
93
+ readonly substances?: readonly DrftSubstanceEntry[];
94
+ /**
95
+ * The convex hulls this asset collides as, or absent for a file that carries none.
96
+ *
97
+ * Each entry is xyz-packed points in the asset's own space, at most 64 of them, which is what
98
+ * `hullShape` takes. Written as one `COLL` chunk at 1.12. See `drftColliders.ts` for why the
99
+ * format carries points rather than shapes.
100
+ */
101
+ readonly colliders?: readonly Float32Array[];
102
+ /**
103
+ * Coarse whole-asset levels of detail, **coarsest first**, or absent for a file with none.
104
+ *
105
+ * Each one stands in for the entire model rather than for one part of it, which is the
106
+ * argument docs/FORMAT.md §4.6 makes: a single decimated body reads as a car, and 187 coarse
107
+ * fragments read as a mess. They are written first so a sequential fetch has an outline on
108
+ * screen within the first few kilobytes, and they carry no material entry because they carry
109
+ * their colour per vertex — `MATL` pairs with `MESH` by ordinal and nothing else.
110
+ */
111
+ readonly lods?: readonly MeshData[];
112
+ /**
113
+ * Discrete levels of detail, **finest first**, each a complete `.drft` of its own.
114
+ *
115
+ * Distinct from `lods` above in every way that matters: those are one merged outline per level
116
+ * for a progressive load, these are whole alternative models a source authored by hand, each
117
+ * with its own materials and hierarchy. See `CHUNK_LODF`.
118
+ */
119
+ readonly levels?: readonly Uint8Array[];
120
+ /**
121
+ * The asset's own hierarchy, or absent for one mesh at the origin.
122
+ *
123
+ * What `NODE` carries, and what rigid TRS animation drives. A reader that skips the chunk gets
124
+ * the meshes exactly as it always did, which is why this is additive.
125
+ */
126
+ readonly nodes?: readonly DrftNode[];
127
+ /** Skins, one `SKIN` chunk each, or absent for an asset that deforms nothing. */
128
+ readonly skins?: readonly DrftSkin[];
129
+ /** Clips, one `ANIM` chunk each, or absent for an asset that animates nothing. */
130
+ readonly clips?: readonly AnimationClip[];
131
+ /**
132
+ * A Gaussian splat capture, or absent for a file carrying none.
133
+ *
134
+ * Written as several `SPLT` blocks, interleaved across the whole capture so any prefix of the
135
+ * file is a complete sparse version of it. See `coarseFirst.ts` for the ordering and
136
+ * `CHUNK_SPLT` for why it is several chunks rather than one.
137
+ */
138
+ readonly splats?: DrftSplats;
139
+ }
140
+
141
+ /**
142
+ * The most blocks a capture is split into, and the fewest splats worth having in one.
143
+ *
144
+ * **A ceiling and a floor rather than a fixed count**, because both ends have a real cost. Every
145
+ * block is a sixteen-byte table entry and a forty-byte payload header, so a thousand blocks of
146
+ * two hundred splats is a table nobody wants; one block of a million splats is a capture that
147
+ * arrives all at once and does not stream at all. Sixteen blocks puts the first sixteenth of a
148
+ * capture on screen, which is what `coarseFirst.ts` is built to make worth looking at.
149
+ *
150
+ * **What would make these wrong** is a measurement on a real connection: a capture that reads as
151
+ * finished after two blocks wants fewer and larger, and one that stutters visibly between blocks
152
+ * wants more and smaller. `DrftSplats.blocks` is how a baker that has taken that measurement says
153
+ * so without waiting for these numbers to move.
154
+ */
155
+ const SPLAT_MAX_BLOCKS = 16;
156
+ const SPLAT_MIN_BLOCK = 4096;
157
+
158
+ /**
159
+ * Split a capture into blocks, each a sparse sample of the whole thing.
160
+ *
161
+ * The records are re-ordered on the way out — the file's order is `coarseFirstOrder`'s and not the
162
+ * caller's, which is why nothing here preserves an index. A consumer that needed the original
163
+ * ordering would need it stored, and `CHUNK_SPLT` says so.
164
+ */
165
+ function buildSplats(splats: DrftSplats): PendingChunk[] {
166
+ const { count, wordsPerSplat } = splats;
167
+ if (count <= 0) return [];
168
+ if (wordsPerSplat <= 0) {
169
+ throw new DrftError(`drft: a splat record is ${wordsPerSplat} words, which stores nothing`);
170
+ }
171
+ if (splats.records.length < count * wordsPerSplat) {
172
+ throw new DrftError(
173
+ `drft: ${count} splats of ${wordsPerSplat} words need ${count * wordsPerSplat} entries and ` +
174
+ `the records array has ${splats.records.length}`,
175
+ );
176
+ }
177
+ if (splats.positions.length < count * 3) {
178
+ throw new DrftError(
179
+ `drft: ${count} splats need ${count * 3} position entries and the array has ` +
180
+ `${splats.positions.length}`,
181
+ );
182
+ }
183
+
184
+ const asked = splats.blocks;
185
+ const blocks =
186
+ asked !== undefined && asked > 0
187
+ ? Math.min(asked, count)
188
+ : Math.max(1, Math.min(SPLAT_MAX_BLOCKS, Math.ceil(count / SPLAT_MIN_BLOCK)));
189
+ const order = coarseFirstOrder(splats.positions, count);
190
+ /* Ceiling, so the last block is the short one and every earlier one is full. A reader takes
191
+ each block's own count from its header rather than deriving it, so a short block is ordinary. */
192
+ const perBlock = Math.ceil(count / blocks);
193
+
194
+ const out: PendingChunk[] = [];
195
+ for (let block = 0; block < blocks; block++) {
196
+ const from = block * perBlock;
197
+ if (from >= count) break;
198
+ const howMany = Math.min(perBlock, count - from);
199
+ const bytes = new Uint8Array(SPLAT_BLOCK_PREFIX + howMany * wordsPerSplat * 4);
200
+ const view = new DataView(bytes.buffer);
201
+ view.setUint32(0, howMany, true);
202
+ view.setUint32(4, count, true);
203
+ view.setUint32(8, wordsPerSplat, true);
204
+ view.setUint32(12, splats.sphericalHarmonics ?? 0, true);
205
+ for (let axis = 0; axis < 3; axis++) {
206
+ view.setFloat32(16 + axis * 4, splats.boundsMin[axis] ?? 0, true);
207
+ view.setFloat32(28 + axis * 4, splats.boundsMax[axis] ?? 0, true);
208
+ }
209
+ const records = new Uint32Array(bytes.buffer, SPLAT_BLOCK_PREFIX, howMany * wordsPerSplat);
210
+ for (let slot = 0; slot < howMany; slot++) {
211
+ const splat = order[from + slot] ?? 0;
212
+ records.set(
213
+ splats.records.subarray(splat * wordsPerSplat, (splat + 1) * wordsPerSplat),
214
+ slot * wordsPerSplat,
215
+ );
216
+ }
217
+ /* Optional, so a reader that predates 1.5 skips every one of these in silence. */
218
+ out.push({ code: CHUNK_SPLT, flags: 0, bytes });
219
+ }
220
+ return out;
221
+ }
222
+
223
+ /** A chunk built in memory, before the table is laid out. */
224
+ interface PendingChunk {
225
+ readonly code: number;
226
+ readonly flags: number;
227
+ readonly bytes: Uint8Array;
228
+ }
229
+
230
+ const encoder = new TextEncoder();
231
+
232
+ /** A length-prefixed UTF-8 string, padded so what follows it stays aligned. */
233
+ function encodeString(value: string): Uint8Array {
234
+ const text = encoder.encode(value);
235
+ const bytes = new Uint8Array(align(4 + text.length));
236
+ new DataView(bytes.buffer).setUint32(0, text.length, true);
237
+ bytes.set(text, 4);
238
+ return bytes;
239
+ }
240
+
241
+ function buildHead(head: Partial<DrftHead> | undefined, meshes: readonly MeshData[]): PendingChunk {
242
+ /*
243
+ * Bounds are computed rather than taken on trust. A caller that gets them wrong would
244
+ * produce an asset that culls itself out of frame, which looks like a rendering bug and
245
+ * is not one — and the writer is holding every vertex anyway.
246
+ */
247
+ let minX = Infinity;
248
+ let minY = Infinity;
249
+ let minZ = Infinity;
250
+ let maxX = -Infinity;
251
+ let maxY = -Infinity;
252
+ let maxZ = -Infinity;
253
+ for (const mesh of meshes) {
254
+ for (let at = 0; at + 2 < mesh.positions.length; at += 3) {
255
+ const x = mesh.positions[at] as number;
256
+ const y = mesh.positions[at + 1] as number;
257
+ const z = mesh.positions[at + 2] as number;
258
+ if (x < minX) minX = x;
259
+ if (y < minY) minY = y;
260
+ if (z < minZ) minZ = z;
261
+ if (x > maxX) maxX = x;
262
+ if (y > maxY) maxY = y;
263
+ if (z > maxZ) maxZ = z;
264
+ }
265
+ }
266
+ if (!Number.isFinite(minX)) {
267
+ minX = 0;
268
+ minY = 0;
269
+ minZ = 0;
270
+ maxX = 0;
271
+ maxY = 0;
272
+ maxZ = 0;
273
+ }
274
+
275
+ const name = encodeString(head?.name ?? '');
276
+ const generator = encodeString(head?.generator ?? 'drft');
277
+ const numbers = new Float32Array([head?.unitScale ?? 1, minX, minY, minZ, maxX, maxY, maxZ]);
278
+ const bytes = new Uint8Array(numbers.byteLength + name.length + generator.length);
279
+ bytes.set(new Uint8Array(numbers.buffer), 0);
280
+ bytes.set(name, numbers.byteLength);
281
+ bytes.set(generator, numbers.byteLength + name.length);
282
+ return { code: CHUNK_HEAD, flags: CHUNK_REQUIRED, bytes };
283
+ }
284
+
285
+ /**
286
+ * One mesh payload, under whichever FourCC is carrying it.
287
+ *
288
+ * `MESH` and `LODM` are the same bytes on purpose: a coarse level *is* a mesh, and giving it
289
+ * its own layout would be a second thing to keep in step with the frozen attribute order for
290
+ * no gain. What differs is the code and the flag — a level of detail is optional, so a reader
291
+ * that has never heard of one skips it and draws the model, per rule 2 of §4.4.
292
+ */
293
+ function buildMesh(mesh: MeshData, code = CHUNK_MESH, flags = CHUNK_REQUIRED): PendingChunk {
294
+ /*
295
+ * The check that this whole format exists downstream of. An attribute array not
296
+ * covering every vertex is drawn by some drivers and causes others to drop the draw
297
+ * outright, with no GL error on either path — so it is caught here, where the message
298
+ * can name the array, rather than on somebody's phone where it cannot.
299
+ */
300
+ validateMeshData(mesh);
301
+
302
+ const vertices = mesh.positions.length / 3;
303
+ let attributes = 0;
304
+ if (mesh.specular !== undefined) attributes |= ATTR_SPECULAR;
305
+ if (mesh.uvs !== undefined) attributes |= ATTR_UVS;
306
+ if (mesh.emissiveColor !== undefined) attributes |= ATTR_EMISSIVE_COLOR;
307
+ if (mesh.roughness !== undefined) attributes |= ATTR_ROUGHNESS;
308
+ if (mesh.grain !== undefined) attributes |= ATTR_GRAIN;
309
+ if (mesh.relief !== undefined) attributes |= ATTR_RELIEF;
310
+ if (mesh.tangents !== undefined) attributes |= ATTR_TANGENT;
311
+ /* Both or neither: `validateMeshData` above has already refused one without the other, so
312
+ these two bits are always set together and the reader may rely on it. */
313
+ if (mesh.joints !== undefined) attributes |= ATTR_JOINTS;
314
+ if (mesh.weights !== undefined) attributes |= ATTR_WEIGHTS;
315
+
316
+ /* Order is frozen: mandatory arrays, then optional ones by ascending bit. */
317
+ const arrays: (Float32Array | Uint32Array)[] = [
318
+ mesh.positions,
319
+ mesh.normals,
320
+ mesh.colors,
321
+ mesh.emissive,
322
+ ];
323
+ if (mesh.specular !== undefined) arrays.push(mesh.specular);
324
+ if (mesh.uvs !== undefined) arrays.push(mesh.uvs);
325
+ if (mesh.emissiveColor !== undefined) arrays.push(mesh.emissiveColor);
326
+ if (mesh.roughness !== undefined) arrays.push(mesh.roughness);
327
+ if (mesh.grain !== undefined) arrays.push(mesh.grain);
328
+ if (mesh.relief !== undefined) arrays.push(mesh.relief);
329
+ if (mesh.tangents !== undefined) arrays.push(mesh.tangents);
330
+ /* Last, because the order is frozen and these are the newest bits. See ATTR_JOINTS: the
331
+ container validated this pair for four minor versions while writing neither of them. */
332
+ if (mesh.joints !== undefined) arrays.push(mesh.joints);
333
+ if (mesh.weights !== undefined) arrays.push(mesh.weights);
334
+ arrays.push(mesh.indices);
335
+
336
+ const PREFIX = 16;
337
+ let total = PREFIX;
338
+ for (const array of arrays) total += array.byteLength;
339
+
340
+ const bytes = new Uint8Array(total);
341
+ const view = new DataView(bytes.buffer);
342
+ view.setUint32(0, vertices, true);
343
+ view.setUint32(4, mesh.indices.length, true);
344
+ view.setUint32(8, attributes, true);
345
+ view.setUint32(12, 0, true);
346
+
347
+ let at = PREFIX;
348
+ for (const array of arrays) {
349
+ bytes.set(new Uint8Array(array.buffer, array.byteOffset, array.byteLength), at);
350
+ at += array.byteLength;
351
+ }
352
+ return { code, flags, bytes };
353
+ }
354
+
355
+ /**
356
+ * One embedded image: its codec and size, then the compressed bytes untouched.
357
+ *
358
+ * The payload is not re-encoded. A JPEG the artist exported is already the size it is going
359
+ * to be, and decoding it here to compress it again would cost quality for nothing.
360
+ */
361
+ function buildTexture(texture: DrftTextureSource): PendingChunk {
362
+ const PREFIX = 16;
363
+ /* The name follows the payload rather than preceding it, so the image still starts at a
364
+ fixed offset and a reader that predates the name simply stops before it. */
365
+ const name = encodeString(texture.name);
366
+ const nameAt = align(PREFIX + texture.bytes.length);
367
+ const bytes = new Uint8Array(nameAt + name.length);
368
+ const view = new DataView(bytes.buffer);
369
+ view.setUint32(0, texture.codec, true);
370
+ view.setUint32(4, texture.width, true);
371
+ view.setUint32(8, texture.height, true);
372
+ view.setUint32(12, texture.bytes.length, true);
373
+ bytes.set(texture.bytes, PREFIX);
374
+ bytes.set(name, nameAt);
375
+ return { code: CHUNK_TEXS, flags: 0, bytes };
376
+ }
377
+
378
+ /**
379
+ * Every material in one chunk: they are small, and the loader wants them all before it uploads.
380
+ *
381
+ * **The entry stride is written down rather than implied**, which is what makes a new material
382
+ * field additive instead of a break. A reader takes the fields it knows and steps by the
383
+ * stride it was told, so a file carrying an eleventh float opens in a reader that knows ten.
384
+ * Without it, appending one field changes the meaning of every byte after the first entry,
385
+ * which is precisely what rule 4 of §4.4 forbids, and the failure is silent: every material
386
+ * after the first would describe the wrong surface.
387
+ *
388
+ * Names follow the entries as a length-prefixed block, because they are the one variable
389
+ * length thing here and putting them inline would cost the fixed stride that buys the above.
390
+ */
391
+ function buildMaterials(materials: readonly DrftMaterial[]): PendingChunk {
392
+ const names = materials.map((material) => encodeString(material.name));
393
+ let nameBytes = 0;
394
+ for (const name of names) nameBytes += name.length;
395
+
396
+ const bytes = new Uint8Array(align(8 + materials.length * MATERIAL_ENTRY_BYTES + nameBytes));
397
+ const view = new DataView(bytes.buffer);
398
+ view.setUint32(0, materials.length, true);
399
+ view.setUint32(4, MATERIAL_ENTRY_BYTES, true);
400
+ let namesAt = 8 + materials.length * MATERIAL_ENTRY_BYTES;
401
+ for (const name of names) {
402
+ bytes.set(name, namesAt);
403
+ namesAt += name.length;
404
+ }
405
+
406
+ let at = 8;
407
+ for (const material of materials) {
408
+ view.setFloat32(at, material.color[0], true);
409
+ view.setFloat32(at + 4, material.color[1], true);
410
+ view.setFloat32(at + 8, material.color[2], true);
411
+ view.setFloat32(at + 12, material.specular, true);
412
+ view.setFloat32(at + 16, material.roughness, true);
413
+ view.setFloat32(at + 20, material.emissive, true);
414
+ view.setFloat32(at + 24, material.emissiveColor[0], true);
415
+ view.setFloat32(at + 28, material.emissiveColor[1], true);
416
+ view.setFloat32(at + 32, material.emissiveColor[2], true);
417
+ view.setFloat32(at + 36, material.opacity, true);
418
+ view.setInt32(at + 40, material.albedo, true);
419
+ view.setFloat32(at + 44, material.reflectivity, true);
420
+ view.setInt32(at + 48, material.normalMap, true);
421
+ view.setInt32(at + 52, material.ormMap, true);
422
+ view.setInt32(at + 56, material.emissiveMap, true);
423
+ view.setFloat32(at + 60, material.roughnessScale, true);
424
+ view.setFloat32(at + 64, material.metallicScale, true);
425
+ view.setFloat32(at + 68, material.occlusionStrength, true);
426
+ view.setFloat32(at + 72, material.cutout, true);
427
+ at += MATERIAL_ENTRY_BYTES;
428
+ }
429
+ return { code: CHUNK_MATL, flags: 0, bytes };
430
+ }
431
+
432
+ /**
433
+ * Bake a `.drft` into one `ArrayBuffer`.
434
+ *
435
+ * Every payload lands 4-byte aligned, which is what lets the reader view the vertex data
436
+ * in place rather than copying it out.
437
+ */
438
+ export function writeDrft(source: DrftSource): ArrayBuffer {
439
+ /*
440
+ * **Geometry *or* a capture, since 1.5.** This said "at least one mesh" while a mesh was the
441
+ * only thing an asset could be made of; a file holding one Gaussian splat capture is an
442
+ * ordinary asset and the reader accepts it, so refusing to write one would be the two halves of
443
+ * the format disagreeing about what a file may contain.
444
+ */
445
+ if (source.meshes.length === 0 && (source.splats === undefined || source.splats.count === 0)) {
446
+ throw new DrftError('an asset must contain at least one mesh or a capture with splats in it');
447
+ }
448
+
449
+ const materials = source.materials;
450
+ if (materials !== undefined && materials.length !== source.meshes.length) {
451
+ throw new DrftError(
452
+ `${materials.length} materials for ${source.meshes.length} meshes; they pair by ordinal, so the counts must match`,
453
+ );
454
+ }
455
+
456
+ /*
457
+ * **Laid out in the order a viewer needs it, which is docs/FORMAT.md §4.6 and costs nothing.**
458
+ *
459
+ * Offsets are absolute, so where a payload sits is a bake-time choice no reader pays for, and
460
+ * this order is what makes a *plain sequential fetch* refine the model as bytes arrive: no
461
+ * range requests, no server support beyond serving a file. See `drftStream.ts`.
462
+ *
463
+ * `MATL` before the geometry is the part worth explaining. It is about 8 KB for a 187-mesh
464
+ * car against 74 MB of file, and it is what turns arriving parts from grey shapes into a
465
+ * correctly painted model. Paint costs almost nothing and buys the largest single jump in how
466
+ * finished the thing looks, so it goes early; textures are megabytes and buy the smallest, so
467
+ * they go last.
468
+ *
469
+ * `MATL` is optional and therefore skipped in silence by a reader that predates it, per rule 2
470
+ * in §4.4: an older reader draws the asset with its vertex colours and no maps, which is the
471
+ * same asset less its textures rather than a refusal. Moving it earlier in the file changes
472
+ * nothing about that, because a chunk is found through the table rather than by position.
473
+ */
474
+ const chunks: PendingChunk[] = [buildHead(source.head, source.meshes)];
475
+ /*
476
+ * The outline first, ahead of even the paint. It is the one thing that puts a recognisable
477
+ * whole object on screen, it is a few hundred kilobytes against tens of megabytes, and the
478
+ * order it is written in is the order a plain sequential fetch delivers it.
479
+ */
480
+ for (const lod of source.lods ?? []) chunks.push(buildMesh(lod, CHUNK_LODM, 0));
481
+ /*
482
+ * Discrete levels, finest first — the opposite of `lods` above, and deliberately. `LODM` is
483
+ * coarsest first because it serves a progressive load, where drawing *something* early is the
484
+ * point; a discrete chain serves distance selection, where level 0 should be the full-detail
485
+ * model the consumer already holds.
486
+ */
487
+ for (const level of source.levels ?? [])
488
+ chunks.push({ code: CHUNK_LODF, flags: 0, bytes: level });
489
+ if (materials !== undefined && materials.length > 0) chunks.push(buildMaterials(materials));
490
+ /*
491
+ * After the paint and before the parts. Paint is a few kilobytes and turns everything after it
492
+ * from grey into a finished surface; a capture is the place the parts stand in, and its first
493
+ * block is a recognisable whole. Both come before the meshes for the same reason the outline
494
+ * does: what puts something worth looking at on screen goes early.
495
+ */
496
+ if (source.splats !== undefined) chunks.push(...buildSplats(source.splats));
497
+ /*
498
+ * The rig before the geometry it deforms. A streaming consumer that has the skeleton and the
499
+ * clips can start a character the moment the first mesh lands, where the reverse order makes it
500
+ * wait for both; and all three are small beside the parts, so nothing is delayed by much.
501
+ */
502
+ for (const node of source.nodes === undefined ? [] : [source.nodes]) {
503
+ chunks.push({ code: CHUNK_NODE, flags: 0, bytes: buildNodes(node) });
504
+ }
505
+ for (const skin of source.skins ?? []) {
506
+ chunks.push({ code: CHUNK_SKIN, flags: 0, bytes: buildSkin(skin) });
507
+ }
508
+ for (const clip of source.clips ?? []) {
509
+ chunks.push({ code: CHUNK_ANIM, flags: 0, bytes: buildClip(clip) });
510
+ }
511
+ source.meshes.forEach((mesh, ordinal) => {
512
+ /*
513
+ * **Its deltas immediately before it, not after.** A streaming consumer uploads a mesh the
514
+ * moment its chunk lands, so deltas arriving afterwards are deltas for geometry already on the
515
+ * GPU — and a stream cannot rewrite a mesh it has handed over. Written first, the consumer has
516
+ * them in hand when it builds. The mesh ordinal in the payload still does the pairing, so a
517
+ * reader meeting them in another order is unaffected.
518
+ */
519
+ if (mesh.morphTargets !== undefined && mesh.morphTargetCount !== undefined) {
520
+ chunks.push({
521
+ code: CHUNK_MORP,
522
+ flags: 0,
523
+ bytes: buildMorph({
524
+ mesh: ordinal,
525
+ targetCount: mesh.morphTargetCount,
526
+ deltas: mesh.morphTargets,
527
+ }),
528
+ });
529
+ }
530
+ chunks.push(buildMesh(mesh));
531
+ });
532
+ for (const texture of source.textures ?? []) chunks.push(buildTexture(texture));
533
+ /* One `SUBS` for the file, and only where something was labelled — an empty chunk would cost
534
+ sixteen bytes to say nothing, and rule 2 already covers its absence. */
535
+ if ((source.substances?.length ?? 0) > 0) {
536
+ chunks.push({
537
+ code: CHUNK_SUBS,
538
+ flags: 0,
539
+ bytes: buildSubs({ entries: source.substances as readonly DrftSubstanceEntry[] }),
540
+ });
541
+ }
542
+
543
+ /*
544
+ * `COLL` last among the small chunks, because collision is the one thing a viewer never needs and
545
+ * a consumer that simulates has already waited for the whole file by the time it builds a world.
546
+ */
547
+ if ((source.colliders?.length ?? 0) > 0) {
548
+ chunks.push({
549
+ code: CHUNK_COLL,
550
+ flags: 0,
551
+ bytes: buildColliders(source.colliders as readonly Float32Array[]),
552
+ });
553
+ }
554
+
555
+ const tableBytes = chunks.length * CHUNK_ENTRY_BYTES;
556
+ let cursor = align(HEADER_BYTES + tableBytes);
557
+ const offsets: number[] = [];
558
+ for (const chunk of chunks) {
559
+ offsets.push(cursor);
560
+ cursor = align(cursor + chunk.bytes.length);
561
+ }
562
+
563
+ const total = cursor;
564
+ const buffer = new ArrayBuffer(total);
565
+ const bytes = new Uint8Array(buffer);
566
+ const view = new DataView(buffer);
567
+
568
+ view.setUint32(0, DRFT_MAGIC, true);
569
+ view.setUint16(4, DRFT_VERSION_MAJOR, true);
570
+ view.setUint16(6, DRFT_VERSION_MINOR, true);
571
+ /*
572
+ * The oldest reader that may open this. Equal to the current major because nothing here
573
+ * needs a newer one — a writer raises it only when it emits something a previous
574
+ * generation would misread, which is exactly what a major bump means.
575
+ */
576
+ view.setUint16(8, DRFT_VERSION_MAJOR, true);
577
+ view.setUint16(10, FLAG_LITTLE_ENDIAN, true);
578
+ view.setUint32(12, chunks.length, true);
579
+ view.setUint32(16, total, true);
580
+ /* Bytes 20..31 stay zero: reserved, and readers must ignore them so they can become
581
+ fields later without a major bump. */
582
+
583
+ /*
584
+ * `index` is the ordinal *within a FourCC*, not the chunk's position in the table.
585
+ *
586
+ * It used to be written as the table position, which happened to be right for `MESH`
587
+ * because the meshes come first, and was wrong for everything after them: the six `TEXS`
588
+ * chunks in a 195 chunk file were numbered 188 to 193. Nothing here noticed, because this
589
+ * reader takes textures in the order it meets them rather than by the number they carry.
590
+ * A second implementation reading §4.3 would have used the number, and indexed six
591
+ * textures at 188.
592
+ */
593
+ const ordinals = new Map<number, number>();
594
+ let entry = HEADER_BYTES;
595
+ for (let i = 0; i < chunks.length; i++) {
596
+ const chunk = chunks[i] as PendingChunk;
597
+ const offset = offsets[i] as number;
598
+ const ordinal = ordinals.get(chunk.code) ?? 0;
599
+ ordinals.set(chunk.code, ordinal + 1);
600
+ view.setUint32(entry, chunk.code, true);
601
+ view.setUint32(entry + 4, offset, true);
602
+ view.setUint32(entry + 8, chunk.bytes.length, true);
603
+ view.setUint16(entry + 12, chunk.flags, true);
604
+ view.setUint16(entry + 14, ordinal, true);
605
+ entry += CHUNK_ENTRY_BYTES;
606
+ bytes.set(chunk.bytes, offset);
607
+ if (offset % ALIGNMENT !== 0) {
608
+ throw new DrftError(`internal: chunk ${i} landed unaligned at ${offset}`);
609
+ }
610
+ }
611
+
612
+ return buffer;
613
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * A `.drft` written by version 1.0, kept byte for byte.
3
+ *
4
+ * **Never regenerate this file.** It exists so today's reader can be tested against
5
+ * genuinely old bytes; rewriting it with a newer writer would only prove that the writer
6
+ * and the reader agree, which they always do, and would silently retire the guarantee it
7
+ * is here to defend.
8
+ */
9
+ export const FIXTURE_V1_0 =
10
+ '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ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+ZmZmP83MzD3NzEw+AACAPgAAgD4AAIA+AACAPgAAgD4AAIA+AACAPgAAgD4AAIA+AACAPgAAgD4AAIA+AACAPgAAgD4AAIA+AACAPgAAgD4AAIA+AACAPgAAgD4AAIA+AACAPgAAgD4AAIA+zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPc3MzD3NzMw9zczMPs3MzD7NzMw+zczMPs3MzD7NzMw+zczMPs3MzD7NzMw+zczMPs3MzD7NzMw+zczMPs3MzD7NzMw+zczMPs3MzD7NzMw+zczMPs3MzD7NzMw+zczMPs3MzD7NzMw+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/zcxMPs3MzD5mZmY/mpmZPpqZmT6amZk+mpmZPpqZmT6amZk+mpmZPpqZmT6amZk+mpmZPpqZmT6amZk+mpmZPpqZmT6amZk+mpmZPpqZmT6amZk+mpmZPpqZmT6amZk+mpmZPpqZmT6amZk+mpmZPpqZmT6amZk+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TAAAAFYAAABXAAAATAAAAFcAAABNAAAATQAAAFcAAABYAAAATQAAAFgAAABOAAAATgAAAFgAAABZAAAATwAAAFoAAABQAAAAUAAAAFoAAABbAAAAUAAAAFsAAABRAAAAUQAAAFsAAABcAAAAUQAAAFwAAABSAAAAUgAAAFwAAABdAAAAUgAAAF0AAABTAAAAUwAAAF0AAABeAAAAUwAAAF4AAABUAAAAVAAAAF4AAABfAAAAVAAAAF8AAABVAAAAVQAAAF8AAABgAAAAVQAAAGAAAABWAAAAVgAAAGAAAABhAAAAVgAAAGEAAABXAAAAVwAAAGEAAABiAAAAVwAAAGIAAABYAAAAWAAAAGIAAABjAAAAWAAAAGMAAABZAAAAWQAAAGMAAABkAAAAWgAAAGUAAABbAAAAWwAAAGUAAABmAAAAWwAAAGYAAABcAAAAXAAAAGYAAABnAAAAXAAAAGcAAABdAAAAXQAAAGcAAABoAAAAXQAAAGgAAABeAAAAXgAAAGgAAABpAAAAXgAAAGkAAABfAAAAXwAAAGkAAABqAAAAXwAAAGoAAABgAAAAYAAAAGoAAABrAAAAYAAAAGsAAABhAAAAYQAAAGsAAABsAAAAYQAAAGwAAABiAAAAYgAAAGwAAABtAAAAYgAAAG0AAABjAAAAYwAAAG0AAABuAAAAYwAAAG4AAABkAAAAZAAAAG4AAABvAAAAZQAAAHAAAABmAAAAZgAAAHAAAABxAAAAZgAAAHEAAABnAAAAZwAAAHEAAAByAAAAZwAAAHIAAABoAAAAaAAAAHIAAABzAAAAaAAAAHMAAABpAAAAaQAAAHMAAAB0AAAAaQAAAHQAAABqAAAAagAAAHQAAAB1AAAAagAAAHUAAABrAAAAawAAAHUAAAB2AAAAawAAAHYAAABsAAAAbAAAAHYAAAB3AAAAbAAAAHcAAABtAAAAbQAAAHcAAAB4AAAAbQAAAHgAAABuAAAAbgAAAHgAAAB5AAAAbgAAAHkAAABvAAAAbwAAAHkAAAB6AAAAcAAAAHsAAABxAAAAcQAAAHsAAAB8AAAAcQAAAHwAAAByAAAAcgAAAHwAAAB9AAAAcgAAAH0AAABzAAAAcwAAAH0AAAB+AAAAcwAAAH4AAAB0AAAAdAAAAH4AAAB/AAAAdAAAAH8AAAB1AAAAdQAAAH8AAACAAAAAdQAAAIAAAAB2AAAAdgAAAIAAAACBAAAAdgAAAIEAAAB3AAAAdwAAAIEAAACCAAAAdwAAAIIAAAB4AAAAeAAAAIIAAACDAAAAeAAAAIMAAAB5AAAAeQAAAIMAAACEAAAAeQAAAIQAAAB6AAAAegAAAIQAAACFAAAAewAAAIYAAAB8AAAAfAAAAIYAAACHAAAAfAAAAIcAAAB9AAAAfQAAAIcAAACIAAAAfQAAAIgAAAB+AAAAfgAAAIgAAACJAAAAfgAAAIkAAAB/AAAAfwAAAIkAAACKAAAAfwAAAIoAAACAAAAAgAAAAIoAAACLAAAAgAAAAIsAAACBAAAAgQAAAIsAAACMAAAAgQAAAIwAAACCAAAAggAAAIwAAACNAAAAggAAAI0AAACDAAAAgwAAAI0AAACOAAAAgwAAAI4AAACEAAAAhAAAAI4AAACPAAAAhAAAAI8AAACFAAAAhQAAAI8AAACQAAAAhgAAAJEAAACHAAAAhwAAAJEAAACSAAAAhwAAAJIAAACIAAAAiAAAAJIAAACTAAAAiAAAAJMAAACJAAAAiQAAAJMAAACUAAAAiQAAAJQAAACKAAAAigAAAJQAAACVAAAAigAAAJUAAACLAAAAiwAAAJUAAACWAAAAiwAAAJYAAACMAAAAjAAAAJYAAACXAAAAjAAAAJcAAACNAAAAjQAAAJcAAACYAAAAjQAAAJgAAACOAAAAjgAAAJgAAACZAAAAjgAAAJkAAACPAAAAjwAAAJkAAACaAAAAjwAAAJoAAACQAAAAkAAAAJoAAACbAAAAGAAAACQAAAAAAAAAAAAAAAAAGEEAAAC/AAAAvwAAGEEAAAA/AAAAvwAAGEEAAAA/AAAAPwAAGEEAAAC/AAAAPwAACEEAAAC/AAAAPwAACEEAAAA/AAAAPwAACEEAAAA/AAAAvwAACEEAAAC/AAAAvwAACEEAAAA/AAAAvwAACEEAAAA/AAAAPwAAGEEAAAA/AAAAPwAAGEEAAAA/AAAAvwAAGEEAAAC/AAAAvwAAGEEAAAC/AAAAPwAACEEAAAC/AAAAPwAACEEAAAC/AAAAvwAACEEAAAC/AAAAPwAAGEEAAAC/AAAAPwAAGEEAAAA/AAAAPwAACEEAAAA/AAAAPwAACEEAAAA/AAAAvwAAGEEAAAA/AAAAvwAAGEEAAAC/AAAAvwAACEEAAAC/AAAAvwAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAv83MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPs3MzD3NzEw+mpmZPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAgAAAAAAAAACAAAAAwAAAAQAAAAFAAAABgAAAAQAAAAGAAAABwAAAAgAAAAJAAAACgAAAAgAAAAKAAAACwAAAAwAAAANAAAADgAAAAwAAAAOAAAADwAAABAAAAARAAAAEgAAABAAAAASAAAAEwAAABQAAAAVAAAAFgAAABQAAAAWAAAAFwAAAA==';