feat(editor): terrain-aligned objects, batch convert, WCP import+load

New features:
- Align to Slope: rotates objects to match terrain surface normal at
  their position (trees on hillsides lean naturally). Works with
  multi-select. Available in object panel and right-click context menu
- Batch Convert Assets: File menu option to recursively convert all
  M2→WOM and WMO→WOB files in a data directory to open format
- Import & Load: one-click WCP unpack + auto-open the imported zone
- sampleTerrainNormal() for slope detection via height differencing
- Zone load error toasts for missing/corrupt files
This commit is contained in:
Kelsi 2026-05-05 14:22:21 -07:00
parent acb519a243
commit 115fe8436f
5 changed files with 136 additions and 0 deletions

View file

@ -273,6 +273,22 @@ void EditorUI::renderMenuBar(EditorApp& app) {
else
app.showToast("Import failed — check path");
}
if (ImGui::MenuItem("Import & Load")) {
editor::ContentPackInfo info;
if (editor::ContentPacker::readInfo(wcpImportPath, info) &&
editor::ContentPacker::unpackZone(wcpImportPath, "custom_zones")) {
app.showToast("Imported: " + info.name);
auto zones = pipeline::CustomZoneDiscovery::scan({"custom_zones"});
for (const auto& z : zones) {
if (z.name == info.name && !z.tiles.empty()) {
app.loadADT(z.name, z.tiles[0].first, z.tiles[0].second);
break;
}
}
} else {
app.showToast("Import failed — check path");
}
}
if (ImGui::MenuItem("Inspect Pack Info")) {
editor::ContentPackInfo info;
if (editor::ContentPacker::readInfo(wcpImportPath, info)) {
@ -346,6 +362,17 @@ void EditorUI::renderMenuBar(EditorApp& app) {
fmt(val.hasObjects, true, "objects", "placed objects");
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Batch Convert Assets")) {
static char batchDir[256] = "Data";
ImGui::InputText("Data Directory", batchDir, sizeof(batchDir));
ImGui::TextColored(ImVec4(0.6f,0.6f,0.6f,1),
"Recursively converts M2->WOM and WMO->WOB");
if (ImGui::MenuItem("Convert All")) {
int n = app.batchConvertAssets(batchDir);
app.showToast("Converted " + std::to_string(n) + " assets to open format");
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Add Adjacent Tile", app.hasTerrainLoaded())) {
if (ImGui::MenuItem("North (+X)")) app.addAdjacentTile(1, 0);
if (ImGui::MenuItem("South (-X)")) app.addAdjacentTile(-1, 0);
@ -1483,6 +1510,8 @@ void EditorUI::renderObjectPanel(EditorApp& app) {
if (ImGui::Button("Snap Ground", ImVec2(75, 0)))
app.snapSelectedToGround();
ImGui::SameLine();
if (ImGui::Button("Align Slope", ImVec2(75, 0)))
app.alignSelectedToTerrain();
if (ImGui::Button("Fly To", ImVec2(55, 0)))
app.flyToSelected();
ImGui::SameLine();
@ -2057,6 +2086,8 @@ void EditorUI::renderContextMenu(EditorApp& app) {
ImGui::Separator();
if (ImGui::MenuItem("Snap to Ground"))
app.snapSelectedToGround();
if (ImGui::MenuItem("Align to Slope"))
app.alignSelectedToTerrain();
if (ImGui::MenuItem("Fly To"))
app.flyToSelected();
}