738 lines
24 KiB
C#
738 lines
24 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Numerics;
|
|
using System.Runtime.CompilerServices;
|
|
using T3.Core.Animation;
|
|
|
|
namespace T3.Core.Utils;
|
|
|
|
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
|
public static class MathUtils
|
|
{
|
|
public static float ToRad => (float)(Math.PI / 180.0);
|
|
public static float ToDegree => (float)(180.0 / Math.PI);
|
|
|
|
public static float PerlinNoise(float value, float period, int octaves, int seed)
|
|
{
|
|
var noiseSum = 0.0f;
|
|
octaves = octaves.Clamp(1, 20);
|
|
var frequency = period;
|
|
var amplitude = 0.5f;
|
|
for (var octave = 0; octave < octaves - 1; octave++)
|
|
{
|
|
var v = value * frequency + seed * 12.468f;
|
|
var a = Noise((int)v, seed);
|
|
var b = Noise((int)v + 1, seed);
|
|
var t = Fade(v - (float)Math.Floor(v));
|
|
noiseSum += Lerp(a, b, t) * amplitude;
|
|
frequency *= 2;
|
|
amplitude *= 0.5f;
|
|
}
|
|
|
|
return noiseSum;
|
|
}
|
|
|
|
private static float Noise(int x, int seed)
|
|
{
|
|
int n = x + seed * 137;
|
|
n = (n << 13) ^ n;
|
|
return (float)(1.0 - ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static float GetBias(float b, float x)
|
|
{
|
|
return x / (((1f / b - 2f) * (1f - x)) + 1f);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static float GetSchlickBias(float g, float x)
|
|
{
|
|
if (x < 0.5f)
|
|
{
|
|
x *= 2f;
|
|
x = 0.5f * GetBias(g, x);
|
|
}
|
|
else
|
|
{
|
|
x = 2f * x - 1f;
|
|
x = 0.5f * GetBias(1f - g, x) + 0.5f;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
public static float ApplyGainAndBias(this float value, float gain, float bias)
|
|
{
|
|
var b = bias.Clamp(0,1);
|
|
var g = gain.Clamp(0,1);
|
|
|
|
if (value > 0.999f)
|
|
return 1f;
|
|
|
|
if (value < 0.00001f)
|
|
return 0f;
|
|
|
|
if (g < 0.5f)
|
|
{
|
|
value = GetBias(b, value);
|
|
value = GetSchlickBias(g, value);
|
|
}
|
|
else
|
|
{
|
|
value = GetSchlickBias(g, value);
|
|
value = GetBias(b, value);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
|
|
|
|
|
|
[Obsolete("Please use ApplyGainAndBias()")]
|
|
public static float ApplyBiasAndGain(this float value, float gain,float bias )
|
|
{
|
|
bias = Clamp(bias, 0.001f, 0.999f);
|
|
gain = Clamp(gain, 0.001f, 0.999f);
|
|
|
|
// Apply bias
|
|
value /= ((1.0f / bias - 2.0f) * (1.0f - value) + 1.0f);
|
|
|
|
var gainFactorLow = 1.0f / gain - 2.0f;
|
|
var gainFactorHigh = 1.0f / (1.0f - gain) - 2.0f;
|
|
|
|
// Use conditional expression to determine scaled value
|
|
var scaledValue = value < 0.5f
|
|
? (value * 2.0f) / (gainFactorLow * (1.0f - value * 2.0f) + 1.0f) * 0.5f
|
|
: ((value * 2.0f - 1.0f) / (gainFactorHigh * (1.0f - (value * 2.0f - 1.0f)) + 1.0f)) * 0.5f + 0.5f;
|
|
return scaledValue;
|
|
}
|
|
|
|
|
|
public static uint XxHash(uint p)
|
|
{
|
|
const uint prime32A = 3266489917U;
|
|
const uint prime32B = 668265263U, prime32C = 374761393U;
|
|
|
|
uint h32 = p + prime32C;
|
|
h32 = prime32B * ((h32 << 17) | (h32 >> (32 - 17)));
|
|
h32 = 2246822519U * (h32 ^ (h32 >> 15));
|
|
h32 = prime32A * (h32 ^ (h32 >> 13));
|
|
|
|
return h32 ^ (h32 >> 16);
|
|
}
|
|
|
|
public static int XxHash(int p)
|
|
{
|
|
return (int)XxHash((uint)p);
|
|
}
|
|
|
|
public static float Hash01( uint x )
|
|
{
|
|
x *= 13331U;
|
|
const uint k = 1103515245U; // GLIB C
|
|
x = ((x>>8)^x)*k;
|
|
x = ((x>>8)^x)*k;
|
|
|
|
return (float)( (x & 0x7fffffff) / 2147483648.0);
|
|
}
|
|
|
|
private static float Fade(float t)
|
|
{
|
|
return t * t * t * (t * (t * 6 - 15) + 10);
|
|
}
|
|
|
|
public static float SmootherStep(float min, float max, float value)
|
|
{
|
|
var t = Math.Max(0, Math.Min(1, (value - min) / (max - min)));
|
|
return Fade(t);
|
|
}
|
|
|
|
private static float SmoothStep(float min, float max, float value)
|
|
{
|
|
var x = Math.Max(0, Math.Min(1, (value - min) / (max - min)));
|
|
return x * x * (3 - 2 * x);
|
|
}
|
|
|
|
private static double SmoothStep(double min, double max, double value)
|
|
{
|
|
var x = Math.Max(0, Math.Min(1, (value - min) / (max - min)));
|
|
return x * x * (3 - 2 * x);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static float ToDegrees(this float val)
|
|
{
|
|
return val * 180 / MathF.PI;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static float ToRadians(this float val)
|
|
{
|
|
return val * MathF.PI / 180;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static bool _IsFinite(this float value)
|
|
{
|
|
return !float.IsNaN(value) && !float.IsInfinity(value);
|
|
}
|
|
|
|
public static bool _IsFinite(this Vector3 value)
|
|
{
|
|
return value.X._IsFinite() && value.Y._IsFinite() && value.Z._IsFinite();
|
|
}
|
|
|
|
public static Vector2 Clamp(this Vector2 v, Vector2 mn, Vector2 mx)
|
|
{
|
|
return new Vector2((v.X < mn.X)? mn.X : (v.X > mx.X) ? mx.X : v.X,
|
|
(v.Y < mn.Y) ? mn.Y : (v.Y > mx.Y) ? mx.Y : v.Y);
|
|
}
|
|
|
|
public static Vector3 Clamp(this Vector3 v, Vector3 mn, Vector3 mx)
|
|
{
|
|
return new Vector3((v.X < mn.X)? mn.X : (v.X > mx.X) ? mx.X : v.X,
|
|
(v.Y < mn.Y) ? mn.Y : (v.Y > mx.Y) ? mx.Y : v.Y,
|
|
(v.Z < mn.Z) ? mn.Z : (v.Z > mx.Z) ? mx.Z : v.Z);
|
|
}
|
|
|
|
public static Vector2 Remap(this Vector2 value2, Vector2 inMin, Vector2 inMax, Vector2 outMin, Vector2 outMax)
|
|
{
|
|
var factor = (value2 - inMin) / (inMax - inMin);
|
|
var v = factor * (outMax - outMin) + outMin;
|
|
return v;
|
|
}
|
|
|
|
public static Vector3 Remap(this Vector3 value2, Vector3 inMin, Vector3 inMax, Vector3 outMin, Vector3 outMax)
|
|
{
|
|
var factor = (value2 - inMin) / (inMax - inMin);
|
|
var v = factor * (outMax - outMin) + outMin;
|
|
return v;
|
|
}
|
|
|
|
// TODO: move to another class
|
|
public static int FindIndexForTime<T>(List<T> items, double time, Func<int, double> timeAtIndex)
|
|
{
|
|
if (items.Count == 0)
|
|
return -1;
|
|
|
|
var lastIndex = items.Count - 1;
|
|
var firstIndex = 0;
|
|
|
|
if (timeAtIndex(lastIndex) <= time)
|
|
return lastIndex;
|
|
|
|
if (timeAtIndex(firstIndex) >= time)
|
|
return firstIndex;
|
|
|
|
while (lastIndex - firstIndex > 1)
|
|
{
|
|
var middleIndex = (firstIndex + lastIndex) / 2;
|
|
|
|
var delta = timeAtIndex(middleIndex) - time;
|
|
|
|
if (delta < 0)
|
|
firstIndex = middleIndex;
|
|
else
|
|
lastIndex = middleIndex;
|
|
}
|
|
return firstIndex;
|
|
}
|
|
|
|
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
// public static T Min<T>(T a, T b) where T : INumber<T>
|
|
// => T.Min(a, b);
|
|
//
|
|
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
// public static T Max<T>(T a, T b) where T : INumber<T>
|
|
// => T.Max(a, b);
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static T Clamp<T>(this T v, T min, T max) where T : INumber<T>
|
|
=> T.Min(T.Max(v, min), max);
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static T ClampMin<T>(this T v, T min) where T : INumber<T>
|
|
=> T.Max(v, min);
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static T ClampMax<T>(this T v, T max) where T : INumber<T>
|
|
=> T.Min(v, max);
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static T OptionalClamp<T>(T v, T min, bool clampMin, T max, bool clampMax) where T : INumber<T>
|
|
{
|
|
var r = v;
|
|
if (clampMin) r = T.Max(r, min);
|
|
if (clampMax) r = T.Min(r, max);
|
|
return r;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static int Mod(this int val, int repeat)
|
|
{
|
|
// Prevent exception
|
|
if(repeat == 0)
|
|
return 0;
|
|
|
|
var x = val % repeat;
|
|
if (x < 0)
|
|
x = repeat + x;
|
|
|
|
return x;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static float[] ToArray(this Vector2 vec2)
|
|
{
|
|
return [vec2.X, vec2.Y];
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static float[] ToArray(this Vector3 vec3)
|
|
{
|
|
return [vec3.X, vec3.Y, vec3.Z];
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static float[] ToArray(this Vector4 vec4)
|
|
{
|
|
return [vec4.X, vec4.Y, vec4.Z, vec4.W];
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static float Lerp(float a, float b, float t)
|
|
{
|
|
return a + (b - a) * t;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static float LerpRadianAngle(float from, float to, float t)
|
|
{
|
|
var delta = Fmod((from - to), 2* MathF.PI);
|
|
if (delta > MathF.PI)
|
|
delta -= 2* MathF.PI;
|
|
|
|
return from - delta * t;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static float LerpDegreesAngle(float from, float to, float t)
|
|
{
|
|
var delta = Fmod((from - to), 360);
|
|
if (delta > 180)
|
|
delta -= 360;
|
|
|
|
return from - delta * t;
|
|
}
|
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static float Fmod(float v, float mod)
|
|
{
|
|
return v - mod * (float)Math.Floor(v / mod);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static double Fmod(double v, double mod)
|
|
{
|
|
return v - mod * Math.Floor(v / mod);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static float NormalizeAndClamp(float value, float min, float max)
|
|
{
|
|
return MathF.Max(0, MathF.Min(1,(value - min) / (max - min)));
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static double NormalizeAndClamp(double value, double min, double max)
|
|
{
|
|
return Math.Max(0, Math.Min(1,(value - min) / (max - min)));
|
|
}
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static float RemapAndClamp(this float value, float inMin, float inMax, float outMin, float outMax)
|
|
{
|
|
var factor = (value - inMin) / (inMax - inMin);
|
|
var v = factor * (outMax - outMin) + outMin;
|
|
if (outMin > outMax)
|
|
Utilities.Swap(ref outMin, ref outMax);
|
|
return v.Clamp(outMin, outMax);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static float Remap(this float value, float inMin, float inMax, float outMin, float outMax)
|
|
{
|
|
var factor = (value - inMin) / (inMax - inMin);
|
|
var v = factor * (outMax - outMin) + outMin;
|
|
return v;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static double Remap(this double value, double inMin, double inMax, double outMin, double outMax)
|
|
{
|
|
var factor = (value - inMin) / (inMax - inMin);
|
|
var v = factor * (outMax - outMin) + outMin;
|
|
return v;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static double RemapAndClamp(double value, double inMin, double inMax, double outMin, double outMax)
|
|
{
|
|
var factor = (value - inMin) / (inMax - inMin);
|
|
var v = factor * (outMax - outMin) + outMin;
|
|
if (v > outMax)
|
|
{
|
|
v = outMax;
|
|
}
|
|
else if (v < outMin)
|
|
{
|
|
v = outMin;
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
public static Vector2 Min(Vector2 lhs, Vector2 rhs)
|
|
{
|
|
return new Vector2(lhs.X < rhs.X ? lhs.X : rhs.X, lhs.Y < rhs.Y ? lhs.Y : rhs.Y);
|
|
}
|
|
|
|
public static Vector2 Floor(this Vector2 v)
|
|
{
|
|
return new Vector2((float)Math.Floor(v.X), (float)Math.Floor(v.Y));
|
|
}
|
|
|
|
public static Vector2 Max(Vector2 lhs, Vector2 rhs)
|
|
{
|
|
return new Vector2(lhs.X >= rhs.X ? lhs.X : rhs.X, lhs.Y >= rhs.Y ? lhs.Y : rhs.Y);
|
|
}
|
|
|
|
public static Vector2 Lerp(Vector2 a, Vector2 b, float t)
|
|
{
|
|
return new Vector2(a.X + (b.X - a.X) * t, a.Y + (b.Y - a.Y) * t);
|
|
}
|
|
|
|
public static Vector3 Lerp(Vector3 a, Vector3 b, float t)
|
|
{
|
|
return new Vector3(a.X + (b.X - a.X) * t,
|
|
a.Y + (b.Y - a.Y) * t,
|
|
a.Z + (b.Z - a.Z) * t);
|
|
}
|
|
|
|
public static Vector4 Lerp(Vector4 a, Vector4 b, float t)
|
|
{
|
|
return new Vector4(a.X + (b.X - a.X) * t,
|
|
a.Y + (b.Y - a.Y) * t,
|
|
a.Z + (b.Z - a.Z) * t,
|
|
a.W + (b.W - a.W) * t);
|
|
}
|
|
|
|
public static double Lerp(double a, double b, double t)
|
|
{
|
|
return a + (b - a) * t;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static int Lerp(int a, int b, float t)
|
|
{
|
|
return (int)(a + (b - a) * t);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Slowly lerps the parameter in place towards the target.
|
|
/// </summary>
|
|
/// <returns>Also returns the new parameter for convenience.</returns>
|
|
public static float DampTowards(this ref float value, float target, float damping = 0.9f)
|
|
{
|
|
value = Lerp( target,value, damping);
|
|
if (!value._IsFinite())
|
|
{
|
|
value = 0;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static double Log2(double value)
|
|
{
|
|
return Math.Log10(value) / Math.Log10(2.0);
|
|
}
|
|
|
|
public static float RoundValue(float i, float stepsPerUnit, float stepRatio)
|
|
{
|
|
float u = 1 / stepsPerUnit;
|
|
float v = stepRatio / (2 * stepsPerUnit);
|
|
float m = i % u;
|
|
float r = m - (m < v
|
|
? 0
|
|
: (m > (u - v))
|
|
? u
|
|
: ((m - v) / (1 - 2 * stepsPerUnit * v)));
|
|
float y = i - r;
|
|
return y;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Smooth damps a value with a "critically damped spring" similar to unity's SmoothDamp helper method.
|
|
/// See https://stackoverflow.com/a/5100956
|
|
/// </summary>
|
|
public static float SpringDamp(float target,
|
|
float current,
|
|
ref float velocity,
|
|
float springConstant = 2,
|
|
float timeStep = 1 / 60f)
|
|
{
|
|
//const float springConstant = 0.41f;
|
|
var currentToTarget = target - current;
|
|
var springForce = currentToTarget * springConstant;
|
|
var dampingForce = -velocity * 2 * MathF.Sqrt(springConstant);
|
|
var force = springForce + dampingForce;
|
|
velocity += force * timeStep;
|
|
var displacement = velocity * timeStep;
|
|
return current + displacement;
|
|
}
|
|
|
|
public const float Pi2 = (float)Math.PI * 2;
|
|
|
|
public static Vector3 ToVector3(this Vector4 vec)
|
|
{
|
|
return new Vector3(vec.X / vec.W, vec.Y / vec.W, vec.Z / vec.W);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return true if a boolean changed
|
|
/// </summary>
|
|
public static bool WasChanged(bool newState, ref bool current)
|
|
{
|
|
if (newState == current)
|
|
return false;
|
|
|
|
current = newState;
|
|
return true;
|
|
}
|
|
|
|
public static bool HasChanged(ref int counter, int newCounter)
|
|
{
|
|
if (counter == newCounter)
|
|
return false;
|
|
|
|
counter = newCounter;
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return true if a boolean changed from false to true
|
|
/// </summary>
|
|
public static bool WasTriggered(bool newState, ref bool current)
|
|
{
|
|
if (newState == current)
|
|
return false;
|
|
|
|
current = newState;
|
|
return newState;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return true if a boolean changed from false to true
|
|
/// </summary>
|
|
public static bool WasReleased(bool newState, ref bool current)
|
|
{
|
|
if (newState == current)
|
|
return false;
|
|
|
|
current = newState;
|
|
return !newState;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks for NaN or Infinity, and sets the float to the provided default value if either.
|
|
/// </summary>
|
|
/// <returns>True if NaN or Infinity</returns>
|
|
public static bool ApplyDefaultIfInvalid(ref float val, float defaultValue)
|
|
{
|
|
var isInvalid = float.IsNaN(val) || float.IsInfinity(val);
|
|
val = isInvalid ? defaultValue : val;
|
|
return isInvalid;
|
|
}
|
|
|
|
public static bool ApplyDefaultIfInvalid(ref Vector2 val, Vector2 defaultValue)
|
|
{
|
|
var isInvalid = float.IsNaN(val.X) || float.IsInfinity(val.X) ||
|
|
float.IsNaN(val.Y) || float.IsInfinity(val.Y);
|
|
val = isInvalid ? defaultValue : val;
|
|
return isInvalid;
|
|
}
|
|
|
|
public static bool ApplyDefaultIfInvalid(ref Vector3 val, Vector3 defaultValue)
|
|
{
|
|
var isInvalid = float.IsNaN(val.X) || float.IsInfinity(val.X) ||
|
|
float.IsNaN(val.Y) || float.IsInfinity(val.Y) ||
|
|
float.IsNaN(val.Z) || float.IsInfinity(val.Z);
|
|
val = isInvalid ? defaultValue : val;
|
|
return isInvalid;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks for NaN or Infinity, and sets the double to the provided default value if either.
|
|
/// </summary>
|
|
/// <returns>True if NaN or Infinity</returns>
|
|
public static bool ApplyDefaultIfInvalid(ref double val, double defaultValue)
|
|
{
|
|
bool isInvalid = double.IsNaN(val) || double.IsInfinity(val);
|
|
val = isInvalid ? defaultValue : val;
|
|
return isInvalid;
|
|
}
|
|
|
|
public static Quaternion RotationFromTwoPositions(Vector3 p1, Vector3 p2)
|
|
{
|
|
return Quaternion.CreateFromAxisAngle(new Vector3(0, 0, 1), (float)(Math.Atan2(p1.X - p2.X, -(p1.Y - p2.Y)) + Math.PI / 2));
|
|
}
|
|
|
|
public static float MaxComponent(this Vector4 vector4)
|
|
{
|
|
return MathF.Max(
|
|
MathF.Max(vector4.X, vector4.Y),
|
|
MathF.Max(vector4.Z, vector4.W));
|
|
}
|
|
|
|
|
|
public static bool HasHdrRange(Vector4 color, out float intensity)
|
|
{
|
|
var maxColorBrightness = color.MaxComponent();
|
|
if (maxColorBrightness > 1)
|
|
{
|
|
intensity= 1f - MathF.Pow(1.5f, -maxColorBrightness);
|
|
return true;
|
|
}
|
|
|
|
intensity = 0;
|
|
return false;
|
|
}
|
|
|
|
public static Quaternion LookAt(Vector3 forward, Vector3 up)
|
|
{
|
|
var right = Vector3.Normalize(Vector3.Cross(forward, up));
|
|
up = Vector3.Normalize(Vector3.Cross(forward, right));
|
|
|
|
float m00 = right.X;
|
|
float m01 = right.Y;
|
|
float m02 = right.Z;
|
|
float m10 = up.X;
|
|
float m11 = up.Y;
|
|
float m12 = up.Z;
|
|
float m20 = forward.X;
|
|
float m21 = forward.Y;
|
|
float m22 = forward.Z;
|
|
|
|
float num8 = (m00 + m11) + m22;
|
|
Quaternion q = Quaternion.Identity;
|
|
if (num8 > 0.0)
|
|
{
|
|
float num = MathF.Sqrt(num8 + 1.0f);
|
|
q.W = num * 0.5f;
|
|
num = 0.5f / num;
|
|
q.X = (m12 - m21) * num;
|
|
q.Y = (m20 - m02) * num;
|
|
q.Z = (m01 - m10) * num;
|
|
return q;
|
|
}
|
|
|
|
if ((m00 >= m11) && (m00 >= m22))
|
|
{
|
|
float num7 = MathF.Sqrt(((1.0f + m00) - m11) - m22);
|
|
float num4 = 0.5f / num7;
|
|
q.X = 0.5f * num7;
|
|
q.Y = (m01 + m10) * num4;
|
|
q.Z = (m02 + m20) * num4;
|
|
q.W = (m12 - m21) * num4;
|
|
return q;
|
|
}
|
|
|
|
if (m11 > m22)
|
|
{
|
|
float num6 = MathF.Sqrt(((1.0f + m11) - m00) - m22);
|
|
float num3 = 0.5f / num6;
|
|
q.X = (m10 + m01) * num3;
|
|
q.Y = 0.5f * num6;
|
|
q.Z = (m21 + m12) * num3;
|
|
q.W = (m20 - m02) * num3;
|
|
return q;
|
|
}
|
|
|
|
float num5 = MathF.Sqrt(((1.0f + m22) - m00) - m11);
|
|
float num2 = 0.5f / num5;
|
|
q.X = (m20 + m02) * num2;
|
|
q.Y = (m21 + m12) * num2;
|
|
q.Z = 0.5f * num5;
|
|
q.W = (m01 - m10) * num2;
|
|
return q;
|
|
}
|
|
}
|
|
|
|
public static class EaseFunctions
|
|
{
|
|
public static float EaseOutElastic(float x)
|
|
{
|
|
const float c4 = (float)(2 * Math.PI) / 3;
|
|
|
|
return x <= 0f
|
|
? 0f
|
|
: x >= 1f
|
|
? 1f
|
|
: (float)(Math.Pow(2, -10 * x) * Math.Sin((x * 10 - 0.75) * c4) + 1);
|
|
}
|
|
}
|
|
|
|
public static class DampFunctions
|
|
{
|
|
public enum Methods
|
|
{
|
|
LinearInterpolation,
|
|
DampedSpring
|
|
}
|
|
|
|
public static float DampenFloat(float inputValue, float previousValue, float damping, ref float velocity, Methods method)
|
|
{
|
|
return method switch
|
|
{
|
|
Methods.LinearInterpolation => LinearDamp(inputValue, previousValue, damping),
|
|
Methods.DampedSpring => SpringDampFloat(inputValue, previousValue, damping, ref velocity),
|
|
_ => inputValue
|
|
};
|
|
}
|
|
|
|
public static float SpringDampFloat(float inputValue, float previousValue, float damping, ref float velocity)
|
|
{
|
|
return MathUtils.SpringDamp(inputValue, previousValue, ref velocity, 0.5f / (damping + 0.001f), (float)(Playback.LastFrameDuration).Clamp(0, 1 / 60f));
|
|
}
|
|
|
|
private static float LinearDamp(float targetValue, float currentValue, float damping)
|
|
{
|
|
// TODO: Fix damping factor from framerate
|
|
return MathUtils.Lerp(targetValue, currentValue, damping);
|
|
}
|
|
|
|
public static Vector2 SpringDampVec2(Vector2 targetVec, Vector2 currentValue, float damping, ref Vector2 velocity)
|
|
{
|
|
var dt = (float)(Playback.LastFrameDuration).Clamp(0, 1 / 60f);
|
|
return new Vector2(
|
|
MathUtils.SpringDamp(targetVec.X, currentValue.X, ref velocity.X, 0.5f / (damping + 0.001f), dt),
|
|
MathUtils.SpringDamp(targetVec.Y, currentValue.Y, ref velocity.Y, 0.5f / (damping + 0.001f), dt));
|
|
}
|
|
|
|
public static Vector3 SpringDampVec3(Vector3 targetVec, Vector3 currentValue, float damping, ref Vector3 velocity)
|
|
{
|
|
var dt = (float)(Playback.LastFrameDuration).Clamp(0, 1 / 60f);
|
|
return new Vector3(
|
|
MathUtils.SpringDamp(targetVec.X, currentValue.X, ref velocity.X, 0.5f / (damping + 0.001f), dt),
|
|
MathUtils.SpringDamp(targetVec.Y, currentValue.Y, ref velocity.Y, 0.5f / (damping + 0.001f), dt),
|
|
MathUtils.SpringDamp(targetVec.Z, currentValue.Z, ref velocity.Z, 0.5f / (damping + 0.001f), dt));
|
|
}
|
|
|
|
|
|
} |