mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
522 lines
11 KiB
C
522 lines
11 KiB
C
// DR. ROBOTNIK'S RING RACERS
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 2025 by Kart Krew.
|
|
// Copyright (C) 2021 by Jaime "Lactozilla" Passos.
|
|
// Copyright (C) 2021 by Sonic Team Junior.
|
|
//
|
|
// This program is free software distributed under the
|
|
// terms of the GNU General Public License, version 2.
|
|
// See the 'LICENSE' file for more details.
|
|
//-----------------------------------------------------------------------------
|
|
/// \file m_easing.c
|
|
/// \brief Easing functions
|
|
/// Referenced from https://easings.net/
|
|
|
|
#include "m_easing.h"
|
|
#include "tables.h"
|
|
#include "doomdef.h"
|
|
|
|
/*
|
|
For the computation of the logarithm, we choose, by trial and error, from among
|
|
a sequence of particular factors those, that when multiplied with the function
|
|
argument, normalize it to unity. For every factor chosen, we add up the
|
|
corresponding logarithm value stored in a table. The sum then corresponds to
|
|
the logarithm of the function argument.
|
|
|
|
For the integer portion, we would want to choose
|
|
2^i, i = 1, 2, 4, 8, ...
|
|
and for the factional part we choose
|
|
1+2^-i, i = 1, 2, 3, 4, 5 ...
|
|
|
|
The algorithm for the exponential is closely related and quite literally the inverse
|
|
of the logarithm algorithm. From among the sequence of tabulated logarithms for our
|
|
chosen factors, we pick those that when subtracted from the function argument ultimately
|
|
reduce it to zero. Starting with unity, we multiply with all the factors whose logarithms
|
|
we have subtracted in the process. The resulting product corresponds to the result of the exponentiation.
|
|
|
|
Logarithms of values greater than unity can be computed by applying the algorithm to the reciprocal
|
|
of the function argument (with the negation of the result as appropriate), likewise exponentiation with
|
|
negative function arguments requires us negate the function argument and compute the reciprocal at the end.
|
|
*/
|
|
|
|
static fixed_t logtabdec[FRACBITS] =
|
|
{
|
|
0x95c1, 0x526a, 0x2b80, 0x1663,
|
|
0xb5d, 0x5b9, 0x2e0, 0x170,
|
|
0xb8, 0x5c, 0x2e, 0x17,
|
|
0x0b, 0x06, 0x03, 0x01
|
|
};
|
|
|
|
static fixed_t fixlog2(fixed_t a)
|
|
{
|
|
UINT32 x = a, y = 0;
|
|
INT32 t, i, shift = 8;
|
|
|
|
if (x > FRACUNIT)
|
|
x = FixedDiv(FRACUNIT, x);
|
|
|
|
// Integer part
|
|
// 1<<19 = 0x80000
|
|
// 1<<18 = 0x40000
|
|
// 1<<17 = 0x20000
|
|
// 1<<16 = 0x10000
|
|
|
|
#define dologtab(i) \
|
|
t = (x << shift); \
|
|
if (t < FRACUNIT) \
|
|
{ \
|
|
x = t; \
|
|
y += (1 << (19 - i)); \
|
|
} \
|
|
shift /= 2;
|
|
|
|
dologtab(0)
|
|
dologtab(1)
|
|
dologtab(2)
|
|
dologtab(3)
|
|
|
|
#undef dologtab
|
|
|
|
// Decimal part
|
|
for (i = 0; i < FRACBITS; i++)
|
|
{
|
|
t = x + (x >> (i + 1));
|
|
if (t < FRACUNIT)
|
|
{
|
|
x = t;
|
|
y += logtabdec[i];
|
|
}
|
|
}
|
|
|
|
if (a <= FRACUNIT)
|
|
return -y;
|
|
|
|
return y;
|
|
}
|
|
|
|
// Notice how this is symmetric to fixlog2.
|
|
static INT32 fixexp(fixed_t a)
|
|
{
|
|
UINT32 x, y;
|
|
fixed_t t, i, shift = 8;
|
|
|
|
// Underflow prevention.
|
|
if (a <= -15 * FRACUNIT)
|
|
return 0;
|
|
|
|
x = (a < 0) ? (-a) : (a);
|
|
y = FRACUNIT;
|
|
|
|
// Integer part (see fixlog2)
|
|
#define dologtab(i) \
|
|
t = x - (1 << (19 - i)); \
|
|
if (t >= 0) \
|
|
{ \
|
|
x = t; \
|
|
y <<= shift; \
|
|
} \
|
|
shift /= 2;
|
|
|
|
dologtab(0)
|
|
dologtab(1)
|
|
dologtab(2)
|
|
dologtab(3)
|
|
|
|
#undef dologtab
|
|
|
|
// Decimal part
|
|
for (i = 0; i < FRACBITS; i++)
|
|
{
|
|
t = (x - logtabdec[i]);
|
|
if (t >= 0)
|
|
{
|
|
x = t;
|
|
y += (y >> (i + 1));
|
|
}
|
|
}
|
|
|
|
if (a < 0)
|
|
return FixedDiv(FRACUNIT, y);
|
|
|
|
return y;
|
|
}
|
|
|
|
#define fixpow(x, y) fixexp(FixedMul((y), fixlog2(x)))
|
|
#define fixintmul(x, y) FixedMul((x) * FRACUNIT, y)
|
|
#define fixintdiv(x, y) FixedDiv(x, (y) * FRACUNIT)
|
|
#define fixinterp(start, end, t) FixedMul((FRACUNIT - (t)), start) + FixedMul(t, end)
|
|
|
|
// ==================
|
|
// EASING FUNCTIONS
|
|
// ==================
|
|
|
|
#define EASINGFUNC(type) fixed_t Easing_ ## type (fixed_t t, fixed_t start, fixed_t end)
|
|
|
|
//
|
|
// Linear
|
|
//
|
|
|
|
EASINGFUNC(Linear)
|
|
{
|
|
return fixinterp(start, end, t);
|
|
}
|
|
|
|
//
|
|
// Sine
|
|
//
|
|
|
|
// This is equivalent to calculating (x * pi) and converting the result from radians into degrees.
|
|
#define fixang(x) FixedMul((x), 180*FRACUNIT)
|
|
|
|
EASINGFUNC(InSine)
|
|
{
|
|
fixed_t c = fixang(t / 2);
|
|
fixed_t x = FRACUNIT - FINECOSINE(FixedAngle(c)>>ANGLETOFINESHIFT);
|
|
return fixinterp(start, end, x);
|
|
}
|
|
|
|
EASINGFUNC(OutSine)
|
|
{
|
|
fixed_t c = fixang(t / 2);
|
|
fixed_t x = FINESINE(FixedAngle(c)>>ANGLETOFINESHIFT);
|
|
return fixinterp(start, end, x);
|
|
}
|
|
|
|
EASINGFUNC(InOutSine)
|
|
{
|
|
fixed_t c = fixang(t);
|
|
fixed_t x = -(FINECOSINE(FixedAngle(c)>>ANGLETOFINESHIFT) - FRACUNIT) / 2;
|
|
return fixinterp(start, end, x);
|
|
}
|
|
|
|
#undef fixang
|
|
|
|
//
|
|
// Quad
|
|
//
|
|
|
|
EASINGFUNC(InQuad)
|
|
{
|
|
return fixinterp(start, end, FixedMul(t, t));
|
|
}
|
|
|
|
EASINGFUNC(OutQuad)
|
|
{
|
|
return fixinterp(start, end, FRACUNIT - FixedMul(FRACUNIT - t, FRACUNIT - t));
|
|
}
|
|
|
|
EASINGFUNC(InOutQuad)
|
|
{
|
|
fixed_t x = t < (FRACUNIT/2)
|
|
? fixintmul(2, FixedMul(t, t))
|
|
: FRACUNIT - fixpow(FixedMul(-2*FRACUNIT, t) + 2*FRACUNIT, 2*FRACUNIT) / 2;
|
|
return fixinterp(start, end, x);
|
|
}
|
|
|
|
//
|
|
// Cubic
|
|
//
|
|
|
|
EASINGFUNC(InCubic)
|
|
{
|
|
fixed_t x = FixedMul(t, FixedMul(t, t));
|
|
return fixinterp(start, end, x);
|
|
}
|
|
|
|
EASINGFUNC(OutCubic)
|
|
{
|
|
return fixinterp(start, end, FRACUNIT - fixpow(FRACUNIT - t, 3*FRACUNIT));
|
|
}
|
|
|
|
EASINGFUNC(InOutCubic)
|
|
{
|
|
fixed_t x = t < (FRACUNIT/2)
|
|
? fixintmul(4, FixedMul(t, FixedMul(t, t)))
|
|
: FRACUNIT - fixpow(fixintmul(-2, t) + 2*FRACUNIT, 3*FRACUNIT) / 2;
|
|
return fixinterp(start, end, x);
|
|
}
|
|
|
|
//
|
|
// "Quart"
|
|
//
|
|
|
|
EASINGFUNC(InQuart)
|
|
{
|
|
fixed_t x = FixedMul(FixedMul(t, t), FixedMul(t, t));
|
|
return fixinterp(start, end, x);
|
|
}
|
|
|
|
EASINGFUNC(OutQuart)
|
|
{
|
|
fixed_t x = FRACUNIT - fixpow(FRACUNIT - t, 4 * FRACUNIT);
|
|
return fixinterp(start, end, x);
|
|
}
|
|
|
|
EASINGFUNC(InOutQuart)
|
|
{
|
|
fixed_t x = t < (FRACUNIT/2)
|
|
? fixintmul(8, FixedMul(FixedMul(t, t), FixedMul(t, t)))
|
|
: FRACUNIT - fixpow(fixintmul(-2, t) + 2*FRACUNIT, 4*FRACUNIT) / 2;
|
|
return fixinterp(start, end, x);
|
|
}
|
|
|
|
//
|
|
// "Quint"
|
|
//
|
|
|
|
EASINGFUNC(InQuint)
|
|
{
|
|
fixed_t x = FixedMul(t, FixedMul(FixedMul(t, t), FixedMul(t, t)));
|
|
return fixinterp(start, end, x);
|
|
}
|
|
|
|
EASINGFUNC(OutQuint)
|
|
{
|
|
fixed_t x = FRACUNIT - fixpow(FRACUNIT - t, 5 * FRACUNIT);
|
|
return fixinterp(start, end, x);
|
|
}
|
|
|
|
EASINGFUNC(InOutQuint)
|
|
{
|
|
fixed_t x = t < (FRACUNIT/2)
|
|
? FixedMul(16*FRACUNIT, FixedMul(t, FixedMul(FixedMul(t, t), FixedMul(t, t))))
|
|
: FRACUNIT - fixpow(fixintmul(-2, t) + 2*FRACUNIT, 5*FRACUNIT) / 2;
|
|
return fixinterp(start, end, x);
|
|
}
|
|
|
|
//
|
|
// Exponential
|
|
//
|
|
|
|
EASINGFUNC(InExpo)
|
|
{
|
|
fixed_t x = (!t) ? 0 : fixpow(2*FRACUNIT, fixintmul(10, t) - 10*FRACUNIT);
|
|
return fixinterp(start, end, x);
|
|
}
|
|
|
|
EASINGFUNC(OutExpo)
|
|
{
|
|
fixed_t x = (t >= FRACUNIT) ? FRACUNIT
|
|
: FRACUNIT - fixpow(2*FRACUNIT, fixintmul(-10, t));
|
|
return fixinterp(start, end, x);
|
|
}
|
|
|
|
EASINGFUNC(InOutExpo)
|
|
{
|
|
fixed_t x;
|
|
|
|
if (!t)
|
|
x = 0;
|
|
else if (t >= FRACUNIT)
|
|
x = FRACUNIT;
|
|
else
|
|
{
|
|
if (t < FRACUNIT / 2)
|
|
{
|
|
x = fixpow(2*FRACUNIT, fixintmul(20, t) - 10*FRACUNIT);
|
|
x = fixintdiv(x, 2);
|
|
}
|
|
else
|
|
{
|
|
x = fixpow(2*FRACUNIT, fixintmul(-20, t) + 10*FRACUNIT);
|
|
x = fixintdiv((2*FRACUNIT) - x, 2);
|
|
}
|
|
}
|
|
|
|
return fixinterp(start, end, x);
|
|
}
|
|
|
|
//
|
|
// "Back"
|
|
//
|
|
|
|
#define EASEBACKCONST1 111514 // 1.70158
|
|
#define EASEBACKCONST2 99942 // 1.525
|
|
|
|
static fixed_t EaseInBack(fixed_t t, fixed_t start, fixed_t end, fixed_t c1)
|
|
{
|
|
const fixed_t c3 = c1 + FRACUNIT;
|
|
fixed_t x = FixedMul(FixedMul(t, t), FixedMul(c3, t) - c1);
|
|
return fixinterp(start, end, x);
|
|
}
|
|
|
|
EASINGFUNC(InBack)
|
|
{
|
|
return EaseInBack(t, start, end, EASEBACKCONST1);
|
|
}
|
|
|
|
static fixed_t EaseOutBack(fixed_t t, fixed_t start, fixed_t end, fixed_t c1)
|
|
{
|
|
const fixed_t c3 = c1 + FRACUNIT;
|
|
fixed_t x;
|
|
t -= FRACUNIT;
|
|
x = FRACUNIT + FixedMul(FixedMul(t, t), FixedMul(c3, t) + c1);
|
|
return fixinterp(start, end, x);
|
|
}
|
|
|
|
EASINGFUNC(OutBack)
|
|
{
|
|
return EaseOutBack(t, start, end, EASEBACKCONST1);
|
|
}
|
|
|
|
static fixed_t EaseInOutBack(fixed_t t, fixed_t start, fixed_t end, fixed_t c2)
|
|
{
|
|
fixed_t x, y;
|
|
const fixed_t f2 = 2*FRACUNIT;
|
|
|
|
if (t < FRACUNIT / 2)
|
|
{
|
|
x = fixpow(FixedMul(t, f2), f2);
|
|
y = FixedMul(c2 + FRACUNIT, FixedMul(t, f2));
|
|
x = FixedMul(x, y - c2);
|
|
}
|
|
else
|
|
{
|
|
x = fixpow(-(FixedMul(t, f2) - f2), f2);
|
|
y = FixedMul(c2 + FRACUNIT, FixedMul(t, f2) - f2);
|
|
x = FixedMul(x, y + c2);
|
|
x += f2;
|
|
}
|
|
|
|
x /= 2;
|
|
|
|
return fixinterp(start, end, x);
|
|
}
|
|
|
|
EASINGFUNC(InOutBack)
|
|
{
|
|
return EaseInOutBack(t, start, end, EASEBACKCONST2);
|
|
}
|
|
|
|
#undef EASINGFUNC
|
|
#define EASINGFUNC(type) fixed_t Easing_ ## type (fixed_t t, fixed_t start, fixed_t end, fixed_t param)
|
|
|
|
EASINGFUNC(InBackParameterized)
|
|
{
|
|
return EaseInBack(t, start, end, param);
|
|
}
|
|
|
|
EASINGFUNC(OutBackParameterized)
|
|
{
|
|
return EaseOutBack(t, start, end, param);
|
|
}
|
|
|
|
EASINGFUNC(InOutBackParameterized)
|
|
{
|
|
return EaseInOutBack(t, start, end, param);
|
|
}
|
|
|
|
#undef EASINGFUNC
|
|
|
|
// Function list
|
|
|
|
#define EASINGFUNC(type) Easing_ ## type
|
|
#define COMMA ,
|
|
|
|
easingfunc_t easing_funclist[EASE_MAX] =
|
|
{
|
|
EASINGFUNCLIST(COMMA)
|
|
};
|
|
|
|
// Function names
|
|
|
|
#undef EASINGFUNC
|
|
#define EASINGFUNC(type) #type
|
|
|
|
const char *easing_funcnames[EASE_MAX] =
|
|
{
|
|
EASINGFUNCLIST(COMMA)
|
|
};
|
|
|
|
#undef COMMA
|
|
#undef EASINGFUNC
|
|
|
|
// ==================
|
|
// FEATURE RESCALING
|
|
// ==================
|
|
|
|
/*--------------------------------------------------
|
|
fixed_t Rescale(fixed_t value, fixed_t inmin, fixed_t inmax, easingfunc_t easing_func, fixed_t outmin, fixed_t outmax)
|
|
|
|
Rescales a feature value from [min, max] to [start, end] using
|
|
a custom easing function pointer.
|
|
|
|
Input Arguments:-
|
|
value - The input value to rescale
|
|
inmin - Minimum value of the input range
|
|
inmax - Maximum value of the input range
|
|
easing_func - Pointer to the easing function to use
|
|
outmin - Start value of the output range
|
|
outmax - End value of the output range
|
|
|
|
Return:-
|
|
The rescaled value using the specified easing function.
|
|
--------------------------------------------------*/
|
|
fixed_t FixedRescale(fixed_t value, fixed_t inmin, fixed_t inmax, easingfunc_t easing_func, fixed_t outmin, fixed_t outmax)
|
|
{
|
|
// Handle edge case where min == max
|
|
if (inmin == inmax)
|
|
return outmin;
|
|
|
|
// Clamp the input value to the range
|
|
max(inmin, min(inmax, value));
|
|
|
|
// Normalize the value to [0, FRACUNIT] range
|
|
fixed_t t = FixedDiv(value - inmin, inmax - inmin);
|
|
|
|
// Apply the easing function if provided
|
|
if (easing_func != NULL)
|
|
{
|
|
return easing_func(t, outmin, outmax);
|
|
}
|
|
|
|
// Fallback to linear if no function provided
|
|
return Easing_Linear(t, outmin, outmax);
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
INT16 IntRescale(INT16 value, INT16 inmin, INT16 inmax, easingfunc_t easing_func, INT16 outmin, INT16 outmax)
|
|
|
|
Rescales a feature value from [min, max] to [start, end] using
|
|
a custom easing function pointer.
|
|
Can only take in up to INT16 because it uses fixed_t internally
|
|
|
|
Input Arguments:-
|
|
value - The input value to rescale
|
|
inmin - Minimum value of the input range
|
|
inmax - Maximum value of the input range
|
|
easing_func - Pointer to the easing function to use
|
|
outmin - Start value of the output range
|
|
outmax - End value of the output range
|
|
|
|
Return:-
|
|
The rescaled value using the specified easing function.
|
|
--------------------------------------------------*/
|
|
INT16 IntRescale(INT16 value, INT16 inmin, INT16 inmax, easingfunc_t easing_func, INT16 outmin, INT16 outmax)
|
|
{
|
|
// Handle edge case where min == max
|
|
if (inmin == inmax)
|
|
return outmin;
|
|
|
|
// Clamp the input value to the range
|
|
max(inmin, min(inmax, value));
|
|
|
|
// Conversion shit
|
|
value = value<<FRACBITS;
|
|
inmin = inmin<<FRACBITS;
|
|
inmax = inmax<<FRACBITS;
|
|
outmin = outmin<<FRACBITS;
|
|
outmax = outmax<<FRACBITS;
|
|
|
|
// Normalize the value to [0, FRACUNIT] range
|
|
fixed_t t = FixedDiv(value - inmin, inmax - inmin);
|
|
|
|
// Apply the easing function if provided
|
|
if (easing_func != NULL)
|
|
{
|
|
return easing_func(t, outmin, outmax)>>FRACBITS;
|
|
}
|
|
|
|
// Fallback to linear if no function provided
|
|
return Easing_Linear(t, outmin, outmax)>>FRACBITS;
|
|
}
|