#include #include #include #include #include #include "display.h" #include "mesh.h" #include "vector.h" #include "array.h" #include "matrix.h" #define STEP 0.075 uint32_t colors[12] = { 0xFF6A2EFF, // purple 0xFFE9C46A, // sand yellow 0xFF2A9D8F, // teal green 0xFFF4A261, // orange 0xFF264653, // dark blue-grey 0xFFB5838D, // mauve 0xFFD7263D, // red 0xFF38A3A5, // turquoise 0xFF757575, // grey 0xFF70C1B3, // light green-blue 0xFFFFC300, // yellow 0xFF5D2E8C // deep purple }; triangle_t* triangles_to_render = NULL; //[N_MESH_FACES]; vec3_t camera_position = { 0, 0, 0 }; mat4_t proj_matrix; bool is_running = false; int cull_method = CULL_BACKFACE; int render_method = RENDER_WIRE_VERTEX; bool sort_faces = true; uint32_t previous_frame_time = 0; void setup(void) { // Allocate the required memory in bytes to hold the color buffer color_buffer = (uint32_t*) malloc(sizeof(uint32_t) * window_width * window_height); // Creating a SDL texture that is used to display the color buffer color_buffer_texture = SDL_CreateTexture( renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, window_width, window_height ); float fov = M_PI / 3; float aspect = (float)window_height / (float)window_width; float znear = 0.0f; float zfar = 100.0f; proj_matrix = mat4_make_perspective(fov, aspect, znear, zfar); // load cube mesh data //load_file("..\\assets\\tank.obj"); load_cube_mesh_data(); } void process_input(void) { SDL_Event event; SDL_PollEvent(&event); switch (event.type) { case SDL_QUIT: is_running = false; break; case SDL_KEYDOWN: if (event.key.keysym.sym == SDLK_ESCAPE) is_running = false; //break; if (event.key.keysym.sym == SDLK_UP) mesh.rotation.z += STEP; if (event.key.keysym.sym == SDLK_LEFT) mesh.rotation.x += STEP; if (event.key.keysym.sym == SDLK_RIGHT) mesh.rotation.y += STEP; break; case SDL_KEYUP: SDL_Keycode key = event.key.keysym.sym; if (key == SDLK_c) { cull_method = !cull_method; } if (key == SDLK_s) { sort_faces = !sort_faces; } if (key == SDLK_1) { render_method = RENDER_WIRE_VERTEX; } if (key == SDLK_2) { render_method = RENDER_WIRE; } if (key == SDLK_3) { render_method = RENDER_FILL_TRIANGLE; } if (key == SDLK_4) { render_method = RENDER_FILL_TRIANGLE_LINE; } break; } } //////////////////////////////////////////////////////////////////////////////// // Function that receives a 3D vector and returns a projected 2D point //////////////////////////////////////////////////////////////////////////////// vec2_t project(vec3_t point) { float fov_factor = 1; // placeholder vec2_t projected_point = { .x = roundf((fov_factor * point.x) / point.z), .y = roundf((fov_factor * point.y) / point.z), }; return projected_point; } void update(void) { //while (!SDL_TICKS_PASSED(SDL_GetTicks(), previous_frame_time + FRAME_TARGET_TIME)); int time_to_wait = FRAME_TARGET_TIME - (SDL_GetTicks() - previous_frame_time); // only delay execution if we are running too fast if (time_to_wait > 0 && time_to_wait <= FRAME_TARGET_TIME) { SDL_Delay(time_to_wait); } previous_frame_time = SDL_GetTicks(); mesh.rotation.y += 0.01f; mesh.rotation.x -= 0.01f; mesh.rotation.z += 0.01f; mesh.translation.x += 0.004f; mesh.translation.z = 5.0f; mesh.scale.x += 0.002f; //mesh.scale.y += 0.002f; // create scale matrix that will be used mat4_t scale_matrix = mat4_make_scale(mesh.scale.x, mesh.scale.y, mesh.scale.z); // create translation matrix mat4_t translation_matrix = mat4_make_translation(mesh.translation.x, mesh.translation.y, mesh.translation.z); // create rotation matrices mat4_t rotation_matrix_x = mat4_make_rotation_x(mesh.rotation.x); mat4_t rotation_matrix_y = mat4_make_rotation_y(mesh.rotation.y); mat4_t rotation_matrix_z = mat4_make_rotation_z(mesh.rotation.z); // create world matrix mat4_t world_matrix = mat4_identity(); world_matrix = mat4_mul_mat4(&scale_matrix, &world_matrix); world_matrix = mat4_mul_mat4(&rotation_matrix_x, &world_matrix); world_matrix = mat4_mul_mat4(&rotation_matrix_y, &world_matrix); world_matrix = mat4_mul_mat4(&rotation_matrix_z, &world_matrix); world_matrix = mat4_mul_mat4(&translation_matrix, &world_matrix); triangles_to_render = NULL; int num_faces = array_length(mesh.faces); for (int i = 0; i < num_faces; i++) { face_t mesh_face = mesh.faces[i]; vec3_t face_vertices[3]; face_vertices[0] = mesh.vertices[mesh_face.a - 1]; face_vertices[1] = mesh.vertices[mesh_face.b - 1]; face_vertices[2] = mesh.vertices[mesh_face.c - 1]; vec4_t transformed_vertices[3]; for (int j = 0; j < 3; j++) { vec4_t transformed_vertex = vec4_from_vec3(face_vertices[j]); // use matrix to scale original matrix transformed_vertex = mat4_multiply_vec4(world_matrix, transformed_vertex); // save translated vertex in the array of transformed vertices transformed_vertices[j] = transformed_vertex; } if (cull_method == CULL_BACKFACE) { // check backface culling vec3_t vector_a = vec3_from_vec4(transformed_vertices[0]); /* A */ vec3_t vector_b = vec3_from_vec4(transformed_vertices[1]); /* / \ */ vec3_t vector_c = vec3_from_vec4(transformed_vertices[2]); /* C---B */ // get the vector subtraction of B-A and C-A vec3_t vector_ab = vec3_subtract(vector_b, vector_a); vec3_t vector_ac = vec3_subtract(vector_c, vector_a); vec3_normalize(&vector_ab); vec3_normalize(&vector_ac); // compute the face normal using the cross product to find perpindicular vec3_t normal = vec3_cross(vector_ab, vector_ac); vec3_normalize(&normal); // find the vector between a point in the triangle and the camera origin vec3_t camera_ray = vec3_subtract(camera_position, vector_a); // calculate how aligned the camera ray is with the face normal float dot_normal_camera = vec3_dot(normal, camera_ray); if (dot_normal_camera < 0) { continue; } } vec4_t projected_points[3]; for (int j = 0; j < 3; j++) { // project the current vertex projected_points[j] = mat4_mul_vec4_project(proj_matrix, transformed_vertices[j]); // Scale into the view projected_points[j].x *= roundf(window_width / 2.0); projected_points[j].y *= roundf(window_height / 2.0); // Translate the projected points to the middle of the screen projected_points[j].x += roundf(window_width / 2.0); projected_points[j].y += roundf(window_height / 2.0); //projected_triangle.points[j] = projected_point; } // calculate the average depth of the triangle float avg_depth = (transformed_vertices[0].z + transformed_vertices[1].z + transformed_vertices[2].z) / 3.0f; triangle_t projected_triangle = { .points = { { projected_points[0].x, projected_points[0].y }, { projected_points[1].x, projected_points[1].y }, { projected_points[2].x, projected_points[2].y }, }, .color = mesh_face.color, .average_depth = avg_depth, }; //vec2_t midpoint = triangle_midpoint(projected_triangle); //draw_rect(midpoint.x, midpoint.y, 10, 10, 0xFFFF0000); // save the projected triangle in the array of triangles array_push(triangles_to_render, projected_triangle); triangle_t normal_triangle; } } int depth_comp(const void* a, const void* b) { triangle_t arg_a = *(const triangle_t*)a; triangle_t arg_b = *(const triangle_t*)b; if (arg_a.average_depth > arg_b.average_depth) return -1; if (arg_a.average_depth < arg_b.average_depth) return 1; return 0; } void render(void) { draw_grid(); //uint32_t colors[] = { 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFF757500, 0xFF007575, 0xFF750075 }; // Loop all projected points and render them size_t arr_length = array_length(triangles_to_render); // sort triangles if (sort_faces) qsort(triangles_to_render, arr_length, sizeof(triangle_t), depth_comp); for (int i = 0; i < arr_length; i++) { triangle_t triangle = triangles_to_render[i]; switch (render_method) { case RENDER_WIRE: draw_triangle(triangle, 0x0000ff); break; case RENDER_WIRE_VERTEX: draw_triangle(triangle, 0x0000ff); draw_rect(triangle.points[0].x, triangle.points[0].y, 3, 3, 0xFFFFFF00); draw_rect(triangle.points[1].x, triangle.points[1].y, 3, 3, 0xFFFFFF00); draw_rect(triangle.points[2].x, triangle.points[2].y, 3, 3, 0xFFFFFF00); break; case RENDER_FILL_TRIANGLE: draw_filled_triangle(triangle); break; case RENDER_FILL_TRIANGLE_LINE: draw_filled_triangle(triangle); draw_triangle(triangle, 0x0000ff); break; default: break; } } //vec2_t m = triangle_midpoint(t); //draw_rect(m.x, m.y, 10, 10, 0xFFFF0000); //draw_line(10, 10, 500, 500, 0x00FF00); array_free(triangles_to_render); render_color_buffer(); clear_color_buffer(0xFF000000); SDL_RenderPresent(renderer); } void free_resources(void) { free(color_buffer); array_free(mesh.faces); array_free(mesh.vertices); } int main(int argc, char *argv[]) { is_running = initialize_window(); setup(); while (is_running) { process_input(); update(); render(); } destroy_window(); free_resources(); return 0; }