Compare commits

..

No commits in common. "d01a33e27cea95ae96cb90004769e7f48ed678a2" and "9529b89127c7fc123236b7dfe81aa8a77eb6f862" have entirely different histories.

7 changed files with 154 additions and 266 deletions

View File

@ -1,74 +1,9 @@
cmake_minimum_required(VERSION 4.0) cmake_minimum_required(VERSION 3.22)
project(sdl_project) project(sdl_project)
set(CMAKE_C_STANDARD 23)
# Include the command that downloads libraries set(CMAKE_C_STANDARD 11)
include(FetchContent)
# define a function for adding git dependencies
function(include_dependency libName gitURL gitTag)
FetchContent_Declare(${libName}
GIT_REPOSITORY ${gitURL}
GIT_TAG ${gitTag}
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(${libName})
endfunction()
# add SDL2 support
find_package(SDL2 QUIET)
if (NOT SDL2_FOUND)
message(STATUS "Getting SDL2 from Github")
include_dependency(SDL2 https://github.com/libsdl-org/SDL.git release-2.30.8)
else()
message(STATUS "Using local SDL2")
endif()
# add SDL2_image support
find_package(SDL2_image QUIET)
if (NOT SDL2_image_FOUND)
message(STATUS "Getting SDL2_image from Github")
include_dependency(SDL2_image https://github.com/libsdl-org/SDL_image.git release-2.8.2)
else()
message(STATUS "Using local SDL2_image")
endif()
## add SDL2_gfx support (NO find_package, fetch and add it manually)
#find_package(SDL2_gfx QUIET)
#if (NOT SDL2_gfx)
# message(STATUS "Getting SDL2_gfx from Github")
# include_dependency(SDL2_gfx https://github.com/giroletm/SDL2_gfx.git release-1.0.4)
#else()
# message(STATUS "Using local SDL2_gfx")
#endif()
# SDL2_gfx (DO NOT use the function for this one)
FetchContent_Declare(
SDL2_gfx
GIT_REPOSITORY https://github.com/giroletm/SDL2_gfx.git
GIT_TAG master
)
FetchContent_MakeAvailable(SDL2_gfx)
message(STATUS "sdl2_gfx_SOURCE_DIR is: ${sdl2_gfx_SOURCE_DIR}")
add_library(SDL2_gfx STATIC
${sdl2_gfx_SOURCE_DIR}/SDL2_gfxPrimitives.c
${sdl2_gfx_SOURCE_DIR}/SDL2_rotozoom.c
)
target_include_directories(SDL2_gfx PUBLIC ${sdl2_gfx_SOURCE_DIR}/sdl2_gfx-src)
target_link_libraries(SDL2_gfx PUBLIC SDL2::SDL2)
# add SDL2_ttf support
#set(SDL2TTF_VENDORED ON)
#find_package(SDL2_ttf QUIET)
#if (NOT SDL2_ttf_FOUND)
# message(STATUS "Getting SDL2_ttf from Github")
# include_dependency(SDL2_ttf https://github.com/libsdl-org/SDL_ttf.git release-2.22.0)
#else()
# message(STATUS "Using local SDL2_ttf")
#endif()
# Must set the path to the main.cpp, for example: scripts/main.cpp if it is inside a folder
add_executable(${PROJECT_NAME} scripts/main.c add_executable(${PROJECT_NAME} scripts/main.c
scripts/array.c scripts/array.c
scripts/vector.c scripts/vector.c
@ -79,36 +14,12 @@ add_executable(${PROJECT_NAME} scripts/main.c
scripts/texture.c scripts/texture.c
scripts/swap.c scripts/swap.c
scripts/upng.c scripts/upng.c
scripts/display.c scripts/display.c)
)
# Target include directories for non-gfx libs # --- SDL2 SETUP ---
target_include_directories( set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules)
${PROJECT_NAME} set(SDL2_PATH "SDL2/x86_64-w64-mingw32")
PUBLIC
${SDL2_INCLUDE_DIRS}
${SDL2_IMAGE_INCLUDE_DIRS}
# ${SDL2_TTF_INCLUDE_DIRS}
${sdl2_gfx_SOURCE_DIR} # Add SDL2_gfx headers directory
)
# link all libraries to the project find_package(SDL2 REQUIRED)
target_link_libraries( include_directories(${SDL2_INCLUDE_DIR})
${PROJECT_NAME} target_link_libraries(${PROJECT_NAME} ${SDL2_LIBRARY} ${SDL2_IMAGE_LIBRARIES})
PRIVATE
SDL2::SDL2
SDL2::SDL2main
SDL2_image::SDL2_image
# SDL2_ttf::SDL2_ttf
SDL2_gfx # Link in the SDL2_gfx library we added
)
if (WIN32)
add_custom_command(
TARGET ${PROJECT_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$<TARGET_FILE:SDL2::SDL2>" "$<TARGET_FILE_DIR:sdl_project>"
COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$<TARGET_FILE:SDL2_image::SDL2_image>" "$<TARGET_FILE_DIR:sdl_project>"
#COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$<TARGET_FILE:SDL2_ttf::SDL2_ttf>" "$<TARGET_FILE_DIR:sdl_project>"
VERBATIM
)
endif()

Binary file not shown.

View File

@ -2,10 +2,7 @@
#include "display.h" #include "display.h"
SDL_Window* window = NULL; SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL; SDL_Renderer* renderer = NULL;
uint32_t* color_buffer = NULL; uint32_t* color_buffer = NULL;
float* z_buffer = NULL;
SDL_Texture* color_buffer_texture = NULL; SDL_Texture* color_buffer_texture = NULL;
int window_width = 800; int window_width = 800;
int window_height = 600; int window_height = 600;
@ -17,19 +14,19 @@ bool initialize_window(void) {
} }
// Set width and height of the SDL window with the max screen resolution // Set width and height of the SDL window with the max screen resolution
/*SDL_DisplayMode display_mode; SDL_DisplayMode display_mode;
SDL_GetCurrentDisplayMode(0, &display_mode); SDL_GetCurrentDisplayMode(0, &display_mode);
window_width = display_mode.w; window_width = display_mode.w;
window_height = display_mode.h; window_height = display_mode.h;
*/
// Create a SDL Window // Create a SDL Window
window = SDL_CreateWindow( window = SDL_CreateWindow(
"3D Renderer", NULL,
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
window_width, window_width,
window_height, window_height,
SDL_WINDOW_SHOWN SDL_WINDOW_BORDERLESS
); );
if (!window) { if (!window) {
fprintf(stderr, "Error creating SDL window.\n"); fprintf(stderr, "Error creating SDL window.\n");
@ -107,14 +104,6 @@ void clear_color_buffer(uint32_t color) {
} }
} }
void clear_z_buffer() {
for (int y = 0; y < window_height; y++) {
for (int x = 0; x < window_width; x++) {
z_buffer[(window_width * y) + x] = 1.0F;
}
}
}
void destroy_window(void) { void destroy_window(void) {
SDL_DestroyRenderer(renderer); SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window); SDL_DestroyWindow(window);

View File

@ -25,7 +25,6 @@ enum render_method {
extern SDL_Window* window; extern SDL_Window* window;
extern SDL_Renderer* renderer; extern SDL_Renderer* renderer;
extern uint32_t* color_buffer; extern uint32_t* color_buffer;
extern float* z_buffer;
extern SDL_Texture* color_buffer_texture; extern SDL_Texture* color_buffer_texture;
extern int window_width; extern int window_width;
extern int window_height; extern int window_height;
@ -37,7 +36,6 @@ void draw_line(int x0, int y0, int x1, int y1, uint32_t color);
void draw_rect(int x, int y, int width, int height, uint32_t color); void draw_rect(int x, int y, int width, int height, uint32_t color);
void render_color_buffer(void); void render_color_buffer(void);
void clear_color_buffer(uint32_t color); void clear_color_buffer(uint32_t color);
void clear_z_buffer();
void destroy_window(void); void destroy_window(void);
#endif #endif

View File

@ -33,9 +33,8 @@ void setup(void) {
render_method = RENDER_TEXTURED_WIRE; render_method = RENDER_TEXTURED_WIRE;
cull_method = CULL_BACKFACE; cull_method = CULL_BACKFACE;
// Allocate the required memory in bytes to hold the color buffer and z buffer // Allocate the required memory in bytes to hold the color buffer
color_buffer = (uint32_t*)malloc(sizeof(uint32_t) * window_width * window_height); color_buffer = (uint32_t*)malloc(sizeof(uint32_t) * window_width * window_height);
z_buffer = (float*)malloc(sizeof(float) * window_width * window_height);
// Creating a SDL texture that is used to display the color buffer // Creating a SDL texture that is used to display the color buffer
color_buffer_texture = SDL_CreateTexture( color_buffer_texture = SDL_CreateTexture(
@ -208,6 +207,9 @@ void update(void) {
projected_points[j].y += (window_height / 2.0); projected_points[j].y += (window_height / 2.0);
} }
// Calculate the average depth for each face based on the vertices after transformation
float avg_depth = (transformed_vertices[0].z + transformed_vertices[1].z + transformed_vertices[2].z) / 3.0;
// Calculate the shade intensity based on how aliged is the normal with the flipped light direction ray // Calculate the shade intensity based on how aliged is the normal with the flipped light direction ray
float light_intensity_factor = -vec3_dot(normal, light.direction); float light_intensity_factor = -vec3_dot(normal, light.direction);
@ -226,11 +228,25 @@ void update(void) {
{ mesh_face.c_uv.u, mesh_face.c_uv.v } { mesh_face.c_uv.u, mesh_face.c_uv.v }
}, },
.color = triangle_color, .color = triangle_color,
.avg_depth = avg_depth
}; };
// Save the projected triangle in the array of triangles to render // Save the projected triangle in the array of triangles to render
array_push(triangles_to_render, projected_triangle); array_push(triangles_to_render, projected_triangle);
} }
// Sort the triangles to render by their avg_depth
int num_triangles = array_length(triangles_to_render);
for (int i = 0; i < num_triangles; i++) {
for (int j = i; j < num_triangles; j++) {
if (triangles_to_render[i].avg_depth < triangles_to_render[j].avg_depth) {
// Swap the triangles positions in the array
triangle_t temp = triangles_to_render[i];
triangles_to_render[i] = triangles_to_render[j];
triangles_to_render[j] = temp;
}
}
}
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -249,9 +265,9 @@ void render(void) {
// Draw filled triangle // Draw filled triangle
if (render_method == RENDER_FILL_TRIANGLE || render_method == RENDER_FILL_TRIANGLE_WIRE) { if (render_method == RENDER_FILL_TRIANGLE || render_method == RENDER_FILL_TRIANGLE_WIRE) {
draw_filled_triangle( draw_filled_triangle(
triangle.points[0].x, triangle.points[0].y, triangle.points[0].z, triangle.points[0].w, // vertex A triangle.points[0].x, triangle.points[0].y, // vertex A
triangle.points[1].x, triangle.points[1].y, triangle.points[1].z, triangle.points[1].w, // vertex B triangle.points[1].x, triangle.points[1].y, // vertex B
triangle.points[2].x, triangle.points[2].y, triangle.points[2].z, triangle.points[2].w, // vertex C triangle.points[2].x, triangle.points[2].y, // vertex C
triangle.color triangle.color
); );
} }
@ -289,9 +305,7 @@ void render(void) {
render_color_buffer(); render_color_buffer();
clear_color_buffer(0xFF000000); clear_color_buffer(0xFF000000);
clear_z_buffer();
SDL_RenderPresent(renderer); SDL_RenderPresent(renderer);
} }
@ -301,7 +315,6 @@ void render(void) {
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
void free_resources(void) { void free_resources(void) {
free(color_buffer); free(color_buffer);
free(z_buffer);
array_free(mesh.faces); array_free(mesh.faces);
array_free(mesh.vertices); array_free(mesh.vertices);
upng_free(png_texture); upng_free(png_texture);

View File

@ -2,6 +2,122 @@
#include "swap.h" #include "swap.h"
#include "triangle.h" #include "triangle.h"
///////////////////////////////////////////////////////////////////////////////
// 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);
// Start x_start and x_end from the top vertex (x0,y0)
float x_start = x0;
float x_end = x0;
// 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;
}
}
///////////////////////////////////////////////////////////////////////////////
// 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);
// Start x_start and x_end from the bottom vertex (x2,y2)
float x_start = x2;
float x_end = x2;
// 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;
}
}
///////////////////////////////////////////////////////////////////////////////
// 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 (y1 > y2) {
int_swap(&y1, &y2);
int_swap(&x1, &x2);
}
if (y0 > y1) {
int_swap(&y0, &y1);
int_swap(&x0, &x1);
}
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;
// 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);
}
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Draw a triangle using three raw line calls // Draw a triangle using three raw line calls
@ -92,59 +208,7 @@ void draw_texel(
int tex_y = abs((int)(interpolated_v * texture_height)) % texture_height; int tex_y = abs((int)(interpolated_v * texture_height)) % texture_height;
//tex_x = SDL_clamp(tex_x, 0, texture_width); //tex_x = SDL_clamp(tex_x, 0, texture_width);
//tex_y = SDL_clamp(tex_y, 0, texture_height); //tex_y = SDL_clamp(tex_y, 0, texture_height);
draw_pixel(x, y, texture[(texture_width * tex_y) + tex_x]);
int z_pos = (window_width * y) + x;
// Adjust 1/w so the pixels that are closer to the camera have smaller values
interpolated_reciprocal_w = 1.0 - interpolated_reciprocal_w;
// only draw pixel if depth value is less than one previously stored in z-buffer
if (interpolated_reciprocal_w < z_buffer[z_pos]) {
draw_pixel(x, y, texture[(texture_width * tex_y) + tex_x]);
// update z-buffer value with the 1/w of this current pixel
z_buffer[z_pos] = interpolated_reciprocal_w;
}
}
void draw_triangle_pixel(
int x, int y, uint32_t color,
vec4_t point_a, vec4_t point_b, vec4_t point_c
) {
vec2_t p = { x, y };
vec2_t a = vec2_from_vec4(point_a);
vec2_t b = vec2_from_vec4(point_b);
vec2_t c = vec2_from_vec4(point_c);
// Calculate the barycentric coordinates of our point 'p' inside the triangle
vec3_t weights = barycentric_weights(a, b, c, p);
float alpha = weights.x;
float beta = weights.y;
float gamma = weights.z;
// Variables to store the interpolated values of U, V, and also 1/w for the current pixel
float interpolated_reciprocal_w;
// Also interpolate the value of 1/w for the current pixel
interpolated_reciprocal_w = (1 / point_a.w) * alpha + (1 / point_b.w) * beta + (1 / point_c.w) * gamma;
// Now we can divide back both interpolated values by 1/w
//tex_x = SDL_clamp(tex_x, 0, texture_width);
//tex_y = SDL_clamp(tex_y, 0, texture_height);
int z_pos = (window_width * y) + x;
// Adjust 1/w so the pixels that are closer to the camera have smaller values
interpolated_reciprocal_w = 1.0 - interpolated_reciprocal_w;
// only draw pixel if depth value is less than one previously stored in z-buffer
if (interpolated_reciprocal_w < z_buffer[z_pos]) {
draw_pixel(x, y, color);
// update z-buffer value with the 1/w of this current pixel
z_buffer[z_pos] = interpolated_reciprocal_w;
}
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -262,86 +326,3 @@ void draw_textured_triangle(
} }
} }
} }
void draw_filled_triangle(
int x0, int y0, float z0, float w0,
int x1, int y1, float z1, float w1,
int x2, int y2, float z2, float w2,
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);
float_swap(&z0, &z1);
float_swap(&w0, &w1);
}
if (y1 > y2) {
int_swap(&y1, &y2);
int_swap(&x1, &x2);
float_swap(&z1, &z2);
float_swap(&w1, &w2);
}
if (y0 > y1) {
int_swap(&y0, &y1);
int_swap(&x0, &x1);
float_swap(&z0, &z1);
float_swap(&w0, &w1);
}
// Create vector points and texture coords after we sort the vertices
vec4_t point_a = { x0, y0, z0, w0 };
vec4_t point_b = { x1, y1, z1, w1 };
vec4_t point_c = { x2, y2, z2, w2 };
///////////////////////////////////////////////////////
// Render the upper part of the triangle (flat-bottom)
///////////////////////////////////////////////////////
float inv_slope_1 = 0;
float inv_slope_2 = 0;
if (y1 - y0 != 0) inv_slope_1 = (float)(x1 - x0) / abs(y1 - y0);
if (y2 - y0 != 0) inv_slope_2 = (float)(x2 - x0) / abs(y2 - y0);
if (y1 - y0 != 0) {
for (int y = y0; y <= y1; y++) {
int x_start = x1 + (y - y1) * inv_slope_1;
int x_end = x0 + (y - y0) * inv_slope_2;
if (x_end < x_start) {
int_swap(&x_start, &x_end); // swap if x_start is to the right of x_end
}
for (int x = x_start; x < x_end; x++) {
// Draw our pixel with the color that comes from the texture
draw_triangle_pixel(x, y, color, point_a, point_b, point_c);
}
}
}
///////////////////////////////////////////////////////
// Render the bottom part of the triangle (flat-top)
///////////////////////////////////////////////////////
inv_slope_1 = 0;
inv_slope_2 = 0;
if (y2 - y1 != 0) inv_slope_1 = (float)(x2 - x1) / abs(y2 - y1);
if (y2 - y0 != 0) inv_slope_2 = (float)(x2 - x0) / abs(y2 - y0);
if (y2 - y1 != 0) {
for (int y = y1; y <= y2; y++) {
int x_start = x1 + (y - y1) * inv_slope_1;
int x_end = x0 + (y - y0) * inv_slope_2;
if (x_end < x_start) {
int_swap(&x_start, &x_end); // swap if x_start is to the right of x_end
}
for (int x = x_start; x < x_end; x++) {
// Draw our pixel with the color that comes from the texture
draw_triangle_pixel(x, y, color, point_a, point_b, point_c);
}
}
}
}

View File

@ -23,12 +23,8 @@ typedef struct {
} triangle_t; } triangle_t;
void draw_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color); void draw_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color);
void draw_filled_triangle( void draw_filled_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color);
int x0, int y0, float z0, float w0,
int x1, int y1, float z1, float w1,
int x2, int y2, float z2, float w2,
uint32_t color
);
void draw_textured_triangle( void draw_textured_triangle(
int x0, int y0, float z0, float w0, float u0, float v0, int x0, int y0, float z0, float w0, float u0, float v0,
int x1, int y1, float z1, float w1, float u1, float v1, int x1, int y1, float z1, float w1, float u1, float v1,