浏览代码

Add the final bits to Jolt backend

Panagiotis Christopoulos Charitos 10 月之前
父节点
当前提交
c960df797e

+ 95 - 2
AnKi/Physics2/Common.h

@@ -10,6 +10,7 @@
 #include <AnKi/Util/Ptr.h>
 #include <AnKi/Util/MemoryPool.h>
 #include <AnKi/Math.h>
+#include <AnKi/Util/Enum.h>
 
 #include <Jolt/Jolt.h>
 #include <Jolt/Physics/Body/Body.h>
@@ -19,6 +20,9 @@
 #include <Jolt/Physics/Collision/Shape/SphereShape.h>
 #include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
 #include <Jolt/Physics/Collision/Shape/ScaledShape.h>
+#include <Jolt/Physics/Collision/RayCast.h>
+#include <Jolt/Physics/Collision/NarrowPhaseQuery.h>
+#include <Jolt/Physics/Collision/CastResult.h>
 #include <Jolt/Physics/PhysicsSystem.h>
 #include <Jolt/Physics/Constraints/PointConstraint.h>
 #include <Jolt/Physics/Constraints/HingeConstraint.h>
@@ -57,13 +61,102 @@ private:
 
 ANKI_DEFINE_SUBMODULE_UTIL_CONTAINERS(Physics, PhysicsMemoryPool)
 
+enum class PhysicsObjectType : U8
+{
+	kNone,
+	kCollisionShape,
+	kBody,
+	kPlayerController,
+	kJoint,
+
+	kCount
+};
+ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(PhysicsObjectType);
+
+/// The base class of all physics objects.
+class PhysicsObjectBase
+{
+	template<typename, typename>
+	friend class IntrusivePtr;
+
+public:
+	PhysicsObjectBase(const PhysicsObjectBase&) = delete;
+
+	PhysicsObjectBase& operator=(const PhysicsObjectBase&) = delete;
+
+	PhysicsObjectType getType() const
+	{
+		return m_type;
+	}
+
+	void* getUserData() const
+	{
+		return m_userData;
+	}
+
+	void setUserData(void* userData)
+	{
+		m_userData = userData;
+	}
+
+protected:
+	PhysicsObjectBase(PhysicsObjectType type, void* userData)
+		: m_userData(userData)
+		, m_type(type)
+	{
+		ANKI_ASSERT(type < PhysicsObjectType::kCount);
+	}
+
+	void retain() const
+	{
+		m_refcount.fetchAdd(1);
+	}
+
+	U32 release() const
+	{
+		return m_refcount.fetchSub(1);
+	}
+
+private:
+	void* m_userData;
+	mutable Atomic<U32> m_refcount = {0};
+	PhysicsObjectType m_type;
+};
+
+/// The physics layers a PhysicsBody can be.
 enum class PhysicsLayer : U8
 {
 	kStatic,
 	kMoving,
-	kCharacter,
-	kCount
+	kPlayerController,
+	kTrigger,
+	kDebris,
+
+	kCount,
+	kFirst = 0
+};
+
+enum class PhysicsLayerBit : U8
+{
+	kNone = 0,
+
+	kStatic = 1u << 0u,
+	kMoving = 1u << 1u,
+	kPlayerController = 1u << 2u,
+	kTrigger = 1u << 3u,
+	kDebris = 1u << 4u,
+
+	kAll = kStatic | kMoving | kPlayerController | kTrigger | kDebris
 };
+ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(PhysicsLayerBit)
+
+/// Table that shows which layer collides with whom.
+inline constexpr Array<PhysicsLayerBit, U32(PhysicsLayer::kCount)> kPhysicsLayerCollisionTable = {
+	{/* kStatic           */ PhysicsLayerBit::kAll & ~(PhysicsLayerBit::kStatic | PhysicsLayerBit::kTrigger),
+	 /* kMoving           */ PhysicsLayerBit::kAll & ~(PhysicsLayerBit::kDebris),
+	 /* kPlayerController */ PhysicsLayerBit::kAll & ~(PhysicsLayerBit::kDebris),
+	 /* kTrigger          */ PhysicsLayerBit::kAll & ~(PhysicsLayerBit::kStatic | PhysicsLayerBit::kTrigger),
+	 /* kDebris           */ PhysicsLayerBit::kStatic}};
 
 #define ANKI_PHYSICS_DEFINE_PTRS(className) \
 	class className; \

+ 2 - 2
AnKi/Physics2/PhysicsBody.cpp

