diff --git a/tools/editor/wowee_terrain.cpp b/tools/editor/wowee_terrain.cpp index 26c0bcd7..eafe82fb 100644 --- a/tools/editor/wowee_terrain.cpp +++ b/tools/editor/wowee_terrain.cpp @@ -1,5 +1,6 @@ #include "wowee_terrain.hpp" #include "core/logger.hpp" +#include "stb_image_write.h" #include #include #include @@ -87,6 +88,39 @@ bool WoweeTerrain::exportOpen(const pipeline::ADTTerrain& terrain, return true; } +bool WoweeTerrain::exportNormalMap(const pipeline::ADTTerrain& terrain, + const std::string& path) { + // Export 129x129 normal map as RGB PNG + constexpr int res = 129; + std::vector pixels(res * res * 3); + + for (int cy = 0; cy < 16; cy++) { + for (int cx = 0; cx < 16; cx++) { + const auto& chunk = terrain.chunks[cy * 16 + cx]; + if (!chunk.hasHeightMap()) continue; + for (int v = 0; v < 145; v++) { + int row = v / 17, col = v % 17; + if (col > 8) continue; + int px = cx * 8 + col, py = cy * 8 + row; + if (px >= res || py >= res) continue; + + int ni = v * 3; + float nx = static_cast(chunk.normals[ni]) / 127.0f; + float ny = static_cast(chunk.normals[ni + 1]) / 127.0f; + float nz = static_cast(chunk.normals[ni + 2]) / 127.0f; + + int idx = (py * res + px) * 3; + pixels[idx] = static_cast((nx * 0.5f + 0.5f) * 255); + pixels[idx + 1] = static_cast((ny * 0.5f + 0.5f) * 255); + pixels[idx + 2] = static_cast((nz * 0.5f + 0.5f) * 255); + } + } + } + + stbi_write_png(path.c_str(), res, res, 3, pixels.data(), res * 3); + return true; +} + bool WoweeTerrain::importOpen(const std::string& basePath, pipeline::ADTTerrain& terrain) { std::string hmPath = basePath + ".whm"; std::ifstream f(hmPath, std::ios::binary); diff --git a/tools/editor/wowee_terrain.hpp b/tools/editor/wowee_terrain.hpp index d97a453e..fa459af4 100644 --- a/tools/editor/wowee_terrain.hpp +++ b/tools/editor/wowee_terrain.hpp @@ -14,6 +14,10 @@ public: static bool exportOpen(const pipeline::ADTTerrain& terrain, const std::string& basePath, int tileX, int tileY); + // Export normal map as PNG (129x129 RGB) + static bool exportNormalMap(const pipeline::ADTTerrain& terrain, + const std::string& path); + // Import terrain from open format back to ADTTerrain static bool importOpen(const std::string& basePath, pipeline::ADTTerrain& terrain); };