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
32 const auto materialReplacementListOffset = stream.read<int32_t>();
33
34 const auto bodyPartCount = stream.read<int32_t>();
35 const auto bodyPartOffset = stream.read<int32_t>();
36
37 if (materialReplacementListOffset > 0) {
38 stream.seek(materialReplacementListOffset);
39
40 for (int i = 0; i < this->numLODs; i++) {
41 const auto replacementListPos = materialReplacementListOffset + i * (sizeof(int32_t) * 2);
42 stream.seek_u(replacementListPos);
43
44 auto& replacementList = this->materialReplacementLists[i];
45
46 const auto replacementCount = stream.read<int32_t>();
47 const auto replacementOffset = stream.read<int32_t>();
48
49 if (replacementCount > 0 && replacementOffset > 0) {
50 for (int j = 0; j < replacementCount; j++) {
51 const auto replacementPos = replacementOffset + j * (sizeof(int16_t) + sizeof(int32_t));
52 stream.seek_u(replacementListPos + replacementPos);
53
54 auto& replacement = replacementList.emplace_back();
55 stream.read(replacement.materialID);
56
57 parser::binary::readStringAtOffset(stream, replacement.newMaterialPath, std::ios::cur, 6);
58 }
59 }
60 }
61 }
62
63 for (int i = 0; i < bodyPartCount; i++) {
64 const auto bodyPartPos = bodyPartOffset + i * (sizeof(int32_t) * 2);
65 stream.seek_u(bodyPartPos);
66
67 auto& bodyPart = this->bodyParts.emplace_back();
68
69 const auto modelCount = stream.read<int32_t>();
70 const auto modelOffset = stream.read<int32_t>();
71
72 for (int j = 0; j < modelCount; j++) {
73 const auto modelPos = modelOffset + j * (sizeof(int32_t) * 2);
74 stream.seek_u(bodyPartPos + modelPos);
75
76 auto& model = bodyPart.models.emplace_back();
77
78 const auto modelLODCount = stream.read<int32_t>();
79 const auto modelLODOffset = stream.read<int32_t>();
80
81 for (int k = 0; k < modelLODCount; k++) {
82 const auto modelLODPos = modelLODOffset + k * (sizeof(int32_t) * 2 + sizeof(float));
83 stream.seek_u(bodyPartPos + modelPos + modelLODPos);
84
85 auto& modelLOD = model.modelLODs.emplace_back();
86
87 const auto meshCount = stream.read<int32_t>();
88 const auto meshOffset = stream.read<int32_t>();
89
90 stream.read(modelLOD.switchDistance);
91
92 for (int l = 0; l < meshCount; l++) {
93 const auto meshPos = meshOffset + l * (sizeof(int32_t) * 2 + sizeof(Mesh::Flags));
94 stream.seek_u(bodyPartPos + modelPos + modelLODPos + meshPos);
95
96 auto& mesh = modelLOD.meshes.emplace_back();
97
98 const auto stripGroupCount = stream.read<int32_t>();
99 const auto stripGroupOffset = stream.read<int32_t>();
100
101 stream.read(mesh.flags);
102
103 for (int m = 0; m < stripGroupCount; m++) {
104 int stripGroupNumInts = 6;
105 if (useTopology) {
106 stripGroupNumInts += 2;
107 }
108 const auto stripGroupPos = stripGroupOffset + m * (sizeof(int32_t) * stripGroupNumInts + sizeof(StripGroup::Flags));
109 stream.seek_u(bodyPartPos + modelPos + modelLODPos + meshPos + stripGroupPos);
110
111 auto& stripGroup = mesh.stripGroups.emplace_back();
112
113 const auto vertexCount = stream.read<int32_t>();
114 const auto vertexOffset = stream.read<int32_t>();
115
116 auto stripGroupCurrentPos = stream.tell();
117 stream.seek_u(bodyPartPos + modelPos + modelLODPos + meshPos + stripGroupPos + vertexOffset);
118 for (int n = 0; n < vertexCount; n++) {
119 auto& vertex = stripGroup.vertices.emplace_back();
120
121 stream
122 .read(vertex.boneWeightIndex)
123 .read(vertex.boneCount)
124 .read(vertex.meshVertexID)
125 .read(vertex.boneID);
126 }
127 stream.seek_u(stripGroupCurrentPos);
128
129 const auto indexCount = stream.read<int32_t>();
130 const auto indexOffset = stream.read<int32_t>();
131
132 stripGroupCurrentPos = stream.tell();
133 stream.seek_u(bodyPartPos + modelPos + modelLODPos + meshPos + stripGroupPos + indexOffset);
134 for (int n = 0; n < indexCount; n++) {
135 auto& index = stripGroup.indices.emplace_back();
136 stream.read(index);
137 }
138 stream.seek_u(stripGroupCurrentPos);
139
140 const auto stripCount = stream.read<int32_t>();
141 const auto stripOffset = stream.read<int32_t>();
142
143 stream.read(stripGroup.flags);
144
145 if (useTopology) {
146 const auto topologyIndexCount = stream.read<int32_t>();
147 const auto topologyIndexOffset = stream.read<int32_t>();
148
149 stripGroupCurrentPos = stream.tell();
150 stream.seek_u(bodyPartPos + modelPos + modelLODPos + meshPos + stripGroupPos + topologyIndexOffset);
151 for (int n = 0; n < topologyIndexCount; n++) {
152 auto& topologyIndex = stripGroup.topologyIndices.emplace_back();
153 stream.read(topologyIndex);
154 }
155 stream.seek_u(stripGroupCurrentPos);
156 }
157
158 stream.seek_u(bodyPartPos + modelPos + modelLODPos + meshPos + stripGroupPos + stripOffset);
159 for (int n = 0; n < stripCount; n++) {
160 auto& strip = stripGroup.strips.emplace_back();
161
162 const auto indicesCount = stream.read<int32_t>();
163 stream.read(strip.indicesOffset);
164 // Note: offset is in elements, not bytes
165 strip.indices = std::span(stripGroup.indices.begin() + strip.indicesOffset, indicesCount);
166
167 const auto verticesCount = stream.read<int32_t>();
168 stream.read(strip.verticesOffset);
169 // Note: offset is in elements, not bytes
170 strip.vertices = std::span(stripGroup.vertices.begin() + strip.verticesOffset, verticesCount);
171
172 stream
173 .read(strip.boneCount)
174 .read(strip.flags);
175
176 // bone stuff
177 const auto boneStateChangeCount = stream.read<int32_t>();
178 const auto boneStateChangeOffset = stream.read<int32_t>();
179
180 if (boneStateChangeCount > 0 && boneStateChangeOffset > 0) {
181 const auto savedPos = stream.tell();
182 constexpr auto stripHeaderSize = sizeof(int32_t) * 6 + sizeof(int16_t) + sizeof(Strip::Flags);
183 const auto stripBasePos = bodyPartPos + modelPos + modelLODPos + meshPos + stripGroupPos + stripOffset + n * stripHeaderSize;
184 stream.seek_u(stripBasePos + boneStateChangeOffset);
185
186 for (int p = 0; p < boneStateChangeCount; p++) {
187 auto& boneStateChange = strip.boneStateChanges.emplace_back();
188 stream
189 .read(boneStateChange.hardwareID)
190 .read(boneStateChange.newBoneID);
191 }
192
193 stream.seek_u(savedPos);
194 }
195
196 if (useTopology) {
197 const auto topologyIndicesCount = stream.read<int32_t>();
198 stream.read(strip.topologyIndicesOffset);
199 // Note: offset is in elements, not bytes
200 strip.topologyIndices = std::span(stripGroup.topologyIndices.begin() + strip.topologyIndicesOffset, topologyIndicesCount);
201 }
202 }
203 }
204 }
205 }
206 }
207 }
208 return true;
209 };
210
211 switch (hasTopologyIndices) {
213 const bool useTopology = mdl.version == 49;
214 try {
215 return openInternal(useTopology);
216 } catch (const std::overflow_error&) {
217 if (useTopology) {
218 return openInternal(false);
219 }
220 }
221 return false;
222 }
224 return openInternal(false);
226 return openInternal(true);
227 }
228 return openInternal(false);
229}
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