// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #pragma once #include "Engine/Core/Core.h" #include "Engine/Core/Types/BaseTypes.h" #include #undef PI #define PI 3.1415926535897932f #define TWO_PI 6.28318530718f #define PI_INV 0.31830988618f #define PI_OVER_2 1.57079632679f #define PI_OVER_4 0.78539816339f #define PI_HALF PI_OVER_2 #define GOLDEN_RATIO 1.6180339887f // The value for which all absolute numbers smaller than are considered equal to zero. #define ZeroTolerance 1e-6f // Converts radians to degrees. #define RadiansToDegrees (180.0f / PI) // Converts degrees to radians. #define DegreesToRadians (PI / 180.0f) namespace Math { /// /// Computes the sine and cosine of a scalar float. /// /// The input angle (in radians). /// The output sine value. /// The output cosine value. FLAXENGINE_API void SinCos(float angle, float& sine, float& cosine); /// /// Computes the base 2 logarithm for an integer value that is greater than 0. The result is rounded down to the nearest integer. /// /// The value to compute the log of. /// The Log2 of value. 0 if value is 0. FLAXENGINE_API uint32 FloorLog2(uint32 value); static FORCE_INLINE float Trunc(float value) { return truncf(value); } static FORCE_INLINE float Round(float value) { return roundf(value); } static FORCE_INLINE float Floor(float value) { return floorf(value); } static FORCE_INLINE float Ceil(float value) { return ceilf(value); } static FORCE_INLINE float Sin(float value) { return sinf(value); } static FORCE_INLINE float Asin(float value) { return asinf(value < -1.f ? -1.f : value < 1.f ? value : 1.f); } static FORCE_INLINE float Sinh(float value) { return sinhf(value); } static FORCE_INLINE float Cos(float value) { return cosf(value); } static FORCE_INLINE float Acos(float value) { return acosf(value < -1.f ? -1.f : value < 1.f ? value : 1.f); } static FORCE_INLINE float Tan(float value) { return tanf(value); } static FORCE_INLINE float Atan(float value) { return atanf(value); } static FORCE_INLINE float Atan2(float y, float x) { return atan2f(y, x); } static FORCE_INLINE float InvSqrt(float value) { return 1.0f / sqrtf(value); } static FORCE_INLINE float Log(const float value) { return logf(value); } static FORCE_INLINE float Log2(const float value) { return log2f(value); } static FORCE_INLINE float Log10(const float value) { return log10f(value); } static FORCE_INLINE float Pow(const float base, const float exponent) { return powf(base, exponent); } static FORCE_INLINE float Sqrt(const float value) { return sqrtf(value); } static FORCE_INLINE float Exp(const float value) { return expf(value); } static FORCE_INLINE float Exp2(const float value) { return exp2f(value); } static FORCE_INLINE float Abs(const float value) { return fabsf(value); } static FORCE_INLINE int32 Abs(const int32 value) { return value < 0 ? -value : value; } static FORCE_INLINE int64 Abs(const int64 value) { return value < 0 ? -value : value; } static FORCE_INLINE float Mod(const float a, const float b) { return fmodf(a, b); } static FORCE_INLINE float ModF(const float a, float* b) { return modff(a, b); } /// /// Returns signed fractional part of a float. /// /// Floating point value to convert. /// A float between [0 ; 1) for nonnegative input. A float between [-1; 0) for negative input. static FORCE_INLINE float Fractional(float value) { return value - Trunc(value); } static int32 TruncToInt(float value) { return (int32)value; } static int32 FloorToInt(float value) { return TruncToInt(floorf(value)); } static FORCE_INLINE int32 RoundToInt(float value) { return FloorToInt(value + 0.5f); } static FORCE_INLINE int32 CeilToInt(float value) { return TruncToInt(ceilf(value)); } /// /// Rounds up the value to the next power of 2. /// /// The value. /// The power of 2. static int32 RoundUpToPowerOf2(int32 value) { // Source: // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 if (value < 0) return 0; value++; value |= value >> 1; value |= value >> 2; value |= value >> 4; value |= value >> 8; value |= value >> 16; return value + 1; } /// /// Rounds up the value to the next power of 2. /// /// The value. /// The power of 2. static uint32 RoundUpToPowerOf2(uint32 value) { // Source: // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 value++; value |= value >> 1; value |= value >> 2; value |= value >> 4; value |= value >> 8; value |= value >> 16; return value + 1; } /// /// Rounds up the value to the next power of 2. /// /// The value. /// The power of 2. static int64 RoundUpToPowerOf2(int64 value) { // Source: // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 if (value < 0) return 0; value++; value |= value >> 1; value |= value >> 2; value |= value >> 4; value |= value >> 8; value |= value >> 16; value |= value >> 32; return value + 1; } /// /// Rounds up the value to the next power of 2. /// /// The value. /// The power of 2. static uint64 RoundUpToPowerOf2(uint64 value) { // Source: // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 value++; value |= value >> 1; value |= value >> 2; value |= value >> 4; value |= value >> 8; value |= value >> 16; value |= value >> 32; return value + 1; } // Divides two integers and rounds up template static FORCE_INLINE T DivideAndRoundUp(T dividend, T divisor) { return (dividend + divisor - 1) / divisor; } template static FORCE_INLINE T DivideAndRoundDown(T dividend, T divisor) { return dividend / divisor; } // Check if value is inside the given range // @param value value to check // @param min Minimum value // @param max Maximum value template static bool IsInRange(const T value, const T min, const T max) { return value >= min && value <= max; } // Check if value isn't inside the given range // @param value value to check // @param min Minimum value // @param max Maximum value template static bool IsNotInRange(const T value, const T min, const T max) { return value < min || value > max; } // Checks whether a number is a power of two. // @param value Number to check // @returns True if value is a power of two static bool IsPowerOfTwo(uint32 value) { return (value & value - 1) == 0; } // Clamp value to be between minimum and maximum values, inclusive template static T Clamp(const T value, const T min, const T max) { return value < min ? min : value < max ? value : max; } // Clamp value to be between 0 and 1 range, inclusive template static T Saturate(const T value) { return value < 0 ? 0 : value < 1 ? value : 1; } /// /// Returns average arithmetic of two values. /// /// The first value. /// The second value. /// Average arithmetic of the two values. template static float AverageArithmetic(const T a, const T b) { return (a + b) * 0.5f; } // Returns highest of 2 values template static T Max(const T a, const T b) { return a > b ? a : b; } // Returns lowest of 2 values template static T Min(const T a, const T b) { return a < b ? a : b; } // Returns highest of 3 values template static T Max(const T a, const T b, const T c) { return Max(Max(a, b), c); } // Returns highest of 4 values template static T Max(const T a, const T b, const T c, const T d) { return Max(Max(Max(a, b), c), d); } // Returns lowest of 3 values template static T Min(const T a, const T b, const T c) { return Min(Min(a, b), c); } // Returns lowest of 4 values template static T Min(const T a, const T b, const T c, const T d) { return Min(Min(Min(a, b), c), d); } // Multiply value by itself template static T Square(const T a) { return a * a; } // Performs a linear interpolation between two values, alpha ranges from 0-1 template static T Lerp(const T& a, const T& b, const U& alpha) { return (T)(a + alpha * (b - a)); } // Performs a linear interpolation between two values, alpha ranges from 0-1. Handles full numeric range of T template static T LerpStable(const T& a, const T& b, double alpha) { return (T)(a * (1.0 - alpha) + b * alpha); } // Performs a linear interpolation between two values, alpha ranges from 0-1. Handles full numeric range of T template static T LerpStable(const T& a, const T& b, float alpha) { return (T)(a * (1.0f - alpha) + b * alpha); } // Calculates the linear parameter t that produces the interpolation value within the range [a, b]. template static T InverseLerp(const T& a, const T& b, const U& value) { if (a == b) return (T)0; return Saturate((value - a) / (b - a)); } // Performs smooth (cubic Hermite) interpolation between 0 and 1 // @param amount Value between 0 and 1 indicating interpolation amount static float SmoothStep(float 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 float SmootherStep(float 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.0f) // @param a The floating value // @returns True if the specified value is close to zero (0.0f). otherwise false inline bool IsZero(float 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(float a) { return IsZero(a - 1.0f); } // Returns a value indicating the sign of a number // @returns A number that indicates the sign of value inline float Sign(float v) { return v > 0.0f ? 1.0f : v < 0.0f ? -1.0f : 0.0f; } /// /// Compares the sign of two floating-point 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 float a, const float b) { return a * b >= 0.0f; } /// /// Compares the sign of two floating-point 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 float a, const float b) { return a * b < 0.0f; } /// /// Checks if a and b are not even almost equal, taking into account the magnitude of floating point numbers /// /// The left value to compare /// The right value to compare /// False if a almost equal to b, otherwise true static bool NotNearEqual(float a, float 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 int32 aInt = *(int32*)&a; const int32 bInt = *(int32*)&b; // Different signs means they do not match if (aInt < 0 != bInt < 0) return true; // Find the difference in ULPs const int ulp = 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, taking into account the magnitude of floating point numbers /// /// The left value to compare /// The right value to compare /// True if a almost equal to b, otherwise false static bool NearEqual(float a, float 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/ const int32 aInt = *(int32*)&a; const int32 bInt = *(int32*)&b; // Different signs means they do not match if (aInt < 0 != bInt < 0) return false; // Find the difference in ULPs const int ulp = 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(float a, float b, float 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 float Remap(float value, float fromMin, float fromMax, float toMin, float toMax) { return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin; } template static T AlignUpWithMask(T value, T mask) { return (T)(value + mask & ~mask); } template static T AlignDownWithMask(T value, T mask) { return (T)(value & ~mask); } /// /// Aligns value up to match desire alignment. /// /// The value. /// The alignment. /// Aligned value (multiple of alignment). template static T AlignUp(T value, T alignment) { T mask = alignment - 1; return (T)(value + mask & ~mask); } /// /// Aligns value down to match desire alignment. /// /// The value. /// The alignment. /// Aligned value (multiple of alignment). template static T AlignDown(T value, T alignment) { T mask = alignment - 1; return (T)(value & ~mask); } /// /// Determines whether the specified value is aligned. /// /// The value. /// The alignment. /// true if the specified value is aligned; otherwise, false. template static bool IsAligned(T value, T alignment) { return 0 == (value & alignment - 1); } template static T DivideByMultiple(T value, T alignment) { return (T)((value + alignment - 1) / alignment); } static float ClampAxis(float angle) { angle = Mod(angle, 360.f); if (angle < 0.0f) angle += 360.0f; return angle; } static float NormalizeAxis(float angle) { angle = ClampAxis(angle); if (angle > 180.f) angle -= 360.f; return angle; } // Find the smallest angle between two headings (in radians). static float FindDeltaAngle(float a1, float a2) { float 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 float UnwindRadians(float 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 float UnwindDegrees(float a) { while (a > 180.f) a -= 360.f; while (a < -180.f) a += 360.f; 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 float FloatSelect(float comparand, float valueGEZero, float valueLTZero) { return comparand >= 0.f ? 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 float SmoothStep(float a, float b, float x) { if (x < a) return 0.0f; if (x >= b) return 1.0f; const float fraction = (x - a) / (b - a); return fraction * fraction * (3.0f - 2.0f * fraction); } /// /// 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. template static FORCE_INLINE T CubicInterp(const T& p0, const T& t0, const T& p1, const T& t1, const U& alpha) { const float alpha2 = alpha * alpha; const float alpha3 = alpha2 * alpha; return (T)((2 * alpha3 - 3 * alpha2 + 1) * p0) + (alpha3 - 2 * alpha2 + alpha) * t0 + (alpha3 - alpha2) * t1 + (-2 * alpha3 + 3 * alpha2) * p1; } /// /// Interpolate between A and B, applying an ease in function. Exponent controls the degree of the curve. /// template static FORCE_INLINE T InterpEaseIn(const T& a, const T& b, float alpha, float exponent) { const float blend = Pow(alpha, exponent); return Lerp(a, b, blend); } /// /// Interpolate between A and B, applying an ease out function. Exponent controls the degree of the curve. /// template static FORCE_INLINE T InterpEaseOut(const T& a, const T& b, float alpha, float exponent) { const float blend = 1.f - Pow(1.f - alpha, exponent); return Lerp(a, b, blend); } /// /// Interpolate between A and B, applying an ease in/out function. Exponent controls the degree of the curve. /// template static FORCE_INLINE T InterpEaseInOut(const T& a, const T& b, float alpha, float exponent) { return Lerp(a, b, alpha < 0.5f ? InterpEaseIn(0.f, 1.f, alpha * 2.f, exponent) * 0.5f : InterpEaseOut(0.f, 1.f, alpha * 2.f - 1.f, exponent) * 0.5f + 0.5f); } /// /// Interpolation between A and B, applying a sinusoidal in function. /// template static FORCE_INLINE T InterpSinIn(const T& a, const T& b, float alpha) { const float blend = -1.f * Cos(alpha * PI_HALF) + 1.f; return Lerp(a, b, blend); } /// /// Interpolation between A and B, applying a sinusoidal out function. /// template static FORCE_INLINE T InterpSinOut(const T& a, const T& b, float alpha) { const float blend = Sin(alpha * PI_HALF); return Lerp(a, b, blend); } /// /// Interpolation between A and B, applying a sinusoidal in/out function. /// template static FORCE_INLINE T InterpSinInOut(const T& a, const T& b, float alpha) { return Lerp(a, b, alpha < 0.5f ? InterpSinIn(0.f, 1.f, alpha * 2.f) * 0.5f : InterpSinOut(0.f, 1.f, alpha * 2.f - 1.f) * 0.5f + 0.5f); } /// /// Interpolation between A and B, applying an exponential in function. /// template static FORCE_INLINE T InterpExpoIn(const T& a, const T& b, float alpha) { const float blend = alpha == 0.f ? 0.f : Pow(2.f, 10.f * (alpha - 1.f)); return Lerp(a, b, blend); } /// /// Interpolation between A and B, applying an exponential out function. /// template static FORCE_INLINE T InterpExpoOut(const T& a, const T& b, float alpha) { const float blend = alpha == 1.f ? 1.f : -Pow(2.f, -10.f * alpha) + 1.f; return Lerp(a, b, blend); } /// /// Interpolation between A and B, applying an exponential in/out function. /// template static FORCE_INLINE T InterpExpoInOut(const T& a, const T& b, float alpha) { return Lerp(a, b, alpha < 0.5f ? InterpExpoIn(0.f, 1.f, alpha * 2.f) * 0.5f : InterpExpoOut(0.f, 1.f, alpha * 2.f - 1.f) * 0.5f + 0.5f); } /// /// Interpolation between A and B, applying a circular in function. /// template static FORCE_INLINE T InterpCircularIn(const T& a, const T& b, float alpha) { const float blend = -1.f * (Sqrt(1.f - alpha * alpha) - 1.f); return Lerp(a, b, blend); } /// /// Interpolation between A and B, applying a circular out function. /// template static FORCE_INLINE T InterpCircularOut(const T& a, const T& b, float alpha) { alpha -= 1.f; const float blend = Sqrt(1.f - alpha * alpha); return Lerp(a, b, blend); } /// /// Interpolation between A and B, applying a circular in/out function. /// template static FORCE_INLINE T InterpCircularInOut(const T& a, const T& b, float alpha) { return Lerp(a, b, alpha < 0.5f ? InterpCircularIn(0.f, 1.f, alpha * 2.f) * 0.5f : InterpCircularOut(0.f, 1.f, alpha * 2.f - 1.f) * 0.5f + 0.5f); } // 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); }