From 7177463df1a0419363ef4500871f327de05d87a7 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 5 May 2026 09:32:13 -0700 Subject: [PATCH] =?UTF-8?q?feat(editor):=20Wowee=20Open=20Terrain=20format?= =?UTF-8?q?=20(.wot/.whm)=20=E2=80=94=20no=20Blizzard=20formats?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Novel open terrain format unique to wowee: .wot (Wowee Open Terrain) — JSON metadata: - Tile coordinates, chunk grid dimensions, texture list - Per-chunk layer assignments and hole bitmasks - Water data per chunk (type, height) - Format version "wot-1.0" .whm (Wowee HeightMap) — binary heightmap: - "WHM1" magic header - 256 chunks × (baseHeight + 145 float heights) = 148KB - Direct float storage, no compression, fully portable Both formats are entirely novel — no ADT, no WDT, no Blizzard structures. The WCP content pack can bundle .wot/.whm instead of ADT/WDT for fully open redistribution. Import/export functions: WoweeTerrain::exportOpen() / importOpen() --- CMakeLists.txt | 1 + tools/editor/wowee_terrain.cpp | 118 +++++++++++++++++++++++++++++++++ tools/editor/wowee_terrain.hpp | 22 ++++++ 3 files changed, 141 insertions(+) create mode 100644 tools/editor/wowee_terrain.cpp create mode 100644 tools/editor/wowee_terrain.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bccc893..07ff151d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1295,6 +1295,7 @@ add_executable(wowee_editor tools/editor/transform_gizmo.cpp tools/editor/zone_manifest.cpp tools/editor/content_pack.cpp + tools/editor/wowee_terrain.cpp tools/editor/asset_browser.cpp tools/editor/editor_water.cpp tools/editor/editor_markers.cpp diff --git a/tools/editor/wowee_terrain.cpp b/tools/editor/wowee_terrain.cpp new file mode 100644 index 00000000..26c0bcd7 --- /dev/null +++ b/tools/editor/wowee_terrain.cpp @@ -0,0 +1,118 @@ +#include "wowee_terrain.hpp" +#include "core/logger.hpp" +#include +#include +#include + +namespace wowee { +namespace editor { + +bool WoweeTerrain::exportOpen(const pipeline::ADTTerrain& terrain, + const std::string& basePath, int tileX, int tileY) { + namespace fs = std::filesystem; + fs::create_directories(fs::path(basePath).parent_path()); + + // Export binary heightmap (.whm = Wowee HeightMap) + // Format: 256 chunks × 145 floats = 37120 floats (148480 bytes) + std::string hmPath = basePath + ".whm"; + { + std::ofstream f(hmPath, std::ios::binary); + if (!f) return false; + // Header: "WHM1" + chunkCount(4) + vertsPerChunk(4) + uint32_t magic = 0x314D4857; // "WHM1" + uint32_t chunks = 256, verts = 145; + f.write(reinterpret_cast(&magic), 4); + f.write(reinterpret_cast(&chunks), 4); + f.write(reinterpret_cast(&verts), 4); + // Per-chunk: baseHeight(4) + heights[145](580) + for (int ci = 0; ci < 256; ci++) { + const auto& chunk = terrain.chunks[ci]; + float base = chunk.position[2]; + f.write(reinterpret_cast(&base), 4); + f.write(reinterpret_cast(chunk.heightMap.heights.data()), 145 * 4); + } + } + + // Export JSON metadata (.wot = Wowee Open Terrain) + std::string jsonPath = basePath + ".wot"; + { + std::ofstream f(jsonPath); + if (!f) return false; + f << "{\n"; + f << " \"format\": \"wot-1.0\",\n"; + f << " \"tileX\": " << tileX << ",\n"; + f << " \"tileY\": " << tileY << ",\n"; + f << " \"chunkGrid\": [16, 16],\n"; + f << " \"vertsPerChunk\": 145,\n"; + f << " \"heightmapFile\": \"" << fs::path(hmPath).filename().string() << "\",\n"; + f << " \"textures\": [\n"; + for (size_t i = 0; i < terrain.textures.size(); i++) { + f << " \"" << terrain.textures[i] << "\""; + if (i + 1 < terrain.textures.size()) f << ","; + f << "\n"; + } + f << " ],\n"; + f << " \"chunkLayers\": [\n"; + for (int ci = 0; ci < 256; ci++) { + const auto& chunk = terrain.chunks[ci]; + f << " {\"layers\": ["; + for (size_t li = 0; li < chunk.layers.size(); li++) { + f << chunk.layers[li].textureId; + if (li + 1 < chunk.layers.size()) f << ","; + } + f << "], \"holes\": " << chunk.holes << "}"; + if (ci < 255) f << ","; + f << "\n"; + } + f << " ],\n"; + // Water data + f << " \"water\": [\n"; + for (int ci = 0; ci < 256; ci++) { + const auto& water = terrain.waterData[ci]; + if (water.hasWater()) { + f << " {\"chunk\": " << ci + << ", \"type\": " << water.layers[0].liquidType + << ", \"height\": " << water.layers[0].maxHeight << "}"; + } else { + f << " null"; + } + if (ci < 255) f << ","; + f << "\n"; + } + f << " ]\n"; + f << "}\n"; + } + + LOG_INFO("Open terrain exported: ", basePath, " (.wot + .whm)"); + return true; +} + +bool WoweeTerrain::importOpen(const std::string& basePath, pipeline::ADTTerrain& terrain) { + std::string hmPath = basePath + ".whm"; + std::ifstream f(hmPath, std::ios::binary); + if (!f) return false; + + uint32_t magic, chunks, verts; + f.read(reinterpret_cast(&magic), 4); + if (magic != 0x314D4857) return false; + f.read(reinterpret_cast(&chunks), 4); + f.read(reinterpret_cast(&verts), 4); + if (chunks != 256 || verts != 145) return false; + + terrain.loaded = true; + terrain.version = 18; + for (int ci = 0; ci < 256; ci++) { + auto& chunk = terrain.chunks[ci]; + chunk.heightMap.loaded = true; + float base; + f.read(reinterpret_cast(&base), 4); + chunk.position[2] = base; + f.read(reinterpret_cast(chunk.heightMap.heights.data()), 145 * 4); + } + + LOG_INFO("Open terrain imported: ", basePath); + return true; +} + +} // namespace editor +} // namespace wowee diff --git a/tools/editor/wowee_terrain.hpp b/tools/editor/wowee_terrain.hpp new file mode 100644 index 00000000..d97a453e --- /dev/null +++ b/tools/editor/wowee_terrain.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "pipeline/adt_loader.hpp" +#include + +namespace wowee { +namespace editor { + +// Wowee Open Terrain Format (.wot) +// JSON + binary heightmap — no Blizzard formats, fully open +class WoweeTerrain { +public: + // Export terrain to open format: .wot (JSON metadata) + .whm (binary heightmap) + static bool exportOpen(const pipeline::ADTTerrain& terrain, + const std::string& basePath, int tileX, int tileY); + + // Import terrain from open format back to ADTTerrain + static bool importOpen(const std::string& basePath, pipeline::ADTTerrain& terrain); +}; + +} // namespace editor +} // namespace wowee