diff --git a/demos/demo1-wireframes/src/wirescene.cpp b/demos/demo1-wireframes/src/wirescene.cpp index cd9a825..d4f50ea 100644 --- a/demos/demo1-wireframes/src/wirescene.cpp +++ b/demos/demo1-wireframes/src/wirescene.cpp @@ -7,13 +7,33 @@ #include "wirescene.h" -const unsigned short pal[3] __attribute__((aligned(4))) = { +const unsigned short pal[4] __attribute__((aligned(4))) = { 0x0000, 0xFFFF, 0x3AE2 }; +std::vector WireScene::meshes() { + return { cube.get() }; +} + void WireScene::load() { foregroundPalette = std::unique_ptr(new ForegroundPaletteManager()); backgroundPalette = std::unique_ptr(new BackgroundPaletteManager(pal, sizeof(pal))); + + cube = std::unique_ptr(new Mesh()); + cube->add(GBAVector(1, 1, 1)); + cube->add(GBAVector(10, 10, 1)); + cube->add(GBAVector(20, 20, 1)); + cube->add(GBAVector(30, 30, 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)); + cube->add(GBAVector(1, 1, -1)); + cube->add(GBAVector(1, -1, 1)); + cube->add(GBAVector(-1, -1, -1)); + */ } void WireScene::tick(u16 keys) { diff --git a/demos/demo1-wireframes/src/wirescene.h b/demos/demo1-wireframes/src/wirescene.h index 93e7fb4..b29ff45 100644 --- a/demos/demo1-wireframes/src/wirescene.h +++ b/demos/demo1-wireframes/src/wirescene.h @@ -6,9 +6,12 @@ #define GBA_SPRITE_ENGINE_SAMPLE_START_SCENE_H #include +#include +#include class WireScene : public Scene { private: + std::unique_ptr cube; public: @@ -16,6 +19,7 @@ public: void load() override; void tick(u16 keys) override; + std::vector meshes() override; }; diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 31cd043..aca0f53 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -9,7 +9,7 @@ add_library(${PROJECT_NAME} src/gba/sin_lut.s src/gba/tonc_bios.s src/gba_engine.cpp - src/sound_control.cpp src/scene.cpp src/timer.cpp include/libgba-sprite-engine/gbavector.h src/gbavector.cpp) + src/sound_control.cpp src/scene.cpp src/timer.cpp src/gbavector.cpp src/mesh.cpp) target_include_directories(${PROJECT_NAME} PUBLIC $ diff --git a/engine/include/libgba-sprite-engine/GBAMatrix.h b/engine/include/libgba-sprite-engine/GBAMatrix.h new file mode 100644 index 0000000..c928dc0 --- /dev/null +++ b/engine/include/libgba-sprite-engine/GBAMatrix.h @@ -0,0 +1,74 @@ +// +// 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 + +#ifdef CODE_COMPILED_AS_PART_OF_TEST + #include + #else + #include +#endif + +FIXED HALF = float2fx(0.5); +FIXED ONE = int2fx(1); + +class GBAMatrix { +private: + FIXED m[MATRIX_DIMENSION]; + +public: + inline static FIXED tan(FIXED angle) { + FIXED sin = lu_sin(angle) >> 4; + FIXED cos = lu_cos(angle) >> 4; + + return fxdiv(sin, cos); + } + + 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, tan(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 diff --git a/engine/include/libgba-sprite-engine/gba/tonc_math_stub.h b/engine/include/libgba-sprite-engine/gba/tonc_math_stub.h index 0e0f85b..8b5a7cd 100644 --- a/engine/include/libgba-sprite-engine/gba/tonc_math_stub.h +++ b/engine/include/libgba-sprite-engine/gba/tonc_math_stub.h @@ -213,9 +213,84 @@ INLINE FIXED fxdiv64(FIXED fa, FIXED fb); /*! \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 +// +// Sine lut; 512 entries, 12 fixeds +// +// EDIT Wouter Groeneveld, 2020-07-08: +// generated by sin lut generator from https://www.coranac.com/tonc/text/fixed.htm +// use original lu_sin/cos functions and values, but don't link with sin_lut.s (ARM ASM not compatible with Gtest CXX) +#define SIN_LUT_SIZE 512 +const short sin_lut[SIN_LUT_SIZE]= + { + 0x0000, 0x0032, 0x0064, 0x0096, 0x00C8, 0x00FB, 0x012D, 0x015F, + 0x0191, 0x01C3, 0x01F5, 0x0227, 0x0259, 0x028A, 0x02BC, 0x02ED, + 0x031F, 0x0350, 0x0381, 0x03B2, 0x03E3, 0x0413, 0x0444, 0x0474, + 0x04A5, 0x04D5, 0x0504, 0x0534, 0x0563, 0x0593, 0x05C2, 0x05F0, + 0x061F, 0x064D, 0x067B, 0x06A9, 0x06D7, 0x0704, 0x0731, 0x075E, + 0x078A, 0x07B7, 0x07E2, 0x080E, 0x0839, 0x0864, 0x088F, 0x08B9, + 0x08E3, 0x090D, 0x0936, 0x095F, 0x0987, 0x09B0, 0x09D7, 0x09FF, + 0x0A26, 0x0A4D, 0x0A73, 0x0A99, 0x0ABE, 0x0AE3, 0x0B08, 0x0B2C, + 0x0B50, 0x0B73, 0x0B96, 0x0BB8, 0x0BDA, 0x0BFC, 0x0C1D, 0x0C3E, + 0x0C5E, 0x0C7D, 0x0C9D, 0x0CBB, 0x0CD9, 0x0CF7, 0x0D14, 0x0D31, + 0x0D4D, 0x0D69, 0x0D84, 0x0D9F, 0x0DB9, 0x0DD2, 0x0DEB, 0x0E04, + 0x0E1C, 0x0E33, 0x0E4A, 0x0E60, 0x0E76, 0x0E8B, 0x0EA0, 0x0EB4, + 0x0EC8, 0x0EDB, 0x0EED, 0x0EFF, 0x0F10, 0x0F21, 0x0F31, 0x0F40, + 0x0F4F, 0x0F5D, 0x0F6B, 0x0F78, 0x0F85, 0x0F91, 0x0F9C, 0x0FA7, + 0x0FB1, 0x0FBA, 0x0FC3, 0x0FCB, 0x0FD3, 0x0FDA, 0x0FE1, 0x0FE7, + 0x0FEC, 0x0FF0, 0x0FF4, 0x0FF8, 0x0FFB, 0x0FFD, 0x0FFE, 0x0FFF, + 0x0FFF, 0x0FFF, 0x0FFE, 0x0FFD, 0x0FFB, 0x0FF8, 0x0FF4, 0x0FF0, + 0x0FEC, 0x0FE7, 0x0FE1, 0x0FDA, 0x0FD3, 0x0FCB, 0x0FC3, 0x0FBA, + 0x0FB1, 0x0FA7, 0x0F9C, 0x0F91, 0x0F85, 0x0F78, 0x0F6B, 0x0F5D, + 0x0F4F, 0x0F40, 0x0F31, 0x0F21, 0x0F10, 0x0EFF, 0x0EED, 0x0EDB, + 0x0EC8, 0x0EB4, 0x0EA0, 0x0E8B, 0x0E76, 0x0E60, 0x0E4A, 0x0E33, + 0x0E1C, 0x0E04, 0x0DEB, 0x0DD2, 0x0DB9, 0x0D9F, 0x0D84, 0x0D69, + 0x0D4D, 0x0D31, 0x0D14, 0x0CF7, 0x0CD9, 0x0CBB, 0x0C9D, 0x0C7D, + 0x0C5E, 0x0C3E, 0x0C1D, 0x0BFC, 0x0BDA, 0x0BB8, 0x0B96, 0x0B73, + 0x0B50, 0x0B2C, 0x0B08, 0x0AE3, 0x0ABE, 0x0A99, 0x0A73, 0x0A4D, + 0x0A26, 0x09FF, 0x09D7, 0x09B0, 0x0987, 0x095F, 0x0936, 0x090D, + 0x08E3, 0x08B9, 0x088F, 0x0864, 0x0839, 0x080E, 0x07E2, 0x07B7, + 0x078A, 0x075E, 0x0731, 0x0704, 0x06D7, 0x06A9, 0x067B, 0x064D, + 0x061F, 0x05F0, 0x05C2, 0x0593, 0x0563, 0x0534, 0x0504, 0x04D5, + 0x04A5, 0x0474, 0x0444, 0x0413, 0x03E3, 0x03B2, 0x0381, 0x0350, + 0x031F, 0x02ED, 0x02BC, 0x028A, 0x0259, 0x0227, 0x01F5, 0x01C3, + 0x0191, 0x015F, 0x012D, 0x00FB, 0x00C8, 0x0096, 0x0064, 0x0032, + 0x0000, 0xFFCE, 0xFF9C, 0xFF6A, 0xFF38, 0xFF05, 0xFED3, 0xFEA1, + 0xFE6F, 0xFE3D, 0xFE0B, 0xFDD9, 0xFDA7, 0xFD76, 0xFD44, 0xFD13, + 0xFCE1, 0xFCB0, 0xFC7F, 0xFC4E, 0xFC1D, 0xFBED, 0xFBBC, 0xFB8C, + 0xFB5B, 0xFB2B, 0xFAFC, 0xFACC, 0xFA9D, 0xFA6D, 0xFA3E, 0xFA10, + 0xF9E1, 0xF9B3, 0xF985, 0xF957, 0xF929, 0xF8FC, 0xF8CF, 0xF8A2, + 0xF876, 0xF849, 0xF81E, 0xF7F2, 0xF7C7, 0xF79C, 0xF771, 0xF747, + 0xF71D, 0xF6F3, 0xF6CA, 0xF6A1, 0xF679, 0xF650, 0xF629, 0xF601, + 0xF5DA, 0xF5B3, 0xF58D, 0xF567, 0xF542, 0xF51D, 0xF4F8, 0xF4D4, + 0xF4B0, 0xF48D, 0xF46A, 0xF448, 0xF426, 0xF404, 0xF3E3, 0xF3C2, + 0xF3A2, 0xF383, 0xF363, 0xF345, 0xF327, 0xF309, 0xF2EC, 0xF2CF, + 0xF2B3, 0xF297, 0xF27C, 0xF261, 0xF247, 0xF22E, 0xF215, 0xF1FC, + 0xF1E4, 0xF1CD, 0xF1B6, 0xF1A0, 0xF18A, 0xF175, 0xF160, 0xF14C, + 0xF138, 0xF125, 0xF113, 0xF101, 0xF0F0, 0xF0DF, 0xF0CF, 0xF0C0, + 0xF0B1, 0xF0A3, 0xF095, 0xF088, 0xF07B, 0xF06F, 0xF064, 0xF059, + 0xF04F, 0xF046, 0xF03D, 0xF035, 0xF02D, 0xF026, 0xF01F, 0xF019, + 0xF014, 0xF010, 0xF00C, 0xF008, 0xF005, 0xF003, 0xF002, 0xF001, + 0xF001, 0xF001, 0xF002, 0xF003, 0xF005, 0xF008, 0xF00C, 0xF010, + 0xF014, 0xF019, 0xF01F, 0xF026, 0xF02D, 0xF035, 0xF03D, 0xF046, + 0xF04F, 0xF059, 0xF064, 0xF06F, 0xF07B, 0xF088, 0xF095, 0xF0A3, + 0xF0B1, 0xF0C0, 0xF0CF, 0xF0DF, 0xF0F0, 0xF101, 0xF113, 0xF125, + 0xF138, 0xF14C, 0xF160, 0xF175, 0xF18A, 0xF1A0, 0xF1B6, 0xF1CD, + 0xF1E4, 0xF1FC, 0xF215, 0xF22E, 0xF247, 0xF261, 0xF27C, 0xF297, + 0xF2B3, 0xF2CF, 0xF2EC, 0xF309, 0xF327, 0xF345, 0xF363, 0xF383, + 0xF3A2, 0xF3C2, 0xF3E3, 0xF404, 0xF426, 0xF448, 0xF46A, 0xF48D, + 0xF4B0, 0xF4D4, 0xF4F8, 0xF51D, 0xF542, 0xF567, 0xF58D, 0xF5B3, + 0xF5DA, 0xF601, 0xF629, 0xF650, 0xF679, 0xF6A1, 0xF6CA, 0xF6F3, + 0xF71D, 0xF747, 0xF771, 0xF79C, 0xF7C7, 0xF7F2, 0xF81E, 0xF849, + 0xF876, 0xF8A2, 0xF8CF, 0xF8FC, 0xF929, 0xF957, 0xF985, 0xF9B3, + 0xF9E1, 0xFA10, 0xFA3E, 0xFA6D, 0xFA9D, 0xFACC, 0xFAFC, 0xFB2B, + 0xFB5B, 0xFB8C, 0xFBBC, 0xFBED, 0xFC1D, 0xFC4E, 0xFC7F, 0xFCB0, + 0xFCE1, 0xFD13, 0xFD44, 0xFD76, 0xFDA7, 0xFDD9, 0xFE0B, 0xFE3D, + 0xFE6F, 0xFEA1, 0xFED3, 0xFF05, 0xFF38, 0xFF6A, 0xFF9C, 0xFFCE, + }; + + INLINE s32 lu_sin(uint theta); INLINE s32 lu_cos(uint theta); INLINE uint lu_div(uint x); @@ -419,23 +494,15 @@ INLINE FIXED fxdiv64(FIXED fa, FIXED fb) /*! \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);; -} +INLINE s32 lu_sin(uint theta) +{ return sin_lut[(theta>>7)&0x1FF]; } //! 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);; -} +INLINE s32 lu_cos(uint theta) +{ return sin_lut[((theta>>7)+128)&0x1FF]; } //! Look-up a division value between 0 and 255 /*! \param x reciprocal to look up. diff --git a/engine/include/libgba-sprite-engine/gba_engine.h b/engine/include/libgba-sprite-engine/gba_engine.h index 24675bd..77f06c6 100644 --- a/engine/include/libgba-sprite-engine/gba_engine.h +++ b/engine/include/libgba-sprite-engine/gba_engine.h @@ -14,6 +14,9 @@ #define GBA_SCREEN_WIDTH 240 #define GBA_SCREEN_HEIGHT 160 +#define M4_WIDTH 240 + +const unsigned int black[VRAM_PAGE_SIZE] = {}; class GBAEngine { private: @@ -21,6 +24,8 @@ private: Scene* currentScene; Scene* sceneToTransitionTo; + u16* vid_page; + static std::unique_ptr timer; static std::unique_ptr activeChannelA; static std::unique_ptr activeChannelB; @@ -35,6 +40,11 @@ private: static void stopOnVBlank() { REG_IME = 0; } static void onVBlank(); + void render(); + void renderClear(); + inline void plotPixel(int x, int y, u8 clrId); + void flipPage(); + public: GBAEngine(); diff --git a/engine/include/libgba-sprite-engine/gbavector.h b/engine/include/libgba-sprite-engine/gbavector.h index 4940dd5..a706047 100644 --- a/engine/include/libgba-sprite-engine/gbavector.h +++ b/engine/include/libgba-sprite-engine/gbavector.h @@ -1,33 +1,97 @@ // // 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_SPRITE_ENGINE_PROJECT_MATH_H -#define GBA_SPRITE_ENGINE_PROJECT_MATH_H - -#include - -#ifdef CODE_COMPILED_AS_PART_OF_TEST -#include -#else -#include -#endif +#ifndef GBA_VECTOR_H_ +#define GBA_VECTOR_H_ #include +#include +#include + +#ifdef CODE_COMPILED_AS_PART_OF_TEST + #include +#else + #include +#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 bresenhamLineTo(VECTOR dest); - VECTOR rotateAsCenter(VECTOR point, uint angle); + 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 //GBA_SPRITE_ENGINE_PROJECT_MATH_H +#endif diff --git a/engine/include/libgba-sprite-engine/mesh.h b/engine/include/libgba-sprite-engine/mesh.h new file mode 100644 index 0000000..4a12419 --- /dev/null +++ b/engine/include/libgba-sprite-engine/mesh.h @@ -0,0 +1,30 @@ +// +// Created by Wouter Groeneveld on 08/07/20. +// + +#ifndef GBA_BITMAP_ENGINE_PROJECT_MESH_H +#define GBA_BITMAP_ENGINE_PROJECT_MESH_H + +#include "gbavector.h" +#include +#include + +class Mesh { +private: + GBAVector position; + GBAVector rotation; + + std::vector> verticesArr; + +public: + + void add(GBAVector v); + inline std::vector> const& vertices() const { + return verticesArr; + } + explicit Mesh() {} + Mesh(const Mesh&) = delete; + Mesh& operator=(const Mesh&) = delete; +}; + +#endif //GBA_BITMAP_ENGINE_PROJECT_MESH_H diff --git a/engine/include/libgba-sprite-engine/scene.h b/engine/include/libgba-sprite-engine/scene.h index 712ab92..65f0da2 100644 --- a/engine/include/libgba-sprite-engine/scene.h +++ b/engine/include/libgba-sprite-engine/scene.h @@ -8,6 +8,7 @@ #include #include #include +#include #include class GBAEngine; @@ -22,6 +23,9 @@ public: ForegroundPaletteManager* getForegroundPalette() { return foregroundPalette.get(); } BackgroundPaletteManager* getBackgroundPalette() { return backgroundPalette.get(); } + // WHY raw pointers? they're unwrapped unique_ptrs managed by the scene implementation - will be cleaned up in engine + virtual std::vector meshes() = 0; + virtual void load() = 0; virtual void tick(u16 keys) = 0; diff --git a/engine/src/gba_engine.cpp b/engine/src/gba_engine.cpp index 86869b8..0fbf372 100644 --- a/engine/src/gba_engine.cpp +++ b/engine/src/gba_engine.cpp @@ -4,6 +4,8 @@ #include #include +#include +#include std::unique_ptr GBAEngine::activeChannelA; std::unique_ptr GBAEngine::activeChannelB; @@ -104,7 +106,7 @@ void GBAEngine::enableTimer0AndVBlank() { GBAEngine::GBAEngine() { GBAEngine::timer = std::unique_ptr(new Timer()); // setup screen control flags - REG_DISPCNT = DCNT_MODE4 | DCNT_OBJ | DCNT_OBJ_1D | DCNT_BG0 | DCNT_BG1 | DCNT_BG2 | DCNT_BG3; + REG_DISPCNT = DCNT_MODE4 | DCNT_OBJ | DCNT_OBJ_1D | DCNT_BG2; // setup interrupt control flags for vblank IRQing (started only when sound played) REG_DISPSTAT |= DISPLAY_INTERRUPT_VBLANK_ENABLE; @@ -114,6 +116,7 @@ GBAEngine::GBAEngine() { enableTimer0AndVBlank(); REG_SNDDSCNT = 0; + vid_page = vid_mem_back; } void GBAEngine::update() { @@ -126,6 +129,39 @@ void GBAEngine::update() { // TODO use software interrupt Vsyncing instead of 2 wasteful whiles vsync(); + renderClear(); + render(); + flipPage(); +} + +void GBAEngine::flipPage() { + // toggle the write_buffer's page + vid_page= (COLOR*)((u32)vid_page ^ VRAM_PAGE_SIZE); + REG_DISPCNT ^= DCNT_PAGE; +} + +// http://www.coranac.com/tonc/text/bitmaps.htm +// this thing is supposed to be very slow. see link above. +inline void GBAEngine::plotPixel(int x, int y, u8 clrId) { + u16 *dst = &vid_page[(y * M4_WIDTH + x) / 2]; + if(x & 1) { + *dst = (*dst & 0xFF) | (clrId << 8); + } else { + *dst = (*dst & ~0xFF) | clrId; + } +} + +// does the mesh rendering - to write mem before flipping +void GBAEngine::render() { + for(auto& mesh :currentScene->meshes()) { + for(auto& vertex : mesh->vertices()) { + plotPixel(vertex->x(), vertex->y(), 1); + } + } +} + +void GBAEngine::renderClear() { + dma3_cpy(vid_page, black, VRAM_PAGE_SIZE); } void GBAEngine::cleanupPreviousScene() { diff --git a/engine/src/gbavector.cpp b/engine/src/gbavector.cpp index ae48dc6..84b9b03 100644 --- a/engine/src/gbavector.cpp +++ b/engine/src/gbavector.cpp @@ -4,10 +4,10 @@ #include -VECTOR GBAVector::rotateAsCenter(VECTOR point, uint angle) { +GBAVector GBAVector::rotateAsCenter(GBAVector point, FIXED angle) { auto center = this->v; s32 centerx = center.x, centery = center.y; - s32 defaultx = point.x, defaulty = point.y; + s32 defaultx = point.x(), defaulty = point.y(); s32 cos = lu_cos(angle) >> 4; s32 sin = lu_sin(angle) >> 4; @@ -15,9 +15,14 @@ VECTOR GBAVector::rotateAsCenter(VECTOR point, uint angle) { // 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({ + ( cos * (defaultx - centerx) + sin * (defaulty - centery) + (centerx << 8)) >> 8, + (-sin * (defaultx - centerx) + cos * (defaulty - centery) + (centery << 8)) >> 8}); + */ + return GBAVector({ + (fxmul(cos, (defaultx - centerx)) + fxmul(sin, (defaulty - centery)) + (centerx << 8)) >> 8, + (fxmul(-sin, (defaultx - centerx)) + fxmul(cos, (defaulty - centery) + (centery << 8))) >> 8}); } std::deque GBAVector::bresenhamLineTo(VECTOR dest) { diff --git a/engine/src/mesh.cpp b/engine/src/mesh.cpp new file mode 100644 index 0000000..37b5663 --- /dev/null +++ b/engine/src/mesh.cpp @@ -0,0 +1,12 @@ +// +// Created by Wouter Groeneveld on 08/07/20. +// + +#include +#include + + +void Mesh::add(GBAVector v) { + verticesArr.push_back(std::unique_ptr(new GBAVector(v))); +} + diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2aae0e5..0a107d6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,14 +3,19 @@ cmake_minimum_required(VERSION 3.12) SET(CMAKE_CXX_STANDARD 11) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-narrowing -Wno-int-to-pointer-cast") set(CMAKE_VERBOSE_MAKEFILE on) +#set_property(SOURCE ../engine/src/gba/sin_lut.s PROPERTY LANGUAGE ASM) +SET(ASM_OPTIONS "-x assembler-with-cpp") +SET(CMAKE_ASM_FLAGS "${CFLAGS} ${ASM_OPTIONS}" ) project(Unittest) +enable_language(C CXX ASM) enable_testing() SET(GTEST_LIBRARY "$ENV{GTEST_DIR}") # reset linker flags; ARM + GTest doesn't work SET(CMAKE_EXE_LINKER_FLAGS "${BASE_CMAKE_LINK_FLAGS}") +SET(CMAKE_ASM_COMPILER gcc) SET(CMAKE_C_COMPILER gcc) SET(CMAKE_CXX_COMPILER g++) @@ -25,11 +30,10 @@ add_executable(unittest gbatest.cpp palettetest.cpp real_data.h - ../engine/src/gba/sin_lut.s ../engine/src/palette/palette_manager.cpp ../engine/src/palette/combined_palette.cpp ../engine/src/timer.cpp ../engine/src/gbavector.cpp - timertest.cpp gbavectortest.cpp) + timertest.cpp gbavectortest.cpp gbamatrixtest.cpp fixedpoinmathtest.cpp) target_link_libraries(unittest ${GTEST_LIBRARY}/build/libgtest.a ${GTEST_LIBRARY}/build/libgtest_main.a) diff --git a/test/fixedpoinmathtest.cpp b/test/fixedpoinmathtest.cpp new file mode 100644 index 0000000..e876f48 --- /dev/null +++ b/test/fixedpoinmathtest.cpp @@ -0,0 +1,83 @@ +// +// Created by Wouter Groeneveld on 08/07/20. +// + +#include +#include +#include +#include + +class FpSuite : public ::testing::Test { +protected: + virtual void TearDown() { + } + + virtual void SetUp() { + } +}; + +float rnd(float val) { + return (float) ((std::floor(val * 100) + .5) / 100); +} + +float gr2rad(uint grad) { + return grad*M_PI/180; +} + +FIXED gr2lut(uint grad) { + return 65535 / (360 / grad); +} + +FIXED rad2lut(float rad) { + return gr2lut(rad * 180 / M_PI); +} + +float fx12ToFloat(FIXED fx) { + return fx / (float) (1<<12); +} + +FIXED fx12Tofx8(FIXED fx12) { + return fx12 >> 4; +} + + +// from LUT doxygen: +//! Look-up a cosine value (2π = 0x10000) +/*! \param theta Angle in [0,FFFFh] range => 65535 +* \return .12f cosine value +*/ +TEST_F(FpSuite, LUTValueTests) { + float rad = gr2rad(90); // 1/4th halfway; 90; 360 / 4 + std::cout << "90 degrees is " << rad << " rads" << std::endl; + auto sinR = sin(rad); + ASSERT_EQ(rnd(sinR), 1.005f); + + //FIXED angle = gr2lut(90); // 128 is a quarter circle now + FIXED angle = rad2lut(rad); // this also works! + FIXED sinLut = lu_sin(angle); + float sinLutConv12 = fx12ToFloat(sinLut); // indeed, fx2float() gives me 15.995 instead of 0.995! shit. + float sinLutConv8 = fx2float(fx12Tofx8(sinLut)); // this also works! + + std::cout << angle << " lu_sin is " << sinLut << " .12f and conv: " << sinLutConv12 << std::endl; + ASSERT_EQ(rnd(sinLutConv12), 0.995f); + ASSERT_EQ(rnd(sinLutConv8), 0.995f); +} + +TEST_F(FpSuite, TanUtilityTests) { + // 20 degrees is 0.34906577777777775 rad + // Math.sin(0.3...) / Math.cos(0.3...) = 0.3420 / 0.9396 = 0.3639... + auto rad = gr2rad(20); + auto tanRef = sin(rad) / cos(rad); + auto realTan = tan(rad); + ASSERT_EQ(rnd(tanRef), 0.365f); + ASSERT_EQ(rnd(realTan), 0.365f); + + FIXED lutalpha = rad2lut(rad); + FIXED lusin = lu_sin(lutalpha), lucos = lu_cos(lutalpha); + + std::cout << "sin(14) is " << fx12ToFloat(lusin) << " and cos(14) is " << fx12ToFloat(lucos) << std::endl; + + auto tanFakeLookupTables = fx2float(fxdiv(fx12Tofx8(lu_sin(lutalpha)), fx12Tofx8(lu_cos(lutalpha)))); + ASSERT_EQ(rnd(tanFakeLookupTables), 0.355f); +} + diff --git a/test/gbamatrixtest.cpp b/test/gbamatrixtest.cpp new file mode 100644 index 0000000..daeb1b6 --- /dev/null +++ b/test/gbamatrixtest.cpp @@ -0,0 +1,55 @@ +// +// Created by Wouter Groeneveld on 08/07/20. +// + +#include +#include +#include + +class GBAMatrixSuite : public ::testing::Test { +protected: + virtual void TearDown() { + } + + virtual void SetUp() { + } +}; + +TEST_F(GBAMatrixSuite, TanUtilityTests) { + // Math.sin(14) / Math.cos(14) = 0.99 / 0.13 = 7.2446 + auto tanRef = sin(14) / cos(14); + auto realTan = tan(14); + ASSERT_EQ((std::floor(tanRef * 100) + .5) / 100, 7.245); + ASSERT_EQ((std::floor(realTan * 100) + .5) / 100, 7.245); + + auto tanFakeLookupTables = ((lu_sin(14)) / (lu_cos(14))) << 4; + ASSERT_EQ((std::floor(tanFakeLookupTables * 100) + .5) / 100, 7.245); + + auto result = fx2float(GBAMatrix::tan(int2fx(14))); + ASSERT_EQ(result, 7.2446); +} + +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)); + ASSERT_EQ(fx2float(result.mAt(0)), 1.52); +} diff --git a/test/gbavectortest.cpp b/test/gbavectortest.cpp index 2b864ed..fb598a7 100644 --- a/test/gbavectortest.cpp +++ b/test/gbavectortest.cpp @@ -6,44 +6,157 @@ #include #include +#include "tonc_bios_stub.h" class GBAVectorSuite : public ::testing::Test { protected: GBAVector vector; - VECTOR bottomHalf; virtual void TearDown() { } virtual void SetUp() { vector = GBAVector({120, 80}); - bottomHalf = { 120, 200 }; } }; -// Angle in DEGREES for readability - converted in tonc_math_stub! +TEST_F(GBAVectorSuite, ToAndFromFixedIntWorks) { + auto zAxis = GBAVector::fromInt(0, 0, -10); + ASSERT_EQ(zAxis.y(), 0); + ASSERT_EQ(zAxis.z(), -2560); + auto toInt = zAxis.toInt(); + ASSERT_EQ(toInt.y(), 0); + ASSERT_EQ(toInt.z(), -10); +} + +TEST_F(GBAVectorSuite, Normalize) { + // based on Babylon Math debugging examples + auto zAxis = GBAVector::fromInt(0, 0, -10); + zAxis.normalize(); + float z = fx2float(zAxis.z()); + auto result = zAxis.toInt(); + + ASSERT_EQ(result.x(), 0); + ASSERT_EQ(result.y(), 0); + ASSERT_EQ((std::floor(z * 100) + .5) / 100, -0.975); + //ASSERT_EQ(result.z(), -1); + // TODO will be rounded to zero. problematic later on? +} + +TEST_F(GBAVectorSuite, MinDot) { + auto zAxis = GBAVector::fromInt(0, 0, -1); + auto eye = GBAVector::fromInt(0, 0, 10); + auto result = fx2int(-GBAVector::dot(zAxis, eye)); + + ASSERT_EQ(result, 10); +} + +TEST_F(GBAVectorSuite, Cross) { + auto one = GBAVector::fromInt(0, 1, 0); + auto two = GBAVector::fromInt(0, 0, -1); + auto result = GBAVector::cross(one, two).toInt(); + + ASSERT_EQ(result.x(), -1); + ASSERT_EQ(result.y(), 0); + ASSERT_EQ(result.z(), 0); +} + +TEST_F(GBAVectorSuite, AddTwoVectors) { + GBAVector one(1, 2, 3); + GBAVector two(4, 5, 6); + auto result = one + two; + + ASSERT_EQ(result.x(), 5); + ASSERT_EQ(result.y(), 7); + ASSERT_EQ(result.z(), 9); +} + +TEST_F(GBAVectorSuite, SubstractTwoVectors) { + GBAVector one(1, 2, 3); + GBAVector two(4, 5, 6); + auto result = one - two; + + ASSERT_EQ(result.x(), -3); + ASSERT_EQ(result.y(), -3); + ASSERT_EQ(result.z(), -3); +} + +TEST_F(GBAVectorSuite, LengthSqrtOfAllPowItself) { + auto one = GBAVector::fromInt(1, 2, 3); // 1*1 + 2*2 + 3*3 = 14 + auto result = fx2float(one.length()); + ASSERT_EQ(3.735, (std::floor(result * 100) + .5) / 100); + + auto negative = GBAVector::fromInt(0, 0, -10); + int negResult = fx2int(negative.length()); + + ASSERT_EQ(fx2int(fxmul(int2fx(-10), int2fx(-10))), 100); + ASSERT_EQ(negResult, 10); +} + +TEST_F(GBAVectorSuite, DivideTwoVectors) { + auto one = GBAVector::fromInt(1, 2, 3); + auto two = GBAVector::fromInt(4, 10, 6); + auto result = (two / one).toInt(); + + ASSERT_EQ(result.x(), 4); + ASSERT_EQ(result.y(), 5); + ASSERT_EQ(result.z(), 2); +} + +TEST_F(GBAVectorSuite, MultiplyTwoVectors) { + auto one = GBAVector::fromInt(1, 2, 3); + auto two = GBAVector::fromInt(4, 10, 6); + auto result = (two * one).toInt(); + + ASSERT_EQ(result.x(), 4); + ASSERT_EQ(result.y(), 20); + ASSERT_EQ(result.z(), 18); +} + +TEST_F(GBAVectorSuite, ScaleAVector) { + auto one = GBAVector::fromInt(1, 2, 3); + auto result = one.scale(2).toInt(); + + ASSERT_EQ(result.x(), 2); + ASSERT_EQ(result.y(), 4); + ASSERT_EQ(result.z(), 6); +} + +uint grad2rad(uint grad) { + //return float2fx(grad*M_PI/180); + return fxdiv(fxmul(int2fx(grad), float2fx(M_PI)), int2fx(180)); +} + TEST_F(GBAVectorSuite, Rotate_FromBottomHalf_0_Degrees) { - auto result = vector.rotateAsCenter(bottomHalf, 0); - ASSERT_EQ(120, result.x); - ASSERT_EQ(200, result.y); + auto bottomHalf = GBAVector::fromInt(120, 200, 0); + auto vector = GBAVector::fromInt(120, 80, 0); + auto result = vector.rotateAsCenter(bottomHalf, grad2rad(0)).toInt(); + 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); + auto bottomHalf = GBAVector::fromInt(120, 200, 0); + auto vector = GBAVector::fromInt(120, 80, 0); + auto result = vector.rotateAsCenter(bottomHalf, grad2rad(90)).toInt(); + 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); + auto bottomHalf = GBAVector::fromInt(120, 200, 0); + auto vector = GBAVector::fromInt(120, 80, 0); + auto result = vector.rotateAsCenter(bottomHalf, grad2rad(180)).toInt(); + 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); + auto bottomHalf = GBAVector::fromInt(120, 200, 0); + auto vector = GBAVector::fromInt(120, 80, 0); + auto result = vector.rotateAsCenter(bottomHalf, grad2rad(270)).toInt(); + ASSERT_EQ(0, result.x()); + ASSERT_EQ(80, result.y()); } // ---- // diff --git a/test/tonc_bios_stub.h b/test/tonc_bios_stub.h new file mode 100644 index 0000000..eb38a09 --- /dev/null +++ b/test/tonc_bios_stub.h @@ -0,0 +1,25 @@ +// +// Created by Wouter Groeneveld on 08/07/20. +// + +#ifndef GBA_BITMAP_ENGINE_PROJECT_TONC_BIOS_STUB_H +#define GBA_BITMAP_ENGINE_PROJECT_TONC_BIOS_STUB_H + + +namespace externMath { + +#include + + float root(float num) { + return sqrt(num); + return 0; + } + +} + +u32 Sqrt(u32 num) { + return float2fx(externMath::root(fx2float(num))); +} + + +#endif //GBA_BITMAP_ENGINE_PROJECT_TONC_BIOS_STUB_H