Browse Source

Some refactoring of body and joint components

Panagiotis Christopoulos Charitos 10 months ago
parent
commit
308d5fa052

+ 2 - 0
AnKi/Physics2/Common.h

@@ -20,6 +20,8 @@
 #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/Shape/ConvexHullShape.h>
+#include <Jolt/Physics/Collision/Shape/MeshShape.h>
 #include <Jolt/Physics/Collision/RayCast.h>
 #include <Jolt/Physics/Collision/NarrowPhaseQuery.h>
 #include <Jolt/Physics/Collision/CastResult.h>

+ 5 - 2
AnKi/Physics2/PhysicsBody.cpp

@@ -54,7 +54,7 @@ void PhysicsBody::init(const PhysicsBodyInitInfo& init)
 
 	settings.mIsSensor = init.m_isTrigger;
 
-	// Call the thread-safe version because characters will be creating bodies as well
+	// Call the thread-safe version because many threads may try to create bodies
 	JPH::Body* jphBody = world.m_jphPhysicsSystem->GetBodyInterface().CreateBody(settings);
 	world.m_jphPhysicsSystem->GetBodyInterface().AddBody(jphBody->GetID(), JPH::EActivation::Activate);
 
@@ -64,6 +64,7 @@ void PhysicsBody::init(const PhysicsBodyInitInfo& init)
 	m_scaledShape = scaledShape;
 	m_worldTrf = init.m_transform;
 	m_isTrigger = init.m_isTrigger;
+	m_mass = init.m_mass;
 	setUserData(init.m_userData);
 }
 
@@ -83,7 +84,9 @@ void PhysicsBody::setTransform(const Transform& trf)
 
 void PhysicsBody::applyForce(const Vec3& force, const Vec3& relPos)
 {
-	PhysicsWorld::getSingleton().m_jphPhysicsSystem->GetBodyInterfaceNoLock().AddForce(m_jphBody->GetID(), toJPH(force), toJPH(relPos));
+	const Vec3 worldForcePos = m_worldTrf.transform(relPos);
+
+	PhysicsWorld::getSingleton().m_jphPhysicsSystem->GetBodyInterfaceNoLock().AddForce(m_jphBody->GetID(), toJPH(force), toJPH(worldForcePos));
 }
 
 void PhysicsBody::applyForce(const Vec3& force)

+ 9 - 0
AnKi/Physics2/PhysicsBody.h

@@ -61,6 +61,8 @@ public:
 
 	void setTransform(const Transform& trf);
 
+	/// @param force In Newton and in world space.
+	/// @param relPos The position to apply the force. It's in the local space of the body.
 	void applyForce(const Vec3& force, const Vec3& relPos);
 
 	/// Apply force to the center of mass.
@@ -77,6 +79,11 @@ public:
 		m_triggerCallbacks = callbacks;
 	}
 
+	F32 getMass() const
+	{
+		return m_mass;
+	}
+
 private:
 	JPH::Body* m_jphBody = nullptr;
 	PhysicsCollisionShapePtr m_primaryShape;
@@ -90,6 +97,8 @@ private:
 	U32 m_activated : 1 = false;
 	U32 m_isTrigger : 1 = false;
 
+	F32 m_mass = 0.0f;
+
 	PhysicsBody()
 		: PhysicsObjectBase(PhysicsObjectType::kBody)
 	{

+ 2 - 0
AnKi/Physics2/PhysicsCollisionShape.h

@@ -32,6 +32,8 @@ private:
 		ClassWrapper<JPH::BoxShape> m_box;
 		ClassWrapper<JPH::SphereShape> m_sphere;
 		ClassWrapper<JPH::CapsuleShape> m_capsule;
+		ClassWrapper<JPH::ConvexHullShape> m_convex;
+		ClassWrapper<JPH::MeshShape> m_mesh;
 		ClassWrapper<JPH::ScaledShape> m_scaled; ///< We don't hold a reference to the target shape to avoid locking mutexes twice.
 	};
 

+ 47 - 0
AnKi/Physics2/PhysicsWorld.cpp

