Added better character auto stepping and component settings

This commit is contained in:
Antoine Pilote
2023-08-12 16:12:39 -04:00
parent 1d7f9e007f
commit 691f46e0d6
10 changed files with 236 additions and 70 deletions

View File

@@ -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)
}

View File

@@ -12,6 +12,7 @@ public:
{
if (!entity.HasComponent<Nuake::CharacterControllerComponent>())
return;
using namespace Nuake;
auto& component = entity.GetComponent<Nuake::CharacterControllerComponent>();
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()
}

View File

@@ -8,11 +8,17 @@ namespace Nuake
{
namespace Physics
{
CharacterController::CharacterController(const Ref<PhysicShape>& 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)

View File

@@ -10,35 +10,49 @@ namespace Nuake
namespace Physics
{
struct CharacterControllerSettings
{
Ref<Physics::PhysicShape> 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<PhysicShape> 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<Vector3> m_surfaceHitNormals;
public:
CharacterController(const CharacterControllerSettings& settings);
CharacterController(const Ref<PhysicShape>& 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;
}

View File

@@ -331,29 +331,18 @@ namespace Nuake
void DynamicWorld::AddCharacterController(Ref<CharacterController> cc)
{
JPH::Ref<JPH::CharacterVirtualSettings> settings = new JPH::CharacterVirtualSettings();
//settings->mMaxSlopeAngle = JPH::DegreesToRadians(cc->MaxSlopeAngle);
//settings->mLayer = Layers::MOVING;
//settings->mFriction = cc->Friction;
//settings->mShape = GetJoltShape(cc->Shape);
//settings->mGravityFactor = 0.0f;
settings->mSupportingVolume = JPH::Plane(JPH::Vec3::sAxisY(), -0.5f);
settings->mMaxSlopeAngle = JPH::DegreesToRadians(cc->MaxSlopeAngle);
settings->mMaxStrength = 10.0;
settings->mCharacterPadding = 0.01f;
settings->mMaxStrength = 1.0f;
settings->mCharacterPadding = 0.05f;
settings->mPenetrationRecoverySpeed = 1.0f;
settings->mPredictiveContactDistance = 0.01f;
settings->mShape = GetJoltShape(cc->Shape);
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::CharacterVirtual* character = new JPH::CharacterVirtual(settings, joltPosition, joltRotation, _JoltPhysicsSystem.get());
auto character = new JPH::CharacterVirtual(settings, std::move(joltPosition), std::move(joltRotation), _JoltPhysicsSystem.get());
//character->AddToPhysicsSystem(JPH::EActivation::Activate);
@@ -514,36 +503,49 @@ namespace Nuake
try
{
// TODO: Potential memory leak with new keyword.
auto joltTempAllocator = CreateRef<JPH::TempAllocatorMalloc>();
JPH::CharacterVirtual::ExtendedUpdateSettings update_settings;
update_settings.mStickToFloorStepDown = -JPH::Vec3(0, 1, 0) * update_settings.mStickToFloorStepDown.Length();
update_settings.mWalkStairsStepUp = JPH::Vec3(0, 1, 0) * update_settings.mWalkStairsStepUp.Length();
update_settings.mWalkStairsMinStepForward = 0.10;
update_settings.mWalkStairsStepForwardTest = 0.15f;
JPH::CharacterVirtual::ExtendedUpdateSettings joltUpdateSettings;
for (auto& c : _registeredCharacters)
{
//c.second->PostSimulation(0.05f);
Entity entity{ (entt::entity)c.first, Engine::GetCurrentScene().get() };
c.second->ExtendedUpdate(ts,
-JPH::Vec3(0, 0, 0) * _JoltPhysicsSystem->GetGravity().Length(),
update_settings,
_JoltPhysicsSystem->GetDefaultBroadPhaseLayerFilter(Layers::MOVING),
_JoltPhysicsSystem->GetDefaultLayerFilter(Layers::MOVING),
{ },
{ },
*(new JPH::TempAllocatorMalloc()));
if (entity.HasComponent<CharacterControllerComponent>())
{
auto& characterControllerComponent = entity.GetComponent<CharacterControllerComponent>();
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, new JPH::TempAllocatorMalloc(), _JoltJobSystem);
_JoltPhysicsSystem->Update(ts, collisionSteps, joltTempAllocator.get(), _JoltJobSystem);
}
catch (...)
{
Logger::Log("Failed to run simulation update", "physics", CRITICAL);
}
SyncEntitiesTranforms();
SyncCharactersTransforms();
}

View File

@@ -12,6 +12,7 @@
#include <Jolt/Jolt.h>
namespace JPH
{
class PhysicsSystem;
@@ -26,14 +27,13 @@ namespace JPH
class Ref;
}
namespace Nuake
{
class BPLayerInterfaceImpl;
class MyContactListener;
class MyBodyActivationListener;
namespace Physics
namespace Physics
{
class DynamicWorld
{
@@ -73,6 +73,11 @@ namespace Nuake
JPH::Ref<JPH::Shape> GetJoltShape(const Ref<PhysicShape> shape);
void SyncEntitiesTranforms();
void SyncCharactersTransforms();
JPH::Vec3 CreateJoltVec3(const Vector3& input) const
{
return JPH::Vec3(input.x, input.y, input.z);
}
};
}
}

View File

@@ -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"]);

View File

@@ -1,40 +1,83 @@
#pragma once
#include "src/Core/Physics/CharacterController.h"
namespace Nuake {
namespace Nuake
{
class CharacterControllerComponent
{
public:
Ref<Physics::CharacterController> CharacterController;
private:
Ref<Physics::CharacterController> 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<Physics::CharacterController>& charController)
{
m_CharacterController = charController;
}
Ref<Physics::CharacterController> 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;
}
};

View File

@@ -327,15 +327,25 @@ namespace Nuake
shape = capsuleColliderComponent.Box;
}
const float friction = characterControllerComponent.Friction;
const float maxSlopeAngle = characterControllerComponent.MaxSlopeAngle;
auto characterController = CreateRef<Physics::CharacterController>(shape, friction, maxSlopeAngle);
Physics::CharacterControllerSettings settings
{
shape,
characterControllerComponent.Friction,
characterControllerComponent.MaxSlopeAngle,
characterControllerComponent.AutoStepping,
characterControllerComponent.StickToFloorStepDown,
characterControllerComponent.StepDownExtra,
characterControllerComponent.SteppingStepUp,
characterControllerComponent.SteppingMinDistance,
characterControllerComponent.SteppingForwardDistance
};
auto characterController = CreateRef<Physics::CharacterController>(std::move(settings));
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);
characterControllerComponent.SetCharacterController(characterController);
PhysicsManager::Get().RegisterCharacterController(characterController);
}
}

View File

@@ -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<CharacterControllerComponent>();
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<TransformComponent>();
// 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);