fixes fixpt rendering, make things refs as much as possible

This commit is contained in:
wgroeneveld 2020-07-09 15:48:03 +02:00
parent a38f1eeddd
commit ce51f737b5
12 changed files with 147 additions and 89 deletions

View File

@ -11,8 +11,6 @@ const unsigned short pal[4] __attribute__((aligned(4))) = {
0x0000, 0xFFFF, 0x3AE2
};
const FIXED pointZeroOne = float2fx(0.01);
std::vector<Mesh*> WireScene::meshes() {
return { cube.get() };
}
@ -26,16 +24,31 @@ void WireScene::load() {
backgroundPalette = std::unique_ptr<BackgroundPaletteManager>(new BackgroundPaletteManager(pal, sizeof(pal)));
cube = std::unique_ptr<Mesh>(new Mesh());
cube->add(VectorFx(-1, 1, 1));
cube->add(VectorFx(1, 1, 1));
cube->add(VectorFx(-1, -1, 1));
cube->add(VectorFx(-1, -1, -1));
cube->add(VectorFx(-1, 1, -1));
cube->add(VectorFx(1, 1, -1));
cube->add(VectorFx(1, -1, 1));
cube->add(VectorFx(-1, -1, -1));
cube->add(VectorFx::fromInt(-1, 1, 1));
cube->add(VectorFx::fromInt(1, 1, 1));
cube->add(VectorFx::fromInt(-1, -1, 1));
cube->add(VectorFx::fromInt(-1, -1, -1));
cube->add(VectorFx::fromInt(-1, 1, -1));
cube->add(VectorFx::fromInt(1, 1, -1));
cube->add(VectorFx::fromInt(1, -1, 1));
cube->add(VectorFx::fromInt(1, -1, -1));
/*
* should be translated, with camera 0,0,10, and with GBA dimensions, to:
SoftEngine.js:50 drawing 163,123
SoftEngine.js:50 drawing 155,115
SoftEngine.js:50 drawing 155,44
SoftEngine.js:50 drawing 84,44
SoftEngine.js:50 drawing 76,123
SoftEngine.js:50 drawing 84,115
SoftEngine.js:50 drawing 163,36
SoftEngine.js:50 drawing 76,36
*/
}
void WireScene::tick(u16 keys) {
cube->rotate(pointZeroOne, pointZeroOne);
if(keys & KEY_START || keys & KEY_A) {
cube->rotate(5, 5);
} else if(keys & KEY_B) {
cube->resetRotation();
}
}

View File

@ -32,6 +32,7 @@ private:
Scene* sceneToTransitionTo;
u16* vid_page;
MatrixFx projectionMatrix;
static std::unique_ptr<Timer> timer;
static std::unique_ptr<SoundControl> activeChannelA;
@ -50,7 +51,7 @@ private:
void render();
void renderClear();
inline void plotPixel(int x, int y, u8 clrId);
inline VectorFx project(VectorFx coord, MatrixFx transMat);
inline VectorFx project(const VectorFx &coord, const MatrixFx &transMat);
void flipPage();
public:

View File