@@ -459,6 +459,53 @@ PhysicsCollisionShapePtr PhysicsWorld::newCapsuleCollisionShape(F32 height, F32
 	return newCollisionShape<JPH::CapsuleShape>(height / 2.0f, radius);
 }
 
+PhysicsCollisionShapePtr PhysicsWorld::newConvexHullShape(ConstWeakArray<Vec3> positions)
+{
+	JPH::Array<JPH::Vec3> verts;
+	verts.resize(positions.getSize());
+
+	for(U32 i = 0; i < positions.getSize(); ++i)
+	{
+		verts[i] = toJPH(positions[i]);
+	}
+
+	JPH::ConvexHullShapeSettings settings(std::move(verts));
+	settings.SetEmbedded();
+
+	JPH::Shape::ShapeResult outResult;
+	PhysicsCollisionShapePtr out = newCollisionShape<JPH::ConvexHullShape>(settings, outResult);
+	ANKI_ASSERT(outResult.IsValid());
+	return out;
+}
+
+PhysicsCollisionShapePtr PhysicsWorld::newStaticMeshShape(ConstWeakArray<Vec3> positions, ConstWeakArray<U32> indices)
+{
+	ANKI_ASSERT(positions.getSize() && indices.getSize() && indices.getSize() % 3 == 0);
+
+	JPH::VertexList verts;
+	verts.resize(positions.getSize());
+	for(U32 i = 0; i < positions.getSize(); ++i)
+	{
+		verts[i] = {positions[i].x(), positions[i].y(), positions[i].z()};
+	}
+
+	JPH::IndexedTriangleList idx;
+	idx.resize(indices.getSize() / 3);
+	for(U32 i = 0; i < indices.getSize(); i += 3)
+	{
+		const U32 material = 0;
+		idx[i] = JPH::IndexedTriangle(indices[i], indices[i + 1], indices[i + 2], material);
+	}
+
+	JPH::MeshShapeSettings settings(std::move(verts), std::move(idx));
+	settings.SetEmbedded();
+
+	JPH::Shape::ShapeResult outResult;
+	PhysicsCollisionShapePtr out = newCollisionShape<JPH::MeshShape>(settings, outResult);
+	ANKI_ASSERT(outResult.IsValid());
+	return out;
+}
+
 PhysicsCollisionShapePtr PhysicsWorld::newScaleCollisionObject(const Vec3& scale, PhysicsCollisionShape* baseShape)
 {
 	return newCollisionShape<JPH::ScaledShape>(&baseShape->m_shapeBase, toJPH(scale));

+ 3 - 0
AnKi/Physics2/PhysicsWorld.h

@@ -36,6 +36,7 @@ public:
 };
 
 /// The master container for all physics related stuff.
+/// The newXXX methods are thread-safe between themselves and the dereference of the pointers. Every other method is not thread-safe.
 class PhysicsWorld : public MakeSingleton<PhysicsWorld>
 {
 	template<typename>
@@ -53,6 +54,8 @@ public:
 	PhysicsCollisionShapePtr newSphereCollisionShape(F32 radius);
 	PhysicsCollisionShapePtr newBoxCollisionShape(Vec3 extend);
 	PhysicsCollisionShapePtr newCapsuleCollisionShape(F32 height, F32 radius); ///< Capsule axis is in Y.
+	PhysicsCollisionShapePtr newConvexHullShape(ConstWeakArray<Vec3> positions);
+	PhysicsCollisionShapePtr newStaticMeshShape(ConstWeakArray<Vec3> positions, ConstWeakArray<U32> indices);
 
 	PhysicsBodyPtr newPhysicsBody(const PhysicsBodyInitInfo& init);
 

+ 13 - 3
AnKi/Resource/CpuMeshResource.cpp

@@ -17,11 +17,21 @@ Error CpuMeshResource::load(const ResourceFilename& filename, [[maybe_unused]] B
 	ANKI_CHECK(loader.load(filename));
 	ANKI_CHECK(loader.storeIndicesAndPosition(0, m_indices, m_positions));
 
-	// Create the collision shape
-	const Bool convex = !!(loader.getHeader().m_flags & MeshBinaryFlag::kConvex);
-	m_physicsShape = PhysicsWorld::getSingleton().newInstance<PhysicsTriangleSoup>(m_positions, m_indices, convex);
+	m_isConvex = !!(loader.getHeader().m_flags & MeshBinaryFlag::kConvex);
 
 	return Error::kNone;
 }
 
+const PhysicsCollisionShapePtr& CpuMeshResource::getOrCreateCollisionShape([[maybe_unused]] Bool isStatic) const
+{
+	LockGuard lock(m_shapeMtx);
+
+	if(!m_collisionShape)
+	{
+		m_collisionShape = PhysicsWorld::getSingleton().newInstance<PhysicsTriangleSoup>(m_positions, m_indices, m_isConvex);
+	}
+
+	return m_collisionShape;
+}
+
 } // end namespace anki

