summaryrefslogtreecommitdiff
path: root/vphysics/physics_object.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /vphysics/physics_object.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'vphysics/physics_object.cpp')
-rw-r--r--vphysics/physics_object.cpp2001
1 files changed, 2001 insertions, 0 deletions
diff --git a/vphysics/physics_object.cpp b/vphysics/physics_object.cpp
new file mode 100644
index 0000000..601772d
--- /dev/null
+++ b/vphysics/physics_object.cpp
@@ -0,0 +1,2001 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "ivp_surman_polygon.hxx"
+#include "ivp_compact_ledge.hxx"
+#include "ivp_compact_ledge_solver.hxx"
+#include "ivp_mindist.hxx"
+#include "ivp_friction.hxx"
+#include "ivp_phantom.hxx"
+#include "ivp_listener_collision.hxx"
+#include "ivp_clustering_visualizer.hxx"
+#include "ivp_anomaly_manager.hxx"
+#include "ivp_collision_filter.hxx"
+
+#include "hk_mopp/ivp_surman_mopp.hxx"
+#include "hk_mopp/ivp_compact_mopp.hxx"
+
+#include "ivp_compact_surface.hxx"
+#include "physics_trace.h"
+#include "physics_shadow.h"
+#include "physics_friction.h"
+#include "physics_constraint.h"
+#include "bspflags.h"
+#include "vphysics/player_controller.h"
+#include "vphysics/friction.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+extern IPhysicsCollision *physcollision;
+
+// UNDONE: Make this a stack variable / member variable of some save/load object or function?
+// NOTE: This keeps a list of objects who were saved while asleep, but not created asleep
+// So some info will be lost unless it's regenerated after loading.
+struct postrestore_objectlist_t
+{
+ CPhysicsObject *pObject;
+ bool growFriction;
+ bool enableCollisions;
+
+ void Defaults()
+ {
+ pObject = NULL;
+ growFriction = false;
+ enableCollisions = false;
+ }
+};
+
+static CUtlVector<postrestore_objectlist_t> g_PostRestoreObjectList;
+
+// This angular basis is the integral of each differential drag area's torque over the whole OBB
+// For each axis, each face, the integral is (1/Iaxis) * (1/3 * w^2l^3 + 1/2 * w^4l + lw^2h^2)
+// l,w, & h are half widths - where l is in the direction of the axis, w is in the plane (l/w plane) of the face,
+// and h is perpendicular to the face. So for each axis, you sum up this integral over 2 pairs of faces
+// (this function returns the integral for one pair of opposite faces, not one face)
+static float AngDragIntegral( float invInertia, float l, float w, float h )
+{
+ float w2 = w*w;
+ float l2 = l*l;
+ float h2 = h*h;
+
+ return invInertia * ( (1.f/3.f)*w2*l*l2 + 0.5 * w2*w2*l + l*w2*h2 );
+}
+
+
+CPhysicsObject::CPhysicsObject( void )
+{
+#ifdef _WIN32
+ void *pData = ((char *)this) + sizeof(void *); // offset beyond vtable
+ int dataSize = sizeof(*this) - sizeof(void *);
+
+ Assert( pData == &m_pGameData );
+
+ memset( pData, 0, dataSize );
+#elif POSIX
+
+ //!!HACK HACK - rework this if we ever change compiler versions (from gcc 3.2!!!)
+ void *pData = ((char *)this) + sizeof(void *); // offset beyond vtable
+ int dataSize = sizeof(*this) - sizeof(void *);
+
+ Assert( pData == &m_pGameData );
+
+ memset( pData, 0, dataSize );
+#else
+#error
+#endif
+
+ // HACKHACK: init this as a sphere until someone attaches a surfacemanager
+ m_collideType = COLLIDE_BALL;
+ m_contentsMask = CONTENTS_SOLID;
+ m_hasTouchedDynamic = 0;
+}
+
+void CPhysicsObject::Init( const CPhysCollide *pCollisionModel, IVP_Real_Object *pObject, int materialIndex, float volume, float drag, float angDrag )
+{
+ m_pCollide = pCollisionModel;
+ m_materialIndex = materialIndex;
+ m_pObject = pObject;
+ pObject->client_data = (void *)this;
+ m_pGameData = NULL;
+ m_gameFlags = 0;
+ m_gameIndex = 0;
+ m_sleepState = OBJ_SLEEP; // objects start asleep
+ m_callbacks = CALLBACK_GLOBAL_COLLISION|CALLBACK_GLOBAL_FRICTION|CALLBACK_FLUID_TOUCH|CALLBACK_GLOBAL_TOUCH|CALLBACK_GLOBAL_COLLIDE_STATIC|CALLBACK_DO_FLUID_SIMULATION;
+ m_activeIndex = 0xFFFF;
+ m_pShadow = NULL;
+ m_shadowTempGravityDisable = false;
+ m_forceSilentDelete = false;
+ m_dragBasis = vec3_origin;
+ m_angDragBasis = vec3_origin;
+
+ if ( !IsStatic() && GetCollide() )
+ {
+ RecomputeDragBases();
+ }
+ else
+ {
+ drag = 0;
+ angDrag = 0;
+ }
+
+ m_dragCoefficient = drag;
+ m_angDragCoefficient = angDrag;
+
+ SetVolume( volume );
+}
+
+CPhysicsObject::~CPhysicsObject( void )
+{
+ RemoveShadowController();
+
+ if ( m_pObject )
+ {
+ // prevents callbacks to the game code / unlink from this object
+ m_callbacks = 0;
+ m_pGameData = 0;
+ m_pObject->client_data = 0;
+
+ IVP_Core *pCore = m_pObject->get_core();
+ if ( pCore->physical_unmoveable == IVP_TRUE && pCore->controllers_of_core.n_elems )
+ {
+ // go ahead and notify them if this happens in the real world
+ for(int i = pCore->controllers_of_core.len()-1; i >=0 ;i-- )
+ {
+ IVP_Controller *my_controller = pCore->controllers_of_core.element_at(i);
+ my_controller->core_is_going_to_be_deleted_event(pCore);
+ Assert(my_controller==pCore->environment->get_gravity_controller());
+ }
+ }
+
+ // UNDONE: Don't free the surface manager here
+ // UNDONE: Remove reference to it by calling something in physics_collide
+ IVP_SurfaceManager *pSurman = GetSurfaceManager();
+ CPhysicsEnvironment *pVEnv = GetVPhysicsEnvironment();
+
+ // BUGBUG: Sometimes IVP will call a "revive" on the object we're deleting!
+ MEM_ALLOC_CREDIT();
+ if ( m_forceSilentDelete || (pVEnv && pVEnv->ShouldQuickDelete()) || !m_hasTouchedDynamic )
+ {
+ m_pObject->delete_silently();
+ }
+ else
+ {
+ m_pObject->delete_and_check_vicinity();
+ }
+ delete pSurman;
+ }
+}
+
+void CPhysicsObject::Wake( void )
+{
+ m_pObject->ensure_in_simulation();
+}
+
+// supported
+void CPhysicsObject::Sleep( void )
+{
+ m_pObject->disable_simulation();
+}
+
+
+bool CPhysicsObject::IsAsleep() const
+{
+ if ( m_sleepState == OBJ_AWAKE )
+ return false;
+
+ // double-check that we aren't pending
+ if ( m_pObject->get_core()->is_in_wakeup_vec )
+ return false;
+
+ return true;
+}
+
+void CPhysicsObject::NotifySleep( void )
+{
+ if ( m_sleepState == OBJ_AWAKE )
+ {
+ m_sleepState = OBJ_STARTSLEEP;
+ }
+ else
+ {
+ // UNDONE: This fails sometimes and we get sleep calls for a sleeping object, debug?
+ //Assert(m_sleepState==OBJ_STARTSLEEP);
+ m_sleepState = OBJ_SLEEP;
+ }
+}
+
+
+void CPhysicsObject::NotifyWake( void )
+{
+ m_asleepSinceCreation = false;
+ m_sleepState = OBJ_AWAKE;
+}
+
+
+void CPhysicsObject::SetCallbackFlags( unsigned short callbackflags )
+{
+#if IVP_ENABLE_VISUALIZER
+ unsigned short changedFlags = m_callbacks ^ callbackflags;
+ if ( changedFlags & CALLBACK_MARKED_FOR_TEST )
+ {
+ if ( callbackflags & CALLBACK_MARKED_FOR_TEST )
+ {
+ ENABLE_SHORTRANGE_VISUALIZATION(m_pObject);
+ ENABLE_LONGRANGE_VISUALIZATION(m_pObject);
+ }
+ else
+ {
+ DISABLE_SHORTRANGE_VISUALIZATION(m_pObject);
+ DISABLE_LONGRANGE_VISUALIZATION(m_pObject);
+ }
+ }
+#endif
+ m_callbacks = callbackflags;
+
+}
+
+
+unsigned short CPhysicsObject::GetCallbackFlags() const
+{
+ return m_callbacks;
+}
+
+
+void CPhysicsObject::SetGameFlags( unsigned short userFlags )
+{
+ m_gameFlags = userFlags;
+}
+
+unsigned short CPhysicsObject::GetGameFlags() const
+{
+ return m_gameFlags;
+}
+
+
+void CPhysicsObject::SetGameIndex( unsigned short gameIndex )
+{
+ m_gameIndex = gameIndex;
+}
+
+unsigned short CPhysicsObject::GetGameIndex() const
+{
+ return m_gameIndex;
+}
+
+bool CPhysicsObject::IsStatic() const
+{
+ if ( m_pObject->get_core()->physical_unmoveable )
+ return true;
+
+ return false;
+}
+
+
+void CPhysicsObject::EnableCollisions( bool enable )
+{
+ if ( enable )
+ {
+ m_callbacks |= CALLBACK_ENABLING_COLLISION;
+ BEGIN_IVP_ALLOCATION();
+ m_pObject->enable_collision_detection( IVP_TRUE );
+ END_IVP_ALLOCATION();
+ m_callbacks &= ~CALLBACK_ENABLING_COLLISION;
+ }
+ else
+ {
+ if ( IsCollisionEnabled() )
+ {
+ // Delete all contact points with this physics object because it's collision is becoming disabled
+ IPhysicsFrictionSnapshot *pSnapshot = CreateFrictionSnapshot();
+ while ( pSnapshot->IsValid() )
+ {
+ pSnapshot->MarkContactForDelete();
+ pSnapshot->NextFrictionData();
+ }
+ pSnapshot->DeleteAllMarkedContacts( true );
+ DestroyFrictionSnapshot( pSnapshot );
+ }
+
+ m_pObject->enable_collision_detection( IVP_FALSE );
+ }
+}
+
+void CPhysicsObject::RecheckCollisionFilter()
+{
+ if ( CallbackFlags() & CALLBACK_MARKED_FOR_DELETE )
+ return;
+
+ m_callbacks |= CALLBACK_ENABLING_COLLISION;
+ BEGIN_IVP_ALLOCATION();
+ m_pObject->recheck_collision_filter();
+ // UNDONE: do a RecheckContactPoints() here?
+ END_IVP_ALLOCATION();
+ m_callbacks &= ~CALLBACK_ENABLING_COLLISION;
+}
+
+void CPhysicsObject::RecheckContactPoints()
+{
+ IVP_Environment *pEnv = m_pObject->get_environment();
+ IVP_Collision_Filter *coll_filter = pEnv->get_collision_filter();
+ IPhysicsFrictionSnapshot *pSnapshot = CreateFrictionSnapshot();
+ while ( pSnapshot->IsValid() )
+ {
+ CPhysicsObject *pOther = static_cast<CPhysicsObject *>(pSnapshot->GetObject(1));
+ if ( !coll_filter->check_objects_for_collision_detection( m_pObject, pOther->m_pObject ) )
+ {
+ pSnapshot->MarkContactForDelete();
+ }
+ pSnapshot->NextFrictionData();
+ }
+ pSnapshot->DeleteAllMarkedContacts( true );
+ DestroyFrictionSnapshot( pSnapshot );
+}
+
+CPhysicsEnvironment *CPhysicsObject::GetVPhysicsEnvironment()
+{
+ return (CPhysicsEnvironment *) (m_pObject->get_environment()->client_data);
+}
+
+const CPhysicsEnvironment *CPhysicsObject::GetVPhysicsEnvironment() const
+{
+ return (CPhysicsEnvironment *) (m_pObject->get_environment()->client_data);
+}
+
+
+bool CPhysicsObject::IsControlling( const IVP_Controller *pController ) const
+{
+ IVP_Core *pCore = m_pObject->get_core();
+ for ( int i = 0; i < pCore->controllers_of_core.len(); i++ )
+ {
+ // already controlling this core?
+ if ( pCore->controllers_of_core.element_at(i) == pController )
+ return true;
+ }
+
+ return false;
+}
+
+bool CPhysicsObject::IsGravityEnabled() const
+{
+ if ( !IsStatic() )
+ {
+ return IsControlling( m_pObject->get_core()->environment->get_gravity_controller() );
+ }
+
+ return false;
+}
+
+bool CPhysicsObject::IsDragEnabled() const
+{
+ if ( !IsStatic() )
+ {
+ return IsControlling( GetVPhysicsEnvironment()->GetDragController() );
+ }
+
+ return false;
+}
+
+
+bool CPhysicsObject::IsMotionEnabled() const
+{
+ return m_pObject->get_core()->pinned ? false : true;
+}
+
+
+bool CPhysicsObject::IsMoveable() const
+{
+ if ( IsStatic() || !IsMotionEnabled() )
+ return false;
+ return true;
+}
+
+
+void CPhysicsObject::EnableGravity( bool enable )
+{
+ if ( IsStatic() )
+ return;
+
+
+ bool isEnabled = IsGravityEnabled();
+
+ if ( enable == isEnabled )
+ return;
+
+ IVP_Controller *pGravity = m_pObject->get_core()->environment->get_gravity_controller();
+ if ( enable )
+ {
+ m_pObject->get_core()->add_core_controller( pGravity );
+ }
+ else
+ {
+ m_pObject->get_core()->rem_core_controller( pGravity );
+ }
+}
+
+void CPhysicsObject::EnableDrag( bool enable )
+{
+ if ( IsStatic() )
+ return;
+
+ bool isEnabled = IsDragEnabled();
+
+ if ( enable == isEnabled )
+ return;
+
+ IVP_Controller *pDrag = GetVPhysicsEnvironment()->GetDragController();
+
+ if ( enable )
+ {
+ m_pObject->get_core()->add_core_controller( pDrag );
+ }
+ else
+ {
+ m_pObject->get_core()->rem_core_controller( pDrag );
+ }
+}
+
+
+void CPhysicsObject::SetDragCoefficient( float *pDrag, float *pAngularDrag )
+{
+ if ( pDrag )
+ {
+ m_dragCoefficient = *pDrag;
+ }
+ if ( pAngularDrag )
+ {
+ m_angDragCoefficient = *pAngularDrag;
+ }
+
+ EnableDrag( m_dragCoefficient || m_angDragCoefficient );
+}
+
+
+void CPhysicsObject::RecomputeDragBases()
+{
+ if ( IsStatic() || !GetCollide() )
+ return;
+
+ // Basically we are computing drag as an OBB. Get OBB extents for projection
+ // scale those extents by appropriate mass/inertia to compute velocity directly (not force)
+ // in the controller
+ // NOTE: Compute these even if drag coefficients are zero, because the drag coefficient could change later
+
+ // Get an AABB for this object and use the area of each side as a basis for approximating cross-section area for drag
+ Vector dragMins, dragMaxs;
+ // NOTE: coordinates in/out of physcollision are in HL units, not IVP
+ // PERFORMANCE: Cache this? Expensive.
+ physcollision->CollideGetAABB( &dragMins, &dragMaxs, GetCollide(), vec3_origin, vec3_angle );
+
+ Vector areaFractions = physcollision->CollideGetOrthographicAreas( GetCollide() );
+ Vector delta = dragMaxs - dragMins;
+ ConvertPositionToIVP( delta.x, delta.y, delta.z );
+ delta.x = fabsf(delta.x);
+ delta.y = fabsf(delta.y);
+ delta.z = fabsf(delta.z);
+ // dragBasis is now the area of each side
+ m_dragBasis.x = delta.y * delta.z * areaFractions.x;
+ m_dragBasis.y = delta.x * delta.z * areaFractions.y;
+ m_dragBasis.z = delta.x * delta.y * areaFractions.z;
+ m_dragBasis *= GetInvMass();
+
+ const IVP_U_Float_Point *pInvRI = m_pObject->get_core()->get_inv_rot_inertia();
+
+ // This angular basis is the integral of each differential drag area's torque over the whole OBB
+ // need half lengths for this integral
+ delta *= 0.5;
+ // rotation about the x axis
+ m_angDragBasis.x = areaFractions.z * AngDragIntegral( pInvRI->k[0], delta.x, delta.y, delta.z ) + areaFractions.y * AngDragIntegral( pInvRI->k[0], delta.x, delta.z, delta.y );
+ // rotation about the y axis
+ m_angDragBasis.y = areaFractions.z * AngDragIntegral( pInvRI->k[1], delta.y, delta.x, delta.z ) + areaFractions.x * AngDragIntegral( pInvRI->k[1], delta.y, delta.z, delta.x );
+ // rotation about the z axis
+ m_angDragBasis.z = areaFractions.y * AngDragIntegral( pInvRI->k[2], delta.z, delta.x, delta.y ) + areaFractions.x * AngDragIntegral( pInvRI->k[2], delta.z, delta.y, delta.x );
+}
+
+
+
+void CPhysicsObject::EnableMotion( bool enable )
+{
+ if ( IsStatic() )
+ return;
+
+ bool isMoveable = IsMotionEnabled();
+
+ // no change
+ if ( isMoveable == enable )
+ return;
+
+ BEGIN_IVP_ALLOCATION();
+ m_pObject->set_pinned( enable ? IVP_FALSE : IVP_TRUE );
+ END_IVP_ALLOCATION();
+
+ if ( enable && IsHinged() )
+ {
+ BecomeHinged( m_hingedAxis-1 );
+ }
+ RecheckCollisionFilter();
+ RecheckContactPoints();
+}
+
+bool CPhysicsObject::IsControlledByGame() const
+{
+ if (m_pShadow && !m_pShadow->IsPhysicallyControlled())
+ return true;
+
+ if ( CallbackFlags() & CALLBACK_IS_PLAYER_CONTROLLER )
+ return true;
+
+ return false;
+}
+
+IPhysicsFrictionSnapshot *CPhysicsObject::CreateFrictionSnapshot()
+{
+ return ::CreateFrictionSnapshot( m_pObject );
+}
+
+void CPhysicsObject::DestroyFrictionSnapshot( IPhysicsFrictionSnapshot *pSnapshot )
+{
+ ::DestroyFrictionSnapshot(pSnapshot);
+}
+
+bool CPhysicsObject::IsMassCenterAtDefault() const
+{
+ // this is the actual mass center of the object as created
+ Vector massCenterHL = GetMassCenterLocalSpace();
+
+ // Get the default mass center to see if it has been changed
+ IVP_U_Float_Point massCenterIVPDefault;
+ Vector massCenterHLDefault;
+ GetObject()->get_surface_manager()->get_mass_center( &massCenterIVPDefault );
+ ConvertPositionToHL( massCenterIVPDefault, massCenterHLDefault );
+ float delta = (massCenterHLDefault - massCenterHL).Length();
+
+ return ( delta <= g_PhysicsUnits.collisionSweepIncrementalEpsilon ) ? true : false;
+}
+
+Vector CPhysicsObject::GetMassCenterLocalSpace() const
+{
+ if ( m_pObject->flags.shift_core_f_object_is_zero )
+ return vec3_origin;
+
+ Vector out;
+ ConvertPositionToHL( *m_pObject->get_shift_core_f_object(), out );
+ // core shift is what you add to the mass center to get the origin
+ // so we want the negative core shift (origin relative position of the mass center)
+ return -out;
+}
+
+
+void CPhysicsObject::SetGameData( void *pGameData )
+{
+ m_pGameData = pGameData;
+}
+
+void *CPhysicsObject::GetGameData( void ) const
+{
+ return m_pGameData;
+}
+
+void CPhysicsObject::SetMass( float mass )
+{
+ bool reset = false;
+
+ if ( !IsMoveable() )
+ {
+ reset = true;
+ EnableMotion(true);
+ }
+
+ Assert( mass > 0 );
+ mass = clamp( mass, 0, VPHYSICS_MAX_MASS ); // NOTE: Allow zero procedurally, but not by initialization
+ m_pObject->change_mass( mass );
+ SetVolume( m_volume );
+ RecomputeDragBases();
+ if ( reset )
+ {
+ EnableMotion(false);
+ }
+}
+
+float CPhysicsObject::GetMass( void ) const
+{
+ return m_pObject->get_core()->get_mass();
+}
+
+float CPhysicsObject::GetInvMass( void ) const
+{
+ return m_pObject->get_core()->get_inv_mass();
+}
+
+Vector CPhysicsObject::GetInertia( void ) const
+{
+ const IVP_U_Float_Point *pRI = m_pObject->get_core()->get_rot_inertia();
+
+ Vector hlInertia;
+ ConvertDirectionToHL( *pRI, hlInertia );
+ VectorAbs( hlInertia, hlInertia );
+ return hlInertia;
+}
+
+Vector CPhysicsObject::GetInvInertia( void ) const
+{
+ const IVP_U_Float_Point *pRI = m_pObject->get_core()->get_inv_rot_inertia();
+
+ Vector hlInvInertia;
+ ConvertDirectionToHL( *pRI, hlInvInertia );
+ VectorAbs( hlInvInertia, hlInvInertia );
+ return hlInvInertia;
+}
+
+
+void CPhysicsObject::SetInertia( const Vector &inertia )
+{
+ IVP_U_Float_Point ri;
+ ConvertDirectionToIVP( inertia, ri );
+ ri.k[0] = IVP_Inline_Math::fabsd(ri.k[0]);
+ ri.k[1] = IVP_Inline_Math::fabsd(ri.k[1]);
+ ri.k[2] = IVP_Inline_Math::fabsd(ri.k[2]);
+ m_pObject->get_core()->set_rotation_inertia( &ri );
+}
+
+
+void CPhysicsObject::GetDamping( float *speed, float *rot ) const
+{
+ IVP_Core *pCore = m_pObject->get_core();
+ if ( speed )
+ {
+ *speed = pCore->speed_damp_factor;
+ }
+ if ( rot )
+ {
+ *rot = pCore->rot_speed_damp_factor.k[0];
+ }
+}
+
+void CPhysicsObject::SetDamping( const float *speed, const float *rot )
+{
+ IVP_Core *pCore = m_pObject->get_core();
+ if ( speed )
+ {
+ pCore->speed_damp_factor = *speed;
+ }
+ if ( rot )
+ {
+ pCore->rot_speed_damp_factor.set( *rot, *rot, *rot );
+ }
+}
+
+void CPhysicsObject::SetVolume( float volume )
+{
+ m_volume = volume;
+ if ( volume != 0.f )
+ {
+ // minimum volume is 5 cubic inches - otherwise buoyancy can get unstable
+ if ( volume < 5.0f )
+ {
+ volume = 5.0f;
+ }
+ volume *= HL2IVP_FACTOR*HL2IVP_FACTOR*HL2IVP_FACTOR;
+ float density = GetMass() / volume;
+ float matDensity;
+ physprops->GetPhysicsProperties( GetMaterialIndexInternal(), &matDensity, NULL, NULL, NULL );
+ m_buoyancyRatio = density / matDensity;
+ }
+ else
+ {
+ m_buoyancyRatio = 1.0f;
+ }
+}
+
+float CPhysicsObject::GetVolume() const
+{
+ return m_volume;
+}
+
+
+void CPhysicsObject::SetBuoyancyRatio( float ratio )
+{
+ m_buoyancyRatio = ratio;
+}
+
+void CPhysicsObject::SetContents( unsigned int contents )
+{
+ m_contentsMask = contents;
+}
+
+// converts HL local units to HL world units
+void CPhysicsObject::LocalToWorld( Vector *worldPosition, const Vector &localPosition ) const
+{
+ matrix3x4_t matrix;
+ GetPositionMatrix( &matrix );
+ // copy in case the src == dest
+ VectorTransform( Vector(localPosition), matrix, *worldPosition );
+}
+
+// Converts world HL units to HL local/object units
+void CPhysicsObject::WorldToLocal( Vector *localPosition, const Vector &worldPosition ) const
+{
+ matrix3x4_t matrix;
+ GetPositionMatrix( &matrix );
+ // copy in case the src == dest
+ VectorITransform( Vector(worldPosition), matrix, *localPosition );
+}
+
+void CPhysicsObject::LocalToWorldVector( Vector *worldVector, const Vector &localVector ) const
+{
+ matrix3x4_t matrix;
+ GetPositionMatrix( &matrix );
+ // copy in case the src == dest
+ VectorRotate( Vector(localVector), matrix, *worldVector );
+}
+
+void CPhysicsObject::WorldToLocalVector( Vector *localVector, const Vector &worldVector ) const
+{
+ matrix3x4_t matrix;
+ GetPositionMatrix( &matrix );
+ // copy in case the src == dest
+ VectorIRotate( Vector(worldVector), matrix, *localVector );
+}
+
+
+// Apply force impulse (momentum) to the object
+void CPhysicsObject::ApplyForceCenter( const Vector &forceVector )
+{
+ if ( !IsMoveable() )
+ return;
+
+ IVP_U_Float_Point tmp;
+
+ ConvertForceImpulseToIVP( forceVector, tmp );
+ IVP_Core *core = m_pObject->get_core();
+ tmp.mult( core->get_inv_mass() );
+ m_pObject->async_add_speed_object_ws( &tmp );
+ ClampVelocity();
+}
+
+void CPhysicsObject::ApplyForceOffset( const Vector &forceVector, const Vector &worldPosition )
+{
+ if ( !IsMoveable() )
+ return;
+
+ IVP_U_Point pos;
+ IVP_U_Float_Point force;
+
+ ConvertForceImpulseToIVP( forceVector, force );
+ ConvertPositionToIVP( worldPosition, pos );
+
+ IVP_Core *core = m_pObject->get_core();
+ core->async_push_core_ws( &pos, &force );
+ Wake();
+ ClampVelocity();
+}
+
+void CPhysicsObject::CalculateForceOffset( const Vector &forceVector, const Vector &worldPosition, Vector *centerForce, AngularImpulse *centerTorque ) const
+{
+ IVP_U_Point pos;
+ IVP_U_Float_Point force;
+
+ ConvertPositionToIVP( forceVector, force );
+ ConvertPositionToIVP( worldPosition, pos );
+
+ IVP_Core *core = m_pObject->get_core();
+
+ const IVP_U_Matrix *m_world_f_core = core->get_m_world_f_core_PSI();
+
+ IVP_U_Float_Point point_d_ws;
+ point_d_ws.subtract(&pos, m_world_f_core->get_position());
+
+ IVP_U_Float_Point cross_point_dir;
+
+ cross_point_dir.calc_cross_product( &point_d_ws, &force);
+ m_world_f_core->inline_vimult3( &cross_point_dir, &cross_point_dir);
+
+ ConvertAngularImpulseToHL( cross_point_dir, *centerTorque );
+ ConvertForceImpulseToHL( force, *centerForce );
+}
+
+void CPhysicsObject::CalculateVelocityOffset( const Vector &forceVector, const Vector &worldPosition, Vector *centerVelocity, AngularImpulse *centerAngularVelocity ) const
+{
+ IVP_U_Point pos;
+ IVP_U_Float_Point force;
+
+ ConvertForceImpulseToIVP( forceVector, force );
+ ConvertPositionToIVP( worldPosition, pos );
+
+ IVP_Core *core = m_pObject->get_core();
+
+ const IVP_U_Matrix *m_world_f_core = core->get_m_world_f_core_PSI();
+
+ IVP_U_Float_Point point_d_ws;
+ point_d_ws.subtract(&pos, m_world_f_core->get_position());
+
+ IVP_U_Float_Point cross_point_dir;
+
+ cross_point_dir.calc_cross_product( &point_d_ws, &force);
+ m_world_f_core->inline_vimult3( &cross_point_dir, &cross_point_dir);
+
+ cross_point_dir.set_pairwise_mult( &cross_point_dir, core->get_inv_rot_inertia());
+ ConvertAngularImpulseToHL( cross_point_dir, *centerAngularVelocity );
+ force.set_multiple( &force, core->get_inv_mass() );
+ ConvertForceImpulseToHL( force, *centerVelocity );
+}
+
+void CPhysicsObject::ApplyTorqueCenter( const AngularImpulse &torqueImpulse )
+{
+ if ( !IsMoveable() )
+ return;
+ IVP_U_Float_Point ivpTorque;
+ ConvertAngularImpulseToIVP( torqueImpulse, ivpTorque );
+ IVP_Core *core = m_pObject->get_core();
+ core->async_rot_push_core_multiple_ws( &ivpTorque, 1.0 );
+ Wake();
+ ClampVelocity();
+}
+
+void CPhysicsObject::GetPosition( Vector *worldPosition, QAngle *angles ) const
+{
+ IVP_U_Matrix matrix;
+ m_pObject->get_m_world_f_object_AT( &matrix );
+ if ( worldPosition )
+ {
+ ConvertPositionToHL( matrix.vv, *worldPosition );
+ }
+
+ if ( angles )
+ {
+ ConvertRotationToHL( matrix, *angles );
+ }
+}
+
+
+void CPhysicsObject::GetPositionMatrix( matrix3x4_t *positionMatrix ) const
+{
+ IVP_U_Matrix matrix;
+ m_pObject->get_m_world_f_object_AT( &matrix );
+ ConvertMatrixToHL( matrix, *positionMatrix );
+}
+
+
+void CPhysicsObject::GetImplicitVelocity( Vector *velocity, AngularImpulse *angularVelocity ) const
+{
+ if ( !velocity && !angularVelocity )
+ return;
+
+ IVP_Core *core = m_pObject->get_core();
+ if ( velocity )
+ {
+ // just convert the cached dx
+ ConvertPositionToHL( core->delta_world_f_core_psis, *velocity );
+ }
+
+ if ( angularVelocity )
+ {
+ // compute the relative transform that was actually integrated in the last psi
+ IVP_U_Quat q_core_f_core;
+ q_core_f_core.set_invert_mult( &core->q_world_f_core_last_psi, &core->q_world_f_core_next_psi);
+
+ // now convert that to an axis/angle pair
+ Quaternion q( q_core_f_core.x, q_core_f_core.y, q_core_f_core.z, q_core_f_core.w );
+ AngularImpulse axis;
+ float angle;
+ QuaternionAxisAngle( q, axis, angle );
+
+ // scale it by the timestep to get a velocity
+ angle *= core->i_delta_time;
+
+ // ConvertDirectionToHL() - convert this ipion direction (in HL type) to HL coords
+ float tmpY = axis.z;
+ angularVelocity->z = -axis.y;
+ angularVelocity->y = tmpY;
+ angularVelocity->x = axis.x;
+
+ // now scale the axis by the angle to return the data in the correct format
+ (*angularVelocity) *= angle;
+ }
+}
+
+void CPhysicsObject::GetVelocity( Vector *velocity, AngularImpulse *angularVelocity ) const
+{
+ if ( !velocity && !angularVelocity )
+ return;
+
+ IVP_Core *core = m_pObject->get_core();
+ if ( velocity )
+ {
+ IVP_U_Float_Point speed;
+ speed.add( &core->speed, &core->speed_change );
+ ConvertPositionToHL( speed, *velocity );
+ }
+
+ if ( angularVelocity )
+ {
+ IVP_U_Float_Point rotSpeed;
+ rotSpeed.add( &core->rot_speed, &core->rot_speed_change );
+ // xform to HL space
+ ConvertAngularImpulseToHL( rotSpeed, *angularVelocity );
+ }
+}
+
+void CPhysicsObject::GetVelocityAtPoint( const Vector &worldPosition, Vector *pVelocity ) const
+{
+ IVP_Core *core = m_pObject->get_core();
+ IVP_U_Point pos;
+ ConvertPositionToIVP( worldPosition, pos );
+
+ IVP_U_Float_Point rotSpeed;
+ rotSpeed.add( &core->rot_speed, &core->rot_speed_change );
+
+ IVP_U_Float_Point av_ws;
+ core->get_m_world_f_core_PSI()->vmult3( &rotSpeed, &av_ws);
+
+ IVP_U_Float_Point pos_rel;
+ pos_rel.subtract( &pos, core->get_position_PSI());
+ IVP_U_Float_Point cross;
+ cross.inline_calc_cross_product(&av_ws,&pos_rel);
+
+ IVP_U_Float_Point speed;
+ speed.add(&core->speed, &cross);
+ speed.add(&core->speed_change);
+
+ ConvertPositionToHL( speed, *pVelocity );
+}
+
+
+// UNDONE: Limit these?
+void CPhysicsObject::AddVelocity( const Vector *velocity, const AngularImpulse *angularVelocity )
+{
+ Assert(IsMoveable());
+ if ( !IsMoveable() )
+ return;
+ IVP_Core *core = m_pObject->get_core();
+
+ Wake();
+
+ if ( velocity )
+ {
+ IVP_U_Float_Point ivpVelocity;
+ ConvertPositionToIVP( *velocity, ivpVelocity );
+ core->speed_change.add( &ivpVelocity );
+ }
+
+ if ( angularVelocity )
+ {
+ IVP_U_Float_Point ivpAngularVelocity;
+ ConvertAngularImpulseToIVP( *angularVelocity, ivpAngularVelocity );
+
+ core->rot_speed_change.add(&ivpAngularVelocity);
+ }
+ ClampVelocity();
+}
+
+void CPhysicsObject::SetPosition( const Vector &worldPosition, const QAngle &angles, bool isTeleport )
+{
+ IVP_U_Quat rot;
+ IVP_U_Point pos;
+
+ if ( m_pShadow )
+ {
+ UpdateShadow( worldPosition, angles, false, 0 );
+ }
+ ConvertPositionToIVP( worldPosition, pos );
+
+ ConvertRotationToIVP( angles, rot );
+
+ if ( m_pObject->is_collision_detection_enabled() && isTeleport )
+ {
+ EnableCollisions( false );
+ m_pObject->beam_object_to_new_position( &rot, &pos, IVP_FALSE );
+ EnableCollisions( true );
+ }
+ else
+ {
+ m_pObject->beam_object_to_new_position( &rot, &pos, IVP_FALSE );
+ }
+}
+
+void CPhysicsObject::SetPositionMatrix( const matrix3x4_t& matrix, bool isTeleport )
+{
+ if ( m_pShadow )
+ {
+ Vector worldPosition;
+ QAngle angles;
+ MatrixAngles( matrix, angles );
+ MatrixGetColumn( matrix, 3, worldPosition );
+ UpdateShadow( worldPosition, angles, false, 0 );
+ }
+
+ IVP_U_Quat rot;
+ IVP_U_Matrix mat;
+
+ ConvertMatrixToIVP( matrix, mat );
+
+ rot.set_quaternion( &mat );
+
+ if ( m_pObject->is_collision_detection_enabled() && isTeleport )
+ {
+ EnableCollisions( false );
+ m_pObject->beam_object_to_new_position( &rot, &mat.vv, IVP_FALSE );
+ EnableCollisions( true );
+ }
+ else
+ {
+ m_pObject->beam_object_to_new_position( &rot, &mat.vv, IVP_FALSE );
+ }
+}
+
+void CPhysicsObject::SetVelocityInstantaneous( const Vector *velocity, const AngularImpulse *angularVelocity )
+{
+ Assert(IsMoveable());
+ if ( !IsMoveable() )
+ return;
+ IVP_Core *core = m_pObject->get_core();
+
+ Wake();
+
+ if ( velocity )
+ {
+ ConvertPositionToIVP( *velocity, core->speed );
+ core->speed_change.set_to_zero();
+ }
+
+ if ( angularVelocity )
+ {
+ ConvertAngularImpulseToIVP( *angularVelocity, core->rot_speed );
+ core->rot_speed_change.set_to_zero();
+ }
+ ClampVelocity();
+}
+
+void CPhysicsObject::SetVelocity( const Vector *velocity, const AngularImpulse *angularVelocity )
+{
+ if ( !IsMoveable() )
+ return;
+ IVP_Core *core = m_pObject->get_core();
+
+ Wake();
+
+ if ( velocity )
+ {
+ ConvertPositionToIVP( *velocity, core->speed_change );
+ core->speed.set_to_zero();
+ }
+
+ if ( angularVelocity )
+ {
+ ConvertAngularImpulseToIVP( *angularVelocity, core->rot_speed_change );
+ core->rot_speed.set_to_zero();
+ }
+ ClampVelocity();
+}
+
+
+void CPhysicsObject::ClampVelocity()
+{
+ if ( m_pShadow )
+ return;
+
+ m_pObject->get_core()->apply_velocity_limit();
+}
+
+void GetWorldCoordFromSynapse( IVP_Synapse_Friction *pfriction, IVP_U_Point &world )
+{
+ world.set(pfriction->get_contact_point()->get_contact_point_ws());
+}
+
+
+bool CPhysicsObject::GetContactPoint( Vector *contactPoint, IPhysicsObject **contactObject ) const
+{
+ IVP_Synapse_Friction *pfriction = m_pObject->get_first_friction_synapse();
+ if ( !pfriction )
+ return false;
+
+ if ( contactPoint )
+ {
+ IVP_U_Point world;
+ GetWorldCoordFromSynapse( pfriction, world );
+ ConvertPositionToHL( world, *contactPoint );
+ }
+ if ( contactObject )
+ {
+ IVP_Real_Object *pivp = GetOppositeSynapseObject( pfriction );
+ *contactObject = static_cast<IPhysicsObject *>(pivp->client_data);
+ }
+ return true;
+}
+
+void CPhysicsObject::SetShadow( float maxSpeed, float maxAngularSpeed, bool allowPhysicsMovement, bool allowPhysicsRotation )
+{
+ if ( m_pShadow )
+ {
+ m_pShadow->MaxSpeed( maxSpeed, maxAngularSpeed );
+ }
+ else
+ {
+ m_shadowTempGravityDisable = false;
+
+ CPhysicsEnvironment *pVEnv = GetVPhysicsEnvironment();
+ m_pShadow = pVEnv->CreateShadowController( this, allowPhysicsMovement, allowPhysicsRotation );
+ m_pShadow->MaxSpeed( maxSpeed, maxAngularSpeed );
+ // This really should be in the game code, but do this here because the game may (does) use
+ // shadow/AI control as a collision filter indicator.
+ RecheckCollisionFilter();
+ }
+}
+
+void CPhysicsObject::UpdateShadow( const Vector &targetPosition, const QAngle &targetAngles, bool tempDisableGravity, float timeOffset )
+{
+ if ( tempDisableGravity != m_shadowTempGravityDisable )
+ {
+ m_shadowTempGravityDisable = tempDisableGravity;
+ if ( !m_pShadow || m_pShadow->AllowsTranslation() )
+ {
+ EnableGravity( !m_shadowTempGravityDisable );
+ }
+ }
+ if ( m_pShadow )
+ {
+ m_pShadow->Update( targetPosition, targetAngles, timeOffset );
+ }
+}
+
+
+void CPhysicsObject::RemoveShadowController()
+{
+ if ( m_pShadow )
+ {
+ CPhysicsEnvironment *pVEnv = GetVPhysicsEnvironment();
+ pVEnv->DestroyShadowController( m_pShadow );
+ m_pShadow = NULL;
+ }
+}
+
+// Back door to allow save/restore of backlink between shadow controller and physics object
+void CPhysicsObject::RestoreShadowController( IPhysicsShadowController *pShadowController )
+{
+ Assert( !m_pShadow );
+ m_pShadow = pShadowController;
+}
+
+int CPhysicsObject::GetShadowPosition( Vector *position, QAngle *angles ) const
+{
+ IVP_U_Matrix matrix;
+
+ IVP_Environment *pEnv = m_pObject->get_environment();
+ double psi = pEnv->get_next_PSI_time().get_seconds();
+ m_pObject->calc_at_matrix( psi, &matrix );
+ if ( angles )
+ {
+ ConvertRotationToHL( matrix, *angles );
+ }
+ if ( position )
+ {
+ ConvertPositionToHL( matrix.vv, *position );
+ }
+
+ return 1;
+}
+
+
+IPhysicsShadowController *CPhysicsObject::GetShadowController( void ) const
+{
+ return m_pShadow;
+}
+
+const CPhysCollide *CPhysicsObject::GetCollide( void ) const
+{
+ return m_pCollide;
+}
+
+
+IVP_SurfaceManager *CPhysicsObject::GetSurfaceManager( void ) const
+{
+ if ( m_collideType != COLLIDE_BALL )
+ {
+ return m_pObject->get_surface_manager();
+ }
+ return NULL;
+}
+
+
+float CPhysicsObject::GetDragInDirection( const IVP_U_Float_Point &velocity ) const
+{
+ IVP_U_Float_Point local;
+
+ const IVP_U_Matrix *m_world_f_core = m_pObject->get_core()->get_m_world_f_core_PSI();
+ m_world_f_core->vimult3( &velocity, &local );
+
+ return m_dragCoefficient * IVP_Inline_Math::fabsd( local.k[0] * m_dragBasis.x ) +
+ IVP_Inline_Math::fabsd( local.k[1] * m_dragBasis.y ) +
+ IVP_Inline_Math::fabsd( local.k[2] * m_dragBasis.z );
+}
+
+float CPhysicsObject::GetAngularDragInDirection( const IVP_U_Float_Point &angVelocity ) const
+{
+ return m_angDragCoefficient * IVP_Inline_Math::fabsd( angVelocity.k[0] * m_angDragBasis.x ) +
+ IVP_Inline_Math::fabsd( angVelocity.k[1] * m_angDragBasis.y ) +
+ IVP_Inline_Math::fabsd( angVelocity.k[2] * m_angDragBasis.z );
+}
+
+const char *CPhysicsObject::GetName() const
+{
+ return m_pObject->get_name();
+}
+
+void CPhysicsObject::SetMaterialIndex( int materialIndex )
+{
+ if ( m_materialIndex == materialIndex )
+ return;
+
+ m_materialIndex = materialIndex;
+ IVP_Material *pMaterial = physprops->GetIVPMaterial( materialIndex );
+ Assert(pMaterial);
+ m_pObject->l_default_material = pMaterial;
+ m_callbacks |= CALLBACK_ENABLING_COLLISION;
+ BEGIN_IVP_ALLOCATION();
+ m_pObject->recompile_material_changed();
+ END_IVP_ALLOCATION();
+ m_callbacks &= ~CALLBACK_ENABLING_COLLISION;
+ if ( GetShadowController() )
+ {
+ GetShadowController()->ObjectMaterialChanged( materialIndex );
+ }
+}
+
+// convert square velocity magnitude from IVP to HL
+float CPhysicsObject::GetEnergy() const
+{
+ IVP_Core *pCore = m_pObject->get_core();
+ IVP_FLOAT energy = 0.0f;
+ IVP_U_Float_Point tmp;
+
+ energy = 0.5f * pCore->get_mass() * pCore->speed.dot_product(&pCore->speed); // 1/2mvv
+ tmp.set_pairwise_mult(&pCore->rot_speed, pCore->get_rot_inertia()); // wI
+ energy += 0.5f * tmp.dot_product(&pCore->rot_speed); // 1/2mvv + 1/2wIw
+
+ return ConvertEnergyToHL( energy );
+}
+
+float CPhysicsObject::ComputeShadowControl( const hlshadowcontrol_params_t &params, float secondsToArrival, float dt )
+{
+ return ComputeShadowControllerHL( this, params, secondsToArrival, dt );
+}
+
+float CPhysicsObject::GetSphereRadius() const
+{
+ if ( m_collideType != COLLIDE_BALL )
+ return 0;
+
+ return ConvertDistanceToHL( m_pObject->to_ball()->get_radius() );
+}
+
+float CPhysicsObject::CalculateLinearDrag( const Vector &unitDirection ) const
+{
+ IVP_U_Float_Point ivpDir;
+ ConvertDirectionToIVP( unitDirection, ivpDir );
+
+ return GetDragInDirection( ivpDir );
+}
+
+float CPhysicsObject::CalculateAngularDrag( const Vector &objectSpaceRotationAxis ) const
+{
+ IVP_U_Float_Point ivpAxis;
+ ConvertDirectionToIVP( objectSpaceRotationAxis, ivpAxis );
+
+ // drag factor is per-radian, convert to per-degree
+ return GetAngularDragInDirection( ivpAxis ) * DEG2RAD(1.0);
+}
+
+
+void CPhysicsObject::BecomeTrigger()
+{
+ if ( IsTrigger() )
+ return;
+
+ if ( GetShadowController() )
+ {
+ // triggers won't have the standard collisions, so the material change is no longer necessary
+ // also: This will fix problems with surfaceprops if the trigger becomes a fluid.
+ GetShadowController()->UseShadowMaterial( false );
+ }
+ EnableDrag( false );
+ EnableGravity( false );
+
+ // UNDONE: Use defaults here? Do we want object sets by default?
+ IVP_Template_Phantom trigger;
+ trigger.manage_intruding_cores = IVP_TRUE; // manage a list of intruded objects
+ trigger.manage_sleeping_cores = IVP_TRUE; // don't untouch/touch on sleep/wake
+ trigger.dont_check_for_unmoveables = IVP_TRUE;
+ trigger.exit_policy_extra_radius = 0.1f; // relatively strict exit check [m]
+
+ bool enableCollisions = IsCollisionEnabled();
+ EnableCollisions( false );
+ BEGIN_IVP_ALLOCATION();
+ m_pObject->convert_to_phantom( &trigger );
+ END_IVP_ALLOCATION();
+ // hook up events
+ CPhysicsEnvironment *pVEnv = GetVPhysicsEnvironment();
+ pVEnv->PhantomAdd( this );
+
+
+ EnableCollisions( enableCollisions );
+}
+
+
+void CPhysicsObject::RemoveTrigger()
+{
+ IVP_Controller_Phantom *pController = m_pObject->get_controller_phantom();
+
+ // NOTE: This will remove the back-link in the object
+ delete pController;
+}
+
+
+bool CPhysicsObject::IsTrigger() const
+{
+ return m_pObject->get_controller_phantom() != NULL ? true : false;
+}
+
+bool CPhysicsObject::IsFluid() const
+{
+ IVP_Controller_Phantom *pController = m_pObject->get_controller_phantom();
+ if ( pController )
+ {
+ // UNDONE: Make a base class for triggers? IPhysicsTrigger?
+ // and derive fluids and any other triggers from that class
+ // then you can ask that class what to do here.
+ if ( pController->client_data )
+ return true;
+ }
+
+ return false;
+}
+
+// sets the object to be hinged. Fixed it place, but able to rotate around one axis.
+void CPhysicsObject::BecomeHinged( int localAxis )
+{
+ if ( IsMoveable() )
+ {
+ float savedMass = GetMass();
+
+ IVP_U_Float_Hesse *iri = (IVP_U_Float_Hesse *)m_pObject->get_core()->get_inv_rot_inertia();
+
+ float savedRI[3];
+ for ( int i = 0; i < 3; i++ )
+ savedRI[i] = iri->k[i];
+
+ SetMass( VPHYSICS_MAX_MASS );
+ IVP_U_Float_Hesse tmp = *iri;
+#if 0
+ for ( i = 0; i < 3; i++ )
+ tmp.k[i] = savedRI[i];
+#else
+ int localAxisIVP = ConvertCoordinateAxisToIVP(localAxis);
+ tmp.k[localAxisIVP] = savedRI[localAxisIVP];
+#endif
+
+ SetMass( savedMass );
+ *iri = tmp;
+ }
+ m_hingedAxis = localAxis+1;
+}
+
+void CPhysicsObject::RemoveHinged()
+{
+ m_hingedAxis = 0;
+ m_pObject->get_core()->calc_calc();
+}
+
+// dumps info about the object to Msg()
+void CPhysicsObject::OutputDebugInfo() const
+{
+ Msg("-----------------\nObject: %s\n", m_pObject->get_name());
+ Msg("Mass: %.1f (inv %.3f)\n", GetMass(), GetInvMass() );
+ Vector inertia = GetInertia();
+ Vector invInertia = GetInvInertia();
+ Msg("Inertia: %.2f, %.2f, %.2f (inv %.3f, %.3f, %.3f)\n", inertia.x, inertia.y, inertia.z, invInertia.x, invInertia.y, invInertia.z );
+
+ Vector speed, angSpeed;
+ GetVelocity( &speed, &angSpeed );
+ Msg("Velocity: %.2f, %.2f, %.2f \n", speed.x, speed.y, speed.z );
+ Msg("Ang Velocity: %.2f, %.2f, %.2f \n", angSpeed.x, angSpeed.y, angSpeed.z );
+
+ float damp, angDamp;
+ GetDamping( &damp, &angDamp );
+ Msg("Damping %.2f linear, %.2f angular\n", damp, angDamp );
+
+ Msg("Linear Drag: %.2f, %.2f, %.2f (factor %.2f)\n", m_dragBasis.x, m_dragBasis.y, m_dragBasis.z, m_dragCoefficient );
+ Msg("Angular Drag: %.2f, %.2f, %.2f (factor %.2f)\n", m_angDragBasis.x, m_angDragBasis.y, m_angDragBasis.z, m_angDragCoefficient );
+
+ if ( IsHinged() )
+ {
+ const char *pAxisNames[] = {"x", "y", "z"};
+ Msg("Hinged on %s axis\n", pAxisNames[m_hingedAxis-1] );
+ }
+ Msg("attached to %d controllers\n", m_pObject->get_core()->controllers_of_core.len() );
+ for (int k = m_pObject->get_core()->controllers_of_core.len()-1; k>=0;k--)
+ {
+ // NOTE: Set a breakpoint here and take a look at what it's hooked to
+ IVP_Controller *pController = m_pObject->get_core()->controllers_of_core.element_at(k);
+ Msg("%d) %s\n", k, pController->get_controller_name() );
+ }
+ Msg("State: %s, Collision %s, Motion %s, %sFlags %04X (game %04x, index %d)\n",
+ IsAsleep() ? "Asleep" : "Awake",
+ IsCollisionEnabled() ? "Enabled" : "Disabled",
+ IsStatic() ? "Static" : (IsMotionEnabled() ? "Enabled" : "Disabled"),
+ (GetCallbackFlags() & CALLBACK_MARKED_FOR_TEST) ? "Debug! " : "",
+ (int)GetCallbackFlags(), (int)GetGameFlags(), (int)GetGameIndex() );
+
+ float matDensity = 0;
+ float matThickness = 0;
+ float matFriction = 0;
+ float matElasticity = 0;
+ physprops->GetPhysicsProperties( GetMaterialIndexInternal(), &matDensity, &matThickness, &matFriction, &matElasticity );
+ Msg("Material: %s : density(%.1f), thickness(%.2f), friction(%.2f), elasticity(%.2f)\n", physprops->GetPropName(GetMaterialIndexInternal()),
+ matDensity, matThickness, matFriction, matElasticity );
+ if ( GetCollide() )
+ {
+ OutputCollideDebugInfo( GetCollide() );
+ }
+}
+
+bool CPhysicsObject::IsAttachedToConstraint( bool bExternalOnly ) const
+{
+ if ( m_pObject )
+ {
+ for (int k = m_pObject->get_core()->controllers_of_core.len()-1; k>=0;k--)
+ {
+ IVP_Controller *pController = m_pObject->get_core()->controllers_of_core.element_at(k);
+ if ( pController->get_controller_priority() == IVP_CP_CONSTRAINTS )
+ {
+ if ( !bExternalOnly || IsExternalConstraint(pController, GetGameData()) )
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void InitObjectTemplate( IVP_Template_Real_Object &objectTemplate, int materialIndex, objectparams_t *pParams, bool isStatic )
+{
+ objectTemplate.mass = pParams->mass;
+ objectTemplate.mass = clamp( objectTemplate.mass, VPHYSICS_MIN_MASS, VPHYSICS_MAX_MASS );
+
+ if ( materialIndex >= 0 )
+ {
+ objectTemplate.material = physprops->GetIVPMaterial( materialIndex );
+ }
+ else
+ {
+ materialIndex = physprops->GetSurfaceIndex( "default" );
+ objectTemplate.material = physprops->GetIVPMaterial( materialIndex );
+ }
+
+ // HACKHACK: Do something with this name?
+ BEGIN_IVP_ALLOCATION();
+ if ( IsPC() )
+ {
+ objectTemplate.set_name(pParams->pName);
+ }
+ END_IVP_ALLOCATION();
+#if USE_COLLISION_GROUP_STRING
+ objectTemplate.set_nocoll_group_ident( NULL );
+#endif
+
+ objectTemplate.physical_unmoveable = isStatic ? IVP_TRUE : IVP_FALSE;
+ objectTemplate.rot_inertia_is_factor = IVP_TRUE;
+
+ float inertia = pParams->inertia;
+
+ // don't allow <=0 inertia!!!!
+ if ( inertia <= 0 )
+ inertia = 1.0;
+
+ if ( inertia > 1e18f )
+ inertia = 1e18f;
+
+ objectTemplate.rot_inertia.set(inertia, inertia, inertia);
+ objectTemplate.rot_speed_damp_factor.set(pParams->rotdamping, pParams->rotdamping, pParams->rotdamping);
+ objectTemplate.speed_damp_factor = pParams->damping;
+ objectTemplate.auto_check_rot_inertia = pParams->rotInertiaLimit;
+}
+
+CPhysicsObject *CreatePhysicsObject( CPhysicsEnvironment *pEnvironment, const CPhysCollide *pCollisionModel, int materialIndex, const Vector &position, const QAngle& angles, objectparams_t *pParams, bool isStatic )
+{
+ if ( materialIndex < 0 )
+ {
+ materialIndex = physprops->GetSurfaceIndex( "default" );
+ }
+ AssertOnce(materialIndex>=0 && materialIndex<127);
+ IVP_Template_Real_Object objectTemplate;
+ IVP_U_Quat rotation;
+ IVP_U_Point pos;
+
+ Assert( position.IsValid() );
+ Assert( angles.IsValid() );
+
+#if _WIN32
+ if ( !position.IsValid() || !angles.IsValid() )
+ {
+ DebuggerBreakIfDebugging();
+ Warning("Invalid initial position on %s\n", pParams->pName );
+
+ Vector *pPos = (Vector *)&position;
+ QAngle *pRot = (QAngle *)&angles;
+ if ( !pPos->IsValid() )
+ pPos->Init();
+ if ( !pRot->IsValid() )
+ pRot->Init();
+ }
+#endif
+
+ ConvertRotationToIVP( angles, rotation );
+ ConvertPositionToIVP( position, pos );
+
+ InitObjectTemplate( objectTemplate, materialIndex, pParams, isStatic );
+
+ IVP_U_Matrix massCenterMatrix;
+ massCenterMatrix.init();
+ if ( pParams->massCenterOverride )
+ {
+ IVP_U_Point center;
+ ConvertPositionToIVP( *pParams->massCenterOverride, center );
+ massCenterMatrix.shift_os( &center );
+ objectTemplate.mass_center_override = &massCenterMatrix;
+ }
+
+ CPhysicsObject *pObject = new CPhysicsObject();
+ short collideType;
+ IVP_SurfaceManager *pSurman = CreateSurfaceManager( pCollisionModel, collideType );
+ if ( !pSurman )
+ return NULL;
+ pObject->m_collideType = collideType;
+ pObject->m_asleepSinceCreation = true;
+
+ BEGIN_IVP_ALLOCATION();
+
+ IVP_Polygon *realObject = pEnvironment->GetIVPEnvironment()->create_polygon(pSurman, &objectTemplate, &rotation, &pos);
+
+ pObject->Init( pCollisionModel, realObject, materialIndex, pParams->volume, pParams->dragCoefficient, pParams->dragCoefficient );
+ pObject->SetGameData( pParams->pGameData );
+
+ if ( pParams->enableCollisions )
+ {
+ pObject->EnableCollisions( true );
+ }
+ if ( !isStatic && pParams->dragCoefficient != 0.0f )
+ {
+ pObject->EnableDrag( true );
+ }
+
+ END_IVP_ALLOCATION();
+
+ return pObject;
+}
+
+CPhysicsObject *CreatePhysicsSphere( CPhysicsEnvironment *pEnvironment, float radius, int materialIndex, const Vector &position, const QAngle &angles, objectparams_t *pParams, bool isStatic )
+{
+ IVP_U_Quat rotation;
+ IVP_U_Point pos;
+
+ ConvertRotationToIVP( angles, rotation );
+ ConvertPositionToIVP( position, pos );
+
+ IVP_Template_Real_Object objectTemplate;
+ InitObjectTemplate( objectTemplate, materialIndex, pParams, isStatic );
+
+ IVP_Template_Ball ballTemplate;
+ ballTemplate.radius = ConvertDistanceToIVP( radius );
+
+ MEM_ALLOC_CREDIT();
+ IVP_Ball *realObject = pEnvironment->GetIVPEnvironment()->create_ball( &ballTemplate, &objectTemplate, &rotation, &pos );
+
+ float volume = pParams->volume;
+ if ( volume <= 0 )
+ {
+ volume = 4.0f * radius * radius * radius * M_PI / 3.0f;
+ }
+ CPhysicsObject *pObject = new CPhysicsObject();
+ pObject->Init( NULL, realObject, materialIndex, volume, 0, 0 ); //, pParams->dragCoefficient, pParams->dragCoefficient
+ pObject->SetGameData( pParams->pGameData );
+
+ if ( pParams->enableCollisions )
+ {
+ pObject->EnableCollisions( true );
+ }
+ // drag is not supported on spheres
+ //pObject->EnableDrag( false );
+
+ return pObject;
+}
+
+class CMaterialIndexOps : public CDefSaveRestoreOps
+{
+public:
+ // save data type interface
+ virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave )
+ {
+ int materialIndex = *((int *)fieldInfo.pField);
+ const char *pMaterialName = physprops->GetPropName( materialIndex );
+ if ( !pMaterialName )
+ {
+ pMaterialName = physprops->GetPropName( 0 );
+ }
+ int len = strlen(pMaterialName) + 1;
+ pSave->WriteInt( &len );
+ pSave->WriteString( pMaterialName );
+ }
+
+ virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore )
+ {
+ char nameBuf[1024];
+ int nameLen = pRestore->ReadInt();
+ pRestore->ReadString( nameBuf, sizeof(nameBuf), nameLen );
+ int *pMaterialIndex = (int *)fieldInfo.pField;
+ *pMaterialIndex = physprops->GetSurfaceIndex( nameBuf );
+ if ( *pMaterialIndex < 0 )
+ {
+ *pMaterialIndex = 0;
+ }
+ }
+
+ virtual bool IsEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
+ {
+ int *pMaterialIndex = (int *)fieldInfo.pField;
+ return (*pMaterialIndex == 0);
+ }
+
+ virtual void MakeEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
+ {
+ int *pMaterialIndex = (int *)fieldInfo.pField;
+ *pMaterialIndex = 0;
+ }
+};
+
+static CMaterialIndexOps g_MaterialIndexDataOps;
+
+ISaveRestoreOps* MaterialIndexDataOps()
+{
+ return &g_MaterialIndexDataOps;
+}
+
+BEGIN_SIMPLE_DATADESC( vphysics_save_cphysicsobject_t )
+// DEFINE_FIELD( pCollide, FIELD_??? ), // don't save this
+// DEFINE_FIELD( pName, FIELD_??? ), // don't save this
+DEFINE_FIELD( sphereRadius, FIELD_FLOAT ),
+DEFINE_FIELD( isStatic, FIELD_BOOLEAN ),
+DEFINE_FIELD( collisionEnabled, FIELD_BOOLEAN ),
+DEFINE_FIELD( gravityEnabled, FIELD_BOOLEAN ),
+DEFINE_FIELD( dragEnabled, FIELD_BOOLEAN ),
+DEFINE_FIELD( motionEnabled, FIELD_BOOLEAN ),
+DEFINE_FIELD( isAsleep, FIELD_BOOLEAN ),
+DEFINE_FIELD( isTrigger, FIELD_BOOLEAN ),
+DEFINE_FIELD( asleepSinceCreation, FIELD_BOOLEAN ),
+DEFINE_FIELD( hasTouchedDynamic, FIELD_BOOLEAN ),
+DEFINE_CUSTOM_FIELD( materialIndex, &g_MaterialIndexDataOps ),
+DEFINE_FIELD( mass, FIELD_FLOAT ),
+DEFINE_FIELD( rotInertia, FIELD_VECTOR ),
+DEFINE_FIELD( speedDamping, FIELD_FLOAT ),
+DEFINE_FIELD( rotSpeedDamping, FIELD_FLOAT ),
+DEFINE_FIELD( massCenterOverride, FIELD_VECTOR ),
+DEFINE_FIELD( callbacks, FIELD_INTEGER ),
+DEFINE_FIELD( gameFlags, FIELD_INTEGER ),
+DEFINE_FIELD( contentsMask, FIELD_INTEGER ),
+DEFINE_FIELD( volume, FIELD_FLOAT ),
+DEFINE_FIELD( dragCoefficient, FIELD_FLOAT ),
+DEFINE_FIELD( angDragCoefficient, FIELD_FLOAT ),
+DEFINE_FIELD( hasShadowController,FIELD_BOOLEAN ),
+//DEFINE_VPHYSPTR( pShadow ),
+DEFINE_FIELD( origin, FIELD_POSITION_VECTOR ),
+DEFINE_FIELD( angles, FIELD_VECTOR ),
+DEFINE_FIELD( velocity, FIELD_VECTOR ),
+DEFINE_FIELD( angVelocity, FIELD_VECTOR ),
+DEFINE_FIELD( collideType, FIELD_SHORT ),
+DEFINE_FIELD( gameIndex, FIELD_SHORT ),
+DEFINE_FIELD( hingeAxis, FIELD_INTEGER ),
+END_DATADESC()
+
+bool CPhysicsObject::IsCollisionEnabled() const
+{
+ return GetObject()->is_collision_detection_enabled() ? true : false;
+}
+
+void CPhysicsObject::WriteToTemplate( vphysics_save_cphysicsobject_t &objectTemplate )
+{
+ if ( m_collideType == COLLIDE_BALL )
+ {
+ objectTemplate.pCollide = NULL;
+ objectTemplate.sphereRadius = GetSphereRadius();
+ }
+ else
+ {
+ objectTemplate.pCollide = GetCollide();
+ objectTemplate.sphereRadius = 0;
+ }
+ objectTemplate.isStatic = IsStatic();
+ objectTemplate.collisionEnabled = IsCollisionEnabled();
+ objectTemplate.gravityEnabled = IsGravityEnabled();
+ objectTemplate.dragEnabled = IsDragEnabled();
+ objectTemplate.motionEnabled = IsMotionEnabled();
+ objectTemplate.isAsleep = IsAsleep();
+ objectTemplate.isTrigger = IsTrigger();
+ objectTemplate.asleepSinceCreation = m_asleepSinceCreation;
+ objectTemplate.materialIndex = m_materialIndex;
+ objectTemplate.mass = GetMass();
+
+ objectTemplate.rotInertia = GetInertia();
+ GetDamping( &objectTemplate.speedDamping, &objectTemplate.rotSpeedDamping );
+ objectTemplate.massCenterOverride.Init();
+ if ( !IsMassCenterAtDefault() )
+ {
+ objectTemplate.massCenterOverride = GetMassCenterLocalSpace();
+ }
+
+ objectTemplate.callbacks = m_callbacks;
+ objectTemplate.gameFlags = m_gameFlags;
+ objectTemplate.volume = GetVolume();
+ objectTemplate.dragCoefficient = m_dragCoefficient;
+ objectTemplate.angDragCoefficient = m_angDragCoefficient;
+ objectTemplate.pShadow = m_pShadow;
+ objectTemplate.hasShadowController = (m_pShadow != NULL) ? true : false;
+ objectTemplate.hasTouchedDynamic = HasTouchedDynamic();
+ //bool m_shadowTempGravityDisable;
+ objectTemplate.collideType = m_collideType;
+ objectTemplate.gameIndex = m_gameIndex;
+ objectTemplate.contentsMask = m_contentsMask;
+ objectTemplate.hingeAxis = m_hingedAxis;
+ GetPosition( &objectTemplate.origin, &objectTemplate.angles );
+ GetVelocity( &objectTemplate.velocity, &objectTemplate.angVelocity );
+}
+
+void CPhysicsObject::InitFromTemplate( CPhysicsEnvironment *pEnvironment, void *pGameData, const vphysics_save_cphysicsobject_t &objectTemplate )
+{
+ MEM_ALLOC_CREDIT();
+ m_collideType = objectTemplate.collideType;
+
+ IVP_Template_Real_Object ivpObjectTemplate;
+ IVP_U_Quat rotation;
+ IVP_U_Point pos;
+
+ ConvertRotationToIVP( objectTemplate.angles, rotation );
+ ConvertPositionToIVP( objectTemplate.origin, pos );
+
+ ivpObjectTemplate.mass = objectTemplate.mass;
+
+ if ( objectTemplate.materialIndex >= 0 )
+ {
+ ivpObjectTemplate.material = physprops->GetIVPMaterial( objectTemplate.materialIndex );
+ }
+ else
+ {
+ ivpObjectTemplate.material = physprops->GetIVPMaterial( physprops->GetSurfaceIndex( "default" ) );
+ }
+
+ Assert( ivpObjectTemplate.material );
+ // HACKHACK: Pass this name in for debug
+ ivpObjectTemplate.set_name(objectTemplate.pName);
+#if USE_COLLISION_GROUP_STRING
+ ivpObjectTemplate.set_nocoll_group_ident( NULL );
+#endif
+
+ ivpObjectTemplate.physical_unmoveable = objectTemplate.isStatic ? IVP_TRUE : IVP_FALSE;
+ ivpObjectTemplate.rot_inertia_is_factor = IVP_TRUE;
+
+ ivpObjectTemplate.rot_inertia.set( 1,1,1 );
+ ivpObjectTemplate.rot_speed_damp_factor.set( objectTemplate.rotSpeedDamping, objectTemplate.rotSpeedDamping, objectTemplate.rotSpeedDamping );
+ ivpObjectTemplate.speed_damp_factor = objectTemplate.speedDamping;
+
+ IVP_U_Matrix massCenterMatrix;
+ massCenterMatrix.init();
+ if ( objectTemplate.massCenterOverride != vec3_origin )
+ {
+ IVP_U_Point center;
+ ConvertPositionToIVP( objectTemplate.massCenterOverride, center );
+ massCenterMatrix.shift_os( &center );
+ ivpObjectTemplate.mass_center_override = &massCenterMatrix;
+ }
+
+ IVP_Real_Object *realObject = NULL;
+ if ( m_collideType == COLLIDE_BALL )
+ {
+ IVP_Template_Ball ballTemplate;
+ ballTemplate.radius = ConvertDistanceToIVP( objectTemplate.sphereRadius );
+
+ realObject = pEnvironment->GetIVPEnvironment()->create_ball( &ballTemplate, &ivpObjectTemplate, &rotation, &pos );
+ }
+ else
+ {
+ short collideType;
+ IVP_SurfaceManager *surman = CreateSurfaceManager( objectTemplate.pCollide, collideType );
+ m_collideType = collideType;
+ realObject = pEnvironment->GetIVPEnvironment()->create_polygon(surman, &ivpObjectTemplate, &rotation, &pos);
+ }
+
+ m_pObject = realObject;
+ SetInertia( objectTemplate.rotInertia );
+ Init( objectTemplate.pCollide, realObject, objectTemplate.materialIndex, objectTemplate.volume, objectTemplate.dragCoefficient, objectTemplate.dragCoefficient );
+
+ SetCallbackFlags( (unsigned short) objectTemplate.callbacks );
+ SetGameFlags( (unsigned short) objectTemplate.gameFlags );
+ SetGameIndex( objectTemplate.gameIndex );
+ SetGameData( pGameData );
+ SetContents( objectTemplate.contentsMask );
+
+ if ( objectTemplate.dragEnabled )
+ {
+ Assert( !objectTemplate.isStatic );
+ EnableDrag( true );
+ }
+
+ if ( !objectTemplate.motionEnabled )
+ {
+ Assert( !objectTemplate.isStatic );
+ EnableMotion( false );
+ }
+
+ if ( objectTemplate.isTrigger )
+ {
+ BecomeTrigger();
+ }
+
+ if ( !objectTemplate.gravityEnabled )
+ {
+ EnableGravity( false );
+ }
+
+ if ( objectTemplate.collisionEnabled )
+ {
+ EnableCollisions( true );
+ }
+
+ // will wake up the object
+ if ( objectTemplate.velocity.LengthSqr() != 0 || objectTemplate.angVelocity.LengthSqr() != 0 )
+ {
+ SetVelocityInstantaneous( &objectTemplate.velocity, &objectTemplate.angVelocity );
+ if ( objectTemplate.isAsleep )
+ {
+ Sleep();
+ }
+ }
+
+ m_asleepSinceCreation = objectTemplate.asleepSinceCreation;
+ if ( !objectTemplate.isAsleep )
+ {
+ Assert( !objectTemplate.isStatic );
+ Wake();
+ }
+
+ if ( objectTemplate.hingeAxis )
+ {
+ BecomeHinged( objectTemplate.hingeAxis-1 );
+ }
+ if ( objectTemplate.hasTouchedDynamic )
+ {
+ SetTouchedDynamic();
+ }
+
+ m_pShadow = NULL;
+}
+
+
+bool SavePhysicsObject( const physsaveparams_t &params, CPhysicsObject *pObject )
+{
+ vphysics_save_cphysicsobject_t objectTemplate;
+ memset( &objectTemplate, 0, sizeof(objectTemplate) );
+
+ pObject->WriteToTemplate( objectTemplate );
+ params.pSave->WriteAll( &objectTemplate );
+
+ if ( objectTemplate.hasShadowController )
+ {
+ return SavePhysicsShadowController( params, objectTemplate.pShadow );
+ }
+ return true;
+}
+
+bool RestorePhysicsObject( const physrestoreparams_t &params, CPhysicsObject **ppObject )
+{
+ vphysics_save_cphysicsobject_t objectTemplate;
+ memset( &objectTemplate, 0, sizeof(objectTemplate) );
+ params.pRestore->ReadAll( &objectTemplate );
+ Assert(objectTemplate.origin.IsValid());
+ Assert(objectTemplate.angles.IsValid());
+ objectTemplate.pCollide = params.pCollisionModel;
+ objectTemplate.pName = params.pName;
+ *ppObject = new CPhysicsObject();
+
+ postrestore_objectlist_t entry;
+ entry.Defaults();
+
+ if ( objectTemplate.collisionEnabled )
+ {
+ // queue up the collision enable for these in case their entities have other dependent
+ // physics handlers (like controllers) that need to be restored before callbacks are useful
+ entry.pObject = *ppObject;
+ entry.enableCollisions = true;
+ objectTemplate.collisionEnabled = false;
+ }
+
+ (*ppObject)->InitFromTemplate( static_cast<CPhysicsEnvironment *>(params.pEnvironment), params.pGameData, objectTemplate );
+
+ if ( (*ppObject)->IsAsleep() && !(*ppObject)->m_asleepSinceCreation && !(*ppObject)->IsStatic() )
+ {
+ entry.pObject = *ppObject;
+ entry.growFriction = true;
+ }
+
+ if ( entry.pObject )
+ {
+ g_PostRestoreObjectList.AddToTail( entry );
+ }
+
+ if ( objectTemplate.hasShadowController )
+ {
+ bool restored = RestorePhysicsShadowControllerInternal( params, &objectTemplate.pShadow, *ppObject );
+ (*ppObject)->RestoreShadowController( objectTemplate.pShadow );
+ return restored;
+ }
+
+ return true;
+}
+
+IPhysicsObject *CreateObjectFromBuffer( CPhysicsEnvironment *pEnvironment, void *pGameData, unsigned char *pBuffer, unsigned int bufferSize, bool enableCollisions )
+{
+ CPhysicsObject *pObject = new CPhysicsObject();
+ if ( bufferSize >= sizeof(vphysics_save_cphysicsobject_t))
+ {
+ vphysics_save_cphysicsobject_t *pTemplate = reinterpret_cast<vphysics_save_cphysicsobject_t *>(pBuffer);
+ pTemplate->hasShadowController = false; // this hasn't been saved separately so cannot be supported via this path
+ pObject->InitFromTemplate( pEnvironment, pGameData, *pTemplate );
+ if ( pTemplate->collisionEnabled && enableCollisions )
+ {
+ pObject->EnableCollisions(true);
+ }
+ return pObject;
+ }
+ return NULL;
+}
+
+IPhysicsObject *CreateObjectFromBuffer_UseExistingMemory( CPhysicsEnvironment *pEnvironment, void *pGameData, unsigned char *pBuffer, unsigned int bufferSize, CPhysicsObject *pExistingMemory )
+{
+ if ( bufferSize >= sizeof(vphysics_save_cphysicsobject_t))
+ {
+ vphysics_save_cphysicsobject_t *pTemplate = reinterpret_cast<vphysics_save_cphysicsobject_t *>(pBuffer);
+ // Allow the placement new. If we don't do this, then it'll get a compile error because new
+ // might be defined as the special form in MEMALL_DEBUG_NEW.
+ #include "tier0/memdbgoff.h"
+ pExistingMemory = new ( pExistingMemory ) CPhysicsObject();
+ #include "tier0/memdbgon.h"
+ pExistingMemory->InitFromTemplate( pEnvironment, pGameData, *pTemplate );
+ if ( pTemplate->collisionEnabled )
+ {
+ pExistingMemory->EnableCollisions(true);
+ }
+ return pExistingMemory;
+ }
+ return NULL;
+}
+
+// regenerate the friction systems for these objects. Because when it was saved it had them (came to rest with the contact points).
+// So now we need to recreate them or some objects may not wake up when this object (or its neighbors) are deleted.
+void PostRestorePhysicsObject()
+{
+ for ( int i = g_PostRestoreObjectList.Count()-1; i >= 0; --i )
+ {
+ if ( g_PostRestoreObjectList[i].pObject )
+ {
+ if ( g_PostRestoreObjectList[i].growFriction )
+ {
+ g_PostRestoreObjectList[i].pObject->GetObject()->force_grow_friction_system();
+ }
+ if ( g_PostRestoreObjectList[i].enableCollisions )
+ {
+ g_PostRestoreObjectList[i].pObject->EnableCollisions( true );
+ }
+ }
+ }
+ g_PostRestoreObjectList.Purge();
+}