Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Collision/Shape/PolyhedronSubmergedVolumeCalculator.h
9913 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#pragma once56#include <Jolt/Geometry/Plane.h>7#ifdef JPH_DEBUG_RENDERER8#include <Jolt/Renderer/DebugRenderer.h>9#endif // JPH_DEBUG_RENDERER1011JPH_NAMESPACE_BEGIN1213/// This class calculates the intersection between a fluid surface and a polyhedron and returns the submerged volume and its center of buoyancy14/// Construct this class and then one by one add all faces of the polyhedron using the AddFace function. After all faces have been added the result15/// can be gotten through GetResult.16class PolyhedronSubmergedVolumeCalculator17{18private:19// Calculate submerged volume * 6 and center of mass * 4 for a tetrahedron with 4 vertices submerged20// inV1 .. inV4 are submerged21inline static void sTetrahedronVolume4(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, Vec3Arg inV4, float &outVolumeTimes6, Vec3 &outCenterTimes4)22{23// Calculate center of mass and mass of this tetrahedron,24// see: https://en.wikipedia.org/wiki/Tetrahedron#Volume25outVolumeTimes6 = max((inV1 - inV4).Dot((inV2 - inV4).Cross(inV3 - inV4)), 0.0f); // All contributions should be positive because we use a reference point that is on the surface of the hull26outCenterTimes4 = inV1 + inV2 + inV3 + inV4;27}2829// Get the intersection point with a plane.30// inV1 is inD1 distance away from the plane, inV2 is inD2 distance away from the plane31inline static Vec3 sGetPlaneIntersection(Vec3Arg inV1, float inD1, Vec3Arg inV2, float inD2)32{33JPH_ASSERT(Sign(inD1) != Sign(inD2), "Assuming both points are on opposite ends of the plane");34float delta = inD1 - inD2;35if (abs(delta) < 1.0e-6f)36return inV1; // Parallel to plane, just pick a point37else38return inV1 + inD1 * (inV2 - inV1) / delta;39}4041// Calculate submerged volume * 6 and center of mass * 4 for a tetrahedron with 1 vertex submerged42// inV1 is submerged, inV2 .. inV4 are not43// inD1 .. inD4 are the distances from the points to the plane44inline JPH_IF_NOT_DEBUG_RENDERER(static) void sTetrahedronVolume1(Vec3Arg inV1, float inD1, Vec3Arg inV2, float inD2, Vec3Arg inV3, float inD3, Vec3Arg inV4, float inD4, float &outVolumeTimes6, Vec3 &outCenterTimes4)45{46// A tetrahedron with 1 point submerged is cut along 3 edges forming a new tetrahedron47Vec3 v2 = sGetPlaneIntersection(inV1, inD1, inV2, inD2);48Vec3 v3 = sGetPlaneIntersection(inV1, inD1, inV3, inD3);49Vec3 v4 = sGetPlaneIntersection(inV1, inD1, inV4, inD4);5051#ifdef JPH_DEBUG_RENDERER52// Draw intersection between tetrahedron and surface53if (Shape::sDrawSubmergedVolumes)54{55RVec3 v2w = mBaseOffset + v2;56RVec3 v3w = mBaseOffset + v3;57RVec3 v4w = mBaseOffset + v4;5859DebugRenderer::sInstance->DrawTriangle(v4w, v3w, v2w, Color::sGreen);60DebugRenderer::sInstance->DrawWireTriangle(v4w, v3w, v2w, Color::sWhite);61}62#endif // JPH_DEBUG_RENDERER6364sTetrahedronVolume4(inV1, v2, v3, v4, outVolumeTimes6, outCenterTimes4);65}6667// Calculate submerged volume * 6 and center of mass * 4 for a tetrahedron with 2 vertices submerged68// inV1, inV2 are submerged, inV3, inV4 are not69// inD1 .. inD4 are the distances from the points to the plane70inline JPH_IF_NOT_DEBUG_RENDERER(static) void sTetrahedronVolume2(Vec3Arg inV1, float inD1, Vec3Arg inV2, float inD2, Vec3Arg inV3, float inD3, Vec3Arg inV4, float inD4, float &outVolumeTimes6, Vec3 &outCenterTimes4)71{72// A tetrahedron with 2 points submerged is cut along 4 edges forming a quad73Vec3 c = sGetPlaneIntersection(inV1, inD1, inV3, inD3);74Vec3 d = sGetPlaneIntersection(inV1, inD1, inV4, inD4);75Vec3 e = sGetPlaneIntersection(inV2, inD2, inV4, inD4);76Vec3 f = sGetPlaneIntersection(inV2, inD2, inV3, inD3);7778#ifdef JPH_DEBUG_RENDERER79// Draw intersection between tetrahedron and surface80if (Shape::sDrawSubmergedVolumes)81{82RVec3 cw = mBaseOffset + c;83RVec3 dw = mBaseOffset + d;84RVec3 ew = mBaseOffset + e;85RVec3 fw = mBaseOffset + f;8687DebugRenderer::sInstance->DrawTriangle(cw, ew, dw, Color::sGreen);88DebugRenderer::sInstance->DrawTriangle(cw, fw, ew, Color::sGreen);89DebugRenderer::sInstance->DrawWireTriangle(cw, ew, dw, Color::sWhite);90DebugRenderer::sInstance->DrawWireTriangle(cw, fw, ew, Color::sWhite);91}92#endif // JPH_DEBUG_RENDERER9394// We pick point c as reference (which is on the cut off surface)95// This leaves us with three tetrahedrons to sum up (any faces that are in the same plane as c will have zero volume)96Vec3 center1, center2, center3;97float volume1, volume2, volume3;98sTetrahedronVolume4(e, f, inV2, c, volume1, center1);99sTetrahedronVolume4(e, inV1, d, c, volume2, center2);100sTetrahedronVolume4(e, inV2, inV1, c, volume3, center3);101102// Tally up the totals103outVolumeTimes6 = volume1 + volume2 + volume3;104outCenterTimes4 = outVolumeTimes6 > 0.0f? (volume1 * center1 + volume2 * center2 + volume3 * center3) / outVolumeTimes6 : Vec3::sZero();105}106107// Calculate submerged volume * 6 and center of mass * 4 for a tetrahedron with 3 vertices submerged108// inV1, inV2, inV3 are submerged, inV4 is not109// inD1 .. inD4 are the distances from the points to the plane110inline JPH_IF_NOT_DEBUG_RENDERER(static) void sTetrahedronVolume3(Vec3Arg inV1, float inD1, Vec3Arg inV2, float inD2, Vec3Arg inV3, float inD3, Vec3Arg inV4, float inD4, float &outVolumeTimes6, Vec3 &outCenterTimes4)111{112// A tetrahedron with 1 point above the surface is cut along 3 edges forming a new tetrahedron113Vec3 v1 = sGetPlaneIntersection(inV1, inD1, inV4, inD4);114Vec3 v2 = sGetPlaneIntersection(inV2, inD2, inV4, inD4);115Vec3 v3 = sGetPlaneIntersection(inV3, inD3, inV4, inD4);116117#ifdef JPH_DEBUG_RENDERER118// Draw intersection between tetrahedron and surface119if (Shape::sDrawSubmergedVolumes)120{121RVec3 v1w = mBaseOffset + v1;122RVec3 v2w = mBaseOffset + v2;123RVec3 v3w = mBaseOffset + v3;124125DebugRenderer::sInstance->DrawTriangle(v3w, v2w, v1w, Color::sGreen);126DebugRenderer::sInstance->DrawWireTriangle(v3w, v2w, v1w, Color::sWhite);127}128#endif // JPH_DEBUG_RENDERER129130Vec3 dry_center, total_center;131float dry_volume, total_volume;132133// We first calculate the part that is above the surface134sTetrahedronVolume4(v1, v2, v3, inV4, dry_volume, dry_center);135136// Calculate the total volume137sTetrahedronVolume4(inV1, inV2, inV3, inV4, total_volume, total_center);138139// From this we can calculate the center and volume of the submerged part140outVolumeTimes6 = max(total_volume - dry_volume, 0.0f);141outCenterTimes4 = outVolumeTimes6 > 0.0f? (total_center * total_volume - dry_center * dry_volume) / outVolumeTimes6 : Vec3::sZero();142}143144public:145/// A helper class that contains cached information about a polyhedron vertex146class Point147{148public:149Vec3 mPosition; ///< World space position of vertex150float mDistanceToSurface; ///< Signed distance to the surface (> 0 is above, < 0 is below)151bool mAboveSurface; ///< If the point is above the surface (mDistanceToSurface > 0)152};153154/// Constructor155/// @param inTransform Transform to transform all incoming points with156/// @param inPoints Array of points that are part of the polyhedron157/// @param inPointStride Amount of bytes between each point (should usually be sizeof(Vec3))158/// @param inNumPoints The amount of points159/// @param inSurface The plane that forms the fluid surface (normal should point up)160/// @param ioBuffer A temporary buffer of Point's that should have inNumPoints entries and should stay alive while this class is alive161#ifdef JPH_DEBUG_RENDERER162/// @param inBaseOffset The offset to transform inTransform to world space (in double precision mode this can be used to shift the whole operation closer to the origin). Only used for debug drawing.163#endif // JPH_DEBUG_RENDERER164PolyhedronSubmergedVolumeCalculator(const Mat44 &inTransform, const Vec3 *inPoints, int inPointStride, int inNumPoints, const Plane &inSurface, Point *ioBuffer165#ifdef JPH_DEBUG_RENDERER // Not using JPH_IF_DEBUG_RENDERER for Doxygen166, RVec3 inBaseOffset167#endif // JPH_DEBUG_RENDERER168) :169mPoints(ioBuffer)170#ifdef JPH_DEBUG_RENDERER171, mBaseOffset(inBaseOffset)172#endif // JPH_DEBUG_RENDERER173{174// Convert the points to world space and determine the distance to the surface175float reference_dist = FLT_MAX;176for (int p = 0; p < inNumPoints; ++p)177{178// Calculate values179Vec3 transformed_point = inTransform * *reinterpret_cast<const Vec3 *>(reinterpret_cast<const uint8 *>(inPoints) + p * inPointStride);180float dist = inSurface.SignedDistance(transformed_point);181bool above = dist >= 0.0f;182183// Keep track if all are above or below184mAllAbove &= above;185mAllBelow &= !above;186187// Calculate lowest point, we use this to create tetrahedrons out of all faces188if (reference_dist > dist)189{190mReferencePointIdx = p;191reference_dist = dist;192}193194// Store values195ioBuffer->mPosition = transformed_point;196ioBuffer->mDistanceToSurface = dist;197ioBuffer->mAboveSurface = above;198++ioBuffer;199}200}201202/// Check if all points are above the surface. Should be used as early out.203inline bool AreAllAbove() const204{205return mAllAbove;206}207208/// Check if all points are below the surface. Should be used as early out.209inline bool AreAllBelow() const210{211return mAllBelow;212}213214/// Get the lowest point of the polyhedron. Used to form the 4th vertex to make a tetrahedron out of a polyhedron face.215inline int GetReferencePointIdx() const216{217return mReferencePointIdx;218}219220/// Add a polyhedron face. Supply the indices of the points that form the face (in counter clockwise order).221void AddFace(int inIdx1, int inIdx2, int inIdx3)222{223JPH_ASSERT(inIdx1 != mReferencePointIdx && inIdx2 != mReferencePointIdx && inIdx3 != mReferencePointIdx, "A face using the reference point will not contribute to the volume");224225// Find the points226const Point &ref = mPoints[mReferencePointIdx];227const Point &p1 = mPoints[inIdx1];228const Point &p2 = mPoints[inIdx2];229const Point &p3 = mPoints[inIdx3];230231// Determine which vertices are submerged232uint code = (p1.mAboveSurface? 0 : 0b001) | (p2.mAboveSurface? 0 : 0b010) | (p3.mAboveSurface? 0 : 0b100);233234float volume;235Vec3 center;236switch (code)237{238case 0b000:239// One point submerged240sTetrahedronVolume1(ref.mPosition, ref.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, volume, center);241break;242243case 0b001:244// Two points submerged245sTetrahedronVolume2(ref.mPosition, ref.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, volume, center);246break;247248case 0b010:249// Two points submerged250sTetrahedronVolume2(ref.mPosition, ref.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, volume, center);251break;252253case 0b100:254// Two points submerged255sTetrahedronVolume2(ref.mPosition, ref.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, volume, center);256break;257258case 0b011:259// Three points submerged260sTetrahedronVolume3(ref.mPosition, ref.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, volume, center);261break;262263case 0b101:264// Three points submerged265sTetrahedronVolume3(ref.mPosition, ref.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, volume, center);266break;267268case 0b110:269// Three points submerged270sTetrahedronVolume3(ref.mPosition, ref.mDistanceToSurface, p3.mPosition, p3.mDistanceToSurface, p2.mPosition, p2.mDistanceToSurface, p1.mPosition, p1.mDistanceToSurface, volume, center);271break;272273case 0b111:274// Four points submerged275sTetrahedronVolume4(ref.mPosition, p3.mPosition, p2.mPosition, p1.mPosition, volume, center);276break;277278default:279// Should not be possible280JPH_ASSERT(false);281volume = 0.0f;282center = Vec3::sZero();283break;284}285286mSubmergedVolume += volume;287mCenterOfBuoyancy += volume * center;288}289290/// Call after all faces have been added. Returns the submerged volume and the center of buoyancy for the submerged volume.291void GetResult(float &outSubmergedVolume, Vec3 &outCenterOfBuoyancy) const292{293outCenterOfBuoyancy = mSubmergedVolume > 0.0f? mCenterOfBuoyancy / (4.0f * mSubmergedVolume) : Vec3::sZero(); // Do this before dividing submerged volume by 6 to get correct weight factor294outSubmergedVolume = mSubmergedVolume / 6.0f;295}296297private:298// The precalculated points for this polyhedron299const Point * mPoints;300301// If all points are above/below the surface302bool mAllBelow = true;303bool mAllAbove = true;304305// The lowest point306int mReferencePointIdx = 0;307308// Aggregator for submerged volume and center of buoyancy309float mSubmergedVolume = 0.0f;310Vec3 mCenterOfBuoyancy = Vec3::sZero();311312#ifdef JPH_DEBUG_RENDERER313// Base offset used for drawing314RVec3 mBaseOffset;315#endif316};317318JPH_NAMESPACE_END319320321