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

@ -223,6 +223,32 @@ std::vector<int> TerrainEditor::getAffectedChunks(const glm::vec3& center, float
return result;
}
glm::vec3 TerrainEditor::sampleTerrainNormal(const glm::vec3& worldPos) const {
if (!terrain_) return glm::vec3(0, 0, 1);
auto sampleH = [&](float x, float y) -> float {
rendering::Ray ray;
ray.origin = glm::vec3(x, y, 10000.0f);
ray.direction = glm::vec3(0, 0, -1);
glm::vec3 hit;
if (const_cast<TerrainEditor*>(this)->raycastTerrain(ray, hit))
return hit.z;
return worldPos.z;
};
float step = 2.0f;
float hL = sampleH(worldPos.x - step, worldPos.y);
float hR = sampleH(worldPos.x + step, worldPos.y);
float hD = sampleH(worldPos.x, worldPos.y - step);
float hU = sampleH(worldPos.x, worldPos.y + step);
glm::vec3 dx(2.0f * step, 0, hR - hL);
glm::vec3 dy(0, 2.0f * step, hU - hD);
glm::vec3 n = glm::normalize(glm::cross(dx, dy));
if (n.z < 0) n = -n;
return n;
}
void TerrainEditor::beginStroke() {
if (!terrain_ || strokeActive_) return;
strokeActive_ = true;