raserizer fixes, vectorpx not needed, reduce bitshift ops

This commit is contained in:
wgroeneveld 2020-07-12 08:27:32 +02:00
parent d3bd172d96
commit 2ee8fe4e4a
14 changed files with 185 additions and 43 deletions

View File

@ -10,7 +10,7 @@
int main() { int main() {
std::shared_ptr<GBAEngine> engine(new GBAEngine()); std::shared_ptr<GBAEngine> engine(new GBAEngine());
engine.get()->setRenderer(new WiredRenderer()); engine.get()->setRenderer(new RasterizerRenderer());
MonkeyScene* startScene = new MonkeyScene(engine); MonkeyScene* startScene = new MonkeyScene(engine);
engine->setScene(startScene); engine->setScene(startScene);

View File

@ -23,7 +23,7 @@ add_library(${PROJECT_NAME}
src/renderer/pixelrenderer.cpp src/renderer/pixelrenderer.cpp
src/renderer/wirerenderer.cpp src/renderer/wirerenderer.cpp
src/math.cpp src/math.cpp
src/sound_control.cpp src/scene.cpp src/timer.cpp src/vectorfx.cpp src/mesh.cpp) src/sound_control.cpp src/scene.cpp src/timer.cpp src/vectorfx.cpp src/mesh.cpp src/renderer/raserizerrenderer.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

@ -8,9 +8,9 @@
#include <libgba-sprite-engine/gba/tonc_types.h> #include <libgba-sprite-engine/gba/tonc_types.h>
#ifdef CODE_COMPILED_AS_PART_OF_TEST #ifdef CODE_COMPILED_AS_PART_OF_TEST
#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> #include <cmath>
#include <string> #include <string>
@ -37,8 +37,13 @@ INLINE FIXED fxsin(FIXED fxrad);
INLINE FIXED fxcos(FIXED fxrad); INLINE FIXED fxcos(FIXED fxrad);
INLINE float rnd(float val); INLINE float rnd(float val);
INLINE std::string fstr(FIXED which); INLINE std::string fstr(FIXED which);
INLINE FIXED interpolate(FIXED min, FIXED max, FIXED gradient);
// ---- impl // ---- impl
INLINE FIXED interpolate(FIXED min, FIXED max, FIXED gradient) {
return min + fxmul((max - min), CLAMP(gradient, 0, ONE));
}
INLINE float rnd(float val) { INLINE float rnd(float val) {
return (float) ((std::floor(val * 100) + .5) / 100); return (float) ((std::floor(val * 100) + .5) / 100);
} }

View File

@ -9,7 +9,6 @@
#include <libgba-sprite-engine/gba/tonc_memmap.h> #include <libgba-sprite-engine/gba/tonc_memmap.h>
#include <libgba-sprite-engine/gba/tonc_memmap.h> #include <libgba-sprite-engine/gba/tonc_memmap.h>
#include <libgba-sprite-engine/vectorfx.h> #include <libgba-sprite-engine/vectorfx.h>
#include <libgba-sprite-engine/vectorpx.h>
#include <libgba-sprite-engine/matrixfx.h> #include <libgba-sprite-engine/matrixfx.h>
#include <libgba-sprite-engine/renderer/renderer.h> #include <libgba-sprite-engine/renderer/renderer.h>
#include "libgba-sprite-engine/scene.h" #include "libgba-sprite-engine/scene.h"
@ -71,10 +70,11 @@ public:
void setRenderer(Renderer* r) { renderer = std::unique_ptr<Renderer>(r); }; void setRenderer(Renderer* r) { renderer = std::unique_ptr<Renderer>(r); };
VectorPx project(const VectorFx &coord, const MatrixFx &transMat); VectorFx project(const VectorFx &coord, const MatrixFx &transMat);
void plotPixel(int x, int y, u8 clrId); void plotPixel(int x, int y, u8 clrId);
void plotPixel(const VectorPx &pixel, u8 clrId); void plotPixel(const VectorFx &pixel, u8 clrId);
void plotLine(const VectorPx &point0, const VectorPx &point1, u8 clrId); void plotLine(const VectorFx &point0, const VectorFx &point1, u8 clrId);
void plotLine(int p0x, int p0y, int p1x, int p1y, u8 clrId);
}; };

View File

