@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/LICENSE +674 -0
- package/README.md +58 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2265 -0
- package/dist/index.js.map +7 -0
- package/dist/mlod/Face.d.ts +19 -0
- package/dist/mlod/FaceFlags.d.ts +8 -0
- package/dist/mlod/Mlod.d.ts +39 -0
- package/dist/mlod/MlodLod.d.ts +30 -0
- package/dist/mlod/Point.d.ts +26 -0
- package/dist/mlod/PointFlags.d.ts +9 -0
- package/dist/mlod/Tagg.d.ts +53 -0
- package/dist/mlod/TaggReader.d.ts +18 -0
- package/dist/mlod/Vector3.d.ts +29 -0
- package/dist/mlod/Vertex.d.ts +12 -0
- package/dist/mlod/index.d.ts +15 -0
- package/dist/odol/Animation.d.ts +48 -0
- package/dist/odol/Animations.d.ts +12 -0
- package/dist/odol/Materials.d.ts +51 -0
- package/dist/odol/ModelInfo.d.ts +71 -0
- package/dist/odol/Odol.d.ts +47 -0
- package/dist/odol/OdolLod.d.ts +75 -0
- package/dist/odol/OdolReader.d.ts +30 -0
- package/dist/odol/PackedColor.d.ts +14 -0
- package/dist/odol/VertexData.d.ts +52 -0
- package/dist/odol/auxiliaryStructures.d.ts +49 -0
- package/dist/odol/enums.d.ts +104 -0
- package/dist/odol/index.d.ts +14 -0
- package/dist/odol/math.d.ts +46 -0
- package/dist/odol/structures.d.ts +66 -0
- package/dist/shared/Lod.d.ts +5 -0
- package/dist/shared/P3d.d.ts +19 -0
- package/dist/shared/Resolution.d.ts +1 -0
- package/package.json +56 -0
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
|