diff --git a/.vscode/settings.json b/.vscode/settings.json index 5977a13..12f5689 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -21,6 +21,7 @@ "sokol_log.h": "c", "syslog.h": "c", "base.h": "c", - "stdarg.h": "c" + "stdarg.h": "c", + "rand.h": "c" } } \ No newline at end of file diff --git a/build.bat b/build.sh similarity index 100% rename from build.bat rename to build.sh diff --git a/debug.bat b/debug.bat deleted file mode 100644 index c9d62ef..0000000 --- a/debug.bat +++ /dev/null @@ -1,2 +0,0 @@ -sokol-shdc --slang=wgsl -i src/shaders/base.glsl -o src/generated/base.h --ifdef -emcc src/main.c -o app.html -sUSE_WEBGPU -sASSERTIONS -sWASM_BIGINT -sALLOW_MEMORY_GROWTH -pthread -I../sokol -I../sokol_gp -I../flecs/distr --shell-file=shell.html -sFILESYSTEM=0 -g \ No newline at end of file diff --git a/debug.sh b/debug.sh new file mode 100644 index 0000000..6dfc5ce --- /dev/null +++ b/debug.sh @@ -0,0 +1,2 @@ +xxd -i src/shaders/sprite.wgsl src/generated/sprite.h +emcc src/main.c -o app.html -sUSE_WEBGPU -sASSERTIONS -sWASM_BIGINT -sALLOW_MEMORY_GROWTH -pthread -I../sokol -I../sokol_gp -I../flecs/distr --shell-file=shell.html -sFILESYSTEM=0 -g \ No newline at end of file diff --git a/src/generated/sprite.h b/src/generated/sprite.h new file mode 100644 index 0000000..2008f98 --- /dev/null +++ b/src/generated/sprite.h @@ -0,0 +1,102 @@ +unsigned char src_shaders_sprite_wgsl[] = { + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x43, 0x65, 0x6c, 0x6c, 0x20, + 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x32, 0x66, 0x2c, 0x0d, + 0x0a, 0x7d, 0x3b, 0x0d, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, + 0x56, 0x73, 0x49, 0x20, 0x7b, 0x20, 0x2f, 0x2f, 0x56, 0x65, 0x72, 0x74, + 0x65, 0x78, 0x20, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x69, 0x6e, + 0x70, 0x75, 0x74, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x40, 0x62, 0x75, + 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x28, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x29, 0x20, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x3a, 0x20, 0x75, 0x33, 0x32, 0x2c, + 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x40, 0x6c, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x28, 0x30, 0x29, 0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x76, 0x65, 0x63, 0x32, 0x66, 0x2c, 0x0d, + 0x0a, 0x7d, 0x3b, 0x0d, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, + 0x56, 0x73, 0x32, 0x46, 0x73, 0x20, 0x7b, 0x20, 0x2f, 0x2f, 0x56, 0x65, + 0x72, 0x74, 0x65, 0x78, 0x20, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, + 0x74, 0x6f, 0x20, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, + 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x40, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x28, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x20, 0x70, 0x6f, 0x73, 0x3a, 0x20, + 0x76, 0x65, 0x63, 0x34, 0x66, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x40, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x30, 0x29, + 0x20, 0x40, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x6f, 0x6c, 0x61, 0x74, + 0x65, 0x28, 0x66, 0x6c, 0x61, 0x74, 0x29, 0x20, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x3a, 0x20, 0x75, 0x33, 0x32, 0x2c, 0x0d, 0x0a, + 0x7d, 0x3b, 0x0d, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x46, + 0x73, 0x4f, 0x20, 0x7b, 0x20, 0x2f, 0x2f, 0x46, 0x72, 0x61, 0x67, 0x6d, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x6f, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x40, + 0x62, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x28, 0x66, 0x72, 0x61, 0x67, + 0x5f, 0x64, 0x65, 0x70, 0x74, 0x68, 0x29, 0x20, 0x64, 0x65, 0x70, 0x74, + 0x68, 0x3a, 0x20, 0x66, 0x33, 0x32, 0x2c, 0x0d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x40, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x30, + 0x29, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3a, 0x20, 0x76, 0x65, 0x63, + 0x34, 0x66, 0x2c, 0x0d, 0x0a, 0x7d, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x66, + 0x6e, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x69, 0x3a, 0x20, 0x75, + 0x33, 0x32, 0x29, 0x20, 0x2d, 0x3e, 0x20, 0x76, 0x65, 0x63, 0x33, 0x66, + 0x0d, 0x0a, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, + 0x20, 0x72, 0x3a, 0x20, 0x66, 0x33, 0x32, 0x20, 0x3d, 0x20, 0x66, 0x33, + 0x32, 0x28, 0x28, 0x28, 0x69, 0x20, 0x3e, 0x3e, 0x20, 0x20, 0x30, 0x29, + 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, 0x66, 0x29, 0x29, 0x3b, 0x0d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x74, 0x20, 0x67, 0x3a, 0x20, 0x66, + 0x33, 0x32, 0x20, 0x3d, 0x20, 0x66, 0x33, 0x32, 0x28, 0x28, 0x28, 0x69, + 0x20, 0x3e, 0x3e, 0x20, 0x20, 0x38, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, + 0x66, 0x66, 0x29, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, + 0x65, 0x74, 0x20, 0x62, 0x3a, 0x20, 0x66, 0x33, 0x32, 0x20, 0x3d, 0x20, + 0x66, 0x33, 0x32, 0x28, 0x28, 0x28, 0x69, 0x20, 0x3e, 0x3e, 0x20, 0x31, + 0x36, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, 0x66, 0x29, 0x29, 0x3b, + 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x76, 0x65, 0x63, 0x33, 0x66, 0x28, 0x72, 0x20, 0x2f, 0x20, 0x32, + 0x35, 0x35, 0x2c, 0x20, 0x67, 0x20, 0x2f, 0x20, 0x32, 0x35, 0x35, 0x2c, + 0x20, 0x62, 0x20, 0x2f, 0x20, 0x32, 0x35, 0x35, 0x29, 0x3b, 0x0d, 0x0a, + 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x2f, 0x2f, 0x40, 0x62, 0x69, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x28, 0x30, 0x29, 0x20, 0x40, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x28, 0x30, 0x29, 0x20, 0x76, 0x61, 0x72, 0x3c, 0x75, 0x6e, 0x69, + 0x66, 0x6f, 0x72, 0x6d, 0x3e, 0x20, 0x6d, 0x76, 0x70, 0x3a, 0x20, 0x6d, + 0x61, 0x74, 0x34, 0x78, 0x34, 0x66, 0x3b, 0x0d, 0x0a, 0x40, 0x62, 0x69, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x28, 0x30, 0x29, 0x20, 0x40, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x28, 0x31, 0x29, 0x20, 0x76, 0x61, 0x72, 0x3c, 0x73, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x3e, 0x20, 0x63, 0x65, 0x6c, 0x6c, + 0x73, 0x3a, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x43, 0x65, 0x6c, + 0x6c, 0x3e, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x40, 0x76, 0x65, 0x72, 0x74, + 0x65, 0x78, 0x0d, 0x0a, 0x66, 0x6e, 0x20, 0x76, 0x73, 0x5f, 0x6d, 0x61, + 0x69, 0x6e, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x56, 0x73, + 0x49, 0x29, 0x20, 0x2d, 0x3e, 0x20, 0x56, 0x73, 0x32, 0x46, 0x73, 0x20, + 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6f, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x56, 0x73, 0x32, 0x46, 0x73, + 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x2e, 0x70, 0x6f, 0x73, 0x20, 0x3d, 0x20, 0x76, 0x65, + 0x63, 0x34, 0x66, 0x28, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x5b, 0x69, 0x6e, + 0x70, 0x75, 0x74, 0x2e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x5d, 0x2e, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x78, + 0x79, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x29, + 0x2f, 0x2a, 0x20, 0x2a, 0x20, 0x6d, 0x76, 0x70, 0x2a, 0x2f, 0x3b, 0x0d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2e, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x3d, 0x20, 0x69, + 0x6e, 0x70, 0x75, 0x74, 0x2e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x3b, + 0x0d, 0x0a, 0x7d, 0x0d, 0x0a, 0x0d, 0x0a, 0x40, 0x66, 0x72, 0x61, 0x67, + 0x6d, 0x65, 0x6e, 0x74, 0x0d, 0x0a, 0x66, 0x6e, 0x20, 0x66, 0x73, 0x5f, + 0x6d, 0x61, 0x69, 0x6e, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, + 0x56, 0x73, 0x32, 0x46, 0x73, 0x29, 0x20, 0x2d, 0x3e, 0x20, 0x46, 0x73, + 0x4f, 0x20, 0x7b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, + 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x46, 0x73, 0x4f, + 0x3b, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x2e, 0x64, 0x65, 0x70, 0x74, 0x68, 0x20, 0x3d, 0x20, + 0x31, 0x2e, 0x30, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, + 0x20, 0x76, 0x65, 0x63, 0x34, 0x66, 0x28, 0x31, 0x2e, 0x30, 0x2c, 0x20, + 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, + 0x30, 0x29, 0x3b, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x6f, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, + 0x3d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x66, 0x28, 0x63, 0x6f, 0x6c, 0x6f, + 0x72, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x2e, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x29, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x3b, + 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x3b, 0x0d, 0x0a, + 0x7d, 0x0d, 0x0a +}; +unsigned int src_shaders_sprite_wgsl_len = 1179; diff --git a/src/main.c b/src/main.c index f9ca7db..5b927a4 100644 --- a/src/main.c +++ b/src/main.c @@ -10,12 +10,12 @@ #include "sokol_glue.h" #include "sokol_log.h" #include "util/sokol_memtrack.h" +#include "math.h" +#include "rand.h" -#include "generated/base.h" +#include "generated/sprite.h" -#ifndef _STDIO_H - #include -#endif +#include #include #include #include @@ -37,11 +37,27 @@ void js_log(int severity, const char* format, ...) va_end(va); slog_js_log(severity, buffer); } - -typedef struct position_t +void compute_mvp(Mat4 *ptr, Vec2 pan, float zoom) { - float x, y; -} position_t; + const int width = sapp_width(), height = sapp_height(); + + ptr->Elements[0][0] = (2.0f * zoom)/width; + ptr->Elements[0][1] = 0.0f; + ptr->Elements[0][2] = 0.0f; + ptr->Elements[0][3] = 0.0f; + ptr->Elements[1][0] = 0.0f; + ptr->Elements[1][1] = (2.0f * zoom)/height; + ptr->Elements[1][2] = 0.0f; + ptr->Elements[1][3] = 0.0f; + ptr->Elements[2][0] = 0.0f; + ptr->Elements[2][1] = 0.0f; + ptr->Elements[2][2] = 1.0f; + ptr->Elements[2][3] = 0.0f; + ptr->Elements[3][0] = (-2.0f * pan.X * zoom)/width - 1.0f; + ptr->Elements[3][1] = (-2.0f * pan.Y * zoom)/height - 1.0f; + ptr->Elements[3][2] = 0.0f; + ptr->Elements[3][3] = 0.0f; +} typedef struct renderer_t { sg_pipeline pipeline; @@ -49,13 +65,14 @@ typedef struct renderer_t { sg_bindings binding; sg_range uniform; sg_pass_action pass; + sg_shader sprite_shader; } renderer_t; -typedef struct userdata_t -{ - position_t pan; +typedef struct userdata_t { + Vec2 pan; float zoom; renderer_t renderer; + Mat4 *mvp; } userdata_t; // Called on every frame of the application. @@ -70,9 +87,7 @@ static void frame(void* _userdata) sg_apply_pipeline(userdata->renderer.pipeline); sg_apply_bindings(&userdata->renderer.binding); - - sg_apply_uniforms(UB_vs_uniform, &userdata->renderer.uniform); - sg_apply_uniforms(UB_fs_uniform, &userdata->renderer.uniform); + //sg_apply_uniforms(0, &userdata->renderer.uniform); sg_draw(0, 4, SAMPLE_COUNT); @@ -83,6 +98,8 @@ static void frame(void* _userdata) // Called when the application is initializing. static void init(void* _userdata) { + rand_seed(1); + userdata_t* userdata = (userdata_t*) _userdata; // Initialize Sokol GFX. @@ -96,7 +113,7 @@ static void init(void* _userdata) exit(-1); } - const position_t quad[4] = { + const Vec2 quad[4] = { {0.0f, 1.0f}, // bottom left {1.0f, 1.0f}, // bottom right {1.0f, 0.0f}, // top right @@ -106,23 +123,54 @@ static void init(void* _userdata) 0, 1, 2, 0, 2, 3, }; - const vs_uniform_t uniform = { - .radius = 1, - }; + Mat4 mvp = M4(); + compute_mvp(&mvp, V2(0, 0), 1); - void* tmp_buffer = malloc(sizeof(position_t) * SAMPLE_COUNT); + Vec2* tmp_buffer = malloc(sizeof(Vec2) * SAMPLE_COUNT); + + for(uint32_t i = 0; i < SAMPLE_COUNT; i++) + { + tmp_buffer[i].X = next_float(); + tmp_buffer[i].Y = next_float(); + } + + sg_shader sprite_shader = sg_make_shader(&(sg_shader_desc) { + .vertex_func = { + .source = src_shaders_sprite_wgsl, + .entry = "vs_main", + }, + .fragment_func = { + .source = src_shaders_sprite_wgsl, + .entry = "fs_main", + }, + .storage_buffers[0] = { + .wgsl_group1_binding_n = 0, + .stage = SG_SHADERSTAGE_VERTEX, + .readonly = true, + }, + /*.uniform_blocks[0] = { + .size = sizeof(float) * 16, //mat4x4f + .stage = SG_SHADERSTAGE_VERTEX, + .wgsl_group0_binding_n = 0, + },*/ + .attrs[0] = { + .base_type = SG_SHADERATTRBASETYPE_FLOAT, + }, + .label = "Sprite shader", + }); userdata->renderer = (renderer_t) { .pass = (sg_pass_action) { .colors[0] = { .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f }, .load_action = SG_LOADACTION_CLEAR } }, .pipeline = sg_make_pipeline(&(sg_pipeline_desc) { - .shader = sg_make_shader(base_shader_desc(sg_query_backend())), + .shader = sprite_shader, .depth.write_enabled = true, .index_type = SG_INDEXTYPE_UINT16, .layout.attrs = { - [ATTR_base_in_quad].format = SG_VERTEXFORMAT_FLOAT2, + [0].format = SG_VERTEXFORMAT_FLOAT2, }, + .label = "Sprite pipeline", }), /*.compute = sg_make_pipeline(&(sg_pipeline_desc) { .compute = true, @@ -130,30 +178,34 @@ static void init(void* _userdata) }),*/ .binding = (sg_bindings) { .vertex_buffers = { - [ATTR_base_in_quad] = sg_make_buffer(&(sg_buffer_desc) { + [0] = sg_make_buffer(&(sg_buffer_desc) { .type = SG_BUFFERTYPE_VERTEXBUFFER, .data = SG_RANGE(quad), + .label = "Vertices" }), }, .index_buffer = sg_make_buffer(&(sg_buffer_desc) { .type = SG_BUFFERTYPE_INDEXBUFFER, .data = SG_RANGE(indices), + .label = "Indices" }), .storage_buffers = { - [SBUF_vs_ssbo] = sg_make_buffer(&(sg_buffer_desc) { + [0] = sg_make_buffer(&(sg_buffer_desc) { .type = SG_BUFFERTYPE_STORAGEBUFFER, .data = { .ptr = tmp_buffer, - .size = sizeof(position_t) * SAMPLE_COUNT, + .size = sizeof(Vec2) * SAMPLE_COUNT, }, + .label = "SSBO" }), }, }, - .uniform = SG_RANGE(uniform), + .uniform = SG_RANGE(mvp), }; - userdata->pan = (position_t) { .x = 0, .y = 0 }; + userdata->pan = (Vec2) { .X = 0, .Y = 0 }; userdata->zoom = 1; + userdata->mvp = &mvp; FREE(tmp_buffer); } @@ -192,11 +244,13 @@ static void event(const sapp_event* event, void* _userdata) const float diff = expf(event->scroll_y * 0.01f); const float width = sapp_widthf(), height = sapp_heightf(); - userdata->pan.x = userdata->pan.x - (event->mouse_x / (diff * userdata->zoom) - event->mouse_x / userdata->zoom); - userdata->pan.y = userdata->pan.y - (event->mouse_x / (diff * userdata->zoom) - event->mouse_x / userdata->zoom); + userdata->pan.X = userdata->pan.X - (event->mouse_x / (diff * userdata->zoom) - event->mouse_x / userdata->zoom); + userdata->pan.Y = userdata->pan.Y - (event->mouse_y / (diff * userdata->zoom) - event->mouse_y / userdata->zoom); userdata->zoom = _sg_clamp(userdata->zoom * diff, 0.1f, 3.0f); - js_log(3, "Zoom: %f", userdata->zoom); + compute_mvp(userdata->mvp, userdata->pan, userdata->zoom); + + js_log(3, "Zoom: %f. Pan: %f/%f", userdata->zoom, userdata->pan.X, userdata->pan.Y); break; default: diff --git a/src/math.h b/src/math.h new file mode 100644 index 0000000..a1ee542 --- /dev/null +++ b/src/math.h @@ -0,0 +1,3939 @@ +/* + HandmadeMath.h v2.0.0 + + This is a single header file with a bunch of useful types and functions for + games and graphics. Consider it a lightweight alternative to GLM that works + both C and C++. + + ============================================================================= + CONFIG + ============================================================================= + + By default, all angles in Handmade Math are specified in radians. However, it + can be configured to use degrees or turns instead. Use one of the following + defines to specify the default unit for angles: + + #define HANDMADE_MATH_USE_RADIANS + #define HANDMADE_MATH_USE_DEGREES + #define HANDMADE_MATH_USE_TURNS + + Regardless of the default angle, you can use the following functions to + specify an angle in a particular unit: + + AngleRad(radians) + AngleDeg(degrees) + AngleTurn(turns) + + The definitions of these functions change depending on the default unit. + + ----------------------------------------------------------------------------- + + Handmade Math ships with SSE (SIMD) implementations of several common + operations. To disable the use of SSE intrinsics, you must define + HANDMADE_MATH_NO_SSE before including this file: + + #define HANDMADE_MATH_NO_SSE + #include "HandmadeMath.h" + + ----------------------------------------------------------------------------- + + To use Handmade Math without the C runtime library, you must provide your own + implementations of basic math functions. Otherwise, HandmadeMath.h will use + the runtime library implementation of these functions. + + Define HANDMADE_MATH_PROVIDE_MATH_FUNCTIONS and provide your own + implementations of SINF, COSF, TANF, ACOSF, and SQRTF + before including HandmadeMath.h, like so: + + #define HANDMADE_MATH_PROVIDE_MATH_FUNCTIONS + #define SINF MySinF + #define COSF MyCosF + #define TANF MyTanF + #define ACOSF MyACosF + #define SQRTF MySqrtF + #include "HandmadeMath.h" + + By default, it is assumed that your math functions take radians. To use + different units, you must define ANGLE_USER_TO_INTERNAL and + ANGLE_INTERNAL_TO_USER. For example, if you want to use degrees in your + code but your math functions use turns: + + #define ANGLE_USER_TO_INTERNAL(a) ((a)*DegToTurn) + #define ANGLE_INTERNAL_TO_USER(a) ((a)*TurnToDeg) + + ============================================================================= + + LICENSE + + This software is in the public domain. Where that dedication is not + recognized, you are granted a perpetual, irrevocable license to copy, + distribute, and modify this file as you see fit. + + ============================================================================= + + CREDITS + + Originally written by Zakary Strange. + + Functionality: + Zakary Strange (strangezak@protonmail.com && @strangezak) + Matt Mascarenhas (@miblo_) + Aleph + FieryDrake (@fierydrake) + Gingerbill (@TheGingerBill) + Ben Visness (@bvisness) + Trinton Bullard (@Peliex_Dev) + @AntonDan + Logan Forman (@dev_dwarf) + + Fixes: + Jeroen van Rijn (@J_vanRijn) + Kiljacken (@Kiljacken) + Insofaras (@insofaras) + Daniel Gibson (@DanielGibson) +*/ + +#ifndef HANDMADE_MATH_H +#define HANDMADE_MATH_H + +// Dummy macros for when test framework is not present. +#ifndef COVERAGE +# define COVERAGE(a, b) +#endif + +#ifndef ASSERT_COVERED +# define ASSERT_COVERED(a) +#endif + +#ifdef HANDMADE_MATH_NO_SSE +# warning "HANDMADE_MATH_NO_SSE is deprecated, use HANDMADE_MATH_NO_SIMD instead" +# define HANDMADE_MATH_NO_SIMD +#endif + +/* let's figure out if SSE is really available (unless disabled anyway) + (it isn't on non-x86/x86_64 platforms or even x86 without explicit SSE support) + => only use "#ifdef HANDMADE_MATH__USE_SSE" to check for SSE support below this block! */ +#ifndef HANDMADE_MATH_NO_SIMD +# ifdef _MSC_VER /* MSVC supports SSE in amd64 mode or _M_IX86_FP >= 1 (2 means SSE2) */ +# if defined(_M_AMD64) || ( defined(_M_IX86_FP) && _M_IX86_FP >= 1 ) +# define HANDMADE_MATH__USE_SSE 1 +# endif +# else /* not MSVC, probably GCC, clang, icc or something that doesn't support SSE anyway */ +# ifdef __SSE__ /* they #define __SSE__ if it's supported */ +# define HANDMADE_MATH__USE_SSE 1 +# endif /* __SSE__ */ +# endif /* not _MSC_VER */ +# ifdef __ARM_NEON +# define HANDMADE_MATH__USE_NEON 1 +# endif /* NEON Supported */ +#endif /* #ifndef HANDMADE_MATH_NO_SIMD */ + +#if (!defined(__cplusplus) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) +# define HANDMADE_MATH__USE_C11_GENERICS 1 +#endif + +#ifdef HANDMADE_MATH__USE_SSE +# include +#endif + +#ifdef HANDMADE_MATH__USE_NEON +# include +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4201) +#endif + +#if defined(__GNUC__) || defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +# pragma GCC diagnostic ignored "-Wmissing-braces" +# ifdef __clang__ +# pragma GCC diagnostic ignored "-Wgnu-anonymous-struct" +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# endif +#endif + +#if defined(__GNUC__) || defined(__clang__) +# define DEPRECATED(msg) __attribute__((deprecated(msg))) +#elif defined(_MSC_VER) +# define DEPRECATED(msg) __declspec(deprecated(msg)) +#else +# define DEPRECATED(msg) +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if !defined(HANDMADE_MATH_USE_DEGREES) \ + && !defined(HANDMADE_MATH_USE_TURNS) \ + && !defined(HANDMADE_MATH_USE_RADIANS) +# define HANDMADE_MATH_USE_RADIANS +#endif + +#define PI 3.14159265358979323846 +#define PI32 3.14159265359f +#define DEG180 180.0 +#define DEG18032 180.0f +#define TURNHALF 0.5 +#define TURNHALF32 0.5f +#define RadToDeg ((float)(DEG180/PI)) +#define RadToTurn ((float)(TURNHALF/PI)) +#define DegToRad ((float)(PI/DEG180)) +#define DegToTurn ((float)(TURNHALF/DEG180)) +#define TurnToRad ((float)(PI/TURNHALF)) +#define TurnToDeg ((float)(DEG180/TURNHALF)) + +#if defined(HANDMADE_MATH_USE_RADIANS) +# define AngleRad(a) (a) +# define AngleDeg(a) ((a)*DegToRad) +# define AngleTurn(a) ((a)*TurnToRad) +#elif defined(HANDMADE_MATH_USE_DEGREES) +# define AngleRad(a) ((a)*RadToDeg) +# define AngleDeg(a) (a) +# define AngleTurn(a) ((a)*TurnToDeg) +#elif defined(HANDMADE_MATH_USE_TURNS) +# define AngleRad(a) ((a)*RadToTurn) +# define AngleDeg(a) ((a)*DegToTurn) +# define AngleTurn(a) (a) +#endif + +#if !defined(HANDMADE_MATH_PROVIDE_MATH_FUNCTIONS) +# include +# define SINF sinf +# define COSF cosf +# define TANF tanf +# define SQRTF sqrtf +# define ACOSF acosf +#endif + +#if !defined(ANGLE_USER_TO_INTERNAL) +# define ANGLE_USER_TO_INTERNAL(a) (ToRad(a)) +#endif + +#if !defined(ANGLE_INTERNAL_TO_USER) +# if defined(HANDMADE_MATH_USE_RADIANS) +# define ANGLE_INTERNAL_TO_USER(a) (a) +# elif defined(HANDMADE_MATH_USE_DEGREES) +# define ANGLE_INTERNAL_TO_USER(a) ((a)*RadToDeg) +# elif defined(HANDMADE_MATH_USE_TURNS) +# define ANGLE_INTERNAL_TO_USER(a) ((a)*RadToTurn) +# endif +#endif + +#define MIN(a, b) ((a) > (b) ? (b) : (a)) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#define ABS(a) ((a) > 0 ? (a) : -(a)) +#define MOD(a, m) (((a) % (m)) >= 0 ? ((a) % (m)) : (((a) % (m)) + (m))) +#define SQUARE(x) ((x) * (x)) + +typedef union Vec2 +{ + struct + { + float X, Y; + }; + + struct + { + float U, V; + }; + + struct + { + float Left, Right; + }; + + struct + { + float Width, Height; + }; + + float Elements[2]; + +#ifdef __cplusplus + inline float &operator[](int Index) { return Elements[Index]; } + inline const float& operator[](int Index) const { return Elements[Index]; } +#endif +} Vec2; + +typedef union Vec3 +{ + struct + { + float X, Y, Z; + }; + + struct + { + float U, V, W; + }; + + struct + { + float R, G, B; + }; + + struct + { + Vec2 XY; + float _Ignored0; + }; + + struct + { + float _Ignored1; + Vec2 YZ; + }; + + struct + { + Vec2 UV; + float _Ignored2; + }; + + struct + { + float _Ignored3; + Vec2 VW; + }; + + float Elements[3]; + +#ifdef __cplusplus + inline float &operator[](int Index) { return Elements[Index]; } + inline const float &operator[](int Index) const { return Elements[Index]; } +#endif +} Vec3; + +typedef union Vec4 +{ + struct + { + union + { + Vec3 XYZ; + struct + { + float X, Y, Z; + }; + }; + + float W; + }; + struct + { + union + { + Vec3 RGB; + struct + { + float R, G, B; + }; + }; + + float A; + }; + + struct + { + Vec2 XY; + float _Ignored0; + float _Ignored1; + }; + + struct + { + float _Ignored2; + Vec2 YZ; + float _Ignored3; + }; + + struct + { + float _Ignored4; + float _Ignored5; + Vec2 ZW; + }; + + float Elements[4]; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 SSE; +#endif + +#ifdef HANDMADE_MATH__USE_NEON + float32x4_t NEON; +#endif + +#ifdef __cplusplus + inline float &operator[](int Index) { return Elements[Index]; } + inline const float &operator[](int Index) const { return Elements[Index]; } +#endif +} Vec4; + +typedef union Mat2 +{ + float Elements[2][2]; + Vec2 Columns[2]; + +#ifdef __cplusplus + inline Vec2 &operator[](int Index) { return Columns[Index]; } + inline const Vec2 &operator[](int Index) const { return Columns[Index]; } +#endif +} Mat2; + +typedef union Mat3 +{ + float Elements[3][3]; + Vec3 Columns[3]; + +#ifdef __cplusplus + inline Vec3 &operator[](int Index) { return Columns[Index]; } + inline const Vec3 &operator[](int Index) const { return Columns[Index]; } +#endif +} Mat3; + +typedef union Mat4 +{ + float Elements[4][4]; + Vec4 Columns[4]; + +#ifdef __cplusplus + inline Vec4 &operator[](int Index) { return Columns[Index]; } + inline const Vec4 &operator[](int Index) const { return Columns[Index]; } +#endif +} Mat4; + +typedef union Quat +{ + struct + { + union + { + Vec3 XYZ; + struct + { + float X, Y, Z; + }; + }; + + float W; + }; + + float Elements[4]; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 SSE; +#endif +#ifdef HANDMADE_MATH__USE_NEON + float32x4_t NEON; +#endif +} Quat; + +typedef signed int Bool; + +/* + * Angle unit conversion functions + */ +static inline float ToRad(float Angle) +{ +#if defined(HANDMADE_MATH_USE_RADIANS) + float Result = Angle; +#elif defined(HANDMADE_MATH_USE_DEGREES) + float Result = Angle * DegToRad; +#elif defined(HANDMADE_MATH_USE_TURNS) + float Result = Angle * TurnToRad; +#endif + + return Result; +} + +static inline float ToDeg(float Angle) +{ +#if defined(HANDMADE_MATH_USE_RADIANS) + float Result = Angle * RadToDeg; +#elif defined(HANDMADE_MATH_USE_DEGREES) + float Result = Angle; +#elif defined(HANDMADE_MATH_USE_TURNS) + float Result = Angle * TurnToDeg; +#endif + + return Result; +} + +static inline float ToTurn(float Angle) +{ +#if defined(HANDMADE_MATH_USE_RADIANS) + float Result = Angle * RadToTurn; +#elif defined(HANDMADE_MATH_USE_DEGREES) + float Result = Angle * DegToTurn; +#elif defined(HANDMADE_MATH_USE_TURNS) + float Result = Angle; +#endif + + return Result; +} + +/* + * Floating-point math functions + */ + +COVERAGE(SinF, 1) +static inline float SinF(float Angle) +{ + ASSERT_COVERED(SinF); + return SINF(ANGLE_USER_TO_INTERNAL(Angle)); +} + +COVERAGE(CosF, 1) +static inline float CosF(float Angle) +{ + ASSERT_COVERED(CosF); + return COSF(ANGLE_USER_TO_INTERNAL(Angle)); +} + +COVERAGE(TanF, 1) +static inline float TanF(float Angle) +{ + ASSERT_COVERED(TanF); + return TANF(ANGLE_USER_TO_INTERNAL(Angle)); +} + +COVERAGE(ACosF, 1) +static inline float ACosF(float Arg) +{ + ASSERT_COVERED(ACosF); + return ANGLE_INTERNAL_TO_USER(ACOSF(Arg)); +} + +COVERAGE(SqrtF, 1) +static inline float SqrtF(float Float) +{ + ASSERT_COVERED(SqrtF); + + float Result; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 In = _mm_set_ss(Float); + __m128 Out = _mm_sqrt_ss(In); + Result = _mm_cvtss_f32(Out); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t In = vdupq_n_f32(Float); + float32x4_t Out = vsqrtq_f32(In); + Result = vgetq_lane_f32(Out, 0); +#else + Result = SQRTF(Float); +#endif + + return Result; +} + +COVERAGE(InvSqrtF, 1) +static inline float InvSqrtF(float Float) +{ + ASSERT_COVERED(InvSqrtF); + + float Result; + + Result = 1.0f/SqrtF(Float); + + return Result; +} + + +/* + * Utility functions + */ + +COVERAGE(Lerp, 1) +static inline float Lerp(float A, float Time, float B) +{ + ASSERT_COVERED(Lerp); + return (1.0f - Time) * A + Time * B; +} + +COVERAGE(Clamp, 1) +static inline float Clamp(float Min, float Value, float Max) +{ + ASSERT_COVERED(Clamp); + + float Result = Value; + + if (Result < Min) + { + Result = Min; + } + + if (Result > Max) + { + Result = Max; + } + + return Result; +} + + +/* + * Vector initialization + */ + +COVERAGE(V2, 1) +static inline Vec2 V2(float X, float Y) +{ + ASSERT_COVERED(V2); + + Vec2 Result; + Result.X = X; + Result.Y = Y; + + return Result; +} + +COVERAGE(V3, 1) +static inline Vec3 V3(float X, float Y, float Z) +{ + ASSERT_COVERED(V3); + + Vec3 Result; + Result.X = X; + Result.Y = Y; + Result.Z = Z; + + return Result; +} + +COVERAGE(V4, 1) +static inline Vec4 V4(float X, float Y, float Z, float W) +{ + ASSERT_COVERED(V4); + + Vec4 Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_setr_ps(X, Y, Z, W); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t v = {X, Y, Z, W}; + Result.NEON = v; +#else + Result.X = X; + Result.Y = Y; + Result.Z = Z; + Result.W = W; +#endif + + return Result; +} + +COVERAGE(V4V, 1) +static inline Vec4 V4V(Vec3 Vector, float W) +{ + ASSERT_COVERED(V4V); + + Vec4 Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_setr_ps(Vector.X, Vector.Y, Vector.Z, W); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t v = {Vector.X, Vector.Y, Vector.Z, W}; + Result.NEON = v; +#else + Result.XYZ = Vector; + Result.W = W; +#endif + + return Result; +} + + +/* + * Binary vector operations + */ + +COVERAGE(AddV2, 1) +static inline Vec2 AddV2(Vec2 Left, Vec2 Right) +{ + ASSERT_COVERED(AddV2); + + Vec2 Result; + Result.X = Left.X + Right.X; + Result.Y = Left.Y + Right.Y; + + return Result; +} + +COVERAGE(AddV3, 1) +static inline Vec3 AddV3(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(AddV3); + + Vec3 Result; + Result.X = Left.X + Right.X; + Result.Y = Left.Y + Right.Y; + Result.Z = Left.Z + Right.Z; + + return Result; +} + +COVERAGE(AddV4, 1) +static inline Vec4 AddV4(Vec4 Left, Vec4 Right) +{ + ASSERT_COVERED(AddV4); + + Vec4 Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_add_ps(Left.SSE, Right.SSE); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = vaddq_f32(Left.NEON, Right.NEON); +#else + Result.X = Left.X + Right.X; + Result.Y = Left.Y + Right.Y; + Result.Z = Left.Z + Right.Z; + Result.W = Left.W + Right.W; +#endif + + return Result; +} + +COVERAGE(SubV2, 1) +static inline Vec2 SubV2(Vec2 Left, Vec2 Right) +{ + ASSERT_COVERED(SubV2); + + Vec2 Result; + Result.X = Left.X - Right.X; + Result.Y = Left.Y - Right.Y; + + return Result; +} + +COVERAGE(SubV3, 1) +static inline Vec3 SubV3(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(SubV3); + + Vec3 Result; + Result.X = Left.X - Right.X; + Result.Y = Left.Y - Right.Y; + Result.Z = Left.Z - Right.Z; + + return Result; +} + +COVERAGE(SubV4, 1) +static inline Vec4 SubV4(Vec4 Left, Vec4 Right) +{ + ASSERT_COVERED(SubV4); + + Vec4 Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_sub_ps(Left.SSE, Right.SSE); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = vsubq_f32(Left.NEON, Right.NEON); +#else + Result.X = Left.X - Right.X; + Result.Y = Left.Y - Right.Y; + Result.Z = Left.Z - Right.Z; + Result.W = Left.W - Right.W; +#endif + + return Result; +} + +COVERAGE(MulV2, 1) +static inline Vec2 MulV2(Vec2 Left, Vec2 Right) +{ + ASSERT_COVERED(MulV2); + + Vec2 Result; + Result.X = Left.X * Right.X; + Result.Y = Left.Y * Right.Y; + + return Result; +} + +COVERAGE(MulV2F, 1) +static inline Vec2 MulV2F(Vec2 Left, float Right) +{ + ASSERT_COVERED(MulV2F); + + Vec2 Result; + Result.X = Left.X * Right; + Result.Y = Left.Y * Right; + + return Result; +} + +COVERAGE(MulV3, 1) +static inline Vec3 MulV3(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(MulV3); + + Vec3 Result; + Result.X = Left.X * Right.X; + Result.Y = Left.Y * Right.Y; + Result.Z = Left.Z * Right.Z; + + return Result; +} + +COVERAGE(MulV3F, 1) +static inline Vec3 MulV3F(Vec3 Left, float Right) +{ + ASSERT_COVERED(MulV3F); + + Vec3 Result; + Result.X = Left.X * Right; + Result.Y = Left.Y * Right; + Result.Z = Left.Z * Right; + + return Result; +} + +COVERAGE(MulV4, 1) +static inline Vec4 MulV4(Vec4 Left, Vec4 Right) +{ + ASSERT_COVERED(MulV4); + + Vec4 Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_mul_ps(Left.SSE, Right.SSE); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = vmulq_f32(Left.NEON, Right.NEON); +#else + Result.X = Left.X * Right.X; + Result.Y = Left.Y * Right.Y; + Result.Z = Left.Z * Right.Z; + Result.W = Left.W * Right.W; +#endif + + return Result; +} + +COVERAGE(MulV4F, 1) +static inline Vec4 MulV4F(Vec4 Left, float Right) +{ + ASSERT_COVERED(MulV4F); + + Vec4 Result; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 Scalar = _mm_set1_ps(Right); + Result.SSE = _mm_mul_ps(Left.SSE, Scalar); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = vmulq_n_f32(Left.NEON, Right); +#else + Result.X = Left.X * Right; + Result.Y = Left.Y * Right; + Result.Z = Left.Z * Right; + Result.W = Left.W * Right; +#endif + + return Result; +} + +COVERAGE(DivV2, 1) +static inline Vec2 DivV2(Vec2 Left, Vec2 Right) +{ + ASSERT_COVERED(DivV2); + + Vec2 Result; + Result.X = Left.X / Right.X; + Result.Y = Left.Y / Right.Y; + + return Result; +} + +COVERAGE(DivV2F, 1) +static inline Vec2 DivV2F(Vec2 Left, float Right) +{ + ASSERT_COVERED(DivV2F); + + Vec2 Result; + Result.X = Left.X / Right; + Result.Y = Left.Y / Right; + + return Result; +} + +COVERAGE(DivV3, 1) +static inline Vec3 DivV3(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(DivV3); + + Vec3 Result; + Result.X = Left.X / Right.X; + Result.Y = Left.Y / Right.Y; + Result.Z = Left.Z / Right.Z; + + return Result; +} + +COVERAGE(DivV3F, 1) +static inline Vec3 DivV3F(Vec3 Left, float Right) +{ + ASSERT_COVERED(DivV3F); + + Vec3 Result; + Result.X = Left.X / Right; + Result.Y = Left.Y / Right; + Result.Z = Left.Z / Right; + + return Result; +} + +COVERAGE(DivV4, 1) +static inline Vec4 DivV4(Vec4 Left, Vec4 Right) +{ + ASSERT_COVERED(DivV4); + + Vec4 Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_div_ps(Left.SSE, Right.SSE); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = vdivq_f32(Left.NEON, Right.NEON); +#else + Result.X = Left.X / Right.X; + Result.Y = Left.Y / Right.Y; + Result.Z = Left.Z / Right.Z; + Result.W = Left.W / Right.W; +#endif + + return Result; +} + +COVERAGE(DivV4F, 1) +static inline Vec4 DivV4F(Vec4 Left, float Right) +{ + ASSERT_COVERED(DivV4F); + + Vec4 Result; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 Scalar = _mm_set1_ps(Right); + Result.SSE = _mm_div_ps(Left.SSE, Scalar); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t Scalar = vdupq_n_f32(Right); + Result.NEON = vdivq_f32(Left.NEON, Scalar); +#else + Result.X = Left.X / Right; + Result.Y = Left.Y / Right; + Result.Z = Left.Z / Right; + Result.W = Left.W / Right; +#endif + + return Result; +} + +COVERAGE(EqV2, 1) +static inline Bool EqV2(Vec2 Left, Vec2 Right) +{ + ASSERT_COVERED(EqV2); + return Left.X == Right.X && Left.Y == Right.Y; +} + +COVERAGE(EqV3, 1) +static inline Bool EqV3(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(EqV3); + return Left.X == Right.X && Left.Y == Right.Y && Left.Z == Right.Z; +} + +COVERAGE(EqV4, 1) +static inline Bool EqV4(Vec4 Left, Vec4 Right) +{ + ASSERT_COVERED(EqV4); + return Left.X == Right.X && Left.Y == Right.Y && Left.Z == Right.Z && Left.W == Right.W; +} + +COVERAGE(DotV2, 1) +static inline float DotV2(Vec2 Left, Vec2 Right) +{ + ASSERT_COVERED(DotV2); + return (Left.X * Right.X) + (Left.Y * Right.Y); +} + +COVERAGE(DotV3, 1) +static inline float DotV3(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(DotV3); + return (Left.X * Right.X) + (Left.Y * Right.Y) + (Left.Z * Right.Z); +} + +COVERAGE(DotV4, 1) +static inline float DotV4(Vec4 Left, Vec4 Right) +{ + ASSERT_COVERED(DotV4); + + float Result; + + // NOTE(zak): IN the future if we wanna check what version SSE is support + // we can use _mm_dp_ps (4.3) but for now we will use the old way. + // Or a r = _mm_mul_ps(v1, v2), r = _mm_hadd_ps(r, r), r = _mm_hadd_ps(r, r) for SSE3 +#ifdef HANDMADE_MATH__USE_SSE + __m128 SSEResultOne = _mm_mul_ps(Left.SSE, Right.SSE); + __m128 SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(2, 3, 0, 1)); + SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo); + SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(0, 1, 2, 3)); + SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo); + _mm_store_ss(&Result, SSEResultOne); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t NEONMultiplyResult = vmulq_f32(Left.NEON, Right.NEON); + float32x4_t NEONHalfAdd = vpaddq_f32(NEONMultiplyResult, NEONMultiplyResult); + float32x4_t NEONFullAdd = vpaddq_f32(NEONHalfAdd, NEONHalfAdd); + Result = vgetq_lane_f32(NEONFullAdd, 0); +#else + Result = ((Left.X * Right.X) + (Left.Z * Right.Z)) + ((Left.Y * Right.Y) + (Left.W * Right.W)); +#endif + + return Result; +} + +COVERAGE(Cross, 1) +static inline Vec3 Cross(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(Cross); + + Vec3 Result; + Result.X = (Left.Y * Right.Z) - (Left.Z * Right.Y); + Result.Y = (Left.Z * Right.X) - (Left.X * Right.Z); + Result.Z = (Left.X * Right.Y) - (Left.Y * Right.X); + + return Result; +} + + +/* + * Unary vector operations + */ + +COVERAGE(LenSqrV2, 1) +static inline float LenSqrV2(Vec2 A) +{ + ASSERT_COVERED(LenSqrV2); + return DotV2(A, A); +} + +COVERAGE(LenSqrV3, 1) +static inline float LenSqrV3(Vec3 A) +{ + ASSERT_COVERED(LenSqrV3); + return DotV3(A, A); +} + +COVERAGE(LenSqrV4, 1) +static inline float LenSqrV4(Vec4 A) +{ + ASSERT_COVERED(LenSqrV4); + return DotV4(A, A); +} + +COVERAGE(LenV2, 1) +static inline float LenV2(Vec2 A) +{ + ASSERT_COVERED(LenV2); + return SqrtF(LenSqrV2(A)); +} + +COVERAGE(LenV3, 1) +static inline float LenV3(Vec3 A) +{ + ASSERT_COVERED(LenV3); + return SqrtF(LenSqrV3(A)); +} + +COVERAGE(LenV4, 1) +static inline float LenV4(Vec4 A) +{ + ASSERT_COVERED(LenV4); + return SqrtF(LenSqrV4(A)); +} + +COVERAGE(NormV2, 1) +static inline Vec2 NormV2(Vec2 A) +{ + ASSERT_COVERED(NormV2); + return MulV2F(A, InvSqrtF(DotV2(A, A))); +} + +COVERAGE(NormV3, 1) +static inline Vec3 NormV3(Vec3 A) +{ + ASSERT_COVERED(NormV3); + return MulV3F(A, InvSqrtF(DotV3(A, A))); +} + +COVERAGE(NormV4, 1) +static inline Vec4 NormV4(Vec4 A) +{ + ASSERT_COVERED(NormV4); + return MulV4F(A, InvSqrtF(DotV4(A, A))); +} + +/* + * Utility vector functions + */ + +COVERAGE(LerpV2, 1) +static inline Vec2 LerpV2(Vec2 A, float Time, Vec2 B) +{ + ASSERT_COVERED(LerpV2); + return AddV2(MulV2F(A, 1.0f - Time), MulV2F(B, Time)); +} + +COVERAGE(LerpV3, 1) +static inline Vec3 LerpV3(Vec3 A, float Time, Vec3 B) +{ + ASSERT_COVERED(LerpV3); + return AddV3(MulV3F(A, 1.0f - Time), MulV3F(B, Time)); +} + +COVERAGE(LerpV4, 1) +static inline Vec4 LerpV4(Vec4 A, float Time, Vec4 B) +{ + ASSERT_COVERED(LerpV4); + return AddV4(MulV4F(A, 1.0f - Time), MulV4F(B, Time)); +} + +/* + * SSE stuff + */ + +COVERAGE(LinearCombineV4M4, 1) +static inline Vec4 LinearCombineV4M4(Vec4 Left, Mat4 Right) +{ + ASSERT_COVERED(LinearCombineV4M4); + + Vec4 Result; +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0x00), Right.Columns[0].SSE); + Result.SSE = _mm_add_ps(Result.SSE, _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0x55), Right.Columns[1].SSE)); + Result.SSE = _mm_add_ps(Result.SSE, _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0xaa), Right.Columns[2].SSE)); + Result.SSE = _mm_add_ps(Result.SSE, _mm_mul_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, 0xff), Right.Columns[3].SSE)); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = vmulq_laneq_f32(Right.Columns[0].NEON, Left.NEON, 0); + Result.NEON = vfmaq_laneq_f32(Result.NEON, Right.Columns[1].NEON, Left.NEON, 1); + Result.NEON = vfmaq_laneq_f32(Result.NEON, Right.Columns[2].NEON, Left.NEON, 2); + Result.NEON = vfmaq_laneq_f32(Result.NEON, Right.Columns[3].NEON, Left.NEON, 3); +#else + Result.X = Left.Elements[0] * Right.Columns[0].X; + Result.Y = Left.Elements[0] * Right.Columns[0].Y; + Result.Z = Left.Elements[0] * Right.Columns[0].Z; + Result.W = Left.Elements[0] * Right.Columns[0].W; + + Result.X += Left.Elements[1] * Right.Columns[1].X; + Result.Y += Left.Elements[1] * Right.Columns[1].Y; + Result.Z += Left.Elements[1] * Right.Columns[1].Z; + Result.W += Left.Elements[1] * Right.Columns[1].W; + + Result.X += Left.Elements[2] * Right.Columns[2].X; + Result.Y += Left.Elements[2] * Right.Columns[2].Y; + Result.Z += Left.Elements[2] * Right.Columns[2].Z; + Result.W += Left.Elements[2] * Right.Columns[2].W; + + Result.X += Left.Elements[3] * Right.Columns[3].X; + Result.Y += Left.Elements[3] * Right.Columns[3].Y; + Result.Z += Left.Elements[3] * Right.Columns[3].Z; + Result.W += Left.Elements[3] * Right.Columns[3].W; +#endif + + return Result; +} + +/* + * 2x2 Matrices + */ + +COVERAGE(M2, 1) +static inline Mat2 M2(void) +{ + ASSERT_COVERED(M2); + Mat2 Result = {0}; + return Result; +} + +COVERAGE(M2D, 1) +static inline Mat2 M2D(float Diagonal) +{ + ASSERT_COVERED(M2D); + + Mat2 Result = {0}; + Result.Elements[0][0] = Diagonal; + Result.Elements[1][1] = Diagonal; + + return Result; +} + +COVERAGE(TransposeM2, 1) +static inline Mat2 TransposeM2(Mat2 Matrix) +{ + ASSERT_COVERED(TransposeM2); + + Mat2 Result = Matrix; + + Result.Elements[0][1] = Matrix.Elements[1][0]; + Result.Elements[1][0] = Matrix.Elements[0][1]; + + return Result; +} + +COVERAGE(AddM2, 1) +static inline Mat2 AddM2(Mat2 Left, Mat2 Right) +{ + ASSERT_COVERED(AddM2); + + Mat2 Result; + + Result.Elements[0][0] = Left.Elements[0][0] + Right.Elements[0][0]; + Result.Elements[0][1] = Left.Elements[0][1] + Right.Elements[0][1]; + Result.Elements[1][0] = Left.Elements[1][0] + Right.Elements[1][0]; + Result.Elements[1][1] = Left.Elements[1][1] + Right.Elements[1][1]; + + return Result; +} + +COVERAGE(SubM2, 1) +static inline Mat2 SubM2(Mat2 Left, Mat2 Right) +{ + ASSERT_COVERED(SubM2); + + Mat2 Result; + + Result.Elements[0][0] = Left.Elements[0][0] - Right.Elements[0][0]; + Result.Elements[0][1] = Left.Elements[0][1] - Right.Elements[0][1]; + Result.Elements[1][0] = Left.Elements[1][0] - Right.Elements[1][0]; + Result.Elements[1][1] = Left.Elements[1][1] - Right.Elements[1][1]; + + return Result; +} + +COVERAGE(MulM2V2, 1) +static inline Vec2 MulM2V2(Mat2 Matrix, Vec2 Vector) +{ + ASSERT_COVERED(MulM2V2); + + Vec2 Result; + + Result.X = Vector.Elements[0] * Matrix.Columns[0].X; + Result.Y = Vector.Elements[0] * Matrix.Columns[0].Y; + + Result.X += Vector.Elements[1] * Matrix.Columns[1].X; + Result.Y += Vector.Elements[1] * Matrix.Columns[1].Y; + + return Result; +} + +COVERAGE(MulM2, 1) +static inline Mat2 MulM2(Mat2 Left, Mat2 Right) +{ + ASSERT_COVERED(MulM2); + + Mat2 Result; + Result.Columns[0] = MulM2V2(Left, Right.Columns[0]); + Result.Columns[1] = MulM2V2(Left, Right.Columns[1]); + + return Result; +} + +COVERAGE(MulM2F, 1) +static inline Mat2 MulM2F(Mat2 Matrix, float Scalar) +{ + ASSERT_COVERED(MulM2F); + + Mat2 Result; + + Result.Elements[0][0] = Matrix.Elements[0][0] * Scalar; + Result.Elements[0][1] = Matrix.Elements[0][1] * Scalar; + Result.Elements[1][0] = Matrix.Elements[1][0] * Scalar; + Result.Elements[1][1] = Matrix.Elements[1][1] * Scalar; + + return Result; +} + +COVERAGE(DivM2F, 1) +static inline Mat2 DivM2F(Mat2 Matrix, float Scalar) +{ + ASSERT_COVERED(DivM2F); + + Mat2 Result; + + Result.Elements[0][0] = Matrix.Elements[0][0] / Scalar; + Result.Elements[0][1] = Matrix.Elements[0][1] / Scalar; + Result.Elements[1][0] = Matrix.Elements[1][0] / Scalar; + Result.Elements[1][1] = Matrix.Elements[1][1] / Scalar; + + return Result; +} + +COVERAGE(DeterminantM2, 1) +static inline float DeterminantM2(Mat2 Matrix) +{ + ASSERT_COVERED(DeterminantM2); + return Matrix.Elements[0][0]*Matrix.Elements[1][1] - Matrix.Elements[0][1]*Matrix.Elements[1][0]; +} + + +COVERAGE(InvGeneralM2, 1) +static inline Mat2 InvGeneralM2(Mat2 Matrix) +{ + ASSERT_COVERED(InvGeneralM2); + + Mat2 Result; + float InvDeterminant = 1.0f / DeterminantM2(Matrix); + Result.Elements[0][0] = InvDeterminant * +Matrix.Elements[1][1]; + Result.Elements[1][1] = InvDeterminant * +Matrix.Elements[0][0]; + Result.Elements[0][1] = InvDeterminant * -Matrix.Elements[0][1]; + Result.Elements[1][0] = InvDeterminant * -Matrix.Elements[1][0]; + + return Result; +} + +/* + * 3x3 Matrices + */ + +COVERAGE(M3, 1) +static inline Mat3 M3(void) +{ + ASSERT_COVERED(M3); + Mat3 Result = {0}; + return Result; +} + +COVERAGE(M3D, 1) +static inline Mat3 M3D(float Diagonal) +{ + ASSERT_COVERED(M3D); + + Mat3 Result = {0}; + Result.Elements[0][0] = Diagonal; + Result.Elements[1][1] = Diagonal; + Result.Elements[2][2] = Diagonal; + + return Result; +} + +COVERAGE(TransposeM3, 1) +static inline Mat3 TransposeM3(Mat3 Matrix) +{ + ASSERT_COVERED(TransposeM3); + + Mat3 Result = Matrix; + + Result.Elements[0][1] = Matrix.Elements[1][0]; + Result.Elements[0][2] = Matrix.Elements[2][0]; + Result.Elements[1][0] = Matrix.Elements[0][1]; + Result.Elements[1][2] = Matrix.Elements[2][1]; + Result.Elements[2][1] = Matrix.Elements[1][2]; + Result.Elements[2][0] = Matrix.Elements[0][2]; + + return Result; +} + +COVERAGE(AddM3, 1) +static inline Mat3 AddM3(Mat3 Left, Mat3 Right) +{ + ASSERT_COVERED(AddM3); + + Mat3 Result; + + Result.Elements[0][0] = Left.Elements[0][0] + Right.Elements[0][0]; + Result.Elements[0][1] = Left.Elements[0][1] + Right.Elements[0][1]; + Result.Elements[0][2] = Left.Elements[0][2] + Right.Elements[0][2]; + Result.Elements[1][0] = Left.Elements[1][0] + Right.Elements[1][0]; + Result.Elements[1][1] = Left.Elements[1][1] + Right.Elements[1][1]; + Result.Elements[1][2] = Left.Elements[1][2] + Right.Elements[1][2]; + Result.Elements[2][0] = Left.Elements[2][0] + Right.Elements[2][0]; + Result.Elements[2][1] = Left.Elements[2][1] + Right.Elements[2][1]; + Result.Elements[2][2] = Left.Elements[2][2] + Right.Elements[2][2]; + + return Result; +} + +COVERAGE(SubM3, 1) +static inline Mat3 SubM3(Mat3 Left, Mat3 Right) +{ + ASSERT_COVERED(SubM3); + + Mat3 Result; + + Result.Elements[0][0] = Left.Elements[0][0] - Right.Elements[0][0]; + Result.Elements[0][1] = Left.Elements[0][1] - Right.Elements[0][1]; + Result.Elements[0][2] = Left.Elements[0][2] - Right.Elements[0][2]; + Result.Elements[1][0] = Left.Elements[1][0] - Right.Elements[1][0]; + Result.Elements[1][1] = Left.Elements[1][1] - Right.Elements[1][1]; + Result.Elements[1][2] = Left.Elements[1][2] - Right.Elements[1][2]; + Result.Elements[2][0] = Left.Elements[2][0] - Right.Elements[2][0]; + Result.Elements[2][1] = Left.Elements[2][1] - Right.Elements[2][1]; + Result.Elements[2][2] = Left.Elements[2][2] - Right.Elements[2][2]; + + return Result; +} + +COVERAGE(MulM3V3, 1) +static inline Vec3 MulM3V3(Mat3 Matrix, Vec3 Vector) +{ + ASSERT_COVERED(MulM3V3); + + Vec3 Result; + + Result.X = Vector.Elements[0] * Matrix.Columns[0].X; + Result.Y = Vector.Elements[0] * Matrix.Columns[0].Y; + Result.Z = Vector.Elements[0] * Matrix.Columns[0].Z; + + Result.X += Vector.Elements[1] * Matrix.Columns[1].X; + Result.Y += Vector.Elements[1] * Matrix.Columns[1].Y; + Result.Z += Vector.Elements[1] * Matrix.Columns[1].Z; + + Result.X += Vector.Elements[2] * Matrix.Columns[2].X; + Result.Y += Vector.Elements[2] * Matrix.Columns[2].Y; + Result.Z += Vector.Elements[2] * Matrix.Columns[2].Z; + + return Result; +} + +COVERAGE(MulM3, 1) +static inline Mat3 MulM3(Mat3 Left, Mat3 Right) +{ + ASSERT_COVERED(MulM3); + + Mat3 Result; + Result.Columns[0] = MulM3V3(Left, Right.Columns[0]); + Result.Columns[1] = MulM3V3(Left, Right.Columns[1]); + Result.Columns[2] = MulM3V3(Left, Right.Columns[2]); + + return Result; +} + +COVERAGE(MulM3F, 1) +static inline Mat3 MulM3F(Mat3 Matrix, float Scalar) +{ + ASSERT_COVERED(MulM3F); + + Mat3 Result; + + Result.Elements[0][0] = Matrix.Elements[0][0] * Scalar; + Result.Elements[0][1] = Matrix.Elements[0][1] * Scalar; + Result.Elements[0][2] = Matrix.Elements[0][2] * Scalar; + Result.Elements[1][0] = Matrix.Elements[1][0] * Scalar; + Result.Elements[1][1] = Matrix.Elements[1][1] * Scalar; + Result.Elements[1][2] = Matrix.Elements[1][2] * Scalar; + Result.Elements[2][0] = Matrix.Elements[2][0] * Scalar; + Result.Elements[2][1] = Matrix.Elements[2][1] * Scalar; + Result.Elements[2][2] = Matrix.Elements[2][2] * Scalar; + + return Result; +} + +COVERAGE(DivM3F, 1) +static inline Mat3 DivM3F(Mat3 Matrix, float Scalar) +{ + ASSERT_COVERED(DivM3F); + + Mat3 Result; + + Result.Elements[0][0] = Matrix.Elements[0][0] / Scalar; + Result.Elements[0][1] = Matrix.Elements[0][1] / Scalar; + Result.Elements[0][2] = Matrix.Elements[0][2] / Scalar; + Result.Elements[1][0] = Matrix.Elements[1][0] / Scalar; + Result.Elements[1][1] = Matrix.Elements[1][1] / Scalar; + Result.Elements[1][2] = Matrix.Elements[1][2] / Scalar; + Result.Elements[2][0] = Matrix.Elements[2][0] / Scalar; + Result.Elements[2][1] = Matrix.Elements[2][1] / Scalar; + Result.Elements[2][2] = Matrix.Elements[2][2] / Scalar; + + return Result; +} + +COVERAGE(DeterminantM3, 1) +static inline float DeterminantM3(Mat3 Matrix) +{ + ASSERT_COVERED(DeterminantM3); + + Mat3 _Cross; + _Cross.Columns[0] = Cross(Matrix.Columns[1], Matrix.Columns[2]); + _Cross.Columns[1] = Cross(Matrix.Columns[2], Matrix.Columns[0]); + _Cross.Columns[2] = Cross(Matrix.Columns[0], Matrix.Columns[1]); + + return DotV3(_Cross.Columns[2], Matrix.Columns[2]); +} + +COVERAGE(InvGeneralM3, 1) +static inline Mat3 InvGeneralM3(Mat3 Matrix) +{ + ASSERT_COVERED(InvGeneralM3); + + Mat3 _Cross; + _Cross.Columns[0] = Cross(Matrix.Columns[1], Matrix.Columns[2]); + _Cross.Columns[1] = Cross(Matrix.Columns[2], Matrix.Columns[0]); + _Cross.Columns[2] = Cross(Matrix.Columns[0], Matrix.Columns[1]); + + float InvDeterminant = 1.0f / DotV3(_Cross.Columns[2], Matrix.Columns[2]); + + Mat3 Result; + Result.Columns[0] = MulV3F(_Cross.Columns[0], InvDeterminant); + Result.Columns[1] = MulV3F(_Cross.Columns[1], InvDeterminant); + Result.Columns[2] = MulV3F(_Cross.Columns[2], InvDeterminant); + + return TransposeM3(Result); +} + +/* + * 4x4 Matrices + */ + +COVERAGE(M4, 1) +static inline Mat4 M4(void) +{ + ASSERT_COVERED(M4); + Mat4 Result = {0}; + return Result; +} + +COVERAGE(M4D, 1) +static inline Mat4 M4D(float Diagonal) +{ + ASSERT_COVERED(M4D); + + Mat4 Result = {0}; + Result.Elements[0][0] = Diagonal; + Result.Elements[1][1] = Diagonal; + Result.Elements[2][2] = Diagonal; + Result.Elements[3][3] = Diagonal; + + return Result; +} + +COVERAGE(TransposeM4, 1) +static inline Mat4 TransposeM4(Mat4 Matrix) +{ + ASSERT_COVERED(TransposeM4); + + Mat4 Result; +#ifdef HANDMADE_MATH__USE_SSE + Result = Matrix; + _MM_TRANSPOSE4_PS(Result.Columns[0].SSE, Result.Columns[1].SSE, Result.Columns[2].SSE, Result.Columns[3].SSE); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4x4_t Transposed = vld4q_f32((float*)Matrix.Columns); + Result.Columns[0].NEON = Transposed.val[0]; + Result.Columns[1].NEON = Transposed.val[1]; + Result.Columns[2].NEON = Transposed.val[2]; + Result.Columns[3].NEON = Transposed.val[3]; +#else + Result.Elements[0][0] = Matrix.Elements[0][0]; + Result.Elements[0][1] = Matrix.Elements[1][0]; + Result.Elements[0][2] = Matrix.Elements[2][0]; + Result.Elements[0][3] = Matrix.Elements[3][0]; + Result.Elements[1][0] = Matrix.Elements[0][1]; + Result.Elements[1][1] = Matrix.Elements[1][1]; + Result.Elements[1][2] = Matrix.Elements[2][1]; + Result.Elements[1][3] = Matrix.Elements[3][1]; + Result.Elements[2][0] = Matrix.Elements[0][2]; + Result.Elements[2][1] = Matrix.Elements[1][2]; + Result.Elements[2][2] = Matrix.Elements[2][2]; + Result.Elements[2][3] = Matrix.Elements[3][2]; + Result.Elements[3][0] = Matrix.Elements[0][3]; + Result.Elements[3][1] = Matrix.Elements[1][3]; + Result.Elements[3][2] = Matrix.Elements[2][3]; + Result.Elements[3][3] = Matrix.Elements[3][3]; +#endif + + return Result; +} + +COVERAGE(AddM4, 1) +static inline Mat4 AddM4(Mat4 Left, Mat4 Right) +{ + ASSERT_COVERED(AddM4); + + Mat4 Result; + + Result.Columns[0] = AddV4(Left.Columns[0], Right.Columns[0]); + Result.Columns[1] = AddV4(Left.Columns[1], Right.Columns[1]); + Result.Columns[2] = AddV4(Left.Columns[2], Right.Columns[2]); + Result.Columns[3] = AddV4(Left.Columns[3], Right.Columns[3]); + + return Result; +} + +COVERAGE(SubM4, 1) +static inline Mat4 SubM4(Mat4 Left, Mat4 Right) +{ + ASSERT_COVERED(SubM4); + + Mat4 Result; + + Result.Columns[0] = SubV4(Left.Columns[0], Right.Columns[0]); + Result.Columns[1] = SubV4(Left.Columns[1], Right.Columns[1]); + Result.Columns[2] = SubV4(Left.Columns[2], Right.Columns[2]); + Result.Columns[3] = SubV4(Left.Columns[3], Right.Columns[3]); + + return Result; +} + +COVERAGE(MulM4, 1) +static inline Mat4 MulM4(Mat4 Left, Mat4 Right) +{ + ASSERT_COVERED(MulM4); + + Mat4 Result; + Result.Columns[0] = LinearCombineV4M4(Right.Columns[0], Left); + Result.Columns[1] = LinearCombineV4M4(Right.Columns[1], Left); + Result.Columns[2] = LinearCombineV4M4(Right.Columns[2], Left); + Result.Columns[3] = LinearCombineV4M4(Right.Columns[3], Left); + + return Result; +} + +COVERAGE(MulM4F, 1) +static inline Mat4 MulM4F(Mat4 Matrix, float Scalar) +{ + ASSERT_COVERED(MulM4F); + + Mat4 Result; + + +#ifdef HANDMADE_MATH__USE_SSE + __m128 SSEScalar = _mm_set1_ps(Scalar); + Result.Columns[0].SSE = _mm_mul_ps(Matrix.Columns[0].SSE, SSEScalar); + Result.Columns[1].SSE = _mm_mul_ps(Matrix.Columns[1].SSE, SSEScalar); + Result.Columns[2].SSE = _mm_mul_ps(Matrix.Columns[2].SSE, SSEScalar); + Result.Columns[3].SSE = _mm_mul_ps(Matrix.Columns[3].SSE, SSEScalar); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.Columns[0].NEON = vmulq_n_f32(Matrix.Columns[0].NEON, Scalar); + Result.Columns[1].NEON = vmulq_n_f32(Matrix.Columns[1].NEON, Scalar); + Result.Columns[2].NEON = vmulq_n_f32(Matrix.Columns[2].NEON, Scalar); + Result.Columns[3].NEON = vmulq_n_f32(Matrix.Columns[3].NEON, Scalar); +#else + Result.Elements[0][0] = Matrix.Elements[0][0] * Scalar; + Result.Elements[0][1] = Matrix.Elements[0][1] * Scalar; + Result.Elements[0][2] = Matrix.Elements[0][2] * Scalar; + Result.Elements[0][3] = Matrix.Elements[0][3] * Scalar; + Result.Elements[1][0] = Matrix.Elements[1][0] * Scalar; + Result.Elements[1][1] = Matrix.Elements[1][1] * Scalar; + Result.Elements[1][2] = Matrix.Elements[1][2] * Scalar; + Result.Elements[1][3] = Matrix.Elements[1][3] * Scalar; + Result.Elements[2][0] = Matrix.Elements[2][0] * Scalar; + Result.Elements[2][1] = Matrix.Elements[2][1] * Scalar; + Result.Elements[2][2] = Matrix.Elements[2][2] * Scalar; + Result.Elements[2][3] = Matrix.Elements[2][3] * Scalar; + Result.Elements[3][0] = Matrix.Elements[3][0] * Scalar; + Result.Elements[3][1] = Matrix.Elements[3][1] * Scalar; + Result.Elements[3][2] = Matrix.Elements[3][2] * Scalar; + Result.Elements[3][3] = Matrix.Elements[3][3] * Scalar; +#endif + + return Result; +} + +COVERAGE(MulM4V4, 1) +static inline Vec4 MulM4V4(Mat4 Matrix, Vec4 Vector) +{ + ASSERT_COVERED(MulM4V4); + return LinearCombineV4M4(Vector, Matrix); +} + +COVERAGE(DivM4F, 1) +static inline Mat4 DivM4F(Mat4 Matrix, float Scalar) +{ + ASSERT_COVERED(DivM4F); + + Mat4 Result; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 SSEScalar = _mm_set1_ps(Scalar); + Result.Columns[0].SSE = _mm_div_ps(Matrix.Columns[0].SSE, SSEScalar); + Result.Columns[1].SSE = _mm_div_ps(Matrix.Columns[1].SSE, SSEScalar); + Result.Columns[2].SSE = _mm_div_ps(Matrix.Columns[2].SSE, SSEScalar); + Result.Columns[3].SSE = _mm_div_ps(Matrix.Columns[3].SSE, SSEScalar); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t NEONScalar = vdupq_n_f32(Scalar); + Result.Columns[0].NEON = vdivq_f32(Matrix.Columns[0].NEON, NEONScalar); + Result.Columns[1].NEON = vdivq_f32(Matrix.Columns[1].NEON, NEONScalar); + Result.Columns[2].NEON = vdivq_f32(Matrix.Columns[2].NEON, NEONScalar); + Result.Columns[3].NEON = vdivq_f32(Matrix.Columns[3].NEON, NEONScalar); +#else + Result.Elements[0][0] = Matrix.Elements[0][0] / Scalar; + Result.Elements[0][1] = Matrix.Elements[0][1] / Scalar; + Result.Elements[0][2] = Matrix.Elements[0][2] / Scalar; + Result.Elements[0][3] = Matrix.Elements[0][3] / Scalar; + Result.Elements[1][0] = Matrix.Elements[1][0] / Scalar; + Result.Elements[1][1] = Matrix.Elements[1][1] / Scalar; + Result.Elements[1][2] = Matrix.Elements[1][2] / Scalar; + Result.Elements[1][3] = Matrix.Elements[1][3] / Scalar; + Result.Elements[2][0] = Matrix.Elements[2][0] / Scalar; + Result.Elements[2][1] = Matrix.Elements[2][1] / Scalar; + Result.Elements[2][2] = Matrix.Elements[2][2] / Scalar; + Result.Elements[2][3] = Matrix.Elements[2][3] / Scalar; + Result.Elements[3][0] = Matrix.Elements[3][0] / Scalar; + Result.Elements[3][1] = Matrix.Elements[3][1] / Scalar; + Result.Elements[3][2] = Matrix.Elements[3][2] / Scalar; + Result.Elements[3][3] = Matrix.Elements[3][3] / Scalar; +#endif + + return Result; +} + +COVERAGE(DeterminantM4, 1) +static inline float DeterminantM4(Mat4 Matrix) +{ + ASSERT_COVERED(DeterminantM4); + + Vec3 C01 = Cross(Matrix.Columns[0].XYZ, Matrix.Columns[1].XYZ); + Vec3 C23 = Cross(Matrix.Columns[2].XYZ, Matrix.Columns[3].XYZ); + Vec3 B10 = SubV3(MulV3F(Matrix.Columns[0].XYZ, Matrix.Columns[1].W), MulV3F(Matrix.Columns[1].XYZ, Matrix.Columns[0].W)); + Vec3 B32 = SubV3(MulV3F(Matrix.Columns[2].XYZ, Matrix.Columns[3].W), MulV3F(Matrix.Columns[3].XYZ, Matrix.Columns[2].W)); + + return DotV3(C01, B32) + DotV3(C23, B10); +} + +COVERAGE(InvGeneralM4, 1) +// Returns a general-purpose inverse of an Mat4. Note that special-purpose inverses of many transformations +// are available and will be more efficient. +static inline Mat4 InvGeneralM4(Mat4 Matrix) +{ + ASSERT_COVERED(InvGeneralM4); + + Vec3 C01 = Cross(Matrix.Columns[0].XYZ, Matrix.Columns[1].XYZ); + Vec3 C23 = Cross(Matrix.Columns[2].XYZ, Matrix.Columns[3].XYZ); + Vec3 B10 = SubV3(MulV3F(Matrix.Columns[0].XYZ, Matrix.Columns[1].W), MulV3F(Matrix.Columns[1].XYZ, Matrix.Columns[0].W)); + Vec3 B32 = SubV3(MulV3F(Matrix.Columns[2].XYZ, Matrix.Columns[3].W), MulV3F(Matrix.Columns[3].XYZ, Matrix.Columns[2].W)); + + float InvDeterminant = 1.0f / (DotV3(C01, B32) + DotV3(C23, B10)); + C01 = MulV3F(C01, InvDeterminant); + C23 = MulV3F(C23, InvDeterminant); + B10 = MulV3F(B10, InvDeterminant); + B32 = MulV3F(B32, InvDeterminant); + + Mat4 Result; + Result.Columns[0] = V4V(AddV3(Cross(Matrix.Columns[1].XYZ, B32), MulV3F(C23, Matrix.Columns[1].W)), -DotV3(Matrix.Columns[1].XYZ, C23)); + Result.Columns[1] = V4V(SubV3(Cross(B32, Matrix.Columns[0].XYZ), MulV3F(C23, Matrix.Columns[0].W)), +DotV3(Matrix.Columns[0].XYZ, C23)); + Result.Columns[2] = V4V(AddV3(Cross(Matrix.Columns[3].XYZ, B10), MulV3F(C01, Matrix.Columns[3].W)), -DotV3(Matrix.Columns[3].XYZ, C01)); + Result.Columns[3] = V4V(SubV3(Cross(B10, Matrix.Columns[2].XYZ), MulV3F(C01, Matrix.Columns[2].W)), +DotV3(Matrix.Columns[2].XYZ, C01)); + + return TransposeM4(Result); +} + +/* + * Common graphics transformations + */ + +COVERAGE(Orthographic_RH_NO, 1) +// Produces a right-handed orthographic projection matrix with Z ranging from -1 to 1 (the GL convention). +// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes. +// Near and Far specify the distances to the near and far clipping planes. +static inline Mat4 Orthographic_RH_NO(float Left, float Right, float Bottom, float Top, float Near, float Far) +{ + ASSERT_COVERED(Orthographic_RH_NO); + + Mat4 Result = {0}; + + Result.Elements[0][0] = 2.0f / (Right - Left); + Result.Elements[1][1] = 2.0f / (Top - Bottom); + Result.Elements[2][2] = 2.0f / (Near - Far); + Result.Elements[3][3] = 1.0f; + + Result.Elements[3][0] = (Left + Right) / (Left - Right); + Result.Elements[3][1] = (Bottom + Top) / (Bottom - Top); + Result.Elements[3][2] = (Near + Far) / (Near - Far); + + return Result; +} + +COVERAGE(Orthographic_RH_ZO, 1) +// Produces a right-handed orthographic projection matrix with Z ranging from 0 to 1 (the DirectX convention). +// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes. +// Near and Far specify the distances to the near and far clipping planes. +static inline Mat4 Orthographic_RH_ZO(float Left, float Right, float Bottom, float Top, float Near, float Far) +{ + ASSERT_COVERED(Orthographic_RH_ZO); + + Mat4 Result = {0}; + + Result.Elements[0][0] = 2.0f / (Right - Left); + Result.Elements[1][1] = 2.0f / (Top - Bottom); + Result.Elements[2][2] = 1.0f / (Near - Far); + Result.Elements[3][3] = 1.0f; + + Result.Elements[3][0] = (Left + Right) / (Left - Right); + Result.Elements[3][1] = (Bottom + Top) / (Bottom - Top); + Result.Elements[3][2] = (Near) / (Near - Far); + + return Result; +} + +COVERAGE(Orthographic_LH_NO, 1) +// Produces a left-handed orthographic projection matrix with Z ranging from -1 to 1 (the GL convention). +// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes. +// Near and Far specify the distances to the near and far clipping planes. +static inline Mat4 Orthographic_LH_NO(float Left, float Right, float Bottom, float Top, float Near, float Far) +{ + ASSERT_COVERED(Orthographic_LH_NO); + + Mat4 Result = Orthographic_RH_NO(Left, Right, Bottom, Top, Near, Far); + Result.Elements[2][2] = -Result.Elements[2][2]; + + return Result; +} + +COVERAGE(Orthographic_LH_ZO, 1) +// Produces a left-handed orthographic projection matrix with Z ranging from 0 to 1 (the DirectX convention). +// Left, Right, Bottom, and Top specify the coordinates of their respective clipping planes. +// Near and Far specify the distances to the near and far clipping planes. +static inline Mat4 Orthographic_LH_ZO(float Left, float Right, float Bottom, float Top, float Near, float Far) +{ + ASSERT_COVERED(Orthographic_LH_ZO); + + Mat4 Result = Orthographic_RH_ZO(Left, Right, Bottom, Top, Near, Far); + Result.Elements[2][2] = -Result.Elements[2][2]; + + return Result; +} + +COVERAGE(InvOrthographic, 1) +// Returns an inverse for the given orthographic projection matrix. Works for all orthographic +// projection matrices, regardless of handedness or NDC convention. +static inline Mat4 InvOrthographic(Mat4 OrthoMatrix) +{ + ASSERT_COVERED(InvOrthographic); + + Mat4 Result = {0}; + Result.Elements[0][0] = 1.0f / OrthoMatrix.Elements[0][0]; + Result.Elements[1][1] = 1.0f / OrthoMatrix.Elements[1][1]; + Result.Elements[2][2] = 1.0f / OrthoMatrix.Elements[2][2]; + Result.Elements[3][3] = 1.0f; + + Result.Elements[3][0] = -OrthoMatrix.Elements[3][0] * Result.Elements[0][0]; + Result.Elements[3][1] = -OrthoMatrix.Elements[3][1] * Result.Elements[1][1]; + Result.Elements[3][2] = -OrthoMatrix.Elements[3][2] * Result.Elements[2][2]; + + return Result; +} + +COVERAGE(Perspective_RH_NO, 1) +static inline Mat4 Perspective_RH_NO(float FOV, float AspectRatio, float Near, float Far) +{ + ASSERT_COVERED(Perspective_RH_NO); + + Mat4 Result = {0}; + + // See https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml + + float Cotangent = 1.0f / TanF(FOV / 2.0f); + Result.Elements[0][0] = Cotangent / AspectRatio; + Result.Elements[1][1] = Cotangent; + Result.Elements[2][3] = -1.0f; + + Result.Elements[2][2] = (Near + Far) / (Near - Far); + Result.Elements[3][2] = (2.0f * Near * Far) / (Near - Far); + + return Result; +} + +COVERAGE(Perspective_RH_ZO, 1) +static inline Mat4 Perspective_RH_ZO(float FOV, float AspectRatio, float Near, float Far) +{ + ASSERT_COVERED(Perspective_RH_ZO); + + Mat4 Result = {0}; + + // See https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml + + float Cotangent = 1.0f / TanF(FOV / 2.0f); + Result.Elements[0][0] = Cotangent / AspectRatio; + Result.Elements[1][1] = Cotangent; + Result.Elements[2][3] = -1.0f; + + Result.Elements[2][2] = (Far) / (Near - Far); + Result.Elements[3][2] = (Near * Far) / (Near - Far); + + return Result; +} + +COVERAGE(Perspective_LH_NO, 1) +static inline Mat4 Perspective_LH_NO(float FOV, float AspectRatio, float Near, float Far) +{ + ASSERT_COVERED(Perspective_LH_NO); + + Mat4 Result = Perspective_RH_NO(FOV, AspectRatio, Near, Far); + Result.Elements[2][2] = -Result.Elements[2][2]; + Result.Elements[2][3] = -Result.Elements[2][3]; + + return Result; +} + +COVERAGE(Perspective_LH_ZO, 1) +static inline Mat4 Perspective_LH_ZO(float FOV, float AspectRatio, float Near, float Far) +{ + ASSERT_COVERED(Perspective_LH_ZO); + + Mat4 Result = Perspective_RH_ZO(FOV, AspectRatio, Near, Far); + Result.Elements[2][2] = -Result.Elements[2][2]; + Result.Elements[2][3] = -Result.Elements[2][3]; + + return Result; +} + +COVERAGE(InvPerspective_RH, 1) +static inline Mat4 InvPerspective_RH(Mat4 PerspectiveMatrix) +{ + ASSERT_COVERED(InvPerspective_RH); + + Mat4 Result = {0}; + Result.Elements[0][0] = 1.0f / PerspectiveMatrix.Elements[0][0]; + Result.Elements[1][1] = 1.0f / PerspectiveMatrix.Elements[1][1]; + Result.Elements[2][2] = 0.0f; + + Result.Elements[2][3] = 1.0f / PerspectiveMatrix.Elements[3][2]; + Result.Elements[3][3] = PerspectiveMatrix.Elements[2][2] * Result.Elements[2][3]; + Result.Elements[3][2] = PerspectiveMatrix.Elements[2][3]; + + return Result; +} + +COVERAGE(InvPerspective_LH, 1) +static inline Mat4 InvPerspective_LH(Mat4 PerspectiveMatrix) +{ + ASSERT_COVERED(InvPerspective_LH); + + Mat4 Result = {0}; + Result.Elements[0][0] = 1.0f / PerspectiveMatrix.Elements[0][0]; + Result.Elements[1][1] = 1.0f / PerspectiveMatrix.Elements[1][1]; + Result.Elements[2][2] = 0.0f; + + Result.Elements[2][3] = 1.0f / PerspectiveMatrix.Elements[3][2]; + Result.Elements[3][3] = PerspectiveMatrix.Elements[2][2] * -Result.Elements[2][3]; + Result.Elements[3][2] = PerspectiveMatrix.Elements[2][3]; + + return Result; +} + +COVERAGE(Translate, 1) +static inline Mat4 Translate(Vec3 Translation) +{ + ASSERT_COVERED(Translate); + + Mat4 Result = M4D(1.0f); + Result.Elements[3][0] = Translation.X; + Result.Elements[3][1] = Translation.Y; + Result.Elements[3][2] = Translation.Z; + + return Result; +} + +COVERAGE(InvTranslate, 1) +static inline Mat4 InvTranslate(Mat4 TranslationMatrix) +{ + ASSERT_COVERED(InvTranslate); + + Mat4 Result = TranslationMatrix; + Result.Elements[3][0] = -Result.Elements[3][0]; + Result.Elements[3][1] = -Result.Elements[3][1]; + Result.Elements[3][2] = -Result.Elements[3][2]; + + return Result; +} + +COVERAGE(Rotate_RH, 1) +static inline Mat4 Rotate_RH(float Angle, Vec3 Axis) +{ + ASSERT_COVERED(Rotate_RH); + + Mat4 Result = M4D(1.0f); + + Axis = NormV3(Axis); + + float SinTheta = SinF(Angle); + float CosTheta = CosF(Angle); + float CosValue = 1.0f - CosTheta; + + Result.Elements[0][0] = (Axis.X * Axis.X * CosValue) + CosTheta; + Result.Elements[0][1] = (Axis.X * Axis.Y * CosValue) + (Axis.Z * SinTheta); + Result.Elements[0][2] = (Axis.X * Axis.Z * CosValue) - (Axis.Y * SinTheta); + + Result.Elements[1][0] = (Axis.Y * Axis.X * CosValue) - (Axis.Z * SinTheta); + Result.Elements[1][1] = (Axis.Y * Axis.Y * CosValue) + CosTheta; + Result.Elements[1][2] = (Axis.Y * Axis.Z * CosValue) + (Axis.X * SinTheta); + + Result.Elements[2][0] = (Axis.Z * Axis.X * CosValue) + (Axis.Y * SinTheta); + Result.Elements[2][1] = (Axis.Z * Axis.Y * CosValue) - (Axis.X * SinTheta); + Result.Elements[2][2] = (Axis.Z * Axis.Z * CosValue) + CosTheta; + + return Result; +} + +COVERAGE(Rotate_LH, 1) +static inline Mat4 Rotate_LH(float Angle, Vec3 Axis) +{ + ASSERT_COVERED(Rotate_LH); + /* NOTE(lcf): Matrix will be inverse/transpose of RH. */ + return Rotate_RH(-Angle, Axis); +} + +COVERAGE(InvRotate, 1) +static inline Mat4 InvRotate(Mat4 RotationMatrix) +{ + ASSERT_COVERED(InvRotate); + return TransposeM4(RotationMatrix); +} + +COVERAGE(Scale, 1) +static inline Mat4 Scale(Vec3 Scale) +{ + ASSERT_COVERED(Scale); + + Mat4 Result = M4D(1.0f); + Result.Elements[0][0] = Scale.X; + Result.Elements[1][1] = Scale.Y; + Result.Elements[2][2] = Scale.Z; + + return Result; +} + +COVERAGE(InvScale, 1) +static inline Mat4 InvScale(Mat4 ScaleMatrix) +{ + ASSERT_COVERED(InvScale); + + Mat4 Result = ScaleMatrix; + Result.Elements[0][0] = 1.0f / Result.Elements[0][0]; + Result.Elements[1][1] = 1.0f / Result.Elements[1][1]; + Result.Elements[2][2] = 1.0f / Result.Elements[2][2]; + + return Result; +} + +static inline Mat4 _LookAt(Vec3 F, Vec3 S, Vec3 U, Vec3 Eye) +{ + Mat4 Result; + + Result.Elements[0][0] = S.X; + Result.Elements[0][1] = U.X; + Result.Elements[0][2] = -F.X; + Result.Elements[0][3] = 0.0f; + + Result.Elements[1][0] = S.Y; + Result.Elements[1][1] = U.Y; + Result.Elements[1][2] = -F.Y; + Result.Elements[1][3] = 0.0f; + + Result.Elements[2][0] = S.Z; + Result.Elements[2][1] = U.Z; + Result.Elements[2][2] = -F.Z; + Result.Elements[2][3] = 0.0f; + + Result.Elements[3][0] = -DotV3(S, Eye); + Result.Elements[3][1] = -DotV3(U, Eye); + Result.Elements[3][2] = DotV3(F, Eye); + Result.Elements[3][3] = 1.0f; + + return Result; +} + +COVERAGE(LookAt_RH, 1) +static inline Mat4 LookAt_RH(Vec3 Eye, Vec3 Center, Vec3 Up) +{ + ASSERT_COVERED(LookAt_RH); + + Vec3 F = NormV3(SubV3(Center, Eye)); + Vec3 S = NormV3(Cross(F, Up)); + Vec3 U = Cross(S, F); + + return _LookAt(F, S, U, Eye); +} + +COVERAGE(LookAt_LH, 1) +static inline Mat4 LookAt_LH(Vec3 Eye, Vec3 Center, Vec3 Up) +{ + ASSERT_COVERED(LookAt_LH); + + Vec3 F = NormV3(SubV3(Eye, Center)); + Vec3 S = NormV3(Cross(F, Up)); + Vec3 U = Cross(S, F); + + return _LookAt(F, S, U, Eye); +} + +COVERAGE(InvLookAt, 1) +static inline Mat4 InvLookAt(Mat4 Matrix) +{ + ASSERT_COVERED(InvLookAt); + Mat4 Result; + + Mat3 Rotation = {0}; + Rotation.Columns[0] = Matrix.Columns[0].XYZ; + Rotation.Columns[1] = Matrix.Columns[1].XYZ; + Rotation.Columns[2] = Matrix.Columns[2].XYZ; + Rotation = TransposeM3(Rotation); + + Result.Columns[0] = V4V(Rotation.Columns[0], 0.0f); + Result.Columns[1] = V4V(Rotation.Columns[1], 0.0f); + Result.Columns[2] = V4V(Rotation.Columns[2], 0.0f); + Result.Columns[3] = MulV4F(Matrix.Columns[3], -1.0f); + Result.Elements[3][0] = -1.0f * Matrix.Elements[3][0] / + (Rotation.Elements[0][0] + Rotation.Elements[0][1] + Rotation.Elements[0][2]); + Result.Elements[3][1] = -1.0f * Matrix.Elements[3][1] / + (Rotation.Elements[1][0] + Rotation.Elements[1][1] + Rotation.Elements[1][2]); + Result.Elements[3][2] = -1.0f * Matrix.Elements[3][2] / + (Rotation.Elements[2][0] + Rotation.Elements[2][1] + Rotation.Elements[2][2]); + Result.Elements[3][3] = 1.0f; + + return Result; +} + +/* + * Quaternion operations + */ + +COVERAGE(Q, 1) +static inline Quat Q(float X, float Y, float Z, float W) +{ + ASSERT_COVERED(Q); + + Quat Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_setr_ps(X, Y, Z, W); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t v = { X, Y, Z, W }; + Result.NEON = v; +#else + Result.X = X; + Result.Y = Y; + Result.Z = Z; + Result.W = W; +#endif + + return Result; +} + +COVERAGE(QV4, 1) +static inline Quat QV4(Vec4 Vector) +{ + ASSERT_COVERED(QV4); + + Quat Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = Vector.SSE; +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = Vector.NEON; +#else + Result.X = Vector.X; + Result.Y = Vector.Y; + Result.Z = Vector.Z; + Result.W = Vector.W; +#endif + + return Result; +} + +COVERAGE(AddQ, 1) +static inline Quat AddQ(Quat Left, Quat Right) +{ + ASSERT_COVERED(AddQ); + + Quat Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_add_ps(Left.SSE, Right.SSE); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = vaddq_f32(Left.NEON, Right.NEON); +#else + + Result.X = Left.X + Right.X; + Result.Y = Left.Y + Right.Y; + Result.Z = Left.Z + Right.Z; + Result.W = Left.W + Right.W; +#endif + + return Result; +} + +COVERAGE(SubQ, 1) +static inline Quat SubQ(Quat Left, Quat Right) +{ + ASSERT_COVERED(SubQ); + + Quat Result; + +#ifdef HANDMADE_MATH__USE_SSE + Result.SSE = _mm_sub_ps(Left.SSE, Right.SSE); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = vsubq_f32(Left.NEON, Right.NEON); +#else + Result.X = Left.X - Right.X; + Result.Y = Left.Y - Right.Y; + Result.Z = Left.Z - Right.Z; + Result.W = Left.W - Right.W; +#endif + + return Result; +} + +COVERAGE(MulQ, 1) +static inline Quat MulQ(Quat Left, Quat Right) +{ + ASSERT_COVERED(MulQ); + + Quat Result; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 SSEResultOne = _mm_xor_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, _MM_SHUFFLE(0, 0, 0, 0)), _mm_setr_ps(0.f, -0.f, 0.f, -0.f)); + __m128 SSEResultTwo = _mm_shuffle_ps(Right.SSE, Right.SSE, _MM_SHUFFLE(0, 1, 2, 3)); + __m128 SSEResultThree = _mm_mul_ps(SSEResultTwo, SSEResultOne); + + SSEResultOne = _mm_xor_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, _MM_SHUFFLE(1, 1, 1, 1)) , _mm_setr_ps(0.f, 0.f, -0.f, -0.f)); + SSEResultTwo = _mm_shuffle_ps(Right.SSE, Right.SSE, _MM_SHUFFLE(1, 0, 3, 2)); + SSEResultThree = _mm_add_ps(SSEResultThree, _mm_mul_ps(SSEResultTwo, SSEResultOne)); + + SSEResultOne = _mm_xor_ps(_mm_shuffle_ps(Left.SSE, Left.SSE, _MM_SHUFFLE(2, 2, 2, 2)), _mm_setr_ps(-0.f, 0.f, 0.f, -0.f)); + SSEResultTwo = _mm_shuffle_ps(Right.SSE, Right.SSE, _MM_SHUFFLE(2, 3, 0, 1)); + SSEResultThree = _mm_add_ps(SSEResultThree, _mm_mul_ps(SSEResultTwo, SSEResultOne)); + + SSEResultOne = _mm_shuffle_ps(Left.SSE, Left.SSE, _MM_SHUFFLE(3, 3, 3, 3)); + SSEResultTwo = _mm_shuffle_ps(Right.SSE, Right.SSE, _MM_SHUFFLE(3, 2, 1, 0)); + Result.SSE = _mm_add_ps(SSEResultThree, _mm_mul_ps(SSEResultTwo, SSEResultOne)); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t Right1032 = vrev64q_f32(Right.NEON); + float32x4_t Right3210 = vcombine_f32(vget_high_f32(Right1032), vget_low_f32(Right1032)); + float32x4_t Right2301 = vrev64q_f32(Right3210); + + float32x4_t FirstSign = {1.0f, -1.0f, 1.0f, -1.0f}; + Result.NEON = vmulq_f32(Right3210, vmulq_f32(vdupq_laneq_f32(Left.NEON, 0), FirstSign)); + float32x4_t SecondSign = {1.0f, 1.0f, -1.0f, -1.0f}; + Result.NEON = vfmaq_f32(Result.NEON, Right2301, vmulq_f32(vdupq_laneq_f32(Left.NEON, 1), SecondSign)); + float32x4_t ThirdSign = {-1.0f, 1.0f, 1.0f, -1.0f}; + Result.NEON = vfmaq_f32(Result.NEON, Right1032, vmulq_f32(vdupq_laneq_f32(Left.NEON, 2), ThirdSign)); + Result.NEON = vfmaq_laneq_f32(Result.NEON, Right.NEON, Left.NEON, 3); + +#else + Result.X = Right.Elements[3] * +Left.Elements[0]; + Result.Y = Right.Elements[2] * -Left.Elements[0]; + Result.Z = Right.Elements[1] * +Left.Elements[0]; + Result.W = Right.Elements[0] * -Left.Elements[0]; + + Result.X += Right.Elements[2] * +Left.Elements[1]; + Result.Y += Right.Elements[3] * +Left.Elements[1]; + Result.Z += Right.Elements[0] * -Left.Elements[1]; + Result.W += Right.Elements[1] * -Left.Elements[1]; + + Result.X += Right.Elements[1] * -Left.Elements[2]; + Result.Y += Right.Elements[0] * +Left.Elements[2]; + Result.Z += Right.Elements[3] * +Left.Elements[2]; + Result.W += Right.Elements[2] * -Left.Elements[2]; + + Result.X += Right.Elements[0] * +Left.Elements[3]; + Result.Y += Right.Elements[1] * +Left.Elements[3]; + Result.Z += Right.Elements[2] * +Left.Elements[3]; + Result.W += Right.Elements[3] * +Left.Elements[3]; +#endif + + return Result; +} + +COVERAGE(MulQF, 1) +static inline Quat MulQF(Quat Left, float Multiplicative) +{ + ASSERT_COVERED(MulQF); + + Quat Result; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 Scalar = _mm_set1_ps(Multiplicative); + Result.SSE = _mm_mul_ps(Left.SSE, Scalar); +#elif defined(HANDMADE_MATH__USE_NEON) + Result.NEON = vmulq_n_f32(Left.NEON, Multiplicative); +#else + Result.X = Left.X * Multiplicative; + Result.Y = Left.Y * Multiplicative; + Result.Z = Left.Z * Multiplicative; + Result.W = Left.W * Multiplicative; +#endif + + return Result; +} + +COVERAGE(DivQF, 1) +static inline Quat DivQF(Quat Left, float Divnd) +{ + ASSERT_COVERED(DivQF); + + Quat Result; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 Scalar = _mm_set1_ps(Divnd); + Result.SSE = _mm_div_ps(Left.SSE, Scalar); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t Scalar = vdupq_n_f32(Divnd); + Result.NEON = vdivq_f32(Left.NEON, Scalar); +#else + Result.X = Left.X / Divnd; + Result.Y = Left.Y / Divnd; + Result.Z = Left.Z / Divnd; + Result.W = Left.W / Divnd; +#endif + + return Result; +} + +COVERAGE(DotQ, 1) +static inline float DotQ(Quat Left, Quat Right) +{ + ASSERT_COVERED(DotQ); + + float Result; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 SSEResultOne = _mm_mul_ps(Left.SSE, Right.SSE); + __m128 SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(2, 3, 0, 1)); + SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo); + SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(0, 1, 2, 3)); + SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo); + _mm_store_ss(&Result, SSEResultOne); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t NEONMultiplyResult = vmulq_f32(Left.NEON, Right.NEON); + float32x4_t NEONHalfAdd = vpaddq_f32(NEONMultiplyResult, NEONMultiplyResult); + float32x4_t NEONFullAdd = vpaddq_f32(NEONHalfAdd, NEONHalfAdd); + Result = vgetq_lane_f32(NEONFullAdd, 0); +#else + Result = ((Left.X * Right.X) + (Left.Z * Right.Z)) + ((Left.Y * Right.Y) + (Left.W * Right.W)); +#endif + + return Result; +} + +COVERAGE(InvQ, 1) +static inline Quat InvQ(Quat Left) +{ + ASSERT_COVERED(InvQ); + + Quat Result; + Result.X = -Left.X; + Result.Y = -Left.Y; + Result.Z = -Left.Z; + Result.W = Left.W; + + return DivQF(Result, (DotQ(Left, Left))); +} + +COVERAGE(NormQ, 1) +static inline Quat NormQ(Quat _Quat) +{ + ASSERT_COVERED(NormQ); + + /* NOTE(lcf): Take advantage of SSE implementation in NormV4 */ + Vec4 Vec = {_Quat.X, _Quat.Y, _Quat.Z, _Quat.W}; + Vec = NormV4(Vec); + Quat Result = {Vec.X, Vec.Y, Vec.Z, Vec.W}; + + return Result; +} + +static inline Quat _MixQ(Quat Left, float MixLeft, Quat Right, float MixRight) { + Quat Result; + +#ifdef HANDMADE_MATH__USE_SSE + __m128 ScalarLeft = _mm_set1_ps(MixLeft); + __m128 ScalarRight = _mm_set1_ps(MixRight); + __m128 SSEResultOne = _mm_mul_ps(Left.SSE, ScalarLeft); + __m128 SSEResultTwo = _mm_mul_ps(Right.SSE, ScalarRight); + Result.SSE = _mm_add_ps(SSEResultOne, SSEResultTwo); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t ScaledLeft = vmulq_n_f32(Left.NEON, MixLeft); + float32x4_t ScaledRight = vmulq_n_f32(Right.NEON, MixRight); + Result.NEON = vaddq_f32(ScaledLeft, ScaledRight); +#else + Result.X = Left.X*MixLeft + Right.X*MixRight; + Result.Y = Left.Y*MixLeft + Right.Y*MixRight; + Result.Z = Left.Z*MixLeft + Right.Z*MixRight; + Result.W = Left.W*MixLeft + Right.W*MixRight; +#endif + + return Result; +} + +COVERAGE(NLerp, 1) +static inline Quat NLerp(Quat Left, float Time, Quat Right) +{ + ASSERT_COVERED(NLerp); + + Quat Result = _MixQ(Left, 1.0f-Time, Right, Time); + Result = NormQ(Result); + + return Result; +} + +COVERAGE(SLerp, 1) +static inline Quat SLerp(Quat Left, float Time, Quat Right) +{ + ASSERT_COVERED(SLerp); + + Quat Result; + + float Cos_Theta = DotQ(Left, Right); + + if (Cos_Theta < 0.0f) { /* NOTE(lcf): Take shortest path on Hyper-sphere */ + Cos_Theta = -Cos_Theta; + Right = Q(-Right.X, -Right.Y, -Right.Z, -Right.W); + } + + /* NOTE(lcf): Use Normalized Linear interpolation when vectors are roughly not L.I. */ + if (Cos_Theta > 0.9995f) { + Result = NLerp(Left, Time, Right); + } else { + float Angle = ACosF(Cos_Theta); + float MixLeft = SinF((1.0f - Time) * Angle); + float MixRight = SinF(Time * Angle); + + Result = _MixQ(Left, MixLeft, Right, MixRight); + Result = NormQ(Result); + } + + return Result; +} + +COVERAGE(QToM4, 1) +static inline Mat4 QToM4(Quat Left) +{ + ASSERT_COVERED(QToM4); + + Mat4 Result; + + Quat NormalizedQ = NormQ(Left); + + float XX, YY, ZZ, + XY, XZ, YZ, + WX, WY, WZ; + + XX = NormalizedQ.X * NormalizedQ.X; + YY = NormalizedQ.Y * NormalizedQ.Y; + ZZ = NormalizedQ.Z * NormalizedQ.Z; + XY = NormalizedQ.X * NormalizedQ.Y; + XZ = NormalizedQ.X * NormalizedQ.Z; + YZ = NormalizedQ.Y * NormalizedQ.Z; + WX = NormalizedQ.W * NormalizedQ.X; + WY = NormalizedQ.W * NormalizedQ.Y; + WZ = NormalizedQ.W * NormalizedQ.Z; + + Result.Elements[0][0] = 1.0f - 2.0f * (YY + ZZ); + Result.Elements[0][1] = 2.0f * (XY + WZ); + Result.Elements[0][2] = 2.0f * (XZ - WY); + Result.Elements[0][3] = 0.0f; + + Result.Elements[1][0] = 2.0f * (XY - WZ); + Result.Elements[1][1] = 1.0f - 2.0f * (XX + ZZ); + Result.Elements[1][2] = 2.0f * (YZ + WX); + Result.Elements[1][3] = 0.0f; + + Result.Elements[2][0] = 2.0f * (XZ + WY); + Result.Elements[2][1] = 2.0f * (YZ - WX); + Result.Elements[2][2] = 1.0f - 2.0f * (XX + YY); + Result.Elements[2][3] = 0.0f; + + Result.Elements[3][0] = 0.0f; + Result.Elements[3][1] = 0.0f; + Result.Elements[3][2] = 0.0f; + Result.Elements[3][3] = 1.0f; + + return Result; +} + +// This method taken from Mike Day at Insomniac Games. +// https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2015/01/matrix-to-quat.pdf +// +// Note that as mentioned at the top of the paper, the paper assumes the matrix +// would be *post*-multiplied to a vector to rotate it, meaning the matrix is +// the transpose of what we're dealing with. But, because our matrices are +// stored in column-major order, the indices *appear* to match the paper. +// +// For example, m12 in the paper is row 1, column 2. We need to transpose it to +// row 2, column 1. But, because the column comes first when referencing +// elements, it looks like M.Elements[1][2]. +// +// Don't be confused! Or if you must be confused, at least trust this +// comment. :) +COVERAGE(M4ToQ_RH, 4) +static inline Quat M4ToQ_RH(Mat4 M) +{ + float T; + Quat _Q; + + if (M.Elements[2][2] < 0.0f) { + if (M.Elements[0][0] > M.Elements[1][1]) { + ASSERT_COVERED(M4ToQ_RH); + + T = 1 + M.Elements[0][0] - M.Elements[1][1] - M.Elements[2][2]; + _Q = Q( + T, + M.Elements[0][1] + M.Elements[1][0], + M.Elements[2][0] + M.Elements[0][2], + M.Elements[1][2] - M.Elements[2][1] + ); + } else { + ASSERT_COVERED(M4ToQ_RH); + + T = 1 - M.Elements[0][0] + M.Elements[1][1] - M.Elements[2][2]; + _Q = Q( + M.Elements[0][1] + M.Elements[1][0], + T, + M.Elements[1][2] + M.Elements[2][1], + M.Elements[2][0] - M.Elements[0][2] + ); + } + } else { + if (M.Elements[0][0] < -M.Elements[1][1]) { + ASSERT_COVERED(M4ToQ_RH); + + T = 1 - M.Elements[0][0] - M.Elements[1][1] + M.Elements[2][2]; + _Q = Q( + M.Elements[2][0] + M.Elements[0][2], + M.Elements[1][2] + M.Elements[2][1], + T, + M.Elements[0][1] - M.Elements[1][0] + ); + } else { + ASSERT_COVERED(M4ToQ_RH); + + T = 1 + M.Elements[0][0] + M.Elements[1][1] + M.Elements[2][2]; + _Q = Q( + M.Elements[1][2] - M.Elements[2][1], + M.Elements[2][0] - M.Elements[0][2], + M.Elements[0][1] - M.Elements[1][0], + T + ); + } + } + + _Q = MulQF(_Q, 0.5f / SqrtF(T)); + + return _Q; +} + +COVERAGE(M4ToQ_LH, 4) +static inline Quat M4ToQ_LH(Mat4 M) +{ + float T; + Quat _Q; + + if (M.Elements[2][2] < 0.0f) { + if (M.Elements[0][0] > M.Elements[1][1]) { + ASSERT_COVERED(M4ToQ_LH); + + T = 1 + M.Elements[0][0] - M.Elements[1][1] - M.Elements[2][2]; + _Q = Q( + T, + M.Elements[0][1] + M.Elements[1][0], + M.Elements[2][0] + M.Elements[0][2], + M.Elements[2][1] - M.Elements[1][2] + ); + } else { + ASSERT_COVERED(M4ToQ_LH); + + T = 1 - M.Elements[0][0] + M.Elements[1][1] - M.Elements[2][2]; + _Q = Q( + M.Elements[0][1] + M.Elements[1][0], + T, + M.Elements[1][2] + M.Elements[2][1], + M.Elements[0][2] - M.Elements[2][0] + ); + } + } else { + if (M.Elements[0][0] < -M.Elements[1][1]) { + ASSERT_COVERED(M4ToQ_LH); + + T = 1 - M.Elements[0][0] - M.Elements[1][1] + M.Elements[2][2]; + _Q = Q( + M.Elements[2][0] + M.Elements[0][2], + M.Elements[1][2] + M.Elements[2][1], + T, + M.Elements[1][0] - M.Elements[0][1] + ); + } else { + ASSERT_COVERED(M4ToQ_LH); + + T = 1 + M.Elements[0][0] + M.Elements[1][1] + M.Elements[2][2]; + _Q = Q( + M.Elements[2][1] - M.Elements[1][2], + M.Elements[0][2] - M.Elements[2][0], + M.Elements[1][0] - M.Elements[0][2], + T + ); + } + } + + _Q = MulQF(_Q, 0.5f / SqrtF(T)); + + return _Q; +} + + +COVERAGE(QFromAxisAngle_RH, 1) +static inline Quat QFromAxisAngle_RH(Vec3 Axis, float Angle) +{ + ASSERT_COVERED(QFromAxisAngle_RH); + + Quat Result; + + Vec3 AxisNormalized = NormV3(Axis); + float SineOfRotation = SinF(Angle / 2.0f); + + Result.XYZ = MulV3F(AxisNormalized, SineOfRotation); + Result.W = CosF(Angle / 2.0f); + + return Result; +} + +COVERAGE(QFromAxisAngle_LH, 1) +static inline Quat QFromAxisAngle_LH(Vec3 Axis, float Angle) +{ + ASSERT_COVERED(QFromAxisAngle_LH); + + return QFromAxisAngle_RH(Axis, -Angle); +} + +COVERAGE(QFromNormPair, 1) +static inline Quat QFromNormPair(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(QFromNormPair); + + Quat Result; + + Result.XYZ = Cross(Left, Right); + Result.W = 1.0f + DotV3(Left, Right); + + return NormQ(Result); +} + +COVERAGE(QFromVecPair, 1) +static inline Quat QFromVecPair(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(QFromVecPair); + + return QFromNormPair(NormV3(Left), NormV3(Right)); +} + +COVERAGE(RotateV2, 1) +static inline Vec2 RotateV2(Vec2 V, float Angle) +{ + ASSERT_COVERED(RotateV2) + + float sinA = SinF(Angle); + float cosA = CosF(Angle); + + return V2(V.X * cosA - V.Y * sinA, V.X * sinA + V.Y * cosA); +} + +// implementation from +// https://blog.molecular-matters.com/2013/05/24/a-faster-quaternion-vector-multiplication/ +COVERAGE(RotateV3Q, 1) +static inline Vec3 RotateV3Q(Vec3 V, Quat Q) +{ + ASSERT_COVERED(RotateV3Q); + + Vec3 t = MulV3F(Cross(Q.XYZ, V), 2); + return AddV3(V, AddV3(MulV3F(t, Q.W), Cross(Q.XYZ, t))); +} + +COVERAGE(RotateV3AxisAngle_LH, 1) +static inline Vec3 RotateV3AxisAngle_LH(Vec3 V, Vec3 Axis, float Angle) { + ASSERT_COVERED(RotateV3AxisAngle_LH); + + return RotateV3Q(V, QFromAxisAngle_LH(Axis, Angle)); +} + +COVERAGE(RotateV3AxisAngle_RH, 1) +static inline Vec3 RotateV3AxisAngle_RH(Vec3 V, Vec3 Axis, float Angle) { + ASSERT_COVERED(RotateV3AxisAngle_RH); + + return RotateV3Q(V, QFromAxisAngle_RH(Axis, Angle)); +} + + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus + +COVERAGE(LenV2CPP, 1) +static inline float Len(Vec2 A) +{ + ASSERT_COVERED(LenV2CPP); + return LenV2(A); +} + +COVERAGE(LenV3CPP, 1) +static inline float Len(Vec3 A) +{ + ASSERT_COVERED(LenV3CPP); + return LenV3(A); +} + +COVERAGE(LenV4CPP, 1) +static inline float Len(Vec4 A) +{ + ASSERT_COVERED(LenV4CPP); + return LenV4(A); +} + +COVERAGE(LenSqrV2CPP, 1) +static inline float LenSqr(Vec2 A) +{ + ASSERT_COVERED(LenSqrV2CPP); + return LenSqrV2(A); +} + +COVERAGE(LenSqrV3CPP, 1) +static inline float LenSqr(Vec3 A) +{ + ASSERT_COVERED(LenSqrV3CPP); + return LenSqrV3(A); +} + +COVERAGE(LenSqrV4CPP, 1) +static inline float LenSqr(Vec4 A) +{ + ASSERT_COVERED(LenSqrV4CPP); + return LenSqrV4(A); +} + +COVERAGE(NormV2CPP, 1) +static inline Vec2 Norm(Vec2 A) +{ + ASSERT_COVERED(NormV2CPP); + return NormV2(A); +} + +COVERAGE(NormV3CPP, 1) +static inline Vec3 Norm(Vec3 A) +{ + ASSERT_COVERED(NormV3CPP); + return NormV3(A); +} + +COVERAGE(NormV4CPP, 1) +static inline Vec4 Norm(Vec4 A) +{ + ASSERT_COVERED(NormV4CPP); + return NormV4(A); +} + +COVERAGE(NormQCPP, 1) +static inline Quat Norm(Quat A) +{ + ASSERT_COVERED(NormQCPP); + return NormQ(A); +} + +COVERAGE(DotV2CPP, 1) +static inline float Dot(Vec2 Left, Vec2 VecTwo) +{ + ASSERT_COVERED(DotV2CPP); + return DotV2(Left, VecTwo); +} + +COVERAGE(DotV3CPP, 1) +static inline float Dot(Vec3 Left, Vec3 VecTwo) +{ + ASSERT_COVERED(DotV3CPP); + return DotV3(Left, VecTwo); +} + +COVERAGE(DotV4CPP, 1) +static inline float Dot(Vec4 Left, Vec4 VecTwo) +{ + ASSERT_COVERED(DotV4CPP); + return DotV4(Left, VecTwo); +} + +COVERAGE(LerpV2CPP, 1) +static inline Vec2 Lerp(Vec2 Left, float Time, Vec2 Right) +{ + ASSERT_COVERED(LerpV2CPP); + return LerpV2(Left, Time, Right); +} + +COVERAGE(LerpV3CPP, 1) +static inline Vec3 Lerp(Vec3 Left, float Time, Vec3 Right) +{ + ASSERT_COVERED(LerpV3CPP); + return LerpV3(Left, Time, Right); +} + +COVERAGE(LerpV4CPP, 1) +static inline Vec4 Lerp(Vec4 Left, float Time, Vec4 Right) +{ + ASSERT_COVERED(LerpV4CPP); + return LerpV4(Left, Time, Right); +} + +COVERAGE(TransposeM2CPP, 1) +static inline Mat2 Transpose(Mat2 Matrix) +{ + ASSERT_COVERED(TransposeM2CPP); + return TransposeM2(Matrix); +} + +COVERAGE(TransposeM3CPP, 1) +static inline Mat3 Transpose(Mat3 Matrix) +{ + ASSERT_COVERED(TransposeM3CPP); + return TransposeM3(Matrix); +} + +COVERAGE(TransposeM4CPP, 1) +static inline Mat4 Transpose(Mat4 Matrix) +{ + ASSERT_COVERED(TransposeM4CPP); + return TransposeM4(Matrix); +} + +COVERAGE(DeterminantM2CPP, 1) +static inline float Determinant(Mat2 Matrix) +{ + ASSERT_COVERED(DeterminantM2CPP); + return DeterminantM2(Matrix); +} + +COVERAGE(DeterminantM3CPP, 1) +static inline float Determinant(Mat3 Matrix) +{ + ASSERT_COVERED(DeterminantM3CPP); + return DeterminantM3(Matrix); +} + +COVERAGE(DeterminantM4CPP, 1) +static inline float Determinant(Mat4 Matrix) +{ + ASSERT_COVERED(DeterminantM4CPP); + return DeterminantM4(Matrix); +} + +COVERAGE(InvGeneralM2CPP, 1) +static inline Mat2 InvGeneral(Mat2 Matrix) +{ + ASSERT_COVERED(InvGeneralM2CPP); + return InvGeneralM2(Matrix); +} + +COVERAGE(InvGeneralM3CPP, 1) +static inline Mat3 InvGeneral(Mat3 Matrix) +{ + ASSERT_COVERED(InvGeneralM3CPP); + return InvGeneralM3(Matrix); +} + +COVERAGE(InvGeneralM4CPP, 1) +static inline Mat4 InvGeneral(Mat4 Matrix) +{ + ASSERT_COVERED(InvGeneralM4CPP); + return InvGeneralM4(Matrix); +} + +COVERAGE(DotQCPP, 1) +static inline float Dot(Quat QuatOne, Quat QuatTwo) +{ + ASSERT_COVERED(DotQCPP); + return DotQ(QuatOne, QuatTwo); +} + +COVERAGE(AddV2CPP, 1) +static inline Vec2 Add(Vec2 Left, Vec2 Right) +{ + ASSERT_COVERED(AddV2CPP); + return AddV2(Left, Right); +} + +COVERAGE(AddV3CPP, 1) +static inline Vec3 Add(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(AddV3CPP); + return AddV3(Left, Right); +} + +COVERAGE(AddV4CPP, 1) +static inline Vec4 Add(Vec4 Left, Vec4 Right) +{ + ASSERT_COVERED(AddV4CPP); + return AddV4(Left, Right); +} + +COVERAGE(AddM2CPP, 1) +static inline Mat2 Add(Mat2 Left, Mat2 Right) +{ + ASSERT_COVERED(AddM2CPP); + return AddM2(Left, Right); +} + +COVERAGE(AddM3CPP, 1) +static inline Mat3 Add(Mat3 Left, Mat3 Right) +{ + ASSERT_COVERED(AddM3CPP); + return AddM3(Left, Right); +} + +COVERAGE(AddM4CPP, 1) +static inline Mat4 Add(Mat4 Left, Mat4 Right) +{ + ASSERT_COVERED(AddM4CPP); + return AddM4(Left, Right); +} + +COVERAGE(AddQCPP, 1) +static inline Quat Add(Quat Left, Quat Right) +{ + ASSERT_COVERED(AddQCPP); + return AddQ(Left, Right); +} + +COVERAGE(SubV2CPP, 1) +static inline Vec2 Sub(Vec2 Left, Vec2 Right) +{ + ASSERT_COVERED(SubV2CPP); + return SubV2(Left, Right); +} + +COVERAGE(SubV3CPP, 1) +static inline Vec3 Sub(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(SubV3CPP); + return SubV3(Left, Right); +} + +COVERAGE(SubV4CPP, 1) +static inline Vec4 Sub(Vec4 Left, Vec4 Right) +{ + ASSERT_COVERED(SubV4CPP); + return SubV4(Left, Right); +} + +COVERAGE(SubM2CPP, 1) +static inline Mat2 Sub(Mat2 Left, Mat2 Right) +{ + ASSERT_COVERED(SubM2CPP); + return SubM2(Left, Right); +} + +COVERAGE(SubM3CPP, 1) +static inline Mat3 Sub(Mat3 Left, Mat3 Right) +{ + ASSERT_COVERED(SubM3CPP); + return SubM3(Left, Right); +} + +COVERAGE(SubM4CPP, 1) +static inline Mat4 Sub(Mat4 Left, Mat4 Right) +{ + ASSERT_COVERED(SubM4CPP); + return SubM4(Left, Right); +} + +COVERAGE(SubQCPP, 1) +static inline Quat Sub(Quat Left, Quat Right) +{ + ASSERT_COVERED(SubQCPP); + return SubQ(Left, Right); +} + +COVERAGE(MulV2CPP, 1) +static inline Vec2 Mul(Vec2 Left, Vec2 Right) +{ + ASSERT_COVERED(MulV2CPP); + return MulV2(Left, Right); +} + +COVERAGE(MulV2FCPP, 1) +static inline Vec2 Mul(Vec2 Left, float Right) +{ + ASSERT_COVERED(MulV2FCPP); + return MulV2F(Left, Right); +} + +COVERAGE(MulV3CPP, 1) +static inline Vec3 Mul(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(MulV3CPP); + return MulV3(Left, Right); +} + +COVERAGE(MulV3FCPP, 1) +static inline Vec3 Mul(Vec3 Left, float Right) +{ + ASSERT_COVERED(MulV3FCPP); + return MulV3F(Left, Right); +} + +COVERAGE(MulV4CPP, 1) +static inline Vec4 Mul(Vec4 Left, Vec4 Right) +{ + ASSERT_COVERED(MulV4CPP); + return MulV4(Left, Right); +} + +COVERAGE(MulV4FCPP, 1) +static inline Vec4 Mul(Vec4 Left, float Right) +{ + ASSERT_COVERED(MulV4FCPP); + return MulV4F(Left, Right); +} + +COVERAGE(MulM2CPP, 1) +static inline Mat2 Mul(Mat2 Left, Mat2 Right) +{ + ASSERT_COVERED(MulM2CPP); + return MulM2(Left, Right); +} + +COVERAGE(MulM3CPP, 1) +static inline Mat3 Mul(Mat3 Left, Mat3 Right) +{ + ASSERT_COVERED(MulM3CPP); + return MulM3(Left, Right); +} + +COVERAGE(MulM4CPP, 1) +static inline Mat4 Mul(Mat4 Left, Mat4 Right) +{ + ASSERT_COVERED(MulM4CPP); + return MulM4(Left, Right); +} + +COVERAGE(MulM2FCPP, 1) +static inline Mat2 Mul(Mat2 Left, float Right) +{ + ASSERT_COVERED(MulM2FCPP); + return MulM2F(Left, Right); +} + +COVERAGE(MulM3FCPP, 1) +static inline Mat3 Mul(Mat3 Left, float Right) +{ + ASSERT_COVERED(MulM3FCPP); + return MulM3F(Left, Right); +} + +COVERAGE(MulM4FCPP, 1) +static inline Mat4 Mul(Mat4 Left, float Right) +{ + ASSERT_COVERED(MulM4FCPP); + return MulM4F(Left, Right); +} + +COVERAGE(MulM2V2CPP, 1) +static inline Vec2 Mul(Mat2 Matrix, Vec2 Vector) +{ + ASSERT_COVERED(MulM2V2CPP); + return MulM2V2(Matrix, Vector); +} + +COVERAGE(MulM3V3CPP, 1) +static inline Vec3 Mul(Mat3 Matrix, Vec3 Vector) +{ + ASSERT_COVERED(MulM3V3CPP); + return MulM3V3(Matrix, Vector); +} + +COVERAGE(MulM4V4CPP, 1) +static inline Vec4 Mul(Mat4 Matrix, Vec4 Vector) +{ + ASSERT_COVERED(MulM4V4CPP); + return MulM4V4(Matrix, Vector); +} + +COVERAGE(MulQCPP, 1) +static inline Quat Mul(Quat Left, Quat Right) +{ + ASSERT_COVERED(MulQCPP); + return MulQ(Left, Right); +} + +COVERAGE(MulQFCPP, 1) +static inline Quat Mul(Quat Left, float Right) +{ + ASSERT_COVERED(MulQFCPP); + return MulQF(Left, Right); +} + +COVERAGE(DivV2CPP, 1) +static inline Vec2 Div(Vec2 Left, Vec2 Right) +{ + ASSERT_COVERED(DivV2CPP); + return DivV2(Left, Right); +} + +COVERAGE(DivV2FCPP, 1) +static inline Vec2 Div(Vec2 Left, float Right) +{ + ASSERT_COVERED(DivV2FCPP); + return DivV2F(Left, Right); +} + +COVERAGE(DivV3CPP, 1) +static inline Vec3 Div(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(DivV3CPP); + return DivV3(Left, Right); +} + +COVERAGE(DivV3FCPP, 1) +static inline Vec3 Div(Vec3 Left, float Right) +{ + ASSERT_COVERED(DivV3FCPP); + return DivV3F(Left, Right); +} + +COVERAGE(DivV4CPP, 1) +static inline Vec4 Div(Vec4 Left, Vec4 Right) +{ + ASSERT_COVERED(DivV4CPP); + return DivV4(Left, Right); +} + +COVERAGE(DivV4FCPP, 1) +static inline Vec4 Div(Vec4 Left, float Right) +{ + ASSERT_COVERED(DivV4FCPP); + return DivV4F(Left, Right); +} + +COVERAGE(DivM2FCPP, 1) +static inline Mat2 Div(Mat2 Left, float Right) +{ + ASSERT_COVERED(DivM2FCPP); + return DivM2F(Left, Right); +} + +COVERAGE(DivM3FCPP, 1) +static inline Mat3 Div(Mat3 Left, float Right) +{ + ASSERT_COVERED(DivM3FCPP); + return DivM3F(Left, Right); +} + +COVERAGE(DivM4FCPP, 1) +static inline Mat4 Div(Mat4 Left, float Right) +{ + ASSERT_COVERED(DivM4FCPP); + return DivM4F(Left, Right); +} + +COVERAGE(DivQFCPP, 1) +static inline Quat Div(Quat Left, float Right) +{ + ASSERT_COVERED(DivQFCPP); + return DivQF(Left, Right); +} + +COVERAGE(EqV2CPP, 1) +static inline Bool Eq(Vec2 Left, Vec2 Right) +{ + ASSERT_COVERED(EqV2CPP); + return EqV2(Left, Right); +} + +COVERAGE(EqV3CPP, 1) +static inline Bool Eq(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(EqV3CPP); + return EqV3(Left, Right); +} + +COVERAGE(EqV4CPP, 1) +static inline Bool Eq(Vec4 Left, Vec4 Right) +{ + ASSERT_COVERED(EqV4CPP); + return EqV4(Left, Right); +} + +COVERAGE(AddV2Op, 1) +static inline Vec2 operator+(Vec2 Left, Vec2 Right) +{ + ASSERT_COVERED(AddV2Op); + return AddV2(Left, Right); +} + +COVERAGE(AddV3Op, 1) +static inline Vec3 operator+(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(AddV3Op); + return AddV3(Left, Right); +} + +COVERAGE(AddV4Op, 1) +static inline Vec4 operator+(Vec4 Left, Vec4 Right) +{ + ASSERT_COVERED(AddV4Op); + return AddV4(Left, Right); +} + +COVERAGE(AddM2Op, 1) +static inline Mat2 operator+(Mat2 Left, Mat2 Right) +{ + ASSERT_COVERED(AddM2Op); + return AddM2(Left, Right); +} + +COVERAGE(AddM3Op, 1) +static inline Mat3 operator+(Mat3 Left, Mat3 Right) +{ + ASSERT_COVERED(AddM3Op); + return AddM3(Left, Right); +} + +COVERAGE(AddM4Op, 1) +static inline Mat4 operator+(Mat4 Left, Mat4 Right) +{ + ASSERT_COVERED(AddM4Op); + return AddM4(Left, Right); +} + +COVERAGE(AddQOp, 1) +static inline Quat operator+(Quat Left, Quat Right) +{ + ASSERT_COVERED(AddQOp); + return AddQ(Left, Right); +} + +COVERAGE(SubV2Op, 1) +static inline Vec2 operator-(Vec2 Left, Vec2 Right) +{ + ASSERT_COVERED(SubV2Op); + return SubV2(Left, Right); +} + +COVERAGE(SubV3Op, 1) +static inline Vec3 operator-(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(SubV3Op); + return SubV3(Left, Right); +} + +COVERAGE(SubV4Op, 1) +static inline Vec4 operator-(Vec4 Left, Vec4 Right) +{ + ASSERT_COVERED(SubV4Op); + return SubV4(Left, Right); +} + +COVERAGE(SubM2Op, 1) +static inline Mat2 operator-(Mat2 Left, Mat2 Right) +{ + ASSERT_COVERED(SubM2Op); + return SubM2(Left, Right); +} + +COVERAGE(SubM3Op, 1) +static inline Mat3 operator-(Mat3 Left, Mat3 Right) +{ + ASSERT_COVERED(SubM3Op); + return SubM3(Left, Right); +} + +COVERAGE(SubM4Op, 1) +static inline Mat4 operator-(Mat4 Left, Mat4 Right) +{ + ASSERT_COVERED(SubM4Op); + return SubM4(Left, Right); +} + +COVERAGE(SubQOp, 1) +static inline Quat operator-(Quat Left, Quat Right) +{ + ASSERT_COVERED(SubQOp); + return SubQ(Left, Right); +} + +COVERAGE(MulV2Op, 1) +static inline Vec2 operator*(Vec2 Left, Vec2 Right) +{ + ASSERT_COVERED(MulV2Op); + return MulV2(Left, Right); +} + +COVERAGE(MulV3Op, 1) +static inline Vec3 operator*(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(MulV3Op); + return MulV3(Left, Right); +} + +COVERAGE(MulV4Op, 1) +static inline Vec4 operator*(Vec4 Left, Vec4 Right) +{ + ASSERT_COVERED(MulV4Op); + return MulV4(Left, Right); +} + +COVERAGE(MulM2Op, 1) +static inline Mat2 operator*(Mat2 Left, Mat2 Right) +{ + ASSERT_COVERED(MulM2Op); + return MulM2(Left, Right); +} + +COVERAGE(MulM3Op, 1) +static inline Mat3 operator*(Mat3 Left, Mat3 Right) +{ + ASSERT_COVERED(MulM3Op); + return MulM3(Left, Right); +} + +COVERAGE(MulM4Op, 1) +static inline Mat4 operator*(Mat4 Left, Mat4 Right) +{ + ASSERT_COVERED(MulM4Op); + return MulM4(Left, Right); +} + +COVERAGE(MulQOp, 1) +static inline Quat operator*(Quat Left, Quat Right) +{ + ASSERT_COVERED(MulQOp); + return MulQ(Left, Right); +} + +COVERAGE(MulV2FOp, 1) +static inline Vec2 operator*(Vec2 Left, float Right) +{ + ASSERT_COVERED(MulV2FOp); + return MulV2F(Left, Right); +} + +COVERAGE(MulV3FOp, 1) +static inline Vec3 operator*(Vec3 Left, float Right) +{ + ASSERT_COVERED(MulV3FOp); + return MulV3F(Left, Right); +} + +COVERAGE(MulV4FOp, 1) +static inline Vec4 operator*(Vec4 Left, float Right) +{ + ASSERT_COVERED(MulV4FOp); + return MulV4F(Left, Right); +} + +COVERAGE(MulM2FOp, 1) +static inline Mat2 operator*(Mat2 Left, float Right) +{ + ASSERT_COVERED(MulM2FOp); + return MulM2F(Left, Right); +} + +COVERAGE(MulM3FOp, 1) +static inline Mat3 operator*(Mat3 Left, float Right) +{ + ASSERT_COVERED(MulM3FOp); + return MulM3F(Left, Right); +} + +COVERAGE(MulM4FOp, 1) +static inline Mat4 operator*(Mat4 Left, float Right) +{ + ASSERT_COVERED(MulM4FOp); + return MulM4F(Left, Right); +} + +COVERAGE(MulQFOp, 1) +static inline Quat operator*(Quat Left, float Right) +{ + ASSERT_COVERED(MulQFOp); + return MulQF(Left, Right); +} + +COVERAGE(MulV2FOpLeft, 1) +static inline Vec2 operator*(float Left, Vec2 Right) +{ + ASSERT_COVERED(MulV2FOpLeft); + return MulV2F(Right, Left); +} + +COVERAGE(MulV3FOpLeft, 1) +static inline Vec3 operator*(float Left, Vec3 Right) +{ + ASSERT_COVERED(MulV3FOpLeft); + return MulV3F(Right, Left); +} + +COVERAGE(MulV4FOpLeft, 1) +static inline Vec4 operator*(float Left, Vec4 Right) +{ + ASSERT_COVERED(MulV4FOpLeft); + return MulV4F(Right, Left); +} + +COVERAGE(MulM2FOpLeft, 1) +static inline Mat2 operator*(float Left, Mat2 Right) +{ + ASSERT_COVERED(MulM2FOpLeft); + return MulM2F(Right, Left); +} + +COVERAGE(MulM3FOpLeft, 1) +static inline Mat3 operator*(float Left, Mat3 Right) +{ + ASSERT_COVERED(MulM3FOpLeft); + return MulM3F(Right, Left); +} + +COVERAGE(MulM4FOpLeft, 1) +static inline Mat4 operator*(float Left, Mat4 Right) +{ + ASSERT_COVERED(MulM4FOpLeft); + return MulM4F(Right, Left); +} + +COVERAGE(MulQFOpLeft, 1) +static inline Quat operator*(float Left, Quat Right) +{ + ASSERT_COVERED(MulQFOpLeft); + return MulQF(Right, Left); +} + +COVERAGE(MulM2V2Op, 1) +static inline Vec2 operator*(Mat2 Matrix, Vec2 Vector) +{ + ASSERT_COVERED(MulM2V2Op); + return MulM2V2(Matrix, Vector); +} + +COVERAGE(MulM3V3Op, 1) +static inline Vec3 operator*(Mat3 Matrix, Vec3 Vector) +{ + ASSERT_COVERED(MulM3V3Op); + return MulM3V3(Matrix, Vector); +} + +COVERAGE(MulM4V4Op, 1) +static inline Vec4 operator*(Mat4 Matrix, Vec4 Vector) +{ + ASSERT_COVERED(MulM4V4Op); + return MulM4V4(Matrix, Vector); +} + +COVERAGE(DivV2Op, 1) +static inline Vec2 operator/(Vec2 Left, Vec2 Right) +{ + ASSERT_COVERED(DivV2Op); + return DivV2(Left, Right); +} + +COVERAGE(DivV3Op, 1) +static inline Vec3 operator/(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(DivV3Op); + return DivV3(Left, Right); +} + +COVERAGE(DivV4Op, 1) +static inline Vec4 operator/(Vec4 Left, Vec4 Right) +{ + ASSERT_COVERED(DivV4Op); + return DivV4(Left, Right); +} + +COVERAGE(DivV2FOp, 1) +static inline Vec2 operator/(Vec2 Left, float Right) +{ + ASSERT_COVERED(DivV2FOp); + return DivV2F(Left, Right); +} + +COVERAGE(DivV3FOp, 1) +static inline Vec3 operator/(Vec3 Left, float Right) +{ + ASSERT_COVERED(DivV3FOp); + return DivV3F(Left, Right); +} + +COVERAGE(DivV4FOp, 1) +static inline Vec4 operator/(Vec4 Left, float Right) +{ + ASSERT_COVERED(DivV4FOp); + return DivV4F(Left, Right); +} + +COVERAGE(DivM4FOp, 1) +static inline Mat4 operator/(Mat4 Left, float Right) +{ + ASSERT_COVERED(DivM4FOp); + return DivM4F(Left, Right); +} + +COVERAGE(DivM3FOp, 1) +static inline Mat3 operator/(Mat3 Left, float Right) +{ + ASSERT_COVERED(DivM3FOp); + return DivM3F(Left, Right); +} + +COVERAGE(DivM2FOp, 1) +static inline Mat2 operator/(Mat2 Left, float Right) +{ + ASSERT_COVERED(DivM2FOp); + return DivM2F(Left, Right); +} + +COVERAGE(DivQFOp, 1) +static inline Quat operator/(Quat Left, float Right) +{ + ASSERT_COVERED(DivQFOp); + return DivQF(Left, Right); +} + +COVERAGE(AddV2Assign, 1) +static inline Vec2 &operator+=(Vec2 &Left, Vec2 Right) +{ + ASSERT_COVERED(AddV2Assign); + return Left = Left + Right; +} + +COVERAGE(AddV3Assign, 1) +static inline Vec3 &operator+=(Vec3 &Left, Vec3 Right) +{ + ASSERT_COVERED(AddV3Assign); + return Left = Left + Right; +} + +COVERAGE(AddV4Assign, 1) +static inline Vec4 &operator+=(Vec4 &Left, Vec4 Right) +{ + ASSERT_COVERED(AddV4Assign); + return Left = Left + Right; +} + +COVERAGE(AddM2Assign, 1) +static inline Mat2 &operator+=(Mat2 &Left, Mat2 Right) +{ + ASSERT_COVERED(AddM2Assign); + return Left = Left + Right; +} + +COVERAGE(AddM3Assign, 1) +static inline Mat3 &operator+=(Mat3 &Left, Mat3 Right) +{ + ASSERT_COVERED(AddM3Assign); + return Left = Left + Right; +} + +COVERAGE(AddM4Assign, 1) +static inline Mat4 &operator+=(Mat4 &Left, Mat4 Right) +{ + ASSERT_COVERED(AddM4Assign); + return Left = Left + Right; +} + +COVERAGE(AddQAssign, 1) +static inline Quat &operator+=(Quat &Left, Quat Right) +{ + ASSERT_COVERED(AddQAssign); + return Left = Left + Right; +} + +COVERAGE(SubV2Assign, 1) +static inline Vec2 &operator-=(Vec2 &Left, Vec2 Right) +{ + ASSERT_COVERED(SubV2Assign); + return Left = Left - Right; +} + +COVERAGE(SubV3Assign, 1) +static inline Vec3 &operator-=(Vec3 &Left, Vec3 Right) +{ + ASSERT_COVERED(SubV3Assign); + return Left = Left - Right; +} + +COVERAGE(SubV4Assign, 1) +static inline Vec4 &operator-=(Vec4 &Left, Vec4 Right) +{ + ASSERT_COVERED(SubV4Assign); + return Left = Left - Right; +} + +COVERAGE(SubM2Assign, 1) +static inline Mat2 &operator-=(Mat2 &Left, Mat2 Right) +{ + ASSERT_COVERED(SubM2Assign); + return Left = Left - Right; +} + +COVERAGE(SubM3Assign, 1) +static inline Mat3 &operator-=(Mat3 &Left, Mat3 Right) +{ + ASSERT_COVERED(SubM3Assign); + return Left = Left - Right; +} + +COVERAGE(SubM4Assign, 1) +static inline Mat4 &operator-=(Mat4 &Left, Mat4 Right) +{ + ASSERT_COVERED(SubM4Assign); + return Left = Left - Right; +} + +COVERAGE(SubQAssign, 1) +static inline Quat &operator-=(Quat &Left, Quat Right) +{ + ASSERT_COVERED(SubQAssign); + return Left = Left - Right; +} + +COVERAGE(MulV2Assign, 1) +static inline Vec2 &operator*=(Vec2 &Left, Vec2 Right) +{ + ASSERT_COVERED(MulV2Assign); + return Left = Left * Right; +} + +COVERAGE(MulV3Assign, 1) +static inline Vec3 &operator*=(Vec3 &Left, Vec3 Right) +{ + ASSERT_COVERED(MulV3Assign); + return Left = Left * Right; +} + +COVERAGE(MulV4Assign, 1) +static inline Vec4 &operator*=(Vec4 &Left, Vec4 Right) +{ + ASSERT_COVERED(MulV4Assign); + return Left = Left * Right; +} + +COVERAGE(MulV2FAssign, 1) +static inline Vec2 &operator*=(Vec2 &Left, float Right) +{ + ASSERT_COVERED(MulV2FAssign); + return Left = Left * Right; +} + +COVERAGE(MulV3FAssign, 1) +static inline Vec3 &operator*=(Vec3 &Left, float Right) +{ + ASSERT_COVERED(MulV3FAssign); + return Left = Left * Right; +} + +COVERAGE(MulV4FAssign, 1) +static inline Vec4 &operator*=(Vec4 &Left, float Right) +{ + ASSERT_COVERED(MulV4FAssign); + return Left = Left * Right; +} + +COVERAGE(MulM2FAssign, 1) +static inline Mat2 &operator*=(Mat2 &Left, float Right) +{ + ASSERT_COVERED(MulM2FAssign); + return Left = Left * Right; +} + +COVERAGE(MulM3FAssign, 1) +static inline Mat3 &operator*=(Mat3 &Left, float Right) +{ + ASSERT_COVERED(MulM3FAssign); + return Left = Left * Right; +} + +COVERAGE(MulM4FAssign, 1) +static inline Mat4 &operator*=(Mat4 &Left, float Right) +{ + ASSERT_COVERED(MulM4FAssign); + return Left = Left * Right; +} + +COVERAGE(MulQFAssign, 1) +static inline Quat &operator*=(Quat &Left, float Right) +{ + ASSERT_COVERED(MulQFAssign); + return Left = Left * Right; +} + +COVERAGE(DivV2Assign, 1) +static inline Vec2 &operator/=(Vec2 &Left, Vec2 Right) +{ + ASSERT_COVERED(DivV2Assign); + return Left = Left / Right; +} + +COVERAGE(DivV3Assign, 1) +static inline Vec3 &operator/=(Vec3 &Left, Vec3 Right) +{ + ASSERT_COVERED(DivV3Assign); + return Left = Left / Right; +} + +COVERAGE(DivV4Assign, 1) +static inline Vec4 &operator/=(Vec4 &Left, Vec4 Right) +{ + ASSERT_COVERED(DivV4Assign); + return Left = Left / Right; +} + +COVERAGE(DivV2FAssign, 1) +static inline Vec2 &operator/=(Vec2 &Left, float Right) +{ + ASSERT_COVERED(DivV2FAssign); + return Left = Left / Right; +} + +COVERAGE(DivV3FAssign, 1) +static inline Vec3 &operator/=(Vec3 &Left, float Right) +{ + ASSERT_COVERED(DivV3FAssign); + return Left = Left / Right; +} + +COVERAGE(DivV4FAssign, 1) +static inline Vec4 &operator/=(Vec4 &Left, float Right) +{ + ASSERT_COVERED(DivV4FAssign); + return Left = Left / Right; +} + +COVERAGE(DivM4FAssign, 1) +static inline Mat4 &operator/=(Mat4 &Left, float Right) +{ + ASSERT_COVERED(DivM4FAssign); + return Left = Left / Right; +} + +COVERAGE(DivQFAssign, 1) +static inline Quat &operator/=(Quat &Left, float Right) +{ + ASSERT_COVERED(DivQFAssign); + return Left = Left / Right; +} + +COVERAGE(EqV2Op, 1) +static inline Bool operator==(Vec2 Left, Vec2 Right) +{ + ASSERT_COVERED(EqV2Op); + return EqV2(Left, Right); +} + +COVERAGE(EqV3Op, 1) +static inline Bool operator==(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(EqV3Op); + return EqV3(Left, Right); +} + +COVERAGE(EqV4Op, 1) +static inline Bool operator==(Vec4 Left, Vec4 Right) +{ + ASSERT_COVERED(EqV4Op); + return EqV4(Left, Right); +} + +COVERAGE(EqV2OpNot, 1) +static inline Bool operator!=(Vec2 Left, Vec2 Right) +{ + ASSERT_COVERED(EqV2OpNot); + return !EqV2(Left, Right); +} + +COVERAGE(EqV3OpNot, 1) +static inline Bool operator!=(Vec3 Left, Vec3 Right) +{ + ASSERT_COVERED(EqV3OpNot); + return !EqV3(Left, Right); +} + +COVERAGE(EqV4OpNot, 1) +static inline Bool operator!=(Vec4 Left, Vec4 Right) +{ + ASSERT_COVERED(EqV4OpNot); + return !EqV4(Left, Right); +} + +COVERAGE(UnaryMinusV2, 1) +static inline Vec2 operator-(Vec2 In) +{ + ASSERT_COVERED(UnaryMinusV2); + + Vec2 Result; + Result.X = -In.X; + Result.Y = -In.Y; + + return Result; +} + +COVERAGE(UnaryMinusV3, 1) +static inline Vec3 operator-(Vec3 In) +{ + ASSERT_COVERED(UnaryMinusV3); + + Vec3 Result; + Result.X = -In.X; + Result.Y = -In.Y; + Result.Z = -In.Z; + + return Result; +} + +COVERAGE(UnaryMinusV4, 1) +static inline Vec4 operator-(Vec4 In) +{ + ASSERT_COVERED(UnaryMinusV4); + + Vec4 Result; +#if HANDMADE_MATH__USE_SSE + Result.SSE = _mm_xor_ps(In.SSE, _mm_set1_ps(-0.0f)); +#elif defined(HANDMADE_MATH__USE_NEON) + float32x4_t Zero = vdupq_n_f32(0.0f); + Result.NEON = vsubq_f32(Zero, In.NEON); +#else + Result.X = -In.X; + Result.Y = -In.Y; + Result.Z = -In.Z; + Result.W = -In.W; +#endif + + return Result; +} + +#endif /* __cplusplus*/ + +#ifdef HANDMADE_MATH__USE_C11_GENERICS + +void __invalid_generic(); + +#define Add(A, B) _Generic((A), \ + Vec2: AddV2, \ + Vec3: AddV3, \ + Vec4: AddV4, \ + Mat2: AddM2, \ + Mat3: AddM3, \ + Mat4: AddM4, \ + Quat: AddQ \ +)(A, B) + +#define Sub(A, B) _Generic((A), \ + Vec2: SubV2, \ + Vec3: SubV3, \ + Vec4: SubV4, \ + Mat2: SubM2, \ + Mat3: SubM3, \ + Mat4: SubM4, \ + Quat: SubQ \ +)(A, B) + +#define Mul(A, B) _Generic((B), \ + float: _Generic((A), \ + Vec2: MulV2F, \ + Vec3: MulV3F, \ + Vec4: MulV4F, \ + Mat2: MulM2F, \ + Mat3: MulM3F, \ + Mat4: MulM4F, \ + Quat: MulQF, \ + default: __invalid_generic \ + ), \ + Vec2: _Generic((A), \ + Vec2: MulV2, \ + Mat2: MulM2V2, \ + default: __invalid_generic \ + ), \ + Vec3: _Generic((A), \ + Vec3: MulV3, \ + Mat3: MulM3V3, \ + default: __invalid_generic \ + ), \ + Vec4: _Generic((A), \ + Vec4: MulV4, \ + Mat4: MulM4V4, \ + default: __invalid_generic \ + ), \ + Mat2: MulM2, \ + Mat3: MulM3, \ + Mat4: MulM4, \ + Quat: MulQ \ +)(A, B) + +#define Div(A, B) _Generic((B), \ + float: _Generic((A), \ + Vec2: DivV2F, \ + Vec3: DivV3F, \ + Vec4: DivV4F, \ + Mat2: DivM2F, \ + Mat3: DivM3F, \ + Mat4: DivM4F, \ + Quat: DivQF \ + ), \ + Vec2: DivV2, \ + Vec3: DivV3, \ + Vec4: DivV4 \ +)(A, B) + +#define Len(A) _Generic((A), \ + Vec2: LenV2, \ + Vec3: LenV3, \ + Vec4: LenV4 \ +)(A) + +#define LenSqr(A) _Generic((A), \ + Vec2: LenSqrV2, \ + Vec3: LenSqrV3, \ + Vec4: LenSqrV4 \ +)(A) + +#define Norm(A) _Generic((A), \ + Vec2: NormV2, \ + Vec3: NormV3, \ + Vec4: NormV4, \ + Quat: NormQ \ +)(A) + +#define Dot(A, B) _Generic((A), \ + Vec2: DotV2, \ + Vec3: DotV3, \ + Vec4: DotV4, \ + Quat: DotQ \ +)(A, B) + +#define Lerp(A, T, B) _Generic((A), \ + float: Lerp, \ + Vec2: LerpV2, \ + Vec3: LerpV3, \ + Vec4: LerpV4 \ +)(A, T, B) + +#define Eq(A, B) _Generic((A), \ + Vec2: EqV2, \ + Vec3: EqV3, \ + Vec4: EqV4 \ +)(A, B) + +#define Transpose(M) _Generic((M), \ + Mat2: TransposeM2, \ + Mat3: TransposeM3, \ + Mat4: TransposeM4 \ +)(M) + +#define Determinant(M) _Generic((M), \ + Mat2: DeterminantM2, \ + Mat3: DeterminantM3, \ + Mat4: DeterminantM4 \ +)(M) + +#define InvGeneral(M) _Generic((M), \ + Mat2: InvGeneralM2, \ + Mat3: InvGeneralM3, \ + Mat4: InvGeneralM4 \ +)(M) + +#endif + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#endif /* HANDMADE_MATH_H */ \ No newline at end of file diff --git a/src/rand.h b/src/rand.h new file mode 100644 index 0000000..0ab2f70 --- /dev/null +++ b/src/rand.h @@ -0,0 +1,59 @@ +uint32_t seed; + +uint32_t xorshift32(void); +void rand_seed(uint32_t _seed); +uint32_t next_int(void); +uint32_t next_int_max(uint32_t max); +uint32_t next_int_minmax(uint32_t min, uint32_t max); +float next_float(void); +float next_float_max(float_t max); +float next_float_minmax(float_t min, float_t max); + +uint32_t xorshift32(void) +{ + seed ^= seed<<13; + seed ^= seed>>17; + seed ^= seed<<5; + return seed; +} +void rand_seed(uint32_t _seed) +{ + if(_seed == 0) + return; + + seed = _seed; + xorshift32(); +} +// PRNG [0-UINT32_MAX] +uint32_t next_int(void) +{ + return xorshift32(); +} +// PRNG [0-max] +uint32_t next_int_max(uint32_t max) +{ + return (uint32_t) floorf(xorshift32() / (float) UINT32_MAX * max); +} +// PRNG [min-max] +uint32_t next_int_minmax(uint32_t min, uint32_t max) +{ + const float x = xorshift32() / (float) UINT32_MAX; + //(1.0f - Time) * A + Time * B + return (1.0f - x) * min + x * max; +} +// PRNG [0-1] +float next_float(void) +{ + return xorshift32() / (float) UINT32_MAX; +} +// PRNG [0-max] +float next_float_max(float_t max) +{ + return xorshift32() / (float) UINT32_MAX * max; +} +// PRNG [min-max] +float next_float_minmax(float_t min, float_t max) +{ + const float x = xorshift32() / (float) UINT32_MAX; + return (1.0f - x) * min + x * max; +} \ No newline at end of file diff --git a/src/shaders/base.glsl b/src/shaders/base.glsl index 72411d2..e257d9f 100644 --- a/src/shaders/base.glsl +++ b/src/shaders/base.glsl @@ -1,4 +1,6 @@ @block common +@ctype vec2 position_t; + struct cell { vec2 pos; @@ -23,8 +25,7 @@ layout(location = 1) out vec2 _quad; layout(location = 2) out vec2 _centroid; layout(location = 3) flat out vec3 _color; -layout(binding = 0) readonly buffer vs_ssbo -{ +readonly: layout(binding = 0) readonly buffer vs_ssbo { cell cells[]; }; diff --git a/src/shaders/sprite.wgsl b/src/shaders/sprite.wgsl new file mode 100644 index 0000000..1f550a9 --- /dev/null +++ b/src/shaders/sprite.wgsl @@ -0,0 +1,47 @@ +struct Cell { + position: vec2f, +}; +struct VsI { //Vertex shader input + @builtin(instance_index) instance: u32, + @location(0) position: vec2f, +}; +struct Vs2Fs { //Vertex shader to Fragment shader + @builtin(position) pos: vec4f, + @location(0) @interpolate(flat) instance: u32, +}; +struct FsO { //Fragment shader output + @builtin(frag_depth) depth: f32, + @location(0) color: vec4f, +}; + +fn color(i: u32) -> vec3f +{ + let r: f32 = f32(((i >> 0) & 0xff)); + let g: f32 = f32(((i >> 8) & 0xff)); + let b: f32 = f32(((i >> 16) & 0xff)); + return vec3f(r / 255, g / 255, b / 255); +} + +//@binding(0) @group(0) var mvp: mat4x4f; +@binding(0) @group(1) var cells: array; + +@vertex +fn vs_main(input: VsI) -> Vs2Fs { + var output: Vs2Fs; + + output.pos = vec4f(cells[input.instance].position.xy, 0.0, 0.0)/* * mvp*/; + output.instance = input.instance; + + return output; +} + +@fragment +fn fs_main(input: Vs2Fs) -> FsO { + var output: FsO; + + output.depth = 1.0; + output.color = vec4f(1.0, 0.0, 1.0, 1.0); + //output.color = vec4f(color(input.instance), 1.0); + + return output; +}