@ -21,13 +21,20 @@ public:
class PixelRenderer : public Renderer { class PixelRenderer : public Renderer {
public: public:
void render(const MatrixFx &transformationMatrix, const Mesh* mesh) override; void render(const MatrixFx &transformationMatrix, const Mesh* mesh) override;
}; };
class WiredRenderer : public Renderer { class WiredRenderer : public Renderer {
public: public:
void render(const MatrixFx &transformationMatrix, const Mesh* mesh) override; void render(const MatrixFx &transformationMatrix, const Mesh* mesh) override;
};
class RasterizerRenderer : public Renderer {
private:
void plotTriangle(const VectorFx& p1, const VectorFx& p2, const VectorFx& p3, COLOR color);
void processScanLine(FIXED y, const VectorFx& pa, const VectorFx& pb, const VectorFx& pc, const VectorFx& pd, COLOR color);
public:
void render(const MatrixFx &transformationMatrix, const Mesh* mesh) override;
}; };
#endif //GBA_BITMAP_ENGINE_PROJECT_RENDERER_H #endif //GBA_BITMAP_ENGINE_PROJECT_RENDERER_H

View File

@ -88,6 +88,20 @@ public:
v.z = fxmul(v.z, num); v.z = fxmul(v.z, num);
} }
inline static FIXED gradient(FIXED y, const VectorFx& pa, const VectorFx &pb) {
if(pa.y() == pb.y()) {
return int2fx(1);
}
return fxdiv((y - pa.y()), (pb.y() - pa.y()));
}
inline static FIXED slope(const VectorFx &p1, const VectorFx &p2) {
if(p2.y() - p1.y() > 0) {
return fxdiv(p2.x() - p1.x(), p2.y() - p1.y());
}
return 0;
}
inline FIXED x() const { return v.x; } inline FIXED x() const { return v.x; }
inline float floatX() const { return fx2float(v.x); } inline float floatX() const { return fx2float(v.x); }
inline void setX(FIXED x) { v.x = x; } inline void setX(FIXED x) { v.x = x; }

View File

@ -1,23 +0,0 @@
//
// Created by Wouter Groeneveld on 10/07/20.
//
#ifndef GBA_BITMAP_ENGINE_PROJECT_VECTOR2_H
#define GBA_BITMAP_ENGINE_PROJECT_VECTOR2_H
// a 2D vector with pixel-based ints
class VectorPx {
private:
int v_x, v_y;
public:
VectorPx(int theX, int theY) : v_x(theX), v_y(theY) {}
static inline VectorPx fromFx(FIXED x, FIXED y) { return VectorPx(fx2int(x), fx2int(y)); }
inline int x() const { return v_x; }
inline int y() const { return v_y; }
};
#endif //GBA_BITMAP_ENGINE_PROJECT_VECTOR2_H

View File

@ -163,7 +163,7 @@ void bmp8_line(int x1, int y1, int x2, int y2, u32 clr,
// with mask-flips // with mask-flips
// NOTE: (mask>>31) is equivalent to (x&1) ? 0 : 1 // NOTE: (mask>>31) is equivalent to (x&1) ? 0 : 1
if(dx>=dy) // Diagonal, slope <= 1 if(dx>=dy) // Diagonal, slopeFx <= 1
{ {
dd= 2*dy - dx; dd= 2*dy - dx;
@ -180,7 +180,7 @@ void bmp8_line(int x1, int y1, int x2, int y2, u32 clr,
mask = ~mask; mask = ~mask;
} }
} }
else // # Diagonal, slope > 1 else // # Diagonal, slopeFx > 1
{ {
dd= 2*dx - dy; dd= 2*dx - dy;

View File

@ -162,21 +162,29 @@ void GBAEngine::flipPage() {
REG_DISPCNT ^= DCNT_PAGE; REG_DISPCNT ^= DCNT_PAGE;
} }
void GBAEngine::plotPixel(const VectorPx &pixel, u8 clrId) { void GBAEngine::plotPixel(int x, int y, u8 clrId) {
m4_plot(pixel.x(), pixel.y(), clrId); m4_plot(x, y, clrId);
} }
void GBAEngine::plotLine(const VectorPx &point0, const VectorPx &point1, u8 clrId) { void GBAEngine::plotPixel(const VectorFx &pixel, u8 clrId) {
// uses tonc's optimalization tricks to get 10 FPS extra compared to standard bline algorithms plotPixel(fx2int(pixel.x()), fx2int(pixel.y()), clrId);
m4_line(point0.x(), point0.y(), point1.x(), point1.y(), clrId);
} }
VectorPx GBAEngine::project(const VectorFx &coord, const MatrixFx &transMat) { // uses tonc's optimalization tricks to get 10 FPS extra compared to standard bline algorithms
void GBAEngine::plotLine(int p0x, int p0y, int p1x, int p1y, u8 clrId) {
m4_line(p0x, p0y, p1x, p1y, clrId);
}
void GBAEngine::plotLine(const VectorFx &point0, const VectorFx &point1, u8 clrId) {
plotLine(fx2int(point0.x()), fx2int(point0.y()), fx2int(point1.x()), fx2int(point1.y()), clrId);
}
VectorFx GBAEngine::project(const VectorFx &coord, const MatrixFx &transMat) {
auto point = MatrixFx::transformCoordinates(coord, transMat); auto point = MatrixFx::transformCoordinates(coord, transMat);
auto x = fxmul(point.x(), GBA_SCREEN_WIDTH_FX) + fxdiv(GBA_SCREEN_WIDTH_FX, TWO); 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); auto y = fxmul(-point.y(), GBA_SCREEN_HEIGHT_FX) + fxdiv(GBA_SCREEN_HEIGHT_FX, TWO);
return VectorPx::fromFx(x, y); return VectorFx(x, y, point.z());
} }
// does the mesh rendering - to write mem before flipping // does the mesh rendering - to write mem before flipping

