Browse Source

Add broadphase collision filtering

Panagiotis Christopoulos Charitos 7 years ago
parent
commit
2741f68234

+ 4 - 2
samples/physics_playground/Main.cpp

@@ -22,7 +22,7 @@ Error MyApp::sampleExtraInit()
 	ANKI_CHECK(getScriptManager().evalString(script->getSource()));
 	ANKI_CHECK(getScriptManager().evalString(script->getSource()));
 
 
 	// Create the player
 	// Create the player
-	if(0)
+	if(1)
 	{
 	{
 		SceneNode& cam = getSceneGraph().getActiveCameraNode();
 		SceneNode& cam = getSceneGraph().getActiveCameraNode();
 		cam.getComponent<MoveComponent>().setLocalTransform(
 		cam.getComponent<MoveComponent>().setLocalTransform(
@@ -30,6 +30,8 @@ Error MyApp::sampleExtraInit()
 
 
 		PlayerNode* player;
 		PlayerNode* player;
 		ANKI_CHECK(getSceneGraph().newSceneNode("player", player, Vec4(0.0f, 2.5f, 0.0f, 0.0f)));
 		ANKI_CHECK(getSceneGraph().newSceneNode("player", player, Vec4(0.0f, 2.5f, 0.0f, 0.0f)));
+		PlayerControllerComponent& pcomp = player->getComponent<PlayerControllerComponent>();
+		pcomp.getPhysicsPlayerController()->setMaterialMask(PhysicsMaterialBit::STATIC_GEOMETRY);
 
 
 		player->addChild(&cam);
 		player->addChild(&cam);
 	}
 	}
@@ -96,7 +98,7 @@ Error MyApp::sampleExtraInit()
 
 
 Error MyApp::userMainLoop(Bool& quit)
 Error MyApp::userMainLoop(Bool& quit)
 {
 {
-	ANKI_CHECK(SampleApp::userMainLoop(quit));
+	// ANKI_CHECK(SampleApp::userMainLoop(quit));
 
 
 	if(getInput().getKey(KeyCode::ESCAPE))
 	if(getInput().getKey(KeyCode::ESCAPE))
 	{
 	{

+ 1 - 1
src/anki/core/Config.cpp

@@ -70,7 +70,7 @@ Config::Config()
 	newOption("core.storagePerFrameMemorySize", 16_MB);
 	newOption("core.storagePerFrameMemorySize", 16_MB);
 	newOption("core.vertexPerFrameMemorySize", 10_MB);
 	newOption("core.vertexPerFrameMemorySize", 10_MB);
 	newOption("core.textureBufferPerFrameMemorySize", 1_MB);
 	newOption("core.textureBufferPerFrameMemorySize", 1_MB);
-	newOption("core.mainThreadCount", getCpuCoresCount() / 2 - 1);
+	newOption("core.mainThreadCount", max(2u, getCpuCoresCount() / 2u - 1u));
 	newOption("core.displayStats", false);
 	newOption("core.displayStats", false);
 	newOption("core.clearCaches", false);
 	newOption("core.clearCaches", false);
 }
 }

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

@@ -30,6 +30,7 @@ namespace anki
 
 
 // Forward
 // Forward
 class PhysicsObject;
 class PhysicsObject;
+class PhysicsFilteredObject;
 class PhysicsWorld;
 class PhysicsWorld;
 class PhysicsCollisionShape;
 class PhysicsCollisionShape;
 class PhysicsBody;
 class PhysicsBody;
@@ -59,14 +60,15 @@ using PhysicsJointPtr = PhysicsPtr<PhysicsJoint>;
 using PhysicsTriggerPtr = PhysicsPtr<PhysicsTrigger>;
 using PhysicsTriggerPtr = PhysicsPtr<PhysicsTrigger>;
 
 
 /// Material types.
 /// Material types.
-enum class PhysicsMaterialBit : U16
+enum class PhysicsMaterialBit : U64
 {
 {
 	NONE = 0,
 	NONE = 0,
 	STATIC_GEOMETRY = 1 << 0,
 	STATIC_GEOMETRY = 1 << 0,
 	DYNAMIC_GEOMETRY = 1 << 1,
 	DYNAMIC_GEOMETRY = 1 << 1,
-	RAGDOLL = 1 << 2,
-	PARTICLES = 1 << 3,
-	ALL = MAX_U16
+	TRIGGER = 1 << 2,
+	PLAYER = 1 << 3,
+
+	ALL = MAX_U64
 };
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(PhysicsMaterialBit, inline)
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(PhysicsMaterialBit, inline)
 
 

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

@@ -27,7 +27,7 @@ public:
 };
 };
 
 
 PhysicsBody::PhysicsBody(PhysicsWorld* world, const PhysicsBodyInitInfo& init)
 PhysicsBody::PhysicsBody(PhysicsWorld* world, const PhysicsBodyInitInfo& init)
-	: PhysicsObject(CLASS_TYPE, world)
+	: PhysicsFilteredObject(CLASS_TYPE, world)
 {
 {
 	const Bool dynamic = init.m_mass > 0.0f;
 	const Bool dynamic = init.m_mass > 0.0f;
 	m_shape = init.m_shape;
 	m_shape = init.m_shape;
@@ -52,6 +52,10 @@ PhysicsBody::PhysicsBody(PhysicsWorld* world, const PhysicsBodyInitInfo& init)
 	// User pointer
 	// User pointer
 	m_body->setUserPointer(static_cast<PhysicsObject*>(this));
 	m_body->setUserPointer(static_cast<PhysicsObject*>(this));
 
 
+	// Other
+	setMaterialGroup(PhysicsMaterialBit::DYNAMIC_GEOMETRY | PhysicsMaterialBit::STATIC_GEOMETRY);
+	setMaterialMask(PhysicsMaterialBit::ALL);
+
 	// Add to world
 	// Add to world
 	auto lock = getWorld().lockBtWorld();
 	auto lock = getWorld().lockBtWorld();
 	getWorld().getBtWorld()->addRigidBody(m_body);
 	getWorld().getBtWorld()->addRigidBody(m_body);

+ 1 - 3
src/anki/physics/PhysicsBody.h

@@ -20,13 +20,11 @@ public:
 	PhysicsCollisionShapePtr m_shape;
 	PhysicsCollisionShapePtr m_shape;
 	F32 m_mass = 0.0f;
 	F32 m_mass = 0.0f;
 	Transform m_startTrf = Transform::getIdentity();
 	Transform m_startTrf = Transform::getIdentity();
-	Bool m_kinematic = false;
-	Bool m_gravity = true;
 	F32 m_friction = 0.5f;
 	F32 m_friction = 0.5f;
 };
 };
 
 
 /// Rigid body.
 /// Rigid body.
-class PhysicsBody : public PhysicsObject
+class PhysicsBody : public PhysicsFilteredObject
 {
 {
 	ANKI_PHYSICS_OBJECT
 	ANKI_PHYSICS_OBJECT
 
 

+ 69 - 2
src/anki/physics/PhysicsObject.h

@@ -18,14 +18,16 @@ namespace anki
 enum class PhysicsObjectType : U8
 enum class PhysicsObjectType : U8
 {
 {
 	COLLISION_SHAPE,
 	COLLISION_SHAPE,
-	BODY,
 	JOINT,
 	JOINT,
+	BODY,
 	PLAYER_CONTROLLER,
 	PLAYER_CONTROLLER,
 	TRIGGER,
 	TRIGGER,
 
 
 	COUNT,
 	COUNT,
 	FIRST = 0,
 	FIRST = 0,
-	LAST = COUNT - 1
+	LAST = COUNT - 1,
+	FIRST_FILTERED = BODY,
+	LAST_FILTERED = TRIGGER,
 };
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(PhysicsObjectType, inline)
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(PhysicsObjectType, inline)
 
 
@@ -77,6 +79,71 @@ private:
 #define ANKI_PHYSICS_OBJECT \
 #define ANKI_PHYSICS_OBJECT \
 	friend class PhysicsWorld; \
 	friend class PhysicsWorld; \
 	friend class PhysicsPtrDeleter;
 	friend class PhysicsPtrDeleter;
+
+/// This is a callback that will decide if two filtered objects will be checked for collision.
+using PhysicsBroadPhaseCallback = Bool (*)(const PhysicsFilteredObject& a, const PhysicsFilteredObject& b);
+
+/// A PhysicsObject that takes part into collision detection. Has functionality to filter the broad phase detection.
+class PhysicsFilteredObject : public PhysicsObject
+{
+public:
+	PhysicsFilteredObject(PhysicsObjectType type, PhysicsWorld* world)
+		: PhysicsObject(type, world)
+	{
+	}
+
+	~PhysicsFilteredObject()
+	{
+	}
+
+	static Bool classof(const PhysicsObject* obj)
+	{
+		return obj->getType() >= PhysicsObjectType::FIRST_FILTERED
+			   && obj->getType() <= PhysicsObjectType::LAST_FILTERED;
+	}
+
+	/// Get the material(s) this object belongs.
+	PhysicsMaterialBit getMaterialGroup() const
+	{
+		return m_materialGroup;
+	}
+
+	/// Set the material(s) this object belongs.
+	void setMaterialGroup(PhysicsMaterialBit bits)
+	{
+		m_materialGroup = bits;
+	}
+
+	/// Get the materials this object collides.
+	PhysicsMaterialBit getMaterialMask() const
+	{
+		return m_materialMask;
+	}
+
+	/// Set the materials this object collides.
+	void setMaterialMask(PhysicsMaterialBit bit)
+	{
+		m_materialMask = bit;
+	}
+
+	/// Get the broadphase callback.
+	PhysicsBroadPhaseCallback getBroadPhaseCallback() const
+	{
+		return m_broadPhaseCallback;
+	}
+
+	/// Set the broadphase callback.
+	void setBroadPhaseCallback(PhysicsBroadPhaseCallback callback)
+	{
+		m_broadPhaseCallback = callback;
+	}
+
+private:
+	PhysicsMaterialBit m_materialGroup = PhysicsMaterialBit::ALL;
+	PhysicsMaterialBit m_materialMask = PhysicsMaterialBit::ALL;
+
+	PhysicsBroadPhaseCallback m_broadPhaseCallback = nullptr;
+};
 /// @}
 /// @}
 
 
 } // end namespace anki
 } // end namespace anki

+ 4 - 2
src/anki/physics/PhysicsPlayerController.cpp

@@ -10,7 +10,7 @@ namespace anki
 {
 {
 
 
 PhysicsPlayerController::PhysicsPlayerController(PhysicsWorld* world, const PhysicsPlayerControllerInitInfo& init)
 PhysicsPlayerController::PhysicsPlayerController(PhysicsWorld* world, const PhysicsPlayerControllerInitInfo& init)
-	: PhysicsObject(CLASS_TYPE, world)
+	: PhysicsFilteredObject(CLASS_TYPE, world)
 {
 {
 	const btTransform trf = toBt(Transform(init.m_position.xyz0(), Mat3x4::getIdentity(), 1.0f));
 	const btTransform trf = toBt(Transform(init.m_position.xyz0(), Mat3x4::getIdentity(), 1.0f));
 
 
@@ -19,7 +19,9 @@ PhysicsPlayerController::PhysicsPlayerController(PhysicsWorld* world, const Phys
 	m_ghostObject = getAllocator().newInstance<btPairCachingGhostObject>();
 	m_ghostObject = getAllocator().newInstance<btPairCachingGhostObject>();
 	m_ghostObject->setWorldTransform(trf);
 	m_ghostObject->setWorldTransform(trf);
 	m_ghostObject->setCollisionShape(m_convexShape);
 	m_ghostObject->setCollisionShape(m_convexShape);
-	m_ghostObject->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT);
+	m_ghostObject->setUserPointer(static_cast<PhysicsObject*>(this));
+	setMaterialGroup(PhysicsMaterialBit::PLAYER);
+	setMaterialMask(PhysicsMaterialBit::ALL);
 
 
 	m_controller = getAllocator().newInstance<btKinematicCharacterController>(
 	m_controller = getAllocator().newInstance<btKinematicCharacterController>(
 		m_ghostObject, m_convexShape, init.m_stepHeight, btVector3(0, 1, 0));
 		m_ghostObject, m_convexShape, init.m_stepHeight, btVector3(0, 1, 0));

+ 1 - 1
src/anki/physics/PhysicsPlayerController.h

@@ -26,7 +26,7 @@ public:
 };
 };
 
 
 /// A player controller that walks the world.
 /// A player controller that walks the world.
-class PhysicsPlayerController final : public PhysicsObject
+class PhysicsPlayerController final : public PhysicsFilteredObject
 {
 {
 	ANKI_PHYSICS_OBJECT
 	ANKI_PHYSICS_OBJECT
 
 

+ 4 - 1
src/anki/physics/PhysicsTrigger.cpp

@@ -11,7 +11,7 @@ namespace anki
 {
 {
 
 
 PhysicsTrigger::PhysicsTrigger(PhysicsWorld* world, PhysicsCollisionShapePtr shape)
 PhysicsTrigger::PhysicsTrigger(PhysicsWorld* world, PhysicsCollisionShapePtr shape)
-	: PhysicsObject(CLASS_TYPE, world)
+	: PhysicsFilteredObject(CLASS_TYPE, world)
 {
 {
 	m_shape = shape;
 	m_shape = shape;
 
 
@@ -21,6 +21,9 @@ PhysicsTrigger::PhysicsTrigger(PhysicsWorld* world, PhysicsCollisionShapePtr sha
 
 
 	m_ghostShape->setUserPointer(static_cast<PhysicsObject*>(this));
 	m_ghostShape->setUserPointer(static_cast<PhysicsObject*>(this));
 
 
+	setMaterialGroup(PhysicsMaterialBit::TRIGGER);
+	setMaterialMask(PhysicsMaterialBit::ALL);
+
 	auto lock = getWorld().lockBtWorld();
 	auto lock = getWorld().lockBtWorld();
 	getWorld().getBtWorld()->addCollisionObject(m_ghostShape);
 	getWorld().getBtWorld()->addCollisionObject(m_ghostShape);
 }
 }

+ 1 - 1
src/anki/physics/PhysicsTrigger.h

@@ -33,7 +33,7 @@ public:
 };
 };
 
 
 /// A trigger that uses a PhysicsShape and its purpose is to collect collision events.
 /// A trigger that uses a PhysicsShape and its purpose is to collect collision events.
-class PhysicsTrigger : public PhysicsObject
+class PhysicsTrigger : public PhysicsFilteredObject
 {
 {
 	ANKI_PHYSICS_OBJECT
 	ANKI_PHYSICS_OBJECT
 
 

+ 58 - 0
src/anki/physics/PhysicsWorld.cpp

@@ -7,6 +7,7 @@
 #include <anki/physics/PhysicsCollisionShape.h>
 #include <anki/physics/PhysicsCollisionShape.h>
 #include <anki/physics/PhysicsBody.h>
 #include <anki/physics/PhysicsBody.h>
 #include <anki/physics/PhysicsTrigger.h>
 #include <anki/physics/PhysicsTrigger.h>
+#include <anki/util/Rtti.h>
 #include <BulletCollision/Gimpact/btGImpactCollisionAlgorithm.h>
 #include <BulletCollision/Gimpact/btGImpactCollisionAlgorithm.h>
 
 
 namespace anki
 namespace anki
@@ -27,6 +28,60 @@ static void btFree(void* ptr)
 	gAlloc->getMemoryPool().free(ptr);
 	gAlloc->getMemoryPool().free(ptr);
 }
 }
 
 
+/// Broad phase collision callback.
+class PhysicsWorld::MyOverlapFilterCallback : public btOverlapFilterCallback
+{
+public:
+	bool needBroadphaseCollision(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) const override
+	{
+		ANKI_ASSERT(proxy0 && proxy1);
+
+		const btCollisionObject* btObj0 = static_cast<const btCollisionObject*>(proxy0->m_clientObject);
+		const btCollisionObject* btObj1 = static_cast<const btCollisionObject*>(proxy1->m_clientObject);
+		ANKI_ASSERT(btObj0 && btObj1);
+
+		const PhysicsObject* aobj0 = static_cast<const PhysicsObject*>(btObj0->getUserPointer());
+		const PhysicsObject* aobj1 = static_cast<const PhysicsObject*>(btObj1->getUserPointer());
+
+		if(aobj0 == nullptr || aobj1 == nullptr)
+		{
+			return false;
+		}
+
+		const PhysicsFilteredObject* fobj0 = dcast<const PhysicsFilteredObject*>(aobj0);
+		const PhysicsFilteredObject* fobj1 = dcast<const PhysicsFilteredObject*>(aobj1);
+
+		// First check the masks
+		Bool collide = !!(fobj0->getMaterialGroup() & fobj1->getMaterialMask());
+		collide = collide && !!(fobj1->getMaterialGroup() & fobj0->getMaterialMask());
+		if(!collide)
+		{
+			return false;
+		}
+
+		// Detailed tests using callbacks
+		if(fobj0->getBroadPhaseCallback())
+		{
+			collide = fobj0->getBroadPhaseCallback()(*fobj0, *fobj1);
+			if(!collide)
+			{
+				return false;
+			}
+		}
+
+		if(fobj1->getBroadPhaseCallback())
+		{
+			collide = fobj1->getBroadPhaseCallback()(*fobj1, *fobj0);
+			if(!collide)
+			{
+				return false;
+			}
+		}
+
+		return true;
+	}
+};
+
 PhysicsWorld::PhysicsWorld()
 PhysicsWorld::PhysicsWorld()
 {
 {
 }
 }
@@ -46,6 +101,7 @@ PhysicsWorld::~PhysicsWorld()
 	m_alloc.deleteInstance(m_collisionConfig);
 	m_alloc.deleteInstance(m_collisionConfig);
 	m_alloc.deleteInstance(m_broadphase);
 	m_alloc.deleteInstance(m_broadphase);
 	m_alloc.deleteInstance(m_gpc);
 	m_alloc.deleteInstance(m_gpc);
+	m_alloc.deleteInstance(m_filterCallback);
 
 
 	gAlloc = nullptr;
 	gAlloc = nullptr;
 }
 }
@@ -63,6 +119,8 @@ Error PhysicsWorld::create(AllocAlignedCallback allocCb, void* allocCbData)
 	m_broadphase = m_alloc.newInstance<btDbvtBroadphase>();
 	m_broadphase = m_alloc.newInstance<btDbvtBroadphase>();
 	m_gpc = m_alloc.newInstance<btGhostPairCallback>();
 	m_gpc = m_alloc.newInstance<btGhostPairCallback>();
 	m_broadphase->getOverlappingPairCache()->setInternalGhostPairCallback(m_gpc);
 	m_broadphase->getOverlappingPairCache()->setInternalGhostPairCallback(m_gpc);
+	m_filterCallback = m_alloc.newInstance<MyOverlapFilterCallback>();
+	m_broadphase->getOverlappingPairCache()->setOverlapFilterCallback(m_filterCallback);
 
 
 	m_collisionConfig = m_alloc.newInstance<btDefaultCollisionConfiguration>();
 	m_collisionConfig = m_alloc.newInstance<btDefaultCollisionConfiguration>();
 
 

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

@@ -70,11 +70,14 @@ anki_internal:
 	void destroyObject(PhysicsObject* obj);
 	void destroyObject(PhysicsObject* obj);
 
 
 private:
 private:
+	class MyOverlapFilterCallback;
+
 	HeapAllocator<U8> m_alloc;
 	HeapAllocator<U8> m_alloc;
 	StackAllocator<U8> m_tmpAlloc;
 	StackAllocator<U8> m_tmpAlloc;
 
 
 	btBroadphaseInterface* m_broadphase = nullptr;
 	btBroadphaseInterface* m_broadphase = nullptr;
 	btGhostPairCallback* m_gpc = nullptr;
 	btGhostPairCallback* m_gpc = nullptr;
+	MyOverlapFilterCallback* m_filterCallback = nullptr;
 
 
 	btDefaultCollisionConfiguration* m_collisionConfig = nullptr;
 	btDefaultCollisionConfiguration* m_collisionConfig = nullptr;
 	btCollisionDispatcher* m_dispatcher = nullptr;
 	btCollisionDispatcher* m_dispatcher = nullptr;

+ 5 - 0
src/anki/scene/BodyNode.h

@@ -24,6 +24,11 @@ public:
 
 
 	ANKI_USE_RESULT Error init(const CString& resourceFname);
 	ANKI_USE_RESULT Error init(const CString& resourceFname);
 
 
+	PhysicsBodyPtr getPhysicsBody() const
+	{
+		return m_body;
+	}
+
 private:
 private:
 	CollisionResourcePtr m_rsrc;
 	CollisionResourcePtr m_rsrc;
 	PhysicsBodyPtr m_body;
 	PhysicsBodyPtr m_body;

+ 5 - 0
src/anki/scene/components/PlayerControllerComponent.h

@@ -54,6 +54,11 @@ public:
 		return Error::NONE;
 		return Error::NONE;
 	}
 	}
 
 
+	PhysicsPlayerControllerPtr getPhysicsPlayerController() const
+	{
+		return m_player;
+	}
+
 private:
 private:
 	PhysicsPlayerControllerPtr m_player;
 	PhysicsPlayerControllerPtr m_player;
 	Transform m_trf;
 	Transform m_trf;

+ 3 - 3
src/anki/util/Rtti.h

@@ -48,16 +48,16 @@ struct ExtractType<T*>
 
 
 /// Check if a class is of certain type.
 /// Check if a class is of certain type.
 template<typename TTo, typename TFrom>
 template<typename TTo, typename TFrom>
-inline Bool isa(TFrom& c)
+inline Bool isa(const TFrom& c)
 {
 {
 	return TTo::classof(c);
 	return TTo::classof(c);
 }
 }
 
 
 /// Check if a class is of certain type.
 /// Check if a class is of certain type.
 template<typename TTo, typename TFrom>
 template<typename TTo, typename TFrom>
-inline Bool isa(TFrom* c)
+inline Bool isa(const TFrom* c)
 {
 {
-	return TTo::classof(*c);
+	return TTo::classof(c);
 }
 }
 
 
 /// Custom dynamic cast.
 /// Custom dynamic cast.