build/physics.o \
build/sim.o
-ifeq (, $(shell which tcc))
- CC=gcc
-else
-ifeq ($(RELEASE), 0)
- CC=gcc
-else
- CC=tcc
-endif
-endif
-
+# ifeq (, $(shell which tcc))
+# CC=gcc
+# else
+# ifeq ($(RELEASE), 0)
+# CC=gcc
+# else
+# CC=tcc
+# endif
+# endif
+
+CC=g++
INCLUDES=-I./include
LIBS=-lGL -lglfw -lm
TARGET=./sim
ifeq ($(RELEASE), 1)
- FLAGS=-O3
+ FLAGS=-O2
else
FLAGS=-g3
endif
+build/%.o: src/%.cpp
+ $(CC) $(TIMEFLAG) $(FLAGS) -c $< -o $@ $(INCLUDES)
+
build/%.o: src/%.c
$(CC) $(TIMEFLAG) $(FLAGS) -c $< -o $@ $(INCLUDES)
#ifndef LOG_H
#define LOG_H
-typedef enum LogLevel {
+enum LogLevel
+{
LOG_LEVEL_INFO,
LOG_LEVEL_WARNING,
LOG_LEVEL_ERROR,
LOG_LEVEL_COUNT,
-} LogLevel;
+};
+// NOTE(Brendan): The minimum log level that will be printed out.
+// Useful for hiding a bunch of INFO logs.
extern LogLevel LOG_LEVEL_MINIMUM;
void logprint(LogLevel level, const char* format, ...);
#include <vecmath.h>
-typedef struct Body {
+struct Body
+{
V2f pos;
V2f vel;
f32 mass;
-} Body;
+};
#endif //PHYSICS_H
// 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, ...);
+[[noreturn]] void panic_and_die(const char* msg, ...);
+
+#ifndef defer
+struct defer_dummy {};
+
+template <class F>
+struct deferrer
+{
+ F f;
+ ~deferrer() { f(); }
+};
+
+template <class F>
+deferrer<F>
+operator*(defer_dummy, F f)
+{
+ return { f };
+}
+
+#define DEFER_(LINE) zz_defer##LINE
+#define DEFER(LINE_) DEFER_(LINE)
+#define defer auto DEFER(__LINE__) = defer_dummy{} * [&]()
+
+#endif // defer
#endif //UTILS_H
\ No newline at end of file
#include "types.h"
-typedef struct V2f { f32 x, y; } V2f;
+struct V2f
+{
+ f32 x, y;
+};
V2f v2f_add (V2f a, V2f b);
V2f v2f_sub (V2f a, V2f b);
V2f v2f_norm(V2f a);
V2f v2f_proj(V2f a, V2f onto);
+
+
typedef float mat4[16];
void mat4_identity(mat4 *mat);
patterns = {
"*.c",
+"*.cpp",
"*.h",
"*.sh",
"*.glsl",
+++ /dev/null
-#include <stdio.h>
-#include <stdarg.h>
-
-#include "log.h"
-#include "utils.h"
-
-LogLevel LOG_LEVEL_MINIMUM = LOG_LEVEL_INFO;
-
-internal const char* log_level_strs[LOG_LEVEL_COUNT] = {
- "INFO",
- "WARN",
- "ERROR",
-};
-
-void
-logprint(LogLevel level, const char *format, ...)
-{
- va_list va;
- va_start(va, format);
- logvprint(level, format, va);
- va_end(va);
-}
-
-// NOTE(Brendan): This always prints a newline.
-void
-logvprint(LogLevel level, const char* format, va_list va)
-{
- if (level < LOG_LEVEL_MINIMUM) return;
-
- printf("[%s] ", log_level_strs[level]);
- vprintf(format, va);
- printf("\n");
-}
\ No newline at end of file
--- /dev/null
+#include <cstdio>
+#include <cstdarg>
+
+#include "log.h"
+#include "utils.h"
+
+LogLevel LOG_LEVEL_MINIMUM = LOG_LEVEL_INFO;
+
+internal const char* log_level_strs[LOG_LEVEL_COUNT] = {
+ "INFO",
+ "WARN",
+ "ERROR",
+};
+
+void
+logprint(LogLevel level, const char *format, ...)
+{
+ va_list va;
+ va_start(va, format);
+ logvprint(level, format, va);
+ va_end(va);
+}
+
+// NOTE(Brendan): This always prints a newline.
+void
+logvprint(LogLevel level, const char* format, va_list va)
+{
+ if (level < LOG_LEVEL_MINIMUM) return;
+
+ printf("[%s] ", log_level_strs[level]);
+ vprintf(format, va);
+ printf("\n");
+}
+++ /dev/null
-#include "physics.h"
-
--- /dev/null
+#include "physics.h"
+
+++ /dev/null
-#define DEBUG
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <malloc.h>
-#include <alloca.h>
-#include <unistd.h>
-#include <math.h>
-
-#include <GLES3/gl3.h>
-#include <GLFW/glfw3.h>
-
-#include "types.h"
-#include "utils.h"
-#include "physics.h"
-
-#include "log.h"
-
-#define PI 3.141592653589793238462643383
-
-#define WINDOW_WIDTH 1600
-#define WINDOW_HEIGHT 900
-#define WINDOW_TITLE "N-Body Simulation"
-
-internal 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);
-}
-
-internal void
-glfw_resize_handler(GLFWwindow* window, i32 width, i32 height)
-{
- glViewport(0, 0, width, height);
-}
-
-internal void
-glfw_error_handler(i32 error, const char* desc)
-{
- panic_and_die("GLFW Error (%d): %s\n", error, desc);
-}
-
-GLFWwindow* window;
-internal void
-init_glfw()
-{
- logprint(LOG_LEVEL_INFO, "Initializing 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);
-
- glfwSwapInterval(1);
- glfwSetKeyCallback(window, glfw_key_handler);
- glfwSetFramebufferSizeCallback(window, glfw_resize_handler);
-
- // NOTE(Brendan): This may need to be changed if the screen orientation changes.
- //glEnable(GL_CULL_FACE);
- //glFrontFace(GL_CW);
- //glCullFace(GL_BACK);
-}
-
-internal void
-deinit_glfw()
-{
- glfwDestroyWindow(window);
- glfwTerminate();
-}
-
-internal GLuint
-load_shader(GLenum shader_type, const char* shader_loc)
-{
- logprint(LOG_LEVEL_INFO, "Loading shader: %s", 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 shader %s:\n%s\n",
- shader_loc,
- shader_log);
- }
-
- free(shader_buffer);
-
- return shader;
-}
-
-internal GLuint
-create_program(GLuint vertex_shader, GLuint fragment_shader)
-{
- logprint(LOG_LEVEL_INFO, "Linking GL program");
-
- 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.
-internal GLsizei
-create_circle_mesh()
-{
- logprint(LOG_LEVEL_INFO, "Generating 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) * 40.0f + 400.0f;
- circle_points[i].y = sin(t * 2 * PI) * 40.0f + 200.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;
-}
-
-
-
-// NOTE(Brendan): dt is expected to be in units of "per second".
-internal void
-update(f64 dt)
-{
-}
-
-internal GLsizei circle_mesh;
-internal void
-draw()
-{
- glClearColor(0.1, 0.1, 0.1, 1.0);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- glBindVertexArray(circle_mesh);
- glDrawElements(GL_TRIANGLE_FAN, CIRCLE_POINT_COUNT, GL_UNSIGNED_BYTE, 0);
- glBindVertexArray(-1);
-
- glfwSwapBuffers(window);
-}
-
-internal void
-loop()
-{
- f64 last_time = glfwGetTime();
- f64 curr_time = last_time;
- f64 delta;
-
- while (!glfwWindowShouldClose(window)) {
- glfwPollEvents();
-
- curr_time = glfwGetTime();
- delta = curr_time - last_time;
- last_time = curr_time;
-
- if (delta > 0.0) {
- update(delta);
- draw();
- }
- }
-}
-
-i32
-main(i32 argc, char* argv[])
-{
- init_glfw();
- circle_mesh = create_circle_mesh(NULL);
-
- 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);
-
- mat4 ortho_mat;
- mat4_ortho(&ortho_mat, 0, 800, 0, 800, 0.0f, 100.0f);
-
- GLuint ortho_mat_loc = glGetUniformLocation(program, "u_proj");
- glUniformMatrix4fv(ortho_mat_loc, 1, false, (f32 *) ortho_mat);
-
- loop();
-
- deinit_glfw();
-
- return 0;
-}
\ No newline at end of file
--- /dev/null
+#define DEBUG
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstdbool>
+#include <malloc.h>
+#include <alloca.h>
+#include <unistd.h>
+#include <math.h>
+
+#include <GLES3/gl3.h>
+#include <GLFW/glfw3.h>
+
+#include "types.h"
+#include "utils.h"
+#include "physics.h"
+
+#include "log.h"
+
+#define PI 3.141592653589793238462643383
+
+#define WINDOW_WIDTH 1600
+#define WINDOW_HEIGHT 900
+#define WINDOW_TITLE "N-Body Simulation"
+
+internal 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);
+}
+
+internal void
+glfw_resize_handler(GLFWwindow* window, i32 width, i32 height)
+{
+ glViewport(0, 0, width, height);
+}
+
+internal void
+glfw_error_handler(i32 error, const char* desc)
+{
+ panic_and_die("GLFW Error (%d): %s\n", error, desc);
+}
+
+GLFWwindow* window;
+internal void
+init_glfw()
+{
+ logprint(LOG_LEVEL_INFO, "Initializing 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);
+
+ glfwSwapInterval(1);
+ glfwSetKeyCallback(window, glfw_key_handler);
+ glfwSetFramebufferSizeCallback(window, glfw_resize_handler);
+
+ // NOTE(Brendan): This may need to be changed if the screen orientation changes.
+ glEnable(GL_CULL_FACE);
+ glFrontFace(GL_CW);
+ glCullFace(GL_BACK);
+}
+
+internal void
+deinit_glfw()
+{
+ glfwDestroyWindow(window);
+ glfwTerminate();
+}
+
+internal GLuint
+load_shader(GLenum shader_type, const char* shader_loc)
+{
+ logprint(LOG_LEVEL_INFO, "Loading shader: %s", 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 = (char *) 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 shader %s:\n%s\n",
+ shader_loc,
+ shader_log);
+ }
+
+ free(shader_buffer);
+
+ return shader;
+}
+
+internal GLuint
+create_program(GLuint vertex_shader, GLuint fragment_shader)
+{
+ logprint(LOG_LEVEL_INFO, "Linking GL program");
+
+ 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.
+internal GLuint
+create_circle_mesh()
+{
+ logprint(LOG_LEVEL_INFO, "Generating circle mesh");
+
+ GLuint 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) * 40.0f + 400.0f;
+ circle_points[i].y = sin(t * 2 * PI) * 40.0f + 200.0f;
+ }
+
+ GLuint 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;
+
+ GLuint 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;
+}
+
+
+
+// NOTE(Brendan): dt is expected to be in units of "per second".
+internal void
+update(f64 dt)
+{
+}
+
+internal GLsizei circle_mesh;
+internal void
+draw()
+{
+ glClearColor(0.1, 0.1, 0.1, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glBindVertexArray(circle_mesh);
+ glDrawElements(GL_TRIANGLE_FAN, CIRCLE_POINT_COUNT, GL_UNSIGNED_BYTE, 0);
+ glBindVertexArray(-1);
+
+ glfwSwapBuffers(window);
+}
+
+internal void
+loop()
+{
+ f64 last_time = glfwGetTime();
+ f64 curr_time = last_time;
+ f64 delta;
+
+ while (!glfwWindowShouldClose(window)) {
+ glfwPollEvents();
+
+ curr_time = glfwGetTime();
+ delta = curr_time - last_time;
+ last_time = curr_time;
+
+ if (delta > 0.0) {
+ update(delta);
+ draw();
+ }
+ }
+}
+
+i32
+main(i32 argc, char* argv[])
+{
+ init_glfw();
+ defer { deinit_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);
+
+ mat4 ortho_mat;
+ mat4_ortho(&ortho_mat, 0, 800, 0, 800, 0.0f, 100.0f);
+
+ GLuint ortho_mat_loc = glGetUniformLocation(program, "u_proj");
+ glUniformMatrix4fv(ortho_mat_loc, 1, false, (f32 *) ortho_mat);
+
+ loop();
+
+ return 0;
+}
+++ /dev/null
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
-
-#include "log.h"
-
-void
-panic_and_die(const char* msg, ...)
-{
- puts("************ PANIC ************");
-
- va_list va;
- va_start(va, msg);
- logvprint(LOG_LEVEL_ERROR, 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
--- /dev/null
+#include <cstdio>
+#include <cstdarg>
+#include <cstdlib>
+
+#include "log.h"
+
+void
+panic_and_die(const char* msg, ...)
+{
+ puts("************ PANIC ************");
+
+ va_list va;
+ va_start(va, msg);
+ logvprint(LOG_LEVEL_ERROR, msg, va);
+ va_end(va);
+
+#ifdef DEBUG
+ // NOTE: This allows for a debugger to stop here.
+ __asm("int $3");
+#endif
+
+ exit(1);
+}
+++ /dev/null
-#include "vecmath.h"
-#include <math.h>
-
-#include "utils.h"
-
-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);
-}
-
-
-
-void
-mat4_identity(mat4 *mat)
-{
- foreach(i, 0, 16) (*mat)[i] = 0.0f;
- (*mat)[0 * 4 + 0] = 1.0f;
- (*mat)[1 * 4 + 1] = 1.0f;
- (*mat)[2 * 4 + 2] = 1.0f;
- (*mat)[3 * 4 + 3] = 1.0f;
-}
-
-void
-mat4_ortho(mat4 *mat,
- f32 left, f32 right,
- f32 top, f32 bottom,
- f32 near, f32 far)
-{
- foreach(i, 0, 16) (*mat)[i] = 0.0;
-
- (*mat)[0 * 4 + 0] = 2.0f / (right - left);
- (*mat)[1 * 4 + 1] = 2.0f / (top - bottom);
- (*mat)[2 * 4 + 2] = -2.0f / (far - near);
- (*mat)[3 * 4 + 3] = 1.0f;
-
- (*mat)[3 * 4 + 0] = -(right + left) / (right - left);
- (*mat)[3 * 4 + 1] = -(top + bottom) / (top - bottom);
- (*mat)[3 * 4 + 2] = -(far + near) / (far - near);
-}
-
-
-void
-mat4_mul(mat4 a, mat4 b, mat4* out)
-{
- foreach(row, 0, 4) {
- foreach(col, 0, 4) {
- (*out)[row * 4 + col] = 0.0f;
-
- foreach(i, 0, 4) {
- (*out)[row * 4 + col] += a[row * 4 + i] + b[i * 4 + col];
- }
- }
- }
-}
\ No newline at end of file
--- /dev/null
+#include "vecmath.h"
+#include <cmath>
+
+#include "utils.h"
+
+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);
+}
+
+
+
+void
+mat4_identity(mat4 *mat)
+{
+ foreach(i, 0, 16) (*mat)[i] = 0.0f;
+ (*mat)[0 * 4 + 0] = 1.0f;
+ (*mat)[1 * 4 + 1] = 1.0f;
+ (*mat)[2 * 4 + 2] = 1.0f;
+ (*mat)[3 * 4 + 3] = 1.0f;
+}
+
+void
+mat4_ortho(mat4 *mat,
+ f32 left, f32 right,
+ f32 top, f32 bottom,
+ f32 near, f32 far)
+{
+ foreach(i, 0, 16) (*mat)[i] = 0.0;
+
+ (*mat)[0 * 4 + 0] = 2.0f / (right - left);
+ (*mat)[1 * 4 + 1] = 2.0f / (top - bottom);
+ (*mat)[2 * 4 + 2] = -2.0f / (far - near);
+ (*mat)[3 * 4 + 3] = 1.0f;
+
+ (*mat)[3 * 4 + 0] = -(right + left) / (right - left);
+ (*mat)[3 * 4 + 1] = -(top + bottom) / (top - bottom);
+ (*mat)[3 * 4 + 2] = -(far + near) / (far - near);
+}
+
+
+void
+mat4_mul(mat4 a, mat4 b, mat4* out)
+{
+ foreach(row, 0, 4) {
+ foreach(col, 0, 4) {
+ (*out)[row * 4 + col] = 0.0f;
+
+ foreach(i, 0, 4) {
+ (*out)[row * 4 + col] += a[row * 4 + i] + b[i * 4 + col];
+ }
+ }
+ }
+}