/Utilities/Datatypes/Ray.cs
C# | 493 lines | 351 code | 29 blank | 113 comment | 30 complexity | 5e4dfe9a7998bb045a033a4ffd9f44bf MD5 | raw file
Possible License(s): Apache-2.0
- using System;
- using System.Diagnostics;
- using System.IO;
- using System.Runtime.InteropServices;
- using Delta.Utilities.Datatypes.Advanced;
- using NUnit.Framework;
-
- namespace Delta.Utilities.Datatypes
- {
- /// <summary>
- /// Ray struct, used to fire rays into a 3D scene to find out what we can
- /// hit with that ray (for mouse picking and other simple collision stuff)
- /// </summary>ZeroTolerance
- [StructLayout(LayoutKind.Sequential)]
- [DebuggerDisplay("Ray(RayStart={RayStart.X}, {RayStart.Y}, {RayStart.Z}, " +
- "Direction=({Direction.X}, {Direction.Y}, {Direction.Z})")]
- public struct Ray : ISaveLoadBinary, IEquatable<Ray>, IIntersects
- {
- #region Constants
- private const float ZeroTolerance = 0.00001f;
- #endregion
-
- #region Position (Public)
- /// <summary>
- /// Gets or sets the starting point of the ray
- /// </summary>
- public Vector Position;
- #endregion
-
- #region Direction (Public)
- /// <summary>
- /// Gets or sets the direction of the ray
- /// </summary>
- public Vector Direction;
- #endregion
-
- #region Constructors
- /// <summary>
- /// Create ray
- /// </summary>
- /// <param name="rayStart">Ray start</param>
- /// <param name="direction">Direction</param>
- public Ray(Vector rayStart, Vector direction)
- {
- Position = rayStart;
- Direction = direction;
- }
- #endregion
-
- #region IEquatable<Ray> Members
- /// <summary>
- /// Check if another ray is equal to this ray.
- /// </summary>
- /// <param name="other">Other ray</param>
- /// <returns>
- /// True if the other ray has the same values as this ray.
- /// </returns>
- public bool Equals(Ray other)
- {
- return this == other;
- }
- #endregion
-
- #region IIntersects Members
- /// <summary>
- /// Intersects the current object with specified plane and returns true
- /// plus the intersectionPosition if the plane intersects this object.
- /// </summary>
- /// <param name="plane">The plane to check with</param>
- /// <param name="intersectionPosition">The intersection vector</param>
- /// <returns>True if the plane intersected with this object</returns>
- public bool Intersects(Plane plane, out Vector intersectionPosition)
- {
- intersectionPosition = Vector.Zero;
- float direction = Vector.Dot(plane.Normal, Direction);
- if (Math.Abs(direction) < ZeroTolerance)
- {
- intersectionPosition = Vector.Zero;
- return false;
- }
- float position = Vector.Dot(plane.Normal, Position);
- float distance = (plane.Distance - position) / direction;
- if (distance < 0f)
- {
- if (distance < -ZeroTolerance)
- {
- return false;
- }
- return true;
- }
- intersectionPosition = Position + (Direction * distance);
- return true;
- }
-
- /// <summary>
- /// Intersects the current object with specified ray and returns true
- /// plus the intersectionPosition if the ray hits this object.
- /// </summary>
- /// <param name="ray">The ray to check with</param>
- /// <param name="intersectionPosition">The intersection vector</param>
- /// <returns>True if the ray intersected with this object</returns>
- public bool Intersects(Ray ray, out Vector intersectionPosition)
- {
- Vector cross = Vector.Cross(Direction, ray.Direction);
- intersectionPosition = Vector.Zero;
- float denominator = cross.Length;
- // 5Lines are parallel.
- if (Math.Abs(denominator) < ZeroTolerance)
- {
- // Lines are parallel and on top of each other.
- if (Math.Abs(ray.Position.X - Position.X) < ZeroTolerance &&
- Math.Abs(ray.Position.Y - Position.Y) < ZeroTolerance &&
- Math.Abs(ray.Position.Z - Position.Z) < ZeroTolerance)
- {
- return true;
- }
- }
- denominator = denominator * denominator;
- // 3x3 matrix for the first ray.
- float m11 = ray.Position.X - Position.X;
- float m12 = ray.Position.Y - Position.Y;
- float m13 = ray.Position.Z - Position.Z;
- float m21 = ray.Direction.X;
- float m22 = ray.Direction.Y;
- float m23 = ray.Direction.Z;
- float m31 = cross.X;
- float m32 = cross.Y;
- float m33 = cross.Z;
- // Determinant of first matrix.
- float dets =
- m11 * m22 * m33 +
- m12 * m23 * m31 +
- m13 * m21 * m32 -
- m11 * m23 * m32 -
- m12 * m21 * m33 -
- m13 * m22 * m31;
- // 3x3 matrix for the second ray.
- m21 = Direction.X;
- m22 = Direction.Y;
- m23 = Direction.Z;
- // Determinant of the second matrix.
- float dett =
- m11 * m22 * m33 +
- m12 * m23 * m31 +
- m13 * m21 * m32 -
- m11 * m23 * m32 -
- m12 * m21 * m33 -
- m13 * m22 * m31;
- // t values of the point of intersection.
- float s = dets / denominator;
- float t = dett / denominator;
- //The points of intersection.
- Vector point1 = Position + (s * Direction);
- Vector point2 = ray.Position + (t * ray.Direction);
- // If the points are not equal, no intersection has occurred.
- if (Math.Abs(point2.X - point1.X) > ZeroTolerance ||
- Math.Abs(point2.Y - point1.Y) > ZeroTolerance ||
- Math.Abs(point2.Z - point1.Z) > ZeroTolerance)
- {
- return false;
- }
- intersectionPosition = point1;
- return true;
- }
-
- /// <summary>
- /// Intersects the current object with the specified sphere.
- /// </summary>
- /// <param name="sphere">The sphere to check against</param>
- /// <returns>True if the sphere intersected with this object</returns>
- public bool Intersects(BoundingSphere sphere)
- {
- Vector distance;
- Vector.Subtract(ref Position, ref sphere.Center, out distance);
- float b = Vector.Dot(distance, Direction);
- float c = Vector.Dot(distance, distance) -
- (sphere.Radius * sphere.Radius);
- if (c > 0f && b > 0f)
- {
- return false;
- }
- float discriminant = b * b - c;
-
- if (discriminant < 0f)
- {
- return false;
- }
- float intersectionPoint = -b - (float)Math.Sqrt(discriminant);
- if (intersectionPoint < 0f)
- {
- return true;
- }
- return false;
- }
-
- /// <summary>
- /// Intersects the current object with the specified box.
- /// </summary>
- /// <param name="box">The box to check against</param>
- /// <returns>True if the box intersected with this object</returns>
- public bool Intersects(BoundingBox box)
- {
- float intersectionPoint = 0f;
- float tmax = float.MaxValue;
-
- if (Math.Abs(Direction.X) < ZeroTolerance)
- {
- if (Position.X < box.Min.X || Position.X > box.Max.X)
- {
- return false;
- }
- }
- else
- {
- float inverse = 1.0f / Direction.X;
- float t1 = (box.Min.X - Position.X) * inverse;
- float t2 = (box.Max.X - Position.X) * inverse;
-
- if (t1 > t2)
- {
- float temp = t1;
- t1 = t2;
- t2 = temp;
- }
- intersectionPoint = Math.Max(t1, intersectionPoint);
- tmax = Math.Min(t2, tmax);
- if (intersectionPoint > tmax)
- {
- return false;
- }
- }
- if (Math.Abs(Direction.Y) < ZeroTolerance)
- {
- if (Position.Y < box.Min.Y || Position.Y > box.Max.Y)
- {
- return false;
- }
- }
- else
- {
- float inverse = 1.0f / Direction.Y;
- float t1 = (box.Min.Y - Position.Y) * inverse;
- float t2 = (box.Max.Y - Position.Y) * inverse;
-
- if (t1 > t2)
- {
- float temp = t1;
- t1 = t2;
- t2 = temp;
- }
- intersectionPoint = Math.Max(t1, intersectionPoint);
- tmax = Math.Min(t2, tmax);
- if (intersectionPoint > tmax)
- {
- return false;
- }
- }
- if (Math.Abs(Direction.Z) < ZeroTolerance)
- {
- if (Position.Z < box.Min.Z || Position.Z > box.Max.Z)
- {
- return false;
- }
- }
- else
- {
- float inverse = 1.0f / Direction.Z;
- float t1 = (box.Min.Z - Position.Z) * inverse;
- float t2 = (box.Max.Z - Position.Z) * inverse;
- if (t1 > t2)
- {
- float temp = t1;
- t1 = t2;
- t2 = temp;
- }
- intersectionPoint = Math.Max(t1, intersectionPoint);
- tmax = Math.Min(t2, tmax);
-
- if (intersectionPoint > tmax)
- {
- return false;
- }
- }
- return true;
- }
- #endregion
-
- #region ISaveLoadBinary Members
- /// <summary>
- /// Load the position and direction of this ray from a stream.
- /// </summary>
- /// <param name="reader">reader</param>
- public void Load(BinaryReader reader)
- {
- Position.Load(reader);
- Direction.Load(reader);
- }
-
- /// <summary>
- /// Save the position and direction of this ray into a stream.
- /// </summary>
- /// <param name="writer">writer</param>
- public void Save(BinaryWriter writer)
- {
- Position.Save(writer);
- Direction.Save(writer);
- }
- #endregion
-
- #region op_Equality (Operator)
- /// <summary>
- /// Operator for equality
- /// </summary>
- /// <param name="value1">Ray 1</param>
- /// <param name="value2">Ray 2</param>
- /// <returns>True if both rays are the same</returns>
- public static bool operator ==(Ray value1, Ray value2)
- {
- return (value1.Position == value2.Position) &&
- (value1.Direction == value2.Direction);
- }
- #endregion
-
- #region op_Inequality (Operator)
- /// <summary>
- /// Operator for inequality
- /// </summary>
- /// <param name="value1">Ray 1</param>
- /// <param name="value2">Ray 2</param>
- /// <returns>True if both rays are not the same</returns>
- public static bool operator !=(Ray value1, Ray value2)
- {
- return !(value1 == value2);
- }
- #endregion
-
- #region Equals (Public)
- /// <summary>
- /// Equals
- /// </summary>
- /// <param name="obj">Other ray</param>
- /// <returns>
- /// True if the other ray has the same values as this ray.
- /// </returns>
- public override bool Equals(object obj)
- {
- bool flag = false;
- if (obj is Ray)
- {
- flag = Equals((Ray)obj);
- }
-
- return flag;
- }
- #endregion
-
- #region GetHashCode (Public)
- /// <summary>
- /// Get hash code
- /// </summary>
- public override int GetHashCode()
- {
- return Position.GetHashCode() ^ Direction.GetHashCode();
- }
- #endregion
-
- #region ToString (Public)
- /// <summary>
- /// To string
- /// </summary>
- /// <returns>string</returns>
- public override string ToString()
- {
- return "(Position:" + Position + " Direction:" + Direction + ")";
- }
- #endregion
-
- /// <summary>
- /// Tests
- /// </summary>
- internal class RayTests
- {
- #region TestProperties (Static)
- /// <summary>
- /// Test properties
- /// </summary>
- [Test]
- public static void TestProperties()
- {
- var testRay = new Ray(Vector.Zero, new Vector(1f, 0.4f, 0f));
- Assert.Equal(Vector.Zero, testRay.Position);
- Assert.Equal(new Vector(1f, 0.4f, 0f), testRay.Direction);
- }
- #endregion
-
- #region TestEquals (Static)
- /// <summary>
- /// Test equals
- /// </summary>
- [Test]
- public static void TestEquals()
- {
- var testRay1 = new Ray(Vector.Zero, new Vector(1f, 0.4f, 0f));
- var testRay2 = new Ray(Vector.UnitY, new Vector(1f, 0.4f, 0f));
- Assert.True(testRay1.Equals(testRay1));
- Assert.False(testRay1.Equals(testRay2));
- }
- #endregion
-
- #region TestToString (Static)
- /// <summary>
- /// Test to string
- /// </summary>
- [Test]
- public static void TestToString()
- {
- var testRay = new Ray(Vector.Zero, new Vector(1f, 0.4f, 0f));
- Assert.Equal(
- "(Position:(0.0000, 0.0000, 0.0000) Direction:(1.0000, 0.4000, 0.0000))",
- testRay.ToString());
- }
- #endregion
-
- #region TestIntersectPlane
- /// <summary>
- /// Test for Ray.Intersects(Plane)
- /// </summary>
- [Test]
- public void TestIntersectPlane()
- {
- Vector point;
- var testRay1 = new Ray(Vector.UnitZ, new Vector(0.1f, 0.1f, -0.2f));
- var testRay2 = new Ray(Vector.UnitZ, new Vector(0.1f, 0.1f, 0.1f));
- var testPlane = new Plane(Vector.UnitZ, 0);
- bool IsIntersect = testRay1.Intersects(testPlane, out point);
- bool IsIntersectNot = testRay2.Intersects(testPlane, out point);
- Assert.Equal(IsIntersect, true);
- Assert.Equal(IsIntersectNot, false);
- }
- #endregion
-
- #region TestIntersectsBox
- /// <summary>
- /// Test Ray.Intersects(Box)
- /// </summary>
- [Test]
- public void TestIntersectsBox()
- {
- var testRay1 = new Ray(new Vector(0f, -10f, 10f),
- new Vector(0f, 10f, -10f));
- var testRay2 = new Ray(new Vector(0f, -10f, 10f), Vector.Zero);
- var testBox = new BoundingBox(
- new Vector(-0.5f, -0.5f, -0.5f),
- new Vector(0.5f, 0.5f, 0.5f));
- Assert.True(testRay1.Intersects(testBox));
- Assert.False(testRay2.Intersects(testBox));
- }
- #endregion
-
- #region TestIntersectSphere
- /// <summary>
- /// Test Ray.Intersects(Sphere)
- /// </summary>
- [Test]
- public void TestIntersectSphere()
- {
- var testRay1 = new Ray(Vector.Half,
- new Vector(0f, 10f, 10f));
- var testRay2 = new Ray(new Vector(0f, -10f, 10f), Vector.Zero);
- var sphere = new BoundingSphere(Vector.Half, 5.0f);
- Assert.True(testRay1.Intersects(sphere));
- Assert.False(testRay2.Intersects(sphere));
- }
- #endregion
-
- #region TestIntersectRay
- /// <summary>
- /// Test Ray.Intersects(Ray)
- /// </summary>
- [Test]
- public void TestIntersectRay()
- {
- Vector point1, point2;
- var one = new Ray(new Vector(-1, -1, 2), new Vector(-2, -1, 2));
- var two = new Ray(new Vector(7, 7, 0), new Vector(4, 4, 0));
- var three = new Ray(new Vector(6, 5, 0), new Vector(4, 7, 0));
- Assert.True(two.Intersects(three, out point1));
- Assert.False(one.Intersects(two, out point2));
- }
- #endregion
- }
- }
- }