2025-07-08 23:48:29 -07:00

228 lines
6.6 KiB
C

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <SDL.h>
#include "display.h"
#include "mesh.h"
#include "vector.h"
#include "array.h"
#define STEP 0.075
triangle_t* triangles_to_render = NULL; //[N_MESH_FACES];
vec3_t camera_position = { 0, 0, 0 };
float fov_factor = 640;
bool is_running = false;
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;
}
}
////////////////////////////////////////////////////////////////////////////////
// Function that receives a 3D vector and returns a projected 2D point
////////////////////////////////////////////////////////////////////////////////
vec2_t project(vec3_t point) {
vec2_t projected_point = {
.x = (fov_factor * point.x) / point.z,
.y = (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;
}
// 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;
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);
normal_triangle.points[j] = projected_point;
}
array_push(triangles_to_render, normal_triangle);
}
}
void render(void) {
draw_grid();
// 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];
draw_filled_triangle(triangle, 0xFFFFFFFF);
draw_triangle(triangle, 0xFF000000);
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);
}
//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;
}