Skip to content

Add texture hit info to processLineOfSight #3099

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions Client/game_sa/CColModelSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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");
Expand Down
19 changes: 19 additions & 0 deletions Client/game_sa/CCollisionSA.cpp
Original file line number Diff line number Diff line change
@@ -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<bool(__cdecl*)(const CColLineSA&, const CColSphereSA&)>(0x417470)(line, sphere);
}
19 changes: 19 additions & 0 deletions Client/game_sa/CCollisionSA.h
Original file line number Diff line number Diff line change
@@ -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;
};
2 changes: 1 addition & 1 deletion Client/game_sa/CRenderWareSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
156 changes: 155 additions & 1 deletion Client/game_sa/CWorldSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<CColSphereSA&>(*RpAtomicGetBoundingSphere(a)) // Fine for now
)) {
return true; // Line segment doesn't touch bsp
}
#endif
const auto localDir = localEnd - localOrigin;

const auto verts = reinterpret_cast<CVector*>(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<RpAtomic*>(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
Expand Down
2 changes: 1 addition & 1 deletion Client/game_sa/CWorldSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading