NarrowPhaseQuery.cpp 15 KB

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