#include "renderer.h" #include #include #include "math/vector.h" #include "o3d/tri.h" #include "o3d/tri_deriv.h" #include "fb/chfb.h" #include "fb/pixfb.h" using namespace engine; using engine::math::Vector2, engine::math::Vector4, engine::o3d::Triangle, engine::o3d::TriangleDerived, engine::o3d::VertexData, engine::o3d::DerivedVertex, engine::fb::CharacterFrameBuffer, engine::fb::PixelFrameBuffer; template template Renderer::Renderer(FBArg&& fb) : fb{std::forward(fb)} { depth_buf.resize(this->fb.width() * this->fb.height()); } template void Renderer::resize(unsigned int w, unsigned int h) { fb.resize(w, h); depth_buf.resize(fb.width() * fb.height()); } template void Renderer::clear() { std::fill(depth_buf.begin(), depth_buf.end(), 1.f); fb.clear(); } enum TriangleSide { top, bottom }; template void Renderer::draw_triangle(const Triangle& triangle) { Vector2 fdim_over_2 = Vector2{static_cast(fb.width()), static_cast(fb.height())} / 2.f; for (const auto& t1 : triangle.to_derived().crop_z_out(-1.f, 1.f)) { for (auto t2 : t1.div_by_w().perspective_crop_xy_out(-1.f, 1.f, -1.f, 1.f)) { auto& v1 = t2.derived_vertex1.vertex; auto& v2 = t2.derived_vertex2.vertex; auto& v3 = t2.derived_vertex3.vertex; auto pp1 = v1.xy(); auto pp2 = v2.xy(); auto pp3 = v3.xy(); if ((pp2 - pp1).det(pp3 - pp1) >= 0.f) continue; v1 = Vector4{(pp1 + 1.f).mul_term(fdim_over_2) - .5f, v1.z, v1.w}; v2 = Vector4{(pp2 + 1.f).mul_term(fdim_over_2) - .5f, v2.z, v2.w}; v3 = Vector4{(pp3 + 1.f).mul_term(fdim_over_2) - .5f, v3.z, v3.w}; _draw_cropped_triangle(triangle, t2); } } } template void Renderer::_draw_cropped_triangle(const Triangle& root, const TriangleDerived& triangle) { std::array sorted_vs = { &triangle.derived_vertex1, &triangle.derived_vertex2, &triangle.derived_vertex3 }; { const auto swap_if_gt = [&](auto x, auto y) { if (sorted_vs[x]->vertex.y > sorted_vs[y]->vertex.y) { auto temp = sorted_vs[x]; sorted_vs[x] = sorted_vs[y]; sorted_vs[y] = temp; } }; swap_if_gt(0, 1); swap_if_gt(1, 2); swap_if_gt(0, 1); } auto middle_vl = *sorted_vs[1]; const float fac = (sorted_vs[1]->vertex.y - sorted_vs[0]->vertex.y) / (sorted_vs[2]->vertex.y - sorted_vs[0]->vertex.y); const float middle_vr_vertex_w = 1.f / (1.f / sorted_vs[0]->vertex.w + fac * (1.f / sorted_vs[2]->vertex.w - 1.f / sorted_vs[0]->vertex.w)); const float fac_b0 = middle_vr_vertex_w * (1.f - fac) / sorted_vs[0]->vertex.w; DerivedVertex middle_vr{ { sorted_vs[0]->vertex.x + fac * (sorted_vs[2]->vertex.x - sorted_vs[0]->vertex.x), sorted_vs[1]->vertex.y, (fac_b0 * sorted_vs[0]->vertex.w * sorted_vs[0]->vertex.z + (1.f - fac_b0) * sorted_vs[2]->vertex.w * sorted_vs[2]->vertex.z) / middle_vr_vertex_w, middle_vr_vertex_w }, fac_b0 * sorted_vs[0]->b0 + (1.f - fac_b0) * sorted_vs[2]->b0, fac_b0 * sorted_vs[0]->b1 + (1.f - fac_b0) * sorted_vs[2]->b1 }; if (middle_vr.vertex.x < middle_vl.vertex.x) { auto temp = middle_vr; middle_vr = middle_vl; middle_vl = temp; } const auto _draw_cropped_triangle_side = [&]() { const int vertex_end_index = ([&]() { if constexpr (side == TriangleSide::top) return 0; else return 2; })(); const DerivedVertex& vertex_end = *sorted_vs[vertex_end_index]; if (vertex_end.vertex.y == sorted_vs[1]->vertex.y) return; int top_y; int bottom_y; if constexpr (side == TriangleSide::top) { top_y = static_cast(std::floor(vertex_end.vertex.y + 1.f)); bottom_y = static_cast(std::ceil(sorted_vs[1]->vertex.y - 1.f)); } else { bottom_y = static_cast(std::floor(vertex_end.vertex.y)); top_y = static_cast(std::ceil(sorted_vs[1]->vertex.y)); } for (int y = top_y; y <= bottom_y; y++) { float s = (static_cast(y) - vertex_end.vertex.y) / (sorted_vs[1]->vertex.y - vertex_end.vertex.y); float projected_relative_b0 = 1.f - s; float xl = vertex_end.vertex.x + s * (middle_vl.vertex.x - vertex_end.vertex.x); float xr = vertex_end.vertex.x + s * (middle_vr.vertex.x - vertex_end.vertex.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 t = (static_cast(x) - xl) / (xr - xl); float projected_relative_b1 = s * (1.f - t); float point_w = 1.f / ( projected_relative_b0 / vertex_end.vertex.w + projected_relative_b1 / middle_vl.vertex.w + (1.f - projected_relative_b0 - projected_relative_b1) / middle_vr.vertex.w ); float relative_b0 = point_w * projected_relative_b0 / vertex_end.vertex.w; float relative_b1 = point_w * projected_relative_b1 / middle_vl.vertex.w; float loc_z = relative_b0 * vertex_end.vertex.w * vertex_end.vertex.z + relative_b1 * middle_vl.vertex.w * middle_vl.vertex.z + (1.f - relative_b0 - relative_b1) * middle_vr.vertex.w * middle_vr.vertex.z; if (loc_z >= depth_buf[x + y * fb.width()]) continue; depth_buf[x + y * fb.width()] = loc_z; float b0 = relative_b0 * vertex_end.b0 + relative_b1 * middle_vl.b0 + (1.f - relative_b0 - relative_b1) * middle_vr.b0; float b1 = relative_b0 * vertex_end.b1 + relative_b1 * middle_vl.b1 + (1.f - relative_b0 - relative_b1) * middle_vr.b1; Vector3 loc{ b0 * root.vertex1.vertex.x + b1 * root.vertex2.vertex.x + (1.f - b0 - b1) * root.vertex3.vertex.x, b0 * root.vertex1.vertex.y + b1 * root.vertex2.vertex.y + (1.f - b0 - b1) * root.vertex3.vertex.y, loc_z }; fb.draw_point(x, y, loc, VertexData::bilerp(root.vertex1.data, root.vertex2.data, root.vertex3.data, b0, b1), Vector3::bilerp(root.vertex1.normal, root.vertex2.normal, root.vertex3.normal, b0, b1).normalize()); } } }; _draw_cropped_triangle_side.template operator()(); _draw_cropped_triangle_side.template operator()(); } template class Renderer; template Renderer::Renderer(CharacterFrameBuffer&&); template Renderer::Renderer(const CharacterFrameBuffer&); template class Renderer; template Renderer::Renderer(PixelFrameBuffer&&); template Renderer::Renderer(const PixelFrameBuffer&);