SphereShape.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  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/Shape/SphereShape.h>
  6. #include <Jolt/Physics/Collision/Shape/ScaleHelpers.h>
  7. #include <Jolt/Physics/Collision/Shape/GetTrianglesContext.h>
  8. #include <Jolt/Physics/Collision/RayCast.h>
  9. #include <Jolt/Physics/Collision/CastResult.h>
  10. #include <Jolt/Physics/Collision/CollidePointResult.h>
  11. #include <Jolt/Physics/Collision/TransformedShape.h>
  12. #include <Jolt/Physics/SoftBody/SoftBodyVertex.h>
  13. #include <Jolt/Geometry/RaySphere.h>
  14. #include <Jolt/Geometry/Plane.h>
  15. #include <Jolt/Core/StreamIn.h>
  16. #include <Jolt/Core/StreamOut.h>
  17. #include <Jolt/ObjectStream/TypeDeclarations.h>
  18. #ifdef JPH_DEBUG_RENDERER
  19. #include <Jolt/Renderer/DebugRenderer.h>
  20. #endif // JPH_DEBUG_RENDERER
  21. JPH_NAMESPACE_BEGIN
  22. JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(SphereShapeSettings)
  23. {
  24. JPH_ADD_BASE_CLASS(SphereShapeSettings, ConvexShapeSettings)
  25. JPH_ADD_ATTRIBUTE(SphereShapeSettings, mRadius)
  26. }
  27. ShapeSettings::ShapeResult SphereShapeSettings::Create() const
  28. {
  29. if (mCachedResult.IsEmpty())
  30. Ref<Shape> shape = new SphereShape(*this, mCachedResult);
  31. return mCachedResult;
  32. }
  33. SphereShape::SphereShape(const SphereShapeSettings &inSettings, ShapeResult &outResult) :
  34. ConvexShape(EShapeSubType::Sphere, inSettings, outResult),
  35. mRadius(inSettings.mRadius)
  36. {
  37. if (inSettings.mRadius <= 0.0f)
  38. {
  39. outResult.SetError("Invalid radius");
  40. return;
  41. }
  42. outResult.Set(this);
  43. }
  44. float SphereShape::GetScaledRadius(Vec3Arg inScale) const
  45. {
  46. JPH_ASSERT(IsValidScale(inScale));
  47. Vec3 abs_scale = inScale.Abs();
  48. return abs_scale.GetX() * mRadius;
  49. }
  50. AABox SphereShape::GetLocalBounds() const
  51. {
  52. Vec3 half_extent = Vec3::sReplicate(mRadius);
  53. return AABox(-half_extent, half_extent);
  54. }
  55. AABox SphereShape::GetWorldSpaceBounds(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale) const
  56. {
  57. float scaled_radius = GetScaledRadius(inScale);
  58. Vec3 half_extent = Vec3::sReplicate(scaled_radius);
  59. AABox bounds(-half_extent, half_extent);
  60. bounds.Translate(inCenterOfMassTransform.GetTranslation());
  61. return bounds;
  62. }
  63. class SphereShape::SphereNoConvex final : public Support
  64. {
  65. public:
  66. explicit SphereNoConvex(float inRadius) :
  67. mRadius(inRadius)
  68. {
  69. static_assert(sizeof(SphereNoConvex) <= sizeof(SupportBuffer), "Buffer size too small");
  70. JPH_ASSERT(IsAligned(this, alignof(SphereNoConvex)));
  71. }
  72. virtual Vec3 GetSupport(Vec3Arg inDirection) const override
  73. {
  74. return Vec3::sZero();
  75. }
  76. virtual float GetConvexRadius() const override
  77. {
  78. return mRadius;
  79. }
  80. private:
  81. float mRadius;
  82. };
  83. class SphereShape::SphereWithConvex final : public Support
  84. {
  85. public:
  86. explicit SphereWithConvex(float inRadius) :
  87. mRadius(inRadius)
  88. {
  89. static_assert(sizeof(SphereWithConvex) <= sizeof(SupportBuffer), "Buffer size too small");
  90. JPH_ASSERT(IsAligned(this, alignof(SphereWithConvex)));
  91. }
  92. virtual Vec3 GetSupport(Vec3Arg inDirection) const override
  93. {
  94. float len = inDirection.Length();
  95. return len > 0.0f? (mRadius / len) * inDirection : Vec3::sZero();
  96. }
  97. virtual float GetConvexRadius() const override
  98. {
  99. return 0.0f;
  100. }
  101. private:
  102. float mRadius;
  103. };
  104. const ConvexShape::Support *SphereShape::GetSupportFunction(ESupportMode inMode, SupportBuffer &inBuffer, Vec3Arg inScale) const
  105. {
  106. float scaled_radius = GetScaledRadius(inScale);
  107. switch (inMode)
  108. {
  109. case ESupportMode::IncludeConvexRadius:
  110. return new (&inBuffer) SphereWithConvex(scaled_radius);
  111. case ESupportMode::ExcludeConvexRadius:
  112. case ESupportMode::Default:
  113. return new (&inBuffer) SphereNoConvex(scaled_radius);
  114. }
  115. JPH_ASSERT(false);
  116. return nullptr;
  117. }
  118. MassProperties SphereShape::GetMassProperties() const
  119. {
  120. MassProperties p;
  121. // Calculate mass
  122. float r2 = mRadius * mRadius;
  123. p.mMass = (4.0f / 3.0f * JPH_PI) * mRadius * r2 * GetDensity();
  124. // Calculate inertia
  125. float inertia = (2.0f / 5.0f) * p.mMass * r2;
  126. p.mInertia = Mat44::sScale(inertia);
  127. return p;
  128. }
  129. Vec3 SphereShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocalSurfacePosition) const
  130. {
  131. JPH_ASSERT(inSubShapeID.IsEmpty(), "Invalid subshape ID");
  132. float len = inLocalSurfacePosition.Length();
  133. return len != 0.0f? inLocalSurfacePosition / len : Vec3::sAxisY();
  134. }
  135. void SphereShape::GetSubmergedVolume(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, const Plane &inSurface, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, RVec3Arg inBaseOffset)) const
  136. {
  137. float scaled_radius = GetScaledRadius(inScale);
  138. outTotalVolume = (4.0f / 3.0f * JPH_PI) * Cubed(scaled_radius);
  139. float distance_to_surface = inSurface.SignedDistance(inCenterOfMassTransform.GetTranslation());
  140. if (distance_to_surface >= scaled_radius)
  141. {
  142. // Above surface
  143. outSubmergedVolume = 0.0f;
  144. outCenterOfBuoyancy = Vec3::sZero();
  145. }
  146. else if (distance_to_surface <= -scaled_radius)
  147. {
  148. // Under surface
  149. outSubmergedVolume = outTotalVolume;
  150. outCenterOfBuoyancy = inCenterOfMassTransform.GetTranslation();
  151. }
  152. else
  153. {
  154. // Intersecting surface
  155. // Calculate submerged volume, see: https://en.wikipedia.org/wiki/Spherical_cap
  156. float h = scaled_radius - distance_to_surface;
  157. outSubmergedVolume = (JPH_PI / 3.0f) * Square(h) * (3.0f * scaled_radius - h);
  158. // Calculate center of buoyancy, see: http://mathworld.wolfram.com/SphericalCap.html (eq 10)
  159. float z = (3.0f / 4.0f) * Square(2.0f * scaled_radius - h) / (3.0f * scaled_radius - h);
  160. outCenterOfBuoyancy = inCenterOfMassTransform.GetTranslation() - z * inSurface.GetNormal(); // Negative normal since we want the portion under the water
  161. #ifdef JPH_DEBUG_RENDERER
  162. // Draw intersection between sphere and water plane
  163. if (sDrawSubmergedVolumes)
  164. {
  165. Vec3 circle_center = inCenterOfMassTransform.GetTranslation() - distance_to_surface * inSurface.GetNormal();
  166. float circle_radius = sqrt(Square(scaled_radius) - Square(distance_to_surface));
  167. DebugRenderer::sInstance->DrawPie(inBaseOffset + circle_center, circle_radius, inSurface.GetNormal(), inSurface.GetNormal().GetNormalizedPerpendicular(), -JPH_PI, JPH_PI, Color::sGreen, DebugRenderer::ECastShadow::Off);
  168. }
  169. #endif // JPH_DEBUG_RENDERER
  170. }
  171. #ifdef JPH_DEBUG_RENDERER
  172. // Draw center of buoyancy
  173. if (sDrawSubmergedVolumes)
  174. DebugRenderer::sInstance->DrawWireSphere(inBaseOffset + outCenterOfBuoyancy, 0.05f, Color::sRed, 1);
  175. #endif // JPH_DEBUG_RENDERER
  176. }
  177. #ifdef JPH_DEBUG_RENDERER
  178. void SphereShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const
  179. {
  180. DebugRenderer::EDrawMode draw_mode = inDrawWireframe? DebugRenderer::EDrawMode::Wireframe : DebugRenderer::EDrawMode::Solid;
  181. inRenderer->DrawUnitSphere(inCenterOfMassTransform * Mat44::sScale(mRadius * inScale.Abs().GetX()), inUseMaterialColors? GetMaterial()->GetDebugColor() : inColor, DebugRenderer::ECastShadow::On, draw_mode);
  182. }
  183. #endif // JPH_DEBUG_RENDERER
  184. bool SphereShape::CastRay(const RayCast &inRay, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) const
  185. {
  186. float fraction = RaySphere(inRay.mOrigin, inRay.mDirection, Vec3::sZero(), mRadius);
  187. if (fraction < ioHit.mFraction)
  188. {
  189. ioHit.mFraction = fraction;
  190. ioHit.mSubShapeID2 = inSubShapeIDCreator.GetID();
  191. return true;
  192. }
  193. return false;
  194. }
  195. void SphereShape::CastRay(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) const
  196. {
  197. // Test shape filter
  198. if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID()))
  199. return;
  200. float min_fraction, max_fraction;
  201. int num_results = RaySphere(inRay.mOrigin, inRay.mDirection, Vec3::sZero(), mRadius, min_fraction, max_fraction);
  202. if (num_results > 0 // Ray should intersect
  203. && max_fraction >= 0.0f // End of ray should be inside sphere
  204. && min_fraction < ioCollector.GetEarlyOutFraction()) // Start of ray should be before early out fraction
  205. {
  206. // Better hit than the current hit
  207. RayCastResult hit;
  208. hit.mBodyID = TransformedShape::sGetBodyID(ioCollector.GetContext());
  209. hit.mSubShapeID2 = inSubShapeIDCreator.GetID();
  210. // Check front side hit
  211. if (inRayCastSettings.mTreatConvexAsSolid || min_fraction > 0.0f)
  212. {
  213. hit.mFraction = max(0.0f, min_fraction);
  214. ioCollector.AddHit(hit);
  215. }
  216. // Check back side hit
  217. if (inRayCastSettings.mBackFaceMode == EBackFaceMode::CollideWithBackFaces
  218. && num_results > 1 // Ray should have 2 intersections
  219. && max_fraction < ioCollector.GetEarlyOutFraction()) // End of ray should be before early out fraction
  220. {
  221. hit.mFraction = max_fraction;
  222. ioCollector.AddHit(hit);
  223. }
  224. }
  225. }
  226. void SphereShape::CollidePoint(Vec3Arg inPoint, const SubShapeIDCreator &inSubShapeIDCreator, CollidePointCollector &ioCollector, const ShapeFilter &inShapeFilter) const
  227. {
  228. // Test shape filter
  229. if (!inShapeFilter.ShouldCollide(this, inSubShapeIDCreator.GetID()))
  230. return;
  231. if (inPoint.LengthSq() <= Square(mRadius))
  232. ioCollector.AddHit({ TransformedShape::sGetBodyID(ioCollector.GetContext()), inSubShapeIDCreator.GetID() });
  233. }
  234. void SphereShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, [[maybe_unused]] float inDeltaTime, [[maybe_unused]] Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const
  235. {
  236. Vec3 center = inCenterOfMassTransform.GetTranslation();
  237. float radius = GetScaledRadius(inScale);
  238. for (SoftBodyVertex *v = ioVertices, *sbv_end = ioVertices + inNumVertices; v < sbv_end; ++v)
  239. if (v->mInvMass > 0.0f)
  240. {
  241. // Calculate penetration
  242. Vec3 delta = v->mPosition - center;
  243. float distance = delta.Length();
  244. float penetration = radius - distance;
  245. if (penetration > v->mLargestPenetration)
  246. {
  247. v->mLargestPenetration = penetration;
  248. // Calculate contact point and normal
  249. Vec3 normal = distance > 0.0f? delta / distance : Vec3::sAxisY();
  250. Vec3 point = center + radius * normal;
  251. // Store collision
  252. v->mCollisionPlane = Plane::sFromPointAndNormal(point, normal);
  253. v->mCollidingShapeIndex = inCollidingShapeIndex;
  254. }
  255. }
  256. }
  257. void SphereShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const
  258. {
  259. Vec3 scale;
  260. Mat44 transform = inCenterOfMassTransform.Decompose(scale);
  261. TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator());
  262. ts.SetShapeScale(ScaleHelpers::MakeUniformScale(scale.Abs()));
  263. ioCollector.AddHit(ts);
  264. }
  265. void SphereShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const
  266. {
  267. float scaled_radius = GetScaledRadius(inScale);
  268. new (&ioContext) GetTrianglesContextVertexList(inPositionCOM, inRotation, Vec3::sReplicate(1.0f), Mat44::sScale(scaled_radius), sUnitSphereTriangles.data(), sUnitSphereTriangles.size(), GetMaterial());
  269. }
  270. int SphereShape::GetTrianglesNext(GetTrianglesContext &ioContext, int inMaxTrianglesRequested, Float3 *outTriangleVertices, const PhysicsMaterial **outMaterials) const
  271. {
  272. return ((GetTrianglesContextVertexList &)ioContext).GetTrianglesNext(inMaxTrianglesRequested, outTriangleVertices, outMaterials);
  273. }
  274. void SphereShape::SaveBinaryState(StreamOut &inStream) const
  275. {
  276. ConvexShape::SaveBinaryState(inStream);
  277. inStream.Write(mRadius);
  278. }
  279. void SphereShape::RestoreBinaryState(StreamIn &inStream)
  280. {
  281. ConvexShape::RestoreBinaryState(inStream);
  282. inStream.Read(mRadius);
  283. }
  284. bool SphereShape::IsValidScale(Vec3Arg inScale) const
  285. {
  286. return ConvexShape::IsValidScale(inScale) && ScaleHelpers::IsUniformScale(inScale.Abs());
  287. }
  288. void SphereShape::sRegister()
  289. {
  290. ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Sphere);
  291. f.mConstruct = []() -> Shape * { return new SphereShape; };
  292. f.mColor = Color::sGreen;
  293. }
  294. JPH_NAMESPACE_END