From e690007190dcac29577eeda60a5f38351f2eab31 Mon Sep 17 00:00:00 2001 From: wgroeneveld Date: Fri, 14 Dec 2018 21:11:28 +0100 Subject: [PATCH] refactoring math suite into GBAVector, added tests for point rotation, stub for sin/cos --- demos/demo3-foodthrowing/src/bullet.cpp | 6 +- demos/demo3-foodthrowing/src/bullet.h | 4 + demos/demo3-foodthrowing/src/food_scene.cpp | 13 +- engine/CMakeLists.txt | 3 +- .../libgba-sprite-engine/gba/tonc_math_stub.h | 702 ++++++++++++++++++ .../libgba-sprite-engine/gba/tonc_oam.h | 5 + .../include/libgba-sprite-engine/gbavector.h | 33 + engine/include/libgba-sprite-engine/math.h | 16 - .../libgba-sprite-engine/sprites/sprite.h | 6 + engine/src/{math.cpp => gbavector.cpp} | 21 +- test/CMakeLists.txt | 5 +- test/{mathtest.cpp => gbavectortest.cpp} | 62 +- test/palettetest.cpp | 2 +- 13 files changed, 826 insertions(+), 52 deletions(-) create mode 100644 engine/include/libgba-sprite-engine/gba/tonc_math_stub.h create mode 100644 engine/include/libgba-sprite-engine/gbavector.h delete mode 100644 engine/include/libgba-sprite-engine/math.h rename engine/src/{math.cpp => gbavector.cpp} (66%) rename test/{mathtest.cpp => gbavectortest.cpp} (53%) diff --git a/demos/demo3-foodthrowing/src/bullet.cpp b/demos/demo3-foodthrowing/src/bullet.cpp index efbc15a..34f701c 100644 --- a/demos/demo3-foodthrowing/src/bullet.cpp +++ b/demos/demo3-foodthrowing/src/bullet.cpp @@ -5,15 +5,15 @@ #include #include #include -#include +#include #include "bullet.h" void Bullet::setDestination(VECTOR destination) { - auto currentPos = sprite->getPos(); + auto currentPos = sprite->getPosAsVector(); this->dest = destination; - this->coords = Math::bresenhamLineBetween(currentPos, destination); + this->coords = currentPos.bresenhamLineTo(destination); } void Bullet::tick() { diff --git a/demos/demo3-foodthrowing/src/bullet.h b/demos/demo3-foodthrowing/src/bullet.h index e9b3f17..cdc4c85 100644 --- a/demos/demo3-foodthrowing/src/bullet.h +++ b/demos/demo3-foodthrowing/src/bullet.h @@ -7,7 +7,11 @@ #include +#ifdef CODE_COMPILED_AS_PART_OF_TEST +#include +#else #include +#endif #include class Bullet { diff --git a/demos/demo3-foodthrowing/src/food_scene.cpp b/demos/demo3-foodthrowing/src/food_scene.cpp index f37e8dd..a1b1b13 100644 --- a/demos/demo3-foodthrowing/src/food_scene.cpp +++ b/demos/demo3-foodthrowing/src/food_scene.cpp @@ -42,18 +42,7 @@ void FoodScene::removeBulletsOffScreen() { } VECTOR FoodScene::rotateAround(VECTOR center, VECTOR point) { - s32 centerx = center.x, centery = center.y; - s32 defaultx = point.x, defaulty = point.y; - - s32 cos = lu_cos(avatarRotation) >> 4; - s32 sin = lu_sin(avatarRotation) >> 4; - - // affine matriches are 8.8 fixed point numbers, so shift all input 8 spaces up and forth - // possibilities: instead of between [-1.0, 1.0] it's between [-256, +256] - // 90° rotation in inversed y-axis needs to flip sin sign - return { - ( cos * (defaultx - centerx) + sin * (defaulty - centery) + (centerx << 8)) >> 8, - (-sin * (defaultx - centerx) + cos * (defaulty - centery) + (centery << 8)) >> 8}; + return GBAVector(center).rotateAsCenter(point, avatarRotation); } void FoodScene::tick(u16 keys) { diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 597e713..8a69dce 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -1,6 +1,5 @@ project(gba-sprite-engine) set_property(SOURCE src/gba/sin_lut.s PROPERTY LANGUAGE C) - set_property(SOURCE src/gba/tonc_bios.s PROPERTY LANGUAGE C) set_source_files_properties(src/gba/tonc_bios.s PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp") @@ -18,7 +17,7 @@ add_library(${PROJECT_NAME} src/background/text_stream.cpp src/background/background.cpp src/effects/fade_out_scene.cpp - src/sound_control.cpp src/scene.cpp src/timer.cpp include/libgba-sprite-engine/math.h src/math.cpp) + src/sound_control.cpp src/scene.cpp src/timer.cpp include/libgba-sprite-engine/gbavector.h src/gbavector.cpp) target_include_directories(${PROJECT_NAME} PUBLIC $ diff --git a/engine/include/libgba-sprite-engine/gba/tonc_math_stub.h b/engine/include/libgba-sprite-engine/gba/tonc_math_stub.h new file mode 100644 index 0000000..0e0f85b --- /dev/null +++ b/engine/include/libgba-sprite-engine/gba/tonc_math_stub.h @@ -0,0 +1,702 @@ +// +// Created by Wouter Groeneveld on 14/12/18. +// + +#ifndef GBA_SPRITE_ENGINE_PROJECT_TONC_MATH_STUB_H +#define GBA_SPRITE_ENGINE_PROJECT_TONC_MATH_STUB_H + +// +// Mathematical functions +// +//! \file tonc_math.h +//! \author J Vijn +//! \date 20060508 - 20060908 +// +// === NOTES === + + +#include +#include "tonc_types.h" + +// --- Doxygen modules --- + +/*! \defgroup grpMathBase Base math +* \brief Basic math macros and functions like MIN, MAX +* \ingroup grpMath +*/ + +/*! \defgroup grpMathFixed Fixed point math +* \ingroup grpMath +*/ + +/*! \defgroup grpMathLut Look-up tables +* \brief Tonc's internal look-up tables and related routines. +* \ingroup grpMath +*/ + +/*! \defgroup grpMathPoint Point functions +* \ingroup grpMath +*/ + +/*! \defgroup grpMathVector Vector functions +* \ingroup grpMath +*/ + +/*! \defgroup grpMathRect Rect functions +* \ingroup grpMath +*/ + + +// -------------------------------------------------------------------- +// GENERAL +// -------------------------------------------------------------------- + + +/*! \addtogroup grpMathBase */ +/*! \{ */ + +// Also available as inline functions + +//! \name core math macros +//\{ + +#ifndef ABS +//! Get the absolute value of \a x +#define ABS(x) ( (x)>=0 ? (x) : -(x) ) +#endif // ABS + +#ifndef SGN +//! Get the sign of \a x. +#define SGN(x) ( (x)>=0 ? 1 : -1 ) +#define SGN2 SGN +#endif // SGN + +#ifndef SGN3 +//! Tri-state sign: -1 for negative, 0 for 0, +1 for positive. +#define SGN3(x) ( (x)>0 ? 1 : ( (x)<0 ? -1 : 0) ) +#endif // SGN3 + +#ifndef MAX + +//! Get the maximum of \a a and \a b +#define MAX(a, b) ( ((a) > (b)) ? (a) : (b) ) + +//! Get the minimum of \a a and \a b +#define MIN(a, b) ( ((a) < (b)) ? (a) : (b) ) +#endif // MAX + +#ifndef SWAP +//! In-place swap. +#define SWAP2(a, b) do { a=(a)-(b); b=(a)+(b); a=(b)-(a); } while(0) + +#define SWAP SWAP2 + +//Alternative: +//#define SWAP2(a, b) ( (b) ^= ((a) ^= ((b) ^= (a))) ) + +//! Swaps \a a and \a b, using \a tmp as a temporary +#define SWAP3(a, b, tmp) do { (tmp)=(a); (a)=(b); (b)=(tmp); } while(0) +#endif // SWAP + + +INLINE int sgn(int x); +INLINE int sgn3(int x); +INLINE int max(int a, int b); +INLINE int min(int a, int b); + +//\} + + +//! \name Boundary response macros +//\{ + + +//! Range check +#define IN_RANGE(x, min, max) ( ((x)>=(min)) && ((x)<(max)) ) + +//! Truncates \a x to stay in range [\a min, \a max> +/*! \return Truncated value of \a x. +* \note \a max is exclusive! +*/ +#define CLAMP(x, min, max) \ + ( (x)>=(max) ? ((max)-1) : ( ((x)<(min)) ? (min) : (x) ) ) + +//! Reflects \a x at boundaries \a min and \a max +/*! If \a x is outside the range [\a min, \a max>, +* it'll be placed inside again with the same distance +* to the 'wall', but on the other side. Example for lower +* border: y = \a min - (\a x- \a min) = 2*\a min + \a x. +* \return Reflected value of \a x. +* \note \a max is exclusive! +*/ +#define REFLECT(x, min, max) \ + ( (x)>=(max) ? 2*((max)-1)-(x) : ( ((x)<(min)) ? 2*(min)-(x) : (x) ) ) + +//! Wraps \a x to stay in range [\a min, \a max> +#define WRAP(x, min, max) \ + ( (x)>=(max) ? (x)+(min)-(max) : ( ((x)<(min)) ? (x)+(max)-(min) : (x) ) ) + + +INLINE BOOL in_range(int x, int min, int max); +INLINE int clamp(int x, int min, int max); +INLINE int reflect(int x, int min, int max); +INLINE int wrap(int x, int min, int max); + +//\} + +/* \} */ + + +// -------------------------------------------------------------------- +// FIXED POINT +// -------------------------------------------------------------------- + + +/*! \addtogroup grpMathFixed */ +/*! \{ */ + +#define FIX_SHIFT 8 +#define FIX_SCALE ( 1<fp and m = (n+a-1)/a (i.e., rounding up) +* \li Maximum safe numerator \a x: x < n/(m*a-n) +* \li Minimum n for known \a x: n > x*(a-1) +*/ +#define FX_RECIMUL(x, a, fp) ( ((x)*((1<<(fp))+(a)-1)/(a))>>(fp) ) + +INLINE FIXED int2fx(int d); +INLINE FIXED float2fx(float f); +INLINE u32 fx2uint(FIXED fx); +INLINE u32 fx2ufrac(FIXED fx); +INLINE int fx2int(FIXED fx); +INLINE float fx2float(FIXED fx); +INLINE FIXED fxadd(FIXED fa, FIXED fb); +INLINE FIXED fxsub(FIXED fa, FIXED fb); +INLINE FIXED fxmul(FIXED fa, FIXED fb); +INLINE FIXED fxdiv(FIXED fa, FIXED fb); + +INLINE FIXED fxmul64(FIXED fa, FIXED fb); +INLINE FIXED fxdiv64(FIXED fa, FIXED fb); + +/*! \} */ + +// === LUT ============================================================ + + +/*! \addtogroup grpMathLut */ +/*! \{ */ + +#define SIN_LUT_SIZE 514 // 512 for main lut, 2 extra for lerp +#define DIV_LUT_SIZE 257 // 256 for main lut, 1 extra for lerp + +INLINE s32 lu_sin(uint theta); +INLINE s32 lu_cos(uint theta); +INLINE uint lu_div(uint x); + +INLINE int lu_lerp32(const s32 lut[], uint x, const uint shift); +INLINE int lu_lerp16(const s16 lut[], uint x, const uint shift); + +/*! \} */ + +// === POINT ========================================================== + +struct RECT; + +//! \addtogroup grpMathPoint +//! \{ + +//! 2D Point struct +typedef struct POINT { int x, y; } POINT, POINT32; + + +// --- Point functions --- +INLINE POINT *pt_set(POINT *pd, int x, int y); +INLINE POINT *pt_add(POINT *pd, const POINT *pa, const POINT *pb); +INLINE POINT *pt_sub(POINT *pd, const POINT *pa, const POINT *pb); +INLINE POINT *pt_scale(POINT *pd, const POINT *pa, int c); + +INLINE POINT *pt_add_eq(POINT *pd, const POINT *pb); +INLINE POINT *pt_sub_eq(POINT *pd, const POINT *pb); +INLINE POINT *pt_scale_eq(POINT *pd, int c); + +INLINE int pt_cross(const POINT *pa, const POINT *pb); +INLINE int pt_dot(const POINT *pa, const POINT *pb); + +int pt_in_rect(const POINT *pt, const struct RECT *rc); + +//! \} + + +// === RECT =========================================================== + +/*! \addtogroup grpMathRect */ +/*! \{ */ + +//! Rectangle struct +typedef struct RECT +{ + int left, top; + int right, bottom; +} RECT, RECT32; + +INLINE RECT *rc_set(RECT *rc, int l, int t, int r, int b); +INLINE RECT *rc_set2(RECT *rc, int x, int y, int w, int h); +INLINE int rc_width(const RECT *rc); +INLINE int rc_height(const RECT *rc); +INLINE RECT *rc_set_pos(RECT *rc, int x, int y); +INLINE RECT *rc_set_size(RECT *rc, int w, int h); +INLINE RECT *rc_move(RECT *rc, int dx, int dy); +INLINE RECT *rc_inflate(RECT *rc, int dw, int dh); +INLINE RECT *rc_inflate2(RECT *rc, const RECT *dr); + +RECT *rc_normalize(RECT *rc); + +/*! \} */ + + +// === VECTOR ========================================================= + +/*! \addtogroup grpMathVector */ +/*! \{ */ + +//! Vector struct +typedef struct VECTOR { FIXED x, y, z; } VECTOR; + + +INLINE VECTOR *vec_set(VECTOR *vd, FIXED x, FIXED y, FIXED z); +INLINE VECTOR *vec_add(VECTOR *vd, const VECTOR *va, const VECTOR *vb); +INLINE VECTOR *vec_sub(VECTOR *vd, const VECTOR *va, const VECTOR *vb); +INLINE VECTOR *vec_mul(VECTOR *vd, const VECTOR *va, const VECTOR *vb); +INLINE VECTOR *vec_scale(VECTOR *vd, const VECTOR *va, FIXED c); +INLINE FIXED vec_dot(const VECTOR *va, const VECTOR *vb); + +INLINE VECTOR *vec_add_eq(VECTOR *vd, const VECTOR *vb); +INLINE VECTOR *vec_sub_eq(VECTOR *vd, const VECTOR *vb); +INLINE VECTOR *vec_mul_eq(VECTOR *vd, const VECTOR *vb); +INLINE VECTOR *vec_scale_eq(VECTOR *vd, FIXED c); + +VECTOR *vec_cross(VECTOR *vd, const VECTOR *va, const VECTOR *vb); + +/*! \} */ + + + +// === INLINE ========================================================= + +// --- General -------------------------------------------------------- + +//! Get the sign of \a x. +INLINE int sgn(int x) +{ return (x>=0) ? +1 : -1; } + +//! Tri-state sign of \a x: -1 for negative, 0 for 0, +1 for positive. +INLINE int sgn3(int x) +{ return (x>>31) - (-x>>31); } + +//! Get the maximum of \a a and \a b +INLINE int max(int a, int b) +{ return (a > b) ? (a) : (b); } + +//! Get the minimum of \a a and \a b +INLINE int min(int a, int b) +{ return (a < b) ? (a) : (b); } + + +//! Range check +INLINE BOOL in_range(int x, int min, int max) +{ return (u32)(x-min) < (u32)(max-min); } + + +//! Truncates \a x to stay in range [\a min, \a max> +/*! \return Truncated value of \a x. +* \note \a max is exclusive! +*/ +INLINE int clamp(int x, int min, int max) +{ return (x>=max) ? (max-1) : ( (x, +* it'll be placed inside again with the same distance +* to the 'wall', but on the other side. Example for lower +* border: y = \a min - (\a x- \a min) = 2*\a min + \a x. +* \return Reflected value of \a x. +* \note \a max is exclusive! +*/ +INLINE int reflect(int x, int min, int max) +{ return (x>=max) ? (2*(max-1)-x) : ( (x +INLINE int wrap(int x, int min, int max) +{ return (x>=max) ? (x+min-max) : ( (x>FIX_SHIFT; } + +//! Get the unsigned fractional part of a fixed point value (orly?). +INLINE u32 fx2ufrac(FIXED fx) +{ return fx&FIX_MASK; } + +//! Convert a FIXED point value to an signed integer. +INLINE int fx2int(FIXED fx) +{ return fx/FIX_SCALE; } + +//! Convert a fixed point value to floating point. +INLINE float fx2float(FIXED fx) +{ return fx/FIX_SCALEF; } + +//! Add two fixed point values +INLINE FIXED fxadd(FIXED fa, FIXED fb) +{ return fa + fb; } + +//! Subtract two fixed point values +INLINE FIXED fxsub(FIXED fa, FIXED fb) +{ return fa - fb; } + + +//! Multiply two fixed point values +INLINE FIXED fxmul(FIXED fa, FIXED fb) +{ return (fa*fb)>>FIX_SHIFT; } + +//! Divide two fixed point values. +INLINE FIXED fxdiv(FIXED fa, FIXED fb) +{ return ((fa)*FIX_SCALE)/(fb); } + + +//! Multiply two fixed point values using 64bit math. +INLINE FIXED fxmul64(FIXED fa, FIXED fb) +{ return (((s64)fa)*fb)>>FIX_SHIFT; } + + +//! Divide two fixed point values using 64bit math. +INLINE FIXED fxdiv64(FIXED fa, FIXED fb) +{ return ( ((s64)fa)<x). +* You can get values for non-integer \e x via (linear) +* interpolation between f(x) and f(x+1). +* \param lut The LUT to interpolate from. +* \param x Fixed point number to interpolate at. +* \param shift Number of fixed-point bits of \a x. +*/ +INLINE int lu_lerp32(const s32 lut[], uint x, const uint shift) +{ + int xa, ya, yb; + xa=x>>shift; + ya= lut[xa]; yb= lut[xa+1]; + return ya + ( (yb-ya)*(x-(xa<>shift ); +} + +//! As lu_lerp32, but for 16bit LUTs. +INLINE int lu_lerp16(const s16 lut[], uint x, const uint shift) +{ + int xa, ya, yb; + xa=x>>shift; + ya= lut[xa]; yb= lut[xa+1]; + return ya + ( (yb-ya)*(x-(xa<>shift ); +} + + +// --- Point ---------------------------------------------------------- + +//! Initialize \a pd to (\a x, \a y) +INLINE POINT *pt_set(POINT *pd, int x, int y) +{ + pd->x= x; pd->y= y; + return pd; +} + +//! Point addition: \a pd = \a pa + \a pb +INLINE POINT *pt_add(POINT *pd, const POINT *pa, const POINT *pb) +{ + pd->x= pa->x + pb->x; + pd->y= pa->x + pb->y; + return pd; +} + +//! Point subtraction: \a pd = \a pa - \a pb +INLINE POINT *pt_sub(POINT *pd, const POINT *pa, const POINT *pb) +{ + pd->x= pa->x - pb->x; + pd->y= pa->x - pb->y; + return pd; +} + +//! Point scale: \a pd = \a c * \a pa +INLINE POINT *pt_scale(POINT *pd, const POINT *pa, int c) +{ + pd->x= pa->x*c; + pd->y= pa->y*c; + return pd; +} + +//! Point increment: \a pd += \a pb +INLINE POINT *pt_add_eq(POINT *pd, const POINT *pb) +{ + pd->x += pb->y; + pd->y += pb->y; + return pd; +} + +//! Point decrement: \a pd -= \a pb +INLINE POINT *pt_sub_eq(POINT *pd, const POINT *pb) +{ + pd->x -= pb->y; + pd->y -= pb->y; + return pd; +} + +//! Point scale: \a pd *= \a c +INLINE POINT *pt_scale_eq(POINT *pd, int c) +{ + pd->x *= c; + pd->y *= c; + return pd; +} + +//! Point 'cross'-product: \a pa \htmlonly × \endhtmlonly \a pb +/*! Actually, there's no such thing as a 2D cross-product, but you could +* extend it to 3D and get the value of its z-component, +* which can be used for a test for parallelism. +*/ +INLINE int pt_cross(const POINT *pa, const POINT *pb) +{ return pa->x * pb->y - pa->y * pb->x; } + + +//! Point 'dot'-product:\a pa \htmlonly · \endhtmlonly \a pb +INLINE int pt_dot(const POINT *pa, const POINT *pb) +{ return pa->x * pb->x + pa->y * pb->y; } + + + +// --- Rect ----------------------------------------------------------- + +//! Initialize a rectangle. +/*! \param l Left side. +* \param t Top side. +* \param r Right side. +* \param b Bottom side. +*/ +INLINE RECT *rc_set(RECT *rc, int l, int t, int r, int b) +{ + rc->left= l; rc->top= t; rc->right= r; rc->bottom= b; + return rc; +} + +//! Initialize a rectangle, with sizes inside of max boundaries. +/*! \param x Left side. +* \param y Top side. +* \param w Width. +* \param h Height. +*/ +INLINE RECT *rc_set2(RECT *rc, int x, int y, int w, int h) +{ + rc->left= x; rc->top= y; rc->right= x+w; rc->bottom= y+h; + return rc; +} + +//! Get rectangle width. +INLINE int rc_width(const RECT *rc) +{ return rc->right - rc->left; } + +//! Get rectangle height +INLINE int rc_height(const RECT *rc) +{ return rc->bottom - rc->top; } + +//! Move rectangle to (\a x, \a y) position. +INLINE RECT *rc_set_pos(RECT *rc, int x, int y) +{ + rc->right += x-rc->left; rc->left= x; + rc->bottom += y-rc->top; rc->top= y; + return rc; +} + +//! Reside rectangle. +INLINE RECT *rc_set_size(RECT *rc, int w, int h) +{ + rc->right= rc->left+w; rc->bottom= rc->top+h; + return rc; +} + +//! Move rectangle by (\a dx, \a dy). +INLINE RECT *rc_move(RECT *rc, int dx, int dy) +{ + rc->left += dx; rc->top += dy; + rc->right += dx; rc->bottom += dy; + return rc; +} + +//! Increase size by \a dw horizontally and \a dh vertically. +INLINE RECT *rc_inflate(RECT *rc, int dw, int dh) +{ + rc->left -= dw; rc->top -= dh; + rc->right += dw; rc->bottom += dh; + return rc; +} + +//! Increase sizes on all sides by values of rectangle \a dr. +INLINE RECT *rc_inflate2(RECT *rc, const RECT *dr) +{ + rc->left += dr->left; rc->top += dr->top; + rc->right += dr->right; rc->bottom += dr->bottom; + return rc; +} + + +// --- Vector --------------------------------------------------------- + +//! Initialize a vector +INLINE VECTOR *vec_set(VECTOR *vd, FIXED x, FIXED y, FIXED z) +{ + vd->x= x; vd->y= y; vd->z= z; + return vd; +} + +//! Add vectors: \b d = \b a + \b b; +INLINE VECTOR *vec_add(VECTOR *vd, const VECTOR *va, const VECTOR *vb) +{ + vd->x= va->x + vb->x; + vd->y= va->y + vb->y; + vd->z= va->z + vb->z; + return vd; +} + +//! Subtract vectors: \b d = \b a - \b b; +INLINE VECTOR *vec_sub(VECTOR *vd, const VECTOR *va, const VECTOR *vb) +{ + vd->x= va->x - vb->x; + vd->y= va->y - vb->y; + vd->z= va->z - vb->z; + return vd; +} + +//! Multiply vectors elements: \b d = \b S(ax, ay, az) �\b b +INLINE VECTOR *vec_mul(VECTOR *vd, const VECTOR *va, const VECTOR *vb) +{ + vd->x= fxmul(va->x, vb->x); + vd->y= fxmul(va->y, vb->y); + vd->z= fxmul(va->z, vb->z); + return vd; +} + +//! Scale vector: \b d = c*\b a +INLINE VECTOR *vec_scale(VECTOR *vd, const VECTOR *va, FIXED c) +{ + vd->x= fxmul(va->x, c); + vd->y= fxmul(va->y, c); + vd->z= fxmul(va->z, c); + return vd; +} + +//! Dot-product: d = \b a �\b b +INLINE FIXED vec_dot(const VECTOR *va, const VECTOR *vb) +{ + FIXED dot; + dot = fxmul(va->x, vb->x); + dot += fxmul(va->y, vb->y); + dot += fxmul(va->z, vb->z); + return dot; +} + +//! Increment vector: \b d += \b b; +INLINE VECTOR *vec_add_eq(VECTOR *vd, const VECTOR *vb) +{ vd->x += vb->x; vd->y += vb->y; vd->z += vb->z; return vd; } + +//! Decrease vector: \b d -= \b b; +INLINE VECTOR *vec_sub_eq(VECTOR *vd, const VECTOR *vb) +{ vd->x -= vb->x; vd->y -= vb->y; vd->z -= vb->z; return vd; } + +//! Multiply vectors elements: \b d = \b S(dx, dy, dz) �\b b +INLINE VECTOR *vec_mul_eq(VECTOR *vd, const VECTOR *vb) +{ + vd->x= fxmul(vd->x, vb->x); + vd->y= fxmul(vd->y, vb->y); + vd->z= fxmul(vd->z, vb->z); + return vd; +} + +//! Scale vector: \b d = c*\b d +INLINE VECTOR *vec_scale_eq(VECTOR *vd, FIXED c) +{ + vd->x= fxmul(vd->x, c); + vd->y= fxmul(vd->y, c); + vd->z= fxmul(vd->z, c); + return vd; +} + + +#endif //GBA_SPRITE_ENGINE_PROJECT_TONC_MATH_STUB_H diff --git a/engine/include/libgba-sprite-engine/gba/tonc_oam.h b/engine/include/libgba-sprite-engine/gba/tonc_oam.h index 6ba0679..f3212c1 100644 --- a/engine/include/libgba-sprite-engine/gba/tonc_oam.h +++ b/engine/include/libgba-sprite-engine/gba/tonc_oam.h @@ -14,7 +14,12 @@ #include "tonc_memmap.h" #include "tonc_memdef.h" #include "tonc_core.h" +#ifdef CODE_COMPILED_AS_PART_OF_TEST +#include "tonc_math_stub.h" +#else #include "tonc_math.h" +#endif + // -------------------------------------------------------------------- // OBJECTS diff --git a/engine/include/libgba-sprite-engine/gbavector.h b/engine/include/libgba-sprite-engine/gbavector.h new file mode 100644 index 0000000..4940dd5 --- /dev/null +++ b/engine/include/libgba-sprite-engine/gbavector.h @@ -0,0 +1,33 @@ +// +// Created by Wouter Groeneveld on 14/12/18. +// + +#ifndef GBA_SPRITE_ENGINE_PROJECT_MATH_H +#define GBA_SPRITE_ENGINE_PROJECT_MATH_H + +#include + +#ifdef CODE_COMPILED_AS_PART_OF_TEST +#include +#else +#include +#endif + +#include + +class GBAVector { +private: + VECTOR v; +public: + GBAVector() : v({}) {} + GBAVector(VECTOR v) : v(v) {} + + std::deque bresenhamLineTo(VECTOR dest); + VECTOR rotateAsCenter(VECTOR point, uint angle); + + std::string to_string() { + return "(" + std::to_string(v.x) + "," + std::to_string(v.y) + ")"; + } +}; + +#endif //GBA_SPRITE_ENGINE_PROJECT_MATH_H diff --git a/engine/include/libgba-sprite-engine/math.h b/engine/include/libgba-sprite-engine/math.h deleted file mode 100644 index 288f316..0000000 --- a/engine/include/libgba-sprite-engine/math.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// Created by Wouter Groeneveld on 14/12/18. -// - -#ifndef GBA_SPRITE_ENGINE_PROJECT_MATH_H -#define GBA_SPRITE_ENGINE_PROJECT_MATH_H - -#include -#include - -class Math { -public: - static std::deque bresenhamLineBetween(VECTOR src, VECTOR dest); -}; - -#endif //GBA_SPRITE_ENGINE_PROJECT_MATH_H diff --git a/engine/include/libgba-sprite-engine/sprites/sprite.h b/engine/include/libgba-sprite-engine/sprites/sprite.h index f454685..1433fb5 100644 --- a/engine/include/libgba-sprite-engine/sprites/sprite.h +++ b/engine/include/libgba-sprite-engine/sprites/sprite.h @@ -7,7 +7,12 @@ #include #include +#ifdef CODE_COMPILED_AS_PART_OF_TEST +#include +#else #include +#endif +#include #define COLOR_MODE_16 0 #define COLOR_MODE_256 1 @@ -92,6 +97,7 @@ public: u32 getTileIndex() { return tileIndex; } VECTOR getPos() { return {x, y}; } + GBAVector getPosAsVector() { return GBAVector(getPos()); } VECTOR getCenter() { return { x + w / 2, y + h / 2 }; } VECTOR getVelocity() { return { dx, dy}; } u32 getDx() { return dx; } diff --git a/engine/src/math.cpp b/engine/src/gbavector.cpp similarity index 66% rename from engine/src/math.cpp rename to engine/src/gbavector.cpp index 16c99e1..ae48dc6 100644 --- a/engine/src/math.cpp +++ b/engine/src/gbavector.cpp @@ -2,10 +2,27 @@ // Created by Wouter Groeneveld on 14/12/18. // -#include +#include -std::deque Math::bresenhamLineBetween(VECTOR src, VECTOR dest) { +VECTOR GBAVector::rotateAsCenter(VECTOR point, uint angle) { + auto center = this->v; + s32 centerx = center.x, centery = center.y; + s32 defaultx = point.x, defaulty = point.y; + + s32 cos = lu_cos(angle) >> 4; + s32 sin = lu_sin(angle) >> 4; + + // affine matriches are 8.8 fixed point numbers, so shift all input 8 spaces up and forth + // possibilities: instead of between [-1.0, 1.0] it's between [-256, +256] + // 90° rotation in inversed y-axis needs to flip sin sign + return { + ( cos * (defaultx - centerx) + sin * (defaulty - centery) + (centerx << 8)) >> 8, + (-sin * (defaultx - centerx) + cos * (defaulty - centery) + (centery << 8)) >> 8}; +} + +std::deque GBAVector::bresenhamLineTo(VECTOR dest) { // https://www.coranac.com/tonc/text/bitmaps.htm - Bresenham's line algorithm with fixed points + VECTOR src = this->v; VECTOR step, delta; std::deque coords; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 037109b..da283f8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -28,6 +28,7 @@ add_executable(unittest allocatortest.cpp palettetest.cpp real_data.h + ../engine/src/gba/sin_lut.s ../engine/src/background/background.cpp ../engine/src/background/text_stream.cpp ../engine/src/palette/palette_manager.cpp @@ -35,7 +36,7 @@ add_executable(unittest ../engine/src/allocator.cpp ../engine/src/palette/combined_palette.cpp ../engine/src/timer.cpp - ../engine/src/math.cpp - timertest.cpp mathtest.cpp) + ../engine/src/gbavector.cpp + timertest.cpp gbavectortest.cpp) target_link_libraries(unittest ${GTEST_LIBRARY}/build/libgtest.a ${GTEST_LIBRARY}/build/libgtest_main.a) diff --git a/test/mathtest.cpp b/test/gbavectortest.cpp similarity index 53% rename from test/mathtest.cpp rename to test/gbavectortest.cpp index bf399dc..2b864ed 100644 --- a/test/mathtest.cpp +++ b/test/gbavectortest.cpp @@ -5,20 +5,54 @@ #include -#include +#include -class MathSuite : public ::testing::Test { +class GBAVectorSuite : public ::testing::Test { protected: + GBAVector vector; + VECTOR bottomHalf; virtual void TearDown() { } virtual void SetUp() { + vector = GBAVector({120, 80}); + bottomHalf = { 120, 200 }; } }; -TEST_F(MathSuite, LineBetween_Diagonal_ToTopRightCorner) { - auto points = Math::bresenhamLineBetween({120, 80}, {240, 0}); +// Angle in DEGREES for readability - converted in tonc_math_stub! +TEST_F(GBAVectorSuite, Rotate_FromBottomHalf_0_Degrees) { + auto result = vector.rotateAsCenter(bottomHalf, 0); + ASSERT_EQ(120, result.x); + ASSERT_EQ(200, result.y); +} + +TEST_F(GBAVectorSuite, Rotate_FromBottomHalf_90_Degrees) { + auto result = vector.rotateAsCenter(bottomHalf, 90); + ASSERT_EQ(240, result.x); + ASSERT_EQ(80, result.y); +} + +TEST_F(GBAVectorSuite, Rotate_FromBottomHalf_180_Degrees) { + auto result = vector.rotateAsCenter(bottomHalf, 180); + ASSERT_EQ(120, result.x); + ASSERT_EQ(-40, result.y); +} + +TEST_F(GBAVectorSuite, Rotate_FromBottomHalf_270_Degrees) { + auto result = vector.rotateAsCenter(bottomHalf, 270); + ASSERT_EQ(0, result.x); + ASSERT_EQ(80, result.y); +} +// ---- // + +TEST_F(GBAVectorSuite, ToString) { + ASSERT_EQ(std::string("(120,80)"), vector.to_string()); +} + +TEST_F(GBAVectorSuite, LineBetween_Diagonal_ToTopRightCorner) { + auto points = vector.bresenhamLineTo({240, 0}); ASSERT_EQ(121, points.size()); VECTOR pt; @@ -37,8 +71,8 @@ TEST_F(MathSuite, LineBetween_Diagonal_ToTopRightCorner) { ASSERT_EQ(13, pt.y); } -TEST_F(MathSuite, LineBetween_Diagonal_ToTopLeftCorner) { - auto points = Math::bresenhamLineBetween({120, 80}, {0, 0}); +TEST_F(GBAVectorSuite, LineBetween_Diagonal_ToTopLeftCorner) { + auto points = vector.bresenhamLineTo({0, 0}); ASSERT_EQ(121, points.size()); VECTOR pt; @@ -57,8 +91,8 @@ TEST_F(MathSuite, LineBetween_Diagonal_ToTopLeftCorner) { ASSERT_EQ(13, pt.y); } -TEST_F(MathSuite, LineBetween_Diagonal_ToBottomLeftCorner) { - auto points = Math::bresenhamLineBetween({120, 80}, {0, 160}); +TEST_F(GBAVectorSuite, LineBetween_Diagonal_ToBottomLeftCorner) { + auto points = vector.bresenhamLineTo({0, 160}); ASSERT_EQ(121, points.size()); VECTOR pt; @@ -77,8 +111,8 @@ TEST_F(MathSuite, LineBetween_Diagonal_ToBottomLeftCorner) { ASSERT_EQ(147, pt.y); } -TEST_F(MathSuite, LineBetween_Diagonal_ToBottomRightCorner) { - auto points = Math::bresenhamLineBetween({120, 80}, {240, 160}); +TEST_F(GBAVectorSuite, LineBetween_Diagonal_ToBottomRightCorner) { + auto points = vector.bresenhamLineTo({240, 160}); ASSERT_EQ(121, points.size()); VECTOR pt; @@ -97,8 +131,8 @@ TEST_F(MathSuite, LineBetween_Diagonal_ToBottomRightCorner) { ASSERT_EQ(147, pt.y); } -TEST_F(MathSuite, LineBetween_Horizontal_FromCenterToHalfYZeroX) { - auto points = Math::bresenhamLineBetween({120, 80}, {0, 80}); +TEST_F(GBAVectorSuite, LineBetween_Horizontal_FromCenterToHalfYZeroX) { + auto points = vector.bresenhamLineTo({0, 80}); ASSERT_EQ(121, points.size()); for(int i = 0; i <= 120; i++) { auto &vec = points.front(); @@ -108,8 +142,8 @@ TEST_F(MathSuite, LineBetween_Horizontal_FromCenterToHalfYZeroX) { } } -TEST_F(MathSuite, LineBetween_Vertical_FromCenterToHalfXFullY) { - auto points = Math::bresenhamLineBetween({120, 80}, {120, 160}); +TEST_F(GBAVectorSuite, LineBetween_Vertical_FromCenterToHalfXFullY) { + auto points = vector.bresenhamLineTo({120, 160}); ASSERT_EQ(81, points.size()); for(int i = 0; i <= (160 - 80); i++) { auto &vec = points.front(); diff --git a/test/palettetest.cpp b/test/palettetest.cpp index b433e60..54ae733 100644 --- a/test/palettetest.cpp +++ b/test/palettetest.cpp @@ -14,7 +14,7 @@ public: } virtual ~SomePaletteManager() { - delete palette; + delete[] palette; } protected: