#include #include #include #include #include #include #include "display.h" #include "mesh.h" #include "vector.h" #include "array.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 }; float fov_factor = 640; bool is_running = false; int cull_method = CULL_BACKFACE; int render_method = RENDER_WIRE_VERTEX; 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 ); // load cube mesh data load_file(); } 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_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) { vec2_t projected_point = { .x = round((fov_factor * point.x) / point.z), .y = round((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.01; mesh.rotation.x -= 0.01; mesh.rotation.z += 0.01; 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]; vec3_t transformed_vertices[3]; for (int j = 0; j < 3; j++) { vec3_t transformed_vertex = face_vertices[j]; transformed_vertex = vec3_rotate_x(transformed_vertex, mesh.rotation.x); transformed_vertex = vec3_rotate_y(transformed_vertex, mesh.rotation.y); transformed_vertex = vec3_rotate_z(transformed_vertex, mesh.rotation.z); // translate away from camera in z transformed_vertex.z += 5; // 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 = transformed_vertices[0]; /* A */ vec3_t vector_b = transformed_vertices[1]; /* / \ */ vec3_t vector_c = 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; } } triangle_t projected_triangle; for (int j = 0; j < 3; j++) { // project the current vertex vec2_t projected_point = project(transformed_vertices[j]); // scale and translate the projected points to the center of the screen projected_point.x += (window_width / 2); projected_point.y += (window_height / 2); projected_triangle.points[j] = projected_point; } //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; } } void render(void) { draw_grid(); //uint32_t colors[] = { 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFF757500, 0xFF007575, 0xFF750075 }; // Loop all projected points and render them for (int i = 0; i < array_length(triangles_to_render); 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, 0x000055); 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, 0x0000ff); break; case RENDER_FILL_TRIANGLE_LINE: draw_filled_triangle(triangle, 0xff00); 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; }