From 48f2c69c383352d4a39f2b2f517d6157f631633b Mon Sep 17 00:00:00 2001 From: Antoine Pilote Date: Sat, 23 Mar 2024 15:08:43 -0400 Subject: [PATCH] Fixed physics and added ghost body to virtual characters --- Nuake/src/Physics/DynamicWorld.cpp | 95 +++++++++++++++++++++++++----- Nuake/src/Physics/DynamicWorld.h | 8 ++- 2 files changed, 86 insertions(+), 17 deletions(-) diff --git a/Nuake/src/Physics/DynamicWorld.cpp b/Nuake/src/Physics/DynamicWorld.cpp index c07116b3..77ee13b6 100644 --- a/Nuake/src/Physics/DynamicWorld.cpp +++ b/Nuake/src/Physics/DynamicWorld.cpp @@ -72,7 +72,9 @@ namespace Nuake static constexpr uint8_t NON_MOVING = 0; static constexpr uint8_t MOVING = 1; static constexpr uint8_t KINEMATIC = 2; - static constexpr uint8_t NUM_LAYERS = 3; + static constexpr uint8_t CHARACTER_GHOST = 3; + static constexpr uint8_t CHARACTER = 4; + static constexpr uint8_t NUM_LAYERS = 5; }; // Function that determines if two object layers can collide @@ -81,11 +83,13 @@ namespace Nuake switch (inObject1) { case Layers::NON_MOVING: - return inObject2 == Layers::MOVING || inObject2 == Layers::KINEMATIC; // Non moving only collides with moving + return inObject2 == Layers::MOVING || inObject2 == Layers::KINEMATIC || inObject2 == Layers::CHARACTER; // Non moving only collides with moving case Layers::MOVING: return true; // Moving collides with everything case Layers::KINEMATIC: - return inObject2 == Layers::NON_MOVING || inObject2 == Layers::MOVING; // Only collides with non moving + return inObject2 == Layers::NON_MOVING || inObject2 == Layers::MOVING || inObject2 == Layers::CHARACTER; // Only collides with non moving + case Layers::CHARACTER: + return true; default: //JPH_ASSERT(false); return false; @@ -114,6 +118,8 @@ namespace Nuake // Create a mapping table from object to broad phase layer mObjectToBroadPhase[Layers::NON_MOVING] = BroadPhaseLayers::NON_MOVING; mObjectToBroadPhase[Layers::MOVING] = BroadPhaseLayers::MOVING; + mObjectToBroadPhase[Layers::CHARACTER] = BroadPhaseLayers::MOVING; + mObjectToBroadPhase[Layers::CHARACTER_GHOST] = BroadPhaseLayers::MOVING; } virtual JPH::uint GetNumBroadPhaseLayers() const override @@ -239,9 +245,13 @@ namespace Nuake switch (inObject1) { case Layers::NON_MOVING: - return inObject2 == Layers::MOVING; // Non moving only collides with moving + return inObject2 == Layers::MOVING || Layers::CHARACTER_GHOST; // Non moving only collides with moving case Layers::MOVING: return true; // Moving collides with everything + case Layers::CHARACTER_GHOST: + return true;// inObject2 != Layers::CHARACTER; + case Layers::CHARACTER: + return inObject2 != Layers::CHARACTER_GHOST; default: return false; @@ -258,7 +268,7 @@ namespace Nuake { DynamicWorld::DynamicWorld() : _stepCount(0) { - _registeredCharacters = std::map>(); + _registeredCharacters = std::map(); // Initialize Jolt Physics const uint32_t MaxBodies = 4096; @@ -364,7 +374,8 @@ namespace Nuake bodySettings.mUserData = entityId; // Create the actual rigid body JPH::BodyID body = _JoltBodyInterface->CreateAndAddBody(bodySettings, JPH::EActivation::Activate); // Note that if we run out of bodies this can return nullptr - uint32_t bodyIndex = (uint32_t)body.GetIndex(); + uint32_t bodyIndex = (uint32_t)body.GetIndexAndSequenceNumber(); + auto userData = _JoltBodyInterface->GetUserData(body); _registeredBodies.push_back(bodyIndex); } @@ -386,10 +397,34 @@ namespace Nuake const Quat& bodyRotation = cc->Rotation; const auto& joltRotation = JPH::Quat(bodyRotation.x, bodyRotation.y, bodyRotation.z, bodyRotation.w); - auto character = CreateRef(settings, std::move(joltPosition), std::move(joltRotation), _JoltPhysicsSystem.get()); + auto character = CreateRef(settings, std::move(joltPosition), joltRotation, _JoltPhysicsSystem.get()); + + // add ghost kinematic body to respond to hit test as the virtual char are not present in the world. + JPH::BodyInterface& bodyInterface = _JoltPhysicsSystem->GetBodyInterface(); + + const float mass = 0.1f; + JPH::EMotionType motionType = JPH::EMotionType::Kinematic; + JPH::ObjectLayer layer = Layers::CHARACTER_GHOST; + + const auto& startPos = joltPosition; + auto joltShape = GetJoltShape(cc->Shape); + JPH::BodyCreationSettings bodySettings(joltShape, startPos, joltRotation, motionType, layer); + + int entityId = cc->GetEntity().GetID(); + if (entityId == 0) + { + Logger::Log("ERROR"); + } + + //bodySettings.mUserData = entityId; + + // Create the actual rigid body + JPH::BodyID body = _JoltBodyInterface->CreateAndAddBody(bodySettings, JPH::EActivation::Activate); // Note that if we run out of bodies this can return nullptr + uint32_t bodyIndex = body.GetIndexAndSequenceNumber(); + _registeredBodies.push_back(bodyIndex); // To get the jolt character control from a scene entity. - _registeredCharacters[cc->Owner.GetHandle()] = character; + _registeredCharacters[cc->Owner.GetHandle()] = CharacterGhostPair{ character, bodyIndex }; } bool DynamicWorld::IsCharacterGrounded(const Entity& entity) @@ -397,7 +432,7 @@ namespace Nuake const uint32_t entityHandle = entity.GetHandle(); if (_registeredCharacters.find(entityHandle) != _registeredCharacters.end()) { - auto& characterController = _registeredCharacters[entityHandle]; + auto& characterController = _registeredCharacters[entityHandle].Character; const auto groundState = characterController->GetGroundState(); return groundState == JPH::CharacterBase::EGroundState::OnGround; @@ -493,7 +528,7 @@ namespace Nuake { Entity entity { (entt::entity)e.first, Engine::GetCurrentScene().get()}; - Ref characterController = e.second; + Ref characterController = e.second.Character; JPH::Mat44 joltTransform = characterController->GetWorldTransform(); const auto bodyRotation = characterController->GetRotation(); @@ -571,7 +606,7 @@ namespace Nuake auto characterController = characterControllerComponent.GetCharacterController(); const auto& broadPhaseLayerFilter = _JoltPhysicsSystem->GetDefaultBroadPhaseLayerFilter(Layers::NON_MOVING); - const auto& LayerFilter = _JoltPhysicsSystem->GetDefaultLayerFilter(Layers::MOVING); + const auto& LayerFilter = _JoltPhysicsSystem->GetDefaultLayerFilter(Layers::CHARACTER); const auto& joltGravity = _JoltPhysicsSystem->GetGravity(); auto& tempAllocatorPtr = *(joltTempAllocator); if (characterController->AutoStepping) @@ -583,11 +618,11 @@ namespace Nuake joltUpdateSettings.mWalkStairsStepForwardTest = characterController->StepDistance; joltUpdateSettings.mWalkStairsMinStepForward = characterController->StepMinDistance; - c.second->ExtendedUpdate(ts, joltGravity, joltUpdateSettings, broadPhaseLayerFilter, LayerFilter, { }, { }, tempAllocatorPtr); + c.second.Character->ExtendedUpdate(ts, joltGravity, joltUpdateSettings, broadPhaseLayerFilter, LayerFilter, { }, { }, tempAllocatorPtr); } else { - c.second->Update(ts, joltGravity, broadPhaseLayerFilter, LayerFilter, {}, {}, tempAllocatorPtr); + c.second.Character->Update(ts, joltGravity, broadPhaseLayerFilter, LayerFilter, {}, {}, tempAllocatorPtr); } } } @@ -599,6 +634,30 @@ namespace Nuake Logger::Log("Failed to run simulation update", "physics", CRITICAL); } + for (auto& c : _registeredCharacters) + { + uint32_t ghostId = c.second.Ghost; + + JPH::Mat44 joltTransform = c.second.Character->GetWorldTransform(); + const auto bodyRotation = c.second.Character->GetRotation(); + Matrix4 transform = glm::mat4( + joltTransform(0, 0), joltTransform(1, 0), joltTransform(2, 0), joltTransform(3, 0), + joltTransform(0, 1), joltTransform(1, 1), joltTransform(2, 1), joltTransform(3, 1), + joltTransform(0, 2), joltTransform(1, 2), joltTransform(2, 2), joltTransform(3, 2), + joltTransform(0, 3), joltTransform(1, 3), joltTransform(2, 3), joltTransform(3, 3) + ); + + Vector3 scale = Vector3(); + Quat rotation = Quat(); + Vector3 pos = Vector3(); + Vector3 skew = Vector3(); + Vector4 pesp = Vector4(); + glm::decompose(transform, scale, rotation, pos, skew, pesp); + + //auto& bodyInterface = _JoltPhysicsSystem->GetBodyInterfaceNoLock(); + _JoltBodyInterface->MoveKinematic(static_cast(ghostId), JPH::Vec3{ pos.x, pos.y, pos.z }, { rotation.x, rotation.y, rotation.z, rotation.w }, 1.0); + } + SyncEntitiesTranforms(); SyncCharactersTransforms(); } @@ -629,8 +688,12 @@ namespace Nuake const uint32_t entityHandle = entity.GetHandle(); if (_registeredCharacters.find(entityHandle) != _registeredCharacters.end()) { - auto& characterController = _registeredCharacters[entityHandle]; - characterController->SetLinearVelocity(JPH::Vec3(velocity.x, velocity.y, velocity.z)); + auto& characterController = _registeredCharacters[entityHandle].Character; + const auto& joltVelocity = JPH::Vec3(velocity.x, velocity.y, velocity.z); + characterController->SetLinearVelocity(joltVelocity); + + auto& ghost = _registeredCharacters[entityHandle].Ghost; + //_JoltBodyInterface->SetLinearVelocity(static_cast(ghost), joltVelocity); } } @@ -640,7 +703,7 @@ namespace Nuake for (const auto& body : _registeredBodies) { auto bodyId = static_cast(body); - auto entityId = static_cast(bodyInterface.GetUserData(bodyId)); + auto entityId = bodyInterface.GetUserData(bodyId); if (entityId == entity.GetID()) { bodyInterface.AddForce(bodyId, JPH::Vec3(force.x, force.y, force.z)); diff --git a/Nuake/src/Physics/DynamicWorld.h b/Nuake/src/Physics/DynamicWorld.h index 5569b799..78226b9e 100644 --- a/Nuake/src/Physics/DynamicWorld.h +++ b/Nuake/src/Physics/DynamicWorld.h @@ -35,6 +35,12 @@ namespace Nuake namespace Physics { + struct CharacterGhostPair + { + Ref Character; + uint32_t Ghost; + }; + class DynamicWorld { private: @@ -48,7 +54,7 @@ namespace Nuake BPLayerInterfaceImpl* _JoltBroadphaseLayerInterface; std::vector _registeredBodies; - std::map> _registeredCharacters; + std::map _registeredCharacters; public: DynamicWorld();