Bläddra i källkod

PhysX collision callbacks WIP

BearishSun 10 år sedan
förälder
incheckning
c8ecb3aea7

+ 5 - 4
BansheeCore/Include/BsCollision.h

@@ -9,15 +9,16 @@ namespace BansheeEngine
 {
 {
 	struct ContactPoint
 	struct ContactPoint
 	{
 	{
-		Vector3 point;
-		Vector3 normal;
+		Vector3 position; /**< Contact point in world space. */
+		Vector3 normal; /**< Normal pointing from the second shape to the first shape. */
+		/** Impulse applied to the objects to keep them from penetrating. Divide by simulation step to get the force. */
+		float impulse;
+		float separation; /**< Determines how far are the objects. Negative value denotes penetration. */
 	};
 	};
 
 
 	struct CollisionData
 	struct CollisionData
 	{
 	{
 		SPtr<Collider> collider;
 		SPtr<Collider> collider;
 		Vector<ContactPoint> contactPoints;
 		Vector<ContactPoint> contactPoints;
-		Vector3 impulse;
-		Vector3 relativeVelocity;
 	};
 	};
 }
 }

+ 16 - 0
BansheePhysX/Include/BsFPhysXCollider.h

@@ -22,10 +22,26 @@ namespace BansheeEngine
 		void setIsTrigger(bool value) override;
 		void setIsTrigger(bool value) override;
 		bool getIsTrigger() const override;
 		bool getIsTrigger() const override;
 
 
+		/**
+		 * Determines how far apart do two shapes need to be away from each other before the physics runtime starts 
+		 * generating repelling impulse for them. This distance will be the sum of contact offsets of the two interacting
+		 * objects. If objects are moving fast you can increase this value to start generating the impulse earlier and 
+		 * potentially prevent the objects from interpenetrating. This value is in meters.
+		 *
+		 * Also see setRestOffset().
+		 */
 		void setContactOffset(float value) override;
 		void setContactOffset(float value) override;
+
+		/** Returns shape's contact offset in meters. See setContactOffset() to learn contact offset is. */
 		float getContactOffset() override;
 		float getContactOffset() override;
 
 
+		/**
+		 * Sets at what distance should two objects resting on one another come to an equilibrium. The value used in the
+		 * runtime will be the sum of rest offsets for both interacting objects. This value is in meters.
+		 */
 		void setRestOffset(float value) override;
 		void setRestOffset(float value) override;
+
+		/** Returns shepe's rest offset in meters. See setRestOffset() to learn what contact offset is. */
 		float getRestOffset() override;
 		float getRestOffset() override;
 
 
 		void setRigidbody(const SPtr<Rigidbody>& rigidbody) override;
 		void setRigidbody(const SPtr<Rigidbody>& rigidbody) override;

+ 31 - 0
BansheePhysX/Include/BsPhysX.h

@@ -4,6 +4,7 @@
 
 
 #include "BsPhysXPrerequisites.h"
 #include "BsPhysXPrerequisites.h"
 #include "BsPhysics.h"
 #include "BsPhysics.h"
+#include "BSCollision.h"
 #include "PxPhysics.h"
 #include "PxPhysics.h"
 #include "foundation/Px.h"
 #include "foundation/Px.h"
 #include "cooking/PxCooking.h"
 #include "cooking/PxCooking.h"
@@ -12,6 +13,28 @@ namespace BansheeEngine
 {
 {
 	class PhysX : public Physics
 	class PhysX : public Physics
 	{
 	{
+		enum class ContactEventType
+		{
+			ContactStart,
+			ContactStay,
+			ContactStop
+		};
+
+		struct TriggerEvent
+		{
+			Collider* trigger;
+			Collider* other;
+			ContactEventType type;
+		};
+
+		struct ContactEvent
+		{
+			Collider* colliderA;
+			Collider* colliderB;
+			ContactEventType type;
+			Vector<ContactPoint> points;
+		};
+
 	public:
 	public:
 		PhysX();
 		PhysX();
 		~PhysX();
 		~PhysX();
@@ -29,14 +52,22 @@ namespace BansheeEngine
 		SPtr<CapsuleCollider> createCapsuleCollider(float radius, float halfHeight, const Vector3& position, 
 		SPtr<CapsuleCollider> createCapsuleCollider(float radius, float halfHeight, const Vector3& position, 
 			const Quaternion& rotation) override;
 			const Quaternion& rotation) override;
 
 
+		void _reportContactEvent(const ContactEvent& event);
+		void _reportTriggerEvent(const TriggerEvent& event);
+
 		physx::PxMaterial* getDefaultMaterial() const { return mDefaultMaterial; }
 		physx::PxMaterial* getDefaultMaterial() const { return mDefaultMaterial; }
 		physx::PxPhysics* getPhysX() const { return mPhysics; }
 		physx::PxPhysics* getPhysX() const { return mPhysics; }
 		physx::PxScene* getScene() const { return mScene; }
 		physx::PxScene* getScene() const { return mScene; }
 
 
 	private:
 	private:
+		friend class PhysXEventCallback;
+
 		float mSimulationStep = 1.0f/60.0f;
 		float mSimulationStep = 1.0f/60.0f;
 		float mLastSimulationTime = 0.0f;
 		float mLastSimulationTime = 0.0f;
 
 
+		Vector<TriggerEvent> mTriggerEvents;
+		Vector<ContactEvent> mContactEvents;
+
 		physx::PxFoundation* mFoundation = nullptr;
 		physx::PxFoundation* mFoundation = nullptr;
 		physx::PxPhysics* mPhysics = nullptr;
 		physx::PxPhysics* mPhysics = nullptr;
 		physx::PxCooking* mCooking = nullptr;
 		physx::PxCooking* mCooking = nullptr;

+ 137 - 5
BansheePhysX/Source/BsPhysX.cpp

@@ -113,12 +113,129 @@ namespace BansheeEngine
 		}
 		}
 	};
 	};
 
 
