SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
VTX.cpp
Go to the documentation of this file.
1// ReSharper disable CppTooWideScopeInitStatement
2// ReSharper disable CppUseStructuredBinding
3
4#include <mdlpp/structs/VTX.h>
5
6#include <BufferStream.h>
8
9using namespace mdlpp::VTX;
10using namespace sourcepp;
11
12bool VTX::open(const std::byte* data, std::size_t size, const MDL::MDL& mdl, HasTopologyIndices hasTopologyIndices) {
13 const auto openInternal = [this, data, size, checksum = mdl.checksum](bool useTopology) {
14 BufferStreamReadOnly stream{data, size};
15
16 if (stream.read(this->version); this->version != 7) {
17 return false;
18 }
19
20 stream
21 .read(this->vertexCacheSize)
22 .read(this->maxBonesPerStrip)
23 .read(this->maxBonesPerTriangle)
24 .read(this->maxBonesPerVertex);
25
26 if (stream.read<int32_t>() != checksum) {
27 return false;
28 }
29
30 stream.read(this->numLODs);
31 this->numLODs = std::clamp(this->numLODs, 1, MAX_LOD_COUNT);
32
33 const auto materialReplacementListOffset = stream.read<int32_t>();
34
35 const auto bodyPartCount = stream.read<int32_t>();
36 const auto bodyPartOffset = stream.read<int32_t>();
37
38 if (materialReplacementListOffset > 0) {
39 stream.seek(materialReplacementListOffset);
40
41 for (int i = 0; i < this->numLODs; i++) {
42 const auto replacementListPos = materialReplacementListOffset + i * (sizeof(int32_t) * 2);
43 stream.seek_u(replacementListPos);
44
45 auto& replacementList = this->materialReplacementLists[i];
46
47 const auto replacementCount = stream.read<int32_t>();
48 const auto replacementOffset = stream.read<int32_t>();
49
50 if (replacementCount > 0 && replacementOffset > 0) {
51 for (int j = 0; j < replacementCount; j++) {
52 const auto replacementPos = replacementOffset + j * (sizeof(int16_t) + sizeof(int32_t));
53 stream.seek_u(replacementListPos + replacementPos);
54
55 auto& replacement = replacementList.emplace_back();
56 stream.read(replacement.materialID);
57
58 parser::binary::readStringAtOffset(stream, replacement.newMaterialPath, std::ios::cur, 6);
59 }
60 }
61 }
62 }
63
64 for (int i = 0; i < bodyPartCount; i++) {
65 const auto bodyPartPos = bodyPartOffset + i * (sizeof(int32_t) * 2);
66 stream.seek_u(bodyPartPos);
67
68 auto& bodyPart = this->bodyParts.emplace_back();
69
70 const auto modelCount = stream.read<int32_t>();
71 const auto modelOffset = stream.read<int32_t>();
72
73 for (int j = 0; j < modelCount; j++) {
74 const auto modelPos = modelOffset + j * (sizeof(int32_t) * 2);
75 stream.seek_u(bodyPartPos + modelPos);
76
77 auto& model = bodyPart.models.emplace_back();
78
79 const auto modelLODCount = stream.read<int32_t>();
80 const auto modelLODOffset = stream.read<int32_t>();
81
82 for (int k = 0; k < modelLODCount; k++) {
83 const auto modelLODPos = modelLODOffset + k * (sizeof(int32_t) * 2 + sizeof(float));
84 stream.seek_u(bodyPartPos + modelPos + modelLODPos);
85
86 auto& modelLOD = model.modelLODs.emplace_back();
87
88 const auto meshCount = stream.read<int32_t>();
89 const auto meshOffset = stream.read<int32_t>();
90
91 stream.read(modelLOD.switchDistance);
92
93 for (int l = 0; l < meshCount; l++) {
94 const auto meshPos = meshOffset + l * (sizeof(int32_t) * 2 + sizeof(Mesh::Flags));
95 stream.seek_u(bodyPartPos + modelPos + modelLODPos + meshPos);
96
97 auto& mesh = modelLOD.meshes.emplace_back();
98
99 const auto stripGroupCount = stream.read<int32_t>();
100 const auto stripGroupOffset = stream.read<int32_t>();
101
102 stream.read(mesh.flags);
103
104 for (int m = 0; m < stripGroupCount; m++) {
105 int stripGroupNumInts = 6;
106 if (useTopology) {
107 stripGroupNumInts += 2;
108 }
109 const auto stripGroupPos = stripGroupOffset + m * (sizeof(int32_t) * stripGroupNumInts + sizeof(StripGroup::Flags));
110 stream.seek_u(bodyPartPos + modelPos + modelLODPos + meshPos + stripGroupPos);
111
112 auto& stripGroup = mesh.stripGroups.emplace_back();
113
114 const auto vertexCount = stream.read<int32_t>();
115 const auto vertexOffset = stream.read<int32_t>();
116
117 auto stripGroupCurrentPos = stream.tell();
118 stream.seek_u(bodyPartPos + modelPos + modelLODPos + meshPos + stripGroupPos + vertexOffset);
119 for (int n = 0; n < vertexCount; n++) {
120 auto& vertex = stripGroup.vertices.emplace_back();
121
122 stream
123 .read(vertex.boneWeightIndex)
124 .read(vertex.boneCount)
125 .read(vertex.meshVertexID)
126 .read(vertex.boneID);
127 }
128 stream.seek_u(stripGroupCurrentPos);
129
130 const auto indexCount = stream.read<int32_t>();
131 const auto indexOffset = stream.read<int32_t>();
132
133 stripGroupCurrentPos = stream.tell();
134 stream.seek_u(bodyPartPos + modelPos + modelLODPos + meshPos + stripGroupPos + indexOffset);
135 for (int n = 0; n < indexCount; n++) {
136 auto& index = stripGroup.indices.emplace_back();
137 stream.read(index);
138 }
139 stream.seek_u(stripGroupCurrentPos);
140
141 const auto stripCount = stream.read<int32_t>();
142 const auto stripOffset = stream.read<int32_t>();
143
144 stream.read(stripGroup.flags);
145
146 if (useTopology) {
147 const auto topologyIndexCount = stream.read<int32_t>();
148 const auto topologyIndexOffset = stream.read<int32_t>();
149
150 stripGroupCurrentPos = stream.tell();
151 stream.seek_u(bodyPartPos + modelPos + modelLODPos + meshPos + stripGroupPos + topologyIndexOffset);
152 for (int n = 0; n < topologyIndexCount; n++) {
153 auto& topologyIndex = stripGroup.topologyIndices.emplace_back();
154 stream.read(topologyIndex);
155 }
156 stream.seek_u(stripGroupCurrentPos);
157 }
158
159 stream.seek_u(bodyPartPos + modelPos + modelLODPos + meshPos + stripGroupPos + stripOffset);
160 for (int n = 0; n < stripCount; n++) {
161 auto& strip = stripGroup.strips.emplace_back();
162
163 const auto indicesCount = stream.read<int32_t>();
164 stream.read(strip.indicesOffset);
165 // Note: offset is in elements, not bytes
166 strip.indices = std::span(stripGroup.indices.begin() + strip.indicesOffset, indicesCount);
167
168 const auto verticesCount = stream.read<int32_t>();
169 stream.read(strip.verticesOffset);
170 // Note: offset is in elements, not bytes
171 strip.vertices = std::span(stripGroup.vertices.begin() + strip.verticesOffset, verticesCount);
172
173 stream
174 .read(strip.boneCount)
175 .read(strip.flags);
176
177 // bone stuff
178 const auto boneStateChangeCount = stream.read<int32_t>();
179 const auto boneStateChangeOffset = stream.read<int32_t>();
180
181 if (boneStateChangeCount > 0 && boneStateChangeOffset > 0) {
182 const auto savedPos = stream.tell();
183 constexpr auto stripHeaderSize = sizeof(int32_t) * 6 + sizeof(int16_t) + sizeof(Strip::Flags);
184 const auto stripBasePos = bodyPartPos + modelPos + modelLODPos + meshPos + stripGroupPos + stripOffset + n * stripHeaderSize;
185 stream.seek_u(stripBasePos + boneStateChangeOffset);
186
187 for (int p = 0; p < boneStateChangeCount; p++) {
188 auto& boneStateChange = strip.boneStateChanges.emplace_back();
189 stream
190 .read(boneStateChange.hardwareID)
191 .read(boneStateChange.newBoneID);
192 }
193
194 stream.seek_u(savedPos);
195 }
196
197 if (useTopology) {
198 const auto topologyIndicesCount = stream.read<int32_t>();
199 stream.read(strip.topologyIndicesOffset);
200 // Note: offset is in elements, not bytes
201 strip.topologyIndices = std::span(stripGroup.topologyIndices.begin() + strip.topologyIndicesOffset, topologyIndicesCount);
202 }
203 }
204 }
205 }
206 }
207 }
208 }
209 return true;
210 };
211
212 switch (hasTopologyIndices) {
214 const bool useTopology = mdl.version == 49;
215 try {
216 return openInternal(useTopology);
217 } catch (const std::overflow_error&) {
218 if (useTopology) {
219 return openInternal(false);
220 }
221 }
222 return false;
223 }
225 return openInternal(false);
227 return openInternal(true);
228 }
229 return openInternal(false);
230}
constexpr int MAX_LOD_COUNT
Definition Generic.h:11
void readStringAtOffset(BufferStream &stream, std::string &str, std::ios::seekdir offsetFrom=std::ios::cur, std::size_t subtractFromOffset=sizeof(int32_t))
Reads an integer from the stream, seeks there, reads a string, and seeks back.
Definition Binary.cpp:7
int32_t checksum
Definition MDL.h:707
int32_t version
Definition MDL.h:706
int32_t numLODs
Definition VTX.h:135
int32_t vertexCacheSize
Definition VTX.h:130
bool open(const std::byte *data, std::size_t size, const MDL::MDL &mdl, HasTopologyIndices hasTopologyIndices=HasTopologyIndices::AUTOMATIC)
Definition VTX.cpp:12
std::array< std::vector< MaterialReplacement >, MAX_LOD_COUNT > materialReplacementLists
Definition VTX.h:138
std::vector< BodyPart > bodyParts
Definition VTX.h:142
uint16_t maxBonesPerTriangle
Definition VTX.h:132
int32_t maxBonesPerVertex
Definition VTX.h:133
uint16_t maxBonesPerStrip
Definition VTX.h:131