BsPhysX.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. #include "BsPhysX.h"
  2. #include "PxPhysicsAPI.h"
  3. #include "BsPhysXMaterial.h"
  4. #include "BsPhysXRigidbody.h"
  5. #include "BsPhysXBoxCollider.h"
  6. #include "BsPhysXSphereCollider.h"
  7. #include "BsPhysXPlaneCollider.h"
  8. #include "BsPhysXCapsuleCollider.h"
  9. #include "BsTaskScheduler.h"
  10. #include "BsTime.h"
  11. #include "Bsvector3.h"
  12. using namespace physx;
  13. namespace BansheeEngine
  14. {
  15. struct PHYSICS_INIT_DESC
  16. {
  17. float typicalLength = 1.0f;
  18. float typicalSpeed = 9.81f;
  19. Vector3 gravity = Vector3(0.0f, -9.81f, 0.0f);
  20. bool initCooking = false;
  21. float timeStep = 1.0f / 60.0f;
  22. };
  23. class PhysXAllocator : public PxAllocatorCallback
  24. {
  25. public:
  26. void* allocate(size_t size, const char*, const char*, int) override
  27. {
  28. void* ptr = bs_alloc_aligned16((UINT32)size);
  29. PX_ASSERT((reinterpret_cast<size_t>(ptr) & 15) == 0);
  30. return ptr;
  31. }
  32. void deallocate(void* ptr) override
  33. {
  34. bs_free_aligned16(ptr);
  35. }
  36. };
  37. class PhysXErrorCallback : public PxErrorCallback
  38. {
  39. public:
  40. void reportError(PxErrorCode::Enum code, const char* message, const char* file, int line) override
  41. {
  42. {
  43. const char* errorCode = nullptr;
  44. UINT32 severity = 0;
  45. switch (code)
  46. {
  47. case PxErrorCode::eNO_ERROR:
  48. errorCode = "No error";
  49. break;
  50. case PxErrorCode::eINVALID_PARAMETER:
  51. errorCode = "Invalid parameter";
  52. severity = 2;
  53. break;
  54. case PxErrorCode::eINVALID_OPERATION:
  55. errorCode = "Invalid operation";
  56. severity = 2;
  57. break;
  58. case PxErrorCode::eOUT_OF_MEMORY:
  59. errorCode = "Out of memory";
  60. severity = 2;
  61. break;
  62. case PxErrorCode::eDEBUG_INFO:
  63. errorCode = "Info";
  64. break;
  65. case PxErrorCode::eDEBUG_WARNING:
  66. errorCode = "Warning";
  67. severity = 1;
  68. break;
  69. case PxErrorCode::ePERF_WARNING:
  70. errorCode = "Performance warning";
  71. severity = 1;
  72. break;
  73. case PxErrorCode::eABORT:
  74. errorCode = "Abort";
  75. severity = 2;
  76. break;
  77. case PxErrorCode::eINTERNAL_ERROR:
  78. errorCode = "Internal error";
  79. severity = 2;
  80. break;
  81. case PxErrorCode::eMASK_ALL:
  82. default:
  83. errorCode = "Unknown error";
  84. severity = 2;
  85. break;
  86. }
  87. StringStream ss;
  88. switch(severity)
  89. {
  90. case 0:
  91. ss << "PhysX info (" << errorCode << "): " << message << " at " << file << ":" << line;
  92. LOGDBG(ss.str());
  93. break;
  94. case 1:
  95. ss << "PhysX warning (" << errorCode << "): " << message << " at " << file << ":" << line;
  96. LOGWRN(ss.str());
  97. break;
  98. case 2:
  99. ss << "PhysX error (" << errorCode << "): " << message << " at " << file << ":" << line;
  100. LOGERR(ss.str());
  101. BS_ASSERT(false); // Halt execution on debug builds when error occurrs
  102. break;
  103. }
  104. }
  105. }
  106. };
  107. class PhysXCPUDispatcher : public PxCpuDispatcher
  108. {
  109. public:
  110. void submitTask(PxBaseTask& physxTask) override
  111. {
  112. // TODO - Banshee's task scheduler is pretty low granularity. Consider a better task manager in case PhysX ends
  113. // up submitting many tasks.
  114. // - PhysX's task manager doesn't seem much lighter either. But perhaps I can at least create a task pool to
  115. // avoid allocating them constantly.
  116. auto runTask = [&]() { physxTask.run(); physxTask.release(); };
  117. TaskPtr task = Task::create("PhysX", runTask);
  118. TaskScheduler::instance().addTask(task);
  119. }
  120. PxU32 getWorkerCount() const override
  121. {
  122. return (PxU32)TaskScheduler::instance().getNumWorkers();
  123. }
  124. };
  125. PxFilterFlags PhysXFilterShader(PxFilterObjectAttributes attr0, PxFilterData data0, PxFilterObjectAttributes attr1,
  126. PxFilterData data1, PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize)
  127. {
  128. if (PxFilterObjectIsTrigger(attr0) || PxFilterObjectIsTrigger(attr1))
  129. {
  130. pairFlags = PxPairFlag::eTRIGGER_DEFAULT;
  131. return PxFilterFlags();
  132. }
  133. UINT64 groupA = *(UINT64*)&data0.word0;
  134. UINT64 groupB = *(UINT64*)&data1.word0;
  135. bool canCollide = Physics::instance().isCollisionEnabled(groupA, groupB);
  136. if (!canCollide)
  137. return PxFilterFlag::eSUPPRESS;
  138. pairFlags = PxPairFlag::eCONTACT_DEFAULT;
  139. return PxFilterFlags();
  140. }
  141. static PhysXAllocator gPhysXAllocator;
  142. static PhysXErrorCallback gPhysXErrorHandler;
  143. static PhysXCPUDispatcher gPhysXCPUDispatcher;
  144. PhysX::PhysX()
  145. {
  146. PHYSICS_INIT_DESC input; // TODO - Make this an input parameter.
  147. PxTolerancesScale scale; // TODO - Use these same values for cooking, physx init and scene desc
  148. scale.length = input.typicalLength;
  149. scale.speed = input.typicalSpeed;
  150. mFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, gPhysXAllocator, gPhysXErrorHandler);
  151. mPhysics = PxCreateBasePhysics(PX_PHYSICS_VERSION, *mFoundation, scale);
  152. PxRegisterArticulations(*mPhysics);
  153. if (input.initCooking)
  154. {
  155. PxCookingParams cookingParams(scale); // TODO - Potentially allow more customization to set up cooking params
  156. mCooking = PxCreateCooking(PX_PHYSICS_VERSION, *mFoundation, cookingParams);
  157. }
  158. PxSceneDesc sceneDesc(scale); // TODO - Test out various other parameters provided by scene desc
  159. sceneDesc.gravity = toPxVector(input.gravity);
  160. sceneDesc.cpuDispatcher = &gPhysXCPUDispatcher;
  161. sceneDesc.filterShader = PhysXFilterShader;
  162. // TODO - Hook up triggers
  163. // TODO - Allow for continuous collision detection, and regions of interest stuff
  164. // TODO - Set up various performance limits, call flushCache when needed
  165. // TODO - Probably many more startup settings I'm missing
  166. mScene = mPhysics->createScene(sceneDesc);
  167. mSimulationStep = input.timeStep;
  168. mDefaultMaterial = mPhysics->createMaterial(0.0f, 0.0f, 0.0f);
  169. }
  170. PhysX::~PhysX()
  171. {
  172. mScene->release();
  173. if (mCooking != nullptr)
  174. mCooking->release();
  175. mPhysics->release();
  176. mFoundation->release();
  177. }
  178. void PhysX::update()
  179. {
  180. float nextFrameTime = mLastSimulationTime + mSimulationStep;
  181. float curFrameTime = gTime().getTime();
  182. if(curFrameTime < nextFrameTime)
  183. {
  184. // TODO - Interpolate rigidbodies but perform no actual simulation
  185. return;
  186. }
  187. float simulationAmount = curFrameTime - mLastSimulationTime;
  188. while (simulationAmount >= mSimulationStep) // In case we're running really slow multiple updates might be needed
  189. {
  190. // TODO - Consider delaying fetchResults one frame. This could improve performance but at a cost to input latency.
  191. // TODO - Provide a scratch buffer for the simulation (use the frame allocator, but I must extend it so it allocates
  192. // on a 16 byte boundary).
  193. mScene->simulate(mSimulationStep);
  194. mScene->fetchResults(true);
  195. // TODO - Update all rigidbody transfroms from their PhsyX state
  196. simulationAmount -= mSimulationStep;
  197. }
  198. // TODO - Consider extrapolating for the remaining "simulationAmount" value
  199. mLastSimulationTime = curFrameTime;
  200. }
  201. SPtr<PhysicsMaterial> PhysX::createMaterial(float staticFriction, float dynamicFriction, float restitution)
  202. {
  203. return bs_shared_ptr_new<PhysXMaterial>(mPhysics, staticFriction, dynamicFriction, restitution);
  204. }
  205. SPtr<Rigidbody> PhysX::createRigidbody(const Vector3& position, const Quaternion& rotation)
  206. {
  207. return bs_shared_ptr_new<PhysXRigidbody>(mPhysics, mScene, position, rotation);
  208. }
  209. SPtr<BoxCollider> PhysX::createBoxCollider(float extentX, float extentY, float extentZ, const Vector3& position,
  210. const Quaternion& rotation)
  211. {
  212. return bs_shared_ptr_new<PhysXBoxCollider>(mPhysics, position, rotation, extentX, extentY, extentZ);
  213. }
  214. SPtr<SphereCollider> PhysX::createSphereCollider(float radius, const Vector3& position, const Quaternion& rotation)
  215. {
  216. return bs_shared_ptr_new<PhysXSphereCollider>(mPhysics, position, rotation, radius);
  217. }
  218. SPtr<PlaneCollider> PhysX::createPlaneCollider(const Vector3& position, const Quaternion& rotation)
  219. {
  220. return bs_shared_ptr_new<PhysXPlaneCollider>(mPhysics, position, rotation);
  221. }
  222. SPtr<CapsuleCollider> PhysX::createCapsuleCollider(float radius, float halfHeight, const Vector3& position,
  223. const Quaternion& rotation)
  224. {
  225. return bs_shared_ptr_new<PhysXCapsuleCollider>(mPhysics, position, rotation, radius, halfHeight);
  226. }
  227. PhysX& gPhysX()
  228. {
  229. return static_cast<PhysX&>(PhysX::instance());
  230. }
  231. }