// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) // SPDX-FileCopyrightText: 2021 Jorrit Rouwe // SPDX-License-Identifier: MIT #pragma once JPH_NAMESPACE_BEGIN /// An infinite plane described by the formula X . Normal + Constant = 0. class [[nodiscard]] Plane { public: JPH_OVERRIDE_NEW_DELETE /// Constructor Plane() = default; explicit Plane(Vec4Arg inNormalAndConstant) : mNormalAndConstant(inNormalAndConstant) { } Plane(Vec3Arg inNormal, float inConstant) : mNormalAndConstant(inNormal, inConstant) { } /// Create from point and normal static Plane sFromPointAndNormal(Vec3Arg inPoint, Vec3Arg inNormal) { return Plane(Vec4(inNormal, -inNormal.Dot(inPoint))); } /// Create from point and normal, double precision version that more accurately calculates the plane constant static Plane sFromPointAndNormal(DVec3Arg inPoint, Vec3Arg inNormal) { return Plane(Vec4(inNormal, -float(DVec3(inNormal).Dot(inPoint)))); } /// Create from 3 counter clockwise points static Plane sFromPointsCCW(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3) { return sFromPointAndNormal(inV1, (inV2 - inV1).Cross(inV3 - inV1).Normalized()); } // Properties Vec3 GetNormal() const { return Vec3(mNormalAndConstant); } void SetNormal(Vec3Arg inNormal) { mNormalAndConstant = Vec4(inNormal, mNormalAndConstant.GetW()); } float GetConstant() const { return mNormalAndConstant.GetW(); } void SetConstant(float inConstant) { mNormalAndConstant.SetW(inConstant); } /// Offset the plane (positive value means move it in the direction of the plane normal) Plane Offset(float inDistance) const { return Plane(mNormalAndConstant - Vec4(Vec3::sZero(), inDistance)); } /// Transform the plane by a matrix inline Plane GetTransformed(Mat44Arg inTransform) const { Vec3 transformed_normal = inTransform.Multiply3x3(GetNormal()); return Plane(transformed_normal, GetConstant() - inTransform.GetTranslation().Dot(transformed_normal)); } /// Scale the plane, can handle non-uniform and negative scaling inline Plane Scaled(Vec3Arg inScale) const { Vec3 scaled_normal = GetNormal() / inScale; float scaled_normal_length = scaled_normal.Length(); return Plane(scaled_normal / scaled_normal_length, GetConstant() / scaled_normal_length); } /// Distance point to plane float SignedDistance(Vec3Arg inPoint) const { return inPoint.Dot(GetNormal()) + GetConstant(); } /// Project inPoint onto the plane Vec3 ProjectPointOnPlane(Vec3Arg inPoint) const { return inPoint - GetNormal() * SignedDistance(inPoint); } /// Returns intersection point between 3 planes static bool sIntersectPlanes(const Plane &inP1, const Plane &inP2, const Plane &inP3, Vec3 &outPoint) { // We solve the equation: // |ax, ay, az, aw| | x | | 0 | // |bx, by, bz, bw| * | y | = | 0 | // |cx, cy, cz, cw| | z | | 0 | // | 0, 0, 0, 1| | 1 | | 1 | // Where normal of plane 1 = (ax, ay, az), plane constant of 1 = aw, normal of plane 2 = (bx, by, bz) etc. // This involves inverting the matrix and multiplying it with [0, 0, 0, 1] // Fetch the normals and plane constants for the three planes Vec4 a = inP1.mNormalAndConstant; Vec4 b = inP2.mNormalAndConstant; Vec4 c = inP3.mNormalAndConstant; // Result is a vector that we have to divide by: float denominator = Vec3(a).Dot(Vec3(b).Cross(Vec3(c))); if (denominator == 0.0f) return false; // The numerator is: // [aw*(bz*cy-by*cz)+ay*(bw*cz-bz*cw)+az*(by*cw-bw*cy)] // [aw*(bx*cz-bz*cx)+ax*(bz*cw-bw*cz)+az*(bw*cx-bx*cw)] // [aw*(by*cx-bx*cy)+ax*(bw*cy-by*cw)+ay*(bx*cw-bw*cx)] Vec4 numerator = a.SplatW() * (b.Swizzle() * c.Swizzle() - b.Swizzle() * c.Swizzle()) + a.Swizzle() * (b.Swizzle() * c.Swizzle() - b.Swizzle() * c.Swizzle()) + a.Swizzle() * (b.Swizzle() * c.Swizzle() - b.Swizzle() * c.Swizzle()); outPoint = Vec3(numerator) / denominator; return true; } private: #ifdef JPH_OBJECT_STREAM friend void CreateRTTIPlane(class RTTI &); // For JPH_IMPLEMENT_SERIALIZABLE_OUTSIDE_CLASS #endif Vec4 mNormalAndConstant; ///< XYZ = normal, W = constant, plane: x . normal + constant = 0 }; JPH_NAMESPACE_END