refactoring math suite into GBAVector, added tests for point rotation, stub for sin/cos
This commit is contained in:
parent
6fe6187d2c
commit
e690007190
|
@ -5,15 +5,15 @@
|
|||
#include <libgba-sprite-engine/background/text_stream.h>
|
||||
#include <libgba-sprite-engine/gba/tonc_bios.h>
|
||||
#include <cmath>
|
||||
#include <libgba-sprite-engine/math.h>
|
||||
#include <libgba-sprite-engine/gbavector.h>
|
||||
#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() {
|
||||
|
|
|
@ -7,7 +7,11 @@
|
|||
|
||||
|
||||
#include <libgba-sprite-engine/sprites/sprite.h>
|
||||
#ifdef CODE_COMPILED_AS_PART_OF_TEST
|
||||
#include <libgba-sprite-engine/gba/tonc_math_stub.h>
|
||||
#else
|
||||
#include <libgba-sprite-engine/gba/tonc_math.h>
|
||||
#endif
|
||||
#include <deque>
|
||||
|
||||
class Bullet {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
|
|
|
@ -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 <cmath>
|
||||
#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<<FIX_SHIFT )
|
||||
#define FIX_MASK ( FIX_SCALE-1 )
|
||||
#define FIX_SCALEF ( (float)FIX_SCALE )
|
||||
#define FIX_SCALEF_INV ( 1.0/FIX_SCALEF )
|
||||
|
||||
#define FIX_ONE FIX_SCALE
|
||||
|
||||
//! Get the fixed point reciprocal of \a a, in \a fp fractional bits.
|
||||
/*!
|
||||
* \param a Value to take the reciprocal of.
|
||||
* \param fp Number of fixed point bits
|
||||
* \note The routine does do a division, but the compiler will
|
||||
* optimize it to a single constant ... \e if both \a a and \a fp
|
||||
* are constants!
|
||||
* \sa #FX_RECIMUL
|
||||
*/
|
||||
#define FX_RECIPROCAL(a, fp) ( ((1<<(fp))+(a)-1)/(a) )
|
||||
|
||||
//! Perform the division \a x/ \a a by reciprocal multiplication
|
||||
/*! Division is slow, but you can approximate division by a constant
|
||||
* by multiplying with its reciprocal: x/a vs x*(1/a). This routine
|
||||
* gives the reciprocal of \a a as a fixed point number with \a fp
|
||||
* fractional bits.
|
||||
* \param a Value to take the reciprocal of.
|
||||
* \param fp Number of fixed point bits
|
||||
* \note The routine does do a division, but the compiler will
|
||||
* optimize it to a single constant ... \e if both \a a and \a fp
|
||||
* are constants!
|
||||
* \note Rules for safe reciprocal division, using
|
||||
* n = 2<sup>fp</sup> 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<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!
|
||||
*/
|
||||
INLINE int reflect(int x, int min, int max)
|
||||
{ return (x>=max) ? (2*(max-1)-x) : ( (x<min) ? (2*min-x) : x ); }
|
||||
|
||||
//! Wraps \a x to stay in range [\a min, \a max>
|
||||
INLINE int wrap(int x, int min, int max)
|
||||
{ return (x>=max) ? (x+min-max) : ( (x<min) ? (x+max-min) : x ); }
|
||||
|
||||
|
||||
// --- Fixed point ----------------------------------------------------
|
||||
|
||||
|
||||
//! Convert an integer to fixed-point
|
||||
INLINE FIXED int2fx(int d)
|
||||
{ return d<<FIX_SHIFT; }
|
||||
|
||||
//! Convert a float to fixed-point
|
||||
INLINE FIXED float2fx(float f)
|
||||
{ return (FIXED)(f*FIX_SCALEF); }
|
||||
|
||||
|
||||
//! Convert a FIXED point value to an unsigned integer (orly?).
|
||||
INLINE u32 fx2uint(FIXED fx)
|
||||
{ return fx>>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)<<FIX_SHIFT)/(fb); }
|
||||
|
||||
|
||||
// --- LUT ------------------------------------------------------------
|
||||
|
||||
//! Look-up a sine value (2π = 0x10000)
|
||||
/*! \param theta Angle in [0,FFFFh] range
|
||||
* \return .12f sine value
|
||||
*/
|
||||
INLINE s32 lu_sin(uint theta) {
|
||||
// Stub expects for testing the angle in degrees
|
||||
double rad = theta*M_PI/180;
|
||||
auto x = sin(rad);
|
||||
return (int)(x * 65536.0f / 16.0f);;
|
||||
}
|
||||
|
||||
//! Look-up a cosine value (2π = 0x10000)
|
||||
/*! \param theta Angle in [0,FFFFh] range
|
||||
* \return .12f cosine value
|
||||
*/
|
||||
INLINE s32 lu_cos(uint theta) {
|
||||
// Stub expects for testing the angle in degrees
|
||||
double rad = theta*M_PI/180;
|
||||
auto x = cos(rad);
|
||||
return (int)(x * 65536.0f / 16.0f);;
|
||||
}
|
||||
|
||||
//! Look-up a division value between 0 and 255
|
||||
/*! \param x reciprocal to look up.
|
||||
* \return 1/x (.16f)
|
||||
*/
|
||||
INLINE uint lu_div(uint x)
|
||||
{ return 0; }
|
||||
|
||||
|
||||
//! Linear interpolator for 32bit LUTs.
|
||||
/*! A lut is essentially the discrete form of a function, f(<i>x</i>).
|
||||
* 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))>>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))>>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 <i>z</i>-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) <20>\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 <20>\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) <20>\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
|
|
@ -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
|
||||
|
|
|
@ -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 <deque>
|
||||
|
||||
#ifdef CODE_COMPILED_AS_PART_OF_TEST
|
||||
#include <libgba-sprite-engine/gba/tonc_math_stub.h>
|
||||
#else
|
||||
#include <libgba-sprite-engine/gba/tonc_math.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
class GBAVector {
|
||||
private:
|
||||
VECTOR v;
|
||||
public:
|
||||
GBAVector() : v({}) {}
|
||||
GBAVector(VECTOR v) : v(v) {}
|
||||
|
||||
std::deque<VECTOR> 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
|
|
@ -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 <deque>
|
||||
#include <libgba-sprite-engine/gba/tonc_math.h>
|
||||
|
||||
class Math {
|
||||
public:
|
||||
static std::deque<VECTOR> bresenhamLineBetween(VECTOR src, VECTOR dest);
|
||||
};
|
||||
|
||||
#endif //GBA_SPRITE_ENGINE_PROJECT_MATH_H
|
|
@ -7,7 +7,12 @@
|
|||
|
||||
#include <libgba-sprite-engine/gba/tonc_types.h>
|
||||
#include <memory>
|
||||
#ifdef CODE_COMPILED_AS_PART_OF_TEST
|
||||
#include <libgba-sprite-engine/gba/tonc_math_stub.h>
|
||||
#else
|
||||
#include <libgba-sprite-engine/gba/tonc_math.h>
|
||||
#endif
|
||||
#include <libgba-sprite-engine/gbavector.h>
|
||||
|
||||
#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; }
|
||||
|
|
|
@ -2,10 +2,27 @@
|
|||
// Created by Wouter Groeneveld on 14/12/18.
|
||||
//
|
||||
|
||||
#include <libgba-sprite-engine/math.h>
|
||||
#include <libgba-sprite-engine/gbavector.h>
|
||||
|
||||
std::deque<VECTOR> 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<VECTOR> 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<VECTOR> coords;
|
|
@ -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)
|
||||
|
|
|
@ -5,20 +5,54 @@
|
|||
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <libgba-sprite-engine/math.h>
|
||||
#include <libgba-sprite-engine/gbavector.h>
|
||||
|
||||
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();
|
|
@ -14,7 +14,7 @@ public:
|
|||
}
|
||||
|
||||
virtual ~SomePaletteManager() {
|
||||
delete palette;
|
||||
delete[] palette;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
|
Loading…
Reference in New Issue