diff options
Diffstat (limited to 'game/shared/portal/PortalSimulation.cpp')
| -rw-r--r-- | game/shared/portal/PortalSimulation.cpp | 3322 |
1 files changed, 3322 insertions, 0 deletions
diff --git a/game/shared/portal/PortalSimulation.cpp b/game/shared/portal/PortalSimulation.cpp new file mode 100644 index 0000000..233c98f --- /dev/null +++ b/game/shared/portal/PortalSimulation.cpp @@ -0,0 +1,3322 @@ +//========= 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<CPolyhedron *> *pPolyhedronList ); +static void ClipPolyhedrons( CPolyhedron * const *pExistingPolyhedrons, int iPolyhedronCount, const float *pOutwardFacingClipPlanes, int iClipPlaneCount, float fClipEpsilon, CUtlVector<CPolyhedron *> *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<CPortalSimulator *> s_PortalSimulators; +CUtlVector<CPortalSimulator *> 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<CBaseEntity *> 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<CBaseEntity *> 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<CBaseEntity *> &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<int> 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<ICollideable *> 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<int> WallBrushes; + CUtlVector<CPolyhedron *> 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<CPortal_Player*>( 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<CPolyhedron *> *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<CPolyhedron *> *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<CBaseEntity *>(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<UTIL_Remove_PhysicsStack_t> 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 + + + + + + + + + + + + + + |