feat: WOC collision mesh format — 7th novel open format

New format: WOC (Wowee Open Collision) — binary collision mesh for
custom zone walkability. Magic WOC1 (0x31434F57).

- WoweeCollisionBuilder::fromTerrain() generates collision triangles
  from terrain heightmap with slope classification (50 deg threshold)
- Per-triangle flags: walkable (0x01), water (0x02), steep (0x04)
- Respects terrain holes (skips triangles in hole regions)
- Binary save/load with bounds, tile coords, triangle data
- Auto-exported on zone save alongside WOT/WHM/WOM/WOB
- Added to content pack validation (score now 0-7)
- FORMAT_SPEC.md v1.1 updated with WOC binary layout
- 19 new test assertions: flat terrain generation (32k tris all
  walkable), save/load round-trip, hole skipping
- 328 total assertions across 84 test cases
This commit is contained in:
Kelsi 2026-05-05 15:23:58 -07:00
parent 961d863f82
commit 4d5eef480e
10 changed files with 342 additions and 7 deletions

View file

@ -202,6 +202,7 @@ ContentPacker::ValidationResult ContentPacker::validateZone(const std::string& z
static constexpr uint32_t WHM_MAGIC = 0x314D4857; // "WHM1"
static constexpr uint32_t WOM_MAGIC = 0x314D4F57; // "WOM1"
static constexpr uint32_t WOB_MAGIC = 0x31424F57; // "WOB1"
static constexpr uint32_t WOC_MAGIC = 0x31434F57; // "WOC1"
for (auto& entry : fs::recursive_directory_iterator(zoneDir)) {
if (!entry.is_regular_file()) continue;
@ -220,6 +221,10 @@ ContentPacker::ValidationResult ContentPacker::validateZone(const std::string& z
r.hasWob = true;
if (checkMagic(entry.path().string(), WOB_MAGIC)) r.wobValid = true;
}
if (ext == ".woc") {
r.hasWoc = true;
if (checkMagic(entry.path().string(), WOC_MAGIC)) r.wocValid = true;
}
if (ext == ".png") r.hasPng = true;
if (fname == "zone.json") r.hasZoneJson = true;
if (fname == "creatures.json") r.hasCreatures = true;
@ -237,7 +242,8 @@ int ContentPacker::ValidationResult::openFormatScore() const {
if (hasPng) score++;
if (hasWom && womValid) score++;
if (hasWob && wobValid) score++;
return score; // max 6 for fully open
if (hasWoc && wocValid) score++;
return score; // max 7 for fully open
}
std::string ContentPacker::ValidationResult::summary() const {
@ -252,6 +258,7 @@ std::string ContentPacker::ValidationResult::summary() const {
add(hasWhm, whmValid, "WHM");
add(hasWom, womValid, "WOM");
add(hasWob, wobValid, "WOB");
add(hasWoc, wocValid, "WOC");
if (hasZoneJson) s += "zone.json ";
if (hasPng) s += "PNG ";
if (hasCreatures) s += "creatures ";