view/projection/world matrices, first attempt to glue everything together

This commit is contained in:
wgroeneveld 2020-07-09 13:29:02 +02:00
parent c6374b6e59
commit a38f1eeddd
20 changed files with 631 additions and 315 deletions

View File

@ -11,31 +11,31 @@ const unsigned short pal[4] __attribute__((aligned(4))) = {
0x0000, 0xFFFF, 0x3AE2 0x0000, 0xFFFF, 0x3AE2
}; };
const FIXED pointZeroOne = float2fx(0.01);
std::vector<Mesh*> WireScene::meshes() { std::vector<Mesh*> WireScene::meshes() {
return { cube.get() }; return { cube.get() };
} }
Camera WireScene::camera() {
return Camera(VectorFx::fromInt(0, 0, 10), VectorFx::fromInt(0, 0, 0));
}
void WireScene::load() { void WireScene::load() {
foregroundPalette = std::unique_ptr<ForegroundPaletteManager>(new ForegroundPaletteManager()); foregroundPalette = std::unique_ptr<ForegroundPaletteManager>(new ForegroundPaletteManager());
backgroundPalette = std::unique_ptr<BackgroundPaletteManager>(new BackgroundPaletteManager(pal, sizeof(pal))); backgroundPalette = std::unique_ptr<BackgroundPaletteManager>(new BackgroundPaletteManager(pal, sizeof(pal)));
cube = std::unique_ptr<Mesh>(new Mesh()); cube = std::unique_ptr<Mesh>(new Mesh());
cube->add(GBAVector(1, 1, 1)); cube->add(VectorFx(-1, 1, 1));
cube->add(GBAVector(10, 10, 1)); cube->add(VectorFx(1, 1, 1));
cube->add(GBAVector(20, 20, 1)); cube->add(VectorFx(-1, -1, 1));
cube->add(GBAVector(30, 30, 1)); cube->add(VectorFx(-1, -1, -1));
/* cube->add(VectorFx(-1, 1, -1));
cube->add(GBAVector(-1, 1, 1)); cube->add(VectorFx(1, 1, -1));
cube->add(GBAVector(1, 1, 1)); cube->add(VectorFx(1, -1, 1));
cube->add(GBAVector(-1, -1, 1)); cube->add(VectorFx(-1, -1, -1));
cube->add(GBAVector(-1, -1, -1));
cube->add(GBAVector(-1, 1, -1));
cube->add(GBAVector(1, 1, -1));
cube->add(GBAVector(1, -1, 1));
cube->add(GBAVector(-1, -1, -1));
*/
} }
void WireScene::tick(u16 keys) { void WireScene::tick(u16 keys) {
cube->rotate(pointZeroOne, pointZeroOne);
} }

View File

@ -7,6 +7,7 @@
#include <libgba-sprite-engine/scene.h> #include <libgba-sprite-engine/scene.h>
#include <libgba-sprite-engine/mesh.h> #include <libgba-sprite-engine/mesh.h>
#include <libgba-sprite-engine/camera.h>
#include <memory> #include <memory>
class WireScene : public Scene { class WireScene : public Scene {
@ -20,6 +21,7 @@ public:
void load() override; void load() override;
void tick(u16 keys) override; void tick(u16 keys) override;
std::vector<Mesh*> meshes() override; std::vector<Mesh*> meshes() override;
Camera camera() override;
}; };

View File

