VehicleCollisionTester.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  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/Vehicle/VehicleCollisionTester.h>
  6. #include <Jolt/Physics/Vehicle/VehicleConstraint.h>
  7. #include <Jolt/Physics/Collision/RayCast.h>
  8. #include <Jolt/Physics/Collision/ShapeCast.h>
  9. #include <Jolt/Physics/Collision/CastResult.h>
  10. #include <Jolt/Physics/Collision/Shape/SphereShape.h>
  11. #include <Jolt/Physics/Collision/Shape/CylinderShape.h>
  12. #include <Jolt/Physics/Collision/CollisionCollectorImpl.h>
  13. #include <Jolt/Physics/PhysicsSystem.h>
  14. JPH_NAMESPACE_BEGIN
  15. bool VehicleCollisionTesterRay::Collide(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&outBody, SubShapeID &outSubShapeID, RVec3 &outContactPosition, Vec3 &outContactNormal, float &outSuspensionLength) const
  16. {
  17. const DefaultBroadPhaseLayerFilter default_broadphase_layer_filter = inPhysicsSystem.GetDefaultBroadPhaseLayerFilter(mObjectLayer);
  18. const BroadPhaseLayerFilter &broadphase_layer_filter = mBroadPhaseLayerFilter != nullptr? *mBroadPhaseLayerFilter : default_broadphase_layer_filter;
  19. const DefaultObjectLayerFilter default_object_layer_filter = inPhysicsSystem.GetDefaultLayerFilter(mObjectLayer);
  20. const ObjectLayerFilter &object_layer_filter = mObjectLayerFilter != nullptr? *mObjectLayerFilter : default_object_layer_filter;
  21. const IgnoreSingleBodyFilter default_body_filter(inVehicleBodyID);
  22. const BodyFilter &body_filter = mBodyFilter != nullptr? *mBodyFilter : default_body_filter;
  23. const WheelSettings *wheel_settings = inVehicleConstraint.GetWheel(inWheelIndex)->GetSettings();
  24. float wheel_radius = wheel_settings->mRadius;
  25. float ray_length = wheel_settings->mSuspensionMaxLength + wheel_radius;
  26. RRayCast ray { inOrigin, ray_length * inDirection };
  27. class MyCollector : public CastRayCollector
  28. {
  29. public:
  30. MyCollector(PhysicsSystem &inPhysicsSystem, const RRayCast &inRay, Vec3Arg inUpDirection, float inCosMaxSlopeAngle) :
  31. mPhysicsSystem(inPhysicsSystem),
  32. mRay(inRay),
  33. mUpDirection(inUpDirection),
  34. mCosMaxSlopeAngle(inCosMaxSlopeAngle)
  35. {
  36. }
  37. virtual void AddHit(const RayCastResult &inResult) override
  38. {
  39. // Test if this collision is closer than the previous one
  40. if (inResult.mFraction < GetEarlyOutFraction())
  41. {
  42. // Lock the body
  43. BodyLockRead lock(mPhysicsSystem.GetBodyLockInterfaceNoLock(), inResult.mBodyID);
  44. JPH_ASSERT(lock.Succeeded()); // When this runs all bodies are locked so this should not fail
  45. const Body *body = &lock.GetBody();
  46. if (body->IsSensor())
  47. return;
  48. // Test that we're not hitting a vertical wall
  49. RVec3 contact_pos = mRay.GetPointOnRay(inResult.mFraction);
  50. Vec3 normal = body->GetWorldSpaceSurfaceNormal(inResult.mSubShapeID2, contact_pos);
  51. if (normal.Dot(mUpDirection) > mCosMaxSlopeAngle)
  52. {
  53. // Update early out fraction to this hit
  54. UpdateEarlyOutFraction(inResult.mFraction);
  55. // Get the contact properties
  56. mBody = body;
  57. mSubShapeID2 = inResult.mSubShapeID2;
  58. mContactPosition = contact_pos;
  59. mContactNormal = normal;
  60. }
  61. }
  62. }
  63. // Configuration
  64. PhysicsSystem & mPhysicsSystem;
  65. RRayCast mRay;
  66. Vec3 mUpDirection;
  67. float mCosMaxSlopeAngle;
  68. // Resulting closest collision
  69. const Body * mBody = nullptr;
  70. SubShapeID mSubShapeID2;
  71. RVec3 mContactPosition;
  72. Vec3 mContactNormal;
  73. };
  74. RayCastSettings settings;
  75. MyCollector collector(inPhysicsSystem, ray, mUp, mCosMaxSlopeAngle);
  76. inPhysicsSystem.GetNarrowPhaseQueryNoLock().CastRay(ray, settings, collector, broadphase_layer_filter, object_layer_filter, body_filter);
  77. if (collector.mBody == nullptr)
  78. return false;
  79. outBody = const_cast<Body *>(collector.mBody);
  80. outSubShapeID = collector.mSubShapeID2;
  81. outContactPosition = collector.mContactPosition;
  82. outContactNormal = collector.mContactNormal;
  83. outSuspensionLength = max(0.0f, ray_length * collector.GetEarlyOutFraction() - wheel_radius);
  84. return true;
  85. }
  86. bool VehicleCollisionTesterCastSphere::Collide(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&outBody, SubShapeID &outSubShapeID, RVec3 &outContactPosition, Vec3 &outContactNormal, float &outSuspensionLength) const
  87. {
  88. const DefaultBroadPhaseLayerFilter default_broadphase_layer_filter = inPhysicsSystem.GetDefaultBroadPhaseLayerFilter(mObjectLayer);
  89. const BroadPhaseLayerFilter &broadphase_layer_filter = mBroadPhaseLayerFilter != nullptr? *mBroadPhaseLayerFilter : default_broadphase_layer_filter;
  90. const DefaultObjectLayerFilter default_object_layer_filter = inPhysicsSystem.GetDefaultLayerFilter(mObjectLayer);
  91. const ObjectLayerFilter &object_layer_filter = mObjectLayerFilter != nullptr? *mObjectLayerFilter : default_object_layer_filter;
  92. const IgnoreSingleBodyFilter default_body_filter(inVehicleBodyID);
  93. const BodyFilter &body_filter = mBodyFilter != nullptr? *mBodyFilter : default_body_filter;
  94. SphereShape sphere(mRadius);
  95. sphere.SetEmbedded();
  96. const WheelSettings *wheel_settings = inVehicleConstraint.GetWheel(inWheelIndex)->GetSettings();
  97. float wheel_radius = wheel_settings->mRadius;
  98. float shape_cast_length = wheel_settings->mSuspensionMaxLength + wheel_radius - mRadius;
  99. RShapeCast shape_cast(&sphere, Vec3::sReplicate(1.0f), RMat44::sTranslation(inOrigin), inDirection * shape_cast_length);
  100. ShapeCastSettings settings;
  101. settings.mUseShrunkenShapeAndConvexRadius = true;
  102. settings.mReturnDeepestPoint = true;
  103. class MyCollector : public CastShapeCollector
  104. {
  105. public:
  106. MyCollector(PhysicsSystem &inPhysicsSystem, const RShapeCast &inShapeCast, Vec3Arg inUpDirection, float inCosMaxSlopeAngle) :
  107. mPhysicsSystem(inPhysicsSystem),
  108. mShapeCast(inShapeCast),
  109. mUpDirection(inUpDirection),
  110. mCosMaxSlopeAngle(inCosMaxSlopeAngle)
  111. {
  112. }
  113. virtual void AddHit(const ShapeCastResult &inResult) override
  114. {
  115. // Test if this collision is closer than the previous one
  116. if (inResult.mFraction < GetEarlyOutFraction())
  117. {
  118. // Lock the body
  119. BodyLockRead lock(mPhysicsSystem.GetBodyLockInterfaceNoLock(), inResult.mBodyID2);
  120. JPH_ASSERT(lock.Succeeded()); // When this runs all bodies are locked so this should not fail
  121. const Body *body = &lock.GetBody();
  122. if (body->IsSensor())
  123. return;
  124. // Test that we're not hitting a vertical wall
  125. Vec3 normal = -inResult.mPenetrationAxis.Normalized();
  126. if (normal.Dot(mUpDirection) > mCosMaxSlopeAngle)
  127. {
  128. // Update early out fraction to this hit
  129. UpdateEarlyOutFraction(inResult.mFraction);
  130. // Get the contact properties
  131. mBody = body;
  132. mSubShapeID2 = inResult.mSubShapeID2;
  133. mContactPosition = mShapeCast.mCenterOfMassStart.GetTranslation() + inResult.mContactPointOn2;
  134. mContactNormal = normal;
  135. }
  136. }
  137. }
  138. // Configuration
  139. PhysicsSystem & mPhysicsSystem;
  140. const RShapeCast & mShapeCast;
  141. Vec3 mUpDirection;
  142. float mCosMaxSlopeAngle;
  143. // Resulting closest collision
  144. const Body * mBody = nullptr;
  145. SubShapeID mSubShapeID2;
  146. RVec3 mContactPosition;
  147. Vec3 mContactNormal;
  148. };
  149. MyCollector collector(inPhysicsSystem, shape_cast, mUp, mCosMaxSlopeAngle);
  150. inPhysicsSystem.GetNarrowPhaseQueryNoLock().CastShape(shape_cast, settings, shape_cast.mCenterOfMassStart.GetTranslation(), collector, broadphase_layer_filter, object_layer_filter, body_filter);
  151. if (collector.mBody == nullptr)
  152. return false;
  153. outBody = const_cast<Body *>(collector.mBody);
  154. outSubShapeID = collector.mSubShapeID2;
  155. outContactPosition = collector.mContactPosition;
  156. outContactNormal = collector.mContactNormal;
  157. outSuspensionLength = max(0.0f, shape_cast_length * collector.GetEarlyOutFraction() + mRadius - wheel_radius);
  158. return true;
  159. }
  160. bool VehicleCollisionTesterCastCylinder::Collide(PhysicsSystem &inPhysicsSystem, const VehicleConstraint &inVehicleConstraint, uint inWheelIndex, RVec3Arg inOrigin, Vec3Arg inDirection, const BodyID &inVehicleBodyID, Body *&outBody, SubShapeID &outSubShapeID, RVec3 &outContactPosition, Vec3 &outContactNormal, float &outSuspensionLength) const
  161. {
  162. const DefaultBroadPhaseLayerFilter default_broadphase_layer_filter = inPhysicsSystem.GetDefaultBroadPhaseLayerFilter(mObjectLayer);
  163. const BroadPhaseLayerFilter &broadphase_layer_filter = mBroadPhaseLayerFilter != nullptr? *mBroadPhaseLayerFilter : default_broadphase_layer_filter;
  164. const DefaultObjectLayerFilter default_object_layer_filter = inPhysicsSystem.GetDefaultLayerFilter(mObjectLayer);
  165. const ObjectLayerFilter &object_layer_filter = mObjectLayerFilter != nullptr? *mObjectLayerFilter : default_object_layer_filter;
  166. const IgnoreSingleBodyFilter default_body_filter(inVehicleBodyID);
  167. const BodyFilter &body_filter = mBodyFilter != nullptr? *mBodyFilter : default_body_filter;
  168. const WheelSettings *wheel_settings = inVehicleConstraint.GetWheel(inWheelIndex)->GetSettings();
  169. float max_suspension_length = wheel_settings->mSuspensionMaxLength;
  170. // Get the wheel transform given that the cylinder rotates around the Y axis
  171. RMat44 shape_cast_start = inVehicleConstraint.GetWheelWorldTransform(inWheelIndex, Vec3::sAxisY(), Vec3::sAxisX());
  172. shape_cast_start.SetTranslation(inOrigin);
  173. // Construct a cylinder with the dimensions of the wheel
  174. float wheel_half_width = 0.5f * wheel_settings->mWidth;
  175. CylinderShape cylinder(wheel_half_width, wheel_settings->mRadius, min(wheel_half_width, wheel_settings->mRadius) * mConvexRadiusFraction);
  176. cylinder.SetEmbedded();
  177. RShapeCast shape_cast(&cylinder, Vec3::sReplicate(1.0f), shape_cast_start, inDirection * max_suspension_length);
  178. ShapeCastSettings settings;
  179. settings.mUseShrunkenShapeAndConvexRadius = true;
  180. settings.mReturnDeepestPoint = true;
  181. class MyCollector : public CastShapeCollector
  182. {
  183. public:
  184. MyCollector(PhysicsSystem &inPhysicsSystem, const RShapeCast &inShapeCast) :
  185. mPhysicsSystem(inPhysicsSystem),
  186. mShapeCast(inShapeCast)
  187. {
  188. }
  189. virtual void AddHit(const ShapeCastResult &inResult) override
  190. {
  191. // Test if this collision is closer than the previous one
  192. if (inResult.mFraction < GetEarlyOutFraction())
  193. {
  194. // Lock the body
  195. BodyLockRead lock(mPhysicsSystem.GetBodyLockInterfaceNoLock(), inResult.mBodyID2);
  196. JPH_ASSERT(lock.Succeeded()); // When this runs all bodies are locked so this should not fail
  197. const Body *body = &lock.GetBody();
  198. if (body->IsSensor())
  199. return;
  200. // Update early out fraction to this hit
  201. UpdateEarlyOutFraction(inResult.mFraction);
  202. // Get the contact properties
  203. mBody = body;
  204. mSubShapeID2 = inResult.mSubShapeID2;
  205. mContactPosition = mShapeCast.mCenterOfMassStart.GetTranslation() + inResult.mContactPointOn2;
  206. mContactNormal = -inResult.mPenetrationAxis.Normalized();
  207. }
  208. }
  209. // Configuration
  210. PhysicsSystem & mPhysicsSystem;
  211. const RShapeCast & mShapeCast;
  212. // Resulting closest collision
  213. const Body * mBody = nullptr;
  214. SubShapeID mSubShapeID2;
  215. RVec3 mContactPosition;
  216. Vec3 mContactNormal;
  217. };
  218. MyCollector collector(inPhysicsSystem, shape_cast);
  219. inPhysicsSystem.GetNarrowPhaseQueryNoLock().CastShape(shape_cast, settings, shape_cast.mCenterOfMassStart.GetTranslation(), collector, broadphase_layer_filter, object_layer_filter, body_filter);
  220. if (collector.mBody == nullptr)
  221. return false;
  222. outBody = const_cast<Body *>(collector.mBody);
  223. outSubShapeID = collector.mSubShapeID2;
  224. outContactPosition = collector.mContactPosition;
  225. outContactNormal = collector.mContactNormal;
  226. outSuspensionLength = max_suspension_length * collector.GetEarlyOutFraction();
  227. return true;
  228. }
  229. JPH_NAMESPACE_END