PhysicsSystem.cpp 89 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <Jolt.h>
  4. #include <Physics/PhysicsSystem.h>
  5. #include <Physics/PhysicsSettings.h>
  6. #include <Physics/PhysicsUpdateContext.h>
  7. #include <Physics/PhysicsStepListener.h>
  8. #include <Physics/Collision/BroadPhase/BroadPhaseBruteForce.h>
  9. #include <Physics/Collision/BroadPhase/BroadPhaseQuadTree.h>
  10. #include <Physics/Collision/CollisionDispatch.h>
  11. #include <Physics/Collision/AABoxCast.h>
  12. #include <Physics/Collision/ShapeCast.h>
  13. #include <Physics/Collision/CollideShape.h>
  14. #include <Physics/Collision/CollisionCollectorImpl.h>
  15. #include <Physics/Collision/CastResult.h>
  16. #include <Physics/Collision/CollideConvexVsTriangles.h>
  17. #include <Physics/Collision/ManifoldBetweenTwoFaces.h>
  18. #include <Physics/Collision/Shape/ConvexShape.h>
  19. #include <Physics/Constraints/ConstraintPart/AxisConstraintPart.h>
  20. #include <Geometry/RayAABox.h>
  21. #include <Core/JobSystem.h>
  22. #include <Core/StatCollector.h>
  23. #include <Core/TempAllocator.h>
  24. namespace JPH {
  25. #ifdef JPH_DEBUG_RENDERER
  26. bool PhysicsSystem::sDrawMotionQualityLinearCast = false;
  27. #endif // JPH_DEBUG_RENDERER
  28. //#define BROAD_PHASE BroadPhaseBruteForce
  29. #define BROAD_PHASE BroadPhaseQuadTree
  30. static const Color cColorUpdateBroadPhaseFinalize = Color::sGetDistinctColor(1);
  31. static const Color cColorUpdateBroadPhasePrepare = Color::sGetDistinctColor(2);
  32. static const Color cColorFindCollisions = Color::sGetDistinctColor(3);
  33. static const Color cColorApplyGravity = Color::sGetDistinctColor(4);
  34. static const Color cColorSetupVelocityConstraints = Color::sGetDistinctColor(5);
  35. static const Color cColorBuildIslandsFromConstraints = Color::sGetDistinctColor(6);
  36. static const Color cColorDetermineActiveConstraints = Color::sGetDistinctColor(7);
  37. static const Color cColorFinalizeIslands = Color::sGetDistinctColor(8);
  38. static const Color cColorContactRemovedCallbacks = Color::sGetDistinctColor(9);
  39. static const Color cColorBodySetIslandIndex = Color::sGetDistinctColor(10);
  40. static const Color cColorStartNextStep = Color::sGetDistinctColor(11);
  41. static const Color cColorSolveVelocityConstraints = Color::sGetDistinctColor(12);
  42. static const Color cColorIntegrateVelocity = Color::sGetDistinctColor(13);
  43. static const Color cColorResolveCCDContacts = Color::sGetDistinctColor(14);
  44. static const Color cColorSolvePositionConstraints = Color::sGetDistinctColor(15);
  45. static const Color cColorStartNextSubStep = Color::sGetDistinctColor(16);
  46. static const Color cColorFindCCDContacts = Color::sGetDistinctColor(17);
  47. static const Color cColorStepListeners = Color::sGetDistinctColor(18);
  48. PhysicsSystem::~PhysicsSystem()
  49. {
  50. // Remove broadphase
  51. delete mBroadPhase;
  52. }
  53. void PhysicsSystem::Init(uint inMaxBodies, uint inMaxBodyPairs, uint inMaxContactConstraints, const ObjectToBroadPhaseLayer &inObjectToBroadPhaseLayer, BroadPhaseLayerPairFilter inBroadPhaseLayerPairFilter, ObjectLayerPairFilter inObjectLayerPairFilter)
  54. {
  55. mBroadPhaseLayerPairFilter = inBroadPhaseLayerPairFilter;
  56. mObjectLayerPairFilter = inObjectLayerPairFilter;
  57. mObjectToBroadPhaseLayer = inObjectToBroadPhaseLayer;
  58. // Initialize body manager
  59. mBodyManager.Init(inMaxBodies);
  60. // Create broadphase
  61. mBroadPhase = new BROAD_PHASE();
  62. mBroadPhase->Init(&mBodyManager, mObjectToBroadPhaseLayer);
  63. // Init contact constraint manager
  64. mContactManager.Init(inMaxBodyPairs, inMaxContactConstraints);
  65. // Init islands builder
  66. mIslandBuilder.Init(inMaxBodies);
  67. // Initialize body interface
  68. mBodyInterfaceLocking.~BodyInterface();
  69. new (&mBodyInterfaceLocking) BodyInterface(mBodyLockInterfaceLocking, mBodyManager, *mBroadPhase);
  70. mBodyInterfaceNoLock.~BodyInterface();
  71. new (&mBodyInterfaceNoLock) BodyInterface(mBodyLockInterfaceNoLock, mBodyManager, *mBroadPhase);
  72. // Initialize narrow phase query
  73. mNarrowPhaseQueryLocking.~NarrowPhaseQuery();
  74. new (&mNarrowPhaseQueryLocking) NarrowPhaseQuery(mBodyLockInterfaceLocking, *mBroadPhase);
  75. mNarrowPhaseQueryNoLock.~NarrowPhaseQuery();
  76. new (&mNarrowPhaseQueryNoLock) NarrowPhaseQuery(mBodyLockInterfaceNoLock, *mBroadPhase);
  77. }
  78. void PhysicsSystem::OptimizeBroadPhase()
  79. {
  80. mBroadPhase->Optimize();
  81. }
  82. void PhysicsSystem::AddStepListener(PhysicsStepListener *inListener)
  83. {
  84. lock_guard<Mutex> lock(mStepListenersMutex);
  85. JPH_ASSERT(find(mStepListeners.begin(), mStepListeners.end(), inListener) == mStepListeners.end());
  86. mStepListeners.push_back(inListener);
  87. }
  88. void PhysicsSystem::RemoveStepListener(PhysicsStepListener *inListener)
  89. {
  90. lock_guard<Mutex> lock(mStepListenersMutex);
  91. StepListeners::iterator i = find(mStepListeners.begin(), mStepListeners.end(), inListener);
  92. JPH_ASSERT(i != mStepListeners.end());
  93. mStepListeners.erase(i);
  94. }
  95. void PhysicsSystem::Update(float inDeltaTime, int inCollisionSteps, int inIntegrationSubSteps, TempAllocator *inTempAllocator, JobSystem *inJobSystem)
  96. {
  97. JPH_PROFILE_FUNCTION();
  98. JPH_ASSERT(inDeltaTime >= 0.0f);
  99. JPH_ASSERT(inIntegrationSubSteps <= PhysicsUpdateContext::cMaxSubSteps);
  100. #ifdef JPH_STAT_COLLECTOR
  101. // Reset stats
  102. mManifoldsBeforeReduction = 0;
  103. mManifoldsAfterReduction = 0;
  104. CollideConvexVsTriangles::sResetStats();
  105. ConvexShape::sResetStats();
  106. #endif // JPH_STAT_COLLECTOR
  107. // Sync point for the broadphase. This will allow it to do clean up operations without having any mutexes locked yet.
  108. mBroadPhase->FrameSync();
  109. // If there are no active bodies or there's no time delta
  110. uint32 num_active_bodies = mBodyManager.GetNumActiveBodies();
  111. if (num_active_bodies == 0 || inDeltaTime <= 0.0f)
  112. {
  113. mBodyManager.LockAllBodies();
  114. // Update broadphase
  115. mBroadPhase->LockModifications();
  116. BroadPhase::UpdateState update_state = mBroadPhase->UpdatePrepare();
  117. mBroadPhase->UpdateFinalize(update_state);
  118. mBroadPhase->UnlockModifications();
  119. mBodyManager.UnlockAllBodies();
  120. return;
  121. }
  122. // Calculate ratio between current and previous frame delta time to scale initial constraint forces
  123. float sub_step_delta_time = inDeltaTime / (inCollisionSteps * inIntegrationSubSteps);
  124. float warm_start_impulse_ratio = mPhysicsSettings.mConstraintWarmStart && mPreviousSubStepDeltaTime > 0.0f? sub_step_delta_time / mPreviousSubStepDeltaTime : 0.0f;
  125. mPreviousSubStepDeltaTime = sub_step_delta_time;
  126. // Create the context used for passing information between jobs
  127. PhysicsUpdateContext context;
  128. context.mPhysicsSystem = this;
  129. context.mTempAllocator = inTempAllocator;
  130. context.mJobSystem = inJobSystem;
  131. context.mBarrier = inJobSystem->CreateBarrier();
  132. context.mIslandBuilder = &mIslandBuilder;
  133. context.mStepDeltaTime = inDeltaTime / inCollisionSteps;
  134. context.mSubStepDeltaTime = sub_step_delta_time;
  135. context.mWarmStartImpulseRatio = warm_start_impulse_ratio;
  136. // Allocate space for body pairs
  137. JPH_ASSERT(context.mBodyPairs == nullptr);
  138. context.mBodyPairs = static_cast<BodyPair *>(inTempAllocator->Allocate(sizeof(BodyPair) * mPhysicsSettings.mMaxInFlightBodyPairs));
  139. // Lock all bodies for write so that we can freely touch them
  140. mStepListenersMutex.lock();
  141. mBodyManager.LockAllBodies();
  142. mBroadPhase->LockModifications();
  143. // Get max number of concurrent jobs
  144. int max_concurrency = context.GetMaxConcurrency();
  145. // Calculate how many step listener jobs we spawn
  146. int num_step_listener_jobs = mStepListeners.empty()? 0 : max(1, min((int)mStepListeners.size() / mPhysicsSettings.mStepListenersBatchSize / mPhysicsSettings.mStepListenerBatchesPerJob, max_concurrency));
  147. // Number of gravity jobs depends on the amount of active bodies.
  148. // Launch max 1 job per batch of active bodies
  149. // Leave 1 thread for update broadphase prepare and 1 for determine active constraints
  150. int num_apply_gravity_jobs = max(1, min(((int)num_active_bodies + cApplyGravityBatchSize - 1) / cApplyGravityBatchSize, max_concurrency - 2));
  151. // Number of determine active constraints jobs to run depends on number of constraints.
  152. // Leave 1 thread for update broadphase prepare and 1 for apply gravity
  153. int num_determine_active_constraints_jobs = max(1, min(((int)mConstraintManager.GetNumConstraints() + cDetermineActiveConstraintsBatchSize - 1) / cDetermineActiveConstraintsBatchSize, max_concurrency - 2));
  154. // Number of find collisions jobs to run depends on number of active bodies.
  155. int num_find_collisions_jobs = max(1, min(((int)num_active_bodies + cActiveBodiesBatchSize - 1) / cActiveBodiesBatchSize, max_concurrency));
  156. {
  157. JPH_PROFILE("Build Jobs");
  158. // Iterate over collision steps
  159. context.mSteps.resize(inCollisionSteps);
  160. for (int step_idx = 0; step_idx < inCollisionSteps; ++step_idx)
  161. {
  162. bool is_first_step = step_idx == 0;
  163. bool is_last_step = step_idx == inCollisionSteps - 1;
  164. PhysicsUpdateContext::Step &step = context.mSteps[step_idx];
  165. step.mContext = &context;
  166. step.mSubSteps.resize(inIntegrationSubSteps);
  167. // Create job to do broadphase finalization
  168. // This job must finish before integrating velocities. Until then the positions will not be updated neither will bodies be added / removed.
  169. step.mUpdateBroadphaseFinalize = inJobSystem->CreateJob("UpdateBroadPhaseFinalize", cColorUpdateBroadPhaseFinalize, [&context, &step]()
  170. {
  171. // Validate that all find collision jobs have stopped
  172. JPH_ASSERT(step.mActiveFindCollisionJobs == 0);
  173. // Finalize the broadphase update
  174. context.mPhysicsSystem->mBroadPhase->UpdateFinalize(step.mBroadPhaseUpdateState);
  175. // Signal that it is done
  176. step.mSubSteps[0].mIntegrateVelocity.RemoveDependency();
  177. }, num_find_collisions_jobs + 2); // depends on: find collisions, broadphase prepare update, finish building jobs
  178. // The immediate jobs below are only immediate for the first step, the all finished job will kick them for the next step
  179. int previous_step_dependency_count = is_first_step? 0 : 1;
  180. // Start job immediately: Start the prepare broadphase
  181. // Must be done under body lock protection since the order is body locks then broadphase mutex
  182. // If this is turned around the RemoveBody call will hang since it locks in that order
  183. step.mBroadPhasePrepare = inJobSystem->CreateJob("UpdateBroadPhasePrepare", cColorUpdateBroadPhasePrepare, [&context, &step]()
  184. {
  185. // Prepare the broadphase update
  186. step.mBroadPhaseUpdateState = context.mPhysicsSystem->mBroadPhase->UpdatePrepare();
  187. // Now the finalize can run (if other dependencies are met too)
  188. step.mUpdateBroadphaseFinalize.RemoveDependency();
  189. }, previous_step_dependency_count);
  190. // This job will find all collisions
  191. step.mBodyPairQueues.resize(max_concurrency);
  192. step.mMaxBodyPairsPerQueue = mPhysicsSettings.mMaxInFlightBodyPairs / max_concurrency;
  193. step.mActiveFindCollisionJobs = ~PhysicsUpdateContext::JobMask(0) >> (sizeof(PhysicsUpdateContext::JobMask) * 8 - num_find_collisions_jobs);
  194. step.mFindCollisions.resize(num_find_collisions_jobs);
  195. for (int i = 0; i < num_find_collisions_jobs; ++i)
  196. {
  197. step.mFindCollisions[i] = inJobSystem->CreateJob("FindCollisions", cColorFindCollisions, [&step, i]()
  198. {
  199. step.mContext->mPhysicsSystem->JobFindCollisions(&step, i);
  200. }, num_apply_gravity_jobs + num_determine_active_constraints_jobs + 1); // depends on: apply gravity, determine active constraints, finish building jobs
  201. }
  202. if (is_first_step)
  203. {
  204. #ifdef JPH_ENABLE_ASSERTS
  205. // Don't allow write operations to the active bodies list
  206. mBodyManager.SetActiveBodiesLocked(true);
  207. #endif
  208. // Store the number of active bodies at the start of the step
  209. step.mNumActiveBodiesAtStepStart = mBodyManager.GetNumActiveBodies();
  210. // Lock all constraints
  211. mConstraintManager.LockAllConstraints();
  212. // Allocate memory for storing the active constraints
  213. JPH_ASSERT(context.mActiveConstraints == nullptr);
  214. context.mActiveConstraints = static_cast<Constraint **>(inTempAllocator->Allocate(mConstraintManager.GetNumConstraints() * sizeof(Constraint *)));
  215. // Prepare contact buffer
  216. mContactManager.PrepareConstraintBuffer(&context);
  217. // Setup island builder
  218. mIslandBuilder.PrepareContactConstraints(mContactManager.GetMaxConstraints(), context.mTempAllocator);
  219. }
  220. // This job applies gravity to all active bodies
  221. step.mApplyGravity.resize(num_apply_gravity_jobs);
  222. for (int i = 0; i < num_apply_gravity_jobs; ++i)
  223. step.mApplyGravity[i] = inJobSystem->CreateJob("ApplyGravity", cColorApplyGravity, [&context, &step]()
  224. {
  225. context.mPhysicsSystem->JobApplyGravity(&context, &step);
  226. JobHandle::sRemoveDependencies(step.mFindCollisions);
  227. }, num_step_listener_jobs > 0? num_step_listener_jobs : previous_step_dependency_count); // depends on: step listeners (or previous step if no step listeners)
  228. // This job will setup velocity constraints for non-collision constraints
  229. step.mSetupVelocityConstraints = inJobSystem->CreateJob("SetupVelocityConstraints", cColorSetupVelocityConstraints, [&context, &step]()
  230. {
  231. context.mPhysicsSystem->JobSetupVelocityConstraints(context.mSubStepDeltaTime, &step);
  232. JobHandle::sRemoveDependencies(step.mSubSteps[0].mSolveVelocityConstraints);
  233. }, num_determine_active_constraints_jobs + 1); // depends on: determine active constraints, finish building jobs
  234. // This job will build islands from constraints
  235. step.mBuildIslandsFromConstraints = inJobSystem->CreateJob("BuildIslandsFromConstraints", cColorBuildIslandsFromConstraints, [&context, &step]()
  236. {
  237. context.mPhysicsSystem->JobBuildIslandsFromConstraints(&context, &step);
  238. step.mFinalizeIslands.RemoveDependency();
  239. }, num_determine_active_constraints_jobs + 1); // depends on: determine active constraints, finish building jobs
  240. // This job determines active constraints
  241. step.mDetermineActiveConstraints.resize(num_determine_active_constraints_jobs);
  242. for (int i = 0; i < num_determine_active_constraints_jobs; ++i)
  243. step.mDetermineActiveConstraints[i] = inJobSystem->CreateJob("DetermineActiveConstraints", cColorDetermineActiveConstraints, [&context, &step]()
  244. {
  245. context.mPhysicsSystem->JobDetermineActiveConstraints(&step);
  246. step.mSetupVelocityConstraints.RemoveDependency();
  247. step.mBuildIslandsFromConstraints.RemoveDependency();
  248. // Kick find collisions last as they will use up all CPU cores leaving no space for the previous 2 jobs
  249. JobHandle::sRemoveDependencies(step.mFindCollisions);
  250. }, num_step_listener_jobs > 0? num_step_listener_jobs : previous_step_dependency_count); // depends on: step listeners (or previous step if no step listeners)
  251. // This job calls the step listeners
  252. step.mStepListeners.resize(num_step_listener_jobs);
  253. for (int i = 0; i < num_step_listener_jobs; ++i)
  254. step.mStepListeners[i] = inJobSystem->CreateJob("StepListeners", cColorStepListeners, [&context, &step]()
  255. {
  256. // Call the step listeners
  257. context.mPhysicsSystem->JobStepListeners(&step);
  258. // Kick apply gravity and determine active constraint jobs
  259. JobHandle::sRemoveDependencies(step.mApplyGravity);
  260. JobHandle::sRemoveDependencies(step.mDetermineActiveConstraints);
  261. }, previous_step_dependency_count);
  262. // Unblock the previous step
  263. if (!is_first_step)
  264. context.mSteps[step_idx - 1].mStartNextStep.RemoveDependency();
  265. // This job will finalize the simulation islands
  266. step.mFinalizeIslands = inJobSystem->CreateJob("FinalizeIslands", cColorFinalizeIslands, [&context, &step]()
  267. {
  268. // Validate that all find collision jobs have stopped
  269. JPH_ASSERT(step.mActiveFindCollisionJobs == 0);
  270. context.mPhysicsSystem->JobFinalizeIslands(&context);
  271. JobHandle::sRemoveDependencies(step.mSubSteps[0].mSolveVelocityConstraints);
  272. step.mBodySetIslandIndex.RemoveDependency();
  273. }, num_find_collisions_jobs + 2); // depends on: find collisions, build islands from constraints, finish building jobs
  274. // Unblock previous jobs (find collisions last since it will use up all CPU cores)
  275. step.mBuildIslandsFromConstraints.RemoveDependency();
  276. JobHandle::sRemoveDependencies(step.mFindCollisions);
  277. // This job will call the contact removed callbacks
  278. step.mContactRemovedCallbacks = inJobSystem->CreateJob("ContactRemovedCallbacks", cColorContactRemovedCallbacks, [&context, &step]()
  279. {
  280. context.mPhysicsSystem->JobContactRemovedCallbacks();
  281. if (step.mStartNextStep.IsValid())
  282. step.mStartNextStep.RemoveDependency();
  283. }, 1); // depends on the find ccd contacts of the last sub step
  284. // This job will set the island index on each body (only used for debug drawing purposes)
  285. // It will also delete any bodies that have been destroyed in the last frame
  286. step.mBodySetIslandIndex = inJobSystem->CreateJob("BodySetIslandIndex", cColorBodySetIslandIndex, [&context, &step]()
  287. {
  288. context.mPhysicsSystem->JobBodySetIslandIndex(&context);
  289. if (step.mStartNextStep.IsValid())
  290. step.mStartNextStep.RemoveDependency();
  291. }, 1); // depends on: finalize islands
  292. // Job to start the next collision step
  293. if (!is_last_step)
  294. {
  295. PhysicsUpdateContext::Step *next_step = &context.mSteps[step_idx + 1];
  296. step.mStartNextStep = inJobSystem->CreateJob("StartNextStep", cColorStartNextStep, [this, next_step]()
  297. {
  298. #ifdef _DEBUG
  299. // Validate that the cached bounds are correct
  300. mBodyManager.ValidateActiveBodyBounds();
  301. #endif // _DEBUG
  302. // Store the number of active bodies at the start of the step
  303. next_step->mNumActiveBodiesAtStepStart = mBodyManager.GetNumActiveBodies();
  304. // Clear the island builder
  305. TempAllocator *temp_allocator = next_step->mContext->mTempAllocator;
  306. mIslandBuilder.ResetIslands(temp_allocator);
  307. // Setup island builder
  308. mIslandBuilder.PrepareContactConstraints(mContactManager.GetMaxConstraints(), temp_allocator);
  309. // Restart the contact manager
  310. mContactManager.RecycleConstraintBuffer();
  311. // Kick the jobs of the next step (in the same order as the first step)
  312. next_step->mBroadPhasePrepare.RemoveDependency();
  313. if (next_step->mStepListeners.empty())
  314. {
  315. // Kick the gravity and active constraints jobs immediately
  316. JobHandle::sRemoveDependencies(next_step->mApplyGravity);
  317. JobHandle::sRemoveDependencies(next_step->mDetermineActiveConstraints);
  318. }
  319. else
  320. {
  321. // Kick the step listeners job first
  322. JobHandle::sRemoveDependencies(next_step->mStepListeners);
  323. }
  324. }, max_concurrency + 3); // depends on: solve position constraints of the last step, body set island index, contact removed callbacks, finish building the previous step
  325. }
  326. // Create solve jobs for each of the integration sub steps
  327. for (int sub_step_idx = 0; sub_step_idx < inIntegrationSubSteps; ++sub_step_idx)
  328. {
  329. bool is_first_sub_step = sub_step_idx == 0;
  330. bool is_last_sub_step = sub_step_idx == inIntegrationSubSteps - 1;
  331. PhysicsUpdateContext::SubStep &sub_step = step.mSubSteps[sub_step_idx];
  332. sub_step.mStep = &step;
  333. sub_step.mIsFirst = is_first_sub_step;
  334. sub_step.mIsLast = is_last_sub_step;
  335. sub_step.mIsLastOfAll = is_last_step && is_last_sub_step;
  336. // This job will solve the velocity constraints
  337. int num_dependencies_solve_velocity_constraints = is_first_sub_step? 3 : 2; // in first sub step depends on: finalize islands, setup velocity constraints, in later sub steps depends on: previous sub step finished. For both: finish building jobs.
  338. sub_step.mSolveVelocityConstraints.resize(max_concurrency);
  339. for (int i = 0; i < max_concurrency; ++i)
  340. sub_step.mSolveVelocityConstraints[i] = inJobSystem->CreateJob("SolveVelocityConstraints", cColorSolveVelocityConstraints, [&context, &sub_step]()
  341. {
  342. context.mPhysicsSystem->JobSolveVelocityConstraints(&context, &sub_step);
  343. sub_step.mIntegrateVelocity.RemoveDependency();
  344. }, num_dependencies_solve_velocity_constraints);
  345. // Unblock previous jobs
  346. if (is_first_sub_step)
  347. {
  348. step.mFinalizeIslands.RemoveDependency();
  349. step.mSetupVelocityConstraints.RemoveDependency();
  350. }
  351. else
  352. {
  353. step.mSubSteps[sub_step_idx - 1].mStartNextSubStep.RemoveDependency();
  354. }
  355. // This job will update the positions of all active bodies
  356. int num_dependencies_integrate_velocities = is_first_sub_step? 2 + max_concurrency : 1 + max_concurrency; // depends on: broadphase update finalize in first step, solve velocity constraints in all steps. For both: finish building jobs.
  357. sub_step.mIntegrateVelocity = inJobSystem->CreateJob("IntegrateVelocity", cColorIntegrateVelocity, [&context, &sub_step]()
  358. {
  359. context.mPhysicsSystem->JobIntegrateVelocity(&context, &sub_step);
  360. sub_step.mResolveCCDContacts.RemoveDependency();
  361. }, num_dependencies_integrate_velocities);
  362. // Unblock previous jobs
  363. if (is_first_sub_step)
  364. step.mUpdateBroadphaseFinalize.RemoveDependency();
  365. JobHandle::sRemoveDependencies(sub_step.mSolveVelocityConstraints);
  366. // This job will update the positions and velocities for all bodies that need continuous collision detection
  367. sub_step.mResolveCCDContacts = inJobSystem->CreateJob("ResolveCCDContacts", cColorResolveCCDContacts, [&context, &sub_step]()
  368. {
  369. context.mPhysicsSystem->JobResolveCCDContacts(&context, &sub_step);
  370. JobHandle::sRemoveDependencies(sub_step.mSolvePositionConstraints);
  371. }, 2); // depends on: integrate velocities, detect ccd contacts (added dynamically), finish building jobs.
  372. // Unblock previous job
  373. sub_step.mIntegrateVelocity.RemoveDependency();
  374. // Fixes up drift in positions and updates the broadphase with new body positions
  375. sub_step.mSolvePositionConstraints.resize(max_concurrency);
  376. for (int i = 0; i < max_concurrency; ++i)
  377. sub_step.mSolvePositionConstraints[i] = inJobSystem->CreateJob("SolvePositionConstraints", cColorSolvePositionConstraints, [&context, &sub_step]()
  378. {
  379. context.mPhysicsSystem->JobSolvePositionConstraints(&context, &sub_step);
  380. // Kick the next sub step
  381. if (sub_step.mStartNextSubStep.IsValid())
  382. sub_step.mStartNextSubStep.RemoveDependency();
  383. }, 2); // depends on: resolve ccd contacts, finish building jobs.
  384. // Unblock previous job.
  385. sub_step.mResolveCCDContacts.RemoveDependency();
  386. // This job starts the next sub step
  387. if (!is_last_sub_step)
  388. {
  389. PhysicsUpdateContext::SubStep &next_sub_step = step.mSubSteps[sub_step_idx + 1];
  390. sub_step.mStartNextSubStep = inJobSystem->CreateJob("StartNextSubStep", cColorStartNextSubStep, [&next_sub_step]()
  391. {
  392. // Kick velocity constraint solving for the next sub step
  393. JobHandle::sRemoveDependencies(next_sub_step.mSolveVelocityConstraints);
  394. }, max_concurrency + 1); // depends on: solve position constraints, finish building jobs.
  395. }
  396. else
  397. sub_step.mStartNextSubStep = step.mStartNextStep;
  398. // Unblock previous jobs
  399. JobHandle::sRemoveDependencies(sub_step.mSolvePositionConstraints);
  400. }
  401. }
  402. }
  403. // Build the list of jobs to wait for
  404. JobSystem::Barrier *barrier = context.mBarrier;
  405. {
  406. JPH_PROFILE("Build job barrier");
  407. StaticArray<JobHandle, cMaxPhysicsJobs> handles;
  408. for (PhysicsUpdateContext::Step &step : context.mSteps)
  409. {
  410. if (step.mBroadPhasePrepare.IsValid())
  411. handles.push_back(step.mBroadPhasePrepare);
  412. for (JobHandle &h : step.mStepListeners)
  413. handles.push_back(h);
  414. for (JobHandle &h : step.mDetermineActiveConstraints)
  415. handles.push_back(h);
  416. for (JobHandle &h : step.mApplyGravity)
  417. handles.push_back(h);
  418. for (JobHandle &h : step.mFindCollisions)
  419. handles.push_back(h);
  420. if (step.mUpdateBroadphaseFinalize.IsValid())
  421. handles.push_back(step.mUpdateBroadphaseFinalize);
  422. handles.push_back(step.mSetupVelocityConstraints);
  423. handles.push_back(step.mBuildIslandsFromConstraints);
  424. handles.push_back(step.mFinalizeIslands);
  425. handles.push_back(step.mBodySetIslandIndex);
  426. for (PhysicsUpdateContext::SubStep &sub_step : step.mSubSteps)
  427. {
  428. for (JobHandle &h : sub_step.mSolveVelocityConstraints)
  429. handles.push_back(h);
  430. handles.push_back(sub_step.mIntegrateVelocity);
  431. handles.push_back(sub_step.mResolveCCDContacts);
  432. for (JobHandle &h : sub_step.mSolvePositionConstraints)
  433. handles.push_back(h);
  434. if (sub_step.mStartNextSubStep.IsValid())
  435. handles.push_back(sub_step.mStartNextSubStep);
  436. }
  437. handles.push_back(step.mContactRemovedCallbacks);
  438. }
  439. barrier->AddJobs(handles.data(), handles.size());
  440. }
  441. // Wait until all jobs finish
  442. // Note we don't just wait for the last job. If we would and another job
  443. // would be scheduled in between there is the possibility of a deadlock.
  444. // The other job could try to e.g. add/remove a body which would try to
  445. // lock a body mutex while this thread has already locked the mutex
  446. inJobSystem->WaitForJobs(barrier);
  447. // We're done with the barrier for this update
  448. inJobSystem->DestroyBarrier(barrier);
  449. #ifdef _DEBUG
  450. // Validate that the cached bounds are correct
  451. mBodyManager.ValidateActiveBodyBounds();
  452. #endif // _DEBUG
  453. // Clear the island builder
  454. mIslandBuilder.ResetIslands(inTempAllocator);
  455. // Clear the contact manager
  456. mContactManager.FinishConstraintBuffer();
  457. // Free active constraints
  458. inTempAllocator->Free(context.mActiveConstraints, mConstraintManager.GetNumConstraints() * sizeof(Constraint *));
  459. context.mActiveConstraints = nullptr;
  460. // Free body pairs
  461. inTempAllocator->Free(context.mBodyPairs, sizeof(BodyPair) * mPhysicsSettings.mMaxInFlightBodyPairs);
  462. context.mBodyPairs = nullptr;
  463. // Unlock the broadphase
  464. mBroadPhase->UnlockModifications();
  465. // Unlock all constraints
  466. mConstraintManager.UnlockAllConstraints();
  467. #ifdef JPH_ENABLE_ASSERTS
  468. // Allow write operations to the active bodies list
  469. mBodyManager.SetActiveBodiesLocked(false);
  470. #endif
  471. // Unlock all bodies
  472. mBodyManager.UnlockAllBodies();
  473. // Unlock step listeners
  474. mStepListenersMutex.unlock();
  475. }
  476. void PhysicsSystem::JobStepListeners(PhysicsUpdateContext::Step *ioStep)
  477. {
  478. #ifdef JPH_ENABLE_ASSERTS
  479. // Read positions (broadphase updates concurrently so we can't write), read/write velocities
  480. BodyAccess::Grant grant(BodyAccess::EAccess::ReadWrite, BodyAccess::EAccess::Read);
  481. // Can activate bodies only (we cache the amount of active bodies at the beginning of the step in mNumActiveBodiesAtStepStart so we cannot deactivate here)
  482. BodyManager::GrantActiveBodiesAccess grant_active(true, false);
  483. #endif
  484. float step_time = ioStep->mContext->mStepDeltaTime;
  485. uint32 batch_size = mPhysicsSettings.mStepListenersBatchSize;
  486. for (;;)
  487. {
  488. // Get the start of a new batch
  489. uint32 batch = ioStep->mStepListenerReadIdx.fetch_add(batch_size);
  490. if (batch >= mStepListeners.size())
  491. break;
  492. // Call the listeners
  493. for (uint32 i = batch, i_end = min((uint32)mStepListeners.size(), batch + batch_size); i < i_end; ++i)
  494. mStepListeners[i]->OnStep(step_time, *this);
  495. }
  496. }
  497. void PhysicsSystem::JobDetermineActiveConstraints(PhysicsUpdateContext::Step *ioStep)
  498. {
  499. #ifdef JPH_ENABLE_ASSERTS
  500. // No body access
  501. BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::None);
  502. #endif
  503. uint32 num_constraints = mConstraintManager.GetNumConstraints();
  504. uint32 num_active_constraints;
  505. Constraint **active_constraints = (Constraint **)JPH_STACK_ALLOC(cDetermineActiveConstraintsBatchSize * sizeof(Constraint *));
  506. for (;;)
  507. {
  508. // Atomically fetch a batch of constraints
  509. uint32 constraint_idx = ioStep->mConstraintReadIdx.fetch_add(cDetermineActiveConstraintsBatchSize);
  510. if (constraint_idx >= num_constraints)
  511. break;
  512. // Calculate the end of the batch
  513. uint32 constraint_idx_end = min(num_constraints, constraint_idx + cDetermineActiveConstraintsBatchSize);
  514. // Store the active constraints at the start of the step (bodies get activated during the step which in turn may activate constraints leading to an inconsistent shapshot)
  515. mConstraintManager.GetActiveConstraints(constraint_idx, constraint_idx_end, active_constraints, num_active_constraints);
  516. // Copy the block of active constraints to the global list of active constraints
  517. if (num_active_constraints > 0)
  518. {
  519. uint32 active_constraint_idx = ioStep->mNumActiveConstraints.fetch_add(num_active_constraints);
  520. memcpy(ioStep->mContext->mActiveConstraints + active_constraint_idx, active_constraints, num_active_constraints * sizeof(Constraint *));
  521. }
  522. }
  523. }
  524. void PhysicsSystem::JobApplyGravity(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep)
  525. {
  526. #ifdef JPH_ENABLE_ASSERTS
  527. // We update velocities and need the rotation to do so
  528. BodyAccess::Grant grant(BodyAccess::EAccess::ReadWrite, BodyAccess::EAccess::Read);
  529. #endif
  530. // Get list of active bodies that we had at the start of the physics update.
  531. // Any body that is activated as part of the simulation step does not receive gravity this frame.
  532. // Note that bodies may be activated during this job but not deactivated, this means that only elements
  533. // will be added to the array. Since the array is made to not reallocate, this is a safe operation.
  534. const BodyID *active_bodies = mBodyManager.GetActiveBodiesUnsafe();
  535. uint32 num_active_bodies_at_step_start = ioStep->mNumActiveBodiesAtStepStart;
  536. // Fetch delta time once outside the loop
  537. float delta_time = ioContext->mSubStepDeltaTime;
  538. // Update velocities from forces
  539. for (;;)
  540. {
  541. // Atomically fetch a batch of bodies
  542. uint32 active_body_idx = ioStep->mApplyGravityReadIdx.fetch_add(cApplyGravityBatchSize);
  543. if (active_body_idx >= num_active_bodies_at_step_start)
  544. break;
  545. // Calculate the end of the batch
  546. uint32 active_body_idx_end = min(num_active_bodies_at_step_start, active_body_idx + cApplyGravityBatchSize);
  547. // Process the batch
  548. while (active_body_idx < active_body_idx_end)
  549. {
  550. Body &body = mBodyManager.GetBody(active_bodies[active_body_idx]);
  551. if (body.IsDynamic())
  552. body.GetMotionProperties()->ApplyForceTorqueAndDragInternal(body.GetRotation(), mGravity, delta_time);
  553. active_body_idx++;
  554. }
  555. }
  556. }
  557. void PhysicsSystem::JobSetupVelocityConstraints(float inDeltaTime, PhysicsUpdateContext::Step *ioStep)
  558. {
  559. #ifdef JPH_ENABLE_ASSERTS
  560. // We only read positions
  561. BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::Read);
  562. #endif
  563. mConstraintManager.SetupVelocityConstraints(ioStep->mContext->mActiveConstraints, ioStep->mNumActiveConstraints, inDeltaTime);
  564. }
  565. void PhysicsSystem::JobBuildIslandsFromConstraints(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::Step *ioStep)
  566. {
  567. #ifdef JPH_ENABLE_ASSERTS
  568. // We read constraints and positions
  569. BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::Read);
  570. // Can only activate bodies
  571. BodyManager::GrantActiveBodiesAccess grant_active(true, false);
  572. #endif
  573. // Prepare the island builder
  574. mIslandBuilder.PrepareNonContactConstraints(ioStep->mNumActiveConstraints, ioContext->mTempAllocator);
  575. // Build the islands
  576. mConstraintManager.BuildIslands(ioStep->mContext->mActiveConstraints, ioStep->mNumActiveConstraints, mIslandBuilder, mBodyManager);
  577. }
  578. void PhysicsSystem::TrySpawnJobFindCollisions(PhysicsUpdateContext::Step *ioStep)
  579. {
  580. // Get how many jobs we can spawn and check if we can spawn more
  581. uint max_jobs = ioStep->mBodyPairQueues.size();
  582. if (CountBits(ioStep->mActiveFindCollisionJobs) >= max_jobs)
  583. return;
  584. // Count how many body pairs we have waiting
  585. uint32 num_body_pairs = 0;
  586. for (const PhysicsUpdateContext::BodyPairQueue &queue : ioStep->mBodyPairQueues)
  587. num_body_pairs += queue.mWriteIdx - queue.mReadIdx;
  588. // Count how many active bodies we have waiting
  589. uint32 num_active_bodies = mBodyManager.GetNumActiveBodies() - ioStep->mActiveBodyReadIdx;
  590. // Calculate how many jobs we would like
  591. uint desired_num_jobs = min((num_body_pairs + cNarrowPhaseBatchSize - 1) / cNarrowPhaseBatchSize + (num_active_bodies + cActiveBodiesBatchSize - 1) / cActiveBodiesBatchSize, max_jobs);
  592. for (;;)
  593. {
  594. // Get the bit mask of active jobs and see if we can spawn more
  595. PhysicsUpdateContext::JobMask current_active_jobs = ioStep->mActiveFindCollisionJobs;
  596. if (CountBits(current_active_jobs) >= desired_num_jobs)
  597. break;
  598. // Loop through all possible job indices
  599. for (uint job_index = 0; job_index < max_jobs; ++job_index)
  600. {
  601. // Test if it has been started
  602. PhysicsUpdateContext::JobMask job_mask = PhysicsUpdateContext::JobMask(1) << job_index;
  603. if ((current_active_jobs & job_mask) == 0)
  604. {
  605. // Try to claim the job index
  606. PhysicsUpdateContext::JobMask prev_value = ioStep->mActiveFindCollisionJobs.fetch_or(job_mask);
  607. if ((prev_value & job_mask) == 0)
  608. {
  609. // Add dependencies from the find collisions job to the next jobs
  610. ioStep->mUpdateBroadphaseFinalize.AddDependency();
  611. ioStep->mFinalizeIslands.AddDependency();
  612. // Start the job
  613. JobHandle job = ioStep->mContext->mJobSystem->CreateJob("FindCollisions", cColorFindCollisions, [step = ioStep, job_index]()
  614. {
  615. step->mContext->mPhysicsSystem->JobFindCollisions(step, job_index);
  616. });
  617. // Add the job to the job barrier so the main updating thread can execute the job too
  618. ioStep->mContext->mBarrier->AddJob(job);
  619. // Spawn only 1 extra job at a time
  620. return;
  621. }
  622. }
  623. }
  624. }
  625. }
  626. void PhysicsSystem::JobFindCollisions(PhysicsUpdateContext::Step *ioStep, int inJobIndex)
  627. {
  628. #ifdef JPH_ENABLE_ASSERTS
  629. // We only read positions
  630. BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::Read);
  631. #endif
  632. // Determine initial queue to read pairs from if no broadphase work can be done
  633. // (always start looking at results from the next job)
  634. int read_queue_idx = (inJobIndex + 1) % ioStep->mBodyPairQueues.size();
  635. for (;;)
  636. {
  637. // Check if there are active bodies to be processed
  638. uint32 active_bodies_read_idx = ioStep->mActiveBodyReadIdx;
  639. uint32 num_active_bodies = mBodyManager.GetNumActiveBodies();
  640. if (active_bodies_read_idx < num_active_bodies)
  641. {
  642. // Take a batch of active bodies
  643. uint32 active_bodies_read_idx_end = min(num_active_bodies, active_bodies_read_idx + cActiveBodiesBatchSize);
  644. if (ioStep->mActiveBodyReadIdx.compare_exchange_strong(active_bodies_read_idx, active_bodies_read_idx_end))
  645. {
  646. // Callback when a new body pair is found
  647. class MyBodyPairCallback : public BodyPairCollector
  648. {
  649. public:
  650. // Constructor
  651. MyBodyPairCallback(PhysicsUpdateContext::Step *inStep, int inJobIndex) :
  652. mStep(inStep),
  653. mJobIndex(inJobIndex)
  654. {
  655. }
  656. // Callback function when a body pair is found
  657. virtual void AddHit(const BodyPair &inPair) override
  658. {
  659. // Check if we have space in our write queue
  660. PhysicsUpdateContext::BodyPairQueue &queue = mStep->mBodyPairQueues[mJobIndex];
  661. uint32 body_pairs_in_queue = queue.mWriteIdx - queue.mReadIdx;
  662. if (body_pairs_in_queue >= mStep->mMaxBodyPairsPerQueue)
  663. {
  664. // Buffer full, process the pair now
  665. mStep->mContext->mPhysicsSystem->ProcessBodyPair(inPair);
  666. }
  667. else
  668. {
  669. // Store the pair in our own queue
  670. mStep->mContext->mBodyPairs[mJobIndex * mStep->mMaxBodyPairsPerQueue + queue.mWriteIdx % mStep->mMaxBodyPairsPerQueue] = inPair;
  671. ++queue.mWriteIdx;
  672. }
  673. }
  674. private:
  675. PhysicsUpdateContext::Step * mStep;
  676. int mJobIndex;
  677. };
  678. MyBodyPairCallback add_pair(ioStep, inJobIndex);
  679. // Copy active bodies to temporary array, broadphase will reorder them
  680. uint32 batch_size = active_bodies_read_idx_end - active_bodies_read_idx;
  681. BodyID *active_bodies = (BodyID *)JPH_STACK_ALLOC(batch_size * sizeof(BodyID));
  682. memcpy(active_bodies, mBodyManager.GetActiveBodiesUnsafe() + active_bodies_read_idx, batch_size * sizeof(BodyID));
  683. // Find pairs in the broadphase
  684. mBroadPhase->FindCollidingPairs(active_bodies, batch_size, mPhysicsSettings.mSpeculativeContactDistance, mBroadPhaseLayerPairFilter, mObjectLayerPairFilter, add_pair);
  685. // Check if we have enough pairs in the buffer to start a new job
  686. PhysicsUpdateContext::BodyPairQueue &queue = ioStep->mBodyPairQueues[inJobIndex];
  687. uint32 body_pairs_in_queue = queue.mWriteIdx - queue.mReadIdx;
  688. if (body_pairs_in_queue >= cNarrowPhaseBatchSize)
  689. TrySpawnJobFindCollisions(ioStep);
  690. }
  691. }
  692. else
  693. {
  694. // Lockless loop to get the next body pair from the pairs buffer
  695. PhysicsUpdateContext *context = ioStep->mContext;
  696. int first_read_queue_idx = read_queue_idx;
  697. for (;;)
  698. {
  699. PhysicsUpdateContext::BodyPairQueue &queue = ioStep->mBodyPairQueues[read_queue_idx];
  700. // Get the next pair to process
  701. uint32 pair_idx = queue.mReadIdx;
  702. // If the pair hasn't been written yet
  703. if (pair_idx >= queue.mWriteIdx)
  704. {
  705. // Go to the next queue
  706. read_queue_idx = (read_queue_idx + 1) % ioStep->mBodyPairQueues.size();
  707. // If we're back at the first queue, we've looked at all of them and found nothing
  708. if (read_queue_idx == first_read_queue_idx)
  709. {
  710. // Mark this job as inactive
  711. ioStep->mActiveFindCollisionJobs.fetch_and(~PhysicsUpdateContext::JobMask(1 << inJobIndex));
  712. // Trigger the next jobs
  713. ioStep->mUpdateBroadphaseFinalize.RemoveDependency();
  714. ioStep->mFinalizeIslands.RemoveDependency();
  715. return;
  716. }
  717. // Try again reading from the next queue
  718. continue;
  719. }
  720. // Copy the body pair out of the buffer
  721. BodyPair bp = context->mBodyPairs[read_queue_idx * ioStep->mMaxBodyPairsPerQueue + pair_idx % ioStep->mMaxBodyPairsPerQueue];
  722. // Mark this pair as taken
  723. if (queue.mReadIdx.compare_exchange_strong(pair_idx, pair_idx + 1))
  724. {
  725. // Process the actual body pair
  726. ProcessBodyPair(bp);
  727. break;
  728. }
  729. }
  730. }
  731. }
  732. }
  733. void PhysicsSystem::ProcessBodyPair(const BodyPair &inBodyPair)
  734. {
  735. JPH_PROFILE_FUNCTION();
  736. #ifdef JPH_ENABLE_ASSERTS
  737. // We read positions and read velocities (for elastic collisions)
  738. BodyAccess::Grant grant(BodyAccess::EAccess::Read, BodyAccess::EAccess::Read);
  739. // Can only activate bodies
  740. BodyManager::GrantActiveBodiesAccess grant_active(true, false);
  741. #endif
  742. // Fetch body pair
  743. Body *body1 = &mBodyManager.GetBody(inBodyPair.mBodyA);
  744. Body *body2 = &mBodyManager.GetBody(inBodyPair.mBodyB);
  745. JPH_ASSERT(body1->IsActive());
  746. // Ensure that body1 is dynamic, this ensures that we do the collision detection in the space of a moving body, which avoids accuracy problems when testing a very large static object against a small dynamic object
  747. // Ensure that body1 id < body2 id for dynamic vs dynamic
  748. if (!body1->IsDynamic() || (body2->IsDynamic() && inBodyPair.mBodyB < inBodyPair.mBodyA))
  749. swap(body1, body2);
  750. JPH_ASSERT(body1->IsDynamic());
  751. // Check if the contact points from the previous frame are reusable and if so copy them
  752. bool pair_handled = false, contact_found = false;
  753. if (mPhysicsSettings.mUseBodyPairContactCache && !(body1->IsCollisionCacheInvalid() || body2->IsCollisionCacheInvalid()))
  754. mContactManager.GetContactsFromCache(*body1, *body2, pair_handled, contact_found);
  755. // If the cache hasn't handled this body pair do actual collision detection
  756. if (!pair_handled)
  757. {
  758. // Create entry in the cache for this body pair
  759. // Needs to happen irrespective if we found a collision or not (we want to remember that no collision was found too)
  760. ContactConstraintManager::BodyPairHandle body_pair_handle = mContactManager.AddBodyPair(*body1, *body2);
  761. if (body_pair_handle == nullptr)
  762. return; // Out of cache space
  763. // Create the query settings
  764. CollideShapeSettings settings;
  765. settings.mCollectFacesMode = ECollectFacesMode::CollectFaces;
  766. settings.mActiveEdgeMode = mPhysicsSettings.mCheckActiveEdges? EActiveEdgeMode::CollideOnlyWithActive : EActiveEdgeMode::CollideWithAll;
  767. settings.mMaxSeparationDistance = mPhysicsSettings.mSpeculativeContactDistance;
  768. settings.mActiveEdgeMovementDirection = body1->GetLinearVelocity() - body2->GetLinearVelocity();
  769. if (mPhysicsSettings.mUseManifoldReduction)
  770. {
  771. // Version WITH contact manifold reduction
  772. class MyManifold : public ContactManifold
  773. {
  774. public:
  775. Vec3 mFirstWorldSpaceNormal;
  776. };
  777. // A temporary structure that allows us to keep track of the all manifolds between this body pair
  778. using Manifolds = StaticArray<MyManifold, 32>;
  779. // Create collector
  780. class ReductionCollideShapeCollector : public CollideShapeCollector
  781. {
  782. public:
  783. ReductionCollideShapeCollector(PhysicsSystem *inSystem, const Body *inBody1, const Body *inBody2) :
  784. mSystem(inSystem),
  785. mBody1(inBody1),
  786. mBody2(inBody2)
  787. {
  788. }
  789. virtual void AddHit(const CollideShapeResult &inResult) override
  790. {
  791. // Body 1 should always be dynamic, body 2 may be static / kinematic
  792. JPH_ASSERT(mBody1->IsDynamic());
  793. JPH_ASSERT(!ShouldEarlyOut());
  794. // Test if we want to accept this hit
  795. if (mValidateBodyPair)
  796. {
  797. switch (mSystem->mContactManager.ValidateContactPoint(*mBody1, *mBody2, inResult))
  798. {
  799. case ValidateResult::AcceptContact:
  800. // We're just accepting this one, nothing to do
  801. break;
  802. case ValidateResult::AcceptAllContactsForThisBodyPair:
  803. // Accept and stop calling the validate callback
  804. mValidateBodyPair = false;
  805. break;
  806. case ValidateResult::RejectContact:
  807. // Skip this contact
  808. return;
  809. case ValidateResult::RejectAllContactsForThisBodyPair:
  810. // Skip this and early out
  811. ForceEarlyOut();
  812. return;
  813. }
  814. }
  815. // Count amount of manifolds before reduction
  816. JPH_IF_STAT_COLLECTOR(++mSystem->mManifoldsBeforeReduction;)
  817. // Calculate normal
  818. Vec3 world_space_normal = inResult.mPenetrationAxis.Normalized();
  819. // Check if we can add it to an existing manifold
  820. Manifolds::iterator manifold;
  821. float contact_normal_cos_max_delta_rot = mSystem->mPhysicsSettings.mContactNormalCosMaxDeltaRotation;
  822. for (manifold = mManifolds.begin(); manifold != mManifolds.end(); ++manifold)
  823. if (world_space_normal.Dot(manifold->mFirstWorldSpaceNormal) >= contact_normal_cos_max_delta_rot)
  824. {
  825. // Update average normal
  826. manifold->mWorldSpaceNormal += world_space_normal;
  827. manifold->mPenetrationDepth = max(manifold->mPenetrationDepth, inResult.mPenetrationDepth);
  828. break;
  829. }
  830. if (manifold == mManifolds.end())
  831. {
  832. // Check if array is full
  833. if (mManifolds.size() == mManifolds.capacity())
  834. {
  835. // Full, find manifold with least amount of penetration
  836. manifold = mManifolds.begin();
  837. for (Manifolds::iterator m = mManifolds.begin() + 1; m < mManifolds.end(); ++m)
  838. if (m->mPenetrationDepth < manifold->mPenetrationDepth)
  839. manifold = m;
  840. // If this contacts penetration is smaller than the smallest manifold, we skip this contact
  841. if (inResult.mPenetrationDepth < manifold->mPenetrationDepth)
  842. return;
  843. // Replace the manifold
  844. *manifold = { { world_space_normal, inResult.mPenetrationDepth, inResult.mSubShapeID1, inResult.mSubShapeID2, { }, { } }, world_space_normal };
  845. }
  846. else
  847. {
  848. // Not full, create new manifold
  849. mManifolds.push_back({ { world_space_normal, inResult.mPenetrationDepth, inResult.mSubShapeID1, inResult.mSubShapeID2, { }, { } }, world_space_normal });
  850. manifold = mManifolds.end() - 1;
  851. // Count manifolds after reduction
  852. JPH_IF_STAT_COLLECTOR(mSystem->mManifoldsAfterReduction++;)
  853. }
  854. }
  855. // Determine contact points
  856. const PhysicsSettings &settings = mSystem->mPhysicsSettings;
  857. ManifoldBetweenTwoFaces(inResult.mContactPointOn1, inResult.mContactPointOn2, inResult.mPenetrationAxis, Square(settings.mSpeculativeContactDistance) + settings.mManifoldToleranceSq, inResult.mShape1Face, inResult.mShape2Face, manifold->mWorldSpaceContactPointsOn1, manifold->mWorldSpaceContactPointsOn2);
  858. // Prune if we have more than 32 points (this means we could run out of space in the next iteration)
  859. if (manifold->mWorldSpaceContactPointsOn1.size() > 32)
  860. PruneContactPoints(mBody1->GetCenterOfMassPosition(), manifold->mFirstWorldSpaceNormal, manifold->mWorldSpaceContactPointsOn1, manifold->mWorldSpaceContactPointsOn2);
  861. }
  862. PhysicsSystem * mSystem;
  863. const Body * mBody1;
  864. const Body * mBody2;
  865. bool mValidateBodyPair = true;
  866. Manifolds mManifolds;
  867. };
  868. ReductionCollideShapeCollector collector(this, body1, body2);
  869. // Perform collision detection between the two shapes
  870. SubShapeIDCreator part1, part2;
  871. CollisionDispatch::sCollideShapeVsShape(body1->GetShape(), body2->GetShape(), Vec3::sReplicate(1.0f), Vec3::sReplicate(1.0f), body1->GetCenterOfMassTransform(), body2->GetCenterOfMassTransform(), part1, part2, settings, collector);
  872. // Add the contacts
  873. for (ContactManifold &manifold : collector.mManifolds)
  874. {
  875. // Normalize the normal (is a sum of all normals from merged manifolds)
  876. manifold.mWorldSpaceNormal = manifold.mWorldSpaceNormal.Normalized();
  877. // If we still have too many points, prune them now
  878. if (manifold.mWorldSpaceContactPointsOn1.size() > 4)
  879. PruneContactPoints(body1->GetCenterOfMassPosition(), manifold.mWorldSpaceNormal, manifold.mWorldSpaceContactPointsOn1, manifold.mWorldSpaceContactPointsOn2);
  880. // Actually add the contact points to the manager
  881. mContactManager.AddContactConstraint(body_pair_handle, *body1, *body2, manifold);
  882. }
  883. contact_found = !collector.mManifolds.empty();
  884. }
  885. else
  886. {
  887. // Version WITHOUT contact manifold reduction
  888. // Create collector
  889. class NonReductionCollideShapeCollector : public CollideShapeCollector
  890. {
  891. public:
  892. NonReductionCollideShapeCollector(PhysicsSystem *inSystem, Body *inBody1, Body *inBody2, const ContactConstraintManager::BodyPairHandle &inPairHandle) :
  893. mSystem(inSystem),
  894. mBody1(inBody1),
  895. mBody2(inBody2),
  896. mBodyPairHandle(inPairHandle)
  897. {
  898. }
  899. virtual void AddHit(const CollideShapeResult &inResult) override
  900. {
  901. // Body 1 should always be dynamic, body 2 may be static / kinematic
  902. JPH_ASSERT(mBody1->IsDynamic());
  903. JPH_ASSERT(!ShouldEarlyOut());
  904. // Test if we want to accept this hit
  905. if (mValidateBodyPair)
  906. {
  907. switch (mSystem->mContactManager.ValidateContactPoint(*mBody1, *mBody2, inResult))
  908. {
  909. case ValidateResult::AcceptContact:
  910. // We're just accepting this one, nothing to do
  911. break;
  912. case ValidateResult::AcceptAllContactsForThisBodyPair:
  913. // Accept and stop calling the validate callback
  914. mValidateBodyPair = false;
  915. break;
  916. case ValidateResult::RejectContact:
  917. // Skip this contact
  918. return;
  919. case ValidateResult::RejectAllContactsForThisBodyPair:
  920. // Skip this and early out
  921. ForceEarlyOut();
  922. return;
  923. }
  924. }
  925. // Determine contact points
  926. ContactManifold manifold;
  927. const PhysicsSettings &settings = mSystem->mPhysicsSettings;
  928. ManifoldBetweenTwoFaces(inResult.mContactPointOn1, inResult.mContactPointOn2, inResult.mPenetrationAxis, Square(settings.mSpeculativeContactDistance) + settings.mManifoldToleranceSq, inResult.mShape1Face, inResult.mShape2Face, manifold.mWorldSpaceContactPointsOn1, manifold.mWorldSpaceContactPointsOn2);
  929. // Calculate normal
  930. manifold.mWorldSpaceNormal = inResult.mPenetrationAxis.Normalized();
  931. // Store penetration depth
  932. manifold.mPenetrationDepth = inResult.mPenetrationDepth;
  933. // Prune if we have more than 4 points
  934. if (manifold.mWorldSpaceContactPointsOn1.size() > 4)
  935. PruneContactPoints(mBody1->GetCenterOfMassPosition(), manifold.mWorldSpaceNormal, manifold.mWorldSpaceContactPointsOn1, manifold.mWorldSpaceContactPointsOn2);
  936. // Set other properties
  937. manifold.mSubShapeID1 = inResult.mSubShapeID1;
  938. manifold.mSubShapeID2 = inResult.mSubShapeID2;
  939. // Actually add the contact points to the manager
  940. mSystem->mContactManager.AddContactConstraint(mBodyPairHandle, *mBody1, *mBody2, manifold);
  941. // Mark contact found
  942. mContactFound = true;
  943. }
  944. PhysicsSystem * mSystem;
  945. Body * mBody1;
  946. Body * mBody2;
  947. ContactConstraintManager::BodyPairHandle mBodyPairHandle;
  948. bool mValidateBodyPair = true;
  949. bool mContactFound = false;
  950. };
  951. NonReductionCollideShapeCollector collector(this, body1, body2, body_pair_handle);
  952. // Perform collision detection between the two shapes
  953. SubShapeIDCreator part1, part2;
  954. CollisionDispatch::sCollideShapeVsShape(body1->GetShape(), body2->GetShape(), Vec3::sReplicate(1.0f), Vec3::sReplicate(1.0f), body1->GetCenterOfMassTransform(), body2->GetCenterOfMassTransform(), part1, part2, settings, collector);
  955. contact_found = collector.mContactFound;
  956. }
  957. }
  958. // If an actual contact is present we need to do some extra work
  959. if (contact_found)
  960. {
  961. // Wake up sleeping bodies
  962. BodyID body_ids[2];
  963. int num_bodies = 0;
  964. if (body1->IsDynamic() && !body1->IsActive())
  965. body_ids[num_bodies++] = body1->GetID();
  966. if (body2->IsDynamic() && !body2->IsActive())
  967. body_ids[num_bodies++] = body2->GetID();
  968. if (num_bodies > 0)
  969. mBodyManager.ActivateBodies(body_ids, num_bodies);
  970. // Link the two bodies
  971. mIslandBuilder.LinkBodies(body1->GetIndexInActiveBodiesInternal(), body2->GetIndexInActiveBodiesInternal());
  972. }
  973. }
  974. void PhysicsSystem::JobFinalizeIslands(PhysicsUpdateContext *ioContext)
  975. {
  976. #ifdef JPH_ENABLE_ASSERTS
  977. // We only touch island data
  978. BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::None);
  979. #endif
  980. // Finish collecting the islands, at this point the active body list doesn't change so it's safe to access
  981. mIslandBuilder.Finalize(mBodyManager.GetActiveBodiesUnsafe(), mBodyManager.GetNumActiveBodies(), mContactManager.GetNumConstraints(), ioContext->mTempAllocator);
  982. }
  983. void PhysicsSystem::JobBodySetIslandIndex(PhysicsUpdateContext *ioContext)
  984. {
  985. #ifdef JPH_ENABLE_ASSERTS
  986. // We only touch island data
  987. BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::None);
  988. #endif
  989. // Loop through the result and tag all bodies with an island index
  990. for (uint32 island_idx = 0, n = mIslandBuilder.GetNumIslands(); island_idx < n; ++island_idx)
  991. {
  992. BodyID *body_start, *body_end;
  993. mIslandBuilder.GetBodiesInIsland(island_idx, body_start, body_end);
  994. for (const BodyID *body = body_start; body < body_end; ++body)
  995. mBodyManager.GetBody(*body).GetMotionProperties()->SetIslandIndexInternal(island_idx);
  996. }
  997. }
  998. void PhysicsSystem::JobSolveVelocityConstraints(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::SubStep *ioSubStep)
  999. {
  1000. #ifdef JPH_ENABLE_ASSERTS
  1001. // We update velocities and need to read positions to do so
  1002. BodyAccess::Grant grant(BodyAccess::EAccess::ReadWrite, BodyAccess::EAccess::Read);
  1003. #endif
  1004. float delta_time = ioContext->mSubStepDeltaTime;
  1005. float warm_start_impulse_ratio = ioContext->mWarmStartImpulseRatio;
  1006. Constraint **active_constraints = ioContext->mActiveConstraints;
  1007. bool first_sub_step = ioSubStep->mIsFirst;
  1008. bool last_sub_step = ioSubStep->mIsLast;
  1009. for (;;)
  1010. {
  1011. // Next island
  1012. uint32 island_idx = ioSubStep->mSolveVelocityConstraintsNextIsland++;
  1013. if (island_idx >= mIslandBuilder.GetNumIslands())
  1014. break;
  1015. JPH_PROFILE("Island");
  1016. // Get iterators
  1017. uint32 *constraints_begin, *constraints_end;
  1018. bool has_constraints = mIslandBuilder.GetConstraintsInIsland(island_idx, constraints_begin, constraints_end);
  1019. uint32 *contacts_begin, *contacts_end;
  1020. bool has_contacts = mIslandBuilder.GetContactsInIsland(island_idx, contacts_begin, contacts_end);
  1021. if (first_sub_step)
  1022. {
  1023. // If we don't have any contacts or constraints, we know that none of the following islands have any contacts or constraints
  1024. // (because they're sorted by most constraints first). This means we're done.
  1025. if (!has_contacts && !has_constraints)
  1026. {
  1027. #ifdef JPH_ENABLE_ASSERTS
  1028. // Validate our assumption that the next islands don't have any constraints or contacts
  1029. for (; island_idx < mIslandBuilder.GetNumIslands(); ++island_idx)
  1030. {
  1031. JPH_ASSERT(!mIslandBuilder.GetConstraintsInIsland(island_idx, constraints_begin, constraints_end));
  1032. JPH_ASSERT(!mIslandBuilder.GetContactsInIsland(island_idx, contacts_begin, contacts_end));
  1033. }
  1034. #endif // JPH_ENABLE_ASSERTS
  1035. return;
  1036. }
  1037. // Sort constraints to give a deterministic simulation
  1038. mConstraintManager.SortConstraints(active_constraints, constraints_begin, constraints_end);
  1039. // Sort contacts to give a deterministic simulation
  1040. mContactManager.SortContacts(contacts_begin, contacts_end);
  1041. }
  1042. else
  1043. {
  1044. {
  1045. JPH_PROFILE("Apply Gravity");
  1046. // Get bodies in this island
  1047. BodyID *bodies_begin, *bodies_end;
  1048. mIslandBuilder.GetBodiesInIsland(island_idx, bodies_begin, bodies_end);
  1049. // Apply gravity. In the first step this is done in a separate job.
  1050. for (const BodyID *body_id = bodies_begin; body_id < bodies_end; ++body_id)
  1051. {
  1052. Body &body = mBodyManager.GetBody(*body_id);
  1053. if (body.IsDynamic())
  1054. body.GetMotionProperties()->ApplyForceTorqueAndDragInternal(body.GetRotation(), mGravity, delta_time);
  1055. }
  1056. }
  1057. // If we don't have any contacts or constraints, we don't need to run the solver, but we do need to process
  1058. // the next island in order to apply gravity
  1059. if (!has_contacts && !has_constraints)
  1060. continue;
  1061. // Prepare velocity constraints. In the first step this is done when adding the contact constraints.
  1062. mConstraintManager.SetupVelocityConstraints(active_constraints, constraints_begin, constraints_end, delta_time);
  1063. mContactManager.SetupVelocityConstraints(contacts_begin, contacts_end, delta_time);
  1064. }
  1065. // Warm start
  1066. mConstraintManager.WarmStartVelocityConstraints(active_constraints, constraints_begin, constraints_end, warm_start_impulse_ratio);
  1067. mContactManager.WarmStartVelocityConstraints(contacts_begin, contacts_end, warm_start_impulse_ratio);
  1068. // Solve
  1069. for (int velocity_step = 0; velocity_step < mPhysicsSettings.mNumVelocitySteps; ++velocity_step)
  1070. {
  1071. bool constraint_impulse = mConstraintManager.SolveVelocityConstraints(active_constraints, constraints_begin, constraints_end, delta_time);
  1072. bool contact_impulse = mContactManager.SolveVelocityConstraints(contacts_begin, contacts_end);
  1073. if (!constraint_impulse && !contact_impulse)
  1074. break;
  1075. }
  1076. // Save back the lambdas in the contact cache for the warm start of the next physics update
  1077. if (last_sub_step)
  1078. mContactManager.StoreAppliedImpulses(contacts_begin, contacts_end);
  1079. }
  1080. }
  1081. void PhysicsSystem::JobIntegrateVelocity(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::SubStep *ioSubStep)
  1082. {
  1083. #ifdef JPH_ENABLE_ASSERTS
  1084. // We update positions and need velocity to do so, we also clamp velocities so need to write to them
  1085. BodyAccess::Grant grant(BodyAccess::EAccess::ReadWrite, BodyAccess::EAccess::ReadWrite);
  1086. #endif
  1087. float delta_time = ioContext->mSubStepDeltaTime;
  1088. uint32 num_active_bodies_after_find_collisions = ioSubStep->mStep->mActiveBodyReadIdx;
  1089. // Reserve enough space for all bodies that may need a cast
  1090. TempAllocator *temp_allocator = ioContext->mTempAllocator;
  1091. JPH_ASSERT(ioSubStep->mCCDBodies == nullptr);
  1092. ioSubStep->mCCDBodiesCapacity = mBodyManager.GetNumActiveCCDBodies();
  1093. ioSubStep->mCCDBodies = (CCDBody *)temp_allocator->Allocate(ioSubStep->mCCDBodiesCapacity * sizeof(CCDBody));
  1094. // Initialize the mapping table between active body and CCD body
  1095. const BodyID *active_bodies = mBodyManager.GetActiveBodiesUnsafe();
  1096. uint32 num_active_bodies = mBodyManager.GetNumActiveBodies();
  1097. JPH_ASSERT(ioSubStep->mActiveBodyToCCDBody == nullptr);
  1098. ioSubStep->mActiveBodyToCCDBody = (int *)temp_allocator->Allocate(num_active_bodies * sizeof(int));
  1099. // We can move bodies that are not part of an island. In this case we need to notify the broadphase of the movement.
  1100. static constexpr int cBodiesBatch = 64;
  1101. BodyID *bodies_to_update_bounds = (BodyID *)JPH_STACK_ALLOC(cBodiesBatch * sizeof(BodyID));
  1102. int num_bodies_to_update_bounds = 0;
  1103. // Update the positions using an Symplectic Euler step (which integrates using the updated velocity v1' rather
  1104. // than the original velocity v1):
  1105. // x1' = x1 + h * v1'
  1106. // At this point the active bodies list does not change, so it is safe to access the array.
  1107. for (const BodyID *id = active_bodies, *id_end = active_bodies + num_active_bodies; id < id_end; ++id)
  1108. {
  1109. Body &body = mBodyManager.GetBody(*id);
  1110. MotionProperties *mp = body.GetMotionProperties();
  1111. // Clamp velocities (not for kinematic bodies)
  1112. if (body.IsDynamic())
  1113. {
  1114. mp->ClampLinearVelocity();
  1115. mp->ClampAngularVelocity();
  1116. }
  1117. // Update the rotation of the body according to the angular velocity
  1118. // For motion type discrete we need to do this anyway, for motion type linear cast we have multiple choices
  1119. // 1. Rotate the body first and then sweep
  1120. // 2. First sweep and then rotate the body at the end
  1121. // 3. Pick some inbetween rotation (e.g. half way), then sweep and finally rotate the remainder
  1122. // (1) has some clear advantages as when a long thin body hits a surface away from the center of mass, this will result in a large angular velocity and a limited reduction in linear velocity.
  1123. // When simulation the rotation first before doing the translation, the body will be able to rotate away from the contact point allowing the center of mass to approach the surface. When using
  1124. // approach (2) in this case what will happen is that we will immediately detect the same collision again (the body has not rotated and the body was already colliding at the end of the previous
  1125. // time step) resulting in a lot of stolen time and the body appearing to be frozen in an unnatural pose (like it is glued at an angle to the surface). (2) obviously has some negative side effects
  1126. // too as simulating the rotation first may cause it to tunnel through a small object that the linear cast might have otherwise dectected. In any case a linear cast is not good for detecting
  1127. // tunneling due to angular rotation, so we don't care about that too much (you'd need a full cast to take angular effects into account).
  1128. body.AddRotationStep(body.GetAngularVelocity() * delta_time);
  1129. // Get delta position
  1130. Vec3 delta_pos = body.GetLinearVelocity() * delta_time;
  1131. // If the position should be updated (or if it is delayed because of CCD)
  1132. bool update_position = true;
  1133. switch (mp->GetMotionQuality())
  1134. {
  1135. case EMotionQuality::Discrete:
  1136. // No additional collision checking to be done
  1137. break;
  1138. case EMotionQuality::LinearCast:
  1139. if (body.IsDynamic()) // Kinematic bodies cannot be stopped
  1140. {
  1141. // Determine inner radius (the smallest sphere that fits into the shape)
  1142. float inner_radius = body.GetShape()->GetInnerRadius();
  1143. JPH_ASSERT(inner_radius > 0.0f, "The shape has no inner radius, this makes the shape unsuitable for the linear cast motion quality as we cannot move it without risking tunneling.");
  1144. // Measure translation in this step and check if it above the treshold to perform a linear cast
  1145. float linear_cast_threshold_sq = Square(mPhysicsSettings.mLinearCastThreshold * inner_radius);
  1146. if (delta_pos.LengthSq() > linear_cast_threshold_sq)
  1147. {
  1148. // This body needs a cast
  1149. ioSubStep->mActiveBodyToCCDBody[ioSubStep->mNumActiveBodyToCCDBody++] = ioSubStep->mNumCCDBodies;
  1150. new (&ioSubStep->mCCDBodies[ioSubStep->mNumCCDBodies++]) CCDBody(*id, delta_pos, linear_cast_threshold_sq, min(mPhysicsSettings.mPenetrationSlop, mPhysicsSettings.mLinearCastMaxPenetration * inner_radius));
  1151. update_position = false;
  1152. }
  1153. }
  1154. break;
  1155. }
  1156. if (update_position)
  1157. {
  1158. // Move the body now
  1159. body.AddPositionStep(delta_pos);
  1160. // If the body was activated due to an earlier CCD step it will have an index in the active
  1161. // body list that it higher than the highest one we processed during FindCollisions
  1162. // which means it hasn't been assigned an island and will not be updated by an island
  1163. // this means that we need to update its bounds manually
  1164. if (mp->GetIndexInActiveBodiesInternal() >= num_active_bodies_after_find_collisions)
  1165. {
  1166. body.CalculateWorldSpaceBoundsInternal();
  1167. bodies_to_update_bounds[num_bodies_to_update_bounds++] = body.GetID();
  1168. if (num_bodies_to_update_bounds == cBodiesBatch)
  1169. {
  1170. // Buffer full, flush now
  1171. mBroadPhase->NotifyBodiesAABBChanged(bodies_to_update_bounds, num_bodies_to_update_bounds);
  1172. num_bodies_to_update_bounds = 0;
  1173. }
  1174. }
  1175. // We did not create a CCD body
  1176. ioSubStep->mActiveBodyToCCDBody[ioSubStep->mNumActiveBodyToCCDBody++] = -1;
  1177. }
  1178. }
  1179. // Validate that our reservations were correct
  1180. JPH_ASSERT(ioSubStep->mNumCCDBodies <= mBodyManager.GetNumActiveCCDBodies());
  1181. JPH_ASSERT(ioSubStep->mNumActiveBodyToCCDBody == num_active_bodies);
  1182. // Notify change bounds on requested bodies
  1183. if (num_bodies_to_update_bounds > 0)
  1184. mBroadPhase->NotifyBodiesAABBChanged(bodies_to_update_bounds, num_bodies_to_update_bounds, false);
  1185. if (ioSubStep->mNumCCDBodies == 0)
  1186. {
  1187. // No continous collision detection jobs -> kick the next job ourselves
  1188. if (ioSubStep->mIsLast)
  1189. ioSubStep->mStep->mContactRemovedCallbacks.RemoveDependency();
  1190. }
  1191. else
  1192. {
  1193. // Run the continous collision detection jobs
  1194. int num_continuous_collision_jobs = min(int(ioSubStep->mNumCCDBodies + cNumCCDBodiesPerJob - 1) / cNumCCDBodiesPerJob, ioContext->GetMaxConcurrency());
  1195. ioSubStep->mResolveCCDContacts.AddDependency(num_continuous_collision_jobs);
  1196. if (ioSubStep->mIsLast)
  1197. ioSubStep->mStep->mContactRemovedCallbacks.AddDependency(num_continuous_collision_jobs - 1); // Already had 1 dependency
  1198. for (int i = 0; i < num_continuous_collision_jobs; ++i)
  1199. {
  1200. JobHandle job = ioContext->mJobSystem->CreateJob("FindCCDContacts", cColorFindCCDContacts, [ioContext, ioSubStep]()
  1201. {
  1202. ioContext->mPhysicsSystem->JobFindCCDContacts(ioContext, ioSubStep);
  1203. ioSubStep->mResolveCCDContacts.RemoveDependency();
  1204. if (ioSubStep->mIsLast)
  1205. ioSubStep->mStep->mContactRemovedCallbacks.RemoveDependency();
  1206. });
  1207. ioContext->mBarrier->AddJob(job);
  1208. }
  1209. }
  1210. }
  1211. // Helper function to calculate the motion of a body during this CCD step
  1212. inline static Vec3 sCalculateBodyMotion(const Body &inBody, float inDeltaTime)
  1213. {
  1214. // If the body is linear casting, the body has not yet moved so we need to calculate its motion
  1215. if (inBody.IsDynamic() && inBody.GetMotionProperties()->GetMotionQuality() == EMotionQuality::LinearCast)
  1216. return inDeltaTime * inBody.GetLinearVelocity();
  1217. // Body has already moved, so we don't need to correct for anything
  1218. return Vec3::sZero();
  1219. }
  1220. // Helper function that finds the CCD body corresponding to a body (if it exists)
  1221. inline static PhysicsUpdateContext::SubStep::CCDBody *sGetCCDBody(const Body &inBody, PhysicsUpdateContext::SubStep *inSubStep)
  1222. {
  1223. // If the body has no motion properties it cannot have a CCD body
  1224. const MotionProperties *motion_properties = inBody.GetMotionPropertiesUnchecked();
  1225. if (motion_properties == nullptr)
  1226. return nullptr;
  1227. // If it is not active it cannot have a CCD body
  1228. uint32 active_index = motion_properties->GetIndexInActiveBodiesInternal();
  1229. if (active_index == Body::cInactiveIndex)
  1230. return nullptr;
  1231. // Check if the active body has a corresponding CCD body
  1232. JPH_ASSERT(active_index < inSubStep->mNumActiveBodyToCCDBody); // Ensure that the body has a mapping to CCD body
  1233. int ccd_index = inSubStep->mActiveBodyToCCDBody[active_index];
  1234. if (ccd_index < 0)
  1235. return nullptr;
  1236. PhysicsUpdateContext::SubStep::CCDBody *ccd_body = &inSubStep->mCCDBodies[ccd_index];
  1237. JPH_ASSERT(ccd_body->mBodyID1 == inBody.GetID(), "We found the wrong CCD body!");
  1238. return ccd_body;
  1239. }
  1240. void PhysicsSystem::JobFindCCDContacts(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::SubStep *ioSubStep)
  1241. {
  1242. #ifdef JPH_ENABLE_ASSERTS
  1243. // We only read positions, but the validate callback may read body positions and velocities
  1244. BodyAccess::Grant grant(BodyAccess::EAccess::Read, BodyAccess::EAccess::Read);
  1245. #endif
  1246. // Settings
  1247. ShapeCastSettings settings;
  1248. settings.mUseShrunkenShapeAndConvexRadius = true;
  1249. settings.mBackFaceModeTriangles = EBackFaceMode::IgnoreBackFaces;
  1250. settings.mBackFaceModeConvex = EBackFaceMode::IgnoreBackFaces;
  1251. settings.mReturnDeepestPoint = true;
  1252. settings.mCollectFacesMode = ECollectFacesMode::CollectFaces;
  1253. settings.mActiveEdgeMode = mPhysicsSettings.mCheckActiveEdges? EActiveEdgeMode::CollideOnlyWithActive : EActiveEdgeMode::CollideWithAll;
  1254. for (;;)
  1255. {
  1256. // Fetch the next body to cast
  1257. uint32 idx = ioSubStep->mNextCCDBody++;
  1258. if (idx >= ioSubStep->mNumCCDBodies)
  1259. break;
  1260. CCDBody &ccd_body = ioSubStep->mCCDBodies[idx];
  1261. const Body &body = mBodyManager.GetBody(ccd_body.mBodyID1);
  1262. // Filter out layers
  1263. DefaultBroadPhaseLayerFilter broadphase_layer_filter = GetDefaultBroadPhaseLayerFilter(body.GetObjectLayer());
  1264. DefaultObjectLayerFilter object_layer_filter = GetDefaultLayerFilter(body.GetObjectLayer());
  1265. #ifdef JPH_DEBUG_RENDERER
  1266. // Draw start and end shape of cast
  1267. if (sDrawMotionQualityLinearCast)
  1268. {
  1269. Mat44 com = body.GetCenterOfMassTransform();
  1270. body.GetShape()->Draw(DebugRenderer::sInstance, com, Vec3::sReplicate(1.0f), Color::sGreen, false, true);
  1271. DebugRenderer::sInstance->DrawArrow(com.GetTranslation(), com.GetTranslation() + ccd_body.mDeltaPosition, Color::sGreen, 0.1f);
  1272. body.GetShape()->Draw(DebugRenderer::sInstance, Mat44::sTranslation(ccd_body.mDeltaPosition) * com, Vec3::sReplicate(1.0f), Color::sRed, false, true);
  1273. }
  1274. #endif // JPH_DEBUG_RENDERER
  1275. // Create a collector that will find the maximum distance allowed to travel while not penetrating more than 'max penetration'
  1276. class CCDNarrowPhaseCollector : public CastShapeCollector
  1277. {
  1278. public:
  1279. CCDNarrowPhaseCollector(const BodyManager &inBodyManager, ContactConstraintManager &inContactConstraintManager, CCDBody &inCCDBody, ShapeCastResult &inResult, float inDeltaTime) :
  1280. mBodyManager(inBodyManager),
  1281. mContactConstraintManager(inContactConstraintManager),
  1282. mCCDBody(inCCDBody),
  1283. mResult(inResult),
  1284. mDeltaTime(inDeltaTime)
  1285. {
  1286. }
  1287. virtual void AddHit(const ShapeCastResult &inResult) override
  1288. {
  1289. JPH_PROFILE_FUNCTION();
  1290. // Check if this is a possible earlier hit than the one before
  1291. float fraction = inResult.mFraction;
  1292. if (fraction < mCCDBody.mFractionPlusSlop)
  1293. {
  1294. // Normalize normal
  1295. Vec3 normal = inResult.mPenetrationAxis.Normalized();
  1296. // Calculate how much we can add to the fraction to penetrate the collision point by mMaxPenetration.
  1297. // Note that the normal is pointing towards body 2!
  1298. // Let the extra distance that we can travel along delta_pos be 'dist': mMaxPenetration / dist = cos(angle between normal and delta_pos) = normal . delta_pos / |delta_pos|
  1299. // <=> dist = mMaxPenetration * |delta_pos| / normal . delta_pos
  1300. // Converting to a faction: delta_fraction = dist / |delta_pos| = mLinearCastTreshold / normal . delta_pos
  1301. float denominator = normal.Dot(mCCDBody.mDeltaPosition);
  1302. if (denominator > mCCDBody.mMaxPenetration) // Avoid dividing by zero, if extra hit fraction > 1 there's also no point in continuing
  1303. {
  1304. float fraction_plus_slop = fraction + mCCDBody.mMaxPenetration / denominator;
  1305. if (fraction_plus_slop < mCCDBody.mFractionPlusSlop)
  1306. {
  1307. const Body &body2 = mBodyManager.GetBody(inResult.mBodyID2);
  1308. // Check if we've already accepted all hits from this body
  1309. if (mValidateBodyPair)
  1310. {
  1311. // Validate the contact result
  1312. ValidateResult validate_result = mContactConstraintManager.ValidateContactPoint(mBodyManager.GetBody(mCCDBody.mBodyID1), body2, inResult);
  1313. switch (validate_result)
  1314. {
  1315. case ValidateResult::AcceptContact:
  1316. // Just continue
  1317. break;
  1318. case ValidateResult::AcceptAllContactsForThisBodyPair:
  1319. // Accept this and all following contacts from this body
  1320. mValidateBodyPair = false;
  1321. break;
  1322. case ValidateResult::RejectContact:
  1323. return;
  1324. case ValidateResult::RejectAllContactsForThisBodyPair:
  1325. // Reject this and all following contacts from this body
  1326. mRejectAll = true;
  1327. ForceEarlyOut();
  1328. return;
  1329. }
  1330. }
  1331. // This is the earliest hit so far, store it
  1332. mCCDBody.mContactNormal = normal;
  1333. mCCDBody.mBodyID2 = inResult.mBodyID2;
  1334. mCCDBody.mFraction = fraction;
  1335. mCCDBody.mFractionPlusSlop = fraction_plus_slop;
  1336. mResult = inResult;
  1337. // Result was assuming body 2 is not moving, but it is, so we need to correct for it
  1338. Vec3 movement2 = fraction * sCalculateBodyMotion(body2, mDeltaTime);
  1339. if (!movement2.IsNearZero())
  1340. {
  1341. mResult.mContactPointOn1 += movement2;
  1342. mResult.mContactPointOn2 += movement2;
  1343. for (Vec3 &v : mResult.mShape1Face)
  1344. v += movement2;
  1345. for (Vec3 &v : mResult.mShape2Face)
  1346. v += movement2;
  1347. }
  1348. // Update early out fraction
  1349. CastShapeCollector::UpdateEarlyOutFraction(fraction_plus_slop);
  1350. }
  1351. }
  1352. }
  1353. }
  1354. bool mValidateBodyPair; ///< If we still have to call the ValidateContactPoint for this body pair
  1355. bool mRejectAll; ///< Reject all further contacts between this body pair
  1356. private:
  1357. const BodyManager & mBodyManager;
  1358. ContactConstraintManager & mContactConstraintManager;
  1359. CCDBody & mCCDBody;
  1360. ShapeCastResult & mResult;
  1361. float mDeltaTime;
  1362. BodyID mAcceptedBodyID;
  1363. };
  1364. // Narrowphase collector
  1365. ShapeCastResult cast_shape_result;
  1366. CCDNarrowPhaseCollector np_collector(mBodyManager, mContactManager, ccd_body, cast_shape_result, ioContext->mSubStepDeltaTime);
  1367. // This collector wraps the narrowphase collector and collects the closest hit
  1368. class CCDBroadPhaseCollector : public CastShapeBodyCollector
  1369. {
  1370. public:
  1371. CCDBroadPhaseCollector(const CCDBody &inCCDBody, const Body &inBody1, const ShapeCast &inShapeCast, ShapeCastSettings &inShapeCastSettings, CCDNarrowPhaseCollector &ioCollector, const BodyManager &inBodyManager, PhysicsUpdateContext::SubStep *inSubStep, float inDeltaTime) :
  1372. mCCDBody(inCCDBody),
  1373. mBody1(inBody1),
  1374. mBody1Extent(inShapeCast.mShapeWorldBounds.GetExtent()),
  1375. mShapeCast(inShapeCast),
  1376. mShapeCastSettings(inShapeCastSettings),
  1377. mCollector(ioCollector),
  1378. mBodyManager(inBodyManager),
  1379. mSubStep(inSubStep),
  1380. mDeltaTime(inDeltaTime)
  1381. {
  1382. }
  1383. virtual void AddHit(const BroadPhaseCastResult &inResult) override
  1384. {
  1385. JPH_PROFILE_FUNCTION();
  1386. JPH_ASSERT(inResult.mFraction <= GetEarlyOutFraction(), "This hit should not have been passed on to the collector");
  1387. // Test if we're colliding with ourselves
  1388. if (mBody1.GetID() == inResult.mBodyID)
  1389. return;
  1390. // Avoid treating duplicates, if both bodies are doing CCD then only consider collision if body ID < other body ID
  1391. const Body &body2 = mBodyManager.GetBody(inResult.mBodyID);
  1392. const CCDBody *ccd_body2 = sGetCCDBody(body2, mSubStep);
  1393. if (ccd_body2 != nullptr && mCCDBody.mBodyID1 > ccd_body2->mBodyID1)
  1394. return;
  1395. // Test group filter
  1396. if (!mBody1.GetCollisionGroup().CanCollide(body2.GetCollisionGroup()))
  1397. return;
  1398. // TODO: For now we ignore sensors
  1399. if (body2.IsSensor())
  1400. return;
  1401. // Get relative movement of these two bodies
  1402. Vec3 direction = mShapeCast.mDirection - sCalculateBodyMotion(body2, mDeltaTime);
  1403. // Test if the remaining movement is less than our movement threshold
  1404. if (direction.LengthSq() < mCCDBody.mLinearCastThresholdSq)
  1405. return;
  1406. // Get the bounds of 2, widen it by the extent of 1 and test a ray to see if it hits earlier than the current early out fraction
  1407. AABox bounds = body2.GetWorldSpaceBounds();
  1408. bounds.mMin -= mBody1Extent;
  1409. bounds.mMax += mBody1Extent;
  1410. float hit_fraction = RayAABox(mShapeCast.mCenterOfMassStart.GetTranslation(), RayInvDirection(direction), bounds.mMin, bounds.mMax);
  1411. if (hit_fraction > max(FLT_MIN, GetEarlyOutFraction())) // If early out fraction <= 0, we have the possibility of finding a deeper hit so we need to clamp the early out fraction
  1412. return;
  1413. // Reset collector (this is a new body pair)
  1414. mCollector.ResetEarlyOutFraction(GetEarlyOutFraction());
  1415. mCollector.mValidateBodyPair = true;
  1416. mCollector.mRejectAll = false;
  1417. // Provide direction as hint for the active edges algorithm
  1418. mShapeCastSettings.mActiveEdgeMovementDirection = direction;
  1419. // Do narrow phase collision check
  1420. ShapeCast relative_cast(mShapeCast.mShape, mShapeCast.mScale, mShapeCast.mCenterOfMassStart, direction, mShapeCast.mShapeWorldBounds);
  1421. body2.GetTransformedShape().CastShape(relative_cast, mShapeCastSettings, mCollector);
  1422. // Update early out fraction based on narrow phase collector
  1423. if (!mCollector.mRejectAll)
  1424. UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction());
  1425. }
  1426. const CCDBody & mCCDBody;
  1427. const Body & mBody1;
  1428. Vec3 mBody1Extent;
  1429. ShapeCast mShapeCast;
  1430. ShapeCastSettings & mShapeCastSettings;
  1431. CCDNarrowPhaseCollector & mCollector;
  1432. const BodyManager & mBodyManager;
  1433. PhysicsUpdateContext::SubStep *mSubStep;
  1434. float mDeltaTime;
  1435. };
  1436. // Check if we collide with any other body
  1437. ShapeCast shape_cast(body.GetShape(), Vec3::sReplicate(1.0f), body.GetCenterOfMassTransform(), ccd_body.mDeltaPosition);
  1438. CCDBroadPhaseCollector bp_collector(ccd_body, body, shape_cast, settings, np_collector, mBodyManager, ioSubStep, ioContext->mSubStepDeltaTime);
  1439. mBroadPhase->CastAABox({ shape_cast.mShapeWorldBounds, shape_cast.mDirection }, bp_collector, broadphase_layer_filter, object_layer_filter);
  1440. // Check if there was a hit
  1441. if (ccd_body.mFractionPlusSlop < 1.0f)
  1442. {
  1443. const Body &body2 = mBodyManager.GetBody(ccd_body.mBodyID2);
  1444. // Determine contact manifold
  1445. ContactManifold manifold;
  1446. ManifoldBetweenTwoFaces(cast_shape_result.mContactPointOn1, cast_shape_result.mContactPointOn2, cast_shape_result.mPenetrationAxis, mPhysicsSettings.mManifoldToleranceSq, cast_shape_result.mShape1Face, cast_shape_result.mShape2Face, manifold.mWorldSpaceContactPointsOn1, manifold.mWorldSpaceContactPointsOn2);
  1447. manifold.mSubShapeID1 = cast_shape_result.mSubShapeID1;
  1448. manifold.mSubShapeID2 = cast_shape_result.mSubShapeID2;
  1449. manifold.mPenetrationDepth = cast_shape_result.mPenetrationDepth;
  1450. manifold.mWorldSpaceNormal = ccd_body.mContactNormal;
  1451. // Call contact point callbacks
  1452. mContactManager.OnCCDContactAdded(body, body2, manifold, ccd_body.mContactSettings);
  1453. // Calculate the average position from the manifold (this will result in the same impulse applied as when we apply impulses to all contact points)
  1454. if (manifold.mWorldSpaceContactPointsOn2.size() > 1)
  1455. {
  1456. Vec3 average_contact_point = Vec3::sZero();
  1457. for (const Vec3 &v : manifold.mWorldSpaceContactPointsOn2)
  1458. average_contact_point += v;
  1459. average_contact_point /= (float)manifold.mWorldSpaceContactPointsOn2.size();
  1460. ccd_body.mContactPointOn2 = average_contact_point;
  1461. }
  1462. else
  1463. ccd_body.mContactPointOn2 = cast_shape_result.mContactPointOn2;
  1464. }
  1465. }
  1466. }
  1467. void PhysicsSystem::JobResolveCCDContacts(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::SubStep *ioSubStep)
  1468. {
  1469. #ifdef JPH_ENABLE_ASSERTS
  1470. // Read/write body access
  1471. BodyAccess::Grant grant(BodyAccess::EAccess::ReadWrite, BodyAccess::EAccess::ReadWrite);
  1472. // We activate bodies that we collide with
  1473. BodyManager::GrantActiveBodiesAccess grant_active(true, false);
  1474. #endif
  1475. uint32 num_active_bodies_after_find_collisions = ioSubStep->mStep->mActiveBodyReadIdx;
  1476. TempAllocator *temp_allocator = ioContext->mTempAllocator;
  1477. // Check if there's anything to do
  1478. uint num_ccd_bodies = ioSubStep->mNumCCDBodies;
  1479. if (num_ccd_bodies > 0)
  1480. {
  1481. // Sort on fraction so that we process earliest collisions first
  1482. // This is needed to make the simulation deterministic and also to be able to stop contact processing
  1483. // between body pairs if an earlier hit was found involving the body by another CCD body
  1484. // (if it's body ID < this CCD body's body ID - see filtering logic in CCDBroadPhaseCollector)
  1485. CCDBody **sorted_ccd_bodies = (CCDBody **)temp_allocator->Allocate(num_ccd_bodies * sizeof(CCDBody *));
  1486. {
  1487. JPH_PROFILE("Sort");
  1488. // We don't want to copy the entire struct (it's quite big), so we create a pointer array first
  1489. CCDBody *src_ccd_bodies = ioSubStep->mCCDBodies;
  1490. CCDBody **dst_ccd_bodies = sorted_ccd_bodies;
  1491. CCDBody **dst_ccd_bodies_end = dst_ccd_bodies + num_ccd_bodies;
  1492. while (dst_ccd_bodies < dst_ccd_bodies_end)
  1493. *(dst_ccd_bodies++) = src_ccd_bodies++;
  1494. // Which we then sort
  1495. sort(sorted_ccd_bodies, sorted_ccd_bodies + num_ccd_bodies, [](const CCDBody *inBody1, const CCDBody *inBody2)
  1496. {
  1497. if (inBody1->mFractionPlusSlop != inBody2->mFractionPlusSlop)
  1498. return inBody1->mFractionPlusSlop < inBody2->mFractionPlusSlop;
  1499. return inBody1->mBodyID1 < inBody2->mBodyID1;
  1500. });
  1501. }
  1502. // We can collide with bodies that are not active, we track them here so we can activate them in one go at the end.
  1503. // This is also needed because we can't modify the active body array while we iterate it.
  1504. static constexpr int cBodiesBatch = 64;
  1505. BodyID *bodies_to_activate = (BodyID *)JPH_STACK_ALLOC(cBodiesBatch * sizeof(BodyID));
  1506. int num_bodies_to_activate = 0;
  1507. // We can move bodies that are not part of an island. In this case we need to notify the broadphase of the movement.
  1508. BodyID *bodies_to_update_bounds = (BodyID *)JPH_STACK_ALLOC(cBodiesBatch * sizeof(BodyID));
  1509. int num_bodies_to_update_bounds = 0;
  1510. for (uint i = 0; i < num_ccd_bodies; ++i)
  1511. {
  1512. const CCDBody *ccd_body = sorted_ccd_bodies[i];
  1513. Body &body1 = mBodyManager.GetBody(ccd_body->mBodyID1);
  1514. MotionProperties *body_mp = body1.GetMotionProperties();
  1515. // If there was a hit
  1516. if (!ccd_body->mBodyID2.IsInvalid())
  1517. {
  1518. Body &body2 = mBodyManager.GetBody(ccd_body->mBodyID2);
  1519. // Determine if the other body has a CCD body
  1520. CCDBody *ccd_body2 = sGetCCDBody(body2, ioSubStep);
  1521. if (ccd_body2 != nullptr)
  1522. {
  1523. JPH_ASSERT(ccd_body2->mBodyID2 != ccd_body->mBodyID1, "If we collided with another body, that other body should have ignored collisions with us!");
  1524. // Check if the other body found a hit that is further away
  1525. if (ccd_body2->mFraction > ccd_body->mFraction)
  1526. {
  1527. // Reset the colliding body of the other CCD body. The other body will shorten its distance travelled and will not do any collision response (we'll do that).
  1528. // This means that at this point we have triggered a contact point add/persist for our further hit by accident for the other body.
  1529. // We accept this as calling the contact point callbacks here would require persisting the manifolds up to this point and doing the callbacks single threaded.
  1530. ccd_body2->mBodyID2 = BodyID();
  1531. ccd_body2->mFractionPlusSlop = ccd_body->mFraction;
  1532. }
  1533. }
  1534. // If the other body moved less than us before hitting something, we're not colliding with it so we again have triggered contact point add/persist callbacks by accident.
  1535. // We'll just move to the collision position anyway (as that's the last position we know is good), but we won't do any collision response.
  1536. if (ccd_body2 == nullptr || ccd_body2->mFraction >= ccd_body->mFraction)
  1537. {
  1538. // Calculate contact points relative to center of mass of both bodies
  1539. Vec3 r1_plus_u = ccd_body->mContactPointOn2 - (body1.GetCenterOfMassPosition() + ccd_body->mFraction * ccd_body->mDeltaPosition);
  1540. Vec3 r2 = ccd_body->mContactPointOn2 - body2.GetCenterOfMassPosition();
  1541. // Calculate velocity of collision points
  1542. Vec3 v1 = body1.GetLinearVelocity() + body1.GetAngularVelocity().Cross(r1_plus_u);
  1543. Vec3 v2 = body2.GetLinearVelocity() + body2.GetAngularVelocity().Cross(r2);
  1544. Vec3 relative_velocity = v2 - v1;
  1545. float normal_velocity = relative_velocity.Dot(ccd_body->mContactNormal);
  1546. // Calculate velocity bias due to restitution
  1547. float normal_velocity_bias;
  1548. if (ccd_body->mContactSettings.mCombinedRestitution > 0.0f && normal_velocity < -mPhysicsSettings.mMinVelocityForRestitution)
  1549. normal_velocity_bias = ccd_body->mContactSettings.mCombinedRestitution * normal_velocity;
  1550. else
  1551. normal_velocity_bias = 0.0f;
  1552. // Solve contact constraint
  1553. AxisConstraintPart contact_constraint;
  1554. contact_constraint.CalculateConstraintProperties(ioContext->mSubStepDeltaTime, body1, r1_plus_u, body2, r2, ccd_body->mContactNormal, normal_velocity_bias);
  1555. contact_constraint.SolveVelocityConstraint(body1, body2, ccd_body->mContactNormal, -FLT_MAX, FLT_MAX);
  1556. // Apply friction
  1557. if (ccd_body->mContactSettings.mCombinedFriction > 0.0f)
  1558. {
  1559. Vec3 tangent1 = ccd_body->mContactNormal.GetNormalizedPerpendicular();
  1560. Vec3 tangent2 = ccd_body->mContactNormal.Cross(tangent1);
  1561. float max_lambda_f = ccd_body->mContactSettings.mCombinedFriction * contact_constraint.GetTotalLambda();
  1562. AxisConstraintPart friction1;
  1563. friction1.CalculateConstraintProperties(ioContext->mSubStepDeltaTime, body1, r1_plus_u, body2, r2, tangent1, 0.0f);
  1564. friction1.SolveVelocityConstraint(body1, body2, tangent1, -max_lambda_f, max_lambda_f);
  1565. AxisConstraintPart friction2;
  1566. friction2.CalculateConstraintProperties(ioContext->mSubStepDeltaTime, body1, r1_plus_u, body2, r2, tangent2, 0.0f);
  1567. friction2.SolveVelocityConstraint(body1, body2, tangent2, -max_lambda_f, max_lambda_f);
  1568. }
  1569. // Clamp velocities
  1570. body_mp->ClampLinearVelocity();
  1571. body_mp->ClampAngularVelocity();
  1572. if (body2.IsDynamic())
  1573. {
  1574. MotionProperties *body2_mp = body2.GetMotionProperties();
  1575. body2_mp->ClampLinearVelocity();
  1576. body2_mp->ClampAngularVelocity();
  1577. // Activate the body if it is not already active
  1578. if (!body2.IsActive())
  1579. {
  1580. bodies_to_activate[num_bodies_to_activate++] = ccd_body->mBodyID2;
  1581. if (num_bodies_to_activate == cBodiesBatch)
  1582. {
  1583. // Batch is full, activate now
  1584. mBodyManager.ActivateBodies(bodies_to_activate, num_bodies_to_activate);
  1585. num_bodies_to_activate = 0;
  1586. }
  1587. }
  1588. }
  1589. #ifdef JPH_DEBUG_RENDERER
  1590. if (sDrawMotionQualityLinearCast)
  1591. {
  1592. // Draw the collision location
  1593. Mat44 collision_transform = Mat44::sTranslation(ccd_body->mFraction * ccd_body->mDeltaPosition) * body1.GetCenterOfMassTransform();
  1594. body1.GetShape()->Draw(DebugRenderer::sInstance, collision_transform, Vec3::sReplicate(1.0f), Color::sYellow, false, true);
  1595. // Draw the collision location + slop
  1596. Mat44 collision_transform_plus_slop = Mat44::sTranslation(ccd_body->mFractionPlusSlop * ccd_body->mDeltaPosition) * body1.GetCenterOfMassTransform();
  1597. body1.GetShape()->Draw(DebugRenderer::sInstance, collision_transform_plus_slop, Vec3::sReplicate(1.0f), Color::sOrange, false, true);
  1598. // Draw contact normal
  1599. DebugRenderer::sInstance->DrawArrow(ccd_body->mContactPointOn2, ccd_body->mContactPointOn2 - ccd_body->mContactNormal, Color::sYellow, 0.1f);
  1600. // Draw post contact velocity
  1601. DebugRenderer::sInstance->DrawArrow(collision_transform.GetTranslation(), collision_transform.GetTranslation() + body1.GetLinearVelocity(), Color::sOrange, 0.1f);
  1602. DebugRenderer::sInstance->DrawArrow(collision_transform.GetTranslation(), collision_transform.GetTranslation() + body1.GetAngularVelocity(), Color::sPurple, 0.1f);
  1603. }
  1604. #endif // JPH_DEBUG_RENDERER
  1605. }
  1606. }
  1607. // Update body position
  1608. body1.AddPositionStep(ccd_body->mDeltaPosition * ccd_body->mFractionPlusSlop);
  1609. // If the body was activated due to an earlier CCD step it will have an index in the active
  1610. // body list that it higher than the highest one we processed during FindCollisions
  1611. // which means it hasn't been assigned an island and will not be updated by an island
  1612. // this means that we need to update its bounds manually
  1613. if (body_mp->GetIndexInActiveBodiesInternal() >= num_active_bodies_after_find_collisions)
  1614. {
  1615. body1.CalculateWorldSpaceBoundsInternal();
  1616. bodies_to_update_bounds[num_bodies_to_update_bounds++] = body1.GetID();
  1617. if (num_bodies_to_update_bounds == cBodiesBatch)
  1618. {
  1619. // Buffer full, flush now
  1620. mBroadPhase->NotifyBodiesAABBChanged(bodies_to_update_bounds, num_bodies_to_update_bounds);
  1621. num_bodies_to_update_bounds = 0;
  1622. }
  1623. }
  1624. }
  1625. // Activate the requested bodies
  1626. if (num_bodies_to_activate > 0)
  1627. mBodyManager.ActivateBodies(bodies_to_activate, num_bodies_to_activate);
  1628. // Notify change bounds on requested bodies
  1629. if (num_bodies_to_update_bounds > 0)
  1630. mBroadPhase->NotifyBodiesAABBChanged(bodies_to_update_bounds, num_bodies_to_update_bounds, false);
  1631. // Free the sorted ccd bodies
  1632. temp_allocator->Free(sorted_ccd_bodies, num_ccd_bodies * sizeof(CCDBody *));
  1633. }
  1634. // Ensure we free the CCD bodies array now, will not call the destructor!
  1635. temp_allocator->Free(ioSubStep->mActiveBodyToCCDBody, ioSubStep->mNumActiveBodyToCCDBody * sizeof(int));
  1636. ioSubStep->mActiveBodyToCCDBody = nullptr;
  1637. ioSubStep->mNumActiveBodyToCCDBody = 0;
  1638. temp_allocator->Free(ioSubStep->mCCDBodies, ioSubStep->mCCDBodiesCapacity * sizeof(CCDBody));
  1639. ioSubStep->mCCDBodies = nullptr;
  1640. ioSubStep->mCCDBodiesCapacity = 0;
  1641. }
  1642. void PhysicsSystem::JobContactRemovedCallbacks()
  1643. {
  1644. #ifdef JPH_ENABLE_ASSERTS
  1645. // We don't touch any bodies
  1646. BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::None);
  1647. #endif
  1648. // Reset the Body::EFlags::InvalidateContactCache flag for all bodies
  1649. mBodyManager.ValidateContactCacheForAllBodies();
  1650. // Trigger all contact removed callbacks by looking at last step contact points that have not been flagged as reused
  1651. mContactManager.ContactPointRemovedCallbacks();
  1652. // Finalize the contact cache (this swaps the read and write versions of the contact cache)
  1653. mContactManager.FinalizeContactCache();
  1654. }
  1655. void PhysicsSystem::JobSolvePositionConstraints(PhysicsUpdateContext *ioContext, PhysicsUpdateContext::SubStep *ioSubStep)
  1656. {
  1657. #ifdef JPH_ENABLE_ASSERTS
  1658. // We fix up position errors
  1659. BodyAccess::Grant grant(BodyAccess::EAccess::None, BodyAccess::EAccess::ReadWrite);
  1660. // Can only deactivate bodies
  1661. BodyManager::GrantActiveBodiesAccess grant_active(false, true);
  1662. #endif
  1663. float delta_time = ioContext->mSubStepDeltaTime;
  1664. Constraint **active_constraints = ioContext->mActiveConstraints;
  1665. for (;;)
  1666. {
  1667. // Next island
  1668. uint32 island_idx = ioSubStep->mSolvePositionConstraintsNextIsland++;
  1669. if (island_idx >= mIslandBuilder.GetNumIslands())
  1670. break;
  1671. JPH_PROFILE("Island");
  1672. // Get iterators for this island
  1673. BodyID *bodies_begin, *bodies_end;
  1674. mIslandBuilder.GetBodiesInIsland(island_idx, bodies_begin, bodies_end);
  1675. uint32 *constraints_begin, *constraints_end;
  1676. bool has_constraints = mIslandBuilder.GetConstraintsInIsland(island_idx, constraints_begin, constraints_end);
  1677. uint32 *contacts_begin, *contacts_end;
  1678. bool has_contacts = mIslandBuilder.GetContactsInIsland(island_idx, contacts_begin, contacts_end);
  1679. // Correct positions
  1680. if (has_contacts || has_constraints)
  1681. {
  1682. float baumgarte = mPhysicsSettings.mBaumgarte;
  1683. for (int position_step = 0; position_step < mPhysicsSettings.mNumPositionSteps; ++position_step)
  1684. {
  1685. bool constraint_impulse = mConstraintManager.SolvePositionConstraints(active_constraints, constraints_begin, constraints_end, delta_time, baumgarte);
  1686. bool contact_impulse = mContactManager.SolvePositionConstraints(contacts_begin, contacts_end);
  1687. if (!constraint_impulse && !contact_impulse)
  1688. break;
  1689. }
  1690. }
  1691. // Only check sleeping in the last sub step of the last step
  1692. // Also resets force and torque used during the apply gravity phase
  1693. if (ioSubStep->mIsLastOfAll)
  1694. {
  1695. JPH_PROFILE("Check Sleeping");
  1696. static_assert(int(Body::ECanSleep::CannotSleep) == 0 && int(Body::ECanSleep::CanSleep) == 1, "Loop below makes this assumption");
  1697. int all_can_sleep = mPhysicsSettings.mAllowSleeping? int(Body::ECanSleep::CanSleep) : int(Body::ECanSleep::CannotSleep);
  1698. float time_before_sleep = mPhysicsSettings.mTimeBeforeSleep;
  1699. float max_movement = mPhysicsSettings.mPointVelocitySleepThreshold * time_before_sleep;
  1700. for (const BodyID *body_id = bodies_begin; body_id < bodies_end; ++body_id)
  1701. {
  1702. Body &body = mBodyManager.GetBody(*body_id);
  1703. // Update bounding box
  1704. body.CalculateWorldSpaceBoundsInternal();
  1705. // Update sleeping
  1706. all_can_sleep &= int(body.UpdateSleepStateInternal(ioContext->mSubStepDeltaTime, max_movement, time_before_sleep));
  1707. // Reset force and torque
  1708. body.GetMotionProperties()->ResetForceAndTorqueInternal();
  1709. }
  1710. // If all bodies indicate they can sleep we can deactivate them
  1711. if (all_can_sleep == int(Body::ECanSleep::CanSleep))
  1712. mBodyManager.DeactivateBodies(bodies_begin, int(bodies_end - bodies_begin));
  1713. }
  1714. else
  1715. {
  1716. JPH_PROFILE("Update Bounds");
  1717. // Update bounding box only for all other sub steps
  1718. for (const BodyID *body_id = bodies_begin; body_id < bodies_end; ++body_id)
  1719. {
  1720. Body &body = mBodyManager.GetBody(*body_id);
  1721. body.CalculateWorldSpaceBoundsInternal();
  1722. }
  1723. }
  1724. // Notify broadphase of changed objects (find ccd contacts can do linear casts in the next step, so
  1725. // we need to do this every sub step)
  1726. // Note: Shuffles the BodyID's around!!!
  1727. mBroadPhase->NotifyBodiesAABBChanged(bodies_begin, int(bodies_end - bodies_begin), false);
  1728. }
  1729. }
  1730. #ifdef JPH_STAT_COLLECTOR
  1731. void PhysicsSystem::CollectStats()
  1732. {
  1733. JPH_PROFILE_FUNCTION();
  1734. JPH_STAT_COLLECTOR_ADD("ContactConstraint.ManifoldReductionPercentage", mManifoldsBeforeReduction > 0? 100.0f * (mManifoldsBeforeReduction - mManifoldsAfterReduction) / mManifoldsBeforeReduction : 0.0f);
  1735. mBodyManager.CollectStats();
  1736. mConstraintManager.CollectStats();
  1737. mContactManager.CollectStats();
  1738. CollideConvexVsTriangles::sCollectStats();
  1739. ConvexShape::sCollectStats();
  1740. }
  1741. #endif // JPH_STAT_COLLECTOR
  1742. void PhysicsSystem::SaveState(StateRecorder &inStream) const
  1743. {
  1744. JPH_PROFILE_FUNCTION();
  1745. inStream.Write(mPreviousSubStepDeltaTime);
  1746. inStream.Write(mGravity);
  1747. mBodyManager.SaveState(inStream);
  1748. mContactManager.SaveState(inStream);
  1749. mConstraintManager.SaveState(inStream);
  1750. }
  1751. bool PhysicsSystem::RestoreState(StateRecorder &inStream)
  1752. {
  1753. JPH_PROFILE_FUNCTION();
  1754. inStream.Read(mPreviousSubStepDeltaTime);
  1755. inStream.Read(mGravity);
  1756. if (!mBodyManager.RestoreState(inStream))
  1757. return false;
  1758. if (!mContactManager.RestoreState(inStream))
  1759. return false;
  1760. if (!mConstraintManager.RestoreState(inStream))
  1761. return false;
  1762. // Update bounding boxes for all bodies in the broadphase
  1763. vector<BodyID> bodies;
  1764. for (Body *b : mBodyManager.GetBodies())
  1765. if (BodyManager::sIsValidBodyPointer(b) && b->IsInBroadPhase())
  1766. bodies.push_back(b->GetID());
  1767. if (!bodies.empty())
  1768. mBroadPhase->NotifyBodiesAABBChanged(&bodies[0], (int)bodies.size());
  1769. return true;
  1770. }
  1771. } // JPH