@@ -50,8 +50,7 @@ void PhysicsBody::init(const PhysicsBodyInitInfo& init)
 	}
 
 	settings.mFriction = init.m_friction;
-	settings.mUserData = ptrToNumber(this);
-	ANKI_ASSERT((settings.mUserData & 1u) == 0 && "We encode a flag in the lower bits");
+	settings.mUserData = ptrToNumber(static_cast<PhysicsObjectBase*>(this));
 
 	settings.mIsSensor = init.m_isTrigger;
 
@@ -65,6 +64,7 @@ void PhysicsBody::init(const PhysicsBodyInitInfo& init)
 	m_scaledShape = scaledShape;
 	m_worldTrf = init.m_transform;
 	m_isTrigger = init.m_isTrigger;
+	setUserData(init.m_userData);
 }
 
 void PhysicsBody::setTransform(const Transform& trf)

+ 9 - 29
AnKi/Physics2/PhysicsBody.h

@@ -20,22 +20,12 @@ class PhysicsTriggerCallbacks
 {
 public:
 	/// Will be called whenever a contact first touches a trigger.
-	virtual void onTriggerEnter([[maybe_unused]] const PhysicsBody& trigger, [[maybe_unused]] const PhysicsBody& obj)
-	{
-	}
-
-	/// Will be called whenever a contact first touches a trigger.
-	virtual void onTriggerEnter([[maybe_unused]] const PhysicsBody& trigger, [[maybe_unused]] const PhysicsPlayerController& obj)
+	virtual void onTriggerEnter([[maybe_unused]] const PhysicsBody& trigger, [[maybe_unused]] const PhysicsObjectBase& obj)
 	{
 	}
 
 	/// Will be called whenever a contact stops touching a trigger.
-	virtual void onTriggerExit([[maybe_unused]] const PhysicsBody& trigger, [[maybe_unused]] const PhysicsBody& obj)
-	{
-	}
-
-	/// Will be called whenever a contact stops touching a trigger.
-	virtual void onTriggerExit([[maybe_unused]] const PhysicsBody& trigger, [[maybe_unused]] const PhysicsPlayerController& obj)
+	virtual void onTriggerExit([[maybe_unused]] const PhysicsBody& trigger, [[maybe_unused]] const PhysicsObjectBase& obj)
 	{
 	}
 };
@@ -50,19 +40,16 @@ public:
 	F32 m_friction = 0.5f;
 	PhysicsLayer m_layer = PhysicsLayer::kStatic;
 	Bool m_isTrigger = false;
+	void* m_userData = nullptr;
 };
 
 /// Rigid body.
