pull/14/head
cosmonaut 2021-01-20 14:44:59 -08:00
parent d22a70c116
commit e251d30aa9
19 changed files with 11718 additions and 1 deletions

23
licenses/LICENSE Normal file
View File

@ -0,0 +1,23 @@
MoonWorks - Game Development Framework
Copyright (c) 2021 Evan Hemsley
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from
the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Evan "cosmonaut" Hemsley <evan@moonside.games>

63
licenses/fna.LICENSE Normal file
View File

@ -0,0 +1,63 @@
Microsoft Public License (Ms-PL)
FNA - Copyright 2009-2021 Ethan Lee and the MonoGame Team
All rights reserved.
This license governs use of the accompanying software. If you use the software,
you accept this license. If you do not accept the license, do not use the
software.
1. Definitions
The terms "reproduce," "reproduction," "derivative works," and "distribution"
have the same meaning here as under U.S. copyright law.
A "contribution" is the original software, or any additions or changes to the
software.
A "contributor" is any person that distributes its contribution under this
license.
"Licensed patents" are a contributor's patent claims that read directly on its
contribution.
2. Grant of Rights
(A) Copyright Grant- Subject to the terms of this license, including the
license conditions and limitations in section 3, each contributor grants you a
non-exclusive, worldwide, royalty-free copyright license to reproduce its
contribution, prepare derivative works of its contribution, and distribute its
contribution or any derivative works that you create.
(B) Patent Grant- Subject to the terms of this license, including the license
conditions and limitations in section 3, each contributor grants you a
non-exclusive, worldwide, royalty-free license under its licensed patents to
make, have made, use, sell, offer for sale, import, and/or otherwise dispose of
its contribution in the software or derivative works of the contribution in the
software.
3. Conditions and Limitations
(A) No Trademark License- This license does not grant you rights to use any
contributors' name, logo, or trademarks.
(B) If you bring a patent claim against any contributor over patents that you
claim are infringed by the software, your patent license from such contributor
to the software ends automatically.
(C) If you distribute any portion of the software, you must retain all
copyright, patent, trademark, and attribution notices that are present in the
software.
(D) If you distribute any portion of the software in source code form, you may
do so only under this license by including a complete copy of this license with
your distribution. If you distribute any portion of the software in compiled or
object code form, you may only do so under a license that complies with this
license.
(E) The software is licensed "as-is." You bear the risk of using it. The
contributors give no express warranties, guarantees or conditions. You may have
additional consumer rights under your local laws which this license cannot
change. To the extent permitted under your local laws, the contributors exclude
the implied warranties of merchantability, fitness for a particular purpose and
non-infringement.

22
licenses/monoxna.LICENSE Normal file
View File

@ -0,0 +1,22 @@
MIT License
Copyright 2006 The Mono.Xna Team
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -67,7 +67,7 @@ namespace MoonWorks.Audio
_pitch = value;
FAudio.FAudioSourceVoice_SetFrequencyRatio(
Handle,
(float) Math.Pow(2.0, _pitch) * doppler,
(float) System.Math.Pow(2.0, _pitch) * doppler,
0
);
}

614
src/Math/BoundingBox.cs Normal file
View File

@ -0,0 +1,614 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.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.Collections.Generic;
using System.Diagnostics;
#endregion
namespace MoonWorks.Math
{
[Serializable]
[DebuggerDisplay("{DebugDisplayString,nq}")]
public struct BoundingBox : IEquatable<BoundingBox>
{
#region Internal Properties
internal string DebugDisplayString
{
get
{
return string.Concat(
"Min( ", Min.DebugDisplayString, " ) \r\n",
"Max( ", Max.DebugDisplayString, " )"
);
}
}
#endregion
#region Public Fields
public Vector3 Min;
public Vector3 Max;
public const int CornerCount = 8;
#endregion
#region Private Static Variables
private static readonly Vector3 MaxVector3 = new Vector3(float.MaxValue);
private static readonly Vector3 MinVector3 = new Vector3(float.MinValue);
#endregion
#region Public Constructors
public BoundingBox(Vector3 min, Vector3 max)
{
this.Min = min;
this.Max = max;
}
#endregion
#region Public Methods
public void Contains(ref BoundingBox box, out ContainmentType result)
{
result = Contains(box);
}
public void Contains(ref BoundingSphere sphere, out ContainmentType result)
{
result = this.Contains(sphere);
}
public ContainmentType Contains(Vector3 point)
{
ContainmentType result;
this.Contains(ref point, out result);
return result;
}
public ContainmentType Contains(BoundingBox box)
{
// Test if all corner is in the same side of a face by just checking min and max
if ( box.Max.X < Min.X ||
box.Min.X > Max.X ||
box.Max.Y < Min.Y ||
box.Min.Y > Max.Y ||
box.Max.Z < Min.Z ||
box.Min.Z > Max.Z )
{
return ContainmentType.Disjoint;
}
if ( box.Min.X >= Min.X &&
box.Max.X <= Max.X &&
box.Min.Y >= Min.Y &&
box.Max.Y <= Max.Y &&
box.Min.Z >= Min.Z &&
box.Max.Z <= Max.Z )
{
return ContainmentType.Contains;
}
return ContainmentType.Intersects;
}
public ContainmentType Contains(BoundingFrustum frustum)
{
/* TODO: bad done here need a fix.
* Because the question is not if frustum contains box but the reverse and
* this is not the same.
*/
int i;
ContainmentType contained;
Vector3[] corners = frustum.GetCorners();
// First we check if frustum is in box.
for (i = 0; i < corners.Length; i += 1)
{
this.Contains(ref corners[i], out contained);
if (contained == ContainmentType.Disjoint)
{
break;
}
}
// This means we checked all the corners and they were all contain or instersect
if (i == corners.Length)
{
return ContainmentType.Contains;
}
// If i is not equal to zero, we can fastpath and say that this box intersects
if (i != 0)
{
return ContainmentType.Intersects;
}
/* If we get here, it means the first (and only) point we checked was
* actually contained in the frustum. So we assume that all other points
* will also be contained. If one of the points is disjoint, we can
* exit immediately saying that the result is Intersects
*/
i += 1;
for (; i < corners.Length; i += 1)
{
this.Contains(ref corners[i], out contained);
if (contained != ContainmentType.Contains)
{
return ContainmentType.Intersects;
}
}
/* If we get here, then we know all the points were actually contained,
* therefore result is Contains.
*/
return ContainmentType.Contains;
}
public ContainmentType Contains(BoundingSphere sphere)
{
if ( sphere.Center.X - Min.X >= sphere.Radius &&
sphere.Center.Y - Min.Y >= sphere.Radius &&
sphere.Center.Z - Min.Z >= sphere.Radius &&
Max.X - sphere.Center.X >= sphere.Radius &&
Max.Y - sphere.Center.Y >= sphere.Radius &&
Max.Z - sphere.Center.Z >= sphere.Radius )
{
return ContainmentType.Contains;
}
double dmin = 0;
double e = sphere.Center.X - Min.X;
if (e < 0)
{
if (e < -sphere.Radius)
{
return ContainmentType.Disjoint;
}
dmin += e * e;
}
else
{
e = sphere.Center.X - Max.X;
if (e > 0)
{
if (e > sphere.Radius)
{
return ContainmentType.Disjoint;
}
dmin += e * e;
}
}
e = sphere.Center.Y - Min.Y;
if (e < 0)
{
if (e < -sphere.Radius)
{
return ContainmentType.Disjoint;
}
dmin += e * e;
}
else
{
e = sphere.Center.Y - Max.Y;
if (e > 0)
{
if (e > sphere.Radius)
{
return ContainmentType.Disjoint;
}
dmin += e * e;
}
}
e = sphere.Center.Z - Min.Z;
if (e < 0)
{
if (e < -sphere.Radius)
{
return ContainmentType.Disjoint;
}
dmin += e * e;
}
else
{
e = sphere.Center.Z - Max.Z;
if (e > 0)
{
if (e > sphere.Radius)
{
return ContainmentType.Disjoint;
}
dmin += e * e;
}
}
if (dmin <= sphere.Radius * sphere.Radius)
{
return ContainmentType.Intersects;
}
return ContainmentType.Disjoint;
}
public void Contains(ref Vector3 point, out ContainmentType result)
{
// Determine if point is outside of this box.
if ( point.X < this.Min.X ||
point.X > this.Max.X ||
point.Y < this.Min.Y ||
point.Y > this.Max.Y ||
point.Z < this.Min.Z ||
point.Z > this.Max.Z )
{
result = ContainmentType.Disjoint;
}
else
{
result = ContainmentType.Contains;
}
}
public Vector3[] GetCorners()
{
return new Vector3[] {
new Vector3(this.Min.X, this.Max.Y, this.Max.Z),
new Vector3(this.Max.X, this.Max.Y, this.Max.Z),
new Vector3(this.Max.X, this.Min.Y, this.Max.Z),
new Vector3(this.Min.X, this.Min.Y, this.Max.Z),
new Vector3(this.Min.X, this.Max.Y, this.Min.Z),
new Vector3(this.Max.X, this.Max.Y, this.Min.Z),
new Vector3(this.Max.X, this.Min.Y, this.Min.Z),
new Vector3(this.Min.X, this.Min.Y, this.Min.Z)
};
}
public void GetCorners(Vector3[] corners)
{
if (corners == null)
{
throw new ArgumentNullException("corners");
}
if (corners.Length < 8)
{
throw new ArgumentOutOfRangeException("corners", "Not Enought Corners");
}
corners[0].X = this.Min.X;
corners[0].Y = this.Max.Y;
corners[0].Z = this.Max.Z;
corners[1].X = this.Max.X;
corners[1].Y = this.Max.Y;
corners[1].Z = this.Max.Z;
corners[2].X = this.Max.X;
corners[2].Y = this.Min.Y;
corners[2].Z = this.Max.Z;
corners[3].X = this.Min.X;
corners[3].Y = this.Min.Y;
corners[3].Z = this.Max.Z;
corners[4].X = this.Min.X;
corners[4].Y = this.Max.Y;
corners[4].Z = this.Min.Z;
corners[5].X = this.Max.X;
corners[5].Y = this.Max.Y;
corners[5].Z = this.Min.Z;
corners[6].X = this.Max.X;
corners[6].Y = this.Min.Y;
corners[6].Z = this.Min.Z;
corners[7].X = this.Min.X;
corners[7].Y = this.Min.Y;
corners[7].Z = this.Min.Z;
}
public Nullable<float> Intersects(Ray ray)
{
return ray.Intersects(this);
}
public void Intersects(ref Ray ray, out Nullable<float> result)
{
result = Intersects(ray);
}
public bool Intersects(BoundingFrustum frustum)
{
return frustum.Intersects(this);
}
public void Intersects(ref BoundingSphere sphere, out bool result)
{
result = Intersects(sphere);
}
public bool Intersects(BoundingBox box)
{
bool result;
Intersects(ref box, out result);
return result;
}
public PlaneIntersectionType Intersects(Plane plane)
{
PlaneIntersectionType result;
Intersects(ref plane, out result);
return result;
}
public void Intersects(ref BoundingBox box, out bool result)
{
if ((this.Max.X >= box.Min.X) && (this.Min.X <= box.Max.X))
{
if ((this.Max.Y < box.Min.Y) || (this.Min.Y > box.Max.Y))
{
result = false;
return;
}
result = (this.Max.Z >= box.Min.Z) && (this.Min.Z <= box.Max.Z);
return;
}
result = false;
return;
}
public bool Intersects(BoundingSphere sphere)
{
if ( sphere.Center.X - Min.X > sphere.Radius &&
sphere.Center.Y - Min.Y > sphere.Radius &&
sphere.Center.Z - Min.Z > sphere.Radius &&
Max.X - sphere.Center.X > sphere.Radius &&
Max.Y - sphere.Center.Y > sphere.Radius &&
Max.Z - sphere.Center.Z > sphere.Radius )
{
return true;
}
double dmin = 0;
if (sphere.Center.X - Min.X <= sphere.Radius)
{
dmin += (sphere.Center.X - Min.X) * (sphere.Center.X - Min.X);
}
else if (Max.X - sphere.Center.X <= sphere.Radius)
{
dmin += (sphere.Center.X - Max.X) * (sphere.Center.X - Max.X);
}
if (sphere.Center.Y - Min.Y <= sphere.Radius)
{
dmin += (sphere.Center.Y - Min.Y) * (sphere.Center.Y - Min.Y);
}
else if (Max.Y - sphere.Center.Y <= sphere.Radius)
{
dmin += (sphere.Center.Y - Max.Y) * (sphere.Center.Y - Max.Y);
}
if (sphere.Center.Z - Min.Z <= sphere.Radius)
{
dmin += (sphere.Center.Z - Min.Z) * (sphere.Center.Z - Min.Z);
}
else if (Max.Z - sphere.Center.Z <= sphere.Radius)
{
dmin += (sphere.Center.Z - Max.Z) * (sphere.Center.Z - Max.Z);
}
if (dmin <= sphere.Radius * sphere.Radius)
{
return true;
}
return false;
}
public void Intersects(ref Plane plane, out PlaneIntersectionType result)
{
// See http://zach.in.tu-clausthal.de/teaching/cg_literatur/lighthouse3d_view_frustum_culling/index.html
Vector3 positiveVertex;
Vector3 negativeVertex;
if (plane.Normal.X >= 0)
{
positiveVertex.X = Max.X;
negativeVertex.X = Min.X;
}
else
{
positiveVertex.X = Min.X;
negativeVertex.X = Max.X;
}
if (plane.Normal.Y >= 0)
{
positiveVertex.Y = Max.Y;
negativeVertex.Y = Min.Y;
}
else
{
positiveVertex.Y = Min.Y;
negativeVertex.Y = Max.Y;
}
if (plane.Normal.Z >= 0)
{
positiveVertex.Z = Max.Z;
negativeVertex.Z = Min.Z;
}
else
{
positiveVertex.Z = Min.Z;
negativeVertex.Z = Max.Z;
}
// Inline Vector3.Dot(plane.Normal, negativeVertex) + plane.D;
float distance = (
plane.Normal.X * negativeVertex.X +
plane.Normal.Y * negativeVertex.Y +
plane.Normal.Z * negativeVertex.Z +
plane.D
);
if (distance > 0)
{
result = PlaneIntersectionType.Front;
return;
}
// Inline Vector3.Dot(plane.Normal, positiveVertex) + plane.D;
distance = (
plane.Normal.X * positiveVertex.X +
plane.Normal.Y * positiveVertex.Y +
plane.Normal.Z * positiveVertex.Z +
plane.D
);
if (distance < 0)
{
result = PlaneIntersectionType.Back;
return;
}
result = PlaneIntersectionType.Intersecting;
}
public bool Equals(BoundingBox other)
{
return (this.Min == other.Min) && (this.Max == other.Max);
}
#endregion
#region Public Static Methods
/// <summary>
/// Create a bounding box from the given list of points.
/// </summary>
/// <param name="points">
/// The list of Vector3 instances defining the point cloud to bound.
/// </param>
/// <returns>A bounding box that encapsulates the given point cloud.</returns>
/// <exception cref="System.ArgumentException">
/// Thrown if the given list has no points.
/// </exception>
public static BoundingBox CreateFromPoints(IEnumerable<Vector3> points)
{
if (points == null)
{
throw new ArgumentNullException("points");
}
bool empty = true;
Vector3 minVec = MaxVector3;
Vector3 maxVec = MinVector3;
foreach (Vector3 ptVector in points)
{
minVec.X = (minVec.X < ptVector.X) ? minVec.X : ptVector.X;
minVec.Y = (minVec.Y < ptVector.Y) ? minVec.Y : ptVector.Y;
minVec.Z = (minVec.Z < ptVector.Z) ? minVec.Z : ptVector.Z;
maxVec.X = (maxVec.X > ptVector.X) ? maxVec.X : ptVector.X;
maxVec.Y = (maxVec.Y > ptVector.Y) ? maxVec.Y : ptVector.Y;
maxVec.Z = (maxVec.Z > ptVector.Z) ? maxVec.Z : ptVector.Z;
empty = false;
}
if (empty)
{
throw new ArgumentException("Collection is empty", "points");
}
return new BoundingBox(minVec, maxVec);
}
public static BoundingBox CreateFromSphere(BoundingSphere sphere)
{
BoundingBox result;
CreateFromSphere(ref sphere, out result);
return result;
}
public static void CreateFromSphere(ref BoundingSphere sphere, out BoundingBox result)
{
Vector3 corner = new Vector3(sphere.Radius);
result.Min = sphere.Center - corner;
result.Max = sphere.Center + corner;
}
public static BoundingBox CreateMerged(BoundingBox original, BoundingBox additional)
{
BoundingBox result;
CreateMerged(ref original, ref additional, out result);
return result;
}
public static void CreateMerged(ref BoundingBox original, ref BoundingBox additional, out BoundingBox result)
{
result.Min.X = System.Math.Min(original.Min.X, additional.Min.X);
result.Min.Y = System.Math.Min(original.Min.Y, additional.Min.Y);
result.Min.Z = System.Math.Min(original.Min.Z, additional.Min.Z);
result.Max.X = System.Math.Max(original.Max.X, additional.Max.X);
result.Max.Y = System.Math.Max(original.Max.Y, additional.Max.Y);
result.Max.Z = System.Math.Max(original.Max.Z, additional.Max.Z);
}
#endregion
#region Public Static Operators and Override Methods
public override bool Equals(object obj)
{
return (obj is BoundingBox) && Equals((BoundingBox) obj);
}
public override int GetHashCode()
{
return this.Min.GetHashCode() + this.Max.GetHashCode();
}
public static bool operator ==(BoundingBox a, BoundingBox b)
{
return a.Equals(b);
}
public static bool operator !=(BoundingBox a, BoundingBox b)
{
return !a.Equals(b);
}
public override string ToString()
{
return (
"{{Min:" + Min.ToString() +
" Max:" + Max.ToString() +
"}}"
);
}
#endregion
}
}

