diff --git a/demos/demo1-wireframes/src/wirescene.cpp b/demos/demo1-wireframes/src/wirescene.cpp index d660f8d..bad47e6 100644 --- a/demos/demo1-wireframes/src/wirescene.cpp +++ b/demos/demo1-wireframes/src/wirescene.cpp @@ -32,23 +32,32 @@ void WireScene::load() { 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 - */ - //cube->rotateTo(int2fx(13), int2fx(13)); + + cube->addFace({ 0, 1, 2}); + cube->addFace({ 1, 2, 3}); + cube->addFace({ 1, 3, 6}); + cube->addFace({ 1, 5, 6}); + cube->addFace({ 0, 1, 4}); + cube->addFace({ 1, 4, 5}); + + cube->addFace({ 2, 3, 7}); + cube->addFace({ 3, 6, 7}); + cube->addFace({ 0, 2, 7}); + cube->addFace({ 0, 4, 7}); + cube->addFace({ 4, 5, 6}); + cube->addFace({ 4, 6, 7}); } void WireScene::tick(u16 keys) { cube->rotate(2, 2); - if(keys & KEY_START || keys & KEY_A) { + + if(keys & KEY_A) { cube->resetRotation(); + } else if(keys & KEY_B) { + if(cube->isWired()) { + cube->unwire(); + } else { + cube->wire(); + } } } diff --git a/engine/include/libgba-sprite-engine/gba_engine.h b/engine/include/libgba-sprite-engine/gba_engine.h index 5d647de..7858899 100644 --- a/engine/include/libgba-sprite-engine/gba_engine.h +++ b/engine/include/libgba-sprite-engine/gba_engine.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "scene.h" #include "sound_control.h" @@ -50,8 +51,7 @@ private: void render(); void renderClear(); - inline void plotPixel(int x, int y, u8 clrId); - inline VectorFx project(const VectorFx &coord, const MatrixFx &transMat); + inline VectorPx project(const VectorFx &coord, const MatrixFx &transMat); void flipPage(); public: @@ -70,9 +70,12 @@ public: u16 readKeys(); void update(); - void delay(int times) { + inline void delay(int times) { for(int i = 0; i < times; i++){} } + + inline void plotPixel(const VectorPx &pixel, u8 clrId); + inline void plotLine(const VectorPx &point0, const VectorPx &point1, u8 clrId); }; diff --git a/engine/include/libgba-sprite-engine/mesh.h b/engine/include/libgba-sprite-engine/mesh.h index 2ed0c0d..cd76ff2 100644 --- a/engine/include/libgba-sprite-engine/mesh.h +++ b/engine/include/libgba-sprite-engine/mesh.h @@ -9,23 +9,41 @@ #include #include +typedef struct { + int a; + int b; + int c; +} Face; + class Mesh { private: VectorFx pos; VectorFx rot; + u8 cIndex; + bool wired; std::vector> verticesArr; + std::vector facesArr; public: void add(VectorFx v); + void addFace(Face f); inline std::vector> const& vertices() const { return verticesArr; } + inline std::vector const& faces() const { + return facesArr; + } + inline VectorFx &position() { return pos; } inline VectorFx &rotation() { return rot; } + inline void wire() { wired = true; } + inline void unwire() { wired = false; } + inline bool isWired() const { return wired; } + inline u8 colorIndex() const { return cIndex; } inline FIXED rotx() const { return rot.x(); } inline FIXED roty() const { return rot.y(); } inline FIXED rotz() const { return rot.z(); } @@ -43,7 +61,7 @@ public: rot.setY(rot.y() + y); } - explicit Mesh() : pos(VectorFx()), rot(VectorFx()) {} + explicit Mesh() : pos(VectorFx()), rot(VectorFx()), cIndex(1), wired(true) {} Mesh(const Mesh&) = delete; Mesh& operator=(const Mesh&) = delete; }; diff --git a/engine/include/libgba-sprite-engine/vectorpx.h b/engine/include/libgba-sprite-engine/vectorpx.h new file mode 100644 index 0000000..4fecd3b --- /dev/null +++ b/engine/include/libgba-sprite-engine/vectorpx.h @@ -0,0 +1,23 @@ +// +// 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 diff --git a/engine/src/gba_engine.cpp b/engine/src/gba_engine.cpp index f08d2f6..2519497 100644 --- a/engine/src/gba_engine.cpp +++ b/engine/src/gba_engine.cpp @@ -8,6 +8,7 @@ #include #include #include +#include std::unique_ptr GBAEngine::activeChannelA; std::unique_ptr GBAEngine::activeChannelB; @@ -147,23 +148,46 @@ void GBAEngine::flipPage() { // 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) { +inline void GBAEngine::plotPixel(const VectorPx &pixel, u8 clrId) { + u16 *dst = &vid_page[(pixel.y() * M4_WIDTH + pixel.x()) / 2]; + if(pixel.x() & 1) { *dst = (*dst & 0xFF) | (clrId << 8); } else { *dst = (*dst & ~0xFF) | clrId; } } -int i = 0; +// more or less 1-to-1: +// https://www.davrous.com/2013/06/14/tutorial-part-2-learning-how-to-write-a-3d-soft-engine-from-scratch-in-c-ts-or-js-drawing-lines-triangles/ +inline void GBAEngine::plotLine(const VectorPx &point0, const VectorPx &point1, u8 clrId) { + int x0 = point0.x(); + int y0 = point0.y(); + int x1 = point1.x(); + int y1 = point1.y(); -inline VectorFx GBAEngine::project(const VectorFx &coord, const MatrixFx &transMat) { + int dx = ABS(x1 - x0); + int dy = ABS(y1 - y0); + int sx = (x0 < x1) ? 1 : -1; + int sy = (y0 < y1) ? 1 : -1; + int err = dx - dy; + + while (true) { + plotPixel(VectorPx(x0, y0), clrId); + + if ((x0 == x1) && (y0 == y1)) break; + auto e2 = 2 * err; + if (e2 > -dy) { err -= dy; x0 += sx; } + if (e2 < dx) { err += dx; y0 += sy; } + } +} + + +inline VectorPx 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, TWO); auto y = fxmul(-point.y(), GBA_SCREEN_HEIGHT_FX) + fxdiv(GBA_SCREEN_HEIGHT_FX, TWO); - return VectorFx(x, y, 0); + return VectorPx::fromFx(x, y); } // does the mesh rendering - to write mem before flipping @@ -174,11 +198,27 @@ void GBAEngine::render() { auto worldMatrix = MatrixFx::rotationYawPitchRoll(mesh->roty(), mesh->rotx(), mesh->rotz()) * MatrixFx::translation(mesh->position()); auto transformMatrix = worldMatrix * viewMatrix * projectionMatrix; - TextStream::instance().setText("rot: " + mesh->rotation().to_stringfl(), 1, 1); + if(mesh->isWired()) { + // triangular faces wireframes + for(auto& face : mesh->faces()) { + auto& vertexA = mesh->vertices()[face.a]; + auto& vertexB = mesh->vertices()[face.b]; + auto& vertexC = mesh->vertices()[face.c]; - for(auto& vertex : mesh->vertices()) { - auto projectedPoint = project(*vertex.get(), transformMatrix).toInt(); - plotPixel(projectedPoint.x(), projectedPoint.y(), 1); + auto pixelA = project(*vertexA.get(), transformMatrix); + auto pixelB = project(*vertexB.get(), transformMatrix); + auto pixelC = project(*vertexC.get(), transformMatrix); + + plotLine(pixelA, pixelB, mesh->colorIndex()); + plotLine(pixelB, pixelC, mesh->colorIndex()); + plotLine(pixelC, pixelA, mesh->colorIndex()); + } + } else { + // pixel-only + for (auto &vertex : mesh->vertices()) { + auto projectedPoint = project(*vertex.get(), transformMatrix); + plotPixel(projectedPoint, mesh->colorIndex()); + } } } } diff --git a/engine/src/mesh.cpp b/engine/src/mesh.cpp index a4eab63..ffe2d33 100644 --- a/engine/src/mesh.cpp +++ b/engine/src/mesh.cpp @@ -10,3 +10,6 @@ void Mesh::add(VectorFx v) { verticesArr.push_back(std::unique_ptr(new VectorFx(v))); } +void Mesh::addFace(Face f) { + facesArr.push_back(f); +}