Files
GTASource/game/task/Physics/NmDebug.cpp
T
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

740 lines
30 KiB
C++

#if __BANK
// Game includes:
#include "peds/ped.h"
#include "Task/Physics/NmDebug.h"
#include "Task/Physics/TaskNM.h"
#include "Task/Physics/TaskNMShot.h"
#include "debug/DebugScene.h"
// Framework includes:
#include "ai/aichannel.h"
#include "grcore/debugdraw.h"
#include "fwmaths/Angle.h"
// RAGE includes:
#include "art/ARTRockstar.h"
#include "fragmentnm/manager.h"
#include "phbound/boundcapsule.h"
#include "physics/WorldProbe/worldprobe.h"
AI_OPTIMISATIONS()
// Initialise static member variables:
bool CNmDebug::ms_bDrawTransforms = false;
bool CNmDebug::ms_bDrawFeedbackHistory = false;
bool CNmDebug::ms_bDrawTeeterEdgeDetection = false;
//bool CNmDebug::ms_bDrawStumbleEnvironmentDetection = false;
bool CNmDebug::ms_bDrawBuoyancyEnvironmentDetection = false;
int CNmDebug::m_nNumBounds = -1;
Matrix34 CNmDebug::ms_currentMatrices[RAGDOLL_NUM_COMPONENTS];
CNmDebug::SFeedbackHistory CNmDebug::ms_feedbackMessageHistory;
CPed* CNmDebug::ms_pFocusPed = 0;
// RAG variables:
bool CNmDebug::ms_bFbMsgOnlyShowFocusPed = false;
bool CNmDebug::ms_bFbMsgShowSuccess = true;
bool CNmDebug::ms_bFbMsgShowFailure = true;
bool CNmDebug::ms_bFbMsgShowEvent = true;
bool CNmDebug::ms_bFbMsgShowStart = true;
bool CNmDebug::ms_bFbMsgShowFinish = true;
float CNmDebug::ms_fListHeaderX = 0.05f;
float CNmDebug::ms_fListHeaderY = 0.375f;
float CNmDebug::ms_fListElementHeight = 0.017f;
u32 CNmDebug::ms_nColourFadeStartTick = 0;
u32 CNmDebug::ms_nColourFadeEndTick = 40;
u32 CNmDebug::ms_nEndFadeColour = 100;
bool CNmDebug::ms_bDrawComponentMatrices = false;
int CNmDebug::ms_nSelectedRagdollComponent = 0;
float CNmDebug::ms_fEdgeTestAngle = 0.0f;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CNmDebug::RenderDebug()
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
{
StoreFocusPedAddress();
if(CTaskNMShot::sm_Tunables.m_bEnableDebugDraw)
{
RenderShotImpactCones();
}
if(ms_bDrawTransforms)
{
RenderIncomingTransforms();
}
if(ms_bDrawFeedbackHistory)
{
RenderFeedbackHistory();
}
if(ms_bDrawComponentMatrices)
{
RenderRagdollComponentMatrices();
}
if(ms_bDrawTeeterEdgeDetection)
{
static dev_bool bShowNewEdgeDetect = false;
if(bShowNewEdgeDetect)
RenderMoreEfficientEdgeDetectionResults();
else
RenderEdgeDetectionResults();
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CNmDebug::StoreFocusPedAddress()
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
{
// Null this pointer in case there is no focus ped.
ms_pFocusPed = 0;
CEntity* pFocusEntity = CDebugScene::FocusEntities_Get(0);
// Early out if no ped is selected.
if(!pFocusEntity) return;
if(!pFocusEntity->GetIsTypePed()) return;
// We must have a selected ped by this stage:
ms_pFocusPed = static_cast<CPed*>(pFocusEntity);
taskAssertf(ms_pFocusPed, "Ped should be selected but pointer to ped is NULL.");
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CNmDebug::RenderShotImpactCones()
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
{
// Visualise the front and back impact cones on a selected ped. These cones define the impact angles which will trigger
// a certain animPose reaction in the shot behaviour.
if(!ms_pFocusPed)
return;
// Start by defining the start point and various axes for the cone.
//Vector3 vConeOrigin = ms_pFocusPed->GetPosition();
Matrix34 compMatrix;
ms_pFocusPed->GetRagdollComponentMatrix(compMatrix, CTaskNMShot::sm_Tunables.m_eImpactConeRagdollComponent);
Vector3 vConeOrigin = compMatrix.d;
Vector3 vPedFrontNormal;
Matrix34 m = MAT34V_TO_MATRIX34(ms_pFocusPed->GetMatrix());
vPedFrontNormal.Scale(m.GetVector(2), -1.0f);
Vector3 vPedSideAxis = m.GetVector(1);
// Create an array of vectors which live on the surface of a cone around the normal coming out of
// the front of the ped.
WIN32_ONLY(const) static int nVectorsInCone = 20;
Vector3 *vCone = Alloca(Vector3,nVectorsInCone);
for(int i = 0; i < nVectorsInCone; ++i)
{
vCone[i] = Vector3(YAXIS);
vCone[i].RotateAboutAxis(CTaskNMShot::sm_Tunables.m_fImpactConeAngleFront * PI/180.0f, 'x');
vCone[i].RotateAboutAxis((float)i*(2*PI/nVectorsInCone), 'y');
// TODO RA: Hack to correct for discrepancy between animated and ragdoll inst matrices. Remove when this
// has been resolved.
if(ms_pFocusPed->GetRagdollState() == RAGDOLL_STATE_PHYS)
{
vCone[i].RotateAboutAxis(-PI/2.0f, 'x');
}
// Rotate the cone to match the ped's orientation.
vCone[i] = VEC3V_TO_VECTOR3(ms_pFocusPed->GetTransform().Transform3x3(VECTOR3_TO_VEC3V(vCone[i])));
}
for(int i = 0; i < nVectorsInCone; ++i)
{
// Draw the vectors.
Vector3 vEndOfConeVector;
vEndOfConeVector.Add(vConeOrigin, vCone[i]);
grcDebugDraw::Line(vConeOrigin, vEndOfConeVector, Color_blue);
// Draw a low-res "circle" at the end of the cone vectors.
if(i > 0)
{
Vector3 vStart, vEnd;
vStart.Add(vConeOrigin, vCone[i]);
vEnd.Add(vConeOrigin, vCone[i-1]);
grcDebugDraw::Line(vStart, vEnd, Color_blue);
}
else
{
Vector3 vStart, vEnd;
vStart.Add(vConeOrigin, vCone[nVectorsInCone-1]);
vEnd.Add(vConeOrigin, vCone[0]);
grcDebugDraw::Line(vStart, vEnd, Color_blue);
}
}
// Draw "front" normal for this component.
Vector3 vEndOfLine;
vEndOfLine.Add(vConeOrigin, vPedFrontNormal);
grcDebugDraw::Line(vConeOrigin, vEndOfLine, Color_blue);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CNmDebug::RenderIncomingTransforms()
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
{
// Show the position and orientation of the transformation matrices sent to NM.
static bool bSolid = true;
if(!ms_pFocusPed)
return;
// The bound geometries are defined by an archetype. Use the current world space matrices being sent
// to NM to transform the bounds and draw them.
aiAssertf(dynamic_cast<phBoundComposite*>(ms_pFocusPed->GetRagdollInst()->GetArchetype()->GetBound()),
"Ragdoll physics archetype should have composite bound");
phBoundComposite* pCompBound = static_cast<phBoundComposite*>(ms_pFocusPed->GetRagdollInst()->GetArchetype()->GetBound());
// Draw the collision bounds:
for(int i = 0; i < m_nNumBounds; ++i)
{
phBound* pBound = pCompBound->GetBound(i);
phBoundCapsule* pBndCap = NULL;
phBoundBox* pBndBox = NULL;
Vec3V v1, v2, vCentroidOffset, vCentroidOffsetWorldSpace;
ScalarV vCapsuleLength;
Vec3V vHalfCapsuleLengthY;
Mat34V centerMtx;
switch(pBound->GetType())
{
case phBound::CAPSULE:
pBndCap = static_cast<phBoundCapsule*>(pBound);
vCentroidOffset = pBndCap->GetCentroidOffset();
vCapsuleLength = pBndCap->GetLengthV();
vCentroidOffsetWorldSpace = Transform(RCC_MAT34V(ms_currentMatrices[i]), vCentroidOffset);
vHalfCapsuleLengthY = And(Vec3V(vCapsuleLength), Vec3V(V_MASKY));
vHalfCapsuleLengthY *= ScalarV(V_HALF);
v1 = vHalfCapsuleLengthY;
v2 = -vHalfCapsuleLengthY;
v1 = Add(v1, vCentroidOffset);
v2 = Add(v2, vCentroidOffset);
v1 = Transform(RCC_MAT34V(ms_currentMatrices[i]), v1);
v2 = Transform(RCC_MAT34V(ms_currentMatrices[i]), v2);
grcDebugDraw::Line(v1, v2, Color_purple);
grcDebugDraw::Sphere(v1, pBndCap->GetRadius(), Color_purple, bSolid);
grcDebugDraw::Sphere(v2, pBndCap->GetRadius(), Color_purple, bSolid);
break;
case phBound::BOX:
pBndBox = static_cast<phBoundBox*>(pBound);
v1 = pBndBox->GetBoundingBoxMin();
v2 = pBndBox->GetBoundingBoxMin(); // TODO: Should that be Max() instead? /MAK
centerMtx = RCC_MAT34V(ms_currentMatrices[i]);
centerMtx.SetCol3(Transform(RCC_MAT34V(ms_currentMatrices[i]), pBndBox->GetCentroidOffset()));
grcDebugDraw::BoxOriented(v1, v2, centerMtx, Color_purple, bSolid);
default:
grcDebugDraw::Sphere(ms_currentMatrices[i].d, 0.02f, Color_purple);
break;
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CNmDebug::SetComponentTMsFromSkeleton(const crSkeleton& skeleton)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
{
if(!ms_pFocusPed)
return;
// Does this ped have a Natural Motion agent?
int nAgentId = ms_pFocusPed->GetRagdollInst()->m_AgentId;
if(ms_bDrawTransforms && nAgentId != -1)
{
aiAssert(ms_pFocusPed->GetRagdollInst()->GetType());
int numChildren = ms_pFocusPed->GetRagdollInst()->GetTypePhysics()->GetNumChildren();
aiAssert(numChildren <= RAGDOLL_NUM_COMPONENTS);
m_nNumBounds = numChildren;
Matrix34* currentMatrices = FRAGNMASSETMGR->GetWorldCurrentMatrices(nAgentId);
// Go through each fragTypeChild/bound component ...
for(int childIndex = 0; childIndex < numChildren; ++childIndex)
{
fragTypeChild* child = ms_pFocusPed->GetRagdollInst()->GetTypePhysics()->GetAllChildren()[childIndex];
int boneIndex = ms_pFocusPed->GetRagdollInst()->GetType()->GetBoneIndexFromID(child->GetBoneID());
Assert(boneIndex >= 0);
const Matrix34* pattachment = ART::getComponentToBoneTransform(nAgentId, childIndex);
Assertf(pattachment, "Failed to find attachment matrix from getComponentToBoneTransform");
Matrix34 attachment;
if (pattachment)
{
attachment = *pattachment;
attachment.Inverse();
}
else
{
attachment.Identity();
}
Matrix34 boneMtx;
skeleton.GetGlobalMtx(boneIndex, RC_MAT34V(boneMtx));
currentMatrices[childIndex] = attachment;
currentMatrices[childIndex].Dot(boneMtx);
currentMatrices[childIndex].a.w = 0.0f;
currentMatrices[childIndex].b.w = 0.0f;
currentMatrices[childIndex].c.w = 0.0f;
currentMatrices[childIndex].d.w = 1.0f;
ms_currentMatrices[childIndex].Set(currentMatrices[childIndex]);
/*
// Store the necessary geometric components to draw the bounds later.
if(type->GetCompositeBounds()->GetBound(childIndex)->GetType() == phBound::CAPSULE)
{
phBoundCapsule* pBound = static_cast<phBoundCapsule*>(type->GetCompositeBounds()->GetBound(childIndex));
ms_vBoundStart[childIndex].Set(pBound->GetWorldPoint(0));
ms_vBoundEnd[childIndex].Set(pBound->GetWorldPoint(1));
ms_fBoundRadius[childIndex] = pBound->GetRadius();
}
else
{
ms_vBoundStart[childIndex].Set(currentMatrices[childIndex].d);
ms_vBoundEnd[childIndex].Set(currentMatrices[childIndex].d);
ms_fBoundRadius[childIndex] = 0.05f;
}
*/
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CNmDebug::AddBehaviourFeedbackMessage(const char* zMessage)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
{
// Add a new message to the history and, if the list is already at full capacity, remove the oldest message.
if(ms_feedbackMessageHistory.ms_nCount < NUM_FEEDBACK_HISTORY_ELEMENTS)
{
strcpy(ms_feedbackMessageHistory.ms_aMessages[ms_feedbackMessageHistory.ms_nCount], zMessage);
ms_feedbackMessageHistory.ms_nAgeOfMessage[ms_feedbackMessageHistory.ms_nCount] = 0;
ms_feedbackMessageHistory.ms_nCount++;
}
else
{
// Remove the oldest message by overwriting with the new.
strcpy(ms_feedbackMessageHistory.ms_aMessages[ms_feedbackMessageHistory.ms_nOldestIndex], zMessage);
ms_feedbackMessageHistory.ms_nAgeOfMessage[ms_feedbackMessageHistory.ms_nOldestIndex] = 0;
ms_feedbackMessageHistory.ms_nOldestIndex++;
ms_feedbackMessageHistory.ms_nNewestIndex++;
if(ms_feedbackMessageHistory.ms_nOldestIndex == NUM_FEEDBACK_HISTORY_ELEMENTS) ms_feedbackMessageHistory.ms_nOldestIndex = 0;
if(ms_feedbackMessageHistory.ms_nNewestIndex == NUM_FEEDBACK_HISTORY_ELEMENTS) ms_feedbackMessageHistory.ms_nNewestIndex = 0;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CNmDebug::RenderFeedbackHistory()
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
{
// Display a history of feedback strings sent from the NM system.
// Display the title message:
char sFlagDebugString[] = "NM FEEDBACK MESSAGES:";
grcDebugDraw::Text(Vector2(ms_fListHeaderX, ms_fListHeaderY), Color32(0xff, 0xff, 0xff, 0xff), sFlagDebugString, true);
int nMessageIndex = ms_feedbackMessageHistory.ms_nOldestIndex;
int nTextColour;
for(int i = 0; i < ms_feedbackMessageHistory.ms_nCount; ++i)
{
// Determine grey-scale colour based on age of message in history and fade rate.
if(ms_feedbackMessageHistory.ms_nAgeOfMessage[nMessageIndex] < ms_nColourFadeStartTick)
{
nTextColour = 0xff;
}
else if(ms_feedbackMessageHistory.ms_nAgeOfMessage[nMessageIndex] > ms_nColourFadeEndTick)
{
nTextColour = ms_nEndFadeColour;
}
else
{
u32 nTotalFadePeriod = ms_nColourFadeEndTick - ms_nColourFadeStartTick;
u32 nFadeTime = ms_feedbackMessageHistory.ms_nAgeOfMessage[nMessageIndex] - ms_nColourFadeStartTick;
float fFadeFactor = 1.0f - (float)nFadeTime/(float)nTotalFadePeriod;
nTextColour = ms_nEndFadeColour + (u32)(fFadeFactor*(float)(0xff-ms_nEndFadeColour));
}
grcDebugDraw::Text(Vector2(ms_fListHeaderX, ms_fListHeaderY + (i+1)*ms_fListElementHeight), Color32(nTextColour, nTextColour, nTextColour, 0xff),
ms_feedbackMessageHistory.ms_aMessages[nMessageIndex], false);
nMessageIndex++;
if(nMessageIndex == NUM_FEEDBACK_HISTORY_ELEMENTS) nMessageIndex = 0;
// Update the age of this message.
if(!fwTimer::IsGamePaused())
ms_feedbackMessageHistory.ms_nAgeOfMessage[nMessageIndex]++;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CNmDebug::RenderRagdollComponentMatrices()
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
{
if(!ms_pFocusPed)
return;
Matrix34 ragdollComponentMatrix;
if(ms_pFocusPed->GetRagdollComponentMatrix(ragdollComponentMatrix, ms_nSelectedRagdollComponent))
{
grcDebugDraw::Axis(ragdollComponentMatrix, 0.5f, true);
}
else
{
taskAssertf(false, "GetRagdollComponentMatrix() returned \"false\".");
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CNmDebug::RenderEdgeDetectionResults()
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
{
if(!ms_pFocusPed)
return;
bool bEdgeDefined = false;
Vector3 vEdgeLeft, vEdgeRight, vEdgeMiddle;
Vector3 vPedPos = VEC3V_TO_VECTOR3(ms_pFocusPed->GetTransform().GetPosition());
// This is a normal vector in the direction the ped is facing (eventually to be the direction of motion).
Vector3 vPedDirNormal(rage::Sinf(ms_fEdgeTestAngle), rage::Cosf(ms_fEdgeTestAngle), 0.0f); //ms_pFocusPed->GetB();
// Rotate the direction vector above to get the directions for the left and right extrema probes.
/*
static dev_float sfMaxProbeAngle = 30.0f * PI/180.0f;
Vector3 vLeftProbeDir = vPedDirNormal; vLeftProbeDir.RotateZ(sfMaxProbeAngle)
Vector3 vRightProbeDir = vPedDirNormal; vRightProbeDir.RotateZ(-sfMaxProbeAngle);
*/
const float fPelvisToGround = vPedPos.z - ms_pFocusPed->GetGroundPos().z;
Vector3 vProbe0Start, vProbe0End;
Vector3 vProbe1Start, vProbe1End;
Vector3 vProbe2Start, vProbe2End;
static dev_float sfHorProbeLength = 3.0f;
static dev_float sfCriticalDropHeight = 2.0f; // Define the critical height difference which triggers a teeter reaction.
// Define start and end points for horizontal probe at hip height:
vProbe0Start = vPedPos;
vProbe0End.Add(vProbe0Start, vPedDirNormal*sfHorProbeLength);
// Define start and end points for horizontal probe at ankle height:
vProbe1Start = vPedPos;
static dev_float sfDistBetweenHipAndAnkleProbes = 1.0f;
vProbe1Start.Subtract(Vector3(ZAXIS)*sfDistBetweenHipAndAnkleProbes);
vProbe1End.Add(vProbe1Start, vPedDirNormal*sfHorProbeLength);
// Define start and end points for vertical probe at end of horizontal probes.
vProbe2Start = vProbe0End;
vProbe2End = vProbe2Start; vProbe2End.z -= sfCriticalDropHeight;
// Probe along the horizontal lines defined above looking for scenery.
WorldProbe::CShapeTestProbeDesc probe0;
WorldProbe::CShapeTestFixedResults<> probe0Results;
probe0.SetStartAndEnd(vProbe0Start, vProbe0End);
probe0.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES);
probe0.SetResultsStructure(&probe0Results);
WorldProbe::GetShapeTestManager()->SubmitTest(probe0, WorldProbe::PERFORM_SYNCHRONOUS_TEST);
//
WorldProbe::CShapeTestProbeDesc probe1;
WorldProbe::CShapeTestFixedResults<> probe1Results;
probe1.SetStartAndEnd(vProbe1Start, vProbe1End);
probe1.SetResultsStructure(&probe1Results);
probe1.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES);
WorldProbe::GetShapeTestManager()->SubmitTest(probe1, WorldProbe::PERFORM_SYNCHRONOUS_TEST);
// Do a vertical test at the end of the horizontal tests to see if we are near a significant drop.
if(!probe0Results[0].GetHitDetected()) // Is it safe to test vertically down from here?
{
WorldProbe::CShapeTestProbeDesc probe2;
WorldProbe::CShapeTestFixedResults<> probe2Results;
probe2.SetStartAndEnd(vProbe2Start, vProbe2End);
probe2.SetResultsStructure(&probe2Results);
probe2.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES);
WorldProbe::GetShapeTestManager()->SubmitTest(probe2, WorldProbe::PERFORM_SYNCHRONOUS_TEST);
// If we found a drop, fire rays back to detect the edge.
if(!probe2Results[0].GetHitDetected())
{
Vector3 vProbeFindEdgeStart, vProbeFindEdgeEndL, vProbeFindEdgeEndR;
// // Should be along the same line as the previous horizontal probes but just under the
// // level of the ground where the ped is standing.
vProbeFindEdgeStart = vProbe0End; vProbeFindEdgeStart.z = vPedPos.z - fPelvisToGround;
vProbeFindEdgeEndL = vProbe0Start; vProbeFindEdgeEndL.z -= 1.0f;
vProbeFindEdgeEndR = vProbe0Start; vProbeFindEdgeEndR.z -= 1.0f;
static dev_float sfCapsuleRadius = 1.0f;
// Use two line tests to define the end points along the edge which we will pass to NM.
Vector3 vEdgeLeftProbeStart, vEdgeLeftProbeEnd;
Vector3 vEdgeRightProbeStart, vEdgeRightProbeEnd;
Vector3 vEdgeMiddleProbeStart, vEdgeMiddleProbeEnd;
vEdgeLeftProbeStart = vProbeFindEdgeStart; vEdgeLeftProbeStart.z -= sfCapsuleRadius;
vEdgeRightProbeStart = vProbeFindEdgeStart; vEdgeRightProbeStart.z -= sfCapsuleRadius;
vEdgeMiddleProbeStart = vProbeFindEdgeStart; vEdgeMiddleProbeStart.z -= sfCapsuleRadius;
Vector3 vOrthoTestLine = vPedDirNormal; vOrthoTestLine.RotateZ(PI/2.0f);
vEdgeLeftProbeEnd.Add(vProbeFindEdgeEndL, vOrthoTestLine*sfCapsuleRadius);
vEdgeRightProbeEnd.Add(vProbeFindEdgeEndR, vOrthoTestLine*-sfCapsuleRadius);
vEdgeMiddleProbeEnd = ms_pFocusPed->GetGroundPos();
// Define the roughly horizontal probes to define the edge, look for corners, etc.
WorldProbe::CShapeTestProbeDesc probeEdgeLeft;
WorldProbe::CShapeTestFixedResults<> probeEdgeLeftResults;
probeEdgeLeft.SetStartAndEnd(vEdgeLeftProbeStart, vEdgeLeftProbeEnd);
probeEdgeLeft.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES);
probeEdgeLeft.SetResultsStructure(&probeEdgeLeftResults);
WorldProbe::GetShapeTestManager()->SubmitTest(probeEdgeLeft, WorldProbe::PERFORM_SYNCHRONOUS_TEST);
//
WorldProbe::CShapeTestProbeDesc probeEdgeRight;
WorldProbe::CShapeTestFixedResults<> probeEdgeRightResults;
probeEdgeRight.SetStartAndEnd(vEdgeRightProbeStart, vEdgeRightProbeEnd);
probeEdgeRight.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES);
probeEdgeRight.SetResultsStructure(&probeEdgeRightResults);
WorldProbe::GetShapeTestManager()->SubmitTest(probeEdgeRight, WorldProbe::PERFORM_SYNCHRONOUS_TEST);
//
WorldProbe::CShapeTestProbeDesc probeEdgeMiddle;
WorldProbe::CShapeTestFixedResults<> probeEdgeMiddleResults;
probeEdgeMiddle.SetStartAndEnd(vEdgeMiddleProbeStart, vEdgeMiddleProbeEnd);
probeEdgeMiddle.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES);
probeEdgeMiddle.SetResultsStructure(&probeEdgeMiddleResults);
WorldProbe::GetShapeTestManager()->SubmitTest(probeEdgeMiddle, WorldProbe::PERFORM_SYNCHRONOUS_TEST);
// Visualise probe and collision results.
grcDebugDraw::Line(vEdgeLeftProbeStart, vEdgeLeftProbeEnd, probeEdgeLeftResults[0].GetHitDetected() ? Color_red : Color_green);
grcDebugDraw::Line(vEdgeRightProbeStart, vEdgeRightProbeEnd, probeEdgeRightResults[0].GetHitDetected() ? Color_red : Color_blue);
grcDebugDraw::Line(vEdgeMiddleProbeStart, vEdgeMiddleProbeEnd, probeEdgeMiddleResults[0].GetHitDetected() ? Color_red : Color_yellow);
// And (barring a test for being at a corner) we should have the end-points of a line along the edge.
//float fLeftHeading = 0.0f, fRightHeading = 0.0f;
if(probeEdgeLeftResults[0].GetHitDetected())
{
vEdgeLeft = probeEdgeLeftResults[0].GetHitPosition(); vEdgeLeft.z = ms_pFocusPed->GetGroundPos().z;
grcDebugDraw::Sphere(vEdgeLeft, 0.05f, Color_green, true);
// Work out the heading of the line between this point and the intersection of the capsule test.
/*Vector3 vLeftHeading = capsuleIsect.GetPosition();
vLeftHeading.Subtract(vEdgeLeft);
fLeftHeading = atan(vLeftHeading.y/vLeftHeading.x);
if(fLeftHeading > PI) fLeftHeading -= PI;*/
}
if(probeEdgeRightResults[0].GetHitDetected())
{
vEdgeRight = probeEdgeRightResults[0].GetHitPosition(); vEdgeRight.z = ms_pFocusPed->GetGroundPos().z;
grcDebugDraw::Sphere(vEdgeRight, 0.05f, Color_blue, true);
// Work out the heading of the line between this point and the intersection of the capsule test.
/*Vector3 vRightHeading = capsuleIsect.GetPosition();
vRightHeading.Subtract(vEdgeRight);
fRightHeading = atan(vRightHeading.y/vRightHeading.x);
if(fRightHeading > PI) fRightHeading -= PI;*/
}
if(probeEdgeMiddleResults[0].GetHitDetected())
{
vEdgeMiddle = probeEdgeMiddleResults[0].GetHitPosition(); vEdgeMiddle.z = ms_pFocusPed->GetGroundPos().z;
grcDebugDraw::Sphere(vEdgeMiddle, 0.05f, Color_red, true);
}
if( probeEdgeLeftResults[0].GetHitDetected() && probeEdgeRightResults[0].GetHitDetected())
{
bEdgeDefined = true;
}
else if( probeEdgeLeftResults[0].GetHitDetected())
{
vEdgeRight = vEdgeMiddle;
bEdgeDefined = true;
}
else if(probeEdgeRightResults[0].GetHitDetected())
{
vEdgeLeft = vEdgeMiddle;
bEdgeDefined = true;
}
else
{
bEdgeDefined = false;
}
}
// Visualise probe and collision results.
grcDebugDraw::Line(vProbe2Start, vProbe2End, probe2Results[0].GetHitDetected() ? Color_red : Color_yellow);
// Visualise any collisions with scenery.
grcDebugDraw::Line(vProbe0Start, vProbe0End, probe0Results[0].GetHitDetected() ? Color_red : Color_yellow);
grcDebugDraw::Line(vProbe1Start, vProbe1End, probe1Results[0].GetHitDetected() ? Color_red : Color_yellow);
if(probe0Results[0].GetHitDetected())
{
grcDebugDraw::Sphere(probe0Results[0].GetHitPosition(), 0.05f, Color_red, true);
}
if(probe1Results[0].GetHitDetected())
{
grcDebugDraw::Sphere(probe1Results[0].GetHitPosition(), 0.05f, Color_red, true);
}
// Visualise end result if edge detected.
if(bEdgeDefined)
{
Vector3 v1(vEdgeLeft.x, vEdgeLeft.y, vEdgeLeft.z+0.2f);
Vector3 v2(vEdgeRight.x, vEdgeRight.y, vEdgeRight.z+0.2f);
grcDebugDraw::Line(v1, v2, Color_white);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CNmDebug::RenderMoreEfficientEdgeDetectionResults()
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
{
if(!ms_pFocusPed)
return;
bool bEdgeDefined = false;
static dev_float sfEdgeProbeSeparationAngle = 60.0f; // Angle in degrees between the left and right probes.
static dev_float sfCriticalDepth = 1.5f; // Critical height difference from pelvis to ground to consider a ledge.
static dev_float sfEdgeHorDistThreshold = 0.5f; // The horizontal distance from an edge at which the drop is first detected.
const float fPelvisToGround = 0.5f;
// Compute quantities derived from the above parameters.
float fTheta = tan(sfEdgeHorDistThreshold/fPelvisToGround);
float fProbeHorDist = sfCriticalDepth*atan(fTheta);
// TEMP!!!!!
static Vector3 s_vLeftEdgePoint(Vector3::ZeroType);
static Vector3 s_vMiddleEdgePoint(Vector3::ZeroType);
static Vector3 s_vRightEdgePoint(Vector3::ZeroType);
static Vector3 s_vEdgeLineLeft(Vector3::ZeroType);
static Vector3 s_vEdgeLineRight(Vector3::ZeroType);
////////////
Vector3 vPedPos = VEC3V_TO_VECTOR3(ms_pFocusPed->GetTransform().GetPosition());
// This is a normal vector in the direction the ped is facing (eventually to be the direction of motion).
//Vector3 vPedDirNormal(rage::Sinf(ms_fEdgeTestAngle), rage::Cosf(ms_fEdgeTestAngle), 0.0f);
Vector3 vPedDirNormal = VEC3V_TO_VECTOR3(ms_pFocusPed->GetTransform().GetB());
// Define the start and end points of the line tests to look for a large drop.
Vector3 vProbeStart = vPedPos;
//
Vector3 vProbeEndMiddle;
vProbeEndMiddle.Add(vPedDirNormal*fProbeHorDist, vProbeStart);
vProbeEndMiddle.z -= sfCriticalDepth;
//
Vector3 vProbeEndLeft, vProbeLeftDirNormal;
vProbeLeftDirNormal = vPedDirNormal;
vProbeLeftDirNormal.RotateZ(0.5f*sfEdgeProbeSeparationAngle*PI/180.0f);
vProbeEndLeft.Add(vProbeLeftDirNormal*fProbeHorDist, vProbeStart);
vProbeEndLeft.z -= sfCriticalDepth;
//
Vector3 vProbeEndRight, vProbeRightDirNormal;
vProbeRightDirNormal = vPedDirNormal;
vProbeRightDirNormal.RotateZ(-0.5f*sfEdgeProbeSeparationAngle*PI/180.0f);
vProbeEndRight.Add(vProbeRightDirNormal*fProbeHorDist, vProbeStart);
vProbeEndRight.z -= sfCriticalDepth;
//
grcDebugDraw::Sphere(vProbeStart, 0.03f, Color_blue, true);
grcDebugDraw::Sphere(vProbeEndLeft, 0.03f, Color_blue, true);
grcDebugDraw::Sphere(vProbeEndRight, 0.03f, Color_blue, true);
grcDebugDraw::Sphere(vProbeEndMiddle, 0.03f, Color_blue, true);
grcDebugDraw::Line(vProbeStart, vProbeEndLeft, Color_yellow);
grcDebugDraw::Line(vProbeStart, vProbeEndRight, Color_yellow);
grcDebugDraw::Line(vProbeStart, vProbeEndMiddle, Color_yellow);
// Probe along the horizontal lines defined above looking for scenery.
WorldProbe::CShapeTestProbeDesc probeLeft;
WorldProbe::CShapeTestFixedResults<> probeResultLeft;
probeLeft.SetResultsStructure(&probeResultLeft);
probeLeft.SetStartAndEnd(vProbeStart, vProbeEndLeft);
probeLeft.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES);
//
WorldProbe::CShapeTestProbeDesc probeRight;
WorldProbe::CShapeTestFixedResults<> probeResultRight;
probeRight.SetResultsStructure(&probeResultRight);
probeRight.SetStartAndEnd(vProbeStart, vProbeEndRight);
probeRight.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES);
//
WorldProbe::CShapeTestProbeDesc probeMiddle;
WorldProbe::CShapeTestFixedResults<> probeResultMiddle;
probeMiddle.SetResultsStructure(&probeResultMiddle);
probeMiddle.SetStartAndEnd(vProbeStart, vProbeEndMiddle);
probeMiddle.SetIncludeFlags(ArchetypeFlags::GTA_ALL_MAP_TYPES);
//
WorldProbe::GetShapeTestManager()->SubmitTest(probeLeft);
WorldProbe::GetShapeTestManager()->SubmitTest(probeRight);
WorldProbe::GetShapeTestManager()->SubmitTest(probeMiddle);
static bool bLeftFoundDrop = false;
static bool bRightFoundDrop = false;
static bool bMiddleFoundDrop = false;
static int nFirstDropDetection = -1;
if(probeResultLeft[0].GetHitDetected())
{
grcDebugDraw::Sphere(probeResultLeft[0].GetHitPosition(), 0.05f, Color_red, true);
s_vLeftEdgePoint = probeResultLeft[0].GetHitPosition();
bLeftFoundDrop = false;
}
else
{
// "No hit" means we are near a drop.
bLeftFoundDrop = true;
// The last two probes to find the edge define it.
if(!bRightFoundDrop && !bMiddleFoundDrop)
{
nFirstDropDetection = 0;
}
grcDebugDraw::Sphere(s_vLeftEdgePoint, 0.05f, Color_purple, true);
}
if(probeResultRight[0].GetHitDetected())
{
grcDebugDraw::Sphere(probeResultRight[0].GetHitPosition(), 0.05f, Color_red, true);
s_vRightEdgePoint = probeResultRight[0].GetHitPosition();
bRightFoundDrop = false;
}
else
{
bRightFoundDrop = true;
if(!bLeftFoundDrop && !bMiddleFoundDrop)
{
nFirstDropDetection = 1;
}
grcDebugDraw::Sphere(s_vRightEdgePoint, 0.05f, Color_grey, true);
}
if(probeResultMiddle[0].GetHitDetected())
{
bMiddleFoundDrop = false;
grcDebugDraw::Sphere(probeResultMiddle[0].GetHitPosition(), 0.05f, Color_red, true);
s_vMiddleEdgePoint = probeResultMiddle[0].GetHitPosition();
}
else
{
bMiddleFoundDrop = true;
if(!bLeftFoundDrop && !bRightFoundDrop)
{
nFirstDropDetection = 2;
}
grcDebugDraw::Sphere(s_vMiddleEdgePoint, 0.05f, Color_green, true);
}
if(nFirstDropDetection == 0 && bRightFoundDrop && bMiddleFoundDrop)
{
s_vEdgeLineLeft = s_vMiddleEdgePoint;
s_vEdgeLineRight = s_vRightEdgePoint;
bEdgeDefined = true;
}
else if(nFirstDropDetection == 1 && bLeftFoundDrop && bMiddleFoundDrop)
{
s_vEdgeLineLeft = s_vLeftEdgePoint;
s_vEdgeLineRight = s_vMiddleEdgePoint;
bEdgeDefined = true;
}
else if(nFirstDropDetection == 2 && bLeftFoundDrop && bRightFoundDrop)
{
s_vEdgeLineLeft = s_vLeftEdgePoint;
s_vEdgeLineRight = s_vRightEdgePoint;
bEdgeDefined = true;
}
else
{
bEdgeDefined = false;
}
if(bEdgeDefined)
{
grcDebugDraw::Line(s_vEdgeLineLeft, s_vEdgeLineRight, Color_white);
}
}
#endif // __BANK