NarrowPhaseQuery.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <Jolt/Jolt.h>
  4. #include <Jolt/Physics/Collision/NarrowPhaseQuery.h>
  5. #include <Jolt/Physics/Collision/CollisionDispatch.h>
  6. #include <Jolt/Physics/Collision/RayCast.h>
  7. #include <Jolt/Physics/Collision/AABoxCast.h>
  8. #include <Jolt/Physics/Collision/ShapeCast.h>
  9. #include <Jolt/Physics/Collision/CollideShape.h>
  10. #include <Jolt/Physics/Collision/CollisionCollectorImpl.h>
  11. #include <Jolt/Physics/Collision/CastResult.h>
  12. JPH_NAMESPACE_BEGIN
  13. bool NarrowPhaseQuery::CastRay(const RRayCast &inRay, RayCastResult &ioHit, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter) const
  14. {
  15. JPH_PROFILE_FUNCTION();
  16. class MyCollector : public RayCastBodyCollector
  17. {
  18. public:
  19. MyCollector(const RRayCast &inRay, RayCastResult &ioHit, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter) :
  20. mRay(inRay),
  21. mHit(ioHit),
  22. mBodyLockInterface(inBodyLockInterface),
  23. mBodyFilter(inBodyFilter)
  24. {
  25. UpdateEarlyOutFraction(ioHit.mFraction);
  26. }
  27. virtual void AddHit(const ResultType &inResult) override
  28. {
  29. JPH_ASSERT(inResult.mFraction < mHit.mFraction, "This hit should not have been passed on to the collector");
  30. // Only test shape if it passes the body filter
  31. if (mBodyFilter.ShouldCollide(inResult.mBodyID))
  32. {
  33. // Lock the body
  34. BodyLockRead lock(mBodyLockInterface, inResult.mBodyID);
  35. if (lock.Succeeded())
  36. {
  37. const Body &body = lock.GetBody();
  38. // Check body filter again now that we've locked the body
  39. if (mBodyFilter.ShouldCollideLocked(body))
  40. {
  41. // Collect the transformed shape
  42. TransformedShape ts = body.GetTransformedShape();
  43. // Release the lock now, we have all the info we need in the transformed shape
  44. lock.ReleaseLock();
  45. // Do narrow phase collision check
  46. if (ts.CastRay(mRay, mHit))
  47. {
  48. // Test that we didn't find a further hit by accident
  49. JPH_ASSERT(mHit.mFraction >= 0.0f && mHit.mFraction < GetEarlyOutFraction());
  50. // Update early out fraction based on narrow phase collector
  51. UpdateEarlyOutFraction(mHit.mFraction);
  52. }
  53. }
  54. }
  55. }
  56. }
  57. RRayCast mRay;
  58. RayCastResult & mHit;
  59. const BodyLockInterface & mBodyLockInterface;
  60. const BodyFilter & mBodyFilter;
  61. };
  62. // Do broadphase test, note that the broadphase uses floats so we drop precision here
  63. MyCollector collector(inRay, ioHit, *mBodyLockInterface, inBodyFilter);
  64. mBroadPhase->CastRay(RayCast(inRay), collector, inBroadPhaseLayerFilter, inObjectLayerFilter);
  65. return ioHit.mFraction <= 1.0f;
  66. }
  67. void NarrowPhaseQuery::CastRay(const RRayCast &inRay, const RayCastSettings &inRayCastSettings, CastRayCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const
  68. {
  69. JPH_PROFILE_FUNCTION();
  70. class MyCollector : public RayCastBodyCollector
  71. {
  72. public:
  73. MyCollector(const RRayCast &inRay, const RayCastSettings &inRayCastSettings, CastRayCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) :
  74. mRay(inRay),
  75. mRayCastSettings(inRayCastSettings),
  76. mCollector(ioCollector),
  77. mBodyLockInterface(inBodyLockInterface),
  78. mBodyFilter(inBodyFilter),
  79. mShapeFilter(inShapeFilter)
  80. {
  81. UpdateEarlyOutFraction(ioCollector.GetEarlyOutFraction());
  82. }
  83. virtual void AddHit(const ResultType &inResult) override
  84. {
  85. JPH_ASSERT(inResult.mFraction < mCollector.GetEarlyOutFraction(), "This hit should not have been passed on to the collector");
  86. // Only test shape if it passes the body filter
  87. if (mBodyFilter.ShouldCollide(inResult.mBodyID))
  88. {
  89. // Lock the body
  90. BodyLockRead lock(mBodyLockInterface, inResult.mBodyID);
  91. if (lock.Succeeded())
  92. {
  93. const Body &body = lock.GetBody();
  94. // Check body filter again now that we've locked the body
  95. if (mBodyFilter.ShouldCollideLocked(body))
  96. {
  97. // Collect the transformed shape
  98. TransformedShape ts = body.GetTransformedShape();
  99. // Notify collector of new body
  100. mCollector.OnBody(body);
  101. // Release the lock now, we have all the info we need in the transformed shape
  102. lock.ReleaseLock();
  103. // Do narrow phase collision check
  104. ts.CastRay(mRay, mRayCastSettings, mCollector, mShapeFilter);
  105. // Update early out fraction based on narrow phase collector
  106. UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction());
  107. }
  108. }
  109. }
  110. }
  111. RRayCast mRay;
  112. RayCastSettings mRayCastSettings;
  113. CastRayCollector & mCollector;
  114. const BodyLockInterface & mBodyLockInterface;
  115. const BodyFilter & mBodyFilter;
  116. const ShapeFilter & mShapeFilter;
  117. };
  118. // Do broadphase test, note that the broadphase uses floats so we drop precision here
  119. MyCollector collector(inRay, inRayCastSettings, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter);
  120. mBroadPhase->CastRay(RayCast(inRay), collector, inBroadPhaseLayerFilter, inObjectLayerFilter);
  121. }
  122. void NarrowPhaseQuery::CollidePoint(RVec3Arg inPoint, CollidePointCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const
  123. {
  124. JPH_PROFILE_FUNCTION();
  125. class MyCollector : public CollideShapeBodyCollector
  126. {
  127. public:
  128. MyCollector(RVec3Arg inPoint, CollidePointCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) :
  129. mPoint(inPoint),
  130. mCollector(ioCollector),
  131. mBodyLockInterface(inBodyLockInterface),
  132. mBodyFilter(inBodyFilter),
  133. mShapeFilter(inShapeFilter)
  134. {
  135. }
  136. virtual void AddHit(const ResultType &inResult) override
  137. {
  138. // Only test shape if it passes the body filter
  139. if (mBodyFilter.ShouldCollide(inResult))
  140. {
  141. // Lock the body
  142. BodyLockRead lock(mBodyLockInterface, inResult);
  143. if (lock.Succeeded())
  144. {
  145. const Body &body = lock.GetBody();
  146. // Check body filter again now that we've locked the body
  147. if (mBodyFilter.ShouldCollideLocked(body))
  148. {
  149. // Collect the transformed shape
  150. TransformedShape ts = body.GetTransformedShape();
  151. // Notify collector of new body
  152. mCollector.OnBody(body);
  153. // Release the lock now, we have all the info we need in the transformed shape
  154. lock.ReleaseLock();
  155. // Do narrow phase collision check
  156. ts.CollidePoint(mPoint, mCollector, mShapeFilter);
  157. // Update early out fraction based on narrow phase collector
  158. UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction());
  159. }
  160. }
  161. }
  162. }
  163. RVec3 mPoint;
  164. CollidePointCollector & mCollector;
  165. const BodyLockInterface & mBodyLockInterface;
  166. const BodyFilter & mBodyFilter;
  167. const ShapeFilter & mShapeFilter;
  168. };
  169. // Do broadphase test (note: truncates double to single precision since the broadphase uses single precision)
  170. MyCollector collector(inPoint, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter);
  171. mBroadPhase->CollidePoint(Vec3(inPoint), collector, inBroadPhaseLayerFilter, inObjectLayerFilter);
  172. }
  173. void NarrowPhaseQuery::CollideShape(const Shape *inShape, Vec3Arg inShapeScale, RMat44Arg inCenterOfMassTransform, const CollideShapeSettings &inCollideShapeSettings, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const
  174. {
  175. JPH_PROFILE_FUNCTION();
  176. class MyCollector : public CollideShapeBodyCollector
  177. {
  178. public:
  179. MyCollector(const Shape *inShape, Vec3Arg inShapeScale, RMat44Arg inCenterOfMassTransform, const CollideShapeSettings &inCollideShapeSettings, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) :
  180. mShape(inShape),
  181. mShapeScale(inShapeScale),
  182. mCenterOfMassTransform(inCenterOfMassTransform),
  183. mCollideShapeSettings(inCollideShapeSettings),
  184. mBaseOffset(inBaseOffset),
  185. mCollector(ioCollector),
  186. mBodyLockInterface(inBodyLockInterface),
  187. mBodyFilter(inBodyFilter),
  188. mShapeFilter(inShapeFilter)
  189. {
  190. }
  191. virtual void AddHit(const ResultType &inResult) override
  192. {
  193. // Only test shape if it passes the body filter
  194. if (mBodyFilter.ShouldCollide(inResult))
  195. {
  196. // Lock the body
  197. BodyLockRead lock(mBodyLockInterface, inResult);
  198. if (lock.Succeeded())
  199. {
  200. const Body &body = lock.GetBody();
  201. // Check body filter again now that we've locked the body
  202. if (mBodyFilter.ShouldCollideLocked(body))
  203. {
  204. // Collect the transformed shape
  205. TransformedShape ts = body.GetTransformedShape();
  206. // Notify collector of new body
  207. mCollector.OnBody(body);
  208. // Release the lock now, we have all the info we need in the transformed shape
  209. lock.ReleaseLock();
  210. // Do narrow phase collision check
  211. ts.CollideShape(mShape, mShapeScale, mCenterOfMassTransform, mCollideShapeSettings, mBaseOffset, mCollector, mShapeFilter);
  212. // Update early out fraction based on narrow phase collector
  213. UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction());
  214. }
  215. }
  216. }
  217. }
  218. const Shape * mShape;
  219. Vec3 mShapeScale;
  220. RMat44 mCenterOfMassTransform;
  221. const CollideShapeSettings & mCollideShapeSettings;
  222. RVec3 mBaseOffset;
  223. CollideShapeCollector & mCollector;
  224. const BodyLockInterface & mBodyLockInterface;
  225. const BodyFilter & mBodyFilter;
  226. const ShapeFilter & mShapeFilter;
  227. };
  228. // Calculate bounds for shape and expand by max separation distance
  229. AABox bounds = inShape->GetWorldSpaceBounds(inCenterOfMassTransform, inShapeScale);
  230. bounds.ExpandBy(Vec3::sReplicate(inCollideShapeSettings.mMaxSeparationDistance));
  231. // Do broadphase test
  232. MyCollector collector(inShape, inShapeScale, inCenterOfMassTransform, inCollideShapeSettings, inBaseOffset, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter);
  233. mBroadPhase->CollideAABox(bounds, collector, inBroadPhaseLayerFilter, inObjectLayerFilter);
  234. }
  235. void NarrowPhaseQuery::CastShape(const RShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, RVec3Arg inBaseOffset, CastShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const
  236. {
  237. JPH_PROFILE_FUNCTION();
  238. class MyCollector : public CastShapeBodyCollector
  239. {
  240. private:
  241. /// Update early out fraction based on narrow phase collector
  242. inline void PropagateEarlyOutFraction()
  243. {
  244. // The CastShapeCollector uses negative values for penetration depth so we want to clamp to the smallest positive number to keep receiving deeper hits
  245. if (mCollector.ShouldEarlyOut())
  246. ForceEarlyOut();
  247. else
  248. UpdateEarlyOutFraction(max(FLT_MIN, mCollector.GetEarlyOutFraction()));
  249. }
  250. public:
  251. MyCollector(const RShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, RVec3Arg inBaseOffset, CastShapeCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) :
  252. mShapeCast(inShapeCast),
  253. mShapeCastSettings(inShapeCastSettings),
  254. mBaseOffset(inBaseOffset),
  255. mCollector(ioCollector),
  256. mBodyLockInterface(inBodyLockInterface),
  257. mBodyFilter(inBodyFilter),
  258. mShapeFilter(inShapeFilter)
  259. {
  260. PropagateEarlyOutFraction();
  261. }
  262. virtual void AddHit(const ResultType &inResult) override
  263. {
  264. JPH_ASSERT(inResult.mFraction <= max(0.0f, mCollector.GetEarlyOutFraction()), "This hit should not have been passed on to the collector");
  265. // Only test shape if it passes the body filter
  266. if (mBodyFilter.ShouldCollide(inResult.mBodyID))
  267. {
  268. // Lock the body
  269. BodyLockRead lock(mBodyLockInterface, inResult.mBodyID);
  270. if (lock.Succeeded())
  271. {
  272. const Body &body = lock.GetBody();
  273. // Check body filter again now that we've locked the body
  274. if (mBodyFilter.ShouldCollideLocked(body))
  275. {
  276. // Collect the transformed shape
  277. TransformedShape ts = body.GetTransformedShape();
  278. // Notify collector of new body
  279. mCollector.OnBody(body);
  280. // Release the lock now, we have all the info we need in the transformed shape
  281. lock.ReleaseLock();
  282. // Do narrow phase collision check
  283. ts.CastShape(mShapeCast, mShapeCastSettings, mBaseOffset, mCollector, mShapeFilter);
  284. // Update early out fraction based on narrow phase collector
  285. PropagateEarlyOutFraction();
  286. }
  287. }
  288. }
  289. }
  290. RShapeCast mShapeCast;
  291. const ShapeCastSettings & mShapeCastSettings;
  292. RVec3 mBaseOffset;
  293. CastShapeCollector & mCollector;
  294. const BodyLockInterface & mBodyLockInterface;
  295. const BodyFilter & mBodyFilter;
  296. const ShapeFilter & mShapeFilter;
  297. };
  298. // Do broadphase test
  299. MyCollector collector(inShapeCast, inShapeCastSettings, inBaseOffset, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter);
  300. mBroadPhase->CastAABox({ inShapeCast.mShapeWorldBounds, inShapeCast.mDirection }, collector, inBroadPhaseLayerFilter, inObjectLayerFilter);
  301. }
  302. void NarrowPhaseQuery::CollectTransformedShapes(const AABox &inBox, TransformedShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const
  303. {
  304. class MyCollector : public CollideShapeBodyCollector
  305. {
  306. public:
  307. MyCollector(const AABox &inBox, TransformedShapeCollector &ioCollector, const BodyLockInterface &inBodyLockInterface, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) :
  308. mBox(inBox),
  309. mCollector(ioCollector),
  310. mBodyLockInterface(inBodyLockInterface),
  311. mBodyFilter(inBodyFilter),
  312. mShapeFilter(inShapeFilter)
  313. {
  314. }
  315. virtual void AddHit(const ResultType &inResult) override
  316. {
  317. // Only test shape if it passes the body filter
  318. if (mBodyFilter.ShouldCollide(inResult))
  319. {
  320. // Lock the body
  321. BodyLockRead lock(mBodyLockInterface, inResult);
  322. if (lock.Succeeded())
  323. {
  324. const Body &body = lock.GetBody();
  325. // Check body filter again now that we've locked the body
  326. if (mBodyFilter.ShouldCollideLocked(body))
  327. {
  328. // Collect the transformed shape
  329. TransformedShape ts = body.GetTransformedShape();
  330. // Notify collector of new body
  331. mCollector.OnBody(body);
  332. // Release the lock now, we have all the info we need in the transformed shape
  333. lock.ReleaseLock();
  334. // Do narrow phase collision check
  335. ts.CollectTransformedShapes(mBox, mCollector, mShapeFilter);
  336. // Update early out fraction based on narrow phase collector
  337. UpdateEarlyOutFraction(mCollector.GetEarlyOutFraction());
  338. }
  339. }
  340. }
  341. }
  342. const AABox & mBox;
  343. TransformedShapeCollector & mCollector;
  344. const BodyLockInterface & mBodyLockInterface;
  345. const BodyFilter & mBodyFilter;
  346. const ShapeFilter & mShapeFilter;
  347. };
  348. // Do broadphase test
  349. MyCollector collector(inBox, ioCollector, *mBodyLockInterface, inBodyFilter, inShapeFilter);
  350. mBroadPhase->CollideAABox(inBox, collector, inBroadPhaseLayerFilter, inObjectLayerFilter);
  351. }
  352. JPH_NAMESPACE_END