diff --git a/Source/Engine/Core/Math/Math.h b/Source/Engine/Core/Math/Math.h
index e9bd6e58f..f138fadb3 100644
--- a/Source/Engine/Core/Math/Math.h
+++ b/Source/Engine/Core/Math/Math.h
@@ -141,11 +141,6 @@ namespace Math
return exp2f(value);
}
- static FORCE_INLINE double Abs(const double value)
- {
- return fabs(value);
- }
-
static FORCE_INLINE float Abs(const float value)
{
return fabsf(value);
@@ -161,11 +156,6 @@ namespace Math
return value < 0 ? -value : value;
}
- static FORCE_INLINE double Mod(const double a, const double b)
- {
- return fmod(a, b);
- }
-
static FORCE_INLINE float Mod(const float a, const float b)
{
return fmodf(a, b);
@@ -435,14 +425,6 @@ namespace Math
return amount <= 0 ? 0 : amount >= 1 ? 1 : amount * amount * amount * (amount * (amount * 6 - 15) + 10);
}
- // Determines whether the specified value is close to zero (0.0)
- // @param a The floating value
- // @returns True if the specified value is close to zero (0.0). otherwise false
- inline bool IsZero(double a)
- {
- return Abs(a) < 1e-7;
- }
-
// Determines whether the specified value is close to zero (0.0f)
// @param a The floating value
// @returns True if the specified value is close to zero (0.0f). otherwise false
diff --git a/Source/Engine/Core/Math/Mathd.cs b/Source/Engine/Core/Math/Mathd.cs
new file mode 100644
index 000000000..42a65e023
--- /dev/null
+++ b/Source/Engine/Core/Math/Mathd.cs
@@ -0,0 +1,1111 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+using System;
+using System.ComponentModel;
+
+namespace FlaxEngine
+{
+ ///
+ /// A collection of common math functions on double floating-points.
+ ///
+ [HideInEditor]
+ public static class Mathd
+ {
+ ///
+ /// The value for which all absolute numbers smaller than are considered equal to zero.
+ ///
+ public const double Epsilon = 1e-7f;
+
+ ///
+ /// A value specifying the approximation of π which is 180 degrees.
+ ///
+ public const double Pi = Math.PI;
+
+ ///
+ /// A value specifying the approximation of 2π which is 360 degrees.
+ ///
+ public const double TwoPi = 2.0 * Math.PI;
+
+ ///
+ /// A value specifying the approximation of π/2 which is 90 degrees.
+ ///
+ public const double PiOverTwo = Math.PI / 2.0;
+
+ ///
+ /// A value specifying the approximation of π/4 which is 45 degrees.
+ ///
+ public const double PiOverFour = Math.PI / 4.0;
+
+ ///
+ /// A value specifying the golden mean
+ ///
+ public const double GoldenRatio = 1.6180339887;
+
+ ///
+ /// Returns the absolute value of f.
+ ///
+ ///
+ public static double Abs(double f)
+ {
+ return Math.Abs(f);
+ }
+
+ ///
+ /// Returns the arc-cosine of f - the angle in radians whose cosine is f.
+ ///
+ ///
+ public static double Acos(double f)
+ {
+ return Math.Acos(f);
+ }
+
+ ///
+ /// Compares two floating point values if they are similar.
+ ///
+ ///
+ ///
+ public static bool Approximately(double a, double b)
+ {
+ return Abs(b - a) < Max(Epsilon * Max(Abs(a), Abs(b)), Epsilon * 8f);
+ }
+
+ ///
+ /// Returns the arc-sine of f - the angle in radians whose sine is f.
+ ///
+ ///
+ public static double Asin(double f)
+ {
+ return Math.Asin(f);
+ }
+
+ ///
+ /// Returns the arc-tangent of f - the angle in radians whose tangent is f.
+ ///
+ ///
+ public static double Atan(double f)
+ {
+ return Math.Atan(f);
+ }
+
+ ///
+ /// Returns the angle in radians whose Tan is y/x.
+ ///
+ ///
+ ///
+ public static double Atan2(double y, double x)
+ {
+ return Math.Atan2(y, x);
+ }
+
+ ///
+ /// Returns the smallest integer greater to or equal to f.
+ ///
+ ///
+ public static double Ceil(double f)
+ {
+ return Math.Ceiling(f);
+ }
+
+ ///
+ /// Returns the smallest integer greater to or equal to f.
+ ///
+ ///
+ public static long CeilToInt(double f)
+ {
+ return (long)Math.Ceiling(f);
+ }
+
+ ///
+ /// Clamps value between 0 and 1 and returns value.
+ ///
+ /// Value to clamp
+ /// Result value
+ public static double Saturate(double value)
+ {
+ if (value < 0d)
+ return 0d;
+ return value > 1d ? 1d : value;
+ }
+
+ ///
+ /// Returns the cosine of angle f in radians.
+ ///
+ ///
+ public static double Cos(double f)
+ {
+ return Math.Cos(f);
+ }
+
+ ///
+ /// Calculates the shortest difference between two given angles given in degrees.
+ ///
+ ///
+ ///
+ public static double DeltaAngle(double current, double target)
+ {
+ double t = Repeat(target - current, 360f);
+ if (t > 180d)
+ t -= 360d;
+ return t;
+ }
+
+ ///
+ /// Returns e raised to the specified power.
+ ///
+ ///
+ public static double Exp(double power)
+ {
+ return Math.Exp(power);
+ }
+
+ ///
+ /// Returns the largest integer smaller to or equal to f.
+ ///
+ ///
+ public static double Floor(double f)
+ {
+ return Math.Floor(f);
+ }
+
+ ///
+ /// Returns the largest integer smaller to or equal to f.
+ ///
+ ///
+ public static long FloorToInt(double f)
+ {
+ return (long)Math.Floor(f);
+ }
+
+ ///
+ /// Remaps the specified value from the specified range to another.
+ ///
+ /// The value to remap.
+ /// The source range minimum.
+ /// The source range maximum.
+ /// The destination range minimum.
+ /// The destination range maximum.
+ /// The remapped value.
+ public static double Remap(double value, double fromMin, double fromMax, double toMin, double toMax)
+ {
+ return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin;
+ }
+
+ ///
+ /// Calculates the linear parameter t that produces the interpolation value within the range [a, b].
+ ///
+ ///
+ ///
+ ///
+ public static double InverseLerp(double a, double b, double value)
+ {
+ if (a == b)
+ return 0d;
+ return Saturate((value - a) / (b - a));
+ }
+
+ ///
+ /// Same as Lerp but makes sure the values interpolate correctly when they wrap around 360 degrees.
+ ///
+ ///
+ ///
+ ///
+ public static double LerpAngle(double a, double b, double t)
+ {
+ double c = Repeat(b - a, 360d);
+ if (c > 180d)
+ c -= 360d;
+ return a + c * Saturate(t);
+ }
+
+ ///
+ /// Returns the logarithm of a specified number in a specified base.
+ ///
+ ///
+ ///
+ public static double Log(double f, double p)
+ {
+ return Math.Log(f, p);
+ }
+
+ ///
+ /// Returns the natural (base e) logarithm of a specified number.
+ ///
+ ///
+ public static double Log(double f)
+ {
+ return Math.Log(f);
+ }
+
+ ///
+ /// Returns the base 10 logarithm of a specified number.
+ ///
+ ///
+ public static double Log10(double f)
+ {
+ return Math.Log10(f);
+ }
+
+ ///
+ /// Returns largest of two or more values.
+ ///
+ ///
+ ///
+ public static double Max(double a, double b)
+ {
+ return a <= b ? b : a;
+ }
+
+
+ ///
+ /// Returns largest of two or more values.
+ ///
+ ///
+ public static double Max(params double[] values)
+ {
+ int length = values.Length;
+ if (length == 0)
+ return 0d;
+
+ double t = values[0];
+ for (var i = 1; i < length; i++)
+ if (values[i] > t)
+ t = values[i];
+ return t;
+ }
+
+
+ ///
+ /// Returns the smallest of two or more values.
+ ///
+ ///
+ ///
+ public static double Min(double a, double b)
+ {
+ return a >= b ? b : a;
+ }
+
+
+ ///
+ /// Returns the smallest of two or more values.
+ ///
+ ///
+ public static double Min(params double[] values)
+ {
+ int length = values.Length;
+ if (length == 0)
+ return 0d;
+
+ double t = values[0];
+ for (var i = 1; i < length; i++)
+ if (values[i] < t)
+ t = values[i];
+
+ return t;
+ }
+
+ ///
+ /// Moves a value current towards target.
+ ///
+ /// The current value.
+ /// The value to move towards.
+ /// The maximum change that should be applied to the value.
+ public static double MoveTowards(double current, double target, double maxDelta)
+ {
+ if (Abs(target - current) <= maxDelta)
+ return target;
+ return current + Sign(target - current) * maxDelta;
+ }
+
+ ///
+ /// Same as MoveTowards but makes sure the values interpolate correctly when they wrap around 360 degrees.
+ ///
+ ///
+ ///
+ ///
+ public static double MoveTowardsAngle(double current, double target, double maxDelta)
+ {
+ double delta = DeltaAngle(current, target);
+ if ((-maxDelta < delta) && (delta < maxDelta))
+ return target;
+ target = current + delta;
+ return MoveTowards(current, target, maxDelta);
+ }
+
+ ///
+ /// PingPongs the value t, so that it is never larger than length and never smaller than 0.
+ ///
+ ///
+ ///
+ public static double PingPong(double t, double length)
+ {
+ t = Repeat(t, length * 2f);
+ return length - Abs(t - length);
+ }
+
+ ///
+ /// Returns f raised to power p.
+ ///
+ ///
+ ///
+ public static double Pow(double f, double p)
+ {
+ return Math.Pow(f, p);
+ }
+
+ ///
+ /// Loops the value t, so that it is never larger than length and never smaller than 0.
+ ///
+ ///
+ ///
+ public static double Repeat(double t, double length)
+ {
+ return t - Floor(t / length) * length;
+ }
+
+ ///
+ /// Returns f rounded to the nearest integer.
+ ///
+ ///
+ public static double Round(double f)
+ {
+ return Math.Round(f);
+ }
+
+ ///
+ /// Returns f rounded to the nearest integer.
+ ///
+ ///
+ public static int RoundToInt(double f)
+ {
+ return (int)Math.Round(f);
+ }
+
+ ///
+ /// Returns f rounded to the nearest integer.
+ ///
+ ///
+ public static long RoundToLong(double f)
+ {
+ return (long)Math.Round(f);
+ }
+
+ ///
+ /// Returns the sign of f.
+ ///
+ ///
+ public static double Sign(double f)
+ {
+ return f < 0d ? -1d : 1d;
+ }
+
+ ///
+ /// Returns the sine of angle f in radians.
+ ///
+ ///
+ public static double Sin(double f)
+ {
+ return Math.Sin(f);
+ }
+
+ ///
+ /// Gradually changes a value towards a desired goal over time with smoothing.
+ ///
+ /// The current value.
+ /// The target value.
+ /// The current velocity.
+ /// The smoothing time. Smaller values increase blending time.
+ /// The maximum speed.
+ /// The smoothed value.
+ public static double SmoothDamp(double current, double target, ref double currentVelocity, double smoothTime, double maxSpeed)
+ {
+ return SmoothDamp(current, target, ref currentVelocity, smoothTime, maxSpeed, Time.DeltaTime);
+ }
+
+ ///
+ /// Gradually changes a value towards a desired goal over time with smoothing.
+ ///
+ /// The current value.
+ /// The target value.
+ /// The current velocity.
+ /// The smoothing time. Smaller values increase blending time.
+ /// The smoothed value.
+ public static double SmoothDamp(double current, double target, ref double currentVelocity, double smoothTime)
+ {
+ return SmoothDamp(current, target, ref currentVelocity, smoothTime, double.PositiveInfinity, Time.DeltaTime);
+ }
+
+ ///
+ /// Gradually changes a value towards a desired goal over time with smoothing.
+ ///
+ /// The current value.
+ /// The target value.
+ /// The current velocity.
+ /// The smoothing time. Smaller values increase blending time.
+ /// The maximum speed.
+ /// The delta time (in seconds) since last update.
+ /// The smoothed value.
+ public static double SmoothDamp(double current, double target, ref double currentVelocity, double smoothTime, [DefaultValue("double.PositiveInfinity")]
+ double maxSpeed, [DefaultValue("Time.DeltaTime")] double deltaTime)
+ {
+ smoothTime = Max(0.0001d, smoothTime);
+ double a = 2d / smoothTime;
+ double b = a * deltaTime;
+ double c = 1d / (1d + b + 0.48d * b * b + 0.235d * b * b * b);
+ double d = current - target;
+ double e = target;
+ double f = maxSpeed * smoothTime;
+ d = Clamp(d, -f, f);
+ target = current - d;
+ double g = (currentVelocity + a * d) * deltaTime;
+ currentVelocity = (currentVelocity - a * g) * c;
+ double h = target + (d + g) * c;
+ if (e - current > 0d == h > e)
+ {
+ h = e;
+ currentVelocity = (h - e) / deltaTime;
+ }
+ return h;
+ }
+
+ ///
+ /// Gradually changes an angle towards a desired goal over time with smoothing.
+ ///
+ /// The current angle.
+ /// The target angle.
+ /// The current velocity.
+ /// The smoothing time. Smaller values increase blending time.
+ /// The maximum speed.
+ /// The smoothed value.
+ public static double SmoothDampAngle(double current, double target, ref double currentVelocity, double smoothTime, double maxSpeed)
+ {
+ return SmoothDampAngle(current, target, ref currentVelocity, smoothTime, maxSpeed, Time.DeltaTime);
+ }
+
+ ///
+ /// Gradually changes an angle towards a desired goal over time with smoothing.
+ ///
+ /// The current angle.
+ /// The target angle.
+ /// The current velocity.
+ /// The smoothing time. Smaller values increase blending time.
+ /// The smoothed value.
+ public static double SmoothDampAngle(double current, double target, ref double currentVelocity, double smoothTime)
+ {
+ return SmoothDampAngle(current, target, ref currentVelocity, smoothTime, double.PositiveInfinity, Time.DeltaTime);
+ }
+
+ ///
+ /// Gradually changes an angle towards a desired goal over time with smoothing.
+ ///
+ /// The current angle.
+ /// The target angle.
+ /// The current velocity.
+ /// The smoothing time. Smaller values increase blending time.
+ /// The maximum speed.
+ /// The delta time (in seconds) since last update.
+ /// The smoothed value.
+ public static double SmoothDampAngle(double current, double target, ref double currentVelocity, double smoothTime, [DefaultValue("double.PositiveInfinity")]
+ double maxSpeed, [DefaultValue("Time.DeltaTime")] double deltaTime)
+ {
+ target = current + DeltaAngle(current, target);
+ return SmoothDamp(current, target, ref currentVelocity, smoothTime, maxSpeed, deltaTime);
+ }
+
+ ///
+ /// Interpolates between min and max with smoothing at the limits.
+ ///
+ ///
+ ///
+ ///
+ public static double SmoothStep(double from, double to, double t)
+ {
+ t = Saturate(t);
+ t = -2d * t * t * t + 3d * t * t;
+ return to * t + from * (1d - t);
+ }
+
+ ///
+ /// Performs a cubic interpolation.
+ ///
+ /// The first point.
+ /// The tangent direction at first point.
+ /// The second point.
+ /// The tangent direction at second point.
+ /// The distance along the spline.
+ /// The interpolated value.
+ public static double CubicInterp(double p0, double t0, double p1, double t1, double alpha)
+ {
+ double alpha2 = alpha * alpha;
+ double alpha3 = alpha2 * alpha;
+ return (((2d * alpha3) - (3d * alpha2) + 1d) * p0) + ((alpha3 - (2d * alpha2) + alpha) * t0) + ((alpha3 - alpha2) * t1) + (((-2d * alpha3) + (3d * alpha2)) * p1);
+ }
+
+ ///
+ /// Interpolate between A and B, applying an ease in function. Exponent controls the degree of the curve.
+ ///
+ public static double InterpEaseIn(double a, double b, double alpha, double exponent)
+ {
+ double modifiedAlpha = Pow(alpha, exponent);
+ return Lerp(a, b, modifiedAlpha);
+ }
+
+ ///
+ /// Interpolate between A and B, applying an ease out function. Exponent controls the degree of the curve.
+ ///
+ public static double InterpEaseOut(double a, double b, double alpha, double exponent)
+ {
+ double modifiedAlpha = 1d - Pow(1d - alpha, exponent);
+ return Lerp(a, b, modifiedAlpha);
+ }
+
+ ///
+ /// Interpolate between A and B, applying an ease in/out function. Exponent controls the degree of the curve.
+ ///
+ public static double InterpEaseInOut(double a, double b, double alpha, double exponent)
+ {
+ return Lerp(a, b, (alpha < 0.5d) ? InterpEaseIn(0d, 1d, alpha * 2d, exponent) * 0.5d : InterpEaseOut(0d, 1d, alpha * 2d - 1d, exponent) * 0.5d + 0.5d);
+ }
+
+ ///
+ /// Interpolation between A and B, applying a sinusoidal in function.
+ ///
+ public static double InterpSinIn(double a, double b, double alpha)
+ {
+ double modifiedAlpha = -1d * Cos(alpha * PiOverTwo) + 1d;
+ return Lerp(a, b, modifiedAlpha);
+ }
+
+ ///
+ /// Interpolation between A and B, applying a sinusoidal out function.
+ ///
+ public static double InterpSinOut(double a, double b, double alpha)
+ {
+ double modifiedAlpha = Sin(alpha * PiOverTwo);
+ return Lerp(a, b, modifiedAlpha);
+ }
+
+ ///
+ /// Interpolation between A and B, applying a sinusoidal in/out function.
+ ///
+ public static double InterpSinInOut(double a, double b, double alpha)
+ {
+ return Lerp(a, b, (alpha < 0.5d) ? InterpSinIn(0d, 1d, alpha * 2d) * 0.5d : InterpSinOut(0d, 1d, alpha * 2d - 1d) * 0.5d + 0.5d);
+ }
+
+ ///
+ /// Interpolation between A and B, applying an exponential in function.
+ ///
+ public static double InterpExpoIn(double a, double b, double alpha)
+ {
+ double modifiedAlpha = (alpha == 0d) ? 0d : Pow(2d, 10d * (alpha - 1d));
+ return Lerp(a, b, modifiedAlpha);
+ }
+
+ ///
+ /// Interpolation between A and B, applying an exponential out function.
+ ///
+ public static double InterpExpoOut(double a, double b, double alpha)
+ {
+ double modifiedAlpha = (alpha == 1d) ? 1d : -Pow(2d, -10d * alpha) + 1d;
+ return Lerp(a, b, modifiedAlpha);
+ }
+
+ ///
+ /// Interpolation between A and B, applying an exponential in/out function.
+ ///
+ public static double InterpExpoInOut(double a, double b, double alpha)
+ {
+ return Lerp(a, b, (alpha < 0.5d) ? InterpExpoIn(0d, 1d, alpha * 2d) * 0.5d : InterpExpoOut(0d, 1d, alpha * 2d - 1d) * 0.5d + 0.5d);
+ }
+
+ ///
+ /// Interpolation between A and B, applying a circular in function.
+ ///
+ public static double InterpCircularIn(double a, double b, double alpha)
+ {
+ double modifiedAlpha = -1d * (Sqrt(1d - alpha * alpha) - 1d);
+ return Lerp(a, b, modifiedAlpha);
+ }
+
+ ///
+ /// Interpolation between A and B, applying a circular out function.
+ ///
+ public static double InterpCircularOut(double a, double b, double alpha)
+ {
+ alpha -= 1d;
+ double modifiedAlpha = Sqrt(1d - alpha * alpha);
+ return Lerp(a, b, modifiedAlpha);
+ }
+
+ ///
+ /// Interpolation between A and B, applying a circular in/out function.
+ ///
+ public static double InterpCircularInOut(double a, double b, double alpha)
+ {
+ return Lerp(a, b, (alpha < 0.5d) ? InterpCircularIn(0d, 1d, alpha * 2d) * 0.5d : InterpCircularOut(0d, 1d, alpha * 2d - 1d) * 0.5d + 0.5d);
+ }
+
+ ///
+ /// Maps the specified value from the given range into another.
+ ///
+ /// The value to map from range [fromMin; fromMax].
+ /// The source range minimum value.
+ /// The source range maximum value.
+ /// The destination range minimum value.
+ /// The destination range maximum value.
+ /// The mapped value in range [toMin; toMax].
+ public static double Map(double value, double fromMin, double fromMax, double toMin, double toMax)
+ {
+ double t = (value - fromMin) / (fromMax - fromMin);
+ return toMin + t * (toMax - toMin);
+ }
+
+ ///
+ /// Get the next power of two for a size.
+ ///
+ /// The size.
+ /// System.Int32.
+ public static double NextPowerOfTwo(double size)
+ {
+ return Math.Pow(2d, Math.Ceiling(Math.Log(size, 2d)));
+ }
+
+ ///
+ /// Converts a float value from sRGB to linear.
+ ///
+ /// The sRGB value.
+ /// A linear value.
+ public static double SRgbToLinear(double sRgbValue)
+ {
+ if (sRgbValue < 0.04045d)
+ return sRgbValue / 12.92d;
+ return Math.Pow((sRgbValue + 0.055d) / 1.055d, 2.4d);
+ }
+
+ ///
+ /// Converts a float value from linear to sRGB.
+ ///
+ /// The linear value.
+ /// The encoded sRGB value.
+ public static double LinearToSRgb(double linearValue)
+ {
+ if (linearValue < 0.0031308d)
+ return linearValue * 12.92d;
+ return 1.055d * Math.Pow(linearValue, 1d / 2.4d) - 0.055d;
+ }
+
+ ///
+ /// Returns square root of f.
+ ///
+ ///
+ public static double Sqrt(double f)
+ {
+ return Math.Sqrt(f);
+ }
+
+ ///
+ /// Returns square of the given value.
+ ///
+ /// The value.
+ /// The value * value.
+ public static double Square(double f)
+ {
+ return f * f;
+ }
+
+ ///
+ /// Returns the tangent of angle f in radians.
+ ///
+ ///
+ public static double Tan(double f)
+ {
+ return Math.Tan(f);
+ }
+
+ ///
+ /// Checks if a and b are almost equals, taking into account the magnitude of floating point numbers (unlike
+ /// method). See Remarks.
+ /// See remarks.
+ ///
+ /// The left value to compare.
+ /// The right value to compare.
+ /// true if a almost equal to b, false otherwise
+ ///
+ /// The code is using the technique described by Bruce Dawson in
+ ///
+ /// Comparing
+ /// Floating point numbers 2012 edition
+ ///
+ /// .
+ ///
+ public static unsafe bool NearEqual(double a, double b)
+ {
+ // Check if the numbers are really close -- needed
+ // when comparing numbers near zero.
+ if (IsZero(a - b))
+ return true;
+
+ // Original from Bruce Dawson: http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
+ long aInt = *(long*)&a;
+ long bInt = *(long*)&b;
+
+ // Different signs means they do not match.
+ if (aInt < 0 != bInt < 0)
+ return false;
+
+ // Find the difference in ULPs.
+ long ulp = Math.Abs(aInt - bInt);
+
+ // Choose of maxUlp = 4
+ // according to http://code.google.com/p/googletest/source/browse/trunk/include/gtest/internal/gtest-internal.h
+ const long maxUlp = 4;
+ return ulp <= maxUlp;
+ }
+
+ ///
+ /// Determines whether the specified value is close to zero (0.0f).
+ ///
+ /// The floating value.
+ /// true if the specified value is close to zero (0.0f); otherwise, false.
+ public static bool IsZero(double a)
+ {
+ return Math.Abs(a) < Epsilon;
+ }
+
+ ///
+ /// Determines whether the specified value is close to one (1.0f).
+ ///
+ /// The floating value.
+ /// true if the specified value is close to one (1.0f); otherwise, false.
+ public static bool IsOne(double a)
+ {
+ return IsZero(a - 1d);
+ }
+
+ ///
+ /// Checks if a - b are almost equals within a float epsilon.
+ ///
+ /// The left value to compare.
+ /// The right value to compare.
+ /// Epsilon value
+ /// true if a almost equal to b within a float epsilon, false otherwise
+ public static bool WithinEpsilon(double a, double b, double epsilon)
+ {
+ double num = a - b;
+ return (-epsilon <= num) && (num <= epsilon);
+ }
+
+ ///
+ /// Determines whether the specified value is in a given range [min; max].
+ ///
+ /// The value.
+ /// The minimum.
+ /// The maximum.
+ ///
+ /// true if the specified value is in a given range; otherwise, false.
+ ///
+ public static bool IsInRange(double value, double min, double max)
+ {
+ return value >= min && value <= max;
+ }
+
+ ///
+ /// Determines whether the specified value is NOT in a given range [min; max].
+ ///
+ /// The value.
+ /// The minimum.
+ /// The maximum.
+ ///
+ /// true if the specified value is NOT in a given range; otherwise, false.
+ ///
+ public static bool IsNotInRange(double value, double min, double max)
+ {
+ return value < min || value > max;
+ }
+
+ #region Angle units conversions
+
+ ///
+ /// Converts revolutions to degrees.
+ ///
+ public static double RevolutionsToDegrees = 360d;
+
+ ///
+ /// Converts revolutions to radians.
+ ///
+ public static double RevolutionsToRadians = TwoPi;
+
+ ///
+ /// Converts revolutions to gradians.
+ ///
+ public static double RevolutionsToGradians = 400d;
+
+ ///
+ /// Converts degrees to revolutions.
+ ///
+ public static double DegreesToRevolutions = (1d / 360d);
+
+ ///
+ /// Converts degrees to radians.
+ ///
+ public static double DegreesToRadians = (Pi / 180d);
+
+ ///
+ /// Converts radians to revolutions.
+ ///
+ public static double RadiansToRevolutions = (1d / TwoPi);
+
+ ///
+ /// Converts radians to gradians.
+ ///
+ public static double RadiansToGradians = (200d / Pi);
+
+ ///
+ /// Converts gradians to revolutions.
+ ///
+ public static double GradiansToRevolutions = (1d / 400d);
+
+ ///
+ /// Converts gradians to degrees.
+ ///
+ public static double GradiansToDegrees = (9.0f / 10d);
+
+ ///
+ /// Converts gradians to radians.
+ ///
+ public static double GradiansToRadians = (Pi / 200d);
+
+ ///
+ /// Converts radians to degrees.
+ ///
+ public static double RadiansToDegrees = (180d / Pi);
+
+ #endregion
+
+ ///
+ /// Given a heading which may be outside the +/- PI range, 'unwind' it back into that range.
+ ///
+ /// Angle in radians to unwind.
+ /// Valid angle in radians.
+ public static double UnwindRadians(double angle)
+ {
+ // TODO: make it faster?
+
+ while (angle > Pi)
+ {
+ angle -= TwoPi;
+ }
+
+ while (angle < -Pi)
+ {
+ angle += TwoPi;
+ }
+
+ return angle;
+ }
+
+ ///
+ /// Utility to ensure angle is between +/- 180 degrees by unwinding
+ ///
+ /// Angle in degrees to unwind.
+ /// Valid angle in degrees.
+ public static double UnwindDegrees(double angle)
+ {
+ // TODO: make it faster?
+
+ while (angle > 180.0f)
+ {
+ angle -= 360.0f;
+ }
+
+ while (angle < -180.0f)
+ {
+ angle += 360.0f;
+ }
+
+ return angle;
+ }
+
+ ///
+ /// Clamps the specified value.
+ ///
+ /// The value.
+ /// The min.
+ /// The max.
+ /// The result of clamping a value between min and max
+ public static double Clamp(double value, double min, double max)
+ {
+ return value < min ? min : value > max ? max : value;
+ }
+
+ ///
+ /// Interpolates between two values using a linear function by a given amount.
+ ///
+ ///
+ /// See http://www.encyclopediaofmath.org/index.php/Linear_interpolation and
+ /// http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
+ ///
+ /// Value to interpolate from.
+ /// Value to interpolate to.
+ /// Interpolation amount.
+ /// The result of linear interpolation of values based on the amount.
+ public static double Lerp(double from, double to, double amount)
+ {
+ return from + (to - from) * amount;
+ }
+
+
+ ///
+ /// Performs smooth (cubic Hermite) interpolation between 0 and 1.
+ ///
+ ///
+ /// See https://en.wikipedia.org/wiki/Smoothstep
+ ///
+ /// Value between 0 and 1 indicating interpolation amount.
+ public static double SmoothStep(double amount)
+ {
+ return amount <= 0d ? 0d
+ : amount >= 1d ? 1d
+ : amount * amount * (3d - 2d * amount);
+ }
+
+ ///
+ /// Performs a smooth(er) interpolation between 0 and 1 with 1st and 2nd order derivatives of zero at endpoints.
+ ///
+ ///
+ /// See https://en.wikipedia.org/wiki/Smoothstep
+ ///
+ /// Value between 0 and 1 indicating interpolation amount.
+ public static double SmootherStep(double amount)
+ {
+ return amount <= 0d ? 0d
+ : amount >= 1d ? 1d
+ : amount * amount * amount * (amount * (amount * 6d - 15d) + 10d);
+ }
+
+ ///
+ /// Calculates the modulo of the specified value.
+ ///
+ /// The value.
+ /// The modulo.
+ /// The result of the modulo applied to value
+ public static double Mod(double value, double modulo)
+ {
+ if (modulo == 0d)
+ return value;
+
+ return value % modulo;
+ }
+
+ ///
+ /// Calculates the modulo 2*PI of the specified value.
+ ///
+ /// The value.
+ /// The result of the modulo applied to value
+ public static double Mod2PI(double value)
+ {
+ return Mod(value, TwoPi);
+ }
+
+ ///
+ /// Wraps the specified value into a range [min, max]
+ ///
+ /// The value.
+ /// The min.
+ /// The max.
+ /// Result of the wrapping.
+ /// Is thrown when is greater than .
+ public static double Wrap(double value, double min, double max)
+ {
+ if (NearEqual(min, max))
+ return min;
+
+ double mind = min;
+ double maxd = max;
+ double valued = value;
+
+ if (mind > maxd)
+ throw new ArgumentException(string.Format("min {0} should be less than or equal to max {1}", min, max), nameof(min));
+
+ double rangeSize = maxd - mind;
+ return mind + (valued - mind) - rangeSize * Math.Floor((valued - mind) / rangeSize);
+ }
+
+ ///
+ /// Gauss function.
+ /// http://en.wikipedia.org/wiki/Gaussian_function#Two-dimensional_Gaussian_function
+ ///
+ /// Curve amplitude.
+ /// Position X.
+ /// Position Y
+ /// Center X.
+ /// Center Y.
+ /// Curve sigma X.
+ /// Curve sigma Y.
+ /// The result of Gaussian function.
+ public static double Gauss(double amplitude, double x, double y, double centerX, double centerY, double sigmaX, double sigmaY)
+ {
+ double cx = x - centerX;
+ double cy = y - centerY;
+
+ double componentX = cx * cx / (2 * sigmaX * sigmaX);
+ double componentY = cy * cy / (2 * sigmaY * sigmaY);
+
+ return amplitude * Math.Exp(-(componentX + componentY));
+ }
+
+ ///
+ /// Converts the input alpha value from a linear 0-1 value into the output alpha described by blend mode.
+ ///
+ /// The alpha (normalized to 0-1).
+ /// The mode.
+ /// The output alpha (normalized to 0-1).
+ public static double InterpolateAlphaBlend(double alpha, AlphaBlendMode mode)
+ {
+ switch (mode)
+ {
+ case AlphaBlendMode.Sinusoidal:
+ alpha = (Sin(alpha * Pi - PiOverTwo) + 1d) / 2d;
+ break;
+ case AlphaBlendMode.Cubic:
+ alpha = CubicInterp(0d, 0d, 1d, 0d, alpha);
+ break;
+ case AlphaBlendMode.QuadraticInOut:
+ alpha = InterpEaseInOut(0d, 1d, alpha, 2d);
+ break;
+ case AlphaBlendMode.CubicInOut:
+ alpha = InterpEaseInOut(0d, 1d, alpha, 3d);
+ break;
+ case AlphaBlendMode.HermiteCubic:
+ alpha = SmoothStep(0d, 1d, alpha);
+ break;
+ case AlphaBlendMode.QuarticInOut:
+ alpha = InterpEaseInOut(0d, 1d, alpha, 4d);
+ break;
+ case AlphaBlendMode.QuinticInOut:
+ alpha = InterpEaseInOut(0d, 1d, alpha, 5d);
+ break;
+ case AlphaBlendMode.CircularIn:
+ alpha = InterpCircularIn(0d, 1d, alpha);
+ break;
+ case AlphaBlendMode.CircularOut:
+ alpha = InterpCircularOut(0d, 1d, alpha);
+ break;
+ case AlphaBlendMode.CircularInOut:
+ alpha = InterpCircularInOut(0d, 1d, alpha);
+ break;
+ case AlphaBlendMode.ExpIn:
+ alpha = InterpExpoIn(0d, 1d, alpha);
+ break;
+ case AlphaBlendMode.ExpOut:
+ alpha = InterpExpoOut(0d, 1d, alpha);
+ break;
+ case AlphaBlendMode.ExpInOut:
+ alpha = InterpExpoInOut(0d, 1d, alpha);
+ break;
+ }
+
+ return Saturate(alpha);
+ }
+ }
+}
diff --git a/Source/Engine/Core/Math/Mathd.h b/Source/Engine/Core/Math/Mathd.h
new file mode 100644
index 000000000..47b16b608
--- /dev/null
+++ b/Source/Engine/Core/Math/Mathd.h
@@ -0,0 +1,362 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#pragma once
+
+#include
+#include "Engine/Core/Types/BaseTypes.h"
+
+namespace Math
+{
+ ///
+ /// Computes the sine and cosine of a scalar double.
+ ///
+ /// The input angle (in radians).
+ /// The output sine value.
+ /// The output cosine value.
+ FLAXENGINE_API void SinCos(double angle, double& sine, double& cosine);
+
+ static FORCE_INLINE double Trunc(double value)
+ {
+ return trunc(value);
+ }
+
+ static FORCE_INLINE double Round(double value)
+ {
+ return round(value);
+ }
+
+ static FORCE_INLINE double Floor(double value)
+ {
+ return floor(value);
+ }
+
+ static FORCE_INLINE double Ceil(double value)
+ {
+ return ceil(value);
+ }
+
+ static FORCE_INLINE double Sin(double value)
+ {
+ return sin(value);
+ }
+
+ static FORCE_INLINE double Asin(double value)
+ {
+ return asin(value < -1. ? -1. : value < 1. ? value : 1.);
+ }
+
+ static FORCE_INLINE double Sinh(double value)
+ {
+ return sinh(value);
+ }
+
+ static FORCE_INLINE double Cos(double value)
+ {
+ return cos(value);
+ }
+
+ static FORCE_INLINE double Acos(double value)
+ {
+ return acos(value < -1. ? -1. : value < 1. ? value : 1.);
+ }
+
+ static FORCE_INLINE double Tan(double value)
+ {
+ return tan(value);
+ }
+
+ static FORCE_INLINE double Atan(double value)
+ {
+ return atan(value);
+ }
+
+ static FORCE_INLINE double Atan2(double y, double x)
+ {
+ return atan2(y, x);
+ }
+
+ static FORCE_INLINE double InvSqrt(double value)
+ {
+ return 1.0f / sqrt(value);
+ }
+
+ static FORCE_INLINE double Log(const double value)
+ {
+ return log(value);
+ }
+
+ static FORCE_INLINE double Log2(const double value)
+ {
+ return log2(value);
+ }
+
+ static FORCE_INLINE double Log10(const double value)
+ {
+ return log10(value);
+ }
+
+ static FORCE_INLINE double Pow(const double base, const double exponent)
+ {
+ return pow(base, exponent);
+ }
+
+ static FORCE_INLINE double Sqrt(const double value)
+ {
+ return sqrt(value);
+ }
+
+ static FORCE_INLINE double Exp(const double value)
+ {
+ return exp(value);
+ }
+
+ static FORCE_INLINE double Exp2(const double value)
+ {
+ return exp2(value);
+ }
+
+ static FORCE_INLINE double Abs(const double value)
+ {
+ return fabs(value);
+ }
+
+ static FORCE_INLINE double Mod(const double a, const double b)
+ {
+ return fmod(a, b);
+ }
+
+ static FORCE_INLINE double ModF(double a, double* b)
+ {
+ return modf(a, b);
+ }
+
+ ///
+ /// Returns signed fractional part of a double.
+ ///
+ /// Double point value to convert.
+ /// A double between [0 ; 1) for nonnegative input. A double between [-1; 0) for negative input.
+ static FORCE_INLINE double Fractional(double value)
+ {
+ return value - Trunc(value);
+ }
+
+ static int64 TruncToInt(double value)
+ {
+ return (int64)value;
+ }
+
+ static int64 FloorToInt(double value)
+ {
+ return TruncToInt(floor(value));
+ }
+
+ static FORCE_INLINE int64 RoundToInt(double value)
+ {
+ return FloorToInt(value + 0.5);
+ }
+
+ static FORCE_INLINE int64 CeilToInt(double value)
+ {
+ return TruncToInt(ceil(value));
+ }
+
+ // Performs smooth (cubic Hermite) interpolation between 0 and 1
+ // @param amount Value between 0 and 1 indicating interpolation amount
+ static double SmoothStep(double amount)
+ {
+ return amount <= 0. ? 0. : amount >= 1. ? 1. : amount * amount * (3. - 2. * amount);
+ }
+
+ // Performs a smooth(er) interpolation between 0 and 1 with 1st and 2nd order derivatives of zero at endpoints
+ // @param amount Value between 0 and 1 indicating interpolation amount
+ static double SmootherStep(double amount)
+ {
+ return amount <= 0. ? 0. : amount >= 1. ? 1. : amount * amount * amount * (amount * (amount * 6. - 15.) + 10.);
+ }
+
+ // Determines whether the specified value is close to zero (0.0)
+ // @param a The floating value
+ // @returns True if the specified value is close to zero (0.0). otherwise false
+ inline bool IsZero(double a)
+ {
+ return Abs(a) < ZeroTolerance;
+ }
+
+ // Determines whether the specified value is close to one (1.0f)
+ // @param a The floating value
+ // @returns True if the specified value is close to one (1.0f). otherwise false
+ inline bool IsOne(double a)
+ {
+ return IsZero(a - 1.);
+ }
+
+ // Returns a value indicating the sign of a number
+ // @returns A number that indicates the sign of value
+ inline double Sign(double v)
+ {
+ return v > 0. ? 1. : v < 0. ? -1. : 0.;
+ }
+
+ ///
+ /// Compares the sign of two double values.
+ ///
+ /// The first value.
+ /// The second value.
+ /// True if given values have the same sign (both positive or negative); otherwise false.
+ inline bool SameSign(const double a, const double b)
+ {
+ return a * b >= 0.;
+ }
+
+ ///
+ /// Compares the sign of two double values.
+ ///
+ /// The first value.
+ /// The second value.
+ /// True if given values don't have the same sign (first is positive and second is negative or vice versa); otherwise false.
+ inline bool NotSameSign(const double a, const double b)
+ {
+ return a * b < 0.;
+ }
+
+ ///
+ /// Checks if a and b are not even almost equal, taking into account the magnitude of double numbers
+ ///
+ /// The left value to compare
+ /// The right value to compare
+ /// False if a almost equal to b, otherwise true
+ static bool NotNearEqual(double a, double b)
+ {
+ // Check if the numbers are really close - needed when comparing numbers near zero
+ if (IsZero(a - b))
+ return false;
+
+ // Original from Bruce Dawson: http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
+ const int64 aInt = *(int64*)&a;
+ const int64 bInt = *(int64*)&b;
+
+ // Different signs means they do not match
+ if (aInt < 0 != bInt < 0)
+ return true;
+
+ // Find the difference in ULPs
+ const int64 ulp = Math::Abs(aInt - bInt);
+
+ // Choose of maxUlp = 4
+ // according to http://code.google.com/p/googletest/source/browse/trunk/include/gtest/internal/gtest-internal.h
+ const int maxUlp = 4;
+ return ulp > maxUlp;
+ }
+
+
+ ///
+ /// Checks if a and b are almost equals within the given epsilon value.
+ ///
+ /// The left value to compare.
+ /// The right value to compare.
+ /// The comparision epsilon value. Should be 1e-4 or less.
+ /// True if a almost equal to b, otherwise false
+ static bool NearEqual(double a, double b, double eps)
+ {
+ return Abs(a - b) < eps;
+ }
+
+ ///
+ /// Remaps the specified value from the specified range to another.
+ ///
+ /// The value to remap.
+ /// The source range minimum.
+ /// The source range maximum.
+ /// The destination range minimum.
+ /// The destination range maximum.
+ /// The remapped value.
+ static double Remap(double value, double fromMin, double fromMax, double toMin, double toMax)
+ {
+ return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin;
+ }
+
+ static double ClampAxis(double angle)
+ {
+ angle = Mod(angle, 360.);
+ if (angle < 0.)
+ angle += 360.;
+ return angle;
+ }
+
+ static double NormalizeAxis(double angle)
+ {
+ angle = ClampAxis(angle);
+ if (angle > 180.)
+ angle -= 360.;
+ return angle;
+ }
+
+ // Find the smallest angle between two headings (in radians).
+ static double FindDeltaAngle(double a1, double a2)
+ {
+ double delta = a2 - a1;
+ if (delta > PI)
+ delta = delta - TWO_PI;
+ else if (delta < -PI)
+ delta = delta + TWO_PI;
+ return delta;
+ }
+
+ // Given a heading which may be outside the +/- PI range, 'unwind' it back into that range
+ static double UnwindRadians(double a)
+ {
+ while (a > PI)
+ a -= TWO_PI;
+ while (a < -PI)
+ a += TWO_PI;
+ return a;
+ }
+
+ // Utility to ensure angle is between +/- 180 degrees by unwinding
+ static double UnwindDegrees(double a)
+ {
+ while (a > 180.)
+ a -= 360.;
+ while (a < -180.)
+ a += 360.;
+ return a;
+ }
+
+ ///
+ /// Returns value based on comparand. The main purpose of this function is to avoid branching based on floating point comparison which can be avoided via compiler intrinsics.
+ ///
+ ///
+ /// Please note that this doesn't define what happens in the case of NaNs as there might be platform specific differences.
+ ///
+ /// Comparand the results are based on.
+ /// The result value if comparand >= 0.
+ /// The result value if comparand < 0.
+ /// the valueGEZero if comparand >= 0, valueLTZero otherwise
+ static double DoubleSelect(double comparand, double valueGEZero, double valueLTZero)
+ {
+ return comparand >= 0. ? valueGEZero : valueLTZero;
+ }
+
+ ///
+ /// Returns a smooth Hermite interpolation between 0 and 1 for the value X (where X ranges between A and B). Clamped to 0 for X <= A and 1 for X >= B.
+ ///
+ /// The minimum value of x.
+ /// The maximum value of x.
+ /// The x.
+ /// The smoothed value between 0 and 1.
+ static double SmoothStep(double a, double b, double x)
+ {
+ if (x < a)
+ return 0.;
+ if (x >= b)
+ return 1.;
+ const double fraction = (x - a) / (b - a);
+ return fraction * fraction * (3. - 2. * fraction);
+ }
+
+//TODO: When double vectors are implemented
+ // Rotates position about the given axis by the given angle, in radians, and returns the offset to position
+ //Vector3 FLAXENGINE_API RotateAboutAxis(const Vector3& normalizedRotationAxis, float angle, const Vector3& positionOnAxis, const Vector3& position);
+
+ //Vector3 FLAXENGINE_API ExtractLargestComponent(const Vector3& v);
+}
diff --git a/Source/Engine/Core/Math/Mathf.cs b/Source/Engine/Core/Math/Mathf.cs
index 93e0ff8a4..488c46777 100644
--- a/Source/Engine/Core/Math/Mathf.cs
+++ b/Source/Engine/Core/Math/Mathf.cs
@@ -6,7 +6,7 @@ using System.ComponentModel;
namespace FlaxEngine
{
///
- /// A collection of common math functions.
+ /// A collection of common math functions on single floating-points.
///
[HideInEditor]
public static class Mathf
diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp
index 4b3cbd658..683b195be 100644
--- a/Source/Engine/Core/Types/Variant.cpp
+++ b/Source/Engine/Core/Types/Variant.cpp
@@ -6,6 +6,7 @@
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Content/Asset.h"
#include "Engine/Core/Log.h"
+#include "Engine/Core/Math/Mathd.h"
#include "Engine/Core/Math/BoundingBox.h"
#include "Engine/Core/Math/BoundingSphere.h"
#include "Engine/Core/Math/Vector2.h"
diff --git a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp
index dd1873bab..80dadd8c7 100644
--- a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp
+++ b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp
@@ -4,6 +4,7 @@
#include "ModelTool.h"
#include "Engine/Core/Log.h"
+#include "Engine/Core/Math/Mathd.h"
#include "Engine/Core/Math/Matrix.h"
#include "Engine/Core/Collections/Sorting.h"
#include "Engine/Platform/FileSystem.h"