Compare commits

...

10 Commits

25 changed files with 458 additions and 80 deletions

View File

@ -456,13 +456,13 @@ int main(int argc, char* argv[])
if (lastHitInfo.hitBody && lastHitInfo.hitEntity != boxColliderEntity)
{
#if USE_JOLT
grabConstraint.emplace(*lastHitInfo.hitBody, lastHitInfo.hitPosition);
grabConstraint.emplace(static_cast<Nz::JoltRigidBody3D&>(*lastHitInfo.hitBody), lastHitInfo.hitPosition);
#else
grabConstraint.emplace(*lastHitInfo.hitBody, lastHitInfo.hitPosition);
grabConstraint->SetImpulseClamp(30.f);
#endif
grabbedObjectMove.Connect(eventHandler.OnMouseMoved, [&, body = lastHitInfo.hitBody, distance = Nz::Vector3f::Distance(from, lastHitInfo.hitPosition)](const Nz::WindowEventHandler*, const Nz::WindowEvent::MouseMoveEvent& event)
grabbedObjectMove.Connect(eventHandler.OnMouseMoved, [&, distance = Nz::Vector3f::Distance(from, lastHitInfo.hitPosition)](const Nz::WindowEventHandler*, const Nz::WindowEvent::MouseMoveEvent& event)
{
Nz::Vector3f from = cameraComponent.Unproject({ float(event.x), float(event.y), 0.f });
Nz::Vector3f to = cameraComponent.Unproject({ float(event.x), float(event.y), 1.f });

View File

@ -38,7 +38,7 @@ namespace Nz
static ErrorModeFlags ApplyFlags(ErrorModeFlags orFlags, ErrorModeFlags andFlags);
static constexpr std::string_view GetCurrentFileRelativeToEngine(std::string_view file);
static constexpr std::string_view TranslateFilepath(std::string_view file);
static ErrorModeFlags GetFlags();
static std::string GetLastError(std::string_view* file = nullptr, unsigned int* line = nullptr, std::string_view* function = nullptr);
static unsigned int GetLastSystemErrorCode();

View File

@ -4,13 +4,13 @@
namespace Nz
{
constexpr std::string_view Error::GetCurrentFileRelativeToEngine(std::string_view file)
constexpr std::string_view Error::TranslateFilepath(std::string_view file)
{
if (std::size_t offset = file.find("NazaraEngine/"); offset != file.npos)
return file.substr(offset);
if (std::size_t offset = file.rfind('/'); offset != file.npos)
return file.substr(offset + 1);
if (std::size_t offset = file.find("NazaraEngine\\"); offset != file.npos)
return file.substr(offset);
if (std::size_t offset = file.rfind('\\'); offset != file.npos)
return file.substr(offset + 1);
return file;
}
@ -22,6 +22,6 @@ namespace Nz
inline void Error::Trigger(ErrorType type, unsigned int line, std::string_view file, std::string_view function, std::string error)
{
return TriggerInternal(type, std::move(error), line, GetCurrentFileRelativeToEngine(file), function);
return TriggerInternal(type, std::move(error), line, TranslateFilepath(file), function);
}
}

View File

@ -0,0 +1,32 @@
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - JoltPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_JOLTPHYSICS3D_JOLTABSTRACTBODY_HPP
#define NAZARA_JOLTPHYSICS3D_JOLTABSTRACTBODY_HPP
#include <NazaraUtils/Prerequisites.hpp>
#include <Nazara/JoltPhysics3D/Config.hpp>
namespace Nz
{
class NAZARA_JOLTPHYSICS3D_API JoltAbstractBody
{
public:
JoltAbstractBody() = default;
JoltAbstractBody(const JoltAbstractBody&) = delete;
JoltAbstractBody(JoltAbstractBody&&) = delete;
virtual ~JoltAbstractBody();
virtual UInt32 GetBodyIndex() const = 0;
JoltAbstractBody& operator=(const JoltAbstractBody&) = delete;
JoltAbstractBody& operator=(JoltAbstractBody&&) = delete;
};
}
#include <Nazara/JoltPhysics3D/JoltAbstractBody.inl>
#endif // NAZARA_JOLTPHYSICS3D_JOLTABSTRACTBODY_HPP

View File

@ -0,0 +1,12 @@
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - JoltPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/JoltPhysics3D/JoltAbstractBody.hpp>
#include <Nazara/JoltPhysics3D/Debug.hpp>
namespace Nz
{
}
#include <Nazara/JoltPhysics3D/DebugOff.hpp>

View File

@ -9,6 +9,7 @@
#include <NazaraUtils/Prerequisites.hpp>
#include <Nazara/JoltPhysics3D/Config.hpp>
#include <Nazara/JoltPhysics3D/JoltAbstractBody.hpp>
#include <Nazara/JoltPhysics3D/JoltPhysicsStepListener.hpp>
#include <Nazara/Math/Quaternion.hpp>
#include <Nazara/Math/Vector3.hpp>
@ -27,7 +28,7 @@ namespace Nz
class JoltCollider3D;
class JoltPhysWorld3D;
class NAZARA_JOLTPHYSICS3D_API JoltCharacter : public JoltPhysicsStepListener
class NAZARA_JOLTPHYSICS3D_API JoltCharacter : public JoltAbstractBody, public JoltPhysicsStepListener
{
friend JoltPhysWorld3D;
@ -42,11 +43,14 @@ namespace Nz
inline void DisableSleeping();
void EnableSleeping(bool enable);
inline UInt32 GetBodyIndex() const;
UInt32 GetBodyIndex() const override;
inline const std::shared_ptr<JoltCollider3D>& GetCollider() const;
Vector3f GetLinearVelocity() const;
Quaternionf GetRotation() const;
inline JoltPhysWorld3D& GetPhysWorld();
inline const JoltPhysWorld3D& GetPhysWorld() const;
Vector3f GetPosition() const;
std::pair<Vector3f, Quaternionf> GetPositionAndRotation() const;
Quaternionf GetRotation() const;
Vector3f GetUp() const;
bool IsOnGround() const;
@ -78,7 +82,7 @@ namespace Nz
void Destroy();
private:
void PostSimulate() override;
void PostSimulate(float elapsedTime) override;
void PreSimulate(float elapsedTime) override;
std::shared_ptr<JoltCharacterImpl> m_impl;
@ -96,7 +100,7 @@ namespace Nz
JoltCharacterImpl(JoltCharacterImpl&&) = delete;
virtual ~JoltCharacterImpl();
virtual void PostSimulate(JoltCharacter& character);
virtual void PostSimulate(JoltCharacter& character, float elapsedTime);
virtual void PreSimulate(JoltCharacter& character, float elapsedTime);
JoltCharacterImpl& operator=(const JoltCharacterImpl&) = delete;

View File

@ -11,9 +11,19 @@ namespace Nz
return EnableSleeping(false);
}
inline UInt32 JoltCharacter::GetBodyIndex() const
inline const std::shared_ptr<JoltCollider3D>& JoltCharacter::GetCollider() const
{
return m_bodyIndex;
return m_collider;
}
inline JoltPhysWorld3D& JoltCharacter::GetPhysWorld()
{
return *m_world;
}
inline const JoltPhysWorld3D& JoltCharacter::GetPhysWorld() const
{
return *m_world;
}
inline void JoltCharacter::SetImpl(std::shared_ptr<JoltCharacterImpl> characterImpl)

View File

@ -27,8 +27,10 @@ namespace JPH
namespace Nz
{
class JoltAbstractBody;
class JoltCharacter;
class JoltCharacterImpl;
class JoltCollider3D;
class JoltPhysicsStepListener;
class JoltRigidBody3D;
@ -38,13 +40,19 @@ namespace Nz
friend JoltRigidBody3D;
public:
struct PointCollisionInfo;
struct RaycastHit;
struct ShapeCollisionInfo;
JoltPhysWorld3D();
JoltPhysWorld3D(const JoltPhysWorld3D&) = delete;
JoltPhysWorld3D(JoltPhysWorld3D&& ph) = delete;
~JoltPhysWorld3D();
bool CollisionQuery(const Vector3f& point, const FunctionRef<std::optional<float>(const PointCollisionInfo& collisionInfo)>& callback);
bool CollisionQuery(const JoltCollider3D& collider, const Matrix4f& colliderTransform, const FunctionRef<std::optional<float>(const ShapeCollisionInfo& hitInfo)>& callback);
bool CollisionQuery(const JoltCollider3D& collider, const Matrix4f& colliderTransform, const Vector3f& colliderScale, const FunctionRef<std::optional<float>(const ShapeCollisionInfo& hitInfo)>& callback);
UInt32 GetActiveBodyCount() const;
Vector3f GetGravity() const;
std::size_t GetMaxStepCount() const;
@ -72,14 +80,28 @@ namespace Nz
JoltPhysWorld3D& operator=(const JoltPhysWorld3D&) = delete;
JoltPhysWorld3D& operator=(JoltPhysWorld3D&&) = delete;
struct PointCollisionInfo
{
JoltAbstractBody* hitBody = nullptr;
};
struct RaycastHit
{
float fraction;
JoltRigidBody3D* hitBody = nullptr;
JoltAbstractBody* hitBody = nullptr;
Vector3f hitNormal;
Vector3f hitPosition;
};
struct ShapeCollisionInfo
{
JoltAbstractBody* hitBody = nullptr;
Vector3f collisionPosition1;
Vector3f collisionPosition2;
Vector3f penetrationAxis;
float penetrationDepth;
};
private:
class BodyActivationListener;
friend BodyActivationListener;

View File

@ -20,7 +20,7 @@ namespace Nz
JoltPhysicsStepListener(JoltPhysicsStepListener&&) = delete;
virtual ~JoltPhysicsStepListener();
virtual void PostSimulate();
virtual void PostSimulate(float elapsedTime);
virtual void PreSimulate(float elapsedTime);
JoltPhysicsStepListener& operator=(const JoltPhysicsStepListener&) = delete;

View File

@ -10,6 +10,7 @@
#include <NazaraUtils/Prerequisites.hpp>
#include <Nazara/Core/Enums.hpp>
#include <Nazara/JoltPhysics3D/Config.hpp>
#include <Nazara/JoltPhysics3D/JoltAbstractBody.hpp>
#include <Nazara/JoltPhysics3D/JoltCollider3D.hpp>
#include <Nazara/Math/Matrix4.hpp>
#include <Nazara/Math/Quaternion.hpp>
@ -26,7 +27,7 @@ namespace Nz
{
class JoltPhysWorld3D;
class NAZARA_JOLTPHYSICS3D_API JoltRigidBody3D
class NAZARA_JOLTPHYSICS3D_API JoltRigidBody3D : public JoltAbstractBody
{
public:
struct DynamicSettings;
@ -54,7 +55,7 @@ namespace Nz
Vector3f GetAngularVelocity() const;
inline JPH::Body* GetBody();
inline const JPH::Body* GetBody() const;
inline UInt32 GetBodyIndex() const;
UInt32 GetBodyIndex() const override;
inline const std::shared_ptr<JoltCollider3D>& GetGeom() const;
float GetLinearDamping() const;
Vector3f GetLinearVelocity() const;

View File

@ -36,11 +36,6 @@ namespace Nz
return m_body;
}
inline UInt32 JoltRigidBody3D::GetBodyIndex() const
{
return m_bodyIndex;
}
inline const std::shared_ptr<JoltCollider3D>& JoltRigidBody3D::GetGeom() const
{
return m_geom;

View File

@ -25,13 +25,19 @@ namespace Nz
static constexpr Int64 ExecutionOrder = 0;
using Components = TypeList<JoltCharacterComponent, JoltRigidBody3DComponent, class NodeComponent>;
struct PointCollisionInfo;
struct RaycastHit;
struct ShapeCollisionInfo;
JoltPhysics3DSystem(entt::registry& registry);
JoltPhysics3DSystem(const JoltPhysics3DSystem&) = delete;
JoltPhysics3DSystem(JoltPhysics3DSystem&&) = delete;
~JoltPhysics3DSystem();
bool CollisionQuery(const Vector3f& point, const FunctionRef<std::optional<float>(const PointCollisionInfo& collisionInfo)>& callback);
bool CollisionQuery(const JoltCollider3D& collider, const Matrix4f& colliderTransform, const FunctionRef<std::optional<float>(const ShapeCollisionInfo& hitInfo)>& callback);
bool CollisionQuery(const JoltCollider3D& collider, const Matrix4f& colliderTransform, const Vector3f& colliderScale, const FunctionRef<std::optional<float>(const ShapeCollisionInfo& hitInfo)>& callback);
inline JoltPhysWorld3D& GetPhysWorld();
inline const JoltPhysWorld3D& GetPhysWorld() const;
inline entt::handle GetRigidBodyEntity(UInt32 bodyIndex) const;
@ -44,15 +50,26 @@ namespace Nz
JoltPhysics3DSystem& operator=(const JoltPhysics3DSystem&) = delete;
JoltPhysics3DSystem& operator=(JoltPhysics3DSystem&&) = delete;
struct PointCollisionInfo : JoltPhysWorld3D::PointCollisionInfo
{
entt::handle hitEntity;
};
struct RaycastHit : JoltPhysWorld3D::RaycastHit
{
entt::handle hitEntity;
};
struct ShapeCollisionInfo : JoltPhysWorld3D::ShapeCollisionInfo
{
entt::handle hitEntity;
};
private:
void OnBodyConstruct(entt::registry& registry, entt::entity entity);
void OnCharacterConstruct(entt::registry& registry, entt::entity entity);
void OnBodyDestruct(entt::registry& registry, entt::entity entity);
void OnCharacterConstruct(entt::registry& registry, entt::entity entity);
void OnCharacterDestruct(entt::registry& registry, entt::entity entity);
std::size_t m_stepCount;
std::vector<entt::entity> m_bodyIndicesToEntity;
@ -60,8 +77,9 @@ namespace Nz
entt::observer m_characterConstructObserver;
entt::observer m_rigidBodyConstructObserver;
entt::scoped_connection m_bodyConstructConnection;
entt::scoped_connection m_characterConstructConnection;
entt::scoped_connection m_bodyDestructConnection;
entt::scoped_connection m_characterConstructConnection;
entt::scoped_connection m_characterDestructConnection;
JoltPhysWorld3D m_physWorld;
};
}

View File

@ -8,15 +8,14 @@
#define NAZARA_MATH_QUATERNION_HPP
#include <Nazara/Math/Angle.hpp>
#include <Nazara/Math/EulerAngles.hpp>
#include <Nazara/Math/Vector3.hpp>
#include <string>
namespace Nz
{
struct SerializationContext;
template<typename T> class EulerAngles;
template<typename T> class Vector3;
template<typename T> class Quaternion
{
public:
@ -31,6 +30,7 @@ namespace Nz
constexpr Quaternion(Quaternion&&) = default;
~Quaternion() = default;
RadianAngle<T> AngleBetween(const Quaternion& vec) const;
constexpr bool ApproxEqual(const Quaternion& quat, T maxDifference = std::numeric_limits<T>::epsilon()) const;
Quaternion& ComputeW();
@ -75,12 +75,14 @@ namespace Nz
constexpr bool operator>(const Quaternion& quat) const;
constexpr bool operator>=(const Quaternion& quat) const;
static RadianAngle<T> AngleBetween(const Quaternion& lhs, const Quaternion& rhs);
static constexpr bool ApproxEqual(const Quaternion& lhs, const Quaternion& rhs, T maxDifference = std::numeric_limits<T>::epsilon());
static constexpr Quaternion Identity();
static constexpr Quaternion Lerp(const Quaternion& from, const Quaternion& to, T interpolation);
static Quaternion LookAt(const Vector3<T>& forward, const Vector3<T>& up);
static Quaternion Normalize(const Quaternion& quat, T* length = nullptr);
static Quaternion RotationBetween(const Vector3<T>& from, const Vector3<T>& to);
static Quaternion RotateTowards(const Quaternion& from, const Quaternion& to, RadianAngle<T> maxRotation);
static Quaternion Mirror(Quaternion quat, const Vector3<T>& axis);
static Quaternion Slerp(const Quaternion& from, const Quaternion& to, T interpolation);
static constexpr Quaternion Zero();

View File

@ -4,8 +4,6 @@
#include <Nazara/Core/Algorithm.hpp>
#include <Nazara/Math/Config.hpp>
#include <Nazara/Math/EulerAngles.hpp>
#include <Nazara/Math/Vector3.hpp>
#include <cstring>
#include <limits>
#include <sstream>
@ -116,6 +114,13 @@ namespace Nz
{
}
template<typename T>
RadianAngle<T> Quaternion<T>::AngleBetween(const Quaternion& quat) const
{
T alpha = Vector3<T>::DotProduct(Vector3<T>(x, y, z), Vector3<T>(quat.x, quat.y, quat.z));
return std::acos(Nz::Clamp(alpha, T(-1.0), T(1.0)));
}
template<typename T>
constexpr bool Quaternion<T>::ApproxEqual(const Quaternion& quat, T maxDifference) const
{
@ -571,6 +576,12 @@ namespace Nz
return z >= quat.z;
}
template<typename T>
RadianAngle<T> Quaternion<T>::AngleBetween(const Quaternion& lhs, const Quaternion& rhs)
{
return lhs.AngleBetween(rhs);
}
template<typename T>
constexpr bool Quaternion<T>::ApproxEqual(const Quaternion& lhs, const Quaternion& rhs, T maxDifference)
{
@ -687,6 +698,16 @@ namespace Nz
}
}
template<typename T>
Quaternion<T> Quaternion<T>::RotateTowards(const Quaternion& from, const Quaternion& to, RadianAngle<T> maxRotation)
{
RadianAngle<T> rotationBetween = AngleBetween(from, to);
if (rotationBetween < maxRotation)
return to;
return Slerp(from, to, std::min(maxRotation.value / rotationBetween.value), 1.f);
}
template<typename T>
Quaternion<T> Quaternion<T>::Mirror(Quaternion quat, const Vector3<T>& axis)
{

View File

@ -105,6 +105,7 @@ namespace Nz
static constexpr Vector3 Lerp(const Vector3& from, const Vector3& to, T interpolation);
static constexpr Vector3 Max(const Vector3& lhs, const Vector3& rhs);
static constexpr Vector3 Min(const Vector3& lhs, const Vector3& rhs);
static Vector3 RotateTowards(const Vector3& from, const Vector3& to, RadianAngle<T> maxAngle);
static Vector3 Normalize(const Vector3& vec);
static constexpr Vector3 Right();
static constexpr T SquaredDistance(const Vector3& vec1, const Vector3& vec2);

View File

@ -123,7 +123,7 @@ namespace Nz
*
* \param vec The other vector to measure the angle with
*
* \remark The vectors do not need to be normalised
* \remark The vectors need to be normalised
* \remark Produce a NazaraError if one of the vec components is null with NAZARA_MATH_SAFE defined
* \throw std::domain_error if NAZARA_MATH_SAFE is defined and one of the vec components is null
*
@ -132,20 +132,7 @@ namespace Nz
template<typename T>
RadianAngle<T> Vector3<T>::AngleBetween(const Vector3& vec) const
{
// sqrt(a) * sqrt(b) = sqrt(a*b)
T divisor = std::sqrt(GetSquaredLength() * vec.GetSquaredLength());
#if NAZARA_MATH_SAFE
if (NumberEquals(divisor, T(0.0)))
{
std::string error("Division by zero");
NazaraError(error);
throw std::domain_error(std::move(error));
}
#endif
T alpha = DotProduct(vec) / divisor;
T alpha = DotProduct(vec);
return std::acos(Nz::Clamp(alpha, T(-1.0), T(1.0)));
}
@ -835,6 +822,20 @@ namespace Nz
return min;
}
template<typename T>
Vector3<T> Vector3<T>::RotateTowards(const Vector3& from, const Vector3& to, RadianAngle<T> maxAngle)
{
// https://gamedev.stackexchange.com/a/203036
RadianAngle<T> angleBetween = from.AngleBetween(to);
if (angleBetween < maxAngle)
return to;
Vector3 axis = CrossProduct(from, to);
Quaternion<T> rotationIncrement = Quaternion(maxAngle, axis);
return rotationIncrement * from;
}
/*!
* \brief Gives the normalized vector
* \return A normalized vector from the vec

View File

@ -32,6 +32,10 @@ namespace Nz
Canvas& operator=(const Canvas&) = delete;
Canvas& operator=(Canvas&&) = delete;
NazaraSignal(OnUnhandledMouseButtonPressed, const WindowEventHandler* /*eventHandler*/, const WindowEvent::MouseButtonEvent& /*event*/);
NazaraSignal(OnUnhandledMouseButtonReleased, const WindowEventHandler* /*eventHandler*/, const WindowEvent::MouseButtonEvent& /*event*/);
NazaraSignal(OnUnhandledMouseMoved, const WindowEventHandler* /*eventHandler*/, const WindowEvent::MouseMoveEvent& /*event*/);
NazaraSignal(OnUnhandledMouseWheelMoved, const WindowEventHandler* /*eventHandler*/, const WindowEvent::MouseWheelEvent& /*event*/);
NazaraSignal(OnUnhandledKeyPressed, const WindowEventHandler* /*eventHandler*/, const WindowEvent::KeyEvent& /*event*/);
NazaraSignal(OnUnhandledKeyReleased, const WindowEventHandler* /*eventHandler*/, const WindowEvent::KeyEvent& /*event*/);
@ -55,7 +59,7 @@ namespace Nz
void UnregisterWidget(std::size_t index);
private:
template<typename F> void DispatchEvent(std::size_t widgetIndex, F&& functor);
template<typename F> bool DispatchEvent(std::size_t widgetIndex, F&& functor);
void OnEventMouseButtonPressed(const WindowEventHandler* eventHandler, const WindowEvent::MouseButtonEvent& event);
void OnEventMouseButtonRelease(const WindowEventHandler* eventHandler, const WindowEvent::MouseButtonEvent& event);

View File

@ -25,7 +25,10 @@ namespace Nz
// Regular OpenAL contexts are process-wide, but ALC_EXT_thread_local_context allows thread-local contexts
const OpenALDevice* s_currentGlobalALDevice;
#ifdef ALC_EXT_thread_local_context
thread_local const OpenALDevice* s_currentThreadALDevice;
#endif
template<typename FuncType, std::size_t FuncIndex, typename>
struct ALWrapper;
@ -175,8 +178,10 @@ namespace Nz
alcDestroyContext(m_context);
alcCloseDevice(m_device);
#ifdef ALC_EXT_thread_local_context
if (s_currentThreadALDevice)
s_currentThreadALDevice = nullptr;
#endif
if (s_currentGlobalALDevice == this)
s_currentGlobalALDevice = nullptr;
@ -331,6 +336,7 @@ namespace Nz
{
NAZARA_USE_ANONYMOUS_NAMESPACE
#ifdef ALC_EXT_thread_local_context
if (alcSetThreadContext)
{
if (s_currentThreadALDevice != this)
@ -340,17 +346,21 @@ namespace Nz
}
}
else
#endif
{
if (s_currentGlobalALDevice != this)
{
alcMakeContextCurrent(m_context);
s_currentGlobalALDevice = this;
#ifdef ALC_EXT_thread_local_context
/*
From EXT_thread_local_context:
alcMakeContextCurrent changes the current process-wide context and set the current thread-local context to NULL.
This has the side effect of changing the current thread-local context, so that the new current process-wide context will be used.
*/
alcMakeContextCurrent(m_context);
s_currentGlobalALDevice = this;
s_currentThreadALDevice = nullptr;
#endif
}
}
}
@ -514,9 +524,13 @@ namespace Nz
const OpenALDevice* OpenALDevice::GetCurrentDevice()
{
NAZARA_USE_ANONYMOUS_NAMESPACE
#ifdef ALC_EXT_thread_local_context
const OpenALDevice* device = s_currentThreadALDevice;
if (device)
return device;
#endif
return s_currentGlobalALDevice;
}

View File

@ -0,0 +1,11 @@
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - JoltPhysics3D module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/JoltPhysics3D/JoltAbstractBody.hpp>
#include <Nazara/JoltPhysics3D/Debug.hpp>
namespace Nz
{
JoltAbstractBody::~JoltAbstractBody() = default;
}

View File

@ -29,7 +29,13 @@ namespace Nz
m_bodyIndex(character.m_bodyIndex)
{
character.m_bodyIndex = std::numeric_limits<UInt32>::max();
if (m_character)
{
JPH::BodyInterface& bodyInterface = m_world->GetPhysicsSystem()->GetBodyInterfaceNoLock();
bodyInterface.SetUserData(m_character->GetBodyID(), SafeCast<UInt64>(reinterpret_cast<std::uintptr_t>(this)));
}
if (m_world)
{
m_world->UnregisterStepListener(&character);
@ -52,6 +58,11 @@ namespace Nz
bodyLock.GetBody().SetAllowSleeping(enable);
}
UInt32 JoltCharacter::GetBodyIndex() const
{
return m_bodyIndex;
}
Vector3f JoltCharacter::GetLinearVelocity() const
{
return FromJolt(m_character->GetLinearVelocity(false));
@ -135,6 +146,12 @@ namespace Nz
character.m_bodyIndex = std::numeric_limits<UInt32>::max();
if (m_character)
{
JPH::BodyInterface& bodyInterface = m_world->GetPhysicsSystem()->GetBodyInterfaceNoLock();
bodyInterface.SetUserData(m_character->GetBodyID(), SafeCast<UInt64>(reinterpret_cast<std::uintptr_t>(this)));
}
return *this;
}
@ -157,6 +174,9 @@ namespace Nz
m_bodyIndex = m_character->GetBodyID().GetIndex();
JPH::BodyInterface& bodyInterface = m_world->GetPhysicsSystem()->GetBodyInterfaceNoLock();
bodyInterface.SetUserData(m_character->GetBodyID(), SafeCast<UInt64>(reinterpret_cast<std::uintptr_t>(this)));
m_world->RegisterStepListener(this);
}
@ -177,10 +197,10 @@ namespace Nz
m_collider.reset();
}
void JoltCharacter::PostSimulate()
void JoltCharacter::PostSimulate(float elapsedTime)
{
m_character->PostSimulation(0.05f);
m_impl->PostSimulate(*this);
m_impl->PostSimulate(*this, elapsedTime);
}
void JoltCharacter::PreSimulate(float elapsedTime)
@ -191,7 +211,7 @@ namespace Nz
JoltCharacterImpl::~JoltCharacterImpl() = default;
void JoltCharacterImpl::PostSimulate(JoltCharacter& /*character*/)
void JoltCharacterImpl::PostSimulate(JoltCharacter& /*character*/, float /*elapsedTime*/)
{
}

View File

@ -4,6 +4,7 @@
#include <Nazara/JoltPhysics3D/JoltPhysWorld3D.hpp>
#include <Nazara/JoltPhysics3D/JoltCharacter.hpp>
#include <Nazara/JoltPhysics3D/JoltCollider3D.hpp>
#include <Nazara/JoltPhysics3D/JoltHelper.hpp>
#include <Nazara/JoltPhysics3D/JoltPhysics3D.hpp>
#include <Nazara/JoltPhysics3D/JoltPhysicsStepListener.hpp>
@ -16,6 +17,8 @@
#include <Jolt/Physics/PhysicsSystem.h>
#include <Jolt/Physics/Body/BodyActivationListener.h>
#include <Jolt/Physics/Collision/CastResult.h>
#include <Jolt/Physics/Collision/CollidePointResult.h>
#include <Jolt/Physics/Collision/CollideShape.h>
#include <Jolt/Physics/Collision/CollisionCollectorImpl.h>
#include <Jolt/Physics/Collision/ObjectLayer.h>
#include <Jolt/Physics/Collision/RayCast.h>
@ -162,10 +165,106 @@ namespace Nz
{
namespace NAZARA_ANONYMOUS_NAMESPACE
{
class CallbackHitResult : public JPH::CastRayCollector
class PointCallbackHitResult : public JPH::CollidePointCollector
{
public:
CallbackHitResult(const JPH::BodyLockInterface& bodyLockInterface, const Vector3f& from, const Vector3f& to, const FunctionRef<std::optional<float>(const JoltPhysWorld3D::RaycastHit& hitInfo)>& callback) :
PointCallbackHitResult(const JPH::BodyLockInterface& bodyLockInterface, const FunctionRef<std::optional<float>(const JoltPhysWorld3D::PointCollisionInfo& hitInfo)>& callback) :
m_bodyLockInterface(bodyLockInterface),
m_callback(callback),
m_didHit(false)
{
}
void AddHit(const JPH::CollidePointResult& result) override
{
JoltPhysWorld3D::PointCollisionInfo hitInfo;
JPH::BodyLockWrite lock(m_bodyLockInterface, result.mBodyID);
if (!lock.Succeeded())
return; //< body was destroyed
JPH::Body& body = lock.GetBody();
hitInfo.hitBody = reinterpret_cast<JoltAbstractBody*>(static_cast<std::uintptr_t>(body.GetUserData()));
if (auto fractionOpt = m_callback(hitInfo))
{
float fraction = fractionOpt.value();
if (fraction > 0.f)
{
m_didHit = true;
UpdateEarlyOutFraction(fraction);
}
else
ForceEarlyOut();
}
}
bool DidHit() const
{
return m_didHit;
}
private:
const JPH::BodyLockInterface& m_bodyLockInterface;
const FunctionRef<std::optional<float>(const JoltPhysWorld3D::PointCollisionInfo& hitInfo)>& m_callback;
bool m_didHit;
};
class ShapeCallbackHitResult : public JPH::CollideShapeCollector
{
public:
ShapeCallbackHitResult(const JPH::BodyLockInterface& bodyLockInterface, const FunctionRef<std::optional<float>(const JoltPhysWorld3D::ShapeCollisionInfo& hitInfo)>& callback) :
m_bodyLockInterface(bodyLockInterface),
m_callback(callback),
m_didHit(false)
{
}
void AddHit(const JPH::CollideShapeResult& result) override
{
JoltPhysWorld3D::ShapeCollisionInfo hitInfo;
hitInfo.collisionPosition1 = FromJolt(result.mContactPointOn1);
hitInfo.collisionPosition2 = FromJolt(result.mContactPointOn2);
hitInfo.penetrationAxis = FromJolt(result.mPenetrationAxis);
hitInfo.penetrationDepth = result.mPenetrationDepth;
JPH::BodyLockWrite lock(m_bodyLockInterface, result.mBodyID2);
if (!lock.Succeeded())
return; //< body was destroyed
JPH::Body& body = lock.GetBody();
hitInfo.hitBody = reinterpret_cast<JoltAbstractBody*>(static_cast<std::uintptr_t>(body.GetUserData()));
if (auto fractionOpt = m_callback(hitInfo))
{
float fraction = fractionOpt.value();
if (fraction > 0.f)
{
m_didHit = true;
UpdateEarlyOutFraction(fraction);
}
else
ForceEarlyOut();
}
}
bool DidHit() const
{
return m_didHit;
}
private:
const JPH::BodyLockInterface& m_bodyLockInterface;
const FunctionRef<std::optional<float>(const JoltPhysWorld3D::ShapeCollisionInfo& hitInfo)>& m_callback;
bool m_didHit;
};
class RaycastCallbackHitResult : public JPH::CastRayCollector
{
public:
RaycastCallbackHitResult(const JPH::BodyLockInterface& bodyLockInterface, const Vector3f& from, const Vector3f& to, const FunctionRef<std::optional<float>(const JoltPhysWorld3D::RaycastHit& hitInfo)>& callback) :
m_bodyLockInterface(bodyLockInterface),
m_callback(callback),
m_from(from),
@ -186,7 +285,7 @@ namespace Nz
JPH::Body& body = lock.GetBody();
hitInfo.hitBody = reinterpret_cast<JoltRigidBody3D*>(static_cast<std::uintptr_t>(body.GetUserData()));
hitInfo.hitBody = reinterpret_cast<JoltAbstractBody*>(static_cast<std::uintptr_t>(body.GetUserData()));
hitInfo.hitNormal = FromJolt(body.GetWorldSpaceSurfaceNormal(result.mSubShapeID2, ToJolt(hitInfo.hitPosition)));
if (auto fractionOpt = m_callback(hitInfo))
@ -323,6 +422,35 @@ namespace Nz
JoltPhysWorld3D::~JoltPhysWorld3D() = default;
bool JoltPhysWorld3D::CollisionQuery(const Vector3f& point, const FunctionRef<std::optional<float>(const PointCollisionInfo& collisionInfo)>& callback)
{
NAZARA_USE_ANONYMOUS_NAMESPACE
PointCallbackHitResult collector(m_world->physicsSystem.GetBodyLockInterface(), callback);
m_world->physicsSystem.GetNarrowPhaseQuery().CollidePoint(ToJolt(point), collector);
return collector.DidHit();
}
bool JoltPhysWorld3D::CollisionQuery(const JoltCollider3D& collider, const Matrix4f& colliderTransform, const FunctionRef<std::optional<float>(const ShapeCollisionInfo& hitInfo)>& callback)
{
return CollisionQuery(collider, colliderTransform, Vector3f::Unit(), callback);
}
bool JoltPhysWorld3D::CollisionQuery(const JoltCollider3D& collider, const Matrix4f& colliderTransform, const Vector3f& colliderScale, const FunctionRef<std::optional<float>(const ShapeCollisionInfo& hitInfo)>& callback)
{
NAZARA_USE_ANONYMOUS_NAMESPACE
JPH::Shape* shape = collider.GetShapeSettings()->Create().Get();
JPH::CollideShapeSettings collideShapeSettings;
ShapeCallbackHitResult collector(m_world->physicsSystem.GetBodyLockInterface(), callback);
m_world->physicsSystem.GetNarrowPhaseQuery().CollideShape(shape, ToJolt(colliderScale), ToJolt(colliderTransform), collideShapeSettings, JPH::Vec3::sZero(), collector);
return collector.DidHit();
}
UInt32 JoltPhysWorld3D::GetActiveBodyCount() const
{
return m_world->physicsSystem.GetNumActiveBodies(JPH::EBodyType::RigidBody);
@ -358,7 +486,7 @@ namespace Nz
JPH::RayCastSettings rayCastSettings;
CallbackHitResult collector(m_world->physicsSystem.GetBodyLockInterface(), from, to, callback);
RaycastCallbackHitResult collector(m_world->physicsSystem.GetBodyLockInterface(), from, to, callback);
m_world->physicsSystem.GetNarrowPhaseQuery().CastRay(rayCast, rayCastSettings, collector);
return collector.DidHit();
@ -387,7 +515,7 @@ namespace Nz
RaycastHit hitInfo;
hitInfo.fraction = collector.mHit.GetEarlyOutFraction();
hitInfo.hitPosition = Lerp(from, to, hitInfo.fraction);
hitInfo.hitBody = reinterpret_cast<JoltRigidBody3D*>(static_cast<std::uintptr_t>(body.GetUserData()));
hitInfo.hitBody = reinterpret_cast<JoltAbstractBody*>(static_cast<std::uintptr_t>(body.GetUserData()));
hitInfo.hitNormal = FromJolt(body.GetWorldSpaceSurfaceNormal(collector.mHit.mSubShapeID2, rayCast.GetPointOnRay(collector.mHit.GetEarlyOutFraction())));
callback(hitInfo);
@ -473,7 +601,7 @@ namespace Nz
m_world->physicsSystem.Update(stepSize, 1, &m_world->tempAllocator, &jobSystem);
for (JoltPhysicsStepListener* stepListener : m_stepListeners)
stepListener->PostSimulate();
stepListener->PostSimulate(stepSize);
m_timestepAccumulator -= m_stepSize;
stepCount++;

View File

@ -9,7 +9,7 @@ namespace Nz
{
JoltPhysicsStepListener::~JoltPhysicsStepListener() = default;
void JoltPhysicsStepListener::PostSimulate()
void JoltPhysicsStepListener::PostSimulate(float /*elapsedTime*/)
{
}

View File

@ -120,6 +120,11 @@ namespace Nz
return FromJolt(m_body->GetAngularVelocity());
}
UInt32 JoltRigidBody3D::GetBodyIndex() const
{
return m_bodyIndex;
}
float JoltRigidBody3D::GetLinearDamping() const
{
if NAZARA_UNLIKELY(IsStatic())

View File

@ -3,6 +3,7 @@
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/JoltPhysics3D/Systems/JoltPhysics3DSystem.hpp>
#include <Nazara/JoltPhysics3D/JoltAbstractBody.hpp>
#include <Nazara/Core/Components/DisabledComponent.hpp>
#include <Nazara/Utility/Components/NodeComponent.hpp>
#include <Nazara/JoltPhysics3D/Debug.hpp>
@ -15,8 +16,9 @@ namespace Nz
m_rigidBodyConstructObserver(m_registry, entt::collector.group<JoltRigidBody3DComponent, NodeComponent>(entt::exclude<DisabledComponent, JoltCharacterComponent>))
{
m_bodyConstructConnection = registry.on_construct<JoltRigidBody3DComponent>().connect<&JoltPhysics3DSystem::OnBodyConstruct>(this);
m_characterConstructConnection = registry.on_construct<JoltCharacterComponent>().connect<&JoltPhysics3DSystem::OnCharacterConstruct>(this);
m_bodyDestructConnection = registry.on_destroy<JoltRigidBody3DComponent>().connect<&JoltPhysics3DSystem::OnBodyDestruct>(this);
m_characterConstructConnection = registry.on_construct<JoltCharacterComponent>().connect<&JoltPhysics3DSystem::OnCharacterConstruct>(this);
m_characterDestructConnection = registry.on_destroy<JoltCharacterComponent>().connect<&JoltPhysics3DSystem::OnCharacterDestruct>(this);
}
JoltPhysics3DSystem::~JoltPhysics3DSystem()
@ -34,6 +36,47 @@ namespace Nz
rigidBodyComponent.Destroy(true);
}
bool JoltPhysics3DSystem::CollisionQuery(const Vector3f& point, const FunctionRef<std::optional<float>(const PointCollisionInfo& collisionInfo)>& callback)
{
return m_physWorld.CollisionQuery(point, [&](const JoltPhysWorld3D::PointCollisionInfo& hitInfo)
{
PointCollisionInfo extendedHitInfo;
static_cast<JoltPhysWorld3D::PointCollisionInfo&>(extendedHitInfo) = hitInfo;
if (extendedHitInfo.hitBody)
{
std::size_t bodyIndex = extendedHitInfo.hitBody->GetBodyIndex();
if (bodyIndex < m_bodyIndicesToEntity.size())
extendedHitInfo.hitEntity = entt::handle(m_registry, m_bodyIndicesToEntity[bodyIndex]);
}
return callback(extendedHitInfo);
});
}
bool JoltPhysics3DSystem::CollisionQuery(const JoltCollider3D& collider, const Matrix4f& shapeTransform, const FunctionRef<std::optional<float>(const ShapeCollisionInfo& hitInfo)>& callback)
{
return CollisionQuery(collider, shapeTransform, Vector3f::Unit(), callback);
}
bool JoltPhysics3DSystem::CollisionQuery(const JoltCollider3D& collider, const Matrix4f& colliderTransform, const Vector3f& colliderScale, const FunctionRef<std::optional<float>(const ShapeCollisionInfo& hitInfo)>& callback)
{
return m_physWorld.CollisionQuery(collider, colliderTransform, colliderScale, [&](const JoltPhysWorld3D::ShapeCollisionInfo& hitInfo)
{
ShapeCollisionInfo extendedHitInfo;
static_cast<JoltPhysWorld3D::ShapeCollisionInfo&>(extendedHitInfo) = hitInfo;
if (extendedHitInfo.hitBody)
{
std::size_t bodyIndex = extendedHitInfo.hitBody->GetBodyIndex();
if (bodyIndex < m_bodyIndicesToEntity.size())
extendedHitInfo.hitEntity = entt::handle(m_registry, m_bodyIndicesToEntity[bodyIndex]);
}
return callback(extendedHitInfo);
});
}
bool JoltPhysics3DSystem::RaycastQuery(const Vector3f& from, const Vector3f& to, const FunctionRef<std::optional<float>(const RaycastHit& hitInfo)>& callback)
{
return m_physWorld.RaycastQuery(from, to, [&](const JoltPhysWorld3D::RaycastHit& hitInfo)
@ -139,12 +182,6 @@ namespace Nz
m_bodyIndicesToEntity[uniqueIndex] = entity;
}
void JoltPhysics3DSystem::OnCharacterConstruct(entt::registry& registry, entt::entity entity)
{
JoltCharacterComponent& character = registry.get<JoltCharacterComponent>(entity);
character.Construct(m_physWorld);
}
void JoltPhysics3DSystem::OnBodyDestruct(entt::registry& registry, entt::entity entity)
{
// Unregister owning entity
@ -154,4 +191,26 @@ namespace Nz
m_bodyIndicesToEntity[uniqueIndex] = entt::null;
}
void JoltPhysics3DSystem::OnCharacterConstruct(entt::registry& registry, entt::entity entity)
{
JoltCharacterComponent& character = registry.get<JoltCharacterComponent>(entity);
character.Construct(m_physWorld);
UInt32 uniqueIndex = character.GetBodyIndex();
if (uniqueIndex >= m_bodyIndicesToEntity.size())
m_bodyIndicesToEntity.resize(uniqueIndex + 1);
m_bodyIndicesToEntity[uniqueIndex] = entity;
}
void JoltPhysics3DSystem::OnCharacterDestruct(entt::registry& registry, entt::entity entity)
{
// Unregister owning entity
JoltCharacterComponent& character = registry.get<JoltCharacterComponent>(entity);
UInt32 uniqueIndex = character.GetBodyIndex();
assert(uniqueIndex <= m_bodyIndicesToEntity.size());
m_bodyIndicesToEntity[uniqueIndex] = entt::null;
}
}

View File

@ -91,28 +91,29 @@ namespace Nz
}
template<typename F>
void Canvas::DispatchEvent(std::size_t widgetIndex, F&& functor)
bool Canvas::DispatchEvent(std::size_t widgetIndex, F&& functor)
{
for (;;)
{
WidgetEntry& targetWidget = m_widgetEntries[widgetIndex];
if (functor(targetWidget))
return;
return true;
if (!targetWidget.widget->m_parentWidget)
return;
return false;
widgetIndex = targetWidget.widget->m_parentWidget->m_canvasIndex;
}
}
void Canvas::OnEventMouseButtonPressed(const WindowEventHandler* /*eventHandler*/, const WindowEvent::MouseButtonEvent& event)
void Canvas::OnEventMouseButtonPressed(const WindowEventHandler* eventHandler, const WindowEvent::MouseButtonEvent& event)
{
UpdateHoveredWidget(event.x, event.y);
bool handled = false;
if (std::size_t targetWidgetIndex = GetMouseEventTarget(); targetWidgetIndex != InvalidCanvasIndex)
{
DispatchEvent(targetWidgetIndex, [&](WidgetEntry& widgetEntry)
handled = DispatchEvent(targetWidgetIndex, [&](WidgetEntry& widgetEntry)
{
int x = static_cast<int>(std::round(event.x - widgetEntry.box.x));
int y = static_cast<int>(std::round(m_size.y - event.y - widgetEntry.box.y));
@ -128,13 +129,17 @@ namespace Nz
SetMouseOwner(m_hoveredWidget);
m_mouseOwnerButtons[event.button] = true;
if (!handled)
OnUnhandledMouseButtonPressed(eventHandler, event);
}
void Canvas::OnEventMouseButtonRelease(const WindowEventHandler* /*eventHandler*/, const WindowEvent::MouseButtonEvent& event)
void Canvas::OnEventMouseButtonRelease(const WindowEventHandler* eventHandler, const WindowEvent::MouseButtonEvent& event)
{
bool handled = false;
if (std::size_t targetWidgetIndex = GetMouseEventTarget(); targetWidgetIndex != InvalidCanvasIndex)
{
DispatchEvent(targetWidgetIndex, [&](WidgetEntry& widgetEntry)
handled = DispatchEvent(targetWidgetIndex, [&](WidgetEntry& widgetEntry)
{
int x = static_cast<int>(std::round(event.x - widgetEntry.box.x));
int y = static_cast<int>(std::round(m_size.y - event.y - widgetEntry.box.y));
@ -148,6 +153,9 @@ namespace Nz
SetMouseOwner(InvalidCanvasIndex);
UpdateHoveredWidget(event.x, event.y);
if (!handled)
OnUnhandledMouseButtonReleased(eventHandler, event);
}
void Canvas::OnEventMouseEntered(const WindowEventHandler* /*eventHandler*/)
@ -185,7 +193,7 @@ namespace Nz
}
}
void Canvas::OnEventMouseMoved(const WindowEventHandler* /*eventHandler*/, const WindowEvent::MouseMoveEvent& event)
void Canvas::OnEventMouseMoved(const WindowEventHandler* eventHandler, const WindowEvent::MouseMoveEvent& event)
{
// Don't update hovered widget while the user doesn't release its mouse
UpdateHoveredWidget(event.x, event.y);
@ -197,15 +205,19 @@ namespace Nz
int x = static_cast<int>(std::round(event.x - targetWidget.box.x));
int y = static_cast<int>(std::round(m_size.y - event.y - targetWidget.box.y));
targetWidget.widget->OnMouseMoved(x, y, event.deltaX, -event.deltaY);
if (targetWidget.widget->OnMouseMoved(x, y, event.deltaX, -event.deltaY))
return;
}
OnUnhandledMouseMoved(eventHandler, event);
}
void Canvas::OnEventMouseWheelMoved(const WindowEventHandler* /*eventHandler*/, const WindowEvent::MouseWheelEvent& event)
void Canvas::OnEventMouseWheelMoved(const WindowEventHandler* eventHandler, const WindowEvent::MouseWheelEvent& event)
{
bool handled = false;
if (std::size_t targetWidgetIndex = GetMouseEventTarget(); targetWidgetIndex != InvalidCanvasIndex)
{
DispatchEvent(targetWidgetIndex, [&](WidgetEntry& widgetEntry)
handled = DispatchEvent(targetWidgetIndex, [&](WidgetEntry& widgetEntry)
{
int x = static_cast<int>(std::round(event.x - widgetEntry.box.x));
int y = static_cast<int>(std::round(m_size.y - event.y - widgetEntry.box.y));
@ -213,6 +225,9 @@ namespace Nz
return widgetEntry.widget->OnMouseWheelMoved(x, y, event.delta);
});
}
if (!handled)
OnUnhandledMouseWheelMoved(eventHandler, event);
}
void Canvas::OnEventKeyPressed(const WindowEventHandler* eventHandler, const WindowEvent::KeyEvent& event)
@ -276,7 +291,10 @@ namespace Nz
void Canvas::OnEventKeyReleased(const WindowEventHandler* eventHandler, const WindowEvent::KeyEvent& event)
{
if (m_keyboardOwner != InvalidCanvasIndex)
m_widgetEntries[m_keyboardOwner].widget->OnKeyReleased(event);
{
if (m_widgetEntries[m_keyboardOwner].widget->OnKeyReleased(event))
return;
}
OnUnhandledKeyReleased(eventHandler, event);
}