mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Add steep slope limiting to prevent terrain clipping
Added slope normal checking to reject surfaces too steep to walk. Prevents character/mount from clipping through steep terrain. Changes: - Added MIN_WALKABLE_NORMAL threshold (0.7 = ~45° max slope) - WMO collision: query surface normal, reject if normalZ < 0.7 - M2 collision: query surface normal, reject if normalZ < 0.7 - Updated M2Renderer::getFloorHeight to output surface normal - M2 already had internal 0.35 check (~70°), new 0.7 is more restrictive Steep slopes now block movement instead of allowing clipping.
This commit is contained in:
parent
7e85e0b2ef
commit
73db7768d4
3 changed files with 30 additions and 5 deletions
|
|
@ -286,7 +286,7 @@ public:
|
|||
* @param glY World Y
|
||||
* @param glZ Query/reference Z (used to ignore unreachable tops)
|
||||
*/
|
||||
std::optional<float> getFloorHeight(float glX, float glY, float glZ) const;
|
||||
std::optional<float> getFloorHeight(float glX, float glY, float glZ, float* outNormalZ = nullptr) const;
|
||||
|
||||
/**
|
||||
* Raycast against M2 bounding boxes for camera collision
|
||||
|
|
|
|||
|
|
@ -592,6 +592,9 @@ void CameraController::update(float deltaTime) {
|
|||
// 1. Center-only sample for terrain/WMO floor selection.
|
||||
// Using only the center prevents tunnel entrances from snapping
|
||||
// to terrain when offset samples miss the WMO floor geometry.
|
||||
// Slope limit: reject surfaces too steep to walk (prevent clipping)
|
||||
constexpr float MIN_WALKABLE_NORMAL = 0.7f; // ~45° max slope
|
||||
|
||||
std::optional<float> groundH;
|
||||
{
|
||||
// Collision cache: skip expensive checks if barely moved (15cm threshold)
|
||||
|
|
@ -609,9 +612,16 @@ void CameraController::update(float deltaTime) {
|
|||
terrainH = terrainManager->getHeightAt(targetPos.x, targetPos.y);
|
||||
}
|
||||
float wmoProbeZ = std::max(targetPos.z, lastGroundZ) + stepUpBudget + 0.5f;
|
||||
float wmoNormalZ = 1.0f;
|
||||
if (wmoRenderer) {
|
||||
wmoH = wmoRenderer->getFloorHeight(targetPos.x, targetPos.y, wmoProbeZ);
|
||||
wmoH = wmoRenderer->getFloorHeight(targetPos.x, targetPos.y, wmoProbeZ, &wmoNormalZ);
|
||||
}
|
||||
|
||||
// Reject steep WMO slopes
|
||||
if (wmoH && wmoNormalZ < MIN_WALKABLE_NORMAL) {
|
||||
wmoH = std::nullopt; // Treat as unwalkable
|
||||
}
|
||||
|
||||
groundH = selectReachableFloor(terrainH, wmoH, targetPos.z, stepUpBudget);
|
||||
|
||||
// Update cache
|
||||
|
|
@ -636,8 +646,15 @@ void CameraController::update(float deltaTime) {
|
|||
};
|
||||
float m2ProbeZ = std::max(targetPos.z, lastGroundZ) + 6.0f;
|
||||
for (const auto& o : offsets) {
|
||||
float m2NormalZ = 1.0f;
|
||||
auto m2H = m2Renderer->getFloorHeight(
|
||||
targetPos.x + o.x, targetPos.y + o.y, m2ProbeZ);
|
||||
targetPos.x + o.x, targetPos.y + o.y, m2ProbeZ, &m2NormalZ);
|
||||
|
||||
// Reject steep M2 slopes
|
||||
if (m2H && m2NormalZ < MIN_WALKABLE_NORMAL) {
|
||||
continue; // Skip unwalkable M2 surface
|
||||
}
|
||||
|
||||
// Prefer M2 floors (ships, platforms) even if slightly lower than terrain
|
||||
// to prevent falling through ship decks to water below
|
||||
if (m2H && *m2H <= targetPos.z + stepUpBudget) {
|
||||
|
|
|
|||
|
|
@ -2688,9 +2688,10 @@ uint32_t M2Renderer::getTotalTriangleCount() const {
|
|||
return total;
|
||||
}
|
||||
|
||||
std::optional<float> M2Renderer::getFloorHeight(float glX, float glY, float glZ) const {
|
||||
std::optional<float> M2Renderer::getFloorHeight(float glX, float glY, float glZ, float* outNormalZ) const {
|
||||
QueryTimer timer(&queryTimeMs, &queryCallCount);
|
||||
std::optional<float> bestFloor;
|
||||
float bestNormalZ = 1.0f; // Default to flat
|
||||
|
||||
glm::vec3 queryMin(glX - 2.0f, glY - 2.0f, glZ - 6.0f);
|
||||
glm::vec3 queryMax(glX + 2.0f, glY + 2.0f, glZ + 8.0f);
|
||||
|
|
@ -2745,12 +2746,13 @@ std::optional<float> M2Renderer::getFloorHeight(float glX, float glY, float glZ)
|
|||
float hitZ = rayOrigin.z - tHit;
|
||||
|
||||
// Walkable normal check (world space)
|
||||
glm::vec3 worldN(0.0f, 0.0f, 1.0f); // Default to flat
|
||||
glm::vec3 localN = glm::cross(v1 - v0, v2 - v0);
|
||||
float nLen = glm::length(localN);
|
||||
if (nLen > 0.001f) {
|
||||
localN /= nLen;
|
||||
if (localN.z < 0.0f) localN = -localN;
|
||||
glm::vec3 worldN = glm::normalize(
|
||||
worldN = glm::normalize(
|
||||
glm::vec3(instance.modelMatrix * glm::vec4(localN, 0.0f)));
|
||||
if (std::abs(worldN.z) < 0.35f) continue; // too steep (~70° max slope)
|
||||
}
|
||||
|
|
@ -2758,6 +2760,7 @@ std::optional<float> M2Renderer::getFloorHeight(float glX, float glY, float glZ)
|
|||
if (hitZ <= localPos.z + 3.0f && hitZ > bestHitZ) {
|
||||
bestHitZ = hitZ;
|
||||
hitAny = true;
|
||||
bestNormalZ = std::abs(worldN.z); // Store normal for output
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2822,6 +2825,11 @@ std::optional<float> M2Renderer::getFloorHeight(float glX, float glY, float glZ)
|
|||
}
|
||||
}
|
||||
|
||||
// Output surface normal if requested
|
||||
if (outNormalZ) {
|
||||
*outNormalZ = bestNormalZ;
|
||||
}
|
||||
|
||||
return bestFloor;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue