Character controller now moving! 🏃‍♂️

- Added character controller transform syncing Jolt -> Scene
- Physic system code cleanup
- Fixed crash with WrenScript loading
- Fixed Normalize() returning NaN vector when lenght is 0
This commit is contained in:
Antoine Pilote
2023-07-04 03:09:52 -04:00
parent 95ffbdb6a9
commit 0d108bc44a
11 changed files with 134 additions and 36 deletions

View File

@@ -108,10 +108,14 @@ class Vector3 {
Normalize() {
var length = this.Length()
var x = _x / length
var y = _y / length
var z = _z / length
return Vector3.new(x, y, z)
if(length > 0.0) {
var x = _x / length
var y = _y / length
var z = _z / length
return Vector3.new(x, y, z)
}
return Vector3.new(0, 0, 0)
}
Angle(vec) {

View File

@@ -11,6 +11,7 @@
* Scripting API for editing UI maybe
* Launch game standalone
* Fix physics system
* --play argument rendering in framebuffer
*/
// Welcome to the Nuake source code.

View File

@@ -2,6 +2,8 @@
#include "src/Core/Physics/PhysicsManager.h"
#include "src/Core/Physics/RaycastResult.h"
#include "src/Core/Physics/PhysicsManager.h"
namespace Nuake
{
namespace Physics
@@ -23,12 +25,9 @@ namespace Nuake
return Owner;
}
void CharacterController::MoveAndSlide(glm::vec3 velocity)
void CharacterController::MoveAndSlide(const Vector3& velocity)
{
IsOnGround = false;
ParseGhostContacts();
UpdatePosition();
UpdateVelocity();
PhysicsManager::Get().GetWorld()->MoveAndSlideCharacterController(Owner, velocity);
}
void CharacterController::ParseGhostContacts()
@@ -44,8 +43,6 @@ namespace Nuake
void CharacterController::UpdateVelocity()
{
// Decelerate
//m_manualVelocity -= m_manualVelocity * m_deceleration * m_pPhysicsWorld->GetScene()->m_frameTimer.GetTimeMultiplier();

View File

@@ -37,7 +37,7 @@ namespace Nuake
void SetEntity(Entity& ent);
Entity GetEntity() const;
void MoveAndSlide(glm::vec3 velocity);
void MoveAndSlide(const Vector3& velocity);
bool IsOnFloor()
{

View File

@@ -4,6 +4,8 @@
#include "src/Core/Core.h"
#include "src/Core/Logger.h"
#include <src/Core/Physics/PhysicsShapes.h>
#include "src/Scene/Components/TransformComponent.h"
#include "src/Scene/Components/CharacterControllerComponent.h"
#include <src/Vendors/glm/ext/quaternion_common.hpp>
#include "src/Vendors/glm/gtx/matrix_decompose.hpp"
@@ -198,6 +200,9 @@ namespace Nuake
{
DynamicWorld::DynamicWorld() : _stepCount(0)
{
_registeredCharacters = std::map<uint32_t, JPH::Character*>();
// Initialize Jolt Physics
const uint32_t MaxBodies = 1024;
const uint32_t NumBodyMutexes = 0;
const uint32_t MaxBodyPairs = 1024;
@@ -265,8 +270,8 @@ namespace Nuake
bodySettings.mUserData = rb->GetEntity().GetID();
// Create the actual rigid body
JPH::BodyID floor = _JoltBodyInterface->CreateAndAddBody(bodySettings, JPH::EActivation::Activate); // Note that if we run out of bodies this can return nullptr
_registeredBodies.push_back((uint32_t)floor.GetIndexAndSequenceNumber());
JPH::BodyID body = _JoltBodyInterface->CreateAndAddBody(bodySettings, JPH::EActivation::Activate); // Note that if we run out of bodies this can return nullptr
_registeredBodies.push_back((uint32_t)body.GetIndexAndSequenceNumber());
}
void DynamicWorld::AddGhostbody(Ref<GhostObject> gb)
@@ -286,6 +291,9 @@ namespace Nuake
JPH::Character* character = new JPH::Character(settings, joltPos, JPH::Quat::sIdentity(), cc->GetEntity().GetID() , _JoltPhysicsSystem.get());
character->AddToPhysicsSystem(JPH::EActivation::Activate);
// To get the jolt character control from a scene entity.
_registeredCharacters[cc->Owner.GetHandle()] = character;
}
RaycastResult DynamicWorld::Raycast(glm::vec3 from, glm::vec3 to)
@@ -304,12 +312,9 @@ namespace Nuake
return result;
}
void DynamicWorld::StepSimulation(Timestep ts)
void DynamicWorld::SyncEntitiesTranforms()
{
// Next step
++_stepCount;
const auto& bodyInterface = _JoltPhysicsSystem->GetBodyInterface();
for (const auto& body : _registeredBodies)
{
auto bodyId = static_cast<JPH::BodyID>(body);
@@ -339,14 +344,52 @@ namespace Nuake
transformComponent.SetLocalRotation(Quat(bodyRotation.GetW(), bodyRotation.GetX(), bodyRotation.GetY(), bodyRotation.GetZ()));
transformComponent.SetLocalTransform(transform);
transformComponent.Dirty = false;
/* Logging
const std::string& name = entity.GetComponent<NameComponent>().Name;
const std::string& posStr = "(" + std::to_string(pos.x) + ", " + std::to_string(pos.y) + ", " + std::to_string(pos.z) + ")";
const std::string& logMsg = "Physics pos: " + name + " at " + posStr;
Logger::Log(logMsg);
*/
}
}
void DynamicWorld::SyncCharactersTransforms()
{
// TODO(ANTO): Finish this to connect updated jolt transforms back to the entity.
// The problem was that I dont know yet how to go from jolt body ptr to the entity
// Combinations of find and iterators etc. I do not have the brain power rn zzz.
// const auto& bodyInterface = _JoltPhysicsSystem->GetBodyInterface();
for (const auto& e : _registeredCharacters)
{
Entity entity { (entt::entity)e.first, Engine::GetCurrentScene().get()};
JPH::Character* characterController = e.second;
JPH::Mat44 joltTransform = characterController->GetWorldTransform();
const auto bodyRotation = characterController->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& transformComponent = entity.GetComponent<TransformComponent>();
transformComponent.SetLocalPosition(pos);
transformComponent.SetLocalRotation(Quat(bodyRotation.GetW(), bodyRotation.GetX(), bodyRotation.GetY(), bodyRotation.GetZ()));
transformComponent.SetLocalTransform(transform);
transformComponent.Dirty = false;
}
}
void DynamicWorld::StepSimulation(Timestep ts)
{
// Next step
++_stepCount;
SyncEntitiesTranforms();
SyncCharactersTransforms();
// If you take larger steps than 1 / 60th of a second you need to do multiple collision steps in order to keep the simulation stable.
// Do 1 collision step per 1 / 60th of a second (round up).
@@ -364,6 +407,7 @@ namespace Nuake
_JoltPhysicsSystem->Update(ts, collisionSteps, subSteps, new JPH::TempAllocatorMalloc(), _JoltJobSystem);
}
void DynamicWorld::Clear()
{
_stepCount = 0;
@@ -377,6 +421,54 @@ namespace Nuake
_registeredBodies.clear();
}
void DynamicWorld::MoveAndSlideCharacterController(const Entity& entity, const Vector3 velocity)
{
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));
}
}
const Matrix4& DynamicWorld::GetCharacterControllerSimulatedTransform(const Entity& entity)
{
const uint32_t entityHandle = entity.GetHandle();
if (_registeredCharacters.find(entityHandle) != _registeredCharacters.end())
{
auto& characterController = _registeredCharacters[entityHandle];
JPH::Mat44 joltTransform = characterController->GetWorldTransform();
const auto bodyRotation = characterController->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)
);
return transform;
//Vector3 scale = Vector3();
//Quat rotation = Quat();
//Vector3 pos = Vector3();
//Vector3 skew = Vector3();
//Vector4 pesp = Vector4();
//glm::decompose(transform, scale, rotation, pos, skew, pesp);
//auto& transformComponent = entity.GetComponent<TransformComponent>();
//transformComponent.SetLocalPosition(pos);
//transformComponent.SetLocalRotation(Quat(bodyRotation.GetW(), bodyRotation.GetX(), bodyRotation.GetY(), bodyRotation.GetZ()));
//transformComponent.SetLocalTransform(transform);
//transformComponent.Dirty = false;
}
assert("Shouldn't have reached here!");
return Matrix4(1.0);
}
JPH::Ref<JPH::Shape> DynamicWorld::GetJoltShape(const Ref<PhysicShape> shape)
{
JPH::ShapeSettings::ShapeResult result;
@@ -426,8 +518,8 @@ namespace Nuake
JPH::TriangleList triangles;
triangles.reserve(indices.size());
Matrix4 transform; //rb->_transform;
transform[3] = Vector4(0, 0, 0, 1.0f);
auto transform = Matrix4(1.0f);
transform[3] = Vector4(0.0f, 0.0f, 0.0f, 1.0f);
for (int i = 0; i < indices.size() - 3; i += 3)
{
const Vector3& p1 = vertices[indices[i]].position;
@@ -448,7 +540,6 @@ namespace Nuake
case CONVEX_HULL:
{
auto* convexHullShape = (Physics::ConvexHullShape*)shape.get();
const auto& hullPoints = convexHullShape->GetPoints();
JPH::Array<JPH::Vec3> points;
points.reserve(std::size(hullPoints));

View File

@@ -19,6 +19,7 @@ namespace JPH
class BodyActivationListener;
class BodyInterface;
class Shape;
class Character;
template<class T>
class Ref;
@@ -45,7 +46,7 @@ namespace Nuake
BPLayerInterfaceImpl* _JoltBroadphaseLayerInterface;
std::vector<uint32_t> _registeredBodies;
std::vector<uint32_t> _registeredCharacters;
std::map<uint32_t, JPH::Character*> _registeredCharacters;
public:
DynamicWorld();
@@ -57,13 +58,18 @@ namespace Nuake
void AddGhostbody(Ref<GhostObject> gb);
void AddCharacterController(Ref<CharacterController> cc);
// This is going to be ugly. TODO: Find a better way that passing itself as a parameter
void MoveAndSlideCharacterController(const Entity& entity, const Vector3 velocity);
const Matrix4& GetCharacterControllerSimulatedTransform(const Entity& entity);
RaycastResult Raycast(glm::vec3 from, glm::vec3 to);
void StepSimulation(Timestep ts);
void Clear();
private:
JPH::Ref<JPH::Shape> GetJoltShape(const Ref<PhysicShape> shape);
void SyncEntitiesTranforms();
void SyncCharactersTransforms();
};
}
}

