17#include <welder/vocabulary.hpp>
33 =welder::weld_as(
"M2SkinSection"),
36 One renderable geometry section (submesh). Abstract over the client version; construct a concrete
37 version with M2SkinSection.ForVersion / for_version.)")
49 =welder::weld_as(
"M2SkinProfile"),
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.)")
62 template <ClientVersion V>
68 =welder::doc(
"A skin submesh, vanilla layout (no sort data yet).")
70 [[=welder::doc(
"Mesh part (geoset) id.")]]
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).")
76 [[=welder::doc(
"First local vertex.")]]
78 [[=welder::doc(
"Local vertex count.")]]
80 [[=welder::doc(
"First triangle index.")]]
82 [[=welder::doc(
"Triangle index count.")]]
84 [[=welder::doc(
"Bone-lookup entries used.")]]
86 [[=welder::doc(
"First bone-lookup entry.")]]
88 [[=welder::doc(
"Highest bones-per-vertex in the submesh.")]]
91 "The bone nearest the submesh center; wowdev leaves it otherwise undescribed.")
94 [[=welder::doc(
"Average vertex position.")]]
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;
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).")
110 std::uint16_t level = 0;
111 [[=welder::doc(
"First local vertex.")]]
113 [[=welder::doc(
"Local vertex count.")]]
115 [[=welder::doc(
"First triangle index.")]]
117 [[=welder::doc(
"Triangle index count.")]]
119 [[=welder::doc(
"Bone-lookup entries used.")]]
121 [[=welder::doc(
"First bone-lookup entry.")]]
123 [[=welder::doc(
"Highest bones-per-vertex in the submesh.")]]
124 std::uint16_t boneInfluences = 0;
126 "The bone nearest the submesh center; wowdev leaves it otherwise undescribed.")
128 std::uint16_t centerBoneIndex = 0;
129 [[=welder::doc(
"Average vertex position.")]]
131 [[=welder::doc(
"Center of the submesh bounding box.")]]
133 [[=welder::doc(
"Distance of the farthest vertex from it.")]]
134 float sortRadius = 0;
142 template <ClientVersion V>
151 =welder::doc(
"An M2 skin render batch (texture unit): submesh + material "
152 "+ the lookup bases the shaders consume.")
154 [[=welder::doc(
"0x10 static texture, 0x40 transparency quirk, ...")]]
155 std::uint8_t
flags = 0;
156 [[=welder::doc(
"Draw-order priority plane.")]]
158 [[=welder::doc(
"Pre-Cata: 0 on disk, computed at runtime.")]]
160 [[=welder::doc(
"The submesh this batch renders.")]]
162 [[=welder::doc(
"BfA+: renamed flags2 (0x2 projected, 0x8 EDGF).")]]
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;
184 static_assert(
sizeof(
M2Batch) == 24);
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;
194 "If auto-generated: bits derived from the material's flags and blend mode.")
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;
216 template <ClientVersion V>
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 "
224 [[=welder::doc(
"Local -> global vertex lookup.")]]
225 std::vector<std::uint16_t>
vertices;
229 =welder::doc(
"Triangle list into the local vertices.")]]
230 std::vector<std::uint16_t>
indices;
233 =welder::doc(
"Per-vertex 4-bone indices.")]]
234 std::vector<std::array<std::uint8_t, 4>>
bones;
237 [[=welder::doc(
"The submeshes (skin sections).")]]
238 std::vector<skin::M2SkinSection<V>>
submeshes;
239 [[=welder::doc(
"The render batches (texture units).")]]
241 [[=welder::doc(
"Max bones per draw call (21/53/64/256).")]]
246 =welder::doc(
"Shadow batches (Cata+).")]]
259 [[=welder::mark::exclude]]
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));
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()));
290 template <ClientVersion V>
Client version identity, the flavor axis that separates a client's CONTENT version from the engine ge...
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.
The M2 version vocabulary: the layout pivots record/entity partial specializations key on,...
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.