1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
|
//========= 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
|