#include "cbase.h" #include "coordsize.h" #include "mathlib/polyhedron.h" #include "vjolt_parse.h" #include "vjolt_querymodel.h" #include "vjolt_collide.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //------------------------------------------------------------------------------------------------- // Also in vjolt_collide_trace.cpp, should unify or just remove entirely static constexpr float kMaxConvexRadius = JPH::cDefaultConvexRadius; JoltPhysicsCollision JoltPhysicsCollision::s_PhysicsCollision; EXPOSE_SINGLE_INTERFACE_GLOBALVAR( JoltPhysicsCollision, IPhysicsCollision, VPHYSICS_COLLISION_INTERFACE_VERSION, JoltPhysicsCollision::GetInstance() ); //------------------------------------------------------------------------------------------------- // Creates a shape from shape settings, resolving the reference template < typename ShapeType, typename T > ShapeType *ShapeSettingsToShape( const T& settings ) { auto result = settings.Create(); if ( !result.IsValid() ) { // Josh: // Need to handle degenerate convexes and stuff. const char *error = result.HasError() ? result.GetError().c_str() : "Unknown"; Log_Warning( LOG_VJolt, "Failed to create shape: %s.\n", error ); return nullptr; } return static_cast< ShapeType * >( ToDanglingRef( result.Get() ) ); } // Creates a JPH::ConvexShape from shape settings, and casts to a CPhysConvex template < typename T > CPhysConvex *ShapeSettingsToPhysConvex( const T& settings ) { return CPhysConvex::FromConvexShape( ShapeSettingsToShape< JPH::ConvexShape >( settings ) ); } // Creates a JPH::Shape from shape settings, and casts to a CPhysCollide template < typename T > CPhysCollide *ShapeSettingsToPhysCollide( const T& settings ) { return CPhysCollide::FromShape( ShapeSettingsToShape< JPH::Shape >( settings ) ); } //------------------------------------------------------------------------------------------------- CPhysConvex *JoltPhysicsCollision::ConvexFromVerts( Vector **pVerts, int vertCount ) { // This uses this and not a CUtlVector or std::vector or whatever for two reasons: // 1: This is a single allocation we can then toss away instead of growing // 2: We do not want to initialize these vectors on allocation // 3: Automatic deletion when out of scope // Please do not change me to either. std::unique_ptr< JPH::Vec3[] > verts = std::make_unique< JPH::Vec3[] >( vertCount ); for ( int i = 0; i < vertCount; i++ ) verts[ i ] = SourceToJolt::Distance( *pVerts[ i ] ); JPH::ConvexHullShapeSettings settings( verts.get(), vertCount, kMaxConvexRadius, nullptr /* material */ ); settings.mHullTolerance = 0.0f; return ShapeSettingsToPhysConvex( settings ); } CPhysConvex *JoltPhysicsCollision::ConvexFromPlanes( float *pPlanes, int planeCount, float mergeDistance ) { Log_Stub( LOG_VJolt ); return nullptr; } float JoltPhysicsCollision::ConvexVolume( CPhysConvex *pConvex ) { return JoltToSource::Volume( pConvex->ToConvexShape()->GetVolume() ); } //------------------------------------------------------------------------------------------------- float JoltPhysicsCollision::ConvexSurfaceArea( CPhysConvex *pConvex ) { Log_Stub( LOG_VJolt ); return 0.0f; } void JoltPhysicsCollision::SetConvexGameData( CPhysConvex *pConvex, unsigned int gameData ) { pConvex->ToConvexShape()->SetUserData( gameData ); } void JoltPhysicsCollision::ConvexFree( CPhysConvex *pConvex ) { pConvex->ToConvexShape()->Release(); } CPhysConvex *JoltPhysicsCollision::BBoxToConvex( const Vector &mins, const Vector &maxs ) { JPH::AABox aabox = SourceToJolt::AABBBounds( mins, maxs ); JPH::BoxShape *pBoxShape = new JPH::BoxShape( aabox.GetExtent(), kMaxConvexRadius, nullptr /* material */ ); JPH::RotatedTranslatedShapeSettings rotatedSettings( aabox.mMin + aabox.GetExtent(), JPH::Quat::sIdentity(), pBoxShape ); return ShapeSettingsToPhysConvex( rotatedSettings ); } CPhysConvex *JoltPhysicsCollision::ConvexFromConvexPolyhedron( const CPolyhedron &ConvexPolyhedron ) { std::unique_ptr< JPH::Vec3[] > pPoints = std::make_unique< JPH::Vec3[] >( ConvexPolyhedron.iVertexCount ); // This loop fills me with rage for ( unsigned short i = 0; i < ConvexPolyhedron.iVertexCount; ++i ) pPoints[i] = SourceToJolt::Distance( ConvexPolyhedron.pVertices[i] ); JPH::ConvexHullShapeSettings settings( pPoints.get(), ConvexPolyhedron.iVertexCount, kMaxConvexRadius, nullptr /* material */); settings.mHullTolerance = 0.0f; // Slart: Otherwise some polyhedrons crash :( CPhysConvex *pPhysConvex = ShapeSettingsToPhysConvex( settings ); return pPhysConvex; } void JoltPhysicsCollision::ConvexesFromConvexPolygon( const Vector &vPolyNormal, const Vector *pPoints, int iPointCount, CPhysConvex **pOutput ) { // Slart: Unused Log_Stub( LOG_VJolt ); } //------------------------------------------------------------------------------------------------- CPhysPolysoup *JoltPhysicsCollision::PolysoupCreate() { return new CPhysPolysoup; } void JoltPhysicsCollision::PolysoupDestroy( CPhysPolysoup *pSoup ) { delete pSoup; } void JoltPhysicsCollision::PolysoupAddTriangle( CPhysPolysoup *pSoup, const Vector &a, const Vector &b, const Vector &c, int materialIndex7bits ) { // Add both windings to make this two-faced. pSoup->Triangles.push_back( JPH::Triangle( SourceToJolt::DistanceFloat3( c ), SourceToJolt::DistanceFloat3( b ), SourceToJolt::DistanceFloat3( a ) ) ); pSoup->Triangles.push_back( JPH::Triangle( SourceToJolt::DistanceFloat3( a ), SourceToJolt::DistanceFloat3( b ), SourceToJolt::DistanceFloat3( c ) ) ); } CPhysCollide *JoltPhysicsCollision::ConvertPolysoupToCollide( CPhysPolysoup *pSoup, bool useMOPP ) { VJoltAssertMsg( !useMOPP, "MOPPs not supported\n" ); if ( useMOPP ) return nullptr; // ConvertPolysoupToCollide does NOT free the Polysoup. JPH::MeshShapeSettings settings( pSoup->Triangles ); return ShapeSettingsToPhysCollide( settings ); } //------------------------------------------------------------------------------------------------- CPhysCollide *JoltPhysicsCollision::ConvertConvexToCollide( CPhysConvex **pConvex, int convexCount ) { // If we only have one convex shape, we can just use that directly, // without making a compound shape. if ( convexCount == 1 ) return pConvex[0]->ToPhysCollide(); JPH::StaticCompoundShapeSettings settings; for ( int i = 0; i < convexCount; i++ ) settings.AddShape( JPH::Vec3::sZero(), JPH::Quat::sIdentity(), pConvex[i]->ToConvexShape() ); CPhysCollide *pCollide = ShapeSettingsToPhysCollide( settings ); // This function also 'frees' the convexes. for ( int i = 0; i < convexCount; i++ ) pConvex[i]->ToConvexShape()->Release(); return pCollide; } CPhysCollide *JoltPhysicsCollision::ConvertConvexToCollideParams( CPhysConvex **pConvex, int convexCount, const convertconvexparams_t &convertParams ) { // Slart: The parameters are just IVP crap and the only one that isn't is never ever used. HA! return ConvertConvexToCollide( pConvex, convexCount ); } void JoltPhysicsCollision::DestroyCollide( CPhysCollide *pCollide ) { if ( !pCollide ) return; pCollide->ToShape()->Release(); } //------------------------------------------------------------------------------------------------- int JoltPhysicsCollision::CollideSize( CPhysCollide *pCollide ) { Log_Stub( LOG_VJolt ); return 0; } int JoltPhysicsCollision::CollideWrite( char *pDest, CPhysCollide *pCollide, bool bSwap /*= false*/ ) { Log_Stub( LOG_VJolt ); return 0; } CPhysCollide *JoltPhysicsCollision::UnserializeCollide( char *pBuffer, int size, int index ) { Log_Stub( LOG_VJolt ); return nullptr; } //------------------------------------------------------------------------------------------------- float JoltPhysicsCollision::CollideVolume( CPhysCollide *pCollide ) { return JoltToSource::Volume( pCollide->ToShape()->GetVolume() ); } float JoltPhysicsCollision::CollideSurfaceArea( CPhysCollide *pCollide ) { Log_Stub( LOG_VJolt ); return 0.0f; } //------------------------------------------------------------------------------------------------- Vector JoltPhysicsCollision::CollideGetExtent( const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles, const Vector &direction ) { if ( !pCollide ) return collideOrigin; const JPH::Shape *pShape = pCollide->ToShape(); JPH::Vec3 vecDirection = JPH::Vec3( direction.x, direction.y, direction.z ); JPH::Mat44 matCollideTransform = JPH::Mat44::sRotationTranslation( SourceToJolt::Angle( collideAngles ), SourceToJolt::Distance( collideOrigin ) - pShape->GetCenterOfMass() ); float flMaxDot = -FLT_MAX; JPH::Vec3 vecMaxExtent = JPH::Vec3::sZero(); ActOnSubShapes< JPH::ConvexShape >( pShape, [&]( const JPH::ConvexShape* pConvexShape, JPH::Mat44Arg matSubShapeTransform ) { JPH::ConvexShape::SupportingFace supportingFace; pConvexShape->GetSupportingFace( vecDirection, JPH::Vec3::sReplicate( 1.0f ), supportingFace ); for ( const JPH::Vec3 &vecVertex : supportingFace ) { JPH::Vec3 vecTransformedVertex = matCollideTransform * matSubShapeTransform * vecVertex; const float flDot = vecTransformedVertex.Dot( vecDirection ); if ( flDot > flMaxDot ) { vecMaxExtent = vecTransformedVertex; flMaxDot = flDot; } } }); return JoltToSource::Distance( vecMaxExtent ); } //------------------------------------------------------------------------------------------------- void JoltPhysicsCollision::CollideGetAABB( Vector *pMins, Vector *pMaxs, const CPhysCollide *pCollide, const Vector &collideOrigin, const QAngle &collideAngles ) { JPH::Vec3 position = SourceToJolt::Distance( collideOrigin ); JPH::Quat rotation = SourceToJolt::Angle( collideAngles ); const JPH::Shape *pShape = pCollide->ToShape(); const JPH::Mat44 translation = JPH::Mat44::sRotationTranslation( rotation, position + rotation * pShape->GetCenterOfMass() ); const JPH::AABox worldBounds = pShape->GetWorldSpaceBounds( translation, JPH::Vec3::sReplicate( 1.0f ) ); JoltToSource::AABBBounds( worldBounds, *pMins, *pMaxs ); } //------------------------------------------------------------------------------------------------- void JoltPhysicsCollision::CollideGetMassCenter( CPhysCollide *pCollide, Vector *pOutMassCenter ) { *pOutMassCenter = JoltToSource::Distance( pCollide->ToShape()->GetCenterOfMass() ); } void JoltPhysicsCollision::CollideSetMassCenter( CPhysCollide *pCollide, const Vector &massCenter ) { // Slart: Only used in studiomdl Log_Stub( LOG_VJolt ); } Vector JoltPhysicsCollision::CollideGetOrthographicAreas( const CPhysCollide *pCollide ) { // Slart: Only used in studiomdl... In a part that is #if 0'd out... Log_Stub( LOG_VJolt ); return vec3_origin; } void JoltPhysicsCollision::CollideSetOrthographicAreas( CPhysCollide *pCollide, const Vector &areas ) { // Slart: Never used Log_Stub( LOG_VJolt ); } //------------------------------------------------------------------------------------------------- int JoltPhysicsCollision::CollideIndex( const CPhysCollide *pCollide ) { // Slart: Only used by code behind #ifdef _DEBUG Log_Stub( LOG_VJolt ); return 0; } //------------------------------------------------------------------------------------------------- CPhysCollide *JoltPhysicsCollision::BBoxToCollide( const Vector &mins, const Vector &maxs ) { return BBoxToConvex( mins, maxs )->ToPhysCollide(); } int JoltPhysicsCollision::GetConvexesUsedInCollideable( const CPhysCollide *pCollideable, CPhysConvex **pOutputArray, int iOutputArrayLimit ) { const JPH::Shape *pShape = pCollideable->ToShape(); JPH::EShapeType shapeType = pShape->GetType(); if ( shapeType != JPH::EShapeType::Compound ) { pOutputArray[0] = const_cast( CPhysConvex::FromConvexShape( static_cast( pShape ) ) ); return 1; } const JPH::StaticCompoundShape *pCompoundShape = static_cast( pShape ); const JPH::StaticCompoundShape::SubShapes &subShapes = pCompoundShape->GetSubShapes(); const uint maxNumShapes = Min( pCompoundShape->GetNumSubShapes(), iOutputArrayLimit ); for ( uint i = 0; i < maxNumShapes; ++i ) pOutputArray[i] = const_cast( CPhysConvex::FromConvexShape( static_cast( subShapes[i].mShape.GetPtr() ) ) ); return maxNumShapes; } //------------------------------------------------------------------------------------------------- bool JoltPhysicsCollision::IsBoxIntersectingCone( const Vector &boxAbsMins, const Vector &boxAbsMaxs, const truncatedcone_t &cone ) { // Slart: Never used Log_Stub( LOG_VJolt ); return false; } //------------------------------------------------------------------------------------------------- // // See studiobyteswap from the public 2013 SDK for more info about these defines. // https://github.com/ValveSoftware/source-sdk-2013/blob/master/sp/src/common/studiobyteswap.cpp // namespace ivp_compat { struct collideheader_t { int size; int vphysicsID; short version; short modelType; }; struct compactsurfaceheader_t { int surfaceSize; Vector dragAxisAreas; int axisMapSize; }; struct moppsurfaceheader_t { int moppSize; }; struct compactsurface_t { float mass_center[3]; float rotation_inertia[3]; float upper_limit_radius; unsigned int max_factor_surface_deviation : 8; int byte_size : 24; int offset_ledgetree_root; int dummy[3]; }; struct compactmopp_t { float mass_center[3]; float rotation_inertia[3]; float upper_limit_radius; unsigned int max_factor_surface_deviation : 8; int byte_size : 24; int offset_ledgetree_root; int offset_ledges; int size_convex_hull; int dummy; }; struct compactledge_t { int c_point_offset; union { int ledgetree_node_offset; int client_data; }; struct { uint has_children_flag : 2; int is_compact_flag : 2; uint dummy : 4; uint size_div_16 : 24; }; short n_triangles; short for_future_use; }; struct compactedge_t { uint start_point_index : 16; int opposite_index : 15; uint is_virtual : 1; }; struct compacttriangle_t { uint tri_index : 12; uint pierce_index : 12; uint material_index : 7; uint is_virtual : 1; compactedge_t c_three_edges[3]; }; struct compactledgenode_t { int offset_right_node; int offset_compact_ledge; float center[3]; float radius; unsigned char box_sizes[3]; unsigned char free_0; const compactledge_t *GetCompactLedge() const { VJoltAssert( this->offset_right_node == 0 ); return ( compactledge_t * )( ( char * )this + this->offset_compact_ledge ); } const compactledgenode_t *GetLeftChild() const { VJoltAssert( this->offset_right_node ); return this + 1; } const compactledgenode_t *GetRightChild() const { VJoltAssert( this->offset_right_node ); return ( compactledgenode_t * )( ( char * )this + this->offset_right_node ); } bool IsTerminal() const { return this->offset_right_node == 0; } const compactledge_t *GetCompactHull() const { if ( this->offset_compact_ledge ) return ( compactledge_t * )( ( char * )this + this->offset_compact_ledge ); else return nullptr; } }; static constexpr int IVP_COMPACT_SURFACE_ID = MAKEID('I','V','P','S'); static constexpr int IVP_COMPACT_SURFACE_ID_SWAPPED = MAKEID('S','P','V','I'); static constexpr int IVP_COMPACT_MOPP_ID = MAKEID('M','O','P','P'); static constexpr int VPHYSICS_COLLISION_ID = MAKEID('V','P','H','Y'); static constexpr short VPHYSICS_COLLISION_VERSION = 0x0100; enum { COLLIDE_POLY = 0, COLLIDE_MOPP = 1, COLLIDE_BALL = 2, COLLIDE_VIRTUAL = 3, }; JPH::ConvexShape *IVPLedgeToConvexShape( const compactledge_t *pLedge ) { if ( !pLedge->n_triangles ) return nullptr; const char *pVertices = reinterpret_cast< const char * >( pLedge ) + pLedge->c_point_offset; const compacttriangle_t *pTriangles = reinterpret_cast< const compacttriangle_t * >( pLedge + 1 ); const int nVertCount = pLedge->n_triangles * 3; std::unique_ptr< JPH::Vec3[] > verts = std::make_unique< JPH::Vec3[] >( nVertCount ); // Each triangle for ( int i = 0; i < pLedge->n_triangles; i++ ) { // For each point of the current triangle for ( int j = 0; j < 3; j++ ) { static constexpr size_t IVPAlignedVectorSize = 16; const int nIndex = pTriangles[ i ].c_three_edges[ j ].start_point_index; const float *pVertex = reinterpret_cast< const float * >( pVertices + ( nIndex * IVPAlignedVectorSize ) ); verts[ ( i * 3 ) + j ] = JPH::Vec3( pVertex[ 0 ], pVertex[ 2 ], -pVertex[ 1 ] ); } } JPH::ConvexHullShapeSettings settings{ verts.get(), nVertCount, kMaxConvexRadius, nullptr /* material */ }; settings.mHullTolerance = 0.0f; JPH::ConvexShape *pConvexShape = ShapeSettingsToShape< JPH::ConvexShape >( settings ); if ( !pConvexShape ) return nullptr; pConvexShape->SetUserData( pLedge->client_data ); return pConvexShape; } void GetAllIVPEdges( const compactledgenode_t *pNode, CUtlVector< const compactledge_t * >& vecOut ) { if ( !pNode ) return; if ( !pNode->IsTerminal() ) { GetAllIVPEdges( pNode->GetRightChild(), vecOut ); GetAllIVPEdges( pNode->GetLeftChild(), vecOut ); } else vecOut.AddToTail( pNode->GetCompactLedge() ); } CPhysCollide *DeserializeIVP_Poly( const collideheader_t *pCollideHeader ) { const compactsurfaceheader_t *pSurfaceHeader = reinterpret_cast< const compactsurfaceheader_t* >( pCollideHeader + 1 ); const compactsurface_t *pSurface = reinterpret_cast< const compactsurface_t* >( pSurfaceHeader + 1 ); if ( pSurface->dummy[2] != IVP_COMPACT_SURFACE_ID ) return nullptr; const compactledgenode_t *pFirstLedgeNode = reinterpret_cast< const compactledgenode_t * >( reinterpret_cast< const char * >( pSurface ) + pSurface->offset_ledgetree_root ); CUtlVector< const compactledge_t * > ledges; GetAllIVPEdges( pFirstLedgeNode, ledges ); VJoltAssert( !ledges.IsEmpty() ); if ( ledges.Count() != 1 ) { JPH::StaticCompoundShapeSettings settings{}; // One compound convex per ledge. for ( int i = 0; i < ledges.Count(); i++ ) { const JPH::Shape* pShape = IVPLedgeToConvexShape( ledges[i] ); // Josh: // Some models have degenerate convexes which fails to make // a subshape in Jolt, so we need to ignore those ledges. if ( pShape ) settings.AddShape( JPH::Vec3::sZero(), JPH::Quat::sIdentity(), pShape ); } CPhysCollide* pCollide = ShapeSettingsToPhysCollide( settings ); return pCollide; } else { JPH::ConvexShape *pShape = IVPLedgeToConvexShape( ledges[ 0 ] ); return CPhysConvex::FromConvexShape( pShape )->ToPhysCollide(); } } } void JoltPhysicsCollision::VCollideLoad( vcollide_t *pOutput, int solidCount, const char *pBuffer, int size, bool swap /*= false*/ ) { if ( swap ) { Log_Error( LOG_VJolt, "If you got here. Tell me what you did!\n" ); return; } pOutput->solidCount = solidCount; pOutput->solids = new CPhysCollide*[ solidCount ]; const char *pCursor = pBuffer; for ( int i = 0; i < solidCount; i++ ) { // Be safe ahead of time as so much can go wrong with // this mess! :p pOutput->solids[ i ] = nullptr; const ivp_compat::collideheader_t *pCollideHeader = reinterpret_cast( pCursor ); if ( pCollideHeader->vphysicsID != ivp_compat::VPHYSICS_COLLISION_ID || pCollideHeader->version != ivp_compat::VPHYSICS_COLLISION_VERSION ) { Log_Warning( LOG_VJolt, "Skipped solid %d due to invalid header (id: %.4s, version: 0x%x)\n", i, &pCollideHeader->vphysicsID, pCollideHeader->version ); continue; } switch ( pCollideHeader->modelType ) { case ivp_compat::COLLIDE_POLY: pOutput->solids[ i ] = DeserializeIVP_Poly( pCollideHeader ); break; case ivp_compat::COLLIDE_MOPP: Log_Warning( LOG_VJolt, "Unsupported solid type COLLIDE_MOPP on solid %d. Skipping...\n", i ); break; case ivp_compat::COLLIDE_BALL: Log_Warning( LOG_VJolt, "Unsupported solid type COLLIDE_BALL on solid %d. Skipping...\n", i ); break; case ivp_compat::COLLIDE_VIRTUAL: Log_Warning( LOG_VJolt, "Unsupported solid type COLLIDE_VIRTUAL on solid %d. Skipping...\n", i ); break; default: Log_Warning( LOG_VJolt, "Unsupported solid type 0x%x on solid %d. Skipping...\n", (int)pCollideHeader->modelType, i ); break; } // Size does not include the size of "size", ironically! // add sizeof( int ) for that. pCursor += pCollideHeader->size + sizeof( int ); } // The rest of the buffer is KV. const int keyValuesSize = size - ( uintp( pCursor ) - uintp( pBuffer ) ); pOutput->pKeyValues = new char[ keyValuesSize + 1 ]; V_memcpy( pOutput->pKeyValues, pCursor, keyValuesSize ); pOutput->pKeyValues[ keyValuesSize ] = '\0'; pOutput->descSize = keyValuesSize; pOutput->isPacked = false; #ifdef GAME_ASW_OR_NEWER pOutput->pUserData = nullptr; #endif } void JoltPhysicsCollision::VCollideUnload( vcollide_t *pVCollide ) { VCollideFreeUserData( pVCollide ); for ( int i = 0; i < pVCollide->solidCount; i++ ) delete pVCollide->solids[ i ]; delete[] pVCollide->solids; delete[] pVCollide->pKeyValues; V_memset( pVCollide, 0, sizeof( *pVCollide ) ); } //------------------------------------------------------------------------------------------------- IVPhysicsKeyParser *JoltPhysicsCollision::VPhysicsKeyParserCreate( const char *pKeyData ) { return CreateVPhysicsKeyParser( pKeyData, false ); } IVPhysicsKeyParser *JoltPhysicsCollision::VPhysicsKeyParserCreate( vcollide_t *pVCollide ) { return CreateVPhysicsKeyParser( pVCollide->pKeyValues, pVCollide->isPacked ); } void JoltPhysicsCollision::VPhysicsKeyParserDestroy( IVPhysicsKeyParser *pParser ) { DestroyVPhysicsKeyParser( pParser ); } //------------------------------------------------------------------------------------------------- int JoltPhysicsCollision::CreateDebugMesh( CPhysCollide const *pCollisionModel, Vector **outVerts ) { const JPH::Shape *pShape = pCollisionModel->ToShape(); JPH::Shape::VisitedShapes visitedShapes; JPH::Shape::Stats stats = pShape->GetStatsRecursive( visitedShapes ); const int nMaxTriCount = int( stats.mNumTriangles ); const int nRequestCount = Max( nMaxTriCount, JPH::Shape::cGetTrianglesMinTrianglesRequested ); const int nRequestVertCount = nRequestCount * 3; Vector *pVerts = new Vector[ nRequestVertCount ]; JPH::AllHitCollisionCollector collector; JPH::ShapeFilter filter; pShape->CollectTransformedShapes( JPH::AABox::sBiggest(), pShape->GetCenterOfMass() * JoltToSource::Factor, JPH::Quat::sIdentity(), JPH::Vec3::sReplicate( JoltToSource::Factor ), JPH::SubShapeIDCreator(), collector, filter ); int nAccumTris = 0; for ( auto &shape : collector.mHits ) { JPH::Shape::GetTrianglesContext ctx; shape.GetTrianglesStart( ctx, JPH::AABox::sBiggest() ); for ( ;; ) { int nSubShapeTriCount = shape.GetTrianglesNext( ctx, nRequestCount, reinterpret_cast( &pVerts[ nAccumTris * 3 ] ), nullptr /* materials */); if ( nSubShapeTriCount == 0 || nAccumTris + nSubShapeTriCount > nMaxTriCount ) break; nAccumTris += nSubShapeTriCount; } } *outVerts = pVerts; return nAccumTris * 3; } void JoltPhysicsCollision::DestroyDebugMesh( int vertCount, Vector *outVerts ) { delete[] outVerts; } //------------------------------------------------------------------------------------------------- ICollisionQuery *JoltPhysicsCollision::CreateQueryModel( CPhysCollide *pCollide ) { return new JoltCollisionQuery( pCollide->ToShape() ); } void JoltPhysicsCollision::DestroyQueryModel( ICollisionQuery *pQuery ) { delete pQuery; } //------------------------------------------------------------------------------------------------- IPhysicsCollision *JoltPhysicsCollision::ThreadContextCreate() { return this; } void JoltPhysicsCollision::ThreadContextDestroy( IPhysicsCollision *pThreadContex ) { // Does nothing in VPhysics. } //------------------------------------------------------------------------------------------------- CPhysCollide *JoltPhysicsCollision::CreateVirtualMesh( const virtualmeshparams_t ¶ms ) { IVirtualMeshEvent *event = params.pMeshEventHandler; virtualmeshlist_t meshList; event->GetVirtualMesh( params.userData, &meshList ); JPH::VertexList vertexList; vertexList.resize( meshList.vertexCount ); for ( int i = 0; i < meshList.vertexCount; ++i ) vertexList[i] = SourceToJolt::DistanceFloat3( meshList.pVerts[i] ); JPH::IndexedTriangleList indexedTriangleList; indexedTriangleList.resize( meshList.indexCount * 2 ); for ( int i = 0; i < meshList.triangleCount; ++i ) { // Add both windings to make this two-faced. // Probably doesn't matter too much but matches what used to happen. indexedTriangleList[i*2+0].mIdx[0] = meshList.indices[i*3+0]; indexedTriangleList[i*2+0].mIdx[1] = meshList.indices[i*3+1]; indexedTriangleList[i*2+0].mIdx[2] = meshList.indices[i*3+2]; indexedTriangleList[i*2+1].mIdx[2] = meshList.indices[i*3+0]; indexedTriangleList[i*2+1].mIdx[1] = meshList.indices[i*3+1]; indexedTriangleList[i*2+1].mIdx[0] = meshList.indices[i*3+2]; } JPH::MeshShapeSettings settings( vertexList, indexedTriangleList ); return ShapeSettingsToPhysCollide( settings ); } bool JoltPhysicsCollision::SupportsVirtualMesh() { return true; } //------------------------------------------------------------------------------------------------- bool JoltPhysicsCollision::GetBBoxCacheSize( int *pCachedSize, int *pCachedCount ) { // Josh: We don't use a bbox cache as we have box shapes directly, // and this is only used for debug stats. *pCachedSize = 0; *pCachedCount = 0; return true; } //------------------------------------------------------------------------------------------------- CPolyhedron *JoltPhysicsCollision::PolyhedronFromConvex( CPhysConvex * const pConvex, bool bUseTempPolyhedron ) { // This is vile Log_Stub( LOG_VJolt ); return nullptr; } //------------------------------------------------------------------------------------------------- void JoltPhysicsCollision::OutputDebugInfo( const CPhysCollide *pCollide ) { Log_Stub( LOG_VJolt ); } unsigned int JoltPhysicsCollision::ReadStat( int statID ) { // Josh: // This always returns 0 in VPhysics. // It was used by the HL2 Beta physgun at one point for... // something... return 0; } //------------------------------------------------------------------------------------------------- float JoltPhysicsCollision::CollideGetRadius( const CPhysCollide *pCollide ) { return pCollide->ToShape()->GetInnerRadius(); } //------------------------------------------------------------------------------------------------- void *JoltPhysicsCollision::VCollideAllocUserData( vcollide_t *pVCollide, size_t userDataSize ) { #ifdef GAME_ASW_OR_NEWER VCollideFreeUserData( pVCollide ); if ( userDataSize ) pVCollide->pUserData = malloc( userDataSize ); return pVCollide->pUserData; #else return nullptr; #endif } void JoltPhysicsCollision::VCollideFreeUserData( vcollide_t *pVCollide ) { #ifdef GAME_ASW_OR_NEWER if ( pVCollide->pUserData ) { free( pVCollide->pUserData ); pVCollide->pUserData = nullptr; } #endif } void JoltPhysicsCollision::VCollideCheck( vcollide_t *pVCollide, const char *pName ) { // Josh: // A thing to spew warnings about non-optimal solids in IVP. // Entirely useless for us. } bool JoltPhysicsCollision::TraceBoxAA( const Ray_t &ray, const CPhysCollide *pCollide, trace_t *ptr ) { TraceBox( ray, pCollide, vec3_origin, vec3_angle, ptr ); return true; } //------------------------------------------------------------------------------------------------- void JoltPhysicsCollision::DuplicateAndScale( vcollide_t *pOut, const vcollide_t *pIn, float flScale ) { CPhysCollide **pSolids = new CPhysCollide * [pIn->solidCount]; for ( unsigned short i = 0; i < pIn->solidCount; i++ ) { const JPH::Shape* pShape = pIn->solids[i]->ToShape(); pSolids[i] = CPhysCollide::FromShape( ToDanglingRef( pShape->ScaleShape( JPH::Vec3::sReplicate( flScale ) ).Get() ) ); } char *pKeyValues = new char[ pIn->descSize ]; V_memcpy( pKeyValues, pIn->pKeyValues, pIn->descSize ); *pOut = vcollide_t { .solidCount = pIn->solidCount, .isPacked = pIn->isPacked, .descSize = pIn->descSize, .solids = pSolids, .pKeyValues = pKeyValues, #ifdef GAME_ASW_OR_NEWER .pUserData = nullptr, #endif }; } //------------------------------------------------------------------------------------------------- const JPH::Shape *CreateCOMOverrideShape( const JPH::Shape* pShape, JPH::Vec3Arg comOverride ) { JPH::Vec3 comOffset = comOverride - pShape->GetCenterOfMass(); if ( comOffset.IsNearZero() ) return pShape; JPH::OffsetCenterOfMassShapeSettings settings( comOffset, pShape ); return ShapeSettingsToShape< JPH::OffsetCenterOfMassShape >( settings ); }