#if REAL_T_IS_DOUBLE using real_t = System.Double; #else using real_t = System.Single; #endif using System; using System.Runtime.InteropServices; namespace Godot { /// /// Plane represents a normalized plane equation. /// "Over" or "Above" the plane is considered the side of /// the plane towards where the normal is pointing. /// [Serializable] [StructLayout(LayoutKind.Sequential)] public struct Plane : IEquatable { private Vector3 _normal; /// /// The normal of the plane, which must be normalized. /// In the scalar equation of the plane `ax + by + cz = d`, this is /// the vector `(a, b, c)`, where `d` is the property. /// /// Equivalent to `x`, `y`, and `z`. public Vector3 Normal { get { return _normal; } set { _normal = value; } } /// /// The X component of the plane's normal vector. /// /// Equivalent to 's X value. public real_t x { get { return _normal.x; } set { _normal.x = value; } } /// /// The Y component of the plane's normal vector. /// /// Equivalent to 's Y value. public real_t y { get { return _normal.y; } set { _normal.y = value; } } /// /// The Z component of the plane's normal vector. /// /// Equivalent to 's Z value. public real_t z { get { return _normal.z; } set { _normal.z = value; } } /// /// The distance from the origin to the plane (in the direction of /// ). This value is typically non-negative. /// In the scalar equation of the plane `ax + by + cz = d`, /// this is `d`, while the `(a, b, c)` coordinates are represented /// by the property. /// /// The plane's distance from the origin. public real_t D { get; set; } /// /// The center of the plane, the point where the normal line intersects the plane. /// /// Equivalent to multiplied by `D`. public Vector3 Center { get { return _normal * D; } set { _normal = value.Normalized(); D = value.Length(); } } /// /// Returns the shortest distance from this plane to the position `point`. /// /// The position to use for the calculation. /// The shortest distance. public real_t DistanceTo(Vector3 point) { return _normal.Dot(point) - D; } /// /// Returns true if point is inside the plane. /// Comparison uses a custom minimum epsilon threshold. /// /// The point to check. /// The tolerance threshold. /// A bool for whether or not the plane has the point. public bool HasPoint(Vector3 point, real_t epsilon = Mathf.Epsilon) { real_t dist = _normal.Dot(point) - D; return Mathf.Abs(dist) <= epsilon; } /// /// Returns the intersection point of the three planes: `b`, `c`, /// and this plane. If no intersection is found, `null` is returned. /// /// One of the three planes to use in the calculation. /// One of the three planes to use in the calculation. /// The intersection, or `null` if none is found. public Vector3? Intersect3(Plane b, Plane c) { real_t denom = _normal.Cross(b._normal).Dot(c._normal); if (Mathf.IsZeroApprox(denom)) { return null; } Vector3 result = b._normal.Cross(c._normal) * D + c._normal.Cross(_normal) * b.D + _normal.Cross(b._normal) * c.D; return result / denom; } /// /// Returns the intersection point of a ray consisting of the /// position `from` and the direction normal `dir` with this plane. /// If no intersection is found, `null` is returned. /// /// The start of the ray. /// The direction of the ray, normalized. /// The intersection, or `null` if none is found. public Vector3? IntersectRay(Vector3 from, Vector3 dir) { real_t den = _normal.Dot(dir); if (Mathf.IsZeroApprox(den)) { return null; } real_t dist = (_normal.Dot(from) - D) / den; // This is a ray, before the emitting pos (from) does not exist if (dist > Mathf.Epsilon) { return null; } return from - (dir * dist); } /// /// Returns the intersection point of a line segment from /// position `begin` to position `end` with this plane. /// If no intersection is found, `null` is returned. /// /// The start of the line segment. /// The end of the line segment. /// The intersection, or `null` if none is found. public Vector3? IntersectSegment(Vector3 begin, Vector3 end) { Vector3 segment = begin - end; real_t den = _normal.Dot(segment); if (Mathf.IsZeroApprox(den)) { return null; } real_t dist = (_normal.Dot(begin) - D) / den; // Only allow dist to be in the range of 0 to 1, with tolerance. if (dist < -Mathf.Epsilon || dist > 1.0f + Mathf.Epsilon) { return null; } return begin - (segment * dist); } /// /// Returns true if `point` is located above the plane. /// /// The point to check. /// A bool for whether or not the point is above the plane. public bool IsPointOver(Vector3 point) { return _normal.Dot(point) > D; } /// /// Returns the plane scaled to unit length. /// /// A normalized version of the plane. public Plane Normalized() { real_t len = _normal.Length(); if (len == 0) { return new Plane(0, 0, 0, 0); } return new Plane(_normal / len, D / len); } /// /// Returns the orthogonal projection of `point` into the plane. /// /// The point to project. /// The projected point. public Vector3 Project(Vector3 point) { return point - _normal * DistanceTo(point); } // Constants private static readonly Plane _planeYZ = new Plane(1, 0, 0, 0); private static readonly Plane _planeXZ = new Plane(0, 1, 0, 0); private static readonly Plane _planeXY = new Plane(0, 0, 1, 0); /// /// A plane that extends in the Y and Z axes (normal vector points +X). /// /// Equivalent to `new Plane(1, 0, 0, 0)`. public static Plane PlaneYZ { get { return _planeYZ; } } /// /// A plane that extends in the X and Z axes (normal vector points +Y). /// /// Equivalent to `new Plane(0, 1, 0, 0)`. public static Plane PlaneXZ { get { return _planeXZ; } } /// /// A plane that extends in the X and Y axes (normal vector points +Z). /// /// Equivalent to `new Plane(0, 0, 1, 0)`. public static Plane PlaneXY { get { return _planeXY; } } /// /// Constructs a plane from four values. `a`, `b` and `c` become the /// components of the resulting plane's vector. /// `d` becomes the plane's distance from the origin. /// /// The X component of the plane's normal vector. /// The Y component of the plane's normal vector. /// The Z component of the plane's normal vector. /// The plane's distance from the origin. This value is typically non-negative. public Plane(real_t a, real_t b, real_t c, real_t d) { _normal = new Vector3(a, b, c); this.D = d; } /// /// Constructs a plane from a normal vector and the plane's distance to the origin. /// /// The normal of the plane, must be normalized. /// The plane's distance from the origin. This value is typically non-negative. public Plane(Vector3 normal, real_t d) { this._normal = normal; this.D = d; } /// /// Constructs a plane from the three points, given in clockwise order. /// /// The first point. /// The second point. /// The third point. public Plane(Vector3 v1, Vector3 v2, Vector3 v3) { _normal = (v1 - v3).Cross(v1 - v2); _normal.Normalize(); D = _normal.Dot(v1); } public static Plane operator -(Plane plane) { return new Plane(-plane._normal, -plane.D); } public static bool operator ==(Plane left, Plane right) { return left.Equals(right); } public static bool operator !=(Plane left, Plane right) { return !left.Equals(right); } public override bool Equals(object obj) { if (obj is Plane) { return Equals((Plane)obj); } return false; } public bool Equals(Plane other) { return _normal == other._normal && D == other.D; } /// /// Returns true if this plane and `other` are approximately equal, by running /// on each component. /// /// The other plane to compare. /// Whether or not the planes are approximately equal. public bool IsEqualApprox(Plane other) { return _normal.IsEqualApprox(other._normal) && Mathf.IsEqualApprox(D, other.D); } public override int GetHashCode() { return _normal.GetHashCode() ^ D.GetHashCode(); } public override string ToString() { return String.Format("{0}, {1}", new object[] { _normal.ToString(), D.ToString() }); } public string ToString(string format) { return String.Format("{0}, {1}", new object[] { _normal.ToString(format), D.ToString(format) }); } } }