rw-parser-ng 2.0.5 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -674
- package/README.md +88 -88
- package/lib/renderware/RwFile.js +3 -0
- package/lib/renderware/dff/DffParser.js +16 -2
- package/lib/renderware/txd/TxdParser.js +3 -2
- package/lib/utils/ByteStream.d.ts +1 -0
- package/lib/utils/ByteStream.js +4 -0
- package/package.json +10 -9
- package/src/index.ts +12 -12
- package/src/renderware/RwFile.ts +26 -22
- package/src/renderware/RwSections.ts +27 -27
- package/src/renderware/common/types.ts +58 -58
- package/src/renderware/dff/DffModelType.ts +4 -4
- package/src/renderware/dff/DffParser.ts +611 -596
- package/src/renderware/errors/RwParseError.ts +11 -11
- package/src/renderware/ifp/IfpData.ts +32 -32
- package/src/renderware/ifp/IfpParser.ts +202 -202
- package/src/renderware/txd/TxdParser.ts +235 -234
- package/src/renderware/utils/ImageDecoder.ts +570 -570
- package/src/renderware/utils/ImageFormatEnums.ts +27 -27
- package/src/renderware/utils/RwVersion.ts +28 -28
- package/src/utils/ByteStream.ts +80 -75
- package/tsconfig.json +66 -66
|
@@ -1,596 +1,611 @@
|
|
|
1
|
-
import { RwFile } from '../RwFile';
|
|
2
|
-
import { RwSections } from '../RwSections';
|
|
3
|
-
import { RwParseStructureNotFoundError } from '../errors/RwParseError';
|
|
4
|
-
import RwVersion from '../utils/RwVersion';
|
|
5
|
-
import { DffModelType } from './DffModelType';
|
|
6
|
-
import { RwColor, RwMatrix3, RwMatrix4, RwSphere, RwTextureCoordinate, RwTriangle, RwVector3 } from '../common/types';
|
|
7
|
-
|
|
8
|
-
export interface RwDff {
|
|
9
|
-
modelType: DffModelType,
|
|
10
|
-
version: string,
|
|
11
|
-
versionNumber: number,
|
|
12
|
-
geometryList: RwGeometryList | null,
|
|
13
|
-
frameList: RwFrameList | null,
|
|
14
|
-
atomics: number[],
|
|
15
|
-
dummies: string[],
|
|
16
|
-
animNodes: RwAnimNode[],
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface RwClump {
|
|
20
|
-
atomicCount: number,
|
|
21
|
-
lightCount?: number,
|
|
22
|
-
cameraCount?: number,
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface RwAnimNode {
|
|
26
|
-
boneId: number,
|
|
27
|
-
bonesCount: number,
|
|
28
|
-
bones: RwBone[],
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface RwBone {
|
|
32
|
-
boneId: number,
|
|
33
|
-
boneIndex: number,
|
|
34
|
-
flags: number,
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
export interface RwFrame {
|
|
39
|
-
rotationMatrix: RwMatrix3,
|
|
40
|
-
coordinatesOffset: RwVector3,
|
|
41
|
-
parentFrame: number,
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface RwFrameList {
|
|
45
|
-
frameCount: number,
|
|
46
|
-
frames: RwFrame[],
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface RwTexture {
|
|
50
|
-
textureFiltering: number,
|
|
51
|
-
uAddressing: number,
|
|
52
|
-
vAddressing: number,
|
|
53
|
-
usesMipLevels: boolean,
|
|
54
|
-
textureName: string,
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export interface RwMaterial {
|
|
58
|
-
color: RwColor,
|
|
59
|
-
isTextured: boolean,
|
|
60
|
-
ambient?: number,
|
|
61
|
-
specular?: number,
|
|
62
|
-
diffuse?: number,
|
|
63
|
-
texture?: RwTexture,
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface RwMaterialList {
|
|
67
|
-
materialInstanceCount: number,
|
|
68
|
-
materialData: RwMaterial[],
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export interface RwGeometry {
|
|
72
|
-
vertexColorInformation: RwColor[],
|
|
73
|
-
textureCoordinatesCount: number,
|
|
74
|
-
textureMappingInformation: RwTextureCoordinate[][],
|
|
75
|
-
hasVertices: boolean,
|
|
76
|
-
hasNormals: boolean,
|
|
77
|
-
triangleInformation: RwTriangle[],
|
|
78
|
-
vertexInformation: RwVector3[],
|
|
79
|
-
normalInformation: RwVector3[],
|
|
80
|
-
boundingSphere?: RwSphere,
|
|
81
|
-
materialList: RwMaterialList,
|
|
82
|
-
binMesh: RwBinMesh,
|
|
83
|
-
skin?: RwSkin,
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export interface RwGeometryList {
|
|
87
|
-
geometricObjectCount: number,
|
|
88
|
-
geometries: RwGeometry[],
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export interface RwAtomic {
|
|
92
|
-
frameIndex: number,
|
|
93
|
-
geometryIndex: number,
|
|
94
|
-
flags: number,
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export interface RwBinMesh {
|
|
98
|
-
meshCount: number,
|
|
99
|
-
meshes: RwMesh[],
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export interface RwSkin {
|
|
103
|
-
boneCount: number,
|
|
104
|
-
usedBoneCount: number,
|
|
105
|
-
maxWeightsPerVertex: number,
|
|
106
|
-
boneVertexIndices: number[][],
|
|
107
|
-
vertexWeights: number[][],
|
|
108
|
-
inverseBoneMatrices: RwMatrix4[],
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export interface RwMesh {
|
|
112
|
-
materialIndex: number,
|
|
113
|
-
indexCount: number,
|
|
114
|
-
indices: number[],
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export class DffParser extends RwFile {
|
|
118
|
-
|
|
119
|
-
constructor(buffer: Buffer) {
|
|
120
|
-
super(buffer);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
parse(): RwDff {
|
|
124
|
-
let version: string | undefined;
|
|
125
|
-
let versionNumber: number | undefined;
|
|
126
|
-
let atomics: number[] = [];
|
|
127
|
-
let dummies: string[] = [];
|
|
128
|
-
let animNodes: RwAnimNode[] = [];
|
|
129
|
-
let geometryList: RwGeometryList | null = null;
|
|
130
|
-
let frameList: RwFrameList | null = null;
|
|
131
|
-
|
|
132
|
-
while (this.getPosition() < this.getSize()) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
break;
|
|
178
|
-
case RwSections.
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
public
|
|
228
|
-
this.readSectionHeader();
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
let
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const
|
|
295
|
-
const
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
public
|
|
500
|
-
this.
|
|
501
|
-
this.
|
|
502
|
-
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
this.
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
const
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
}
|
|
1
|
+
import { RwFile } from '../RwFile';
|
|
2
|
+
import { RwSections } from '../RwSections';
|
|
3
|
+
import { RwParseStructureNotFoundError } from '../errors/RwParseError';
|
|
4
|
+
import RwVersion from '../utils/RwVersion';
|
|
5
|
+
import { DffModelType } from './DffModelType';
|
|
6
|
+
import { RwColor, RwMatrix3, RwMatrix4, RwSphere, RwTextureCoordinate, RwTriangle, RwVector3 } from '../common/types';
|
|
7
|
+
|
|
8
|
+
export interface RwDff {
|
|
9
|
+
modelType: DffModelType,
|
|
10
|
+
version: string,
|
|
11
|
+
versionNumber: number,
|
|
12
|
+
geometryList: RwGeometryList | null,
|
|
13
|
+
frameList: RwFrameList | null,
|
|
14
|
+
atomics: number[],
|
|
15
|
+
dummies: string[],
|
|
16
|
+
animNodes: RwAnimNode[],
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface RwClump {
|
|
20
|
+
atomicCount: number,
|
|
21
|
+
lightCount?: number,
|
|
22
|
+
cameraCount?: number,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface RwAnimNode {
|
|
26
|
+
boneId: number,
|
|
27
|
+
bonesCount: number,
|
|
28
|
+
bones: RwBone[],
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface RwBone {
|
|
32
|
+
boneId: number,
|
|
33
|
+
boneIndex: number,
|
|
34
|
+
flags: number,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
export interface RwFrame {
|
|
39
|
+
rotationMatrix: RwMatrix3,
|
|
40
|
+
coordinatesOffset: RwVector3,
|
|
41
|
+
parentFrame: number,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface RwFrameList {
|
|
45
|
+
frameCount: number,
|
|
46
|
+
frames: RwFrame[],
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface RwTexture {
|
|
50
|
+
textureFiltering: number,
|
|
51
|
+
uAddressing: number,
|
|
52
|
+
vAddressing: number,
|
|
53
|
+
usesMipLevels: boolean,
|
|
54
|
+
textureName: string,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface RwMaterial {
|
|
58
|
+
color: RwColor,
|
|
59
|
+
isTextured: boolean,
|
|
60
|
+
ambient?: number,
|
|
61
|
+
specular?: number,
|
|
62
|
+
diffuse?: number,
|
|
63
|
+
texture?: RwTexture,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface RwMaterialList {
|
|
67
|
+
materialInstanceCount: number,
|
|
68
|
+
materialData: RwMaterial[],
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface RwGeometry {
|
|
72
|
+
vertexColorInformation: RwColor[],
|
|
73
|
+
textureCoordinatesCount: number,
|
|
74
|
+
textureMappingInformation: RwTextureCoordinate[][],
|
|
75
|
+
hasVertices: boolean,
|
|
76
|
+
hasNormals: boolean,
|
|
77
|
+
triangleInformation: RwTriangle[],
|
|
78
|
+
vertexInformation: RwVector3[],
|
|
79
|
+
normalInformation: RwVector3[],
|
|
80
|
+
boundingSphere?: RwSphere,
|
|
81
|
+
materialList: RwMaterialList,
|
|
82
|
+
binMesh: RwBinMesh,
|
|
83
|
+
skin?: RwSkin,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface RwGeometryList {
|
|
87
|
+
geometricObjectCount: number,
|
|
88
|
+
geometries: RwGeometry[],
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface RwAtomic {
|
|
92
|
+
frameIndex: number,
|
|
93
|
+
geometryIndex: number,
|
|
94
|
+
flags: number,
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface RwBinMesh {
|
|
98
|
+
meshCount: number,
|
|
99
|
+
meshes: RwMesh[],
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface RwSkin {
|
|
103
|
+
boneCount: number,
|
|
104
|
+
usedBoneCount: number,
|
|
105
|
+
maxWeightsPerVertex: number,
|
|
106
|
+
boneVertexIndices: number[][],
|
|
107
|
+
vertexWeights: number[][],
|
|
108
|
+
inverseBoneMatrices: RwMatrix4[],
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface RwMesh {
|
|
112
|
+
materialIndex: number,
|
|
113
|
+
indexCount: number,
|
|
114
|
+
indices: number[],
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export class DffParser extends RwFile {
|
|
118
|
+
|
|
119
|
+
constructor(buffer: Buffer) {
|
|
120
|
+
super(buffer);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
parse(): RwDff {
|
|
124
|
+
let version: string | undefined;
|
|
125
|
+
let versionNumber: number | undefined;
|
|
126
|
+
let atomics: number[] = [];
|
|
127
|
+
let dummies: string[] = [];
|
|
128
|
+
let animNodes: RwAnimNode[] = [];
|
|
129
|
+
let geometryList: RwGeometryList | null = null;
|
|
130
|
+
let frameList: RwFrameList | null = null;
|
|
131
|
+
|
|
132
|
+
while (this.getPosition() < this.getSize()) {
|
|
133
|
+
let header;
|
|
134
|
+
try {
|
|
135
|
+
header = this.readSectionHeader();
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.warn(`Failed to read section header at offset ${this.getPosition().toString(16)}: ${error instanceof Error ? error.message : error}. Truncating file.`);
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (header.sectionType === 0) {
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (header.sectionSize == 0) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (this.getPosition() + header.sectionSize > this.getSize()) {
|
|
150
|
+
console.warn(`Section at offset ${this.getPosition().toString(16)} claims size ${header.sectionSize} but only ${this.getSize() - this.getPosition()} bytes remaining. Truncating file.`);
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
switch (header.sectionType) {
|
|
155
|
+
case RwSections.RwClump:
|
|
156
|
+
// Multiple clumps are used in SA player models, so we should eventually support it
|
|
157
|
+
versionNumber = RwVersion.unpackVersion(header.versionNumber);
|
|
158
|
+
version = RwVersion.versions[versionNumber];
|
|
159
|
+
break;
|
|
160
|
+
case RwSections.RwFrameList:
|
|
161
|
+
frameList = this.readFrameList();
|
|
162
|
+
break;
|
|
163
|
+
case RwSections.RwExtension:
|
|
164
|
+
const extensionHeader = this.readSectionHeader();
|
|
165
|
+
switch (extensionHeader.sectionType) {
|
|
166
|
+
case RwSections.RwNodeName:
|
|
167
|
+
dummies.push(this.readString(extensionHeader.sectionSize));
|
|
168
|
+
break;
|
|
169
|
+
case RwSections.RwAnim:
|
|
170
|
+
animNodes.push(this.readAnimNode());
|
|
171
|
+
break;
|
|
172
|
+
default:
|
|
173
|
+
console.debug(`Extension type ${extensionHeader.sectionType} (${extensionHeader.sectionType.toString(16)}) not found at offset (${this.getPosition().toString(16)}). Skipping ${extensionHeader.sectionSize} bytes.`);
|
|
174
|
+
this.skip(extensionHeader.sectionSize);
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
break;
|
|
178
|
+
case RwSections.RwGeometryList:
|
|
179
|
+
geometryList = this.readGeometryList();
|
|
180
|
+
break;
|
|
181
|
+
case RwSections.RwAtomic:
|
|
182
|
+
const atomic = this.readAtomic();
|
|
183
|
+
atomics[atomic.geometryIndex] = atomic.frameIndex;
|
|
184
|
+
break;
|
|
185
|
+
case RwSections.RwNodeName:
|
|
186
|
+
// For some reason, this frame is outside RwExtension.
|
|
187
|
+
dummies.push(this.readString(header.sectionSize));
|
|
188
|
+
break;
|
|
189
|
+
case RwSections.RwAnim:
|
|
190
|
+
// For III / VC models
|
|
191
|
+
animNodes.push(this.readAnimNode());
|
|
192
|
+
break;
|
|
193
|
+
default:
|
|
194
|
+
console.debug(`Section type ${header.sectionType} (${header.sectionType.toString(16)}) not found at offset (${this.getPosition().toString(16)}). Skipping ${header.sectionSize} bytes.`);
|
|
195
|
+
this.skip(header.sectionSize);
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (!version || !versionNumber) {
|
|
201
|
+
throw new RwParseStructureNotFoundError('version');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (!geometryList || !geometryList.geometries || geometryList.geometries.length === 0) {
|
|
205
|
+
throw new RwParseStructureNotFoundError('geometry list');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
let modelType = DffModelType.GENERIC;
|
|
209
|
+
if (geometryList.geometries.some(g => g.skin)) {
|
|
210
|
+
modelType = DffModelType.SKIN;
|
|
211
|
+
} else if (dummies.some(d => d.toLowerCase().includes('wheel') || d.toLowerCase().includes('chassis'))) {
|
|
212
|
+
modelType = DffModelType.VEHICLE;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
modelType,
|
|
217
|
+
version: version,
|
|
218
|
+
versionNumber: versionNumber,
|
|
219
|
+
geometryList: geometryList,
|
|
220
|
+
frameList: frameList,
|
|
221
|
+
atomics: atomics,
|
|
222
|
+
dummies: dummies,
|
|
223
|
+
animNodes: animNodes,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
public readClump(): RwClump {
|
|
228
|
+
const { versionNumber } = this.readSectionHeader();
|
|
229
|
+
|
|
230
|
+
const atomicCount = this.readUint32();
|
|
231
|
+
|
|
232
|
+
let lightCount;
|
|
233
|
+
let cameraCount;
|
|
234
|
+
if (versionNumber > 0x33000) {
|
|
235
|
+
lightCount = this.readUint32();
|
|
236
|
+
cameraCount = this.readUint32();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return { atomicCount, lightCount, cameraCount };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
public readFrameList(): RwFrameList {
|
|
243
|
+
this.readSectionHeader();
|
|
244
|
+
|
|
245
|
+
const frameCount = this.readUint32();
|
|
246
|
+
|
|
247
|
+
let frames: RwFrame[] = [];
|
|
248
|
+
|
|
249
|
+
for (let i = 0; i < frameCount; i++) {
|
|
250
|
+
// All these could probably be moved to readFrameData()
|
|
251
|
+
|
|
252
|
+
const rotationMatrix: RwMatrix3 = {
|
|
253
|
+
right: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() },
|
|
254
|
+
up: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() },
|
|
255
|
+
at: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() },
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const coordinatesOffset: RwVector3 = { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() };
|
|
259
|
+
|
|
260
|
+
const parentFrame = this.readInt32();
|
|
261
|
+
|
|
262
|
+
// Skip matrix creation internal flags
|
|
263
|
+
// They are read by the game but are not used
|
|
264
|
+
this.skip(4);
|
|
265
|
+
|
|
266
|
+
frames.push({ rotationMatrix, coordinatesOffset, parentFrame });
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return { frameCount, frames };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
public readGeometryList(): RwGeometryList {
|
|
273
|
+
const header = this.readSectionHeader();
|
|
274
|
+
|
|
275
|
+
const geometricObjectCount = this.readUint32();
|
|
276
|
+
|
|
277
|
+
let geometries: RwGeometry[] = [];
|
|
278
|
+
|
|
279
|
+
for (let i = 0; i < geometricObjectCount; i++) {
|
|
280
|
+
this.readSectionHeader();
|
|
281
|
+
this.readSectionHeader();
|
|
282
|
+
const versionNumber = RwVersion.unpackVersion(header.versionNumber);
|
|
283
|
+
const geometryData = this.readGeometry(versionNumber);
|
|
284
|
+
geometries.push(geometryData);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return { geometricObjectCount, geometries };
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
public readGeometry(versionNumber: number): RwGeometry {
|
|
291
|
+
const flags = this.readUint16();
|
|
292
|
+
const textureCoordinatesCount = this.readUint8();
|
|
293
|
+
const _nativeGeometryFlags = this.readUint8();
|
|
294
|
+
const triangleCount = this.readUint32();
|
|
295
|
+
const vertexCount = this.readUint32();
|
|
296
|
+
const _morphTargetCount = this.readUint32();
|
|
297
|
+
|
|
298
|
+
// Surface properties
|
|
299
|
+
let _ambient;
|
|
300
|
+
let _specular;
|
|
301
|
+
let _diffuse;
|
|
302
|
+
|
|
303
|
+
if (versionNumber < 0x34000) {
|
|
304
|
+
_ambient = this.readFloat();
|
|
305
|
+
_specular = this.readFloat();
|
|
306
|
+
_diffuse = this.readFloat();
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const _isTriangleStrip = (flags & (1 << 0)) !== 0;
|
|
310
|
+
const _vertexTranslation = (flags & (1 << 1)) !== 0;
|
|
311
|
+
const isTexturedUV1 = (flags & (1 << 2)) !== 0;
|
|
312
|
+
const isGeometryPrelit = (flags & (1 << 3)) !== 0;
|
|
313
|
+
const _hasNormals = (flags & (1 << 4)) !== 0;
|
|
314
|
+
const _isGeometryLit = (flags & (1 << 5)) !== 0;
|
|
315
|
+
const _shouldModulateMaterialColor = (flags & (1 << 6)) !== 0;
|
|
316
|
+
const isTexturedUV2 = (flags & (1 << 7)) !== 0;
|
|
317
|
+
|
|
318
|
+
const vertexColorInformation: RwColor[] = [];
|
|
319
|
+
const textureMappingInformation: RwTextureCoordinate[][] = [];
|
|
320
|
+
const triangleInformation: RwTriangle[] = [];
|
|
321
|
+
|
|
322
|
+
// Geometry is marked as prelit
|
|
323
|
+
if (isGeometryPrelit) {
|
|
324
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
325
|
+
vertexColorInformation[i] = { r: this.readUint8(), g: this.readUint8(), b: this.readUint8(), a: this.readUint8() };
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Geometry either has first or second texture
|
|
330
|
+
if (isTexturedUV1 || isTexturedUV2) {
|
|
331
|
+
for (let textureCoordinateIndex = 0; textureCoordinateIndex < textureCoordinatesCount; textureCoordinateIndex++) {
|
|
332
|
+
textureMappingInformation[textureCoordinateIndex] = [];
|
|
333
|
+
for (let vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++) {
|
|
334
|
+
textureMappingInformation[textureCoordinateIndex][vertexIndex] = { u: this.readFloat(), v: this.readFloat() };
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
for (let i = 0; i < triangleCount; i++) {
|
|
340
|
+
// Information is written in this order
|
|
341
|
+
const vertex2 = this.readUint16();
|
|
342
|
+
const vertex1 = this.readUint16();
|
|
343
|
+
const materialId = this.readUint16();
|
|
344
|
+
const vertex3 = this.readUint16();
|
|
345
|
+
triangleInformation[i] = { vector: { x: vertex1, y: vertex2, z: vertex3 }, materialId: materialId }
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// We are sure that there's only one morph target, but if
|
|
349
|
+
// we are wrong, we have to loop these through morphTargetCount
|
|
350
|
+
|
|
351
|
+
const boundingSphere: RwSphere = {
|
|
352
|
+
vector: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() },
|
|
353
|
+
radius: this.readFloat(),
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const hasVertices = !!this.readUint32();
|
|
357
|
+
const hasNormals = !!this.readUint32();
|
|
358
|
+
|
|
359
|
+
const vertexInformation = [];
|
|
360
|
+
if (hasVertices) {
|
|
361
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
362
|
+
vertexInformation[i] = { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() };
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const normalInformation = [];
|
|
367
|
+
if (hasNormals) {
|
|
368
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
369
|
+
normalInformation[i] = { x: this.readFloat(), y: this.readFloat(), z: this.readFloat() };
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
let materialList = this.readMaterialList();
|
|
374
|
+
let sectionSize = this.readSectionHeader().sectionSize;
|
|
375
|
+
let position = this.getPosition();
|
|
376
|
+
let binMesh = this.readBinMesh();
|
|
377
|
+
let skin = undefined;
|
|
378
|
+
|
|
379
|
+
if (this.readSectionHeader().sectionType == RwSections.RwSkin) {
|
|
380
|
+
skin = this.readSkin(vertexCount);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
this.setPosition(position + sectionSize);
|
|
384
|
+
|
|
385
|
+
return {
|
|
386
|
+
textureCoordinatesCount,
|
|
387
|
+
textureMappingInformation,
|
|
388
|
+
boundingSphere,
|
|
389
|
+
hasVertices,
|
|
390
|
+
hasNormals,
|
|
391
|
+
vertexColorInformation,
|
|
392
|
+
vertexInformation,
|
|
393
|
+
normalInformation,
|
|
394
|
+
triangleInformation,
|
|
395
|
+
materialList,
|
|
396
|
+
binMesh,
|
|
397
|
+
skin,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
public readBinMesh(): RwBinMesh {
|
|
402
|
+
this.readSectionHeader();
|
|
403
|
+
|
|
404
|
+
// Flags (0: triangle list, 1: triangle strip)
|
|
405
|
+
this.skip(4);
|
|
406
|
+
|
|
407
|
+
const meshCount = this.readUint32();
|
|
408
|
+
|
|
409
|
+
// Total number of indices
|
|
410
|
+
this.skip(4);
|
|
411
|
+
|
|
412
|
+
const meshes: RwMesh[] = [];
|
|
413
|
+
|
|
414
|
+
for (let i = 0; i < meshCount; i++) {
|
|
415
|
+
meshes.push(this.readMesh());
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return {
|
|
419
|
+
meshCount, meshes
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
public readSkin(vertexCount : number): RwSkin {
|
|
424
|
+
const boneCount = this.readUint8();
|
|
425
|
+
const usedBoneCount = this.readUint8();
|
|
426
|
+
const maxWeightsPerVertex = this.readUint8();
|
|
427
|
+
|
|
428
|
+
this.skip(1); // Padding
|
|
429
|
+
this.skip(usedBoneCount); // Skipping special indices
|
|
430
|
+
|
|
431
|
+
const boneVertexIndices: number[][] = [];
|
|
432
|
+
const vertexWeights: number[][] = [];
|
|
433
|
+
const inverseBoneMatrices: RwMatrix4[] = [];
|
|
434
|
+
|
|
435
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
436
|
+
const indices: number[] = [];
|
|
437
|
+
for (let j = 0; j < 4; j++) {
|
|
438
|
+
indices.push(this.readUint8());
|
|
439
|
+
}
|
|
440
|
+
boneVertexIndices.push(indices);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
444
|
+
const weights: number[] = [];
|
|
445
|
+
for (let j = 0; j < 4; j++) {
|
|
446
|
+
weights.push(this.readFloat());
|
|
447
|
+
}
|
|
448
|
+
vertexWeights.push(weights);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
for (let i = 0; i < boneCount; i++) {
|
|
452
|
+
const matrix4x4: RwMatrix4 = {
|
|
453
|
+
right: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat(), t: this.readFloat() },
|
|
454
|
+
up: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat(), t: this.readFloat() },
|
|
455
|
+
at: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat(), t: this.readFloat() },
|
|
456
|
+
transform: { x: this.readFloat(), y: this.readFloat(), z: this.readFloat(), t: this.readFloat() },
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
inverseBoneMatrices.push(matrix4x4);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return {
|
|
463
|
+
boneCount,
|
|
464
|
+
usedBoneCount,
|
|
465
|
+
maxWeightsPerVertex,
|
|
466
|
+
boneVertexIndices,
|
|
467
|
+
vertexWeights,
|
|
468
|
+
inverseBoneMatrices,
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
public readAnimNode() :RwAnimNode {
|
|
473
|
+
this.skip(4); // Skipping AnimVersion property (0x100)
|
|
474
|
+
const boneId = this.readInt32();
|
|
475
|
+
const boneCount = this.readInt32();
|
|
476
|
+
const bones :RwBone[] = [];
|
|
477
|
+
|
|
478
|
+
if (boneId == 0) {
|
|
479
|
+
this.skip(8); // Skipping flags and keyFrameSize properties
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (boneCount > 0) {
|
|
483
|
+
for (let i = 0; i < boneCount; i++){
|
|
484
|
+
bones.push({
|
|
485
|
+
boneId: this.readInt32(),
|
|
486
|
+
boneIndex: this.readInt32(),
|
|
487
|
+
flags: this.readInt32()
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return {
|
|
493
|
+
boneId: boneId,
|
|
494
|
+
bonesCount: boneCount,
|
|
495
|
+
bones: bones
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
public readMesh(): RwMesh {
|
|
500
|
+
const indexCount = this.readUint32();
|
|
501
|
+
const materialIndex = this.readUint32();
|
|
502
|
+
|
|
503
|
+
const indices: number[] = [];
|
|
504
|
+
|
|
505
|
+
for (let i = 0; i < indexCount; i++) {
|
|
506
|
+
indices.push(this.readUint32());
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
return {
|
|
510
|
+
indexCount, materialIndex, indices
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
public readMaterialList(): RwMaterialList {
|
|
515
|
+
this.readSectionHeader();
|
|
516
|
+
this.readSectionHeader();
|
|
517
|
+
|
|
518
|
+
const materialInstanceCount = this.readUint32();
|
|
519
|
+
const materialIndices: number[] = [];
|
|
520
|
+
|
|
521
|
+
for (let i = 0; i < materialInstanceCount; i++) {
|
|
522
|
+
const materialIndex = this.readInt32();
|
|
523
|
+
materialIndices.push(materialIndex);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const materialData: RwMaterial[] = [];
|
|
527
|
+
|
|
528
|
+
for (let i = 0; i < materialInstanceCount; i++) {
|
|
529
|
+
let materialIndex = materialIndices[i];
|
|
530
|
+
|
|
531
|
+
if (materialIndex == -1) {
|
|
532
|
+
materialData.push(this.readMaterial());
|
|
533
|
+
} else {
|
|
534
|
+
materialData.push(materialData[materialIndex]);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return { materialInstanceCount, materialData };
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
public readMaterial(): RwMaterial {
|
|
542
|
+
this.readSectionHeader();
|
|
543
|
+
const header = this.readSectionHeader();
|
|
544
|
+
|
|
545
|
+
// Flags - not used
|
|
546
|
+
this.skip(4);
|
|
547
|
+
|
|
548
|
+
const color: RwColor = { r: this.readUint8(), g: this.readUint8(), b: this.readUint8(), a: this.readUint8() };
|
|
549
|
+
|
|
550
|
+
// Unknown - not used
|
|
551
|
+
this.skip(4);
|
|
552
|
+
|
|
553
|
+
const isTextured = this.readUint32() > 0;
|
|
554
|
+
|
|
555
|
+
// Surface properties
|
|
556
|
+
let ambient;
|
|
557
|
+
let specular;
|
|
558
|
+
let diffuse;
|
|
559
|
+
|
|
560
|
+
if (header.versionNumber > 0x30400) {
|
|
561
|
+
ambient = this.readFloat();
|
|
562
|
+
specular = this.readFloat();
|
|
563
|
+
diffuse = this.readFloat();
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
let texture;
|
|
567
|
+
|
|
568
|
+
if (isTextured) {
|
|
569
|
+
texture = this.readTexture();
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Skip various unused extensions
|
|
573
|
+
this.skip(this.readSectionHeader().sectionSize);
|
|
574
|
+
|
|
575
|
+
return { color, isTextured, ambient, specular, diffuse, texture };
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
public readTexture(): RwTexture {
|
|
579
|
+
this.readSectionHeader();
|
|
580
|
+
this.readSectionHeader();
|
|
581
|
+
|
|
582
|
+
const textureData = this.readUint32();
|
|
583
|
+
|
|
584
|
+
const textureFiltering = (textureData & 0xFF);
|
|
585
|
+
const uAddressing = (textureData & 0xF00) >> 8;
|
|
586
|
+
const vAddressing = (textureData & 0xF000) >> 12;
|
|
587
|
+
const usesMipLevels = (textureData & (1 << 16)) !== 0;
|
|
588
|
+
|
|
589
|
+
let textureNameSize = this.readSectionHeader().sectionSize;
|
|
590
|
+
const textureName = this.readString(textureNameSize);
|
|
591
|
+
|
|
592
|
+
// Skip various unused extensions
|
|
593
|
+
this.skip(this.readSectionHeader().sectionSize);
|
|
594
|
+
this.skip(this.readSectionHeader().sectionSize);
|
|
595
|
+
|
|
596
|
+
return { textureFiltering, uAddressing, vAddressing, usesMipLevels, textureName };
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
public readAtomic(): RwAtomic {
|
|
600
|
+
this.readSectionHeader();
|
|
601
|
+
|
|
602
|
+
const frameIndex = this.readUint32();
|
|
603
|
+
const geometryIndex = this.readUint32();
|
|
604
|
+
const flags = this.readUint32();
|
|
605
|
+
|
|
606
|
+
// Skip unused bytes
|
|
607
|
+
this.skip(4);
|
|
608
|
+
|
|
609
|
+
return { frameIndex, geometryIndex, flags };
|
|
610
|
+
}
|
|
611
|
+
}
|