From 17ea24b26dfcda03ea7204ed044bf1769343dcf6 Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Thu, 22 Jan 2026 20:13:58 +0100 Subject: [PATCH 1/2] Improve UV calculation and fallback handling in ProcessLineAgainstMesh --- Client/game_sa/CWorldSA.cpp | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/Client/game_sa/CWorldSA.cpp b/Client/game_sa/CWorldSA.cpp index b67adf8b70..423bc5d02a 100644 --- a/Client/game_sa/CWorldSA.cpp +++ b/Client/game_sa/CWorldSA.cpp @@ -402,22 +402,39 @@ auto CWorldSA::ProcessLineAgainstMesh(CEntitySAInterface* targetEntity, CVector // Now, calculate texture UV, etc based on the hit [if we've hit anything at all] // Since we have the barycentric coords of the hit, calculating it is easy ret.uv = {}; - for (int i = 0; i < 3; i++) - { - // UV set index - Usually models only use level 0 indices, so let's stick with that - const int uvSetIdx = 0; - // Vertex's UV position - RwTextureCoordinates* const vtxUV = &c.hitGeo->texcoords[uvSetIdx][c.hitTri->verts[i]]; + // Index of the UV set to use + const int uvSetIdx = 0; + + // Check if texcoords exist (some models only have colored mesh without textures) + if (c.hitGeo->texcoords[uvSetIdx]) + { + for (int i = 0; i < 3; i++) + { + // Vertex's UV position + RwTextureCoordinates* const vtxUV = &c.hitGeo->texcoords[uvSetIdx][c.hitTri->verts[i]]; - // Now, just interpolate - ret.uv += CVector2D{vtxUV->u, vtxUV->v} * c.hitBary[i]; + // Now, just interpolate + ret.uv += CVector2D{vtxUV->u, vtxUV->v} * c.hitBary[i]; + } + } + else + { + // No texture coords available, use barycentric coords as UV fallback + ret.uv = CVector2D{c.hitBary.fX, c.hitBary.fY}; } // Find out material texture name // For some reason this is sometimes null - RwTexture* const tex = c.hitGeo->materials.materials[c.hitTri->materialId]->texture; - ret.textureName = tex ? tex->name : nullptr; + if (c.hitGeo->materials.materials && c.hitGeo->materials.materials[c.hitTri->materialId]) + { + RwTexture* const tex = c.hitGeo->materials.materials[c.hitTri->materialId]->texture; + ret.textureName = tex ? tex->name : "unknown"; + } + else + { + ret.textureName = "unknown"; + } RwFrame* const hitFrame = RpAtomicGetFrame(c.hitAtomic); ret.frameName = hitFrame ? hitFrame->szName : nullptr; From 6f73768738f18465db529e563abb4c2dea6bae4e Mon Sep 17 00:00:00 2001 From: Xenius97 Date: Thu, 22 Jan 2026 20:23:25 +0100 Subject: [PATCH 2/2] Return nullptr instead of "unknown" --- Client/game_sa/CWorldSA.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Client/game_sa/CWorldSA.cpp b/Client/game_sa/CWorldSA.cpp index 423bc5d02a..ae2cf2ea93 100644 --- a/Client/game_sa/CWorldSA.cpp +++ b/Client/game_sa/CWorldSA.cpp @@ -429,11 +429,11 @@ auto CWorldSA::ProcessLineAgainstMesh(CEntitySAInterface* targetEntity, CVector if (c.hitGeo->materials.materials && c.hitGeo->materials.materials[c.hitTri->materialId]) { RwTexture* const tex = c.hitGeo->materials.materials[c.hitTri->materialId]->texture; - ret.textureName = tex ? tex->name : "unknown"; + ret.textureName = tex ? tex->name : nullptr; } else { - ret.textureName = "unknown"; + ret.textureName = nullptr; } RwFrame* const hitFrame = RpAtomicGetFrame(c.hitAtomic);