//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=====================================================================================// #include "cbase.h" #include "PortalSimulation.h" #include "vphysics_interface.h" #include "physics.h" #include "portal_shareddefs.h" #include "StaticCollisionPolyhedronCache.h" #include "model_types.h" #include "filesystem.h" #include "collisionutils.h" #include "tier1/callqueue.h" #ifndef CLIENT_DLL #include "world.h" #include "portal_player.h" //TODO: Move any portal mod specific code to callback functions or something #include "physicsshadowclone.h" #include "portal/weapon_physcannon.h" #include "player_pickup.h" #include "isaverestore.h" #include "hierarchy.h" #include "env_debughistory.h" #else #include "c_world.h" #endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" CCallQueue *GetPortalCallQueue(); extern IPhysicsConstraintEvent *g_pConstraintEvents; static ConVar sv_portal_collision_sim_bounds_x( "sv_portal_collision_sim_bounds_x", "200", FCVAR_REPLICATED, "Size of box used to grab collision geometry around placed portals. These should be at the default size or larger only!" ); static ConVar sv_portal_collision_sim_bounds_y( "sv_portal_collision_sim_bounds_y", "200", FCVAR_REPLICATED, "Size of box used to grab collision geometry around placed portals. These should be at the default size or larger only!" ); static ConVar sv_portal_collision_sim_bounds_z( "sv_portal_collision_sim_bounds_z", "252", FCVAR_REPLICATED, "Size of box used to grab collision geometry around placed portals. These should be at the default size or larger only!" ); //#define DEBUG_PORTAL_SIMULATION_CREATION_TIMES //define to output creation timings to developer 2 //#define DEBUG_PORTAL_COLLISION_ENVIRONMENTS //define this to allow for glview collision dumps of portal simulators #if defined( DEBUG_PORTAL_COLLISION_ENVIRONMENTS ) || defined( DEBUG_PORTAL_SIMULATION_CREATION_TIMES ) # if !defined( PORTAL_SIMULATORS_EMBED_GUID ) # pragma message( __FILE__ "(" __LINE__AS_STRING ") : error custom: Portal simulators require a GUID to debug, enable the GUID in PortalSimulation.h ." ) # endif #endif #if defined( DEBUG_PORTAL_COLLISION_ENVIRONMENTS ) void DumpActiveCollision( const CPortalSimulator *pPortalSimulator, const char *szFileName ); //appends to the existing file if it exists #endif #define PORTAL_WALL_FARDIST 200.0f #define PORTAL_WALL_TUBE_DEPTH 1.0f #define PORTAL_WALL_TUBE_OFFSET 0.01f #define PORTAL_WALL_MIN_THICKNESS 0.1f #define PORTAL_POLYHEDRON_CUT_EPSILON (1.0f/1099511627776.0f) // 1 / (1<<40) #define PORTAL_WORLD_WALL_HALF_SEPARATION_AMOUNT 0.1f //separating the world collision from wall collision by a small amount gets rid of extremely thin erroneous collision at the separating plane #ifdef DEBUG_PORTAL_COLLISION_ENVIRONMENTS static ConVar sv_dump_portalsimulator_collision( "sv_dump_portalsimulator_collision", "0", FCVAR_REPLICATED | FCVAR_CHEAT ); //whether to actually dump out the data now that the possibility exists static void PortalSimulatorDumps_DumpCollideToGlView( CPhysCollide *pCollide, const Vector &origin, const QAngle &angles, float fColorScale, const char *pFilename ); static void PortalSimulatorDumps_DumpBoxToGlView( const Vector &vMins, const Vector &vMaxs, float fRed, float fGreen, float fBlue, const char *pszFileName ); #endif #ifdef DEBUG_PORTAL_SIMULATION_CREATION_TIMES #define STARTDEBUGTIMER(x) { x.Start(); } #define STOPDEBUGTIMER(x) { x.End(); } #define DEBUGTIMERONLY(x) x #define CREATEDEBUGTIMER(x) CFastTimer x; static const char *s_szTabSpacing[] = { "", "\t", "\t\t", "\t\t\t", "\t\t\t\t", "\t\t\t\t\t", "\t\t\t\t\t\t", "\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t\t\t" }; static int s_iTabSpacingIndex = 0; static int s_iPortalSimulatorGUID = 0; //used in standalone function that have no idea what a portal simulator is #define INCREMENTTABSPACING() ++s_iTabSpacingIndex; #define DECREMENTTABSPACING() --s_iTabSpacingIndex; #define TABSPACING (s_szTabSpacing[s_iTabSpacingIndex]) #else #define STARTDEBUGTIMER(x) #define STOPDEBUGTIMER(x) #define DEBUGTIMERONLY(x) #define CREATEDEBUGTIMER(x) #define INCREMENTTABSPACING() #define DECREMENTTABSPACING() #define TABSPACING #endif #define PORTAL_HOLE_HALF_HEIGHT (PORTAL_HALF_HEIGHT + 0.1f) #define PORTAL_HOLE_HALF_WIDTH (PORTAL_HALF_WIDTH + 0.1f) static void ConvertBrushListToClippedPolyhedronList( const int *pBrushes, int iBrushCount, const float *pOutwardFacingClipPlanes, int iClipPlaneCount, float fClipEpsilon, CUtlVector *pPolyhedronList ); static void ClipPolyhedrons( CPolyhedron * const *pExistingPolyhedrons, int iPolyhedronCount, const float *pOutwardFacingClipPlanes, int iClipPlaneCount, float fClipEpsilon, CUtlVector *pPolyhedronList ); static inline CPolyhedron *TransformAndClipSinglePolyhedron( CPolyhedron *pExistingPolyhedron, const VMatrix &Transform, const float *pOutwardFacingClipPlanes, int iClipPlaneCount, float fCutEpsilon, bool bUseTempMemory ); static int GetEntityPhysicsObjects( IPhysicsEnvironment *pEnvironment, CBaseEntity *pEntity, IPhysicsObject **pRetList, int iRetListArraySize ); static CPhysCollide *ConvertPolyhedronsToCollideable( CPolyhedron **pPolyhedrons, int iPolyhedronCount ); #ifndef CLIENT_DLL static void UpdateShadowClonesPortalSimulationFlags( const CBaseEntity *pSourceEntity, unsigned int iFlags, int iSourceFlags ); static bool g_bPlayerIsInSimulator = false; #endif static CUtlVector s_PortalSimulators; CUtlVector const &g_PortalSimulators = s_PortalSimulators; static CPortalSimulator *s_OwnedEntityMap[MAX_EDICTS] = { NULL }; static CPortalSimulatorEventCallbacks s_DummyPortalSimulatorCallback; const char *PS_SD_Static_World_StaticProps_ClippedProp_t::szTraceSurfaceName = "**studio**"; const int PS_SD_Static_World_StaticProps_ClippedProp_t::iTraceSurfaceFlags = 0; CBaseEntity *PS_SD_Static_World_StaticProps_ClippedProp_t::pTraceEntity = NULL; CPortalSimulator::CPortalSimulator( void ) : m_bLocalDataIsReady(false), m_bGenerateCollision(true), m_bSimulateVPhysics(true), m_bSharedCollisionConfiguration(false), m_pLinkedPortal(NULL), m_bInCrossLinkedFunction(false), m_pCallbacks(&s_DummyPortalSimulatorCallback), m_DataAccess(m_InternalData) { s_PortalSimulators.AddToTail( this ); #ifdef CLIENT_DLL m_bGenerateCollision = (GameRules() && GameRules()->IsMultiplayer()); #endif m_CreationChecklist.bPolyhedronsGenerated = false; m_CreationChecklist.bLocalCollisionGenerated = false; m_CreationChecklist.bLinkedCollisionGenerated = false; m_CreationChecklist.bLocalPhysicsGenerated = false; m_CreationChecklist.bLinkedPhysicsGenerated = false; #ifdef PORTAL_SIMULATORS_EMBED_GUID static int s_iPortalSimulatorGUIDAllocator = 0; m_iPortalSimulatorGUID = s_iPortalSimulatorGUIDAllocator++; #endif #ifndef CLIENT_DLL PS_SD_Static_World_StaticProps_ClippedProp_t::pTraceEntity = GetWorldEntity(); //will overinitialize, but it's cheap m_InternalData.Simulation.pCollisionEntity = (CPSCollisionEntity *)CreateEntityByName( "portalsimulator_collisionentity" ); Assert( m_InternalData.Simulation.pCollisionEntity != NULL ); if( m_InternalData.Simulation.pCollisionEntity ) { m_InternalData.Simulation.pCollisionEntity->m_pOwningSimulator = this; MarkAsOwned( m_InternalData.Simulation.pCollisionEntity ); m_InternalData.Simulation.Dynamic.EntFlags[m_InternalData.Simulation.pCollisionEntity->entindex()] |= PSEF_OWNS_PHYSICS; DispatchSpawn( m_InternalData.Simulation.pCollisionEntity ); } #else PS_SD_Static_World_StaticProps_ClippedProp_t::pTraceEntity = GetClientWorldEntity(); #endif } CPortalSimulator::~CPortalSimulator( void ) { //go assert crazy here DetachFromLinked(); ClearEverything(); for( int i = s_PortalSimulators.Count(); --i >= 0; ) { if( s_PortalSimulators[i] == this ) { s_PortalSimulators.FastRemove( i ); break; } } if( m_InternalData.Placement.pHoleShapeCollideable ) physcollision->DestroyCollide( m_InternalData.Placement.pHoleShapeCollideable ); #ifndef CLIENT_DLL if( m_InternalData.Simulation.pCollisionEntity ) { m_InternalData.Simulation.pCollisionEntity->m_pOwningSimulator = NULL; m_InternalData.Simulation.Dynamic.EntFlags[m_InternalData.Simulation.pCollisionEntity->entindex()] &= ~PSEF_OWNS_PHYSICS; MarkAsReleased( m_InternalData.Simulation.pCollisionEntity ); UTIL_Remove( m_InternalData.Simulation.pCollisionEntity ); m_InternalData.Simulation.pCollisionEntity = NULL; } #endif } void CPortalSimulator::MoveTo( const Vector &ptCenter, const QAngle &angles ) { if( (m_InternalData.Placement.ptCenter == ptCenter) && (m_InternalData.Placement.qAngles == angles) ) //not actually moving at all return; CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::MoveTo() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); INCREMENTTABSPACING(); #ifndef CLIENT_DLL //create a list of all entities that are actually within the portal hole, they will likely need to be moved out of solid space when the portal moves CBaseEntity **pFixEntities = (CBaseEntity **)stackalloc( sizeof( CBaseEntity * ) * m_InternalData.Simulation.Dynamic.OwnedEntities.Count() ); int iFixEntityCount = 0; for( int i = m_InternalData.Simulation.Dynamic.OwnedEntities.Count(); --i >= 0; ) { CBaseEntity *pEntity = m_InternalData.Simulation.Dynamic.OwnedEntities[i]; if( CPhysicsShadowClone::IsShadowClone( pEntity ) || CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pEntity ) ) continue; if( EntityIsInPortalHole( pEntity) ) { pFixEntities[iFixEntityCount] = pEntity; ++iFixEntityCount; } } VPlane OldPlane = m_InternalData.Placement.PortalPlane; //used in fixing code #endif //update geometric data { m_InternalData.Placement.ptCenter = ptCenter; m_InternalData.Placement.qAngles = angles; AngleVectors( angles, &m_InternalData.Placement.vForward, &m_InternalData.Placement.vRight, &m_InternalData.Placement.vUp ); m_InternalData.Placement.PortalPlane.Init( m_InternalData.Placement.vForward, m_InternalData.Placement.vForward.Dot( m_InternalData.Placement.ptCenter ) ); } //Clear(); #ifndef CLIENT_DLL ClearLinkedPhysics(); ClearLocalPhysics(); #endif ClearLinkedCollision(); ClearLocalCollision(); ClearPolyhedrons(); m_bLocalDataIsReady = true; UpdateLinkMatrix(); //update hole shape - used to detect if an entity is within the portal hole bounds { if( m_InternalData.Placement.pHoleShapeCollideable ) physcollision->DestroyCollide( m_InternalData.Placement.pHoleShapeCollideable ); float fHolePlanes[6*4]; //first and second planes are always forward and backward planes fHolePlanes[(0*4) + 0] = m_InternalData.Placement.PortalPlane.m_Normal.x; fHolePlanes[(0*4) + 1] = m_InternalData.Placement.PortalPlane.m_Normal.y; fHolePlanes[(0*4) + 2] = m_InternalData.Placement.PortalPlane.m_Normal.z; fHolePlanes[(0*4) + 3] = m_InternalData.Placement.PortalPlane.m_Dist - 0.5f; fHolePlanes[(1*4) + 0] = -m_InternalData.Placement.PortalPlane.m_Normal.x; fHolePlanes[(1*4) + 1] = -m_InternalData.Placement.PortalPlane.m_Normal.y; fHolePlanes[(1*4) + 2] = -m_InternalData.Placement.PortalPlane.m_Normal.z; fHolePlanes[(1*4) + 3] = (-m_InternalData.Placement.PortalPlane.m_Dist) + 500.0f; //the remaining planes will always have the same ordering of normals, with different distances plugged in for each convex we're creating //normal order is up, down, left, right fHolePlanes[(2*4) + 0] = m_InternalData.Placement.vUp.x; fHolePlanes[(2*4) + 1] = m_InternalData.Placement.vUp.y; fHolePlanes[(2*4) + 2] = m_InternalData.Placement.vUp.z; fHolePlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + (m_InternalData.Placement.vUp * (PORTAL_HALF_HEIGHT * 0.98f)) ); fHolePlanes[(3*4) + 0] = -m_InternalData.Placement.vUp.x; fHolePlanes[(3*4) + 1] = -m_InternalData.Placement.vUp.y; fHolePlanes[(3*4) + 2] = -m_InternalData.Placement.vUp.z; fHolePlanes[(3*4) + 3] = -m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter - (m_InternalData.Placement.vUp * (PORTAL_HALF_HEIGHT * 0.98f)) ); fHolePlanes[(4*4) + 0] = -m_InternalData.Placement.vRight.x; fHolePlanes[(4*4) + 1] = -m_InternalData.Placement.vRight.y; fHolePlanes[(4*4) + 2] = -m_InternalData.Placement.vRight.z; fHolePlanes[(4*4) + 3] = -m_InternalData.Placement.vRight.Dot( m_InternalData.Placement.ptCenter - (m_InternalData.Placement.vRight * (PORTAL_HALF_WIDTH * 0.98f)) ); fHolePlanes[(5*4) + 0] = m_InternalData.Placement.vRight.x; fHolePlanes[(5*4) + 1] = m_InternalData.Placement.vRight.y; fHolePlanes[(5*4) + 2] = m_InternalData.Placement.vRight.z; fHolePlanes[(5*4) + 3] = m_InternalData.Placement.vRight.Dot( m_InternalData.Placement.ptCenter + (m_InternalData.Placement.vRight * (PORTAL_HALF_WIDTH * 0.98f)) ); CPolyhedron *pPolyhedron = GeneratePolyhedronFromPlanes( fHolePlanes, 6, PORTAL_POLYHEDRON_CUT_EPSILON, true ); Assert( pPolyhedron != NULL ); CPhysConvex *pConvex = physcollision->ConvexFromConvexPolyhedron( *pPolyhedron ); pPolyhedron->Release(); Assert( pConvex != NULL ); m_InternalData.Placement.pHoleShapeCollideable = physcollision->ConvertConvexToCollide( &pConvex, 1 ); } #ifndef CLIENT_DLL for( int i = 0; i != iFixEntityCount; ++i ) { if( !EntityIsInPortalHole( pFixEntities[i] ) ) { //this entity is most definitely stuck in a solid wall right now //pFixEntities[i]->SetAbsOrigin( pFixEntities[i]->GetAbsOrigin() + (OldPlane.m_Normal * 50.0f) ); FindClosestPassableSpace( pFixEntities[i], OldPlane.m_Normal ); continue; } //entity is still in the hole, but it's possible the hole moved enough where they're in part of the wall { //TODO: figure out if that's the case and fix it } } #endif CreatePolyhedrons(); CreateAllCollision(); #ifndef CLIENT_DLL CreateAllPhysics(); #endif #if defined( DEBUG_PORTAL_COLLISION_ENVIRONMENTS ) && !defined( CLIENT_DLL ) if( sv_dump_portalsimulator_collision.GetBool() ) { const char *szFileName = "pscd.txt"; filesystem->RemoveFile( szFileName ); DumpActiveCollision( this, szFileName ); if( m_pLinkedPortal ) { szFileName = "pscd_linked.txt"; filesystem->RemoveFile( szFileName ); DumpActiveCollision( m_pLinkedPortal, szFileName ); } } #endif #ifndef CLIENT_DLL Assert( (m_InternalData.Simulation.pCollisionEntity == NULL) || OwnsEntity(m_InternalData.Simulation.pCollisionEntity) ); #endif STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::MoveTo() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); } void CPortalSimulator::UpdateLinkMatrix( void ) { if( m_pLinkedPortal && m_pLinkedPortal->m_bLocalDataIsReady ) { Vector vLocalLeft = -m_InternalData.Placement.vRight; VMatrix matLocalToWorld( m_InternalData.Placement.vForward, vLocalLeft, m_InternalData.Placement.vUp ); matLocalToWorld.SetTranslation( m_InternalData.Placement.ptCenter ); VMatrix matLocalToWorldInverse; MatrixInverseTR( matLocalToWorld,matLocalToWorldInverse ); //180 degree rotation about up VMatrix matRotation; matRotation.Identity(); matRotation.m[0][0] = -1.0f; matRotation.m[1][1] = -1.0f; Vector vRemoteLeft = -m_pLinkedPortal->m_InternalData.Placement.vRight; VMatrix matRemoteToWorld( m_pLinkedPortal->m_InternalData.Placement.vForward, vRemoteLeft, m_pLinkedPortal->m_InternalData.Placement.vUp ); matRemoteToWorld.SetTranslation( m_pLinkedPortal->m_InternalData.Placement.ptCenter ); //final m_InternalData.Placement.matThisToLinked = matRemoteToWorld * matRotation * matLocalToWorldInverse; } else { m_InternalData.Placement.matThisToLinked.Identity(); } m_InternalData.Placement.matThisToLinked.InverseTR( m_InternalData.Placement.matLinkedToThis ); MatrixAngles( m_InternalData.Placement.matThisToLinked.As3x4(), m_InternalData.Placement.ptaap_ThisToLinked.qAngleTransform, m_InternalData.Placement.ptaap_ThisToLinked.ptOriginTransform ); MatrixAngles( m_InternalData.Placement.matLinkedToThis.As3x4(), m_InternalData.Placement.ptaap_LinkedToThis.qAngleTransform, m_InternalData.Placement.ptaap_LinkedToThis.ptOriginTransform ); if( m_pLinkedPortal && (m_pLinkedPortal->m_bInCrossLinkedFunction == false) ) { Assert( m_bInCrossLinkedFunction == false ); //I'm pretty sure switching to a stack would have negative repercussions m_bInCrossLinkedFunction = true; m_pLinkedPortal->UpdateLinkMatrix(); m_bInCrossLinkedFunction = false; } } #ifdef DEBUG_PORTAL_COLLISION_ENVIRONMENTS static ConVar sv_debug_dumpportalhole_nextcheck( "sv_debug_dumpportalhole_nextcheck", "0", FCVAR_CHEAT | FCVAR_REPLICATED ); #endif bool CPortalSimulator::EntityIsInPortalHole( CBaseEntity *pEntity ) const { if( m_bLocalDataIsReady == false ) return false; Assert( m_InternalData.Placement.pHoleShapeCollideable != NULL ); #ifdef DEBUG_PORTAL_COLLISION_ENVIRONMENTS const char *szDumpFileName = "ps_entholecheck.txt"; if( sv_debug_dumpportalhole_nextcheck.GetBool() ) { filesystem->RemoveFile( szDumpFileName ); DumpActiveCollision( this, szDumpFileName ); PortalSimulatorDumps_DumpCollideToGlView( m_InternalData.Placement.pHoleShapeCollideable, vec3_origin, vec3_angle, 1.0f, szDumpFileName ); } #endif trace_t Trace; switch( pEntity->GetSolid() ) { case SOLID_VPHYSICS: { ICollideable *pCollideable = pEntity->GetCollideable(); vcollide_t *pVCollide = modelinfo->GetVCollide( pCollideable->GetCollisionModel() ); //Assert( pVCollide != NULL ); //brush models? if( pVCollide != NULL ) { Vector ptEntityPosition = pCollideable->GetCollisionOrigin(); QAngle qEntityAngles = pCollideable->GetCollisionAngles(); #ifdef DEBUG_PORTAL_COLLISION_ENVIRONMENTS if( sv_debug_dumpportalhole_nextcheck.GetBool() ) { for( int i = 0; i != pVCollide->solidCount; ++i ) PortalSimulatorDumps_DumpCollideToGlView( m_InternalData.Placement.pHoleShapeCollideable, vec3_origin, vec3_angle, 0.4f, szDumpFileName ); sv_debug_dumpportalhole_nextcheck.SetValue( false ); } #endif for( int i = 0; i != pVCollide->solidCount; ++i ) { physcollision->TraceCollide( ptEntityPosition, ptEntityPosition, pVCollide->solids[i], qEntityAngles, m_InternalData.Placement.pHoleShapeCollideable, vec3_origin, vec3_angle, &Trace ); if( Trace.startsolid ) return true; } } else { //energy balls lack a vcollide Vector vMins, vMaxs, ptCenter; pCollideable->WorldSpaceSurroundingBounds( &vMins, &vMaxs ); ptCenter = (vMins + vMaxs) * 0.5f; vMins -= ptCenter; vMaxs -= ptCenter; physcollision->TraceBox( ptCenter, ptCenter, vMins, vMaxs, m_InternalData.Placement.pHoleShapeCollideable, vec3_origin, vec3_angle, &Trace ); return Trace.startsolid; } break; } case SOLID_BBOX: { Vector ptEntityPosition = pEntity->GetAbsOrigin(); CCollisionProperty *pCollisionProp = pEntity->CollisionProp(); physcollision->TraceBox( ptEntityPosition, ptEntityPosition, pCollisionProp->OBBMins(), pCollisionProp->OBBMaxs(), m_InternalData.Placement.pHoleShapeCollideable, vec3_origin, vec3_angle, &Trace ); #ifdef DEBUG_PORTAL_COLLISION_ENVIRONMENTS if( sv_debug_dumpportalhole_nextcheck.GetBool() ) { Vector vMins = ptEntityPosition + pCollisionProp->OBBMins(); Vector vMaxs = ptEntityPosition + pCollisionProp->OBBMaxs(); PortalSimulatorDumps_DumpBoxToGlView( vMins, vMaxs, 1.0f, 1.0f, 1.0f, szDumpFileName ); sv_debug_dumpportalhole_nextcheck.SetValue( false ); } #endif if( Trace.startsolid ) return true; break; } case SOLID_NONE: #ifdef DEBUG_PORTAL_COLLISION_ENVIRONMENTS if( sv_debug_dumpportalhole_nextcheck.GetBool() ) sv_debug_dumpportalhole_nextcheck.SetValue( false ); #endif return false; default: Assert( false ); //make a handler }; #ifdef DEBUG_PORTAL_COLLISION_ENVIRONMENTS if( sv_debug_dumpportalhole_nextcheck.GetBool() ) sv_debug_dumpportalhole_nextcheck.SetValue( false ); #endif return false; } bool CPortalSimulator::EntityHitBoxExtentIsInPortalHole( CBaseAnimating *pBaseAnimating ) const { if( m_bLocalDataIsReady == false ) return false; bool bFirstVert = true; Vector vMinExtent; Vector vMaxExtent; CStudioHdr *pStudioHdr = pBaseAnimating->GetModelPtr(); if ( !pStudioHdr ) return false; mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( pBaseAnimating->m_nHitboxSet ); if ( !set ) return false; Vector position; QAngle angles; for ( int i = 0; i < set->numhitboxes; i++ ) { mstudiobbox_t *pbox = set->pHitbox( i ); pBaseAnimating->GetBonePosition( pbox->bone, position, angles ); // Build a rotation matrix from orientation matrix3x4_t fRotateMatrix; AngleMatrix( angles, fRotateMatrix ); //Vector pVerts[8]; Vector vecPos; for ( int i = 0; i < 8; ++i ) { vecPos[0] = ( i & 0x1 ) ? pbox->bbmax[0] : pbox->bbmin[0]; vecPos[1] = ( i & 0x2 ) ? pbox->bbmax[1] : pbox->bbmin[1]; vecPos[2] = ( i & 0x4 ) ? pbox->bbmax[2] : pbox->bbmin[2]; Vector vRotVec; VectorRotate( vecPos, fRotateMatrix, vRotVec ); vRotVec += position; if ( bFirstVert ) { vMinExtent = vRotVec; vMaxExtent = vRotVec; bFirstVert = false; } else { vMinExtent = vMinExtent.Min( vRotVec ); vMaxExtent = vMaxExtent.Max( vRotVec ); } } } Vector ptCenter = (vMinExtent + vMaxExtent) * 0.5f; vMinExtent -= ptCenter; vMaxExtent -= ptCenter; trace_t Trace; physcollision->TraceBox( ptCenter, ptCenter, vMinExtent, vMaxExtent, m_InternalData.Placement.pHoleShapeCollideable, vec3_origin, vec3_angle, &Trace ); if( Trace.startsolid ) return true; return false; } void CPortalSimulator::RemoveEntityFromPortalHole( CBaseEntity *pEntity ) { if( EntityIsInPortalHole( pEntity ) ) { FindClosestPassableSpace( pEntity, m_InternalData.Placement.PortalPlane.m_Normal ); } } bool CPortalSimulator::RayIsInPortalHole( const Ray_t &ray ) const { trace_t Trace; physcollision->TraceBox( ray, m_InternalData.Placement.pHoleShapeCollideable, vec3_origin, vec3_angle, &Trace ); return Trace.DidHit(); } void CPortalSimulator::ClearEverything( void ) { CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::Clear() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); INCREMENTTABSPACING(); #ifndef CLIENT_DLL ClearAllPhysics(); #endif ClearAllCollision(); ClearPolyhedrons(); #ifndef CLIENT_DLL ReleaseAllEntityOwnership(); Assert( (m_InternalData.Simulation.pCollisionEntity == NULL) || OwnsEntity(m_InternalData.Simulation.pCollisionEntity) ); #endif STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::Clear() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); } void CPortalSimulator::AttachTo( CPortalSimulator *pLinkedPortalSimulator ) { Assert( pLinkedPortalSimulator ); if( pLinkedPortalSimulator == m_pLinkedPortal ) return; CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::AttachTo() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); INCREMENTTABSPACING(); DetachFromLinked(); m_pLinkedPortal = pLinkedPortalSimulator; pLinkedPortalSimulator->m_pLinkedPortal = this; if( m_bLocalDataIsReady && m_pLinkedPortal->m_bLocalDataIsReady ) { UpdateLinkMatrix(); CreateLinkedCollision(); #ifndef CLIENT_DLL CreateLinkedPhysics(); #endif } #if defined( DEBUG_PORTAL_COLLISION_ENVIRONMENTS ) && !defined( CLIENT_DLL ) if( sv_dump_portalsimulator_collision.GetBool() ) { const char *szFileName = "pscd.txt"; filesystem->RemoveFile( szFileName ); DumpActiveCollision( this, szFileName ); if( m_pLinkedPortal ) { szFileName = "pscd_linked.txt"; filesystem->RemoveFile( szFileName ); DumpActiveCollision( m_pLinkedPortal, szFileName ); } } #endif STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::AttachTo() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); } #ifndef CLIENT_DLL void CPortalSimulator::TakeOwnershipOfEntity( CBaseEntity *pEntity ) { AssertMsg( m_bLocalDataIsReady, "Tell the portal simulator where it is with MoveTo() before using it in any other way." ); Assert( pEntity != NULL ); if( pEntity == NULL ) return; if( pEntity->IsWorld() ) return; if( CPhysicsShadowClone::IsShadowClone( pEntity ) ) return; if( pEntity->GetServerVehicle() != NULL ) //we don't take kindly to vehicles in these here parts. Their physics controllers currently don't migrate properly and cause a crash return; if( OwnsEntity( pEntity ) ) return; Assert( GetSimulatorThatOwnsEntity( pEntity ) == NULL ); MarkAsOwned( pEntity ); Assert( GetSimulatorThatOwnsEntity( pEntity ) == this ); if( EntityIsInPortalHole( pEntity ) ) m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] |= PSEF_IS_IN_PORTAL_HOLE; else m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] &= ~PSEF_IS_IN_PORTAL_HOLE; UpdateShadowClonesPortalSimulationFlags( pEntity, PSEF_IS_IN_PORTAL_HOLE, m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] ); m_pCallbacks->PortalSimulator_TookOwnershipOfEntity( pEntity ); if( IsSimulatingVPhysics() ) TakePhysicsOwnership( pEntity ); pEntity->CollisionRulesChanged(); //absolutely necessary in single-environment mode, possibly expendable in multi-environment moder //pEntity->SetGroundEntity( NULL ); IPhysicsObject *pObject = pEntity->VPhysicsGetObject(); if( pObject ) { pObject->Wake(); pObject->RecheckContactPoints(); } CUtlVector childrenList; GetAllChildren( pEntity, childrenList ); for ( int i = childrenList.Count(); --i >= 0; ) { CBaseEntity *pEnt = childrenList[i]; CPortalSimulator *pOwningSimulator = GetSimulatorThatOwnsEntity( pEnt ); if( pOwningSimulator != this ) { if( pOwningSimulator != NULL ) pOwningSimulator->ReleaseOwnershipOfEntity( pEnt, (pOwningSimulator == m_pLinkedPortal) ); TakeOwnershipOfEntity( childrenList[i] ); } } } void CPortalSimulator::TakePhysicsOwnership( CBaseEntity *pEntity ) { if( m_InternalData.Simulation.pPhysicsEnvironment == NULL ) return; if( CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pEntity ) ) return; Assert( CPhysicsShadowClone::IsShadowClone( pEntity ) == false ); Assert( OwnsEntity( pEntity ) ); //taking physics ownership happens AFTER general ownership if( OwnsPhysicsForEntity( pEntity ) ) return; int iEntIndex = pEntity->entindex(); m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] |= PSEF_OWNS_PHYSICS; //physics cloning { #ifdef _DEBUG { int iDebugIndex; for( iDebugIndex = m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count(); --iDebugIndex >= 0; ) { if( m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[iDebugIndex]->GetClonedEntity() == pEntity ) break; } AssertMsg( iDebugIndex < 0, "Trying to own an entity, when a clone from the linked portal already exists" ); if( m_pLinkedPortal ) { for( iDebugIndex = m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count(); --iDebugIndex >= 0; ) { if( m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[iDebugIndex]->GetClonedEntity() == pEntity ) break; } AssertMsg( iDebugIndex < 0, "Trying to own an entity, when we're already exporting a clone to the linked portal" ); } //Don't require a copy from main to already exist } #endif EHANDLE hEnt = pEntity; //To linked portal if( m_pLinkedPortal && m_pLinkedPortal->m_InternalData.Simulation.pPhysicsEnvironment ) { DBG_CODE( for( int i = m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count(); --i >= 0; ) AssertMsg( m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[i]->GetClonedEntity() != pEntity, "Already cloning to linked portal." ); ); CPhysicsShadowClone *pClone = CPhysicsShadowClone::CreateShadowClone( m_pLinkedPortal->m_InternalData.Simulation.pPhysicsEnvironment, hEnt, "CPortalSimulator::TakePhysicsOwnership(): To Linked Portal", &m_InternalData.Placement.matThisToLinked.As3x4() ); if( pClone ) { //bool bHeldByPhyscannon = false; CBaseEntity *pHeldEntity = NULL; CPortal_Player *pPlayer = (CPortal_Player *)GetPlayerHoldingEntity( pEntity ); if ( !pPlayer && pEntity->IsPlayer() ) { pPlayer = (CPortal_Player *)pEntity; } if ( pPlayer ) { pHeldEntity = GetPlayerHeldEntity( pPlayer ); /*if ( !pHeldEntity ) { pHeldEntity = PhysCannonGetHeldEntity( pPlayer->GetActiveWeapon() ); bHeldByPhyscannon = true; }*/ } if( pHeldEntity ) { //player is holding the entity, force them to pick it back up again bool bIsHeldObjectOnOppositeSideOfPortal = pPlayer->IsHeldObjectOnOppositeSideOfPortal(); pPlayer->m_bSilentDropAndPickup = true; pPlayer->ForceDropOfCarriedPhysObjects( pHeldEntity ); pPlayer->SetHeldObjectOnOppositeSideOfPortal( bIsHeldObjectOnOppositeSideOfPortal ); } m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.AddToTail( pClone ); m_pLinkedPortal->MarkAsOwned( pClone ); m_pLinkedPortal->m_InternalData.Simulation.Dynamic.EntFlags[pClone->entindex()] |= PSEF_OWNS_PHYSICS; m_pLinkedPortal->m_InternalData.Simulation.Dynamic.EntFlags[pClone->entindex()] |= m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] & PSEF_IS_IN_PORTAL_HOLE; pClone->CollisionRulesChanged(); //adding the clone to the portal simulator changes how it collides if( pHeldEntity ) { /*if ( bHeldByPhyscannon ) { PhysCannonPickupObject( pPlayer, pHeldEntity ); } else*/ { PlayerPickupObject( pPlayer, pHeldEntity ); } pPlayer->m_bSilentDropAndPickup = false; } } } } m_pCallbacks->PortalSimulator_TookPhysicsOwnershipOfEntity( pEntity ); } void RecheckEntityCollision( CBaseEntity *pEntity ) { CCallQueue *pCallQueue; if ( (pCallQueue = GetPortalCallQueue()) != NULL ) { pCallQueue->QueueCall( RecheckEntityCollision, pEntity ); return; } pEntity->CollisionRulesChanged(); //absolutely necessary in single-environment mode, possibly expendable in multi-environment mode //pEntity->SetGroundEntity( NULL ); IPhysicsObject *pObject = pEntity->VPhysicsGetObject(); if( pObject ) { pObject->Wake(); pObject->RecheckContactPoints(); } } void CPortalSimulator::ReleaseOwnershipOfEntity( CBaseEntity *pEntity, bool bMovingToLinkedSimulator /*= false*/ ) { if( pEntity == NULL ) return; if( pEntity->IsWorld() ) return; if( !OwnsEntity( pEntity ) ) return; if( m_InternalData.Simulation.pPhysicsEnvironment ) ReleasePhysicsOwnership( pEntity, true, bMovingToLinkedSimulator ); m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] &= ~PSEF_IS_IN_PORTAL_HOLE; UpdateShadowClonesPortalSimulationFlags( pEntity, PSEF_IS_IN_PORTAL_HOLE, m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] ); Assert( GetSimulatorThatOwnsEntity( pEntity ) == this ); MarkAsReleased( pEntity ); Assert( GetSimulatorThatOwnsEntity( pEntity ) == NULL ); for( int i = m_InternalData.Simulation.Dynamic.OwnedEntities.Count(); --i >= 0; ) { if( m_InternalData.Simulation.Dynamic.OwnedEntities[i] == pEntity ) { m_InternalData.Simulation.Dynamic.OwnedEntities.FastRemove(i); break; } } if( bMovingToLinkedSimulator == false ) { RecheckEntityCollision( pEntity ); } m_pCallbacks->PortalSimulator_ReleasedOwnershipOfEntity( pEntity ); CUtlVector childrenList; GetAllChildren( pEntity, childrenList ); for ( int i = childrenList.Count(); --i >= 0; ) ReleaseOwnershipOfEntity( childrenList[i] ); } void CPortalSimulator::ReleaseAllEntityOwnership( void ) { //Assert( m_bLocalDataIsReady || (m_InternalData.Simulation.Dynamic.OwnedEntities.Count() == 0) ); int iSkippedObjects = 0; while( m_InternalData.Simulation.Dynamic.OwnedEntities.Count() != iSkippedObjects ) //the release function changes OwnedEntities { CBaseEntity *pEntity = m_InternalData.Simulation.Dynamic.OwnedEntities[iSkippedObjects]; if( CPhysicsShadowClone::IsShadowClone( pEntity ) || CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pEntity ) ) { ++iSkippedObjects; continue; } RemoveEntityFromPortalHole( pEntity ); //assume that whenever someone wants to release all entities, it's because the portal is going away ReleaseOwnershipOfEntity( pEntity ); } Assert( (m_InternalData.Simulation.pCollisionEntity == NULL) || OwnsEntity(m_InternalData.Simulation.pCollisionEntity) ); } void CPortalSimulator::ReleasePhysicsOwnership( CBaseEntity *pEntity, bool bContinuePhysicsCloning /*= true*/, bool bMovingToLinkedSimulator /*= false*/ ) { if( CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pEntity ) ) return; Assert( OwnsEntity( pEntity ) ); //releasing physics ownership happens BEFORE releasing general ownership Assert( CPhysicsShadowClone::IsShadowClone( pEntity ) == false ); if( m_InternalData.Simulation.pPhysicsEnvironment == NULL ) return; if( !OwnsPhysicsForEntity( pEntity ) ) return; if( IsSimulatingVPhysics() == false ) bContinuePhysicsCloning = false; int iEntIndex = pEntity->entindex(); m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] &= ~PSEF_OWNS_PHYSICS; //physics cloning { #ifdef _DEBUG { int iDebugIndex; for( iDebugIndex = m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count(); --iDebugIndex >= 0; ) { if( m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[iDebugIndex]->GetClonedEntity() == pEntity ) break; } AssertMsg( iDebugIndex < 0, "Trying to release an entity, when a clone from the linked portal already exists." ); } #endif //clear exported clones { DBG_CODE_NOSCOPE( bool bFoundAlready = false; ); DBG_CODE_NOSCOPE( const char *szLastFoundMarker = NULL; ); //to linked portal if( m_pLinkedPortal ) { DBG_CODE_NOSCOPE( bFoundAlready = false; ); for( int i = m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count(); --i >= 0; ) { if( m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[i]->GetClonedEntity() == pEntity ) { CPhysicsShadowClone *pClone = m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[i]; AssertMsg( bFoundAlready == false, "Multiple clones to linked portal found." ); DBG_CODE_NOSCOPE( bFoundAlready = true; ); DBG_CODE_NOSCOPE( szLastFoundMarker = pClone->m_szDebugMarker ); //bool bHeldByPhyscannon = false; CBaseEntity *pHeldEntity = NULL; CPortal_Player *pPlayer = (CPortal_Player *)GetPlayerHoldingEntity( pEntity ); if ( !pPlayer && pEntity->IsPlayer() ) { pPlayer = (CPortal_Player *)pEntity; } if ( pPlayer ) { pHeldEntity = GetPlayerHeldEntity( pPlayer ); /*if ( !pHeldEntity ) { pHeldEntity = PhysCannonGetHeldEntity( pPlayer->GetActiveWeapon() ); bHeldByPhyscannon = true; }*/ } if( pHeldEntity ) { //player is holding the entity, force them to pick it back up again bool bIsHeldObjectOnOppositeSideOfPortal = pPlayer->IsHeldObjectOnOppositeSideOfPortal(); pPlayer->m_bSilentDropAndPickup = true; pPlayer->ForceDropOfCarriedPhysObjects( pHeldEntity ); pPlayer->SetHeldObjectOnOppositeSideOfPortal( bIsHeldObjectOnOppositeSideOfPortal ); } else { pHeldEntity = NULL; } m_pLinkedPortal->m_InternalData.Simulation.Dynamic.EntFlags[pClone->entindex()] &= ~PSEF_OWNS_PHYSICS; m_pLinkedPortal->MarkAsReleased( pClone ); pClone->Free(); m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.FastRemove(i); if( pHeldEntity ) { /*if ( bHeldByPhyscannon ) { PhysCannonPickupObject( pPlayer, pHeldEntity ); } else*/ { PlayerPickupObject( pPlayer, pHeldEntity ); } pPlayer->m_bSilentDropAndPickup = false; } DBG_CODE_NOSCOPE( continue; ); break; } } } } } m_pCallbacks->PortalSimulator_ReleasedPhysicsOwnershipOfEntity( pEntity ); } void CPortalSimulator::StartCloningEntity( CBaseEntity *pEntity ) { if( CPhysicsShadowClone::IsShadowClone( pEntity ) || CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pEntity ) ) return; if( (m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] & PSEF_CLONES_ENTITY_FROM_MAIN) != 0 ) return; //already cloned, no work to do #ifdef _DEBUG for( int i = m_InternalData.Simulation.Dynamic.ShadowClones.ShouldCloneFromMain.Count(); --i >= 0; ) Assert( m_InternalData.Simulation.Dynamic.ShadowClones.ShouldCloneFromMain[i] != pEntity ); #endif //NDebugOverlay::EntityBounds( pEntity, 0, 255, 0, 50, 5.0f ); m_InternalData.Simulation.Dynamic.ShadowClones.ShouldCloneFromMain.AddToTail( pEntity ); m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] |= PSEF_CLONES_ENTITY_FROM_MAIN; } void CPortalSimulator::StopCloningEntity( CBaseEntity *pEntity ) { if( (m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] & PSEF_CLONES_ENTITY_FROM_MAIN) == 0 ) { Assert( m_InternalData.Simulation.Dynamic.ShadowClones.ShouldCloneFromMain.Find( pEntity ) == -1 ); return; //not cloned, no work to do } //NDebugOverlay::EntityBounds( pEntity, 255, 0, 0, 50, 5.0f ); m_InternalData.Simulation.Dynamic.ShadowClones.ShouldCloneFromMain.FastRemove(m_InternalData.Simulation.Dynamic.ShadowClones.ShouldCloneFromMain.Find( pEntity )); m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] &= ~PSEF_CLONES_ENTITY_FROM_MAIN; } /*void CPortalSimulator::TeleportEntityToLinkedPortal( CBaseEntity *pEntity ) { //TODO: migrate teleportation code from CProp_Portal::Touch to here }*/ void CPortalSimulator::MarkAsOwned( CBaseEntity *pEntity ) { Assert( pEntity != NULL ); int iEntIndex = pEntity->entindex(); Assert( s_OwnedEntityMap[iEntIndex] == NULL ); #ifdef _DEBUG for( int i = m_InternalData.Simulation.Dynamic.OwnedEntities.Count(); --i >= 0; ) Assert( m_InternalData.Simulation.Dynamic.OwnedEntities[i] != pEntity ); #endif Assert( (m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] & PSEF_OWNS_ENTITY) == 0 ); m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] |= PSEF_OWNS_ENTITY; s_OwnedEntityMap[iEntIndex] = this; m_InternalData.Simulation.Dynamic.OwnedEntities.AddToTail( pEntity ); if ( pEntity->IsPlayer() ) { g_bPlayerIsInSimulator = true; } } void CPortalSimulator::MarkAsReleased( CBaseEntity *pEntity ) { Assert( pEntity != NULL ); int iEntIndex = pEntity->entindex(); Assert( s_OwnedEntityMap[iEntIndex] == this ); Assert( ((m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] & PSEF_OWNS_ENTITY) != 0) || CPSCollisionEntity::IsPortalSimulatorCollisionEntity(pEntity) ); s_OwnedEntityMap[iEntIndex] = NULL; m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] &= ~PSEF_OWNS_ENTITY; int i; for( i = m_InternalData.Simulation.Dynamic.OwnedEntities.Count(); --i >= 0; ) { if( m_InternalData.Simulation.Dynamic.OwnedEntities[i] == pEntity ) { m_InternalData.Simulation.Dynamic.OwnedEntities.FastRemove(i); break; } } Assert( i >= 0 ); if ( pEntity->IsPlayer() ) { g_bPlayerIsInSimulator = false; } } void CPortalSimulator::CreateAllPhysics( void ) { if( IsSimulatingVPhysics() == false ) return; CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateAllPhysics() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); INCREMENTTABSPACING(); CreateMinimumPhysics(); CreateLocalPhysics(); CreateLinkedPhysics(); STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateAllPhysics() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); } void CPortalSimulator::CreateMinimumPhysics( void ) { if( IsSimulatingVPhysics() == false ) return; if( m_InternalData.Simulation.pPhysicsEnvironment != NULL ) return; CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateMinimumPhysics() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); INCREMENTTABSPACING(); m_InternalData.Simulation.pPhysicsEnvironment = physenv_main; STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateMinimumPhysics() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); } void CPortalSimulator::CreateLocalPhysics( void ) { if( IsSimulatingVPhysics() == false ) return; AssertMsg( m_bLocalDataIsReady, "Portal simulator attempting to create local physics before being placed." ); if( m_CreationChecklist.bLocalPhysicsGenerated ) return; CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateLocalPhysics() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); INCREMENTTABSPACING(); CreateMinimumPhysics(); //int iDefaultSurfaceIndex = physprops->GetSurfaceIndex( "default" ); objectparams_t params = g_PhysDefaultObjectParams; // Any non-moving object can point to world safely-- Make sure we dont use 'params' for something other than that beyond this point. if( m_InternalData.Simulation.pCollisionEntity ) params.pGameData = m_InternalData.Simulation.pCollisionEntity; else GetWorldEntity(); //World { Assert( m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject == NULL ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation if( m_InternalData.Simulation.Static.World.Brushes.pCollideable != NULL ) { m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject = m_InternalData.Simulation.pPhysicsEnvironment->CreatePolyObjectStatic( m_InternalData.Simulation.Static.World.Brushes.pCollideable, m_InternalData.Simulation.Static.SurfaceProperties.surface.surfaceProps, vec3_origin, vec3_angle, ¶ms ); if( (m_InternalData.Simulation.pCollisionEntity != NULL) && (m_InternalData.Simulation.pCollisionEntity->VPhysicsGetObject() == NULL) ) m_InternalData.Simulation.pCollisionEntity->VPhysicsSetObject(m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject); m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject->RecheckCollisionFilter(); //some filters only work after the variable is stored in the class } //Assert( m_InternalData.Simulation.Static.World.StaticProps.PhysicsObjects.Count() == 0 ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation #ifdef _DEBUG for( int i = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) { Assert( m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[i].pPhysicsObject == NULL ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation } #endif if( m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count() != 0 ) { Assert( m_InternalData.Simulation.Static.World.StaticProps.bCollisionExists ); for( int i = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) { PS_SD_Static_World_StaticProps_ClippedProp_t &Representation = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[i]; Assert( Representation.pCollide != NULL ); Assert( Representation.pPhysicsObject == NULL ); Representation.pPhysicsObject = m_InternalData.Simulation.pPhysicsEnvironment->CreatePolyObjectStatic( Representation.pCollide, Representation.iTraceSurfaceProps, vec3_origin, vec3_angle, ¶ms ); Assert( Representation.pPhysicsObject != NULL ); Representation.pPhysicsObject->RecheckCollisionFilter(); //some filters only work after the variable is stored in the class } } m_InternalData.Simulation.Static.World.StaticProps.bPhysicsExists = true; } //Wall { Assert( m_InternalData.Simulation.Static.Wall.Local.Brushes.pPhysicsObject == NULL ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation if( m_InternalData.Simulation.Static.Wall.Local.Brushes.pCollideable != NULL ) { m_InternalData.Simulation.Static.Wall.Local.Brushes.pPhysicsObject = m_InternalData.Simulation.pPhysicsEnvironment->CreatePolyObjectStatic( m_InternalData.Simulation.Static.Wall.Local.Brushes.pCollideable, m_InternalData.Simulation.Static.SurfaceProperties.surface.surfaceProps, vec3_origin, vec3_angle, ¶ms ); if( (m_InternalData.Simulation.pCollisionEntity != NULL) && (m_InternalData.Simulation.pCollisionEntity->VPhysicsGetObject() == NULL) ) m_InternalData.Simulation.pCollisionEntity->VPhysicsSetObject(m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject); m_InternalData.Simulation.Static.Wall.Local.Brushes.pPhysicsObject->RecheckCollisionFilter(); //some filters only work after the variable is stored in the class } Assert( m_InternalData.Simulation.Static.Wall.Local.Tube.pPhysicsObject == NULL ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation if( m_InternalData.Simulation.Static.Wall.Local.Tube.pCollideable != NULL ) { m_InternalData.Simulation.Static.Wall.Local.Tube.pPhysicsObject = m_InternalData.Simulation.pPhysicsEnvironment->CreatePolyObjectStatic( m_InternalData.Simulation.Static.Wall.Local.Tube.pCollideable, m_InternalData.Simulation.Static.SurfaceProperties.surface.surfaceProps, vec3_origin, vec3_angle, ¶ms ); if( (m_InternalData.Simulation.pCollisionEntity != NULL) && (m_InternalData.Simulation.pCollisionEntity->VPhysicsGetObject() == NULL) ) m_InternalData.Simulation.pCollisionEntity->VPhysicsSetObject(m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject); m_InternalData.Simulation.Static.Wall.Local.Tube.pPhysicsObject->RecheckCollisionFilter(); //some filters only work after the variable is stored in the class } } //re-acquire environment physics for owned entities for( int i = m_InternalData.Simulation.Dynamic.OwnedEntities.Count(); --i >= 0; ) TakePhysicsOwnership( m_InternalData.Simulation.Dynamic.OwnedEntities[i] ); if( m_InternalData.Simulation.pCollisionEntity ) m_InternalData.Simulation.pCollisionEntity->CollisionRulesChanged(); STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateLocalPhysics() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); m_CreationChecklist.bLocalPhysicsGenerated = true; } void CPortalSimulator::CreateLinkedPhysics( void ) { if( IsSimulatingVPhysics() == false ) return; AssertMsg( m_bLocalDataIsReady, "Portal simulator attempting to create linked physics before being placed itself." ); if( (m_pLinkedPortal == NULL) || (m_pLinkedPortal->m_bLocalDataIsReady == false) ) return; if( m_CreationChecklist.bLinkedPhysicsGenerated ) return; CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateLinkedPhysics() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); INCREMENTTABSPACING(); CreateMinimumPhysics(); //int iDefaultSurfaceIndex = physprops->GetSurfaceIndex( "default" ); objectparams_t params = g_PhysDefaultObjectParams; if( m_InternalData.Simulation.pCollisionEntity ) params.pGameData = m_InternalData.Simulation.pCollisionEntity; else params.pGameData = GetWorldEntity(); //everything in our linked collision should be based on the linked portal's world collision PS_SD_Static_World_t &RemoteSimulationStaticWorld = m_pLinkedPortal->m_InternalData.Simulation.Static.World; Assert( m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject == NULL ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation if( RemoteSimulationStaticWorld.Brushes.pCollideable != NULL ) { m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject = m_InternalData.Simulation.pPhysicsEnvironment->CreatePolyObjectStatic( RemoteSimulationStaticWorld.Brushes.pCollideable, m_pLinkedPortal->m_InternalData.Simulation.Static.SurfaceProperties.surface.surfaceProps, m_InternalData.Placement.ptaap_LinkedToThis.ptOriginTransform, m_InternalData.Placement.ptaap_LinkedToThis.qAngleTransform, ¶ms ); m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject->RecheckCollisionFilter(); //some filters only work after the variable is stored in the class } Assert( m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.PhysicsObjects.Count() == 0 ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation if( RemoteSimulationStaticWorld.StaticProps.ClippedRepresentations.Count() != 0 ) { for( int i = RemoteSimulationStaticWorld.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) { PS_SD_Static_World_StaticProps_ClippedProp_t &Representation = RemoteSimulationStaticWorld.StaticProps.ClippedRepresentations[i]; IPhysicsObject *pPhysObject = m_InternalData.Simulation.pPhysicsEnvironment->CreatePolyObjectStatic( Representation.pCollide, Representation.iTraceSurfaceProps, m_InternalData.Placement.ptaap_LinkedToThis.ptOriginTransform, m_InternalData.Placement.ptaap_LinkedToThis.qAngleTransform, ¶ms ); if( pPhysObject ) { m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.PhysicsObjects.AddToTail( pPhysObject ); pPhysObject->RecheckCollisionFilter(); //some filters only work after the variable is stored in the class } } } //re-clone physicsshadowclones from the remote environment CUtlVector &RemoteOwnedEntities = m_pLinkedPortal->m_InternalData.Simulation.Dynamic.OwnedEntities; for( int i = RemoteOwnedEntities.Count(); --i >= 0; ) { if( CPhysicsShadowClone::IsShadowClone( RemoteOwnedEntities[i] ) || CPSCollisionEntity::IsPortalSimulatorCollisionEntity( RemoteOwnedEntities[i] ) ) continue; int j; for( j = m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count(); --j >= 0; ) { if( m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[j]->GetClonedEntity() == RemoteOwnedEntities[i] ) break; } if( j >= 0 ) //already cloning continue; EHANDLE hEnt = RemoteOwnedEntities[i]; CPhysicsShadowClone *pClone = CPhysicsShadowClone::CreateShadowClone( m_InternalData.Simulation.pPhysicsEnvironment, hEnt, "CPortalSimulator::CreateLinkedPhysics(): From Linked Portal", &m_InternalData.Placement.matLinkedToThis.As3x4() ); if( pClone ) { MarkAsOwned( pClone ); m_InternalData.Simulation.Dynamic.EntFlags[pClone->entindex()] |= PSEF_OWNS_PHYSICS; m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.AddToTail( pClone ); pClone->CollisionRulesChanged(); //adding the clone to the portal simulator changes how it collides } } if( m_pLinkedPortal && (m_pLinkedPortal->m_bInCrossLinkedFunction == false) ) { Assert( m_bInCrossLinkedFunction == false ); //I'm pretty sure switching to a stack would have negative repercussions m_bInCrossLinkedFunction = true; m_pLinkedPortal->CreateLinkedPhysics(); m_bInCrossLinkedFunction = false; } if( m_InternalData.Simulation.pCollisionEntity ) m_InternalData.Simulation.pCollisionEntity->CollisionRulesChanged(); STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateLinkedPhysics() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); m_CreationChecklist.bLinkedPhysicsGenerated = true; } void CPortalSimulator::ClearAllPhysics( void ) { CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearAllPhysics() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); INCREMENTTABSPACING(); ClearLinkedPhysics(); ClearLocalPhysics(); ClearMinimumPhysics(); STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearAllPhysics() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); } void CPortalSimulator::ClearMinimumPhysics( void ) { if( m_InternalData.Simulation.pPhysicsEnvironment == NULL ) return; CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearMinimumPhysics() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); INCREMENTTABSPACING(); m_InternalData.Simulation.pPhysicsEnvironment = NULL; STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearMinimumPhysics() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); } void CPortalSimulator::ClearLocalPhysics( void ) { if( m_CreationChecklist.bLocalPhysicsGenerated == false ) return; if( m_InternalData.Simulation.pPhysicsEnvironment == NULL ) return; CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearLocalPhysics() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); INCREMENTTABSPACING(); m_InternalData.Simulation.pPhysicsEnvironment->CleanupDeleteList(); m_InternalData.Simulation.pPhysicsEnvironment->SetQuickDelete( true ); //if we don't do this, things crash the next time we cleanup the delete list while checking mindists if( m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject ) { m_InternalData.Simulation.pPhysicsEnvironment->DestroyObject( m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject ); m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject = NULL; } if( m_InternalData.Simulation.Static.World.StaticProps.bPhysicsExists && (m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count() != 0) ) { for( int i = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) { PS_SD_Static_World_StaticProps_ClippedProp_t &Representation = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[i]; if( Representation.pPhysicsObject ) { m_InternalData.Simulation.pPhysicsEnvironment->DestroyObject( Representation.pPhysicsObject ); Representation.pPhysicsObject = NULL; } } } m_InternalData.Simulation.Static.World.StaticProps.bPhysicsExists = false; if( m_InternalData.Simulation.Static.Wall.Local.Brushes.pPhysicsObject ) { m_InternalData.Simulation.pPhysicsEnvironment->DestroyObject( m_InternalData.Simulation.Static.Wall.Local.Brushes.pPhysicsObject ); m_InternalData.Simulation.Static.Wall.Local.Brushes.pPhysicsObject = NULL; } if( m_InternalData.Simulation.Static.Wall.Local.Tube.pPhysicsObject ) { m_InternalData.Simulation.pPhysicsEnvironment->DestroyObject( m_InternalData.Simulation.Static.Wall.Local.Tube.pPhysicsObject ); m_InternalData.Simulation.Static.Wall.Local.Tube.pPhysicsObject = NULL; } //all physics clones { for( int i = m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count(); --i >= 0; ) { CPhysicsShadowClone *pClone = m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[i]; Assert( GetSimulatorThatOwnsEntity( pClone ) == this ); m_InternalData.Simulation.Dynamic.EntFlags[pClone->entindex()] &= ~PSEF_OWNS_PHYSICS; MarkAsReleased( pClone ); Assert( GetSimulatorThatOwnsEntity( pClone ) == NULL ); pClone->Free(); } m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.RemoveAll(); } Assert( m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count() == 0 ); //release physics ownership of owned entities for( int i = m_InternalData.Simulation.Dynamic.OwnedEntities.Count(); --i >= 0; ) ReleasePhysicsOwnership( m_InternalData.Simulation.Dynamic.OwnedEntities[i], false ); Assert( m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count() == 0 ); m_InternalData.Simulation.pPhysicsEnvironment->CleanupDeleteList(); m_InternalData.Simulation.pPhysicsEnvironment->SetQuickDelete( false ); if( m_InternalData.Simulation.pCollisionEntity ) m_InternalData.Simulation.pCollisionEntity->CollisionRulesChanged(); STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearLocalPhysics() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); m_CreationChecklist.bLocalPhysicsGenerated = false; } void CPortalSimulator::ClearLinkedPhysics( void ) { if( m_CreationChecklist.bLinkedPhysicsGenerated == false ) return; if( m_InternalData.Simulation.pPhysicsEnvironment == NULL ) return; CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearLinkedPhysics() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); INCREMENTTABSPACING(); m_InternalData.Simulation.pPhysicsEnvironment->CleanupDeleteList(); m_InternalData.Simulation.pPhysicsEnvironment->SetQuickDelete( true ); //if we don't do this, things crash the next time we cleanup the delete list while checking mindists //static collideables { if( m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject ) { m_InternalData.Simulation.pPhysicsEnvironment->DestroyObject( m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject ); m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject = NULL; } if( m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.PhysicsObjects.Count() ) { for( int i = m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.PhysicsObjects.Count(); --i >= 0; ) m_InternalData.Simulation.pPhysicsEnvironment->DestroyObject( m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.PhysicsObjects[i] ); m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.PhysicsObjects.RemoveAll(); } } //clones from the linked portal { for( int i = m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count(); --i >= 0; ) { CPhysicsShadowClone *pClone = m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[i]; m_InternalData.Simulation.Dynamic.EntFlags[pClone->entindex()] &= ~PSEF_OWNS_PHYSICS; MarkAsReleased( pClone ); pClone->Free(); } m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.RemoveAll(); } if( m_pLinkedPortal && (m_pLinkedPortal->m_bInCrossLinkedFunction == false) ) { Assert( m_bInCrossLinkedFunction == false ); //I'm pretty sure switching to a stack would have negative repercussions m_bInCrossLinkedFunction = true; m_pLinkedPortal->ClearLinkedPhysics(); m_bInCrossLinkedFunction = false; } Assert( (m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count() == 0) && ((m_pLinkedPortal == NULL) || (m_pLinkedPortal->m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count() == 0)) ); m_InternalData.Simulation.pPhysicsEnvironment->CleanupDeleteList(); m_InternalData.Simulation.pPhysicsEnvironment->SetQuickDelete( false ); if( m_InternalData.Simulation.pCollisionEntity ) m_InternalData.Simulation.pCollisionEntity->CollisionRulesChanged(); STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearLinkedPhysics() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); m_CreationChecklist.bLinkedPhysicsGenerated = false; } void CPortalSimulator::ClearLinkedEntities( void ) { //clones from the linked portal { for( int i = m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.Count(); --i >= 0; ) { CPhysicsShadowClone *pClone = m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal[i]; m_InternalData.Simulation.Dynamic.EntFlags[pClone->entindex()] &= ~PSEF_OWNS_PHYSICS; MarkAsReleased( pClone ); pClone->Free(); } m_InternalData.Simulation.Dynamic.ShadowClones.FromLinkedPortal.RemoveAll(); } } #endif //#ifndef CLIENT_DLL void CPortalSimulator::CreateAllCollision( void ) { CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateAllCollision() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); INCREMENTTABSPACING(); CreateLocalCollision(); CreateLinkedCollision(); STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateAllCollision() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); } void CPortalSimulator::CreateLocalCollision( void ) { AssertMsg( m_bLocalDataIsReady, "Portal simulator attempting to create local collision before being placed." ); if( m_CreationChecklist.bLocalCollisionGenerated ) return; if( IsCollisionGenerationEnabled() == false ) return; DEBUGTIMERONLY( s_iPortalSimulatorGUID = GetPortalSimulatorGUID() ); CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateLocalCollision() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); INCREMENTTABSPACING(); CREATEDEBUGTIMER( worldBrushTimer ); STARTDEBUGTIMER( worldBrushTimer ); Assert( m_InternalData.Simulation.Static.World.Brushes.pCollideable == NULL ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation if( m_InternalData.Simulation.Static.World.Brushes.Polyhedrons.Count() != 0 ) m_InternalData.Simulation.Static.World.Brushes.pCollideable = ConvertPolyhedronsToCollideable( m_InternalData.Simulation.Static.World.Brushes.Polyhedrons.Base(), m_InternalData.Simulation.Static.World.Brushes.Polyhedrons.Count() ); STOPDEBUGTIMER( worldBrushTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sWorld Brushes=%fms\n", GetPortalSimulatorGUID(), TABSPACING, worldBrushTimer.GetDuration().GetMillisecondsF() ); ); CREATEDEBUGTIMER( worldPropTimer ); STARTDEBUGTIMER( worldPropTimer ); #ifdef _DEBUG for( int i = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) { Assert( m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[i].pCollide == NULL ); } #endif Assert( m_InternalData.Simulation.Static.World.StaticProps.bCollisionExists == false ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation if( m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count() != 0 ) { Assert( m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons.Count() != 0 ); CPolyhedron **pPolyhedronsBase = m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons.Base(); for( int i = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) { PS_SD_Static_World_StaticProps_ClippedProp_t &Representation = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[i]; Assert( Representation.pCollide == NULL ); Representation.pCollide = ConvertPolyhedronsToCollideable( &pPolyhedronsBase[Representation.PolyhedronGroup.iStartIndex], Representation.PolyhedronGroup.iNumPolyhedrons ); Assert( Representation.pCollide != NULL ); } } m_InternalData.Simulation.Static.World.StaticProps.bCollisionExists = true; STOPDEBUGTIMER( worldPropTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sWorld Props=%fms\n", GetPortalSimulatorGUID(), TABSPACING, worldPropTimer.GetDuration().GetMillisecondsF() ); ); if( IsSimulatingVPhysics() ) { //only need the tube when simulating player movement //TODO: replace the complete wall with the wall shell CREATEDEBUGTIMER( wallBrushTimer ); STARTDEBUGTIMER( wallBrushTimer ); Assert( m_InternalData.Simulation.Static.Wall.Local.Brushes.pCollideable == NULL ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation if( m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons.Count() != 0 ) m_InternalData.Simulation.Static.Wall.Local.Brushes.pCollideable = ConvertPolyhedronsToCollideable( m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons.Base(), m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons.Count() ); STOPDEBUGTIMER( wallBrushTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sWall Brushes=%fms\n", GetPortalSimulatorGUID(), TABSPACING, wallBrushTimer.GetDuration().GetMillisecondsF() ); ); } CREATEDEBUGTIMER( wallTubeTimer ); STARTDEBUGTIMER( wallTubeTimer ); Assert( m_InternalData.Simulation.Static.Wall.Local.Tube.pCollideable == NULL ); //Be sure to find graceful fixes for asserts, performance is a big concern with portal simulation if( m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.Count() != 0 ) m_InternalData.Simulation.Static.Wall.Local.Tube.pCollideable = ConvertPolyhedronsToCollideable( m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.Base(), m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.Count() ); STOPDEBUGTIMER( wallTubeTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sWall Tube=%fms\n", GetPortalSimulatorGUID(), TABSPACING, wallTubeTimer.GetDuration().GetMillisecondsF() ); ); //grab surface properties to use for the portal environment { CTraceFilterWorldAndPropsOnly filter; trace_t Trace; UTIL_TraceLine( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vForward, m_InternalData.Placement.ptCenter - (m_InternalData.Placement.vForward * 500.0f), MASK_SOLID_BRUSHONLY, &filter, &Trace ); if( Trace.fraction != 1.0f ) { m_InternalData.Simulation.Static.SurfaceProperties.contents = Trace.contents; m_InternalData.Simulation.Static.SurfaceProperties.surface = Trace.surface; m_InternalData.Simulation.Static.SurfaceProperties.pEntity = Trace.m_pEnt; } else { m_InternalData.Simulation.Static.SurfaceProperties.contents = CONTENTS_SOLID; m_InternalData.Simulation.Static.SurfaceProperties.surface.name = "**empty**"; m_InternalData.Simulation.Static.SurfaceProperties.surface.flags = 0; m_InternalData.Simulation.Static.SurfaceProperties.surface.surfaceProps = 0; #ifndef CLIENT_DLL m_InternalData.Simulation.Static.SurfaceProperties.pEntity = GetWorldEntity(); #else m_InternalData.Simulation.Static.SurfaceProperties.pEntity = GetClientWorldEntity(); #endif } #ifndef CLIENT_DLL if( m_InternalData.Simulation.pCollisionEntity ) m_InternalData.Simulation.Static.SurfaceProperties.pEntity = m_InternalData.Simulation.pCollisionEntity; #endif } STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreateLocalCollision() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); m_CreationChecklist.bLocalCollisionGenerated = true; } void CPortalSimulator::CreateLinkedCollision( void ) { if( m_CreationChecklist.bLinkedCollisionGenerated ) return; if( IsCollisionGenerationEnabled() == false ) return; //nothing to do for now, the current set of collision is just transformed from the linked simulator when needed. It's really cheap to transform in traces and physics generation. m_CreationChecklist.bLinkedCollisionGenerated = true; } void CPortalSimulator::ClearAllCollision( void ) { CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearAllCollision() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); INCREMENTTABSPACING(); ClearLinkedCollision(); ClearLocalCollision(); STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearAllCollision() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); } void CPortalSimulator::ClearLinkedCollision( void ) { if( m_CreationChecklist.bLinkedCollisionGenerated == false ) return; //nothing to do for now, the current set of collision is just transformed from the linked simulator when needed. It's really cheap to transform in traces and physics generation. m_CreationChecklist.bLinkedCollisionGenerated = false; } void CPortalSimulator::ClearLocalCollision( void ) { if( m_CreationChecklist.bLocalCollisionGenerated == false ) return; CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearLocalCollision() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); INCREMENTTABSPACING(); if( m_InternalData.Simulation.Static.Wall.Local.Brushes.pCollideable ) { physcollision->DestroyCollide( m_InternalData.Simulation.Static.Wall.Local.Brushes.pCollideable ); m_InternalData.Simulation.Static.Wall.Local.Brushes.pCollideable = NULL; } if( m_InternalData.Simulation.Static.Wall.Local.Tube.pCollideable ) { physcollision->DestroyCollide( m_InternalData.Simulation.Static.Wall.Local.Tube.pCollideable ); m_InternalData.Simulation.Static.Wall.Local.Tube.pCollideable = NULL; } if( m_InternalData.Simulation.Static.World.Brushes.pCollideable ) { physcollision->DestroyCollide( m_InternalData.Simulation.Static.World.Brushes.pCollideable ); m_InternalData.Simulation.Static.World.Brushes.pCollideable = NULL; } if( m_InternalData.Simulation.Static.World.StaticProps.bCollisionExists && (m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count() != 0) ) { for( int i = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) { PS_SD_Static_World_StaticProps_ClippedProp_t &Representation = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[i]; if( Representation.pCollide ) { physcollision->DestroyCollide( Representation.pCollide ); Representation.pCollide = NULL; } } } m_InternalData.Simulation.Static.World.StaticProps.bCollisionExists = false; STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearLocalCollision() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); m_CreationChecklist.bLocalCollisionGenerated = false; } void CPortalSimulator::CreatePolyhedrons( void ) { if( m_CreationChecklist.bPolyhedronsGenerated ) return; if( IsCollisionGenerationEnabled() == false ) return; CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreatePolyhedrons() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); INCREMENTTABSPACING(); //forward reverse conventions signify whether the normal is the same direction as m_InternalData.Placement.PortalPlane.m_Normal //World and wall conventions signify whether it's been shifted in front of the portal plane or behind it float fWorldClipPlane_Forward[4] = { m_InternalData.Placement.PortalPlane.m_Normal.x, m_InternalData.Placement.PortalPlane.m_Normal.y, m_InternalData.Placement.PortalPlane.m_Normal.z, m_InternalData.Placement.PortalPlane.m_Dist + PORTAL_WORLD_WALL_HALF_SEPARATION_AMOUNT }; float fWorldClipPlane_Reverse[4] = { -fWorldClipPlane_Forward[0], -fWorldClipPlane_Forward[1], -fWorldClipPlane_Forward[2], -fWorldClipPlane_Forward[3] }; float fWallClipPlane_Forward[4] = { m_InternalData.Placement.PortalPlane.m_Normal.x, m_InternalData.Placement.PortalPlane.m_Normal.y, m_InternalData.Placement.PortalPlane.m_Normal.z, m_InternalData.Placement.PortalPlane.m_Dist }; // - PORTAL_WORLD_WALL_HALF_SEPARATION_AMOUNT //float fWallClipPlane_Reverse[4] = { -fWallClipPlane_Forward[0], // -fWallClipPlane_Forward[1], // -fWallClipPlane_Forward[2], // -fWallClipPlane_Forward[3] }; //World { Vector vOBBForward = m_InternalData.Placement.vForward; Vector vOBBRight = m_InternalData.Placement.vRight; Vector vOBBUp = m_InternalData.Placement.vUp; //scale the extents to usable sizes float flScaleX = sv_portal_collision_sim_bounds_x.GetFloat(); if ( flScaleX < 200.0f ) flScaleX = 200.0f; float flScaleY = sv_portal_collision_sim_bounds_y.GetFloat(); if ( flScaleY < 200.0f ) flScaleY = 200.0f; float flScaleZ = sv_portal_collision_sim_bounds_z.GetFloat(); if ( flScaleZ < 252.0f ) flScaleZ = 252.0f; vOBBForward *= flScaleX; vOBBRight *= flScaleY; vOBBUp *= flScaleZ; // default size for scale z (252) is player (height + portal half height) * 2. Any smaller than this will allow for players to // reach unsimulated geometry before an end touch with teh portal. Vector ptOBBOrigin = m_InternalData.Placement.ptCenter; ptOBBOrigin -= vOBBRight / 2.0f; ptOBBOrigin -= vOBBUp / 2.0f; Vector vAABBMins, vAABBMaxs; vAABBMins = vAABBMaxs = ptOBBOrigin; for( int i = 1; i != 8; ++i ) { Vector ptTest = ptOBBOrigin; if( i & (1 << 0) ) ptTest += vOBBForward; if( i & (1 << 1) ) ptTest += vOBBRight; if( i & (1 << 2) ) ptTest += vOBBUp; if( ptTest.x < vAABBMins.x ) vAABBMins.x = ptTest.x; if( ptTest.y < vAABBMins.y ) vAABBMins.y = ptTest.y; if( ptTest.z < vAABBMins.z ) vAABBMins.z = ptTest.z; if( ptTest.x > vAABBMaxs.x ) vAABBMaxs.x = ptTest.x; if( ptTest.y > vAABBMaxs.y ) vAABBMaxs.y = ptTest.y; if( ptTest.z > vAABBMaxs.z ) vAABBMaxs.z = ptTest.z; } //Brushes { Assert( m_InternalData.Simulation.Static.World.Brushes.Polyhedrons.Count() == 0 ); CUtlVector WorldBrushes; enginetrace->GetBrushesInAABB( vAABBMins, vAABBMaxs, &WorldBrushes, MASK_SOLID_BRUSHONLY|CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP ); //create locally clipped polyhedrons for the world { int *pBrushList = WorldBrushes.Base(); int iBrushCount = WorldBrushes.Count(); ConvertBrushListToClippedPolyhedronList( pBrushList, iBrushCount, fWorldClipPlane_Reverse, 1, PORTAL_POLYHEDRON_CUT_EPSILON, &m_InternalData.Simulation.Static.World.Brushes.Polyhedrons ); } } //static props { Assert( m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons.Count() == 0 ); CUtlVector StaticProps; staticpropmgr->GetAllStaticPropsInAABB( vAABBMins, vAABBMaxs, &StaticProps ); for( int i = StaticProps.Count(); --i >= 0; ) { ICollideable *pProp = StaticProps[i]; CPolyhedron *PolyhedronArray[1024]; int iPolyhedronCount = g_StaticCollisionPolyhedronCache.GetStaticPropPolyhedrons( pProp, PolyhedronArray, 1024 ); StaticPropPolyhedronGroups_t indices; indices.iStartIndex = m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons.Count(); for( int j = 0; j != iPolyhedronCount; ++j ) { CPolyhedron *pPropPolyhedronPiece = PolyhedronArray[j]; if( pPropPolyhedronPiece ) { CPolyhedron *pClippedPropPolyhedron = ClipPolyhedron( pPropPolyhedronPiece, fWorldClipPlane_Reverse, 1, 0.01f, false ); if( pClippedPropPolyhedron ) m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons.AddToTail( pClippedPropPolyhedron ); } } indices.iNumPolyhedrons = m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons.Count() - indices.iStartIndex; if( indices.iNumPolyhedrons != 0 ) { int index = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.AddToTail(); PS_SD_Static_World_StaticProps_ClippedProp_t &NewEntry = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[index]; NewEntry.PolyhedronGroup = indices; NewEntry.pCollide = NULL; #ifndef CLIENT_DLL NewEntry.pPhysicsObject = NULL; #endif NewEntry.pSourceProp = pProp->GetEntityHandle(); const model_t *pModel = pProp->GetCollisionModel(); bool bIsStudioModel = pModel && (modelinfo->GetModelType( pModel ) == mod_studio); AssertOnce( bIsStudioModel ); if( bIsStudioModel ) { studiohdr_t *pStudioHdr = modelinfo->GetStudiomodel( pModel ); Assert( pStudioHdr != NULL ); NewEntry.iTraceContents = pStudioHdr->contents; NewEntry.iTraceSurfaceProps = physprops->GetSurfaceIndex( pStudioHdr->pszSurfaceProp() ); } else { NewEntry.iTraceContents = m_InternalData.Simulation.Static.SurfaceProperties.contents; NewEntry.iTraceSurfaceProps = m_InternalData.Simulation.Static.SurfaceProperties.surface.surfaceProps; } } } } } //(Holy) Wall { Assert( m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.Count() == 0 ); Assert( m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons.Count() == 0 ); Vector vBackward = -m_InternalData.Placement.vForward; Vector vLeft = -m_InternalData.Placement.vRight; Vector vDown = -m_InternalData.Placement.vUp; Vector vOBBForward = -m_InternalData.Placement.vForward; Vector vOBBRight = -m_InternalData.Placement.vRight; Vector vOBBUp = m_InternalData.Placement.vUp; //scale the extents to usable sizes vOBBForward *= PORTAL_WALL_FARDIST / 2.0f; vOBBRight *= PORTAL_WALL_FARDIST * 2.0f; vOBBUp *= PORTAL_WALL_FARDIST * 2.0f; Vector ptOBBOrigin = m_InternalData.Placement.ptCenter; ptOBBOrigin -= vOBBRight / 2.0f; ptOBBOrigin -= vOBBUp / 2.0f; Vector vAABBMins, vAABBMaxs; vAABBMins = vAABBMaxs = ptOBBOrigin; for( int i = 1; i != 8; ++i ) { Vector ptTest = ptOBBOrigin; if( i & (1 << 0) ) ptTest += vOBBForward; if( i & (1 << 1) ) ptTest += vOBBRight; if( i & (1 << 2) ) ptTest += vOBBUp; if( ptTest.x < vAABBMins.x ) vAABBMins.x = ptTest.x; if( ptTest.y < vAABBMins.y ) vAABBMins.y = ptTest.y; if( ptTest.z < vAABBMins.z ) vAABBMins.z = ptTest.z; if( ptTest.x > vAABBMaxs.x ) vAABBMaxs.x = ptTest.x; if( ptTest.y > vAABBMaxs.y ) vAABBMaxs.y = ptTest.y; if( ptTest.z > vAABBMaxs.z ) vAABBMaxs.z = ptTest.z; } float fPlanes[6 * 4]; //first and second planes are always forward and backward planes fPlanes[(0*4) + 0] = fWallClipPlane_Forward[0]; fPlanes[(0*4) + 1] = fWallClipPlane_Forward[1]; fPlanes[(0*4) + 2] = fWallClipPlane_Forward[2]; fPlanes[(0*4) + 3] = fWallClipPlane_Forward[3] - PORTAL_WALL_TUBE_OFFSET; fPlanes[(1*4) + 0] = vBackward.x; fPlanes[(1*4) + 1] = vBackward.y; fPlanes[(1*4) + 2] = vBackward.z; float fTubeDepthDist = vBackward.Dot( m_InternalData.Placement.ptCenter + (vBackward * (PORTAL_WALL_TUBE_DEPTH + PORTAL_WALL_TUBE_OFFSET)) ); fPlanes[(1*4) + 3] = fTubeDepthDist; //the remaining planes will always have the same ordering of normals, with different distances plugged in for each convex we're creating //normal order is up, down, left, right fPlanes[(2*4) + 0] = m_InternalData.Placement.vUp.x; fPlanes[(2*4) + 1] = m_InternalData.Placement.vUp.y; fPlanes[(2*4) + 2] = m_InternalData.Placement.vUp.z; fPlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + (m_InternalData.Placement.vUp * PORTAL_HOLE_HALF_HEIGHT) ); fPlanes[(3*4) + 0] = vDown.x; fPlanes[(3*4) + 1] = vDown.y; fPlanes[(3*4) + 2] = vDown.z; fPlanes[(3*4) + 3] = vDown.Dot( m_InternalData.Placement.ptCenter + (vDown * PORTAL_HOLE_HALF_HEIGHT) ); fPlanes[(4*4) + 0] = vLeft.x; fPlanes[(4*4) + 1] = vLeft.y; fPlanes[(4*4) + 2] = vLeft.z; fPlanes[(4*4) + 3] = vLeft.Dot( m_InternalData.Placement.ptCenter + (vLeft * PORTAL_HOLE_HALF_WIDTH) ); fPlanes[(5*4) + 0] = m_InternalData.Placement.vRight.x; fPlanes[(5*4) + 1] = m_InternalData.Placement.vRight.y; fPlanes[(5*4) + 2] = m_InternalData.Placement.vRight.z; fPlanes[(5*4) + 3] = m_InternalData.Placement.vRight.Dot( m_InternalData.Placement.ptCenter + (m_InternalData.Placement.vRight * PORTAL_HOLE_HALF_WIDTH) ); float *fSidePlanesOnly = &fPlanes[(2*4)]; //these 2 get re-used a bit float fFarRightPlaneDistance = m_InternalData.Placement.vRight.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vRight * (PORTAL_WALL_FARDIST * 10.0f) ); float fFarLeftPlaneDistance = vLeft.Dot( m_InternalData.Placement.ptCenter + vLeft * (PORTAL_WALL_FARDIST * 10.0f) ); CUtlVector WallBrushes; CUtlVector WallBrushPolyhedrons_ClippedToWall; CPolyhedron **pWallClippedPolyhedrons = NULL; int iWallClippedPolyhedronCount = 0; if( IsSimulatingVPhysics() ) //if not simulating vphysics, we skip making the entire wall, and just create the minimal tube instead { enginetrace->GetBrushesInAABB( vAABBMins, vAABBMaxs, &WallBrushes, MASK_SOLID_BRUSHONLY ); if( WallBrushes.Count() != 0 ) ConvertBrushListToClippedPolyhedronList( WallBrushes.Base(), WallBrushes.Count(), fPlanes, 1, PORTAL_POLYHEDRON_CUT_EPSILON, &WallBrushPolyhedrons_ClippedToWall ); if( WallBrushPolyhedrons_ClippedToWall.Count() != 0 ) { for( int i = WallBrushPolyhedrons_ClippedToWall.Count(); --i >= 0; ) { CPolyhedron *pPolyhedron = ClipPolyhedron( WallBrushPolyhedrons_ClippedToWall[i], fSidePlanesOnly, 4, PORTAL_POLYHEDRON_CUT_EPSILON, true ); if( pPolyhedron ) { //a chunk of this brush passes through the hole, not eligible to be removed from cutting pPolyhedron->Release(); } else { //no part of this brush interacts with the hole, no point in cutting the brush any later m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons.AddToTail( WallBrushPolyhedrons_ClippedToWall[i] ); WallBrushPolyhedrons_ClippedToWall.FastRemove( i ); } } if( WallBrushPolyhedrons_ClippedToWall.Count() != 0 ) //might have become 0 while removing uncut brushes { pWallClippedPolyhedrons = WallBrushPolyhedrons_ClippedToWall.Base(); iWallClippedPolyhedronCount = WallBrushPolyhedrons_ClippedToWall.Count(); } } } //upper wall { //minimal portion that extends into the hole space //fPlanes[(1*4) + 3] = fTubeDepthDist; fPlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vUp * (PORTAL_HOLE_HALF_HEIGHT + PORTAL_WALL_MIN_THICKNESS) ); fPlanes[(3*4) + 3] = vDown.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vUp * PORTAL_HOLE_HALF_HEIGHT ); fPlanes[(4*4) + 3] = vLeft.Dot( m_InternalData.Placement.ptCenter + vLeft * (PORTAL_HOLE_HALF_WIDTH + PORTAL_WALL_MIN_THICKNESS) ); fPlanes[(5*4) + 3] = m_InternalData.Placement.vRight.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vRight * (PORTAL_HOLE_HALF_WIDTH + PORTAL_WALL_MIN_THICKNESS) ); CPolyhedron *pTubePolyhedron = GeneratePolyhedronFromPlanes( fPlanes, 6, PORTAL_POLYHEDRON_CUT_EPSILON ); if( pTubePolyhedron ) m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.AddToTail( pTubePolyhedron ); //general hole cut //fPlanes[(1*4) + 3] += 2000.0f; fPlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vUp * (PORTAL_WALL_FARDIST * 10.0f) ); fPlanes[(3*4) + 3] = vDown.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vUp * (PORTAL_HOLE_HALF_HEIGHT + PORTAL_WALL_MIN_THICKNESS) ); fPlanes[(4*4) + 3] = fFarLeftPlaneDistance; fPlanes[(5*4) + 3] = fFarRightPlaneDistance; ClipPolyhedrons( pWallClippedPolyhedrons, iWallClippedPolyhedronCount, fSidePlanesOnly, 4, PORTAL_POLYHEDRON_CUT_EPSILON, &m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons ); } //lower wall { //minimal portion that extends into the hole space //fPlanes[(1*4) + 3] = fTubeDepthDist; fPlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + (vDown * PORTAL_HOLE_HALF_HEIGHT) ); fPlanes[(3*4) + 3] = vDown.Dot( m_InternalData.Placement.ptCenter + vDown * (PORTAL_HOLE_HALF_HEIGHT + PORTAL_WALL_MIN_THICKNESS) ); fPlanes[(4*4) + 3] = vLeft.Dot( m_InternalData.Placement.ptCenter + vLeft * (PORTAL_HOLE_HALF_WIDTH + PORTAL_WALL_MIN_THICKNESS) ); fPlanes[(5*4) + 3] = m_InternalData.Placement.vRight.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vRight * (PORTAL_HOLE_HALF_WIDTH + PORTAL_WALL_MIN_THICKNESS) ); CPolyhedron *pTubePolyhedron = GeneratePolyhedronFromPlanes( fPlanes, 6, PORTAL_POLYHEDRON_CUT_EPSILON ); if( pTubePolyhedron ) m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.AddToTail( pTubePolyhedron ); //general hole cut //fPlanes[(1*4) + 3] += 2000.0f; fPlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + (vDown * (PORTAL_HOLE_HALF_HEIGHT + PORTAL_WALL_MIN_THICKNESS)) ); fPlanes[(3*4) + 3] = vDown.Dot( m_InternalData.Placement.ptCenter + (vDown * (PORTAL_WALL_FARDIST * 10.0f)) ); fPlanes[(4*4) + 3] = fFarLeftPlaneDistance; fPlanes[(5*4) + 3] = fFarRightPlaneDistance; ClipPolyhedrons( pWallClippedPolyhedrons, iWallClippedPolyhedronCount, fSidePlanesOnly, 4, PORTAL_POLYHEDRON_CUT_EPSILON, &m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons ); } //left wall { //minimal portion that extends into the hole space //fPlanes[(1*4) + 3] = fTubeDepthDist; fPlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + (m_InternalData.Placement.vUp * PORTAL_HOLE_HALF_HEIGHT) ); fPlanes[(3*4) + 3] = vDown.Dot( m_InternalData.Placement.ptCenter + (vDown * PORTAL_HOLE_HALF_HEIGHT) ); fPlanes[(4*4) + 3] = vLeft.Dot( m_InternalData.Placement.ptCenter + (vLeft * (PORTAL_HOLE_HALF_WIDTH + PORTAL_WALL_MIN_THICKNESS)) ); fPlanes[(5*4) + 3] = m_InternalData.Placement.vRight.Dot( m_InternalData.Placement.ptCenter + (vLeft * PORTAL_HOLE_HALF_WIDTH) ); CPolyhedron *pTubePolyhedron = GeneratePolyhedronFromPlanes( fPlanes, 6, PORTAL_POLYHEDRON_CUT_EPSILON ); if( pTubePolyhedron ) m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.AddToTail( pTubePolyhedron ); //general hole cut //fPlanes[(1*4) + 3] += 2000.0f; fPlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + (m_InternalData.Placement.vUp * (PORTAL_HOLE_HALF_HEIGHT + PORTAL_WALL_MIN_THICKNESS)) ); fPlanes[(3*4) + 3] = vDown.Dot( m_InternalData.Placement.ptCenter - (m_InternalData.Placement.vUp * (PORTAL_HOLE_HALF_HEIGHT + PORTAL_WALL_MIN_THICKNESS)) ); fPlanes[(4*4) + 3] = fFarLeftPlaneDistance; fPlanes[(5*4) + 3] = m_InternalData.Placement.vRight.Dot( m_InternalData.Placement.ptCenter + (vLeft * (PORTAL_HOLE_HALF_WIDTH + PORTAL_WALL_MIN_THICKNESS)) ); ClipPolyhedrons( pWallClippedPolyhedrons, iWallClippedPolyhedronCount, fSidePlanesOnly, 4, PORTAL_POLYHEDRON_CUT_EPSILON, &m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons ); } //right wall { //minimal portion that extends into the hole space //fPlanes[(1*4) + 3] = fTubeDepthDist; fPlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + (m_InternalData.Placement.vUp * (PORTAL_HOLE_HALF_HEIGHT)) ); fPlanes[(3*4) + 3] = vDown.Dot( m_InternalData.Placement.ptCenter + (vDown * (PORTAL_HOLE_HALF_HEIGHT)) ); fPlanes[(4*4) + 3] = vLeft.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vRight * PORTAL_HOLE_HALF_WIDTH ); fPlanes[(5*4) + 3] = m_InternalData.Placement.vRight.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vRight * (PORTAL_HOLE_HALF_WIDTH + PORTAL_WALL_MIN_THICKNESS) ); CPolyhedron *pTubePolyhedron = GeneratePolyhedronFromPlanes( fPlanes, 6, PORTAL_POLYHEDRON_CUT_EPSILON ); if( pTubePolyhedron ) m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.AddToTail( pTubePolyhedron ); //general hole cut //fPlanes[(1*4) + 3] += 2000.0f; fPlanes[(2*4) + 3] = m_InternalData.Placement.vUp.Dot( m_InternalData.Placement.ptCenter + (m_InternalData.Placement.vUp * (PORTAL_HOLE_HALF_HEIGHT + PORTAL_WALL_MIN_THICKNESS)) ); fPlanes[(3*4) + 3] = vDown.Dot( m_InternalData.Placement.ptCenter + (vDown * (PORTAL_HOLE_HALF_HEIGHT + PORTAL_WALL_MIN_THICKNESS)) ); fPlanes[(4*4) + 3] = vLeft.Dot( m_InternalData.Placement.ptCenter + m_InternalData.Placement.vRight * (PORTAL_HOLE_HALF_WIDTH + PORTAL_WALL_MIN_THICKNESS) ); fPlanes[(5*4) + 3] = fFarRightPlaneDistance; ClipPolyhedrons( pWallClippedPolyhedrons, iWallClippedPolyhedronCount, fSidePlanesOnly, 4, PORTAL_POLYHEDRON_CUT_EPSILON, &m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons ); } for( int i = WallBrushPolyhedrons_ClippedToWall.Count(); --i >= 0; ) WallBrushPolyhedrons_ClippedToWall[i]->Release(); WallBrushPolyhedrons_ClippedToWall.RemoveAll(); } STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::CreatePolyhedrons() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); m_CreationChecklist.bPolyhedronsGenerated = true; } void CPortalSimulator::ClearPolyhedrons( void ) { if( m_CreationChecklist.bPolyhedronsGenerated == false ) return; CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearPolyhedrons() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); INCREMENTTABSPACING(); if( m_InternalData.Simulation.Static.World.Brushes.Polyhedrons.Count() != 0 ) { for( int i = m_InternalData.Simulation.Static.World.Brushes.Polyhedrons.Count(); --i >= 0; ) m_InternalData.Simulation.Static.World.Brushes.Polyhedrons[i]->Release(); m_InternalData.Simulation.Static.World.Brushes.Polyhedrons.RemoveAll(); } if( m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons.Count() != 0 ) { for( int i = m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons.Count(); --i >= 0; ) m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons[i]->Release(); m_InternalData.Simulation.Static.World.StaticProps.Polyhedrons.RemoveAll(); } #ifdef _DEBUG for( int i = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) { #ifndef CLIENT_DLL Assert( m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[i].pPhysicsObject == NULL ); #endif Assert( m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[i].pCollide == NULL ); } #endif m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.RemoveAll(); if( m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons.Count() != 0 ) { for( int i = m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons.Count(); --i >= 0; ) m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons[i]->Release(); m_InternalData.Simulation.Static.Wall.Local.Brushes.Polyhedrons.RemoveAll(); } if( m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.Count() != 0 ) { for( int i = m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.Count(); --i >= 0; ) m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons[i]->Release(); m_InternalData.Simulation.Static.Wall.Local.Tube.Polyhedrons.RemoveAll(); } STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::ClearPolyhedrons() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); m_CreationChecklist.bPolyhedronsGenerated = false; } void CPortalSimulator::DetachFromLinked( void ) { if( m_pLinkedPortal == NULL ) return; CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::DetachFromLinked() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); INCREMENTTABSPACING(); //IMPORTANT: Physics objects must be destroyed before their associated collision data or a fairly cryptic crash will ensue #ifndef CLIENT_DLL ClearLinkedEntities(); ClearLinkedPhysics(); #endif ClearLinkedCollision(); if( m_pLinkedPortal->m_bInCrossLinkedFunction == false ) { Assert( m_bInCrossLinkedFunction == false ); //I'm pretty sure switching to a stack would have negative repercussions m_bInCrossLinkedFunction = true; m_pLinkedPortal->DetachFromLinked(); m_bInCrossLinkedFunction = false; } m_pLinkedPortal = NULL; STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::DetachFromLinked() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); } void CPortalSimulator::SetPortalSimulatorCallbacks( CPortalSimulatorEventCallbacks *pCallbacks ) { if( pCallbacks ) m_pCallbacks = pCallbacks; else m_pCallbacks = &s_DummyPortalSimulatorCallback; //always keep the pointer valid } void CPortalSimulator::SetVPhysicsSimulationEnabled( bool bEnabled ) { AssertMsg( (m_pLinkedPortal == NULL) || (m_pLinkedPortal->m_bSimulateVPhysics == m_bSimulateVPhysics), "Linked portals are in disagreement as to whether they would simulate VPhysics." ); if( bEnabled == m_bSimulateVPhysics ) return; CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::SetVPhysicsSimulationEnabled() START\n", GetPortalSimulatorGUID(), TABSPACING ); ); INCREMENTTABSPACING(); m_bSimulateVPhysics = bEnabled; if( bEnabled ) { //we took some local collision shortcuts when generating while physics simulation is off, regenerate ClearLocalCollision(); ClearPolyhedrons(); CreatePolyhedrons(); CreateLocalCollision(); #ifndef CLIENT_DLL CreateAllPhysics(); #endif } #ifndef CLIENT_DLL else { ClearAllPhysics(); } #endif if( m_pLinkedPortal && (m_pLinkedPortal->m_bInCrossLinkedFunction == false) ) { Assert( m_bInCrossLinkedFunction == false ); //I'm pretty sure switching to a stack would have negative repercussions m_bInCrossLinkedFunction = true; m_pLinkedPortal->SetVPhysicsSimulationEnabled( bEnabled ); m_bInCrossLinkedFunction = false; } STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::SetVPhysicsSimulationEnabled() FINISH: %fms\n", GetPortalSimulatorGUID(), TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); } #ifndef CLIENT_DLL void CPortalSimulator::PrePhysFrame( void ) { int iPortalSimulators = s_PortalSimulators.Count(); if( iPortalSimulators != 0 ) { CPortalSimulator **pAllSimulators = s_PortalSimulators.Base(); for( int i = 0; i != iPortalSimulators; ++i ) { CPortalSimulator *pSimulator = pAllSimulators[i]; if( !pSimulator->IsReadyToSimulate() ) continue; int iOwnedEntities = pSimulator->m_InternalData.Simulation.Dynamic.OwnedEntities.Count(); if( iOwnedEntities != 0 ) { CBaseEntity **pOwnedEntities = pSimulator->m_InternalData.Simulation.Dynamic.OwnedEntities.Base(); for( int j = 0; j != iOwnedEntities; ++j ) { CBaseEntity *pEntity = pOwnedEntities[j]; if( CPhysicsShadowClone::IsShadowClone( pEntity ) ) continue; Assert( (pEntity != NULL) && (pEntity->IsMarkedForDeletion() == false) ); IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject(); if( (pPhysObject == NULL) || pPhysObject->IsAsleep() ) continue; int iEntIndex = pEntity->entindex(); int iExistingFlags = pSimulator->m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex]; if( pSimulator->EntityIsInPortalHole( pEntity ) ) pSimulator->m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] |= PSEF_IS_IN_PORTAL_HOLE; else pSimulator->m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] &= ~PSEF_IS_IN_PORTAL_HOLE; UpdateShadowClonesPortalSimulationFlags( pEntity, PSEF_IS_IN_PORTAL_HOLE, pSimulator->m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] ); if( ((iExistingFlags ^ pSimulator->m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex]) & PSEF_IS_IN_PORTAL_HOLE) != 0 ) //value changed { pEntity->CollisionRulesChanged(); //entity moved into or out of the portal hole, need to either add or remove collision with transformed geometry CPhysicsShadowCloneLL *pClones = CPhysicsShadowClone::GetClonesOfEntity( pEntity ); while( pClones ) { pClones->pClone->CollisionRulesChanged(); pClones = pClones->pNext; } } } } } } } void CPortalSimulator::PostPhysFrame( void ) { if ( g_bPlayerIsInSimulator ) { CPortal_Player* pPlayer = dynamic_cast( UTIL_GetLocalPlayer() ); CProp_Portal* pTouchedPortal = pPlayer->m_hPortalEnvironment.Get(); CPortalSimulator* pSim = GetSimulatorThatOwnsEntity( pPlayer ); if ( pTouchedPortal && pSim && (pTouchedPortal->m_PortalSimulator.GetPortalSimulatorGUID() != pSim->GetPortalSimulatorGUID()) ) { Warning ( "Player is simulated in a physics environment but isn't touching a portal! Can't teleport, but can fall through portal hole. Returning player to main environment.\n" ); ADD_DEBUG_HISTORY( HISTORY_PLAYER_DAMAGE, UTIL_VarArgs( "Player in PortalSimulator but not touching a portal, removing from sim at : %f\n", gpGlobals->curtime ) ); if ( pSim ) { pSim->ReleaseOwnershipOfEntity( pPlayer, false ); } } } } #endif //#ifndef CLIENT_DLL #ifndef CLIENT_DLL int CPortalSimulator::GetMoveableOwnedEntities( CBaseEntity **pEntsOut, int iEntOutLimit ) { int iOwnedEntCount = m_InternalData.Simulation.Dynamic.OwnedEntities.Count(); int iOutputCount = 0; for( int i = 0; i != iOwnedEntCount; ++i ) { CBaseEntity *pEnt = m_InternalData.Simulation.Dynamic.OwnedEntities[i]; Assert( pEnt != NULL ); if( CPhysicsShadowClone::IsShadowClone( pEnt ) ) continue; if( CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pEnt ) ) continue; if( pEnt->GetMoveType() == MOVETYPE_NONE ) continue; pEntsOut[iOutputCount] = pEnt; ++iOutputCount; if( iOutputCount == iEntOutLimit ) break; } return iOutputCount; } CPortalSimulator *CPortalSimulator::GetSimulatorThatOwnsEntity( const CBaseEntity *pEntity ) { #ifdef _DEBUG int iEntIndex = pEntity->entindex(); CPortalSimulator *pOwningSimulatorCheck = NULL; for( int i = s_PortalSimulators.Count(); --i >= 0; ) { if( s_PortalSimulators[i]->m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] & PSEF_OWNS_ENTITY ) { AssertMsg( pOwningSimulatorCheck == NULL, "More than one portal simulator found owning the same entity." ); pOwningSimulatorCheck = s_PortalSimulators[i]; } } AssertMsg( pOwningSimulatorCheck == s_OwnedEntityMap[iEntIndex], "Owned entity mapping out of sync with individual simulator ownership flags." ); #endif return s_OwnedEntityMap[pEntity->entindex()]; } CPortalSimulator *CPortalSimulator::GetSimulatorThatCreatedPhysicsObject( const IPhysicsObject *pObject, PS_PhysicsObjectSourceType_t *pOut_SourceType ) { for( int i = s_PortalSimulators.Count(); --i >= 0; ) { if( s_PortalSimulators[i]->CreatedPhysicsObject( pObject, pOut_SourceType ) ) return s_PortalSimulators[i]; } return NULL; } bool CPortalSimulator::CreatedPhysicsObject( const IPhysicsObject *pObject, PS_PhysicsObjectSourceType_t *pOut_SourceType ) const { if( (pObject == m_InternalData.Simulation.Static.World.Brushes.pPhysicsObject) || (pObject == m_InternalData.Simulation.Static.Wall.Local.Brushes.pPhysicsObject) ) { if( pOut_SourceType ) *pOut_SourceType = PSPOST_LOCAL_BRUSHES; return true; } if( pObject == m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject ) { if( pOut_SourceType ) *pOut_SourceType = PSPOST_REMOTE_BRUSHES; return true; } for( int i = m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) { if( m_InternalData.Simulation.Static.World.StaticProps.ClippedRepresentations[i].pPhysicsObject == pObject ) { if( pOut_SourceType ) *pOut_SourceType = PSPOST_LOCAL_STATICPROPS; return true; } } for( int i = m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.PhysicsObjects.Count(); --i >= 0; ) { if( m_InternalData.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.PhysicsObjects[i] == pObject ) { if( pOut_SourceType ) *pOut_SourceType = PSPOST_REMOTE_STATICPROPS; return true; } } if( pObject == m_InternalData.Simulation.Static.Wall.Local.Tube.pPhysicsObject ) { if( pOut_SourceType ) *pOut_SourceType = PSPOST_HOLYWALL_TUBE; return true; } return false; } #endif //#ifndef CLIENT_DLL static void ConvertBrushListToClippedPolyhedronList( const int *pBrushes, int iBrushCount, const float *pOutwardFacingClipPlanes, int iClipPlaneCount, float fClipEpsilon, CUtlVector *pPolyhedronList ) { if( pPolyhedronList == NULL ) return; if( (pBrushes == NULL) || (iBrushCount == 0) ) return; for( int i = 0; i != iBrushCount; ++i ) { CPolyhedron *pPolyhedron = ClipPolyhedron( g_StaticCollisionPolyhedronCache.GetBrushPolyhedron( pBrushes[i] ), pOutwardFacingClipPlanes, iClipPlaneCount, fClipEpsilon ); if( pPolyhedron ) pPolyhedronList->AddToTail( pPolyhedron ); } } static void ClipPolyhedrons( CPolyhedron * const *pExistingPolyhedrons, int iPolyhedronCount, const float *pOutwardFacingClipPlanes, int iClipPlaneCount, float fClipEpsilon, CUtlVector *pPolyhedronList ) { if( pPolyhedronList == NULL ) return; if( (pExistingPolyhedrons == NULL) || (iPolyhedronCount == 0) ) return; for( int i = 0; i != iPolyhedronCount; ++i ) { CPolyhedron *pPolyhedron = ClipPolyhedron( pExistingPolyhedrons[i], pOutwardFacingClipPlanes, iClipPlaneCount, fClipEpsilon ); if( pPolyhedron ) pPolyhedronList->AddToTail( pPolyhedron ); } } static CPhysCollide *ConvertPolyhedronsToCollideable( CPolyhedron **pPolyhedrons, int iPolyhedronCount ) { if( (pPolyhedrons == NULL) || (iPolyhedronCount == 0 ) ) return NULL; CREATEDEBUGTIMER( functionTimer ); STARTDEBUGTIMER( functionTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sConvertPolyhedronsToCollideable() START\n", s_iPortalSimulatorGUID, TABSPACING ); ); INCREMENTTABSPACING(); CPhysConvex **pConvexes = (CPhysConvex **)stackalloc( iPolyhedronCount * sizeof( CPhysConvex * ) ); int iConvexCount = 0; CREATEDEBUGTIMER( convexTimer ); STARTDEBUGTIMER( convexTimer ); for( int i = 0; i != iPolyhedronCount; ++i ) { pConvexes[iConvexCount] = physcollision->ConvexFromConvexPolyhedron( *pPolyhedrons[i] ); Assert( pConvexes[iConvexCount] != NULL ); if( pConvexes[iConvexCount] ) ++iConvexCount; } STOPDEBUGTIMER( convexTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sConvex Generation:%fms\n", s_iPortalSimulatorGUID, TABSPACING, convexTimer.GetDuration().GetMillisecondsF() ); ); CPhysCollide *pReturn; if( iConvexCount != 0 ) { CREATEDEBUGTIMER( collideTimer ); STARTDEBUGTIMER( collideTimer ); pReturn = physcollision->ConvertConvexToCollide( pConvexes, iConvexCount ); STOPDEBUGTIMER( collideTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCollideable Generation:%fms\n", s_iPortalSimulatorGUID, TABSPACING, collideTimer.GetDuration().GetMillisecondsF() ); ); } else { pReturn = NULL; } STOPDEBUGTIMER( functionTimer ); DECREMENTTABSPACING(); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sConvertPolyhedronsToCollideable() FINISH: %fms\n", s_iPortalSimulatorGUID, TABSPACING, functionTimer.GetDuration().GetMillisecondsF() ); ); return pReturn; } static inline CPolyhedron *TransformAndClipSinglePolyhedron( CPolyhedron *pExistingPolyhedron, const VMatrix &Transform, const float *pOutwardFacingClipPlanes, int iClipPlaneCount, float fCutEpsilon, bool bUseTempMemory ) { Vector *pTempPointArray = (Vector *)stackalloc( sizeof( Vector ) * pExistingPolyhedron->iVertexCount ); Polyhedron_IndexedPolygon_t *pTempPolygonArray = (Polyhedron_IndexedPolygon_t *)stackalloc( sizeof( Polyhedron_IndexedPolygon_t ) * pExistingPolyhedron->iPolygonCount ); Polyhedron_IndexedPolygon_t *pOriginalPolygons = pExistingPolyhedron->pPolygons; pExistingPolyhedron->pPolygons = pTempPolygonArray; Vector *pOriginalPoints = pExistingPolyhedron->pVertices; pExistingPolyhedron->pVertices = pTempPointArray; for( int j = 0; j != pExistingPolyhedron->iPolygonCount; ++j ) { pTempPolygonArray[j].iFirstIndex = pOriginalPolygons[j].iFirstIndex; pTempPolygonArray[j].iIndexCount = pOriginalPolygons[j].iIndexCount; pTempPolygonArray[j].polyNormal = Transform.ApplyRotation( pOriginalPolygons[j].polyNormal ); } for( int j = 0; j != pExistingPolyhedron->iVertexCount; ++j ) { pTempPointArray[j] = Transform * pOriginalPoints[j]; } CPolyhedron *pNewPolyhedron = ClipPolyhedron( pExistingPolyhedron, pOutwardFacingClipPlanes, iClipPlaneCount, fCutEpsilon, bUseTempMemory ); //copy the polyhedron //restore the original polyhedron to its former self pExistingPolyhedron->pVertices = pOriginalPoints; pExistingPolyhedron->pPolygons = pOriginalPolygons; return pNewPolyhedron; } static int GetEntityPhysicsObjects( IPhysicsEnvironment *pEnvironment, CBaseEntity *pEntity, IPhysicsObject **pRetList, int iRetListArraySize ) { int iCount, iRetCount = 0; const IPhysicsObject **pList = pEnvironment->GetObjectList( &iCount ); if( iCount > iRetListArraySize ) iCount = iRetListArraySize; for ( int i = 0; i < iCount; ++i ) { CBaseEntity *pEnvEntity = reinterpret_cast(pList[i]->GetGameData()); if ( pEntity == pEnvEntity ) { pRetList[iRetCount] = (IPhysicsObject *)(pList[i]); ++iRetCount; } } return iRetCount; } #ifndef CLIENT_DLL //Move all entities back to the main environment for removal, and make sure the main environment is in control during the UTIL_Remove process struct UTIL_Remove_PhysicsStack_t { IPhysicsEnvironment *pPhysicsEnvironment; CEntityList *pShadowList; }; static CUtlVector s_UTIL_Remove_PhysicsStack; void CPortalSimulator::Pre_UTIL_Remove( CBaseEntity *pEntity ) { int index = s_UTIL_Remove_PhysicsStack.AddToTail(); s_UTIL_Remove_PhysicsStack[index].pPhysicsEnvironment = physenv; s_UTIL_Remove_PhysicsStack[index].pShadowList = g_pShadowEntities; int iEntIndex = pEntity->entindex(); //NDebugOverlay::EntityBounds( pEntity, 0, 0, 0, 50, 5.0f ); if( (CPhysicsShadowClone::IsShadowClone( pEntity ) == false) && (CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pEntity ) == false) ) { CPortalSimulator *pOwningSimulator = CPortalSimulator::GetSimulatorThatOwnsEntity( pEntity ); if( pOwningSimulator ) { pOwningSimulator->ReleasePhysicsOwnership( pEntity, false ); pOwningSimulator->ReleaseOwnershipOfEntity( pEntity ); } //might be cloned from main to a few environments for( int i = s_PortalSimulators.Count(); --i >= 0; ) s_PortalSimulators[i]->StopCloningEntity( pEntity ); } for( int i = s_PortalSimulators.Count(); --i >= 0; ) { s_PortalSimulators[i]->m_InternalData.Simulation.Dynamic.EntFlags[iEntIndex] = 0; } physenv = physenv_main; g_pShadowEntities = g_pShadowEntities_Main; } void CPortalSimulator::Post_UTIL_Remove( CBaseEntity *pEntity ) { int index = s_UTIL_Remove_PhysicsStack.Count() - 1; Assert( index >= 0 ); UTIL_Remove_PhysicsStack_t &PhysicsStackEntry = s_UTIL_Remove_PhysicsStack[index]; physenv = PhysicsStackEntry.pPhysicsEnvironment; g_pShadowEntities = PhysicsStackEntry.pShadowList; s_UTIL_Remove_PhysicsStack.FastRemove(index); #ifdef _DEBUG for( int i = CPhysicsShadowClone::g_ShadowCloneList.Count(); --i >= 0; ) { Assert( CPhysicsShadowClone::g_ShadowCloneList[i]->GetClonedEntity() != pEntity ); //shouldn't be any clones of this object anymore } #endif } void UpdateShadowClonesPortalSimulationFlags( const CBaseEntity *pSourceEntity, unsigned int iFlags, int iSourceFlags ) { unsigned int iOrFlags = iSourceFlags & iFlags; CPhysicsShadowCloneLL *pClones = CPhysicsShadowClone::GetClonesOfEntity( pSourceEntity ); while( pClones ) { CPhysicsShadowClone *pClone = pClones->pClone; CPortalSimulator *pCloneSimulator = CPortalSimulator::GetSimulatorThatOwnsEntity( pClone ); unsigned int *pFlags = (unsigned int *)&pCloneSimulator->m_DataAccess.Simulation.Dynamic.EntFlags[pClone->entindex()]; *pFlags &= ~iFlags; *pFlags |= iOrFlags; Assert( ((iSourceFlags ^ *pFlags) & iFlags) == 0 ); pClones = pClones->pNext; } } #endif #ifndef CLIENT_DLL class CPS_AutoGameSys_EntityListener : public CAutoGameSystem, public IEntityListener #else class CPS_AutoGameSys_EntityListener : public CAutoGameSystem #endif { public: virtual void LevelInitPreEntity( void ) { for( int i = s_PortalSimulators.Count(); --i >= 0; ) s_PortalSimulators[i]->ClearEverything(); } virtual void LevelShutdownPreEntity( void ) { for( int i = s_PortalSimulators.Count(); --i >= 0; ) s_PortalSimulators[i]->ClearEverything(); } #ifndef CLIENT_DLL virtual bool Init( void ) { gEntList.AddListenerEntity( this ); return true; } //virtual void OnEntityCreated( CBaseEntity *pEntity ) {} virtual void OnEntitySpawned( CBaseEntity *pEntity ) { } virtual void OnEntityDeleted( CBaseEntity *pEntity ) { CPortalSimulator *pSimulator = CPortalSimulator::GetSimulatorThatOwnsEntity( pEntity ); if( pSimulator ) { pSimulator->ReleasePhysicsOwnership( pEntity, false ); pSimulator->ReleaseOwnershipOfEntity( pEntity ); } Assert( CPortalSimulator::GetSimulatorThatOwnsEntity( pEntity ) == NULL ); } #endif //#ifndef CLIENT_DLL }; static CPS_AutoGameSys_EntityListener s_CPS_AGS_EL_Singleton; #ifndef CLIENT_DLL LINK_ENTITY_TO_CLASS( portalsimulator_collisionentity, CPSCollisionEntity ); static bool s_PortalSimulatorCollisionEntities[MAX_EDICTS] = { false }; CPSCollisionEntity::CPSCollisionEntity( void ) { m_pOwningSimulator = NULL; } CPSCollisionEntity::~CPSCollisionEntity( void ) { if( m_pOwningSimulator ) { m_pOwningSimulator->m_InternalData.Simulation.Dynamic.EntFlags[entindex()] &= ~PSEF_OWNS_PHYSICS; m_pOwningSimulator->MarkAsReleased( this ); m_pOwningSimulator->m_InternalData.Simulation.pCollisionEntity = NULL; m_pOwningSimulator = NULL; } s_PortalSimulatorCollisionEntities[entindex()] = false; } void CPSCollisionEntity::UpdateOnRemove( void ) { VPhysicsSetObject( NULL ); if( m_pOwningSimulator ) { m_pOwningSimulator->m_InternalData.Simulation.Dynamic.EntFlags[entindex()] &= ~PSEF_OWNS_PHYSICS; m_pOwningSimulator->MarkAsReleased( this ); m_pOwningSimulator->m_InternalData.Simulation.pCollisionEntity = NULL; m_pOwningSimulator = NULL; } s_PortalSimulatorCollisionEntities[entindex()] = false; BaseClass::UpdateOnRemove(); } void CPSCollisionEntity::Spawn( void ) { BaseClass::Spawn(); SetSolid( SOLID_CUSTOM ); SetMoveType( MOVETYPE_NONE ); SetCollisionGroup( COLLISION_GROUP_NONE ); s_PortalSimulatorCollisionEntities[entindex()] = true; VPhysicsSetObject( NULL ); AddFlag( FL_WORLDBRUSH ); AddEffects( EF_NODRAW | EF_NOSHADOW | EF_NORECEIVESHADOW ); IncrementInterpolationFrame(); } void CPSCollisionEntity::Activate( void ) { BaseClass::Activate(); CollisionRulesChanged(); } int CPSCollisionEntity::ObjectCaps( void ) { return ((BaseClass::ObjectCaps() | FCAP_DONT_SAVE) & ~(FCAP_FORCE_TRANSITION | FCAP_ACROSS_TRANSITION | FCAP_MUST_SPAWN | FCAP_SAVE_NON_NETWORKABLE)); } bool CPSCollisionEntity::ShouldCollide( int collisionGroup, int contentsMask ) const { return GetWorldEntity()->ShouldCollide( collisionGroup, contentsMask ); } IPhysicsObject *CPSCollisionEntity::VPhysicsGetObject( void ) { if( m_pOwningSimulator == NULL ) return NULL; if( m_pOwningSimulator->m_DataAccess.Simulation.Static.World.Brushes.pPhysicsObject != NULL ) return m_pOwningSimulator->m_DataAccess.Simulation.Static.World.Brushes.pPhysicsObject; else if( m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pPhysicsObject != NULL ) return m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pPhysicsObject; else if( m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.Local.Tube.pPhysicsObject != NULL ) return m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pPhysicsObject; else if( m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject != NULL ) return m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject; else return NULL; } int CPSCollisionEntity::VPhysicsGetObjectList( IPhysicsObject **pList, int listMax ) { if( m_pOwningSimulator == NULL ) return 0; if( (pList == NULL) || (listMax == 0) ) return 0; int iRetVal = 0; if( m_pOwningSimulator->m_DataAccess.Simulation.Static.World.Brushes.pPhysicsObject != NULL ) { pList[iRetVal] = m_pOwningSimulator->m_DataAccess.Simulation.Static.World.Brushes.pPhysicsObject; ++iRetVal; if( iRetVal == listMax ) return iRetVal; } if( m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pPhysicsObject != NULL ) { pList[iRetVal] = m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pPhysicsObject; ++iRetVal; if( iRetVal == listMax ) return iRetVal; } if( m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.Local.Tube.pPhysicsObject != NULL ) { pList[iRetVal] = m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.Local.Tube.pPhysicsObject; ++iRetVal; if( iRetVal == listMax ) return iRetVal; } if( m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject != NULL ) { pList[iRetVal] = m_pOwningSimulator->m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pPhysicsObject; ++iRetVal; if( iRetVal == listMax ) return iRetVal; } return iRetVal; } bool CPSCollisionEntity::IsPortalSimulatorCollisionEntity( const CBaseEntity *pEntity ) { return s_PortalSimulatorCollisionEntities[pEntity->entindex()]; } #endif //#ifndef CLIENT_DLL #ifdef DEBUG_PORTAL_COLLISION_ENVIRONMENTS #include "filesystem.h" static void PortalSimulatorDumps_DumpCollideToGlView( CPhysCollide *pCollide, const Vector &origin, const QAngle &angles, float fColorScale, const char *pFilename ); static void PortalSimulatorDumps_DumpPlanesToGlView( float *pPlanes, int iPlaneCount, const char *pszFileName ); static void PortalSimulatorDumps_DumpBoxToGlView( const Vector &vMins, const Vector &vMaxs, float fRed, float fGreen, float fBlue, const char *pszFileName ); static void PortalSimulatorDumps_DumpOBBoxToGlView( const Vector &ptOrigin, const Vector &vExtent1, const Vector &vExtent2, const Vector &vExtent3, float fRed, float fGreen, float fBlue, const char *pszFileName ); void DumpActiveCollision( const CPortalSimulator *pPortalSimulator, const char *szFileName ) { CREATEDEBUGTIMER( collisionDumpTimer ); STARTDEBUGTIMER( collisionDumpTimer ); //color coding scheme, static prop collision is brighter than brush collision. Remote world stuff transformed to the local wall is darker than completely local stuff #define PSDAC_INTENSITY_LOCALBRUSH 0.25f #define PSDAC_INTENSITY_LOCALPROP 1.0f #define PSDAC_INTENSITY_REMOTEBRUSH 0.125f #define PSDAC_INTENSITY_REMOTEPROP 0.5f if( pPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable ) PortalSimulatorDumps_DumpCollideToGlView( pPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable, vec3_origin, vec3_angle, PSDAC_INTENSITY_LOCALBRUSH, szFileName ); if( pPortalSimulator->m_DataAccess.Simulation.Static.World.StaticProps.bCollisionExists ) { for( int i = pPortalSimulator->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) { Assert( pPortalSimulator->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations[i].pCollide ); PortalSimulatorDumps_DumpCollideToGlView( pPortalSimulator->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations[i].pCollide, vec3_origin, vec3_angle, PSDAC_INTENSITY_LOCALPROP, szFileName ); } } if( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pCollideable ) PortalSimulatorDumps_DumpCollideToGlView( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pCollideable, vec3_origin, vec3_angle, PSDAC_INTENSITY_LOCALBRUSH, szFileName ); if( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Tube.pCollideable ) PortalSimulatorDumps_DumpCollideToGlView( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Tube.pCollideable, vec3_origin, vec3_angle, PSDAC_INTENSITY_LOCALBRUSH, szFileName ); //if( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pCollideable ) // PortalSimulatorDumps_DumpCollideToGlView( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pCollideable, vec3_origin, vec3_angle, PSDAC_INTENSITY_REMOTEBRUSH, szFileName ); CPortalSimulator *pLinkedPortal = pPortalSimulator->GetLinkedPortalSimulator(); if( pLinkedPortal ) { if( pLinkedPortal->m_DataAccess.Simulation.Static.World.Brushes.pCollideable ) PortalSimulatorDumps_DumpCollideToGlView( pLinkedPortal->m_DataAccess.Simulation.Static.World.Brushes.pCollideable, pPortalSimulator->m_DataAccess.Placement.ptaap_LinkedToThis.ptOriginTransform, pPortalSimulator->m_DataAccess.Placement.ptaap_LinkedToThis.qAngleTransform, PSDAC_INTENSITY_REMOTEBRUSH, szFileName ); //for( int i = pPortalSimulator->m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.Collideables.Count(); --i >= 0; ) // PortalSimulatorDumps_DumpCollideToGlView( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.StaticProps.Collideables[i], vec3_origin, vec3_angle, PSDAC_INTENSITY_REMOTEPROP, szFileName ); if( pLinkedPortal->m_DataAccess.Simulation.Static.World.StaticProps.bCollisionExists ) { for( int i = pLinkedPortal->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); --i >= 0; ) { Assert( pLinkedPortal->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations[i].pCollide ); PortalSimulatorDumps_DumpCollideToGlView( pLinkedPortal->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations[i].pCollide, pPortalSimulator->m_DataAccess.Placement.ptaap_LinkedToThis.ptOriginTransform, pPortalSimulator->m_DataAccess.Placement.ptaap_LinkedToThis.qAngleTransform, PSDAC_INTENSITY_REMOTEPROP, szFileName ); } } } STOPDEBUGTIMER( collisionDumpTimer ); DEBUGTIMERONLY( DevMsg( 2, "[PSDT:%d] %sCPortalSimulator::DumpActiveCollision() Spent %fms generating a collision dump\n", pPortalSimulator->GetPortalSimulatorGUID(), TABSPACING, collisionDumpTimer.GetDuration().GetMillisecondsF() ); ); } static void PortalSimulatorDumps_DumpCollideToGlView( CPhysCollide *pCollide, const Vector &origin, const QAngle &angles, float fColorScale, const char *pFilename ) { if ( !pCollide ) return; printf("Writing %s...\n", pFilename ); Vector *outVerts; int vertCount = physcollision->CreateDebugMesh( pCollide, &outVerts ); FileHandle_t fp = filesystem->Open( pFilename, "ab" ); int triCount = vertCount / 3; int vert = 0; VMatrix tmp = SetupMatrixOrgAngles( origin, angles ); int i; for ( i = 0; i < vertCount; i++ ) { outVerts[i] = tmp.VMul4x3( outVerts[i] ); } for ( i = 0; i < triCount; i++ ) { filesystem->FPrintf( fp, "3\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f 0 0\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fColorScale ); vert++; filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f 0 %.2f 0\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fColorScale ); vert++; filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f 0 0 %.2f\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fColorScale ); vert++; } filesystem->Close( fp ); physcollision->DestroyDebugMesh( vertCount, outVerts ); } static void PortalSimulatorDumps_DumpPlanesToGlView( float *pPlanes, int iPlaneCount, const char *pszFileName ) { FileHandle_t fp = filesystem->Open( pszFileName, "wb" ); for( int i = 0; i < iPlaneCount; ++i ) { Vector vPlaneVerts[4]; float fRed, fGreen, fBlue; fRed = rand()/32768.0f; fGreen = rand()/32768.0f; fBlue = rand()/32768.0f; PolyFromPlane( vPlaneVerts, *(Vector *)(pPlanes + (i*4)), pPlanes[(i*4) + 3], 1000.0f ); filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vPlaneVerts[3].x, vPlaneVerts[3].y, vPlaneVerts[3].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vPlaneVerts[2].x, vPlaneVerts[2].y, vPlaneVerts[2].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vPlaneVerts[1].x, vPlaneVerts[1].y, vPlaneVerts[1].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vPlaneVerts[0].x, vPlaneVerts[0].y, vPlaneVerts[0].z, fRed, fGreen, fBlue ); } filesystem->Close( fp ); } static void PortalSimulatorDumps_DumpBoxToGlView( const Vector &vMins, const Vector &vMaxs, float fRed, float fGreen, float fBlue, const char *pszFileName ) { FileHandle_t fp = filesystem->Open( pszFileName, "ab" ); //x min side filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); //x max side filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); //y min side filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); //y max side filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); //z min side filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMins.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMins.z, fRed, fGreen, fBlue ); //z max side filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMaxs.z, fRed, fGreen, fBlue ); filesystem->Close( fp ); } static void PortalSimulatorDumps_DumpOBBoxToGlView( const Vector &ptOrigin, const Vector &vExtent1, const Vector &vExtent2, const Vector &vExtent3, float fRed, float fGreen, float fBlue, const char *pszFileName ) { FileHandle_t fp = filesystem->Open( pszFileName, "ab" ); Vector ptExtents[8]; int counter; for( counter = 0; counter != 8; ++counter ) { ptExtents[counter] = ptOrigin; if( counter & (1<<0) ) ptExtents[counter] += vExtent1; if( counter & (1<<1) ) ptExtents[counter] += vExtent2; if( counter & (1<<2) ) ptExtents[counter] += vExtent3; } //x min side filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[0].x, ptExtents[0].y, ptExtents[0].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[4].x, ptExtents[4].y, ptExtents[4].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[6].x, ptExtents[6].y, ptExtents[6].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[2].x, ptExtents[2].y, ptExtents[2].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[2].x, ptExtents[2].y, ptExtents[2].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[6].x, ptExtents[6].y, ptExtents[6].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[4].x, ptExtents[4].y, ptExtents[4].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[0].x, ptExtents[0].y, ptExtents[0].z, fRed, fGreen, fBlue ); //x max side filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[1].x, ptExtents[1].y, ptExtents[1].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[5].x, ptExtents[5].y, ptExtents[5].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[7].x, ptExtents[7].y, ptExtents[7].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[3].x, ptExtents[3].y, ptExtents[3].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[3].x, ptExtents[3].y, ptExtents[3].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[7].x, ptExtents[7].y, ptExtents[7].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[5].x, ptExtents[5].y, ptExtents[5].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[1].x, ptExtents[1].y, ptExtents[1].z, fRed, fGreen, fBlue ); //y min side filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[0].x, ptExtents[0].y, ptExtents[0].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[4].x, ptExtents[4].y, ptExtents[4].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[5].x, ptExtents[5].y, ptExtents[5].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[1].x, ptExtents[1].y, ptExtents[1].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[1].x, ptExtents[1].y, ptExtents[1].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[5].x, ptExtents[5].y, ptExtents[5].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[4].x, ptExtents[4].y, ptExtents[4].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[0].x, ptExtents[0].y, ptExtents[0].z, fRed, fGreen, fBlue ); //y max side filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[2].x, ptExtents[2].y, ptExtents[2].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[6].x, ptExtents[6].y, ptExtents[6].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[7].x, ptExtents[7].y, ptExtents[7].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[3].x, ptExtents[3].y, ptExtents[3].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[3].x, ptExtents[3].y, ptExtents[3].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[7].x, ptExtents[7].y, ptExtents[7].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[6].x, ptExtents[6].y, ptExtents[6].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[2].x, ptExtents[2].y, ptExtents[2].z, fRed, fGreen, fBlue ); //z min side filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[0].x, ptExtents[0].y, ptExtents[0].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[2].x, ptExtents[2].y, ptExtents[2].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[3].x, ptExtents[3].y, ptExtents[3].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[1].x, ptExtents[1].y, ptExtents[1].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[1].x, ptExtents[1].y, ptExtents[1].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[3].x, ptExtents[3].y, ptExtents[3].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[2].x, ptExtents[2].y, ptExtents[2].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[0].x, ptExtents[0].y, ptExtents[0].z, fRed, fGreen, fBlue ); //z max side filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[4].x, ptExtents[4].y, ptExtents[4].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[6].x, ptExtents[6].y, ptExtents[6].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[7].x, ptExtents[7].y, ptExtents[7].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[5].x, ptExtents[5].y, ptExtents[5].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "4\n" ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[5].x, ptExtents[5].y, ptExtents[5].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[7].x, ptExtents[7].y, ptExtents[7].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[6].x, ptExtents[6].y, ptExtents[6].z, fRed, fGreen, fBlue ); filesystem->FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", ptExtents[4].x, ptExtents[4].y, ptExtents[4].z, fRed, fGreen, fBlue ); filesystem->Close( fp ); } #endif