@ -18,6 +18,7 @@
extern FIXED HALF;
extern FIXED ONE;
extern FIXED TWO;
#define FIX12_SHIFT 12
#define FIX12_SCALE ( 1<<FIX12_SHIFT )
@ -69,18 +70,21 @@ INLINE FIXED fx12Tofx8(FIXED fx12) {
}
INLINE FIXED fxsin(FIXED fxrad) {
if(fxrad == 0) return 0;
FIXED theta = fxrad2lut(fxrad);
FIXED sin = lu_sin(theta);
return fx12Tofx8(sin);
}
INLINE FIXED fxcos(FIXED fxrad) {
if(fxrad == 0) return ONE;
FIXED theta = fxrad2lut(fxrad);
FIXED cos = lu_cos(theta);
return fx12Tofx8(cos);
}
INLINE FIXED fxtan(FIXED fxrad) {
if(fxrad == 0) return 0;
FIXED theta = fxrad2lut(fxrad);
FIXED sin = lu_sin(theta);
FIXED cos = lu_cos(theta);

View File

@ -97,7 +97,7 @@ public:
m[15] = m44;
}
inline static VectorFx transformCoordinates(VectorFx vector, MatrixFx transformation) {
inline static VectorFx transformCoordinates(const VectorFx &vector, const 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));
@ -126,7 +126,7 @@ public:
return result;
}
inline static MatrixFx lookAtLH(VectorFx eye, VectorFx target, VectorFx up) {
inline static MatrixFx lookAtLH(const VectorFx &eye, const VectorFx &target, const VectorFx &up) {
auto zAxis = target - eye;
zAxis.normalize();
auto xAxis = VectorFx::cross(up, zAxis);
@ -144,8 +144,8 @@ public:
auto result = MatrixFx::zero();
auto s = fxsin(angle);
auto c = fxcos(angle);
result.m[0] = 1.0;
result.m[15] = 1.0;
result.m[0] = ONE;
result.m[15] = ONE;
result.m[5] = c;
result.m[10] = c;
result.m[9] = -s;
@ -157,8 +157,8 @@ public:
auto result = MatrixFx::zero();
auto s = fxsin(angle);
auto c = fxcos(angle);
result.m[5] = 1.0;
result.m[15] = 1.0;
result.m[5] = ONE;
result.m[15] = ONE;
result.m[0] = c;
result.m[2] = -s;
result.m[8] = s;
@ -170,8 +170,8 @@ public:
auto result = MatrixFx::zero();
auto s = fxsin(angle);
auto c = fxcos(angle);
result.m[10] = 1.0;
result.m[15] = 1.0;
result.m[10] = ONE;
result.m[15] = ONE;
result.m[0] = c;
result.m[1] = s;
result.m[4] = -s;
@ -179,7 +179,7 @@ public:
return result;
}
inline static MatrixFx translation(VectorFx vec) {
inline static MatrixFx translation(const VectorFx &vec) {
auto result = MatrixFx::identity();
result.m[12] = vec.x();
result.m[13] = vec.y();

View File

@ -26,12 +26,17 @@ public:
inline FIXED rotx() { return rot.x(); }
inline FIXED roty() { return rot.y(); }
inline FIXED rotz() { return rot.z(); }
inline void resetRotation() {
rot.setX(0);
rot.setY(0);
}
inline void rotate(FIXED x, FIXED y) {
rot.setX(rot.x() + x);
rot.setY(rot.y() + y);
}
explicit Mesh() {}
explicit Mesh() : pos(VectorFx()), rot(VectorFx()) {}
Mesh(const Mesh&) = delete;
Mesh& operator=(const Mesh&) = delete;
};

View File

@ -36,7 +36,6 @@ public:
}
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); }

View File

