18#include <welder/vocabulary.hpp>
30 The magma/slime reading of an MLIQ vertex (8 bytes): two int16 texture
31 coordinates sharing the layout with the water reading (SMOLVert). Obtain
32 one with SMOLVert.as_magma(); which reading is correct is set by the
33 group's liquid type, not stored per vertex.)")
35 [[=welder::doc("Texture coordinate s (the water flow1/flow2 bytes).")]]
38 [[=welder::doc(
"Texture coordinate t (the water flow1_pct/filler bytes).")]]
41 [[=welder::doc(
"Liquid surface height, shared with the water reading.")]]
45 static_assert(
sizeof(
SMOMVert) == 0x8);
50 One MLIQ liquid vertex (8 bytes). The byte layout is fixed but its
51 interpretation depends on the group's liquid type: water/ocean read the
52 named flow fields; magma/slime reinterpret the first four bytes as two
53 int16 texture coordinates (s, t) — use as_magma()/set_magma() for that
54 reading. Both are 8 bytes, so round-trip is layout-agnostic — pick the
55 reading from the group's liquid type.)")
57 [[=welder::doc("Water: flow velocity 1. Magma/slime: low byte of int16 s.")]
59 std::uint8_t flow1 = 0;
61 [[=welder::doc(
"Water: flow velocity 2. Magma/slime: high byte of int16 s.")
63 std::uint8_t flow2 = 0;
66 "Water: flow-1 percentage. Magma/slime: low byte of int16 t.")]]
67 std::uint8_t flow1Pct = 0;
69 [[=welder::doc(
"Water: filler. Magma/slime: high byte of int16 t.")]]
70 std::uint8_t filler = 0;
72 [[=welder::doc(
"Liquid surface height at this vertex.")]]
76 [[=welder::doc(
"Reinterpret this vertex under the magma/slime reading "
77 "(int16 s, t texcoords). A pure byte reinterpretation; use "
78 "it only when the group's liquid type is magma or slime.")]]
79 SMOMVert asMagma()
const {
return std::bit_cast<SMOMVert>(*
this); }
81 [[=welder::doc(
"Overwrite this vertex's bytes from a magma/slime reading.")]
83 void setMagma(
const SMOMVert& magma) {
84 *
this = std::bit_cast<SMOLVert>(magma);
88 static_assert(
sizeof(
SMOLVert) == 0x8);
92 =welder::doc(
"One MLIQ tile flag byte: bits 0-5 the legacy liquid type, "
93 "bit 6 fishable, bit 7 shared with an adjacent group.")
95 [[=welder::doc(
"Packed liquid-type / fishable / shared flags.")]]
96 std::uint8_t flags = 0;
104 Structured MLIQ liquid data: a vertex grid and a tile-flag grid with a
105 base position and material id. The on-disk header is 30 bytes (the
106 uint16 material id sits directly after a 12-byte vector, so the header
107 is read field-by-field, not memcpy'd) followed by verts_dim.x*verts_dim.y
108 vertices and tiles_dim.x*tiles_dim.y tile bytes.)")
110 [[=welder::doc("Vertex grid dimensions (xverts, yverts).")]]
113 [[=welder::doc(
"Tile grid dimensions (xtiles, ytiles); one less than the "
114 "vertex dimensions in each axis.")]]
117 [[=welder::doc(
"Grid origin in group space.")]]
120 [[=welder::doc(
"Liquid material id: an MOMT index, or a LiquidType id when "
121 "the root's use_liquid_type_dbc_id flag is set.")]]
124 [[=welder::doc(
"verts_dim.x * verts_dim.y liquid vertices, row-major."),
125 =welder::mark::no_reassign]]
128 [[=welder::doc(
"tiles_dim.x * tiles_dim.y tile flag bytes, row-major."),
129 =welder::mark::no_reassign]]
130 std::vector<SMOLTile>
tiles;
143 [[=welder::mark::exclude]]
147 std::format(
"MLIQ header needs {} bytes, got {}",
155 const std::size_t nVerts =
156 static_cast<std::size_t
>(
vertsDim.x) *
static_cast<std::size_t
>(
158 const std::size_t nTiles =
159 static_cast<std::size_t
>(
tilesDim.x) *
static_cast<std::size_t
>(
161 const std::size_t need =
163 if (payload.size() < need)
165 std::format(
"MLIQ body needs {} bytes ({} verts, {} "
167 need, nVerts, nTiles, payload.size()));
172 tiles.resize(nTiles);
173 std::memcpy(
tiles.data(),
183 [[=welder::mark::exclude]]
185 const auto put = [&out](
const auto& field) {
186 const auto* b =
reinterpret_cast<const std::byte*
>(&field);
187 out.insert(out.end(), b, b +
sizeof field);
194 const auto* vb =
reinterpret_cast<const std::byte*
>(vertices.data());
195 out.insert(out.end(), vb, vb + vertices.size() *
sizeof(
SMOLVert));
196 const auto* tb =
reinterpret_cast<const std::byte*
>(tiles.data());
197 out.insert(out.end(), tb, tb + tiles.size() *
sizeof(
SMOLTile));
202 [[=welder::getter, =welder::doc(
203 "Whether this group carries no liquid grid.")]]
204 bool empty()
const {
return vertices.empty(); }
The owning byte buffer file contents are read into.
The error-handling vocabulary: ErrorCode, Error and the Result<T> alias every fallible wowlib operati...
std::expected< T, Error > Result
Every fallible wowlib operation returns Result<T>; bindings translate the error branch into a target-...
std::unexpected< Error > makeError(ErrorCode code, std::string message, std::uint32_t nativeError=0)
Shorthand for constructing the error branch of a Result.
@ ChunkTruncated
A chunk header or payload overruns the file buffer.
std::vector< std::byte > FileBuffer
Owning byte buffer for file contents read out of a client storage.
The binary-level math and color primitives shared across WoW file formats (wowdev....