From 7a91b727082b1f125f207ff94cbf09f747e8025a Mon Sep 17 00:00:00 2001 From: jb Date: Wed, 16 Jul 2025 23:53:19 -0700 Subject: [PATCH] refactor to plain x/y --- CMakeLists.txt | 1 + scripts/main.c | 22 ++++-- scripts/swap.c | 13 ++++ scripts/swap.h | 2 + scripts/triangle.c | 165 ++++++++++++++++++++++++++++++--------------- scripts/triangle.h | 7 +- 6 files changed, 145 insertions(+), 65 deletions(-) create mode 100644 scripts/swap.c create mode 100644 scripts/swap.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a4c8d6..bca68a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ add_executable(${PROJECT_NAME} scripts/main.c scripts/triangle.c scripts/light.c scripts/texture.c + scripts/swap.c scripts/display.c) # --- SDL2 SETUP --- diff --git a/scripts/main.c b/scripts/main.c index fea3324..d1f2397 100644 --- a/scripts/main.c +++ b/scripts/main.c @@ -152,9 +152,9 @@ void update(void) { previous_frame_time = SDL_GetTicks(); - //mesh.rotation.y += 0.01f; + mesh.rotation.y += 0.01f; mesh.rotation.x -= 0.01f; - //mesh.rotation.z += 0.01f; + mesh.rotation.z += 0.01f; //mesh.translation.x += 0.004f; mesh.translation.z = 5.0f; @@ -317,17 +317,27 @@ void render(void) { draw_rect(triangle.points[2].x, triangle.points[2].y, 3, 3, 0xFFFFFF00); break; case RENDER_FILL_TRIANGLE: - draw_filled_triangle(triangle); + draw_filled_triangle( + triangle.points[0].x, triangle.points[0].y, // vertex A + triangle.points[1].x, triangle.points[1].y, // vertex B + triangle.points[2].x, triangle.points[2].y, + triangle.color + ); break; case RENDER_FILL_TRIANGLE_LINE: - draw_filled_triangle(triangle); + draw_filled_triangle( + triangle.points[0].x, triangle.points[0].y, // vertex A + triangle.points[1].x, triangle.points[1].y, // vertex B + triangle.points[2].x, triangle.points[2].y, // vertex C + triangle.color + ); draw_triangle(triangle, 0xff0000ff); break; case RENDER_TEXTURED: - draw_textured_triangle(triangle, mesh_texture); + //draw_textured_triangle(triangle, mesh_texture); break; case RENDER_TEXTURED_LINE: - draw_textured_triangle(triangle, mesh_texture); + //draw_textured_triangle(triangle, mesh_texture); draw_triangle(triangle, 0xff0000ff); default: break; diff --git a/scripts/swap.c b/scripts/swap.c new file mode 100644 index 0000000..3b628c6 --- /dev/null +++ b/scripts/swap.c @@ -0,0 +1,13 @@ +#include "swap.h" + +void int_swap(int* a, int* b) { + int tmp = *a; + *a = *b; + *b = tmp; +} + +void float_swap(float* a, float* b) { + float tmp = *a; + *a = *b; + *b = tmp; +} diff --git a/scripts/swap.h b/scripts/swap.h new file mode 100644 index 0000000..72aced2 --- /dev/null +++ b/scripts/swap.h @@ -0,0 +1,2 @@ +void int_swap(int* a, int* b); +void float_swap(float* a, float* b); \ No newline at end of file diff --git a/scripts/triangle.c b/scripts/triangle.c index 2a609d9..ce1ec68 100644 --- a/scripts/triangle.c +++ b/scripts/triangle.c @@ -1,5 +1,6 @@ #include "triangle.h" +#include #include #include "display.h" @@ -23,94 +24,145 @@ uint32_t invert_rgb(uint32_t color) { return a | rgb; } -void fill_flat_bottom_triangle(const triangle_t* triangle, color_t color) { - float inv_slope_1 = (float)(triangle->points[1].x - triangle->points[0].x) / (float)(triangle->points[1].y - triangle->points[0].y); - float inv_slope_2 = (float)(triangle->points[2].x - triangle->points[0].x) / (float)(triangle->points[2].y - triangle->points[0].y);; +/////////////////////////////////////////////////////////////////////////////// +// Draw a filled a triangle with a flat bottom +/////////////////////////////////////////////////////////////////////////////// +// +// (x0,y0) +// / \ +// / \ +// / \ +// / \ +// / \ +// (x1,y1)------(x2,y2) +// +/////////////////////////////////////////////////////////////////////////////// +void fill_flat_bottom_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color) { + // Find the two slopes (two triangle legs) + float inv_slope_1 = (float)(x1 - x0) / (y1 - y0); + float inv_slope_2 = (float)(x2 - x0) / (y2 - y0); - float x_start = (float)triangle->points[0].x, x_end = (float)triangle->points[0].x; + // Start x_start and x_end from the top vertex (x0,y0) + float x_start = x0; + float x_end = x0; - for (int x = triangle->points[0].y; x <= triangle->points[2].y; x++) { - //draw_rect((int)triangle->points[0].x, i, 1, 1, 0xFFFF0000); - draw_line((int)x_start, x, (int)x_end, x, color); + // Loop all the scanlines from top to bottom + for (int y = y0; y <= y2; y++) { + draw_line(x_start, y, x_end, y, color); x_start += inv_slope_1; x_end += inv_slope_2; } } -void fill_flat_top_triangle(const triangle_t* triangle, color_t color) { - float inv_slope_1 = (float)(triangle->points[2].x - triangle->points[0].x) / (float)(triangle->points[2].y - triangle->points[0].y); - float inv_slope_2 = (float)(triangle->points[2].x - triangle->points[1].x) / (float)(triangle->points[2].y - triangle->points[1].y);; +/////////////////////////////////////////////////////////////////////////////// +// Draw a filled a triangle with a flat top +/////////////////////////////////////////////////////////////////////////////// +// +// (x0,y0)------(x1,y1) +// \ / +// \ / +// \ / +// \ / +// \ / +// (x2,y2) +// +/////////////////////////////////////////////////////////////////////////////// +void fill_flat_top_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color) { + // Find the two slopes (two triangle legs) + float inv_slope_1 = (float)(x2 - x0) / (y2 - y0); + float inv_slope_2 = (float)(x2 - x1) / (y2 - y1); - float x_start = (float)triangle->points[2].x, x_end = (float)triangle->points[2].x; + // Start x_start and x_end from the bottom vertex (x2,y2) + float x_start = x2; + float x_end = x2; - for (int y = triangle->points[2].y; y >= triangle->points[0].y; y--) { - //draw_rect((int)triangle->points[0].x, i, 1, 1, 0xFFFF0000); - draw_line((int)x_start, y, (int)x_end, y, color); + // Loop all the scanlines from bottom to top + for (int y = y2; y >= y0; y--) { + draw_line(x_start, y, x_end, y, color); x_start -= inv_slope_1; x_end -= inv_slope_2; } } -void draw_filled_triangle(triangle_t triangle) { - // sort by y - int temp = 0, min_idx = 0, mid_idx = 1, max_idx = 2; - - if (triangle.points[min_idx].y > triangle.points[mid_idx].y) { - temp = min_idx; min_idx = mid_idx; mid_idx = temp; +/////////////////////////////////////////////////////////////////////////////// +// Draw a filled triangle with the flat-top/flat-bottom method +// We split the original triangle in two, half flat-bottom and half flat-top +/////////////////////////////////////////////////////////////////////////////// +// +// (x0,y0) +// / \ +// / \ +// / \ +// / \ +// / \ +// (x1,y1)------(Mx,My) +// \_ \ +// \_ \ +// \_ \ +// \_ \ +// \ \ +// \_ \ +// \_\ +// \ +// (x2,y2) +// +/////////////////////////////////////////////////////////////////////////////// +void draw_filled_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color) { + // We need to sort the vertices by y-coordinate ascending (y0 < y1 < y2) + if (y0 > y1) { + int_swap(&y0, &y1); + int_swap(&x0, &x1); } - if (triangle.points[min_idx].y > triangle.points[max_idx].y) { - temp = min_idx; min_idx = max_idx; max_idx = temp; + if (y1 > y2) { + int_swap(&y1, &y2); + int_swap(&x1, &x2); } - if (triangle.points[mid_idx].y > triangle.points[max_idx].y) { - temp = mid_idx; mid_idx = max_idx; max_idx = temp; + if (y0 > y1) { + int_swap(&y0, &y1); + int_swap(&x0, &x1); } - color_t color = triangle.color ? triangle.color : 0xFF00FF00; // Default to 0xFF00FF00 if not setr; // Default color if not set + if (y1 == y2) { + // Draw flat-bottom triangle + fill_flat_bottom_triangle(x0, y0, x1, y1, x2, y2, color); + } else if (y0 == y1) { + // Draw flat-top triangle + fill_flat_top_triangle(x0, y0, x1, y1, x2, y2, color); + } else { + // Calculate the new vertex (Mx,My) using triangle similarity + int My = y1; + int Mx = (((x2 - x0) * (y1 - y0)) / (y2 - y0)) + x0; - if (triangle.points[mid_idx].y == triangle.points[max_idx].y) { - fill_flat_bottom_triangle(&triangle, color); - } else if (triangle.points[min_idx].y == triangle.points[mid_idx].y) { - fill_flat_top_triangle(&triangle, color); + // Draw flat-bottom triangle + fill_flat_bottom_triangle(x0, y0, x1, y1, Mx, My, color); + + // Draw flat-top triangle + fill_flat_top_triangle(x1, y1, Mx, My, x2, y2, color); } - - - int_vec2_t midpoint; - midpoint.y = triangle.points[mid_idx].y; - midpoint.x = (((triangle.points[max_idx].x - triangle.points[min_idx].x) * (triangle.points[mid_idx].y - triangle.points[min_idx].y)) / (triangle.points[max_idx].y - triangle.points[min_idx].y)) + triangle.points[min_idx].x; - - triangle_t flat_top, flat_bottom; - - flat_bottom.points[0] = triangle.points[min_idx]; - flat_bottom.points[1] = triangle.points[mid_idx]; - flat_bottom.points[2] = midpoint; - - flat_top.points[0] = triangle.points[mid_idx]; - flat_top.points[1] = midpoint; - flat_top.points[2] = triangle.points[max_idx]; - - fill_flat_bottom_triangle(&flat_bottom, color); - fill_flat_top_triangle(&flat_top, (color)); - } -void draw_textured_triangle(triangle_t triangle, uint32_t* texture) { +void draw_textured_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t* texture) { // sort by y int temp = 0, min_idx = 0, mid_idx = 1, max_idx = 2; - if (triangle.points[min_idx].y > triangle.points[mid_idx].y) { - temp = min_idx; min_idx = mid_idx; mid_idx = temp; + // We need to sort the vertices by y-coordinate ascending (y0 < y1 < y2) + if (y0 > y1) { + int_swap(&y0, &y1); + int_swap(&x0, &x1); } - if (triangle.points[min_idx].y > triangle.points[max_idx].y) { - temp = min_idx; min_idx = max_idx; max_idx = temp; + if (y1 > y2) { + int_swap(&y1, &y2); + int_swap(&x1, &x2); } - if (triangle.points[mid_idx].y > triangle.points[max_idx].y) { - temp = mid_idx; mid_idx = max_idx; max_idx = temp; + if (y0 > y1) { + int_swap(&y0, &y1); + int_swap(&x0, &x1); } float inv_slope_1 = 0; float inv_slope_2 = 0; - if (triangle.points[mid_idx].y - triangle.points[min_idx].y != 0) { + /*if (triangle.points[mid_idx].y - triangle.points[min_idx].y != 0) { inv_slope_1 = (float)(triangle.points[mid_idx].x - triangle.points[min_idx].x) / fabsf(triangle.points[mid_idx].y - triangle.points[min_idx].y); } @@ -121,6 +173,7 @@ void draw_textured_triangle(triangle_t triangle, uint32_t* texture) { for (int y = triangle.points[min_idx].y; y <= triangle.points[mid_idx].y; y++) { //float x_start = triangle } + */ } \ No newline at end of file diff --git a/scripts/triangle.h b/scripts/triangle.h index 0d220e7..31371de 100644 --- a/scripts/triangle.h +++ b/scripts/triangle.h @@ -4,6 +4,7 @@ #include #include "texture.h" +#include "swap.h" #include "vector.h" #include "texture.h" @@ -25,14 +26,14 @@ typedef struct { } int_vec2_t; typedef struct { - int_vec2_t points[3]; + vec2_t points[3]; tex2_t texcoords[3]; color_t color; float average_depth; } triangle_t; -void draw_filled_triangle(triangle_t triangle); -void draw_textured_triangle(triangle_t triangle, uint32_t* texture); +void draw_filled_triangle(int x0, int y0, int x1, int y1, int x2, int y2, color_t color); +void draw_textured_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t* texture); #endif