diff options
author | vimene <vincent.menegaux@gmail.com> | 2023-12-05 10:42:35 +0100 |
---|---|---|
committer | vimene <vincent.menegaux@gmail.com> | 2023-12-05 10:42:35 +0100 |
commit | 9b70ca7b3a1b7bfd3bf434d8b84bde42f5572ca0 (patch) | |
tree | 7078a70f7fc5b590c61386f55f4fbcc67e36b0f3 | |
parent | 48ec7df0fd27fab05c9918e83a3a7da1cc32d7a0 (diff) | |
download | engine-9b70ca7b3a1b7bfd3bf434d8b84bde42f5572ca0.tar.gz |
added renderer, improved various things
- Added renderer to unify frame buffers
- Added FrameBuffer class as a parent for frame buffers' classes
- Improved formatting in Makefile.am
- Added const modifier in Matrix4's methods
-rw-r--r-- | Makefile.am | 23 | ||||
-rw-r--r-- | src/engine.cpp | 36 | ||||
-rw-r--r-- | src/fb/chfb.cpp | 119 | ||||
-rw-r--r-- | src/fb/chfb.h | 12 | ||||
-rw-r--r-- | src/fb/fb.h | 20 | ||||
-rw-r--r-- | src/fb/pixfb.cpp | 116 | ||||
-rw-r--r-- | src/fb/pixfb.h | 11 | ||||
-rw-r--r-- | src/math/mat4.cpp | 24 | ||||
-rw-r--r-- | src/math/mat4.h | 15 | ||||
-rw-r--r-- | src/o3d/vertex_data.cpp | 4 | ||||
-rw-r--r-- | src/o3d/vertex_data.h | 4 | ||||
-rw-r--r-- | src/renderer.cpp | 145 | ||||
-rw-r--r-- | src/renderer.h | 30 |
13 files changed, 276 insertions, 283 deletions
diff --git a/Makefile.am b/Makefile.am index c0f1b53..ecfea3a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,22 +1,23 @@ EXTRA_DIST = m4/NOTES SUBDIRS = if ENABLE_TESTS - SUBDIRS += tests + SUBDIRS += tests endif ACLOCAL_AMFLAGS = -Im4 --install bin_PROGRAMS = engine -engine_SOURCES = src/engine.cpp \ - src/fb/chfb.h src/fb/chfb.cpp src/fb/pixfb.h src/fb/pixfb.cpp \ - src/math/vector.h src/math/vector.cpp \ - src/math/mat4.h src/math/mat4.cpp \ - src/o3d/mesh.h src/o3d/mesh.cpp src/o3d/tri_vertex.h \ - src/o3d/obj3d.h src/o3d/obj3d.cpp src/o3d/tri_vertex.h \ - src/o3d/tri_vertex.cpp src/o3d/vertex.h src/o3d/vertex.cpp \ - src/o3d/vertex_data.h src/o3d/vertex_data.cpp \ - src/o3d/camera.h src/o3d/camera.cpp \ - src/o3d/scene.h src/o3d/scene.cpp +engine_SOURCES = \ + src/engine.cpp src/renderer.h src/renderer.cpp \ + src/fb/fb.h src/fb/chfb.h src/fb/chfb.cpp src/fb/pixfb.h src/fb/pixfb.cpp \ + src/math/vector.h src/math/vector.cpp \ + src/math/mat4.h src/math/mat4.cpp \ + src/o3d/mesh.h src/o3d/mesh.cpp src/o3d/tri_vertex.h \ + src/o3d/obj3d.h src/o3d/obj3d.cpp src/o3d/tri_vertex.h \ + src/o3d/tri_vertex.cpp src/o3d/vertex.h src/o3d/vertex.cpp \ + src/o3d/vertex_data.h src/o3d/vertex_data.cpp \ + src/o3d/camera.h src/o3d/camera.cpp \ + src/o3d/scene.h src/o3d/scene.cpp engine_CPPFLAGS = -Wall -Wextra $(DEPS_CPPFLAGS) engine_LDFLAGS = -Wall -Wextra engine_LDADD = $(DEPS_LIBS) diff --git a/src/engine.cpp b/src/engine.cpp index aeae21c..91c0b80 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -10,6 +10,8 @@ #include <functional> #include <utility> #include <iterator> +#include <memory> +#include <utility> #include <SDL.h> #ifdef ENABLE_NCURSES @@ -27,6 +29,7 @@ #include "o3d/camera.h" #include "math/vector.h" #include "math/mat4.h" +#include "renderer.h" #define FPS 60 @@ -50,8 +53,7 @@ static void usage_error_exit() { std::exit(EXIT_FAILURE); } -template <class FB> -static void scene_main(FB& fb, std::function<bool()> update_frame) { +static void scene_main(engine::Renderer& renderer, engine::math::Matrix4 final_transform_mat, std::function<bool()> update_frame) { float dist = 4.f; float rad = 5.f; bool cont = true; @@ -76,7 +78,7 @@ static void scene_main(FB& fb, std::function<bool()> update_frame) { scene.camera.rot_y += .0065f; scene.camera.rot_z += .0080f; - fb.clear(); + renderer.clear(); auto transform_mat = engine::math::Matrix4::translate(-scene.camera.loc) * engine::math::Matrix4::rot_x(-scene.camera.rot_x) @@ -86,15 +88,16 @@ static void scene_main(FB& fb, std::function<bool()> update_frame) { transform_mat * engine::math::Matrix4::translate(engine::math::Vector3{-.5f * rad, -.5f * rad, -.5f * rad}) * scale_mat, transform_mat * engine::math::Matrix4::translate(engine::math::Vector3{+.5f * rad, +.5f * rad, +.5f * rad}) * scale_mat, }}; - auto projection_mat = engine::math::Matrix4::projection(static_cast<float>(fb.height()) / static_cast<float>(fb.width()), 2.f, 50.f); + auto pre_final_mat = final_transform_mat + * engine::math::Matrix4::projection(static_cast<float>(renderer.height()) / static_cast<float>(renderer.width()), 2.f, 50.f); for (int i = 0; i < 2; i++) { - auto final_mat = projection_mat * mats[i]; + auto final_mat = pre_final_mat * mats[i]; const auto& mesh = scene.objs[i].mesh; std::vector<engine::o3d::Vertex4> pts; for (const auto& vert : mesh.pts) pts.push_back({ final_mat * engine::math::Vector4{vert.point}, vert.data }); for (auto face : mesh.faces) - fb.draw_triangle({pts[face[0]], pts[face[1]], pts[face[2]]}); + renderer.draw_triangle({pts[face[0]], pts[face[1]], pts[face[2]]}); } cont = update_frame(); } @@ -121,10 +124,13 @@ static int main_term() { int w, h; getmaxyx(stdscr, h, w); - engine::fb::CharacterFrameBuffer cfb{static_cast<unsigned int>(w), static_cast<unsigned int>(h)}; + engine::Renderer renderer{ + std::make_unique<engine::fb::CharacterFrameBuffer>(static_cast<unsigned int>(w), static_cast<unsigned int>(h))}; - scene_main(cfb, [&]() { - mvaddnstr(0, 0, cfb.chars(), cfb.width() * cfb.height()); + scene_main(renderer, engine::math::Matrix4::scale(engine::math::Vector3(2.f, 1.f, 1.f)), [&]() { + mvaddnstr(0, 0, + static_cast<engine::fb::CharacterFrameBuffer*>(renderer.fb.get())->chars(), + renderer.width() * renderer.height()); bool cont = true; //timeout(1000 / FPS); @@ -156,7 +162,7 @@ static int main_term() { } getmaxyx(stdscr, h, w); - cfb.resize(static_cast<unsigned int>(w), static_cast<unsigned int>(h)); + renderer.resize(static_cast<unsigned int>(w), static_cast<unsigned int>(h)); return cont; }); @@ -183,17 +189,21 @@ static int main_graphical() { } window = SDL_CreateWindow("Engine", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN); if (window == NULL) { + SDL_Quit(); std::cerr << "Error: SDL_CreateWindow error: " << SDL_GetError() << std::endl; return EXIT_FAILURE; } renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH, SCREEN_HEIGHT); - engine::fb::PixelFrameBuffer pfb{SCREEN_WIDTH, SCREEN_HEIGHT}; + engine::Renderer engine_renderer{ + std::make_unique<engine::fb::PixelFrameBuffer>(SCREEN_WIDTH, SCREEN_HEIGHT)}; SDL_Event e; - scene_main(pfb, [&]() { - SDL_UpdateTexture(texture, NULL, pfb.pixels(), SCREEN_WIDTH * 4); + scene_main(engine_renderer, engine::math::Matrix4::idty(), [&]() { + SDL_UpdateTexture(texture, NULL, + static_cast<engine::fb::PixelFrameBuffer*>(engine_renderer.fb.get())->pixels(), + SCREEN_WIDTH * 4); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); diff --git a/src/fb/chfb.cpp b/src/fb/chfb.cpp index c0e5cc7..b5c0ae0 100644 --- a/src/fb/chfb.cpp +++ b/src/fb/chfb.cpp @@ -17,7 +17,6 @@ void CharacterFrameBuffer::resize(unsigned int w, unsigned int h) { this->w = w; this->h = h; chars_vector.resize(w * h); - depth_buf.resize(w * h); clear(); } @@ -35,124 +34,14 @@ const char* CharacterFrameBuffer::chars() const { void CharacterFrameBuffer::clear() { std::fill(chars_vector.begin(), chars_vector.end(), ' '); - std::fill(depth_buf.begin(), depth_buf.end(), 1.f); - face_ind = -1; } -void CharacterFrameBuffer::_draw_cropped_triangle(engine::o3d::TriangleVertex3 triangle) { - std::array<engine::o3d::Vertex3*, 3> sorted_vs = { &triangle.vertex1, &triangle.vertex2, &triangle.vertex3 }; - -#define SWAP_IF_LT(X,Y) ({\ - if (sorted_vs[X]->point.y < sorted_vs[Y]->point.y) {\ - engine::o3d::Vertex3* temp = sorted_vs[X];\ - sorted_vs[X] = sorted_vs[Y];\ - sorted_vs[Y] = temp;\ - }\ - }) - SWAP_IF_LT(1, 0); - SWAP_IF_LT(2, 1); - SWAP_IF_LT(1, 0); -#undef SWAP_IF_LT - - engine::o3d::Vertex3 middle_vl = *sorted_vs[1]; - float fac = (sorted_vs[1]->point.y - sorted_vs[0]->point.y) / (sorted_vs[2]->point.y - sorted_vs[0]->point.y); - engine::o3d::Vertex3 middle_vr{ - { - sorted_vs[0]->point.x + fac * (sorted_vs[2]->point.x - sorted_vs[0]->point.x), - sorted_vs[1]->point.y, - sorted_vs[0]->point.z + fac * (sorted_vs[2]->point.z - sorted_vs[0]->point.z) - }, - engine::o3d::VertexData::lerp(sorted_vs[0]->data, sorted_vs[2]->data, fac) - }; - if (middle_vr.point.x < middle_vl.point.x) { - engine::o3d::Vertex3 temp = middle_vr; - middle_vr = middle_vl; - middle_vl = temp; - } - - // top triangle - { - if (sorted_vs[0]->point.y != sorted_vs[1]->point.y) { - int top_y = static_cast<int>(std::floor(sorted_vs[0]->point.y + 1.f)); - int bottom_y = static_cast<int>(std::ceil(sorted_vs[1]->point.y - 1.f)); - for (int y = top_y; y <= bottom_y; y++) { - float iy = static_cast<float>(y); - float s = (iy - sorted_vs[0]->point.y) / (sorted_vs[1]->point.y - sorted_vs[0]->point.y); - float xl = sorted_vs[0]->point.x + s * (middle_vl.point.x - sorted_vs[0]->point.x); - float xr = sorted_vs[0]->point.x + s * (middle_vr.point.x - sorted_vs[0]->point.x); - int left_x = static_cast<int>(std::ceil(xl)); - int right_x = static_cast<int>(std::ceil(xr - 1.f)); - for (int x = left_x; x <= right_x; x++) { - float ix = static_cast<float>(x); - float t = (ix - xl) / (xr - xl); - // depth and vd don't take into account perspective - float depth = sorted_vs[0]->point.z + s * (middle_vl.point.z - sorted_vs[0]->point.z + t * (middle_vr.point.z - middle_vl.point.z)); - // VertexData vd = VertexData::bilerp(sorted_vs[0]->data, middle_vl.data, middle_vr.data, s, t); - if (depth < depth_buf[x + y * w]) { - depth_buf[x + y * w] = depth; - chars_vector[x + y * w] = face_char(); - } - } - } - } - } - - // bottom triangle - { - if (sorted_vs[1]->point.y != sorted_vs[2]->point.y) { - int bottom_y = static_cast<int>(std::floor(sorted_vs[2]->point.y)); - int top_y = static_cast<int>(std::ceil(sorted_vs[1]->point.y)); - for (int y = top_y; y <= bottom_y; y++) { - float iy = static_cast<float>(y); - float s = (iy - sorted_vs[2]->point.y) / (sorted_vs[1]->point.y - sorted_vs[2]->point.y); - float xl = sorted_vs[2]->point.x + s * (middle_vl.point.x - sorted_vs[2]->point.x); - float xr = sorted_vs[2]->point.x + s * (middle_vr.point.x - sorted_vs[2]->point.x); - int left_x = static_cast<int>(std::ceil(xl)); - int right_x = static_cast<int>(std::ceil(xr - 1.f)); - for (int x = left_x; x <= right_x; x++) { - float ix = static_cast<float>(x); - float t = (ix - xl) / (xr - xl); - // depth and vd don't take into account perspective - float depth = sorted_vs[2]->point.z + s * (middle_vl.point.z - sorted_vs[2]->point.z + t * (middle_vr.point.z - middle_vl.point.z)); - // VertexData vd = VertexData::bilerp(sorted_vs[2]->data, middle_vl.data, middle_vr.data, s, t); - if (depth < depth_buf[x + y * w]) { - depth_buf[x + y * w] = depth; - chars_vector[x + y * w] = face_char(); - } - } - } - } - } -} - -void CharacterFrameBuffer::draw_triangle(engine::o3d::TriangleVertex4 triangle) { - face_ind++; - for (auto t1 : triangle.crop_z_out(-1.f, 1.f)) { - auto t1_2 = t1.div_by_w(); - t1_2.vertex1.point.x *= 2.f; - t1_2.vertex2.point.x *= 2.f; - t1_2.vertex3.point.x *= 2.f; - for (auto t2 : t1_2.crop_xy_out(-1.f, 1.f, -1.f, 1.f)) { - engine::math::Vector2 pp1 = t2.vertex1.point.xy(), - pp2 = t2.vertex2.point.xy(), - pp3 = t2.vertex3.point.xy(); - if ((pp2 - pp1).det(pp3 - pp1) >= 0.f) continue; - t2.vertex1.point = (t2.vertex1.point + engine::math::Vector3{1.f, 1.f, 0.f}) / 2.f; - t2.vertex2.point = (t2.vertex2.point + engine::math::Vector3{1.f, 1.f, 0.f}) / 2.f; - t2.vertex3.point = (t2.vertex3.point + engine::math::Vector3{1.f, 1.f, 0.f}) / 2.f; - float fw = static_cast<float>(w), fh = static_cast<float>(h); - t2.vertex1.point.x = t2.vertex1.point.x * fw - .5f; - t2.vertex1.point.y = t2.vertex1.point.y * fh - .5f; - t2.vertex2.point.x = t2.vertex2.point.x * fw - .5f; - t2.vertex2.point.y = t2.vertex2.point.y * fh - .5f; - t2.vertex3.point.x = t2.vertex3.point.x * fw - .5f; - t2.vertex3.point.y = t2.vertex3.point.y * fh - .5f; - _draw_cropped_triangle(t2); - } - } +void CharacterFrameBuffer::draw_point(int x, int y, engine::math::Vector3 loc, const engine::o3d::VertexData& vd) { + (void) vd; + chars_vector[x + y * w] = face_char(static_cast<int>(loc.x)); } -char CharacterFrameBuffer::face_char() const { +char CharacterFrameBuffer::face_char(int face_ind) const { int n = 1 + face_ind / 2; return (n < 10 ? '0' : 'A' - 10) + n; } diff --git a/src/fb/chfb.h b/src/fb/chfb.h index 9e10a0d..280f6d8 100644 --- a/src/fb/chfb.h +++ b/src/fb/chfb.h @@ -2,12 +2,11 @@ #define FB_CHFB_H #include <vector> -#include "math/vector.h" -#include "o3d/tri_vertex.h" +#include "fb/fb.h" namespace engine::fb { -class CharacterFrameBuffer { +class CharacterFrameBuffer : public FrameBuffer { public: CharacterFrameBuffer(unsigned int w, unsigned int h); void resize(unsigned int w, unsigned int h); @@ -15,16 +14,13 @@ class CharacterFrameBuffer { unsigned int height() const; const char* chars() const; void clear(); - void draw_triangle(engine::o3d::TriangleVertex4 triangle); + void draw_point(int x, int y, engine::math::Vector3 loc, const engine::o3d::VertexData& vd); private: unsigned int w, h; std::vector<char> chars_vector; - std::vector<float> depth_buf; - int face_ind; - void _draw_cropped_triangle(engine::o3d::TriangleVertex3 triangle); - char face_char() const; + char face_char(int face_inf) const; }; } diff --git a/src/fb/fb.h b/src/fb/fb.h new file mode 100644 index 0000000..04b13ee --- /dev/null +++ b/src/fb/fb.h @@ -0,0 +1,20 @@ +#ifndef FB_FB_H +#define FB_FB_H + +#include "math/vector.h" +#include "o3d/vertex_data.h" + +namespace engine::fb { + +class FrameBuffer { + public: + virtual void resize(unsigned int w, unsigned int h) = 0; + virtual unsigned int width() const = 0; + virtual unsigned int height() const = 0; + virtual void clear() = 0; + virtual void draw_point(int x, int y, engine::math::Vector3 loc, const engine::o3d::VertexData& vd) = 0; +}; + +} + +#endif // FB_FB_H diff --git a/src/fb/pixfb.cpp b/src/fb/pixfb.cpp index af220c6..26bc8f2 100644 --- a/src/fb/pixfb.cpp +++ b/src/fb/pixfb.cpp @@ -3,6 +3,7 @@ #include <cmath> #include <vector> #include <cstdint> +#include <algorithm> #include "math/vector.h" #include "o3d/vertex.h" #include "o3d/tri_vertex.h" @@ -18,7 +19,6 @@ void PixelFrameBuffer::resize(unsigned int w, unsigned int h) { this->w = w; this->h = h; pixels_vector.resize(w * h); - depth_buf.resize(w * h); clear(); } @@ -36,120 +36,14 @@ const uint32_t* PixelFrameBuffer::pixels() const { void PixelFrameBuffer::clear() { std::fill(pixels_vector.begin(), pixels_vector.end(), 0x000000FF); - std::fill(depth_buf.begin(), depth_buf.end(), 1.f); - face_ind = -1; } -void PixelFrameBuffer::_draw_cropped_triangle(engine::o3d::TriangleVertex3 triangle) { - std::array<engine::o3d::Vertex3*, 3> sorted_vs = { &triangle.vertex1, &triangle.vertex2, &triangle.vertex3 }; - -#define SWAP_IF_LT(X,Y) ({\ - if (sorted_vs[X]->point.y < sorted_vs[Y]->point.y) {\ - engine::o3d::Vertex3* temp = sorted_vs[X];\ - sorted_vs[X] = sorted_vs[Y];\ - sorted_vs[Y] = temp;\ - }\ - }) - SWAP_IF_LT(1, 0); - SWAP_IF_LT(2, 1); - SWAP_IF_LT(1, 0); -#undef SWAP_IF_LT - - engine::o3d::Vertex3 middle_vl = *sorted_vs[1]; - float fac = (sorted_vs[1]->point.y - sorted_vs[0]->point.y) / (sorted_vs[2]->point.y - sorted_vs[0]->point.y); - engine::o3d::Vertex3 middle_vr{ - { - sorted_vs[0]->point.x + fac * (sorted_vs[2]->point.x - sorted_vs[0]->point.x), - sorted_vs[1]->point.y, - sorted_vs[0]->point.z + fac * (sorted_vs[2]->point.z - sorted_vs[0]->point.z) - }, - engine::o3d::VertexData::lerp(sorted_vs[0]->data, sorted_vs[2]->data, fac) - }; - if (middle_vr.point.x < middle_vl.point.x) { - engine::o3d::Vertex3 temp = middle_vr; - middle_vr = middle_vl; - middle_vl = temp; - } - - // top triangle - { - if (sorted_vs[0]->point.y != sorted_vs[1]->point.y) { - int top_y = static_cast<int>(std::floor(sorted_vs[0]->point.y + 1.f)); - int bottom_y = static_cast<int>(std::ceil(sorted_vs[1]->point.y - 1.f)); - for (int y = top_y; y <= bottom_y; y++) { - float iy = static_cast<float>(y); - float s = (iy - sorted_vs[0]->point.y) / (sorted_vs[1]->point.y - sorted_vs[0]->point.y); - float xl = sorted_vs[0]->point.x + s * (middle_vl.point.x - sorted_vs[0]->point.x); - float xr = sorted_vs[0]->point.x + s * (middle_vr.point.x - sorted_vs[0]->point.x); - int left_x = static_cast<int>(std::ceil(xl)); - int right_x = static_cast<int>(std::ceil(xr - 1.f)); - for (int x = left_x; x <= right_x; x++) { - float ix = static_cast<float>(x); - float t = (ix - xl) / (xr - xl); - // depth and vd don't take into account perspective - float depth = sorted_vs[0]->point.z + s * (middle_vl.point.z - sorted_vs[0]->point.z + t * (middle_vr.point.z - middle_vl.point.z)); - // VertexData vd = VertexData::bilerp(sorted_vs[0]->data, middle_vl.data, middle_vr.data, s, t); - if (depth < depth_buf[x + y * w]) { - depth_buf[x + y * w] = depth; - pixels_vector[x + y * w] = face_color(); - } - } - } - } - } - - // bottom triangle - { - if (sorted_vs[1]->point.y != sorted_vs[2]->point.y) { - int bottom_y = static_cast<int>(std::floor(sorted_vs[2]->point.y)); - int top_y = static_cast<int>(std::ceil(sorted_vs[1]->point.y)); - for (int y = top_y; y <= bottom_y; y++) { - float iy = static_cast<float>(y); - float s = (iy - sorted_vs[2]->point.y) / (sorted_vs[1]->point.y - sorted_vs[2]->point.y); - float xl = sorted_vs[2]->point.x + s * (middle_vl.point.x - sorted_vs[2]->point.x); - float xr = sorted_vs[2]->point.x + s * (middle_vr.point.x - sorted_vs[2]->point.x); - int left_x = static_cast<int>(std::ceil(xl)); - int right_x = static_cast<int>(std::ceil(xr - 1.f)); - for (int x = left_x; x <= right_x; x++) { - float ix = static_cast<float>(x); - float t = (ix - xl) / (xr - xl); - // depth and vd don't take into account perspective - float depth = sorted_vs[2]->point.z + s * (middle_vl.point.z - sorted_vs[2]->point.z + t * (middle_vr.point.z - middle_vl.point.z)); - // VertexData vd = VertexData::bilerp(sorted_vs[2]->data, middle_vl.data, middle_vr.data, s, t); - if (depth < depth_buf[x + y * w]) { - depth_buf[x + y * w] = depth; - pixels_vector[x + y * w] = face_color(); - } - } - } - } - } -} - -void PixelFrameBuffer::draw_triangle(engine::o3d::TriangleVertex4 triangle) { - face_ind++; - for (auto t1 : triangle.crop_z_out(-1.f, 1.f)) { - for (auto t2 : t1.div_by_w().crop_xy_out(-1.f, 1.f, -1.f, 1.f)) { - engine::math::Vector2 pp1 = t2.vertex1.point.xy(), - pp2 = t2.vertex2.point.xy(), - pp3 = t2.vertex3.point.xy(); - if ((pp2 - pp1).det(pp3 - pp1) >= 0.f) continue; - t2.vertex1.point = (t2.vertex1.point + engine::math::Vector3{1.f, 1.f, 0.f}) / 2.f; - t2.vertex2.point = (t2.vertex2.point + engine::math::Vector3{1.f, 1.f, 0.f}) / 2.f; - t2.vertex3.point = (t2.vertex3.point + engine::math::Vector3{1.f, 1.f, 0.f}) / 2.f; - float fw = static_cast<float>(w), fh = static_cast<float>(h); - t2.vertex1.point.x = t2.vertex1.point.x * fw - .5f; - t2.vertex1.point.y = t2.vertex1.point.y * fh - .5f; - t2.vertex2.point.x = t2.vertex2.point.x * fw - .5f; - t2.vertex2.point.y = t2.vertex2.point.y * fh - .5f; - t2.vertex3.point.x = t2.vertex3.point.x * fw - .5f; - t2.vertex3.point.y = t2.vertex3.point.y * fh - .5f; - _draw_cropped_triangle(t2); - } - } +void PixelFrameBuffer::draw_point(int x, int y, engine::math::Vector3 loc, const engine::o3d::VertexData& vd) { + (void) vd; + pixels_vector[x + y * w] = face_color(static_cast<int>(loc.x)); } -uint32_t PixelFrameBuffer::face_color() const { +uint32_t PixelFrameBuffer::face_color(int face_ind) const { int n = face_ind / 2; return ((n % 2 ? 0xFF000000 : 0x55000000) >> ((n % 6) / 2 * 8)) | 0xFF; } diff --git a/src/fb/pixfb.h b/src/fb/pixfb.h index cdfac18..84bce2c 100644 --- a/src/fb/pixfb.h +++ b/src/fb/pixfb.h @@ -3,12 +3,11 @@ #include <vector> #include <cstdint> -#include "math/vector.h" -#include "o3d/tri_vertex.h" +#include "fb/fb.h" namespace engine::fb { -class PixelFrameBuffer { +class PixelFrameBuffer : public FrameBuffer { public: PixelFrameBuffer(unsigned int w, unsigned int h); void resize(unsigned int w, unsigned int h); @@ -16,16 +15,14 @@ class PixelFrameBuffer { unsigned int height() const; const uint32_t* pixels() const; void clear(); - void draw_triangle(engine::o3d::TriangleVertex4 triangle); + void draw_point(int x, int y, engine::math::Vector3 loc, const engine::o3d::VertexData& vd); private: unsigned int w, h; std::vector<uint32_t> pixels_vector; - std::vector<float> depth_buf; int face_ind; - void _draw_cropped_triangle(engine::o3d::TriangleVertex3 triangle); - uint32_t face_color() const; + uint32_t face_color(int face_ind) const; }; } diff --git a/src/math/mat4.cpp b/src/math/mat4.cpp index f1f28b7..b9dff15 100644 --- a/src/math/mat4.cpp +++ b/src/math/mat4.cpp @@ -22,6 +22,7 @@ Matrix4 Matrix4::translate(Vector3 v) { 0.f, 0.f, 0.f, 1.f, }; } + Matrix4 Matrix4::scale(float fac) { return { fac, 0.f, 0.f, 0.f, @@ -31,6 +32,15 @@ Matrix4 Matrix4::scale(float fac) { }; } +Matrix4 Matrix4::scale(Vector3 facs) { + return { + facs.x, 0.f, 0.f, 0.f, + 0.f, facs.y, 0.f, 0.f, + 0.f, 0.f, facs.z, 0.f, + 0.f, 0.f, 0.f, 1.f, + }; +} + Matrix4 Matrix4::rot_x(float a) { float c = std::cos(a); float s = std::sin(a); @@ -73,11 +83,11 @@ Matrix4 Matrix4::projection(float aspect_ratio, float min_z, float max_z) { }}; } -Matrix4 Matrix4::operator+() { +Matrix4 Matrix4::operator+() const { return *this; } -Matrix4 Matrix4::operator-() { +Matrix4 Matrix4::operator-() const { return { -values[ 0], -values[ 1], -values[ 2], -values[ 3], -values[ 4], -values[ 5], -values[ 6], -values[ 7], @@ -86,7 +96,7 @@ Matrix4 Matrix4::operator-() { }; } -Matrix4 Matrix4::operator+(Matrix4 m) { +Matrix4 Matrix4::operator+(Matrix4 m) const { return { values[ 0] + m.values[ 0], values[ 1] + m.values[ 1], values[ 2] + m.values[ 2], values[ 3] + m.values[ 3], values[ 4] + m.values[ 4], values[ 5] + m.values[ 5], values[ 6] + m.values[ 6], values[ 7] + m.values[ 7], @@ -95,11 +105,11 @@ Matrix4 Matrix4::operator+(Matrix4 m) { }; } -Matrix4 Matrix4::operator-(Matrix4 m) { +Matrix4 Matrix4::operator-(Matrix4 m) const { return *this + (-m); } -Matrix4 Matrix4::operator*(Matrix4 m) { +Matrix4 Matrix4::operator*(Matrix4 m) const { Matrix4 ret; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { @@ -111,7 +121,7 @@ Matrix4 Matrix4::operator*(Matrix4 m) { return ret; } -Vector4 Matrix4::operator*(Vector4 v) { +Vector4 Matrix4::operator*(Vector4 v) const { return { values[ 0] * v.x + values[ 1] * v.y + values[ 2] * v.z + values[ 3] * v.w, values[ 4] * v.x + values[ 5] * v.y + values[ 6] * v.z + values[ 7] * v.w, @@ -120,7 +130,7 @@ Vector4 Matrix4::operator*(Vector4 v) { }; } -std::array<Vector4, 4> Matrix4::to_vecs() { +std::array<Vector4, 4> Matrix4::to_vecs() const { return {{ { values[ 0], values[ 4], values[ 8], values[12] }, { values[ 1], values[ 5], values[ 9], values[13] }, diff --git a/src/math/mat4.h b/src/math/mat4.h index 66d6cd2..35b1ad2 100644 --- a/src/math/mat4.h +++ b/src/math/mat4.h @@ -11,6 +11,7 @@ class Matrix4 { static Matrix4 idty(); static Matrix4 translate(Vector3 v); static Matrix4 scale(float fac); + static Matrix4 scale(Vector3 facs); static Matrix4 rot_x(float a); static Matrix4 rot_y(float a); static Matrix4 rot_z(float a); @@ -18,13 +19,13 @@ class Matrix4 { std::array<float, 16> values; - Matrix4 operator+(); - Matrix4 operator-(); - Matrix4 operator+(Matrix4 m); - Matrix4 operator-(Matrix4 m); - Matrix4 operator*(Matrix4 m); - Vector4 operator*(Vector4 v); - std::array<Vector4, 4> to_vecs(); + Matrix4 operator+() const; + Matrix4 operator-() const; + Matrix4 operator+(Matrix4 m) const; + Matrix4 operator-(Matrix4 m) const; + Matrix4 operator*(Matrix4 m) const; + Vector4 operator*(Vector4 v) const; + std::array<Vector4, 4> to_vecs() const; }; Matrix4 operator*(float fac, Matrix4 m); diff --git a/src/o3d/vertex_data.cpp b/src/o3d/vertex_data.cpp index 4e0ab04..ea8c5fd 100644 --- a/src/o3d/vertex_data.cpp +++ b/src/o3d/vertex_data.cpp @@ -2,14 +2,14 @@ using namespace engine::o3d; -VertexData VertexData::lerp(VertexData& vd1, VertexData& vd2, float s) { +VertexData VertexData::lerp(const VertexData& vd1, const VertexData& vd2, float s) { (void) vd1; (void) vd2; (void) s; return {}; } -VertexData VertexData::bilerp(VertexData& vd1, VertexData& vd2, VertexData& vd3, float s, float t) { +VertexData VertexData::bilerp(const VertexData& vd1, const VertexData& vd2, const VertexData& vd3, float s, float t) { (void) vd1; (void) vd2; (void) vd3; diff --git a/src/o3d/vertex_data.h b/src/o3d/vertex_data.h index dda881b..ec5fa25 100644 --- a/src/o3d/vertex_data.h +++ b/src/o3d/vertex_data.h @@ -5,8 +5,8 @@ namespace engine::o3d { class VertexData { public: - static VertexData lerp(VertexData& vd1, VertexData& vd2, float s); - static VertexData bilerp(VertexData& vd1, VertexData& vd2, VertexData& vd3, float s, float t); + static VertexData lerp(const VertexData& vd1, const VertexData& vd2, float s); + static VertexData bilerp(const VertexData& vd1, const VertexData& vd2, const VertexData& vd3, float s, float t); VertexData(); }; diff --git a/src/renderer.cpp b/src/renderer.cpp new file mode 100644 index 0000000..a946ce3 --- /dev/null +++ b/src/renderer.cpp @@ -0,0 +1,145 @@ +#include "renderer.h" +#include <array> +#include <cmath> +#include <algorithm> +#include <memory> +#include "math/vector.h" +#include "o3d/vertex.h" +#include "o3d/tri_vertex.h" +#include "o3d/vertex_data.h" + +using namespace engine; + +Renderer::Renderer(std::unique_ptr<engine::fb::FrameBuffer> fb) : fb{std::move(fb)} { + depth_buf.resize(this->fb->width() * this->fb->height()); +} + +void Renderer::resize(unsigned int w, unsigned int h) { + fb->resize(w, h); + depth_buf.resize(fb->width() * fb->height()); +} + +unsigned int Renderer::width() const { + return fb->width(); +} + +unsigned int Renderer::height() const { + return fb->height(); +} + +void Renderer::clear() { + std::fill(depth_buf.begin(), depth_buf.end(), 1.f); + face_ind = -1; + fb->clear(); +} + +void Renderer::_draw_cropped_triangle(engine::o3d::TriangleVertex3 triangle) { + std::array<engine::o3d::Vertex3*, 3> sorted_vs = { &triangle.vertex1, &triangle.vertex2, &triangle.vertex3 }; + +#define SWAP_IF_LT(X,Y) ({\ + if (sorted_vs[X]->point.y < sorted_vs[Y]->point.y) {\ + engine::o3d::Vertex3* temp = sorted_vs[X];\ + sorted_vs[X] = sorted_vs[Y];\ + sorted_vs[Y] = temp;\ + }\ + }) + SWAP_IF_LT(1, 0); + SWAP_IF_LT(2, 1); + SWAP_IF_LT(1, 0); +#undef SWAP_IF_LT + + engine::o3d::Vertex3 middle_vl = *sorted_vs[1]; + float fac = (sorted_vs[1]->point.y - sorted_vs[0]->point.y) / (sorted_vs[2]->point.y - sorted_vs[0]->point.y); + engine::o3d::Vertex3 middle_vr{ + { + sorted_vs[0]->point.x + fac * (sorted_vs[2]->point.x - sorted_vs[0]->point.x), + sorted_vs[1]->point.y, + sorted_vs[0]->point.z + fac * (sorted_vs[2]->point.z - sorted_vs[0]->point.z) + }, + engine::o3d::VertexData::lerp(sorted_vs[0]->data, sorted_vs[2]->data, fac) + }; + if (middle_vr.point.x < middle_vl.point.x) { + engine::o3d::Vertex3 temp = middle_vr; + middle_vr = middle_vl; + middle_vl = temp; + } + + // top triangle + { + if (sorted_vs[0]->point.y != sorted_vs[1]->point.y) { + int top_y = static_cast<int>(std::floor(sorted_vs[0]->point.y + 1.f)); + int bottom_y = static_cast<int>(std::ceil(sorted_vs[1]->point.y - 1.f)); + for (int y = top_y; y <= bottom_y; y++) { + float iy = static_cast<float>(y); + float s = (iy - sorted_vs[0]->point.y) / (sorted_vs[1]->point.y - sorted_vs[0]->point.y); + float xl = sorted_vs[0]->point.x + s * (middle_vl.point.x - sorted_vs[0]->point.x); + float xr = sorted_vs[0]->point.x + s * (middle_vr.point.x - sorted_vs[0]->point.x); + int left_x = static_cast<int>(std::ceil(xl)); + int right_x = static_cast<int>(std::ceil(xr - 1.f)); + for (int x = left_x; x <= right_x; x++) { + float ix = static_cast<float>(x); + float t = (ix - xl) / (xr - xl); + // depth and vd don't take into account perspective + float u = s, v = t; + float depth = sorted_vs[0]->point.z + u * (middle_vl.point.z - sorted_vs[0]->point.z + v * (middle_vr.point.z - middle_vl.point.z)); + if (depth < depth_buf[x + y * fb->width()]) { + depth_buf[x + y * fb->width()] = depth; + fb->draw_point(x, y, engine::math::Vector3{static_cast<float>(face_ind) + .5f, 0.f, 0.f}, + engine::o3d::VertexData::bilerp(sorted_vs[0]->data, middle_vl.data, middle_vr.data, u, v)); + } + } + } + } + } + + // bottom triangle + { + if (sorted_vs[1]->point.y != sorted_vs[2]->point.y) { + int bottom_y = static_cast<int>(std::floor(sorted_vs[2]->point.y)); + int top_y = static_cast<int>(std::ceil(sorted_vs[1]->point.y)); + for (int y = top_y; y <= bottom_y; y++) { + float iy = static_cast<float>(y); + float s = (iy - sorted_vs[2]->point.y) / (sorted_vs[1]->point.y - sorted_vs[2]->point.y); + float xl = sorted_vs[2]->point.x + s * (middle_vl.point.x - sorted_vs[2]->point.x); + float xr = sorted_vs[2]->point.x + s * (middle_vr.point.x - sorted_vs[2]->point.x); + int left_x = static_cast<int>(std::ceil(xl)); + int right_x = static_cast<int>(std::ceil(xr - 1.f)); + for (int x = left_x; x <= right_x; x++) { + float ix = static_cast<float>(x); + float t = (ix - xl) / (xr - xl); + // depth and vd don't take into account perspective + float u = s, v = t; + float depth = sorted_vs[2]->point.z + u * (middle_vl.point.z - sorted_vs[2]->point.z + v * (middle_vr.point.z - middle_vl.point.z)); + if (depth < depth_buf[x + y * fb->width()]) { + depth_buf[x + y * fb->width()] = depth; + fb->draw_point(x, y, engine::math::Vector3{static_cast<float>(face_ind) + .5f, 0.f, 0.f}, + engine::o3d::VertexData::bilerp(sorted_vs[2]->data, middle_vl.data, middle_vr.data, u, v)); + } + } + } + } + } +} + +void Renderer::draw_triangle(engine::o3d::TriangleVertex4 triangle) { + face_ind++; + for (auto t1 : triangle.crop_z_out(-1.f, 1.f)) { + for (auto t2 : t1.div_by_w().crop_xy_out(-1.f, 1.f, -1.f, 1.f)) { + engine::math::Vector2 pp1 = t2.vertex1.point.xy(), + pp2 = t2.vertex2.point.xy(), + pp3 = t2.vertex3.point.xy(); + if ((pp2 - pp1).det(pp3 - pp1) >= 0.f) continue; + t2.vertex1.point = (t2.vertex1.point + engine::math::Vector3{1.f, 1.f, 0.f}) / 2.f; + t2.vertex2.point = (t2.vertex2.point + engine::math::Vector3{1.f, 1.f, 0.f}) / 2.f; + t2.vertex3.point = (t2.vertex3.point + engine::math::Vector3{1.f, 1.f, 0.f}) / 2.f; + float fw = static_cast<float>(fb->width()), fh = static_cast<float>(fb->height()); + t2.vertex1.point.x = t2.vertex1.point.x * fw - .5f; + t2.vertex1.point.y = t2.vertex1.point.y * fh - .5f; + t2.vertex2.point.x = t2.vertex2.point.x * fw - .5f; + t2.vertex2.point.y = t2.vertex2.point.y * fh - .5f; + t2.vertex3.point.x = t2.vertex3.point.x * fw - .5f; + t2.vertex3.point.y = t2.vertex3.point.y * fh - .5f; + _draw_cropped_triangle(t2); + } + } +} diff --git a/src/renderer.h b/src/renderer.h new file mode 100644 index 0000000..211a6fc --- /dev/null +++ b/src/renderer.h @@ -0,0 +1,30 @@ +#ifndef RENDERER_H +#define RENDERER_H + +#include <memory> +#include "fb/fb.h" +#include "o3d/tri_vertex.h" + +namespace engine { + +class Renderer { + public: + std::unique_ptr<engine::fb::FrameBuffer> fb; + + Renderer(std::unique_ptr<engine::fb::FrameBuffer> fb); + void resize(unsigned int w, unsigned int h); + unsigned int width() const; + unsigned int height() const; + void clear(); + void draw_triangle(engine::o3d::TriangleVertex4 triangle); + + private: + std::vector<float> depth_buf; + int face_ind; + + void _draw_cropped_triangle(engine::o3d::TriangleVertex3 triangle); +}; + +} + +#endif // RENDERER_H |