729
src/Math/BoundingFrustum.cs Normal file
View File

@ -0,0 +1,729 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.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 MoonWorks.Math
{
/// <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
}
}

683
src/Math/BoundingSphere.cs Normal file
View File

@ -0,0 +1,683 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.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.Collections.Generic;
using System.Diagnostics;
#endregion
namespace MoonWorks.Math
{
/// <summary>
/// Describes a sphere in 3D-space for bounding operations.
/// </summary>
[Serializable]
[DebuggerDisplay("{DebugDisplayString,nq}")]
public struct BoundingSphere : IEquatable<BoundingSphere>
{
#region Internal Properties
internal string DebugDisplayString
{
get
{
return string.Concat(
"Center( ", Center.DebugDisplayString, " ) \r\n",
"Radius( ", Radius.ToString(), " ) "
);
}
}
#endregion
#region Public Fields
/// <summary>
/// The sphere center.
/// </summary>
public Vector3 Center;
/// <summary>
/// The sphere radius.
/// </summary>
public float Radius;
#endregion
#region Public Constructors
/// <summary>
/// Constructs a bounding sphere with the specified center and radius.
/// </summary>
/// <param name="center">The sphere center.</param>
/// <param name="radius">The sphere radius.</param>
public BoundingSphere(Vector3 center, float radius)
{
this.Center = center;
this.Radius = radius;
}
#endregion
#region Public Methods
/// <summary>
/// Creates a new <see cref="BoundingSphere"/> that contains a transformation of translation and scale from this sphere by the specified <see cref="Matrix"/>.
/// </summary>
/// <param name="matrix">The transformation <see cref="Matrix"/>.</param>
/// <returns>Transformed <see cref="BoundingSphere"/>.</returns>
public BoundingSphere Transform(Matrix matrix)
{
BoundingSphere sphere = new BoundingSphere();
sphere.Center = Vector3.Transform(this.Center, matrix);
sphere.Radius = this.Radius *
(
(float) System.Math.Sqrt((double) System.Math.Max(
((matrix.M11 * matrix.M11) + (matrix.M12 * matrix.M12)) + (matrix.M13 * matrix.M13),
System.Math.Max(
((matrix.M21 * matrix.M21) + (matrix.M22 * matrix.M22)) + (matrix.M23 * matrix.M23),
((matrix.M31 * matrix.M31) + (matrix.M32 * matrix.M32)) + (matrix.M33 * matrix.M33))
)
)
);
return sphere;
}
/// <summary>
/// Creates a new <see cref="BoundingSphere"/> that contains a transformation of translation and scale from this sphere by the specified <see cref="Matrix"/>.
/// </summary>
/// <param name="matrix">The transformation <see cref="Matrix"/>.</param>
/// <param name="result">Transformed <see cref="BoundingSphere"/> as an output parameter.</param>
public void Transform(ref Matrix matrix, out BoundingSphere result)
{
result.Center = Vector3.Transform(this.Center, matrix);
result.Radius = this.Radius *
(
(float) System.Math.Sqrt((double) System.Math.Max(
((matrix.M11 * matrix.M11) + (matrix.M12 * matrix.M12)) + (matrix.M13 * matrix.M13),
System.Math.Max(
((matrix.M21 * matrix.M21) + (matrix.M22 * matrix.M22)) + (matrix.M23 * matrix.M23),
((matrix.M31 * matrix.M31) + (matrix.M32 * matrix.M32)) + (matrix.M33 * matrix.M33))
)
)
);
}
/// <summary>
/// Test if a bounding box is fully inside, outside, or just intersecting the sphere.
/// </summary>
/// <param name="box">The box for testing.</param>
/// <param name="result">The containment type as an output parameter.</param>
public void Contains(ref BoundingBox box, out ContainmentType result)
{
result = this.Contains(box);
}
/// <summary>
/// Test if a sphere is fully inside, outside, or just intersecting the sphere.
/// </summary>
/// <param name="sphere">The other sphere for testing.</param>
/// <param name="result">The containment type as an output parameter.</param>
public void Contains(ref BoundingSphere sphere, out ContainmentType result)
{
result = Contains(sphere);
}
/// <summary>
/// Test if a point is fully inside, outside, or just intersecting the sphere.
/// </summary>
/// <param name="point">The vector in 3D-space for testing.</param>
/// <param name="result">The containment type as an output parameter.</param>
public void Contains(ref Vector3 point, out ContainmentType result)
{
result = Contains(point);
}
/// <summary>
/// Test if a bounding box is fully inside, outside, or just intersecting the sphere.
/// </summary>
/// <param name="box">The box for testing.</param>
/// <returns>The containment type.</returns>
public ContainmentType Contains(BoundingBox box)
{
// Check if all corners are in sphere.
bool inside = true;
foreach (Vector3 corner in box.GetCorners())
{
if (this.Contains(corner) == ContainmentType.Disjoint)
{
inside = false;
break;
}
}
if (inside)
{
return ContainmentType.Contains;
}
// Check if the distance from sphere center to cube face is less than radius.
double dmin = 0;
if (Center.X < box.Min.X)
{
dmin += (Center.X - box.Min.X) * (Center.X - box.Min.X);
}
else if (Center.X > box.Max.X)
{
dmin += (Center.X - box.Max.X) * (Center.X - box.Max.X);
}
if (Center.Y < box.Min.Y)
{
dmin += (Center.Y - box.Min.Y) * (Center.Y - box.Min.Y);
}
else if (Center.Y > box.Max.Y)
{
dmin += (Center.Y - box.Max.Y) * (Center.Y - box.Max.Y);
}
if (Center.Z < box.Min.Z)
{
dmin += (Center.Z - box.Min.Z) * (Center.Z - box.Min.Z);
}
else if (Center.Z > box.Max.Z)
{
dmin += (Center.Z - box.Max.Z) * (Center.Z - box.Max.Z);
}
if (dmin <= Radius * Radius)
{
return ContainmentType.Intersects;
}
// Else disjoint
return ContainmentType.Disjoint;
}
/// <summary>
/// Test if a frustum is fully inside, outside, or just intersecting the sphere.
/// </summary>
/// <param name="box">The box for testing.</param>
/// <param name="result">The containment type as an output parameter.</param>
public ContainmentType Contains(BoundingFrustum frustum)
{
// Check if all corners are in sphere.
bool inside = true;
Vector3[] corners = frustum.GetCorners();
foreach (Vector3 corner in corners)
{
if (this.Contains(corner) == ContainmentType.Disjoint)
{
inside = false;
break;
}
}
if (inside)
{
return ContainmentType.Contains;
}
// Check if the distance from sphere center to frustrum face is less than radius.
double dmin = 0;
// TODO : calcul dmin
if (dmin <= Radius * Radius)
{
return ContainmentType.Intersects;
}
// Else disjoint
return ContainmentType.Disjoint;
}
/// <summary>
/// Test if a sphere is fully inside, outside, or just intersecting the sphere.
/// </summary>
/// <param name="sphere">The other sphere for testing.</param>
/// <returns>The containment type.</returns>
public ContainmentType Contains(BoundingSphere sphere)
{
float sqDistance;
Vector3.DistanceSquared(ref sphere.Center, ref Center, out sqDistance);
if (sqDistance > (sphere.Radius + Radius) * (sphere.Radius + Radius))
{
return ContainmentType.Disjoint;
}
else if (sqDistance <= (Radius - sphere.Radius) * (Radius - sphere.Radius))
{
return ContainmentType.Contains;
}
return ContainmentType.Intersects;
}
/// <summary>
/// Test if a point is fully inside, outside, or just intersecting the sphere.
/// </summary>
/// <param name="point">The vector in 3D-space for testing.</param>
/// <returns>The containment type.</returns>
public ContainmentType Contains(Vector3 point)
{
float sqRadius = Radius * Radius;
float sqDistance;
Vector3.DistanceSquared(ref point, ref Center, out sqDistance);
if (sqDistance > sqRadius)
{
return ContainmentType.Disjoint;
}
else if (sqDistance < sqRadius)
{
return ContainmentType.Contains;
}
return ContainmentType.Intersects;
}
/// <summary>
/// Compares whether current instance is equal to specified <see cref="BoundingSphere"/>.
/// </summary>
/// <param name="other">The <see cref="BoundingSphere"/> to compare.</param>
/// <returns><c>true</c> if the instances are equal; <c>false</c> otherwise.</returns>
public bool Equals(BoundingSphere other)
{
return ( Center == other.Center &&
Radius == other.Radius );
}
#endregion
#region Public Static Methods
/// <summary>
/// Creates the smallest <see cref="BoundingSphere"/> that can contain a specified <see cref="BoundingBox"/>.
/// </summary>
/// <param name="box">The box to create the sphere from.</param>
/// <returns>The new <see cref="BoundingSphere"/>.</returns>
public static BoundingSphere CreateFromBoundingBox(BoundingBox box)
{
BoundingSphere result;
CreateFromBoundingBox(ref box, out result);
return result;
}
/// <summary>
/// Creates the smallest <see cref="BoundingSphere"/> that can contain a specified <see cref="BoundingBox"/>.
/// </summary>
/// <param name="box">The box to create the sphere from.</param>
/// <param name="result">The new <see cref="BoundingSphere"/> as an output parameter.</param>
public static void CreateFromBoundingBox(ref BoundingBox box, out BoundingSphere result)
{
// Find the center of the box.
Vector3 center = new Vector3(
(box.Min.X + box.Max.X) / 2.0f,
(box.Min.Y + box.Max.Y) / 2.0f,
(box.Min.Z + box.Max.Z) / 2.0f
);
// Find the distance between the center and one of the corners of the box.
float radius = Vector3.Distance(center, box.Max);
result = new BoundingSphere(center, radius);
}
/// <summary>
/// Creates the smallest <see cref="BoundingSphere"/> that can contain a specified <see cref="BoundingFrustum"/>.
/// </summary>
/// <param name="frustum">The frustum to create the sphere from.</param>
/// <returns>The new <see cref="BoundingSphere"/>.</returns>
public static BoundingSphere CreateFromFrustum(BoundingFrustum frustum)
{
return CreateFromPoints(frustum.GetCorners());
}
/// <summary>
/// Creates the smallest <see cref="BoundingSphere"/> that can contain a specified list of points in 3D-space.
/// </summary>
/// <param name="points">List of point to create the sphere from.</param>
/// <returns>The new <see cref="BoundingSphere"/>.</returns>
public static BoundingSphere CreateFromPoints(IEnumerable<Vector3> points)
{
if (points == null)
{
throw new ArgumentNullException("points");
}
// From "Real-Time Collision Detection" (Page 89)
Vector3 minx = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
Vector3 maxx = -minx;
Vector3 miny = minx;
Vector3 maxy = -minx;
Vector3 minz = minx;
Vector3 maxz = -minx;
// Find the most extreme points along the principle axis.
int numPoints = 0;
foreach (Vector3 pt in points)
{
numPoints += 1;
if (pt.X < minx.X)
{
minx = pt;
}
if (pt.X > maxx.X)
{
maxx = pt;
}
if (pt.Y < miny.Y)
{
miny = pt;
}
if (pt.Y > maxy.Y)
{
maxy = pt;
}
if (pt.Z < minz.Z)
{
minz = pt;
}
if (pt.Z > maxz.Z)
{
maxz = pt;
}
}
if (numPoints == 0)
{
throw new ArgumentException(
"You should have at least one point in points."
);
}
float sqDistX = Vector3.DistanceSquared(maxx, minx);
float sqDistY = Vector3.DistanceSquared(maxy, miny);
float sqDistZ = Vector3.DistanceSquared(maxz, minz);
// Pick the pair of most distant points.
Vector3 min = minx;
Vector3 max = maxx;
if (sqDistY > sqDistX && sqDistY > sqDistZ)
{
max = maxy;
min = miny;
}
if (sqDistZ > sqDistX && sqDistZ > sqDistY)
{
max = maxz;
min = minz;
}
Vector3 center = (min + max) * 0.5f;
float radius = Vector3.Distance(max, center);
// Test every point and expand the sphere.
// The current bounding sphere is just a good approximation and may not enclose all points.
// From: Mathematics for 3D Game Programming and Computer Graphics, Eric Lengyel, Third Edition.
// Page 218
float sqRadius = radius * radius;
foreach (Vector3 pt in points)
{
Vector3 diff = (pt - center);
float sqDist = diff.LengthSquared();
if (sqDist > sqRadius)
{
float distance = (float) System.Math.Sqrt(sqDist); // equal to diff.Length();
Vector3 direction = diff / distance;
Vector3 G = center - radius * direction;
center = (G + pt) / 2;
radius = Vector3.Distance(pt, center);
sqRadius = radius * radius;
}
}
return new BoundingSphere(center, radius);
}
/// <summary>
/// Creates the smallest <see cref="BoundingSphere"/> that can contain two spheres.
/// </summary>
/// <param name="original">First sphere.</param>
/// <param name="additional">Second sphere.</param>
/// <returns>The new <see cref="BoundingSphere"/>.</returns>
public static BoundingSphere CreateMerged(BoundingSphere original, BoundingSphere additional)
{
BoundingSphere result;
CreateMerged(ref original, ref additional, out result);
return result;
}
/// <summary>
/// Creates the smallest <see cref="BoundingSphere"/> that can contain two spheres.
/// </summary>
/// <param name="original">First sphere.</param>
/// <param name="additional">Second sphere.</param>
/// <param name="result">The new <see cref="BoundingSphere"/> as an output parameter.</param>
public static void CreateMerged(
ref BoundingSphere original,
ref BoundingSphere additional,
out BoundingSphere result
) {
Vector3 ocenterToaCenter = Vector3.Subtract(additional.Center, original.Center);
float distance = ocenterToaCenter.Length();
// Intersect
if (distance <= original.Radius + additional.Radius)
{
// Original contains additional.
if (distance <= original.Radius - additional.Radius)
{
result = original;
return;
}
// Additional contains original.
if (distance <= additional.Radius - original.Radius)
{
result = additional;
return;
}
}
// Else find center of new sphere and radius
float leftRadius = System.Math.Max(original.Radius - distance, additional.Radius);
float Rightradius = System.Math.Max(original.Radius + distance, additional.Radius);
// oCenterToResultCenter
ocenterToaCenter = ocenterToaCenter +
(
((leftRadius - Rightradius) / (2 * ocenterToaCenter.Length()))
* ocenterToaCenter
);
result = new BoundingSphere();
result.Center = original.Center + ocenterToaCenter;
result.Radius = (leftRadius + Rightradius) / 2;
}
/// <summary>
/// Gets whether or not a specified <see cref="BoundingBox"/> intersects with this sphere.
/// </summary>
/// <param name="box">The box for testing.</param>
/// <returns><c>true</c> if <see cref="BoundingBox"/> intersects with this sphere; <c>false</c> otherwise.</returns>
public bool Intersects(BoundingBox box)
{
return box.Intersects(this);
}
/// <summary>
/// Gets whether or not a specified <see cref="BoundingBox"/> intersects with this sphere.
/// </summary>
/// <param name="box">The box for testing.</param>
/// <param name="result"><c>true</c> if <see cref="BoundingBox"/> intersects with this sphere; <c>false</c> otherwise. As an output parameter.</param>
public void Intersects(ref BoundingBox box, out bool result)
{
box.Intersects(ref this, out result);
}
public bool Intersects(BoundingFrustum frustum)
{
return frustum.Intersects(this);
}
/// <summary>
/// Gets whether or not the other <see cref="BoundingSphere"/> intersects with this sphere.
/// </summary>
/// <param name="sphere">The other sphere for testing.</param>
/// <returns><c>true</c> if other <see cref="BoundingSphere"/> intersects with this sphere; <c>false</c> otherwise.</returns>
public bool Intersects(BoundingSphere sphere)
{
bool result;
Intersects(ref sphere, out result);
return result;
}
/// <summary>
/// Gets whether or not the other <see cref="BoundingSphere"/> intersects with this sphere.
/// </summary>
/// <param name="sphere">The other sphere for testing.</param>
/// <param name="result"><c>true</c> if other <see cref="BoundingSphere"/> intersects with this sphere; <c>false</c> otherwise. As an output parameter.</param>
public void Intersects(ref BoundingSphere sphere, out bool result)
{
float sqDistance;
Vector3.DistanceSquared(ref sphere.Center, ref Center, out sqDistance);
result = !(sqDistance > (sphere.Radius + Radius) * (sphere.Radius + Radius));
}
/// <summary>
/// Gets whether or not a specified <see cref="Ray"/> intersects with this sphere.
/// </summary>
/// <param name="ray">The ray for testing.</param>
/// <returns>Distance of ray intersection or <c>null</c> if there is no intersection.</returns>
public float? Intersects(Ray ray)
{
return ray.Intersects(this);
}
/// <summary>
/// Gets whether or not a specified <see cref="Ray"/> intersects with this sphere.
/// </summary>
/// <param name="ray">The ray for testing.</param>
/// <param name="result">Distance of ray intersection or <c>null</c> if there is no intersection as an output parameter.</param>
public void Intersects(ref Ray ray, out float? result)
{
ray.Intersects(ref this, out result);
}
/// <summary>
/// Gets whether or not a specified <see cref="Plane"/> intersects with this sphere.
/// </summary>
/// <param name="plane">The plane for testing.</param>
/// <returns>Type of intersection.</returns>
public PlaneIntersectionType Intersects(Plane plane)
{
PlaneIntersectionType result = default(PlaneIntersectionType);
// TODO: We might want to inline this for performance reasons.
this.Intersects(ref plane, out result);
return result;
}
/// <summary>
/// Gets whether or not a specified <see cref="Plane"/> intersects with this sphere.
/// </summary>
/// <param name="plane">The plane for testing.</param>
/// <param name="result">Type of intersection as an output parameter.</param>
public void Intersects(ref Plane plane, out PlaneIntersectionType result)
{
float distance = default(float);
// TODO: We might want to inline this for performance reasons.
Vector3.Dot(ref plane.Normal, ref this.Center, out distance);
distance += plane.D;
if (distance > this.Radius)
{
result = PlaneIntersectionType.Front;
}
else if (distance < -this.Radius)
{
result = PlaneIntersectionType.Back;
}
else
{
result = PlaneIntersectionType.Intersecting;
}
}
#endregion
#region Public Static Operators and Override Methods
/// <summary>
/// Compares whether current instance is equal to specified <see cref="Object"/>.
/// </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 BoundingSphere) && Equals((BoundingSphere) obj);
}
/// <summary>
/// Gets the hash code of this <see cref="BoundingSphere"/>.
/// </summary>
/// <returns>Hash code of this <see cref="BoundingSphere"/>.</returns>
public override int GetHashCode()
{
return this.Center.GetHashCode() + this.Radius.GetHashCode();
}
/// <summary>
/// Returns a <see cref="String"/> representation of this <see cref="BoundingSphere"/> in the format:
/// {Center:[<see cref="Center"/>] Radius:[<see cref="Radius"/>]}
/// </summary>
/// <returns>A <see cref="String"/> representation of this <see cref="BoundingSphere"/>.</returns>
public override string ToString()
{
return (
"{Center:" + Center.ToString() +
" Radius:" + Radius.ToString() +
"}"
);
}
/// <summary>
/// Compares whether two <see cref="BoundingSphere"/> instances are equal.
/// </summary>
/// <param name="a"><see cref="BoundingSphere"/> instance on the left of the equal sign.</param>
/// <param name="b"><see cref="BoundingSphere"/> 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 ==(BoundingSphere a, BoundingSphere b)
{
return a.Equals(b);
}
/// <summary>
/// Compares whether two <see cref="BoundingSphere"/> instances are not equal.
/// </summary>
/// <param name="a"><see cref="BoundingSphere"/> instance on the left of the not equal sign.</param>
/// <param name="b"><see cref="BoundingSphere"/> 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 !=(BoundingSphere a, BoundingSphere b)
{
return !a.Equals(b);
}
#endregion
}
}

