BsPhysX.cpp 6.6 KB

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