fixed point math, vectors, initial matrix

This commit is contained in:
wgroeneveld 2020-07-08 18:12:38 +02:00
parent 27e6a9065c
commit aedb3cdfcb
17 changed files with 656 additions and 50 deletions

View File

@ -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<Mesh*> WireScene::meshes() {
return { cube.get() };
}
void WireScene::load() {
foregroundPalette = std::unique_ptr<ForegroundPaletteManager>(new ForegroundPaletteManager());
backgroundPalette = std::unique_ptr<BackgroundPaletteManager>(new BackgroundPaletteManager(pal, sizeof(pal)));
cube = std::unique_ptr<Mesh>(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) {

View File

@ -6,9 +6,12 @@
#define GBA_SPRITE_ENGINE_SAMPLE_START_SCENE_H
#include <libgba-sprite-engine/scene.h>
#include <libgba-sprite-engine/mesh.h>
#include <memory>
class WireScene : public Scene {
private:
std::unique_ptr<Mesh> cube;
public:
@ -16,6 +19,7 @@ public:
void load() override;
void tick(u16 keys) override;
std::vector<Mesh*> meshes() override;
};

View File

@ -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
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>

View File

@ -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 <libgba-sprite-engine/gba/tonc_math_stub.h>
#else
#include <libgba-sprite-engine/gba/tonc_math.h>
#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

View File

@ -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&#960; = 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.

View File

@ -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> timer;
static std::unique_ptr<SoundControl> activeChannelA;
static std::unique_ptr<SoundControl> 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();

View File

@ -1,11 +1,15 @@
//
// 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
#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>
@ -13,21 +17,81 @@
#include <libgba-sprite-engine/gba/tonc_math.h>
#endif
#include <string>
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);
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

View File

@ -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 <vector>
#include <memory>
class Mesh {
private:
GBAVector position;
GBAVector rotation;
std::vector<std::unique_ptr<GBAVector>> verticesArr;
public:
void add(GBAVector v);
inline std::vector<std::unique_ptr<GBAVector>> const& vertices() const {
return verticesArr;
}
explicit Mesh() {}
Mesh(const Mesh&) = delete;
Mesh& operator=(const Mesh&) = delete;
};
#endif //GBA_BITMAP_ENGINE_PROJECT_MESH_H

View File

@ -8,6 +8,7 @@
#include <vector>
#include <memory>
#include <functional>
#include <libgba-sprite-engine/mesh.h>
#include <libgba-sprite-engine/palette/palette_manager.h>
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<Mesh*> meshes() = 0;
virtual void load() = 0;
virtual void tick(u16 keys) = 0;

View File

@ -4,6 +4,8 @@
#include <libgba-sprite-engine/gba/tonc_memdef.h>
#include <libgba-sprite-engine/gba_engine.h>
#include <cstring>
#include <libgba-sprite-engine/gba/tonc_core.h>
std::unique_ptr<SoundControl> GBAEngine::activeChannelA;
std::unique_ptr<SoundControl> GBAEngine::activeChannelB;
@ -104,7 +106,7 @@ void GBAEngine::enableTimer0AndVBlank() {
GBAEngine::GBAEngine() {
GBAEngine::timer = std::unique_ptr<Timer>(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() {

View File

@ -4,10 +4,10 @@
#include <libgba-sprite-engine/gbavector.h>
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 {
/*
return GBAVector({
( 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({
(fxmul(cos, (defaultx - centerx)) + fxmul(sin, (defaulty - centery)) + (centerx << 8)) >> 8,
(fxmul(-sin, (defaultx - centerx)) + fxmul(cos, (defaulty - centery) + (centery << 8))) >> 8});
}
std::deque<VECTOR> GBAVector::bresenhamLineTo(VECTOR dest) {

12
engine/src/mesh.cpp Normal file
View File

@ -0,0 +1,12 @@
//
// Created by Wouter Groeneveld on 08/07/20.
//
#include <libgba-sprite-engine/mesh.h>
#include <memory>
void Mesh::add(GBAVector v) {
verticesArr.push_back(std::unique_ptr<GBAVector>(new GBAVector(v)));
}

View File

@ -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)

View File

@ -0,0 +1,83 @@
//
// Created by Wouter Groeneveld on 08/07/20.
//
#include <gtest/gtest.h>
#include <math.h>
#include <cmath>
#include <libgba-sprite-engine/gba/tonc_math_stub.h>
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&#960; = 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);
}

55
test/gbamatrixtest.cpp Normal file
View File

@ -0,0 +1,55 @@
//
// Created by Wouter Groeneveld on 08/07/20.
//
#include <gtest/gtest.h>
#include <math.h>
#include <libgba-sprite-engine/gbamatrix.h>
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);
}

View File

@ -6,44 +6,157 @@
#include <gtest/gtest.h>
#include <libgba-sprite-engine/gbavector.h>
#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());
}
// ---- //

25
test/tonc_bios_stub.h Normal file
View File

@ -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 <math.h>
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