mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-07 17:43:51 +00:00
feat(editor): add --bake-project-obj for whole-project terrain export
Project-level OBJ bake — combines every zone's terrain into one
giant OBJ with one 'g zone_NAME' block per zone. Useful for
previewing an entire multi-zone project's terrain in MeshLab/
Blender at once, or for printing the full map:
wowee_editor --bake-project-obj custom_zones
Baked custom_zones -> custom_zones/project.obj
2 zone(s), 3 tiles, 62208 verts, 98304 tris
Layout: single global vertex pool (so OBJ indexing stays valid),
per-zone face groups so designers can hide individual zones in
their viewer for area-by-area inspection. Hole bits respected.
Coords match WoweeCollisionBuilder's outer-grid layout exactly so
zones spatially line up at WoW grid boundaries — adjacent tiles
across zones connect seamlessly.
Pairs with the existing --bake-zone-* family (single zone) and
--export-project-html (web index of per-zone viewers). Three
levels of granularity now available:
--export-glb / --export-obj / --export-stl single model/file
--bake-zone-glb / -obj / -stl single zone
--bake-project-obj entire project <- new
Verified: 2-zone project (Forest 2 tiles + Desert 1 tile) baked
to project.obj with 62208 verts (3 × 20736), 98304 tris (3 ×
32768), 2 'g' blocks correctly named (zone_Desert, zone_Forest).
This commit is contained in:
parent
b628535a91
commit
54c309a779
1 changed files with 130 additions and 0 deletions
|
|
@ -592,6 +592,8 @@ static void printUsage(const char* argv0) {
|
|||
std::printf(" Bake every WHM tile in a zone into one STL for 3D-printing the terrain\n");
|
||||
std::printf(" --bake-zone-obj <zoneDir> [out.obj]\n");
|
||||
std::printf(" Bake every WHM tile in a zone into one Wavefront OBJ (one g-block per tile)\n");
|
||||
std::printf(" --bake-project-obj <projectDir> [out.obj]\n");
|
||||
std::printf(" Bake every zone in a project into one Wavefront OBJ (one g-block per zone)\n");
|
||||
std::printf(" --import-obj <obj-path> [wom-base]\n");
|
||||
std::printf(" Convert a Wavefront OBJ back into WOM (round-trips with --export-obj)\n");
|
||||
std::printf(" --export-wob-obj <wob-base> [out.obj]\n");
|
||||
|
|
@ -797,6 +799,7 @@ int main(int argc, char* argv[]) {
|
|||
"--export-glb", "--export-wob-glb", "--export-whm-glb",
|
||||
"--export-stl", "--import-stl",
|
||||
"--bake-zone-glb", "--bake-zone-stl", "--bake-zone-obj",
|
||||
"--bake-project-obj",
|
||||
"--convert-m2", "--convert-wmo",
|
||||
"--convert-dbc-json", "--convert-json-dbc", "--convert-blp-png",
|
||||
"--migrate-wom", "--migrate-zone", "--migrate-jsondbc",
|
||||
|
|
@ -7636,6 +7639,133 @@ int main(int argc, char* argv[]) {
|
|||
loadedTiles, totalVerts,
|
||||
static_cast<unsigned long long>(totalFaces));
|
||||
return 0;
|
||||
} else if (std::strcmp(argv[i], "--bake-project-obj") == 0 && i + 1 < argc) {
|
||||
// Project-level OBJ bake: every zone in <projectDir> gets
|
||||
// emitted into one giant OBJ with one 'g zone_NAME' block
|
||||
// per zone. Useful for previewing an entire project's terrain
|
||||
// in MeshLab/Blender at once, or for printing the whole map.
|
||||
std::string projectDir = argv[++i];
|
||||
std::string outPath;
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') outPath = argv[++i];
|
||||
namespace fs = std::filesystem;
|
||||
if (!fs::exists(projectDir) || !fs::is_directory(projectDir)) {
|
||||
std::fprintf(stderr,
|
||||
"bake-project-obj: %s is not a directory\n",
|
||||
projectDir.c_str());
|
||||
return 1;
|
||||
}
|
||||
if (outPath.empty()) outPath = projectDir + "/project.obj";
|
||||
std::vector<std::string> zoneDirs;
|
||||
for (const auto& entry : fs::directory_iterator(projectDir)) {
|
||||
if (!entry.is_directory()) continue;
|
||||
if (!fs::exists(entry.path() / "zone.json")) continue;
|
||||
zoneDirs.push_back(entry.path().string());
|
||||
}
|
||||
std::sort(zoneDirs.begin(), zoneDirs.end());
|
||||
if (zoneDirs.empty()) {
|
||||
std::fprintf(stderr,
|
||||
"bake-project-obj: no zones found in %s\n",
|
||||
projectDir.c_str());
|
||||
return 1;
|
||||
}
|
||||
std::ofstream out(outPath);
|
||||
if (!out) {
|
||||
std::fprintf(stderr,
|
||||
"bake-project-obj: cannot write %s\n", outPath.c_str());
|
||||
return 1;
|
||||
}
|
||||
constexpr float kTileSize = 533.33333f;
|
||||
constexpr float kChunkSize = kTileSize / 16.0f;
|
||||
constexpr float kVertSpacing = kChunkSize / 8.0f;
|
||||
out << "# Wavefront OBJ generated by wowee_editor --bake-project-obj\n";
|
||||
out << "# Project: " << projectDir << " (" << zoneDirs.size() << " zones)\n";
|
||||
// Single global vertex pool. Per-zone we accumulate verts then
|
||||
// emit faces; same shape as --bake-zone-obj.
|
||||
int totalZones = 0, totalTiles = 0;
|
||||
int totalVerts = 0;
|
||||
uint64_t totalFaces = 0;
|
||||
struct Pending {
|
||||
std::string zoneName;
|
||||
uint32_t vertBase; // 1-based OBJ index
|
||||
std::vector<uint32_t> faceI0, faceI1, faceI2;
|
||||
};
|
||||
std::vector<Pending> queues;
|
||||
for (const auto& zoneDir : zoneDirs) {
|
||||
wowee::editor::ZoneManifest zm;
|
||||
if (!zm.load(zoneDir + "/zone.json")) continue;
|
||||
Pending pq;
|
||||
pq.zoneName = zm.mapName;
|
||||
pq.vertBase = static_cast<uint32_t>(totalVerts + 1);
|
||||
int zoneTiles = 0;
|
||||
uint32_t zoneLocalIdx = 0;
|
||||
for (const auto& [tx, ty] : zm.tiles) {
|
||||
std::string tileBase = zoneDir + "/" + zm.mapName + "_" +
|
||||
std::to_string(tx) + "_" +
|
||||
std::to_string(ty);
|
||||
if (!wowee::pipeline::WoweeTerrainLoader::exists(tileBase)) continue;
|
||||
wowee::pipeline::ADTTerrain terrain;
|
||||
wowee::pipeline::WoweeTerrainLoader::load(tileBase, terrain);
|
||||
zoneTiles++;
|
||||
for (int cx = 0; cx < 16; ++cx) {
|
||||
for (int cy = 0; cy < 16; ++cy) {
|
||||
const auto& chunk = terrain.getChunk(cx, cy);
|
||||
if (!chunk.heightMap.isLoaded()) continue;
|
||||
float chunkBaseX = (32.0f - terrain.coord.y) * kTileSize - cy * kChunkSize;
|
||||
float chunkBaseY = (32.0f - terrain.coord.x) * kTileSize - cx * kChunkSize;
|
||||
uint32_t chunkBaseLocal = zoneLocalIdx;
|
||||
for (int row = 0; row < 9; ++row) {
|
||||
for (int col = 0; col < 9; ++col) {
|
||||
float x = chunkBaseX - row * kVertSpacing;
|
||||
float y = chunkBaseY - col * kVertSpacing;
|
||||
float z = chunk.position[2] +
|
||||
chunk.heightMap.heights[row * 17 + col];
|
||||
out << "v " << x << " " << y << " " << z << "\n";
|
||||
zoneLocalIdx++;
|
||||
}
|
||||
}
|
||||
bool isHoleChunk = (chunk.holes != 0);
|
||||
for (int row = 0; row < 8; ++row) {
|
||||
for (int col = 0; col < 8; ++col) {
|
||||
if (isHoleChunk) {
|
||||
int hx = col / 2, hy = row / 2;
|
||||
if (chunk.holes & (1 << (hy * 4 + hx))) continue;
|
||||
}
|
||||
auto idx = [&](int r, int c) {
|
||||
return chunkBaseLocal + r * 9 + c;
|
||||
};
|
||||
pq.faceI0.push_back(idx(row, col));
|
||||
pq.faceI1.push_back(idx(row, col + 1));
|
||||
pq.faceI2.push_back(idx(row + 1, col + 1));
|
||||
pq.faceI0.push_back(idx(row, col));
|
||||
pq.faceI1.push_back(idx(row + 1, col + 1));
|
||||
pq.faceI2.push_back(idx(row + 1, col));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (zoneLocalIdx == 0) continue;
|
||||
totalVerts += zoneLocalIdx;
|
||||
totalTiles += zoneTiles;
|
||||
totalZones++;
|
||||
queues.push_back(std::move(pq));
|
||||
}
|
||||
// After all verts written, emit faces grouped by zone.
|
||||
for (const auto& pq : queues) {
|
||||
out << "g zone_" << pq.zoneName << "\n";
|
||||
for (size_t k = 0; k < pq.faceI0.size(); ++k) {
|
||||
out << "f " << (pq.faceI0[k] + pq.vertBase) << " "
|
||||
<< (pq.faceI1[k] + pq.vertBase) << " "
|
||||
<< (pq.faceI2[k] + pq.vertBase) << "\n";
|
||||
totalFaces++;
|
||||
}
|
||||
}
|
||||
out.close();
|
||||
std::printf("Baked %s -> %s\n", projectDir.c_str(), outPath.c_str());
|
||||
std::printf(" %d zone(s), %d tiles, %d verts, %llu tris\n",
|
||||
totalZones, totalTiles, totalVerts,
|
||||
static_cast<unsigned long long>(totalFaces));
|
||||
return 0;
|
||||
} else if (std::strcmp(argv[i], "--export-wob-obj") == 0 && i + 1 < argc) {
|
||||
// WOB is the WMO replacement; like --export-obj for WOM, this
|
||||
// bridges WOB into the universal-3D-tool ecosystem. Each WOB
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue