#include "fb/chfb.h" #include #include #include #include "math/math_vector.h" #include "o3d/vertex.h" #include "o3d/tri_vertex.h" #include "o3d/vertex_data.h" CharacterFrameBuffer::CharacterFrameBuffer(unsigned int w, unsigned int h) { resize(w, h); } 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(); } unsigned int CharacterFrameBuffer::width() const { return w; } unsigned int CharacterFrameBuffer::height() const { return h; } const char* CharacterFrameBuffer::chars() const { return chars_vector.data(); } 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(TriangleVertex3 triangle) { std::array 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) {\ 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 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); 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) }, VertexData::lerp(sorted_vs[0]->data, sorted_vs[2]->data, fac) }; if (middle_vr.point.x < middle_vl.point.x) { 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(std::floor(sorted_vs[0]->point.y + 1.f)); int bottom_y = static_cast(std::ceil(sorted_vs[1]->point.y - 1.f)); for (int y = top_y; y <= bottom_y; y++) { float iy = static_cast(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(std::ceil(xl)); int right_x = static_cast(std::ceil(xr - 1.f)); for (int x = left_x; x <= right_x; x++) { float ix = static_cast(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(std::floor(sorted_vs[2]->point.y)); int top_y = static_cast(std::ceil(sorted_vs[1]->point.y)); for (int y = top_y; y <= bottom_y; y++) { float iy = static_cast(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(std::ceil(xl)); int right_x = static_cast(std::ceil(xr - 1.f)); for (int x = left_x; x <= right_x; x++) { float ix = static_cast(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(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)) { MathVector2 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 + MathVector3{1.f, 1.f, 0.f}) / 2.f; t2.vertex2.point = (t2.vertex2.point + MathVector3{1.f, 1.f, 0.f}) / 2.f; t2.vertex3.point = (t2.vertex3.point + MathVector3{1.f, 1.f, 0.f}) / 2.f; float fw = static_cast(w), fh = static_cast(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); } } } char CharacterFrameBuffer::face_char() const { int n = 1 + face_ind / 2; return (n < 10 ? '0' : 'A' - 10) + n; }