+ 6 - 5
AnKi/Resource/CpuMeshResource.h

@@ -37,15 +37,16 @@ public:
 		return m_indices;
 	}
 
-	const PhysicsCollisionShapePtr& getCollisionShape() const
-	{
-		return m_physicsShape;
-	}
+	const PhysicsCollisionShapePtr& getOrCreateCollisionShape(Bool isStatic) const;
 
 private:
 	ResourceDynamicArray<Vec3> m_positions;
 	ResourceDynamicArray<U32> m_indices;
-	PhysicsCollisionShapePtr m_physicsShape;
+
+	mutable PhysicsCollisionShapePtr m_collisionShape;
+	mutable SpinLock m_shapeMtx;
+
+	Bool m_isConvex = false;
 };
 /// @}
 

+ 40 - 39
AnKi/Scene/Components/BodyComponent.cpp

@@ -28,27 +28,14 @@ BodyComponent::~BodyComponent()
 void BodyComponent::loadMeshResource(CString meshFilename)
 {
 	CpuMeshResourcePtr rsrc;
-	const Error err = ResourceManager::getSingleton().loadResource(meshFilename, rsrc);
-	if(err)
+	if(ResourceManager::getSingleton().loadResource(meshFilename, rsrc))
 	{
 		ANKI_SCENE_LOGE("Failed to load mesh");
 		return;
 	}
 
 	m_mesh = std::move(rsrc);
-
-	const Transform prevTransform = m_node->getWorldTransform();
-	const F32 prevMass = (m_body) ? m_body->getMass() : 0.0f;
-
-	PhysicsBodyInitInfo init;
-	init.m_mass = prevMass;
-	init.m_transform = prevTransform;
-	init.m_shape = m_mesh->getCollisionShape();
-	m_body = PhysicsWorld::getSingleton().newInstance<PhysicsBody>(init);
-	m_body->setUserData(this);
-	m_body->setTransform(m_node->getWorldTransform());
-
-	m_dirty = true;
+	m_shapeDirty = true;
 }
 
 void BodyComponent::setMeshFromModelComponent(U32 patchIndex)
@@ -73,44 +60,48 @@ CString BodyComponent::getMeshResourceFilename() const
 
 void BodyComponent::setMass(F32 mass)
 {
-	if(!ANKI_SCENE_ASSERT(mass >= 0.0f && m_body.isCreated()))
+	if(!ANKI_SCENE_ASSERT(mass >= 0.0f))
 	{
 		return;
 	}
 
-	if((m_body->getMass() == 0.0f && mass != 0.0f) || (m_body->getMass() != 0.0f && mass == 0.0f))
+	m_mass = mass;
+	m_shapeDirty = true;
+}
+
+void BodyComponent::teleportTo(const Transform& trf)
+{
+	m_teleportTrf = trf;
+	m_teleported = true;
+
+	m_node->setLocalTransform(trf); // Set that just to be sure
+}
+
+Error BodyComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+{
+	if(m_shapeDirty)
 	{
-		// Will become from static to dynamic or the opposite, re-create the body
+		updated = true;
+		m_shapeDirty = false;
+
+		m_body.reset(nullptr);
 
-		const Transform& prevTransform = m_body->getTransform();
 		PhysicsBodyInitInfo init;
-		init.m_transform = prevTransform;
-		init.m_mass = mass;
-		init.m_shape = m_mesh->getCollisionShape();
+		init.m_mass = m_mass;
+		init.m_transform = (m_teleported) ? m_teleportTrf : m_node->getWorldTransform();
+		init.m_shape = m_mesh->getOrCreateCollisionShape(m_mass == 0.0f);
 		m_body = PhysicsWorld::getSingleton().newInstance<PhysicsBody>(init);
 		m_body->setUserData(this);
 
-		m_dirty = true;
+		m_teleported = false; // Cancel teleportation since the body was re-created
 	}
-	else
-	{
-		m_body->setMass(mass);
-	}
-}
 
-void BodyComponent::teleportTo(const Transform& trf)
-{
-	if(ANKI_SCENE_ASSERT(m_body.isCreated()))
+	if(m_body && m_teleported)
 	{
-		m_body->setTransform(trf);
-		m_node->setLocalTransform(trf); // Set that just to be sure
+		updated = true;
+		m_teleported = false;
+		m_body->setTransform(m_teleportTrf);
 	}
-}
-
-Error BodyComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
-{
-	updated = m_dirty;
-	m_dirty = false;
 
 	if(m_body && m_body->getTransform() != info.m_node->getWorldTransform())
 	{
@@ -118,6 +109,16 @@ Error BodyComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 		info.m_node->setLocalTransform(m_body->getTransform());
 	}
 
+	if(m_force.getLengthSquared() > 0.0f)
+	{
+		if(m_body)
+		{
+			m_body->applyForce(m_force, m_forcePosition);
+		}
+
+		m_force = Vec3(0.0f);
+	}
+
 	return Error::kNone;
 }
 

+ 40 - 6
AnKi/Scene/Components/BodyComponent.h

@@ -30,31 +30,65 @@ public:
 
 	CString getMeshResourceFilename() const;
 
+	void removeBody()
+	{
+		m_body.reset(nullptr);
+		m_teleported = false;
+		m_force = Vec3(0.0f);
+	}
+
 	void setMass(F32 mass);
 
 	F32 getMass() const
 	{
-		return (m_body) ? m_body->getMass() : 0.0f;
+		return m_mass;
 	}
 
-	PhysicsBodyPtr getPhysicsBody() const
+	const PhysicsBodyPtr& getPhysicsBody() const
 	{
 		return m_body;
 	}
 
-	Bool isEnabled() const
+	void applyForce(Vec3 force, Vec3 relativePosition)
 	{
-		return m_mesh.isCreated();
+		m_force = force;
+		m_forcePosition = relativePosition;
 	}
 
 	void teleportTo(const Transform& trf);
 
 private:
+	enum class ShapeType : U8
+	{
+		kMesh,
+		kAabb,
+		kSphere,
+		kCount
+	};
+
 	SceneNode* m_node = nullptr;
+	PhysicsBodyPtr m_body;
+
 	ModelComponent* m_modelc = nullptr;
 	CpuMeshResourcePtr m_mesh;
-	PhysicsBodyPtr m_body;
-	Bool m_dirty = true;
+
+	union
+	{
+		Vec3 m_aabbExtend = Vec3(0.0f);
+		F32 m_sphereRadius;
+	};
+
+	F32 m_mass = 0.0f;
+
+	Transform m_teleportTrf;
+
+	Vec3 m_force = Vec3(0.0f);
+	Vec3 m_forcePosition = Vec3(0.0f);
+
+	Bool m_shapeDirty = true;
+	Bool m_teleported = false;
+
+	ShapeType m_shapeType = ShapeType::kCount;
 
 	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
 

+ 138 - 50
AnKi/Scene/Components/JointComponent.cpp

@@ -14,7 +14,32 @@ class JointComponent::JointNode : public IntrusiveListEnabled<JointNode>
 {
 public:
 	PhysicsJointPtr m_joint;
-	SceneNode* m_parentNode = nullptr; ///< Keep track the node that is connected with this node through a joint.
+	F32 m_breakingImpulse = 0.0f;
+
+	class P2P
+	{
+	public:
+		Vec3 m_relPosBody1 = Vec3(kMaxF32);
+		Vec3 m_relPosBody2 = Vec3(kMaxF32);
+	};
+
+	class Hinge
+	{
+	public:
+		Vec3 m_relPosBody1 = Vec3(kMaxF32);
+		Vec3 m_relPosBody2 = Vec3(kMaxF32);
+		Vec3 m_axis = Vec3(0.0f);
+	};
+
+	union
+	{
+		Hinge m_hinge = {};
+		P2P m_p2p;
+	};
+
+	JointType m_type = JointType::kCount;
+
+	JointNode() = default;
 };
 
 JointComponent::~JointComponent()
@@ -49,79 +74,92 @@ Vec3 JointComponent::computeLocalPivotFromFactors(const PhysicsBodyPtr& body, co
 	return out;
 }
 
-template<typename TJoint, typename... TArgs>
-void JointComponent::newJoint(const Vec3& relPosFactor, F32 breakingImpulse, TArgs&&... args)
+void JointComponent::newPoint2PointJoint(const Vec3& relPosFactor, F32 breakingImpulse)
 {
-	BodyComponent* bodyc = m_bodyc;
+	JointNode* newNode = newInstance<JointNode>(SceneMemoryPool::getSingleton());
 
-	if(bodyc)
-	{
-		Vec3 relPos = computeLocalPivotFromFactors(bodyc->getPhysicsBody(), relPosFactor);
+	newNode->m_type = JointType::kPoint;
+	newNode->m_p2p.m_relPosBody1 = relPosFactor;
+	newNode->m_breakingImpulse = breakingImpulse;
 
-		PhysicsJointPtr joint = PhysicsWorld::getSingleton().newInstance<TJoint>(bodyc->getPhysicsBody(), relPos, std::forward<TArgs>(args)...);
-		joint->setBreakingImpulseThreshold(breakingImpulse);
+	m_jointList.pushBack(newNode);
+}
 
-		JointNode* newNode = newInstance<JointNode>(SceneMemoryPool::getSingleton());
-		newNode->m_joint = std::move(joint);
-		m_jointList.pushBack(newNode);
-	}
-	else
-	{
-		ANKI_SCENE_LOGW("Can't create new joint. The node is missing a BodyComponent");
-	}
+void JointComponent::newPoint2PointJoint2(const Vec3& relPosFactorA, const Vec3& relPosFactorB, F32 breakingImpulse)
+{
+	JointNode* newNode = newInstance<JointNode>(SceneMemoryPool::getSingleton());
+
+	newNode->m_type = JointType::kPoint;
+	newNode->m_p2p.m_relPosBody1 = relPosFactorA;
+	newNode->m_p2p.m_relPosBody2 = relPosFactorB;
+	newNode->m_breakingImpulse = breakingImpulse;
+
+	m_jointList.pushBack(newNode);
 }
 
-void JointComponent::newPoint2PointJoint(const Vec3& relPosFactor, F32 breakingImpulse)
+void JointComponent::newHingeJoint(const Vec3& relPosFactor, const Vec3& axis, F32 breakingImpulse)
 {
-	newJoint<PhysicsPoint2PointJoint>(relPosFactor, breakingImpulse);
+	JointNode* newNode = newInstance<JointNode>(SceneMemoryPool::getSingleton());
+
+	newNode->m_type = JointType::kHinge;
+	newNode->m_hinge.m_relPosBody1 = relPosFactor;
+	newNode->m_hinge.m_axis = axis;
+	newNode->m_breakingImpulse = breakingImpulse;
+
+	m_jointList.pushBack(newNode);
 }
 
