summaryrefslogtreecommitdiff
path: root/game/shared/portal/PortalSimulation.h
blob: 91c66511982ebde366ae8a2357f7bb6b2b61b480 (plain) (blame)
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