source-engine/game/shared/portal/PortalSimulation.h
2022-04-16 12:05:19 +03:00

482 lines
16 KiB
C++

//========= 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