diff --git a/Editor/resources/Scripts/Scene.wren b/Editor/resources/Scripts/Scene.wren index 2511c6e5..2c0b5fe0 100644 --- a/Editor/resources/Scripts/Scene.wren +++ b/Editor/resources/Scripts/Scene.wren @@ -45,6 +45,7 @@ class Scene { // Transform foreign static GetScript_(e) + foreign static GetLocalTranslation_(e) foreign static GetTranslation_(e) foreign static SetTranslation_(e, x, y, z) foreign static SetRotation_(e, x, y, z) @@ -123,11 +124,17 @@ class TransformComponent { _entityId = id } + GetLocalTranslation() { + var result = Scene.GetLocalTranslation_(_entityId) + return Vector3.new(result[0], result[1], result[2]) + } + GetTranslation() { var result = Scene.GetTranslation_(_entityId) return Vector3.new(result[0], result[1], result[2]) } + SetTranslation(t) { Scene.SetTranslation_(_entityId, t.x, t.y, t.z) } diff --git a/Editor/src/ComponentsPanel/CharacterControllerPanel.h b/Editor/src/ComponentsPanel/CharacterControllerPanel.h index dbbae0c5..78ba6c91 100644 --- a/Editor/src/ComponentsPanel/CharacterControllerPanel.h +++ b/Editor/src/ComponentsPanel/CharacterControllerPanel.h @@ -12,6 +12,7 @@ public: { if (!entity.HasComponent()) return; + using namespace Nuake; auto& component = entity.GetComponent(); BeginComponentTable(CHARACTER CONTROLLER, Nuake::CharacterControllerComponent); @@ -33,6 +34,59 @@ public: ImGui::TableNextColumn(); ComponentTableReset(component.MaxSlopeAngle, 0.45f) } + ImGui::TableNextColumn(); + { + ImGui::Text("Auto stepping"); + ImGui::TableNextColumn(); + + ImGui::Checkbox("##AutoStepping", &component.AutoStepping); + ImGui::TableNextColumn(); + ComponentTableReset(component.AutoStepping, true) + } + + if (component.AutoStepping) + { + ImGui::TableNextColumn(); + { + ImGui::Text("Stick to floor step down"); + ImGui::TableNextColumn(); + ImGui::DragFloat("##StickToFloorStepDown", &component.StickToFloorStepDown.y, -10.0f, 0.01, 0.0f); + ImGui::TableNextColumn(); + ComponentTableReset(component.StickToFloorStepDown.y, -0.5f); + } + ImGui::TableNextColumn(); + { + ImGui::Text("Step down extra"); + ImGui::TableNextColumn(); + ImGui::DragFloat("##StepDownExtra", &component.StepDownExtra.y, -10.0f, 0.01, 0.0f); + ImGui::TableNextColumn(); + ComponentTableReset(component.StepDownExtra.y, 0.0f); + } + ImGui::TableNextColumn(); + { + ImGui::Text("Step up"); + ImGui::TableNextColumn(); + ImGui::DragFloat("##StepUp", &component.SteppingStepUp.y, 0.0f, 0.01, 10.0f); + ImGui::TableNextColumn(); + ComponentTableReset(component.SteppingStepUp.y, 0.4f); + } + ImGui::TableNextColumn(); + { + ImGui::Text("Step distance"); + ImGui::TableNextColumn(); + ImGui::DragFloat("##StepDistance", &component.SteppingForwardDistance, 0.0f, 0.01f, 10.0f); + ImGui::TableNextColumn(); + ComponentTableReset(component.SteppingForwardDistance, 0.250f) + } + ImGui::TableNextColumn(); + { + ImGui::Text("Step min distance"); + ImGui::TableNextColumn(); + ImGui::DragFloat("##StepMinDistance", &component.SteppingMinDistance, 0.0f, 0.01f, component.SteppingForwardDistance); + ImGui::TableNextColumn(); + ComponentTableReset(component.SteppingMinDistance, 0.125f) + } + } } EndComponentTable() } diff --git a/Nuake/src/Core/Physics/CharacterController.cpp b/Nuake/src/Core/Physics/CharacterController.cpp index ddcd671c..0ebb728c 100644 --- a/Nuake/src/Core/Physics/CharacterController.cpp +++ b/Nuake/src/Core/Physics/CharacterController.cpp @@ -8,11 +8,17 @@ namespace Nuake { namespace Physics { - CharacterController::CharacterController(const Ref& shape, float friction, float maxSlopeAngle) + CharacterController::CharacterController(const CharacterControllerSettings& settings) { - Shape = shape; - Friction = friction; - MaxSlopeAngle = maxSlopeAngle; + Shape = settings.Shape; + Friction = settings.Friction; + MaxSlopeAngle = settings.MaxSlopeAngle; + AutoStepping = settings.AutoStepping; + StepDown = settings.StepDown; + StepDownExtra = settings.StepDownExtra; + StepUp = settings.StepUp; + StepDistance = settings.StepDistance; + StepMinDistance = settings.StepMinDistance; } void CharacterController::SetEntity(Entity& ent) diff --git a/Nuake/src/Core/Physics/CharacterController.h b/Nuake/src/Core/Physics/CharacterController.h index b4bf99ab..80e0df4d 100644 --- a/Nuake/src/Core/Physics/CharacterController.h +++ b/Nuake/src/Core/Physics/CharacterController.h @@ -10,35 +10,49 @@ namespace Nuake namespace Physics { + struct CharacterControllerSettings + { + Ref Shape; + float Friction; + float MaxSlopeAngle; + bool AutoStepping; + Vector3 StepDown; + Vector3 StepDownExtra; + Vector3 StepUp; + float StepMinDistance; + float StepDistance; + }; + class CharacterController { public: - Vector3 Position = Vector3(0, 0, 0); - Quat Rotation = Quat(1, 0, 0, 0); - Entity Owner; - bool IsOnGround = false; - bool m_hittingWall; - - float m_stepHeight = 0.35f; - float MaxSlopeAngle = 45.0f; - float Friction = 0.5f; + // Settings Ref Shape; + float Friction = 0.5f; + float MaxSlopeAngle = 45.0f; + bool AutoStepping; + Vector3 StepDown; + Vector3 StepDownExtra; + Vector3 StepUp; + float StepDistance; + float StepMinDistance; - float m_bottomYOffset; - float m_bottomRoundedRegionYOffset; + // State + Vector3 Position = Vector3(0, 0, 0); + Quat Rotation = Quat(1, 0, 0, 0); + bool IsOnGround = false; - Vector3 m_manualVelocity; - std::vector m_surfaceHitNormals; + public: + CharacterController(const CharacterControllerSettings& settings); - CharacterController(const Ref& shape, float friction, float maxSlopeAngle); + void MoveAndSlide(const Vector3& velocity); void SetEntity(Entity& ent); Entity GetEntity() const; - void MoveAndSlide(const Vector3& velocity); - bool IsOnFloor() + bool IsOnFloor() const { return IsOnGround; } diff --git a/Nuake/src/Core/Physics/DynamicWorld.cpp b/Nuake/src/Core/Physics/DynamicWorld.cpp index 349243d5..7ee93c9f 100644 --- a/Nuake/src/Core/Physics/DynamicWorld.cpp +++ b/Nuake/src/Core/Physics/DynamicWorld.cpp @@ -27,9 +27,10 @@ #include #include -#include +#include #include #include +#include #include @@ -245,7 +246,7 @@ namespace Nuake { DynamicWorld::DynamicWorld() : _stepCount(0) { - _registeredCharacters = std::map(); + _registeredCharacters = std::map(); // Initialize Jolt Physics const uint32_t MaxBodies = 2048; @@ -329,26 +330,21 @@ namespace Nuake void DynamicWorld::AddCharacterController(Ref cc) { - JPH::Ref settings = new JPH::CharacterSettings(); + JPH::Ref settings = new JPH::CharacterVirtualSettings(); settings->mMaxSlopeAngle = JPH::DegreesToRadians(cc->MaxSlopeAngle); - settings->mLayer = Layers::MOVING; - settings->mFriction = cc->Friction; + settings->mMaxStrength = 1.0f; + settings->mCharacterPadding = 0.05f; + settings->mPenetrationRecoverySpeed = 1.0f; + settings->mPredictiveContactDistance = 0.01f; settings->mShape = GetJoltShape(cc->Shape); - settings->mGravityFactor = 0.0f; - settings->mSupportingVolume = JPH::Plane(JPH::Vec3::sAxisY(), -0.5f); auto& joltPosition = JPH::Vec3(cc->Position.x, cc->Position.y, cc->Position.z); - Quat& bodyRotation = cc->Rotation; - - // We need to add 180 degrees because our forward is -Z. - const auto& yOffset = Vector3(0.0f, Rad(180.0), 0.0f); - //bodyRotation = glm::normalize(bodyRotation * Quat(yOffset)); - + const Quat& bodyRotation = cc->Rotation; const auto& joltRotation = JPH::Quat(bodyRotation.x, bodyRotation.y, bodyRotation.z, bodyRotation.w); - JPH::Character* character = new JPH::Character(settings, joltPosition, joltRotation, cc->GetEntity().GetID() , _JoltPhysicsSystem.get()); + auto character = new JPH::CharacterVirtual(settings, std::move(joltPosition), std::move(joltRotation), _JoltPhysicsSystem.get()); - character->AddToPhysicsSystem(JPH::EActivation::Activate); + //character->AddToPhysicsSystem(JPH::EActivation::Activate); // To get the jolt character control from a scene entity. _registeredCharacters[cc->Owner.GetHandle()] = character; @@ -452,7 +448,7 @@ namespace Nuake { Entity entity { (entt::entity)e.first, Engine::GetCurrentScene().get()}; - JPH::Character* characterController = e.second; + JPH::CharacterVirtual* characterController = e.second; JPH::Mat44 joltTransform = characterController->GetWorldTransform(); const auto bodyRotation = characterController->GetRotation(); @@ -506,19 +502,50 @@ namespace Nuake // Step the world try { - _JoltPhysicsSystem->Update(ts, collisionSteps, new JPH::TempAllocatorMalloc(), _JoltJobSystem); + // TODO: Potential memory leak with new keyword. + auto joltTempAllocator = CreateRef(); + + JPH::CharacterVirtual::ExtendedUpdateSettings joltUpdateSettings; for (auto& c : _registeredCharacters) { - c.second->PostSimulation(0.05f); + //c.second->PostSimulation(0.05f); + Entity entity{ (entt::entity)c.first, Engine::GetCurrentScene().get() }; + + if (entity.HasComponent()) + { + auto& characterControllerComponent = entity.GetComponent(); + auto characterController = characterControllerComponent.GetCharacterController(); + + const auto& broadPhaseLayerFilter = _JoltPhysicsSystem->GetDefaultBroadPhaseLayerFilter(Layers::MOVING); + const auto& LayerFilter = _JoltPhysicsSystem->GetDefaultLayerFilter(Layers::MOVING); + const auto& joltGravity = _JoltPhysicsSystem->GetGravity(); + auto& tempAllocatorPtr = *(joltTempAllocator); + if (characterController->AutoStepping) + { + // Create update settings from character controller + joltUpdateSettings.mStickToFloorStepDown = CreateJoltVec3(characterController->StepDown); + joltUpdateSettings.mWalkStairsStepDownExtra = CreateJoltVec3(characterController->StepDownExtra); + joltUpdateSettings.mWalkStairsStepUp = CreateJoltVec3(characterController->StepUp); + joltUpdateSettings.mWalkStairsStepForwardTest = characterController->StepDistance; + joltUpdateSettings.mWalkStairsMinStepForward = characterController->StepMinDistance; + + c.second->ExtendedUpdate(ts, joltGravity, joltUpdateSettings, broadPhaseLayerFilter, LayerFilter, { }, { }, tempAllocatorPtr); + } + else + { + c.second->Update(ts, joltGravity, broadPhaseLayerFilter, LayerFilter, {}, {}, tempAllocatorPtr); + } + } } + + _JoltPhysicsSystem->Update(ts, collisionSteps, joltTempAllocator.get(), _JoltJobSystem); } catch (...) { Logger::Log("Failed to run simulation update", "physics", CRITICAL); } - SyncEntitiesTranforms(); SyncCharactersTransforms(); } @@ -538,7 +565,7 @@ namespace Nuake { for (auto& character : _registeredCharacters) { - character.second->RemoveFromPhysicsSystem(); + //character.second->RemoveFromPhysicsSystem(); } _registeredCharacters.clear(); diff --git a/Nuake/src/Core/Physics/DynamicWorld.h b/Nuake/src/Core/Physics/DynamicWorld.h index 0c47bee7..21986f6f 100644 --- a/Nuake/src/Core/Physics/DynamicWorld.h +++ b/Nuake/src/Core/Physics/DynamicWorld.h @@ -12,6 +12,7 @@ #include + namespace JPH { class PhysicsSystem; @@ -20,20 +21,19 @@ namespace JPH class BodyActivationListener; class BodyInterface; class Shape; - class Character; + class CharacterVirtual; template class Ref; } - namespace Nuake { class BPLayerInterfaceImpl; class MyContactListener; class MyBodyActivationListener; - namespace Physics + namespace Physics { class DynamicWorld { @@ -48,7 +48,7 @@ namespace Nuake BPLayerInterfaceImpl* _JoltBroadphaseLayerInterface; std::vector _registeredBodies; - std::map _registeredCharacters; + std::map _registeredCharacters; public: DynamicWorld(); @@ -73,6 +73,11 @@ namespace Nuake JPH::Ref GetJoltShape(const Ref shape); void SyncEntitiesTranforms(); void SyncCharactersTransforms(); + + JPH::Vec3 CreateJoltVec3(const Vector3& input) const + { + return JPH::Vec3(input.x, input.y, input.z); + } }; } } diff --git a/Nuake/src/Resource/Serializable.h b/Nuake/src/Resource/Serializable.h index 8402aaa4..17a22834 100644 --- a/Nuake/src/Resource/Serializable.h +++ b/Nuake/src/Resource/Serializable.h @@ -19,6 +19,12 @@ using json = nlohmann::json; SERIALIZE_VEC3(v) \ j[#v]["w"] = this->v.w; +#define DESERIALIZE_VAL(p) \ +if(j.contains(#p)) \ +{ \ +p = j[#p]; \ +} + #define DESERIALIZE_VEC4(v, p) \ p = Vector4(v["x"], v["y"], v["z"], v["w"]); diff --git a/Nuake/src/Scene/Components/CharacterControllerComponent.h b/Nuake/src/Scene/Components/CharacterControllerComponent.h index b8e2ede6..828ac891 100644 --- a/Nuake/src/Scene/Components/CharacterControllerComponent.h +++ b/Nuake/src/Scene/Components/CharacterControllerComponent.h @@ -1,40 +1,83 @@ #pragma once #include "src/Core/Physics/CharacterController.h" -namespace Nuake { +namespace Nuake +{ class CharacterControllerComponent { - public: - Ref CharacterController; + private: + Ref m_CharacterController; + public: + // Auto stepping settings + bool AutoStepping = false; + Vector3 StickToFloorStepDown = Vector3(0.f, -0.5f, 0.f); + Vector3 StepDownExtra = Vector3(0, 0, 0); // ?? + Vector3 SteppingStepUp = Vector3(0.f, 0.4f, 0.f); + float SteppingMinDistance = 0.125f; + float SteppingForwardDistance = 0.250f; + float Friction = 0.5f; - float MaxSlopeAngle = 0.45f; + float MaxSlopeAngle = 45.0f; + float MaxStrength = 1.0f; CharacterControllerComponent() { + } + void SetCharacterController(const Ref& charController) + { + m_CharacterController = charController; + } + + Ref GetCharacterController() const + { + return m_CharacterController; } json Serialize() { BEGIN_SERIALIZE(); + SERIALIZE_VAL(AutoStepping); + SERIALIZE_VEC3(StickToFloorStepDown); + SERIALIZE_VEC3(StepDownExtra); + SERIALIZE_VEC3(SteppingStepUp); + SERIALIZE_VAL(SteppingMinDistance); + SERIALIZE_VAL(SteppingForwardDistance); + SERIALIZE_VAL(Friction); SERIALIZE_VAL(MaxSlopeAngle); + SERIALIZE_VAL(MaxStrength); END_SERIALIZE(); } bool Deserialize(const json& j) { - if (j.contains("Friction")) - { - Friction = j["Friction"]; - } + DESERIALIZE_VAL(AutoStepping); + DESERIALIZE_VAL(Friction); + DESERIALIZE_VAL(MaxSlopeAngle); - if (j.contains("MaxSlopeAngle")) + if (j.contains("StickToFloorStepDown")) { - MaxSlopeAngle = j["MaxSlopeAngle"]; + DESERIALIZE_VEC3(j["StickToFloorStepDown"], StickToFloorStepDown); } + if (j.contains("StepDownExtra")) + { + DESERIALIZE_VEC3(j["StepDownExtra"], StepDownExtra); + } + + if (j.contains("SteppingStepUp")) + { + DESERIALIZE_VEC3(j["SteppingStepUp"], SteppingStepUp); + } + + DESERIALIZE_VAL(SteppingMinDistance); + DESERIALIZE_VAL(SteppingForwardDistance); + + DESERIALIZE_VAL(Friction); + DESERIALIZE_VAL(MaxSlopeAngle); + DESERIALIZE_VAL(MaxStrength); return true; } }; diff --git a/Nuake/src/Scene/Scene.cpp b/Nuake/src/Scene/Scene.cpp index cad0331e..58750a01 100644 --- a/Nuake/src/Scene/Scene.cpp +++ b/Nuake/src/Scene/Scene.cpp @@ -45,9 +45,9 @@ namespace Nuake m_Environement = CreateRef(); // Adding systems - Order is important + m_Systems.push_back(CreateRef(this)); m_Systems.push_back(CreateRef(this)); m_Systems.push_back(CreateRef(this)); - m_Systems.push_back(CreateRef(this)); m_Systems.push_back(CreateRef(this)); m_SceneRenderer = new SceneRenderer(); @@ -105,7 +105,6 @@ namespace Nuake { for (auto& system : m_Systems) { - Logger::Log("Init system"); if (!system->Init()) { return false; diff --git a/Nuake/src/Scene/Systems/PhysicsSystem.cpp b/Nuake/src/Scene/Systems/PhysicsSystem.cpp index 40ad1d35..e324500b 100644 --- a/Nuake/src/Scene/Systems/PhysicsSystem.cpp +++ b/Nuake/src/Scene/Systems/PhysicsSystem.cpp @@ -315,23 +315,37 @@ namespace Nuake Entity entity = Entity({ e, m_Scene }); auto [transformComponent, characterControllerComponent] = characterControllerView.get(e); + Ref shape; if (entity.HasComponent()) { const auto& capsuleColliderComponent = entity.GetComponent(); - auto& capsule = capsuleColliderComponent.Capsule; - - const float friction = characterControllerComponent.Friction; - const float maxSlopeAngle = characterControllerComponent.MaxSlopeAngle; - auto characterController = CreateRef(capsule, friction, maxSlopeAngle); - characterController->SetEntity(entity); // Used to link back to the entity - characterController->Position = transformComponent.GetGlobalPosition(); - characterController->Rotation = transformComponent.GetGlobalRotation(); - - characterControllerComponent.CharacterController = characterController; - PhysicsManager::Get().RegisterCharacterController(characterControllerComponent.CharacterController); + shape = capsuleColliderComponent.Capsule; + } + else if (entity.HasComponent()) + { + const auto& capsuleColliderComponent = entity.GetComponent(); + shape = capsuleColliderComponent.Box; } - // TODO: Other types of collider supported for character controller? + Physics::CharacterControllerSettings settings + { + shape, + characterControllerComponent.Friction, + characterControllerComponent.MaxSlopeAngle, + characterControllerComponent.AutoStepping, + characterControllerComponent.StickToFloorStepDown, + characterControllerComponent.StepDownExtra, + characterControllerComponent.SteppingStepUp, + characterControllerComponent.SteppingMinDistance, + characterControllerComponent.SteppingForwardDistance + }; + + auto characterController = CreateRef(std::move(settings)); + characterController->SetEntity(entity); // Used to link back to the entity + characterController->Position = transformComponent.GetGlobalPosition(); + characterController->Rotation = transformComponent.GetGlobalRotation(); + characterControllerComponent.SetCharacterController(characterController); + PhysicsManager::Get().RegisterCharacterController(characterController); } } diff --git a/Nuake/src/Scripting/Modules/SceneModule.h b/Nuake/src/Scripting/Modules/SceneModule.h index 0a9cae0e..61e2e75b 100644 --- a/Nuake/src/Scripting/Modules/SceneModule.h +++ b/Nuake/src/Scripting/Modules/SceneModule.h @@ -42,6 +42,7 @@ namespace Nuake RegisterMethod("EntityHasComponent(_,_)", (void*)EntityHasComponent); RegisterMethod("GetTranslation_(_)", (void*)GetTranslation); + RegisterMethod("GetLocalTranslation_(_)", (void*)GetLocalTranslation); RegisterMethod("SetTranslation_(_,_,_,_)", (void*)SetTranslation); RegisterMethod("GetRotation_(_)", (void*)GetRotation); RegisterMethod("SetRotation_(_,_,_,_)", (void*)SetRotation); @@ -290,7 +291,7 @@ namespace Nuake Entity ent = Entity((entt::entity)handle, Engine::GetCurrentScene().get()); auto& characterController = ent.GetComponent(); - characterController.CharacterController->MoveAndSlide(Vector3(x, y, z)); + characterController.GetCharacterController()->MoveAndSlide(Vector3(x, y, z)); } static void AddForce(WrenVM* vm) @@ -323,6 +324,24 @@ namespace Nuake wrenInsertInList(vm, 0, 2, 3); } + static void GetLocalTranslation(WrenVM* vm) + { + double handle = wrenGetSlotDouble(vm, 1); + Entity ent = Entity((entt::entity)handle, Engine::GetCurrentScene().get()); + auto& transform = ent.GetComponent(); + // set the slots + Vector3 position = transform.GetLocalPosition(); + wrenSetSlotDouble(vm, 1, position.x); + wrenSetSlotDouble(vm, 2, position.y); + wrenSetSlotDouble(vm, 3, position.z); + + // Fill the list + wrenSetSlotNewList(vm, 0); + wrenInsertInList(vm, 0, 0, 1); + wrenInsertInList(vm, 0, 1, 2); + wrenInsertInList(vm, 0, 2, 3); + } + static void GetRotation(WrenVM* vm) { double handle = wrenGetSlotDouble(vm, 1);