mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-06 00:53:52 +00:00
feat: client-side WOC collision loading + walkability queries
- TerrainManager loads WOC collision meshes alongside WOT/WHM terrain from both custom_zones/ and output/ directories - CollisionData stored per-tile with triangle array + bounds - isPositionWalkable(x, y): returns whether a world position is on walkable terrain (barycentric point-in-triangle test) - getCollisionFlags(x, y): returns per-triangle flags (walkable, water, steep, indoor) for movement system integration - Defaults to walkable when no collision data is loaded (backward compat) - Custom zone players now have proper terrain physics boundaries
This commit is contained in:
parent
a25c4cfe1f
commit
cff8d359e4
2 changed files with 72 additions and 0 deletions
|
|
@ -260,6 +260,10 @@ public:
|
|||
*/
|
||||
std::optional<float> getHeightAt(float glX, float glY) const;
|
||||
|
||||
// Collision queries using WOC data (custom zones)
|
||||
bool isPositionWalkable(float glX, float glY) const;
|
||||
uint8_t getCollisionFlags(float glX, float glY) const;
|
||||
|
||||
/**
|
||||
* Get dominant terrain texture name at a GL position.
|
||||
* Returns empty if terrain is not loaded at that position.
|
||||
|
|
@ -356,6 +360,16 @@ private:
|
|||
bool taxiStreamingMode_ = false;
|
||||
bool isCustomZone_ = false;
|
||||
|
||||
// Collision data for custom zones (loaded from WOC files)
|
||||
struct CollisionData {
|
||||
struct Triangle { glm::vec3 v0, v1, v2; uint8_t flags; };
|
||||
std::vector<Triangle> triangles;
|
||||
glm::vec3 boundsMin{1e30f}, boundsMax{-1e30f};
|
||||
bool loaded = false;
|
||||
};
|
||||
std::unordered_map<uint64_t, CollisionData> collisionTiles_;
|
||||
uint64_t tileKey(int x, int y) const { return (static_cast<uint64_t>(x) << 32) | static_cast<uint32_t>(y); }
|
||||
|
||||
// Tile size constants (WoW ADT specifications)
|
||||
// A tile (ADT) = 16x16 chunks = 533.33 units across
|
||||
// A chunk = 8x8 vertex quads = 33.33 units across
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "pipeline/wowee_terrain_loader.hpp"
|
||||
#include "pipeline/wowee_model.hpp"
|
||||
#include "pipeline/wowee_building.hpp"
|
||||
#include "pipeline/wowee_collision.hpp"
|
||||
#include "core/memory_monitor.hpp"
|
||||
#include "core/profiler.hpp"
|
||||
#include "pipeline/asset_manager.hpp"
|
||||
|
|
@ -323,6 +324,21 @@ std::shared_ptr<PendingTile> TerrainManager::prepareTile(int x, int y) {
|
|||
if (pipeline::WoweeTerrainLoader::load(wotBase, *terrainPtr)) {
|
||||
loadedFromWot = true;
|
||||
LOG_INFO("Loaded custom zone terrain: ", wotBase);
|
||||
// Load collision mesh if available
|
||||
if (pipeline::WoweeCollisionBuilder::exists(wotBase)) {
|
||||
auto woc = pipeline::WoweeCollisionBuilder::load(wotBase + ".woc");
|
||||
if (woc.isValid()) {
|
||||
CollisionData cd;
|
||||
cd.triangles.reserve(woc.triangles.size());
|
||||
for (const auto& t : woc.triangles)
|
||||
cd.triangles.push_back({t.v0, t.v1, t.v2, t.flags});
|
||||
cd.boundsMin = woc.bounds.min;
|
||||
cd.boundsMax = woc.bounds.max;
|
||||
cd.loaded = true;
|
||||
collisionTiles_[tileKey(coord.x, coord.y)] = std::move(cd);
|
||||
LOG_INFO("Loaded WOC collision: ", woc.triangles.size(), " triangles");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -334,6 +350,20 @@ std::shared_ptr<PendingTile> TerrainManager::prepareTile(int x, int y) {
|
|||
if (pipeline::WoweeTerrainLoader::load(outputBase, *terrainPtr)) {
|
||||
loadedFromWot = true;
|
||||
LOG_INFO("Loaded editor output terrain: ", outputBase);
|
||||
if (pipeline::WoweeCollisionBuilder::exists(outputBase)) {
|
||||
auto woc = pipeline::WoweeCollisionBuilder::load(outputBase + ".woc");
|
||||
if (woc.isValid()) {
|
||||
CollisionData cd;
|
||||
cd.triangles.reserve(woc.triangles.size());
|
||||
for (const auto& t : woc.triangles)
|
||||
cd.triangles.push_back({t.v0, t.v1, t.v2, t.flags});
|
||||
cd.boundsMin = woc.bounds.min;
|
||||
cd.boundsMax = woc.bounds.max;
|
||||
cd.loaded = true;
|
||||
collisionTiles_[tileKey(coord.x, coord.y)] = std::move(cd);
|
||||
LOG_INFO("Loaded WOC collision: ", woc.triangles.size(), " triangles");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2532,5 +2562,33 @@ void TerrainManager::precacheTiles(const std::vector<std::pair<int, int>>& tiles
|
|||
queueCV.notify_all();
|
||||
}
|
||||
|
||||
uint8_t TerrainManager::getCollisionFlags(float glX, float glY) const {
|
||||
for (const auto& [key, cd] : collisionTiles_) {
|
||||
if (!cd.loaded) continue;
|
||||
if (glX < cd.boundsMin.x || glX > cd.boundsMax.x ||
|
||||
glY < cd.boundsMin.y || glY > cd.boundsMax.y) continue;
|
||||
|
||||
for (const auto& tri : cd.triangles) {
|
||||
// Barycentric point-in-triangle test (XY plane)
|
||||
glm::vec2 p(glX, glY);
|
||||
glm::vec2 a(tri.v0.x, tri.v0.y), b(tri.v1.x, tri.v1.y), c(tri.v2.x, tri.v2.y);
|
||||
glm::vec2 v0 = c - a, v1 = b - a, v2 = p - a;
|
||||
float d00 = glm::dot(v0, v0), d01 = glm::dot(v0, v1), d02 = glm::dot(v0, v2);
|
||||
float d11 = glm::dot(v1, v1), d12 = glm::dot(v1, v2);
|
||||
float inv = d00 * d11 - d01 * d01;
|
||||
if (std::abs(inv) < 1e-10f) continue;
|
||||
float u = (d11 * d02 - d01 * d12) / inv;
|
||||
float v = (d00 * d12 - d01 * d02) / inv;
|
||||
if (u >= 0 && v >= 0 && u + v <= 1)
|
||||
return tri.flags;
|
||||
}
|
||||
}
|
||||
return 0x01; // default walkable if no collision data
|
||||
}
|
||||
|
||||
bool TerrainManager::isPositionWalkable(float glX, float glY) const {
|
||||
return (getCollisionFlags(glX, glY) & 0x01) != 0;
|
||||
}
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue