summaryrefslogtreecommitdiff
path: root/vphysics/physics_collide.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /vphysics/physics_collide.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'vphysics/physics_collide.cpp')
-rw-r--r--vphysics/physics_collide.cpp1932
1 files changed, 1932 insertions, 0 deletions
diff --git a/vphysics/physics_collide.cpp b/vphysics/physics_collide.cpp
new file mode 100644
index 0000000..6a6d185
--- /dev/null
+++ b/vphysics/physics_collide.cpp
@@ -0,0 +1,1932 @@
+//========= 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 &params) { 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( &params );
+ 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;
+ memcpy( &size, pBuffer + position, sizeof(int) );
+ position += sizeof(int);
+
+ pOutput->solids[i] = CPhysCollide::UnserializeFromBuffer( pBuffer + position, size, i, swap );
+ position += size;
+ }
+
+ 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
+
+