summaryrefslogtreecommitdiff
path: root/engine/world.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engine/world.cpp')
-rw-r--r--engine/world.cpp320
1 files changed, 320 insertions, 0 deletions
diff --git a/engine/world.cpp b/engine/world.cpp
new file mode 100644
index 0000000..4107712
--- /dev/null
+++ b/engine/world.cpp
@@ -0,0 +1,320 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//===========================================================================//
+#include "quakedef.h"
+#include "world.h"
+#include "eiface.h"
+#include "server.h"
+#include "cmodel_engine.h"
+#include "gl_model_private.h"
+#include "sv_main.h"
+#include "vengineserver_impl.h"
+#include "collisionutils.h"
+#include "vphysics_interface.h"
+#include "ispatialpartitioninternal.h"
+#include "staticpropmgr.h"
+#include "shadowmgr.h"
+#include "string_t.h"
+#include "enginetrace.h"
+#include "sys_dll.h"
+
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//============================================================================
+
+
+/*
+===============
+SV_ClearWorld
+
+===============
+*/
+void SV_ClearWorld (void)
+{
+ MDLCACHE_COARSE_LOCK_(g_pMDLCache);
+ // Clean up static props from the previous level
+#if !defined( SWDS )
+ g_pShadowMgr->LevelShutdown();
+#endif // SWDS
+ StaticPropMgr()->LevelShutdown();
+
+ for ( int i = 0; i < 3; i++ )
+ {
+ if ( host_state.worldmodel->mins[i] < MIN_COORD_INTEGER || host_state.worldmodel->maxs[i] > MAX_COORD_INTEGER )
+ {
+ Host_EndGame(true, "Map coordinate extents are too large!!\nCheck for errors!\n" );
+ }
+ }
+ SpatialPartition()->Init( host_state.worldmodel->mins, host_state.worldmodel->maxs );
+
+ // Load all static props into the spatial partition
+ StaticPropMgr()->LevelInit();
+#if !defined( SWDS )
+ g_pShadowMgr->LevelInit( host_state.worldbrush->numsurfaces );
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Trigger world-space bounds
+//-----------------------------------------------------------------------------
+static void CM_TriggerWorldSpaceBounds( ICollideable *pCollideable, Vector *pMins, Vector *pMaxs )
+{
+ if ( pCollideable->GetSolidFlags() & FSOLID_USE_TRIGGER_BOUNDS )
+ {
+ pCollideable->WorldSpaceTriggerBounds( pMins, pMaxs );
+ }
+ else
+ {
+ CM_WorldSpaceBounds( pCollideable, pMins, pMaxs );
+ }
+}
+
+static void CM_GetCollideableTriggerTestBox( ICollideable *pCollide, Vector *pMins, Vector *pMaxs, bool bUseAccurateBbox )
+{
+ if ( bUseAccurateBbox && pCollide->GetSolid() == SOLID_BBOX )
+ {
+ *pMins = pCollide->OBBMins();
+ *pMaxs = pCollide->OBBMaxs();
+ }
+ else
+ {
+ const Vector &vecStart = pCollide->GetCollisionOrigin();
+ pCollide->WorldSpaceSurroundingBounds( pMins, pMaxs );
+ *pMins -= vecStart;
+ *pMaxs -= vecStart;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Little enumeration class used to try touching all triggers
+//-----------------------------------------------------------------------------
+class CTouchLinks : public IPartitionEnumerator
+{
+public:
+ CTouchLinks( edict_t* pEnt, const Vector* pPrevAbsOrigin, bool accurateBboxTriggerChecks ) : m_TouchedEntities( 8, 8 )
+ {
+ m_pEnt = pEnt;
+ m_pCollide = pEnt->GetCollideable();
+ Assert( m_pCollide );
+
+ Vector vecMins, vecMaxs;
+ CM_GetCollideableTriggerTestBox( m_pCollide, &vecMins, &vecMaxs, accurateBboxTriggerChecks );
+ const Vector &vecStart = m_pCollide->GetCollisionOrigin();
+
+ if (pPrevAbsOrigin)
+ {
+ m_Ray.Init( *pPrevAbsOrigin, vecStart, vecMins, vecMaxs );
+ }
+ else
+ {
+ m_Ray.Init( vecStart, vecStart, vecMins, vecMaxs );
+ }
+ }
+
+ IterationRetval_t EnumElement( IHandleEntity *pHandleEntity )
+ {
+ // Static props should never be in the trigger list
+ Assert( !StaticPropMgr()->IsStaticProp( pHandleEntity ) );
+
+ IServerUnknown *pUnk = static_cast<IServerUnknown*>( pHandleEntity );
+ Assert( pUnk );
+
+ // Convert the IHandleEntity to an edict_t*...
+ // Context is the thing we're testing everything against
+ edict_t* pTouch = pUnk->GetNetworkable()->GetEdict();
+
+ // Can't bump against itself
+ if ( pTouch == m_pEnt )
+ return ITERATION_CONTINUE;
+
+ IServerEntity *pTriggerEntity = pTouch->GetIServerEntity();
+ if ( !pTriggerEntity )
+ return ITERATION_CONTINUE;
+
+ // Hmmm.. everything in this list should be a trigger....
+ ICollideable *pTriggerCollideable = pTriggerEntity->GetCollideable();
+ if ( !m_pCollide->ShouldTouchTrigger(pTriggerCollideable->GetSolidFlags()) )
+ return ITERATION_CONTINUE;
+
+ Assert(pTriggerCollideable->GetSolidFlags() & FSOLID_TRIGGER );
+
+ if ( pTriggerCollideable->GetSolidFlags() & FSOLID_USE_TRIGGER_BOUNDS )
+ {
+ Vector vecTriggerMins, vecTriggerMaxs;
+ pTriggerCollideable->WorldSpaceTriggerBounds( &vecTriggerMins, &vecTriggerMaxs );
+ if ( !IsBoxIntersectingRay( vecTriggerMins, vecTriggerMaxs, m_Ray ) )
+ {
+ return ITERATION_CONTINUE;
+ }
+ }
+ else
+ {
+ trace_t tr;
+ g_pEngineTraceServer->ClipRayToCollideable( m_Ray, MASK_SOLID, pTriggerCollideable, &tr );
+ if ( !(tr.contents & MASK_SOLID) )
+ return ITERATION_CONTINUE;
+ }
+
+ m_TouchedEntities.AddToTail( pTouch );
+
+ return ITERATION_CONTINUE;
+ }
+
+ void HandleTouchedEntities( )
+ {
+ for ( int i = 0; i < m_TouchedEntities.Count(); ++i )
+ {
+ serverGameEnts->MarkEntitiesAsTouching( m_TouchedEntities[i], m_pEnt );
+ }
+ }
+
+ Ray_t m_Ray;
+
+private:
+ edict_t *m_pEnt;
+ ICollideable *m_pCollide;
+ CUtlVector< edict_t* > m_TouchedEntities;
+};
+
+
+// enumerator class that's used to update touch links for a trigger when
+// it moves or changes solid type
+class CTriggerMoved : public IPartitionEnumerator
+{
+public:
+ CTriggerMoved( bool accurateBboxTriggerChecks ) : m_TouchedEntities( 8, 8 )
+ {
+ m_bAccurateBBoxCheck = accurateBboxTriggerChecks;
+ }
+
+ void TriggerMoved( edict_t *pTriggerEntity )
+ {
+ m_pTriggerEntity = pTriggerEntity;
+ m_pTrigger = pTriggerEntity->GetCollideable();
+ m_triggerSolidFlags = m_pTrigger->GetSolidFlags();
+ Vector vecAbsMins, vecAbsMaxs;
+ CM_TriggerWorldSpaceBounds( m_pTrigger, &vecAbsMins, &vecAbsMaxs );
+ SpatialPartition()->EnumerateElementsInBox( PARTITION_ENGINE_SOLID_EDICTS,
+ vecAbsMins, vecAbsMaxs, false, this );
+ }
+
+ IterationRetval_t EnumElement( IHandleEntity *pHandleEntity )
+ {
+ // skip static props, the game DLL doesn't care about them
+ if ( StaticPropMgr()->IsStaticProp( pHandleEntity ) )
+ return ITERATION_CONTINUE;
+
+ IServerUnknown *pUnk = static_cast< IServerUnknown* >( pHandleEntity );
+ Assert( pUnk );
+
+ // Convert the user ID to and edict_t*...
+ edict_t* pTouch = pUnk->GetNetworkable()->GetEdict();
+ Assert( pTouch );
+ ICollideable *pTouchCollide = pUnk->GetCollideable();
+
+ // Can't ever touch itself because it's in the other list
+ if ( pTouchCollide == m_pTrigger )
+ return ITERATION_CONTINUE;
+
+ if ( !pTouchCollide->ShouldTouchTrigger(m_triggerSolidFlags) )
+ return ITERATION_CONTINUE;
+
+ IServerEntity *serverEntity = pTouch->GetIServerEntity();
+ if ( !serverEntity )
+ return ITERATION_CONTINUE;
+
+ // FIXME: Should we be using the surrounding bounds here?
+ Vector vecMins, vecMaxs;
+ CM_GetCollideableTriggerTestBox( pTouchCollide, &vecMins, &vecMaxs, m_bAccurateBBoxCheck );
+
+ const Vector &vecStart = pTouchCollide->GetCollisionOrigin();
+ Ray_t ray;
+ ray.Init( vecStart, vecStart, vecMins, vecMaxs );
+
+ if ( m_pTrigger->GetSolidFlags() & FSOLID_USE_TRIGGER_BOUNDS )
+ {
+ Vector vecTriggerMins, vecTriggerMaxs;
+ m_pTrigger->WorldSpaceTriggerBounds( &vecTriggerMins, &vecTriggerMaxs );
+ if ( !IsBoxIntersectingRay( vecTriggerMins, vecTriggerMaxs, ray ) )
+ {
+ return ITERATION_CONTINUE;
+ }
+ }
+ else
+ {
+ trace_t tr;
+ g_pEngineTraceServer->ClipRayToCollideable( ray, MASK_SOLID, m_pTrigger, &tr );
+ if ( !(tr.contents & MASK_SOLID) )
+ return ITERATION_CONTINUE;
+ }
+
+ m_TouchedEntities.AddToTail( pTouch );
+
+ return ITERATION_CONTINUE;
+ }
+
+ void HandleTouchedEntities( )
+ {
+ for ( int i = 0; i < m_TouchedEntities.Count(); ++i )
+ {
+ serverGameEnts->MarkEntitiesAsTouching( m_TouchedEntities[i], m_pTriggerEntity );
+ }
+ }
+
+private:
+ edict_t* m_pTriggerEntity;
+ ICollideable* m_pTrigger;
+ int m_triggerSolidFlags;
+ Vector m_vecDelta;
+ CUtlVector< edict_t* > m_TouchedEntities;
+ bool m_bAccurateBBoxCheck;
+};
+
+
+//-----------------------------------------------------------------------------
+// Touches triggers. Or, if it is a trigger, causes other things to touch it
+// returns true if untouch needs to be checked
+//-----------------------------------------------------------------------------
+void SV_TriggerMoved( edict_t *pTriggerEnt, bool accurateBboxTriggerChecks )
+{
+ CTriggerMoved triggerEnum( accurateBboxTriggerChecks );
+ triggerEnum.TriggerMoved( pTriggerEnt );
+ triggerEnum.HandleTouchedEntities( );
+}
+
+
+void SV_SolidMoved( edict_t *pSolidEnt, ICollideable *pSolidCollide, const Vector* pPrevAbsOrigin, bool accurateBboxTriggerChecks )
+{
+ if (!pPrevAbsOrigin)
+ {
+ CTouchLinks touchEnumerator(pSolidEnt, NULL, accurateBboxTriggerChecks);
+
+ Vector vecWorldMins, vecWorldMaxs;
+ pSolidCollide->WorldSpaceSurroundingBounds( &vecWorldMins, &vecWorldMaxs );
+
+ SpatialPartition()->EnumerateElementsInBox( PARTITION_ENGINE_TRIGGER_EDICTS,
+ vecWorldMins, vecWorldMaxs, false, &touchEnumerator );
+
+ touchEnumerator.HandleTouchedEntities( );
+ }
+ else
+ {
+ CTouchLinks touchEnumerator(pSolidEnt, pPrevAbsOrigin, accurateBboxTriggerChecks);
+
+ // A version that checks against an extruded ray indicating the motion
+ SpatialPartition()->EnumerateElementsAlongRay( PARTITION_ENGINE_TRIGGER_EDICTS,
+ touchEnumerator.m_Ray, false, &touchEnumerator );
+
+ touchEnumerator.HandleTouchedEntities( );
+ }
+}
+