+	class PhysXEventCallback : public PxSimulationEventCallback
+	{
+		void onConstraintBreak(PxConstraintInfo* constraints, PxU32 count) override { /* Do nothing */ }
+		void onWake(PxActor** actors, PxU32 count) override { /* Do nothing */ }
+		void onSleep(PxActor** actors, PxU32 count) override { /* Do nothing */ }
+
+		void onTrigger(PxTriggerPair* pairs, PxU32 count) override
+		{
+			for (PxU32 i = 0; i < count; i++)
+			{
+				const PxTriggerPair& pair = pairs[i];
+
+				PhysX::ContactEventType type;
+				bool ignoreContact = false;
+				switch ((UINT32)pair.status)
+				{
+				case PxPairFlag::eNOTIFY_TOUCH_FOUND:
+					type = PhysX::ContactEventType::ContactStart;
+					break;
+				case PxPairFlag::eNOTIFY_TOUCH_PERSISTS:
+					type = PhysX::ContactEventType::ContactStay;
+					break;
+				case PxPairFlag::eNOTIFY_TOUCH_LOST:
+					type = PhysX::ContactEventType::ContactStop;
+					break;
+				default:
+					ignoreContact = true;
+					break;
+				}
+
+				if (ignoreContact)
+					continue;
+
+				PhysX::TriggerEvent event;
+				event.trigger = (Collider*)pair.triggerShape->userData;
+				event.other = (Collider*)pair.otherShape->userData;
+				event.type = type;
+
+				gPhysX()._reportTriggerEvent(event);
+			}
+		}
+
+		void onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, PxU32 count) override
+		{
+			for (PxU32 i = 0; i < count; i++)
+			{
+				const PxContactPair& pair = pairs[i];
+
+				PhysX::ContactEventType type;
+				bool ignoreContact = false;
+				switch((UINT32)pair.events)
+				{
+				case PxPairFlag::eNOTIFY_TOUCH_FOUND:
+					type = PhysX::ContactEventType::ContactStart;
+					break;
+				case PxPairFlag::eNOTIFY_TOUCH_PERSISTS:
+					type = PhysX::ContactEventType::ContactStay;
+					break;
+				case PxPairFlag::eNOTIFY_TOUCH_LOST:
+					type = PhysX::ContactEventType::ContactStop;
+					break;
+				default:
+					ignoreContact = true;
+					break;
+				}
+
+				if (ignoreContact)
+					continue;
+
+				PhysX::ContactEvent event;
+				event.colliderA = (Collider*)pair.shapes[0]->userData;
+				event.colliderB = (Collider*)pair.shapes[1]->userData;
+				event.type = type;
+
+				PxU32 contactCount = pair.contactCount;
+				const PxU8* stream = pair.contactStream;
+				PxU16 streamSize = pair.contactStreamSize;
+
+				if (contactCount > 0 && streamSize > 0)
+				{
+					PxU32 contactIdx = 0;
+					PxContactStreamIterator iter((PxU8*)stream, streamSize);
+
+					stream += ((streamSize + 15) & ~15);
+
+					const PxReal* impulses = reinterpret_cast<const PxReal*>(stream);
+					PxU32 hasImpulses = (pair.flags & PxContactPairFlag::eINTERNAL_HAS_IMPULSES);
+
+					while (iter.hasNextPatch())
+					{
+						iter.nextPatch();
+						while (iter.hasNextContact())
+						{
+							iter.nextContact();
+
+							ContactPoint point;
+							point.position = fromPxVector(iter.getContactPoint());
+							point.separation = iter.getSeparation();
+							point.normal = fromPxVector(iter.getContactNormal());
+
+							if (hasImpulses)
+								point.impulse = impulses[contactIdx];
+							else
+								point.impulse = 0.0f;
+
+							event.points.push_back(point);
+
+							contactIdx++;
+						}
+					}
+				}
+
+				gPhysX()._reportContactEvent(event);
+			}
+		}
+	};
+
 	class PhysXCPUDispatcher : public PxCpuDispatcher
 	class PhysXCPUDispatcher : public PxCpuDispatcher
 	{
 	{
 	public:
 	public:
 		void submitTask(PxBaseTask& physxTask) override
 		void submitTask(PxBaseTask& physxTask) override
 		{
 		{
-			// TODO - Banshee's task scheduler is pretty low granularity. Consider a better task manager in case PhysX ends
+			// Note: Banshee's task scheduler is pretty low granularity. Consider a better task manager in case PhysX ends
 			// up submitting many tasks.
 			// up submitting many tasks.
 			// - PhysX's task manager doesn't seem much lighter either. But perhaps I can at least create a task pool to 
 			// - PhysX's task manager doesn't seem much lighter either. But perhaps I can at least create a task pool to 
 			//   avoid allocating them constantly.
 			//   avoid allocating them constantly.
@@ -147,7 +264,7 @@ namespace BansheeEngine
 		UINT64 groupA = *(UINT64*)&data0.word0;
 		UINT64 groupA = *(UINT64*)&data0.word0;
 		UINT64 groupB = *(UINT64*)&data1.word0;
 		UINT64 groupB = *(UINT64*)&data1.word0;
 
 
-		bool canCollide = Physics::instance().isCollisionEnabled(groupA, groupB);
+		bool canCollide = gPhysics().isCollisionEnabled(groupA, groupB);
 		if (!canCollide)
 		if (!canCollide)
 			return PxFilterFlag::eSUPPRESS;
 			return PxFilterFlag::eSUPPRESS;
 
 
@@ -158,7 +275,8 @@ namespace BansheeEngine
 	static PhysXAllocator gPhysXAllocator;
 	static PhysXAllocator gPhysXAllocator;
 	static PhysXErrorCallback gPhysXErrorHandler;
 	static PhysXErrorCallback gPhysXErrorHandler;
 	static PhysXCPUDispatcher gPhysXCPUDispatcher;
 	static PhysXCPUDispatcher gPhysXCPUDispatcher;
-	
+	static PhysXEventCallback gPhysXEventCallback;
+
 	PhysX::PhysX()
 	PhysX::PhysX()
 	{
 	{
 		PHYSICS_INIT_DESC input; // TODO - Make this an input parameter.
 		PHYSICS_INIT_DESC input; // TODO - Make this an input parameter.
@@ -182,15 +300,14 @@ namespace BansheeEngine
 		sceneDesc.gravity = toPxVector(input.gravity);
 		sceneDesc.gravity = toPxVector(input.gravity);
 		sceneDesc.cpuDispatcher = &gPhysXCPUDispatcher;
 		sceneDesc.cpuDispatcher = &gPhysXCPUDispatcher;
 		sceneDesc.filterShader = PhysXFilterShader;
 		sceneDesc.filterShader = PhysXFilterShader;
+		sceneDesc.simulationEventCallback = &gPhysXEventCallback;
 
 
-		// TODO - Hook up triggers
 		// TODO - Allow for continuous collision detection, and regions of interest stuff
 		// TODO - Allow for continuous collision detection, and regions of interest stuff
 		// TODO - Set up various performance limits, call flushCache when needed
 		// TODO - Set up various performance limits, call flushCache when needed
 		// TODO - Probably many more startup settings I'm missing
 		// TODO - Probably many more startup settings I'm missing
 
 
 		mScene = mPhysics->createScene(sceneDesc);
 		mScene = mPhysics->createScene(sceneDesc);
 		mSimulationStep = input.timeStep;
 		mSimulationStep = input.timeStep;
-
 		mDefaultMaterial = mPhysics->createMaterial(0.0f, 0.0f, 0.0f);
 		mDefaultMaterial = mPhysics->createMaterial(0.0f, 0.0f, 0.0f);
 	}
 	}
 
 
@@ -233,6 +350,21 @@ namespace BansheeEngine
 		// TODO - Consider extrapolating for the remaining "simulationAmount" value
 		// TODO - Consider extrapolating for the remaining "simulationAmount" value
 
 
 		mLastSimulationTime = curFrameTime; 
 		mLastSimulationTime = curFrameTime; 
+
+		// TODO - Send out contact and trigger events
+
+		mTriggerEvents.clear();
+		mContactEvents.clear();
+	}
+
+	void PhysX::_reportContactEvent(const ContactEvent& event)
+	{
+		mContactEvents.push_back(event);
+	}
+
+	void PhysX::_reportTriggerEvent(const TriggerEvent& event)
+	{
+		mTriggerEvents.push_back(event);
 	}
 	}
 
 
 	SPtr<PhysicsMaterial> PhysX::createMaterial(float staticFriction, float dynamicFriction, float restitution)
 	SPtr<PhysicsMaterial> PhysX::createMaterial(float staticFriction, float dynamicFriction, float restitution)