1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-04 12:00:25 +00:00

Add Jolt Physics as an alternative 3D physics engine

Co-authored-by: Jorrit Rouwe <jrouwe@gmail.com>
This commit is contained in:
Mikael Hermansson
2024-12-11 13:57:25 +01:00
parent aa8d9b83f6
commit d470c2ac6a
531 changed files with 115764 additions and 4 deletions

View File

@@ -0,0 +1,196 @@
/**************************************************************************/
/* jolt_body_accessor_3d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "jolt_body_accessor_3d.h"
#include "jolt_space_3d.h"
namespace {
template <class... TTypes>
struct VariantVisitors : TTypes... {
using TTypes::operator()...;
};
template <class... TTypes>
VariantVisitors(TTypes...) -> VariantVisitors<TTypes...>;
} // namespace
JoltBodyAccessor3D::JoltBodyAccessor3D(const JoltSpace3D *p_space) :
space(p_space) {
}
JoltBodyAccessor3D::~JoltBodyAccessor3D() = default;
void JoltBodyAccessor3D::acquire(const JPH::BodyID *p_ids, int p_id_count) {
ERR_FAIL_NULL(space);
lock_iface = &space->get_lock_iface();
ids = BodyIDSpan(p_ids, p_id_count);
_acquire_internal(p_ids, p_id_count);
}
void JoltBodyAccessor3D::acquire(const JPH::BodyID &p_id) {
ERR_FAIL_NULL(space);
lock_iface = &space->get_lock_iface();
ids = p_id;
_acquire_internal(&p_id, 1);
}
void JoltBodyAccessor3D::acquire_active() {
const JPH::PhysicsSystem &physics_system = space->get_physics_system();
acquire(physics_system.GetActiveBodiesUnsafe(JPH::EBodyType::RigidBody), (int)physics_system.GetNumActiveBodies(JPH::EBodyType::RigidBody));
}
void JoltBodyAccessor3D::acquire_all() {
ERR_FAIL_NULL(space);
lock_iface = &space->get_lock_iface();
JPH::BodyIDVector *vector = std::get_if<JPH::BodyIDVector>(&ids);
if (vector == nullptr) {
ids = JPH::BodyIDVector();
vector = std::get_if<JPH::BodyIDVector>(&ids);
}
space->get_physics_system().GetBodies(*vector);
_acquire_internal(vector->data(), (int)vector->size());
}
void JoltBodyAccessor3D::release() {
_release_internal();
lock_iface = nullptr;
}
const JPH::BodyID *JoltBodyAccessor3D::get_ids() const {
ERR_FAIL_COND_V(not_acquired(), nullptr);
return std::visit(
VariantVisitors{
[](const JPH::BodyID &p_id) { return &p_id; },
[](const JPH::BodyIDVector &p_vector) { return p_vector.data(); },
[](const BodyIDSpan &p_span) { return p_span.ptr; } },
ids);
}
int JoltBodyAccessor3D::get_count() const {
ERR_FAIL_COND_V(not_acquired(), 0);
return std::visit(
VariantVisitors{
[](const JPH::BodyID &p_id) { return 1; },
[](const JPH::BodyIDVector &p_vector) { return (int)p_vector.size(); },
[](const BodyIDSpan &p_span) { return p_span.count; } },
ids);
}
const JPH::BodyID &JoltBodyAccessor3D::get_at(int p_index) const {
CRASH_BAD_INDEX(p_index, get_count());
return get_ids()[p_index];
}
void JoltBodyReader3D::_acquire_internal(const JPH::BodyID *p_ids, int p_id_count) {
mutex_mask = lock_iface->GetMutexMask(p_ids, p_id_count);
lock_iface->LockRead(mutex_mask);
}
void JoltBodyReader3D::_release_internal() {
ERR_FAIL_COND(not_acquired());
lock_iface->UnlockRead(mutex_mask);
}
JoltBodyReader3D::JoltBodyReader3D(const JoltSpace3D *p_space) :
JoltBodyAccessor3D(p_space) {
}
const JPH::Body *JoltBodyReader3D::try_get(const JPH::BodyID &p_id) const {
if (unlikely(p_id.IsInvalid())) {
return nullptr;
}
ERR_FAIL_COND_V(not_acquired(), nullptr);
return lock_iface->TryGetBody(p_id);
}
const JPH::Body *JoltBodyReader3D::try_get(int p_index) const {
const int count = get_count();
if (unlikely(p_index < 0 || p_index >= count)) {
return nullptr;
}
return try_get(get_at(p_index));
}
const JPH::Body *JoltBodyReader3D::try_get() const {
return try_get(0);
}
void JoltBodyWriter3D::_acquire_internal(const JPH::BodyID *p_ids, int p_id_count) {
mutex_mask = lock_iface->GetMutexMask(p_ids, p_id_count);
lock_iface->LockWrite(mutex_mask);
}
void JoltBodyWriter3D::_release_internal() {
ERR_FAIL_COND(not_acquired());
lock_iface->UnlockWrite(mutex_mask);
}
JoltBodyWriter3D::JoltBodyWriter3D(const JoltSpace3D *p_space) :
JoltBodyAccessor3D(p_space) {
}
JPH::Body *JoltBodyWriter3D::try_get(const JPH::BodyID &p_id) const {
if (unlikely(p_id.IsInvalid())) {
return nullptr;
}
ERR_FAIL_COND_V(not_acquired(), nullptr);
return lock_iface->TryGetBody(p_id);
}
JPH::Body *JoltBodyWriter3D::try_get(int p_index) const {
const int count = get_count();
if (unlikely(p_index < 0 || p_index >= count)) {
return nullptr;
}
return try_get(get_at(p_index));
}
JPH::Body *JoltBodyWriter3D::try_get() const {
return try_get(0);
}

View File

@@ -0,0 +1,214 @@
/**************************************************************************/
/* jolt_body_accessor_3d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef JOLT_BODY_ACCESSOR_3D_H
#define JOLT_BODY_ACCESSOR_3D_H
#include "../objects/jolt_object_3d.h"
#include "Jolt/Jolt.h"
#include "Jolt/Physics/Body/BodyLockInterface.h"
#include <variant>
class JoltArea3D;
class JoltBody3D;
class JoltShapedObject3D;
class JoltSpace3D;
class JoltBodyAccessor3D {
protected:
struct BodyIDSpan {
BodyIDSpan(const JPH::BodyID *p_ptr, int p_count) :
ptr(p_ptr), count(p_count) {}
const JPH::BodyID *ptr;
int count;
};
virtual void _acquire_internal(const JPH::BodyID *p_ids, int p_id_count) = 0;
virtual void _release_internal() = 0;
const JoltSpace3D *space = nullptr;
const JPH::BodyLockInterface *lock_iface = nullptr;
std::variant<JPH::BodyID, JPH::BodyIDVector, BodyIDSpan> ids;
public:
explicit JoltBodyAccessor3D(const JoltSpace3D *p_space);
virtual ~JoltBodyAccessor3D() = 0;
void acquire(const JPH::BodyID *p_ids, int p_id_count);
void acquire(const JPH::BodyID &p_id);
void acquire_active();
void acquire_all();
void release();
bool is_acquired() const { return lock_iface != nullptr; }
bool not_acquired() const { return lock_iface == nullptr; }
const JoltSpace3D &get_space() const { return *space; }
const JPH::BodyID *get_ids() const;
int get_count() const;
const JPH::BodyID &get_at(int p_index) const;
};
class JoltBodyReader3D final : public JoltBodyAccessor3D {
virtual void _acquire_internal(const JPH::BodyID *p_ids, int p_id_count) override;
virtual void _release_internal() override;
JPH::BodyLockInterface::MutexMask mutex_mask = 0;
public:
explicit JoltBodyReader3D(const JoltSpace3D *p_space);
const JPH::Body *try_get(const JPH::BodyID &p_id) const;
const JPH::Body *try_get(int p_index) const;
const JPH::Body *try_get() const;
};
class JoltBodyWriter3D final : public JoltBodyAccessor3D {
virtual void _acquire_internal(const JPH::BodyID *p_ids, int p_id_count) override;
virtual void _release_internal() override;
JPH::BodyLockInterface::MutexMask mutex_mask = 0;
public:
explicit JoltBodyWriter3D(const JoltSpace3D *p_space);
JPH::Body *try_get(const JPH::BodyID &p_id) const;
JPH::Body *try_get(int p_index) const;
JPH::Body *try_get() const;
};
template <typename TBodyAccessor>
class JoltScopedBodyAccessor3D {
TBodyAccessor inner;
public:
JoltScopedBodyAccessor3D(const JoltSpace3D &p_space, const JPH::BodyID *p_ids, int p_id_count) :
inner(&p_space) { inner.acquire(p_ids, p_id_count); }
JoltScopedBodyAccessor3D(const JoltSpace3D &p_space, const JPH::BodyID &p_id) :
inner(&p_space) { inner.acquire(p_id); }
JoltScopedBodyAccessor3D(const JoltScopedBodyAccessor3D &p_other) = delete;
JoltScopedBodyAccessor3D(JoltScopedBodyAccessor3D &&p_other) = default;
~JoltScopedBodyAccessor3D() { inner.release(); }
const JoltSpace3D &get_space() const { return inner.get_space(); }
int get_count() const { return inner.get_count(); }
const JPH::BodyID &get_at(int p_index) const { return inner.get_at(p_index); }
JoltScopedBodyAccessor3D &operator=(const JoltScopedBodyAccessor3D &p_other) = delete;
JoltScopedBodyAccessor3D &operator=(JoltScopedBodyAccessor3D &&p_other) = default;
decltype(auto) try_get(const JPH::BodyID &p_id) const { return inner.try_get(p_id); }
decltype(auto) try_get(int p_index) const { return inner.try_get(p_index); }
decltype(auto) try_get() const { return inner.try_get(); }
};
template <typename TAccessor, typename TBody>
class JoltAccessibleBody3D {
TAccessor accessor;
TBody *body = nullptr;
public:
JoltAccessibleBody3D(const JoltSpace3D &p_space, const JPH::BodyID &p_id) :
accessor(p_space, p_id), body(accessor.try_get()) {}
bool is_valid() const { return body != nullptr; }
bool is_invalid() const { return body == nullptr; }
JoltObject3D *as_object() const {
if (body != nullptr) {
return reinterpret_cast<JoltObject3D *>(body->GetUserData());
} else {
return nullptr;
}
}
JoltShapedObject3D *as_shaped() const {
if (JoltObject3D *object = as_object(); object != nullptr && object->is_shaped()) {
return reinterpret_cast<JoltShapedObject3D *>(body->GetUserData());
} else {
return nullptr;
}
}
JoltBody3D *as_body() const {
if (JoltObject3D *object = as_object(); object != nullptr && object->is_body()) {
return reinterpret_cast<JoltBody3D *>(body->GetUserData());
} else {
return nullptr;
}
}
JoltArea3D *as_area() const {
if (JoltObject3D *object = as_object(); object != nullptr && object->is_area()) {
return reinterpret_cast<JoltArea3D *>(body->GetUserData());
} else {
return nullptr;
}
}
TBody *operator->() const { return body; }
TBody &operator*() const { return *body; }
explicit operator TBody *() const { return body; }
};
template <typename TAccessor, typename TBody>
class JoltAccessibleBodies3D {
TAccessor accessor;
public:
JoltAccessibleBodies3D(const JoltSpace3D &p_space, const JPH::BodyID *p_ids, int p_id_count) :
accessor(p_space, p_ids, p_id_count) {}
JoltAccessibleBody3D<TAccessor, TBody> operator[](int p_index) const {
const JPH::BodyID &body_id = p_index < accessor.get_count() ? accessor.get_at(p_index) : JPH::BodyID();
return JoltAccessibleBody3D<TAccessor, TBody>(accessor.get_space(), body_id);
}
};
typedef JoltScopedBodyAccessor3D<JoltBodyReader3D> JoltScopedBodyReader3D;
typedef JoltScopedBodyAccessor3D<JoltBodyWriter3D> JoltScopedBodyWriter3D;
typedef JoltAccessibleBody3D<JoltScopedBodyReader3D, const JPH::Body> JoltReadableBody3D;
typedef JoltAccessibleBody3D<JoltScopedBodyWriter3D, JPH::Body> JoltWritableBody3D;
typedef JoltAccessibleBodies3D<JoltScopedBodyReader3D, const JPH::Body> JoltReadableBodies3D;
typedef JoltAccessibleBodies3D<JoltScopedBodyWriter3D, JPH::Body> JoltWritableBodies3D;
#endif // JOLT_BODY_ACCESSOR_3D_H

View File

@@ -0,0 +1,52 @@
/**************************************************************************/
/* jolt_broad_phase_layer.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef JOLT_BROAD_PHASE_LAYER_H
#define JOLT_BROAD_PHASE_LAYER_H
#include "Jolt/Jolt.h"
#include "Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h"
#include <stdint.h>
namespace JoltBroadPhaseLayer {
constexpr JPH::BroadPhaseLayer BODY_STATIC(0);
constexpr JPH::BroadPhaseLayer BODY_STATIC_BIG(1);
constexpr JPH::BroadPhaseLayer BODY_DYNAMIC(2);
constexpr JPH::BroadPhaseLayer AREA_DETECTABLE(3);
constexpr JPH::BroadPhaseLayer AREA_UNDETECTABLE(4);
constexpr uint32_t COUNT = 5;
} // namespace JoltBroadPhaseLayer
#endif // JOLT_BROAD_PHASE_LAYER_H

View File

@@ -0,0 +1,548 @@
/**************************************************************************/
/* jolt_contact_listener_3d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "jolt_contact_listener_3d.h"
#include "../jolt_project_settings.h"
#include "../misc/jolt_type_conversions.h"
#include "../objects/jolt_area_3d.h"
#include "../objects/jolt_body_3d.h"
#include "../objects/jolt_soft_body_3d.h"
#include "jolt_space_3d.h"
#include "Jolt/Physics/Collision/EstimateCollisionResponse.h"
#include "Jolt/Physics/SoftBody/SoftBodyManifold.h"
void JoltContactListener3D::OnContactAdded(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold, JPH::ContactSettings &p_settings) {
_try_override_collision_response(p_body1, p_body2, p_settings);
_try_apply_surface_velocities(p_body1, p_body2, p_settings);
_try_add_contacts(p_body1, p_body2, p_manifold, p_settings);
_try_evaluate_area_overlap(p_body1, p_body2, p_manifold);
#ifdef DEBUG_ENABLED
_try_add_debug_contacts(p_body1, p_body2, p_manifold);
#endif
}
void JoltContactListener3D::OnContactPersisted(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold, JPH::ContactSettings &p_settings) {
_try_override_collision_response(p_body1, p_body2, p_settings);
_try_apply_surface_velocities(p_body1, p_body2, p_settings);
_try_add_contacts(p_body1, p_body2, p_manifold, p_settings);
_try_evaluate_area_overlap(p_body1, p_body2, p_manifold);
#ifdef DEBUG_ENABLED
_try_add_debug_contacts(p_body1, p_body2, p_manifold);
#endif
}
void JoltContactListener3D::OnContactRemoved(const JPH::SubShapeIDPair &p_shape_pair) {
if (_try_remove_contacts(p_shape_pair)) {
return;
}
if (_try_remove_area_overlap(p_shape_pair)) {
return;
}
}
JPH::SoftBodyValidateResult JoltContactListener3D::OnSoftBodyContactValidate(const JPH::Body &p_soft_body, const JPH::Body &p_other_body, JPH::SoftBodyContactSettings &p_settings) {
_try_override_collision_response(p_soft_body, p_other_body, p_settings);
return JPH::SoftBodyValidateResult::AcceptContact;
}
#ifdef DEBUG_ENABLED
void JoltContactListener3D::OnSoftBodyContactAdded(const JPH::Body &p_soft_body, const JPH::SoftBodyManifold &p_manifold) {
_try_add_debug_contacts(p_soft_body, p_manifold);
}
#endif
bool JoltContactListener3D::_is_listening_for(const JPH::Body &p_body) const {
return listening_for.has(p_body.GetID());
}
bool JoltContactListener3D::_try_override_collision_response(const JPH::Body &p_jolt_body1, const JPH::Body &p_jolt_body2, JPH::ContactSettings &p_settings) {
if (p_jolt_body1.IsSensor() || p_jolt_body2.IsSensor()) {
return false;
}
if (!p_jolt_body1.IsDynamic() && !p_jolt_body2.IsDynamic()) {
return false;
}
const JoltBody3D *body1 = reinterpret_cast<JoltBody3D *>(p_jolt_body1.GetUserData());
const JoltBody3D *body2 = reinterpret_cast<JoltBody3D *>(p_jolt_body2.GetUserData());
const bool can_collide1 = body1->can_collide_with(*body2);
const bool can_collide2 = body2->can_collide_with(*body1);
if (can_collide1 && !can_collide2) {
p_settings.mInvMassScale2 = 0.0f;
p_settings.mInvInertiaScale2 = 0.0f;
} else if (can_collide2 && !can_collide1) {
p_settings.mInvMassScale1 = 0.0f;
p_settings.mInvInertiaScale1 = 0.0f;
}
return true;
}
bool JoltContactListener3D::_try_override_collision_response(const JPH::Body &p_jolt_soft_body, const JPH::Body &p_jolt_other_body, JPH::SoftBodyContactSettings &p_settings) {
if (p_jolt_other_body.IsSensor()) {
return false;
}
const JoltSoftBody3D *soft_body = reinterpret_cast<JoltSoftBody3D *>(p_jolt_soft_body.GetUserData());
const JoltBody3D *other_body = reinterpret_cast<JoltBody3D *>(p_jolt_other_body.GetUserData());
const bool can_collide1 = soft_body->can_collide_with(*other_body);
const bool can_collide2 = other_body->can_collide_with(*soft_body);
if (can_collide1 && !can_collide2) {
p_settings.mInvMassScale2 = 0.0f;
p_settings.mInvInertiaScale2 = 0.0f;
} else if (can_collide2 && !can_collide1) {
p_settings.mInvMassScale1 = 0.0f;
}
return true;
}
bool JoltContactListener3D::_try_apply_surface_velocities(const JPH::Body &p_jolt_body1, const JPH::Body &p_jolt_body2, JPH::ContactSettings &p_settings) {
if (p_jolt_body1.IsSensor() || p_jolt_body2.IsSensor()) {
return false;
}
const bool supports_surface_velocity1 = !p_jolt_body1.IsDynamic();
const bool supports_surface_velocity2 = !p_jolt_body2.IsDynamic();
if (supports_surface_velocity1 == supports_surface_velocity2) {
return false;
}
const JoltBody3D *body1 = reinterpret_cast<JoltBody3D *>(p_jolt_body1.GetUserData());
const JoltBody3D *body2 = reinterpret_cast<JoltBody3D *>(p_jolt_body2.GetUserData());
const bool has_surface_velocity1 = supports_surface_velocity1 && (body1->get_linear_surface_velocity() != Vector3() || body1->get_angular_surface_velocity() != Vector3());
const bool has_surface_velocity2 = supports_surface_velocity2 && (body2->get_linear_surface_velocity() != Vector3() || body2->get_angular_surface_velocity() != Vector3());
if (has_surface_velocity1 == has_surface_velocity2) {
return false;
}
const JPH::Vec3 linear_velocity1 = to_jolt(body1->get_linear_surface_velocity());
const JPH::Vec3 angular_velocity1 = to_jolt(body1->get_angular_surface_velocity());
const JPH::Vec3 linear_velocity2 = to_jolt(body2->get_linear_surface_velocity());
const JPH::Vec3 angular_velocity2 = to_jolt(body2->get_angular_surface_velocity());
const JPH::RVec3 com1 = p_jolt_body1.GetCenterOfMassPosition();
const JPH::RVec3 com2 = p_jolt_body2.GetCenterOfMassPosition();
const JPH::Vec3 rel_com2 = JPH::Vec3(com2 - com1);
const JPH::Vec3 angular_linear_velocity2 = rel_com2.Cross(angular_velocity2);
const JPH::Vec3 total_linear_velocity2 = linear_velocity2 + angular_linear_velocity2;
p_settings.mRelativeLinearSurfaceVelocity = total_linear_velocity2 - linear_velocity1;
p_settings.mRelativeAngularSurfaceVelocity = angular_velocity2 - angular_velocity1;
return true;
}
bool JoltContactListener3D::_try_add_contacts(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold, JPH::ContactSettings &p_settings) {
if (p_body1.IsSensor() || p_body2.IsSensor()) {
return false;
}
if (!_is_listening_for(p_body1) && !_is_listening_for(p_body2)) {
return false;
}
const JPH::SubShapeIDPair shape_pair(p_body1.GetID(), p_manifold.mSubShapeID1, p_body2.GetID(), p_manifold.mSubShapeID2);
Manifold &manifold = [&]() -> Manifold & {
const MutexLock write_lock(write_mutex);
return manifolds_by_shape_pair[shape_pair];
}();
const JPH::uint contact_count = p_manifold.mRelativeContactPointsOn1.size();
manifold.contacts1.reserve((uint32_t)contact_count);
manifold.contacts2.reserve((uint32_t)contact_count);
manifold.depth = p_manifold.mPenetrationDepth;
JPH::CollisionEstimationResult collision;
JPH::EstimateCollisionResponse(p_body1, p_body2, p_manifold, collision, p_settings.mCombinedFriction, p_settings.mCombinedRestitution, JoltProjectSettings::get_bounce_velocity_threshold(), 5);
for (JPH::uint i = 0; i < contact_count; ++i) {
const JPH::RVec3 relative_point1 = JPH::RVec3(p_manifold.mRelativeContactPointsOn1[i]);
const JPH::RVec3 relative_point2 = JPH::RVec3(p_manifold.mRelativeContactPointsOn2[i]);
const JPH::RVec3 world_point1 = p_manifold.mBaseOffset + relative_point1;
const JPH::RVec3 world_point2 = p_manifold.mBaseOffset + relative_point2;
const JPH::Vec3 velocity1 = p_body1.GetPointVelocity(world_point1);
const JPH::Vec3 velocity2 = p_body2.GetPointVelocity(world_point2);
const JPH::CollisionEstimationResult::Impulse &impulse = collision.mImpulses[i];
const JPH::Vec3 contact_impulse = p_manifold.mWorldSpaceNormal * impulse.mContactImpulse;
const JPH::Vec3 friction_impulse1 = collision.mTangent1 * impulse.mFrictionImpulse1;
const JPH::Vec3 friction_impulse2 = collision.mTangent2 * impulse.mFrictionImpulse2;
const JPH::Vec3 combined_impulse = contact_impulse + friction_impulse1 + friction_impulse2;
Contact contact1;
contact1.point_self = to_godot(world_point1);
contact1.point_other = to_godot(world_point2);
contact1.normal = to_godot(-p_manifold.mWorldSpaceNormal);
contact1.velocity_self = to_godot(velocity1);
contact1.velocity_other = to_godot(velocity2);
contact1.impulse = to_godot(-combined_impulse);
manifold.contacts1.push_back(contact1);
Contact contact2;
contact2.point_self = to_godot(world_point2);
contact2.point_other = to_godot(world_point1);
contact2.normal = to_godot(p_manifold.mWorldSpaceNormal);
contact2.velocity_self = to_godot(velocity2);
contact2.velocity_other = to_godot(velocity1);
contact2.impulse = to_godot(combined_impulse);
manifold.contacts2.push_back(contact2);
}
return true;
}
bool JoltContactListener3D::_try_evaluate_area_overlap(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold) {
if (!p_body1.IsSensor() && !p_body2.IsSensor()) {
return false;
}
auto evaluate = [&](const auto &p_area, const auto &p_object, const JPH::SubShapeIDPair &p_shape_pair) {
const MutexLock write_lock(write_mutex);
if (p_area.can_monitor(p_object)) {
if (!area_overlaps.has(p_shape_pair)) {
area_overlaps.insert(p_shape_pair);
area_enters.insert(p_shape_pair);
}
} else {
if (area_overlaps.erase(p_shape_pair)) {
area_exits.insert(p_shape_pair);
}
}
};
const JPH::SubShapeIDPair shape_pair1(p_body1.GetID(), p_manifold.mSubShapeID1, p_body2.GetID(), p_manifold.mSubShapeID2);
const JPH::SubShapeIDPair shape_pair2(p_body2.GetID(), p_manifold.mSubShapeID2, p_body1.GetID(), p_manifold.mSubShapeID1);
const JoltObject3D *object1 = reinterpret_cast<JoltObject3D *>(p_body1.GetUserData());
const JoltObject3D *object2 = reinterpret_cast<JoltObject3D *>(p_body2.GetUserData());
const JoltArea3D *area1 = object1->as_area();
const JoltArea3D *area2 = object2->as_area();
const JoltBody3D *body1 = object1->as_body();
const JoltBody3D *body2 = object2->as_body();
if (area1 != nullptr && area2 != nullptr) {
evaluate(*area1, *area2, shape_pair1);
evaluate(*area2, *area1, shape_pair2);
} else if (area1 != nullptr && body2 != nullptr) {
evaluate(*area1, *body2, shape_pair1);
} else if (area2 != nullptr && body1 != nullptr) {
evaluate(*area2, *body1, shape_pair2);
}
return true;
}
bool JoltContactListener3D::_try_remove_contacts(const JPH::SubShapeIDPair &p_shape_pair) {
const MutexLock write_lock(write_mutex);
return manifolds_by_shape_pair.erase(p_shape_pair);
}
bool JoltContactListener3D::_try_remove_area_overlap(const JPH::SubShapeIDPair &p_shape_pair) {
const JPH::SubShapeIDPair swapped_shape_pair(p_shape_pair.GetBody2ID(), p_shape_pair.GetSubShapeID2(), p_shape_pair.GetBody1ID(), p_shape_pair.GetSubShapeID1());
const MutexLock write_lock(write_mutex);
bool removed = false;
if (area_overlaps.erase(p_shape_pair)) {
area_exits.insert(p_shape_pair);
removed = true;
}
if (area_overlaps.erase(swapped_shape_pair)) {
area_exits.insert(swapped_shape_pair);
removed = true;
}
return removed;
}
#ifdef DEBUG_ENABLED
bool JoltContactListener3D::_try_add_debug_contacts(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold) {
if (p_body1.IsSensor() || p_body2.IsSensor()) {
return false;
}
const int64_t max_count = debug_contacts.size();
if (max_count == 0) {
return false;
}
const int additional_pairs = (int)p_manifold.mRelativeContactPointsOn1.size();
const int additional_contacts = additional_pairs * 2;
int current_count = debug_contact_count.load(std::memory_order_relaxed);
bool exchanged = false;
do {
const int new_count = current_count + additional_contacts;
if (new_count > max_count) {
return false;
}
exchanged = debug_contact_count.compare_exchange_weak(current_count, new_count, std::memory_order_release, std::memory_order_relaxed);
} while (!exchanged);
for (int i = 0; i < additional_pairs; ++i) {
const int pair_index = current_count + i * 2;
const JPH::RVec3 point_on_1 = p_manifold.GetWorldSpaceContactPointOn1((JPH::uint)i);
const JPH::RVec3 point_on_2 = p_manifold.GetWorldSpaceContactPointOn2((JPH::uint)i);
debug_contacts.write[pair_index + 0] = to_godot(point_on_1);
debug_contacts.write[pair_index + 1] = to_godot(point_on_2);
}
return true;
}
bool JoltContactListener3D::_try_add_debug_contacts(const JPH::Body &p_soft_body, const JPH::SoftBodyManifold &p_manifold) {
const int64_t max_count = debug_contacts.size();
if (max_count == 0) {
return false;
}
int additional_contacts = 0;
for (const JPH::SoftBodyVertex &vertex : p_manifold.GetVertices()) {
if (p_manifold.HasContact(vertex)) {
additional_contacts += 1;
}
}
int current_count = debug_contact_count.load(std::memory_order_relaxed);
bool exchanged = false;
do {
const int new_count = current_count + additional_contacts;
if (new_count > max_count) {
return false;
}
exchanged = debug_contact_count.compare_exchange_weak(current_count, new_count, std::memory_order_release, std::memory_order_relaxed);
} while (!exchanged);
int contact_index = current_count;
for (const JPH::SoftBodyVertex &vertex : p_manifold.GetVertices()) {
if (!p_manifold.HasContact(vertex)) {
continue;
}
const JPH::RMat44 body_com_transform = p_soft_body.GetCenterOfMassTransform();
const JPH::Vec3 local_contact_point = p_manifold.GetLocalContactPoint(vertex);
const JPH::RVec3 contact_point = body_com_transform * local_contact_point;
debug_contacts.write[contact_index++] = to_godot(contact_point);
}
return true;
}
#endif
void JoltContactListener3D::_flush_contacts() {
for (KeyValue<JPH::SubShapeIDPair, Manifold> &E : manifolds_by_shape_pair) {
const JPH::SubShapeIDPair &shape_pair = E.key;
Manifold &manifold = E.value;
const JPH::BodyID body_ids[2] = { shape_pair.GetBody1ID(), shape_pair.GetBody2ID() };
const JoltReadableBodies3D jolt_bodies = space->read_bodies(body_ids, 2);
JoltBody3D *body1 = jolt_bodies[0].as_body();
ERR_FAIL_NULL(body1);
JoltBody3D *body2 = jolt_bodies[1].as_body();
ERR_FAIL_NULL(body2);
const int shape_index1 = body1->find_shape_index(shape_pair.GetSubShapeID1());
const int shape_index2 = body2->find_shape_index(shape_pair.GetSubShapeID2());
for (const Contact &contact : manifold.contacts1) {
body1->add_contact(body2, manifold.depth, shape_index1, shape_index2, contact.normal, contact.point_self, contact.point_other, contact.velocity_self, contact.velocity_other, contact.impulse);
}
for (const Contact &contact : manifold.contacts2) {
body2->add_contact(body1, manifold.depth, shape_index2, shape_index1, contact.normal, contact.point_self, contact.point_other, contact.velocity_self, contact.velocity_other, contact.impulse);
}
manifold.contacts1.clear();
manifold.contacts2.clear();
}
}
void JoltContactListener3D::_flush_area_enters() {
for (const JPH::SubShapeIDPair &shape_pair : area_enters) {
const JPH::BodyID &body_id1 = shape_pair.GetBody1ID();
const JPH::BodyID &body_id2 = shape_pair.GetBody2ID();
const JPH::SubShapeID &sub_shape_id1 = shape_pair.GetSubShapeID1();
const JPH::SubShapeID &sub_shape_id2 = shape_pair.GetSubShapeID2();
const JPH::BodyID body_ids[2] = { body_id1, body_id2 };
const JoltReadableBodies3D jolt_bodies = space->read_bodies(body_ids, 2);
const JoltReadableBody3D jolt_body1 = jolt_bodies[0];
const JoltReadableBody3D jolt_body2 = jolt_bodies[1];
if (jolt_body1.is_invalid() || jolt_body2.is_invalid()) {
continue;
}
JoltArea3D *area1 = jolt_body1.as_area();
JoltArea3D *area2 = jolt_body2.as_area();
if (area1 != nullptr && area2 != nullptr) {
area1->area_shape_entered(body_id2, sub_shape_id2, sub_shape_id1);
} else if (area1 != nullptr && area2 == nullptr) {
area1->body_shape_entered(body_id2, sub_shape_id2, sub_shape_id1);
} else if (area1 == nullptr && area2 != nullptr) {
area2->body_shape_entered(body_id1, sub_shape_id1, sub_shape_id2);
}
}
area_enters.clear();
}
void JoltContactListener3D::_flush_area_shifts() {
for (const JPH::SubShapeIDPair &shape_pair : area_overlaps) {
auto is_shifted = [&](const JPH::BodyID &p_body_id, const JPH::SubShapeID &p_sub_shape_id) {
const JoltReadableBody3D jolt_body = space->read_body(p_body_id);
const JoltShapedObject3D *object = jolt_body.as_shaped();
ERR_FAIL_NULL_V(object, false);
if (object->get_previous_jolt_shape() == nullptr) {
return false;
}
const JPH::Shape &current_shape = *object->get_jolt_shape();
const JPH::Shape &previous_shape = *object->get_previous_jolt_shape();
const uint32_t current_id = (uint32_t)current_shape.GetSubShapeUserData(p_sub_shape_id);
const uint32_t previous_id = (uint32_t)previous_shape.GetSubShapeUserData(p_sub_shape_id);
return current_id != previous_id;
};
if (is_shifted(shape_pair.GetBody1ID(), shape_pair.GetSubShapeID1()) || is_shifted(shape_pair.GetBody2ID(), shape_pair.GetSubShapeID2())) {
area_enters.insert(shape_pair);
area_exits.insert(shape_pair);
}
}
}
void JoltContactListener3D::_flush_area_exits() {
for (const JPH::SubShapeIDPair &shape_pair : area_exits) {
const JPH::BodyID &body_id1 = shape_pair.GetBody1ID();
const JPH::BodyID &body_id2 = shape_pair.GetBody2ID();
const JPH::SubShapeID &sub_shape_id1 = shape_pair.GetSubShapeID1();
const JPH::SubShapeID &sub_shape_id2 = shape_pair.GetSubShapeID2();
const JPH::BodyID body_ids[2] = { body_id1, body_id2 };
const JoltReadableBodies3D jolt_bodies = space->read_bodies(body_ids, 2);
const JoltReadableBody3D jolt_body1 = jolt_bodies[0];
const JoltReadableBody3D jolt_body2 = jolt_bodies[1];
JoltArea3D *area1 = jolt_body1.as_area();
JoltArea3D *area2 = jolt_body2.as_area();
const JoltBody3D *body1 = jolt_body1.as_body();
const JoltBody3D *body2 = jolt_body2.as_body();
if (area1 != nullptr && area2 != nullptr) {
area1->area_shape_exited(body_id2, sub_shape_id2, sub_shape_id1);
} else if (area1 != nullptr && body2 != nullptr) {
area1->body_shape_exited(body_id2, sub_shape_id2, sub_shape_id1);
} else if (body1 != nullptr && area2 != nullptr) {
area2->body_shape_exited(body_id1, sub_shape_id1, sub_shape_id2);
} else if (area1 != nullptr) {
area1->shape_exited(body_id2, sub_shape_id2, sub_shape_id1);
} else if (area2 != nullptr) {
area2->shape_exited(body_id1, sub_shape_id1, sub_shape_id2);
}
}
area_exits.clear();
}
void JoltContactListener3D::listen_for(JoltShapedObject3D *p_object) {
listening_for.insert(p_object->get_jolt_id());
}
void JoltContactListener3D::pre_step() {
listening_for.clear();
#ifdef DEBUG_ENABLED
debug_contact_count = 0;
#endif
}
void JoltContactListener3D::post_step() {
_flush_contacts();
_flush_area_shifts();
_flush_area_exits();
_flush_area_enters();
}

View File

@@ -0,0 +1,147 @@
/**************************************************************************/
/* jolt_contact_listener_3d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef JOLT_CONTACT_LISTENER_3D_H
#define JOLT_CONTACT_LISTENER_3D_H
#include "core/templates/hash_map.h"
#include "core/templates/hash_set.h"
#include "core/templates/hashfuncs.h"
#include "core/templates/local_vector.h"
#include "core/templates/safe_refcount.h"
#include "core/variant/variant.h"
#include "Jolt/Jolt.h"
#include "Jolt/Physics/Body/Body.h"
#include "Jolt/Physics/Collision/ContactListener.h"
#include "Jolt/Physics/SoftBody/SoftBodyContactListener.h"
#include <stdint.h>
#include <new>
class JoltShapedObject3D;
class JoltSpace3D;
class JoltContactListener3D final
: public JPH::ContactListener,
public JPH::SoftBodyContactListener {
struct BodyIDHasher {
static uint32_t hash(const JPH::BodyID &p_id) { return hash_fmix32(p_id.GetIndexAndSequenceNumber()); }
};
struct ShapePairHasher {
static uint32_t hash(const JPH::SubShapeIDPair &p_pair) {
uint32_t hash = hash_murmur3_one_32(p_pair.GetBody1ID().GetIndexAndSequenceNumber());
hash = hash_murmur3_one_32(p_pair.GetSubShapeID1().GetValue(), hash);
hash = hash_murmur3_one_32(p_pair.GetBody2ID().GetIndexAndSequenceNumber(), hash);
hash = hash_murmur3_one_32(p_pair.GetSubShapeID2().GetValue(), hash);
return hash_fmix32(hash);
}
};
struct Contact {
Vector3 point_self;
Vector3 point_other;
Vector3 normal;
Vector3 velocity_self;
Vector3 velocity_other;
Vector3 impulse;
};
typedef LocalVector<Contact> Contacts;
struct Manifold {
Contacts contacts1;
Contacts contacts2;
float depth = 0.0f;
};
HashMap<JPH::SubShapeIDPair, Manifold, ShapePairHasher> manifolds_by_shape_pair;
HashSet<JPH::BodyID, BodyIDHasher> listening_for;
HashSet<JPH::SubShapeIDPair, ShapePairHasher> area_overlaps;
HashSet<JPH::SubShapeIDPair, ShapePairHasher> area_enters;
HashSet<JPH::SubShapeIDPair, ShapePairHasher> area_exits;
Mutex write_mutex;
JoltSpace3D *space = nullptr;
#ifdef DEBUG_ENABLED
PackedVector3Array debug_contacts;
std::atomic_int debug_contact_count;
#endif
virtual void OnContactAdded(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold, JPH::ContactSettings &p_settings) override;
virtual void OnContactPersisted(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold, JPH::ContactSettings &p_settings) override;
virtual void OnContactRemoved(const JPH::SubShapeIDPair &p_shape_pair) override;
virtual JPH::SoftBodyValidateResult OnSoftBodyContactValidate(const JPH::Body &p_soft_body, const JPH::Body &p_other_body, JPH::SoftBodyContactSettings &p_settings) override;
#ifdef DEBUG_ENABLED
virtual void OnSoftBodyContactAdded(const JPH::Body &p_soft_body, const JPH::SoftBodyManifold &p_manifold) override;
#endif
bool _is_listening_for(const JPH::Body &p_body) const;
bool _try_override_collision_response(const JPH::Body &p_jolt_body1, const JPH::Body &p_jolt_body2, JPH::ContactSettings &p_settings);
bool _try_override_collision_response(const JPH::Body &p_jolt_soft_body, const JPH::Body &p_jolt_other_body, JPH::SoftBodyContactSettings &p_settings);
bool _try_apply_surface_velocities(const JPH::Body &p_jolt_body1, const JPH::Body &p_jolt_body2, JPH::ContactSettings &p_settings);
bool _try_add_contacts(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold, JPH::ContactSettings &p_settings);
bool _try_evaluate_area_overlap(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold);
bool _try_remove_contacts(const JPH::SubShapeIDPair &p_shape_pair);
bool _try_remove_area_overlap(const JPH::SubShapeIDPair &p_shape_pair);
#ifdef DEBUG_ENABLED
bool _try_add_debug_contacts(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold);
bool _try_add_debug_contacts(const JPH::Body &p_soft_body, const JPH::SoftBodyManifold &p_manifold);
#endif
void _flush_contacts();
void _flush_area_enters();
void _flush_area_shifts();
void _flush_area_exits();
public:
explicit JoltContactListener3D(JoltSpace3D *p_space) :
space(p_space) {}
void listen_for(JoltShapedObject3D *p_object);
void pre_step();
void post_step();
#ifdef DEBUG_ENABLED
const PackedVector3Array &get_debug_contacts() const { return debug_contacts; }
int get_debug_contact_count() const { return debug_contact_count.load(std::memory_order_acquire); }
int get_max_debug_contacts() const { return (int)debug_contacts.size(); }
void set_max_debug_contacts(int p_count) { debug_contacts.resize(p_count); }
#endif
};
#endif // JOLT_CONTACT_LISTENER_3D_H

View File

@@ -0,0 +1,201 @@
/**************************************************************************/
/* jolt_job_system.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "jolt_job_system.h"
#include "../jolt_project_settings.h"
#include "core/debugger/engine_debugger.h"
#include "core/object/worker_thread_pool.h"
#include "core/os/os.h"
#include "core/os/time.h"
#include "Jolt/Physics/PhysicsSettings.h"
void JoltJobSystem::Job::_execute(void *p_user_data) {
Job *job = static_cast<Job *>(p_user_data);
#ifdef DEBUG_ENABLED
const uint64_t time_start = Time::get_singleton()->get_ticks_usec();
#endif
job->Execute();
#ifdef DEBUG_ENABLED
const uint64_t time_end = Time::get_singleton()->get_ticks_usec();
const uint64_t time_elapsed = time_end - time_start;
timings_lock.lock();
timings_by_job[job->name] += time_elapsed;
timings_lock.unlock();
#endif
job->Release();
}
JoltJobSystem::Job::Job(const char *p_name, JPH::ColorArg p_color, JPH::JobSystem *p_job_system, const JPH::JobSystem::JobFunction &p_job_function, JPH::uint32 p_dependency_count) :
JPH::JobSystem::Job(p_name, p_color, p_job_system, p_job_function, p_dependency_count)
#ifdef DEBUG_ENABLED
,
name(p_name)
#endif
{
}
JoltJobSystem::Job::~Job() {
if (task_id != -1) {
WorkerThreadPool::get_singleton()->wait_for_task_completion(task_id);
}
}
void JoltJobSystem::Job::push_completed(Job *p_job) {
Job *prev_head = nullptr;
do {
prev_head = completed_head.load(std::memory_order_relaxed);
p_job->completed_next = prev_head;
} while (!completed_head.compare_exchange_weak(prev_head, p_job, std::memory_order_release, std::memory_order_relaxed));
}
JoltJobSystem::Job *JoltJobSystem::Job::pop_completed() {
Job *prev_head = nullptr;
do {
prev_head = completed_head.load(std::memory_order_relaxed);
if (prev_head == nullptr) {
return nullptr;
}
} while (!completed_head.compare_exchange_weak(prev_head, prev_head->completed_next, std::memory_order_acquire, std::memory_order_relaxed));
return prev_head;
}
void JoltJobSystem::Job::queue() {
AddRef();
// Ideally we would use Jolt's actual job name here, but I'd rather not incur the overhead of a memory allocation or
// thread-safe lookup every time we create/queue a task. So instead we use the same cached description for all of them.
static const String task_name("Jolt Physics");
task_id = WorkerThreadPool::get_singleton()->add_native_task(&_execute, this, true, task_name);
}
int JoltJobSystem::GetMaxConcurrency() const {
return thread_count;
}
JPH::JobHandle JoltJobSystem::CreateJob(const char *p_name, JPH::ColorArg p_color, const JPH::JobSystem::JobFunction &p_job_function, JPH::uint32 p_dependency_count) {
Job *job = nullptr;
while (true) {
JPH::uint32 job_index = jobs.ConstructObject(p_name, p_color, this, p_job_function, p_dependency_count);
if (job_index != JPH::FixedSizeFreeList<Job>::cInvalidObjectIndex) {
job = &jobs.Get(job_index);
break;
}
WARN_PRINT_ONCE("Jolt Physics job system exceeded the maximum number of jobs. This should not happen. Please report this. Waiting for jobs to become available...");
OS::get_singleton()->delay_usec(100);
_reclaim_jobs();
}
// This will increment the job's reference count, so must happen before we queue the job
JPH::JobHandle job_handle(job);
if (p_dependency_count == 0) {
QueueJob(job);
}
return job_handle;
}
void JoltJobSystem::QueueJob(JPH::JobSystem::Job *p_job) {
static_cast<Job *>(p_job)->queue();
}
void JoltJobSystem::QueueJobs(JPH::JobSystem::Job **p_jobs, JPH::uint p_job_count) {
for (JPH::uint i = 0; i < p_job_count; ++i) {
QueueJob(p_jobs[i]);
}
}
void JoltJobSystem::FreeJob(JPH::JobSystem::Job *p_job) {
Job::push_completed(static_cast<Job *>(p_job));
}
void JoltJobSystem::_reclaim_jobs() {
while (Job *job = Job::pop_completed()) {
jobs.DestructObject(job);
}
}
JoltJobSystem::JoltJobSystem() :
JPH::JobSystemWithBarrier(JPH::cMaxPhysicsBarriers),
thread_count(MAX(1, WorkerThreadPool::get_singleton()->get_thread_count())) {
jobs.Init(JPH::cMaxPhysicsJobs, JPH::cMaxPhysicsJobs);
}
void JoltJobSystem::pre_step() {
// Nothing to do.
}
void JoltJobSystem::post_step() {
_reclaim_jobs();
}
#ifdef DEBUG_ENABLED
void JoltJobSystem::flush_timings() {
static const StringName profiler_name("servers");
EngineDebugger *engine_debugger = EngineDebugger::get_singleton();
if (engine_debugger->is_profiling(profiler_name)) {
Array timings;
for (const KeyValue<const void *, uint64_t> &E : timings_by_job) {
timings.push_back(static_cast<const char *>(E.key));
timings.push_back(USEC_TO_SEC(E.value));
}
timings.push_front("physics_3d");
engine_debugger->profiler_add_frame_data(profiler_name, timings);
}
for (KeyValue<const void *, uint64_t> &E : timings_by_job) {
E.value = 0;
}
}
#endif

View File

@@ -0,0 +1,107 @@
/**************************************************************************/
/* jolt_job_system.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef JOLT_JOB_SYSTEM_H
#define JOLT_JOB_SYSTEM_H
#include "core/os/spin_lock.h"
#include "core/templates/hash_map.h"
#include "Jolt/Jolt.h"
#include "Jolt/Core/FixedSizeFreeList.h"
#include "Jolt/Core/JobSystemWithBarrier.h"
#include <stdint.h>
#include <atomic>
class JoltJobSystem final : public JPH::JobSystemWithBarrier {
class Job : public JPH::JobSystem::Job {
inline static std::atomic<Job *> completed_head = nullptr;
#ifdef DEBUG_ENABLED
const char *name = nullptr;
#endif
int64_t task_id = -1;
std::atomic<Job *> completed_next = nullptr;
static void _execute(void *p_user_data);
public:
Job(const char *p_name, JPH::ColorArg p_color, JPH::JobSystem *p_job_system, const JPH::JobSystem::JobFunction &p_job_function, JPH::uint32 p_dependency_count);
Job(const Job &p_other) = delete;
Job(Job &&p_other) = delete;
~Job();
static void push_completed(Job *p_job);
static Job *pop_completed();
void queue();
Job &operator=(const Job &p_other) = delete;
Job &operator=(Job &&p_other) = delete;
};
#ifdef DEBUG_ENABLED
// We use `const void*` here to avoid the cost of hashing the actual string, since the job names
// are always literals and as such will point to the same address every time.
inline static HashMap<const void *, uint64_t> timings_by_job;
// TODO: Check whether the usage of SpinLock is justified or if this should be a mutex instead.
inline static SpinLock timings_lock;
#endif
JPH::FixedSizeFreeList<Job> jobs;
int thread_count = 0;
virtual int GetMaxConcurrency() const override;
virtual JPH::JobHandle CreateJob(const char *p_name, JPH::ColorArg p_color, const JPH::JobSystem::JobFunction &p_job_function, JPH::uint32 p_dependency_count = 0) override;
virtual void QueueJob(JPH::JobSystem::Job *p_job) override;
virtual void QueueJobs(JPH::JobSystem::Job **p_jobs, JPH::uint p_job_count) override;
virtual void FreeJob(JPH::JobSystem::Job *p_job) override;
void _reclaim_jobs();
public:
JoltJobSystem();
void pre_step();
void post_step();
#ifdef DEBUG_ENABLED
void flush_timings();
#endif
};
#endif // JOLT_JOB_SYSTEM_H

View File

@@ -0,0 +1,223 @@
/**************************************************************************/
/* jolt_layers.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "jolt_layers.h"
#include "../jolt_project_settings.h"
#include "jolt_broad_phase_layer.h"
#include "core/error/error_macros.h"
#include "core/variant/variant.h"
static_assert(sizeof(JPH::ObjectLayer) == 2, "Size of Jolt's object layer has changed.");
static_assert(sizeof(JPH::BroadPhaseLayer::Type) == 1, "Size of Jolt's broadphase layer has changed.");
static_assert(JoltBroadPhaseLayer::COUNT <= 8, "Maximum number of broadphase layers exceeded.");
namespace {
template <uint8_t TSize = JoltBroadPhaseLayer::COUNT>
class JoltBroadPhaseMatrix {
typedef JPH::BroadPhaseLayer LayerType;
typedef LayerType::Type UnderlyingType;
public:
JoltBroadPhaseMatrix() {
using namespace JoltBroadPhaseLayer;
allow_collision(BODY_STATIC, BODY_DYNAMIC);
allow_collision(BODY_STATIC_BIG, BODY_DYNAMIC);
allow_collision(BODY_DYNAMIC, BODY_STATIC);
allow_collision(BODY_DYNAMIC, BODY_STATIC_BIG);
allow_collision(BODY_DYNAMIC, BODY_DYNAMIC);
allow_collision(BODY_DYNAMIC, AREA_DETECTABLE);
allow_collision(BODY_DYNAMIC, AREA_UNDETECTABLE);
allow_collision(AREA_DETECTABLE, BODY_DYNAMIC);
allow_collision(AREA_DETECTABLE, AREA_DETECTABLE);
allow_collision(AREA_DETECTABLE, AREA_UNDETECTABLE);
allow_collision(AREA_UNDETECTABLE, BODY_DYNAMIC);
allow_collision(AREA_UNDETECTABLE, AREA_DETECTABLE);
if (JoltProjectSettings::areas_detect_static_bodies()) {
allow_collision(BODY_STATIC, AREA_DETECTABLE);
allow_collision(BODY_STATIC, AREA_UNDETECTABLE);
allow_collision(BODY_STATIC_BIG, AREA_DETECTABLE);
allow_collision(BODY_STATIC_BIG, AREA_UNDETECTABLE);
allow_collision(AREA_DETECTABLE, BODY_STATIC);
allow_collision(AREA_DETECTABLE, BODY_STATIC_BIG);
allow_collision(AREA_UNDETECTABLE, BODY_STATIC);
allow_collision(AREA_UNDETECTABLE, BODY_STATIC_BIG);
}
}
void allow_collision(UnderlyingType p_layer1, UnderlyingType p_layer2) { masks[p_layer1] |= uint8_t(1U << p_layer2); }
void allow_collision(LayerType p_layer1, LayerType p_layer2) { allow_collision((UnderlyingType)p_layer1, (UnderlyingType)p_layer2); }
bool should_collide(UnderlyingType p_layer1, UnderlyingType p_layer2) const { return (masks[p_layer1] & uint8_t(1U << p_layer2)) != 0; }
bool should_collide(LayerType p_layer1, LayerType p_layer2) const { return should_collide((UnderlyingType)p_layer1, (UnderlyingType)p_layer2); }
private:
uint8_t masks[TSize] = {};
};
constexpr JPH::ObjectLayer encode_layers(JPH::BroadPhaseLayer p_broad_phase_layer, JPH::ObjectLayer p_object_layer) {
const uint16_t upper_bits = uint16_t((uint8_t)p_broad_phase_layer << 13U);
const uint16_t lower_bits = uint16_t(p_object_layer);
return JPH::ObjectLayer(upper_bits | lower_bits);
}
constexpr void decode_layers(JPH::ObjectLayer p_encoded_layers, JPH::BroadPhaseLayer &r_broad_phase_layer, JPH::ObjectLayer &r_object_layer) {
r_broad_phase_layer = JPH::BroadPhaseLayer(uint8_t(p_encoded_layers >> 13U));
r_object_layer = JPH::ObjectLayer(p_encoded_layers & 0b0001'1111'1111'1111U);
}
constexpr uint64_t encode_collision(uint32_t p_collision_layer, uint32_t p_collision_mask) {
const uint64_t upper_bits = (uint64_t)p_collision_layer << 32U;
const uint64_t lower_bits = (uint64_t)p_collision_mask;
return upper_bits | lower_bits;
}
constexpr void decode_collision(uint64_t p_collision, uint32_t &r_collision_layer, uint32_t &r_collision_mask) {
r_collision_layer = uint32_t(p_collision >> 32U);
r_collision_mask = uint32_t(p_collision & 0xFFFFFFFFU);
}
} // namespace
uint32_t JoltLayers::GetNumBroadPhaseLayers() const {
return JoltBroadPhaseLayer::COUNT;
}
JPH::BroadPhaseLayer JoltLayers::GetBroadPhaseLayer(JPH::ObjectLayer p_layer) const {
JPH::BroadPhaseLayer broad_phase_layer = JoltBroadPhaseLayer::BODY_STATIC;
JPH::ObjectLayer object_layer = 0;
decode_layers(p_layer, broad_phase_layer, object_layer);
return broad_phase_layer;
}
#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
const char *JoltLayers::GetBroadPhaseLayerName(JPH::BroadPhaseLayer p_layer) const {
switch ((JPH::BroadPhaseLayer::Type)p_layer) {
case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::BODY_STATIC: {
return "BODY_STATIC";
}
case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::BODY_STATIC_BIG: {
return "BODY_STATIC_BIG";
}
case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::BODY_DYNAMIC: {
return "BODY_DYNAMIC";
}
case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::AREA_DETECTABLE: {
return "AREA_DETECTABLE";
}
case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::AREA_UNDETECTABLE: {
return "AREA_UNDETECTABLE";
}
default: {
return "UNKNOWN";
}
}
}
#endif
bool JoltLayers::ShouldCollide(JPH::ObjectLayer p_encoded_layer1, JPH::ObjectLayer p_encoded_layer2) const {
JPH::BroadPhaseLayer broad_phase_layer1 = JoltBroadPhaseLayer::BODY_STATIC;
uint32_t collision_layer1 = 0;
uint32_t collision_mask1 = 0;
from_object_layer(p_encoded_layer1, broad_phase_layer1, collision_layer1, collision_mask1);
JPH::BroadPhaseLayer broad_phase_layer2 = JoltBroadPhaseLayer::BODY_STATIC;
uint32_t collision_layer2 = 0;
uint32_t collision_mask2 = 0;
from_object_layer(p_encoded_layer2, broad_phase_layer2, collision_layer2, collision_mask2);
const bool first_scans_second = (collision_mask1 & collision_layer2) != 0;
const bool second_scans_first = (collision_mask2 & collision_layer1) != 0;
return first_scans_second || second_scans_first;
}
bool JoltLayers::ShouldCollide(JPH::ObjectLayer p_encoded_layer1, JPH::BroadPhaseLayer p_broad_phase_layer2) const {
static const JoltBroadPhaseMatrix matrix;
JPH::BroadPhaseLayer broad_phase_layer1 = JoltBroadPhaseLayer::BODY_STATIC;
JPH::ObjectLayer object_layer1 = 0;
decode_layers(p_encoded_layer1, broad_phase_layer1, object_layer1);
return matrix.should_collide(broad_phase_layer1, p_broad_phase_layer2);
}
JPH::ObjectLayer JoltLayers::_allocate_object_layer(uint64_t p_collision) {
const JPH::ObjectLayer new_object_layer = next_object_layer++;
collisions_by_layer.resize(new_object_layer + 1);
collisions_by_layer[new_object_layer] = p_collision;
layers_by_collision[p_collision] = new_object_layer;
return new_object_layer;
}
JoltLayers::JoltLayers() {
_allocate_object_layer(0);
}
JPH::ObjectLayer JoltLayers::to_object_layer(JPH::BroadPhaseLayer p_broad_phase_layer, uint32_t p_collision_layer, uint32_t p_collision_mask) {
const uint64_t collision = encode_collision(p_collision_layer, p_collision_mask);
JPH::ObjectLayer object_layer = 0;
HashMap<uint64_t, JPH::ObjectLayer>::Iterator iter = layers_by_collision.find(collision);
if (iter != layers_by_collision.end()) {
object_layer = iter->value;
} else {
constexpr uint16_t object_layer_count = 1U << 13U;
ERR_FAIL_COND_V_MSG(next_object_layer == object_layer_count, 0,
vformat("Maximum number of object layers (%d) reached. "
"This means there are %d combinations of collision layers and masks."
"This should not happen under normal circumstances. Consider reporting this.",
object_layer_count, object_layer_count));
object_layer = _allocate_object_layer(collision);
}
return encode_layers(p_broad_phase_layer, object_layer);
}
void JoltLayers::from_object_layer(JPH::ObjectLayer p_encoded_layer, JPH::BroadPhaseLayer &r_broad_phase_layer, uint32_t &r_collision_layer, uint32_t &r_collision_mask) const {
JPH::ObjectLayer object_layer = 0;
decode_layers(p_encoded_layer, r_broad_phase_layer, object_layer);
const uint64_t collision = collisions_by_layer[object_layer];
decode_collision(collision, r_collision_layer, r_collision_mask);
}

View File

@@ -0,0 +1,71 @@
/**************************************************************************/
/* jolt_layers.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef JOLT_LAYERS_H
#define JOLT_LAYERS_H
#include "core/templates/hash_map.h"
#include "core/templates/local_vector.h"
#include "Jolt/Jolt.h"
#include "Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h"
#include "Jolt/Physics/Collision/ObjectLayer.h"
#include <stdint.h>
class JoltLayers final
: public JPH::BroadPhaseLayerInterface,
public JPH::ObjectLayerPairFilter,
public JPH::ObjectVsBroadPhaseLayerFilter {
LocalVector<uint64_t> collisions_by_layer;
HashMap<uint64_t, JPH::ObjectLayer> layers_by_collision;
JPH::ObjectLayer next_object_layer = 0;
virtual uint32_t GetNumBroadPhaseLayers() const override;
virtual JPH::BroadPhaseLayer GetBroadPhaseLayer(JPH::ObjectLayer p_layer) const override;
#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
virtual const char *GetBroadPhaseLayerName(JPH::BroadPhaseLayer p_layer) const override;
#endif
virtual bool ShouldCollide(JPH::ObjectLayer p_encoded_layer1, JPH::ObjectLayer p_encoded_layer2) const override;
virtual bool ShouldCollide(JPH::ObjectLayer p_encoded_layer1, JPH::BroadPhaseLayer p_broad_phase_layer2) const override;
JPH::ObjectLayer _allocate_object_layer(uint64_t p_collision);
public:
JoltLayers();
JPH::ObjectLayer to_object_layer(JPH::BroadPhaseLayer p_broad_phase_layer, uint32_t p_collision_layer, uint32_t p_collision_mask);
void from_object_layer(JPH::ObjectLayer p_encoded_layer, JPH::BroadPhaseLayer &r_broad_phase_layer, uint32_t &r_collision_layer, uint32_t &r_collision_mask) const;
};
#endif // JOLT_LAYERS_H

View File

@@ -0,0 +1,114 @@
/**************************************************************************/
/* jolt_motion_filter_3d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "jolt_motion_filter_3d.h"
#include "../objects/jolt_body_3d.h"
#include "../objects/jolt_object_3d.h"
#include "../shapes/jolt_custom_motion_shape.h"
#include "../shapes/jolt_custom_ray_shape.h"
#include "../shapes/jolt_custom_shape_type.h"
#include "../shapes/jolt_shape_3d.h"
#include "jolt_broad_phase_layer.h"
#include "jolt_space_3d.h"
JoltMotionFilter3D::JoltMotionFilter3D(const JoltBody3D &p_body, const HashSet<RID> &p_excluded_bodies, const HashSet<ObjectID> &p_excluded_objects, bool p_collide_separation_ray) :
body_self(p_body),
space(*body_self.get_space()),
excluded_bodies(p_excluded_bodies),
excluded_objects(p_excluded_objects),
collide_separation_ray(p_collide_separation_ray) {
}
bool JoltMotionFilter3D::ShouldCollide(JPH::BroadPhaseLayer p_broad_phase_layer) const {
const JPH::BroadPhaseLayer::Type broad_phase_layer = (JPH::BroadPhaseLayer::Type)p_broad_phase_layer;
switch (broad_phase_layer) {
case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::BODY_STATIC:
case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::BODY_STATIC_BIG:
case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::BODY_DYNAMIC: {
return true;
} break;
case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::AREA_DETECTABLE:
case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::AREA_UNDETECTABLE: {
return false;
} break;
default: {
ERR_FAIL_V_MSG(false, vformat("Unhandled broad phase layer: '%d'. This should not happen. Please report this.", broad_phase_layer));
}
}
}
bool JoltMotionFilter3D::ShouldCollide(JPH::ObjectLayer p_object_layer) const {
JPH::BroadPhaseLayer object_broad_phase_layer = JoltBroadPhaseLayer::BODY_STATIC;
uint32_t object_collision_layer = 0;
uint32_t object_collision_mask = 0;
space.map_from_object_layer(p_object_layer, object_broad_phase_layer, object_collision_layer, object_collision_mask);
return (body_self.get_collision_mask() & object_collision_layer) != 0;
}
bool JoltMotionFilter3D::ShouldCollide(const JPH::BodyID &p_jolt_id) const {
return p_jolt_id != body_self.get_jolt_id();
}
bool JoltMotionFilter3D::ShouldCollideLocked(const JPH::Body &p_jolt_body) const {
if (p_jolt_body.IsSoftBody()) {
return false;
}
const JoltObject3D *object = reinterpret_cast<const JoltObject3D *>(p_jolt_body.GetUserData());
if (excluded_objects.has(object->get_instance_id()) || excluded_bodies.has(object->get_rid())) {
return false;
}
const JoltReadableBody3D jolt_body_self = space.read_body(body_self);
return jolt_body_self->GetCollisionGroup().CanCollide(p_jolt_body.GetCollisionGroup());
}
bool JoltMotionFilter3D::ShouldCollide(const JPH::Shape *p_jolt_shape, const JPH::SubShapeID &p_jolt_shape_id) const {
return true;
}
bool JoltMotionFilter3D::ShouldCollide(const JPH::Shape *p_jolt_shape_self, const JPH::SubShapeID &p_jolt_shape_id_self, const JPH::Shape *p_jolt_shape_other, const JPH::SubShapeID &p_jolt_shape_id_other) const {
if (collide_separation_ray) {
return true;
}
const JoltCustomMotionShape *motion_shape = static_cast<const JoltCustomMotionShape *>(p_jolt_shape_self);
const JPH::ConvexShape &actual_shape_self = motion_shape->get_inner_shape();
if (actual_shape_self.GetSubType() == JoltCustomShapeSubType::RAY) {
// When `slide_on_slope` is enabled the ray shape acts as a regular shape.
return static_cast<const JoltCustomRayShape &>(actual_shape_self).slide_on_slope;
}
return true;
}

View File

@@ -0,0 +1,72 @@
/**************************************************************************/
/* jolt_motion_filter_3d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef JOLT_MOTION_FILTER_3D_H
#define JOLT_MOTION_FILTER_3D_H
#include "core/object/object_id.h"
#include "core/templates/hash_set.h"
#include "core/templates/rid.h"
#include "Jolt/Jolt.h"
#include "Jolt/Physics/Body/Body.h"
#include "Jolt/Physics/Body/BodyFilter.h"
#include "Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h"
#include "Jolt/Physics/Collision/ObjectLayer.h"
#include "Jolt/Physics/Collision/ShapeFilter.h"
class JoltBody3D;
class JoltPhysicsServer3D;
class JoltSpace3D;
class JoltMotionFilter3D final
: public JPH::BroadPhaseLayerFilter,
public JPH::ObjectLayerFilter,
public JPH::BodyFilter,
public JPH::ShapeFilter {
const JoltBody3D &body_self;
const JoltSpace3D &space;
const HashSet<RID> &excluded_bodies;
const HashSet<ObjectID> &excluded_objects;
bool collide_separation_ray = false;
public:
explicit JoltMotionFilter3D(const JoltBody3D &p_body, const HashSet<RID> &p_excluded_bodies, const HashSet<ObjectID> &p_excluded_objects, bool p_collide_separation_ray = true);
virtual bool ShouldCollide(JPH::BroadPhaseLayer p_broad_phase_layer) const override;
virtual bool ShouldCollide(JPH::ObjectLayer p_object_layer) const override;
virtual bool ShouldCollide(const JPH::BodyID &p_jolt_id) const override;
virtual bool ShouldCollideLocked(const JPH::Body &p_jolt_body) const override;
virtual bool ShouldCollide(const JPH::Shape *p_jolt_shape, const JPH::SubShapeID &p_jolt_shape_id) const override;
virtual bool ShouldCollide(const JPH::Shape *p_jolt_shape_self, const JPH::SubShapeID &p_jolt_shape_id_self, const JPH::Shape *p_jolt_shape_other, const JPH::SubShapeID &p_jolt_shape_id_other) const override;
};
#endif // JOLT_MOTION_FILTER_3D_H

View File

@@ -0,0 +1,936 @@
/**************************************************************************/
/* jolt_physics_direct_space_state_3d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "jolt_physics_direct_space_state_3d.h"
#include "../jolt_physics_server_3d.h"
#include "../jolt_project_settings.h"
#include "../misc/jolt_type_conversions.h"
#include "../objects/jolt_area_3d.h"
#include "../objects/jolt_body_3d.h"
#include "../objects/jolt_object_3d.h"
#include "../shapes/jolt_custom_motion_shape.h"
#include "../shapes/jolt_shape_3d.h"
#include "jolt_motion_filter_3d.h"
#include "jolt_query_collectors.h"
#include "jolt_query_filter_3d.h"
#include "jolt_space_3d.h"
#include "Jolt/Geometry/GJKClosestPoint.h"
#include "Jolt/Physics/Body/Body.h"
#include "Jolt/Physics/Body/BodyFilter.h"
#include "Jolt/Physics/Collision/BroadPhase/BroadPhaseQuery.h"
#include "Jolt/Physics/Collision/CastResult.h"
#include "Jolt/Physics/Collision/CollidePointResult.h"
#include "Jolt/Physics/Collision/NarrowPhaseQuery.h"
#include "Jolt/Physics/Collision/RayCast.h"
#include "Jolt/Physics/Collision/Shape/MeshShape.h"
#include "Jolt/Physics/PhysicsSystem.h"
bool JoltPhysicsDirectSpaceState3D::_cast_motion_impl(const JPH::Shape &p_jolt_shape, const Transform3D &p_transform_com, const Vector3 &p_scale, const Vector3 &p_motion, bool p_use_edge_removal, bool p_ignore_overlaps, const JPH::CollideShapeSettings &p_settings, const JPH::BroadPhaseLayerFilter &p_broad_phase_layer_filter, const JPH::ObjectLayerFilter &p_object_layer_filter, const JPH::BodyFilter &p_body_filter, const JPH::ShapeFilter &p_shape_filter, real_t &r_closest_safe, real_t &r_closest_unsafe) const {
r_closest_safe = 1.0f;
r_closest_unsafe = 1.0f;
ERR_FAIL_COND_V_MSG(p_jolt_shape.GetType() != JPH::EShapeType::Convex, false, "Shape-casting with non-convex shapes is not supported.");
const float motion_length = (float)p_motion.length();
if (p_ignore_overlaps && motion_length == 0.0f) {
return false;
}
const JPH::RMat44 transform_com = to_jolt_r(p_transform_com);
const JPH::Vec3 scale = to_jolt(p_scale);
const JPH::Vec3 motion = to_jolt(p_motion);
const JPH::Vec3 motion_local = transform_com.Multiply3x3Transposed(motion);
JPH::AABox aabb = p_jolt_shape.GetWorldSpaceBounds(transform_com, scale);
JPH::AABox aabb_translated = aabb;
aabb_translated.Translate(motion);
aabb.Encapsulate(aabb_translated);
JoltQueryCollectorAnyMulti<JPH::CollideShapeBodyCollector, 2048> aabb_collector;
space->get_broad_phase_query().CollideAABox(aabb, aabb_collector, p_broad_phase_layer_filter, p_object_layer_filter);
if (!aabb_collector.had_hit()) {
return false;
}
const JPH::RVec3 base_offset = transform_com.GetTranslation();
JoltCustomMotionShape motion_shape(static_cast<const JPH::ConvexShape &>(p_jolt_shape));
auto collides = [&](const JPH::Body &p_other_body, float p_fraction) {
motion_shape.set_motion(motion_local * p_fraction);
const JPH::TransformedShape other_shape = p_other_body.GetTransformedShape();
JoltQueryCollectorAny<JPH::CollideShapeCollector> collector;
if (p_use_edge_removal) {
JPH::CollideShapeSettings eier_settings = p_settings;
eier_settings.mActiveEdgeMode = JPH::EActiveEdgeMode::CollideWithAll;
eier_settings.mCollectFacesMode = JPH::ECollectFacesMode::CollectFaces;
JPH::InternalEdgeRemovingCollector eier_collector(collector);
other_shape.CollideShape(&motion_shape, scale, transform_com, eier_settings, base_offset, eier_collector, p_shape_filter);
eier_collector.Flush();
} else {
other_shape.CollideShape(&motion_shape, scale, transform_com, p_settings, base_offset, collector, p_shape_filter);
}
return collector.had_hit();
};
// Figure out the number of steps we need in our binary search in order to achieve millimeter precision, within reason.
const int step_count = CLAMP(int(logf(1000.0f * motion_length) / (float)Math_LN2), 4, 16);
bool collided = false;
for (int i = 0; i < aabb_collector.get_hit_count(); ++i) {
const JPH::BodyID other_jolt_id = aabb_collector.get_hit(i);
if (!p_body_filter.ShouldCollide(other_jolt_id)) {
continue;
}
const JoltReadableBody3D other_jolt_body = space->read_body(other_jolt_id);
if (!p_body_filter.ShouldCollideLocked(*other_jolt_body)) {
continue;
}
if (!collides(*other_jolt_body, 1.0f)) {
continue;
}
if (p_ignore_overlaps && collides(*other_jolt_body, 0.0f)) {
continue;
}
float lo = 0.0f;
float hi = 1.0f;
float coeff = 0.5f;
for (int j = 0; j < step_count; ++j) {
const float fraction = lo + (hi - lo) * coeff;
if (collides(*other_jolt_body, fraction)) {
collided = true;
hi = fraction;
if (j == 0 || lo > 0.0f) {
coeff = 0.5f;
} else {
coeff = 0.25f;
}
} else {
lo = fraction;
if (j == 0 || hi < 1.0f) {
coeff = 0.5f;
} else {
coeff = 0.75f;
}
}
}
if (lo < r_closest_safe) {
r_closest_safe = lo;
r_closest_unsafe = hi;
}
}
return collided;
}
bool JoltPhysicsDirectSpaceState3D::_body_motion_recover(const JoltBody3D &p_body, const Transform3D &p_transform, float p_margin, const HashSet<RID> &p_excluded_bodies, const HashSet<ObjectID> &p_excluded_objects, Vector3 &r_recovery) const {
const int recovery_iterations = JoltProjectSettings::get_motion_query_recovery_iterations();
const float recovery_amount = JoltProjectSettings::get_motion_query_recovery_amount();
const JPH::Shape *jolt_shape = p_body.get_jolt_shape();
const Vector3 com_scaled = to_godot(jolt_shape->GetCenterOfMass());
Transform3D transform_com = p_transform.translated_local(com_scaled);
JPH::CollideShapeSettings settings;
settings.mMaxSeparationDistance = p_margin;
const Vector3 &base_offset = transform_com.origin;
const JoltMotionFilter3D motion_filter(p_body, p_excluded_bodies, p_excluded_objects);
JoltQueryCollectorAnyMulti<JPH::CollideShapeCollector, 32> collector;
bool recovered = false;
for (int i = 0; i < recovery_iterations; ++i) {
collector.reset();
_collide_shape_kinematics(jolt_shape, JPH::Vec3::sReplicate(1.0f), to_jolt_r(transform_com), settings, to_jolt_r(base_offset), collector, motion_filter, motion_filter, motion_filter, motion_filter);
if (!collector.had_hit()) {
break;
}
const int hit_count = collector.get_hit_count();
float combined_priority = 0.0;
for (int j = 0; j < hit_count; j++) {
const JPH::CollideShapeResult &hit = collector.get_hit(j);
const JoltReadableBody3D other_jolt_body = space->read_body(hit.mBodyID2);
const JoltBody3D *other_body = other_jolt_body.as_body();
ERR_CONTINUE(other_body == nullptr);
combined_priority += other_body->get_collision_priority();
}
const float average_priority = MAX(combined_priority / (float)hit_count, (float)CMP_EPSILON);
recovered = true;
Vector3 recovery;
for (int j = 0; j < hit_count; ++j) {
const JPH::CollideShapeResult &hit = collector.get_hit(j);
const Vector3 penetration_axis = to_godot(hit.mPenetrationAxis.Normalized());
const Vector3 margin_offset = penetration_axis * p_margin;
const Vector3 point_on_1 = base_offset + to_godot(hit.mContactPointOn1) + margin_offset;
const Vector3 point_on_2 = base_offset + to_godot(hit.mContactPointOn2);
const real_t distance_to_1 = penetration_axis.dot(point_on_1 + recovery);
const real_t distance_to_2 = penetration_axis.dot(point_on_2);
const float penetration_depth = float(distance_to_1 - distance_to_2);
if (penetration_depth <= 0.0f) {
continue;
}
const JoltReadableBody3D other_jolt_body = space->read_body(hit.mBodyID2);
const JoltBody3D *other_body = other_jolt_body.as_body();
ERR_CONTINUE(other_body == nullptr);
const float recovery_distance = penetration_depth * recovery_amount;
const float other_priority = other_body->get_collision_priority();
const float other_priority_normalized = other_priority / average_priority;
const float scaled_recovery_distance = recovery_distance * other_priority_normalized;
recovery -= penetration_axis * scaled_recovery_distance;
}
if (recovery == Vector3()) {
break;
}
r_recovery += recovery;
transform_com.origin += recovery;
}
return recovered;
}
bool JoltPhysicsDirectSpaceState3D::_body_motion_cast(const JoltBody3D &p_body, const Transform3D &p_transform, const Vector3 &p_scale, const Vector3 &p_motion, bool p_collide_separation_ray, const HashSet<RID> &p_excluded_bodies, const HashSet<ObjectID> &p_excluded_objects, real_t &r_safe_fraction, real_t &r_unsafe_fraction) const {
const Transform3D body_transform = p_transform.scaled_local(p_scale);
const JPH::CollideShapeSettings settings;
const JoltMotionFilter3D motion_filter(p_body, p_excluded_bodies, p_excluded_objects, p_collide_separation_ray);
bool collided = false;
for (int i = 0; i < p_body.get_shape_count(); ++i) {
if (p_body.is_shape_disabled(i)) {
continue;
}
JoltShape3D *shape = p_body.get_shape(i);
if (!shape->is_convex()) {
continue;
}
const JPH::ShapeRefC jolt_shape = shape->try_build();
if (unlikely(jolt_shape == nullptr)) {
return false;
}
const Vector3 com_scaled = to_godot(jolt_shape->GetCenterOfMass());
const Transform3D transform_local = p_body.get_shape_transform_scaled(i);
const Transform3D transform_com_local = transform_local.translated_local(com_scaled);
Transform3D transform_com = body_transform * transform_com_local;
Vector3 scale = transform_com.basis.get_scale();
JOLT_ENSURE_SCALE_VALID(jolt_shape, scale, "body_test_motion was passed an invalid transform along with body '%s'. This results in invalid scaling for shape at index %d.");
transform_com.basis.orthonormalize();
real_t shape_safe_fraction = 1.0;
real_t shape_unsafe_fraction = 1.0;
collided |= _cast_motion_impl(*jolt_shape, transform_com, scale, p_motion, JoltProjectSettings::use_enhanced_internal_edge_removal_for_motion_queries(), false, settings, motion_filter, motion_filter, motion_filter, motion_filter, shape_safe_fraction, shape_unsafe_fraction);
r_safe_fraction = MIN(r_safe_fraction, shape_safe_fraction);
r_unsafe_fraction = MIN(r_unsafe_fraction, shape_unsafe_fraction);
}
return collided;
}
bool JoltPhysicsDirectSpaceState3D::_body_motion_collide(const JoltBody3D &p_body, const Transform3D &p_transform, const Vector3 &p_motion, float p_margin, int p_max_collisions, const HashSet<RID> &p_excluded_bodies, const HashSet<ObjectID> &p_excluded_objects, PhysicsServer3D::MotionResult *p_result) const {
if (p_max_collisions == 0) {
return false;
}
const JPH::Shape *jolt_shape = p_body.get_jolt_shape();
const Vector3 com_scaled = to_godot(jolt_shape->GetCenterOfMass());
const Transform3D transform_com = p_transform.translated_local(com_scaled);
JPH::CollideShapeSettings settings;
settings.mCollectFacesMode = JPH::ECollectFacesMode::CollectFaces;
settings.mMaxSeparationDistance = p_margin;
const Vector3 &base_offset = transform_com.origin;
const JoltMotionFilter3D motion_filter(p_body, p_excluded_bodies, p_excluded_objects);
JoltQueryCollectorClosestMulti<JPH::CollideShapeCollector, 32> collector(p_max_collisions);
_collide_shape_kinematics(jolt_shape, JPH::Vec3::sReplicate(1.0f), to_jolt_r(transform_com), settings, to_jolt_r(base_offset), collector, motion_filter, motion_filter, motion_filter, motion_filter);
if (!collector.had_hit() || p_result == nullptr) {
return collector.had_hit();
}
int count = 0;
for (int i = 0; i < collector.get_hit_count(); ++i) {
const JPH::CollideShapeResult &hit = collector.get_hit(i);
const float penetration_depth = hit.mPenetrationDepth + p_margin;
if (penetration_depth <= 0.0f) {
continue;
}
const Vector3 normal = to_godot(-hit.mPenetrationAxis.Normalized());
if (p_motion.length_squared() > 0) {
const Vector3 direction = p_motion.normalized();
if (direction.dot(normal) >= -CMP_EPSILON) {
continue;
}
}
JPH::ContactPoints contact_points1;
JPH::ContactPoints contact_points2;
if (p_max_collisions > 1) {
_generate_manifold(hit, contact_points1, contact_points2 JPH_IF_DEBUG_RENDERER(, to_jolt_r(base_offset)));
} else {
contact_points2.push_back(hit.mContactPointOn2);
}
const JoltReadableBody3D collider_jolt_body = space->read_body(hit.mBodyID2);
const JoltShapedObject3D *collider = collider_jolt_body.as_shaped();
ERR_FAIL_NULL_V(collider, false);
const int local_shape = p_body.find_shape_index(hit.mSubShapeID1);
ERR_FAIL_COND_V(local_shape == -1, false);
const int collider_shape = collider->find_shape_index(hit.mSubShapeID2);
ERR_FAIL_COND_V(collider_shape == -1, false);
for (JPH::Vec3 contact_point : contact_points2) {
const Vector3 position = base_offset + to_godot(contact_point);
PhysicsServer3D::MotionCollision &collision = p_result->collisions[count++];
collision.position = position;
collision.normal = normal;
collision.collider_velocity = collider->get_velocity_at_position(position);
collision.collider_angular_velocity = collider->get_angular_velocity();
collision.depth = penetration_depth;
collision.local_shape = local_shape;
collision.collider_id = collider->get_instance_id();
collision.collider = collider->get_rid();
collision.collider_shape = collider_shape;
if (count == p_max_collisions) {
break;
}
}
if (count == p_max_collisions) {
break;
}
}
p_result->collision_count = count;
return count > 0;
}
int JoltPhysicsDirectSpaceState3D::_try_get_face_index(const JPH::Body &p_body, const JPH::SubShapeID &p_sub_shape_id) {
if (!JoltProjectSettings::enable_ray_cast_face_index()) {
return -1;
}
const JPH::Shape *root_shape = p_body.GetShape();
JPH::SubShapeID sub_shape_id_remainder;
const JPH::Shape *leaf_shape = root_shape->GetLeafShape(p_sub_shape_id, sub_shape_id_remainder);
if (leaf_shape->GetType() != JPH::EShapeType::Mesh) {
return -1;
}
const JPH::MeshShape *mesh_shape = static_cast<const JPH::MeshShape *>(leaf_shape);
return (int)mesh_shape->GetTriangleUserData(sub_shape_id_remainder);
}
void JoltPhysicsDirectSpaceState3D::_generate_manifold(const JPH::CollideShapeResult &p_hit, JPH::ContactPoints &r_contact_points1, JPH::ContactPoints &r_contact_points2 JPH_IF_DEBUG_RENDERER(, JPH::RVec3Arg p_center_of_mass)) const {
const JPH::PhysicsSystem &physics_system = space->get_physics_system();
const JPH::PhysicsSettings &physics_settings = physics_system.GetPhysicsSettings();
const JPH::Vec3 penetration_axis = p_hit.mPenetrationAxis.Normalized();
JPH::ManifoldBetweenTwoFaces(p_hit.mContactPointOn1, p_hit.mContactPointOn2, penetration_axis, physics_settings.mManifoldToleranceSq, p_hit.mShape1Face, p_hit.mShape2Face, r_contact_points1, r_contact_points2 JPH_IF_DEBUG_RENDERER(, p_center_of_mass));
if (r_contact_points1.size() > 4) {
JPH::PruneContactPoints(penetration_axis, r_contact_points1, r_contact_points2 JPH_IF_DEBUG_RENDERER(, p_center_of_mass));
}
}
void JoltPhysicsDirectSpaceState3D::_collide_shape_queries(
const JPH::Shape *p_shape,
JPH::Vec3Arg p_scale,
JPH::RMat44Arg p_transform_com,
const JPH::CollideShapeSettings &p_settings,
JPH::RVec3Arg p_base_offset,
JPH::CollideShapeCollector &p_collector,
const JPH::BroadPhaseLayerFilter &p_broad_phase_layer_filter,
const JPH::ObjectLayerFilter &p_object_layer_filter,
const JPH::BodyFilter &p_body_filter,
const JPH::ShapeFilter &p_shape_filter) const {
if (JoltProjectSettings::use_enhanced_internal_edge_removal_for_queries()) {
space->get_narrow_phase_query().CollideShapeWithInternalEdgeRemoval(p_shape, p_scale, p_transform_com, p_settings, p_base_offset, p_collector, p_broad_phase_layer_filter, p_object_layer_filter, p_body_filter, p_shape_filter);
} else {
space->get_narrow_phase_query().CollideShape(p_shape, p_scale, p_transform_com, p_settings, p_base_offset, p_collector, p_broad_phase_layer_filter, p_object_layer_filter, p_body_filter, p_shape_filter);
}
}
void JoltPhysicsDirectSpaceState3D::_collide_shape_kinematics(
const JPH::Shape *p_shape,
JPH::Vec3Arg p_scale,
JPH::RMat44Arg p_transform_com,
const JPH::CollideShapeSettings &p_settings,
JPH::RVec3Arg p_base_offset,
JPH::CollideShapeCollector &p_collector,
const JPH::BroadPhaseLayerFilter &p_broad_phase_layer_filter,
const JPH::ObjectLayerFilter &p_object_layer_filter,
const JPH::BodyFilter &p_body_filter,
const JPH::ShapeFilter &p_shape_filter) const {
if (JoltProjectSettings::use_enhanced_internal_edge_removal_for_motion_queries()) {
space->get_narrow_phase_query().CollideShapeWithInternalEdgeRemoval(p_shape, p_scale, p_transform_com, p_settings, p_base_offset, p_collector, p_broad_phase_layer_filter, p_object_layer_filter, p_body_filter, p_shape_filter);
} else {
space->get_narrow_phase_query().CollideShape(p_shape, p_scale, p_transform_com, p_settings, p_base_offset, p_collector, p_broad_phase_layer_filter, p_object_layer_filter, p_body_filter, p_shape_filter);
}
}
JoltPhysicsDirectSpaceState3D::JoltPhysicsDirectSpaceState3D(JoltSpace3D *p_space) :
space(p_space) {
}
bool JoltPhysicsDirectSpaceState3D::intersect_ray(const RayParameters &p_parameters, RayResult &r_result) {
ERR_FAIL_COND_V_MSG(space->is_stepping(), false, "intersect_ray must not be called while the physics space is being stepped.");
space->try_optimize();
const JoltQueryFilter3D query_filter(*this, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, p_parameters.exclude, p_parameters.pick_ray);
const JPH::RVec3 from = to_jolt_r(p_parameters.from);
const JPH::RVec3 to = to_jolt_r(p_parameters.to);
const JPH::Vec3 vector = JPH::Vec3(to - from);
const JPH::RRayCast ray(from, vector);
const JPH::EBackFaceMode back_face_mode = p_parameters.hit_back_faces ? JPH::EBackFaceMode::CollideWithBackFaces : JPH::EBackFaceMode::IgnoreBackFaces;
JPH::RayCastSettings settings;
settings.mTreatConvexAsSolid = p_parameters.hit_from_inside;
settings.mBackFaceModeTriangles = back_face_mode;
JoltQueryCollectorClosest<JPH::CastRayCollector> collector;
space->get_narrow_phase_query().CastRay(ray, settings, collector, query_filter, query_filter, query_filter);
if (!collector.had_hit()) {
return false;
}
const JPH::RayCastResult &hit = collector.get_hit();
const JPH::BodyID &body_id = hit.mBodyID;
const JPH::SubShapeID &sub_shape_id = hit.mSubShapeID2;
const JoltReadableBody3D body = space->read_body(body_id);
const JoltObject3D *object = body.as_object();
ERR_FAIL_NULL_V(object, false);
const JPH::RVec3 position = ray.GetPointOnRay(hit.mFraction);
JPH::Vec3 normal = JPH::Vec3::sZero();
if (!p_parameters.hit_from_inside || hit.mFraction > 0.0f) {
normal = body->GetWorldSpaceSurfaceNormal(sub_shape_id, position);
// If we got a back-face normal we need to flip it.
if (normal.Dot(vector) > 0) {
normal = -normal;
}
}
r_result.position = to_godot(position);
r_result.normal = to_godot(normal);
r_result.rid = object->get_rid();
r_result.collider_id = object->get_instance_id();
r_result.collider = object->get_instance();
r_result.shape = 0;
if (const JoltShapedObject3D *shaped_object = object->as_shaped()) {
const int shape_index = shaped_object->find_shape_index(sub_shape_id);
ERR_FAIL_COND_V(shape_index == -1, false);
r_result.shape = shape_index;
r_result.face_index = _try_get_face_index(*body, sub_shape_id);
}
return true;
}
int JoltPhysicsDirectSpaceState3D::intersect_point(const PointParameters &p_parameters, ShapeResult *r_results, int p_result_max) {
ERR_FAIL_COND_V_MSG(space->is_stepping(), false, "intersect_point must not be called while the physics space is being stepped.");
if (p_result_max == 0) {
return 0;
}
space->try_optimize();
const JoltQueryFilter3D query_filter(*this, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, p_parameters.exclude);
JoltQueryCollectorAnyMulti<JPH::CollidePointCollector, 32> collector(p_result_max);
space->get_narrow_phase_query().CollidePoint(to_jolt_r(p_parameters.position), collector, query_filter, query_filter, query_filter);
const int hit_count = collector.get_hit_count();
for (int i = 0; i < hit_count; ++i) {
const JPH::CollidePointResult &hit = collector.get_hit(i);
const JoltReadableBody3D body = space->read_body(hit.mBodyID);
const JoltObject3D *object = body.as_object();
ERR_FAIL_NULL_V(object, 0);
ShapeResult &result = *r_results++;
result.shape = 0;
if (const JoltShapedObject3D *shaped_object = object->as_shaped()) {
const int shape_index = shaped_object->find_shape_index(hit.mSubShapeID2);
ERR_FAIL_COND_V(shape_index == -1, 0);
result.shape = shape_index;
}
result.rid = object->get_rid();
result.collider_id = object->get_instance_id();
result.collider = object->get_instance();
}
return hit_count;
}
int JoltPhysicsDirectSpaceState3D::intersect_shape(const ShapeParameters &p_parameters, ShapeResult *r_results, int p_result_max) {
ERR_FAIL_COND_V_MSG(space->is_stepping(), false, "intersect_shape must not be called while the physics space is being stepped.");
if (p_result_max == 0) {
return 0;
}
space->try_optimize();
JoltShape3D *shape = JoltPhysicsServer3D::get_singleton()->get_shape(p_parameters.shape_rid);
ERR_FAIL_NULL_V(shape, 0);
const JPH::ShapeRefC jolt_shape = shape->try_build();
ERR_FAIL_NULL_V(jolt_shape, 0);
Transform3D transform = p_parameters.transform;
JOLT_ENSURE_SCALE_NOT_ZERO(transform, "intersect_shape was passed an invalid transform.");
Vector3 scale = transform.basis.get_scale();
JOLT_ENSURE_SCALE_VALID(jolt_shape, scale, "intersect_shape was passed an invalid transform.");
transform.basis.orthonormalize();
const Vector3 com_scaled = to_godot(jolt_shape->GetCenterOfMass());
const Transform3D transform_com = transform.translated_local(com_scaled);
JPH::CollideShapeSettings settings;
settings.mMaxSeparationDistance = (float)p_parameters.margin;
const JoltQueryFilter3D query_filter(*this, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, p_parameters.exclude);
JoltQueryCollectorAnyMulti<JPH::CollideShapeCollector, 32> collector(p_result_max);
_collide_shape_queries(jolt_shape, to_jolt(scale), to_jolt_r(transform_com), settings, to_jolt_r(transform_com.origin), collector, query_filter, query_filter, query_filter);
const int hit_count = collector.get_hit_count();
for (int i = 0; i < hit_count; ++i) {
const JPH::CollideShapeResult &hit = collector.get_hit(i);
const JoltReadableBody3D body = space->read_body(hit.mBodyID2);
const JoltObject3D *object = body.as_object();
ERR_FAIL_NULL_V(object, 0);
ShapeResult &result = *r_results++;
result.shape = 0;
if (const JoltShapedObject3D *shaped_object = object->as_shaped()) {
const int shape_index = shaped_object->find_shape_index(hit.mSubShapeID2);
ERR_FAIL_COND_V(shape_index == -1, 0);
result.shape = shape_index;
}
result.rid = object->get_rid();
result.collider_id = object->get_instance_id();
result.collider = object->get_instance();
}
return hit_count;
}
bool JoltPhysicsDirectSpaceState3D::cast_motion(const ShapeParameters &p_parameters, real_t &r_closest_safe, real_t &r_closest_unsafe, ShapeRestInfo *r_info) {
ERR_FAIL_COND_V_MSG(space->is_stepping(), false, "cast_motion must not be called while the physics space is being stepped.");
ERR_FAIL_COND_V_MSG(r_info != nullptr, false, "Providing rest info as part of cast_motion is not supported when using Jolt Physics.");
space->try_optimize();
JoltShape3D *shape = JoltPhysicsServer3D::get_singleton()->get_shape(p_parameters.shape_rid);
ERR_FAIL_NULL_V(shape, false);
const JPH::ShapeRefC jolt_shape = shape->try_build();
ERR_FAIL_NULL_V(jolt_shape, false);
Transform3D transform = p_parameters.transform;
JOLT_ENSURE_SCALE_NOT_ZERO(transform, "cast_motion (maybe from ShapeCast3D?) was passed an invalid transform.");
Vector3 scale = transform.basis.get_scale();
JOLT_ENSURE_SCALE_VALID(jolt_shape, scale, "cast_motion (maybe from ShapeCast3D?) was passed an invalid transform.");
transform.basis.orthonormalize();
const Vector3 com_scaled = to_godot(jolt_shape->GetCenterOfMass());
Transform3D transform_com = transform.translated_local(com_scaled);
JPH::CollideShapeSettings settings;
settings.mMaxSeparationDistance = (float)p_parameters.margin;
const JoltQueryFilter3D query_filter(*this, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, p_parameters.exclude);
_cast_motion_impl(*jolt_shape, transform_com, scale, p_parameters.motion, JoltProjectSettings::use_enhanced_internal_edge_removal_for_queries(), true, settings, query_filter, query_filter, query_filter, JPH::ShapeFilter(), r_closest_safe, r_closest_unsafe);
return true;
}
bool JoltPhysicsDirectSpaceState3D::collide_shape(const ShapeParameters &p_parameters, Vector3 *r_results, int p_result_max, int &r_result_count) {
r_result_count = 0;
ERR_FAIL_COND_V_MSG(space->is_stepping(), false, "collide_shape must not be called while the physics space is being stepped.");
if (p_result_max == 0) {
return false;
}
space->try_optimize();
JoltShape3D *shape = JoltPhysicsServer3D::get_singleton()->get_shape(p_parameters.shape_rid);
ERR_FAIL_NULL_V(shape, false);
const JPH::ShapeRefC jolt_shape = shape->try_build();
ERR_FAIL_NULL_V(jolt_shape, false);
Transform3D transform = p_parameters.transform;
JOLT_ENSURE_SCALE_NOT_ZERO(transform, "collide_shape was passed an invalid transform.");
Vector3 scale = transform.basis.get_scale();
JOLT_ENSURE_SCALE_VALID(jolt_shape, scale, "collide_shape was passed an invalid transform.");
transform.basis.orthonormalize();
const Vector3 com_scaled = to_godot(jolt_shape->GetCenterOfMass());
const Transform3D transform_com = transform.translated_local(com_scaled);
JPH::CollideShapeSettings settings;
settings.mCollectFacesMode = JPH::ECollectFacesMode::CollectFaces;
settings.mMaxSeparationDistance = (float)p_parameters.margin;
const Vector3 &base_offset = transform_com.origin;
const JoltQueryFilter3D query_filter(*this, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, p_parameters.exclude);
JoltQueryCollectorAnyMulti<JPH::CollideShapeCollector, 32> collector(p_result_max);
_collide_shape_queries(jolt_shape, to_jolt(scale), to_jolt_r(transform_com), settings, to_jolt_r(base_offset), collector, query_filter, query_filter, query_filter);
if (!collector.had_hit()) {
return false;
}
const int max_points = p_result_max * 2;
int point_count = 0;
for (int i = 0; i < collector.get_hit_count(); ++i) {
const JPH::CollideShapeResult &hit = collector.get_hit(i);
const Vector3 penetration_axis = to_godot(hit.mPenetrationAxis.Normalized());
const Vector3 margin_offset = penetration_axis * (float)p_parameters.margin;
JPH::ContactPoints contact_points1;
JPH::ContactPoints contact_points2;
_generate_manifold(hit, contact_points1, contact_points2 JPH_IF_DEBUG_RENDERER(, to_jolt_r(base_offset)));
for (JPH::uint j = 0; j < contact_points1.size(); ++j) {
r_results[point_count++] = base_offset + to_godot(contact_points1[j]) + margin_offset;
r_results[point_count++] = base_offset + to_godot(contact_points2[j]);
if (point_count >= max_points) {
break;
}
}
if (point_count >= max_points) {
break;
}
}
r_result_count = point_count / 2;
return true;
}
bool JoltPhysicsDirectSpaceState3D::rest_info(const ShapeParameters &p_parameters, ShapeRestInfo *r_info) {
ERR_FAIL_COND_V_MSG(space->is_stepping(), false, "get_rest_info must not be called while the physics space is being stepped.");
space->try_optimize();
JoltShape3D *shape = JoltPhysicsServer3D::get_singleton()->get_shape(p_parameters.shape_rid);
ERR_FAIL_NULL_V(shape, false);
const JPH::ShapeRefC jolt_shape = shape->try_build();
ERR_FAIL_NULL_V(jolt_shape, false);
Transform3D transform = p_parameters.transform;
JOLT_ENSURE_SCALE_NOT_ZERO(transform, "get_rest_info (maybe from ShapeCast3D?) was passed an invalid transform.");
Vector3 scale = transform.basis.get_scale();
JOLT_ENSURE_SCALE_VALID(jolt_shape, scale, "get_rest_info (maybe from ShapeCast3D?) was passed an invalid transform.");
transform.basis.orthonormalize();
const Vector3 com_scaled = to_godot(jolt_shape->GetCenterOfMass());
const Transform3D transform_com = transform.translated_local(com_scaled);
JPH::CollideShapeSettings settings;
settings.mMaxSeparationDistance = (float)p_parameters.margin;
const Vector3 &base_offset = transform_com.origin;
const JoltQueryFilter3D query_filter(*this, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, p_parameters.exclude);
JoltQueryCollectorClosest<JPH::CollideShapeCollector> collector;
_collide_shape_queries(jolt_shape, to_jolt(scale), to_jolt_r(transform_com), settings, to_jolt_r(base_offset), collector, query_filter, query_filter, query_filter);
if (!collector.had_hit()) {
return false;
}
const JPH::CollideShapeResult &hit = collector.get_hit();
const JoltReadableBody3D body = space->read_body(hit.mBodyID2);
const JoltObject3D *object = body.as_object();
ERR_FAIL_NULL_V(object, false);
r_info->shape = 0;
if (const JoltShapedObject3D *shaped_object = object->as_shaped()) {
const int shape_index = shaped_object->find_shape_index(hit.mSubShapeID2);
ERR_FAIL_COND_V(shape_index == -1, false);
r_info->shape = shape_index;
}
const Vector3 hit_point = base_offset + to_godot(hit.mContactPointOn2);
r_info->point = hit_point;
r_info->normal = to_godot(-hit.mPenetrationAxis.Normalized());
r_info->rid = object->get_rid();
r_info->collider_id = object->get_instance_id();
r_info->shape = 0;
r_info->linear_velocity = object->get_velocity_at_position(hit_point);
return true;
}
Vector3 JoltPhysicsDirectSpaceState3D::get_closest_point_to_object_volume(RID p_object, Vector3 p_point) const {
ERR_FAIL_COND_V_MSG(space->is_stepping(), Vector3(), "get_closest_point_to_object_volume must not be called while the physics space is being stepped.");
space->try_optimize();
JoltPhysicsServer3D *physics_server = JoltPhysicsServer3D::get_singleton();
JoltObject3D *object = physics_server->get_area(p_object);
if (object == nullptr) {
object = physics_server->get_body(p_object);
}
ERR_FAIL_NULL_V(object, Vector3());
ERR_FAIL_COND_V(object->get_space() != space, Vector3());
const JoltReadableBody3D body = space->read_body(*object);
const JPH::TransformedShape root_shape = body->GetTransformedShape();
JoltQueryCollectorAll<JPH::TransformedShapeCollector, 32> collector;
root_shape.CollectTransformedShapes(body->GetWorldSpaceBounds(), collector);
const JPH::RVec3 point = to_jolt_r(p_point);
float closest_distance_sq = FLT_MAX;
JPH::RVec3 closest_point = JPH::RVec3::sZero();
bool found_point = false;
for (int i = 0; i < collector.get_hit_count(); ++i) {
const JPH::TransformedShape &shape_transformed = collector.get_hit(i);
const JPH::Shape &shape = *shape_transformed.mShape;
if (shape.GetType() != JPH::EShapeType::Convex) {
continue;
}
const JPH::ConvexShape &shape_convex = static_cast<const JPH::ConvexShape &>(shape);
JPH::GJKClosestPoint gjk;
JPH::ConvexShape::SupportBuffer shape_support_buffer;
const JPH::ConvexShape::Support *shape_support = shape_convex.GetSupportFunction(JPH::ConvexShape::ESupportMode::IncludeConvexRadius, shape_support_buffer, shape_transformed.GetShapeScale());
const JPH::Quat &shape_rotation = shape_transformed.mShapeRotation;
const JPH::RVec3 &shape_pos_com = shape_transformed.mShapePositionCOM;
const JPH::RMat44 shape_3x3 = JPH::RMat44::sRotation(shape_rotation);
const JPH::Vec3 shape_com_local = shape.GetCenterOfMass();
const JPH::Vec3 shape_com = shape_3x3.Multiply3x3(shape_com_local);
const JPH::RVec3 shape_pos = shape_pos_com - JPH::RVec3(shape_com);
const JPH::RMat44 shape_4x4 = shape_3x3.PostTranslated(shape_pos);
const JPH::RMat44 shape_4x4_inv = shape_4x4.InversedRotationTranslation();
JPH::PointConvexSupport point_support;
point_support.mPoint = JPH::Vec3(shape_4x4_inv * point);
JPH::Vec3 separating_axis = JPH::Vec3::sAxisX();
JPH::Vec3 point_on_a = JPH::Vec3::sZero();
JPH::Vec3 point_on_b = JPH::Vec3::sZero();
const float distance_sq = gjk.GetClosestPoints(*shape_support, point_support, JPH::cDefaultCollisionTolerance, FLT_MAX, separating_axis, point_on_a, point_on_b);
if (distance_sq == 0.0f) {
closest_point = point;
found_point = true;
break;
}
if (distance_sq < closest_distance_sq) {
closest_distance_sq = distance_sq;
closest_point = shape_4x4 * point_on_a;
found_point = true;
}
}
if (found_point) {
return to_godot(closest_point);
} else {
return to_godot(body->GetPosition());
}
}
bool JoltPhysicsDirectSpaceState3D::body_test_motion(const JoltBody3D &p_body, const PhysicsServer3D::MotionParameters &p_parameters, PhysicsServer3D::MotionResult *r_result) const {
ERR_FAIL_COND_V_MSG(space->is_stepping(), false, "body_test_motion (maybe from move_and_slide?) must not be called while the physics space is being stepped.");
const float margin = MAX((float)p_parameters.margin, 0.0001f);
const int max_collisions = MIN(p_parameters.max_collisions, 32);
Transform3D transform = p_parameters.from;
JOLT_ENSURE_SCALE_NOT_ZERO(transform, vformat("body_test_motion (maybe from move_and_slide?) was passed an invalid transform along with body '%s'.", p_body.to_string()));
Vector3 scale = transform.basis.get_scale();
transform.basis.orthonormalize();
space->try_optimize();
Vector3 recovery;
const bool recovered = _body_motion_recover(p_body, transform, margin, p_parameters.exclude_bodies, p_parameters.exclude_objects, recovery);
transform.origin += recovery;
real_t safe_fraction = 1.0;
real_t unsafe_fraction = 1.0;
const bool hit = _body_motion_cast(p_body, transform, scale, p_parameters.motion, p_parameters.collide_separation_ray, p_parameters.exclude_bodies, p_parameters.exclude_objects, safe_fraction, unsafe_fraction);
bool collided = false;
if (hit || (recovered && p_parameters.recovery_as_collision)) {
collided = _body_motion_collide(p_body, transform.translated(p_parameters.motion * unsafe_fraction), p_parameters.motion, margin, max_collisions, p_parameters.exclude_bodies, p_parameters.exclude_objects, r_result);
}
if (r_result == nullptr) {
return collided;
}
if (collided) {
const PhysicsServer3D::MotionCollision &deepest = r_result->collisions[0];
r_result->travel = recovery + p_parameters.motion * safe_fraction;
r_result->remainder = p_parameters.motion - p_parameters.motion * safe_fraction;
r_result->collision_depth = deepest.depth;
r_result->collision_safe_fraction = safe_fraction;
r_result->collision_unsafe_fraction = unsafe_fraction;
} else {
r_result->travel = recovery + p_parameters.motion;
r_result->remainder = Vector3();
r_result->collision_depth = 0.0f;
r_result->collision_safe_fraction = 1.0f;
r_result->collision_unsafe_fraction = 1.0f;
r_result->collision_count = 0;
}
return collided;
}

View File

@@ -0,0 +1,87 @@
/**************************************************************************/
/* jolt_physics_direct_space_state_3d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef JOLT_PHYSICS_DIRECT_SPACE_STATE_3D_H
#define JOLT_PHYSICS_DIRECT_SPACE_STATE_3D_H
#include "servers/physics_server_3d.h"
#include "Jolt/Jolt.h"
#include "Jolt/Physics/Body/Body.h"
#include "Jolt/Physics/Body/BodyFilter.h"
#include "Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h"
#include "Jolt/Physics/Collision/ContactListener.h"
#include "Jolt/Physics/Collision/ObjectLayer.h"
#include "Jolt/Physics/Collision/Shape/Shape.h"
#include "Jolt/Physics/Collision/ShapeFilter.h"
class JoltBody3D;
class JoltShape3D;
class JoltSpace3D;
class JoltPhysicsDirectSpaceState3D final : public PhysicsDirectSpaceState3D {
GDCLASS(JoltPhysicsDirectSpaceState3D, PhysicsDirectSpaceState3D)
JoltSpace3D *space = nullptr;
static void _bind_methods() {}
bool _cast_motion_impl(const JPH::Shape &p_jolt_shape, const Transform3D &p_transform_com, const Vector3 &p_scale, const Vector3 &p_motion, bool p_use_edge_removal, bool p_ignore_overlaps, const JPH::CollideShapeSettings &p_settings, const JPH::BroadPhaseLayerFilter &p_broad_phase_layer_filter, const JPH::ObjectLayerFilter &p_object_layer_filter, const JPH::BodyFilter &p_body_filter, const JPH::ShapeFilter &p_shape_filter, real_t &r_closest_safe, real_t &r_closest_unsafe) const;
bool _body_motion_recover(const JoltBody3D &p_body, const Transform3D &p_transform, float p_margin, const HashSet<RID> &p_excluded_bodies, const HashSet<ObjectID> &p_excluded_objects, Vector3 &r_recovery) const;
bool _body_motion_cast(const JoltBody3D &p_body, const Transform3D &p_transform, const Vector3 &p_scale, const Vector3 &p_motion, bool p_collide_separation_ray, const HashSet<RID> &p_excluded_bodies, const HashSet<ObjectID> &p_excluded_objects, real_t &r_safe_fraction, real_t &r_unsafe_fraction) const;
bool _body_motion_collide(const JoltBody3D &p_body, const Transform3D &p_transform, const Vector3 &p_motion, float p_margin, int p_max_collisions, const HashSet<RID> &p_excluded_bodies, const HashSet<ObjectID> &p_excluded_objects, PhysicsServer3D::MotionResult *r_result) const;
int _try_get_face_index(const JPH::Body &p_body, const JPH::SubShapeID &p_sub_shape_id);
void _generate_manifold(const JPH::CollideShapeResult &p_hit, JPH::ContactPoints &r_contact_points1, JPH::ContactPoints &r_contact_points2 JPH_IF_DEBUG_RENDERER(, JPH::RVec3Arg p_center_of_mass)) const;
void _collide_shape_queries(const JPH::Shape *p_shape, JPH::Vec3Arg p_scale, JPH::RMat44Arg p_transform_com, const JPH::CollideShapeSettings &p_settings, JPH::RVec3Arg p_base_offset, JPH::CollideShapeCollector &p_collector, const JPH::BroadPhaseLayerFilter &p_broad_phase_layer_filter = JPH::BroadPhaseLayerFilter(), const JPH::ObjectLayerFilter &p_object_layer_filter = JPH::ObjectLayerFilter(), const JPH::BodyFilter &p_body_filter = JPH::BodyFilter(), const JPH::ShapeFilter &p_shape_filter = JPH::ShapeFilter()) const;
void _collide_shape_kinematics(const JPH::Shape *p_shape, JPH::Vec3Arg p_scale, JPH::RMat44Arg p_transform_com, const JPH::CollideShapeSettings &p_settings, JPH::RVec3Arg p_base_offset, JPH::CollideShapeCollector &p_collector, const JPH::BroadPhaseLayerFilter &p_broad_phase_layer_filter = JPH::BroadPhaseLayerFilter(), const JPH::ObjectLayerFilter &p_object_layer_filter = JPH::ObjectLayerFilter(), const JPH::BodyFilter &p_body_filter = JPH::BodyFilter(), const JPH::ShapeFilter &p_shape_filter = JPH::ShapeFilter()) const;
public:
JoltPhysicsDirectSpaceState3D() = default;
explicit JoltPhysicsDirectSpaceState3D(JoltSpace3D *p_space);
virtual bool intersect_ray(const RayParameters &p_parameters, RayResult &r_result) override;
virtual int intersect_point(const PointParameters &p_parameters, ShapeResult *r_results, int p_result_max) override;
virtual int intersect_shape(const ShapeParameters &p_parameters, ShapeResult *r_results, int p_result_max) override;
virtual bool cast_motion(const ShapeParameters &p_parameters, real_t &r_closest_safe, real_t &r_closest_unsafe, ShapeRestInfo *r_info = nullptr) override;
virtual bool collide_shape(const ShapeParameters &p_parameters, Vector3 *r_results, int p_result_max, int &r_result_count) override;
virtual bool rest_info(const ShapeParameters &p_parameters, ShapeRestInfo *r_info) override;
virtual Vector3 get_closest_point_to_object_volume(RID p_object, Vector3 p_point) const override;
bool body_test_motion(const JoltBody3D &p_body, const PhysicsServer3D::MotionParameters &p_parameters, PhysicsServer3D::MotionResult *r_result) const;
JoltSpace3D &get_space() const { return *space; }
};
#endif // JOLT_PHYSICS_DIRECT_SPACE_STATE_3D_H

View File

@@ -0,0 +1,238 @@
/**************************************************************************/
/* jolt_query_collectors.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef JOLT_QUERY_COLLECTORS_H
#define JOLT_QUERY_COLLECTORS_H
#include "../jolt_project_settings.h"
#include "jolt_space_3d.h"
#include "core/templates/local_vector.h"
#include "Jolt/Jolt.h"
#include "Jolt/Physics/Collision/InternalEdgeRemovingCollector.h"
#include "Jolt/Physics/Collision/Shape/Shape.h"
template <typename TBase, int TDefaultCapacity>
class JoltQueryCollectorAll final : public TBase {
public:
typedef typename TBase::ResultType Hit;
private:
JPH::Array<Hit> hits;
public:
bool had_hit() const {
return !hits.is_empty();
}
int get_hit_count() const {
return hits.size();
}
const Hit &get_hit(int p_index) const {
return hits[p_index];
}
void reset() { Reset(); }
virtual void Reset() override {
TBase::Reset();
hits.clear();
}
virtual void AddHit(const Hit &p_hit) override {
hits.push_back(p_hit);
}
};
template <typename TBase>
class JoltQueryCollectorAny final : public TBase {
public:
typedef typename TBase::ResultType Hit;
private:
Hit hit;
bool valid = false;
public:
bool had_hit() const { return valid; }
const Hit &get_hit() const { return hit; }
void reset() {
Reset();
}
virtual void Reset() override {
TBase::Reset();
valid = false;
}
virtual void AddHit(const Hit &p_hit) override {
hit = p_hit;
valid = true;
TBase::ForceEarlyOut();
}
};
template <typename TBase, int TDefaultCapacity>
class JoltQueryCollectorAnyMulti final : public TBase {
public:
typedef typename TBase::ResultType Hit;
private:
JPH::Array<Hit> hits;
int max_hits = 0;
public:
explicit JoltQueryCollectorAnyMulti(int p_max_hits = TDefaultCapacity) :
max_hits(p_max_hits) {}
bool had_hit() const {
return hits.size() > 0;
}
int get_hit_count() const {
return hits.size();
}
const Hit &get_hit(int p_index) const {
return hits[p_index];
}
void reset() {
Reset();
}
virtual void Reset() override {
TBase::Reset();
hits.clear();
}
virtual void AddHit(const Hit &p_hit) override {
if ((int)hits.size() < max_hits) {
hits.push_back(p_hit);
}
if ((int)hits.size() == max_hits) {
TBase::ForceEarlyOut();
}
}
};
template <typename TBase>
class JoltQueryCollectorClosest final : public TBase {
public:
typedef typename TBase::ResultType Hit;
private:
Hit hit;
bool valid = false;
public:
bool had_hit() const { return valid; }
const Hit &get_hit() const { return hit; }
void reset() {
Reset();
}
virtual void Reset() override {
TBase::Reset();
valid = false;
}
virtual void AddHit(const Hit &p_hit) override {
const float early_out = p_hit.GetEarlyOutFraction();
if (!valid || early_out < hit.GetEarlyOutFraction()) {
TBase::UpdateEarlyOutFraction(early_out);
hit = p_hit;
valid = true;
}
}
};
template <typename TBase, int TDefaultCapacity>
class JoltQueryCollectorClosestMulti final : public TBase {
public:
typedef typename TBase::ResultType Hit;
private:
JPH::Array<Hit> hits;
int max_hits = 0;
public:
explicit JoltQueryCollectorClosestMulti(int p_max_hits = TDefaultCapacity) :
max_hits(p_max_hits) {}
bool had_hit() const {
return hits.size() > 0;
}
int get_hit_count() const {
return hits.size();
}
const Hit &get_hit(int p_index) const {
return hits[p_index];
}
void reset() {
Reset();
}
virtual void Reset() override {
TBase::Reset();
hits.clear();
}
virtual void AddHit(const Hit &p_hit) override {
typename JPH::Array<Hit>::const_iterator E = hits.cbegin();
for (; E != hits.cend(); ++E) {
if (p_hit.GetEarlyOutFraction() < E->GetEarlyOutFraction()) {
break;
}
}
hits.insert(E, p_hit);
if ((int)hits.size() > max_hits) {
hits.resize(max_hits);
}
}
};
#endif // JOLT_QUERY_COLLECTORS_H

View File

@@ -0,0 +1,83 @@
/**************************************************************************/
/* jolt_query_filter_3d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "jolt_query_filter_3d.h"
#include "../objects/jolt_object_3d.h"
#include "jolt_broad_phase_layer.h"
#include "jolt_physics_direct_space_state_3d.h"
#include "jolt_space_3d.h"
JoltQueryFilter3D::JoltQueryFilter3D(const JoltPhysicsDirectSpaceState3D &p_space_state, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, const HashSet<RID> &p_excluded, bool p_picking) :
space(p_space_state.get_space()),
excluded(p_excluded),
collision_mask(p_collision_mask),
collide_with_bodies(p_collide_with_bodies),
collide_with_areas(p_collide_with_areas),
picking(p_picking) {
}
bool JoltQueryFilter3D::ShouldCollide(JPH::BroadPhaseLayer p_broad_phase_layer) const {
const JPH::BroadPhaseLayer::Type broad_phase_layer = (JPH::BroadPhaseLayer::Type)p_broad_phase_layer;
switch (broad_phase_layer) {
case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::BODY_STATIC:
case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::BODY_STATIC_BIG:
case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::BODY_DYNAMIC: {
return collide_with_bodies;
} break;
case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::AREA_DETECTABLE:
case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::AREA_UNDETECTABLE: {
return collide_with_areas;
} break;
default: {
ERR_FAIL_V_MSG(false, vformat("Unhandled broad phase layer: '%d'. This should not happen. Please report this.", broad_phase_layer));
}
}
}
bool JoltQueryFilter3D::ShouldCollide(JPH::ObjectLayer p_object_layer) const {
JPH::BroadPhaseLayer object_broad_phase_layer = JoltBroadPhaseLayer::BODY_STATIC;
uint32_t object_collision_layer = 0;
uint32_t object_collision_mask = 0;
space.map_from_object_layer(p_object_layer, object_broad_phase_layer, object_collision_layer, object_collision_mask);
return (collision_mask & object_collision_layer) != 0;
}
bool JoltQueryFilter3D::ShouldCollide(const JPH::BodyID &p_body_id) const {
return true;
}
bool JoltQueryFilter3D::ShouldCollideLocked(const JPH::Body &p_body) const {
JoltObject3D *object = reinterpret_cast<JoltObject3D *>(p_body.GetUserData());
return (!picking || object->is_pickable()) && !excluded.has(object->get_rid());
}

View File

@@ -0,0 +1,67 @@
/**************************************************************************/
/* jolt_query_filter_3d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef JOLT_QUERY_FILTER_3D_H
#define JOLT_QUERY_FILTER_3D_H
#include "core/templates/hash_set.h"
#include "core/templates/rid.h"
#include "Jolt/Jolt.h"
#include "Jolt/Physics/Body/Body.h"
#include "Jolt/Physics/Body/BodyFilter.h"
#include "Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h"
#include "Jolt/Physics/Collision/ObjectLayer.h"
class JoltPhysicsDirectSpaceState3D;
class JoltSpace3D;
class JoltQueryFilter3D final
: public JPH::BroadPhaseLayerFilter,
public JPH::ObjectLayerFilter,
public JPH::BodyFilter {
const JoltSpace3D &space;
const HashSet<RID> &excluded;
uint32_t collision_mask = 0;
bool collide_with_bodies = false;
bool collide_with_areas = false;
bool picking = false;
public:
JoltQueryFilter3D(const JoltPhysicsDirectSpaceState3D &p_space_state, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, const HashSet<RID> &p_excluded, bool p_picking = false);
virtual bool ShouldCollide(JPH::BroadPhaseLayer p_broad_phase_layer) const override;
virtual bool ShouldCollide(JPH::ObjectLayer p_object_layer) const override;
virtual bool ShouldCollide(const JPH::BodyID &p_body_id) const override;
virtual bool ShouldCollideLocked(const JPH::Body &p_body) const override;
};
#endif // JOLT_QUERY_FILTER_3D_H

View File

@@ -0,0 +1,514 @@
/**************************************************************************/
/* jolt_space_3d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "jolt_space_3d.h"
#include "../joints/jolt_joint_3d.h"
#include "../jolt_physics_server_3d.h"
#include "../jolt_project_settings.h"
#include "../misc/jolt_stream_wrappers.h"
#include "../objects/jolt_area_3d.h"
#include "../objects/jolt_body_3d.h"
#include "../shapes/jolt_custom_shape_type.h"
#include "../shapes/jolt_shape_3d.h"
#include "jolt_contact_listener_3d.h"
#include "jolt_layers.h"
#include "jolt_physics_direct_space_state_3d.h"
#include "jolt_temp_allocator.h"
#include "core/io/file_access.h"
#include "core/os/time.h"
#include "core/string/print_string.h"
#include "core/variant/variant_utility.h"
#include "Jolt/Physics/PhysicsScene.h"
namespace {
constexpr double DEFAULT_CONTACT_RECYCLE_RADIUS = 0.01;
constexpr double DEFAULT_CONTACT_MAX_SEPARATION = 0.05;
constexpr double DEFAULT_CONTACT_MAX_ALLOWED_PENETRATION = 0.01;
constexpr double DEFAULT_CONTACT_DEFAULT_BIAS = 0.8;
constexpr double DEFAULT_SLEEP_THRESHOLD_LINEAR = 0.1;
constexpr double DEFAULT_SLEEP_THRESHOLD_ANGULAR = 8.0 * Math_PI / 180;
constexpr double DEFAULT_SOLVER_ITERATIONS = 8;
} // namespace
void JoltSpace3D::_pre_step(float p_step) {
body_accessor.acquire_all();
contact_listener->pre_step();
const int body_count = body_accessor.get_count();
for (int i = 0; i < body_count; ++i) {
if (JPH::Body *jolt_body = body_accessor.try_get(i)) {
if (jolt_body->IsSoftBody()) {
continue;
}
JoltShapedObject3D *object = reinterpret_cast<JoltShapedObject3D *>(jolt_body->GetUserData());
object->pre_step(p_step, *jolt_body);
if (object->reports_contacts()) {
contact_listener->listen_for(object);
}
}
}
body_accessor.release();
}
void JoltSpace3D::_post_step(float p_step) {
body_accessor.acquire_all();
contact_listener->post_step();
const int body_count = body_accessor.get_count();
for (int i = 0; i < body_count; ++i) {
if (JPH::Body *jolt_body = body_accessor.try_get(i)) {
if (jolt_body->IsSoftBody()) {
continue;
}
JoltObject3D *object = reinterpret_cast<JoltObject3D *>(jolt_body->GetUserData());
object->post_step(p_step, *jolt_body);
}
}
body_accessor.release();
}
JoltSpace3D::JoltSpace3D(JPH::JobSystem *p_job_system) :
body_accessor(this),
job_system(p_job_system),
temp_allocator(new JoltTempAllocator()),
layers(new JoltLayers()),
contact_listener(new JoltContactListener3D(this)),
physics_system(new JPH::PhysicsSystem()) {
physics_system->Init((JPH::uint)JoltProjectSettings::get_max_bodies(), 0, (JPH::uint)JoltProjectSettings::get_max_pairs(), (JPH::uint)JoltProjectSettings::get_max_contact_constraints(), *layers, *layers, *layers);
JPH::PhysicsSettings settings;
settings.mBaumgarte = JoltProjectSettings::get_baumgarte_stabilization_factor();
settings.mSpeculativeContactDistance = JoltProjectSettings::get_speculative_contact_distance();
settings.mPenetrationSlop = JoltProjectSettings::get_penetration_slop();
settings.mLinearCastThreshold = JoltProjectSettings::get_ccd_movement_threshold();
settings.mLinearCastMaxPenetration = JoltProjectSettings::get_ccd_max_penetration();
settings.mBodyPairCacheMaxDeltaPositionSq = JoltProjectSettings::get_body_pair_cache_distance_sq();
settings.mBodyPairCacheCosMaxDeltaRotationDiv2 = JoltProjectSettings::get_body_pair_cache_angle_cos_div2();
settings.mNumVelocitySteps = (JPH::uint)JoltProjectSettings::get_simulation_velocity_steps();
settings.mNumPositionSteps = (JPH::uint)JoltProjectSettings::get_simulation_position_steps();
settings.mMinVelocityForRestitution = JoltProjectSettings::get_bounce_velocity_threshold();
settings.mTimeBeforeSleep = JoltProjectSettings::get_sleep_time_threshold();
settings.mPointVelocitySleepThreshold = JoltProjectSettings::get_sleep_velocity_threshold();
settings.mUseBodyPairContactCache = JoltProjectSettings::is_body_pair_contact_cache_enabled();
settings.mAllowSleeping = JoltProjectSettings::is_sleep_allowed();
physics_system->SetPhysicsSettings(settings);
physics_system->SetGravity(JPH::Vec3::sZero());
physics_system->SetContactListener(contact_listener);
physics_system->SetSoftBodyContactListener(contact_listener);
physics_system->SetCombineFriction([](const JPH::Body &p_body1, const JPH::SubShapeID &p_sub_shape_id1, const JPH::Body &p_body2, const JPH::SubShapeID &p_sub_shape_id2) {
return ABS(MIN(p_body1.GetFriction(), p_body2.GetFriction()));
});
physics_system->SetCombineRestitution([](const JPH::Body &p_body1, const JPH::SubShapeID &p_sub_shape_id1, const JPH::Body &p_body2, const JPH::SubShapeID &p_sub_shape_id2) {
return CLAMP(p_body1.GetRestitution() + p_body2.GetRestitution(), 0.0f, 1.0f);
});
}
JoltSpace3D::~JoltSpace3D() {
if (direct_state != nullptr) {
memdelete(direct_state);
direct_state = nullptr;
}
if (physics_system != nullptr) {
delete physics_system;
physics_system = nullptr;
}
if (contact_listener != nullptr) {
delete contact_listener;
contact_listener = nullptr;
}
if (layers != nullptr) {
delete layers;
layers = nullptr;
}
if (temp_allocator != nullptr) {
delete temp_allocator;
temp_allocator = nullptr;
}
}
void JoltSpace3D::step(float p_step) {
stepping = true;
last_step = p_step;
_pre_step(p_step);
const JPH::EPhysicsUpdateError update_error = physics_system->Update(p_step, 1, temp_allocator, job_system);
if ((update_error & JPH::EPhysicsUpdateError::ManifoldCacheFull) != JPH::EPhysicsUpdateError::None) {
WARN_PRINT_ONCE(vformat("Jolt Physics manifold cache exceeded capacity and contacts were ignored. "
"Consider increasing maximum number of contact constraints in project settings. "
"Maximum number of contact constraints is currently set to %d.",
JoltProjectSettings::get_max_contact_constraints()));
}
if ((update_error & JPH::EPhysicsUpdateError::BodyPairCacheFull) != JPH::EPhysicsUpdateError::None) {
WARN_PRINT_ONCE(vformat("Jolt Physics body pair cache exceeded capacity and contacts were ignored. "
"Consider increasing maximum number of body pairs in project settings. "
"Maximum number of body pairs is currently set to %d.",
JoltProjectSettings::get_max_pairs()));
}
if ((update_error & JPH::EPhysicsUpdateError::ContactConstraintsFull) != JPH::EPhysicsUpdateError::None) {
WARN_PRINT_ONCE(vformat("Jolt Physics contact constraint buffer exceeded capacity and contacts were ignored. "
"Consider increasing maximum number of contact constraints in project settings. "
"Maximum number of contact constraints is currently set to %d.",
JoltProjectSettings::get_max_contact_constraints()));
}
_post_step(p_step);
bodies_added_since_optimizing = 0;
has_stepped = true;
stepping = false;
}
void JoltSpace3D::call_queries() {
if (!has_stepped) {
// We need to skip the first invocation of this method, because there will be pending notifications that need to
// be flushed first, which can cause weird conflicts with things like `_integrate_forces`. This happens to also
// emulate the behavior of Godot Physics, where (active) collision objects must register to have `call_queries`
// invoked, which they don't do until the physics step, which happens after this.
//
// TODO: This would be better solved by just doing what Godot Physics does with `GodotSpace*D::active_list`.
return;
}
body_accessor.acquire_all();
const int body_count = body_accessor.get_count();
for (int i = 0; i < body_count; ++i) {
if (JPH::Body *jolt_body = body_accessor.try_get(i)) {
if (!jolt_body->IsSensor() && !jolt_body->IsSoftBody()) {
JoltBody3D *body = reinterpret_cast<JoltBody3D *>(jolt_body->GetUserData());
body->call_queries(*jolt_body);
}
}
}
for (int i = 0; i < body_count; ++i) {
if (JPH::Body *jolt_body = body_accessor.try_get(i)) {
if (jolt_body->IsSensor()) {
JoltArea3D *area = reinterpret_cast<JoltArea3D *>(jolt_body->GetUserData());
area->call_queries(*jolt_body);
}
}
}
body_accessor.release();
}
double JoltSpace3D::get_param(PhysicsServer3D::SpaceParameter p_param) const {
switch (p_param) {
case PhysicsServer3D::SPACE_PARAM_CONTACT_RECYCLE_RADIUS: {
return DEFAULT_CONTACT_RECYCLE_RADIUS;
}
case PhysicsServer3D::SPACE_PARAM_CONTACT_MAX_SEPARATION: {
return DEFAULT_CONTACT_MAX_SEPARATION;
}
case PhysicsServer3D::SPACE_PARAM_CONTACT_MAX_ALLOWED_PENETRATION: {
return DEFAULT_CONTACT_MAX_ALLOWED_PENETRATION;
}
case PhysicsServer3D::SPACE_PARAM_CONTACT_DEFAULT_BIAS: {
return DEFAULT_CONTACT_DEFAULT_BIAS;
}
case PhysicsServer3D::SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_THRESHOLD: {
return DEFAULT_SLEEP_THRESHOLD_LINEAR;
}
case PhysicsServer3D::SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD: {
return DEFAULT_SLEEP_THRESHOLD_ANGULAR;
}
case PhysicsServer3D::SPACE_PARAM_BODY_TIME_TO_SLEEP: {
return JoltProjectSettings::get_sleep_time_threshold();
}
case PhysicsServer3D::SPACE_PARAM_SOLVER_ITERATIONS: {
return DEFAULT_SOLVER_ITERATIONS;
}
default: {
ERR_FAIL_V_MSG(0.0, vformat("Unhandled space parameter: '%d'. This should not happen. Please report this.", p_param));
}
}
}
void JoltSpace3D::set_param(PhysicsServer3D::SpaceParameter p_param, double p_value) {
switch (p_param) {
case PhysicsServer3D::SPACE_PARAM_CONTACT_RECYCLE_RADIUS: {
WARN_PRINT("Space-specific contact recycle radius is not supported when using Jolt Physics. Any such value will be ignored.");
} break;
case PhysicsServer3D::SPACE_PARAM_CONTACT_MAX_SEPARATION: {
WARN_PRINT("Space-specific contact max separation is not supported when using Jolt Physics. Any such value will be ignored.");
} break;
case PhysicsServer3D::SPACE_PARAM_CONTACT_MAX_ALLOWED_PENETRATION: {
WARN_PRINT("Space-specific contact max allowed penetration is not supported when using Jolt Physics. Any such value will be ignored.");
} break;
case PhysicsServer3D::SPACE_PARAM_CONTACT_DEFAULT_BIAS: {
WARN_PRINT("Space-specific contact default bias is not supported when using Jolt Physics. Any such value will be ignored.");
} break;
case PhysicsServer3D::SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_THRESHOLD: {
WARN_PRINT("Space-specific linear velocity sleep threshold is not supported when using Jolt Physics. Any such value will be ignored.");
} break;
case PhysicsServer3D::SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD: {
WARN_PRINT("Space-specific angular velocity sleep threshold is not supported when using Jolt Physics. Any such value will be ignored.");
} break;
case PhysicsServer3D::SPACE_PARAM_BODY_TIME_TO_SLEEP: {
WARN_PRINT("Space-specific body sleep time is not supported when using Jolt Physics. Any such value will be ignored.");
} break;
case PhysicsServer3D::SPACE_PARAM_SOLVER_ITERATIONS: {
WARN_PRINT("Space-specific solver iterations is not supported when using Jolt Physics. Any such value will be ignored.");
} break;
default: {
ERR_FAIL_MSG(vformat("Unhandled space parameter: '%d'. This should not happen. Please report this.", p_param));
} break;
}
}
JPH::BodyInterface &JoltSpace3D::get_body_iface() {
return physics_system->GetBodyInterfaceNoLock();
}
const JPH::BodyInterface &JoltSpace3D::get_body_iface() const {
return physics_system->GetBodyInterfaceNoLock();
}
const JPH::BodyLockInterface &JoltSpace3D::get_lock_iface() const {
return physics_system->GetBodyLockInterfaceNoLock();
}
const JPH::BroadPhaseQuery &JoltSpace3D::get_broad_phase_query() const {
return physics_system->GetBroadPhaseQuery();
}
const JPH::NarrowPhaseQuery &JoltSpace3D::get_narrow_phase_query() const {
return physics_system->GetNarrowPhaseQueryNoLock();
}
JPH::ObjectLayer JoltSpace3D::map_to_object_layer(JPH::BroadPhaseLayer p_broad_phase_layer, uint32_t p_collision_layer, uint32_t p_collision_mask) {
return layers->to_object_layer(p_broad_phase_layer, p_collision_layer, p_collision_mask);
}
void JoltSpace3D::map_from_object_layer(JPH::ObjectLayer p_object_layer, JPH::BroadPhaseLayer &r_broad_phase_layer, uint32_t &r_collision_layer, uint32_t &r_collision_mask) const {
layers->from_object_layer(p_object_layer, r_broad_phase_layer, r_collision_layer, r_collision_mask);
}
JoltReadableBody3D JoltSpace3D::read_body(const JPH::BodyID &p_body_id) const {
return JoltReadableBody3D(*this, p_body_id);
}
JoltReadableBody3D JoltSpace3D::read_body(const JoltObject3D &p_object) const {
return read_body(p_object.get_jolt_id());
}
JoltWritableBody3D JoltSpace3D::write_body(const JPH::BodyID &p_body_id) const {
return JoltWritableBody3D(*this, p_body_id);
}
JoltWritableBody3D JoltSpace3D::write_body(const JoltObject3D &p_object) const {
return write_body(p_object.get_jolt_id());
}
JoltReadableBodies3D JoltSpace3D::read_bodies(const JPH::BodyID *p_body_ids, int p_body_count) const {
return JoltReadableBodies3D(*this, p_body_ids, p_body_count);
}
JoltWritableBodies3D JoltSpace3D::write_bodies(const JPH::BodyID *p_body_ids, int p_body_count) const {
return JoltWritableBodies3D(*this, p_body_ids, p_body_count);
}
JoltPhysicsDirectSpaceState3D *JoltSpace3D::get_direct_state() {
if (direct_state == nullptr) {
direct_state = memnew(JoltPhysicsDirectSpaceState3D(this));
}
return direct_state;
}
void JoltSpace3D::set_default_area(JoltArea3D *p_area) {
if (default_area == p_area) {
return;
}
if (default_area != nullptr) {
default_area->set_default_area(false);
}
default_area = p_area;
if (default_area != nullptr) {
default_area->set_default_area(true);
}
}
JPH::BodyID JoltSpace3D::add_rigid_body(const JoltObject3D &p_object, const JPH::BodyCreationSettings &p_settings, bool p_sleeping) {
const JPH::BodyID body_id = get_body_iface().CreateAndAddBody(p_settings, p_sleeping ? JPH::EActivation::DontActivate : JPH::EActivation::Activate);
if (unlikely(body_id.IsInvalid())) {
ERR_PRINT_ONCE(vformat("Failed to create underlying Jolt Physics body for '%s'. "
"Consider increasing maximum number of bodies in project settings. "
"Maximum number of bodies is currently set to %d.",
p_object.to_string(), JoltProjectSettings::get_max_bodies()));
return JPH::BodyID();
}
bodies_added_since_optimizing += 1;
return body_id;
}
JPH::BodyID JoltSpace3D::add_soft_body(const JoltObject3D &p_object, const JPH::SoftBodyCreationSettings &p_settings, bool p_sleeping) {
const JPH::BodyID body_id = get_body_iface().CreateAndAddSoftBody(p_settings, p_sleeping ? JPH::EActivation::DontActivate : JPH::EActivation::Activate);
if (unlikely(body_id.IsInvalid())) {
ERR_PRINT_ONCE(vformat("Failed to create underlying Jolt Physics body for '%s'. "
"Consider increasing maximum number of bodies in project settings. "
"Maximum number of bodies is currently set to %d.",
p_object.to_string(), JoltProjectSettings::get_max_bodies()));
return JPH::BodyID();
}
bodies_added_since_optimizing += 1;
return body_id;
}
void JoltSpace3D::remove_body(const JPH::BodyID &p_body_id) {
JPH::BodyInterface &body_iface = get_body_iface();
body_iface.RemoveBody(p_body_id);
body_iface.DestroyBody(p_body_id);
}
void JoltSpace3D::try_optimize() {
// This makes assumptions about the underlying acceleration structure of Jolt's broad-phase, which currently uses a
// quadtree, and which gets walked with a fixed-size node stack of 128. This means that when the quadtree is
// completely unbalanced, as is the case if we add bodies one-by-one without ever stepping the simulation, like in
// the editor viewport, we would exceed this stack size (resulting in an incomplete search) as soon as we perform a
// physics query after having added somewhere in the order of 128 * 3 bodies. We leave a hefty margin just in case.
if (likely(bodies_added_since_optimizing < 128)) {
return;
}
physics_system->OptimizeBroadPhase();
bodies_added_since_optimizing = 0;
}
void JoltSpace3D::add_joint(JPH::Constraint *p_jolt_ref) {
physics_system->AddConstraint(p_jolt_ref);
}
void JoltSpace3D::add_joint(JoltJoint3D *p_joint) {
add_joint(p_joint->get_jolt_ref());
}
void JoltSpace3D::remove_joint(JPH::Constraint *p_jolt_ref) {
physics_system->RemoveConstraint(p_jolt_ref);
}
void JoltSpace3D::remove_joint(JoltJoint3D *p_joint) {
remove_joint(p_joint->get_jolt_ref());
}
#ifdef DEBUG_ENABLED
void JoltSpace3D::dump_debug_snapshot(const String &p_dir) {
const Dictionary datetime = Time::get_singleton()->get_datetime_dict_from_system();
const String datetime_str = vformat("%04d-%02d-%02d_%02d-%02d-%02d", datetime["year"], datetime["month"], datetime["day"], datetime["hour"], datetime["minute"], datetime["second"]);
const String path = p_dir + vformat("/jolt_snapshot_%s_%d.bin", datetime_str, rid.get_id());
Ref<FileAccess> file_access = FileAccess::open(path, FileAccess::ModeFlags::WRITE);
ERR_FAIL_COND_MSG(file_access.is_null(), vformat("Failed to open '%s' for writing when saving snapshot of physics space with RID '%d'.", path, rid.get_id()));
JPH::PhysicsScene physics_scene;
physics_scene.FromPhysicsSystem(physics_system);
for (JPH::BodyCreationSettings &settings : physics_scene.GetBodies()) {
const JoltObject3D *object = reinterpret_cast<const JoltObject3D *>(settings.mUserData);
if (const JoltBody3D *body = object->as_body()) {
// Since we do our own integration of gravity and damping, while leaving Jolt's own values at zero, we need to transfer over the correct values.
settings.mGravityFactor = body->get_gravity_scale();
settings.mLinearDamping = body->get_total_linear_damp();
settings.mAngularDamping = body->get_total_angular_damp();
}
settings.SetShape(JoltShape3D::without_custom_shapes(settings.GetShape()));
}
JoltStreamOutputWrapper output_stream(file_access);
physics_scene.SaveBinaryState(output_stream, true, false);
ERR_FAIL_COND_MSG(file_access->get_error() != OK, vformat("Writing snapshot of physics space with RID '%d' to '%s' failed with error '%s'.", rid.get_id(), path, VariantUtilityFunctions::error_string(file_access->get_error())));
print_line(vformat("Snapshot of physics space with RID '%d' saved to '%s'.", rid.get_id(), path));
}
const PackedVector3Array &JoltSpace3D::get_debug_contacts() const {
return contact_listener->get_debug_contacts();
}
int JoltSpace3D::get_debug_contact_count() const {
return contact_listener->get_debug_contact_count();
}
int JoltSpace3D::get_max_debug_contacts() const {
return contact_listener->get_max_debug_contacts();
}
void JoltSpace3D::set_max_debug_contacts(int p_count) {
contact_listener->set_max_debug_contacts(p_count);
}
#endif

View File

@@ -0,0 +1,149 @@
/**************************************************************************/
/* jolt_space_3d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef JOLT_SPACE_3D_H
#define JOLT_SPACE_3D_H
#include "jolt_body_accessor_3d.h"
#include "servers/physics_server_3d.h"
#include "Jolt/Jolt.h"
#include "Jolt/Core/JobSystem.h"
#include "Jolt/Core/TempAllocator.h"
#include "Jolt/Physics/Body/BodyInterface.h"
#include "Jolt/Physics/Collision/BroadPhase/BroadPhaseQuery.h"
#include "Jolt/Physics/Collision/NarrowPhaseQuery.h"
#include "Jolt/Physics/Constraints/Constraint.h"
#include "Jolt/Physics/PhysicsSystem.h"
#include <stdint.h>
class JoltArea3D;
class JoltContactListener3D;
class JoltJoint3D;
class JoltLayers;
class JoltObject3D;
class JoltPhysicsDirectSpaceState3D;
class JoltSpace3D {
JoltBodyWriter3D body_accessor;
RID rid;
JPH::JobSystem *job_system = nullptr;
JPH::TempAllocator *temp_allocator = nullptr;
JoltLayers *layers = nullptr;
JoltContactListener3D *contact_listener = nullptr;
JPH::PhysicsSystem *physics_system = nullptr;
JoltPhysicsDirectSpaceState3D *direct_state = nullptr;
JoltArea3D *default_area = nullptr;
float last_step = 0.0f;
int bodies_added_since_optimizing = 0;
bool active = false;
bool stepping = false;
bool has_stepped = false;
void _pre_step(float p_step);
void _post_step(float p_step);
public:
explicit JoltSpace3D(JPH::JobSystem *p_job_system);
~JoltSpace3D();
void step(float p_step);
void call_queries();
RID get_rid() const { return rid; }
void set_rid(const RID &p_rid) { rid = p_rid; }
bool is_active() const { return active; }
void set_active(bool p_active) { active = p_active; }
bool is_stepping() const { return stepping; }
double get_param(PhysicsServer3D::SpaceParameter p_param) const;
void set_param(PhysicsServer3D::SpaceParameter p_param, double p_value);
JPH::PhysicsSystem &get_physics_system() const { return *physics_system; }
JPH::BodyInterface &get_body_iface();
const JPH::BodyInterface &get_body_iface() const;
const JPH::BodyLockInterface &get_lock_iface() const;
const JPH::BroadPhaseQuery &get_broad_phase_query() const;
const JPH::NarrowPhaseQuery &get_narrow_phase_query() const;
JPH::ObjectLayer map_to_object_layer(JPH::BroadPhaseLayer p_broad_phase_layer, uint32_t p_collision_layer, uint32_t p_collision_mask);
void map_from_object_layer(JPH::ObjectLayer p_object_layer, JPH::BroadPhaseLayer &r_broad_phase_layer, uint32_t &r_collision_layer, uint32_t &r_collision_mask) const;
JoltReadableBody3D read_body(const JPH::BodyID &p_body_id) const;
JoltReadableBody3D read_body(const JoltObject3D &p_object) const;
JoltWritableBody3D write_body(const JPH::BodyID &p_body_id) const;
JoltWritableBody3D write_body(const JoltObject3D &p_object) const;
JoltReadableBodies3D read_bodies(const JPH::BodyID *p_body_ids, int p_body_count) const;
JoltWritableBodies3D write_bodies(const JPH::BodyID *p_body_ids, int p_body_count) const;
JoltPhysicsDirectSpaceState3D *get_direct_state();
JoltArea3D *get_default_area() const { return default_area; }
void set_default_area(JoltArea3D *p_area);
float get_last_step() const { return last_step; }
JPH::BodyID add_rigid_body(const JoltObject3D &p_object, const JPH::BodyCreationSettings &p_settings, bool p_sleeping = false);
JPH::BodyID add_soft_body(const JoltObject3D &p_object, const JPH::SoftBodyCreationSettings &p_settings, bool p_sleeping = false);
void remove_body(const JPH::BodyID &p_body_id);
void try_optimize();
void add_joint(JPH::Constraint *p_jolt_ref);
void add_joint(JoltJoint3D *p_joint);
void remove_joint(JPH::Constraint *p_jolt_ref);
void remove_joint(JoltJoint3D *p_joint);
#ifdef DEBUG_ENABLED
void dump_debug_snapshot(const String &p_dir);
const PackedVector3Array &get_debug_contacts() const;
int get_debug_contact_count() const;
int get_max_debug_contacts() const;
void set_max_debug_contacts(int p_count);
#endif
};
#endif // JOLT_SPACE_3D_H

View File

@@ -0,0 +1,120 @@
/**************************************************************************/
/* jolt_temp_allocator.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
/*
Adapted to Godot from the Jolt Physics library.
*/
/*
Copyright 2021 Jorrit Rouwe
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR
A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "jolt_temp_allocator.h"
#include "../jolt_project_settings.h"
#include "core/variant/variant.h"
#include "Jolt/Core/Memory.h"
namespace {
template <typename TValue, typename TAlignment>
constexpr TValue align_up(TValue p_value, TAlignment p_alignment) {
return (p_value + p_alignment - 1) & ~(p_alignment - 1);
}
} //namespace
JoltTempAllocator::JoltTempAllocator() :
capacity((uint64_t)JoltProjectSettings::get_temp_memory_b()),
base(static_cast<uint8_t *>(JPH::Allocate((size_t)capacity))) {
}
JoltTempAllocator::~JoltTempAllocator() {
JPH::Free(base);
}
void *JoltTempAllocator::Allocate(uint32_t p_size) {
if (p_size == 0) {
return nullptr;
}
p_size = align_up(p_size, 16U);
const uint64_t new_top = top + p_size;
void *ptr = nullptr;
if (new_top <= capacity) {
ptr = base + top;
} else {
WARN_PRINT_ONCE(vformat("Jolt Physics temporary memory allocator exceeded capacity of %d MiB. "
"Falling back to slower general-purpose allocator. "
"Consider increasing maximum temporary memory in project settings.",
JoltProjectSettings::get_temp_memory_mib()));
ptr = JPH::Allocate(p_size);
}
top = new_top;
return ptr;
}
void JoltTempAllocator::Free(void *p_ptr, uint32_t p_size) {
if (p_ptr == nullptr) {
return;
}
p_size = align_up(p_size, 16U);
const uint64_t new_top = top - p_size;
if (top <= capacity) {
if (base + new_top != p_ptr) {
CRASH_NOW_MSG("Jolt Physics temporary memory was freed in the wrong order.");
}
} else {
JPH::Free(p_ptr);
}
top = new_top;
}

View File

@@ -0,0 +1,53 @@
/**************************************************************************/
/* jolt_temp_allocator.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef JOLT_TEMP_ALLOCATOR_H
#define JOLT_TEMP_ALLOCATOR_H
#include "Jolt/Jolt.h"
#include "Jolt/Core/TempAllocator.h"
#include <stdint.h>
class JoltTempAllocator final : public JPH::TempAllocator {
uint64_t capacity = 0;
uint64_t top = 0;
uint8_t *base = nullptr;
public:
explicit JoltTempAllocator();
virtual ~JoltTempAllocator() override;
virtual void *Allocate(JPH::uint p_size) override;
virtual void Free(void *p_ptr, JPH::uint p_size) override;
};
#endif // JOLT_TEMP_ALLOCATOR_H