-class PhysicsBody
+class PhysicsBody : public PhysicsObjectBase
 {
 	ANKI_PHYSICS_COMMON_FRIENDS
 	friend class PhysicsBodyPtrDeleter;
 
 public:
-	PhysicsBody(const PhysicsBody&) = delete;
-
-	PhysicsBody& operator=(const PhysicsBody&) = delete;
-
 	const Transform& getTransform(U32* version = nullptr) const
 	{
 		if(version)
@@ -86,6 +73,7 @@ public:
 
 	void setPhysicsTriggerCallbacks(PhysicsTriggerCallbacks* callbacks)
 	{
+		ANKI_ASSERT(m_isTrigger);
 		m_triggerCallbacks = callbacks;
 	}
 
@@ -93,7 +81,6 @@ private:
 	JPH::Body* m_jphBody = nullptr;
 	PhysicsCollisionShapePtr m_primaryShape;
 	PhysicsCollisionShapePtr m_scaledShape;
-	mutable Atomic<U32> m_refcount = {0};
 
 	Transform m_worldTrf;
 	U32 m_worldTrfVersion = 1;
@@ -104,22 +91,15 @@ private:
 	U32 m_activated : 1 = false;
 	U32 m_isTrigger : 1 = false;
 
-	PhysicsBody() = default;
+	PhysicsBody()
+		: PhysicsObjectBase(PhysicsObjectType::kBody, nullptr)
+	{
+	}
 
 	~PhysicsBody() = default;
 
 	void init(const PhysicsBodyInitInfo& init);
 
-	void retain() const
-	{
-		m_refcount.fetchAdd(1);
-	}
-
-	U32 release() const
-	{
-		return m_refcount.fetchSub(1);
-	}
-
 	void postPhysicsUpdate();
 };
 /// @}

+ 3 - 13
AnKi/Physics2/PhysicsCollisionShape.h

@@ -13,7 +13,7 @@ namespace anki {
 namespace v2 {
 
 /// Wrapper on top of JPH collision shapes.
-class PhysicsCollisionShape
+class PhysicsCollisionShape : public PhysicsObjectBase
 {
 	ANKI_PHYSICS_COMMON_FRIENDS
 	friend class PhysicsCollisionShapePtrDeleter;
@@ -47,12 +47,12 @@ private:
 		ClassWrapper<JPH::ScaledShape> m_scaled; ///< We don't hold a reference to the target shape to avoid locking mutexes twice.
 	};
 
-	mutable Atomic<U32> m_refcount = {0};
 	U32 m_arrayIndex = kMaxU32;
 	ShapeType m_type;
 
 	PhysicsCollisionShape(ShapeType type)
-		: m_type(type)
+		: PhysicsObjectBase(PhysicsObjectType::kCollisionShape, nullptr)
+		, m_type(type)
 	{
 		ANKI_ASSERT(type < ShapeType::kCount);
 		ANKI_ASSERT(&m_shapeBase == static_cast<JPH::Shape*>(&m_box));
@@ -65,16 +65,6 @@ private:
 	{
 		m_shapeBase.destroy();
 	}
-
-	void retain() const
-	{
-		m_refcount.fetchAdd(1);
-	}
-
-	U32 release() const
-	{
-		return m_refcount.fetchSub(1);
-	}
 };
 
 } // namespace v2

+ 3 - 18
AnKi/Physics2/PhysicsJoint.h

@@ -15,16 +15,11 @@ namespace v2 {
 /// @{
 
 /// Wrapper on top of Jolt joints.
-class PhysicsJoint
+class PhysicsJoint : public PhysicsObjectBase
 {
 	ANKI_PHYSICS_COMMON_FRIENDS
 	friend class PhysicsJointPtrDeleter;
 
-public:
-	PhysicsJoint(const PhysicsJoint&) = delete;
-
-	PhysicsJoint& operator=(const PhysicsJoint&) = delete;
-
 private:
 	enum class Type : U8
 	{
@@ -44,12 +39,12 @@ private:
 	PhysicsBodyPtr m_body1;
 	PhysicsBodyPtr m_body2;
 
-	mutable Atomic<U32> m_refcount = {0};
 	U32 m_arrayIndex = kMaxU32;
 	Type m_type;
 
 	PhysicsJoint(Type type)
-		: m_type(type)
+		: PhysicsObjectBase(PhysicsObjectType::kJoint, nullptr)
+		, m_type(type)
 	{
 		ANKI_ASSERT(type < Type::kCount);
 		ANKI_ASSERT(&m_base == static_cast<JPH::TwoBodyConstraint*>(&m_point));
@@ -60,16 +55,6 @@ private:
 	{
 		m_base.destroy();
 	}
-
-	void retain() const
-	{
-		m_refcount.fetchAdd(1);
-	}
-
-	U32 release() const
-	{
-		return m_refcount.fetchSub(1);
-	}
 };
 /// @}
 

+ 5 - 7
AnKi/Physics2/PhysicsPlayerController.cpp

@@ -41,15 +41,13 @@ void PhysicsPlayerController::init(const PhysicsPlayerControllerInitInfo& init)
 	settings.mSupportingVolume = JPH::Plane(JPH::Vec3::sAxisY(), -init.m_waistWidth); // Accept contacts that touch the lower sphere of the capsule
 	settings.mEnhancedInternalEdgeRemoval = kEnhancedInternalEdgeRemoval;
 	settings.mInnerBodyShape = &m_innerStandingShape->m_capsule;
+	settings.mInnerBodyLayer = JPH::ObjectLayer(PhysicsLayer::kPlayerController);
 
-	U64 userData = ptrToNumber(this);
-	ANKI_ASSERT((userData & 1u) == 0);
-	userData |= 1u; ///< Encode an 1 to show that it's a controller
-
-	m_jphCharacter.construct(&settings, toJPH(init.m_initialPosition), JPH::Quat::sIdentity(), userData,
+	m_jphCharacter.construct(&settings, toJPH(init.m_initialPosition), JPH::Quat::sIdentity(), ptrToNumber(static_cast<PhysicsObjectBase*>(this)),
 							 &PhysicsWorld::getSingleton().m_jphPhysicsSystem);
 
 	m_position = init.m_initialPosition;
+	setUserData(init.m_userData);
 }
 
 void PhysicsPlayerController::prePhysicsUpdate(Second dt)
@@ -140,8 +138,8 @@ void PhysicsPlayerController::prePhysicsUpdate(Second dt)
 		const Bool isStanding = m_jphCharacter->GetShape() == &m_standingShape->m_shapeBase;
 		const JPH::Shape* shape = (isStanding) ? &m_crouchingShape->m_shapeBase : &m_standingShape->m_shapeBase;
 		if(m_jphCharacter->SetShape(shape, 1.5f * physicsSystem.GetPhysicsSettings().mPenetrationSlop,
-									physicsSystem.GetDefaultBroadPhaseLayerFilter(JPH::ObjectLayer(PhysicsLayer::kCharacter)),
-									physicsSystem.GetDefaultLayerFilter(JPH::ObjectLayer(PhysicsLayer::kCharacter)), {}, {}, g_tempAllocator))
+									physicsSystem.GetDefaultBroadPhaseLayerFilter(JPH::ObjectLayer(PhysicsLayer::kPlayerController)),
+									physicsSystem.GetDefaultLayerFilter(JPH::ObjectLayer(PhysicsLayer::kPlayerController)), {}, {}, g_tempAllocator))
 		{
 			const JPH::Shape* innerShape = (isStanding) ? &m_innerCrouchingShape->m_capsule : &m_innerCrouchingShape->m_capsule;
 			m_jphCharacter->SetInnerBodyShape(innerShape);

+ 6 - 13
AnKi/Physics2/PhysicsPlayerController.h

@@ -27,10 +27,12 @@ public:
 	Vec3 m_initialPosition = Vec3(0.0f);
 
 	Bool m_controlMovementDuringJump = true;
+
+	void* m_userData = nullptr;
 };
 
 /// A player controller that walks the world.
-class PhysicsPlayerController
+class PhysicsPlayerController : public PhysicsObjectBase
 {
 	ANKI_PHYSICS_COMMON_FRIENDS
 	friend class PhysicsPlayerControllerPtrDeleter;
@@ -93,26 +95,17 @@ private:
 	Vec3 m_position;
 	U32 m_positionVersion = 0;
 
-	mutable Atomic<U32> m_refcount = {0};
-
 	U32 m_arrayIndex : 29 = kMaxU32 >> 3u;
 	U32 m_controlMovementDuringJump : 1 = true;
 	U32 m_allowSliding : 1 = false;
 	U32 m_crouching : 1 = false;
 
-	PhysicsPlayerController() = default;
-
-	~PhysicsPlayerController();
-
-	void retain() const
+	PhysicsPlayerController()
+		: PhysicsObjectBase(PhysicsObjectType::kPlayerController, nullptr)
 	{
-		m_refcount.fetchAdd(1);
 	}
 
-	U32 release() const
-	{
-		return m_refcount.fetchSub(1);
-	}
+	~PhysicsPlayerController();
 
 	void init(const PhysicsPlayerControllerInitInfo& init);
 

+ 170 - 82
AnKi/Physics2/PhysicsWorld.cpp

@@ -9,56 +9,40 @@
 namespace anki {
 namespace v2 {
 
-class BodyOrController
+class BroadphaseLayer
 {
 public:
-	union
-	{
-		PhysicsBody* m_body = nullptr;
-		PhysicsPlayerController* m_controller;
-	};
+	static constexpr JPH::BroadPhaseLayer kStatic{0};
+	static constexpr JPH::BroadPhaseLayer kDynamic{1};
+	static constexpr JPH::BroadPhaseLayer kDebris{2};
 
-	Bool m_isBody = false;
+	static constexpr U32 kCount = 3;
 };
 
-static BodyOrController decodeBodyUserData(U64 userData)
+static JPH::BroadPhaseLayer objectLayerToBroadphaseLayer(PhysicsLayer objectLayer)
 {
-	BodyOrController out;
-	ANKI_ASSERT(userData);
-	if(userData & 1u)
-	{
-		out.m_controller = numberToPtr<PhysicsPlayerController*>(userData & (~1_U64));
-		out.m_isBody = false;
-	}
-	else
+	switch(PhysicsLayer(objectLayer))
 	{
-		out.m_body = numberToPtr<PhysicsBody*>(userData);
-		out.m_isBody = true;
+	case PhysicsLayer::kStatic:
+		return BroadphaseLayer::kStatic;
+	case PhysicsLayer::kDebris:
+		return BroadphaseLayer::kDebris;
+	default:
+		return BroadphaseLayer::kDynamic;
 	}
-
-	return out;
 }
 
 class BPLayerInterfaceImpl final : public JPH::BroadPhaseLayerInterface
 {
 public:
-	static constexpr JPH::BroadPhaseLayer kStaticBroadPhaseLayer{0};
-	static constexpr JPH::BroadPhaseLayer kDynamicBroadPhaseLayer{1};
-
-	virtual U32 GetNumBroadPhaseLayers() const override
+	U32 GetNumBroadPhaseLayers() const override
 	{
-		return 2;
+		return BroadphaseLayer::kCount;
 	}
 
-	virtual JPH::BroadPhaseLayer GetBroadPhaseLayer(JPH::ObjectLayer inLayer) const override
+	JPH::BroadPhaseLayer GetBroadPhaseLayer(JPH::ObjectLayer objLayer) const override
 	{
-		switch(PhysicsLayer(inLayer))
-		{
-		case PhysicsLayer::kStatic:
-			return kStaticBroadPhaseLayer;
-		default:
-			return kDynamicBroadPhaseLayer;
-		}
+		return objectLayerToBroadphaseLayer(PhysicsLayer(objLayer));
 	}
 };
 
@@ -66,12 +50,14 @@ public:
 class ObjectVsBroadPhaseLayerFilterImpl final : public JPH::ObjectVsBroadPhaseLayerFilter
 {
 public:
-	virtual bool ShouldCollide(JPH::ObjectLayer inLayer1, JPH::BroadPhaseLayer inLayer2) const override
+	Bool ShouldCollide(JPH::ObjectLayer layer1, JPH::BroadPhaseLayer layer2) const override
 	{
-		switch(PhysicsLayer(inLayer1))
+		switch(PhysicsLayer(layer1))
 		{
 		case PhysicsLayer::kStatic:
-			return inLayer2 != BPLayerInterfaceImpl::kStaticBroadPhaseLayer; // Static doesn't collide with static.
+			return layer2 != BroadphaseLayer::kStatic; // Static doesn't collide with static
+		case PhysicsLayer::kDebris:
+			return layer2 == BroadphaseLayer::kStatic; // Debris only collides with static
 		default:
 			return true;
 		}
@@ -82,15 +68,46 @@ public:
 class ObjectLayerPairFilterImpl final : public JPH::ObjectLayerPairFilter
 {
 public:
-	virtual bool ShouldCollide(JPH::ObjectLayer inObject1, JPH::ObjectLayer inObject2) const override
+	Bool ShouldCollide(JPH::ObjectLayer layer1, JPH::ObjectLayer layer2) const override
 	{
-		switch(PhysicsLayer(inObject1))
+		const PhysicsLayerBit layer1Bit = PhysicsLayerBit(1u << layer1);
+		const PhysicsLayerBit layer2Bit = PhysicsLayerBit(1u << layer2);
+
+		const PhysicsLayerBit layer1Mask = kPhysicsLayerCollisionTable[layer1];
+		const PhysicsLayerBit layer2Mask = kPhysicsLayerCollisionTable[layer2];
+
+		return !!(layer1Bit & layer2Mask) && !!(layer2Bit & layer1Mask);
+	}
+};
+
+class MaskBroadPhaseLayerFilter final : public JPH::BroadPhaseLayerFilter
+{
+public:
+	PhysicsLayerBit m_layerMask;
+
+	Bool ShouldCollide(JPH::BroadPhaseLayer inLayer) const override
+	{
+		for(PhysicsLayer layer : EnumBitsIterable<PhysicsLayer, PhysicsLayerBit>(m_layerMask))
 		{
-		case PhysicsLayer::kStatic:
-			return PhysicsLayer(inObject2) != PhysicsLayer::kStatic; // Static doesn't collide with static.
-		default:
-			return true; // Moving collides with everything
+			if(objectLayerToBroadphaseLayer(layer) == inLayer)
+			{
+				return true;
+			}
 		}
+
+		return false;
+	}
+};
+
+class MaskObjectLayerFilter final : public JPH::ObjectLayerFilter
+{
+public:
+	PhysicsLayerBit m_layerMask;
+
+	Bool ShouldCollide(JPH::ObjectLayer inLayer) const override
+	{
+		const PhysicsLayerBit inMask = PhysicsLayerBit(1u << inLayer);
+		return !!(m_layerMask & inMask);
 	}
 };
 
@@ -99,29 +116,29 @@ class PhysicsWorld::MyBodyActivationListener final : public JPH::BodyActivationL
 public:
 	void OnBodyActivated([[maybe_unused]] const JPH::BodyID& inBodyID, U64 bodyUserData) override
 	{
-		const BodyOrController userData = decodeBodyUserData(bodyUserData);
+		PhysicsObjectBase* base = numberToPtr<PhysicsObjectBase*>(bodyUserData);
 
-		if(userData.m_isBody)
+		if(base->getType() == PhysicsObjectType::kBody)
 		{
-			userData.m_body->m_activated = 1;
+			static_cast<PhysicsBody*>(base)->m_activated = 1;
 		}
 		else
 		{
-			// It's a player controller, do nothing for now
+			// Don't care
 		}
 	}
 
 	void OnBodyDeactivated([[maybe_unused]] const JPH::BodyID& inBodyID, U64 bodyUserData) override
 	{
-		const BodyOrController userData = decodeBodyUserData(bodyUserData);
+		PhysicsObjectBase* base = numberToPtr<PhysicsObjectBase*>(bodyUserData);
 
-		if(userData.m_isBody)
+		if(base->getType() == PhysicsObjectType::kBody)
 		{
-			userData.m_body->m_activated = 0;
+			static_cast<PhysicsBody*>(base)->m_activated = 0;
 		}
 		else
 		{
-			// It's a player controller, do nothing for now
+			// Don't care
 		}
 	}
 };
@@ -129,25 +146,27 @@ public:
 class PhysicsWorld::MyContactListener final : public JPH::ContactListener
 {
 public:
-	void gatherObjects(U64 body1UserData, U64 body2UserData, PhysicsBody*& trigger, BodyOrController& receiver)
+	void gatherObjects(U64 body1UserData, U64 body2UserData, PhysicsBody*& trigger, PhysicsObjectBase*& receiver)
 	{
-		const BodyOrController userData1 = decodeBodyUserData(body1UserData);
-		const BodyOrController userData2 = decodeBodyUserData(body2UserData);
+		PhysicsObjectBase* obj1 = numberToPtr<PhysicsObjectBase*>(body1UserData);
+		PhysicsObjectBase* obj2 = numberToPtr<PhysicsObjectBase*>(body2UserData);
 
-		if(userData1.m_isBody && userData1.m_body->m_isTrigger)
+		if(obj1->getType() == PhysicsObjectType::kBody && static_cast<PhysicsBody*>(obj1)->m_triggerCallbacks)
 		{
-			trigger = userData1.m_body;
-			receiver = userData2;
+			ANKI_ASSERT(obj2->getType() == PhysicsObjectType::kBody || obj2->getType() == PhysicsObjectType::kPlayerController);
+			trigger = static_cast<PhysicsBody*>(obj1);
+			receiver = obj2;
 		}
-		else if(userData2.m_isBody && userData2.m_body->m_isTrigger)
+		else if(obj2->getType() == PhysicsObjectType::kBody && static_cast<PhysicsBody*>(obj2)->m_triggerCallbacks)
 		{
-			trigger = userData2.m_body;
-			receiver = userData1;
+			ANKI_ASSERT(obj1->getType() == PhysicsObjectType::kBody || obj1->getType() == PhysicsObjectType::kPlayerController);
+			trigger = static_cast<PhysicsBody*>(obj2);
+			receiver = obj1;
 		}
 		else
 		{
 			trigger = nullptr;
-			receiver = {};
+			receiver = nullptr;
 		}
 	}
 
@@ -157,15 +176,15 @@ public:
 		// You can practically do nothing with the bodies so stash them
 
 		PhysicsBody* trigger;
-		BodyOrController receiver;
+		PhysicsObjectBase* receiver;
 		gatherObjects(inBody1.GetUserData(), inBody2.GetUserData(), trigger, receiver);
 
-		if(!trigger || trigger->m_triggerCallbacks == nullptr)
+		if(!trigger)
 		{
 			return;
 		}
 
-		const Contact contact = {trigger, receiver.m_body, receiver.m_isBody};
+		const Contact contact = {trigger, receiver};
 
 		PhysicsWorld& world = PhysicsWorld::getSingleton();
 
@@ -180,16 +199,16 @@ public:
 		PhysicsWorld& world = PhysicsWorld::getSingleton();
 
 		PhysicsBody* trigger;
-		BodyOrController receiver;
+		PhysicsObjectBase* receiver;
 		gatherObjects(world.m_jphPhysicsSystem->GetBodyInterfaceNoLock().GetUserData(pair.GetBody1ID()),
 					  world.m_jphPhysicsSystem->GetBodyInterfaceNoLock().GetUserData(pair.GetBody2ID()), trigger, receiver);
 
-		if(!trigger || trigger->m_triggerCallbacks == nullptr)
+		if(!trigger)
 		{
 			return;
 		}
 
-		const Contact contact = {trigger, receiver.m_body, receiver.m_isBody};
+		const Contact contact = {trigger, receiver};
 
 		LockGuard lock(world.m_deletedContactsMtx);
 		world.m_deletedContacts.emplaceBack(contact);
@@ -363,28 +382,14 @@ void PhysicsWorld::update(Second dt)
 
 		for(Contact& contact : m_insertedContacts)
 		{
-			if(contact.m_recieverIsBody)
-			{
-				contact.m_trigger->m_triggerCallbacks->onTriggerEnter(*contact.m_trigger, *contact.m_recieverBody);
-			}
-			else
-			{
-				contact.m_trigger->m_triggerCallbacks->onTriggerEnter(*contact.m_trigger, *contact.m_recieverController);
-			}
+			contact.m_trigger->m_triggerCallbacks->onTriggerEnter(*contact.m_trigger, *contact.m_receiver);
 		}
 
 		m_insertedContacts.destroy();
 
 		for(Contact& contact : m_deletedContacts)
 		{
-			if(contact.m_recieverIsBody)
-			{
-				contact.m_trigger->m_triggerCallbacks->onTriggerExit(*contact.m_trigger, *contact.m_recieverBody);
-			}
-			else
-			{
-				contact.m_trigger->m_triggerCallbacks->onTriggerExit(*contact.m_trigger, *contact.m_recieverController);
-			}
+			contact.m_trigger->m_triggerCallbacks->onTriggerExit(*contact.m_trigger, *contact.m_receiver);
 		}
 
 		m_deletedContacts.destroy();
@@ -498,5 +503,88 @@ PhysicsPlayerControllerPtr PhysicsWorld::newPlayerController(const PhysicsPlayer
 	return PhysicsPlayerControllerPtr(newChar);
 }
 
+RayHitResult PhysicsWorld::jphToAnKi(const JPH::RRayCast& ray, const JPH::RayCastResult& hit)
+{
+	RayHitResult result;
+
+	const JPH::RVec3 hitPosJph = ray.GetPointOnRay(hit.mFraction);
+	result.m_hitPosition = toAnKi(hitPosJph);
+
+	const U64 userData = m_jphPhysicsSystem->GetBodyInterfaceNoLock().GetUserData(hit.mBodyID);
+	result.m_object = numberToPtr<PhysicsObjectBase*>(userData);
+
+	JPH::BodyLockRead lock(m_jphPhysicsSystem->GetBodyLockInterfaceNoLock(), hit.mBodyID);
+	if(lock.Succeeded())
+	{
+		result.m_normal = toAnKi(lock.GetBody().GetWorldSpaceSurfaceNormal(hit.mSubShapeID2, hitPosJph));
+	}
+
+	return result;
+}
+
+Bool PhysicsWorld::castRayClosestHit(const Vec3& rayStart, const Vec3& rayEnd, PhysicsLayerBit layers, RayHitResult& result)
+{
+	MaskBroadPhaseLayerFilter broadphaseFilter;
+	broadphaseFilter.m_layerMask = layers;
+
+	MaskObjectLayerFilter objectFilter;
+	objectFilter.m_layerMask = layers;
+
+	JPH::RRayCast ray;
+	ray.mOrigin = toJPH(rayStart);
+	ray.mDirection = toJPH(rayEnd - rayStart); // Not exactly a direction if it's not normalized but anyway
+	JPH::RayCastResult hit;
+	const Bool success = m_jphPhysicsSystem->GetNarrowPhaseQueryNoLock().CastRay(ray, hit, broadphaseFilter, objectFilter);
+
+	if(success)
+	{
+		result = jphToAnKi(ray, hit);
+	}
+	else
+	{
+		result = {};
+	}
+
+	return success;
+}
+
+Bool PhysicsWorld::castRayAllHitsInternal(const Vec3& rayStart, const Vec3& rayEnd, PhysicsLayerBit layers,
+										  PhysicsDynamicArray<RayHitResult>& results)
+{
+	MaskBroadPhaseLayerFilter broadphaseFilter;
+	broadphaseFilter.m_layerMask = layers;
+
+	MaskObjectLayerFilter objectFilter;
+	objectFilter.m_layerMask = layers;
+
+	JPH::RRayCast ray;
+	ray.mOrigin = toJPH(rayStart);
+	ray.mDirection = toJPH(rayEnd - rayStart); // Not exactly a direction if it's not normalized but anyway
+	JPH::RayCastResult hit;
+
+	JPH::RayCastSettings settings;
+
+	class MyCastRayCollector final : public JPH::CastRayCollector
+	{
+	public:
+		PhysicsDynamicArray<RayHitResult>* m_resArray;
+		JPH::RRayCast m_ray;
+		PhysicsWorld* m_world;
+
+		void AddHit(const JPH::RayCastResult& hit) override
+		{
+			const RayHitResult result = m_world->jphToAnKi(m_ray, hit);
+			m_resArray->emplaceBack(result);
+		}
+	} collector;
+	collector.m_resArray = &results;
+	collector.m_ray = ray;
+	collector.m_world = this;
+
+	m_jphPhysicsSystem->GetNarrowPhaseQueryNoLock().CastRay(ray, settings, collector, broadphaseFilter, objectFilter);
+
+	return results.getSize() > 0;
+}
+
 } // namespace v2
 } // end namespace anki

+ 33 - 8
AnKi/Physics2/PhysicsWorld.h

@@ -18,6 +18,15 @@ namespace v2 {
 /// @addtogroup physics
 /// @{
 
+/// @memberof PhysicsWorld
+class RayHitResult
+{
+public:
+	PhysicsObjectBase* m_object = nullptr;
+	Vec3 m_normal; ///< In world space.
+	Vec3 m_hitPosition; ///< In world space.
+};
+
 /// The master container for all physics related stuff.
 class PhysicsWorld : public MakeSingleton<PhysicsWorld>
 {
@@ -45,6 +54,25 @@ public:
 
 	void update(Second dt);
 
+	/// Returns the closest hit.
+	Bool castRayClosestHit(const Vec3& rayStart, const Vec3& rayEnd, PhysicsLayerBit layers, RayHitResult& result);
+
+	/// Executes a callback for all hits found.
+	template<typename TFunc>
+	Bool castRayAllHits(const Vec3& rayStart, const Vec3& rayEnd, PhysicsLayerBit layers, TFunc func)
+	{
+		PhysicsDynamicArray<RayHitResult> results;
+		const Bool success = castRayAllHits(rayStart, rayEnd, layers, results);
+		if(success)
+		{
+			for(RayHitResult& res : results)
+			{
+				func(res);
+			}
+		}
+		return success;
+	}
+
 private:
 	class MyBodyActivationListener;
 	class MyContactListener;
@@ -61,14 +89,7 @@ private:
 	{
 	public:
 		PhysicsBody* m_trigger;
-
-		union
-		{
-			PhysicsBody* m_recieverBody;
-			PhysicsPlayerController* m_recieverController;
-		};
-
-		Bool m_recieverIsBody;
+		PhysicsObjectBase* m_receiver;
 	};
 
 	ClassWrapper<JPH::PhysicsSystem> m_jphPhysicsSystem;
@@ -101,6 +122,10 @@ private:
 	PhysicsJointPtr newJoint(PhysicsJoint::Type type, PhysicsBody* body1, PhysicsBody* body2, TArgs&&... args);
 
 	PhysicsCollisionShapePtr newScaleCollisionObject(const Vec3& scale, PhysicsCollisionShape* baseShape);
+
+	RayHitResult jphToAnKi(const JPH::RRayCast& ray, const JPH::RayCastResult& hit);
+
+	Bool castRayAllHitsInternal(const Vec3& rayStart, const Vec3& rayEnd, PhysicsLayerBit layers, PhysicsDynamicArray<RayHitResult>& results);
 };
 /// @}
 

+ 1 - 1
AnKi/Util/Enum.h

@@ -239,7 +239,7 @@ private:
 
 /// Allow a mask to be used in a for range loop of a compatible enum.
 /// @code
-/// for(SomeEnum type : EnumIterableBits<SomeEnum, SomeCompatibleBitEnum>(bitmask))
+/// for(SomeEnum type : EnumBitsIterable<SomeEnum, SomeCompatibleBitEnum>(bitmask))
 /// {
 /// 	...
 /// }