mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-06 00:53:52 +00:00
feat: WOB material serialization, FORMAT_SPEC v1.1, material tests
- WOB save/load now serializes Material struct fields (flags, shader, blendMode, texturePath) per group — was saving only texture paths - FORMAT_SPEC.md v1.1: documents WOT doodad/WMO placements, WOB material fields, doodad rotation, terrain stamps, WCP file list - Test coverage: 5 new assertions verify material round-trip (flags, shader, blendMode all preserved through save→load cycle) - 260 total assertions across 75 test cases, all passing
This commit is contained in:
parent
9e33ad645c
commit
47eff19cb6
3 changed files with 60 additions and 10 deletions
|
|
@ -64,6 +64,23 @@ WoweeBuilding WoweeBuildingLoader::load(const std::string& basePath) {
|
||||||
f.read(tp.data(), tl);
|
f.read(tp.data(), tl);
|
||||||
grp.texturePaths.push_back(tp);
|
grp.texturePaths.push_back(tp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read material data (v1.1+)
|
||||||
|
uint32_t mc = 0;
|
||||||
|
if (f.read(reinterpret_cast<char*>(&mc), 4) && mc > 0 && mc <= 256) {
|
||||||
|
for (uint32_t mi = 0; mi < mc; mi++) {
|
||||||
|
WoweeBuilding::Material mat;
|
||||||
|
uint16_t pl;
|
||||||
|
f.read(reinterpret_cast<char*>(&pl), 2);
|
||||||
|
mat.texturePath.resize(pl);
|
||||||
|
f.read(mat.texturePath.data(), pl);
|
||||||
|
f.read(reinterpret_cast<char*>(&mat.flags), 4);
|
||||||
|
f.read(reinterpret_cast<char*>(&mat.shader), 4);
|
||||||
|
f.read(reinterpret_cast<char*>(&mat.blendMode), 4);
|
||||||
|
grp.materials.push_back(mat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bld.groups.push_back(std::move(grp));
|
bld.groups.push_back(std::move(grp));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,6 +157,18 @@ bool WoweeBuildingLoader::save(const WoweeBuilding& bld, const std::string& base
|
||||||
f.write(reinterpret_cast<const char*>(&tl), 2);
|
f.write(reinterpret_cast<const char*>(&tl), 2);
|
||||||
f.write(tp.data(), tl);
|
f.write(tp.data(), tl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write material data
|
||||||
|
uint32_t mc = static_cast<uint32_t>(grp.materials.size());
|
||||||
|
f.write(reinterpret_cast<const char*>(&mc), 4);
|
||||||
|
for (const auto& mat : grp.materials) {
|
||||||
|
uint16_t pl = static_cast<uint16_t>(mat.texturePath.size());
|
||||||
|
f.write(reinterpret_cast<const char*>(&pl), 2);
|
||||||
|
f.write(mat.texturePath.data(), pl);
|
||||||
|
f.write(reinterpret_cast<const char*>(&mat.flags), 4);
|
||||||
|
f.write(reinterpret_cast<const char*>(&mat.shader), 4);
|
||||||
|
f.write(reinterpret_cast<const char*>(&mat.blendMode), 4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& portal : bld.portals) {
|
for (const auto& portal : bld.portals) {
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,12 @@ TEST_CASE("WOB save and load round-trip", "[wob]") {
|
||||||
grp.vertices.push_back({{0,1,0}, {0,0,1}, {0,1}, {1,1,1,1}});
|
grp.vertices.push_back({{0,1,0}, {0,0,1}, {0,1}, {1,1,1,1}});
|
||||||
grp.indices = {0, 1, 2};
|
grp.indices = {0, 1, 2};
|
||||||
grp.texturePaths.push_back("textures/wall.png");
|
grp.texturePaths.push_back("textures/wall.png");
|
||||||
|
WoweeBuilding::Material mat;
|
||||||
|
mat.texturePath = "textures/wall.png";
|
||||||
|
mat.flags = 0x10;
|
||||||
|
mat.shader = 3;
|
||||||
|
mat.blendMode = 1;
|
||||||
|
grp.materials.push_back(mat);
|
||||||
bld.groups.push_back(grp);
|
bld.groups.push_back(grp);
|
||||||
|
|
||||||
WoweeBuilding::Portal portal;
|
WoweeBuilding::Portal portal;
|
||||||
|
|
@ -63,6 +69,11 @@ TEST_CASE("WOB save and load round-trip", "[wob]") {
|
||||||
REQUIRE(loaded.groups[0].vertices.size() == 3);
|
REQUIRE(loaded.groups[0].vertices.size() == 3);
|
||||||
REQUIRE(loaded.groups[0].indices.size() == 3);
|
REQUIRE(loaded.groups[0].indices.size() == 3);
|
||||||
REQUIRE(loaded.groups[0].isOutdoor == false);
|
REQUIRE(loaded.groups[0].isOutdoor == false);
|
||||||
|
REQUIRE(loaded.groups[0].materials.size() == 1);
|
||||||
|
REQUIRE(loaded.groups[0].materials[0].texturePath == "textures/wall.png");
|
||||||
|
REQUIRE(loaded.groups[0].materials[0].flags == 0x10);
|
||||||
|
REQUIRE(loaded.groups[0].materials[0].shader == 3);
|
||||||
|
REQUIRE(loaded.groups[0].materials[0].blendMode == 1);
|
||||||
REQUIRE(loaded.portals.size() == 1);
|
REQUIRE(loaded.portals.size() == 1);
|
||||||
REQUIRE(loaded.portals[0].groupA == 0);
|
REQUIRE(loaded.portals[0].groupA == 0);
|
||||||
REQUIRE(loaded.portals[0].vertices.size() == 4);
|
REQUIRE(loaded.portals[0].vertices.size() == 4);
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,18 @@
|
||||||
# Wowee Open Format Specification v1.0
|
# Wowee Open Format Specification v1.1
|
||||||
|
|
||||||
Novel file formats for custom WoW zone content. No Blizzard IP.
|
Novel file formats for custom WoW zone content. No Blizzard IP.
|
||||||
|
|
||||||
## WOT — Wowee Open Terrain (JSON metadata)
|
## WOT — Wowee Open Terrain (JSON metadata)
|
||||||
- Extension: `.wot`
|
- Extension: `.wot`
|
||||||
- Contains: tile coords, texture list, per-chunk layers/holes, water data
|
- Contains: tile coords, texture list, per-chunk layers/holes, water data,
|
||||||
|
doodad placements (M2 objects), WMO placements (buildings)
|
||||||
- Key: `"format": "wot-1.0"`
|
- Key: `"format": "wot-1.0"`
|
||||||
|
- Placement fields: `doodadNames[]`, `doodads[]` (nameId, uniqueId, pos, rot, scale, flags)
|
||||||
|
- WMO fields: `wmoNames[]`, `wmos[]` (nameId, uniqueId, pos, rot, flags, doodadSet)
|
||||||
|
|
||||||
## WHM — Wowee HeightMap (binary)
|
## WHM — Wowee HeightMap (binary)
|
||||||
- Extension: `.whm`
|
- Extension: `.whm`
|
||||||
- Magic: `WHM1` (0x314D4857)
|
- Magic: `WHM1` (0x314D4857)
|
||||||
- Version: 1 (embedded in magic — WHM2 for future revisions)
|
|
||||||
- Layout: magic(4) + chunks(4) + vertsPerChunk(4) + per-chunk data × 256
|
- Layout: magic(4) + chunks(4) + vertsPerChunk(4) + per-chunk data × 256
|
||||||
- Per-chunk: baseHeight(4) + heights[145](580) + alphaSize(4) + alphaData(alphaSize)
|
- Per-chunk: baseHeight(4) + heights[145](580) + alphaSize(4) + alphaData(alphaSize)
|
||||||
- Alpha data: raw alpha blend maps for texture layers (same format as ADT MCAL)
|
- Alpha data: raw alpha blend maps for texture layers (same format as ADT MCAL)
|
||||||
|
|
@ -18,29 +20,32 @@ Novel file formats for custom WoW zone content. No Blizzard IP.
|
||||||
|
|
||||||
## WOM — Wowee Open Model (binary)
|
## WOM — Wowee Open Model (binary)
|
||||||
- Extension: `.wom`
|
- Extension: `.wom`
|
||||||
- Magic: `WOM1` (0x314D4F57) — version 1
|
- Magic: `WOM1` (0x314D4F57)
|
||||||
- Version: embedded in magic (WOM2 for future revisions with animation support)
|
|
||||||
- Layout: magic(4) + vertCount(4) + indexCount(4) + texCount(4) + bounds(28) + name + vertices + indices + texPaths
|
- Layout: magic(4) + vertCount(4) + indexCount(4) + texCount(4) + bounds(28) + name + vertices + indices + texPaths
|
||||||
- Vertex: position(vec3) + normal(vec3) + texCoord(vec2) = 32 bytes
|
- Vertex: position(vec3) + normal(vec3) + texCoord(vec2) = 32 bytes
|
||||||
|
- Note: geometry-only (no skeletal animation — WOM2 planned for bone data)
|
||||||
|
|
||||||
## WOB — Wowee Open Building (binary)
|
## WOB — Wowee Open Building (binary)
|
||||||
- Extension: `.wob`
|
- Extension: `.wob`
|
||||||
- Magic: `WOB1` (0x31424F57)
|
- Magic: `WOB1` (0x31424F57)
|
||||||
- Layout: magic(4) + groupCount(4) + portalCount(4) + doodadCount(4) + bounds(4) + name + groups + portals + doodads
|
- Layout: magic(4) + groupCount(4) + portalCount(4) + doodadCount(4) + bounds(4) + name + groups + portals + doodads
|
||||||
- Group: name + vertices(pos+normal+uv+color) + indices + texPaths + bounds + outdoor flag
|
- Group: name + vertexCount(4) + indexCount(4) + texCount(4) + outdoor(1) + bounds(24)
|
||||||
|
+ vertices(pos+normal+uv+color) + indices + texPaths + materialCount(4) + materials
|
||||||
|
- Material: texturePath + flags(4) + shader(4) + blendMode(4)
|
||||||
|
- Doodad: modelPath + position(12) + rotation(12) + scale(4)
|
||||||
|
- Portal: groupA(4) + groupB(4) + vertexCount(4) + vertices
|
||||||
|
|
||||||
## WCP — Wowee Content Pack (archive)
|
## WCP — Wowee Content Pack (archive)
|
||||||
- Extension: `.wcp`
|
- Extension: `.wcp`
|
||||||
- Magic: `WCP1` (0x31504357)
|
- Magic: `WCP1` (0x31504357)
|
||||||
- Layout: magic(4) + fileCount(4) + infoJsonSize(4) + infoJSON + [pathLen(2) + path + dataSize(4) + data] × N
|
- Layout: magic(4) + fileCount(4) + infoJsonSize(4) + infoJSON + [pathLen(2) + path + dataSize(4) + data] × N
|
||||||
|
- Info JSON includes categorized file list (terrain/model/building/texture/data)
|
||||||
|
|
||||||
## zone.json — Map Definition
|
## zone.json — Map Definition
|
||||||
- Replaces WDT
|
- Replaces WDT
|
||||||
- Contains: mapName, mapId, tiles, biome, file references
|
- Fields: `mapName`, `displayName`, `mapId`, `biome`, `baseHeight`
|
||||||
|
|
||||||
## zone.json Fields
|
|
||||||
- `mapName`, `displayName`, `mapId`, `biome`, `baseHeight`
|
|
||||||
- `hasCreatures`, `description`, `tiles` array, `files` map
|
- `hasCreatures`, `description`, `tiles` array, `files` map
|
||||||
|
- `doodadNames[]`, `doodads[]`, `wmoNames[]`, `wmos[]` for placed objects
|
||||||
- `editorVersion` for compatibility tracking
|
- `editorVersion` for compatibility tracking
|
||||||
|
|
||||||
## JSON DBC — Data Table Replacement
|
## JSON DBC — Data Table Replacement
|
||||||
|
|
@ -54,6 +59,11 @@ Novel file formats for custom WoW zone content. No Blizzard IP.
|
||||||
- Standard PNG format, loaded by client's texture override system
|
- Standard PNG format, loaded by client's texture override system
|
||||||
- Editor auto-converts BLP→PNG on export via stb_image_write
|
- Editor auto-converts BLP→PNG on export via stb_image_write
|
||||||
|
|
||||||
|
## Terrain Stamps (.json)
|
||||||
|
- Portable terrain feature snapshots (mountains, craters, etc.)
|
||||||
|
- Format: `{"format": "wowee-stamp-1.0", "vertices": [[dx, dy, height], ...]}`
|
||||||
|
- Can be saved/loaded across zones and sessions
|
||||||
|
|
||||||
## Open Format Scoring (0-6)
|
## Open Format Scoring (0-6)
|
||||||
1. WOT terrain metadata present
|
1. WOT terrain metadata present
|
||||||
2. WHM heightmap with valid magic
|
2. WHM heightmap with valid magic
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue