Browse Source

Some work on the joints and the triggers

Panagiotis Christopoulos Charitos 7 years ago
parent
commit
f662c58789

+ 52 - 8
samples/physics_playground/Main.cpp

@@ -22,14 +22,17 @@ Error MyApp::sampleExtraInit()
 	ANKI_CHECK(getScriptManager().evalString(script->getSource()));
 
 	// Create the player
-	SceneNode& cam = getSceneGraph().getActiveCameraNode();
-	cam.getComponent<MoveComponent>().setLocalTransform(
-		Transform(Vec4(0.0, 0.0, 5.0, 0.0), Mat3x4::getIdentity(), 1.0));
+	if(0)
+	{
+		SceneNode& cam = getSceneGraph().getActiveCameraNode();
+		cam.getComponent<MoveComponent>().setLocalTransform(
+			Transform(Vec4(0.0, 0.0, 5.0, 0.0), Mat3x4::getIdentity(), 1.0));
 
-	PlayerNode* player;
-	ANKI_CHECK(getSceneGraph().newSceneNode("player", player, Vec4(0.0f, 2.5f, 0.0f, 0.0f)));
+		PlayerNode* player;
+		ANKI_CHECK(getSceneGraph().newSceneNode("player", player, Vec4(0.0f, 2.5f, 0.0f, 0.0f)));
 
-	player->addChild(&cam);
+		player->addChild(&cam);
+	}
 
 	// Create a body component with joint
 	{
@@ -40,19 +43,60 @@ Error MyApp::sampleExtraInit()
 		BodyNode* body;
 		ANKI_CHECK(getSceneGraph().newSceneNode<BodyNode>("bmonkey_p2p", body, "assets/Suzanne.ankicl"));
 		body->getComponent<BodyComponent>().setTransform(
-			Transform(Vec4(-2.0f, 4.0f, -3.0f, 0.0f), Mat3x4::getIdentity(), 1.0f));
+			Transform(Vec4(-0.0f, 4.0f, -3.0f, 0.0f), Mat3x4::getIdentity(), 1.0f));
 
 		body->addChild(monkey);
 
 		body->getComponent<JointComponent>().newHingeJoint(Vec3(0.2f, 1.0f, 0.0f), Vec3(1, 0, 0));
 	}
 
+	// Create a chain
+	{
+		const U LINKS = 5;
+
+		BodyNode* prevBody = nullptr;
+		for(U i = 0; i < LINKS; ++i)
+		{
+			ModelNode* monkey;
+			ANKI_CHECK(getSceneGraph().newSceneNode<ModelNode>(
+				StringAuto(getAllocator()).sprintf("monkey_chain%u", i).toCString(),
+				monkey,
+				"assets/SuzanneMaterial-material.ankimdl"));
+
+			Transform trf(Vec4(-4.3f, 12.0f, -3.0f, 0.0f), Mat3x4::getIdentity(), 1.0f);
+			trf.getOrigin().y() -= i * 1.25f;
+			// trf.getOrigin().x() -= i * 0.25f;
+
+			// monkey->getComponent<MoveComponent>().setLocalTransform(trf);
+
+			BodyNode* body;
+			ANKI_CHECK(getSceneGraph().newSceneNode<BodyNode>(
+				StringAuto(getAllocator()).sprintf("bmonkey_chain%u", i).toCString(), body, "assets/Suzanne.ankicl"));
+			body->getComponent<BodyComponent>().setTransform(trf);
+
+			// Create joint
+			JointComponent& jointc = body->getComponent<JointComponent>();
+			if(prevBody == nullptr)
+			{
+				jointc.newPoint2PointJoint(Vec3(0, 1, 0));
+			}
+			else
+			{
+				prevBody->addChild(body);
+				jointc.newPoint2PointJoint2(Vec3(0, 1.0, 0), Vec3(0, -1.0, 0));
+			}
+
+			body->addChild(monkey);
+			prevBody = body;
+		}
+	}
+
 	return Error::NONE;
 }
 
 Error MyApp::userMainLoop(Bool& quit)
 {
-	// ANKI_CHECK(SampleApp::userMainLoop(quit));
+	ANKI_CHECK(SampleApp::userMainLoop(quit));
 
 	if(getInput().getKey(KeyCode::ESCAPE))
 	{

+ 1 - 0
src/anki/Physics.h

@@ -10,5 +10,6 @@
 #include <anki/physics/PhysicsCollisionShape.h>
 #include <anki/physics/PhysicsJoint.h>
 #include <anki/physics/PhysicsPlayerController.h>
+#include <anki/physics/PhysicsTrigger.h>
 
 /// @defgroup physics Physics subsystem

+ 24 - 0
src/anki/math/Transform.h

@@ -47,6 +47,30 @@ public:
 	{
 		checkW();
 	}
+
+	explicit TTransform(const TVec4<T>& origin)
+		: m_origin(origin)
+		, m_rotation(Mat3x4::getIdentity())
+		, m_scale(1.0f)
+	{
+		checkW();
+	}
+
+	explicit TTransform(const TMat3x4<T>& rotation)
+		: m_origin(Vec4(0.0f))
+		, m_rotation(rotation)
+		, m_scale(1.0f)
+	{
+		checkW();
+	}
+
+	TTransform(const T scale)
+		: m_origin(Vec4(0.0f))
+		, m_rotation(Mat3x4::getIdentity())
+		, m_scale(scale)
+	{
+		checkW();
+	}
 	/// @}
 
 	/// @name Accessors

+ 2 - 1
src/anki/physics/Common.cpp

@@ -18,7 +18,8 @@ void PhysicsPtrDeleter::operator()(PhysicsObject* ptr)
 	}
 
 	auto alloc = ptr->getAllocator();
-	alloc.deleteInstance(ptr);
+	ptr->~PhysicsObject();
+	alloc.getMemoryPool().free(ptr);
 }
 
 } // end namespace anki

+ 2 - 0
src/anki/physics/Common.h

@@ -35,6 +35,7 @@ class PhysicsCollisionShape;
 class PhysicsBody;
 class PhysicsPlayerController;
 class PhysicsJoint;
+class PhysicsTrigger;
 
 /// @addtogroup physics
 /// @{
@@ -54,6 +55,7 @@ using PhysicsCollisionShapePtr = PhysicsPtr<PhysicsCollisionShape>;
 using PhysicsBodyPtr = PhysicsPtr<PhysicsBody>;
 using PhysicsPlayerControllerPtr = PhysicsPtr<PhysicsPlayerController>;
 using PhysicsJointPtr = PhysicsPtr<PhysicsJoint>;
+using PhysicsTriggerPtr = PhysicsPtr<PhysicsTrigger>;
 
 /// Material types.
 enum class PhysicsMaterialBit : U16

+ 5 - 2
src/anki/physics/PhysicsBody.cpp

@@ -23,7 +23,6 @@ public:
 	void setWorldTransform(const btTransform& worldTrans) override
 	{
 		m_body->m_trf = toAnki(worldTrans);
-		m_body->m_updated = true;
 	}
 };
 
@@ -31,13 +30,14 @@ PhysicsBody::PhysicsBody(PhysicsWorld* world, const PhysicsBodyInitInfo& init)
 	: PhysicsObject(PhysicsObjectType::BODY, world)
 {
 	const Bool dynamic = init.m_mass > 0.0f;
+	m_shape = init.m_shape;
 
 	// Create motion state
 	m_motionState = getAllocator().newInstance<MotionState>();
 	m_motionState->m_body = this;
 
 	// Compute inertia
-	btCollisionShape* shape = init.m_shape->getBtShape(dynamic);
+	btCollisionShape* shape = m_shape->getBtShape(dynamic);
 	btVector3 localInertia(0, 0, 0);
 	if(dynamic)
 	{
@@ -49,6 +49,9 @@ PhysicsBody::PhysicsBody(PhysicsWorld* world, const PhysicsBodyInitInfo& init)
 	cInfo.m_friction = init.m_friction;
 	m_body = getAllocator().newInstance<btRigidBody>(cInfo);
 
+	// User pointer
+	m_body->setUserPointer(static_cast<PhysicsObject*>(this));
+
 	// Add to world
 	auto lock = getWorld().lockWorld();
 	getWorld().getBtWorld()->addRigidBody(m_body);

+ 8 - 8
src/anki/physics/PhysicsBody.h

@@ -28,15 +28,11 @@ public:
 /// Rigid body.
 class PhysicsBody : public PhysicsObject
 {
-public:
-	PhysicsBody(PhysicsWorld* world, const PhysicsBodyInitInfo& init);
+	ANKI_PHYSICS_OBJECT
 
-	~PhysicsBody();
-
-	const Transform& getTransform(Bool& updated)
+public:
+	const Transform& getTransform() const
 	{
-		updated = m_updated;
-		m_updated = false;
 		return m_trf;
 	}
 
@@ -86,6 +82,7 @@ anki_internal:
 private:
 	class MotionState;
 
+	PhysicsCollisionShapePtr m_shape;
 	MotionState* m_motionState = nullptr;
 	btRigidBody* m_body = nullptr;
 
@@ -93,7 +90,10 @@ private:
 	F32 m_friction = 0.03f;
 	F32 m_elasticity = 0.1f;
 	PhysicsMaterialBit m_materialBits = PhysicsMaterialBit::ALL;
-	Bool8 m_updated = true;
+
+	PhysicsBody(PhysicsWorld* world, const PhysicsBodyInitInfo& init);
+
+	~PhysicsBody();
 };
 /// @}
 

+ 4 - 0
src/anki/physics/PhysicsCollisionShape.cpp

@@ -20,6 +20,7 @@ PhysicsSphere::PhysicsSphere(PhysicsWorld* world, F32 radius)
 {
 	m_shape = getAllocator().newInstance<btSphereShape>(radius);
 	m_shape->setMargin(getWorld().getCollisionMargin());
+	m_shape->setUserPointer(static_cast<PhysicsObject*>(this));
 }
 
 PhysicsBox::PhysicsBox(PhysicsWorld* world, const Vec3& extend)
@@ -27,6 +28,7 @@ PhysicsBox::PhysicsBox(PhysicsWorld* world, const Vec3& extend)
 {
 	m_shape = getAllocator().newInstance<btBoxShape>(toBt(extend));
 	m_shape->setMargin(getWorld().getCollisionMargin());
+	m_shape->setUserPointer(static_cast<PhysicsObject*>(this));
 }
 
 PhysicsTriangleSoup::PhysicsTriangleSoup(
@@ -52,6 +54,8 @@ PhysicsTriangleSoup::PhysicsTriangleSoup(
 	btBvhTriangleMeshShape* triShape = getAllocator().newInstance<btBvhTriangleMeshShape>(m_mesh, true);
 	m_staticShape = triShape;
 	m_staticShape->setMargin(getWorld().getCollisionMargin());
+
+	m_shape->setUserPointer(static_cast<PhysicsObject*>(this));
 }
 
 PhysicsTriangleSoup::~PhysicsTriangleSoup()

+ 21 - 15
src/anki/physics/PhysicsCollisionShape.h

@@ -17,14 +17,6 @@ namespace anki
 /// The base of all collision shapes.
 class PhysicsCollisionShape : public PhysicsObject
 {
-public:
-	PhysicsCollisionShape(PhysicsWorld* world)
-		: PhysicsObject(PhysicsObjectType::COLLISION_SHAPE, world)
-	{
-	}
-
-	~PhysicsCollisionShape();
-
 anki_internal:
 	virtual btCollisionShape* getBtShape(Bool forDynamicBodies = false) const
 	{
@@ -34,41 +26,55 @@ anki_internal:
 
 protected:
 	btCollisionShape* m_shape = nullptr;
+
+	PhysicsCollisionShape(PhysicsWorld* world)
+		: PhysicsObject(PhysicsObjectType::COLLISION_SHAPE, world)
+	{
+	}
+
+	~PhysicsCollisionShape();
 };
 
 /// Sphere collision shape.
 class PhysicsSphere final : public PhysicsCollisionShape
 {
-public:
+	ANKI_PHYSICS_OBJECT
+
+private:
 	PhysicsSphere(PhysicsWorld* world, F32 radius);
 };
 
 /// Box collision shape.
 class PhysicsBox final : public PhysicsCollisionShape
 {
-public:
+	ANKI_PHYSICS_OBJECT
+
+private:
 	PhysicsBox(PhysicsWorld* world, const Vec3& extend);
 };
 
 /// Convex hull collision shape.
 class PhysicsConvexHull final : public PhysicsCollisionShape
 {
-public:
+	ANKI_PHYSICS_OBJECT
+
+private:
 	PhysicsConvexHull(PhysicsWorld* world, const Vec3* positions, U32 positionsCount, U32 positionsStride);
 };
 
 /// Static triangle mesh shape.
 class PhysicsTriangleSoup final : public PhysicsCollisionShape
 {
-public:
-	PhysicsTriangleSoup(PhysicsWorld* world, ConstWeakArray<Vec3> positions, ConstWeakArray<U32> indices);
-
-	~PhysicsTriangleSoup();
+	ANKI_PHYSICS_OBJECT
 
 private:
 	btTriangleMesh* m_mesh = nullptr;
 	btCollisionShape* m_staticShape = nullptr;
 
+	PhysicsTriangleSoup(PhysicsWorld* world, ConstWeakArray<Vec3> positions, ConstWeakArray<U32> indices);
+
+	~PhysicsTriangleSoup();
+
 	btCollisionShape* getBtShape(Bool forDynamicBodies = false) const override;
 };
 /// @}

+ 16 - 0
src/anki/physics/PhysicsJoint.cpp

@@ -29,6 +29,8 @@ PhysicsJoint::~PhysicsJoint()
 void PhysicsJoint::addToWorld()
 {
 	ANKI_ASSERT(m_joint);
+	m_joint->setUserConstraintPtr(static_cast<PhysicsObject*>(this));
+
 	auto lock = getWorld().lockWorld();
 	getWorld().getBtWorld()->addConstraint(m_joint);
 }
@@ -43,6 +45,20 @@ PhysicsPoint2PointJoint::PhysicsPoint2PointJoint(PhysicsWorld* world, PhysicsBod
 	addToWorld();
 }
 
+PhysicsPoint2PointJoint::PhysicsPoint2PointJoint(
+	PhysicsWorld* world, PhysicsBodyPtr bodyA, const Vec3& relPosA, PhysicsBodyPtr bodyB, const Vec3& relPosB)
+	: PhysicsJoint(world)
+{
+	ANKI_ASSERT(bodyA != bodyB);
+	m_bodyA = bodyA;
+	m_bodyB = bodyB;
+
+	m_joint = getAllocator().newInstance<btPoint2PointConstraint>(
+		*m_bodyA->getBtBody(), *m_bodyB->getBtBody(), toBt(relPosA), toBt(relPosB));
+
+	addToWorld();
+}
+
 PhysicsHingeJoint::PhysicsHingeJoint(PhysicsWorld* world, PhysicsBodyPtr bodyA, const Vec3& relPos, const Vec3& axis)
 	: PhysicsJoint(world)
 {

+ 20 - 7
src/anki/physics/PhysicsJoint.h

@@ -17,10 +17,6 @@ namespace anki
 class PhysicsJoint : public PhysicsObject
 {
 public:
-	PhysicsJoint(PhysicsWorld* world);
-
-	~PhysicsJoint();
-
 	void setBreakingImpulseThreshold(F32 impulse)
 	{
 		m_joint->setBreakingImpulseThreshold(impulse);
@@ -31,23 +27,40 @@ protected:
 	PhysicsBodyPtr m_bodyA;
 	PhysicsBodyPtr m_bodyB;
 
+	PhysicsJoint(PhysicsWorld* world);
+
+	~PhysicsJoint();
+
 	void addToWorld();
 };
 
 /// Point 2 point joint.
 class PhysicsPoint2PointJoint : public PhysicsJoint
 {
-public:
+	ANKI_PHYSICS_OBJECT
+
+private:
 	PhysicsPoint2PointJoint(PhysicsWorld* world, PhysicsBodyPtr bodyA, const Vec3& relPos);
 
-	PhysicsPoint2PointJoint(PhysicsWorld* world, PhysicsBodyPtr bodyA, PhysicsBodyPtr bodyB);
+	PhysicsPoint2PointJoint(
+		PhysicsWorld* world, PhysicsBodyPtr bodyA, const Vec3& relPosA, PhysicsBodyPtr bodyB, const Vec3& relPosB);
 };
 
 /// Hinge joint.
 class PhysicsHingeJoint : public PhysicsJoint
 {
-public:
+	ANKI_PHYSICS_OBJECT
+
+private:
 	PhysicsHingeJoint(PhysicsWorld* world, PhysicsBodyPtr bodyA, const Vec3& relPos, const Vec3& axis);
+
+	PhysicsHingeJoint(PhysicsWorld* world,
+		PhysicsBodyPtr bodyA,
+		const Vec3& relPosA,
+		const Vec3& axisA,
+		PhysicsBodyPtr bodyB,
+		const Vec3& relPosB,
+		const Vec3& axisB);
 };
 /// @}
 

+ 6 - 0
src/anki/physics/PhysicsObject.h

@@ -20,6 +20,7 @@ enum class PhysicsObjectType : U8
 	BODY,
 	JOINT,
 	PLAYER_CONTROLLER,
+	TRIGGER,
 	COUNT
 };
 
@@ -66,7 +67,12 @@ protected:
 private:
 	Atomic<I32> m_refcount = {0};
 	PhysicsObjectType m_type;
+	void* m_userData = nullptr;
 };
+
+#define ANKI_PHYSICS_OBJECT \
+	friend class PhysicsWorld; \
+	friend class PhysicsPtrDeleter;
 /// @}
 
 } // end namespace anki

+ 6 - 4
src/anki/physics/PhysicsPlayerController.h

@@ -28,11 +28,9 @@ public:
 /// A player controller that walks the world.
 class PhysicsPlayerController final : public PhysicsObject
 {
-public:
-	PhysicsPlayerController(PhysicsWorld* world, const PhysicsPlayerControllerInitInfo& init);
-
-	~PhysicsPlayerController();
+	ANKI_PHYSICS_OBJECT
 
+public:
 	// Update the state machine
 	void setVelocity(F32 forwardSpeed, F32 strafeSpeed, F32 jumpSpeed, const Vec4& forwardDir)
 	{
@@ -54,6 +52,10 @@ private:
 	btKinematicCharacterController* m_controller = nullptr;
 
 	Transform m_prevTrf = Transform::getIdentity();
+
+	PhysicsPlayerController(PhysicsWorld* world, const PhysicsPlayerControllerInitInfo& init);
+
+	~PhysicsPlayerController();
 };
 /// @}
 

+ 52 - 0
src/anki/physics/PhysicsTrigger.cpp

@@ -0,0 +1,52 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/physics/PhysicsTrigger.h>
+#include <anki/physics/PhysicsCollisionShape.h>
+#include <anki/physics/PhysicsWorld.h>
+
+namespace anki
+{
+
+PhysicsTrigger::PhysicsTrigger(PhysicsWorld* world, PhysicsCollisionShapePtr shape)
+	: PhysicsObject(PhysicsObjectType::TRIGGER, world)
+{
+	m_shape = shape;
+
+	m_ghostShape = getAllocator().newInstance<btPairCachingGhostObject>();
+	m_ghostShape->setWorldTransform(btTransform::getIdentity());
+	m_ghostShape->setCollisionShape(shape->getBtShape(true));
+
+	m_ghostShape->setUserPointer(static_cast<PhysicsObject*>(this));
+
+	auto lock = getWorld().lockWorld();
+	getWorld().getBtWorld()->addCollisionObject(m_ghostShape);
+}
+
+PhysicsTrigger::~PhysicsTrigger()
+{
+	{
+		auto lock = getWorld().lockWorld();
+		getWorld().getBtWorld()->removeCollisionObject(m_ghostShape);
+	}
+
+	getAllocator().deleteInstance(m_ghostShape);
+}
+
+void PhysicsTrigger::processContacts()
+{
+	// TODO
+#if 0
+	for(U i = 0; i < m_ghostShape->getOverlappingPairCache()->getNumOverlappingPairs(); ++i)
+	{
+		btBroadphasePair* collisionPair = &m_ghostShape->getOverlappingPairCache()->getOverlappingPairArray()[i];
+
+		btCollisionObject* obj0 = static_cast<btCollisionObject*>(collisionPair->m_pProxy0->m_clientObject);
+        btCollisionObject* obj1 = static_cast<btCollisionObject*>(collisionPair->m_pProxy1->m_clientObject);
+	}
+#endif
+}
+
+} // end namespace anki

+ 39 - 0
src/anki/physics/PhysicsTrigger.h

@@ -0,0 +1,39 @@
+// Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/physics/PhysicsObject.h>
+
+namespace anki
+{
+
+/// @addtogroup physics
+/// @{
+
+/// A trigger that uses a PhysicsShape and its purpose is to collect collision events.
+class PhysicsTrigger : public PhysicsObject
+{
+	ANKI_PHYSICS_OBJECT
+
+public:
+	void setTransform(const Transform& trf)
+	{
+		m_ghostShape->setWorldTransform(toBt(trf));
+	}
+
+private:
+	PhysicsCollisionShapePtr m_shape;
+	btPairCachingGhostObject* m_ghostShape = nullptr;
+
+	PhysicsTrigger(PhysicsWorld* world, PhysicsCollisionShapePtr shape);
+
+	~PhysicsTrigger();
+
+	void processContacts();
+};
+/// @}
+
+} // end namespace anki

+ 3 - 2
src/anki/physics/PhysicsWorld.h

@@ -26,9 +26,10 @@ public:
 	template<typename T, typename... TArgs>
 	PhysicsPtr<T> newInstance(TArgs&&... args)
 	{
+		void* mem = m_alloc.getMemoryPool().allocate(sizeof(T), alignof(T));
+		::new(mem) T(this, std::forward<TArgs>(args)...);
 		PhysicsPtr<T> out;
-		T* ptr = m_alloc.template newInstance<T>(this, std::forward<TArgs>(args)...);
-		out.reset(ptr);
+		out.reset(static_cast<T*>(mem));
 		return out;
 	}
 

+ 1 - 1
src/anki/scene/BodyNode.cpp

@@ -69,7 +69,7 @@ Error BodyNode::init(const CString& resourceFname)
 	newComponent<BodyFeedbackComponent>(this);
 
 	// Move component
-	newComponent<MoveComponent>(this);
+	newComponent<MoveComponent>(this, MoveComponentFlag::IGNORE_PARENT_TRANSFORM);
 
 	return Error::NONE;
 }

+ 4 - 2
src/anki/scene/components/BodyComponent.h

@@ -45,13 +45,15 @@ public:
 
 	ANKI_USE_RESULT Error update(Second, Second, Bool& updated) override
 	{
-		m_trf = m_body->getTransform(updated);
+		Transform newTrf = m_body->getTransform();
+		updated = newTrf != m_trf;
+		m_trf = newTrf;
 		return Error::NONE;
 	}
 
 private:
 	PhysicsBodyPtr m_body;
-	Transform m_trf;
+	Transform m_trf = Transform::getIdentity();
 };
 /// @}
 

+ 78 - 17
src/anki/scene/components/JointComponent.cpp

@@ -11,9 +11,27 @@
 namespace anki
 {
 
+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.
+};
+
 JointComponent::~JointComponent()
 {
-	m_jointList.destroy(getAllocator());
+	removeAllJoints();
+}
+
+void JointComponent::removeAllJoints()
+{
+	while(!m_jointList.isEmpty())
+	{
+		JointNode* node = &m_jointList.getFront();
+		m_jointList.popFront();
+
+		getAllocator().deleteInstance(node);
+	}
 }
 
 Vec3 JointComponent::computeLocalPivotFromFactors(const PhysicsBodyPtr& body, const Vec3& factors)
@@ -37,7 +55,8 @@ Vec3 JointComponent::computeLocalPivotFromFactors(const PhysicsBodyPtr& body, co
 	return out;
 }
 
-void JointComponent::newPoint2PointJoint(const Vec3& relPosFactor, F32 brakingImpulse)
+template<typename TJoint, typename... TArgs>
+void JointComponent::newJoint(const Vec3& relPosFactor, F32 breakingImpulse, TArgs&&... args)
 {
 	BodyComponent* bodyc = m_node->tryGetComponent<BodyComponent>();
 
@@ -45,41 +64,83 @@ void JointComponent::newPoint2PointJoint(const Vec3& relPosFactor, F32 brakingIm
 	{
 		Vec3 relPos = computeLocalPivotFromFactors(bodyc->getPhysicsBody(), relPosFactor);
 
-		PhysicsJointPtr joint =
-			getSceneGraph().getPhysicsWorld().newInstance<PhysicsPoint2PointJoint>(bodyc->getPhysicsBody(), relPos);
-		joint->setBreakingImpulseThreshold(brakingImpulse);
+		PhysicsJointPtr joint = getSceneGraph().getPhysicsWorld().newInstance<TJoint>(
+			bodyc->getPhysicsBody(), relPos, std::forward<TArgs>(args)...);
+		joint->setBreakingImpulseThreshold(breakingImpulse);
 
-		m_jointList.pushBack(getAllocator(), joint);
+		JointNode* newNode = getAllocator().newInstance<JointNode>();
+		newNode->m_joint = joint;
+		m_jointList.pushBack(newNode);
 	}
 	else
 	{
-		ANKI_SCENE_LOGW("Can't create new joint. The node is missing a body component");
+		ANKI_SCENE_LOGW("Can't create new joint. The node is missing a BodyComponent");
 	}
 }
 
-void JointComponent::newHingeJoint(const Vec3& relPosFactor, const Vec3& axis, F32 brakingImpulse)
+void JointComponent::newPoint2PointJoint(const Vec3& relPosFactor, F32 breakingImpulse)
 {
-	BodyComponent* bodyc = m_node->tryGetComponent<BodyComponent>();
+	newJoint<PhysicsPoint2PointJoint>(relPosFactor, breakingImpulse);
+}
 
-	if(bodyc)
+void JointComponent::newPoint2PointJoint2(const Vec3& relPosFactorA, const Vec3& relPosFactorB, F32 breakingImpulse)
+{
+	BodyComponent* bodycA = m_node->tryGetComponent<BodyComponent>();
+	BodyComponent* bodycB = nullptr;
+	if(m_node->getParent())
 	{
-		Vec3 relPos = computeLocalPivotFromFactors(bodyc->getPhysicsBody(), relPosFactor);
+		bodycB = m_node->getParent()->tryGetComponent<BodyComponent>();
+	}
 
-		PhysicsJointPtr joint =
-			getSceneGraph().getPhysicsWorld().newInstance<PhysicsHingeJoint>(bodyc->getPhysicsBody(), relPos, axis);
-		joint->setBreakingImpulseThreshold(brakingImpulse);
+	if(bodycA && bodycB)
+	{
+		Vec3 relPosA = computeLocalPivotFromFactors(bodycA->getPhysicsBody(), relPosFactorA);
+		Vec3 relPosB = computeLocalPivotFromFactors(bodycB->getPhysicsBody(), relPosFactorB);
+
+		PhysicsJointPtr joint = getSceneGraph().getPhysicsWorld().newInstance<PhysicsPoint2PointJoint>(
+			bodycA->getPhysicsBody(), relPosA, bodycB->getPhysicsBody(), relPosB);
+		joint->setBreakingImpulseThreshold(breakingImpulse);
 
-		m_jointList.pushBack(getAllocator(), joint);
+		JointNode* newNode = getAllocator().newInstance<JointNode>();
+		newNode->m_joint = joint;
+		newNode->m_parentNode = m_node->getParent();
+		m_jointList.pushBack(newNode);
 	}
 	else
 	{
-		ANKI_SCENE_LOGW("Can't create new joint. The node is missing a body component");
+		ANKI_SCENE_LOGW("Can't create new joint. The node or its parent are missing a BodyComponent");
 	}
 }
 
+void JointComponent::newHingeJoint(const Vec3& relPosFactor, const Vec3& axis, F32 breakingImpulse)
+{
+	newJoint<PhysicsHingeJoint>(relPosFactor, breakingImpulse, axis);
+}
+
 Error JointComponent::update(Second prevTime, Second crntTime, Bool& updated)
 {
-	// TODO
+	// Iterate the joints and check if the connected scene node is not the parent of this node anymore.
+	while(true)
+	{
+		Bool erasedOne = false;
+		for(auto node : m_jointList)
+		{
+			if(node.m_parentNode != m_node->getParent())
+			{
+				m_jointList.erase(&node);
+				getAllocator().deleteInstance(&node);
+				erasedOne = true;
+				updated = true;
+				break;
+			}
+		}
+
+		if(!erasedOne)
+		{
+			break;
+		}
+	}
+
 	return Error::NONE;
 }
 

+ 13 - 1
src/anki/scene/components/JointComponent.h

@@ -27,18 +27,30 @@ public:
 
 	~JointComponent();
 
+	/// Create a point 2 point joint on the BodyComponent of the SceneNode.
 	void newPoint2PointJoint(const Vec3& relPosFactor, F32 brakingImpulse = MAX_F32);
 
+	/// Create a point 2 point joint on the BodyComponents of the SceneNode and its child node.
+	void newPoint2PointJoint2(const Vec3& relPosFactorA, const Vec3& relPosFactorB, F32 brakingImpulse = MAX_F32);
+
+	/// Create a hinge joint on the BodyComponent of the SceneNode.
 	void newHingeJoint(const Vec3& relPosFactor, const Vec3& axis, F32 brakingImpulse = MAX_F32);
 
 	ANKI_USE_RESULT Error update(Second prevTime, Second crntTime, Bool& updated) override;
 
 private:
-	List<PhysicsJointPtr> m_jointList;
+	class JointNode;
+
+	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);
+
+	void removeAllJoints();
+
+	template<typename TJoint, typename... TArgs>
+	void newJoint(const Vec3& relPosFactor, F32 brakingImpulse, TArgs&&... args);
 };
 /// @}