From e8869220f5a5b245be1a89106c77f50ec07624fd Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Mon, 7 Dec 2020 18:13:43 -0500 Subject: switched to box2d --- Makefile | 12 +- base.c | 40 ---- base.cpp | 40 ++++ gui.h | 94 ---------- gui.hpp | 94 ++++++++++ main.c | 411 ---------------------------------------- main.cpp | 411 ++++++++++++++++++++++++++++++++++++++++ math.c | 640 --------------------------------------------------------------- math.cpp | 640 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sim.c | 416 ----------------------------------------- sim.cpp | 427 ++++++++++++++++++++++++++++++++++++++++++ sim.h | 144 -------------- sim.hpp | 148 +++++++++++++++ time.c | 65 ------- time.cpp | 65 +++++++ util.c | 175 ----------------- util.cpp | 175 +++++++++++++++++ 17 files changed, 2006 insertions(+), 1991 deletions(-) delete mode 100644 base.c create mode 100644 base.cpp delete mode 100644 gui.h create mode 100644 gui.hpp delete mode 100644 main.c create mode 100644 main.cpp delete mode 100644 math.c create mode 100644 math.cpp delete mode 100644 sim.c create mode 100644 sim.cpp delete mode 100644 sim.h create mode 100644 sim.hpp delete mode 100644 time.c create mode 100644 time.cpp delete mode 100644 util.c create mode 100644 util.cpp diff --git a/Makefile b/Makefile index 4c96a7d..a91771b 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,10 @@ -WARNINGS=-Wall -Wextra -Wshadow -Wconversion -Wpedantic -pedantic -std=gnu11 -Wno-unused-function -Wno-fixed-enum-extension -Wimplicit-fallthrough -LIBS=-lSDL2 -lGL -ldl +WARNINGS=-Wall -Wextra -Wshadow -Wconversion -Wpedantic -pedantic -std=gnu++11 -Wno-unused-function -Wno-fixed-enum-extension -Wimplicit-fallthrough +LIBS=-ldl `pkg-config --libs --cflags box2d sdl2 gl` DEBUG_CFLAGS=$(CFLAGS) $(WARNINGS) $(LIBS) -DDEBUG -O0 -g obj/debug: physics obj/sim.so touch obj/debug -physics: main.c gui.h sim.c time.c - $(CC) main.c -o $@ $(DEBUG_CFLAGS) -obj/sim.so: *.[ch] - $(CC) sim.c -fPIC -shared -o $@ $(DEBUG_CFLAGS) +physics: main.cpp gui.hpp sim.cpp time.cpp + $(CXX) main.cpp -o $@ $(DEBUG_CFLAGS) +obj/sim.so: *.[ch]* + $(CXX) sim.cpp -fPIC -shared -o $@ $(DEBUG_CFLAGS) touch obj/sim.so_changed diff --git a/base.c b/base.c deleted file mode 100644 index adab1ea..0000000 --- a/base.c +++ /dev/null @@ -1,40 +0,0 @@ -// this file includes functions, etc. used just about everywhere - -#if DEBUG -#define logln(...) printf(__VA_ARGS__), printf("\n"); -#else -#define logln(...) -#endif - -// allocates aligned temporary memory -static u8 *tmp_alloc(State *state, size_t bytes) { - u32 used = state->tmp_mem_used; - u32 max_aligns_needed = (u32)(bytes + sizeof(MaxAlign) - 1) / (u32)sizeof(MaxAlign); // = ceil(bytes / sizeof(MaxAlign)) - MaxAlign *ret = state->tmp_mem + used; - if (bytes == 0) { - return NULL; - } - if (used + max_aligns_needed > arr_count(state->tmp_mem)) { - assert(0); - return NULL; - } - state->tmp_mem_used += max_aligns_needed; - memset(ret, 0, bytes); - return (u8 *)ret; -} - -/* -these functions save and restore the state of the temporary memory. -*/ -static u32 tmp_push(State *state) { - return state->tmp_mem_used; -} - -static void tmp_pop(State *state, u32 mark) { - state->tmp_mem_used = mark; -} - -#define tmp_alloc_object(state, type) ((type *)tmp_alloc((state), sizeof(type))) -#define tmp_alloc_arr(state, type, n) ((type *)tmp_alloc((state), (n) * sizeof(type))) -#define calloc_object(type) ((type *)calloc(1, sizeof(type))) -#define calloc_arr(type, n) ((type *)calloc((n), sizeof(type))) diff --git a/base.cpp b/base.cpp new file mode 100644 index 0000000..adab1ea --- /dev/null +++ b/base.cpp @@ -0,0 +1,40 @@ +// this file includes functions, etc. used just about everywhere + +#if DEBUG +#define logln(...) printf(__VA_ARGS__), printf("\n"); +#else +#define logln(...) +#endif + +// allocates aligned temporary memory +static u8 *tmp_alloc(State *state, size_t bytes) { + u32 used = state->tmp_mem_used; + u32 max_aligns_needed = (u32)(bytes + sizeof(MaxAlign) - 1) / (u32)sizeof(MaxAlign); // = ceil(bytes / sizeof(MaxAlign)) + MaxAlign *ret = state->tmp_mem + used; + if (bytes == 0) { + return NULL; + } + if (used + max_aligns_needed > arr_count(state->tmp_mem)) { + assert(0); + return NULL; + } + state->tmp_mem_used += max_aligns_needed; + memset(ret, 0, bytes); + return (u8 *)ret; +} + +/* +these functions save and restore the state of the temporary memory. +*/ +static u32 tmp_push(State *state) { + return state->tmp_mem_used; +} + +static void tmp_pop(State *state, u32 mark) { + state->tmp_mem_used = mark; +} + +#define tmp_alloc_object(state, type) ((type *)tmp_alloc((state), sizeof(type))) +#define tmp_alloc_arr(state, type, n) ((type *)tmp_alloc((state), (n) * sizeof(type))) +#define calloc_object(type) ((type *)calloc(1, sizeof(type))) +#define calloc_arr(type, n) ((type *)calloc((n), sizeof(type))) diff --git a/gui.h b/gui.h deleted file mode 100644 index 9d13303..0000000 --- a/gui.h +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef PLATFORM_H_ -#define PLATFORM_H_ - -#ifdef DEBUG -#undef DEBUG -#define DEBUG 1 -#else -#define DEBUG 0 -#define NDEBUG 1 -#endif - -#include -#include -#include - -#if __STDC_VERSION__ >= 201112 -#include -typedef max_align_t MaxAlign; -#else -typedef union { - long -#if __STDC_VERSION__ >= 199901 || _MSC_VER - long -#endif - a; - long double b; - void *c; - void (*d)(void); -} MaxAlign; -#endif - -typedef unsigned long ulong; -typedef unsigned uint; - -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; -#define U8_MAX 0xff -#define U16_MAX 0xffff -#define U32_MAX 0xffffffff -#define U64_MAX 0xffffffffffffffff - -typedef int8_t i8; -typedef int16_t i16; -typedef int32_t i32; -typedef int64_t i64; -#define I8_MAX 0x7f -#define I16_MAX 0x7fff -#define I32_MAX 0x7fffffff -#define I64_Max 0x7fffffffffffffff - -enum { - KEY_UNKNOWN, - KEY_UP, KEY_LEFT, KEY_RIGHT, KEY_DOWN, - KEY_PAGEDOWN, KEY_PAGEUP, - KEY_SPACE, - KEY_BACKSPACE, - KEY_ESCAPE, - KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, - KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, - KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, - KEY_EXCLAMATION_MARK, KEY_AT, KEY_HASH, KEY_DOLLAR, KEY_PERCENT, - KEY_CARET, KEY_AMPERSAND, KEY_ASTERISK, KEY_LPAREN, KEY_RPAREN, - KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, - KEY_LCTRL, KEY_RCTRL, - KEY_LSHIFT, KEY_RSHIFT, - KEY_ENTER, - KEY_MINUS, KEY_UNDERSCORE, - KEY_EQUALS, KEY_PLUS, - NKEYS -}; -typedef u16 Key; - -typedef struct { - bool closed; // was the window closed? - u8 keys_pressed[NKEYS]; // [i] = how many times was key #i pressed this frame? - u8 keys_released[NKEYS]; // [i] = how many times was key #i released this frame? - bool keys_down[NKEYS]; // [i] = is key #i down? - bool shift, ctrl; -} Input; - -typedef struct { - Input input; - i32 width, height; // window width and height in pixels - bool close; // should the window be closed? default: input.closed - void *memory; - size_t memory_size; - double dt; // time in seconds between this frame and the last one - void (*(*get_gl_proc)(char const *))(void); // get a GL function - char title[64]; // window title -} Frame; - -#endif // PLATFORM_H_ diff --git a/gui.hpp b/gui.hpp new file mode 100644 index 0000000..9d13303 --- /dev/null +++ b/gui.hpp @@ -0,0 +1,94 @@ +#ifndef PLATFORM_H_ +#define PLATFORM_H_ + +#ifdef DEBUG +#undef DEBUG +#define DEBUG 1 +#else +#define DEBUG 0 +#define NDEBUG 1 +#endif + +#include +#include +#include + +#if __STDC_VERSION__ >= 201112 +#include +typedef max_align_t MaxAlign; +#else +typedef union { + long +#if __STDC_VERSION__ >= 199901 || _MSC_VER + long +#endif + a; + long double b; + void *c; + void (*d)(void); +} MaxAlign; +#endif + +typedef unsigned long ulong; +typedef unsigned uint; + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +#define U8_MAX 0xff +#define U16_MAX 0xffff +#define U32_MAX 0xffffffff +#define U64_MAX 0xffffffffffffffff + +typedef int8_t i8; +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; +#define I8_MAX 0x7f +#define I16_MAX 0x7fff +#define I32_MAX 0x7fffffff +#define I64_Max 0x7fffffffffffffff + +enum { + KEY_UNKNOWN, + KEY_UP, KEY_LEFT, KEY_RIGHT, KEY_DOWN, + KEY_PAGEDOWN, KEY_PAGEUP, + KEY_SPACE, + KEY_BACKSPACE, + KEY_ESCAPE, + KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, + KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, + KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, + KEY_EXCLAMATION_MARK, KEY_AT, KEY_HASH, KEY_DOLLAR, KEY_PERCENT, + KEY_CARET, KEY_AMPERSAND, KEY_ASTERISK, KEY_LPAREN, KEY_RPAREN, + KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, + KEY_LCTRL, KEY_RCTRL, + KEY_LSHIFT, KEY_RSHIFT, + KEY_ENTER, + KEY_MINUS, KEY_UNDERSCORE, + KEY_EQUALS, KEY_PLUS, + NKEYS +}; +typedef u16 Key; + +typedef struct { + bool closed; // was the window closed? + u8 keys_pressed[NKEYS]; // [i] = how many times was key #i pressed this frame? + u8 keys_released[NKEYS]; // [i] = how many times was key #i released this frame? + bool keys_down[NKEYS]; // [i] = is key #i down? + bool shift, ctrl; +} Input; + +typedef struct { + Input input; + i32 width, height; // window width and height in pixels + bool close; // should the window be closed? default: input.closed + void *memory; + size_t memory_size; + double dt; // time in seconds between this frame and the last one + void (*(*get_gl_proc)(char const *))(void); // get a GL function + char title[64]; // window title +} Frame; + +#endif // PLATFORM_H_ diff --git a/main.c b/main.c deleted file mode 100644 index aae22c8..0000000 --- a/main.c +++ /dev/null @@ -1,411 +0,0 @@ -#ifdef _WIN32 -#include -#endif -#include "gui.h" -#if DEBUG -typedef void (*SimFrameFn)(Frame *); -#else -#include "sim.c" -#endif -#include "time.c" - -#ifdef _WIN32 -#include -#else -#include -#endif -#include -#include -#include - -#if __unix__ -#include -#include -#include -#include -#elif defined _WIN32 -#else -#error "Unsupported operating system." -#endif - -#if DEBUG -#if __unix__ -#define debug_log printf -#else // __unix__ -static void debug_log(char const *fmt, ...) { - char buf[256]; - va_list args; - va_start(args, fmt); - vsprintf_s(buf, sizeof buf, fmt, args); - va_end(args); - OutputDebugStringA(buf); - OutputDebugStringA("\n"); -} -#endif // __unix__ -#else // DEBUG -#define debug_log(...) -#endif - -static void die(char const *fmt, ...) { - char buf[256] = {0}; - - va_list args; - va_start(args, fmt); - vsnprintf(buf, sizeof buf - 1, fmt, args); - va_end(args); - - // show a message box, and if that fails, print to stderr - if (SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", buf, NULL) < 0) { - fprintf(stderr, "%s\n", buf); - } - - exit(EXIT_FAILURE); -} - -// returns true on success -static bool copy_file(char const *filename_from, char const *filename_to) { - bool success = false; - FILE *from = fopen(filename_from, "rb"); - if (from) { - FILE *to = fopen(filename_to, "wb"); - if (to) { - char buf[4096]; - success = true; - while (fread(buf, 1, sizeof buf, from) == sizeof buf) { - if (fwrite(buf, 1, sizeof buf, to) != sizeof buf) { - success = false; - } - } - fwrite(buf, 1, sizeof buf, to); - if (ferror(from) || ferror(to)) { - success = false; - } - fclose(to); - } - fclose(from); - } - return success; -} - -static Key keycode_to_key(SDL_Keycode sym) { - switch (sym) { - case SDLK_LEFT: return KEY_LEFT; - case SDLK_RIGHT: return KEY_RIGHT; - case SDLK_UP: return KEY_UP; - case SDLK_DOWN: return KEY_DOWN; - case SDLK_SPACE: return KEY_SPACE; - case SDLK_LCTRL: return KEY_LCTRL; - case SDLK_RCTRL: return KEY_RCTRL; - case SDLK_LSHIFT: return KEY_LSHIFT; - case SDLK_RSHIFT: return KEY_RSHIFT; - case SDLK_RETURN: return KEY_ENTER; - case SDLK_BACKSPACE: return KEY_BACKSPACE; - case SDLK_ESCAPE: return KEY_ESCAPE; - case SDLK_PAGEDOWN: return KEY_PAGEDOWN; - case SDLK_PAGEUP: return KEY_PAGEUP; - case SDLK_EXCLAIM: return KEY_EXCLAMATION_MARK; - case SDLK_AT: return KEY_AT; - case SDLK_HASH: return KEY_HASH; - case SDLK_DOLLAR: return KEY_DOLLAR; - case SDLK_PERCENT: return KEY_PERCENT; - case SDLK_CARET: return KEY_CARET; - case SDLK_AMPERSAND: return KEY_AMPERSAND; - case SDLK_ASTERISK: return KEY_ASTERISK; - case SDLK_LEFTPAREN: return KEY_LPAREN; - case SDLK_RIGHTPAREN: return KEY_RPAREN; - case SDLK_KP_0: return KEY_0; - case SDLK_PLUS: return KEY_PLUS; - case SDLK_MINUS: return KEY_MINUS; - case SDLK_EQUALS: return KEY_EQUALS; - case SDLK_UNDERSCORE: return KEY_UNDERSCORE; - default: - if (sym >= SDLK_a && sym <= SDLK_z) { - return (u16)(sym - SDLK_a + KEY_A); - } else if (sym >= SDLK_0 && sym <= SDLK_9) { - return (u16)(sym - SDLK_0 + KEY_0); - } else if (sym >= SDLK_KP_1 && sym <= SDLK_KP_9) { - return (u16)(sym - SDLK_KP_1 + KEY_1); - } else if (sym >= SDLK_F1 && sym <= SDLK_F12) { - return (u16)(sym - SDLK_F1 + KEY_F1); - } - } - return KEY_UNKNOWN; -} - -static SDL_Scancode key_to_scancode(Key key, bool *shift) { - *shift = false; - switch (key) { - case KEY_LEFT: return SDL_SCANCODE_LEFT; - case KEY_RIGHT: return SDL_SCANCODE_RIGHT; - case KEY_UP: return SDL_SCANCODE_UP; - case KEY_DOWN: return SDL_SCANCODE_DOWN; - case KEY_SPACE: return SDL_SCANCODE_SPACE; - case KEY_LCTRL: return SDL_SCANCODE_LCTRL; - case KEY_RCTRL: return SDL_SCANCODE_RCTRL; - case KEY_LSHIFT: return SDL_SCANCODE_LSHIFT; - case KEY_RSHIFT: return SDL_SCANCODE_RSHIFT; - case KEY_ENTER: return SDL_SCANCODE_RETURN; - case KEY_BACKSPACE: return SDL_SCANCODE_BACKSPACE; - case KEY_ESCAPE: return SDL_SCANCODE_ESCAPE; - case KEY_PAGEDOWN: return SDL_SCANCODE_PAGEDOWN; - case KEY_PAGEUP: return SDL_SCANCODE_PAGEUP; - case KEY_MINUS: return SDL_SCANCODE_MINUS; - case KEY_PLUS: *shift = true; return SDL_SCANCODE_MINUS; - case KEY_EQUALS: return SDL_SCANCODE_EQUALS; - case KEY_UNDERSCORE: *shift = true; return SDL_SCANCODE_EQUALS; - - case KEY_EXCLAMATION_MARK: *shift = true; return SDL_SCANCODE_1; - case KEY_AT: *shift = true; return SDL_SCANCODE_2; - case KEY_HASH: *shift = true; return SDL_SCANCODE_3; - case KEY_DOLLAR: *shift = true; return SDL_SCANCODE_4; - case KEY_PERCENT: *shift = true; return SDL_SCANCODE_5; - case KEY_CARET: *shift = true; return SDL_SCANCODE_6; - case KEY_AMPERSAND: *shift = true; return SDL_SCANCODE_7; - case KEY_ASTERISK: *shift = true; return SDL_SCANCODE_8; - case KEY_LPAREN: *shift = true; return SDL_SCANCODE_9; - case KEY_RPAREN: *shift = true; return SDL_SCANCODE_0; - - case KEY_0: return SDL_SCANCODE_0; // SDL_SCANCODE_0 != SDL_SCANCODE_1 - 1 - default: - if (key >= KEY_A && key <= KEY_Z) { - return SDL_SCANCODE_A + (key - KEY_A); - } else if (key >= KEY_1 && key <= KEY_9) { - return SDL_SCANCODE_1 + (key - KEY_1); - } else if (key >= KEY_F1 && key <= KEY_F12) { - return SDL_SCANCODE_F1 + (key - KEY_F1); - } - break; - } - return 0; -} - -#ifdef _WIN32 -int WinMain( - HINSTANCE hInstance, - HINSTANCE hPrevInstance, - LPSTR lpCmdLine, - int nShowCmd -) { - (void)hInstance; - (void)hPrevInstance; - (void)lpCmdLine; - (void)nShowCmd; -#else -int main(void) { -#endif - Frame frame = {0}; - Input *input = &frame.input; -#if DEBUG - struct timespec dynlib_last_modified = {0}; - SimFrameFn sim_frame = NULL; - #if __unix__ - void *dynlib = NULL; - #else - HMODULE dynlib = NULL; - #endif -#endif - - srand((unsigned)time(NULL)); - - SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1"); - if (SDL_Init(SDL_INIT_VIDEO) < 0) { - die("%s", SDL_GetError()); - } - - SDL_Window *window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - 720, 720, SDL_WINDOW_SHOWN|SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); - if (!window) { - die("%s", SDL_GetError()); - } - - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - SDL_GLContext *glctx = SDL_GL_CreateContext(window); - if (!glctx) { - die("%s", SDL_GetError()); - } - - SDL_GL_SetSwapInterval(1); // vsync - - frame.memory_size = (size_t)16 << 20; - -#if DEBUG - { - void *address = (void *)(13ULL << 28); - #if __unix__ - frame.memory = mmap(address, frame.memory_size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (frame.memory == MAP_FAILED) { - frame.memory = NULL; - } - #else - frame.memory = VirtualAlloc(address, frame.memory_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - #endif - } -#else - frame.memory = calloc(1, frame.memory_size); -#endif - - if (!frame.memory) { - die("Couldn't allocate memory (%lu bytes).", (ulong)frame.memory_size); - } - - frame.get_gl_proc = (void (*(*)(char const *))(void))SDL_GL_GetProcAddress; - - Uint32 last_frame_ticks = SDL_GetTicks(); - - while (1) { - SDL_Event event; - memset(input, 0, sizeof *input); - while (SDL_PollEvent(&event)) { - switch (event.type) { - case SDL_KEYDOWN: { - Key key = keycode_to_key(event.key.keysym.sym); - ++input->keys_pressed[key]; - } break; - case SDL_KEYUP: { - Key key = keycode_to_key(event.key.keysym.sym); - ++input->keys_released[key]; - } break; - case SDL_QUIT: - input->closed = true; - break; - } - } - - frame.close = input->closed; - - #if DEBUG - #if __unix__ - #define DYNLIB_EXT "so" - #else - #define DYNLIB_EXT "dll" - #endif - struct timespec new_dynlib_last_modified = time_last_modified("obj/sim." DYNLIB_EXT "_changed"); - if (timespec_cmp(dynlib_last_modified, new_dynlib_last_modified) != 0) { - // reload dynlib - char new_filename[256] = {0}; - snprintf(new_filename, sizeof new_filename-1, "obj/sim%08x%08x." DYNLIB_EXT, rand(), rand()); - copy_file("obj/sim." DYNLIB_EXT, new_filename); - - #if __unix__ - chmod(new_filename, 0755); - void *new_dynlib = dlopen(new_filename, RTLD_NOW); - - if (new_dynlib) { - SimFrameFn new_sim_frame = (SimFrameFn)dlsym(new_dynlib, "sim_frame"); - if (new_sim_frame) { - // function loaded from dynamic library successfully - if (dynlib) dlclose(dynlib); - debug_log("Successfully loaded %s\n", new_filename); - sim_frame = new_sim_frame; - dynlib = new_dynlib; - // delete other .so files - { - DIR *obj = opendir("obj"); - if (obj) { - struct dirent *ent; - while ((ent = readdir(obj))) { - if (ent->d_type == DT_REG) { - char *name = ent->d_name; - char *ext = strrchr(name, '.'); - if (ext && strcmp(ext, ".so") == 0 && strcmp(name, "sim.so") != 0 && strcmp(name, new_filename + 4) != 0) { - char path[256] = {0}; - snprintf(path, sizeof path - 1, "obj/%s", name); - debug_log("Deleting old file %s.\n", path); - unlink(path); - } - } - } - closedir(obj); - } - } - } else { - debug_log("Couldn't get sim_frame from dynamic library: %s\n", dlerror()); - } - } else { - debug_log("Error opening dynamic library %s: %s\n", new_filename, dlerror()); - } - - #else - HMODULE new_dynlib = LoadLibraryA(new_filename); - if (new_dynlib) { - SimFrameFn new_sim_frame = (SimFrameFn)GetProcAddress(new_dynlib, "sim_frame"); - if (new_sim_frame) { - // function loaded from dynamic librayr successfully - if (dynlib) FreeLibrary(dynlib); - debug_log("Successfully loaded %s.\n", new_filename); - sim_frame = new_sim_frame; - dynlib = new_dynlib; - // delete other .dll files - { - WIN32_FIND_DATA find_data; - HANDLE find = FindFirstFileA("obj\\*.dll", &find_data); - if (find) { - do { - char const *filename = find_data.cFileName; - if (strcmp(filename, "sim.dll") != 0 && strcmp(filename, "sim.dll_changed") != 0 && strcmp(filename, new_filename) != 0) { - char path[256] = {0}; - snprintf(path, sizeof path-1, "obj/%s", filename); - debug_log("Deleting old dll: %s\n", path); - DeleteFileA(path); - } - } while (FindNextFile(find, &find_data) != 0); - FindClose(find); - } else { - debug_log("Error finding dll files: %ld\n", (long)GetLastError()); - } - } - } else { - FreeLibrary(new_dynlib); - debug_log("Couldn't get sim_frame from dynamic library: %ld\n", (long)GetLastError()); - } - } else { - debug_log("Error opening dynamic library %s: %ld\n", new_filename, (long)GetLastError()); - } - #endif - dynlib_last_modified = new_dynlib_last_modified; - } - #endif - - { - Uint8 const *kbd = SDL_GetKeyboardState(NULL); - bool *keys_down = input->keys_down; - bool shift = input->shift = keys_down[KEY_LSHIFT] || keys_down[KEY_RSHIFT]; - input->ctrl = keys_down[KEY_LCTRL] || keys_down[KEY_RCTRL]; - for (Key i = 0; i < NKEYS; ++i) { - bool needs_shift; - SDL_Scancode scan = key_to_scancode(i, &needs_shift); - keys_down[i] = kbd[scan]; - if (needs_shift) { - keys_down[i] &= shift; - } - } - } - - { - int w = 0, h = 0; - SDL_GetWindowSize(window, &w, &h); - frame.width = w; - frame.height = h; - } - - { - Uint32 this_frame_ticks = SDL_GetTicks(); - Uint32 frame_ms = 0; - if (this_frame_ticks > last_frame_ticks) // SDL_GetTicks wraps after 49 days - frame_ms = this_frame_ticks - last_frame_ticks; - frame.dt = 0.001 * (double)frame_ms; - last_frame_ticks = this_frame_ticks; - } - - sim_frame(&frame); - - SDL_SetWindowTitle(window, frame.title); - - if (frame.close) break; - - SDL_GL_SwapWindow(window); - } - return 0; -} diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..9365a48 --- /dev/null +++ b/main.cpp @@ -0,0 +1,411 @@ +#ifdef _WIN32 +#include +#endif +#include "gui.hpp" +#if DEBUG +typedef void (*SimFrameFn)(Frame *); +#else +#include "sim.cpp" +#endif +#include "time.cpp" + +#ifdef _WIN32 +#include +#else +#include +#endif +#include +#include +#include + +#if __unix__ +#include +#include +#include +#include +#elif defined _WIN32 +#else +#error "Unsupported operating system." +#endif + +#if DEBUG +#if __unix__ +#define debug_log printf +#else // __unix__ +static void debug_log(char const *fmt, ...) { + char buf[256]; + va_list args; + va_start(args, fmt); + vsprintf_s(buf, sizeof buf, fmt, args); + va_end(args); + OutputDebugStringA(buf); + OutputDebugStringA("\n"); +} +#endif // __unix__ +#else // DEBUG +#define debug_log(...) +#endif + +static void die(char const *fmt, ...) { + char buf[256] = {0}; + + va_list args; + va_start(args, fmt); + vsnprintf(buf, sizeof buf - 1, fmt, args); + va_end(args); + + // show a message box, and if that fails, print to stderr + if (SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", buf, NULL) < 0) { + fprintf(stderr, "%s\n", buf); + } + + exit(EXIT_FAILURE); +} + +// returns true on success +static bool copy_file(char const *filename_from, char const *filename_to) { + bool success = false; + FILE *from = fopen(filename_from, "rb"); + if (from) { + FILE *to = fopen(filename_to, "wb"); + if (to) { + char buf[4096]; + success = true; + while (fread(buf, 1, sizeof buf, from) == sizeof buf) { + if (fwrite(buf, 1, sizeof buf, to) != sizeof buf) { + success = false; + } + } + fwrite(buf, 1, sizeof buf, to); + if (ferror(from) || ferror(to)) { + success = false; + } + fclose(to); + } + fclose(from); + } + return success; +} + +static Key keycode_to_key(SDL_Keycode sym) { + switch (sym) { + case SDLK_LEFT: return KEY_LEFT; + case SDLK_RIGHT: return KEY_RIGHT; + case SDLK_UP: return KEY_UP; + case SDLK_DOWN: return KEY_DOWN; + case SDLK_SPACE: return KEY_SPACE; + case SDLK_LCTRL: return KEY_LCTRL; + case SDLK_RCTRL: return KEY_RCTRL; + case SDLK_LSHIFT: return KEY_LSHIFT; + case SDLK_RSHIFT: return KEY_RSHIFT; + case SDLK_RETURN: return KEY_ENTER; + case SDLK_BACKSPACE: return KEY_BACKSPACE; + case SDLK_ESCAPE: return KEY_ESCAPE; + case SDLK_PAGEDOWN: return KEY_PAGEDOWN; + case SDLK_PAGEUP: return KEY_PAGEUP; + case SDLK_EXCLAIM: return KEY_EXCLAMATION_MARK; + case SDLK_AT: return KEY_AT; + case SDLK_HASH: return KEY_HASH; + case SDLK_DOLLAR: return KEY_DOLLAR; + case SDLK_PERCENT: return KEY_PERCENT; + case SDLK_CARET: return KEY_CARET; + case SDLK_AMPERSAND: return KEY_AMPERSAND; + case SDLK_ASTERISK: return KEY_ASTERISK; + case SDLK_LEFTPAREN: return KEY_LPAREN; + case SDLK_RIGHTPAREN: return KEY_RPAREN; + case SDLK_KP_0: return KEY_0; + case SDLK_PLUS: return KEY_PLUS; + case SDLK_MINUS: return KEY_MINUS; + case SDLK_EQUALS: return KEY_EQUALS; + case SDLK_UNDERSCORE: return KEY_UNDERSCORE; + default: + if (sym >= SDLK_a && sym <= SDLK_z) { + return (u16)(sym - SDLK_a + KEY_A); + } else if (sym >= SDLK_0 && sym <= SDLK_9) { + return (u16)(sym - SDLK_0 + KEY_0); + } else if (sym >= SDLK_KP_1 && sym <= SDLK_KP_9) { + return (u16)(sym - SDLK_KP_1 + KEY_1); + } else if (sym >= SDLK_F1 && sym <= SDLK_F12) { + return (u16)(sym - SDLK_F1 + KEY_F1); + } + } + return KEY_UNKNOWN; +} + +static SDL_Scancode key_to_scancode(Key key, bool *shift) { + *shift = false; + switch (key) { + case KEY_LEFT: return SDL_SCANCODE_LEFT; + case KEY_RIGHT: return SDL_SCANCODE_RIGHT; + case KEY_UP: return SDL_SCANCODE_UP; + case KEY_DOWN: return SDL_SCANCODE_DOWN; + case KEY_SPACE: return SDL_SCANCODE_SPACE; + case KEY_LCTRL: return SDL_SCANCODE_LCTRL; + case KEY_RCTRL: return SDL_SCANCODE_RCTRL; + case KEY_LSHIFT: return SDL_SCANCODE_LSHIFT; + case KEY_RSHIFT: return SDL_SCANCODE_RSHIFT; + case KEY_ENTER: return SDL_SCANCODE_RETURN; + case KEY_BACKSPACE: return SDL_SCANCODE_BACKSPACE; + case KEY_ESCAPE: return SDL_SCANCODE_ESCAPE; + case KEY_PAGEDOWN: return SDL_SCANCODE_PAGEDOWN; + case KEY_PAGEUP: return SDL_SCANCODE_PAGEUP; + case KEY_MINUS: return SDL_SCANCODE_MINUS; + case KEY_PLUS: *shift = true; return SDL_SCANCODE_MINUS; + case KEY_EQUALS: return SDL_SCANCODE_EQUALS; + case KEY_UNDERSCORE: *shift = true; return SDL_SCANCODE_EQUALS; + + case KEY_EXCLAMATION_MARK: *shift = true; return SDL_SCANCODE_1; + case KEY_AT: *shift = true; return SDL_SCANCODE_2; + case KEY_HASH: *shift = true; return SDL_SCANCODE_3; + case KEY_DOLLAR: *shift = true; return SDL_SCANCODE_4; + case KEY_PERCENT: *shift = true; return SDL_SCANCODE_5; + case KEY_CARET: *shift = true; return SDL_SCANCODE_6; + case KEY_AMPERSAND: *shift = true; return SDL_SCANCODE_7; + case KEY_ASTERISK: *shift = true; return SDL_SCANCODE_8; + case KEY_LPAREN: *shift = true; return SDL_SCANCODE_9; + case KEY_RPAREN: *shift = true; return SDL_SCANCODE_0; + + case KEY_0: return SDL_SCANCODE_0; // SDL_SCANCODE_0 != SDL_SCANCODE_1 - 1 + default: + if (key >= KEY_A && key <= KEY_Z) { + return (SDL_Scancode)(SDL_SCANCODE_A + (key - KEY_A)); + } else if (key >= KEY_1 && key <= KEY_9) { + return (SDL_Scancode)(SDL_SCANCODE_1 + (key - KEY_1)); + } else if (key >= KEY_F1 && key <= KEY_F12) { + return (SDL_Scancode)(SDL_SCANCODE_F1 + (key - KEY_F1)); + } + break; + } + return (SDL_Scancode)0; +} + +#ifdef _WIN32 +int WinMain( + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nShowCmd +) { + (void)hInstance; + (void)hPrevInstance; + (void)lpCmdLine; + (void)nShowCmd; +#else +int main(void) { +#endif + Frame frame = {}; + Input *input = &frame.input; +#if DEBUG + struct timespec dynlib_last_modified = {}; + SimFrameFn sim_frame = NULL; + #if __unix__ + void *dynlib = NULL; + #else + HMODULE dynlib = NULL; + #endif +#endif + + srand((unsigned)time(NULL)); + + SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1"); + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + die("%s", SDL_GetError()); + } + + SDL_Window *window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + 720, 720, SDL_WINDOW_SHOWN|SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); + if (!window) { + die("%s", SDL_GetError()); + } + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + SDL_GLContext glctx = SDL_GL_CreateContext(window); + if (!glctx) { + die("%s", SDL_GetError()); + } + + SDL_GL_SetSwapInterval(1); // vsync + + frame.memory_size = (size_t)16 << 20; + +#if DEBUG + { + void *address = (void *)(13ULL << 28); + #if __unix__ + frame.memory = mmap(address, frame.memory_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (frame.memory == MAP_FAILED) { + frame.memory = NULL; + } + #else + frame.memory = VirtualAlloc(address, frame.memory_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + #endif + } +#else + frame.memory = calloc(1, frame.memory_size); +#endif + + if (!frame.memory) { + die("Couldn't allocate memory (%lu bytes).", (ulong)frame.memory_size); + } + + frame.get_gl_proc = (void (*(*)(char const *))(void))SDL_GL_GetProcAddress; + + Uint32 last_frame_ticks = SDL_GetTicks(); + + while (1) { + SDL_Event event; + memset(input, 0, sizeof *input); + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_KEYDOWN: { + Key key = keycode_to_key(event.key.keysym.sym); + ++input->keys_pressed[key]; + } break; + case SDL_KEYUP: { + Key key = keycode_to_key(event.key.keysym.sym); + ++input->keys_released[key]; + } break; + case SDL_QUIT: + input->closed = true; + break; + } + } + + frame.close = input->closed; + + #if DEBUG + #if __unix__ + #define DYNLIB_EXT "so" + #else + #define DYNLIB_EXT "dll" + #endif + struct timespec new_dynlib_last_modified = time_last_modified("obj/sim." DYNLIB_EXT "_changed"); + if (timespec_cmp(dynlib_last_modified, new_dynlib_last_modified) != 0) { + // reload dynlib + char new_filename[256] = {0}; + snprintf(new_filename, sizeof new_filename-1, "obj/sim%08x%08x." DYNLIB_EXT, rand(), rand()); + copy_file("obj/sim." DYNLIB_EXT, new_filename); + + #if __unix__ + chmod(new_filename, 0755); + void *new_dynlib = dlopen(new_filename, RTLD_NOW); + + if (new_dynlib) { + SimFrameFn new_sim_frame = (SimFrameFn)dlsym(new_dynlib, "sim_frame"); + if (new_sim_frame) { + // function loaded from dynamic library successfully + if (dynlib) dlclose(dynlib); + debug_log("Successfully loaded %s\n", new_filename); + sim_frame = new_sim_frame; + dynlib = new_dynlib; + // delete other .so files + { + DIR *obj = opendir("obj"); + if (obj) { + struct dirent *ent; + while ((ent = readdir(obj))) { + if (ent->d_type == DT_REG) { + char *name = ent->d_name; + char *ext = strrchr(name, '.'); + if (ext && strcmp(ext, ".so") == 0 && strcmp(name, "sim.so") != 0 && strcmp(name, new_filename + 4) != 0) { + char path[256] = {0}; + snprintf(path, sizeof path - 1, "obj/%s", name); + debug_log("Deleting old file %s.\n", path); + unlink(path); + } + } + } + closedir(obj); + } + } + } else { + debug_log("Couldn't get sim_frame from dynamic library: %s\n", dlerror()); + } + } else { + debug_log("Error opening dynamic library %s: %s\n", new_filename, dlerror()); + } + + #else + HMODULE new_dynlib = LoadLibraryA(new_filename); + if (new_dynlib) { + SimFrameFn new_sim_frame = (SimFrameFn)GetProcAddress(new_dynlib, "sim_frame"); + if (new_sim_frame) { + // function loaded from dynamic librayr successfully + if (dynlib) FreeLibrary(dynlib); + debug_log("Successfully loaded %s.\n", new_filename); + sim_frame = new_sim_frame; + dynlib = new_dynlib; + // delete other .dll files + { + WIN32_FIND_DATA find_data; + HANDLE find = FindFirstFileA("obj\\*.dll", &find_data); + if (find) { + do { + char const *filename = find_data.cFileName; + if (strcmp(filename, "sim.dll") != 0 && strcmp(filename, "sim.dll_changed") != 0 && strcmp(filename, new_filename) != 0) { + char path[256] = {0}; + snprintf(path, sizeof path-1, "obj/%s", filename); + debug_log("Deleting old dll: %s\n", path); + DeleteFileA(path); + } + } while (FindNextFile(find, &find_data) != 0); + FindClose(find); + } else { + debug_log("Error finding dll files: %ld\n", (long)GetLastError()); + } + } + } else { + FreeLibrary(new_dynlib); + debug_log("Couldn't get sim_frame from dynamic library: %ld\n", (long)GetLastError()); + } + } else { + debug_log("Error opening dynamic library %s: %ld\n", new_filename, (long)GetLastError()); + } + #endif + dynlib_last_modified = new_dynlib_last_modified; + } + #endif + + { + Uint8 const *kbd = SDL_GetKeyboardState(NULL); + bool *keys_down = input->keys_down; + bool shift = input->shift = keys_down[KEY_LSHIFT] || keys_down[KEY_RSHIFT]; + input->ctrl = keys_down[KEY_LCTRL] || keys_down[KEY_RCTRL]; + for (Key i = 0; i < NKEYS; ++i) { + bool needs_shift; + SDL_Scancode scan = key_to_scancode(i, &needs_shift); + keys_down[i] = kbd[scan]; + if (needs_shift) { + keys_down[i] &= shift; + } + } + } + + { + int w = 0, h = 0; + SDL_GetWindowSize(window, &w, &h); + frame.width = w; + frame.height = h; + } + + { + Uint32 this_frame_ticks = SDL_GetTicks(); + Uint32 frame_ms = 0; + if (this_frame_ticks > last_frame_ticks) // SDL_GetTicks wraps after 49 days + frame_ms = this_frame_ticks - last_frame_ticks; + frame.dt = 0.001 * (double)frame_ms; + last_frame_ticks = this_frame_ticks; + } + + sim_frame(&frame); + + SDL_SetWindowTitle(window, frame.title); + + if (frame.close) break; + + SDL_GL_SwapWindow(window); + } + return 0; +} diff --git a/math.c b/math.c deleted file mode 100644 index 79aed51..0000000 --- a/math.c +++ /dev/null @@ -1,640 +0,0 @@ -#ifdef MATH_GL -#undef MATH_GL -#define MATH_GL 1 -#endif - -#include -#include -#include - -#define PIf 3.14159265358979f -#define HALF_PIf 1.5707963267948966f -#define TAUf 6.283185307179586f -#define SQRT2f 1.4142135623730951f -#define HALF_SQRT2f 0.7071067811865476f -#define SQRT3f 1.7320508075688772f -#define HALF_SQRT3f 0.8660254037844386f - -#include - -static float degrees(float r) { - return r * (180.0f / PIf); -} -static float radians(float r) { - return r * (PIf / 180.f); -} - -// map x from the interval [0, 1] to the interval [a, b]. does NOT clamp. -static float lerpf(float x, float a, float b) { - return x * (b-a) + a; -} - -// opposite of lerp; map x from the interval [a, b] to the interval [0, 1]. does NOT clamp. -static float normf(float x, float a, float b) { - return (x-a) / (b-a); -} - -static float clampf(float x, float a, float b) { - if (x < a) return a; - if (x > b) return b; - return x; -} - -static int clampi(int x, int a, int b) { - if (x < a) return a; - if (x > b) return b; - return x; -} - -static i32 clampi32(i32 x, i32 a, i32 b) { - if (x < a) return a; - if (x > b) return b; - return x; -} - -// remap x from the interval [from_a, from_b] to the interval [to_a, to_b], NOT clamping if x is outside the "from" interval. -static float remapf(float x, float from_a, float from_b, float to_a, float to_b) { - float pos = (x - from_a) / (from_b - from_a); - return lerpf(pos, to_a, to_b); -} - -static float minf(float a, float b) { - return a < b ? a : b; -} - -static float maxf(float a, float b) { - return a > b ? a : b; -} - -static float smoothstepf(float x) { - if (x <= 0) return 0; - if (x >= 1) return 1; - return x * x * (3 - 2 * x); -} - -static float randf(void) { - return (float)rand() / (float)((ulong)RAND_MAX + 1); -} - -static u32 rand_u32(void) { - return ((u32)rand() & 0xfff) - | ((u32)rand() & 0xfff) << 12 - | ((u32)rand() & 0xff) << 24; -} - -static float rand_uniform(float from, float to) { - return lerpf(randf(), from, to); -} - -static float sigmoidf(float x) { - return 1.0f / (1.0f + expf(-x)); -} - -// returns ⌈x/y⌉ (x/y rounded up) -static i32 ceildivi32(i32 x, i32 y) { - if (y < 0) { - // negating both operands doesn't change the answer - x = -x; - y = -y; - } - if (x < 0) { - // truncation is the same as ceiling for negative numbers - return x / y; - } else { - return (x + (y-1)) / y; - } -} - -typedef struct { - float x, y; -} v2; - -static v2 const v2_zero = {0, 0}; -static v2 V2(float x, float y) { - v2 v; - v.x = x; - v.y = y; - return v; -} - -static v2 v2_add(v2 a, v2 b) { - return V2(a.x + b.x, a.y + b.y); -} - -static v2 v2_add_const(v2 a, float c) { - return V2(a.x + c, a.y + c); -} - -static v2 v2_sub(v2 a, v2 b) { - return V2(a.x - b.x, a.y - b.y); -} - -static v2 v2_scale(v2 v, float s) { - return V2(v.x * s, v.y * s); -} - -static v2 v2_mul(v2 a, v2 b) { - return V2(a.x * b.x, a.y * b.y); -} - -static float v2_dot(v2 a, v2 b) { - return a.x * b.x + a.y * b.y; -} - -static float v2_len(v2 v) { - return sqrtf(v2_dot(v, v)); -} - -static v2 v2_lerp(float x, v2 a, v2 b) { - return V2(lerpf(x, a.x, b.x), lerpf(x, a.y, b.y)); -} - -static v2 v2_normalize(v2 v) { - float len = v2_len(v); - float mul = len == 0.0f ? 1.0f : 1.0f/len; - return v2_scale(v, mul); -} - -static float v2_dist(v2 a, v2 b) { - return v2_len(v2_sub(a, b)); -} - -static float v2_dist_squared(v2 a, v2 b) { - v2 diff = v2_sub(a, b); - return v2_dot(diff, diff); -} - -static void v2_print(v2 v) { - printf("(%f, %f)\n", v.x, v.y); -} - -#if MATH_GL -static void v2_gl_vertex(v2 v) { - glVertex2f(v.x, v.y); -} -#endif - -static v2 v2_rand_unit(void) { - float theta = rand_uniform(0, TAUf); - return V2(cosf(theta), sinf(theta)); -} - -#if MATH_GL -static void v2_rand_unit_test(void) { - int i; - glColor3f(1.0f, 0.0f, 0.0f); - glBegin(GL_POINTS); - for (i = 0; i < 100000; ++i) { - v2_gl_vertex(v2_rand_unit()); - } - glEnd(); -} -#endif - -static v2 v2_polar(float r, float theta) { - return V2(r * cosf(theta), r * sinf(theta)); -} - -typedef struct { - float x, y, z; -} v3; - -static v3 const v3_zero = {0, 0, 0}; - -static v3 V3(float x, float y, float z) { - v3 v; - v.x = x; - v.y = y; - v.z = z; - return v; -} - -#if MATH_GL -static void v3_gl_vertex(v3 v) { - glVertex3f(v.x, v.y, v.z); -} - -static void v3_gl_color(v3 v) { - glColor3f(v.x, v.y, v.z); -} - -static void v3_gl_color_alpha(v3 v, float alpha) { - glColor4f(v.x, v.y, v.z, alpha); -} -#endif - -static v3 v3_add(v3 a, v3 b) { - return V3(a.x + b.x, a.y + b.y, a.z + b.z); -} - -static v3 v3_sub(v3 a, v3 b) { - return V3(a.x - b.x, a.y - b.y, a.z - b.z); -} - -static v3 v3_scale(v3 v, float s) { - return V3(v.x * s, v.y * s, v.z * s); -} - -static v3 v3_lerp(float x, v3 a, v3 b) { - return V3(lerpf(x, a.x, b.x), lerpf(x, a.y, b.y), lerpf(x, a.z, b.z)); -} - -static float v3_dot(v3 u, v3 v) { - return u.x*v.x + u.y*v.y + u.z*v.z; -} - -static v3 v3_cross(v3 u, v3 v) { - v3 prod = V3(u.y*v.z - u.z*v.y, u.z*v.x - u.x*v.z, u.x*v.y - u.y*v.x); - return prod; -} - -static float v3_len(v3 v) { - return sqrtf(v3_dot(v, v)); -} - -static float v3_dist(v3 a, v3 b) { - return v3_len(v3_sub(a, b)); -} - -static float v3_dist_squared(v3 a, v3 b) { - v3 diff = v3_sub(a, b); - return v3_dot(diff, diff); -} - -static v3 v3_normalize(v3 v) { - float len = v3_len(v); - float mul = len == 0.0f ? 1.0f : 1.0f/len; - return v3_scale(v, mul); -} - -// a point on a unit sphere -static v3 v3_on_sphere(float yaw, float pitch) { - return V3(cosf(yaw) * cosf(pitch), sinf(pitch), sinf(yaw) * cosf(pitch)); -} - -static void v3_print(v3 v) { - printf("(%f, %f, %f)\n", v.x, v.y, v.z); -} - -static v3 v3_rand(void) { - return V3(randf(), randf(), randf()); -} - -static v3 v3_rand_unit(void) { - /* - monte carlo method - keep generating random points in cube of radius 1 (width 2) centered at origin, - until you get a point in the unit sphere, then extend it to find the point lying - on the sphere. - */ - while (1) { - v3 v = V3(rand_uniform(-1.0f, +1.0f), rand_uniform(-1.0f, +1.0f), rand_uniform(-1.0f, +1.0f)); - float dist_squared_to_origin = v3_dot(v, v); - if (dist_squared_to_origin <= 1 && dist_squared_to_origin != 0.0f) { - return v3_scale(v, 1.0f / sqrtf(dist_squared_to_origin)); - } - } - return V3(0, 0, 0); -} - -#if MATH_GL -static void v3_rand_unit_test(void) { - int i; - glColor3f(1.0f, 0.0f, 0.0f); - glBegin(GL_POINTS); - for (i = 0; i < 100000; ++i) { - v3_gl_vertex(v3_rand_unit()); - } - glEnd(); -} -#endif - -typedef struct { - float x, y, z, w; -} v4; - -static v4 const v4_zero = {0, 0, 0, 0}; - -static v4 V4(float x, float y, float z, float w) { - v4 v; - v.x = x; - v.y = y; - v.z = z; - v.w = w; - return v; -} - -#if MATH_GL -static void v4_gl_color(v4 v) { - glColor4f(v.x, v.y, v.z, v.w); -} -#endif - -static v4 v4_add(v4 a, v4 b) { - return V4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); -} - -static v4 v4_sub(v4 a, v4 b) { - return V4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); -} - -static v4 v4_scale(v4 v, float s) { - return V4(v.x * s, v.y * s, v.z * s, v.w * s); -} - -static v4 v4_scale_xyz(v4 v, float s) { - return V4(v.x * s, v.y * s, v.z * s, v.w); -} - -static v4 v4_lerp(float x, v4 a, v4 b) { - return V4(lerpf(x, a.x, b.x), lerpf(x, a.y, b.y), lerpf(x, a.z, b.z), lerpf(x, a.w, b.w)); -} - -static float v4_dot(v4 u, v4 v) { - return u.x*v.x + u.y*v.y + u.z*v.z + u.w*v.w; -} - -// create a new vector by multiplying the respective components of u and v -static v4 v4_mul(v4 u, v4 v) { - return V4(u.x * v.x, u.y * v.y, u.z * v.z, u.w * v.w); -} - -static float v4_len(v4 v) { - return sqrtf(v4_dot(v, v)); -} - -static v4 v4_normalize(v4 v) { - float len = v4_len(v); - float mul = len == 0.0f ? 1.0f : 1.0f/len; - return v4_scale(v, mul); -} - -static v3 v4_xyz(v4 v) { - return V3(v.x, v.y, v.z); -} - -static v4 v4_rand(void) { - return V4(randf(), randf(), randf(), randf()); -} - -static void v4_print(v4 v) { - printf("(%f, %f, %f, %f)\n", v.x, v.y, v.z, v.w); -} - - -// matrices are column-major, because that's what they are in OpenGL -typedef struct { - float e[16]; -} m4; - -static m4 const m4_identity = {{ - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 -}}; - -static void m4_print(m4 m) { - int i; - for (i = 0; i < 4; ++i) - printf("[ %f %f %f %f ]\n", m.e[i], m.e[i+4], m.e[i+8], m.e[i+12]); - printf("\n"); -} - -static m4 M4( - float a, float b, float c, float d, - float e, float f, float g, float h, - float i, float j, float k, float l, - float m, float n, float o, float p) { - m4 ret; - float *x = ret.e; - x[0] = a; x[4] = b; x[ 8] = c; x[12] = d; - x[1] = e; x[5] = f; x[ 9] = g; x[13] = h; - x[2] = i; x[6] = j; x[10] = k; x[14] = l; - x[3] = m; x[7] = n; x[11] = o; x[15] = p; - return ret; -} - -// see https://en.wikipedia.org/wiki/Rotation_matrix#General_rotations -static m4 m4_yaw(float yaw) { - float c = cosf(yaw), s = sinf(yaw); - return M4( - c, 0, -s, 0, - 0, 1, 0, 0, - s, 0, c, 0, - 0, 0, 0, 1 - ); -} - -static m4 m4_pitch(float pitch) { - float c = cosf(pitch), s = sinf(pitch); - return M4( - 1, 0, 0, 0, - 0, c, -s, 0, - 0, s, c, 0, - 0, 0, 0, 1 - ); -} - -// https://en.wikipedia.org/wiki/Translation_(geometry) -static m4 m4_translate(v3 t) { - return M4( - 1, 0, 0, t.x, - 0, 1, 0, t.y, - 0, 0, 1, t.z, - 0, 0, 0, 1 - ); -} - -// multiply m by [v.x, v.y, v.z, 1] -static v3 m4_mul_v3(m4 m, v3 v) { - return v3_add(v3_scale(V3(m.e[0], m.e[1], m.e[2]), v.x), v3_add(v3_scale(V3(m.e[4], m.e[5], m.e[6]), v.y), - v3_add(v3_scale(V3(m.e[8], m.e[9], m.e[10]), v.z), V3(m.e[12], m.e[13], m.e[14])))); -} - -/* -4x4 perspective matrix. -fov - field of view in radians, aspect - width:height aspect ratio, z_near/z_far - clipping planes -math stolen from gluPerspective (https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml) -*/ -static m4 m4_perspective(float fov, float aspect, float z_near, float z_far) { - float f = 1.0f / tanf(fov / 2.0f); - return M4( - f/aspect, 0, 0, 0, - 0, f, 0, 0, - 0, 0, (z_far+z_near) / (z_near-z_far), (2.0f*z_far*z_near) / (z_near-z_far), - 0, 0, -1, 0 - ); -} - -// windows.h defines near and far, so let's not use those -static m4 m4_ortho(float left, float right, float bottom, float top, float near_, float far_) { - float tx = -(right + left)/(right - left); - float ty = -(top + bottom)/(top - bottom); - float tz = -(far_ + near_)/(far_ - near_); - return M4( - 2.0f / (right - left), 0, 0, tx, - 0, 2.0f / (top - bottom), 0, ty, - 0, 0, -2.0f / (far_ - near_), tz, - 0, 0, 0, 1 - ); -} - - -static m4 m4_mul(m4 a, m4 b) { - m4 prod = {0}; - int i, j; - float *x = prod.e; - for (i = 0; i < 4; ++i) { - for (j = 0; j < 4; ++j, ++x) { - float *as = &a.e[j]; - float *bs = &b.e[4*i]; - *x = as[0]*bs[0] + as[4]*bs[1] + as[8]*bs[2] + as[12]*bs[3]; - } - } - return prod; -} - -typedef struct { - int x, y; -} v2i; - -static v2i V2I(int x, int y) { - v2i v; - v.x = x; - v.y = y; - return v; -} - -static bool rect_contains_point_v2(v2 pos, v2 size, v2 point) { - float x1 = pos.x, y1 = pos.y, x2 = pos.x + size.x, y2 = pos.y + size.y, - x = point.x, y = point.y; - return x >= x1 && x < x2 && y >= y1 && y < y2; -} - -static bool centered_rect_contains_point(v2 center, v2 size, v2 point) { - return rect_contains_point_v2(v2_sub(center, v2_scale(size, 0.5f)), size, point); -} - -typedef struct { - v2 pos, size; -} Rect; - -static Rect rect(v2 pos, v2 size) { - Rect r; - r.pos = pos; - r.size = size; - return r; -} - -static Rect rect4(float x1, float y1, float x2, float y2) { - assert(x2 >= x1); - assert(y2 >= y1); - return rect(V2(x1,y1), V2(x2-x1, y2-y1)); -} - -static Rect rect_centered(v2 center, v2 size) { - Rect r; - r.pos = v2_sub(center, v2_scale(size, 0.5f)); - r.size = size; - return r; -} - -static v2 rect_center(Rect r) { - return v2_add(r.pos, v2_scale(r.size, 0.5f)); -} - -static bool rect_contains_point(Rect r, v2 point) { - return rect_contains_point_v2(r.pos, r.size, point); -} - -static float rect_x1(Rect r) { return r.pos.x; } -static float rect_y1(Rect r) { return r.pos.y; } -static float rect_x2(Rect r) { return r.pos.x + r.size.x; } -static float rect_y2(Rect r) { return r.pos.y + r.size.y; } - -static void rect_coords(Rect r, float *x1, float *y1, float *x2, float *y2) { - *x1 = r.pos.x; - *y1 = r.pos.y; - *x2 = r.pos.x + r.size.x; - *y2 = r.pos.y + r.size.y; -} - -static void rect_print(Rect r) { - printf("Position: (%f, %f), Size: (%f, %f)\n", r.pos.x, r.pos.y, r.size.x, r.size.y); -} - -#if MATH_GL -// must be rendering GL_QUADS to use these functions! - -static void rect_render(Rect r) { - float x1 = r.pos.x, y1 = r.pos.y, x2 = x1 + r.size.x, y2 = y1 + r.size.y; - glVertex2f(x1, y1); - glVertex2f(x2, y1); - glVertex2f(x2, y2); - glVertex2f(x1, y2); -} - -static void rect_render_border(Rect r, float border_radius) { - float x1 = r.pos.x, y1 = r.pos.y, x2 = x1 + r.size.x, y2 = y1 + r.size.y; - //float a = 0.3f; // for debugging - - //glColor4f(1,0,0,a); - glVertex2f(x1+border_radius, y1-border_radius); - glVertex2f(x1+border_radius, y1+border_radius); - glVertex2f(x2+border_radius, y1+border_radius); - glVertex2f(x2+border_radius, y1-border_radius); - - //glColor4f(0,1,0,a); - glVertex2f(x1-border_radius, y2-border_radius); - glVertex2f(x1-border_radius, y2+border_radius); - glVertex2f(x2-border_radius, y2+border_radius); - glVertex2f(x2-border_radius, y2-border_radius); - - //glColor4f(0,0,1,a); - glVertex2f(x1-border_radius, y1-border_radius); - glVertex2f(x1+border_radius, y1-border_radius); - glVertex2f(x1+border_radius, y2-border_radius); - glVertex2f(x1-border_radius, y2-border_radius); - - //glColor4f(1,1,0,a); - glVertex2f(x2-border_radius, y1+border_radius); - glVertex2f(x2+border_radius, y1+border_radius); - glVertex2f(x2+border_radius, y2+border_radius); - glVertex2f(x2-border_radius, y2+border_radius); -} - - -/* - gl grayscale color - i am tired of not having this -*/ -static void gl_color1f(float v) { - glColor3f(v,v,v); -} - -/* - gl grayscale+alpha color -*/ -static void gl_color2f(float v, float a) { - glColor4f(v,v,v,a); -} - -// color is 0xRRGGBBAA -static void gl_rgbacolor(u32 color) { - glColor4ub((color >> 24) & 0xff, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); -} - -// color is 0xRRGGBB -static void gl_rgbcolor(u32 color) { - gl_rgbacolor((color << 8) | 0xff); -} - -static void gl_quad(float x1, float y1, float x2, float y2) { - glVertex2f(x1, y1); - glVertex2f(x2, y1); - glVertex2f(x2, y2); - glVertex2f(x1, y2); -} -#endif - diff --git a/math.cpp b/math.cpp new file mode 100644 index 0000000..25c5917 --- /dev/null +++ b/math.cpp @@ -0,0 +1,640 @@ +#ifdef MATH_GL +#undef MATH_GL +#define MATH_GL 1 +#endif + +#include +#include +#include + +#define PIf 3.14159265358979f +#define HALF_PIf 1.5707963267948966f +#define TAUf 6.283185307179586f +#define SQRT2f 1.4142135623730951f +#define HALF_SQRT2f 0.7071067811865476f +#define SQRT3f 1.7320508075688772f +#define HALF_SQRT3f 0.8660254037844386f + +#include + +static float degrees(float r) { + return r * (180.0f / PIf); +} +static float radians(float r) { + return r * (PIf / 180.f); +} + +// map x from the interval [0, 1] to the interval [a, b]. does NOT clamp. +static float lerpf(float x, float a, float b) { + return x * (b-a) + a; +} + +// opposite of lerp; map x from the interval [a, b] to the interval [0, 1]. does NOT clamp. +static float normf(float x, float a, float b) { + return (x-a) / (b-a); +} + +static float clampf(float x, float a, float b) { + if (x < a) return a; + if (x > b) return b; + return x; +} + +static int clampi(int x, int a, int b) { + if (x < a) return a; + if (x > b) return b; + return x; +} + +static i32 clampi32(i32 x, i32 a, i32 b) { + if (x < a) return a; + if (x > b) return b; + return x; +} + +// remap x from the interval [from_a, from_b] to the interval [to_a, to_b], NOT clamping if x is outside the "from" interval. +static float remapf(float x, float from_a, float from_b, float to_a, float to_b) { + float pos = (x - from_a) / (from_b - from_a); + return lerpf(pos, to_a, to_b); +} + +static float minf(float a, float b) { + return a < b ? a : b; +} + +static float maxf(float a, float b) { + return a > b ? a : b; +} + +static float smoothstepf(float x) { + if (x <= 0) return 0; + if (x >= 1) return 1; + return x * x * (3 - 2 * x); +} + +static float randf(void) { + return (float)rand() / (float)((ulong)RAND_MAX + 1); +} + +static u32 rand_u32(void) { + return ((u32)rand() & 0xfff) + | ((u32)rand() & 0xfff) << 12 + | ((u32)rand() & 0xff) << 24; +} + +static float rand_uniform(float from, float to) { + return lerpf(randf(), from, to); +} + +static float sigmoidf(float x) { + return 1.0f / (1.0f + expf(-x)); +} + +// returns ⌈x/y⌉ (x/y rounded up) +static i32 ceildivi32(i32 x, i32 y) { + if (y < 0) { + // negating both operands doesn't change the answer + x = -x; + y = -y; + } + if (x < 0) { + // truncation is the same as ceiling for negative numbers + return x / y; + } else { + return (x + (y-1)) / y; + } +} + +typedef struct { + float x, y; +} v2; + +static v2 const v2_zero = {0, 0}; +static v2 V2(float x, float y) { + v2 v; + v.x = x; + v.y = y; + return v; +} + +static v2 v2_add(v2 a, v2 b) { + return V2(a.x + b.x, a.y + b.y); +} + +static v2 v2_add_const(v2 a, float c) { + return V2(a.x + c, a.y + c); +} + +static v2 v2_sub(v2 a, v2 b) { + return V2(a.x - b.x, a.y - b.y); +} + +static v2 v2_scale(v2 v, float s) { + return V2(v.x * s, v.y * s); +} + +static v2 v2_mul(v2 a, v2 b) { + return V2(a.x * b.x, a.y * b.y); +} + +static float v2_dot(v2 a, v2 b) { + return a.x * b.x + a.y * b.y; +} + +static float v2_len(v2 v) { + return sqrtf(v2_dot(v, v)); +} + +static v2 v2_lerp(float x, v2 a, v2 b) { + return V2(lerpf(x, a.x, b.x), lerpf(x, a.y, b.y)); +} + +static v2 v2_normalize(v2 v) { + float len = v2_len(v); + float mul = len == 0.0f ? 1.0f : 1.0f/len; + return v2_scale(v, mul); +} + +static float v2_dist(v2 a, v2 b) { + return v2_len(v2_sub(a, b)); +} + +static float v2_dist_squared(v2 a, v2 b) { + v2 diff = v2_sub(a, b); + return v2_dot(diff, diff); +} + +static void v2_print(v2 v) { + printf("(%f, %f)\n", v.x, v.y); +} + +#if MATH_GL +static void v2_gl_vertex(v2 v) { + glVertex2f(v.x, v.y); +} +#endif + +static v2 v2_rand_unit(void) { + float theta = rand_uniform(0, TAUf); + return V2(cosf(theta), sinf(theta)); +} + +#if MATH_GL +static void v2_rand_unit_test(void) { + int i; + glColor3f(1.0f, 0.0f, 0.0f); + glBegin(GL_POINTS); + for (i = 0; i < 100000; ++i) { + v2_gl_vertex(v2_rand_unit()); + } + glEnd(); +} +#endif + +static v2 v2_polar(float r, float theta) { + return V2(r * cosf(theta), r * sinf(theta)); +} + +typedef struct { + float x, y, z; +} v3; + +static v3 const v3_zero = {0, 0, 0}; + +static v3 V3(float x, float y, float z) { + v3 v; + v.x = x; + v.y = y; + v.z = z; + return v; +} + +#if MATH_GL +static void v3_gl_vertex(v3 v) { + glVertex3f(v.x, v.y, v.z); +} + +static void v3_gl_color(v3 v) { + glColor3f(v.x, v.y, v.z); +} + +static void v3_gl_color_alpha(v3 v, float alpha) { + glColor4f(v.x, v.y, v.z, alpha); +} +#endif + +static v3 v3_add(v3 a, v3 b) { + return V3(a.x + b.x, a.y + b.y, a.z + b.z); +} + +static v3 v3_sub(v3 a, v3 b) { + return V3(a.x - b.x, a.y - b.y, a.z - b.z); +} + +static v3 v3_scale(v3 v, float s) { + return V3(v.x * s, v.y * s, v.z * s); +} + +static v3 v3_lerp(float x, v3 a, v3 b) { + return V3(lerpf(x, a.x, b.x), lerpf(x, a.y, b.y), lerpf(x, a.z, b.z)); +} + +static float v3_dot(v3 u, v3 v) { + return u.x*v.x + u.y*v.y + u.z*v.z; +} + +static v3 v3_cross(v3 u, v3 v) { + v3 prod = V3(u.y*v.z - u.z*v.y, u.z*v.x - u.x*v.z, u.x*v.y - u.y*v.x); + return prod; +} + +static float v3_len(v3 v) { + return sqrtf(v3_dot(v, v)); +} + +static float v3_dist(v3 a, v3 b) { + return v3_len(v3_sub(a, b)); +} + +static float v3_dist_squared(v3 a, v3 b) { + v3 diff = v3_sub(a, b); + return v3_dot(diff, diff); +} + +static v3 v3_normalize(v3 v) { + float len = v3_len(v); + float mul = len == 0.0f ? 1.0f : 1.0f/len; + return v3_scale(v, mul); +} + +// a point on a unit sphere +static v3 v3_on_sphere(float yaw, float pitch) { + return V3(cosf(yaw) * cosf(pitch), sinf(pitch), sinf(yaw) * cosf(pitch)); +} + +static void v3_print(v3 v) { + printf("(%f, %f, %f)\n", v.x, v.y, v.z); +} + +static v3 v3_rand(void) { + return V3(randf(), randf(), randf()); +} + +static v3 v3_rand_unit(void) { + /* + monte carlo method + keep generating random points in cube of radius 1 (width 2) centered at origin, + until you get a point in the unit sphere, then extend it to find the point lying + on the sphere. + */ + while (1) { + v3 v = V3(rand_uniform(-1.0f, +1.0f), rand_uniform(-1.0f, +1.0f), rand_uniform(-1.0f, +1.0f)); + float dist_squared_to_origin = v3_dot(v, v); + if (dist_squared_to_origin <= 1 && dist_squared_to_origin != 0.0f) { + return v3_scale(v, 1.0f / sqrtf(dist_squared_to_origin)); + } + } + return V3(0, 0, 0); +} + +#if MATH_GL +static void v3_rand_unit_test(void) { + int i; + glColor3f(1.0f, 0.0f, 0.0f); + glBegin(GL_POINTS); + for (i = 0; i < 100000; ++i) { + v3_gl_vertex(v3_rand_unit()); + } + glEnd(); +} +#endif + +typedef struct { + float x, y, z, w; +} v4; + +static v4 const v4_zero = {0, 0, 0, 0}; + +static v4 V4(float x, float y, float z, float w) { + v4 v; + v.x = x; + v.y = y; + v.z = z; + v.w = w; + return v; +} + +#if MATH_GL +static void v4_gl_color(v4 v) { + glColor4f(v.x, v.y, v.z, v.w); +} +#endif + +static v4 v4_add(v4 a, v4 b) { + return V4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); +} + +static v4 v4_sub(v4 a, v4 b) { + return V4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); +} + +static v4 v4_scale(v4 v, float s) { + return V4(v.x * s, v.y * s, v.z * s, v.w * s); +} + +static v4 v4_scale_xyz(v4 v, float s) { + return V4(v.x * s, v.y * s, v.z * s, v.w); +} + +static v4 v4_lerp(float x, v4 a, v4 b) { + return V4(lerpf(x, a.x, b.x), lerpf(x, a.y, b.y), lerpf(x, a.z, b.z), lerpf(x, a.w, b.w)); +} + +static float v4_dot(v4 u, v4 v) { + return u.x*v.x + u.y*v.y + u.z*v.z + u.w*v.w; +} + +// create a new vector by multiplying the respective components of u and v +static v4 v4_mul(v4 u, v4 v) { + return V4(u.x * v.x, u.y * v.y, u.z * v.z, u.w * v.w); +} + +static float v4_len(v4 v) { + return sqrtf(v4_dot(v, v)); +} + +static v4 v4_normalize(v4 v) { + float len = v4_len(v); + float mul = len == 0.0f ? 1.0f : 1.0f/len; + return v4_scale(v, mul); +} + +static v3 v4_xyz(v4 v) { + return V3(v.x, v.y, v.z); +} + +static v4 v4_rand(void) { + return V4(randf(), randf(), randf(), randf()); +} + +static void v4_print(v4 v) { + printf("(%f, %f, %f, %f)\n", v.x, v.y, v.z, v.w); +} + + +// matrices are column-major, because that's what they are in OpenGL +typedef struct { + float e[16]; +} m4; + +static m4 const m4_identity = {{ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 +}}; + +static void m4_print(m4 m) { + int i; + for (i = 0; i < 4; ++i) + printf("[ %f %f %f %f ]\n", m.e[i], m.e[i+4], m.e[i+8], m.e[i+12]); + printf("\n"); +} + +static m4 M4( + float a, float b, float c, float d, + float e, float f, float g, float h, + float i, float j, float k, float l, + float m, float n, float o, float p) { + m4 ret; + float *x = ret.e; + x[0] = a; x[4] = b; x[ 8] = c; x[12] = d; + x[1] = e; x[5] = f; x[ 9] = g; x[13] = h; + x[2] = i; x[6] = j; x[10] = k; x[14] = l; + x[3] = m; x[7] = n; x[11] = o; x[15] = p; + return ret; +} + +// see https://en.wikipedia.org/wiki/Rotation_matrix#General_rotations +static m4 m4_yaw(float yaw) { + float c = cosf(yaw), s = sinf(yaw); + return M4( + c, 0, -s, 0, + 0, 1, 0, 0, + s, 0, c, 0, + 0, 0, 0, 1 + ); +} + +static m4 m4_pitch(float pitch) { + float c = cosf(pitch), s = sinf(pitch); + return M4( + 1, 0, 0, 0, + 0, c, -s, 0, + 0, s, c, 0, + 0, 0, 0, 1 + ); +} + +// https://en.wikipedia.org/wiki/Translation_(geometry) +static m4 m4_translate(v3 t) { + return M4( + 1, 0, 0, t.x, + 0, 1, 0, t.y, + 0, 0, 1, t.z, + 0, 0, 0, 1 + ); +} + +// multiply m by [v.x, v.y, v.z, 1] +static v3 m4_mul_v3(m4 m, v3 v) { + return v3_add(v3_scale(V3(m.e[0], m.e[1], m.e[2]), v.x), v3_add(v3_scale(V3(m.e[4], m.e[5], m.e[6]), v.y), + v3_add(v3_scale(V3(m.e[8], m.e[9], m.e[10]), v.z), V3(m.e[12], m.e[13], m.e[14])))); +} + +/* +4x4 perspective matrix. +fov - field of view in radians, aspect - width:height aspect ratio, z_near/z_far - clipping planes +math stolen from gluPerspective (https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml) +*/ +static m4 m4_perspective(float fov, float aspect, float z_near, float z_far) { + float f = 1.0f / tanf(fov / 2.0f); + return M4( + f/aspect, 0, 0, 0, + 0, f, 0, 0, + 0, 0, (z_far+z_near) / (z_near-z_far), (2.0f*z_far*z_near) / (z_near-z_far), + 0, 0, -1, 0 + ); +} + +// windows.h defines near and far, so let's not use those +static m4 m4_ortho(float left, float right, float bottom, float top, float near_, float far_) { + float tx = -(right + left)/(right - left); + float ty = -(top + bottom)/(top - bottom); + float tz = -(far_ + near_)/(far_ - near_); + return M4( + 2.0f / (right - left), 0, 0, tx, + 0, 2.0f / (top - bottom), 0, ty, + 0, 0, -2.0f / (far_ - near_), tz, + 0, 0, 0, 1 + ); +} + + +static m4 m4_mul(m4 a, m4 b) { + m4 prod = {0}; + int i, j; + float *x = prod.e; + for (i = 0; i < 4; ++i) { + for (j = 0; j < 4; ++j, ++x) { + float *as = &a.e[j]; + float *bs = &b.e[4*i]; + *x = as[0]*bs[0] + as[4]*bs[1] + as[8]*bs[2] + as[12]*bs[3]; + } + } + return prod; +} + +typedef struct { + int x, y; +} v2i; + +static v2i V2I(int x, int y) { + v2i v; + v.x = x; + v.y = y; + return v; +} + +static bool rect_contains_point_v2(v2 pos, v2 size, v2 point) { + float x1 = pos.x, y1 = pos.y, x2 = pos.x + size.x, y2 = pos.y + size.y, + x = point.x, y = point.y; + return x >= x1 && x < x2 && y >= y1 && y < y2; +} + +static bool centered_rect_contains_point(v2 center, v2 size, v2 point) { + return rect_contains_point_v2(v2_sub(center, v2_scale(size, 0.5f)), size, point); +} + +typedef struct { + v2 pos, size; +} Rect; + +static Rect rect(v2 pos, v2 size) { + Rect r; + r.pos = pos; + r.size = size; + return r; +} + +static Rect rect4(float x1, float y1, float x2, float y2) { + assert(x2 >= x1); + assert(y2 >= y1); + return rect(V2(x1,y1), V2(x2-x1, y2-y1)); +} + +static Rect rect_centered(v2 center, v2 size) { + Rect r; + r.pos = v2_sub(center, v2_scale(size, 0.5f)); + r.size = size; + return r; +} + +static v2 rect_center(Rect r) { + return v2_add(r.pos, v2_scale(r.size, 0.5f)); +} + +static bool rect_contains_point(Rect r, v2 point) { + return rect_contains_point_v2(r.pos, r.size, point); +} + +static float rect_x1(Rect r) { return r.pos.x; } +static float rect_y1(Rect r) { return r.pos.y; } +static float rect_x2(Rect r) { return r.pos.x + r.size.x; } +static float rect_y2(Rect r) { return r.pos.y + r.size.y; } + +static void rect_coords(Rect r, float *x1, float *y1, float *x2, float *y2) { + *x1 = r.pos.x; + *y1 = r.pos.y; + *x2 = r.pos.x + r.size.x; + *y2 = r.pos.y + r.size.y; +} + +static void rect_print(Rect r) { + printf("Position: (%f, %f), Size: (%f, %f)\n", r.pos.x, r.pos.y, r.size.x, r.size.y); +} + +#if MATH_GL +// must be rendering GL_QUADS to use these functions! + +static void rect_render(Rect r) { + float x1 = r.pos.x, y1 = r.pos.y, x2 = x1 + r.size.x, y2 = y1 + r.size.y; + glVertex2f(x1, y1); + glVertex2f(x2, y1); + glVertex2f(x2, y2); + glVertex2f(x1, y2); +} + +static void rect_render_border(Rect r, float border_radius) { + float x1 = r.pos.x, y1 = r.pos.y, x2 = x1 + r.size.x, y2 = y1 + r.size.y; + //float a = 0.3f; // for debugging + + //glColor4f(1,0,0,a); + glVertex2f(x1+border_radius, y1-border_radius); + glVertex2f(x1+border_radius, y1+border_radius); + glVertex2f(x2+border_radius, y1+border_radius); + glVertex2f(x2+border_radius, y1-border_radius); + + //glColor4f(0,1,0,a); + glVertex2f(x1-border_radius, y2-border_radius); + glVertex2f(x1-border_radius, y2+border_radius); + glVertex2f(x2-border_radius, y2+border_radius); + glVertex2f(x2-border_radius, y2-border_radius); + + //glColor4f(0,0,1,a); + glVertex2f(x1-border_radius, y1-border_radius); + glVertex2f(x1+border_radius, y1-border_radius); + glVertex2f(x1+border_radius, y2-border_radius); + glVertex2f(x1-border_radius, y2-border_radius); + + //glColor4f(1,1,0,a); + glVertex2f(x2-border_radius, y1+border_radius); + glVertex2f(x2+border_radius, y1+border_radius); + glVertex2f(x2+border_radius, y2+border_radius); + glVertex2f(x2-border_radius, y2+border_radius); +} + + +/* + gl grayscale color + i am tired of not having this +*/ +static void gl_color1f(float v) { + glColor3f(v,v,v); +} + +/* + gl grayscale+alpha color +*/ +static void gl_color2f(float v, float a) { + glColor4f(v,v,v,a); +} + +// color is 0xRRGGBBAA +static void gl_rgbacolor(u32 color) { + glColor4ub((u8)(color >> 24), (u8)(color >> 16), (u8)(color >> 8), (u8)color); +} + +// color is 0xRRGGBB +static void gl_rgbcolor(u32 color) { + gl_rgbacolor((color << 8) | 0xff); +} + +static void gl_quad(float x1, float y1, float x2, float y2) { + glVertex2f(x1, y1); + glVertex2f(x2, y1); + glVertex2f(x2, y2); + glVertex2f(x1, y2); +} +#endif + diff --git a/sim.c b/sim.c deleted file mode 100644 index 57ef51c..0000000 --- a/sim.c +++ /dev/null @@ -1,416 +0,0 @@ -#include "gui.h" -#ifdef _WIN32 -#include -#include "lib/glcorearb.h" -#endif -#include -#include -#include -#include -#include -#include -#include -#include - -#define MATH_GL -#include "math.c" -#include "sim.h" -#include "time.c" -#include "util.c" -#include "base.c" - -// compile a vertex or fragment shader -static GLuint shader_compile_from_file(GL *gl, char const *filename, GLenum shader_type) { - FILE *fp = fopen(filename, "rb"); - if (fp) { - char code[16384] = {0}; - char log[4096] = {0}; - char const *const_code = code; - fread(code, 1, sizeof code, fp); - GLuint shader = gl->CreateShader(shader_type); - if (shader == 0) { - logln("Couldn't create shader: %u",glGetError()); - } - gl->ShaderSource(shader, 1, &const_code, NULL); - gl->CompileShader(shader); - gl->GetShaderInfoLog(shader, sizeof log - 1, NULL, log); - if (*log) { - logln("Error compiling shader:\n%s", log); - gl->DeleteShader(shader); - return 0; - } - return shader; - } else { - logln("File does not exist: %s.", filename); - return 0; - } -} - -#if DEBUG -static struct timespec shader_get_last_modified(ShaderBase *shader) { - // the last modified time of the shader is whichever of the vertex/fragment shaders was modified last - return timespec_max( - time_last_modified(shader->vertex_filename), - time_last_modified(shader->fragment_filename) - ); -} -#endif - -static GLuint shader_attrib_location(GL *gl, ShaderBase *shader, char const *attrib) { - GLint loc = gl->GetAttribLocation(shader->program, attrib); - if (loc == -1) { - printf("Couldn't find vertex attribute %s.\n", attrib); - return 0; - } - return (GLuint)loc; -} - -static GLint shader_uniform_location(GL *gl, ShaderBase *shader, char const *uniform) { - GLint loc = gl->GetUniformLocation(shader->program, uniform); - if (loc == -1) { - printf("Couldn't find uniform: %s.\n", uniform); - return -1; - } - return loc; -} - -// compile a full shader program -static void shader_load(GL *gl, ShaderBase *shader, char const *vertex_filename, char const *fragment_filename) { -#if DEBUG - str_cpy(shader->vertex_filename, sizeof shader->vertex_filename, vertex_filename); - str_cpy(shader->fragment_filename, sizeof shader->fragment_filename, fragment_filename); - shader->last_modified = shader_get_last_modified(shader); - - if (shader->program) gl->DeleteProgram(shader->program); -#endif - - - GLuint vertex_shader = shader_compile_from_file(gl, vertex_filename, GL_VERTEX_SHADER); - GLuint fragment_shader = shader_compile_from_file(gl, fragment_filename, GL_FRAGMENT_SHADER); - GLuint program = gl->CreateProgram(); - gl->AttachShader(program, vertex_shader); - gl->AttachShader(program, fragment_shader); - gl->LinkProgram(program); - char log[4096] = {0}; - gl->GetProgramInfoLog(program, sizeof log - 1, NULL, log); - if (*log) { - logln("Error linking shader:\n%s", log); - gl->DeleteProgram(program); - program = 0; - } - gl->DeleteShader(vertex_shader); - gl->DeleteShader(fragment_shader); - shader->program = program; - - GLenum err = glGetError(); - if (err) { - logln("Error loading shader %s/%s: %u.", vertex_filename, fragment_filename, err); - } else { - logln("Loaded shader %s/%s as %u.", vertex_filename, fragment_filename, program); - } -} - -static void shader_start_using(GL *gl, ShaderBase *shader) { - gl->UseProgram(shader->program); -} - -static void shader_stop_using(GL *gl) { - gl->UseProgram(0); -} - -#if DEBUG -static bool shader_needs_reloading(ShaderBase *shader) { - return !timespec_eq(shader->last_modified, shader_get_last_modified(shader)); -} -#endif - -static void shader_platform_load(GL *gl, ShaderPlatform *shader) { - ShaderBase *base = &shader->base; - shader_load(gl, base, "assets/platform_v.glsl", "assets/platform_f.glsl"); - shader->vertex_p1 = shader_attrib_location(gl, base, "vertex_p1"); - shader->vertex_p2 = shader_attrib_location(gl, base, "vertex_p2"); - shader->uniform_thickness = shader_uniform_location(gl, base, "thickness"); - shader->uniform_transform = shader_uniform_location(gl, base, "transform"); -} - -static void shader_ball_load(GL *gl, ShaderBall *shader) { - ShaderBase *base = &shader->base; - shader_load(gl, base, "assets/ball_v.glsl", "assets/ball_f.glsl"); - shader->uniform_transform = shader_uniform_location(gl, base, "transform"); - shader->uniform_center = shader_uniform_location(gl, base, "center"); - shader->uniform_radius = shader_uniform_location(gl, base, "radius"); -} - -static void shaders_load(State *state) { - GL *gl = &state->gl; - shader_platform_load(gl, &state->shader_platform); - shader_ball_load(gl, &state->shader_ball); -} - -#if DEBUG -static void shaders_reload_if_necessary(State *state) { - GL *gl = &state->gl; - if (shader_needs_reloading(&state->shader_platform.base)) - shader_platform_load(gl, &state->shader_platform); - if (shader_needs_reloading(&state->shader_ball.base)) - shader_ball_load(gl, &state->shader_ball); -} -#endif - -// render the given platforms -static void platforms_render(State *state, Platform *platforms, u32 nplatforms) { - GL *gl = &state->gl; - ShaderPlatform *shader = &state->shader_platform; - float thickness = 0.005f; - - shader_start_using(gl, &shader->base); - - gl->Uniform1f(shader->uniform_thickness, thickness); - gl->UniformMatrix4fv(shader->uniform_transform, 1, GL_FALSE, state->transform.e); - - glBegin(GL_QUADS); - glColor3f(1,0,1); - for (Platform *platform = platforms, *end = platform + nplatforms; platform != end; ++platform) { - // calculate endpoints of platform - maybe_unused float radius = platform->size * 0.5f; - v2 endpoint1 = v2_add(platform->center, v2_polar(radius, platform->angle)); - v2 endpoint2 = v2_sub(platform->center, v2_polar(radius, platform->angle)); - - #if 1 - v2 r = v2_polar(thickness, platform->angle - HALF_PIf); - gl->VertexAttrib2f(shader->vertex_p1, endpoint1.x, endpoint1.y); - gl->VertexAttrib2f(shader->vertex_p2, endpoint2.x, endpoint2.y); - v2_gl_vertex(v2_sub(endpoint1, r)); - v2_gl_vertex(v2_sub(endpoint2, r)); - v2_gl_vertex(v2_add(endpoint2, r)); - v2_gl_vertex(v2_add(endpoint1, r)); - #else - v2_gl_vertex(endpoint1); - v2_gl_vertex(endpoint2); - #endif - } - glEnd(); - shader_stop_using(gl); -} - -// render the ball -static void ball_render(State *state) { - GL *gl = &state->gl; - Ball *ball = &state->ball; - float ball_x = ball->pos.x, ball_y = ball->pos.y; - float ball_r = ball->radius; - ShaderBall *shader = &state->shader_ball; - - shader_start_using(gl, &shader->base); - - gl->UniformMatrix4fv(shader->uniform_transform, 1, GL_FALSE, state->transform.e); - gl->Uniform2f(shader->uniform_center, ball_x, ball_y); - gl->Uniform1f(shader->uniform_radius, ball_r); - - glBegin(GL_QUADS); - glColor3f(1,1,1); - glVertex2f(ball_x-ball_r, ball_y-ball_r); - glVertex2f(ball_x-ball_r, ball_y+ball_r); - glVertex2f(ball_x+ball_r, ball_y+ball_r); - glVertex2f(ball_x+ball_r, ball_y-ball_r); - glEnd(); - shader_stop_using(gl); -} - -// calculate new position/velocity of ball -static void ball_update(State *state, float dt) { - Ball *ball = &state->ball; - float ball_r_squared = ball->radius * ball->radius; - float gravity = 0.001f; - v2 *vel = &ball->vel; - v2 acceleration = V2(0, -gravity); - // apply velocity to position - v2 ball_pos = v2_add(ball->pos, v2_scale(*vel, dt)); - // correct position according to acceleration (p' = p + v⋅t + 1/2⋅a⋅t²) - ball_pos = v2_add(ball_pos, v2_scale(acceleration, 0.5f * dt * dt)); - // apply acceleration to velocity - *vel = v2_add(*vel, acceleration); - for (Platform *platform = state->platforms, *end = platform + state->nplatforms; - platform != end; ++platform) { - v2 platform_center = platform->center; - float platform_size = platform->size; - float platform_angle = platform->angle; - v2 platform_length = v2_polar(platform_size, platform_angle); - platform_center = v2_sub(platform_center, ball_pos); // now the center of the ball is (0, 0) - v2 platform_pos = v2_sub(platform_center, v2_scale(platform_length, 0.5f)); - - // do some math to figure out if the ball & platform intersect - float dot = v2_dot(platform_length, platform_pos); - float len_squared = v2_dot(platform_length, platform_length); - float pos_squared = v2_dot(platform_pos, platform_pos); - float discriminant = dot * dot - len_squared * (pos_squared - ball_r_squared); - if (discriminant < 0) { - // full line (not segment) defined by platform doesn't even intersect ball, let alone the platform proper - } else { - float one_over_len_squared = 1.0f / len_squared; - float d1 = (-dot - sqrtf(discriminant)) * one_over_len_squared; - float d2 = (-dot + sqrtf(discriminant)) * one_over_len_squared; - // points platform_pos + d1 * platform_len and - // platform_pos + d2 * platform_len - // are where the platform intersects the circle. - // if either is between 0 and 1, then the intersection actually happens on the platform - if ((d1 >= 0 && d1 <= 1) || (d2 >= 0 && d2 <= 1)) { - // platform intersects ball - float d = (d1 >= 0 && d1 <= 1) ? d1 : d2; - v2 collide_pos = v2_add(platform_pos, v2_scale(platform_length, d)); - - // calculate new velocity after collision - float vel_angle = -atan2f(vel->y, vel->x); - float vel_angle_relative_to_platform = vel_angle - platform->angle; - float new_angle = vel_angle + (PIf - 2 * vel_angle_relative_to_platform); - *vel = v2_polar(v2_len(*vel), new_angle); - ball_pos = v2_add(ball->pos, v2_scale(*vel, dt)); - break; - } - } - - } - ball->pos = ball_pos; -} - -#ifdef _WIN32 -__declspec(dllexport) -#endif -#ifdef __cplusplus -extern "C" -#endif -void sim_frame(Frame *frame) { - if (frame->memory_size < sizeof(State)) { - printf("Not enough memory (got %lu, require %lu).\n", (ulong)frame->memory_size, (ulong)sizeof(State)); - frame->close = true; - return; - } - State *state = (State *)frame->memory; - i32 width = frame->width, height = frame->height; - Input *input = &frame->input; - GL *gl = &state->gl; - maybe_unused u8 *keys_pressed = input->keys_pressed; - maybe_unused bool *keys_down = input->keys_down; - - state->win_width = width; - state->win_height = height; - state->gl_width = (float)width / (float)height; - state->dt = (float)frame->dt; - - state->transform = m4_ortho(0, state->gl_width, 0, 1, -1, +1); - - // set up GL - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glViewport(0, 0, width, height); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glOrtho(0, state->gl_width, 0, 1, -1, +1); - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - - if (state->magic_number != MAGIC_NUMBER || keys_pressed[KEY_F5]) { - memset(state, 0, sizeof *state); - } - - - if (!state->initialized) { - logln("Initializing..."); - strcpy(frame->title, "physics"); - - void (*(*get_gl_proc)(char const *))(void) = frame->get_gl_proc; - - // get GL functions - #define required_gl_proc(name) if (!(gl->name = (GL ## name)get_gl_proc("gl" #name))) { printf("Couldn't get GL proc: %s.\n", #name); exit(-1); } - #define optional_gl_proc(name) gl->name = (GL ## name)get_gl_proc("gl" #name) - required_gl_proc(AttachShader); - required_gl_proc(CompileShader); - required_gl_proc(CreateProgram); - required_gl_proc(CreateShader); - required_gl_proc(DeleteProgram); - required_gl_proc(DeleteShader); - required_gl_proc(GetAttribLocation); - required_gl_proc(GetProgramInfoLog); - required_gl_proc(GetProgramiv); - required_gl_proc(GetShaderInfoLog); - required_gl_proc(GetShaderiv); - required_gl_proc(GetUniformLocation); - required_gl_proc(LinkProgram); - required_gl_proc(ShaderSource); - required_gl_proc(Uniform1f); - required_gl_proc(Uniform2f); - required_gl_proc(Uniform3f); - required_gl_proc(Uniform4f); - required_gl_proc(Uniform1i); - required_gl_proc(Uniform2i); - required_gl_proc(Uniform3i); - required_gl_proc(Uniform4i); - required_gl_proc(UniformMatrix4fv); - required_gl_proc(UseProgram); - required_gl_proc(VertexAttrib1f); - required_gl_proc(VertexAttrib2f); - required_gl_proc(VertexAttrib3f); - required_gl_proc(VertexAttrib4f); - #undef optional_gl_proc - #undef required_gl_proc - - shaders_load(state); - - - { // initialize platforms - state->nplatforms = 2; - Platform *p = &state->platforms[0]; - p->center = V2(0.5f, 0.5f); - p->angle = PIf * 0.3f; - p->size = 0.2f; - ++p; - p->center = V2(0.4f, 0.5f); - p->angle = PIf * 0.7f; - p->size = 0.2f; - } - - Ball *ball = &state->ball; - ball->radius = 0.002f; - ball->pos = V2(0.5f, 0.8f); - ball->vel = V2(0, 0); - - - state->initialized = true; - #if DEBUG - state->magic_number = MAGIC_NUMBER; - #endif - } - if (input->keys_pressed[KEY_ESCAPE]) { - frame->close = true; - return; - } - -#if DEBUG - shaders_reload_if_necessary(state); -#endif - - // simulate physics - { - float dt = state->dt; - float physics_step = 0.01f; // maximum dt for each physics step - if (dt > 100) - dt = 100; // make sure that dt -= physics_step actually does something (if dt is very large, it might not) - // do a number of fixed dt steps - while (dt > physics_step) { - ball_update(state, physics_step); - dt -= physics_step; - } - // update for remaining time - ball_update(state, dt); - } - - platforms_render(state, state->platforms, state->nplatforms); - ball_render(state); - - #if DEBUG - GLuint error = glGetError(); - if (error) { - printf("!!! GL ERROR: %u\n", error); - } - #endif -} diff --git a/sim.cpp b/sim.cpp new file mode 100644 index 0000000..d2ae5ba --- /dev/null +++ b/sim.cpp @@ -0,0 +1,427 @@ +#include "gui.hpp" +#ifdef _WIN32 +#include +#include "lib/glcorearb.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#define MATH_GL +#include "math.cpp" +#include "sim.hpp" +#include "time.cpp" +#include "util.cpp" +#include "base.cpp" + +// how much to scale up objects for Box2D +#define B2_SCALE 30 +#define B2_INV_SCALE (1.0f / B2_SCALE) + +static b2Vec2 v2_to_b2(v2 v) { + return b2Vec2(v.x, v.y); +} + +// compile a vertex or fragment shader +static GLuint shader_compile_from_file(GL *gl, char const *filename, GLenum shader_type) { + FILE *fp = fopen(filename, "rb"); + if (fp) { + char code[16384] = {0}; + char log[4096] = {0}; + char const *const_code = code; + fread(code, 1, sizeof code, fp); + GLuint shader = gl->CreateShader(shader_type); + if (shader == 0) { + logln("Couldn't create shader: %u",glGetError()); + } + gl->ShaderSource(shader, 1, &const_code, NULL); + gl->CompileShader(shader); + gl->GetShaderInfoLog(shader, sizeof log - 1, NULL, log); + if (*log) { + logln("Error compiling shader:\n%s", log); + gl->DeleteShader(shader); + return 0; + } + return shader; + } else { + logln("File does not exist: %s.", filename); + return 0; + } +} + +#if DEBUG +static struct timespec shader_get_last_modified(ShaderBase *shader) { + // the last modified time of the shader is whichever of the vertex/fragment shaders was modified last + return timespec_max( + time_last_modified(shader->vertex_filename), + time_last_modified(shader->fragment_filename) + ); +} +#endif + +static GLuint shader_attrib_location(GL *gl, ShaderBase *shader, char const *attrib) { + GLint loc = gl->GetAttribLocation(shader->program, attrib); + if (loc == -1) { + printf("Couldn't find vertex attribute %s.\n", attrib); + return 0; + } + return (GLuint)loc; +} + +static GLint shader_uniform_location(GL *gl, ShaderBase *shader, char const *uniform) { + GLint loc = gl->GetUniformLocation(shader->program, uniform); + if (loc == -1) { + printf("Couldn't find uniform: %s.\n", uniform); + return -1; + } + return loc; +} + +// compile a full shader program +static void shader_load(GL *gl, ShaderBase *shader, char const *vertex_filename, char const *fragment_filename) { +#if DEBUG + str_cpy(shader->vertex_filename, sizeof shader->vertex_filename, vertex_filename); + str_cpy(shader->fragment_filename, sizeof shader->fragment_filename, fragment_filename); + shader->last_modified = shader_get_last_modified(shader); + + if (shader->program) gl->DeleteProgram(shader->program); +#endif + + + GLuint vertex_shader = shader_compile_from_file(gl, vertex_filename, GL_VERTEX_SHADER); + GLuint fragment_shader = shader_compile_from_file(gl, fragment_filename, GL_FRAGMENT_SHADER); + GLuint program = gl->CreateProgram(); + gl->AttachShader(program, vertex_shader); + gl->AttachShader(program, fragment_shader); + gl->LinkProgram(program); + char log[4096] = {0}; + gl->GetProgramInfoLog(program, sizeof log - 1, NULL, log); + if (*log) { + logln("Error linking shader:\n%s", log); + gl->DeleteProgram(program); + program = 0; + } + gl->DeleteShader(vertex_shader); + gl->DeleteShader(fragment_shader); + shader->program = program; + + GLenum err = glGetError(); + if (err) { + logln("Error loading shader %s/%s: %u.", vertex_filename, fragment_filename, err); + } else { + logln("Loaded shader %s/%s as %u.", vertex_filename, fragment_filename, program); + } +} + +static void shader_start_using(GL *gl, ShaderBase *shader) { + gl->UseProgram(shader->program); +} + +static void shader_stop_using(GL *gl) { + gl->UseProgram(0); +} + +#if DEBUG +static bool shader_needs_reloading(ShaderBase *shader) { + return !timespec_eq(shader->last_modified, shader_get_last_modified(shader)); +} +#endif + +static void shader_platform_load(GL *gl, ShaderPlatform *shader) { + ShaderBase *base = &shader->base; + shader_load(gl, base, "assets/platform_v.glsl", "assets/platform_f.glsl"); + shader->vertex_p1 = shader_attrib_location(gl, base, "vertex_p1"); + shader->vertex_p2 = shader_attrib_location(gl, base, "vertex_p2"); + shader->uniform_thickness = shader_uniform_location(gl, base, "thickness"); + shader->uniform_transform = shader_uniform_location(gl, base, "transform"); +} + +static void shader_ball_load(GL *gl, ShaderBall *shader) { + ShaderBase *base = &shader->base; + shader_load(gl, base, "assets/ball_v.glsl", "assets/ball_f.glsl"); + shader->uniform_transform = shader_uniform_location(gl, base, "transform"); + shader->uniform_center = shader_uniform_location(gl, base, "center"); + shader->uniform_radius = shader_uniform_location(gl, base, "radius"); +} + +static void shaders_load(State *state) { + GL *gl = &state->gl; + shader_platform_load(gl, &state->shader_platform); + shader_ball_load(gl, &state->shader_ball); +} + +#if DEBUG +static void shaders_reload_if_necessary(State *state) { + GL *gl = &state->gl; + if (shader_needs_reloading(&state->shader_platform.base)) + shader_platform_load(gl, &state->shader_platform); + if (shader_needs_reloading(&state->shader_ball.base)) + shader_ball_load(gl, &state->shader_ball); +} +#endif + +// get endpoints and bounding box of platform +static void platform_get_coords(State *state, Platform *platform, v2 coords[6]) { + float radius = platform->size * 0.5f; + v2 thickness_r = v2_polar(state->platform_thickness, platform->angle - HALF_PIf); + v2 platform_r = v2_polar(radius, platform->angle); + v2 endpoint1 = v2_add(platform->center, platform_r); + v2 endpoint2 = v2_sub(platform->center, platform_r); + coords[0] = endpoint1; + coords[1] = endpoint2; + coords[2] = v2_sub(endpoint1, thickness_r); + coords[3] = v2_sub(endpoint2, thickness_r); + coords[4] = v2_add(endpoint2, thickness_r); + coords[5] = v2_add(endpoint1, thickness_r); +} + +// render the given platforms +static void platforms_render(State *state, Platform *platforms, u32 nplatforms) { + GL *gl = &state->gl; + ShaderPlatform *shader = &state->shader_platform; + float thickness = state->platform_thickness; + + shader_start_using(gl, &shader->base); + + gl->Uniform1f(shader->uniform_thickness, thickness); + gl->UniformMatrix4fv(shader->uniform_transform, 1, GL_FALSE, state->transform.e); + + glBegin(GL_QUADS); + glColor3f(1,0,1); + for (Platform *platform = platforms, *end = platform + nplatforms; platform != end; ++platform) { + v2 coords[6]; + platform_get_coords(state, platform, coords); + + #if 1 + gl->VertexAttrib2f(shader->vertex_p1, coords[0].x, coords[0].y); + gl->VertexAttrib2f(shader->vertex_p2, coords[1].x, coords[1].y); + v2_gl_vertex(coords[2]); + v2_gl_vertex(coords[3]); + v2_gl_vertex(coords[4]); + v2_gl_vertex(coords[5]); + #else + v2_gl_vertex(endpoint1); + v2_gl_vertex(endpoint2); + #endif + } + glEnd(); + shader_stop_using(gl); +} + +// render the ball +static void ball_render(State *state) { + GL *gl = &state->gl; + Ball *ball = &state->ball; + float ball_x = ball->pos.x, ball_y = ball->pos.y; + float ball_r = ball->radius; + ShaderBall *shader = &state->shader_ball; + + shader_start_using(gl, &shader->base); + + gl->UniformMatrix4fv(shader->uniform_transform, 1, GL_FALSE, state->transform.e); + gl->Uniform2f(shader->uniform_center, ball_x, ball_y); + gl->Uniform1f(shader->uniform_radius, ball_r); + + glBegin(GL_QUADS); + glColor3f(1,1,1); + glVertex2f(ball_x-ball_r, ball_y-ball_r); + glVertex2f(ball_x-ball_r, ball_y+ball_r); + glVertex2f(ball_x+ball_r, ball_y+ball_r); + glVertex2f(ball_x+ball_r, ball_y-ball_r); + glEnd(); + shader_stop_using(gl); +} + + +static b2Body *platform_to_body(State *state, Platform *platform) { + b2World *world = state->world; + + float half_size = platform->size * 0.5f * B2_SCALE; + v2 center = v2_scale(platform->center, B2_SCALE); + + b2BodyDef body_def; + body_def.position.Set(center.x, center.y); + b2Body *body = world->CreateBody(&body_def); + + b2PolygonShape shape; + shape.SetAsBox(half_size, state->platform_thickness, b2Vec2(0, 0), platform->angle); + body->CreateFixture(&shape, 0.0f); + return body; +} + +#ifdef _WIN32 +__declspec(dllexport) +#endif +#ifdef __cplusplus +extern "C" +#endif +void sim_frame(Frame *frame) { + if (frame->memory_size < sizeof(State)) { + printf("Not enough memory (got %lu, require %lu).\n", (ulong)frame->memory_size, (ulong)sizeof(State)); + frame->close = true; + return; + } + State *state = (State *)frame->memory; + Ball *ball = &state->ball; + i32 width = frame->width, height = frame->height; + Input *input = &frame->input; + GL *gl = &state->gl; + maybe_unused u8 *keys_pressed = input->keys_pressed; + maybe_unused bool *keys_down = input->keys_down; + + state->win_width = width; + state->win_height = height; + state->gl_width = (float)width / (float)height; + state->dt = (float)frame->dt; + + state->transform = m4_ortho(0, state->gl_width, 0, 1, -1, +1); + + // set up GL + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glViewport(0, 0, width, height); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glOrtho(0, state->gl_width, 0, 1, -1, +1); + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + + if (state->magic_number != MAGIC_NUMBER || keys_pressed[KEY_F5]) { + memset(state, 0, sizeof *state); + } + + + if (!state->initialized) { + logln("Initializing..."); + strcpy(frame->title, "physics"); + + void (*(*get_gl_proc)(char const *))(void) = frame->get_gl_proc; + + // get GL functions + #define required_gl_proc(name) if (!(gl->name = (GL ## name)get_gl_proc("gl" #name))) { printf("Couldn't get GL proc: %s.\n", #name); exit(-1); } + #define optional_gl_proc(name) gl->name = (GL ## name)get_gl_proc("gl" #name) + required_gl_proc(AttachShader); + required_gl_proc(CompileShader); + required_gl_proc(CreateProgram); + required_gl_proc(CreateShader); + required_gl_proc(DeleteProgram); + required_gl_proc(DeleteShader); + required_gl_proc(GetAttribLocation); + required_gl_proc(GetProgramInfoLog); + required_gl_proc(GetProgramiv); + required_gl_proc(GetShaderInfoLog); + required_gl_proc(GetShaderiv); + required_gl_proc(GetUniformLocation); + required_gl_proc(LinkProgram); + required_gl_proc(ShaderSource); + required_gl_proc(Uniform1f); + required_gl_proc(Uniform2f); + required_gl_proc(Uniform3f); + required_gl_proc(Uniform4f); + required_gl_proc(Uniform1i); + required_gl_proc(Uniform2i); + required_gl_proc(Uniform3i); + required_gl_proc(Uniform4i); + required_gl_proc(UniformMatrix4fv); + required_gl_proc(UseProgram); + required_gl_proc(VertexAttrib1f); + required_gl_proc(VertexAttrib2f); + required_gl_proc(VertexAttrib3f); + required_gl_proc(VertexAttrib4f); + #undef optional_gl_proc + #undef required_gl_proc + + shaders_load(state); + + state->platform_thickness = 0.005f; + + { // initialize platforms + state->nplatforms = 2; + Platform *p = &state->platforms[0]; + p->center = V2(0.5f, 0.5f); + p->angle = PIf * 0.46f; + p->size = 0.3f; + ++p; + p->center = V2(0.4f, 0.5f); + p->angle = PIf * 0.54f; + p->size = 0.3f; + } + + ball->radius = 0.02f; + ball->pos = V2(0.5f, 0.8f); + + b2Vec2 gravity(0, -30.0f); + b2World *world = state->world = new b2World(gravity); + + b2BodyDef ground_body_def; + ground_body_def.position.Set(0.0f, -10.0f); + b2Body *ground_body = world->CreateBody(&ground_body_def); + + b2PolygonShape ground_shape; + ground_shape.SetAsBox(50.0f, 10.0f); + ground_body->CreateFixture(&ground_shape, 0.0f); + + b2BodyDef ball_def; + ball_def.type = b2_dynamicBody; + ball_def.position.Set(ball->pos.x * B2_SCALE, ball->pos.y * B2_SCALE); + b2Body *ball_body = ball->body = world->CreateBody(&ball_def); + + b2CircleShape ball_shape; + ball_shape.m_radius = ball->radius * B2_SCALE; + + b2FixtureDef ball_fixture; + ball_fixture.shape = &ball_shape; + ball_fixture.density = 1.0f; + ball_fixture.friction = 0.3f; + ball_fixture.restitution = 0.9f; // bounciness + + ball_body->CreateFixture(&ball_fixture); + + for (u32 i = 0; i < state->nplatforms; ++i) + platform_to_body(state, &state->platforms[i]); + + state->initialized = true; + #if DEBUG + state->magic_number = MAGIC_NUMBER; + #endif + } + if (input->keys_pressed[KEY_ESCAPE]) { + frame->close = true; + return; + } + +#if DEBUG + shaders_reload_if_necessary(state); +#endif + + b2World *world = state->world; + + // simulate physics + { + float time_step = 0.01f; // fixed time step + float dt = state->dt; + if (dt > 100) dt = 100; // prevent floating-point problems for very large dt's + while (dt >= time_step) { + world->Step(time_step, 8, 3); // step using recommended parameters + dt -= time_step; + } + b2Vec2 ball_pos = ball->body->GetPosition(); + ball->pos.x = ball_pos.x * B2_INV_SCALE; + ball->pos.y = ball_pos.y * B2_INV_SCALE; + } + + platforms_render(state, state->platforms, state->nplatforms); + ball_render(state); + + #if DEBUG + GLuint error = glGetError(); + if (error) { + printf("!!! GL ERROR: %u\n", error); + } + #endif +} diff --git a/sim.h b/sim.h deleted file mode 100644 index 4de3191..0000000 --- a/sim.h +++ /dev/null @@ -1,144 +0,0 @@ -// enums with a specified width are a clang C extension & available in C++11 -#if defined __clang__ || __cplusplus >= 201103L -#define ENUM_U8 typedef enum : u8 -#define ENUM_U8_END(name) name -#define ENUM_U16 typedef enum : u16 -#define ENUM_U16_END(name) name -#else -#define ENUM_U8 enum -#define ENUM_U8_END(name) ; typedef u8 name -#define ENUM_U16 enum -#define ENUM_U16_END(name) ; typedef u16 name -#endif - - -#ifdef __GNUC__ -#define maybe_unused __attribute__((unused)) -#else -#define maybe_unused -#endif - -typedef void (APIENTRY *GLAttachShader)(GLuint program, GLuint shader); -typedef void (APIENTRY *GLCompileShader)(GLuint shader); -typedef GLuint (APIENTRY *GLCreateProgram)(void); -typedef GLuint (APIENTRY *GLCreateShader)(GLenum shaderType); -typedef void (APIENTRY *GLDeleteProgram)(GLuint program); -typedef void (APIENTRY *GLDeleteShader)(GLuint shader); -typedef GLint (APIENTRY *GLGetAttribLocation)(GLuint program, const char *name); -typedef void (APIENTRY *GLGetProgramInfoLog)(GLuint program, GLsizei maxLength, GLsizei *length, char *infoLog); -typedef void (APIENTRY *GLGetProgramiv)(GLuint program, GLenum pname, GLint *params); -typedef void (APIENTRY *GLGetShaderInfoLog)(GLuint shader, GLsizei maxLength, GLsizei *length, char *infoLog); -typedef void (APIENTRY *GLGetShaderiv)(GLuint shader, GLenum pname, GLint *params); -typedef GLint (APIENTRY *GLGetUniformLocation)(GLuint program, char const *name); -typedef void (APIENTRY *GLLinkProgram)(GLuint program); -typedef void (APIENTRY *GLShaderSource)(GLuint shader, GLsizei count, const char *const *string, const GLint *length); -typedef void (APIENTRY *GLUniform1f)(GLint location, GLfloat v0); -typedef void (APIENTRY *GLUniform2f)(GLint location, GLfloat v0, GLfloat v1); -typedef void (APIENTRY *GLUniform3f)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); -typedef void (APIENTRY *GLUniform4f)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); -typedef void (APIENTRY *GLUniform1i)(GLint location, GLint v0); -typedef void (APIENTRY *GLUniform2i)(GLint location, GLint v0, GLint v1); -typedef void (APIENTRY *GLUniform3i)(GLint location, GLint v0, GLint v1, GLint v2); -typedef void (APIENTRY *GLUniform4i)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); -typedef void (APIENTRY *GLUniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); -typedef void (APIENTRY *GLUseProgram)(GLuint program); -typedef void (APIENTRY *GLVertexAttrib1f)(GLuint index, GLfloat v0); -typedef void (APIENTRY *GLVertexAttrib2f)(GLuint index, GLfloat v0, GLfloat v1); -typedef void (APIENTRY *GLVertexAttrib3f)(GLuint index, GLfloat v0, GLfloat v1, GLfloat v2); -typedef void (APIENTRY *GLVertexAttrib4f)(GLuint index, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); - -typedef struct { - GLAttachShader AttachShader; - GLCompileShader CompileShader; - GLCreateProgram CreateProgram; - GLCreateShader CreateShader; - GLDeleteProgram DeleteProgram; - GLDeleteShader DeleteShader; - GLGetAttribLocation GetAttribLocation; - GLGetProgramInfoLog GetProgramInfoLog; - GLGetProgramiv GetProgramiv; - GLGetShaderInfoLog GetShaderInfoLog; - GLGetShaderiv GetShaderiv; - GLGetUniformLocation GetUniformLocation; - GLLinkProgram LinkProgram; - GLShaderSource ShaderSource; - GLUniform1f Uniform1f; - GLUniform2f Uniform2f; - GLUniform3f Uniform3f; - GLUniform4f Uniform4f; - GLUniform1i Uniform1i; - GLUniform2i Uniform2i; - GLUniform3i Uniform3i; - GLUniform4i Uniform4i; - GLUniformMatrix4fv UniformMatrix4fv; - GLUseProgram UseProgram; - GLVertexAttrib1f VertexAttrib1f; - GLVertexAttrib2f VertexAttrib2f; - GLVertexAttrib3f VertexAttrib3f; - GLVertexAttrib4f VertexAttrib4f; -} GL; - -typedef struct { - GLuint program; -#if DEBUG - char vertex_filename[32]; - char fragment_filename[32]; - struct timespec last_modified; -#endif -} ShaderBase; - -typedef GLuint VertexAttributeLocation; -typedef GLint UniformLocation; - -// shader for platforms -typedef struct { - ShaderBase base; - VertexAttributeLocation vertex_p1, vertex_p2; // endpoints of platform - UniformLocation uniform_thickness; // this is half the "width" of the platform - UniformLocation uniform_transform; // 4x4 matrix position is multiplied by -} ShaderPlatform; - -typedef struct { - ShaderBase base; - UniformLocation uniform_transform, uniform_center, uniform_radius; -} ShaderBall; - -typedef struct { - v2 center; - float size; - float angle; -} Platform; - -typedef struct { - v2 pos; // position - v2 vel; // velocity - float radius; -} Ball; - -typedef struct { - bool initialized; - - i32 win_width, win_height; // width,height of window - float gl_width; // width of window in GL coordinates; height is always 1 - - float dt; // time in seconds since last frame - m4 transform; // the transform for converting our coordinates to GL coordinates - - GL gl; // gl functions - ShaderPlatform shader_platform; - ShaderBall shader_ball; - - Ball ball; - - u32 nplatforms; - Platform platforms[1000]; - - u32 tmp_mem_used; // this is not measured in bytes, but in MaxAligns -#define TMP_MEM_BYTES (4L<<20) - MaxAlign tmp_mem[TMP_MEM_BYTES / sizeof(MaxAlign)]; -#if DEBUG - u32 magic_number; - #define MAGIC_NUMBER 0x1234ACAB -#endif -} State; - diff --git a/sim.hpp b/sim.hpp new file mode 100644 index 0000000..a4824c8 --- /dev/null +++ b/sim.hpp @@ -0,0 +1,148 @@ +#include +// enums with a specified width are a clang C extension & available in C++11 +#if defined __clang__ || __cplusplus >= 201103L +#define ENUM_U8 typedef enum : u8 +#define ENUM_U8_END(name) name +#define ENUM_U16 typedef enum : u16 +#define ENUM_U16_END(name) name +#else +#define ENUM_U8 enum +#define ENUM_U8_END(name) ; typedef u8 name +#define ENUM_U16 enum +#define ENUM_U16_END(name) ; typedef u16 name +#endif + + +#ifdef __GNUC__ +#define maybe_unused __attribute__((unused)) +#else +#define maybe_unused +#endif + +typedef void (APIENTRY *GLAttachShader)(GLuint program, GLuint shader); +typedef void (APIENTRY *GLCompileShader)(GLuint shader); +typedef GLuint (APIENTRY *GLCreateProgram)(void); +typedef GLuint (APIENTRY *GLCreateShader)(GLenum shaderType); +typedef void (APIENTRY *GLDeleteProgram)(GLuint program); +typedef void (APIENTRY *GLDeleteShader)(GLuint shader); +typedef GLint (APIENTRY *GLGetAttribLocation)(GLuint program, const char *name); +typedef void (APIENTRY *GLGetProgramInfoLog)(GLuint program, GLsizei maxLength, GLsizei *length, char *infoLog); +typedef void (APIENTRY *GLGetProgramiv)(GLuint program, GLenum pname, GLint *params); +typedef void (APIENTRY *GLGetShaderInfoLog)(GLuint shader, GLsizei maxLength, GLsizei *length, char *infoLog); +typedef void (APIENTRY *GLGetShaderiv)(GLuint shader, GLenum pname, GLint *params); +typedef GLint (APIENTRY *GLGetUniformLocation)(GLuint program, char const *name); +typedef void (APIENTRY *GLLinkProgram)(GLuint program); +typedef void (APIENTRY *GLShaderSource)(GLuint shader, GLsizei count, const char *const *string, const GLint *length); +typedef void (APIENTRY *GLUniform1f)(GLint location, GLfloat v0); +typedef void (APIENTRY *GLUniform2f)(GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRY *GLUniform3f)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRY *GLUniform4f)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRY *GLUniform1i)(GLint location, GLint v0); +typedef void (APIENTRY *GLUniform2i)(GLint location, GLint v0, GLint v1); +typedef void (APIENTRY *GLUniform3i)(GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRY *GLUniform4i)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRY *GLUniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRY *GLUseProgram)(GLuint program); +typedef void (APIENTRY *GLVertexAttrib1f)(GLuint index, GLfloat v0); +typedef void (APIENTRY *GLVertexAttrib2f)(GLuint index, GLfloat v0, GLfloat v1); +typedef void (APIENTRY *GLVertexAttrib3f)(GLuint index, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRY *GLVertexAttrib4f)(GLuint index, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); + +typedef struct { + GLAttachShader AttachShader; + GLCompileShader CompileShader; + GLCreateProgram CreateProgram; + GLCreateShader CreateShader; + GLDeleteProgram DeleteProgram; + GLDeleteShader DeleteShader; + GLGetAttribLocation GetAttribLocation; + GLGetProgramInfoLog GetProgramInfoLog; + GLGetProgramiv GetProgramiv; + GLGetShaderInfoLog GetShaderInfoLog; + GLGetShaderiv GetShaderiv; + GLGetUniformLocation GetUniformLocation; + GLLinkProgram LinkProgram; + GLShaderSource ShaderSource; + GLUniform1f Uniform1f; + GLUniform2f Uniform2f; + GLUniform3f Uniform3f; + GLUniform4f Uniform4f; + GLUniform1i Uniform1i; + GLUniform2i Uniform2i; + GLUniform3i Uniform3i; + GLUniform4i Uniform4i; + GLUniformMatrix4fv UniformMatrix4fv; + GLUseProgram UseProgram; + GLVertexAttrib1f VertexAttrib1f; + GLVertexAttrib2f VertexAttrib2f; + GLVertexAttrib3f VertexAttrib3f; + GLVertexAttrib4f VertexAttrib4f; +} GL; + +typedef struct { + GLuint program; +#if DEBUG + char vertex_filename[32]; + char fragment_filename[32]; + struct timespec last_modified; +#endif +} ShaderBase; + +typedef GLuint VertexAttributeLocation; +typedef GLint UniformLocation; + +// shader for platforms +typedef struct { + ShaderBase base; + VertexAttributeLocation vertex_p1, vertex_p2; // endpoints of platform + UniformLocation uniform_thickness; // this is half the "width" of the platform + UniformLocation uniform_transform; // 4x4 matrix position is multiplied by +} ShaderPlatform; + +typedef struct { + ShaderBase base; + UniformLocation uniform_transform, uniform_center, uniform_radius; +} ShaderBall; + +typedef struct { + v2 center; + float size; + float angle; +} Platform; + +typedef struct { + v2 pos; // position + float radius; + b2Body *body; +} Ball; + +typedef struct { + bool initialized; + + i32 win_width, win_height; // width,height of window + float gl_width; // width of window in GL coordinates; height is always 1 + + float dt; // time in seconds since last frame + m4 transform; // the transform for converting our coordinates to GL coordinates + + GL gl; // gl functions + ShaderPlatform shader_platform; + ShaderBall shader_ball; + + b2World *world; + + Ball ball; + + float platform_thickness; + u32 nplatforms; + Platform platforms[1000]; + + u32 tmp_mem_used; // this is not measured in bytes, but in MaxAligns +#define TMP_MEM_BYTES (4L<<20) + MaxAlign tmp_mem[TMP_MEM_BYTES / sizeof(MaxAlign)]; +#if DEBUG + u32 magic_number; + #define MAGIC_NUMBER 0x1234ACAB +#endif +} State; + diff --git a/time.c b/time.c deleted file mode 100644 index b616463..0000000 --- a/time.c +++ /dev/null @@ -1,65 +0,0 @@ -#include -#include -#include - -static struct timespec time_last_modified(char const *filename) { -#if __unix__ - struct stat statbuf = {0}; - stat(filename, &statbuf); - return statbuf.st_mtim; -#else - // windows' _stat does not have st_mtim - struct _stat statbuf = {0}; - struct timespec ts = {0}; - _stat(filename, &statbuf); - ts.tv_sec = statbuf.st_mtime; - return ts; -#endif -} - -static int timespec_cmp(struct timespec a, struct timespec b) { - if (a.tv_sec > b.tv_sec) return 1; - if (a.tv_sec < b.tv_sec) return -1; - if (a.tv_nsec > b.tv_nsec) return 1; - if (a.tv_nsec < b.tv_nsec) return -1; - return 0; -} - -static bool timespec_eq(struct timespec a, struct timespec b) { - return timespec_cmp(a, b) == 0; -} - -static struct timespec timespec_max(struct timespec a, struct timespec b) { - return timespec_cmp(a, b) < 0 ? b : a; -} - -// sleep for a certain number of nanoseconds -static void sleep_ns(u64 ns) { -#if __unix__ - struct timespec rem = {0}, req = { - ns / 1000000000, - ns % 1000000000 - }; - - while (nanosleep(&req, &rem) == EINTR) // sleep interrupted by signal - req = rem; -#else - // windows.... - Sleep((DWORD)(ns / 1000000)); -#endif -} - -// sleep for microseconds -static void time_sleep_us(u64 us) { - sleep_ns(us * 1000); -} - -// sleep for milliseconds -static void time_sleep_ms(u64 ms) { - sleep_ns(ms * 1000000); -} - -// sleep for seconds -static void time_sleep_s(u64 s) { - sleep_ns(s * 1000000000); -} diff --git a/time.cpp b/time.cpp new file mode 100644 index 0000000..fb007ba --- /dev/null +++ b/time.cpp @@ -0,0 +1,65 @@ +#include +#include +#include + +static struct timespec time_last_modified(char const *filename) { +#if __unix__ + struct stat statbuf = {}; + stat(filename, &statbuf); + return statbuf.st_mtim; +#else + // windows' _stat does not have st_mtim + struct _stat statbuf = {}; + struct timespec ts = {}; + _stat(filename, &statbuf); + ts.tv_sec = statbuf.st_mtime; + return ts; +#endif +} + +static int timespec_cmp(struct timespec a, struct timespec b) { + if (a.tv_sec > b.tv_sec) return 1; + if (a.tv_sec < b.tv_sec) return -1; + if (a.tv_nsec > b.tv_nsec) return 1; + if (a.tv_nsec < b.tv_nsec) return -1; + return 0; +} + +static bool timespec_eq(struct timespec a, struct timespec b) { + return timespec_cmp(a, b) == 0; +} + +static struct timespec timespec_max(struct timespec a, struct timespec b) { + return timespec_cmp(a, b) < 0 ? b : a; +} + +// sleep for a certain number of nanoseconds +static void sleep_ns(u64 ns) { +#if __unix__ + struct timespec rem = {}, req = { + (time_t)(ns / 1000000000), + (long)(ns % 1000000000) + }; + + while (nanosleep(&req, &rem) == EINTR) // sleep interrupted by signal + req = rem; +#else + // windows.... + Sleep((DWORD)(ns / 1000000)); +#endif +} + +// sleep for microseconds +static void time_sleep_us(u64 us) { + sleep_ns(us * 1000); +} + +// sleep for milliseconds +static void time_sleep_ms(u64 ms) { + sleep_ns(ms * 1000000); +} + +// sleep for seconds +static void time_sleep_s(u64 s) { + sleep_ns(s * 1000000000); +} diff --git a/util.c b/util.c deleted file mode 100644 index cb37336..0000000 --- a/util.c +++ /dev/null @@ -1,175 +0,0 @@ -#ifndef arr_count -#define arr_count(a) (sizeof (a) / sizeof *(a)) -#endif - -static bool streq(char const *a, char const *b) { - return strcmp(a, b) == 0; -} - -// on 16-bit systems, this is 16383. on 32/64-bit systems, this is 1073741823 -// it is unusual to have a string that long. -#define STRLEN_SAFE_MAX (UINT_MAX >> 2) - -// safer version of strcat. dst_sz includes a null terminator. -static void str_cat(char *dst, size_t dst_sz, char const *src) { - size_t dst_len = strlen(dst), src_len = strlen(src); - - // make sure dst_len + src_len + 1 doesn't overflow - if (dst_len > STRLEN_SAFE_MAX || src_len > STRLEN_SAFE_MAX) { - assert(0); - return; - } - - if (dst_len >= dst_sz) { - // dst doesn't actually contain a null-terminated string! - assert(0); - return; - } - - if (dst_len + src_len + 1 > dst_sz) { - // number of bytes left in dst, not including null terminator - size_t n = dst_sz - dst_len - 1; - memcpy(dst + dst_len, src, n); - dst[dst_sz - 1] = 0; // dst_len + n == dst_sz - 1 - } else { - memcpy(dst + dst_len, src, src_len); - dst[dst_len + src_len] = 0; - } -} - -// safer version of strncpy. dst_sz includes a null terminator. -static void str_cpy(char *dst, size_t dst_sz, char const *src) { - size_t srclen = strlen(src); - size_t n = srclen; // number of bytes to copy - - if (dst_sz == 0) { - assert(0); - return; - } - - if (dst_sz-1 < n) - n = dst_sz-1; - memcpy(dst, src, n); - dst[n] = 0; -} - -/* -returns the first instance of needle in haystack, ignoring the case of the characters, -or NULL if the haystack does not contain needle -WARNING: O(strlen(haystack) * strlen(needle)) -*/ -static char *stristr(char const *haystack, char const *needle) { - size_t needle_len = strlen(needle), haystack_len = strlen(haystack), i, j; - - if (needle_len > haystack_len) return NULL; // a larger string can't fit in a smaller string - - for (i = 0; i <= haystack_len - needle_len; ++i) { - char const *p = haystack + i, *q = needle; - bool match = true; - for (j = 0; j < needle_len; ++j) { - if (tolower(*p) != tolower(*q)) { - match = false; - break; - } - ++p; - ++q; - } - if (match) - return (char *)haystack + i; - } - return NULL; -} - -static void print_bytes(u8 *bytes, size_t n) { - u8 *b, *end; - for (b = bytes, end = bytes + n; b != end; ++b) - printf("%x ", *b); - printf("\n"); -} - -/* -does this predicate hold for all the characters of s. predicate is int (*)(int) instead -of bool (*)(char) so that you can pass isprint, etc. to it. -*/ -static bool str_satisfies(char const *s, int (*predicate)(int)) { - char const *p; - for (p = s; *p; ++p) - if (!predicate(*p)) - return false; - return true; -} - -static int is_lowercase_hex_digit(int c) { - return isdigit(c) || (c >= 'a' && c <= 'f'); -} - -// can c be a character in an integer (i.e. a '-' or a digit?) -static int is_int_char(int c) { - return isdigit(c) || c == '-'; -} - -// can c be a character in a positive float (i.e. a '.' or digit?) -static int is_positive_float_char(int c) { - return isdigit(c) || c == '.'; -} - -// can c be a character in a float (i.e. a '-', '.', or digit?) -static int is_float_char(int c) { - return isdigit(c) || c == '-' || c == '.'; -} - -// converts s to a 32-bit signed integer. sets *success to whether s is actually an integer, if success is not NULL. -// returns 0 on failure in addition to setting *success to false -// fails if s is -2^31 even though that technically does fit in an i32 -static i32 str_to_i32(char const *s, bool *success) { - bool negative = false; - if (success) *success = false; - if (s[0] == '-') { - negative = true; - ++s; - } - - if (s[0] == '\0') { // empty number - return 0; - } - - { - size_t len = strlen(s); - if (len > 10 || (len == 10 && s[0] > '2')) { - // overflow - return 0; - } - } - - if (!str_satisfies(s, isdigit)) { // non-digits in number - return 0; - } - - { - unsigned long ul = 0; - i32 magnitude; - int ret = sscanf(s, "%lu", &ul); - (void)ret; - assert(ret == 1); - if (ul > I32_MAX) - return 0; - magnitude = (i32)ul; - if (success) *success = true; - return negative ? -magnitude : +magnitude; - } -} - -// qsort comparison function for strings, usually case insensitive -static int qsort_stricmp(const void *av, const void *bv) { - char const *a = *(char const *const *)av; - char const *b = *(char const *const *)bv; -#ifdef __unix__ - return strcasecmp(a, b); -#elif defined _MSC_VER - return _stricmp(a, b); -#else - // give up on case insensitivity - return strcmp(a, b); -#endif -} - diff --git a/util.cpp b/util.cpp new file mode 100644 index 0000000..cb37336 --- /dev/null +++ b/util.cpp @@ -0,0 +1,175 @@ +#ifndef arr_count +#define arr_count(a) (sizeof (a) / sizeof *(a)) +#endif + +static bool streq(char const *a, char const *b) { + return strcmp(a, b) == 0; +} + +// on 16-bit systems, this is 16383. on 32/64-bit systems, this is 1073741823 +// it is unusual to have a string that long. +#define STRLEN_SAFE_MAX (UINT_MAX >> 2) + +// safer version of strcat. dst_sz includes a null terminator. +static void str_cat(char *dst, size_t dst_sz, char const *src) { + size_t dst_len = strlen(dst), src_len = strlen(src); + + // make sure dst_len + src_len + 1 doesn't overflow + if (dst_len > STRLEN_SAFE_MAX || src_len > STRLEN_SAFE_MAX) { + assert(0); + return; + } + + if (dst_len >= dst_sz) { + // dst doesn't actually contain a null-terminated string! + assert(0); + return; + } + + if (dst_len + src_len + 1 > dst_sz) { + // number of bytes left in dst, not including null terminator + size_t n = dst_sz - dst_len - 1; + memcpy(dst + dst_len, src, n); + dst[dst_sz - 1] = 0; // dst_len + n == dst_sz - 1 + } else { + memcpy(dst + dst_len, src, src_len); + dst[dst_len + src_len] = 0; + } +} + +// safer version of strncpy. dst_sz includes a null terminator. +static void str_cpy(char *dst, size_t dst_sz, char const *src) { + size_t srclen = strlen(src); + size_t n = srclen; // number of bytes to copy + + if (dst_sz == 0) { + assert(0); + return; + } + + if (dst_sz-1 < n) + n = dst_sz-1; + memcpy(dst, src, n); + dst[n] = 0; +} + +/* +returns the first instance of needle in haystack, ignoring the case of the characters, +or NULL if the haystack does not contain needle +WARNING: O(strlen(haystack) * strlen(needle)) +*/ +static char *stristr(char const *haystack, char const *needle) { + size_t needle_len = strlen(needle), haystack_len = strlen(haystack), i, j; + + if (needle_len > haystack_len) return NULL; // a larger string can't fit in a smaller string + + for (i = 0; i <= haystack_len - needle_len; ++i) { + char const *p = haystack + i, *q = needle; + bool match = true; + for (j = 0; j < needle_len; ++j) { + if (tolower(*p) != tolower(*q)) { + match = false; + break; + } + ++p; + ++q; + } + if (match) + return (char *)haystack + i; + } + return NULL; +} + +static void print_bytes(u8 *bytes, size_t n) { + u8 *b, *end; + for (b = bytes, end = bytes + n; b != end; ++b) + printf("%x ", *b); + printf("\n"); +} + +/* +does this predicate hold for all the characters of s. predicate is int (*)(int) instead +of bool (*)(char) so that you can pass isprint, etc. to it. +*/ +static bool str_satisfies(char const *s, int (*predicate)(int)) { + char const *p; + for (p = s; *p; ++p) + if (!predicate(*p)) + return false; + return true; +} + +static int is_lowercase_hex_digit(int c) { + return isdigit(c) || (c >= 'a' && c <= 'f'); +} + +// can c be a character in an integer (i.e. a '-' or a digit?) +static int is_int_char(int c) { + return isdigit(c) || c == '-'; +} + +// can c be a character in a positive float (i.e. a '.' or digit?) +static int is_positive_float_char(int c) { + return isdigit(c) || c == '.'; +} + +// can c be a character in a float (i.e. a '-', '.', or digit?) +static int is_float_char(int c) { + return isdigit(c) || c == '-' || c == '.'; +} + +// converts s to a 32-bit signed integer. sets *success to whether s is actually an integer, if success is not NULL. +// returns 0 on failure in addition to setting *success to false +// fails if s is -2^31 even though that technically does fit in an i32 +static i32 str_to_i32(char const *s, bool *success) { + bool negative = false; + if (success) *success = false; + if (s[0] == '-') { + negative = true; + ++s; + } + + if (s[0] == '\0') { // empty number + return 0; + } + + { + size_t len = strlen(s); + if (len > 10 || (len == 10 && s[0] > '2')) { + // overflow + return 0; + } + } + + if (!str_satisfies(s, isdigit)) { // non-digits in number + return 0; + } + + { + unsigned long ul = 0; + i32 magnitude; + int ret = sscanf(s, "%lu", &ul); + (void)ret; + assert(ret == 1); + if (ul > I32_MAX) + return 0; + magnitude = (i32)ul; + if (success) *success = true; + return negative ? -magnitude : +magnitude; + } +} + +// qsort comparison function for strings, usually case insensitive +static int qsort_stricmp(const void *av, const void *bv) { + char const *a = *(char const *const *)av; + char const *b = *(char const *const *)bv; +#ifdef __unix__ + return strcasecmp(a, b); +#elif defined _MSC_VER + return _stricmp(a, b); +#else + // give up on case insensitivity + return strcmp(a, b); +#endif +} + -- cgit v1.2.3