ConvexShape.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <Jolt.h>
  4. #include <Physics/Collision/Shape/ConvexShape.h>
  5. #include <Physics/Collision/RayCast.h>
  6. #include <Physics/Collision/ShapeCast.h>
  7. #include <Physics/Collision/CollideShape.h>
  8. #include <Physics/Collision/CastResult.h>
  9. #include <Physics/Collision/CollidePointResult.h>
  10. #include <Physics/Collision/Shape/ScaleHelpers.h>
  11. #include <Physics/Collision/Shape/GetTrianglesContext.h>
  12. #include <Physics/Collision/Shape/PolyhedronSubmergedVolumeCalculator.h>
  13. #include <Physics/Collision/TransformedShape.h>
  14. #include <Physics/Collision/CollisionDispatch.h>
  15. #include <Physics/PhysicsSettings.h>
  16. #include <Core/StatCollector.h>
  17. #include <Core/StreamIn.h>
  18. #include <Core/StreamOut.h>
  19. #include <Geometry/EPAPenetrationDepth.h>
  20. #include <Geometry/OrientedBox.h>
  21. #include <ObjectStream/TypeDeclarations.h>
  22. namespace JPH {
  23. #ifdef JPH_STAT_COLLECTOR
  24. alignas(JPH_CACHE_LINE_SIZE) atomic<int> ConvexShape::sNumCollideChecks { 0 };
  25. alignas(JPH_CACHE_LINE_SIZE) atomic<int> ConvexShape::sNumGJKChecks { 0 };
  26. alignas(JPH_CACHE_LINE_SIZE) atomic<int> ConvexShape::sNumEPAChecks { 0 };
  27. alignas(JPH_CACHE_LINE_SIZE) atomic<int> ConvexShape::sNumCollisions { 0 };
  28. #endif // JPH_STAT_COLLECTOR
  29. JPH_IMPLEMENT_SERIALIZABLE_ABSTRACT(ConvexShapeSettings)
  30. {
  31. JPH_ADD_BASE_CLASS(ConvexShapeSettings, ShapeSettings)
  32. JPH_ADD_ATTRIBUTE(ConvexShapeSettings, mDensity)
  33. JPH_ADD_ATTRIBUTE(ConvexShapeSettings, mMaterial)
  34. }
  35. const vector<Vec3> ConvexShape::sUnitSphereTriangles = []() {
  36. const int level = 2;
  37. vector<Vec3> verts;
  38. GetTrianglesContextVertexList::sCreateHalfUnitSphereTop(verts, level);
  39. GetTrianglesContextVertexList::sCreateHalfUnitSphereBottom(verts, level);
  40. return verts;
  41. }();
  42. void ConvexShape::sCollideConvexVsConvex(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector)
  43. {
  44. JPH_PROFILE_FUNCTION();
  45. JPH_IF_STAT_COLLECTOR(sNumCollideChecks++;)
  46. // Get the shapes
  47. JPH_ASSERT(inShape1->GetType() == EShapeType::Convex);
  48. JPH_ASSERT(inShape2->GetType() == EShapeType::Convex);
  49. const ConvexShape *shape1 = static_cast<const ConvexShape *>(inShape1);
  50. const ConvexShape *shape2 = static_cast<const ConvexShape *>(inShape2);
  51. // Get transforms
  52. Mat44 inverse_transform1 = inCenterOfMassTransform1.InversedRotationTranslation();
  53. Mat44 transform_2_to_1 = inverse_transform1 * inCenterOfMassTransform2;
  54. // Get bounding boxes
  55. AABox shape1_bbox = shape1->GetLocalBounds().Scaled(inScale1);
  56. shape1_bbox.ExpandBy(Vec3::sReplicate(inCollideShapeSettings.mMaxSeparationDistance));
  57. AABox shape2_bbox = shape2->GetLocalBounds().Scaled(inScale2);
  58. // Check if they overlap
  59. if (!OrientedBox(transform_2_to_1, shape2_bbox).Overlaps(shape1_bbox))
  60. return;
  61. Vec3 penetration_axis = Vec3::sAxisX(), point1, point2;
  62. EPAPenetrationDepth pen_depth;
  63. EPAPenetrationDepth::EStatus status;
  64. // Scope to limit lifetime of SupportBuffer
  65. {
  66. JPH_IF_STAT_COLLECTOR(sNumGJKChecks++;)
  67. // Create support function
  68. SupportBuffer buffer1_excl_cvx_radius, buffer2_excl_cvx_radius;
  69. const Support *shape1_excl_cvx_radius = shape1->GetSupportFunction(ConvexShape::ESupportMode::ExcludeConvexRadius, buffer1_excl_cvx_radius, inScale1);
  70. const Support *shape2_excl_cvx_radius = shape2->GetSupportFunction(ConvexShape::ESupportMode::ExcludeConvexRadius, buffer2_excl_cvx_radius, inScale2);
  71. // Transform shape 2 in the space of shape 1
  72. TransformedConvexObject<Support> transformed2_excl_cvx_radius(transform_2_to_1, *shape2_excl_cvx_radius);
  73. // Perform GJK step
  74. status = pen_depth.GetPenetrationDepthStepGJK(*shape1_excl_cvx_radius, shape1_excl_cvx_radius->GetConvexRadius() + inCollideShapeSettings.mMaxSeparationDistance, transformed2_excl_cvx_radius, shape2_excl_cvx_radius->GetConvexRadius(), inCollideShapeSettings.mCollisionTolerance, penetration_axis, point1, point2);
  75. }
  76. // Check result of collision detection
  77. switch (status)
  78. {
  79. case EPAPenetrationDepth::EStatus::Colliding:
  80. break;
  81. case EPAPenetrationDepth::EStatus::NotColliding:
  82. return;
  83. case EPAPenetrationDepth::EStatus::Indeterminate:
  84. {
  85. // Need to run expensive EPA algorithm
  86. JPH_IF_STAT_COLLECTOR(sNumEPAChecks++;)
  87. // Create support function
  88. SupportBuffer buffer1_incl_cvx_radius, buffer2_incl_cvx_radius;
  89. const Support *shape1_incl_cvx_radius = shape1->GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer1_incl_cvx_radius, inScale1);
  90. const Support *shape2_incl_cvx_radius = shape2->GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer2_incl_cvx_radius, inScale2);
  91. // Add separation distance
  92. AddConvexRadius<Support> shape1_add_max_separation_distance(*shape1_incl_cvx_radius, inCollideShapeSettings.mMaxSeparationDistance);
  93. // Transform shape 2 in the space of shape 1
  94. TransformedConvexObject<Support> transformed2_incl_cvx_radius(transform_2_to_1, *shape2_incl_cvx_radius);
  95. // Perform EPA step
  96. if (!pen_depth.GetPenetrationDepthStepEPA(shape1_add_max_separation_distance, transformed2_incl_cvx_radius, inCollideShapeSettings.mPenetrationTolerance, penetration_axis, point1, point2))
  97. return;
  98. break;
  99. }
  100. }
  101. // Check if the penetration is bigger than the early out fraction
  102. float penetration_depth = (point2 - point1).Length() - inCollideShapeSettings.mMaxSeparationDistance;
  103. if (-penetration_depth >= ioCollector.GetEarlyOutFraction())
  104. return;
  105. // Correct point1 for the added separation distance
  106. float penetration_axis_len = penetration_axis.Length();
  107. if (penetration_axis_len > 0.0f)
  108. point1 -= penetration_axis * (inCollideShapeSettings.mMaxSeparationDistance / penetration_axis_len);
  109. // Convert to world space
  110. point1 = inCenterOfMassTransform1 * point1;
  111. point2 = inCenterOfMassTransform1 * point2;
  112. Vec3 penetration_axis_world = inCenterOfMassTransform1.Multiply3x3(penetration_axis);
  113. // Create collision result
  114. CollideShapeResult result(point1, point2, penetration_axis_world, penetration_depth, inSubShapeIDCreator1.GetID(), inSubShapeIDCreator2.GetID(), TransformedShape::sGetBodyID(ioCollector.GetContext()));
  115. // Gather faces
  116. if (inCollideShapeSettings.mCollectFacesMode == ECollectFacesMode::CollectFaces)
  117. {
  118. // Get supporting face of shape 1
  119. shape1->GetSupportingFace(-penetration_axis, inScale1, result.mShape1Face);
  120. // Convert to world space
  121. for (Vec3 &p : result.mShape1Face)
  122. p = inCenterOfMassTransform1 * p;
  123. // Get supporting face of shape 2
  124. shape2->GetSupportingFace(transform_2_to_1.Multiply3x3Transposed(penetration_axis), inScale2, result.mShape2Face);
  125. // Convert to world space
  126. for (Vec3 &p : result.mShape2Face)
  127. p = inCenterOfMassTransform2 * p;
  128. }
  129. JPH_IF_STAT_COLLECTOR(sNumCollisions++;)
  130. // Notify the collector
  131. ioCollector.AddHit(result);
  132. }
  133. void ConvexShape::sCastConvexVsShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, const Shape *inShape, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector)
  134. {
  135. inShape->CastShape(inShapeCast, inShapeCastSettings, inScale, inShapeFilter, inCenterOfMassTransform2, inSubShapeIDCreator1, inSubShapeIDCreator2, ioCollector);
  136. }
  137. bool ConvexShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const
  138. {
  139. // Note: This is a fallback routine, most convex shapes should implement a more performant version!
  140. JPH_PROFILE_FUNCTION();
  141. // Create support function
  142. SupportBuffer buffer;
  143. const Support *support = GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer, Vec3::sReplicate(1.0f));
  144. // Cast ray
  145. GJKClosestPoint gjk;
  146. if (gjk.CastRay(inRay.mOrigin, inRay.mDirection, cDefaultCollisionTolerance, *support, ioHit.mFraction))
  147. {
  148. ioHit.mSubShapeID2 = inSubShapeIDCreator.GetID();
  149. return true;
  150. }
  151. return false;
  152. }
  153. void ConvexShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector) const
  154. {
  155. // Note: This is a fallback routine, most convex shapes should implement a more performant version!
  156. // First do a normal raycast, limited to the early out fraction
  157. RayCastResult hit;
  158. hit.mFraction = ioCollector.GetEarlyOutFraction();
  159. if (CastRay(inRay, inSubShapeIDCreator, hit))
  160. {
  161. // Check front side
  162. if (inRayCastSettings.mTreatConvexAsSolid || hit.mFraction > 0.0f)
  163. {
  164. hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext());
  165. ioCollector.AddHit(hit);
  166. }
  167. // Check if we want back facing hits and the collector still accepts additional hits
  168. if (inRayCastSettings.mBackFaceMode == EBackFaceMode::CollideWithBackFaces && !ioCollector.ShouldEarlyOut())
  169. {
  170. // Invert the ray, going from the early out fraction back to the fraction where we found our forward hit
  171. float start_fraction = min(1.0f, ioCollector.GetEarlyOutFraction());
  172. float delta_fraction = hit.mFraction - start_fraction;
  173. if (delta_fraction < 0.0f)
  174. {
  175. RayCast inverted_ray { inRay.mOrigin + start_fraction * inRay.mDirection, delta_fraction * inRay.mDirection };
  176. // Cast another ray
  177. RayCastResult inverted_hit;
  178. inverted_hit.mFraction = 1.0f;
  179. if (CastRay(inverted_ray, inSubShapeIDCreator, inverted_hit)
  180. && inverted_hit.mFraction > 0.0f) // Ignore hits with fraction 0, this means the ray ends inside the object and we don't want to report it as a back facing hit
  181. {
  182. // Invert fraction and rescale it to the fraction of the original ray
  183. inverted_hit.mFraction = hit.mFraction + (inverted_hit.mFraction - 1.0f) * delta_fraction;
  184. inverted_hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext());
  185. ioCollector.AddHit(inverted_hit);
  186. }
  187. }
  188. }
  189. }
  190. }
  191. void ConvexShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector) const
  192. {
  193. // First test bounding box
  194. if (GetLocalBounds().Contains(inPoint))
  195. {
  196. // Create support function
  197. SupportBuffer buffer;
  198. const Support *support = GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer, Vec3::sReplicate(1.0f));
  199. // Create support function for point
  200. PointConvexSupport point { inPoint };
  201. // Test intersection
  202. GJKClosestPoint gjk;
  203. Vec3 v = inPoint;
  204. if (gjk.Intersects(*support, point, cDefaultCollisionTolerance, v))
  205. ioCollector.AddHit({ TransformedShape::sGetBodyID(ioCollector.GetContext()), inSubShapeIDCreator.GetID() });
  206. }
  207. }
  208. void ConvexShape::CastShape(const ShapeCast &inShapeCast, const ShapeCastSettings &inShapeCastSettings, Vec3Arg inScale, const ShapeFilter &inShapeFilter, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, CastShapeCollector &ioCollector) const
  209. {
  210. JPH_PROFILE_FUNCTION();
  211. // Only supported for convex shapes
  212. JPH_ASSERT(inShapeCast.mShape->GetType() == EShapeType::Convex);
  213. const ConvexShape *cast_shape = static_cast<const ConvexShape *>(inShapeCast.mShape);
  214. // Determine if we want to use the actual shape or a shrunken shape with convex radius
  215. ConvexShape::ESupportMode support_mode = inShapeCastSettings.mUseShrunkenShapeAndConvexRadius? ConvexShape::ESupportMode::ExcludeConvexRadius : ConvexShape::ESupportMode::IncludeConvexRadius;
  216. // Create support function for shape to cast
  217. SupportBuffer cast_buffer;
  218. const Support *cast_support = cast_shape->GetSupportFunction(support_mode, cast_buffer, inShapeCast.mScale);
  219. // Create support function for target shape
  220. SupportBuffer target_buffer;
  221. const Support *target_support = GetSupportFunction(support_mode, target_buffer, inScale);
  222. // Do a raycast against the result
  223. EPAPenetrationDepth epa;
  224. float fraction = ioCollector.GetEarlyOutFraction();
  225. Vec3 contact_point_a, contact_point_b, contact_normal;
  226. if (epa.CastShape(inShapeCast.mCenterOfMassStart, inShapeCast.mDirection, inShapeCastSettings.mCollisionTolerance, inShapeCastSettings.mPenetrationTolerance, *cast_support, *target_support, cast_support->GetConvexRadius(), target_support->GetConvexRadius(), inShapeCastSettings.mReturnDeepestPoint, fraction, contact_point_a, contact_point_b, contact_normal))
  227. {
  228. // Test if backfacing
  229. if (inShapeCastSettings.mBackFaceModeConvex == EBackFaceMode::CollideWithBackFaces
  230. || contact_normal.Dot(inShapeCast.mDirection) > 0.0f)
  231. {
  232. // Convert to world space
  233. contact_point_a = inCenterOfMassTransform2 * contact_point_a;
  234. contact_point_b = inCenterOfMassTransform2 * contact_point_b;
  235. Vec3 contact_normal_world = inCenterOfMassTransform2.Multiply3x3(contact_normal);
  236. ShapeCastResult result(fraction, contact_point_a, contact_point_b, contact_normal_world, false, inSubShapeIDCreator1.GetID(), inSubShapeIDCreator2.GetID(), TransformedShape::sGetBodyID(ioCollector.GetContext()));
  237. // Gather faces
  238. if (inShapeCastSettings.mCollectFacesMode == ECollectFacesMode::CollectFaces)
  239. {
  240. // Get supporting face of shape 1
  241. Mat44 transform_1_to_2 = inShapeCast.mCenterOfMassStart;
  242. transform_1_to_2.SetTranslation(transform_1_to_2.GetTranslation() + fraction * inShapeCast.mDirection);
  243. cast_shape->GetSupportingFace(transform_1_to_2.Multiply3x3Transposed(-contact_normal), inShapeCast.mScale, result.mShape1Face);
  244. // Convert to world space
  245. Mat44 transform_1_to_world = inCenterOfMassTransform2 * transform_1_to_2;
  246. for (Vec3 &p : result.mShape1Face)
  247. p = transform_1_to_world * p;
  248. // Get supporting face of shape 2
  249. GetSupportingFace(contact_normal, inScale, result.mShape2Face);
  250. // Convert to world space
  251. for (Vec3 &p : result.mShape2Face)
  252. p = inCenterOfMassTransform2 * p;
  253. }
  254. ioCollector.AddHit(result);
  255. }
  256. }
  257. }
  258. class ConvexShape::CSGetTrianglesContext
  259. {
  260. public:
  261. CSGetTrianglesContext(const ConvexShape *inShape, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) :
  262. mLocalToWorld(Mat44::sRotationTranslation(inRotation, inPositionCOM) * Mat44::sScale(inScale)),
  263. mIsInsideOut(ScaleHelpers::IsInsideOut(inScale))
  264. {
  265. mSupport = inShape->GetSupportFunction(ESupportMode::IncludeConvexRadius, mSupportBuffer, Vec3::sReplicate(1.0f));
  266. }
  267. SupportBuffer mSupportBuffer;
  268. const Support * mSupport;
  269. Mat44 mLocalToWorld;
  270. bool mIsInsideOut;
  271. size_t mCurrentVertex = 0;
  272. };
  273. void ConvexShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const
  274. {
  275. static_assert(sizeof(CSGetTrianglesContext) <= sizeof(GetTrianglesContext), "GetTrianglesContext too small");
  276. JPH_ASSERT(IsAligned(&ioContext, alignof(CSGetTrianglesContext)));
  277. new (&ioContext) CSGetTrianglesContext(this, inPositionCOM, inRotation, inScale);
  278. }
  279. int ConvexShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const
  280. {
  281. JPH_ASSERT(inMaxTrianglesRequested >= cGetTrianglesMinTrianglesRequested);
  282. CSGetTrianglesContext &context = (CSGetTrianglesContext &)ioContext;
  283. int total_num_vertices = min(inMaxTrianglesRequested * 3, int(sUnitSphereTriangles.size() - context.mCurrentVertex));
  284. if (context.mIsInsideOut)
  285. {
  286. // Store triangles flipped
  287. for (const Vec3 *v = sUnitSphereTriangles.data() + context.mCurrentVertex, *v_end = v + total_num_vertices; v < v_end; v += 3)
  288. {
  289. (context.mLocalToWorld * context.mSupport->GetSupport(v[0])).StoreFloat3(outTriangleVertices++);
  290. (context.mLocalToWorld * context.mSupport->GetSupport(v[2])).StoreFloat3(outTriangleVertices++);
  291. (context.mLocalToWorld * context.mSupport->GetSupport(v[1])).StoreFloat3(outTriangleVertices++);
  292. }
  293. }
  294. else
  295. {
  296. // Store triangles
  297. for (const Vec3 *v = sUnitSphereTriangles.data() + context.mCurrentVertex, *v_end = v + total_num_vertices; v < v_end; v += 3)
  298. {
  299. (context.mLocalToWorld * context.mSupport->GetSupport(v[0])).StoreFloat3(outTriangleVertices++);
  300. (context.mLocalToWorld * context.mSupport->GetSupport(v[1])).StoreFloat3(outTriangleVertices++);
  301. (context.mLocalToWorld * context.mSupport->GetSupport(v[2])).StoreFloat3(outTriangleVertices++);
  302. }
  303. }
  304. context.mCurrentVertex += total_num_vertices;
  305. int total_num_triangles = total_num_vertices / 3;
  306. // Store materials
  307. if (outMaterials != nullptr)
  308. {
  309. const PhysicsMaterial *material = GetMaterial();
  310. for (const PhysicsMaterial **m = outMaterials, **m_end = outMaterials + total_num_triangles; m < m_end; ++m)
  311. *m = material;
  312. }
  313. return total_num_triangles;
  314. }
  315. void ConvexShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy) const
  316. {
  317. // Calculate total volume
  318. Vec3 abs_scale = inScale.Abs();
  319. Vec3 extent = GetLocalBounds().GetExtent() * abs_scale;
  320. outTotalVolume = 8.0f * extent.GetX() * extent.GetY() * extent.GetZ();
  321. // Points of the bounding box
  322. Vec3 points[] =
  323. {
  324. Vec3(-1, -1, -1),
  325. Vec3( 1, -1, -1),
  326. Vec3(-1, 1, -1),
  327. Vec3( 1, 1, -1),
  328. Vec3(-1, -1, 1),
  329. Vec3( 1, -1, 1),
  330. Vec3(-1, 1, 1),
  331. Vec3( 1, 1, 1),
  332. };
  333. // Faces of the bounding box
  334. using Face = int[5];
  335. #define MAKE_FACE(a, b, c, d) { a, b, c, d, ((1 << a) | (1 << b) | (1 << c) | (1 << d)) } // Last int is a bit mask that indicates which indices are used
  336. Face faces[] =
  337. {
  338. MAKE_FACE(0, 2, 3, 1),
  339. MAKE_FACE(4, 6, 2, 0),
  340. MAKE_FACE(4, 5, 7, 6),
  341. MAKE_FACE(1, 3, 7, 5),
  342. MAKE_FACE(2, 6, 7, 3),
  343. MAKE_FACE(0, 1, 5, 4),
  344. };
  345. PolyhedronSubmergedVolumeCalculator::Point *buffer = (PolyhedronSubmergedVolumeCalculator::Point *)JPH_STACK_ALLOC(8 * sizeof(PolyhedronSubmergedVolumeCalculator::Point));
  346. PolyhedronSubmergedVolumeCalculator submerged_vol_calc(inCenterOfMassTransform * Mat44::sScale(extent), points, sizeof(Vec3), 8, inSurface, buffer);
  347. if (submerged_vol_calc.AreAllAbove())
  348. {
  349. // We're above the water
  350. outSubmergedVolume = 0.0f;
  351. outCenterOfBuoyancy = Vec3::sZero();
  352. }
  353. else if (submerged_vol_calc.AreAllBelow())
  354. {
  355. // We're fully submerged
  356. outSubmergedVolume = outTotalVolume;
  357. outCenterOfBuoyancy = inCenterOfMassTransform.GetTranslation();
  358. }
  359. else
  360. {
  361. // Calculate submerged volume
  362. int reference_point_bit = 1 << submerged_vol_calc.GetReferencePointIdx();
  363. for (const Face &f : faces)
  364. {
  365. // Test if this face includes the reference point
  366. if ((f[4] & reference_point_bit) == 0)
  367. {
  368. // Triangulate the face (a quad)
  369. submerged_vol_calc.AddFace(f[0], f[1], f[2]);
  370. submerged_vol_calc.AddFace(f[0], f[2], f[3]);
  371. }
  372. }
  373. submerged_vol_calc.GetResult(outSubmergedVolume, outCenterOfBuoyancy);
  374. }
  375. }
  376. #ifdef JPH_DEBUG_RENDERER
  377. void ConvexShape::DrawGetSupportFunction(DebugRenderer *inRenderer, Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inDrawSupportDirection) const
  378. {
  379. DebugRenderer::GeometryRef &geometry = mGetSupportFunctionGeometry[inScale];
  380. if (geometry == nullptr)
  381. {
  382. SupportBuffer buffer;
  383. const Support *support = GetSupportFunction(ESupportMode::ExcludeConvexRadius, buffer, inScale);
  384. AddConvexRadius<Support> add_convex(*support, support->GetConvexRadius());
  385. geometry = inRenderer->CreateTriangleGeometryForConvex([&add_convex](Vec3Arg inDirection) { return add_convex.GetSupport(inDirection); });
  386. }
  387. AABox bounds = geometry->mBounds.Transformed(inCenterOfMassTransform);
  388. float lod_scale_sq = geometry->mBounds.GetExtent().LengthSq();
  389. inRenderer->DrawGeometry(inCenterOfMassTransform, bounds, lod_scale_sq, inColor, geometry);
  390. if (inDrawSupportDirection)
  391. {
  392. // Get the support function with convex radius
  393. SupportBuffer buffer;
  394. const Support *support = GetSupportFunction(ESupportMode::ExcludeConvexRadius, buffer, inScale);
  395. AddConvexRadius<Support> add_convex(*support, support->GetConvexRadius());
  396. // Iterate on all directions and draw the support point and an arrow in the direction that was sampled to test if the support points make sense
  397. for (Vec3 v : Vec3::sUnitSphere)
  398. {
  399. Vec3 direction = 0.05f * v;
  400. Vec3 pos = add_convex.GetSupport(direction);
  401. Vec3 from = inCenterOfMassTransform * pos;
  402. Vec3 to = inCenterOfMassTransform * (pos + direction);
  403. inRenderer->DrawMarker(from, Color::sWhite, 0.001f);
  404. inRenderer->DrawArrow(from, to, Color::sWhite, 0.001f);
  405. }
  406. }
  407. }
  408. void ConvexShape::DrawGetSupportingFace(DebugRenderer *inRenderer, Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const
  409. {
  410. // Sample directions and map which faces belong to which directions
  411. using FaceToDirection = unordered_map<SupportingFace, vector<Vec3>>;
  412. FaceToDirection faces;
  413. for (Vec3 v : Vec3::sUnitSphere)
  414. {
  415. Vec3 direction = 0.05f * v;
  416. SupportingFace face;
  417. GetSupportingFace(direction, inScale, face);
  418. if (!face.empty())
  419. {
  420. JPH_ASSERT(face.size() >= 2, "The GetSupportingFace function should either return nothing or at least an edge");
  421. faces[face].push_back(direction);
  422. }
  423. }
  424. // Draw each face in a unique color and draw corresponding directions
  425. int color_it = 0;
  426. for (FaceToDirection::value_type &ftd : faces)
  427. {
  428. Color color = Color::sGetDistinctColor(color_it++);
  429. // Create copy of face (key in map is read only)
  430. SupportingFace face = ftd.first;
  431. // Displace the face a little bit forward so it is easier to see
  432. Vec3 normal = face.size() >= 3? (face[2] - face[1]).Cross(face[0] - face[1]).Normalized() : Vec3::sZero();
  433. Vec3 displacement = 0.001f * normal;
  434. // Transform face to world space and calculate center of mass
  435. Vec3 com = Vec3::sZero();
  436. for (Vec3 &v : face)
  437. {
  438. v = inCenterOfMassTransform * (v + displacement);
  439. com += v;
  440. }
  441. com /= (float)face.size();
  442. // Draw the polygon and directions
  443. inRenderer->DrawWirePolygon(face, color, face.size() >= 3? 0.001f : 0.0f);
  444. if (face.size() >= 3)
  445. inRenderer->DrawArrow(com, com + inCenterOfMassTransform.Multiply3x3(normal), color, 0.01f);
  446. for (Vec3 &v : ftd.second)
  447. inRenderer->DrawArrow(com, com + inCenterOfMassTransform.Multiply3x3(-v), color, 0.001f);
  448. }
  449. }
  450. #endif // JPH_DEBUG_RENDERER
  451. #ifdef JPH_STAT_COLLECTOR
  452. void ConvexShape::sResetStats()
  453. {
  454. sNumCollideChecks = 0;
  455. sNumGJKChecks = 0;
  456. sNumEPAChecks = 0;
  457. sNumCollisions = 0;
  458. }
  459. void ConvexShape::sCollectStats()
  460. {
  461. JPH_PROFILE_FUNCTION();
  462. JPH_STAT_COLLECTOR_ADD("ConvexVsConvex.NumChecks", int(sNumCollideChecks));
  463. JPH_STAT_COLLECTOR_ADD("ConvexVsConvex.NumCollisions", int(sNumCollisions));
  464. if (sNumCollideChecks > 0)
  465. {
  466. JPH_STAT_COLLECTOR_ADD("ConvexVsConvex.GJKCheckPercentage", 100.0f * sNumGJKChecks / sNumCollideChecks);
  467. JPH_STAT_COLLECTOR_ADD("ConvexVsConvex.EPACheckPercentage", 100.0f * sNumEPAChecks / sNumCollideChecks);
  468. }
  469. }
  470. #endif // JPH_STAT_COLLECTOR
  471. void ConvexShape::SaveBinaryState(StreamOut &inStream) const
  472. {
  473. Shape::SaveBinaryState(inStream);
  474. inStream.Write(mDensity);
  475. }
  476. void ConvexShape::RestoreBinaryState(StreamIn &inStream)
  477. {
  478. Shape::RestoreBinaryState(inStream);
  479. inStream.Read(mDensity);
  480. }
  481. void ConvexShape::SaveMaterialState(PhysicsMaterialList &outMaterials) const
  482. {
  483. outMaterials.clear();
  484. outMaterials.push_back(mMaterial);
  485. }
  486. void ConvexShape::RestoreMaterialState(const PhysicsMaterialRefC *inMaterials, uint inNumMaterials)
  487. {
  488. JPH_ASSERT(inNumMaterials == 1);
  489. mMaterial = inMaterials[0];
  490. }
  491. void ConvexShape::sRegister()
  492. {
  493. for (EShapeSubType s1 : sConvexSubShapeTypes)
  494. {
  495. for (EShapeSubType s2 : sConvexSubShapeTypes)
  496. CollisionDispatch::sRegisterCollideShape(s1, s2, sCollideConvexVsConvex);
  497. CollisionDispatch::sRegisterCastShape(s1, sCastConvexVsShape);
  498. }
  499. }
  500. } // JPH