View File

@ -0,0 +1,82 @@
//
// Created by Wouter Groeneveld on 11/07/20.
//
#include <libgba-sprite-engine/renderer/renderer.h>
#include <libgba-sprite-engine/renderer/gba_engine.h>
#include <libgba-sprite-engine/math.h>
#include <libgba-sprite-engine/background/text_stream.h>
// drawing line between 2 points from left to right
// papb -> pcpd
// pa, pb, pc, pd must then be sorted before
void RasterizerRenderer::processScanLine(FIXED y, const VectorFx& pa, const VectorFx& pb, const VectorFx& pc, const VectorFx& pd, COLOR color) {
FIXED gradient1 = VectorFx::gradient(y, pa, pb), gradient2 = VectorFx::gradient(y, pc, pd);
int sx = fx2int(interpolate(pa.x(), pb.x(), gradient1));
int ex = fx2int(interpolate(pc.x(), pd.x(), gradient2));
int yy = fx2int(y);
engine.get()->plotLine(sx, yy, ex, yy, color);
}
void RasterizerRenderer::plotTriangle(const VectorFx& pt1, const VectorFx& pt2, const VectorFx& pt3, COLOR color) {
// Sorting the points in order to always have this order on screen p1, p2 & p3
// with p1 always up (thus having the Y the lowest possible to be near the top screen)
// then p2 between p1 & p3
auto p1 = &pt1, p2 = &pt2, p3 = &pt3;
if (p1->y() > p2->y()) {
auto temp = p2;
p2 = p1;
p1 = temp;
}
if (p2->y() > p3->y()) {
auto temp = p2;
p2 = p3;
p3 = temp;
}
if (p1->y() > p2->y()) {
auto temp = p2;
p2 = p1;
p1 = temp;
}
FIXED dP1P2 = VectorFx::slope(*p1, *p2), dP1P3 = VectorFx::slope(*p1, *p3);
if (dP1P2 > dP1P3) {
for (auto y = p1->y(); y <= p3->y(); y+=ONE) {
if (y < p2->y()) {
processScanLine(y, *p1, *p3, *p1, *p2, color);
} else {
processScanLine(y, *p1, *p3, *p2, *p3, color);
}
}
} else {
for (auto y = p1->y(); y <= p3->y(); y+=ONE) {
if (y < p2->y()) {
processScanLine(y, *p1, *p2, *p1, *p3, color);
} else {
processScanLine(y, *p2, *p3, *p1, *p3, color);
}
}
}
}
void RasterizerRenderer::render(const MatrixFx &transformationMatrix, const Mesh *mesh) {
bool colorSwitch = false;
for (auto &face : mesh->faces()) {
auto &vertexA = mesh->vertices()[face.a];
auto &vertexB = mesh->vertices()[face.b];
auto &vertexC = mesh->vertices()[face.c];
auto pixelA = engine->project(*vertexA.get(), transformationMatrix);
auto pixelB = engine->project(*vertexB.get(), transformationMatrix);
auto pixelC = engine->project(*vertexC.get(), transformationMatrix);
plotTriangle(pixelA, pixelB, pixelC, colorSwitch ? 2 : 1);
colorSwitch = !colorSwitch;
}
}

View File

