rw-parser-ng 2.1.0 → 2.3.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/README.md +252 -52
- package/lib/codecs/bitmap.d.ts +6 -0
- package/lib/codecs/bitmap.js +77 -0
- package/lib/codecs/color.d.ts +6 -0
- package/lib/codecs/color.js +43 -0
- package/lib/codecs/dxt.d.ts +3 -0
- package/lib/codecs/dxt.js +212 -0
- package/lib/codecs/formats.d.ts +25 -0
- package/lib/codecs/formats.js +32 -0
- package/lib/codecs/index.d.ts +5 -0
- package/lib/codecs/index.js +21 -0
- package/lib/codecs/palette.d.ts +6 -0
- package/lib/codecs/palette.js +86 -0
- package/lib/common/types.d.ts +50 -0
- package/lib/common/types.js +2 -0
- package/lib/core/byte-stream.d.ts +20 -0
- package/lib/core/byte-stream.js +71 -0
- package/lib/core/errors.d.ts +5 -0
- package/lib/core/errors.js +34 -0
- package/lib/core/index.d.ts +5 -0
- package/lib/core/index.js +21 -0
- package/lib/core/rw-file.d.ts +9 -0
- package/lib/core/rw-file.js +36 -0
- package/lib/core/rw-sections.d.ts +24 -0
- package/lib/core/rw-sections.js +28 -0
- package/lib/core/rw-version.d.ts +3 -0
- package/lib/core/rw-version.js +27 -0
- package/lib/dff/dff-model-type.d.ts +5 -0
- package/lib/dff/dff-model-type.js +9 -0
- package/lib/dff/index.d.ts +4 -0
- package/lib/dff/index.js +20 -0
- package/lib/dff/parser.d.ts +5 -0
- package/lib/dff/parser.js +154 -0
- package/lib/dff/readers/2d-effect.d.ts +3 -0
- package/lib/dff/readers/2d-effect.js +211 -0
- package/lib/dff/readers/geometry.d.ts +4 -0
- package/lib/dff/readers/geometry.js +229 -0
- package/lib/dff/readers/index.d.ts +5 -0
- package/lib/dff/readers/index.js +21 -0
- package/lib/dff/readers/material.d.ts +5 -0
- package/lib/dff/readers/material.js +72 -0
- package/lib/dff/readers/mesh.d.ts +5 -0
- package/lib/dff/readers/mesh.js +84 -0
- package/lib/dff/readers/structure.d.ts +6 -0
- package/lib/dff/readers/structure.js +67 -0
- package/lib/dff/types.d.ts +237 -0
- package/lib/dff/types.js +44 -0
- package/lib/fxp/index.d.ts +2 -0
- package/lib/fxp/index.js +18 -0
- package/lib/fxp/info-parser.d.ts +2 -0
- package/lib/fxp/info-parser.js +169 -0
- package/lib/fxp/parser.d.ts +3 -0
- package/lib/fxp/parser.js +285 -0
- package/lib/fxp/types/common.d.ts +36 -0
- package/lib/fxp/types/common.js +2 -0
- package/lib/fxp/types/index.d.ts +3 -0
- package/lib/fxp/types/index.js +19 -0
- package/lib/fxp/types/info.d.ts +186 -0
- package/lib/fxp/types/info.js +2 -0
- package/lib/fxp/types/system.d.ts +27 -0
- package/lib/fxp/types/system.js +2 -0
- package/lib/ifp/index.d.ts +4 -0
- package/lib/ifp/index.js +20 -0
- package/lib/ifp/parser.d.ts +5 -0
- package/lib/ifp/parser.js +38 -0
- package/lib/ifp/readers/anp3.d.ts +3 -0
- package/lib/ifp/readers/anp3.js +56 -0
- package/lib/ifp/readers/anpk.d.ts +3 -0
- package/lib/ifp/readers/anpk.js +86 -0
- package/lib/ifp/types.d.ts +28 -0
- package/lib/ifp/types.js +9 -0
- package/lib/index.d.ts +6 -9
- package/lib/index.js +6 -13
- package/lib/renderware/fxp/FxpData.d.ts +246 -0
- package/lib/renderware/fxp/FxpData.js +2 -0
- package/lib/renderware/fxp/FxpParser.d.ts +3 -0
- package/lib/renderware/fxp/FxpParser.js +512 -0
- package/lib/renderware/fxp/index.d.ts +2 -0
- package/lib/renderware/fxp/index.js +18 -0
- package/lib/renderware/txd/TxdParser.js +3 -2
- package/lib/txd/bitmap-decoder.d.ts +3 -0
- package/lib/txd/bitmap-decoder.js +57 -0
- package/lib/txd/index.d.ts +3 -0
- package/lib/txd/index.js +19 -0
- package/lib/txd/parser.d.ts +8 -0
- package/lib/txd/parser.js +122 -0
- package/lib/txd/types.d.ts +27 -0
- package/lib/txd/types.js +2 -0
- package/package.json +16 -9
- package/src/codecs/bitmap.ts +75 -0
- package/src/codecs/color.ts +40 -0
- package/src/codecs/dxt.ts +243 -0
- package/src/codecs/formats.ts +28 -0
- package/src/codecs/index.ts +5 -0
- package/src/codecs/palette.ts +106 -0
- package/src/common/types.ts +59 -0
- package/src/core/byte-stream.ts +87 -0
- package/src/core/errors.ts +7 -0
- package/src/core/index.ts +5 -0
- package/src/core/rw-file.ts +23 -0
- package/src/core/rw-sections.ts +24 -0
- package/src/core/rw-version.ts +24 -0
- package/src/dff/dff-model-type.ts +5 -0
- package/src/dff/index.ts +4 -0
- package/src/dff/parser.ts +142 -0
- package/src/dff/readers/2d-effect.ts +265 -0
- package/src/dff/readers/geometry.ts +264 -0
- package/src/dff/readers/index.ts +5 -0
- package/src/dff/readers/material.ts +89 -0
- package/src/dff/readers/mesh.ts +93 -0
- package/src/dff/readers/structure.ts +79 -0
- package/src/dff/types.ts +278 -0
- package/src/fxp/index.ts +2 -0
- package/src/fxp/info-parser.ts +166 -0
- package/src/fxp/parser.ts +323 -0
- package/src/fxp/types/common.ts +23 -0
- package/src/fxp/types/index.ts +3 -0
- package/src/fxp/types/info.ts +251 -0
- package/src/fxp/types/system.ts +30 -0
- package/src/ifp/index.ts +4 -0
- package/src/ifp/parser.ts +16 -0
- package/src/ifp/readers/anp3.ts +75 -0
- package/src/ifp/readers/anpk.ts +110 -0
- package/src/ifp/types.ts +33 -0
- package/src/index.ts +6 -12
- package/src/txd/bitmap-decoder.ts +73 -0
- package/src/txd/index.ts +3 -0
- package/src/txd/parser.ts +148 -0
- package/src/txd/types.ts +29 -0
- package/src/renderware/RwFile.ts +0 -26
- package/src/renderware/RwSections.ts +0 -27
- package/src/renderware/common/types.ts +0 -59
- package/src/renderware/dff/DffModelType.ts +0 -5
- package/src/renderware/dff/DffParser.ts +0 -611
- package/src/renderware/errors/RwParseError.ts +0 -11
- package/src/renderware/ifp/IfpData.ts +0 -33
- package/src/renderware/ifp/IfpParser.ts +0 -203
- package/src/renderware/txd/TxdParser.ts +0 -234
- package/src/renderware/utils/ImageDecoder.ts +0 -571
- package/src/renderware/utils/ImageFormatEnums.ts +0 -28
- package/src/renderware/utils/RwVersion.ts +0 -28
- package/src/utils/ByteStream.ts +0 -80
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import type { RwFile } from "../../core/rw-file";
|
|
2
|
+
import {
|
|
3
|
+
Rw2dEffectType,
|
|
4
|
+
type Rw2dEffect,
|
|
5
|
+
type Rw2dEffectAttractor,
|
|
6
|
+
type Rw2dEffectCoverPoint,
|
|
7
|
+
type Rw2dEffectEnEx,
|
|
8
|
+
type Rw2dEffectEscalator,
|
|
9
|
+
type Rw2dEffectGeneric,
|
|
10
|
+
type Rw2dEffectLight,
|
|
11
|
+
type Rw2dEffectParticle,
|
|
12
|
+
type Rw2dEffectRoadsign,
|
|
13
|
+
} from "../types";
|
|
14
|
+
|
|
15
|
+
export function read2dEffects(file: RwFile, sectionSize: number): Rw2dEffect[] {
|
|
16
|
+
const effects: Rw2dEffect[] = [];
|
|
17
|
+
const endPosition = file.getPosition() + sectionSize;
|
|
18
|
+
const effectCount = file.readUint32();
|
|
19
|
+
|
|
20
|
+
for (let i = 0; i < effectCount && file.getPosition() < endPosition; i++) {
|
|
21
|
+
const effect = read2dEffect(file);
|
|
22
|
+
if (effect) effects.push(effect);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
file.setPosition(endPosition);
|
|
26
|
+
return effects;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function read2dEffect(file: RwFile): Rw2dEffect | null {
|
|
30
|
+
const position = {
|
|
31
|
+
x: file.readFloat(),
|
|
32
|
+
y: file.readFloat(),
|
|
33
|
+
z: file.readFloat(),
|
|
34
|
+
};
|
|
35
|
+
const type = file.readUint32() as Rw2dEffectType;
|
|
36
|
+
const dataSize = file.readUint32();
|
|
37
|
+
const dataStart = file.getPosition();
|
|
38
|
+
|
|
39
|
+
let effect: Rw2dEffect | null = null;
|
|
40
|
+
|
|
41
|
+
switch (type) {
|
|
42
|
+
case Rw2dEffectType.LIGHT:
|
|
43
|
+
effect = readLight(file, position);
|
|
44
|
+
break;
|
|
45
|
+
case Rw2dEffectType.PARTICLE:
|
|
46
|
+
effect = readParticle(file, position);
|
|
47
|
+
break;
|
|
48
|
+
case Rw2dEffectType.ATTRACTOR:
|
|
49
|
+
effect = readAttractor(file, position);
|
|
50
|
+
break;
|
|
51
|
+
case Rw2dEffectType.ENEX:
|
|
52
|
+
effect = readEnEx(file, position);
|
|
53
|
+
break;
|
|
54
|
+
case Rw2dEffectType.ROADSIGN:
|
|
55
|
+
effect = readRoadsign(file, position);
|
|
56
|
+
break;
|
|
57
|
+
case Rw2dEffectType.COVER_POINT:
|
|
58
|
+
effect = readCoverPoint(file, position);
|
|
59
|
+
break;
|
|
60
|
+
case Rw2dEffectType.ESCALATOR:
|
|
61
|
+
effect = readEscalator(file, position);
|
|
62
|
+
break;
|
|
63
|
+
default:
|
|
64
|
+
effect = readGeneric(file, position, type, dataSize);
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
file.setPosition(dataStart + dataSize);
|
|
69
|
+
return effect;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function readLight(
|
|
73
|
+
file: RwFile,
|
|
74
|
+
position: { x: number; y: number; z: number },
|
|
75
|
+
): Rw2dEffectLight {
|
|
76
|
+
const color = {
|
|
77
|
+
r: file.readUint8(),
|
|
78
|
+
g: file.readUint8(),
|
|
79
|
+
b: file.readUint8(),
|
|
80
|
+
a: file.readUint8(),
|
|
81
|
+
};
|
|
82
|
+
const coronaFarClip = file.readFloat();
|
|
83
|
+
const pointlightRange = file.readFloat();
|
|
84
|
+
const coronaSize = file.readFloat();
|
|
85
|
+
const shadowSize = file.readFloat();
|
|
86
|
+
const coronaShowMode = file.readUint8();
|
|
87
|
+
const coronaEnableReflection = file.readUint8() !== 0;
|
|
88
|
+
const coronaFlareType = file.readUint8();
|
|
89
|
+
const shadowColorMultiplier = file.readUint8();
|
|
90
|
+
const flags1 = file.readUint8();
|
|
91
|
+
const coronaTexName = file.readString(24);
|
|
92
|
+
const shadowTexName = file.readString(24);
|
|
93
|
+
const shadowZDistance = file.readUint8();
|
|
94
|
+
const flags2 = file.readUint8();
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
type: Rw2dEffectType.LIGHT,
|
|
98
|
+
position,
|
|
99
|
+
color,
|
|
100
|
+
coronaFarClip,
|
|
101
|
+
pointlightRange,
|
|
102
|
+
coronaSize,
|
|
103
|
+
shadowSize,
|
|
104
|
+
coronaShowMode,
|
|
105
|
+
coronaEnableReflection,
|
|
106
|
+
coronaFlareType,
|
|
107
|
+
shadowColorMultiplier,
|
|
108
|
+
flags1,
|
|
109
|
+
coronaTexName,
|
|
110
|
+
shadowTexName,
|
|
111
|
+
shadowZDistance,
|
|
112
|
+
flags2,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function readParticle(
|
|
117
|
+
file: RwFile,
|
|
118
|
+
position: { x: number; y: number; z: number },
|
|
119
|
+
): Rw2dEffectParticle {
|
|
120
|
+
const effectName = file.readString(24).replace(/\0+$/, "");
|
|
121
|
+
return { type: Rw2dEffectType.PARTICLE, position, effectName };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function readAttractor(
|
|
125
|
+
file: RwFile,
|
|
126
|
+
position: { x: number; y: number; z: number },
|
|
127
|
+
): Rw2dEffectAttractor {
|
|
128
|
+
const queueDir = {
|
|
129
|
+
x: file.readFloat(),
|
|
130
|
+
y: file.readFloat(),
|
|
131
|
+
z: file.readFloat(),
|
|
132
|
+
};
|
|
133
|
+
const useDir = {
|
|
134
|
+
x: file.readFloat(),
|
|
135
|
+
y: file.readFloat(),
|
|
136
|
+
z: file.readFloat(),
|
|
137
|
+
};
|
|
138
|
+
const forwardDir = {
|
|
139
|
+
x: file.readFloat(),
|
|
140
|
+
y: file.readFloat(),
|
|
141
|
+
z: file.readFloat(),
|
|
142
|
+
};
|
|
143
|
+
const attractorType = file.readInt8();
|
|
144
|
+
const pedExistingProbability = file.readUint8();
|
|
145
|
+
file.skip(1);
|
|
146
|
+
const flags = file.readUint8();
|
|
147
|
+
const scriptName = file.readString(8).replace(/\0+$/, "");
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
type: Rw2dEffectType.ATTRACTOR,
|
|
151
|
+
position,
|
|
152
|
+
queueDir,
|
|
153
|
+
useDir,
|
|
154
|
+
forwardDir,
|
|
155
|
+
attractorType,
|
|
156
|
+
pedExistingProbability,
|
|
157
|
+
flags,
|
|
158
|
+
scriptName,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function readEnEx(
|
|
163
|
+
file: RwFile,
|
|
164
|
+
position: { x: number; y: number; z: number },
|
|
165
|
+
): Rw2dEffectEnEx {
|
|
166
|
+
const enterAngle = file.readFloat();
|
|
167
|
+
const radiusX = file.readFloat();
|
|
168
|
+
const radiusY = file.readFloat();
|
|
169
|
+
const exitPos = {
|
|
170
|
+
x: file.readFloat(),
|
|
171
|
+
y: file.readFloat(),
|
|
172
|
+
z: file.readFloat(),
|
|
173
|
+
};
|
|
174
|
+
const exitAngle = file.readFloat();
|
|
175
|
+
const interiorId = file.readInt16();
|
|
176
|
+
const flags1 = file.readUint8();
|
|
177
|
+
const skyColor = file.readUint8();
|
|
178
|
+
const interiorName = file.readString(8).replace(/\0+$/, "");
|
|
179
|
+
const timeOn = file.readUint8();
|
|
180
|
+
const timeOff = file.readUint8();
|
|
181
|
+
const flags2 = file.readUint8();
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
type: Rw2dEffectType.ENEX,
|
|
185
|
+
position,
|
|
186
|
+
enterAngle,
|
|
187
|
+
radiusX,
|
|
188
|
+
radiusY,
|
|
189
|
+
exitPos,
|
|
190
|
+
exitAngle,
|
|
191
|
+
interiorId,
|
|
192
|
+
flags1,
|
|
193
|
+
skyColor,
|
|
194
|
+
interiorName,
|
|
195
|
+
timeOn,
|
|
196
|
+
timeOff,
|
|
197
|
+
flags2,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function readRoadsign(
|
|
202
|
+
file: RwFile,
|
|
203
|
+
position: { x: number; y: number; z: number },
|
|
204
|
+
): Rw2dEffectRoadsign {
|
|
205
|
+
const size = { x: file.readFloat(), y: file.readFloat() };
|
|
206
|
+
const rotation = {
|
|
207
|
+
x: file.readFloat(),
|
|
208
|
+
y: file.readFloat(),
|
|
209
|
+
z: file.readFloat(),
|
|
210
|
+
};
|
|
211
|
+
const flags = file.readUint16();
|
|
212
|
+
file.skip(2);
|
|
213
|
+
file.skip(8);
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
type: Rw2dEffectType.ROADSIGN,
|
|
217
|
+
position,
|
|
218
|
+
size,
|
|
219
|
+
rotation,
|
|
220
|
+
flags,
|
|
221
|
+
text: "",
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function readCoverPoint(
|
|
226
|
+
file: RwFile,
|
|
227
|
+
position: { x: number; y: number; z: number },
|
|
228
|
+
): Rw2dEffectCoverPoint {
|
|
229
|
+
const coverDir = { x: file.readFloat(), y: file.readFloat() };
|
|
230
|
+
const usage = file.readUint32();
|
|
231
|
+
return { type: Rw2dEffectType.COVER_POINT, position, coverDir, usage };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function readEscalator(
|
|
235
|
+
file: RwFile,
|
|
236
|
+
position: { x: number; y: number; z: number },
|
|
237
|
+
): Rw2dEffectEscalator {
|
|
238
|
+
const bottom = {
|
|
239
|
+
x: file.readFloat(),
|
|
240
|
+
y: file.readFloat(),
|
|
241
|
+
z: file.readFloat(),
|
|
242
|
+
};
|
|
243
|
+
const top = { x: file.readFloat(), y: file.readFloat(), z: file.readFloat() };
|
|
244
|
+
const end = { x: file.readFloat(), y: file.readFloat(), z: file.readFloat() };
|
|
245
|
+
const direction = file.readUint8();
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
type: Rw2dEffectType.ESCALATOR,
|
|
249
|
+
position,
|
|
250
|
+
bottom,
|
|
251
|
+
top,
|
|
252
|
+
end,
|
|
253
|
+
direction,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function readGeneric(
|
|
258
|
+
file: RwFile,
|
|
259
|
+
position: { x: number; y: number; z: number },
|
|
260
|
+
type: Rw2dEffectType,
|
|
261
|
+
dataSize: number,
|
|
262
|
+
): Rw2dEffectGeneric {
|
|
263
|
+
const data = file.readBytes(dataSize);
|
|
264
|
+
return { type: type as Rw2dEffectGeneric["type"], position, data };
|
|
265
|
+
}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import type { RwTextureCoordinate } from "../../common/types";
|
|
2
|
+
import type { RwFile, RwSectionHeader } from "../../core/rw-file";
|
|
3
|
+
import { RwSections } from "../../core/rw-sections";
|
|
4
|
+
import { unpackVersion } from "../../core/rw-version";
|
|
5
|
+
import type {
|
|
6
|
+
Rw2dEffect,
|
|
7
|
+
RwBreakable,
|
|
8
|
+
RwGeometry,
|
|
9
|
+
RwGeometryList,
|
|
10
|
+
} from "../types";
|
|
11
|
+
import { read2dEffects } from "./2d-effect";
|
|
12
|
+
import { readMaterialList } from "./material";
|
|
13
|
+
import { readBinMesh, readSkin } from "./mesh";
|
|
14
|
+
|
|
15
|
+
export function readGeometryList(
|
|
16
|
+
file: RwFile,
|
|
17
|
+
header: RwSectionHeader,
|
|
18
|
+
): RwGeometryList {
|
|
19
|
+
file.readSectionHeader();
|
|
20
|
+
const geometricObjectCount = file.readUint32();
|
|
21
|
+
const geometries: RwGeometry[] = [];
|
|
22
|
+
const versionNumber = unpackVersion(header.versionNumber);
|
|
23
|
+
|
|
24
|
+
for (let i = 0; i < geometricObjectCount; i++) {
|
|
25
|
+
file.readSectionHeader();
|
|
26
|
+
file.readSectionHeader();
|
|
27
|
+
geometries.push(readGeometry(file, versionNumber));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return { geometricObjectCount, geometries };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function readGeometry(file: RwFile, versionNumber: number): RwGeometry {
|
|
34
|
+
const flags = file.readUint16();
|
|
35
|
+
const textureCoordinatesCount = file.readUint8();
|
|
36
|
+
file.readUint8();
|
|
37
|
+
const triangleCount = file.readUint32();
|
|
38
|
+
const vertexCount = file.readUint32();
|
|
39
|
+
file.readUint32();
|
|
40
|
+
|
|
41
|
+
if (versionNumber < 0x34000) {
|
|
42
|
+
file.skip(12);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const isTriangleStrip = (flags & (1 << 0)) !== 0;
|
|
46
|
+
const isTexturedUV1 = (flags & (1 << 2)) !== 0;
|
|
47
|
+
const isGeometryPrelit = (flags & (1 << 3)) !== 0;
|
|
48
|
+
const isTexturedUV2 = (flags & (1 << 7)) !== 0;
|
|
49
|
+
|
|
50
|
+
const vertexColorInformation = [];
|
|
51
|
+
if (isGeometryPrelit) {
|
|
52
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
53
|
+
vertexColorInformation.push({
|
|
54
|
+
r: file.readUint8(),
|
|
55
|
+
g: file.readUint8(),
|
|
56
|
+
b: file.readUint8(),
|
|
57
|
+
a: file.readUint8(),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const textureMappingInformation: RwTextureCoordinate[][] = [];
|
|
63
|
+
if (isTexturedUV1 || isTexturedUV2) {
|
|
64
|
+
for (let t = 0; t < textureCoordinatesCount; t++) {
|
|
65
|
+
textureMappingInformation[t] = [];
|
|
66
|
+
for (let v = 0; v < vertexCount; v++) {
|
|
67
|
+
textureMappingInformation[t][v] = {
|
|
68
|
+
u: file.readFloat(),
|
|
69
|
+
v: file.readFloat(),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const triangleInformation = [];
|
|
76
|
+
for (let i = 0; i < triangleCount; i++) {
|
|
77
|
+
const vertex2 = file.readUint16();
|
|
78
|
+
const vertex1 = file.readUint16();
|
|
79
|
+
const materialId = file.readUint16();
|
|
80
|
+
const vertex3 = file.readUint16();
|
|
81
|
+
triangleInformation.push({
|
|
82
|
+
vector: { x: vertex1, y: vertex2, z: vertex3 },
|
|
83
|
+
materialId,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const boundingSphere = {
|
|
88
|
+
vector: { x: file.readFloat(), y: file.readFloat(), z: file.readFloat() },
|
|
89
|
+
radius: file.readFloat(),
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const hasVertices = !!file.readUint32();
|
|
93
|
+
const hasNormals = !!file.readUint32();
|
|
94
|
+
|
|
95
|
+
const vertexInformation = [];
|
|
96
|
+
if (hasVertices) {
|
|
97
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
98
|
+
vertexInformation.push({
|
|
99
|
+
x: file.readFloat(),
|
|
100
|
+
y: file.readFloat(),
|
|
101
|
+
z: file.readFloat(),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const normalInformation = [];
|
|
107
|
+
if (hasNormals) {
|
|
108
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
109
|
+
normalInformation.push({
|
|
110
|
+
x: file.readFloat(),
|
|
111
|
+
y: file.readFloat(),
|
|
112
|
+
z: file.readFloat(),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const materialList = readMaterialList(file);
|
|
118
|
+
const extensionSize = file.readSectionHeader().sectionSize;
|
|
119
|
+
let binMesh;
|
|
120
|
+
let skin;
|
|
121
|
+
let effects2d: Rw2dEffect[] | undefined;
|
|
122
|
+
let extraVertColour;
|
|
123
|
+
let breakable: RwBreakable | undefined;
|
|
124
|
+
let relativePosition = 0;
|
|
125
|
+
|
|
126
|
+
while (relativePosition < extensionSize) {
|
|
127
|
+
const extHeader = file.readSectionHeader();
|
|
128
|
+
relativePosition += extHeader.sectionSize + 12;
|
|
129
|
+
const sectionStart = file.getPosition();
|
|
130
|
+
|
|
131
|
+
switch (extHeader.sectionType) {
|
|
132
|
+
case RwSections.RwBinMesh:
|
|
133
|
+
binMesh = readBinMesh(file);
|
|
134
|
+
break;
|
|
135
|
+
case RwSections.RwSkin:
|
|
136
|
+
skin = readSkin(file, vertexCount);
|
|
137
|
+
break;
|
|
138
|
+
case RwSections.Rw2dEffect:
|
|
139
|
+
effects2d = read2dEffects(file, extHeader.sectionSize);
|
|
140
|
+
break;
|
|
141
|
+
case RwSections.RwExtraVertColour:
|
|
142
|
+
extraVertColour = [];
|
|
143
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
144
|
+
extraVertColour.push({
|
|
145
|
+
r: file.readUint8(),
|
|
146
|
+
g: file.readUint8(),
|
|
147
|
+
b: file.readUint8(),
|
|
148
|
+
a: file.readUint8(),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
break;
|
|
152
|
+
case RwSections.RwBreakable:
|
|
153
|
+
breakable = readBreakable(file);
|
|
154
|
+
break;
|
|
155
|
+
default:
|
|
156
|
+
file.skip(extHeader.sectionSize);
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
file.setPosition(sectionStart + extHeader.sectionSize);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
textureCoordinatesCount,
|
|
165
|
+
textureMappingInformation,
|
|
166
|
+
boundingSphere,
|
|
167
|
+
hasVertices,
|
|
168
|
+
hasNormals,
|
|
169
|
+
isTriangleStrip,
|
|
170
|
+
vertexColorInformation,
|
|
171
|
+
vertexInformation,
|
|
172
|
+
normalInformation,
|
|
173
|
+
triangleInformation,
|
|
174
|
+
materialList,
|
|
175
|
+
binMesh,
|
|
176
|
+
skin,
|
|
177
|
+
effects2d,
|
|
178
|
+
extraVertColour,
|
|
179
|
+
breakable,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function readBreakable(file: RwFile): RwBreakable | undefined {
|
|
184
|
+
const sectionHeader = file.readUint32();
|
|
185
|
+
if (!sectionHeader) return undefined;
|
|
186
|
+
|
|
187
|
+
const positionRule = file.readUint32();
|
|
188
|
+
const numVertices = file.readUint16();
|
|
189
|
+
file.skip(2);
|
|
190
|
+
file.skip(4 * 3);
|
|
191
|
+
const numTriangles = file.readUint16();
|
|
192
|
+
file.skip(2);
|
|
193
|
+
file.skip(4 * 2);
|
|
194
|
+
const numMaterials = file.readUint16();
|
|
195
|
+
file.skip(2);
|
|
196
|
+
file.skip(4 * 4);
|
|
197
|
+
|
|
198
|
+
const vertices = [];
|
|
199
|
+
for (let i = 0; i < numVertices; i++) {
|
|
200
|
+
vertices.push({
|
|
201
|
+
x: file.readFloat(),
|
|
202
|
+
y: file.readFloat(),
|
|
203
|
+
z: file.readFloat(),
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const texCoords = [];
|
|
208
|
+
for (let i = 0; i < numVertices; i++) {
|
|
209
|
+
texCoords.push({ u: file.readFloat(), v: file.readFloat() });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const colors = [];
|
|
213
|
+
for (let i = 0; i < numVertices; i++) {
|
|
214
|
+
colors.push({
|
|
215
|
+
r: file.readUint8(),
|
|
216
|
+
g: file.readUint8(),
|
|
217
|
+
b: file.readUint8(),
|
|
218
|
+
a: file.readUint8(),
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const triangles = [];
|
|
223
|
+
for (let i = 0; i < numTriangles; i++) {
|
|
224
|
+
triangles.push({
|
|
225
|
+
a: file.readUint16(),
|
|
226
|
+
b: file.readUint16(),
|
|
227
|
+
c: file.readUint16(),
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const triangleMaterialIndices = [];
|
|
232
|
+
for (let i = 0; i < numTriangles; i++) {
|
|
233
|
+
triangleMaterialIndices.push(file.readUint16());
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const textureNames: string[] = [];
|
|
237
|
+
for (let i = 0; i < numMaterials; i++) {
|
|
238
|
+
textureNames.push(file.readString(32).replace(/\0+$/, ""));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const maskNames: string[] = [];
|
|
242
|
+
for (let i = 0; i < numMaterials; i++) {
|
|
243
|
+
maskNames.push(file.readString(32).replace(/\0+$/, ""));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const materials = [];
|
|
247
|
+
for (let i = 0; i < numMaterials; i++) {
|
|
248
|
+
materials.push({
|
|
249
|
+
textureName: textureNames[i],
|
|
250
|
+
maskName: maskNames[i],
|
|
251
|
+
color: { r: file.readFloat(), g: file.readFloat(), b: file.readFloat() },
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
positionRule,
|
|
257
|
+
vertices,
|
|
258
|
+
texCoords,
|
|
259
|
+
colors,
|
|
260
|
+
triangles,
|
|
261
|
+
triangleMaterialIndices,
|
|
262
|
+
materials,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { RwFile } from "../../core/rw-file";
|
|
2
|
+
import type { RwMaterial, RwMaterialList, RwTexture } from "../types";
|
|
3
|
+
|
|
4
|
+
export function readTexture(file: RwFile): RwTexture {
|
|
5
|
+
file.readSectionHeader();
|
|
6
|
+
file.readSectionHeader();
|
|
7
|
+
|
|
8
|
+
const textureData = file.readUint32();
|
|
9
|
+
const textureFiltering = textureData & 0xff;
|
|
10
|
+
const uAddressing = (textureData & 0xf00) >> 8;
|
|
11
|
+
const vAddressing = (textureData & 0xf000) >> 12;
|
|
12
|
+
const usesMipLevels = (textureData & (1 << 16)) !== 0;
|
|
13
|
+
|
|
14
|
+
const textureNameSize = file.readSectionHeader().sectionSize;
|
|
15
|
+
const textureName = file.readString(textureNameSize);
|
|
16
|
+
|
|
17
|
+
file.skip(file.readSectionHeader().sectionSize);
|
|
18
|
+
file.skip(file.readSectionHeader().sectionSize);
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
textureFiltering,
|
|
22
|
+
uAddressing,
|
|
23
|
+
vAddressing,
|
|
24
|
+
usesMipLevels,
|
|
25
|
+
textureName,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function readMaterial(file: RwFile): RwMaterial {
|
|
30
|
+
file.readSectionHeader();
|
|
31
|
+
const header = file.readSectionHeader();
|
|
32
|
+
|
|
33
|
+
file.skip(4);
|
|
34
|
+
|
|
35
|
+
const color = {
|
|
36
|
+
r: file.readUint8(),
|
|
37
|
+
g: file.readUint8(),
|
|
38
|
+
b: file.readUint8(),
|
|
39
|
+
a: file.readUint8(),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
file.skip(4);
|
|
43
|
+
|
|
44
|
+
const isTextured = file.readUint32() > 0;
|
|
45
|
+
|
|
46
|
+
let ambient: number | undefined;
|
|
47
|
+
let specular: number | undefined;
|
|
48
|
+
let diffuse: number | undefined;
|
|
49
|
+
|
|
50
|
+
if (header.versionNumber > 0x30400) {
|
|
51
|
+
ambient = file.readFloat();
|
|
52
|
+
specular = file.readFloat();
|
|
53
|
+
diffuse = file.readFloat();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let texture: RwTexture | undefined;
|
|
57
|
+
if (isTextured) {
|
|
58
|
+
texture = readTexture(file);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
file.skip(file.readSectionHeader().sectionSize);
|
|
62
|
+
|
|
63
|
+
return { color, isTextured, ambient, specular, diffuse, texture };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function readMaterialList(file: RwFile): RwMaterialList {
|
|
67
|
+
file.readSectionHeader();
|
|
68
|
+
file.readSectionHeader();
|
|
69
|
+
|
|
70
|
+
const materialInstanceCount = file.readUint32();
|
|
71
|
+
const materialIndices: number[] = [];
|
|
72
|
+
|
|
73
|
+
for (let i = 0; i < materialInstanceCount; i++) {
|
|
74
|
+
materialIndices.push(file.readInt32());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const materialData: RwMaterial[] = [];
|
|
78
|
+
|
|
79
|
+
for (let i = 0; i < materialInstanceCount; i++) {
|
|
80
|
+
const materialIndex = materialIndices[i];
|
|
81
|
+
if (materialIndex === -1) {
|
|
82
|
+
materialData.push(readMaterial(file));
|
|
83
|
+
} else {
|
|
84
|
+
materialData.push(materialData[materialIndex]);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { materialInstanceCount, materialData };
|
|
89
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { RwFile } from "../../core/rw-file";
|
|
2
|
+
import type { RwBinMesh, RwMesh, RwSkin } from "../types";
|
|
3
|
+
|
|
4
|
+
export function readMesh(file: RwFile): RwMesh {
|
|
5
|
+
const indexCount = file.readUint32();
|
|
6
|
+
const materialIndex = file.readUint32();
|
|
7
|
+
const indices: number[] = [];
|
|
8
|
+
|
|
9
|
+
for (let i = 0; i < indexCount; i++) {
|
|
10
|
+
indices.push(file.readUint32());
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return { indexCount, materialIndex, indices };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function readBinMesh(file: RwFile): RwBinMesh {
|
|
17
|
+
file.skip(4);
|
|
18
|
+
const meshCount = file.readUint32();
|
|
19
|
+
file.skip(4);
|
|
20
|
+
|
|
21
|
+
const meshes: RwMesh[] = [];
|
|
22
|
+
for (let i = 0; i < meshCount; i++) {
|
|
23
|
+
meshes.push(readMesh(file));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return { meshCount, meshes };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function readSkin(file: RwFile, vertexCount: number): RwSkin {
|
|
30
|
+
const boneCount = file.readUint8();
|
|
31
|
+
const usedBoneCount = file.readUint8();
|
|
32
|
+
const maxWeightsPerVertex = file.readUint8();
|
|
33
|
+
|
|
34
|
+
file.skip(1);
|
|
35
|
+
file.skip(usedBoneCount);
|
|
36
|
+
|
|
37
|
+
const boneVertexIndices: number[][] = [];
|
|
38
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
39
|
+
const indices: number[] = [];
|
|
40
|
+
for (let j = 0; j < 4; j++) {
|
|
41
|
+
indices.push(file.readUint8());
|
|
42
|
+
}
|
|
43
|
+
boneVertexIndices.push(indices);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const vertexWeights: number[][] = [];
|
|
47
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
48
|
+
const weights: number[] = [];
|
|
49
|
+
for (let j = 0; j < 4; j++) {
|
|
50
|
+
weights.push(file.readFloat());
|
|
51
|
+
}
|
|
52
|
+
vertexWeights.push(weights);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const inverseBoneMatrices = [];
|
|
56
|
+
for (let i = 0; i < boneCount; i++) {
|
|
57
|
+
inverseBoneMatrices.push({
|
|
58
|
+
right: {
|
|
59
|
+
x: file.readFloat(),
|
|
60
|
+
y: file.readFloat(),
|
|
61
|
+
z: file.readFloat(),
|
|
62
|
+
t: file.readFloat(),
|
|
63
|
+
},
|
|
64
|
+
up: {
|
|
65
|
+
x: file.readFloat(),
|
|
66
|
+
y: file.readFloat(),
|
|
67
|
+
z: file.readFloat(),
|
|
68
|
+
t: file.readFloat(),
|
|
69
|
+
},
|
|
70
|
+
at: {
|
|
71
|
+
x: file.readFloat(),
|
|
72
|
+
y: file.readFloat(),
|
|
73
|
+
z: file.readFloat(),
|
|
74
|
+
t: file.readFloat(),
|
|
75
|
+
},
|
|
76
|
+
transform: {
|
|
77
|
+
x: file.readFloat(),
|
|
78
|
+
y: file.readFloat(),
|
|
79
|
+
z: file.readFloat(),
|
|
80
|
+
t: file.readFloat(),
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
boneCount,
|
|
87
|
+
usedBoneCount,
|
|
88
|
+
maxWeightsPerVertex,
|
|
89
|
+
boneVertexIndices,
|
|
90
|
+
vertexWeights,
|
|
91
|
+
inverseBoneMatrices,
|
|
92
|
+
};
|
|
93
|
+
}
|