-void JointComponent::newPoint2PointJoint2(const Vec3& relPosFactorA, const Vec3& relPosFactorB, F32 breakingImpulse)
+Error JointComponent::update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated)
 {
-	BodyComponent* bodycA = m_bodyc;
-	BodyComponent* bodycB = nullptr;
-	if(m_node->getParent())
+	SceneNode* parent = m_node->getParent();
+	BodyComponent* bodyc1 = (m_bodyc && m_bodyc->getPhysicsBody()) ? m_bodyc : nullptr;
+	BodyComponent* bodyc2 = nullptr;
+	if(parent)
 	{
-		bodycB = m_node->getParent()->tryGetFirstComponentOfType<BodyComponent>();
+		bodyc2 = parent->tryGetFirstComponentOfType<BodyComponent>();
+		bodyc2 = (bodyc2 && bodyc2->getPhysicsBody()) ? bodyc2 : nullptr;
 	}
 
-	if(bodycA && bodycB)
-	{
-		Vec3 relPosA = computeLocalPivotFromFactors(bodycA->getPhysicsBody(), relPosFactorA);
-		Vec3 relPosB = computeLocalPivotFromFactors(bodycB->getPhysicsBody(), relPosFactorB);
+	auto resetJoints = [this]() -> Bool {
+		U32 resetCount = 0;
+		for(JointNode& node : m_jointList)
+		{
+			resetCount += !!(node.m_joint);
+			node.m_joint.reset(nullptr);
+		}
 
-		PhysicsJointPtr joint =
-			PhysicsWorld::getSingleton().newInstance<PhysicsPoint2PointJoint>(bodycA->getPhysicsBody(), relPosA, bodycB->getPhysicsBody(), relPosB);
-		joint->setBreakingImpulseThreshold(breakingImpulse);
+		return resetCount > 0;
+	};
 
-		JointNode* newNode = newInstance<JointNode>(SceneMemoryPool::getSingleton());
-		newNode->m_joint = std::move(joint);
-		newNode->m_parentNode = m_node->getParent();
-		m_jointList.pushBack(newNode);
-	}
-	else
+	if(!bodyc1)
 	{
-		ANKI_SCENE_LOGW("Can't create new joint. The node or its parent are missing a BodyComponent");
+		// No body no joints
+		if(resetJoints())
+		{
+			updated = true;
+		}
+		return Error::kNone;
 	}
-}
 
-void JointComponent::newHingeJoint(const Vec3& relPosFactor, const Vec3& axis, F32 breakingImpulse)
-{
-	newJoint<PhysicsHingeJoint>(relPosFactor, breakingImpulse, axis);
-}
+	const Bool parentChanged = parent && m_parentUuid != parent->getUuid();
+	if(parentChanged)
+	{
+		// New parent, reset joints
+		updated = true;
+		m_parentUuid = parent->getUuid();
+		resetJoints();
+	}
 
