|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Provides structures and classes necessary to simulate a portal.
//
// $NoKeywords: $
//=====================================================================================//
#ifndef PORTALSIMULATION_H
#define PORTALSIMULATION_H
#ifdef _WIN32
#pragma once
#endif
#include "mathlib/polyhedron.h"
#include "const.h"
#include "tier1/utlmap.h"
#include "tier1/utlvector.h"
#define PORTAL_SIMULATORS_EMBED_GUID //define this to embed a unique integer with each portal simulator for debugging purposes
struct StaticPropPolyhedronGroups_t //each static prop is made up of a group of polyhedrons, these help us pull those groups from an array
{ int iStartIndex; int iNumPolyhedrons; };
enum PortalSimulationEntityFlags_t { PSEF_OWNS_ENTITY = (1 << 0), //this environment is responsible for the entity's physics objects
PSEF_OWNS_PHYSICS = (1 << 1), PSEF_IS_IN_PORTAL_HOLE = (1 << 2), //updated per-phyframe
PSEF_CLONES_ENTITY_FROM_MAIN = (1 << 3), //entity is close enough to the portal to affect objects intersecting the portal
//PSEF_HAS_LINKED_CLONE = (1 << 1), //this environment has a clone of the entity which is transformed from its linked portal
};
enum PS_PhysicsObjectSourceType_t { PSPOST_LOCAL_BRUSHES, PSPOST_REMOTE_BRUSHES, PSPOST_LOCAL_STATICPROPS, PSPOST_REMOTE_STATICPROPS, PSPOST_HOLYWALL_TUBE };
struct PortalTransformAsAngledPosition_t //a matrix transformation from this portal to the linked portal, stored as vector and angle transforms
{ Vector ptOriginTransform; QAngle qAngleTransform; };
inline bool LessFunc_Integer( const int &a, const int &b ) { return a < b; };
class CPortalSimulatorEventCallbacks //sends out notifications of events to game specific code
{ public: virtual void PortalSimulator_TookOwnershipOfEntity( CBaseEntity *pEntity ) { }; virtual void PortalSimulator_ReleasedOwnershipOfEntity( CBaseEntity *pEntity ) { };
virtual void PortalSimulator_TookPhysicsOwnershipOfEntity( CBaseEntity *pEntity ) { }; virtual void PortalSimulator_ReleasedPhysicsOwnershipOfEntity( CBaseEntity *pEntity ) { }; };
//====================================================================================
// To any coder trying to understand the following nested structures....
//
// You may be wondering... why? wtf?
//
// The answer. The previous incarnation of server side portal simulation suffered
// terribly from evolving variables with increasingly cryptic names with no clear
// definition of what part of the system the variable was involved with.
//
// It's my hope that a nested structure with clear boundaries will eliminate that
// horrible, awful, nasty, frustrating confusion. (It was really really bad). This
// system has the added benefit of pseudo-forcing a naming structure.
//
// Lastly, if it all roots in one struct, we can const reference it out to allow
// easy reads without writes
//
// It's broken out like this to solve a few problems....
// 1. It cleans up intellisense when you don't actually define a structure
// within a structure.
// 2. Shorter typenames when you want to have a pointer/reference deep within
// the nested structure.
// 3. Needed at least one level removed from CPortalSimulator so
// pointers/references could be made while the primary instance of the
// data was private/protected.
//
// It may be slightly difficult to understand in it's broken out structure, but
// intellisense brings all the data together in a very cohesive manner for
// working with.
//====================================================================================
struct PS_PlacementData_t //stuff useful for geometric operations
{ Vector ptCenter; QAngle qAngles; Vector vForward; Vector vUp; Vector vRight; VPlane PortalPlane; VMatrix matThisToLinked; VMatrix matLinkedToThis; PortalTransformAsAngledPosition_t ptaap_ThisToLinked; PortalTransformAsAngledPosition_t ptaap_LinkedToThis; CPhysCollide *pHoleShapeCollideable; //used to test if a collideable is in the hole, should NOT be collided against in general
PS_PlacementData_t( void ) { memset( this, 0, sizeof( PS_PlacementData_t ) ); } };
struct PS_SD_Static_World_Brushes_t { CUtlVector<CPolyhedron *> Polyhedrons; //the building blocks of more complex collision
CPhysCollide *pCollideable; #ifndef CLIENT_DLL
IPhysicsObject *pPhysicsObject; PS_SD_Static_World_Brushes_t() : pCollideable(NULL), pPhysicsObject(NULL) {}; #else
PS_SD_Static_World_Brushes_t() : pCollideable(NULL) {}; #endif
};
struct PS_SD_Static_World_StaticProps_ClippedProp_t { StaticPropPolyhedronGroups_t PolyhedronGroup; CPhysCollide * pCollide; #ifndef CLIENT_DLL
IPhysicsObject * pPhysicsObject; #endif
IHandleEntity * pSourceProp;
int iTraceContents; short iTraceSurfaceProps; static CBaseEntity * pTraceEntity; static const char * szTraceSurfaceName; //same for all static props, here just for easy reference
static const int iTraceSurfaceFlags; //same for all static props, here just for easy reference
};
struct PS_SD_Static_World_StaticProps_t { CUtlVector<CPolyhedron *> Polyhedrons; //the building blocks of more complex collision
CUtlVector<PS_SD_Static_World_StaticProps_ClippedProp_t> ClippedRepresentations; bool bCollisionExists; //the shortcut to know if collideables exist for each prop
bool bPhysicsExists; //the shortcut to know if physics obects exist for each prop
PS_SD_Static_World_StaticProps_t( void ) : bCollisionExists( false ), bPhysicsExists( false ) { }; };
struct PS_SD_Static_World_t //stuff in front of the portal
{ PS_SD_Static_World_Brushes_t Brushes; PS_SD_Static_World_StaticProps_t StaticProps; };
struct PS_SD_Static_Wall_Local_Tube_t //a minimal tube, an object must fit inside this to be eligible for portaling
{ CUtlVector<CPolyhedron *> Polyhedrons; //the building blocks of more complex collision
CPhysCollide *pCollideable;
#ifndef CLIENT_DLL
IPhysicsObject *pPhysicsObject; PS_SD_Static_Wall_Local_Tube_t() : pCollideable(NULL), pPhysicsObject(NULL) {}; #else
PS_SD_Static_Wall_Local_Tube_t() : pCollideable(NULL) {}; #endif
};
struct PS_SD_Static_Wall_Local_Brushes_t { CUtlVector<CPolyhedron *> Polyhedrons; //the building blocks of more complex collision
CPhysCollide *pCollideable;
#ifndef CLIENT_DLL
IPhysicsObject *pPhysicsObject; PS_SD_Static_Wall_Local_Brushes_t() : pCollideable(NULL), pPhysicsObject(NULL) {}; #else
PS_SD_Static_Wall_Local_Brushes_t() : pCollideable(NULL) {}; #endif
};
struct PS_SD_Static_Wall_Local_t //things in the wall that are completely independant of having a linked portal
{ PS_SD_Static_Wall_Local_Tube_t Tube; PS_SD_Static_Wall_Local_Brushes_t Brushes; };
struct PS_SD_Static_Wall_RemoteTransformedToLocal_Brushes_t { IPhysicsObject *pPhysicsObject; PS_SD_Static_Wall_RemoteTransformedToLocal_Brushes_t() : pPhysicsObject(NULL) {}; };
struct PS_SD_Static_Wall_RemoteTransformedToLocal_StaticProps_t { CUtlVector<IPhysicsObject *> PhysicsObjects; };
struct PS_SD_Static_Wall_RemoteTransformedToLocal_t //things taken from the linked portal's "World" collision and transformed into local space
{ PS_SD_Static_Wall_RemoteTransformedToLocal_Brushes_t Brushes; PS_SD_Static_Wall_RemoteTransformedToLocal_StaticProps_t StaticProps; };
struct PS_SD_Static_Wall_t //stuff behind the portal
{ PS_SD_Static_Wall_Local_t Local; #ifndef CLIENT_DLL
PS_SD_Static_Wall_RemoteTransformedToLocal_t RemoteTransformedToLocal; #endif
};
struct PS_SD_Static_SurfaceProperties_t //surface properties to pretend every collideable here is using
{ int contents; csurface_t surface; CBaseEntity *pEntity; };
struct PS_SD_Static_t //stuff that doesn't move around
{ PS_SD_Static_World_t World; PS_SD_Static_Wall_t Wall; PS_SD_Static_SurfaceProperties_t SurfaceProperties; };
class CPhysicsShadowClone;
struct PS_SD_Dynamic_PhysicsShadowClones_t { CUtlVector<CBaseEntity *> ShouldCloneFromMain; //a list of entities that should be cloned from main if physics simulation is enabled
//in single-environment mode, this helps us track who should collide with who
CUtlVector<CPhysicsShadowClone *> FromLinkedPortal; };
struct PS_SD_Dynamic_t //stuff that moves around
{ unsigned int EntFlags[MAX_EDICTS]; //flags maintained for every entity in the world based on its index
PS_SD_Dynamic_PhysicsShadowClones_t ShadowClones;
CUtlVector<CBaseEntity *> OwnedEntities;
PS_SD_Dynamic_t() { memset( EntFlags, 0, sizeof( EntFlags ) ); } };
class CPSCollisionEntity;
struct PS_SimulationData_t //compartmentalized data for coherent management
{ PS_SD_Static_t Static;
#ifndef CLIENT_DLL
PS_SD_Dynamic_t Dynamic;
IPhysicsEnvironment *pPhysicsEnvironment; CPSCollisionEntity *pCollisionEntity; //the entity we'll be tying physics objects to for collision
PS_SimulationData_t() : pPhysicsEnvironment(NULL), pCollisionEntity(NULL) {}; #endif
};
struct PS_InternalData_t { PS_PlacementData_t Placement; PS_SimulationData_t Simulation; };
class CPortalSimulator { public: CPortalSimulator( void ); ~CPortalSimulator( void );
void MoveTo( const Vector &ptCenter, const QAngle &angles ); void ClearEverything( void );
void AttachTo( CPortalSimulator *pLinkedPortalSimulator ); void DetachFromLinked( void ); //detach portals to sever the connection, saves work when planning on moving both portals
CPortalSimulator *GetLinkedPortalSimulator( void ) const;
void SetPortalSimulatorCallbacks( CPortalSimulatorEventCallbacks *pCallbacks ); bool IsReadyToSimulate( void ) const; //is active and linked to another portal
void SetCollisionGenerationEnabled( bool bEnabled ); //enable/disable collision generation for the hole in the wall, needed for proper vphysics simulation
bool IsCollisionGenerationEnabled( void ) const;
void SetVPhysicsSimulationEnabled( bool bEnabled ); //enable/disable vphysics simulation. Will automatically update the linked portal to be the same
bool IsSimulatingVPhysics( void ) const; //this portal is setup to handle any physically simulated object, false means the portal is handling player movement only
bool EntityIsInPortalHole( CBaseEntity *pEntity ) const; //true if the entity is within the portal cutout bounds and crossing the plane. Not just *near* the portal
bool EntityHitBoxExtentIsInPortalHole( CBaseAnimating *pBaseAnimating ) const; //true if the entity is within the portal cutout bounds and crossing the plane. Not just *near* the portal
void RemoveEntityFromPortalHole( CBaseEntity *pEntity ); //if the entity is in the portal hole, this forcibly moves it out by any means possible
bool RayIsInPortalHole( const Ray_t &ray ) const; //traces a ray against the same detector for EntityIsInPortalHole(), bias is towards false positives
#ifndef CLIENT_DLL
int GetMoveableOwnedEntities( CBaseEntity **pEntsOut, int iEntOutLimit ); //gets owned entities that aren't either world or static props. Excludes fake portal ents such as physics clones
static CPortalSimulator *GetSimulatorThatOwnsEntity( const CBaseEntity *pEntity ); //fairly cheap to call
static CPortalSimulator *GetSimulatorThatCreatedPhysicsObject( const IPhysicsObject *pObject, PS_PhysicsObjectSourceType_t *pOut_SourceType = NULL ); static void Pre_UTIL_Remove( CBaseEntity *pEntity ); static void Post_UTIL_Remove( CBaseEntity *pEntity );
//these three really should be made internal and the public interface changed to a "watch this entity" setup
void TakeOwnershipOfEntity( CBaseEntity *pEntity ); //general ownership, not necessarily physics ownership
void ReleaseOwnershipOfEntity( CBaseEntity *pEntity, bool bMovingToLinkedSimulator = false ); //if bMovingToLinkedSimulator is true, the code skips some steps that are going to be repeated when the entity is added to the other simulator
void ReleaseAllEntityOwnership( void ); //go back to not owning any entities
//void TeleportEntityToLinkedPortal( CBaseEntity *pEntity );
void StartCloningEntity( CBaseEntity *pEntity ); void StopCloningEntity( CBaseEntity *pEntity );
bool OwnsEntity( const CBaseEntity *pEntity ) const; bool OwnsPhysicsForEntity( const CBaseEntity *pEntity ) const;
bool CreatedPhysicsObject( const IPhysicsObject *pObject, PS_PhysicsObjectSourceType_t *pOut_SourceType = NULL ) const; //true if the physics object was generated by this portal simulator
static void PrePhysFrame( void ); static void PostPhysFrame( void );
#endif //#ifndef CLIENT_DLL
#ifdef PORTAL_SIMULATORS_EMBED_GUID
int GetPortalSimulatorGUID( void ) const { return m_iPortalSimulatorGUID; }; #endif
protected: bool m_bLocalDataIsReady; //this side of the portal is properly setup, no guarantees as to linkage to another portal
bool m_bSimulateVPhysics; bool m_bGenerateCollision; bool m_bSharedCollisionConfiguration; //when portals are in certain configurations, they need to cross-clip and share some collision data and things get nasty. For the love of all that is holy, pray that this is false.
CPortalSimulator *m_pLinkedPortal; bool m_bInCrossLinkedFunction; //A flag to mark that we're already in a linked function and that the linked portal shouldn't call our side
CPortalSimulatorEventCallbacks *m_pCallbacks; #ifdef PORTAL_SIMULATORS_EMBED_GUID
int m_iPortalSimulatorGUID; #endif
struct { bool bPolyhedronsGenerated; bool bLocalCollisionGenerated; bool bLinkedCollisionGenerated; bool bLocalPhysicsGenerated; bool bLinkedPhysicsGenerated; } m_CreationChecklist;
friend class CPSCollisionEntity;
#ifndef CLIENT_DLL //physics handled purely by server side
void TakePhysicsOwnership( CBaseEntity *pEntity ); void ReleasePhysicsOwnership( CBaseEntity *pEntity, bool bContinuePhysicsCloning = true, bool bMovingToLinkedSimulator = false );
void CreateAllPhysics( void ); void CreateMinimumPhysics( void ); //stuff needed by any part of physics simulations
void CreateLocalPhysics( void ); void CreateLinkedPhysics( void );
void ClearAllPhysics( void ); void ClearMinimumPhysics( void ); void ClearLocalPhysics( void ); void ClearLinkedPhysics( void );
void ClearLinkedEntities( void ); //gets rid of transformed shadow clones
#endif
void CreateAllCollision( void ); void CreateLocalCollision( void ); void CreateLinkedCollision( void );
void ClearAllCollision( void ); void ClearLinkedCollision( void ); void ClearLocalCollision( void );
void CreatePolyhedrons( void ); //carves up the world around the portal's position into sets of polyhedrons
void ClearPolyhedrons( void );
void UpdateLinkMatrix( void );
void MarkAsOwned( CBaseEntity *pEntity ); void MarkAsReleased( CBaseEntity *pEntity );
PS_InternalData_t m_InternalData;
public: const PS_InternalData_t &m_DataAccess;
friend class CPS_AutoGameSys_EntityListener; };
extern CUtlVector<CPortalSimulator *> const &g_PortalSimulators;
#ifndef CLIENT_DLL
class CPSCollisionEntity : public CBaseEntity { DECLARE_CLASS( CPSCollisionEntity, CBaseEntity ); private: CPortalSimulator *m_pOwningSimulator;
public: CPSCollisionEntity( void ); virtual ~CPSCollisionEntity( void );
virtual void Spawn( void ); virtual void Activate( void ); virtual int ObjectCaps( void ); virtual IPhysicsObject *VPhysicsGetObject( void ); virtual int VPhysicsGetObjectList( IPhysicsObject **pList, int listMax ); virtual void UpdateOnRemove( void ); virtual bool ShouldCollide( int collisionGroup, int contentsMask ) const; virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ) {} virtual void VPhysicsFriction( IPhysicsObject *pObject, float energy, int surfaceProps, int surfacePropsHit ) {}
static bool IsPortalSimulatorCollisionEntity( const CBaseEntity *pEntity ); friend class CPortalSimulator; }; #endif
#ifndef CLIENT_DLL
inline bool CPortalSimulator::OwnsEntity( const CBaseEntity *pEntity ) const { return ((m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] & PSEF_OWNS_ENTITY) != 0); }
inline bool CPortalSimulator::OwnsPhysicsForEntity( const CBaseEntity *pEntity ) const { return ((m_InternalData.Simulation.Dynamic.EntFlags[pEntity->entindex()] & PSEF_OWNS_PHYSICS) != 0); } #endif
inline bool CPortalSimulator::IsReadyToSimulate( void ) const { return m_bLocalDataIsReady && m_pLinkedPortal && m_pLinkedPortal->m_bLocalDataIsReady; }
inline bool CPortalSimulator::IsSimulatingVPhysics( void ) const { return m_bSimulateVPhysics; }
inline bool CPortalSimulator::IsCollisionGenerationEnabled( void ) const { return m_bGenerateCollision; }
inline CPortalSimulator *CPortalSimulator::GetLinkedPortalSimulator( void ) const { return m_pLinkedPortal; }
#endif //#ifndef PORTALSIMULATION_H
|