6cb09b03bfef416b2476ecdae0e91e39f844b209
[tar-legacy.git] / MCDV / vtx.hpp
1 #pragma once
2 #include <string>
3 #include <vector>
4 #include <iostream>
5 #include <fstream>
6
7 #include "util.h"
8
9 namespace vtx
10 {
11 //Make sure everything is nice and together
12 #pragma pack(push, 1)
13
14 struct Vertex
15 {
16 // these index into the mesh's vert[origMeshVertID]'s bones
17 unsigned char boneWeightIndex[3];
18 unsigned char numBones;
19
20 unsigned short origMeshVertID;
21
22 // for sw skinned verts, these are indices into the global list of bones
23 // for hw skinned verts, these are hardware bone indices
24 char boneID[3];
25 };
26
27 enum StripGroupFlags
28 {
29 STRIPGROUP_IS_FLEXED = 0x01,
30 STRIPGROUP_IS_HWSKINNED = 0x02,
31 STRIPGROUP_IS_DELTA_FLEXED = 0x04,
32 STRIPGROUP_SUPPRESS_HW_MORPH = 0x08, // NOTE: This is a temporary flag used at run time.
33 };
34
35 // A strip is a piece of a stripgroup which is divided by bones
36 struct StripHeader
37 {
38 //Indices array
39 int numIndices;
40 int indexOffset;
41
42 //Vertices array
43 int numVerts;
44 int vertOffset;
45
46 short numBones;
47
48 unsigned char flags;
49
50 int numBoneStateChanges;
51 int boneStateChangeOffset;
52 };
53
54 // a locking group
55 // a single vertex buffer
56 // a single index buffer
57 struct StripGroupHeader
58 {
59 // These are the arrays of all verts and indices for this mesh. strips index into this.
60 int numVerts;
61 int vertOffset;
62
63 int numIndices;
64 int indexOffset;
65
66 int numStrips;
67 int stripOffset;
68
69 unsigned char flags;
70 };
71
72 struct MeshHeader
73 {
74 int numStripGroups;
75 int stripGroupHeaderOffset;
76
77 unsigned char flags;
78 };
79
80 struct ModelLODHeader
81 {
82 //Mesh array
83 int numMeshes;
84 int meshOffset;
85
86 float switchPoint;
87 };
88
89 // This maps one to one with models in the mdl file.
90 struct ModelHeader
91 {
92 //LOD mesh array
93 int numLODs; //This is also specified in FileHeader_t
94 int lodOffset;
95 };
96
97 struct BodyPartHeader
98 {
99 //Model array
100 int numModels;
101 int modelOffset;
102 };
103
104 struct FileHeader
105 {
106 // file version as defined by OPTIMIZED_MODEL_FILE_VERSION (currently 7)
107 int version;
108
109 // hardware params that affect how the model is to be optimized.
110 int vertCacheSize;
111 unsigned short maxBonesPerStrip;
112 unsigned short maxBonesPerTri;
113 int maxBonesPerVert;
114
115 // must match checkSum in the .mdl
116 int checkSum;
117
118 int numLODs; // Also specified in ModelHeader_t's and should match
119
120 // Offset to materialReplacementList Array. one of these for each LOD, 8 in total
121 int materialReplacementListOffset;
122
123 //Defines the size and location of the body part array
124 int numBodyParts;
125 int bodyPartOffset;
126 };
127
128 /*
129 .VTX file structure
130 =============================================
131
132 FileHeader
133 L BodyParts::
134 L Models::
135 L LODS::
136 L Meshes::
137 L StripGroups::
138 L VerticesTable[StudioMDL.Vertex]
139 L IndicesTable[UINT16]
140 |
141 L Strips::
142 L Vertices[UINT16]
143 L Indices[UINT16]
144 */
145
146 #pragma pack(pop)
147 }
148
149 class vtx_mesh : public util::verboseControl
150 {
151 public:
152 std::vector<unsigned short> vertexSequence;
153 vtx::FileHeader header;
154
155 vtx_mesh(std::string filepath, bool verbose = false)
156 {
157 this->use_verbose = verbose;
158
159 //Create file handle
160 std::ifstream reader(filepath, std::ios::in | std::ios::binary);
161
162 if (!reader) {
163 throw std::exception("VTX::LOAD FAILED"); return;
164 }
165
166 //Read header
167 reader.read((char*)&this->header, sizeof(this->header));
168 this->debug("VTX version:", this->header.version);
169 this->debug("Num LODS:", this->header.numLODs);
170
171 /* Read bulk of .VTX file */
172
173 /* Body part array */
174 reader.seekg(header.bodyPartOffset);
175 int abs_body_base_offset = reader.tellg();
176
177 for (int body = 0; body < header.numBodyParts; body++)
178 {
179 //Move to current body part array item
180 reader.seekg(abs_body_base_offset);
181 reader.seekg(body * sizeof(vtx::BodyPartHeader), std::ios::cur);
182
183 //Read the body part
184 vtx::BodyPartHeader BODY;
185 reader.read((char*)&BODY, sizeof(BODY));
186
187 /* Model array */
188 reader.seekg(BODY.modelOffset - sizeof(vtx::BodyPartHeader), std::ios::cur);
189 int abs_model_base_offset = reader.tellg();
190
191 int total_verts = 0;
192 //NOTE: Total verts may need to be initialized outside the body array
193
194 for (int model = 0; model < BODY.numModels; model++)
195 {
196 //Move to current model array item
197 reader.seekg(abs_model_base_offset);
198 reader.seekg(model * sizeof(vtx::ModelHeader), std::ios::cur);
199
200 //Read the Model
201 vtx::ModelHeader MODEL;
202 reader.read((char*)&MODEL, sizeof(MODEL));
203
204 /* LOD array */
205 reader.seekg(MODEL.lodOffset - sizeof(vtx::ModelHeader), std::ios::cur);
206 int abs_lod_base_offset = reader.tellg();
207
208 for (int lod = 0; lod < MODEL.numLODs; lod++){
209 if (lod > 0) goto IL_EXIT; // Skip all the other lods for now
210
211 //Move to the current LOD header array item
212 reader.seekg(abs_lod_base_offset);
213 reader.seekg(lod * sizeof(vtx::ModelLODHeader), std::ios::cur);
214
215 //Read the LOD header
216 vtx::ModelLODHeader LOD;
217 reader.read((char*)&LOD, sizeof(LOD));
218
219 /* Mesh array */
220 reader.seekg(LOD.meshOffset - sizeof(vtx::ModelLODHeader), std::ios::cur);
221 int abs_mesh_base_offset = reader.tellg();
222
223 for (int mesh = 0; mesh < LOD.numMeshes; mesh++)
224 {
225 //Move to the current mesh array item
226 reader.seekg(abs_mesh_base_offset);
227 reader.seekg(mesh * sizeof(vtx::MeshHeader), std::ios::cur);
228
229 //Read the Mesh header
230 vtx::MeshHeader MESH;
231 reader.read((char*)&MESH, sizeof(MESH));
232
233 /* Strip Group array */
234 reader.seekg(MESH.stripGroupHeaderOffset - sizeof(vtx::MeshHeader), std::ios::cur);
235 int abs_strip_group_base_offset = reader.tellg();
236
237 for (int sgroup = 0; sgroup < MESH.numStripGroups; sgroup++)
238 {
239 //Move to the current stripgroup array item
240 reader.seekg(abs_strip_group_base_offset);
241 reader.seekg(sgroup * sizeof(vtx::StripGroupHeader), std::ios::cur);
242
243 //Read the strip group header
244 vtx::StripGroupHeader SGROUP;
245 reader.read((char*)&SGROUP, sizeof(SGROUP));
246
247 int base_location = (int)reader.tellg() - sizeof(vtx::StripGroupHeader);
248 int location_vertex_array = base_location + SGROUP.vertOffset;
249 int location_indices_array = base_location + SGROUP.indexOffset;
250
251 //Read vertex table
252 std::vector<vtx::Vertex> vertexTable;
253 reader.seekg(location_vertex_array);
254 for (int i = 0; i < SGROUP.numVerts; i++)
255 {
256 vtx::Vertex vert;
257 reader.read((char*)&vert, sizeof(vert));
258 vertexTable.push_back(vert);
259 }
260
261 //Read indices set
262 std::vector<unsigned short> indicesTable;
263 reader.seekg(location_indices_array);
264 for (int i = 0; i < SGROUP.numIndices; i++)
265 {
266 unsigned short index;
267 reader.read((char*)&index, sizeof(index));
268 indicesTable.push_back(index);
269 }
270
271 /* Strips array */
272 reader.seekg(base_location);
273 reader.seekg(SGROUP.stripOffset, std::ios::cur);
274 int abs_strip_base_offset = reader.tellg();
275
276 for (int strip = 0; strip < SGROUP.numStrips; strip++)
277 {
278 //Move to current strip array item
279 reader.seekg(abs_strip_base_offset);
280 reader.seekg(strip * sizeof(vtx::StripHeader), std::ios::cur);
281
282 //Read the strip
283 vtx::StripHeader STRIP;
284 reader.read((char*)&STRIP, sizeof(STRIP));
285
286 //Virtual vertices pool
287 std::vector<vtx::Vertex> v_verts;
288 for (int i = 0; i < STRIP.numVerts; i++)
289 if ((STRIP.vertOffset + i) >= vertexTable.size())
290 throw std::exception("VTX::DECOMPILE::VERT_TABLE OUT OF RANGE");
291 else
292 v_verts.push_back(vertexTable[STRIP.vertOffset + i]);
293
294 //Virtual indices pool
295 std::vector<unsigned short> v_indices;
296 for (int i = 0; i < STRIP.numIndices; i++)
297 if ((STRIP.indexOffset + i) >= indicesTable.size())
298 throw std::exception("VTX::DECOMPILE::INDEX_TABLE OUT OF RANGE");
299 else
300 v_indices.push_back(indicesTable[STRIP.indexOffset + i]);
301
302 for (int i = 0; i < v_indices.size(); i++)
303 {
304 this->vertexSequence.push_back(v_verts[v_indices[i]].origMeshVertID + total_verts);
305 }
306 }
307
308 total_verts += SGROUP.numVerts;
309 }
310 }
311 }
312 }
313 }
314
315 IL_EXIT: __noop;
316 //Dispose stream
317 reader.close();
318 }
319
320 vtx_mesh(std::ifstream* stream, unsigned int offset, bool verbost = false){
321 this->use_verbose = verbost;
322 stream->seekg(offset);
323
324 //Read header
325 stream->read((char*)&this->header, sizeof(this->header));
326 this->debug("VTX version:", this->header.version);
327 this->debug("Num LODS:", this->header.numLODs);
328
329 /* Read bulk of .VTX file */
330
331 /* Body part array */
332 stream->seekg(offset + header.bodyPartOffset);
333 int abs_body_base_offset = stream->tellg();
334
335 for (int body = 0; body < header.numBodyParts; body++){
336 //Move to current body part array item
337 stream->seekg(abs_body_base_offset);
338 stream->seekg(body * sizeof(vtx::BodyPartHeader), std::ios::cur);
339
340 //Read the body part
341 vtx::BodyPartHeader BODY;
342 stream->read((char*)&BODY, sizeof(BODY));
343
344 /* Model array */
345 stream->seekg(BODY.modelOffset - sizeof(vtx::BodyPartHeader), std::ios::cur);
346 int abs_model_base_offset = stream->tellg();
347
348 int total_verts = 0;
349 //NOTE: Total verts may need to be initialized outside the body array
350
351 for (int model = 0; model < BODY.numModels; model++){
352 //Move to current model array item
353 stream->seekg(abs_model_base_offset);
354 stream->seekg(model * sizeof(vtx::ModelHeader), std::ios::cur);
355
356 //Read the Model
357 vtx::ModelHeader MODEL;
358 stream->read((char*)&MODEL, sizeof(MODEL));
359
360 /* LOD array */
361 stream->seekg(MODEL.lodOffset - sizeof(vtx::ModelHeader), std::ios::cur);
362 int abs_lod_base_offset = stream->tellg();
363
364 for (int lod = 0; lod < MODEL.numLODs; lod++){
365 if (lod > 0) goto IL_EXIT; // Skip all the other lods for now
366
367 //Move to the current LOD header array item
368 stream->seekg(abs_lod_base_offset);
369 stream->seekg(lod * sizeof(vtx::ModelLODHeader), std::ios::cur);
370
371 //Read the LOD header
372 vtx::ModelLODHeader LOD;
373 stream->read((char*)&LOD, sizeof(LOD));
374
375 /* Mesh array */
376 stream->seekg(LOD.meshOffset - sizeof(vtx::ModelLODHeader), std::ios::cur);
377 int abs_mesh_base_offset = stream->tellg();
378
379 for (int mesh = 0; mesh < LOD.numMeshes; mesh++){
380 //Move to the current mesh array item
381 stream->seekg(abs_mesh_base_offset);
382 stream->seekg(mesh * sizeof(vtx::MeshHeader), std::ios::cur);
383
384 //Read the Mesh header
385 vtx::MeshHeader MESH;
386 stream->read((char*)&MESH, sizeof(MESH));
387
388 /* Strip Group array */
389 stream->seekg(MESH.stripGroupHeaderOffset - sizeof(vtx::MeshHeader), std::ios::cur);
390 int abs_strip_group_base_offset = stream->tellg();
391
392 for (int sgroup = 0; sgroup < MESH.numStripGroups; sgroup++){
393 //Move to the current stripgroup array item
394 stream->seekg(abs_strip_group_base_offset);
395 stream->seekg(sgroup * sizeof(vtx::StripGroupHeader), std::ios::cur);
396
397 //Read the strip group header
398 vtx::StripGroupHeader SGROUP;
399 stream->read((char*)&SGROUP, sizeof(SGROUP));
400
401 int base_location = (int)stream->tellg() - sizeof(vtx::StripGroupHeader);
402 int location_vertex_array = base_location + SGROUP.vertOffset;
403 int location_indices_array = base_location + SGROUP.indexOffset;
404
405 //Read vertex table
406 std::vector<vtx::Vertex> vertexTable;
407 stream->seekg(location_vertex_array);
408 for (int i = 0; i < SGROUP.numVerts; i++)
409 {
410 vtx::Vertex vert;
411 stream->read((char*)&vert, sizeof(vert));
412 vertexTable.push_back(vert);
413 }
414
415 //Read indices set
416 std::vector<unsigned short> indicesTable;
417 stream->seekg(location_indices_array);
418 for (int i = 0; i < SGROUP.numIndices; i++)
419 {
420 unsigned short index;
421 stream->read((char*)&index, sizeof(index));
422 indicesTable.push_back(index);
423 }
424
425 /* Strips array */
426 stream->seekg(base_location);
427 stream->seekg(SGROUP.stripOffset, std::ios::cur);
428 int abs_strip_base_offset = stream->tellg();
429
430 for (int strip = 0; strip < SGROUP.numStrips; strip++)
431 {
432 //Move to current strip array item
433 stream->seekg(abs_strip_base_offset);
434 stream->seekg(strip * sizeof(vtx::StripHeader), std::ios::cur);
435
436 //Read the strip
437 vtx::StripHeader STRIP;
438 stream->read((char*)&STRIP, sizeof(STRIP));
439
440 //Virtual vertices pool
441 std::vector<vtx::Vertex> v_verts;
442 for (int i = 0; i < STRIP.numVerts; i++)
443 if ((STRIP.vertOffset + i) >= vertexTable.size())
444 throw std::exception("VTX::DECOMPILE::VERT_TABLE OUT OF RANGE");
445 else
446 v_verts.push_back(vertexTable[STRIP.vertOffset + i]);
447
448 //Virtual indices pool
449 std::vector<unsigned short> v_indices;
450 for (int i = 0; i < STRIP.numIndices; i++)
451 if ((STRIP.indexOffset + i) >= indicesTable.size())
452 throw std::exception("VTX::DECOMPILE::INDEX_TABLE OUT OF RANGE");
453 else
454 v_indices.push_back(indicesTable[STRIP.indexOffset + i]);
455
456 for (int i = 0; i < v_indices.size(); i++)
457 {
458 this->vertexSequence.push_back(v_verts[v_indices[i]].origMeshVertID + total_verts);
459 }
460 }
461
462 total_verts += SGROUP.numVerts;
463 }
464 }
465 }
466 }
467 }
468 IL_EXIT: __noop;
469 }
470
471 virtual ~vtx_mesh() {}
472 };