SphereShape.cpp 11 KB

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