-Error JointComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
-{
-	// Iterate the joints and check if the connected scene node is not the parent of this node anymore.
+	// Remove broken joints
 	while(true)
 	{
 		Bool erasedOne = false;
-		for(JointNode& joint : m_jointList)
+		for(JointNode& node : m_jointList)
 		{
-			if(joint.m_parentNode != info.m_node->getParent() || joint.m_joint->isBroken())
+			if(node.m_joint && node.m_joint->isBroken())
 			{
-				m_jointList.erase(&joint);
-				deleteInstance(SceneMemoryPool::getSingleton(), &joint);
+				m_jointList.erase(&node);
+				deleteInstance(SceneMemoryPool::getSingleton(), &node);
 				erasedOne = true;
 				updated = true;
 				break;
@@ -134,6 +172,56 @@ Error JointComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 		}
 	}
 
+	// Create new joints
+	for(JointNode& node : m_jointList)
+	{
+		if(node.m_joint)
+		{
+			continue;
+		}
+
+		updated = true;
+
+		switch(node.m_type)
+		{
+		case JointType::kPoint:
+		{
+			const Vec3 relPos1 = computeLocalPivotFromFactors(bodyc1->getPhysicsBody(), node.m_p2p.m_relPosBody1);
+
+			if(node.m_p2p.m_relPosBody2 != Vec3(kMaxF32) && bodyc2)
+			{
+				const Vec3 relPos2 = computeLocalPivotFromFactors(bodyc2->getPhysicsBody(), node.m_p2p.m_relPosBody2);
+
+				node.m_joint = PhysicsWorld::getSingleton().newInstance<PhysicsPoint2PointJoint>(bodyc1->getPhysicsBody(), relPos1,
+																								 bodyc2->getPhysicsBody(), relPos2);
+			}
+			else
+			{
+				node.m_joint = PhysicsWorld::getSingleton().newInstance<PhysicsPoint2PointJoint>(bodyc1->getPhysicsBody(), relPos1);
+			}
+			break;
+		}
+		case JointType::kHinge:
+		{
+			const Vec3 relPos1 = computeLocalPivotFromFactors(bodyc1->getPhysicsBody(), node.m_hinge.m_relPosBody1);
+
+			if(node.m_hinge.m_relPosBody2 != Vec3(kMaxF32) && bodyc2)
+			{
+				ANKI_ASSERT(!"TODO");
+			}
+			else
+			{
+				node.m_joint = PhysicsWorld::getSingleton().newInstance<PhysicsHingeJoint>(bodyc1->getPhysicsBody(), relPos1, node.m_hinge.m_axis);
+			}
+			break;
+		}
+		default:
+			ANKI_ASSERT(0);
+		}
+
+		node.m_joint->setBreakingImpulseThreshold(node.m_breakingImpulse);
+	}
+
 	return Error::kNone;
 }
 

+ 9 - 3
AnKi/Scene/Components/JointComponent.h

@@ -39,18 +39,24 @@ public:
 private:
 	class JointNode;
 
+	enum class JointType : U8
+	{
+		kPoint,
+		kHinge,
+		kCount
+	};
+
 	SceneNode* m_node = nullptr;
 	BodyComponent* m_bodyc = nullptr;
 
+	U32 m_parentUuid = 0;
+
 	IntrusiveList<JointNode> m_jointList;
 
 	/// Given a 3 coodrinates that lie in [-1.0, +1.0] compute a pivot point that lies into the AABB of the collision
 	/// shape of the body.
 	static Vec3 computeLocalPivotFromFactors(const PhysicsBodyPtr& body, const Vec3& factors);
 
-	template<typename TJoint, typename... TArgs>
-	void newJoint(const Vec3& relPosFactor, F32 brakingImpulse, TArgs&&... args);
-
 	Error update(SceneComponentUpdateInfo& info, Bool& updated) override;
 
 	void onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added) override;

+ 1 - 1
AnKi/Scene/Components/SceneComponentClasses.def.h

@@ -15,7 +15,7 @@ ANKI_SCENE_COMPONENT_SEPARATOR
 ANKI_DEFINE_SCENE_COMPONENT(PlayerController, 10.0f)
 ANKI_SCENE_COMPONENT_SEPARATOR
 
-ANKI_DEFINE_SCENE_COMPONENT(Joint, 10.0f)
+ANKI_DEFINE_SCENE_COMPONENT(Joint, 15.0f)
 ANKI_SCENE_COMPONENT_SEPARATOR
 
 ANKI_DEFINE_SCENE_COMPONENT(Move, 30.0f)

+ 1 - 1
AnKi/Scene/SceneNode.h

@@ -45,7 +45,7 @@ public:
 		return (!m_name.isEmpty()) ? m_name.toCString() : CString();
 	}
 
-	U64 getUuid() const
+	U32 getUuid() const
 	{
 		return m_uuid;
 	}

+ 1 - 2
Samples/PhysicsPlayground/Main.cpp

@@ -350,8 +350,7 @@ Error MyApp::userMainLoop(Bool& quit, [[maybe_unused]] Second elapsedTime)
 		bodyc->teleportTo(camTrf);
 		bodyc->setMass(1.0f);
 
-		PhysicsBodyPtr pbody = bodyc->getPhysicsBody();
-		pbody->applyForce(camTrf.getRotation().getZAxis().xyz() * -1500.0f, Vec3(0.0f, 0.0f, 0.0f));
+		bodyc->applyForce(camTrf.getRotation().getZAxis().xyz() * -1500.0f, Vec3(0.0f, 0.0f, 0.0f));
 
 		// Create the destruction event
 		ANKI_CHECK(createDestructionEvent(monkey));