@ -10,7 +10,7 @@ add_library(${PROJECT_NAME}
src/gba/tonc_bios.s src/gba/tonc_bios.s
src/gba_engine.cpp src/gba_engine.cpp
src/math.cpp src/math.cpp
src/sound_control.cpp src/scene.cpp src/timer.cpp src/gbavector.cpp src/mesh.cpp) src/sound_control.cpp src/scene.cpp src/timer.cpp src/vectorfx.cpp src/mesh.cpp)
target_include_directories(${PROJECT_NAME} PUBLIC target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>

View File

@ -1,66 +0,0 @@
//
// Created by Wouter Groeneveld on 08/07/20.
//
#ifndef GBA_BITMAP_ENGINE_PROJECT_GBAMATRIX_H
#define GBA_BITMAP_ENGINE_PROJECT_GBAMATRIX_H
#define MATRIX_DIMENSION 16
#include <libgba-sprite-engine/math.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
class GBAMatrix {
private:
FIXED m[MATRIX_DIMENSION];
public:
inline static GBAMatrix zero() { return GBAMatrix(); }
inline FIXED mAt(int index) const {
return m[index];
}
GBAMatrix() {}
GBAMatrix(FIXED m11, FIXED m12, FIXED m13, FIXED m14, FIXED m21, FIXED m22, FIXED m23, FIXED m24, FIXED m31, FIXED m32, FIXED m33, FIXED m34, FIXED m41, FIXED m42, FIXED m43, FIXED m44) {
m[0] = m11;
m[1] = m12;
m[2] = m13;
m[3] = m14;
m[4] = m21;
m[5] = m22;
m[6] = m23;
m[7] = m24;
m[8] = m31;
m[9] = m32;
m[10] = m33;
m[11] = m34;
m[12] = m41;
m[13] = m42;
m[14] = m43;
m[15] = m44;
}
inline static GBAMatrix perspectiveFovLH(FIXED fov, FIXED aspect, FIXED znear, FIXED zfar) {
auto matrix = GBAMatrix::zero();
FIXED tanResult = fxdiv(ONE, fxtan(fxmul(fov, HALF)));
matrix.m[0] = fxdiv(tanResult, aspect);
matrix.m[1] = matrix.m[2] = matrix.m[3] = 0;
matrix.m[5] = tanResult;
matrix.m[4] = matrix.m[6] = matrix.m[7] = 0;
matrix.m[8] = matrix.m[9] = 0;
matrix.m[10] = fxdiv(-zfar, (znear - zfar));
matrix.m[11] = ONE;
matrix.m[12] = matrix.m[13] = matrix.m[15] = 0;
matrix.m[14] = fxdiv(fxmul(znear, zfar), (znear - zfar));
return matrix;
}
};
#endif //GBA_BITMAP_ENGINE_PROJECT_GBAMATRIX_H

View File

@ -0,0 +1,30 @@
//
// Created by Wouter Groeneveld on 09/07/20.
//
#ifndef GBA_BITMAP_ENGINE_PROJECT_CAMERA_H
#define GBA_BITMAP_ENGINE_PROJECT_CAMERA_H
#include <libgba-sprite-engine/vectorfx.h>
class Camera {
private:
VectorFx position;
VectorFx target;
public:
Camera() : position(VectorFx()), target(VectorFx()) {}
Camera(VectorFx pos, VectorFx targ) : position(pos), target(targ) {}
VectorFx getPosition() { return position; }
VectorFx getTarget() { return target; }
void setPosition(VectorFx pos) {
position = pos;
}
void setTarget(VectorFx tar) {
target = tar;
}
};
#endif //GBA_BITMAP_ENGINE_PROJECT_CAMERA_H

View File

@ -7,13 +7,19 @@
#include <libgba-sprite-engine/gba/tonc_memmap.h> #include <libgba-sprite-engine/gba/tonc_memmap.h>
#include <libgba-sprite-engine/gba/tonc_memdef.h> #include <libgba-sprite-engine/gba/tonc_memmap.h>
#include <libgba-sprite-engine/vectorfx.h>
#include <libgba-sprite-engine/matrixfx.h>
#include "scene.h" #include "scene.h"
#include "sound_control.h" #include "sound_control.h"
#include "timer.h" #include "timer.h"
#define GBA_SCREEN_WIDTH 240 #define GBA_SCREEN_WIDTH 240
#define GBA_SCREEN_WIDTH_FX GBA_SCREEN_WIDTH << 8
#define GBA_SCREEN_HEIGHT 160 #define GBA_SCREEN_HEIGHT 160
#define GBA_SCREEN_HEIGHT_FX GBA_SCREEN_HEIGHT << 8
#define M4_WIDTH 240 #define M4_WIDTH 240
const unsigned int black[VRAM_PAGE_SIZE] = {}; const unsigned int black[VRAM_PAGE_SIZE] = {};
@ -22,6 +28,7 @@ class GBAEngine {
private: private:
// WHY raw pointers? the engine does the transition and cleanup work itself // WHY raw pointers? the engine does the transition and cleanup work itself
Scene* currentScene; Scene* currentScene;
Camera currentCamera;
Scene* sceneToTransitionTo; Scene* sceneToTransitionTo;
u16* vid_page; u16* vid_page;
@ -43,6 +50,7 @@ private:
void render(); void render();
void renderClear(); void renderClear();
inline void plotPixel(int x, int y, u8 clrId); inline void plotPixel(int x, int y, u8 clrId);
inline VectorFx project(VectorFx coord, MatrixFx transMat);
void flipPage(); void flipPage();
public: public:

View File

@ -1,97 +0,0 @@
//
// Created by Wouter Groeneveld on 14/12/18.
// This is a FIXED-POINT VECTOR REPRESENTATION!
// Wrapper around Tonc's VECTOR for convenience and readability.
//
#ifndef GBA_VECTOR_H_
#define GBA_VECTOR_H_
#include <string>
#include <deque>
#include <libgba-sprite-engine/gba/tonc_bios.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
class GBAVector {
private:
VECTOR v;
public:
GBAVector() : v({}) {}
GBAVector(const GBAVector& other) : v(other.v) {}
GBAVector(VECTOR v) : v(v) {}
GBAVector(FIXED x, FIXED y, FIXED z) : v({ x, y, z}) {}
static GBAVector fromInt(int x, int y, int z) {
return GBAVector(int2fx(x), int2fx(y), int2fx(z));
}
inline GBAVector toInt() {
return GBAVector(fx2int(v.x), fx2int(v.y), fx2int(v.z));
}
std::deque<VECTOR> bresenhamLineTo(VECTOR dest);
GBAVector rotateAsCenter(GBAVector point, FIXED angle);
// WHY all these inlines? performance reasons.
inline static GBAVector up() { return GBAVector(0, int2fx(1), 0); }
inline static FIXED dot(const GBAVector &left, const GBAVector &right) {
return (fxmul(left.v.x, right.v.x) + fxmul(left.v.y, right.v.y) + fxmul(left.v.z, right.v.z));
}
inline static GBAVector cross(const GBAVector &left, const GBAVector &right) {
FIXED x = fxmul(left.v.y, right.v.z) - fxmul(left.v.z, right.v.y);
FIXED y = fxmul(left.v.z, right.v.x) - fxmul(left.v.x, right.v.z);
FIXED z = fxmul(left.v.x, right.v.y) - fxmul(left.v.y, right.v.x);
return GBAVector(x, y, z);
}
inline friend GBAVector operator+(const GBAVector &one, const GBAVector &two) {
return GBAVector(one.v.x + two.v.x, one.v.y + two.v.y, one.v.z + two.v.z);
}
inline friend GBAVector operator-(const GBAVector &one, const GBAVector &two) {
return GBAVector(one.v.x - two.v.x, one.v.y - two.v.y, one.v.z - two.v.z);
}
inline friend GBAVector operator*(const GBAVector &one, const GBAVector &two) {
return GBAVector(fxmul(one.v.x, two.v.x), fxmul(one.v.y, two.v.y), fxmul(one.v.z, two.v.z));
}
inline friend GBAVector operator/(const GBAVector &one, const GBAVector &two) {
return GBAVector(fxdiv(one.v.x, two.v.x), fxdiv(one.v.y, two.v.y), fxdiv(one.v.z, two.v.z));
}
inline FIXED length() {
FIXED toRoot = fxmul(v.x, v.x) + fxmul(v.y, v.y) + fxmul(v.z, v.z);
return Sqrt(toRoot);
}
inline GBAVector negate() {
return GBAVector(-v.x, -v.y, -v.z);
}
inline GBAVector scale(int scale) {
FIXED fac = int2fx(scale);
return GBAVector(fxmul(v.x, fac), fxmul(v.y, fac), fxmul(v.z, fac));
}
inline void normalize() {
auto len = length();
if(len == 0) return;
FIXED num = fxdiv(int2fx(1), len);
v.x = fxmul(v.x, num);
v.y = fxmul(v.y, num);
v.z = fxmul(v.z, num);
}
inline FIXED x() const { return v.x; }
inline float floatX() const { return fx2float(v.x); }
inline FIXED y() const { return v.y; }
inline float floatY() const { return fx2float(v.y); }
inline FIXED z() const { return v.z; }
inline float floatZ() const { return fx2float(v.z); }
std::string to_string() {
return "(" + std::to_string(v.x) + "," + std::to_string(v.y) + ")";
}
};
#endif

View File

@ -10,13 +10,18 @@
#include <libgba-sprite-engine/gba/tonc_math_stub.h> #include <libgba-sprite-engine/gba/tonc_math_stub.h>
#else #else
#include <libgba-sprite-engine/gba/tonc_math.h> #include <libgba-sprite-engine/gba/tonc_math.h>
#endif #endif
#include <cmath>
// fixed point math things that were missing from TONC // fixed point math things that were missing from TONC
extern FIXED HALF; extern FIXED HALF;
extern FIXED ONE; extern FIXED ONE;
#define FIX12_SHIFT 12
#define FIX12_SCALE ( 1<<FIX12_SHIFT )
// ----- // -----
INLINE float gr2rad(uint grad); INLINE float gr2rad(uint grad);
INLINE FIXED gr2lut(uint grad); INLINE FIXED gr2lut(uint grad);
@ -25,6 +30,8 @@ INLINE FIXED fxrad2lut(FIXED rad);
INLINE float fx12ToFloat(FIXED fx); INLINE float fx12ToFloat(FIXED fx);
INLINE FIXED fx12Tofx8(FIXED fx12); INLINE FIXED fx12Tofx8(FIXED fx12);
INLINE FIXED fxtan(FIXED fxrad); INLINE FIXED fxtan(FIXED fxrad);
INLINE FIXED fxsin(FIXED fxrad);
INLINE FIXED fxcos(FIXED fxrad);
INLINE float rnd(float val); INLINE float rnd(float val);
// ---- impl // ---- impl
@ -50,19 +57,35 @@ INLINE FIXED fxrad2lut(FIXED rad) {
} }
INLINE float fx12ToFloat(FIXED fx) { INLINE float fx12ToFloat(FIXED fx) {
return fx / (float) (1<<12); return fx / (float) FIX12_SCALE;
}
INLINE FIXED fx12div(FIXED fa, FIXED fb) {
return ((fa)*FIX12_SCALE)/(fb);
} }
INLINE FIXED fx12Tofx8(FIXED fx12) { INLINE FIXED fx12Tofx8(FIXED fx12) {
return fx12 >> 4; return fx12 >> 4;
} }
INLINE FIXED fxsin(FIXED fxrad) {
FIXED theta = fxrad2lut(fxrad);
FIXED sin = lu_sin(theta);
return fx12Tofx8(sin);
}
INLINE FIXED fxcos(FIXED fxrad) {
FIXED theta = fxrad2lut(fxrad);
FIXED cos = lu_cos(theta);
return fx12Tofx8(cos);
}
INLINE FIXED fxtan(FIXED fxrad) { INLINE FIXED fxtan(FIXED fxrad) {
FIXED theta = fxrad2lut(fxrad); FIXED theta = fxrad2lut(fxrad);
FIXED sin = fx12Tofx8(lu_sin(theta)); FIXED sin = lu_sin(theta);
FIXED cos = fx12Tofx8(lu_cos(theta)); FIXED cos = lu_cos(theta);
return fxdiv(sin, cos); return fx12Tofx8(fx12div(sin, cos));
} }
#endif //GBA_BITMAP_ENGINE_PROJECT_MATH_H #endif //GBA_BITMAP_ENGINE_PROJECT_MATH_H

View File

@ -0,0 +1,210 @@
//
// Created by Wouter Groeneveld on 08/07/20.
//
#ifndef GBA_BITMAP_ENGINE_PROJECT_GBAMATRIX_H
#define GBA_BITMAP_ENGINE_PROJECT_GBAMATRIX_H
#define MATRIX_DIMENSION 16
#include <libgba-sprite-engine/math.h>
#include <libgba-sprite-engine/vectorfx.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
class MatrixFx {
private:
FIXED m[MATRIX_DIMENSION];
public:
inline static MatrixFx zero() { return MatrixFx(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); }
inline static MatrixFx identity() { return MatrixFx(ONE, 0, 0, 0, 0, ONE, 0, 0, 0, 0, ONE, 0, 0, 0, 0, ONE); }
inline FIXED mAt(int index) const {
return m[index];
}
MatrixFx() {}
inline static MatrixFx fromFloat(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24, float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44) {
return MatrixFx(
float2fx(m11),
float2fx(m12),
float2fx(m13),
float2fx(m14),
float2fx(m21),
float2fx(m22),
float2fx(m23),
float2fx(m24),
float2fx(m31),
float2fx(m32),
float2fx(m33),
float2fx(m34),
float2fx(m41),
float2fx(m42),
float2fx(m43),
float2fx(m44)
);
}
inline static MatrixFx fromInt(int m11, int m12, int m13, int m14, int m21, int m22, int m23, int m24, int m31, int m32, int m33, int m34, int m41, int m42, int m43, int m44) {
return MatrixFx(
int2fx(m11),
int2fx(m12),
int2fx(m13),
int2fx(m14),
int2fx(m21),
int2fx(m22),
int2fx(m23),
int2fx(m24),
int2fx(m31),
int2fx(m32),
int2fx(m33),
int2fx(m34),
int2fx(m41),
int2fx(m42),
int2fx(m43),
int2fx(m44)
);
}
MatrixFx(FIXED m11, FIXED m12, FIXED m13, FIXED m14, FIXED m21, FIXED m22, FIXED m23, FIXED m24, FIXED m31, FIXED m32, FIXED m33, FIXED m34, FIXED m41, FIXED m42, FIXED m43, FIXED m44) {
m[0] = m11;
m[1] = m12;
m[2] = m13;
m[3] = m14;
m[4] = m21;
m[5] = m22;
m[6] = m23;
m[7] = m24;
m[8] = m31;
m[9] = m32;
m[10] = m33;
m[11] = m34;
m[12] = m41;
m[13] = m42;
m[14] = m43;
m[15] = m44;
}
inline static VectorFx transformCoordinates(VectorFx vector, MatrixFx transformation) {
FIXED x = fxmul(vector.x(), transformation.mAt(0)) + fxmul(vector.y(), transformation.mAt(4)) + fxmul(vector.z(), transformation.mAt(8) + transformation.mAt(12));
FIXED y = fxmul(vector.x(), transformation.mAt(1)) + fxmul(vector.y(), transformation.mAt(5)) + fxmul(vector.z(), transformation.mAt(9) + transformation.mAt(13));
FIXED z = fxmul(vector.x(), transformation.mAt(2)) + fxmul(vector.y(), transformation.mAt(6)) + fxmul(vector.z(), transformation.mAt(10) + transformation.mAt(14));
FIXED w = fxmul(vector.x(), transformation.mAt(3)) + fxmul(vector.y(), transformation.mAt(7)) + fxmul(vector.z(), transformation.mAt(11) + transformation.mAt(15));
return VectorFx(fxdiv(x, w), fxdiv(y, w), fxdiv(z, w));
}
inline friend MatrixFx operator*(const MatrixFx &thiz, const MatrixFx &other) {
auto result = MatrixFx();
result.m[0] = fxmul(thiz.m[0], other.m[0]) + fxmul(thiz.m[1], other.m[4]) + fxmul(thiz.m[2], other.m[8]) + fxmul(thiz.m[3], other.m[12]);
result.m[1] = fxmul(thiz.m[0], other.m[1]) + fxmul(thiz.m[1], other.m[5]) + fxmul(thiz.m[2], other.m[9]) + fxmul(thiz.m[3], other.m[13]);
result.m[2] = fxmul(thiz.m[0], other.m[2]) + fxmul(thiz.m[1], other.m[6]) + fxmul(thiz.m[2], other.m[10]) + fxmul(thiz.m[3], other.m[14]);
result.m[3] = fxmul(thiz.m[0], other.m[3]) + fxmul(thiz.m[1], other.m[7]) + fxmul(thiz.m[2], other.m[11]) + fxmul(thiz.m[3], other.m[15]);
result.m[4] = fxmul(thiz.m[4], other.m[0]) + fxmul(thiz.m[5], other.m[4]) + fxmul(thiz.m[6], other.m[8]) + fxmul(thiz.m[7], other.m[12]);
result.m[5] = fxmul(thiz.m[4], other.m[1]) + fxmul(thiz.m[5], other.m[5]) + fxmul(thiz.m[6], other.m[9]) + fxmul(thiz.m[7], other.m[13]);
result.m[6] = fxmul(thiz.m[4], other.m[2]) + fxmul(thiz.m[5], other.m[6]) + fxmul(thiz.m[6], other.m[10]) + fxmul(thiz.m[7], other.m[14]);
result.m[7] = fxmul(thiz.m[4], other.m[3]) + fxmul(thiz.m[5], other.m[7]) + fxmul(thiz.m[6], other.m[11]) + fxmul(thiz.m[7], other.m[15]);
result.m[8] = fxmul(thiz.m[8], other.m[0]) + fxmul(thiz.m[9], other.m[4]) + fxmul(thiz.m[10], other.m[8]) + fxmul(thiz.m[11], other.m[12]);
result.m[9] = fxmul(thiz.m[8], other.m[1]) + fxmul(thiz.m[9], other.m[5]) + fxmul(thiz.m[10], other.m[9]) + fxmul(thiz.m[11], other.m[13]);
result.m[10] = fxmul(thiz.m[8], other.m[2]) + fxmul(thiz.m[9], other.m[6]) + fxmul(thiz.m[10], other.m[10]) + fxmul(thiz.m[11], other.m[14]);
result.m[11] = fxmul(thiz.m[8], other.m[3]) + fxmul(thiz.m[9], other.m[7]) + fxmul(thiz.m[10], other.m[11]) + fxmul(thiz.m[11], other.m[15]);
result.m[12] = fxmul(thiz.m[12], other.m[0]) + fxmul(thiz.m[13], other.m[4]) + fxmul(thiz.m[14], other.m[8]) + fxmul(thiz.m[15], other.m[12]);
result.m[13] = fxmul(thiz.m[12], other.m[1]) + fxmul(thiz.m[13], other.m[5]) + fxmul(thiz.m[14], other.m[9]) + fxmul(thiz.m[15], other.m[13]);
result.m[14] = fxmul(thiz.m[12], other.m[2]) + fxmul(thiz.m[13], other.m[6]) + fxmul(thiz.m[14], other.m[10]) + fxmul(thiz.m[15], other.m[14]);
result.m[15] = fxmul(thiz.m[12], other.m[3]) + fxmul(thiz.m[13], other.m[7]) + fxmul(thiz.m[14], other.m[11]) + fxmul(thiz.m[15], other.m[15]);
return result;
}
inline static MatrixFx lookAtLH(VectorFx eye, VectorFx target, VectorFx up) {
auto zAxis = target - eye;
zAxis.normalize();
auto xAxis = VectorFx::cross(up, zAxis);
xAxis.normalize();
auto yAxis = VectorFx::cross(zAxis, xAxis);
yAxis.normalize();
auto ex = -VectorFx::dot(xAxis, eye);
auto ey = -VectorFx::dot(yAxis, eye);
auto ez = -VectorFx::dot(zAxis, eye);
return MatrixFx(xAxis.x(), yAxis.x(), zAxis.x(), 0, xAxis.y(), yAxis.y(), zAxis.y(), 0, xAxis.z(), yAxis.z(), zAxis.z(), 0, ex, ey, ez, ONE);
}
inline static MatrixFx rotationX(FIXED angle) {
auto result = MatrixFx::zero();
auto s = fxsin(angle);
auto c = fxcos(angle);
result.m[0] = 1.0;
result.m[15] = 1.0;
result.m[5] = c;
result.m[10] = c;
result.m[9] = -s;
result.m[6] = s;
return result;
}
inline static MatrixFx rotationY(FIXED angle) {
auto result = MatrixFx::zero();
auto s = fxsin(angle);
auto c = fxcos(angle);
result.m[5] = 1.0;
result.m[15] = 1.0;
result.m[0] = c;
result.m[2] = -s;
result.m[8] = s;
result.m[10] = c;
return result;
}
inline static MatrixFx rotationZ(FIXED angle) {
auto result = MatrixFx::zero();
auto s = fxsin(angle);
auto c = fxcos(angle);
result.m[10] = 1.0;
result.m[15] = 1.0;
result.m[0] = c;
result.m[1] = s;
result.m[4] = -s;
result.m[5] = c;
return result;
}
inline static MatrixFx translation(VectorFx vec) {
auto result = MatrixFx::identity();
result.m[12] = vec.x();
result.m[13] = vec.y();
result.m[14] = vec.z();
return result;
}
inline static MatrixFx rotationYawPitchRoll(FIXED yaw, FIXED pitch, FIXED roll) {
return MatrixFx::rotationZ(roll) * MatrixFx::rotationX(pitch) * MatrixFx::rotationY(yaw);
}
inline static MatrixFx perspectiveFovLH(FIXED fov, FIXED aspect, FIXED znear, FIXED zfar) {
auto matrix = MatrixFx::zero();
FIXED tanResult = fxdiv(ONE, fxtan(fxmul(fov, HALF)));
matrix.m[0] = fxdiv(tanResult, aspect);
matrix.m[1] = matrix.m[2] = matrix.m[3] = 0;
matrix.m[5] = tanResult;
matrix.m[4] = matrix.m[6] = matrix.m[7] = 0;
matrix.m[8] = matrix.m[9] = 0;
matrix.m[10] = fxdiv(-zfar, (znear - zfar));
matrix.m[11] = ONE;
matrix.m[12] = matrix.m[13] = matrix.m[15] = 0;
matrix.m[14] = fxdiv(fxmul(znear, zfar), (znear - zfar));
return matrix;
}
};
#endif //GBA_BITMAP_ENGINE_PROJECT_GBAMATRIX_H

View File

@ -5,23 +5,32 @@
#ifndef GBA_BITMAP_ENGINE_PROJECT_MESH_H #ifndef GBA_BITMAP_ENGINE_PROJECT_MESH_H
#define GBA_BITMAP_ENGINE_PROJECT_MESH_H #define GBA_BITMAP_ENGINE_PROJECT_MESH_H
#include "gbavector.h" #include "vectorfx.h"
#include <vector> #include <vector>
#include <memory> #include <memory>
class Mesh { class Mesh {
private: private:
GBAVector position; VectorFx pos;
GBAVector rotation; VectorFx rot;
std::vector<std::unique_ptr<GBAVector>> verticesArr; std::vector<std::unique_ptr<VectorFx>> verticesArr;
public: public:
void add(GBAVector v); void add(VectorFx v);
inline std::vector<std::unique_ptr<GBAVector>> const& vertices() const { inline std::vector<std::unique_ptr<VectorFx>> const& vertices() const {
return verticesArr; return verticesArr;
} }
inline VectorFx position() { return pos; }
inline FIXED rotx() { return rot.x(); }
inline FIXED roty() { return rot.y(); }
inline FIXED rotz() { return rot.z(); }
inline void rotate(FIXED x, FIXED y) {
rot.setX(rot.x() + x);
rot.setY(rot.y() + y);
}
explicit Mesh() {} explicit Mesh() {}
Mesh(const Mesh&) = delete; Mesh(const Mesh&) = delete;
Mesh& operator=(const Mesh&) = delete; Mesh& operator=(const Mesh&) = delete;

View File

@ -8,6 +8,7 @@
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <functional> #include <functional>
#include <libgba-sprite-engine/camera.h>
#include <libgba-sprite-engine/mesh.h> #include <libgba-sprite-engine/mesh.h>
#include <libgba-sprite-engine/palette/palette_manager.h> #include <libgba-sprite-engine/palette/palette_manager.h>
@ -25,6 +26,7 @@ public:
// WHY raw pointers? they're unwrapped unique_ptrs managed by the scene implementation - will be cleaned up in engine // WHY raw pointers? they're unwrapped unique_ptrs managed by the scene implementation - will be cleaned up in engine
virtual std::vector<Mesh*> meshes() = 0; virtual std::vector<Mesh*> meshes() = 0;
virtual Camera camera() = 0;
virtual void load() = 0; virtual void load() = 0;
virtual void tick(u16 keys) = 0; virtual void tick(u16 keys) = 0;

View File

@ -0,0 +1,106 @@
//
// Created by Wouter Groeneveld on 14/12/18.
// This is a FIXED-POINT VECTOR REPRESENTATION!
// Wrapper around Tonc's VECTOR for convenience and readability.
//
#ifndef GBA_VECTOR_H_
#define GBA_VECTOR_H_
#include <string>
#include <deque>
#include <libgba-sprite-engine/gba/tonc_bios.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
class VectorFx {
private:
VECTOR v;
public:
VectorFx() : v({}) {}
VectorFx(const VectorFx& other) : v(other.v) {}
VectorFx(VECTOR v) : v(v) {}
VectorFx(FIXED x, FIXED y, FIXED z) : v({ x, y, z}) {}
static VectorFx fromInt(int x, int y, int z) {
return VectorFx(int2fx(x), int2fx(y), int2fx(z));
}
inline VectorFx toInt() {
return VectorFx(fx2int(v.x), fx2int(v.y), fx2int(v.z));
}
std::deque<VECTOR> bresenhamLineTo(VECTOR dest);
VectorFx rotateAsCenter(VectorFx point, FIXED angle);
// WHY all these inlines? performance reasons.
inline static VectorFx up() { return VectorFx::fromInt(0, 1, 0); }
inline static FIXED dot(const VectorFx &left, const VectorFx &right) {
return (fxmul(left.v.x, right.v.x) + fxmul(left.v.y, right.v.y) + fxmul(left.v.z, right.v.z));
}
inline static VectorFx cross(const VectorFx &left, const VectorFx &right) {
FIXED x = fxmul(left.v.y, right.v.z) - fxmul(left.v.z, right.v.y);
FIXED y = fxmul(left.v.z, right.v.x) - fxmul(left.v.x, right.v.z);
FIXED z = fxmul(left.v.x, right.v.y) - fxmul(left.v.y, right.v.x);
return VectorFx(x, y, z);
}
inline friend VectorFx operator+(const VectorFx &one, const VectorFx &two) {
return VectorFx(one.v.x + two.v.x, one.v.y + two.v.y, one.v.z + two.v.z);
}
inline friend VectorFx operator-(const VectorFx &one, const VectorFx &two) {
return VectorFx(one.v.x - two.v.x, one.v.y - two.v.y, one.v.z - two.v.z);
}
inline friend VectorFx operator*(const VectorFx &one, const VectorFx &two) {
return VectorFx(fxmul(one.v.x, two.v.x), fxmul(one.v.y, two.v.y), fxmul(one.v.z, two.v.z));
}
inline friend VectorFx operator/(const VectorFx &one, const VectorFx &two) {
return VectorFx(fxdiv(one.v.x, two.v.x), fxdiv(one.v.y, two.v.y), fxdiv(one.v.z, two.v.z));
}
inline FIXED length() {
FIXED toRoot = fxmul(v.x, v.x) + fxmul(v.y, v.y) + fxmul(v.z, v.z);
// tonc's fx() methods are .8fx, and BIOS needs .16 (results are .8??)
return Sqrt(toRoot << 8);
}
inline VectorFx negate() {
return VectorFx(-v.x, -v.y, -v.z);
}
inline VectorFx scale(int scale) {
FIXED fac = int2fx(scale);
return VectorFx(fxmul(v.x, fac), fxmul(v.y, fac), fxmul(v.z, fac));
}
inline void normalize() {
auto len = length();
if(len == 0) return;
FIXED num = fxdiv(int2fx(1), len);
v.x = fxmul(v.x, num);
v.y = fxmul(v.y, num);
v.z = fxmul(v.z, num);
}
inline FIXED x() const { return v.x; }
inline float floatX() const { return fx2float(v.x); }
inline void setX(FIXED x) { v.x = x; }
inline FIXED y() const { return v.y; }
inline float floatY() const { return fx2float(v.y); }
inline void setY(FIXED y) { v.y = y; }
inline FIXED z() const { return v.z; }
inline float floatZ() const { return fx2float(v.z); }
inline void setZ(FIXED z) { v.z = z; }
std::string to_string() {
return "(" + std::to_string(v.x) + "," + std::to_string(v.y) + "," + std::to_string(v.z) + ")";
}
std::string to_stringfl() {
return "(" + std::to_string(fx2float(v.x)) + "," + std::to_string(fx2float(v.y)) + "," + std::to_string(fx2float(v.z)) + ")";
}
};
#endif

View File

@ -6,6 +6,7 @@
#include <libgba-sprite-engine/gba_engine.h> #include <libgba-sprite-engine/gba_engine.h>
#include <cstring> #include <cstring>
#include <libgba-sprite-engine/gba/tonc_core.h> #include <libgba-sprite-engine/gba/tonc_core.h>
#include <libgba-sprite-engine/matrixfx.h>
std::unique_ptr<SoundControl> GBAEngine::activeChannelA; std::unique_ptr<SoundControl> GBAEngine::activeChannelA;
std::unique_ptr<SoundControl> GBAEngine::activeChannelB; std::unique_ptr<SoundControl> GBAEngine::activeChannelB;
@ -151,11 +152,28 @@ inline void GBAEngine::plotPixel(int x, int y, u8 clrId) {
} }
} }
inline VectorFx GBAEngine::project(VectorFx coord, MatrixFx transMat) {
auto point = MatrixFx::transformCoordinates(coord, transMat);
auto x = fxmul(point.x(), GBA_SCREEN_WIDTH_FX) + fxdiv(GBA_SCREEN_WIDTH_FX, 2.0);
auto y = fxmul(-point.y(), GBA_SCREEN_HEIGHT_FX) + fxdiv(GBA_SCREEN_HEIGHT_FX, 2.0);
return VectorFx(x, y, 0);
}
// does the mesh rendering - to write mem before flipping // does the mesh rendering - to write mem before flipping
void GBAEngine::render() { void GBAEngine::render() {
auto viewMatrix = MatrixFx::lookAtLH(currentCamera.getPosition(), currentCamera.getTarget(), VectorFx::up());
// TODO float fxes and other stuff out of render loop?
auto projectionMatrix = MatrixFx::perspectiveFovLH(float2fx(0.78), fxdiv(GBA_SCREEN_WIDTH_FX, GBA_SCREEN_HEIGHT_FX), float2fx(0.01), ONE);
for(auto& mesh :currentScene->meshes()) { for(auto& mesh :currentScene->meshes()) {
auto worldMatrix = MatrixFx::rotationYawPitchRoll(mesh->roty(), mesh->rotx(), mesh->rotz()) * MatrixFx::translation(mesh->position());
auto transformMatrix = worldMatrix * viewMatrix * projectionMatrix;
for(auto& vertex : mesh->vertices()) { for(auto& vertex : mesh->vertices()) {
plotPixel(vertex->x(), vertex->y(), 1); auto projectedPoint = project(*vertex.get(), transformMatrix).toInt();
plotPixel(projectedPoint.x(), projectedPoint.y(), 1);
} }
} }
} }
@ -183,4 +201,5 @@ void GBAEngine::setScene(Scene* scene) {
bgPalette->persist(); bgPalette->persist();
this->currentScene = scene; this->currentScene = scene;
this->currentCamera = this->currentScene->camera();
} }

