From a588bea31956850e5f9e957c0f5616269c5a7580 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Tue, 6 Aug 2024 13:30:40 +0100 Subject: [PATCH] player: Rewrite player controller to use Jolt's Character class --- vphysics_jolt/vjolt_controller_player.cpp | 409 ++++++++++++---------- vphysics_jolt/vjolt_controller_player.h | 43 ++- 2 files changed, 254 insertions(+), 198 deletions(-) diff --git a/vphysics_jolt/vjolt_controller_player.cpp b/vphysics_jolt/vjolt_controller_player.cpp index 1a11e1b1..44acdb1e 100644 --- a/vphysics_jolt/vjolt_controller_player.cpp +++ b/vphysics_jolt/vjolt_controller_player.cpp @@ -4,13 +4,44 @@ #include "vjolt_layers.h" #include "vjolt_controller_player.h" +#include "vjolt_debugrender.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +static ConVar vjolt_player_collision_tolerance( "vjolt_player_collision_tolerance", "0.05" ); +static ConVar vjolt_player_character_padding( "vjolt_player_character_padding", "0.02" ); + +static ConVar vjolt_player_debug( "vjolt_player_debug", "0" ); +static ConVar vjolt_player_disable_limit( "vjolt_player_disable_limit", "0.1", 0, "The min speed^2 before we just go where physics wants to take us" ); + //------------------------------------------------------------------------------------------------- -static ConVar vjolt_player_collision_tolerance( "vjolt_player_collision_tolerance", "0.05" ); +// Component-wise Vector clamp +static Vector ClampVector( const Vector &x, const Vector &min, const Vector &max ) +{ + return Vector( + Clamp( x.x, min.x, max.x ), + Clamp( x.y, min.y, max.y ), + Clamp( x.z, min.z, max.z ) ); +} + +static void ComputePlayerController( Vector &vCurrentSpeed, const Vector &vDelta, const Vector &vMaxSpeed, float flScaleDelta, float flDamping, Vector *pOutImpulse ) +{ + if ( vCurrentSpeed.LengthSqr() < 1e-6f ) + { + vCurrentSpeed = vec3_origin; + } + + Vector vDampAccel = vCurrentSpeed * -flDamping; + Vector vDeltaAccel = vDelta * flScaleDelta; + + Vector vAcceleration = ClampVector( vDeltaAccel + vDampAccel, -vMaxSpeed, vMaxSpeed ); + + vCurrentSpeed += vAcceleration; + if ( pOutImpulse ) + *pOutImpulse = vAcceleration; +} //------------------------------------------------------------------------------------------------- @@ -22,30 +53,35 @@ JoltPhysicsPlayerController::JoltPhysicsPlayerController( JoltPhysicsObject *pOb JoltPhysicsPlayerController::~JoltPhysicsPlayerController() { SetObjectInternal( nullptr ); - SetGround( nullptr ); } //------------------------------------------------------------------------------------------------- void JoltPhysicsPlayerController::Update( const Vector &position, const Vector &velocity, float secondsToArrival, bool onground, IPhysicsObject *ground ) { - // timeOffset == secondsToArrival + m_bUpdatedSinceLast = true; - const JPH::Vec3 targetPosition = SourceToJolt::Distance( position ); - - if ( targetPosition.IsClose( m_targetPosition ) ) + if ( ( velocity - m_vCurrentSpeed ).LengthSqr() < 1e-6f && ( position - m_vTargetPosition ).LengthSqr() < 1e-6f ) return; - const JPH::Vec3 targetVelocity = SourceToJolt::Distance( velocity ); + m_vTargetPosition = position; + m_flSecondsToArrival = secondsToArrival < 0 ? 0 : secondsToArrival; - m_targetPosition = targetPosition; - m_targetVelocity = targetVelocity; - m_secondsToArrival = secondsToArrival; + m_vCurrentSpeed = velocity; + m_pCharacter->Activate(); + m_bEnable = true; - // Bogus assertion: onground can be true and ground can be null when touching the world. That is okay - //VJoltAssert( ( onground && ground ) || ( !onground && !ground ) ); + if ( velocity.LengthSqr() <= vjolt_player_disable_limit.GetFloat() ) + { + // No input velocity, just go where physics takes you. + m_bEnable = false; + } + else + { + MaxSpeed( velocity ); + } - SetGround( static_cast( ground ) ); + // We ignore the given ground here, we use the Jolt player controller's ground. } void JoltPhysicsPlayerController::SetEventHandler( IPhysicsPlayerControllerEvent *handler ) @@ -61,9 +97,21 @@ bool JoltPhysicsPlayerController::IsInContact() void JoltPhysicsPlayerController::MaxSpeed( const Vector &velocity ) { - // Do we have to care about this? IVP used a rigid body for the player shadow because it didn't - // have the concept of kinematic objects, our Jolt shadow follows the player exactly and - // is kinematic so if the game follows this max speed limit we don't need to care. + Vector vCurrentVelocity; + m_pObject->GetVelocity( &vCurrentVelocity, nullptr ); + + Vector vDirection = velocity; + float flLength = VectorNormalize( vDirection ); // Normalizes in place. + + float flDot = DotProduct( vDirection, vCurrentVelocity ); + if ( flDot > 0 ) + { + m_vMaxSpeed = VectorAbs( velocity - ( vDirection * flDot * flLength ) ); + } + else + { + m_vMaxSpeed = VectorAbs( velocity ); + } } //------------------------------------------------------------------------------------------------- @@ -85,10 +133,12 @@ void JoltPhysicsPlayerController::StepUp( float height ) if ( height == 0.0f ) return; - // Since the player is a kinematic object that slides around the world using velocity, when - // stepping up onto a platform we need to go there instantly, AddToPosition does that. - - m_pObject->AddToPosition( JPH::Vec3( 0.0f, 0.0f, SourceToJolt::Distance( height ) ) ); + Vector vPos; + QAngle qAngles; + m_pObject->GetPosition( &vPos, &qAngles ); + vPos.z += height; + // Teleport, do not influence implicit velocity. + m_pObject->SetPosition( vPos, qAngles, true ); } void JoltPhysicsPlayerController::Jump() @@ -101,13 +151,10 @@ void JoltPhysicsPlayerController::GetShadowVelocity( Vector *velocity ) if ( !velocity ) return; - JPH::Vec3 jphVelocity = m_pObject->GetBody()->GetLinearVelocity(); - if ( m_pGround ) - { - jphVelocity -= m_pGround->GetBody()->GetPointVelocity( m_groundPos ); - } + m_pObject->GetVelocity( velocity, nullptr ); - *velocity = JoltToSource::Distance( jphVelocity ); + Vector vBaseVelocity = JoltToSource::Distance( m_pCharacter->GetGroundVelocity() ); + *velocity -= vBaseVelocity; } IPhysicsObject *JoltPhysicsPlayerController::GetObject() @@ -117,7 +164,8 @@ IPhysicsObject *JoltPhysicsPlayerController::GetObject() void JoltPhysicsPlayerController::GetLastImpulse( Vector *pOut ) { - VectorClear( *pOut ); + if ( pOut ) + *pOut = m_vLastImpulse; } //------------------------------------------------------------------------------------------------- @@ -155,44 +203,27 @@ bool JoltPhysicsPlayerController::WasFrozen() //------------------------------------------------------------------------------------------------- -static void CheckCollision( JoltPhysicsObject *pObject, JPH::CollideShapeCollector &ioCollector, JPH::BodyFilter &ioFilter ) +bool JoltPhysicsPlayerController::OnContactValidate( const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2 ) { - JPH::PhysicsSystem *pSystem = pObject->GetEnvironment()->GetPhysicsSystem(); - - // TODO(Josh): Make a PLAYER ONLY layer that will only collide with MOVING ONLY annd - // NOTHING ELSE tomorrow. - - // Create query broadphase layer filter - JPH::DefaultBroadPhaseLayerFilter broadphase_layer_filter = pSystem->GetDefaultBroadPhaseLayerFilter( Layers::MOVING ); - - // Create query object layer filter - JPH::DefaultObjectLayerFilter object_layer_filter = pSystem->GetDefaultLayerFilter( Layers::MOVING ); - - // Determine position to test - JPH::Vec3 position; - JPH::Quat rotation; - JPH::BodyInterface &bi = pSystem->GetBodyInterfaceNoLock(); - bi.GetPositionAndRotation( pObject->GetBodyID(), position, rotation ); - JPH::Mat44 query_transform = JPH::Mat44::sRotationTranslation( rotation, position + rotation * pObject->GetBody()->GetShape()->GetCenterOfMass() ); - - // Settings for collide shape - JPH::CollideShapeSettings settings; - settings.mActiveEdgeMode = JPH::EActiveEdgeMode::CollideOnlyWithActive; - settings.mActiveEdgeMovementDirection = bi.GetLinearVelocity( pObject->GetBodyID() ); - settings.mBackFaceMode = JPH::EBackFaceMode::IgnoreBackFaces; - settings.mMaxSeparationDistance = vjolt_player_collision_tolerance.GetFloat(); + JPH::Body *pOtherBody = m_pObject->GetEnvironment()->GetPhysicsSystem()->GetBodyLockInterfaceNoLock().TryGetBody( inBodyID2 ); + JoltPhysicsObject* pOtherObject = reinterpret_cast< JoltPhysicsObject* >( pOtherBody->GetUserData() ); + JoltPhysicsContactListener *pListener = m_pObject->GetEnvironment()->GetContactListener(); + return pListener->ShouldCollide( m_pObject, pOtherObject ); +} - pSystem->GetNarrowPhaseQueryNoLock().CollideShape( pObject->GetBody()->GetShape(), JPH::Vec3::sReplicate( 1.0f ), query_transform, settings, JPH::Vec3::sZero(), ioCollector, broadphase_layer_filter, object_layer_filter, ioFilter ); +void JoltPhysicsPlayerController::OnContactAdded( const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings ) +{ + JoltPhysicsContactListener *pListener = m_pObject->GetEnvironment()->GetContactListener(); } -// Slart: This is a version of CheckCollision that projects the player by their velocity, to attempt to push objects that we'll walk into soon -#if 0 -static void CheckCollision2( JoltPhysicsObject *pObject, JPH::CollideShapeCollector &ioCollector, const JPH::Vec3Arg targetVelocity, float flDeltaTime ) +//------------------------------------------------------------------------------------------------- + +static void CheckCollision( JoltPhysicsObject *pObject, JPH::CollideShapeCollector &ioCollector, JPH::BodyFilter &ioFilter ) { JPH::PhysicsSystem *pSystem = pObject->GetEnvironment()->GetPhysicsSystem(); - // TODO(Josh): Make a PLAYER ONLY layer that will only collide with MOVING ONLY annd - // NOTHING ELSE tomorrow. + if ( !pObject->IsCollisionEnabled() ) + return; // Create query broadphase layer filter JPH::DefaultBroadPhaseLayerFilter broadphase_layer_filter = pSystem->GetDefaultBroadPhaseLayerFilter( Layers::MOVING ); @@ -200,15 +231,11 @@ static void CheckCollision2( JoltPhysicsObject *pObject, JPH::CollideShapeCollec // Create query object layer filter JPH::DefaultObjectLayerFilter object_layer_filter = pSystem->GetDefaultLayerFilter( Layers::MOVING ); - // Ignore my own body - JPH::IgnoreSingleBodyFilter body_filter( pObject->GetBodyID() ); - // Determine position to test JPH::Vec3 position; JPH::Quat rotation; JPH::BodyInterface &bi = pSystem->GetBodyInterfaceNoLock(); bi.GetPositionAndRotation( pObject->GetBodyID(), position, rotation ); - position += targetVelocity * ( flDeltaTime * 2.0f ); JPH::Mat44 query_transform = JPH::Mat44::sRotationTranslation( rotation, position + rotation * pObject->GetBody()->GetShape()->GetCenterOfMass() ); // Settings for collide shape @@ -216,11 +243,11 @@ static void CheckCollision2( JoltPhysicsObject *pObject, JPH::CollideShapeCollec settings.mActiveEdgeMode = JPH::EActiveEdgeMode::CollideOnlyWithActive; settings.mActiveEdgeMovementDirection = bi.GetLinearVelocity( pObject->GetBodyID() ); settings.mBackFaceMode = JPH::EBackFaceMode::IgnoreBackFaces; - settings.mMaxSeparationDistance = vjolt_player_collision_tolerance.GetFloat(); + settings.mMaxSeparationDistance = vjolt_player_character_padding.GetFloat(); - pSystem->GetNarrowPhaseQueryNoLock().CollideShape( pObject->GetBody()->GetShape(), JPH::Vec3::sReplicate( 1.0f ), query_transform, settings, ioCollector, broadphase_layer_filter, object_layer_filter, body_filter ); + pSystem->GetNarrowPhaseQueryNoLock().CollideShape( pObject->GetBody()->GetShape(), JPH::Vec3::sReplicate( 1.0f ), query_transform, settings, JPH::Vec3::sZero(), ioCollector, broadphase_layer_filter, object_layer_filter, ioFilter ); } -#endif + template class SourceHitFilter : public JPH::BodyFilter @@ -257,48 +284,6 @@ class SourceHitFilter : public JPH::BodyFilter JoltPhysicsObject *m_pSelfObject; }; -class NormalWeightedCollector : public JPH::CollideShapeCollector -{ -public: - NormalWeightedCollector( JPH::PhysicsSystem *pPhysicsSystem ) - : m_pPhysicsSystem( pPhysicsSystem ) - { - } - - void Reset() override - { - JPH::CollideShapeCollector::Reset(); - - m_bHadHit = false; - m_flLowestNormalZ = 1.0f; - } - - void AddHit( const JPH::CollideShapeResult &inResult ) override - { - JPH::BodyLockRead lock( m_pPhysicsSystem->GetBodyLockInterfaceNoLock(), inResult.mBodyID2 ); - const JPH::Body &body = lock.GetBody(); - - JPH::Vec3 normal = body.GetWorldSpaceSurfaceNormal( inResult.mSubShapeID2, inResult.mContactPointOn2 ); - m_flLowestNormalZ = Min( m_flLowestNormalZ, -normal.GetZ() ); - - m_Hit = inResult; - m_bHadHit = true; - } - - inline bool HadHit() const - { - return m_bHadHit; - } - - float m_flLowestNormalZ = 1.0f; - - JPH::CollideShapeCollector::ResultType m_Hit; - -private: - JPH::PhysicsSystem *m_pPhysicsSystem; - bool m_bHadHit = false; -}; - uint32 JoltPhysicsPlayerController::GetContactState( uint16 nGameFlags ) { // This does not seem to affect much, we should aspire to have our physics be as 1:1 to brush collisions as possible anyway @@ -359,69 +344,138 @@ uint32 JoltPhysicsPlayerController::GetContactState( uint16 nGameFlags ) //------------------------------------------------------------------------------------------------- +int JoltPhysicsPlayerController::TryTeleportObject() +{ + if ( m_pHandler ) + { + if ( !m_pHandler->ShouldMoveTo( m_pObject, m_vTargetPosition ) ) + return 0; + } + + QAngle qCurrentAngles; + m_pObject->GetPosition( nullptr, &qCurrentAngles ); + m_pObject->SetPosition( m_vTargetPosition, qCurrentAngles, true ); + m_pCharacter->SetPosition( SourceToJolt::Distance( m_vTargetPosition ) ); + return 1; +} + void JoltPhysicsPlayerController::OnPreSimulate( float flDeltaTime ) { - VJoltAssertMsg( m_pObject->GetBody()->GetMotionType() == JPH::EMotionType::Kinematic, "Shadow controllers must be kinematic!" ); + m_pCharacter->SetLayer( m_pObject->IsCollisionEnabled() ? Layers::MOVING : Layers::NO_COLLIDE ); -#if 0 - if ( m_pGround ) + // Update position from dummy object. { - JPH::Mat44 matrix = JPH::Mat44::sRotationTranslation( m_pObject->GetBody()->GetRotation(), m_pObject->GetBody()->GetPosition() ).Transposed3x3(); - m_groundPos = -matrix.Multiply3x3( m_targetPosition ); + Vector vObjectPosition; + QAngle qObjectAngle; + m_pObject->GetPosition( &vObjectPosition, &qObjectAngle ); + m_pCharacter->SetPositionAndRotation( SourceToJolt::Distance( vObjectPosition ), SourceToJolt::Angle( qObjectAngle ), JPH::EActivation::DontActivate ); + } - matrix3x4_t mat; - m_pGround->GetPositionMatrix( &mat ); - m_targetPosition = mat.TransformVector( m_GroundPos ); + Vector vOldPosition = JoltToSource::Distance( m_pCharacter->GetPosition() ); + Vector vOldVelocity = JoltToSource::Distance( m_pCharacter->GetLinearVelocity() ); - m_pGround->GetVelocityAtPoint( m_GroundPos, &groundVelocity ); - m_pObject->AddVelocity( -groundVelocity ); - } -#else - /*if ( m_pGround ) - { - JPH::Mat44 matrix = JPH::Mat44::sRotationTranslation( m_pObject->GetBody()->GetRotation(), m_pObject->GetBody()->GetPosition() ).Transposed3x3(); - JPH::Vec3 groundPos = -matrix.Multiply3x3( m_targetPosition ); - int g = 5; - m_targetPosition = matrix * groundPos; - }*/ -#endif + Vector vDeltaPos = m_vTargetPosition - vOldPosition; - // Apply downwards force to the ground - // This code mimics JoltObject::ApplyForceOffset but without Source > Jolt conversions - /*if ( m_pGround && m_pGround->IsMoveable() ) + if ( m_bEnable ) { - JPH::PhysicsSystem *pPhysicsSystem = m_pGround->GetEnvironment()->GetPhysicsSystem(); + // Totally bogus! Measure error using last known estimate not current position. + if ( vDeltaPos.LengthSqr() > JPH::Square( m_flMaxDeltaPosition ) ) + { + if ( TryTeleportObject() ) + return; + } + } - JPH::BodyInterface &bodyInterface = pPhysicsSystem->GetBodyInterfaceNoLock(); - bodyInterface.AddImpulse( m_pGround->GetBodyID(), pPhysicsSystem->GetGravity() * m_pObject->GetMass() * flDeltaTime, m_pObject->GetBody()->GetPosition() ); - }*/ + float flFraction = Min( m_flSecondsToArrival > 0.0f ? flDeltaTime / m_flSecondsToArrival : 1.0f, 1.0f ); - JPH::PhysicsSystem *pPhysicsSystem = m_pObject->GetEnvironment()->GetPhysicsSystem(); - JPH::BodyInterface &bodyInterface = pPhysicsSystem->GetBodyInterfaceNoLock(); + // XXX TODO Set Mass + //m_pCharacter->GetBodyID()->SetMass(m_pObject->GetMass() * vjolt_player_mass_scale.GetFloat()); + m_pCharacter->SetPosition( SourceToJolt::Distance( vOldPosition ) ); - // Project ourselves towards our velocity - NormalWeightedCollector collector( pPhysicsSystem ); - SourceHitFilter filter( pPhysicsSystem, m_pObject ); - CheckCollision( m_pObject, collector, filter ); - - // Source typically uses -0.7 for ground. - if ( collector.HadHit() && collector.m_flLowestNormalZ < -0.7f ) + if ( m_bEnable ) { - JPH::BodyID otherID = collector.m_Hit.mBodyID2; + Vector vGroundVelocity = JoltToSource::Distance( m_pCharacter->GetGroundVelocity() ); + + Vector vControllerVelocity = vOldVelocity; + + vControllerVelocity -= vGroundVelocity; + if ( !m_bUpdatedSinceLast ) + { + float flLen = m_vLastImpulse.Length(); + Vector vTempMaxSpeed = Vector( flLen, flLen, flLen ); + ComputePlayerController( vControllerVelocity, vDeltaPos, vTempMaxSpeed, flFraction / flDeltaTime, m_flDampFactor, nullptr ); + } + else + { + ComputePlayerController( vControllerVelocity, vDeltaPos, m_vMaxSpeed, flFraction / flDeltaTime, m_flDampFactor, &m_vLastImpulse ); + } + vControllerVelocity += vGroundVelocity; - //bodyInterface.AddImpulse( otherID, m_pObject->GetMass() * m_targetVelocity * flDeltaTime, m_pObject->GetBody()->GetPosition() ); - bodyInterface.AddImpulse( otherID, m_pObject->GetMass() * pPhysicsSystem->GetGravity() * flDeltaTime, m_pObject->GetBody()->GetPosition()); + m_pCharacter->SetLinearVelocity( SourceToJolt::Distance( vControllerVelocity ) ); } - if ( m_secondsToArrival > 0.0f ) - bodyInterface.MoveKinematic( m_pObject->GetBodyID(), m_targetPosition, JPH::Quat::sIdentity(), m_secondsToArrival ); - else + m_vOldPosition = vOldPosition; +} + +void JoltPhysicsPlayerController::OnPostSimulate( float flDeltaTime ) +{ + m_pCharacter->PostSimulation( vjolt_player_collision_tolerance.GetFloat() ); + + // Calculate effective velocity + Vector vNewPosition = JoltToSource::Distance( m_pCharacter->GetPosition() ); + Vector vNewVelocity = ( vNewPosition - m_vOldPosition ) / flDeltaTime; + AngularImpulse vAngularImpulse; + + m_pObject->SetPosition( vNewPosition, QAngle(), false ); + m_pObject->SetVelocity( &vNewVelocity, &vAngularImpulse ); + + m_vLastImpulse = vNewVelocity; + + if ( vjolt_player_debug.GetBool() ) { - bodyInterface.SetPositionAndRotation( m_pObject->GetBodyID(), m_targetPosition, JPH::Quat::sIdentity(), JPH::EActivation::Activate ); - bodyInterface.SetLinearAndAngularVelocity( m_pObject->GetBodyID(), JPH::Vec3::sZero(), JPH::Vec3::sZero() ); + JoltPhysicsDebugRenderer& debugRenderer = JoltPhysicsDebugRenderer::GetInstance(); + + // Draw last impulse as a blue line. + debugRenderer.DrawLine( + SourceToJolt::Distance( vNewPosition ), + SourceToJolt::Distance( vNewPosition + m_vLastImpulse ), + JPH::Color( 0, 0, 255, 255 ) ); + + // Draw new player velocity as a purple line. + debugRenderer.DrawLine( + SourceToJolt::Distance( vNewPosition ), + SourceToJolt::Distance( vNewPosition + vNewVelocity ), + JPH::Color( 255, 0, 255, 255 ) ); + + Vector vecMins, vecMaxs; + JPH::Mat44 matComTransform = m_pCharacter->GetWorldTransform().PreTranslated( m_pCharacter->GetShape()->GetCenterOfMass() ); + JoltToSource::AABBBounds( m_pCharacter->GetShape()->GetWorldSpaceBounds( matComTransform, JPH::Vec3{ 1.0f, 1.0f, 1.0f } ), vecMins, vecMaxs ); + debugRenderer.GetDebugOverlay()->AddBoxOverlay( vec3_origin, vecMins, vecMaxs, QAngle(), m_bEnable ? 0 : 255, m_bEnable ? 255 : 0, 0, 100, 0.0f ); + +#if 0 + Log_Msg( LOG_VJolt, + "Player State:\n" + " vOldPosition: %g %g %g\n" + " vOldVelocity: %g %g %g\n" + " vNewPosition: %g %g %g\n" + " vNewVelocity: %g %g %g\n" + " m_vLastImpulse: %g %g %g\n" + " vControllerVelocity: %g %g %g\n" + " vGroundVelocity: %g %g %g\n", + vOldPosition.x, vOldPosition.x, vOldPosition.z, + vOldVelocity.x, vOldVelocity.x, vOldVelocity.z, + vNewPosition.x, vNewPosition.x, vNewPosition.z, + vNewVelocity.x, vNewVelocity.x, vNewVelocity.z, + m_vLastImpulse.x, m_vLastImpulse.x, m_vLastImpulse.z, + vControllerVelocity.x, vControllerVelocity.x, vControllerVelocity.z, + vGroundVelocity.x, vGroundVelocity.x, vGroundVelocity.z ); +#endif } - m_secondsToArrival = Max( m_secondsToArrival - flDeltaTime, 0.0f ); + if ( m_bEnable ) + { + m_flSecondsToArrival = Max( m_flSecondsToArrival - flDeltaTime, 0.0f ); + } } void JoltPhysicsPlayerController::OnJoltPhysicsObjectDestroyed( JoltPhysicsObject *pObject ) @@ -430,10 +484,6 @@ void JoltPhysicsPlayerController::OnJoltPhysicsObjectDestroyed( JoltPhysicsObjec { SetObjectInternal( nullptr ); } - if ( pObject == m_pGround ) - { - SetGround( nullptr ); - } } //------------------------------------------------------------------------------------------------- @@ -446,10 +496,12 @@ void JoltPhysicsPlayerController::SetObjectInternal( JoltPhysicsObject *pObject // Reset the last object if ( m_pObject ) { - // Don't bother resetting kinematic or sleep state, it does not matter because - // any object tied to a player controller was created to be a player object m_pObject->RemoveDestroyedListener( this ); m_pObject->RemoveCallbackFlags( CALLBACK_IS_PLAYER_CONTROLLER ); + m_pObject->UpdateLayer(); + + m_pCharacter->RemoveFromPhysicsSystem(); + m_pCharacter = nullptr; } // Set our new object @@ -460,28 +512,27 @@ void JoltPhysicsPlayerController::SetObjectInternal( JoltPhysicsObject *pObject { // Set kinematic m_pObject->GetBody()->SetMotionType( JPH::EMotionType::Kinematic ); - m_pObject->GetBody()->SetAllowSleeping( false ); - m_pObject->AddDestroyedListener( this ); m_pObject->AddCallbackFlags( CALLBACK_IS_PLAYER_CONTROLLER ); - } -} - -void JoltPhysicsPlayerController::SetGround( JoltPhysicsObject *pGround ) -{ - if ( m_pGround == pGround ) - return; - - if ( m_pGround ) - { - m_pGround->RemoveDestroyedListener( this ); - } - - // Set our new ground - m_pGround = pGround; - - if ( m_pGround ) - { - m_pGround->AddDestroyedListener( this ); + m_pObject->UpdateLayer(); + + static constexpr float k_flNormalSurfaceFriction = 0.8f; // Default surface friction. + // We can't always get external convars in VPhysics Jolt sadly... + // At least to my knowledge. + // Assume a friction of "8" (the default) for now + //ConVarRef sv_friction( "sv_friction" ); + static constexpr float sv_friction = 8; + + JPH::Ref settings = new JPH::CharacterSettings(); + settings->mMass = m_pObject->GetMass(); + settings->mLayer = Layers::MOVING; + settings->mUp = JPH::Vec3::sAxisZ(); + settings->mFriction = sv_friction * k_flNormalSurfaceFriction * ( 1.0f / 64.0f ); // Account for Source's friction being tick based. + settings->mShape = m_pObject->GetBody()->GetShape(); + settings->mMaxSlopeAngle = JPH::DegreesToRadians( 45.573 ); + settings->mEnhancedInternalEdgeRemoval = true; + + m_pCharacter = new JPH::Character( settings, m_pObject->GetBody()->GetPosition(), JPH::Quat::sIdentity(), m_pObject->GetBody()->GetUserData(), m_pObject->GetEnvironment()->GetPhysicsSystem() ); + m_pCharacter->AddToPhysicsSystem(); } } diff --git a/vphysics_jolt/vjolt_controller_player.h b/vphysics_jolt/vjolt_controller_player.h index b27cbdf6..68989cde 100644 --- a/vphysics_jolt/vjolt_controller_player.h +++ b/vphysics_jolt/vjolt_controller_player.h @@ -4,7 +4,7 @@ #include "vjolt_object.h" #include "vjolt_environment.h" -class JoltPhysicsPlayerController : public IPhysicsPlayerController, public IJoltObjectDestroyedListener, public IJoltPhysicsController +class JoltPhysicsPlayerController : public IPhysicsPlayerController, public IJoltObjectDestroyedListener, public IJoltPhysicsController, public JPH::CharacterContactListener { public: JoltPhysicsPlayerController( JoltPhysicsObject *pObject ); @@ -38,33 +38,38 @@ class JoltPhysicsPlayerController : public IPhysicsPlayerController, public IJol void OnJoltPhysicsObjectDestroyed( JoltPhysicsObject *pObject ) override; // IJoltPhysicsController void OnPreSimulate( float flDeltaTime ) override; + void OnPostSimulate( float flDeltaTime ) override; + + int TryTeleportObject(); + + bool OnContactValidate( const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2 ); + void OnContactAdded( const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings ); private: void SetObjectInternal( JoltPhysicsObject *pObject ); - void SetGround( JoltPhysicsObject *pObject ); private: + + JPH::Ref m_pCharacter; + JPH::Ref m_pShape; + JoltPhysicsObject *m_pObject = nullptr; - IPhysicsPlayerControllerEvent *m_pHandler = nullptr; - JoltPhysicsObject *m_pGround = nullptr; - JPH::Vec3 m_groundPos = JPH::Vec3::sZero(); + Vector m_vOldPosition = vec3_origin; - JPH::Vec3 m_targetPosition = JPH::Vec3::sZero(); // Where we want to be - JPH::Vec3 m_targetVelocity = JPH::Vec3::sZero(); // How we want to be - float m_secondsToArrival = FLT_EPSILON; // When we want to be + IPhysicsPlayerControllerEvent *m_pHandler = nullptr; + float m_flMaxDeltaPosition = 24.0f; + float m_flDampFactor = 1.0f; + float m_flSecondsToArrival = 0.0f; + float m_flPushableSpeedLimit = 1e4f; + float m_flPushableMassLimit = VPHYSICS_MAX_MASS; + Vector m_vTargetPosition = vec3_origin; + Vector m_vMaxSpeed = vec3_origin; + Vector m_vCurrentSpeed = vec3_origin; + Vector m_vLastImpulse = vec3_origin; - float m_maxSpeed = 0.0f; - float m_maxDampSpeed = 0.0f; - float m_maxAngular = 0.0f; - float m_maxDampAngular = 0.0f; - float m_teleportDistance = 0.0f; - bool m_isPhysicallyControlled = false; // If true we're a bone follower on an NPC or something... - bool m_allowTranslation = false; // Should we translate? - bool m_allowRotation = false; // Should we rotate? + bool m_bEnable = false; + bool m_bUpdatedSinceLast = false; - float m_flPushableMassLimit = 1e4f; - float m_flPushableSpeedLimit = 1e4f; - uint16 m_savedMaterialIndex = 0; };