BsPhysX.cpp 7.0 KB

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