From 86f3344d1371a9783c2c7b755b895160a03ff6cd Mon Sep 17 00:00:00 2001 From: Pirulax Date: Tue, 15 Aug 2023 11:46:30 +0200 Subject: [PATCH 1/2] Add optional texture hit info to processLineOfSight (PR #3099) --- Client/game_sa/CColModelSA.h | 16 +- Client/game_sa/CCollisionSA.cpp | 19 +++ Client/game_sa/CCollisionSA.h | 19 +++ Client/game_sa/CRenderWareSA.cpp | 2 +- Client/game_sa/CWorldSA.cpp | 156 +++++++++++++++++- Client/game_sa/CWorldSA.h | 2 +- .../logic/CStaticFunctionDefinitions.cpp | 4 +- .../logic/CStaticFunctionDefinitions.h | 2 +- .../logic/luadefs/CLuaWorldDefs.cpp | 89 ++++++---- Client/sdk/game/CCollision.h | 20 +++ Client/sdk/game/CWorld.h | 10 +- Client/sdk/game/RenderWare.h | 65 ++++++-- Shared/sdk/CVector.h | 16 +- 13 files changed, 356 insertions(+), 64 deletions(-) create mode 100644 Client/game_sa/CCollisionSA.cpp create mode 100644 Client/game_sa/CCollisionSA.h create mode 100644 Client/sdk/game/CCollision.h diff --git a/Client/game_sa/CColModelSA.h b/Client/game_sa/CColModelSA.h index cb32be3412..dba62b0805 100644 --- a/Client/game_sa/CColModelSA.h +++ b/Client/game_sa/CColModelSA.h @@ -72,13 +72,13 @@ struct CColSphereSA : CSphereSA { union { - EColSurface m_material; + EColSurface m_material{}; std::uint8_t m_collisionSlot; }; union { - std::uint8_t m_flags; + std::uint8_t m_flags{}; struct { @@ -93,15 +93,13 @@ struct CColSphereSA : CSphereSA }; }; - std::uint8_t m_lighting; - std::uint8_t m_light; + std::uint8_t m_lighting{}; + std::uint8_t m_light{}; - CColSphereSA() + CColSphereSA() = default; + CColSphereSA(const CSphereSA& sp) : + CSphereSA{ sp } { - m_collisionSlot = 0; - m_flags = 0; - m_lighting = 0; - m_light = 0; } }; static_assert(sizeof(CColSphereSA) == 0x14, "Invalid size for CColSphereSA"); diff --git a/Client/game_sa/CCollisionSA.cpp b/Client/game_sa/CCollisionSA.cpp new file mode 100644 index 0000000000..a377b3e08b --- /dev/null +++ b/Client/game_sa/CCollisionSA.cpp @@ -0,0 +1,19 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CCollisionSA.cpp + * PURPOSE: Implementation of `CCollision` - collision detection related functions + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" + +#include "CCollisionSA.h" + +bool CCollisionSA::TestLineSphere(const CColLineSA& line, const CColSphereSA& sphere) const +{ + return reinterpret_cast(0x417470)(line, sphere); +} diff --git a/Client/game_sa/CCollisionSA.h b/Client/game_sa/CCollisionSA.h new file mode 100644 index 0000000000..2ce964d391 --- /dev/null +++ b/Client/game_sa/CCollisionSA.h @@ -0,0 +1,19 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CCollisionSA.h + * PURPOSE: Header file for `CCollision` - collision detection related functions + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include "game/CCollision.h" + +class CCollisionSA : CCollision { +public: + bool TestLineSphere(const CColLineSA& line, const CColSphereSA& sphere) const override; +}; diff --git a/Client/game_sa/CRenderWareSA.cpp b/Client/game_sa/CRenderWareSA.cpp index e5ba0086b0..31556739d3 100644 --- a/Client/game_sa/CRenderWareSA.cpp +++ b/Client/game_sa/CRenderWareSA.cpp @@ -149,7 +149,7 @@ static bool ReplaceAllCB(RpAtomic* atomic, void* pData) data->pReplacements[i].atomic->renderCallback = atomic->renderCallback; data->pReplacements[i].atomic->frame = atomic->frame; data->pReplacements[i].atomic->render = atomic->render; - data->pReplacements[i].atomic->interpolation = atomic->interpolation; + data->pReplacements[i].atomic->interpolator = atomic->interpolator; data->pReplacements[i].atomic->info = atomic->info; // add the new atomic to the vehicle clump diff --git a/Client/game_sa/CWorldSA.cpp b/Client/game_sa/CWorldSA.cpp index 291b90da66..527a8108a1 100644 --- a/Client/game_sa/CWorldSA.cpp +++ b/Client/game_sa/CWorldSA.cpp @@ -15,6 +15,9 @@ #include "CGameSA.h" #include "CPoolsSA.h" #include "CWorldSA.h" +#include "CColModelSA.h" +#include "gamesa_renderware.h" +#include "CCollisionSA.h" extern CCoreInterface* g_pCore; extern CGameSA* pGame; @@ -252,7 +255,7 @@ void ConvertMatrixToEulerAngles(const CMatrix_Padded& matrixPadded, float& fX, f } bool CWorldSA::ProcessLineOfSight(const CVector* vecStart, const CVector* vecEnd, CColPoint** colCollision, CEntity** CollisionEntity, - const SLineOfSightFlags flags, SLineOfSightBuildingResult* pBuildingResult) + const SLineOfSightFlags flags, SLineOfSightBuildingResult* pBuildingResult, SProcessLineOfSightMaterialInfoResult* outMatInfo) { DWORD dwPadding[100]; // stops the function missbehaving and overwriting the return address dwPadding[0] = 0; // prevent the warning and eventual compiler optimizations from removing it @@ -350,6 +353,157 @@ bool CWorldSA::ProcessLineOfSight(const CVector* vecStart, const CVector* vecEnd } } } + + if (outMatInfo && targetEntity && targetEntity->m_pRwObject) + { + 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); + } + } + if (colCollision) *colCollision = pColPointSA; else diff --git a/Client/game_sa/CWorldSA.h b/Client/game_sa/CWorldSA.h index e896a17c11..ea5db7b985 100644 --- a/Client/game_sa/CWorldSA.h +++ b/Client/game_sa/CWorldSA.h @@ -49,7 +49,7 @@ class CWorldSA : public CWorld void Remove(CEntitySAInterface* entityInterface, eDebugCaller CallerId); void RemoveReferencesToDeletedObject(CEntitySAInterface* entity); bool ProcessLineOfSight(const CVector* vecStart, const CVector* vecEnd, CColPoint** colCollision, CEntity** CollisionEntity, const SLineOfSightFlags flags, - SLineOfSightBuildingResult* pBuildingResult); + SLineOfSightBuildingResult* pBuildingResult, SProcessLineOfSightMaterialInfoResult* outMatInfo = nullptr); void IgnoreEntity(CEntity* entity); float FindGroundZFor3DPosition(CVector* vecPosition); float FindRoofZFor3DCoord(CVector* pvecPosition, bool* pbOutResult); diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index d176174f36..f0bae16600 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -6316,7 +6316,7 @@ bool CStaticFunctionDefinitions::SetTime(unsigned char ucHour, unsigned char ucM bool CStaticFunctionDefinitions::ProcessLineOfSight(const CVector& vecStart, const CVector& vecEnd, bool& bCollision, CColPoint** pColPoint, CClientEntity** pColEntity, const SLineOfSightFlags& flags, CEntity* pIgnoredEntity, - SLineOfSightBuildingResult* pBuildingResult) + SLineOfSightBuildingResult* pBuildingResult, SProcessLineOfSightMaterialInfoResult* outMatInfo) { assert(pColPoint); assert(pColEntity); @@ -6325,7 +6325,7 @@ bool CStaticFunctionDefinitions::ProcessLineOfSight(const CVector& vecStart, con g_pGame->GetWorld()->IgnoreEntity(pIgnoredEntity); CEntity* pColGameEntity = 0; - bCollision = g_pGame->GetWorld()->ProcessLineOfSight(&vecStart, &vecEnd, pColPoint, &pColGameEntity, flags, pBuildingResult); + bCollision = g_pGame->GetWorld()->ProcessLineOfSight(&vecStart, &vecEnd, pColPoint, &pColGameEntity, flags, pBuildingResult, outMatInfo); if (pIgnoredEntity) g_pGame->GetWorld()->IgnoreEntity(NULL); diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h index 9a1798c3dc..b001aa0775 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h @@ -561,7 +561,7 @@ class CStaticFunctionDefinitions static bool GetTime(unsigned char& ucHour, unsigned char& ucMin); static bool ProcessLineOfSight(const CVector& vecStart, const CVector& vecEnd, bool& bCollision, CColPoint** pColPoint, CClientEntity** pColEntity, const SLineOfSightFlags& flags = SLineOfSightFlags(), CEntity* pIgnoredEntity = NULL, - SLineOfSightBuildingResult* pBuildingResult = NULL); + SLineOfSightBuildingResult* pBuildingResult = NULL, SProcessLineOfSightMaterialInfoResult* outMatInfo = nullptr); static bool IsLineOfSightClear(const CVector& vecStart, const CVector& vecEnd, bool& bIsClear, const SLineOfSightFlags& flags = SLineOfSightFlags(), CEntity* pIgnoredEntity = NULL); static bool TestLineAgainstWater(CVector& vecStart, CVector& vecEnd, CVector& vecCollision); diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp index 2fe8bbf777..a5ab6bcff3 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp @@ -229,7 +229,7 @@ int CLuaWorldDefs::GetRoofPosition(lua_State* luaVM) return 1; } -int CLuaWorldDefs::ProcessLineOfSight(lua_State* luaVM) +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, // float endZ, @@ -241,8 +241,9 @@ int CLuaWorldDefs::ProcessLineOfSight(lua_State* luaVM) SLineOfSightFlags flags; CClientEntity* pIgnoredElement; bool bIncludeBuildingInfo; + bool bIncludeExtraMateriaInfo; - CScriptArgReader argStream(luaVM); + CScriptArgReader argStream(L); argStream.ReadVector3D(vecStart); argStream.ReadVector3D(vecEnd); argStream.ReadBool(flags.bCheckBuildings, true); @@ -256,16 +257,18 @@ int CLuaWorldDefs::ProcessLineOfSight(lua_State* luaVM) argStream.ReadUserData(pIgnoredElement, NULL); argStream.ReadBool(bIncludeBuildingInfo, false); argStream.ReadBool(flags.bCheckCarTires, false); + argStream.ReadBool(bIncludeExtraMateriaInfo, false); if (!argStream.HasErrors()) { - CEntity* pIgnoredEntity = pIgnoredElement ? pIgnoredElement->GetGameEntity() : NULL; - CColPoint* pColPoint = NULL; - CClientEntity* pColEntity = NULL; - bool bCollision; - SLineOfSightBuildingResult buildingResult; + CEntity* pIgnoredEntity = pIgnoredElement ? pIgnoredElement->GetGameEntity() : NULL; + CColPoint* pColPoint = NULL; + CClientEntity* pColEntity = NULL; + bool bCollision; + SLineOfSightBuildingResult buildingResult; + SProcessLineOfSightMaterialInfoResult matInfo; if (CStaticFunctionDefinitions::ProcessLineOfSight(vecStart, vecEnd, bCollision, &pColPoint, &pColEntity, flags, pIgnoredEntity, - bIncludeBuildingInfo ? &buildingResult : NULL)) + bIncludeBuildingInfo ? &buildingResult : NULL, bIncludeExtraMateriaInfo ? &matInfo : nullptr)) { // Got a collision? CVector vecColPosition; @@ -288,50 +291,70 @@ int CLuaWorldDefs::ProcessLineOfSight(lua_State* luaVM) pColPoint->Destroy(); } - lua_pushboolean(luaVM, bCollision); + lua_pushboolean(L, bCollision); if (bCollision) { - lua_pushnumber(luaVM, vecColPosition.fX); - lua_pushnumber(luaVM, vecColPosition.fY); - lua_pushnumber(luaVM, vecColPosition.fZ); + lua_pushnumber(L, vecColPosition.fX); + lua_pushnumber(L, vecColPosition.fY); + lua_pushnumber(L, vecColPosition.fZ); if (pColEntity) - lua_pushelement(luaVM, pColEntity); + lua_pushelement(L, pColEntity); else - lua_pushnil(luaVM); + lua_pushnil(L); - lua_pushnumber(luaVM, vecColNormal.fX); - lua_pushnumber(luaVM, vecColNormal.fY); - lua_pushnumber(luaVM, vecColNormal.fZ); + lua_pushnumber(L, vecColNormal.fX); + lua_pushnumber(L, vecColNormal.fY); + lua_pushnumber(L, vecColNormal.fZ); - lua_pushinteger(luaVM, iMaterial); - lua_pushnumber(luaVM, fLighting); - lua_pushinteger(luaVM, iPiece); + lua_pushinteger(L, iMaterial); + lua_pushnumber(L, fLighting); + lua_pushinteger(L, iPiece); - if (bIncludeBuildingInfo && buildingResult.bValid) + if (bIncludeBuildingInfo && buildingResult.bValid) // 8 args { - lua_pushnumber(luaVM, buildingResult.usModelID); + lua_pushnumber(L, buildingResult.usModelID); + + lua_pushnumber(L, buildingResult.vecPosition.fX); + lua_pushnumber(L, buildingResult.vecPosition.fY); + lua_pushnumber(L, buildingResult.vecPosition.fZ); + + lua_pushnumber(L, ConvertRadiansToDegrees(buildingResult.vecRotation.fX)); + lua_pushnumber(L, ConvertRadiansToDegrees(buildingResult.vecRotation.fY)); + lua_pushnumber(L, ConvertRadiansToDegrees(buildingResult.vecRotation.fZ)); + + lua_pushnumber(L, buildingResult.usLODModelID); + } else { + for (auto i = 1 + 3 + 3 + 1; i-- > 0;) { + lua_pushnil(L); + } + } - lua_pushnumber(luaVM, buildingResult.vecPosition.fX); - lua_pushnumber(luaVM, buildingResult.vecPosition.fY); - lua_pushnumber(luaVM, buildingResult.vecPosition.fZ); + if (bIncludeExtraMateriaInfo && matInfo.valid) { // 7 args + lua::Push(L, matInfo.uv.fX); + lua::Push(L, matInfo.uv.fY); - lua_pushnumber(luaVM, ConvertRadiansToDegrees(buildingResult.vecRotation.fX)); - lua_pushnumber(luaVM, ConvertRadiansToDegrees(buildingResult.vecRotation.fY)); - lua_pushnumber(luaVM, ConvertRadiansToDegrees(buildingResult.vecRotation.fZ)); + lua::Push(L, matInfo.textureName); + lua::Push(L, matInfo.frameName); - lua_pushnumber(luaVM, buildingResult.usLODModelID); - return 19; + lua::Push(L, matInfo.hitPos.fX); + lua::Push(L, matInfo.hitPos.fY); + lua::Push(L, matInfo.hitPos.fZ); + } else { + for (auto i = 2 + 1 + 1 + 3; i-- > 0;) { + lua_pushnil(L); + } } - return 11; + + return 11 + 8 + 7; } return 1; } } else - m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + m_pScriptDebugging->LogCustom(L, argStream.GetFullErrorMessage()); - lua_pushboolean(luaVM, false); + lua_pushboolean(L, false); return 1; } diff --git a/Client/sdk/game/CCollision.h b/Client/sdk/game/CCollision.h new file mode 100644 index 0000000000..9b0c26caad --- /dev/null +++ b/Client/sdk/game/CCollision.h @@ -0,0 +1,20 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: sdk/game/CCollision.h + * PURPOSE: Interface file for `CCollisionSA` + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +struct CColLineSA; +struct CColSphereSA; + +class CCollision { +public: + virtual bool TestLineSphere(const CColLineSA& line, const CColSphereSA& sphere) const = 0; +}; diff --git a/Client/sdk/game/CWorld.h b/Client/sdk/game/CWorld.h index 33fbe40a90..dda88e296e 100644 --- a/Client/sdk/game/CWorld.h +++ b/Client/sdk/game/CWorld.h @@ -51,6 +51,14 @@ struct SLineOfSightBuildingResult CEntitySAInterface* pInterface; }; +struct SProcessLineOfSightMaterialInfoResult { + CVector2D uv; //< On-texture UV coordinates of the intersection point + const char* textureName; //< GTA texture name + const char* frameName; //< The name of the frame the hit geometry belongs to + CVector hitPos; //< Precise hit position on the clump [World space] + bool valid{}; //< Data found in this struct is only valid if this is `true`! +}; + struct SBuildingRemoval { SBuildingRemoval() @@ -308,7 +316,7 @@ class CWorld virtual void Remove(CEntity* entity, eDebugCaller CallerId) = 0; virtual void Remove(CEntitySAInterface* entityInterface, eDebugCaller CallerId) = 0; virtual bool ProcessLineOfSight(const CVector* vecStart, const CVector* vecEnd, CColPoint** colCollision, CEntity** CollisionEntity, - const SLineOfSightFlags flags = SLineOfSightFlags(), SLineOfSightBuildingResult* pBuildingResult = NULL) = 0; + const SLineOfSightFlags flags = SLineOfSightFlags(), SLineOfSightBuildingResult* pBuildingResult = NULL, SProcessLineOfSightMaterialInfoResult* outMatInfo = {}) = 0; virtual void IgnoreEntity(CEntity* entity) = 0; virtual float FindGroundZFor3DPosition(CVector* vecPosition) = 0; virtual float FindRoofZFor3DCoord(CVector* pvecPosition, bool* pbOutResult) = 0; diff --git a/Client/sdk/game/RenderWare.h b/Client/sdk/game/RenderWare.h index cc5841f86d..20249e01fd 100644 --- a/Client/sdk/game/RenderWare.h +++ b/Client/sdk/game/RenderWare.h @@ -321,25 +321,35 @@ struct RwGeometry unsigned char unknown1[14]; unsigned short refs; }; -struct RpInterpolation + +/* Interpolator flags */ +enum RpInterpolatorFlag : int32_t +{ + rpINTERPOLATORDIRTYINSTANCE = 0x01, + rpINTERPOLATORDIRTYSPHERE = 0x02, + rpINTERPOLATORNOFRAMEDIRTY = 0x04, +}; +struct RpInterpolator { - unsigned int unknown1; - unsigned int unknown2; - float unknown3; - float unknown4; - float unknown5; + int32_t flags; + int16_t startMorphTarget; + int16_t endMorphTarget; + float time; + float recipTime; + float position; }; + struct RpAtomic { RwObjectFrame object; void* info; RpGeometry* geometry; - RwSphere bsphereLocal; - RwSphere bsphereWorld; + RwSphere boundingSphere; + RwSphere worldBoundingSphere; RpClump* clump; RwListEntry globalClumps; RpAtomicCallback renderCallback; - RpInterpolation interpolation; + RpInterpolator interpolator; unsigned short frame; unsigned short unknown7; RwList sectors; @@ -391,9 +401,16 @@ struct RpMaterials }; struct RpTriangle { - unsigned short v1, v2, v3; + unsigned short verts[3]; unsigned short materialId; }; +struct RpMorphTarget +{ + RpGeometry* parentGeom; + RwSphere boundingSphere; + RwV3d* verts; + RwV3d* normals; +}; struct RpGeometry { RwObject object; @@ -412,9 +429,35 @@ struct RpGeometry RwTextureCoordinates* texcoords[RW_MAX_TEXTURE_COORDS]; void* unknown2; void* info; - void* unknown3; + RpMorphTarget* morph_target; }; +inline auto rwObjectGetParent(RwObject* o) { + return (RwObject*)o->parent; +} + +inline auto RpAtomicGetFrame(RpAtomic* atomic) { + return (RwFrame*)atomic->object.object.parent; +} + +inline auto RwFrameGetParent(RwFrame* f) { + return (RwFrame*)rwObjectGetParent((RwObject*)f); +} + +inline RwMatrix* RwFrameGetMatrix(RwFrame* f) { + return &f->modelling; +} + +inline void _rpAtomicResyncInterpolatedSphere(RpAtomic* atomic) { + reinterpret_cast(0x7491F0)(atomic); +} + +/* NB "RpAtomicGetBoundingSphere(atomic++)" will break it */ +#define RpAtomicGetBoundingSphere(_atomic) \ + ((((_atomic)->interpolator.flags & rpINTERPOLATORDIRTYSPHERE)? \ + _rpAtomicResyncInterpolatedSphere(_atomic), 0: 0), \ + &((_atomic)->boundingSphere)) + /*****************************************************************************/ /** RenderWare I/O **/ /*****************************************************************************/ diff --git a/Shared/sdk/CVector.h b/Shared/sdk/CVector.h index f9ddfa2f21..265b186e9d 100644 --- a/Shared/sdk/CVector.h +++ b/Shared/sdk/CVector.h @@ -15,6 +15,8 @@ #include "CVector4D.h" +class CVector2D; + /** * CVector Structure used to store a 3D vertex. */ @@ -137,15 +139,15 @@ class CVector } // https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm - bool IntersectsSegmentTriangle(const CVector& vecSegment, const CVector& vecVert1, const CVector& vecVert2, const CVector& vecVert3, - CVector* outVec) const noexcept + bool IntersectsSegmentTriangle(const CVector& dir, const CVector& vecVert1, const CVector& vecVert2, const CVector& vecVert3, + CVector* outVec, CVector* outHitBary = nullptr) const noexcept { constexpr float fEpsilon = 1e-6f; CVector vecEdge1, vecEdge2, h, s; float a, f, u, v; - CVector vecRay = vecSegment; + CVector vecRay = dir; vecRay.Normalize(); h = vecRay; @@ -177,11 +179,15 @@ class CVector } float t = f * vecEdge2.DotProduct(&sCrossE1); - if (t > fEpsilon && t <= vecSegment.Length()) + if (t > fEpsilon && t <= dir.Length()) { *outVec = *this + vecRay * t; + if (outHitBary) { // Calculate all barycentric coords if necessary + *outHitBary = { 1.f - u - v, u, v }; // For vertices A, B, C [I assume?] + } return true; } + return false; } @@ -261,4 +267,6 @@ class CVector } bool operator!=(const CVector& param) const noexcept { return !(*this == param); } + + float operator[](size_t i) const noexcept { return ((float*)this)[i]; } }; From 0daf5882fbcbbc4166d2b882f7f8605f2b16f8b7 Mon Sep 17 00:00:00 2001 From: Lpsd <40902730+Lpsd@users.noreply.github.com> Date: Tue, 15 Aug 2023 12:22:22 +0100 Subject: [PATCH 2/2] Add support for double click events in CEF (Fixes #2963, PR #3040) --- Client/cefweb/CWebView.cpp | 4 ++-- Client/cefweb/CWebView.h | 2 +- Client/gui/CGUIWebBrowser_Impl.cpp | 21 ++++++++++++++++--- Client/gui/CGUIWebBrowser_Impl.h | 1 + .../deathmatch/logic/CClientWebBrowser.cpp | 4 ++-- .../mods/deathmatch/logic/CClientWebBrowser.h | 2 +- .../logic/luadefs/CLuaBrowserDefs.cpp | 10 +++++---- Client/sdk/core/CWebViewInterface.h | 2 +- 8 files changed, 32 insertions(+), 14 deletions(-) diff --git a/Client/cefweb/CWebView.cpp b/Client/cefweb/CWebView.cpp index e9e0f31e29..e4e4e3ba20 100644 --- a/Client/cefweb/CWebView.cpp +++ b/Client/cefweb/CWebView.cpp @@ -343,7 +343,7 @@ void CWebView::InjectMouseMove(int iPosX, int iPosY) m_vecMousePosition.y = iPosY; } -void CWebView::InjectMouseDown(eWebBrowserMouseButton mouseButton) +void CWebView::InjectMouseDown(eWebBrowserMouseButton mouseButton, int count) { if (!m_pWebView) return; @@ -355,7 +355,7 @@ void CWebView::InjectMouseDown(eWebBrowserMouseButton mouseButton) // Save mouse button states m_mouseButtonStates[static_cast(mouseButton)] = true; - m_pWebView->GetHost()->SendMouseClickEvent(mouseEvent, static_cast(mouseButton), false, 1); + m_pWebView->GetHost()->SendMouseClickEvent(mouseEvent, static_cast(mouseButton), false, count); } void CWebView::InjectMouseUp(eWebBrowserMouseButton mouseButton) diff --git a/Client/cefweb/CWebView.h b/Client/cefweb/CWebView.h index f294d5a938..69fa691d3a 100644 --- a/Client/cefweb/CWebView.h +++ b/Client/cefweb/CWebView.h @@ -80,7 +80,7 @@ class CWebView : public CWebViewInterface, bool GetProperty(const SString& strKey, SString& outProperty); void InjectMouseMove(int iPosX, int iPosY); - void InjectMouseDown(eWebBrowserMouseButton mouseButton); + void InjectMouseDown(eWebBrowserMouseButton mouseButton, int count); void InjectMouseUp(eWebBrowserMouseButton mouseButton); void InjectMouseWheel(int iScrollVert, int iScrollHorz); void InjectKeyboardEvent(const CefKeyEvent& keyEvent); diff --git a/Client/gui/CGUIWebBrowser_Impl.cpp b/Client/gui/CGUIWebBrowser_Impl.cpp index 4ea4066810..308f2945d0 100644 --- a/Client/gui/CGUIWebBrowser_Impl.cpp +++ b/Client/gui/CGUIWebBrowser_Impl.cpp @@ -39,6 +39,7 @@ CGUIWebBrowser_Impl::CGUIWebBrowser_Impl(CGUI_Impl* pGUI, CGUIElement* pParent) // Apply browser events m_pWindow->subscribeEvent(CEGUI::Window::EventMouseButtonDown, CEGUI::Event::Subscriber(&CGUIWebBrowser_Impl::Event_MouseButtonDown, this)); m_pWindow->subscribeEvent(CEGUI::Window::EventMouseButtonUp, CEGUI::Event::Subscriber(&CGUIWebBrowser_Impl::Event_MouseButtonUp, this)); + m_pWindow->subscribeEvent(CEGUI::Window::EventMouseDoubleClick, CEGUI::Event::Subscriber(&CGUIWebBrowser_Impl::Event_MouseDoubleClick, this)); m_pWindow->subscribeEvent(CEGUI::Window::EventMouseMove, CEGUI::Event::Subscriber(&CGUIWebBrowser_Impl::Event_MouseMove, this)); m_pWindow->subscribeEvent(CEGUI::Window::EventMouseWheel, CEGUI::Event::Subscriber(&CGUIWebBrowser_Impl::Event_MouseWheel, this)); m_pWindow->subscribeEvent(CEGUI::Window::EventActivated, CEGUI::Event::Subscriber(&CGUIWebBrowser_Impl::Event_Activated, this)); @@ -158,11 +159,11 @@ bool CGUIWebBrowser_Impl::Event_MouseButtonDown(const CEGUI::EventArgs& e) const CEGUI::MouseEventArgs& args = reinterpret_cast(e); if (args.button == CEGUI::MouseButton::LeftButton) - m_pWebView->InjectMouseDown(eWebBrowserMouseButton::BROWSER_MOUSEBUTTON_LEFT); + m_pWebView->InjectMouseDown(eWebBrowserMouseButton::BROWSER_MOUSEBUTTON_LEFT, 1); else if (args.button == CEGUI::MouseButton::MiddleButton) - m_pWebView->InjectMouseDown(eWebBrowserMouseButton::BROWSER_MOUSEBUTTON_MIDDLE); + m_pWebView->InjectMouseDown(eWebBrowserMouseButton::BROWSER_MOUSEBUTTON_MIDDLE, 1); else if (args.button == CEGUI::MouseButton::RightButton) - m_pWebView->InjectMouseDown(eWebBrowserMouseButton::BROWSER_MOUSEBUTTON_RIGHT); + m_pWebView->InjectMouseDown(eWebBrowserMouseButton::BROWSER_MOUSEBUTTON_RIGHT, 1); return true; } @@ -181,6 +182,20 @@ bool CGUIWebBrowser_Impl::Event_MouseButtonUp(const CEGUI::EventArgs& e) return true; } +bool CGUIWebBrowser_Impl::Event_MouseDoubleClick(const CEGUI::EventArgs& e) +{ + const CEGUI::MouseEventArgs& args = reinterpret_cast(e); + + if (args.button == CEGUI::MouseButton::LeftButton) + m_pWebView->InjectMouseDown(eWebBrowserMouseButton::BROWSER_MOUSEBUTTON_LEFT, 2); + else if (args.button == CEGUI::MouseButton::MiddleButton) + m_pWebView->InjectMouseDown(eWebBrowserMouseButton::BROWSER_MOUSEBUTTON_MIDDLE, 2); + else if (args.button == CEGUI::MouseButton::RightButton) + m_pWebView->InjectMouseDown(eWebBrowserMouseButton::BROWSER_MOUSEBUTTON_RIGHT, 2); + + return true; +} + bool CGUIWebBrowser_Impl::Event_MouseMove(const CEGUI::EventArgs& e) { const CEGUI::MouseEventArgs& args = reinterpret_cast(e); diff --git a/Client/gui/CGUIWebBrowser_Impl.h b/Client/gui/CGUIWebBrowser_Impl.h index 805386fd21..3ab96dceaf 100644 --- a/Client/gui/CGUIWebBrowser_Impl.h +++ b/Client/gui/CGUIWebBrowser_Impl.h @@ -46,6 +46,7 @@ class CGUIWebBrowser_Impl : public CGUIWebBrowser, public CGUIElement_Impl protected: bool Event_MouseButtonDown(const CEGUI::EventArgs& e); bool Event_MouseButtonUp(const CEGUI::EventArgs& e); + bool Event_MouseDoubleClick(const CEGUI::EventArgs& e); bool Event_MouseWheel(const CEGUI::EventArgs& e); bool Event_MouseMove(const CEGUI::EventArgs& e); bool Event_Activated(const CEGUI::EventArgs& e); diff --git a/Client/mods/deathmatch/logic/CClientWebBrowser.cpp b/Client/mods/deathmatch/logic/CClientWebBrowser.cpp index 05d7a5e3a9..cf54f463e7 100644 --- a/Client/mods/deathmatch/logic/CClientWebBrowser.cpp +++ b/Client/mods/deathmatch/logic/CClientWebBrowser.cpp @@ -101,9 +101,9 @@ void CClientWebBrowser::InjectMouseMove(int iPosX, int iPosY) m_pWebView->InjectMouseMove(iPosX, iPosY); } -void CClientWebBrowser::InjectMouseDown(eWebBrowserMouseButton mouseButton) +void CClientWebBrowser::InjectMouseDown(eWebBrowserMouseButton mouseButton, int count) { - m_pWebView->InjectMouseDown(mouseButton); + m_pWebView->InjectMouseDown(mouseButton, count); } void CClientWebBrowser::InjectMouseUp(eWebBrowserMouseButton mouseButton) diff --git a/Client/mods/deathmatch/logic/CClientWebBrowser.h b/Client/mods/deathmatch/logic/CClientWebBrowser.h index 294e2d5b70..09de6ce0af 100644 --- a/Client/mods/deathmatch/logic/CClientWebBrowser.h +++ b/Client/mods/deathmatch/logic/CClientWebBrowser.h @@ -40,7 +40,7 @@ class CClientWebBrowser : public CClientTexture, public CWebBrowserEventsInterfa bool GetProperty(const SString& strKey, SString& outValue); void InjectMouseMove(int iPosX, int iPosY); - void InjectMouseDown(eWebBrowserMouseButton mouseButton); + void InjectMouseDown(eWebBrowserMouseButton mouseButton, int count); void InjectMouseUp(eWebBrowserMouseButton mouseButton); void InjectMouseWheel(int iScrollVert, int iScrollHorz); diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaBrowserDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaBrowserDefs.cpp index eded57d0a7..c496fca0cd 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaBrowserDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaBrowserDefs.cpp @@ -310,17 +310,19 @@ int CLuaBrowserDefs::InjectBrowserMouseMove(lua_State* luaVM) int CLuaBrowserDefs::InjectBrowserMouseDown(lua_State* luaVM) { - // bool injectBrowserMouseDown ( browser webBrowser, string mouseButton ) - CClientWebBrowser* pWebBrowser; - eWebBrowserMouseButton mouseButton; + // bool injectBrowserMouseDown ( browser webBrowser, string mouseButton [ , bool doubleClick = false ] ) + CClientWebBrowser* pWebBrowser{}; + eWebBrowserMouseButton mouseButton{}; + bool doubleClick{}; CScriptArgReader argStream(luaVM); argStream.ReadUserData(pWebBrowser); argStream.ReadEnumString(mouseButton); + argStream.ReadBool(doubleClick, false); if (!argStream.HasErrors()) { - pWebBrowser->InjectMouseDown(mouseButton); + pWebBrowser->InjectMouseDown(mouseButton, doubleClick ? 2 : 1); lua_pushboolean(luaVM, true); return 1; } diff --git a/Client/sdk/core/CWebViewInterface.h b/Client/sdk/core/CWebViewInterface.h index 7ed6da7981..aadc90ecc3 100644 --- a/Client/sdk/core/CWebViewInterface.h +++ b/Client/sdk/core/CWebViewInterface.h @@ -36,7 +36,7 @@ class CWebViewInterface virtual bool GetProperty(const SString& strKey, SString& outProperty) = 0; virtual void InjectMouseMove(int iPosX, int iPosY) = 0; - virtual void InjectMouseDown(eWebBrowserMouseButton mouseButton) = 0; + virtual void InjectMouseDown(eWebBrowserMouseButton mouseButton, int count) = 0; virtual void InjectMouseUp(eWebBrowserMouseButton mouseButton) = 0; virtual void InjectMouseWheel(int iScrollVert, int iScrollHorz) = 0;