@ -42,7 +42,7 @@ std::deque<VECTOR> VectorFx::bresenhamLineTo(VECTOR dest) {
y += step.y; y += step.y;
} }
} else if(delta.x >= delta.y) { } else if(delta.x >= delta.y) {
// Diagonal, slope <= 1 // Diagonal, slopeFx <= 1
dd = 2 * delta.y - delta.x; dd = 2 * delta.y - delta.x;
for(ii = 0; ii <= delta.x; ii++) { for(ii = 0; ii <= delta.x; ii++) {
@ -55,7 +55,7 @@ std::deque<VECTOR> VectorFx::bresenhamLineTo(VECTOR dest) {
x += step.x; x += step.x;
} }
} else { } else {
// Diagonal, slope > 1 // Diagonal, slopeFx > 1
dd = 2 * delta.x - delta.y; dd = 2 * delta.x - delta.y;
for(ii = 0; ii <= delta.y; ii++) { for(ii = 0; ii <= delta.y; ii++) {

View File

@ -17,6 +17,11 @@ protected:
} }
}; };
TEST_F(FpSuite, InterpolateTests) {
ASSERT_FLOAT_EQ(fx2float(interpolate(ONE, int2fx(10), float2fx(0))), 1);
ASSERT_FLOAT_EQ(fx2float(interpolate(ONE, int2fx(10), ONE)), 9.9648438);
ASSERT_FLOAT_EQ(fx2float(interpolate(ONE, int2fx(10), float2fx(0.1))), 1.8789062);
}
// from LUT doxygen: // from LUT doxygen:
//! Look-up a cosine value (2&#960; = 0x10000) //! Look-up a cosine value (2&#960; = 0x10000)

View File

@ -9,6 +9,7 @@
#include <libgba-sprite-engine/mesh.h> #include <libgba-sprite-engine/mesh.h>
#include <libgba-sprite-engine/camera.h> #include <libgba-sprite-engine/camera.h>
#include <libgba-sprite-engine/renderer/gba_engine.h> #include <libgba-sprite-engine/renderer/gba_engine.h>
#include <libgba-sprite-engine/gba/toolbox.h>
class MatrixFxSuite : public ::testing::Test { class MatrixFxSuite : public ::testing::Test {
protected: protected:

View File

@ -34,6 +34,49 @@ void assertVector(VectorFx expected, VectorFx actual) {
ASSERT_EQ(rnd2(fx2float(actual.z())), rnd2(fx2float(actual.z()))) << "z incorrect: (act, exp) " << str; ASSERT_EQ(rnd2(fx2float(actual.z())), rnd2(fx2float(actual.z()))) << "z incorrect: (act, exp) " << str;
} }
TEST_F(VectorFxSuite, GradientTest) {
int y = int2fx(88);
/*
* pa:
* x: 202.9834560692982
y: 84.93338221282914
z: 1.0091180828593247
pb:
x: x: 211.95655997027734
y: 104.46714141689979
z: 1.0090892519150232
gradient: 0.1569906619168215
sx: 204
*/
auto pa = VectorFx::fromInt(203, 85, 1), pb = VectorFx::fromInt(212, 104, 1);
auto gradient = VectorFx::gradient(y, pa, pb);
ASSERT_FLOAT_EQ(fx2float(gradient), 0.15625);
}
TEST_F(VectorFxSuite, GradientThenInterpolateTest) {
int y = int2fx(88);
auto pa = VectorFx::fromInt(203, 85, 1), pb = VectorFx::fromInt(212, 104, 1);
auto gradient = VectorFx::gradient(y, pa, pb);
auto interpolated = interpolate(pa.x(), pb.x(), gradient);
ASSERT_EQ(fx2int(interpolated), 204);
}
TEST_F(VectorFxSuite, ComputeSlope) {
/* p2:
* x: 210.58517158313248
y: 88.7224853699867
z: 1.0090972814678028
p1:
x: 202.9834560692982
y: 84.93338221282914
z: 1.0091180828593247
*/
auto p2 = VectorFx::fromInt(210, 88, 1), p1 = VectorFx::fromInt(203, 85, 1);
auto result = VectorFx::slope(p1, p2);
ASSERT_FLOAT_EQ(fx2float(result), 2.3320312);
}
TEST_F(VectorFxSuite, ToAndFromFixedIntWorks) { TEST_F(VectorFxSuite, ToAndFromFixedIntWorks) {
auto zAxis = VectorFx::fromInt(0, 0, -10); auto zAxis = VectorFx::fromInt(0, 0, -10);
ASSERT_EQ(zAxis.y(), 0); ASSERT_EQ(zAxis.y(), 0);