Show More
Commit Description:
Add timers for Simulation and various engines...
Commit Description:
Add timers for Simulation and various engines Starting to add additional timers for different stages of the process of updating in order to get more insight into what is slowing it down. The update takes 9ms, which is much longer than it used to. Engine-specific timers are coming later.
File last commit:
Show/Diff file:
Action:
FNA/src/BoundingFrustum.cs
727 lines | 21.6 KiB | text/x-csharp | CSharpLexer
#region License
/* FNA - XNA4 Reimplementation for Desktop Platforms
* Copyright 2009-2020 Ethan Lee and the MonoGame Team
*
* Released under the Microsoft Public License.
* See LICENSE for details.
*/
/* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Diagnostics;
using System.Text;
#endregion
namespace Microsoft.Xna.Framework
{
/// <summary>
/// Defines a viewing frustum for intersection operations.
/// </summary>
[DebuggerDisplay("{DebugDisplayString,nq}")]
public class BoundingFrustum : IEquatable<BoundingFrustum>
{
#region Public Properties
/// <summary>
/// Gets or sets the <see cref="Matrix"/> of the frustum.
/// </summary>
public Matrix Matrix
{
get
{
return this.matrix;
}
set
{
/* FIXME: The odds are the planes will be used a lot more often than
* the matrix is updated, so this should help performance. I hope. ;)
*/
this.matrix = value;
this.CreatePlanes();
this.CreateCorners();
}
}
/// <summary>
/// Gets the near plane of the frustum.
/// </summary>
public Plane Near
{
get
{
return this.planes[0];
}
}
/// <summary>
/// Gets the far plane of the frustum.
/// </summary>
public Plane Far
{
get
{
return this.planes[1];
}
}
/// <summary>
/// Gets the left plane of the frustum.
/// </summary>
public Plane Left
{
get
{
return this.planes[2];
}
}
/// <summary>
/// Gets the right plane of the frustum.
/// </summary>
public Plane Right
{
get
{
return this.planes[3];
}
}
/// <summary>
/// Gets the top plane of the frustum.
/// </summary>
public Plane Top
{
get
{
return this.planes[4];
}
}
/// <summary>
/// Gets the bottom plane of the frustum.
/// </summary>
public Plane Bottom
{
get
{
return this.planes[5];
}
}
#endregion
#region Internal Properties
internal string DebugDisplayString
{
get
{
return string.Concat(
"Near( ", planes[0].DebugDisplayString, " ) \r\n",
"Far( ", planes[1].DebugDisplayString, " ) \r\n",
"Left( ", planes[2].DebugDisplayString, " ) \r\n",
"Right( ", planes[3].DebugDisplayString, " ) \r\n",
"Top( ", planes[4].DebugDisplayString, " ) \r\n",
"Bottom( ", planes[5].DebugDisplayString, " ) "
);
}
}
#endregion
#region Public Fields
/// <summary>
/// The number of corner points in the frustum.
/// </summary>
public const int CornerCount = 8;
#endregion
#region Private Fields
private Matrix matrix;
private readonly Vector3[] corners = new Vector3[CornerCount];
private readonly Plane[] planes = new Plane[PlaneCount];
/// <summary>
/// The number of planes in the frustum.
/// </summary>
private const int PlaneCount = 6;
#endregion
#region Public Constructors
/// <summary>
/// Constructs the frustum by extracting the view planes from a matrix.
/// </summary>
/// <param name="value">Combined matrix which usually is (View * Projection).</param>
public BoundingFrustum(Matrix value)
{
this.matrix = value;
this.CreatePlanes();
this.CreateCorners();
}
#endregion
#region Public Methods
/// <summary>
/// Containment test between this <see cref="BoundingFrustum"/> and specified <see cref="BoundingFrustum"/>.
/// </summary>
/// <param name="frustum">A <see cref="BoundingFrustum"/> for testing.</param>
/// <returns>Result of testing for containment between this <see cref="BoundingFrustum"/> and specified <see cref="BoundingFrustum"/>.</returns>
public ContainmentType Contains(BoundingFrustum frustum)
{
if (this == frustum)
{
return ContainmentType.Contains;
}
bool intersects = false;
for (int i = 0; i < PlaneCount; i += 1)
{
PlaneIntersectionType planeIntersectionType;
frustum.Intersects(ref planes[i], out planeIntersectionType);
if (planeIntersectionType == PlaneIntersectionType.Front)
{
return ContainmentType.Disjoint;
}
else if (planeIntersectionType == PlaneIntersectionType.Intersecting)
{
intersects = true;
}
}
return intersects ? ContainmentType.Intersects : ContainmentType.Contains;
}
/// <summary>
/// Containment test between this <see cref="BoundingFrustum"/> and specified <see cref="BoundingBox"/>.
/// </summary>
/// <param name="box">A <see cref="BoundingBox"/> for testing.</param>
/// <returns>Result of testing for containment between this <see cref="BoundingFrustum"/> and specified <see cref="BoundingBox"/>.</returns>
public ContainmentType Contains(BoundingBox box)
{
ContainmentType result = default(ContainmentType);
this.Contains(ref box, out result);
return result;
}
/// <summary>
/// Containment test between this <see cref="BoundingFrustum"/> and specified <see cref="BoundingBox"/>.
/// </summary>
/// <param name="box">A <see cref="BoundingBox"/> for testing.</param>
/// <param name="result">Result of testing for containment between this <see cref="BoundingFrustum"/> and specified <see cref="BoundingBox"/> as an output parameter.</param>
public void Contains(ref BoundingBox box, out ContainmentType result)
{
bool intersects = false;
for (int i = 0; i < PlaneCount; i += 1)
{
PlaneIntersectionType planeIntersectionType = default(PlaneIntersectionType);
box.Intersects(ref this.planes[i], out planeIntersectionType);
switch (planeIntersectionType)
{
case PlaneIntersectionType.Front:
result = ContainmentType.Disjoint;
return;
case PlaneIntersectionType.Intersecting:
intersects = true;
break;
}
}
result = intersects ? ContainmentType.Intersects : ContainmentType.Contains;
}
/// <summary>
/// Containment test between this <see cref="BoundingFrustum"/> and specified <see cref="BoundingSphere"/>.
/// </summary>
/// <param name="sphere">A <see cref="BoundingSphere"/> for testing.</param>
/// <returns>Result of testing for containment between this <see cref="BoundingFrustum"/> and specified <see cref="BoundingSphere"/>.</returns>
public ContainmentType Contains(BoundingSphere sphere)
{
ContainmentType result = default(ContainmentType);
this.Contains(ref sphere, out result);
return result;
}
/// <summary>
/// Containment test between this <see cref="BoundingFrustum"/> and specified <see cref="BoundingSphere"/>.
/// </summary>
/// <param name="sphere">A <see cref="BoundingSphere"/> for testing.</param>
/// <param name="result">Result of testing for containment between this <see cref="BoundingFrustum"/> and specified <see cref="BoundingSphere"/> as an output parameter.</param>
public void Contains(ref BoundingSphere sphere, out ContainmentType result)
{
bool intersects = false;
for (int i = 0; i < PlaneCount; i += 1)
{
PlaneIntersectionType planeIntersectionType = default(PlaneIntersectionType);
// TODO: We might want to inline this for performance reasons.
sphere.Intersects(ref this.planes[i], out planeIntersectionType);
switch (planeIntersectionType)
{
case PlaneIntersectionType.Front:
result = ContainmentType.Disjoint;
return;
case PlaneIntersectionType.Intersecting:
intersects = true;
break;
}
}
result = intersects ? ContainmentType.Intersects : ContainmentType.Contains;
}
/// <summary>
/// Containment test between this <see cref="BoundingFrustum"/> and specified <see cref="Vector3"/>.
/// </summary>
/// <param name="point">A <see cref="Vector3"/> for testing.</param>
/// <returns>Result of testing for containment between this <see cref="BoundingFrustum"/> and specified <see cref="Vector3"/>.</returns>
public ContainmentType Contains(Vector3 point)
{
ContainmentType result = default(ContainmentType);
this.Contains(ref point, out result);
return result;
}
/// <summary>
/// Containment test between this <see cref="BoundingFrustum"/> and specified <see cref="Vector3"/>.
/// </summary>
/// <param name="point">A <see cref="Vector3"/> for testing.</param>
/// <param name="result">Result of testing for containment between this <see cref="BoundingFrustum"/> and specified <see cref="Vector3"/> as an output parameter.</param>
public void Contains(ref Vector3 point, out ContainmentType result)
{
bool intersects = false;
for (int i = 0; i < PlaneCount; i += 1)
{
float classifyPoint = (
(point.X * planes[i].Normal.X) +
(point.Y * planes[i].Normal.Y) +
(point.Z * planes[i].Normal.Z) +
planes[i].D
);
if (classifyPoint > 0)
{
result = ContainmentType.Disjoint;
return;
}
else if (classifyPoint == 0)
{
intersects = true;
break;
}
}
result = intersects ? ContainmentType.Intersects : ContainmentType.Contains;
}
/// <summary>
/// Returns a copy of internal corners array.
/// </summary>
/// <returns>The array of corners.</returns>
public Vector3[] GetCorners()
{
return (Vector3[]) this.corners.Clone();
}
/// <summary>
/// Returns a copy of internal corners array.
/// </summary>
/// <param name="corners">The array which values will be replaced to corner values of this instance. It must have size of <see cref="BoundingFrustum.CornerCount"/>.</param>
public void GetCorners(Vector3[] corners)
{
if (corners == null)
{
throw new ArgumentNullException("corners");
}
if (corners.Length < CornerCount)
{
throw new ArgumentOutOfRangeException("corners");
}
this.corners.CopyTo(corners, 0);
}
/// <summary>
/// Gets whether or not a specified <see cref="BoundingFrustum"/> intersects with this <see cref="BoundingFrustum"/>.
/// </summary>
/// <param name="frustum">An other <see cref="BoundingFrustum"/> for intersection test.</param>
/// <returns><c>true</c> if other <see cref="BoundingFrustum"/> intersects with this <see cref="BoundingFrustum"/>; <c>false</c> otherwise.</returns>
public bool Intersects(BoundingFrustum frustum)
{
return (Contains(frustum) != ContainmentType.Disjoint);
}
/// <summary>
/// Gets whether or not a specified <see cref="BoundingBox"/> intersects with this <see cref="BoundingFrustum"/>.
/// </summary>
/// <param name="box">A <see cref="BoundingBox"/> for intersection test.</param>
/// <returns><c>true</c> if specified <see cref="BoundingBox"/> intersects with this <see cref="BoundingFrustum"/>; <c>false</c> otherwise.</returns>
public bool Intersects(BoundingBox box)
{
bool result = false;
this.Intersects(ref box, out result);
return result;
}
/// <summary>
/// Gets whether or not a specified <see cref="BoundingBox"/> intersects with this <see cref="BoundingFrustum"/>.
/// </summary>
/// <param name="box">A <see cref="BoundingBox"/> for intersection test.</param>
/// <param name="result"><c>true</c> if specified <see cref="BoundingBox"/> intersects with this <see cref="BoundingFrustum"/>; <c>false</c> otherwise as an output parameter.</param>
public void Intersects(ref BoundingBox box, out bool result)
{
ContainmentType containment = default(ContainmentType);
this.Contains(ref box, out containment);
result = containment != ContainmentType.Disjoint;
}
/// <summary>
/// Gets whether or not a specified <see cref="BoundingSphere"/> intersects with this <see cref="BoundingFrustum"/>.
/// </summary>
/// <param name="sphere">A <see cref="BoundingSphere"/> for intersection test.</param>
/// <returns><c>true</c> if specified <see cref="BoundingSphere"/> intersects with this <see cref="BoundingFrustum"/>; <c>false</c> otherwise.</returns>
public bool Intersects(BoundingSphere sphere)
{
bool result = default(bool);
this.Intersects(ref sphere, out result);
return result;
}
/// <summary>
/// Gets whether or not a specified <see cref="BoundingSphere"/> intersects with this <see cref="BoundingFrustum"/>.
/// </summary>
/// <param name="sphere">A <see cref="BoundingSphere"/> for intersection test.</param>
/// <param name="result"><c>true</c> if specified <see cref="BoundingSphere"/> intersects with this <see cref="BoundingFrustum"/>; <c>false</c> otherwise as an output parameter.</param>
public void Intersects(ref BoundingSphere sphere, out bool result)
{
ContainmentType containment = default(ContainmentType);
this.Contains(ref sphere, out containment);
result = containment != ContainmentType.Disjoint;
}
/// <summary>
/// Gets type of intersection between specified <see cref="Plane"/> and this <see cref="BoundingFrustum"/>.
/// </summary>
/// <param name="plane">A <see cref="Plane"/> for intersection test.</param>
/// <returns>A plane intersection type.</returns>
public PlaneIntersectionType Intersects(Plane plane)
{
PlaneIntersectionType result;
Intersects(ref plane, out result);
return result;
}
/// <summary>
/// Gets type of intersection between specified <see cref="Plane"/> and this <see cref="BoundingFrustum"/>.
/// </summary>
/// <param name="plane">A <see cref="Plane"/> for intersection test.</param>
/// <param name="result">A plane intersection type as an output parameter.</param>
public void Intersects(ref Plane plane, out PlaneIntersectionType result)
{
result = plane.Intersects(ref corners[0]);
for (int i = 1; i < corners.Length; i += 1)
{
if (plane.Intersects(ref corners[i]) != result)
{
result = PlaneIntersectionType.Intersecting;
}
}
}
/// <summary>
/// Gets the distance of intersection of <see cref="Ray"/> and this <see cref="BoundingFrustum"/> or null if no intersection happens.
/// </summary>
/// <param name="ray">A <see cref="Ray"/> for intersection test.</param>
/// <returns>Distance at which ray intersects with this <see cref="BoundingFrustum"/> or null if no intersection happens.</returns>
public float? Intersects(Ray ray)
{
float? result;
Intersects(ref ray, out result);
return result;
}
/// <summary>
/// Gets the distance of intersection of <see cref="Ray"/> and this <see cref="BoundingFrustum"/> or null if no intersection happens.
/// </summary>
/// <param name="ray">A <see cref="Ray"/> for intersection test.</param>
/// <param name="result">Distance at which ray intersects with this <see cref="BoundingFrustum"/> or null if no intersection happens as an output parameter.</param>
public void Intersects(ref Ray ray, out float? result)
{
ContainmentType ctype;
Contains(ref ray.Position, out ctype);
if (ctype == ContainmentType.Disjoint)
{
result = null;
return;
}
if (ctype == ContainmentType.Contains)
{
result = 0.0f;
return;
}
if (ctype != ContainmentType.Intersects)
{
throw new ArgumentOutOfRangeException("ctype");
}
throw new NotImplementedException();
}
#endregion
#region Private Methods
private void CreateCorners()
{
IntersectionPoint(
ref this.planes[0],
ref this.planes[2],
ref this.planes[4],
out this.corners[0]
);
IntersectionPoint(
ref this.planes[0],
ref this.planes[3],
ref this.planes[4],
out this.corners[1]
);
IntersectionPoint(
ref this.planes[0],
ref this.planes[3],
ref this.planes[5],
out this.corners[2]
);
IntersectionPoint(
ref this.planes[0],
ref this.planes[2],
ref this.planes[5],
out this.corners[3]
);
IntersectionPoint(
ref this.planes[1],
ref this.planes[2],
ref this.planes[4],
out this.corners[4]
);
IntersectionPoint(
ref this.planes[1],
ref this.planes[3],
ref this.planes[4],
out this.corners[5]
);
IntersectionPoint(
ref this.planes[1],
ref this.planes[3],
ref this.planes[5],
out this.corners[6]
);
IntersectionPoint(
ref this.planes[1],
ref this.planes[2],
ref this.planes[5],
out this.corners[7]
);
}
private void CreatePlanes()
{
this.planes[0] = new Plane(
-this.matrix.M13,
-this.matrix.M23,
-this.matrix.M33,
-this.matrix.M43
);
this.planes[1] = new Plane(
this.matrix.M13 - this.matrix.M14,
this.matrix.M23 - this.matrix.M24,
this.matrix.M33 - this.matrix.M34,
this.matrix.M43 - this.matrix.M44
);
this.planes[2] = new Plane(
-this.matrix.M14 - this.matrix.M11,
-this.matrix.M24 - this.matrix.M21,
-this.matrix.M34 - this.matrix.M31,
-this.matrix.M44 - this.matrix.M41
);
this.planes[3] = new Plane(
this.matrix.M11 - this.matrix.M14,
this.matrix.M21 - this.matrix.M24,
this.matrix.M31 - this.matrix.M34,
this.matrix.M41 - this.matrix.M44
);
this.planes[4] = new Plane(
this.matrix.M12 - this.matrix.M14,
this.matrix.M22 - this.matrix.M24,
this.matrix.M32 - this.matrix.M34,
this.matrix.M42 - this.matrix.M44
);
this.planes[5] = new Plane(
-this.matrix.M14 - this.matrix.M12,
-this.matrix.M24 - this.matrix.M22,
-this.matrix.M34 - this.matrix.M32,
-this.matrix.M44 - this.matrix.M42
);
this.NormalizePlane(ref this.planes[0]);
this.NormalizePlane(ref this.planes[1]);
this.NormalizePlane(ref this.planes[2]);
this.NormalizePlane(ref this.planes[3]);
this.NormalizePlane(ref this.planes[4]);
this.NormalizePlane(ref this.planes[5]);
}
private void NormalizePlane(ref Plane p)
{
float factor = 1f / p.Normal.Length();
p.Normal.X *= factor;
p.Normal.Y *= factor;
p.Normal.Z *= factor;
p.D *= factor;
}
#endregion
#region Private Static Methods
private static void IntersectionPoint(
ref Plane a,
ref Plane b,
ref Plane c,
out Vector3 result
) {
/* Formula used
* d1 ( N2 * N3 ) + d2 ( N3 * N1 ) + d3 ( N1 * N2 )
* P = -------------------------------------------------------------------
* N1 . ( N2 * N3 )
*
* Note: N refers to the normal, d refers to the displacement. '.' means dot
* product. '*' means cross product
*/
Vector3 v1, v2, v3;
Vector3 cross;
Vector3.Cross(ref b.Normal, ref c.Normal, out cross);
float f;
Vector3.Dot(ref a.Normal, ref cross, out f);
f *= -1.0f;
Vector3.Cross(ref b.Normal, ref c.Normal, out cross);
Vector3.Multiply(ref cross, a.D, out v1);
// v1 = (a.D * (Vector3.Cross(b.Normal, c.Normal)));
Vector3.Cross(ref c.Normal, ref a.Normal, out cross);
Vector3.Multiply(ref cross, b.D, out v2);
// v2 = (b.D * (Vector3.Cross(c.Normal, a.Normal)));
Vector3.Cross(ref a.Normal, ref b.Normal, out cross);
Vector3.Multiply(ref cross, c.D, out v3);
// v3 = (c.D * (Vector3.Cross(a.Normal, b.Normal)));
result.X = (v1.X + v2.X + v3.X) / f;
result.Y = (v1.Y + v2.Y + v3.Y) / f;
result.Z = (v1.Z + v2.Z + v3.Z) / f;
}
#endregion
#region Public Static Operators and Override Methods
/// <summary>
/// Compares whether two <see cref="BoundingFrustum"/> instances are equal.
/// </summary>
/// <param name="a"><see cref="BoundingFrustum"/> instance on the left of the equal sign.</param>
/// <param name="b"><see cref="BoundingFrustum"/> instance on the right of the equal sign.</param>
/// <returns><c>true</c> if the instances are equal; <c>false</c> otherwise.</returns>
public static bool operator ==(BoundingFrustum a, BoundingFrustum b)
{
if (object.Equals(a, null))
{
return (object.Equals(b, null));
}
if (object.Equals(b, null))
{
return (object.Equals(a, null));
}
return a.matrix == (b.matrix);
}
/// <summary>
/// Compares whether two <see cref="BoundingFrustum"/> instances are not equal.
/// </summary>
/// <param name="a"><see cref="BoundingFrustum"/> instance on the left of the not equal sign.</param>
/// <param name="b"><see cref="BoundingFrustum"/> instance on the right of the not equal sign.</param>
/// <returns><c>true</c> if the instances are not equal; <c>false</c> otherwise.</returns>
public static bool operator !=(BoundingFrustum a, BoundingFrustum b)
{
return !(a == b);
}
/// <summary>
/// Compares whether current instance is equal to specified <see cref="BoundingFrustum"/>.
/// </summary>
/// <param name="other">The <see cref="BoundingFrustum"/> to compare.</param>
/// <returns><c>true</c> if the instances are equal; <c>false</c> otherwise.</returns>
public bool Equals(BoundingFrustum other)
{
return (this == other);
}
/// <summary>
/// Compares whether current instance is equal to specified <see cref="BoundingFrustum"/>.
/// </summary>
/// <param name="obj">The <see cref="Object"/> to compare.</param>
/// <returns><c>true</c> if the instances are equal; <c>false</c> otherwise.</returns>
public override bool Equals(object obj)
{
return (obj is BoundingFrustum) && Equals((BoundingFrustum) obj);
}
/// <summary>
/// Returns a <see cref="String"/> representation of this <see cref="BoundingFrustum"/> in the format:
/// {Near:[nearPlane] Far:[farPlane] Left:[leftPlane] Right:[rightPlane] Top:[topPlane] Bottom:[bottomPlane]}
/// </summary>
/// <returns><see cref="String"/> representation of this <see cref="BoundingFrustum"/>.</returns>
public override string ToString()
{
StringBuilder sb = new StringBuilder(256);
sb.Append("{Near:");
sb.Append(this.planes[0].ToString());
sb.Append(" Far:");
sb.Append(this.planes[1].ToString());
sb.Append(" Left:");
sb.Append(this.planes[2].ToString());
sb.Append(" Right:");
sb.Append(this.planes[3].ToString());
sb.Append(" Top:");
sb.Append(this.planes[4].ToString());
sb.Append(" Bottom:");
sb.Append(this.planes[5].ToString());
sb.Append("}");
return sb.ToString();
}
/// <summary>
/// Gets the hash code of this <see cref="BoundingFrustum"/>.
/// </summary>
/// <returns>Hash code of this <see cref="BoundingFrustum"/>.</returns>
public override int GetHashCode()
{
return this.matrix.GetHashCode();
}
#endregion
}
}