View File

@ -6,7 +6,7 @@
#include <memory> #include <memory>
void Mesh::add(GBAVector v) { void Mesh::add(VectorFx v) {
verticesArr.push_back(std::unique_ptr<GBAVector>(new GBAVector(v))); verticesArr.push_back(std::unique_ptr<VectorFx>(new VectorFx(v)));
} }

View File

@ -2,9 +2,9 @@
// Created by Wouter Groeneveld on 14/12/18. // Created by Wouter Groeneveld on 14/12/18.
// //
#include <libgba-sprite-engine/gbavector.h> #include <libgba-sprite-engine/vectorfx.h>
GBAVector GBAVector::rotateAsCenter(GBAVector point, FIXED angle) { VectorFx VectorFx::rotateAsCenter(VectorFx point, FIXED angle) {
auto center = this->v; auto center = this->v;
s32 centerx = center.x, centery = center.y; s32 centerx = center.x, centery = center.y;
s32 defaultx = point.x(), defaulty = point.y(); s32 defaultx = point.x(), defaulty = point.y();
@ -16,16 +16,16 @@ GBAVector GBAVector::rotateAsCenter(GBAVector point, FIXED angle) {
// possibilities: instead of between [-1.0, 1.0] it's between [-256, +256] // possibilities: instead of between [-1.0, 1.0] it's between [-256, +256]
// 90° rotation in inversed y-axis needs to flip sin sign // 90° rotation in inversed y-axis needs to flip sin sign
/* /*
return GBAVector({ return VectorFx({
( cos * (defaultx - centerx) + sin * (defaulty - centery) + (centerx << 8)) >> 8, ( cos * (defaultx - centerx) + sin * (defaulty - centery) + (centerx << 8)) >> 8,
(-sin * (defaultx - centerx) + cos * (defaulty - centery) + (centery << 8)) >> 8}); (-sin * (defaultx - centerx) + cos * (defaulty - centery) + (centery << 8)) >> 8});
*/ */
return GBAVector({ return VectorFx({
(fxmul(cos, (defaultx - centerx)) + fxmul(sin, (defaulty - centery)) + (centerx << 8)) >> 8, (fxmul(cos, (defaultx - centerx)) + fxmul(sin, (defaulty - centery)) + (centerx << 8)) >> 8,
(fxmul(-sin, (defaultx - centerx)) + fxmul(cos, (defaulty - centery) + (centery << 8))) >> 8}); (fxmul(-sin, (defaultx - centerx)) + fxmul(cos, (defaulty - centery) + (centery << 8))) >> 8});
} }
std::deque<VECTOR> GBAVector::bresenhamLineTo(VECTOR dest) { std::deque<VECTOR> VectorFx::bresenhamLineTo(VECTOR dest) {
// https://www.coranac.com/tonc/text/bitmaps.htm - Bresenham's line algorithm with fixed points // https://www.coranac.com/tonc/text/bitmaps.htm - Bresenham's line algorithm with fixed points
VECTOR src = this->v; VECTOR src = this->v;
VECTOR step, delta; VECTOR step, delta;

View File

@ -34,7 +34,7 @@ add_executable(unittest
../engine/src/palette/combined_palette.cpp ../engine/src/palette/combined_palette.cpp
../engine/src/timer.cpp ../engine/src/timer.cpp
../engine/src/math.cpp ../engine/src/math.cpp
../engine/src/gbavector.cpp ../engine/src/vectorfx.cpp
timertest.cpp gbavectortest.cpp gbamatrixtest.cpp fixedpoinmathtest.cpp) timertest.cpp vectorfxtest.cpp matrixfxtest.cpp fixedpoinmathtest.cpp)
target_link_libraries(unittest ${GTEST_LIBRARY}/build/libgtest.a ${GTEST_LIBRARY}/build/libgtest_main.a) target_link_libraries(unittest ${GTEST_LIBRARY}/build/libgtest.a ${GTEST_LIBRARY}/build/libgtest_main.a)

View File

@ -1,50 +0,0 @@
//
// Created by Wouter Groeneveld on 08/07/20.
//
#include <gtest/gtest.h>
#include <math.h>
#include <libgba-sprite-engine/gbamatrix.h>
#include <libgba-sprite-engine/math.h>
class GBAMatrixSuite : public ::testing::Test {
protected:
virtual void TearDown() {
}
virtual void SetUp() {
}
};
TEST_F(GBAMatrixSuite, PerspectiveFovLH_TestData) {
/* IN:
* fov 0.78, aspect 1.6, znear 0.01, zfar 1
* OUT:
0: 1.520478108791285
1: 0
2: 0
3: 0
4: 0
5: 2.4327649740660564
6: 0
7: 0
8: 0
9: 0
10: 1.0101010101010102
11: 1
12: 0
13: 0
14: -0.010101010101010102
15: 0
*/
auto result = GBAMatrix::perspectiveFovLH(float2fx(0.78), float2fx(1.6), float2fx(0.01), float2fx(1));
// hmm... is this correct? rounding issues aside?
ASSERT_EQ(rnd(fx2float(result.mAt(0))), 1.565f);
ASSERT_EQ(fx2float(result.mAt(1)), 0);
ASSERT_EQ(rnd(fx2float(result.mAt(5))), 2.505f);
ASSERT_EQ(rnd(fx2float(result.mAt(10))), 1.005f);
ASSERT_EQ(rnd(fx2float(result.mAt(14))), -0.005f);
ASSERT_EQ(fx2float(result.mAt(15)), 0);
}

96
test/matrixfxtest.cpp Normal file
View File

@ -0,0 +1,96 @@
//
// Created by Wouter Groeneveld on 08/07/20.
//
#include <gtest/gtest.h>
#include <math.h>
#include <libgba-sprite-engine/matrixfx.h>
#include <libgba-sprite-engine/math.h>
class MatrixFxSuite : public ::testing::Test {
protected:
virtual void TearDown() {
}
virtual void SetUp() {
}
};
INLINE float rnd2(float val) {
return (float) ((std::floor(val * 10) + .5) / 10);
}
void assertMatrix(MatrixFx expected, MatrixFx actual) {
for(int i = 0; i < MATRIX_DIMENSION; i++) {
auto expect = expected.mAt(i);
auto act = actual.mAt(i);
// WHY check the rounded floats instead of fixed numbers? conversion issues. -256 and -257 should be 'equal'
float expectFl = rnd2(fx2float(expect));
float actFl = rnd2(fx2float(act));
ASSERT_EQ(expectFl, actFl) << "M[" << i << "] does not match: (exp, act) " << expect << ", " << act << " - floats: " << expectFl << ", " << actFl;
}
}
TEST_F(MatrixFxSuite, lookAtLH_TestData) {
/*
* IN:
* eye 0, 0, 10
* taget: 0, 0, 0
* up: 0, 1, 0
* OUT:
0: -1
1: 0
2: 0
3: 0
4: 0
5: 1
6: 0
7: 0
8: 0
9: 0
10: -1
11: 0
12: -0
13: -0
14: 10
15: 1
*/
auto eye = VectorFx::fromInt(0, 0, 10);
auto target = VectorFx::fromInt(0, 0, 0);
auto up = VectorFx::up();
auto result = MatrixFx::lookAtLH(eye, target, up);
auto expected = MatrixFx(-257, 0, 0, 0, 0, 256, 0, 0, 0, 0, -250, 0, 0, 0, 2500, 256);
assertMatrix(expected, result);
}
TEST_F(MatrixFxSuite, PerspectiveFovLH_TestData) {
/* IN:
* fov 0.78, aspect 1.6, znear 0.01, zfar 1
* OUT:
0: 1.520478108791285
1: 0
2: 0
3: 0
4: 0
5: 2.4327649740660564
6: 0
7: 0
8: 0
9: 0
10: 1.0101010101010102
11: 1
12: 0
13: 0
14: -0.010101010101010102
15: 0
*/
auto result = MatrixFx::perspectiveFovLH(float2fx(0.78), float2fx(1.6), float2fx(0.01), float2fx(1));
auto expected = MatrixFx::fromFloat(1.565f, 0, 0, 0, 0, 2.505f, 0, 0, 0, 0, 1.005f, 1, 0, 0, -0.005f, 0);
assertMatrix(expected, result);
}

View File

@ -12,13 +12,15 @@ namespace externMath {
float root(float num) { float root(float num) {
return sqrt(num); return sqrt(num);
return 0;
} }
} }
u32 Sqrt(u32 num) { u32 Sqrt(u32 num16fx) {
return float2fx(externMath::root(fx2float(num))); // tonc's fx() methods are .8fx, and BIOS needs .16 (results are .8??)
// this means we expect the input to be .16f
float numfloat = num16fx / ( (float)( 1<<16 ));
return float2fx(externMath::root(numfloat));
} }

View File

@ -5,23 +5,37 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <libgba-sprite-engine/gbavector.h> #include <libgba-sprite-engine/vectorfx.h>
#include "tonc_bios_stub.h" #include "tonc_bios_stub.h"
class GBAVectorSuite : public ::testing::Test { class VectorFxSuite : public ::testing::Test {
protected: protected:
GBAVector vector; VectorFx vector;
virtual void TearDown() { virtual void TearDown() {
} }
virtual void SetUp() { virtual void SetUp() {
vector = GBAVector({120, 80}); vector = VectorFx({120, 80});
} }
}; };
TEST_F(GBAVectorSuite, ToAndFromFixedIntWorks) { INLINE float rnd2(float val) {
auto zAxis = GBAVector::fromInt(0, 0, -10); return (float) ((std::floor(val * 10) + .5) / 10);
}
void assertVector(VectorFx expected, VectorFx actual) {
auto str = "[fx: " + actual.to_string() + ", fl: " + actual.to_stringfl() + "] , [fx: " + expected.to_string() + ", fl: " + expected.to_stringfl() + "]";
// WHY use rounding instead of checking fixed-point itself?
// because of a lot of rounding errors... -250 (-0.97) is supposed to be -256 (-1)...
ASSERT_EQ(rnd2(fx2float(actual.x())), rnd2(fx2float(expected.x()))) << "x incorrect: (act, exp) " << str;
ASSERT_EQ(rnd2(fx2float(actual.y())), rnd2(fx2float(actual.y()))) << "y incorrect: (act, exp) " << str;
ASSERT_EQ(rnd2(fx2float(actual.z())), rnd2(fx2float(actual.z()))) << "z incorrect: (act, exp) " << str;
}
TEST_F(VectorFxSuite, ToAndFromFixedIntWorks) {
auto zAxis = VectorFx::fromInt(0, 0, -10);
ASSERT_EQ(zAxis.y(), 0); ASSERT_EQ(zAxis.y(), 0);
ASSERT_EQ(zAxis.z(), -2560); ASSERT_EQ(zAxis.z(), -2560);
auto toInt = zAxis.toInt(); auto toInt = zAxis.toInt();
@ -29,41 +43,49 @@ TEST_F(GBAVectorSuite, ToAndFromFixedIntWorks) {
ASSERT_EQ(toInt.z(), -10); ASSERT_EQ(toInt.z(), -10);
} }
TEST_F(GBAVectorSuite, Normalize) { TEST_F(VectorFxSuite, Normalize) {
// based on Babylon Math debugging examples // based on Babylon Math debugging examples
auto zAxis = GBAVector::fromInt(0, 0, -10); auto zAxis = VectorFx::fromInt(0, 0, -10);
zAxis.normalize(); auto l = zAxis.length();
float z = fx2float(zAxis.z());
auto result = zAxis.toInt();
ASSERT_EQ(result.x(), 0); auto expected = VectorFx::fromInt(0, 0, -1);
ASSERT_EQ(result.y(), 0); zAxis.normalize();
ASSERT_EQ((std::floor(z * 100) + .5) / 100, -0.975);
//ASSERT_EQ(result.z(), -1); assertVector(expected, zAxis);
// TODO will be rounded to zero. problematic later on?
} }
TEST_F(GBAVectorSuite, MinDot) { TEST_F(VectorFxSuite, MinDot) {
auto zAxis = GBAVector::fromInt(0, 0, -1); auto zAxis = VectorFx::fromInt(0, 0, -1);
auto eye = GBAVector::fromInt(0, 0, 10); auto eye = VectorFx::fromInt(0, 0, 10);
auto result = fx2int(-GBAVector::dot(zAxis, eye)); auto result = fx2int(-VectorFx::dot(zAxis, eye));
ASSERT_EQ(result, 10); ASSERT_EQ(result, 10);
} }
TEST_F(GBAVectorSuite, Cross) { TEST_F(VectorFxSuite, Cross_AssertToInt) {
auto one = GBAVector::fromInt(0, 1, 0); auto one = VectorFx::fromInt(0, 1, 0);
auto two = GBAVector::fromInt(0, 0, -1); auto two = VectorFx::fromInt(0, 0, -1);
auto result = GBAVector::cross(one, two).toInt(); auto result = VectorFx::cross(one, two).toInt();
ASSERT_EQ(result.x(), -1); ASSERT_EQ(result.x(), -1);
ASSERT_EQ(result.y(), 0); ASSERT_EQ(result.y(), 0);
ASSERT_EQ(result.z(), 0); ASSERT_EQ(result.z(), 0);
} }
TEST_F(GBAVectorSuite, AddTwoVectors) { TEST_F(VectorFxSuite, Cross_AssertFixed) {
GBAVector one(1, 2, 3); auto one = VectorFx::fromInt(0, 1, 0);
GBAVector two(4, 5, 6); auto two = VectorFx::fromInt(0, 0, -1);
auto result = VectorFx::cross(one, two);
auto expected = VectorFx::fromInt(-1, 0, 0);
ASSERT_EQ(result.x(), expected.x());
ASSERT_EQ(result.y(), expected.y());
ASSERT_EQ(result.z(), expected.z());
}
TEST_F(VectorFxSuite, AddTwoVectors) {
VectorFx one(1, 2, 3);
VectorFx two(4, 5, 6);
auto result = one + two; auto result = one + two;
ASSERT_EQ(result.x(), 5); ASSERT_EQ(result.x(), 5);
@ -71,9 +93,9 @@ TEST_F(GBAVectorSuite, AddTwoVectors) {
ASSERT_EQ(result.z(), 9); ASSERT_EQ(result.z(), 9);
} }
TEST_F(GBAVectorSuite, SubstractTwoVectors) { TEST_F(VectorFxSuite, SubstractTwoVectors) {
GBAVector one(1, 2, 3); VectorFx one(1, 2, 3);
GBAVector two(4, 5, 6); VectorFx two(4, 5, 6);
auto result = one - two; auto result = one - two;
ASSERT_EQ(result.x(), -3); ASSERT_EQ(result.x(), -3);
@ -81,21 +103,21 @@ TEST_F(GBAVectorSuite, SubstractTwoVectors) {
ASSERT_EQ(result.z(), -3); ASSERT_EQ(result.z(), -3);
} }
TEST_F(GBAVectorSuite, LengthSqrtOfAllPowItself) { TEST_F(VectorFxSuite, LengthSqrtOfAllPowItself) {
auto one = GBAVector::fromInt(1, 2, 3); // 1*1 + 2*2 + 3*3 = 14 auto one = VectorFx::fromInt(1, 2, 3); // 1*1 + 2*2 + 3*3 = 14
auto result = fx2float(one.length()); auto result = fx2float(one.length());
ASSERT_EQ(3.735, (std::floor(result * 100) + .5) / 100); ASSERT_EQ(3.735, (std::floor(result * 100) + .5) / 100);
auto negative = GBAVector::fromInt(0, 0, -10); auto negative = VectorFx::fromInt(0, 0, -10);
int negResult = fx2int(negative.length()); int negResult = fx2int(negative.length());
ASSERT_EQ(fx2int(fxmul(int2fx(-10), int2fx(-10))), 100); ASSERT_EQ(fx2int(fxmul(int2fx(-10), int2fx(-10))), 100);
ASSERT_EQ(negResult, 10); ASSERT_EQ(negResult, 10);
} }
TEST_F(GBAVectorSuite, DivideTwoVectors) { TEST_F(VectorFxSuite, DivideTwoVectors) {
auto one = GBAVector::fromInt(1, 2, 3); auto one = VectorFx::fromInt(1, 2, 3);
auto two = GBAVector::fromInt(4, 10, 6); auto two = VectorFx::fromInt(4, 10, 6);
auto result = (two / one).toInt(); auto result = (two / one).toInt();
ASSERT_EQ(result.x(), 4); ASSERT_EQ(result.x(), 4);
@ -103,9 +125,9 @@ TEST_F(GBAVectorSuite, DivideTwoVectors) {
ASSERT_EQ(result.z(), 2); ASSERT_EQ(result.z(), 2);
} }
TEST_F(GBAVectorSuite, MultiplyTwoVectors) { TEST_F(VectorFxSuite, MultiplyTwoVectors) {
auto one = GBAVector::fromInt(1, 2, 3); auto one = VectorFx::fromInt(1, 2, 3);
auto two = GBAVector::fromInt(4, 10, 6); auto two = VectorFx::fromInt(4, 10, 6);
auto result = (two * one).toInt(); auto result = (two * one).toInt();
ASSERT_EQ(result.x(), 4); ASSERT_EQ(result.x(), 4);
@ -113,8 +135,8 @@ TEST_F(GBAVectorSuite, MultiplyTwoVectors) {
ASSERT_EQ(result.z(), 18); ASSERT_EQ(result.z(), 18);
} }
TEST_F(GBAVectorSuite, ScaleAVector) { TEST_F(VectorFxSuite, ScaleAVector) {
auto one = GBAVector::fromInt(1, 2, 3); auto one = VectorFx::fromInt(1, 2, 3);
auto result = one.scale(2).toInt(); auto result = one.scale(2).toInt();
ASSERT_EQ(result.x(), 2); ASSERT_EQ(result.x(), 2);
@ -127,44 +149,44 @@ uint grad2rad(uint grad) {
return fxdiv(fxmul(int2fx(grad), float2fx(M_PI)), int2fx(180)); return fxdiv(fxmul(int2fx(grad), float2fx(M_PI)), int2fx(180));
} }
TEST_F(GBAVectorSuite, Rotate_FromBottomHalf_0_Degrees) { TEST_F(VectorFxSuite, Rotate_FromBottomHalf_0_Degrees) {
auto bottomHalf = GBAVector::fromInt(120, 200, 0); auto bottomHalf = VectorFx::fromInt(120, 200, 0);
auto vector = GBAVector::fromInt(120, 80, 0); auto vector = VectorFx::fromInt(120, 80, 0);
auto result = vector.rotateAsCenter(bottomHalf, grad2rad(0)).toInt(); auto result = vector.rotateAsCenter(bottomHalf, grad2rad(0)).toInt();
ASSERT_EQ(120, result.x()); ASSERT_EQ(120, result.x());
ASSERT_EQ(200, result.y()); ASSERT_EQ(200, result.y());
} }
TEST_F(GBAVectorSuite, Rotate_FromBottomHalf_90_Degrees) { TEST_F(VectorFxSuite, Rotate_FromBottomHalf_90_Degrees) {
auto bottomHalf = GBAVector::fromInt(120, 200, 0); auto bottomHalf = VectorFx::fromInt(120, 200, 0);
auto vector = GBAVector::fromInt(120, 80, 0); auto vector = VectorFx::fromInt(120, 80, 0);
auto result = vector.rotateAsCenter(bottomHalf, grad2rad(90)).toInt(); auto result = vector.rotateAsCenter(bottomHalf, grad2rad(90)).toInt();
ASSERT_EQ(240, result.x()); ASSERT_EQ(240, result.x());
ASSERT_EQ(80, result.y()); ASSERT_EQ(80, result.y());
} }
TEST_F(GBAVectorSuite, Rotate_FromBottomHalf_180_Degrees) { TEST_F(VectorFxSuite, Rotate_FromBottomHalf_180_Degrees) {
auto bottomHalf = GBAVector::fromInt(120, 200, 0); auto bottomHalf = VectorFx::fromInt(120, 200, 0);
auto vector = GBAVector::fromInt(120, 80, 0); auto vector = VectorFx::fromInt(120, 80, 0);
auto result = vector.rotateAsCenter(bottomHalf, grad2rad(180)).toInt(); auto result = vector.rotateAsCenter(bottomHalf, grad2rad(180)).toInt();
ASSERT_EQ(120, result.x()); ASSERT_EQ(120, result.x());
ASSERT_EQ(-40, result.y()); ASSERT_EQ(-40, result.y());
} }
TEST_F(GBAVectorSuite, Rotate_FromBottomHalf_270_Degrees) { TEST_F(VectorFxSuite, Rotate_FromBottomHalf_270_Degrees) {
auto bottomHalf = GBAVector::fromInt(120, 200, 0); auto bottomHalf = VectorFx::fromInt(120, 200, 0);
auto vector = GBAVector::fromInt(120, 80, 0); auto vector = VectorFx::fromInt(120, 80, 0);
auto result = vector.rotateAsCenter(bottomHalf, grad2rad(270)).toInt(); auto result = vector.rotateAsCenter(bottomHalf, grad2rad(270)).toInt();
ASSERT_EQ(0, result.x()); ASSERT_EQ(0, result.x());
ASSERT_EQ(80, result.y()); ASSERT_EQ(80, result.y());
} }
// ---- // // ---- //
TEST_F(GBAVectorSuite, ToString) { TEST_F(VectorFxSuite, ToString) {
ASSERT_EQ(std::string("(120,80)"), vector.to_string()); ASSERT_EQ(std::string("(120,80,0)"), vector.to_string());
} }
TEST_F(GBAVectorSuite, LineBetween_Diagonal_ToTopRightCorner) { TEST_F(VectorFxSuite, LineBetween_Diagonal_ToTopRightCorner) {
auto points = vector.bresenhamLineTo({240, 0}); auto points = vector.bresenhamLineTo({240, 0});
ASSERT_EQ(121, points.size()); ASSERT_EQ(121, points.size());
VECTOR pt; VECTOR pt;
@ -184,7 +206,7 @@ TEST_F(GBAVectorSuite, LineBetween_Diagonal_ToTopRightCorner) {
ASSERT_EQ(13, pt.y); ASSERT_EQ(13, pt.y);
} }
TEST_F(GBAVectorSuite, LineBetween_Diagonal_ToTopLeftCorner) { TEST_F(VectorFxSuite, LineBetween_Diagonal_ToTopLeftCorner) {
auto points = vector.bresenhamLineTo({0, 0}); auto points = vector.bresenhamLineTo({0, 0});
ASSERT_EQ(121, points.size()); ASSERT_EQ(121, points.size());
VECTOR pt; VECTOR pt;
@ -204,7 +226,7 @@ TEST_F(GBAVectorSuite, LineBetween_Diagonal_ToTopLeftCorner) {
ASSERT_EQ(13, pt.y); ASSERT_EQ(13, pt.y);
} }
TEST_F(GBAVectorSuite, LineBetween_Diagonal_ToBottomLeftCorner) { TEST_F(VectorFxSuite, LineBetween_Diagonal_ToBottomLeftCorner) {
auto points = vector.bresenhamLineTo({0, 160}); auto points = vector.bresenhamLineTo({0, 160});
ASSERT_EQ(121, points.size()); ASSERT_EQ(121, points.size());
VECTOR pt; VECTOR pt;
@ -224,7 +246,7 @@ TEST_F(GBAVectorSuite, LineBetween_Diagonal_ToBottomLeftCorner) {
ASSERT_EQ(147, pt.y); ASSERT_EQ(147, pt.y);
} }
TEST_F(GBAVectorSuite, LineBetween_Diagonal_ToBottomRightCorner) { TEST_F(VectorFxSuite, LineBetween_Diagonal_ToBottomRightCorner) {
auto points = vector.bresenhamLineTo({240, 160}); auto points = vector.bresenhamLineTo({240, 160});
ASSERT_EQ(121, points.size()); ASSERT_EQ(121, points.size());
VECTOR pt; VECTOR pt;
@ -244,7 +266,7 @@ TEST_F(GBAVectorSuite, LineBetween_Diagonal_ToBottomRightCorner) {
ASSERT_EQ(147, pt.y); ASSERT_EQ(147, pt.y);
} }
TEST_F(GBAVectorSuite, LineBetween_Horizontal_FromCenterToHalfYZeroX) { TEST_F(VectorFxSuite, LineBetween_Horizontal_FromCenterToHalfYZeroX) {
auto points = vector.bresenhamLineTo({0, 80}); auto points = vector.bresenhamLineTo({0, 80});
ASSERT_EQ(121, points.size()); ASSERT_EQ(121, points.size());
for(int i = 0; i <= 120; i++) { for(int i = 0; i <= 120; i++) {
@ -255,7 +277,7 @@ TEST_F(GBAVectorSuite, LineBetween_Horizontal_FromCenterToHalfYZeroX) {
} }
} }
TEST_F(GBAVectorSuite, LineBetween_Vertical_FromCenterToHalfXFullY) { TEST_F(VectorFxSuite, LineBetween_Vertical_FromCenterToHalfXFullY) {
auto points = vector.bresenhamLineTo({120, 160}); auto points = vector.bresenhamLineTo({120, 160});
ASSERT_EQ(81, points.size()); ASSERT_EQ(81, points.size());
for(int i = 0; i <= (160 - 80); i++) { for(int i = 0; i <= (160 - 80); i++) {