Files
GTASource/game/Peds/PedCapsule.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

661 lines
23 KiB
C++

// FILE : PedCapsule.cpp
// PURPOSE : Load in capsule information for varying types of peds
// CREATED : 10-11-2010
// class header
#include "PedCapsule.h"
// Framework headers
// Game headers
#include "modelinfo/modelinfo_channel.h"
#include "physics/gtaMaterialManager.h"
#include "modelinfo/PedModelInfo.h"
#include "physics/gtaArchetype.h"
// rage headers
#include "fwmaths/angle.h"
#include "phbound/boundcapsule.h"
#include "phbound/boundcomposite.h"
#include "phbound/boundcurvedgeom.h"
#include "phbound/boundcylinder.h"
#if USE_SPHERES
#include "phbound/boundsphere.h"
#else
#include "phbound/boundbox.h"
#endif
#include "bank/bank.h"
#include "parser/manager.h"
// Parser headers
#include "PedCapsule_parser.h"
AI_OPTIMISATIONS()
//////////////////////////////////////////////////////////////////////////
// CPedCapsuleInfoManager
//////////////////////////////////////////////////////////////////////////
// Static Manager object
CPedCapsuleInfoManager CPedCapsuleInfoManager::m_CapsuleManagerInstance;
void CPedCapsuleInfoManager::Init(const char* pFileName)
{
Load(pFileName);
}
void CPedCapsuleInfoManager::Shutdown()
{
// Delete
Reset();
}
void CPedCapsuleInfoManager::Load(const char* pFileName)
{
if(Verifyf(!m_CapsuleManagerInstance.m_aPedCapsule.GetCount(), "Capsule Info has already been loaded"))
{
// Reset/Delete the existing data before loading
Reset();
// Load
PARSER.LoadObject(pFileName, "xml", m_CapsuleManagerInstance);
}
}
void CPedCapsuleInfoManager::Append(const char* pFileName)
{
CPedCapsuleInfoManager tempInst;
PARSER.LoadObject(pFileName, "xml", tempInst);
for(int i = 0; i < tempInst.m_aPedCapsule.GetCount(); i++)
{
m_aPedCapsule.PushAndGrow(tempInst.m_aPedCapsule[i]);
}
}
void CPedCapsuleInfoManager::Reset()
{
for (int i=0 ; i<m_CapsuleManagerInstance.m_aPedCapsule.GetCount() ; i++)
{
delete m_CapsuleManagerInstance.m_aPedCapsule[i];
}
m_CapsuleManagerInstance.m_aPedCapsule.Reset();
}
#if USE_GEOMETRY_CURVED
phBound* CBipedCapsuleInfo::InitPhysCreateCurvedGeomentryBound(float fRadius, float fHeight, float fMargin, phBoundCurvedGeometry* pCurvedBound)
{
int numVerts = 4;
int numMaterials = 1;
int numPolys = 0;
int numCurvedEdges = 4;
int numCurvedFaces = 0;
// Create and initialize a curved geometry bound if we weren't given one
if(pCurvedBound == NULL)
{
pCurvedBound = rage_new phBoundCurvedGeometry;
pCurvedBound->Init(numVerts,numMaterials,numPolys,numCurvedEdges,numCurvedFaces);
}
else
{
modelinfoAssertf(pCurvedBound->GetNumVertices() == numVerts, "Invalid bound given");
modelinfoAssertf(pCurvedBound->GetNumMaterials() == numMaterials,"Invalid bound given");
modelinfoAssertf(pCurvedBound->GetNumPolygons() == numPolys,"Invalid bound given");
modelinfoAssertf(pCurvedBound->GetNumCurvedEdges() == numCurvedEdges,"Invalid bound given");
modelinfoAssertf(pCurvedBound->GetNumCurvedFaces() == numCurvedFaces,"Invalid bound given");
}
Vector3 bbMin(-fRadius - fMargin, -fRadius - fMargin, -0.5f*fHeight - fMargin);
Vector3 bbMax( fRadius + fMargin, fRadius + fMargin, 0.5f*fHeight + fMargin);
#if COMPRESSED_VERTEX_METHOD > 0
// If the verts are compressed, the quantization needs to be initialized before we can set verts
pCurvedBound->InitQuantization(RCC_VEC3V(bbMin), RCC_VEC3V(bbMax));
#else
pCurvedBound->SetBoundingBox(RCC_VEC3V(bbMin), RCC_VEC3V(bbMax));
#endif
// Set four vertices.
pCurvedBound->SetMargin(fMargin);
pCurvedBound->SetVertex(0,Vec3V( fRadius, 0.0f, -0.5f*fHeight));
pCurvedBound->SetVertex(1,Vec3V(-fRadius, 0.0f, -0.5f*fHeight));
pCurvedBound->SetVertex(2,Vec3V( fRadius, 0.0f, 0.5f*fHeight));
pCurvedBound->SetVertex(3,Vec3V(-fRadius, 0.0f, 0.5f*fHeight));
// Set two half-circular curved edges for the bottom.
pCurvedBound->SetCurvedEdge(0, 1, 0, fRadius, -ZAXIS, YAXIS);
pCurvedBound->SetCurvedEdge(1, 0, 1, fRadius, -ZAXIS, -YAXIS);
// Set two half-circular curved edges for the top.
pCurvedBound->SetCurvedEdge(2, 2, 3, fRadius, ZAXIS, -YAXIS);
pCurvedBound->SetCurvedEdge(3, 3, 2, fRadius, ZAXIS, YAXIS);
//pCurvedBound->CalculateGeomExtents();
pCurvedBound->SetCentroidOffset(Vec3V(V_ZERO));
pCurvedBound->SetCGOffset(Vec3V(V_ZERO));
pCurvedBound->SetRadiusAroundCentroid(Dist(pCurvedBound->GetBoundingBoxMax(), pCurvedBound->GetCentroidOffset()));
//pCurvedBound->CalculateGeomExtents();
pCurvedBound->SetMaterial(PGTAMATERIALMGR->g_idPhysPedCapsule);
return pCurvedBound;
}
#endif // USE_GEOMETRY_CURVED
phBoundComposite* CBipedCapsuleInfo::GeneratePedBound(CPedModelInfo &/*rPedModelInfo*/, phBoundComposite* BANK_ONLY(pCompositeBound)) const
{
// create a bound consisting of a capsule
// (needs to be a composite because capsules are orientated around local y-axis)
int nPedBounds = 1;
bool bPedInternalMotion = false;
Assertf((GetHeadHeight()-GetCapsuleZOffset()) > 2.0f*GetRadius(),"Invalid ped bound dimensions for '%s'. The difference between the top and bottom of the capsule cannot be less than double the radius Head %f Cap Offset %f Radius %f.",GetName(), GetHeadHeight(), GetCapsuleZOffset(), GetRadius());
const float fMainCapsuleHeight = GetHeadHeight();
#if PED_USE_CAPSULE_PROBES
nPedBounds++;
bPedInternalMotion = true;
#endif
#if PED_USE_EXTRA_BOUND
nPedBounds++;
#endif
#if PLAYER_USE_LOWER_LEG_BOUND
if(GetUseLowerLegBound())
nPedBounds++;
#endif
#if PLAYER_USE_FPS_HEAD_BOUND
if(GetUseFPSHeadBlocker())
nPedBounds++;
#endif
#if PED_CURVEDGEOM_FOR_MAIN_CAPSULE
// We need an extra capsule bound for shapetests to work if a curved geom bound is used
nPedBounds++;
#endif
#if __BANK
phBoundComposite* pComposite;
if (pCompositeBound)
{
if (pCompositeBound->GetNumBounds() != nPedBounds)
{
Errorf("Tuning requires change of bound count - not supported at runtime. Please save file out and reload the game to see!");
return NULL;
}
//We've been handed a bound to rebuild. Delete it's current contents
pCompositeBound->Shutdown();
pComposite = pCompositeBound;
}
else
{
pComposite = rage_new phBoundComposite;
}
#else
phBoundComposite* pComposite = rage_new phBoundComposite;
#endif
pComposite->Init(nPedBounds, bPedInternalMotion);
#if PED_CURVEDGEOM_FOR_MAIN_CAPSULE
static dev_float sfPedKneeHeight = GetCapsuleZOffset();
float fCurvedGeomRadius = GetRadius() - PED_CURVEDGEOM_FOR_MAIN_MARGIN;
float fCurvedGeomLength = fMainCapsuleHeight - sfPedKneeHeight - 2.0f*PED_CURVEDGEOM_FOR_MAIN_MARGIN;
phBound* pMainCurvedGeomBound = InitPhysCreateCurvedGeomentryBound(fCurvedGeomRadius, fCurvedGeomLength, PED_CURVEDGEOM_FOR_MAIN_MARGIN, NULL);
pComposite->SetBound(BOUND_MAIN_CAPSULE, pMainCurvedGeomBound);
// remember to release AFTER ownership has been passed to the composite
pMainCurvedGeomBound->Release();
Matrix34 capsuleMat;
capsuleMat.Identity();
capsuleMat.d.z = 0.5f * (GetCapsuleZOffset() + fMainCapsuleHeight);
pComposite->SetCurrentMatrix(BOUND_MAIN_CAPSULE, capsuleMat);
pComposite->SetLastMatrix(BOUND_MAIN_CAPSULE, capsuleMat);
// Keep the old style capsule bound so shape tests still work, since most tests don't work against curved geometry bounds
#if PED_USE_SHAPETEST_CAPSULE > 0
phBoundCapsule *pShapetestCapsule = rage_new phBoundCapsule();
pShapetestCapsule->SetCapsuleSize(GetRadius(), fMainCapsuleHeight - GetCapsuleZOffset() - 2.0f*GetRadius());
pShapetestCapsule->SetMaterial(PGTAMATERIALMGR->g_idPhysPedCapsule);
pComposite->SetBound(PED_USE_SHAPETEST_CAPSULE, pShapetestCapsule);
pShapetestCapsule->Release();
capsuleMat.Identity();
capsuleMat.d.z = 0.5f * (fMainCapsuleHeight + GetCapsuleZOffset());
capsuleMat.RotateX(HALF_PI);
pComposite->SetCurrentMatrix(PED_USE_SHAPETEST_CAPSULE, capsuleMat);
pComposite->SetLastMatrix(PED_USE_SHAPETEST_CAPSULE, capsuleMat);
// We don't want this capsule to collide with stuff except shapetests
pComposite->AllocateTypeAndIncludeFlags();
pComposite->SetIncludeFlags(PED_USE_SHAPETEST_CAPSULE,ArchetypeFlags::GTA_ALL_SHAPETEST_TYPES);
pComposite->SetTypeFlags(PED_USE_SHAPETEST_CAPSULE,ArchetypeFlags::GTA_PED_TYPE);
#endif // PED_USE_SHAPETEST_CAPSULE
#else
// need to setup capsule AFTER we've added it into the composite bound because the phBoundComposite::Init() function does a memset(0) on all structures!
phBoundCapsule *pMainCapsule = rage_new phBoundCapsule();
pMainCapsule->SetCapsuleSize(GetRadius(), fMainCapsuleHeight - GetCapsuleZOffset() - 2.0f*GetRadius());
pMainCapsule->SetMaterial(PGTAMATERIALMGR->g_idPhysPedCapsule);
pComposite->SetBound(BOUND_MAIN_CAPSULE, pMainCapsule);
pMainCapsule->Release();
// The old backface culler doesn't work well with our PreComputeImpacts side shaving code. The only reason
// the new backface culler isn't used by default is vehicles heavily depend on the old one.
pMainCapsule->EnableUseNewBackFaceCull();
Matrix34 capsuleMat;
capsuleMat.Identity();
capsuleMat.d.z = 0.5f * (fMainCapsuleHeight + GetCapsuleZOffset());
capsuleMat.RotateX(HALF_PI);
pComposite->SetCurrentMatrix(BOUND_MAIN_CAPSULE, RCC_MAT34V(capsuleMat));
pComposite->SetLastMatrix(BOUND_MAIN_CAPSULE, RCC_MAT34V(capsuleMat));
if (!pComposite->GetTypeAndIncludeFlags())
{
pComposite->AllocateTypeAndIncludeFlags();
}
// The main capsule shouldn't collide with stair slopes as they are allowed to have exposed edges which gives unrealistic contact normals
pComposite->SetIncludeFlags(BOUND_MAIN_CAPSULE, pComposite->GetIncludeFlags(BOUND_MAIN_CAPSULE) & ~ArchetypeFlags::GTA_STAIR_SLOPE_TYPE);
#endif
u8 nPedBoundIndex = 1;
#if PED_USE_CAPSULE_PROBES
const float fPedProbeTop = GetKneeHeight() + 2.0f * GetProbeRadius();
const float fPedProbeBottom = (-GetGroundToRootOffset()) - GetGroundOffsetExtend();
// second capsule to replace the ped probes
phBoundCapsule *pStandCapsule = rage_new phBoundCapsule();
float fStandCapsuleLength = fPedProbeTop - fPedProbeBottom - 2.0f*GetProbeRadius();
pStandCapsule->SetCapsuleSize(GetProbeRadius(), fStandCapsuleLength);
pStandCapsule->SetMaterial(PGTAMATERIALMGR->g_idPhysPedCapsule);
pComposite->SetBound(BOUND_CAPSULE_PROBE, pStandCapsule);
pStandCapsule->Release();
capsuleMat.Identity();
capsuleMat.d.z = 0.5f * (fPedProbeTop + fPedProbeBottom);
capsuleMat.d.y += GetProbeYOffset();
capsuleMat.RotateX(HALF_PI);
pComposite->SetCurrentMatrix(BOUND_CAPSULE_PROBE, RCC_MAT34V(capsuleMat));
capsuleMat.d.z = 0.5f * (fPedProbeTop + fPedProbeBottom) + (GetHeadHeight() - fPedProbeTop);;
pComposite->SetLastMatrix(BOUND_CAPSULE_PROBE, RCC_MAT34V(capsuleMat));
nPedBoundIndex++;
#endif //PED_USE_CAPSULE_PROBES
#if PED_USE_EXTRA_BOUND
phBoundCapsule* pExtraBound = rage_new phBoundCapsule();
// Initialize the bound using the same size and transform as the main capsule as we don't want it affecting
// the overall extents of the composite bound
pExtraBound->SetCapsuleSize(GetRadius(), fMainCapsuleHeight - GetCapsuleZOffset() - 2.0f*GetRadius());
pExtraBound->SetMaterial(PGTAMATERIALMGR->g_idPhysPedCapsule);
pComposite->SetBound(nPedBoundIndex, pExtraBound);
pExtraBound->Release();
capsuleMat.Identity();
capsuleMat.d.z = 0.5f * (fMainCapsuleHeight + GetCapsuleZOffset());
capsuleMat.RotateX(HALF_PI);
pComposite->SetCurrentMatrix(nPedBoundIndex, RCC_MAT34V(capsuleMat));
pComposite->SetLastMatrix(nPedBoundIndex, RCC_MAT34V(capsuleMat));
if (!pComposite->GetTypeAndIncludeFlags())
{
pComposite->AllocateTypeAndIncludeFlags();
}
//! Initially disabled. We enable it as needed.
pComposite->SetIncludeFlags(nPedBoundIndex, 0);
pComposite->SetTypeFlags(nPedBoundIndex, ArchetypeFlags::GTA_PED_TYPE);
nPedBoundIndex++;
#endif //PED_USE_EXTRA_BOUND
#if PLAYER_USE_LOWER_LEG_BOUND
if(GetUseLowerLegBound())
{
#if !PLAYER_USE_CAPSULE_LEG_BOUND
CompileTimeAssert(USE_GEOMETRY_CURVED);
float fRadius = GetRadius() - PLAYER_LEGS_MARGIN;
float fHeight = PLAYER_LEGS_TOP - PLAYER_LEGS_BOTTOM - 2.0f * PLAYER_LEGS_MARGIN;
phBound* pLegsBound = InitPhysCreateCurvedGeomentryBound(fRadius, fHeight, PLAYER_LEGS_MARGIN, NULL);
#else // !PLAYER_USE_CAPSULE_LEG_BOUND
// Create a cylinder bound that is equivalent to the curved geometry bound.
float fRadius = GetRadius();
float fHeight = PLAYER_LEGS_TOP - PLAYER_LEGS_BOTTOM;
phBoundCylinder * pLegsBound = rage_new phBoundCylinder;
pLegsBound->SetMargin(PLAYER_LEGS_MARGIN);
pLegsBound->SetCylinderRadiusAndHalfHeight(ScalarV(fRadius), ScalarV(.5f * fHeight));
#endif // !PLAYER_USE_CAPSULE_LEG_BOUND
pComposite->SetBound(nPedBoundIndex, pLegsBound);
// remember to release AFTER ownership has been passed to the composite
pLegsBound->Release();
#if !PLAYER_USE_CAPSULE_LEG_BOUND
capsuleMat.Identity();
#else // !PLAYER_USE_CAPSULE_LEG_BOUND
// The cylinder axis is along the y direction.
// The player up axis is along the z direction, y forward, x right.
// Set the cylinder orientation so that its axis is directed along the player's up axis.
capsuleMat.Set(Vector3(1,0,0),Vector3(0,0,1),Vector3(0,-1,0),Vector3(0,0,0));
#endif // !PLAYER_USE_CAPSULE_LEG_BOUND
capsuleMat.d.z = 0.5f * (PLAYER_LEGS_TOP + PLAYER_LEGS_BOTTOM);
pComposite->SetCurrentMatrix(nPedBoundIndex, RCC_MAT34V(capsuleMat));
pComposite->SetLastMatrix(nPedBoundIndex, RCC_MAT34V(capsuleMat));
nPedBoundIndex++;
}
#endif //PLAYER_USE_LOWER_LEG_BOUND
#if PLAYER_USE_FPS_HEAD_BOUND
if (GetUseFPSHeadBlocker())
{
// Create a sphere to cover the peds head when in FPS mode to prevent camera clipping with obstacles
phBoundSphere* pFPSHeadBound = rage_new phBoundSphere();
pFPSHeadBound->SetSphereRadius(GetRadiusFPSHeadBlocker());
pFPSHeadBound->SetMaterial(PGTAMATERIALMGR->g_idPhysPedCapsule);
pComposite->SetBound(nPedBoundIndex, pFPSHeadBound);
pFPSHeadBound->Release();
capsuleMat.Identity();
capsuleMat.d.z = GetHeadHeight();
capsuleMat.RotateX(HALF_PI);
pComposite->SetCurrentMatrix(nPedBoundIndex, RCC_MAT34V(capsuleMat));
pComposite->SetLastMatrix(nPedBoundIndex, RCC_MAT34V(capsuleMat));
if (!pComposite->GetTypeAndIncludeFlags())
{
pComposite->AllocateTypeAndIncludeFlags();
}
//! Initially disabled. We enable it as needed.
pComposite->SetIncludeFlags(nPedBoundIndex, 0);
pComposite->SetTypeFlags(nPedBoundIndex, ArchetypeFlags::GTA_PED_TYPE);
nPedBoundIndex++;
}
#endif //PLAYER_USE_FPS_HEAD_BOUND
pComposite->CalculateCompositeExtents();
pComposite->UpdateBvh(true);
return pComposite;
}
phBoundComposite* CFishCapsuleInfo::GeneratePedBound(CPedModelInfo &/*rPedModelInfo*/, phBoundComposite* BANK_ONLY(pCompositeBound)) const
{
#if __BANK
phBoundComposite* pComposite;
if (pCompositeBound)
{
//We've been handed a bound to rebuild. Delete it's current contents
pCompositeBound->Shutdown();
pComposite = pCompositeBound;
}
else
{
pComposite = rage_new phBoundComposite;
}
#else
phBoundComposite* pComposite = rage_new phBoundComposite;
#endif
pComposite->Init(1, true);
// need to setup capsule AFTER we've added it into the composite bound because the phBoundComposite::Init() function does a memset(0) on all structures!
phBoundCapsule *pMainCapsule = rage_new phBoundCapsule();
pMainCapsule->SetCapsuleSize(m_Radius, m_Length);
pMainCapsule->SetMaterial(PGTAMATERIALMGR->g_idPhysPedCapsule);
pComposite->SetBound(BOUND_MAIN_CAPSULE, pMainCapsule);
pMainCapsule->Release();
Matrix34 capsuleMat;
capsuleMat.Identity();
capsuleMat.d.y = m_YOffset;
capsuleMat.d.z = m_ZOffset;
pComposite->SetCurrentMatrix(BOUND_MAIN_CAPSULE, RCC_MAT34V(capsuleMat));
pComposite->SetLastMatrix(BOUND_MAIN_CAPSULE, RCC_MAT34V(capsuleMat));
//Finish building the composite
pComposite->CalculateCompositeExtents();
pComposite->UpdateBvh(true);
return pComposite;
}
void CQuadrupedCapsuleInfo::OnPostLoad()
{
m_MinSolidHeight = GetGroundToRootOffset() - m_Radius + m_ZOffset;
if (m_Blocker)
{
m_MinSolidHeight -= (m_BlockerLength * Abs(cosf(m_BlockerXRotation))) + m_BlockerRadius;
}
}
phBoundComposite* CQuadrupedCapsuleInfo::GeneratePedBound(CPedModelInfo &rPedModelInfo, phBoundComposite* BANK_ONLY(pCompositeBound)) const
{
// create a bound consisting of a capsule
// (needs to be a composite because capsules are orientated around local y-axis)
int nPedBounds = 3; //Main plus swept spheres for legs
if (m_Blocker)
{
nPedBounds += 1;
}
if (m_PropBlocker)
{
nPedBounds += 1;
}
if (m_NeckBound)
{
nPedBounds += 1;
}
#if __BANK
phBoundComposite* pComposite;
if (pCompositeBound)
{
if (pCompositeBound->GetNumBounds() != nPedBounds)
{
Errorf("Tuning requires change of bound count - not supported at runtime. Please save file out and reload the game to see!");
return NULL;
}
//We've been handed a bound to rebuild. Delete it's current contents
pCompositeBound->Shutdown();
pComposite = pCompositeBound;
}
else
{
pComposite = rage_new phBoundComposite;
}
#else
phBoundComposite* pComposite = rage_new phBoundComposite;
#endif
pComposite->Init(nPedBounds, true);
// need to setup capsule AFTER we've added it into the composite bound because the phBoundComposite::Init() function does a memset(0) on all structures!
phBoundCapsule *pMainCapsule = rage_new phBoundCapsule();
pMainCapsule->SetCapsuleSize(m_Radius, m_Length);
pMainCapsule->SetMaterial(PGTAMATERIALMGR->g_idPhysPedCapsule);
pComposite->SetBound(BOUND_MAIN_CAPSULE, pMainCapsule);
pMainCapsule->Release();
Matrix34 capsuleMat;
capsuleMat.Identity();
capsuleMat.d.y = m_YOffset;
capsuleMat.d.z = m_ZOffset;
pComposite->SetCurrentMatrix(BOUND_MAIN_CAPSULE, RCC_MAT34V(capsuleMat));
pComposite->SetLastMatrix(BOUND_MAIN_CAPSULE, RCC_MAT34V(capsuleMat));
if (!pComposite->GetTypeAndIncludeFlags())
{
pComposite->AllocateTypeAndIncludeFlags();
}
// The main capsule shouldn't collide with stair slopes as they are allowed to have exposed edges which gives unrealistic contact normals
pComposite->SetIncludeFlags(BOUND_MAIN_CAPSULE, pComposite->GetIncludeFlags(BOUND_MAIN_CAPSULE) & ~ArchetypeFlags::GTA_STAIR_SLOPE_TYPE);
//Swept spheres (in composite bound)
// second capsule to replace the ped probes
#if USE_SPHERES
Assertf(GetProbeRadius() < m_Radius, "Quadruped %s probe radius is larger or equal in size to the main capsule. This might cause unusual behaviour in some cases.", rPedModelInfo.GetModelName());
phBoundSphere *pStandCapsule = rage_new phBoundSphere();
pStandCapsule->SetSphereRadius(GetProbeRadius());
#else
const float kfBoxSize = GetProbeRadius()*2.0f;
const Vector3 vHorseScale(kfBoxSize,kfBoxSize*2.0f,kfBoxSize);
phBoundBox *pStandCapsule = rage_new phBoundBox(vHorseScale);
#endif
pStandCapsule->SetMaterial(PGTAMATERIALMGR->g_idPhysPedCapsule);
pComposite->SetBound(BOUND_CAPSULE_PROBE, pStandCapsule);
pStandCapsule->Release();
Matrix34 scratchMtx;
scratchMtx.Identity();
scratchMtx.d.z = -m_ProbeDepthFromRoot;
scratchMtx.d.y = GetProbeYOffset();
scratchMtx.RotateX(HALF_PI); //TODO: Remove?
pComposite->SetCurrentMatrix(BOUND_CAPSULE_PROBE, RCC_MAT34V(scratchMtx));
float fLastMatZ = capsuleMat.d.z - m_Radius;
rPedModelInfo.SetQuadrupedProbeDepth( fLastMatZ - scratchMtx.d.z );
//scratchMtx.d = capsuleMat.d;
scratchMtx.d.z = fLastMatZ;
rPedModelInfo.SetQuadrupedProbeTopPosition(BOUND_CAPSULE_PROBE, scratchMtx.d);
pComposite->SetLastMatrix(BOUND_CAPSULE_PROBE, RCC_MAT34V(scratchMtx));
#if USE_SPHERES
phBoundSphere *pRearStandCapsule = rage_new phBoundSphere();
pRearStandCapsule->SetSphereRadius(GetProbeRadius());
#else
phBoundBox *pRearStandCapsule = rage_new phBoundBox(vHorseScale);
#endif
pRearStandCapsule->SetMaterial(PGTAMATERIALMGR->g_idPhysPedCapsule);
pComposite->SetBound(BOUND_CAPSULE_PROBE_2, pRearStandCapsule);
pRearStandCapsule->Release();
scratchMtx.d.y -= GetProbeYOffset()*2.0f; //Make tunable?
scratchMtx.d.z = -m_ProbeDepthFromRoot;
pComposite->SetCurrentMatrix(BOUND_CAPSULE_PROBE_2, RCC_MAT34V(scratchMtx));
scratchMtx.d.z = fLastMatZ;
rPedModelInfo.SetQuadrupedProbeTopPosition(BOUND_CAPSULE_PROBE_2, scratchMtx.d);
pComposite->SetLastMatrix(BOUND_CAPSULE_PROBE_2, RCC_MAT34V(scratchMtx));
//Don't stand on peds / animals. It looks silly
if (!pComposite->GetTypeAndIncludeFlags())
{
pComposite->AllocateTypeAndIncludeFlags();
}
u32 probeFlags = pComposite->GetIncludeFlags(1) & ~(ArchetypeFlags::GTA_PED_TYPE|ArchetypeFlags::GTA_HORSE_TYPE);
pComposite->SetIncludeFlags(BOUND_CAPSULE_PROBE, probeFlags);
pComposite->SetIncludeFlags(BOUND_CAPSULE_PROBE_2, probeFlags);
//Blocker to prevent rising up over things
if (m_Blocker)
{
scratchMtx.Identity();
scratchMtx.RotateX(m_BlockerXRotation);
scratchMtx.d.y = m_BlockerYOffset;
scratchMtx.d.z = m_BlockerZOffset;
phBoundCapsule *pBlockCapsule = rage_new phBoundCapsule();
pBlockCapsule->SetCapsuleSize(m_BlockerRadius, m_BlockerLength);
pBlockCapsule->SetMaterial(PGTAMATERIALMGR->g_idPhysPedCapsule);
pComposite->SetBound(m_BlockerSlot, pBlockCapsule);
pComposite->SetCurrentMatrix(m_BlockerSlot, RCC_MAT34V(scratchMtx));
pComposite->SetLastMatrix(m_BlockerSlot, RCC_MAT34V(scratchMtx));
pBlockCapsule->Release();
}
if (m_PropBlocker)
{
if (!pComposite->GetTypeAndIncludeFlags())
{
pComposite->AllocateTypeAndIncludeFlags();
}
scratchMtx.Identity();
scratchMtx.RotateX(HALF_PI);
scratchMtx.d = Vector3(0, m_PropBlockerY, m_PropBlockerZ);
phBoundCapsule *pBlockCapsule = rage_new phBoundCapsule();
pBlockCapsule->SetCapsuleSize(m_PropBlockerRadius, m_PropBlockerLength);
pBlockCapsule->SetMaterial(PGTAMATERIALMGR->g_idPhysPedCapsule);
u32 uIncludeFlags = ArchetypeFlags::GTA_OBJECT_TYPE | ArchetypeFlags::GTA_RAGDOLL_TYPE | ArchetypeFlags::GTA_PED_TYPE
| ArchetypeFlags::GTA_HORSE_TYPE | ArchetypeFlags::GTA_HORSE_RAGDOLL_TYPE;
pComposite->SetIncludeFlags(m_PropBlockerSlot, uIncludeFlags);
pComposite->SetBound(m_PropBlockerSlot, pBlockCapsule);
pComposite->SetCurrentMatrix(m_PropBlockerSlot, RCC_MAT34V(scratchMtx));
pComposite->SetLastMatrix(m_PropBlockerSlot, RCC_MAT34V(scratchMtx));
pBlockCapsule->Release();
}
//Neck bound, for reigns and preventing head penetration
if (m_NeckBound)
{
scratchMtx.Identity();
scratchMtx.RotateX(m_NeckBoundRotation);
scratchMtx.d = Vector3(0, m_NeckBoundFwdOffset, m_NeckBoundHeightOffset);
phBoundCapsule *pNeckBoundCapsule = rage_new phBoundCapsule();
pNeckBoundCapsule->SetCapsuleSize(m_NeckBoundRadius, m_NeckBoundLength);
pNeckBoundCapsule->SetMaterial(PGTAMATERIALMGR->g_idPhysPedCapsule);
pComposite->SetBound(m_NeckBoundSlot, pNeckBoundCapsule);
pComposite->SetCurrentMatrix(m_NeckBoundSlot, RCC_MAT34V(scratchMtx));
pComposite->SetLastMatrix(m_NeckBoundSlot, RCC_MAT34V(scratchMtx));
pNeckBoundCapsule->Release();
}
pComposite->CalculateCompositeExtents();
pComposite->UpdateBvh(true);
return pComposite;
}
#if __BANK
void CPedCapsuleInfoManager::AddWidgets(bkBank& bank)
{
bank.PushGroup("Ped Capsules");
bank.AddButton("Save", Save);
bank.AddButton("Refresh", Refresh);
bank.PushGroup("Capsules");
for(s32 i = 0; i < m_CapsuleManagerInstance.m_aPedCapsule.GetCount(); i++)
{
bank.PushGroup(m_CapsuleManagerInstance.m_aPedCapsule[i]->GetName());
PARSER.AddWidgets(bank, m_CapsuleManagerInstance.m_aPedCapsule[i]);
bank.PopGroup();
}
bank.PopGroup();
bank.PopGroup();
}
void CPedCapsuleInfoManager::Save()
{
Verifyf(PARSER.SaveObject("common:/data/pedbounds", "xml", &m_CapsuleManagerInstance, parManager::XML), "Failed to save ped bounds/capsules");
}
void CPedCapsuleInfoManager::Refresh()
{
CPedModelInfo::PedDataChangedCB();
}
#endif // __BANK