You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1937 lines
57 KiB
1937 lines
57 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
#include "cbase.h" |
|
|
|
#include "ivp_surbuild_pointsoup.hxx" |
|
#include "ivp_surbuild_ledge_soup.hxx" |
|
#include "ivp_surman_polygon.hxx" |
|
#include "ivp_compact_surface.hxx" |
|
#include "ivp_compact_ledge.hxx" |
|
#include "ivp_compact_ledge_solver.hxx" |
|
#include "ivp_halfspacesoup.hxx" |
|
#include "ivp_surbuild_halfspacesoup.hxx" |
|
#include "ivp_template_surbuild.hxx" |
|
#include "hk_mopp/ivp_surbuild_mopp.hxx" |
|
#include "hk_mopp/ivp_surman_mopp.hxx" |
|
#include "hk_mopp/ivp_compact_mopp.hxx" |
|
#include "ivp_surbuild_polygon_convex.hxx" |
|
#include "ivp_templates_intern.hxx" |
|
|
|
#include "cmodel.h" |
|
#include "physics_trace.h" |
|
#include "vcollide_parse_private.h" |
|
#include "physics_virtualmesh.h" |
|
|
|
#include "mathlib/polyhedron.h" |
|
#include "tier1/byteswap.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
class CPhysCollideCompactSurface; |
|
struct bboxcache_t |
|
{ |
|
Vector mins; |
|
Vector maxs; |
|
CPhysCollideCompactSurface *pCollide; |
|
}; |
|
|
|
class CPhysicsCollision : public IPhysicsCollision |
|
{ |
|
public: |
|
CPhysicsCollision() |
|
{ |
|
} |
|
CPhysConvex *ConvexFromVerts( Vector **pVerts, int vertCount ); |
|
CPhysConvex *ConvexFromVertsFast( Vector **pVerts, int vertCount ); |
|
CPhysConvex *ConvexFromPlanes( float *pPlanes, int planeCount, float mergeDistance ); |
|
CPhysConvex *ConvexFromConvexPolyhedron( const CPolyhedron &ConvexPolyhedron ); |
|
void ConvexesFromConvexPolygon( const Vector &vPolyNormal, const Vector *pPoints, int iPointCount, CPhysConvex **pOutput ); |
|
CPhysConvex *RebuildConvexFromPlanes( CPhysConvex *pConvex, float mergeDistance ); |
|
float ConvexVolume( CPhysConvex *pConvex ); |
|
float ConvexSurfaceArea( CPhysConvex *pConvex ); |
|
CPhysCollide *ConvertConvexToCollide( CPhysConvex **pConvex, int convexCount ); |
|
CPhysCollide *ConvertConvexToCollideParams( CPhysConvex **pConvex, int convexCount, const convertconvexparams_t &convertParams ); |
|
|
|
|
|
CPolyhedron *PolyhedronFromConvex( CPhysConvex * const pConvex, bool bUseTempPolyhedron ); |
|
int GetConvexesUsedInCollideable( const CPhysCollide *pCollideable, CPhysConvex **pOutputArray, int iOutputArrayLimit ); |
|
|
|
// store game-specific data in a convex solid |
|
void SetConvexGameData( CPhysConvex *pConvex, unsigned int gameData ); |
|
void ConvexFree( CPhysConvex *pConvex ); |
|
|
|
CPhysPolysoup *PolysoupCreate( void ); |
|
void PolysoupDestroy( CPhysPolysoup *pSoup ); |
|
void PolysoupAddTriangle( CPhysPolysoup *pSoup, const Vector &a, const Vector &b, const Vector &c, int materialIndex7bits ); |
|
CPhysCollide *ConvertPolysoupToCollide( CPhysPolysoup *pSoup, bool useMOPP = true ); |
|
|
|
int CollideSize( CPhysCollide *pCollide ); |
|
int CollideWrite( char *pDest, CPhysCollide *pCollide, bool bSwap = false ); |
|
// Get the AABB of an oriented collide |
|
virtual void CollideGetAABB( Vector *pMins, Vector *pMaxs, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles ); |
|
virtual Vector CollideGetExtent( const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, const Vector &direction ); |
|
// compute the volume of a collide |
|
virtual float CollideVolume( CPhysCollide *pCollide ); |
|
virtual float CollideSurfaceArea( CPhysCollide *pCollide ); |
|
|
|
// Free a collide that was created with ConvertConvexToCollide() |
|
// UNDONE: Move this up near the other Collide routines when the version is changed |
|
virtual void DestroyCollide( CPhysCollide *pCollide ); |
|
|
|
CPhysCollide *BBoxToCollide( const Vector &mins, const Vector &maxs ); |
|
CPhysConvex *BBoxToConvex( const Vector &mins, const Vector &maxs ); |
|
|
|
// loads a set of solids into a vcollide_t |
|
virtual void VCollideLoad( vcollide_t *pOutput, int solidCount, const char *pBuffer, int size, bool swap ); |
|
// destroyts the set of solids created by VCollideLoad |
|
virtual void VCollideUnload( vcollide_t *pVCollide ); |
|
|
|
// Trace an AABB against a collide |
|
void TraceBox( const Vector &start, const Vector &end, const Vector &mins, const Vector &maxs, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *ptr ); |
|
void TraceBox( const Ray_t &ray, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *ptr ); |
|
void TraceBox( const Ray_t &ray, unsigned int contentsMask, IConvexInfo *pConvexInfo, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *ptr ); |
|
// Trace one collide against another |
|
void TraceCollide( const Vector &start, const Vector &end, const CPhysCollide *pSweepCollide, const QAngle &sweepAngles, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *ptr ); |
|
bool IsBoxIntersectingCone( const Vector &boxAbsMins, const Vector &boxAbsMaxs, const truncatedcone_t &cone ); |
|
|
|
// begins parsing a vcollide. NOTE: This keeps pointers to the text |
|
// If you delete the text and call members of IVPhysicsKeyParser, it will crash |
|
virtual IVPhysicsKeyParser *VPhysicsKeyParserCreate( const char *pKeyData ); |
|
// Free the parser created by VPhysicsKeyParserCreate |
|
virtual void VPhysicsKeyParserDestroy( IVPhysicsKeyParser *pParser ); |
|
|
|
// creates a list of verts from a collision mesh |
|
int CreateDebugMesh( const CPhysCollide *pCollisionModel, Vector **outVerts ); |
|
// destroy the list of verts created by CreateDebugMesh |
|
void DestroyDebugMesh( int vertCount, Vector *outVerts ); |
|
// create a queryable version of the collision model |
|
ICollisionQuery *CreateQueryModel( CPhysCollide *pCollide ); |
|
// destroy the queryable version |
|
void DestroyQueryModel( ICollisionQuery *pQuery ); |
|
|
|
virtual IPhysicsCollision *ThreadContextCreate( void ); |
|
virtual void ThreadContextDestroy( IPhysicsCollision *pThreadContex ); |
|
virtual unsigned int ReadStat( int statID ) { return 0; } |
|
virtual void CollideGetMassCenter( CPhysCollide *pCollide, Vector *pOutMassCenter ); |
|
virtual void CollideSetMassCenter( CPhysCollide *pCollide, const Vector &massCenter ); |
|
|
|
virtual int CollideIndex( const CPhysCollide *pCollide ); |
|
virtual Vector CollideGetOrthographicAreas( const CPhysCollide *pCollide ); |
|
virtual void OutputDebugInfo( const CPhysCollide *pCollide ); |
|
virtual CPhysCollide *CreateVirtualMesh(const virtualmeshparams_t ¶ms) { return ::CreateVirtualMesh(params); } |
|
virtual bool GetBBoxCacheSize( int *pCachedSize, int *pCachedCount ); |
|
|
|
virtual bool SupportsVirtualMesh() { return true; } |
|
|
|
virtual CPhysCollide *UnserializeCollide( char *pBuffer, int size, int index ); |
|
virtual void CollideSetOrthographicAreas( CPhysCollide *pCollide, const Vector &areas ); |
|
|
|
private: |
|
void InitBBoxCache(); |
|
bool IsBBoxCache( CPhysCollide *pCollide ); |
|
void AddBBoxCache( CPhysCollideCompactSurface *pCollide, const Vector &mins, const Vector &maxs ); |
|
CPhysCollideCompactSurface *GetBBoxCache( const Vector &mins, const Vector &maxs ); |
|
CPhysCollideCompactSurface *FastBboxCollide( const CPhysCollideCompactSurface *pCollide, const Vector &mins, const Vector &maxs ); |
|
|
|
private: |
|
CPhysicsTrace m_traceapi; |
|
CUtlVector<bboxcache_t> m_bboxCache; |
|
byte m_bboxVertMap[8]; |
|
}; |
|
|
|
CPhysicsCollision g_PhysicsCollision; |
|
IPhysicsCollision *physcollision = &g_PhysicsCollision; |
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CPhysicsCollision, IPhysicsCollision, VPHYSICS_COLLISION_INTERFACE_VERSION, g_PhysicsCollision ); |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Abstract compact_surface vs. compact_mopp |
|
//----------------------------------------------------------------------------- |
|
#define IVP_COMPACT_SURFACE_ID MAKEID('I','V','P','S') |
|
#define IVP_COMPACT_SURFACE_ID_SWAPPED MAKEID('S','P','V','I') |
|
#define IVP_COMPACT_MOPP_ID MAKEID('M','O','P','P') |
|
#define VPHYSICS_COLLISION_ID MAKEID('V','P','H','Y') |
|
#define VPHYSICS_COLLISION_VERSION 0x0100 |
|
// You can disable all of the havok Mopp collision model building by undefining this symbol |
|
#define ENABLE_IVP_MOPP 0 |
|
|
|
struct physcollideheader_t |
|
{ |
|
DECLARE_BYTESWAP_DATADESC(); |
|
int vphysicsID; |
|
short version; |
|
short modelType; |
|
|
|
void Defaults( short inputModelType ) |
|
{ |
|
vphysicsID = VPHYSICS_COLLISION_ID; |
|
|
|
version = VPHYSICS_COLLISION_VERSION; |
|
modelType = inputModelType; |
|
} |
|
}; |
|
|
|
struct compactsurfaceheader_t : public physcollideheader_t |
|
{ |
|
DECLARE_BYTESWAP_DATADESC(); |
|
int surfaceSize; |
|
Vector dragAxisAreas; |
|
int axisMapSize; |
|
|
|
void CompactSurface( const IVP_Compact_Surface *pSurface, const Vector &orthoAreas ) |
|
{ |
|
Defaults( COLLIDE_POLY ); |
|
surfaceSize = pSurface->byte_size; |
|
dragAxisAreas = orthoAreas; |
|
axisMapSize = 0; // NOTE: not yet supported |
|
} |
|
}; |
|
|
|
BEGIN_BYTESWAP_DATADESC( physcollideheader_t ) |
|
DEFINE_FIELD( vphysicsID, FIELD_INTEGER ), |
|
DEFINE_FIELD( version, FIELD_SHORT), |
|
DEFINE_FIELD( modelType, FIELD_SHORT ), |
|
END_BYTESWAP_DATADESC() |
|
|
|
BEGIN_BYTESWAP_DATADESC_( compactsurfaceheader_t, physcollideheader_t ) |
|
DEFINE_FIELD( surfaceSize, FIELD_INTEGER ), |
|
DEFINE_FIELD( dragAxisAreas, FIELD_VECTOR ), |
|
DEFINE_FIELD( axisMapSize, FIELD_INTEGER ), |
|
END_BYTESWAP_DATADESC() |
|
|
|
#if ENABLE_IVP_MOPP |
|
struct moppheader_t : public physcollideheader_t |
|
{ |
|
int moppSize; |
|
void Mopp( const IVP_Compact_Mopp *pMopp ) |
|
{ |
|
Defaults( COLLIDE_MOPP ); |
|
moppSize = pMopp->byte_size; |
|
} |
|
}; |
|
#endif |
|
|
|
#if ENABLE_IVP_MOPP |
|
class CPhysCollideMopp : public CPhysCollide |
|
{ |
|
public: |
|
CPhysCollideMopp( const moppheader_t *pHeader ); |
|
CPhysCollideMopp( IVP_Compact_Mopp *pMopp ); |
|
CPhysCollideMopp( const char *pBuffer, unsigned int size ); |
|
~CPhysCollideMopp(); |
|
|
|
void Init( const char *pBuffer, unsigned int size ); |
|
|
|
// IPhysCollide |
|
virtual int GetVCollideIndex() const { return 0; } |
|
virtual IVP_SurfaceManager *CreateSurfaceManager( short & ) const; |
|
virtual void GetAllLedges( IVP_U_BigVector<IVP_Compact_Ledge> &ledges ) const; |
|
virtual unsigned int GetSerializationSize() const; |
|
virtual Vector GetMassCenter() const; |
|
virtual void SetMassCenter( const Vector &massCenter ); |
|
virtual unsigned int SerializeToBuffer( char *pDest, bool bSwap = false ) const; |
|
virtual void OutputDebugInfo() const; |
|
|
|
private: |
|
IVP_Compact_Mopp *m_pMopp; |
|
}; |
|
#endif |
|
|
|
class CPhysCollideCompactSurface : public CPhysCollide |
|
{ |
|
public: |
|
~CPhysCollideCompactSurface(); |
|
CPhysCollideCompactSurface( const char *pBuffer, unsigned int size, int index, bool swap = false ); |
|
CPhysCollideCompactSurface( const compactsurfaceheader_t *pHeader, int index, bool swap = false ); |
|
CPhysCollideCompactSurface( IVP_Compact_Surface *pSurface ); |
|
|
|
void Init( const char *pBuffer, unsigned int size, int index, bool swap = false ); |
|
|
|
// IPhysCollide |
|
virtual int GetVCollideIndex() const { return m_pCompactSurface->dummy[0]; } |
|
virtual IVP_SurfaceManager *CreateSurfaceManager( short & ) const; |
|
virtual void GetAllLedges( IVP_U_BigVector<IVP_Compact_Ledge> &ledges ) const; |
|
virtual unsigned int GetSerializationSize() const; |
|
virtual Vector GetMassCenter() const; |
|
virtual void SetMassCenter( const Vector &massCenter ); |
|
virtual unsigned int SerializeToBuffer( char *pDest, bool bSwap = false ) const; |
|
virtual Vector GetOrthographicAreas() const; |
|
void SetOrthographicAreas( const Vector &areas ); |
|
virtual void ComputeOrthographicAreas( float epsilon ); |
|
virtual void OutputDebugInfo() const; |
|
|
|
const IVP_Compact_Surface *GetCompactSurface() const { return m_pCompactSurface; } |
|
virtual const collidemap_t *GetCollideMap() const { return m_pCollideMap; } |
|
|
|
private: |
|
|
|
struct hullinfo_t |
|
{ |
|
hullinfo_t() |
|
{ |
|
hasOuterHull = false; |
|
convexCount = 0; |
|
} |
|
bool hasOuterHull; |
|
int convexCount; |
|
}; |
|
|
|
void ComputeHullInfo_r( hullinfo_t *pOut, const IVP_Compact_Ledgetree_Node *node ) const; |
|
void InitCollideMap(); |
|
|
|
IVP_Compact_Surface *m_pCompactSurface; |
|
Vector m_orthoAreas; |
|
collidemap_t *m_pCollideMap; |
|
}; |
|
|
|
|
|
static const IVP_Compact_Surface *ConvertPhysCollideToCompactSurface( const CPhysCollide *pCollide ) |
|
{ |
|
return pCollide->GetCompactSurface(); |
|
} |
|
|
|
IVP_SurfaceManager *CreateSurfaceManager( const CPhysCollide *pCollisionModel, short &collideType ) |
|
{ |
|
return pCollisionModel ? pCollisionModel->CreateSurfaceManager( collideType ) : NULL; |
|
} |
|
|
|
void OutputCollideDebugInfo( const CPhysCollide *pCollisionModel ) |
|
{ |
|
pCollisionModel->OutputDebugInfo(); |
|
} |
|
|
|
|
|
CPhysCollide *CPhysCollide::UnserializeFromBuffer( const char *pBuffer, unsigned int size, int index, bool swap ) |
|
{ |
|
const physcollideheader_t *pHeader = reinterpret_cast<const physcollideheader_t *>(pBuffer); |
|
if ( pHeader->vphysicsID == VPHYSICS_COLLISION_ID ) |
|
{ |
|
Assert(pHeader->version == VPHYSICS_COLLISION_VERSION); |
|
switch( pHeader->modelType ) |
|
{ |
|
case COLLIDE_POLY: |
|
return new CPhysCollideCompactSurface( (compactsurfaceheader_t *)pHeader, index, swap ); |
|
case COLLIDE_MOPP: |
|
#if ENABLE_IVP_MOPP |
|
return new CPhysCollideMopp( (moppheader_t *)pHeader ); |
|
#else |
|
DevMsg( 2, "Null physics model\n"); |
|
return NULL; |
|
#endif |
|
default: |
|
Assert(0); |
|
return NULL; |
|
} |
|
} |
|
const IVP_Compact_Surface *pSurface = reinterpret_cast<const IVP_Compact_Surface *>(pBuffer); |
|
if ( pSurface->dummy[2] == IVP_COMPACT_MOPP_ID ) |
|
{ |
|
#if ENABLE_IVP_MOPP |
|
return new CPhysCollideMopp( pBuffer, size ); |
|
#else |
|
Assert(0); |
|
return NULL; |
|
#endif |
|
} |
|
if ( pSurface->dummy[2] == IVP_COMPACT_SURFACE_ID || |
|
pSurface->dummy[2] == IVP_COMPACT_SURFACE_ID_SWAPPED || |
|
pSurface->dummy[2] == 0 ) |
|
{ |
|
if ( pSurface->dummy[2] == 0 ) |
|
{ |
|
// UNDONE: Print a name here? |
|
DevMsg( 1, "Old format .PHY file loaded!!!\n" ); |
|
} |
|
return new CPhysCollideCompactSurface( pBuffer, size, index, swap ); |
|
} |
|
|
|
Assert(0); |
|
return NULL; |
|
} |
|
|
|
#if ENABLE_IVP_MOPP |
|
|
|
void CPhysCollideMopp::Init( const char *pBuffer, unsigned int size ) |
|
{ |
|
m_pMopp = (IVP_Compact_Mopp *)ivp_malloc_aligned( size, 32 ); |
|
memcpy( m_pMopp, pBuffer, size ); |
|
} |
|
|
|
CPhysCollideMopp::CPhysCollideMopp( const char *pBuffer, unsigned int size ) |
|
{ |
|
Init( pBuffer, size ); |
|
} |
|
|
|
CPhysCollideMopp::CPhysCollideMopp( const moppheader_t *pHeader ) |
|
{ |
|
Init( (const char *)(pHeader+1), pHeader->moppSize ); |
|
} |
|
|
|
CPhysCollideMopp::CPhysCollideMopp( IVP_Compact_Mopp *pMopp ) |
|
{ |
|
m_pMopp = pMopp; |
|
pMopp->dummy = IVP_COMPACT_MOPP_ID; |
|
} |
|
|
|
CPhysCollideMopp::~CPhysCollideMopp() |
|
{ |
|
ivp_free_aligned(m_pMopp); |
|
} |
|
|
|
void CPhysCollideMopp::GetAllLedges( IVP_U_BigVector<IVP_Compact_Ledge> &ledges ) const |
|
{ |
|
IVP_Compact_Ledge_Solver::get_all_ledges( m_pMopp, &ledges ); |
|
} |
|
|
|
IVP_SurfaceManager *CPhysCollideMopp::CreateSurfaceManager( short &collideType ) const |
|
{ |
|
collideType = COLLIDE_MOPP; |
|
return new IVP_SurfaceManager_Mopp( m_pMopp ); |
|
} |
|
|
|
unsigned int CPhysCollideMopp::GetSerializationSize() const |
|
{ |
|
return m_pMopp->byte_size + sizeof(moppheader_t); |
|
} |
|
|
|
unsigned int CPhysCollideMopp::SerializeToBuffer( char *pDest, bool bSwap ) const |
|
{ |
|
moppheader_t header; |
|
header.Mopp( m_pMopp ); |
|
memcpy( pDest, &header, sizeof(header) ); |
|
pDest += sizeof(header); |
|
memcpy( pDest, m_pMopp, m_pMopp->byte_size ); |
|
return GetSerializationSize(); |
|
} |
|
|
|
Vector CPhysCollideMopp::GetMassCenter() const |
|
{ |
|
Vector massCenterHL; |
|
ConvertPositionToHL( m_pMopp->mass_center, massCenterHL ); |
|
return massCenterHL; |
|
} |
|
|
|
void CPhysCollideMopp::SetMassCenter( const Vector &massCenterHL ) |
|
{ |
|
ConvertPositionToIVP( massCenterHL, m_pMopp->mass_center ); |
|
} |
|
|
|
void CPhysCollideMopp::OutputDebugInfo() const |
|
{ |
|
Msg("CollisionModel: MOPP\n"); |
|
} |
|
#endif |
|
|
|
void CPhysCollideCompactSurface::InitCollideMap() |
|
{ |
|
m_pCollideMap = NULL; |
|
if ( m_pCompactSurface ) |
|
{ |
|
IVP_U_BigVector<IVP_Compact_Ledge> ledges; |
|
GetAllLedges( ledges ); |
|
// don't make these for really large models because there's a linear search involved in using this atm. |
|
if ( !ledges.len() || ledges.len() > 32 ) |
|
return; |
|
int allocSize = sizeof(collidemap_t) + ((ledges.len()-1) * sizeof(leafmap_t)); |
|
m_pCollideMap = (collidemap_t *)malloc(allocSize); |
|
m_pCollideMap->leafCount = ledges.len(); |
|
for ( int i = 0; i < ledges.len(); i++ ) |
|
{ |
|
InitLeafmap( ledges.element_at(i), &m_pCollideMap->leafmap[i] ); |
|
} |
|
} |
|
} |
|
|
|
void CPhysCollideCompactSurface::Init( const char *pBuffer, unsigned int size, int index, bool bSwap ) |
|
{ |
|
m_pCompactSurface = (IVP_Compact_Surface *)ivp_malloc_aligned( size, 32 ); |
|
memcpy( m_pCompactSurface, pBuffer, size ); |
|
if ( bSwap ) |
|
{ |
|
m_pCompactSurface->byte_swap_all(); |
|
} |
|
m_pCompactSurface->dummy[0] = index; |
|
m_orthoAreas.Init(1,1,1); |
|
InitCollideMap(); |
|
} |
|
|
|
CPhysCollideCompactSurface::CPhysCollideCompactSurface( const char *pBuffer, unsigned int size, int index, bool swap ) |
|
{ |
|
Init( pBuffer, size, index, swap ); |
|
} |
|
CPhysCollideCompactSurface::CPhysCollideCompactSurface( const compactsurfaceheader_t *pHeader, int index, bool swap ) |
|
{ |
|
Init( (const char *)(pHeader+1), pHeader->surfaceSize, index, swap ); |
|
m_orthoAreas = pHeader->dragAxisAreas; |
|
} |
|
|
|
CPhysCollideCompactSurface::CPhysCollideCompactSurface( IVP_Compact_Surface *pSurface ) |
|
{ |
|
m_pCompactSurface = pSurface; |
|
pSurface->dummy[2] = IVP_COMPACT_SURFACE_ID; |
|
m_pCompactSurface->dummy[0] = 0; |
|
m_orthoAreas.Init(1,1,1); |
|
InitCollideMap(); |
|
} |
|
|
|
CPhysCollideCompactSurface::~CPhysCollideCompactSurface() |
|
{ |
|
ivp_free_aligned(m_pCompactSurface); |
|
if ( m_pCollideMap ) |
|
{ |
|
free(m_pCollideMap); |
|
} |
|
} |
|
|
|
IVP_SurfaceManager *CPhysCollideCompactSurface::CreateSurfaceManager( short &collideType ) const |
|
{ |
|
collideType = COLLIDE_POLY; |
|
return new IVP_SurfaceManager_Polygon( m_pCompactSurface ); |
|
} |
|
|
|
void CPhysCollideCompactSurface::GetAllLedges( IVP_U_BigVector<IVP_Compact_Ledge> &ledges ) const |
|
{ |
|
IVP_Compact_Ledge_Solver::get_all_ledges( m_pCompactSurface, &ledges ); |
|
} |
|
|
|
unsigned int CPhysCollideCompactSurface::GetSerializationSize() const |
|
{ |
|
return m_pCompactSurface->byte_size + sizeof(compactsurfaceheader_t); |
|
} |
|
|
|
unsigned int CPhysCollideCompactSurface::SerializeToBuffer( char *pDest, bool bSwap ) const |
|
{ |
|
compactsurfaceheader_t header; |
|
header.CompactSurface( m_pCompactSurface, m_orthoAreas ); |
|
if ( bSwap ) |
|
{ |
|
CByteswap swap; |
|
swap.ActivateByteSwapping( true ); |
|
swap.SwapFieldsToTargetEndian( &header ); |
|
} |
|
memcpy( pDest, &header, sizeof(header) ); |
|
pDest += sizeof(header); |
|
int surfaceSize = m_pCompactSurface->byte_size; |
|
int serializationSize = GetSerializationSize(); |
|
if ( bSwap ) |
|
{ |
|
m_pCompactSurface->byte_swap_all(); |
|
} |
|
memcpy( pDest, m_pCompactSurface, surfaceSize ); |
|
return serializationSize; |
|
} |
|
|
|
Vector CPhysCollideCompactSurface::GetMassCenter() const |
|
{ |
|
Vector massCenterHL; |
|
ConvertPositionToHL( m_pCompactSurface->mass_center, massCenterHL ); |
|
return massCenterHL; |
|
} |
|
|
|
void CPhysCollideCompactSurface::SetMassCenter( const Vector &massCenterHL ) |
|
{ |
|
ConvertPositionToIVP( massCenterHL, m_pCompactSurface->mass_center ); |
|
} |
|
|
|
Vector CPhysCollideCompactSurface::GetOrthographicAreas() const |
|
{ |
|
return m_orthoAreas; |
|
} |
|
|
|
void CPhysCollideCompactSurface::SetOrthographicAreas( const Vector &areas ) |
|
{ |
|
m_orthoAreas = areas; |
|
} |
|
|
|
|
|
void CPhysCollideCompactSurface::ComputeOrthographicAreas( float epsilon ) |
|
{ |
|
Vector mins, maxs, areas; |
|
|
|
physcollision->CollideGetAABB( &mins, &maxs, this, vec3_origin, vec3_angle ); |
|
float side = sqrt( epsilon ); |
|
if ( side < 1e-4f ) |
|
side = 1e-4f; |
|
Vector size = maxs-mins; |
|
|
|
m_orthoAreas.Init(1,1,1); |
|
trace_t tr; |
|
for ( int axis = 0; axis < 3; axis++ ) |
|
{ |
|
int u = (axis+1)%3; |
|
int v = (axis+2)%3; |
|
int hits = 0; |
|
int total = 0; |
|
float halfSide = side * 0.5; |
|
for ( float u0 = mins[u] + halfSide; u0 < maxs[u]; u0 += side ) |
|
{ |
|
for ( float v0 = mins[v] + halfSide; v0 < maxs[v]; v0 += side ) |
|
{ |
|
Vector start, end; |
|
start[axis] = mins[axis]-1; |
|
end[axis] = maxs[axis]+1; |
|
start[u] = u0; |
|
end[u] = u0; |
|
start[v] = v0; |
|
end[v] = v0; |
|
|
|
physcollision->TraceBox( start, end, vec3_origin, vec3_origin, this, vec3_origin, vec3_angle, &tr ); |
|
if ( tr.DidHit() ) |
|
{ |
|
hits++; |
|
} |
|
total++; |
|
} |
|
} |
|
|
|
if ( total <= 0 ) |
|
total = 1; |
|
m_orthoAreas[axis] = (float)hits / (float)total; |
|
} |
|
} |
|
|
|
|
|
void CPhysCollideCompactSurface::ComputeHullInfo_r( hullinfo_t *pOut, const IVP_Compact_Ledgetree_Node *node ) const |
|
{ |
|
if ( !node->is_terminal() ) |
|
{ |
|
if ( node->get_compact_hull() ) |
|
pOut->hasOuterHull = true; |
|
|
|
ComputeHullInfo_r( pOut, node->left_son() ); |
|
ComputeHullInfo_r( pOut, node->right_son() ); |
|
} |
|
else |
|
{ |
|
// terminal node, add one ledge |
|
pOut->convexCount++; |
|
} |
|
} |
|
|
|
|
|
void CPhysCollideCompactSurface::OutputDebugInfo() const |
|
{ |
|
hullinfo_t info; |
|
|
|
ComputeHullInfo_r( &info, m_pCompactSurface->get_compact_ledge_tree_root() ); |
|
const char *pOuterHull = info.hasOuterHull ? "with" : "no"; |
|
Msg("CollisionModel: Compact Surface: %d convex pieces %s outer hull\n", info.convexCount, pOuterHull ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Create a convex element from a point cloud |
|
// Input : **pVerts - array of points |
|
// vertCount - length of array |
|
// Output : opaque pointer to convex element |
|
//----------------------------------------------------------------------------- |
|
CPhysConvex *CPhysicsCollision::ConvexFromVertsFast( Vector **pVerts, int vertCount ) |
|
{ |
|
IVP_U_Vector<IVP_U_Point> points; |
|
int i; |
|
|
|
for ( i = 0; i < vertCount; i++ ) |
|
{ |
|
IVP_U_Point *tmp = new IVP_U_Point; |
|
|
|
ConvertPositionToIVP( *pVerts[i], *tmp ); |
|
|
|
BEGIN_IVP_ALLOCATION(); |
|
points.add( tmp ); |
|
END_IVP_ALLOCATION(); |
|
} |
|
|
|
BEGIN_IVP_ALLOCATION(); |
|
IVP_Compact_Ledge *pLedge = IVP_SurfaceBuilder_Pointsoup::convert_pointsoup_to_compact_ledge( &points ); |
|
END_IVP_ALLOCATION(); |
|
|
|
for ( i = 0; i < points.len(); i++ ) |
|
{ |
|
delete points.element_at(i); |
|
} |
|
points.clear(); |
|
|
|
return reinterpret_cast<CPhysConvex *>(pLedge); |
|
} |
|
|
|
CPhysConvex *CPhysicsCollision::RebuildConvexFromPlanes( CPhysConvex *pConvex, float mergeTolerance ) |
|
{ |
|
if ( !pConvex ) |
|
return NULL; |
|
|
|
IVP_Compact_Ledge *pLedge = (IVP_Compact_Ledge *)pConvex; |
|
int triangleCount = pLedge->get_n_triangles(); |
|
|
|
IVP_Compact_Triangle *pTri = pLedge->get_first_triangle(); |
|
IVP_U_Hesse plane; |
|
IVP_Halfspacesoup halfspaces; |
|
|
|
for ( int j = 0; j < triangleCount; j++ ) |
|
{ |
|
const IVP_Compact_Edge *pEdge = pTri->get_edge( 0 ); |
|
const IVP_U_Float_Point *p0 = IVP_Compact_Ledge_Solver::give_object_coords(pEdge, pLedge); |
|
const IVP_U_Float_Point *p2 = IVP_Compact_Ledge_Solver::give_object_coords(pEdge->get_next(), pLedge); |
|
const IVP_U_Float_Point *p1 = IVP_Compact_Ledge_Solver::give_object_coords(pEdge->get_prev(), pLedge); |
|
plane.calc_hesse(p0, p2, p1); |
|
float testLen = plane.real_length(); |
|
// if the triangle is less than 1mm on each side then skip it |
|
if ( testLen > 1e-6f ) |
|
{ |
|
plane.normize(); |
|
halfspaces.add_halfspace( &plane ); |
|
} |
|
|
|
pTri = pTri->get_next_tri(); |
|
} |
|
|
|
IVP_Compact_Ledge *pLedgeOut = IVP_SurfaceBuilder_Halfspacesoup::convert_halfspacesoup_to_compact_ledge( &halfspaces, mergeTolerance ); |
|
return reinterpret_cast<CPhysConvex *>( pLedgeOut ); |
|
} |
|
|
|
CPhysConvex *CPhysicsCollision::ConvexFromVerts( Vector **pVerts, int vertCount ) |
|
{ |
|
CPhysConvex *pConvex = ConvexFromVertsFast( pVerts, vertCount ); |
|
CPhysConvex *pReturn = RebuildConvexFromPlanes( pConvex, 0.01f ); // remove interior coplanar verts! |
|
if ( pReturn ) |
|
{ |
|
ConvexFree( pConvex ); |
|
return pReturn; |
|
} |
|
return pConvex; |
|
} |
|
|
|
// produce a convex element from planes (csg of planes) |
|
CPhysConvex *CPhysicsCollision::ConvexFromPlanes( float *pPlanes, int planeCount, float mergeDistance ) |
|
{ |
|
// NOTE: We're passing in planes with outward-facing normals |
|
// Ipion expects inward facing ones; we'll need to reverse plane directon |
|
struct listplane_t |
|
{ |
|
float normal[3]; |
|
float dist; |
|
}; |
|
|
|
listplane_t *pList = (listplane_t *)pPlanes; |
|
IVP_U_Hesse plane; |
|
IVP_Halfspacesoup halfspaces; |
|
|
|
mergeDistance = ConvertDistanceToIVP( mergeDistance ); |
|
|
|
for ( int i = 0; i < planeCount; i++ ) |
|
{ |
|
Vector tmp( -pList[i].normal[0], -pList[i].normal[1], -pList[i].normal[2] ); |
|
ConvertPlaneToIVP( tmp, -pList[i].dist, plane ); |
|
halfspaces.add_halfspace( &plane ); |
|
} |
|
|
|
IVP_Compact_Ledge *pLedge = IVP_SurfaceBuilder_Halfspacesoup::convert_halfspacesoup_to_compact_ledge( &halfspaces, mergeDistance ); |
|
return reinterpret_cast<CPhysConvex *>( pLedge ); |
|
} |
|
|
|
|
|
|
|
CPhysConvex *CPhysicsCollision::ConvexFromConvexPolyhedron( const CPolyhedron &ConvexPolyhedron ) |
|
{ |
|
IVP_Template_Polygon polyTemplate(ConvexPolyhedron.iVertexCount, ConvexPolyhedron.iLineCount, ConvexPolyhedron.iPolygonCount ); |
|
|
|
//convert/copy coordinates |
|
for( int i = 0; i != ConvexPolyhedron.iVertexCount; ++i ) |
|
ConvertPositionToIVP( ConvexPolyhedron.pVertices[i], polyTemplate.points[i] ); |
|
|
|
//copy lines |
|
for( int i = 0; i != ConvexPolyhedron.iLineCount; ++i ) |
|
polyTemplate.lines[i].set( ConvexPolyhedron.pLines[i].iPointIndices[0], ConvexPolyhedron.pLines[i].iPointIndices[1] ); |
|
|
|
//copy polygons |
|
for( int i = 0; i != ConvexPolyhedron.iPolygonCount; ++i ) |
|
{ |
|
polyTemplate.surfaces[i].init_surface( ConvexPolyhedron.pPolygons[i].iIndexCount ); //num vertices in a convex polygon == num lines |
|
polyTemplate.surfaces[i].templ_poly = &polyTemplate; |
|
|
|
ConvertPositionToIVP( ConvexPolyhedron.pPolygons[i].polyNormal, polyTemplate.surfaces[i].normal ); |
|
|
|
Polyhedron_IndexedLineReference_t *pLineReferences = &ConvexPolyhedron.pIndices[ConvexPolyhedron.pPolygons[i].iFirstIndex]; |
|
for( int j = 0; j != ConvexPolyhedron.pPolygons[i].iIndexCount; ++j ) |
|
{ |
|
polyTemplate.surfaces[i].lines[j] = pLineReferences[j].iLineIndex; |
|
polyTemplate.surfaces[i].revert_line[j] = pLineReferences[j].iEndPointIndex; |
|
} |
|
} |
|
|
|
//final conversion |
|
IVP_Compact_Ledge *pLedge = IVP_SurfaceBuilder_Polygon_Convex::convert_template_to_ledge(&polyTemplate); |
|
|
|
//cleanup |
|
for( int i = 0; i != ConvexPolyhedron.iPolygonCount; ++i ) |
|
polyTemplate.surfaces[i].close_surface(); |
|
|
|
return reinterpret_cast<CPhysConvex *>(pLedge); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
struct PolyhedronMesh_Triangle |
|
{ |
|
struct |
|
{ |
|
int iPointIndices[2]; |
|
} Edges[3]; |
|
}; |
|
|
|
|
|
|
|
//TODO: Optimize the returned polyhedron to get away from the triangulated mesh |
|
CPolyhedron *CPhysicsCollision::PolyhedronFromConvex( CPhysConvex * const pConvex, bool bUseTempPolyhedron ) |
|
{ |
|
IVP_Compact_Ledge *pLedge = (IVP_Compact_Ledge *)pConvex; |
|
int iTriangles = pLedge->get_n_triangles(); |
|
|
|
PolyhedronMesh_Triangle *pTriangles = (PolyhedronMesh_Triangle *)stackalloc( iTriangles * sizeof( PolyhedronMesh_Triangle ) ); |
|
|
|
int iHighestPointIndex = 0; |
|
const IVP_Compact_Triangle *pTri = pLedge->get_first_triangle(); |
|
for( int i = 0; i != iTriangles; ++i ) |
|
{ |
|
//reverse point ordering while creating edges |
|
pTriangles[i].Edges[2].iPointIndices[1] = pTriangles[i].Edges[0].iPointIndices[0] = pTri->get_edge( 2 )->get_start_point_index(); |
|
pTriangles[i].Edges[0].iPointIndices[1] = pTriangles[i].Edges[1].iPointIndices[0] = pTri->get_edge( 1 )->get_start_point_index(); |
|
pTriangles[i].Edges[1].iPointIndices[1] = pTriangles[i].Edges[2].iPointIndices[0] = pTri->get_edge( 0 )->get_start_point_index(); |
|
|
|
for( int j = 0; j != 3; ++j ) |
|
{ |
|
//get_n_points() has a whole bunch of ifdefs that apparently disable it in this case, detect number of points |
|
if( pTriangles[i].Edges[j].iPointIndices[0] > iHighestPointIndex ) |
|
iHighestPointIndex = pTriangles[i].Edges[j].iPointIndices[0]; |
|
} |
|
|
|
pTri = pTri->get_next_tri(); |
|
} |
|
|
|
++iHighestPointIndex; |
|
|
|
//apparently points might be shared between ledges and not all points will be used. So now we get to compress them into a smaller set |
|
int *pPointRemapping = (int *)stackalloc( iHighestPointIndex * sizeof( int ) ); |
|
memset( pPointRemapping, 0, iHighestPointIndex * sizeof( int ) ); |
|
for( int i = 0; i != iTriangles; ++i ) |
|
{ |
|
for( int j = 0; j != 3; ++j ) |
|
++(pPointRemapping[pTriangles[i].Edges[j].iPointIndices[0]]); |
|
} |
|
|
|
int iInsertIndex = 0; |
|
|
|
for( int i = 0; i != iHighestPointIndex; ++i ) |
|
{ |
|
if( pPointRemapping[i] ) |
|
{ |
|
pPointRemapping[i] = iInsertIndex; |
|
++iInsertIndex; |
|
} |
|
else |
|
{ |
|
pPointRemapping[i] = -1; |
|
} |
|
} |
|
|
|
const int iNumPoints = iInsertIndex; |
|
|
|
for( int i = 0; i != iTriangles; ++i ) |
|
{ |
|
for( int j = 0; j != 3; ++j ) |
|
{ |
|
for( int k = 0; k != 2; ++k ) |
|
pTriangles[i].Edges[j].iPointIndices[k] = pPointRemapping[pTriangles[i].Edges[j].iPointIndices[k]]; |
|
} |
|
} |
|
|
|
|
|
bool *bLinks = (bool *)stackalloc( iNumPoints * iNumPoints * sizeof( bool ) ); |
|
memset( bLinks, 0, iNumPoints * iNumPoints * sizeof( bool ) ); |
|
|
|
int iLinkCount = 0; |
|
for( int i = 0; i != iTriangles; ++i ) |
|
{ |
|
for( int j = 0; j != 3; ++j ) |
|
{ |
|
const int *pIndices = pTriangles[i].Edges[j].iPointIndices; |
|
int iLow = ((pIndices[0] > pIndices[1])?1:(0)); |
|
++iLinkCount; //this will technically make the link count double the actual number |
|
bLinks[(pIndices[iLow] * iNumPoints) + pIndices[1-iLow]] = true; |
|
} |
|
} |
|
|
|
iLinkCount /= 2; //cut the link count in half since we overcounted |
|
|
|
CPolyhedron *pReturn; |
|
if( bUseTempPolyhedron ) |
|
pReturn = GetTempPolyhedron( iNumPoints, iLinkCount, iLinkCount * 2, iTriangles ); |
|
else |
|
pReturn = CPolyhedron_AllocByNew::Allocate( iNumPoints, iLinkCount, iLinkCount * 2, iTriangles ); |
|
|
|
//copy/convert vertices |
|
const IVP_Compact_Poly_Point *pLedgePoints = pLedge->get_point_array(); |
|
Vector *pWriteVertices = pReturn->pVertices; |
|
for( int i = 0; i != iHighestPointIndex; ++i ) |
|
{ |
|
if( pPointRemapping[i] != -1 ) |
|
ConvertPositionToHL( pLedgePoints[i], pWriteVertices[pPointRemapping[i]] ); |
|
} |
|
|
|
|
|
//convert lines |
|
iInsertIndex = 0; |
|
for( int i = 0; i != iNumPoints; ++i ) |
|
{ |
|
for( int j = i + 1; j != iNumPoints; ++j ) |
|
{ |
|
if( bLinks[(i * iNumPoints) + j] ) |
|
{ |
|
pReturn->pLines[iInsertIndex].iPointIndices[0] = i; |
|
pReturn->pLines[iInsertIndex].iPointIndices[1] = j; |
|
++iInsertIndex; |
|
} |
|
} |
|
} |
|
|
|
|
|
int *pStartIndices = (int *)stackalloc( iNumPoints * sizeof( int ) ); //for quicker lookup of which edges to use in polygons |
|
|
|
pStartIndices[0] = 0; //the lowest index point drives links, so if the first point isn't the first link, then something is extremely messed up |
|
Assert( pReturn->pLines[0].iPointIndices[0] == 0 ); |
|
iInsertIndex = 1; |
|
for( int i = 1; i != iNumPoints; ++i ) |
|
{ |
|
for( int j = iInsertIndex; j != iLinkCount; ++j ) |
|
{ |
|
if( pReturn->pLines[j].iPointIndices[0] == i ) |
|
{ |
|
pStartIndices[i] = j; |
|
iInsertIndex = j + 1; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
//convert polygons and setup line references as a subtask |
|
iInsertIndex = 0; |
|
for( int i = 0; i != iTriangles; ++i ) |
|
{ |
|
pReturn->pPolygons[i].iFirstIndex = iInsertIndex; |
|
pReturn->pPolygons[i].iIndexCount = 3; |
|
|
|
Vector *p1, *p2, *p3; |
|
p1 = &pReturn->pVertices[pTriangles[i].Edges[0].iPointIndices[0]]; |
|
p2 = &pReturn->pVertices[pTriangles[i].Edges[1].iPointIndices[0]]; |
|
p3 = &pReturn->pVertices[pTriangles[i].Edges[2].iPointIndices[0]]; |
|
|
|
Vector v1to2, v1to3; |
|
|
|
v1to2 = *p2 - *p1; |
|
v1to3 = *p3 - *p1; |
|
|
|
pReturn->pPolygons[i].polyNormal = v1to3.Cross( v1to2 ); |
|
pReturn->pPolygons[i].polyNormal.NormalizeInPlace(); |
|
|
|
for( int j = 0; j != 3; ++j, ++iInsertIndex ) |
|
{ |
|
const int *pIndices = pTriangles[i].Edges[j].iPointIndices; |
|
int iLow = (pIndices[0] > pIndices[1])?1:0; |
|
int iLineIndex; |
|
for( iLineIndex = pStartIndices[pIndices[iLow]]; iLineIndex != iLinkCount; ++iLineIndex ) |
|
{ |
|
if( (pReturn->pLines[iLineIndex].iPointIndices[0] == pIndices[iLow]) && |
|
(pReturn->pLines[iLineIndex].iPointIndices[1] == pIndices[1 - iLow]) ) |
|
{ |
|
break; |
|
} |
|
} |
|
|
|
pReturn->pIndices[iInsertIndex].iLineIndex = iLineIndex; |
|
pReturn->pIndices[iInsertIndex].iEndPointIndex = 1 - iLow; |
|
} |
|
} |
|
|
|
return pReturn; |
|
} |
|
|
|
|
|
int CPhysicsCollision::GetConvexesUsedInCollideable( const CPhysCollide *pCollideable, CPhysConvex **pOutputArray, int iOutputArrayLimit ) |
|
{ |
|
IVP_U_BigVector<IVP_Compact_Ledge> ledges; |
|
pCollideable->GetAllLedges( ledges ); |
|
|
|
int iLedgeCount = ledges.len(); |
|
if( iLedgeCount > iOutputArrayLimit ) |
|
iLedgeCount = iOutputArrayLimit; |
|
|
|
for( int i = 0; i != iLedgeCount; ++i ) |
|
{ |
|
IVP_Compact_Ledge *pLedge = ledges.element_at(i); //doing as a 2 step since a single convert seems more error prone (without compile error) in this case |
|
pOutputArray[i] = (CPhysConvex *)pLedge; |
|
} |
|
|
|
return iLedgeCount; |
|
} |
|
|
|
void CPhysicsCollision::ConvexesFromConvexPolygon( const Vector &vPolyNormal, const Vector *pPoints, int iPointCount, CPhysConvex **pOutput ) |
|
{ |
|
IVP_U_Point *pIVP_Points = (IVP_U_Point *)stackalloc( sizeof( IVP_U_Point ) * iPointCount ); |
|
IVP_U_Point **pTriangulator = (IVP_U_Point **)stackalloc( sizeof( IVP_U_Point * ) * iPointCount ); |
|
IVP_U_Point **pRead = pTriangulator; |
|
IVP_U_Point **pWrite = pTriangulator; |
|
|
|
//convert coordinates |
|
{ |
|
for( int i = 0; i != iPointCount; ++i ) |
|
ConvertPositionToIVP( pPoints[i], pIVP_Points[i] ); |
|
} |
|
|
|
int iOutputCount = 0; |
|
|
|
//chunk this out like a triangle strip |
|
int iForwardCounter = 1; |
|
int iReverseCounter = iPointCount - 1; //guaranteed to be >= 2 to start |
|
|
|
*pWrite = &pIVP_Points[0]; |
|
++pWrite; |
|
*pWrite = &pIVP_Points[iReverseCounter]; |
|
++pWrite; |
|
--iReverseCounter; |
|
|
|
do |
|
{ |
|
//forward |
|
*pWrite = &pIVP_Points[iForwardCounter]; |
|
++iForwardCounter; |
|
|
|
pOutput[iOutputCount] = reinterpret_cast<CPhysConvex *>(IVP_SurfaceBuilder_Pointsoup::convert_triangle_to_compace_ledge( pRead[0], pRead[1], pRead[2] )); |
|
Assert( pOutput[iOutputCount] ); |
|
++iOutputCount; |
|
if( iForwardCounter > iReverseCounter ) |
|
break; |
|
|
|
++pRead; |
|
++pWrite; |
|
|
|
|
|
|
|
//backward |
|
*pWrite = &pIVP_Points[iReverseCounter]; |
|
--iReverseCounter; |
|
|
|
pOutput[iOutputCount] = reinterpret_cast<CPhysConvex *>(IVP_SurfaceBuilder_Pointsoup::convert_triangle_to_compace_ledge( pRead[0], pRead[1], pRead[2] )); |
|
Assert( pOutput[iOutputCount] ); |
|
++iOutputCount; |
|
|
|
if( iForwardCounter > iReverseCounter ) |
|
break; |
|
|
|
++pRead; |
|
++pWrite; |
|
} while( true ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: copies the first vert int pLedge to out |
|
// Input : *pLedge - compact ledge |
|
// *out - destination float array for the vert |
|
//----------------------------------------------------------------------------- |
|
static void LedgeInsidePoint( IVP_Compact_Ledge *pLedge, Vector& out ) |
|
{ |
|
IVP_Compact_Triangle *pTri = pLedge->get_first_triangle(); |
|
const IVP_Compact_Edge *pEdge = pTri->get_edge( 0 ); |
|
const IVP_U_Float_Point *pPoint = pEdge->get_start_point( pLedge ); |
|
ConvertPositionToHL( *pPoint, out ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Calculate the volume of a tetrahedron with these vertices |
|
// Input : p0 - points of tetrahedron |
|
// p1 - |
|
// p2 - |
|
// p3 - |
|
// Output : float (volume in units^3) |
|
//----------------------------------------------------------------------------- |
|
static float TetrahedronVolume( const Vector &p0, const Vector &p1, const Vector &p2, const Vector &p3 ) |
|
{ |
|
Vector a, b, c, cross; |
|
float volume = 1.0f / 6.0f; |
|
|
|
a = p1 - p0; |
|
b = p2 - p0; |
|
c = p3 - p0; |
|
cross = CrossProduct( b, c ); |
|
|
|
volume *= DotProduct( a, cross ); |
|
if ( volume < 0 ) |
|
return -volume; |
|
return volume; |
|
} |
|
|
|
|
|
static float TriangleArea( const Vector &p0, const Vector &p1, const Vector &p2 ) |
|
{ |
|
Vector e0 = p1 - p0; |
|
Vector e1 = p2 - p0; |
|
Vector cross; |
|
|
|
CrossProduct( e0, e1, cross ); |
|
return 0.5 * cross.Length(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Tetrahedronalize this ledge and compute it's volume in BSP space |
|
// Input : convex - the ledge |
|
// Output : float - volume in HL units (in^3) |
|
//----------------------------------------------------------------------------- |
|
float CPhysicsCollision::ConvexVolume( CPhysConvex *pConvex ) |
|
{ |
|
IVP_Compact_Ledge *pLedge = (IVP_Compact_Ledge *)pConvex; |
|
int triangleCount = pLedge->get_n_triangles(); |
|
|
|
IVP_Compact_Triangle *pTri = pLedge->get_first_triangle(); |
|
|
|
Vector vert; |
|
float volume = 0; |
|
// vert is in HL units |
|
LedgeInsidePoint( pLedge, vert ); |
|
|
|
for ( int j = 0; j < triangleCount; j++ ) |
|
{ |
|
Vector points[3]; |
|
for ( int k = 0; k < 3; k++ ) |
|
{ |
|
const IVP_Compact_Edge *pEdge = pTri->get_edge( k ); |
|
const IVP_U_Float_Point *pPoint = pEdge->get_start_point( pLedge ); |
|
ConvertPositionToHL( *pPoint, points[k] ); |
|
} |
|
volume += TetrahedronVolume( vert, points[0], points[1], points[2] ); |
|
|
|
pTri = pTri->get_next_tri(); |
|
} |
|
|
|
return volume; |
|
} |
|
|
|
|
|
float CPhysicsCollision::ConvexSurfaceArea( CPhysConvex *pConvex ) |
|
{ |
|
IVP_Compact_Ledge *pLedge = (IVP_Compact_Ledge *)pConvex; |
|
int triangleCount = pLedge->get_n_triangles(); |
|
|
|
IVP_Compact_Triangle *pTri = pLedge->get_first_triangle(); |
|
|
|
float area = 0; |
|
|
|
for ( int j = 0; j < triangleCount; j++ ) |
|
{ |
|
Vector points[3]; |
|
for ( int k = 0; k < 3; k++ ) |
|
{ |
|
const IVP_Compact_Edge *pEdge = pTri->get_edge( k ); |
|
const IVP_U_Float_Point *pPoint = pEdge->get_start_point( pLedge ); |
|
ConvertPositionToHL( *pPoint, points[k] ); |
|
} |
|
area += TriangleArea( points[0], points[1], points[2] ); |
|
|
|
pTri = pTri->get_next_tri(); |
|
} |
|
|
|
return area; |
|
} |
|
|
|
// Convert an array of convex elements to a compiled collision model (this deletes the convex elements) |
|
CPhysCollide *CPhysicsCollision::ConvertConvexToCollide( CPhysConvex **pConvex, int convexCount ) |
|
{ |
|
convertconvexparams_t convertParams; |
|
convertParams.Defaults(); |
|
return ConvertConvexToCollideParams( pConvex, convexCount, convertParams ); |
|
} |
|
|
|
CPhysCollide *CPhysicsCollision::ConvertConvexToCollideParams( CPhysConvex **pConvex, int convexCount, const convertconvexparams_t &convertParams ) |
|
{ |
|
if ( !convexCount || !pConvex ) |
|
return NULL; |
|
|
|
int validConvex = 0; |
|
BEGIN_IVP_ALLOCATION(); |
|
IVP_SurfaceBuilder_Ledge_Soup builder; |
|
IVP_Compact_Surface *pSurface = NULL; |
|
|
|
for ( int i = 0; i < convexCount; i++ ) |
|
{ |
|
if ( pConvex[i] ) |
|
{ |
|
validConvex++; |
|
builder.insert_ledge( (IVP_Compact_Ledge *)pConvex[i] ); |
|
} |
|
} |
|
// if the outside code does something stupid, don't crash |
|
if ( validConvex ) |
|
{ |
|
IVP_Template_Surbuild_LedgeSoup params; |
|
params.force_convex_hull = (IVP_Compact_Ledge *)convertParams.pForcedOuterHull; |
|
params.build_root_convex_hull = convertParams.buildOuterConvexHull ? IVP_TRUE : IVP_FALSE; |
|
|
|
// NOTE: THIS FREES THE LEDGES in pConvex!!! |
|
pSurface = builder.compile( ¶ms ); |
|
CPhysCollide *pCollide = new CPhysCollideCompactSurface( pSurface ); |
|
if ( convertParams.buildDragAxisAreas ) |
|
{ |
|
pCollide->ComputeOrthographicAreas( convertParams.dragAreaEpsilon ); |
|
} |
|
|
|
END_IVP_ALLOCATION(); |
|
return pCollide; |
|
} |
|
|
|
END_IVP_ALLOCATION(); |
|
|
|
return NULL; |
|
} |
|
|
|
static void InitBoxVerts( Vector *boxVerts, Vector **ppVerts, const Vector &mins, const Vector &maxs ) |
|
{ |
|
for (int i = 0; i < 8; ++i) |
|
{ |
|
boxVerts[i][0] = (i & 0x1) ? maxs[0] : mins[0]; |
|
boxVerts[i][1] = (i & 0x2) ? maxs[1] : mins[1]; |
|
boxVerts[i][2] = (i & 0x4) ? maxs[2] : mins[2]; |
|
if ( ppVerts ) |
|
{ |
|
ppVerts[i] = &boxVerts[i]; |
|
} |
|
} |
|
} |
|
|
|
|
|
#define FAST_BBOX 1 |
|
CPhysCollideCompactSurface *CPhysicsCollision::FastBboxCollide( const CPhysCollideCompactSurface *pCollide, const Vector &mins, const Vector &maxs ) |
|
{ |
|
Vector boxVerts[8]; |
|
InitBoxVerts( boxVerts, NULL, mins, maxs ); |
|
// copy the compact ledge at bboxCache 0 |
|
// stuff the verts in there |
|
const IVP_Compact_Surface *pSurface = ConvertPhysCollideToCompactSurface( pCollide ); |
|
Assert( pSurface ); |
|
const IVP_Compact_Ledgetree_Node *node = pSurface->get_compact_ledge_tree_root(); |
|
Assert( node->is_terminal() == IVP_TRUE ); |
|
const IVP_Compact_Ledge *pLedge = node->get_compact_ledge(); |
|
int ledgeSize = pLedge->get_size(); |
|
IVP_Compact_Ledge *pNewLedge = (IVP_Compact_Ledge *)ivp_malloc_aligned( ledgeSize, 16 ); |
|
memcpy( pNewLedge, pLedge, ledgeSize ); |
|
pNewLedge->set_client_data(0); |
|
IVP_Compact_Poly_Point *pPoints = pNewLedge->get_point_array(); |
|
for ( int i = 0; i < 8; i++ ) |
|
{ |
|
IVP_U_Float_Hesse ivp; |
|
ConvertPositionToIVP( boxVerts[m_bboxVertMap[i]], ivp ); |
|
ivp.hesse_val = 0; |
|
pPoints[i].set4(&ivp); |
|
} |
|
CPhysConvex *pConvex = (CPhysConvex *)pNewLedge; |
|
return (CPhysCollideCompactSurface *)ConvertConvexToCollide( &pConvex, 1 ); |
|
} |
|
|
|
void CPhysicsCollision::InitBBoxCache() |
|
{ |
|
Vector boxVerts[8], *ppVerts[8]; |
|
Vector mins(-16,-16,0), maxs(16,16,72); |
|
// init with the player box |
|
InitBoxVerts( boxVerts, ppVerts, mins, maxs ); |
|
// Generate a convex hull from the verts |
|
CPhysConvex *pConvex = ConvexFromVertsFast( ppVerts, 8 ); |
|
IVP_Compact_Poly_Point *pPoints = reinterpret_cast<IVP_Compact_Ledge *>(pConvex)->get_point_array(); |
|
for ( int i = 0; i < 8; i++ ) |
|
{ |
|
int nearest = -1; |
|
float minDist = 0.1; |
|
Vector tmp; |
|
ConvertPositionToHL( pPoints[i], tmp ); |
|
for ( int j = 0; j < 8; j++ ) |
|
{ |
|
float dist = (boxVerts[j] - tmp).Length(); |
|
if ( dist < minDist ) |
|
{ |
|
minDist = dist; |
|
nearest = j; |
|
} |
|
} |
|
|
|
m_bboxVertMap[i] = nearest; |
|
|
|
#if _DEBUG |
|
for ( int k = 0; k < i; k++ ) |
|
{ |
|
Assert( m_bboxVertMap[k] != m_bboxVertMap[i] ); |
|
} |
|
#endif |
|
// NOTE: If this is wrong, you can disable FAST_BBOX above to fix |
|
AssertMsg( nearest != -1, "CPhysCollide: Vert map is wrong\n" ); |
|
} |
|
CPhysCollide *pCollide = ConvertConvexToCollide( &pConvex, 1 ); |
|
AddBBoxCache( (CPhysCollideCompactSurface *)pCollide, mins, maxs ); |
|
} |
|
|
|
|
|
CPhysConvex *CPhysicsCollision::BBoxToConvex( const Vector &mins, const Vector &maxs ) |
|
{ |
|
Vector boxVerts[8], *ppVerts[8]; |
|
InitBoxVerts( boxVerts, ppVerts, mins, maxs ); |
|
// Generate a convex hull from the verts |
|
return ConvexFromVertsFast( ppVerts, 8 ); |
|
} |
|
|
|
CPhysCollide *CPhysicsCollision::BBoxToCollide( const Vector &mins, const Vector &maxs ) |
|
{ |
|
// can't create a collision model for an empty box ! |
|
if ( mins == maxs ) |
|
{ |
|
Assert(0); |
|
return NULL; |
|
} |
|
|
|
// find this bbox in the cache |
|
CPhysCollide *pCollide = GetBBoxCache( mins, maxs ); |
|
if ( pCollide ) |
|
return pCollide; |
|
|
|
// FAST_BBOX: uses an existing compact ledge as a template for fast generation |
|
// building convex hulls from points is slow |
|
#if FAST_BBOX |
|
if ( m_bboxCache.Count() == 0 ) |
|
{ |
|
InitBBoxCache(); |
|
} |
|
pCollide = FastBboxCollide( m_bboxCache[0].pCollide, mins, maxs ); |
|
#else |
|
CPhysConvex *pConvex = BBoxToConvex( mins, maxs ); |
|
pCollide = ConvertConvexToCollide( &pConvex, 1 ); |
|
#endif |
|
AddBBoxCache( (CPhysCollideCompactSurface *)pCollide, mins, maxs ); |
|
return pCollide; |
|
} |
|
|
|
bool CPhysicsCollision::IsBBoxCache( CPhysCollide *pCollide ) |
|
{ |
|
// UNDONE: Sort the list so it can be searched spatially instead of linearly? |
|
for ( int i = m_bboxCache.Count()-1; i >= 0; i-- ) |
|
{ |
|
if ( m_bboxCache[i].pCollide == pCollide ) |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
void CPhysicsCollision::AddBBoxCache( CPhysCollideCompactSurface *pCollide, const Vector &mins, const Vector &maxs ) |
|
{ |
|
int index = m_bboxCache.AddToTail(); |
|
bboxcache_t *pCache = &m_bboxCache[index]; |
|
pCache->pCollide = pCollide; |
|
pCache->mins = mins; |
|
pCache->maxs = maxs; |
|
} |
|
|
|
CPhysCollideCompactSurface *CPhysicsCollision::GetBBoxCache( const Vector &mins, const Vector &maxs ) |
|
{ |
|
for ( int i = m_bboxCache.Count()-1; i >= 0; i-- ) |
|
{ |
|
if ( m_bboxCache[i].mins == mins && m_bboxCache[i].maxs == maxs ) |
|
return m_bboxCache[i].pCollide; |
|
} |
|
return NULL; |
|
} |
|
|
|
|
|
void CPhysicsCollision::ConvexFree( CPhysConvex *pConvex ) |
|
{ |
|
if ( !pConvex ) |
|
return; |
|
ivp_free_aligned( pConvex ); |
|
} |
|
|
|
// Get the size of the collision model for serialization |
|
int CPhysicsCollision::CollideSize( CPhysCollide *pCollide ) |
|
{ |
|
return pCollide->GetSerializationSize(); |
|
} |
|
|
|
int CPhysicsCollision::CollideWrite( char *pDest, CPhysCollide *pCollide, bool bSwap ) |
|
{ |
|
return pCollide->SerializeToBuffer( pDest, bSwap ); |
|
} |
|
|
|
CPhysCollide *CPhysicsCollision::UnserializeCollide( char *pBuffer, int size, int index ) |
|
{ |
|
return CPhysCollide::UnserializeFromBuffer( pBuffer, size, index ); |
|
} |
|
|
|
class CPhysPolysoup |
|
{ |
|
public: |
|
CPhysPolysoup(); |
|
#if ENABLE_IVP_MOPP |
|
IVP_SurfaceBuilder_Mopp m_builder; |
|
#endif |
|
IVP_SurfaceBuilder_Ledge_Soup m_builderSoup; |
|
IVP_U_Vector<IVP_U_Point> m_points; |
|
IVP_U_Point m_triangle[3]; |
|
|
|
bool m_isValid; |
|
}; |
|
|
|
CPhysPolysoup::CPhysPolysoup() |
|
{ |
|
m_isValid = false; |
|
m_points.add( &m_triangle[0] ); |
|
m_points.add( &m_triangle[1] ); |
|
m_points.add( &m_triangle[2] ); |
|
} |
|
|
|
CPhysPolysoup *CPhysicsCollision::PolysoupCreate( void ) |
|
{ |
|
return new CPhysPolysoup; |
|
} |
|
|
|
void CPhysicsCollision::PolysoupDestroy( CPhysPolysoup *pSoup ) |
|
{ |
|
delete pSoup; |
|
} |
|
|
|
void CPhysicsCollision::PolysoupAddTriangle( CPhysPolysoup *pSoup, const Vector &a, const Vector &b, const Vector &c, int materialIndex7bits ) |
|
{ |
|
pSoup->m_isValid = true; |
|
ConvertPositionToIVP( a, pSoup->m_triangle[0] ); |
|
ConvertPositionToIVP( b, pSoup->m_triangle[1] ); |
|
ConvertPositionToIVP( c, pSoup->m_triangle[2] ); |
|
IVP_Compact_Ledge *pLedge = IVP_SurfaceBuilder_Pointsoup::convert_pointsoup_to_compact_ledge(&pSoup->m_points); |
|
if ( !pLedge ) |
|
{ |
|
Warning("Degenerate Triangle\n"); |
|
Warning("(%.2f, %.2f, %.2f), ", a.x, a.y, a.z ); |
|
Warning("(%.2f, %.2f, %.2f), ", b.x, b.y, b.z ); |
|
Warning("(%.2f, %.2f, %.2f)\n", c.x, c.y, c.z ); |
|
return; |
|
} |
|
IVP_Compact_Triangle *pTriangle = pLedge->get_first_triangle(); |
|
pTriangle->set_material_index( materialIndex7bits ); |
|
#if ENABLE_IVP_MOPP |
|
pSoup->m_builder.insert_ledge(pLedge); |
|
#endif |
|
pSoup->m_builderSoup.insert_ledge(pLedge); |
|
} |
|
|
|
CPhysCollide *CPhysicsCollision::ConvertPolysoupToCollide( CPhysPolysoup *pSoup, bool useMOPP ) |
|
{ |
|
if ( !pSoup->m_isValid ) |
|
return NULL; |
|
|
|
CPhysCollide *pCollide = NULL; |
|
#if ENABLE_IVP_MOPP |
|
if ( useMOPP ) |
|
{ |
|
IVP_Compact_Mopp *pSurface = pSoup->m_builder.compile(); |
|
pCollide = new CPhysCollideMopp( pSurface ); |
|
} |
|
else |
|
#endif |
|
{ |
|
IVP_Compact_Surface *pSurface = pSoup->m_builderSoup.compile(); |
|
pCollide = new CPhysCollideCompactSurface( pSurface ); |
|
} |
|
|
|
Assert(pCollide); |
|
|
|
// There's a bug in IVP where the duplicated triangles (for 2D) |
|
// don't get the materials set properly, so copy them |
|
IVP_U_BigVector<IVP_Compact_Ledge> ledges; |
|
pCollide->GetAllLedges( ledges ); |
|
|
|
for ( int i = 0; i < ledges.len(); i++ ) |
|
{ |
|
IVP_Compact_Ledge *pLedge = ledges.element_at( i ); |
|
int triangleCount = pLedge->get_n_triangles(); |
|
|
|
IVP_Compact_Triangle *pTri = pLedge->get_first_triangle(); |
|
int materialIndex = pTri->get_material_index(); |
|
if ( !materialIndex ) |
|
{ |
|
for ( int j = 0; j < triangleCount; j++ ) |
|
{ |
|
if ( pTri->get_material_index() != 0 ) |
|
{ |
|
materialIndex = pTri->get_material_index(); |
|
} |
|
pTri = pTri->get_next_tri(); |
|
} |
|
} |
|
for ( int j = 0; j < triangleCount; j++ ) |
|
{ |
|
pTri->set_material_index( materialIndex ); |
|
pTri = pTri->get_next_tri(); |
|
} |
|
} |
|
|
|
return pCollide; |
|
} |
|
|
|
int CPhysicsCollision::CreateDebugMesh( const CPhysCollide *pCollisionModel, Vector **outVerts ) |
|
{ |
|
int i; |
|
|
|
IVP_U_BigVector<IVP_Compact_Ledge> ledges; |
|
pCollisionModel->GetAllLedges( ledges ); |
|
|
|
int vertCount = 0; |
|
|
|
for ( i = 0; i < ledges.len(); i++ ) |
|
{ |
|
IVP_Compact_Ledge *pLedge = ledges.element_at( i ); |
|
vertCount += pLedge->get_n_triangles() * 3; |
|
} |
|
Vector *verts = new Vector[ vertCount ]; |
|
|
|
int vertIndex = 0; |
|
for ( i = 0; i < ledges.len(); i++ ) |
|
{ |
|
IVP_Compact_Ledge *pLedge = ledges.element_at( i ); |
|
int triangleCount = pLedge->get_n_triangles(); |
|
|
|
IVP_Compact_Triangle *pTri = pLedge->get_first_triangle(); |
|
for ( int j = 0; j < triangleCount; j++ ) |
|
{ |
|
for ( int k = 2; k >= 0; k-- ) |
|
{ |
|
const IVP_Compact_Edge *pEdge = pTri->get_edge( k ); |
|
const IVP_U_Float_Point *pPoint = pEdge->get_start_point( pLedge ); |
|
|
|
Vector* pVec = verts + vertIndex; |
|
ConvertPositionToHL( *pPoint, *pVec ); |
|
vertIndex++; |
|
} |
|
pTri = pTri->get_next_tri(); |
|
} |
|
} |
|
|
|
*outVerts = verts; |
|
return vertCount; |
|
} |
|
|
|
|
|
void CPhysicsCollision::DestroyDebugMesh( int vertCount, Vector *outVerts ) |
|
{ |
|
delete[] outVerts; |
|
} |
|
|
|
|
|
void CPhysicsCollision::SetConvexGameData( CPhysConvex *pConvex, unsigned int gameData ) |
|
{ |
|
IVP_Compact_Ledge *pLedge = reinterpret_cast<IVP_Compact_Ledge *>( pConvex ); |
|
pLedge->set_client_data( gameData ); |
|
} |
|
|
|
|
|
void CPhysicsCollision::TraceBox( const Vector &start, const Vector &end, const Vector &mins, const Vector &maxs, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *ptr ) |
|
{ |
|
m_traceapi.SweepBoxIVP( start, end, mins, maxs, pCollide, collideOrigin, collideAngles, ptr ); |
|
} |
|
|
|
void CPhysicsCollision::TraceBox( const Ray_t &ray, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *ptr ) |
|
{ |
|
TraceBox( ray, MASK_ALL, NULL, pCollide, collideOrigin, collideAngles, ptr ); |
|
} |
|
|
|
void CPhysicsCollision::TraceBox( const Ray_t &ray, unsigned int contentsMask, IConvexInfo *pConvexInfo, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *ptr ) |
|
{ |
|
m_traceapi.SweepBoxIVP( ray, contentsMask, pConvexInfo, pCollide, collideOrigin, collideAngles, ptr ); |
|
} |
|
|
|
// Trace one collide against another |
|
void CPhysicsCollision::TraceCollide( const Vector &start, const Vector &end, const CPhysCollide *pSweepCollide, const QAngle &sweepAngles, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, trace_t *ptr ) |
|
{ |
|
m_traceapi.SweepIVP( start, end, pSweepCollide, sweepAngles, pCollide, collideOrigin, collideAngles, ptr ); |
|
} |
|
|
|
void CPhysicsCollision::CollideGetAABB( Vector *pMins, Vector *pMaxs, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles ) |
|
{ |
|
m_traceapi.GetAABB( pMins, pMaxs, pCollide, collideOrigin, collideAngles ); |
|
} |
|
|
|
|
|
Vector CPhysicsCollision::CollideGetExtent( const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, const Vector &direction ) |
|
{ |
|
if ( !pCollide ) |
|
return collideOrigin; |
|
|
|
return m_traceapi.GetExtent( pCollide, collideOrigin, collideAngles, direction ); |
|
} |
|
|
|
bool CPhysicsCollision::IsBoxIntersectingCone( const Vector &boxAbsMins, const Vector &boxAbsMaxs, const truncatedcone_t &cone ) |
|
{ |
|
return m_traceapi.IsBoxIntersectingCone( boxAbsMins, boxAbsMaxs, cone ); |
|
} |
|
|
|
// Free a collide that was created with ConvertConvexToCollide() |
|
void CPhysicsCollision::DestroyCollide( CPhysCollide *pCollide ) |
|
{ |
|
if ( !IsBBoxCache( pCollide ) ) |
|
{ |
|
delete pCollide; |
|
} |
|
} |
|
|
|
// calculate the volume of a collide by calling ConvexVolume on its parts |
|
float CPhysicsCollision::CollideVolume( CPhysCollide *pCollide ) |
|
{ |
|
IVP_U_BigVector<IVP_Compact_Ledge> ledges; |
|
pCollide->GetAllLedges( ledges ); |
|
|
|
float volume = 0; |
|
for ( int i = 0; i < ledges.len(); i++ ) |
|
{ |
|
volume += ConvexVolume( (CPhysConvex *)ledges.element_at(i) ); |
|
} |
|
|
|
return volume; |
|
} |
|
|
|
// calculate the volume of a collide by calling ConvexVolume on its parts |
|
float CPhysicsCollision::CollideSurfaceArea( CPhysCollide *pCollide ) |
|
{ |
|
IVP_U_BigVector<IVP_Compact_Ledge> ledges; |
|
pCollide->GetAllLedges( ledges ); |
|
|
|
float area = 0; |
|
for ( int i = 0; i < ledges.len(); i++ ) |
|
{ |
|
area += ConvexSurfaceArea( (CPhysConvex *)ledges.element_at(i) ); |
|
} |
|
|
|
return area; |
|
} |
|
|
|
|
|
// loads a set of solids into a vcollide_t |
|
void CPhysicsCollision::VCollideLoad( vcollide_t *pOutput, int solidCount, const char *pBuffer, int bufferSize, bool swap ) |
|
{ |
|
memset( pOutput, 0, sizeof(*pOutput) ); |
|
int position = 0; |
|
|
|
pOutput->solidCount = solidCount; |
|
pOutput->solids = new CPhysCollide *[solidCount]; |
|
|
|
BEGIN_IVP_ALLOCATION(); |
|
|
|
for ( int i = 0; i < solidCount; i++ ) |
|
{ |
|
int size = *(int*)(pBuffer + position); |
|
// memcpy( &size, pBuffer + position, sizeof(int) ); |
|
position += sizeof(int); |
|
|
|
char *tmpbuf = new char[size]; |
|
memcpy(tmpbuf, pBuffer + position, size); |
|
|
|
pOutput->solids[i] = CPhysCollide::UnserializeFromBuffer( tmpbuf, size, i, swap ); |
|
position += size; |
|
|
|
delete[] tmpbuf; |
|
} |
|
|
|
END_IVP_ALLOCATION(); |
|
pOutput->isPacked = false; |
|
int keySize = bufferSize - position; |
|
pOutput->pKeyValues = new char[keySize]; |
|
memcpy( pOutput->pKeyValues, pBuffer + position, keySize ); |
|
pOutput->descSize = 0; |
|
} |
|
|
|
// destroys the set of solids created by VCollideCreateCPhysCollide |
|
void CPhysicsCollision::VCollideUnload( vcollide_t *pVCollide ) |
|
{ |
|
for ( int i = 0; i < pVCollide->solidCount; i++ ) |
|
{ |
|
#if _DEBUG |
|
// HACKHACK: 1024 is just "some big number" |
|
// GetActiveEnvironmentByIndex() will eventually return NULL when there are no more environments. |
|
// In HL2 & TF2, there are only 2 environments - so j > 1 is probably an error! |
|
for ( int j = 0; j < 1024; j++ ) |
|
{ |
|
IPhysicsEnvironment *pEnv = g_PhysicsInternal->GetActiveEnvironmentByIndex( j ); |
|
if ( !pEnv ) |
|
break; |
|
|
|
if ( pEnv->IsCollisionModelUsed( (CPhysCollide *)pVCollide->solids[i] ) ) |
|
{ |
|
AssertMsg(0, "Freed collision model while in use!!!\n"); |
|
return; |
|
} |
|
} |
|
#endif |
|
delete pVCollide->solids[i]; |
|
} |
|
delete[] pVCollide->solids; |
|
delete[] pVCollide->pKeyValues; |
|
memset( pVCollide, 0, sizeof(*pVCollide) ); |
|
} |
|
|
|
// begins parsing a vcollide. NOTE: This keeps pointers to the vcollide_t |
|
// If you delete the vcollide_t and call members of IVCollideParse, it will crash |
|
IVPhysicsKeyParser *CPhysicsCollision::VPhysicsKeyParserCreate( const char *pKeyData ) |
|
{ |
|
return CreateVPhysicsKeyParser( pKeyData ); |
|
} |
|
|
|
// Free the parser created by VPhysicsKeyParserCreate |
|
void CPhysicsCollision::VPhysicsKeyParserDestroy( IVPhysicsKeyParser *pParser ) |
|
{ |
|
DestroyVPhysicsKeyParser( pParser ); |
|
} |
|
|
|
IPhysicsCollision *CPhysicsCollision::ThreadContextCreate( void ) |
|
{ |
|
return this; |
|
} |
|
|
|
void CPhysicsCollision::ThreadContextDestroy( IPhysicsCollision *pThreadContext ) |
|
{ |
|
} |
|
|
|
|
|
void CPhysicsCollision::CollideGetMassCenter( CPhysCollide *pCollide, Vector *pOutMassCenter ) |
|
{ |
|
*pOutMassCenter = pCollide->GetMassCenter(); |
|
} |
|
|
|
void CPhysicsCollision::CollideSetMassCenter( CPhysCollide *pCollide, const Vector &massCenter ) |
|
{ |
|
pCollide->SetMassCenter( massCenter ); |
|
} |
|
|
|
int CPhysicsCollision::CollideIndex( const CPhysCollide *pCollide ) |
|
{ |
|
if ( !pCollide ) |
|
return 0; |
|
return pCollide->GetVCollideIndex(); |
|
} |
|
|
|
Vector CPhysicsCollision::CollideGetOrthographicAreas( const CPhysCollide *pCollide ) |
|
{ |
|
if ( !pCollide ) |
|
return vec3_origin; |
|
return pCollide->GetOrthographicAreas(); |
|
} |
|
|
|
void CPhysicsCollision::CollideSetOrthographicAreas( CPhysCollide *pCollide, const Vector &areas ) |
|
{ |
|
if ( pCollide ) |
|
pCollide->SetOrthographicAreas( areas ); |
|
} |
|
|
|
// returns true if this collide has an outer hull built |
|
void CPhysicsCollision::OutputDebugInfo( const CPhysCollide *pCollide ) |
|
{ |
|
pCollide->OutputDebugInfo(); |
|
} |
|
|
|
bool CPhysicsCollision::GetBBoxCacheSize( int *pCachedSize, int *pCachedCount ) |
|
{ |
|
*pCachedSize = 0; |
|
*pCachedCount = m_bboxCache.Count(); |
|
for ( int i = 0; i < *pCachedCount; i++ ) |
|
{ |
|
*pCachedSize += m_bboxCache[i].pCollide->GetSerializationSize(); |
|
} |
|
return true; |
|
} |
|
|
|
class CCollisionQuery : public ICollisionQuery |
|
{ |
|
public: |
|
CCollisionQuery( CPhysCollide *pCollide ); |
|
~CCollisionQuery( void ) {} |
|
|
|
// number of convex pieces in the whole solid |
|
virtual int ConvexCount( void ); |
|
// triangle count for this convex piece |
|
virtual int TriangleCount( int convexIndex ); |
|
|
|
// get the stored game data |
|
virtual unsigned int GetGameData( int convexIndex ); |
|
|
|
// Gets the triangle's verts to an array |
|
virtual void GetTriangleVerts( int convexIndex, int triangleIndex, Vector *verts ); |
|
|
|
// UNDONE: This doesn't work!!! |
|
virtual void SetTriangleVerts( int convexIndex, int triangleIndex, const Vector *verts ); |
|
|
|
// returns the 7-bit material index |
|
virtual int GetTriangleMaterialIndex( int convexIndex, int triangleIndex ); |
|
// sets a 7-bit material index for this triangle |
|
virtual void SetTriangleMaterialIndex( int convexIndex, int triangleIndex, int index7bits ); |
|
|
|
private: |
|
IVP_Compact_Triangle *Triangle( IVP_Compact_Ledge *pLedge, int triangleIndex ); |
|
|
|
IVP_U_BigVector <IVP_Compact_Ledge> m_ledges; |
|
}; |
|
|
|
|
|
// create a queryable version of the collision model |
|
ICollisionQuery *CPhysicsCollision::CreateQueryModel( CPhysCollide *pCollide ) |
|
{ |
|
return new CCollisionQuery( pCollide ); |
|
} |
|
|
|
// destroy the queryable version |
|
void CPhysicsCollision::DestroyQueryModel( ICollisionQuery *pQuery ) |
|
{ |
|
delete pQuery; |
|
} |
|
|
|
|
|
CCollisionQuery::CCollisionQuery( CPhysCollide *pCollide ) |
|
{ |
|
pCollide->GetAllLedges( m_ledges ); |
|
} |
|
|
|
|
|
// number of convex pieces in the whole solid |
|
int CCollisionQuery::ConvexCount( void ) |
|
{ |
|
return m_ledges.len(); |
|
} |
|
|
|
// triangle count for this convex piece |
|
int CCollisionQuery::TriangleCount( int convexIndex ) |
|
{ |
|
IVP_Compact_Ledge *pLedge = m_ledges.element_at(convexIndex); |
|
if ( pLedge ) |
|
{ |
|
return pLedge->get_n_triangles(); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
|
|
unsigned int CCollisionQuery::GetGameData( int convexIndex ) |
|
{ |
|
IVP_Compact_Ledge *pLedge = m_ledges.element_at( convexIndex ); |
|
if ( pLedge ) |
|
return pLedge->get_client_data(); |
|
return 0; |
|
} |
|
|
|
// Gets the triangle's verts to an array |
|
void CCollisionQuery::GetTriangleVerts( int convexIndex, int triangleIndex, Vector *verts ) |
|
{ |
|
IVP_Compact_Ledge *pLedge = m_ledges.element_at( convexIndex ); |
|
IVP_Compact_Triangle *pTriangle = Triangle( pLedge, triangleIndex ); |
|
|
|
int vertIndex = 0; |
|
for ( int k = 2; k >= 0; k-- ) |
|
{ |
|
const IVP_Compact_Edge *pEdge = pTriangle->get_edge( k ); |
|
const IVP_U_Float_Point *pPoint = pEdge->get_start_point( pLedge ); |
|
|
|
Vector* pVec = verts + vertIndex; |
|
ConvertPositionToHL( *pPoint, *pVec ); |
|
vertIndex++; |
|
} |
|
} |
|
|
|
// UNDONE: This doesn't work!!! |
|
void CCollisionQuery::SetTriangleVerts( int convexIndex, int triangleIndex, const Vector *verts ) |
|
{ |
|
IVP_Compact_Ledge *pLedge = m_ledges.element_at( convexIndex ); |
|
Triangle( pLedge, triangleIndex ); |
|
} |
|
|
|
|
|
int CCollisionQuery::GetTriangleMaterialIndex( int convexIndex, int triangleIndex ) |
|
{ |
|
IVP_Compact_Ledge *pLedge = m_ledges.element_at( convexIndex ); |
|
IVP_Compact_Triangle *pTriangle = Triangle( pLedge, triangleIndex ); |
|
|
|
return pTriangle->get_material_index(); |
|
} |
|
|
|
void CCollisionQuery::SetTriangleMaterialIndex( int convexIndex, int triangleIndex, int index7bits ) |
|
{ |
|
IVP_Compact_Ledge *pLedge = m_ledges.element_at( convexIndex ); |
|
IVP_Compact_Triangle *pTriangle = Triangle( pLedge, triangleIndex ); |
|
|
|
pTriangle->set_material_index( index7bits ); |
|
} |
|
|
|
IVP_Compact_Triangle *CCollisionQuery::Triangle( IVP_Compact_Ledge *pLedge, int triangleIndex ) |
|
{ |
|
if ( !pLedge ) |
|
return NULL; |
|
|
|
return pLedge->get_first_triangle() + triangleIndex; |
|
} |
|
|
|
|
|
#if 0 |
|
void TestCubeVolume( void ) |
|
{ |
|
float volume = 0; |
|
Vector verts[8]; |
|
typedef struct |
|
{ |
|
int a, b, c; |
|
} triangle_t; |
|
|
|
triangle_t triangles[12] = |
|
{ |
|
{0,1,3}, // front 0123 |
|
{0,3,2}, |
|
{4,5,1}, // top 4501 |
|
{4,1,0}, |
|
{2,3,7}, // bottom 2367 |
|
{2,7,6}, |
|
{1,5,7}, // right 1537 |
|
{1,7,3}, |
|
{4,0,2}, // left 4062 |
|
{4,2,6}, |
|
{5,4,6}, // back 5476 |
|
{5,6,7} |
|
}; |
|
|
|
int i = 0; |
|
for ( int x = -1; x <= 1; x +=2 ) |
|
for ( int y = -1; y <= 1; y +=2 ) |
|
for ( int z = -1; z <= 1; z +=2 ) |
|
{ |
|
verts[i][0] = x; |
|
verts[i][1] = y; |
|
verts[i][2] = z; |
|
i++; |
|
} |
|
|
|
|
|
for ( i = 0; i < 12; i++ ) |
|
{ |
|
triangle_t *pTri = triangles + i; |
|
volume += TetrahedronVolume( verts[0], verts[pTri->a], verts[pTri->b], verts[pTri->c] ); |
|
} |
|
// should report a volume of 8. This is a cube that is 2 on a side |
|
printf("Test volume %.4f\n", volume ); |
|
} |
|
#endif |
|
|
|
|
|
|