BsPhysX.cpp 6.9 KB

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