-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlibmdl++.hpp
492 lines (428 loc) · 13.3 KB
/
libmdl++.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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
178
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
228
229
230
231
232
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
295
296
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
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
#pragma once
#include <span>
#include <cstdint>
//-----------------------------------------------------------------------------
namespace libmdl {
constexpr int MAX_NUM_LODS = 8;
constexpr int MAX_NUM_BONES_PER_VERT = 3;
using u8 = std::uint8_t; static_assert(sizeof(u8) == 1);
using i16 = std::int16_t; static_assert(sizeof(i16) == 2);
using u16 = std::uint16_t; static_assert(sizeof(u16) == 2);
using i32 = std::int32_t; static_assert(sizeof(i32) == 4);
using u64 = std::uint64_t; static_assert(sizeof(u64) == 8);
using b8 = bool; static_assert(sizeof(b8) == 1);
using f32 = float; static_assert(sizeof(f32) == 4); // std::float32_t in C++23
//-----------------------------------------------------------------------------
struct ID32 {
const i32 value;
constexpr ID32(const char id[4])
: value((id[0] << 0) | (id[1] << 8) | (id[2] << 16) | (id[3] << 24)) {}
};
//-----------------------------------------------------------------------------
#ifndef LIBMDL_CUSTOM_VECTORS
struct vec2 { f32 x, y; }; // Vector2D
struct vec3 { f32 x, y, z; }; // Vector
struct vec4 { f32 x, y, z, w; }; // Vector4D
#endif
struct AABB { vec3 min, max; }; // AABB_t
//-----------------------------------------------------------------------------
// Equivalent to CResourcePointer
template <typename T>
struct ResPtr {
i32 offset;
const T* get(const auto* base, size_t i = 0) const {
const u8* start = reinterpret_cast<const u8*>(base) + offset;
return reinterpret_cast<const T*>(start) + i;
}
// No accidentally passing pointers to pointers
const T* get(const auto** base, size_t i = 0) const = delete;
const T* operator()(const auto& base) const {
return get(&base, 0);
}
};
// Equivalent to CResourceString
struct ResString : public ResPtr<char> {};
struct ResStringView : public ResString {
i32 length;
};
// Equivalent to CResourceArray
template <typename T>
struct ResArray {
i32 count;
i32 offset;
const T* get(const void* base, size_t i) const {
return reinterpret_cast<const T*>(reinterpret_cast<const u8*>(base) + offset) + i;
}
// No accidentally passing pointers to pointers
const T* get(const auto** base, size_t i = 0) const = delete;
const std::span<const T> operator()(const auto& base) const {
return { get(&base, 0), size_t(count) };
}
};
template <typename T>
struct NamedResArray : public ResArray<T> {
i32 nameOffset;
};
//-----------------------------------------------------------------------------
// .MDL File Format (derived from public/studio.h)
// Contains overall model structure info
//-----------------------------------------------------------------------------
namespace mdl {
static constexpr ID32 ID = "IDST";
static constexpr i32 VERSION = 49;
// mstudioflex_t
struct Flex {
i32 flexDesc;
f32 targets[4];
i32 numVerts;
i32 vertIndex;
i32 flexPair;
u8 vertAnimType;
u8 unusedBytes[3];
i32 unused[6];
};
// mstudio_modelvertexdata_t
struct ModelVertexData {
u64 pVertexData;
u64 pTangentData;
u64 pExtraData;
};
#pragma pack( push, 4 )
// mstudio_meshvertexdata_t
struct MeshVertexData {
i32 unused_modelVertexData;
i32 numLODVertices[MAX_NUM_LODS];
u64 modelVertexData;
};
// mstudiomesh_t
struct Mesh {
i32 material;
ResPtr<struct Model> model;
i32 numVertices;
i32 vertexOffset;
ResArray<Flex> flexes;
i32 materialType;
i32 materialParam;
i32 id;
vec3 center;
MeshVertexData vertexData;
i32 unused[6];
};
// mstudiomodel_t
struct Model {
char name[64];
i32 type;
f32 boundingRadius;
ResArray<Mesh> meshes;
i32 numVertices;
i32 vertexIndex;
i32 tangentsIndex;
i32 numAttachments;
i32 attachmentIndex;
ResArray<struct Eyeball> eyeballs;
ModelVertexData vertexData;
i32 unused[4];
};
#pragma pack( pop )
// mstudioeyeball_t
struct Eyeball {
ResString name;
i32 bone;
vec3 org;
f32 zOffset;
f32 radius;
vec3 up;
vec3 forward;
i32 texture;
i32 unused1;
f32 irisScale;
i32 unused2;
i32 upperFlexDesc[3];
i32 lowerFlexDesc[3];
f32 upperTarget[3];
f32 lowerTarget[3];
i32 upperLidFlexDesc;
i32 lowerLidFlexDesc;
i32 unused[4];
b8 nonFACS;
u8 unused3[3];
i32 unused4[7];
};
// mstudiobodyparts_t
struct BodyPart {
ResString name;
i32 numModels;
i32 base;
ResPtr<Model> modelData;
};
// mstudiotexture_t
struct Texture {
ResString name;
i32 flags;
i32 used;
i32 unused1;
mutable u64 material;
mutable u64 clientMaterial;
i32 unused[8];
};
// TODO: Fill in these structs
struct Bone {}; // mstudiobone_t
struct BoneController {}; // mstudiobonecontroller_t
struct BoneFlexDriver {}; // mstudioboneflexdriver_t
struct HitboxSet {}; // mstudiohitboxset_t
struct AnimDesc {}; // mstudioanimdesc_t
struct SeqDesc {}; // mstudioseqdesc_t
struct Attachment {}; // mstudioattachment_t
struct FlexDesc {}; // mstudioflexdesc_t
struct FlexController {}; // mstudioflexcontroller_t
struct FlexControllerUI {}; // mstudioflexcontrollerui_t
struct FlexRule {}; // mstudioflexrule_t
struct IKChain {}; // mstudioikchain_t
struct IKLock {}; // mstudioiklock_t
struct Mouth {}; // mstudiomouth_t
struct PoseParamDesc {}; // mstudioposeparamdesc_t
struct ModelGroup {}; // mstudiomodelgroup_t
struct AnimBlock {}; // mstudioanimblock_t
struct SrcBoneTransform {}; // mstudiosrcbonetransform_t
struct LinearBone {}; // mstudiolinearbone_t
struct BodyGroupPreset {}; // mstudiobodygrouppreset_t
struct PhysFeModelDesc {}; // PhysFeModelDesc_t
// studiohdr2_t
struct Header2 {
// Some resource offsets are relative to Header, not Header2
ResArray<SrcBoneTransform> srcBoneTransforms;
i32 illumPositionAttachmentIndex;
f32 maxEyeDeflection;
ResPtr<LinearBone> linearBones;
ResString name;
ResArray<BoneFlexDriver> boneFlexDrivers;
ResPtr<PhysFeModelDesc> feModel;
ResArray<BodyGroupPreset> bodyGroupPresets;
i32 unused;
// Additional data... (virtualModel, animblockModel, pVertexBase, pIndexBase, etc.)
};
// studiohdr_t
struct Header {
i32 id;
i32 version;
i32 checksum;
char name[64];
i32 length;
vec3 eyePosition;
vec3 illumPosition;
AABB hull;
AABB viewBox;
i32 flags;
ResArray<Bone> bones;
ResArray<BoneController> boneControllers;
ResArray<HitboxSet> hitboxSets;
ResArray<AnimDesc> localAnims;
ResArray<SeqDesc> localSeqs;
mutable i32 activityListVersion;
mutable i32 eventsIndexed;
ResArray<Texture> textures;
ResArray<ResString> textureDirs;
i32 numSkinRef; // ?
ResArray<i16> skinRefs;
ResArray<BodyPart> bodyParts;
ResArray<Attachment> localAttachments;
NamedResArray<u8> localNodes;
ResArray<FlexDesc> flexDescs;
ResArray<FlexController> flexControllers;
ResArray<FlexRule> flexRules;
ResArray<IKChain> ikChains;
ResArray<Mouth> mouths;
ResArray<PoseParamDesc> localPoseParams;
ResString surfaceProp;
ResStringView keyValuesText;
ResArray<IKLock> localIKAutoplayLocks;
f32 mass;
i32 contents;
ResArray<ModelGroup> includeModels;
i32 unused_virtualModel;
ResString animBlockName;
ResArray<AnimBlock> animBlocks;
i32 unused_animBlockModel;
ResPtr<u8> animBlockModel;
i32 unused_pVertexBase;
i32 unused_pIndexBase;
u8 constDirectionalLightDot;
u8 rootLOD;
u8 numAllowedRootLODs;
u8 unused;
i32 unused4; // zero out if version < 47
ResArray<FlexControllerUI> flexControllerUIs;
f32 vertAnimFixedPointScale;
mutable i32 surfacePropLookup;
ResPtr<Header2> hdr2;
i32 unused2;
};
} // namespace mdl
//-----------------------------------------------------------------------------
// .VVD File Format (derived from public/studio.h)
// Contains the global vertex list
//-----------------------------------------------------------------------------
namespace vvd {
static constexpr ID32 ID = "IDSV";
static constexpr ID32 ID_THIN = "IDCV";
static constexpr ID32 ID_NULL = "IDDV";
static constexpr i32 VERSION = 4;
// mstudioboneweight_t
struct BoneWeights {
f32 weight[MAX_NUM_BONES_PER_VERT];
u8 bone[MAX_NUM_BONES_PER_VERT];
u8 numBones;
};
static_assert(sizeof(BoneWeights) == 16);
// mstudiovertex_t
struct Vertex {
BoneWeights boneWeights;
vec3 position;
vec3 normal;
vec2 texCoord;
};
static_assert(sizeof(Vertex) == 48);
// vertexFileFixup_t
struct VVDFixup {
i32 lod;
i32 sourceVertexID;
i32 numVertexes;
};
// vertexFileHeader_t
struct Header {
i32 id;
i32 version;
i32 checksum;
i32 numLODs;
i32 numLODVertexes[MAX_NUM_LODS];
ResArray<VVDFixup> fixups;
ResPtr<Vertex> vertexData;
ResPtr<vec4> tangentData;
const Vertex& getVertex(size_t i) const {
return *vertexData.get(this, i);
}
};
} // namespace vvd
//-----------------------------------------------------------------------------
// .VTX File Format (derived from public/optimize.h)
// Contains hardware optimized model data (vertex and index buffers)
//-----------------------------------------------------------------------------
namespace vtx {
#pragma pack(1)
// OptimizedModel::Vertex_t
struct Vertex {
u8 boneWeightIndex[MAX_NUM_BONES_PER_VERT];
u8 numBones;
u16 origMeshVertID;
u8 boneID[MAX_NUM_BONES_PER_VERT];
};
// OptimizedModel::BoneStateChangeHeader_t
struct BoneStateChange {
i32 hardwareID;
i32 newBoneID;
};
// OptimizedModel::StripHeader_t
struct Strip {
enum Flags : u8 {
TriList = 1,
QuadList = 2,
QuadListExtra = 4
};
i32 numIndices;
i32 indexOffset;
i32 numVerts;
i32 vertOffset;
i16 numBones;
Flags flags;
ResArray<BoneStateChange> boneStateChanges;
i32 numTopologyIndices;
i32 topologyOffset;
};
// OptimizedModel::StripGroupHeader_t
struct StripGroup {
enum Flags : u8 {
HWSkinned = 2,
DeltaFlexed = 4,
SuppressHWMorph = 8
};
ResArray<Vertex> vertices;
ResArray<u16> indices;
ResArray<Strip> strips;
Flags flags;
ResArray<u16> topologyIndices;
};
// OptimizedModel::MeshHeader_t
struct Mesh {
enum Flags : u8 {
Teeth = 1,
Eyes = 2
};
ResArray<StripGroup> stripGroups;
Flags flags;
};
// OptimizedModel::ModelLODHeader_t
struct ModelLOD {
ResArray<Mesh> meshes;
f32 switchPoint;
};
// OptimizedModel::ModelHeader_t
struct Model {
ResArray<ModelLOD> lods;
};
// OptimizedModel::BodyPartHeader_t
struct BodyPart {
ResArray<Model> models;
};
// OptimizedModel::MaterialReplacementHeader_t
struct MaterialReplacement {
i16 materialID;
ResString replacementMaterialName;
};
// OptimizedModel::MaterialReplacementListHeader_t
struct MaterialReplacementList {
ResArray<MaterialReplacement> replacements;
};
// OptimizedModel::FileHeader_t
struct Header {
static constexpr i32 VERSION = 7;
i32 version;
i32 vertCacheSize;
u16 maxBonesPerStrip;
u16 maxBonesPerFace;
i32 maxBonesPerVert;
i32 checkSum;
i32 numLODs;
ResPtr<MaterialReplacementList> materialReplacementList; // one per LOD
ResArray<BodyPart> bodyParts;
};
#pragma pack()
} // namespace vtx
//-----------------------------------------------------------------------------
using MDLHeader = mdl::Header;
using VVDHeader = vvd::Header;
using VTXHeader = vtx::Header;
using Buffer = std::span<u8>;
class ModelData {
public:
ModelData(Buffer mdl) : mdlbuf(mdl) {
this->mdl = reinterpret_cast<mdl::Header*>(mdlbuf.data());
}
ModelData(Buffer mdl, Buffer vvd) : mdlbuf(mdl), vvdbuf(vvd) {
this->mdl = reinterpret_cast<mdl::Header*>(mdlbuf.data());
this->vvd = reinterpret_cast<vvd::Header*>(vvdbuf.data());
}
ModelData(Buffer mdl, Buffer vvd, Buffer vtx) : mdlbuf(mdl), vvdbuf(vvd), vtxbuf(vtx) {
this->mdl = reinterpret_cast<mdl::Header*>(mdlbuf.data());
this->vvd = reinterpret_cast<vvd::Header*>(vvdbuf.data());
this->vtx = reinterpret_cast<vtx::Header*>(vtxbuf.data());
}
const MDLHeader& getMDL() const { return *mdl; }
const VVDHeader& getVertices() const { return *vvd; }
const VTXHeader& getMeshData() const { return *vtx; }
protected:
MDLHeader* mdl = nullptr;
VVDHeader* vvd = nullptr;
VTXHeader* vtx = nullptr;
Buffer mdlbuf, vvdbuf, vtxbuf;
};
} // namespace libmdl