From aa87f478a2b304f823c2b4198ee4491a519ee256 Mon Sep 17 00:00:00 2001 From: Pirulax Date: Fri, 8 Sep 2023 18:49:34 +0200 Subject: [PATCH 1/4] Add `ProcessLineAgainstMesh` --- Client/game_sa/CWorldSA.cpp | 310 +++++++++--------- Client/game_sa/CWorldSA.h | 1 + .../logic/luadefs/CLuaWorldDefs.cpp | 26 ++ .../deathmatch/logic/luadefs/CLuaWorldDefs.h | 2 + Client/sdk/game/CWorld.h | 1 + .../deathmatch/logic/lua/CLuaMultiReturn.h | 2 +- 6 files changed, 194 insertions(+), 148 deletions(-) diff --git a/Client/game_sa/CWorldSA.cpp b/Client/game_sa/CWorldSA.cpp index 527a8108a1..4dce3c06e4 100644 --- a/Client/game_sa/CWorldSA.cpp +++ b/Client/game_sa/CWorldSA.cpp @@ -254,6 +254,167 @@ void ConvertMatrixToEulerAngles(const CMatrix_Padded& matrixPadded, float& fX, f } } + +auto CWorldSA::ProcessLineAgainstMesh(CEntity* targetEntity, CVector start, CVector end) -> SProcessLineOfSightMaterialInfoResult +{ + assert(targetEntity); + + SProcessLineOfSightMaterialInfoResult ret; + + struct Context + { + float minHitDistSq{}; //< [squared] hit distance from the line segment's origin + CVector originOS, endOS, dirOS; //< Line origin, end and dir [in object space] + CMatrix entMat, entInvMat; //< The hit entity's matrix, and it's inverse + RpTriangle* hitTri{}; //< The triangle hit + RpAtomic* hitAtomic{}; //< The atomic of the hit triangle's geometry + RpGeometry* hitGeo{}; //< The geometry of the hit triangle + CVector hitBary{}; //< Barycentric coordinates [on the hit triangle] of the hit + CVector hitPosOS{}; //< Hit position in object space + CEntitySAInterface* entity{}; //< The hit entity + } c = {}; + + c.entity = targetEntity->GetInterface(); + + if (!c.entity->m_pRwObject) { + return ret; // isValid will be false in this case + } + + // Get matrix, and it's inverse + c.entity->Placeable.matrix->ConvertToMatrix(c.entMat); + c.entInvMat = c.entMat.Inverse(); + + // ...to transform the line origin and end into object space + c.originOS = c.entInvMat.TransformVector(start); + c.endOS = c.entInvMat.TransformVector(end); + c.dirOS = c.endOS - c.originOS; + c.minHitDistSq = c.dirOS.LengthSquared(); // By setting it to this value we avoid collisions that would be detected after the line segment + // [but are still ont the ray] + + // Do raycast against the DFF to get hit position material UV and name + // This is very slow + // Perhaps we could parallelize it somehow? [OpenMP?] + const auto ProcessOneAtomic = [](RpAtomic* a, void* data) + { + const auto c = (Context*)data; + const auto f = RpAtomicGetFrame(a); + + const auto GetFrameCMatrix = [](RwFrame* f) + { + CMatrix out; + pGame->GetRenderWare()->RwMatrixToCMatrix(*RwFrameGetMatrix(f), out); + return out; + }; + + // Atomic not visible + if (!a->renderCallback || !(a->object.object.flags & 0x04 /*rpATOMICRENDER*/)) + { + return true; + } + + // Sometimes atomics have no geometry [I don't think that should be possible, but okay] + const auto geo = a->geometry; + if (!geo) + { + return true; + } + + // Calculate transformation by traversing the hierarchy from the bottom (this frame) -> top (root frame) + CMatrix localToObjTransform{}; + for (auto i = f; i && i != i->root; i = RwFrameGetParent(i)) + { + localToObjTransform = GetFrameCMatrix(i) * localToObjTransform; + } + const auto objToLocalTransform = localToObjTransform.Inverse(); + + const auto ObjectToLocalSpace = [&](const CVector& in) { return objToLocalTransform.TransformVector(in); }; + + // Transform from object space, into local (the frame's) space + const auto localOrigin = ObjectToLocalSpace(c->originOS); + const auto localEnd = ObjectToLocalSpace(c->endOS); + +#if 0 + if (!CCollisionSA::TestLineSphere( + CColLineSA{localOrigin, 0.f, localEnd, 0.f}, + reinterpret_cast(*RpAtomicGetBoundingSphere(a)) // Fine for now + )) { + return true; // Line segment doesn't touch bsp + } +#endif + const auto localDir = localEnd - localOrigin; + + const auto verts = reinterpret_cast(geo->morph_target->verts); // It's fine, trust me bro + for (auto i = geo->triangles_size; i-- > 0;) + { + const auto tri = &geo->triangles[i]; + + // Process the line against the triangle + CVector hitBary, hitPos; + if (!localOrigin.IntersectsSegmentTriangle(localDir, verts[tri->verts[0]], verts[tri->verts[1]], verts[tri->verts[2]], &hitPos, &hitBary)) + { + continue; // No intersection at all + } + + // Intersection, check if it's closer than the previous one + const auto hitDistSq = (hitPos - localOrigin).LengthSquared(); + if (c->minHitDistSq > hitDistSq) + { + c->minHitDistSq = hitDistSq; + c->hitGeo = geo; + c->hitAtomic = a; + c->hitTri = tri; + c->hitBary = hitBary; + c->hitPosOS = localToObjTransform.TransformVector(hitPos); // Transform back into object space + } + } + + return true; + }; + + if (c.entity->m_pRwObject->object.type == 2 /*rpCLUMP*/) + { + RpClumpForAllAtomics(c.entity->m_pRwObject, ProcessOneAtomic, &c); + } + else + { // Object is a single atomic, so process directly + ProcessOneAtomic(reinterpret_cast(c.entity->m_pRwObject), &c); + } + + // It might be false if the collision model differs from the clump + // This is completely normal as collisions models are meant to be simplified + // compared to the clump's geometry + if (ret.valid = c.hitGeo != nullptr) + { + // 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 (auto i = 3u; i-- > 0;) + { + // UV set index - Usually models only use level 0 indices, so let's stick with that + const auto uvSetIdx = 0; + + // Vertex's UV position + const auto vtxUV = &c.hitGeo->texcoords[uvSetIdx][c.hitTri->verts[i]]; + + // Now, just interpolate + ret.uv += CVector2D{vtxUV->u, vtxUV->v} * c.hitBary[i]; + } + + // Find out material texture name + // For some reason this is sometimes null + const auto tex = c.hitGeo->materials.materials[c.hitTri->materialId]->texture; + ret.textureName = tex ? tex->name : nullptr; + + const auto frame = RpAtomicGetFrame(c.hitAtomic); // `RpAtomicGetFrame` + ret.frameName = frame ? frame->szName : nullptr; + + // Get hit position in world space + ret.hitPos = c.entMat.TransformVector(c.hitPosOS); + } + + return ret; +} + bool CWorldSA::ProcessLineOfSight(const CVector* vecStart, const CVector* vecEnd, CColPoint** colCollision, CEntity** CollisionEntity, const SLineOfSightFlags flags, SLineOfSightBuildingResult* pBuildingResult, SProcessLineOfSightMaterialInfoResult* outMatInfo) { @@ -354,154 +515,9 @@ bool CWorldSA::ProcessLineOfSight(const CVector* vecStart, const CVector* vecEnd } } - if (outMatInfo && targetEntity && targetEntity->m_pRwObject) + if (outMatInfo && *CollisionEntity) { - struct Context - { - float minHitDistSq{}; //< [squared] hit distance from the line segment's origin - CVector originOS, endOS, dirOS; //< Line origin, end and dir [in object space] - CMatrix entMat, entInvMat; //< The hit entity's matrix, and it's inverse - RpTriangle* hitTri{}; //< The triangle hit - RpAtomic* hitAtomic{}; //< The atomic of the hit triangle's geometry - RpGeometry* hitGeo{}; //< The geometry of the hit triangle - CVector hitBary{}; //< Barycentric coordinates [on the hit triangle] of the hit - CVector hitPosOS{}; //< Hit position in object space - CEntitySAInterface* entity{}; //< The hit entity - } c = {}; - - c.entity = targetEntity; - - // Get matrix, and it's inverse - targetEntity->Placeable.matrix->ConvertToMatrix(c.entMat); - c.entInvMat = c.entMat.Inverse(); - - // ...to transform the line origin and end into object space - c.originOS = c.entInvMat.TransformVector(*vecStart); - c.endOS = c.entInvMat.TransformVector(*vecEnd); - c.dirOS = c.endOS - c.originOS; - c.minHitDistSq = c.dirOS.LengthSquared(); // By setting it to this value we avoid collisions that would be detected after the line segment - // [but are still ont the ray] - - // Do raycast against the DFF to get hit position material UV and name - // This is very slow - // Perhaps we could paralellize it somehow? [OpenMP?] - const auto ProcessOneAtomic = [](RpAtomic* a, void* data) - { - const auto c = (Context*)data; - const auto f = RpAtomicGetFrame(a); - - const auto GetFrameCMatrix = [](RwFrame* f) - { - CMatrix out; - pGame->GetRenderWare()->RwMatrixToCMatrix(*RwFrameGetMatrix(f), out); - return out; - }; - - // Atomic not visible - if (!a->renderCallback || !(a->object.object.flags & 0x04 /*rpATOMICRENDER*/)) - { - return true; - } - - // Sometimes atomics have no geometry [I don't think that should be possible, but okay] - const auto geo = a->geometry; - if (!geo) - { - return true; - } - - // Calculate transformation by traversing the hierarchy from the bottom (this frame) -> top (root frame) - CMatrix localToObjTransform{}; - for (auto i = f; i && i != i->root; i = RwFrameGetParent(i)) - { - localToObjTransform = GetFrameCMatrix(i) * localToObjTransform; - } - const auto objToLocalTransform = localToObjTransform.Inverse(); - - const auto ObjectToLocalSpace = [&](const CVector& in) { return objToLocalTransform.TransformVector(in); }; - - // Transform from object space, into local (the frame's) space - const auto localOrigin = ObjectToLocalSpace(c->originOS); - const auto localEnd = ObjectToLocalSpace(c->endOS); - -#if 0 - if (!CCollisionSA::TestLineSphere( - CColLineSA{localOrigin, 0.f, localEnd, 0.f}, - reinterpret_cast(*RpAtomicGetBoundingSphere(a)) // Fine for now - )) { - return true; // Line segment doesn't touch bsp - } -#endif - const auto localDir = localEnd - localOrigin; - - const auto verts = reinterpret_cast(geo->morph_target->verts); // It's fine, trust me bro - for (auto i = geo->triangles_size; i-- > 0;) - { - const auto tri = &geo->triangles[i]; - - // Process the line against the triangle - CVector hitBary, hitPos; - if (!localOrigin.IntersectsSegmentTriangle(localDir, verts[tri->verts[0]], verts[tri->verts[1]], verts[tri->verts[2]], &hitPos, &hitBary)) - { - continue; // No intersection at all - } - - // Intersection, check if it's closer than the previous one - const auto hitDistSq = (hitPos - localOrigin).LengthSquared(); - if (c->minHitDistSq > hitDistSq) - { - c->minHitDistSq = hitDistSq; - c->hitGeo = geo; - c->hitAtomic = a; - c->hitTri = tri; - c->hitBary = hitBary; - c->hitPosOS = localToObjTransform.TransformVector(hitPos); // Transform back into object space - } - } - - return true; - }; - - if (targetEntity->m_pRwObject->object.type == 2 /*rpCLUMP*/) - { - RpClumpForAllAtomics(targetEntity->m_pRwObject, ProcessOneAtomic, &c); - } - else - { // Object is a single atomic, so process directly - ProcessOneAtomic(reinterpret_cast(targetEntity->m_pRwObject), &c); - } - - // It might be false if the collision model differs from the clump - // This is completely normal as collisions models are meant to be simplified - // compared to the clump's geometry - if (outMatInfo->valid = c.hitGeo != nullptr) - { - // 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 - outMatInfo->uv = {}; - for (auto i = 3u; i-- > 0;) - { - // UV set index - Usually models only use level 0 indices, so let's stick with that - const auto uvSetIdx = 0; - - // Vertex's UV position - const auto vtxUV = &c.hitGeo->texcoords[uvSetIdx][c.hitTri->verts[i]]; - - // Now, just interpolate - outMatInfo->uv += CVector2D{vtxUV->u, vtxUV->v} * c.hitBary[i]; - } - - // Find out material texture name - // For some reason this is sometimes null - const auto tex = c.hitGeo->materials.materials[c.hitTri->materialId]->texture; - outMatInfo->textureName = tex ? tex->name : nullptr; - - const auto frame = RpAtomicGetFrame(c.hitAtomic); // `RpAtomicGetFrame` - outMatInfo->frameName = frame ? frame->szName : nullptr; - - // Get hit position in world space - outMatInfo->hitPos = c.entMat.TransformVector(c.hitPosOS); - } + *outMatInfo = ProcessLineAgainstMesh(*CollisionEntity, *vecStart, *vecEnd); } if (colCollision) diff --git a/Client/game_sa/CWorldSA.h b/Client/game_sa/CWorldSA.h index ea5db7b985..aee985ed7e 100644 --- a/Client/game_sa/CWorldSA.h +++ b/Client/game_sa/CWorldSA.h @@ -48,6 +48,7 @@ class CWorldSA : public CWorld void Remove(CEntity* entity, eDebugCaller CallerId); void Remove(CEntitySAInterface* entityInterface, eDebugCaller CallerId); void RemoveReferencesToDeletedObject(CEntitySAInterface* entity); + auto ProcessLineAgainstMesh(CEntity* e, CVector start, CVector end) -> SProcessLineOfSightMaterialInfoResult; bool ProcessLineOfSight(const CVector* vecStart, const CVector* vecEnd, CColPoint** colCollision, CEntity** CollisionEntity, const SLineOfSightFlags flags, SLineOfSightBuildingResult* pBuildingResult, SProcessLineOfSightMaterialInfoResult* outMatInfo = nullptr); void IgnoreEntity(CEntity* entity); diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp index ba7655511d..1e0f5eef8d 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp @@ -21,6 +21,7 @@ void CLuaWorldDefs::LoadFunctions() {"getColorFilter", ArgumentParser}, {"getRoofPosition", GetRoofPosition}, {"getGroundPosition", GetGroundPosition}, + {"processLineAgainstMesh", ArgumentParser}, {"processLineOfSight", ProcessLineOfSight}, {"getWorldFromScreenPosition", GetWorldFromScreenPosition}, {"getScreenFromWorldPosition", GetScreenFromWorldPosition}, @@ -230,6 +231,31 @@ int CLuaWorldDefs::GetRoofPosition(lua_State* luaVM) return 1; } +std::variant> CLuaWorldDefs::ProcessLineAgainstMesh(CClientEntity* e, CVector start, CVector end) { + const auto ge = e->GetGameEntity(); + if (!ge) { + // Element likely not streamed in, and such + // Can't process it. This isn't an error per-se, thus we won't raise anything and treat this as a no-hit scenario + return { false }; + } + SProcessLineOfSightMaterialInfoResult matInfo{}; + g_pGame->GetWorld()->ProcessLineAgainstMesh(ge, start, end); + if (!matInfo.valid) { + return { false }; // No hit + } + return CLuaMultiReturn{ + matInfo.uv.fX, + matInfo.uv.fY, + + matInfo.textureName, + matInfo.frameName, + + matInfo.hitPos.fX, + matInfo.hitPos.fY, + matInfo.hitPos.fZ, + }; +} + int CLuaWorldDefs::ProcessLineOfSight(lua_State* L) { // bool float float float element float float float int int int processLineOfSight ( float startX, float startY, float startZ, float endX, float endY, diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.h index b273237984..dfce7dfd1f 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.h @@ -15,9 +15,11 @@ class CLuaWorldDefs : public CLuaDefs public: static void LoadFunctions(); + LUA_DECLARE(GetTime); LUA_DECLARE(GetGroundPosition); LUA_DECLARE(GetRoofPosition); + static std::variant> ProcessLineAgainstMesh(CClientEntity* e, CVector start, CVector end); LUA_DECLARE(ProcessLineOfSight); LUA_DECLARE(IsLineOfSightClear); LUA_DECLARE(GetWorldFromScreenPosition); diff --git a/Client/sdk/game/CWorld.h b/Client/sdk/game/CWorld.h index dda88e296e..1b6f50a761 100644 --- a/Client/sdk/game/CWorld.h +++ b/Client/sdk/game/CWorld.h @@ -315,6 +315,7 @@ class CWorld virtual void Add(CEntity* entity, eDebugCaller CallerId) = 0; virtual void Remove(CEntity* entity, eDebugCaller CallerId) = 0; virtual void Remove(CEntitySAInterface* entityInterface, eDebugCaller CallerId) = 0; + virtual auto ProcessLineAgainstMesh(CEntity* e, CVector start, CVector end) -> SProcessLineOfSightMaterialInfoResult; virtual bool ProcessLineOfSight(const CVector* vecStart, const CVector* vecEnd, CColPoint** colCollision, CEntity** CollisionEntity, const SLineOfSightFlags flags = SLineOfSightFlags(), SLineOfSightBuildingResult* pBuildingResult = NULL, SProcessLineOfSightMaterialInfoResult* outMatInfo = {}) = 0; virtual void IgnoreEntity(CEntity* entity) = 0; diff --git a/Shared/mods/deathmatch/logic/lua/CLuaMultiReturn.h b/Shared/mods/deathmatch/logic/lua/CLuaMultiReturn.h index 218c08f225..47587fac84 100644 --- a/Shared/mods/deathmatch/logic/lua/CLuaMultiReturn.h +++ b/Shared/mods/deathmatch/logic/lua/CLuaMultiReturn.h @@ -16,7 +16,7 @@ struct CLuaMultiReturn { // Note: We use a separate template for the constructor arguments // to allow type conversions. For example: return { "hello", 42 }; - // is a valid statement to construct a LuaMuliReturn + // is a valid statement to construct a CLuaMultiReturn template CLuaMultiReturn(Args... args) : values{args...} { From 478ec369d68ee970dc1f46b6086f24d859c51038 Mon Sep 17 00:00:00 2001 From: Pirulax Date: Sat, 9 Sep 2023 13:52:14 +0200 Subject: [PATCH 2/4] Use `CEntitySAInterface` instead of `CEntity` --- Client/game_sa/CWorldSA.cpp | 12 ++++++++---- Client/game_sa/CWorldSA.h | 2 +- .../mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp | 2 +- Client/sdk/game/CWorld.h | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Client/game_sa/CWorldSA.cpp b/Client/game_sa/CWorldSA.cpp index 4dce3c06e4..5f81eae7b5 100644 --- a/Client/game_sa/CWorldSA.cpp +++ b/Client/game_sa/CWorldSA.cpp @@ -255,7 +255,7 @@ void ConvertMatrixToEulerAngles(const CMatrix_Padded& matrixPadded, float& fX, f } -auto CWorldSA::ProcessLineAgainstMesh(CEntity* targetEntity, CVector start, CVector end) -> SProcessLineOfSightMaterialInfoResult +auto CWorldSA::ProcessLineAgainstMesh(CEntitySAInterface* targetEntity, CVector start, CVector end) -> SProcessLineOfSightMaterialInfoResult { assert(targetEntity); @@ -274,7 +274,7 @@ auto CWorldSA::ProcessLineAgainstMesh(CEntity* targetEntity, CVector start, CVec CEntitySAInterface* entity{}; //< The hit entity } c = {}; - c.entity = targetEntity->GetInterface(); + c.entity = targetEntity; if (!c.entity->m_pRwObject) { return ret; // isValid will be false in this case @@ -515,9 +515,13 @@ bool CWorldSA::ProcessLineOfSight(const CVector* vecStart, const CVector* vecEnd } } - if (outMatInfo && *CollisionEntity) + if (outMatInfo) { - *outMatInfo = ProcessLineAgainstMesh(*CollisionEntity, *vecStart, *vecEnd); + outMatInfo->valid = false; + if (targetEntity) + { + *outMatInfo = ProcessLineAgainstMesh(targetEntity, *vecStart, *vecEnd); + } } if (colCollision) diff --git a/Client/game_sa/CWorldSA.h b/Client/game_sa/CWorldSA.h index aee985ed7e..5b18c9b35d 100644 --- a/Client/game_sa/CWorldSA.h +++ b/Client/game_sa/CWorldSA.h @@ -48,7 +48,7 @@ class CWorldSA : public CWorld void Remove(CEntity* entity, eDebugCaller CallerId); void Remove(CEntitySAInterface* entityInterface, eDebugCaller CallerId); void RemoveReferencesToDeletedObject(CEntitySAInterface* entity); - auto ProcessLineAgainstMesh(CEntity* e, CVector start, CVector end) -> SProcessLineOfSightMaterialInfoResult; + auto ProcessLineAgainstMesh(CEntitySAInterface* e, CVector start, CVector end) -> SProcessLineOfSightMaterialInfoResult override; bool ProcessLineOfSight(const CVector* vecStart, const CVector* vecEnd, CColPoint** colCollision, CEntity** CollisionEntity, const SLineOfSightFlags flags, SLineOfSightBuildingResult* pBuildingResult, SProcessLineOfSightMaterialInfoResult* outMatInfo = nullptr); void IgnoreEntity(CEntity* entity); diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp index 1e0f5eef8d..c8f8304690 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp @@ -239,7 +239,7 @@ std::variantGetWorld()->ProcessLineAgainstMesh(ge, start, end); + g_pGame->GetWorld()->ProcessLineAgainstMesh(ge->GetInterface(), start, end); if (!matInfo.valid) { return { false }; // No hit } diff --git a/Client/sdk/game/CWorld.h b/Client/sdk/game/CWorld.h index 1b6f50a761..c2319988d9 100644 --- a/Client/sdk/game/CWorld.h +++ b/Client/sdk/game/CWorld.h @@ -315,7 +315,7 @@ class CWorld virtual void Add(CEntity* entity, eDebugCaller CallerId) = 0; virtual void Remove(CEntity* entity, eDebugCaller CallerId) = 0; virtual void Remove(CEntitySAInterface* entityInterface, eDebugCaller CallerId) = 0; - virtual auto ProcessLineAgainstMesh(CEntity* e, CVector start, CVector end) -> SProcessLineOfSightMaterialInfoResult; + virtual auto ProcessLineAgainstMesh(CEntitySAInterface* e, CVector start, CVector end) -> SProcessLineOfSightMaterialInfoResult = 0; virtual bool ProcessLineOfSight(const CVector* vecStart, const CVector* vecEnd, CColPoint** colCollision, CEntity** CollisionEntity, const SLineOfSightFlags flags = SLineOfSightFlags(), SLineOfSightBuildingResult* pBuildingResult = NULL, SProcessLineOfSightMaterialInfoResult* outMatInfo = {}) = 0; virtual void IgnoreEntity(CEntity* entity) = 0; From 22242b4ddbdd9ab058880462bbc90013c1cccb59 Mon Sep 17 00:00:00 2001 From: Pirulax Date: Mon, 11 Sep 2023 18:01:31 +0200 Subject: [PATCH 3/4] Fix bug --- Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp | 9 +++++---- Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp index c8f8304690..de78ad3ce9 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp @@ -231,19 +231,20 @@ int CLuaWorldDefs::GetRoofPosition(lua_State* luaVM) return 1; } -std::variant> CLuaWorldDefs::ProcessLineAgainstMesh(CClientEntity* e, CVector start, CVector end) { +std::variant> CLuaWorldDefs::ProcessLineAgainstMesh(CClientEntity* e, CVector start, CVector end) { const auto ge = e->GetGameEntity(); if (!ge) { // Element likely not streamed in, and such // Can't process it. This isn't an error per-se, thus we won't raise anything and treat this as a no-hit scenario return { false }; } - SProcessLineOfSightMaterialInfoResult matInfo{}; - g_pGame->GetWorld()->ProcessLineAgainstMesh(ge->GetInterface(), start, end); + const SProcessLineOfSightMaterialInfoResult matInfo{g_pGame->GetWorld()->ProcessLineAgainstMesh(ge->GetInterface(), start, end)}; if (!matInfo.valid) { return { false }; // No hit } - return CLuaMultiReturn{ + return CLuaMultiReturn{ + true, + matInfo.uv.fX, matInfo.uv.fY, diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.h index dfce7dfd1f..894404f271 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.h @@ -19,7 +19,7 @@ class CLuaWorldDefs : public CLuaDefs LUA_DECLARE(GetTime); LUA_DECLARE(GetGroundPosition); LUA_DECLARE(GetRoofPosition); - static std::variant> ProcessLineAgainstMesh(CClientEntity* e, CVector start, CVector end); + static std::variant> ProcessLineAgainstMesh(CClientEntity* e, CVector start, CVector end); LUA_DECLARE(ProcessLineOfSight); LUA_DECLARE(IsLineOfSightClear); LUA_DECLARE(GetWorldFromScreenPosition); From d178afe90235023a7ae58ada4de39a27e1b85a27 Mon Sep 17 00:00:00 2001 From: Pirulax Date: Mon, 18 Sep 2023 00:10:31 +0200 Subject: [PATCH 4/4] Apply suggestions from CR --- Client/game_sa/CWorldSA.cpp | 40 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Client/game_sa/CWorldSA.cpp b/Client/game_sa/CWorldSA.cpp index 5f81eae7b5..b946b7e3c1 100644 --- a/Client/game_sa/CWorldSA.cpp +++ b/Client/game_sa/CWorldSA.cpp @@ -296,8 +296,8 @@ auto CWorldSA::ProcessLineAgainstMesh(CEntitySAInterface* targetEntity, CVector // Perhaps we could parallelize it somehow? [OpenMP?] const auto ProcessOneAtomic = [](RpAtomic* a, void* data) { - const auto c = (Context*)data; - const auto f = RpAtomicGetFrame(a); + Context* const c = static_cast(data); + RwFrame* const f = RpAtomicGetFrame(a); const auto GetFrameCMatrix = [](RwFrame* f) { @@ -313,7 +313,7 @@ auto CWorldSA::ProcessLineAgainstMesh(CEntitySAInterface* targetEntity, CVector } // Sometimes atomics have no geometry [I don't think that should be possible, but okay] - const auto geo = a->geometry; + RpGeometry* const geo = a->geometry; if (!geo) { return true; @@ -321,17 +321,17 @@ auto CWorldSA::ProcessLineAgainstMesh(CEntitySAInterface* targetEntity, CVector // Calculate transformation by traversing the hierarchy from the bottom (this frame) -> top (root frame) CMatrix localToObjTransform{}; - for (auto i = f; i && i != i->root; i = RwFrameGetParent(i)) + for (RwFrame* i = f; i && i != i->root; i = RwFrameGetParent(i)) { localToObjTransform = GetFrameCMatrix(i) * localToObjTransform; } - const auto objToLocalTransform = localToObjTransform.Inverse(); + const CMatrix objToLocalTransform = localToObjTransform.Inverse(); const auto ObjectToLocalSpace = [&](const CVector& in) { return objToLocalTransform.TransformVector(in); }; // Transform from object space, into local (the frame's) space - const auto localOrigin = ObjectToLocalSpace(c->originOS); - const auto localEnd = ObjectToLocalSpace(c->endOS); + const CVector localOrigin = ObjectToLocalSpace(c->originOS); + const CVector localEnd = ObjectToLocalSpace(c->endOS); #if 0 if (!CCollisionSA::TestLineSphere( @@ -341,12 +341,12 @@ auto CWorldSA::ProcessLineAgainstMesh(CEntitySAInterface* targetEntity, CVector return true; // Line segment doesn't touch bsp } #endif - const auto localDir = localEnd - localOrigin; + const CVector localDir = localEnd - localOrigin; - const auto verts = reinterpret_cast(geo->morph_target->verts); // It's fine, trust me bro + const CVector* const verts = reinterpret_cast(geo->morph_target->verts); // It's fine, trust me bro for (auto i = geo->triangles_size; i-- > 0;) { - const auto tri = &geo->triangles[i]; + RpTriangle* const tri = &geo->triangles[i]; // Process the line against the triangle CVector hitBary, hitPos; @@ -356,7 +356,7 @@ auto CWorldSA::ProcessLineAgainstMesh(CEntitySAInterface* targetEntity, CVector } // Intersection, check if it's closer than the previous one - const auto hitDistSq = (hitPos - localOrigin).LengthSquared(); + const float hitDistSq = (hitPos - localOrigin).LengthSquared(); if (c->minHitDistSq > hitDistSq) { c->minHitDistSq = hitDistSq; @@ -380,21 +380,18 @@ auto CWorldSA::ProcessLineAgainstMesh(CEntitySAInterface* targetEntity, CVector ProcessOneAtomic(reinterpret_cast(c.entity->m_pRwObject), &c); } - // It might be false if the collision model differs from the clump - // This is completely normal as collisions models are meant to be simplified - // compared to the clump's geometry if (ret.valid = c.hitGeo != nullptr) { // 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 (auto i = 3u; i-- > 0;) + for (int i = 0; i < 3; i++) { // UV set index - Usually models only use level 0 indices, so let's stick with that - const auto uvSetIdx = 0; + const int uvSetIdx = 0; // Vertex's UV position - const auto vtxUV = &c.hitGeo->texcoords[uvSetIdx][c.hitTri->verts[i]]; + RwTextureCoordinates* const vtxUV = &c.hitGeo->texcoords[uvSetIdx][c.hitTri->verts[i]]; // Now, just interpolate ret.uv += CVector2D{vtxUV->u, vtxUV->v} * c.hitBary[i]; @@ -402,11 +399,11 @@ auto CWorldSA::ProcessLineAgainstMesh(CEntitySAInterface* targetEntity, CVector // Find out material texture name // For some reason this is sometimes null - const auto tex = c.hitGeo->materials.materials[c.hitTri->materialId]->texture; + RwTexture* const tex = c.hitGeo->materials.materials[c.hitTri->materialId]->texture; ret.textureName = tex ? tex->name : nullptr; - const auto frame = RpAtomicGetFrame(c.hitAtomic); // `RpAtomicGetFrame` - ret.frameName = frame ? frame->szName : nullptr; + RwFrame* const hitFrame = RpAtomicGetFrame(c.hitAtomic); + ret.frameName = hitFrame ? hitFrame->szName : nullptr; // Get hit position in world space ret.hitPos = c.entMat.TransformVector(c.hitPosOS); @@ -520,6 +517,9 @@ bool CWorldSA::ProcessLineOfSight(const CVector* vecStart, const CVector* vecEnd outMatInfo->valid = false; if (targetEntity) { + // There might not be a texture hit info result as the collision model differs from the mesh itself. + // This is completely normal as collisions models are meant to be simplified + // compared to the mesh *outMatInfo = ProcessLineAgainstMesh(targetEntity, *vecStart, *vecEnd); } }