View File

@ -0,0 +1,37 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
namespace MoonWorks.Math
{
/// <summary>
/// Defines how the bounding volumes intersects or contain one another.
/// </summary>
public enum ContainmentType
{
/// <summary>
/// Indicates that there is no overlap between two bounding volumes.
/// </summary>
Disjoint,
/// <summary>
/// Indicates that one bounding volume completely contains another volume.
/// </summary>
Contains,
/// <summary>
/// Indicates that bounding volumes partially overlap one another.
/// </summary>
Intersects
}
}

415
src/Math/MathHelper.cs Normal file
View File

@ -0,0 +1,415 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
namespace MoonWorks.Math
{
/// <summary>
/// Contains commonly used precalculated values and mathematical operations.
/// </summary>
public static class MathHelper
{
#region Public Constants
/// <summary>
/// Represents the mathematical constant e(2.71828175).
/// </summary>
public const float E = (float) System.Math.E;
/// <summary>
/// Represents the log base ten of e(0.4342945).
/// </summary>
public const float Log10E = 0.4342945f;
/// <summary>
/// Represents the log base two of e(1.442695).
/// </summary>
public const float Log2E = 1.442695f;
/// <summary>
/// Represents the value of pi(3.14159274).
/// </summary>
public const float Pi = (float) System.Math.PI;
/// <summary>
/// Represents the value of pi divided by two(1.57079637).
/// </summary>
public const float PiOver2 = (float) (System.Math.PI / 2.0);
/// <summary>
/// Represents the value of pi divided by four(0.7853982).
/// </summary>
public const float PiOver4 = (float) (System.Math.PI / 4.0);
/// <summary>
/// Represents the value of pi times two(6.28318548).
/// </summary>
public const float TwoPi = (float) (System.Math.PI * 2.0);
#endregion
#region Internal Static Readonly Fields
internal static readonly float MachineEpsilonFloat = GetMachineEpsilonFloat();
#endregion
#region Public Static Methods
/// <summary>
/// Returns the Cartesian coordinate for one axis of a point that is defined by a
/// given triangle and two normalized barycentric (areal) coordinates.
/// </summary>
/// <param name="value1">
/// The coordinate on one axis of vertex 1 of the defining triangle.
/// </param>
/// <param name="value2">
/// The coordinate on the same axis of vertex 2 of the defining triangle.
/// </param>
/// <param name="value3">
/// The coordinate on the same axis of vertex 3 of the defining triangle.
/// </param>
/// <param name="amount1">
/// The normalized barycentric (areal) coordinate b2, equal to the weighting factor
/// for vertex 2, the coordinate of which is specified in value2.
/// </param>
/// <param name="amount2">
/// The normalized barycentric (areal) coordinate b3, equal to the weighting factor
/// for vertex 3, the coordinate of which is specified in value3.
/// </param>
/// <returns>
/// Cartesian coordinate of the specified point with respect to the axis being used.
/// </returns>
public static float Barycentric(
float value1,
float value2,
float value3,
float amount1,
float amount2
) {
return value1 + (value2 - value1) * amount1 + (value3 - value1) * amount2;
}
/// <summary>
/// Performs a Catmull-Rom interpolation using the specified positions.
/// </summary>
/// <param name="value1">The first position in the interpolation.</param>
/// <param name="value2">The second position in the interpolation.</param>
/// <param name="value3">The third position in the interpolation.</param>
/// <param name="value4">The fourth position in the interpolation.</param>
/// <param name="amount">Weighting factor.</param>
/// <returns>A position that is the result of the Catmull-Rom interpolation.</returns>
public static float CatmullRom(
float value1,
float value2,
float value3,
float value4,
float amount
) {
/* Using formula from http://www.mvps.org/directx/articles/catmull/
* Internally using doubles not to lose precision.
*/
double amountSquared = amount * amount;
double amountCubed = amountSquared * amount;
return (float) (
0.5 *
(
((2.0 * value2 + (value3 - value1) * amount) +
((2.0 * value1 - 5.0 * value2 + 4.0 * value3 - value4) * amountSquared) +
(3.0 * value2 - value1 - 3.0 * value3 + value4) * amountCubed)
)
);
}
/// <summary>
/// Restricts a value to be within a specified range.
/// </summary>
/// <param name="value">The value to clamp.</param>
/// <param name="min">
/// The minimum value. If <c>value</c> is less than <c>min</c>, <c>min</c>
/// will be returned.
/// </param>
/// <param name="max">
/// The maximum value. If <c>value</c> is greater than <c>max</c>, <c>max</c>
/// will be returned.
/// </param>
/// <returns>The clamped value.</returns>
public static float Clamp(float value, float min, float max)
{
// First we check to see if we're greater than the max.
value = (value > max) ? max : value;
// Then we check to see if we're less than the min.
value = (value < min) ? min : value;
// There's no check to see if min > max.
return value;
}
/// <summary>
/// Calculates the absolute value of the difference of two values.
/// </summary>
/// <param name="value1">Source value.</param>
/// <param name="value2">Source value.</param>
/// <returns>Distance between the two values.</returns>
public static float Distance(float value1, float value2)
{
return System.Math.Abs(value1 - value2);
}
/// <summary>
/// Performs a Hermite spline interpolation.
/// </summary>
/// <param name="value1">Source position.</param>
/// <param name="tangent1">Source tangent.</param>
/// <param name="value2">Source position.</param>
/// <param name="tangent2">Source tangent.</param>
/// <param name="amount">Weighting factor.</param>
/// <returns>The result of the Hermite spline interpolation.</returns>
public static float Hermite(
float value1,
float tangent1,
float value2,
float tangent2,
float amount
) {
/* All transformed to double not to lose precision
* Otherwise, for high numbers of param:amount the result is NaN instead
* of Infinity.
*/
double v1 = value1, v2 = value2, t1 = tangent1, t2 = tangent2, s = amount;
double result;
double sCubed = s * s * s;
double sSquared = s * s;
if (WithinEpsilon(amount, 0f))
{
result = value1;
}
else if (WithinEpsilon(amount, 1f))
{
result = value2;
}
else
{
result = (
((2 * v1 - 2 * v2 + t2 + t1) * sCubed) +
((3 * v2 - 3 * v1 - 2 * t1 - t2) * sSquared) +
(t1 * s) +
v1
);
}
return (float) result;
}
/// <summary>
/// Linearly interpolates between two values.
/// </summary>
/// <param name="value1">Source value.</param>
/// <param name="value2">Source value.</param>
/// <param name="amount">
/// Value between 0 and 1 indicating the weight of value2.
/// </param>
/// <returns>Interpolated value.</returns>
/// <remarks>
/// This method performs the linear interpolation based on the following formula.
/// <c>value1 + (value2 - value1) * amount</c>
/// Passing amount a value of 0 will cause value1 to be returned, a value of 1 will
/// cause value2 to be returned.
/// </remarks>
public static float Lerp(float value1, float value2, float amount)
{
return value1 + (value2 - value1) * amount;
}
/// <summary>
/// Returns the greater of two values.
/// </summary>
/// <param name="value1">Source value.</param>
/// <param name="value2">Source value.</param>
/// <returns>The greater value.</returns>
public static float Max(float value1, float value2)
{
return value1 > value2 ? value1 : value2;
}
/// <summary>
/// Returns the lesser of two values.
/// </summary>
/// <param name="value1">Source value.</param>
/// <param name="value2">Source value.</param>
/// <returns>The lesser value.</returns>
public static float Min(float value1, float value2)
{
return value1 < value2 ? value1 : value2;
}
/// <summary>
/// Interpolates between two values using a cubic equation.
/// </summary>
/// <param name="value1">Source value.</param>
/// <param name="value2">Source value.</param>
/// <param name="amount">Weighting value.</param>
/// <returns>Interpolated value.</returns>
public static float SmoothStep(float value1, float value2, float amount)
{
/* It is expected that 0 < amount < 1.
* If amount < 0, return value1.
* If amount > 1, return value2.
*/
float result = MathHelper.Clamp(amount, 0f, 1f);
result = MathHelper.Hermite(value1, 0f, value2, 0f, result);
return result;
}
/// <summary>
/// Converts radians to degrees.
/// </summary>
/// <param name="radians">The angle in radians.</param>
/// <returns>The angle in degrees.</returns>
/// <remarks>
/// This method uses double precision internally, though it returns single float.
/// Factor = 180 / pi
/// </remarks>
public static float ToDegrees(float radians)
{
return (float) (radians * 57.295779513082320876798154814105);
}
/// <summary>
/// Converts degrees to radians.
/// </summary>
/// <param name="degrees">The angle in degrees.</param>
/// <returns>The angle in radians.</returns>
/// <remarks>
/// This method uses double precision internally, though it returns single float.
/// Factor = pi / 180
/// </remarks>
public static float ToRadians(float degrees)
{
return (float) (degrees * 0.017453292519943295769236907684886);
}
/// <summary>
/// Reduces a given angle to a value between pi and -pi.
/// </summary>
/// <param name="angle">The angle to reduce, in radians.</param>
/// <returns>The new angle, in radians.</returns>
public static float WrapAngle(float angle)
{
if ((angle > -Pi) && (angle <= Pi))
{
return angle;
}
angle %= TwoPi;
if (angle <= -Pi)
{
return angle + TwoPi;
}
if (angle > Pi)
{
return angle - TwoPi;
}
return angle;
}
#endregion
#region Internal Static Methods
// FIXME: This could be an extension! ClampIntEXT? -flibit
/// <summary>
/// Restricts a value to be within a specified range.
/// </summary>
/// <param name="value">The value to clamp.</param>
/// <param name="min">
/// The minimum value. If <c>value</c> is less than <c>min</c>, <c>min</c>
/// will be returned.
/// </param>
/// <param name="max">
/// The maximum value. If <c>value</c> is greater than <c>max</c>, <c>max</c>
/// will be returned.
/// </param>
/// <returns>The clamped value.</returns>
internal static int Clamp(int value, int min, int max)
{
value = (value > max) ? max : value;
value = (value < min) ? min : value;
return value;
}
internal static bool WithinEpsilon(float floatA, float floatB)
{
return System.Math.Abs(floatA - floatB) < MachineEpsilonFloat;
}
internal static int ClosestMSAAPower(int value)
{
/* Checking for the highest power of two _after_ than the given int:
* http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
* Take result, divide by 2, get the highest power of two _before_!
* -flibit
*/
if (value == 1)
{
// ... Except for 1, which is invalid for MSAA -flibit
return 0;
}
int result = value - 1;
result |= result >> 1;
result |= result >> 2;
result |= result >> 4;
result |= result >> 8;
result |= result >> 16;
result += 1;
if (result == value)
{
return result;
}
return result >> 1;
}
#endregion
#region Private Static Methods
/// <summary>
/// Find the current machine's Epsilon for the float data type.
/// (That is, the largest float, e, where e == 0.0f is true.)
/// </summary>
private static float GetMachineEpsilonFloat()
{
float machineEpsilon = 1.0f;
float comparison;
/* Keep halving the working value of machineEpsilon until we get a number that
* when added to 1.0f will still evaluate as equal to 1.0f.
*/
do
{
machineEpsilon *= 0.5f;
comparison = 1.0f + machineEpsilon;
}
while (comparison > 1.0f);
return machineEpsilon;
}
#endregion
}
}

2661
src/Math/Matrix.cs Normal file

File diff suppressed because it is too large Load Diff

329
src/Math/Plane.cs Normal file
View File

@ -0,0 +1,329 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.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;
#endregion
namespace MoonWorks.Math
{
[Serializable]
[DebuggerDisplay("{DebugDisplayString,nq}")]
public struct Plane : IEquatable<Plane>
{
#region Internal Properties
internal string DebugDisplayString
{
get
{
return string.Concat(
Normal.DebugDisplayString, " ",
D.ToString()
);
}
}
#endregion
#region Public Fields
public Vector3 Normal;
public float D;
#endregion
#region Public Constructors
public Plane(Vector4 value)
: this(new Vector3(value.X, value.Y, value.Z), value.W)
{
}
public Plane(Vector3 normal, float d)
{
Normal = normal;
D = d;
}
public Plane(Vector3 a, Vector3 b, Vector3 c)
{
Vector3 ab = b - a;
Vector3 ac = c - a;
Vector3 cross = Vector3.Cross(ab, ac);
Vector3.Normalize(ref cross, out Normal);
D = -(Vector3.Dot(Normal, a));
}
public Plane(float a, float b, float c, float d)
: this(new Vector3(a, b, c), d)
{
}
#endregion
#region Public Methods
public float Dot(Vector4 value)
{
return (
(this.Normal.X * value.X) +
(this.Normal.Y * value.Y) +
(this.Normal.Z * value.Z) +
(this.D * value.W)
);
}
public void Dot(ref Vector4 value, out float result)
{
result = (
(this.Normal.X * value.X) +
(this.Normal.Y * value.Y) +
(this.Normal.Z * value.Z) +
(this.D * value.W)
);
}
public float DotCoordinate(Vector3 value)
{
return (
(this.Normal.X * value.X) +
(this.Normal.Y * value.Y) +
(this.Normal.Z * value.Z) +
this.D
);
}
public void DotCoordinate(ref Vector3 value, out float result)
{
result = (
(this.Normal.X * value.X) +
(this.Normal.Y * value.Y) +
(this.Normal.Z * value.Z) +
this.D
);
}
public float DotNormal(Vector3 value)
{
return (
(this.Normal.X * value.X) +
(this.Normal.Y * value.Y) +
(this.Normal.Z * value.Z)
);
}
public void DotNormal(ref Vector3 value, out float result)
{
result = (
(this.Normal.X * value.X) +
(this.Normal.Y * value.Y) +
(this.Normal.Z * value.Z)
);
}
public void Normalize()
{
float length = Normal.Length();
float factor = 1.0f / length;
Vector3.Multiply(ref Normal, factor, out Normal);
D = D * factor;
}
public PlaneIntersectionType Intersects(BoundingBox box)
{
return box.Intersects(this);
}
public void Intersects(ref BoundingBox box, out PlaneIntersectionType result)
{
box.Intersects(ref this, out result);
}
public PlaneIntersectionType Intersects(BoundingSphere sphere)
{
return sphere.Intersects(this);
}
public void Intersects(ref BoundingSphere sphere, out PlaneIntersectionType result)
{
sphere.Intersects(ref this, out result);
}
public PlaneIntersectionType Intersects(BoundingFrustum frustum)
{
return frustum.Intersects(this);
}
#endregion
#region Internal Methods
internal PlaneIntersectionType Intersects(ref Vector3 point)
{
float distance;
DotCoordinate(ref point, out distance);
if (distance > 0)
{
return PlaneIntersectionType.Front;
}
if (distance < 0)
{
return PlaneIntersectionType.Back;
}
return PlaneIntersectionType.Intersecting;
}
#endregion
#region Public Static Methods
public static Plane Normalize(Plane value)
{
Plane ret;
Normalize(ref value, out ret);
return ret;
}
public static void Normalize(ref Plane value, out Plane result)
{
float length = value.Normal.Length();
float factor = 1.0f / length;
Vector3.Multiply(ref value.Normal, factor, out result.Normal);
result.D = value.D * factor;
}
/// <summary>
/// Transforms a normalized plane by a matrix.
/// </summary>
/// <param name="plane">The normalized plane to transform.</param>
/// <param name="matrix">The transformation matrix.</param>
/// <returns>The transformed plane.</returns>
public static Plane Transform(Plane plane, Matrix matrix)
{
Plane result;
Transform(ref plane, ref matrix, out result);
return result;
}
/// <summary>
/// Transforms a normalized plane by a matrix.
/// </summary>
/// <param name="plane">The normalized plane to transform.</param>
/// <param name="matrix">The transformation matrix.</param>
/// <param name="result">The transformed plane.</param>
public static void Transform(
ref Plane plane,
ref Matrix matrix,
out Plane result
) {
/* See "Transforming Normals" in
* http://www.glprogramming.com/red/appendixf.html
* for an explanation of how this works.
*/
Matrix transformedMatrix;
Matrix.Invert(ref matrix, out transformedMatrix);
Matrix.Transpose(
ref transformedMatrix,
out transformedMatrix
);
Vector4 vector = new Vector4(plane.Normal, plane.D);
Vector4 transformedVector;
Vector4.Transform(
ref vector,
ref transformedMatrix,
out transformedVector
);
result = new Plane(transformedVector);
}
/// <summary>
/// Transforms a normalized plane by a quaternion rotation.
/// </summary>
/// <param name="plane">The normalized plane to transform.</param>
/// <param name="rotation">The quaternion rotation.</param>
/// <returns>The transformed plane.</returns>
public static Plane Transform(Plane plane, Quaternion rotation)
{
Plane result;
Transform(ref plane, ref rotation, out result);
return result;
}
/// <summary>
/// Transforms a normalized plane by a quaternion rotation.
/// </summary>
/// <param name="plane">The normalized plane to transform.</param>
/// <param name="rotation">The quaternion rotation.</param>
/// <param name="result">The transformed plane.</param>
public static void Transform(
ref Plane plane,
ref Quaternion rotation,
out Plane result
) {
Vector3.Transform(
ref plane.Normal,
ref rotation,
out result.Normal
);
result.D = plane.D;
}
#endregion
#region Public Static Operators and Override Methods
public static bool operator !=(Plane plane1, Plane plane2)
{
return !plane1.Equals(plane2);
}
public static bool operator ==(Plane plane1, Plane plane2)
{
return plane1.Equals(plane2);
}
public override bool Equals(object obj)
{
return (obj is Plane) && this.Equals((Plane) obj);
}
public bool Equals(Plane other)
{
return (Normal == other.Normal && D == other.D);
}
public override int GetHashCode()
{
return Normal.GetHashCode() ^ D.GetHashCode();
}
public override string ToString()
{
return (
"{Normal:" + Normal.ToString() +
" D:" + D.ToString() +
"}"
);
}
#endregion
}
}

View File

@ -0,0 +1,37 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.LICENSE for details.
* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
namespace MoonWorks.Math
{
/// <summary>
/// Defines the intersection between a <see cref="Plane"/> and a bounding volume.
/// </summary>
public enum PlaneIntersectionType
{
/// <summary>
/// There is no intersection, the bounding volume is in the negative half space of the plane.
/// </summary>
Front,
/// <summary>
/// There is no intersection, the bounding volume is in the positive half space of the plane.
/// </summary>
Back,
/// <summary>
/// The plane is intersected.
/// </summary>
Intersecting
}
}

214
src/Math/Point.cs Normal file
View File

@ -0,0 +1,214 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.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;
#endregion
namespace MoonWorks.Math
{
/// <summary>
/// Describes a 2D-point.
/// </summary>
[Serializable]
[DebuggerDisplay("{DebugDisplayString,nq}")]
public struct Point : IEquatable<Point>
{
#region Public Static Properties
/// <summary>
/// Returns a <see cref="Point"/> with coordinates 0, 0.
/// </summary>
public static Point Zero
{
get
{
return zeroPoint;
}
}
#endregion
#region Internal Properties
internal string DebugDisplayString
{
get
{
return string.Concat(
X.ToString(), " ",
Y.ToString()
);
}
}
#endregion
#region Public Fields
/// <summary>
/// The x coordinate of this <see cref="Point"/>.
/// </summary>
public int X;
/// <summary>
/// The y coordinate of this <see cref="Point"/>.
/// </summary>
public int Y;
#endregion
#region Private Static Variables
private static readonly Point zeroPoint = new Point();
#endregion
#region Public Constructors
/// <summary>
/// Constructs a point with X and Y from two values.
/// </summary>
/// <param name="x">The x coordinate in 2d-space.</param>
/// <param name="y">The y coordinate in 2d-space.</param>
public Point(int x, int y)
{
this.X = x;
this.Y = y;
}
#endregion
#region Public Methods
/// <summary>
/// Compares whether current instance is equal to specified <see cref="Point"/>.
/// </summary>
/// <param name="other">The <see cref="Point"/> to compare.</param>
/// <returns><c>true</c> if the instances are equal; <c>false</c> otherwise.</returns>
public bool Equals(Point other)
{
return ((X == other.X) && (Y == other.Y));
}
/// <summary>
/// Compares whether current instance is equal to specified <see cref="Object"/>.
/// </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 Point) && Equals((Point) obj);
}
/// <summary>
/// Gets the hash code of this <see cref="Point"/>.
/// </summary>
/// <returns>Hash code of this <see cref="Point"/>.</returns>
public override int GetHashCode()
{
return X ^ Y;
}
/// <summary>
/// Returns a <see cref="String"/> representation of this <see cref="Point"/> in the format:
/// {X:[<see cref="X"/>] Y:[<see cref="Y"/>]}
/// </summary>
/// <returns><see cref="String"/> representation of this <see cref="Point"/>.</returns>
public override string ToString()
{
return (
"{X:" + X.ToString() +
" Y:" + Y.ToString() +
"}"
);
}
#endregion
#region Public Static Operators
/// <summary>
/// Adds two points.
/// </summary>
/// <param name="value1">Source <see cref="Point"/> on the left of the add sign.</param>
/// <param name="value2">Source <see cref="Point"/> on the right of the add sign.</param>
/// <returns>Sum of the points.</returns>
public static Point operator +(Point value1, Point value2)
{
return new Point(value1.X + value2.X, value1.Y + value2.Y);
}
/// <summary>
/// Subtracts a <see cref="Point"/> from a <see cref="Point"/>.
/// </summary>
/// <param name="value1">Source <see cref="Point"/> on the left of the sub sign.</param>
/// <param name="value2">Source <see cref="Point"/> on the right of the sub sign.</param>
/// <returns>Result of the subtraction.</returns>
public static Point operator -(Point value1, Point value2)
{
return new Point(value1.X - value2.X, value1.Y - value2.Y);
}
/// <summary>
/// Multiplies the components of two points by each other.
/// </summary>
/// <param name="value1">Source <see cref="Point"/> on the left of the mul sign.</param>
/// <param name="value2">Source <see cref="Point"/> on the right of the mul sign.</param>
/// <returns>Result of the multiplication.</returns>
public static Point operator *(Point value1, Point value2)
{
return new Point(value1.X * value2.X, value1.Y * value2.Y);
}
/// <summary>
/// Divides the components of a <see cref="Point"/> by the components of another <see cref="Point"/>.
/// </summary>
/// <param name="source">Source <see cref="Point"/> on the left of the div sign.</param>
/// <param name="divisor">Divisor <see cref="Point"/> on the right of the div sign.</param>
/// <returns>The result of dividing the points.</returns>
public static Point operator /(Point value1, Point value2)
{
return new Point(value1.X / value2.X, value1.Y / value2.Y);
}
/// <summary>
/// Compares whether two <see cref="Point"/> instances are equal.
/// </summary>
/// <param name="a"><see cref="Point"/> instance on the left of the equal sign.</param>
/// <param name="b"><see cref="Point"/> 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 ==(Point a, Point b)
{
return a.Equals(b);
}
/// <summary>
/// Compares whether two <see cref="Point"/> instances are not equal.
/// </summary>
/// <param name="a"><see cref="Point"/> instance on the left of the not equal sign.</param>
/// <param name="b"><see cref="Point"/> 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 !=(Point a, Point b)
{
return !a.Equals(b);
}
#endregion
}
}

989
src/Math/Quaternion.cs Normal file
View File

@ -0,0 +1,989 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.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;
#endregion
namespace MoonWorks.Math
{
/// <summary>
/// An efficient mathematical representation for three dimensional rotations.
/// </summary>
[Serializable]
[DebuggerDisplay("{DebugDisplayString,nq}")]
public struct Quaternion : IEquatable<Quaternion>
{
#region Public Static Properties
/// <summary>
/// Returns a quaternion representing no rotation.
/// </summary>
public static Quaternion Identity
{
get
{
return identity;
}
}
#endregion
#region Internal Properties
internal string DebugDisplayString
{
get
{
if (this == Quaternion.Identity)
{
return "Identity";
}
return string.Concat(
X.ToString(), " ",
Y.ToString(), " ",
Z.ToString(), " ",
W.ToString()
);
}
}
#endregion
#region Public Fields
/// <summary>
/// The x coordinate of this <see cref="Quaternion"/>.
/// </summary>
public float X;
/// <summary>
/// The y coordinate of this <see cref="Quaternion"/>.
/// </summary>
public float Y;
/// <summary>
/// The z coordinate of this <see cref="Quaternion"/>.
/// </summary>
public float Z;
/// <summary>
/// The rotation component of this <see cref="Quaternion"/>.
/// </summary>
public float W;
#endregion
#region Private Static Variables
private static readonly Quaternion identity = new Quaternion(0, 0, 0, 1);
#endregion
#region Public Constructors
/// <summary>
/// Constructs a quaternion with X, Y, Z and W from four values.
/// </summary>
/// <param name="x">The x coordinate in 3d-space.</param>
/// <param name="y">The y coordinate in 3d-space.</param>
/// <param name="z">The z coordinate in 3d-space.</param>
/// <param name="w">The rotation component.</param>
public Quaternion(float x, float y, float z, float w)
{
this.X = x;
this.Y = y;
this.Z = z;
this.W = w;
}
/// <summary>
/// Constructs a quaternion with X, Y, Z from <see cref="Vector3"/> and rotation component from a scalar.
/// </summary>
/// <param name="value">The x, y, z coordinates in 3d-space.</param>
/// <param name="w">The rotation component.</param>
public Quaternion(Vector3 vectorPart, float scalarPart)
{
this.X = vectorPart.X;
this.Y = vectorPart.Y;
this.Z = vectorPart.Z;
this.W = scalarPart;
}
#endregion
#region Public Methods
/// <summary>
/// Transforms this quaternion into its conjugated version.
/// </summary>
public void Conjugate()
{
X = -X;
Y = -Y;
Z = -Z;
}
/// <summary>
/// Compares whether current instance is equal to specified <see cref="Object"/>.
/// </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 Quaternion) && Equals((Quaternion) obj);
}
/// <summary>
/// Compares whether current instance is equal to specified <see cref="Quaternion"/>.
/// </summary>
/// <param name="other">The <see cref="Quaternion"/> to compare.</param>
/// <returns><c>true</c> if the instances are equal; <c>false</c> otherwise.</returns>
public bool Equals(Quaternion other)
{
return ( X == other.X &&
Y == other.Y &&
Z == other.Z &&
W == other.W );
}
/// <summary>
/// Gets the hash code of this <see cref="Quaternion"/>.
/// </summary>
/// <returns>Hash code of this <see cref="Quaternion"/>.</returns>
public override int GetHashCode()
{
return (
this.X.GetHashCode() +
this.Y.GetHashCode() +
this.Z.GetHashCode() +
this.W.GetHashCode()
);
}
/// <summary>
/// Returns the magnitude of the quaternion components.
/// </summary>
/// <returns>The magnitude of the quaternion components.</returns>
public float Length()
{
float num = (
(this.X * this.X) +
(this.Y * this.Y) +
(this.Z * this.Z) +
(this.W * this.W)
);
return (float) System.Math.Sqrt((double) num);
}
/// <summary>
/// Returns the squared magnitude of the quaternion components.
/// </summary>
/// <returns>The squared magnitude of the quaternion components.</returns>
public float LengthSquared()
{
return (
(this.X * this.X) +
(this.Y * this.Y) +
(this.Z * this.Z) +
(this.W * this.W)
);
}
/// <summary>
/// Scales the quaternion magnitude to unit length.
/// </summary>
public void Normalize()
{
float num = 1.0f / ((float) System.Math.Sqrt(
(X * X) +
(Y * Y) +
(Z * Z) +
(W * W)
));
this.X *= num;
this.Y *= num;
this.Z *= num;
this.W *= num;
}
/// <summary>
/// Returns a <see cref="String"/> representation of this <see cref="Quaternion"/> in the format:
/// {X:[<see cref="X"/>] Y:[<see cref="Y"/>] Z:[<see cref="Z"/>] W:[<see cref="W"/>]}
/// </summary>
/// <returns>A <see cref="String"/> representation of this <see cref="Quaternion"/>.</returns>
public override string ToString()
{
return (
"{X:" + X.ToString() +
" Y:" + Y.ToString() +
" Z:" + Z.ToString() +
" W:" + W.ToString() +
"}"
);
}
#endregion
#region Public Static Methods
/// <summary>
/// Creates a new <see cref="Quaternion"/> that contains the sum of two quaternions.
/// </summary>
/// <param name="quaternion1">Source <see cref="Quaternion"/>.</param>
/// <param name="quaternion2">Source <see cref="Quaternion"/>.</param>
/// <returns>The result of the quaternion addition.</returns>
public static Quaternion Add(Quaternion quaternion1, Quaternion quaternion2)
{
Quaternion quaternion;
Add(ref quaternion1, ref quaternion2, out quaternion);
return quaternion;
}
/// <summary>
/// Creates a new <see cref="Quaternion"/> that contains the sum of two quaternions.
/// </summary>
/// <param name="quaternion1">Source <see cref="Quaternion"/>.</param>
/// <param name="quaternion2">Source <see cref="Quaternion"/>.</param>
/// <param name="result">The result of the quaternion addition as an output parameter.</param>
public static void Add(
ref Quaternion quaternion1,
ref Quaternion quaternion2,
out Quaternion result
) {
result.X = quaternion1.X + quaternion2.X;
result.Y = quaternion1.Y + quaternion2.Y;
result.Z = quaternion1.Z + quaternion2.Z;
result.W = quaternion1.W + quaternion2.W;
}
/// <summary>
/// Creates a new <see cref="Quaternion"/> that contains concatenation between two quaternion.
/// </summary>
/// <param name="value1">The first <see cref="Quaternion"/> to concatenate.</param>
/// <param name="value2">The second <see cref="Quaternion"/> to concatenate.</param>
/// <returns>The result of rotation of <paramref name="value1"/> followed by <paramref name="value2"/> rotation.</returns>
public static Quaternion Concatenate(Quaternion value1, Quaternion value2)
{
Quaternion quaternion;
Concatenate(ref value1, ref value2, out quaternion);
return quaternion;
}
/// <summary>
/// Creates a new <see cref="Quaternion"/> that contains concatenation between two quaternion.
/// </summary>
/// <param name="value1">The first <see cref="Quaternion"/> to concatenate.</param>
/// <param name="value2">The second <see cref="Quaternion"/> to concatenate.</param>
/// <param name="result">The result of rotation of <paramref name="value1"/> followed by <paramref name="value2"/> rotation as an output parameter.</param>
public static void Concatenate(
ref Quaternion value1,
ref Quaternion value2,
out Quaternion result
) {
float x1 = value1.X;
float y1 = value1.Y;
float z1 = value1.Z;
float w1 = value1.W;
float x2 = value2.X;
float y2 = value2.Y;
float z2 = value2.Z;
float w2 = value2.W;
result.X = ((x2 * w1) + (x1 * w2)) + ((y2 * z1) - (z2 * y1));
result.Y = ((y2 * w1) + (y1 * w2)) + ((z2 * x1) - (x2 * z1));
result.Z = ((z2 * w1) + (z1 * w2)) + ((x2 * y1) - (y2 * x1));
result.W = (w2 * w1) - (((x2 * x1) + (y2 * y1)) + (z2 * z1));
}
/// <summary>
/// Creates a new <see cref="Quaternion"/> that contains conjugated version of the specified quaternion.
/// </summary>
/// <param name="value">The quaternion which values will be used to create the conjugated version.</param>
/// <returns>The conjugate version of the specified quaternion.</returns>
public static Quaternion Conjugate(Quaternion value)
{
return new Quaternion(-value.X, -value.Y, -value.Z, value.W);
}
/// <summary>
/// Creates a new <see cref="Quaternion"/> that contains conjugated version of the specified quaternion.
/// </summary>
/// <param name="value">The quaternion which values will be used to create the conjugated version.</param>
/// <param name="result">The conjugated version of the specified quaternion as an output parameter.</param>
public static void Conjugate(ref Quaternion value, out Quaternion result)
{
result.X = -value.X;
result.Y = -value.Y;
result.Z = -value.Z;
result.W = value.W;
}
/// <summary>
/// Creates a new <see cref="Quaternion"/> from the specified axis and angle.
/// </summary>
/// <param name="axis">The axis of rotation.</param>
/// <param name="angle">The angle in radians.</param>
/// <returns>The new quaternion builded from axis and angle.</returns>
public static Quaternion CreateFromAxisAngle(Vector3 axis, float angle)
{
Quaternion quaternion;
CreateFromAxisAngle(ref axis, angle, out quaternion);
return quaternion;
}
/// <summary>
/// Creates a new <see cref="Quaternion"/> from the specified axis and angle.
/// </summary>
/// <param name="axis">The axis of rotation.</param>
/// <param name="angle">The angle in radians.</param>
/// <param name="result">The new quaternion builded from axis and angle as an output parameter.</param>
public static void CreateFromAxisAngle(
ref Vector3 axis,
float angle,
out Quaternion result
) {
float half = angle * 0.5f;
float sin = (float) System.Math.Sin((double) half);
float cos = (float) System.Math.Cos((double) half);
result.X = axis.X * sin;
result.Y = axis.Y * sin;
result.Z = axis.Z * sin;
result.W = cos;
}
/// <summary>
/// Creates a new <see cref="Quaternion"/> from the specified <see cref="Matrix"/>.
/// </summary>
/// <param name="matrix">The rotation matrix.</param>
/// <returns>A quaternion composed from the rotation part of the matrix.</returns>
public static Quaternion CreateFromRotationMatrix(Matrix matrix)
{
Quaternion quaternion;
CreateFromRotationMatrix(ref matrix, out quaternion);
return quaternion;
}
/// <summary>
/// Creates a new <see cref="Quaternion"/> from the specified <see cref="Matrix"/>.
/// </summary>
/// <param name="matrix">The rotation matrix.</param>
/// <param name="result">A quaternion composed from the rotation part of the matrix as an output parameter.</param>
public static void CreateFromRotationMatrix(ref Matrix matrix, out Quaternion result)
{
float sqrt;
float half;
float scale = matrix.M11 + matrix.M22 + matrix.M33;
if (scale > 0.0f)
{
sqrt = (float) System.Math.Sqrt(scale + 1.0f);
result.W = sqrt * 0.5f;
sqrt = 0.5f / sqrt;
result.X = (matrix.M23 - matrix.M32) * sqrt;
result.Y = (matrix.M31 - matrix.M13) * sqrt;
result.Z = (matrix.M12 - matrix.M21) * sqrt;
}
else if ((matrix.M11 >= matrix.M22) && (matrix.M11 >= matrix.M33))
{
sqrt = (float) System.Math.Sqrt(1.0f + matrix.M11 - matrix.M22 - matrix.M33);
half = 0.5f / sqrt;
result.X = 0.5f * sqrt;
result.Y = (matrix.M12 + matrix.M21) * half;
result.Z = (matrix.M13 + matrix.M31) * half;
result.W = (matrix.M23 - matrix.M32) * half;
}
else if (matrix.M22 > matrix.M33)
{
sqrt = (float) System.Math.Sqrt(1.0f + matrix.M22 - matrix.M11 - matrix.M33);
half = 0.5f/sqrt;
result.X = (matrix.M21 + matrix.M12)*half;
result.Y = 0.5f*sqrt;
result.Z = (matrix.M32 + matrix.M23)*half;
result.W = (matrix.M31 - matrix.M13)*half;
}
else
{
sqrt = (float) System.Math.Sqrt(1.0f + matrix.M33 - matrix.M11 - matrix.M22);
half = 0.5f / sqrt;
result.X = (matrix.M31 + matrix.M13) * half;
result.Y = (matrix.M32 + matrix.M23) * half;
result.Z = 0.5f * sqrt;
result.W = (matrix.M12 - matrix.M21) * half;
}
}
/// <summary>
/// Creates a new <see cref="Quaternion"/> from the specified yaw, pitch and roll angles.
/// </summary>
/// <param name="yaw">Yaw around the y axis in radians.</param>
/// <param name="pitch">Pitch around the x axis in radians.</param>
/// <param name="roll">Roll around the z axis in radians.</param>
/// <returns>A new quaternion from the concatenated yaw, pitch, and roll angles.</returns>
public static Quaternion CreateFromYawPitchRoll(float yaw, float pitch, float roll)
{
Quaternion quaternion;
CreateFromYawPitchRoll(yaw, pitch, roll, out quaternion);
return quaternion;
}
/// <summary>
/// Creates a new <see cref="Quaternion"/> from the specified yaw, pitch and roll angles.
/// </summary>
/// <param name="yaw">Yaw around the y axis in radians.</param>
/// <param name="pitch">Pitch around the x axis in radians.</param>
/// <param name="roll">Roll around the z axis in radians.</param>
/// <param name="result">A new quaternion from the concatenated yaw, pitch, and roll angles as an output parameter.</param>
public static void CreateFromYawPitchRoll(
float yaw,
float pitch,
float roll,
out Quaternion result)
{
float halfRoll = roll * 0.5f;
float sinRoll = (float) System.Math.Sin(halfRoll);
float cosRoll = (float) System.Math.Cos(halfRoll);
float halfPitch = pitch * 0.5f;
float sinPitch = (float) System.Math.Sin(halfPitch);
float cosPitch = (float) System.Math.Cos(halfPitch);
float halfYaw = yaw * 0.5f;
float sinYaw = (float) System.Math.Sin(halfYaw);
float cosYaw = (float) System.Math.Cos(halfYaw);
result.X = ((cosYaw * sinPitch) * cosRoll) + ((sinYaw * cosPitch) * sinRoll);
result.Y = ((sinYaw * cosPitch) * cosRoll) - ((cosYaw * sinPitch) * sinRoll);
result.Z = ((cosYaw * cosPitch) * sinRoll) - ((sinYaw * sinPitch) * cosRoll);
result.W = ((cosYaw * cosPitch) * cosRoll) + ((sinYaw * sinPitch) * sinRoll);
}
/// <summary>
/// Divides a <see cref="Quaternion"/> by the other <see cref="Quaternion"/>.
/// </summary>
/// <param name="quaternion1">Source <see cref="Quaternion"/>.</param>
/// <param name="quaternion2">Divisor <see cref="Quaternion"/>.</param>
/// <returns>The result of dividing the quaternions.</returns>
public static Quaternion Divide(Quaternion quaternion1, Quaternion quaternion2)
{
Quaternion quaternion;
Divide(ref quaternion1, ref quaternion2, out quaternion);
return quaternion;
}
/// <summary>
/// Divides a <see cref="Quaternion"/> by the other <see cref="Quaternion"/>.
/// </summary>
/// <param name="quaternion1">Source <see cref="Quaternion"/>.</param>
/// <param name="quaternion2">Divisor <see cref="Quaternion"/>.</param>
/// <param name="result">The result of dividing the quaternions as an output parameter.</param>
public static void Divide(
ref Quaternion quaternion1,
ref Quaternion quaternion2,
out Quaternion result
) {
float x = quaternion1.X;
float y = quaternion1.Y;
float z = quaternion1.Z;
float w = quaternion1.W;
float num14 = (
(quaternion2.X * quaternion2.X) +
(quaternion2.Y * quaternion2.Y) +
(quaternion2.Z * quaternion2.Z) +
(quaternion2.W * quaternion2.W)
);
float num5 = 1f / num14;
float num4 = -quaternion2.X * num5;
float num3 = -quaternion2.Y * num5;
float num2 = -quaternion2.Z * num5;
float num = quaternion2.W * num5;
float num13 = (y * num2) - (z * num3);
float num12 = (z * num4) - (x * num2);
float num11 = (x * num3) - (y * num4);
float num10 = ((x * num4) + (y * num3)) + (z * num2);
result.X = ((x * num) + (num4 * w)) + num13;
result.Y = ((y * num) + (num3 * w)) + num12;
result.Z = ((z * num) + (num2 * w)) + num11;
result.W = (w * num) - num10;
}
/// <summary>
/// Returns a dot product of two quaternions.
/// </summary>
/// <param name="quaternion1">The first quaternion.</param>
/// <param name="quaternion2">The second quaternion.</param>
/// <returns>The dot product of two quaternions.</returns>
public static float Dot(Quaternion quaternion1, Quaternion quaternion2)
{
return (
(quaternion1.X * quaternion2.X) +
(quaternion1.Y * quaternion2.Y) +
(quaternion1.Z * quaternion2.Z) +
(quaternion1.W * quaternion2.W)
);
}
/// <summary>
/// Returns a dot product of two quaternions.
/// </summary>
/// <param name="quaternion1">The first quaternion.</param>
/// <param name="quaternion2">The second quaternion.</param>
/// <param name="result">The dot product of two quaternions as an output parameter.</param>
public static void Dot(
ref Quaternion quaternion1,
ref Quaternion quaternion2,
out float result
) {
result = (
(quaternion1.X * quaternion2.X) +
(quaternion1.Y * quaternion2.Y) +
(quaternion1.Z * quaternion2.Z) +
(quaternion1.W * quaternion2.W)
);
}
/// <summary>
/// Returns the inverse quaternion which represents the opposite rotation.
/// </summary>
/// <param name="quaternion">Source <see cref="Quaternion"/>.</param>
/// <returns>The inverse quaternion.</returns>
public static Quaternion Inverse(Quaternion quaternion)
{
Quaternion inverse;
Inverse(ref quaternion, out inverse);
return inverse;
}
/// <summary>
/// Returns the inverse quaternion which represents the opposite rotation.
/// </summary>
/// <param name="quaternion">Source <see cref="Quaternion"/>.</param>
/// <param name="result">The inverse quaternion as an output parameter.</param>
public static void Inverse(ref Quaternion quaternion, out Quaternion result)
{
float num2 = (
(quaternion.X * quaternion.X) +
(quaternion.Y * quaternion.Y) +
(quaternion.Z * quaternion.Z) +
(quaternion.W * quaternion.W)
);
float num = 1f / num2;
result.X = -quaternion.X * num;
result.Y = -quaternion.Y * num;
result.Z = -quaternion.Z * num;
result.W = quaternion.W * num;
}
/// <summary>
/// Performs a linear blend between two quaternions.
/// </summary>
/// <param name="quaternion1">Source <see cref="Quaternion"/>.</param>
/// <param name="quaternion2">Source <see cref="Quaternion"/>.</param>
/// <param name="amount">The blend amount where 0 returns <paramref name="quaternion1"/> and 1 <paramref name="quaternion2"/>.</param>
/// <returns>The result of linear blending between two quaternions.</returns>
public static Quaternion Lerp(
Quaternion quaternion1,
Quaternion quaternion2,
float amount
) {
Quaternion quaternion;
Lerp(ref quaternion1, ref quaternion2, amount, out quaternion);
return quaternion;
}
/// <summary>
/// Performs a linear blend between two quaternions.
/// </summary>
/// <param name="quaternion1">Source <see cref="Quaternion"/>.</param>
/// <param name="quaternion2">Source <see cref="Quaternion"/>.</param>
/// <param name="amount">The blend amount where 0 returns <paramref name="quaternion1"/> and 1 <paramref name="quaternion2"/>.</param>
/// <param name="result">The result of linear blending between two quaternions as an output parameter.</param>
public static void Lerp(
ref Quaternion quaternion1,
ref Quaternion quaternion2,
float amount,
out Quaternion result
) {
float num = amount;
float num2 = 1f - num;
float num5 = (
(quaternion1.X * quaternion2.X) +
(quaternion1.Y * quaternion2.Y) +
(quaternion1.Z * quaternion2.Z) +
(quaternion1.W * quaternion2.W)
);
if (num5 >= 0f)
{
result.X = (num2 * quaternion1.X) + (num * quaternion2.X);
result.Y = (num2 * quaternion1.Y) + (num * quaternion2.Y);
result.Z = (num2 * quaternion1.Z) + (num * quaternion2.Z);
result.W = (num2 * quaternion1.W) + (num * quaternion2.W);
}
else
{
result.X = (num2 * quaternion1.X) - (num * quaternion2.X);
result.Y = (num2 * quaternion1.Y) - (num * quaternion2.Y);
result.Z = (num2 * quaternion1.Z) - (num * quaternion2.Z);
result.W = (num2 * quaternion1.W) - (num * quaternion2.W);
}
float num4 = (
(result.X * result.X) +
(result.Y * result.Y) +
(result.Z * result.Z) +
(result.W * result.W)
);
float num3 = 1f / ((float) System.Math.Sqrt((double) num4));
result.X *= num3;
result.Y *= num3;
result.Z *= num3;
result.W *= num3;
}
/// <summary>
/// Performs a spherical linear blend between two quaternions.
/// </summary>
/// <param name="quaternion1">Source <see cref="Quaternion"/>.</param>
/// <param name="quaternion2">Source <see cref="Quaternion"/>.</param>
/// <param name="amount">The blend amount where 0 returns <paramref name="quaternion1"/> and 1 <paramref name="quaternion2"/>.</param>
/// <returns>The result of spherical linear blending between two quaternions.</returns>
public static Quaternion Slerp(
Quaternion quaternion1,
Quaternion quaternion2,
float amount
) {
Quaternion quaternion;
Slerp(ref quaternion1, ref quaternion2, amount, out quaternion);
return quaternion;
}
/// <summary>
/// Performs a spherical linear blend between two quaternions.
/// </summary>
/// <param name="quaternion1">Source <see cref="Quaternion"/>.</param>
/// <param name="quaternion2">Source <see cref="Quaternion"/>.</param>
/// <param name="amount">The blend amount where 0 returns <paramref name="quaternion1"/> and 1 <paramref name="quaternion2"/>.</param>
/// <param name="result">The result of spherical linear blending between two quaternions as an output parameter.</param>
public static void Slerp(
ref Quaternion quaternion1,
ref Quaternion quaternion2,
float amount,
out Quaternion result
) {
float num2;
float num3;
float num = amount;
float num4 = (
(quaternion1.X * quaternion2.X) +
(quaternion1.Y * quaternion2.Y) +
(quaternion1.Z * quaternion2.Z) +
(quaternion1.W * quaternion2.W)
);
float flag = 1.0f;
if (num4 < 0f)
{
flag = -1.0f;
num4 = -num4;
}
if (num4 > 0.999999f)
{
num3 = 1f - num;
num2 = num * flag;
}
else
{
float num5 = (float) System.Math.Acos((double) num4);
float num6 = (float) (1.0 / System.Math.Sin((double) num5));
num3 = ((float) System.Math.Sin((double) ((1f - num) * num5))) * num6;
num2 = flag * (((float) System.Math.Sin((double) (num * num5))) * num6);
}
result.X = (num3 * quaternion1.X) + (num2 * quaternion2.X);
result.Y = (num3 * quaternion1.Y) + (num2 * quaternion2.Y);
result.Z = (num3 * quaternion1.Z) + (num2 * quaternion2.Z);
result.W = (num3 * quaternion1.W) + (num2 * quaternion2.W);
}
/// <summary>
/// Creates a new <see cref="Quaternion"/> that contains subtraction of one <see cref="Quaternion"/> from another.
/// </summary>
/// <param name="quaternion1">Source <see cref="Quaternion"/>.</param>
/// <param name="quaternion2">Source <see cref="Quaternion"/>.</param>
/// <returns>The result of the quaternion subtraction.</returns>
public static Quaternion Subtract(Quaternion quaternion1, Quaternion quaternion2)
{
Quaternion quaternion;
Subtract(ref quaternion1, ref quaternion2, out quaternion);
return quaternion;
}
/// <summary>
/// Creates a new <see cref="Quaternion"/> that contains subtraction of one <see cref="Quaternion"/> from another.
/// </summary>
/// <param name="quaternion1">Source <see cref="Quaternion"/>.</param>
/// <param name="quaternion2">Source <see cref="Quaternion"/>.</param>
/// <param name="result">The result of the quaternion subtraction as an output parameter.</param>
public static void Subtract(
ref Quaternion quaternion1,
ref Quaternion quaternion2,
out Quaternion result
) {
result.X = quaternion1.X - quaternion2.X;
result.Y = quaternion1.Y - quaternion2.Y;
result.Z = quaternion1.Z - quaternion2.Z;
result.W = quaternion1.W - quaternion2.W;
}
/// <summary>
/// Creates a new <see cref="Quaternion"/> that contains a multiplication of two quaternions.
/// </summary>
/// <param name="quaternion1">Source <see cref="Quaternion"/>.</param>
/// <param name="quaternion2">Source <see cref="Quaternion"/>.</param>
/// <returns>The result of the quaternion multiplication.</returns>
public static Quaternion Multiply(Quaternion quaternion1, Quaternion quaternion2)
{
Quaternion quaternion;
Multiply(ref quaternion1, ref quaternion2, out quaternion);
return quaternion;
}
/// <summary>
/// Creates a new <see cref="Quaternion"/> that contains a multiplication of <see cref="Quaternion"/> and a scalar.
/// </summary>
/// <param name="quaternion1">Source <see cref="Quaternion"/>.</param>
/// <param name="scaleFactor">Scalar value.</param>
/// <returns>The result of the quaternion multiplication with a scalar.</returns>
public static Quaternion Multiply(Quaternion quaternion1, float scaleFactor)
{
Quaternion quaternion;
Multiply(ref quaternion1, scaleFactor, out quaternion);
return quaternion;
}
/// <summary>
/// Creates a new <see cref="Quaternion"/> that contains a multiplication of two quaternions.
/// </summary>
/// <param name="quaternion1">Source <see cref="Quaternion"/>.</param>
/// <param name="quaternion2">Source <see cref="Quaternion"/>.</param>
/// <param name="result">The result of the quaternion multiplication as an output parameter.</param>
public static void Multiply(
ref Quaternion quaternion1,
ref Quaternion quaternion2,
out Quaternion result
) {
float x = quaternion1.X;
float y = quaternion1.Y;
float z = quaternion1.Z;
float w = quaternion1.W;
float num4 = quaternion2.X;
float num3 = quaternion2.Y;
float num2 = quaternion2.Z;
float num = quaternion2.W;
float num12 = (y * num2) - (z * num3);
float num11 = (z * num4) - (x * num2);
float num10 = (x * num3) - (y * num4);
float num9 = ((x * num4) + (y * num3)) + (z * num2);
result.X = ((x * num) + (num4 * w)) + num12;
result.Y = ((y * num) + (num3 * w)) + num11;
result.Z = ((z * num) + (num2 * w)) + num10;
result.W = (w * num) - num9;
}
/// <summary>
/// Creates a new <see cref="Quaternion"/> that contains a multiplication of <see cref="Quaternion"/> and a scalar.
/// </summary>
/// <param name="quaternion1">Source <see cref="Quaternion"/>.</param>
/// <param name="scaleFactor">Scalar value.</param>
/// <param name="result">The result of the quaternion multiplication with a scalar as an output parameter.</param>
public static void Multiply(
ref Quaternion quaternion1,
float scaleFactor,
out Quaternion result
) {
result.X = quaternion1.X * scaleFactor;
result.Y = quaternion1.Y * scaleFactor;
result.Z = quaternion1.Z * scaleFactor;
result.W = quaternion1.W * scaleFactor;
}
/// <summary>
/// Flips the sign of the all the quaternion components.
/// </summary>
/// <param name="quaternion">Source <see cref="Quaternion"/>.</param>
/// <returns>The result of the quaternion negation.</returns>
public static Quaternion Negate(Quaternion quaternion)
{
return new Quaternion(
-quaternion.X,
-quaternion.Y,
-quaternion.Z,
-quaternion.W
);
}
/// <summary>
/// Flips the sign of the all the quaternion components.
/// </summary>
/// <param name="quaternion">Source <see cref="Quaternion"/>.</param>
/// <param name="result">The result of the quaternion negation as an output parameter.</param>
public static void Negate(ref Quaternion quaternion, out Quaternion result)
{
result.X = -quaternion.X;
result.Y = -quaternion.Y;
result.Z = -quaternion.Z;
result.W = -quaternion.W;
}
/// <summary>
/// Scales the quaternion magnitude to unit length.
/// </summary>
/// <param name="quaternion">Source <see cref="Quaternion"/>.</param>
/// <returns>The unit length quaternion.</returns>
public static Quaternion Normalize(Quaternion quaternion)
{
Quaternion quaternion2;
Normalize(ref quaternion, out quaternion2);
return quaternion2;
}
/// <summary>
/// Scales the quaternion magnitude to unit length.
/// </summary>
/// <param name="quaternion">Source <see cref="Quaternion"/>.</param>
/// <param name="result">The unit length quaternion an output parameter.</param>
public static void Normalize(ref Quaternion quaternion, out Quaternion result)
{
float num = 1.0f / ((float) System.Math.Sqrt(
(quaternion.X * quaternion.X) +
(quaternion.Y * quaternion.Y) +
(quaternion.Z * quaternion.Z) +
(quaternion.W * quaternion.W)
));
result.X = quaternion.X * num;
result.Y = quaternion.Y * num;
result.Z = quaternion.Z * num;
result.W = quaternion.W * num;
}
#endregion
#region Public Static Operator Overloads
/// <summary>
/// Adds two quaternions.
/// </summary>
/// <param name="quaternion1">Source <see cref="Quaternion"/> on the left of the add sign.</param>
/// <param name="quaternion2">Source <see cref="Quaternion"/> on the right of the add sign.</param>
/// <returns>Sum of the vectors.</returns>
public static Quaternion operator +(Quaternion quaternion1, Quaternion quaternion2)
{
Quaternion quaternion;
Add(ref quaternion1, ref quaternion2, out quaternion);
return quaternion;
}
/// <summary>
/// Divides a <see cref="Quaternion"/> by the other <see cref="Quaternion"/>.
/// </summary>
/// <param name="quaternion1">Source <see cref="Quaternion"/> on the left of the div sign.</param>
/// <param name="quaternion2">Divisor <see cref="Quaternion"/> on the right of the div sign.</param>
/// <returns>The result of dividing the quaternions.</returns>
public static Quaternion operator /(Quaternion quaternion1, Quaternion quaternion2)
{
Quaternion quaternion;
Divide(ref quaternion1, ref quaternion2, out quaternion);
return quaternion;
}
/// <summary>
/// Compares whether two <see cref="Quaternion"/> instances are equal.
/// </summary>
/// <param name="quaternion1"><see cref="Quaternion"/> instance on the left of the equal sign.</param>
/// <param name="quaternion2"><see cref="Quaternion"/> 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 ==(Quaternion quaternion1, Quaternion quaternion2)
{
return quaternion1.Equals(quaternion2);
}
/// <summary>
/// Compares whether two <see cref="Quaternion"/> instances are not equal.
/// </summary>
/// <param name="quaternion1"><see cref="Quaternion"/> instance on the left of the not equal sign.</param>
/// <param name="quaternion2"><see cref="Quaternion"/> 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 !=(Quaternion quaternion1, Quaternion quaternion2)
{
return !quaternion1.Equals(quaternion2);
}
/// <summary>
/// Multiplies two quaternions.
/// </summary>
/// <param name="quaternion1">Source <see cref="Quaternion"/> on the left of the mul sign.</param>
/// <param name="quaternion2">Source <see cref="Quaternion"/> on the right of the mul sign.</param>
/// <returns>Result of the quaternions multiplication.</returns>
public static Quaternion operator *(Quaternion quaternion1, Quaternion quaternion2)
{
Quaternion quaternion;
Multiply(ref quaternion1, ref quaternion2, out quaternion);
return quaternion;
}
/// <summary>
/// Multiplies the components of quaternion by a scalar.
/// </summary>
/// <param name="quaternion1">Source <see cref="Vector3"/> on the left of the mul sign.</param>
/// <param name="scaleFactor">Scalar value on the right of the mul sign.</param>
/// <returns>Result of the quaternion multiplication with a scalar.</returns>
public static Quaternion operator *(Quaternion quaternion1, float scaleFactor)
{
Quaternion quaternion;
Multiply(ref quaternion1, scaleFactor, out quaternion);
return quaternion;
}
/// <summary>
/// Subtracts a <see cref="Quaternion"/> from a <see cref="Quaternion"/>.
/// </summary>
/// <param name="quaternion1">Source <see cref="Vector3"/> on the left of the sub sign.</param>
/// <param name="quaternion2">Source <see cref="Vector3"/> on the right of the sub sign.</param>
/// <returns>Result of the quaternion subtraction.</returns>
public static Quaternion operator -(Quaternion quaternion1, Quaternion quaternion2)
{
Quaternion quaternion;
Subtract(ref quaternion1, ref quaternion2, out quaternion);
return quaternion;
}
/// <summary>
/// Flips the sign of the all the quaternion components.
/// </summary>
/// <param name="quaternion">Source <see cref="Quaternion"/> on the right of the sub sign.</param>
/// <returns>The result of the quaternion negation.</returns>
public static Quaternion operator -(Quaternion quaternion)
{
Quaternion quaternion2;
Negate(ref quaternion, out quaternion2);
return quaternion2;
}
#endregion
}
}

300
src/Math/Ray.cs Normal file
View File

@ -0,0 +1,300 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.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;
#endregion
namespace MoonWorks.Math
{
[Serializable]
[DebuggerDisplay("{DebugDisplayString,nq}")]
public struct Ray : IEquatable<Ray>
{
#region Internal Properties
internal string DebugDisplayString
{
get
{
return string.Concat(
"Pos( ", Position.DebugDisplayString, " ) \r\n",
"Dir( ", Direction.DebugDisplayString, " )"
);
}
}
#endregion
#region Public Fields
public Vector3 Position;
public Vector3 Direction;
#endregion
#region Public Constructors
public Ray(Vector3 position, Vector3 direction)
{
Position = position;
Direction = direction;
}
#endregion
#region Public Methods
public override bool Equals(object obj)
{
return (obj is Ray) && Equals((Ray) obj);
}
public bool Equals(Ray other)
{
return ( this.Position.Equals(other.Position) &&
this.Direction.Equals(other.Direction) );
}
public override int GetHashCode()
{
return Position.GetHashCode() ^ Direction.GetHashCode();
}
// Adapted from http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/
public float? Intersects(BoundingBox box)
{
float? tMin = null, tMax = null;
if (MathHelper.WithinEpsilon(Direction.X, 0.0f))
{
if (Position.X < box.Min.X || Position.X > box.Max.X)
{
return null;
}
}
else
{
tMin = (box.Min.X - Position.X) / Direction.X;
tMax = (box.Max.X - Position.X) / Direction.X;
if (tMin > tMax)
{
float? temp = tMin;
tMin = tMax;
tMax = temp;
}
}
if (MathHelper.WithinEpsilon(Direction.Y, 0.0f))
{
if (Position.Y < box.Min.Y || Position.Y > box.Max.Y)
{
return null;
}
}
else
{
float tMinY = (box.Min.Y - Position.Y) / Direction.Y;
float tMaxY = (box.Max.Y - Position.Y) / Direction.Y;
if (tMinY > tMaxY)
{
float temp = tMinY;
tMinY = tMaxY;
tMaxY = temp;
}
if ( (tMin.HasValue && tMin > tMaxY) ||
(tMax.HasValue && tMinY > tMax) )
{
return null;
}
if (!tMin.HasValue || tMinY > tMin) tMin = tMinY;
if (!tMax.HasValue || tMaxY < tMax) tMax = tMaxY;
}
if (MathHelper.WithinEpsilon(Direction.Z, 0.0f))
{
if (Position.Z < box.Min.Z || Position.Z > box.Max.Z)
{
return null;
}
}
else
{
float tMinZ = (box.Min.Z - Position.Z) / Direction.Z;
float tMaxZ = (box.Max.Z - Position.Z) / Direction.Z;
if (tMinZ > tMaxZ)
{
float temp = tMinZ;
tMinZ = tMaxZ;
tMaxZ = temp;
}
if ( (tMin.HasValue && tMin > tMaxZ) ||
(tMax.HasValue && tMinZ > tMax) )
{
return null;
}
if (!tMin.HasValue || tMinZ > tMin) tMin = tMinZ;
if (!tMax.HasValue || tMaxZ < tMax) tMax = tMaxZ;
}
/* Having a positive tMin and a negative tMax means the ray is inside the
* box we expect the intesection distance to be 0 in that case.
*/
if ((tMin.HasValue && tMin < 0) && tMax > 0) return 0;
/* A negative tMin means that the intersection point is behind the ray's
* origin. We discard these as not hitting the AABB.
*/
if (tMin < 0) return null;
return tMin;
}
public void Intersects(ref BoundingBox box, out float? result)
{
result = Intersects(box);
}
public float? Intersects(BoundingSphere sphere)
{
float? result;
Intersects(ref sphere, out result);
return result;
}
public float? Intersects(Plane plane)
{
float? result;
Intersects(ref plane, out result);
return result;
}
public float? Intersects(BoundingFrustum frustum)
{
float? result;
frustum.Intersects(ref this, out result);
return result;
}
public void Intersects(ref Plane plane, out float? result)
{
float den = Vector3.Dot(Direction, plane.Normal);
if (System.Math.Abs(den) < 0.00001f)
{
result = null;
return;
}
result = (-plane.D - Vector3.Dot(plane.Normal, Position)) / den;
if (result < 0.0f)
{
if (result < -0.00001f)
{
result = null;
return;
}
result = 0.0f;
}
}
public void Intersects(ref BoundingSphere sphere, out float? result)
{
// Find the vector between where the ray starts the the sphere's center.
Vector3 difference = sphere.Center - this.Position;
float differenceLengthSquared = difference.LengthSquared();
float sphereRadiusSquared = sphere.Radius * sphere.Radius;
float distanceAlongRay;
/* If the distance between the ray start and the sphere's center is less than
* the radius of the sphere, it means we've intersected. Checking the
* LengthSquared is faster.
*/
if (differenceLengthSquared < sphereRadiusSquared)
{
result = 0.0f;
return;
}
Vector3.Dot(ref this.Direction, ref difference, out distanceAlongRay);
// If the ray is pointing away from the sphere then we don't ever intersect.
if (distanceAlongRay < 0)
{
result = null;
return;
}
/* Next we kinda use Pythagoras to check if we are within the bounds of the
* sphere.
* if x = radius of sphere
* if y = distance between ray position and sphere centre
* if z = the distance we've travelled along the ray
* if x^2 + z^2 - y^2 < 0, we do not intersect
*/
float dist = (
sphereRadiusSquared +
(distanceAlongRay * distanceAlongRay) -
differenceLengthSquared
);
result = (dist < 0) ? null : distanceAlongRay - (float?) System.Math.Sqrt(dist);
}
#endregion
#region Public Static Methods
public static bool operator !=(Ray a, Ray b)
{
return !a.Equals(b);
}
public static bool operator ==(Ray a, Ray b)
{
return a.Equals(b);
}
public override string ToString()
{
return (
"{{Position:" + Position.ToString() +
" Direction:" + Direction.ToString() +
"}}"
);
}
#endregion
}
}

447
src/Math/Rectangle.cs Normal file
View File

@ -0,0 +1,447 @@
#region License
/* MoonWorks - Game Development Framework
* Copyright 2021 Evan Hemsley
*/
/* Derived from code by Ethan Lee (Copyright 2009-2021).
* Released under the Microsoft Public License.
* See fna.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;
#endregion
namespace MoonWorks.Math
{
/// <summary>
/// Describes a 2D-rectangle.
/// </summary>
[Serializable]
[DebuggerDisplay("{DebugDisplayString,nq}")]
public struct Rectangle : IEquatable<Rectangle>
{
#region Public Properties
/// <summary>
/// Returns the x coordinate of the left edge of this <see cref="Rectangle"/>.
/// </summary>
public int Left
{
get
{
return X;
}
}
/// <summary>
/// Returns the x coordinate of the right edge of this <see cref="Rectangle"/>.
/// </summary>
public int Right
{
get
{
return (X + Width);
}
}
/// <summary>
/// Returns the y coordinate of the top edge of this <see cref="Rectangle"/>.
/// </summary>
public int Top
{
get
{
return Y;
}
}
/// <summary>
/// Returns the y coordinate of the bottom edge of this <see cref="Rectangle"/>.
/// </summary>
public int Bottom
{
get
{
return (Y + Height);
}
}
/// <summary>
/// The top-left coordinates of this <see cref="Rectangle"/>.
/// </summary>
public Point Location
{
get
{
return new Point(X, Y);
}
set
{
X = value.X;
Y = value.Y;
}
}
/// <summary>
/// A <see cref="Point"/> located in the center of this <see cref="Rectangle"/>'s bounds.
/// </summary>
/// <remarks>
/// If <see cref="Width"/> or <see cref="Height"/> is an odd number,
/// the center point will be rounded down.
/// </remarks>
public Point Center
{
get
{
return new Point(
X + (Width / 2),
Y + (Height / 2)
);
}
}
/// <summary>
/// Whether or not this <see cref="Rectangle"/> has a width and
/// height of 0, and a position of (0, 0).
/// </summary>
public bool IsEmpty
{
get
{
return ( (Width == 0) &&
(Height == 0) &&
(X == 0) &&
(Y == 0) );
}
}
#endregion
#region Public Static Properties
/// <summary>
/// Returns a <see cref="Rectangle"/> with X=0, Y=0, Width=0, and Height=0.
/// </summary>
public static Rectangle Empty
{
get
{
return emptyRectangle;
}
}
#endregion
#region Internal Properties
internal string DebugDisplayString
{
get
{
return string.Concat(
X.ToString(), " ",
Y.ToString(), " ",
Width.ToString(), " ",
Height.ToString()
);
}
}
#endregion
#region Public Fields
/// <summary>
/// The x coordinate of the top-left corner of this <see cref="Rectangle"/>.
/// </summary>
public int X;
/// <summary>
/// The y coordinate of the top-left corner of this <see cref="Rectangle"/>.
/// </summary>
public int Y;
/// <summary>
/// The width of this <see cref="Rectangle"/>.
/// </summary>
public int Width;
/// <summary>
/// The height of this <see cref="Rectangle"/>.
/// </summary>
public int Height;
#endregion
#region Private Static Fields
private static Rectangle emptyRectangle = new Rectangle();
#endregion
#region Public Constructors
/// <summary>
/// Creates a <see cref="Rectangle"/> with the specified
/// position, width, and height.
/// </summary>
/// <param name="x">The x coordinate of the top-left corner of the created <see cref="Rectangle"/>.</param>
/// <param name="y">The y coordinate of the top-left corner of the created <see cref="Rectangle"/>.</param>
/// <param name="width">The width of the created <see cref="Rectangle"/>.</param>
/// <param name="height">The height of the created <see cref="Rectangle"/>.</param>
public Rectangle(int x, int y, int width, int height)
{
X = x;
Y = y;
Width = width;
Height = height;
}
#endregion
#region Public Methods
/// <summary>
/// Gets whether or not the provided coordinates lie within the bounds of this <see cref="Rectangle"/>.
/// </summary>
/// <param name="x">The x coordinate of the point to check for containment.</param>
/// <param name="y">The y coordinate of the point to check for containment.</param>
/// <returns><c>true</c> if the provided coordinates lie inside this <see cref="Rectangle"/>. <c>false</c> otherwise.</returns>
public bool Contains(int x, int y)
{
return ( (this.X <= x) &&
(x < (this.X + this.Width)) &&
(this.Y <= y) &&
(y < (this.Y + this.Height)) );
}
/// <summary>
/// Gets whether or not the provided <see cref="Point"/> lies within the bounds of this <see cref="Rectangle"/>.
/// </summary>
/// <param name="value">The coordinates to check for inclusion in this <see cref="Rectangle"/>.</param>
/// <returns><c>true</c> if the provided <see cref="Point"/> lies inside this <see cref="Rectangle"/>. <c>false</c> otherwise.</returns>
public bool Contains(Point value)
{
return ( (this.X <= value.X) &&
(value.X < (this.X + this.Width)) &&
(this.Y <= value.Y) &&
(value.Y < (this.Y + this.Height)) );
}
/// <summary>
/// Gets whether or not the provided <see cref="Rectangle"/> lies within the bounds of this <see cref="Rectangle"/>.
/// </summary>
/// <param name="value">The <see cref="Rectangle"/> to check for inclusion in this <see cref="Rectangle"/>.</param>
/// <returns><c>true</c> if the provided <see cref="Rectangle"/>'s bounds lie entirely inside this <see cref="Rectangle"/>. <c>false</c> otherwise.</returns>
public bool Contains(Rectangle value)
{
return ( (this.X <= value.X) &&
((value.X + value.Width) <= (this.X + this.Width)) &&
(this.Y <= value.Y) &&
((value.Y + value.Height) <= (this.Y + this.Height)) );
}
public void Contains(ref Point value, out bool result)
{
result = ( (this.X <= value.X) &&
(value.X < (this.X + this.Width)) &&
(this.Y <= value.Y) &&
(value.Y < (this.Y + this.Height)) );
}
public void Contains(ref Rectangle value, out bool result)
{
result = ( (this.X <= value.X) &&
((value.X + value.Width) <= (this.X + this.Width)) &&
(this.Y <= value.Y) &&
((value.Y + value.Height) <= (this.Y + this.Height)) );
}
/// <summary>
/// Increments this <see cref="Rectangle"/>'s <see cref="Location"/> by the
/// x and y components of the provided <see cref="Point"/>.
/// </summary>
/// <param name="offset">The x and y components to add to this <see cref="Rectangle"/>'s <see cref="Location"/>.</param>
public void Offset(Point offset)
{
X += offset.X;
Y += offset.Y;
}
/// <summary>
/// Increments this <see cref="Rectangle"/>'s <see cref="Location"/> by the
/// provided x and y coordinates.
/// </summary>
/// <param name="offsetX">The x coordinate to add to this <see cref="Rectangle"/>'s <see cref="Location"/>.</param>
/// <param name="offsetY">The y coordinate to add to this <see cref="Rectangle"/>'s <see cref="Location"/>.</param>
public void Offset(int offsetX, int offsetY)
{
X += offsetX;
Y += offsetY;
}
public void Inflate(int horizontalValue, int verticalValue)
{
X -= horizontalValue;
Y -= verticalValue;
Width += horizontalValue * 2;
Height += verticalValue * 2;
}
/// <summary>
/// Checks whether or not this <see cref="Rectangle"/> is equivalent
/// to a provided <see cref="Rectangle"/>.
/// </summary>
/// <param name="other">The <see cref="Rectangle"/> to test for equality.</param>
/// <returns>
/// <c>true</c> if this <see cref="Rectangle"/>'s x coordinate, y coordinate, width, and height
/// match the values for the provided <see cref="Rectangle"/>. <c>false</c> otherwise.
/// </returns>
public bool Equals(Rectangle other)
{
return this == other;
}
/// <summary>
/// Checks whether or not this <see cref="Rectangle"/> is equivalent
/// to a provided object.
/// </summary>
/// <param name="obj">The <see cref="object"/> to test for equality.</param>
/// <returns>
/// <c>true</c> if the provided object is a <see cref="Rectangle"/>, and this
/// <see cref="Rectangle"/>'s x coordinate, y coordinate, width, and height
/// match the values for the provided <see cref="Rectangle"/>. <c>false</c> otherwise.
/// </returns>
public override bool Equals(object obj)
{
return (obj is Rectangle) && this == ((Rectangle) obj);
}
public override string ToString()
{
return (
"{X:" + X.ToString() +
" Y:" + Y.ToString() +
" Width:" + Width.ToString() +
" Height:" + Height.ToString() +
"}"
);
}
public override int GetHashCode()
{
return (this.X ^ this.Y ^ this.Width ^ this.Height);
}
/// <summary>
/// Gets whether or not the other <see cref="Rectangle"/> intersects with this rectangle.
/// </summary>
/// <param name="value">The other rectangle for testing.</param>
/// <returns><c>true</c> if other <see cref="Rectangle"/> intersects with this rectangle; <c>false</c> otherwise.</returns>
public bool Intersects(Rectangle value)
{
return ( value.Left < Right &&
Left < value.Right &&
value.Top < Bottom &&
Top < value.Bottom );
}
/// <summary>
/// Gets whether or not the other <see cref="Rectangle"/> intersects with this rectangle.
/// </summary>
/// <param name="value">The other rectangle for testing.</param>
/// <param name="result"><c>true</c> if other <see cref="Rectangle"/> intersects with this rectangle; <c>false</c> otherwise. As an output parameter.</param>
public void Intersects(ref Rectangle value, out bool result)
{
result = ( value.Left < Right &&
Left < value.Right &&
value.Top < Bottom &&
Top < value.Bottom );
}
#endregion
#region Public Static Methods
public static bool operator ==(Rectangle a, Rectangle b)
{
return ( (a.X == b.X) &&
(a.Y == b.Y) &&
(a.Width == b.Width) &&
(a.Height == b.Height) );
}
public static bool operator !=(Rectangle a, Rectangle b)
{
return !(a == b);
}
public static Rectangle Intersect(Rectangle value1, Rectangle value2)
{
Rectangle rectangle;
Intersect(ref value1, ref value2, out rectangle);
return rectangle;
}
public static void Intersect(
ref Rectangle value1,
ref Rectangle value2,
out Rectangle result
) {
if (value1.Intersects(value2))
{
int right_side = System.Math.Min(
value1.X + value1.Width,
value2.X + value2.Width
);
int left_side = System.Math.Max(value1.X, value2.X);
int top_side = System.Math.Max(value1.Y, value2.Y);
int bottom_side = System.Math.Min(
value1.Y + value1.Height,
value2.Y + value2.Height
);
result = new Rectangle(
left_side,
top_side,
right_side - left_side,
bottom_side - top_side
);
}
else
{
result = new Rectangle(0, 0, 0, 0);
}
}
public static Rectangle Union(Rectangle value1, Rectangle value2)
{
int x = System.Math.Min(value1.X, value2.X);
int y = System.Math.Min(value1.Y, value2.Y);
return new Rectangle(
x,
y,
System.Math.Max(value1.Right, value2.Right) - x,
System.Math.Max(value1.Bottom, value2.Bottom) - y
);
}
public static void Union(ref Rectangle value1, ref Rectangle value2, out Rectangle result)
{
result.X = System.Math.Min(value1.X, value2.X);
result.Y = System.Math.Min(value1.Y, value2.Y);
result.Width = System.Math.Max(value1.Right, value2.Right) - result.X;
result.Height = System.Math.Max(value1.Bottom, value2.Bottom) - result.Y;
}
#endregion
}
}

1166
src/Math/Vector2.cs Normal file

File diff suppressed because it is too large Load Diff

1509
src/Math/Vector3.cs Normal file

File diff suppressed because it is too large Load Diff

1479
src/Math/Vector4.cs Normal file

File diff suppressed because it is too large Load Diff