View File

@@ -20,7 +20,6 @@ namespace Nuake
void PhysicsManager::RegisterGhostBody(Ref<GhostObject> rb)
{
m_World->AddGhostbody(rb);
}
void PhysicsManager::RegisterCharacterController(Ref<Physics::CharacterController> cc)

View File

@@ -9,11 +9,12 @@ namespace Nuake {
std::string Script;
unsigned int mModule = 0;
Ref<WrenScript> mWrenScript;
Ref<WrenScript> mWrenScript = nullptr;
void LoadScript(const std::string& path)
{
Ref<Nuake::File> nuakeFile = Nuake::FileSystem::GetFile(Script);
Script = path;
Ref<Nuake::File> nuakeFile = Nuake::FileSystem::GetFile(path);
mWrenScript = CreateRef<Nuake::WrenScript>(nuakeFile, true);
auto modules = mWrenScript->GetModules();
if (modules.size() > 0)

View File

@@ -17,7 +17,7 @@ namespace Nuake
void AddChild(Entity ent);
int GetHandle() { return (int)m_EntityHandle; }
int GetHandle() const { return (int)m_EntityHandle; }
int GetID() { return GetComponent<NameComponent>().ID; }
template<typename T>

View File

@@ -108,7 +108,7 @@ namespace Nuake {
wrenGetSlotDouble(vm, 3));
float length = glm::length(v1);
wrenSetSlotDouble(vm, 0, glm::length(v1));
wrenSetSlotDouble(vm, 0, length);
}
};
}

View File

@@ -77,7 +77,6 @@ namespace Nuake {
if (name == "Transform") result = ent.HasComponent<TransformComponent>();
if (name == "Light") result = ent.HasComponent<LightComponent>();
if (name == "QuakeMap") result = ent.HasComponent<QuakeMapComponent>();
if (name == "CharacterController")
{
result = ent.HasComponent<CharacterControllerComponent>();