A game about forced loneliness, made by TACStudios
1using System; 2using System.Diagnostics; 3using System.Runtime.CompilerServices; 4using Unity.IL2CPP.CompilerServices; 5 6namespace Unity.Mathematics.Geometry 7{ 8 /// <summary> 9 /// A plane represented by a normal vector and a distance along the normal from the origin. 10 /// </summary> 11 /// <remarks> 12 /// A plane splits the 3D space in half. The normal vector points to the positive half and the other half is 13 /// considered negative. 14 /// </remarks> 15 [DebuggerDisplay("{Normal}, {Distance}")] 16 [Serializable] 17 [Il2CppEagerStaticClassConstruction] 18 public struct Plane 19 { 20 /// <summary> 21 /// A plane in the form Ax + By + Cz + Dw = 0. 22 /// </summary> 23 /// <remarks> 24 /// Stores the plane coefficients A, B, C, D where (A, B, C) is a unit normal vector and D is the distance 25 /// from the origin. A plane stored with a unit normal vector is called a normalized plane. 26 /// </remarks> 27 public float4 NormalAndDistance; 28 29 /// <summary> 30 /// Constructs a Plane from arbitrary coefficients A, B, C, D of the plane equation Ax + By + Cz + Dw = 0. 31 /// </summary> 32 /// <remarks> 33 /// The constructed plane will be the normalized form of the plane specified by the given coefficients. 34 /// </remarks> 35 /// <param name="coefficientA">Coefficient A from plane equation.</param> 36 /// <param name="coefficientB">Coefficient B from plane equation.</param> 37 /// <param name="coefficientC">Coefficient C from plane equation.</param> 38 /// <param name="coefficientD">Coefficient D from plane equation.</param> 39 [MethodImpl(MethodImplOptions.AggressiveInlining)] 40 public Plane(float coefficientA, float coefficientB, float coefficientC, float coefficientD) 41 { 42 NormalAndDistance = Normalize(new float4(coefficientA, coefficientB, coefficientC, coefficientD)); 43 } 44 45 /// <summary> 46 /// Constructs a plane with a normal vector and distance from the origin. 47 /// </summary> 48 /// <remarks> 49 /// The constructed plane will be the normalized form of the plane specified by the inputs. 50 /// </remarks> 51 /// <param name="normal">A non-zero vector that is perpendicular to the plane. It may be any length.</param> 52 /// <param name="distance">Distance from the origin along the normal. A negative value moves the plane in the 53 /// same direction as the normal while a positive value moves it in the opposite direction.</param> 54 [MethodImpl(MethodImplOptions.AggressiveInlining)] 55 public Plane(float3 normal, float distance) 56 { 57 NormalAndDistance = Normalize(new float4(normal, distance)); 58 } 59 60 /// <summary> 61 /// Constructs a plane with a normal vector and a point that lies in the plane. 62 /// </summary> 63 /// <remarks> 64 /// The constructed plane will be the normalized form of the plane specified by the inputs. 65 /// </remarks> 66 /// <param name="normal">A non-zero vector that is perpendicular to the plane. It may be any length.</param> 67 /// <param name="pointInPlane">A point that lies in the plane.</param> 68 [MethodImpl(MethodImplOptions.AggressiveInlining)] 69 public Plane(float3 normal, float3 pointInPlane) 70 : this(normal, -math.dot(normal, pointInPlane)) 71 { 72 } 73 74 /// <summary> 75 /// Constructs a plane with two vectors and a point that all lie in the plane. 76 /// </summary> 77 /// <remarks> 78 /// The constructed plane will be the normalized form of the plane specified by the inputs. 79 /// </remarks> 80 /// <param name="vector1InPlane">A non-zero vector that lies in the plane. It may be any length.</param> 81 /// <param name="vector2InPlane">A non-zero vector that lies in the plane. It may be any length and must not be a scalar multiple of <paramref name="vector1InPlane"/>.</param> 82 /// <param name="pointInPlane">A point that lies in the plane.</param> 83 [MethodImpl(MethodImplOptions.AggressiveInlining)] 84 public Plane(float3 vector1InPlane, float3 vector2InPlane, float3 pointInPlane) 85 : this(math.cross(vector1InPlane, vector2InPlane), pointInPlane) 86 { 87 } 88 89 /// <summary> 90 /// Creates a normalized Plane directly without normalization cost. 91 /// </summary> 92 /// <remarks> 93 /// If you have a unit length normal vector, you can create a Plane faster than using one of its constructors 94 /// by calling this function. 95 /// </remarks> 96 /// <param name="unitNormal">A non-zero vector that is perpendicular to the plane. It must be unit length.</param> 97 /// <param name="distance">Distance from the origin along the normal. A negative value moves the plane in the 98 /// same direction as the normal while a positive value moves it in the opposite direction.</param> 99 /// <returns>Normalized Plane constructed from given inputs.</returns> 100 [MethodImpl(MethodImplOptions.AggressiveInlining)] 101 public static Plane CreateFromUnitNormalAndDistance(float3 unitNormal, float distance) 102 { 103 return new Plane { NormalAndDistance = new float4(unitNormal, distance) }; 104 } 105 106 /// <summary> 107 /// Creates a normalized Plane without normalization cost. 108 /// </summary> 109 /// <remarks> 110 /// If you have a unit length normal vector, you can create a Plane faster than using one of its constructors 111 /// by calling this function. 112 /// </remarks> 113 /// <param name="unitNormal">A non-zero vector that is perpendicular to the plane. It must be unit length.</param> 114 /// <param name="pointInPlane">A point that lies in the plane.</param> 115 /// <returns>Normalized Plane constructed from given inputs.</returns> 116 [MethodImpl(MethodImplOptions.AggressiveInlining)] 117 public static Plane CreateFromUnitNormalAndPointInPlane(float3 unitNormal, float3 pointInPlane) 118 { 119 return new Plane { NormalAndDistance = new float4(unitNormal, -math.dot(unitNormal, pointInPlane)) }; 120 } 121 122 /// <summary> 123 /// Get/set the normal vector of the plane. 124 /// </summary> 125 /// <remarks> 126 /// It is assumed that the normal is unit length. If you set a new plane such that Ax + By + Cz + Dw = 0 but 127 /// (A, B, C) is not unit length, then you must normalize the plane by calling <see cref="Normalize(Plane)"/>. 128 /// </remarks> 129 public float3 Normal 130 { 131 get => NormalAndDistance.xyz; 132 set => NormalAndDistance.xyz = value; 133 } 134 135 /// <summary> 136 /// Get/set the distance of the plane from the origin. May be a negative value. 137 /// </summary> 138 /// <remarks> 139 /// It is assumed that the normal is unit length. If you set a new plane such that Ax + By + Cz + Dw = 0 but 140 /// (A, B, C) is not unit length, then you must normalize the plane by calling <see cref="Normalize(Plane)"/>. 141 /// </remarks> 142 public float Distance 143 { 144 get => NormalAndDistance.w; 145 set => NormalAndDistance.w = value; 146 } 147 148 /// <summary> 149 /// Normalizes the given Plane. 150 /// </summary> 151 /// <param name="plane">Plane to normalize.</param> 152 /// <returns>Normalized Plane.</returns> 153 [MethodImpl(MethodImplOptions.AggressiveInlining)] 154 public static Plane Normalize(Plane plane) 155 { 156 return new Plane { NormalAndDistance = Normalize(plane.NormalAndDistance) }; 157 } 158 159 /// <summary> 160 /// Normalizes the plane represented by the given plane coefficients. 161 /// </summary> 162 /// <remarks> 163 /// The plane coefficients are A, B, C, D and stored in that order in the <see cref="float4"/>. 164 /// </remarks> 165 /// <param name="planeCoefficients">Plane coefficients A, B, C, D stored in x, y, z, w (respectively).</param> 166 /// <returns>Normalized plane coefficients.</returns> 167 [MethodImpl(MethodImplOptions.AggressiveInlining)] 168 public static float4 Normalize(float4 planeCoefficients) 169 { 170 float recipLength = math.rsqrt(math.lengthsq(planeCoefficients.xyz)); 171 return new Plane { NormalAndDistance = planeCoefficients * recipLength }; 172 } 173 174 /// <summary> 175 /// Get the signed distance from the point to the plane. 176 /// </summary> 177 /// <remarks> 178 /// Plane must be normalized prior to calling this function. Distance is positive if point is on side of the 179 /// plane the normal points to, negative if on the opposite side and zero if the point lies in the plane. 180 /// Avoid comparing equality with 0.0f when testing if a point lies exactly in the plane and use an approximate 181 /// comparison instead. 182 /// </remarks> 183 /// <param name="point">Point to find the signed distance with.</param> 184 /// <returns>Signed distance of the point from the plane.</returns> 185 [MethodImpl(MethodImplOptions.AggressiveInlining)] 186 public float SignedDistanceToPoint(float3 point) 187 { 188 CheckPlaneIsNormalized(); 189 return math.dot(NormalAndDistance, new float4(point, 1.0f)); 190 } 191 192 /// <summary> 193 /// Projects the given point onto the plane. 194 /// </summary> 195 /// <remarks> 196 /// Plane must be normalized prior to calling this function. The result is the position closest to the point 197 /// that still lies in the plane. 198 /// </remarks> 199 /// <param name="point">Point to project onto the plane.</param> 200 /// <returns>Projected point that's inside the plane.</returns> 201 [MethodImpl(MethodImplOptions.AggressiveInlining)] 202 public float3 Projection(float3 point) 203 { 204 CheckPlaneIsNormalized(); 205 return point - Normal * SignedDistanceToPoint(point); 206 } 207 208 /// <summary> 209 /// Flips the plane so the normal points in the opposite direction. 210 /// </summary> 211 public Plane Flipped => new Plane { NormalAndDistance = -NormalAndDistance }; 212 213 /// <summary> 214 /// Implicitly converts a <see cref="Plane"/> to <see cref="float4"/>. 215 /// </summary> 216 /// <param name="plane">Plane to convert.</param> 217 /// <returns>A <see cref="float4"/> representing the plane.</returns> 218 [MethodImpl(MethodImplOptions.AggressiveInlining)] 219 public static implicit operator float4(Plane plane) => plane.NormalAndDistance; 220 221 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] 222 void CheckPlaneIsNormalized() 223 { 224 float ll = math.lengthsq(Normal.xyz); 225 const float lowerBound = 0.999f * 0.999f; 226 const float upperBound = 1.001f * 1.001f; 227 228 if (ll < lowerBound || ll > upperBound) 229 { 230 throw new System.ArgumentException("Plane must be normalized. Call Plane.Normalize() to normalize plane."); 231 } 232 } 233 } 234}