From 9f6493343cdfe18c7a0e4cd1b4a8e4b03242ad17 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Fri, 16 Oct 2020 11:46:55 -0500 Subject: [PATCH] pink circle! --- Makefile | 3 + include/physics.h | 13 +++ include/types.h | 19 ++++ include/utils.h | 16 ++++ include/vecmath.h | 18 ++++ project.4coder | 3 + res/shaders/planet_frag.glsl | 9 ++ res/shaders/planet_vert.glsl | 11 +++ src/physics.c | 2 + src/sim.c | 180 +++++++++++++++++++++++++++-------- src/utils.c | 19 ++++ src/vecmath.c | 36 +++++++ 12 files changed, 291 insertions(+), 38 deletions(-) create mode 100644 include/physics.h create mode 100644 include/types.h create mode 100644 include/utils.h create mode 100644 include/vecmath.h create mode 100644 res/shaders/planet_frag.glsl create mode 100644 res/shaders/planet_vert.glsl create mode 100644 src/physics.c create mode 100644 src/utils.c create mode 100644 src/vecmath.c diff --git a/Makefile b/Makefile index 56b386b..a0c02c1 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,9 @@ RELEASE=0 OBJ_FILES=\ + build/utils.o \ + build/vecmath.o \ + build/physics.o \ build/sim.o ifeq (, $(shell which tcc)) diff --git a/include/physics.h b/include/physics.h new file mode 100644 index 0000000..f79b9ba --- /dev/null +++ b/include/physics.h @@ -0,0 +1,13 @@ +#ifndef PHYSICS_H +#define PHYSICS_H + +#include + +typedef struct Body { + V2f pos; + V2f vel; + + f32 mass; +} Body; + +#endif //PHYSICS_H diff --git a/include/types.h b/include/types.h new file mode 100644 index 0000000..a471fbd --- /dev/null +++ b/include/types.h @@ -0,0 +1,19 @@ +#ifndef TYPES_H +#define TYPES_H + +#include + +// NOTE(brendan): Standard remaps for common types. +// This ensures that the exact size of the type is known. +typedef int8_t i8; +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef float f32; +typedef double f64; + +#endif //TYPES_H diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 0000000..1c0e0dc --- /dev/null +++ b/include/utils.h @@ -0,0 +1,16 @@ +#ifndef UTILS_H +#define UTILS_H + +#include // NOTE(Brendan): Only for intptr_t + +// NOTE(Brendan): Hacky way to get the offset of a struct member. +// offsetof() is the standard way in C to get it, but it is not guarenteed +// to be defined in all C implementations. +#define offset_of(S, mem) (intptr_t) &(((S*)(0))->mem) + +// NOTE(Brendan): This is useful in many situations and I believe it cleans up the code by making simple, counter based for loops easier to recognize at a glance. +#define foreach(var, lo, hi) for (i32 var = lo; var < hi; var++) + +void _Noreturn panic_and_die(const char* msg, ...); + +#endif //UTILS_H \ No newline at end of file diff --git a/include/vecmath.h b/include/vecmath.h new file mode 100644 index 0000000..8853176 --- /dev/null +++ b/include/vecmath.h @@ -0,0 +1,18 @@ +#ifndef VECMATH_H +#define VECMATH_H + +#include "types.h" + +typedef struct V2f { f32 x, y; } V2f; + +V2f v2f_add (V2f a, V2f b); +V2f v2f_sub (V2f a, V2f b); +V2f v2f_mul (V2f a, f32 scalar); +f32 v2f_dot (V2f a, V2f b); +f32 v2f_smag(V2f a); +f32 v2f_mag (V2f a); +V2f v2f_norm(V2f a); +V2f v2f_proj(V2f a, V2f onto); + + +#endif //VECMATH_H diff --git a/project.4coder b/project.4coder index f694913..fa79a8e 100644 --- a/project.4coder +++ b/project.4coder @@ -5,7 +5,10 @@ project_name = "CSC718_Project"; patterns = { "*.c", "*.h", +"*.sh", +"*.glsl", "*.4coder", +"Makefile", }; blacklist_patterns = { diff --git a/res/shaders/planet_frag.glsl b/res/shaders/planet_frag.glsl new file mode 100644 index 0000000..863ee86 --- /dev/null +++ b/res/shaders/planet_frag.glsl @@ -0,0 +1,9 @@ +#version 300 es + +precision mediump float; + +out vec4 fragColor; + +void main() { + fragColor = vec4(1.0, 0.0, 1.0, 1.0); +} diff --git a/res/shaders/planet_vert.glsl b/res/shaders/planet_vert.glsl new file mode 100644 index 0000000..594d966 --- /dev/null +++ b/res/shaders/planet_vert.glsl @@ -0,0 +1,11 @@ +#version 300 es + +precision mediump float; + +layout(location = 0) in vec2 a_shape_pos; +layout(location = 1) in vec2 a_obj_pos; +layout(location = 2) in float a_mass; + +void main() { + gl_Position = vec4(a_shape_pos, 0, 1); +} \ No newline at end of file diff --git a/src/physics.c b/src/physics.c new file mode 100644 index 0000000..5f4f488 --- /dev/null +++ b/src/physics.c @@ -0,0 +1,2 @@ +#include "physics.h" + diff --git a/src/sim.c b/src/sim.c index f81b8c1..3f452b6 100644 --- a/src/sim.c +++ b/src/sim.c @@ -2,60 +2,151 @@ #include #include +#include #include -#include +#include #include +#include #include #include +#include "types.h" +#include "utils.h" +#include "physics.h" + +#define PI 3.141592653589793238462643383 + #define WINDOW_WIDTH 1600 #define WINDOW_HEIGHT 900 #define WINDOW_TITLE "N-Body Simulation" -void panic_and_die(const char* msg, ...) __attribute__((noreturn)); -void panic_and_die(const char* msg, ...) { - puts("************ PANIC ************"); - - va_list va; - va_start(va, msg); - vprintf(msg, va); - va_end(va); - -#ifdef DEBUG - // NOTE: This allows for a debugger to stop here. - __asm("int $3"); -#endif - - exit(1); -} - -void glfw_key_handler(GLFWwindow* window, int key, int scancode, int action, int mods) { +void glfw_key_handler(GLFWwindow* window, i32 key, i32 scancode, i32 action, i32 mods) { if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) glfwSetWindowShouldClose(window, 1); } -void glfw_resize_handler(GLFWwindow* window, int width, int height) { +void glfw_resize_handler(GLFWwindow* window, i32 width, i32 height) { glViewport(0, 0, width, height); } -void glfw_error_handler(int error, const char* desc) { +void glfw_error_handler(i32 error, const char* desc) { panic_and_die("GLFW Error (%d): %s\n", error, desc); } GLFWwindow* window; -void init_opengl() { +void init_glfw() { if (!glfwInit()) panic_and_die("Failed to initalize GLFW."); glfwSetErrorCallback(glfw_error_handler); window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE, NULL, NULL); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - glfwMakeContextCurrent(window); + glfwMakeContextCurrent(window); glfwSwapInterval(1); glfwSetKeyCallback(window, glfw_key_handler); glfwSetFramebufferSizeCallback(window, glfw_resize_handler); + + glEnable(GL_CULL_FACE); + glCullFace(GL_CCW); +} + +GLuint load_shader(GLenum shader_type, const char* shader_loc) { + GLuint shader = glCreateShader(shader_type); + + FILE* shader_file = fopen(shader_loc, "rb"); + if (shader_file == NULL) panic_and_die("Shader file not found: %s\n", shader_loc); + + fseek(shader_file, 0, SEEK_END); + i32 shader_file_size = ftell(shader_file); + fseek(shader_file, 0, SEEK_SET); + + char* shader_buffer = malloc(shader_file_size + 1); + fread(shader_buffer, 1, shader_file_size, shader_file); + fclose(shader_file); + + shader_buffer[shader_file_size] = 0; + + glShaderSource(shader, 1, (const char* const*) &shader_buffer, NULL); + glCompileShader(shader); + + GLint successful; + glGetShaderiv(shader, GL_COMPILE_STATUS, &successful); + if (successful != GL_TRUE) { + GLsizei log_length = 0; + GLchar shader_log[1024]; + glGetShaderInfoLog(shader, 1023, &log_length, shader_log); + shader_log[log_length] = 0; + + panic_and_die("Error compiling %s:\n%s\n", shader_loc, shader_log); + } + + free(shader_buffer); + + return shader; +} + +GLuint create_program(GLuint vertex_shader, GLuint fragment_shader) { + GLuint program = glCreateProgram(); + glAttachShader(program, vertex_shader); + glAttachShader(program, fragment_shader); + glLinkProgram(program); + + GLint successful; + glGetProgramiv(program, GL_LINK_STATUS, &successful); + if (successful != GL_TRUE) { + GLsizei log_length = 0; + GLchar program_log[1024]; + glGetProgramInfoLog(program, 1023, &log_length, program_log); + program_log[log_length] = 0; + + panic_and_die("Error linking program:\n%s", program_log); + } + + return program; +} + + + + + + + +#define CIRCLE_POINT_COUNT 36 // NOTE(Brendan): Treat a circle as a many-sided polygon. + +// NOTE(Brendan): Returns the VAO where the mesh data was bound. +GLsizei create_circle_mesh() { + GLsizei vao; + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + V2f circle_points[CIRCLE_POINT_COUNT] = {}; + foreach (i, 0, CIRCLE_POINT_COUNT) { + f32 t = (f32) i / (f32) CIRCLE_POINT_COUNT; + circle_points[i].x = cos(t * 2 * PI) / 2.0f; + circle_points[i].y = sin(t * 2 * PI) / 2.0f; + } + + GLsizei vertex_buffer; + glGenBuffers(1, &vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(circle_points), &circle_points, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(V2f), (void *) offset_of(V2f, x)); + glBindBuffer(GL_ARRAY_BUFFER, -1); + + u8 circle_indicies[CIRCLE_POINT_COUNT] = {}; + foreach(i, 0, CIRCLE_POINT_COUNT) circle_indicies[i] = i; + + GLsizei index_buffer; + glGenBuffers(1, &index_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(circle_indicies), &circle_indicies, GL_STATIC_DRAW); + + glBindVertexArray(-1); + + return vao; } void deinit_opengl() { @@ -63,39 +154,52 @@ void deinit_opengl() { glfwTerminate(); } -// NOTE: dt is expected to be in units of "per second". -void update(double dt) { +// NOTE(Brendan): dt is expected to be in units of "per second". +void update(f64 dt) { } +GLsizei circle_mesh; void draw() { glClearColor(0.1, 0.1, 0.1, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glfwSwapBuffers(window); + + glBindVertexArray(circle_mesh); + glDrawElements(GL_TRIANGLE_FAN, CIRCLE_POINT_COUNT, GL_UNSIGNED_BYTE, 0); + glBindVertexArray(-1); + + glfwSwapBuffers(window); } void loop() { - double last_time = glfwGetTime(); - double curr_time = last_time; - double delta; + f64 last_time = glfwGetTime(); + f64 curr_time = last_time; + f64 delta; - while (!glfwWindowShouldClose(window)) { - glfwPollEvents(); + while (!glfwWindowShouldClose(window)) { + glfwPollEvents(); - curr_time = glfwGetTime(); - delta = curr_time - last_time; - last_time = curr_time; + curr_time = glfwGetTime(); + delta = curr_time - last_time; + last_time = curr_time; if (delta > 0.0) { - update(delta); draw(); } - } + } } -int main(int argc, char* argv[]) { - init_opengl(); +i32 main(i32 argc, char* argv[]) { + init_glfw(); + circle_mesh = create_circle_mesh(); + + GLuint v_shader = load_shader(GL_VERTEX_SHADER, "res/shaders/planet_vert.glsl"); + GLuint f_shader = load_shader(GL_FRAGMENT_SHADER, "res/shaders/planet_frag.glsl"); + GLuint program = create_program(v_shader, f_shader); + + glUseProgram(program); loop(); + deinit_opengl(); return 0; diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..96faa94 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,19 @@ +#include +#include +#include + +void panic_and_die(const char* msg, ...) { + puts("************ PANIC ************"); + + va_list va; + va_start(va, msg); + vprintf(msg, va); + va_end(va); + +#ifdef DEBUG + // NOTE: This allows for a debugger to stop here. + __asm("int $3"); +#endif + + exit(1); +} \ No newline at end of file diff --git a/src/vecmath.c b/src/vecmath.c new file mode 100644 index 0000000..8f9382c --- /dev/null +++ b/src/vecmath.c @@ -0,0 +1,36 @@ +#include "vecmath.h" +#include + +V2f v2f_add(V2f a, V2f b) { + return (V2f) { .x = a.x + b.x, .y = a.y + b.y }; +} + +V2f v2f_sub(V2f a, V2f b) { + return (V2f) { .x = a.x - b.x, .y = a.y - b.y }; +} + +V2f v2f_mul(V2f a, f32 scalar) { + return (V2f) { .x = a.x * scalar, .y = a.y * scalar }; +} + +f32 v2f_dot(V2f a, V2f b) { + return a.x * b.x + a.y * b.y; +} + +f32 v2f_smag(V2f a) { + return v2f_dot(a, a); +} + +f32 v2f_mag(V2f a) { + return sqrt(v2f_smag(a)); +} + +V2f v2f_norm(V2f a) { + const f32 mag = v2f_mag(a); + return v2f_mul(a, 1.0f / mag); +} + +V2f v2f_proj(V2f a, V2f onto) { + const f32 dp = v2f_dot(a, onto) / v2f_mag(onto); + return v2f_mul(onto, dp); +} \ No newline at end of file -- 2.25.1