diff --git a/README.md b/README.md index 03f3d63..8f54dfd 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,10 @@ gb single-file public domain libraries for C & C++ library | latest version | category | languages | description ----------------|----------------|----------|-----------|------------- -**gb_string.h** | 0.93 | strings | C, C++ | A better string library for C & C++ -**gb.h** | 0.01 | misc | C | A C helper library geared towards game development (NOT a port of gb.hpp) -**gb_math.h** | 0.04 | math | C | A C vector math library geared towards game development (NOT A PORT OF gb_math.hpp) +**gb.h** | 0.02a | misc | C, C++ | A C helper library for C & C++ +**gb_math.h** | 0.04d | math | C, C++ | A C (and C++) vector math library geared towards game development +**gb_string.h** | 0.93 | strings | C, C++ | A better string library for C & C++ (this is built into gb.h too!) **gb_ini.h** | 0.91a | misc | C, C++ | A simple ini file loader library for C & C++ -**gb.hpp** | 0.32 | misc | C++11 | (Experimental) A C++11 helper library without STL geared towards game development -**gb_math.hpp** | 0.04 | math | C++11 | A C++11 math library geared towards game development ## FAQ diff --git a/gb.h b/gb.h index 96a686f..9e75b6b 100644 --- a/gb.h +++ b/gb.h @@ -1,39 +1,47 @@ -// gb.h - v0.01 - public domain C helper library - no warranty implied; use at your own risk -// (Experimental) A C helper library geared towards game development +/* gb.h - v0.02a - Ginger Bill's C Helper Library - public domain + - no warranty implied; use at your own risk + + This is a single header file with a bunch of useful stuff + to replace the C/C++ standard library + +=========================================================================== + YOU MUST + + #define GB_IMPLEMENTATION + + in EXACTLY _one_ C or C++ file that includes this header, BEFORE the + include like this: + + #define GB_IMPLEMENTATION + #include "gb.h" + + All other files should just #include "gb.h" without #define +=========================================================================== + +Conventions used: + gbTypesAreLikeThis (None core types) + gb_functions_and_variables_like_this + Prefer // Comments + Never use _t suffix for types (I think they are stupid...) + + +Version History: + 0.02a - Bug fixes + 0.02 - Change naming convention and gbArray(Type) + 0.01 - Initial Version -/* LICENSE - This software is in the public domain. Where that dedication is not - recognized, you are granted a perpetual, irrevocable license to copy, - distribute, and modify this file as you see fit. + This software is dual-licensed to the public domain and under the following + license: you are granted a perpetual, irrevocable license to copy, modify, + publish, and distribute this file as you see fit. WARNING - - This library is _highly_ experimental and features may not work as expected. + - This library is _slightly_ experimental and features may not work as expected. - This also means that many functions are not documented. -CONTENTS - - Common Macros - - Assert - - Types - - Cast macro (easy grepping) - - Memory - - Custom Allocation - - gb_Allocator - - gb_Arena - - gb_Pool - - gb_String +CREDITS + Written by Ginger Bill -TODO - - Mutex - - Atomics - - Semaphore - - Thread - - OS Types and Functions (File/IO/OS/etc.) -*/ - -/* -Version History: - 0.01 - Initial Version */ @@ -44,161 +52,294 @@ Version History: extern "C" { #endif -#include -#include -// NOTE(bill): Because static means three different things in C/C++ -// Great design(!) -#ifndef local_persist -#define global static -#define internal static -#define local_persist static -#endif - -// NOTE(bill): If I put gb_inline, I _want_ it inlined -#ifndef gb_inline - #if defined(_MSC_VER) - #define gb_inline __forceinline +#ifndef GB_EXTERN + #if defined(__cplusplus) + #define GB_EXTERN extern "C" #else - #define gb_inline __attribute__ ((__always_inline__)) + #define GB_EXTERN extern #endif #endif - -#if !defined(GB_NO_STDIO) -#include +#ifndef GB_DLL_EXPORT +#define GB_DLL_EXPORT __declspec(dllexport) #endif -#ifndef GB_ASSERT -#include -#define GB_ASSERT(cond) assert(cond) +#ifndef GB_DLL_IMPORT +#define GB_DLL_IMPORT __declspec(dllimport) #endif -#define GB_STATIC_ASSERT(cond, msg) typedef char gb__static_assertion_##msg[(!!(cond))*2-1] -// NOTE(bill): Token pasting madness -#define GB_COMPILE_TIME_ASSERT3(cond, line) GB_STATIC_ASSERT(cond, static_assertion_at_line_##line) -#define GB_COMPILE_TIME_ASSERT2(cond, line) GB_COMPILE_TIME_ASSERT3(cond, line) -#define GB_COMPILE_TIME_ASSERT(cond) GB_COMPILE_TIME_ASSERT2(cond, __LINE__) + +// NOTE(bill): Redefine for DLL, etc. +#ifndef GB_DEF + #ifdef GB_STATIC + #define GB_DEF static + #else + #define GB_DEF extern + #endif +#endif + +#if defined(_WIN32) || defined(_WIN64) + #if defined(_WIN64) + #ifndef GB_ARCH_64_BIT + #define GB_ARCH_64_BIT 1 + #endif + #else + #ifndef GB_ARCH_32_BIT + #define GB_ARCH_32_BIT 1 + #endif + #endif +#endif + +// TODO(bill): Check if this works on clang +#if defined(__GNUC__) + #if defined(__x86_64__) || defined(__ppc64__) + #ifndef GB_ARCH_64_BIT + #define GB_ARCH_64_BIT 1 + #endif + #else + #ifndef GB_ARCH_32_BIT + #define GB_ARCH_32_BIT 1 + #endif + #endif +#endif + +#ifndef GB_EDIAN_ORDER +#define GB_EDIAN_ORDER + #define GB_IS_BIG_EDIAN (!*(u8*)&(u16){1}) + #define GB_IS_LITTLE_EDIAN (!GB_IS_BIG_EDIAN) +#endif -#if !defined(GB_NO_STDIO) && defined(_MSC_VER) - // snprintf_msvc - gb_inline int - gb__vsnprintf_compatible(char* buffer, size_t size, char const *format, va_list args) - { - int result = -1; - if (size > 0) - result = _vsnprintf_s(buffer, size, _TRUNCATE, format, args); - if (result == -1) - return _vscprintf(format, args); - return result; - } +#if defined(_WIN32) || defined(_WIN64) + #ifndef GB_SYSTEM_WINDOWS + #define GB_SYSTEM_WINDOWS 1 + #endif +#elif defined(__APPLE__) && defined(__MACH__) + #ifndef GB_SYSTEM_OSX + #define GB_SYSTEM_OSX 1 + #endif +#elif defined(__unix__) + #ifndef GB_SYSTEM_UNIX + #define GB_SYSTEM_UNIX 1 + #endif - gb_inline int - gb__snprintf_compatible(char* buffer, size_t size, char const *format, ...) - { - int result = -1; - va_list args; - va_start(args, format); - result = gb__vsnprintf_compatible(buffer, size, format, args); - va_end(args); - return result; - } + #if defined(__linux__) + #ifndef GB_SYSTEM_LINUX + #define GB_SYSTEM_LINUX 1 + #endif + #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + #ifndef GB_SYSTEM_FREEBSD + #define GB_SYSTEM_FREEBSD 1 + #endif + #else + #error This UNIX operating system is not supported + #endif +#else + #error This operating system is not supported +#endif - #if !defined(GB_DO_NOT_USE_MSVC_SPRINTF_FIX) - #define snprintf gb__snprintf_compatible - #define vsnprintf gb__vsnprintf_compatible - #endif /* GB_DO_NOT_USE_MSVC_SPRINTF_FIX */ -#endif /* !defined(GB_NO_STDIO) */ + +#ifndef GB_STATIC_ASSERT + #define GB_STATIC_ASSERT3(cond, msg) typedef char static_assertion_##msg[(!!(cond))*2-1] + // NOTE(bill): Token pasting madness!! + #define GB_STATIC_ASSERT2(cond, line) GB_STATIC_ASSERT3(cond, static_assertion_at_line_##line) + #define GB_STATIC_ASSERT1(cond, line) GB_STATIC_ASSERT2(cond, line) + #define GB_STATIC_ASSERT(cond) GB_STATIC_ASSERT1(cond, __LINE__) +#endif +//////////////////////////////////////////////////////////////// +// +// Headers +// +// +#if defined(_WIN32) && !defined(__MINGW32__) + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif +#endif + +#include +#include +#include // TODO(bill): Remove and replace with OS Specific stuff +#include // NOTE(bill): For memcpy, memmove, memcmp, etc. + +#if defined(GB_SYSTEM_WINDOWS) + #define NOMINMAX 1 + #define WIN32_LEAN_AND_MEAN 1 + #define VC_EXTRALEAN 1 + #include +#else +#error Add OS Specific headers +#endif + + +#if !defined(GB_NO_STDLIB) +#include +#endif + +#ifndef gb_malloc +#define gb_malloc(sz) malloc(sz) +#endif + +#ifndef gb_mfree +#define gb_mfree(ptr) free(ptr) +#endif + + +//////////////////////////////////////////////////////////////// +// +// Base Types +// +// #if defined(_MSC_VER) - typedef unsigned __int8 u8; - typedef signed __int8 s8; + typedef unsigned __int8 u8; + typedef signed __int8 i8; typedef unsigned __int16 u16; - typedef signed __int16 s16; + typedef signed __int16 i16; typedef unsigned __int32 u32; - typedef signed __int32 s32; + typedef signed __int32 i32; typedef unsigned __int64 u64; - typedef signed __int64 s64; + typedef signed __int64 i64; #else #include - typedef uint8_t u8; - typedef int8_t s8; + typedef uint8_t u8; + typedef int8_t i8; typedef uint16_t u16; - typedef int16_t s16; + typedef int16_t i16; typedef uint32_t u32; - typedef int32_t s32; + typedef int32_t i32; typedef uint64_t u64; - typedef int64_t s64; + typedef int64_t i64; #endif -GB_COMPILE_TIME_ASSERT(sizeof(s8) == 1); -GB_COMPILE_TIME_ASSERT(sizeof(s16) == 2); -GB_COMPILE_TIME_ASSERT(sizeof(s32) == 4); -GB_COMPILE_TIME_ASSERT(sizeof(s64) == 8); +GB_STATIC_ASSERT(sizeof(u8) == sizeof(i8)); +GB_STATIC_ASSERT(sizeof(u16) == sizeof(i16)); +GB_STATIC_ASSERT(sizeof(u32) == sizeof(i32)); +GB_STATIC_ASSERT(sizeof(u64) == sizeof(i64)); -typedef size_t usize; +GB_STATIC_ASSERT(sizeof(u8) == 1); +GB_STATIC_ASSERT(sizeof(u16) == 2); +GB_STATIC_ASSERT(sizeof(u32) == 4); +GB_STATIC_ASSERT(sizeof(u64) == 8); + +typedef size_t usize; +typedef ptrdiff_t isize; + +GB_STATIC_ASSERT(sizeof(usize) == sizeof(isize)); typedef uintptr_t uintptr; +typedef intptr_t intptr; typedef float f32; typedef double f64; -#include // NOTE(bill): To get false/true +GB_STATIC_ASSERT(sizeof(f32) == 4); +GB_STATIC_ASSERT(sizeof(f64) == 8); -// Boolean Types -typedef s8 b8; -typedef s32 b32; +typedef u16 char16; +typedef u32 char32; +// NOTE(bill): I think C99 and C++ `bool` is stupid for numerous reasons but there are too many +// to write in this small comment. +typedef i8 b8; +typedef i16 b16; +typedef i32 b32; +// NOTE(bill): Get true and false +#if !defined(__cplusplus) + #if defined(_MSC_VER) && _MSC_VER <= 1800 + #ifndef false + #define false 0 + #endif + #ifndef true + #define true 1 + #endif + #else + #include + #endif +#endif +#ifndef U8_MIN #define U8_MIN 0u #define U8_MAX 0xffu -#define S8_MIN (-0x7f - 1) -#define S8_MAX 0x7f +#define I8_MIN (-0x7f - 1) +#define I8_MAX 0x7f #define U16_MIN 0u #define U16_MAX 0xffffu -#define S16_MIN (-0x7fff - 1) -#define S16_MAX 0x7fff +#define I16_MIN (-0x7fff - 1) +#define I16_MAX 0x7fff #define U32_MIN 0u #define U32_MAX 0xffffffffu -#define S32_MIN (-0x7fffffff - 1) -#define S32_MAX 0x7fffffff +#define I32_MIN (-0x7fffffff - 1) +#define I32_MAX 0x7fffffff #define U64_MIN 0ull #define U64_MAX 0xffffffffffffffffull -#define S64_MIN (-0x7fffffffffffffffll - 1) -#define S64_MAX 0x7fffffffffffffffll +#define I64_MIN (-0x7fffffffffffffffll - 1) +#define I64_MAX 0x7fffffffffffffffll +#if defined(GB_ARCH_32_BIT) + #define USIZE_MIX U32_MIN + #define USIZE_MAX U32_MAX + #define ISIZE_MIX S32_MIN + #define ISIZE_MAX S32_MAX +#elif defined(GB_ARCH_64_BIT) + #define USIZE_MIX U64_MIN + #define USIZE_MAX U64_MAX - - -#ifndef COUNT_OF -#define COUNT_OF(x) (sizeof((x)) / sizeof(0[(x)])) + #define ISIZE_MIX I64_MIN + #define ISIZE_MAX I64_MAX +#else + #error Unknown architecture size #endif -// NOTE(bill): Allows for easy grep of casts -// NOTE(bill): Still not as type safe as C++ static_cast, reinterpret_cast, const_cast -#ifndef cast -#define cast(x) (x) +#define F32_MIN 1.17549435e-38f +#define F32_MAX 3.40282347e+38f + +#define F64_MIN 2.2250738585072014e-308 +#define F64_MAX 1.7976931348623157e+308 + + #endif + + #ifndef NULL -#define NULL ((void *)0) + #if defined(__cplusplus) + #if __cplusplus >= 201103L + #define NULL nullptr + #else + #define NULL 0 + #endif + #else + #define NULL ((void *)0) + #endif #endif -#ifndef GB_UNUSED -#define GB_UNUSED(x) ((void)(sizeof(x))) + + +#if !defined(__cplusplus) && defined(_MSC_VER) && _MSC_VER <= 1800 + #define inline __inline #endif -#ifndef gb_inline +#if !defined(gb_inline) + #if defined(_MSC_VER) + #define gb_restrict __restrict + #else + #define gb_restrict restrict + #endif +#endif + +// TODO(bill): Should force inline be a separate keyword and gb_inline be inline? +#if !defined(gb_inline) #if defined(_MSC_VER) #define gb_inline __forceinline #else @@ -206,39 +347,1169 @@ typedef s32 b32; #endif #endif - - - - - - -//////////////////////////////// -// // -// Memory // -// // -//////////////////////////////// - -#include // For memcpy/memmove/memset/etc. - -#ifndef GB_IS_POWER_OF_TWO -#define GB_IS_POWER_OF_TWO(x) ((x) != 0) && !((x) & ((x)-1)) +#if !defined(gb_no_inline) + #if defined(_MSC_VER) + #define gb_no_inline __declspec(noinline) + #else + #define gb_no_inline __attribute__ ((noinline)) + #endif #endif -void *gb_align_forward(void *ptr, usize align); - -void gb_zero_size(void *ptr, usize size); -#define gb_zero_struct(t) gb_zero_size((t), sizeof(*(t))) // NOTE(bill): Pass pointer of struct -#define gb_zero_array(a, count) gb_zero_size((a), sizeof((a)[0])*count) +// NOTE(bill): Easy to grep +// NOTE(bill): Not needed in macros +#ifndef cast +#define cast(Type) (Type) +#endif + + +// NOTE(bill): Because a signed sizeof is more useful +#ifndef gb_size_of +#define gb_size_of(x) (isize)(sizeof(x)) +#endif + +#ifndef gb_count_of +#define gb_count_of(x) ((gb_size_of(x)/gb_size_of(0[x])) / ((isize)(!(gb_size_of(x) % gb_size_of(0[x]))))) +#endif + +#ifndef gb_offset_of +#define gb_offset_of(Type, element) ((isize)&(((Type *)0)->element)) +#endif + +#if defined(__cplusplus) +#ifndef gb_align_of + #if __cplusplus >= 201103L + #define gb_align_of(Type) (isize)alignof(Type) + #else +extern "C++" { + // NOTE(bill): Fucking Templates! + template struct gbAlignment_Trick { char c; T member; }; + #define gb_align_of(Type) gb_offset_of(gbAlignment_Trick, member) +} + #endif +#endif +#else + #ifndef gb_align_of + #define gb_align_of(Type) gb_offset_of(struct { char c; Type member; }, member) + #endif +#endif + +// NOTE(bill): I do which I had a type_of that was portable +#ifndef gb_swap +#define gb_swap(Type, a, b) do { Type tmp = (a); (a) = (b); (b) = tmp; } while (0) +#endif + +// NOTE(bill): Because static means 3/4 different things in C/C++. Great design (!) +#ifndef gb_global +#define gb_global static // Global variables +#define gb_internal static // Internal linkage +#define gb_local_persist static // Local Persisting variables +#endif + + +#ifndef gb_unused +#define gb_unused(x) ((void)(gb_size_of(x))) +#endif + + + + + + +//////////////////////////////////////////////////////////////// +// +// Defer statement +// Akin to D's SCOPE_EXIT or +// similar to Go's defer but scope-based +// +// NOTE: C++11 (and above) only! +// +#if defined(__cplusplus) +extern "C++" { +namespace gb { + + // NOTE(bill): Stupid fucking templates + template struct RemoveReference { typedef T Type; }; + template struct RemoveReference { typedef T Type; }; + template struct RemoveReference { typedef T Type; }; + + // NOTE(bill): "Move" semantics - invented because the C++ committee are idiots (as a collective not as indiviuals (well a least some aren't)) + template inline T &&forward(typename RemoveReference::Type &t) { return static_cast(t); } + template inline T &&forward(typename RemoveReference::Type &&t) { return static_cast(t); } + template inline T &&move(T &&t) { return static::Type &&>(t); } + template + struct privDefer { + F f; + privDefer(F &&f) : f(forward(f)) {} + ~privDefer() { f(); } + }; + template privDefer priv_defer_func(F &&f) { return privDefer(forward(f)); } + + #ifndef defer + #define GB_DEFER_1(x, y) x##y + #define GB_DEFER_2(x, y) GB_DEFER_1(x, y) + #define GB_DEFER_3(x) GB_DEFER_2(x, __COUNTER__) + #define defer(code) auto GB_DEFER_3(_defer_) = priv_defer_func([&](){code;}) + #endif +} // namespace gb +} + + // Example + #if 0 + gbMutex m; + gb_mutex_init(&m); + { + gb_mutex_lock(&m); + defer (gb_mutex_unlock(&m)); + + ... + } + #endif + +#endif + + +//////////////////////////////// +// +// Macro Fun! +// +// + +#ifndef GB_JOIN_MACROS +#define GB_JOIN_MACROS + #define GB_JOIN2_IND(a, b) a##b + #define GB_JOIN3_IND(a, b, c) a##b##c + + #define GB_JOIN2(a, b) GB_JOIN2_IND(a, b) + #define GB_JOIN3(a, b, c) GB_JOIN3_IND(a, b, c) +#endif + + +#ifndef GB_BIT +#define GB_BIT(x) (1< (b) ? (a) : (b)) +#endif + +#ifndef gb_clamp +#define gb_clamp(x, lower, upper) gb_min(gb_max((x), (lower)), (upper)) +#endif + +#ifndef gb_clamp01 +#define gb_clamp01(x) gb_clamp((x), 0, 1) +#endif + + +//////////////////////////////// +// +// Debug +// +// + + +#ifndef GB_DEBUG_TRAP + #if defined(_MSC_VER) + #define GB_DEBUG_TRAP() __debugbreak() + #else + #define GB_DEBUG_TRAP() __builtin_trap() + #endif +#endif + +// TODO(bill): This relies upon variadic macros which are not supported in MSVC 2003 and below, check for it if needed +#ifndef GB_ASSERT_MSG +#define GB_ASSERT_MSG(cond, msg, ...) do { \ + if (!(cond)) { \ + gb_assert_handler(#cond, __FILE__, cast(i64)__LINE__, msg, ##__VA_ARGS__); \ + GB_DEBUG_TRAP(); \ + } \ +} while (0) +#endif + +#ifndef GB_ASSERT +#define GB_ASSERT(cond) GB_ASSERT_MSG(cond, NULL) +#endif + +#ifndef GB_ASSERT_NOT_NULL +#define GB_ASSERT_NOT_NULL(ptr) GB_ASSERT_MSG((ptr) != NULL, #ptr " must not be NULL") +#endif + +// NOTE(bill): Things that shouldn't happen +#ifndef GB_PANIC +#define GB_PANIC(msg, ...) GB_ASSERT_MSG(0, msg, ##__VA_ARGS__) +#endif + +GB_DEF void gb_assert_handler(char const *condition, char const *file, i64 line, char const *msg, ...); + + + + + +//////////////////////////////// +// +// Printing +// +// + +// Some compilers support applying printf-style warnings to user functions. +#if defined(__clang__) || defined(__GNUC__) +#define GB_PRINTF_ARGS(FMT) __attribute__((format(printf, FMT, (FMT+1)))) +#else +#define GB_PRINTF_ARGS(FMT) +#endif + +// TODO(bill): Should I completely rename these functions as they are a little weird to begin with? + +GB_DEF i32 gb_printf(char const *fmt, ...) GB_PRINTF_ARGS(1); +GB_DEF i32 gb_printf_va(char const *fmt, va_list va); +GB_DEF i32 gb_fprintf(FILE *f, char const *fmt, ...) GB_PRINTF_ARGS(2); +GB_DEF i32 gb_fprintf_va(FILE *f, char const *fmt, va_list va); +GB_DEF char *gb_sprintf(char const *fmt, ...) GB_PRINTF_ARGS(1); // NOTE(bill): A locally persisting buffer is used internally +GB_DEF char *gb_sprintf_va(char const *fmt, va_list va); // NOTE(bill): A locally persisting buffer is used internally +GB_DEF i32 gb_snprintf(char *str, isize n, char const *fmt, ...) GB_PRINTF_ARGS(3); +GB_DEF i32 gb_snprintf_va(char *str, isize n, char const *fmt, va_list va); + +GB_DEF i32 gb_println(char const *str); +GB_DEF i32 gb_fprintln(FILE *f, char const *str); + + + +//////////////////////////////////////////////////////////////// +// +// Memory +// +// + +#ifndef gb_align_to +#define gb_align_to(value, alignment) (((value) + ((alignment)-1)) & ~((alignment) - 1)) +#endif + +#ifndef gb_is_power_of_two +#define gb_is_power_of_two(x) ((x) != 0) && !((x) & ((x)-1)) +#endif + +GB_DEF void *gb_align_forward(void *ptr, isize alignment); +GB_DEF void *gb_pointer_add(void *ptr, isize bytes); +GB_DEF void *gb_pointer_sub(void *ptr, isize bytes); + +GB_DEF void gb_zero_size(void *ptr, isize size); + +#ifndef gb_zero_struct +#define gb_zero_struct(t) gb_zero_size((t), gb_size_of(*(t))) // NOTE(bill): Pass pointer of struct +#define gb_zero_array(a, count) gb_zero_size((a), gb_size_of((a)[0])*count) +#endif + +GB_DEF void *gb_memcopy(void *dest, void const *source, isize size); +GB_DEF void *gb_memmove(void *dest, void const *source, isize size); +GB_DEF void *gb_memset(void *data, u8 byte_value, isize size); + +// NOTE(bill): Very similar to doing `*cast(T *)(&u)` +#ifndef GB_BIT_CAST +#define GB_BIT_CAST(dest, source) do { \ + GB_STATIC_ASSERT(gb_size_of(*(dest)) <= gb_size_of(source)); \ + gb_memcopy((dest), &(source), gb_size_of(*dest)); \ +} while (0) +#endif + + + +#ifndef gb_kilobytes +#define gb_kilobytes(x) ( (x) * (i64)(1024)) +#define gb_megabytes(x) (gb_kilobytes(x) * (i64)(1024)) +#define gb_gigabytes(x) (gb_megabytes(x) * (i64)(1024)) +#define gb_terabytes(x) (gb_gigabytes(x) * (i64)(1024)) +#endif + + +// Atomics +typedef struct gbAtomic32 { i32 volatile value; } gbAtomic32; +typedef struct gbAtomic64 { i64 volatile value; } gbAtomic64; + +GB_DEF i32 gb_atomic32_load(gbAtomic32 const volatile *a); +GB_DEF void gb_atomic32_store(gbAtomic32 volatile *a, i32 value); +GB_DEF i32 gb_atomic32_compare_exchange_strong(gbAtomic32 volatile *a, i32 expected, i32 desired); +GB_DEF i32 gb_atomic32_exchanged(gbAtomic32 volatile *a, i32 desired); +GB_DEF i32 gb_atomic32_fetch_add(gbAtomic32 volatile *a, i32 operand); +GB_DEF i32 gb_atomic32_fetch_and(gbAtomic32 volatile *a, i32 operand); +GB_DEF i32 gb_atomic32_fetch_or(gbAtomic32 volatile *a, i32 operand); + +GB_DEF i64 gb_atomic64_load(gbAtomic64 const volatile *a); +GB_DEF void gb_atomic64_store(gbAtomic64 volatile *a, i64 value); +GB_DEF i64 gb_atomic64_compare_exchange_strong(gbAtomic64 volatile *a, i64 expected, i64 desired); +GB_DEF i64 gb_atomic64_exchanged(gbAtomic64 volatile *a, i64 desired); +GB_DEF i64 gb_atomic64_fetch_add(gbAtomic64 volatile *a, i64 operand); +GB_DEF i64 gb_atomic64_fetch_and(gbAtomic64 volatile *a, i64 operand); +GB_DEF i64 gb_atomic64_fetch_or(gbAtomic64 volatile *a, i64 operand); + + +typedef struct gbMutex { +#if defined(GB_SYSTEM_WINDOWS) + HANDLE win32_handle; +#else + pthread_mutex_t posix_handle; +#endif +} gbMutex; + +GB_DEF void gb_mutex_init(gbMutex *m); +GB_DEF void gb_mutex_destroy(gbMutex *m); +GB_DEF void gb_mutex_lock(gbMutex *m); +GB_DEF b32 gb_mutex_try_lock(gbMutex *m); +GB_DEF void gb_mutex_unlock(gbMutex *m); + +// NOTE(bill): If you wanted a Scoped Mutex in C++, why not use the defer() construct? +// No need for a silly wrapper class +#if 0 +gbMutex m = {0}; +gb_mutex_init(&m); +{ + gb_mutex_lock(&m); + defer (gb_mutex_unlock(&m)); + + // Do whatever +} +#endif + + + +typedef struct gbSemaphore { +#if defined(GB_SYSTEM_WINDOWS) + HANDLE win32_handle; +#else + gbMutex mutex; + pthread_cond_t cond; + i32 count; +#endif +} gbSemaphore; + +GB_DEF void gb_semaphore_init(gbSemaphore *s); +GB_DEF void gb_semaphore_destroy(gbSemaphore *s); +GB_DEF void gb_semaphore_post(gbSemaphore *s, i32 count); +GB_DEF void gb_semaphore_wait(gbSemaphore *s); + + + + +#define GB_THREAD_PROC(name) void name(void *data) +;typedef GB_THREAD_PROC(gbThreadProc); + +typedef struct gbThread { +#if defined(GB_SYSTEM_WINDOWS) + HANDLE win32_handle; +#else + pthread_t posix_handle; +#endif + + gbThreadProc *proc; + void *data; + + gbSemaphore semaphore; + isize stack_size; + b32 is_running; +} gbThread; + +GB_DEF void gb_thread_init(gbThread *t); +GB_DEF void gb_thread_destory(gbThread *t); +GB_DEF void gb_thread_start(gbThread *t, gbThreadProc *proc, void *data); +GB_DEF void gb_thread_start_with_stack(gbThread *t, gbThreadProc *proc, void *data, isize stack_size); +GB_DEF void gb_thread_join(gbThread *t); +GB_DEF b32 gb_thread_is_running(gbThread const *t); +GB_DEF u32 gb_thread_current_id(void); + +//////////////////////////////////////////////////////////////// +// +// Virtual Memory +// +// + +#if 0 +typedef enum gbVmAllocationFlag { + GB_VM_COMMIT = GB_BIT(1), + GB_VM_RESERVE = GB_BIT(2), + GB_VM_RESET = GB_BIT(3), + GB_VM_RESET_UNDO = GB_BIT(4), +} gbVmAllocationFlag; + +typedef enum gbVmProtectionFlag { + GB_VM_PAGE_NO_ACCESS = GB_BIT(1), + GB_VM_PAGE_GUARD = GB_BIT(2), + GB_VM_PAGE_NO_CACHE = GB_BIT(3), + GB_VM_PAGE_WRITE_COMBINE = GB_BIT(4), +} gbVmProtectionFlag; + +typedef enum gbVmFreeType { + GB_VM_RELEASE = GB_BIT(1), + GB_VM_DECOMMIT = GB_BIT(2), +} gbVmFreeType; + +typedef struct gbVirtualMemory { + void *memory; + isize size; + u32 allocation_flags; + u32 protection_flags; +} gbVirtualMemory; + +GB_DEF gbVirtualMemory gb_vm_alloc(void *base_address, isize size, u32 allocation_flags, u32 protection_flags); +GB_DEF void gb_vm_free(gbVirtualMemory *vm); +#endif + + +//////////////////////////////////////////////////////////////// +// +// Custom Allocation +// +// + +typedef enum gbAllocationType { + GB_ALLOCATION_ALLOC, + GB_ALLOCATION_FREE, + GB_ALLOCATION_FREE_ALL, + GB_ALLOCATION_RESIZE, +} gbAllocationType; + +// NOTE(bill): This is useful so you can define an allocator of the same type and parameters +#define GB_ALLOCATOR_PROC(name) \ +void *name(void *allocator_data, gbAllocationType type, \ + isize size, isize alignment, \ + void *old_memory, isize old_size, \ + u64 options) +;typedef GB_ALLOCATOR_PROC(gbAllocatorProc); + +typedef struct gbAllocator { + gbAllocatorProc *proc; + void *data; +} gbAllocator; + +#ifndef GB_DEFAULT_MEMORY_ALIGNMENT +#define GB_DEFAULT_MEMORY_ALIGNMENT 4 +#endif + +GB_DEF void *gb_alloc_align(gbAllocator a, isize size, isize alignment); +GB_DEF void *gb_alloc(gbAllocator a, isize size); +GB_DEF void gb_free(gbAllocator a, void *ptr); +GB_DEF void gb_free_all(gbAllocator a); +GB_DEF void *gb_resize(gbAllocator a, void *ptr, isize old_size, isize new_size); +GB_DEF void *gb_resize_align(gbAllocator a, void *ptr, isize old_size, isize new_size, isize alignment); + +GB_DEF void *gb_alloc_copy(gbAllocator a, void const *src, isize size); +GB_DEF void *gb_alloc_copy_align(gbAllocator a, void const *src, isize size, isize alignment); + +GB_DEF char *gb_alloc_cstring(gbAllocator a, char const *str); + + +// NOTE(bill): These are very useful and the type case has saved me from numerous bugs +#ifndef gb_alloc_struct +#define gb_alloc_struct(allocator, Type) (Type *)gb_alloc_align(allocator, gb_size_of(Type)) +#define gb_alloc_array(allocator, Type, count) (Type *)gb_alloc(allocator, gb_size_of(Type) * (count)) +#endif + + + + + +GB_DEF gbAllocator gb_heap_allocator(void); +GB_DEF GB_ALLOCATOR_PROC(gb_heap_allocator_proc); + + + + + +typedef struct gbArena { + gbAllocator backing; + void *physical_start; + isize total_size; + isize total_allocated; + u32 temp_count; +} gbArena; + +GB_DEF void gb_arena_init_from_memory(gbArena *arena, void *start, isize size); +GB_DEF void gb_arena_init_from_allocator(gbArena *arena, gbAllocator backing, isize size); +GB_DEF void gb_arena_init_subarena(gbArena *arena, gbArena *parent_arena, isize size); +GB_DEF void gb_arena_free(gbArena *arena); + +GB_DEF isize gb_arena_alignment_of(gbArena *arena, isize alignment); +GB_DEF isize gb_arena_size_remaining(gbArena *arena, isize alignment); +GB_DEF void gb_arena_check(gbArena *arena); + + +GB_DEF gbAllocator gb_arena_allocator(gbArena *arena); +GB_DEF GB_ALLOCATOR_PROC(gb_arena_allocator_proc); + + + +typedef struct gbTempArenaMemory { + gbArena *arena; + isize original_count; +} gbTempArenaMemory; + +GB_DEF gbTempArenaMemory gb_temp_arena_memory_begin(gbArena *arena); +GB_DEF void gb_temp_arena_memory_end(gbTempArenaMemory tmp_mem); + + + + + + + + + +typedef struct gbPool { + gbAllocator backing; + + void *physical_start; + void *free_list; + + isize block_size; + isize block_align; + isize total_size; +} gbPool; + +GB_DEF void gb_pool_init(gbPool *pool, gbAllocator backing, isize num_blocks, isize block_size); +GB_DEF void gb_pool_init_align(gbPool *pool, gbAllocator backing, isize num_blocks, isize block_size, isize block_align); +GB_DEF void gb_pool_free(gbPool *pool); + + +GB_DEF gbAllocator gb_pool_allocator(gbPool *pool); +GB_DEF GB_ALLOCATOR_PROC(gb_pool_allocator_proc); + + + + + + + +//////////////////////////////////////////////////////////////// +// +// Char Functions +// +// + +GB_DEF char gb_char_to_lower(char c); +GB_DEF char gb_char_to_upper(char c); +GB_DEF b32 gb_char_is_space(char c); +GB_DEF b32 gb_char_is_digit(char c); +GB_DEF b32 gb_char_is_hex_digit(char c); +GB_DEF b32 gb_char_is_alpha(char c); +GB_DEF b32 gb_char_is_alphanumeric(char c); + + +// + +GB_DEF void gb_to_lower(char *str); +GB_DEF void gb_to_upper(char *str); + +GB_DEF isize gb_strlen(char const *str); +GB_DEF i32 gb_strcmp(char const *s1, char const *s2); +GB_DEF char * gb_strncpy(char *dest, char const *source, isize len); +GB_DEF i32 gb_strncmp(char const *s1, char const *s2, isize len); + +GB_DEF char const *gb_char_first_occurence(char const *s1, char c); +GB_DEF char const *gb_char_last_occurence(char const *s1, char c); + +GB_DEF void gb_cstr_concat(char *dest, isize dest_len, + char const *src_a, isize src_a_len, + char const *src_b, isize src_b_len); + + +//////////////////////////////////////////////////////////////// +// +// Windows UTF-8 Handling +// +// +// Windows doesn't handle 8 bit filenames well ('cause Micro$hit) + +GB_DEF char16 *gb_utf8_to_utf16(char16 *buffer, char *str, isize len); +GB_DEF char * gb_utf16_to_utf8(char *buffer, char16 *str, isize len); + + +//////////////////////////////////////////////////////////////// +// +// gbString - C Read-Only-Compatible +// +// + + +// Pascal like strings in C +typedef char *gbString; + + +// This is stored at the beginning of the string +// NOTE(bill): If you only need a small string, just use a standard c string or change the size +typedef struct gbStringHeader { + gbAllocator allocator; + isize length; + isize capacity; +} gbStringHeader; + +#define GB_STRING_HEADER(str) (cast(gbStringHeader *)(str) - 1) + +GB_DEF gbString gb_string_make(gbAllocator a, char const *str); +GB_DEF gbString gb_string_make_length(gbAllocator a, void const *str, isize num_bytes); +GB_DEF void gb_string_free(gbString str); + +GB_DEF gbString gb_string_duplicate(gbAllocator a, gbString const str); + +GB_DEF isize gb_string_length(gbString const str); +GB_DEF isize gb_string_capacity(gbString const str); +GB_DEF isize gb_string_available_space(gbString const str); + +GB_DEF void gb_string_clear(gbString str); + +GB_DEF gbString gb_string_append_string(gbString str, gbString const other); +GB_DEF gbString gb_string_append_string_length(gbString str, void const *other, isize num_bytes); +GB_DEF gbString gb_string_append_cstring(gbString str, char const *other); + +GB_DEF gbString gb_string_set(gbString str, char const *cstr); + +GB_DEF gbString gb_string_make_space_for(gbString str, isize add_len); +GB_DEF isize gb_string_allocation_size(gbString const str); + +GB_DEF b32 gb_strings_are_equal(gbString const lhs, gbString const rhs); + +GB_DEF gbString gb_string_trim(gbString str, char const *cut_set); +GB_DEF gbString gb_string_trim_space(gbString str); /* Whitespace ` \t\r\n\v\f` */ + + + +////////////////////////////////////////////////////////////////// +// +// Fixed Capacity Buffer (POD Types) +// +// + +#ifndef GB_BUFFER_TYPE +#define GB_BUFFER_TYPE +#endif + +#define gbBuffer(Type) struct { isize count, capacity; Type *data; } + +typedef gbBuffer(u8) gbByteBuffer; + +#define gb_buffer_init(x, allocator, cap) do { \ + void **data = cast(void **)&((x)->data); \ + gb_zero_struct(x); \ + (x)->capacity = (cap); \ + *data = gb_alloc((allocator), (cap)*gb_size_of((x)->data[0])); \ +} while (0) + +#define gb_buffer_free(x, allocator) do { gb_free(allocator, (x)->data); } while (0) + +#define gb_buffer_append(x, item) do { (x)->data[(x)->count++] = item; } while (0) + +#define gb_buffer_appendv(x, items, item_count) do { \ + GB_ASSERT(gb_size_of((items)[0]) == gb_size_of((x)->data[0])); \ + GB_ASSERT((x)->count+item_count <= (x)->capacity); \ + gb_memcopy((x)->data[a->count], (items), gb_size_of((x)->data[0])*(item_count)); \ + (x)->count += (item_count); \ +} while (0) + +#define gb_buffer_pop(x) do { GB_ASSERT((x)->count > 0); (x)->count--; } while (0) +#define gb_buffer_clear(x) do { (x)->count = 0; } while (0) + + + +////////////////////////////////////////////////////////////////// +// +// Dynamic Array (POD Types) +// +// + +// NOTE(bill): I know this is a macro hell but C is an old (and shit) language with no proper arrays +// Also why the fuck not?! It fucking works! And it has custom allocation, which is already better than C++! + +// NOTE(bill): Typedef every array or you get anonymous structures everywhere! +// e.g. typedef gbArray(int) gb_Int_Array; +#ifndef GB_ARRAY_TYPE +#define GB_ARRAY_TYPE + +#define gbArray(Type) struct { gbAllocator allocator; isize count, capacity; Type *data; } + + +typedef gbArray(void) gbVoidArray; // NOTE(bill): Useful for generic stuff + +// Available Procedures for gbArray(Type) +// gb_array_init +// gb_array_free +// gb_array_set_capacity +// gb_array_grow +// gb_array_append +// gb_array_appendv +// gb_array_pop +// gb_array_clear +// gb_array_resize +// gb_array_reserve +// + +#define gb_array_init(x, allocator_) do { gb_zero_struct(x); (x)->allocator = allocator_; } while (0) + +#define gb_array_free(x) do { \ + if ((x)->allocator.proc) { \ + gbAllocator a = (x)->allocator; \ + gb_free(a, (x)->data); \ + gb_array_init((x), a); \ + } \ +} while (0) + +#define gb_array_set_capacity(array, capacity) gb__array_set_capacity((array), (capacity), gb_size_of((array)->data[0])) +// NOTE(bill): Do not use the thing below directly, use the macro +GB_DEF void gb__array_set_capacity(void *array, isize capacity, isize element_size); + +#ifndef GB_ARRAY_GROW_FORMULA +#define GB_ARRAY_GROW_FORMULA(x) (2*(x) + 8) +#endif + +// TODO(bill): Decide on a decent growing formula for gbArray +#define gb_array_grow(x, min_capacity) do { \ + isize capacity = GB_ARRAY_GROW_FORMULA((x)->capacity); \ + if (capacity < (min_capacity)) \ + capacity = (min_capacity); \ + gb_array_set_capacity(x, capacity); \ +} while (0) + + +#define gb_array_append(x, item) do { \ + if ((x)->capacity < (x)->count+1) \ + gb_array_grow(x, 0); \ + (x)->data[(x)->count++] = (item); \ +} while (0) + +#define gb_array_appendv(x, items, item_count) do { \ + GB_ASSERT(gb_size_of((items)[0]) == gb_size_of((x)->data[0])); \ + if ((x)->capacity < (x)->count+(item_count)) \ + gb_array_grow(x, (x)->count+(item_count)); \ + gb_memcopy((x)->data[(x)->count], (items), gb_size_of((x)->data[0])*(item_count)); \ + (x)->count += (item_count); \ +} while (0) + + + +#define gb_array_pop(x) do { GB_ASSERT((x)->count > 0); (x)->count--; } while (0) +#define gb_array_clear(x) do { (x)->count = 0; } while (0) + +#define gb_array_resize(x, count) do { \ + if ((x)->capacity < (count)) \ + gb_array_grow(x, count); \ + (x)->count = (count); \ +} while (0) + + +#define gb_array_reserve(x, new_capacity) do { \ + if ((x)->capacity < (new_capacity)) \ + gb_array_set_capacity(x, new_capacity); \ +} while (0) + + +#endif /* GB_ARRAY_TYPE */ + + + + + + + + +//////////////////////////////////////////////////////////////// +// +// +// Hash_Table (POD Types) +// +// + +#if 0 +// TODO(bill): Hash Table and make it decent!!! + +// NOTE(bill): All keys are u64 +#ifndef GB_HASH_TABLE_TYPE +#define GB_HASH_TABLE_TYPE + +typedef struct gbHashTableEntry { + u64 key; + isize value; + isize next; +} gbHashTableEntry; + +#define GB_HASH_TABLE_HEADER \ + gbArray(isize) hashes; \ + gbArray(gbHashTableEntry) entries \ + +typedef struct gbHashTableHeader { + GB_HASH_TABLE_HEADER; +} gbHashTableHeader; + +#define gbHashTable(Type) struct { \ + GB_HASH_TABLE_HEADER; \ + gbArray(Type) data; \ +} + +typedef gbHashTable(void) gbHashTableVoid; // Generic Type + +#define gb_hash_table_init(h, allocator) do { \ + gb_array_init((h)->hashes, allocator); \ + gb_array_init((h)->entries, allocator); \ + gb_array_init((h)->data, allocator); \ +} while (0) + +#define gb_hash_table_free(h) do { \ + gb_free(&(h)->hashes); \ + gb_free(&(h)->entries); \ + gb_free(&(h)->data); \ +} while (0) + +#define gb_hash_table_header(h) (cast(gbHashTableHeader *)(h)) + + +// TODO(bill): Should the use have to pass the Hash Table and their array? + + + + +/* TODO(bill): Hash_Table Procs + gb_hash_table_init(h, allocator) + gb_hash_table_free(h) + + gb_hash_table_has(h, key) // Return false/true + gb_hash_table_get(h, key) // Return entries index + gb_hash_table_set(h, key, value) + gb_hash_table_remove(h, key) + gb_hash_table_reserve(h, capacity) + gb_hash_table_clear(h) + +*/ + + + +#endif + +#endif /* GB_HASH_TABLE_TYPE */ + + + +//////////////////////////////////////////////////////////////// +// +// File Handling +// +// +typedef u64 gbFileTime; + +typedef enum gbFileType { + GB_FILE_TYPE_READ = 1, + GB_FILE_TYPE_WRITE = 2, +} gbFileType; + +typedef struct gbFile { + void *handle; // File to fread/fwrite + char *path; + i64 size; + b32 is_open; + gbFileType type; + gbFileTime last_write_time; +} gbFile; + +typedef struct gbFileContents { + void *data; + isize size; +} gbFileContents; + + +GB_DEF b32 gb_file_create(gbFile *file, char const *filepath, ...); // TODO(bill): Give file permissions +GB_DEF b32 gb_file_open(gbFile *file, char const *filepath, ...); +GB_DEF b32 gb_file_close(gbFile *file); +GB_DEF b32 gb_file_read_at(gbFile *file, void *buffer, isize size, i64 offset); +GB_DEF b32 gb_file_write_at(gbFile *file, void const *buffer, isize size, i64 offset); +GB_DEF i64 gb_file_size(gbFile *file); +GB_DEF b32 gb_file_has_changed(gbFile *file); + +GB_DEF gbFileTime gb_file_last_write_time(char const *filepath, ...); + + +GB_DEF b32 gb_file_copy(char const *existing_filename, char const *new_filename, b32 fail_if_exists); +GB_DEF b32 gb_file_move(char const *existing_filename, char const *new_filename); + + +GB_DEF gbFileContents gb_read_entire_file_contents(gbAllocator a, b32 zero_terminate, char const *filepath, ...); + + +#ifndef GB_PATH_SEPARATOR + #if defined(GB_SYSTEM_WINDOWS) + #define GB_PATH_SEPARATOR '\\' + #else + #define GB_PATH_SEPARATOR '/' + #endif +#endif + +GB_DEF b32 gb_path_is_absolute(char const *path); +GB_DEF b32 gb_path_is_relative(char const *path); +GB_DEF b32 gb_path_is_root(char const *path); +GB_DEF char const *gb_path_base_name(char const *path); +GB_DEF char const *gb_path_extension(char const *path); + + +GB_DEF void gb_exit(u32 code); + + +//////////////////////////////////////////////////////////////// +// +// DLL Handling +// +// + +typedef void *gbDllHandle; +typedef void (*gbDllProc)(void); + +GB_DEF gbDllHandle gb_dll_load(char const *filepath, ...); +GB_DEF void gb_dll_unload(gbDllHandle dll); +GB_DEF gbDllProc gb_dll_proc_address(gbDllHandle dll, char const *proc_name); + + +//////////////////////////////////////////////////////////////// +// +// Time +// +// + +typedef struct gbDate { + i32 year; + i32 month; // 1 - Janurary, ... 12 - December + i32 day; // 1 - 31 + i32 day_of_week; // 0 - Sunday, ... 6 - Saturday + i32 hour; // 0 - 23 + i32 minute; // 0 - 59 + i32 second; // 0 - 60 (leap seconds) + i32 milliseconds; // 0 - 999 +} gbDate; + + +GB_DEF u64 gb_rdtsc(void); +GB_DEF f64 gb_time_now(void); + +GB_DEF void gb_get_system_date(gbDate *date); +GB_DEF void gb_get_local_date(gbDate *date); + + + +//////////////////////////////////////////////////////////////// +// +// Colour Type +// It's quite useful +// + +typedef union gbColour { + u32 rgba; // NOTE(bill): 0xaabbggrr + struct { u8 r, g, b, a; }; + u8 e[4]; +} gbColour; +GB_STATIC_ASSERT(gb_size_of(gbColour) == gb_size_of(u32)); + +gb_inline gbColour +gb_colour(f32 r, f32 g, f32 b, f32 a) +{ + gbColour result; + result.r = cast(u8)(gb_clamp01(r) * 255.0f); + result.g = cast(u8)(gb_clamp01(g) * 255.0f); + result.b = cast(u8)(gb_clamp01(b) * 255.0f); + result.a = cast(u8)(gb_clamp01(a) * 255.0f); + return result; +} + +gb_global gbColour const GB_COLOUR_WHITE = {0xffffffff}; +gb_global gbColour const GB_COLOUR_GREY = {0xff808080}; +gb_global gbColour const GB_COLOUR_BLACK = {0xff000000}; + +gb_global gbColour const GB_COLOUR_RED = {0xff0000ff}; +gb_global gbColour const GB_COLOUR_ORANGE = {0xff0099ff}; +gb_global gbColour const GB_COLOUR_YELLOW = {0xff00ffff}; +gb_global gbColour const GB_COLOUR_GREEN = {0xff00ff00}; +gb_global gbColour const GB_COLOUR_CYAN = {0xffffff00}; +gb_global gbColour const GB_COLOUR_BLUE = {0xffff0000}; +gb_global gbColour const GB_COLOUR_VIOLET = {0xffff007f}; +gb_global gbColour const GB_COLOUR_MAGENTA = {0xffff00ff}; + + + +#if defined(__cplusplus) +} +#endif + +#endif /* GB_INCLUDE_GB_H */ + + + + + + +//////////////////////////////////////////////////////////////// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// Implementation +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +//////////////////////////////////////////////////////////////// + +#if defined(GB_IMPLEMENTATION) && !defined(GB_IMPLEMENTATION_DONE) +#define GB_IMPLEMENTATION_DONE + +#if defined(__cplusplus) +extern "C" { +#endif + + +i32 +gb_printf(char const *fmt, ...) +{ + i32 res; + va_list va; + va_start(va, fmt); + res = gb_fprintf_va(stdout, fmt, va); + va_end(va); + return res; +} + + +i32 +gb_fprintf(FILE *f, char const *fmt, ...) +{ + i32 res; + va_list va; + va_start(va, fmt); + res = gb_fprintf_va(stdout, fmt, va); + va_end(va); + return res; +} + +char * +gb_sprintf(char const *fmt, ...) +{ + va_list va; + char *str; + va_start(va, fmt); + str = gb_sprintf_va(fmt, va); + va_end(va); + return str; +} + +i32 +gb_snprintf(char *str, isize n, char const *fmt, ...) +{ + i32 res; + va_list va; + va_start(va, fmt); + res = gb_snprintf_va(str, n, fmt, va); + va_end(va); + return res; +} + + +gb_inline i32 gb_printf_va(char const *fmt, va_list va) { return gb_fprintf_va(stdout, fmt, va); } +gb_inline i32 gb_fprintf_va(FILE *f, char const *fmt, va_list va) { return vfprintf(f, fmt, va); } + +gb_inline char * +gb_sprintf_va(char const *fmt, va_list va) +{ + gb_local_persist char buffer[1024]; + gb_snprintf_va(buffer, gb_size_of(buffer), fmt, va); + return buffer; +} + +gb_inline i32 +gb_snprintf_va(char *str, isize n, char const *fmt, va_list va) +{ + i32 res; +#if defined(_WIN32) + res = _vsnprintf(str, n, fmt, va); +#else + res = vsnprintf(str, n, fmt, va) +#endif + if (n) str[n-1] = 0; + // NOTE(bill): Unix returns length output would require, Windows returns negative when truncated. + return (res >= n || res < 0) ? -1 : res; +} + + +gb_inline i32 gb_println(char const *str) { return gb_fprintln(stdout, str); } + +gb_inline i32 +gb_fprintln(FILE *f, char const *str) +{ + i32 res; + res = gb_fprintf(f, str); + gb_fprintf(f, "\n"); + res++; + return res; +} + + + + + +void +gb_assert_handler(char const *condition, char const *file, i64 line, char const *msg, ...) +{ + gb_fprintf(stderr, "%s:%lld: Assert Failure: ", file, cast(long long)line); + if (condition) + gb_fprintf(stderr, "`%s` ", condition); + + if (msg) { + va_list va; + va_start(va, msg); + gb_fprintf(stderr, msg, va); + va_end(va); + } + + gb_fprintf(stderr, "\n"); +} + -#if defined(GB_IMPLEMENTATION) gb_inline void * -gb_align_forward(void *ptr, usize align) +gb_align_forward(void *ptr, isize align) { uintptr p; - usize modulo; + isize modulo; - GB_ASSERT(GB_IS_POWER_OF_TWO(align)); + GB_ASSERT(gb_is_power_of_two(align)); p = cast(uintptr)ptr; modulo = p % align; @@ -246,193 +1517,376 @@ gb_align_forward(void *ptr, usize align) return cast(void *)p; } -gb_inline void gb_zero_size(void *ptr, usize size) { memset(ptr, 0, size); } +gb_inline void *gb_pointer_add(void *ptr, isize bytes) { return cast(void *)(cast(u8 *)ptr + bytes); } +gb_inline void *gb_pointer_sub(void *ptr, isize bytes) { return cast(void *)(cast(u8 *)ptr - bytes); } -#endif // GB_IMPLEMENTATION + +gb_inline void gb_zero_size(void *ptr, isize size) { gb_memset(ptr, 0, size); } + + +gb_inline void *gb_memcopy(void *dest, void const *source, isize size) { return memcpy(dest, source, size); } +gb_inline void *gb_memmove(void *dest, void const *source, isize size) { return memmove(dest, source, size); } +gb_inline void *gb_memset(void *data, u8 byte_value, isize size) { return memset(data, byte_value, size); } +gb_inline void *gb_alloc_align(gbAllocator a, isize size, isize alignment) { return a.proc(a.data, GB_ALLOCATION_ALLOC, size, alignment, NULL, 0, 0); } +gb_inline void *gb_alloc(gbAllocator a, isize size) { return gb_alloc_align(a, size, GB_DEFAULT_MEMORY_ALIGNMENT); } +gb_inline void gb_free(gbAllocator a, void *ptr) { a.proc(a.data, GB_ALLOCATION_FREE, 0, 0, ptr, 0, 0); } +gb_inline void gb_free_all(gbAllocator a) { a.proc(a.data, GB_ALLOCATION_FREE_ALL, 0, 0, NULL, 0, 0); } +gb_inline void *gb_resize(gbAllocator a, void *ptr, isize old_size, isize new_size) { return gb_resize_align(a, ptr, old_size, new_size, GB_DEFAULT_MEMORY_ALIGNMENT); } +gb_inline void *gb_resize_align(gbAllocator a, void *ptr, isize old_size, isize new_size, isize alignment) { return a.proc(a.data, GB_ALLOCATION_RESIZE, new_size, alignment, ptr, old_size, 0); }; +gb_inline void *gb_alloc_copy(gbAllocator a, void const *src, isize size) { return gb_memcopy(gb_alloc(a, size), src, size); } +gb_inline void *gb_alloc_copy_align(gbAllocator a, void const *src, isize size, isize alignment) { return gb_memcopy(gb_alloc_align(a, size, alignment), src, size); } - -//////////////////////////////// -// // -// Custom Allocation // -// // -//////////////////////////////// - -typedef enum gb_Allocation_Type +gb_inline char * +gb_alloc_cstring(gbAllocator a, char const *str) { - GB_ALLOCATION_TYPE_ALLOC, - GB_ALLOCATION_TYPE_FREE, - GB_ALLOCATION_TYPE_FREE_ALL, - GB_ALLOCATION_TYPE_RESIZE, -} gb_Allocation_Type; + char *result; + isize len = gb_strlen(str); + result = cast(char *)gb_alloc_copy(a, str, len+1); + result[len] = '\0'; + return result; +} -#define GB_ALLOCATOR_PROCEDURE(name) void *name(void *allocator_data, gb_Allocation_Type type, usize size, usize alignment, void *old_memory, usize old_size, u32 options) -typedef GB_ALLOCATOR_PROCEDURE(gb_Allocator_Procedure); -typedef struct gb_Allocator + + + +//////////////////////////////////////////////////////////////// +// +// Concurrency +// + +#if defined(_MSC_VER) +gb_inline i32 +gb_atomic32_load(gbAtomic32 const volatile *a) { - gb_Allocator_Procedure *procedure; - void *data; -} gb_Allocator; + return a->value; +} +gb_inline void +gb_atomic32_store(gbAtomic32 volatile *a, i32 value) +{ + a->value = value; +} -#ifndef GB_DEFAULT_ALIGNMENT -#define GB_DEFAULT_ALIGNMENT 8 +gb_inline i32 +gb_atomic32_compare_exchange_strong(gbAtomic32 volatile *a, i32 expected, i32 desired) +{ + return _InterlockedCompareExchange(cast(long volatile *)a, desired, expected); +} + +gb_inline i32 +gb_atomic32_exchanged(gbAtomic32 volatile *a, i32 desired) +{ + return _InterlockedExchange(cast(long volatile *)a, desired); +} + +gb_inline i32 +gb_atomic32_fetch_add(gbAtomic32 volatile *a, i32 operand) +{ + return _InterlockedExchangeAdd(cast(long volatile *)a, operand); +} + +gb_inline i32 +gb_atomic32_fetch_and(gbAtomic32 volatile *a, i32 operand) +{ + return _InterlockedAnd(cast(long volatile *)a, operand); +} + +gb_inline i32 +gb_atomic32_fetch_or(gbAtomic32 volatile *a, i32 operand) +{ + return _InterlockedOr(cast(long volatile *)a, operand); +} + + +gb_inline i64 +gb_atomic64_load(gbAtomic64 const volatile *a) +{ +#if defined(GB_ARCH_64_BIT) + return a->value; +#else + // NOTE(bill): The most compatible way to get an atomic 64-bit load on x86 is with cmpxchg8b + i64 result; + __asm { + mov esi, a; + mov ebx, eax; + mov ecx, edx; + lock cmpxchg8b [esi]; + mov dword ptr result, eax; + mov dword ptr result[4], edx; + } + return result; +#endif +} + +gb_inline void +gb_atomic64_store(gbAtomic64 volatile *a, i64 value) +{ +#if defined(GB_ARCH_64_BIT) + a->value = value; +#else + // NOTE(bill): The most compatible way to get an atomic 64-bit store on x86 is with cmpxchg8b + __asm { + mov esi, a; + mov ebx, dword ptr value; + mov ecx, dword ptr value[4]; + retry: + cmpxchg8b [esi]; + jne retry; + } +#endif +} + +gb_inline i64 +gb_atomic64_compare_exchange_strong(gbAtomic64 volatile *a, i64 expected, i64 desired) +{ + return _InterlockedCompareExchange64(cast(i64 volatile *)a, desired, expected); +} + +gb_inline i64 +gb_atomic64_exchanged(gbAtomic64 volatile *a, i64 desired) +{ +#if defined(GB_ARCH_64_BIT) + return _InterlockedExchange64(cast(i64 volatile *)a, desired); +#else + i64 expected = a->value; + for (;;) { + i64 original = _InterlockedCompareExchange64(cast(i64 volatile *)a, desired, expected); + if (original == expected) + return original; + expected = original; + } +#endif +} + +gb_inline i64 +gb_atomic64_fetch_add(gbAtomic64 volatile *a, i64 operand) +{ +#if defined(GB_ARCH_64_BIT) + return _InterlockedExchangeAdd64(cast(i64 volatile *)a, operand); +#else + i64 expected = a->value; + for (;;) { + i64 original = _InterlockedExchange64(cast(i64 volatile *)a, expected + operand, expected); + if (original == expected) + return original; + expected = original; + } +#endif +} + +gb_inline i64 +gb_atomic64_fetch_and(gbAtomic64 volatile *a, i64 operand) +{ +#if defined(GB_ARCH_64_BIT) + return _InterlockedAnd64(cast(i64 volatile *)a, operand); +#else + i64 expected = a->value; + for (;;) { + i64 original = _InterlockedCompareExchange64(cast(i64 volatile *)a, expected & operand, expected); + if (original == expected) + return original; + expected = original; + } +#endif +} + +gb_inline i64 +gb_atomic64_fetch_or(gbAtomic64 volatile *a, i64 operand) +{ +#if defined(GB_ARCH_64_BIT) + return _InterlockedAnd64(cast(i64 volatile *)a, operand); +#else + i64 expected = object->nonatomic; + for (;;) { + i64 original = _InterlockedCompareExchange64(cast(i64 volatile *)a, expected | operand, expected); + if (original == expected) + return original; + expected = original; + } +#endif +} + +#else +#error Implement atomics for this platform #endif -gb_inline void *gb_alloc_align(gb_Allocator a, usize size, usize alignment) { return a.procedure(a.data, GB_ALLOCATION_TYPE_ALLOC, size, alignment, NULL, 0, 0); } -gb_inline void *gb_alloc(gb_Allocator a, usize size) { return gb_alloc_align(a, size, GB_DEFAULT_ALIGNMENT); } -gb_inline void gb_free(gb_Allocator a, void *ptr) { a.procedure(a.data, GB_ALLOCATION_TYPE_FREE, 0, 0, ptr, 0, 0); } -gb_inline void gb_free_all(gb_Allocator a) { a.procedure(a.data, GB_ALLOCATION_TYPE_FREE_ALL, 0, 0, NULL, 0, 0); } -gb_inline void *gb_resize(gb_Allocator a, void *ptr, usize new_size) { return a.procedure(a.data, GB_ALLOCATION_TYPE_RESIZE, new_size, 0, ptr, 0, 0); } - -gb_inline void *gb_alloc_copy(gb_Allocator a, void* src, usize size) { return memcpy(gb_alloc(a, size), src, size); } -gb_inline void *gb_alloc_align_copy(gb_Allocator a, void* src, usize size, usize alignment) { return memcpy(gb_alloc_align(a, size, alignment), src, size); } - -#define gb_alloc_struct(allocator, Type) (Type *)gb_alloc_align(allocator, sizeof(Type)) -#define gb_alloc_array(allocator, Type, count) (Type *)gb_alloc(allocator, sizeof(Type) * (count)) -typedef struct gb_Arena -{ - gb_Allocator backing; - void *physical_start; - usize total_size; - usize total_allocated_count; - usize prev_allocated_count; - u32 temp_count; -} gb_Arena; - -void gb_init_arena_from_memory(gb_Arena *arena, void *start, usize size); -void gb_init_arena_from_allocator(gb_Arena *arena, gb_Allocator backing, usize size); -void gb_free_arena(gb_Arena *arena); - -gb_Allocator gb_make_arena_allocator(gb_Arena *arena); -GB_ALLOCATOR_PROCEDURE(gb_arena_allocator_procedure); +gb_inline void gb_mutex_init(gbMutex *m) { m->win32_handle = CreateMutex(0, false, 0); } +gb_inline void gb_mutex_destroy(gbMutex *m) { CloseHandle(m->win32_handle); } +gb_inline void gb_mutex_lock(gbMutex *m) { WaitForSingleObject(m->win32_handle, INFINITE); } +gb_inline b32 gb_mutex_try_lock(gbMutex *m) { return WaitForSingleObject(m->win32_handle, 0) == WAIT_OBJECT_0; } +gb_inline void gb_mutex_unlock(gbMutex *m) { ReleaseMutex(m->win32_handle); } -typedef struct gb_Temp_Arena_Memory -{ - gb_Arena *arena; - usize original_count; -} gb_Temp_Arena_Memory; - -gb_Temp_Arena_Memory gb_begin_temp_arena_memory(gb_Arena *arena); -void gb_end_temp_arena_memory(gb_Temp_Arena_Memory tmp_mem); - - - -typedef struct gb_Pool -{ - gb_Allocator backing; - - void *physical_start; - void *free_list; - - usize block_size; - usize block_align; - usize total_size; -} gb_Pool; - -void gb_init_pool(gb_Pool *pool, gb_Allocator backing, usize num_blocks, usize block_size); -void gb_init_pool_align(gb_Pool *pool, gb_Allocator backing, usize num_blocks, usize block_size, usize block_align); -void gb_free_pool(gb_Pool *pool); - - -gb_Allocator gb_make_pool_allocator(gb_Pool *pool); -GB_ALLOCATOR_PROCEDURE(gb_pool_allocator_procedure); - - - - -#if defined(GB_IMPLEMENTATION) gb_inline void -gb_init_arena_from_memory(gb_Arena *arena, void *start, usize size) +gb_semaphore_init(gbSemaphore *s) { - arena->backing.procedure = NULL; - arena->backing.data = NULL; - arena->physical_start = start; - arena->total_size = size; - arena->total_allocated_count = 0; - arena->temp_count = 0; + s->win32_handle = CreateSemaphore(NULL, 0, I32_MAX, NULL); + GB_ASSERT_MSG(s->win32_handle != NULL, "CreateSemaphore: GetLastError = %d", GetLastError()); } gb_inline void -gb_init_arena_from_allocator(gb_Arena *arena, gb_Allocator backing, usize size) +gb_semaphore_destroy(gbSemaphore *s) { - arena->backing = backing; - arena->physical_start = gb_alloc(backing, size); - arena->total_size = size; - arena->total_allocated_count = 0; - arena->temp_count = 0; + BOOL err = CloseHandle(s->win32_handle); + GB_ASSERT_MSG(err != 0, "CloseHandle: GetLastError = %d", GetLastError()); } gb_inline void -gb_free_arena(gb_Arena *arena) +gb_semaphore_post(gbSemaphore *s, i32 count) { - if (arena->backing.procedure) { - gb_free(arena->backing, arena->physical_start); - arena->physical_start = NULL; - } + BOOL err = ReleaseSemaphore(s->win32_handle, count, NULL); + GB_ASSERT_MSG(err != 0, "ReleaseSemaphore: GetLastError = %d", GetLastError()); +} + +gb_inline void +gb_semaphore_wait(gbSemaphore *s) +{ + DWORD result = WaitForSingleObject(s->win32_handle, INFINITE); + GB_ASSERT_MSG(result == WAIT_OBJECT_0, "WaitForSingleObject: GetLastError = %d", GetLastError()); +} + + +void +gb_thread_init(gbThread *t) +{ + gb_zero_struct(t); + t->win32_handle = INVALID_HANDLE_VALUE; + gb_semaphore_init(&t->semaphore); +} + +void +gb_thread_destory(gbThread *t) +{ + if (t->is_running) gb_thread_join(t); + gb_semaphore_destroy(&t->semaphore); +} + + +gb_internal void +gb__thread_run(gbThread *t) +{ + gb_semaphore_post(&t->semaphore, 1); + t->proc(t->data); +} + +#if defined(GB_SYSTEM_WINDOWS) + gb_internal DWORD WINAPI gb__thread_proc(void *arg) { gb__thread_run(cast(gbThread *)arg); return 0; } +#else + gb_internal void *gb__thread_proc(void *arg) { gb__thread_run(cast(gbThread *)arg); return NULL; } +#endif + +gb_inline void gb_thread_start(gbThread *t, gbThreadProc *proc, void *data) { gb_thread_start_with_stack(t, proc, data, 0); } + +gb_inline void +gb_thread_start_with_stack(gbThread *t, gbThreadProc *proc, void *data, isize stack_size) +{ + GB_ASSERT(!t->is_running); + GB_ASSERT(proc != NULL); + t->proc = proc; + t->data = data; + t->stack_size = stack_size; + + t->win32_handle = CreateThread(NULL, stack_size, gb__thread_proc, t, 0, NULL); + GB_ASSERT_MSG(t->win32_handle != NULL, "CreateThread: GetLastError = %d", GetLastError()); + + t->is_running = true; + gb_semaphore_wait(&t->semaphore); +} + +gb_inline void +gb_thread_join(gbThread *t) +{ + if (!t->is_running) return; + + WaitForSingleObject(t->win32_handle, INFINITE); + CloseHandle(t->win32_handle); + t->win32_handle = INVALID_HANDLE_VALUE; + t->is_running = false; +} + +gb_inline b32 gb_thread_is_running(gbThread const *t) { return t->is_running != 0; } + +gb_inline u32 +gb_thread_current_id(void) +{ + u32 thread_id; +#if defined(GB_SYSTEM_WINDOWS) + thread_id = GetCurrentThreadId(); +#elif defined(GB_SYSTEM_OSX) && defined(GB_ARCH_64_BIT) + asm("mov %%gs:0x00,%0" : "=r"(thread_id)); +#elif defined(GB_ARCH_32_BIT) + asm("mov %%gs:0x08,%0" : "=r"(thread_id)); +#elif defined(GB_ARCH_64_BIT) + asm("mov %%gs:0x10,%0" : "=r"(thread_id)); +#else + #error Unsupported architecture for thread::current_id() +#endif + + return thread_id; } -gb_inline gb_Allocator -gb_make_arena_allocator(gb_Arena *arena) + + +gb_inline gbAllocator +gb_heap_allocator(void) { - gb_Allocator allocator; - allocator.procedure = gb_arena_allocator_procedure; - allocator.data = arena; - return allocator; + gbAllocator a; + a.proc = gb_heap_allocator_proc; + a.data = NULL; + return a; } -GB_ALLOCATOR_PROCEDURE(gb_arena_allocator_procedure) +GB_ALLOCATOR_PROC(gb_heap_allocator_proc) { - gb_Arena *arena = cast(gb_Arena *)allocator_data; - - GB_UNUSED(options); - GB_UNUSED(old_size); - switch (type) { - case GB_ALLOCATION_TYPE_ALLOC: { + case GB_ALLOCATION_ALLOC: { void *ptr; - usize actual_size = size + alignment; + isize actual_size = size + alignment; + ptr = gb_align_forward(gb_malloc(actual_size), alignment); - // NOTE(bill): Out of memory - if (arena->total_allocated_count + actual_size > cast(usize)arena->total_size) - return NULL; - - ptr = gb_align_forward(cast(u8 *)arena->physical_start + arena->total_allocated_count, alignment); - arena->prev_allocated_count = arena->total_allocated_count; - arena->total_allocated_count += actual_size; return ptr; } break; - case GB_ALLOCATION_TYPE_FREE: { - // NOTE(bill): Free all at once - // NOTE(bill): Use Temp_Arena_Memory if you want to free a block + case GB_ALLOCATION_FREE: { + gb_mfree(old_memory); } break; - case GB_ALLOCATION_TYPE_FREE_ALL: - arena->total_allocated_count = 0; + case GB_ALLOCATION_FREE_ALL: break; - case GB_ALLOCATION_TYPE_RESIZE: { - // TODO(bill): Check if ptr is at the top - void *ptr = gb_alloc_align(gb_make_arena_allocator(arena), size, alignment); - memcpy(ptr, old_memory, size); - return ptr; + case GB_ALLOCATION_RESIZE: { + // TODO(bill): Check if ptr is on top of stack and just extend + gbAllocator a = gb_heap_allocator(); + if (!old_memory) return gb_alloc_align(a, size, alignment); + + if (size < old_size) + size = old_size; + + if (old_size == size) { + return old_memory; + } else { + void *new_memory = gb_alloc_align(a, size, alignment); + if (!new_memory) return NULL; + gb_memmove(new_memory, old_memory, gb_min(size, old_size)); + gb_free(a, old_memory); + return new_memory; + } } break; } @@ -440,90 +1894,228 @@ GB_ALLOCATOR_PROCEDURE(gb_arena_allocator_procedure) } -gb_inline gb_Temp_Arena_Memory -gb_begin_temp_arena_memory(gb_Arena *arena) + + + + + + +gb_inline void +gb_arena_init_from_memory(gbArena *arena, void *start, isize size) { - gb_Temp_Arena_Memory tmp; + arena->backing.proc = NULL; + arena->backing.data = NULL; + arena->physical_start = start; + arena->total_size = size; + arena->total_allocated = 0; + arena->temp_count = 0; +} + +gb_inline void +gb_arena_init_from_allocator(gbArena *arena, gbAllocator backing, isize size) +{ + arena->backing = backing; + arena->physical_start = gb_alloc(backing, size); // NOTE(bill): Uses default alignment + arena->total_size = size; + arena->total_allocated = 0; + arena->temp_count = 0; +} + +gb_inline void gb_arena_init_subarena(gbArena *arena, gbArena *parent_arena, isize size) { gb_arena_init_from_allocator(arena, gb_arena_allocator(parent_arena), size); } + + +gb_inline void +gb_arena_free(gbArena *arena) +{ + if (arena->backing.proc) { + gb_free(arena->backing, arena->physical_start); + arena->physical_start = NULL; + } +} + + +gb_inline isize +gb_arena_alignment_of(gbArena *arena, isize alignment) +{ + isize alignment_offset, result_pointer, mask; + GB_ASSERT(gb_is_power_of_two(alignment)); + + alignment_offset = 0; + result_pointer = cast(isize)arena->physical_start + arena->total_allocated; + mask = alignment - 1; + if (result_pointer & mask) + alignment_offset = alignment - (result_pointer & mask); + + return alignment_offset; +} + +gb_inline isize +gb_arena_size_remaining(gbArena *arena, isize alignment) +{ + isize result = arena->total_size - (arena->total_allocated + gb_arena_alignment_of(arena, alignment)); + return result; +} + +gb_inline void gb_arena_check(gbArena *arena) { GB_ASSERT(arena->temp_count == 0); } + + + + + + +gb_inline gbAllocator +gb_arena_allocator(gbArena *arena) +{ + gbAllocator allocator; + allocator.proc = gb_arena_allocator_proc; + allocator.data = arena; + return allocator; +} + + +GB_ALLOCATOR_PROC(gb_arena_allocator_proc) +{ + gbArena *arena = cast(gbArena *)allocator_data; + + gb_unused(options); + gb_unused(old_size); + + switch (type) { + case GB_ALLOCATION_ALLOC: { + void *ptr; + isize actual_size = size + alignment; + + // NOTE(bill): Out of memory + if (arena->total_allocated + actual_size > cast(isize)arena->total_size) + return NULL; + + ptr = gb_align_forward(gb_pointer_add(arena->physical_start, arena->total_allocated), alignment); + arena->total_allocated += actual_size; + return ptr; + } break; + + case GB_ALLOCATION_FREE: + // NOTE(bill): Free all at once + // NOTE(bill): Use Temp_Arena_Memory if you want to free a block + // TODO(bill): Free it if it's on top of the stack + break; + + case GB_ALLOCATION_FREE_ALL: + arena->total_allocated = 0; + break; + + case GB_ALLOCATION_RESIZE: { + // TODO(bill): Check if ptr is on top of stack and just extend + gbAllocator a = gb_arena_allocator(arena); + if (!old_memory) return gb_alloc_align(a, size, alignment); + + if (size < old_size) + size = old_size; + + if (old_size == size) { + return old_memory; + } else { + void *new_memory = gb_alloc_align(a, size, alignment); + if (!new_memory) return NULL; + gb_memmove(new_memory, old_memory, gb_min(size, old_size)); + gb_free(a, old_memory); + return new_memory; + } + } break; + } + + return NULL; // NOTE(bill): Default return value +} + + +gb_inline gbTempArenaMemory +gb_temp_arena_memory_begin(gbArena *arena) +{ + gbTempArenaMemory tmp; tmp.arena = arena; - tmp.original_count = arena->total_allocated_count; + tmp.original_count = arena->total_allocated; arena->temp_count++; return tmp; } gb_inline void -gb_end_temp_arena_memory(gb_Temp_Arena_Memory tmp) +gb_temp_arena_memory_end(gbTempArenaMemory tmp) { - GB_ASSERT(tmp.arena->total_allocated_count >= tmp.original_count); + GB_ASSERT(tmp.arena->total_allocated >= tmp.original_count); GB_ASSERT(tmp.arena->temp_count > 0); - tmp.arena->total_allocated_count = tmp.original_count; + tmp.arena->total_allocated = tmp.original_count; tmp.arena->temp_count--; } -void -gb_init_pool(gb_Pool *pool, gb_Allocator backing, usize num_blocks, usize block_size) +gb_inline void +gb_pool_init(gbPool *pool, gbAllocator backing, isize num_blocks, isize block_size) { - gb_init_pool_align(pool, backing, num_blocks, block_size, GB_DEFAULT_ALIGNMENT); + gb_pool_init_align(pool, backing, num_blocks, block_size, GB_DEFAULT_MEMORY_ALIGNMENT); } void -gb_init_pool_align(gb_Pool *pool, gb_Allocator backing, usize num_blocks, usize block_size, usize block_align) +gb_pool_init_align(gbPool *pool, gbAllocator backing, isize num_blocks, isize block_size, isize block_align) { - memset(pool, 0, sizeof(gb_Pool)); + isize actual_block_size, pool_size, block_index; + void *data, *curr; + uintptr *end; + + gb_zero_struct(pool); pool->backing = backing; pool->block_size = block_size; pool->block_align = block_align; - usize actual_block_size = block_size + block_align; - usize pool_size = num_blocks * actual_block_size; + actual_block_size = block_size + block_align; + pool_size = num_blocks * actual_block_size; - u8 *data = cast(u8 *)gb_alloc_align(backing, pool_size, block_align); + data = gb_alloc_align(backing, pool_size, block_align); - // Init intrusive freelist - u8 *curr = data; - for (usize block_index = 0; block_index < num_blocks-1; block_index++) { + // NOTE(bill): Init intrusive freelist + curr = data; + for (block_index = 0; block_index < num_blocks-1; block_index++) { uintptr *next = cast(uintptr *)curr; *next = cast(uintptr)curr + actual_block_size; - curr += actual_block_size; + curr = gb_pointer_add(curr, actual_block_size); } - uintptr *end = cast(uintptr*)curr; + end = cast(uintptr *)curr; *end = cast(uintptr)NULL; pool->physical_start = data; pool->free_list = data; } -void -gb_free_pool(gb_Pool *pool) +gb_inline void +gb_pool_free(gbPool *pool) { - if (pool->backing.procedure) { + if (pool->backing.proc) { gb_free(pool->backing, pool->physical_start); } } -gb_Allocator -gb_make_pool_allocator(gb_Pool *pool) +gb_inline gbAllocator +gb_pool_allocator(gbPool *pool) { - gb_Allocator allocator; - allocator.procedure = gb_pool_allocator_procedure; + gbAllocator allocator; + allocator.proc = gb_pool_allocator_proc; allocator.data = pool; return allocator; } -GB_ALLOCATOR_PROCEDURE(gb_pool_allocator_procedure) +GB_ALLOCATOR_PROC(gb_pool_allocator_proc) { - gb_Pool *pool = cast(gb_Pool *)allocator_data; + gbPool *pool = cast(gbPool *)allocator_data; - GB_UNUSED(options); - GB_UNUSED(old_size); + gb_unused(options); + gb_unused(old_size); switch (type) { - case GB_ALLOCATION_TYPE_ALLOC: { + case GB_ALLOCATION_ALLOC: { uintptr next_free; void *ptr; GB_ASSERT(size == pool->block_size); @@ -537,7 +2129,7 @@ GB_ALLOCATOR_PROCEDURE(gb_pool_allocator_procedure) return ptr; } break; - case GB_ALLOCATION_TYPE_FREE: { + case GB_ALLOCATION_FREE: { uintptr *next; if (old_memory == NULL) return NULL; @@ -547,13 +2139,13 @@ GB_ALLOCATOR_PROCEDURE(gb_pool_allocator_procedure) pool->total_size -= pool->block_size; } break; - case GB_ALLOCATION_TYPE_FREE_ALL: { + case GB_ALLOCATION_FREE_ALL: // TODO(bill): - } break; + break; - case GB_ALLOCATION_TYPE_RESIZE: { + case GB_ALLOCATION_RESIZE: // NOTE(bill): Cannot resize - } break; + break; } return NULL; @@ -565,89 +2157,212 @@ GB_ALLOCATOR_PROCEDURE(gb_pool_allocator_procedure) - - - - -#endif // GB_IMPLEMENTATION - - -//////////////////////////////// -// // -// gb_String - C Compatible // -// // -//////////////////////////////// - - -// Pascal like strings in C -typedef char *gb_String; - -#ifndef GB_STRING_SIZE -#define GB_STRING_SIZE -typedef u32 gb_String_Size; -#endif - - -// This is stored at the beginning of the string -// NOTE(bill): It is (2*sizeof(gb_String_Size) + 2*sizeof(void*)) (default: 16B (32bit), 24B (64bit)) -// NOTE(bill): If you only need a small string, just use a standard c string -typedef struct gb_String_Header +gb_inline char +gb_char_to_lower(char c) { - gb_Allocator allocator; - gb_String_Size length; - gb_String_Size capacity; -} gb_String_Header; + if (c >= 'A' && c <= 'Z') + return 'a' + (c - 'A'); + return c; +} -#define GB_STRING_HEADER(str) (cast(gb_String_Header *)str - 1) - -gb_String gb_string_make(gb_Allocator a, char const *str); -gb_String gb_string_make_length(gb_Allocator a, void const *str, gb_String_Size num_bytes); -void gb_string_free(gb_String str); - -gb_String gb_string_duplicate(gb_Allocator a, gb_String const str); - -gb_String_Size gb_string_length(gb_String const str); -gb_String_Size gb_string_capacity(gb_String const str); -gb_String_Size gb_string_available_space(gb_String const str); - -void gb_string_clear(gb_String str); - -gb_String gb_string_append_string(gb_String str, gb_String const other); -gb_String gb_string_append_string_length(gb_String str, void const *other, gb_String_Size num_bytes); -gb_String gb_string_append_cstring(gb_String str, char const *other); - -gb_String gb_string_set(gb_String str, char const *cstr); - -gb_String gb_string_make_space_for(gb_String str, gb_String_Size add_len); -gb_String_Size gb_string_allocation_size(gb_String const str); - -b32 gb_strings_are_equal(gb_String const lhs, gb_String const rhs); - -gb_String gb_string_trim(gb_String str, char const *cut_set); -gb_String gb_string_trim_space(gb_String str); /* Whitespace ` \t\r\n\v\f` */ - - -#if defined(GB_IMPLEMENTATION) - -gb_inline void gb__string_set_length(gb_String str, gb_String_Size len) { GB_STRING_HEADER(str)->length = len; } -gb_inline void gb__string_set_capacity(gb_String str, gb_String_Size cap) { GB_STRING_HEADER(str)->capacity = cap; } - - -gb_inline gb_String -gb_string_make(gb_Allocator a, char const *str) +gb_inline char +gb_char_to_upper(char c) { - gb_String_Size len = cast(gb_String_Size)(str ? strlen(str) : 0); + if (c >= 'a' && c <= 'z') + return 'A' + (c - 'a'); + return c; +} + +gb_inline b32 +gb_char_is_space(char c) +{ + if (c == ' ' || + c == '\t' || + c == '\n' || + c == '\r' || + c == '\f' || + c == '\v') + return true; + return false; +} + +gb_inline b32 +gb_char_is_digit(char c) +{ + if (c >= '0' && c <= '9') + return true; + return false; +} + +gb_inline b32 +gb_char_is_hex_digit(char c) +{ + if (gb_char_is_digit(c) || + (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F')) + return true; + return false; +} + +gb_inline b32 +gb_char_is_alpha(char c) +{ + if ((c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z')) + return true; + return false; +} + +gb_inline b32 +gb_char_is_alphanumeric(char c) +{ + return gb_char_is_alpha(c) || gb_char_is_digit(c); +} + + + + +gb_inline void +gb_to_lower(char *str) +{ + if (!str) return; + while (*str) { + *str = gb_char_to_lower(*str); + str++; + } +} + +gb_inline void +gb_to_upper(char *str) +{ + if (!str) return; + while (*str) { + *str = gb_char_to_upper(*str); + str++; + } +} + + +gb_inline isize +gb_strlen(char const *str) +{ + isize result = 0; + if (str) { + char const *end = str; + while (*end) end++; + result = end - str; + } + return result; +} + +gb_inline i32 +gb_strcmp(char const *s1, char const *s2) +{ + while (*s1 && (*s1 == *s2)) { + s1++, s2++; + } + return *(u8 *)s1 - *(u8 *)s2; +} + + +gb_inline char * +gb_strncpy(char *dest, char const *source, isize len) +{ + GB_ASSERT_NOT_NULL(dest); + if (source) { + char *str = dest; + while (len > 0 && *source) { + *str++ = *source++; + len--; + } + while (len > 0) { + *str++ = '\0'; + len--; + } + } + return dest; +} + +gb_inline i32 +gb_strncmp(char const *s1, char const *s2, isize len) +{ + for(; len > 0; s1++, s2++, len--) { + if (*s1 != *s2) + return ((cast(uintptr)s1 < cast(uintptr)s2) ? -1 : +1); + else if (*s1 == '\0') + return 0; + } + return 0; +} + + +gb_inline char const * +gb_char_first_occurence(char const *s, char c) +{ + char ch = c; + for (; *s != ch; s++) { + if (*s == '\0') + return 0; + } + return s; +} + + +gb_inline char const * +gb_char_last_occurence(char const *s, char c) +{ + char const *result = NULL; + do { + if (*s == c) + result = s; + } while (*s++); + + return result; +} + + + +gb_inline void +gb_cstr_concat(char *dest, isize dest_len, + char const *src_a, isize src_a_len, + char const *src_b, isize src_b_len) +{ + GB_ASSERT(dest_len >= src_a_len+src_b_len+1); + if (dest) { + gb_memcopy(dest, src_a, src_a_len); + gb_memcopy(dest+src_a_len, src_b, src_b_len); + dest[src_a_len+src_b_len] = '\0'; + } +} + + + + + + + + + + +gb_inline void gb__string_set_length(gbString str, isize len) { GB_STRING_HEADER(str)->length = len; } +gb_inline void gb__string_set_capacity(gbString str, isize cap) { GB_STRING_HEADER(str)->capacity = cap; } + + +gb_inline gbString +gb_string_make(gbAllocator a, char const *str) +{ + isize len = str ? gb_strlen(str) : 0; return gb_string_make_length(a, str, len); } -gb_String -gb_string_make_length(gb_Allocator a, void const *init_str, gb_String_Size num_bytes) +gbString +gb_string_make_length(gbAllocator a, void const *init_str, isize num_bytes) { - gb_String_Size header_size = sizeof(gb_String_Header); + isize header_size = gb_size_of(gbStringHeader); void *ptr = gb_alloc(a, header_size + num_bytes + 1); - gb_String str; - gb_String_Header *header; + gbString str; + gbStringHeader *header; if (!init_str) gb_zero_size(ptr, header_size + num_bytes + 1); if (ptr == NULL) return NULL; @@ -658,76 +2373,75 @@ gb_string_make_length(gb_Allocator a, void const *init_str, gb_String_Size num_b header->length = num_bytes; header->capacity = num_bytes; if (num_bytes && init_str) - memcpy(str, init_str, num_bytes); + gb_memcopy(str, init_str, num_bytes); str[num_bytes] = '\0'; return str; } gb_inline void -gb_string_free(gb_String str) +gb_string_free(gbString str) { if (str) { - gb_String_Header *header; - header = GB_STRING_HEADER(str); + gbStringHeader *header = GB_STRING_HEADER(str); gb_free(header->allocator, header); } } -gb_inline gb_String gb_string_duplicate(gb_Allocator a, gb_String const str) { return gb_string_make_length(a, str, gb_string_length(str)); } +gb_inline gbString gb_string_duplicate(gbAllocator a, gbString const str) { return gb_string_make_length(a, str, gb_string_length(str)); } -gb_inline gb_String_Size gb_string_length(gb_String const str) { return GB_STRING_HEADER(str)->length; } -gb_inline gb_String_Size gb_string_capacity(gb_String const str) { return GB_STRING_HEADER(str)->capacity; } +gb_inline isize gb_string_length(gbString const str) { return GB_STRING_HEADER(str)->length; } +gb_inline isize gb_string_capacity(gbString const str) { return GB_STRING_HEADER(str)->capacity; } -gb_inline gb_String_Size -gb_string_available_space(gb_String const str) +gb_inline isize +gb_string_available_space(gbString const str) { - gb_String_Header *h = GB_STRING_HEADER(str); + gbStringHeader *h = GB_STRING_HEADER(str); if (h->capacity > h->length) return h->capacity - h->length; return 0; } -gb_inline void gb_string_clear(gb_String str) { gb__string_set_length(str, 0); str[0] = '\0'; } +gb_inline void gb_string_clear(gbString str) { gb__string_set_length(str, 0); str[0] = '\0'; } -gb_inline gb_String gb_string_append_string(gb_String str, gb_String const other) { return gb_string_append_string_length(str, other, gb_string_length(other)); } +gb_inline gbString gb_string_append_string(gbString str, gbString const other) { return gb_string_append_string_length(str, other, gb_string_length(other)); } -gb_String -gb_string_append_string_length(gb_String str, void const *other, gb_String_Size other_len) +gbString +gb_string_append_string_length(gbString str, void const *other, isize other_len) { - gb_String_Size curr_len = gb_string_length(str); + isize curr_len = gb_string_length(str); str = gb_string_make_space_for(str, other_len); if (str == NULL) return NULL; - memcpy(str + curr_len, other, other_len); + gb_memcopy(str + curr_len, other, other_len); str[curr_len + other_len] = '\0'; gb__string_set_length(str, curr_len + other_len); return str; } -gb_inline gb_String -gb_string_append_cstring(gb_String str, char const *other) +gb_inline gbString +gb_string_append_cstring(gbString str, char const *other) { - return gb_string_append_string_length(str, other, cast(gb_String_Size)strlen(other)); + return gb_string_append_string_length(str, other, cast(isize)strlen(other)); } -gb_String -gb_string_set(gb_String str, char const *cstr) +gbString +gb_string_set(gbString str, char const *cstr) { - gb_String_Size len = cast(gb_String_Size)strlen(cstr); + isize len = gb_strlen(cstr); if (gb_string_capacity(str) < len) { str = gb_string_make_space_for(str, len - gb_string_length(str)); if (str == NULL) return NULL; } - memcpy(str, cstr, len); + gb_memcopy(str, cstr, len); str[len] = '\0'; gb__string_set_length(str, len); @@ -735,44 +2449,22 @@ gb_string_set(gb_String str, char const *cstr) } -local_persist void * -gb__string_realloc(gb_Allocator a, void *ptr, gb_String_Size old_size, gb_String_Size new_size) + +gbString +gb_string_make_space_for(gbString str, isize add_len) { - if (!ptr) return gb_alloc(a, new_size); - - if (new_size < old_size) - new_size = old_size; - - if (old_size == new_size) { - return ptr; - } else { - void *new_ptr = gb_alloc(a, new_size); - if (!new_ptr) - return NULL; - - memcpy(new_ptr, ptr, old_size); - gb_free(a, ptr); - return new_ptr; - } -} - - - -gb_String -gb_string_make_space_for(gb_String str, gb_String_Size add_len) -{ - gb_String_Size available = gb_string_available_space(str); + isize available = gb_string_available_space(str); // Return if there is enough space left if (available >= add_len) { return str; } else { - gb_String_Size new_len = gb_string_length(str) + add_len; + isize new_len = gb_string_length(str) + add_len; void *ptr = GB_STRING_HEADER(str); - gb_String_Size old_size = sizeof(struct gb_String_Header) + gb_string_length(str) + 1; - gb_String_Size new_size = sizeof(struct gb_String_Header) + new_len + 1; + isize old_size = gb_size_of(gbStringHeader) + gb_string_length(str) + 1; + isize new_size = gb_size_of(gbStringHeader) + new_len + 1; - void *new_ptr = gb__string_realloc(GB_STRING_HEADER(str)->allocator, ptr, old_size, new_size); + void *new_ptr = gb_resize(GB_STRING_HEADER(str)->allocator, ptr, old_size, new_size); if (new_ptr == NULL) return NULL; str = cast(char *)(GB_STRING_HEADER(new_ptr) + 1); @@ -782,18 +2474,18 @@ gb_string_make_space_for(gb_String str, gb_String_Size add_len) } } -gb_inline gb_String_Size -gb_string_allocation_size(gb_String const str) +gb_inline isize +gb_string_allocation_size(gbString const str) { - gb_String_Size cap = gb_string_capacity(str); - return sizeof(gb_String_Header) + cap; + isize cap = gb_string_capacity(str); + return gb_size_of(gbStringHeader) + cap; } gb_inline b32 -gb_strings_are_equal(gb_String const lhs, gb_String const rhs) +gb_strings_are_equal(gbString const lhs, gbString const rhs) { - gb_String_Size lhs_len, rhs_len, i; + isize lhs_len, rhs_len, i; lhs_len = gb_string_length(lhs); rhs_len = gb_string_length(rhs); if (lhs_len != rhs_len) @@ -808,24 +2500,24 @@ gb_strings_are_equal(gb_String const lhs, gb_String const rhs) } -gb_String -gb_string_trim(gb_String str, char const *cut_set) +gbString +gb_string_trim(gbString str, char const *cut_set) { char *start, *end, *start_pos, *end_pos; - gb_String_Size len; + isize len; start_pos = start = str; end_pos = end = str + gb_string_length(str) - 1; - while (start_pos <= end && strchr(cut_set, *start_pos)) + while (start_pos <= end && gb_char_first_occurence(cut_set, *start_pos)) start_pos++; - while (end_pos > start_pos && strchr(cut_set, *end_pos)) + while (end_pos > start_pos && gb_char_first_occurence(cut_set, *end_pos)) end_pos--; - len = cast(gb_String_Size)((start_pos > end_pos) ? 0 : ((end_pos - start_pos)+1)); + len = cast(isize)((start_pos > end_pos) ? 0 : ((end_pos - start_pos)+1)); if (str != start_pos) - memmove(str, start_pos, len); + gb_memmove(str, start_pos, len); str[len] = '\0'; gb__string_set_length(str, len); @@ -833,23 +2525,512 @@ gb_string_trim(gb_String str, char const *cut_set) return str; } -gb_inline gb_String gb_string_trim_space(gb_String str) { return gb_string_trim(str, " \t\r\n\v\f"); } +gb_inline gbString gb_string_trim_space(gbString str) { return gb_string_trim(str, " \t\r\n\v\f"); } + + +//////////////////////////////////////////////////////////////// +// +// Windows UTF-8 Handling +// +// + + +char16 * +gb_utf8_to_utf16(char16 *buffer, char *s, isize len) +{ + u8 *str = cast(u8 *)s; + char32 c; + isize i = 0; + len--; + while (*str) { + if (i >= len) + return NULL; + if (!(*str & 0x80)) { + buffer[i++] = *str++; + } else if ((*str & 0xe0) == 0xc0) { + if (*str < 0xc2) + return NULL; + c = (*str++ & 0x1f) << 6; + if ((*str & 0xc0) != 0x80) + return NULL; + buffer[i++] = c + (*str++ & 0x3f); + } else if ((*str & 0xf0) == 0xe0) { + if (*str == 0xe0 && + (str[1] < 0xa0 || str[1] > 0xbf)) + return NULL; + if (*str == 0xed && str[1] > 0x9f) // str[1] < 0x80 is checked below + return NULL; + c = (*str++ & 0x0f) << 12; + if ((*str & 0xc0) != 0x80) + return NULL; + c += (*str++ & 0x3f) << 6; + if ((*str & 0xc0) != 0x80) + return NULL; + buffer[i++] = c + (*str++ & 0x3f); + } else if ((*str & 0xf8) == 0xf0) { + if (*str > 0xf4) + return NULL; + if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) + return NULL; + if (*str == 0xf4 && str[1] > 0x8f) // str[1] < 0x80 is checked below + return NULL; + c = (*str++ & 0x07) << 18; + if ((*str & 0xc0) != 0x80) + return NULL; + c += (*str++ & 0x3f) << 12; + if ((*str & 0xc0) != 0x80) + return NULL; + c += (*str++ & 0x3f) << 6; + if ((*str & 0xc0) != 0x80) + return NULL; + c += (*str++ & 0x3f); + // UTF-8 encodings of values used in surrogate pairs are invalid + if ((c & 0xfffff800) == 0xd800) + return NULL; + if (c >= 0x10000) { + c -= 0x10000; + if (i+2 > len) + return NULL; + buffer[i++] = 0xd800 | (0x3ff & (c>>10)); + buffer[i++] = 0xdc00 | (0x3ff & (c )); + } + } else { + return NULL; + } + } + buffer[i] = 0; + return buffer; +} + +char * +gb_utf16_to_utf8(char *buffer, char16 *str, isize len) +{ + isize i = 0; + len--; + while (*str) { + if (*str < 0x80) { + if (i+1 > len) + return NULL; + buffer[i++] = (char) *str++; + } else if (*str < 0x800) { + if (i+2 > len) + return NULL; + buffer[i++] = 0xc0 + (*str >> 6); + buffer[i++] = 0x80 + (*str & 0x3f); + str += 1; + } else if (*str >= 0xd800 && *str < 0xdc00) { + char32 c; + if (i+4 > len) + return NULL; + c = ((str[0] - 0xd800) << 10) + ((str[1]) - 0xdc00) + 0x10000; + buffer[i++] = 0xf0 + (c >> 18); + buffer[i++] = 0x80 + ((c >> 12) & 0x3f); + buffer[i++] = 0x80 + ((c >> 6) & 0x3f); + buffer[i++] = 0x80 + ((c ) & 0x3f); + str += 2; + } else if (*str >= 0xdc00 && *str < 0xe000) { + return NULL; + } else { + if (i+3 > len) + return NULL; + buffer[i++] = 0xe0 + (*str >> 12); + buffer[i++] = 0x80 + ((*str >> 6) & 0x3f); + buffer[i++] = 0x80 + ((*str ) & 0x3f); + str += 1; + } + } + buffer[i] = 0; + return buffer; +} + + + + +gb_no_inline void +gb__array_set_capacity(void *array_, isize capacity, isize element_size) +{ + // NOTE(bill): I know this is unsafe so don't call this function directly + gbVoidArray *a = cast(gbVoidArray *)array_; + void *data = NULL; + + GB_ASSERT(element_size > 0); + + if (capacity == a->capacity) + return; + + if (capacity < a->count) { + if (a->capacity < capacity) { + isize new_capacity = GB_ARRAY_GROW_FORMULA(a->capacity); + if (new_capacity < capacity) + new_capacity = capacity; + gb__array_set_capacity(a, new_capacity, element_size); + } + a->count = capacity; + } + + if (capacity > 0) { + data = gb_alloc(a->allocator, element_size*capacity); + gb_memcopy(data, a->data, element_size*a->count); + } + gb_free(a->allocator, a->data); + a->data = data; + a->capacity = capacity; +} + + + + + + +//////////////////////////////////////////////////////////////// +// +// File Handling +// +// + +b32 +gb_file_create(gbFile *file, char const *filepath, ...) +{ + va_list va; + char *path; + gb_zero_struct(file); + + va_start(va, filepath); + path = gb_sprintf_va(filepath, va); + va_end(va); + + file->handle = fopen(path, "wb"); + if (file->handle) { + file->path = gb_alloc_cstring(gb_heap_allocator(), path); + file->size = gb_file_size(file); + file->is_open = true; + file->type = GB_FILE_TYPE_WRITE; + file->last_write_time = gb_file_last_write_time(file->path); + return true; + } + return false; +} + + + +b32 +gb_file_open(gbFile *file, char const *filepath, ...) +{ + va_list va; + char *path; + gb_zero_struct(file); + + va_start(va, filepath); + path = gb_sprintf_va(filepath, va); + va_end(va); + + file->handle = fopen(path, "rb"); + if (file->handle) { + file->path = gb_alloc_cstring(gb_heap_allocator(), path); + file->size = gb_file_size(file); + file->is_open = true; + file->type = GB_FILE_TYPE_READ; + file->last_write_time = gb_file_last_write_time(file->path); + return true; + } + return false; +} + +gb_inline b32 +gb_file_close(gbFile *file) +{ + b32 result = true; + if (file && file->handle) + result = fclose(cast(FILE *)file->handle) != 0; // TODO(bill): Handle fclose errors + + if (file->path) gb_free(gb_heap_allocator(), file->path); + file->is_open = false; + + return result; +} + +gb_inline b32 +gb_file_read_at(gbFile *file, void *buffer, isize size, i64 offset) +{ + i64 prev_cursor_pos; + + GB_ASSERT(file->type == GB_FILE_TYPE_READ); + + prev_cursor_pos = ftell(cast(FILE *)file->handle); + fseek(cast(FILE *)file->handle, offset, SEEK_SET); + fread(buffer, 1, size, cast(FILE *)file->handle); + fseek(cast(FILE *)file->handle, prev_cursor_pos, SEEK_SET); + return true; +} + +gb_inline b32 +gb_file_write_at(gbFile *file, void const *buffer, isize size, i64 offset) +{ + isize written_size; + i64 prev_cursor_pos; + + GB_ASSERT(file->type == GB_FILE_TYPE_WRITE); + + prev_cursor_pos = ftell(cast(FILE *)file->handle); + fseek(cast(FILE *)file->handle, offset, SEEK_SET); + + written_size = fwrite(buffer, 1, size, cast(FILE *)file->handle); + fseek(cast(FILE *)file->handle, prev_cursor_pos, SEEK_SET); + if (written_size != size) { + GB_PANIC("Failed to write file data"); + return false; + } + + return true; +} + +gb_inline i64 +gb_file_size(gbFile *file) +{ + i64 result_size; + + fseek(cast(FILE *)file->handle, 0, SEEK_END); + result_size = cast(i64)ftell(cast(FILE *)file->handle); + fseek(cast(FILE *)file->handle, 0, SEEK_SET); + return result_size; +} + +b32 +gb_file_has_changed(gbFile *file) +{ + b32 result = false; + gbFileTime last_write_time = gb_file_last_write_time(file->path); + if (file->last_write_time != last_write_time) { + result = true; + file->last_write_time = last_write_time; + } + return result; +} + + + +#if defined(GB_SYSTEM_WINDOWS) +gbFileTime +gb_file_last_write_time(char const *filepath, ...) +{ + ULARGE_INTEGER li = {0}; + FILETIME last_write_time = {0}; + WIN32_FILE_ATTRIBUTE_DATA data = {0}; + + va_list va; + va_start(va, filepath); + + if (GetFileAttributesEx(gb_sprintf_va(filepath, va), GetFileExInfoStandard, &data)) + last_write_time = data.ftLastWriteTime; + + va_end(va); + + li.LowPart = last_write_time.dwLowDateTime; + li.HighPart = last_write_time.dwHighDateTime; + return cast(gbFileTime)li.QuadPart; +} + +gb_inline b32 +gb_file_copy(char const *existing_filename, char const *new_filename, b32 fail_if_exists) +{ + return CopyFile(existing_filename, new_filename, fail_if_exists); +} + +gb_inline b32 +gb_file_move(char const *existing_filename, char const *new_filename) +{ + return MoveFile(existing_filename, new_filename); +} + + + +#else +#error #endif -//////////////////////////////// -// // -// Unfinished code // -// // -//////////////////////////////// + + +gbFileContents +gb_read_entire_file_contents(gbAllocator a, b32 zero_terminate, char const *filepath, ...) +{ + gbFileContents result = {0}; + gbFile file = {0}; + char *path; + va_list va; + va_start(va, filepath); + path = gb_sprintf_va(filepath, va); + va_end(va); + + if (gb_file_open(&file, path)) { + i64 file_size = gb_file_size(&file); + if (file_size > 0) { + result.data = gb_alloc(a, zero_terminate ? file_size+1 : file_size); + result.size = file_size; + gb_file_read_at(&file, result.data, result.size, 0); + if (zero_terminate) { + u8 *str = cast(u8 *)result.data; + str[file_size] = '\0'; + } + } + gb_file_close(&file); + } + + return result; +} + + + + + +gb_inline b32 +gb_path_is_absolute(char const *path) +{ + b32 result = false; + GB_ASSERT_NOT_NULL(path); +#if defined(GB_SYSTEM_WINDOWS) + result == (gb_strlen(path) > 2) && + gb_char_is_alpha(path[0]) && + (path[1] == ':' && path[2] == GB_PATH_SEPARATOR); +#else + result = (gb_strlen(path) > 0 && path[0] == GB_PATH_SEPARATOR); +#endif + return result; +} + +gb_inline b32 gb_path_is_relative(char const *path) { return !gb_path_is_absolute(path); } + +gb_inline b32 +gb_path_is_root(char const *path) +{ + b32 result = false; + GB_ASSERT_NOT_NULL(path); +#if defined(GB_SYSTEM_WINDOWS) + result = gb_path_is_absolute(path) && gb_strlen(path) == 3; +#else + result = gb_path_is_absolute(path) && gb_strlen(path) == 1; +#endif + return result; +} + +gb_inline char const * +gb_path_base_name(char const *path) +{ + char const *ls; + GB_ASSERT_NOT_NULL(path); + ls = gb_char_last_occurence(path, '/'); + return (ls == NULL) ? path : ls+1; +} + +gb_inline char const * +gb_path_extension(char const *path) +{ + char const *ld; + GB_ASSERT_NOT_NULL(path); + ld = gb_char_last_occurence(path, '.'); + return (ld == NULL) ? NULL : ld+1; +} + +#if defined(GB_SYSTEM_WINDOWS) +gb_inline void gb_exit(u32 code) { ExitProcess(code); } +#else +#error +#endif + + +//////////////////////////////////////////////////////////////// +// +// DLL Handling +// +// + +#if defined(GB_SYSTEM_WINDOWS) + +gbDllHandle +gb_dll_load(char const *filepath, ...) +{ + gb_local_persist char buffer[512]; + va_list va; + va_start(va, filepath); + gb_snprintf_va(buffer, gb_size_of(buffer), filepath, va); + va_end(va); + return cast(gbDllHandle)LoadLibraryA(buffer); +} +gb_inline void gb_dll_unload(gbDllHandle dll) { FreeLibrary(cast(HMODULE)dll); } +gb_inline gbDllProc gb_dll_proc_address(gbDllHandle dll, char const *proc_name) { return cast(gbDllProc)GetProcAddress(cast(HMODULE)dll, proc_name); } + +#else +#error +#endif + + +//////////////////////////////////////////////////////////////// +// +// Time +// +// +#if defined(GB_SYSTEM_WINDOWS) + +gb_inline u64 gb_rdtsc(void) { return __rdtsc(); } + +gb_global LARGE_INTEGER gb__win32_perf_count_freq = {0}; + +gb_inline f64 +gb_time_now(void) +{ + f64 result; + LARGE_INTEGER counter; + if (!gb__win32_perf_count_freq.QuadPart) + QueryPerformanceFrequency(&gb__win32_perf_count_freq); + GB_ASSERT(gb__win32_perf_count_freq.QuadPart != 0); + + QueryPerformanceCounter(&counter); + + result = counter.QuadPart / cast(f64)(gb__win32_perf_count_freq.QuadPart); + return result; +} + + +gb_inline void +gb_get_system_date(gbDate *date) +{ + SYSTEMTIME st = {0}; + GetSystemTime(&st); + date->year = st.wYear; + date->month = st.wMonth; + date->day_of_week = st.wDayOfWeek; + date->day = st.wDay; + date->hour = st.wHour; + date->minute = st.wMinute; + date->second = st.wSecond; + date->milliseconds = st.wMilliseconds; +} + +gb_inline void +gb_get_local_date(gbDate *date) +{ + SYSTEMTIME st = {0}; + GetLocalTime(&st); + date->year = st.wYear; + date->month = st.wMonth; + date->day_of_week = st.wDayOfWeek; + date->day = st.wDay; + date->hour = st.wHour; + date->minute = st.wMinute; + date->second = st.wSecond; + date->milliseconds = st.wMilliseconds; +} + +#else +#error +#endif #if defined(__cplusplus) } #endif - -#endif // GB_INCLUDE_GB_H +#endif /* GB_IMPLEMENTATION */ diff --git a/gb.hpp b/gb.hpp deleted file mode 100644 index 24f2cdf..0000000 --- a/gb.hpp +++ /dev/null @@ -1,4091 +0,0 @@ -// gb.hpp - v0.32 - public domain C++11 helper library - no warranty implied; use at your own risk -// (Experimental) A C++11 helper library without STL geared towards game development - -/* -LICENSE - This software is in the public domain. Where that dedication is not - recognized, you are granted a perpetual, irrevocable license to copy, - distribute, and modify this file as you see fit. - -WARNING - - This library is _highly_ experimental and features may not work as expected. - - This also means that many functions are not documented. - - This library is not compatible with STL at all! (By design) - -CONTENTS: - - Common Macros - - Assert - - Types - - Type Traits - - C++11 Move Semantics - - Defer - - Casts - - bit_cast - - pseudo_cast - - Memory - - Mutex - - Atomics - - Semaphore - - Thread - - Allocator - - Heap Allocator - - Arena Allocator - - Functions - - String - - Array - - Hash Table - - Hash Functions -*/ - -/* -Version History: - 0.33 - Explicit Everything! No ctor/dtor on Array and Hash_Table - 0.32 - Change const position convention - 0.31a - Minor fixes - 0.31 - Remove `_Allocator` suffix for allocator types - 0.30 - sort::quick - 0.29 - GB_ASSERT prints call stack - 0.28 - Pool Allocator - 0.27 - Dealloc to Free & More Hashing Functions - 0.26a - Heap_Allocator Fix - 0.26 - Better Allocation system - 0.25a - Array bug fix - 0.25 - Faster Heap_Allocator for Windows using HeapAlloc - 0.24b - Even More Hash_Table Bug Fixes - 0.24a - Hash_Table Bug Fixes - 0.24 - More documentation and bug fixes - 0.23 - Move Semantics for Array and Hash_Table - 0.22 - Code rearrangment into namespaces - 0.21d - Fix array::free - 0.21c - Fix Another Typo causing unresolved external symbol - 0.21b - Typo fixes - 0.21a - Better `static` keywords - 0.21 - Separate Math Library - 0.20a - #ifndef for many macros - 0.20 - Angle - 0.19 - Cache friendly Transform and String fixes - 0.18 - Hash_Table bug fixes - 0.17 - Death to OOP - 0.16 - All References are const convention - 0.15 - Namespaced Types - 0.14 - Casts and Quaternion Look At - 0.13a - Fix Todos - 0.13 - Basic Type Traits - 0.12 - Random - 0.11 - Complex - 0.10 - Atomics - 0.09 - Bug Fixes - 0.08 - Matrix(2,3) - 0.07 - Bug Fixes - 0.06 - Os spec ideas - 0.05 - Transform Type and Quaternion Functions - 0.04 - String - 0.03 - Hash Functions - 0.02 - Hash Table - 0.01 - Initial Version -*/ - - -#ifndef GB_INCLUDE_GB_HPP -#define GB_INCLUDE_GB_HPP - -#if !defined(__cplusplus) && __cplusplus >= 201103L - #error This library is only for C++11 and above -#endif - -// NOTE(bill): Because static means three different things in C/C++ -// Great Design(!) -#ifndef global_variable -#define global_variable static -#define internal_linkage static -#define local_persist static -#endif - -/* - Example for static defines - - global_variable const f32 TAU = 6.283185f; - global_variable void* g_memory; - - internal_linkage void - some_function(...) - { - local_persist u32 count = 0; - ... - count++; - ... - } -*/ - -// `global_variable` : variable is in the global scope -// `internal_linkage` : function is only visible for this linkage -// `local_persist` : variable persists for the particular function scope - - -#if defined(_MSC_VER) - #define _ALLOW_KEYWORD_MACROS - - #ifndef alignof // Needed for MSVC 2013 'cause Microsoft "loves" standards - #define alignof(x) __alignof(x) - #endif -#endif - - -//////////////////////////////// -// // -// System OS // -// // -//////////////////////////////// -#if defined(_WIN32) || defined(_WIN64) - #ifndef GB_SYSTEM_WINDOWS - #define GB_SYSTEM_WINDOWS 1 - #endif -#elif defined(__APPLE__) && defined(__MACH__) - #ifndef GB_SYSTEM_OSX - #define GB_SYSTEM_OSX 1 - #endif -#elif defined(__unix__) - #ifndef GB_SYSTEM_UNIX - #define GB_SYSTEM_UNIX 1 - #endif - - #if defined(__linux__) - #ifndef GB_SYSTEM_LINUX - #define GB_SYSTEM_LINUX 1 - #endif - #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) - #ifndef GB_SYSTEM_FREEBSD - #define GB_SYSTEM_FREEBSD 1 - #endif - #else - #error This UNIX operating system is not supported by gb.hpp - #endif -#else - #error This operating system is not supported by gb.hpp -#endif - - -#if defined(_MSC_VER) - // Microsoft Visual Studio - #define GB_COMPILER_MSVC 1 -#elif defined(__clang__) - // Clang - #define GB_COMPILER_CLANG 1 -#elif defined(__GNUC__) || defined(__GNUG__) && !(defined(__clang__) || defined(__INTEL_COMPILER)) - // GNU GCC/G++ Compiler - #define GB_COMPILER_GNU_GCC 1 -#elif defined(__INTEL_COMPILER) - // Intel C++ Compiler - #define GB_COMPILER_INTEL 1 -#endif - -//////////////////////////////// -// // -// Environment Bit Size // -// // -//////////////////////////////// -#if defined(_WIN32) || defined(_WIN64) - #if defined(_WIN64) - #ifndef GB_ARCH_64_BIT - #define GB_ARCH_64_BIT 1 - #endif - #else - #ifndef GB_ARCH_32_BIT - #define GB_ARCH_32_BIT 1 - #endif - #endif -#endif - -// TODO(bill): Check if this works on clang -#if defined(__GNUC__) - #if defined(__x86_64__) || defined(__ppc64__) - #ifndef GB_ARCH_64_BIT - #define GB_ARCH_64_BIT 1 - #endif - #else - #ifndef GB_ARCH_32_BIT - #define GB_ARCH_32_BIT 1 - #endif - #endif -#endif - - -#ifndef GB_EDIAN_ORDER -#define GB_EDIAN_ORDER - #define GB_IS_BIG_EDIAN (!*(unsigned char*)&(unsigned short){1}) - #define GB_IS_LITTLE_EDIAN (!GB_IS_BIG_EDIAN) -#endif - -#ifndef GB_IS_POWER_OF_TWO -#define GB_IS_POWER_OF_TWO(x) ((x) != 0) && !((x) & ((x) - 1)) -#endif - -#if !defined(GB_HAS_NO_CONSTEXPR) - #if defined(_GNUC_VER) && _GNUC_VER < 406 // Less than gcc 4.06 - #define GB_HAS_NO_CONSTEXPR 1 - #elif defined(_MSC_VER) && _MSC_VER < 1900 // Less than Visual Studio 2015/MSVC++ 14.0 - #define GB_HAS_NO_CONSTEXPR 1 - #elif !defined(__GXX_EXPERIMENTAL_CXX0X__) && __cplusplus < 201103L - #define GB_HAS_NO_CONSTEXPR 1 - #endif -#endif - -#if defined(GB_HAS_NO_CONSTEXPR) - #define GB_CONSTEXPR -#else - #define GB_CONSTEXPR constexpr -#endif - -#ifndef GB_FORCE_INLINE - #if defined(_MSC_VER) - #define GB_FORCE_INLINE __forceinline - #else - #define GB_FORCE_INLINE __attribute__ ((__always_inline__)) - #endif -#endif - - - - - -//////////////////////////////// -// // -// Includes // -// // -//////////////////////////////// - -#include -#include -#if !defined(GB_NO_STDIO) -#include -#endif - -#if defined(GB_SYSTEM_WINDOWS) - #define NOMINMAX 1 - #define VC_EXTRALEAN 1 - #define WIN32_EXTRA_LEAN 1 - #define WIN32_LEAN_AND_MEAN 1 - - #include // TODO(bill): Should we include only the needed headers? - #include // Time functions - - #undef NOMINMAX - #undef VC_EXTRALEAN - #undef WIN32_EXTRA_LEAN - #undef WIN32_LEAN_AND_MEAN - - #include -#else - #include - #include -#endif - - -#ifndef GB_UNUSED -#define GB_UNUSED(x) ((void)sizeof(x)) -#endif - -#if !defined(GB_ASSERT) - #if !defined(NDEBUG) - #define GB_ASSERT(x, ...) ((void)(gb__assert_handler((x), #x, __FILE__, __LINE__, ##__VA_ARGS__))) - - // Helper function used as a better alternative to assert which allows for - // optional printf style error messages - extern "C" void - gb__assert_handler(bool condition, char const* condition_str, - char const* filename, size_t line, - char const* error_text = nullptr, ...); - #else - #define GB_ASSERT(x, ...) ((void)sizeof(x)) - #endif -#endif - -//////////////////////////////// -// // -// snprintf_msvc // -// // -//////////////////////////////// -#if !defined(GB_NO_STDIO) && defined(_MSC_VER) - extern "C" inline int - gb__vsnprintf_compatible(char* buffer, size_t size, char const* format, va_list args) - { - int result = -1; - if (size > 0) - result = _vsnprintf_s(buffer, size, _TRUNCATE, format, args); - if (result == -1) - return _vscprintf(format, args); - - return result; - } - - extern "C" inline int - gb__snprintf_compatible(char* buffer, size_t size, char const* format, ...) - { - va_list args; - va_start(args, format); - int result = gb__vsnprintf_compatible(buffer, size, format, args); - va_end(args); - return result; - } - - #if !defined(GB_DO_NOT_USE_MSVC_SPRINTF_FIX) - #define snprintf gb__snprintf_compatible - #define vsnprintf gb__vsnprintf_compatible - #endif // GB_DO_NOT_USE_MSVC_SPRINTF_FIX -#endif - - -#if defined(GB_NO_GB_NAMESPACE) - #define __GB_NAMESPACE_START - #define __GB_NAMESPACE_END - #define __GB_NAMESPACE_PREFIX -#else - #ifndef __GB_NAMESPACE_PREFIX - #define __GB_NAMESPACE_PREFIX gb - #endif - - // NOTE(bill): __GB_NAMESPACE_PREFIX cannot be blank - // This is why macros that not meant to be touched have `__` prefix - // You can change them if you know what you are doing - - #define __GB_NAMESPACE_START namespace __GB_NAMESPACE_PREFIX { - #define __GB_NAMESPACE_END } // namespace __GB_NAMESPACE_PREFIX -#endif - -#if !defined(GB_BASIC_WITHOUT_NAMESPACE) -__GB_NAMESPACE_START -#endif // GB_BASIC_WITHOUT_NAMESPACE - -//////////////////////////////// -// // -// Types // -// // -//////////////////////////////// - -#ifndef GB_BASIC_TYPES -#define GB_BASIC_TYPES - #if defined(_MSC_VER) - using u8 = unsigned __int8; - using s8 = signed __int8; - using u16 = unsigned __int16; - using s16 = signed __int16; - using u32 = unsigned __int32; - using s32 = signed __int32; - using u64 = unsigned __int64; - using s64 = signed __int64; - #else - // NOTE(bill): Of the platforms that I build for, these will be correct - using u8 = unsigned char; - using s8 = signed char; - using u16 = unsigned short; - using s16 = signed short; - using u32 = unsigned int; - using s32 = signed int; - using u64 = unsigned long long; - using s64 = signed long long; - #endif - - static_assert( sizeof(u8) == 1, "u8 is not 8 bits"); - static_assert(sizeof(u16) == 2, "u16 is not 16 bits"); - static_assert(sizeof(u32) == 4, "u32 is not 32 bits"); - static_assert(sizeof(u64) == 8, "u64 is not 64 bits"); - - using f32 = float; - using f64 = double; - - #if defined(GB_B8_AS_BOOL) - using bool8 = bool; - #else - using bool8 = s8; - #endif - using bool32 = s32; - - // NOTE(bill): (std::)size_t is not used not because it's a bad concept but on - // the platforms that I will be using: - // sizeof(size_t) == sizeof(usize) == sizeof(ssize) - // NOTE(bill): This also allows for a signed version of size_t which is similar - // to ptrdiff_t - // NOTE(bill): If (u)intptr is a better fit, please use that. - // NOTE(bill): Also, I hate the `_t` suffix - #if defined(GB_ARCH_64_BIT) - using ssize = s64; - using usize = u64; - #elif defined(GB_ARCH_32_BIT) - using usize = s32; - using usize = u32; - #else - #error Unknown architecture bit size - #endif - - static_assert(sizeof(usize) == sizeof(size_t), - "`usize` is not the same size as `size_t`"); - static_assert(sizeof(ssize) == sizeof(usize), - "`ssize` is not the same size as `usize`"); - - using intptr = intptr_t; - using uintptr = uintptr_t; - - using ptrdiff = ptrdiff_t; - -#endif - -#if !defined(GB_U8_MIN) - #define GB_U8_MIN 0u - #define GB_U8_MAX 0xffu - #define GB_S8_MIN (-0x7f - 1) - #define GB_S8_MAX 0x7f - - #define GB_U16_MIN 0u - #define GB_U16_MAX 0xffffu - #define GB_S16_MIN (-0x7fff - 1) - #define GB_S16_MAX 0x7fff - - #define GB_U32_MIN 0u - #define GB_U32_MAX 0xffffffffu - #define GB_S32_MIN (-0x7fffffff - 1) - #define GB_S32_MAX 0x7fffffff - - #define GB_U64_MIN 0ull - #define GB_U64_MAX 0xffffffffffffffffull - #define GB_S64_MIN (-0x7fffffffffffffffll - 1) - #define GB_S64_MAX 0x7fffffffffffffffll -#endif - -#if defined(GB_ARCH_64_BIT) && !defined(GB_USIZE_MIX) - #define GB_USIZE_MIX GB_U64_MIN - #define GB_USIZE_MAX GB_U64_MAX - - #define GB_SSIZE_MIX GB_S64_MIN - #define GB_SSIZE_MAX GB_S64_MAX -#elif defined(GB_ARCH_32_BIT) && !defined(GB_USIZE_MIX) - #define GB_USIZE_MIX GB_U32_MIN - #define GB_USIZE_MAX GB_U32_MAX - - #define GB_SSIZE_MIX GB_S32_MIN - #define GB_SSIZE_MAX GB_S32_MAX -#endif - -#if defined(GB_BASIC_WITHOUT_NAMESPACE) && !defined(U8_MIN) - #define U8_MIN 0u - #define U8_MAX 0xffu - #define S8_MIN (-0x7f - 1) - #define S8_MAX 0x7f - - #define U16_MIN 0u - #define U16_MAX 0xffffu - #define S16_MIN (-0x7fff - 1) - #define S16_MAX 0x7fff - - #define U32_MIN 0u - #define U32_MAX 0xffffffffu - #define S32_MIN (-0x7fffffff - 1) - #define S32_MAX 0x7fffffff - - #define U64_MIN 0ull - #define U64_MAX 0xffffffffffffffffull - #define S64_MIN (-0x7fffffffffffffffll - 1) - #define S64_MAX 0x7fffffffffffffffll - - #if defined(GB_ARCH_64_BIT) && !defined(GB_USIZE_MIX) - #define USIZE_MIX U64_MIN - #define USIZE_MAX U64_MAX - - #define SSIZE_MIX S64_MIN - #define SSIZE_MAX S64_MAX - #elif defined(GB_ARCH_32_BIT) && !defined(GB_USIZE_MIX) - #define USIZE_MIX U32_MIN - #define USIZE_MAX U32_MAX - - #define SSIZE_MIX S32_MIN - #define SSIZE_MAX S32_MAX - #endif -#endif - - - -#if !defined(GB_BASIC_WITHOUT_NAMESPACE) -__GB_NAMESPACE_END -#endif // GB_BASIC_WITHOUT_NAMESPACE - - - - - -__GB_NAMESPACE_START -//////////////////////////////// -// // -// C++11 Types Traits // -// // -//////////////////////////////// - -template struct Add_Const_Def { using Type = const T; }; -template using Add_Const = typename Add_Const_Def::Type; - -template struct Add_Volatile_Def { using Type = volatile T; }; -template using Add_Volatile = typename Add_Volatile_Def::Type; - -template using Add_Const_Volatile = Add_Const>; - -template struct Add_Lvalue_Reference_Def { using Type = T&; }; -template struct Add_Lvalue_Reference_Def { using Type = T&; }; -template struct Add_Lvalue_Reference_Def { using Type = T&; }; -template <> struct Add_Lvalue_Reference_Def { using Type = void; }; -template <> struct Add_Lvalue_Reference_Def { using Type = void const; }; -template <> struct Add_Lvalue_Reference_Def { using Type = void volatile; }; -template <> struct Add_Lvalue_Reference_Def { using Type = void const volatile; }; -template using Add_Lvalue_Reference = typename Add_Lvalue_Reference_Def::Type; - -template struct Add_Rvalue_Reference_Def { using Type = T&&; }; -template struct Add_Rvalue_Reference_Def { using Type = T&; }; -template struct Add_Rvalue_Reference_Def { using Type = T&&; }; -template <> struct Add_Rvalue_Reference_Def { using Type = void; }; -template <> struct Add_Rvalue_Reference_Def { using Type = void const; }; -template <> struct Add_Rvalue_Reference_Def { using Type = void volatile; }; -template <> struct Add_Rvalue_Reference_Def { using Type = void const volatile; }; -template using Add_Rvalue_Reference = typename Add_Rvalue_Reference_Def::Type; - - -template struct Remove_Pointer_Def { using Type = T; }; -template struct Remove_Pointer_Def { using Type = T; }; -template struct Remove_Pointer_Def { using Type = T; }; -template struct Remove_Pointer_Def { using Type = T; }; -template struct Remove_Pointer_Def { using Type = T; }; -template using Remove_Pointer = typename Remove_Pointer_Def::Type; - -template struct Add_Pointer_Def { using Type = T*; }; -template using Add_Pointer = typename Add_Pointer_Def::Type; - -template struct Remove_Const_Def { using Type = T; }; -template struct Remove_Const_Def { using Type = T; }; -template using Remove_Const = typename Remove_Const_Def::Type; - -template struct Remove_Volatile_Def { using Type = T; }; -template struct Remove_Volatile_Def { using Type = T; }; -template using Remove_Volatile = typename Remove_Const_Def::Type; - -template using Remove_Const_Volatile = Remove_Const>; - -template struct Remove_Reference_Def { using Type = T; }; -template struct Remove_Reference_Def { using Type = T; }; -template struct Remove_Reference_Def { using Type = T; }; -template using Remove_Reference = typename Remove_Reference_Def::Type; - -template struct Integral_Constant { global_variable const T VALUE = v; using Value_Type = T; using Type = Integral_Constant; }; - -template struct Extent : Integral_Constant {}; -template struct Extent : Integral_Constant {}; -template struct Extent : Integral_Constant::VALUE> {}; -template struct Extent : Integral_Constant {}; -template struct Extent : Integral_Constant::VALUE> {}; - -template struct Remove_Extent_Def { using Type = T; }; -template struct Remove_Extent_Def { using Type = T; }; -template struct Remove_Extent_Def { using Type = T; }; - -// TODO(bill): Do I "need" all of these template traits? - - -//////////////////////////////// -// // -// C++11 Move Semantics // -// // -//////////////////////////////// - -template -inline T&& -forward_ownership(Remove_Reference& t) -{ - return static_cast(t); -} - -template -inline T&& -forward_ownership(Remove_Reference&& t) -{ - return static_cast(t); -} - -template -inline Remove_Reference&& -move_ownership(T&& t) -{ - return static_cast&&>(t); -} -__GB_NAMESPACE_END - - - - - -//////////////////////////////// -// // -// Defer // -// // -//////////////////////////////// - -#ifndef GB_DEFER -#define GB_DEFER - __GB_NAMESPACE_START - namespace impl - { - template - struct Defer - { - Func f; - - Defer(Func&& f) : f{forward_ownership(f)} {} - ~Defer() { f(); }; - }; - - template - inline Defer - defer_func(Func&& f) { return Defer(forward_ownership(f)); } - } // namespace impl - __GB_NAMESPACE_END - - // NOTE(bill): These macros are in the global namespace thus, defer can be treated without a __GB_NAMESPACE_PREFIX:: prefix - #define GB_DEFER_1(x, y) x##y - #define GB_DEFER_2(x, y) GB_DEFER_1(x, y) - #define GB_DEFER_3(x) GB_DEFER_2(GB_DEFER_2(GB_DEFER_2(x, __COUNTER__), _), __LINE__) - #define defer(code) auto GB_DEFER_3(_defer_) = ::impl::defer_func([&](){code;}) - - /* EXAMPLES - - // `defer (...)` will defer a statement till the end of scope - - FILE* file = fopen("test.txt", "rb"); - if (file == nullptr) - { - // Handle Error - } - defer (fclose(file)); // Will always be called at the end of scope - - // - - auto m = mutex::make(); - defer (mutex::destroy(&m)); // Mutex will be destroyed at the end of scope - - { - mutex::lock(&m); - defer (mutex::unlock(&m)); // Mutex will unlock at end of scope - - // Do whatever - } - - // You can scope multiple statements together if needed with {...} - defer ({ - func1(); - func2(); - func3(); - }); - - */ -#endif - - - - - -#if !defined(GB_CASTS_WITHOUT_NAMESPACE) -__GB_NAMESPACE_START -#endif // GB_CASTS_WITHOUT_NAMESPACE - -#ifndef GB_SPECIAL_CASTS -#define GB_SPECIAL_CASTS - // NOTE(bill): Very similar to doing `*(T*)(&u)` - template - inline Dest - bit_cast(Source const& source) - { - static_assert(sizeof(Dest) <= sizeof(Source), - "bit_cast(Source const&) - sizeof(Dest) <= sizeof(Source)"); - Dest dest; - ::memcpy(&dest, &source, sizeof(Dest)); - return dest; - } - - // IMPORTANT NOTE(bill): Very similar to doing `*(T*)(&u)` but easier/clearer to write - // however, it can be dangerous if sizeof(T) > sizeof(U) e.g. unintialized memory, undefined behavior - // *(T*)(&u) ~~ pseudo_cast(u) - template - inline T - pseudo_cast(U const& u) - { - return reinterpret_cast(u); - } - - /* - EXAMPLES: - - // bit_cast - - u8 arr[4] = {0x78, 0x56, 0x34, 0x12}; - u32 var = bit_cast(arr); // Little edian => 0x12345678 - - // pseudo_cast - except from gb_math.hpp - Sphere - calculate_min_bounding(void const* vertices, usize num_vertices, usize stride, usize offset, f32 step) - { - auto gen = random::make(0); - - u8 const* vertex = reinterpret_cast(vertices); - vertex += offset; - - Vector3 position = pseudo_cast(vertex[0]); - Vector3 center = position; - center += pseudo_cast(vertex[1 * stride]); - center *= 0.5f; - - Vector3 d = position - center; - f32 max_dist_sq = math::dot(d, d); - f32 radius_step = step * 0.37f; - - bool done; - do - { - done = true; - for (u32 i = 0, index = random::uniform_u32(&gen, 0, num_vertices-1); - i < num_vertices; - i++, index = (index + 1)%num_vertices) - { - Vector3 position = pseudo_cast(vertex[index * stride]); - - d = position - center; - f32 dist_sq = math::dot(d, d); - - if (dist_sq > max_dist_sq) - { - done = false; - - center = d * radius_step; - max_dist_sq = math::lerp(max_dist_sq, dist_sq, step); - - break; - } - } - } - while (!done); - - Sphere result; - - result.center = center; - result.radius = math::sqrt(max_dist_sq); - - return result; - } - - */ -#endif - -// FORENOTE(bill): There used to be a magic_cast that was equivalent to -// a C-style cast but I removed it as I could not get it work as intented -// for everything using only C++ style casts (it needed to a c-style cast) - -#if !defined(GB_CASTS_WITHOUT_NAMESPACE) -__GB_NAMESPACE_END -#endif // GB_CASTS_WITHOUT_NAMESPACE - - - - - -__GB_NAMESPACE_START - -//////////////////////////////// -// // -// Memory // -// // -//////////////////////////////// - -template -inline usize array_count(T const(& )[N]) { return N; } - -inline s64 kilobytes(s64 x) { return (x) * 1024ll; } -inline s64 megabytes(s64 x) { return kilobytes(x) * 1024ll; } -inline s64 gigabytes(s64 x) { return megabytes(x) * 1024ll; } -inline s64 terabytes(s64 x) { return gigabytes(x) * 1024ll; } - - - - -struct Mutex -{ -#if defined(GB_SYSTEM_WINDOWS) - HANDLE win32_mutex; -#else - pthread_mutex_t posix_mutex; -#endif -}; - -namespace mutex -{ -Mutex make(); -void destroy(Mutex* mutex); -void lock(Mutex* mutex); -bool try_lock(Mutex* mutex); -void unlock(Mutex* mutex); -} // namespace mutex - - - - -// Atomic Types -struct Atomic32 { u32 nonatomic; }; -struct Atomic64 { u64 nonatomic; }; - -namespace atomic -{ -u32 load(Atomic32 const volatile* object); -void store(Atomic32 volatile* object, u32 value); -u32 compare_exchange_strong(Atomic32 volatile* object, u32 expected, u32 desired); -u32 exchanged(Atomic32 volatile* object, u32 desired); -u32 fetch_add(Atomic32 volatile* object, s32 operand); -u32 fetch_and(Atomic32 volatile* object, u32 operand); -u32 fetch_or(Atomic32 volatile* object, u32 operand); - -u64 load(Atomic64 const volatile* object); -void store(Atomic64 volatile* object, u64 value); -u64 compare_exchange_strong(Atomic64 volatile* object, u64 expected, u64 desired); -u64 exchanged(Atomic64 volatile* object, u64 desired); -u64 fetch_add(Atomic64 volatile* object, s64 operand); -u64 fetch_and(Atomic64 volatile* object, u64 operand); -u64 fetch_or(Atomic64 volatile* object, u64 operand); -} // namespace atomic - - - - -struct Semaphore -{ -#if defined(GB_SYSTEM_WINDOWS) - HANDLE win32_handle; -#else - Mutex mutex; - pthread_cond_t cond; - s32 count; -#endif -}; - -namespace semaphore -{ -Semaphore make(); -void destroy(Semaphore* semaphore); -void post(Semaphore* semaphore, u32 count = 1); -void wait(Semaphore* semaphore); -} // namespace semaphore - - - - -// TODO(bill): Is this thread procedure definition good enough? -using Thread_Procedure = void(void*); - -struct Thread -{ -#if defined(GB_SYSTEM_WINDOWS) - HANDLE win32_handle; -#else - pthread_t posix_handle; -#endif - - Thread_Procedure* function; - void* data; - - Semaphore semaphore; - usize stack_size; - bool32 is_running; -}; - -namespace thread -{ -Thread make(); -void destroy(Thread* t); -void start(Thread* t, Thread_Procedure* func, void* data = nullptr, usize stack_size = 0); -void join(Thread* t); -bool is_running(Thread const& t); -u32 current_id(); -} // namespace thread - - - - -// Default alignment for memory allocations -#ifndef GB_DEFAULT_ALIGNMENT -#define GB_DEFAULT_ALIGNMENT 8 -#endif - -// Base class for memory allocators - Pretty much a vtable -struct Allocator -{ - // Allocates the specified amount of memory aligned to the specified alignment - void* (*alloc)(Allocator* a, usize size, usize align); - // Frees an allocation made with alloc() - void (*free)(Allocator* a, void* ptr); - // Returns the amount of usuable memory allocated at `ptr`. - /// - // If the allocator does not support tracking of the allocation size, - // the function will return -1 - s64 (*allocated_size)(Allocator* a, void const* ptr); - // Returns the total amount of memory allocated by this allocator - /// - // If the allocator does not track memory, the function will return -1 - s64 (*total_allocated)(Allocator* a); -}; - - - - -struct Heap : Allocator -{ - struct Header - { - usize size; - }; - - Mutex mutex; - bool32 use_mutex; - s64 total_allocated_count; - s64 allocation_count; - -#if defined(GB_SYSTEM_WINDOWS) - HANDLE win32_heap_handle; -#endif -}; - -namespace heap -{ -Heap make(bool use_mutex = true); -void destroy(Heap* heap); -} // namespace heap - - - - - -struct Arena : Allocator -{ - Allocator* backing; - void* physical_start; - s64 total_size; - s64 total_allocated_count; - s64 temp_count; -}; - -namespace arena -{ -Arena make(Allocator* backing, usize size); -Arena make(void* start, usize size); -void destroy(Arena* arena); -void clear(Arena* arena); -} // namespace arena - - - - - -struct Temporary_Arena_Memory -{ - Arena* arena; - s64 original_count; -}; - -namespace temporary_arena_memory -{ -Temporary_Arena_Memory make(Arena* arena); -void free(Temporary_Arena_Memory tmp); -} // namespace temporary_arena_memory - - - - -struct Pool : Allocator -{ - Allocator* backing; - - void* physical_start; - void* free_list; - - usize block_size; - usize block_align; - s64 total_size; -}; - -namespace pool -{ -Pool make(Allocator* backing, usize num_blocks, usize block_size, - usize block_align = GB_DEFAULT_ALIGNMENT); -void destroy(Pool* pool); -} // namespace pool - - - - -namespace memory -{ -void* align_forward(void* ptr, usize align); - -void* pointer_add(void* ptr, usize bytes); -void* pointer_sub(void* ptr, usize bytes); -void const* pointer_add(void const* ptr, usize bytes); -void const* pointer_sub(void const* ptr, usize bytes); - -template -void fill(T* ptr, usize count, T const& value); - -template -void fill(T* ptr, usize count, T&& value); - -void zero(void* ptr, usize bytes); -void copy(void const* src, usize bytes, void* dest); -void move(void const* src, usize bytes, void* dest); -bool equals(void const* a, void const* b, usize bytes); - -template -void zero_struct(T* ptr); - -template -void zero_array(T* ptr, usize count); - -template -void copy_array(T const* src_array, usize count, T* dest_array); - -template -void copy_struct(T const* src_array, T* dest_array); - - -template -void swap(T* a, T* b); - -template -void swap(T (& a)[N], T (& b)[N]); -} // namespace memory - - - - -// Allocator Functions -void* alloc(Allocator* a, usize size, usize align = GB_DEFAULT_ALIGNMENT); -void free(Allocator* a, void* ptr); -s64 allocated_size(Allocator* a, void const* ptr); -s64 total_allocated(Allocator* a); - -template -inline T* alloc_struct(Allocator* a) { return static_cast(alloc(a, sizeof(T), alignof(T))); } - -template -inline T* alloc_array(Allocator* a, usize count) { return static_cast(alloc(a, count * sizeof(T), alignof(T))); } - - - - - -//////////////////////////////// -// // -// String // -// // -// C compatible string // -// // -//////////////////////////////// - -// A "better" string type that is compatible with C style read-only functions -using String = char*; - -namespace string -{ -using Size = u32; - -struct Header -{ - Allocator* allocator; - Size length; - Size capacity; -}; - -inline Header* header(String str) { return reinterpret_cast(str) - 1; } - -String make(Allocator* a, char const* str = ""); -String make(Allocator* a, void const* str, Size num_bytes); -void free(String str); - -String duplicate(Allocator* a, String const str); - -Size length(String const str); -Size capacity(String const str); -Size available_space(String const str); - -void clear(String str); - -void append(String* str, char c); -void append(String* str, String const other); -void append_cstring(String* str, char const* other); -void append(String* str, void const* other, Size num_bytes); - -void make_space_for(String* str, Size add_len); -usize allocation_size(String const str); - -bool equals(String const lhs, String const rhs); -int compare(String const lhs, String const rhs); // NOTE(bill): three-way comparison - -void trim(String* str, char const* cut_set); -void trim_space(String* str); -} // namespace string - - -// TODO(bill): string libraries - - - -//////////////////////////////// -// // -// Array // -// // -//////////////////////////////// - -#ifndef GB_ARRAY_BOUND_CHECKING -#define GB_ARRAY_BOUND_CHECKING 1 -#endif - -// Dynamic resizable array for POD types only -template -struct Array -{ - using Type = T; - - Allocator* allocator; - s64 count; - s64 capacity; - T* data; - - T const& operator[](usize index) const; - T& operator[](usize index); -}; - -// NOTE(bill): There are not ctor/dtor for Array. -// These are explicit functions e.g. -/* -auto old_array = array::make(...); -auto new_array = array::copy(old_array); -array::free(&old_array); -array::free(&new_array); -*/ -// This allows functions to be passed by value at a low cost - -namespace array -{ -// Helper functions to make, free, and copy an array -template Array make(Allocator* allocator, usize count = 0); -template void free(Array* array); -// TODO(bill): Is passing by value okay here or is pass by const& ? -// (sizeof(Array) = 16 + sizeof(void*)) (24 bytes on x86, 32 bytes on x64) -template Array copy(Array array, Allocator* allocator = nullptr); - -// Appends `item` to the end of the array -template void append(Array* a, T const& item); -template void append(Array* a, T&& item); -// Appends `items[count]` to the end of the array -template void append(Array* a, T const* items, usize count); -// Append the contents of another array of the same type -template void append(Array* a, Array other); - -// Pops the last item form the array. The array cannot be empty. -template void pop(Array* a); - -// Removes all items from the array - does not free memory -template void clear(Array* a); -// Modify the size of a array - only reallocates when necessary -template void resize(Array* a, usize count); -// Makes sure that the array has at least the specified capacity - or the array the grows -template void reserve(Array* a, usize capacity); -// Reallocates the array to the specific capacity -template void set_capacity(Array* a, usize capacity); -// Grows the array to keep append() to be O(1) -template void grow(Array* a, usize min_capacity = 0); -} // namespace array - -// Used to iterate over the array with a C++11 for loop -template inline T* begin(Array& a) { return a.data; } -template inline T const* begin(Array const& a) { return a.data; } -template inline T* begin(Array&& a) { return a.data; } -template inline T* end(Array& a) { return a.data + a.count; } -template inline T const* end(Array const& a) { return a.data + a.count; } -template inline T* end(Array&& a) { return a.data + a.count; } - - - - - -//////////////////////////////// -// // -// Hash Table // -// // -//////////////////////////////// - -// Hash table for POD types only with a u64 key -template -struct Hash_Table -{ - using Type = T; - - struct Entry - { - u64 key; - s64 next; - T value; - }; - - Array hashes; - Array entries; -}; - -namespace hash_table -{ -// Helper function to make, free, and copy a hash table -template Hash_Table make(Allocator* a); -template void free(Hash_Table* h); -template Hash_Table copy(Hash_Table const& h, Allocator* a = nullptr); - -// Return `true` if the specified key exist in the hash table -template bool has(Hash_Table const& h, u64 key); -// Returns the value stored at the key, or a `default_value` if the key is not found in the hash table -template T const& get(Hash_Table const& h, u64 key, T const& default_value); -// Sets the value for the key in the hash table -template void set(Hash_Table* h, u64 key, T const& value); -template void set(Hash_Table* h, u64 key, T&& value); -// Removes the key from the hash table if it exists -template void remove(Hash_Table* h, u64 key); -// Resizes the hash table's lookup table to the specified size -template void reserve(Hash_Table* h, usize capacity); -// Remove all elements from the hash table -template void clear(Hash_Table* h); -} // namespace hash_table - -// Used to iterate over the array with a C++11 for loop - in random order -template typename Hash_Table::Entry const* begin(Hash_Table const& h); -template typename Hash_Table::Entry const* end(Hash_Table const& h); - -namespace multi_hash_table -{ -// Outputs all the items that with the specified key -template void get(Hash_Table const& h, u64 key, Array& items); -// Returns the count of entries with the specified key -template usize count(Hash_Table const& h, u64 key); - -// Finds the first entry with specified key in the hash table -template typename Hash_Table::Entry const* find_first(Hash_Table const& h, u64 key); -// Finds the next entry with same key as `e` -template typename Hash_Table::Entry const* find_next(Hash_Table const& h, typename Hash_Table::Entry const* e); - -// Inserts the `value` as an additional value for the specified key -template void insert(Hash_Table* h, u64 key, T const& value); -template void insert(Hash_Table* h, u64 key, T&& value); -// Removes a specified entry `e` from the hash table -template void remove_entry(Hash_Table* h, typename Hash_Table::Entry const* e); -// Removes all entries with from the hash table with the specified key -template void remove_all(Hash_Table* h, u64 key); -} // namespace multi_hash_table - - - - - -//////////////////////////////// -// // -// Hash // -// // -//////////////////////////////// - -namespace hash -{ -u32 adler32(void const* key, u32 num_bytes); - -u32 crc32(void const* key, u32 num_bytes); -u64 crc64(void const* key, usize num_bytes); - -u32 fnv32(void const* key, usize num_bytes); -u64 fnv64(void const* key, usize num_bytes); -u32 fnv32a(void const* key, usize num_bytes); -u64 fnv64a(void const* key, usize num_bytes); - -u32 murmur32(void const* key, u32 num_bytes, u32 seed = 0x9747b28c); -u64 murmur64(void const* key, usize num_bytes, u64 seed = 0x9747b28c); -} // namespace hash - - - -//////////////////////////////// -// // -// Sort // -// // -//////////////////////////////// - -namespace sort -{ -// Comparison_Function -// NOTE(bill): Similar to str(n)cmp -// a < b --> -1 -// a == b --> 0 -// a > b --> +1 - -// Quick Sort (Qsort) -template -void quick(T* array, usize count, Comparison_Function compare); - -// TODO(bill): Implement other sorting algorithms -} // namespace sort - - -//////////////////////////////// -// // -// Time // -// // -//////////////////////////////// - -struct Time -{ - s64 microseconds; -}; - -extern Time const TIME_ZERO; - -namespace time -{ -Time now(); -void sleep(Time time); - -Time seconds(f32 s); -Time milliseconds(s32 ms); -Time microseconds(s64 us); - -f32 as_seconds(Time t); -s32 as_milliseconds(Time t); -s64 as_microseconds(Time t); -} // namespace time - -bool operator==(Time left, Time right); -bool operator!=(Time left, Time right); - -bool operator<(Time left, Time right); -bool operator>(Time left, Time right); - -bool operator<=(Time left, Time right); -bool operator>=(Time left, Time right); - -Time operator+(Time right); -Time operator-(Time right); - -Time operator+(Time left, Time right); -Time operator-(Time left, Time right); - -Time& operator+=(Time& left, Time right); -Time& operator-=(Time& left, Time right); - -Time operator*(Time left, f32 right); -Time operator*(Time left, s64 right); -Time operator*(f32 left, Time right); -Time operator*(s64 left, Time right); - -Time& operator*=(Time& left, f32 right); -Time& operator*=(Time& left, s64 right); - -Time operator/(Time left, f32 right); -Time operator/(Time left, s64 right); - -Time& operator/=(Time& left, f32 right); -Time& operator/=(Time& left, s64 right); - -f32 operator/(Time left, Time right); - -Time operator%(Time left, Time right); -Time& operator%=(Time& left, Time right); - - - - - -//////////////////////////////// -// // -// OS // -// // -//////////////////////////////// - -// TODO(bill): Should this be system:: vs os:: ? -namespace os -{ -u64 rdtsc(); -} // namespace os - - - - - - -//////////////////////////////// -// // -// Template Implementations // -// // -//////////////////////////////// - -//////////////////////////////// -// // -// Array // -// // -//////////////////////////////// - -template -inline T const& -Array::operator[](usize index) const -{ -#if GB_ARRAY_BOUND_CHECKING - GB_ASSERT(index < static_cast(capacity), "Array out of bounds"); -#endif - return data[index]; -} - -template -inline T& -Array::operator[](usize index) -{ -#if GB_ARRAY_BOUND_CHECKING - GB_ASSERT(index < static_cast(capacity), "Array out of bounds"); -#endif - return data[index]; -} - - -namespace array -{ -template -inline Array -make(Allocator* allocator, usize count) -{ - Array result = {}; - result.allocator = allocator; - - if (count > 0) - { - result.data = alloc_array(allocator, count); - if (result.data) - result.count = result.capacity = count; - } - - return result; -} - -template -inline void -free(Array* a) -{ - if (a->allocator) - free(a->allocator, a->data); - a->count = 0; - a->capacity = 0; - a->data = nullptr; -} - -template -inline Array -copy(Array other, Allocator* allocator) -{ - Array result = {}; - - if (allocator) - result.allocator = allocator; - else - result.allocator = other.allocator; - - auto new_count = other.count; - - array::resize(&result, new_count); - memory::copy_array(other.data, new_count, data); - - return result; -} - - - - -template -inline void -append(Array* a, T const& item) -{ - if (a->capacity < a->count + 1) - array::grow(a); - a->data[a->count++] = item; -} - -template -inline void -append(Array* a, T&& item) -{ - if (a->capacity < a->count + 1) - array::grow(a); - a->data[a->count++] = move_ownership(item); -} - -template -inline void -append(Array* a, T const* items, usize count) -{ - if (a->capacity <= a->count + static_cast(count)) - array::grow(a, a->count + count); - - memory::copy_array(items, count, &a->data[a->count]); - a->count += count; -} - -template -inline void -append(Array* a, Array other) -{ - array::append(a, other.data, other.count); -} - - -template -inline void -pop(Array* a) -{ - GB_ASSERT(a->count > 0); - - a->count--; -} - -template -inline void -clear(Array* a) -{ - a->count = 0; -} - -template -inline void -resize(Array* a, usize count) -{ - if (a->capacity < static_cast(count)) - array::grow(a, count); - a->count = count; -} - -template -inline void -reserve(Array* a, usize capacity) -{ - if (a->capacity < static_cast(capacity)) - array::set_capacity(a, capacity); -} - -template -inline void -set_capacity(Array* a, usize capacity) -{ - if (static_cast(capacity) == a->capacity) - return; - - if (static_cast(capacity) < a->count) - array::resize(a, capacity); - - T* data = nullptr; - if (capacity > 0) - { - data = alloc_array(a->allocator, capacity); - memory::copy_array(a->data, a->count, data); - } - free(a->allocator, a->data); - a->data = data; - a - - ->capacity = capacity; -} - -template -inline void -grow(Array* a, usize min_capacity) -{ - // TODO(bill): Decide on decent growing formula for Array - usize capacity = 2 * a->capacity + 8; - if (capacity < min_capacity) - capacity = min_capacity; - set_capacity(a, capacity); -} -} // namespace array - - - - - - - -//////////////////////////////// -// // -// Hash Table // -// // -//////////////////////////////// - -namespace hash_table -{ -template -inline Hash_Table -make(Allocator* a) -{ - Hash_Table result = {}; - - result.hashes = array::make(a); - result.entries = array::make::Entry>(a); - - return result; -} - -template -inline void -free(Hash_Table* h) -{ - if (h->hashes.allocator) - array::free(&h->hashes); - - if (h->entries.allocator) - array::free(&h->entries); -} - -template -inline Hash_Table -copy(Hash_Table const& other, Allocator* allocator) -{ - Allocator* a = other.hashes.allocator; - if (allocator) a = allocator; - - Hash_Table result = {}; - result.hashes = array::copy(other.hashes, a); - result.entries = array::copy(other.entries, a); - - return result; -} - - -namespace impl -{ -struct Find_Result -{ - s64 hash_index; - s64 data_prev; - s64 entry_index; -}; - -template usize add_entry(Hash_Table* h, u64 key); -template void erase(Hash_Table* h, Find_Result const& fr); -template Find_Result find_result_from_key(Hash_Table const& h, u64 key); -template Find_Result find_result_from_entry(Hash_Table const& h, typename Hash_Table::Entry const* e); -template s64 make_entry(Hash_Table* h, u64 key); -template void find_and_erase_entry(Hash_Table* h, u64 key); -template s64 find_entry_or_fail(Hash_Table const& h, u64 key); -template s64 find_or_make_entry(Hash_Table* h, u64 key); -template void rehash(Hash_Table* h, usize new_capacity); -template void grow(Hash_Table* h); -template bool is_full(Hash_Table* h); - -template -usize -add_entry(Hash_Table* h, u64 key) -{ - typename Hash_Table::Entry e = {}; - e.key = key; - e.next = -1; - usize e_index = h->entries.count; - array::append(&h->entries, e); - - return e_index; -} - -template -void -erase(Hash_Table* h, Find_Result const& fr) -{ - if (fr.data_prev < 0) - h->hashes[fr.hash_index] = h->entries[fr.entry_index].next; - else - h->entries[fr.data_prev].next = h->entries[fr.entry_index].next; - - array::pop(&h->entries); // Update array count - - if (fr.entry_index == h->entries.count) - return; - - h->entries[fr.entry_index] = h->entries[h->entries.count]; - - auto last = impl::find_result_from_key(*h, h->entries[fr.entry_index].key); - - if (last.data_prev < 0) - h->hashes[last.hash_index] = fr.entry_index; - else - h->entries[last.entry_index].next = fr.entry_index; -} - -template -Find_Result -find_result_from_key(Hash_Table const& h, u64 key) -{ - Find_Result fr = {}; - fr.hash_index = -1; - fr.data_prev = -1; - fr.entry_index = -1; - - if (h.hashes.count == 0) - return fr; - - fr.hash_index = key % h.hashes.count; - fr.entry_index = h.hashes[fr.hash_index]; - while (fr.entry_index >= 0) - { - if (h.entries[fr.entry_index].key == key) - return fr; - fr.data_prev = fr.entry_index; - fr.entry_index = h.entries[fr.entry_index].next; - } - - return fr; -} - - -template -Find_Result -find_result_from_entry(Hash_Table const& h, typename Hash_Table::Entry const* e) -{ - Find_Result fr = {}; - fr.hash_index = -1; - fr.data_prev = -1; - fr.entry_index = -1; - - if (h.hashes.count == 0 || !e) - return fr; - - fr.hash_index = e->key % h.hashes.count; - fr.entry_index = h.hashes[fr.hash_index]; - while (fr.entry_index >= 0) - { - if (&h.entries[fr.entry_index] == e) - return fr; - fr.data_prev = fr.entry_index; - fr.entry_index = h.entries[fr.entry_index].next; - } - - return fr; -} - -template -s64 -make_entry(Hash_Table* h, u64 key) -{ - Find_Result const fr = impl::find_result_from_key(*h, key); - s64 const index = impl::add_entry(h, key); - - if (fr.data_prev < 0) - h->hashes[fr.hash_index] = index; - else - h->entries[fr.data_prev].next = index; - - h->entries[index].next = fr.entry_index; - - return index; -} - -template -void -find_and_erase_entry(Hash_Table* h, u64 key) -{ - Find_Result const fr = impl::find_result_from_key(*h, key); - if (fr.entry_index >= 0) - impl::erase(h, fr); -} - -template -s64 -find_entry_or_fail(Hash_Table const& h, u64 key) -{ - return impl::find_result_from_key(h, key).entry_index; -} - -template -s64 -find_or_make_entry(Hash_Table* h, u64 key) -{ - auto const fr = impl::find_result_from_key(*h, key); - if (fr.entry_index >= 0) - return fr.entry_index; - - s64 index = impl::add_entry(h, key); - if (fr.data_prev < 0) - h->hashes[fr.hash_index] = index; - else - h->entries[fr.data_prev].next = index; - - return index; -} - -template -void -rehash(Hash_Table* h, usize new_capacity) -{ - auto nh = hash_table::make(h->hashes.allocator); - array::resize(&nh.hashes, new_capacity); - array::reserve(&nh.entries, h->entries.count); - - for (usize i = 0; i < new_capacity; i++) - nh.hashes[i] = -1; - - for (s64 i = 0; i < h->entries.count; i++) - { - auto const* e = &h->entries[i]; - multi_hash_table::insert(&nh, e->key, e->value); - } - - Hash_Table empty_ht{h->hashes.allocator}; - hash_table::free(h); - - memory::copy_struct(&nh, h); - memory::copy_struct(&empty_ht, &nh); -} - -template -inline void -grow(Hash_Table* h) -{ - const usize new_capacity = 2 * h->entries.count + 8; - impl::rehash(h, new_capacity); -} - -template -inline bool -is_full(Hash_Table* h) -{ - // Make sure that there is enough space - f64 const maximum_load_coefficient = 0.75; - return h->entries.count >= maximum_load_coefficient * h->hashes.count; -} -} // namespace impl - -template -inline bool -has(Hash_Table const& h, u64 key) -{ - return impl::find_entry_or_fail(h, key) >= 0; -} - -template -inline T const& -get(Hash_Table const& h, u64 key, T const& default_value) -{ - s64 const index = impl::find_entry_or_fail(h, key); - - if (index < 0) - return default_value; - return h.entries[index].value; -} - -template -inline void -set(Hash_Table* h, u64 key, T const& value) -{ - if (h->hashes.count == 0) - impl::grow(h); - - s64 const index = impl::find_or_make_entry(h, key); - h->entries[index].value = value; - if (impl::is_full(h)) - impl::grow(h); -} - -template -inline void -set(Hash_Table* h, u64 key, T&& value) -{ - if (h->hashes.count == 0) - impl::grow(h); - - s64 const index = impl::find_or_make_entry(h, key); - h->entries[index].value = move_ownership(value); - if (impl::is_full(h)) - impl::grow(h); -} - -template -inline void -remove(Hash_Table* h, u64 key) -{ - impl::find_and_erase_entry(h, key); -} - -template -inline void -reserve(Hash_Table* h, usize capacity) -{ - impl::rehash(h, capacity); -} - -template -inline void -clear(Hash_Table* h) -{ - array::clear(&h->hashes); - array::clear(&h->entries); -} -} // namespace hash_table - -template -inline typename Hash_Table::Entry const* -begin(Hash_Table const& h) -{ - return begin(h.entries); -} - -template -inline typename Hash_Table::Entry const* -end(Hash_Table const& h) -{ - return end(h.entries); -} - - -namespace multi_hash_table -{ -template -inline void -get(Hash_Table const& h, u64 key, Array& items) -{ - auto e = multi_hash_table::find_first(h, key); - while (e) - { - array::append(items, e->value); - e = multi_hash_table::find_next(h, e); - } -} - -template -inline usize -count(Hash_Table const& h, u64 key) -{ - usize count = 0; - auto e = multi_hash_table::find_first(h, key); - while (e) - { - count++; - e = multi_hash_table::find_next(h, e); - } - - return count; -} - - -template -inline typename Hash_Table::Entry const* -find_first(Hash_Table const& h, u64 key) -{ - s64 const index = hash_table::impl::find_entry_or_fail(h, key); - if (index < 0) - return nullptr; - return &h.entries[index]; -} - -template -typename Hash_Table::Entry const* -find_next(Hash_Table const& h, typename Hash_Table::Entry const* e) -{ - if (!e) - return nullptr; - - auto index = e->next; - while (index >= 0) - { - if (h.entries[index].key == e->key) - return &h.entries[index]; - index = h.entries[index].next; - } - - return nullptr; -} - - -template -inline void -insert(Hash_Table* h, u64 key, T const& value) -{ - if (h->hashes.count == 0) - hash_table::impl::grow(h); - - auto next = hash_table::impl::make_entry(h, key); - h->entries[next].value = value; - - if (hash_table::impl::is_full(h)) - hash_table::impl::grow(h); -} - -template -inline void -insert(Hash_Table* h, u64 key, T&& value) -{ - if (h->hashes.count == 0) - hash_table::impl::grow(h); - - auto next = hash_table::impl::make_entry(h, key); - h->entries[next].value = move_ownership(value); - - if (hash_table::impl::is_full(h)) - hash_table::impl::grow(h); -} - -template -inline void -remove_entry(Hash_Table* h, typename Hash_Table::Entry const* e) -{ - auto const fr = hash_table::impl::find_result_from_entry(*h, e); - if (fr.entry_index >= 0) - hash_table::impl::erase(h, fr); -} - -template -inline void -remove_all(Hash_Table* h, u64 key) -{ - while (hash_table::has(*h, key)) - hash_table::remove(h, key); -} -} // namespace multi_hash_table - - -namespace memory -{ -template -inline void -fill(T* ptr, usize count, T const& value) -{ - for (usize i = 0; i < count; i++) - ptr[i] = value; -} - -template -inline void -fill(T* ptr, usize count, T&& value) -{ - for (usize i = 0; i < count; i++) - ptr[i] = move_ownership(value); -} - -template -inline void -zero_struct(T* ptr) -{ - memory::zero(ptr, sizeof(T)); -} - -template -inline void -zero_array(T* ptr, usize count) -{ - memory::zero(ptr, count * sizeof(T)); -} - -template -inline void -copy_array(T const* src_array, usize count, T* dest_array) -{ - memory::copy(src_array, count * sizeof(T), dest_array); -} - -template -inline void -copy_struct(T const* src_array, T* dest_array) -{ - memory::copy(src_array, sizeof(T), dest_array); -} - - -template -inline void -swap(T* a, T* b) -{ - T c = move_ownership(*a); - *a = move_ownership(*b); - *b = move_ownership(c); -} - -template -inline void -swap(T (& a)[N], T (& b)[N]) -{ - for (usize i = 0; i < N; i++) - math::swap(&a[i], &b[i]); -} -} // namespace memory - - - -//////////////////////////////// -// // -// Sort // -// // -//////////////////////////////// - -namespace sort -{ -template -void -quick(T* array, usize count, Comparison_Function compare) -{ - if (count < 2) return; - - T const& mid = array[count/2]; - - s64 i = 0; - s64 j = count-1; - - while (true) - { - while (compare(array[i], mid) < 0) i++; - while (compare(mid, array[j]) < 0) j--; - - if (i >= j) break; - - memory::swap(&array[i], &array[j]); - - i++; - j--; - } - - sort::quick(array, i, compare); - sort::quick(array+i, count-i, compare); -} -} // namespace sort - - - -__GB_NAMESPACE_END - -#endif // GB_INCLUDE_GB_HPP - -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// It's turtles all the way down! -// -// -// -// -// -//////////////////////////////// -// // -// Implemenation // -// // -//////////////////////////////// - -#if defined(GB_IMPLEMENTATION) - -#if defined(GB_SYSTEM_WINDOWS) - - #if !defined(GB_NO_STDIO) - #include - #pragma comment(lib, "dbghelp.lib") // TODO(bill): Should this be pragma included or not? - - internal_linkage void - gb__print_call_stack(FILE* out_stream) - { - SymInitialize(GetCurrentProcess(), nullptr, true); - SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME); - - DWORD mtype = {}; - CONTEXT ctx = {}; - ctx.ContextFlags = CONTEXT_CONTROL; - - RtlCaptureContext(&ctx); - - STACKFRAME64 stack = {}; - - #if defined(_M_IX86) - mtype = IMAGE_FILE_MACHINE_I386; - stack.AddrPC.Offset = ctx.Eip; - stack.AddrPC.Mode = AddrModeFlat; - stack.AddrFrame.Offset = ctx.Ebp; - stack.AddrFrame.Mode = AddrModeFlat; - stack.AddrStack.Offset = ctx.Esp; - stack.AddrStack.Mode = AddrModeFlat; - #elif defined(_M_X64) - mtype = IMAGE_FILE_MACHINE_AMD64; - stack.AddrPC.Offset = ctx.Rip; - stack.AddrPC.Mode = AddrModeFlat; - stack.AddrFrame.Offset = ctx.Rsp; - stack.AddrFrame.Mode = AddrModeFlat; - stack.AddrStack.Offset = ctx.Rsp; - stack.AddrStack.Mode = AddrModeFlat; - #else - #error Unknown Windows Platform - #endif - - DWORD ldsp = 0; - IMAGEHLP_LINE64 line = {}; - line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); - - char buf[sizeof(SYMBOL_INFO) + (MAX_SYM_NAME * sizeof(TCHAR))]; - - SYMBOL_INFO* sym = reinterpret_cast(buf); - sym->SizeOfStruct = sizeof(SYMBOL_INFO); - sym->MaxNameLen = MAX_SYM_NAME; - - UINT layer_count = 0; - while (StackWalk64(mtype, - GetCurrentProcess(), GetCurrentThread(), - &stack, &ctx, nullptr, - SymFunctionTableAccess64, SymGetModuleBase64, nullptr)) - { - if (stack.AddrPC.Offset == 0) - break; - - BOOL result = SymGetLineFromAddr64(GetCurrentProcess(), stack.AddrPC.Offset, &ldsp, &line); - result = result && SymFromAddr(GetCurrentProcess(), stack.AddrPC.Offset, 0, sym); - - if (result) - { - fprintf(out_stream, - "\t[%u] `%s` (%s:%d)\n", - layer_count, sym->Name, line.FileName, line.LineNumber); - } - else - { - fprintf(out_stream, - "\t[%u] 0x%p\n", - layer_count, stack.AddrPC.Offset); - } - - layer_count++; - } - - SymCleanup(GetCurrentProcess()); - } - #endif -#else - #error gb__print_call_stack() not implemeneted - // TODO(bill): Implemenet gb__print_call_stack() -#endif - -// Helper function used as a better alternative to assert which allows for -// optional printf style error messages -inline void -gb__assert_handler(bool condition, char const* condition_str, - char const* filename, size_t line, - char const* error_text, ...) -{ - if (condition) - return; - -#if !defined(GB_NO_STDIO) - FILE* out_stream = stderr; - - fprintf(out_stream, "ASSERT! %s(%lu): %s", filename, line, condition_str); - if (error_text) - { - fprintf(out_stream, " - "); - - va_list args; - va_start(args, error_text); - vfprintf(out_stream, error_text, args); - va_end(args); - } - fprintf(out_stream, "\n"); - - fprintf(out_stream, "Stacktrack:\n"); - gb__print_call_stack(out_stream); -#endif - - // TODO(bill): Are these decent breaking functions??? -#if defined(GB_COMPILER_MSVC) - __debugbreak(); -#elif defined(GB_COMPILER_GNU_GCC) - __builtin_trap(); -#else - #error Implement aborting function -#endif -} - - - -__GB_NAMESPACE_START - -//////////////////////////////// -// // -// Memory // -// // -//////////////////////////////// - -namespace mutex -{ -inline Mutex -make() -{ - Mutex m = {}; -#if defined(GB_SYSTEM_WINDOWS) - m.win32_mutex = CreateMutex(0, false, 0); -#else - pthread_mutex_init(&m.posix_mutex, nullptr); -#endif - return m; -} - -inline void -destroy(Mutex* m) -{ -#if defined(GB_SYSTEM_WINDOWS) - CloseHandle(m->win32_mutex); -#else - pthread_mutex_destroy(&m->posix_mutex); -#endif -} - - -inline void -lock(Mutex* m) -{ -#if defined(GB_SYSTEM_WINDOWS) - WaitForSingleObject(m->win32_mutex, INFINITE); -#else - pthread_mutex_lock(&m->posix_mutex); -#endif -} - -inline bool -try_lock(Mutex* m) -{ -#if defined(GB_SYSTEM_WINDOWS) - return WaitForSingleObject(m->win32_mutex, 0) == WAIT_OBJECT_0; -#else - return pthread_mutex_trylock(&m->posix_mutex) == 0; -#endif -} - - -inline void -unlock(Mutex* m) -{ -#if defined(GB_SYSTEM_WINDOWS) - ReleaseMutex(m->win32_mutex); -#else - pthread_mutex_unlock(&m->posix_mutex); -#endif -} -} // namespace mutex - - - - - -// Atomics -namespace atomic -{ -#if defined(_MSC_VER) -inline u32 -load(Atomic32 const volatile* object) -{ - return object->nonatomic; -} - -inline void -store(Atomic32 volatile* object, u32 value) -{ - object->nonatomic = value; -} - -inline u32 -compare_exchange_strong(Atomic32 volatile* object, u32 expected, u32 desired) -{ - return _InterlockedCompareExchange(reinterpret_cast(object), desired, expected); -} - -inline u32 -exchanged(Atomic32 volatile* object, u32 desired) -{ - return _InterlockedExchange(reinterpret_cast(object), desired); -} - -inline u32 -fetch_add(Atomic32 volatile* object, s32 operand) -{ - return _InterlockedExchangeAdd(reinterpret_cast(object), operand); -} - -inline u32 -fetch_and(Atomic32 volatile* object, u32 operand) -{ - return _InterlockedAnd(reinterpret_cast(object), operand); -} - -inline u32 -fetch_or_32(Atomic32 volatile* object, u32 operand) -{ - return _InterlockedOr(reinterpret_cast(object), operand); -} - -inline u64 -load(Atomic64 const volatile* object) -{ -#if defined(GB_ARCH_64_BIT) - return object->nonatomic; -#else - // NOTE(bill): The most compatible way to get an atomic 64-bit load on x86 is with cmpxchg8b - u64 result; - __asm - { - mov esi, object; - mov ebx, eax; - mov ecx, edx; - lock cmpxchg8b [esi]; - mov dword ptr result, eax; - mov dword ptr result[4], edx; - } - return result; -#endif -} - -inline void -store(Atomic64 volatile* object, u64 value) -{ -#if defined(GB_ARCH_64_BIT) - object->nonatomic = value; -#else - // NOTE(bill): The most compatible way to get an atomic 64-bit store on x86 is with cmpxchg8b - __asm - { - mov esi, object; - mov ebx, dword ptr value; - mov ecx, dword ptr value[4]; - retry: - cmpxchg8b [esi]; - jne retry; - } -#endif -} - -inline u64 -compare_exchange_strong(Atomic64 volatile* object, u64 expected, u64 desired) -{ - return _InterlockedCompareExchange64(reinterpret_cast(object), desired, expected); -} - -inline u64 -exchanged(Atomic64 volatile* object, u64 desired) -{ -#if defined(GB_ARCH_64_BIT) - return _InterlockedExchange64(reinterpret_cast(object), desired); -#else - u64 expected = object->nonatomic; - while (true) - { - u64 original = _InterlockedCompareExchange64(reinterpret_cast(object), desired, expected); - if (original == expected) - return original; - expected = original; - } -#endif -} - -inline u64 -fetch_add(Atomic64 volatile* object, s64 operand) -{ -#if defined(GB_ARCH_64_BIT) - return _InterlockedExchangeAdd64(reinterpret_cast(object), operand); -#else - u64 expected = object->nonatomic; - while (true) - { - u64 original = _InterlockedExchange64(reinterpret_cast(object), expected + operand, expected); - if (original == expected) - return original; - expected = original; - } -#endif -} - -inline u64 -fetch_and(Atomic64 volatile* object, u64 operand) -{ -#if defined(GB_ARCH_64_BIT) - return _InterlockedAnd64(reinterpret_cast(object), operand); -#else - u64 expected = object->nonatomic; - while (true) - { - u64 original = _InterlockedCompareExchange64(reinterpret_cast(object), expected & operand, expected); - if (original == expected) - return original; - expected = original; - } -#endif -} - -inline u64 -fetch_or(Atomic64 volatile* object, u64 operand) -{ -#if defined(GB_ARCH_64_BIT) - return _InterlockedAnd64(reinterpret_cast(object), operand); -#else - u64 expected = object->nonatomic; - while (true) - { - u64 original = _InterlockedCompareExchange64(reinterpret_cast(object), expected | operand, expected); - if (original == expected) - return original; - expected = original; - } -#endif -} - -#else -#error TODO(bill): Implement atomics for this platform -#endif -} // namespace atomic - - - - -namespace semaphore -{ -Semaphore -make() -{ - Semaphore semaphore = {}; -#if defined(GB_SYSTEM_WINDOWS) - semaphore.win32_handle = CreateSemaphore(nullptr, 0, GB_S32_MAX, nullptr); - - GB_ASSERT(semaphore.win32_handle != nullptr, "CreateSemaphore: GetLastError = %d", GetLastError()); - -#else - semaphore.count = 0; - s32 result = pthread_cond_init(&semaphore.cond, nullptr); - GB_ASSERT(result == 0, "pthread_cond_init: errno = %d", result); - - semaphore.mutex = mutex::make(); -#endif - - return semaphore; -} - -void -destroy(Semaphore* semaphore) -{ -#if defined(GB_SYSTEM_WINDOWS) - BOOL err = CloseHandle(semaphore->win32_handle); - GB_ASSERT(err != 0, "CloseHandle: GetLastError = %d", GetLastError()); -#else - s32 result = pthread_cond_destroy(&semaphore->cond); - GB_ASSERT(result == 0, "pthread_cond_destroy: errno = %d", result); - mutex::destroy(&semaphore->mutex); -#endif -} - -void -post(Semaphore* semaphore, u32 count) -{ -#if defined(GB_SYSTEM_WINDOWS) - BOOL err = ReleaseSemaphore(semaphore->win32_handle, count, nullptr); - GB_ASSERT(err != 0, "ReleaseSemaphore: GetLastError = %d", GetLastError()); -#else - mutex::lock(&semaphore->mutex); - - for (u32 i = 0; i < count; i++) - { - s32 result = pthread_cond_signal(&semaphore->cond); - GB_ASSERT(result == 0, "pthread_cond_signal: errno = %d", result); - } - - semaphore->count += count; - - mutex::unlock(&semaphore->mutex); -#endif -} - -void -wait(Semaphore* semaphore) -{ -#if defined(GB_SYSTEM_WINDOWS) - DWORD result = WaitForSingleObject(semaphore->win32_handle, INFINITE); - GB_ASSERT(result == WAIT_OBJECT_0, "WaitForSingleObject: GetLastError = %d", GetLastError()); -#else - mutex::lock(&semaphore->mutex); - - while (count <= 0) - { - s32 result = pthread_cond_wait(&semaphore->cond, &semaphore->mutex.posix_mutex); - GB_ASSERT(result == 0, "pthread_cond_wait: errno = %d", result); - } - - count--; - - mutex::unlock(&semaphore->mutex); -#endif -} -} // namespace semaphore - -namespace thread -{ -Thread -make() -{ - Thread t = {}; -#if defined(GB_SYSTEM_WINDOWS) - t.win32_handle = INVALID_HANDLE_VALUE; -#else - t.posix_handle = 0; -#endif - t.function = nullptr; - t.data = nullptr; - t.stack_size = 0; - t.is_running = false; - t.semaphore = semaphore::make(); - - return t; -} - -void -destroy(Thread* t) -{ - if (t->is_running) - thread::join(t); - - semaphore::destroy(&t->semaphore); -} - -internal_linkage void -run(Thread* t) -{ - semaphore::post(&t->semaphore); - t->function(t->data); -} - -#if defined(GB_SYSTEM_WINDOWS) -internal_linkage DWORD WINAPI -thread_proc(void* arg) -{ - thread::run(static_cast(arg)); - return 0; -} - -#else -internal_linkage void* -thread_proc(void* arg) -{ - thread::run(static_cast(arg)); - return nullptr; -} -#endif - -void -start(Thread* t, Thread_Procedure* func, void* data, usize stack_size) -{ - GB_ASSERT(!t->is_running); - GB_ASSERT(func != nullptr); - t->function = func; - t->data = data; - t->stack_size = stack_size; - -#if defined(GB_SYSTEM_WINDOWS) - t->win32_handle = CreateThread(nullptr, stack_size, thread_proc, t, 0, nullptr); - GB_ASSERT(t->win32_handle != nullptr, - "CreateThread: GetLastError = %d", GetLastError()); - -#else - pthread_attr_t attr; - s32 result = pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); - GB_ASSERT(result == 0, "pthread_attr_init: errno = %d", result); - - if (t->stack_size != 0) - { - result = pthread_attr_setstacksize(&attr, t->stack_size); - GB_ASSERT(result == 0, "pthread_attr_setstacksize: errno = %d", result); - } - - result = pthread_create(&t->posix_handle, &attr, thread_proc, thread); - GB_ASSERT(result == 0, "pthread_create: errno = %d", result); - - // NOTE(bill): Free attr memory - result = pthread_attr_destroy(&attr); - GB_ASSERT(result == 0, "pthread_attr_destroy: errno = %d", result); - - // NOTE(bill): So much boiler patch compared to windows.h (for once) -#endif - - t->is_running = true; - semaphore::wait(&t->semaphore); -} - -void -join(Thread* t) -{ - if (!t->is_running) return; - -#if defined(GB_SYSTEM_WINDOWS) - WaitForSingleObject(t->win32_handle, INFINITE); - CloseHandle(t->win32_handle); - t->win32_handle = INVALID_HANDLE_VALUE; -#else - int result = pthread_join(t->posix_handle, nullptr); - t->posix_handle = 0; -#endif - - t->is_running = false; -} - -inline bool -is_running(Thread const& thread) -{ - return thread.is_running != 0; -} - -inline u32 -current_id() -{ - u32 thread_id; - -#if defined(GB_SYSTEM_WINDOWS) - u8* thread_local_storage = reinterpret_cast(__readgsqword(0x30)); - thread_id = *reinterpret_cast(thread_local_storage + 0x48); - -#elif defined(GB_SYSTEM_OSX) && defined(GB_ARCH_64_BIT) - asm("mov %%gs:0x00,%0" : "=r"(thread_id)); -#elif defined(GB_ARCH_32_BIT) - asm("mov %%gs:0x08,%0" : "=r"(thread_id)); -#elif defined(GB_ARCH_64_BIT) - asm("mov %%gs:0x10,%0" : "=r"(thread_id)); -#else - #error Unsupported architecture for thread::current_id() -#endif - - return thread_id; -} - - -} // namespace thread - - -namespace heap -{ -namespace functions -{ -internal_linkage void* -alloc(Allocator* a, usize size, usize align) -{ - Heap* heap = reinterpret_cast(a); - - if (heap->use_mutex) mutex::lock(&heap->mutex); - - usize total = size + align - (size % align); - -#if defined (GB_SYSTEM_WINDOWS) - total += sizeof(Heap::Header); - - void* data = HeapAlloc(heap->win32_heap_handle, 0, total); - - Heap::Header* h = static_cast(data); - h->size = total; - data = (h + 1); - -#else - // TODO(bill): Find a better malloc alternative for this platform - void* data = malloc(total); -#endif - - heap->total_allocated_count += total; - heap->allocation_count++; - - if (heap->use_mutex) mutex::unlock(&heap->mutex); - - return data; -} - -internal_linkage void -free(Allocator* a, void* ptr) -{ - if (!ptr) - return; - - Heap* heap = reinterpret_cast(a); - - if (heap->use_mutex) mutex::lock(&heap->mutex); - - heap->total_allocated_count -= allocated_size(heap, ptr); - heap->allocation_count--; - -#if defined (GB_SYSTEM_WINDOWS) - auto* header = static_cast(ptr) - 1; - HeapFree(heap->win32_heap_handle, 0, header); -#else - ::free(ptr); -#endif - - if (heap->use_mutex) mutex::unlock(&heap->mutex); -} - -inline s64 -allocated_size(Allocator* a, void const* ptr) -{ -#if defined(GB_SYSTEM_WINDOWS) - auto* heap = reinterpret_cast(a); - - if (heap->use_mutex) mutex::lock(&heap->mutex); - - auto const* h = static_cast(ptr) - 1; - s64 result = h->size; - - if (heap->use_mutex) mutex::unlock(&heap->mutex); - - return static_cast(result); - -#elif defined(GB_SYSTEM_OSX) - return static_cast(malloc_size(ptr)); - -#elif defined(GB_SYSTEM_LINUX) - return static_cast(malloc_usable_size(ptr)); - -#else - #error Implement Heap::allocated_size -#endif -} - -inline s64 -total_allocated(Allocator* a) -{ - auto* heap = reinterpret_cast(a); - - if (heap->use_mutex) mutex::lock(&heap->mutex); - - s64 result = heap->total_allocated_count; - - if (heap->use_mutex) mutex::unlock(&heap->mutex); - - return result; -} -} // namespace functions - -Heap -make(bool use_mutex) -{ - Heap heap = {}; - - heap.use_mutex = use_mutex; - if (use_mutex) heap.mutex = mutex::make(); - -#if defined(GB_SYSTEM_WINDOWS) - heap.win32_heap_handle = HeapCreate(0, 0, 0); -#endif - - heap.alloc = functions::alloc; - heap.free = functions::free; - heap.allocated_size = functions::allocated_size; - heap.total_allocated = functions::total_allocated; - - return heap; -} -void -destroy(Heap* heap) -{ - if (heap->use_mutex) mutex::destroy(&heap->mutex); - -#if defined (GB_SYSTEM_WINDOWS) - HeapDestroy(heap->win32_heap_handle); -#endif -} -} // namespace heap - - -namespace arena -{ -namespace functions -{ -internal_linkage void* -alloc(Allocator* a, usize size, usize align) -{ - Arena* arena = reinterpret_cast(a); - - s64 actual_size = size + align; - - if (arena->total_allocated_count + actual_size > arena->total_size) - { - GB_ASSERT(arena->total_allocated_count + actual_size <= arena->total_size, - "Arena has no more space for allocation"); - return nullptr; - } - - void* ptr = memory::align_forward(memory::pointer_add(arena->physical_start, arena->total_allocated_count), align); - - arena->total_allocated_count += actual_size; - - return ptr; -} - -inline void free(Allocator*, void*) {} // NOTE(bill): Arenas free all at once - -inline s64 allocated_size(Allocator*, void const*) { return -1; } - -inline s64 -total_allocated(Allocator* a) -{ - return reinterpret_cast(a)->total_allocated_count; -} -} // namespace functions - -Arena -make(Allocator* backing, usize size) -{ - Arena arena = {}; - - arena.backing = backing; - arena.physical_start = nullptr; - arena.total_size = size; - arena.temp_count = 0; - arena.total_allocated_count = 0; - - arena.physical_start = alloc(arena.backing, size); - - arena.alloc = functions::alloc; - arena.free = functions::free; - arena.allocated_size = functions::allocated_size; - arena.total_allocated = functions::total_allocated; - - return arena; -} - -Arena -make(void* start, usize size) -{ - Arena arena = {}; - - arena.backing = nullptr; - arena.physical_start = start; - arena.total_size = size; - arena.temp_count = 0; - arena.total_allocated_count = 0; - - arena.alloc = functions::alloc; - arena.free = functions::free; - arena.allocated_size = functions::allocated_size; - arena.total_allocated = functions::total_allocated; - - return arena; -} - -void -destroy(Arena* arena) -{ - if (arena->backing) - free(arena->backing, arena->physical_start); - - clear(arena); -} - -inline void -clear(Arena* arena) -{ - GB_ASSERT(arena->temp_count == 0, - "%ld Temporary_Arena_Memory have not be cleared", arena->temp_count); - - arena->total_allocated_count = 0; -} -} // namespace arena - - -namespace temporary_arena_memory -{ -inline Temporary_Arena_Memory -make(Arena* arena) -{ - Temporary_Arena_Memory tmp = {}; - tmp.arena = arena; - tmp.original_count = arena->total_allocated_count; - arena->temp_count++; - return tmp; -} - -inline void -free(Temporary_Arena_Memory tmp) -{ - GB_ASSERT(total_allocated(tmp.arena) >= tmp.original_count); - tmp.arena->total_allocated_count = tmp.original_count; - GB_ASSERT(tmp.arena->temp_count > 0); - tmp.arena->temp_count--; -} -} // namespace temporary_arena_memory - - - - -namespace pool -{ -namespace functions -{ -internal_linkage void* -alloc(Allocator* a, usize size, usize align) -{ - Pool* pool = reinterpret_cast(a); - - GB_ASSERT(size == pool->block_size, "Size must match block size"); - GB_ASSERT(align == pool->block_align, "Align must match block align"); - GB_ASSERT(pool->free_list != nullptr, "Pool out of memory"); - - uintptr next_free = *reinterpret_cast(pool->free_list); - void* ptr = pool->free_list; - pool->free_list = reinterpret_cast(next_free); - - pool->total_size += pool->block_size; - - return ptr; -} - -internal_linkage void -free(Allocator* a, void* ptr) -{ - if (!ptr) return; - - Pool* pool = reinterpret_cast(a); - - uintptr* next = static_cast(ptr); - *next = reinterpret_cast(pool->free_list); - - pool->free_list = ptr; - - pool->total_size -= pool->block_size; -} - -internal_linkage s64 -allocated_size(Allocator*, void const*) -{ - return -1; -} - -internal_linkage s64 -total_allocated(Allocator* a) -{ - Pool* pool = reinterpret_cast(a); - return pool->total_size; -} -} // namespace functions - - -Pool -make(Allocator* backing, usize num_blocks, usize block_size, usize block_align) -{ - Pool pool = {}; - - pool.backing = backing; - pool.block_size = block_size; - pool.block_align = block_align; - - usize actual_block_size = block_size + block_align; - usize pool_size = num_blocks * actual_block_size; - - u8* data = static_cast(alloc(backing, pool_size, block_align)); - - - // Init intrusive freelist - u8* curr = data; - for (usize block_index = 0; block_index < num_blocks-1; block_index++) - { - uintptr* next = reinterpret_cast(curr); - *next = reinterpret_cast(curr) + actual_block_size; - curr += actual_block_size; - } - - uintptr* end = reinterpret_cast(curr); - *end = reinterpret_cast(nullptr); - - pool.physical_start = data; - pool.free_list = data; - - // Set functions pointers - pool.alloc = functions::alloc; - pool.free = functions::free; - pool.allocated_size = functions::allocated_size; - pool.total_allocated = functions::total_allocated; - - return pool; -} - -inline void -destroy(Pool* pool) -{ - free(pool->backing, pool->physical_start); -} -} // namespace pool - - - - - - -//////////////////////////////// -// // -// Memory // -// // -//////////////////////////////// - -namespace memory -{ -inline void* -align_forward(void* ptr, usize align) -{ - GB_ASSERT(GB_IS_POWER_OF_TWO(align), - "Alignment must be a power of two and not zero -- %llu", align); - - uintptr p = uintptr(ptr); - const usize modulo = p % align; - if (modulo) p += (align - modulo); - return reinterpret_cast(p); -} - -inline void* -pointer_add(void* ptr, usize bytes) -{ - return static_cast(static_cast(ptr) + bytes); -} - -inline void const* -pointer_add(void const* ptr, usize bytes) -{ - return static_cast(static_cast(ptr) + bytes); -} - -inline void* -pointer_sub(void* ptr, usize bytes) -{ - return static_cast(static_cast(ptr) - bytes); -} - -inline void const* -pointer_sub(void const* ptr, usize bytes) -{ - return static_cast(static_cast(ptr) - bytes); -} - - -GB_FORCE_INLINE void -zero(void* ptr, usize bytes) -{ - memset(ptr, 0, bytes); -} - -GB_FORCE_INLINE void -copy(void const* src, usize bytes, void* dest) -{ - memcpy(dest, src, bytes); -} - -GB_FORCE_INLINE void -move(void const* src, usize bytes, void* dest) -{ - memmove(dest, src, bytes); -} - -GB_FORCE_INLINE bool -equals(void const* a, void const* b, usize bytes) -{ - return (memcmp(a, b, bytes) == 0); -} -} // namespace memory - -inline void* -alloc(Allocator* a, usize size, usize align) -{ - GB_ASSERT(a != nullptr); - return a->alloc(a, size, align); -} - -inline void -free(Allocator* a, void* ptr) -{ - GB_ASSERT(a != nullptr); - if (ptr) a->free(a, ptr); -} - -inline s64 -allocated_size(Allocator* a, void const* ptr) -{ - GB_ASSERT(a != nullptr); - return a->allocated_size(a, ptr); -} - -inline s64 -total_allocated(Allocator* a) -{ - GB_ASSERT(a != nullptr); - return a->total_allocated(a); -} - -//////////////////////////////// -// // -// String // -// // -//////////////////////////////// - -namespace string -{ -inline String -make(Allocator* a, char const* str) -{ - return string::make(a, str, (string::Size)strlen(str)); -} - -String -make(Allocator* a, void const* init_str, Size len) -{ - usize header_size = sizeof(string::Header); - void* ptr = alloc(a, header_size + len + 1); - if (!ptr) return nullptr; - - if (!init_str) memory::zero(ptr, header_size + len + 1); - - String str = static_cast(ptr) + header_size; - - string::Header* header = string::header(str); - header->allocator = a; - header->length = len; - header->capacity = len; - - if (len && init_str) - memory::copy(init_str, len, str); - str[len] = '\0'; - - return str; -} - -inline void -free(String str) -{ - if (str == nullptr) return; - - string::Header* h = string::header(str); - - if (h->allocator) free(h->allocator, h); -} - -inline String -duplicate(Allocator* a, String const str) -{ - return string::make(a, str, string::length(str)); -} - -inline Size -length(String const str) -{ - return string::header(str)->length; -} - -inline Size -capacity(String const str) -{ - return string::header(str)->capacity; -} - -inline Size -available_space(String const str) -{ - string::Header* h = string::header(str); - if (h->capacity > h->length) - return h->capacity - h->length; - return 0; -} - -inline void -clear(String str) -{ - string::header(str)->length = 0; - str[0] = '\0'; -} - -void -append(String* str, char c) -{ - Size curr_len = string::length(*str); - - string::make_space_for(str, 1); - if (str == nullptr) return; - - (*str)[curr_len] = c; - (*str)[curr_len + 1] = '\0'; - string::header(*str)->length = curr_len + 1; -} - -inline void -append(String* str, String const other) -{ - string::append(str, other, string::length(other)); -} - -inline void -append_cstring(String* str, char const* other) -{ - string::append(str, other, (Size)strlen(other)); -} - -void -append(String* str, void const* other, Size other_len) -{ - Size curr_len = string::length(*str); - - string::make_space_for(str, other_len); - if (str == nullptr) - return; - - memory::copy(other, other_len, (*str) + curr_len); - (*str)[curr_len + other_len] = '\0'; - string::header(*str)->length = curr_len + other_len; -} - -namespace impl -{ -// NOTE(bill): ptr _must_ be allocated with Allocator* a -internal_linkage inline void* -string_realloc(Allocator* a, void* ptr, usize old_size, usize new_size) -{ - if (!ptr) - return alloc(a, new_size); - - if (new_size < old_size) - new_size = old_size; - - if (old_size == new_size) - return ptr; - - void* new_ptr = alloc(a, new_size); - if (!new_ptr) - return nullptr; - - memory::copy(ptr, old_size, new_ptr); - - free(a, ptr); - - return new_ptr; -} -} // namespace impl - -void -make_space_for(String* str, Size add_len) -{ - Size len = string::length(*str); - Size new_len = len + add_len; - - Size available = string::available_space(*str); - if (available >= add_len) // Return if there is enough space left - return; - - void* ptr = string::header(*str); - usize old_size = sizeof(string::Header) + string::length(*str) + 1; - usize new_size = sizeof(string::Header) + new_len + 1; - - Allocator* a = string::header(*str)->allocator; - void* new_ptr = impl::string_realloc(a, ptr, old_size, new_size); - if (new_ptr == nullptr) - return; - - string::Header* header = static_cast(new_ptr); - header->allocator = a; - header->length = len; - header->capacity = new_len; - - *str = reinterpret_cast(header + 1); -} - -usize -allocation_size(String const str) -{ - Size cap = string::capacity(str); - return sizeof(string::Header) + cap; -} - -bool -equals(String const lhs, String const rhs) -{ - Size lhs_len = string::length(lhs); - Size rhs_len = string::length(rhs); - if (lhs_len != rhs_len) - return false; - - for (Size i = 0; i < lhs_len; i++) - { - if (lhs[i] != rhs[i]) - return false; - } - - return true; -} - -int -compare(String const lhs, String const rhs) // NOTE(bill): three-way comparison -{ - // Treat as cstring - char const* str1 = lhs; - char const* str2 = rhs; - int s1; - int s2; - do - { - s1 = *str1++; - s2 = *str2++; - if (s1 == 0) - break; - } - while (s1 == s2); - - return (s1 < s2) ? -1 : (s1 > s2); -} - -void -trim(String* str, char const* cut_set) -{ - char* start; - char* end; - char* start_pos; - char* end_pos; - - start_pos = start = *str; - end_pos = end = *str + string::length(*str) - 1; - - while (start_pos <= end && strchr(cut_set, *start_pos)) - start_pos++; - while (end_pos > start_pos && strchr(cut_set, *end_pos)) - end_pos--; - - Size len = static_cast((start_pos > end_pos) ? 0 : ((end_pos - start_pos)+1)); - - if (*str != start_pos) - memory::move(start_pos, len, *str); - (*str)[len] = '\0'; - - string::header(*str)->length = len; -} -inline void -trim_space(String* str) -{ - trim(str, " \n\r\t\v\f"); -} -} // namespace string - - - - - - - - - -//////////////////////////////// -// // -// Hash // -// // -//////////////////////////////// - -namespace hash -{ -u32 -adler32(void const* key, u32 num_bytes) -{ - const u32 MOD_ADLER = 65521; - - u32 a = 1; - u32 b = 0; - - u8 const* bytes = static_cast(key); - for (u32 i = 0; i < num_bytes; i++) - { - a = (a + bytes[i]) % MOD_ADLER; - b = (b + a) % MOD_ADLER; - } - - return (b << 16) | a; -} - -global_variable const u32 GB_CRC32_TABLE[256] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, - 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, - 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, - 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, - 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, - 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, - 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, - 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, - 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, - 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, - 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, - 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, - 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, - 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, - 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, - 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, - 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, -}; - -global_variable const u64 GB_CRC64_TABLE[256] = { - 0x0000000000000000ull, 0x42F0E1EBA9EA3693ull, 0x85E1C3D753D46D26ull, 0xC711223CFA3E5BB5ull, - 0x493366450E42ECDFull, 0x0BC387AEA7A8DA4Cull, 0xCCD2A5925D9681F9ull, 0x8E224479F47CB76Aull, - 0x9266CC8A1C85D9BEull, 0xD0962D61B56FEF2Dull, 0x17870F5D4F51B498ull, 0x5577EEB6E6BB820Bull, - 0xDB55AACF12C73561ull, 0x99A54B24BB2D03F2ull, 0x5EB4691841135847ull, 0x1C4488F3E8F96ED4ull, - 0x663D78FF90E185EFull, 0x24CD9914390BB37Cull, 0xE3DCBB28C335E8C9ull, 0xA12C5AC36ADFDE5Aull, - 0x2F0E1EBA9EA36930ull, 0x6DFEFF5137495FA3ull, 0xAAEFDD6DCD770416ull, 0xE81F3C86649D3285ull, - 0xF45BB4758C645C51ull, 0xB6AB559E258E6AC2ull, 0x71BA77A2DFB03177ull, 0x334A9649765A07E4ull, - 0xBD68D2308226B08Eull, 0xFF9833DB2BCC861Dull, 0x388911E7D1F2DDA8ull, 0x7A79F00C7818EB3Bull, - 0xCC7AF1FF21C30BDEull, 0x8E8A101488293D4Dull, 0x499B3228721766F8ull, 0x0B6BD3C3DBFD506Bull, - 0x854997BA2F81E701ull, 0xC7B97651866BD192ull, 0x00A8546D7C558A27ull, 0x4258B586D5BFBCB4ull, - 0x5E1C3D753D46D260ull, 0x1CECDC9E94ACE4F3ull, 0xDBFDFEA26E92BF46ull, 0x990D1F49C77889D5ull, - 0x172F5B3033043EBFull, 0x55DFBADB9AEE082Cull, 0x92CE98E760D05399ull, 0xD03E790CC93A650Aull, - 0xAA478900B1228E31ull, 0xE8B768EB18C8B8A2ull, 0x2FA64AD7E2F6E317ull, 0x6D56AB3C4B1CD584ull, - 0xE374EF45BF6062EEull, 0xA1840EAE168A547Dull, 0x66952C92ECB40FC8ull, 0x2465CD79455E395Bull, - 0x3821458AADA7578Full, 0x7AD1A461044D611Cull, 0xBDC0865DFE733AA9ull, 0xFF3067B657990C3Aull, - 0x711223CFA3E5BB50ull, 0x33E2C2240A0F8DC3ull, 0xF4F3E018F031D676ull, 0xB60301F359DBE0E5ull, - 0xDA050215EA6C212Full, 0x98F5E3FE438617BCull, 0x5FE4C1C2B9B84C09ull, 0x1D14202910527A9Aull, - 0x93366450E42ECDF0ull, 0xD1C685BB4DC4FB63ull, 0x16D7A787B7FAA0D6ull, 0x5427466C1E109645ull, - 0x4863CE9FF6E9F891ull, 0x0A932F745F03CE02ull, 0xCD820D48A53D95B7ull, 0x8F72ECA30CD7A324ull, - 0x0150A8DAF8AB144Eull, 0x43A04931514122DDull, 0x84B16B0DAB7F7968ull, 0xC6418AE602954FFBull, - 0xBC387AEA7A8DA4C0ull, 0xFEC89B01D3679253ull, 0x39D9B93D2959C9E6ull, 0x7B2958D680B3FF75ull, - 0xF50B1CAF74CF481Full, 0xB7FBFD44DD257E8Cull, 0x70EADF78271B2539ull, 0x321A3E938EF113AAull, - 0x2E5EB66066087D7Eull, 0x6CAE578BCFE24BEDull, 0xABBF75B735DC1058ull, 0xE94F945C9C3626CBull, - 0x676DD025684A91A1ull, 0x259D31CEC1A0A732ull, 0xE28C13F23B9EFC87ull, 0xA07CF2199274CA14ull, - 0x167FF3EACBAF2AF1ull, 0x548F120162451C62ull, 0x939E303D987B47D7ull, 0xD16ED1D631917144ull, - 0x5F4C95AFC5EDC62Eull, 0x1DBC74446C07F0BDull, 0xDAAD56789639AB08ull, 0x985DB7933FD39D9Bull, - 0x84193F60D72AF34Full, 0xC6E9DE8B7EC0C5DCull, 0x01F8FCB784FE9E69ull, 0x43081D5C2D14A8FAull, - 0xCD2A5925D9681F90ull, 0x8FDAB8CE70822903ull, 0x48CB9AF28ABC72B6ull, 0x0A3B7B1923564425ull, - 0x70428B155B4EAF1Eull, 0x32B26AFEF2A4998Dull, 0xF5A348C2089AC238ull, 0xB753A929A170F4ABull, - 0x3971ED50550C43C1ull, 0x7B810CBBFCE67552ull, 0xBC902E8706D82EE7ull, 0xFE60CF6CAF321874ull, - 0xE224479F47CB76A0ull, 0xA0D4A674EE214033ull, 0x67C58448141F1B86ull, 0x253565A3BDF52D15ull, - 0xAB1721DA49899A7Full, 0xE9E7C031E063ACECull, 0x2EF6E20D1A5DF759ull, 0x6C0603E6B3B7C1CAull, - 0xF6FAE5C07D3274CDull, 0xB40A042BD4D8425Eull, 0x731B26172EE619EBull, 0x31EBC7FC870C2F78ull, - 0xBFC9838573709812ull, 0xFD39626EDA9AAE81ull, 0x3A28405220A4F534ull, 0x78D8A1B9894EC3A7ull, - 0x649C294A61B7AD73ull, 0x266CC8A1C85D9BE0ull, 0xE17DEA9D3263C055ull, 0xA38D0B769B89F6C6ull, - 0x2DAF4F0F6FF541ACull, 0x6F5FAEE4C61F773Full, 0xA84E8CD83C212C8Aull, 0xEABE6D3395CB1A19ull, - 0x90C79D3FEDD3F122ull, 0xD2377CD44439C7B1ull, 0x15265EE8BE079C04ull, 0x57D6BF0317EDAA97ull, - 0xD9F4FB7AE3911DFDull, 0x9B041A914A7B2B6Eull, 0x5C1538ADB04570DBull, 0x1EE5D94619AF4648ull, - 0x02A151B5F156289Cull, 0x4051B05E58BC1E0Full, 0x87409262A28245BAull, 0xC5B073890B687329ull, - 0x4B9237F0FF14C443ull, 0x0962D61B56FEF2D0ull, 0xCE73F427ACC0A965ull, 0x8C8315CC052A9FF6ull, - 0x3A80143F5CF17F13ull, 0x7870F5D4F51B4980ull, 0xBF61D7E80F251235ull, 0xFD913603A6CF24A6ull, - 0x73B3727A52B393CCull, 0x31439391FB59A55Full, 0xF652B1AD0167FEEAull, 0xB4A25046A88DC879ull, - 0xA8E6D8B54074A6ADull, 0xEA16395EE99E903Eull, 0x2D071B6213A0CB8Bull, 0x6FF7FA89BA4AFD18ull, - 0xE1D5BEF04E364A72ull, 0xA3255F1BE7DC7CE1ull, 0x64347D271DE22754ull, 0x26C49CCCB40811C7ull, - 0x5CBD6CC0CC10FAFCull, 0x1E4D8D2B65FACC6Full, 0xD95CAF179FC497DAull, 0x9BAC4EFC362EA149ull, - 0x158E0A85C2521623ull, 0x577EEB6E6BB820B0ull, 0x906FC95291867B05ull, 0xD29F28B9386C4D96ull, - 0xCEDBA04AD0952342ull, 0x8C2B41A1797F15D1ull, 0x4B3A639D83414E64ull, 0x09CA82762AAB78F7ull, - 0x87E8C60FDED7CF9Dull, 0xC51827E4773DF90Eull, 0x020905D88D03A2BBull, 0x40F9E43324E99428ull, - 0x2CFFE7D5975E55E2ull, 0x6E0F063E3EB46371ull, 0xA91E2402C48A38C4ull, 0xEBEEC5E96D600E57ull, - 0x65CC8190991CB93Dull, 0x273C607B30F68FAEull, 0xE02D4247CAC8D41Bull, 0xA2DDA3AC6322E288ull, - 0xBE992B5F8BDB8C5Cull, 0xFC69CAB42231BACFull, 0x3B78E888D80FE17Aull, 0x7988096371E5D7E9ull, - 0xF7AA4D1A85996083ull, 0xB55AACF12C735610ull, 0x724B8ECDD64D0DA5ull, 0x30BB6F267FA73B36ull, - 0x4AC29F2A07BFD00Dull, 0x08327EC1AE55E69Eull, 0xCF235CFD546BBD2Bull, 0x8DD3BD16FD818BB8ull, - 0x03F1F96F09FD3CD2ull, 0x41011884A0170A41ull, 0x86103AB85A2951F4ull, 0xC4E0DB53F3C36767ull, - 0xD8A453A01B3A09B3ull, 0x9A54B24BB2D03F20ull, 0x5D45907748EE6495ull, 0x1FB5719CE1045206ull, - 0x919735E51578E56Cull, 0xD367D40EBC92D3FFull, 0x1476F63246AC884Aull, 0x568617D9EF46BED9ull, - 0xE085162AB69D5E3Cull, 0xA275F7C11F7768AFull, 0x6564D5FDE549331Aull, 0x279434164CA30589ull, - 0xA9B6706FB8DFB2E3ull, 0xEB46918411358470ull, 0x2C57B3B8EB0BDFC5ull, 0x6EA7525342E1E956ull, - 0x72E3DAA0AA188782ull, 0x30133B4B03F2B111ull, 0xF7021977F9CCEAA4ull, 0xB5F2F89C5026DC37ull, - 0x3BD0BCE5A45A6B5Dull, 0x79205D0E0DB05DCEull, 0xBE317F32F78E067Bull, 0xFCC19ED95E6430E8ull, - 0x86B86ED5267CDBD3ull, 0xC4488F3E8F96ED40ull, 0x0359AD0275A8B6F5ull, 0x41A94CE9DC428066ull, - 0xCF8B0890283E370Cull, 0x8D7BE97B81D4019Full, 0x4A6ACB477BEA5A2Aull, 0x089A2AACD2006CB9ull, - 0x14DEA25F3AF9026Dull, 0x562E43B4931334FEull, 0x913F6188692D6F4Bull, 0xD3CF8063C0C759D8ull, - 0x5DEDC41A34BBEEB2ull, 0x1F1D25F19D51D821ull, 0xD80C07CD676F8394ull, 0x9AFCE626CE85B507ull, -}; - - -u32 -crc32(void const* key, u32 num_bytes) -{ - u32 result = static_cast(~0); - u8 const* c = reinterpret_cast(key); - - for (u32 remaining = num_bytes; remaining--; c++) - result = (result >> 8) ^ (GB_CRC32_TABLE[(result ^ *c) & 0xff]); - - return ~result; -} - -u64 -crc64(void const* key, usize num_bytes) -{ - u64 result = static_cast(~0); - u8 const* c = reinterpret_cast(key); - for (usize remaining = num_bytes; remaining--; c++) - result = (result >> 8) ^ (GB_CRC64_TABLE[(result ^ *c) & 0xff]); - - return ~result; -} - -inline u32 -fnv32(void const* key, usize num_bytes) -{ - u32 h = 0x811c9dc5; - u8 const* buffer = static_cast(key); - - for (usize i = 0; i < num_bytes; i++) - { - h = (h * 0x01000193) ^ buffer[i]; - } - - return h; -} - -inline u64 -fnv64(void const* key, usize num_bytes) -{ - u64 h = 0xcbf29ce484222325ull; - u8 const* buffer = static_cast(key); - - for (usize i = 0; i < num_bytes; i++) - { - h = (h * 0x100000001b3ll) ^ buffer[i]; - } - - return h; -} - -inline u32 -fnv32a(void const* key, usize num_bytes) -{ - u32 h = 0x811c9dc5; - u8 const* buffer = static_cast(key); - - for (usize i = 0; i < num_bytes; i++) - { - h = (h ^ buffer[i]) * 0x01000193; - } - - return h; -} - -inline u64 -fnv64a(void const* key, usize num_bytes) -{ - u64 h = 0xcbf29ce484222325ull; - u8 const* buffer = static_cast(key); - - for (usize i = 0; i < num_bytes; i++) - { - h = (h ^ buffer[i]) * 0x100000001b3ll; - } - - return h; -} - -u32 -murmur32(void const* key, u32 num_bytes, u32 seed) -{ - const u32 c1 = 0xcc9e2d51; - const u32 c2 = 0x1b873593; - const u32 r1 = 15; - const u32 r2 = 13; - const u32 m = 5; - const u32 n = 0xe6546b64; - - u32 hash = seed; - - const usize nblocks = num_bytes / 4; - const u32* blocks = static_cast(key); - for (usize i = 0; i < nblocks; i++) { - u32 k = blocks[i]; - k *= c1; - k = (k << r1) | (k >> (32 - r1)); - k *= c2; - - hash ^= k; - hash = ((hash << r2) | (hash >> (32 - r2))) * m + n; - } - - u8 const* tail = (static_cast(key)) + nblocks * 4; - u32 k1 = 0; - - switch (num_bytes & 3) { - case 3: - k1 ^= tail[2] << 16; - case 2: - k1 ^= tail[1] << 8; - case 1: - k1 ^= tail[0]; - - k1 *= c1; - k1 = (k1 << r1) | (k1 >> (32 - r1)); - k1 *= c2; - hash ^= k1; - } - - hash ^= num_bytes; - hash ^= (hash >> 16); - hash *= 0x85ebca6b; - hash ^= (hash >> 13); - hash *= 0xc2b2ae35; - hash ^= (hash >> 16); - - return hash; -} - -#if defined(GB_ARCH_64_BIT) - u64 - murmur64(void const* key, usize num_bytes, u64 seed) - { - const u64 m = 0xc6a4a7935bd1e995ULL; - const s32 r = 47; - - u64 h = seed ^ (num_bytes * m); - - const u64* data = static_cast(key); - const u64* end = data + (num_bytes / 8); - - while (data != end) - { - u64 k = *data++; - - k *= m; - k ^= k >> r; - k *= m; - - h ^= k; - h *= m; - } - - u8 const* data2 = reinterpret_cast(data); - - switch (num_bytes & 7) - { - case 7: h ^= static_cast(data2[6]) << 48; - case 6: h ^= static_cast(data2[5]) << 40; - case 5: h ^= static_cast(data2[4]) << 32; - case 4: h ^= static_cast(data2[3]) << 24; - case 3: h ^= static_cast(data2[2]) << 16; - case 2: h ^= static_cast(data2[1]) << 8; - case 1: h ^= static_cast(data2[0]); - h *= m; - }; - - h ^= h >> r; - h *= m; - h ^= h >> r; - - return h; - } -#elif GB_ARCH_32_BIT - u64 - murmur64(void const* key, usize num_bytes, u64 seed) - { - const u32 m = 0x5bd1e995; - const s32 r = 24; - - u32 h1 = static_cast(seed) ^ static_cast(num_bytes); - u32 h2 = static_cast(seed >> 32); - - const u32* data = static_cast(key); - - while (num_bytes >= 8) - { - u32 k1 = *data++; - k1 *= m; - k1 ^= k1 >> r; - k1 *= m; - h1 *= m; - h1 ^= k1; - num_bytes -= 4; - - u32 k2 = *data++; - k2 *= m; - k2 ^= k2 >> r; - k2 *= m; - h2 *= m; - h2 ^= k2; - num_bytes -= 4; - } - - if (num_bytes >= 4) - { - u32 k1 = *data++; - k1 *= m; - k1 ^= k1 >> r; - k1 *= m; - h1 *= m; - h1 ^= k1; - num_bytes -= 4; - } - - switch (num_bytes) - { - case 3: h2 ^= reinterpret_cast(data)[2] << 16; - case 2: h2 ^= reinterpret_cast(data)[1] << 8; - case 1: h2 ^= reinterpret_cast(data)[0] << 0; - h2 *= m; - }; - - h1 ^= h2 >> 18; - h1 *= m; - h2 ^= h1 >> 22; - h2 *= m; - h1 ^= h2 >> 17; - h1 *= m; - h2 ^= h1 >> 19; - h2 *= m; - - u64 h = h1; - - h = (h << 32) | h2; - - return h; - } -#else - #error murmur64 function not supported on this architecture -#endif -} // namespace hash - -//////////////////////////////// -// // -// Time // -// // -//////////////////////////////// - -Time const TIME_ZERO = time::seconds(0); - -namespace time -{ -#if defined(GB_SYSTEM_WINDOWS) - internal_linkage LARGE_INTEGER - win32_get_frequency() - { - LARGE_INTEGER f; - QueryPerformanceFrequency(&f); - return f; - } - - Time - now() - { - // NOTE(bill): std::chrono does not have a good enough precision in MSVC12 - // and below. This may have been fixed in MSVC14 but unsure as of yet. - - // Force the following code to run on first core - // NOTE(bill): See - // http://msdn.microsoft.com/en-us/library/windows/desktop/ms644904(v=vs.85).aspx - HANDLE currentThread = GetCurrentThread(); - DWORD_PTR previousMask = SetThreadAffinityMask(currentThread, 1); - - // Get the frequency of the performance counter - // It is constant across the program's lifetime - local_persist LARGE_INTEGER s_frequency = win32_get_frequency(); - - // Get the current time - LARGE_INTEGER t; - QueryPerformanceCounter(&t); - - // Restore the thread affinity - SetThreadAffinityMask(currentThread, previousMask); - - return time::microseconds(1000000ll * t.QuadPart / s_frequency.QuadPart); - } - - void - sleep(Time t) - { - if (t.microseconds <= 0) - return; - - // Get the supported timer resolutions on this system - TIMECAPS tc; - timeGetDevCaps(&tc, sizeof(TIMECAPS)); - // Set the timer resolution to the minimum for the Sleep call - timeBeginPeriod(tc.wPeriodMin); - - // Wait... - ::Sleep(time::as_milliseconds(t)); - - // Reset the timer resolution back to the system default - timeBeginPeriod(tc.wPeriodMin); - } - -#else - Time - now() - { - #if defined(GB_SYSTEM_OSX) - s64 t = static_cast(mach_absolute_time()); - return microseconds(t); - #else - struct timeval t; - gettimeofday(&t, nullptr); - - return microseconds((t.tv_sec * 1000000ll) + (t.tv_usec * 1ll)); - #endif - } - - void - sleep(Time t) - { - if (t.microseconds <= 0) - return; - - struct timespec spec = {}; - spec.tv_sec = static_cast(as_seconds(t)); - spec.tv_nsec = 1000ll * (as_microseconds(t) % 1000000ll); - - nanosleep(&spec, nullptr); - } - -#endif - -Time seconds(f32 s) { return {static_cast(s * 1000000ll)}; } -Time milliseconds(s32 ms) { return {static_cast(ms * 1000ll)}; } -Time microseconds(s64 us) { return {us}; } -f32 as_seconds(Time t) { return static_cast(t.microseconds / 1000000.0f); } -s32 as_milliseconds(Time t) { return static_cast(t.microseconds / 1000ll); } -s64 as_microseconds(Time t) { return t.microseconds; } -} // namespace time - -bool operator==(Time left, Time right) { return left.microseconds == right.microseconds; } -bool operator!=(Time left, Time right) { return !operator==(left, right); } - -bool operator<(Time left, Time right) { return left.microseconds < right.microseconds; } -bool operator>(Time left, Time right) { return left.microseconds > right.microseconds; } - -bool operator<=(Time left, Time right) { return left.microseconds <= right.microseconds; } -bool operator>=(Time left, Time right) { return left.microseconds >= right.microseconds; } - -Time operator+(Time right) { return {+right.microseconds}; } -Time operator-(Time right) { return {-right.microseconds}; } - -Time operator+(Time left, Time right) { return {left.microseconds + right.microseconds}; } -Time operator-(Time left, Time right) { return {left.microseconds - right.microseconds}; } - -Time& operator+=(Time& left, Time right) { return (left = left + right); } -Time& operator-=(Time& left, Time right) { return (left = left - right); } - -Time operator*(Time left, f32 right) { return time::seconds(time::as_seconds(left) * right); } -Time operator*(Time left, s64 right) { return time::microseconds(time::as_microseconds(left) * right); } -Time operator*(f32 left, Time right) { return time::seconds(time::as_seconds(right) * left); } -Time operator*(s64 left, Time right) { return time::microseconds(time::as_microseconds(right) * left); } - -Time& operator*=(Time& left, f32 right) { return (left = left * right); } -Time& operator*=(Time& left, s64 right) { return (left = left * right); } - -Time operator/(Time left, f32 right) { return time::seconds(time::as_seconds(left) / right); } -Time operator/(Time left, s64 right) { return time::microseconds(time::as_microseconds(left) / right); } -f32 operator/(Time left, Time right) { return time::as_seconds(left) / time::as_seconds(right); } - -Time& operator/=(Time& left, f32 right) { return (left = left / right); } -Time& operator/=(Time& left, s64 right) { return (left = left / right); } - -Time operator%(Time left, Time right) { return time::microseconds(time::as_microseconds(left) % time::as_microseconds(right)); } -Time& operator%=(Time& left, Time right) { return (left = left % right); } - -//////////////////////////////// -// // -// OS // -// // -//////////////////////////////// - -namespace os -{ -GB_FORCE_INLINE u64 -rdtsc() -{ -#if GB_SYSTEM_WINDOWS - return ::__rdtsc(); -#else - // TODO(bill): Check that rdtsc() works - return ::rdtsc(); -#endif -} -} // namespace os - -__GB_NAMESPACE_END - -#endif // GB_IMPLEMENTATION diff --git a/gb_math.h b/gb_math.h index 9158181..d8278f9 100644 --- a/gb_math.h +++ b/gb_math.h @@ -1,16 +1,21 @@ -// gb_math.h - v0.03 - public domain C math library - no warranty implied; use at your own risk +// gb_math.h - v0.04d - public domain C math library - no warranty implied; use at your own risk // A C math library geared towards game development +// use '#define GB_MATH_IMPLEMENTATION' before including to create the implementation in _ONE_ file /* Version History: + 0.04d - License Update + 0.04c - Use 64-bit murmur64 version on WIN64 + 0.04b - Fix strict aliasing in gb_quake_inv_sqrt + 0.04a - Minor bug fixes + 0.04 - Namespace everything with gb 0.03 - Complete Replacement 0.01 - Initial Version LICENSE - This software is in the public domain. Where that dedication is not - recognized, you are granted a perpetual, irrevocable license to copy, - distribute, and modify this file as you see fit. - + This software is dual-licensed to the public domain and under the following + license: you are granted a perpetual, irrevocable license to copy, modify, + publish, and distribute this file as you see fit. WARNING - This library is _slightly_ experimental and features may not work as expected. - This also means that many functions are not documented. @@ -18,419 +23,1107 @@ WARNING CONTENTS - Common Macros - Types - - Vec(2,3,4) - - Mat(2,3,4) - - Float(2,3,4) - - Rect(2,3) - - Aabb(2,3) + - gbVec(2,3,4) + - gbMat(2,3,4) + - gbFloat(2,3,4) + - gbQuat + - gbRect(2,3) + - gbAabb(2,3) + - gb_half (16-bit floating point) (storage only) - Operations - Functions - Type Functions - Random - -TODO - - Complex - - Quaternion - - More Math Functions + - Hash */ #ifndef GB_MATH_INCLUDE_GB_MATH_H #define GB_MATH_INCLUDE_GB_MATH_H +// TODO(bill): What of this do I actually need? And can include elsewhere (e.g. implementation) #include +#include +#include +#include +#include // memcpy, memmove, etc. -#ifdef GB_MATH_STATIC -#define GB_MATH_DEF static -#else -#define GB_MATH_DEF extern +#ifndef GB_MATH_DEF + #ifdef GB_MATH_STATIC + #define GB_MATH_DEF static + #else + #define GB_MATH_DEF extern + #endif #endif -typedef union Vec2 -{ +typedef union gbVec2 { struct { float x, y; }; float e[2]; -} Vec2; +} gbVec2; -typedef union Vec3 -{ +typedef union gbVec3 { struct { float x, y, z; }; struct { float r, g, b; }; - Vec2 xy; + gbVec2 xy; float e[2]; -} Vec3; +} gbVec3; -typedef union Vec4 -{ +typedef union gbVec4 { struct { float x, y, z, w; }; struct { float r, g, b, a; }; - struct { Vec2 xy, zw; }; - Vec3 xyz; - Vec3 rgb; + struct { gbVec2 xy, zw; }; + gbVec3 xyz; + gbVec3 rgb; float e[4]; -} Vec4; +} gbVec4; -typedef union Mat2 -{ - struct { Vec2 x, y; }; - Vec4 col[2]; +typedef union gbMat2 { + struct { gbVec2 x, y; }; + gbVec4 col[2]; float e[4]; -} Mat2; +} gbMat2; -typedef union Mat3 -{ - struct { Vec3 x, y, z; }; - Vec4 col[3]; +typedef union gbMat3 { + struct { gbVec3 x, y, z; }; + gbVec3 col[3]; float e[9]; -} Mat3; +} gbMat3; -typedef union Mat4 -{ - struct { Vec4 x, y, z, w; }; - Vec4 col[4]; +typedef union gbMat4 { + struct { gbVec4 x, y, z, w; }; + gbVec4 col[4]; float e[16]; -} Mat4; +} gbMat4; + + +typedef union gbQuat { + struct { float x, y, z, w; }; + gbVec4 xyzw; + gbVec3 xyz; + float e[4]; +} gbQuat; - -typedef float Float2[2]; -typedef float Float3[3]; -typedef float Float4[4]; +typedef float gbFloat2[2]; +typedef float gbFloat3[3]; +typedef float gbFloat4[4]; -typedef struct Rect2 { Vec2 pos, dim; } Rect2; -typedef struct Rect3 { Vec3 pos, dim; } Rect3; +typedef struct gbRect2 { gbVec2 pos, dim; } gbRect2; +typedef struct gbRect3 { gbVec3 pos, dim; } gbRect3; -typedef struct Aabb2 { Vec2 center, half_size; } Aabb2; -typedef struct Aabb3 { Vec3 center, half_size; } Aabb3; +typedef struct gbAabb2 { gbVec2 centre, half_size; } gbAabb2; +typedef struct gbAabb3 { gbVec3 centre, half_size; } gbAabb3; #if defined(_MSC_VER) - typedef unsigned __int8 gb_math_u8; typedef unsigned __int32 gb_math_u32; typedef unsigned __int64 gb_math_u64; #else - #include - typedef uint8_t gb_math_u8; - typedef uint32_t gb_math_u32; - typedef uint64_t gb_math_u64; + #if defined(GB_USE_STDINT) + #include + typedef uint32_t gb_math_u32; + typedef uint64_t gb_math_u64; + #else + typedef unsigned int gb_math_u32; + typedef unsigned long long gb_math_u64; + #endif #endif +typedef short gb_half; + + // Constants -#define MATH_TAU 6.28318530718f +#ifndef GB_MATH_CONSTANTS +#define GB_MATH_CONSTANTS + #define GB_MATH_EPSILON 1.19209290e-7f + #define GB_MATH_ZERO 0.0f + #define GB_MATH_ONE 1.0f + #define GB_MATH_TWO_THIRDS 0.666666666666666666666666666666666666667f + + #define GB_MATH_TAU 6.28318530717958647692528676655900576f + #define GB_MATH_PI 3.14159265358979323846264338327950288f + #define GB_MATH_ONE_OVER_TAU 0.636619772367581343075535053490057448f + #define GB_MATH_ONE_OVER_PI 0.159154943091895335768883763372514362f + + #define GB_MATH_E 2.71828182845904523536f + #define GB_MATH_SQRT_TWO 1.41421356237309504880168872420969808f + #define GB_MATH_SQRT_THREE 1.73205080756887729352744634150587236f + #define GB_MATH_SQRT_FIVE 2.23606797749978969640917366873127623f + + #define GB_MATH_LOG_TWO 0.693147180559945309417232121458176568f + #define GB_MATH_LOG_TEN 2.30258509299404568401799145468436421f +#endif + #if defined(__cplusplus) extern "C" { #endif -// Vector +// Basic +#ifndef gb_clamp +#define gb_clamp(x, lower, upper) (gb_min(gb_max(x, (lower)), (upper))) +#endif +#ifndef gb_clamp01 +#define gb_clamp01(x) gb_clamp(x, 0, 1) +#endif -GB_MATH_DEF Vec2 vec2_zero(void); -GB_MATH_DEF Vec2 vec2(float x, float y); -GB_MATH_DEF Vec2 vec2v(float x[2]); +#ifndef gb_square +#define gb_square(x) ((x)*(x)) +#endif -GB_MATH_DEF Vec3 vec3_zero(void); -GB_MATH_DEF Vec3 vec3(float x, float y, float z); -GB_MATH_DEF Vec3 vec3v(float x[3]); +#ifndef gb_cube +#define gb_cube(x) ((x)*(x)*(x)) +#endif -GB_MATH_DEF Vec4 vec4_zero(void); -GB_MATH_DEF Vec4 vec4(float x, float y, float z, float w); -GB_MATH_DEF Vec4 vec4v(float x[4]); +#ifndef gb_abs +#define gb_abs(x) ((x) > 0 ? (x) : -(x)) +#endif -GB_MATH_DEF void vec2_add(Vec2 *d, Vec2 v0, Vec2 v1); -GB_MATH_DEF void vec2_sub(Vec2 *d, Vec2 v0, Vec2 v1); -GB_MATH_DEF void vec2_mul(Vec2 *d, Vec2 v, float s); -GB_MATH_DEF void vec2_div(Vec2 *d, Vec2 v, float s); +#ifndef gb_sign +#define gb_sign(x) ((x) >= 0 ? 1 : -1) +#endif -GB_MATH_DEF void vec3_add(Vec3 *d, Vec3 v0, Vec3 v1); -GB_MATH_DEF void vec3_sub(Vec3 *d, Vec3 v0, Vec3 v1); -GB_MATH_DEF void vec3_mul(Vec3 *d, Vec3 v, float s); -GB_MATH_DEF void vec3_div(Vec3 *d, Vec3 v, float s); -GB_MATH_DEF void vec4_add(Vec4 *d, Vec4 v0, Vec4 v1); -GB_MATH_DEF void vec4_sub(Vec4 *d, Vec4 v0, Vec4 v1); -GB_MATH_DEF void vec4_mul(Vec4 *d, Vec4 v, float s); -GB_MATH_DEF void vec4_div(Vec4 *d, Vec4 v, float s); +GB_MATH_DEF float gb_to_radians(float degrees); +GB_MATH_DEF float gb_to_degrees(float radians); -GB_MATH_DEF void vec2_addeq(Vec2 *d, Vec2 v); -GB_MATH_DEF void vec2_subeq(Vec2 *d, Vec2 v); -GB_MATH_DEF void vec2_muleq(Vec2 *d, float s); -GB_MATH_DEF void vec2_diveq(Vec2 *d, float s); +// NOTE(bill): Because to interpolate angles +GB_MATH_DEF float gb_angle_diff(float radians_a, float radians_b); -GB_MATH_DEF void vec3_addeq(Vec3 *d, Vec3 v); -GB_MATH_DEF void vec3_subeq(Vec3 *d, Vec3 v); -GB_MATH_DEF void vec3_muleq(Vec3 *d, float s); -GB_MATH_DEF void vec3_diveq(Vec3 *d, float s); +#ifndef gb_min +#define gb_min(a, b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef gb_max +#define gb_max(a, b) ((a) > (b) ? (a) : (b)) +#endif -GB_MATH_DEF void vec4_addeq(Vec4 *d, Vec4 v); -GB_MATH_DEF void vec4_subeq(Vec4 *d, Vec4 v); -GB_MATH_DEF void vec4_muleq(Vec4 *d, float s); -GB_MATH_DEF void vec4_diveq(Vec4 *d, float s); +GB_MATH_DEF float gb_sqrt(float a); +GB_MATH_DEF float gb_quake_inv_sqrt(float a); // NOTE(bill): It's probably better to use 1.0f/gb_sqrt(a) + // And for simd, there is usually isqrt functions too! -GB_MATH_DEF float vec2_dot(Vec2 v0, Vec2 v1); -GB_MATH_DEF float vec3_dot(Vec3 v0, Vec3 v1); -GB_MATH_DEF float vec4_dot(Vec4 v0, Vec4 v1); +GB_MATH_DEF float gb_sin(float radians); +GB_MATH_DEF float gb_cos(float radians); +GB_MATH_DEF float gb_tan(float radians); +GB_MATH_DEF float gb_arcsin(float a); +GB_MATH_DEF float gb_arccos(float a); +GB_MATH_DEF float gb_arctan(float a); +GB_MATH_DEF float gb_arctan2(float y, float x); -GB_MATH_DEF void vec2_cross(float *d, Vec2 v0, Vec2 v1); -GB_MATH_DEF void vec3_cross(Vec3 *d, Vec3 v0, Vec3 v1); -GB_MATH_DEF float vec2_mag2(Vec2 v); -GB_MATH_DEF float vec3_mag2(Vec3 v); -GB_MATH_DEF float vec4_mag2(Vec4 v); +GB_MATH_DEF float gb_exp(float x); +GB_MATH_DEF float gb_exp2(float x); +GB_MATH_DEF float gb_log(float x); +GB_MATH_DEF float gb_log2(float x); +GB_MATH_DEF float gb_fast_exp(float x); // NOTE(bill): Only valid from -1 <= x <= +1 +GB_MATH_DEF float gb_fast_exp2(float x); // NOTE(bill): Only valid from -1 <= x <= +1 +GB_MATH_DEF float gb_pow(float x, float y); // x^y -GB_MATH_DEF float vec2_mag(Vec2 v); -GB_MATH_DEF float vec3_mag(Vec3 v); -GB_MATH_DEF float vec4_mag(Vec4 v); -GB_MATH_DEF void vec2_norm(Vec2 *d, Vec2 v); -GB_MATH_DEF void vec3_norm(Vec3 *d, Vec3 v); -GB_MATH_DEF void vec4_norm(Vec4 *d, Vec4 v); +GB_MATH_DEF float gb_half_to_float(gb_half value); +GB_MATH_DEF gb_half gb_float_to_half(float value); -GB_MATH_DEF float vec2_aspect(Vec2 v); +// Vec + +GB_MATH_DEF gbVec2 gb_vec2_zero(void); +GB_MATH_DEF gbVec2 gb_vec2(float x, float y); +GB_MATH_DEF gbVec2 gb_vec2v(float x[2]); + +GB_MATH_DEF gbVec3 gb_vec3_zero(void); +GB_MATH_DEF gbVec3 gb_vec3(float x, float y, float z); +GB_MATH_DEF gbVec3 gb_vec3v(float x[3]); + +GB_MATH_DEF gbVec4 gb_vec4_zero(void); +GB_MATH_DEF gbVec4 gb_vec4(float x, float y, float z, float w); +GB_MATH_DEF gbVec4 gb_vec4v(float x[4]); + + +GB_MATH_DEF void gb_vec2_add(gbVec2 *d, gbVec2 v0, gbVec2 v1); +GB_MATH_DEF void gb_vec2_sub(gbVec2 *d, gbVec2 v0, gbVec2 v1); +GB_MATH_DEF void gb_vec2_mul(gbVec2 *d, gbVec2 v, float s); +GB_MATH_DEF void gb_vec2_div(gbVec2 *d, gbVec2 v, float s); + +GB_MATH_DEF void gb_vec3_add(gbVec3 *d, gbVec3 v0, gbVec3 v1); +GB_MATH_DEF void gb_vec3_sub(gbVec3 *d, gbVec3 v0, gbVec3 v1); +GB_MATH_DEF void gb_vec3_mul(gbVec3 *d, gbVec3 v, float s); +GB_MATH_DEF void gb_vec3_div(gbVec3 *d, gbVec3 v, float s); + +GB_MATH_DEF void gb_vec4_add(gbVec4 *d, gbVec4 v0, gbVec4 v1); +GB_MATH_DEF void gb_vec4_sub(gbVec4 *d, gbVec4 v0, gbVec4 v1); +GB_MATH_DEF void gb_vec4_mul(gbVec4 *d, gbVec4 v, float s); +GB_MATH_DEF void gb_vec4_div(gbVec4 *d, gbVec4 v, float s); + +GB_MATH_DEF void gb_vec2_addeq(gbVec2 *d, gbVec2 v); +GB_MATH_DEF void gb_vec2_subeq(gbVec2 *d, gbVec2 v); +GB_MATH_DEF void gb_vec2_muleq(gbVec2 *d, float s); +GB_MATH_DEF void gb_vec2_diveq(gbVec2 *d, float s); + +GB_MATH_DEF void gb_vec3_addeq(gbVec3 *d, gbVec3 v); +GB_MATH_DEF void gb_vec3_subeq(gbVec3 *d, gbVec3 v); +GB_MATH_DEF void gb_vec3_muleq(gbVec3 *d, float s); +GB_MATH_DEF void gb_vec3_diveq(gbVec3 *d, float s); + +GB_MATH_DEF void gb_vec4_addeq(gbVec4 *d, gbVec4 v); +GB_MATH_DEF void gb_vec4_subeq(gbVec4 *d, gbVec4 v); +GB_MATH_DEF void gb_vec4_muleq(gbVec4 *d, float s); +GB_MATH_DEF void gb_vec4_diveq(gbVec4 *d, float s); + +GB_MATH_DEF float gb_vec2_dot(gbVec2 v0, gbVec2 v1); +GB_MATH_DEF float gb_vec3_dot(gbVec3 v0, gbVec3 v1); +GB_MATH_DEF float gb_vec4_dot(gbVec4 v0, gbVec4 v1); + +GB_MATH_DEF void gb_vec2_cross(float *d, gbVec2 v0, gbVec2 v1); +GB_MATH_DEF void gb_vec3_cross(gbVec3 *d, gbVec3 v0, gbVec3 v1); + +GB_MATH_DEF float gb_vec2_mag2(gbVec2 v); +GB_MATH_DEF float gb_vec3_mag2(gbVec3 v); +GB_MATH_DEF float gb_vec4_mag2(gbVec4 v); + +GB_MATH_DEF float gb_vec2_mag(gbVec2 v); +GB_MATH_DEF float gb_vec3_mag(gbVec3 v); +GB_MATH_DEF float gb_vec4_mag(gbVec4 v); + +GB_MATH_DEF void gb_vec2_norm(gbVec2 *d, gbVec2 v); +GB_MATH_DEF void gb_vec3_norm(gbVec3 *d, gbVec3 v); +GB_MATH_DEF void gb_vec4_norm(gbVec4 *d, gbVec4 v); + +GB_MATH_DEF void gb_vec2_norm0(gbVec2 *d, gbVec2 v); +GB_MATH_DEF void gb_vec3_norm0(gbVec3 *d, gbVec3 v); +GB_MATH_DEF void gb_vec4_norm0(gbVec4 *d, gbVec4 v); + +GB_MATH_DEF void gb_vec2_reflect(gbVec2 *d, gbVec2 i, gbVec2 n); +GB_MATH_DEF void gb_vec3_reflect(gbVec3 *d, gbVec3 i, gbVec3 n); +GB_MATH_DEF void gb_vec2_refract(gbVec2 *d, gbVec2 i, gbVec2 n, float eta); +GB_MATH_DEF void gb_vec3_refract(gbVec3 *d, gbVec3 i, gbVec3 n, float eta); + +GB_MATH_DEF float gb_vec2_aspect_ratio(gbVec2 v); // Matrix -GB_MATH_DEF void mat2_identity(Mat2 *m); -GB_MATH_DEF void float22_identity(float m[2][2]); +GB_MATH_DEF void gb_mat2_identity(gbMat2 *m); +GB_MATH_DEF void gb_float22_identity(float m[2][2]); -GB_MATH_DEF void mat2_transpose(Mat2 *m); -GB_MATH_DEF void mat2_mul(Mat2 *out, Mat2 *m1, Mat2 *m2); -GB_MATH_DEF void mat2_mul_vec2(Vec2 *out, Mat2 *m, Vec2 in); +GB_MATH_DEF void gb_mat2_transpose(gbMat2 *m); +GB_MATH_DEF void gb_mat2_mul(gbMat2 *out, gbMat2 *m1, gbMat2 *m2); +GB_MATH_DEF void gb_mat2_mul_vec2(gbVec2 *out, gbMat2 *m, gbVec2 in); -GB_MATH_DEF Mat2 *mat2_v(Vec2 m[2]); -GB_MATH_DEF Mat2 *mat2_f(float m[2][2]); -GB_MATH_DEF Float2 *float22_m(Mat2 *m); -GB_MATH_DEF Float2 *float22_v(Vec2 m[2]); -GB_MATH_DEF Float2 *float22_4(float m[4]); +GB_MATH_DEF gbMat2 *gb_mat2_v(gbVec2 m[2]); +GB_MATH_DEF gbMat2 *gb_mat2_f(float m[2][2]); +GB_MATH_DEF gbFloat2 *gb_float22_m(gbMat2 *m); +GB_MATH_DEF gbFloat2 *gb_float22_v(gbVec2 m[2]); +GB_MATH_DEF gbFloat2 *gb_float22_4(float m[4]); -GB_MATH_DEF void float22_transpose(float (*vec)[2]); -GB_MATH_DEF void float22_mul(float (*out)[2], float (*mat1)[2], float (*mat2)[2]); -GB_MATH_DEF void float22_mul_vec2(Vec2 *out, float m[2][2], Vec2 in); +GB_MATH_DEF void gb_float22_transpose(float (*vec)[2]); +GB_MATH_DEF void gb_float22_mul(float (*out)[2], float (*mat1)[2], float (*mat2)[2]); +GB_MATH_DEF void gb_float22_mul_vec2(gbVec2 *out, float m[2][2], gbVec2 in); -GB_MATH_DEF void mat3_identity(Mat3 *m); -GB_MATH_DEF void float33_identity(float m[3][3]); +GB_MATH_DEF void gb_mat3_identity(gbMat3 *m); +GB_MATH_DEF void gb_float33_identity(float m[3][3]); -GB_MATH_DEF void mat3_transpose(Mat3 *m); -GB_MATH_DEF void mat3_mul(Mat3 *out, Mat3 *m1, Mat3 *m2); -GB_MATH_DEF void mat3_mul_vec3(Vec3 *out, Mat3 *m, Vec3 in); +GB_MATH_DEF void gb_mat3_transpose(gbMat3 *m); +GB_MATH_DEF void gb_mat3_mul(gbMat3 *out, gbMat3 *m1, gbMat3 *m2); +GB_MATH_DEF void gb_mat3_mul_vec3(gbVec3 *out, gbMat3 *m, gbVec3 in); -GB_MATH_DEF Mat3 *mat3_v(Vec3 m[3]); -GB_MATH_DEF Mat3 *mat3_f(float m[3][3]); -GB_MATH_DEF Float3 *float33_m(Mat3 *m); -GB_MATH_DEF Float3 *float33_v(Vec3 m[3]); -GB_MATH_DEF Float3 *float33_9(float m[9]); +GB_MATH_DEF gbMat3 *gb_mat3_v(gbVec3 m[3]); +GB_MATH_DEF gbMat3 *gb_mat3_f(float m[3][3]); +GB_MATH_DEF gbFloat3 *gb_float33_m(gbMat3 *m); +GB_MATH_DEF gbFloat3 *gb_float33_v(gbVec3 m[3]); +GB_MATH_DEF gbFloat3 *gb_float33_9(float m[9]); -GB_MATH_DEF void float33_transpose(float (*vec)[3]); -GB_MATH_DEF void float33_mul(float (*out)[3], float (*mat1)[3], float (*mat2)[3]); -GB_MATH_DEF void float33_mul_vec3(Vec3 *out, float m[3][3], Vec3 in); +GB_MATH_DEF void gb_float33_transpose(float (*vec)[3]); +GB_MATH_DEF void gb_float33_mul(float (*out)[3], float (*mat1)[3], float (*mat2)[3]); +GB_MATH_DEF void gb_float33_mul_vec3(gbVec3 *out, float m[3][3], gbVec3 in); -GB_MATH_DEF void mat4_identity(Mat4 *m); -GB_MATH_DEF void float44_identity(float m[4][4]); +GB_MATH_DEF void gb_mat4_identity(gbMat4 *m); +GB_MATH_DEF void gb_float44_identity(float m[4][4]); -GB_MATH_DEF void mat4_transpose(Mat4 *m); -GB_MATH_DEF void mat4_mul(Mat4 *out, Mat4 *m1, Mat4 *m2); -GB_MATH_DEF void mat4_mul_vec4(Vec4 *out, Mat4 *m, Vec4 in); +GB_MATH_DEF void gb_mat4_transpose(gbMat4 *m); +GB_MATH_DEF void gb_mat4_mul(gbMat4 *out, gbMat4 *m1, gbMat4 *m2); +GB_MATH_DEF void gb_mat4_mul_vec4(gbVec4 *out, gbMat4 *m, gbVec4 in); -GB_MATH_DEF Mat4 *mat4_v(Vec4 m[4]); -GB_MATH_DEF Mat4 *mat4_f(float m[4][4]); -GB_MATH_DEF Float4 *float44_m(Mat4 *m); -GB_MATH_DEF Float4 *float44_v(Vec4 m[4]); -GB_MATH_DEF Float4 *float44_16(float m[16]); +GB_MATH_DEF gbMat4 *gb_mat4_v(gbVec4 m[4]); +GB_MATH_DEF gbMat4 *gb_mat4_f(float m[4][4]); +GB_MATH_DEF gbFloat4 *gb_float44_m(gbMat4 *m); +GB_MATH_DEF gbFloat4 *gb_float44_v(gbVec4 m[4]); +GB_MATH_DEF gbFloat4 *gb_float44_16(float m[16]); -GB_MATH_DEF void float44_transpose(float (*vec)[4]); -GB_MATH_DEF void float44_mul(float (*out)[4], float (*mat1)[4], float (*mat2)[4]); -GB_MATH_DEF void float44_mul_vec4(Vec4 *out, float m[4][4], Vec4 in); +GB_MATH_DEF void gb_float44_transpose(float (*vec)[4]); +GB_MATH_DEF void gb_float44_mul(float (*out)[4], float (*mat1)[4], float (*mat2)[4]); +GB_MATH_DEF void gb_float44_mul_vec4(gbVec4 *out, float m[4][4], gbVec4 in); -// Hermite Interpolations -GB_MATH_DEF float lerp(float a, float b, float t); -GB_MATH_DEF float smooth_step(float a, float b, float t); -GB_MATH_DEF float smoother_step(float a, float b, float t); +GB_MATH_DEF void gb_mat4_translate(gbMat4 *out, gbVec3 v); +GB_MATH_DEF void gb_mat4_rotate(gbMat4 *out, gbVec3 v, float angle_radians); +GB_MATH_DEF void gb_mat4_scale(gbMat4 *out, gbVec3 v); +GB_MATH_DEF void gb_mat4_scalef(gbMat4 *out, float s); +GB_MATH_DEF void gb_mat4_ortho2d(gbMat4 *out, float left, float right, float bottom, float top); +GB_MATH_DEF void gb_mat4_ortho3d(gbMat4 *out, float left, float right, float bottom, float top, float z_near, float z_far); +GB_MATH_DEF void gb_mat4_perspective(gbMat4 *out, float fovy, float aspect, float z_near, float z_far); +GB_MATH_DEF void gb_mat4_infinite_perspective(gbMat4 *out, float fovy, float aspect, float z_near); -GB_MATH_DEF void vec2_lerp(Vec2 *d, Vec2 a, Vec2 b, float t); -GB_MATH_DEF void vec3_lerp(Vec3 *d, Vec3 a, Vec3 b, float t); -GB_MATH_DEF void vec4_lerp(Vec4 *d, Vec4 a, Vec4 b, float t); +GB_MATH_DEF void gb_mat4_look_at(gbMat4 *out, gbVec3 eye, gbVec3 centre, gbVec3 up); -// Angles -GB_MATH_DEF float to_radians(float degrees); -GB_MATH_DEF float to_degrees(float radians); -// Projections -// NOTE(bill): Uses OpenGL Projection Conventions (-1 to 1) -// NOTE(bill): If Direct3D is needed, use custom projection to shift-z to (0 to 1) -GB_MATH_DEF void mat4_ortho2d(Mat4 *out, float left, float right, float bottom, float top); -GB_MATH_DEF void mat4_ortho3d(Mat4 *out, float left, float right, float bottom, float top, float z_near, float z_far); +GB_MATH_DEF gbQuat gb_quat(float x, float y, float z, float w); +GB_MATH_DEF gbQuat gb_quatv(float e[4]); +GB_MATH_DEF gbQuat gb_quat_axis_angle(gbVec3 axis, float angle_radians); +GB_MATH_DEF gbQuat gb_quat_euler_angles(float pitch, float yaw, float roll); +GB_MATH_DEF gbQuat gb_quat_identity(void); -#ifndef MURMUR64_SEED -#define MURMUR64_SEED 0x9747b28c +GB_MATH_DEF void gb_quat_add(gbQuat *d, gbQuat q0, gbQuat q1); +GB_MATH_DEF void gb_quat_sub(gbQuat *d, gbQuat q0, gbQuat q1); +GB_MATH_DEF void gb_quat_mul(gbQuat *d, gbQuat q0, gbQuat q1); +GB_MATH_DEF void gb_quat_div(gbQuat *d, gbQuat q0, gbQuat q1); + +GB_MATH_DEF void gb_quat_mulf(gbQuat *d, gbQuat q, float s); +GB_MATH_DEF void gb_quat_divf(gbQuat *d, gbQuat q, float s); + + +GB_MATH_DEF void gb_quat_addeq(gbQuat *d, gbQuat q); +GB_MATH_DEF void gb_quat_subeq(gbQuat *d, gbQuat q); +GB_MATH_DEF void gb_quat_muleq(gbQuat *d, gbQuat q); +GB_MATH_DEF void gb_quat_diveq(gbQuat *d, gbQuat q); + + +GB_MATH_DEF void gb_quat_muleqf(gbQuat *d, float s); +GB_MATH_DEF void gb_quat_diveqf(gbQuat *d, float s); + + + + +GB_MATH_DEF float gb_quat_dot(gbQuat q0, gbQuat q1); +GB_MATH_DEF float gb_quat_mag(gbQuat q); + +GB_MATH_DEF void gb_quat_norm(gbQuat *d, gbQuat q); +GB_MATH_DEF void gb_quat_conj(gbQuat *d, gbQuat q); +GB_MATH_DEF void gb_quat_inverse(gbQuat *d, gbQuat q); + +GB_MATH_DEF void gb_quat_axis(gbVec3 *axis, gbQuat q); +GB_MATH_DEF float gb_quat_angle(gbQuat q); + +GB_MATH_DEF float gb_quat_pitch(gbQuat q); +GB_MATH_DEF float gb_quat_yaw(gbQuat q); +GB_MATH_DEF float gb_quat_roll(gbQuat q); + +// Rotate v by q +GB_MATH_DEF void gb_quat_rotate_vec3(gbVec3 *d, gbQuat q, gbVec3 v); +GB_MATH_DEF void gb_mat4_from_quat(gbMat4 *out, gbQuat q); +GB_MATH_DEF void gb_quat_from_mat4(gbQuat *out, gbMat4 *m); + + + +// Interpolations +GB_MATH_DEF float gb_lerp(float a, float b, float t); +GB_MATH_DEF float gb_smooth_step(float a, float b, float t); +GB_MATH_DEF float gb_smoother_step(float a, float b, float t); + +GB_MATH_DEF void gb_vec2_lerp(gbVec2 *d, gbVec2 a, gbVec2 b, float t); +GB_MATH_DEF void gb_vec3_lerp(gbVec3 *d, gbVec3 a, gbVec3 b, float t); +GB_MATH_DEF void gb_vec4_lerp(gbVec4 *d, gbVec4 a, gbVec4 b, float t); + +GB_MATH_DEF void gb_quat_lerp(gbQuat *d, gbQuat a, gbQuat b, float t); +GB_MATH_DEF void gb_quat_nlerp(gbQuat *d, gbQuat a, gbQuat b, float t); +GB_MATH_DEF void gb_quat_slerp(gbQuat *d, gbQuat a, gbQuat b, float t); +GB_MATH_DEF void gb_quat_slerp_approx(gbQuat *d, gbQuat a, gbQuat b, float t); +GB_MATH_DEF void gb_quat_nquad(gbQuat *d, gbQuat p, gbQuat a, gbQuat b, gbQuat q, float t); +GB_MATH_DEF void gb_quat_squad(gbQuat *d, gbQuat p, gbQuat a, gbQuat b, gbQuat q, float t); +GB_MATH_DEF void gb_quat_squad_approx(gbQuat *d, gbQuat p, gbQuat a, gbQuat b, gbQuat q, float t); + + +// Rects +GB_MATH_DEF gbRect2 gb_rect2(gbVec2 pos, gbVec2 dim); +GB_MATH_DEF gbRect3 gb_rect3(gbVec3 pos, gbVec3 dim); + +GB_MATH_DEF int gb_rect2_contains(gbRect2 a, float x, float y); +GB_MATH_DEF int gb_rect2_contains_vec2(gbRect2 a, gbVec2 p); +GB_MATH_DEF int gb_rect2_intersects(gbRect2 a, gbRect2 b); +GB_MATH_DEF int gb_rect2_intersection_result(gbRect2 a, gbRect2 b, gbRect2 *intersection); + + +#ifndef GB_MURMUR64_DEFAULT_SEED +#define GB_MURMUR64_DEFAULT_SEED 0x9747b28c #endif // Hashing -GB_MATH_DEF gb_math_u64 hash_murmur64(void const *key, size_t num_bytes); +GB_MATH_DEF gb_math_u64 gb_hash_murmur64(void const *key, size_t num_bytes, gb_math_u64 seed); // Random // TODO(bill): Use a generator for the random numbers -GB_MATH_DEF float random_range_float(float min_inc, float max_inc); -GB_MATH_DEF int random_range_int(int min_inc, int max_inc); +GB_MATH_DEF float gb_random_range_float(float min_inc, float max_inc); +GB_MATH_DEF int gb_random_range_int(int min_inc, int max_inc); #if defined(__cplusplus) } #endif +#if defined(__cplusplus) && defined(GB_MATH_USE_OPERATOR_OVERLOADS) + +bool operator==(gbVec2 a, gbVec2 b) { return (a.x == b.x) && (a.y == b.y); } +bool operator!=(gbVec2 a, gbVec2 b) { return !operator==(a, b); } + +gbVec2 operator+(gbVec2 a) { return a; } +gbVec2 operator-(gbVec2 a) { gbVec2 r = {-a.x, -a.y}; return r; } + +gbVec2 operator+(gbVec2 a, gbVec2 b) { gbVec2 r; gb_vec2_add(&r, a, b); return r; } +gbVec2 operator-(gbVec2 a, gbVec2 b) { gbVec2 r; gb_vec2_sub(&r, a, b); return r; } + +gbVec2 operator*(gbVec2 a, float scalar) { gbVec2 r; gb_vec2_mul(&r, a, scalar); return r; } +gbVec2 operator*(float scalar, gbVec2 a) { return operator*(a, scalar); } + +gbVec2 operator/(gbVec2 a, float scalar) { return operator*(a, 1.0f/scalar); } + +// Hadamard Product +gbVec2 operator*(gbVec2 a, gbVec2 b) { gbVec2 r = {a.x*b.x, a.y*b.y}; return r; } +gbVec2 operator/(gbVec2 a, gbVec2 b) { gbVec2 r = {a.x/b.x, a.y/b.y}; return r; } + +gbVec2 &operator+=(gbVec2 &a, gbVec2 b) { return (a = a + b); } +gbVec2 &operator-=(gbVec2 &a, gbVec2 b) { return (a = a - b); } +gbVec2 &operator*=(gbVec2 &a, float scalar) { return (a = a * scalar); } +gbVec2 &operator/=(gbVec2 &a, float scalar) { return (a = a / scalar); } + + +bool operator==(gbVec3 a, gbVec3 b) { return (a.x == b.x) && (a.y == b.y) && (a.z == b.z); } +bool operator!=(gbVec3 a, gbVec3 b) { return !operator==(a, b); } + +gbVec3 operator+(gbVec3 a) { return a; } +gbVec3 operator-(gbVec3 a) { gbVec3 r = {-a.x, -a.y, -a.z}; return r; } + +gbVec3 operator+(gbVec3 a, gbVec3 b) { gbVec3 r; gb_vec3_add(&r, a, b); return r; } +gbVec3 operator-(gbVec3 a, gbVec3 b) { gbVec3 r; gb_vec3_sub(&r, a, b); return r; } + +gbVec3 operator*(gbVec3 a, float scalar) { gbVec3 r; gb_vec3_mul(&r, a, scalar); return r; } +gbVec3 operator*(float scalar, gbVec3 a) { return operator*(a, scalar); } + +gbVec3 operator/(gbVec3 a, float scalar) { return operator*(a, 1.0f/scalar); } + +// Hadamard Product +gbVec3 operator*(gbVec3 a, gbVec3 b) { gbVec3 r = {a.x*b.x, a.y*b.y, a.z*b.z}; return r; } +gbVec3 operator/(gbVec3 a, gbVec3 b) { gbVec3 r = {a.x/b.x, a.y/b.y, a.z/b.z}; return r; } + +gbVec3 &operator+=(gbVec3 &a, gbVec3 b) { return (a = a + b); } +gbVec3 &operator-=(gbVec3 &a, gbVec3 b) { return (a = a - b); } +gbVec3 &operator*=(gbVec3 &a, float scalar) { return (a = a * scalar); } +gbVec3 &operator/=(gbVec3 &a, float scalar) { return (a = a / scalar); } + + +bool operator==(gbVec4 a, gbVec4 b) { return (a.x == b.x) && (a.y == b.y) && (a.z == b.z) && (a.w == b.w); } +bool operator!=(gbVec4 a, gbVec4 b) { return !operator==(a, b); } + +gbVec4 operator+(gbVec4 a) { return a; } +gbVec4 operator-(gbVec4 a) { gbVec4 r = {-a.x, -a.y, -a.z, -a.w}; return r; } + +gbVec4 operator+(gbVec4 a, gbVec4 b) { gbVec4 r; gb_vec4_add(&r, a, b); return r; } +gbVec4 operator-(gbVec4 a, gbVec4 b) { gbVec4 r; gb_vec4_sub(&r, a, b); return r; } + +gbVec4 operator*(gbVec4 a, float scalar) { gbVec4 r; gb_vec4_mul(&r, a, scalar); return r; } +gbVec4 operator*(float scalar, gbVec4 a) { return operator*(a, scalar); } + +gbVec4 operator/(gbVec4 a, float scalar) { return operator*(a, 1.0f/scalar); } + +// Hadamard Product +gbVec4 operator*(gbVec4 a, gbVec4 b) { gbVec4 r = {a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w}; return r; } +gbVec4 operator/(gbVec4 a, gbVec4 b) { gbVec4 r = {a.x/b.x, a.y/b.y, a.z/b.z, a.w/b.w}; return r; } + +gbVec4 &operator+=(gbVec4 &a, gbVec4 b) { return (a = a + b); } +gbVec4 &operator-=(gbVec4 &a, gbVec4 b) { return (a = a - b); } +gbVec4 &operator*=(gbVec4 &a, float scalar) { return (a = a * scalar); } +gbVec4 &operator/=(gbVec4 &a, float scalar) { return (a = a / scalar); } + + +gbMat2 operator+(gbMat2 const &a, gbMat2 const &b) +{ + int i, j; + gbMat2 r = {0}; + for (j = 0; j < 2; j++) { + for (i = 0; i < 2; i++) + r.e[2*j+i] = a.e[2*j+i] + b.e[2*j+i]; + } + return r; +} + +gbMat2 operator-(gbMat2 const &a, gbMat2 const &b) +{ + int i, j; + gbMat2 r = {0}; + for (j = 0; j < 2; j++) { + for (i = 0; i < 2; i++) + r.e[2*j+i] = a.e[2*j+i] - b.e[2*j+i]; + } + return r; +} + +gbMat2 operator*(gbMat2 const &a, gbMat2 const &b) { gbMat2 r; gb_mat2_mul(&r, (gbMat2 *)&a, (gbMat2 *)&b); return r; } +gbVec2 operator*(gbMat2 const &a, gbVec2 v) { gbVec2 r; gb_mat2_mul_vec2(&r, (gbMat2 *)&a, v); return r; } +gbMat2 operator*(gbMat2 const &a, float scalar) +{ + gbMat2 r = {0}; + int i; + for (i = 0; i < 2*2; i++) r.e[i] = a.e[i] * scalar; + return r; +} +gbMat2 operator*(float scalar, gbMat2 const &a) { return operator*(a, scalar); } +gbMat2 operator/(gbMat2 const &a, float scalar) { return operator*(a, 1.0f/scalar); } + +gbMat2& operator+=(gbMat2& a, gbMat2 const &b) { return (a = a + b); } +gbMat2& operator-=(gbMat2& a, gbMat2 const &b) { return (a = a - b); } +gbMat2& operator*=(gbMat2& a, gbMat2 const &b) { return (a = a * b); } + + + +gbMat3 operator+(gbMat3 const &a, gbMat3 const &b) +{ + int i, j; + gbMat3 r = {0}; + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) + r.e[3*j+i] = a.e[3*j+i] + b.e[3*j+i]; + } + return r; +} + +gbMat3 operator-(gbMat3 const &a, gbMat3 const &b) +{ + int i, j; + gbMat3 r = {0}; + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) + r.e[3*j+i] = a.e[3*j+i] - b.e[3*j+i]; + } + return r; +} + +gbMat3 operator*(gbMat3 const &a, gbMat3 const &b) { gbMat3 r; gb_mat3_mul(&r, (gbMat3 *)&a, (gbMat3 *)&b); return r; } +gbVec3 operator*(gbMat3 const &a, gbVec3 v) { gbVec3 r; gb_mat3_mul_vec3(&r, (gbMat3 *)&a, v); return r; } +gbMat3 operator*(gbMat3 const &a, float scalar) +{ + gbMat3 r = {0}; + int i; + for (i = 0; i < 3*3; i++) r.e[i] = a.e[i] * scalar; + return r; +} +gbMat3 operator*(float scalar, gbMat3 const &a) { return operator*(a, scalar); } +gbMat3 operator/(gbMat3 const &a, float scalar) { return operator*(a, 1.0f/scalar); } + +gbMat3& operator+=(gbMat3& a, gbMat3 const &b) { return (a = a + b); } +gbMat3& operator-=(gbMat3& a, gbMat3 const &b) { return (a = a - b); } +gbMat3& operator*=(gbMat3& a, gbMat3 const &b) { return (a = a * b); } + + + +gbMat4 operator+(gbMat4 const &a, gbMat4 const &b) +{ + int i, j; + gbMat4 r = {0}; + for (j = 0; j < 4; j++) { + for (i = 0; i < 4; i++) + r.e[4*j+i] = a.e[4*j+i] + b.e[4*j+i]; + } + return r; +} + +gbMat4 operator-(gbMat4 const &a, gbMat4 const &b) +{ + int i, j; + gbMat4 r = {0}; + for (j = 0; j < 4; j++) { + for (i = 0; i < 4; i++) + r.e[4*j+i] = a.e[4*j+i] - b.e[4*j+i]; + } + return r; +} + +gbMat4 operator*(gbMat4 const &a, gbMat4 const &b) { gbMat4 r; gb_mat4_mul(&r, (gbMat4 *)&a, (gbMat4 *)&b); return r; } +gbVec4 operator*(gbMat4 const &a, gbVec4 v) { gbVec4 r; gb_mat4_mul_vec4(&r, (gbMat4 *)&a, v); return r; } +gbMat4 operator*(gbMat4 const &a, float scalar) +{ + gbMat4 r = {0}; + int i; + for (i = 0; i < 4*4; i++) r.e[i] = a.e[i] * scalar; + return r; +} +gbMat4 operator*(float scalar, gbMat4 const &a) { return operator*(a, scalar); } +gbMat4 operator/(gbMat4 const &a, float scalar) { return operator*(a, 1.0f/scalar); } + +gbMat4& operator+=(gbMat4 &a, gbMat4 const &b) { return (a = a + b); } +gbMat4& operator-=(gbMat4 &a, gbMat4 const &b) { return (a = a - b); } +gbMat4& operator*=(gbMat4 &a, gbMat4 const &b) { return (a = a * b); } + + + +bool operator==(gbQuat a, gbQuat b) { return a.xyzw == b.xyzw; } +bool operator!=(gbQuat a, gbQuat b) { return !operator==(a, b); } + +gbQuat operator+(gbQuat q) { return q; } +gbQuat operator-(gbQuat q) { return gb_quat(-q.x, -q.y, -q.z, -q.w); } + +gbQuat operator+(gbQuat a, gbQuat b) { gbQuat r; gb_quat_add(&r, a, b); return r; } +gbQuat operator-(gbQuat a, gbQuat b) { gbQuat r; gb_quat_sub(&r, a, b); return r; } + +gbQuat operator*(gbQuat a, gbQuat b) { gbQuat r; gb_quat_mul(&r, a, b); return r; } +gbQuat operator*(gbQuat q, float s) { gbQuat r; gb_quat_mulf(&r, q, s); return r; } +gbQuat operator*(float s, gbQuat q) { return operator*(q, s); } +gbQuat operator/(gbQuat q, float s) { gbQuat r; gb_quat_divf(&r, q, s); return r; } + +gbQuat &operator+=(gbQuat &a, gbQuat b) { gb_quat_addeq(&a, b); return a; } +gbQuat &operator-=(gbQuat &a, gbQuat b) { gb_quat_subeq(&a, b); return a; } +gbQuat &operator*=(gbQuat &a, gbQuat b) { gb_quat_muleq(&a, b); return a; } +gbQuat &operator/=(gbQuat &a, gbQuat b) { gb_quat_diveq(&a, b); return a; } + +gbQuat &operator*=(gbQuat &a, float b) { gb_quat_muleqf(&a, b); return a; } +gbQuat &operator/=(gbQuat &a, float b) { gb_quat_diveqf(&a, b); return a; } + +// Rotate v by a +gbVec3 operator*(gbQuat q, gbVec3 v) { gbVec3 r; gb_quat_rotate_vec3(&r, q, v); return r; } + +#endif + + + #endif // GB_MATH_INCLUDE_GB_MATH_H //////////////////// // // +// // +// // +// // +// // +// // +// // +// // +// // +// // +// // +// // +// // +// // +// // +// // // Implementation // // // +// // +// // +// // +// // +// // +// // +// // +// // +// // +// // +// // +// // +// // +// // +// // //////////////////// -#if defined(GB_MATH_IMPLEMENTATION) - -#include - -#define VEC2_2OP(a,c,post) \ - a->x = c.x post; \ - a->y = c.y post; - -#define VEC2_3OP(a,b,op,c,post) \ - a->x = b.x op c.x post; \ - a->y = b.y op c.y post; - -#define VEC3_2OP(a,c,post) \ - a->x = c.x post; \ - a->y = c.y post; \ - a->z = c.z post; - -#define VEC3_3OP(a,b,op,c,post) \ - a->x = b.x op c.x post; \ - a->y = b.y op c.y post; \ - a->z = b.z op c.z post; - -#define VEC4_2OP(a,c,post) \ - a->x = c.x post; \ - a->y = c.y post; \ - a->z = c.z post; \ - a->w = c.w post; - -#define VEC4_3OP(a,b,op,c,post) \ - a->x = b.x op c.x post; \ - a->y = b.y op c.y post; \ - a->z = b.z op c.z post; \ - a->w = b.w op c.w post; +#if defined(GB_MATH_IMPLEMENTATION) && !defined(GB_MATH_IMPLEMENTATION_DONE) +#define GB_MATH_IMPLEMENTATION_DONE -Vec2 vec2_zero(void) { Vec2 v = {0, 0}; return v; } -Vec2 vec2(float x, float y) { Vec2 v = {x, y}; return v; } -Vec2 vec2v(float x[2]) { Vec2 v = {x[0], x[1]}; return v; } +float gb_to_radians(float degrees) { return degrees * GB_MATH_TAU / 360.0f; } +float gb_to_degrees(float radians) { return radians * 360.0f / GB_MATH_TAU; } -Vec3 vec3_zero(void) { Vec3 v = {0, 0, 0}; return v; } -Vec3 vec3(float x, float y, float z) { Vec3 v = {x, y, z}; return v; } -Vec3 vec3v(float x[3]) { Vec3 v = {x[0], x[1], x[2]}; return v; } - -Vec4 vec4_zero(void) { Vec4 v = {0, 0, 0, 0}; return v; } -Vec4 vec4(float x, float y, float z, float w) { Vec4 v = {x, y, z, w}; return v; } -Vec4 vec4v(float x[4]) { Vec4 v = {x[0], x[1], x[2], x[3]}; return v; } - - -void vec2_add(Vec2 *d, Vec2 v0, Vec2 v1) { VEC2_3OP(d,v0,+,v1,+0); } -void vec2_sub(Vec2 *d, Vec2 v0, Vec2 v1) { VEC2_3OP(d,v0,-,v1,+0); } -void vec2_mul(Vec2 *d, Vec2 v, float s) { VEC2_2OP(d,v,* s); } -void vec2_div(Vec2 *d, Vec2 v, float s) { VEC2_2OP(d,v,/ s); } - -void vec3_add(Vec3 *d, Vec3 v0, Vec3 v1) { VEC3_3OP(d,v0,+,v1,+0); } -void vec3_sub(Vec3 *d, Vec3 v0, Vec3 v1) { VEC3_3OP(d,v0,-,v1,+0); } -void vec3_mul(Vec3 *d, Vec3 v, float s) { VEC3_2OP(d,v,* s); } -void vec3_div(Vec3 *d, Vec3 v, float s) { VEC3_2OP(d,v,/ s); } - -void vec4_add(Vec4 *d, Vec4 v0, Vec4 v1) { VEC4_3OP(d,v0,+,v1,+0); } -void vec4_sub(Vec4 *d, Vec4 v0, Vec4 v1) { VEC4_3OP(d,v0,-,v1,+0); } -void vec4_mul(Vec4 *d, Vec4 v, float s) { VEC4_2OP(d,v,* s); } -void vec4_div(Vec4 *d, Vec4 v, float s) { VEC4_2OP(d,v,/ s); } - - -void vec2_addeq(Vec2 *d, Vec2 v) { VEC2_3OP(d,(*d),+,v,+0); } -void vec2_subeq(Vec2 *d, Vec2 v) { VEC2_3OP(d,(*d),-,v,+0); } -void vec2_muleq(Vec2 *d, float s) { VEC2_2OP(d,(*d),* s); } -void vec2_diveq(Vec2 *d, float s) { VEC2_2OP(d,(*d),/ s); } - -void vec3_addeq(Vec3 *d, Vec3 v) { VEC3_3OP(d,(*d),+,v,+0); } -void vec3_subeq(Vec3 *d, Vec3 v) { VEC3_3OP(d,(*d),-,v,+0); } -void vec3_muleq(Vec3 *d, float s) { VEC3_2OP(d,(*d),* s); } -void vec3_diveq(Vec3 *d, float s) { VEC3_2OP(d,(*d),/ s); } - -void vec4_addeq(Vec4 *d, Vec4 v) { VEC4_3OP(d,(*d),+,v,+0); } -void vec4_subeq(Vec4 *d, Vec4 v) { VEC4_3OP(d,(*d),-,v,+0); } -void vec4_muleq(Vec4 *d, float s) { VEC4_2OP(d,(*d),* s); } -void vec4_diveq(Vec4 *d, float s) { VEC4_2OP(d,(*d),/ s); } - - -float vec2_dot(Vec2 v0, Vec2 v1) { return v0.x*v1.x + v0.y*v1.y; } -float vec3_dot(Vec3 v0, Vec3 v1) { return v0.x*v1.x + v0.y*v1.y + v0.z*v1.z; } -float vec4_dot(Vec4 v0, Vec4 v1) { return v0.x*v1.x + v0.y*v1.y + v0.z*v1.z + v0.w*v1.w; } - -void vec2_cross(float *d, Vec2 v0, Vec2 v1) { *d = v0.x*v1.y - v1.x*v0.y; } -void vec3_cross(Vec3 *d, Vec3 v0, Vec3 v1) { d->x = v0.y*v1.z - v0.z*v1.y; - d->y = v0.z*v1.x - v0.x*v1.z; - d->z = v0.x*v1.y - v0.y*v1.x; } - -float vec2_mag2(Vec2 v) { return vec2_dot(v, v); } -float vec3_mag2(Vec3 v) { return vec3_dot(v, v); } -float vec4_mag2(Vec4 v) { return vec4_dot(v, v); } - -// TODO(bill): Create custom sqrt function -float vec2_mag(Vec2 v) { return sqrtf(vec2_dot(v, v)); } -float vec3_mag(Vec3 v) { return sqrtf(vec3_dot(v, v)); } -float vec4_mag(Vec4 v) { return sqrtf(vec4_dot(v, v)); } - -// TODO(bill): Should I calculate inv_sqrt directly? -void vec2_norm(Vec2 *d, Vec2 v) { vec2_div(d, v, vec2_mag(v)); } -void vec3_norm(Vec3 *d, Vec3 v) { vec3_div(d, v, vec3_mag(v)); } -void vec4_norm(Vec4 *d, Vec4 v) { vec4_div(d, v, vec4_mag(v)); } - -float vec2_aspect(Vec2 v) +float +gb_angle_diff(float radians_a, float radians_b) { - if (v.y < 0.0001f) - return 0.0f; - return v.x/v.y; + float delta = fmodf(radians_b-radians_a, GB_MATH_TAU); + delta = fmodf(delta + 1.5f*GB_MATH_TAU, GB_MATH_TAU); + delta -= 0.5f*GB_MATH_TAU; + return delta; +} + + +float gb_sqrt(float a) { return sqrtf(a); } + +float +gb_quake_inv_sqrt(float a) +{ + union { + int i; + float f; + } t; + float x2; + float const three_halfs = 1.5f; + + x2 = a * 0.5f; + t.f = a; + t.i = 0x5f375a86 - (t.i >> 1); // What the fuck? + t.f = t.f * (three_halfs - (x2 * t.f * t.f)); // 1st iteration + t.f = t.f * (three_halfs - (x2 * t.f * t.f)); // 2nd iteration, this can be removed + + return t.f; +} + +float gb_sin(float radians) { return sinf(radians); } +float gb_cos(float radians) { return cosf(radians); } +float gb_tan(float radians) { return tanf(radians); } +float gb_arcsin(float a) { return asinf(a); } +float gb_arccos(float a) { return acosf(a); } +float gb_arctan(float a) { return atanf(a); } +float gb_arctan2(float y, float x) { return atan2f(y, x); } + + + +float gb_exp(float x) { return expf(x); } +float gb_exp2(float x) { return gb_exp(GB_MATH_LOG_TWO * x); } +float gb_log(float x) { return logf(x); } +float gb_log2(float x) { return gb_log(x) / GB_MATH_LOG_TWO; } + +float +gb_fast_exp(float x) +{ + // NOTE(bill): Only works in the range -1 <= x <= +1 + float e = 1.0f + x*(1.0f + x*0.5f*(1.0f + x*0.3333333333f*(1.0f + x*0.25*(1.0f + x*0.2f)))); + return e; +} + +float gb_fast_exp2(float x) { return gb_fast_exp(GB_MATH_LOG_TWO * x); } + +// TODO(bill): Should this be gb_exp(y * gb_log(x)) ??? +float gb_pow(float x, float y) { return powf(x, y); } + + +typedef union { + unsigned int i; + float f; +} gb_uif32; + +float +gb_half_to_float(gb_half value) +{ + gb_uif32 result; + int s = (value >> 15) & 0x001; + int e = (value >> 10) & 0x01f; + int m = value & 0x3ff; + + if (e == 0) { + if (m == 0) { + // Plus or minus zero + result.i = (unsigned int)(s << 31); + return result.f; + } else { + // Denormalized number + while (!(m & 0x00000400)) { + m <<= 1; + e -= 1; + } + + e += 1; + m &= ~0x00000400; + } + } else if (e == 31) { + if (m == 0) { + // Positive or negative infinity + result.i = (unsigned int)((s << 31) | 0x7f800000); + return result.f; + } else { + // Nan + result.i = (unsigned int)((s << 31) | 0x7f800000 | (m << 13)); + return result.f; + } + } + + e = e + (127 - 15); + m = m << 13; + + result.i = (unsigned int)((s << 31) | (e << 23) | m); + return result.f; +} + +gb_half +gb_float_to_half(float value) +{ + gb_uif32 v; + int i, s, e, m; + + v.f = value; + i = (int)v.i; + + s = (i >> 16) & 0x00008000; + e = ((i >> 23) & 0x000000ff) - (127 - 15); + m = i & 0x007fffff; + + + if (e <= 0) { + if (e < -10) return (gb_half)s; + m = (m | 0x00800000) >> (1 - e); + + if (m & 0x00001000) + m += 0x00002000; + + return (gb_half)(s | (m >> 13)); + } else if (e == 0xff - (127 - 15)) { + if (m == 0) { + return (gb_half)(s | 0x7c00); // NOTE(bill): infinity + } else { + // NOTE(bill): NAN + m >>= 13; + return (gb_half)(s | 0x7c00 | m | (m == 0)); + } + } else { + if (m & 0x00001000) { + m += 0x00002000; + if (m & 0x00800000) { + m = 0; + e += 1; + } + } + + if (e > 30) { + float volatile f = 1e12f; + int j; + for (j = 0; j < 10; j++) + f *= f; // NOTE(bill): Cause overflow + + return (gb_half)(s | 0x7c00); + } + + return (gb_half)(s | (e << 10) | (m >> 13)); + } } -void mat2_transpose(Mat2 *m) { float22_transpose(float22_m(m)); } -void mat2_identity(Mat2 *m) { float22_identity(float22_m(m)); } -void mat2_mul(Mat2 *out, Mat2 *m1, Mat2 *m2) { float22_mul(float22_m(out), float22_m(m1), float22_m(m2)); } + + +#define GB_VEC2_2OP(a,c,post) \ + a->x = c.x post; \ + a->y = c.y post; + +#define GB_VEC2_3OP(a,b,op,c,post) \ + a->x = b.x op c.x post; \ + a->y = b.y op c.y post; + +#define GB_VEC3_2OP(a,c,post) \ + a->x = c.x post; \ + a->y = c.y post; \ + a->z = c.z post; + +#define GB_VEC3_3OP(a,b,op,c,post) \ + a->x = b.x op c.x post; \ + a->y = b.y op c.y post; \ + a->z = b.z op c.z post; + +#define GB_VEC4_2OP(a,c,post) \ + a->x = c.x post; \ + a->y = c.y post; \ + a->z = c.z post; \ + a->w = c.w post; + +#define GB_VEC4_3OP(a,b,op,c,post) \ + a->x = b.x op c.x post; \ + a->y = b.y op c.y post; \ + a->z = b.z op c.z post; \ + a->w = b.w op c.w post; + + +gbVec2 gb_vec2_zero(void) { gbVec2 v = {0, 0}; return v; } +gbVec2 gb_vec2(float x, float y) { gbVec2 v = {x, y}; return v; } +gbVec2 gb_vec2v(float x[2]) { gbVec2 v = {x[0], x[1]}; return v; } + +gbVec3 gb_vec3_zero(void) { gbVec3 v = {0, 0, 0}; return v; } +gbVec3 gb_vec3(float x, float y, float z) { gbVec3 v = {x, y, z}; return v; } +gbVec3 gb_vec3v(float x[3]) { gbVec3 v = {x[0], x[1], x[2]}; return v; } + +gbVec4 gb_vec4_zero(void) { gbVec4 v = {0, 0, 0, 0}; return v; } +gbVec4 gb_vec4(float x, float y, float z, float w) { gbVec4 v = {x, y, z, w}; return v; } +gbVec4 gb_vec4v(float x[4]) { gbVec4 v = {x[0], x[1], x[2], x[3]}; return v; } + + +void gb_vec2_add(gbVec2 *d, gbVec2 v0, gbVec2 v1) { GB_VEC2_3OP(d,v0,+,v1,+0); } +void gb_vec2_sub(gbVec2 *d, gbVec2 v0, gbVec2 v1) { GB_VEC2_3OP(d,v0,-,v1,+0); } +void gb_vec2_mul(gbVec2 *d, gbVec2 v, float s) { GB_VEC2_2OP(d,v,* s); } +void gb_vec2_div(gbVec2 *d, gbVec2 v, float s) { GB_VEC2_2OP(d,v,/ s); } + +void gb_vec3_add(gbVec3 *d, gbVec3 v0, gbVec3 v1) { GB_VEC3_3OP(d,v0,+,v1,+0); } +void gb_vec3_sub(gbVec3 *d, gbVec3 v0, gbVec3 v1) { GB_VEC3_3OP(d,v0,-,v1,+0); } +void gb_vec3_mul(gbVec3 *d, gbVec3 v, float s) { GB_VEC3_2OP(d,v,* s); } +void gb_vec3_div(gbVec3 *d, gbVec3 v, float s) { GB_VEC3_2OP(d,v,/ s); } + +void gb_vec4_add(gbVec4 *d, gbVec4 v0, gbVec4 v1) { GB_VEC4_3OP(d,v0,+,v1,+0); } +void gb_vec4_sub(gbVec4 *d, gbVec4 v0, gbVec4 v1) { GB_VEC4_3OP(d,v0,-,v1,+0); } +void gb_vec4_mul(gbVec4 *d, gbVec4 v, float s) { GB_VEC4_2OP(d,v,* s); } +void gb_vec4_div(gbVec4 *d, gbVec4 v, float s) { GB_VEC4_2OP(d,v,/ s); } + + +void gb_vec2_addeq(gbVec2 *d, gbVec2 v) { GB_VEC2_3OP(d,(*d),+,v,+0); } +void gb_vec2_subeq(gbVec2 *d, gbVec2 v) { GB_VEC2_3OP(d,(*d),-,v,+0); } +void gb_vec2_muleq(gbVec2 *d, float s) { GB_VEC2_2OP(d,(*d),* s); } +void gb_vec2_diveq(gbVec2 *d, float s) { GB_VEC2_2OP(d,(*d),/ s); } + +void gb_vec3_addeq(gbVec3 *d, gbVec3 v) { GB_VEC3_3OP(d,(*d),+,v,+0); } +void gb_vec3_subeq(gbVec3 *d, gbVec3 v) { GB_VEC3_3OP(d,(*d),-,v,+0); } +void gb_vec3_muleq(gbVec3 *d, float s) { GB_VEC3_2OP(d,(*d),* s); } +void gb_vec3_diveq(gbVec3 *d, float s) { GB_VEC3_2OP(d,(*d),/ s); } + +void gb_vec4_addeq(gbVec4 *d, gbVec4 v) { GB_VEC4_3OP(d,(*d),+,v,+0); } +void gb_vec4_subeq(gbVec4 *d, gbVec4 v) { GB_VEC4_3OP(d,(*d),-,v,+0); } +void gb_vec4_muleq(gbVec4 *d, float s) { GB_VEC4_2OP(d,(*d),* s); } +void gb_vec4_diveq(gbVec4 *d, float s) { GB_VEC4_2OP(d,(*d),/ s); } + + +#undef GB_VEC2_2OP +#undef GB_VEC2_3OP +#undef GB_VEC3_3OP +#undef GB_VEC3_2OP +#undef GB_VEC4_2OP +#undef GB_VEC4_3OP + + + + +float gb_vec2_dot(gbVec2 v0, gbVec2 v1) { return v0.x*v1.x + v0.y*v1.y; } +float gb_vec3_dot(gbVec3 v0, gbVec3 v1) { return v0.x*v1.x + v0.y*v1.y + v0.z*v1.z; } +float gb_vec4_dot(gbVec4 v0, gbVec4 v1) { return v0.x*v1.x + v0.y*v1.y + v0.z*v1.z + v0.w*v1.w; } + +void gb_vec2_cross(float *d, gbVec2 v0, gbVec2 v1) { *d = v0.x*v1.y - v1.x*v0.y; } +void gb_vec3_cross(gbVec3 *d, gbVec3 v0, gbVec3 v1) { d->x = v0.y*v1.z - v0.z*v1.y; + d->y = v0.z*v1.x - v0.x*v1.z; + d->z = v0.x*v1.y - v0.y*v1.x; } + +float gb_vec2_mag2(gbVec2 v) { return gb_vec2_dot(v, v); } +float gb_vec3_mag2(gbVec3 v) { return gb_vec3_dot(v, v); } +float gb_vec4_mag2(gbVec4 v) { return gb_vec4_dot(v, v); } + +// TODO(bill): Create custom sqrt function +float gb_vec2_mag(gbVec2 v) { return gb_sqrt(gb_vec2_dot(v, v)); } +float gb_vec3_mag(gbVec3 v) { return gb_sqrt(gb_vec3_dot(v, v)); } +float gb_vec4_mag(gbVec4 v) { return gb_sqrt(gb_vec4_dot(v, v)); } + +// TODO(bill): Should I calculate inv_sqrt directly? +void +gb_vec2_norm(gbVec2 *d, gbVec2 v) +{ + float mag = gb_vec2_mag(v); + gb_vec2_div(d, v, mag); +} +void +gb_vec3_norm(gbVec3 *d, gbVec3 v) +{ + float mag = gb_vec3_mag(v); + gb_vec3_div(d, v, mag); +} +void +gb_vec4_norm(gbVec4 *d, gbVec4 v) +{ + float mag = gb_vec4_mag(v); + gb_vec4_div(d, v, mag); +} void -float22_identity(float m[2][2]) +gb_vec2_norm0(gbVec2 *d, gbVec2 v) +{ + float mag = gb_vec2_mag(v); + if (mag > 0) + gb_vec2_div(d, v, mag); + else + *d = gb_vec2_zero(); +} +void +gb_vec3_norm0(gbVec3 *d, gbVec3 v) +{ + float mag = gb_vec3_mag(v); + if (mag > 0) + gb_vec3_div(d, v, mag); + else + *d = gb_vec3_zero(); +} +void +gb_vec4_norm0(gbVec4 *d, gbVec4 v) +{ + float mag = gb_vec4_mag(v); + if (mag > 0) + gb_vec4_div(d, v, mag); + else + *d = gb_vec4_zero(); +} + + +void +gb_vec2_reflect(gbVec2 *d, gbVec2 i, gbVec2 n) +{ + gbVec2 b = n; + gb_vec2_muleq(&b, 2.0f*gb_vec2_dot(n, i)); + gb_vec2_sub(d, i, b); +} + +void +gb_vec3_reflect(gbVec3 *d, gbVec3 i, gbVec3 n) +{ + gbVec3 b = n; + gb_vec3_muleq(&b, 2.0f*gb_vec3_dot(n, i)); + gb_vec3_sub(d, i, b); +} + +void +gb_vec2_refract(gbVec2 *d, gbVec2 i, gbVec2 n, float eta) +{ + gbVec2 a, b; + float dv, k; + + dv = gb_vec2_dot(n, i); + k = 1.0f - eta*eta * (1.0f - dv*dv); + gb_vec2_mul(&a, i, eta); + gb_vec2_mul(&b, n, eta*dv*gb_sqrt(k)); + gb_vec2_sub(d, a, b); + gb_vec2_muleq(d, (float)(k >= 0.0f)); +} + +void +gb_vec3_refract(gbVec3 *d, gbVec3 i, gbVec3 n, float eta) +{ + gbVec3 a, b; + float dv, k; + + dv = gb_vec3_dot(n, i); + k = 1.0f - eta*eta * (1.0f - dv*dv); + gb_vec3_mul(&a, i, eta); + gb_vec3_mul(&b, n, eta*dv*gb_sqrt(k)); + gb_vec3_sub(d, a, b); + gb_vec3_muleq(d, (float)(k >= 0.0f)); +} + + + + + +float gb_vec2_aspect_ratio(gbVec2 v) { return (v.y < 0.0001f) ? 0.0f : v.x/v.y; } + + + + + +void gb_mat2_transpose(gbMat2 *m) { gb_float22_transpose(gb_float22_m(m)); } +void gb_mat2_identity(gbMat2 *m) { gb_float22_identity(gb_float22_m(m)); } +void gb_mat2_mul(gbMat2 *out, gbMat2 *m1, gbMat2 *m2) { gb_float22_mul(gb_float22_m(out), gb_float22_m(m1), gb_float22_m(m2)); } + +void +gb_float22_identity(float m[2][2]) { m[0][0] = 1; m[0][1] = 0; m[1][0] = 0; m[1][1] = 1; } -void -mat2_mul_vec2(Vec2 *out, Mat2 *m, Vec2 in) -{ - float22_mul_vec2(out, float22_m(m), in); -} +void gb_mat2_mul_vec2(gbVec2 *out, gbMat2 *m, gbVec2 in) { gb_float22_mul_vec2(out, gb_float22_m(m), in); } -Mat2 *mat2_v(Vec2 m[2]) { return (Mat2 *)m; } -Mat2 *mat2_f(float m[2][2]) { return (Mat2 *)m; } +gbMat2 *gb_mat2_v(gbVec2 m[2]) { return (gbMat2 *)m; } +gbMat2 *gb_mat2_f(float m[2][2]) { return (gbMat2 *)m; } -Float2 *float22_m(Mat2 *m) { return (Float2 *)m; } -Float2 *float22_v(Vec2 m[2]) { return (Float2 *)m; } -Float2 *float22_4(float m[4]) { return (Float2 *)m; } +gbFloat2 *gb_float22_m(gbMat2 *m) { return (gbFloat2 *)m; } +gbFloat2 *gb_float22_v(gbVec2 m[2]) { return (gbFloat2 *)m; } +gbFloat2 *gb_float22_4(float m[4]) { return (gbFloat2 *)m; } void -float22_transpose(float (*vec)[2]) +gb_float22_transpose(float (*vec)[2]) { int i, j; for (j = 0; j < 2; j++) { @@ -443,7 +1136,7 @@ float22_transpose(float (*vec)[2]) } void -float22_mul(float (*out)[2], float (*mat1)[2], float (*mat2)[2]) +gb_float22_mul(float (*out)[2], float (*mat1)[2], float (*mat2)[2]) { int i, j; float temp1[2][2], temp2[2][2]; @@ -458,10 +1151,10 @@ float22_mul(float (*out)[2], float (*mat1)[2], float (*mat2)[2]) } void -float22_mul_vec2(Vec2 *out, float m[2][2], Vec2 v) +gb_float22_mul_vec2(gbVec2 *out, float m[2][2], gbVec2 v) { - out->x = m[0][0] * v.x + m[0][1] * v.y; - out->y = m[1][0] * v.x + m[1][1] * v.y; + out->x = m[0][0]*v.x + m[0][1]*v.y; + out->y = m[1][0]*v.x + m[1][1]*v.y; } @@ -469,33 +1162,29 @@ float22_mul_vec2(Vec2 *out, float m[2][2], Vec2 v) -void mat3_transpose(Mat3 *m) { float33_transpose(float33_m(m)); } -void mat3_identity(Mat3 *m) { float33_identity(float33_m(m)); } -void mat3_mul(Mat3 *out, Mat3 *m1, Mat3 *m2) { float33_mul(float33_m(out), float33_m(m1), float33_m(m2)); } +void gb_mat3_transpose(gbMat3 *m) { gb_float33_transpose(gb_float33_m(m)); } +void gb_mat3_identity(gbMat3 *m) { gb_float33_identity(gb_float33_m(m)); } +void gb_mat3_mul(gbMat3 *out, gbMat3 *m1, gbMat3 *m2) { gb_float33_mul(gb_float33_m(out), gb_float33_m(m1), gb_float33_m(m2)); } void -float33_identity(float m[3][3]) +gb_float33_identity(float m[3][3]) { m[0][0] = 1; m[0][1] = 0; m[0][2] = 0; m[1][0] = 0; m[1][1] = 1; m[1][2] = 0; m[2][0] = 0; m[2][1] = 0; m[2][2] = 1; } -void -mat3_mul_vec3(Vec3 *out, Mat3 *m, Vec3 in) -{ - float33_mul_vec3(out, float33_m(m), in); -} +void gb_mat3_mul_vec3(gbVec3 *out, gbMat3 *m, gbVec3 in) { gb_float33_mul_vec3(out, gb_float33_m(m), in); } -Mat3 *mat3_v(Vec3 m[3]) { return (Mat3 *)m; } -Mat3 *mat3_f(float m[3][3]) { return (Mat3 *)m; } +gbMat3 *gb_mat3_v(gbVec3 m[3]) { return (gbMat3 *)m; } +gbMat3 *gb_mat3_f(float m[3][3]) { return (gbMat3 *)m; } -Float3 *float33_m(Mat3 *m) { return (Float3 *)m; } -Float3 *float33_v(Vec3 m[3]) { return (Float3 *)m; } -Float3 *float33_16(float m[9]) { return (Float3 *)m; } +gbFloat3 *gb_float33_m(gbMat3 *m) { return (gbFloat3 *)m; } +gbFloat3 *gb_float33_v(gbVec3 m[3]) { return (gbFloat3 *)m; } +gbFloat3 *gb_float33_16(float m[9]) { return (gbFloat3 *)m; } void -float33_transpose(float (*vec)[3]) +gb_float33_transpose(float (*vec)[3]) { int i, j; for (j = 0; j < 3; j++) { @@ -508,7 +1197,7 @@ float33_transpose(float (*vec)[3]) } void -float33_mul(float (*out)[3], float (*mat1)[3], float (*mat2)[3]) +gb_float33_mul(float (*out)[3], float (*mat1)[3], float (*mat2)[3]) { int i, j; float temp1[3][3], temp2[3][3]; @@ -524,11 +1213,11 @@ float33_mul(float (*out)[3], float (*mat1)[3], float (*mat2)[3]) } void -float33_mul_vec3(Vec3 *out, float m[3][3], Vec3 v) +gb_float33_mul_vec3(gbVec3 *out, float m[3][3], gbVec3 v) { - out->x = m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z; - out->y = m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z; - out->z = m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z; + out->x = m[0][0]*v.x + m[0][1]*v.y + m[0][2]*v.z; + out->y = m[1][0]*v.x + m[1][1]*v.y + m[1][2]*v.z; + out->z = m[2][0]*v.x + m[2][1]*v.y + m[2][2]*v.z; } @@ -539,12 +1228,12 @@ float33_mul_vec3(Vec3 *out, float m[3][3], Vec3 v) -void mat4_transpose(Mat4 *m) { float44_transpose(float44_m(m)); } -void mat4_identity(Mat4 *m) { float44_identity(float44_m(m)); } -void mat4_mul(Mat4 *out, Mat4 *m1, Mat4 *m2) { float44_mul(float44_m(out), float44_m(m1), float44_m(m2)); } +void gb_mat4_transpose(gbMat4 *m) { gb_float44_transpose(gb_float44_m(m)); } +void gb_mat4_identity(gbMat4 *m) { gb_float44_identity(gb_float44_m(m)); } +void gb_mat4_mul(gbMat4 *out, gbMat4 *m1, gbMat4 *m2) { gb_float44_mul(gb_float44_m(out), gb_float44_m(m1), gb_float44_m(m2)); } void -float44_identity(float m[4][4]) +gb_float44_identity(float m[4][4]) { m[0][0] = 1; m[0][1] = 0; m[0][2] = 0; m[0][3] = 0; m[1][0] = 0; m[1][1] = 1; m[1][2] = 0; m[1][3] = 0; @@ -553,20 +1242,20 @@ float44_identity(float m[4][4]) } void -mat4_mul_vec4(Vec4 *out, Mat4 *m, Vec4 in) +gb_mat4_mul_vec4(gbVec4 *out, gbMat4 *m, gbVec4 in) { - float44_mul_vec4(out, float44_m(m), in); + gb_float44_mul_vec4(out, gb_float44_m(m), in); } -Mat4 *mat4_v(Vec4 m[4]) { return (Mat4 *)m; } -Mat4 *mat4_f(float m[4][4]) { return (Mat4 *)m; } +gbMat4 *gb_mat4_v(gbVec4 m[4]) { return (gbMat4 *)m; } +gbMat4 *gb_mat4_f(float m[4][4]) { return (gbMat4 *)m; } -Float4 *float44_m(Mat4 *m) { return (Float4 *)m; } -Float4 *float44_v(Vec4 m[4]) { return (Float4 *)m; } -Float4 *float44_16(float m[16]) { return (Float4 *)m; } +gbFloat4 *gb_float44_m(gbMat4 *m) { return (gbFloat4 *)m; } +gbFloat4 *gb_float44_v(gbVec4 m[4]) { return (gbFloat4 *)m; } +gbFloat4 *gb_float44_16(float m[16]) { return (gbFloat4 *)m; } void -float44_transpose(float (*vec)[4]) +gb_float44_transpose(float (*vec)[4]) { int i, j; for (j = 0; j < 4; j++) { @@ -579,7 +1268,7 @@ float44_transpose(float (*vec)[4]) } void -float44_mul(float (*out)[4], float (*mat1)[4], float (*mat2)[4]) +gb_float44_mul(float (*out)[4], float (*mat1)[4], float (*mat2)[4]) { int i, j; float temp1[4][4], temp2[4][4]; @@ -596,44 +1285,81 @@ float44_mul(float (*out)[4], float (*mat1)[4], float (*mat2)[4]) } void -float44_mul_vec4(Vec4 *out, float m[4][4], Vec4 v) +gb_float44_mul_vec4(gbVec4 *out, float m[4][4], gbVec4 v) { - out->x = m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z + m[0][3] * v.w; - out->y = m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z + m[1][3] * v.w; - out->z = m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z + m[2][3] * v.w; - out->w = m[3][0] * v.x + m[3][1] * v.y + m[3][2] * v.z + m[3][3] * v.w; + out->x = m[0][0]*v.x + m[0][1]*v.y + m[0][2]*v.z + m[0][3]*v.w; + out->y = m[1][0]*v.x + m[1][1]*v.y + m[1][2]*v.z + m[1][3]*v.w; + out->z = m[2][0]*v.x + m[2][1]*v.y + m[2][2]*v.z + m[2][3]*v.w; + out->w = m[3][0]*v.x + m[3][1]*v.y + m[3][2]*v.z + m[3][3]*v.w; } +void +gb_mat4_translate(gbMat4 *out, gbVec3 v) +{ + gb_mat4_identity(out); + out->col[3].xyz = v; + out->col[3].w = 1; +} -float lerp(float a, float b, float t) { return a*(1.0f-t) + b*t; } -float smooth_step(float a, float b, float t) { float x = (t - a)/(b - a); return x*x*(3.0f - 2.0f*x); } -float smoother_step(float a, float b, float t) { float x = (t - a)/(b - a); return x*x*x*(x*(6.0f*x - 15.0f) + 10.0f); } +void +gb_mat4_rotate(gbMat4 *out, gbVec3 v, float angle_radians) +{ + float c, s; + gbVec3 axis, t; + gbFloat4 *rot; -#define VEC_LERPN(N, d, a, b, t) Vec##N db; vec##N##_mul(&db, b, t); vec##N##_add(d, a, db) -void vec2_lerp(Vec2 *d, Vec2 a, Vec2 b, float t) { VEC_LERPN(2, d, a, b, t); } -void vec3_lerp(Vec3 *d, Vec3 a, Vec3 b, float t) { VEC_LERPN(3, d, a, b, t); } -void vec4_lerp(Vec4 *d, Vec4 a, Vec4 b, float t) { VEC_LERPN(4, d, a, b, t); } + c = gb_cos(angle_radians); + s = gb_sin(angle_radians); + gb_vec3_norm(&axis, v); + gb_vec3_mul(&t, axis, 1.0f-c); + gb_mat4_identity(out); + rot = gb_float44_m(out); + rot[0][0] = c + t.x*axis.x; + rot[0][1] = 0 + t.x*axis.y + s*axis.z; + rot[0][2] = 0 + t.x*axis.z - s*axis.y; + rot[0][3] = 0; + rot[1][0] = 0 + t.y*axis.x - s*axis.z; + rot[1][1] = c + t.y*axis.y; + rot[1][2] = 0 + t.y*axis.z + s*axis.x; + rot[1][3] = 0; -float to_radians(float degrees) { return degrees * MATH_TAU / 360.0f; } -float to_degrees(float radians) { return radians * 360.0f / MATH_TAU; } - - + rot[2][0] = 0 + t.z*axis.x + s*axis.y; + rot[2][1] = 0 + t.z*axis.y - s*axis.x; + rot[2][2] = c + t.z*axis.z; + rot[2][3] = 0; +} +void +gb_mat4_scale(gbMat4 *out, gbVec3 v) +{ + gb_mat4_identity(out); + out->e[0] = v.x; + out->e[5] = v.y; + out->e[10] = v.z; +} +void +gb_mat4_scalef(gbMat4 *out, float s) +{ + gb_mat4_identity(out); + out->e[0] = s; + out->e[5] = s; + out->e[10] = s; +} void -mat4_ortho2d(Mat4 *out, float left, float right, float bottom, float top) +gb_mat4_ortho2d(gbMat4 *out, float left, float right, float bottom, float top) { - Float4 *m; - mat4_identity(out); - m = float44_m(out); + gbFloat4 *m; + gb_mat4_identity(out); + m = gb_float44_m(out); m[0][0] = 2.0f / (right - left); m[1][1] = 2.0f / (top - bottom); @@ -643,37 +1369,480 @@ mat4_ortho2d(Mat4 *out, float left, float right, float bottom, float top) } void -mat4_ortho3d(Mat4 *out, float left, float right, float bottom, float top, float z_near, float z_far) +gb_mat4_ortho3d(gbMat4 *out, float left, float right, float bottom, float top, float z_near, float z_far) { - Float4 *m; - mat4_identity(out); - m = float44_m(out); + gbFloat4 *m; + gb_mat4_identity(out); + m = gb_float44_m(out); m[0][0] = +2.0f / (right - left); m[1][1] = +2.0f / (top - bottom); m[2][2] = -2.0f / (z_far - z_near); - m[3][0] = -(right + left) / (right - left); - m[3][1] = -(top + bottom) / (top - bottom); + m[3][0] = -(right + left) / (right - left); + m[3][1] = -(top + bottom) / (top - bottom); m[3][2] = -(z_far + z_near) / (z_far - z_near); } -#if defined(__x86_64__) || defined(__ppc64__) +void +gb_mat4_perspective(gbMat4 *out, float fovy, float aspect, float z_near, float z_far) +{ + float tan_half_fovy = gb_tan(0.5f * fovy); - u64 - hash_murmur64(void const *key, size_t num_bytes) + gbFloat4 *m = gb_float44_m(out); + memset(m, 0, sizeof(gbMat4)); + + m[0][0] = 1.0f / (aspect*tan_half_fovy); + m[1][1] = 1.0f / (tan_half_fovy); + m[2][2] = -(z_far + z_near) / (z_far - z_near); + m[2][3] = -1.0f; + m[3][2] = -2.0f*z_far*z_near / (z_far - z_near); +} + +void +gb_mat4_infinite_perspective(gbMat4 *out, float fovy, float aspect, float z_near) +{ + float range = gb_tan(0.5f * fovy) * z_near; + float left = -range * aspect; + float right = range * aspect; + float bottom = -range; + float top = range; + + gbFloat4 *m = gb_float44_m(out); + memset(m, 0, sizeof(gbMat4)); + + m[0][0] = (2.0f*z_near) / (right - left); + m[1][1] = (2.0f*z_near) / (top - bottom); + m[2][2] = -1.0f; + m[2][3] = -1.0f; + m[3][2] = -2.0f*z_near; +} + +void +gb_mat4_look_at(gbMat4 *out, gbVec3 eye, gbVec3 centre, gbVec3 up) +{ + gbVec3 f, s, u; + gbFloat4 *m; + + gb_vec3_sub(&f, centre, eye); + gb_vec3_norm(&f, f); + + gb_vec3_cross(&s, f, up); + gb_vec3_norm(&s, s); + + gb_vec3_cross(&u, s, f); + + gb_mat4_identity(out); + m = gb_float44_m(out); + + m[0][0] = +s.x; + m[1][0] = +s.y; + m[2][0] = +s.z; + + m[0][1] = +u.x; + m[1][1] = +u.y; + m[2][1] = +u.z; + + m[0][2] = -f.x; + m[1][2] = -f.y; + m[2][2] = -f.z; + + m[3][0] = gb_vec3_dot(s, eye); + m[3][1] = gb_vec3_dot(u, eye); + m[3][2] = gb_vec3_dot(f, eye); +} + + + + + + + + + + + + +gbQuat gb_quat(float x, float y, float z, float w) { gbQuat q = {x, y, z, w}; return q; } +gbQuat gb_quatv(float e[4]) { gbQuat q = {e[0], e[1], e[2], e[3]}; return q; } + +gbQuat +gb_quat_axis_angle(gbVec3 axis, float angle_radians) +{ + gbQuat q; + gb_vec3_norm(&q.xyz, axis); + gb_vec3_muleq(&q.xyz, gb_sin(0.5f*angle_radians)); + q.w = gb_cos(0.5f*angle_radians); + return q; +} + +gbQuat +gb_quat_euler_angles(float pitch, float yaw, float roll) +{ + // TODO(bill): Do without multiplication, i.e. make it faster + gbQuat q, p, y, r; + p = gb_quat_axis_angle(gb_vec3(1, 0, 0), pitch); + y = gb_quat_axis_angle(gb_vec3(0, 1, 0), yaw); + r = gb_quat_axis_angle(gb_vec3(0, 0, 1), roll); + + gb_quat_mul(&q, y, p); + gb_quat_muleq(&q, r); + + return q; +} + +gbQuat gb_quat_identity(void) { gbQuat q = {0, 0, 0, 1}; return q; } + + +void gb_quat_add(gbQuat *d, gbQuat q0, gbQuat q1) { gb_vec4_add(&d->xyzw, q0.xyzw, q1.xyzw); } +void gb_quat_sub(gbQuat *d, gbQuat q0, gbQuat q1) { gb_vec4_sub(&d->xyzw, q0.xyzw, q1.xyzw); } + +void +gb_quat_mul(gbQuat *d, gbQuat q0, gbQuat q1) +{ + d->x = q0.w * q1.x + q0.x * q1.w + q0.y * q1.z - q0.z * q1.y; + d->y = q0.w * q1.y - q0.x * q1.z + q0.y * q1.w + q0.z * q1.x; + d->z = q0.w * q1.z + q0.x * q1.y - q0.y * q1.x + q0.z * q1.w; + d->w = q0.w * q1.w - q0.x * q1.x - q0.y * q1.y - q0.z * q1.z; +} + +void gb_quat_div(gbQuat *d, gbQuat q0, gbQuat q1){ gbQuat iq1; gb_quat_inverse(&iq1, q1); gb_quat_mul(d, q0, iq1); } + +void gb_quat_mulf(gbQuat *d, gbQuat q0, float s) { gb_vec4_mul(&d->xyzw, q0.xyzw, s); } +void gb_quat_divf(gbQuat *d, gbQuat q0, float s) { gb_vec4_div(&d->xyzw, q0.xyzw, s); } + + +void gb_quat_addeq(gbQuat *d, gbQuat q) { gb_vec4_addeq(&d->xyzw, q.xyzw); } +void gb_quat_subeq(gbQuat *d, gbQuat q) { gb_vec4_subeq(&d->xyzw, q.xyzw); } +void gb_quat_muleq(gbQuat *d, gbQuat q) { gb_quat_mul(d, *d, q); } +void gb_quat_diveq(gbQuat *d, gbQuat q) { gb_quat_div(d, *d, q); } + + +void gb_quat_muleqf(gbQuat *d, float s) { gb_vec4_muleq(&d->xyzw, s); } +void gb_quat_diveqf(gbQuat *d, float s) { gb_vec4_diveq(&d->xyzw, s); } + +float gb_quat_dot(gbQuat q0, gbQuat q1) { float r = gb_vec3_dot(q0.xyz, q1.xyz) + q0.w*q1.w; return r; } +float gb_quat_mag(gbQuat q) { float r = gb_sqrt(gb_quat_dot(q, q)); return r; } + +void gb_quat_norm(gbQuat *d, gbQuat q) { gb_quat_divf(d, q, gb_quat_mag(q)); } +void gb_quat_conj(gbQuat *d, gbQuat q) { d->xyz = gb_vec3(-q.x, -q.y, -q.z); d->w = q.w; } +void gb_quat_inverse(gbQuat *d, gbQuat q) { gb_quat_conj(d, q); gb_quat_diveqf(d, gb_quat_dot(q, q)); } + + +void +gb_quat_axis(gbVec3 *axis, gbQuat q) +{ + gbQuat n; gb_quat_norm(&n, q); + gb_vec3_div(axis, n.xyz, gb_sin(gb_arccos(q.w))); +} + +float +gb_quat_angle(gbQuat q) +{ + float mag = gb_quat_mag(q); + float c = q.w * (1.0f/mag); + float angle = 2.0f*gb_arccos(c); + return angle; +} + + +float gb_quat_roll(gbQuat q) { return gb_arctan2(2.0f*q.x*q.y + q.z*q.w, q.x*q.x + q.w*q.w - q.y*q.y - q.z*q.z); } +float gb_quat_pitch(gbQuat q) { return gb_arctan2(2.0f*q.y*q.z + q.w*q.x, q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z); } +float gb_quat_yaw(gbQuat q) { return gb_arcsin(-2.0f*(q.x*q.z - q.w*q.y)); } + +void +gb_quat_rotate_vec3(gbVec3 *d, gbQuat q, gbVec3 v) +{ + // gbVec3 t = 2.0f * cross(q.xyz, v); + // *d = q.w*t + v + cross(q.xyz, t); + gbVec3 t, p; + gb_vec3_cross(&t, q.xyz, v); + gb_vec3_muleq(&t, 2.0f); + + gb_vec3_cross(&p, q.xyz, t); + + gb_vec3_mul(d, t, q.w); + gb_vec3_addeq(d, v); + gb_vec3_addeq(d, p); +} + + +void +gb_mat4_from_quat(gbMat4 *out, gbQuat q) +{ + gbFloat4 *m; + gbQuat a; + float xx, yy, zz, + xy, xz, yz, + wx, wy, wz; + + gb_quat_norm(&a, q); + xx = a.x*a.x; yy = a.y*a.y; zz = a.z*a.z; + xy = a.x*a.y; xz = a.x*a.z; yz = a.y*a.z; + wx = a.w*a.x; wy = a.w*a.y; wz = a.w*a.z; + + gb_mat4_identity(out); + m = gb_float44_m(out); + + m[0][0] = 1.0f - 2.0f*(yy + zz); + m[0][1] = 2.0f*(xy + wz); + m[0][2] = 2.0f*(xz - wy); + + m[1][0] = 2.0f*(xy - wz); + m[1][1] = 1.0f - 2.0f*(xx + zz); + m[1][2] = 2.0f*(yz + wx); + + m[2][0] = 2.0f*(xz + wy); + m[2][1] = 2.0f*(yz - wx); + m[2][2] = 1.0f - 2.0f*(xx + yy); +} + +void +gb_quat_from_mat4(gbQuat *out, gbMat4 *mat) +{ + gbFloat4 *m; + float four_x_squared_minus_1, four_y_squared_minus_1, + four_z_squared_minus_1, four_w_squared_minus_1, + four_biggest_squared_minus_1; + int biggest_index = 0; + float biggest_value, mult; + + m = gb_float44_m(mat); + + four_x_squared_minus_1 = m[0][0] - m[1][1] - m[2][2]; + four_y_squared_minus_1 = m[1][1] - m[0][0] - m[2][2]; + four_z_squared_minus_1 = m[2][2] - m[0][0] - m[1][1]; + four_w_squared_minus_1 = m[0][0] + m[1][1] + m[2][2]; + + four_biggest_squared_minus_1 = four_w_squared_minus_1; + if (four_x_squared_minus_1 > four_biggest_squared_minus_1) { + four_biggest_squared_minus_1 = four_x_squared_minus_1; + biggest_index = 1; + } + if (four_y_squared_minus_1 > four_biggest_squared_minus_1) { + four_biggest_squared_minus_1 = four_y_squared_minus_1; + biggest_index = 2; + } + if (four_z_squared_minus_1 > four_biggest_squared_minus_1) { + four_biggest_squared_minus_1 = four_z_squared_minus_1; + biggest_index = 3; + } + + biggest_value = gb_sqrt(four_biggest_squared_minus_1 + 1.0f) * 0.5f; + mult = 0.25f / biggest_value; + + switch (biggest_index) { + case 0: + out->w = biggest_value; + out->x = (m[1][2] - m[2][1]) * mult; + out->y = (m[2][0] - m[0][2]) * mult; + out->z = (m[0][1] - m[1][0]) * mult; + break; + case 1: + out->w = (m[1][2] - m[2][1]) * mult; + out->x = biggest_value; + out->y = (m[0][1] + m[1][0]) * mult; + out->z = (m[2][0] + m[0][2]) * mult; + break; + case 2: + out->w = (m[2][0] - m[0][2]) * mult; + out->x = (m[0][1] + m[1][0]) * mult; + out->y = biggest_value; + out->z = (m[1][2] + m[2][1]) * mult; + break; + case 3: + out->w = (m[0][1] - m[1][0]) * mult; + out->x = (m[2][0] + m[0][2]) * mult; + out->y = (m[1][2] + m[2][1]) * mult; + out->z = biggest_value; + break; + default: + // NOTE(bill): This shouldn't fucking happen!!! + break; + } + +} + + + + + + +float gb_lerp(float a, float b, float t) { return a*(1.0f-t) + b*t; } +float gb_smooth_step(float a, float b, float t) { float x = (t - a)/(b - a); return x*x*(3.0f - 2.0f*x); } +float gb_smoother_step(float a, float b, float t) { float x = (t - a)/(b - a); return x*x*x*(x*(6.0f*x - 15.0f) + 10.0f); } + + +#define GB_VEC_LERPN(N, d, a, b, t) \ + gbVec##N db; \ + gb_vec##N##_sub(&db, b, a); \ + gb_vec##N##_muleq(&db, t); \ + gb_vec##N##_add(d, a, db) +void gb_vec2_lerp(gbVec2 *d, gbVec2 a, gbVec2 b, float t) { GB_VEC_LERPN(2, d, a, b, t); } +void gb_vec3_lerp(gbVec3 *d, gbVec3 a, gbVec3 b, float t) { GB_VEC_LERPN(3, d, a, b, t); } +void gb_vec4_lerp(gbVec4 *d, gbVec4 a, gbVec4 b, float t) { GB_VEC_LERPN(4, d, a, b, t); } + +#undef GB_VEC_LERPN + +void gb_quat_lerp(gbQuat *d, gbQuat a, gbQuat b, float t) { gb_vec4_lerp(&d->xyzw, a.xyzw, b.xyzw, t); } +void gb_quat_nlerp(gbQuat *d, gbQuat a, gbQuat b, float t) { gb_quat_lerp(d, a, b, t); gb_quat_norm(d, *d); } + +void +gb_quat_slerp(gbQuat *d, gbQuat a, gbQuat b, float t) +{ + gbQuat x, y, z; + float cos_theta, angle; + float s1, s0, is; + + z = b; + cos_theta = gb_quat_dot(a, b); + + if (cos_theta < 0.0f) { + z = gb_quat(-b.x, -b.y, -b.z, -b.w); + cos_theta = -cos_theta; + } + + if (cos_theta > 1.0f) { + // NOTE(bill): Use lerp not nlerp as it's not a real angle or they are not normalized + gb_quat_lerp(d, a, b, t); + } + + angle = gb_arccos(cos_theta); + + s1 = gb_sin(1.0f - t*angle); + s0 = gb_sin(t*angle); + is = 1.0f/gb_sin(angle); + gb_quat_mulf(&x, z, s1); + gb_quat_mulf(&y, z, s0); + gb_quat_add(d, x, y); + gb_quat_muleqf(d, is); +} + +void +gb_quat_slerp_approx(gbQuat *d, gbQuat a, gbQuat b, float t) +{ + // NOTE(bill): Derived by taylor expanding the geometric interpolation equation + // Even works okay for nearly anti-parallel versors!!! + // NOTE(bill): Extra interations cannot be used as they require angle^4 which is not worth it to approximate + float tp = t + (1.0f - gb_quat_dot(a, b))/3.0f * t*(-2.0f*t*t + 3.0f*t - 1.0f); + gb_quat_nlerp(d, a, b, tp); +} + +void +gb_quat_nquad(gbQuat *d, gbQuat p, gbQuat a, gbQuat b, gbQuat q, float t) +{ + gbQuat x, y; + gb_quat_nlerp(&x, p, q, t); + gb_quat_nlerp(&y, a, b, t); + gb_quat_nlerp(d, x, y, 2.0f*t*(1.0f-t)); +} + +void +gb_quat_squad(gbQuat *d, gbQuat p, gbQuat a, gbQuat b, gbQuat q, float t) +{ + gbQuat x, y; + gb_quat_slerp(&x, p, q, t); + gb_quat_slerp(&y, a, b, t); + gb_quat_slerp(d, x, y, 2.0f*t*(1.0f-t)); +} + +void +gb_quat_squad_approx(gbQuat *d, gbQuat p, gbQuat a, gbQuat b, gbQuat q, float t) +{ + gbQuat x, y; + gb_quat_slerp_approx(&x, p, q, t); + gb_quat_slerp_approx(&y, a, b, t); + gb_quat_slerp_approx(d, x, y, 2.0f*t*(1.0f-t)); +} + + + + + + +gbRect2 +gb_rect2(gbVec2 pos, gbVec2 dim) +{ + gbRect2 r; + r.pos = pos; + r.dim = dim; + return r; +} + +gbRect3 +gb_rect3(gbVec3 pos, gbVec3 dim) +{ + gbRect3 r; + r.pos = pos; + r.dim = dim; + return r; +} + +int +gb_rect2_contains(gbRect2 a, float x, float y) +{ + float min_x = gb_min(a.pos.x, a.pos.x+a.dim.x); + float max_x = gb_max(a.pos.x, a.pos.x+a.dim.x); + float min_y = gb_min(a.pos.y, a.pos.y+a.dim.y); + float max_y = gb_max(a.pos.y, a.pos.y+a.dim.y); + int result = (x >= min_x) & (x < max_x) & (y >= min_y) & (y < max_y); + return result; +} + +int gb_rect2_contains_vec2(gbRect2 a, gbVec2 p) { return gb_rect2_contains(a, p.x, p.y); } + +int +gb_rect2_intersects(gbRect2 a, gbRect2 b) +{ + gbRect2 r = {0}; + return gb_rect2_intersection_result(a, b, &r); +} + +int +gb_rect2_intersection_result(gbRect2 a, gbRect2 b, gbRect2 *intersection) +{ + float a_min_x = gb_min(a.pos.x, a.pos.x+a.dim.x); + float a_max_x = gb_max(a.pos.x, a.pos.x+a.dim.x); + float a_min_y = gb_min(a.pos.y, a.pos.y+a.dim.y); + float a_max_y = gb_max(a.pos.y, a.pos.y+a.dim.y); + + float b_min_x = gb_min(b.pos.x, b.pos.x+b.dim.x); + float b_max_x = gb_max(b.pos.x, b.pos.x+b.dim.x); + float b_min_y = gb_min(b.pos.y, b.pos.y+b.dim.y); + float b_max_y = gb_max(b.pos.y, b.pos.y+b.dim.y); + + float x0 = gb_max(a_min_x, b_min_x); + float y0 = gb_max(a_min_y, b_min_y); + float x1 = gb_min(a_max_x, b_max_x); + float y1 = gb_min(a_max_y, b_max_y); + + if ((x0 < x1) && (y0 < y1)) { + gbRect2 r = gb_rect2(gb_vec2(x0, y0), gb_vec2(x1-x0, y1-y0)); + *intersection = r; + return 1; + } else { + gbRect2 r = {0}; + *intersection = r; + return 0; + } +} + + +#if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__) + gb_math_u64 + gb_hash_murmur64(void const *key, size_t num_bytes, gb_math_u64 seed) { - u64 const m = 0xc6a4a7935bd1e995ULL; - int const r = 47; + gb_math_u64 const m = 0xc6a4a7935bd1e995ULL; + gb_math_u64 const r = 47; - u64 h = MURMUR64_SEED ^ (num_bytes * m); + gb_math_u64 h = seed ^ (num_bytes * m); - u64 *data = (u64 *)(key); - u64 *end = data + (num_bytes / 8); - u8 *data2; + gb_math_u64 *data = (gb_math_u64 *)(key); + gb_math_u64 *end = data + (num_bytes / 8); + unsigned char *data2; while (data != end) { - u64 k = *data++; + gb_math_u64 k = *data++; k *= m; k ^= k >> r; k *= m; @@ -681,16 +1850,16 @@ mat4_ortho3d(Mat4 *out, float left, float right, float bottom, float top, float h *= m; } - data2 = (u8 *)data; + data2 = (unsigned char *)data; switch (num_bytes & 7) { - case 7: h ^= (u64)data2[6] << 48; - case 6: h ^= (u64)data2[5] << 40; - case 5: h ^= (u64)data2[4] << 32; - case 4: h ^= (u64)data2[3] << 24; - case 3: h ^= (u64)data2[2] << 16; - case 2: h ^= (u64)data2[1] << 8; - case 1: h ^= (u64)data2[0]; + case 7: h ^= (gb_math_u64)data2[6] << 48; + case 6: h ^= (gb_math_u64)data2[5] << 40; + case 5: h ^= (gb_math_u64)data2[4] << 32; + case 4: h ^= (gb_math_u64)data2[3] << 24; + case 3: h ^= (gb_math_u64)data2[2] << 16; + case 2: h ^= (gb_math_u64)data2[1] << 8; + case 1: h ^= (gb_math_u64)data2[0]; h *= m; }; @@ -702,14 +1871,14 @@ mat4_ortho3d(Mat4 *out, float left, float right, float bottom, float top, float } #else gb_math_u64 - hash_murmur64(void const *key, size_t num_bytes) + gb_hash_murmur64(void const *key, size_t num_bytes, gb_math_u64 seed) { gb_math_u32 const m = 0x5bd1e995; gb_math_u32 const r = 24; gb_math_u64 h = 0; - gb_math_u32 h1 = (gb_math_u32)MURMUR64_SEED ^ (gb_math_u32)num_bytes; - gb_math_u32 h2 = (gb_math_u32)((gb_math_u64)MURMUR64_SEED >> 32); + gb_math_u32 h1 = (gb_math_u32)seed ^ (gb_math_u32)num_bytes; + gb_math_u32 h2 = (gb_math_u32)((gb_math_u64)seed >> 32); gb_math_u32 *data = (gb_math_u32 *)key; @@ -744,9 +1913,10 @@ mat4_ortho3d(Mat4 *out, float left, float right, float bottom, float top, float } switch (num_bytes) { - case 3: h2 ^= (gb_math_u32)((gb_math_u8 *)(data)[2]) << 16; - case 2: h2 ^= (gb_math_u32)((gb_math_u8 *)(data)[1]) << 8; - case 1: h2 ^= (gb_math_u32)((gb_math_u8 *)(data)[0]) << 0; + gb_math_u32 a, b, c; + case 3: c = data[2]; h2 ^= c << 16; + case 2: b = data[1]; h2 ^= b << 8; + case 1: a = data[0]; h2 ^= a << 0; h2 *= m; }; @@ -768,21 +1938,17 @@ mat4_ortho3d(Mat4 *out, float left, float right, float bottom, float top, float // TODO(bill): Make better random number generators float -random_range_float(float min_inc, float max_inc) +gb_random_range_float(float min_inc, float max_inc) { - static int random_value = 0xdeadbeef; // Random Value - float result; - random_value = random_value * 2147001325 + 715136305; // BCPL generator - result = *(float *)&random_value; // bits - result /= 4294967295.0f; - result *= (max_inc - min_inc); + int int_result = gb_random_range_int(0, INT_MAX-1); // Prevent integer overflow + float result = int_result/(float)(INT_MAX-1); + result *= max_inc - min_inc; result += min_inc; - return result; } int -random_range_int(int min_inc, int max_inc) +gb_random_range_int(int min_inc, int max_inc) { static int random_value = 0xdeadbeef; // Random Value int diff, result; diff --git a/gb_math.hpp b/gb_math.hpp deleted file mode 100644 index b81bde9..0000000 --- a/gb_math.hpp +++ /dev/null @@ -1,3882 +0,0 @@ -// gb_math.hpp - v0.03a - public domain C++11 math library - no warranty implied; use at your own risk -// A C++11 math library geared towards game development -// This is meant to be used the gb.hpp library but it doesn't have to be - -/* -Version History: - 0.04 - Change const position convention - 0.03a - Remove templated clamp - 0.03 - Remove templated min/max/clamp - 0.02b - Typo fixes - 0.02a - Better `static` keywords - 0.02 - More Angle Units and templated min/max/clamp/lerp - 0.01 - Initial Version - -LICENSE - This software is in the public domain. Where that dedication is not - recognized, you are granted a perpetual, irrevocable license to copy, - distribute, and modify this file as you see fit. - -WARNING - - This library is _slightly_ experimental and features may not work as expected. - - This also means that many functions are not documented. - - This library was developed in conjunction with `gb.hpp` - -CONTENTS: - - Common Macros - - Assert - - Types - - Vector(2,3,4) - - Complex - - Quaternion - - Matrix(2,3,4) - - Euler_Angles - - Transform - - Aabb - - Sphere - - Plane - - Operations - - Functions & Constants - - Type Functions - - Random -*/ - -#ifndef GB_MATH_INCLUDE_GB_MATH_HPP -#define GB_MATH_INCLUDE_GB_MATH_HPP - -#if !defined(__cplusplus) && __cplusplus >= 201103L - #error This library is only for C++11 and above -#endif - -// NOTE(bill): Because static means three different things in C/C++ -// Great Design(!) -#ifndef global_variable -#define global_variable static -#define internal_linkage static -#define local_persist static -#endif - -#if defined(_MSC_VER) - #define _ALLOW_KEYWORD_MACROS - - #ifndef alignof // Needed for MSVC 2013 'cause Microsoft "loves" standards - #define alignof(x) __alignof(x) - #endif -#endif - - -//////////////////////////////// -/// /// -/// System OS /// -/// /// -//////////////////////////////// -#if defined(_WIN32) || defined(_WIN64) - #ifndef GB_SYSTEM_WINDOWS - #define GB_SYSTEM_WINDOWS 1 - #endif -#elif defined(__APPLE__) && defined(__MACH__) - #ifndef GB_SYSTEM_OSX - #define GB_SYSTEM_OSX 1 - #endif -#elif defined(__unix__) - #ifndef GB_SYSTEM_UNIX - #define GB_SYSTEM_UNIX 1 - #endif - - #if defined(__linux__) - #ifndef GB_SYSTEM_LINUX - #define GB_SYSTEM_LINUX 1 - #endif - #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) - #ifndef GB_SYSTEM_FREEBSD - #define GB_SYSTEM_FREEBSD 1 - #endif - #else - #error This UNIX operating system is not supported by gb.hpp - #endif -#else - #error This operating system is not supported by gb.hpp -#endif - - -#if defined(_MSC_VER) - // Microsoft Visual Studio - #define GB_COMPILER_MSVC 1 -#elif defined(__clang__) - // Clang - #define GB_COMPILER_CLANG 1 -#elif defined(__GNUC__) || defined(__GNUG__) && !(defined(__clang__) || defined(__INTEL_COMPILER)) - // GNU GCC/G++ Compiler - #define GB_COMPILER_GNU_GCC 1 -#elif defined(__INTEL_COMPILER) - // Intel C++ Compiler - #define GB_COMPILER_INTEL 1 -#endif - -//////////////////////////////// -/// /// -/// Environment Bit Size /// -/// /// -//////////////////////////////// -#if defined(_WIN32) || defined(_WIN64) - #if defined(_WIN64) - #ifndef GB_ARCH_64_BIT - #define GB_ARCH_64_BIT 1 - #endif - #else - #ifndef GB_ARCH_32_BIT - #define GB_ARCH_32_BIT 1 - #endif - #endif -#endif - -// TODO(bill): Check if this KEPLER_ENVIRONMENT works on clang -#if defined(__GNUC__) - #if defined(__x86_64__) || defined(__ppc64__) - #ifndef GB_ARCH_64_BIT - #define GB_ARCH_64_BIT 1 - #endif - #else - #ifndef GB_ARCH_32_BIT - #define GB_ARCH_32_BIT 1 - #endif - #endif -#endif - - - -#ifndef GB_EDIAN_ORDER -#define GB_EDIAN_ORDER - #define GB_IS_BIG_EDIAN (!*(unsigned char*)&(unsigned short){1}) - #define GB_IS_LITTLE_EDIAN (!GB_IS_BIG_EDIAN) -#endif - -#ifndef GB_IS_POWER_OF_TWO -#define GB_IS_POWER_OF_TWO(x) ((x) != 0) && !((x) & ((x) - 1)) -#endif - - -#if !defined(GB_HAS_NO_CONSTEXPR) - #if defined(_GNUC_VER) && _GNUC_VER < 406 // Less than gcc 4.06 - #define GB_HAS_NO_CONSTEXPR 1 - #elif defined(_MSC_VER) && _MSC_VER < 1900 // Less than Visual Studio 2015/MSVC++ 14.0 - #define GB_HAS_NO_CONSTEXPR 1 - #elif !defined(__GXX_EXPERIMENTAL_CXX0X__) && __cplusplus < 201103L - #define GB_HAS_NO_CONSTEXPR 1 - #endif -#endif - -#if defined(GB_HAS_NO_CONSTEXPR) - #define GB_CONSTEXPR -#else - #define GB_CONSTEXPR constexpr -#endif - -#ifndef GB_FORCE_INLINE - #if defined(_MSC_VER) - #define GB_FORCE_INLINE __forceinline - #else - #define GB_FORCE_INLINE __attribute__ ((__always_inline__)) - #endif -#endif - -#if defined(GB_SYSTEM_WINDOWS) - #define NOMINMAX 1 - #define VC_EXTRALEAN 1 - #define WIN32_EXTRA_LEAN 1 - #define WIN32_LEAN_AND_MEAN 1 - - #include - #include - - #undef NOMINMAX - #undef VC_EXTRALEAN - #undef WIN32_EXTRA_LEAN - #undef WIN32_LEAN_AND_MEAN -#else - // -#endif - - -#if !defined(GB_ASSERT) - #if !defined(NDEBUG) - #define GB_ASSERT(x, ...) ((void)(::gb__assert_handler((x), #x, __FILE__, __LINE__, ##__VA_ARGS__))) - - /// Helper function used as a better alternative to assert which allows for - /// optional printf style error messages - extern "C" inline void - gb__assert_handler(bool condition, const char* condition_str, - const char* filename, size_t line, - const char* error_text = nullptr, ...) - { - if (condition) - return; - - fprintf(stderr, "ASSERT! %s(%lu): %s", filename, line, condition_str); - if (error_text) - { - fprintf(stderr, " - "); - - va_list args; - va_start(args, error_text); - vfprintf(stderr, error_text, args); - va_end(args); - } - fprintf(stderr, "\n"); - // TODO(bill): Get a better way to abort - *(int*)0 = 0; - } - - #else - #define GB_ASSERT(x, ...) ((void)sizeof(x)) - #endif -#endif - -#if !defined(__GB_NAMESPACE_PREFIX) && !defined(GB_NO_GB_NAMESPACE) - #define __GB_NAMESPACE_PREFIX gb -#else - #define __GB_NAMESPACE_PREFIX -#endif - -#if defined(GB_NO_GB_NAMESPACE) - #define __GB_NAMESPACE_START - #define __GB_NAMESPACE_END -#else - #define __GB_NAMESPACE_START namespace __GB_NAMESPACE_PREFIX { - #define __GB_NAMESPACE_END } // namespace __GB_NAMESPACE_PREFIX -#endif - - -#if !defined(GB_BASIC_WITHOUT_NAMESPACE) -__GB_NAMESPACE_START -#endif // GB_BASIC_WITHOUT_NAMESPACE - -//////////////////////////////// -/// /// -/// Types /// -/// /// -//////////////////////////////// - - -#ifndef GB_BASIC_TYPES -#define GB_BASIC_TYPES - #if defined(_MSC_VER) - using u8 = unsigned __int8; - using s8 = signed __int8; - using u16 = unsigned __int16; - using s16 = signed __int16; - using u32 = unsigned __int32; - using s32 = signed __int32; - using u64 = unsigned __int64; - using s64 = signed __int64; - #else - using u8 = unsigned char; - using s8 = signed char; - using u16 = unsigned short; - using s16 = signed short; - using u32 = unsigned int; - using s32 = signed int; - using u64 = unsigned long long; - using s64 = signed long long; - #endif - - static_assert( sizeof(u8) == 1, "u8 is not 8 bits"); - static_assert(sizeof(u16) == 2, "u16 is not 16 bits"); - static_assert(sizeof(u32) == 4, "u32 is not 32 bits"); - static_assert(sizeof(u64) == 8, "u64 is not 64 bits"); - - using f32 = float; - using f64 = double; - - #if defined(GB_B8_AS_BOOL) - using b8 = bool; - #else - using b8 = s8; - #endif - using b32 = s32; - - // NOTE(bill): (std::)size_t is not used not because it's a bad concept but on - // the platforms that I will be using: - // sizeof(size_t) == sizeof(usize) == sizeof(ssize) - // NOTE(bill): This also allows for a signed version of size_t which is similar - // to ptrdiff_t - // NOTE(bill): If (u)intptr is a better fit, please use that. - // NOTE(bill): Also, I hate the `_t` suffix - #if defined(GB_ARCH_64_BIT) - using ssize = s64; - using usize = u64; - #elif defined(GB_ARCH_32_BIT) - using usize = s32; - using usize = u32; - #else - #error Unknown architecture bit size - #endif - - static_assert(sizeof(usize) == sizeof(size_t), - "`usize` is not the same size as `size_t`"); - static_assert(sizeof(ssize) == sizeof(usize), - "`ssize` is not the same size as `usize`"); - - using intptr = intptr_t; - using uintptr = uintptr_t; - - using ptrdiff = ptrdiff_t; - -#endif - -#if !defined(GB_U8_MIN) - #define GB_U8_MIN 0u - #define GB_U8_MAX 0xffu - #define GB_S8_MIN (-0x7f - 1) - #define GB_S8_MAX 0x7f - - #define GB_U16_MIN 0u - #define GB_U16_MAX 0xffffu - #define GB_S16_MIN (-0x7fff - 1) - #define GB_S16_MAX 0x7fff - - #define GB_U32_MIN 0u - #define GB_U32_MAX 0xffffffffu - #define GB_S32_MIN (-0x7fffffff - 1) - #define GB_S32_MAX 0x7fffffff - - #define GB_U64_MIN 0ull - #define GB_U64_MAX 0xffffffffffffffffull - #define GB_S64_MIN (-0x7fffffffffffffffll - 1) - #define GB_S64_MAX 0x7fffffffffffffffll -#endif - -#if defined(GB_ARCH_64_BIT) && !defined(GB_USIZE_MIX) - #define GB_USIZE_MIX GB_U64_MIN - #define GB_USIZE_MAX GB_U64_MAX - - #define GB_SSIZE_MIX GB_S64_MIN - #define GB_SSIZE_MAX GB_S64_MAX -#elif defined(GB_ARCH_32_BIT) && !defined(GB_USIZE_MIX) - #define GB_USIZE_MIX GB_U32_MIN - #define GB_USIZE_MAX GB_U32_MAX - - #define GB_SSIZE_MIX GB_S32_MIN - #define GB_SSIZE_MAX GB_S32_MAX -#endif - -#if defined(GB_BASIC_WITHOUT_NAMESPACE) && !defined(U8_MIN) - #define U8_MIN 0u - #define U8_MAX 0xffu - #define S8_MIN (-0x7f - 1) - #define S8_MAX 0x7f - - #define U16_MIN 0u - #define U16_MAX 0xffffu - #define S16_MIN (-0x7fff - 1) - #define S16_MAX 0x7fff - - #define U32_MIN 0u - #define U32_MAX 0xffffffffu - #define S32_MIN (-0x7fffffff - 1) - #define S32_MAX 0x7fffffff - - #define U64_MIN 0ull - #define U64_MAX 0xffffffffffffffffull - #define S64_MIN (-0x7fffffffffffffffll - 1) - #define S64_MAX 0x7fffffffffffffffll - - #if defined(GB_ARCH_64_BIT) && !defined(GB_USIZE_MIX) - #define USIZE_MIX U64_MIN - #define USIZE_MAX U64_MAX - - #define SSIZE_MIX S64_MIN - #define SSIZE_MAX S64_MAX - #elif defined(GB_ARCH_32_BIT) && !defined(GB_USIZE_MIX) - #define USIZE_MIX U32_MIN - #define USIZE_MAX U32_MAX - - #define SSIZE_MIX S32_MIN - #define SSIZE_MAX S32_MAX - #endif -#endif - - - -#if !defined(GB_BASIC_WITHOUT_NAMESPACE) -__GB_NAMESPACE_END -#endif // GB_BASIC_WITHOUT_NAMESPACE - -__GB_NAMESPACE_START -#ifndef GB_SPECIAL_CASTS -#define GB_SPECIAL_CASTS - // IMPORTANT NOTE(bill): Very similar to doing `*(T*)(&u)` but easier/clearer to write - // however, it can be dangerous if sizeof(T) > sizeof(U) e.g. unintialized memory, undefined behavior - // *(T*)(&u) ~~ pseudo_cast(u) - template - inline T - pseudo_cast(U const& u) - { - return reinterpret_cast(u); - } - - // NOTE(bill): Very similar to doing `*(T*)(&u)` - template - inline Dest - bit_cast(Source const& source) - { - static_assert(sizeof(Dest) <= sizeof(Source), - "bit_cast(Source const&) - sizeof(Dest) <= sizeof(Source)"); - Dest dest; - ::memcpy(&dest, &source, sizeof(Dest)); - return dest; - } -#endif -// FORENOTE(bill): There used to be a magic_cast that was equivalent to -// a C-style cast but I removed it as I could not get it work as intented -// for everything using only C++ style casts - -#if !defined(GB_CASTS_WITHOUT_NAMESPACE) -__GB_NAMESPACE_END -#endif // GB_CASTS_WITHOUT_NAMESPACE - -__GB_NAMESPACE_START -//////////////////////////////// -/// /// -/// Math Types /// -/// /// -//////////////////////////////// - -// TODO(bill): Should the math part be a separate library? - -struct Vector2 -{ - union - { - struct { f32 x, y; }; - f32 data[2]; - }; - - inline f32 operator[](usize index) const { return data[index]; } - inline f32& operator[](usize index) { return data[index]; } -}; - -struct Vector3 -{ - union - { - struct { f32 x, y, z; }; - struct { f32 r, g, b; }; - Vector2 xy; - f32 data[3]; - }; - - inline f32 operator[](usize index) const { return data[index]; } - inline f32& operator[](usize index) { return data[index]; } -}; - -struct Vector4 -{ - union - { - struct { f32 x, y, z, w; }; - struct { f32 r, g, b, a; }; - struct { Vector2 xy, zw; }; - Vector3 xyz; - Vector3 rgb; - f32 data[4]; - }; - - inline f32 operator[](usize index) const { return data[index]; } - inline f32& operator[](usize index) { return data[index]; } -}; - -struct Complex -{ - union - { - struct { f32 x, y; }; - struct { f32 real, imag; }; - f32 data[2]; - }; - - inline f32 operator[](usize index) const { return data[index]; } - inline f32& operator[](usize index) { return data[index]; } -}; - -struct Quaternion -{ - union - { - struct { f32 x, y, z, w; }; - Vector3 xyz; - f32 data[4]; - }; - - inline f32 operator[](usize index) const { return data[index]; } - inline f32& operator[](usize index) { return data[index]; } -}; - -struct Matrix2 -{ - union - { - struct { Vector2 x, y; }; - Vector2 columns[2]; - f32 data[4]; - }; - - inline Vector2 operator[](usize index) const { return columns[index]; } - inline Vector2& operator[](usize index) { return columns[index]; } -}; - -struct Matrix3 -{ - union - { - struct { Vector3 x, y, z; }; - Vector3 columns[3]; - f32 data[9]; - }; - - inline Vector3 operator[](usize index) const { return columns[index]; } - inline Vector3& operator[](usize index) { return columns[index]; } -}; - -struct Matrix4 -{ - union - { - struct { Vector4 x, y, z, w; }; - Vector4 columns[4]; - f32 data[16]; - }; - - inline Vector4 operator[](usize index) const { return columns[index]; } - inline Vector4& operator[](usize index) { return columns[index]; } -}; - -struct Angle -{ - f32 radians; -}; - -struct Euler_Angles -{ - Angle pitch, yaw, roll; -}; - -struct Transform -{ - Vector3 position; - Quaternion orientation; - f32 scale; - // NOTE(bill): Scale is only f32 to make sizeof(Transform) == 32 bytes -}; - -struct Aabb -{ - Vector3 center; - Vector3 half_size; -}; - -struct Oobb -{ - Matrix4 transform; - Aabb aabb; -}; - -struct Sphere -{ - Vector3 center; - f32 radius; -}; - -struct Plane -{ - Vector3 normal; - f32 distance; // negative distance to origin -}; - -//////////////////////////////// -/// /// -/// Math Type Op Overloads /// -/// /// -//////////////////////////////// - -// Vector2 Operators -bool operator==(Vector2 a, Vector2 b); -bool operator!=(Vector2 a, Vector2 b); - -Vector2 operator+(Vector2 a); -Vector2 operator-(Vector2 a); - -Vector2 operator+(Vector2 a, Vector2 b); -Vector2 operator-(Vector2 a, Vector2 b); - -Vector2 operator*(Vector2 a, f32 scalar); -Vector2 operator*(f32 scalar, Vector2 a); - -Vector2 operator/(Vector2 a, f32 scalar); - -Vector2 operator*(Vector2 a, Vector2 b); // Hadamard Product -Vector2 operator/(Vector2 a, Vector2 b); // Hadamard Product - -Vector2& operator+=(Vector2& a, Vector2 b); -Vector2& operator-=(Vector2& a, Vector2 b); -Vector2& operator*=(Vector2& a, f32 scalar); -Vector2& operator/=(Vector2& a, f32 scalar); - -// Vector3 Operators -bool operator==(Vector3 a, Vector3 b); -bool operator!=(Vector3 a, Vector3 b); - -Vector3 operator+(Vector3 a); -Vector3 operator-(Vector3 a); - -Vector3 operator+(Vector3 a, Vector3 b); -Vector3 operator-(Vector3 a, Vector3 b); - -Vector3 operator*(Vector3 a, f32 scalar); -Vector3 operator*(f32 scalar, Vector3 a); - -Vector3 operator/(Vector3 a, f32 scalar); - -Vector3 operator*(Vector3 a, Vector3 b); // Hadamard Product -Vector3 operator/(Vector3 a, Vector3 b); // Hadamard Product - -Vector3& operator+=(Vector3& a, Vector3 b); -Vector3& operator-=(Vector3& a, Vector3 b); -Vector3& operator*=(Vector3& a, f32 scalar); -Vector3& operator/=(Vector3& a, f32 scalar); - -// Vector4 Operators -bool operator==(Vector4 a, Vector4 b); -bool operator!=(Vector4 a, Vector4 b); - -Vector4 operator+(Vector4 a); -Vector4 operator-(Vector4 a); - -Vector4 operator+(Vector4 a, Vector4 b); -Vector4 operator-(Vector4 a, Vector4 b); - -Vector4 operator*(Vector4 a, f32 scalar); -Vector4 operator*(f32 scalar, Vector4 a); - -Vector4 operator/(Vector4 a, f32 scalar); - -Vector4 operator*(Vector4 a, Vector4 b); // Hadamard Product -Vector4 operator/(Vector4 a, Vector4 b); // Hadamard Product - -Vector4& operator+=(Vector4& a, Vector4 b); -Vector4& operator-=(Vector4& a, Vector4 b); -Vector4& operator*=(Vector4& a, f32 scalar); -Vector4& operator/=(Vector4& a, f32 scalar); - -// Complex Operators -bool operator==(Complex a, Complex b); -bool operator!=(Complex a, Complex b); - -Complex operator+(Complex a); -Complex operator-(Complex a); - -Complex operator+(Complex a, Complex b); -Complex operator-(Complex a, Complex b); - -Complex operator*(Complex a, Complex b); -Complex operator*(Complex a, f32 s); -Complex operator*(f32 s, Complex a); - -Complex operator/(Complex a, f32 s); - -// Quaternion Operators -bool operator==(Quaternion a, Quaternion b); -bool operator!=(Quaternion a, Quaternion b); - -Quaternion operator+(Quaternion a); -Quaternion operator-(Quaternion a); - -Quaternion operator+(Quaternion a, Quaternion b); -Quaternion operator-(Quaternion a, Quaternion b); - -Quaternion operator*(Quaternion a, Quaternion b); -Quaternion operator*(Quaternion a, f32 s); -Quaternion operator*(f32 s, Quaternion a); - -Quaternion operator/(Quaternion a, f32 s); - -Vector3 operator*(Quaternion a, Vector3 v); // Rotate v by a - -// Matrix2 Operators -bool operator==(Matrix2 a, Matrix2 b); -bool operator!=(Matrix2 a, Matrix2 b); - -Matrix2 operator+(Matrix2 a); -Matrix2 operator-(Matrix2 a); - -Matrix2 operator+(Matrix2 a, Matrix2 b); -Matrix2 operator-(Matrix2 a, Matrix2 b); - -Matrix2 operator*(Matrix2 a, Matrix2 b); -Vector2 operator*(Matrix2 a, Vector2 v); -Matrix2 operator*(Matrix2 a, f32 scalar); -Matrix2 operator*(f32 scalar, Matrix2 a); - -Matrix2 operator/(Matrix2 a, f32 scalar); - -Matrix2& operator+=(Matrix2& a, Matrix2 b); -Matrix2& operator-=(Matrix2& a, Matrix2 b); -Matrix2& operator*=(Matrix2& a, Matrix2 b); - -// Matrix3 Operators -bool operator==(Matrix3 const& a, Matrix3 const& b); -bool operator!=(Matrix3 const& a, Matrix3 const& b); - -Matrix3 operator+(Matrix3 const& a); -Matrix3 operator-(Matrix3 const& a); - -Matrix3 operator+(Matrix3 const& a, Matrix3 const& b); -Matrix3 operator-(Matrix3 const& a, Matrix3 const& b); - -Matrix3 operator*(Matrix3 const& a, Matrix3 const& b); -Vector3 operator*(Matrix3 const& a, Vector3 v); -Matrix3 operator*(Matrix3 const& a, f32 scalar); -Matrix3 operator*(f32 scalar, Matrix3 const& a); - -Matrix3 operator/(Matrix3 const& a, f32 scalar); - -Matrix3& operator+=(Matrix3& a, Matrix3 const& b); -Matrix3& operator-=(Matrix3& a, Matrix3 const& b); -Matrix3& operator*=(Matrix3& a, Matrix3 const& b); - -// Matrix4 Operators -bool operator==(Matrix4 const& a, Matrix4 const& b); -bool operator!=(Matrix4 const& a, Matrix4 const& b); - -Matrix4 operator+(Matrix4 const& a); -Matrix4 operator-(Matrix4 const& a); - -Matrix4 operator+(Matrix4 const& a, Matrix4 const& b); -Matrix4 operator-(Matrix4 const& a, Matrix4 const& b); - -Matrix4 operator*(Matrix4 const& a, Matrix4 const& b); -Vector4 operator*(Matrix4 const& a, Vector4 v); -Matrix4 operator*(Matrix4 const& a, f32 scalar); -Matrix4 operator*(f32 scalar, Matrix4 const& a); - -Matrix4 operator/(Matrix4 const& a, f32 scalar); - -Matrix4& operator+=(Matrix4& a, Matrix4 const& b); -Matrix4& operator-=(Matrix4& a, Matrix4 const& b); -Matrix4& operator*=(Matrix4& a, Matrix4 const& b); - -// Angle Operators -bool operator==(Angle a, Angle b); -bool operator!=(Angle a, Angle b); - -Angle operator+(Angle a); -Angle operator-(Angle a); - -Angle operator+(Angle a, Angle b); -Angle operator-(Angle a, Angle b); - -Angle operator*(Angle a, f32 scalar); -Angle operator*(f32 scalar, Angle a); - -Angle operator/(Angle a, f32 scalar); - -f32 operator/(Angle a, Angle b); - -Angle& operator+=(Angle& a, Angle b); -Angle& operator-=(Angle& a, Angle b); -Angle& operator*=(Angle& a, f32 scalar); -Angle& operator/=(Angle& a, f32 scalar); - -// Transform Operators -// World = Parent * Local -Transform operator*(Transform const& ps, Transform const& ls); -Transform& operator*=(Transform& ps, Transform const& ls); -// Local = World / Parent -Transform operator/(Transform const& ws, Transform const& ps); -Transform& operator/=(Transform& ws, Transform const& ps); - -namespace angle -{ -Angle radians(f32 r); -Angle degrees(f32 d); -Angle turns(f32 t); -Angle grads(f32 g); -Angle gons(f32 g); - -f32 as_radians(Angle a); -f32 as_degrees(Angle a); -f32 as_turns(Angle a); -f32 as_grads(Angle a); -f32 as_gons(Angle a); -} // namespace angle - -////////////////////////////////// -/// /// -/// Math Functions & Constants /// -/// /// -////////////////////////////////// -extern Vector2 const VECTOR2_ZERO; -extern Vector3 const VECTOR3_ZERO; -extern Vector4 const VECTOR4_ZERO; -extern Complex const COMPLEX_ZERO; -extern Quaternion const QUATERNION_IDENTITY; -extern Matrix2 const MATRIX2_IDENTITY; -extern Matrix3 const MATRIX3_IDENTITY; -extern Matrix4 const MATRIX4_IDENTITY; -extern Euler_Angles const EULER_ANGLES_ZERO; -extern Transform const TRANSFORM_IDENTITY; - -namespace math -{ -extern f32 const ZERO; -extern f32 const ONE; -extern f32 const THIRD; -extern f32 const TWO_THIRDS; -extern f32 const E; -extern f32 const PI; -extern f32 const TAU; -extern f32 const SQRT_2; -extern f32 const SQRT_3; -extern f32 const SQRT_5; - -extern f32 const F32_PRECISION; - -// Power -f32 sqrt(f32 x); -f32 pow(f32 x, f32 y); -f32 cbrt(f32 x); -f32 fast_inv_sqrt(f32 x); - -// Trigonometric -f32 sin(Angle a); -f32 cos(Angle a); -f32 tan(Angle a); - -Angle arcsin(f32 x); -Angle arccos(f32 x); -Angle arctan(f32 x); -Angle arctan2(f32 y, f32 x); - -// Hyperbolic -f32 sinh(f32 x); -f32 cosh(f32 x); -f32 tanh(f32 x); - -f32 arsinh(f32 x); -f32 arcosh(f32 x); -f32 artanh(f32 x); - -// Rounding -f32 ceil(f32 x); -f32 floor(f32 x); -f32 mod(f32 x, f32 y); -f32 truncate(f32 x); -f32 round(f32 x); - -s32 sign(s32 x); -s64 sign(s64 x); -f32 sign(f32 x); - -// Other -f32 abs(f32 x); -s8 abs( s8 x); -s16 abs(s16 x); -s32 abs(s32 x); -s64 abs(s64 x); - -bool is_infinite(f32 x); -bool is_nan(f32 x); - -s32 kronecker_delta(s32 i, s32 j); -s64 kronecker_delta(s64 i, s64 j); -f32 kronecker_delta(f32 i, f32 j); - -// NOTE(bill): Just incase -#undef min -#undef max - -f32 min(f32 x, f32 y); -s32 min(s32 x, s32 y); -s64 min(s64 x, s64 y); - -f32 max(f32 x, f32 y); -s32 max(s32 x, s32 y); -s64 max(s64 x, s64 y); - -f32 clamp(f32 x, f32 min, f32 max); -s32 clamp(s32 x, s32 min, s32 max); -s64 clamp(s64 x, s64 min, s64 max); - -// TODO(bill): Should this be a template or just normal function overloading? -template -T lerp(T const& x, T const& y, f32 t); - -bool equals(f32 a, f32 b, f32 precision = F32_PRECISION); - -// Vector2 functions -f32 dot(Vector2 a, Vector2 b); -f32 cross(Vector2 a, Vector2 b); - -f32 magnitude(Vector2 a); -Vector2 normalize(Vector2 a); - -Vector2 hadamard(Vector2 a, Vector2 b); - -f32 aspect_ratio(Vector2 a); - -// Vector3 functions -f32 dot(Vector3 a, Vector3 b); -Vector3 cross(Vector3 a, Vector3 b); - -f32 magnitude(Vector3 a); -Vector3 normalize(Vector3 a); - -Vector3 hadamard(Vector3 a, Vector3 b); - -// Vector4 functions -f32 dot(Vector4 a, Vector4 b); - -f32 magnitude(Vector4 a); -Vector4 normalize(Vector4 a); - -Vector4 hadamard(Vector4 a, Vector4 b); - -// Complex functions -f32 dot(Complex a, Complex b); - -f32 magnitude(Complex a); -f32 norm(Complex a); -Complex normalize(Complex a); - -Complex conjugate(Complex a); -Complex inverse(Complex a); - -f32 complex_angle(Complex a); -inline f32 complex_argument(Complex a) { return complex_angle(a); } -Complex magnitude_angle(f32 magnitude, Angle a); -inline Complex complex_polar(f32 magnitude, Angle a) { return magnitude_angle(magnitude, a); } - -// Quaternion functions -f32 dot(Quaternion a, Quaternion b); -Quaternion cross(Quaternion a, Quaternion b); - -f32 magnitude(Quaternion a); -f32 norm(Quaternion a); -Quaternion normalize(Quaternion a); - -Quaternion conjugate(Quaternion a); -Quaternion inverse(Quaternion a); - -Angle quaternion_angle(Quaternion a); -Vector3 quaternion_axis(Quaternion a); -Quaternion axis_angle(Vector3 axis, Angle a); - -Angle quaternion_roll(Quaternion a); -Angle quaternion_pitch(Quaternion a); -Angle quaternion_yaw(Quaternion a); - -Euler_Angles quaternion_to_euler_angles(Quaternion a); -Quaternion euler_angles_to_quaternion(Euler_Angles const& e, - Vector3 x_axis = {1, 0, 0}, - Vector3 y_axis = {0, 1, 0}, - Vector3 z_axis = {0, 0, 1}); - -// Spherical Linear Interpolation -Quaternion slerp(Quaternion x, Quaternion y, f32 t); - -// Shoemake's Quaternion Curves -// Sqherical Cubic Interpolation -Quaternion squad(Quaternion p, - Quaternion a, - Quaternion b, - Quaternion q, - f32 t); -// Matrix2 functions -Matrix2 transpose(Matrix2 m); -f32 determinant(Matrix2 m); -Matrix2 inverse(Matrix2 m); -Matrix2 hadamard(Matrix2 a, const Matrix2&b); -Matrix4 matrix2_to_matrix4(Matrix2 m); - -// Matrix3 functions -Matrix3 transpose(Matrix3 const& m); -f32 determinant(Matrix3 const& m); -Matrix3 inverse(Matrix3 const& m); -Matrix3 hadamard(Matrix3 const& a, const Matrix3&b); -Matrix4 matrix3_to_matrix4(Matrix3 const& m); - -// Matrix4 functions -Matrix4 transpose(Matrix4 const& m); -f32 determinant(Matrix4 const& m); -Matrix4 inverse(Matrix4 const& m); -Matrix4 hadamard(Matrix4 const& a, const Matrix4&b); -bool is_affine(Matrix4 const& m); - -Matrix4 quaternion_to_matrix4(Quaternion a); -Quaternion matrix4_to_quaternion(Matrix4 const& m); - -Matrix4 translate(Vector3 v); -Matrix4 rotate(Vector3 v, Angle angle); -Matrix4 scale(Vector3 v); -Matrix4 ortho(f32 left, f32 right, f32 bottom, f32 top); -Matrix4 ortho(f32 left, f32 right, f32 bottom, f32 top, f32 z_near, f32 z_far); -Matrix4 perspective(Angle fovy, f32 aspect, f32 z_near, f32 z_far); -Matrix4 infinite_perspective(Angle fovy, f32 aspect, f32 z_near); - -Matrix4 -look_at_matrix4(Vector3 eye, Vector3 center, Vector3 up = {0, 1, 0}); - -Quaternion -look_at_quaternion(Vector3 eye, Vector3 center, Vector3 up = {0, 1, 0}); - -// Transform Functions -Vector3 transform_point(Transform const& transform, Vector3 point); -Transform inverse(Transform const& t); -Matrix4 transform_to_matrix4(Transform const& t); -} // namespace math - -namespace aabb -{ -Aabb calculate(void const* vertices, usize num_vertices, usize stride, usize offset); - -f32 surface_area(Aabb const& aabb); -f32 volume(Aabb const& aabb); - -Sphere to_sphere(Aabb const& aabb); - -bool contains(Aabb const& aabb, Vector3 point); -bool contains(Aabb const& a, Aabb const& b); -bool intersects(Aabb const& a, Aabb const& b); - -Aabb transform_affine(Aabb const& aabb, Matrix4 const& m); -} // namespace aabb - -namespace sphere -{ -Sphere calculate_min_bounding_sphere(void const* vertices, usize num_vertices, usize stride, usize offset, f32 step); -Sphere calculate_max_bounding_sphere(void const* vertices, usize num_vertices, usize stride, usize offset); - -f32 surface_area(Sphere s); -f32 volume(Sphere s); - -Aabb to_aabb(Sphere sphere); - -bool contains_point(Sphere s, Vector3 point); - -f32 ray_intersection(Vector3 from, Vector3 dir, Sphere s); -} // namespace sphere - -namespace plane -{ -f32 ray_intersection(Vector3 from, Vector3 dir, Plane p); - -bool intersection3(Plane p1, Plane p2, Plane p3, Vector3* ip); -} // namespace plane - - -#if !defined(GB_MATH_NO_RANDOM) - -namespace random -{ -struct Random // NOTE(bill): Mt19937_64 -{ - s64 seed; - u32 index; - s64 mt[312]; -}; - -Random make(s64 seed); - -void set_seed(Random* r, s64 seed); - -s64 next(Random* r); - -void next_from_device(void* buffer, u32 length_in_bytes); - -s32 next_s32(Random* r); -u32 next_u32(Random* r); -f32 next_f32(Random* r); -s64 next_s64(Random* r); -u64 next_u64(Random* r); -f64 next_f64(Random* r); - -s32 uniform_s32(Random* r, s32 min_inc, s32 max_inc); -u32 uniform_u32(Random* r, u32 min_inc, u32 max_inc); -f32 uniform_f32(Random* r, f32 min_inc, f32 max_inc); -s64 uniform_s64(Random* r, s64 min_inc, s64 max_inc); -u64 uniform_u64(Random* r, u64 min_inc, u64 max_inc); -f64 uniform_f64(Random* r, f64 min_inc, f64 max_inc); - - -// TODO(bill): Should these noise functions be in the `random` module? -f32 perlin_3d(f32 x, f32 y, f32 z, s32 x_wrap = 0, s32 y_wrap = 0, s32 z_wrap = 0); - -// TODO(bill): Implement simplex noise -// f32 simplex_2d_octave(f32 x, f32 y, f32 octaves, f32 persistence, f32 scale); -// f32 simplex_3d_octave(f32 x, f32 y, f32 z, f32 octaves, f32 persistence, f32 scale); -// f32 simplex_4d_octave(f32 x, f32 y, f32 z, f32 w, f32 octaves, f32 persistence, f32 scale); - -} // namespace random - -#endif - -namespace math -{ -template inline T lerp(T const& x, T const& y, f32 t) { return x + (y - x) * t; } -} // namespace math - -__GB_NAMESPACE_END - -#endif // GB_INCLUDE_GB_HPP - -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// So long and thanks for all the fish! -/// -/// -/// -/// -/// -//////////////////////////////// -/// /// -/// Implemenation /// -/// /// -//////////////////////////////// -#if defined(GB_MATH_IMPLEMENTATION) - -#include - -__GB_NAMESPACE_START - -//////////////////////////////// -/// /// -/// Math /// -/// /// -//////////////////////////////// - -Vector2 const VECTOR2_ZERO = Vector2{0, 0}; -Vector3 const VECTOR3_ZERO = Vector3{0, 0, 0}; -Vector4 const VECTOR4_ZERO = Vector4{0, 0, 0, 0}; -Complex const COMPLEX_ZERO = Complex{0, 0}; -Quaternion const QUATERNION_IDENTITY = Quaternion{0, 0, 0, 1}; -Matrix2 const MATRIX2_IDENTITY = Matrix2{1, 0, - 0, 1}; -Matrix3 const MATRIX3_IDENTITY = Matrix3{1, 0, 0, - 0, 1, 0, - 0, 0, 1}; -Matrix4 const MATRIX4_IDENTITY = Matrix4{1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1}; -Euler_Angles const EULER_ANGLES_ZERO = Euler_Angles{0, 0, 0}; -Transform const TRANSFORM_IDENTITY = Transform{VECTOR3_ZERO, QUATERNION_IDENTITY, 1}; - -//////////////////////////////// -/// Math Type Op Overloads /// -//////////////////////////////// - -// Vector2 Operators -inline bool -operator==(Vector2 a, Vector2 b) -{ - return (a.x == b.x) && (a.y == b.y); -} - -inline bool -operator!=(Vector2 a, Vector2 b) -{ - return !operator==(a, b); -} - -inline Vector2 -operator+(Vector2 a) -{ - return a; -} - -inline Vector2 -operator-(Vector2 a) -{ - return {-a.x, -a.y}; -} - -inline Vector2 -operator+(Vector2 a, Vector2 b) -{ - return {a.x + b.x, a.y + b.y}; -} - -inline Vector2 -operator-(Vector2 a, Vector2 b) -{ - return {a.x - b.x, a.y - b.y}; -} - -inline Vector2 -operator*(Vector2 a, f32 scalar) -{ - return {a.x * scalar, a.y * scalar}; -} - -inline Vector2 -operator*(f32 scalar, Vector2 a) -{ - return {a.x * scalar, a.y * scalar}; -} - -inline Vector2 -operator/(Vector2 a, f32 scalar) -{ - return {a.x / scalar, a.y / scalar}; -} - -inline Vector2 -operator*(Vector2 a, Vector2 b) // Hadamard Product -{ - return {a.x * b.x, a.y * b.y}; -} - -inline Vector2 -operator/(Vector2 a, Vector2 b) // Hadamard Product -{ - return {a.x / b.x, a.y / b.y}; -} - -inline Vector2& -operator+=(Vector2& a, Vector2 b) -{ - a.x += b.x; - a.y += b.y; - - return a; -} - -inline Vector2& -operator-=(Vector2& a, Vector2 b) -{ - a.x -= b.x; - a.y -= b.y; - - return a; -} - -inline Vector2& -operator*=(Vector2& a, f32 scalar) -{ - a.x *= scalar; - a.y *= scalar; - - return a; -} - -inline Vector2& -operator/=(Vector2& a, f32 scalar) -{ - a.x /= scalar; - a.y /= scalar; - - return a; -} - -// Vector3 Operators -inline bool -operator==(Vector3 a, Vector3 b) -{ - return (a.x == b.x) && (a.y == b.y) && (a.z == b.z); -} - -inline bool -operator!=(Vector3 a, Vector3 b) -{ - return !operator==(a, b); -} - -inline Vector3 -operator+(Vector3 a) -{ - return a; -} - -inline Vector3 -operator-(Vector3 a) -{ - return {-a.x, -a.y, -a.z}; -} - -inline Vector3 -operator+(Vector3 a, Vector3 b) -{ - return {a.x + b.x, a.y + b.y, a.z + b.z}; -} - -inline Vector3 -operator-(Vector3 a, Vector3 b) -{ - return {a.x - b.x, a.y - b.y, a.z - b.z}; -} - -inline Vector3 -operator*(Vector3 a, f32 scalar) -{ - return {a.x * scalar, a.y * scalar, a.z * scalar}; -} - -inline Vector3 -operator*(f32 scalar, Vector3 a) -{ - return {a.x * scalar, a.y * scalar, a.z * scalar}; -} - -inline Vector3 -operator/(Vector3 a, f32 scalar) -{ - return {a.x / scalar, a.y / scalar, a.z / scalar}; -} - -inline Vector3 -operator*(Vector3 a, Vector3 b) // Hadamard Product -{ - return {a.x * b.x, a.y * b.y, a.z * b.z}; -} - -inline Vector3 -operator/(Vector3 a, Vector3 b) // Hadamard Product -{ - return {a.x / b.x, a.y / b.y, a.z / b.z}; -} - -inline Vector3& -operator+=(Vector3& a, Vector3 b) -{ - a.x += b.x; - a.y += b.y; - a.z += b.z; - - return a; -} - -inline Vector3& -operator-=(Vector3& a, Vector3 b) -{ - a.x -= b.x; - a.y -= b.y; - a.z -= b.z; - - return a; -} - -inline Vector3& -operator*=(Vector3& a, f32 scalar) -{ - a.x *= scalar; - a.y *= scalar; - a.z *= scalar; - - return a; -} - -inline Vector3& -operator/=(Vector3& a, f32 scalar) -{ - a.x /= scalar; - a.y /= scalar; - a.z /= scalar; - - return a; -} - -// Vector4 Operators -inline bool -operator==(Vector4 a, Vector4 b) -{ - return (a.x == b.x) && (a.y == b.y) && (a.z == b.z) && (a.w == b.w); -} - -inline bool -operator!=(Vector4 a, Vector4 b) -{ - return !operator==(a, b); -} - -inline Vector4 -operator+(Vector4 a) -{ - return a; -} - -inline Vector4 -operator-(Vector4 a) -{ - return {-a.x, -a.y, -a.z, -a.w}; -} - -inline Vector4 -operator+(Vector4 a, Vector4 b) -{ - return {a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w}; -} - -inline Vector4 -operator-(Vector4 a, Vector4 b) -{ - return {a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w}; -} - -inline Vector4 -operator*(Vector4 a, f32 scalar) -{ - return {a.x * scalar, a.y * scalar, a.z * scalar, a.w * scalar}; -} - -inline Vector4 -operator*(f32 scalar, Vector4 a) -{ - return {a.x * scalar, a.y * scalar, a.z * scalar, a.w * scalar}; -} - -inline Vector4 -operator/(Vector4 a, f32 scalar) -{ - return {a.x / scalar, a.y / scalar, a.z / scalar, a.w / scalar}; -} - -inline Vector4 -operator*(Vector4 a, Vector4 b) // Hadamard Product -{ - return {a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w}; -} - -inline Vector4 -operator/(Vector4 a, Vector4 b) // Hadamard Product -{ - return {a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w}; -} - -inline Vector4& -operator+=(Vector4& a, Vector4 b) -{ - a.x += b.x; - a.y += b.y; - a.z += b.z; - a.w += b.w; - - return a; -} - -inline Vector4& -operator-=(Vector4& a, Vector4 b) -{ - a.x -= b.x; - a.y -= b.y; - a.z -= b.z; - a.w -= b.w; - - return a; -} - -inline Vector4& -operator*=(Vector4& a, f32 scalar) -{ - a.x *= scalar; - a.y *= scalar; - a.z *= scalar; - a.w *= scalar; - - return a; -} - -inline Vector4& -operator/=(Vector4& a, f32 scalar) -{ - a.x /= scalar; - a.y /= scalar; - a.z /= scalar; - a.w /= scalar; - - return a; -} - -// Complex Operators -inline bool -operator==(Complex a, Complex b) -{ - return (a.x == b.x) && (a.y == b.y); -} - -inline bool -operator!=(Complex a, Complex b) -{ - return !operator==(a, b); -} - -inline Complex -operator+(Complex a) -{ - return a; -} - -inline Complex -operator-(Complex a) -{ - return {-a.x, -a.y}; -} - -inline Complex -operator+(Complex a, Complex b) -{ - return {a.x + b.x, a.y + b.y}; -} - -inline Complex -operator-(Complex a, Complex b) -{ - return {a.x - b.x, a.y - b.y}; - -} - -inline Complex -operator*(Complex a, Complex b) -{ - Complex c = {}; - - c.x = a.x * b.x - a.y * b.y; - c.y = a.y * b.x - a.y * b.x; - - return c; -} - -inline Complex -operator*(Complex a, f32 s) -{ - return {a.x * s, a.y * s}; -} - -inline Complex -operator*(f32 s, Complex a) -{ - return {a.x * s, a.y * s}; -} - -inline Complex -operator/(Complex a, f32 s) -{ - return {a.x / s, a.y / s}; -} - -// Quaternion Operators -inline bool -operator==(Quaternion a, Quaternion b) -{ - return (a.x == b.x) && (a.y == b.y) && (a.z == b.z) && (a.w == b.w); -} - -inline bool -operator!=(Quaternion a, Quaternion b) -{ - return !operator==(a, b); -} - -inline Quaternion -operator+(Quaternion a) -{ - return {+a.x, +a.y, +a.z, +a.w}; -} - -inline Quaternion -operator-(Quaternion a) -{ - return {-a.x, -a.y, -a.z, -a.w}; -} - -inline Quaternion -operator+(Quaternion a, Quaternion b) -{ - return {a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w}; -} - -inline Quaternion -operator-(Quaternion a, Quaternion b) -{ - return {a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w}; - -} - -inline Quaternion -operator*(Quaternion a, Quaternion b) -{ - Quaternion q = {}; - - q.x = a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y; - q.y = a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x; - q.z = a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w; - q.w = a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z; - - return q; -} - -inline Quaternion -operator*(Quaternion a, f32 s) -{ - return {a.x * s, a.y * s, a.z * s, a.w * s}; -} - -inline Quaternion -operator*(f32 s, Quaternion a) -{ - return {a.x * s, a.y * s, a.z * s, a.w * s}; -} - -inline Quaternion -operator/(Quaternion a, f32 s) -{ - return {a.x / s, a.y / s, a.z / s, a.w / s}; -} - -inline Vector3 -operator*(Quaternion a, Vector3 v) // Rotate v by q -{ - // return (q * Quaternion{v.x, v.y, v.z, 0} * math::conjugate(q)).xyz; // More Expensive - const Vector3 t = 2.0f * math::cross(a.xyz, v); - return (v + a.w * t + math::cross(a.xyz, t)); -} - -// Matrix2 Operators -inline bool -operator==(Matrix2 a, Matrix2 b) -{ - for (usize i = 0; i < 4; i++) - { - if (a[i] != b[i]) - return false; - } - return true; -} - -inline bool -operator!=(Matrix2 a, Matrix2 b) -{ - return !operator==(a, b); -} - -inline Matrix2 -operator+(Matrix2 a) -{ - return a; -} - -inline Matrix2 -operator-(Matrix2 a) -{ - return {-a.x, -a.y}; -} - -inline Matrix2 -operator+(Matrix2 a, Matrix2 b) -{ - Matrix2 mat; - mat[0] = a[0] + b[0]; - mat[1] = a[1] + b[1]; - return mat; -} - -inline Matrix2 -operator-(Matrix2 a, Matrix2 b) -{ - Matrix2 mat; - mat[0] = a[0] - b[0]; - mat[1] = a[1] - b[1]; - return mat; -} - -inline Matrix2 -operator*(Matrix2 a, Matrix2 b) -{ - Matrix2 result; - result[0] = a[0] * b[0][0] + a[1] * b[0][1]; - result[1] = a[0] * b[1][0] + a[1] * b[1][1]; - return result; -} - -inline Vector2 -operator*(Matrix2 a, Vector2 v) -{ - return Vector2{a[0][0] * v.x + a[1][0] * v.y, - a[0][1] * v.x + a[1][1] * v.y}; -} - -inline Matrix2 -operator*(Matrix2 a, f32 scalar) -{ - Matrix2 mat; - mat[0] = a[0] * scalar; - mat[1] = a[1] * scalar; - return mat; -} - -inline Matrix2 -operator*(f32 scalar, Matrix2 a) -{ - Matrix2 mat; - mat[0] = a[0] * scalar; - mat[1] = a[1] * scalar; - return mat; -} - -inline Matrix2 -operator/(Matrix2 a, f32 scalar) -{ - Matrix2 mat; - mat[0] = a[0] / scalar; - mat[1] = a[1] / scalar; - return mat; -} - -inline Matrix2& -operator+=(Matrix2& a, Matrix2 b) -{ - return (a = a + b); -} - -inline Matrix2& -operator-=(Matrix2& a, Matrix2 b) -{ - return (a = a - b); -} - -inline Matrix2& -operator*=(Matrix2& a, Matrix2 b) -{ - return (a = a * b); -} - - -// Matrix3 Operators -inline bool -operator==(Matrix3 const& a, Matrix3 const& b) -{ - for (usize i = 0; i < 3; i++) - { - if (a[i] != b[i]) - return false; - } - return true; -} - -inline bool -operator!=(Matrix3 const& a, Matrix3 const& b) -{ - return !operator==(a, b); -} - -inline Matrix3 -operator+(Matrix3 const& a) -{ - return a; -} - -inline Matrix3 -operator-(Matrix3 const& a) -{ - return {-a.x, -a.y, -a.z}; -} - -inline Matrix3 -operator+(Matrix3 const& a, Matrix3 const& b) -{ - Matrix3 mat; - mat[0] = a[0] + b[0]; - mat[1] = a[1] + b[1]; - mat[2] = a[2] + b[2]; - return mat; -} - -inline Matrix3 -operator-(Matrix3 const& a, Matrix3 const& b) -{ - Matrix3 mat; - mat[0] = a[0] - b[0]; - mat[1] = a[1] - b[1]; - mat[2] = a[2] - b[2]; - return mat; -} - -inline Matrix3 -operator*(Matrix3 const& a, Matrix3 const& b) -{ - Matrix3 result; - result[0] = a[0] * b[0][0] + a[1] * b[0][1] + a[2] * b[0][2]; - result[1] = a[0] * b[1][0] + a[1] * b[1][1] + a[2] * b[1][2]; - result[2] = a[0] * b[2][0] + a[1] * b[2][1] + a[2] * b[2][2]; - return result; -} - -inline Vector3 -operator*(Matrix3 const& a, Vector3 v) -{ - return Vector3{a[0][0] * v.x + a[1][0] * v.y + a[2][0] * v.z, - a[0][1] * v.x + a[1][1] * v.y + a[2][1] * v.z, - a[0][2] * v.x + a[1][2] * v.y + a[2][2] * v.z}; -} - -inline Matrix3 -operator*(Matrix3 const& a, f32 scalar) -{ - Matrix3 mat; - mat[0] = a[0] * scalar; - mat[1] = a[1] * scalar; - mat[2] = a[2] * scalar; - return mat; -} - -inline Matrix3 -operator*(f32 scalar, Matrix3 const& a) -{ - Matrix3 mat; - mat[0] = a[0] * scalar; - mat[1] = a[1] * scalar; - mat[2] = a[2] * scalar; - return mat; -} - -inline Matrix3 -operator/(Matrix3 const& a, f32 scalar) -{ - Matrix3 mat; - mat[0] = a[0] / scalar; - mat[1] = a[1] / scalar; - mat[2] = a[2] / scalar; - return mat; -} - -inline Matrix3& -operator+=(Matrix3& a, Matrix3 const& b) -{ - return (a = a + b); -} - -inline Matrix3& -operator-=(Matrix3& a, Matrix3 const& b) -{ - return (a = a - b); -} - -inline Matrix3& -operator*=(Matrix3& a, Matrix3 const& b) -{ - return (a = a * b); -} - - -// Matrix4 Operators -inline bool -operator==(Matrix4 const& a, Matrix4 const& b) -{ - for (usize i = 0; i < 4; i++) - { - if (a[i] != b[i]) - return false; - } - return true; -} - -inline bool -operator!=(Matrix4 const& a, Matrix4 const& b) -{ - return !operator==(a, b); -} - -inline Matrix4 -operator+(Matrix4 const& a) -{ - return a; -} - -inline Matrix4 -operator-(Matrix4 const& a) -{ - return {-a.x, -a.y, -a.z, -a.w}; -} - -inline Matrix4 -operator+(Matrix4 const& a, Matrix4 const& b) -{ - Matrix4 mat; - mat[0] = a[0] + b[0]; - mat[1] = a[1] + b[1]; - mat[2] = a[2] + b[2]; - mat[3] = a[3] + b[3]; - return mat; -} - -inline Matrix4 -operator-(Matrix4 const& a, Matrix4 const& b) -{ - Matrix4 mat; - mat[0] = a[0] - b[0]; - mat[1] = a[1] - b[1]; - mat[2] = a[2] - b[2]; - mat[3] = a[3] - b[3]; - return mat; -} - -inline Matrix4 -operator*(Matrix4 const& a, Matrix4 const& b) -{ - Matrix4 result; - result[0] = a[0] * b[0][0] + a[1] * b[0][1] + a[2] * b[0][2] + a[3] * b[0][3]; - result[1] = a[0] * b[1][0] + a[1] * b[1][1] + a[2] * b[1][2] + a[3] * b[1][3]; - result[2] = a[0] * b[2][0] + a[1] * b[2][1] + a[2] * b[2][2] + a[3] * b[2][3]; - result[3] = a[0] * b[3][0] + a[1] * b[3][1] + a[2] * b[3][2] + a[3] * b[3][3]; - return result; -} - -inline Vector4 -operator*(Matrix4 const& a, Vector4 v) -{ - return Vector4{a[0][0] * v.x + a[1][0] * v.y + a[2][0] * v.z + a[3][0] * v.w, - a[0][1] * v.x + a[1][1] * v.y + a[2][1] * v.z + a[3][1] * v.w, - a[0][2] * v.x + a[1][2] * v.y + a[2][2] * v.z + a[3][2] * v.w, - a[0][3] * v.x + a[1][3] * v.y + a[2][3] * v.z + a[3][3] * v.w}; -} - -inline Matrix4 -operator*(Matrix4 const& a, f32 scalar) -{ - Matrix4 mat; - mat[0] = a[0] * scalar; - mat[1] = a[1] * scalar; - mat[2] = a[2] * scalar; - mat[3] = a[3] * scalar; - return mat; -} - -inline Matrix4 -operator*(f32 scalar, Matrix4 const& a) -{ - Matrix4 mat; - mat[0] = a[0] * scalar; - mat[1] = a[1] * scalar; - mat[2] = a[2] * scalar; - mat[3] = a[3] * scalar; - return mat; -} - -inline Matrix4 -operator/(Matrix4 const& a, f32 scalar) -{ - Matrix4 mat; - mat[0] = a[0] / scalar; - mat[1] = a[1] / scalar; - mat[2] = a[2] / scalar; - mat[3] = a[3] / scalar; - return mat; -} - -inline Matrix4& -operator+=(Matrix4& a, Matrix4 const& b) -{ - return (a = a + b); -} - -inline Matrix4& -operator-=(Matrix4& a, Matrix4 const& b) -{ - return (a = a - b); -} - -inline Matrix4& -operator*=(Matrix4& a, Matrix4 const& b) -{ - return (a = a * b); -} - -// Angle Operators -inline bool -operator==(Angle a, Angle b) -{ - return a.radians == b.radians; -} - -inline bool -operator!=(Angle a, Angle b) -{ - return !operator==(a, b); -} - -inline Angle -operator+(Angle a) -{ - return {+a.radians}; -} - -inline Angle -operator-(Angle a) -{ - return {-a.radians}; -} - -inline Angle -operator+(Angle a, Angle b) -{ - return {a.radians + b.radians}; -} - -inline Angle -operator-(Angle a, Angle b) -{ - return {a.radians - b.radians}; -} - -inline Angle -operator*(Angle a, f32 scalar) -{ - return {a.radians * scalar}; -} - -inline Angle -operator*(f32 scalar, Angle a) -{ - return {a.radians * scalar}; -} - -inline Angle -operator/(Angle a, f32 scalar) -{ - return {a.radians / scalar}; -} - -inline f32 -operator/(Angle a, Angle b) -{ - return a.radians / b.radians; -} - -inline Angle& -operator+=(Angle& a, Angle b) -{ - return (a = a + b); -} - -inline Angle& -operator-=(Angle& a, Angle b) -{ - return (a = a - b); -} - -inline Angle& -operator*=(Angle& a, f32 scalar) -{ - return (a = a * scalar); -} - -inline Angle& -operator/=(Angle& a, f32 scalar) -{ - return (a = a / scalar); -} - - -// Transform Operators -// World = Parent * Local -Transform -operator*(Transform const& ps, Transform const& ls) -{ - Transform ws; - - ws.position = ps.position + ps.orientation * (ps.scale * ls.position); - ws.orientation = ps.orientation * ls.orientation; - // ws.scale = ps.scale * (ps.orientation * ls.scale); // Vector3 scale - ws.scale = ps.scale * ls.scale; - - return ws; -} - -inline Transform& -operator*=(Transform& ps, Transform const& ls) -{ - return (ps = ps * ls); -} - -// Local = World / Parent -Transform -operator/(Transform const& ws, Transform const& ps) -{ - Transform ls; - - const Quaternion ps_conjugate = math::conjugate(ps.orientation); - - ls.position = (ps_conjugate * (ws.position - ps.position)) / ps.scale; - ls.orientation = ps_conjugate * ws.orientation; - // ls.scale = ps_conjugate * (ws.scale / ps.scale); // Vector3 scale - ls.scale = ws.scale / ps.scale; - - return ls; -} - -inline Transform& -operator/=(Transform& ws, Transform const& ps) -{ - return (ws = ws / ps); -} - - -namespace angle -{ -inline Angle radians(f32 r) { return {r}; } -inline Angle degrees(f32 d) { return {d * math::TAU / 360.0f}; } -inline Angle turns(f32 t) { return {t * math::TAU}; } -inline Angle grads(f32 g) { return {g * math::TAU / 400.0f}; } -inline Angle gons(f32 g) { return {g * math::TAU / 400.0f}; } - -inline f32 as_radians(Angle a) { return a.radians; } -inline f32 as_degrees(Angle a) { return a.radians * (360.0f / math::TAU); } -inline f32 as_turns(Angle a) { return a.radians * ( 1.0f / math::TAU); } -inline f32 as_grads(Angle a) { return a.radians * (400.0f / math::TAU); } -inline f32 as_gons(Angle a) { return a.radians * (400.0f / math::TAU); } -} // namespace angle - -//////////////////////////////// -/// /// -/// Math Functions /// -/// /// -//////////////////////////////// - - -namespace math -{ -f32 const ZERO = 0.0f; -f32 const ONE = 1.0f; -f32 const THIRD = 0.33333333f; -f32 const TWO_THIRDS = 0.66666667f; -f32 const E = 2.718281828f; -f32 const PI = 3.141592654f; -f32 const TAU = 6.283185307f; -f32 const SQRT_2 = 1.414213562f; -f32 const SQRT_3 = 1.732050808f; -f32 const SQRT_5 = 2.236067978f; - -f32 const F32_PRECISION = 1.0e-7f; - -// Power -inline f32 sqrt(f32 x) { return ::sqrtf(x); } -inline f32 pow(f32 x, f32 y) { return static_cast(::powf(x, y)); } -inline f32 cbrt(f32 x) { return static_cast(::cbrtf(x)); } - -inline f32 -fast_inv_sqrt(f32 x) -{ - const f32 THREE_HALFS = 1.5f; - - const f32 x2 = x * 0.5f; - f32 y = x; - u32 i = bit_cast(y); // Evil floating point bit level hacking - // i = 0x5f3759df - (i >> 1); // What the fuck? Old - i = 0x5f375a86 - (i >> 1); // What the fuck? Improved! - y = bit_cast(i); - y = y * (THREE_HALFS - (x2 * y * y)); // 1st iteration - // y = y * (THREE_HALFS - (x2 * y * y)); // 2nd iteration, this can be removed - - return y; -} - -// Trigonometric -inline f32 sin(Angle a) { return ::sinf(angle::as_radians(a)); } -inline f32 cos(Angle a) { return ::cosf(angle::as_radians(a)); } -inline f32 tan(Angle a) { return ::tanf(angle::as_radians(a)); } - -inline Angle arcsin(f32 x) { return angle::radians(::asinf(x)); } -inline Angle arccos(f32 x) { return angle::radians(::acosf(x)); } -inline Angle arctan(f32 x) { return angle::radians(::atanf(x)); } -inline Angle arctan2(f32 y, f32 x) { return angle::radians(::atan2f(y, x)); } - -// Hyperbolic -inline f32 sinh(f32 x) { return ::sinhf(x); } -inline f32 cosh(f32 x) { return ::coshf(x); } -inline f32 tanh(f32 x) { return ::tanhf(x); } - -inline f32 arsinh(f32 x) { return ::asinhf(x); } -inline f32 arcosh(f32 x) { return ::acoshf(x); } -inline f32 artanh(f32 x) { return ::atanhf(x); } - -// Rounding -inline f32 ceil(f32 x) { return ::ceilf(x); } -inline f32 floor(f32 x) { return ::floorf(x); } -inline f32 mod(f32 x, f32 y) { return ::fmodf(x, y); } -inline f32 truncate(f32 x) { return ::truncf(x); } -inline f32 round(f32 x) { return ::roundf(x); } - -inline s32 sign(s32 x) { return x >= 0 ? +1 : -1; } -inline s64 sign(s64 x) { return x >= 0 ? +1 : -1; } -inline f32 sign(f32 x) { return x >= 0.0f ? +1.0f : -1.0f; } - -// Other -inline f32 -abs(f32 x) -{ - u32 i = bit_cast(x); - i &= 0x7FFFFFFFul; - return bit_cast(i); -} - -inline s8 -abs(s8 x) -{ - u8 i = bit_cast(x); - i &= 0x7Fu; - return bit_cast(i); -} - -inline s16 -abs(s16 x) -{ - u16 i = bit_cast(x); - i &= 0x7FFFu; - return bit_cast(i); -} - -inline s32 -abs(s32 x) -{ - u32 i = bit_cast(x); - i &= 0x7FFFFFFFul; - return bit_cast(i); -} - -inline s64 -abs(s64 x) -{ - u64 i = bit_cast(x); - i &= 0x7FFFFFFFFFFFFFFFull; - return bit_cast(i); -} - -inline bool -is_infinite(f32 x) -{ - return isinf(x); -} - -inline bool -is_nan(f32 x) -{ - return isnan(x); -} - -inline s32 -kronecker_delta(s32 i, s32 j) -{ - return static_cast(i == j); -} - -inline s64 -kronecker_delta(s64 i, s64 j) -{ - return static_cast(i == j); -} - -inline f32 -kronecker_delta(f32 i, f32 j) -{ - return static_cast(i == j); -} - -inline f32 -min(f32 x, f32 y) -{ - // TODO(bill): Check if this is even good - return x < y ? x : y; -} - -inline s32 -min(s32 x, s32 y) -{ - return y + ((x-y) & (x-y)>>31); -} - -inline s64 -min(s64 x, s64 y) -{ - return y + ((x-y) & (x-y)>>63); -} - -inline f32 -max(f32 x, f32 y) -{ - // TODO(bill): Check if this is even good - return x > y ? x : y; -} - -inline s32 -max(s32 x, s32 y) -{ - return x - ((x-y) & (x-y)>>31); -} - -inline s64 -max(s64 x, s64 y) -{ - return x - ((x-y) & (x-y)>>63); -} - -inline f32 -clamp(f32 x, f32 min, f32 max) -{ - const f32 t = x < min ? min : x; - return t > max ? max : t; -} - -inline s32 -clamp(s32 x, s32 min, s32 max) -{ - const s32 t = x < min ? min : x; - return t > max ? max : t; -} - -inline s64 -clamp(s64 x, s64 min, s64 max) -{ - const s64 t = x < min ? min : x; - return t > max ? max : t; -} - -inline bool -equals(f32 a, f32 b, f32 precision) -{ - return ((b <= (a + precision)) && (b >= (a - precision))); -} - -// Vector2 functions -inline f32 -dot(Vector2 a, Vector2 b) -{ - return a.x * b.x + a.y * b.y; -} - -inline f32 -cross(Vector2 a, Vector2 b) -{ - return a.x * b.y - a.y * b.x; -} - -inline f32 -magnitude(Vector2 a) -{ - return math::sqrt(math::dot(a, a)); -} - -inline Vector2 -normalize(Vector2 a) -{ - f32 m = magnitude(a); - if (m > 0) - return a * (1.0f / m); - return {}; -} - -inline Vector2 -hadamard(Vector2 a, Vector2 b) -{ - return {a.x * b.x, a.y * b.y}; -} - -inline f32 -aspect_ratio(Vector2 a) -{ - return a.x / a.y; -} - - -inline Matrix4 -matrix2_to_matrix4(Matrix2 m) -{ - Matrix4 result = MATRIX4_IDENTITY; - result[0][0] = m[0][0]; - result[0][1] = m[0][1]; - result[1][0] = m[1][0]; - result[1][1] = m[1][1]; - return result; -} - -// Vector3 functions -inline f32 -dot(Vector3 a, Vector3 b) -{ - return a.x * b.x + a.y * b.y + a.z * b.z; -} - -inline Vector3 -cross(Vector3 a, Vector3 b) -{ - return Vector3{ - a.y * b.z - b.y * a.z, // x - a.z * b.x - b.z * a.x, // y - a.x * b.y - b.x * a.y // z - }; -} - -inline f32 -magnitude(Vector3 a) -{ - return math::sqrt(math::dot(a, a)); -} - -inline Vector3 -normalize(Vector3 a) -{ - f32 m = magnitude(a); - if (m > 0) - return a * (1.0f / m); - return {}; -} - -inline Vector3 -hadamard(Vector3 a, Vector3 b) -{ - return {a.x * b.x, a.y * b.y, a.z * b.z}; -} - -inline Matrix4 -matrix3_to_matrix4(Matrix3 const& m) -{ - Matrix4 result = MATRIX4_IDENTITY; - result[0][0] = m[0][0]; - result[0][1] = m[0][1]; - result[0][2] = m[0][2]; - result[1][0] = m[1][0]; - result[1][1] = m[1][1]; - result[1][2] = m[1][2]; - result[2][0] = m[2][0]; - result[2][1] = m[2][1]; - result[2][2] = m[2][2]; - return result; -} - -// Vector4 functions -inline f32 -dot(Vector4 a, Vector4 b) -{ - return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; -} - -inline f32 -magnitude(Vector4 a) -{ - return math::sqrt(math::dot(a, a)); -} - -inline Vector4 -normalize(Vector4 a) -{ - f32 m = magnitude(a); - if (m > 0) - return a * (1.0f / m); - return {}; -} - -inline Vector4 -hadamard(Vector4 a, Vector4 b) -{ - return {a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w}; -} - -// Complex Functions -inline f32 -dot(Complex a, Complex b) -{ - return a.real * b.real + a.imag * b.imag; -} - -inline f32 -magnitude(Complex a) -{ - return math::sqrt(norm(a)); -} - -inline f32 -norm(Complex a) -{ - return math::dot(a, a); -} - -inline Complex -normalize(Complex a) -{ - f32 m = magnitude(a); - if (m > 0) - return a / magnitude(a); - return COMPLEX_ZERO; -} - -inline Complex -conjugate(Complex a) -{ - return {a.real, -a.imag}; -} - -inline Complex -inverse(Complex a) -{ - f32 m = norm(a); - if (m > 0) - return conjugate(a) / norm(a); - return COMPLEX_ZERO; -} - -inline f32 -complex_angle(Complex a) -{ - return atan2f(a.imag, a.real); -} - -inline Complex -magnitude_angle(f32 magnitude, Angle a) -{ - f32 real = magnitude * math::cos(a); - f32 imag = magnitude * math::sin(a); - return {real, imag}; -} - -// Quaternion functions -inline f32 -dot(Quaternion a, Quaternion b) -{ - return math::dot(a.xyz, b.xyz) + a.w*b.w; -} - -inline Quaternion -cross(Quaternion a, Quaternion b) -{ - return Quaternion{a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y, - a.w * b.y + a.y * b.w + a.z * b.x - a.x * b.z, - a.w * b.z + a.z * b.w + a.x * b.y - a.y * b.x, - a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z}; -} - -inline f32 -magnitude(Quaternion a) -{ - return math::sqrt(math::dot(a, a)); -} - -inline f32 -norm(Quaternion a) -{ - return math::dot(a, a); -} - -inline Quaternion -normalize(Quaternion a) -{ - f32 m = magnitude(a); - if (m > 0) - return a * (1.0f / m); - return {}; -} - -inline Quaternion -conjugate(Quaternion a) -{ - return {-a.x, -a.y, -a.z, a.w}; -} - -inline Quaternion -inverse(Quaternion a) -{ - f32 m = 1.0f / dot(a, a); - return math::conjugate(a) * m; -} - -inline Angle -quaternion_angle(Quaternion a) -{ - return 2.0f * math::arccos(a.w); -} - -inline Vector3 -quaternion_axis(Quaternion a) -{ - f32 s2 = 1.0f - a.w * a.w; - - if (s2 <= 0.0f) - return {0, 0, 1}; - - f32 invs2 = 1.0f / math::sqrt(s2); - - return a.xyz * invs2; -} - -inline Quaternion -axis_angle(Vector3 axis, Angle angle) -{ - Vector3 a = math::normalize(axis); - f32 s = math::sin(0.5f * angle); - - Quaternion q; - q.xyz = a * s; - q.w = math::cos(0.5f * angle); - - return q; -} - -inline Angle -quaternion_roll(Quaternion a) -{ - return math::arctan2(2.0f * a.x * a.y + a.z * a.w, - a.x * a.x + a.w * a.w - a.y * a.y - a.z * a.z); -} - -inline Angle -quaternion_pitch(Quaternion a) -{ - return math::arctan2(2.0f * a.y * a.z + a.w * a.x, - a.w * a.w - a.x * a.x - a.y * a.y + a.z * a.z); -} - -inline Angle -quaternion_yaw(Quaternion a) -{ - return math::arcsin(-2.0f * (a.x * a.z - a.w * a.y)); - -} - -inline Euler_Angles -quaternion_to_euler_angles(Quaternion a) -{ - return {quaternion_pitch(a), quaternion_yaw(a), quaternion_roll(a)}; -} - -inline Quaternion -euler_angles_to_quaternion(Euler_Angles const& e, - Vector3 x_axis, - Vector3 y_axis, - Vector3 z_axis) -{ - Quaternion p = axis_angle(x_axis, e.pitch); - Quaternion y = axis_angle(y_axis, e.yaw); - Quaternion r = axis_angle(z_axis, e.roll); - - return y * p * r; -} - - -// Spherical Linear Interpolation -inline Quaternion -slerp(Quaternion x, Quaternion y, f32 t) -{ - Quaternion z = y; - - f32 cos_theta = dot(x, y); - - if (cos_theta < 0.0f) - { - z = -y; - cos_theta = -cos_theta; - } - - if (cos_theta > 1.0f) - { - return Quaternion{lerp(x.x, y.x, t), - lerp(x.y, y.y, t), - lerp(x.z, y.z, t), - lerp(x.w, y.w, t)}; - } - - Angle angle = math::arccos(cos_theta); - - Quaternion result = math::sin(angle::radians(1.0f) - (t * angle)) * x + math::sin(t * angle) * z; - return result * (1.0f / math::sin(angle)); -} - -// Shoemake's Quaternion Curves -// Sqherical Cubic Interpolation -inline Quaternion -squad(Quaternion p, - Quaternion a, - Quaternion b, - Quaternion q, - f32 t) -{ - return slerp(slerp(p, q, t), slerp(a, b, t), 2.0f * t * (1.0f - t)); -} - -// Matrix2 functions -inline Matrix2 -transpose(Matrix2 m) -{ - Matrix2 result; - for (usize i = 0; i < 2; i++) - { - for (usize j = 0; j < 2; j++) - result[i][j] = m[j][i]; - } - return result; -} - -inline f32 -determinant(Matrix2 m) -{ - return m[0][0] * m[1][1] - m[1][0] * m[0][1]; -} - -inline Matrix2 -inverse(Matrix2 m) -{ - f32 inv_det = 1.0f / (m[0][0] * m[1][1] - m[1][0] * m[0][1]); - Matrix2 result; - result[0][0] = m[1][1] * inv_det; - result[0][1] = -m[0][1] * inv_det; - result[1][0] = -m[1][0] * inv_det; - result[1][1] = m[0][0] * inv_det; - return result; -} - -inline Matrix2 -hadamard(Matrix2 a, const Matrix2&b) -{ - Matrix2 result; - result[0] = a[0] * b[0]; - result[1] = a[1] * b[1]; - return result; -} - -// Matrix3 functions -inline Matrix3 -transpose(Matrix3 const& m) -{ - Matrix3 result; - - for (usize i = 0; i < 3; i++) - { - for (usize j = 0; j < 3; j++) - result[i][j] = m[j][i]; - } - return result; -} - -inline f32 -determinant(Matrix3 const& m) -{ - return (+m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2]) - -m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2]) - +m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2])); -} - -inline Matrix3 -inverse(Matrix3 const& m) -{ - f32 inv_det = 1.0f / ( - + m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2]) - - m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2]) - + m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2])); - - Matrix3 result; - - result[0][0] = +(m[1][1] * m[2][2] - m[2][1] * m[1][2]) * inv_det; - result[1][0] = -(m[1][0] * m[2][2] - m[2][0] * m[1][2]) * inv_det; - result[2][0] = +(m[1][0] * m[2][1] - m[2][0] * m[1][1]) * inv_det; - result[0][1] = -(m[0][1] * m[2][2] - m[2][1] * m[0][2]) * inv_det; - result[1][1] = +(m[0][0] * m[2][2] - m[2][0] * m[0][2]) * inv_det; - result[2][1] = -(m[0][0] * m[2][1] - m[2][0] * m[0][1]) * inv_det; - result[0][2] = +(m[0][1] * m[1][2] - m[1][1] * m[0][2]) * inv_det; - result[1][2] = -(m[0][0] * m[1][2] - m[1][0] * m[0][2]) * inv_det; - result[2][2] = +(m[0][0] * m[1][1] - m[1][0] * m[0][1]) * inv_det; - - return result; -} - -inline Matrix3 -hadamard(Matrix3 const& a, const Matrix3&b) -{ - Matrix3 result; - result[0] = a[0] * b[0]; - result[1] = a[1] * b[1]; - result[2] = a[2] * b[2]; - return result; -} - -// Matrix4 functions -inline Matrix4 -transpose(Matrix4 const& m) -{ - Matrix4 result; - - for (usize i = 0; i < 4; i++) - { - for (usize j = 0; j < 4; j++) - result[i][j] = m[j][i]; - } - return result; -} - -f32 -determinant(Matrix4 const& m) -{ - f32 coef00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; - f32 coef02 = m[1][2] * m[3][3] - m[3][2] * m[1][3]; - f32 coef03 = m[1][2] * m[2][3] - m[2][2] * m[1][3]; - - f32 coef04 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; - f32 coef06 = m[1][1] * m[3][3] - m[3][1] * m[1][3]; - f32 coef07 = m[1][1] * m[2][3] - m[2][1] * m[1][3]; - - f32 coef08 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; - f32 coef10 = m[1][1] * m[3][2] - m[3][1] * m[1][2]; - f32 coef11 = m[1][1] * m[2][2] - m[2][1] * m[1][2]; - - f32 coef12 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; - f32 coef14 = m[1][0] * m[3][3] - m[3][0] * m[1][3]; - f32 coef15 = m[1][0] * m[2][3] - m[2][0] * m[1][3]; - - f32 coef16 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; - f32 coef18 = m[1][0] * m[3][2] - m[3][0] * m[1][2]; - f32 coef19 = m[1][0] * m[2][2] - m[2][0] * m[1][2]; - - f32 coef20 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; - f32 coef22 = m[1][0] * m[3][1] - m[3][0] * m[1][1]; - f32 coef23 = m[1][0] * m[2][1] - m[2][0] * m[1][1]; - - Vector4 fac0 = {coef00, coef00, coef02, coef03}; - Vector4 fac1 = {coef04, coef04, coef06, coef07}; - Vector4 fac2 = {coef08, coef08, coef10, coef11}; - Vector4 fac3 = {coef12, coef12, coef14, coef15}; - Vector4 fac4 = {coef16, coef16, coef18, coef19}; - Vector4 fac5 = {coef20, coef20, coef22, coef23}; - - Vector4 vec0 = {m[1][0], m[0][0], m[0][0], m[0][0]}; - Vector4 vec1 = {m[1][1], m[0][1], m[0][1], m[0][1]}; - Vector4 vec2 = {m[1][2], m[0][2], m[0][2], m[0][2]}; - Vector4 vec3 = {m[1][3], m[0][3], m[0][3], m[0][3]}; - - Vector4 inv0 = vec1 * fac0 - vec2 * fac1 + vec3 * fac2; - Vector4 inv1 = vec0 * fac0 - vec2 * fac3 + vec3 * fac4; - Vector4 inv2 = vec0 * fac1 - vec1 * fac3 + vec3 * fac5; - Vector4 inv3 = vec0 * fac2 - vec1 * fac4 + vec2 * fac5; - - Vector4 signA = {+1, -1, +1, -1}; - Vector4 signB = {-1, +1, -1, +1}; - Matrix4 inverse = {inv0 * signA, inv1 * signB, inv2 * signA, inv3 * signB}; - - Vector4 row0 = {inverse[0][0], inverse[1][0], inverse[2][0], inverse[3][0]}; - - Vector4 dot0 = m[0] * row0; - f32 dot1 = (dot0[0] + dot0[1]) + (dot0[2] + dot0[3]); - return dot1; -} - -Matrix4 -inverse(Matrix4 const& m) -{ - f32 coef00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; - f32 coef02 = m[1][2] * m[3][3] - m[3][2] * m[1][3]; - f32 coef03 = m[1][2] * m[2][3] - m[2][2] * m[1][3]; - f32 coef04 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; - f32 coef06 = m[1][1] * m[3][3] - m[3][1] * m[1][3]; - f32 coef07 = m[1][1] * m[2][3] - m[2][1] * m[1][3]; - f32 coef08 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; - f32 coef10 = m[1][1] * m[3][2] - m[3][1] * m[1][2]; - f32 coef11 = m[1][1] * m[2][2] - m[2][1] * m[1][2]; - f32 coef12 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; - f32 coef14 = m[1][0] * m[3][3] - m[3][0] * m[1][3]; - f32 coef15 = m[1][0] * m[2][3] - m[2][0] * m[1][3]; - f32 coef16 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; - f32 coef18 = m[1][0] * m[3][2] - m[3][0] * m[1][2]; - f32 coef19 = m[1][0] * m[2][2] - m[2][0] * m[1][2]; - f32 coef20 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; - f32 coef22 = m[1][0] * m[3][1] - m[3][0] * m[1][1]; - f32 coef23 = m[1][0] * m[2][1] - m[2][0] * m[1][1]; - - Vector4 fac0 = {coef00, coef00, coef02, coef03}; - Vector4 fac1 = {coef04, coef04, coef06, coef07}; - Vector4 fac2 = {coef08, coef08, coef10, coef11}; - Vector4 fac3 = {coef12, coef12, coef14, coef15}; - Vector4 fac4 = {coef16, coef16, coef18, coef19}; - Vector4 fac5 = {coef20, coef20, coef22, coef23}; - - Vector4 vec0 = {m[1][0], m[0][0], m[0][0], m[0][0]}; - Vector4 vec1 = {m[1][1], m[0][1], m[0][1], m[0][1]}; - Vector4 vec2 = {m[1][2], m[0][2], m[0][2], m[0][2]}; - Vector4 vec3 = {m[1][3], m[0][3], m[0][3], m[0][3]}; - - Vector4 inv0 = vec1 * fac0 - vec2 * fac1 + vec3 * fac2; - Vector4 inv1 = vec0 * fac0 - vec2 * fac3 + vec3 * fac4; - Vector4 inv2 = vec0 * fac1 - vec1 * fac3 + vec3 * fac5; - Vector4 inv3 = vec0 * fac2 - vec1 * fac4 + vec2 * fac5; - - Vector4 signA = {+1, -1, +1, -1}; - Vector4 signB = {-1, +1, -1, +1}; - Matrix4 inverse = {inv0 * signA, inv1 * signB, inv2 * signA, inv3 * signB}; - - Vector4 row0 = {inverse[0][0], inverse[1][0], inverse[2][0], inverse[3][0]}; - - Vector4 dot0 = m[0] * row0; - f32 dot1 = (dot0[0] + dot0[1]) + (dot0[2] + dot0[3]); - - f32 oneOverDeterminant = 1.0f / dot1; - - return inverse * oneOverDeterminant; -} - -inline Matrix4 -hadamard(Matrix4 const& a, Matrix4 const& b) -{ - Matrix4 result; - - result[0] = a[0] * b[0]; - result[1] = a[1] * b[1]; - result[2] = a[2] * b[2]; - result[3] = a[3] * b[3]; - - return result; -} - -inline bool -is_affine(Matrix4 const& m) -{ - // E.g. No translation - return (equals(m.columns[3].x, 0)) & - (equals(m.columns[3].y, 0)) & - (equals(m.columns[3].z, 0)) & - (equals(m.columns[3].w, 1.0f)); -} - - -inline Matrix4 -quaternion_to_matrix4(Quaternion q) -{ - Matrix4 mat = MATRIX4_IDENTITY; - - Quaternion a = math::normalize(q); - - f32 xx = a.x * a.x; - f32 yy = a.y * a.y; - f32 zz = a.z * a.z; - f32 xy = a.x * a.y; - f32 xz = a.x * a.z; - f32 yz = a.y * a.z; - f32 wx = a.w * a.x; - f32 wy = a.w * a.y; - f32 wz = a.w * a.z; - - mat[0][0] = 1.0f - 2.0f * (yy + zz); - mat[0][1] = 2.0f * (xy + wz); - mat[0][2] = 2.0f * (xz - wy); - - mat[1][0] = 2.0f * (xy - wz); - mat[1][1] = 1.0f - 2.0f * (xx + zz); - mat[1][2] = 2.0f * (yz + wx); - - mat[2][0] = 2.0f * (xz + wy); - mat[2][1] = 2.0f * (yz - wx); - mat[2][2] = 1.0f - 2.0f * (xx + yy); - - return mat; -} - -Quaternion -matrix4_to_quaternion(Matrix4 const& m) -{ - f32 four_x_squared_minus_1 = m[0][0] - m[1][1] - m[2][2]; - f32 four_y_squared_minus_1 = m[1][1] - m[0][0] - m[2][2]; - f32 four_z_squared_minus_1 = m[2][2] - m[0][0] - m[1][1]; - f32 four_w_squared_minus_1 = m[0][0] + m[1][1] + m[2][2]; - - s32 biggestIndex = 0; - f32 four_biggest_squared_minus_1 = four_w_squared_minus_1; - if (four_x_squared_minus_1 > four_biggest_squared_minus_1) - { - four_biggest_squared_minus_1 = four_x_squared_minus_1; - biggestIndex = 1; - } - if (four_y_squared_minus_1 > four_biggest_squared_minus_1) - { - four_biggest_squared_minus_1 = four_y_squared_minus_1; - biggestIndex = 2; - } - if (four_z_squared_minus_1 > four_biggest_squared_minus_1) - { - four_biggest_squared_minus_1 = four_z_squared_minus_1; - biggestIndex = 3; - } - - f32 biggestVal = math::sqrt(four_biggest_squared_minus_1 + 1.0f) * 0.5f; - f32 mult = 0.25f / biggestVal; - - Quaternion q = QUATERNION_IDENTITY; - - switch (biggestIndex) - { - case 0: - { - q.w = biggestVal; - q.x = (m[1][2] - m[2][1]) * mult; - q.y = (m[2][0] - m[0][2]) * mult; - q.z = (m[0][1] - m[1][0]) * mult; - } - break; - case 1: - { - q.w = (m[1][2] - m[2][1]) * mult; - q.x = biggestVal; - q.y = (m[0][1] + m[1][0]) * mult; - q.z = (m[2][0] + m[0][2]) * mult; - } - break; - case 2: - { - q.w = (m[2][0] - m[0][2]) * mult; - q.x = (m[0][1] + m[1][0]) * mult; - q.y = biggestVal; - q.z = (m[1][2] + m[2][1]) * mult; - } - break; - case 3: - { - q.w = (m[0][1] - m[1][0]) * mult; - q.x = (m[2][0] + m[0][2]) * mult; - q.y = (m[1][2] + m[2][1]) * mult; - q.z = biggestVal; - } - break; - default: // Should never actually get here. Just for sanities sake. - { - GB_ASSERT(false, "How did you get here?!"); - } - break; - } - - return q; -} - - -inline Matrix4 -translate(Vector3 v) -{ - Matrix4 result = MATRIX4_IDENTITY; - result[3].xyz = v; - result[3].w = 1; - return result; -} - -inline Matrix4 -rotate(Vector3 v, Angle angle) -{ - const f32 c = math::cos(angle); - const f32 s = math::sin(angle); - - const Vector3 axis = math::normalize(v); - const Vector3 t = (1.0f - c) * axis; - - Matrix4 rot = MATRIX4_IDENTITY; - - rot[0][0] = c + t.x * axis.x; - rot[0][1] = 0 + t.x * axis.y + s * axis.z; - rot[0][2] = 0 + t.x * axis.z - s * axis.y; - rot[0][3] = 0; - - rot[1][0] = 0 + t.y * axis.x - s * axis.z; - rot[1][1] = c + t.y * axis.y; - rot[1][2] = 0 + t.y * axis.z + s * axis.x; - rot[1][3] = 0; - - rot[2][0] = 0 + t.z * axis.x + s * axis.y; - rot[2][1] = 0 + t.z * axis.y - s * axis.x; - rot[2][2] = c + t.z * axis.z; - rot[2][3] = 0; - - return rot; -} - -inline Matrix4 -scale(Vector3 v) -{ - return { v.x, 0, 0, 0, - 0, v.y, 0, 0, - 0, 0, v.z, 0, - 0, 0, 0, 1 }; -} - -inline Matrix4 -ortho(f32 left, f32 right, f32 bottom, f32 top) -{ - return ortho(left, right, bottom, top, -1.0f, 1.0f); -} - -inline Matrix4 -ortho(f32 left, f32 right, f32 bottom, f32 top, f32 z_near, f32 z_far) -{ - Matrix4 result = MATRIX4_IDENTITY; - - result[0][0] = 2.0f / (right - left); - result[1][1] = 2.0f / (top - bottom); - result[2][2] = -2.0f / (z_far - z_near); - result[3][0] = -(right + left) / (right - left); - result[3][1] = -(top + bottom) / (top - bottom); - result[3][2] = -(z_far + z_near) / (z_far - z_near); - - return result; -} - -inline Matrix4 -perspective(Angle fovy, f32 aspect, f32 z_near, f32 z_far) -{ - GB_ASSERT(math::abs(aspect) > 0.0f, - "math::perspective `fovy` is %f rad", angle::as_radians(fovy)); - - f32 tan_half_fovy = math::tan(0.5f * fovy); - - Matrix4 result = {}; - result[0][0] = 1.0f / (aspect * tan_half_fovy); - result[1][1] = 1.0f / (tan_half_fovy); - result[2][2] = -(z_far + z_near) / (z_far - z_near); - result[2][3] = -1.0f; - result[3][2] = -2.0f * z_far * z_near / (z_far - z_near); - - return result; -} - -inline Matrix4 -infinite_perspective(Angle fovy, f32 aspect, f32 z_near) -{ - f32 range = math::tan(0.5f * fovy) * z_near; - f32 left = -range * aspect; - f32 right = range * aspect; - f32 bottom = -range; - f32 top = range; - - Matrix4 result = {}; - - result[0][0] = (2.0f * z_near) / (right - left); - result[1][1] = (2.0f * z_near) / (top - bottom); - result[2][2] = -1.0f; - result[2][3] = -1.0f; - result[3][2] = -2.0f * z_near; - - return result; -} - - -inline Matrix4 -look_at_matrix4(Vector3 eye, Vector3 center, Vector3 up) -{ - const Vector3 f = math::normalize(center - eye); - const Vector3 s = math::normalize(math::cross(f, up)); - const Vector3 u = math::cross(s, f); - - Matrix4 result = MATRIX4_IDENTITY; - - result[0][0] = +s.x; - result[1][0] = +s.y; - result[2][0] = +s.z; - - result[0][1] = +u.x; - result[1][1] = +u.y; - result[2][1] = +u.z; - - result[0][2] = -f.x; - result[1][2] = -f.y; - result[2][2] = -f.z; - - result[3][0] = -math::dot(s, eye); - result[3][1] = -math::dot(u, eye); - result[3][2] = +math::dot(f, eye); - - return result; -} - - -inline Quaternion -look_at_quaternion(Vector3 eye, Vector3 center, Vector3 up) -{ - if (math::equals(math::magnitude(center - eye), 0, 0.001f)) - return QUATERNION_IDENTITY; // You cannot look at where you are! - -#if 1 - return matrix4_to_quaternion(look_at_matrix4(eye, center, up)); -#else - // TODO(bill): Thoroughly test this look_at_quaternion! - // Is it more efficient that that a converting a Matrix4 to a Quaternion? - Vector3 forward_l = math::normalize(center - eye); - Vector3 forward_w = {1, 0, 0}; - Vector3 axis = math::cross(forward_l, forward_w); - - f32 angle = math::acos(math::dot(forward_l, forward_w)); - - Vector3 third = math::cross(axis, forward_w); - if (math::dot(third, forward_l) < 0) - angle = -angle; - - Quaternion q1 = math::axis_angle(axis, angle); - - Vector3 up_l = q1 * math::normalize(up); - Vector3 right = math::normalize(math::cross(forward_l, up)); - Vector3 up_w = math::normalize(math::cross(right, forward_l)); - - Vector3 axis2 = math::cross(up_l, up_w); - f32 angle2 = math::acos(math::dot(up_l, up_w)); - - Quaternion q2 = math::axis_angle(axis2, angle2); - - return q2 * q1; -#endif -} - -// Transform Functions -inline Vector3 -transform_point(Transform const& transform, Vector3 point) -{ - return (math::conjugate(transform.orientation) * (transform.position - point)) / transform.scale; -} - -inline Transform -inverse(Transform const& t) -{ - const Quaternion inv_orientation = math::conjugate(t.orientation); - - Transform inv_transform; - - inv_transform.position = (inv_orientation * -t.position) / t.scale; - inv_transform.orientation = inv_orientation; - // inv_transform.scale = inv_orientation * (Vector3{1, 1, 1} / t.scale); // Vector3 scale - inv_transform.scale = 1.0f / t.scale; - - return inv_transform; -} - -inline Matrix4 -transform_to_matrix4(Transform const& t) -{ - return math::translate(t.position) * - math::quaternion_to_matrix4(t.orientation) * - math::scale({t.scale, t.scale, t.scale}); -} -} // namespace math - - -namespace aabb -{ -inline Aabb -calculate(void const* vertices, usize num_vertices, usize stride, usize offset) -{ - Vector3 min; - Vector3 max; - const u8* vertex = reinterpret_cast(vertices); - vertex += offset; - Vector3 position = pseudo_cast(vertex); - min.x = max.x = position.x; - min.y = max.y = position.y; - min.z = max.z = position.z; - vertex += stride; - - for (usize i = 1; i < num_vertices; i++) - { - position = pseudo_cast(vertex); - vertex += stride; - - Vector3 p = position; - min.x = math::min(p.x, min.x); - min.y = math::min(p.y, min.y); - min.z = math::min(p.z, min.z); - max.x = math::max(p.x, max.x); - max.y = math::max(p.y, max.y); - max.z = math::max(p.z, max.z); - } - - Aabb aabb; - - aabb.center = 0.5f * (min + max); - aabb.half_size = 0.5f * (max - min); - - return aabb; -} - -inline f32 -surface_area(Aabb const& aabb) -{ - Vector3 h = aabb.half_size * 2.0f; - f32 s = 0.0f; - s += h.x * h.y; - s += h.y * h.z; - s += h.z * h.x; - s *= 3.0f; - return s; -} - -inline f32 -volume(Aabb const& aabb) -{ - Vector3 h = aabb.half_size * 2.0f; - return h.x * h.y * h.z; -} - -inline Sphere -to_sphere(Aabb const& aabb) -{ - Sphere s; - s.center = aabb.center; - s.radius = math::magnitude(aabb.half_size); - return s; -} - - -inline bool -contains(Aabb const& aabb, Vector3 point) -{ - Vector3 distance = aabb.center - point; - - // NOTE(bill): & is faster than && - return (math::abs(distance.x) <= aabb.half_size.x) & - (math::abs(distance.y) <= aabb.half_size.y) & - (math::abs(distance.z) <= aabb.half_size.z); -} - -inline bool -contains(Aabb const& a, Aabb const& b) -{ - Vector3 dist = a.center - b.center; - - // NOTE(bill): & is faster than && - return (math::abs(dist.x) + b.half_size.x <= a.half_size.x) & - (math::abs(dist.y) + b.half_size.y <= a.half_size.y) & - (math::abs(dist.z) + b.half_size.z <= a.half_size.z); -} - - -inline bool -intersects(Aabb const& a, Aabb const& b) -{ - Vector3 dist = a.center - b.center; - Vector3 sum_half_sizes = a.half_size + b.half_size; - - // NOTE(bill): & is faster than && - return (math::abs(dist.x) <= sum_half_sizes.x) & - (math::abs(dist.y) <= sum_half_sizes.y) & - (math::abs(dist.z) <= sum_half_sizes.z); -} - -inline Aabb -transform_affine(Aabb const& aabb, Matrix4 const& m) -{ - GB_ASSERT(math::is_affine(m), - "Passed Matrix4 must be an affine matrix"); - - Aabb result; - Vector4 ac; - ac.xyz = aabb.center; - ac.w = 1; - result.center = (m * ac).xyz; - - Vector3 hs = aabb.half_size; - f32 x = math::abs(m[0][0] * hs.x + math::abs(m[0][1]) * hs.y + math::abs(m[0][2]) * hs.z); - f32 y = math::abs(m[1][0] * hs.x + math::abs(m[1][1]) * hs.y + math::abs(m[1][2]) * hs.z); - f32 z = math::abs(m[2][0] * hs.x + math::abs(m[2][1]) * hs.y + math::abs(m[2][2]) * hs.z); - - result.half_size.x = math::is_infinite(math::abs(hs.x)) ? hs.x : x; - result.half_size.y = math::is_infinite(math::abs(hs.y)) ? hs.y : y; - result.half_size.z = math::is_infinite(math::abs(hs.z)) ? hs.z : z; - - return result; -} -} // namespace aabb - -namespace sphere -{ -Sphere -calculate_min_bounding(void const* vertices, usize num_vertices, usize stride, usize offset, f32 step) -{ -#if !defined(GB_MATH_NO_RANDOM) - auto gen = random::make(0); -#endif - - u8 const* vertex = reinterpret_cast(vertices); - vertex += offset; - - Vector3 position = pseudo_cast(vertex[0]); - Vector3 center = position; - center += pseudo_cast(vertex[1 * stride]); - center *= 0.5f; - - Vector3 d = position - center; - f32 max_dist_sq = math::dot(d, d); - f32 radius_step = step * 0.37f; - - bool done; - do - { - done = true; -#if !defined(GB_MATH_NO_RANDOM) - for (u32 i = 0, index = random::uniform_u32(&gen, 0, num_vertices-1); - i < num_vertices; - i++, index = (index + 1)%num_vertices) -#else - for (u32 i = 0, index = num_vertices/2; - i < num_vertices; - i++, index = (index + 1)%num_vertices) -#endif - { - Vector3 position = pseudo_cast(vertex[index * stride]); - - d = position - center; - f32 dist_sq = math::dot(d, d); - - if (dist_sq > max_dist_sq) - { - done = false; - - center = d * radius_step; - max_dist_sq = math::lerp(max_dist_sq, dist_sq, step); - - break; - } - } - } - while (!done); - - Sphere result; - - result.center = center; - result.radius = math::sqrt(max_dist_sq); - - return result; -} - -Sphere -calculate_max_bounding(void const* vertices, usize num_vertices, usize stride, usize offset) -{ - Aabb aabb = aabb::calculate(vertices, num_vertices, stride, offset); - - Vector3 center = aabb.center; - - f32 max_dist_sq = 0.0f; - const u8* vertex = reinterpret_cast(vertices); - vertex += offset; - - for (usize i = 0; i < num_vertices; i++) - { - Vector3 position = pseudo_cast(vertex); - vertex += stride; - - Vector3 d = position - center; - f32 dist_sq = math::dot(d, d); - max_dist_sq = math::max(dist_sq, max_dist_sq); - } - - Sphere sphere; - sphere.center = center; - sphere.radius = math::sqrt(max_dist_sq); - - return sphere; -} - -inline f32 -surface_area(Sphere s) -{ - return 2.0f * math::TAU * s.radius * s.radius; -} - -inline f32 -volume(Sphere s) -{ - return math::TWO_THIRDS * math::TAU * s.radius * s.radius * s.radius; -} - -inline Aabb -to_aabb(Sphere s) -{ - Aabb a; - a.center = s.center; - a.half_size.x = s.radius * math::SQRT_3; - a.half_size.y = s.radius * math::SQRT_3; - a.half_size.z = s.radius * math::SQRT_3; - return a; -} - -inline bool -contains_point(Sphere s, Vector3 point) -{ - Vector3 dr = point - s.center; - f32 distance = math::dot(dr, dr); - return distance < s.radius * s.radius; -} - -inline f32 -ray_intersection(Vector3 from, Vector3 dir, Sphere s) -{ - Vector3 v = s.center - from; - f32 b = math::dot(v, dir); - f32 det = (s.radius * s.radius) - math::dot(v, v) + (b * b); - - if (det < 0.0 || b < s.radius) - return -1.0f; - return b - math::sqrt(det); -} -} // namespace sphere - -namespace plane -{ -inline f32 -ray_intersection(Vector3 from, Vector3 dir, Plane p) -{ - f32 nd = math::dot(dir, p.normal); - f32 orpn = math::dot(from, p.normal); - f32 dist = -1.0f; - - if (nd < 0.0f) - dist = (-p.distance - orpn) / nd; - - return dist > 0.0f ? dist : -1.0f; -} - -inline bool -intersection3(Plane p1, Plane p2, Plane p3, Vector3* ip) -{ - f32 den = -math::dot(math::cross(p1.normal, p2.normal), p3.normal); - - if (math::equals(den, 0.0f)) - return false; - - Vector3 res = p1.distance * math::cross(p2.normal, p3.normal) - + p2.distance * math::cross(p3.normal, p1.normal) - + p3.distance * math::cross(p1.normal, p2.normal); - *ip = res / den; - - return true; -} -} // namespace plane - -#if !defined(GB_MATH_NO_RANDOM) -namespace random -{ -inline Random -make(s64 seed) -{ - Random r = {}; - set_seed(&r, seed); - return r; -} - -void -set_seed(Random* r, s64 seed) -{ - r->seed = seed; - r->mt[0] = seed; - for (u64 i = 1; i < 312; i++) - r->mt[i] = 6364136223846793005ull * (r->mt[i-1] ^ r->mt[i-1] >> 62) + i; -} - -s64 -next(Random* r) -{ - const u64 MAG01[2] = {0ull, 0xb5026f5aa96619e9ull}; - - u64 x; - if (r->index > 312) - { - u32 i = 0; - for (; i < 312-156; i++) - { - x = (r->mt[i] & 0xffffffff80000000ull) | (r->mt[i+1] & 0x7fffffffull); - r->mt[i] = r->mt[i+156] ^ (x>>1) ^ MAG01[(u32)(x & 1ull)]; - } - for (; i < 312-1; i++) - { - x = (r->mt[i] & 0xffffffff80000000ull) | (r->mt[i+1] & 0x7fffffffull); - r->mt[i] = r->mt[i + (312-156)] ^ (x >> 1) ^ MAG01[(u32)(x & 1ull)]; - } - x = (r->mt[312-1] & 0xffffffff80000000ull) | (r->mt[0] & 0x7fffffffull); - r->mt[312-1] = r->mt[156-1] ^ (x>>1) ^ MAG01[(u32)(x & 1ull)]; - - r->index = 0; - } - - x = r->mt[r->index++]; - - x ^= (x >> 29) & 0x5555555555555555ull; - x ^= (x << 17) & 0x71d67fffeda60000ull; - x ^= (x << 37) & 0xfff7eee000000000ull; - x ^= (x >> 43); - - return x; -} - -void -next_from_device(void* buffer, u32 length_in_bytes) -{ -#if defined(GB_SYSTEM_WINDOWS) - HCRYPTPROV prov; - - bool ok = CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); - GB_ASSERT(ok, "CryptAcquireContext"); - ok = CryptGenRandom(prov, length_in_bytes, reinterpret_cast(&buffer)); - GB_ASSERT(ok, "CryptGenRandom"); - - CryptReleaseContext(prov, 0); - -#else - #error Implement random::next_from_device() -#endif -} - -inline s32 -next_s32(Random* r) -{ - return bit_cast(random::next(r)); -} - -inline u32 -next_u32(Random* r) -{ - return bit_cast(random::next(r)); -} - -inline f32 -next_f32(Random* r) -{ - return bit_cast(random::next(r)); -} - -inline s64 -next_s64(Random* r) -{ - return random::next(r); -} - -inline u64 -next_u64(Random* r) -{ - return bit_cast(random::next(r)); -} - -inline f64 -next_f64(Random* r) -{ - return bit_cast(random::next(r)); -} - -inline s32 -uniform_s32(Random* r, s32 min_inc, s32 max_inc) -{ - return (random::next_s32(r) & (max_inc - min_inc + 1)) + min_inc; -} - -inline u32 -uniform_u32(Random* r, u32 min_inc, u32 max_inc) -{ - return (random::next_u32(r) & (max_inc - min_inc + 1)) + min_inc; -} - -inline f32 -uniform_f32(Random* r, f32 min_inc, f32 max_inc) -{ - f64 n = (random::next_s64(r) >> 11) * (1.0/4503599627370495.0); - return static_cast(n * (max_inc - min_inc + 1.0) + min_inc); -} - -inline s64 -uniform_s64(Random* r, s64 min_inc, s64 max_inc) -{ - return (random::next_s32(r) & (max_inc - min_inc + 1)) + min_inc; -} - -inline u64 -uniform_u64(Random* r, u64 min_inc, u64 max_inc) -{ - return (random::next_u64(r) & (max_inc - min_inc + 1)) + min_inc; -} - -inline f64 -uniform_f64(Random* r, f64 min_inc, f64 max_inc) -{ - f64 n = (random::next_s64(r) >> 11) * (1.0/4503599627370495.0); - return (n * (max_inc - min_inc + 1.0) + min_inc); -} - - -global_variable const s32 g_perlin_randtab[512] = -{ - 23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123, - 152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72, - 175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240, - 8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57, - 225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233, - 94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172, - 165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243, - 65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122, - 26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76, - 250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246, - 132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3, - 91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231, - 38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221, - 131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62, - 27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135, - 61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5, - -// Copy - 23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123, - 152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72, - 175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240, - 8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57, - 225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233, - 94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172, - 165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243, - 65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122, - 26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76, - 250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246, - 132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3, - 91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231, - 38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221, - 131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62, - 27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135, - 61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5, -}; - - -internal_linkage f32 -perlin_grad(s32 hash, f32 x, f32 y, f32 z) -{ - local_persist const f32 basis[12][4] = - { - { 1, 1, 0}, - {-1, 1, 0}, - { 1,-1, 0}, - {-1,-1, 0}, - { 1, 0, 1}, - {-1, 0, 1}, - { 1, 0,-1}, - {-1, 0,-1}, - { 0, 1, 1}, - { 0,-1, 1}, - { 0, 1,-1}, - { 0,-1,-1}, - }; - - local_persist const u8 indices[64] = - { - 0,1,2,3,4,5,6,7,8,9,10,11, - 0,9,1,11, - 0,1,2,3,4,5,6,7,8,9,10,11, - 0,1,2,3,4,5,6,7,8,9,10,11, - 0,1,2,3,4,5,6,7,8,9,10,11, - 0,1,2,3,4,5,6,7,8,9,10,11, - }; - - const f32* grad = basis[indices[hash & 63]]; - return grad[0]*x + grad[1]*y + grad[2]*z; -} - - -inline f32 -perlin_3d(f32 x, f32 y, f32 z, s32 x_wrap, s32 y_wrap, s32 z_wrap) -{ - u32 x_mask = (x_wrap-1) & 255; - u32 y_mask = (y_wrap-1) & 255; - u32 z_mask = (z_wrap-1) & 255; - - s32 px = static_cast(math::floor(x)); - s32 py = static_cast(math::floor(y)); - s32 pz = static_cast(math::floor(z)); - - s32 x0 = (px) & x_mask; - s32 x1 = (px+1) & x_mask; - s32 y0 = (py) & y_mask; - s32 y1 = (py+1) & y_mask; - s32 z0 = (pz) & z_mask; - s32 z1 = (pz+1) & z_mask; - - x -= px; - y -= py; - z -= pz; - -#define GB__PERLIN_EASE(t) (((6*t - 15)*t + 10)*t*t*t) - f32 u = GB__PERLIN_EASE(x); - f32 v = GB__PERLIN_EASE(y); - f32 w = GB__PERLIN_EASE(z); -#undef GB__PERLIN_EASE - - s32 r0 = g_perlin_randtab[x0]; - s32 r1 = g_perlin_randtab[x1]; - - s32 r00 = g_perlin_randtab[r0 + y0]; - s32 r01 = g_perlin_randtab[r0 + y1]; - s32 r10 = g_perlin_randtab[r1 + y0]; - s32 r11 = g_perlin_randtab[r1 + y1]; - - f32 n000 = perlin_grad(g_perlin_randtab[r00 + z0], x, y, z ); - f32 n001 = perlin_grad(g_perlin_randtab[r00 + z1], x, y, z - 1); - f32 n010 = perlin_grad(g_perlin_randtab[r01 + z0], x, y - 1, z ); - f32 n011 = perlin_grad(g_perlin_randtab[r01 + z1], x, y - 1, z - 1); - f32 n100 = perlin_grad(g_perlin_randtab[r10 + z0], x - 1, y, z ); - f32 n101 = perlin_grad(g_perlin_randtab[r10 + z1], x - 1, y, z - 1); - f32 n110 = perlin_grad(g_perlin_randtab[r11 + z0], x - 1, y - 1, z ); - f32 n111 = perlin_grad(g_perlin_randtab[r11 + z1], x - 1, y - 1, z - 1); - - f32 n00 = math::lerp(n000, n001, w); - f32 n01 = math::lerp(n010, n011, w); - f32 n10 = math::lerp(n100, n101, w); - f32 n11 = math::lerp(n110, n111, w); - - f32 n0 = math::lerp(n00, n01, v); - f32 n1 = math::lerp(n10, n11, v); - - return math::lerp(n0, n1, u); -} - -} // namespace random -#endif - -__GB_NAMESPACE_END - -#endif // GB_MATH_IMPLEMENTATION