mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Overhaul WMO collision: precompute normals, fix floor selection, optimize queries
- Precompute triangle normals in buildCollisionGrid, eliminating per-query cross+normalize in getFloorHeight, checkWallCollision, and raycastBoundingBoxes - Fix floor selection: remove redundant allowAbove (callers already elevate probeZ by stepUpBudget), preventing upper-story snap at doorway transitions - Align wall classification threshold (absNz < 0.35) with runtime skip check, eliminating ~30% wasted wall triangle fetches - Replace O(n log n) sort+unique dedup in range queries with O(n) visited bitset - Rename wallTriScratch to triScratch_, fix stale threshold comments
This commit is contained in:
parent
35384b2c52
commit
64879b8aab
2 changed files with 117 additions and 74 deletions
|
|
@ -390,13 +390,19 @@ private:
|
||||||
std::vector<std::vector<uint32_t>> cellTriangles;
|
std::vector<std::vector<uint32_t>> cellTriangles;
|
||||||
|
|
||||||
// Pre-classified triangle lists per cell (built at load time)
|
// Pre-classified triangle lists per cell (built at load time)
|
||||||
std::vector<std::vector<uint32_t>> cellFloorTriangles; // abs(normal.z) >= 0.45
|
std::vector<std::vector<uint32_t>> cellFloorTriangles; // abs(normal.z) >= 0.35
|
||||||
std::vector<std::vector<uint32_t>> cellWallTriangles; // abs(normal.z) < 0.55
|
std::vector<std::vector<uint32_t>> cellWallTriangles; // abs(normal.z) < 0.35
|
||||||
|
|
||||||
// Pre-computed per-triangle Z bounds for fast vertical reject
|
// Pre-computed per-triangle Z bounds for fast vertical reject
|
||||||
struct TriBounds { float minZ; float maxZ; };
|
struct TriBounds { float minZ; float maxZ; };
|
||||||
std::vector<TriBounds> triBounds; // indexed by triStart/3
|
std::vector<TriBounds> triBounds; // indexed by triStart/3
|
||||||
|
|
||||||
|
// Pre-computed per-triangle normals (unit length, indexed by triStart/3)
|
||||||
|
std::vector<glm::vec3> triNormals;
|
||||||
|
|
||||||
|
// Scratch bitset for deduplicating triangle queries (sized to numTriangles)
|
||||||
|
mutable std::vector<uint8_t> triVisited;
|
||||||
|
|
||||||
// Build the spatial grid from collision geometry
|
// Build the spatial grid from collision geometry
|
||||||
void buildCollisionGrid();
|
void buildCollisionGrid();
|
||||||
|
|
||||||
|
|
@ -675,7 +681,7 @@ private:
|
||||||
std::unordered_map<GridCell, std::vector<uint32_t>, GridCellHash> spatialGrid;
|
std::unordered_map<GridCell, std::vector<uint32_t>, GridCellHash> spatialGrid;
|
||||||
std::unordered_map<uint32_t, size_t> instanceIndexById;
|
std::unordered_map<uint32_t, size_t> instanceIndexById;
|
||||||
mutable std::vector<size_t> candidateScratch;
|
mutable std::vector<size_t> candidateScratch;
|
||||||
mutable std::vector<uint32_t> wallTriScratch; // Scratch for wall collision grid queries
|
mutable std::vector<uint32_t> triScratch_; // Scratch for collision grid queries
|
||||||
mutable std::unordered_set<uint32_t> candidateIdScratch;
|
mutable std::unordered_set<uint32_t> candidateIdScratch;
|
||||||
|
|
||||||
// Parallel visibility culling
|
// Parallel visibility culling
|
||||||
|
|
|
||||||
|
|
@ -1263,6 +1263,8 @@ void WMORenderer::gatherCandidates(const glm::vec3& queryMin, const glm::vec3& q
|
||||||
}
|
}
|
||||||
|
|
||||||
void WMORenderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const Camera& camera) {
|
void WMORenderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const Camera& camera) {
|
||||||
|
++currentFrameId;
|
||||||
|
|
||||||
if (!opaquePipeline_ || instances.empty()) {
|
if (!opaquePipeline_ || instances.empty()) {
|
||||||
lastDrawCalls = 0;
|
lastDrawCalls = 0;
|
||||||
return;
|
return;
|
||||||
|
|
@ -2474,6 +2476,8 @@ void WMORenderer::GroupResources::buildCollisionGrid() {
|
||||||
|
|
||||||
size_t numTriangles = collisionIndices.size() / 3;
|
size_t numTriangles = collisionIndices.size() / 3;
|
||||||
triBounds.resize(numTriangles);
|
triBounds.resize(numTriangles);
|
||||||
|
triNormals.resize(numTriangles);
|
||||||
|
triVisited.resize(numTriangles, 0);
|
||||||
|
|
||||||
float invCellW = gridCellsX / std::max(0.01f, extentX);
|
float invCellW = gridCellsX / std::max(0.01f, extentX);
|
||||||
float invCellH = gridCellsY / std::max(0.01f, extentY);
|
float invCellH = gridCellsY / std::max(0.01f, extentY);
|
||||||
|
|
@ -2494,16 +2498,23 @@ void WMORenderer::GroupResources::buildCollisionGrid() {
|
||||||
float triMaxZ = std::max({v0.z, v1.z, v2.z});
|
float triMaxZ = std::max({v0.z, v1.z, v2.z});
|
||||||
triBounds[i / 3] = { triMinZ, triMaxZ };
|
triBounds[i / 3] = { triMinZ, triMaxZ };
|
||||||
|
|
||||||
// Classify floor vs wall by normal.
|
// Precompute and store unit normal
|
||||||
// Wall threshold matches MAX_WALK_SLOPE_DOT (cos 50° ≈ 0.6428) so that
|
|
||||||
// surfaces too steep to walk on are always tested for wall collision.
|
|
||||||
glm::vec3 edge1 = v1 - v0;
|
glm::vec3 edge1 = v1 - v0;
|
||||||
glm::vec3 edge2 = v2 - v0;
|
glm::vec3 edge2 = v2 - v0;
|
||||||
glm::vec3 normal = glm::cross(edge1, edge2);
|
glm::vec3 normal = glm::cross(edge1, edge2);
|
||||||
float normalLen = glm::length(normal);
|
float normalLen = glm::length(normal);
|
||||||
float absNz = (normalLen > 0.001f) ? std::abs(normal.z / normalLen) : 0.0f;
|
if (normalLen > 0.001f) {
|
||||||
|
normal /= normalLen;
|
||||||
|
} else {
|
||||||
|
normal = glm::vec3(0.0f, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
triNormals[i / 3] = normal;
|
||||||
|
|
||||||
|
// Classify floor vs wall by normal.
|
||||||
|
// Wall threshold matches the runtime skip in checkWallCollision (absNz >= 0.35).
|
||||||
|
float absNz = std::abs(normal.z);
|
||||||
bool isFloor = (absNz >= 0.35f); // ~70° max slope (relaxed for steep stairs)
|
bool isFloor = (absNz >= 0.35f); // ~70° max slope (relaxed for steep stairs)
|
||||||
bool isWall = (absNz < 0.65f); // Matches walkable slope threshold
|
bool isWall = (absNz < 0.35f); // Matches checkWallCollision skip threshold
|
||||||
|
|
||||||
int cellMinX = std::max(0, static_cast<int>((triMinX - gridOrigin.x) * invCellW));
|
int cellMinX = std::max(0, static_cast<int>((triMinX - gridOrigin.x) * invCellW));
|
||||||
int cellMinY = std::max(0, static_cast<int>((triMinY - gridOrigin.y) * invCellH));
|
int cellMinY = std::max(0, static_cast<int>((triMinY - gridOrigin.y) * invCellH));
|
||||||
|
|
@ -2556,18 +2567,30 @@ void WMORenderer::GroupResources::getTrianglesInRange(
|
||||||
|
|
||||||
if (cellMinX > cellMaxX || cellMinY > cellMaxY) return;
|
if (cellMinX > cellMaxX || cellMinY > cellMaxY) return;
|
||||||
|
|
||||||
// Collect unique triangle indices from all overlapping cells
|
// Collect unique triangle indices using visited bitset (O(n) dedup)
|
||||||
for (int cy = cellMinY; cy <= cellMaxY; ++cy) {
|
bool multiCell = (cellMinX != cellMaxX || cellMinY != cellMaxY);
|
||||||
for (int cx = cellMinX; cx <= cellMaxX; ++cx) {
|
if (multiCell && !triVisited.empty()) {
|
||||||
const auto& cell = cellTriangles[cy * gridCellsX + cx];
|
for (int cy = cellMinY; cy <= cellMaxY; ++cy) {
|
||||||
out.insert(out.end(), cell.begin(), cell.end());
|
for (int cx = cellMinX; cx <= cellMaxX; ++cx) {
|
||||||
|
const auto& cell = cellTriangles[cy * gridCellsX + cx];
|
||||||
|
for (uint32_t tri : cell) {
|
||||||
|
uint32_t idx = tri / 3;
|
||||||
|
if (!triVisited[idx]) {
|
||||||
|
triVisited[idx] = 1;
|
||||||
|
out.push_back(tri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Clear visited bits
|
||||||
|
for (uint32_t tri : out) triVisited[tri / 3] = 0;
|
||||||
|
} else {
|
||||||
|
for (int cy = cellMinY; cy <= cellMaxY; ++cy) {
|
||||||
|
for (int cx = cellMinX; cx <= cellMaxX; ++cx) {
|
||||||
|
const auto& cell = cellTriangles[cy * gridCellsX + cx];
|
||||||
|
out.insert(out.end(), cell.begin(), cell.end());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Remove duplicates (triangles spanning multiple cells)
|
|
||||||
if (cellMinX != cellMaxX || cellMinY != cellMaxY) {
|
|
||||||
std::sort(out.begin(), out.end());
|
|
||||||
out.erase(std::unique(out.begin(), out.end()), out.end());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2589,16 +2612,28 @@ void WMORenderer::GroupResources::getFloorTrianglesInRange(
|
||||||
|
|
||||||
if (cellMinX > cellMaxX || cellMinY > cellMaxY) return;
|
if (cellMinX > cellMaxX || cellMinY > cellMaxY) return;
|
||||||
|
|
||||||
for (int cy = cellMinY; cy <= cellMaxY; ++cy) {
|
bool multiCell = (cellMinX != cellMaxX || cellMinY != cellMaxY);
|
||||||
for (int cx = cellMinX; cx <= cellMaxX; ++cx) {
|
if (multiCell && !triVisited.empty()) {
|
||||||
const auto& cell = cellFloorTriangles[cy * gridCellsX + cx];
|
for (int cy = cellMinY; cy <= cellMaxY; ++cy) {
|
||||||
out.insert(out.end(), cell.begin(), cell.end());
|
for (int cx = cellMinX; cx <= cellMaxX; ++cx) {
|
||||||
|
const auto& cell = cellFloorTriangles[cy * gridCellsX + cx];
|
||||||
|
for (uint32_t tri : cell) {
|
||||||
|
uint32_t idx = tri / 3;
|
||||||
|
if (!triVisited[idx]) {
|
||||||
|
triVisited[idx] = 1;
|
||||||
|
out.push_back(tri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint32_t tri : out) triVisited[tri / 3] = 0;
|
||||||
|
} else {
|
||||||
|
for (int cy = cellMinY; cy <= cellMaxY; ++cy) {
|
||||||
|
for (int cx = cellMinX; cx <= cellMaxX; ++cx) {
|
||||||
|
const auto& cell = cellFloorTriangles[cy * gridCellsX + cx];
|
||||||
|
out.insert(out.end(), cell.begin(), cell.end());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (cellMinX != cellMaxX || cellMinY != cellMaxY) {
|
|
||||||
std::sort(out.begin(), out.end());
|
|
||||||
out.erase(std::unique(out.begin(), out.end()), out.end());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2620,22 +2655,35 @@ void WMORenderer::GroupResources::getWallTrianglesInRange(
|
||||||
|
|
||||||
if (cellMinX > cellMaxX || cellMinY > cellMaxY) return;
|
if (cellMinX > cellMaxX || cellMinY > cellMaxY) return;
|
||||||
|
|
||||||
for (int cy = cellMinY; cy <= cellMaxY; ++cy) {
|
bool multiCell = (cellMinX != cellMaxX || cellMinY != cellMaxY);
|
||||||
for (int cx = cellMinX; cx <= cellMaxX; ++cx) {
|
if (multiCell && !triVisited.empty()) {
|
||||||
const auto& cell = cellWallTriangles[cy * gridCellsX + cx];
|
for (int cy = cellMinY; cy <= cellMaxY; ++cy) {
|
||||||
out.insert(out.end(), cell.begin(), cell.end());
|
for (int cx = cellMinX; cx <= cellMaxX; ++cx) {
|
||||||
|
const auto& cell = cellWallTriangles[cy * gridCellsX + cx];
|
||||||
|
for (uint32_t tri : cell) {
|
||||||
|
uint32_t idx = tri / 3;
|
||||||
|
if (!triVisited[idx]) {
|
||||||
|
triVisited[idx] = 1;
|
||||||
|
out.push_back(tri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint32_t tri : out) triVisited[tri / 3] = 0;
|
||||||
|
} else {
|
||||||
|
for (int cy = cellMinY; cy <= cellMaxY; ++cy) {
|
||||||
|
for (int cx = cellMinX; cx <= cellMaxX; ++cx) {
|
||||||
|
const auto& cell = cellWallTriangles[cy * gridCellsX + cx];
|
||||||
|
out.insert(out.end(), cell.begin(), cell.end());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (cellMinX != cellMaxX || cellMinY != cellMaxY) {
|
|
||||||
std::sort(out.begin(), out.end());
|
|
||||||
out.erase(std::unique(out.begin(), out.end()), out.end());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<float> WMORenderer::getFloorHeight(float glX, float glY, float glZ, float* outNormalZ) const {
|
std::optional<float> WMORenderer::getFloorHeight(float glX, float glY, float glZ, float* outNormalZ) const {
|
||||||
// All floor caching disabled - even per-frame cache can return stale results
|
// Per-frame cache disabled: camera and player query the same (x,y) at
|
||||||
// when player Z changes between queries, causing fall-through at stairs.
|
// different Z within a single frame. The allowAbove filter depends on glZ,
|
||||||
|
// so caching by (x,y) alone returns wrong floors across Z contexts.
|
||||||
|
|
||||||
QueryTimer timer(&queryTimeMs, &queryCallCount);
|
QueryTimer timer(&queryTimeMs, &queryCallCount);
|
||||||
std::optional<float> bestFloor;
|
std::optional<float> bestFloor;
|
||||||
|
|
@ -2660,9 +2708,9 @@ std::optional<float> WMORenderer::getFloorHeight(float glX, float glY, float glZ
|
||||||
group.getTrianglesInRange(
|
group.getTrianglesInRange(
|
||||||
localOrigin.x - 1.0f, localOrigin.y - 1.0f,
|
localOrigin.x - 1.0f, localOrigin.y - 1.0f,
|
||||||
localOrigin.x + 1.0f, localOrigin.y + 1.0f,
|
localOrigin.x + 1.0f, localOrigin.y + 1.0f,
|
||||||
wallTriScratch);
|
triScratch_);
|
||||||
|
|
||||||
for (uint32_t triStart : wallTriScratch) {
|
for (uint32_t triStart : triScratch_) {
|
||||||
const glm::vec3& v0 = verts[indices[triStart]];
|
const glm::vec3& v0 = verts[indices[triStart]];
|
||||||
const glm::vec3& v1 = verts[indices[triStart + 1]];
|
const glm::vec3& v1 = verts[indices[triStart + 1]];
|
||||||
const glm::vec3& v2 = verts[indices[triStart + 2]];
|
const glm::vec3& v2 = verts[indices[triStart + 2]];
|
||||||
|
|
@ -2676,23 +2724,20 @@ std::optional<float> WMORenderer::getFloorHeight(float glX, float glY, float glZ
|
||||||
glm::vec3 hitLocal = localOrigin + localDir * t;
|
glm::vec3 hitLocal = localOrigin + localDir * t;
|
||||||
glm::vec3 hitWorld = glm::vec3(instance.modelMatrix * glm::vec4(hitLocal, 1.0f));
|
glm::vec3 hitWorld = glm::vec3(instance.modelMatrix * glm::vec4(hitLocal, 1.0f));
|
||||||
|
|
||||||
float allowAbove = model.isLowPlatform ? 12.0f : 2.0f;
|
// Accept floors at or below glZ (the caller already elevates
|
||||||
if (hitWorld.z <= glZ + allowAbove) {
|
// glZ by stepUpBudget to handle step-up range). Among those,
|
||||||
|
// pick the highest (closest to feet).
|
||||||
|
if (hitWorld.z <= glZ) {
|
||||||
if (!bestFloor || hitWorld.z > *bestFloor) {
|
if (!bestFloor || hitWorld.z > *bestFloor) {
|
||||||
bestFloor = hitWorld.z;
|
bestFloor = hitWorld.z;
|
||||||
bestFromLowPlatform = model.isLowPlatform;
|
bestFromLowPlatform = model.isLowPlatform;
|
||||||
|
|
||||||
// Compute local normal and transform to world space
|
// Use precomputed normal, ensure upward, transform to world
|
||||||
glm::vec3 localNormal = glm::cross(v1 - v0, v2 - v0);
|
glm::vec3 localNormal = group.triNormals[triStart / 3];
|
||||||
float len = glm::length(localNormal);
|
if (localNormal.z < 0.0f) localNormal = -localNormal;
|
||||||
if (len > 0.001f) {
|
glm::vec3 worldNormal = glm::normalize(
|
||||||
localNormal /= len;
|
glm::vec3(instance.modelMatrix * glm::vec4(localNormal, 0.0f)));
|
||||||
// Ensure normal points upward
|
bestNormalZ = std::abs(worldNormal.z);
|
||||||
if (localNormal.z < 0.0f) localNormal = -localNormal;
|
|
||||||
glm::vec3 worldNormal = glm::normalize(
|
|
||||||
glm::vec3(instance.modelMatrix * glm::vec4(localNormal, 0.0f)));
|
|
||||||
bestNormalZ = std::abs(worldNormal.z);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2735,8 +2780,8 @@ std::optional<float> WMORenderer::getFloorHeight(float glX, float glY, float glZ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Full scan: test all instances (active group fast path removed to fix
|
// Full scan: test all instances (active group result above is not
|
||||||
// bridge clipping where early-return missed other WMO instances)
|
// early-returned because overlapping WMO instances need full coverage).
|
||||||
glm::vec3 queryMin(glX - 2.0f, glY - 2.0f, glZ - 8.0f);
|
glm::vec3 queryMin(glX - 2.0f, glY - 2.0f, glZ - 8.0f);
|
||||||
glm::vec3 queryMax(glX + 2.0f, glY + 2.0f, glZ + 10.0f);
|
glm::vec3 queryMax(glX + 2.0f, glY + 2.0f, glZ + 10.0f);
|
||||||
gatherCandidates(queryMin, queryMax, candidateScratch);
|
gatherCandidates(queryMin, queryMax, candidateScratch);
|
||||||
|
|
@ -2898,9 +2943,9 @@ bool WMORenderer::checkWallCollision(const glm::vec3& from, const glm::vec3& to,
|
||||||
float rangeMinY = std::min(localFrom.y, localTo.y) - PLAYER_RADIUS - 1.5f;
|
float rangeMinY = std::min(localFrom.y, localTo.y) - PLAYER_RADIUS - 1.5f;
|
||||||
float rangeMaxX = std::max(localFrom.x, localTo.x) + PLAYER_RADIUS + 1.5f;
|
float rangeMaxX = std::max(localFrom.x, localTo.x) + PLAYER_RADIUS + 1.5f;
|
||||||
float rangeMaxY = std::max(localFrom.y, localTo.y) + PLAYER_RADIUS + 1.5f;
|
float rangeMaxY = std::max(localFrom.y, localTo.y) + PLAYER_RADIUS + 1.5f;
|
||||||
group.getWallTrianglesInRange(rangeMinX, rangeMinY, rangeMaxX, rangeMaxY, wallTriScratch);
|
group.getWallTrianglesInRange(rangeMinX, rangeMinY, rangeMaxX, rangeMaxY, triScratch_);
|
||||||
|
|
||||||
for (uint32_t triStart : wallTriScratch) {
|
for (uint32_t triStart : triScratch_) {
|
||||||
// Use pre-computed Z bounds for fast vertical reject
|
// Use pre-computed Z bounds for fast vertical reject
|
||||||
const auto& tb = group.triBounds[triStart / 3];
|
const auto& tb = group.triBounds[triStart / 3];
|
||||||
|
|
||||||
|
|
@ -2919,13 +2964,9 @@ bool WMORenderer::checkWallCollision(const glm::vec3& from, const glm::vec3& to,
|
||||||
const glm::vec3& v1 = verts[indices[triStart + 1]];
|
const glm::vec3& v1 = verts[indices[triStart + 1]];
|
||||||
const glm::vec3& v2 = verts[indices[triStart + 2]];
|
const glm::vec3& v2 = verts[indices[triStart + 2]];
|
||||||
|
|
||||||
// Triangle normal for swept test and push fallback
|
// Use precomputed normal for swept test and push fallback
|
||||||
glm::vec3 edge1 = v1 - v0;
|
glm::vec3 normal = group.triNormals[triStart / 3];
|
||||||
glm::vec3 edge2 = v2 - v0;
|
if (glm::dot(normal, normal) < 0.5f) continue; // degenerate
|
||||||
glm::vec3 normal = glm::cross(edge1, edge2);
|
|
||||||
float normalLen = glm::length(normal);
|
|
||||||
if (normalLen < 0.001f) continue;
|
|
||||||
normal /= normalLen;
|
|
||||||
|
|
||||||
// Recompute plane distances with current (possibly pushed) localTo
|
// Recompute plane distances with current (possibly pushed) localTo
|
||||||
float fromDist = glm::dot(localFrom - v0, normal);
|
float fromDist = glm::dot(localFrom - v0, normal);
|
||||||
|
|
@ -3268,19 +3309,15 @@ float WMORenderer::raycastBoundingBoxes(const glm::vec3& origin, const glm::vec3
|
||||||
float rMinY = std::min(localOrigin.y, localEnd.y) - 1.0f;
|
float rMinY = std::min(localOrigin.y, localEnd.y) - 1.0f;
|
||||||
float rMaxX = std::max(localOrigin.x, localEnd.x) + 1.0f;
|
float rMaxX = std::max(localOrigin.x, localEnd.x) + 1.0f;
|
||||||
float rMaxY = std::max(localOrigin.y, localEnd.y) + 1.0f;
|
float rMaxY = std::max(localOrigin.y, localEnd.y) + 1.0f;
|
||||||
group.getWallTrianglesInRange(rMinX, rMinY, rMaxX, rMaxY, wallTriScratch);
|
group.getWallTrianglesInRange(rMinX, rMinY, rMaxX, rMaxY, triScratch_);
|
||||||
|
|
||||||
for (uint32_t triStart : wallTriScratch) {
|
for (uint32_t triStart : triScratch_) {
|
||||||
const glm::vec3& v0 = verts[indices[triStart]];
|
const glm::vec3& v0 = verts[indices[triStart]];
|
||||||
const glm::vec3& v1 = verts[indices[triStart + 1]];
|
const glm::vec3& v1 = verts[indices[triStart + 1]];
|
||||||
const glm::vec3& v2 = verts[indices[triStart + 2]];
|
const glm::vec3& v2 = verts[indices[triStart + 2]];
|
||||||
glm::vec3 triNormal = glm::cross(v1 - v0, v2 - v0);
|
glm::vec3 triNormal = group.triNormals[triStart / 3];
|
||||||
float normalLenSq = glm::dot(triNormal, triNormal);
|
if (glm::dot(triNormal, triNormal) < 0.5f) continue; // degenerate
|
||||||
if (normalLenSq < 1e-8f) {
|
// Wall list pre-filters at 0.35; apply stricter camera threshold
|
||||||
continue;
|
|
||||||
}
|
|
||||||
triNormal /= std::sqrt(normalLenSq);
|
|
||||||
// Wall list pre-filters at 0.55; apply stricter camera threshold
|
|
||||||
if (std::abs(triNormal.z) > MAX_WALKABLE_ABS_NORMAL_Z) {
|
if (std::abs(triNormal.z) > MAX_WALKABLE_ABS_NORMAL_Z) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue