Refactor engine to support double-precision vectors

This commit is contained in:
Wojtek Figat
2022-06-13 00:40:32 +02:00
parent f82e370392
commit a881c90b2e
744 changed files with 19062 additions and 12467 deletions

View File

@@ -1,3 +1,9 @@
#if USE_LARGE_WORLDS
using Real = System.Double;
#else
using Real = System.Single;
#endif
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
// -----------------------------------------------------------------------------
@@ -185,6 +191,17 @@ namespace FlaxEngine
Z = value.Z;
}
/// <summary>
/// Initializes a new instance of the <see cref="Double3" /> struct.
/// </summary>
/// <param name="value">A vector containing the values with which to initialize the X, Y and Z components.</param>
public Double3(Float3 value)
{
X = value.X;
Y = value.Y;
Z = value.Z;
}
/// <summary>
/// Initializes a new instance of the <see cref="Double3" /> struct.
/// </summary>
@@ -1208,7 +1225,7 @@ namespace FlaxEngine
double dot = Mathd.Clamp(Dot(from.Normalized, to.Normalized), -1.0, 1.0);
if (Math.Abs(dot) > (1 - Mathd.Epsilon))
return dot > 0.0 ? 0.0 : 180.0;
return Mathd.Acos(dot) * Mathd.RadiansToDegrees;
return Math.Acos(dot) * Mathd.RadiansToDegrees;
}
/// <summary>
@@ -1295,10 +1312,7 @@ namespace FlaxEngine
/// <param name="vector">The source vector.</param>
/// <param name="normal">Normal of the surface.</param>
/// <param name="result">When the method completes, contains the reflected vector.</param>
/// <remarks>
/// Reflect only gives the direction of a reflection off a surface, it does not determine
/// whether the original vector was close enough to the surface to hit it.
/// </remarks>
/// <remarks>Reflect only gives the direction of a reflection off a surface, it does not determine whether the original vector was close enough to the surface to hit it.</remarks>
public static void Reflect(ref Double3 vector, ref Double3 normal, out Double3 result)
{
double dot = vector.X * normal.X + vector.Y * normal.Y + vector.Z * normal.Z;
@@ -1320,95 +1334,6 @@ namespace FlaxEngine
return result;
}
/// <summary>
/// Orthogonalizes a list of vectors.
/// </summary>
/// <param name="destination">The list of orthogonalized vectors.</param>
/// <param name="source">The list of vectors to orthogonalize.</param>
/// <remarks>
/// <para>
/// Orthogonalization is the process of making all vectors orthogonal to each other. This
/// means that any given vector in the list will be orthogonal to any other given vector in the
/// list.
/// </para>
/// <para>
/// Because this method uses the modified Gram-Schmidt process, the resulting vectors
/// tend to be numerically unstable. The numeric stability decreases according to the vectors
/// position in the list so that the first vector is the most stable and the last vector is the
/// least stable.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="source" /> or <paramref name="destination" /> is <c>null</c>.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="destination" /> is shorter in length than <paramref name="source" />.</exception>
public static void Orthogonalize(Double3[] destination, params Double3[] source)
{
//Uses the modified Gram-Schmidt process.
//q1 = m1
//q2 = m2 - ((q1 ⋅ m2) / (q1 ⋅ q1)) * q1
//q3 = m3 - ((q1 ⋅ m3) / (q1 ⋅ q1)) * q1 - ((q2 ⋅ m3) / (q2 ⋅ q2)) * q2
//q4 = m4 - ((q1 ⋅ m4) / (q1 ⋅ q1)) * q1 - ((q2 ⋅ m4) / (q2 ⋅ q2)) * q2 - ((q3 ⋅ m4) / (q3 ⋅ q3)) * q3
//q5 = ...
if (source == null)
throw new ArgumentNullException(nameof(source));
if (destination == null)
throw new ArgumentNullException(nameof(destination));
if (destination.Length < source.Length)
throw new ArgumentOutOfRangeException(nameof(destination), "The destination array must be of same length or larger length than the source array.");
for (var i = 0; i < source.Length; ++i)
{
Double3 v = source[i];
for (var r = 0; r < i; ++r)
v -= Dot(destination[r], v) / Dot(destination[r], destination[r]) * destination[r];
destination[i] = v;
}
}
/// <summary>
/// Orthonormalizes a list of vectors.
/// </summary>
/// <param name="destination">The list of orthonormalized vectors.</param>
/// <param name="source">The list of vectors to orthonormalize.</param>
/// <remarks>
/// <para>
/// Orthonormalization is the process of making all vectors orthogonal to each
/// other and making all vectors of unit length. This means that any given vector will
/// be orthogonal to any other given vector in the list.
/// </para>
/// <para>
/// Because this method uses the modified Gram-Schmidt process, the resulting vectors
/// tend to be numerically unstable. The numeric stability decreases according to the vectors
/// position in the list so that the first vector is the most stable and the last vector is the
/// least stable.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="source" /> or <paramref name="destination" /> is <c>null</c>.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="destination" /> is shorter in length than <paramref name="source" />.</exception>
public static void Orthonormalize(Double3[] destination, params Double3[] source)
{
//Uses the modified Gram-Schmidt process.
//Because we are making unit vectors, we can optimize the math for orthogonalization
//and simplify the projection operation to remove the division.
//q1 = m1 / |m1|
//q2 = (m2 - (q1 ⋅ m2) * q1) / |m2 - (q1 ⋅ m2) * q1|
//q3 = (m3 - (q1 ⋅ m3) * q1 - (q2 ⋅ m3) * q2) / |m3 - (q1 ⋅ m3) * q1 - (q2 ⋅ m3) * q2|
//q4 = (m4 - (q1 ⋅ m4) * q1 - (q2 ⋅ m4) * q2 - (q3 ⋅ m4) * q3) / |m4 - (q1 ⋅ m4) * q1 - (q2 ⋅ m4) * q2 - (q3 ⋅ m4) * q3|
//q5 = ...
if (source == null)
throw new ArgumentNullException(nameof(source));
if (destination == null)
throw new ArgumentNullException(nameof(destination));
if (destination.Length < source.Length)
throw new ArgumentOutOfRangeException(nameof(destination), "The destination array must be of same length or larger length than the source array.");
for (var i = 0; i < source.Length; ++i)
{
Double3 v = source[i];
for (var r = 0; r < i; ++r)
v -= Dot(destination[r], v) * destination[r];
v.Normalize();
destination[i] = v;
}
}
/// <summary>
/// Transforms a 3D vector by the given <see cref="Quaternion" /> rotation.
/// </summary>
@@ -1446,51 +1371,6 @@ namespace FlaxEngine
return result;
}
/// <summary>
/// Transforms an array of vectors by the given <see cref="Quaternion" /> rotation.
/// </summary>
/// <param name="source">The array of vectors to transform.</param>
/// <param name="rotation">The <see cref="Quaternion" /> rotation to apply.</param>
/// <param name="destination">The array for which the transformed vectors are stored. This array may be the same array as <paramref name="source" />.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="source" /> or <paramref name="destination" /> is <c>null</c>.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="destination" /> is shorter in length than <paramref name="source" />.</exception>
public static void Transform(Double3[] source, ref Quaternion rotation, Double3[] destination)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (destination == null)
throw new ArgumentNullException(nameof(destination));
if (destination.Length < source.Length)
throw new ArgumentOutOfRangeException(nameof(destination), "The destination array must be of same length or larger length than the source array.");
double x = rotation.X + rotation.X;
double y = rotation.Y + rotation.Y;
double z = rotation.Z + rotation.Z;
double wx = rotation.W * x;
double wy = rotation.W * y;
double wz = rotation.W * z;
double xx = rotation.X * x;
double xy = rotation.X * y;
double xz = rotation.X * z;
double yy = rotation.Y * y;
double yz = rotation.Y * z;
double zz = rotation.Z * z;
double num1 = 1.0 - yy - zz;
double num2 = xy - wz;
double num3 = xz + wy;
double num4 = xy + wz;
double num5 = 1.0 - xx - zz;
double num6 = yz - wx;
double num7 = xz - wy;
double num8 = yz + wx;
double num9 = 1.0 - xx - yy;
for (var i = 0; i < source.Length; i++)
{
destination[i] = new Double3(source[i].X * num1 + source[i].Y * num2 + source[i].Z * num3,
source[i].X * num4 + source[i].Y * num5 + source[i].Z * num6,
source[i].X * num7 + source[i].Y * num8 + source[i].Z * num9);
}
}
/// <summary>
/// Transforms a 3D vector by the given <see cref="Matrix3x3"/>.
/// </summary>
@@ -1555,26 +1435,6 @@ namespace FlaxEngine
return result;
}
/// <summary>
/// Transforms an array of 3D vectors by the given <see cref="Matrix" />.
/// </summary>
/// <param name="source">The array of vectors to transform.</param>
/// <param name="transform">The transformation <see cref="Matrix" />.</param>
/// <param name="destination">The array for which the transformed vectors are stored.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="source" /> or <paramref name="destination" /> is <c>null</c>.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="destination" /> is shorter in length than <paramref name="source" />.</exception>
public static void Transform(Double3[] source, ref Matrix transform, Double4[] destination)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (destination == null)
throw new ArgumentNullException(nameof(destination));
if (destination.Length < source.Length)
throw new ArgumentOutOfRangeException(nameof(destination), "The destination array must be of same length or larger length than the source array.");
for (var i = 0; i < source.Length; ++i)
Transform(ref source[i], ref transform, out destination[i]);
}
/// <summary>
/// Performs a coordinate transformation using the given <see cref="Matrix" />.
/// </summary>
@@ -1619,35 +1479,6 @@ namespace FlaxEngine
return result;
}
/// <summary>
/// Performs a coordinate transformation on an array of vectors using the given <see cref="Matrix" />.
/// </summary>
/// <param name="source">The array of coordinate vectors to transform.</param>
/// <param name="transform">The transformation <see cref="Matrix" />.</param>
/// <param name="destination">The array for which the transformed vectors are stored. This array may be the same array as <paramref name="source" />.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="source" /> or <paramref name="destination" /> is <c>null</c>.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="destination" /> is shorter in length than
/// <paramref name="source" />.
/// </exception>
/// <remarks>
/// A coordinate transform performs the transformation with the assumption that the w component
/// is one. The four dimensional vector obtained from the transformation operation has each
/// component in the vector divided by the w component. This forces the w component to be one and
/// therefore makes the vector homogeneous. The homogeneous vector is often preferred when working
/// with coordinates as the w component can safely be ignored.
/// </remarks>
public static void TransformCoordinate(Double3[] source, ref Matrix transform, Double3[] destination)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (destination == null)
throw new ArgumentNullException(nameof(destination));
if (destination.Length < source.Length)
throw new ArgumentOutOfRangeException(nameof(destination), "The destination array must be of same length or larger length than the source array.");
for (var i = 0; i < source.Length; i++)
TransformCoordinate(ref source[i], ref transform, out destination[i]);
}
/// <summary>
/// Performs a normal transformation using the given <see cref="Matrix" />.
/// </summary>
@@ -1687,33 +1518,6 @@ namespace FlaxEngine
return result;
}
/// <summary>
/// Performs a normal transformation on an array of vectors using the given <see cref="Matrix" />.
/// </summary>
/// <param name="source">The array of normal vectors to transform.</param>
/// <param name="transform">The transformation <see cref="Matrix" />.</param>
/// <param name="destination">The array for which the transformed vectors are stored. This array may be the same array as <paramref name="source" />.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="source" /> or <paramref name="destination" /> is <c>null</c>.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="destination" /> is shorter in length than <paramref name="source" />.</exception>
/// <remarks>
/// A normal transform performs the transformation with the assumption that the w component
/// is zero. This causes the fourth row and fourth column of the matrix to be unused. The
/// end result is a vector that is not translated, but all other transformation properties
/// apply. This is often preferred for normal vectors as normals purely represent direction
/// rather than location because normal vectors should not be translated.
/// </remarks>
public static void TransformNormal(Double3[] source, ref Matrix transform, Double3[] destination)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (destination == null)
throw new ArgumentNullException(nameof(destination));
if (destination.Length < source.Length)
throw new ArgumentOutOfRangeException(nameof(destination), "The destination array must be of same length or larger length than the source array.");
for (var i = 0; i < source.Length; ++i)
TransformNormal(ref source[i], ref transform, out destination[i]);
}
/// <summary>
/// Adds two vectors.
/// </summary>
@@ -1935,6 +1739,26 @@ namespace FlaxEngine
return !Mathd.NearEqual(left.X, right.X) || !Mathd.NearEqual(left.Y, right.Y) || !Mathd.NearEqual(left.Z, right.Z);
}
/// <summary>
/// Performs an implicit conversion from <see cref="Double3" /> to <see cref="Float3" />.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator Float3(Double3 value)
{
return new Float3((float)value.X, (float)value.Y, (float)value.Z);
}
/// <summary>
/// Performs an implicit conversion from <see cref="Double3" /> to <see cref="Vector3" />.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator Vector3(Double3 value)
{
return new Vector3((Real)value.X, (Real)value.Y, (Real)value.Z);
}
/// <summary>
/// Performs an explicit conversion from <see cref="Double3" /> to <see cref="Double2" />.
/// </summary>
@@ -2042,9 +1866,7 @@ namespace FlaxEngine
/// <returns><c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.</returns>
public override bool Equals(object value)
{
if (!(value is Double3 other))
return false;
return Mathd.NearEqual(other.X, X) && Mathd.NearEqual(other.Y, Y) && Mathd.NearEqual(other.Z, Z);
return value is Double3 other && Mathd.NearEqual(other.X, X) && Mathd.NearEqual(other.Y, Y) && Mathd.NearEqual(other.Z, Z);
}
}
}