@ -118,6 +118,7 @@ GBAEngine::GBAEngine() {
REG_SNDDSCNT = 0;
vid_page = vid_mem_back;
projectionMatrix = MatrixFx::perspectiveFovLH(float2fx(0.78), fxdiv(GBA_SCREEN_WIDTH_FX, GBA_SCREEN_HEIGHT_FX), float2fx(0.01), ONE);
}
void GBAEngine::update() {
@ -152,22 +153,19 @@ inline void GBAEngine::plotPixel(int x, int y, u8 clrId) {
}
}
inline VectorFx GBAEngine::project(VectorFx coord, MatrixFx transMat) {
inline VectorFx GBAEngine::project(const VectorFx &coord, const 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);
auto x = fxmul(point.x(), GBA_SCREEN_WIDTH_FX) + fxdiv(GBA_SCREEN_WIDTH_FX, TWO);
auto y = fxmul(-point.y(), GBA_SCREEN_HEIGHT_FX) + fxdiv(GBA_SCREEN_HEIGHT_FX, TWO);
return VectorFx(x, y, 0);
}
// does the mesh rendering - to write mem before flipping
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()) {
auto worldMatrix = MatrixFx::rotationYawPitchRoll(mesh->roty(), mesh->rotx(), mesh->rotz()) * MatrixFx::translation(mesh->position());
auto transformMatrix = worldMatrix * viewMatrix * projectionMatrix;
@ -176,6 +174,17 @@ void GBAEngine::render() {
plotPixel(projectedPoint.x(), projectedPoint.y(), 1);
}
}
/*
plotPixel(150, 40, 1);
plotPixel(60, 40, 1);
plotPixel(150, 140, 1);
plotPixel(93, 26, 1);
plotPixel(93, 115, 1);
plotPixel(172, 115, 1);
plotPixel(60, 140, 1);
plotPixel(93, 26, 1);
*/
}
void GBAEngine::renderClear() {

View File

@ -6,3 +6,4 @@
FIXED HALF = float2fx(0.5);
FIXED ONE = int2fx(1);
FIXED TWO = int2fx(2);

View File

@ -4,26 +4,6 @@
#include <libgba-sprite-engine/vectorfx.h>
VectorFx VectorFx::rotateAsCenter(VectorFx point, FIXED angle) {
auto center = this->v;
s32 centerx = center.x, centery = center.y;
s32 defaultx = point.x(), defaulty = point.y();
s32 cos = lu_cos(angle) >> 4;
s32 sin = lu_sin(angle) >> 4;
// affine matriches are 8.8 fixed point numbers, so shift all input 8 spaces up and forth
// possibilities: instead of between [-1.0, 1.0] it's between [-256, +256]
// 90° rotation in inversed y-axis needs to flip sin sign
/*
return VectorFx({
( cos * (defaultx - centerx) + sin * (defaulty - centery) + (centerx << 8)) >> 8,
(-sin * (defaultx - centerx) + cos * (defaulty - centery) + (centery << 8)) >> 8});
*/
return VectorFx({
(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> VectorFx::bresenhamLineTo(VECTOR dest) {
// https://www.coranac.com/tonc/text/bitmaps.htm - Bresenham's line algorithm with fixed points

View File

@ -19,6 +19,9 @@ SET(CMAKE_ASM_COMPILER gcc)
SET(CMAKE_C_COMPILER gcc)
SET(CMAKE_CXX_COMPILER g++)
# remove -03 optimization flag otherwise debugging will be annoying as hell
SET(CMAKE_CXX_FLAGS "-Wno-narrowing")
add_definitions(-DCODE_COMPILED_AS_PART_OF_TEST)
include_directories(${GTEST_LIBRARY}/include)
@ -34,6 +37,7 @@ add_executable(unittest
../engine/src/palette/combined_palette.cpp
../engine/src/timer.cpp
../engine/src/math.cpp
../engine/src/mesh.cpp
../engine/src/vectorfx.cpp
timertest.cpp vectorfxtest.cpp matrixfxtest.cpp fixedpoinmathtest.cpp)

View File

@ -6,6 +6,9 @@
#include <math.h>
#include <libgba-sprite-engine/matrixfx.h>
#include <libgba-sprite-engine/math.h>
#include <libgba-sprite-engine/mesh.h>
#include <libgba-sprite-engine/camera.h>
#include <libgba-sprite-engine/gba_engine.h>
class MatrixFxSuite : public ::testing::Test {
protected:
@ -21,7 +24,7 @@ INLINE float rnd2(float val) {
}
void assertMatrix(MatrixFx expected, MatrixFx actual) {
void assertMatrix(MatrixFx expected, MatrixFx actual, std::string matrixName) {
for(int i = 0; i < MATRIX_DIMENSION; i++) {
auto expect = expected.mAt(i);
auto act = actual.mAt(i);
@ -30,7 +33,82 @@ void assertMatrix(MatrixFx expected, MatrixFx actual) {
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;
ASSERT_EQ(expectFl, actFl) << matrixName << "[" << i << "] does not match: (exp, act) " << expect << ", " << act << " - floats: " << expectFl << ", " << actFl;
}
}
TEST_F(MatrixFxSuite, RotationMatriches) {
auto result = MatrixFx::rotationZ(0);
auto expectedIdMatrix = MatrixFx(float2fx(1.0), 0, 0, 0, 0, float2fx(1.0), 0, 0, 0, 0, float2fx(1.0), 0, 0, 0, 0, float2fx(1.0));
assertMatrix(expectedIdMatrix, result, "rotz");
}
TEST_F(MatrixFxSuite, RotationYawPitchRoll) {
auto expectedIdMatrix = MatrixFx(float2fx(1.0), 0, 0, 0, 0, float2fx(1.0), 0, 0, 0, 0, float2fx(1.0), 0, 0, 0, 0, float2fx(1.0));
auto rotYwaPitchRoll = MatrixFx::rotationYawPitchRoll(0, 0, 0);
assertMatrix(expectedIdMatrix, rotYwaPitchRoll, "rotywa");
}
TEST_F(MatrixFxSuite, MeshToTransformMatrix_IntegrationTest) {
// source:
Mesh cube;
cube.add(VectorFx(-1, 1, 1));
cube.add(VectorFx(1, 1, 1));
cube.add(VectorFx(-1, -1, 1));
cube.add(VectorFx(-1, -1, -1));
cube.add(VectorFx(-1, 1, -1));
cube.add(VectorFx(1, 1, -1));
cube.add(VectorFx(1, -1, 1));
cube.add(VectorFx(-1, -1, -1));
auto currentCamera = Camera(VectorFx::fromInt(0, 0, 10), VectorFx::fromInt(0, 0, 0));
auto viewMatrix = MatrixFx::lookAtLH(currentCamera.getPosition(), currentCamera.getTarget(), VectorFx::up());
auto projectionMatrix = MatrixFx::perspectiveFovLH(float2fx(0.78), fxdiv(GBA_SCREEN_WIDTH_FX, GBA_SCREEN_HEIGHT_FX), float2fx(0.01), ONE);
auto expectedProjectionMatrix = MatrixFx(float2fx(1.64f), 0, 0, 0, 0, float2fx(2.55f), 0, 0, 0, 0, float2fx(1.05), float2fx(1.05), 0, 0, float2fx(-0.05), 0);
assertMatrix(expectedProjectionMatrix, projectionMatrix, "project");
auto expectedIdMatrix = MatrixFx(float2fx(1.0), 0, 0, 0, 0, float2fx(1.0), 0, 0, 0, 0, float2fx(1.0), 0, 0, 0, 0, float2fx(1.0));
auto rotYwaPitchRoll = MatrixFx::rotationYawPitchRoll(cube.roty(), cube.rotx(), cube.rotz());
assertMatrix(expectedIdMatrix, rotYwaPitchRoll, "rotywa");
auto translatedPos = MatrixFx::translation(cube.position());
assertMatrix(expectedIdMatrix, translatedPos, "translpos");
auto worldMatrix = rotYwaPitchRoll * translatedPos;
assertMatrix(expectedIdMatrix, worldMatrix, "worldmatrix");
auto transformMatrix = worldMatrix * viewMatrix * projectionMatrix;
auto expectedTransformMatrix = MatrixFx(float2fx(-1.67), 0, 0, 0, 0, float2fx(2.55), 0, 0, 0, 0, float2fx(-1.0), float2fx(-1.0), 0, 0, float2fx(9.85), float2fx(9.75));
assertMatrix(expectedTransformMatrix, transformMatrix, "transfomatrix");
auto coord = *cube.vertices()[0].get();
auto point = MatrixFx::transformCoordinates(coord, transformMatrix);
ASSERT_EQ(fx2float(point.x()), 0.125f);
ASSERT_EQ(fx2float(point.y()), 0.25f);
ASSERT_EQ(fx2float(point.z()), 1.00f);
auto x = fxmul(point.x(), GBA_SCREEN_WIDTH_FX) + fxdiv(GBA_SCREEN_WIDTH_FX, int2fx(2));
auto y = fxmul(-point.y(), GBA_SCREEN_HEIGHT_FX) + fxdiv(GBA_SCREEN_HEIGHT_FX, int2fx(2));
ASSERT_EQ(fx2float(x), 150);
ASSERT_EQ(fx2float(y), 40);
// dest in Babylon - dest according to for loop below (should print something roughly similar)
/*
* 163, 36 - 150,40
* 76, 36 - 60,40
* 163, 123 - 150,140
* 155, 115 - 93,26
* 155, 44 - 93,115
* 84, 44 - 172,115
* 76, 123 - 60,140
* 84, 115 - 93,26
*
*/
for(auto& vertex : cube.vertices()) {
auto point = MatrixFx::transformCoordinates(*vertex.get(), transformMatrix);
auto x = fxmul(point.x(), GBA_SCREEN_WIDTH_FX) + fxdiv(GBA_SCREEN_WIDTH_FX, int2fx(2));
auto y = fxmul(-point.y(), GBA_SCREEN_HEIGHT_FX) + fxdiv(GBA_SCREEN_HEIGHT_FX, int2fx(2));
std::cout << "plotting (" << fx2int(x) << "," << fx2int(y) << ")" << std::endl;
}
}
@ -64,7 +142,7 @@ TEST_F(MatrixFxSuite, lookAtLH_TestData) {
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);
assertMatrix(expected, result, "M");
}
@ -92,5 +170,5 @@ TEST_F(MatrixFxSuite, PerspectiveFovLH_TestData) {
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);
assertMatrix(expected, result, "M");
}

View File

@ -144,42 +144,6 @@ TEST_F(VectorFxSuite, ScaleAVector) {
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(VectorFxSuite, Rotate_FromBottomHalf_0_Degrees) {
auto bottomHalf = VectorFx::fromInt(120, 200, 0);
auto vector = VectorFx::fromInt(120, 80, 0);
auto result = vector.rotateAsCenter(bottomHalf, grad2rad(0)).toInt();
ASSERT_EQ(120, result.x());
ASSERT_EQ(200, result.y());
}
TEST_F(VectorFxSuite, Rotate_FromBottomHalf_90_Degrees) {
auto bottomHalf = VectorFx::fromInt(120, 200, 0);
auto vector = VectorFx::fromInt(120, 80, 0);
auto result = vector.rotateAsCenter(bottomHalf, grad2rad(90)).toInt();
ASSERT_EQ(240, result.x());
ASSERT_EQ(80, result.y());
}
TEST_F(VectorFxSuite, Rotate_FromBottomHalf_180_Degrees) {
auto bottomHalf = VectorFx::fromInt(120, 200, 0);
auto vector = VectorFx::fromInt(120, 80, 0);
auto result = vector.rotateAsCenter(bottomHalf, grad2rad(180)).toInt();
ASSERT_EQ(120, result.x());
ASSERT_EQ(-40, result.y());
}
TEST_F(VectorFxSuite, Rotate_FromBottomHalf_270_Degrees) {
auto bottomHalf = VectorFx::fromInt(120, 200, 0);
auto vector = VectorFx::fromInt(120, 80, 0);
auto result = vector.rotateAsCenter(bottomHalf, grad2rad(270)).toInt();
ASSERT_EQ(0, result.x());
ASSERT_EQ(80, result.y());
}
// ---- //
TEST_F(VectorFxSuite, ToString) {