From 92aa9071fe7bc4815834afed94abed46acb38cf1 Mon Sep 17 00:00:00 2001 From: Alexander Bass Date: Tue, 23 Apr 2024 01:08:47 -0400 Subject: [PATCH] deltatime handling --- .clangd | 2 + makefile | 5 +- src/donut.c | 13 +++-- src/main.c | 132 ++++++++++++++++++++++++++++++++++---------------- src/ppm/ppm.c | 9 ++-- 5 files changed, 107 insertions(+), 54 deletions(-) create mode 100644 .clangd diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..a0bbc3b --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + Add: [-std=c17, -D_POSIX_C_SOURCE=199309L] \ No newline at end of file diff --git a/makefile b/makefile index 5b39bf1..56067aa 100644 --- a/makefile +++ b/makefile @@ -6,10 +6,11 @@ SRC_DIRS := ./src ./includes RESOURCE_DIRS := ./resources CC := gcc -ld := g++ +ld := gcc -BASEFLAGS = -g -Wall -Wextra -Wconversion -Wduplicated-branches -Wduplicated-cond -Wlogical-op -Wnull-dereference -Wdouble-promotion -Wshadow -Wformat=2 +LDFLAGS = -lm +BASEFLAGS = -std=c17 -g -O3 -D_POSIX_C_SOURCE=199309L -Wall -Wextra -Wconversion -Wduplicated-branches -Wduplicated-cond -Wlogical-op -Wnull-dereference -Wdouble-promotion -Wshadow -Wformat=2 CFLAGS = $(BASEFLAGS) -Wjump-misses-init # Find all the C and C++ files we want to compile # Note the single quotes around the * expressions. The shell will incorrectly expand these otherwise, but we want to send the * directly to the find command. diff --git a/src/donut.c b/src/donut.c index 8ba5126..a8036b6 100644 --- a/src/donut.c +++ b/src/donut.c @@ -4,17 +4,16 @@ #include "donut.h" #include "point.h" -#include "types.h" - #include "point_list.h" #include "texture.h" +#include "types.h" #include "vec3.h" - +#include vec3_t sample_rgb(texture_t tex, size_t index) { u8 red = tex.bytes[index]; u8 green = tex.bytes[index + 1]; u8 blue = tex.bytes[index + 2]; - vec3_t color = vec3((f64)red, (f64)green, (f64)blue); + vec3_t color = {(f64)red, (f64)green, (f64)blue}; color = mul_vec(color, vec3(1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0)); return color; }; @@ -27,16 +26,16 @@ vec3_t mix_transparency(vec3_t transparent, vec3_t solid) { } } void generate_cube(point_list_t *points, texture_t bottom_tex, texture_t top_tex, texture_t side_tex, i32 face_x, i32 face_y) { - const vec3_t grass_color_scale = vec3(0.4, 1.0, 0.4); + const vec3_t grass_color_scale = {0.4, 1.0, 0.4}; const size_t image_size = 16; - const size_t point_count = 32; + const size_t point_count = 20; const size_t world_width = 16; for (size_t l_x = 0; l_x < point_count; l_x++) { for (size_t l_y = 0; l_y < point_count; l_y++) { f64 sx = (f64)l_x / (f64)point_count; f64 sy = (f64)l_y / (f64)point_count; - size_t index = 3 * ((size_t)(sx * (f64)(image_size)) + (size_t)(sy * (f64)(image_size)) * image_size); + size_t index = 3 * ((size_t)(round(sx * (f64)(image_size))) + (size_t)(round(sy * (f64)(image_size)) * image_size)); vec3_t dirt = sample_rgb(bottom_tex, index); vec3_t grass = sample_rgb(top_tex, index); vec3_t side = sample_rgb(side_tex, index); diff --git a/src/main.c b/src/main.c index 6eae782..c02e4ad 100644 --- a/src/main.c +++ b/src/main.c @@ -1,6 +1,6 @@ // Alexander Bass // Created 4/16/24 -// Modified 4/21/24 +// Modified 4/23/24 #include "donut.h" #include "point.h" @@ -15,49 +15,74 @@ #include #include #include +#include #include -const i32 WIDTH = 120; -const i32 HEIGHT = 90; -const i32 DEPTH = WIDTH; +#define WIDTH 120 +#define HEIGHT 90 +#define DEPTH WIDTH -void draw(vec4_t *, const point_list_t *); -void update(point_list_t *); +#define SECONDS_TO_NANOSECONDS 1000000000 +#define FRAMERATE 30 + +#define LARGEST_PIXEL_CHAR_STRING "\033[38;2;255;255;255m██" +#define LARGEST_PIXEL_CHAR_LEN sizeof(LARGEST_PIXEL_CHAR_STRING) - 1 + +void draw(vec4_t *, const point_list_t *, char *); +void update(point_list_t *, u64); int main() { texture_t dirt = load_ppm_file("dirt.ppm"); texture_t grass = load_ppm_file("grass_top.ppm"); texture_t grass_side = load_ppm_file("grass_side.ppm"); - // point_list_t points = point_list(300); - point_list_t points = generate_donut(dirt, grass, grass_side); - // extend_point_list(&points, &donut_points); - // free_point_list(&donut_points); free_texture(&dirt); free_texture(&grass); free_texture(&grass_side); - vec4_t *pixel_buffer = malloc((size_t)(WIDTH * HEIGHT) * sizeof(vec4_t)); - for (u32 i = 0; i < (u32)(WIDTH * HEIGHT); i++) { - // pixel_buffer[i] = vec4(0.0, 0.0, 0.0, 0.0); + vec4_t *pixel_buffer = malloc(WIDTH * HEIGHT * sizeof(vec4_t)); + for (u32 i = 0; i < (WIDTH * HEIGHT); i++) { + pixel_buffer[i] = vec4(0.0, 0.0, 0.0, 0.0); }; - struct timespec tim; - tim.tv_sec = 0; - tim.tv_nsec = 1000000L * 1000L / 60L; + size_t char_buffer_capacity = (HEIGHT * WIDTH * LARGEST_PIXEL_CHAR_LEN + HEIGHT); + char *screen_char_buffer = malloc(char_buffer_capacity * sizeof(char)); + + __SYSCALL_SLONG_TYPE frame_budget = SECONDS_TO_NANOSECONDS / FRAMERATE; + struct timespec frame_start, frame_end, sleep_time; + sleep_time.tv_sec = 0; + sleep_time.tv_nsec = frame_budget; + u64 counter = 0; while (true) { + + clock_gettime(CLOCK_MONOTONIC_RAW, &frame_start); + // Move cursor to top left printf("\033[0;0H"); // Draw points - draw(pixel_buffer, &points); + draw(pixel_buffer, &points, screen_char_buffer); // Rotate points - update(&points); - // Wait - nanosleep(&tim, NULL); - // Repeat + update(&points, counter); + + // Compute time until next frame + clock_gettime(CLOCK_MONOTONIC_RAW, &frame_end); + __SYSCALL_SLONG_TYPE diff_ns = frame_end.tv_nsec - frame_start.tv_nsec; + __SYSCALL_SLONG_TYPE diff_s = frame_end.tv_sec - frame_start.tv_sec; + diff_ns += SECONDS_TO_NANOSECONDS * diff_s; + + __SYSCALL_SLONG_TYPE excess = diff_ns > frame_budget ? 0 : frame_budget - diff_ns; + // if (excess == 0) { + // printf("\033[38;2;255;255;255mCan't keep up; Exiting. Ran for %lu frames\n", counter); + // exit(0); + // } + + sleep_time.tv_nsec = excess; + counter += 1; + // Wait until next frame + nanosleep(&sleep_time, NULL); } free_point_list(&points); @@ -65,20 +90,32 @@ int main() { return 0; } -i32 w_x_to_screen(f64 w_x) { - return (i32)(round(w_x)) + WIDTH / 2; +inline size_t w_x_to_screen(f64 w_x) { + return (size_t)(round(w_x)) + WIDTH / 2; } -i32 w_y_to_screen(f64 w_y) { - return (i32)(round(w_y)) + HEIGHT / 2; +inline size_t w_y_to_screen(f64 w_y) { + return (size_t)(round(w_y)) + HEIGHT / 2; } -f64 w_z_to_screen(f64 w_z) { +inline f64 w_z_to_screen(f64 w_z) { return (w_z + (f64)(DEPTH) / 2.0) / (f64)(DEPTH); } -void draw(vec4_t *pixel_buffer, const point_list_t *points) { - for (u32 i = 0; i < (u32)(WIDTH * HEIGHT); i++) { +inline void push_string(char *to, const char *from, size_t *to_length) { + // Assumes `to` has enough room for the string in `from` + size_t i = 0; + while (from[i] != '\0') { + char c = from[i]; + to[i + *to_length] = c; + i++; + } + (*to_length) += i; +} + +void draw(vec4_t *pixel_buffer, const point_list_t *points, char *screen_char_buffer) { + // Reset Depth buffer + for (size_t i = 0; i < WIDTH * HEIGHT; i++) { pixel_buffer[i].l = 0.0; }; @@ -93,12 +130,12 @@ void draw(vec4_t *pixel_buffer, const point_list_t *points) { f64 p_y = pos.j; f64 p_z = pos.k; - i32 s_x = w_x_to_screen(p_x); - i32 s_y = w_y_to_screen(p_y); + size_t s_x = w_x_to_screen(p_x); + size_t s_y = w_y_to_screen(p_y); f64 s_z = w_z_to_screen(p_z); - if (0 <= s_x && s_x < WIDTH && 0 <= s_y && s_y < HEIGHT) { - size_t index = (size_t)(s_x + s_y * WIDTH); + if (s_x < WIDTH && s_y < HEIGHT) { + size_t index = (s_x + s_y * WIDTH); vec4_t existing = pixel_buffer[index]; if (existing.l < s_z) { pixel_buffer[index].i = color.i; @@ -109,33 +146,44 @@ void draw(vec4_t *pixel_buffer, const point_list_t *points) { }; } - for (size_t y = 0; y < (size_t)(HEIGHT); y++) { - for (size_t x = 0; x < (size_t)(WIDTH); x++) { - size_t index = x + y * (size_t)(WIDTH); + size_t char_buffer_length = 0; + + for (size_t y = 0; y < HEIGHT; y++) { + for (size_t x = 0; x < WIDTH; x++) { + size_t index = x + y * WIDTH; vec4_t pixel = pixel_buffer[index]; f64 depth = pixel.l; if (depth <= 0.0) { - printf(" "); + screen_char_buffer[char_buffer_length] = ' '; + screen_char_buffer[char_buffer_length + 1] = ' '; + char_buffer_length += 2; continue; } assert(depth >= 0.0); assert(depth <= 1.0); + char color_buf[64] = ""; + u8 red = (u8)(round(pixel.i * depth * 255.0)); u8 green = (u8)(round(pixel.j * depth * 255.0)); u8 blue = (u8)(round(pixel.k * depth * 255.0)); - printf("\033[38;2;%u;%u;%um██", red, green, blue); + + snprintf(color_buf, 32, "\033[38;2;%hhu;%hhu;%hhum██", red, green, blue); + push_string(screen_char_buffer, color_buf, &char_buffer_length); } - printf("\n"); + screen_char_buffer[char_buffer_length] = '\n'; + char_buffer_length++; } + + fwrite(screen_char_buffer, 1, char_buffer_length, stdout); } -void update(point_list_t *points) { +void update(point_list_t *points, u64 frame_count) { for (size_t i = 0; i < points->length; i++) { vec3_t *pos = &points->list[i].pos; - rotate_i(pos, 0.02); - rotate_j(pos, -0.04); - rotate_k(pos, 0.03); + // rotate_i(pos, sin(frame_count / 32.0) / 32.0); + rotate_j(pos, 0.04); + rotate_k(pos, -0.037); } } diff --git a/src/ppm/ppm.c b/src/ppm/ppm.c index 8419718..bbe15ae 100644 --- a/src/ppm/ppm.c +++ b/src/ppm/ppm.c @@ -15,8 +15,10 @@ texture_t load_ppm(FILE *file) { u32 width, height; u32 color; - fscanf(file, "%d %d", &width, &height); - fscanf(file, "%d", &color); + int res1 = fscanf(file, "%d %d", &width, &height); + assert(res1 > 0); + int res2 = fscanf(file, "%d", &color); + assert(res2 > 0); assert(width == 16 && height == 16); assert(color == 255); @@ -25,7 +27,8 @@ texture_t load_ppm(FILE *file) { for (size_t i = 0; i < width * height * 3; i++) { u8 byte; - fscanf(file, "%hhd", &byte); + int res3 = fscanf(file, "%hhd", &byte); + assert(res3 > 0); tex.bytes[i] = byte; }