/*============================================================================================================== * Copyright (c) 2020 John Jackson * Gunslinger: A simple, header-only c99 multi-media framework * File: gs.h * Github: https://github.com/MrFrenik/gunslinger * All Rights Reserved * MIT License * May all those that this source may reach be blessed by the LORD and find peace and joy in life. * Everyone who drinks of this water will be thirsty again; but whoever drinks of the water * that I will give him shall never thirst; John 4:13-14 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated * documentation files (the "Software"), to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE WARRANTIES OF MECHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. =================================================================================================================*/ #ifndef GS_H #define GS_H /*═█═════════════════════════════════════════════════════════════════════════════════════█═╗ ████ ██████╗ ██╗ ██╗███╗ ██╗███████╗██╗ ██╗███╗ ██╗ ██████╗ ███████╗██████╗ ██████═█ █║█ ██╔════╝ ██║ ██║████╗ ██║██╔════╝██║ ██║████╗ ██║██╔════╝ ██╔════╝██╔══██╗ ██═████ ███ ██║ ███╗██║ ██║██╔██╗ ██║███████╗██║ ██║██╔██╗ ██║██║ ███╗█████╗ ██████╔╝ █████═██ ╚██ ██║ ██║██║ ██║██║╚██╗██║╚════██║██║ ██║██║╚██╗██║██║ ██║██╔══╝ ██╔══██╗ ███ █╝█ █║█ ╚██████╔╝╚██████╔╝██║ ╚████║███████║███████╗██║██║ ╚████║╚██████╔╝███████╗██║ ██║ ██═████ ████ ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ █═█═██ ╚═██════════════════════════════════════════════════════════════════════════════════════██═╝*/ /* Gunslinger is a header-only, c99 framework for multi-media applications and tools. USAGE: (IMPORTANT) ================================================================================================================= Before including, define the gunslinger implementation like this: #define GS_IMPL in EXACTLY ONE C or C++ file that includes this header, BEFORE the include, like this: #define GS_IMPL #include "gs.h" All other files should just #include "gs.h" without the #define. (Thanks to SB for the template for this instructional message. I was too lazy to come up with my own wording.) NOTE: All main interface stuff in here, then system implementations can be in separate header files All provided implementations will be in impl/xxx.h This is just to keep everything from being in one huge file ================================================================================================================ Contained within (Contents): * GS_APPLICATION * GS_UTIL * GS_CONTAINERS * GS_ASSET_TYPES * GS_MATH * GS_LEXER * GS_PLATFORM * GS_GRAPHICS * GS_AUDIO ================================================================================================================ GS_APPLICATION: Gunslinger is a framework that expects to take flow control over your app and calls into your code at certain sync points. These points are 'init', 'update', and 'shutdown'. When creating your application, you provide certain information to the framework via a `gs_app_desc_t` descriptor object. Gunslinger acts as the main entry point to your application, so instead of defining "main" as you typically would for a c/c++ program, you instead implement the `gs_main` function that will be called by gunslinger. This last bit is optional and a different method for entry is covered, however this is the most convenient way to use the framework. Basic Example Application: #define GS_IMPL #include "gs.h" void my_init(void* app) { // Do your initialization } void my_update(void* app) { // Do your updates } void my_shutdown(void* app) { // Do your shutdown } gs_app_desc_t gs_main(int32_t argc, char** argv) { return (gs_app_desc_t) { .init = my_init, // Function pointer to call into your init code .update = my_update, // Function pointer to call into your update code .shutdown = my_shutdown // Function pointer to call into your shutdown code }; } If you do not provide information for any of this information, defaults will be provided by the framework. Therefore, it is possible to return a completely empty app descriptor back to the framework to run: gs_app_desc_t gs_main(int32_t argc, char** argv) { return (gs_app_desc_t){0}; } NOTE: In addition to function callbacks, there are quite a few options available to provide for this descriptor, such as window width, window height, window flags, window title, etc. Refer to the section for gs_app_desc_t further in the code for all provided options. It's also possible to define GS_NO_HIJACK_MAIN for your application. This will make it so gunslinger will not be the main entry point to your application. You will instead be responsible for creating a gunslinger instance and passing in your application description to it. #define GS_NO_HIJACK_MAIN int32_t main(int32_t argv, char** argc) { gs_app_desc_t app = {0}; // Fill this with whatever your app needs gs_create(app); // Create instance of framework and run while (gs_app()->is_running) { gs_frame(); } return 0; } NOTE: Lastly, while it is possible to use gunslinger without it controlling the main application loop, this isn't recommended. Internally, gunslinger does its best to handle the boiler plate drudge work of implementing (in correct order) the various layers required for a basic hardware accelerated multi-media application program to work. This involves allocating memory for internal data structures for these layers as well initializing them in a particular order so they can inter-operate as expected. If you're interested in taking care of this yourself, look at the `gs_frame()` function to get a feeling for how this is being handled. GS_MATH: Gunslinger includes utilities for common graphics/game related math structures. These aren't required to be used, however they are used internally for certain function calls in various APIs. Where possible, I've tried to add as much redundancy as possible to the APIs so that these structures are not required if you prefer. * common utils: - interpolation * Vectors: - gs_vec2: float[2] - gs_vec3: float[3] - gs_vec4: float[4] * vector ops: - dot product - normalize - add/subtract (component wise) - multiply - cross product - length *Quaternions: - gs_quat: float[4] * quaternion ops: - add/subtract/multiply - inverse - conjugate - angle/axis | axis/angle - to euler | from euler - to mat4 *Mat4x4: - gs_mat4: float[16] * mat4 ops: - add/subtract/multiply - transpose - inverse - homogenous transformations: - rotation - scale - translate - view: - lookat - projection: - orthographic - perspective *VQS: - gs_vqs: gs_vec3, gs_quat, gs_vec3 (SPECIAL NOTE): `gs_vqs` is a transform structure that's commonly used in games/physics sims, especially with complex child/parent hierarchies. It stands for `Vector-Quaternion-Scalar` to denote its internal components. Typically this encodes position, rotation (in quaternion), and a uniform scale for transformation. Gunslinger uses non-uniform scale in the form of a gs_vec3. gs_vqs xform = {0}; xform.position = gs_v3(...); xform.rotation = gs_quat(...); xform.scale = gs_v3(...); This structure can then be converted into a final form float[16] mat4x4 for any typical homogenous graphics transformations: gs_mat4 model = gs_vqs_to_mat4(&xform); The real power in VQS transforms is the ability to easily encode parent/child hierarchies. This is done using the two functions `gs_vqs_absolute_transform` and `gs_vqs_relative_transform`: gs_vqs parent = ...; gs_vqs child = ...; { gs_vqs relative = gs_vqs_relative_transform(&child, &parent); // Get relative transform with respect to parent gs_vqs absolute = gs_vqs_absolute_transform(&local, &parent); // Get absolute transform with respect to local GS_UTIL: memory allocation hashing(32/64 bit) siphash (hash generic bytes) string utils file utils GS_CONTAINERS: gs_dyn_array: Inspired GREATLY from Shawn Barret's "stretchy buffer" implementation. A generic, dynamic array of type T, which is defined by the user: gs_dyn_array(float) arr = NULL; // Dynamic array of type float This works by using the macro `gs_dyn_array(T)`, which evaluates to `T*`. The dynamic array stores a bit of header information right before the actual array in memory for a table to describe properties of the array: [header][actual array data] The header is a structure of uint32_t[2]: typedef struct gs_array_header_t { uint32_t size; uint32_t capacity; } gs_array_header_t; The array can be randomly accessed using the [] operator, just like any regular c array. There are also provided functions for accessing information using this provided table. This dynamic structure is the baseline for all other containers provided in gunslinger. Array Usage: gs_dyn_array(float) arr = NULL; // Create dynamic array of type float. gs_dyn_array_push(arr, 10.f); // Push value into back of array. Will dynamically grow/initialize on demand. float v = arr[0]; // Gets value of array data at index 0; float* vp = &arr[0]; // Gets pointer reference of array data at index 0; uint32_t sz = gs_dyn_array_size(arr); // Gets size of array. Return 0 if NULL. uint32_t cap = gs_dyn_array_capacity(arr); // Gets capacity of array. Return 0 if NULL. bool is_empty = gs_dyn_array_empty(arr); // Returns whether array is empty. Return true if NULL. gs_dyn_array_reserve(arr, 10); // Reserves internal space in the array for N, non-initialized elements. gs_dyn_array_clear(arr); // Clears all elements. Simply sets array size to 0. gs_dyn_array_free(arr); // Frees array data calling `gs_free` internally. gs_hash_table: generic hash table of key:K and val:V Inspired GREATLY from Shawn Barret's "stb_ds.h" implementation. A generic hash table of K,V which is defined by the user: gs_hash_table(uint32_t, float) ht = NULL; // Creates a hash table with K = uint32_t, V = float Internally, the hash table uses a 64-bit siphash to hash generic byte data to an unsigned 64-bit key. This means it's possible to pass up arbitrary data to the hash table and it will hash accordingly, such as structures: typedef struct key_t { uint32_t id0; uint64_t id1; } key_t; gs_hash_table(key_t, float) ht = NULL; // Create hash table with K = key_t, V = float Inserting into the array with "complex" types is as simple as: key_t k = {.ido0 = 5, .id1 = 32}; // Create structure for "key" gs_hash_table_insert(ht, k, 5.f); // Insert into hash table using key float v = gs_hash_table_get(ht, k); // Get data at key It is possible to return a reference to the data using `gs_hash_table_getp()`. However, keep in mind that this comes with the danger that the reference could be lost IF the internal data array grows or shrinks in between you caching the pointer and using it. float* val = gs_hash_table_getp(ht, k); // Cache pointer to internal data. Dangerous game. gs_hash_table_insert(ht, new_key); // At this point, your pointer could be invalidated due to growing internal array. Hash tables provide iterators to iterate the data: for ( gs_hash_table_iter it = 0; gs_hash_table_iter_valid(ht, it); gs_hash_table_iter_advance(ht, it) ) { float v = gs_hash_table_iter_get(ht, it); // Get value using iterator float* vp = gs_hash_table_iter_getp(ht, it); // Get value pointer using iterator key_t k = gs_hash_table_iter_getk(ht, it); // Get key using iterator key_t* kp = gs_hash_table_iter_getkp(ht, it); // Get key pointer using iterator } Hash Table Usage: gs_hash_table(uint32_t, float) ht = NULL; // Create hash table with key = uint32_t, val = float gs_hash_table_insert(ht, 64, 3.145f); // Insert key/val pair {64, 3.145f} into hash table. Will dynamically grow/init on demand. bool exists = gs_hash_table_key_exists(ht, 64); // Use to query whether or not a key exists in the table. float v = gs_hash_table_get(ht, 64); // Get value at key = 64. Will crash if not available. float* vp = gs_hash_table_get(ht, 64); // Get pointer reference to data at key = 64. Will crash if not available. bool is_empty = gs_hash_table_empty(ht); // Returns whether hash table is empty. Returns true if NULL. uint32_t sz = gs_hash_table_size(ht); // Get size of hash table. Returns 0 if NULL. uint32_t cap = gs_hash_table_capacity(ht); // Get capacity of hash table. Returns 0 if NULL. gs_hash_table_clear(ht); // Clears all elements. Sets size to 0. gs_hash_table_free(ht); // Frees hash table internal data calling `gs_free` internally. gs_slot_array: Slot arrays are internally just dynamic arrays but alleviate the issue with losing references to internal data when the arrays grow. Slot arrays therefore hold two internals arrays: gs_dyn_array(T) your_data; gs_dyn_array(uint32_t) indirection_array; The indirection array takes an opaque uint32_t handle and then dereferences it to find the actual index for the data you're interested in. Just like dynamic arrays, they are NULL initialized and then allocated/initialized internally upon use: gs_slot_array(float) arr = NULL; // Slot array with internal 'float' data uint32_t hndl = gs_slot_array_insert(arr, 3.145f); // Inserts your data into the slot array, returns handle to you float val = gs_slot_array_get(arr, hndl); // Returns copy of data to you using handle as lookup It is possible to return a reference to the data using `gs_slot_array_getp()`. However, keep in mind that this comes with the danger that the reference could be lost IF the internal data array grows or shrinks in between you caching the pointer and using it. float* val = gs_slot_array_getp(arr, hndl); // Cache pointer to internal data. Dangerous game. gs_slot_array_insert(arr, 5.f); // At this point, your pointer could be invalidated due to growing internal array. Slot arrays provide iterators to iterate the data: for ( gs_slot_array_iter it = 0; gs_slot_array_iter_valid(sa, it); gs_slot_array_iter_advance(sa, it) ) { float v = gs_slot_array_iter_get(sa, it); // Get value using iterator float* vp = gs_slot_array_iter_getp(sa, it); // Get value pointer using iterator } Slot Array Usage: gs_slot_array(float) sa = NULL; // Create slot array with internal 'float' data uint32_t hndl = gs_slot_array_insert(sa, 3.145); // Insert data into slot array. Returns uint32_t handle. Init/Grow on demand. float v = gs_slot_array_get(sa, hndl); // Get data at hndl. float* vp = gs_slot_array_getp(sa, hndl); // Get pointer reference at hndl. Dangerous. uint32_t sz = gs_slot_array_size(sa); // Size of slot array. Returns 0 if NULL. uint32_t cap = gs_slot_array_capacity(sa); // Capacity of slot array. Returns 0 if NULL. gs_slot_array_empty(sa); // Returns whether slot array is empty. Returns true if NULL. gs_slot_array_clear(sa); // Clears array. Sets size to 0. gs_slot_array_free(sa); // Frees array memory. Calls `gs_free` internally. gs_slot_map: Works exactly the same, functionally, as gs_slot_array, however allows the user to use one more layer of indirection by hashing any data as a key type. Also worth note, the slot map does not return a handle to the user. Instead, the user is expected to use the key to access data. gs_slot_map(float, uint64_t) sm = NULL; // Slot map with key = float, val = uint64_t gs_slot_map_insert(sm, 1.f, 32); // Insert data into slot map. uint64_t v = gs_slot_map_get(sm, 1.f); // Returns copy of data to you at key `1.f` Like the slot array, it is possible to return a reference via pointer using `gs_slot_map_getp()`. Again, this comes with the same danger of losing references if not careful about growing the internal data. uint64_t* v = gs_slot_map_getp(sm, 1.f); // Cache pointer to data gs_slot_map_insert(sm, 2.f, 10); // Possibly have just invalidated your previous pointer Slot maps provide iterators to iterate the data: for ( gs_slot_map_iter it = 0; gs_slot_map_iter_valid(sm, it); gs_slot_map_iter_advance(sm, it) ) { uint64_t v = gs_slot_map_iter_get(sm, it); // Get value using iterator uint64_t* vp = gs_slot_map_iter_getp(sm, it); // Get value pointer using iterator float k = gs_slot_map_iter_get_key(sm, it); // Get key using iterator float* kp = gs_slot_map_iter_get_keyp(sm, it); // Get key pointer using iterator } Slot Map Usage: gs_slot_map(float, uint64_t) sm = NULL; // Create slot map with K = float, V = uint64_t uint32_t hndl = gs_slot_map_insert(sm, 3.145f, 32); // Insert data into slot map. Init/Grow on demand. uint64_t v = gs_slot_map_get(sm, 3.145f); // Get data at key. uint64_t* vp = gs_slot_map_getp(sm, 3.145f); // Get pointer reference at hndl. Dangerous. uint32_t sz = gs_slot_map_size(sm); // Size of slot map. Returns 0 if NULL. uint32_t cap = gs_slot_map_capacity(sm); // Capacity of slot map. Returns 0 if NULL. gs_slot_map_empty(sm); // Returns whether slot map is empty. Returns true if NULL. gs_slot_map_clear(sm); // Clears map. Sets size to 0. gs_slot_map_free(sm); // Frees map memory. Calls `gs_free` internally. GS_PLATFORM: By default, Gunslinger supports (via included GLFW) the following platforms: - Win32 - OSX - Linux To define your own custom implementation and not use the included glfw implementation, define GS_PLATFORM_IMPL_CUSTOM in your project. Gunslinger will see this and leave the implementation of the platform API up to you: // For custom platform implementation #define GS_PLATFORM_IMPL_CUSTOM Internally, the platform interface holds the following data: gs_platform_settings_t settings; // Settings for platform, including video driver settings gs_platform_time_t time; // Time structure, used to query frame time, delta time, render time gs_platform_input_t input; // Input struture, used to query input state for mouse, keyboard, controllers gs_slot_array(void*) windows; // Slot array of raw platform window data and handles void* cursors[GS_PLATFORM_CURSOR_COUNT]; // Raw platform cursors void* user_data; // Specific user data (for custom implementations) Upon creation, the framework will create a main window for you and then store its handle with slot id 0. All platform related window query functions require passing in an opaque uint32_t handle to get the actual data internally. There is a convenience function available for querying the main window handle created for you: uint32_t main_window_hndl = gs_platform_main_window(); Internally, the platform layer queries the platform backend and updates its exposed gs_platform_input_t data structure for you to use. Several utility functions exist: gs_platform_key_down(gs_platform_keycode) // Checks to see if a key is held down (pressed last frame and this frame) gs_platform_key_released(gs_platform_keycode) // Checks to see if a key was released this frame gs_platform_key_pressed(gs_platform_keycode) // Checks to see if a key was pressed this frame (not pressed last frame) gs_platform_mouse_down(gs_platform_mouse_button_code) // Checks to see if mouse button is held down gs_platform_mouse_pressed(gs_platform_mouse_button_code) // Checks to see if mouse button was pressed this frame (not pressed last frame) gs_platform_mouse_released(gs_platform_mouse_button_code) // Checks to see if mouse button was released this frame GS_AUDIO: By default, Gunslinger includes and uses miniaudio for its audio backend. To define your own custom implementation and not use the included miniaudio implementation, define GS_AUDIO_IMPL_CUSTOM in your project. Gunslinger will see this and leave the implementation of the audio API up to you: // For custom audio implementation #define GS_AUDIO_IMPL_CUSTOM GS_GRAPHICS: // For custom graphics implementation #define GS_GRAPHICS_IMPL_CUSTOM */ /*===== Gunslinger Include ======*/ // #ifdef __cplusplus // extern "C" { // #endif /*======================== // Defines ========================*/ #include // valist #include // ptrdiff_t #include // malloc, realloc, free #include // standard types #include // INT32_MAX, UINT32_MAX #include // memset #include // FLT_MAX #include // FILE #include // time #include // tolower #include // floor, acos, sin, sqrt, tan #include // assert #include // alloca/_alloca /*=========================================================== // gs_inline, gs_global, gs_local_persist, gs_force_inline ===========================================================*/ #ifndef gs_inline #define gs_inline static inline #endif #ifndef gs_local_persist #define gs_local_persist static #endif #ifndef gs_global #define gs_global static #endif #if (defined _WIN32 || defined _WIN64) #define gs_force_inline gs_inline #elif (defined __APPLE__ || defined _APPLE) #define gs_force_inline static __attribute__((always_inline)) #else #define gs_force_inline gs_inline #endif #define GS_INLINE gs_force_inline #define GS_GLOBAL gs_global #define GS_LOCAL_PERSIST gs_local_persist #ifdef __cplusplus #pragma warning(disable:4996) #endif /*=================== // GS_API_DECL ===================*/ #ifdef GS_API_DLL_EXPORT #ifdef __cplusplus #define GS_API_EXTERN extern "C" __declspec(dllexport) #else #define GS_API_EXTERN extern __declspec(dllexport) #endif #else #ifdef __cplusplus #define GS_API_EXTERN extern "C" #else #define GS_API_EXTERN extern #endif #endif #define GS_API_DECL GS_API_EXTERN #define GS_API_PRIVATE GS_API_EXTERN /*=================== // PLATFORM DEFINES ===================*/ /* Platform Android */ #if (defined __ANDROID__) #define GS_PLATFORM_ANDROID /* Platform Apple */ #elif (defined __APPLE__ || defined _APPLE) #define GS_PLATFORM_APPLE /* Platform Windows */ #elif (defined _WIN32 || defined _WIN64) #define __USE_MINGW_ANSI_STDIO 1 // Necessary windows defines before including windows.h, because it's retarded. #define OEMRESOURCE #define GS_PLATFORM_WIN #define GS_PLATFORM_WINDOWS #include #define WIN32_LEAN_AND_MEAN /* Platform Linux */ #elif (defined linux || defined _linux || defined __linux__) #define GS_PLATFORM_LINUX /* Platform Emscripten */ #elif (defined __EMSCRIPTEN__) #define GS_PLATFORM_WEB /* Else - Platform Undefined and Unsupported or custom */ #endif /*============================================================ // C primitive types ============================================================*/ #ifndef __cplusplus #define false 0 #define true 1 #endif #ifdef __cplusplus typedef bool b8; #else #ifndef __bool_true_false_are_defined typedef _Bool bool; #endif typedef bool b8; #endif typedef size_t usize; typedef uint8_t u8; typedef int8_t s8; typedef uint16_t u16; typedef int16_t s16; typedef uint32_t u32; typedef int32_t s32; typedef s32 b32; typedef uint64_t u64; typedef int64_t s64; typedef float f32; typedef double f64; typedef const char* const_str; typedef int32_t bool32_t; typedef float float32_t; typedef double float64_t; typedef bool32_t bool32; #define uint16_max UINT16_MAX #define uint32_max UINT32_MAX #define int32_max INT32_MAX #define float_max FLT_MAX #define float_min FLT_MIN /*============================================================ // gs utils ============================================================*/ /** @defgroup gs_util Common Utils * Gunslinger Common Utils * @{ */ // Helper macro for compiling to nothing #define gs_empty_instruction(...) #define gs_array_size(__ARR) sizeof(__ARR) / sizeof(__ARR[0]) #ifndef gs_assert #define gs_assert assert #endif #if defined (__cplusplus) #define gs_default_val() {} #else #define gs_default_val() {0} #endif // Helper macro for an in place for-range loop #define gs_for_range_i(__COUNT)\ for (uint32_t i = 0; i < __COUNT; ++i) // Helper macro for an in place for-range loop #define gs_for_range_j(__COUNT)\ for (uint32_t j = 0; j < __COUNT; ++j) #define gs_for_range(__COUNT)\ for(uint32_t gs_macro_token_paste(__T, __LINE__) = 0;\ gs_macro_token_paste(__T, __LINE__) < __COUNT;\ ++(gs_macro_token_paste(__T, __LINE__))) #define gs_max(A, B) ((A) > (B) ? (A) : (B)) #define gs_min(A, B) ((A) < (B) ? (A) : (B)) #define gs_clamp(V, MIN, MAX) ((V) > (MAX) ? (MAX) : (V) < (MIN) ? (MIN) : (V)) #define gs_is_nan(V) ((V) != (V)) // Helpful macro for casting one type to another #define gs_cast(A, B) ((A*)(B)) #ifdef __cplusplus #define gs_ctor(TYPE, ...) (TYPE {__VA_ARGS__}) #else #define gs_ctor(TYPE, ...) ((TYPE){__VA_ARGS__}) #endif // Helpful marco for calculating offset (in bytes) of an element from a given structure type #define gs_offset(TYPE, ELEMENT) ((size_t)(&(((TYPE*)(0))->ELEMENT))) // macro for turning any given type into a const char* of itself #define gs_to_str(TYPE) ((const char*)#TYPE) #define gs_macro_token_paste(X, Y) X##Y #define gs_macro_cat(X, Y) gs_macro_token_paste(X, Y) #define gs_timed_action(INTERVAL, ...)\ do {\ static uint32_t gs_macro_cat(gs_macro_cat(__T, __LINE__), t) = 0;\ if (gs_macro_cat(gs_macro_cat(__T, __LINE__), t)++ > INTERVAL) {\ gs_macro_cat(gs_macro_cat(__T, __LINE__), t) = 0;\ __VA_ARGS__\ }\ } while (0) #define gs_int2voidp(I) (void*)(uintptr_t)(I) #define gs_if(INIT, CHECK, ...)\ do {\ INIT;\ if (CHECK)\ {\ __VA_ARGS__\ }\ } while (0) //=== Logging ===// #define gs_log_info(MESSAGE, ...) gs_println("LOG::%s::%s(%zu)::" MESSAGE, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) #define gs_log_success(MESSAGE, ...) gs_println("SUCCESS::%s::%s(%zu)::" MESSAGE, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) #define gs_log_warning(MESSAGE, ...) gs_println("WARNING::%s::%s(%zu)::" MESSAGE, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) #define gs_log_error(MESSAGE, ...) do {gs_println("ERROR::%s::%s(%zu)::" MESSAGE, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__);\ gs_assert(false);\ } while (0) /*=================================== // Memory Allocation Utils ===================================*/ // Operating system function pointer typedef struct gs_os_api_s { void* (* malloc)(size_t sz); void (* free)(void* ptr); void* (* realloc)(void* ptr, size_t sz); void* (* calloc)(size_t num, size_t sz); void* (* alloca)(size_t sz); void* (* malloc_init)(size_t sz); char* (* strdup)(const char* str); } gs_os_api_t; // TODO(john): Check if all defaults need to be set, in case gs context will not be used GS_API_DECL void* _gs_malloc_init_impl(size_t sz); // Default memory allocations #ifndef GS_NO_OS_MEMORY_ALLOC_DEFAULT #define gs_malloc malloc #define gs_free free #define gs_realloc realloc #define gs_calloc calloc #define gs_alloca malloc #define gs_malloc_init(__T) (__T*)_gs_malloc_init_impl(sizeof(__T)) #endif GS_API_DECL gs_os_api_t gs_os_api_new_default(); #ifndef gs_os_api_new #define gs_os_api_new gs_os_api_new_default #endif #ifndef gs_malloc #define gs_malloc(__SZ) (gs_ctx()->os.malloc(__SZ)) #endif #ifndef gs_malloc_init #define gs_malloc_init(__T) ((__T*)gs_ctx()->os.malloc_init(sizeof(__T))) #endif #ifndef gs_free #define gs_free(__MEM) (gs_ctx()->os.free(__MEM)) #endif #ifndef gs_realloc #define gs_realloc(__MEM, __AZ) (gs_ctx()->os.realloc(__MEM, __AZ)) #endif #ifndef gs_calloc #define gs_calloc(__NUM, __SZ) (gs_ctx()->os.calloc(__NUM, __SZ)) #endif #ifndef gs_alloca #define gs_alloca(__SZ) (gs_ctx()->os.alloca(__SZ)) #endif #ifndef gs_strdup #define gs_strdup(__STR) (gs_ctx()->os.strdup(__STR)) #endif // Modified from: https://stackoverflow.com/questions/11815894/how-to-read-write-arbitrary-bits-in-c-c #define gs_bit_mask(INDEX, SIZE)\ (((1u << (SIZE)) - 1u) << (INDEX)) #define gs_write_bits(DATA, INDEX, SIZE, VAL)\ ((DATA) = (((DATA) & (~BIT_MASK((INDEX), (SIZE)))) | (((VAL) << (INDEX)) & (BIT_MASK((INDEX), (SIZE)))))) #define gs_read_bits(DATA, INDEX, SIZE)\ (((DATA) & BIT_MASK((INDEX), (SIZE))) >> (INDEX)) /*============================================================ // Result ============================================================*/ typedef enum gs_result { GS_RESULT_SUCCESS, GS_RESULT_IN_PROGRESS, GS_RESULT_INCOMPLETE, GS_RESULT_FAILURE } gs_result; /*=================================== // Resource Handles ===================================*/ // Useful typedefs for typesafe, internal resource handles #define gs_handle(TYPE)\ gs_handle_##TYPE #define gs_handle_decl(TYPE)\ typedef struct {uint32_t id;} gs_handle(TYPE);\ gs_inline\ gs_handle(TYPE) gs_handle_invalid_##TYPE()\ {\ gs_handle(TYPE) h;\ h.id = UINT32_MAX;\ return h;\ }\ \ gs_inline\ gs_handle(TYPE) gs_handle_create_##TYPE(uint32_t id)\ {\ gs_handle(TYPE) h;\ h.id = id;\ return h;\ } #define gs_handle_invalid(__TYPE)\ gs_handle_invalid_##__TYPE() #define gs_handle_create(__TYPE, __ID)\ gs_handle_create_##__TYPE(__ID) #define gs_handle_is_valid(HNDL)\ ((HNDL.id) != UINT32_MAX) /*=================================== // Color ===================================*/ #define gs_hsv(...) gs_hsv_ctor(__VA_ARGS__) #define gs_color(...) gs_color_ctor(__VA_ARGS__) typedef struct gs_hsv_t { union { float hsv[3]; struct { float h, s, v; }; }; } gs_hsv_t; gs_force_inline gs_hsv_t gs_hsv_ctor(float h, float s, float v) { gs_hsv_t hsv; hsv.h = h; hsv.s = s; hsv.v = v; return hsv; } typedef struct gs_color_t { union { uint8_t rgba[4]; struct { uint8_t r, g, b, a; }; }; } gs_color_t; gs_force_inline gs_color_t gs_color_ctor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { gs_color_t color; color.r = r; color.g = g; color.b = b; color.a = a; return color; } #define GS_COLOR_BLACK gs_color(0, 0, 0, 255) #define GS_COLOR_WHITE gs_color(255, 255, 255, 255) #define GS_COLOR_RED gs_color(255, 0, 0, 255) #define GS_COLOR_GREEN gs_color(0, 255, 0, 255) #define GS_COLOR_BLUE gs_color(0, 0, 255, 255) #define GS_COLOR_ORANGE gs_color(255, 100, 0, 255) #define GS_COLOR_YELLOW gs_color(255, 255, 0, 255) #define GS_COLOR_PURPLE gs_color(128, 0, 128, 255) #define GS_COLOR_MAROON gs_color(128, 0, 0, 255) #define GS_COLOR_BROWN gs_color(165, 42, 42, 255) #define GS_COLOR_MAGENTA gs_color(255, 0, 255, 255) gs_force_inline gs_color_t gs_color_alpha(gs_color_t c, uint8_t a) { return gs_color(c.r, c.g, c.b, a); } gs_force_inline gs_hsv_t gs_rgb2hsv(gs_color_t in) { float ir = (float)in.r / 255.f; float ig = (float)in.g / 255.f; float ib = (float)in.b / 255.f; float ia = (float)in.a / 255.f; gs_hsv_t out = gs_default_val(); double min, max, delta; min = ir < ig ? ir : ig; min = min < ib ? min : ib; max = ir > ig ? ir : ig; max = max > ib ? max : ib; out.v = max; // v delta = max - min; if (delta < 0.00001) { out.s = 0; out.h = 0; // undefined, maybe nan? return out; } if(max > 0.0) { // NOTE: if Max is == 0, this divide would cause a crash out.s = (delta / max); // s } else { // if max is 0, then r = g = b = 0 // s = 0, h is undefined out.s = 0.0; out.h = NAN; // its now undefined return out; } if(ir >= max) // > is bogus, just keeps compilor happy out.h = (ig - ib) / delta; // between yellow & magenta else if( ig >= max ) out.h = 2.0 + ( ib - ir ) / delta; // between cyan & yellow else out.h = 4.0 + ( ir - ig ) / delta; // between magenta & cyan out.h *= 60.0; // degrees if( out.h < 0.0 ) out.h += 360.0; return out; } gs_force_inline gs_color_t gs_hsv2rgb(gs_hsv_t in) { double hh, p, q, t, ff; long i; gs_color_t out; if(in.s <= 0.0) { // < is bogus, just shuts up warnings out.r = in.v * 255; out.g = in.v * 255; out.b = in.v * 255; out.a = 255; return out; } hh = in.h; if(hh >= 360.0) hh = 0.0; hh /= 60.0; i = (long)hh; ff = hh - i; p = in.v * (1.0 - in.s); q = in.v * (1.0 - (in.s * ff)); t = in.v * (1.0 - (in.s * (1.0 - ff))); uint8_t iv = in.v * 255; uint8_t it = t * 255; uint8_t ip = p * 255; uint8_t iq = q * 255; switch(i) { case 0: out.r = iv; out.g = it; out.b = ip; break; case 1: out.r = iq; out.g = iv; out.b = ip; break; case 2: out.r = ip; out.g = iv; out.b = it; break; case 3: out.r = ip; out.g = iq; out.b = iv; break; case 4: out.r = it; out.g = ip; out.b = iv; break; case 5: default: out.r = iv; out.g = ip; out.b = iq; break; } return out; } /*=================================== // String Utils ===================================*/ gs_force_inline uint32_t gs_string_length(const char* txt) { uint32_t sz = 0; while (txt != NULL && txt[ sz ] != '\0') { sz++; } return sz; } #define gs_strlen gs_string_length // Expects null terminated strings gs_force_inline b32 gs_string_compare_equal ( const char* txt, const char* cmp ) { // Grab sizes of both strings uint32_t a_sz = gs_string_length(txt); uint32_t b_sz = gs_string_length(cmp); // Return false if sizes do not match if (a_sz != b_sz) { return false; } for(uint32_t i = 0; i < a_sz; ++i) { if (*txt++ != *cmp++) { return false; } }; return true; } gs_force_inline b32 gs_string_compare_equal_n ( const char* txt, const char* cmp, uint32_t n ) { uint32_t a_sz = gs_string_length(txt); uint32_t b_sz = gs_string_length(cmp); // Not enough characters to do operation if (a_sz < n || b_sz < n) { return false; } for (uint32_t i = 0; i < n; ++i) { if (*txt++ != *cmp++) { return false; } }; return true; } gs_force_inline void gs_util_str_to_lower ( const char* src, char* buffer, size_t buffer_sz ) { size_t src_sz = gs_string_length(src); size_t len = gs_min(src_sz, buffer_sz-1); for (uint32_t i = 0; i < len; ++i) { buffer[i] = tolower(src[i]); } if (len) buffer[len] = '\0'; } gs_force_inline b32 gs_util_str_is_numeric(const char* str) { const char* at = str; while (at && *at) { while (*at == '\n' || *at == '\t' || *at == ' ' || *at == '\r') at++;; char c = *at++; if (c < '0' || c > '9') { return false; } } return true; } // Will return a null buffer if file does not exist or allocation fails GS_API_DECL char* gs_read_file_contents_into_string_null_term (const char* file_path, const char* mode, size_t* _sz); gs_force_inline b32 gs_util_file_exists(const char* file_path) { FILE* fp = fopen(file_path, "r"); if (fp) { fclose(fp); return true; } return false; } gs_force_inline void gs_util_get_file_extension ( char* buffer, uint32_t buffer_size, const char* file_path ) { // assumes that buffer and buffer_size is non-zero const char* extension = strrchr(file_path, '.'); if (extension) { uint32_t extension_len = strlen(extension+1); uint32_t len = (extension_len >= buffer_size) ? buffer_size - 1 : extension_len; memcpy(buffer, extension+1, len); buffer[len] = '\0'; } else { buffer[0] = '\0'; } } gs_force_inline void gs_util_get_dir_from_file ( char* buffer, uint32_t buffer_size, const char* file_path ) { uint32_t str_len = gs_string_length(file_path); const char* end = (file_path + str_len); for (uint32_t i = 0; i < str_len; ++i) { if (file_path[i] == '/' || file_path[i] == '\\') { end = &file_path[i]; } } size_t dir_len = end - file_path; memcpy(buffer, file_path, gs_min(buffer_size, dir_len + 1)); if (dir_len + 1 <= buffer_size) { buffer[dir_len] = '\0'; } } gs_force_inline void gs_util_get_file_name ( char* buffer, uint32_t buffer_size, const char* file_path ) { uint32_t str_len = gs_string_length(file_path); const char* file_start = file_path; const char* file_end = (file_path + str_len); for (uint32_t i = 0; i < str_len; ++i) { if (file_path[i] == '/' || file_path[i] == '\\') { file_start = &file_path[i + 1]; } else if (file_path[i] == '.') { file_end = &file_path[i]; } } size_t dir_len = file_end - file_start; memcpy(buffer, file_start, gs_min(buffer_size, dir_len + 1)); if (dir_len + 1 <= buffer_size) { buffer[dir_len] = '\0'; } } gs_force_inline void gs_util_string_substring ( const char* src, char* dst, size_t sz, uint32_t start, uint32_t end ) { uint32_t str_len = gs_string_length(src); if (end > str_len) { end = str_len; } if (start > str_len) { start = str_len; } const char* at = src + start; const char* e = src + end; uint32_t ct = 0; while (at && *at != '\0' && at != e) { dst[ ct ] = *at; at++; ct++; } } gs_force_inline void gs_util_string_remove_character ( const char* src, char* buffer, uint32_t buffer_size, char delimiter ) { uint32_t ct = 0; uint32_t str_len = gs_string_length(src); const char* at = src; while (at && *at != '\0' && ct < buffer_size) { char c = *at; if (c != delimiter) { buffer[ ct ] = c; ct++; } at++; } } gs_force_inline void gs_util_string_replace( char* buffer, size_t buffer_sz, const char* replace, char fallback ) { // Replace all characters with characters of keyword, then the rest replace with spaces size_t len = gs_string_length(replace); for (uint32_t c = 0; c < buffer_sz; ++c) { if (c < len) { buffer[c] = replace[c]; } else { buffer[c] = fallback; } } } gs_force_inline void gs_util_string_replace_delim ( const char* source_str, char* buffer, uint32_t buffer_size, char delimiter, char replace ) { uint32_t str_len = gs_string_length(source_str); const char* at = source_str; while (at && *at != '\0') { char c = *at; if (c == delimiter) { c = replace; } buffer[(at - source_str)] = c; at++; } } GS_API_DECL char* gs_util_string_concat(char* s1, const char* s2); gs_force_inline void gs_util_normalize_path ( const char* path, char* buffer, uint32_t buffer_size ) { // Normalize the path somehow... } // Custom printf defines #ifndef gs_printf #ifdef __MINGW32__ #define gs_printf(__FMT, ...) __mingw_printf(__FMT, ##__VA_ARGS__) #elif (defined GS_PLATFORM_ANDROID) #include #define gs_printf(__FMT, ...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __FMT, ## __VA_ARGS__)) #else gs_force_inline void gs_printf ( const char* fmt, ... ) { va_list args; va_start (args, fmt); vprintf(fmt, args); va_end(args); } #endif #endif #define gs_println(__FMT, ...)\ do {\ gs_printf(__FMT, ## __VA_ARGS__);\ gs_printf("\n");\ } while (0) #ifndef gs_fprintf gs_force_inline void gs_fprintf ( FILE* fp, const char* fmt, ... ) { va_list args; va_start (args, fmt); vfprintf(fp, fmt, args); va_end(args); } #endif gs_force_inline void gs_fprintln ( FILE* fp, const char* fmt, ... ) { va_list args; va_start(args, fmt); vfprintf(fp, fmt, args); va_end(args); gs_fprintf(fp, "\n"); } gs_force_inline void gs_fprintln_t ( FILE* fp, uint32_t tabs, const char* fmt, ... ) { va_list args; va_start(args, fmt); for (uint32_t i = 0; i < tabs; ++i) { gs_fprintf(fp, "\t"); } vfprintf(fp, fmt, args); va_end(args); gs_fprintf(fp, "\n"); } #ifdef __MINGW32__ #define gs_snprintf(__NAME, __SZ, __FMT, ...) __mingw_snprintf(__NAME, __SZ, __FMT, ## __VA_ARGS__) #else gs_force_inline void gs_snprintf ( char* buffer, size_t buffer_size, const char* fmt, ... ) { va_list args; va_start(args, fmt); vsnprintf(buffer, buffer_size, fmt, args); va_end(args); } #endif #define gs_transient_buffer(__N, __SZ)\ char __N[__SZ] = gs_default_val();\ memset(__N, 0, __SZ); #define gs_snprintfc(__NAME, __SZ, __FMT, ...)\ char __NAME[__SZ] = gs_default_val();\ gs_snprintf(__NAME, __SZ, __FMT, ## __VA_ARGS__); gs_force_inline uint32_t gs_util_safe_truncate_u64(uint64_t value) { gs_assert(value <= 0xFFFFFFFF); uint32_t result = (uint32_t)value; return result; } gs_force_inline uint32_t gs_hash_uint32_t(uint32_t x) { x = ((x >> 16) ^ x) * 0x45d9f3b; x = ((x >> 16) ^ x) * 0x45d9f3b; x = (x >> 16) ^ x; return x; } #define gs_hash_u32_ip(__X, __OUT)\ do {\ __OUT = ((__X >> 16) ^ __X) * 0x45d9f3b;\ __OUT = ((__OUT >> 16) ^ __OUT) * 0x45d9f3b;\ __OUT = (__OUT >> 16) ^ __OUT;\ } while (0) gs_force_inline uint32_t gs_hash_u64(uint64_t x) { x = (x ^ (x >> 31) ^ (x >> 62)) * UINT64_C(0x319642b2d24d8ec3); x = (x ^ (x >> 27) ^ (x >> 54)) * UINT64_C(0x96de1b173f119089); x = x ^ (x >> 30) ^ (x >> 60); return (uint32_t)x; } // Note(john): source: http://www.cse.yorku.ca/~oz/hash.html // djb2 hash by dan bernstein gs_force_inline uint32_t gs_hash_str(const char* str) { uint32_t hash = 5381; s32 c; while ((c = *str++)) { hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ } return hash; } gs_force_inline uint64_t gs_hash_str64(const char* str) { uint32_t hash1 = 5381; uint32_t hash2 = 52711; uint32_t i = gs_string_length(str); while(i--) { char c = str[ i ]; hash1 = (hash1 * 33) ^ c; hash2 = (hash2 * 33) ^ c; } return (hash1 >> 0) * 4096 + (hash2 >> 0); } gs_force_inline bool gs_compare_bytes(void* b0, void* b1, size_t len) { return 0 == memcmp(b0, b1, len); } // Hash generic bytes using (ripped directly from Sean Barret's stb_ds.h) #define GS_SIZE_T_BITS ((sizeof(size_t)) * 8) #define GS_SIPHASH_C_ROUNDS 1 #define GS_SIPHASH_D_ROUNDS 1 #define gs_rotate_left(__V, __N) (((__V) << (__N)) | ((__V) >> (GS_SIZE_T_BITS - (__N)))) #define gs_rotate_right(__V, __N) (((__V) >> (__N)) | ((__V) << (GS_SIZE_T_BITS - (__N)))) #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4127) // conditional expression is constant, for do..while(0) and sizeof()== #endif gs_force_inline size_t gs_hash_siphash_bytes(void *p, size_t len, size_t seed) { unsigned char *d = (unsigned char *) p; size_t i,j; size_t v0,v1,v2,v3, data; // hash that works on 32- or 64-bit registers without knowing which we have // (computes different results on 32-bit and 64-bit platform) // derived from siphash, but on 32-bit platforms very different as it uses 4 32-bit state not 4 64-bit v0 = ((((size_t) 0x736f6d65 << 16) << 16) + 0x70736575) ^ seed; v1 = ((((size_t) 0x646f7261 << 16) << 16) + 0x6e646f6d) ^ ~seed; v2 = ((((size_t) 0x6c796765 << 16) << 16) + 0x6e657261) ^ seed; v3 = ((((size_t) 0x74656462 << 16) << 16) + 0x79746573) ^ ~seed; #ifdef STBDS_TEST_SIPHASH_2_4 // hardcoded with key material in the siphash test vectors v0 ^= 0x0706050403020100ull ^ seed; v1 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed; v2 ^= 0x0706050403020100ull ^ seed; v3 ^= 0x0f0e0d0c0b0a0908ull ^ ~seed; #endif #define gs_sipround() \ do { \ v0 += v1; v1 = gs_rotate_left(v1, 13); v1 ^= v0; v0 = gs_rotate_left(v0,GS_SIZE_T_BITS/2); \ v2 += v3; v3 = gs_rotate_left(v3, 16); v3 ^= v2; \ v2 += v1; v1 = gs_rotate_left(v1, 17); v1 ^= v2; v2 = gs_rotate_left(v2,GS_SIZE_T_BITS/2); \ v0 += v3; v3 = gs_rotate_left(v3, 21); v3 ^= v0; \ } while (0) for (i=0; i+sizeof(size_t) <= len; i += sizeof(size_t), d += sizeof(size_t)) { data = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); data |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // discarded if size_t == 4 v3 ^= data; for (j=0; j < GS_SIPHASH_C_ROUNDS; ++j) gs_sipround(); v0 ^= data; } data = len << (GS_SIZE_T_BITS-8); switch (len - i) { case 7: data |= ((size_t) d[6] << 24) << 24; // fall through case 6: data |= ((size_t) d[5] << 20) << 20; // fall through case 5: data |= ((size_t) d[4] << 16) << 16; // fall through case 4: data |= (d[3] << 24); // fall through case 3: data |= (d[2] << 16); // fall through case 2: data |= (d[1] << 8); // fall through case 1: data |= d[0]; // fall through case 0: break; } v3 ^= data; for (j=0; j < GS_SIPHASH_C_ROUNDS; ++j) gs_sipround(); v0 ^= data; v2 ^= 0xff; for (j=0; j < GS_SIPHASH_D_ROUNDS; ++j) gs_sipround(); #if 0 return v0^v1^v2^v3; #else return v1^v2^v3; // slightly stronger since v0^v3 in above cancels out final round operation? I tweeted at the authors of SipHash about this but they didn't reply #endif } gs_force_inline size_t gs_hash_murmur3(void *p, size_t len, size_t seed) { const uint8_t* data = (const uint8_t*)p; size_t h = seed ^ len; for (uint32_t i = 0; i < len / 4; ++i) { uint32_t k = *(uint32_t*)(data + i * 4); k *= 0xcc9e2d51; k = (k << 15) | (k >> (32 - 15)); k *= 0x1b873593; h ^= k; h = (h << 13) | (h >> (32 - 13)); h = h * 5 + 0xe6546b64; } // Handle remaining bytes const uint8_t* tail = data + (len & ~3); uint32_t k1 = 0; switch (len & 3) { case 3: k1 ^= tail[2] << 16; case 2: k1 ^= tail[1] << 8; case 1: k1 ^= tail[0]; k1 *= 0xcc9e2d51; k1 = (k1 << 15) | (k1 >> (32 - 15)); k1 *= 0x1b873593; h ^= k1; } h ^= h >> 16; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; h ^= h >> 16; return h; } gs_force_inline size_t gs_hash_bytes(void *p, size_t len, size_t seed) { #if 1 return gs_hash_siphash_bytes(p, len, seed); #else return gs_hash_murmur3(p, len, seed); #endif } #ifdef _MSC_VER #pragma warning(pop) #endif /* Resource Loading Util */ GS_API_DECL bool32_t gs_util_load_texture_data_from_file(const char* file_path, int32_t* width, int32_t* height, uint32_t* num_comps, void** data, bool32_t flip_vertically_on_load); GS_API_DECL bool32_t gs_util_load_texture_data_from_memory(const void* memory, size_t sz, int32_t* width, int32_t* height, uint32_t* num_comps, void** data, bool32_t flip_vertically_on_load); /** @} */ // end of gs_util /*======================== // GS_CONTAINERS ========================*/ /** @defgroup gs_containers Containers * Gunslinger Containers * @{ */ /*======================== // Byte Buffer ========================*/ /** @defgroup gs_byte_buffer Byte Buffer * @ingroup gs_containers * Byte Buffer */ #define GS_BYTE_BUFFER_DEFAULT_CAPCITY 1024 /** @addtogroup gs_byte_buffer */ typedef struct gs_byte_buffer_t { uint8_t* data; // Buffer that actually holds all relevant byte data uint32_t size; // Current size of the stored buffer data uint32_t position; // Current read/write position in the buffer uint32_t capacity; // Current max capacity for the buffer } gs_byte_buffer_t; // Generic "write" function for a byte buffer #define gs_byte_buffer_write(__BB, __T, __VAL)\ do {\ gs_byte_buffer_t* __BUFFER = __BB;\ if (!__BUFFER || !__BUFFER->data) break;\ usize __SZ = sizeof(__T);\ usize __TWS = __BUFFER->position + __SZ;\ if (__TWS >= (usize)__BUFFER->capacity)\ {\ usize __CAP = __BUFFER->capacity * 2;\ while(__CAP < __TWS)\ {\ __CAP *= 2;\ }\ uint8_t* __OLD_DATA = __BUFFER->data;\ uint32_t __OLD_POS = __BUFFER->position;\ uint32_t __OLD_SIZE = __BUFFER->size;\ gs_byte_buffer_resize(__BUFFER, __CAP);\ if (__BUFFER->data == NULL) {\ __BUFFER->data = __OLD_DATA;\ __BUFFER->position = __OLD_POS;\ __BUFFER->size = __OLD_SIZE;\ break;\ }\ }\ if (!__BUFFER || !__BUFFER->data || __BUFFER->position + sizeof(__T) > __BUFFER->capacity) break;\ *(__T*)(__BUFFER->data + __BUFFER->position) = __VAL;\ __BUFFER->position += (uint32_t)__SZ;\ __BUFFER->size += (uint32_t)__SZ;\ } while (0) // Generic "read" function #define gs_byte_buffer_read(__BUFFER, __T, __VAL_P)\ do {\ __T* __V = (__T*)(__VAL_P);\ gs_byte_buffer_t* __BB = (__BUFFER);\ *(__V) = *(__T*)(__BB->data + __BB->position);\ __BB->position += sizeof(__T);\ } while (0) // Defines variable and sets value from buffer in place // Use to construct a new variable #define gs_byte_buffer_readc(__BUFFER, __T, __NAME)\ __T __NAME = gs_default_val();\ gs_byte_buffer_read((__BUFFER), __T, &__NAME); #define gs_byte_buffer_read_bulkc(__BUFFER, __T, __NAME, __SZ)\ __T __NAME = gs_default_val();\ __T* gs_macro_cat(__NAME, __LINE__) = &(__NAME);\ gs_byte_buffer_read_bulk(__BUFFER, (void**)&gs_macro_cat(__NAME, __LINE__), __SZ); GS_API_DECL void gs_byte_buffer_init(gs_byte_buffer_t* buffer); GS_API_DECL gs_byte_buffer_t gs_byte_buffer_new(); GS_API_DECL void gs_byte_buffer_free(gs_byte_buffer_t* buffer); GS_API_DECL void gs_byte_buffer_clear(gs_byte_buffer_t* buffer); GS_API_DECL bool gs_byte_buffer_empty(gs_byte_buffer_t* buffer); GS_API_DECL size_t gs_byte_buffer_size(gs_byte_buffer_t* buffer); GS_API_DECL void gs_byte_buffer_resize(gs_byte_buffer_t* buffer, size_t sz); GS_API_DECL void gs_byte_buffer_seek_to_beg(gs_byte_buffer_t* buffer); GS_API_DECL void gs_byte_buffer_seek_to_end(gs_byte_buffer_t* buffer); GS_API_DECL void gs_byte_buffer_advance_position(gs_byte_buffer_t* buffer, size_t sz); GS_API_DECL void gs_byte_buffer_write_str(gs_byte_buffer_t* buffer, const char* str); // Expects a null terminated string GS_API_DECL void gs_byte_buffer_read_str(gs_byte_buffer_t* buffer, char* str); // Expects an allocated string GS_API_DECL void gs_byte_buffer_write_bulk(gs_byte_buffer_t* buffer, void* src, size_t sz); GS_API_DECL void gs_byte_buffer_read_bulk(gs_byte_buffer_t* buffer, void** dst, size_t sz); GS_API_DECL gs_result gs_byte_buffer_write_to_file(gs_byte_buffer_t* buffer, const char* output_path); // Assumes that the output directory exists GS_API_DECL gs_result gs_byte_buffer_read_from_file(gs_byte_buffer_t* buffer, const char* file_path); // Assumes an allocated byte buffer GS_API_DECL void gs_byte_buffer_memset(gs_byte_buffer_t* buffer, uint8_t val); /*====================// //=== Static Array ===// ======================*/ #define gs_array(__T, __N)\ struct {\ __T data[__N];\ size_t size;\ } #define gs_array_capacity(__Q) (sizeof((__Q).data) / sizeof((__Q).data[0])) #define gs_array_push(__Q, __V)\ do {\ const size_t cap = gs_array_capacity((__Q));\ if ((__Q).size < cap) {\ (__Q).data[(__Q).size++] = (__V);\ }\ } while (0) #define gs_array_pop(__Q)\ do {\ if ((__Q).size) {\ const size_t cap = gs_array_capacity((__Q));\ (__Q).size--;\ }\ } while (0) #define gs_array_empty(__Q) (__Q).size ? false : true #define gs_array_clear(__Q) do {(__Q).size = 0;} while (0) #define gs_array_back(__Q)\ !gs_array_empty(__Q) ? &__Q.data[__Q.size - 1] : NULL /*=============================// //=== Static Circular Queue ===// ===============================*/ #define gs_cqueue(__T, __N)\ struct {\ __T data[__N];\ size_t size;\ size_t head;\ } #define gs_cqueue_size(__Q) ((__Q).size) #define gs_cqueue_capacity(__Q) (sizeof((__Q).data) / sizeof((__Q).data[0])) #define gs_cqueue_push(__Q, __V)\ do {\ const size_t cap = gs_cqueue_capacity((__Q));\ if ((__Q).size < cap) {\ (__Q).data[((__Q).head + (__Q).size++) % cap] = (__V);\ }\ } while (0) #define gs_cqueue_pop(__Q)\ do {\ if ((__Q).size) {\ const size_t cap = gs_cqueue_capacity((__Q));\ (__Q).size--;\ (__Q).head = ((__Q).head + 1) % cap;\ }\ else {\ (__Q).head = 0;\ }\ } while (0) #define gs_cqueue_peek(__Q) (__Q).size ? &(__Q).data[(__Q).head] : NULL #define gs_cqueue_empty(__Q) (__Q).size ? false : true #define gs_cqueue_clear(__Q)\ do {\ (__Q).size = 0;\ (__Q).head = 0;\ } while (0) /*=================================== // Dynamic Array ===================================*/ /** @defgroup gs_dyn_array Dynamic Array * @ingroup gs_containers * Dynamic Array */ /** @addtogroup gs_dyn_array */ typedef struct gs_dyn_array { int32_t size; int32_t capacity; } gs_dyn_array; #define gs_dyn_array_head(__ARR)\ ((gs_dyn_array*)((uint8_t*)(__ARR) - sizeof(gs_dyn_array))) #define gs_dyn_array_size(__ARR)\ (__ARR == NULL ? 0 : gs_dyn_array_head((__ARR))->size) #define gs_dyn_array_capacity(__ARR)\ (__ARR == NULL ? 0 : gs_dyn_array_head((__ARR))->capacity) #define gs_dyn_array_full(__ARR)\ ((gs_dyn_array_size((__ARR)) == gs_dyn_array_capacity((__ARR)))) #define gs_dyn_array_byte_size(__ARR)\ (gs_dyn_array_size((__ARR)) * sizeof(*__ARR)) GS_API_DECL void* gs_dyn_array_resize_impl(void* arr, size_t sz, size_t amount); #define gs_dyn_array_need_grow(__ARR, __N)\ ((__ARR) == 0 || gs_dyn_array_size(__ARR) + (__N) >= gs_dyn_array_capacity(__ARR)) #define gs_dyn_array_grow(__ARR)\ gs_dyn_array_resize_impl((__ARR), sizeof(*(__ARR)), gs_dyn_array_capacity(__ARR) ? gs_dyn_array_capacity(__ARR) * 2 : 1) #define gs_dyn_array_grow_size(__ARR, __SZ )\ gs_dyn_array_resize_impl((__ARR), (__SZ ), gs_dyn_array_capacity(__ARR) ? gs_dyn_array_capacity(__ARR) * 2 : 1) GS_API_DECL void** gs_dyn_array_init(void** arr, size_t val_len); GS_API_DECL void gs_dyn_array_push_data(void** arr, void* val, size_t val_len); gs_force_inline void gs_dyn_array_set_data_i(void** arr, void* val, size_t val_len, uint32_t offset) { memcpy(((char*)(*arr)) + offset * val_len, val, val_len); } #define gs_dyn_array_push(__ARR, __ARRVAL)\ do {\ gs_dyn_array_init((void**)&(__ARR), sizeof(*(__ARR)));\ if (!(__ARR) || ((__ARR) && gs_dyn_array_need_grow(__ARR, 1))) {\ *((void **)&(__ARR)) = gs_dyn_array_grow(__ARR); \ }\ (__ARR)[gs_dyn_array_size(__ARR)] = (__ARRVAL);\ gs_dyn_array_head(__ARR)->size++;\ } while(0) #define gs_dyn_array_reserve(__ARR, __AMOUNT)\ do {\ if ((!__ARR)) gs_dyn_array_init((void**)&(__ARR), sizeof(*(__ARR)));\ if ((!__ARR) || (size_t)__AMOUNT > gs_dyn_array_capacity(__ARR)) {\ *((void **)&(__ARR)) = gs_dyn_array_resize_impl(__ARR, sizeof(*__ARR), __AMOUNT);\ }\ } while(0) #define gs_dyn_array_empty(__ARR)\ (gs_dyn_array_init((void**)&(__ARR), sizeof(*(__ARR))), (gs_dyn_array_size(__ARR) == 0)) #define gs_dyn_array_pop(__ARR)\ do {\ if (__ARR && !gs_dyn_array_empty(__ARR)) {\ gs_dyn_array_head(__ARR)->size -= 1;\ }\ } while (0) #define gs_dyn_array_back(__ARR)\ *(__ARR + (gs_dyn_array_size(__ARR) ? gs_dyn_array_size(__ARR) - 1 : 0)) #define gs_dyn_array_for(__ARR, __T, __IT_NAME)\ for (__T* __IT_NAME = __ARR; __IT_NAME != gs_dyn_array_back(__ARR); ++__IT_NAME) #define gs_dyn_array_new(__T)\ ((__T*)gs_dyn_array_resize_impl(NULL, sizeof(__T), 0)) #define gs_dyn_array_clear(__ARR)\ do {\ if (__ARR) {\ gs_dyn_array_head(__ARR)->size = 0;\ }\ } while (0) #define gs_dyn_array(__T) __T* #define gs_dyn_array_free(__ARR)\ do {\ if (__ARR) {\ gs_free(gs_dyn_array_head(__ARR));\ (__ARR) = NULL;\ }\ } while (0) /*=================================== // Static Array ===================================*/ /*=================================== // Hash Table ===================================*/ /* If using struct for keys, requires struct to be word-aligned. */ #define GS_HASH_TABLE_HASH_SEED 0x31415296 #define GS_HASH_TABLE_INVALID_INDEX UINT32_MAX typedef enum gs_hash_table_entry_state { GS_HASH_TABLE_ENTRY_INACTIVE = 0x00, GS_HASH_TABLE_ENTRY_ACTIVE = 0x01 } gs_hash_table_entry_state; typedef size_t (*gs_hash_func_t)(void*, size_t, size_t); #define __gs_hash_table_entry(__HMK, __HMV)\ struct {\ __HMK key;\ __HMV val;\ size_t hash;\ gs_hash_table_entry_state state;\ } #define gs_hash_table(__HMK, __HMV)\ struct {\ __gs_hash_table_entry(__HMK, __HMV)* data;\ __HMK tmp_key;\ __HMV tmp_val;\ size_t stride;\ size_t klpvl;\ size_t tmp_idx;\ gs_hash_func_t hash_func;\ }* // Need a way to create a temporary key so I can take the address of it #define gs_hash_table_new(__K, __V)\ NULL GS_API_DECL void __gs_hash_table_init_impl(void** ht, size_t sz); #define gs_hash_table_init_ex(__HT, __K, __V, __HFUNC)\ do {\ size_t entry_sz = sizeof(*__HT->data);\ size_t ht_sz = sizeof(*__HT);\ __gs_hash_table_init_impl((void**)&(__HT), ht_sz);\ memset((__HT), 0, ht_sz);\ gs_dyn_array_reserve(__HT->data, 2);\ __HT->data[0].state = GS_HASH_TABLE_ENTRY_INACTIVE;\ __HT->data[1].state = GS_HASH_TABLE_ENTRY_INACTIVE;\ uintptr_t d0 = (uintptr_t)&((__HT)->data[0]);\ uintptr_t d1 = (uintptr_t)&((__HT)->data[1]);\ ptrdiff_t diff = (d1 - d0);\ ptrdiff_t klpvl = (uintptr_t)&(__HT->data[0].state) - (uintptr_t)(&__HT->data[0]);\ (__HT)->stride = (size_t)(diff);\ (__HT)->klpvl = (size_t)(klpvl);\ (__HT)->hash_func = (__HFUNC);\ } while (0) // Internal helper to compute hash with optional custom function gs_force_inline size_t __gs_hash_table_compute_hash(gs_hash_func_t hash_func, void* key, size_t key_len, size_t seed) { if (hash_func) { return hash_func(key, key_len, seed); } return gs_hash_bytes(key, key_len, seed); } #define gs_hash_table_init(__HT, __K, __V)\ gs_hash_table_init_ex(__HT, __K, __V, gs_hash_bytes) #define gs_hash_table_reserve(_HT, _KT, _VT, _CT)\ do {\ if ((_HT) == NULL) {\ gs_hash_table_init((_HT), _KT, _VT);\ }\ gs_dyn_array_reserve((_HT)->data, _CT);\ } while (0) #define gs_hash_table_size(__HT)\ ((__HT) != NULL ? gs_dyn_array_size((__HT)->data) : 0) #define gs_hash_table_capacity(__HT)\ ((__HT) != NULL ? gs_dyn_array_capacity((__HT)->data) : 0) #define gs_hash_table_load_factor(__HT)\ (gs_hash_table_capacity(__HT) ? (float)(gs_hash_table_size(__HT)) / (float)(gs_hash_table_capacity(__HT)) : 0.f) #define gs_hash_table_grow(__HT, __C)\ ((__HT)->data = gs_dyn_array_resize_impl((__HT)->data, sizeof(*((__HT)->data)), (__C))) #define gs_hash_table_empty(__HT)\ ((__HT) != NULL ? gs_dyn_array_size((__HT)->data) == 0 : true) #define gs_hash_table_clear(__HT)\ do {\ if ((__HT) != NULL) {\ uint32_t capacity = gs_dyn_array_capacity((__HT)->data);\ for (uint32_t i = 0; i < capacity; ++i) {\ (__HT)->data[i].state = GS_HASH_TABLE_ENTRY_INACTIVE;\ }\ /*memset((__HT)->data, 0, gs_dyn_array_capacity((__HT)->data) * );*/\ gs_dyn_array_clear((__HT)->data);\ }\ } while (0) #define gs_hash_table_free(__HT)\ do {\ if ((__HT) != NULL) {\ gs_dyn_array_free((__HT)->data);\ (__HT)->data = NULL;\ gs_free(__HT);\ (__HT) = NULL;\ }\ } while (0) // Find available slot to insert k/v pair into #define gs_hash_table_insert(__HT, __HMK, __HMV)\ do {\ /* Check for null hash table, init if necessary */\ if ((__HT) == NULL) {\ gs_hash_table_init((__HT), (__HMK), (__HMV));\ }\ \ /* Grow table if necessary */\ uint32_t __CAP = gs_hash_table_capacity(__HT);\ float __LF = gs_hash_table_load_factor(__HT);\ if (__LF >= 0.5f || !__CAP)\ {\ uint32_t NEW_CAP = __CAP ? __CAP * 2 : 2;\ size_t ENTRY_SZ = sizeof((__HT)->tmp_key) + sizeof((__HT)->tmp_val) + sizeof(size_t) + sizeof(gs_hash_table_entry_state);\ gs_dyn_array_reserve((__HT)->data, NEW_CAP);\ /**((void **)&(__HT->data)) = gs_dyn_array_resize_impl(__HT->data, ENTRY_SZ, NEW_CAP);*/\ /* Iterate through data and set state to null, from __CAP -> __CAP * 2 */\ /* Memset here instead */\ for (uint32_t __I = __CAP; __I < NEW_CAP; ++__I) {\ (__HT)->data[__I].state = GS_HASH_TABLE_ENTRY_INACTIVE;\ }\ __CAP = gs_hash_table_capacity(__HT);\ }\ \ /* Get hash of key */\ (__HT)->tmp_key = (__HMK);\ size_t __HSH = __gs_hash_table_compute_hash((__HT)->hash_func, (void*)&((__HT)->tmp_key), sizeof((__HT)->tmp_key), GS_HASH_TABLE_HASH_SEED);\ size_t __HSH_IDX = __HSH % __CAP;\ (__HT)->tmp_key = (__HT)->data[__HSH_IDX].key;\ uint32_t c = 0;\ \ /* Find valid idx and place data */\ while (\ c < __CAP\ && __HSH != __gs_hash_table_compute_hash((__HT)->hash_func, (void*)&(__HT)->tmp_key, sizeof((__HT)->tmp_key), GS_HASH_TABLE_HASH_SEED)\ && (__HT)->data[__HSH_IDX].state == GS_HASH_TABLE_ENTRY_ACTIVE)\ {\ __HSH_IDX = ((__HSH_IDX + 1) % __CAP);\ (__HT)->tmp_key = (__HT)->data[__HSH_IDX].key;\ ++c;\ }\ (__HT)->data[__HSH_IDX].key = (__HMK);\ (__HT)->data[__HSH_IDX].val = (__HMV);\ (__HT)->data[__HSH_IDX].hash = __HSH;\ (__HT)->data[__HSH_IDX].state = GS_HASH_TABLE_ENTRY_ACTIVE;\ (__HT)->tmp_idx = __HSH_IDX;\ gs_dyn_array_head((__HT)->data)->size++;\ } while (0) // Need size difference between two entries // Need size of key + val gs_force_inline uint32_t gs_hash_table_get_key_index_func(void** data, void* key, size_t key_len, size_t val_len, size_t stride, size_t klpvl, gs_hash_func_t hash_func) { if (!data || !key) return GS_HASH_TABLE_INVALID_INDEX; // Need a better way to handle this. Can't do it like this anymore. // Need to fix this. Seriously messing me up. uint32_t capacity = gs_dyn_array_capacity(*data); uint32_t size = gs_dyn_array_size(*data); if (!capacity || !size) return (size_t)GS_HASH_TABLE_INVALID_INDEX; size_t idx = (size_t)GS_HASH_TABLE_INVALID_INDEX; // size_t hash = (size_t)gs_hash_bytes(key, key_len, GS_HASH_TABLE_HASH_SEED); size_t hash = (size_t)hash_func(key, key_len, GS_HASH_TABLE_HASH_SEED); size_t hash_idx = (hash % capacity); // Iterate through data for (size_t i = hash_idx, c = 0; c < capacity; ++c, i = ((i + 1) % capacity)) { size_t offset = (i * stride); gs_hash_table_entry_state state = *(gs_hash_table_entry_state*)((char*)(*data) + offset + (klpvl)); // Early out inactive slots immediately if (state != GS_HASH_TABLE_ENTRY_ACTIVE) continue; size_t stored_hash = *(size_t*)((char*)(*data) + offset + (klpvl) - sizeof(size_t)); // Quick hash comparison first if (hash != stored_hash) continue; // Byte comparison void* k = ((char*)(*data) + (offset)); if (gs_compare_bytes(k, key, key_len)) { idx = i; break; } } return (uint32_t)idx; } // Get key at index #define gs_hash_table_getk(__HT, __I)\ (((__HT))->data[(__I)].key) // Get val at index #define gs_hash_table_geti(__HT, __I)\ ((__HT)->data[(__I)].val) // Could search for the index in the macro instead now. Does this help me? #define gs_hash_table_get(__HT, __HTK)\ ((__HT)->tmp_key = (__HTK),\ (gs_hash_table_geti((__HT),\ gs_hash_table_get_key_index_func((void**)&(__HT)->data, (void*)&((__HT)->tmp_key),\ sizeof((__HT)->tmp_key), sizeof((__HT)->tmp_val), (__HT)->stride, (__HT)->klpvl, (__HT)->hash_func)))) #define gs_hash_table_getp(__HT, __HTK)\ (\ (__HT)->tmp_key = (__HTK),\ ((__HT)->tmp_idx = (uint32_t)gs_hash_table_get_key_index_func((void**)&(__HT->data), (void*)&(__HT->tmp_key), sizeof(__HT->tmp_key),\ sizeof(__HT->tmp_val), __HT->stride, __HT->klpvl, __HT->hash_func)),\ ((__HT)->tmp_idx != GS_HASH_TABLE_INVALID_INDEX ? &gs_hash_table_geti((__HT), (__HT)->tmp_idx) : NULL)\ ) #define _gs_hash_table_key_exists_internal(__HT, __HTK)\ ((__HT)->tmp_key = (__HTK),\ (__HT)->tmp_idx = gs_hash_table_get_key_index_func((void**)&(__HT->data), (void*)&(__HT->tmp_key), sizeof(__HT->tmp_key),\ sizeof(__HT->tmp_val), __HT->stride, __HT->klpvl, __HT->hash_func), ((__HT->tmp_idx) != GS_HASH_TABLE_INVALID_INDEX)) // uint32_t gs_hash_table_get_key_index_func(void** data, void* key, size_t key_len, size_t val_len, size_t stride, size_t klpvl) #define gs_hash_table_exists(__HT, __HTK)\ (__HT && _gs_hash_table_key_exists_internal((__HT), (__HTK))) #define gs_hash_table_key_exists(__HT, __HTK)\ (gs_hash_table_exists((__HT), (__HTK))) #define gs_hash_table_erase(__HT, __HTK)\ do {\ if ((__HT))\ {\ /* Get idx for key */\ (__HT)->tmp_key = (__HTK);\ uint32_t __IDX = gs_hash_table_get_key_index_func((void**)&(__HT)->data, (void*)&((__HT)->tmp_key), sizeof((__HT)->tmp_key), sizeof((__HT)->tmp_val), (__HT)->stride, (__HT)->klpvl, (__HT)->hash_func);\ if (__IDX != GS_HASH_TABLE_INVALID_INDEX) {\ (__HT)->data[__IDX].state = GS_HASH_TABLE_ENTRY_INACTIVE;\ if (gs_dyn_array_head((__HT)->data)->size) gs_dyn_array_head((__HT)->data)->size--;\ }\ }\ } while (0) /*===== Hash Table Iterator ====*/ typedef uint32_t gs_hash_table_iter; typedef gs_hash_table_iter gs_hash_table_iter_t; gs_force_inline uint32_t __gs_find_first_valid_iterator(void* data, size_t key_len, size_t val_len, uint32_t idx, size_t stride, size_t klpvl) { uint32_t it = (uint32_t)idx; for (; it < (uint32_t)gs_dyn_array_capacity(data); ++it) { size_t offset = (it * stride); gs_hash_table_entry_state state = *(gs_hash_table_entry_state*)((uint8_t*)data + offset + (klpvl)); if (state == GS_HASH_TABLE_ENTRY_ACTIVE) { break; } } return it; } /* Find first valid iterator idx */ #define gs_hash_table_iter_new(__HT)\ (__HT ? __gs_find_first_valid_iterator((__HT)->data, sizeof((__HT)->tmp_key), sizeof((__HT)->tmp_val), 0, (__HT)->stride, (__HT)->klpvl) : 0) #define gs_hash_table_iter_valid(__HT, __IT)\ ((__IT) < gs_hash_table_capacity((__HT))) // Have to be able to do this for hash table... gs_force_inline void __gs_hash_table_iter_advance_func(void** data, size_t key_len, size_t val_len, uint32_t* it, size_t stride, size_t klpvl) { (*it)++; for (; *it < (uint32_t)gs_dyn_array_capacity(*data); ++*it) { size_t offset = (size_t)(*it * stride); gs_hash_table_entry_state state = *(gs_hash_table_entry_state*)((uint8_t*)*data + offset + (klpvl)); if (state == GS_HASH_TABLE_ENTRY_ACTIVE) { break; } } } #define gs_hash_table_find_valid_iter(__HT, __IT)\ ((__IT) = __gs_find_first_valid_iterator((void**)&(__HT)->data, sizeof((__HT)->tmp_key), sizeof((__HT)->tmp_val), (__IT), (__HT)->stride, (__HT)->klpvl)) #define gs_hash_table_iter_advance(__HT, __IT)\ (__gs_hash_table_iter_advance_func((void**)&(__HT)->data, sizeof((__HT)->tmp_key), sizeof((__HT)->tmp_val), &(__IT), (__HT)->stride, (__HT)->klpvl)) #define gs_hash_table_iter_get(__HT, __IT)\ gs_hash_table_geti(__HT, __IT) #define gs_hash_table_iter_getp(__HT, __IT)\ (&(gs_hash_table_geti(__HT, __IT))) #define gs_hash_table_iter_getk(__HT, __IT)\ (gs_hash_table_getk(__HT, __IT)) #define gs_hash_table_iter_getkp(__HT, __IT)\ (&(gs_hash_table_getk(__HT, __IT))) /*================== //=== Hash Set ===// ==================*/ #define GS_HASH_SET_HASH_SEED 0x31415296 #define GS_HASH_SET_INVALID_INDEX UINT32_MAX #define GS_HASH_SET_MAX_PROBE 64 #define GS_HASH_SET_MAX_LOAD_FACTOR 0.75f typedef enum gs_hash_set_entry_state { GS_HASH_SET_ENTRY_INACTIVE = 0x00, GS_HASH_SET_ENTRY_ACTIVE = 0x01 } gs_hash_set_entry_state; #define __gs_hash_set_entry(__T)\ struct\ {\ __T val;\ gs_hash_set_entry_state state;\ } #define gs_hash_set(__T)\ struct {\ __gs_hash_set_entry(__T)* data;\ __T tmp_val;\ size_t stride;\ size_t klpvl;\ size_t tmp_idx;\ gs_hash_func_t hash_func;\ }* #define gs_hash_set_new(T)\ NULL GS_API_DECL void __gs_hash_set_init_impl( void** ht, size_t sz ); #define gs_hash_set_init_ex(__S, __T, __HFUNC)\ do {\ size_t entry_sz = sizeof(*__S->data);\ size_t ht_sz = sizeof(*__S);\ __gs_hash_set_init_impl((void**)&(__S), ht_sz);\ memset((__S), 0, ht_sz);\ gs_dyn_array_reserve(__S->data, 2);\ __S->data[0].state = GS_HASH_SET_ENTRY_INACTIVE;\ __S->data[1].state = GS_HASH_SET_ENTRY_INACTIVE;\ uintptr_t d0 = (uintptr_t)&((__S)->data[0]);\ uintptr_t d1 = (uintptr_t)&((__S)->data[1]);\ ptrdiff_t diff = (d1 - d0);\ ptrdiff_t klpvl = (uintptr_t)&(__S->data[0].state) - (uintptr_t)(&__S->data[0]);\ (__S)->hash_func = __HFUNC;\ (__S)->stride = (size_t)(diff);\ (__S)->klpvl = (size_t)(klpvl);\ } while (0) #define gs_hash_set_init(__S, __T)\ gs_hash_set_init_ex(__S, __T, gs_hash_bytes) #define gs_hash_set_reserve(__S, __T, __CT)\ do {\ if ((__S) == NULL) {\ gs_hash_set_init((__S), __T);\ }\ gs_dyn_array_reserve((__S)->data, __CT);\ } while (0) #define gs_hash_set_size(__S)\ ((__S) != NULL ? gs_dyn_array_size((__S)->data) : 0) #define gs_hash_set_capacity(__S)\ ((__S) != NULL ? gs_dyn_array_capacity((__S)->data) : 0) #define gs_hash_set_load_factor(__S)\ (gs_hash_set_capacity(__S) ? (float)(gs_hash_set_size(__S)) / (float)(gs_hash_set_capacity(__S)) : 0.f) #define gs_hash_set_grow(__S, __C)\ ((__S)->data = gs_dyn_array_resize_impl((__S)->data, sizeof(*((__S)->data)), (__C))) #define gs_hash_set_empty(__S)\ ((__S) != NULL ? gs_dyn_array_size((__S)->data) == 0 : true) #define gs_hash_set_clear(__S)\ do {\ if ((__S) != NULL) {\ uint32_t capacity = gs_dyn_array_capacity((__S)->data);\ memset((__S)->data, 0, gs_dyn_array_capacity((__S)->data) * sizeof(*(__S)->data));\ gs_dyn_array_clear((__S)->data);\ }\ } while (0) #define gs_hash_set_free(__S)\ do {\ if ((__S) != NULL) {\ gs_dyn_array_free((__S)->data);\ (__S)->data = NULL;\ gs_free(__S);\ (__S) = NULL;\ }\ } while (0) #define gs_hash_set_hash_idx(_I, _C, _CAP)\ ((_I + _C) % _CAP) gs_force_inline uint32_t gs_hash_set_get_key_index_func(void** data, void* key, size_t key_len, size_t stride, size_t klpvl, gs_hash_func_t hfunc) { if (!data || !key) return GS_HASH_SET_INVALID_INDEX; uint32_t capacity = gs_dyn_array_capacity(*data); uint32_t size = gs_dyn_array_size(*data); if (!capacity || !size) return (size_t)GS_HASH_SET_INVALID_INDEX; size_t idx = (size_t)GS_HASH_SET_INVALID_INDEX; size_t hash = (size_t)hfunc(key, key_len, GS_HASH_SET_HASH_SEED); size_t hash_idx = (hash % capacity); size_t c = 0; size_t max_probe = GS_HASH_SET_MAX_PROBE; // Iterate through data for (size_t i = hash_idx; c < max_probe; ++c, i = ((i + c) % capacity)) { size_t offset = (i * stride); void* k = ((char*)(*data) + (offset)); size_t kh = hfunc(k, key_len, GS_HASH_SET_HASH_SEED); bool comp = gs_compare_bytes(k, key, key_len); gs_hash_set_entry_state state = *(gs_hash_set_entry_state*)((char*)(*data) + offset + (klpvl)); if (comp && hash == kh && state == GS_HASH_SET_ENTRY_ACTIVE) { idx = i; break; } } return (uint32_t)idx; } gs_force_inline void gs_hash_set_rehash(void** data, void** new_data, size_t new_cap, size_t key_len, size_t stride, size_t klpvl, gs_hash_func_t hfunc) { if (!data | !new_data) return; uint32_t capacity = gs_dyn_array_capacity(*data); if (new_cap <= capacity) return; for (uint32_t i = 0; i < capacity; ++i) { // Get original data size_t offset = (i * stride); // If this entry is inactive, then continue if (*((gs_hash_set_entry_state*)((char*)(*data) + offset + (klpvl))) == GS_HASH_SET_ENTRY_INACTIVE) { continue; } void* k = ((char*)(*data) + offset); size_t kh = hfunc(k, key_len, GS_HASH_SET_HASH_SEED); // Hash idx into new data with new capacity uint32_t c = 0; size_t hash_idx = (kh % new_cap); /* Find valid idx and place data */ while ( c < new_cap && *((gs_hash_set_entry_state*)((char*)(*new_data) + (hash_idx * stride) + (klpvl))) == GS_HASH_SET_ENTRY_ACTIVE ) { ++c; hash_idx = ((hash_idx + c)) % new_cap; } // Set new data in new array size_t noff = hash_idx * stride; uint32_t tmp = 20; memcpy((void*)(((uint8_t*)(*new_data)) + noff), k, key_len); *((gs_hash_set_entry_state*)((char*)(*new_data) + (hash_idx * stride) + klpvl)) = GS_HASH_SET_ENTRY_ACTIVE; } } // Find available slot to insert k/v pair into #define gs_hash_set_insert(__S, __T)\ do {\ /* Check for null hash table, init if necessary */\ if ((__S) == NULL) {\ gs_hash_set_init((__S), (__T));\ }\ \ /* Grow table if necessary */\ uint32_t __CAP = gs_hash_set_capacity(__S);\ float __LF = gs_hash_set_load_factor(__S);\ if (__LF >= GS_HASH_SET_MAX_LOAD_FACTOR || !__CAP)\ {\ uint32_t NEW_CAP = __CAP ? __CAP * 2 : 2;\ size_t ENTRY_SZ = sizeof((__S)->tmp_val) + sizeof(gs_hash_set_entry_state);\ void* new_data = gs_calloc(NEW_CAP * 2, ENTRY_SZ);\ /*Rehash data, reserve, copy, free*/\ gs_hash_set_rehash((void**)&(__S)->data, (void**)&new_data, NEW_CAP, sizeof((__S)->tmp_val), (__S)->stride, (__S)->klpvl, (__S)->hash_func);\ gs_dyn_array_reserve((__S)->data, NEW_CAP);\ memcpy((__S)->data, new_data, NEW_CAP * ENTRY_SZ);\ __CAP = gs_hash_set_capacity(__S);\ gs_free(new_data);\ }\ \ /* Get hash of key */\ (__S)->tmp_val = (__T);\ size_t __HSH = (__S)->hash_func((void*)&((__S)->tmp_val), sizeof((__S)->tmp_val), GS_HASH_SET_HASH_SEED);\ size_t __HSH_IDX = __HSH % __CAP;\ uint32_t c = 0;\ bool exists = false;\ \ /* Find valid idx and place data */\ while (c < __CAP) {\ /*If active entry*/\ if (__S->data[__HSH_IDX].state == GS_HASH_SET_ENTRY_ACTIVE) {\ if (gs_compare_bytes((void*)&(__S->tmp_val), (void*)&((__S)->data[__HSH_IDX].val), sizeof((__S)->tmp_val))) {\ exists = true;\ break;\ }\ ++c;\ __HSH_IDX = ((__HSH_IDX + c) % __CAP);\ }\ /*Inactive entry, so break*/\ else {\ break;\ }\ }\ if (!exists) {\ (__S)->data[__HSH_IDX].val = (__T);\ (__S)->data[__HSH_IDX].state = GS_HASH_SET_ENTRY_ACTIVE;\ gs_dyn_array_head((__S)->data)->size++;\ }\ } while (0) // Get val at index #define gs_hash_set_geti(__S, __I)\ ((__S)->data[(__I)].val) // Could search for the index in the macro instead now. Does this help me? #define gs_hash_set_get(__S, __SK)\ ((__S)->tmp_val = (__SK),\ (gs_hash_set_geti((__S),\ gs_hash_set_get_key_index_func((void**)&(__S)->data, (void*)&((__S)->tmp_val),\ sizeof((__S)->tmp_val), (__S)->stride, (__S)->klpvl, (__S)->hash_func)))) #define gs_hash_set_getp(__S, __SK)\ (\ (__S)->tmp_val = (__SK),\ ((__S)->tmp_idx = (uint32_t)gs_hash_set_get_key_index_func((void**)&(__S->data), (void*)&(__S->tmp_val), sizeof(__S->tmp_val),\ __S->stride, __S->klpvl, __S->hash_func)),\ ((__S)->tmp_idx != GS_HASH_SET_INVALID_INDEX ? &gs_hash_set_geti((__S), (__S)->tmp_idx) : NULL)\ ) #define _gs_hash_set_key_exists_internal(__S, __SK)\ ((__S)->tmp_val = (__SK),\ (gs_hash_set_get_key_index_func((void**)&(__S->data), (void*)&(__S->tmp_val), sizeof(__S->tmp_val),\ __S->stride, __S->klpvl, __S->hash_func) != GS_HASH_SET_INVALID_INDEX)) #define gs_hash_set_exists(__S, __SK)\ (__S && _gs_hash_set_key_exists_internal((__S), (__SK))) #define gs_hash_set_key_exists(__S, __SK)\ (gs_hash_set_exists((__S), (__SK))) #define gs_hash_set_erase(__S, __SK)\ do {\ if ((__S))\ {\ /* Get idx for key */\ (__S)->tmp_val = (__SK);\ uint32_t __IDX = gs_hash_set_get_key_index_func((void**)&(__S)->data, (void*)&((__S)->tmp_val), sizeof((__S)->tmp_val), (__S)->stride, (__S)->klpvl, (__S)->hash_func);\ if (__IDX != GS_HASH_SET_INVALID_INDEX) {\ (__S)->data[__IDX].state = GS_HASH_SET_ENTRY_INACTIVE;\ if (gs_dyn_array_head((__S)->data)->size) gs_dyn_array_head((__S)->data)->size--;\ }\ }\ } while (0) gs_force_inline bool _gs_hash_set_is_subset_of_internal(void** s0, void** s1, size_t key_len, size_t stride, size_t klpvl, gs_hash_func_t hfunc) { // If sz0 > sz1, then cannot be subset uint32_t sz0 = gs_dyn_array_size(*s0); uint32_t sz1 = gs_dyn_array_size(*s1); if (sz0 > sz1) return false; // If there exists an element in s0 not in s1, then not a subset uint32_t c0 = gs_dyn_array_capacity(*s0); uint32_t c1 = gs_dyn_array_capacity(*s1); for (uint32_t i = 0; i < c0; ++i) { // Continue past invalid entry size_t offset = (i * stride); gs_hash_set_entry_state state = *(gs_hash_set_entry_state*)((char*)(*s0) + offset + (klpvl)); if (state == GS_HASH_SET_ENTRY_INACTIVE) { continue; } // Valid entry, check against s1 void* k0 = ((char*)(*s0) + (offset)); size_t kh = hfunc(k0, key_len, GS_HASH_SET_HASH_SEED); uint32_t hidx = (kh % c1); // Hash idx into super set uint32_t cc = 0; bool found = false; while ( !found && cc < GS_HASH_SET_MAX_PROBE ) { // Not active entry size_t o1 = hidx * stride; // New offset gs_hash_set_entry_state es = *((gs_hash_set_entry_state*)((char*)(*s1) + (hidx * stride) + (klpvl))); void* k1 = ((char*)(*s1) + (o1)); // Found if (es == GS_HASH_SET_ENTRY_ACTIVE && gs_compare_bytes(k0, k1, key_len)) { found = true; break; } // Continue ++cc; hidx = ((hidx + cc)) % c1; } if (!found) return false; } return true; } #define gs_hash_set_is_subset_of(__S0, __S1)\ (__S0 && __S1 &&\ sizeof((__S0)->tmp_val) == sizeof((__S1)->tmp_val) &&\ (__S0)->stride == (__S1)->stride &&\ (__S0)->klpvl == (__S1)->klpvl &&\ _gs_hash_set_is_subset_of_internal((void**)&(__S0)->data, (void**)&(__S1)->data,\ sizeof((__S0)->tmp_val), (__S0)->stride, (__S0)->klpvl, (__S0)->hfunc)) /*===== Set Iterator ====*/ typedef uint32_t gs_hash_set_iter; typedef gs_hash_set_iter gs_hash_set_iter_t; gs_force_inline uint32_t __gs_hash_set_find_first_valid_iterator(void* data, uint32_t idx, size_t stride, size_t klpvl) { uint32_t it = (uint32_t)idx; for (; it < (uint32_t)gs_dyn_array_capacity(data); ++it) { size_t offset = (it * stride); gs_hash_set_entry_state state = *(gs_hash_set_entry_state*)((uint8_t*)data + offset + (klpvl)); if (state == GS_HASH_SET_ENTRY_ACTIVE) { break; } } return it; } /* Find first valid iterator idx */ #define gs_hash_set_iter_new(__S)\ (__S ? __gs_hash_set_find_first_valid_iterator((__S)->data, 0, (__S)->stride, (__S)->klpvl) : 0) #define gs_hash_set_iter_valid(__S, __IT)\ ((__IT) < gs_hash_set_capacity((__S))) gs_force_inline void __gs_hash_set_iter_advance_func(void** data, uint32_t* it, size_t stride, size_t klpvl) { (*it)++; for (; *it < (uint32_t)gs_dyn_array_capacity(*data); ++*it) { size_t offset = (size_t)(*it * stride); gs_hash_set_entry_state state = *(gs_hash_set_entry_state*)((uint8_t*)*data + offset + (klpvl)); if (state == GS_HASH_SET_ENTRY_ACTIVE) { break; } } } #define gs_hash_set_find_valid_iter(__S, __IT)\ ((__IT) = __gs_hash_set_find_first_valid_iterator((void**)&(__S)->data, (__IT), (__S)->stride, (__S)->klpvl)) #define gs_hash_set_iter_advance(__S, __IT)\ (__gs_hash_set_iter_advance_func((void**)&(__S)->data, &(__IT), (__S)->stride, (__S)->klpvl)) #define gs_hash_set_iter_get(__S, __IT)\ gs_hash_set_geti(__S, __IT) #define gs_hash_set_iter_getp(__S, __IT)\ (&(gs_hash_set_geti(__S, __IT))) #define gs_hash_set_iter_getk(__S, __IT)\ (gs_hash_set_getk(__S, __IT)) #define gs_hash_set_iter_getkp(__S, __IT)\ (&(gs_hash_set_getk(__S, __IT))) /*=================================== // Slot Array ===================================*/ #define GS_SLOT_ARRAY_INVALID_HANDLE UINT32_MAX #define gs_slot_array_handle_valid(__SA, __ID)\ ((__SA) && __ID < gs_dyn_array_size((__SA)->indices) && (__SA)->indices[__ID] != GS_SLOT_ARRAY_INVALID_HANDLE) typedef struct __gs_slot_array_dummy_header { gs_dyn_array(uint32_t) indices; gs_dyn_array(uint32_t) data; } __gs_slot_array_dummy_header; #define gs_slot_array(__T)\ struct\ {\ gs_dyn_array(uint32_t) indices;\ gs_dyn_array(__T) data;\ __T tmp;\ }* #define gs_slot_array_new(__T)\ NULL gs_force_inline uint32_t __gs_slot_array_find_next_available_index(gs_dyn_array(uint32_t) indices) { uint32_t idx = GS_SLOT_ARRAY_INVALID_HANDLE; for (uint32_t i = 0; i < (uint32_t)gs_dyn_array_size(indices); ++i) { uint32_t handle = indices[i]; if (handle == GS_SLOT_ARRAY_INVALID_HANDLE) { idx = i; break; } } if (idx == GS_SLOT_ARRAY_INVALID_HANDLE) { idx = gs_dyn_array_size(indices); } return idx; } GS_API_DECL void** gs_slot_array_init(void** sa, size_t sz); #define gs_slot_array_init_all(__SA)\ (gs_slot_array_init((void**)&(__SA), sizeof(*(__SA))), gs_dyn_array_init((void**)&((__SA)->indices), sizeof(uint32_t)),\ gs_dyn_array_init((void**)&((__SA)->data), sizeof((__SA)->tmp))) gs_force_inline uint32_t gs_slot_array_insert_func(void** indices, void** data, void* val, size_t val_len, uint32_t* ip) { // Find next available index u32 idx = __gs_slot_array_find_next_available_index((uint32_t*)*indices); if (idx == gs_dyn_array_size(*indices)) { uint32_t v = 0; gs_dyn_array_push_data(indices, &v, sizeof(uint32_t)); idx = gs_dyn_array_size(*indices) - 1; } // Push data to array gs_dyn_array_push_data(data, val, val_len); // Set data in indices uint32_t bi = gs_dyn_array_size(*data) - 1; gs_dyn_array_set_data_i(indices, &bi, sizeof(uint32_t), idx); if (ip){ *ip = idx; } return idx; } #define gs_slot_array_reserve(__SA, __NUM)\ do {\ gs_slot_array_init_all(__SA);\ gs_dyn_array_reserve((__SA)->data, __NUM);\ gs_dyn_array_reserve((__SA)->indices, __NUM);\ } while (0) #define gs_slot_array_insert(__SA, __VAL)\ (gs_slot_array_init_all(__SA), (__SA)->tmp = (__VAL),\ gs_slot_array_insert_func((void**)&((__SA)->indices), (void**)&((__SA)->data), (void*)&((__SA)->tmp), sizeof(((__SA)->tmp)), NULL)) #define gs_slot_array_insert_hp(__SA, __VAL, __hp)\ (gs_slot_array_init_all(__SA), (__SA)->tmp = (__VAL),\ gs_slot_array_insert_func((void**)&((__SA)->indices), (void**)&((__SA)->data), &((__SA)->tmp), sizeof(((__SA)->tmp)), (__hp))) #define gs_slot_array_insert_no_init(__SA, __VAL)\ ((__SA)->tmp = (__VAL), gs_slot_array_insert_func((void**)&((__SA)->indices), (void**)&((__SA)->data), &((__SA)->tmp), sizeof(((__SA)->tmp)), NULL)) #define gs_slot_array_size(__SA)\ ((__SA) == NULL ? 0 : gs_dyn_array_size((__SA)->data)) #define gs_slot_array_empty(__SA)\ (gs_slot_array_size(__SA) == 0) #define gs_slot_array_clear(__SA)\ do {\ if ((__SA) != NULL) {\ gs_dyn_array_clear((__SA)->data);\ gs_dyn_array_clear((__SA)->indices);\ }\ } while (0) #define gs_slot_array_exists(__SA, __SID)\ ((__SA) && (__SID) < (uint32_t)gs_dyn_array_size((__SA)->indices) && (__SA)->indices[__SID] != GS_SLOT_ARRAY_INVALID_HANDLE) #define gs_slot_array_get(__SA, __SID)\ ((__SA)->data[(__SA)->indices[(__SID) % gs_dyn_array_size(((__SA)->indices))]]) #define gs_slot_array_getp(__SA, __SID)\ (&(gs_slot_array_get(__SA, (__SID)))) #define gs_slot_array_free(__SA)\ do {\ if ((__SA) != NULL) {\ gs_dyn_array_free((__SA)->data);\ gs_dyn_array_free((__SA)->indices);\ (__SA)->indices = NULL;\ (__SA)->data = NULL;\ gs_free((__SA));\ (__SA) = NULL;\ }\ } while (0) #define gs_slot_array_erase(__SA, __id)\ do {\ uint32_t __H0 = (__id) /*% gs_dyn_array_size((__SA)->indices)*/;\ if (gs_slot_array_size(__SA) == 1) {\ gs_slot_array_clear(__SA);\ }\ else if (!gs_slot_array_handle_valid(__SA, __H0)) {\ gs_println("Warning: Attempting to erase invalid slot array handle (%zu)", __H0);\ }\ else {\ uint32_t __OG_DATA_IDX = (__SA)->indices[__H0];\ /* Iterate through handles until last index of data found */\ uint32_t __H = 0;\ for (uint32_t __I = 0; __I < gs_dyn_array_size((__SA)->indices); ++__I)\ {\ if ((__SA)->indices[__I] == gs_dyn_array_size((__SA)->data) - 1)\ {\ __H = __I;\ break;\ }\ }\ \ /* Swap and pop data */\ (__SA)->data[__OG_DATA_IDX] = gs_dyn_array_back((__SA)->data);\ gs_dyn_array_pop((__SA)->data);\ \ /* Point new handle, Set og handle to invalid */\ (__SA)->indices[__H] = __OG_DATA_IDX;\ (__SA)->indices[__H0] = GS_SLOT_ARRAY_INVALID_HANDLE;\ }\ } while (0) /*=== Slot Array Iterator ===*/ // Slot array iterator new typedef uint32_t gs_slot_array_iter; typedef gs_slot_array_iter gs_slot_array_iter_t; #define gs_slot_array_iter_valid(__SA, __IT)\ (__SA && gs_slot_array_exists(__SA, __IT)) gs_force_inline void _gs_slot_array_iter_advance_func(gs_dyn_array(uint32_t) indices, uint32_t* it) { if (!indices) { *it = GS_SLOT_ARRAY_INVALID_HANDLE; return; } (*it)++; for (; *it < (uint32_t)gs_dyn_array_size(indices); ++*it) {\ if (indices[*it] != GS_SLOT_ARRAY_INVALID_HANDLE)\ {\ break;\ }\ }\ } gs_force_inline uint32_t _gs_slot_array_iter_find_first_valid_index(gs_dyn_array(uint32_t) indices) { if (!indices) return GS_SLOT_ARRAY_INVALID_HANDLE; for (uint32_t i = 0; i < (uint32_t)gs_dyn_array_size(indices); ++i) { if (indices[i] != GS_SLOT_ARRAY_INVALID_HANDLE) { return i; } } return GS_SLOT_ARRAY_INVALID_HANDLE; } #define gs_slot_array_iter_new(__SA) (_gs_slot_array_iter_find_first_valid_index((__SA) ? (__SA)->indices : 0)) #define gs_slot_array_iter_advance(__SA, __IT)\ _gs_slot_array_iter_advance_func((__SA) ? (__SA)->indices : NULL, &(__IT)) #define gs_slot_array_iter_get(__SA, __IT)\ gs_slot_array_get(__SA, __IT) #define gs_slot_array_iter_getp(__SA, __IT)\ gs_slot_array_getp(__SA, __IT) /*=================================== // Slot Map ===================================*/ #define gs_slot_map(__SMK, __SMV)\ struct {\ gs_hash_table(__SMK, uint32_t) ht;\ gs_slot_array(__SMV) sa;\ }* #define gs_slot_map_new(__SMK, __SMV)\ NULL GS_API_DECL void** gs_slot_map_init(void** sm); // Could return something, I believe? #define gs_slot_map_insert(__SM, __SMK, __SMV)\ do {\ gs_slot_map_init((void**)&(__SM));\ uint32_t __H = gs_slot_array_insert((__SM)->sa, ((__SMV)));\ gs_hash_table_insert((__SM)->ht, (__SMK), __H);\ } while (0) #define gs_slot_map_get(__SM, __SMK)\ (gs_slot_array_get((__SM)->sa, gs_hash_table_get((__SM)->ht, (__SMK)))) #define gs_slot_map_getp(__SM, __SMK)\ (gs_slot_array_getp((__SM)->sa, gs_hash_table_get((__SM)->ht, (__SMK)))) #define gs_slot_map_size(__SM)\ (gs_slot_array_size((__SM)->sa)) #define gs_slot_map_clear(__SM)\ do {\ if ((__SM) != NULL) {\ gs_hash_table_clear((__SM)->ht);\ gs_slot_array_clear((__SM)->sa);\ }\ } while (0) #define gs_slot_map_erase(__SM, __SMK)\ do {\ uint32_t __K = gs_hash_table_get((__SM)->ht, (__SMK));\ gs_hash_table_erase((__SM)->ht, (__SMK));\ gs_slot_array_erase((__SM)->sa, __K);\ } while (0) #define gs_slot_map_free(__SM)\ do {\ if (__SM != NULL) {\ gs_hash_table_free((__SM)->ht);\ gs_slot_array_free((__SM)->sa);\ gs_free((__SM));\ (__SM) = NULL;\ }\ } while (0) #define gs_slot_map_capacity(__SM)\ (gs_hash_table_capacity((__SM)->ht)) /*=== Slot Map Iterator ===*/ typedef uint32_t gs_slot_map_iter; /* Find first valid iterator idx */ #define gs_slot_map_iter_new(__SM)\ gs_hash_table_iter_new((__SM)->ht) #define gs_slot_map_iter_valid(__SM, __IT)\ ((__IT) < gs_hash_table_capacity((__SM)->ht)) #define gs_slot_map_iter_advance(__SM, __IT)\ __gs_hash_table_iter_advance_func((void**)&((__SM)->ht->data), sizeof((__SM)->ht->tmp_key), sizeof((__SM)->ht->tmp_val), &(__IT), (__SM)->ht->stride, (__SM)->ht->klpvl) #define gs_slot_map_iter_getk(__SM, __IT)\ gs_hash_table_iter_getk((__SM)->ht, (__IT)) //(gs_hash_table_find_valid_iter(__SM->ht, __IT), gs_hash_table_geti((__SM)->ht, (__IT))) #define gs_slot_map_iter_getkp(__SM, __IT)\ (gs_hash_table_find_valid_iter(__SM->ht, __IT), &(gs_hash_table_geti((__SM)->ht, (__IT)))) #define gs_slot_map_iter_get(__SM, __IT)\ ((__SM)->sa->data[gs_hash_table_iter_get((__SM)->ht, (__IT))]) // ((__SM)->sa->data[gs_hash_table_geti((__SM)->ht, (__IT))]) // (gs_hash_table_find_valid_iter(__SM->ht, __IT), (__SM)->sa->data[gs_hash_table_geti((__SM)->ht, (__IT))]) #define gs_slot_map_iter_getp(__SM, __IT)\ (&((__SM)->sa->data[gs_hash_table_geti((__SM)->ht, (__IT))])) // (gs_hash_table_find_valid_iter(__SM->ht, __IT), &((__SM)->sa->data[gs_hash_table_geti((__SM)->ht, (__IT))])) /*=================================== // Priority Queue ===================================*/ // Min heap #define gs_pqueue(__T)\ struct\ {\ gs_dyn_array(__T) data;\ gs_dyn_array(int32_t) priority;\ __T tmp;\ }* #define gs_pqueue_parent_idx(I) gs_max((uint32_t)(ceil(((float)I / 2.f) - 1)), 0) #define gs_pqueue_child_left_idx(I) ((I * 2) + 1) #define gs_pqueue_child_right_idx(I) ((I * 2) + 2) GS_API_DECL void** gs_pqueue_init(void** pq, size_t sz); #define gs_pqueue_init_all(__PQ, __V)\ (gs_pqueue_init((void**)&(__PQ), sizeof(*(__PQ))), gs_dyn_array_init((void**)&((__PQ)->priority), sizeof(int32_t)),\ gs_dyn_array_init((void**)&((__PQ)->data), sizeof(__V))) #define gs_pqueue_size(__PQ)\ gs_dyn_array_size((__PQ)->data) #define gs_pqueue_capacity(__PQ)\ gs_dyn_array_capacity((__PQ)->data) #define gs_pqueue_clear(__PQ)\ do {\ gs_dyn_array_clear((__PQ)->data);\ gs_dyn_array_clear((__PQ)->priority);\ } while (0) #define gs_pqueue_empty(__PQ)\ (!(__PQ) || !gs_pqueue_size(__PQ)) #define __gs_pqueue_swp(__PQ, __I0, __I1, __SZ)\ do {\ /* Move data */\ {\ const size_t sz = (__SZ);\ memmove(&((__PQ)->tmp), &((__PQ)->data[__I0]), sz);\ memmove(&((__PQ)->data[__I0]), &((__PQ)->data[__I1]), sz);\ memmove(&((__PQ)->data[__I1]), &((__PQ)->tmp), sz);\ }\ /* Move priority */\ {\ int32_t tmp = 0;\ const size_t sz = sizeof(int32_t);\ memmove(&tmp, &((__PQ)->priority[__I0]), sz);\ memmove(&((__PQ)->priority[__I0]), &((__PQ)->priority[__I1]), sz);\ memmove(&((__PQ)->priority[__I1]), &tmp, sz);\ }\ } while (0) #define gs_pqueue_push(__PQ, __V, __PRI)\ do {\ /*Init*/\ gs_pqueue_init_all((__PQ), (__V));\ /*Push to end of array*/\ gs_dyn_array_push((__PQ)->data, (__V));\ gs_dyn_array_push((__PQ)->priority, (__PRI));\ /*Compare and sort up*/\ const size_t dsize = sizeof(__V);\ int32_t i = gs_max(gs_pqueue_size((__PQ)) - 1, 0);\ while (i)\ {\ /* Look at parent, compare, then swap indices with parent */\ int32_t pidx = gs_pqueue_parent_idx(i);\ if ((__PQ)->priority[pidx] > __PRI) {\ __gs_pqueue_swp(__PQ, i, pidx, dsize);\ }\ else {\ break;\ }\ i = pidx;\ }\ } while (0) #if 0 /* Need to call into another function to return what I need... Not sure how to do this, since I need to know what TYPE to return... */ #define gs_pqueue_pop(__PQ)\ (\ __gs_pqueue_pop_internal(\ (void**)&(__PQ),\ &(__PQ)->tmp,\ (void**)(&(__PQ)->data),\ (__PQ)->priority,\ gs_pqueue_size((__PQ)),\ sizeof((__PQ)->tmp)\ ),\ (__PQ)->tmp = (__PQ)->data[gs_pqueue_size((__PQ)) - 1],\ (gs_dyn_array_head((__PQ)->data))->size--,\ (gs_dyn_array_head((__PQ)->priority))->size--,\ (__PQ)->tmp\ ) #endif #if 1 /* No return. */ #define gs_pqueue_pop(__PQ)\ do {\ /* Swap elements */\ if (gs_pqueue_empty((__PQ))) break;\ __gs_pqueue_swp(__PQ, 0, gs_pqueue_size((__PQ)) - 1, sizeof((__PQ)->tmp));\ \ int32_t i = 0;\ int32_t c = 0;\ int32_t psz = gs_dyn_array_size((__PQ)->priority) - 1;\ for (int32_t i = 0; gs_pqueue_child_left_idx(i) < psz; i = c)\ {\ /* Set child to smaller of two */\ c = gs_pqueue_child_left_idx(i);\ \ /* Set to right child if valid and less priority */\ if ((c + 1) < psz && (__PQ)->priority[c + 1] < (__PQ)->priority[c]) {\ c++;\ }\ \ /* Check to swp, if necessary */\ if ((__PQ)->priority[i] > (__PQ)->priority[c]) {\ __gs_pqueue_swp((__PQ), i, c, sizeof((__PQ)->tmp));\ }\ /* Otherwise, we're done */\ else\ {\ break;\ }\ }\ (gs_dyn_array_head((__PQ)->data))->size--;\ (gs_dyn_array_head((__PQ)->priority))->size--;\ } while (0) #endif #if 1 /* */ GS_API_PRIVATE void __gs_pqueue_pop_internal(void** pqueue, void* tmp, void** data, int32_t* priority, int32_t pq_sz, size_t d_sz); #endif #define gs_pqueue_peek(__PQ)\ (__PQ)->data[0] #define gs_pqueue_peekp(__PQ)\ &((__PQ)->data[0]) #define gs_pqueue_peek_pri(__PQ)\ (__PQ)->priority[0] #define gs_pqueue_free(__PQ)\ do {\ if ((__PQ) && (__PQ)->data) gs_dyn_array_free((__PQ)->data);\ if ((__PQ) && (__PQ)->priority) gs_dyn_array_free((__PQ)->priority);\ if ((__PQ)) gs_free((__PQ));\ } while (0) /*=== Priority Queue Iterator ===*/ typedef uint32_t gs_pqueue_iter; typedef gs_pqueue_iter gs_pqueue_iter_t; #define gs_pqueue_iter_new(__PQ) 0 #define gs_pqueue_iter_valid(__PQ, __IT)\ ((__IT) < gs_pqueue_size((__PQ))) #define gs_pqueue_iter_advance(__PQ, __IT) ++(__IT) #define gs_pqueue_iter_get(__PQ, __IT)\ (__PQ)->data[(__IT)] #define gs_pqueue_iter_getp(__PQ, __IT)\ &(__PQ)->data[(__IT)] #define gs_pqueue_iter_get_pri(__PQ, __IT)\ (__PQ)->priority[(__IT)] #define gs_pqueue_iter_get_prip(__PQ, __IT)\ &(__PQ)->priority[(__IT)] /*=================================== // Command Buffer ===================================*/ typedef struct gs_command_buffer_t { uint32_t num_commands; gs_byte_buffer_t commands; } gs_command_buffer_t; gs_force_inline gs_command_buffer_t gs_command_buffer_new() { gs_command_buffer_t cb = gs_default_val(); cb.commands = gs_byte_buffer_new(); return cb; } #define gs_command_buffer_write(__CB, __CT, __C, __T, __VAL)\ do {\ gs_command_buffer_t* __cb = (__CB);\ __cb->num_commands++;\ gs_byte_buffer_write(&__cb->commands, __CT, (__C));\ gs_byte_buffer_write(&__cb->commands, __T, (__VAL));\ } while (0) gs_force_inline void gs_command_buffer_clear(gs_command_buffer_t* cb) { cb->num_commands = 0; gs_byte_buffer_clear(&cb->commands); } gs_force_inline void gs_command_buffer_free(gs_command_buffer_t* cb) { gs_byte_buffer_free(&cb->commands); } #define gs_command_buffer_readc(__CB, __T, __NAME)\ __T __NAME = gs_default_val();\ gs_byte_buffer_read(&(__CB)->commands, __T, &__NAME); #ifndef GS_NO_SHORT_NAME typedef gs_command_buffer_t gs_cmdbuf; #endif /** @} */ // end of gs_containers /*======================== // GS_MEMORY ========================*/ /** @defgroup gs_memory Memory * Gunslinger Memory * @{ */ #define gs_ptr_add(P, BYTES) \ (((uint8_t*)P + (BYTES))) typedef struct gs_memory_block_t { uint8_t* data; size_t size; } gs_memory_block_t; GS_API_DECL gs_memory_block_t gs_memory_block_new(size_t sz); GS_API_DECL void gs_memory_block_free(gs_memory_block_t* mem); GS_API_DECL size_t gs_memory_calc_padding(size_t base_address, size_t alignment); GS_API_DECL size_t gs_memory_calc_padding_w_header(size_t base_address, size_t alignment, size_t header_sz); /*================================================================================ // Linear Allocator ================================================================================*/ typedef struct gs_linear_allocator_t { uint8_t* memory; size_t total_size; size_t offset; } gs_linear_allocator_t; GS_API_DECL gs_linear_allocator_t gs_linear_allocator_new(size_t sz); GS_API_DECL void gs_linear_allocator_free(gs_linear_allocator_t* la); GS_API_DECL void* gs_linear_allocator_allocate(gs_linear_allocator_t* la, size_t sz, size_t alignment); GS_API_DECL void gs_linear_allocator_clear(gs_linear_allocator_t* la); /*================================================================================ // Stack Allocator ================================================================================*/ typedef struct gs_stack_allocator_header_t { uint32_t size; } gs_stack_allocator_header_t; typedef struct gs_stack_allocator_t { gs_memory_block_t memory; size_t offset; } gs_stack_allocator_t; GS_API_DECL gs_stack_allocator_t gs_stack_allocator_new(size_t sz); GS_API_DECL void gs_stack_allocator_free(gs_stack_allocator_t* sa); GS_API_DECL void* gs_stack_allocator_allocate(gs_stack_allocator_t* sa, size_t sz); GS_API_DECL void* gs_stack_allocator_peek(gs_stack_allocator_t* sa); GS_API_DECL void* gs_stack_allocator_pop(gs_stack_allocator_t* sa); GS_API_DECL void gs_stack_allocator_clear(gs_stack_allocator_t* sa); /*================================================================================ // Heap Allocator ================================================================================*/ #ifndef GS_HEAP_ALLOC_DEFAULT_SIZE #define GS_HEAP_ALLOC_DEFAULT_SIZE 1024 * 1024 * 20 #endif #ifndef GS_HEAP_ALLOC_DEFAULT_CAPCITY #define GS_HEAP_ALLOC_DEFAULT_CAPCITY 1024 #endif typedef struct gs_heap_allocator_header_t { struct gs_heap_allocator_header_t* next; struct gs_heap_allocator_header_t* prev; size_t size; } gs_heap_allocator_header_t; typedef struct gs_heap_allocator_free_block_t { gs_heap_allocator_header_t* header; size_t size; } gs_heap_allocator_free_block_t; typedef struct gs_heap_allocator_t { gs_heap_allocator_header_t* memory; gs_heap_allocator_free_block_t* free_blocks; uint32_t free_block_count; uint32_t free_block_capacity; } gs_heap_allocator_t; GS_API_DECL gs_heap_allocator_t gs_heap_allocate_new(); GS_API_DECL void gs_heap_allocator_free(gs_heap_allocator_t* ha); GS_API_DECL void* gs_heap_allocator_allocate(gs_heap_allocator_t* ha, size_t sz); GS_API_DECL void gs_heap_allocator_deallocate(gs_heap_allocator_t* ha, void* memory); /*================================================================================ // Pool Allocator ================================================================================*/ /*================================================================================ // Paged Allocator ================================================================================*/ typedef struct gs_paged_allocator_block_t { struct gs_paged_allocator_block_t* next; } gs_paged_allocator_block_t; typedef struct gs_paged_allocator_page_t { struct gs_paged_allocator_page_t* next; struct gs_paged_allocator_block_t* data; } gs_paged_allocator_page_t; typedef struct gs_paged_allocator_t { uint32_t block_size; uint32_t blocks_per_page; gs_paged_allocator_page_t* pages; uint32_t page_count; gs_paged_allocator_block_t* free_list; } gs_paged_allocator_t; GS_API_DECL gs_paged_allocator_t gs_paged_allocator_new(size_t element_size, size_t elements_per_page); GS_API_DECL void gs_paged_allocator_free(gs_paged_allocator_t* pa); GS_API_DECL void* gs_paged_allocator_allocate(gs_paged_allocator_t* pa); GS_API_DECL void gs_paged_allocator_deallocate(gs_paged_allocator_t* pa, void* data); GS_API_DECL void gs_paged_allocator_clear(gs_paged_allocator_t* pa); /** @} */ // end of gs_memory /*======================== // GS_MATH ========================*/ /** @defgroup gs_math Math * Gunslinger Math * @{ */ // Defines #define GS_EPSILON (1e-6) #define GS_PI 3.1415926535897932 #define GS_TAU 2.0 * GS_PI // Useful Utility #define gs_v2(...) gs_vec2_ctor(__VA_ARGS__) #define gs_v3(...) gs_vec3_ctor(__VA_ARGS__) #define gs_v4(...) gs_vec4_ctor(__VA_ARGS__) #define gs_quat(...) gs_quat_ctor(__VA_ARGS__) #define gs_v2s(__S) gs_vec2_ctor((__S), (__S)) #define gs_v3s(__S) gs_vec3_ctor((__S), (__S), (__S)) #define gs_v4s(__S) gs_vec4_ctor((__S), (__S), (__S), (__S)) #define gs_v4_xy_v(__X, __Y, __V) gs_vec4_ctor((__X), (__Y), (__V).x, (__V).y) #define gs_v4_xyz_s(__XYZ, __S) gs_vec4_ctor((__XYZ).x, (__XYZ).y, (__XYZ).z, (__S)) #define GS_XAXIS gs_v3(1.f, 0.f, 0.f) #define GS_YAXIS gs_v3(0.f, 1.f, 0.f) #define GS_ZAXIS gs_v3(0.f, 0.f, 1.f) /*================================================================================ // Useful Common Math Functions ================================================================================*/ #define gs_rad2deg(__R)\ (float)((__R * 180.0f) / GS_PI) #define gs_deg2rad(__D)\ (float)((__D * GS_PI) / 180.0f) // Interpolation // Source: https://codeplea.com/simple-interpolation // Returns v based on t gs_inline float gs_interp_linear(float a, float b, float t) { return (a + t * (b - a)); } // Returns t based on v gs_inline float gs_interp_linear_inv(float a, float b, float v) { return (v - a) / (b - a); } gs_inline float gs_interp_smoothstep(float a, float b, float t) { return gs_interp_linear(a, b, t * t * (3.0f - 2.0f * t)); } gs_inline float gs_interp_cosine(float a, float b, float t) { return gs_interp_linear(a, b, (float)-cos(GS_PI * t) * 0.5f + 0.5f); } gs_inline float gs_interp_acceleration(float a, float b, float t) { return gs_interp_linear(a, b, t * t); } gs_inline float gs_interp_deceleration(float a, float b, float t) { return gs_interp_linear(a, b, 1.0f - (1.0f - t) * (1.0f - t)); } gs_inline float gs_round(float val) { return (float)floor(val + 0.5f); } gs_inline float gs_map_range(float input_start, float input_end, float output_start, float output_end, float val) { float slope = (output_end - output_start) / (input_end - input_start); return (output_start + (slope * (val - input_start))); } // Easings from: https://github.com/raysan5/raylib/blob/ea0f6c7a26f3a61f3be542aa8f066ce033766a9f/examples/others/easings.h gs_inline float gs_ease_cubic_in(float t, float b, float c, float d) { t /= d; return (c*t*t*t + b); } gs_inline float gs_ease_cubic_out(float t, float b, float c, float d) { t = t/d - 1.0f; return (c*(t*t*t + 1.0f) + b); } gs_inline float gs_ease_cubic_in_out(float t, float b, float c, float d) { if ((t/=d/2.0f) < 1.0f) { return (c/2.0f*t*t*t + b); } t -= 2.0f; return (c/2.0f*(t*t*t + 2.0f) + b); } /*================================================================================ // Vec2 ================================================================================*/ /** @brief struct gs_vec2 in gs math */ typedef struct { union { f32 xy[2]; struct { f32 x, y; }; }; } gs_vec2_t; typedef gs_vec2_t gs_vec2; gs_inline gs_vec2 gs_vec2_ctor(f32 _x, f32 _y) { gs_vec2 v; v.x = _x; v.y = _y; return v; } gs_inline bool gs_vec2_nan(gs_vec2 v) { if (v.x != v.x || v.y != v.y) return true; return false; } gs_inline gs_vec2 gs_vec2_add(gs_vec2 v0, gs_vec2 v1) { return gs_vec2_ctor(v0.x + v1.x, v0.y + v1.y); } gs_inline gs_vec2 gs_vec2_sub(gs_vec2 v0, gs_vec2 v1) { return gs_vec2_ctor(v0.x - v1.x, v0.y - v1.y); } gs_inline gs_vec2 gs_vec2_mul(gs_vec2 v0, gs_vec2 v1) { return gs_vec2_ctor(v0.x * v1.x, v0.y * v1.y); } gs_inline gs_vec2 gs_vec2_div(gs_vec2 v0, gs_vec2 v1) { return gs_vec2_ctor(v0.x / v1.x, v0.y / v1.y); } gs_inline bool gs_vec2_equals(gs_vec2 v0, gs_vec2 v1) { return (v0.x == v1.x && v0.y == v1.y); } gs_inline gs_vec2 gs_vec2_scale(gs_vec2 v, f32 s) { return gs_vec2_ctor(v.x * s, v.y * s); } gs_inline f32 gs_vec2_dot(gs_vec2 v0, gs_vec2 v1) { return (f32)(v0.x * v1.x + v0.y * v1.y); } gs_inline f32 gs_vec2_len(gs_vec2 v) { return (f32)sqrt(gs_vec2_dot(v, v)); } gs_inline gs_vec2 gs_vec2_project_onto(gs_vec2 v0, gs_vec2 v1) { f32 dot = gs_vec2_dot(v0, v1); f32 len = gs_vec2_dot(v1, v1); // Orthogonal, so return v1 if (len == 0.f) return v1; return gs_vec2_scale(v1, dot / len); } gs_inline gs_vec2 gs_vec2_norm(gs_vec2 v) { f32 len = gs_vec2_len(v); return gs_vec2_scale(v, len != 0.f ? 1.0f / gs_vec2_len(v) : 1.f); } gs_inline f32 gs_vec2_dist(gs_vec2 a, gs_vec2 b) { f32 dx = (a.x - b.x); f32 dy = (a.y - b.y); return (float)(sqrt(dx * dx + dy * dy)); } gs_inline f32 gs_vec2_dist2(gs_vec2 a, gs_vec2 b) { f32 dx = (a.x - b.x); f32 dy = (a.y - b.y); return (float)(dx * dx + dy * dy); } gs_inline f32 gs_vec2_cross(gs_vec2 a, gs_vec2 b) { return a.x * b.y - a.y * b.x; } gs_inline f32 gs_vec2_angle(gs_vec2 a, gs_vec2 b) { return (float)acos(gs_vec2_dot(a, b) / (gs_vec2_len(a) * gs_vec2_len(b))); } gs_inline b32 gs_vec2_equal(gs_vec2 a, gs_vec2 b) { return (a.x == b.x && a.y == b.y); } /*================================================================================ // Vec3 ================================================================================*/ typedef struct { union { f32 xyz[3]; struct { f32 x, y, z; }; }; } gs_vec3_t; typedef gs_vec3_t gs_vec3; gs_inline gs_vec3 gs_vec3_ctor(f32 _x, f32 _y, f32 _z) { gs_vec3 v; v.x = _x; v.y = _y; v.z = _z; return v; } gs_inline bool gs_vec3_eq(gs_vec3 v0, gs_vec3 v1) { return (v0.x == v1.x && v0.y == v1.y && v0.z == v1.z); } gs_inline gs_vec3 gs_vec3_add(gs_vec3 v0, gs_vec3 v1) { return gs_vec3_ctor(v0.x + v1.x, v0.y + v1.y, v0.z + v1.z); } gs_inline gs_vec3 gs_vec3_sub(gs_vec3 v0, gs_vec3 v1) { return gs_vec3_ctor(v0.x - v1.x, v0.y - v1.y, v0.z - v1.z); } gs_inline gs_vec3 gs_vec3_mul(gs_vec3 v0, gs_vec3 v1) { return gs_vec3_ctor(v0.x * v1.x, v0.y * v1.y, v0.z * v1.z); } gs_inline gs_vec3 gs_vec3_div(gs_vec3 v0, gs_vec3 v1) { return gs_vec3_ctor(v0.x / v1.x, v0.y / v1.y, v0.z / v1.z); } gs_inline gs_vec3 gs_vec3_scale(gs_vec3 v, f32 s) { return gs_vec3_ctor(v.x * s, v.y * s, v.z * s); } gs_inline gs_vec3 gs_vec3_neg(gs_vec3 v) { return gs_vec3_scale(v, -1.f); } gs_inline f32 gs_vec3_dot(gs_vec3 v0, gs_vec3 v1) { f32 dot = (f32)((v0.x * v1.x) + (v0.y * v1.y) + v0.z * v1.z); return dot; } gs_inline bool gs_vec3_same_dir(gs_vec3 v0, gs_vec3 v1) { return (gs_vec3_dot(v0, v1) > 0.f); } gs_inline gs_vec3 gs_vec3_sign(gs_vec3 v) { return (gs_vec3_ctor( v.x < 0.f ? -1.f : v.x > 0.f ? 1.f : 0.f, v.y < 0.f ? -1.f : v.y > 0.f ? 1.f : 0.f, v.z < 0.f ? -1.f : v.z > 0.f ? 1.f : 0.f )); } gs_inline float gs_vec3_signX(gs_vec3 v) { return (v.x < 0.f ? -1.f : v.x > 0.f ? 1.f : 0.f); } gs_inline float gs_vec3_signY(gs_vec3 v) { return (v.y < 0.f ? -1.f : v.y > 0.f ? 1.f : 0.f); } gs_inline float gs_vec3_signZ(gs_vec3 v) { return (v.z < 0.f ? -1.f : v.z > 0.f ? 1.f : 0.f); } gs_inline f32 gs_vec3_len(gs_vec3 v) { return (f32)sqrt(gs_vec3_dot(v, v)); } gs_inline f32 gs_vec3_len2(gs_vec3 v) { return (f32)(gs_vec3_dot(v, v)); } gs_inline gs_vec3 gs_vec3_project_onto(gs_vec3 v0, gs_vec3 v1) { f32 dot = gs_vec3_dot(v0, v1); f32 len = gs_vec3_dot(v1, v1); // Orthogonal, so return v1 if (len == 0.f) return v1; return gs_vec3_scale(v1, dot / len); } gs_inline bool gs_vec3_nan(gs_vec3 v) { if (v.x != v.x || v.y != v.y || v.z != v.z) return true; return false; } gs_inline f32 gs_vec3_dist2(gs_vec3 a, gs_vec3 b) { f32 dx = (a.x - b.x); f32 dy = (a.y - b.y); f32 dz = (a.z - b.z); return (dx * dx + dy * dy + dz * dz); } gs_inline f32 gs_vec3_dist(gs_vec3 a, gs_vec3 b) { return sqrt(gs_vec3_dist2(a, b)); } gs_inline gs_vec3 gs_vec3_norm(gs_vec3 v) { f32 len = gs_vec3_len(v); return len == 0.f ? v : gs_vec3_scale(v, 1.f / len); } gs_inline gs_vec3 gs_vec3_cross(gs_vec3 v0, gs_vec3 v1) { return gs_vec3_ctor ( v0.y * v1.z - v0.z * v1.y, v0.z * v1.x - v0.x * v1.z, v0.x * v1.y - v0.y * v1.x ); } gs_inline void gs_vec3_scale_ip(gs_vec3* vp, f32 s) { vp->x *= s; vp->y *= s; vp->z *= s; } gs_inline float gs_vec3_angle_between(gs_vec3 v0, gs_vec3 v1) { return acosf(gs_vec3_dot(v0, v1)); } gs_inline float gs_vec3_angle_between_signed(gs_vec3 v0, gs_vec3 v1) { return asinf(gs_vec3_len(gs_vec3_cross(v0, v1))); } gs_inline gs_vec3 gs_vec3_triple_cross_product(gs_vec3 a, gs_vec3 b, gs_vec3 c) { return gs_vec3_sub((gs_vec3_scale(b, gs_vec3_dot(c, a))), (gs_vec3_scale(a, gs_vec3_dot(c, b)))); } /*================================================================================ // Vec4 ================================================================================*/ typedef struct { union { f32 xyzw[4]; struct { f32 x, y, z, w; }; }; } gs_vec4_t; typedef gs_vec4_t gs_vec4; gs_inline gs_vec4 gs_vec4_ctor(f32 _x, f32 _y, f32 _z, f32 _w) { gs_vec4 v; v.x = _x; v.y = _y; v.z = _z; v.w = _w; return v; } gs_inline gs_vec4 gs_vec4_add(gs_vec4 v0, gs_vec4 v1) { return gs_vec4_ctor(v0.x + v1.x, v0.y + v1.y, v0.z + v1.z, v0.w + v1.w); } gs_inline gs_vec4 gs_vec4_sub(gs_vec4 v0, gs_vec4 v1) { return gs_vec4_ctor(v0.x - v1.x, v0.y - v1.y, v0.z - v1.z, v0.w - v1.w); } gs_inline gs_vec4 gs_vec4_mul(gs_vec4 v0, gs_vec4 v1) { return gs_vec4_ctor(v0.x * v1.x, v0.y * v1.y, v0.z * v1.z, v0.w * v1.w); } gs_inline gs_vec4 gs_vec4_div(gs_vec4 v0, gs_vec4 v1) { return gs_vec4_ctor(v0.x / v1.x, v0.y / v1.y, v0.z / v1.z, v0.w / v1.w); } gs_inline gs_vec4 gs_vec4_scale(gs_vec4 v, f32 s) { return gs_vec4_ctor(v.x * s, v.y * s, v.z * s, v.w * s); } gs_inline f32 gs_vec4_dot(gs_vec4 v0, gs_vec4 v1) { return (f32)(v0.x * v1.x + v0.y * v1.y + v0.z * v1.z + v0.w * v1.w); } gs_inline f32 gs_vec4_len(gs_vec4 v) { return (f32)sqrt(gs_vec4_dot(v, v)); } gs_inline gs_vec4 gs_vec4_project_onto(gs_vec4 v0, gs_vec4 v1) { f32 dot = gs_vec4_dot(v0, v1); f32 len = gs_vec4_dot(v1, v1); // Orthogonal, so return v1 if (len == 0.f) return v1; return gs_vec4_scale(v1, dot / len); } gs_inline gs_vec4 gs_vec4_norm(gs_vec4 v) { return gs_vec4_scale(v, 1.0f / gs_vec4_len(v)); } gs_inline f32 gs_vec4_dist(gs_vec4 v0, gs_vec4 v1) { f32 dx = (v0.x - v1.x); f32 dy = (v0.y - v1.y); f32 dz = (v0.z - v1.z); f32 dw = (v0.w - v1.w); return (float)(sqrt(dx * dx + dy * dy + dz * dz + dw * dw)); } /*================================================================================ // Useful Vector Functions ================================================================================*/ gs_inline gs_vec3 gs_v4tov3(gs_vec4 v) { return gs_v3(v.x, v.y, v.z); } gs_inline gs_vec2 gs_v3tov2(gs_vec3 v) { return gs_v2(v.x, v.y); } gs_inline gs_vec3 gs_v2tov3(gs_vec2 v) { return gs_v3(v.x, v.y, 0.f); } /*================================================================================ // Mat3x3 ================================================================================*/ /* Matrices are stored in linear, contiguous memory and assume a column-major ordering. */ typedef struct gs_mat3 { f32 m[9]; } gs_mat3; gs_inline gs_mat3 gs_mat3_diag(float val) { gs_mat3 m = gs_default_val(); m.m[0 + 0 * 3] = val; m.m[1 + 1 * 3] = val; m.m[2 + 2 * 3] = val; return m; } #define gs_mat3_identity()\ gs_mat3_diag(1.f) gs_inline gs_mat3 gs_mat3_mul(gs_mat3 m0, gs_mat3 m1) { gs_mat3 m = gs_default_val(); for (u32 y = 0; y < 3; ++y) { for (u32 x = 0; x < 3; ++x) { f32 sum = 0.0f; for (u32 e = 0; e < 3; ++e) { sum += m0.m[x + e * 3] * m1.m[e + y * 3]; } m.m[x + y * 3] = sum; } } return m; } gs_inline gs_vec3 gs_mat3_mul_vec3(gs_mat3 m, gs_vec3 v) { return gs_vec3_ctor( m.m[0] * v.x + m.m[1] * v.y + m.m[2] * v.z, m.m[3] * v.x + m.m[4] * v.y + m.m[5] * v.z, m.m[6] * v.x + m.m[7] * v.y + m.m[8] * v.z ); } gs_inline gs_mat3 gs_mat3_scale(float x, float y, float z) { gs_mat3 m = gs_default_val(); m.m[0] = x; m.m[4] = y; m.m[8] = z; return m; } gs_inline gs_mat3 gs_mat3_rotate(float radians, float x, float y, float z) { gs_mat3 m = gs_default_val(); float s = sinf(radians), c = cosf(radians), c1 = 1.f - c; float xy = x * y; float yz = y * z; float zx = z * x; float xs = x * s; float ys = y * s; float zs = z * s; m.m[0] = c1 * x * x + c; m.m[1] = c1 * xy - zs; m.m[2] = c1 * zx + ys; m.m[3] = c1 * xy + zs; m.m[4] = c1 * y * y + c; m.m[5] = c1 * yz - xs; m.m[6] = c1 * zx - ys; m.m[7] = c1 * yz + xs; m.m[8] = c1 * z * z + c; return m; } gs_inline gs_mat3 gs_mat3_rotatev(float radians, gs_vec3 axis) { return gs_mat3_rotate(radians, axis.x, axis.y, axis.z); } // Turn quaternion into mat3 gs_inline gs_mat3 gs_mat3_rotateq(gs_vec4 q) { gs_mat3 m = gs_default_val(); float x2 = q.x * q.x, y2 = q.y * q.y, z2 = q.z * q.z, w2 = q.w * q.w; float xz = q.x *q.z, xy = q.x * q.y, yz = q.y * q.z, wz = q.w * q.z, wy = q.w * q.y, wx = q.w * q.x; m.m[0] = 1 - 2 * (y2 + z2); m.m[1] = 2 * (xy + wz); m.m[2] = 2 * (xz - wy); m.m[3] = 2 * (xy - wz); m.m[4] = 1 - 2 * (x2 + z2); m.m[5] = 2 * (yz + wx); m.m[6] = 2 * (xz + wy); m.m[7] = 2 * (yz - wx); m.m[8] = 1 - 2 * (x2 + y2); return m; } gs_inline gs_mat3 gs_mat3_rsq(gs_vec4 q, gs_vec3 s) { gs_mat3 mr = gs_mat3_rotateq(q); gs_mat3 ms = gs_mat3_scale(s.x, s.y, s.z); return gs_mat3_mul(mr, ms); } gs_inline gs_mat3 gs_mat3_inverse(gs_mat3 m) { gs_mat3 r = gs_default_val(); double det = (double)(m.m[0 * 3 + 0] * (m.m[1 * 3 + 1] * m.m[2 * 3 + 2] - m.m[2 * 3 + 1] * m.m[1 * 3 + 2]) - m.m[0 * 3 + 1] * (m.m[1 * 3 + 0] * m.m[2 * 3 + 2] - m.m[1 * 3 + 2] * m.m[2 * 3 + 0]) + m.m[0 * 3 + 2] * (m.m[1 * 3 + 0] * m.m[2 * 3 + 1] - m.m[1 * 3 + 1] * m.m[2 * 3 + 0])); double inv_det = det ? 1.0 / det : 0.0; r.m[0 * 3 + 0] = (m.m[1 * 3 + 1] * m.m[2 * 3 + 2] - m.m[2 * 3 + 1] * m.m[1 * 3 + 2]) * inv_det; r.m[0 * 3 + 1] = (m.m[0 * 3 + 2] * m.m[2 * 3 + 1] - m.m[0 * 3 + 1] * m.m[2 * 3 + 2]) * inv_det; r.m[0 * 3 + 2] = (m.m[0 * 3 + 1] * m.m[1 * 3 + 2] - m.m[0 * 3 + 2] * m.m[1 * 3 + 1]) * inv_det; r.m[1 * 3 + 0] = (m.m[1 * 3 + 2] * m.m[2 * 3 + 0] - m.m[1 * 3 + 0] * m.m[2 * 3 + 2]) * inv_det; r.m[1 * 3 + 1] = (m.m[0 * 3 + 0] * m.m[2 * 3 + 2] - m.m[0 * 3 + 2] * m.m[2 * 3 + 0]) * inv_det; r.m[1 * 3 + 2] = (m.m[1 * 3 + 0] * m.m[0 * 3 + 2] - m.m[0 * 3 + 0] * m.m[1 * 3 + 2]) * inv_det; r.m[2 * 3 + 0] = (m.m[1 * 3 + 0] * m.m[2 * 3 + 1] - m.m[2 * 3 + 0] * m.m[1 * 3 + 1]) * inv_det; r.m[2 * 3 + 1] = (m.m[2 * 3 + 0] * m.m[0 * 3 + 1] - m.m[0 * 3 + 0] * m.m[2 * 3 + 1]) * inv_det; r.m[2 * 3 + 2] = (m.m[0 * 3 + 0] * m.m[1 * 3 + 1] - m.m[1 * 3 + 0] * m.m[0 * 3 + 1]) * inv_det; return r; } /*================================================================================ // Mat4x4 ================================================================================*/ /* Matrices are stored in linear, contiguous memory and assume a column-major ordering. */ typedef struct gs_mat4 { union { gs_vec4 rows[4]; float m[4][4]; float elements[16]; struct { gs_vec4 right, up, dir, position; } v; }; } gs_mat4_t; typedef gs_mat4_t gs_mat4; gs_inline gs_mat4 gs_mat4_diag(f32 val) { gs_mat4 m; memset(m.elements, 0, sizeof(m.elements)); m.elements[0 + 0 * 4] = val; m.elements[1 + 1 * 4] = val; m.elements[2 + 2 * 4] = val; m.elements[3 + 3 * 4] = val; return m; } #define gs_mat4_identity()\ gs_mat4_diag(1.0f) gs_inline gs_mat4 gs_mat4_ctor() { gs_mat4 mat = gs_default_val(); return mat; } gs_inline gs_mat4 gs_mat4_elem(const float* elements) { gs_mat4 mat = gs_mat4_ctor(); memcpy(mat.elements, elements, sizeof(f32) * 16); return mat; } gs_inline gs_mat4 gs_mat4_mul(gs_mat4 m0, gs_mat4 m1) { gs_mat4 m_res = gs_mat4_ctor(); for (u32 y = 0; y < 4; ++y) { for (u32 x = 0; x < 4; ++x) { f32 sum = 0.0f; for (u32 e = 0; e < 4; ++e) { sum += m0.elements[x + e * 4] * m1.elements[e + y * 4]; } m_res.elements[x + y * 4] = sum; } } return m_res; } gs_inline gs_mat4 gs_mat4_mul_list(uint32_t count, ...) { va_list ap; gs_mat4 m = gs_mat4_identity(); va_start(ap, count); for (uint32_t i = 0; i < count; ++i) { m = gs_mat4_mul(m, va_arg(ap, gs_mat4)); } va_end(ap); return m; } gs_inline void gs_mat4_set_elements(gs_mat4* m, float* elements, uint32_t count) { for (u32 i = 0; i < count; ++i) { m->elements[i] = elements[i]; } } gs_inline gs_mat4 gs_mat4_ortho_norm(const gs_mat4* m) { gs_mat4 r = *m; r.v.right = gs_vec4_norm(r.v.right); r.v.up = gs_vec4_norm(r.v.up); r.v.dir = gs_vec4_norm(r.v.dir); return r; } gs_inline gs_mat4 gs_mat4_transpose(gs_mat4 m) { gs_mat4 t = gs_mat4_identity(); // First row t.elements[0 * 4 + 0] = m.elements[0 * 4 + 0]; t.elements[1 * 4 + 0] = m.elements[0 * 4 + 1]; t.elements[2 * 4 + 0] = m.elements[0 * 4 + 2]; t.elements[3 * 4 + 0] = m.elements[0 * 4 + 3]; // Second row t.elements[0 * 4 + 1] = m.elements[1 * 4 + 0]; t.elements[1 * 4 + 1] = m.elements[1 * 4 + 1]; t.elements[2 * 4 + 1] = m.elements[1 * 4 + 2]; t.elements[3 * 4 + 1] = m.elements[1 * 4 + 3]; // Third row t.elements[0 * 4 + 2] = m.elements[2 * 4 + 0]; t.elements[1 * 4 + 2] = m.elements[2 * 4 + 1]; t.elements[2 * 4 + 2] = m.elements[2 * 4 + 2]; t.elements[3 * 4 + 2] = m.elements[2 * 4 + 3]; // Fourth row t.elements[0 * 4 + 3] = m.elements[3 * 4 + 0]; t.elements[1 * 4 + 3] = m.elements[3 * 4 + 1]; t.elements[2 * 4 + 3] = m.elements[3 * 4 + 2]; t.elements[3 * 4 + 3] = m.elements[3 * 4 + 3]; return t; } gs_inline gs_mat4 gs_mat4_inverse(gs_mat4 m) { gs_mat4 res = gs_mat4_identity(); f32 temp[16]; temp[0] = m.elements[5] * m.elements[10] * m.elements[15] - m.elements[5] * m.elements[11] * m.elements[14] - m.elements[9] * m.elements[6] * m.elements[15] + m.elements[9] * m.elements[7] * m.elements[14] + m.elements[13] * m.elements[6] * m.elements[11] - m.elements[13] * m.elements[7] * m.elements[10]; temp[4] = -m.elements[4] * m.elements[10] * m.elements[15] + m.elements[4] * m.elements[11] * m.elements[14] + m.elements[8] * m.elements[6] * m.elements[15] - m.elements[8] * m.elements[7] * m.elements[14] - m.elements[12] * m.elements[6] * m.elements[11] + m.elements[12] * m.elements[7] * m.elements[10]; temp[8] = m.elements[4] * m.elements[9] * m.elements[15] - m.elements[4] * m.elements[11] * m.elements[13] - m.elements[8] * m.elements[5] * m.elements[15] + m.elements[8] * m.elements[7] * m.elements[13] + m.elements[12] * m.elements[5] * m.elements[11] - m.elements[12] * m.elements[7] * m.elements[9]; temp[12] = -m.elements[4] * m.elements[9] * m.elements[14] + m.elements[4] * m.elements[10] * m.elements[13] + m.elements[8] * m.elements[5] * m.elements[14] - m.elements[8] * m.elements[6] * m.elements[13] - m.elements[12] * m.elements[5] * m.elements[10] + m.elements[12] * m.elements[6] * m.elements[9]; temp[1] = -m.elements[1] * m.elements[10] * m.elements[15] + m.elements[1] * m.elements[11] * m.elements[14] + m.elements[9] * m.elements[2] * m.elements[15] - m.elements[9] * m.elements[3] * m.elements[14] - m.elements[13] * m.elements[2] * m.elements[11] + m.elements[13] * m.elements[3] * m.elements[10]; temp[5] = m.elements[0] * m.elements[10] * m.elements[15] - m.elements[0] * m.elements[11] * m.elements[14] - m.elements[8] * m.elements[2] * m.elements[15] + m.elements[8] * m.elements[3] * m.elements[14] + m.elements[12] * m.elements[2] * m.elements[11] - m.elements[12] * m.elements[3] * m.elements[10]; temp[9] = -m.elements[0] * m.elements[9] * m.elements[15] + m.elements[0] * m.elements[11] * m.elements[13] + m.elements[8] * m.elements[1] * m.elements[15] - m.elements[8] * m.elements[3] * m.elements[13] - m.elements[12] * m.elements[1] * m.elements[11] + m.elements[12] * m.elements[3] * m.elements[9]; temp[13] = m.elements[0] * m.elements[9] * m.elements[14] - m.elements[0] * m.elements[10] * m.elements[13] - m.elements[8] * m.elements[1] * m.elements[14] + m.elements[8] * m.elements[2] * m.elements[13] + m.elements[12] * m.elements[1] * m.elements[10] - m.elements[12] * m.elements[2] * m.elements[9]; temp[2] = m.elements[1] * m.elements[6] * m.elements[15] - m.elements[1] * m.elements[7] * m.elements[14] - m.elements[5] * m.elements[2] * m.elements[15] + m.elements[5] * m.elements[3] * m.elements[14] + m.elements[13] * m.elements[2] * m.elements[7] - m.elements[13] * m.elements[3] * m.elements[6]; temp[6] = -m.elements[0] * m.elements[6] * m.elements[15] + m.elements[0] * m.elements[7] * m.elements[14] + m.elements[4] * m.elements[2] * m.elements[15] - m.elements[4] * m.elements[3] * m.elements[14] - m.elements[12] * m.elements[2] * m.elements[7] + m.elements[12] * m.elements[3] * m.elements[6]; temp[10] = m.elements[0] * m.elements[5] * m.elements[15] - m.elements[0] * m.elements[7] * m.elements[13] - m.elements[4] * m.elements[1] * m.elements[15] + m.elements[4] * m.elements[3] * m.elements[13] + m.elements[12] * m.elements[1] * m.elements[7] - m.elements[12] * m.elements[3] * m.elements[5]; temp[14] = -m.elements[0] * m.elements[5] * m.elements[14] + m.elements[0] * m.elements[6] * m.elements[13] + m.elements[4] * m.elements[1] * m.elements[14] - m.elements[4] * m.elements[2] * m.elements[13] - m.elements[12] * m.elements[1] * m.elements[6] + m.elements[12] * m.elements[2] * m.elements[5]; temp[3] = -m.elements[1] * m.elements[6] * m.elements[11] + m.elements[1] * m.elements[7] * m.elements[10] + m.elements[5] * m.elements[2] * m.elements[11] - m.elements[5] * m.elements[3] * m.elements[10] - m.elements[9] * m.elements[2] * m.elements[7] + m.elements[9] * m.elements[3] * m.elements[6]; temp[7] = m.elements[0] * m.elements[6] * m.elements[11] - m.elements[0] * m.elements[7] * m.elements[10] - m.elements[4] * m.elements[2] * m.elements[11] + m.elements[4] * m.elements[3] * m.elements[10] + m.elements[8] * m.elements[2] * m.elements[7] - m.elements[8] * m.elements[3] * m.elements[6]; temp[11] = -m.elements[0] * m.elements[5] * m.elements[11] + m.elements[0] * m.elements[7] * m.elements[9] + m.elements[4] * m.elements[1] * m.elements[11] - m.elements[4] * m.elements[3] * m.elements[9] - m.elements[8] * m.elements[1] * m.elements[7] + m.elements[8] * m.elements[3] * m.elements[5]; temp[15] = m.elements[0] * m.elements[5] * m.elements[10] - m.elements[0] * m.elements[6] * m.elements[9] - m.elements[4] * m.elements[1] * m.elements[10] + m.elements[4] * m.elements[2] * m.elements[9] + m.elements[8] * m.elements[1] * m.elements[6] - m.elements[8] * m.elements[2] * m.elements[5]; float determinant = m.elements[0] * temp[0] + m.elements[1] * temp[4] + m.elements[2] * temp[8] + m.elements[3] * temp[12]; determinant = 1.0f / determinant; for (int i = 0; i < 4 * 4; i++) res.elements[i] = (float)(temp[i] * (float)determinant); return res; } /* f32 l : left f32 r : right f32 b : bottom f32 t : top f32 n : near f32 f : far */ gs_inline gs_mat4 gs_mat4_ortho(f32 l, f32 r, f32 b, f32 t, f32 n, f32 f) { gs_mat4 m_res = gs_mat4_identity(); // Main diagonal m_res.elements[0 + 0 * 4] = 2.0f / (r - l); m_res.elements[1 + 1 * 4] = 2.0f / (t - b); m_res.elements[2 + 2 * 4] = -2.0f / (f - n); // Last column m_res.elements[0 + 3 * 4] = -(r + l) / (r - l); m_res.elements[1 + 3 * 4] = -(t + b) / (t - b); m_res.elements[2 + 3 * 4] = -(f + n) / (f - n); return m_res; } gs_inline gs_mat4 gs_mat4_perspective(f32 fov, f32 asp_ratio, f32 n, f32 f) { // Zero matrix gs_mat4 m_res = gs_mat4_ctor(); f32 q = 1.0f / (float)tan(gs_deg2rad(0.5f * fov)); f32 a = q / asp_ratio; f32 b = (n + f) / (n - f); f32 c = (2.0f * n * f) / (n - f); m_res.elements[0 + 0 * 4] = a; m_res.elements[1 + 1 * 4] = q; m_res.elements[2 + 2 * 4] = b; m_res.elements[2 + 3 * 4] = c; m_res.elements[3 + 2 * 4] = -1.0f; return m_res; } gs_inline gs_mat4 gs_mat4_translatev(const gs_vec3 v) { gs_mat4 m_res = gs_mat4_identity(); m_res.elements[0 + 4 * 3] = v.x; m_res.elements[1 + 4 * 3] = v.y; m_res.elements[2 + 4 * 3] = v.z; return m_res; } gs_inline gs_mat4 gs_mat4_translate(float x, float y, float z) { return gs_mat4_translatev(gs_v3(x, y, z)); } gs_inline gs_mat4 gs_mat4_scalev(const gs_vec3 v) { gs_mat4 m_res = gs_mat4_identity(); m_res.elements[0 + 0 * 4] = v.x; m_res.elements[1 + 1 * 4] = v.y; m_res.elements[2 + 2 * 4] = v.z; return m_res; } gs_inline gs_mat4 gs_mat4_scale(float x, float y, float z) { return (gs_mat4_scalev(gs_v3(x, y, z))); } // Assumes normalized axis gs_inline gs_mat4 gs_mat4_rotatev(float angle, gs_vec3 axis) { gs_mat4 m_res = gs_mat4_identity(); float a = angle; float c = (float)cos(a); float s = (float)sin(a); gs_vec3 naxis = gs_vec3_norm(axis); float x = naxis.x; float y = naxis.y; float z = naxis.z; //First column m_res.elements[0 + 0 * 4] = x * x * (1 - c) + c; m_res.elements[1 + 0 * 4] = x * y * (1 - c) + z * s; m_res.elements[2 + 0 * 4] = x * z * (1 - c) - y * s; //Second column m_res.elements[0 + 1 * 4] = x * y * (1 - c) - z * s; m_res.elements[1 + 1 * 4] = y * y * (1 - c) + c; m_res.elements[2 + 1 * 4] = y * z * (1 - c) + x * s; //Third column m_res.elements[0 + 2 * 4] = x * z * (1 - c) + y * s; m_res.elements[1 + 2 * 4] = y * z * (1 - c) - x * s; m_res.elements[2 + 2 * 4] = z * z * (1 - c) + c; return m_res; } gs_inline gs_mat4 gs_mat4_rotate(float angle, float x, float y, float z) { return gs_mat4_rotatev(angle, gs_v3(x, y, z)); } gs_inline gs_mat4 gs_mat4_look_at(gs_vec3 position, gs_vec3 target, gs_vec3 up) { gs_vec3 f = gs_vec3_norm(gs_vec3_sub(target, position)); gs_vec3 s = gs_vec3_norm(gs_vec3_cross(f, up)); gs_vec3 u = gs_vec3_cross(s, f); gs_mat4 m_res = gs_mat4_identity(); m_res.elements[0 * 4 + 0] = s.x; m_res.elements[1 * 4 + 0] = s.y; m_res.elements[2 * 4 + 0] = s.z; m_res.elements[0 * 4 + 1] = u.x; m_res.elements[1 * 4 + 1] = u.y; m_res.elements[2 * 4 + 1] = u.z; m_res.elements[0 * 4 + 2] = -f.x; m_res.elements[1 * 4 + 2] = -f.y; m_res.elements[2 * 4 + 2] = -f.z; m_res.elements[3 * 4 + 0] = -gs_vec3_dot(s, position);; m_res.elements[3 * 4 + 1] = -gs_vec3_dot(u, position); m_res.elements[3 * 4 + 2] = gs_vec3_dot(f, position); return m_res; } // Modified from github.com/CedricGuillemet/ImGuizmo/blob/master/ImGuizmo.cpp gs_inline void gs_mat4_decompose(const gs_mat4* m, float* translation, float* rotation, float* scale) { gs_mat4 mat = *m; scale[0] = gs_vec4_len(mat.v.right); scale[1] = gs_vec4_len(mat.v.up); scale[2] = gs_vec4_len(mat.v.dir); mat = gs_mat4_ortho_norm(&mat); rotation[0] = gs_rad2deg(atan2f(mat.m[1][2], mat.m[2][2])); rotation[1] = gs_rad2deg(atan2f(-mat.m[0][2], sqrtf(mat.m[1][2] * mat.m[1][2] + mat.m[2][2] * mat.m[2][2]))); rotation[2] = gs_rad2deg(atan2f(mat.m[0][1], mat.m[0][0])); translation[0] = mat.v.position.x; translation[1] = mat.v.position.y; translation[2] = mat.v.position.z; } // Modified from github.com/CedricGuillemet/ImGuizmo/blob/master/ImGuizmo.cpp gs_inline gs_mat4 gs_mat4_recompose(const float* translation, const float* rotation, const float* scale) { gs_mat4 mat = gs_mat4_identity(); gs_vec3 direction_unary[3] = { GS_XAXIS, GS_YAXIS, GS_ZAXIS }; gs_mat4 rot[3] = {gs_mat4_identity(), gs_mat4_identity(), gs_mat4_identity()}; for (uint32_t i = 0; i < 3; ++i) { rot[i] = gs_mat4_rotatev(gs_deg2rad(rotation[i]), direction_unary[i]); } mat = gs_mat4_mul_list(3, rot[2], rot[1], rot[0]); float valid_scale[3] = gs_default_val(); for (uint32_t i = 0; i < 3; ++i) { valid_scale[i] = fabsf(scale[i]) < GS_EPSILON ? 0.001f : scale[i]; } mat.v.right = gs_vec4_scale(mat.v.right, valid_scale[0]); mat.v.up = gs_vec4_scale(mat.v.up, valid_scale[1]); mat.v.dir = gs_vec4_scale(mat.v.dir, valid_scale[2]); mat.v.position = gs_v4(translation[0], translation[1], translation[2], 1.f); return mat; } gs_inline gs_vec4 gs_mat4_mul_vec4(gs_mat4 m, gs_vec4 v) { return gs_vec4_ctor ( m.elements[0 + 4 * 0] * v.x + m.elements[0 + 4 * 1] * v.y + m.elements[0 + 4 * 2] * v.z + m.elements[0 + 4 * 3] * v.w, m.elements[1 + 4 * 0] * v.x + m.elements[1 + 4 * 1] * v.y + m.elements[1 + 4 * 2] * v.z + m.elements[1 + 4 * 3] * v.w, m.elements[2 + 4 * 0] * v.x + m.elements[2 + 4 * 1] * v.y + m.elements[2 + 4 * 2] * v.z + m.elements[2 + 4 * 3] * v.w, m.elements[3 + 4 * 0] * v.x + m.elements[3 + 4 * 1] * v.y + m.elements[3 + 4 * 2] * v.z + m.elements[3 + 4 * 3] * v.w ); } gs_inline gs_vec3 gs_mat4_mul_vec3(gs_mat4 m, gs_vec3 v) { return gs_v4tov3(gs_mat4_mul_vec4(m, gs_v4_xyz_s(v, 1.f))); } /*================================================================================ // Quaternion ================================================================================*/ typedef struct { union { struct { union { gs_vec3 xyz; gs_vec3 axis; } axis; float a; } aa; gs_vec4 v; f32 xyzw[4]; struct { f32 x, y, z, w; }; }; } gs_quat_t; typedef gs_quat_t gs_quat; gs_inline gs_quat gs_quat_default() { gs_quat q; q.x = 0.f; q.y = 0.f; q.z = 0.f; q.w = 1.f; return q; } gs_inline gs_quat gs_quat_ctor(f32 _x, f32 _y, f32 _z, f32 _w) { gs_quat q; q.x = _x; q.y = _y; q.z = _z; q.w = _w; return q; } gs_inline gs_quat gs_quat_add(gs_quat q0, gs_quat q1) { return gs_quat_ctor(q0.x + q1.x, q0.y + q1.y, q0.z + q1.z, q0.w + q1.w); } gs_inline gs_quat gs_quat_sub(gs_quat q0, gs_quat q1) { return gs_quat_ctor(q0.x - q1.x, q0.y - q1.y, q0.z - q1.z, q0.w - q1.w); } gs_inline gs_quat gs_quat_mul(gs_quat q0, gs_quat q1) { return gs_quat_ctor( q0.w * q1.x + q1.w * q0.x + q0.y * q1.z - q1.y * q0.z, q0.w * q1.y + q1.w * q0.y + q0.z * q1.x - q1.z * q0.x, q0.w * q1.z + q1.w * q0.z + q0.x * q1.y - q1.x * q0.y, q0.w * q1.w - q0.x * q1.x - q0.y * q1.y - q0.z * q1.z ); } gs_inline gs_quat gs_quat_mul_list(u32 count, ...) { va_list ap; gs_quat q = gs_quat_default(); va_start(ap, count); for (u32 i = 0; i < count; ++i) { q = gs_quat_mul(q, va_arg(ap, gs_quat)); } va_end(ap); return q; } gs_inline gs_quat gs_quat_mul_quat(gs_quat q0, gs_quat q1) { return gs_quat_ctor( q0.w * q1.x + q1.w * q0.x + q0.y * q1.z - q1.y * q0.z, q0.w * q1.y + q1.w * q0.y + q0.z * q1.x - q1.z * q0.x, q0.w * q1.z + q1.w * q0.z + q0.x * q1.y - q1.x * q0.y, q0.w * q1.w - q0.x * q1.x - q0.y * q1.y - q0.z * q1.z ); } gs_inline gs_quat gs_quat_scale(gs_quat q, f32 s) { return gs_quat_ctor(q.x * s, q.y * s, q.z * s, q.w * s); } gs_inline f32 gs_quat_dot(gs_quat q0, gs_quat q1) { return (f32)(q0.x * q1.x + q0.y * q1.y + q0.z * q1.z + q0.w * q1.w); } gs_inline gs_quat gs_quat_conjugate(gs_quat q) { return (gs_quat_ctor(-q.x, -q.y, -q.z, q.w)); } gs_inline f32 gs_quat_len(gs_quat q) { return (f32)sqrt(gs_quat_dot(q, q)); } gs_inline gs_quat gs_quat_norm(gs_quat q) { return gs_quat_scale(q, 1.0f / gs_quat_len(q)); } gs_inline gs_quat gs_quat_cross(gs_quat q0, gs_quat q1) { return gs_quat_ctor ( q0.x * q1.x + q0.x * q1.w + q0.y * q1.z - q0.z * q1.y, q0.w * q1.y + q0.y * q1.w + q0.z * q1.x - q0.x * q1.z, q0.w * q1.z + q0.z * q1.w + q0.x * q1.y - q0.y * q1.x, q0.w * q1.w - q0.x * q1.x - q0.y * q1.y - q0.z * q1.z ); } // Inverse := Conjugate / Dot; gs_inline gs_quat gs_quat_inverse(gs_quat q) { return (gs_quat_scale(gs_quat_conjugate(q), 1.0f / gs_quat_dot(q, q))); } gs_inline gs_quat gs_quat_angle_axis(f32 rad, gs_vec3 axis) { // Normalize axis gs_vec3 a = gs_vec3_norm(axis); // Get scalar f32 half_angle = 0.5f * rad; f32 s = (float)sin(half_angle); return gs_quat_ctor(a.x * s, a.y * s, a.z * s, (float)cos(half_angle)); } gs_inline gs_vec3 gs_quat_rotate(gs_quat q, gs_vec3 v) { // nVidia SDK implementation gs_vec3 qvec = gs_vec3_ctor(q.x, q.y, q.z); gs_vec3 uv = gs_vec3_cross(qvec, v); gs_vec3 uuv = gs_vec3_cross(qvec, uv); uv = gs_vec3_scale(uv, 2.f * q.w); uuv = gs_vec3_scale(uuv, 2.f); return (gs_vec3_add(v, gs_vec3_add(uv, uuv))); } gs_inline gs_vec3 gs_quat_forward(gs_quat q) { return gs_quat_rotate(q, gs_v3(0.f, 0.f, -1.f)); } gs_inline gs_vec3 gs_quat_backward(gs_quat q) { return gs_quat_rotate(q, gs_v3(0.f, 0.f, 1.f)); } gs_inline gs_vec3 gs_quat_left(gs_quat q) { return gs_quat_rotate(q, gs_v3(-1.f, 0.f, 0.f)); } gs_inline gs_vec3 gs_quat_right(gs_quat q) { return gs_quat_rotate(q, gs_v3(1.f, 0.f, 0.f)); } gs_inline gs_vec3 gs_quat_up(gs_quat q) { return gs_quat_rotate(q, gs_v3(0.f, 1.f, 0.f)); } gs_inline gs_vec3 gs_quat_down(gs_quat q) { return gs_quat_rotate(q, gs_v3(0.f, -1.f, 0.f)); } gs_inline gs_quat gs_quat_from_to_rotation(gs_vec3 src, gs_vec3 dst) { src = gs_vec3_norm(src); dst = gs_vec3_norm(dst); const float d = gs_vec3_dot(src, dst); if (d >= 1.f) { return gs_quat_default(); } else if (d <= -1.f) { // Orthonormalize, find axis of rotation gs_vec3 axis = gs_vec3_cross(src, GS_XAXIS); if (gs_vec3_len2(axis) < 1e-6) { axis = gs_vec3_cross(src, GS_YAXIS); } return gs_quat_angle_axis((float)GS_PI, gs_vec3_norm(axis)); } else { const float s = sqrtf(gs_vec3_len2(src) * gs_vec3_len2(dst)) + gs_vec3_dot(src, dst); gs_vec3 axis = gs_vec3_cross(src, dst); return gs_quat_norm(gs_quat_ctor(axis.x, axis.y, axis.z, s)); } } gs_inline gs_quat gs_quat_look_rotation(gs_vec3 position, gs_vec3 target, gs_vec3 up) { const gs_vec3 forward = gs_vec3_norm(gs_vec3_sub(position, target)); const gs_quat q0 = gs_quat_from_to_rotation(GS_ZAXIS, forward); if (gs_vec3_len2(gs_vec3_cross(forward, up)) < 1e-6) { return q0; } const gs_vec3 new_up = gs_quat_rotate(q0, up); const gs_quat q1 = gs_quat_from_to_rotation(new_up, up); return gs_quat_mul(q1, q0); } gs_inline gs_quat gs_quat_slerp(gs_quat a, gs_quat b, f32 t) { f32 c = gs_quat_dot(a, b); gs_quat end = b; if (c < 0.0f) { // Reverse all signs c *= -1.0f; end.x *= -1.0f; end.y *= -1.0f; end.z *= -1.0f; end.w *= -1.0f; } // Calculate coefficients f32 sclp, sclq; if ((1.0f - c) > 0.0001f) { f32 omega = (float)acosf(c); f32 s = (float)sinf(omega); sclp = (float)sinf((1.0f - t) * omega) / s; sclq = (float)sinf(t * omega) / s; } else { sclp = 1.0f - t; sclq = t; } gs_quat q; q.x = sclp * a.x + sclq * end.x; q.y = sclp * a.y + sclq * end.y; q.z = sclp * a.z + sclq * end.z; q.w = sclp * a.w + sclq * end.w; return q; } #define quat_axis_angle(__AXS, __ANG)\ gs_quat_angle_axis(__ANG, __AXS) /* * @brief Convert given quaternion param into equivalent 4x4 rotation matrix * @note: From http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/index.htm */ gs_inline gs_mat4 gs_quat_to_mat4(gs_quat _q) { gs_mat4 mat = gs_mat4_identity(); gs_quat q = gs_quat_norm(_q); f32 xx = q.x * q.x; f32 yy = q.y * q.y; f32 zz = q.z * q.z; f32 xy = q.x * q.y; f32 xz = q.x * q.z; f32 yz = q.y * q.z; f32 wx = q.w * q.x; f32 wy = q.w * q.y; f32 wz = q.w * q.z; mat.elements[0 * 4 + 0] = 1.0f - 2.0f * (yy + zz); mat.elements[1 * 4 + 0] = 2.0f * (xy - wz); mat.elements[2 * 4 + 0] = 2.0f * (xz + wy); mat.elements[0 * 4 + 1] = 2.0f * (xy + wz); mat.elements[1 * 4 + 1] = 1.0f - 2.0f * (xx + zz); mat.elements[2 * 4 + 1] = 2.0f * (yz - wx); mat.elements[0 * 4 + 2] = 2.0f * (xz - wy); mat.elements[1 * 4 + 2] = 2.0f * (yz + wx); mat.elements[2 * 4 + 2] = 1.0f - 2.0f * (xx + yy); return mat; } gs_inline gs_quat gs_quat_from_euler(f32 yaw_deg, f32 pitch_deg, f32 roll_deg) { f32 yaw = gs_deg2rad(yaw_deg); f32 pitch = gs_deg2rad(pitch_deg); f32 roll = gs_deg2rad(roll_deg); gs_quat q; f32 cy = (float)cos(yaw * 0.5f); f32 sy = (float)sin(yaw * 0.5f); f32 cr = (float)cos(roll * 0.5f); f32 sr = (float)sin(roll * 0.5f); f32 cp = (float)cos(pitch * 0.5f); f32 sp = (float)sin(pitch * 0.5f); q.x = cy * sr * cp - sy * cr * sp; q.y = cy * cr * sp + sy * sr * cp; q.z = sy * cr * cp - cy * sr * sp; q.w = cy * cr * cp + sy * sr * sp; return q; } gs_inline float gs_quat_pitch(gs_quat* q) { return atan2(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); } gs_inline float gs_quat_yaw(gs_quat* q) { return asin(-2.0f * (q->x * q->z - q->w * q->y)); } gs_inline float gs_quat_roll(gs_quat* q) { return atan2(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); } gs_inline gs_vec3 gs_quat_to_euler(gs_quat* q) { return gs_v3(gs_quat_yaw(q), gs_quat_pitch(q), gs_quat_roll(q)); } /*================================================================================ // Transform (Non-Uniform Scalar VQS) ================================================================================*/ /* - This follows a traditional 'VQS' structure for complex object transformations, however it differs from the standard in that it allows for non-uniform scaling in the form of a vec3. */ // Source: https://www.eurosis.org/cms/files/conf/gameon-asia/gameon-asia2007/R-SESSION/G1.pdf typedef struct { union { gs_vec3 position; gs_vec3 translation; }; gs_quat rotation; gs_vec3 scale; } gs_vqs_t; typedef gs_vqs_t gs_vqs; gs_inline gs_vqs gs_vqs_ctor(gs_vec3 tns, gs_quat rot, gs_vec3 scl) { gs_vqs t; t.position = tns; t.rotation = rot; t.scale = scl; return t; } gs_inline gs_vqs gs_vqs_default() { gs_vqs t = gs_vqs_ctor ( gs_vec3_ctor(0.0f, 0.0f, 0.0f), gs_quat_ctor(0.0f, 0.0f, 0.0f, 1.0f), gs_vec3_ctor(1.0f, 1.0f, 1.0f) ); return t; } // AbsScale = ParentScale * LocalScale // AbsRot = LocalRot * ParentRot // AbsTrans = ParentPos + [ParentRot * (ParentScale * LocalPos)] gs_inline gs_vqs gs_vqs_absolute_transform(const gs_vqs* local, const gs_vqs* parent) { if (!local || !parent) { return gs_vqs_default(); } // Normalized rotations gs_quat p_rot_norm = gs_quat_norm(parent->rotation); gs_quat l_rot_norm = gs_quat_norm(local->rotation); // Scale gs_vec3 scl = gs_vec3_mul(local->scale, parent->scale); // Rotation gs_quat rot = gs_quat_norm(gs_quat_mul(p_rot_norm, l_rot_norm)); // position gs_vec3 tns = gs_vec3_add(parent->position, gs_quat_rotate(p_rot_norm, gs_vec3_mul(parent->scale, local->position))); return gs_vqs_ctor(tns, rot, scl); } // RelScale = AbsScale / ParentScale // RelRot = Inverse(ParentRot) * AbsRot // RelTrans = [Inverse(ParentRot) * (AbsPos - ParentPosition)] / ParentScale; gs_inline gs_vqs gs_vqs_relative_transform(const gs_vqs* absolute, const gs_vqs* parent) { if (!absolute || !parent) { return gs_vqs_default(); } // Get inverse rotation normalized gs_quat p_rot_inv = gs_quat_norm(gs_quat_inverse(parent->rotation)); // Normalized abs rotation gs_quat a_rot_norm = gs_quat_norm(absolute->rotation); // Scale gs_vec3 scl = gs_vec3_div(absolute->scale, parent->scale); // Rotation gs_quat rot = gs_quat_norm(gs_quat_mul(p_rot_inv, a_rot_norm)); // position gs_vec3 tns = gs_vec3_div(gs_quat_rotate(p_rot_inv, gs_vec3_sub(absolute->position, parent->position)), parent->scale); return gs_vqs_ctor(tns, rot, scl); } gs_inline gs_mat4 gs_vqs_to_mat4(const gs_vqs* transform) { gs_mat4 mat = gs_mat4_identity(); gs_mat4 trans = gs_mat4_translatev(transform->position); gs_mat4 rot = gs_quat_to_mat4(transform->rotation); gs_mat4 scl = gs_mat4_scalev(transform->scale); mat = gs_mat4_mul(mat, trans); mat = gs_mat4_mul(mat, rot); mat = gs_mat4_mul(mat, scl); return mat; } gs_inline gs_vqs gs_vqs_from_mat4(const gs_mat4* m) { gs_vec3 translation = gs_v3s(0.f), rotation = gs_v3s(0.f), scale = gs_v3s(1.f); gs_mat4_decompose(m, (float*)&translation, (float*)&rotation, (float*)&scale); return gs_vqs_ctor( translation, gs_quat_from_euler(rotation.x, rotation.y, rotation.z), scale ); } gs_inline gs_vec3 gs_vqs_forward(const gs_vqs* transform) { return (gs_quat_rotate(transform->rotation, gs_v3(0.0f, 0.0f, -1.0f))); } gs_inline gs_vec3 gs_vqs_backward(const gs_vqs* transform) { return (gs_quat_rotate(transform->rotation, gs_v3(0.0f, 0.0f, 1.0f))); } gs_inline gs_vec3 gs_vqs_left(const gs_vqs* transform) { return (gs_quat_rotate(transform->rotation, gs_v3(-1.0f, 0.0f, 0.0f))); } gs_inline gs_vec3 gs_vqs_right(const gs_vqs* transform) { return (gs_quat_rotate(transform->rotation, gs_v3(1.0f, 0.0f, 0.0f))); } gs_inline gs_vec3 gs_vqs_up(const gs_vqs* transform) { return (gs_quat_rotate(transform->rotation, gs_v3(0.0f, 1.0f, 0.0f))); } gs_inline gs_vec3 gs_vqs_down(const gs_vqs* transform) { return (gs_quat_rotate(transform->rotation, gs_v3(0.0f, -1.0f, 0.0f))); } /*================================================================================ // Random ================================================================================*/ // From: https://github.com/ESultanik/mtwister #define GS_STATE_VECTOR_LENGTH 624 #define GS_STATE_VECTOR_M 397 /* changes to STATE_VECTOR_LENGTH also require changes to this */ typedef struct gs_mt_rand_t { uint64_t mt[GS_STATE_VECTOR_LENGTH]; int32_t index; } gs_mt_rand_t; GS_API_DECL gs_mt_rand_t gs_rand_seed(uint64_t seed); GS_API_DECL int64_t gs_rand_gen_long(gs_mt_rand_t* rand); GS_API_DECL double gs_rand_gen(gs_mt_rand_t* rand); GS_API_DECL double gs_rand_gen_range(gs_mt_rand_t* rand, double min, double max); GS_API_DECL int64_t gs_rand_gen_range_long(gs_mt_rand_t* rand, int32_t min, int32_t max); GS_API_DECL gs_color_t gs_rand_gen_color(gs_mt_rand_t* rand); #ifndef GS_NO_SHORT_NAME typedef gs_mt_rand_t gs_rand; #endif /*================================================================================ // Coroutine (Light wrapper around Minicoro) ================================================================================*/ #define MCO_MALLOC gs_malloc #define MCO_FREE gs_free #include "external/minicoro/minicoro.h" typedef mco_coro gs_coro_t; typedef mco_desc gs_coro_desc_t; typedef mco_result gs_coro_result; // Result #define GS_CORO_SUCCESS MCO_SUCCESS #define GS_CORO_GENERIC_ERROR MCO_GENERIC_ERROR #define GS_CORO_INVALID_POINTER MCO_INVALID_POINTER #define GS_CORO_INVALID_COROUTINE MCO_INVALID_COROUTINE #define GS_CORO_NOT_SUSPENDED MCO_NOT_SUSPENDED #define GS_CORO_NOT_RUNNING MCO_NOT_RUNNING #define GS_CORO_MAKE_CONTEXT_ERROR MCO_MAKE_CONTEXT_ERROR #define GS_CORO_SWITCH_CONTEXT_ERROR MCO_SWITCH_CONTEXT_ERROR #define GS_CORO_NOT_ENOUGH_SPACE MCO_NOT_ENOUGH_SPACE #define GS_CORO_OUT_OF_MEMORY MCO_OUT_OF_MEMORY #define GS_CORO_INVALID_ARGUMENTS MCO_INVALID_ARGUMENTS #define GS_CORO_INVALID_OPERATION MCO_INVALID_OPERATION #define GS_CORO_STACK_OVERFLOW MCO_STACK_OVERFLOW // State #define GS_CORO_DEAD MCO_DEAD // The coroutine has finished normally or was uninitialized before finishing. #define GS_CORO_NORMAL MCO_NORMAL // The coroutine is active but not running (that is, it has resumed another coroutine). #define GS_CORO_RUNNING MCO_RUNNING // The coroutine is active and running #define GS_CORO_SUSPENDED MCO_SUSPENDED // The coroutine is suspended (in a call to yield, or it has not started running yet). // Functions #define gs_coro_desc_init(DESC, V) mco_desc_init((DESC), (V)) #define gs_coro_init(CO, DESC) mco_init((CO), (DESC)) #define gs_coro_uninit(CO) mco_uninit((CO)) #define gs_coro_create(CO, DESC) mco_create((CO), (DESC)) #define gs_coro_destroy(CO) mco_destroy((CO)) #define gs_coro_yield(CO) mco_yield((CO)) #define gs_coro_resume(CO) mco_resume((CO)) #define gs_coro_result_description(RES) mco_result_description((RES)) #define gs_coro_status(CO) mco_status((CO)) /*================================================================================ // Scheduler ================================================================================*/ #include "external/sched/sched.h" #ifdef GS_PLATFORM_WINDOWS #define gs_thread_local __declspec(thread) #else #define gs_thread_local __thread #endif #define GS_SCHED_DEFAULT SCHED_DEFAULT #define gs_sched_fp_t sched_run #define gs_sched_task_t struct sched_task #define gs_sched_task_partition_t struct sched_task_partition #define gs_scheduler_t struct scheduler #define gs_sched_profiling_t struct sched_profiling #define gs_scheduler_init scheduler_init #define gs_scheduler_start scheduler_start #define gs_scheduler_add scheduler_add #define gs_scheduler_join scheduler_join #define gs_scheduler_wait scheduler_wait #define gs_scheduler_stop scheduler_stop #define gs_sched_task_done sched_task_done #define gs_atomic_int_t int32_t GS_API_DECL uint32_t gs_atomic_cmp_swp(volatile uint32_t *dst, uint32_t swap, uint32_t cmp); GS_API_DECL int32_t gs_atomic_add(volatile int32_t *dst, int32_t value); /*================================================================================ // Noise ================================================================================*/ // Perlin noise GS_API_DECL float gs_perlin1(float x); GS_API_DECL float gs_perlin2(float x, float y); GS_API_DECL float gs_perlin3(float x, float y, float z); GS_API_DECL float gs_perlin4(float x, float y, float z, float w); // Perlin periodic noise GS_API_DECL float gs_perlin1p(float x, int32_t px); GS_API_DECL float gs_perlin2p(float x, float y, int32_t px, int32_t py); GS_API_DECL float gs_perlin3p(float x, float y, float z, int32_t px, int32_t py, int32_t pz); GS_API_DECL float gs_perlin4p(float x, float y, float z, float w, int32_t px, int32_t py, int32_t pz, int32_t pw); /*================================================================================ // Camera ================================================================================*/ typedef enum gs_projection_type { GS_PROJECTION_TYPE_ORTHOGRAPHIC, GS_PROJECTION_TYPE_PERSPECTIVE } gs_projection_type; typedef struct gs_camera_t { gs_vqs transform; float fov; float aspect_ratio; float near_plane; float far_plane; float ortho_scale; gs_projection_type proj_type; } gs_camera_t; GS_API_DECL gs_camera_t gs_camera_default(); GS_API_DECL gs_camera_t gs_camera_perspective(); GS_API_DECL gs_mat4 gs_camera_get_view(const gs_camera_t* cam); GS_API_DECL gs_mat4 gs_camera_get_proj(const gs_camera_t* cam, int32_t view_width, int32_t view_height); GS_API_DECL gs_mat4 gs_camera_get_view_projection(const gs_camera_t* cam, int32_t view_width, int32_t view_height); GS_API_DECL gs_vec3 gs_camera_forward(const gs_camera_t* cam); GS_API_DECL gs_vec3 gs_camera_backward(const gs_camera_t* cam); GS_API_DECL gs_vec3 gs_camera_up(const gs_camera_t* cam); GS_API_DECL gs_vec3 gs_camera_down(const gs_camera_t* cam); GS_API_DECL gs_vec3 gs_camera_right(const gs_camera_t* cam); GS_API_DECL gs_vec3 gs_camera_left(const gs_camera_t* cam); GS_API_DECL gs_vec3 gs_camera_screen_to_world(const gs_camera_t* cam, gs_vec3 coords, int32_t view_x, int32_t view_y, int32_t view_width, int32_t view_height); GS_API_DECL gs_vec3 gs_camera_world_to_screen(const gs_camera_t* cam, gs_vec3 coords, int32_t view_width, int32_t view_height); GS_API_DECL void gs_camera_offset_orientation(gs_camera_t* cam, float yaw, float picth); /*================================================================================ // Utils ================================================================================*/ // AABBs /* min is top left of rect, max is bottom right */ /* typedef struct gs_aabb_t { gs_vec2 min; gs_vec2 max; } gs_aabb_t; // Collision Resolution: Minimum Translation Vector gs_force_inline gs_vec2 gs_aabb_aabb_mtv(gs_aabb_t* a0, gs_aabb_t* a1) { gs_vec2 diff = gs_v2(a0->min.x - a1->min.x, a0->min.y - a1->min.y); f32 l, r, b, t; gs_vec2 mtv = gs_v2(0.f, 0.f); l = a1->min.x - a0->max.x; r = a1->max.x - a0->min.x; b = a1->min.y - a0->max.y; t = a1->max.y - a0->min.y; mtv.x = fabsf(l) > r ? r : l; mtv.y = fabsf(b) > t ? t : b; if ( fabsf(mtv.x) <= fabsf(mtv.y)) { mtv.y = 0.f; } else { mtv.x = 0.f; } return mtv; } // 2D AABB collision detection (rect. vs. rect.) gs_force_inline b32 gs_aabb_vs_aabb(gs_aabb_t* a, gs_aabb_t* b) { if (a->max.x > b->min.x && a->max.y > b->min.y && a->min.x < b->max.x && a->min.y < b->max.y) { return true; } return false; } gs_force_inline gs_vec4 gs_aabb_window_coords(gs_aabb_t* aabb, gs_camera_t* camera, gs_vec2 window_size) { // AABB of the player gs_vec4 bounds = gs_default_val(); gs_vec4 tl = gs_v4(aabb->min.x, aabb->min.y, 0.f, 1.f); gs_vec4 br = gs_v4(aabb->max.x, aabb->max.y, 0.f, 1.f); gs_mat4 view_mtx = gs_camera_get_view(camera); gs_mat4 proj_mtx = gs_camera_get_proj(camera, (int32_t)window_size.x, (int32_t)window_size.y); gs_mat4 vp = gs_mat4_mul(proj_mtx, view_mtx); // Transform verts tl = gs_mat4_mul_vec4(vp, tl); br = gs_mat4_mul_vec4(vp, br); // Perspective divide tl = gs_vec4_scale(tl, 1.f / tl.w); br = gs_vec4_scale(br, 1.f / br.w); // NDC [0.f, 1.f] and NDC tl.x = (tl.x * 0.5f + 0.5f); tl.y = (tl.y * 0.5f + 0.5f); br.x = (br.x * 0.5f + 0.5f); br.y = (br.y * 0.5f + 0.5f); // Window Space tl.x = tl.x * window_size.x; tl.y = gs_map_range(1.f, 0.f, 0.f, 1.f, tl.y) * window_size.y; br.x = br.x * window_size.x; br.y = gs_map_range(1.f, 0.f, 0.f, 1.f, br.y) * window_size.y; bounds = gs_v4(tl.x, tl.y, br.x, br.y); return bounds; } */ /** @} */ // end of gs_math /*======================== // GS_LEXER ========================*/ //==== [ Token ] ============================================================// typedef enum gs_token_type { GS_TOKEN_UNKNOWN = 0x00, GS_TOKEN_LPAREN, GS_TOKEN_RPAREN, GS_TOKEN_LTHAN, GS_TOKEN_GTHAN, GS_TOKEN_SEMICOLON, GS_TOKEN_COLON, GS_TOKEN_COMMA, GS_TOKEN_EQUAL, GS_TOKEN_EEQUAL, GS_TOKEN_NEQUAL, GS_TOKEN_LEQUAL, GS_TOKEN_GEQUAL, GS_TOKEN_NOT, GS_TOKEN_HASH, GS_TOKEN_PIPE, GS_TOKEN_AMPERSAND, GS_TOKEN_AND, GS_TOKEN_OR, GS_TOKEN_XOR, GS_TOKEN_NEGATE, GS_TOKEN_LBRACE, GS_TOKEN_RBRACE, GS_TOKEN_LBRACKET, GS_TOKEN_RBRACKET, GS_TOKEN_MINUS, GS_TOKEN_PLUS, GS_TOKEN_ASTERISK, GS_TOKEN_BSLASH, GS_TOKEN_FSLASH, GS_TOKEN_QMARK, GS_TOKEN_SPACE, GS_TOKEN_PERCENT, GS_TOKEN_DOLLAR, GS_TOKEN_NEWLINE, GS_TOKEN_TAB, GS_TOKEN_UNDERSCORE, GS_TOKEN_SINGLE_LINE_COMMENT, GS_TOKEN_MULTI_LINE_COMMENT, GS_TOKEN_IDENTIFIER, GS_TOKEN_SINGLE_QUOTE, GS_TOKEN_DOUBLE_QUOTE, GS_TOKEN_STRING, GS_TOKEN_PERIOD, GS_TOKEN_NUMBER, GS_TOKEN_KEYWORD, GS_TOKEN_COUNT // Max tokens accounted for } gs_token_type; typedef struct gs_token_t { const char* text; gs_token_type type; uint32_t len; uint32_t line; } gs_token_t; GS_API_DECL gs_token_t gs_token_invalid_token(); GS_API_DECL bool gs_token_compare_type(const gs_token_t* t, gs_token_type type); GS_API_DECL bool gs_token_compare_text(const gs_token_t* t, const char* match); GS_API_DECL void gs_token_print_text(const gs_token_t* t); GS_API_DECL void gs_token_debug_print(const gs_token_t* t); GS_API_DECL const char* gs_token_type_to_str(gs_token_type type); GS_API_DECL bool gs_char_is_end_of_line(char c); GS_API_DECL bool gs_char_is_white_space(char c); GS_API_DECL bool gs_char_is_alpha(char c); GS_API_DECL bool gs_char_is_numeric(char c); //==== [ Lexer ] ============================================================// typedef struct gs_lexer_t { const char* at; const char* contents; gs_token_t current_token; bool (* can_lex)(struct gs_lexer_t* lex); void (* eat_white_space)(struct gs_lexer_t* lex); gs_token_t (* next_token)(struct gs_lexer_t*); bool32 skip_white_space; size_t size; // Optional size_t contents_size; // Optional uint32_t line; // Line number } gs_lexer_t; GS_API_DECL void gs_lexer_set_contents(gs_lexer_t* lex, const char* contents); GS_API_DECL gs_token_t gs_lexer_next_token(gs_lexer_t* lex); GS_API_DECL bool gs_lexer_can_lex(gs_lexer_t* lex); GS_API_DECL gs_token_t gs_lexer_current_token(const gs_lexer_t* lex); GS_API_DECL bool gs_lexer_require_token_text(gs_lexer_t* lex, const char* match); GS_API_DECL bool gs_lexer_require_token_type(gs_lexer_t* lex, gs_token_type type); GS_API_DECL bool gs_lexer_current_token_compare_type(const gs_lexer_t* lex, gs_token_type type); GS_API_DECL gs_token_t gs_lexer_peek(gs_lexer_t* lex); GS_API_DECL bool gs_lexer_find_next_token_type(gs_lexer_t* lex, gs_token_type type); GS_API_DECL gs_token_t gs_lexer_advance_before_next_token_type(gs_lexer_t* lex, gs_token_type type); // C specific functions for lexing GS_API_DECL gs_lexer_t gs_lexer_c_ctor(const char* contents); GS_API_DECL bool gs_lexer_c_can_lex(gs_lexer_t* lex); GS_API_DECL void gs_lexer_c_eat_white_space(gs_lexer_t* lex); GS_API_DECL gs_token_t gs_lexer_c_next_token(gs_lexer_t* lex); /*======================== // GS_PLATFORM ========================*/ /** @defgroup gs_platform Platform * Gunslinger Platform * @{ */ /*============================================================ // Platform Time ============================================================*/ typedef struct gs_platform_time_t { float max_fps; float elapsed; float previous; float update; float render; float delta; float frame; } gs_platform_time_t; /*============================================================ // Platform UUID ============================================================*/ #define GS_UUID_STR_SIZE_CONSTANT 32 // 33 characters, all set to 0 #define gs_uuid_temp_str_buffer()\ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} typedef struct gs_uuid_t { uint8_t bytes[16]; } gs_uuid_t; /*============================================================ // Platform Window ============================================================*/ #define GS_WINDOW_FLAGS_NO_RESIZE 0x01 #define GS_WINDOW_FLAGS_FULLSCREEN 0x02 #define GS_WINDOW_FLAGS_INVISIBLE 0x03 // Should have an internal resource cache of window handles (controlled by the platform api) typedef struct gs_platform_window_desc_t { const char* title; uint32_t width; uint32_t height; uint32_t flags; uint32_t num_samples; // Multisamples (if 0, then disabled) uint32_t monitor_index; bool32 vsync; float frame_rate; } gs_platform_window_desc_t; typedef struct gs_platform_window_s { void* hndl; gs_vec2 framebuffer_size; gs_vec2 window_size; gs_vec2 window_position; } gs_platform_window_t; typedef enum gs_platform_cursor { GS_PLATFORM_CURSOR_ARROW, GS_PLATFORM_CURSOR_IBEAM, GS_PLATFORM_CURSOR_SIZE_NW_SE, GS_PLATFORM_CURSOR_SIZE_NE_SW, GS_PLATFORM_CURSOR_SIZE_NS, GS_PLATFORM_CURSOR_SIZE_WE, GS_PLATFORM_CURSOR_SIZE_ALL, GS_PLATFORM_CURSOR_HAND, GS_PLATFORM_CURSOR_NO, GS_PLATFORM_CURSOR_COUNT } gs_platform_cursor; typedef enum gs_platform_keycode { GS_KEYCODE_INVALID, GS_KEYCODE_SPACE, GS_KEYCODE_APOSTROPHE, /* ' */ GS_KEYCODE_COMMA, /* , */ GS_KEYCODE_MINUS, /* - */ GS_KEYCODE_PERIOD, /* . */ GS_KEYCODE_SLASH, /* / */ GS_KEYCODE_0, GS_KEYCODE_1, GS_KEYCODE_2, GS_KEYCODE_3, GS_KEYCODE_4, GS_KEYCODE_5, GS_KEYCODE_6, GS_KEYCODE_7, GS_KEYCODE_8, GS_KEYCODE_9, GS_KEYCODE_SEMICOLON, /* ; */ GS_KEYCODE_EQUAL, /* = */ GS_KEYCODE_A, GS_KEYCODE_B, GS_KEYCODE_C, GS_KEYCODE_D, GS_KEYCODE_E, GS_KEYCODE_F, GS_KEYCODE_G, GS_KEYCODE_H, GS_KEYCODE_I, GS_KEYCODE_J, GS_KEYCODE_K, GS_KEYCODE_L, GS_KEYCODE_M, GS_KEYCODE_N, GS_KEYCODE_O, GS_KEYCODE_P, GS_KEYCODE_Q, GS_KEYCODE_R, GS_KEYCODE_S, GS_KEYCODE_T, GS_KEYCODE_U, GS_KEYCODE_V, GS_KEYCODE_W, GS_KEYCODE_X, GS_KEYCODE_Y, GS_KEYCODE_Z, GS_KEYCODE_LEFT_BRACKET, /* [ */ GS_KEYCODE_BACKSLASH, /* \ */ GS_KEYCODE_RIGHT_BRACKET, /* ] */ GS_KEYCODE_GRAVE_ACCENT, /* ` */ GS_KEYCODE_WORLD_1, /* non-US #1 */ GS_KEYCODE_WORLD_2, /* non-US #2 */ GS_KEYCODE_ESC, GS_KEYCODE_ENTER, GS_KEYCODE_TAB, GS_KEYCODE_BACKSPACE, GS_KEYCODE_INSERT, GS_KEYCODE_DELETE, GS_KEYCODE_RIGHT, GS_KEYCODE_LEFT, GS_KEYCODE_DOWN, GS_KEYCODE_UP, GS_KEYCODE_PAGE_UP, GS_KEYCODE_PAGE_DOWN, GS_KEYCODE_HOME, GS_KEYCODE_END, GS_KEYCODE_CAPS_LOCK, GS_KEYCODE_SCROLL_LOCK, GS_KEYCODE_NUM_LOCK, GS_KEYCODE_PRINT_SCREEN, GS_KEYCODE_PAUSE, GS_KEYCODE_F1, GS_KEYCODE_F2, GS_KEYCODE_F3, GS_KEYCODE_F4, GS_KEYCODE_F5, GS_KEYCODE_F6, GS_KEYCODE_F7, GS_KEYCODE_F8, GS_KEYCODE_F9, GS_KEYCODE_F10, GS_KEYCODE_F11, GS_KEYCODE_F12, GS_KEYCODE_F13, GS_KEYCODE_F14, GS_KEYCODE_F15, GS_KEYCODE_F16, GS_KEYCODE_F17, GS_KEYCODE_F18, GS_KEYCODE_F19, GS_KEYCODE_F20, GS_KEYCODE_F21, GS_KEYCODE_F22, GS_KEYCODE_F23, GS_KEYCODE_F24, GS_KEYCODE_F25, GS_KEYCODE_KP_0, GS_KEYCODE_KP_1, GS_KEYCODE_KP_2, GS_KEYCODE_KP_3, GS_KEYCODE_KP_4, GS_KEYCODE_KP_5, GS_KEYCODE_KP_6, GS_KEYCODE_KP_7, GS_KEYCODE_KP_8, GS_KEYCODE_KP_9, GS_KEYCODE_KP_DECIMAL, GS_KEYCODE_KP_DIVIDE, GS_KEYCODE_KP_MULTIPLY, GS_KEYCODE_KP_SUBTRACT, GS_KEYCODE_KP_ADD, GS_KEYCODE_KP_ENTER, GS_KEYCODE_KP_EQUAL, GS_KEYCODE_LEFT_SHIFT, GS_KEYCODE_LEFT_CONTROL, GS_KEYCODE_LEFT_ALT, GS_KEYCODE_LEFT_SUPER, GS_KEYCODE_RIGHT_SHIFT, GS_KEYCODE_RIGHT_CONTROL, GS_KEYCODE_RIGHT_ALT, GS_KEYCODE_RIGHT_SUPER, GS_KEYCODE_MENU, GS_KEYCODE_COUNT } gs_platform_keycode; #define GS_KEYCODE_LCTRL GS_KEYCODE_LEFT_CONTROL #define GS_KEYCODE_RCTRL GS_KEYCODE_RIGHT_CONTROL #define GS_KEYCODE_LSHIFT GS_KEYCODE_LEFT_SHIFT #define GS_KEYCODE_RSHIFT GS_KEYCODE_RIGHT_SHIFT #define GS_KEYCODE_LALT GS_KEYCODE_LEFT_ALT #define GS_KEYCODE_RALT GS_KEYCODE_RIGHT_ALT typedef enum gs_platform_mouse_button_code { GS_MOUSE_LBUTTON, GS_MOUSE_RBUTTON, GS_MOUSE_MBUTTON, GS_MOUSE_BUTTON_CODE_COUNT } gs_platform_mouse_button_code; typedef struct gs_platform_mouse_t { b32 button_map[GS_MOUSE_BUTTON_CODE_COUNT]; b32 prev_button_map[GS_MOUSE_BUTTON_CODE_COUNT]; gs_vec2 position; gs_vec2 delta; gs_vec2 wheel; b32 moved_this_frame; b32 locked; } gs_platform_mouse_t; enum { GS_PLATFORM_GAMEPAD_BUTTON_A = 0x00, GS_PLATFORM_GAMEPAD_BUTTON_B, GS_PLATFORM_GAMEPAD_BUTTON_X, GS_PLATFORM_GAMEPAD_BUTTON_Y, GS_PLATFORM_GAMEPAD_BUTTON_LBUMPER, GS_PLATFORM_GAMEPAD_BUTTON_RBUMPER, GS_PLATFORM_GAMEPAD_BUTTON_BACK, GS_PLATFORM_GAMEPAD_BUTTON_START, GS_PLATFORM_GAMEPAD_BUTTON_GUIDE, GS_PLATFORM_GAMEPAD_BUTTON_LTHUMB, GS_PLATFORM_GAMEPAD_BUTTON_RTHUMB, GS_PLATFORM_GAMEPAD_BUTTON_DPUP, GS_PLATFORM_GAMEPAD_BUTTON_DPRIGHT, GS_PLATFORM_GAMEPAD_BUTTON_DPDOWN, GS_PLATFORM_GAMEPAD_BUTTON_DPLEFT, GS_PLATFORM_GAMEPAD_BUTTON_COUNT }; #define GS_PLATFORM_GAMEPAD_BUTTON_LAST GS_PLATFORM_GAMEPAD_BUTTON_DPLEFT #define GS_PLATFORM_GAMEPAD_BUTTON_CROSS GS_PLATFORM_GAMEPAD_BUTTON_A #define GS_PLATFORM_GAMEPAD_BUTTON_CIRCLE GS_PLATFORM_GAMEPAD_BUTTON_B #define GS_PLATFORM_GAMEPAD_BUTTON_SQUARE GS_PLATFORM_GAMEPAD_BUTTON_X #define GS_PLATFORM_GAMEPAD_BUTTON_TRIANGLE GS_PLATFORM_GAMEPAD_BUTTON_Y enum { GS_PLATFORM_JOYSTICK_AXIS_LEFT_X = 0x00, GS_PLATFORM_JOYSTICK_AXIS_LEFT_Y, GS_PLATFORM_JOYSTICK_AXIS_RIGHT_X, GS_PLATFORM_JOYSTICK_AXIS_RIGHT_Y, GS_PLATFORM_JOYSTICK_AXIS_LTRIGGER, GS_PLATFORM_JOYSTICK_AXIS_RTRIGGER, GS_PLATFORM_JOYSTICK_AXIS_COUNT }; #define GS_PLATFORM_GAMEPAD_MAX 16 typedef struct gs_platform_gamepad_t { int16_t buttons[GS_PLATFORM_GAMEPAD_BUTTON_COUNT]; float axes[GS_PLATFORM_JOYSTICK_AXIS_COUNT]; int16_t present; } gs_platform_gamepad_t; #define GS_PLATFORM_MAX_TOUCH 5 typedef struct gs_platform_touchpoint_t { size_t id; gs_vec2 position; gs_vec2 delta; uint16_t changed; uint16_t pressed; uint16_t down; } gs_platform_touchpoint_t; typedef struct gs_platform_touch_t { gs_platform_touchpoint_t points[GS_PLATFORM_MAX_TOUCH]; uint16_t size; // Current number of touches active } gs_platform_touch_t; typedef struct gs_platform_input_t { b32 key_map[GS_KEYCODE_COUNT]; b32 prev_key_map[GS_KEYCODE_COUNT]; gs_platform_mouse_t mouse; gs_platform_touch_t touch; gs_platform_gamepad_t gamepads[GS_PLATFORM_GAMEPAD_MAX]; } gs_platform_input_t; // Enumeration of all platform types typedef enum gs_platform_type { GS_PLATFORM_TYPE_UNKNOWN = 0, GS_PLATFORM_TYPE_WINDOWS, GS_PLATFORM_TYPE_LINUX, GS_PLATFORM_TYPE_MAC, GS_PLATFORM_TYPE_ANDROID, GS_PLATFORM_TYPE_IOS, GS_PLATFORM_TYPE_HTML5, GS_PLATFORM_TYPE_RPI } gs_platform_type; typedef enum gs_platform_video_driver_type { GS_PLATFORM_VIDEO_DRIVER_TYPE_NONE = 0, GS_PLATFORM_VIDEO_DRIVER_TYPE_OPENGL, GS_PLATFORM_VIDEO_DRIVER_TYPE_OPENGLES, GS_PLATFORM_VIDEO_DRIVER_TYPE_DIRECTX, GS_PLATFORM_VIDEO_DRIVER_TYPE_VULKAN, GS_PLATFORM_VIDEO_DRIVER_TYPE_METAL, GS_PLATFORM_VIDEO_DRIVER_TYPE_SOFTWARE } gs_platform_video_driver_type; typedef enum gs_opengl_compatibility_flags { GS_OPENGL_COMPATIBILITY_FLAGS_LEGACY = 0, GS_OPENGL_COMPATIBILITY_FLAGS_CORE = 1 << 1, GS_OPENGL_COMPATIBILITY_FLAGS_COMPATIBILITY = 1 << 2, GS_OPENGL_COMPATIBILITY_FLAGS_FORWARD = 1 << 3, GS_OPENGL_COMPATIBILITY_FLAGS_ES = 1 << 4, } gs_opengl_compatibility_flags; // A structure that contains OpenGL video settings typedef struct gs_opengl_video_settings_t { gs_opengl_compatibility_flags compability_flags; uint32_t major_version; uint32_t minor_version; uint8_t multi_sampling_count; void* ctx; } gs_opengl_video_settings_t; typedef union gs_graphics_api_settings_t { gs_opengl_video_settings_t opengl; bool32 debug; } gs_graphics_api_settings_t; typedef struct gs_platform_video_settings_t { gs_graphics_api_settings_t graphics; gs_platform_video_driver_type driver; b32 vsync_enabled; } gs_platform_video_settings_t; typedef struct gs_platform_settings_t { gs_platform_video_settings_t video; } gs_platform_settings_t; typedef enum gs_platform_event_type { GS_PLATFORM_EVENT_MOUSE, GS_PLATFORM_EVENT_KEY, GS_PLATFORM_EVENT_TEXT, GS_PLATFORM_EVENT_WINDOW, GS_PLATFORM_EVENT_TOUCH, GS_PLATFORM_EVENT_APP } gs_platform_event_type; typedef enum gs_platform_key_modifier_type { GS_PLATFORM_KEY_MODIFIER_NONE = 0x00, GS_PLATFORM_KEY_MODIFIER_SHIFT = 0x01, GS_PLATFORM_KEY_MODIFIER_CONTROL = 0x02, GS_PLATFORM_KEY_MODIFIER_ALT = 0x04 } gs_platform_key_modifier_type; typedef enum gs_platform_key_action_type { GS_PLATFORM_KEY_PRESSED, GS_PLATFORM_KEY_DOWN, GS_PLATFORM_KEY_RELEASED } gs_platform_key_action_type; typedef struct gs_platform_key_event_t { int32_t codepoint; gs_platform_keycode keycode; gs_platform_key_action_type action; gs_platform_key_modifier_type modifier; } gs_platform_key_event_t; typedef enum gs_platform_mousebutton_action_type { GS_PLATFORM_MOUSE_BUTTON_PRESSED, GS_PLATFORM_MOUSE_BUTTON_DOWN, GS_PLATFORM_MOUSE_BUTTON_RELEASED, GS_PLATFORM_MOUSE_MOVE, GS_PLATFORM_MOUSE_ENTER, GS_PLATFORM_MOUSE_LEAVE, GS_PLATFORM_MOUSE_WHEEL } gs_platform_mousebutton_action_type; typedef struct gs_platform_mouse_event_t { int32_t codepoint; union { gs_platform_mouse_button_code button; gs_vec2 wheel; gs_vec2 move; }; gs_platform_mousebutton_action_type action; } gs_platform_mouse_event_t; typedef enum gs_platform_window_action_type { GS_PLATFORM_WINDOW_RESIZE, GS_PLATFORM_WINDOW_LOSE_FOCUS, GS_PLATFORM_WINDOW_GAIN_FOCUS, GS_PLATFORM_WINDOW_CREATE, GS_PLATFORM_WINDOW_DESTROY } gs_platform_window_action_type; typedef struct gs_platform_window_event_t { uint32_t hndl; gs_platform_window_action_type action; } gs_platform_window_event_t; typedef struct gs_platform_text_event_t { uint32_t codepoint; } gs_platform_text_event_t; typedef enum gs_platform_touch_action_type { GS_PLATFORM_TOUCH_DOWN, GS_PLATFORM_TOUCH_UP, GS_PLATFORM_TOUCH_MOVE, GS_PLATFORM_TOUCH_CANCEL } gs_platform_touch_action_type; typedef struct gs_platform_point_event_data_t { uintptr_t id; gs_vec2 position; uint16_t changed; } gs_platform_point_event_data_t; typedef struct gs_platform_touch_event_t { gs_platform_touch_action_type action; gs_platform_point_event_data_t point; } gs_platform_touch_event_t; typedef enum gs_platform_app_action_type { GS_PLATFORM_APP_START, GS_PLATFORM_APP_PAUSE, GS_PLATFORM_APP_RESUME, GS_PLATFORM_APP_STOP } gs_platform_app_action_type; typedef struct gs_platform_app_event_t { gs_platform_app_action_type action; } gs_platform_app_event_t; // Platform events typedef struct gs_platform_event_t { gs_platform_event_type type; union { gs_platform_key_event_t key; gs_platform_mouse_event_t mouse; gs_platform_window_event_t window; gs_platform_touch_event_t touch; gs_platform_app_event_t app; gs_platform_text_event_t text; }; uint32_t idx; } gs_platform_event_t; // Necessary function pointer typedefs typedef void (* gs_dropped_files_callback_t)(void*, int32_t count, const char** file_paths); typedef void (* gs_window_close_callback_t)(void*); typedef void (* gs_character_callback_t)(void*, uint32_t code_point); typedef void (* gs_framebuffer_resize_callback_t)(void*, int32_t width, int32_t height); /*=============================================================================================== // Platform Interface ===============================================================================================*/ struct gs_platform_interface_s; typedef struct gs_platform_t { // Settings for platform, including video gs_platform_settings_t settings; // Time gs_platform_time_t time; // Input gs_platform_input_t input; // Window data and handles gs_slot_array(gs_platform_window_t) windows; // Events that user can poll gs_dyn_array(gs_platform_event_t) events; // Cursors void* cursors[GS_PLATFORM_CURSOR_COUNT]; // Specific user data (for custom implementations) void* user_data; // Optional api for stable access across .dll boundaries struct gs_platform_interface_s* api; } gs_platform_t; /*=============================================================================================== // Platform API ===============================================================================================*/ /* == Platform Default API == */ // Platform Create / Destroy GS_API_DECL gs_platform_t* gs_platform_create(); GS_API_DECL void gs_platform_destroy(gs_platform_t* platform); // Platform Init / Update / Shutdown GS_API_DECL void gs_platform_update(gs_platform_t* platform); // Update platform layer // Platform Util GS_API_DECL const gs_platform_time_t* gs_platform_time(); GS_API_DECL float gs_platform_delta_time(); GS_API_DECL float gs_platform_frame_time(); // Platform UUID GS_API_DECL gs_uuid_t gs_platform_uuid_generate(); GS_API_DECL void gs_platform_uuid_to_string(char* temp_buffer, const gs_uuid_t* uuid); // Expects a temp buffer with at least 32 bytes GS_API_DECL uint32_t gs_platform_uuid_hash(const gs_uuid_t* uuid); // Platform Input GS_API_DECL gs_platform_input_t* gs_platform_input(); GS_API_DECL void gs_platform_update_input(gs_platform_input_t* input); GS_API_DECL void gs_platform_press_key(gs_platform_keycode code); GS_API_DECL void gs_platform_release_key(gs_platform_keycode code); GS_API_DECL bool gs_platform_was_key_down(gs_platform_keycode code); GS_API_DECL void gs_platform_press_mouse_button(gs_platform_mouse_button_code code); GS_API_DECL void gs_platform_release_mouse_button(gs_platform_mouse_button_code code); GS_API_DECL bool gs_platform_was_mouse_down(gs_platform_mouse_button_code code); GS_API_DECL void gs_platform_press_touch(uint32_t idx); GS_API_DECL void gs_platform_release_touch(uint32_t idx); GS_API_DECL bool gs_platform_was_touch_down(uint32_t idx); GS_API_DECL bool gs_platform_key_pressed(gs_platform_keycode code); GS_API_DECL bool gs_platform_key_down(gs_platform_keycode code); GS_API_DECL bool gs_platform_key_released(gs_platform_keycode code); GS_API_DECL bool gs_platform_touch_pressed(uint32_t idx); GS_API_DECL bool gs_platform_touch_down(uint32_t idx); GS_API_DECL bool gs_platform_touch_released(uint32_t idx); GS_API_DECL bool gs_platform_mouse_pressed(gs_platform_mouse_button_code code); GS_API_DECL bool gs_platform_mouse_down(gs_platform_mouse_button_code code); GS_API_DECL bool gs_platform_mouse_released(gs_platform_mouse_button_code code); GS_API_DECL gs_vec2 gs_platform_mouse_deltav(); GS_API_DECL void gs_platform_mouse_delta(float* x, float* y); GS_API_DECL gs_vec2 gs_platform_mouse_positionv(); GS_API_DECL void gs_platform_mouse_position(int32_t* x, int32_t* y); GS_API_DECL void gs_platform_mouse_wheel(float* x, float* y); GS_API_DECL gs_vec2 gs_platform_mouse_wheelv(); GS_API_DECL bool gs_platform_mouse_moved(); GS_API_DECL bool gs_platform_mouse_locked(); GS_API_DECL gs_vec2 gs_platform_touch_deltav(uint32_t idx); GS_API_DECL void gs_platform_touch_delta(uint32_t idx, float* x, float* y); GS_API_DECL gs_vec2 gs_platform_touch_positionv(uint32_t idx); GS_API_DECL void gs_platform_touch_position(uint32_t idx, float* x, float* y); // Platform Events GS_API_DECL bool gs_platform_poll_events(gs_platform_event_t* evt, bool32_t consume); GS_API_DECL void gs_platform_add_event(gs_platform_event_t* evt); // Platform Window GS_API_DECL uint32_t gs_platform_window_create(const gs_platform_window_desc_t* desc); GS_API_DECL uint32_t gs_platform_main_window(); GS_API_DECL void gs_platform_window_make_current(uint32_t hndl); // Binds context (main thread only) GS_API_DECL void gs_platform_window_make_current_raw(void* win); // Binds context (can be on separate thread with raw handle) typedef struct gs_platform_file_stats_s { uint64_t modified_time; uint64_t creation_time; uint64_t access_time; } gs_platform_file_stats_t; // Platform File IO (this all needs to be made available for impl rewrites) GS_API_DECL char* gs_platform_read_file_contents_default_impl(const char* file_path, const char* mode, size_t* sz); GS_API_DECL gs_result gs_platform_write_file_contents_default_impl(const char* file_path, const char* mode, void* data, size_t data_size); GS_API_DECL bool gs_platform_file_exists_default_impl(const char* file_path); GS_API_DECL bool gs_platform_dir_exists_default_impl(const char* dir_path); GS_API_DECL int32_t gs_platform_mkdir_default_impl(const char* dir_path, int32_t opt); GS_API_DECL int32_t gs_platform_file_size_in_bytes_default_impl(const char* file_path); GS_API_DECL void gs_platform_file_extension_default_impl(char* buffer, size_t buffer_sz, const char* file_path); GS_API_DECL int32_t gs_platform_file_delete_default_impl(const char* file_path); GS_API_DECL int32_t gs_platform_file_copy_default_impl(const char* src_path, const char* dst_path); GS_API_DECL int32_t gs_platform_file_compare_time(uint64_t time_a, uint64_t time_b); GS_API_DECL gs_platform_file_stats_t gs_platform_file_stats(const char* file_path); GS_API_DECL void* gs_platform_library_load_default_impl(const char* lib_path); GS_API_DECL void gs_platform_library_unload_default_impl(void* lib); GS_API_DECL void* gs_platform_library_proc_address_default_impl(void* lib, const char* func); // Default file implementations #ifndef gs_platform_read_file_contents #define gs_platform_read_file_contents gs_platform_read_file_contents_default_impl #endif #ifndef gs_platform_write_file_contents #define gs_platform_write_file_contents gs_platform_write_file_contents_default_impl #endif #ifndef gs_platform_file_exists #define gs_platform_file_exists gs_platform_file_exists_default_impl #endif #ifndef gs_platform_dir_exists #define gs_platform_dir_exists gs_platform_dir_exists_default_impl #endif #ifndef gs_platform_mkdir #define gs_platform_mkdir gs_platform_mkdir_default_impl #endif #ifndef gs_platform_file_exists #define gs_platform_file_exists gs_platform_file_exists_default_impl #endif #ifndef gs_platform_file_size_in_bytes #define gs_platform_file_size_in_bytes gs_platform_file_size_in_bytes_default_impl #endif #ifndef gs_platform_file_extension #define gs_platform_file_extension gs_platform_file_extension_default_impl #endif #ifndef gs_platform_file_delete #define gs_platform_file_delete gs_platform_file_delete_default_impl #endif #ifndef gs_platform_file_copy #define gs_platform_file_copy gs_platform_file_copy_default_impl #endif #ifndef gs_platform_library_load #define gs_platform_library_load gs_platform_library_load_default_impl #endif #ifndef gs_platform_library_unload #define gs_platform_library_unload gs_platform_library_unload_default_impl #endif #ifndef gs_platform_library_proc_addresss #define gs_platform_library_proc_address gs_platform_library_proc_address_default_impl #endif /* == Platform Dependent API == */ GS_API_DECL void gs_platform_init(gs_platform_t* platform); // Initialize platform layer GS_API_DECL void gs_platform_shutdown(gs_platform_t* platform); // Shutdown platform layer GS_API_DECL void gs_platform_update_internal(gs_platform_t* platform); // Platform Util GS_API_DECL double gs_platform_elapsed_time(); // Returns time in ms since initialization of platform GS_API_DECL void gs_platform_sleep(float ms); // Sleeps platform for time in ms // Platform Video GS_API_DECL void gs_platform_enable_vsync(int32_t enabled); // Platform Input GS_API_DECL void gs_platform_process_input(gs_platform_input_t* input); GS_API_DECL uint32_t gs_platform_key_to_codepoint(gs_platform_keycode code); GS_API_DECL gs_platform_keycode gs_platform_codepoint_to_key(uint32_t code); GS_API_DECL void gs_platform_mouse_set_position(uint32_t handle, float x, float y); GS_API_DECL void gs_platform_lock_mouse(uint32_t handle, bool32_t lock); GS_API_DECL gs_platform_window_t gs_platform_window_create_internal(const gs_platform_window_desc_t* desc); GS_API_DECL void gs_platform_window_swap_buffer(uint32_t handle); GS_API_DECL gs_vec2 gs_platform_window_sizev(uint32_t handle); GS_API_DECL void gs_platform_window_size(uint32_t handle, uint32_t* width, uint32_t* height); GS_API_DECL uint32_t gs_platform_window_width(uint32_t handle); GS_API_DECL uint32_t gs_platform_window_height(uint32_t handle); GS_API_DECL bool32_t gs_platform_window_fullscreen(uint32_t handle); GS_API_DECL gs_vec2 gs_platform_window_positionv(uint32_t handle); GS_API_DECL void gs_platform_window_position(uint32_t handle, uint32_t* x, uint32_t* y); GS_API_DECL void gs_platform_set_window_size(uint32_t handle, uint32_t width, uint32_t height); GS_API_DECL void gs_platform_set_window_sizev(uint32_t handle, gs_vec2 v); GS_API_DECL void gs_platform_set_window_fullscreen(uint32_t handle, bool32_t fullscreen); GS_API_DECL void gs_platform_set_window_position(uint32_t handle, uint32_t x, uint32_t y); GS_API_DECL void gs_platform_set_window_positionv(uint32_t handle, gs_vec2 v); GS_API_DECL void gs_platform_set_cursor(uint32_t handle, gs_platform_cursor cursor); GS_API_DECL void* gs_platform_raw_window_handle(uint32_t handle); GS_API_DECL gs_vec2 gs_platform_framebuffer_sizev(uint32_t handle); GS_API_DECL void gs_platform_framebuffer_size(uint32_t handle, uint32_t* w, uint32_t* h); GS_API_DECL uint32_t gs_platform_framebuffer_width(uint32_t handle); GS_API_DECL uint32_t gs_platform_framebuffer_height(uint32_t handle); GS_API_DECL gs_vec2 gs_platform_monitor_sizev(uint32_t id); GS_API_DECL void gs_platform_window_set_clipboard(uint32_t handle, const char* str); GS_API_DECL const char* gs_platform_window_get_clipboard(uint32_t handle); // Platform callbacks GS_API_DECL void gs_platform_set_framebuffer_resize_callback(uint32_t handle, gs_framebuffer_resize_callback_t cb); GS_API_DECL void gs_platform_set_dropped_files_callback(uint32_t handle, gs_dropped_files_callback_t cb); GS_API_DECL void gs_platform_set_window_close_callback(uint32_t handle, gs_window_close_callback_t cb); GS_API_DECL void gs_platform_set_character_callback(uint32_t handle, gs_character_callback_t cb); /* typedef struct gs_platform_interface_s { } gs_platform_interface_t; */ /** @} */ // end of gs_platform /*============================= // GS_AUDIO =============================*/ /** @defgroup gs_audio Audio * Gunslinger Audio * @{ */ typedef enum gs_audio_file_type { GS_OGG = 0x00, GS_WAV, GS_MP3 } gs_audio_file_type; /*================== // Audio Source ==================*/ typedef struct gs_audio_source_t { int32_t channels; int32_t sample_rate; void* samples; int32_t sample_count; } gs_audio_source_t; gs_handle_decl(gs_audio_source_t); typedef struct gs_audio_instance_decl_t { gs_handle(gs_audio_source_t) src; float volume; float pitch; bool32_t loop; bool32_t persistent; bool32_t playing; double sample_position; void* user_data; } gs_audio_instance_decl_t; typedef gs_audio_instance_decl_t gs_audio_instance_t; gs_handle_decl(gs_audio_instance_t); typedef void (* gs_audio_commit)(int16_t* output, uint32_t num_channels, uint32_t sample_rate, uint32_t frame_count); /*============================= // Audio Interface =============================*/ typedef struct gs_audio_t { // Audio source data cache gs_slot_array(gs_audio_source_t) sources; // Audio instance data cache gs_slot_array(gs_audio_instance_t) instances; // Max global volume setting float max_audio_volume; // Min global volume setting float min_audio_volume; // Samples to actually write to hardware void* sample_out; // Custom user commit function gs_audio_commit commit; // User data for custom impl void* user_data; } gs_audio_t; /*============================= // Audio API =============================*/ /* Audio Create, Destroy, Init, Shutdown, Submit */ GS_API_DECL gs_audio_t* gs_audio_create(); GS_API_DECL void gs_audio_destroy(gs_audio_t* audio); GS_API_DECL gs_result gs_audio_init(gs_audio_t* audio); GS_API_DECL gs_result gs_audio_shutdown(gs_audio_t* audio); // Register commit function GS_API_DECL void gs_audio_register_commit(gs_audio_commit commit); /* Audio create source */ GS_API_DECL gs_handle(gs_audio_source_t) gs_audio_load_from_file(const char* file_path); /* Audio create instance */ GS_API_DECL gs_handle(gs_audio_instance_t) gs_audio_instance_create(gs_audio_instance_decl_t* decl); /* Locking audio thread (optional) */ GS_API_DECL void gs_audio_mutex_lock(gs_audio_t* audio); GS_API_DECL void gs_audio_mutex_unlock(gs_audio_t* audio); /* Audio play instance */ GS_API_DECL void gs_audio_play_source_with_pitch(gs_handle(gs_audio_source_t) src, float volume, float pitch); GS_API_DECL void gs_audio_play_source(gs_handle(gs_audio_source_t) src, float volume); GS_API_DECL void gs_audio_play(gs_handle(gs_audio_instance_t) inst); GS_API_DECL void gs_audio_pause(gs_handle(gs_audio_instance_t) inst); GS_API_DECL void gs_audio_stop(gs_handle(gs_audio_instance_t) inst); GS_API_DECL void gs_audio_restart(gs_handle(gs_audio_instance_t) inst); GS_API_DECL bool32_t gs_audio_is_playing(gs_handle(gs_audio_instance_t) inst); /* Audio instance data */ GS_API_DECL void gs_audio_set_instance_data(gs_handle(gs_audio_instance_t) inst, gs_audio_instance_decl_t decl); GS_API_DECL gs_audio_instance_decl_t gs_audio_get_instance_data(gs_handle(gs_audio_instance_t) inst); GS_API_DECL float gs_audio_get_volume(gs_handle(gs_audio_instance_t) inst); GS_API_DECL void gs_audio_set_volume(gs_handle(gs_audio_instance_t) inst, float volume); /* Audio source data */ GS_API_DECL gs_audio_source_t* gs_audio_get_source_data(gs_handle(gs_audio_source_t) src); GS_API_DECL void gs_audio_get_runtime(gs_handle(gs_audio_source_t) src, int32_t* minutes, int32_t* seconds); GS_API_DECL void gs_audio_convert_to_runtime(int32_t sample_count, int32_t sample_rate, int32_t num_channels, int32_t position, int32_t* minutes_out, int32_t* seconds_out); GS_API_DECL int32_t gs_audio_get_sample_count(gs_handle(gs_audio_source_t) src); GS_API_DECL int32_t gs_audio_get_sample_rate(gs_handle(gs_audio_source_t) src); GS_API_DECL int32_t gs_audio_get_num_channels(gs_handle(gs_audio_source_t) src); /* Resource Loading */ GS_API_DECL bool32_t gs_audio_load_ogg_data_from_file(const char* file_path, int32_t* sample_count, int32_t* channels, int32_t* sample_rate, void** samples); GS_API_DECL bool32_t gs_audio_load_wav_data_from_file(const char* file_path, int32_t* sample_count, int32_t* channels, int32_t* sample_rate, void** samples); GS_API_DECL bool32_t gs_audio_load_mp3_data_from_file(const char* file_path, int32_t* sample_count, int32_t* channels, int32_t* sample_rate, void** samples); /* Short name definitions for convenience */ #ifndef GS_NO_SHORT_NAME /* Resources */ #define gsa_src gs_handle(gs_audio_source_t) #define gsa_inst gs_handle(gs_audio_instance_t) #define gsa_instdecl gs_audio_instance_decl_t /* Create */ #define gsa_create gs_audio_create #define gsa_destroy gs_audio_destroy #define gsa_init gs_audio_init #define gsa_shutdown gs_audio_shutdown #define gsa_submit gs_audio_submit /* Source */ #define gsa_load gs_audio_load_from_file /* Instance */ #define gsa_make_inst gs_audio_instance_create /* Audio play instance */ #define gsa_isplaying gs_audio_is_playing #define gsa_play gs_audio_play #define gsa_play_src gs_audio_play_source #define gsa_pause gs_audio_pause #define gsa_restart gs_audio_restart #endif /** @} */ // end of gs_audio /*============================= // GS_GRAPHICS =============================*/ /** @defgroup gs_graphics Graphics * Gunslinger Graphics * @{ */ // Graphics Pipeline // Main graphics resources: // Shader description: vertex, fragment, compute, geometry, tesselation // Texture Description: texture, depth, render target // Buffer Description: vertex, index, uniform, frame, pixel // Pipeline Description: vert-layout, shader, bindables, render states // Pass Description: render pass, action on render targets (clear, set viewport, etc.) /* Useful macro for forcing enum decl to be uint32_t type with default = 0x00 for quick init */ #define gs_enum_decl(NAME, ...)\ typedef enum NAME {\ _gs_##NAME##_default = 0x0,\ __VA_ARGS__,\ _gs_##NAME##_count,\ _gs_##NAME##_force_u32 = 0x7fffffff\ } NAME; #define gs_enum_count(NAME)\ _gs_##NAME##_count /* Shader Stage Type */ gs_enum_decl(gs_graphics_shader_stage_type, GS_GRAPHICS_SHADER_STAGE_VERTEX, GS_GRAPHICS_SHADER_STAGE_FRAGMENT, GS_GRAPHICS_SHADER_STAGE_COMPUTE ); /* Winding Order Type */ gs_enum_decl(gs_graphics_winding_order_type, GS_GRAPHICS_WINDING_ORDER_CW, GS_GRAPHICS_WINDING_ORDER_CCW ); /* Face Culling Type */ gs_enum_decl(gs_graphics_face_culling_type, GS_GRAPHICS_FACE_CULLING_FRONT, GS_GRAPHICS_FACE_CULLING_BACK, GS_GRAPHICS_FACE_CULLING_FRONT_AND_BACK ); /* Blend Equation Type */ gs_enum_decl(gs_graphics_blend_equation_type, GS_GRAPHICS_BLEND_EQUATION_ADD, GS_GRAPHICS_BLEND_EQUATION_SUBTRACT, GS_GRAPHICS_BLEND_EQUATION_REVERSE_SUBTRACT, GS_GRAPHICS_BLEND_EQUATION_MIN, GS_GRAPHICS_BLEND_EQUATION_MAX ); /* Blend Mode Type */ gs_enum_decl(gs_graphics_blend_mode_type, GS_GRAPHICS_BLEND_MODE_ZERO, GS_GRAPHICS_BLEND_MODE_ONE, GS_GRAPHICS_BLEND_MODE_SRC_COLOR, GS_GRAPHICS_BLEND_MODE_ONE_MINUS_SRC_COLOR, GS_GRAPHICS_BLEND_MODE_DST_COLOR, GS_GRAPHICS_BLEND_MODE_ONE_MINUS_DST_COLOR, GS_GRAPHICS_BLEND_MODE_SRC_ALPHA, GS_GRAPHICS_BLEND_MODE_ONE_MINUS_SRC_ALPHA, GS_GRAPHICS_BLEND_MODE_DST_ALPHA, GS_GRAPHICS_BLEND_MODE_ONE_MINUS_DST_ALPHA, GS_GRAPHICS_BLEND_MODE_CONSTANT_COLOR, GS_GRAPHICS_BLEND_MODE_ONE_MINUS_CONSTANT_COLOR, GS_GRAPHICS_BLEND_MODE_CONSTANT_ALPHA, GS_GRAPHICS_BLEND_MODE_ONE_MINUS_CONSTANT_ALPHA ); /* Shader Language Type */ gs_enum_decl(gs_graphics_shader_language_type, GS_GRAPHICS_SHADER_LANGUAGE_GLSL ); /* Push Constant Type */ // Really don't want to handle "auto-merging" of data types /* Uniform Type */ gs_enum_decl(gs_graphics_uniform_type, GS_GRAPHICS_UNIFORM_FLOAT, GS_GRAPHICS_UNIFORM_INT, GS_GRAPHICS_UNIFORM_VEC2, GS_GRAPHICS_UNIFORM_VEC3, GS_GRAPHICS_UNIFORM_VEC4, GS_GRAPHICS_UNIFORM_MAT4, GS_GRAPHICS_UNIFORM_SAMPLER2D, GS_GRAPHICS_UNIFORM_SAMPLER2DSHADOW, GS_GRAPHICS_UNIFORM_USAMPLER2D, GS_GRAPHICS_UNIFORM_SAMPLERCUBE, GS_GRAPHICS_UNIFORM_IMAGE2D_RGBA32F, GS_GRAPHICS_UNIFORM_BLOCK ); /* Uniform Block Usage Type */ gs_enum_decl(gs_graphics_uniform_block_usage_type, GS_GRAPHICS_UNIFORM_BLOCK_USAGE_STATIC, // Default of 0x00 is static GS_GRAPHICS_UNIFORM_BLOCK_USAGE_PUSH_CONSTANT ); /* Sampler Type */ gs_enum_decl(gs_graphics_sampler_type, GS_GRAPHICS_SAMPLER_2D ); /* Primitive Type */ gs_enum_decl(gs_graphics_primitive_type, GS_GRAPHICS_PRIMITIVE_LINES, GS_GRAPHICS_PRIMITIVE_TRIANGLES, GS_GRAPHICS_PRIMITIVE_QUADS ); /* Vertex Atribute Type */ gs_enum_decl(gs_graphics_vertex_attribute_type, GS_GRAPHICS_VERTEX_ATTRIBUTE_FLOAT4, GS_GRAPHICS_VERTEX_ATTRIBUTE_FLOAT3, GS_GRAPHICS_VERTEX_ATTRIBUTE_FLOAT2, GS_GRAPHICS_VERTEX_ATTRIBUTE_FLOAT, GS_GRAPHICS_VERTEX_ATTRIBUTE_UINT4, GS_GRAPHICS_VERTEX_ATTRIBUTE_UINT3, GS_GRAPHICS_VERTEX_ATTRIBUTE_UINT2, GS_GRAPHICS_VERTEX_ATTRIBUTE_UINT, GS_GRAPHICS_VERTEX_ATTRIBUTE_BYTE4, GS_GRAPHICS_VERTEX_ATTRIBUTE_BYTE3, GS_GRAPHICS_VERTEX_ATTRIBUTE_BYTE2, GS_GRAPHICS_VERTEX_ATTRIBUTE_BYTE ); /* Buffer Type */ gs_enum_decl(gs_graphics_buffer_type, GS_GRAPHICS_BUFFER_VERTEX, GS_GRAPHICS_BUFFER_INDEX, GS_GRAPHICS_BUFFER_FRAME, GS_GRAPHICS_BUFFER_UNIFORM, GS_GRAPHICS_BUFFER_UNIFORM_CONSTANT, GS_GRAPHICS_BUFFER_SHADER_STORAGE, GS_GRAPHICS_BUFFER_SAMPLER ); /* Buffer Usage Type */ gs_enum_decl(gs_graphics_buffer_usage_type, GS_GRAPHICS_BUFFER_USAGE_STATIC, GS_GRAPHICS_BUFFER_USAGE_STREAM, GS_GRAPHICS_BUFFER_USAGE_DYNAMIC ); /* Buffer Update Type */ gs_enum_decl(gs_graphics_buffer_update_type, GS_GRAPHICS_BUFFER_UPDATE_RECREATE, GS_GRAPHICS_BUFFER_UPDATE_SUBDATA ); gs_enum_decl(gs_graphics_access_type, GS_GRAPHICS_ACCESS_READ_ONLY, GS_GRAPHICS_ACCESS_WRITE_ONLY, GS_GRAPHICS_ACCESS_READ_WRITE ); gs_enum_decl(gs_graphics_buffer_flags, GS_GRAPHICS_BUFFER_FLAG_MAP_PERSISTENT, GS_GRAPHICS_BUFFER_FLAG_MAP_COHERENT ); //=== Texture ===// typedef enum { GS_GRAPHICS_TEXTURE_2D = 0x00, GS_GRAPHICS_TEXTURE_CUBEMAP } gs_graphics_texture_type; typedef enum { GS_GRAPHICS_TEXTURE_CUBEMAP_POSITIVE_X = 0x00, GS_GRAPHICS_TEXTURE_CUBEMAP_NEGATIVE_X, GS_GRAPHICS_TEXTURE_CUBEMAP_POSITIVE_Y, GS_GRAPHICS_TEXTURE_CUBEMAP_NEGATIVE_Y, GS_GRAPHICS_TEXTURE_CUBEMAP_POSITIVE_Z, GS_GRAPHICS_TEXTURE_CUBEMAP_NEGATIVE_Z } gs_graphics_cubemap_face_type; gs_enum_decl(gs_graphics_texture_format_type, GS_GRAPHICS_TEXTURE_FORMAT_RGBA8, GS_GRAPHICS_TEXTURE_FORMAT_RGB8, GS_GRAPHICS_TEXTURE_FORMAT_RG8, GS_GRAPHICS_TEXTURE_FORMAT_R16UI, GS_GRAPHICS_TEXTURE_FORMAT_R32UI, GS_GRAPHICS_TEXTURE_FORMAT_R32F, GS_GRAPHICS_TEXTURE_FORMAT_RGBA16F, GS_GRAPHICS_TEXTURE_FORMAT_RGBA32F, GS_GRAPHICS_TEXTURE_FORMAT_A8, GS_GRAPHICS_TEXTURE_FORMAT_R8, GS_GRAPHICS_TEXTURE_FORMAT_DEPTH8, GS_GRAPHICS_TEXTURE_FORMAT_DEPTH16, GS_GRAPHICS_TEXTURE_FORMAT_DEPTH24, GS_GRAPHICS_TEXTURE_FORMAT_DEPTH32F, GS_GRAPHICS_TEXTURE_FORMAT_DEPTH24_STENCIL8, GS_GRAPHICS_TEXTURE_FORMAT_DEPTH32F_STENCIL8, GS_GRAPHICS_TEXTURE_FORMAT_STENCIL8 ); gs_enum_decl(gs_graphics_texture_wrapping_type, GS_GRAPHICS_TEXTURE_WRAP_REPEAT, GS_GRAPHICS_TEXTURE_WRAP_MIRRORED_REPEAT, GS_GRAPHICS_TEXTURE_WRAP_CLAMP_TO_EDGE, GS_GRAPHICS_TEXTURE_WRAP_CLAMP_TO_BORDER ); gs_enum_decl(gs_graphics_texture_filtering_type, GS_GRAPHICS_TEXTURE_FILTER_NEAREST, GS_GRAPHICS_TEXTURE_FILTER_LINEAR ); //=== Clear ===// gs_enum_decl(gs_graphics_clear_flag, GS_GRAPHICS_CLEAR_COLOR = 0x01, GS_GRAPHICS_CLEAR_DEPTH = 0x02, GS_GRAPHICS_CLEAR_STENCIL = 0x04, GS_GRAPHICS_CLEAR_NONE = 0x08 ); #define GS_GRAPHICS_CLEAR_ALL\ GS_GRAPHICS_CLEAR_COLOR |\ GS_GRAPHICS_CLEAR_DEPTH |\ GS_GRAPHICS_CLEAR_STENCIL //=== Bind Type ===// gs_enum_decl(gs_graphics_bind_type, GS_GRAPHICS_BIND_VERTEX_BUFFER, GS_GRAPHICS_BIND_INDEX_BUFFER, GS_GRAPHICS_BIND_UNIFORM_BUFFER, GS_GRAPHICS_BIND_STORAGE_BUFFER, GS_GRAPHICS_BIND_IMAGE_BUFFER, GS_GRAPHICS_BIND_UNIFORM ); /* Depth Function Type */ gs_enum_decl(gs_graphics_depth_func_type, // Default value of 0x00 means depth is disabled GS_GRAPHICS_DEPTH_FUNC_NEVER, GS_GRAPHICS_DEPTH_FUNC_LESS, GS_GRAPHICS_DEPTH_FUNC_EQUAL, GS_GRAPHICS_DEPTH_FUNC_LEQUAL, GS_GRAPHICS_DEPTH_FUNC_GREATER, GS_GRAPHICS_DEPTH_FUNC_NOTEQUAL, GS_GRAPHICS_DEPTH_FUNC_GEQUAL, GS_GRAPHICS_DEPTH_FUNC_ALWAYS ); gs_enum_decl(gs_graphics_depth_mask_type, // Default value 0x00 means depth writing enabled GS_GRAPHICS_DEPTH_MASK_ENABLED, GS_GRAPHICS_DEPTH_MASK_DISABLED ); /* Stencil Function Type */ gs_enum_decl(gs_graphics_stencil_func_type, GS_GRAPHICS_STENCIL_FUNC_NEVER, // Default value of 0x00 means stencil is disabled GS_GRAPHICS_STENCIL_FUNC_LESS, GS_GRAPHICS_STENCIL_FUNC_EQUAL, GS_GRAPHICS_STENCIL_FUNC_LEQUAL, GS_GRAPHICS_STENCIL_FUNC_GREATER, GS_GRAPHICS_STENCIL_FUNC_NOTEQUAL, GS_GRAPHICS_STENCIL_FUNC_GEQUAL, GS_GRAPHICS_STENCIL_FUNC_ALWAYS ); /* Stencil Op Type */ gs_enum_decl(gs_graphics_stencil_op_type, // Default value of 0x00 means keep is used GS_GRAPHICS_STENCIL_OP_KEEP, GS_GRAPHICS_STENCIL_OP_ZERO, GS_GRAPHICS_STENCIL_OP_REPLACE, GS_GRAPHICS_STENCIL_OP_INCR, GS_GRAPHICS_STENCIL_OP_INCR_WRAP, GS_GRAPHICS_STENCIL_OP_DECR, GS_GRAPHICS_STENCIL_OP_DECR_WRAP, GS_GRAPHICS_STENCIL_OP_INVERT ); /* Internal Graphics Resource Handles */ gs_handle_decl(gs_graphics_shader_t); gs_handle_decl(gs_graphics_texture_t); gs_handle_decl(gs_graphics_vertex_buffer_t); gs_handle_decl(gs_graphics_index_buffer_t); gs_handle_decl(gs_graphics_uniform_buffer_t); gs_handle_decl(gs_graphics_storage_buffer_t); gs_handle_decl(gs_graphics_framebuffer_t); gs_handle_decl(gs_graphics_uniform_t); gs_handle_decl(gs_graphics_renderpass_t); gs_handle_decl(gs_graphics_pipeline_t); /* Graphics Shader Source Desc */ typedef struct gs_graphics_shader_source_desc_t { gs_graphics_shader_stage_type type; // Shader stage type (vertex, fragment, tesselation, geometry, compute) const char* source; // Source for shader } gs_graphics_shader_source_desc_t; /* Graphics Shader Desc */ typedef struct gs_graphics_shader_desc_t { gs_graphics_shader_source_desc_t* sources; // Array of shader source descriptions size_t size; // Size in bytes of shader source desc array char name[64]; // Optional (for logging and debugging mainly) } gs_graphics_shader_desc_t; #define GS_GRAPHICS_TEXTURE_DATA_MAX 6 /* Graphics Texture Desc */ typedef struct gs_graphics_texture_desc_t { gs_graphics_texture_type type; uint32_t width; // Width of texture in texels uint32_t height; // Height of texture in texels uint32_t depth; // Depth of texture void* data[GS_GRAPHICS_TEXTURE_DATA_MAX]; // Texture data to upload (can be null) gs_graphics_texture_format_type format; // Format of texture data (rgba32, rgba8, rgba32f, r8, depth32f, etc...) gs_graphics_texture_wrapping_type wrap_s; // Wrapping type for s axis of texture gs_graphics_texture_wrapping_type wrap_t; // Wrapping type for t axis of texture gs_graphics_texture_wrapping_type wrap_r; // Wrapping type for r axis of texture gs_graphics_texture_filtering_type min_filter; // Minification filter for texture gs_graphics_texture_filtering_type mag_filter; // Magnification filter for texture gs_graphics_texture_filtering_type mip_filter; // Mip filter for texture gs_vec2 offset; // Offset for updates uint32_t num_mips; // Number of mips to generate (default 0 is disable mip generation) struct { uint32_t x; // X offset in texels to start read uint32_t y; // Y offset in texels to start read uint32_t width; // Width in texels for texture uint32_t height; // Height in texels for texture size_t size; // Size in bytes for data to be read } read; uint16_t flip_y; // Whether or not y is flipped } gs_graphics_texture_desc_t; /* Graphics Uniform Layout Desc */ typedef struct gs_graphics_uniform_layout_desc_t { gs_graphics_uniform_type type; // Type of field char fname[64]; // Name of field (required for implicit APIs, like OpenGL/ES) uint32_t count; // Count variable (used for arrays such as glUniformXXXv) } gs_graphics_uniform_layout_desc_t; /* Graphics Uniform Desc */ typedef struct gs_graphics_uniform_desc_t { gs_graphics_shader_stage_type stage; char name[64]; // Name of uniform (required for OpenGL/ES, WebGL) gs_graphics_uniform_layout_desc_t* layout; // Layout array for uniform data size_t layout_size; // Size of uniform data in bytes } gs_graphics_uniform_desc_t; typedef struct gs_graphics_buffer_update_desc_t { gs_graphics_buffer_update_type type; size_t offset; } gs_graphics_buffer_update_desc_t; /* Graphics Buffer Desc General */ typedef struct gs_graphics_buffer_base_desc_t { void * data; size_t size; gs_graphics_buffer_usage_type usage; } gs_graphics_buffer_base_desc_t; typedef struct gs_graphics_vertex_buffer_desc_t { void* data; size_t size; gs_graphics_buffer_usage_type usage; gs_graphics_buffer_update_desc_t update; } gs_graphics_vertex_buffer_desc_t; typedef gs_graphics_vertex_buffer_desc_t gs_graphics_index_buffer_desc_t; typedef struct gs_graphics_uniform_buffer_desc_t { void* data; size_t size; gs_graphics_buffer_usage_type usage; const char* name; gs_graphics_shader_stage_type stage; gs_graphics_buffer_update_desc_t update; } gs_graphics_uniform_buffer_desc_t; typedef struct gs_graphics_storage_buffer_desc_t { void* data; void* map; size_t size; char name[64]; gs_graphics_buffer_usage_type usage; gs_graphics_access_type access; gs_graphics_buffer_flags flags; gs_graphics_buffer_update_desc_t update; } gs_graphics_storage_buffer_desc_t; typedef struct gs_graphics_framebuffer_desc_t { void* data; } gs_graphics_framebuffer_desc_t; /* Graphics Clear Action */ typedef struct gs_graphics_clear_action_t { gs_graphics_clear_flag flag; // Flag to be set (clear color, clear depth, clear stencil, clear all) union { float color[4]; // Clear color value float depth; // Clear depth value int32_t stencil; // Clear stencil value }; } gs_graphics_clear_action_t; /* Graphics Clear Desc */ typedef struct gs_graphics_clear_desc_t { gs_graphics_clear_action_t* actions; // Clear action array size_t size; // Size } gs_graphics_clear_desc_t; /* Graphics Render Pass Desc */ typedef struct gs_graphics_renderpass_desc_t { gs_handle(gs_graphics_framebuffer_t) fbo; // Default is set to invalid for backbuffer gs_handle(gs_graphics_texture_t)* color; // Array of color attachments to be bound (useful for MRT, if supported) size_t color_size; // Size of color attachment array gs_handle(gs_graphics_texture_t) depth; // Depth attachment to be bound gs_handle(gs_graphics_texture_t) stencil; // Depth attachment to be bound } gs_graphics_renderpass_desc_t; /* // If you want to write to a color attachment, you have to have a frame buffer attached that isn't the backbuffer */ typedef enum gs_graphics_vertex_data_type { GS_GRAPHICS_VERTEX_DATA_INTERLEAVED = 0x00, GS_GRAPHICS_VERTEX_DATA_NONINTERLEAVED } gs_graphics_vertex_data_type; typedef struct gs_graphics_bind_vertex_buffer_desc_t { gs_handle(gs_graphics_vertex_buffer_t) buffer; size_t offset; gs_graphics_vertex_data_type data_type; } gs_graphics_bind_vertex_buffer_desc_t; typedef struct gs_graphics_bind_index_buffer_desc_t { gs_handle(gs_graphics_index_buffer_t) buffer; } gs_graphics_bind_index_buffer_desc_t; typedef struct gs_graphics_bind_image_buffer_desc_t { gs_handle(gs_graphics_texture_t) tex; uint32_t binding; gs_graphics_access_type access; } gs_graphics_bind_image_buffer_desc_t; typedef struct gs_graphics_bind_uniform_buffer_desc_t { gs_handle(gs_graphics_uniform_buffer_t) buffer; uint32_t binding; struct { size_t offset; // Specify an offset for ranged binds. size_t size; // Specify size for ranged binds. } range; } gs_graphics_bind_uniform_buffer_desc_t; // All this needs to be unified... typedef struct gs_graphics_bind_storage_buffer_desc_t { gs_handle(gs_graphics_storage_buffer_t) buffer; uint32_t binding; struct { size_t offset; // Specify an offset for ranged binds. size_t size; // Specify size for ranged binds. } range; } gs_graphics_bind_storage_buffer_desc_t; typedef struct gs_graphics_bind_uniform_desc_t { gs_handle(gs_graphics_uniform_t) uniform; void* data; uint32_t binding; // Base binding for samplers? } gs_graphics_bind_uniform_desc_t; /* Graphics Binding Desc */ typedef struct gs_graphics_bind_desc_t { struct { gs_graphics_bind_vertex_buffer_desc_t* desc; // Array of vertex buffer declarations (NULL by default) size_t size; // Size of array in bytes (optional if only one) } vertex_buffers; struct { gs_graphics_bind_index_buffer_desc_t* desc; // Array of index buffer declarations (NULL by default) size_t size; // Size of array in bytes (optional if only one) } index_buffers; struct { gs_graphics_bind_uniform_buffer_desc_t* desc; // Array of uniform buffer declarations (NULL by default) size_t size; // Size of array in bytes (optional if only one) } uniform_buffers; struct { gs_graphics_bind_uniform_desc_t* desc; // Array of uniform declarations (NULL by default) size_t size; // Size of array in bytes (optional if one) } uniforms; struct { gs_graphics_bind_image_buffer_desc_t* desc; size_t size; } image_buffers; struct { gs_graphics_bind_storage_buffer_desc_t* desc; size_t size; } storage_buffers; } gs_graphics_bind_desc_t; /* Graphics Blend State Desc */ typedef struct gs_graphics_blend_state_desc_t { gs_graphics_blend_equation_type func; // Equation function to use for blend ops gs_graphics_blend_mode_type src; // Source blend mode gs_graphics_blend_mode_type dst; // Destination blend mode } gs_graphics_blend_state_desc_t; /* Graphics Depth State Desc */ typedef struct gs_graphics_depth_state_desc_t { gs_graphics_depth_func_type func; // Function to set for depth test gs_graphics_depth_mask_type mask; // Whether or not writing is enabled/disabled } gs_graphics_depth_state_desc_t; /* Graphics Stencil State Desc */ typedef struct gs_graphics_stencil_state_desc_t { gs_graphics_stencil_func_type func; // Function to set for stencil test uint32_t ref; // Specifies reference val for stencil test uint32_t comp_mask; // Specifies mask that is ANDed with both ref val and stored stencil val uint32_t write_mask; // Specifies mask that is ANDed with both ref val and stored stencil val gs_graphics_stencil_op_type sfail; // Action to take when stencil test fails gs_graphics_stencil_op_type dpfail; // Action to take when stencil test passes but depth test fails gs_graphics_stencil_op_type dppass; // Action to take when both stencil test passes and either depth passes or is not enabled } gs_graphics_stencil_state_desc_t; /* Graphics Raster State Desc */ typedef struct gs_graphics_raster_state_desc_t { gs_graphics_face_culling_type face_culling; // Face culling mode to be used (front, back, front and back) gs_graphics_winding_order_type winding_order; // Winding order mode to be used (ccw, cw) gs_graphics_primitive_type primitive; // Primitive type for drawing (lines, quads, triangles, triangle strip) gs_handle(gs_graphics_shader_t) shader; // Shader to bind and use (might be in bindables later on, not sure) size_t index_buffer_element_size; // Element size of index buffer (used for parsing internal data) } gs_graphics_raster_state_desc_t; /* Graphics Compute State Desc */ typedef struct gs_graphics_compute_state_desc_t { gs_handle(gs_graphics_shader_t) shader; // Compute shader to bind } gs_graphics_compute_state_desc_t; /* Graphics Vertex Attribute Desc */ typedef struct gs_graphics_vertex_attribute_desc_t { char name[64]; // Attribute name (required for lower versions of OpenGL and ES) gs_graphics_vertex_attribute_type format; // Format for vertex attribute size_t stride; // Total stride of vertex layout (optional, calculated by default) size_t offset; // Offset of this vertex from base pointer of data (optional, calaculated by default) size_t divisor; // Used for instancing. (optional, default = 0x00 for no instancing) uint32_t buffer_idx; // Vertex buffer to use (optional, default = 0x00) } gs_graphics_vertex_attribute_desc_t; /* Graphics Vertex Layout Desc */ typedef struct gs_graphics_vertex_layout_desc_t { gs_graphics_vertex_attribute_desc_t* attrs; // Vertex attribute array size_t size; // Size in bytes of vertex attribute array } gs_graphics_vertex_layout_desc_t; /* Graphics Pipeline Desc */ typedef struct gs_graphics_pipeline_desc_t { gs_graphics_blend_state_desc_t blend; // Blend state desc for pipeline gs_graphics_depth_state_desc_t depth; // Depth state desc for pipeline gs_graphics_raster_state_desc_t raster; // Raster state desc for pipeline gs_graphics_stencil_state_desc_t stencil; // Stencil state desc for pipeline gs_graphics_compute_state_desc_t compute; // Compute state desc for pipeline gs_graphics_vertex_layout_desc_t layout; // Vertex layout desc for pipeline } gs_graphics_pipeline_desc_t; /* Graphics Draw Desc */ typedef struct gs_graphics_draw_desc_t { uint32_t start; uint32_t count; uint32_t instances; uint32_t base_vertex; struct { uint32_t start; uint32_t end; } range; } gs_graphics_draw_desc_t; gs_inline gs_handle(gs_graphics_renderpass_t) __gs_renderpass_default_impl() { gs_handle(gs_graphics_renderpass_t) hndl = gs_default_val(); return hndl; } // Convenience define for default render pass to back buffer #define GS_GRAPHICS_RENDER_PASS_DEFAULT __gs_renderpass_default_impl() typedef struct gs_graphics_info_t { uint32_t major_version; uint32_t minor_version; uint32_t max_texture_units; uint32_t max_ssbo_block_size; struct { bool32 available; uint32_t max_work_group_count[3]; uint32_t max_work_group_size[3]; uint32_t max_work_group_invocations; } compute; } gs_graphics_info_t; /*========================== // Graphics Interface ==========================*/ typedef struct gs_graphics_t { void* user_data; // For internal use gs_graphics_info_t info; // Used for querying by user for features struct { // Create gs_handle(gs_graphics_texture_t) (* texture_create)(const gs_graphics_texture_desc_t* desc); gs_handle(gs_graphics_uniform_t) (* uniform_create)(const gs_graphics_uniform_desc_t* desc); gs_handle(gs_graphics_shader_t) (* shader_create)(const gs_graphics_shader_desc_t* desc); gs_handle(gs_graphics_vertex_buffer_t) (* vertex_buffer_create)(const gs_graphics_vertex_buffer_desc_t* desc); gs_handle(gs_graphics_index_buffer_t) (* index_buffer_create)(const gs_graphics_index_buffer_desc_t* desc); gs_handle(gs_graphics_uniform_buffer_t) (* uniform_buffer_create)(const gs_graphics_uniform_buffer_desc_t* desc); gs_handle(gs_graphics_storage_buffer_t) (* storage_buffer_create)(const gs_graphics_storage_buffer_desc_t* desc); gs_handle(gs_graphics_framebuffer_t) (* framebuffer_create)(const gs_graphics_framebuffer_desc_t* desc); gs_handle(gs_graphics_renderpass_t) (* renderpass_create)(const gs_graphics_renderpass_desc_t* desc); gs_handle(gs_graphics_pipeline_t) (* pipeline_create)(const gs_graphics_pipeline_desc_t* desc); // Destroy void (* texture_destroy)(gs_handle(gs_graphics_texture_t) hndl); void (* uniform_destroy)(gs_handle(gs_graphics_uniform_t) hndl); void (* shader_destroy)(gs_handle(gs_graphics_shader_t) hndl); void (* vertex_buffer_destroy)(gs_handle(gs_graphics_vertex_buffer_t) hndl); void (* index_buffer_destroy)(gs_handle(gs_graphics_index_buffer_t) hndl); void (* uniform_buffer_destroy)(gs_handle(gs_graphics_uniform_buffer_t) hndl); void (* storage_buffer_destroy)(gs_handle(gs_graphics_storage_buffer_t) hndl); void (* framebuffer_destroy)(gs_handle(gs_graphics_framebuffer_t) hndl); void (* renderpass_destroy)(gs_handle(gs_graphics_renderpass_t) hndl); void (* pipeline_destroy)(gs_handle(gs_graphics_pipeline_t) hndl); // Resource Updates (main thread only) void (* vertex_buffer_update)(gs_handle(gs_graphics_vertex_buffer_t) hndl, gs_graphics_vertex_buffer_desc_t* desc); void (* index_buffer_update)(gs_handle(gs_graphics_index_buffer_t) hndl, gs_graphics_index_buffer_desc_t* desc); void (* storage_buffer_update)(gs_handle(gs_graphics_storage_buffer_t) hndl, gs_graphics_storage_buffer_desc_t* desc); void (* texture_update)(gs_handle(gs_graphics_texture_t) hndl, gs_graphics_texture_desc_t* desc); void (* texture_read)(gs_handle(gs_graphics_texture_t) hndl, gs_graphics_texture_desc_t* desc); // Util void* (* storage_buffer_map_get)(gs_handle(gs_graphics_storage_buffer_t) hndl); void* (* storage_buffer_lock)(gs_handle(gs_graphics_storage_buffer_t) hndl, size_t offset, size_t sz); void (* storage_buffer_unlock)(gs_handle(gs_graphics_storage_buffer_t) hndl); void (* storage_buffer_get_data)(gs_handle(gs_graphics_storage_buffer_t) hndl, size_t offset, size_t stride, void* out); // Submission (Main Thread) void (* command_buffer_submit)(gs_command_buffer_t* cb); } api; // Interface for stable access across .dll boundaries } gs_graphics_t; /*========================== // Graphics API ==========================*/ #define gs_graphics() gs_ctx()->graphics // Graphics Interface Creation / Initialization / Shutdown / Destruction GS_API_DECL gs_graphics_t* gs_graphics_create(); GS_API_DECL void gs_graphics_destroy(gs_graphics_t* graphics); GS_API_DECL void gs_graphics_init(gs_graphics_t* graphics); GS_API_DECL void gs_graphics_shutdown(gs_graphics_t* graphics); // Graphics Info Object Query GS_API_DECL gs_graphics_info_t* gs_graphics_info(); // Resource Creation // Create GS_API_DECL gs_handle(gs_graphics_texture_t) gs_graphics_texture_create(const gs_graphics_texture_desc_t* desc); GS_API_DECL gs_handle(gs_graphics_uniform_t) gs_graphics_uniform_create(const gs_graphics_uniform_desc_t* desc); GS_API_DECL gs_handle(gs_graphics_shader_t) gs_graphics_shader_create(const gs_graphics_shader_desc_t* desc); GS_API_DECL gs_handle(gs_graphics_vertex_buffer_t) gs_graphics_vertex_buffer_create(const gs_graphics_vertex_buffer_desc_t* desc); GS_API_DECL gs_handle(gs_graphics_index_buffer_t) gs_graphics_index_buffer_create(const gs_graphics_index_buffer_desc_t* desc); GS_API_DECL gs_handle(gs_graphics_uniform_buffer_t) gs_graphics_uniform_buffer_create(const gs_graphics_uniform_buffer_desc_t* desc); GS_API_DECL gs_handle(gs_graphics_storage_buffer_t) gs_graphics_storage_buffer_create(const gs_graphics_storage_buffer_desc_t* desc); GS_API_DECL gs_handle(gs_graphics_framebuffer_t) gs_graphics_framebuffer_create(const gs_graphics_framebuffer_desc_t* desc); GS_API_DECL gs_handle(gs_graphics_renderpass_t) gs_graphics_renderpass_create(const gs_graphics_renderpass_desc_t* desc); GS_API_DECL gs_handle(gs_graphics_pipeline_t) gs_graphics_pipeline_create(const gs_graphics_pipeline_desc_t* desc); // Destroy GS_API_DECL void gs_graphics_texture_destroy(gs_handle(gs_graphics_texture_t) hndl); GS_API_DECL void gs_graphics_uniform_destroy(gs_handle(gs_graphics_uniform_t) hndl); GS_API_DECL void gs_graphics_shader_destroy(gs_handle(gs_graphics_shader_t) hndl); GS_API_DECL void gs_graphics_vertex_buffer_destroy(gs_handle(gs_graphics_vertex_buffer_t) hndl); GS_API_DECL void gs_graphics_index_buffer_destroy(gs_handle(gs_graphics_index_buffer_t) hndl); GS_API_DECL void gs_graphics_uniform_buffer_destroy(gs_handle(gs_graphics_uniform_buffer_t) hndl); GS_API_DECL void gs_graphics_storage_buffer_destroy(gs_handle(gs_graphics_storage_buffer_t) hndl); GS_API_DECL void gs_graphics_framebuffer_destroy(gs_handle(gs_graphics_framebuffer_t) hndl); GS_API_DECL void gs_graphics_renderpass_destroy(gs_handle(gs_graphics_renderpass_t) hndl); GS_API_DECL void gs_graphics_pipeline_destroy(gs_handle(gs_graphics_pipeline_t) hndl); // Resource Updates (main thread only) GS_API_DECL void gs_graphics_vertex_buffer_update(gs_handle(gs_graphics_vertex_buffer_t) hndl, gs_graphics_vertex_buffer_desc_t* desc); GS_API_DECL void gs_graphics_index_buffer_update(gs_handle(gs_graphics_index_buffer_t) hndl, gs_graphics_index_buffer_desc_t* desc); GS_API_DECL void gs_graphics_storage_buffer_update(gs_handle(gs_graphics_storage_buffer_t) hndl, gs_graphics_storage_buffer_desc_t* desc); GS_API_DECL void gs_graphics_texture_update(gs_handle(gs_graphics_texture_t) hndl, gs_graphics_texture_desc_t* desc); GS_API_DECL void gs_graphics_texture_read(gs_handle(gs_graphics_texture_t) hndl, gs_graphics_texture_desc_t* desc); // Resource Queries GS_API_DECL void gs_graphics_pipeline_desc_query(gs_handle(gs_graphics_pipeline_t) hndl, gs_graphics_pipeline_desc_t* out); GS_API_DECL void gs_graphics_texture_desc_query(gs_handle(gs_graphics_texture_t) hndl, gs_graphics_texture_desc_t* out); GS_API_DECL size_t gs_graphics_uniform_size_query(gs_handle(gs_graphics_uniform_t) hndl); // Util GS_API_DECL void* gs_graphics_storage_buffer_map_get(gs_handle(gs_graphics_storage_buffer_t) hndl); GS_API_DECL void* gs_graphics_storage_buffer_lock(gs_handle(gs_graphics_storage_buffer_t) hndl, size_t offset, size_t sz); GS_API_DECL void gs_graphics_storage_buffer_unlock(gs_handle(gs_graphics_storage_buffer_t) hndl); GS_API_DECL void gs_graphics_storage_buffer_get_data(gs_handle(gs_graphics_storage_buffer_t) hndl, size_t offset, size_t stride, void* out); // Resource In-Flight Update GS_API_DECL void gs_graphics_texture_request_update(gs_command_buffer_t* cb, gs_handle(gs_graphics_texture_t) hndl, gs_graphics_texture_desc_t* desc); GS_API_DECL void gs_graphics_vertex_buffer_request_update(gs_command_buffer_t* cb, gs_handle(gs_graphics_vertex_buffer_t) hndl, gs_graphics_vertex_buffer_desc_t* desc); GS_API_DECL void gs_graphics_index_buffer_request_update(gs_command_buffer_t* cb, gs_handle(gs_graphics_index_buffer_t) hndl, gs_graphics_index_buffer_desc_t* desc); GS_API_DECL void gs_graphics_uniform_buffer_request_update(gs_command_buffer_t* cb, gs_handle(gs_graphics_uniform_buffer_t) hndl, gs_graphics_uniform_buffer_desc_t* desc); GS_API_DECL void gs_graphics_storage_buffer_request_update(gs_command_buffer_t* cb, gs_handle(gs_graphics_storage_buffer_t) hndl, gs_graphics_storage_buffer_desc_t* desc); // Pipeline / Pass / Bind / Draw GS_API_DECL void gs_graphics_renderpass_begin(gs_command_buffer_t* cb, gs_handle(gs_graphics_renderpass_t) hndl); GS_API_DECL void gs_graphics_renderpass_end(gs_command_buffer_t* cb); GS_API_DECL void gs_graphics_set_viewport(gs_command_buffer_t* cb, uint32_t x, uint32_t y, uint32_t w, uint32_t h); GS_API_DECL void gs_graphics_set_view_scissor(gs_command_buffer_t* cb, uint32_t x, uint32_t y, uint32_t w, uint32_t h); GS_API_DECL void gs_graphics_clear(gs_command_buffer_t* cb, gs_graphics_clear_desc_t* desc); GS_API_DECL void gs_graphics_pipeline_bind(gs_command_buffer_t* cb, gs_handle(gs_graphics_pipeline_t) hndl); GS_API_DECL void gs_graphics_apply_bindings(gs_command_buffer_t* cb, gs_graphics_bind_desc_t* binds); GS_API_DECL void gs_graphics_draw(gs_command_buffer_t* cb, gs_graphics_draw_desc_t* desc); GS_API_DECL void gs_graphics_dispatch_compute(gs_command_buffer_t* cb, uint32_t num_x_groups, uint32_t num_y_groups, uint32_t num_z_groups); // Submission (Main Thread) #define gs_graphics_command_buffer_submit(CB) gs_graphics()->api.command_buffer_submit((CB)) #ifndef GS_NO_SHORT_NAME typedef gs_handle(gs_graphics_shader_t) gs_shader_t; typedef gs_handle(gs_graphics_texture_t) gs_texture_t; typedef gs_handle(gs_graphics_renderpass_t) gs_renderpass_t; typedef gs_handle(gs_graphics_framebuffer_t) gs_framebuffer_t; typedef gs_handle(gs_graphics_pipeline_t) gs_pipeline_t; typedef gs_handle(gs_graphics_vertex_buffer_t) gs_vbo_t; typedef gs_handle(gs_graphics_index_buffer_t) gs_ibo_t; typedef gs_handle(gs_graphics_uniform_buffer_t) gs_ubo_t; typedef gs_handle(gs_graphics_uniform_t) gs_uniform_t; typedef gs_handle(gs_graphics_storage_buffer_t) gs_ssbo_t; #endif /** @} */ // end of gs_graphics /*========================== // GS_ASSET_TYPES ==========================*/ /** @addtogroup gs_util * @{ */ // Texture typedef struct gs_asset_texture_t { gs_handle(gs_graphics_texture_t) hndl; gs_graphics_texture_desc_t desc; } gs_asset_texture_t; GS_API_DECL bool gs_asset_texture_load_from_file(const char* path, void* out, gs_graphics_texture_desc_t* desc, bool32_t flip_on_load, bool32_t keep_data); GS_API_DECL bool gs_asset_texture_load_from_memory(const void* memory, size_t sz, void* out, gs_graphics_texture_desc_t* desc, bool32_t flip_on_load, bool32_t keep_data); // Font typedef struct gs_baked_char_t { uint32_t codepoint; uint16_t x0, y0, x1, y1; float xoff, yoff, advance; uint32_t width, height; } gs_baked_char_t; typedef struct gs_asset_font_t { void* font_info; gs_baked_char_t glyphs[96]; gs_asset_texture_t texture; float ascent; float descent; float line_gap; } gs_asset_font_t; GS_API_DECL bool gs_asset_font_load_from_file(const char* path, void* out, uint32_t point_size); GS_API_DECL bool gs_asset_font_load_from_memory(const void* memory, size_t sz, void* out, uint32_t point_size); GS_API_DECL gs_vec2 gs_asset_font_text_dimensions(const gs_asset_font_t* font, const char* text, int32_t len); GS_API_DECL gs_vec2 gs_asset_font_text_dimensions_ex(const gs_asset_font_t* fp, const char* text, int32_t len, bool32_t include_past_baseline); GS_API_DECL float gs_asset_font_max_height(const gs_asset_font_t* font); // Audio typedef struct gs_asset_audio_t { gs_handle(gs_audio_source_t) hndl; } gs_asset_audio_t; GS_API_DECL bool gs_asset_audio_load_from_file(const char* path, void* out); // Mesh gs_enum_decl(gs_asset_mesh_attribute_type, GS_ASSET_MESH_ATTRIBUTE_TYPE_POSITION, GS_ASSET_MESH_ATTRIBUTE_TYPE_NORMAL, GS_ASSET_MESH_ATTRIBUTE_TYPE_TANGENT, GS_ASSET_MESH_ATTRIBUTE_TYPE_JOINT, GS_ASSET_MESH_ATTRIBUTE_TYPE_WEIGHT, GS_ASSET_MESH_ATTRIBUTE_TYPE_TEXCOORD, GS_ASSET_MESH_ATTRIBUTE_TYPE_COLOR, GS_ASSET_MESH_ATTRIBUTE_TYPE_UINT ); typedef struct gs_asset_mesh_layout_t { gs_asset_mesh_attribute_type type; // Type of attribute uint32_t idx; // Optional index (for joint/weight/texcoord/color) } gs_asset_mesh_layout_t; typedef struct gs_asset_mesh_decl_t { gs_asset_mesh_layout_t* layout; // Mesh attribute layout array size_t layout_size; // Size of mesh attribute layout array in bytes size_t index_buffer_element_size; // Size of index data size in bytes } gs_asset_mesh_decl_t; typedef struct gs_asset_mesh_primitive_t { gs_handle(gs_graphics_vertex_buffer_t) vbo; gs_handle(gs_graphics_index_buffer_t) ibo; uint32_t count; } gs_asset_mesh_primitive_t; typedef struct gs_asset_mesh_t { gs_dyn_array(gs_asset_mesh_primitive_t) primitives; } gs_asset_mesh_t; // Structured/packed raw mesh data typedef struct gs_asset_mesh_raw_data_t { uint32_t prim_count; size_t* vertex_sizes; size_t* index_sizes; void** vertices; void** indices; } gs_asset_mesh_raw_data_t; GS_API_DECL bool gs_asset_mesh_load_from_file(const char* path, void* out, gs_asset_mesh_decl_t* decl, void* data_out, size_t data_size); GS_API_DECL bool gs_util_load_gltf_data_from_file(const char* path, gs_asset_mesh_decl_t* decl, gs_asset_mesh_raw_data_t** out, uint32_t* mesh_count); GS_API_DECL bool gs_util_load_gltf_data_from_memory(const void* memory, size_t sz, gs_asset_mesh_decl_t* decl, gs_asset_mesh_raw_data_t** out, uint32_t* mesh_count); /** @} */ // end of gs_util // Material // How to do this? Materials really are utility types... // So should they be relegated to a utility file? /* gs_util_material? */ // Pipeline // Uniform /* // Could pass in a mesh decl? Then it'll just give you back packed vertex/index data for each primitive? GS_API_DECL gs_util_load_gltf_from_file(const char* path, gs_asset_mesh_decl_t* decl, uint32_t* primitive_count, void*** verticies, size_t** vertex_sizes, void*** indices, size_t** index_sizes); To use: // For primitives uint32_t prim_count = 0; size_t* vertex_sizes = NULL; size_t* index_sizes = NULL; float** vertices = NULL; uint32_t** indices = NULL; gs_asset_mesh_raw_data_t data = {}; gs_util_load_gltf_from_file("path", &decl, &data); */ /*========================== // GS_ENGINE / GS_APP ==========================*/ /** @defgroup gs_app App/Engine * Gunslinger App/Engine * @{ */ // Application descriptor for user application typedef struct gs_app_desc_t { void (* init)(); void (* update)(); void (* shutdown)(); gs_platform_window_desc_t window; bool32 is_running; bool32 debug_gfx; void* user_data; int32_t argc; char** argv; // Platform specific data #ifdef GS_PLATFORM_ANDROID struct { void* activity; const char* internal_data_path; } android; #endif } gs_app_desc_t; /* Game Context: * This is the main context for the gunslinger framework. Holds pointers to all interfaces registered with the framework, including the description for your application. */ typedef struct gs_context_t { gs_platform_t* platform; gs_graphics_t* graphics; gs_audio_t* audio; gs_app_desc_t app; gs_os_api_t os; gs_atomic_int_t lock; } gs_context_t; typedef struct gs_t { gs_context_t ctx; void (* shutdown)(); } gs_t; /* Desc */ GS_API_DECL gs_t* gs_create(gs_app_desc_t app_desc); /* Desc */ GS_API_DECL void gs_destroy(); /* Desc */ GS_API_DECL gs_t* gs_instance(); /* Desc */ GS_API_DECL void gs_set_instance(gs_t* gs); /* Desc */ GS_API_DECL gs_context_t* gs_ctx(); /* Desc */ GS_API_DECL gs_app_desc_t* gs_app(); /* Desc */ GS_API_DECL void gs_frame(); /* Desc */ GS_API_DECL void gs_quit(); /* Desc */ GS_API_DECL gs_app_desc_t gs_main(int32_t argc, char** argv); #define gs_subsystem(__T)\ (gs_instance()->ctx.__T) #define gs_user_data(__T)\ (__T*)(gs_instance()->ctx.app.user_data) /** @} */ // end of gs_app /*================================================================================================================================== // ===== Gunslinger Implementation ============================================================================================== // ==================================================================================================================================*/ #ifdef GS_IMPL /*============================= // GS_PLATFORM =============================*/ // Default provided platform implementations (these will be removed eventually) #ifndef GS_PLATFORM_IMPL_CUSTOM #if (defined GS_PLATFORM_WIN || defined GS_PLATFORM_APPLE || defined GS_PLATFORM_LINUX) #define GS_PLATFORM_IMPL_GLFW #elif (defined GS_PLATFORM_WEB) #define GS_PLATFORM_IMPL_EMSCRIPTEN #elif (defined GS_PLATFORM_ANDROID) #define GS_PLATFORM_IMPL_ANDROID #endif #endif #ifdef GS_PLATFORM_IMPL_FILE #include GS_PLATFORM_IMPL_FILE #endif #include "impl/gs_platform_impl.h" /*============================= // GS_GRAPHICS =============================*/ #ifndef GS_GRAPHICS_IMPL_CUSTOM #if (defined GS_PLATFORM_WIN || defined GS_PLATFORM_APPLE || defined GS_PLATFORM_LINUX) #define GS_GRAPHICS_IMPL_OPENGL_CORE #else #define GS_GRAPHICS_IMPL_OPENGL_ES #endif #endif #include "impl/gs_graphics_impl.h" // Resource Creation GS_API_DECL gs_handle(gs_graphics_texture_t) gs_graphics_texture_create(const gs_graphics_texture_desc_t* desc) { return gs_graphics()->api.texture_create(desc); } GS_API_DECL gs_handle(gs_graphics_uniform_t) gs_graphics_uniform_create(const gs_graphics_uniform_desc_t* desc) { return gs_graphics()->api.uniform_create(desc); } GS_API_DECL gs_handle(gs_graphics_shader_t) gs_graphics_shader_create(const gs_graphics_shader_desc_t* desc) { return gs_graphics()->api.shader_create(desc); } GS_API_DECL gs_handle(gs_graphics_vertex_buffer_t) gs_graphics_vertex_buffer_create(const gs_graphics_vertex_buffer_desc_t* desc) { return gs_graphics()->api.vertex_buffer_create(desc); } GS_API_DECL gs_handle(gs_graphics_index_buffer_t) gs_graphics_index_buffer_create(const gs_graphics_index_buffer_desc_t* desc) { return gs_graphics()->api.index_buffer_create(desc); } GS_API_DECL gs_handle(gs_graphics_uniform_buffer_t) gs_graphics_uniform_buffer_create(const gs_graphics_uniform_buffer_desc_t* desc) { return gs_graphics()->api.uniform_buffer_create(desc); } GS_API_DECL gs_handle(gs_graphics_storage_buffer_t) gs_graphics_storage_buffer_create(const gs_graphics_storage_buffer_desc_t* desc) { return gs_graphics()->api.storage_buffer_create(desc); } GS_API_DECL gs_handle(gs_graphics_framebuffer_t) gs_graphics_framebuffer_create(const gs_graphics_framebuffer_desc_t* desc) { return gs_graphics()->api.framebuffer_create(desc); } GS_API_DECL gs_handle(gs_graphics_renderpass_t) gs_graphics_renderpass_create(const gs_graphics_renderpass_desc_t* desc) { return gs_graphics()->api.renderpass_create(desc); } GS_API_DECL gs_handle(gs_graphics_pipeline_t) gs_graphics_pipeline_create(const gs_graphics_pipeline_desc_t* desc) { return gs_graphics()->api.pipeline_create(desc); } // Destroy GS_API_DECL void gs_graphics_texture_destroy(gs_handle(gs_graphics_texture_t) hndl) { gs_graphics()->api.texture_destroy(hndl); } GS_API_DECL void gs_graphics_uniform_destroy(gs_handle(gs_graphics_uniform_t) hndl) { gs_graphics()->api.uniform_destroy(hndl); } GS_API_DECL void gs_graphics_shader_destroy(gs_handle(gs_graphics_shader_t) hndl) { gs_graphics()->api.shader_destroy(hndl); } GS_API_DECL void gs_graphics_vertex_buffer_destroy(gs_handle(gs_graphics_vertex_buffer_t) hndl) { gs_graphics()->api.vertex_buffer_destroy(hndl); } GS_API_DECL void gs_graphics_index_buffer_destroy(gs_handle(gs_graphics_index_buffer_t) hndl) { gs_graphics()->api.index_buffer_destroy(hndl); } GS_API_DECL void gs_graphics_uniform_buffer_destroy(gs_handle(gs_graphics_uniform_buffer_t) hndl) { gs_graphics()->api.uniform_buffer_destroy(hndl); } GS_API_DECL void gs_graphics_storage_buffer_destroy(gs_handle(gs_graphics_storage_buffer_t) hndl) { gs_graphics()->api.storage_buffer_destroy(hndl); } GS_API_DECL void gs_graphics_framebuffer_destroy(gs_handle(gs_graphics_framebuffer_t) hndl) { gs_graphics()->api.framebuffer_destroy(hndl); } GS_API_DECL void gs_graphics_renderpass_destroy(gs_handle(gs_graphics_renderpass_t) hndl) { gs_graphics()->api.renderpass_destroy(hndl); } GS_API_DECL void gs_graphics_pipeline_destroy(gs_handle(gs_graphics_pipeline_t) hndl) { gs_graphics()->api.pipeline_destroy(hndl); } // Resource Updates (main thread only) GS_API_DECL void gs_graphics_vertex_buffer_update(gs_handle(gs_graphics_vertex_buffer_t) hndl, gs_graphics_vertex_buffer_desc_t* desc) { gs_graphics()->api.vertex_buffer_update(hndl, desc); } GS_API_DECL void gs_graphics_index_buffer_update(gs_handle(gs_graphics_index_buffer_t) hndl, gs_graphics_index_buffer_desc_t* desc) { gs_graphics()->api.index_buffer_update(hndl, desc); } GS_API_DECL void gs_graphics_storage_buffer_update(gs_handle(gs_graphics_storage_buffer_t) hndl, gs_graphics_storage_buffer_desc_t* desc) { gs_graphics()->api.storage_buffer_update(hndl, desc); } GS_API_DECL void gs_graphics_texture_update(gs_handle(gs_graphics_texture_t) hndl, gs_graphics_texture_desc_t* desc) { gs_graphics()->api.texture_update(hndl, desc); } GS_API_DECL void gs_graphics_texture_read(gs_handle(gs_graphics_texture_t) hndl, gs_graphics_texture_desc_t* desc) { return gs_graphics()->api.texture_read(hndl, desc); } GS_API_DECL void* gs_graphics_storage_buffer_map_get(gs_handle(gs_graphics_storage_buffer_t) hndl) { return gs_graphics()->api.storage_buffer_map_get(hndl); } GS_API_DECL void gs_graphics_storage_buffer_unlock(gs_handle(gs_graphics_storage_buffer_t) hndl) { return gs_graphics()->api.storage_buffer_unlock(hndl); } GS_API_DECL void* gs_graphics_storage_buffer_lock(gs_handle(gs_graphics_storage_buffer_t) hndl, size_t offset, size_t sz) { return gs_graphics()->api.storage_buffer_lock(hndl, offset, sz); } GS_API_DECL void gs_graphics_storage_buffer_get_data(gs_handle(gs_graphics_storage_buffer_t) hndl, size_t offset, size_t sz, void* out) { return gs_graphics()->api.storage_buffer_get_data(hndl, offset, sz, out); } /*============================= // GS_AUDIO =============================*/ #ifndef GS_AUDIO_IMPL_CUSTOM #define GS_AUDIO_IMPL_MINIAUDIO #endif #include "impl/gs_audio_impl.h" /*========================== // GS_OS ==========================*/ GS_API_DECL void* _gs_malloc_init_impl(size_t sz) { void* data = gs_malloc(sz); memset(data, 0, sz); return data; } GS_API_DECL gs_os_api_t gs_os_api_new_default() { gs_os_api_t os = gs_default_val(); os.malloc = malloc; os.malloc_init = _gs_malloc_init_impl; os.free = free; os.realloc = realloc; os.calloc = calloc; os.strdup = strdup; #ifdef GS_PLATFORM_WIN os.alloca = malloc; #else os.alloca = malloc; #endif return os; } /*======================== // gs_byte_buffer ========================*/ void gs_byte_buffer_init(gs_byte_buffer_t* buffer) { buffer->data = (uint8_t*)gs_malloc(GS_BYTE_BUFFER_DEFAULT_CAPCITY); buffer->capacity = GS_BYTE_BUFFER_DEFAULT_CAPCITY; buffer->size = 0; buffer->position = 0; } gs_byte_buffer_t gs_byte_buffer_new() { gs_byte_buffer_t buffer; gs_byte_buffer_init(&buffer); return buffer; } void gs_byte_buffer_free(gs_byte_buffer_t* buffer) { if (buffer && buffer->data) { gs_free(buffer->data); } } void gs_byte_buffer_clear(gs_byte_buffer_t* buffer) { buffer->size = 0; buffer->position = 0; } bool gs_byte_buffer_empty(gs_byte_buffer_t* buffer) { return (buffer->size == 0); } size_t gs_byte_buffer_size(gs_byte_buffer_t* buffer) { return buffer->size; } void gs_byte_buffer_resize(gs_byte_buffer_t* buffer, size_t sz) { if (!buffer) return; uint8_t* data = (uint8_t*)gs_realloc(buffer->data, sz); if (data == NULL) { gs_log_warning("gs_byte_buffer_resize: realloc failed for size %zu. Keeping old buffer.", sz); return; } buffer->data = data; buffer->capacity = (uint32_t)sz; } void gs_byte_buffer_copy_contents(gs_byte_buffer_t* dst, gs_byte_buffer_t* src) { gs_byte_buffer_seek_to_beg(dst); gs_byte_buffer_seek_to_beg(src); gs_byte_buffer_write_bulk(dst, src->data, src->size); } void gs_byte_buffer_seek_to_beg(gs_byte_buffer_t* buffer) { buffer->position = 0; } void gs_byte_buffer_seek_to_end(gs_byte_buffer_t* buffer) { buffer->position = buffer->size; } void gs_byte_buffer_advance_position(gs_byte_buffer_t* buffer, size_t sz) { buffer->position += (uint32_t)sz; } void gs_byte_buffer_write_bulk(gs_byte_buffer_t* buffer, void* src, size_t size) { if (!buffer || !src || !size) return; // Check for necessary resize size_t total_write_size = buffer->position + size; if (total_write_size >= (size_t)buffer->capacity) { size_t capacity = buffer->capacity * 2; while(capacity <= total_write_size) { capacity *= 2; } uint8_t* old_data = buffer->data; gs_byte_buffer_resize(buffer, capacity); if (buffer->data == NULL) { buffer->data = old_data; return; } } // Safety check before memcpy if (!buffer->data) return; // memcpy data memcpy((buffer->data + buffer->position), src, size); buffer->size += (uint32_t)size; buffer->position += (uint32_t)size; } void gs_byte_buffer_read_bulk(gs_byte_buffer_t* buffer, void** dst, size_t size) { memcpy(*dst, (buffer->data + buffer->position), size); buffer->position += (uint32_t)size; } void gs_byte_buffer_write_str(gs_byte_buffer_t* buffer, const char* str) { // Write size of string uint32_t str_len = gs_string_length(str); gs_byte_buffer_write(buffer, uint16_t, str_len); size_t i; for (i = 0; i < str_len; ++i) { gs_byte_buffer_write(buffer, uint8_t, str[i]); } } void gs_byte_buffer_read_str(gs_byte_buffer_t* buffer, char* str) { // Read in size of string from buffer uint16_t sz; gs_byte_buffer_read(buffer, uint16_t, &sz); uint32_t i; for (i = 0; i < sz; ++i) { gs_byte_buffer_read(buffer, uint8_t, &str[i]); } str[i] = '\0'; } gs_result gs_byte_buffer_write_to_file ( gs_byte_buffer_t* buffer, const char* output_path ) { return gs_platform_write_file_contents(output_path, "wb", buffer->data, buffer->size); } gs_result gs_byte_buffer_read_from_file ( gs_byte_buffer_t* buffer, const char* file_path ) { if (!buffer) return GS_RESULT_FAILURE; if (buffer->data) { gs_byte_buffer_free(buffer); } buffer->data = (u8*)gs_platform_read_file_contents(file_path, "rb", (size_t*)&buffer->size); if (!buffer->data) { gs_assert(false); return GS_RESULT_FAILURE; } buffer->position = 0; buffer->capacity = buffer->size; return GS_RESULT_SUCCESS; } GS_API_DECL void gs_byte_buffer_memset(gs_byte_buffer_t* buffer, uint8_t val) { memset(buffer->data, val, buffer->capacity); } /*======================== // Dynamic Array ========================*/ GS_API_DECL void* gs_dyn_array_resize_impl(void* arr, size_t sz, size_t amount) { size_t capacity; if (arr) { capacity = amount; } else { capacity = 0; } // Create new gs_dyn_array with just the header information gs_dyn_array* data = (gs_dyn_array*)gs_realloc(arr ? gs_dyn_array_head(arr) : 0, capacity * sz + sizeof(gs_dyn_array)); if (data) { if (!arr) { data->size = 0; } data->capacity = (int32_t)capacity; return ((int32_t*)data + 2); } return NULL; } GS_API_DECL void** gs_dyn_array_init(void** arr, size_t val_len) { if (*arr == NULL) { gs_dyn_array* data = (gs_dyn_array*)gs_malloc(val_len + sizeof(gs_dyn_array)); // Allocate capacity of one data->size = 0; data->capacity = 1; *arr = ((int32_t*)data + 2); } return arr; } GS_API_DECL void gs_dyn_array_push_data(void** arr, void* val, size_t val_len) { if (*arr == NULL) { gs_dyn_array_init(arr, val_len); } if (gs_dyn_array_need_grow(*arr, 1)) { int32_t capacity = gs_dyn_array_capacity(*arr) * 2; // Create new gs_dyn_array with just the header information gs_dyn_array* data = (gs_dyn_array*)gs_realloc(gs_dyn_array_head(*arr), capacity * val_len + sizeof(gs_dyn_array)); if (data) { data->capacity = capacity; *arr = ((int32_t*)data + 2); } } size_t offset = gs_dyn_array_size(*arr); memcpy(((uint8_t*)(*arr)) + offset * val_len, val, val_len); gs_dyn_array_head(*arr)->size++; } /*======================== // Hash Table ========================*/ GS_API_DECL void __gs_hash_table_init_impl(void** ht, size_t sz) { *ht = gs_malloc(sz); } /*======================== // Hash Set ========================*/ GS_API_DECL void __gs_hash_set_init_impl(void** hs, size_t sz) { *hs = gs_malloc(sz); } /*======================== // Slot Array ========================*/ GS_API_DECL void** gs_slot_array_init(void** sa, size_t sz) { if (*sa == NULL) { *sa = gs_malloc(sz); memset(*sa, 0, sz); return sa; } else { return NULL; } } /*======================== // Slot Map ========================*/ GS_API_DECL void** gs_slot_map_init(void** sm) { if (*sm == NULL) { (*sm) = gs_malloc(sizeof(size_t) * 2);\ memset((*sm), 0, sizeof(size_t) * 2);\ return sm; } return NULL; } /*======================== // Priotity Queue ========================*/ GS_API_DECL void** gs_pqueue_init(void** pq, size_t sz) { if (*pq == NULL) { (*pq) = gs_malloc(sz); memset((*pq), 0, sz); return pq; } return NULL; } GS_API_PRIVATE void __gs_pqueue_pop_internal(void** pqueue, void* tmp, void** data, int32_t* priority, int32_t pq_sz, size_t d_sz) { // TODO(): Remove these checks for perf if (!pqueue || !tmp || !data || !priority || !pq_sz || !d_sz) return; #define __SWP(__I0, __I1)\ do {\ {\ size_t i0 = d_sz * (__I0);\ size_t i1 = d_sz * (__I1);\ uint8_t** d = (uint8_t**)data;\ memcpy(tmp, ((char*)*(d)) + i0, d_sz);\ memcpy(((char*)(*d)) + i0, ((char*)(*d)) + i1, d_sz);\ memcpy(((char*)(*d)) + i1, tmp, d_sz);\ }\ {\ int32_t t = 0;\ uint32_t i0 = (__I0);\ uint32_t i1 = (__I1);\ uint32_t sz = sizeof(int32_t);\ memcpy(&t, ((char*)((priority)) + i0), sz);\ memcpy((char*)((priority) + i0), (char*)((priority) + i1), sz);\ memcpy((char*)((priority) + i1), &t, sz);\ }\ } while (0) // Swap elements internal __SWP(0, pq_sz - 1); // Work down list from top until priority is sorted // THIS PART IS FUCKED // return; int32_t i = 0; int32_t c = 0; int32_t nwsz = pq_sz - 1; // Right up until the last item we removed int32_t psz = gs_dyn_array_size(priority) - 1; // int32_t* pa = priority; for (int32_t i = 0; gs_pqueue_child_left_idx(i) < psz; i = c) { // Set child to smaller of two c = gs_pqueue_child_left_idx(i); // if (c >= psz) break; // Set to right child if valid and less priority if ((c + 1) < psz && priority[c + 1] < priority[c]) { c++; } // Check to swp, if necessary if (priority[i] > priority[c]) { __SWP(i, c); } // Otherwise, we're done else { break; } } // gs_println("P: %d", priority[nwsz - 1]); } /*======================== // GS_MEMORY ========================*/ // typedef struct gs_memory_block_t { // uint8_t* data; // size_t size; // } gs_memory_block_t; GS_API_DECL gs_memory_block_t gs_memory_block_new(size_t sz) { gs_memory_block_t mem = gs_default_val(); mem.data = (uint8_t*)gs_malloc(sz); memset(mem.data, 0, sz); mem.size = sz; return mem; } GS_API_DECL void gs_memory_block_free(gs_memory_block_t* mem) { gs_assert(mem); gs_assert(mem->data); gs_free(mem->data); mem->data = NULL; mem->size = 0; } // Modified from: https://github.com/mtrebi/memory-allocators/blob/master/includes/Utils.h GS_API_DECL size_t gs_memory_calc_padding(size_t base_address, size_t alignment) { size_t mult = (base_address / alignment) + 1; size_t aligned_addr = mult * alignment; size_t padding = aligned_addr - base_address; return padding; } GS_API_DECL size_t gs_memory_calc_padding_w_header(size_t base_address, size_t alignment, size_t header_sz) { size_t padding = gs_memory_calc_padding(base_address, alignment); size_t needed_space = header_sz; if (padding < needed_space) { needed_space -= padding; if (needed_space % alignment > 0) { padding += alignment * (1 + (needed_space / alignment)); } else { padding += alignment * (needed_space / alignment); } } return padding; } /*================================================================================ // Linear Allocator ================================================================================*/ // typedef struct gs_linear_allocator_t { // uint8_t* memory; // size_t total_size; // size_t offset; // } gs_linear_allocator_t; GS_API_DECL gs_linear_allocator_t gs_linear_allocator_new(size_t sz) { gs_linear_allocator_t la = gs_default_val(); la.memory = (uint8_t*)gs_malloc(sz); memset(la.memory, 0, sz); la.offset = 0; la.total_size = sz; return la; } GS_API_DECL void gs_linear_allocator_free(gs_linear_allocator_t* la) { gs_assert(la); gs_assert(la->memory); gs_free(la->memory); la->memory = NULL; } GS_API_DECL void* gs_linear_allocator_allocate(gs_linear_allocator_t* la, size_t sz, size_t alignment) { gs_assert(la); size_t padding = 0; size_t padding_address = 0; size_t cur_address = (size_t)la->memory + la->offset; // Calculate alignment required if (alignment != 0 && la->offset % alignment != 0) { padding = gs_memory_calc_padding(cur_address, alignment); } // Cannot allocate (not enough memory available) if (la->offset + padding + sz > la->total_size) { return NULL; } // Allocate and return pointer la->offset += padding; size_t next_address = cur_address + padding; la->offset += sz; return (void*)next_address; } GS_API_DECL void gs_linear_allocator_clear(gs_linear_allocator_t* la) { gs_assert(la); la->offset = 0; } /*================================================================================ // Stack Allocator ================================================================================*/ GS_API_DECL gs_stack_allocator_t gs_stack_allocator_new(size_t sz) { gs_stack_allocator_t alloc = gs_default_val(); alloc.memory = gs_memory_block_new(sz); return alloc; } GS_API_DECL void gs_stack_allocator_free(gs_stack_allocator_t* sa) { gs_stack_allocator_clear(sa); gs_memory_block_free(&sa->memory); } GS_API_DECL void* gs_stack_allocator_allocate(gs_stack_allocator_t* sa, size_t sz) { // Not enough memory available size_t total_size = sz + sizeof(gs_stack_allocator_header_t); if (total_size > (size_t)sa->memory.size - sa->offset) { return NULL; } // Create new entry and push size_t header_addr = (size_t)(sa->memory.data + sa->offset + sz); gs_stack_allocator_header_t* header = (gs_stack_allocator_header_t*)(sa->memory.data + sa->offset + sz); uint8_t* data = (uint8_t*)(sa->memory.data + sa->offset); header->size = (uint32_t)sz; // Add this to the memory size sa->offset += total_size; // Return data return data; } GS_API_DECL void* gs_stack_allocator_pop(gs_stack_allocator_t* sa) { // If no entries left, then cannot pop if (sa->offset == 0) { return NULL; } // Move current size back gs_stack_allocator_header_t* header = (gs_stack_allocator_header_t*)(sa->memory.data + sa->offset - sizeof(gs_stack_allocator_header_t)); void* data = (uint8_t*)(sa->memory.data + sa->offset - sizeof(gs_stack_allocator_header_t) - header->size); size_t total_sz = (size_t)header->size + sizeof(gs_stack_allocator_header_t); // Set offset back sa->offset -= total_sz; // Return data return (void*)data; } GS_API_DECL void* gs_stack_allocator_peek(gs_stack_allocator_t* sa) { if (sa->offset == 0) { return NULL; } gs_stack_allocator_header_t* header = (gs_stack_allocator_header_t*)(sa->memory.data + sa->offset - sizeof(gs_stack_allocator_header_t)); return (void*)(sa->memory.data + sa->offset - sizeof(gs_stack_allocator_header_t) - (size_t)header->size); } GS_API_DECL void gs_stack_allocator_clear(gs_stack_allocator_t* sa) { // Clear offset sa->offset = 0; } /*================================================================================ // Paged Allocator ================================================================================*/ GS_API_DECL gs_paged_allocator_t gs_paged_allocator_new(size_t block_size, size_t blocks_per_page) { gs_paged_allocator_t pa = gs_default_val(); pa.block_size = block_size; pa.blocks_per_page = blocks_per_page; pa.pages = NULL; pa.page_count = 0; pa.free_list = NULL; return pa; } GS_API_DECL void gs_paged_allocator_free(gs_paged_allocator_t* pa) { gs_paged_allocator_clear(pa); } GS_API_DECL void* gs_paged_allocator_allocate(gs_paged_allocator_t* pa) { if (pa->free_list) { gs_paged_allocator_block_t* data = pa->free_list; pa->free_list = data->next; return data; } else { gs_paged_allocator_page_t* page = (gs_paged_allocator_page_t*)_gs_malloc_init_impl(pa->block_size * pa->blocks_per_page + sizeof(gs_paged_allocator_page_t)); pa->page_count++; page->next = pa->pages; page->data = (gs_paged_allocator_block_t*)gs_ptr_add(page, sizeof(gs_paged_allocator_page_t)); pa->pages = page; // #define gs_ptr_add(P, BYTES) \ // (((uint8_t*)P + (BYTES))) uint32_t bppmo = pa->blocks_per_page - 1; for (uint32_t i = 0; i < bppmo; ++i) { gs_paged_allocator_block_t* node = (gs_paged_allocator_block_t*)gs_ptr_add(page->data, pa->block_size * i); gs_paged_allocator_block_t* next = (gs_paged_allocator_block_t*)gs_ptr_add(page->data, pa->block_size * (i + 1)); node->next = next; } gs_paged_allocator_block_t* last = (gs_paged_allocator_block_t*)gs_ptr_add(page->data, pa->block_size * bppmo); last->next = NULL; pa->free_list = page->data->next; return page->data; } } GS_API_DECL void gs_paged_allocator_deallocate(gs_paged_allocator_t* pa, void* data) { ((gs_paged_allocator_block_t*)data)->next = pa->free_list; pa->free_list = ((gs_paged_allocator_block_t*)data); } GS_API_DECL void gs_paged_allocator_clear(gs_paged_allocator_t* pa) { gs_paged_allocator_page_t* page = pa->pages; for (uint32_t i = 0; i < pa->page_count; ++i) { gs_paged_allocator_page_t* next = page->next; gs_free(page); page = next; } pa->free_list = NULL; pa->page_count = 0; } /*================================================================================ // Heap Allocator ================================================================================*/ // #ifndef GS_HEAP_ALLOC_DEFAULT_SIZE // #define GS_HEAP_ALLOC_DEFAULT_SIZE 1024 * 1024 * 20 // #endif // #ifndef GS_HEAP_ALLOC_DEFAULT_CAPCITY // #define GS_HEAP_ALLOC_DEFAULT_CAPCITY 1024 // #endif // typedef struct gs_heap_allocator_header_t { // struct gs_heap_allocator_header_t* next; // struct gs_heap_allocator_header_t* prev; // size_t size; // } gs_heap_allocator_header_t; // typedef struct gs_heap_allocator_free_block_t { // gs_heap_allocator_header_t* header; // size_t size; // } gs_heap_allocator_free_block_t; // typedef struct gs_heap_allocator_t { // gs_heap_allocator_header_t* memory; // gs_heap_allocator_free_block_t* free_blocks; // uint32_t free_block_count; // uint32_t free_block_capacity; // } gs_heap_allocator_t; GS_API_DECL gs_heap_allocator_t gs_heap_allocate_new() { gs_heap_allocator_t ha = gs_default_val(); ha.memory = (gs_heap_allocator_header_t*)_gs_malloc_init_impl(GS_HEAP_ALLOC_DEFAULT_SIZE); ha.memory->next = NULL; ha.memory->prev = NULL; ha.memory->size = GS_HEAP_ALLOC_DEFAULT_SIZE; ha.free_blocks = (gs_heap_allocator_free_block_t*)_gs_malloc_init_impl(sizeof(gs_heap_allocator_free_block_t) * GS_HEAP_ALLOC_DEFAULT_CAPCITY); ha.free_block_count = 1; ha.free_block_capacity = GS_HEAP_ALLOC_DEFAULT_CAPCITY; ha.free_blocks->header = ha.memory; ha.free_blocks->size = GS_HEAP_ALLOC_DEFAULT_SIZE; return ha; } GS_API_DECL void gs_heap_allocator_free(gs_heap_allocator_t* ha) { gs_free(ha->memory); gs_free(ha->free_blocks); ha->memory = NULL; ha->free_blocks = NULL; } GS_API_DECL void* gs_heap_allocator_allocate(gs_heap_allocator_t* ha, size_t sz) { size_t size_needed = sz + sizeof(gs_heap_allocator_header_t); gs_heap_allocator_free_block_t* first_fit = NULL; for (uint32_t i = 0; i < ha->free_block_count; ++i) { gs_heap_allocator_free_block_t* block = ha->free_blocks + i; if (block->size >= size_needed) { first_fit = block; break; } } if (!first_fit) { return NULL; } gs_heap_allocator_header_t* node = first_fit->header; gs_heap_allocator_header_t* new_node = (gs_heap_allocator_header_t*)gs_ptr_add(node, size_needed); node->size = size_needed; first_fit->size -= size_needed; first_fit->header = new_node; new_node->next = node->next; if (node->next) { node->next->prev = new_node; } node->next = new_node; new_node->prev = node; return gs_ptr_add(node, sizeof(gs_heap_allocator_header_t)); } GS_API_DECL void gs_heap_allocator_deallocate(gs_heap_allocator_t* ha, void* memory) { // Fill this out... } /*======================== // Util ========================*/ GS_API_DECL char* gs_util_string_concat(char* s1, const char* s2) { const size_t a = strlen(s1); const size_t b = strlen(s2); const size_t ab = a + b + 1; s1 = (char*)gs_realloc((void*)s1, ab); memcpy(s1 + a, s2, b + 1); return s1; } /*======================== // Random ========================*/ /* An implementation of the MT19937 Algorithm for the Mersenne Twister * by Evan Sultanik. Based upon the pseudocode in: M. Matsumoto and * T. Nishimura, "Mersenne Twister: A 623-dimensionally * equidistributed uniform pseudorandom number generator," ACM * Transactions on Modeling and Computer Simulation Vol. 8, No. 1, * January pp.3-30 1998. * * http://www.sultanik.com/Mersenne_twister */ #define GS_RAND_UPPER_MASK 0x80000000 #define GS_RAND_LOWER_MASK 0x7fffffff #define GS_RAND_TEMPERING_MASK_B 0x9d2c5680 #define GS_RAND_TEMPERING_MASK_C 0xefc60000 GS_API_PRIVATE void _gs_rand_seed_impl(gs_mt_rand_t* rand, uint64_t seed) { /* set initial seeds to mt[STATE_VECTOR_LENGTH] using the generator * from Line 25 of Table 1 in: Donald Knuth, "The Art of Computer * Programming," Vol. 2 (2nd Ed.) pp.102. */ rand->mt[0] = seed & 0xffffffff; for (rand->index = 1; rand->index < GS_STATE_VECTOR_LENGTH; rand->index++) { rand->mt[rand->index] = (6069 * rand->mt[rand->index-1]) & 0xffffffff; } } GS_API_DECL gs_mt_rand_t gs_rand_seed(uint64_t seed) { gs_mt_rand_t rand; _gs_rand_seed_impl(&rand, seed); return rand; } GS_API_DECL int64_t gs_rand_gen_long(gs_mt_rand_t* rand) { uint64_t y; static uint64_t mag[2] = {0x0, 0x9908b0df}; /* mag[x] = x * 0x9908b0df for x = 0,1 */ if(rand->index >= GS_STATE_VECTOR_LENGTH || rand->index < 0) { /* generate GS_STATE_VECTOR_LENGTH words at a time */ int kk; if(rand->index >= GS_STATE_VECTOR_LENGTH+1 || rand->index < 0) { _gs_rand_seed_impl(rand, 4357); } for(kk=0; kkmt[kk] & GS_RAND_UPPER_MASK) | (rand->mt[kk+1] & GS_RAND_LOWER_MASK); rand->mt[kk] = rand->mt[kk+GS_STATE_VECTOR_M] ^ (y >> 1) ^ mag[y & 0x1]; } for(; kkmt[kk] & GS_RAND_UPPER_MASK) | (rand->mt[kk+1] & GS_RAND_LOWER_MASK); rand->mt[kk] = rand->mt[kk+(GS_STATE_VECTOR_M-GS_STATE_VECTOR_LENGTH)] ^ (y >> 1) ^ mag[y & 0x1]; } y = (rand->mt[GS_STATE_VECTOR_LENGTH-1] & GS_RAND_UPPER_MASK) | (rand->mt[0] & GS_RAND_LOWER_MASK); rand->mt[GS_STATE_VECTOR_LENGTH-1] = rand->mt[GS_STATE_VECTOR_M-1] ^ (y >> 1) ^ mag[y & 0x1]; rand->index = 0; } y = rand->mt[rand->index++]; y ^= (y >> 11); y ^= (y << 7) & GS_RAND_TEMPERING_MASK_B; y ^= (y << 15) & GS_RAND_TEMPERING_MASK_C; y ^= (y >> 18); return y; } GS_API_DECL double gs_rand_gen(gs_mt_rand_t* rand) { return((double)gs_rand_gen_long(rand) / (uint64_t)0xffffffff); } GS_API_DECL int64_t gs_rand_gen_range_long(gs_mt_rand_t* rand, int32_t min, int32_t max) { return (int64_t)(floorf(gs_rand_gen_range(rand, (double)min, (double)max))); } GS_API_DECL double gs_rand_gen_range(gs_mt_rand_t* rand, double min, double max) { return gs_map_range(0.0, 1.0, min, max, gs_rand_gen(rand)); } GS_API_DECL gs_color_t gs_rand_gen_color(gs_mt_rand_t* rand) { gs_color_t c = gs_default_val(); c.r = (uint8_t)gs_rand_gen_range_long(rand, 0, 255); c.g = (uint8_t)gs_rand_gen_range_long(rand, 0, 255); c.b = (uint8_t)gs_rand_gen_range_long(rand, 0, 255); c.a = (uint8_t)gs_rand_gen_range_long(rand, 0, 255); return c; } /*================================================================================ // Coroutine ================================================================================*/ // Light wrapper around Minicoro #define MINICORO_IMPL #include "external/minicoro/minicoro.h" /*================================================================================ // Scheduler ================================================================================*/ #define SCHED_IMPLEMENTATION #include "external/sched/sched.h" /* --------------------------------------------------------------- * ATOMIC * ---------------------------------------------------------------*/ #if defined(_WIN32) && !(defined(__MINGW32__) || defined(__MINGW64__)) #include void _ReadWriteBarrier(); #pragma intrinsic(_ReadWriteBarrier) #pragma intrinsic(_InterlockedCompareExchange) #pragma intrinsic(_InterlockedExchangeAdd) #endif GS_API_DECL uint32_t gs_atomic_cmp_swp(volatile uint32_t *dst, uint32_t swap, uint32_t cmp) { /* Atomically performs: if (*dst == swapTp){ *dst = swapTo;} * return old *dst (so if sucessfull return cmp) */ #if defined(_WIN32) && !(defined(__MINGW32__) || defined(__MINGW64__)) /* assumes two's complement - unsigned /signed conversion leads to same bit pattern */ return _InterlockedCompareExchange((volatile long*)dst, swap, cmp); #else return __sync_val_compare_and_swap(dst, cmp, swap); #endif } GS_API_DECL gs_atomic_int_t gs_atomic_add(volatile gs_atomic_int_t *dst, int32_t value) { /* Atomically performs: tmp = *dst: *dst += value; return tmp; */ #if defined(_WIN32) && !(defined(__MINGW32__) || defined(__MINGW64__)) return _InterlockedExchangeAdd((long*)dst, value); #else return (sched_int)__sync_add_and_fetch(dst, value); #endif } /*================================================================================ // Noise ================================================================================*/ #define SG_NOISE_IMPL #include "external/sg_noise/sg_noise.h" // Perlin noise GS_API_DECL float gs_perlin1(float x) { return sg_noise1(x); } GS_API_DECL float gs_perlin2(float x, float y) { return sg_noise2(x, y); } GS_API_DECL float gs_perlin3(float x, float y, float z) { return sg_noise3(x, y, z); } GS_API_DECL float gs_perlin4(float x, float y, float z, float w) { return sg_noise4(x, y, z, w); } // Perlin periodic noise GS_API_DECL float gs_perlin1p(float x, int32_t px) { return sg_pnoise1(x, px); } GS_API_DECL float gs_perlin2p(float x, float y, int32_t px, int32_t py) { return sg_pnoise2(x, y, px, py); } GS_API_DECL float gs_perlin3p(float x, float y, float z, int32_t px, int32_t py, int32_t pz) { return sg_pnoise3(x, y, z, px, py, pz); } GS_API_DECL float gs_perlin4p(float x, float y, float z, float w, int32_t px, int32_t py, int32_t pz, int32_t pw) { return sg_pnoise4(x, y, z, w, px, py, pz, pw); } /*============================= // Camera =============================*/ GS_API_DECL gs_camera_t gs_camera_default() { // Construct default camera parameters gs_camera_t cam = gs_default_val(); cam.transform = gs_vqs_default(); cam.transform.position.z = 1.f; cam.fov = 60.f; cam.near_plane = 0.1f; cam.far_plane = 1000.f; cam.ortho_scale = 1.f; cam.proj_type = GS_PROJECTION_TYPE_ORTHOGRAPHIC; return cam; } GS_API_DECL gs_camera_t gs_camera_perspective() { gs_camera_t cam = gs_camera_default(); cam.proj_type = GS_PROJECTION_TYPE_PERSPECTIVE; cam.transform.position.z = 1.f; return cam; } GS_API_DECL gs_vec3 gs_camera_forward(const gs_camera_t* cam) { return (gs_quat_rotate(cam->transform.rotation, gs_v3(0.0f, 0.0f, -1.0f))); } GS_API_DECL gs_vec3 gs_camera_backward(const gs_camera_t* cam) { return (gs_quat_rotate(cam->transform.rotation, gs_v3(0.0f, 0.0f, 1.0f))); } GS_API_DECL gs_vec3 gs_camera_up(const gs_camera_t* cam) { return (gs_quat_rotate(cam->transform.rotation, gs_v3(0.0f, 1.0f, 0.0f))); } GS_API_DECL gs_vec3 gs_camera_down(const gs_camera_t* cam) { return (gs_quat_rotate(cam->transform.rotation, gs_v3(0.0f, -1.0f, 0.0f))); } GS_API_DECL gs_vec3 gs_camera_right(const gs_camera_t* cam) { return (gs_quat_rotate(cam->transform.rotation, gs_v3(1.0f, 0.0f, 0.0f))); } GS_API_DECL gs_vec3 gs_camera_left(const gs_camera_t* cam) { return (gs_quat_rotate(cam->transform.rotation, gs_v3(-1.0f, 0.0f, 0.0f))); } GS_API_DECL gs_vec3 gs_camera_world_to_screen(const gs_camera_t* cam, gs_vec3 coords, int32_t view_width, int32_t view_height) { // Transform world coords to screen coords to place billboarded UI elements in world gs_mat4 vp = gs_camera_get_view_projection(cam, view_width, view_height); gs_vec4 p4 = gs_v4(coords.x, coords.y, coords.z, 1.f); p4 = gs_mat4_mul_vec4(vp, p4); p4.x /= p4.w; p4.y /= p4.w; p4.z /= p4.w; // Bring into ndc p4.x = p4.x * 0.5f + 0.5f; p4.y = p4.y * 0.5f + 0.5f; // Bring into screen space p4.x = p4.x * (float)view_width; p4.y = gs_map_range(1.f, 0.f, 0.f, 1.f, p4.y) * (float)view_height; return gs_v3(p4.x, p4.y, p4.z); } GS_API_DECL gs_vec3 gs_camera_screen_to_world(const gs_camera_t* cam, gs_vec3 coords, s32 view_x, s32 view_y, s32 view_width, s32 view_height) { gs_vec3 wc = gs_default_val(); // Get inverse of view projection from camera gs_mat4 inverse_vp = gs_mat4_inverse(gs_camera_get_view_projection(cam, view_width, view_height)); f32 w_x = (f32)coords.x - (f32)view_x; f32 w_y = (f32)coords.y - (f32)view_y; f32 w_z = (f32)coords.z; // Transform from ndc gs_vec4 in; in.x = (w_x / (f32)view_width) * 2.f - 1.f; in.y = 1.f - (w_y / (f32)view_height) * 2.f; in.z = 2.f * w_z - 1.f; in.w = 1.f; // To world coords gs_vec4 out = gs_mat4_mul_vec4(inverse_vp, in); if (out.w == 0.f) { // Avoid div by zero return wc; } out.w = fabsf(out.w) > GS_EPSILON ? 1.f / out.w : 0.0001f; wc = gs_v3( out.x * out.w, out.y * out.w, out.z * out.w ); return wc; } GS_API_DECL gs_mat4 gs_camera_get_view_projection(const gs_camera_t* cam, s32 view_width, s32 view_height) { gs_mat4 view = gs_camera_get_view(cam); gs_mat4 proj = gs_camera_get_proj(cam, view_width, view_height); return gs_mat4_mul(proj, view); } GS_API_DECL gs_mat4 gs_camera_get_view(const gs_camera_t* cam) { gs_vec3 up = gs_camera_up(cam); gs_vec3 forward = gs_camera_forward(cam); gs_vec3 target = gs_vec3_add(forward, cam->transform.position); return gs_mat4_look_at(cam->transform.position, target, up); } GS_API_DECL gs_mat4 gs_camera_get_proj(const gs_camera_t* cam, s32 view_width, s32 view_height) { gs_mat4 proj_mat = gs_mat4_identity(); switch(cam->proj_type) { case GS_PROJECTION_TYPE_PERSPECTIVE: { proj_mat = gs_mat4_perspective(cam->fov, (f32)view_width / (f32)view_height, cam->near_plane, cam->far_plane); } break; // Don't like this... case GS_PROJECTION_TYPE_ORTHOGRAPHIC: { f32 _ar = (f32)view_width / (f32)view_height; f32 distance = 0.5f * (cam->far_plane - cam->near_plane); const f32 ortho_scale = cam->ortho_scale; const f32 aspect_ratio = _ar; proj_mat = gs_mat4_ortho ( -ortho_scale * aspect_ratio, ortho_scale * aspect_ratio, -ortho_scale, ortho_scale, -distance, distance ); } break; } return proj_mat; } GS_API_DECL void gs_camera_offset_orientation(gs_camera_t* cam, f32 yaw, f32 pitch) { gs_quat x = gs_quat_angle_axis(gs_deg2rad(yaw), gs_v3(0.f, 1.f, 0.f)); // Absolute up gs_quat y = gs_quat_angle_axis(gs_deg2rad(pitch), gs_camera_right(cam)); // Relative right cam->transform.rotation = gs_quat_mul(gs_quat_mul(x, y), cam->transform.rotation); } /*============================= // GS_UTIL =============================*/ #define STBI_MALLOC(sz) gs_malloc(sz) #define STBI_REALLOC(p,newsz) gs_realloc(p,newsz) #define STBI_FREE(p) gs_free(p) #ifndef GS_NO_STB_RECT_PACK #define STB_RECT_PACK_IMPLEMENTATION #endif #ifndef GS_NO_STB_TRUETYPE // #define STBTT_RASTERIZER_VERSION 0 #define STB_TRUETYPE_IMPLEMENTATION #endif #ifndef GS_NO_STB_DEFINE #define STB_DEFINE #endif #ifndef GS_NO_STB_IMAGE #define STB_IMAGE_IMPLEMENTATION // #define STB_IMAGE_WRITE_IMPLEMENTATION #endif // #ifndef GS_NO_STB_DS_DEFINE // #define STB_DS_IMPLEMENTATION // #endif #ifndef GS_NO_CGLTF #define CGLTF_IMPLEMENTATION #endif // STB #ifdef GS_STB_INCLUDE #include "external/stb/stb.h" #endif #include "external/stb/stb_rect_pack.h" #include "external/stb/stb_truetype.h" #include "external/stb/stb_image.h" // #include "external/stb/stb_ds.h" // CGLTF #include "external/cgltf/cgltf.h" GS_API_DECL char* gs_read_file_contents_into_string_null_term ( const char* file_path, const char* mode, size_t* _sz ) { char* buffer = 0; FILE* fp = fopen(file_path, mode); size_t sz = 0; if (fp) { fseek(fp, 0, SEEK_END); sz = ftell(fp); fseek(fp, 0, SEEK_SET); buffer = (char*)gs_malloc(sz + 1); if (buffer) { fread(buffer, 1, sz, fp); } fclose(fp); buffer[sz] = '\0'; if (_sz) *_sz = sz; } return buffer; } bool32_t gs_util_load_texture_data_from_file(const char* file_path, int32_t* width, int32_t* height, uint32_t* num_comps, void** data, bool32_t flip_vertically_on_load) { size_t len = 0; char* file_data = gs_platform_read_file_contents(file_path, "rb", &len); gs_assert(file_data); bool32_t ret = gs_util_load_texture_data_from_memory(file_data, len, width, height, num_comps, data, flip_vertically_on_load); if (!ret) { gs_println("Warning: could not load texture: %s", file_path); } gs_free(file_data); return ret; } GS_API_DECL bool32_t gs_util_load_texture_data_from_memory(const void* memory, size_t sz, int32_t* width, int32_t* height, uint32_t* num_comps, void** data, bool32_t flip_vertically_on_load) { // Load texture data stbi_set_flip_vertically_on_load(flip_vertically_on_load); *data = stbi_load_from_memory((const stbi_uc*)memory, (int32_t)sz, (int32_t*)width, (int32_t*)height, (int32_t*)num_comps, STBI_rgb_alpha); if (!*data) { gs_free(*data); return false; } return true; } /*========================== // GS_ASSET_TYPES ==========================*/ GS_API_DECL bool gs_asset_texture_load_from_file(const char* path, void* out, gs_graphics_texture_desc_t* desc, bool32_t flip_on_load, bool32_t keep_data) { gs_asset_texture_t* t = (gs_asset_texture_t*)out; memset(&t->desc, 0, sizeof(gs_graphics_texture_desc_t)); if (desc) { t->desc = *desc; } else { t->desc.format = GS_GRAPHICS_TEXTURE_FORMAT_RGBA8; t->desc.min_filter = GS_GRAPHICS_TEXTURE_FILTER_LINEAR; t->desc.mag_filter = GS_GRAPHICS_TEXTURE_FILTER_LINEAR; t->desc.wrap_s = GS_GRAPHICS_TEXTURE_WRAP_REPEAT; t->desc.wrap_t = GS_GRAPHICS_TEXTURE_WRAP_REPEAT; } // Load texture data FILE* f = fopen(path, "rb"); if (!f) { return false; } int32_t comp = 0; stbi_set_flip_vertically_on_load(t->desc.flip_y); *t->desc.data = (uint8_t*)stbi_load_from_file(f, (int32_t*)&t->desc.width, (int32_t*)&t->desc.height, (int32_t*)&comp, STBI_rgb_alpha); if (!*t->desc.data) { fclose(f); return false; } t->hndl = gs_graphics_texture_create(&t->desc); if (!keep_data) { gs_free(*t->desc.data); *t->desc.data = NULL; } fclose(f); return true; } /* bool gs_asset_texture_load_from_file(const char* path, void* out, gs_graphics_texture_desc_t* desc, bool32_t flip_on_load, bool32_t keep_data) { size_t len = 0; char* file_data = gs_platform_read_file_contents(path, "rb", &len); gs_assert(file_data); bool32_t ret = gs_asset_texture_load_from_memory(file_data, len, out, desc, flip_on_load, keep_data); gs_free(file_data); return ret; } */ bool gs_asset_texture_load_from_memory(const void* memory, size_t sz, void* out, gs_graphics_texture_desc_t* desc, bool32_t flip_on_load, bool32_t keep_data) { gs_asset_texture_t* t = (gs_asset_texture_t*)out; memset(&t->desc, 0, sizeof(gs_graphics_texture_desc_t)); if (desc) { t->desc = *desc; } else { t->desc.format = GS_GRAPHICS_TEXTURE_FORMAT_RGBA8; t->desc.min_filter = GS_GRAPHICS_TEXTURE_FILTER_LINEAR; t->desc.mag_filter = GS_GRAPHICS_TEXTURE_FILTER_LINEAR; t->desc.wrap_s = GS_GRAPHICS_TEXTURE_WRAP_REPEAT; t->desc.wrap_t = GS_GRAPHICS_TEXTURE_WRAP_REPEAT; } // Load texture data int32_t num_comps = 0; bool32_t loaded = gs_util_load_texture_data_from_memory(memory, sz, (int32_t*)&t->desc.width, (int32_t*)&t->desc.height, (uint32_t*)&num_comps, t->desc.data, t->desc.flip_y); if (!loaded) { return false; } t->hndl = gs_graphics_texture_create(&t->desc); if (!keep_data) { gs_free(*t->desc.data); *t->desc.data = NULL; } return true; } bool gs_asset_font_load_from_file(const char* path, void* out, uint32_t point_size) { size_t len = 0; char* ttf = gs_platform_read_file_contents(path, "rb", &len); if (!point_size) { gs_println("Warning: Font: %s: Point size not declared. Setting to default 16.", path); point_size = 16; } bool ret = gs_asset_font_load_from_memory(ttf, len, out, point_size); if (!ret) { gs_println("Font Failed to Load: %s", path); } else { gs_println("Font Successfully Loaded: %s", path); } gs_free(ttf); return ret; } bool gs_asset_font_load_from_memory(const void* memory, size_t sz, void* out, uint32_t point_size) { gs_asset_font_t* f = (gs_asset_font_t*)out; if (!point_size) { gs_println("Warning: Font: Point size not declared. Setting to default 16."); point_size = 16; } // Poor attempt at an auto resized texture const uint32_t point_wh = gs_max(point_size, 32); const uint32_t w = (point_wh/32 * 512) + (point_wh/32 * 512) % 512; const uint32_t h = (point_wh/32 * 512) + (point_wh/32 * 512) % 512; const uint32_t num_comps = 4; u8* alpha_bitmap = (uint8_t*)gs_malloc(w * h); u8* flipmap = (uint8_t*)gs_malloc(w * h * num_comps); memset(alpha_bitmap, 0, w * h); memset(flipmap, 0, w * h * num_comps); s32 v = stbtt_BakeFontBitmap((u8*)memory, 0, (float)point_size, alpha_bitmap, w, h, 32, 96, (stbtt_bakedchar*)f->glyphs); // no guarantee this fits! // Flip texture u32 r = h - 1; for (u32 i = 0; i < h; ++i) { for (u32 j = 0; j < w; ++j) { u32 i0 = i * w + j; u32 i1 = i * w * num_comps + j * num_comps; u8 a = alpha_bitmap[i0]; flipmap[i1 + 0] = 255; flipmap[i1 + 1] = 255; flipmap[i1 + 2] = 255; flipmap[i1 + 3] = a; } r--; } gs_graphics_texture_desc_t desc = gs_default_val(); desc.width = w; desc.height = h; *desc.data = flipmap; desc.format = GS_GRAPHICS_TEXTURE_FORMAT_RGBA8; desc.min_filter = GS_GRAPHICS_TEXTURE_FILTER_NEAREST; desc.mag_filter = GS_GRAPHICS_TEXTURE_FILTER_NEAREST; // Generate atlas texture for bitmap with bitmap data f->texture.hndl = gs_graphics_texture_create(&desc); f->texture.desc = desc; *f->texture.desc.data = NULL; bool success = false; if (v <= 0) { gs_println("Font Failed to Load, Baked Texture Was Too Small: %d", v); } else { gs_println("Font Successfully Loaded: %d", v); success = true; } gs_free(alpha_bitmap); gs_free(flipmap); return success; } GS_API_DECL float gs_asset_font_max_height(const gs_asset_font_t* fp) { if (!fp) return 0.f; float h = 0.f, x = 0.f, y = 0.f; const char* txt = "1l`'f()ABCDEFGHIJKLMNOjPQqSTU!"; while (txt[0] != '\0') { char c = txt[0]; if (c >= 32 && c <= 127) { stbtt_aligned_quad q = gs_default_val(); stbtt_GetBakedQuad((stbtt_bakedchar*)fp->glyphs, fp->texture.desc.width, fp->texture.desc.height, c - 32, &x, &y, &q, 1); h = gs_max(gs_max(h, fabsf(q.y0)), fabsf(q.y1)); } txt++; }; return h; } GS_API_DECL gs_vec2 gs_asset_font_text_dimensions(const gs_asset_font_t* fp, const char* text, int32_t len) { return gs_asset_font_text_dimensions_ex(fp, text, len, 0); } GS_API_DECL gs_vec2 gs_asset_font_text_dimensions_ex(const gs_asset_font_t* fp, const char* text, int32_t len, bool32_t include_past_baseline) { gs_vec2 dimensions = gs_v2s(0.f); if (!fp || !text) return dimensions; float x = 0.f; float y = 0.f; float y_under = 0; while (text[0] != '\0' && len--) { char c = text[0]; if (c >= 32 && c <= 127) { stbtt_aligned_quad q = gs_default_val(); stbtt_GetBakedQuad((stbtt_bakedchar*)fp->glyphs, fp->texture.desc.width, fp->texture.desc.height, c - 32, &x, &y, &q, 1); dimensions.x = gs_max(dimensions.x, x); dimensions.y = gs_max(dimensions.y, fabsf(q.y0)); if (include_past_baseline) y_under = gs_max(y_under, fabsf(q.y1)); } text++; }; if (include_past_baseline) dimensions.y += y_under; return dimensions; } // Audio bool gs_asset_audio_load_from_file(const char* path, void* out) { gs_asset_audio_t* a = (gs_asset_audio_t*)out; a->hndl = gs_audio_load_from_file(path); return gs_handle_is_valid(a->hndl); } bool gs_util_load_gltf_data_from_file(const char* path, gs_asset_mesh_decl_t* decl, gs_asset_mesh_raw_data_t** out, uint32_t* mesh_count) { // Use cgltf like a boss cgltf_options options = gs_default_val(); size_t len = 0; char* file_data = gs_platform_read_file_contents(path, "rb", &len); gs_println("Loading GLTF: %s", path); cgltf_data* data = NULL; cgltf_result result = cgltf_parse(&options, file_data, (cgltf_size)len, &data); gs_free(file_data); if (result != cgltf_result_success) { gs_println("Mesh:LoadFromFile:Failed load gltf"); cgltf_free(data); return false; } // Load buffers as well result = cgltf_load_buffers(&options, data, path); if (result != cgltf_result_success) { cgltf_free(data); gs_println("Mesh:LoadFromFile:Failed to load buffers"); return false; } // Type of index data size_t index_element_size = decl ? decl->index_buffer_element_size : 0; // Temporary structures gs_dyn_array(gs_vec3) positions = NULL; gs_dyn_array(gs_vec3) normals = NULL; gs_dyn_array(gs_vec3) tangents = NULL; gs_dyn_array(gs_color_t) colors = NULL; gs_dyn_array(gs_vec2) uvs = NULL; gs_dyn_array(gs_asset_mesh_layout_t) layouts = NULL; gs_byte_buffer_t v_data = gs_byte_buffer_new(); gs_byte_buffer_t i_data = gs_byte_buffer_new(); // Allocate memory for buffers *mesh_count = data->meshes_count; *out = (gs_asset_mesh_raw_data_t*)gs_malloc(data->meshes_count * sizeof(gs_asset_mesh_raw_data_t)); memset(*out, 0, sizeof(gs_asset_mesh_raw_data_t) * data->meshes_count); // Iterate through meshes in data for (uint32_t i = 0; i < data->meshes_count; ++i) { // Initialize mesh data gs_asset_mesh_raw_data_t* mesh = out[i]; mesh->prim_count = data->meshes[i].primitives_count; mesh->vertex_sizes = (size_t*)gs_malloc(sizeof(size_t) * mesh->prim_count); mesh->index_sizes = (size_t*)gs_malloc(sizeof(size_t) * mesh->prim_count); mesh->vertices = (void**)gs_malloc(sizeof(size_t) * mesh->prim_count); mesh->indices = (void**)gs_malloc(sizeof(size_t) * mesh->prim_count); // For each primitive in mesh for (uint32_t p = 0; p < data->meshes[i].primitives_count; ++p) { // Clear temp data from previous use gs_dyn_array_clear(positions); gs_dyn_array_clear(normals); gs_dyn_array_clear(tangents); gs_dyn_array_clear(uvs); gs_dyn_array_clear(colors); gs_dyn_array_clear(layouts); gs_byte_buffer_clear(&v_data); gs_byte_buffer_clear(&i_data); #define __GLTF_PUSH_ATTR(ATTR, TYPE, COUNT, ARR, ARR_TYPE, LAYOUTS, LAYOUT_TYPE)\ do {\ int32_t N = 0;\ TYPE* BUF = (TYPE*)ATTR->buffer_view->buffer->data + ATTR->buffer_view->offset/sizeof(TYPE) + ATTR->offset/sizeof(TYPE);\ gs_assert(BUF);\ TYPE V[COUNT] = gs_default_val();\ /* For each vertex */\ for (uint32_t k = 0; k < ATTR->count; k++)\ {\ /* For each element */\ for (int l = 0; l < COUNT; l++) {\ V[l] = BUF[N + l];\ }\ N += (int32_t)(ATTR->stride/sizeof(TYPE));\ /* Add to temp data array */\ ARR_TYPE ELEM = gs_default_val();\ memcpy((void*)&ELEM, (void*)V, sizeof(ARR_TYPE));\ gs_dyn_array_push(ARR, ELEM);\ }\ /* Push into layout */\ gs_asset_mesh_layout_t LAYOUT = gs_default_val();\ LAYOUT.type = LAYOUT_TYPE;\ gs_dyn_array_push(LAYOUTS, LAYOUT);\ } while (0) // For each attribute in primitive for (uint32_t a = 0; a < data->meshes[i].primitives[p].attributes_count; ++a) { // Accessor for attribute data cgltf_accessor* attr = data->meshes[i].primitives[p].attributes[a].data; // Switch on type for reading data switch (data->meshes[i].primitives[p].attributes[a].type) { case cgltf_attribute_type_position: { __GLTF_PUSH_ATTR(attr, float, 3, positions, gs_vec3, layouts, GS_ASSET_MESH_ATTRIBUTE_TYPE_POSITION); } break; case cgltf_attribute_type_normal: { __GLTF_PUSH_ATTR(attr, float, 3, normals, gs_vec3, layouts, GS_ASSET_MESH_ATTRIBUTE_TYPE_NORMAL); } break; case cgltf_attribute_type_tangent: { __GLTF_PUSH_ATTR(attr, float, 3, tangents, gs_vec3, layouts, GS_ASSET_MESH_ATTRIBUTE_TYPE_TANGENT); } break; case cgltf_attribute_type_texcoord: { __GLTF_PUSH_ATTR(attr, float, 2, uvs, gs_vec2, layouts, GS_ASSET_MESH_ATTRIBUTE_TYPE_TEXCOORD); } break; case cgltf_attribute_type_color: { __GLTF_PUSH_ATTR(attr, uint8_t, 4, colors, gs_color_t, layouts, GS_ASSET_MESH_ATTRIBUTE_TYPE_COLOR); } break; // Not sure what to do with these for now case cgltf_attribute_type_joints: { // Push into layout gs_asset_mesh_layout_t layout = gs_default_val(); layout.type = GS_ASSET_MESH_ATTRIBUTE_TYPE_JOINT; gs_dyn_array_push(layouts, layout); } break; case cgltf_attribute_type_weights: { // Push into layout gs_asset_mesh_layout_t layout = gs_default_val(); layout.type = GS_ASSET_MESH_ATTRIBUTE_TYPE_WEIGHT; gs_dyn_array_push(layouts, layout); } break; // Shouldn't hit here... default: { } break; } } // Indices for primitive cgltf_accessor* acc = data->meshes[i].primitives[p].indices; #define __GLTF_PUSH_IDX(BB, ACC, TYPE)\ do {\ int32_t n = 0;\ TYPE* buf = (TYPE*)acc->buffer_view->buffer->data + acc->buffer_view->offset/sizeof(TYPE) + acc->offset/sizeof(TYPE);\ gs_assert(buf);\ TYPE v = 0;\ /* For each index */\ for (uint32_t k = 0; k < acc->count; k++) {\ /* For each element */\ for (int l = 0; l < 1; l++) {\ v = buf[n + l];\ }\ n += (int32_t)(acc->stride/sizeof(TYPE));\ /* Add to temp positions array */\ switch (index_element_size) {\ case 0: gs_byte_buffer_write(BB, uint16_t, (uint16_t)v); break;\ case 2: gs_byte_buffer_write(BB, uint16_t, (uint16_t)v); break;\ case 4: gs_byte_buffer_write(BB, uint32_t, (uint32_t)v); break;\ }\ }\ } while (0) // If indices are available if (acc) { switch (acc->component_type) { case cgltf_component_type_r_8: __GLTF_PUSH_IDX(&i_data, acc, int8_t); break; case cgltf_component_type_r_8u: __GLTF_PUSH_IDX(&i_data, acc, uint8_t); break; case cgltf_component_type_r_16: __GLTF_PUSH_IDX(&i_data, acc, int16_t); break; case cgltf_component_type_r_16u: __GLTF_PUSH_IDX(&i_data, acc, uint16_t); break; case cgltf_component_type_r_32u: __GLTF_PUSH_IDX(&i_data, acc, uint32_t); break; case cgltf_component_type_r_32f: __GLTF_PUSH_IDX(&i_data, acc, float); break; // Shouldn't hit here default: { } break; } } else { // Iterate over positions size, then just push back indices for (uint32_t i = 0; i < gs_dyn_array_size(positions); ++i) { switch (index_element_size) { default: case 0: gs_byte_buffer_write(&i_data, uint16_t, (uint16_t)i); break; case 2: gs_byte_buffer_write(&i_data, uint16_t, (uint16_t)i); break; case 4: gs_byte_buffer_write(&i_data, uint32_t, (uint32_t)i); break; } } } bool warnings[gs_enum_count(gs_asset_mesh_attribute_type)] = gs_default_val(); // Grab mesh layout pointer to use gs_asset_mesh_layout_t* layoutp = decl ? decl->layout : layouts; uint32_t layout_ct = decl ? decl->layout_size / sizeof(gs_asset_mesh_layout_t) : gs_dyn_array_size(layouts); // Iterate layout to fill data buffers according to provided layout { uint32_t vct = 0; vct = gs_max(vct, gs_dyn_array_size(positions)); vct = gs_max(vct, gs_dyn_array_size(colors)); vct = gs_max(vct, gs_dyn_array_size(uvs)); vct = gs_max(vct, gs_dyn_array_size(normals)); vct = gs_max(vct, gs_dyn_array_size(tangents)); #define __GS_GLTF_WRITE_DATA(IT, VDATA, ARR, ARR_TYPE, ARR_DEF_VAL, LAYOUT_TYPE)\ do {\ /* Grab data at index, if available */\ if (IT < gs_dyn_array_size(ARR)) {\ gs_byte_buffer_write(&(VDATA), ARR_TYPE, ARR[IT]);\ }\ else {\ /* Write default value and give warning.*/\ gs_byte_buffer_write(&(VDATA), ARR_TYPE, ARR_DEF_VAL);\ if (!warnings[LAYOUT_TYPE]) {\ gs_println("Warning:Mesh:LoadFromFile:%s:Index out of range.", #LAYOUT_TYPE);\ warnings[LAYOUT_TYPE] = true;\ }\ }\ } while (0) for (uint32_t it = 0; it < vct; ++it) { // For each attribute in layout for (uint32_t l = 0; l < layout_ct; ++l) { switch (layoutp[l].type) { case GS_ASSET_MESH_ATTRIBUTE_TYPE_POSITION: { __GS_GLTF_WRITE_DATA(it, v_data, positions, gs_vec3, gs_v3(0.f, 0.f, 0.f), GS_ASSET_MESH_ATTRIBUTE_TYPE_POSITION); } break; case GS_ASSET_MESH_ATTRIBUTE_TYPE_TEXCOORD: { __GS_GLTF_WRITE_DATA(it, v_data, uvs, gs_vec2, gs_v2(0.f, 0.f), GS_ASSET_MESH_ATTRIBUTE_TYPE_TEXCOORD); } break; case GS_ASSET_MESH_ATTRIBUTE_TYPE_COLOR: { __GS_GLTF_WRITE_DATA(it, v_data, colors, gs_color_t, GS_COLOR_WHITE, GS_ASSET_MESH_ATTRIBUTE_TYPE_COLOR); } break; case GS_ASSET_MESH_ATTRIBUTE_TYPE_NORMAL: { __GS_GLTF_WRITE_DATA(it, v_data, normals, gs_vec3, gs_v3(0.f, 0.f, 1.f), GS_ASSET_MESH_ATTRIBUTE_TYPE_NORMAL); } break; case GS_ASSET_MESH_ATTRIBUTE_TYPE_TANGENT: { __GS_GLTF_WRITE_DATA(it, v_data, tangents, gs_vec3, gs_v3(0.f, 1.f, 0.f), GS_ASSET_MESH_ATTRIBUTE_TYPE_TANGENT); } break; default: { } break; } } } } // Add to out data mesh->vertices[p] = gs_malloc(v_data.size); mesh->indices[p] = gs_malloc(i_data.size); mesh->vertex_sizes[p] = v_data.size; mesh->index_sizes[p] = i_data.size; // Copy data memcpy(mesh->vertices[p], v_data.data, v_data.size); memcpy(mesh->indices[p], i_data.data, i_data.size); } } // Free all data at the end cgltf_free(data); gs_dyn_array_free(positions); gs_dyn_array_free(normals); gs_dyn_array_free(tangents); gs_dyn_array_free(colors); gs_dyn_array_free(uvs); gs_dyn_array_free(layouts); gs_byte_buffer_free(&v_data); gs_byte_buffer_free(&i_data); return true; } bool gs_asset_mesh_load_from_file(const char* path, void* out, gs_asset_mesh_decl_t* decl, void* data_out, size_t data_size) { // Cast mesh data to use gs_asset_mesh_t* mesh = (gs_asset_mesh_t*)out; if (!gs_platform_file_exists(path)) { gs_println("Warning:MeshLoadFromFile:File does not exist: %s", path); return false; } // Mesh data to fill out uint32_t mesh_count = 0; gs_asset_mesh_raw_data_t* meshes = NULL; // Get file extension from path gs_transient_buffer(file_ext, 32); gs_platform_file_extension(file_ext, 32, path); // GLTF if (gs_string_compare_equal(file_ext, "gltf")) { gs_util_load_gltf_data_from_file(path, decl, &meshes, &mesh_count); } else { gs_println("Warning:MeshLoadFromFile:File extension not supported: %s, file: %s", file_ext, path); return false; } // For now, handle meshes with only single mesh count if (mesh_count != 1) { // Error // Free all the memory return false; } // Process all mesh data, add meshes for (uint32_t i = 0; i < mesh_count; ++i) { gs_asset_mesh_raw_data_t* m = &meshes[i]; for (uint32_t p = 0; p < m->prim_count; ++p) { // Construct primitive gs_asset_mesh_primitive_t prim = gs_default_val(); prim.count = m->index_sizes[p] / sizeof(uint16_t); // Vertex buffer decl gs_graphics_vertex_buffer_desc_t vdesc = gs_default_val(); vdesc.data = m->vertices[p]; vdesc.size = m->vertex_sizes[p]; // Construct vertex buffer for primitive prim.vbo = gs_graphics_vertex_buffer_create(&vdesc); // Index buffer decl gs_graphics_index_buffer_desc_t idesc = gs_default_val(); idesc.data = m->indices[p]; idesc.size = m->index_sizes[p]; // Construct index buffer for primitive prim.ibo = gs_graphics_index_buffer_create(&idesc); // Add primitive to mesh gs_dyn_array_push(mesh->primitives, prim); } } // Free all mesh data return true; } /*======================== // GS_LEXER ========================*/ //==== [ Token ] ============================================================// GS_API_DECL gs_token_t gs_token_invalid_token() { gs_token_t t = gs_default_val(); t.text = ""; t.type = GS_TOKEN_UNKNOWN; t.len = 0; return t; } GS_API_DECL bool gs_token_compare_type(const gs_token_t* t, gs_token_type type) { return (t->type == type); } GS_API_DECL bool gs_token_compare_text(const gs_token_t* t, const char* match) { if (t->len != gs_string_length(match)) return false; return (gs_string_compare_equal_n(t->text, match, t->len)); } GS_API_DECL void gs_token_print_text(const gs_token_t* t) { gs_println("%.*s\n", t->len, t->text); } GS_API_DECL void gs_token_debug_print(const gs_token_t* t) { gs_println("%s: %.*s", gs_token_type_to_str(t->type), t->len, t->text); } GS_API_DECL const char* gs_token_type_to_str(gs_token_type type) { switch (type) { default: case GS_TOKEN_UNKNOWN: return gs_to_str(GS_TOKEN_UNKNOWN); break; case GS_TOKEN_LPAREN: return gs_to_str(GS_TOKEN_LPAREN); break; case GS_TOKEN_RPAREN: return gs_to_str(GS_TOKEN_RPAREN); break; case GS_TOKEN_LTHAN: return gs_to_str(GS_TOKEN_LTHAN); break; case GS_TOKEN_GTHAN: return gs_to_str(GS_TOKEN_GTHAN); break; case GS_TOKEN_SEMICOLON: return gs_to_str(GS_TOKEN_SEMICOLON); break; case GS_TOKEN_COLON: return gs_to_str(GS_TOKEN_COLON); break; case GS_TOKEN_COMMA: return gs_to_str(GS_TOKEN_COMMA); break; case GS_TOKEN_EQUAL: return gs_to_str(GS_TOKEN_EQUAL); break; case GS_TOKEN_NOT: return gs_to_str(GS_TOKEN_NOT); break; case GS_TOKEN_HASH: return gs_to_str(GS_TOKEN_HASH); break; case GS_TOKEN_PIPE: return gs_to_str(GS_TOKEN_PIPE); break; case GS_TOKEN_AMPERSAND:return gs_to_str(GS_TOKEN_AMPERSAND); break; case GS_TOKEN_LBRACE: return gs_to_str(GS_TOKEN_LBRACE); break; case GS_TOKEN_RBRACE: return gs_to_str(GS_TOKEN_RBRACE); break; case GS_TOKEN_LBRACKET: return gs_to_str(GS_TOKEN_LBRACKET); break; case GS_TOKEN_RBRACKET: return gs_to_str(GS_TOKEN_RBRACKET); break; case GS_TOKEN_MINUS: return gs_to_str(GS_TOKEN_MINUS); break; case GS_TOKEN_PLUS: return gs_to_str(GS_TOKEN_PLUS); break; case GS_TOKEN_ASTERISK: return gs_to_str(GS_TOKEN_ASTERISK); break; case GS_TOKEN_BSLASH: return gs_to_str(GS_TOKEN_BLASH); break; case GS_TOKEN_FSLASH: return gs_to_str(GS_TOKEN_FLASH); break; case GS_TOKEN_QMARK: return gs_to_str(GS_TOKEN_QMARK); break; case GS_TOKEN_DOLLAR: return gs_to_str(GS_TOKEN_DOLLAR); break; case GS_TOKEN_SPACE: return gs_to_str(GS_TOKEN_SPACE); break; case GS_TOKEN_NEWLINE: return gs_to_str(GS_TOKEN_NEWLINE); break; case GS_TOKEN_TAB: return gs_to_str(GS_TOKEN_TAB); break; case GS_TOKEN_SINGLE_LINE_COMMENT: return gs_to_str(GS_TOKEN_SINGLE_LINE_COMMENT); break; case GS_TOKEN_MULTI_LINE_COMMENT: return gs_to_str(GS_TOKEN_MULTI_LINE_COMMENT); break; case GS_TOKEN_IDENTIFIER: return gs_to_str(GS_TOKEN_IDENTIFIER); break; case GS_TOKEN_NUMBER: return gs_to_str(GS_TOKEN_NUMBER); break; case GS_TOKEN_PERIOD: return gs_to_str(GS_TOKEN_PERIOD); break; case GS_TOKEN_STRING: return gs_to_str(GS_TOKEN_STRING); break; } } GS_API_DECL bool gs_char_is_null_term(char c) { return (c == '\0'); } GS_API_DECL bool gs_char_is_end_of_line(char c) { return (c == '\n' || c == '\r'); } GS_API_DECL bool gs_char_is_white_space(char c) { return (c == '\t' || c == ' ' || gs_char_is_end_of_line(c)); } GS_API_DECL bool gs_char_is_alpha(char c) { return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); } GS_API_DECL bool gs_char_is_numeric(char c) { return (c >= '0' && c <= '9'); } //==== [ Lexer ] ============================================================// GS_API_DECL void gs_lexer_set_contents(gs_lexer_t* lex, const char* contents) { lex->at = contents; lex->current_token = gs_token_invalid_token(); } GS_API_DECL bool gs_lexer_c_can_lex(gs_lexer_t* lex) { bool size_pass = lex->contents_size ? lex->size < lex->contents_size : true; return (size_pass && lex->at && !gs_char_is_null_term(*(lex->at))); } GS_API_DECL void gs_lexer_set_token(gs_lexer_t* lex, gs_token_t token) { lex->at = token.text; lex->current_token = token; } GS_API_DECL void gs_lexer_c_eat_white_space(gs_lexer_t* lex) { for (;;) { if (gs_char_is_white_space(*lex->at)) { if (gs_char_is_end_of_line(*lex->at)) {lex->line++;} lex->at++; } // Single line comment else if ((lex->at[0] == '/') && (lex->at[1]) && (lex->at[1] == '/')) { lex->at += 2; while (*lex->at && !gs_char_is_end_of_line(*lex->at)) { lex->at++; } } // Multi-line comment else if ((lex->at[0] == '/') && (lex->at[1]) && (lex->at[1] == '*')) { lex->at += 2; while (lex->at[0] && lex->at[1] && !(lex->at[0] == '*' && lex->at[1] == '/')) { // if (gs_char_is_end_of_line(*lex->at)) {lex->line++;} lex->at++; } if (lex->at[0] == '*') { lex->at++; } } else { break; } } } GS_API_DECL gs_token_t gs_lexer_c_next_token(gs_lexer_t* lex) { if (lex->skip_white_space) { lex->eat_white_space(lex); } gs_token_t t = gs_token_invalid_token(); t.text = lex->at; t.len = 1; if (lex->can_lex(lex)) { char c = *lex->at; switch (c) { case '(': {t.type = GS_TOKEN_LPAREN; lex->at++;} break; case ')': {t.type = GS_TOKEN_RPAREN; lex->at++;} break; case '<': {t.type = GS_TOKEN_LTHAN; lex->at++;} break; case '>': {t.type = GS_TOKEN_GTHAN; lex->at++;} break; case ';': {t.type = GS_TOKEN_SEMICOLON; lex->at++;} break; case ':': {t.type = GS_TOKEN_COLON; lex->at++;} break; case ',': {t.type = GS_TOKEN_COMMA; lex->at++;} break; case '=': {t.type = GS_TOKEN_EQUAL; lex->at++;} break; case '!': {t.type = GS_TOKEN_NOT; lex->at++;} break; case '#': {t.type = GS_TOKEN_HASH; lex->at++;} break; case '|': {t.type = GS_TOKEN_PIPE; lex->at++;} break; case '&': {t.type = GS_TOKEN_AMPERSAND; lex->at++;} break; case '{': {t.type = GS_TOKEN_LBRACE; lex->at++;} break; case '}': {t.type = GS_TOKEN_RBRACE; lex->at++;} break; case '[': {t.type = GS_TOKEN_LBRACKET; lex->at++;} break; case ']': {t.type = GS_TOKEN_RBRACKET; lex->at++;} break; case '+': {t.type = GS_TOKEN_PLUS; lex->at++;} break; case '*': {t.type = GS_TOKEN_ASTERISK; lex->at++;} break; case '\\': {t.type = GS_TOKEN_BSLASH; lex->at++;} break; case '?': {t.type = GS_TOKEN_QMARK; lex->at++;} break; case '%': {t.type = GS_TOKEN_PERCENT; lex->at++;} break; case '$': {t.type = GS_TOKEN_DOLLAR; lex->at++;} break; case ' ': {t.type = GS_TOKEN_SPACE; lex->at++;} break; case '\n': {t.type = GS_TOKEN_NEWLINE; lex->at++;} break; case '\r': {t.type = GS_TOKEN_NEWLINE; lex->at++;} break; case '\t': {t.type = GS_TOKEN_TAB; lex->at++;} break; case '.': {t.type = GS_TOKEN_PERIOD; lex->at++;} break; case '-': { if (lex->at[1] && !gs_char_is_numeric(lex->at[1])) { t.type = GS_TOKEN_MINUS; lex->at++; } else { lex->at++; uint32_t num_decimals = 0; while ( lex->at[0] && (gs_char_is_numeric(lex->at[0]) || (lex->at[0] == '.' && num_decimals == 0) || lex->at[0] == 'f') ) { // Grab decimal num_decimals = lex->at[0] == '.' ? num_decimals + 1 : num_decimals; //Increment lex->at++; } t.len = lex->at - t.text; t.type = GS_TOKEN_NUMBER; } } break; case '/': { // Single line comment if ((lex->at[0] == '/') && (lex->at[1]) && (lex->at[1] == '/')) { lex->at += 2; while (lex->at[0] && !gs_char_is_end_of_line(lex->at[0])) { lex->at++; } t.len = lex->at - t.text; t.type = GS_TOKEN_SINGLE_LINE_COMMENT; } // Multi line comment else if ((lex->at[0] == '/') && (lex->at[1]) && (lex->at[1] == '*')) { lex->at += 2; while (lex->can_lex(lex)) { // if (gs_char_is_end_of_line(*lex->at)) {lex->line++;} if (lex->at[0] == '*' && lex->at[1] == '/') { lex->at += 2; break; } lex->at++; } t.len = lex->at - t.text; t.type = GS_TOKEN_MULTI_LINE_COMMENT; } // it's just a forward slash else { t.type = GS_TOKEN_FSLASH; lex->at++; } } break; case '"': { // Move forward after finding first quotation lex->at++; while (lex->at && *lex->at != '"') { // if (gs_char_is_end_of_line(*lex->at)) {lex->line++;} if (lex->at[0] == '\\' && lex->at[1]) { lex->at++; } lex->at++; } //Move past quotation lex->at++; t.len = lex->at - t.text; t.type = GS_TOKEN_STRING; } break; // Alpha/Numeric/Identifier default: { if ((gs_char_is_alpha(c) || c == '_') && c != '-') { while ( gs_char_is_alpha(lex->at[0]) || gs_char_is_numeric(lex->at[0]) || lex->at[0] == '_' ) { lex->at++; } t.len = lex->at - t.text; t.type = GS_TOKEN_IDENTIFIER; } else if (gs_char_is_numeric(c) && c != '-') { uint32_t num_decimals = 0; while ( gs_char_is_numeric(lex->at[0]) || (lex->at[0] == '.' && num_decimals == 0) || lex->at[0] == 'f' ) { // Grab decimal num_decimals = lex->at[0] == '.' ? num_decimals + 1 : num_decimals; //Increment lex->at++; } t.len = lex->at - t.text; t.type = GS_TOKEN_NUMBER; } else { t.type = GS_TOKEN_UNKNOWN; lex->at++; } } break; } } // Set current token for lex lex->current_token = t; // Record size lex->size += t.len; return t; } GS_API_DECL gs_token_t gs_lexer_next_token(gs_lexer_t* lex) { return lex->next_token(lex); } GS_API_DECL bool gs_lexer_can_lex(gs_lexer_t* lex) { return lex->can_lex(lex); } GS_API_DECL gs_token_t gs_lexer_current_token(const gs_lexer_t* lex) { return lex->current_token; } GS_API_DECL bool gs_lexer_current_token_compare_type(const gs_lexer_t* lex, gs_token_type type) { return (lex->current_token.type == type); } GS_API_DECL gs_token_t gs_lexer_peek(gs_lexer_t* lex) { // Store current at and current token const char* at = lex->at; gs_token_t cur_t = gs_lexer_current_token(lex); // Get next token gs_token_t next_t = lex->next_token(lex); // Reset lex->current_token = cur_t; lex->at = at; // Return return next_t; } // Check to see if token type of next valid token matches 'match'. Restores place in lex if not. GS_API_DECL bool gs_lexer_require_token_text(gs_lexer_t* lex, const char* match) { // Store current position and token const char* at = lex->at; gs_token_t cur_t = lex->current_token; // Get next token gs_token_t next_t = lex->next_token(lex); // Compare token text if (gs_token_compare_text(&next_t, match)) { return true; } // Error gs_println("error::gs_lexer_require_token_text::%.*s, expected: %s", cur_t.len, cur_t.text, match); // Reset lex->at = at; lex->current_token = cur_t; return false; } GS_API_DECL bool gs_lexer_require_token_type(gs_lexer_t* lex, gs_token_type type) { // Store current position and token const char* at = lex->at; gs_token_t cur_t = lex->current_token; // Get next token gs_token_t next_t = lex->next_token(lex); // Compare token type if (gs_token_compare_type(&next_t, type)) { return true; } // Error // gs_println("error::gs_lexer_require_token_type::%s, expected: %s", gs_token_type_to_str(next_t.type), gs_token_type_to_str(type)); // Reset lex->at = at; lex->current_token = cur_t; return false; } // Advances until next token of given type is found GS_API_DECL bool gs_lexer_find_next_token_type(gs_lexer_t* lex, gs_token_type type) { gs_token_t t = lex->next_token(lex); while (lex->can_lex(lex)) { if (gs_token_compare_type(&t, type)) { return true; } t = lex->next_token(lex); } return false; } GS_API_DECL gs_token_t gs_lexer_advance_before_next_token_type(gs_lexer_t* lex, gs_token_type type) { gs_token_t t = lex->current_token; gs_token_t peek_t = gs_lexer_peek(lex); // Continue right up until required token type while (!gs_token_compare_type(&peek_t, type)) { t = lex->next_token(lex); peek_t = gs_lexer_peek(lex); } return t; } GS_API_DECL gs_lexer_t gs_lexer_c_ctor(const char* contents) { gs_lexer_t lex = gs_default_val(); lex.contents = contents; lex.at = contents; lex.can_lex = gs_lexer_c_can_lex; lex.eat_white_space = gs_lexer_c_eat_white_space; lex.next_token = gs_lexer_c_next_token; lex.skip_white_space = true; return lex; } /*============================= // GS_ENGINE =============================*/ GS_API_DECL void gs_default_app_func(); GS_API_DECL void gs_default_main_window_close_callback(void* window); // Global instance of gunslinger framework (...THERE CAN ONLY BE ONE) gs_global gs_t* _gs_instance = gs_default_val(); GS_API_DECL gs_t* gs_create(gs_app_desc_t app_desc) { if (gs_instance() == NULL) { // Check app desc for defaults if (app_desc.window.width == 0) app_desc.window.width = 800; if (app_desc.window.height == 0) app_desc.window.height = 600; if (app_desc.window.title == 0) app_desc.window.title = "App"; if (app_desc.window.frame_rate <= 0.f) app_desc.window.frame_rate = 60.f; if (app_desc.update == NULL) app_desc.update = &gs_default_app_func; if (app_desc.shutdown == NULL) app_desc.shutdown = &gs_default_app_func; if (app_desc.init == NULL) app_desc.init = &gs_default_app_func; // Set up os api before all? gs_os_api_t os = gs_os_api_new(); // Construct instance and set _gs_instance = (gs_t*)os.malloc(sizeof(gs_t)); memset(_gs_instance, 0, sizeof(gs_t)); gs_instance()->ctx.lock = SCHED_PIPE_INVALID; // Set os api now allocated gs_instance()->ctx.os = os; // Set application description for framework gs_instance()->ctx.app = app_desc; // Set up function pointers gs_instance()->shutdown = &gs_destroy; // Need to have video settings passed down from user gs_subsystem(platform) = gs_platform_create(); // Enable graphics API debugging gs_subsystem(platform)->settings.video.graphics.debug = app_desc.debug_gfx; // Default initialization for platform here gs_platform_init(gs_subsystem(platform)); // Set frame rate for application gs_subsystem(platform)->time.max_fps = app_desc.window.frame_rate; // Construct main window gs_platform_window_create(&app_desc.window); // Set vsync for video gs_platform_enable_vsync(app_desc.window.vsync); // Construct graphics api gs_subsystem(graphics) = gs_graphics_create(); // Initialize graphics here gs_graphics_init(gs_subsystem(graphics)); // Construct audio api gs_subsystem(audio) = gs_audio_create(); // Initialize audio gs_audio_init(gs_subsystem(audio)); // Initialize application and set to running app_desc.init(); gs_ctx()->app.is_running = true; // Set default callback for when main window close button is pressed gs_platform_set_window_close_callback(gs_platform_main_window(), &gs_default_main_window_close_callback); } return gs_instance(); } GS_API_DECL void gs_set_instance(gs_t* gs) { _gs_instance = gs; } GS_API_DECL gs_t* gs_instance() { return _gs_instance; } GS_API_DECL gs_context_t* gs_ctx() { return &gs_instance()->ctx; } GS_API_DECL gs_app_desc_t* gs_app() { return &gs_instance()->ctx.app; } // Define main frame function for framework to step GS_API_DECL void gs_frame() { // Remove these... static uint32_t curr_ticks = 0; static uint32_t prev_ticks = 0; // Cache platform pointer gs_platform_t* platform = gs_subsystem(platform); // Cache times at start of frame platform->time.elapsed = (float)gs_platform_elapsed_time(); platform->time.update = platform->time.elapsed - platform->time.previous; platform->time.previous = platform->time.elapsed; // Update platform and process input gs_platform_update(platform); if (!gs_instance()->ctx.app.is_running) { gs_instance()->shutdown(); return; } // Process application context gs_instance()->ctx.app.update(); if (!gs_instance()->ctx.app.is_running) { gs_instance()->shutdown(); return; } // Clear all platform events gs_dyn_array_clear(platform->events); // NOTE(John): This won't work forever. Must change eventually. // Swap all platform window buffers? Sure... for ( gs_slot_array_iter it = 0; gs_slot_array_iter_valid(platform->windows, it); gs_slot_array_iter_advance(platform->windows, it) ) { gs_platform_window_swap_buffer(it); } // Frame locking (not sure if this should be done here, but it is what it is) platform->time.elapsed = (float)gs_platform_elapsed_time(); platform->time.render = platform->time.elapsed - platform->time.previous; platform->time.previous = platform->time.elapsed; platform->time.frame = platform->time.update + platform->time.render; // Total frame time platform->time.delta = platform->time.frame / 1000.f; float target = (1000.f / platform->time.max_fps); if (platform->time.frame < target) { gs_platform_sleep((float)(target - platform->time.frame)); platform->time.elapsed = (float)gs_platform_elapsed_time(); double wait_time = platform->time.elapsed - platform->time.previous; platform->time.previous = platform->time.elapsed; platform->time.frame += wait_time; platform->time.delta = platform->time.frame / 1000.f; } } void gs_destroy() { // Shutdown application gs_ctx()->app.shutdown(); gs_ctx()->app.is_running = false; // Shutdown subsystems gs_graphics_shutdown(gs_subsystem(graphics)); gs_graphics_destroy(gs_subsystem(graphics)); gs_audio_shutdown(gs_subsystem(audio)); gs_audio_destroy(gs_subsystem(audio)); gs_platform_shutdown(gs_subsystem(platform)); gs_platform_destroy(gs_subsystem(platform)); } GS_API_DECL void gs_default_app_func() { // Nothing... } GS_API_DECL void gs_default_main_window_close_callback(void* window) { gs_instance()->ctx.app.is_running = false; } void gs_quit() { #ifndef GS_PLATFORM_WEB gs_instance()->ctx.app.is_running = false; #endif } #undef GS_IMPL #endif // GS_IMPL #endif // GS_H /* Layout decl // Pipeline should have layout desc for vertices? // Or should it have layout for EACH buffer? non-interleaved vertex data have to be able to specific stride/offset for vertex layouts What are ways to interleave data? layout descriptor? gs_vertex_attribute_type layouts[] = { }; // Need to codify strides/offsets/divisors // This can hold multiple layouts gs_vertex_layout_desc_t layout = { .layouts = layouts, .size = sizeof(layouts) }; Don't want to have to make user calculate strides, right? // If you don't provide manual stride/offset, then it'll calculate it for you based on layout? #version 330 core layout (location = 0) in vec2 aPos; layout (location = 1) in vec3 aColor; layout (location = 2) in vec2 aOffset; out vec3 fColor; void main() { gl_Position = vec4(aPos + aOffset, 0.0, 1.0); fColor = aColor; } typedef struct gs_vertex_attribute_layout_desc_t { gs_vertex_attribute_type format; size_t stride; size_t offset; size_t divisor; } gs_vertex_attribute_layout_desc_t; gs_vertex_attribute_layout_desc_t layout[] = { {.format = GS_VERTEX_ATTRIBUTE_FLOAT2}, {.format = GS_VERTEX_ATTRIBUTE_FLOAT3}, {.format = GS_VERTEX_ATTRIBUTE_FLOAT2, .stride = 2 * sizeof(float), .offset = 0, .divisor = 1} }; What about non-interleaved data? Almost would need to provide an offset. It's specific to the data itself, so have to provide manual offsets for data in binding data gs_graphics_bind_desc_t binds[] = { {.type = GS_GRAPHICS_BIND_VERTEX_BUFFER, .buffer = , .offset = }; } gs_graphics_bind_desc_t binds[] = { (gs_graphics_bind_desc_t){.type = GS_GRAPHICS_BIND_VERTEX_BUFFER, .buffer = vbo, .offset = ...}, (gs_graphics_bind_desc_t){.type = GS_GRAPHICS_BIND_VERTEX_BUFFER, .buffer = vbo, .offset = ...}, }; .layout = { .attributes = &(){ {.format = GS_VERTEX_ATTRIBUTE_FLOAT2}, {.format = GS_VERTEX_ATTRIBUTE_FLOAT2}, {.format = GS_VERTEX_ATTRIBUTE_FLOAT4, .stride = 64, .offset =, .divisor = } } }; sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){ .shader = shd, .layout = { .attrs = { [0].format=SG_VERTEXFORMAT_FLOAT3, [1].format=SG_VERTEXFORMAT_FLOAT4 } } }); .layout = { .attrs = attrs, .attr_size = sizeof(attrs), .strides = strides, .stride_size = sizeof(strides), } sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){ .layout = { .buffers = { [0] = { .stride = 28 }, [1] = { .stride = 12, .step_func=SG_VERTEXSTEP_PER_INSTANCE } }, .attrs = { [0] = { .offset = 0, .format=SG_VERTEXFORMAT_FLOAT3, .buffer_index=0 }, [1] = { .offset = 12, .format=SG_VERTEXFORMAT_FLOAT4, .buffer_index=0 }, [2] = { .offset = 0, .format=SG_VERTEXFORMAT_FLOAT3, .buffer_index=1 } } }, .shader = shd, .index_type = SG_INDEXTYPE_UINT16, .depth_stencil = { .depth_compare_func = SG_COMPAREFUNC_LESS_EQUAL, .depth_write_enabled = true }, .rasterizer.cull_mode = SG_CULLMODE_BACK }); float quadVertices[] = { // positions // colors -0.05f, 0.05f, 1.0f, 0.0f, 0.0f, 0.05f, -0.05f, 0.0f, 1.0f, 0.0f, -0.05f, -0.05f, 0.0f, 0.0f, 1.0f, -0.05f, 0.05f, 1.0f, 0.0f, 0.0f, 0.05f, -0.05f, 0.0f, 1.0f, 0.0f, 0.05f, 0.05f, 0.0f, 1.0f, 1.0f }; // also set instance data glEnableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER, instanceVBO); // this attribute comes from a different vertex buffer glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0); glBindBuffer(GL_ARRAY_BUFFER, 0); glVertexAttribDivisor(2, 1); // tell OpenGL this is an instanced vertex attribute. gltf loading Mesh Attributes: cgltf_attribute_type_invalid, cgltf_attribute_type_position, cgltf_attribute_type_normal, cgltf_attribute_type_tangent, cgltf_attribute_type_texcoord, cgltf_attribute_type_color, cgltf_attribute_type_joints, cgltf_attribute_type_weights, Primitive types: cgltf_primitive_type_points, cgltf_primitive_type_lines, cgltf_primitive_type_line_loop, cgltf_primitive_type_line_strip, cgltf_primitive_type_triangles, cgltf_primitive_type_triangle_strip, cgltf_primitive_type_triangle_fan, For each mesh: For each primitive: For each attribute: Get data and push into mesh definition Is there a way to have the user be able to specify a layout and then use that for the mesh? gs_enum_decl(gs_asset_mesh_attribute_type, GS_ASSET_MESH_ATTRIBUTE_TYPE_POSITION, GS_ASSET_MESH_ATTRIBUTE_TYPE_NORMAL, GS_ASSET_MESH_ATTRIBUTE_TYPE_TANGENT, GS_ASSET_MESH_ATTRIBUTE_TYPE_JOINT, GS_ASSET_MESH_ATTRIBUTE_TYPE_WEIGHT, GS_ASSET_MESH_ATTRIBUTE_TYPE_TEXCOORD, GS_ASSET_MESH_ATTRIBUTE_TYPE_COLOR }); typedef struct gs_asset_mesh_layout_t { gs_asset_mesh_attribute_type type; // Type of attribute uint32_t idx; // Optional index (for joint/weight/texcoord/color) } gs_asset_mesh_layout_t; typedef struct gs_asset_mesh_decl_t { gs_asset_mesh_layout_t* layout; // Mesh attribute layout array size_t layout_size; // Size of mesh attribute layout array in bytes } gs_asset_mesh_decl_t; // Mesh holds...what? // A pipeline? Shouldn't have to. // Material? Nope. // It's just mesh data. (so an index/vertex buffer) typedef struct gs_asset_mesh_t { gs_handle(gs_graphics_buffer_t) vbo; gs_handle(gs_graphics_buffer_t) ibo; } gs_asset_mesh_t; void gs_asset_mesh_load_from_file(const char* path, void* out, gs_asset_mesh_decl_t* decl, void* data_out, size_t data_size) { gs_asset_mesh_t* mesh = (gs_asset_mesh_t*)out; // Parse gltf data } Does this need to line up with a pipeline? Not necessarily, right? // At LEAST position is required to be passed in for the layout, so maybe it's not necessary // to provide this in the layout? // Can you duplicate? I don't think so... gs_asset_mesh_attribute_type layout[] = { POSITION, NORMAL, TANGENT, JOINTS_XXX, WEIGHTS_XXX, TEXCOORD_XXX, COLOR_XXX }; gs_asset_mesh_t mesh = gs_asset_load_gltf(path, layout, sizeof(layout)); // Do you HAVE to have certain attributes for a mesh to make any sense? For instance, do you HAVE to have position? // What if position is NOT the first attribute layout for your vertex attribute? // Need to fill out data for each attribute, then interleave? ^() { For each mesh: For each primitive: For each attribute: Get data and push into mesh definition } main update for web? android? probably want to move to different "main" implementations: gs_win32_main gs_glfw_linux_main gs_glfw_osx_main gs_glfw_win32_main gs_glfw_emsc_main Each platform should define its own main function, control its own loop, reach into application code, then run how it needs to. int main(int32_t argc, char** argv) { emscripten_set_main_loop(gs_create(gs_main(argc, argv))->run(), 0, true); } */