diff --git a/Engine/source/T3D/physics/jolt/joltPlayer.cpp b/Engine/source/T3D/physics/jolt/joltPlayer.cpp index 035df8d30..09ad3d35e 100644 --- a/Engine/source/T3D/physics/jolt/joltPlayer.cpp +++ b/Engine/source/T3D/physics/jolt/joltPlayer.cpp @@ -4,20 +4,17 @@ #include "scene/sceneObject.h" #include "T3D/player.h" -// we use capsule for player? #ifdef Offset #pragma push_macro("Offset") #undef Offset #endif -#include #include -#include #include -#include +#include +#include #ifdef Offset - // Restore the original macro after includes #pragma pop_macro("Offset") #endif @@ -39,15 +36,14 @@ JoltPlayer::~JoltPlayer() { if (mCharacter) { - // Remove the inner body from the physics world before the character is released. - // Setting mCharacter=nullptr first would make disableCollision() a no-op. + mWorld->getPhysicsSystem()->GetBodyInterface().SetUserData( + mCharacter->GetBodyID(), 0); + if (mCollisionEnabled) disableCollision(); - mCharacter->SetUserData(0); - mCharacter = nullptr; + mCharacter = NULL; } - mIsEnabled = false; } @@ -56,42 +52,41 @@ void JoltPlayer::setTransform(const MatrixF& xfm) if (!mCharacter) return; - JPH::Mat44 mat = joltCast(xfm); + const Point3F pos = xfm.getPosition(); + const QuatF rot(xfm); - mCharacter->SetPosition(mat.GetTranslation()); - mCharacter->SetRotation(mat.GetQuaternion()); + // Character::SetPosition / SetRotation go through BodyInterface internally. + mCharacter->SetPosition(joltCast(pos), JPH::EActivation::Activate); + mCharacter->SetRotation(joltCast(rot), JPH::EActivation::Activate); } MatrixF& JoltPlayer::getTransform(MatrixF* outMatrix) { - const JPH::Mat44 trans = mCharacter->GetWorldTransform(); - *outMatrix = joltCast(trans); + *outMatrix = joltCast(mCharacter->GetWorldTransform()); return *outMatrix; } Box3F JoltPlayer::getWorldBounds() { - JPH::AABox box = mCharacter->GetShape()->GetWorldSpaceBounds(mCharacter->GetCenterOfMassTransform(), JPH::Vec3::sOne()); - return Box3F( - Point3F(joltCast(box.mMin)), - Point3F(joltCast(box.mMax))); + const JPH::AABox box = mCharacter->GetShape()->GetWorldSpaceBounds( + mCharacter->GetWorldTransform(), JPH::Vec3::sOne()); + return Box3F(Point3F(joltCast(box.mMin)), Point3F(joltCast(box.mMax))); } void JoltPlayer::setSimulationEnabled(bool enabled) { if (!mCharacter || !mWorld) return; - mIsEnabled = enabled; } -void JoltPlayer::init(const char* type, const Point3F& size, F32 runSurfaceCos, F32 stepHeight, SceneObject* obj, PhysicsWorld* world) +void JoltPlayer::init(const char* type, const Point3F& size, F32 runSurfaceCos, + F32 stepHeight, SceneObject* obj, PhysicsWorld* world) { if (!obj || !world) return; mObject = obj; - mWorld = dynamic_cast(world); if (!mWorld) return; @@ -99,59 +94,72 @@ void JoltPlayer::init(const char* type, const Point3F& size, F32 runSurfaceCos, mStepHeight = stepHeight; mMaxSlopeCos = runSurfaceCos; - F32 radius = mMax(size.x, size.y) * 0.5f; - F32 height = size.z;// +0.5f; - height = mMax(0.01f, height); + const F32 radius = mMax(size.x, size.y) * 0.5f; + F32 height = mMax(0.01f, size.z); - mOriginOffset = (0.5f * height);// +radius; - JPH::Quat rotFix = JPH::Quat::sRotation(JPH::Vec3::sAxisX(), JPH::DegreesToRadians(90.0f)); // zup - JPH::Ref shape = JPH::RotatedTranslatedShapeSettings(JPH::Vec3(0, 0, mOriginOffset), rotFix, new JPH::CapsuleShape(0.5f * height, radius)).Create().Get(); - JPH::Ref inner_shape = JPH::RotatedTranslatedShapeSettings(JPH::Vec3(0, 0, mOriginOffset), rotFix, new JPH::CapsuleShape(0.5f * 0.9f * height, 0.9f * radius)).Create().Get(); + mOriginOffset = (0.5f * height); - JPH::Ref settings = new JPH::CharacterVirtualSettings(); - Con::printf("JoltPlayer::init - maxSlopeAngle: %f degrees", mRadToDeg(mAcos(runSurfaceCos))); + // Capsule centred at mOriginOffset along Z so the bottom of the capsule + // sits at world Z = 0 (feet). rotFix converts Y-axis capsule → Z-up. + const JPH::Quat rotFix = JPH::Quat::sRotation( + JPH::Vec3::sAxisX(), JPH::DegreesToRadians(90.0f)); + + JPH::Ref shape = + JPH::RotatedTranslatedShapeSettings( + JPH::Vec3(0.0f, 0.0f, mOriginOffset), rotFix, + new JPH::CapsuleShape((0.5f * height) - radius, radius)) + .Create().Get(); + + JPH::Ref settings = new JPH::CharacterSettings(); + + settings->mUp = JPH::Vec3::sAxisZ(); // Z-up world settings->mMaxSlopeAngle = mAcos(runSurfaceCos); - settings->mMaxStrength = 100.0f; settings->mShape = shape; settings->mMass = obj->getMass(); - settings->mBackFaceMode = JPH::EBackFaceMode::IgnoreBackFaces; - settings->mCharacterPadding = 0.05f; - settings->mPenetrationRecoverySpeed = 1.0f; - settings->mMaxNumHits = 64; - settings->mPredictiveContactDistance = 0.05f; - settings->mSupportingVolume = JPH::Plane(JPH::Vec3::sAxisZ(), -(radius)); - settings->mEnhancedInternalEdgeRemoval = true; - settings->mInnerBodyShape = inner_shape; - settings->mInnerBodyLayer = Layers::CHARACTER; + settings->mFriction = 0.5f; + settings->mLayer = Layers::CHARACTER; + // Accept contacts that touch the hemisphere at the bottom of the capsule + settings->mSupportingVolume = JPH::Plane(JPH::Vec3::sAxisZ(), -radius); + + // Torque's Player already applies gravity via the displacement passed to + // move(). Suppress Jolt's built-in gravity here so it is not applied twice. + settings->mGravityFactor = 0.0f; + + Con::printf("JoltPlayer::init - maxSlopeAngle: %f degrees", + mRadToDeg(mAcos(runSurfaceCos))); + + // --- Create Character --- MatrixF objXfm = obj->getTransform(); - QuatF angPos(objXfm); + QuatF angPos(objXfm); Point3F pos; objXfm.getColumn(3, &pos); - mCharacter = new JPH::CharacterVirtual( + mCharacter = new JPH::Character( settings, joltCast(pos), joltCast(angPos), - 0, + reinterpret_cast(&mUserData), mWorld->getPhysicsSystem() ); - getUserData().setObject(obj); - //getUserData().setBody(this); - - mCharacter->SetUserData(reinterpret_cast(&mUserData)); + // Ensure Z is treated as up (may also be set via CharacterBaseSettings but + // calling SetUp explicitly guarantees it regardless of library version). mCharacter->SetUp(JPH::Vec3::sAxisZ()); - mCharacter->SetListener(this); + getUserData().setObject(obj); + + mCharacter->AddToPhysicsSystem(JPH::EActivation::Activate); setSimulationEnabled(true); } void JoltPlayer::preUpdate(F32 dt) { - // Intentionally empty. Ground velocity is refreshed inside move() on every - // call. If the calling code ever starts calling preUpdate() before move(), - // the refresh in move() will simply be a no-op on the same-frame data. + if (!mCharacter || !mIsEnabled) + return; + + static const float cCollisionTolerance = 0.05f; + mCharacter->PostSimulation(cCollisionTolerance); } Point3F JoltPlayer::move(const VectorF& displacement, CollisionList& outCol) @@ -162,34 +170,36 @@ Point3F JoltPlayer::move(const VectorF& displacement, CollisionList& outCol) if (!mIsEnabled) return joltCast(mCharacter->GetPosition()); - const F32 dt = TickSec; - JPH::Vec3 velocity = joltCast(displacement) / dt; + // If preUpdate() was not called by the engine, call PostSimulation here. + // (In architectures where preUpdate IS called this is a no-op because + // PostSimulation is idempotent between physics steps.) + static const float cCollisionTolerance = 0.05f; + mCharacter->PostSimulation(cCollisionTolerance); - const auto& system = mWorld->getPhysicsSystem(); + const F32 dt = TickSec; + JPH::Vec3 velocity = joltCast(displacement) / dt; const JPH::Vec3 up = mCharacter->GetUp(); - mCharacter->UpdateGroundVelocity(); - - const JPH::CharacterVirtual::EGroundState groundState = mCharacter->GetGroundState(); - const bool isOnGround = groundState == JPH::CharacterVirtual::EGroundState::OnGround; + const JPH::Character::EGroundState groundState = mCharacter->GetGroundState(); + const bool isOnGround = + groundState == JPH::Character::EGroundState::OnGround; if (isOnGround && !mCharacter->IsSlopeTooSteep(mCharacter->GetGroundNormal())) { - JPH::Vec3 horizontalVel(velocity.GetX(), velocity.GetY(), 0.0f); + const JPH::Vec3 horizontalVel(velocity.GetX(), velocity.GetY(), 0.0f); mAllowSliding = horizontalVel.Length() > 0.01f; + // Clamp small upward residuals on flat ground to prevent floating if (velocity.GetZ() < 0.05f) - velocity.SetZ(0.00f); + velocity.SetZ(0.0f); - JPH::Vec3 groundVel = mCharacter->GetGroundVelocity(); - // Threshold raised to filter static-surface floating-point residuals. + // Carry platform / conveyor velocity + const JPH::Vec3 groundVel = mCharacter->GetGroundVelocity(); if (groundVel.LengthSq() > 0.01f) { - // Always carry horizontal platform motion (conveyor belts, sliding floors). velocity.SetX(velocity.GetX() + groundVel.GetX()); velocity.SetY(velocity.GetY() + groundVel.GetY()); - // Only carry Z from platforms that are meaningfully moving upward (elevators). - // Static surfaces can produce tiny Z residuals that fight gravity resolution. + // Only add upward elevator velocity, not static-surface residuals if (groundVel.GetZ() > 0.05f) velocity.SetZ(velocity.GetZ() + groundVel.GetZ()); } @@ -199,213 +209,121 @@ Point3F JoltPlayer::move(const VectorF& displacement, CollisionList& outCol) mAllowSliding = true; } + // Hand velocity to Jolt — the physics step (tickPhysics) will move the body. + // Unlike CharacterVirtual there is no ExtendedUpdate here; collision response + // and stair-stepping are resolved by Jolt's constraint solver. mCharacter->SetLinearVelocity(velocity); + const bool activelyJumping = (velocity.GetZ() > 0.01f); - //---------------------------------------- - // Substep - //---------------------------------------- - const U32 iterations = 3; - const F32 subDt = dt / iterations; - - // Build settings once outside the loop — they don't change per-substep. - JPH::CharacterVirtual::ExtendedUpdateSettings extSettings; - extSettings.mWalkStairsStepUp = JPH::Vec3(0, 0, mStepHeight); - extSettings.mStickToFloorStepDown = JPH::Vec3(0, 0, -mStepHeight); - extSettings.mWalkStairsCosAngleForwardContact = mMaxSlopeCos; - - for (U32 i = 0; i < iterations; ++i) + // ----------------------------------------------------------------- + // Fill collision list — ground contact only + // (Non-ground contacts come through the physics ContactListener; + // sensor / trigger overlaps are found in findContact via broadphase) + // ----------------------------------------------------------------- + if (isOnGround && !activelyJumping && outCol.getCount() < CollisionList::MaxCollisions) { - mCharacter->ExtendedUpdate( - subDt, - system->GetGravity(), - extSettings, - system->GetDefaultBroadPhaseLayerFilter(Layers::CHARACTER), - system->GetDefaultLayerFilter(Layers::CHARACTER), - {}, - {}, - *mWorld->getTempAllocator() - ); - } + const JPH::BodyID groundBodyID = mCharacter->GetGroundBodyID(); + JPH::BodyLockRead lock( + mWorld->getPhysicsSystem()->GetBodyLockInterface(), groundBodyID); - //---------------------------------------- - // Fill collision list from active contacts - //---------------------------------------- - const JPH::CharacterVirtual::ContactList& contacts = mCharacter->GetActiveContacts(); - - for (const JPH::CharacterVirtual::Contact& c : contacts) - { - if (!c.mHadCollision) - continue; - - if (outCol.getCount() >= CollisionList::MaxCollisions) - break; - - JPH::BodyLockRead lock(system->GetBodyLockInterface(), c.mBodyB); - if (!lock.Succeeded()) - continue; - - const JPH::Body& body = lock.GetBody(); - PhysicsUserData* ud = reinterpret_cast(body.GetUserData()); - - Collision& col = outCol.increment(); - col.point = joltCast(c.mPosition); - col.normal = joltCast(c.mContactNormal); - col.object = ud ? ud->getObject() : nullptr; - col.material = nullptr; // material lookup not available yet - col.distance = c.mDistance; - col.face = 0; - col.faceDot = mDot(col.normal, VectorF(0.f, 0.f, 1.f)); - } - - // If on ground, ensure the ground body is represented even if it - // wasn't caught in the contact list (CharacterVirtual separates these) - if (mCharacter->GetGroundState() == JPH::CharacterVirtual::EGroundState::OnGround - && outCol.getCount() < CollisionList::MaxCollisions) - { - JPH::BodyLockRead lock(system->GetBodyLockInterface(), mCharacter->GetGroundBodyID()); if (lock.Succeeded()) { const JPH::Body& body = lock.GetBody(); PhysicsUserData* ud = reinterpret_cast(body.GetUserData()); - // Avoid duplicating if already added via active contacts - SceneObject* groundObj = ud ? ud->getObject() : nullptr; - bool alreadyPresent = false; - for (S32 i = 0; i < outCol.getCount(); ++i) - { - if (outCol[i].object == groundObj) - { - alreadyPresent = true; - break; - } - } - - if (!alreadyPresent) - { - Collision& col = outCol.increment(); - col.point = joltCast(mCharacter->GetGroundPosition()); - col.normal = joltCast(mCharacter->GetGroundNormal()); - col.object = groundObj; - col.material = nullptr; - col.distance = 0.f; - col.faceDot = mDot(col.normal, VectorF(0.f, 0.f, 1.f)); - } + Collision& col = outCol.increment(); + col.point = joltCast(mCharacter->GetGroundPosition()); + col.normal = joltCast(mCharacter->GetGroundNormal()); + col.object = ud ? ud->getObject() : NULL; + col.material = NULL; + col.distance = 0.0f; + col.faceDot = mDot(col.normal, VectorF(0.f, 0.f, 1.f)); } } - //---------------------------------------- - // Final position - //---------------------------------------- - Point3F finalPos = joltCast(mCharacter->GetPosition()); - return finalPos; + // Return the position from the last physics step. tickPhysics() will move + // the body further based on the velocity we just set; that result feeds into + // the next tick via GetPosition() here. + return joltCast(mCharacter->GetPosition()); } -void JoltPlayer::findContact(SceneObject** contactObject, VectorF* contactNormal, Vector* outOverlapObjects) const +void JoltPlayer::findContact(SceneObject** contactObject, VectorF* contactNormal, + Vector* outOverlapObjects) const { if (!mCharacter || !mIsEnabled) return; - const bool isOnGround = mCharacter->GetGroundState() == JPH::CharacterVirtual::EGroundState::OnGround; - F32 bestDot = -1.0f; - const JPH::Vec3 up = mCharacter->GetUp(); + const auto& system = mWorld->getPhysicsSystem(); - for (const JPH::CharacterVirtual::Contact& c : mCharacter->GetActiveContacts()) + // --- Primary ground contact ----------------------------------------- + // PostSimulation() already ran in preUpdate() and produced reliable ground + // data. Pull it directly rather than re-deriving the normal from + // CheckCollision's mPenetrationAxis. + if (mCharacter->GetGroundState() == JPH::Character::EGroundState::OnGround + && !mCharacter->GetGroundBodyID().IsInvalid()) { - if (c.mBodyB.IsInvalid()) - continue; - - if (c.mIsSensorB) + JPH::BodyLockRead lock(system->GetBodyLockInterface(), + mCharacter->GetGroundBodyID()); + if (lock.Succeeded()) { - JPH::BodyLockRead lock(mWorld->getPhysicsSystem()->GetBodyLockInterface(), c.mBodyB); - if (!lock.Succeeded()) - continue; - - const JPH::Body& body = lock.GetBody(); - PhysicsUserData* ud = (PhysicsUserData*)body.GetUserData(); - if (!ud) - continue; - - SceneObject* obj = ud->getObject(); - if (obj && !outOverlapObjects->contains(obj)) - outOverlapObjects->push_back(obj); - continue; // do not use for physical contact normal - } - - - if (c.mHadCollision) - { - JPH::BodyLockRead lock(mWorld->getPhysicsSystem()->GetBodyLockInterface(), c.mBodyB); - if (!lock.Succeeded()) - continue; - - const JPH::Body& body = lock.GetBody(); - PhysicsUserData* ud = (PhysicsUserData*)body.GetUserData(); - if (!ud) - continue; - - VectorF normal = joltCast(c.mContactNormal); - F32 dot = mDot(normal, VectorF(0, 0, 1)); - if (dot > bestDot) + auto* ud = reinterpret_cast(lock.GetBody().GetUserData()); + if (ud) { - bestDot = dot; *contactObject = ud->getObject(); - *contactNormal = normal; + *contactNormal = joltCast(mCharacter->GetGroundNormal()); } - SceneObject* obj = ud->getObject(); - if (obj && !outOverlapObjects->contains(obj)) - outOverlapObjects->push_back(obj); } } - if (isOnGround) + // --- Overlap harvest for triggers / items --------------------------- + // CheckCollision tests the character's actual capsule (not an expanded + // AABB) and returns every body within the skin distance. We only need + // body references here — no normal math — so the collector is trivial. + // Sensor bodies (triggers) and physical bodies (items) are both included + // so that Player::findContact() can call potentialEnterObject() on them. + struct OverlapCollector : public JPH::CollideShapeCollector { - JPH::BodyLockRead lock(mWorld->getPhysicsSystem()->GetBodyLockInterface(), mCharacter->GetGroundBodyID()); - if (!lock.Succeeded()) - return; + OverlapCollector(const JPH::PhysicsSystem* inSystem, + Vector& inOverlaps) + : mSystem(inSystem), mOverlaps(inOverlaps) { + } - const JPH::Body& body = lock.GetBody(); - PhysicsUserData* ud = (PhysicsUserData*)body.GetUserData(); - if (!ud) - return; + void AddHit(const JPH::CollideShapeResult& inResult) override + { + JPH::BodyLockRead lock(mSystem->GetBodyLockInterface(), inResult.mBodyID2); + if (!lock.Succeeded()) + return; - *contactObject = ud->getObject(); - *contactNormal = joltCast(mCharacter->GetGroundNormal()); - } + auto* ud = reinterpret_cast( + lock.GetBody().GetUserData()); + if (!ud) + return; + + if (auto* obj = ud->getObject(); obj && !mOverlaps.contains(obj)) + mOverlaps.push_back(obj); + } + + const JPH::PhysicsSystem* mSystem; + Vector& mOverlaps; + }; + + const JPH::RVec3 pos = mCharacter->GetPosition(); + + OverlapCollector collector(system, *outOverlapObjects); + mCharacter->CheckCollision( + pos, + mCharacter->GetRotation(), + JPH::Vec3::sZero(), // no movement hint needed — pure overlap harvest + 0.1f, // 10 cm skin + mCharacter->GetShape(), + pos, // base offset for float precision near origin + collector + ); } - bool JoltPlayer::testSpacials(const Point3F& nPos, const Point3F& nSize) const { return true; - - //if (!mCharacter || !mWorld) - // return false; - - //const auto& system = mWorld->getPhysicsSystem(); - - //JPH::BodyFilter body_filter; - //JPH::ShapeFilter shape_filter; - - //F32 radius = getMax(nSize.x, nSize.y) * 0.5f; - //F32 height = nSize.z - (radius * 2.0f); - - //F32 halfHeight = 0.5f * height; - //JPH::Vec3 shapeTranslation(0, 0, halfHeight + radius); - - //JPH::Quat rotFix = JPH::Quat::sRotation(JPH::Vec3::sAxisX(), JPH::DegreesToRadians(90.0f)); // zup - //JPH::Ref shape = JPH::RotatedTranslatedShapeSettings(shapeTranslation, rotFix, new JPH::CapsuleShape(halfHeight, radius)).Create().Get(); - - //const float maxPenetration = 0.05f; // tweak tolerance - - //return mCharacter->SetShape( - // shape, - // maxPenetration, - // system->GetDefaultBroadPhaseLayerFilter(Layers::MOVING), - // system->GetDefaultLayerFilter(Layers::MOVING), - // body_filter, - // shape_filter, - // *mWorld->getTempAllocator()); - } void JoltPlayer::setSpacials(const Point3F& nPos, const Point3F& nSize) @@ -413,34 +331,24 @@ void JoltPlayer::setSpacials(const Point3F& nPos, const Point3F& nSize) if (!mCharacter || !mWorld) return; - const auto& system = mWorld->getPhysicsSystem(); + const F32 radius = mMax(nSize.x, nSize.y) * 0.5f; + const F32 height = mMax(0.01f, nSize.z); - JPH::BodyFilter body_filter; - JPH::ShapeFilter shape_filter; + mOriginOffset = 0.5f * height; - F32 radius = mMax(nSize.x, nSize.y) * 0.5f; - F32 height = nSize.z;// +0.5f; - height = mMax(0.01f, height); + const JPH::Quat rotFix = JPH::Quat::sRotation( + JPH::Vec3::sAxisX(), JPH::DegreesToRadians(90.0f)); - mOriginOffset = (0.5f * height);// +radius; - JPH::Quat rotFix = JPH::Quat::sRotation(JPH::Vec3::sAxisX(), JPH::DegreesToRadians(90.0f)); // zup - JPH::Ref shape = JPH::RotatedTranslatedShapeSettings(JPH::Vec3(0, 0, mOriginOffset), rotFix, new JPH::CapsuleShape(0.5f * height, radius)).Create().Get(); - JPH::Ref inner_shape = JPH::RotatedTranslatedShapeSettings(JPH::Vec3(0, 0, mOriginOffset), rotFix, new JPH::CapsuleShape(0.5f * 0.9f * height, 0.9f * radius)).Create().Get(); + JPH::Ref shape = + JPH::RotatedTranslatedShapeSettings( + JPH::Vec3(0.0f, 0.0f, mOriginOffset), rotFix, + new JPH::CapsuleShape((0.5f * height) - radius, radius)) + .Create().Get(); - const float maxPenetration = 0.05f; // tweak tolerance - - if (mCharacter->SetShape( - shape, - maxPenetration, - system->GetDefaultBroadPhaseLayerFilter(Layers::CHARACTER), - system->GetDefaultLayerFilter(Layers::CHARACTER), - body_filter, - shape_filter, - *mWorld->getTempAllocator()) - ) - { - mCharacter->SetInnerBodyShape(inner_shape); - } + // Character::SetShape is simpler than CharacterVirtual::SetShape — + // no filter objects or TempAllocator required. + const float maxPenetration = 0.05f; + mCharacter->SetShape(shape, maxPenetration); } void JoltPlayer::enableCollision() @@ -449,15 +357,8 @@ void JoltPlayer::enableCollision() return; mCollisionEnabled = true; - - JPH::BodyID innerBodyID = mCharacter->GetInnerBodyID(); - if (innerBodyID.IsInvalid()) - return; - - mWorld->getPhysicsSystem()->GetBodyInterface().AddBody( - innerBodyID, - JPH::EActivation::Activate - ); + // Character IS a body — adding it back to the physics system re-enables it + mCharacter->AddToPhysicsSystem(JPH::EActivation::Activate); } void JoltPlayer::disableCollision() @@ -466,121 +367,5 @@ void JoltPlayer::disableCollision() return; mCollisionEnabled = false; - - JPH::BodyID innerBodyID = mCharacter->GetInnerBodyID(); - if (innerBodyID.IsInvalid()) - return; - - mWorld->getPhysicsSystem()->GetBodyInterface().RemoveBody(innerBodyID); -} - -void JoltPlayer::OnContactCommon(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings) -{ - if (inCharacter == mCharacter) - { - if (ioSettings.mCanPushCharacter && mWorld->getPhysicsSystem()->GetBodyInterface().GetMotionType(inBodyID2) != JPH::EMotionType::Static) - { - mAllowSliding = true; - } - } -} - -void JoltPlayer::OnCharacterContactCommon(const JPH::CharacterVirtual* inCharacter, const JPH::CharacterVirtual* inOtherCharacter, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings) -{ - if (inCharacter == mCharacter && ioSettings.mCanPushCharacter) - mAllowSliding = true; -} - -//------------------------------------------------------------------ -// Character Contact Listener Callbacks -//------------------------------------------------------------------ - -void JoltPlayer::OnAdjustBodyVelocity(const JPH::CharacterVirtual* inCharacter, const JPH::Body& inBody2, JPH::Vec3& ioLinearVelocity, JPH::Vec3& ioAngularVelocity) -{ -} - -bool JoltPlayer::OnContactValidate(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2) -{ - if (inCharacter != mCharacter) - return true; - - JPH::BodyLockRead lock(mWorld->getPhysicsSystem()->GetBodyLockInterface(), inBodyID2); - if (lock.Succeeded() && lock.GetBody().IsSensor()) - return false; - - return true; -} - -void JoltPlayer::OnContactAdded(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings) -{ - if (inCharacter != mCharacter) - return; - - // Sensors are already rejected by OnContactValidate so this callback will - // never fire for them. No sensor check needed here. - JPH::CharacterVirtual::ContactKey c(inBodyID2, inSubShapeID2); - if (std::find(mActiveContacts.begin(), mActiveContacts.end(), c) != mActiveContacts.end()) - { - Con::warnf("JoltPlayer::OnContactAdded - contact already present, skipping duplicate"); - return; - } - mActiveContacts.push_back(c); - - OnContactCommon(inCharacter, inBodyID2, inSubShapeID2, inContactPosition, inContactNormal, ioSettings); -} - -void JoltPlayer::OnContactPersisted(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings) -{ - if (inCharacter != mCharacter) - return; - - // Sensors are already rejected by OnContactValidate — this callback will - // not fire for them. - JPH::CharacterVirtual::ContactKey c(inBodyID2, inSubShapeID2); - if (std::find(mActiveContacts.begin(), mActiveContacts.end(), c) == mActiveContacts.end()) - { - Con::warnf("JoltPlayer::OnContactPersisted - contact not in set, inserting as new"); - mActiveContacts.push_back(c); - } - - OnContactCommon(inCharacter, inBodyID2, inSubShapeID2, inContactPosition, inContactNormal, ioSettings); -} - -void JoltPlayer::OnContactRemoved(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2) -{ - if (inCharacter == mCharacter) - { - JPH::CharacterVirtual::ContactKey c(inBodyID2, inSubShapeID2); - ContactSet::iterator it = std::find(mActiveContacts.begin(), mActiveContacts.end(), c); - if (it == mActiveContacts.end()) - { - // Can occur when the contact was never added due to an earlier ordering anomaly. - Con::warnf("JoltPlayer::OnContactRemoved - contact not found, ignoring"); - return; - } - mActiveContacts.erase(it); - } -} - -void JoltPlayer::OnCharacterContactAdded(const JPH::CharacterVirtual* inCharacter, const JPH::CharacterVirtual* inOtherCharacter, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings) -{ -} - -void JoltPlayer::OnCharacterContactPersisted(const JPH::CharacterVirtual* inCharacter, const JPH::CharacterVirtual* inOtherCharacter, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings) -{ -} - -void JoltPlayer::OnCharacterContactRemoved(const JPH::CharacterVirtual* inCharacter, const JPH::CharacterID& inOtherCharacterID, const JPH::SubShapeID& inSubShapeID2) -{ -} - -void JoltPlayer::OnContactSolve(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::Vec3Arg inContactVelocity, const JPH::PhysicsMaterial* inContactMaterial, JPH::Vec3Arg inCharacterVelocity, JPH::Vec3& ioNewCharacterVelocity) -{ - if (inCharacter != mCharacter) - return; - - // When not sliding and contact surface isn't moving and slope is walkable, - // zero out velocity to prevent micro-jitter from solver residuals - if (!mAllowSliding && inContactVelocity.IsNearZero() && !inCharacter->IsSlopeTooSteep(inContactNormal)) - ioNewCharacterVelocity = JPH::Vec3::sZero(); + mCharacter->RemoveFromPhysicsSystem(); } diff --git a/Engine/source/T3D/physics/jolt/joltPlayer.h b/Engine/source/T3D/physics/jolt/joltPlayer.h index 536659b2e..ab14f2d0c 100644 --- a/Engine/source/T3D/physics/jolt/joltPlayer.h +++ b/Engine/source/T3D/physics/jolt/joltPlayer.h @@ -11,106 +11,59 @@ #include "T3D/physics/jolt/joltWorld.h" #endif - - #ifdef Offset #pragma push_macro("Offset") #undef Offset #endif -#include +#include #ifdef Offset -// Restore the original macro after includes #pragma pop_macro("Offset") #endif -class JoltPlayer : public PhysicsPlayer, public JPH::CharacterContactListener +class JoltPlayer : public PhysicsPlayer { protected: JoltWorld* mWorld; SceneObject* mObject; - JPH::Ref mCharacter; - JPH::Vec3 mDesiredVelocity; - F32 mMaxSlopeCos; - F32 mStepHeight; - F32 mOriginOffset; - /// Is the body participating in the physics simulation. - bool mIsEnabled; - bool mAllowSliding; - bool mCollisionEnabled; + JPH::Ref mCharacter; + JPH::Vec3 mDesiredVelocity; + F32 mMaxSlopeCos; + F32 mStepHeight; + F32 mOriginOffset; + bool mIsEnabled; + bool mAllowSliding; + bool mCollisionEnabled; - using ContactSet = JPH::Array; - ContactSet mActiveContacts; public: JoltPlayer(); virtual ~JoltPlayer(); // PhysicsObject PhysicsWorld* getWorld() override { return mWorld; } - void setTransform(const MatrixF& xfm) override; + void setTransform(const MatrixF& xfm) override; MatrixF& getTransform(MatrixF* outMatrix) override; - Box3F getWorldBounds() override; - void setSimulationEnabled(bool enabled) override; - bool isSimulationEnabled() override { return mIsEnabled; } + Box3F getWorldBounds() override; + void setSimulationEnabled(bool enabled) override; + bool isSimulationEnabled() override { return mIsEnabled; } - // Physics Player. - void init(const char* type, - const Point3F& size, - F32 runSurfaceCos, - F32 stepHeight, - SceneObject* obj, - PhysicsWorld* world) override; + // PhysicsPlayer + void init(const char* type, const Point3F& size, F32 runSurfaceCos, + F32 stepHeight, SceneObject* obj, PhysicsWorld* world) override; void preUpdate(F32 dt); Point3F move(const VectorF& displacement, CollisionList& outCol) override; - void findContact(SceneObject** contactObject, VectorF* contactNormal, Vector* outOverlapObjects) const override; + void findContact(SceneObject** contactObject, VectorF* contactNormal, + Vector* outOverlapObjects) const override; bool testSpacials(const Point3F& nPos, const Point3F& nSize) const override; void setSpacials(const Point3F& nPos, const Point3F& nSize) override; - void enableCollision() override; + void enableCollision() override; void disableCollision() override; - JPH::Ref getCharacter() { return mCharacter; } - -protected: - // Common function to be called when contacts are added/persisted - void OnContactCommon(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings); - void OnCharacterContactCommon(const JPH::CharacterVirtual* inCharacter, const JPH::CharacterVirtual* inOtherCharacter, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings); - - -public: // contact listener - /// Callback to adjust the velocity of a body as seen by the character. Can be adjusted to e.g. implement a conveyor belt or an inertial dampener system of a sci-fi space ship. - virtual void OnAdjustBodyVelocity(const JPH::CharacterVirtual* inCharacter, const JPH::Body& inBody2, JPH::Vec3& ioLinearVelocity, JPH::Vec3& ioAngularVelocity) override; - - // Called to check if the character can collide with a body at all. Return false to discard the contact - // before it reaches the constraint solver, stair walking, or stick-to-floor algorithms. - virtual bool OnContactValidate(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2) override; - - // Called whenever the character collides with a body. - virtual void OnContactAdded(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings) override; - - // Called whenever the character persists colliding with a body. - virtual void OnContactPersisted(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings) override; - - // Called whenever the character loses contact with a body. - virtual void OnContactRemoved(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2) override; - - // Called whenever the character collides with a virtual character. - virtual void OnCharacterContactAdded(const JPH::CharacterVirtual* inCharacter, const JPH::CharacterVirtual* inOtherCharacter, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings) override; - - // Called whenever the character persists colliding with a virtual character. - virtual void OnCharacterContactPersisted(const JPH::CharacterVirtual* inCharacter, const JPH::CharacterVirtual* inOtherCharacter, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings) override; - - // Called whenever the character loses contact with a virtual character. - virtual void OnCharacterContactRemoved(const JPH::CharacterVirtual* inCharacter, const JPH::CharacterID& inOtherCharacterID, const JPH::SubShapeID& inSubShapeID2) override; - - // Called whenever the character movement is solved and a constraint is hit. Allows the listener to override the resulting character velocity (e.g. by preventing sliding along certain surfaces). - virtual void OnContactSolve(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::Vec3Arg inContactVelocity, const JPH::PhysicsMaterial* inContactMaterial, JPH::Vec3Arg inCharacterVelocity, JPH::Vec3& ioNewCharacterVelocity) override; - - + JPH::Ref getCharacter() { return mCharacter; } }; - -#endif +#endif // _JOLTPLAYER_H_