@bis-toolkit/p3d 1.0.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.
package/dist/index.js ADDED
@@ -0,0 +1,2265 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/mlod/index.ts
8
+ var mlod_exports = {};
9
+ __export(mlod_exports, {
10
+ BinaryReader: () => BinaryReader,
11
+ Face: () => Face,
12
+ FaceFlags: () => FaceFlags,
13
+ Mlod: () => Mlod,
14
+ MlodLod: () => MlodLod,
15
+ Point: () => Point,
16
+ PointFlags: () => PointFlags,
17
+ TaggReader: () => TaggReader,
18
+ Vector3: () => Vector3,
19
+ Vertex: () => Vertex,
20
+ getLodName: () => getLodName
21
+ });
22
+
23
+ // ../utils/dist/index.js
24
+ var BinaryReader = class {
25
+ constructor(buffer) {
26
+ this.position = 0;
27
+ this.buffer = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
28
+ this.view = new DataView(this.buffer.buffer, this.buffer.byteOffset, this.buffer.byteLength);
29
+ }
30
+ get length() {
31
+ return this.buffer.length;
32
+ }
33
+ get pos() {
34
+ return this.position;
35
+ }
36
+ seek(offset, origin = "begin") {
37
+ switch (origin) {
38
+ case "begin":
39
+ this.position = offset;
40
+ break;
41
+ case "current":
42
+ this.position += offset;
43
+ break;
44
+ case "end":
45
+ this.position = this.buffer.length + offset;
46
+ break;
47
+ }
48
+ }
49
+ readByte() {
50
+ const value = this.view.getUint8(this.position);
51
+ this.position += 1;
52
+ return value;
53
+ }
54
+ readUInt16() {
55
+ const value = this.view.getUint16(this.position, true);
56
+ this.position += 2;
57
+ return value;
58
+ }
59
+ readUInt32() {
60
+ const value = this.view.getUint32(this.position, true);
61
+ this.position += 4;
62
+ return value;
63
+ }
64
+ readInt32() {
65
+ const value = this.view.getInt32(this.position, true);
66
+ this.position += 4;
67
+ return value;
68
+ }
69
+ readInt24() {
70
+ const b1 = this.view.getUint8(this.position);
71
+ const b2 = this.view.getUint8(this.position + 1);
72
+ const b3 = this.view.getUint8(this.position + 2);
73
+ this.position += 3;
74
+ return b1 | b2 << 8 | b3 << 16;
75
+ }
76
+ readBytes(count) {
77
+ const bytes = this.buffer.subarray(this.position, this.position + count);
78
+ this.position += count;
79
+ return bytes;
80
+ }
81
+ readRawString(length) {
82
+ const bytes = this.buffer.subarray(this.position, this.position + length);
83
+ this.position += length;
84
+ return String.fromCharCode(...bytes);
85
+ }
86
+ readFloat() {
87
+ const value = this.view.getFloat32(this.position, true);
88
+ this.position += 4;
89
+ return value;
90
+ }
91
+ readBoolean() {
92
+ return this.readByte() !== 0;
93
+ }
94
+ /**
95
+ * Read a null-terminated C-style string
96
+ */
97
+ readCString() {
98
+ const start = this.position;
99
+ let end = start;
100
+ while (end < this.buffer.length && this.buffer[end] !== 0) {
101
+ end++;
102
+ }
103
+ const bytes = this.buffer.subarray(start, end);
104
+ this.position = end + 1;
105
+ const decoder = new TextDecoder("utf-8");
106
+ return decoder.decode(bytes);
107
+ }
108
+ /**
109
+ * Alias for readRawString for compatibility
110
+ */
111
+ readString(length) {
112
+ return this.readRawString(length);
113
+ }
114
+ };
115
+ var LZO = class _LZO {
116
+ constructor() {
117
+ this._blockSize = 128 * 1024;
118
+ this._minNewSize = this.blockSize;
119
+ this._out = new Uint8Array(256 * 1024);
120
+ this._cbl = 0;
121
+ this._t = 0;
122
+ this._inputPointer = 0;
123
+ this._outputPointer = 0;
124
+ this._matchPosition = 0;
125
+ this._skipToFirstLiteralFunc = false;
126
+ }
127
+ get blockSize() {
128
+ return this._blockSize;
129
+ }
130
+ set blockSize(value) {
131
+ if (value <= 0) throw new Error("Block size must be a positive integer");
132
+ this._blockSize = value;
133
+ }
134
+ _extendBuffer() {
135
+ const newBuffer = new Uint8Array(
136
+ this._minNewSize + (this.blockSize - this._minNewSize % this.blockSize)
137
+ );
138
+ newBuffer.set(this._out);
139
+ this._out = newBuffer;
140
+ this._cbl = this._out.length;
141
+ }
142
+ _matchNext() {
143
+ this._minNewSize = this._outputPointer + 3;
144
+ if (this._minNewSize > this._cbl) this._extendBuffer();
145
+ this._out[this._outputPointer++] = this._buffer[this._inputPointer++];
146
+ if (this._t > 1) {
147
+ this._out[this._outputPointer++] = this._buffer[this._inputPointer++];
148
+ if (this._t > 2) {
149
+ this._out[this._outputPointer++] = this._buffer[this._inputPointer++];
150
+ }
151
+ }
152
+ this._t = this._buffer[this._inputPointer++];
153
+ }
154
+ _matchDone() {
155
+ this._t = this._buffer[this._inputPointer - 2] & 3;
156
+ return this._t;
157
+ }
158
+ _copyMatch() {
159
+ this._t += 2;
160
+ this._minNewSize = this._outputPointer + this._t;
161
+ if (this._minNewSize > this._cbl) {
162
+ this._extendBuffer();
163
+ }
164
+ do {
165
+ this._out[this._outputPointer++] = this._out[this._matchPosition++];
166
+ } while (--this._t > 0);
167
+ }
168
+ _copyFromBuffer() {
169
+ this._minNewSize = this._outputPointer + this._t;
170
+ if (this._minNewSize > this._cbl) {
171
+ this._extendBuffer();
172
+ }
173
+ do {
174
+ this._out[this._outputPointer++] = this._buffer[this._inputPointer++];
175
+ } while (--this._t > 0);
176
+ }
177
+ _match() {
178
+ while (true) {
179
+ if (this._t >= 64) {
180
+ this._matchPosition = this._outputPointer - 1 - (this._t >> 2 & 7) - (this._buffer[this._inputPointer++] << 3);
181
+ this._t = (this._t >> 5) - 1;
182
+ this._copyMatch();
183
+ } else if (this._t >= 32) {
184
+ this._t &= 31;
185
+ if (this._t === 0) {
186
+ while (this._buffer[this._inputPointer] === 0) {
187
+ this._t += 255;
188
+ this._inputPointer++;
189
+ }
190
+ this._t += 31 + this._buffer[this._inputPointer++];
191
+ }
192
+ this._matchPosition = this._outputPointer - 1 - (this._buffer[this._inputPointer] >> 2) - (this._buffer[this._inputPointer + 1] << 6);
193
+ this._inputPointer += 2;
194
+ this._copyMatch();
195
+ } else if (this._t >= 16) {
196
+ this._matchPosition = this._outputPointer - ((this._t & 8) << 11);
197
+ this._t &= 7;
198
+ if (this._t === 0) {
199
+ while (this._buffer[this._inputPointer] === 0) {
200
+ this._t += 255;
201
+ this._inputPointer++;
202
+ }
203
+ this._t += 7 + this._buffer[this._inputPointer++];
204
+ }
205
+ this._matchPosition -= (this._buffer[this._inputPointer] >> 2) + (this._buffer[this._inputPointer + 1] << 6);
206
+ this._inputPointer += 2;
207
+ if (this._matchPosition === this._outputPointer) {
208
+ return this._out.subarray(0, this._outputPointer);
209
+ } else {
210
+ this._matchPosition -= 16384;
211
+ this._copyMatch();
212
+ }
213
+ } else {
214
+ this._matchPosition = this._outputPointer - 1 - (this._t >> 2) - (this._buffer[this._inputPointer++] << 2);
215
+ this._minNewSize = this._outputPointer + 2;
216
+ if (this._minNewSize > this._cbl) {
217
+ this._extendBuffer();
218
+ }
219
+ this._out[this._outputPointer++] = this._out[this._matchPosition++];
220
+ this._out[this._outputPointer++] = this._out[this._matchPosition];
221
+ }
222
+ if (this._matchDone() === 0) {
223
+ return true;
224
+ }
225
+ this._matchNext();
226
+ }
227
+ }
228
+ _decompressBuffer(buffer) {
229
+ this._buffer = buffer;
230
+ this._cbl = this._out.length;
231
+ this._t = 0;
232
+ this._inputPointer = 0;
233
+ this._outputPointer = 0;
234
+ this._matchPosition = 0;
235
+ this._skipToFirstLiteralFunc = false;
236
+ if (this._buffer[this._inputPointer] > 17) {
237
+ this._t = this._buffer[this._inputPointer++] - 17;
238
+ if (this._t < 4) {
239
+ this._matchNext();
240
+ const matched = this._match();
241
+ if (matched !== true) return matched;
242
+ } else {
243
+ this._copyFromBuffer();
244
+ this._skipToFirstLiteralFunc = true;
245
+ }
246
+ }
247
+ while (true) {
248
+ if (!this._skipToFirstLiteralFunc) {
249
+ this._t = this._buffer[this._inputPointer++];
250
+ if (this._t >= 16) {
251
+ const matched2 = this._match();
252
+ if (matched2 !== true) return matched2;
253
+ continue;
254
+ } else if (this._t === 0) {
255
+ while (this._buffer[this._inputPointer] === 0) {
256
+ this._t += 255;
257
+ this._inputPointer++;
258
+ }
259
+ this._t += 15 + this._buffer[this._inputPointer++];
260
+ }
261
+ this._t += 3;
262
+ this._copyFromBuffer();
263
+ } else this._skipToFirstLiteralFunc = false;
264
+ this._t = this._buffer[this._inputPointer++];
265
+ if (this._t < 16) {
266
+ this._matchPosition = this._outputPointer - (1 + 2048);
267
+ this._matchPosition -= this._t >> 2;
268
+ this._matchPosition -= this._buffer[this._inputPointer++] << 2;
269
+ this._minNewSize = this._outputPointer + 3;
270
+ if (this._minNewSize > this._cbl) {
271
+ this._extendBuffer();
272
+ }
273
+ this._out[this._outputPointer++] = this._out[this._matchPosition++];
274
+ this._out[this._outputPointer++] = this._out[this._matchPosition++];
275
+ this._out[this._outputPointer++] = this._out[this._matchPosition];
276
+ if (this._matchDone() === 0) continue;
277
+ else this._matchNext();
278
+ }
279
+ const matched = this._match();
280
+ if (matched !== true) return matched;
281
+ }
282
+ }
283
+ /**
284
+ * Decompresses the given buffer using the LZO1X-1 algorithm.
285
+ * @param buffer The buffer to decompress.
286
+ * @returns The decompressed buffer.
287
+ */
288
+ static decompress(buffer) {
289
+ return new _LZO()._decompressBuffer(buffer);
290
+ }
291
+ /**
292
+ * Decompresses the given buffer and returns both the decompressed data and bytes read.
293
+ * @param buffer The buffer to decompress.
294
+ * @returns Object containing decompressed data and number of bytes consumed from input.
295
+ */
296
+ static decompressWithSize(buffer) {
297
+ const lzo = new _LZO();
298
+ const decompressed = lzo._decompressBuffer(buffer);
299
+ return {
300
+ data: decompressed,
301
+ bytesRead: lzo._inputPointer
302
+ };
303
+ }
304
+ };
305
+ function lzoDecompressWithSize(src, expectedSize) {
306
+ const input = src instanceof Uint8Array ? src : new Uint8Array(src);
307
+ const result = LZO.decompressWithSize(input);
308
+ if (result.data.length !== expectedSize) {
309
+ throw new Error(`LZO decompression size mismatch: expected ${expectedSize}, got ${result.data.length}`);
310
+ }
311
+ return result;
312
+ }
313
+
314
+ // src/mlod/Vector3.ts
315
+ var Vector3 = class _Vector3 {
316
+ constructor(x = 0, y = 0, z = 0) {
317
+ this.x = x;
318
+ this.y = y;
319
+ this.z = z;
320
+ }
321
+ get length() {
322
+ return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
323
+ }
324
+ static fromReader(reader) {
325
+ return new _Vector3(
326
+ reader.readFloat(),
327
+ reader.readFloat(),
328
+ reader.readFloat()
329
+ );
330
+ }
331
+ /**
332
+ * Returns a new Vector3 with the X coordinate flipped.
333
+ * Useful for converting between coordinate systems (e.g., MLOD to Three.js)
334
+ */
335
+ flipX() {
336
+ return new _Vector3(-this.x, this.y, this.z);
337
+ }
338
+ /**
339
+ * Returns a new Vector3 with the Y coordinate flipped.
340
+ * Useful for converting between coordinate systems
341
+ */
342
+ flipY() {
343
+ return new _Vector3(this.x, -this.y, this.z);
344
+ }
345
+ /**
346
+ * Returns a new Vector3 with the Z coordinate flipped.
347
+ * Useful for converting between coordinate systems
348
+ */
349
+ flipZ() {
350
+ return new _Vector3(this.x, this.y, -this.z);
351
+ }
352
+ toArray() {
353
+ return [this.x, this.y, this.z];
354
+ }
355
+ };
356
+
357
+ // src/mlod/PointFlags.ts
358
+ var PointFlags = /* @__PURE__ */ ((PointFlags2) => {
359
+ PointFlags2[PointFlags2["None"] = 0] = "None";
360
+ PointFlags2[PointFlags2["Selected"] = 1] = "Selected";
361
+ PointFlags2[PointFlags2["Hidden"] = 2] = "Hidden";
362
+ PointFlags2[PointFlags2["Locked"] = 4] = "Locked";
363
+ return PointFlags2;
364
+ })(PointFlags || {});
365
+
366
+ // src/mlod/Point.ts
367
+ var Point = class _Point extends Vector3 {
368
+ constructor(x = 0, y = 0, z = 0, flags = 0 /* None */) {
369
+ super(x, y, z);
370
+ this.flags = flags;
371
+ }
372
+ static fromReader(reader) {
373
+ const x = reader.readFloat();
374
+ const y = reader.readFloat();
375
+ const z = reader.readFloat();
376
+ const flags = reader.readUInt32();
377
+ return new _Point(x, y, z, flags);
378
+ }
379
+ /**
380
+ * Returns a new Point with the X coordinate flipped.
381
+ * Useful for converting between coordinate systems (e.g., MLOD to Three.js)
382
+ */
383
+ flipX() {
384
+ return new _Point(-this.x, this.y, this.z, this.flags);
385
+ }
386
+ /**
387
+ * Returns a new Point with the Y coordinate flipped.
388
+ * Useful for converting between coordinate systems
389
+ */
390
+ flipY() {
391
+ return new _Point(this.x, -this.y, this.z, this.flags);
392
+ }
393
+ /**
394
+ * Returns a new Point with the Z coordinate flipped.
395
+ * Useful for converting between coordinate systems
396
+ */
397
+ flipZ() {
398
+ return new _Point(this.x, this.y, -this.z, this.flags);
399
+ }
400
+ };
401
+
402
+ // src/mlod/Vertex.ts
403
+ var Vertex = class _Vertex {
404
+ constructor(pointIndex, normalIndex, u, v) {
405
+ this.pointIndex = pointIndex;
406
+ this.normalIndex = normalIndex;
407
+ this.u = u;
408
+ this.v = v;
409
+ }
410
+ static fromReader(reader) {
411
+ const pointIndex = reader.readInt32();
412
+ const normalIndex = reader.readInt32();
413
+ const u = reader.readFloat();
414
+ const v = reader.readFloat();
415
+ return new _Vertex(pointIndex, normalIndex, u, v);
416
+ }
417
+ };
418
+
419
+ // src/mlod/Face.ts
420
+ var Face = class _Face {
421
+ constructor(sidesCnt, vertices, flags, texture, material) {
422
+ this.sidesCnt = sidesCnt;
423
+ this.vertices = vertices;
424
+ this.flags = flags;
425
+ this.texture = texture;
426
+ this.material = material;
427
+ }
428
+ static fromReader(reader) {
429
+ const sidesCnt = reader.readInt32();
430
+ const vertices = [];
431
+ for (let i = 0; i < 4; i++) {
432
+ vertices.push(Vertex.fromReader(reader));
433
+ }
434
+ const flags = reader.readInt32();
435
+ const texture = reader.readCString();
436
+ const material = reader.readCString();
437
+ return new _Face(sidesCnt, vertices, flags, texture, material);
438
+ }
439
+ /**
440
+ * Get only the used vertices (based on sidesCnt)
441
+ */
442
+ getUsedVertices() {
443
+ return this.vertices.slice(0, this.sidesCnt);
444
+ }
445
+ };
446
+
447
+ // src/mlod/TaggReader.ts
448
+ var TaggReader = class {
449
+ static readTaggs(reader, verticesLength, faces) {
450
+ const taggSignature = reader.readString(4);
451
+ if (taggSignature !== "TAGG") {
452
+ throw new Error("TAGG section expected");
453
+ }
454
+ const taggs = [];
455
+ while (reader.pos < reader.length) {
456
+ const isActive = reader.readBoolean();
457
+ if (!isActive) {
458
+ throw new Error("Deactivated TAGG encountered");
459
+ }
460
+ const taggName = reader.readCString();
461
+ const tagg = this.readTaggByName(reader, taggName, verticesLength, faces);
462
+ taggs.push(tagg);
463
+ if (tagg.name === "#EndOfFile#") {
464
+ break;
465
+ }
466
+ }
467
+ return taggs;
468
+ }
469
+ static readTaggByName(reader, taggName, verticesLength, faces) {
470
+ switch (taggName) {
471
+ case "#Animation#":
472
+ return this.readAnimationTagg(reader);
473
+ case "#Lock#":
474
+ return this.readLockTagg(reader, verticesLength, faces.length);
475
+ case "#Mass#":
476
+ return this.readMassTagg(reader);
477
+ case "#Property#":
478
+ return this.readPropertyTagg(reader);
479
+ case "#Selected#":
480
+ return this.readSelectedTagg(reader, verticesLength, faces.length);
481
+ case "#SharpEdges#":
482
+ return this.readSharpEdgesTagg(reader);
483
+ case "#UVSet#":
484
+ return this.readUvSetTagg(reader, faces);
485
+ case "#EndOfFile#":
486
+ return this.readEndOfFileTagg(reader);
487
+ default:
488
+ return this.readNamedSelectionTagg(reader, taggName, verticesLength, faces.length);
489
+ }
490
+ }
491
+ static readAnimationTagg(reader) {
492
+ const dataSize = reader.readUInt32();
493
+ const endPos = reader.pos + dataSize;
494
+ const frameTime = reader.readFloat();
495
+ const remainingBytes = endPos - reader.pos;
496
+ const frameCount = Math.max(0, Math.floor(remainingBytes / 12));
497
+ const framePoints = new Array(frameCount);
498
+ for (let i = 0; i < frameCount; i++) {
499
+ framePoints[i] = Vector3.fromReader(reader);
500
+ }
501
+ this.ensureSectionEnd(reader, endPos, "#Animation#");
502
+ return { kind: "Animation", name: "#Animation#", frameTime, framePoints };
503
+ }
504
+ static readLockTagg(reader, verticesLength, facesLength) {
505
+ const dataSize = reader.readUInt32();
506
+ const endPos = reader.pos + dataSize;
507
+ const lockedPoints = new Array(verticesLength);
508
+ for (let i = 0; i < verticesLength; i++) {
509
+ lockedPoints[i] = reader.readBoolean();
510
+ }
511
+ const lockedFaces = new Array(facesLength);
512
+ for (let i = 0; i < facesLength; i++) {
513
+ lockedFaces[i] = reader.readBoolean();
514
+ }
515
+ this.ensureSectionEnd(reader, endPos, "#Lock#");
516
+ return { kind: "Lock", name: "#Lock#", lockedPoints, lockedFaces };
517
+ }
518
+ static readMassTagg(reader) {
519
+ const dataSize = reader.readUInt32();
520
+ const endPos = reader.pos + dataSize;
521
+ const entryCount = Math.max(0, Math.floor(dataSize / 4));
522
+ const mass = new Array(entryCount);
523
+ for (let i = 0; i < entryCount; i++) {
524
+ mass[i] = reader.readFloat();
525
+ }
526
+ this.ensureSectionEnd(reader, endPos, "#Mass#");
527
+ return { kind: "Mass", name: "#Mass#", mass };
528
+ }
529
+ static readPropertyTagg(reader) {
530
+ const dataSize = reader.readUInt32();
531
+ if (dataSize !== 128) {
532
+ throw new Error(`Unexpected #Property# data size: ${dataSize}`);
533
+ }
534
+ const endPos = reader.pos + dataSize;
535
+ const propName = this.readFixedString(reader, 64);
536
+ const propValue = this.readFixedString(reader, 64);
537
+ this.ensureSectionEnd(reader, endPos, "#Property#");
538
+ return { kind: "Property", name: "#Property#", propName, propValue };
539
+ }
540
+ static readSelectedTagg(reader, verticesLength, facesLength) {
541
+ const dataSize = reader.readUInt32();
542
+ const endPos = reader.pos + dataSize;
543
+ const weightedPoints = reader.readBytes(verticesLength);
544
+ const faces = new Array(facesLength);
545
+ for (let i = 0; i < facesLength; i++) {
546
+ faces[i] = reader.readBoolean();
547
+ }
548
+ this.ensureSectionEnd(reader, endPos, "#Selected#");
549
+ return { kind: "Selected", name: "#Selected#", weightedPoints, faces };
550
+ }
551
+ static readSharpEdgesTagg(reader) {
552
+ const dataSize = reader.readUInt32();
553
+ const endPos = reader.pos + dataSize;
554
+ const pairCount = Math.max(0, Math.floor(dataSize / 8));
555
+ const pointIndices = new Array(pairCount);
556
+ for (let i = 0; i < pairCount; i++) {
557
+ pointIndices[i] = [reader.readUInt32(), reader.readUInt32()];
558
+ }
559
+ this.ensureSectionEnd(reader, endPos, "#SharpEdges#");
560
+ return { kind: "SharpEdges", name: "#SharpEdges#", pointIndices };
561
+ }
562
+ static readUvSetTagg(reader, faces) {
563
+ const dataSize = reader.readUInt32();
564
+ const endPos = reader.pos + dataSize;
565
+ const uvSetNr = reader.readUInt32();
566
+ const faceUVs = new Array(faces.length);
567
+ for (let faceIdx = 0; faceIdx < faces.length; faceIdx++) {
568
+ const vertexCount = faces[faceIdx].sidesCnt;
569
+ const uvs = new Array(vertexCount);
570
+ for (let uvIdx = 0; uvIdx < vertexCount; uvIdx++) {
571
+ uvs[uvIdx] = [reader.readFloat(), reader.readFloat()];
572
+ }
573
+ faceUVs[faceIdx] = uvs;
574
+ }
575
+ this.ensureSectionEnd(reader, endPos, "#UVSet#");
576
+ return { kind: "UVSet", name: "#UVSet#", uvSetNr, faceUVs };
577
+ }
578
+ static readNamedSelectionTagg(reader, taggName, verticesLength, facesLength) {
579
+ const dataSize = reader.readUInt32();
580
+ const endPos = reader.pos + dataSize;
581
+ const points = new Array(verticesLength);
582
+ for (let i = 0; i < verticesLength; i++) {
583
+ points[i] = reader.readBoolean();
584
+ }
585
+ const faces = new Array(facesLength);
586
+ for (let i = 0; i < facesLength; i++) {
587
+ faces[i] = reader.readBoolean();
588
+ }
589
+ this.ensureSectionEnd(reader, endPos, taggName);
590
+ return { kind: "NamedSelection", name: taggName, points, faces };
591
+ }
592
+ static readEndOfFileTagg(reader) {
593
+ const dataSize = reader.readUInt32();
594
+ const endPos = reader.pos + dataSize;
595
+ this.ensureSectionEnd(reader, endPos, "#EndOfFile#");
596
+ return { kind: "EndOfFile", name: "#EndOfFile#" };
597
+ }
598
+ static readFixedString(reader, length) {
599
+ const raw = reader.readRawString(length);
600
+ const nullIndex = raw.indexOf("\0");
601
+ return nullIndex >= 0 ? raw.slice(0, nullIndex) : raw;
602
+ }
603
+ static ensureSectionEnd(reader, expectedEnd, taggName) {
604
+ if (reader.pos !== expectedEnd) {
605
+ throw new Error(`TAGG ${taggName} length mismatch (expected end ${expectedEnd}, actual ${reader.pos})`);
606
+ }
607
+ }
608
+ };
609
+
610
+ // src/shared/Resolution.ts
611
+ function getLodName(resolution) {
612
+ if (approxEqual(resolution, 1e3)) return "View-Gunner";
613
+ if (approxEqual(resolution, 1100)) return "View-Pilot";
614
+ if ((resolution > 1200 || approxEqual(resolution, 1200)) && resolution < 1300) {
615
+ return `View-Cargo ${toFixedNumber(resolution, 1200)}`;
616
+ }
617
+ if (approxEqual(resolution, 1300)) return "View-Cargo Fire Geom. [obsolete]";
618
+ if ((resolution > 1e4 || approxEqual(resolution, 1e4)) && resolution < 2e4) {
619
+ return `ShadowVolume ${toFixedNumber(resolution, 1e4)}`;
620
+ }
621
+ if ((resolution > 2e4 || approxEqual(resolution, 2e4)) && resolution < 3e4) {
622
+ return `Edit ${toFixedNumber(resolution, 2e4)}`;
623
+ }
624
+ if (approxEqual(resolution, 1e13)) return "Geometry";
625
+ if (approxEqual(resolution, 2e13)) return "Geometry Buoyancy";
626
+ if (approxEqual(resolution, 3e13)) return "Geometry Phys Old";
627
+ if (approxEqual(resolution, 4e13)) return "Geometry Phys";
628
+ if (approxEqual(resolution, 1e15)) return "Memory";
629
+ if (approxEqual(resolution, 2e15)) return "LandContact";
630
+ if (approxEqual(resolution, 3e15)) return "RoadWay";
631
+ if (approxEqual(resolution, 4e15)) return "Paths";
632
+ if (approxEqual(resolution, 5e15)) return "Hit-Points";
633
+ if (approxEqual(resolution, 6e15)) return "View Geometry";
634
+ if (approxEqual(resolution, 7e15)) return "Fire Geometry";
635
+ if ((resolution > 8e15 || approxEqual(resolution, 8e15)) && resolution < 9e15) {
636
+ return `View-Cargo Geom. ${toFixedNumber(resolution, 8e15)}`;
637
+ }
638
+ if (approxEqual(resolution, 9e15)) return "View-Cargo Fire Geom. [obsolete]";
639
+ if (approxEqual(resolution, 1e16)) return "View-Commander [obsolete]";
640
+ if (approxEqual(resolution, 11e15)) return "View-Commander Geom. [obsolete]";
641
+ if (approxEqual(resolution, 12e15)) return "View-Commander Fire Geom. [obsolete]";
642
+ if (approxEqual(resolution, 13e15)) return "View-Pilot Geom.";
643
+ if (approxEqual(resolution, 14e15)) return "View-Pilot Fire Geom. [obsolete]";
644
+ if (approxEqual(resolution, 15e15)) return "View-Gunner Geom.";
645
+ if (approxEqual(resolution, 16e15)) return "View-Gunner Fire Geom. [obsolete]";
646
+ if (approxEqual(resolution, 17e15)) return "Sub Parts [obsolete]";
647
+ if ((resolution > 18e15 || approxEqual(resolution, 18e15)) && resolution < 19e15) {
648
+ return `ShadowVolume - View Cargo ${toFixedNumber(resolution, 18e15)}`;
649
+ }
650
+ if (approxEqual(resolution, 19e15)) return "ShadowVolume - View Pilot";
651
+ if (approxEqual(resolution, 2e16)) return "ShadowVolume - View Gunner";
652
+ if (approxEqual(resolution, 21e15)) return "Wreck";
653
+ return resolution.toFixed(3);
654
+ function toFixedNumber(value, subtract) {
655
+ return Math.max(0, value - subtract).toFixed(3);
656
+ }
657
+ function approxEqual(a, b, tolerance = 1e-5) {
658
+ if (Math.abs(a) > 1e10 || Math.abs(b) > 1e10) {
659
+ const maxVal = Math.max(Math.abs(a), Math.abs(b));
660
+ return Math.abs(a - b) <= maxVal * tolerance;
661
+ }
662
+ return Math.abs(a - b) <= tolerance;
663
+ }
664
+ }
665
+
666
+ // src/mlod/MlodLod.ts
667
+ var MlodLod = class _MlodLod {
668
+ constructor() {
669
+ this.resolution = 0;
670
+ this.flags = 0;
671
+ this.vertices = [];
672
+ this.normals = [];
673
+ this.faces = [];
674
+ this.taggs = [];
675
+ }
676
+ get resolutionName() {
677
+ return getLodName(this.resolution);
678
+ }
679
+ get verticesCount() {
680
+ return this.vertices.length;
681
+ }
682
+ get facesCount() {
683
+ return this.faces.length;
684
+ }
685
+ /**
686
+ * Get unique textures used in this LOD
687
+ */
688
+ get textures() {
689
+ const uniqueTextures = /* @__PURE__ */ new Set();
690
+ for (const face of this.faces) {
691
+ if (face.texture) {
692
+ uniqueTextures.add(face.texture);
693
+ }
694
+ }
695
+ return Array.from(uniqueTextures);
696
+ }
697
+ /**
698
+ * Get unique materials used in this LOD
699
+ */
700
+ get materials() {
701
+ const uniqueMaterials = /* @__PURE__ */ new Set();
702
+ for (const face of this.faces) {
703
+ if (face.material) {
704
+ uniqueMaterials.add(face.material);
705
+ }
706
+ }
707
+ return Array.from(uniqueMaterials);
708
+ }
709
+ get namedSelections() {
710
+ const selections = this.taggs.filter((tagg) => tagg.kind === "NamedSelection").map((tagg) => tagg.name);
711
+ return selections;
712
+ }
713
+ static fromReader(reader) {
714
+ const lod = new _MlodLod();
715
+ const signature = reader.readString(4);
716
+ if (signature !== "P3DM") {
717
+ const bytes = signature.split("").map((c) => c.charCodeAt(0).toString(16).padStart(2, "0")).join(" ");
718
+ throw new Error(`Unsupported LOD type: "${signature}" (0x${bytes}) at position ${reader.pos - 4}`);
719
+ }
720
+ const majorVersion = reader.readUInt32();
721
+ const minorVersion = reader.readUInt32();
722
+ if (majorVersion !== 28 || minorVersion !== 256) {
723
+ throw new Error(`Unknown P3DM version: ${majorVersion}.${minorVersion}`);
724
+ }
725
+ const pointsCnt = reader.readInt32();
726
+ const normalsCnt = reader.readInt32();
727
+ const facesCnt = reader.readInt32();
728
+ lod.flags = reader.readUInt32();
729
+ lod.vertices = new Array(pointsCnt);
730
+ for (let i = 0; i < pointsCnt; i++) {
731
+ lod.vertices[i] = Point.fromReader(reader);
732
+ }
733
+ lod.normals = new Array(normalsCnt);
734
+ for (let i = 0; i < normalsCnt; i++) {
735
+ lod.normals[i] = Vector3.fromReader(reader);
736
+ }
737
+ lod.faces = new Array(facesCnt);
738
+ for (let i = 0; i < facesCnt; i++) {
739
+ lod.faces[i] = Face.fromReader(reader);
740
+ }
741
+ lod.taggs = TaggReader.readTaggs(reader, lod.vertices.length, lod.faces);
742
+ lod.resolution = reader.readFloat();
743
+ return lod;
744
+ }
745
+ };
746
+
747
+ // src/mlod/Mlod.ts
748
+ var _Mlod = class _Mlod {
749
+ constructor() {
750
+ this.version = 0;
751
+ this.lods = [];
752
+ }
753
+ /**
754
+ * Read MLOD from a buffer
755
+ */
756
+ static fromBuffer(buffer) {
757
+ const reader = new BinaryReader(buffer);
758
+ return _Mlod.fromReader(reader);
759
+ }
760
+ /**
761
+ * Read MLOD from a BinaryReader
762
+ */
763
+ static fromReader(reader) {
764
+ const mlod = new _Mlod();
765
+ const signature = reader.readString(4);
766
+ if (signature !== "MLOD") {
767
+ throw new Error(`Expected MLOD signature, got: ${signature}`);
768
+ }
769
+ mlod.version = reader.readInt32();
770
+ if (mlod.version !== _Mlod.SUPPORTED_VERSION) {
771
+ throw new Error(`Unsupported MLOD version: ${mlod.version} (expected ${_Mlod.SUPPORTED_VERSION})`);
772
+ }
773
+ const lodCount = reader.readInt32();
774
+ mlod.lods = new Array(lodCount);
775
+ for (let i = 0; i < lodCount; i++) {
776
+ mlod.lods[i] = MlodLod.fromReader(reader);
777
+ }
778
+ return mlod;
779
+ }
780
+ /**
781
+ * Get all unique textures across all LODs
782
+ */
783
+ get allTextures() {
784
+ const textures = /* @__PURE__ */ new Set();
785
+ for (const lod of this.lods) {
786
+ for (const texture of lod.textures) {
787
+ textures.add(texture);
788
+ }
789
+ }
790
+ return Array.from(textures);
791
+ }
792
+ /**
793
+ * Get all unique materials across all LODs
794
+ */
795
+ get allMaterials() {
796
+ const materials = /* @__PURE__ */ new Set();
797
+ for (const lod of this.lods) {
798
+ for (const material of lod.materials) {
799
+ materials.add(material);
800
+ }
801
+ }
802
+ return Array.from(materials);
803
+ }
804
+ /**
805
+ * Get statistics about the model
806
+ */
807
+ getStats() {
808
+ return {
809
+ version: this.version,
810
+ lodCount: this.lods.length,
811
+ totalVertices: this.lods.reduce((sum, lod) => sum + lod.vertices.length, 0),
812
+ totalFaces: this.lods.reduce((sum, lod) => sum + lod.faces.length, 0),
813
+ textures: this.allTextures,
814
+ materials: this.allMaterials
815
+ };
816
+ }
817
+ };
818
+ _Mlod.SUPPORTED_VERSION = 257;
819
+ var Mlod = _Mlod;
820
+
821
+ // src/mlod/FaceFlags.ts
822
+ var FaceFlags = /* @__PURE__ */ ((FaceFlags2) => {
823
+ FaceFlags2[FaceFlags2["None"] = 0] = "None";
824
+ FaceFlags2[FaceFlags2["FlatShaded"] = 1] = "FlatShaded";
825
+ FaceFlags2[FaceFlags2["UserValue"] = 128] = "UserValue";
826
+ return FaceFlags2;
827
+ })(FaceFlags || {});
828
+
829
+ // src/odol/index.ts
830
+ var odol_exports = {};
831
+ __export(odol_exports, {
832
+ Animation: () => Animation,
833
+ AnimationRTPair: () => AnimationRTPair,
834
+ AnimationRTWeight: () => AnimationRTWeight,
835
+ AnimationType: () => AnimationType,
836
+ Animations: () => Animations,
837
+ ClipFlags: () => ClipFlags,
838
+ Color: () => Color,
839
+ EmbeddedMaterial: () => EmbeddedMaterial,
840
+ Face: () => Face2,
841
+ Keyframe: () => Keyframe,
842
+ LodMetadata: () => LodMetadata,
843
+ MapType: () => MapType,
844
+ Matrix3: () => Matrix3,
845
+ Matrix4: () => Matrix4,
846
+ ModelInfo: () => ModelInfo,
847
+ NamedSelection: () => NamedSelection,
848
+ Odol: () => Odol,
849
+ OdolLod: () => OdolLod,
850
+ OdolReader: () => OdolReader,
851
+ PackedColor: () => PackedColor,
852
+ Polygons: () => Polygons,
853
+ ProxyObject: () => ProxyObject,
854
+ STPair: () => STPair,
855
+ Section: () => Section,
856
+ ShadowBufferSource: () => ShadowBufferSource,
857
+ Skeleton: () => Skeleton,
858
+ SpecialFlags: () => SpecialFlags,
859
+ StageTexture: () => StageTexture,
860
+ StageTransform: () => StageTransform,
861
+ SubSkeletonIndexSet: () => SubSkeletonIndexSet,
862
+ UVSet: () => UVSet,
863
+ Vector3: () => Vector32,
864
+ VertexData: () => VertexData,
865
+ VertexNeighborInfo: () => VertexNeighborInfo,
866
+ getLodName: () => getLodName
867
+ });
868
+
869
+ // src/odol/math.ts
870
+ var Vector32 = class _Vector3 {
871
+ constructor(x = 0, y = 0, z = 0) {
872
+ this.x = x;
873
+ this.y = y;
874
+ this.z = z;
875
+ }
876
+ get length() {
877
+ return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
878
+ }
879
+ static fromReader(reader) {
880
+ return new _Vector3(
881
+ reader.readFloat(),
882
+ reader.readFloat(),
883
+ reader.readFloat()
884
+ );
885
+ }
886
+ static readCompressed(reader) {
887
+ const compressed = reader.readInt32();
888
+ return _Vector3.fromInt32(compressed);
889
+ }
890
+ static fromInt32(compressed) {
891
+ const scaleFactor = -1 / 511;
892
+ let x = compressed & 1023;
893
+ let y = compressed >> 10 & 1023;
894
+ let z = compressed >> 20 & 1023;
895
+ if (x > 511) x -= 1024;
896
+ if (y > 511) y -= 1024;
897
+ if (z > 511) z -= 1024;
898
+ return new _Vector3(
899
+ x * scaleFactor,
900
+ y * scaleFactor,
901
+ z * scaleFactor
902
+ );
903
+ }
904
+ distance(v) {
905
+ const dx = this.x - v.x;
906
+ const dy = this.y - v.y;
907
+ const dz = this.z - v.z;
908
+ return Math.sqrt(dx * dx + dy * dy + dz * dz);
909
+ }
910
+ normalize() {
911
+ const len = this.length;
912
+ if (len === 0) return new _Vector3(0, 0, 0);
913
+ return new _Vector3(this.x / len, this.y / len, this.z / len);
914
+ }
915
+ toArray() {
916
+ return [this.x, this.y, this.z];
917
+ }
918
+ };
919
+ var Matrix3 = class _Matrix3 {
920
+ constructor(m00 = 0, m01 = 0, m02 = 0, m10 = 0, m11 = 0, m12 = 0, m20 = 0, m21 = 0, m22 = 0) {
921
+ this.m00 = m00;
922
+ this.m01 = m01;
923
+ this.m02 = m02;
924
+ this.m10 = m10;
925
+ this.m11 = m11;
926
+ this.m12 = m12;
927
+ this.m20 = m20;
928
+ this.m21 = m21;
929
+ this.m22 = m22;
930
+ }
931
+ static fromReader(reader) {
932
+ return new _Matrix3(
933
+ reader.readFloat(),
934
+ reader.readFloat(),
935
+ reader.readFloat(),
936
+ reader.readFloat(),
937
+ reader.readFloat(),
938
+ reader.readFloat(),
939
+ reader.readFloat(),
940
+ reader.readFloat(),
941
+ reader.readFloat()
942
+ );
943
+ }
944
+ };
945
+ var Matrix4 = class _Matrix4 {
946
+ // 3x3 orientation matrix
947
+ constructor(m00 = 0, m01 = 0, m02 = 0, m10 = 0, m11 = 0, m12 = 0, m20 = 0, m21 = 0, m22 = 0, px = 0, py = 0, pz = 0) {
948
+ this.m00 = m00;
949
+ this.m01 = m01;
950
+ this.m02 = m02;
951
+ this.m10 = m10;
952
+ this.m11 = m11;
953
+ this.m12 = m12;
954
+ this.m20 = m20;
955
+ this.m21 = m21;
956
+ this.m22 = m22;
957
+ this.px = px;
958
+ this.py = py;
959
+ this.pz = pz;
960
+ }
961
+ static fromReader(reader) {
962
+ return new _Matrix4(
963
+ reader.readFloat(),
964
+ reader.readFloat(),
965
+ reader.readFloat(),
966
+ reader.readFloat(),
967
+ reader.readFloat(),
968
+ reader.readFloat(),
969
+ reader.readFloat(),
970
+ reader.readFloat(),
971
+ reader.readFloat(),
972
+ reader.readFloat(),
973
+ reader.readFloat(),
974
+ reader.readFloat()
975
+ );
976
+ }
977
+ };
978
+
979
+ // src/odol/Animation.ts
980
+ var Animation = class {
981
+ constructor() {
982
+ this.kind = 0;
983
+ this.animName = "";
984
+ this.source = "";
985
+ this.minValue = 0;
986
+ this.maxValue = 0;
987
+ this.minPhase = 0;
988
+ this.maxPhase = 0;
989
+ this.sourceAddress = 0;
990
+ }
991
+ read(reader, kind) {
992
+ this.kind = kind;
993
+ this.animName = reader.readCString();
994
+ this.source = reader.readCString();
995
+ this.minPhase = reader.readFloat();
996
+ this.maxPhase = reader.readFloat();
997
+ this.minValue = reader.readFloat();
998
+ this.maxValue = reader.readFloat();
999
+ this.sourceAddress = reader.readUInt32();
1000
+ }
1001
+ };
1002
+ var AnimationRotation = class _AnimationRotation extends Animation {
1003
+ constructor() {
1004
+ super(...arguments);
1005
+ this.angle0 = 0;
1006
+ this.angle1 = 0;
1007
+ }
1008
+ static fromReader(reader, kind) {
1009
+ const anim = new _AnimationRotation();
1010
+ anim.read(reader, kind);
1011
+ anim.angle0 = reader.readFloat();
1012
+ anim.angle1 = reader.readFloat();
1013
+ return anim;
1014
+ }
1015
+ };
1016
+ var AnimationTranslation = class _AnimationTranslation extends Animation {
1017
+ constructor() {
1018
+ super(...arguments);
1019
+ this.offset0 = 0;
1020
+ this.offset1 = 0;
1021
+ }
1022
+ static fromReader(reader, kind) {
1023
+ const anim = new _AnimationTranslation();
1024
+ anim.read(reader, kind);
1025
+ anim.offset0 = reader.readFloat();
1026
+ anim.offset1 = reader.readFloat();
1027
+ return anim;
1028
+ }
1029
+ };
1030
+ var AnimationDirect = class _AnimationDirect extends Animation {
1031
+ constructor() {
1032
+ super(...arguments);
1033
+ this.axisPos = new Vector32();
1034
+ this.axisDir = new Vector32();
1035
+ this.angle = 0;
1036
+ this.axisOffset = 0;
1037
+ }
1038
+ static fromReader(reader, kind) {
1039
+ const anim = new _AnimationDirect();
1040
+ anim.read(reader, kind);
1041
+ anim.axisPos = Vector32.fromReader(reader);
1042
+ anim.axisDir = Vector32.fromReader(reader);
1043
+ anim.angle = reader.readFloat();
1044
+ anim.axisOffset = reader.readFloat();
1045
+ return anim;
1046
+ }
1047
+ };
1048
+ var AnimationHide = class _AnimationHide extends Animation {
1049
+ constructor() {
1050
+ super(...arguments);
1051
+ this.hideValue = 0;
1052
+ }
1053
+ static fromReader(reader, kind) {
1054
+ const anim = new _AnimationHide();
1055
+ anim.read(reader, kind);
1056
+ anim.hideValue = reader.readFloat();
1057
+ return anim;
1058
+ }
1059
+ };
1060
+ function isAnimationDirect(anim) {
1061
+ return anim.kind === 8 /* Direct */;
1062
+ }
1063
+ function isAnimationHide(anim) {
1064
+ return anim.kind === 9 /* Hide */;
1065
+ }
1066
+
1067
+ // src/odol/Animations.ts
1068
+ var Animations = class _Animations {
1069
+ constructor() {
1070
+ this.animations = [];
1071
+ this.bonesToAnims = [];
1072
+ this.animsToBones = [];
1073
+ }
1074
+ static fromReader(reader) {
1075
+ const animations = new _Animations();
1076
+ const animationCount = reader.readInt32();
1077
+ if (animationCount < 0 || animationCount > 1e4) {
1078
+ throw new Error(`Invalid animations count: ${animationCount}`);
1079
+ }
1080
+ animations.animations = new Array(animationCount);
1081
+ for (let i = 0; i < animationCount; i++) {
1082
+ const kind = reader.readUInt32();
1083
+ switch (kind) {
1084
+ case 0 /* Rotation */:
1085
+ case 1 /* RotationX */:
1086
+ case 2 /* RotationY */:
1087
+ case 3 /* RotationZ */:
1088
+ animations.animations[i] = AnimationRotation.fromReader(reader, kind);
1089
+ break;
1090
+ case 4 /* Translation */:
1091
+ case 5 /* TranslationX */:
1092
+ case 6 /* TranslationY */:
1093
+ case 7 /* TranslationZ */:
1094
+ animations.animations[i] = AnimationTranslation.fromReader(reader, kind);
1095
+ break;
1096
+ case 8 /* Direct */:
1097
+ animations.animations[i] = AnimationDirect.fromReader(reader, kind);
1098
+ break;
1099
+ case 9 /* Hide */:
1100
+ animations.animations[i] = AnimationHide.fromReader(reader, kind);
1101
+ break;
1102
+ default:
1103
+ throw new Error(`Unknown AnimType encountered: ${kind}`);
1104
+ }
1105
+ }
1106
+ const animLodsCount = reader.readInt32();
1107
+ animations.bonesToAnims = new Array(animLodsCount);
1108
+ for (let animLodIdx = 0; animLodIdx < animLodsCount; animLodIdx++) {
1109
+ const length = reader.readUInt32();
1110
+ animations.bonesToAnims[animLodIdx] = new Array(length);
1111
+ for (let i = 0; i < length; i++) {
1112
+ const length2 = reader.readUInt32();
1113
+ animations.bonesToAnims[animLodIdx][i] = new Array(length2);
1114
+ for (let j = 0; j < length2; j++) {
1115
+ animations.bonesToAnims[animLodIdx][i][j] = reader.readUInt32();
1116
+ }
1117
+ }
1118
+ }
1119
+ animations.animsToBones = new Array(animLodsCount);
1120
+ for (let lodIdx = 0; lodIdx < animLodsCount; lodIdx++) {
1121
+ animations.animsToBones[lodIdx] = new Array(animationCount);
1122
+ for (let animIdx = 0; animIdx < animationCount; animIdx++) {
1123
+ const boneIdx = reader.readInt32();
1124
+ if (boneIdx !== -1 && !isAnimationHide(animations.animations[animIdx]) && !isAnimationDirect(animations.animations[animIdx])) {
1125
+ const axisData = [
1126
+ Vector32.fromReader(reader),
1127
+ Vector32.fromReader(reader)
1128
+ ];
1129
+ animations.animsToBones[lodIdx][animIdx] = { boneIdx, axisData };
1130
+ } else {
1131
+ animations.animsToBones[lodIdx][animIdx] = { boneIdx };
1132
+ }
1133
+ }
1134
+ }
1135
+ return animations;
1136
+ }
1137
+ };
1138
+
1139
+ // src/odol/OdolReader.ts
1140
+ var OdolReader = class _OdolReader extends BinaryReader {
1141
+ constructor() {
1142
+ super(...arguments);
1143
+ this.version = 0;
1144
+ }
1145
+ /**
1146
+ * Read an array of elements
1147
+ */
1148
+ readArray(readElement, size) {
1149
+ const count = size ?? this.readInt32();
1150
+ const array = new Array(count);
1151
+ for (let i = 0; i < count; i++) {
1152
+ array[i] = readElement(this);
1153
+ }
1154
+ return array;
1155
+ }
1156
+ /**
1157
+ * Read a compressed array of elements
1158
+ */
1159
+ readCompressedArray(readElement, elemSize) {
1160
+ const size = this.readInt32();
1161
+ const compressed = this.readCompressed(size * elemSize);
1162
+ const tempReader = new _OdolReader(compressed);
1163
+ tempReader.version = this.version;
1164
+ const array = new Array(size);
1165
+ for (let i = 0; i < size; i++) {
1166
+ array[i] = readElement(tempReader);
1167
+ }
1168
+ return array;
1169
+ }
1170
+ /**
1171
+ * Read a compressed fill array (optimized for arrays with many repeated values)
1172
+ */
1173
+ readCompressedFillArray(readElement, sizeOfT) {
1174
+ const size = this.readInt32();
1175
+ const isFilled = this.readBoolean();
1176
+ if (isFilled) {
1177
+ const value = readElement(this);
1178
+ return new Array(size).fill(value);
1179
+ }
1180
+ const compressed = this.readCompressed(size * sizeOfT);
1181
+ const tempReader = new _OdolReader(compressed);
1182
+ tempReader.version = this.version;
1183
+ const array = new Array(size);
1184
+ for (let i = 0; i < size; i++) {
1185
+ array[i] = readElement(tempReader);
1186
+ }
1187
+ return array;
1188
+ }
1189
+ /**
1190
+ * Read compressed data
1191
+ */
1192
+ readCompressed(expectedSize) {
1193
+ if (expectedSize === 0) {
1194
+ return new Uint8Array(0);
1195
+ }
1196
+ const useCompression = expectedSize >= 1024;
1197
+ if (!useCompression) {
1198
+ return this.readBytes(expectedSize);
1199
+ }
1200
+ const worstCompressedSize = expectedSize + Math.floor(expectedSize / 64) + 16 + 3 + 4;
1201
+ const remainingBytes = this.length - this.pos;
1202
+ const bytesToRead = Math.min(worstCompressedSize, remainingBytes);
1203
+ const compressedData = this.readBytes(bytesToRead);
1204
+ const result = lzoDecompressWithSize(compressedData, expectedSize);
1205
+ const bytesNotConsumed = compressedData.length - result.bytesRead;
1206
+ if (bytesNotConsumed > 0) {
1207
+ this.seek(-bytesNotConsumed, "current");
1208
+ }
1209
+ return result.data;
1210
+ }
1211
+ /**
1212
+ * Read compressed vertex index array
1213
+ */
1214
+ readCompressedVertexIndexArray() {
1215
+ return this.readCompressedArray((reader) => reader.readUInt16(), 2);
1216
+ }
1217
+ readCompressedVector3() {
1218
+ const compressed = this.readInt32();
1219
+ return Vector32.fromInt32(compressed);
1220
+ }
1221
+ };
1222
+
1223
+ // src/odol/enums.ts
1224
+ var ClipFlags = /* @__PURE__ */ ((ClipFlags2) => {
1225
+ ClipFlags2[ClipFlags2["DecalNone"] = 0] = "DecalNone";
1226
+ ClipFlags2[ClipFlags2["FogNormal"] = 0] = "FogNormal";
1227
+ ClipFlags2[ClipFlags2["LandNone"] = 0] = "LandNone";
1228
+ ClipFlags2[ClipFlags2["LightNormal"] = 0] = "LightNormal";
1229
+ ClipFlags2[ClipFlags2["None"] = 0] = "None";
1230
+ ClipFlags2[ClipFlags2["Front"] = 1] = "Front";
1231
+ ClipFlags2[ClipFlags2["Back"] = 2] = "Back";
1232
+ ClipFlags2[ClipFlags2["Left"] = 4] = "Left";
1233
+ ClipFlags2[ClipFlags2["Right"] = 8] = "Right";
1234
+ ClipFlags2[ClipFlags2["Bottom"] = 16] = "Bottom";
1235
+ ClipFlags2[ClipFlags2["Top"] = 32] = "Top";
1236
+ ClipFlags2[ClipFlags2["All"] = 63] = "All";
1237
+ ClipFlags2[ClipFlags2["User0"] = 64] = "User0";
1238
+ ClipFlags2[ClipFlags2["MaxUserValue"] = 255] = "MaxUserValue";
1239
+ ClipFlags2[ClipFlags2["LandOn"] = 256] = "LandOn";
1240
+ ClipFlags2[ClipFlags2["LandAbove"] = 512] = "LandAbove";
1241
+ ClipFlags2[ClipFlags2["LandUnder"] = 1024] = "LandUnder";
1242
+ ClipFlags2[ClipFlags2["LandKeep"] = 2048] = "LandKeep";
1243
+ ClipFlags2[ClipFlags2["LandMask"] = 3328] = "LandMask";
1244
+ ClipFlags2[ClipFlags2["DecalNormal"] = 4096] = "DecalNormal";
1245
+ ClipFlags2[ClipFlags2["DecalVertical"] = 8192] = "DecalVertical";
1246
+ ClipFlags2[ClipFlags2["DecalMask"] = 12288] = "DecalMask";
1247
+ ClipFlags2[ClipFlags2["FogDisable"] = 16384] = "FogDisable";
1248
+ ClipFlags2[ClipFlags2["FogSky"] = 32768] = "FogSky";
1249
+ ClipFlags2[ClipFlags2["FogMask"] = 49152] = "FogMask";
1250
+ ClipFlags2[ClipFlags2["UserStep"] = 1048576] = "UserStep";
1251
+ ClipFlags2[ClipFlags2["UserMask"] = 267386880] = "UserMask";
1252
+ ClipFlags2[ClipFlags2["Hints"] = 268435456] = "Hints";
1253
+ ClipFlags2[ClipFlags2["Shining"] = 209715200] = "Shining";
1254
+ ClipFlags2[ClipFlags2["AlwaysInShadow"] = 210763776] = "AlwaysInShadow";
1255
+ ClipFlags2[ClipFlags2["HalfLight"] = 211812352] = "HalfLight";
1256
+ ClipFlags2[ClipFlags2["FullLight"] = 212860928] = "FullLight";
1257
+ return ClipFlags2;
1258
+ })(ClipFlags || {});
1259
+ var SpecialFlags = /* @__PURE__ */ ((SpecialFlags2) => {
1260
+ SpecialFlags2[SpecialFlags2["None"] = 0] = "None";
1261
+ SpecialFlags2[SpecialFlags2["Unk_2"] = 2] = "Unk_2";
1262
+ SpecialFlags2[SpecialFlags2["Unk_16"] = 16] = "Unk_16";
1263
+ SpecialFlags2[SpecialFlags2["NoShadows"] = 32] = "NoShadows";
1264
+ SpecialFlags2[SpecialFlags2["Unk_256"] = 256] = "Unk_256";
1265
+ SpecialFlags2[SpecialFlags2["Unk_512"] = 512] = "Unk_512";
1266
+ SpecialFlags2[SpecialFlags2["Unk_4096"] = 4096] = "Unk_4096";
1267
+ SpecialFlags2[SpecialFlags2["SectionSpecial"] = 8192] = "SectionSpecial";
1268
+ SpecialFlags2[SpecialFlags2["Unk_16384"] = 16384] = "Unk_16384";
1269
+ SpecialFlags2[SpecialFlags2["Unk_32768"] = 32768] = "Unk_32768";
1270
+ SpecialFlags2[SpecialFlags2["Unk_131072"] = 131072] = "Unk_131072";
1271
+ SpecialFlags2[SpecialFlags2["HiddenVertex"] = 4202496] = "HiddenVertex";
1272
+ SpecialFlags2[SpecialFlags2["ZBiasLow"] = 67108864] = "ZBiasLow";
1273
+ SpecialFlags2[SpecialFlags2["ZBiasMiddle"] = 134217728] = "ZBiasMiddle";
1274
+ SpecialFlags2[SpecialFlags2["ZBiasHigh"] = 201326592] = "ZBiasHigh";
1275
+ return SpecialFlags2;
1276
+ })(SpecialFlags || {});
1277
+ var ShadowBufferSource = /* @__PURE__ */ ((ShadowBufferSource2) => {
1278
+ ShadowBufferSource2[ShadowBufferSource2["Visual"] = 0] = "Visual";
1279
+ ShadowBufferSource2[ShadowBufferSource2["ShadowVolume"] = 1] = "ShadowVolume";
1280
+ ShadowBufferSource2[ShadowBufferSource2["Explicit"] = 2] = "Explicit";
1281
+ ShadowBufferSource2[ShadowBufferSource2["None"] = 3] = "None";
1282
+ ShadowBufferSource2[ShadowBufferSource2["VisualEx"] = 4] = "VisualEx";
1283
+ return ShadowBufferSource2;
1284
+ })(ShadowBufferSource || {});
1285
+ var MapType = /* @__PURE__ */ ((MapType2) => {
1286
+ MapType2[MapType2["Tree"] = 0] = "Tree";
1287
+ MapType2[MapType2["SmallTree"] = 1] = "SmallTree";
1288
+ MapType2[MapType2["Bush"] = 2] = "Bush";
1289
+ MapType2[MapType2["Building"] = 3] = "Building";
1290
+ MapType2[MapType2["House"] = 4] = "House";
1291
+ MapType2[MapType2["ForestBorder"] = 5] = "ForestBorder";
1292
+ MapType2[MapType2["ForestTriangle"] = 6] = "ForestTriangle";
1293
+ MapType2[MapType2["ForestSquare"] = 7] = "ForestSquare";
1294
+ MapType2[MapType2["Church"] = 8] = "Church";
1295
+ MapType2[MapType2["Chapel"] = 9] = "Chapel";
1296
+ MapType2[MapType2["Cross"] = 10] = "Cross";
1297
+ MapType2[MapType2["Rock"] = 11] = "Rock";
1298
+ MapType2[MapType2["Bunker"] = 12] = "Bunker";
1299
+ MapType2[MapType2["Fortress"] = 13] = "Fortress";
1300
+ MapType2[MapType2["Fountain"] = 14] = "Fountain";
1301
+ MapType2[MapType2["ViewTower"] = 15] = "ViewTower";
1302
+ MapType2[MapType2["Lighthouse"] = 16] = "Lighthouse";
1303
+ MapType2[MapType2["Quay"] = 17] = "Quay";
1304
+ MapType2[MapType2["Fuelstation"] = 18] = "Fuelstation";
1305
+ MapType2[MapType2["Hospital"] = 19] = "Hospital";
1306
+ MapType2[MapType2["Fence"] = 20] = "Fence";
1307
+ MapType2[MapType2["Wall"] = 21] = "Wall";
1308
+ MapType2[MapType2["Hide"] = 22] = "Hide";
1309
+ MapType2[MapType2["BusStop"] = 23] = "BusStop";
1310
+ MapType2[MapType2["Road"] = 24] = "Road";
1311
+ MapType2[MapType2["Forest"] = 25] = "Forest";
1312
+ MapType2[MapType2["Transmitter"] = 26] = "Transmitter";
1313
+ MapType2[MapType2["Stack"] = 27] = "Stack";
1314
+ MapType2[MapType2["Ruin"] = 28] = "Ruin";
1315
+ MapType2[MapType2["Tourism"] = 29] = "Tourism";
1316
+ MapType2[MapType2["Watertower"] = 30] = "Watertower";
1317
+ MapType2[MapType2["Track"] = 31] = "Track";
1318
+ MapType2[MapType2["MainRoad"] = 32] = "MainRoad";
1319
+ MapType2[MapType2["Rocks"] = 33] = "Rocks";
1320
+ MapType2[MapType2["PowerLines"] = 34] = "PowerLines";
1321
+ MapType2[MapType2["RailWay"] = 35] = "RailWay";
1322
+ return MapType2;
1323
+ })(MapType || {});
1324
+ var AnimationType = /* @__PURE__ */ ((AnimationType2) => {
1325
+ AnimationType2[AnimationType2["None"] = 0] = "None";
1326
+ AnimationType2[AnimationType2["Software"] = 1] = "Software";
1327
+ AnimationType2[AnimationType2["Hardware"] = 2] = "Hardware";
1328
+ return AnimationType2;
1329
+ })(AnimationType || {});
1330
+
1331
+ // src/odol/PackedColor.ts
1332
+ var PackedColor = class _PackedColor {
1333
+ constructor(value = 0) {
1334
+ this.value = value;
1335
+ }
1336
+ static fromReader(reader) {
1337
+ return new _PackedColor(reader.readUInt32());
1338
+ }
1339
+ get r() {
1340
+ return this.value >> 0 & 255;
1341
+ }
1342
+ get g() {
1343
+ return this.value >> 8 & 255;
1344
+ }
1345
+ get b() {
1346
+ return this.value >> 16 & 255;
1347
+ }
1348
+ get a() {
1349
+ return this.value >> 24 & 255;
1350
+ }
1351
+ toArray() {
1352
+ return [this.r, this.g, this.b, this.a];
1353
+ }
1354
+ };
1355
+
1356
+ // src/odol/auxiliaryStructures.ts
1357
+ var Skeleton = class _Skeleton {
1358
+ constructor() {
1359
+ this.name = "";
1360
+ this.isDiscrete = false;
1361
+ this.bones = [];
1362
+ this.pivotsName = "";
1363
+ }
1364
+ static fromReader(reader) {
1365
+ const skeleton = new _Skeleton();
1366
+ skeleton.name = reader.readCString();
1367
+ if (skeleton.name === "") {
1368
+ return skeleton;
1369
+ }
1370
+ skeleton.isDiscrete = reader.readBoolean();
1371
+ const bonesCnt = reader.readInt32();
1372
+ skeleton.bones = new Array(bonesCnt * 2);
1373
+ for (let i = 0; i < bonesCnt; i++) {
1374
+ skeleton.bones[i * 2] = reader.readCString();
1375
+ skeleton.bones[i * 2 + 1] = reader.readCString();
1376
+ }
1377
+ skeleton.pivotsName = reader.readCString();
1378
+ return skeleton;
1379
+ }
1380
+ getBonePairs() {
1381
+ const pairs = [];
1382
+ for (let i = 0; i < this.bones.length; i += 2) {
1383
+ pairs.push([this.bones[i], this.bones[i + 1]]);
1384
+ }
1385
+ return pairs;
1386
+ }
1387
+ };
1388
+ var ProxyObject = class _ProxyObject {
1389
+ constructor() {
1390
+ this.proxyModel = "";
1391
+ this.transformation = new Matrix4();
1392
+ this.sequenceId = 0;
1393
+ this.namedSelectionIndex = 0;
1394
+ this.boneIndex = 0;
1395
+ this.sectionIndex = 0;
1396
+ }
1397
+ static fromReader(reader) {
1398
+ const proxy = new _ProxyObject();
1399
+ proxy.proxyModel = reader.readCString();
1400
+ proxy.transformation = Matrix4.fromReader(reader);
1401
+ proxy.sequenceId = reader.readInt32();
1402
+ proxy.namedSelectionIndex = reader.readInt32();
1403
+ proxy.boneIndex = reader.readInt32();
1404
+ proxy.sectionIndex = reader.readInt32();
1405
+ return proxy;
1406
+ }
1407
+ };
1408
+ var Keyframe = class _Keyframe {
1409
+ constructor() {
1410
+ this.time = 0;
1411
+ this.points = [];
1412
+ }
1413
+ static fromReader(reader) {
1414
+ const keyframe = new _Keyframe();
1415
+ keyframe.time = reader.readFloat();
1416
+ keyframe.points = reader.readArray((r) => Vector32.fromReader(r));
1417
+ return keyframe;
1418
+ }
1419
+ };
1420
+ var SubSkeletonIndexSet = class _SubSkeletonIndexSet {
1421
+ constructor() {
1422
+ this.subSkeletons = [];
1423
+ }
1424
+ static fromReader(reader) {
1425
+ const set = new _SubSkeletonIndexSet();
1426
+ set.subSkeletons = reader.readArray((r) => r.readInt32());
1427
+ return set;
1428
+ }
1429
+ };
1430
+ var LodMetadata = class _LodMetadata {
1431
+ constructor() {
1432
+ this.facesCount = 0;
1433
+ this.color = new PackedColor();
1434
+ this.special = 0 /* None */;
1435
+ this.orHints = 0 /* None */;
1436
+ this.hasSkeleton = false;
1437
+ this.verticesCount = 0;
1438
+ this.faceArea = 0;
1439
+ }
1440
+ static fromReader(reader) {
1441
+ const info = new _LodMetadata();
1442
+ info.facesCount = reader.readInt32();
1443
+ info.color = PackedColor.fromReader(reader);
1444
+ info.special = reader.readInt32();
1445
+ info.orHints = reader.readUInt32();
1446
+ info.hasSkeleton = reader.readBoolean();
1447
+ info.verticesCount = reader.readInt32();
1448
+ info.faceArea = reader.readFloat();
1449
+ return info;
1450
+ }
1451
+ };
1452
+
1453
+ // src/odol/Materials.ts
1454
+ var Color = class _Color {
1455
+ constructor() {
1456
+ this.r = 0;
1457
+ this.g = 0;
1458
+ this.b = 0;
1459
+ this.a = 0;
1460
+ }
1461
+ static fromReader(reader) {
1462
+ const color = new _Color();
1463
+ color.r = reader.readFloat();
1464
+ color.g = reader.readFloat();
1465
+ color.b = reader.readFloat();
1466
+ color.a = reader.readFloat();
1467
+ return color;
1468
+ }
1469
+ };
1470
+ var StageTexture = class _StageTexture {
1471
+ constructor() {
1472
+ this.textureFilter = 0;
1473
+ // TextureFilterType enum
1474
+ this.texture = "";
1475
+ this.stageId = 0;
1476
+ this.useWorldEnvMap = false;
1477
+ }
1478
+ static fromReader(reader) {
1479
+ const stage = new _StageTexture();
1480
+ stage.textureFilter = reader.readUInt32();
1481
+ stage.texture = reader.readCString();
1482
+ stage.stageId = reader.readUInt32();
1483
+ stage.useWorldEnvMap = reader.readBoolean();
1484
+ return stage;
1485
+ }
1486
+ };
1487
+ var StageTransform = class _StageTransform {
1488
+ constructor() {
1489
+ this.uvSource = 0;
1490
+ // UVSource enum
1491
+ this.transformation = new Matrix4();
1492
+ }
1493
+ static fromReader(reader) {
1494
+ const transform = new _StageTransform();
1495
+ transform.uvSource = reader.readUInt32();
1496
+ transform.transformation = Matrix4.fromReader(reader);
1497
+ return transform;
1498
+ }
1499
+ };
1500
+ var EmbeddedMaterial = class _EmbeddedMaterial {
1501
+ constructor() {
1502
+ this.materialName = "";
1503
+ this.version = 0;
1504
+ // Color properties (RGBA floats)
1505
+ this.emissive = new Color();
1506
+ this.ambient = new Color();
1507
+ this.diffuse = new Color();
1508
+ this.forcedDiffuse = new Color();
1509
+ this.specular = new Color();
1510
+ this.emissive2 = new Color();
1511
+ this.specular2 = new Color();
1512
+ this.unkCol1 = new Color();
1513
+ this.unkCol2 = new Color();
1514
+ // Material properties
1515
+ this.specularPower = 0;
1516
+ this.pixelShaderId = 0;
1517
+ this.vertexShaderId = 0;
1518
+ this.mainLight = 0;
1519
+ this.fogMode = 0;
1520
+ this.surfaceFile = "";
1521
+ this.nRenderFlags = 0;
1522
+ this.renderFlags = 0;
1523
+ // Stage data
1524
+ this.stageTextures = [];
1525
+ this.stageTransforms = [];
1526
+ this.stageTI = null;
1527
+ // Unknown fields
1528
+ this.unk2 = 0;
1529
+ this.unk3 = 0;
1530
+ }
1531
+ static fromReader(reader) {
1532
+ const material = new _EmbeddedMaterial();
1533
+ material.materialName = reader.readCString();
1534
+ material.version = reader.readUInt32();
1535
+ material.emissive = Color.fromReader(reader);
1536
+ material.ambient = Color.fromReader(reader);
1537
+ material.diffuse = Color.fromReader(reader);
1538
+ material.forcedDiffuse = Color.fromReader(reader);
1539
+ material.specular = Color.fromReader(reader);
1540
+ material.emissive2 = Color.fromReader(reader);
1541
+ material.unkCol1 = Color.fromReader(reader);
1542
+ material.specular2 = Color.fromReader(reader);
1543
+ material.specularPower = reader.readFloat();
1544
+ material.unk2 = reader.readInt32();
1545
+ material.unk3 = reader.readInt32();
1546
+ material.unkCol2 = Color.fromReader(reader);
1547
+ if (material.version >= 20) {
1548
+ const _unk1 = reader.readInt32();
1549
+ const _unk2 = reader.readInt32();
1550
+ const _unk3 = reader.readInt32();
1551
+ const _unk4 = reader.readFloat();
1552
+ const _unk5 = reader.readFloat();
1553
+ const _unk6 = reader.readInt32();
1554
+ const _unk7 = reader.readInt32();
1555
+ const _unk8 = reader.readInt32();
1556
+ const _unk9 = reader.readFloat();
1557
+ const _unk10 = reader.readInt32();
1558
+ const _unk11 = reader.readInt32();
1559
+ const _unk12 = reader.readInt32();
1560
+ }
1561
+ material.pixelShaderId = reader.readUInt32();
1562
+ material.vertexShaderId = reader.readUInt32();
1563
+ material.mainLight = reader.readUInt32();
1564
+ material.fogMode = reader.readUInt32();
1565
+ material.surfaceFile = reader.readCString();
1566
+ material.nRenderFlags = reader.readUInt32();
1567
+ material.renderFlags = reader.readUInt32();
1568
+ const nStages = reader.readInt32();
1569
+ const nTexGens = reader.readInt32();
1570
+ material.stageTextures = new Array(nStages);
1571
+ for (let i = 0; i < nStages; i++) {
1572
+ material.stageTextures[i] = StageTexture.fromReader(reader);
1573
+ }
1574
+ material.stageTransforms = new Array(nTexGens);
1575
+ for (let i = 0; i < nTexGens; i++) {
1576
+ material.stageTransforms[i] = StageTransform.fromReader(reader);
1577
+ }
1578
+ material.stageTI = StageTexture.fromReader(reader);
1579
+ return material;
1580
+ }
1581
+ };
1582
+
1583
+ // src/odol/structures.ts
1584
+ var Face2 = class _Face {
1585
+ constructor() {
1586
+ this.vertexIndices = [];
1587
+ this.size = 0;
1588
+ }
1589
+ static fromReader(reader) {
1590
+ const face = new _Face();
1591
+ const vertexCount = reader.readByte();
1592
+ face.vertexIndices = new Array(vertexCount);
1593
+ for (let i = 0; i < vertexCount; i++) {
1594
+ face.vertexIndices[i] = reader.readUInt16();
1595
+ }
1596
+ face.size = 1 + vertexCount * 2;
1597
+ return face;
1598
+ }
1599
+ };
1600
+ var Polygons = class _Polygons {
1601
+ constructor() {
1602
+ this.faces = [];
1603
+ this.unk = 0;
1604
+ }
1605
+ static fromReader(reader) {
1606
+ const polygons = new _Polygons();
1607
+ const facesCount = reader.readInt32();
1608
+ const allocationSize = reader.readInt32();
1609
+ polygons.unk = reader.readUInt16();
1610
+ if (polygons.unk !== 0) {
1611
+ console.warn(`Polygons.unk expected to be 0, got: ${polygons.unk}`);
1612
+ }
1613
+ let size = 0;
1614
+ polygons.faces = new Array(facesCount);
1615
+ for (let i = 0; i < facesCount; i++) {
1616
+ polygons.faces[i] = Face2.fromReader(reader);
1617
+ size += polygons.faces[i].size;
1618
+ }
1619
+ size += polygons.faces.length;
1620
+ if (size !== allocationSize) {
1621
+ console.warn(`Polygon allocation size mismatch: expected ${allocationSize}, got ${size}`);
1622
+ }
1623
+ return polygons;
1624
+ }
1625
+ };
1626
+ var Section = class _Section {
1627
+ constructor() {
1628
+ this.faceLowerIndex = 0;
1629
+ this.faceUpperIndex = 0;
1630
+ this.minBoneIndex = 0;
1631
+ this.bonesCount = 0;
1632
+ this.textureIndex = 0;
1633
+ this.commonFaceFlags = 0 /* None */;
1634
+ this.materialIndex = 0;
1635
+ this.material = "";
1636
+ this.areaOverTex = [];
1637
+ this.unk1 = 0;
1638
+ }
1639
+ static fromReader(reader) {
1640
+ const section = new _Section();
1641
+ section.faceLowerIndex = reader.readInt32();
1642
+ section.faceUpperIndex = reader.readInt32();
1643
+ section.minBoneIndex = reader.readInt32();
1644
+ section.bonesCount = reader.readInt32();
1645
+ section.unk1 = reader.readInt32();
1646
+ section.textureIndex = reader.readUInt16();
1647
+ section.commonFaceFlags = reader.readUInt32();
1648
+ section.materialIndex = reader.readInt32();
1649
+ if (section.materialIndex === -1) {
1650
+ section.material = reader.readCString();
1651
+ }
1652
+ section.areaOverTex = reader.readArray((r) => r.readFloat());
1653
+ return section;
1654
+ }
1655
+ getFaceIndexes(faces) {
1656
+ let curFaceOffset = 0;
1657
+ const faceStride = 8;
1658
+ const quadExtra = 2;
1659
+ const indexes = [];
1660
+ for (let index = 0; index < faces.length; index++) {
1661
+ if (curFaceOffset >= this.faceLowerIndex && curFaceOffset < this.faceUpperIndex) {
1662
+ indexes.push(index);
1663
+ }
1664
+ curFaceOffset += faceStride;
1665
+ if (faces[index].vertexIndices.length === 4) {
1666
+ curFaceOffset += quadExtra;
1667
+ }
1668
+ if (curFaceOffset >= this.faceUpperIndex) break;
1669
+ }
1670
+ return indexes;
1671
+ }
1672
+ };
1673
+ var NamedSelection = class _NamedSelection {
1674
+ constructor() {
1675
+ this.name = "";
1676
+ this.isSectional = false;
1677
+ this.selectedFaces = [];
1678
+ this.sections = [];
1679
+ this.selectedVertices = [];
1680
+ this.selectedVerticesWeights = new Uint8Array(0);
1681
+ }
1682
+ static fromReader(reader) {
1683
+ const selection = new _NamedSelection();
1684
+ selection.name = reader.readCString();
1685
+ selection.selectedFaces = reader.readCompressedVertexIndexArray();
1686
+ const unk0 = reader.readInt32();
1687
+ if (unk0 !== 0) {
1688
+ console.warn(`NamedSelection: expected 0, got ${unk0}`);
1689
+ }
1690
+ selection.isSectional = reader.readBoolean();
1691
+ selection.sections = reader.readCompressedArray((r) => r.readInt32(), 4);
1692
+ selection.selectedVertices = reader.readCompressedVertexIndexArray();
1693
+ const weightsSize = reader.readInt32();
1694
+ selection.selectedVerticesWeights = reader.readCompressed(weightsSize);
1695
+ return selection;
1696
+ }
1697
+ };
1698
+ var UVSet = class _UVSet {
1699
+ constructor() {
1700
+ this.minU = 0;
1701
+ this.minV = 0;
1702
+ this.maxU = 0;
1703
+ this.maxV = 0;
1704
+ this.nVertices = 0;
1705
+ this.isDefault = false;
1706
+ this.defaultValue = new Uint8Array(0);
1707
+ this.uvData = new Uint8Array(0);
1708
+ }
1709
+ static fromReader(reader) {
1710
+ const uvSet = new _UVSet();
1711
+ uvSet.minU = reader.readFloat();
1712
+ uvSet.minV = reader.readFloat();
1713
+ uvSet.maxU = reader.readFloat();
1714
+ uvSet.maxV = reader.readFloat();
1715
+ uvSet.nVertices = reader.readInt32();
1716
+ uvSet.isDefault = reader.readBoolean();
1717
+ if (uvSet.isDefault) {
1718
+ uvSet.defaultValue = reader.readBytes(4);
1719
+ } else {
1720
+ uvSet.uvData = reader.readCompressed(uvSet.nVertices * 4);
1721
+ }
1722
+ return uvSet;
1723
+ }
1724
+ /**
1725
+ * Get UV data as float array
1726
+ */
1727
+ getUVData() {
1728
+ const uvData = new Float32Array(this.nVertices * 2);
1729
+ const rangeU = this.maxU - this.minU;
1730
+ const rangeV = this.maxV - this.minV;
1731
+ const view = this.isDefault ? new DataView(this.defaultValue.buffer, this.defaultValue.byteOffset) : new DataView(this.uvData.buffer, this.uvData.byteOffset);
1732
+ if (this.isDefault) {
1733
+ const u = this.decodeUVComponent(view.getInt16(0, true), rangeU, this.minU);
1734
+ const v = this.decodeUVComponent(view.getInt16(2, true), rangeV, this.minV);
1735
+ for (let i = 0; i < this.nVertices; i++) {
1736
+ const dst = i * 2;
1737
+ uvData[dst] = u;
1738
+ uvData[dst + 1] = v;
1739
+ }
1740
+ } else {
1741
+ for (let i = 0; i < this.nVertices; i++) {
1742
+ const src = i * 4;
1743
+ const dst = i * 2;
1744
+ uvData[dst] = this.decodeUVComponent(view.getInt16(src, true), rangeU, this.minU);
1745
+ uvData[dst + 1] = this.decodeUVComponent(view.getInt16(src + 2, true), rangeV, this.minV);
1746
+ }
1747
+ }
1748
+ return uvData;
1749
+ }
1750
+ decodeUVComponent(value, range, min) {
1751
+ return (value + 32767) / 65536 * range + min;
1752
+ }
1753
+ };
1754
+
1755
+ // src/odol/VertexData.ts
1756
+ var AnimationRTPair = class {
1757
+ constructor(selectionIndex, weight) {
1758
+ this.selectionIndex = selectionIndex;
1759
+ this.weight = weight;
1760
+ }
1761
+ };
1762
+ var AnimationRTWeight = class _AnimationRTWeight {
1763
+ constructor() {
1764
+ this.nSmall = 0;
1765
+ this.smallSpace = new Uint8Array(8);
1766
+ }
1767
+ static fromReader(reader) {
1768
+ const weight = new _AnimationRTWeight();
1769
+ weight.nSmall = reader.readInt32();
1770
+ weight.smallSpace = reader.readBytes(8);
1771
+ return weight;
1772
+ }
1773
+ getAnimationRTPairs() {
1774
+ const pairs = [];
1775
+ for (let i = 0; i < this.nSmall; i++) {
1776
+ pairs.push(new AnimationRTPair(
1777
+ this.smallSpace[i * 2],
1778
+ this.smallSpace[i * 2 + 1]
1779
+ ));
1780
+ }
1781
+ return pairs;
1782
+ }
1783
+ };
1784
+ var VertexNeighborInfo = class _VertexNeighborInfo {
1785
+ constructor() {
1786
+ this.posA = 0;
1787
+ this.unk1 = 0;
1788
+ this.weightA = null;
1789
+ this.posB = 0;
1790
+ this.unk2 = 0;
1791
+ this.weightB = null;
1792
+ }
1793
+ static fromReader(reader) {
1794
+ const info = new _VertexNeighborInfo();
1795
+ info.posA = reader.readUInt16();
1796
+ info.unk1 = reader.readUInt16();
1797
+ info.weightA = AnimationRTWeight.fromReader(reader);
1798
+ info.posB = reader.readUInt16();
1799
+ info.unk2 = reader.readUInt16();
1800
+ info.weightB = AnimationRTWeight.fromReader(reader);
1801
+ return info;
1802
+ }
1803
+ };
1804
+ var STPair = class _STPair {
1805
+ constructor() {
1806
+ this.s = new Vector32();
1807
+ this.t = new Vector32();
1808
+ }
1809
+ static fromReader(reader) {
1810
+ const pair = new _STPair();
1811
+ pair.s = reader.readCompressedVector3();
1812
+ pair.t = reader.readCompressedVector3();
1813
+ return pair;
1814
+ }
1815
+ };
1816
+ var VertexData = class _VertexData {
1817
+ constructor() {
1818
+ this.vertexBoneRefIsSimple = false;
1819
+ this.clipFlags = [];
1820
+ this.uvSets = [];
1821
+ this.vertices = [];
1822
+ this.normals = [];
1823
+ this.stCoords = [];
1824
+ this.vertexBoneRef = [];
1825
+ this.neighborBoneRef = [];
1826
+ }
1827
+ static fromReader(reader) {
1828
+ const data = new _VertexData();
1829
+ data.vertexBoneRefIsSimple = reader.readBoolean();
1830
+ const calcSizeStart = reader.pos;
1831
+ const sizeOfData = reader.readUInt32();
1832
+ const clipFlagsInt = reader.readCompressedFillArray((r) => r.readInt32(), 4);
1833
+ data.clipFlags = clipFlagsInt.map((f) => f);
1834
+ const uvSet0 = UVSet.fromReader(reader);
1835
+ const uvSetCount = reader.readInt32();
1836
+ data.uvSets = new Array(uvSetCount);
1837
+ data.uvSets[0] = uvSet0;
1838
+ for (let i = 1; i < uvSetCount; i++) {
1839
+ data.uvSets[i] = UVSet.fromReader(reader);
1840
+ }
1841
+ data.vertices = reader.readCompressedArray((r) => Vector32.fromReader(r), 12);
1842
+ data.normals = reader.readCompressedFillArray((r) => r.readCompressedVector3(), 4);
1843
+ data.stCoords = reader.readCompressedArray((r) => STPair.fromReader(r), 8);
1844
+ data.vertexBoneRef = reader.readCompressedArray((r) => AnimationRTWeight.fromReader(r), 12);
1845
+ data.neighborBoneRef = reader.readCompressedArray((r) => VertexNeighborInfo.fromReader(r), 32);
1846
+ const size = reader.pos - calcSizeStart;
1847
+ if (size !== sizeOfData) {
1848
+ console.warn(`Vertex Data size mismatch: expected ${sizeOfData}, got ${size}`);
1849
+ }
1850
+ return data;
1851
+ }
1852
+ };
1853
+
1854
+ // src/odol/OdolLod.ts
1855
+ var OdolLod = class _OdolLod {
1856
+ constructor() {
1857
+ this.resolution = 0;
1858
+ this.proxyObjects = [];
1859
+ this.subSkeletonsToSkeleton = [];
1860
+ this.skeletonToSubSkeleton = [];
1861
+ this.vertexCount = 0;
1862
+ this.faceArea = 0;
1863
+ this.orHints = 0 /* None */;
1864
+ this.andHints = 0 /* None */;
1865
+ this.bMin = new Vector32();
1866
+ this.bMax = new Vector32();
1867
+ this.bCenter = new Vector32();
1868
+ this.bRadius = 0;
1869
+ this.textures = [];
1870
+ this.materials = [];
1871
+ this.pointToVertex = [];
1872
+ this.vertexToPoint = [];
1873
+ this.polygons = new Polygons();
1874
+ this.sections = [];
1875
+ this.namedSelections = [];
1876
+ this.namedProperties = [];
1877
+ this.frames = [];
1878
+ this.iconColor = 0;
1879
+ this.selectedColor = 0;
1880
+ this.special = 0 /* None */;
1881
+ this.vertexData = new VertexData();
1882
+ this.lodMetadata = null;
1883
+ }
1884
+ static fromReader(reader) {
1885
+ const lod = new _OdolLod();
1886
+ lod.proxyObjects = reader.readArray((r) => ProxyObject.fromReader(r));
1887
+ lod.subSkeletonsToSkeleton = reader.readArray((r) => r.readInt32());
1888
+ lod.skeletonToSubSkeleton = reader.readArray((r) => SubSkeletonIndexSet.fromReader(r));
1889
+ lod.vertexCount = reader.readUInt32();
1890
+ lod.faceArea = reader.readFloat();
1891
+ lod.orHints = reader.readInt32();
1892
+ lod.andHints = reader.readInt32();
1893
+ lod.bMin = Vector32.fromReader(reader);
1894
+ lod.bMax = Vector32.fromReader(reader);
1895
+ lod.bCenter = Vector32.fromReader(reader);
1896
+ lod.bRadius = reader.readFloat();
1897
+ lod.textures = reader.readArray((r) => r.readCString());
1898
+ lod.materials = reader.readArray((r) => EmbeddedMaterial.fromReader(r));
1899
+ lod.pointToVertex = reader.readCompressedVertexIndexArray();
1900
+ lod.vertexToPoint = reader.readCompressedVertexIndexArray();
1901
+ lod.polygons = Polygons.fromReader(reader);
1902
+ lod.sections = reader.readArray((r) => Section.fromReader(r));
1903
+ lod.namedSelections = reader.readArray((r) => NamedSelection.fromReader(r));
1904
+ const namedPropertiesCnt = reader.readInt32();
1905
+ lod.namedProperties = new Array(namedPropertiesCnt);
1906
+ for (let i = 0; i < namedPropertiesCnt; i++) {
1907
+ const key = reader.readCString();
1908
+ const value = reader.readCString();
1909
+ lod.namedProperties[i] = [key, value];
1910
+ }
1911
+ lod.frames = reader.readArray((r) => Keyframe.fromReader(r));
1912
+ lod.iconColor = reader.readInt32();
1913
+ lod.selectedColor = reader.readUInt32();
1914
+ lod.special = reader.readInt32();
1915
+ lod.vertexData = VertexData.fromReader(reader);
1916
+ return lod;
1917
+ }
1918
+ /**
1919
+ * Get resolution name
1920
+ */
1921
+ get resolutionName() {
1922
+ return getLodName(this.resolution);
1923
+ }
1924
+ /**
1925
+ * Get all unique texture paths
1926
+ */
1927
+ get allTextures() {
1928
+ return [...new Set(this.textures)];
1929
+ }
1930
+ /**
1931
+ * Get all material names
1932
+ */
1933
+ get materialNames() {
1934
+ return this.materials.map((m) => m.materialName);
1935
+ }
1936
+ /**
1937
+ * Get vertices
1938
+ */
1939
+ get vertices() {
1940
+ return this.vertexData.vertices;
1941
+ }
1942
+ get verticesCount() {
1943
+ return this.vertexData.vertices.length;
1944
+ }
1945
+ get facesCount() {
1946
+ return this.polygons.faces.length;
1947
+ }
1948
+ /**
1949
+ * Get face count
1950
+ */
1951
+ get faces() {
1952
+ return this.polygons.faces;
1953
+ }
1954
+ /**
1955
+ * Get statistics about this LOD
1956
+ */
1957
+ getStats() {
1958
+ return {
1959
+ resolution: this.resolution,
1960
+ vertexCount: this.vertices.length,
1961
+ faceCount: this.faces.length,
1962
+ textureCount: this.textures.length,
1963
+ materialCount: this.materials.length,
1964
+ sectionCount: this.sections.length,
1965
+ namedSelectionCount: this.namedSelections.length,
1966
+ proxyCount: this.proxyObjects.length
1967
+ };
1968
+ }
1969
+ };
1970
+
1971
+ // src/odol/ModelInfo.ts
1972
+ var ModelInfo = class _ModelInfo {
1973
+ constructor() {
1974
+ this.special = 0;
1975
+ this.boundingSphere = 0;
1976
+ this.geometrySphere = 0;
1977
+ this.remarks = 0;
1978
+ this.andHints = 0 /* None */;
1979
+ this.orHints = 0 /* None */;
1980
+ this.aimingCenter = new Vector32();
1981
+ this.color = new PackedColor();
1982
+ this.colorType = new PackedColor();
1983
+ this.viewDensity = 0;
1984
+ this.bboxMin = new Vector32();
1985
+ this.bboxMax = new Vector32();
1986
+ this.bboxMinVisual = new Vector32();
1987
+ this.bboxMaxVisual = new Vector32();
1988
+ this.boundingCenter = new Vector32();
1989
+ this.geometryCenter = new Vector32();
1990
+ this.centerOfMass = new Vector32();
1991
+ this.invInertia = new Matrix3();
1992
+ this.autoCenter = false;
1993
+ this.lockAutoCenter = false;
1994
+ this.canOcclude = false;
1995
+ this.canBeOccluded = false;
1996
+ this.unknownBool = false;
1997
+ this.unknownFloat = 0;
1998
+ this.forceNotAlphaModel = false;
1999
+ this.sbSource = 0 /* Visual */;
2000
+ this.preferShadowVolume = false;
2001
+ this.shadowOffset = 0;
2002
+ this.animated = false;
2003
+ this.skeleton = new Skeleton();
2004
+ this.mapType = 0 /* Tree */;
2005
+ this.massArray = [0, 0, 0, 0];
2006
+ this.mass = 0;
2007
+ this.invMass = 0;
2008
+ this.armor = 0;
2009
+ this.invArmor = 0;
2010
+ this.htMin = 0;
2011
+ this.htMax = 0;
2012
+ this.afMax = 0;
2013
+ this.mfMax = 0;
2014
+ this.mFact = 0;
2015
+ this.tBody = 0;
2016
+ this.minShadow = 0;
2017
+ this.canBlend = false;
2018
+ this.propertyClass = "";
2019
+ this.propertyDamage = "";
2020
+ this.propertyFrequent = false;
2021
+ this.memoryIndex = 0;
2022
+ this.geometryIndex = 0;
2023
+ this.unkLodType1Index = 0;
2024
+ this.geometryFireIndex = 0;
2025
+ this.geometryViewIndex = 0;
2026
+ this.geometryViewPilotIndex = 0;
2027
+ this.geometryViewGunnerIndex = 0;
2028
+ this.unkLodType2Index = 0;
2029
+ this.geometryViewCargoIndex = 0;
2030
+ this.landContactIndex = 0;
2031
+ this.roadwayIndex = 0;
2032
+ this.pathsIndex = 0;
2033
+ this.hitpointsIndex = 0;
2034
+ }
2035
+ static fromReader(reader) {
2036
+ const info = new _ModelInfo();
2037
+ info.special = reader.readInt32();
2038
+ info.boundingSphere = reader.readFloat();
2039
+ info.geometrySphere = reader.readFloat();
2040
+ info.remarks = reader.readInt32();
2041
+ info.andHints = reader.readUInt32();
2042
+ info.orHints = reader.readUInt32();
2043
+ info.aimingCenter = Vector32.fromReader(reader);
2044
+ info.color = PackedColor.fromReader(reader);
2045
+ info.colorType = PackedColor.fromReader(reader);
2046
+ info.viewDensity = reader.readFloat();
2047
+ info.bboxMin = Vector32.fromReader(reader);
2048
+ info.bboxMax = Vector32.fromReader(reader);
2049
+ info.bboxMinVisual = Vector32.fromReader(reader);
2050
+ info.bboxMaxVisual = Vector32.fromReader(reader);
2051
+ info.boundingCenter = Vector32.fromReader(reader);
2052
+ info.geometryCenter = Vector32.fromReader(reader);
2053
+ info.centerOfMass = Vector32.fromReader(reader);
2054
+ info.invInertia = Matrix3.fromReader(reader);
2055
+ info.autoCenter = reader.readBoolean();
2056
+ info.lockAutoCenter = reader.readBoolean();
2057
+ info.canOcclude = reader.readBoolean();
2058
+ info.canBeOccluded = reader.readBoolean();
2059
+ info.unknownBool = reader.readBoolean();
2060
+ info.unknownFloat = reader.readFloat();
2061
+ console.warn("Unknown ModelInfo values:", info.unknownBool, info.unknownFloat);
2062
+ info.htMin = reader.readFloat();
2063
+ info.htMax = reader.readFloat();
2064
+ info.afMax = reader.readFloat();
2065
+ info.mfMax = reader.readFloat();
2066
+ info.mFact = reader.readFloat();
2067
+ info.tBody = reader.readFloat();
2068
+ info.forceNotAlphaModel = reader.readBoolean();
2069
+ info.sbSource = reader.readInt32();
2070
+ info.preferShadowVolume = reader.readBoolean();
2071
+ info.shadowOffset = reader.readFloat();
2072
+ info.animated = reader.readBoolean();
2073
+ info.skeleton = Skeleton.fromReader(reader);
2074
+ info.mapType = reader.readByte();
2075
+ info.massArray = reader.readCompressedArray((r) => r.readFloat(), 4);
2076
+ info.mass = reader.readFloat();
2077
+ info.invMass = reader.readFloat();
2078
+ info.armor = reader.readFloat();
2079
+ info.invArmor = reader.readFloat();
2080
+ info.memoryIndex = reader.readByte();
2081
+ info.geometryIndex = reader.readByte();
2082
+ if (reader.version >= 54) {
2083
+ info.unkLodType1Index = reader.readByte();
2084
+ }
2085
+ info.geometryFireIndex = reader.readByte();
2086
+ info.geometryViewIndex = reader.readByte();
2087
+ info.geometryViewPilotIndex = reader.readByte();
2088
+ info.geometryViewGunnerIndex = reader.readByte();
2089
+ info.unkLodType2Index = reader.readByte();
2090
+ info.geometryViewCargoIndex = reader.readByte();
2091
+ info.landContactIndex = reader.readByte();
2092
+ info.roadwayIndex = reader.readByte();
2093
+ info.pathsIndex = reader.readByte();
2094
+ info.hitpointsIndex = reader.readByte();
2095
+ info.minShadow = reader.readUInt32();
2096
+ info.canBlend = reader.readBoolean();
2097
+ info.propertyClass = reader.readCString();
2098
+ info.propertyDamage = reader.readCString();
2099
+ info.propertyFrequent = reader.readBoolean();
2100
+ return info;
2101
+ }
2102
+ };
2103
+
2104
+ // src/odol/Odol.ts
2105
+ var LodInfo = class {
2106
+ constructor() {
2107
+ this.resolution = 0;
2108
+ this.lodStartPosition = 0;
2109
+ this.lodEndPosition = 0;
2110
+ this.hasLodMeta = false;
2111
+ this.lodMetadata = null;
2112
+ }
2113
+ };
2114
+ var _Odol = class _Odol {
2115
+ constructor() {
2116
+ this.version = 0;
2117
+ this.modelInfo = null;
2118
+ this.lods = [];
2119
+ this.animations = null;
2120
+ }
2121
+ /**
2122
+ * Read ODOL from a buffer
2123
+ */
2124
+ static fromBuffer(buffer) {
2125
+ const reader = new OdolReader(buffer);
2126
+ return _Odol.fromReader(reader);
2127
+ }
2128
+ /**
2129
+ * Read ODOL from an OdolReader
2130
+ */
2131
+ static fromReader(reader) {
2132
+ const odol = new _Odol();
2133
+ const signature = reader.readString(4);
2134
+ if (signature !== "ODOL") {
2135
+ throw new Error(`Expected ODOL signature, got: ${signature}`);
2136
+ }
2137
+ odol.version = reader.readInt32();
2138
+ reader.version = odol.version;
2139
+ if (odol.version > _Odol.MAX_SUPPORTED_VERSION || odol.version < _Odol.MIN_SUPPORTED_VERSION) {
2140
+ throw new Error(`Unsupported ODOL version: ${odol.version}. Supported versions: ${_Odol.MIN_SUPPORTED_VERSION}-${_Odol.MAX_SUPPORTED_VERSION}`);
2141
+ }
2142
+ const lodCount = reader.readInt32();
2143
+ const lodInfos = new Array(lodCount);
2144
+ for (let i = 0; i < lodCount; i++) {
2145
+ lodInfos[i] = new LodInfo();
2146
+ lodInfos[i].resolution = reader.readFloat();
2147
+ }
2148
+ odol.modelInfo = ModelInfo.fromReader(reader);
2149
+ const _unk = reader.readUInt32();
2150
+ const hasAnimations = reader.readBoolean();
2151
+ if (hasAnimations) {
2152
+ odol.animations = Animations.fromReader(reader);
2153
+ }
2154
+ for (let i = 0; i < lodCount; i++) {
2155
+ lodInfos[i].lodStartPosition = reader.readUInt32();
2156
+ }
2157
+ for (let i = 0; i < lodCount; i++) {
2158
+ lodInfos[i].lodEndPosition = reader.readUInt32();
2159
+ }
2160
+ for (let i = 0; i < lodCount; i++) {
2161
+ lodInfos[i].hasLodMeta = !reader.readBoolean();
2162
+ }
2163
+ for (let i = 0; i < lodCount; i++) {
2164
+ if (lodInfos[i].hasLodMeta) {
2165
+ lodInfos[i].lodMetadata = LodMetadata.fromReader(reader);
2166
+ }
2167
+ }
2168
+ const lowestPos = Math.min(...lodInfos.map((info) => info.lodStartPosition));
2169
+ if (reader.pos !== lowestPos) {
2170
+ throw new Error(`Not all data read correctly, expected LODs to start at ${lowestPos}, but current position is ${reader.pos} (Difference: ${lowestPos - reader.pos})`);
2171
+ }
2172
+ odol.lods = new Array(lodCount);
2173
+ for (let i = 0; i < lodCount; i++) {
2174
+ const lodInfo = lodInfos[i];
2175
+ if (lodInfo.lodStartPosition >= reader.length) {
2176
+ continue;
2177
+ }
2178
+ reader.seek(lodInfo.lodStartPosition, "begin");
2179
+ const lod = OdolLod.fromReader(reader);
2180
+ lod.resolution = lodInfo.resolution;
2181
+ lod.lodMetadata = lodInfos[i].lodMetadata;
2182
+ odol.lods[i] = lod;
2183
+ if (lodInfo.lodEndPosition > 0 && lodInfo.lodEndPosition <= reader.length) {
2184
+ if (reader.pos !== lodInfo.lodEndPosition) {
2185
+ console.warn(`LOD[${i}] position mismatch: expected ${lodInfo.lodEndPosition}, got ${reader.pos}`);
2186
+ }
2187
+ }
2188
+ }
2189
+ return odol;
2190
+ }
2191
+ /**
2192
+ * Get skeleton from model info
2193
+ */
2194
+ get skeleton() {
2195
+ return this.modelInfo?.skeleton ?? null;
2196
+ }
2197
+ /**
2198
+ * Get mass from model info
2199
+ */
2200
+ get mass() {
2201
+ return this.modelInfo?.mass ?? 0;
2202
+ }
2203
+ /**
2204
+ * Check if model has animations
2205
+ */
2206
+ get hasAnims() {
2207
+ return this.animations !== null;
2208
+ }
2209
+ /**
2210
+ * Get all unique textures across all LODs
2211
+ */
2212
+ get allTextures() {
2213
+ const textures = /* @__PURE__ */ new Set();
2214
+ for (const lod of this.lods) {
2215
+ for (const texture of lod.textures) {
2216
+ textures.add(texture);
2217
+ }
2218
+ }
2219
+ return Array.from(textures);
2220
+ }
2221
+ get allMaterials() {
2222
+ const materials = /* @__PURE__ */ new Set();
2223
+ for (const lod of this.lods) {
2224
+ for (const material of lod.materials) {
2225
+ materials.add(material.materialName);
2226
+ }
2227
+ }
2228
+ return Array.from(materials);
2229
+ }
2230
+ /**
2231
+ * Get statistics about the model
2232
+ */
2233
+ getStats() {
2234
+ return {
2235
+ version: this.version,
2236
+ lodCount: this.lods.length,
2237
+ totalVertices: this.lods.reduce((sum, lod) => sum + lod.vertices.length, 0),
2238
+ totalFaces: this.lods.reduce((sum, lod) => sum + lod.faces.length, 0),
2239
+ textures: this.allTextures,
2240
+ materials: this.allMaterials,
2241
+ mass: this.mass,
2242
+ hasAnimations: this.hasAnims,
2243
+ skeleton: this.skeleton?.name ?? "none"
2244
+ };
2245
+ }
2246
+ };
2247
+ _Odol.MIN_SUPPORTED_VERSION = 53;
2248
+ _Odol.MAX_SUPPORTED_VERSION = 54;
2249
+ var Odol = _Odol;
2250
+ export {
2251
+ mlod_exports as MLOD,
2252
+ Mlod,
2253
+ MlodLod,
2254
+ ModelInfo,
2255
+ odol_exports as ODOL,
2256
+ Odol,
2257
+ OdolLod,
2258
+ OdolReader
2259
+ };
2260
+ /**
2261
+ * LZO1X compression and decompression
2262
+ * Based on https://github.com/thaumictom/lzo-ts
2263
+ * @license GPL-3.0
2264
+ */
2265
+ //# sourceMappingURL=index.js.map