wowlib 0.0.0
Read & write World of Warcraft client files — a C++26 core
Loading...
Searching...
No Matches
records.hpp
Go to the documentation of this file.
1#pragma once
2
10
11#include <array>
12#include <cstddef>
13#include <cstdint>
14#include <format>
15#include <vector>
16
17#include <welder/vocabulary.hpp>
18
19#include <wowlib/core/lang.hpp>
25
31 struct [[
32 =welder::weld,
33 =welder::weld_as("M2SkinSection"),
35 =welder::doc(R"(
36 One renderable geometry section (submesh). Abstract over the client version; construct a concrete
37 version with M2SkinSection.ForVersion / for_version.)")
39 // The concretes default operator== — the base must be comparable too.
40 bool operator==(const M2SkinSectionBase&) const = default;
41 };
42
43 /** The version-agnostic base of every M2SkinProfile<V> (welded as "M2SkinProfile").
44 Bindings-only, like every *Base: it gives the per-version classes a
45 common welded supertype, so the family surface hoists their shared
46 members and containers of them carry a base-typed live view. */
47 struct [[
48 =welder::weld,
49 =welder::weld_as("M2SkinProfile"),
51 =welder::doc(R"(
52 One skin profile (a whole LOD's sections and batches). Abstract over the client version; construct a concrete
53 version with M2SkinProfile.ForVersion / for_version.)")
55 // The concretes default operator== — the base must be comparable too.
56 bool operator==(const M2SkinProfileBase&) const = default;
57 };
58
59 namespace detail {
60 // The annotated era layouts; instantiate through the canonicalizing
61 // aliases below, never directly.
62 template <ClientVersion V>
64
65 template <ClientVersion V> requires (V < M2CompressedBones)
66 struct [[
67 =welder::weld,
68 =welder::doc("A skin submesh, vanilla layout (no sort data yet).")
70 [[=welder::doc("Mesh part (geoset) id.")]]
71 std::uint16_t skinSectionId = 0;
72 [[=welder::doc(
73 "Extends the 16-bit index_start by (level << 16) — index lists outgrow 64k first; vertex_start stays plain (verified on level=1 client files).")
74 ]]
75 std::uint16_t level = 0;
76 [[=welder::doc("First local vertex.")]]
77 std::uint16_t vertexStart = 0;
78 [[=welder::doc("Local vertex count.")]]
79 std::uint16_t vertexCount = 0;
80 [[=welder::doc("First triangle index.")]]
81 std::uint16_t indexStart = 0;
82 [[=welder::doc("Triangle index count.")]]
83 std::uint16_t indexCount = 0;
84 [[=welder::doc("Bone-lookup entries used.")]]
85 std::uint16_t boneCount = 0;
86 [[=welder::doc("First bone-lookup entry.")]]
87 std::uint16_t boneComboIndex = 0;
88 [[=welder::doc("Highest bones-per-vertex in the submesh.")]]
89 std::uint16_t boneInfluences = 0;
90 [[=welder::doc(
91 "The bone nearest the submesh center; wowdev leaves it otherwise undescribed.")
92 ]]
93 std::uint16_t centerBoneIndex = 0;
94 [[=welder::doc("Average vertex position.")]]
96
97 bool operator==(const M2SkinSection&) const = default;
98 };
99
100 template <ClientVersion V> requires (V >= M2CompressedBones)
101 struct [[
102 =welder::weld,
103 =welder::doc("A skin submesh (TBC+): adds the sort center and radius.")
105 [[=welder::doc("Mesh part (geoset) id.")]]
106 std::uint16_t skinSectionId = 0;
107 [[=welder::doc(
108 "Extends the 16-bit index_start by (level << 16) — index lists outgrow 64k first; vertex_start stays plain (verified on level=1 client files).")
109 ]]
110 std::uint16_t level = 0;
111 [[=welder::doc("First local vertex.")]]
112 std::uint16_t vertexStart = 0;
113 [[=welder::doc("Local vertex count.")]]
114 std::uint16_t vertexCount = 0;
115 [[=welder::doc("First triangle index.")]]
116 std::uint16_t indexStart = 0;
117 [[=welder::doc("Triangle index count.")]]
118 std::uint16_t indexCount = 0;
119 [[=welder::doc("Bone-lookup entries used.")]]
120 std::uint16_t boneCount = 0;
121 [[=welder::doc("First bone-lookup entry.")]]
122 std::uint16_t boneComboIndex = 0;
123 [[=welder::doc("Highest bones-per-vertex in the submesh.")]]
124 std::uint16_t boneInfluences = 0;
125 [[=welder::doc(
126 "The bone nearest the submesh center; wowdev leaves it otherwise undescribed.")
127 ]]
128 std::uint16_t centerBoneIndex = 0;
129 [[=welder::doc("Average vertex position.")]]
130 C3Vector centerPosition{};
131 [[=welder::doc("Center of the submesh bounding box.")]]
132 C3Vector sortCenterPosition{};
133 [[=welder::doc("Distance of the farthest vertex from it.")]]
134 float sortRadius = 0;
135
136 bool operator==(const M2SkinSection&) const = default;
137 };
139
142 template <ClientVersion V>
145
146 static_assert(sizeof(M2SkinSection<versions::Vanilla>) == 32);
147 static_assert(sizeof(M2SkinSection<versions::Wotlk>) == 48);
149 struct [[
150 =welder::weld,
151 =welder::doc("An M2 skin render batch (texture unit): submesh + material "
152 "+ the lookup bases the shaders consume.")
153 ]] M2Batch {
154 [[=welder::doc("0x10 static texture, 0x40 transparency quirk, ...")]]
155 std::uint8_t flags = 0;
156 [[=welder::doc("Draw-order priority plane.")]]
157 std::int8_t priorityPlane = 0;
158 [[=welder::doc("Pre-Cata: 0 on disk, computed at runtime.")]]
159 std::uint16_t shaderId = 0;
160 [[=welder::doc("The submesh this batch renders.")]]
161 std::uint16_t skinSectionIndex = 0;
162 [[=welder::doc("BfA+: renamed flags2 (0x2 projected, 0x8 EDGF).")]]
163 std::uint16_t geosetIndex = 0;
164 [[=welder::doc("Into the model colors, -1 if none.")]]
165 std::uint16_t colorIndex = 0;
166 [[=welder::doc("Into the model materials.")]]
167 std::uint16_t materialIndex = 0;
168 [[=welder::doc("Capped at 7.")]]
169 std::uint16_t materialLayer = 0;
170 [[=welder::doc("1..4 consecutive lookup entries.")]]
171 std::uint16_t textureCount = 0;
172 [[=welder::doc("Into the texture lookup.")]]
173 std::uint16_t textureComboIndex = 0;
174 [[=welder::doc("Into the texture-mapping lookup.")]]
175 std::uint16_t textureCoordComboIndex = 0;
176 [[=welder::doc("Into the transparency lookup.")]]
177 std::uint16_t textureWeightComboIndex = 0;
178 [[=welder::doc("Into the UV-animation lookup.")]]
179 std::uint16_t textureTransformComboIndex = 0;
181 bool operator==(const M2Batch&) const = default;
182 };
183
184 static_assert(sizeof(M2Batch) == 24);
185
186 struct [[
187 =welder::weld,
188 =welder::doc("An M2 skin shadow batch (Cata+); the client can also "
189 "generate these from the render batches.")
191 [[=welder::doc("If auto-generated: the source render batch's flags.")]]
192 std::uint8_t flags = 0;
193 [[=welder::doc(
194 "If auto-generated: bits derived from the material's flags and blend mode.")
195 ]]
196 std::uint8_t flags2 = 0;
197 [[=welder::doc("Unknown.")]]
198 std::uint16_t unknown1 = 0;
199 [[=welder::doc("The submesh shadowed.")]]
200 std::uint16_t submeshId = 0;
201 [[=welder::doc("Already looked up.")]]
202 std::uint16_t textureId = 0;
203 [[=welder::doc("Into the model colors.")]]
204 std::uint16_t colorId = 0;
205 [[=welder::doc("Already looked up.")]]
206 std::uint16_t transparencyId = 0;
207
208 bool operator==(const M2ShadowBatch&) const = default;
209 };
211 static_assert(sizeof(M2ShadowBatch) == 12);
212
213 namespace detail {
214 // The annotated era layouts; instantiate through the canonicalizing
215 // aliases below, never directly.
216 template <ClientVersion V>
217 struct [[
218 =welder::weld,
219 =welder::doc(
220 "One LOD view: local vertex/bone lookups into the model, submeshes and "
221 "render batches; embedded in the model pre-WotLK, an own .skin file "
222 "after.")
224 [[=welder::doc("Local -> global vertex lookup.")]]
225 std::vector<std::uint16_t> vertices;
226 [[
228 =formats::indexes("vertices"),
229 =welder::doc("Triangle list into the local vertices.")]]
230 std::vector<std::uint16_t> indices;
231 [[
232 =formats::countMatches("vertices"),
233 =welder::doc("Per-vertex 4-bone indices.")]]
234 std::vector<std::array<std::uint8_t, 4>> bones;
235 // through the skin:: alias, NOT the sibling detail raw — member types
236 // must collapse to the same canonical the welded classes use
237 [[=welder::doc("The submeshes (skin sections).")]]
238 std::vector<skin::M2SkinSection<V>> submeshes;
239 [[=welder::doc("The render batches (texture units).")]]
240 std::vector<M2Batch> batches;
241 [[=welder::doc("Max bones per draw call (21/53/64/256).")]]
242 std::uint32_t boneCountMax = 0;
243
244 [[
246 =welder::doc("Shadow batches (Cata+).")]]
247 std::vector<M2ShadowBatch> shadowBatches;
248
255
259 [[=welder::mark::exclude]]
260 void validateExtra(ValidationReport& report) const {
261 for (std::size_t i = 0; i < submeshes.size(); ++i) {
262 const auto& submesh = submeshes[i];
263 const std::size_t indexStart = std::size_t{submesh.indexStart} + (std::size_t{submesh.level} << 16);
264 if (std::size_t{submesh.vertexStart} + submesh.vertexCount > vertices.size())
265 report.addError(std::format("submeshes[{}]", i),
266 std::format("vertex range [{}, {}) overruns the {} local vertices", submesh.vertexStart,
267 submesh.vertexStart + submesh.vertexCount, vertices.size()));
268 if (indexStart + submesh.indexCount > indices.size())
269 report.addError(std::format("submeshes[{}]", i),
270 std::format("index range [{}, {}) overruns the {} indices", indexStart,
271 indexStart + submesh.indexCount, indices.size()));
272 if (submesh.indexCount % 3 != 0)
273 report.addError(std::format("submeshes[{}]", i),
274 std::format("index count {} is not a multiple of 3", submesh.indexCount));
275 }
276
277 for (std::size_t i = 0; i < batches.size(); ++i)
278 if (batches[i].skinSectionIndex >= submeshes.size())
279 report.addError(std::format("batches[{}]", i),
280 std::format("skin_section_index {} out of range: {} submeshes",
281 batches[i].skinSectionIndex, submeshes.size()));
282 }
283
284 bool operator==(const M2SkinProfile&) const = default;
285 };
286 }
287
290 template <ClientVersion V>
291 using M2SkinProfile = detail::M2SkinProfile<canonicalVersion(V, M2SkinProfilePivots, M2Versions)>;
292}
void addError(std::string path, std::string message)
Record an error finding (see add()).
Client version identity, the flavor axis that separates a client's CONTENT version from the engine ge...
The chunk annotation vocabulary format entities declare their binary mapping with.
Binding-language identities welder's core does not name.
#define WOWLIB_CS_FAMILY_SURFACE
The C# rod's family-surface opt-in, spellable in every build.
Definition lang.hpp:44
The M2 version vocabulary: the layout pivots record/entity partial specializations key on,...
One external LOD view of a model ("{model}0N.skin", or SFID FileDataIDs in Legion+).
Definition records.hpp:51
The external LOD view entity (.skin file, WotLK+) and the skin profile records (submeshes,...
Definition records.hpp:26
detail::M2SkinSection< canonicalVersion(V, M2SkinSectionPivots, M2Versions)> M2SkinSection
A skin submesh — the canonicalizing face of detail::M2SkinSection (M2SkinSectionPivots: TBC's sort ce...
Definition records.hpp:123
detail::M2SkinProfile< canonicalVersion(V, M2SkinProfilePivots, M2Versions)> M2SkinProfile
One LOD view — the canonicalizing face of detail::M2SkinProfile (M2SkinProfilePivots: TBC's section l...
Definition records.hpp:253
constexpr ClientVersion M2MultitexParticles
cata (v272): particles turn multi-textured (492-byte record: texture id bitfield, multiTexScale repla...
constexpr std::array M2SkinProfilePivots
M2SkinProfile: the section layout at TBC, shadow batches at Cata.
constexpr std::array M2SkinSectionPivots
M2SkinSection: the sort center/radius tail at TBC.
constexpr ClientVersion M2CompressedBones
TBC (v260+): bone rotations become compressed M2CompQuat (vanilla stored raw C4Quaternion),...
constexpr std::array M2Versions
The versions M2 is instantiated (and welded) for: every targeted last-minor-of-major release,...
consteval detail::IndexesSpec indexes(std::string_view name)
Declare a referential contract: every element of this (integral) vector member is an index into the n...
consteval detail::CountMultipleOfSpec countMultipleOf(std::uint32_t divisor)
Declare a granularity contract: the member's element count must be a multiple of divisor (triangle in...
constexpr ClientVersion canonicalVersion(ClientVersion v, std::span< const ClientVersion > pivots, std::span< const ClientVersion > grid)
The canonical version v collapses to: the FIRST grid version in v's range.
consteval detail::SinceSpec since(ClientVersion v)
Restrict a member to entity versions >= v (inclusive).
consteval detail::CountMatchesSpec countMatches(std::string_view name, std::uint32_t scale=1)
Declare a companion-count contract: when this member is engaged (non-empty), its element count times ...
A 3D float vector — the workhorse: positions, normals, rotations-as-Euler-degrees in placements.
Definition types.hpp:49
An M2 skin render batch (texture unit): submesh + material + the lookup bases the shaders consume.
Definition records.hpp:130
std::uint16_t textureComboIndex
Into the texture lookup.
Definition records.hpp:150
std::uint16_t colorIndex
Into the model colors, -1 if none.
Definition records.hpp:142
std::uint16_t geosetIndex
BfA+: renamed flags2 (0x2 projected, 0x8 EDGF).
Definition records.hpp:140
std::int8_t priorityPlane
Draw-order priority plane.
Definition records.hpp:134
std::uint16_t materialIndex
Into the model materials.
Definition records.hpp:144
std::uint16_t materialLayer
Capped at 7.
Definition records.hpp:146
std::uint16_t textureCount
1..4 consecutive lookup entries.
Definition records.hpp:148
std::uint8_t flags
0x10 static texture, 0x40 transparency quirk, ...
Definition records.hpp:132
std::uint16_t textureWeightComboIndex
Into the transparency lookup.
Definition records.hpp:154
std::uint16_t textureTransformComboIndex
Into the UV-animation lookup.
Definition records.hpp:156
std::uint16_t skinSectionIndex
The submesh this batch renders.
Definition records.hpp:138
std::uint16_t textureCoordComboIndex
Into the texture-mapping lookup.
Definition records.hpp:152
std::uint16_t shaderId
Pre-Cata: 0 on disk, computed at runtime.
Definition records.hpp:136
bool operator==(const M2Batch &) const =default
An M2 skin shadow batch (Cata+); the client can also generate these from the render batches.
Definition records.hpp:164
std::uint16_t textureId
Already looked up.
Definition records.hpp:174
std::uint8_t flags
If auto-generated: the source render batch's flags.
Definition records.hpp:166
std::uint8_t flags2
If auto-generated: bits derived from the material's flags and blend mode.
Definition records.hpp:168
bool operator==(const M2ShadowBatch &) const =default
std::uint16_t submeshId
The submesh shadowed.
Definition records.hpp:172
std::uint16_t colorId
Into the model colors.
Definition records.hpp:176
std::uint16_t transparencyId
Already looked up.
Definition records.hpp:178
The version-agnostic base of every M2SkinProfile<V> (welded as "M2SkinProfile").
Definition records.hpp:46
bool operator==(const M2SkinProfileBase &) const =default
The version-agnostic base of every M2SkinSection<V> (welded as "M2SkinSection").
Definition records.hpp:34
bool operator==(const M2SkinSectionBase &) const =default
One LOD view: local vertex/bone lookups into the model, submeshes and render batches; embedded in the...
Definition records.hpp:190
void validateExtra(ValidationReport &report) const
Validation hook (see formats::detail::validateValue): the submesh and batch slices the annotations ca...
Definition records.hpp:222
bool operator==(const M2SkinProfile &) const =default
std::uint16_t vertexStart
First local vertex.
Definition records.hpp:65
std::uint16_t indexStart
First triangle index.
Definition records.hpp:69
std::uint16_t skinSectionId
Mesh part (geoset) id.
Definition records.hpp:61
std::uint16_t indexCount
Triangle index count.
Definition records.hpp:71
std::uint16_t boneComboIndex
First bone-lookup entry.
Definition records.hpp:75
float sortRadius
Distance of the farthest vertex from it.
Definition records.hpp:114
C3Vector sortCenterPosition
Center of the submesh bounding box.
Definition records.hpp:112
std::uint16_t vertexCount
Local vertex count.
Definition records.hpp:67
std::uint16_t centerBoneIndex
The bone nearest the submesh center; wowdev leaves it otherwise undescribed.
Definition records.hpp:79
std::uint16_t boneCount
Bone-lookup entries used.
Definition records.hpp:73
std::uint16_t level
Extends the 16-bit index_start by (level << 16) — index lists outgrow 64k first; vertex_start stays p...
Definition records.hpp:63
bool operator==(const M2SkinSection &) const =default
C3Vector centerPosition
Average vertex position.
Definition records.hpp:81
std::uint16_t boneInfluences
Highest bones-per-vertex in the submesh.
Definition records.hpp:77
The M2 animation vocabulary (namespace wowlib::formats::m2::root::record): the small fixed-size primi...
The validation vocabulary: the severity scale, the single finding and the report validate() fills.