From 663d34af0dae9959a603e26b287bf0108e2ac697 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 06:35:05 -0700 Subject: [PATCH] fix(woc): sanitize triangle vertices + bounds at save time In-memory collision can be polluted by addMesh on bad input (e.g. a WMO with NaN vertex positions). Without this, the save would persist that NaN into the WOC and the load-time guards would have to clean it up forever. Now scrubs vertices and bounds at write time, matching the WHM save sanitize. --- src/pipeline/wowee_collision.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/pipeline/wowee_collision.cpp b/src/pipeline/wowee_collision.cpp index 0b278f81..c28aed34 100644 --- a/src/pipeline/wowee_collision.cpp +++ b/src/pipeline/wowee_collision.cpp @@ -162,17 +162,28 @@ bool WoweeCollisionBuilder::save(const WoweeCollision& collision, const std::str if (!f) return false; f.write(reinterpret_cast(&WOC_MAGIC), 4); + // Sanitize bounds before writing — produces a clean WOC even when an + // in-memory collision has been polluted by addMesh on bad input. + auto sanVec = [](glm::vec3 v) { + if (!std::isfinite(v.x)) v.x = 0.0f; + if (!std::isfinite(v.y)) v.y = 0.0f; + if (!std::isfinite(v.z)) v.z = 0.0f; + return v; + }; uint32_t triCount = static_cast(collision.triangles.size()); f.write(reinterpret_cast(&triCount), 4); f.write(reinterpret_cast(&collision.tileX), 4); f.write(reinterpret_cast(&collision.tileY), 4); - f.write(reinterpret_cast(&collision.bounds.min), 12); - f.write(reinterpret_cast(&collision.bounds.max), 12); + glm::vec3 bmin = sanVec(collision.bounds.min); + glm::vec3 bmax = sanVec(collision.bounds.max); + f.write(reinterpret_cast(&bmin), 12); + f.write(reinterpret_cast(&bmax), 12); for (const auto& tri : collision.triangles) { - f.write(reinterpret_cast(&tri.v0), 12); - f.write(reinterpret_cast(&tri.v1), 12); - f.write(reinterpret_cast(&tri.v2), 12); + glm::vec3 v0 = sanVec(tri.v0), v1 = sanVec(tri.v1), v2 = sanVec(tri.v2); + f.write(reinterpret_cast(&v0), 12); + f.write(reinterpret_cast(&v1), 12); + f.write(reinterpret_cast(&v2), 12); f.write(reinterpret_cast(&tri.flags), 1); }