diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /vphysics/physics_object.cpp | |
| download | archived-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.cpp | 2001 |
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 ¶ms, 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( ¢er ); + 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( ¢er ); + 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 ¶ms, 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 ¶ms, 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(); +} |