aboutsummaryrefslogtreecommitdiff
path: root/src/renderer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/renderer.cpp')
-rw-r--r--src/renderer.cpp145
1 files changed, 145 insertions, 0 deletions
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);
+ }
+ }
+}