Merge branch 'easings' into 'next'

Easing functions

See merge request STJr/SRB2!1467
This commit is contained in:
Nev3r 2021-05-31 02:34:02 -04:00
commit a48af30465
5 changed files with 651 additions and 2 deletions

View file

@ -32,6 +32,7 @@ set(SRB2_CORE_SOURCES
m_bbox.c
m_cheat.c
m_cond.c
m_easing.c
m_fixed.c
m_menu.c
m_misc.c
@ -102,6 +103,7 @@ set(SRB2_CORE_HEADERS
m_cheat.h
m_cond.h
m_dllist.h
m_easing.h
m_fixed.h
m_menu.h
m_misc.h

View file

@ -502,6 +502,7 @@ OBJS:=$(i_main_o) \
$(OBJDIR)/m_bbox.o \
$(OBJDIR)/m_cheat.o \
$(OBJDIR)/m_cond.o \
$(OBJDIR)/m_easing.o \
$(OBJDIR)/m_fixed.o \
$(OBJDIR)/m_menu.o \
$(OBJDIR)/m_misc.o \

View file

@ -16,6 +16,7 @@
#include "p_local.h"
#include "doomstat.h" // for ALL7EMERALDS
#include "r_main.h" // for R_PointToDist2
#include "m_easing.h"
#include "lua_script.h"
#include "lua_libs.h"
@ -191,7 +192,7 @@ static int lib_coloropposite(lua_State *L)
return 2;
}
static luaL_Reg lib[] = {
static luaL_Reg lib_math[] = {
{"abs", lib_abs},
{"min", lib_min},
{"max", lib_max},
@ -230,9 +231,123 @@ static luaL_Reg lib[] = {
{NULL, NULL}
};
//
// Easing functions
//
#define EASINGFUNC(easetype) \
{ \
fixed_t start = 0; \
fixed_t end = FRACUNIT; \
fixed_t t = luaL_checkfixed(L, 1); \
int n = lua_gettop(L); \
if (n == 2) \
end = luaL_checkfixed(L, 2); \
else if (n >= 3) \
{ \
start = luaL_checkfixed(L, 2); \
end = luaL_checkfixed(L, 3); \
} \
lua_pushfixed(L, (Easing_ ## easetype)(t, start, end)); \
return 1; \
} \
static int lib_easelinear(lua_State *L) { EASINGFUNC(Linear) }
static int lib_easeinsine(lua_State *L) { EASINGFUNC(InSine) }
static int lib_easeoutsine(lua_State *L) { EASINGFUNC(OutSine) }
static int lib_easeinoutsine(lua_State *L) { EASINGFUNC(InOutSine) }
static int lib_easeinquad(lua_State *L) { EASINGFUNC(InQuad) }
static int lib_easeoutquad(lua_State *L) { EASINGFUNC(OutQuad) }
static int lib_easeinoutquad(lua_State *L) { EASINGFUNC(InOutQuad) }
static int lib_easeincubic(lua_State *L) { EASINGFUNC(InCubic) }
static int lib_easeoutcubic(lua_State *L) { EASINGFUNC(OutCubic) }
static int lib_easeinoutcubic(lua_State *L) { EASINGFUNC(InOutCubic) }
static int lib_easeinquart(lua_State *L) { EASINGFUNC(InQuart) }
static int lib_easeoutquart(lua_State *L) { EASINGFUNC(OutQuart) }
static int lib_easeinoutquart(lua_State *L) { EASINGFUNC(InOutQuart) }
static int lib_easeinquint(lua_State *L) { EASINGFUNC(InQuint) }
static int lib_easeoutquint(lua_State *L) { EASINGFUNC(OutQuint) }
static int lib_easeinoutquint(lua_State *L) { EASINGFUNC(InOutQuint) }
static int lib_easeinexpo(lua_State *L) { EASINGFUNC(InExpo) }
static int lib_easeoutexpo(lua_State *L) { EASINGFUNC(OutExpo) }
static int lib_easeinoutexpo(lua_State *L) { EASINGFUNC(InOutExpo) }
#undef EASINGFUNC
#define EASINGFUNC(easetype) \
{ \
boolean useparam = false; \
fixed_t param = 0; \
fixed_t start = 0; \
fixed_t end = FRACUNIT; \
fixed_t t = luaL_checkfixed(L, 1); \
int n = lua_gettop(L); \
if (n == 2) \
end = luaL_checkfixed(L, 2); \
else if (n >= 3) \
{ \
start = (fixed_t)luaL_optinteger(L, 2, start); \
end = (fixed_t)luaL_optinteger(L, 3, end); \
if ((n >= 4) && (useparam = (!lua_isnil(L, 4)))) \
param = luaL_checkfixed(L, 4); \
} \
if (useparam) \
lua_pushfixed(L, (Easing_ ## easetype ## Parameterized)(t, start, end, param)); \
else \
lua_pushfixed(L, (Easing_ ## easetype)(t, start, end)); \
return 1; \
} \
static int lib_easeinback(lua_State *L) { EASINGFUNC(InBack) }
static int lib_easeoutback(lua_State *L) { EASINGFUNC(OutBack) }
static int lib_easeinoutback(lua_State *L) { EASINGFUNC(InOutBack) }
#undef EASINGFUNC
static luaL_Reg lib_ease[] = {
{"linear", lib_easelinear},
{"insine", lib_easeinsine},
{"outsine", lib_easeoutsine},
{"inoutsine", lib_easeinoutsine},
{"inquad", lib_easeinquad},
{"outquad", lib_easeoutquad},
{"inoutquad", lib_easeinoutquad},
{"incubic", lib_easeincubic},
{"outcubic", lib_easeoutcubic},
{"inoutcubic", lib_easeinoutcubic},
{"inquart", lib_easeinquart},
{"outquart", lib_easeoutquart},
{"inoutquart", lib_easeinoutquart},
{"inquint", lib_easeinquint},
{"outquint", lib_easeoutquint},
{"inoutquint", lib_easeinoutquint},
{"inexpo", lib_easeinexpo},
{"outexpo", lib_easeoutexpo},
{"inoutexpo", lib_easeinoutexpo},
{"inback", lib_easeinback},
{"outback", lib_easeoutback},
{"inoutback", lib_easeinoutback},
{NULL, NULL}
};
int LUA_MathLib(lua_State *L)
{
lua_pushvalue(L, LUA_GLOBALSINDEX);
luaL_register(L, NULL, lib);
luaL_register(L, NULL, lib_math);
luaL_register(L, "ease", lib_ease);
return 0;
}

430
src/m_easing.c Normal file
View file

@ -0,0 +1,430 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2020-2021 by Jaime "Lactozilla" Passos.
//
// 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

101
src/m_easing.h Normal file
View file

@ -0,0 +1,101 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2020-2021 by Jaime "Lactozilla" Passos.
//
// 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.h
/// \brief Easing functions
#ifndef __M_EASING_H__
#define __M_EASING_H__
#include "doomtype.h"
#include "m_fixed.h"
typedef enum
{
EASE_LINEAR = 0,
EASE_INSINE,
EASE_OUTSINE,
EASE_INOUTSINE,
EASE_INQUAD,
EASE_OUTQUAD,
EASE_INOUTQUAD,
EASE_INCUBIC,
EASE_OUTCUBIC,
EASE_INOUTCUBIC,
EASE_INQUART,
EASE_OUTQUART,
EASE_INOUTQUART,
EASE_INQUINT,
EASE_OUTQUINT,
EASE_INOUTQUINT,
EASE_INEXPO,
EASE_OUTEXPO,
EASE_INOUTEXPO,
EASE_INBACK,
EASE_OUTBACK,
EASE_INOUTBACK,
EASE_MAX,
} easing_t;
typedef fixed_t (*easingfunc_t)(fixed_t, fixed_t, fixed_t);
extern easingfunc_t easing_funclist[EASE_MAX];
extern const char *easing_funcnames[EASE_MAX];
#define EASINGFUNCLIST(sep) \
EASINGFUNC(Linear) sep /* Easing_Linear */ \
\
EASINGFUNC(InSine) sep /* Easing_InSine */ \
EASINGFUNC(OutSine) sep /* Easing_OutSine */ \
EASINGFUNC(InOutSine) sep /* Easing_InOutSine */ \
\
EASINGFUNC(InQuad) sep /* Easing_InQuad */ \
EASINGFUNC(OutQuad) sep /* Easing_OutQuad */ \
EASINGFUNC(InOutQuad) sep /* Easing_InOutQuad */ \
\
EASINGFUNC(InCubic) sep /* Easing_InCubic */ \
EASINGFUNC(OutCubic) sep /* Easing_OutCubic */ \
EASINGFUNC(InOutCubic) sep /* Easing_InOutCubic */ \
\
EASINGFUNC(InQuart) sep /* Easing_InQuart */ \
EASINGFUNC(OutQuart) sep /* Easing_OutQuart */ \
EASINGFUNC(InOutQuart) sep /* Easing_InOutQuart */ \
\
EASINGFUNC(InQuint) sep /* Easing_InQuint */ \
EASINGFUNC(OutQuint) sep /* Easing_OutQuint */ \
EASINGFUNC(InOutQuint) sep /* Easing_InOutQuint */ \
\
EASINGFUNC(InExpo) sep /* Easing_InExpo */ \
EASINGFUNC(OutExpo) sep /* Easing_OutExpo */ \
EASINGFUNC(InOutExpo) sep /* Easing_InOutExpo */ \
\
EASINGFUNC(InBack) sep /* Easing_InBack */ \
EASINGFUNC(OutBack) sep /* Easing_OutBack */ \
EASINGFUNC(InOutBack) sep /* Easing_InOutBack */
#define EASINGFUNC(type) fixed_t Easing_ ## type (fixed_t t, fixed_t start, fixed_t end);
EASINGFUNCLIST()
#undef EASINGFUNC
#define EASINGFUNC(type) fixed_t Easing_ ## type (fixed_t t, fixed_t start, fixed_t end, fixed_t param);
EASINGFUNC(InBackParameterized) /* Easing_InBackParameterized */
EASINGFUNC(OutBackParameterized) /* Easing_OutBackParameterized */
EASINGFUNC(InOutBackParameterized) /* Easing_InOutBackParameterized */
#undef EASINGFUNC
#endif