diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..c835fb9 --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + CompilationDatabase: "./.build/" diff --git a/.gitignore b/.gitignore index 86abade..cfb2988 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .vs/ .vscode/ .idea/ +.cache/ .build/ build/ out/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a427f3..cbd9348 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,14 @@ add_subdirectory(third-party/physfs) add_subdirectory(third-party/libxm) +if(UNIX) + set(SYSTEM_SOURCE_FILES + src/system/linux/elf.c + ) +else() + set(SYSTEM_SOURCE_FILES) +endif() + set(SOURCE_FILES third-party/physfs/extras/physfsrwops.c third-party/stb/stb_vorbis.c @@ -49,6 +57,8 @@ set(SOURCE_FILES src/game/scenes/scene.c src/game/scenes/scene.h src/game/scenes/title.c src/game/scenes/title.h src/game/scenes/ingame.c src/game/scenes/ingame.h + + ${SYSTEM_SOURCE_FILES} ) # target @@ -58,7 +68,7 @@ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCE_FILES}) set_target_properties(${PROJECT_NAME} PROPERTIES C_STANDARD 11 C_STANDARD_REQUIRED ON - C_EXTENSIONS OFF) + C_EXTENSIONS ON) # extensions are required by stb_ds.h # distribution definitions set(ORGANIZATION_NAME "wanp" CACHE STRING @@ -94,11 +104,22 @@ else() -fno-trapping-math -freciprocal-math) set(BUILD_FLAGS_RELEASE - -flto) + -O3 + -flto + -Wl,-gc-sections + -fdata-sections + -ffunction-sections + -funroll-loops + -fomit-frame-pointer + -fallow-store-data-races) set(BUILD_FLAGS_DEBUG + -O0 -g3 -gdwarf - -fsanitize-trap=undefined) + -fno-omit-frame-pointer + -fstack-protector-all + -fsanitize=undefined + -fsanitize=address) target_compile_options(${PROJECT_NAME} PRIVATE ${WARNING_FLAGS} ${BUILD_FLAGS} diff --git a/data/assets/big-violet.png b/data/assets/big-violet.png new file mode 100644 index 0000000..84f5f75 --- /dev/null +++ b/data/assets/big-violet.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c7c91f67e8bd93e1abf6f40ee14a72cd0990d2e25ae30eca9c553bc1e2381af8 +size 77981 diff --git a/data/assets/light.png b/data/assets/light.png new file mode 100644 index 0000000..128161f --- /dev/null +++ b/data/assets/light.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c4f651e3cf4106856a20519334f3b239305ec014046312f698a70a1df1ce319b +size 1309 diff --git a/src/audio.c b/src/audio.c index c836f3b..e220178 100644 --- a/src/audio.c +++ b/src/audio.c @@ -57,7 +57,7 @@ static int64_t get_audio_data(const char *path, unsigned char **data) { void play_audio(const char *path, const char *channel) { - const struct audio_channel_pair *pair = shgetp_null(ctx.audio_channels, channel); + const struct audio_channel_item *pair = shgetp_null(ctx.audio_channels, channel); if (!pair) play_audio_ex(path, channel, get_default_audio_args()); else @@ -166,7 +166,7 @@ static void repeat_audio(struct audio_channel *channel) { void play_audio_ex(const char *path, const char *channel, t_play_audio_args args) { - struct audio_channel_pair *pair = shgetp_null(ctx.audio_channels, channel); + struct audio_channel_item *pair = shgetp_null(ctx.audio_channels, channel); /* create a channel if it doesn't exist */ if (!pair) { @@ -191,7 +191,7 @@ void play_audio_ex(const char *path, const char *channel, t_play_audio_args args t_play_audio_args *get_audio_args(const char *channel) { - struct audio_channel_pair *pair = shgetp_null(ctx.audio_channels, channel); + struct audio_channel_item *pair = shgetp_null(ctx.audio_channels, channel); if (!pair) return NULL; @@ -244,7 +244,7 @@ static void audio_sample_and_mixin_channel(const struct audio_channel *channel, static uint8_t buffer[16384]; const int int16_buffer_frames = sizeof (buffer) / sizeof (int16_t); const int float_buffer_frames = sizeof (buffer) / sizeof (float); - const int stream_frames = len / sizeof (int16_t); + const int stream_frames = len / (int)(sizeof (int16_t)); switch (channel->file_type) { case audio_file_type_ogg: { diff --git a/src/config.h b/src/config.h index 805214f..11d647f 100644 --- a/src/config.h +++ b/src/config.h @@ -17,6 +17,7 @@ #define RENDER_BASE_WIDTH 640 #define RENDER_BASE_HEIGHT 360 +#define RENDER_BASE_RATIO ((float)RENDER_BASE_WIDTH / (float)RENDER_BASE_HEIGHT) #define TEXTURE_ATLAS_SIZE 2048 #define TEXTURE_ATLAS_BIT_DEPTH 32 diff --git a/src/context.h b/src/context.h index 7ad1d64..b80da22 100644 --- a/src/context.h +++ b/src/context.h @@ -20,11 +20,10 @@ typedef struct context { struct texture_cache texture_cache; struct input_state input; - struct sprite_primitive *render_queue_sprites; - struct rect_primitive *render_queue_rectangles; - struct circle_primitive *render_queue_circles; + struct primitive_2d *render_queue_2d; + struct mesh_batch_item *uncolored_mesh_batches; - struct audio_channel_pair *audio_channels; + struct audio_channel_item *audio_channels; SDL_AudioDeviceID audio_device; int audio_stream_frequency; SDL_AudioFormat audio_stream_format; @@ -37,7 +36,7 @@ typedef struct context { int64_t prev_frame_time; int64_t desired_frametime; /* how long one tick should be */ int64_t frame_accumulator; - int64_t delta_averager_residual; + int64_t delta_averager_residual; int64_t time_averager[4]; int64_t delta_time; /* preserves real time frame delta with no manipilation */ uint64_t tick_count; @@ -51,7 +50,6 @@ typedef struct context { unsigned int update_multiplicity; SDL_GLContext *gl_context; - SDL_Renderer *renderer; SDL_Window *window; uint32_t window_id; int window_w; @@ -64,6 +62,8 @@ typedef struct context { bool debug; bool is_running; bool resync_flag; + + bool window_size_has_changed; } t_ctx; extern t_ctx ctx; diff --git a/src/game/scenes/ingame.c b/src/game/scenes/ingame.c index 1bea67b..666b027 100644 --- a/src/game/scenes/ingame.c +++ b/src/game/scenes/ingame.c @@ -11,7 +11,54 @@ static void ingame_tick(struct state *state) { world_drawdef(scn->world); player_calc(scn->player); - get_audio_args("soundtrack")->volume -= 0.01f; + for (size_t i = 100000; --i;) + push_sprite_ex((t_frect){ .x = 32, .y = 64, .w = 64, .h = 64 }, (t_push_sprite_args){ + .path = "/assets/light.png", + .color = (t_color){255, 0, 0, 255}, + .blend = false }); + + push_sprite_ex((t_frect){ .x = 48, .y = 64, .w = 64, .h = 64 }, (t_push_sprite_args){ + .path = "/assets/light.png", + .color = (t_color){0, 255, 0, 255}, + .blend = true }); + + push_sprite_ex((t_frect){ .x = 64, .y = 64, .w = 64, .h = 64 }, (t_push_sprite_args){ + .path = "/assets/light.png", + .color = (t_color){0, 0, 255, 255}, + .blend = true }); + + push_sprite_ex((t_frect){ .x = 32, .y = 32, .w = 64, .h = 64 }, (t_push_sprite_args){ + .path = "/assets/player/baron-walk.png", + .color = (t_color){255, 255, 255, 255}, + .rotation = (float)M_PI * 2 * (float)(ctx.tick_count % 64) / 64, + .blend = true }); + + push_sprite_ex((t_frect){ .x = 64, .y = 32, .w = 64, .h = 64 }, (t_push_sprite_args){ + .path = "/assets/player/baron-walk.png", + .color = (t_color){255, 255, 255, 255}, + .rotation = (float)M_PI / 64, + .blend = true }); + + push_sprite_ex((t_frect){ .x = 96, .y = 32, .w = 64, .h = 64 }, (t_push_sprite_args){ + .path = "/assets/player/baron-walk.png", + .color = (t_color){255, 255, 255, 255}, + .blend = true }); + + unfurl_triangle("/assets/big-violet.png", + (t_fvec3){ -1, -1, 0 }, + (t_fvec3){ 1, -1, 0 }, + (t_fvec3){ 1, 1, 0 }, + (t_shvec2){ 0, 2048 }, + (t_shvec2){ 2048, 2048 }, + (t_shvec2){ 2048, 0 }); + + unfurl_triangle("/assets/big-violet.png", + (t_fvec3){ 1, 1, 0 }, + (t_fvec3){ -1, 1, 0 }, + (t_fvec3){ -1, -1, 0 }, + (t_shvec2){ 2048, 0 }, + (t_shvec2){ 0, 0 }, + (t_shvec2){ 0, 2048 }); } @@ -19,6 +66,7 @@ static void ingame_end(struct state *state) { struct scene_ingame *scn = (struct scene_ingame *)state->scene; player_destroy(scn->player); world_destroy(scn->world); + free(state->scene); } diff --git a/src/game/scenes/title.c b/src/game/scenes/title.c index ed9b32a..7db88df 100644 --- a/src/game/scenes/title.c +++ b/src/game/scenes/title.c @@ -24,6 +24,7 @@ static void title_end(struct state *state) { struct scene_title *scn = (struct scene_title *)state->scene; player_destroy(scn->player); world_destroy(scn->world); + free(state->scene); } diff --git a/src/game_api.h b/src/game_api.h index 79efee9..49760d2 100644 --- a/src/game_api.h +++ b/src/game_api.h @@ -5,6 +5,7 @@ #include "context.h" #include "rendering.h" +#include "audio.h" #include "util.h" #include diff --git a/src/input.c b/src/input.c index 0117801..ad9196e 100644 --- a/src/input.c +++ b/src/input.c @@ -36,18 +36,21 @@ static void update_action_pressed_state(struct input_state *input, struct action else { action->just_changed = !action->is_pressed; action->is_pressed = true; + action->position.x = (float)input->mouse_window_position.x; + action->position.x = (float)input->mouse_window_position.x; + /* TODO: */ /* * SDL_RenderWindowToLogical will turn window mouse * coords into a position inside the logical render * area. this has to be done to get an accurate point * that can actually be used in game logic */ - SDL_RenderWindowToLogical(input->renderer, - input->mouse_window_position.x, - input->mouse_window_position.y, - &action->position.x, - &action->position.y); + // SDL_RenderWindowToLogical(input->renderer, + // input->mouse_window_position.x, + // input->mouse_window_position.y, + // &action->position.x, + // &action->position.y); return; } break; @@ -59,9 +62,9 @@ static void update_action_pressed_state(struct input_state *input, struct action static void input_bind_code_to_action(struct input_state *input, - char *action_name, - enum button_source source, - union button_code code) + char *action_name, + enum button_source source, + union button_code code) { struct action_hash_item *action_item = shgetp_null(input->action_hash, action_name); if (action_item == NULL) { @@ -172,9 +175,8 @@ static void input_unbind_code_from_action(struct input_state *input, } -void input_state_init(struct input_state *input, SDL_Renderer *renderer) { +void input_state_init(struct input_state *input) { sh_new_strdup(input->action_hash); - input->renderer = renderer; } diff --git a/src/input.h b/src/input.h index fab59b3..0b7e501 100644 --- a/src/input.h +++ b/src/input.h @@ -54,7 +54,6 @@ struct action_hash_item { struct input_state { struct action_hash_item *action_hash; - SDL_Renderer *renderer; /* some input relates to the screen in some way */ const uint8_t *keyboard_state; /* array of booleans indexed by scancode */ uint32_t mouse_state; /* SDL mouse button bitmask */ t_vec2 mouse_window_position; @@ -63,7 +62,7 @@ struct input_state { }; -void input_state_init(struct input_state *input, SDL_Renderer *renderer); +void input_state_init(struct input_state *input); void input_state_deinit(struct input_state *input); void input_state_update(struct input_state *input); diff --git a/src/main.c b/src/main.c index a41cb68..1959a7b 100644 --- a/src/main.c +++ b/src/main.c @@ -23,18 +23,24 @@ static void poll_events(void) { SDL_Event e; + ctx.window_size_has_changed = false; + while (SDL_PollEvent(&e)) { switch (e.type) { case SDL_QUIT: ctx.is_running = false; break; - + case SDL_WINDOWEVENT: if (e.window.windowID != ctx.window_id) break; switch (e.window.event) { case SDL_WINDOWEVENT_RESIZED: + case SDL_WINDOWEVENT_SIZE_CHANGED: + ctx.window_w = e.window.data1; + ctx.window_h = e.window.data2; + ctx.window_size_has_changed = true; break; } @@ -44,6 +50,24 @@ static void poll_events(void) { } +static void APIENTRY opengl_log(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam) +{ + (void)source; + (void)type; + (void)id; + (void)severity; + (void)userParam; + + log_info("OpenGL: %.*s\n", length, message); +} + + void main_loop(void) { /* if (!ctx.is_running) { @@ -52,7 +76,7 @@ void main_loop(void) { emscripten_cancel_main_loop(); } */ - + /* frame timer */ int64_t current_frame_time = SDL_GetPerformanceCounter(); int64_t delta_time = current_frame_time - ctx.prev_frame_time; @@ -110,7 +134,7 @@ void main_loop(void) { ctx.frame_accumulator += delta_time; /* spiral of death protection */ - if (ctx.frame_accumulator > ctx.desired_frametime * 8) { + if (ctx.frame_accumulator > ctx.desired_frametime * 8) { ctx.resync_flag = true; } @@ -145,10 +169,12 @@ static bool initialize(void) { return false; } - SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); /* init got far enough to create a window */ ctx.window = SDL_CreateWindow("emerald", @@ -156,7 +182,7 @@ static bool initialize(void) { SDL_WINDOWPOS_CENTERED, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, - SDL_WINDOW_ALLOW_HIGHDPI | + // SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL); if (ctx.window == NULL) { @@ -171,6 +197,7 @@ static bool initialize(void) { } SDL_GL_MakeCurrent(ctx.window, ctx.gl_context); + SDL_GL_SetSwapInterval(1); int glad_status = gladLoadGL(); if (glad_status == 0) { @@ -178,16 +205,17 @@ static bool initialize(void) { goto fail; } + log_info("OpenGL context: %s\n", glGetString(GL_VERSION)); + /* might need this to have multiple windows */ ctx.window_id = SDL_GetWindowID(ctx.window); - /* now that we have a window, we know a renderer can be created */ - ctx.renderer = SDL_CreateRenderer(ctx.window, -1, SDL_RENDERER_PRESENTVSYNC); - - /* SDL_SetHint(SDL_HINT_RENDER_LOGICAL_SIZE_MODE, "overscan"); */ - /* SDL_RenderSetIntegerScale(ctx.renderer, SDL_TRUE); */ - SDL_RenderSetLogicalSize(ctx.renderer, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT); - SDL_GetRendererOutputSize(ctx.renderer, &ctx.window_w, &ctx.window_h); + glViewport(0, 0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT); + + /* TODO: */ + // SDL_GetRendererOutputSize(ctx.renderer, &ctx.window_w, &ctx.window_h); + ctx.window_w = RENDER_BASE_WIDTH; + ctx.window_h = RENDER_BASE_HEIGHT; /* audio initialization */ { @@ -216,14 +244,14 @@ static bool initialize(void) { } /* filesystem time */ - /* TODO: ANDROID: see the warning in physicsfs PHYSFS_init docs/header */ + /* TODO: ANDROID: see the warning in physicsfs PHYSFS_init docs/header */ if (!PHYSFS_init(ctx.argv[0]) || !PHYSFS_setSaneConfig(ORGANIZATION_NAME, APP_NAME, PACKAGE_EXTENSION, false, true)) { CRY_PHYSFS("Filesystem initialization failed."); goto fail; } - + /* you could change this at runtime if you wanted */ ctx.update_multiplicity = 1; @@ -235,6 +263,12 @@ static bool initialize(void) { ctx.debug = false; #endif + /* hook up opengl debugging callback */ + if (ctx.debug) { + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(opengl_log, NULL); + } + /* random seeding */ /* SDL_GetPerformanceCounter returns some platform-dependent number. */ /* it should vary between game instances. i checked! random enough for me. */ @@ -259,9 +293,7 @@ static bool initialize(void) { /* rendering */ /* these are dynamic arrays and will be allocated lazily by stb_ds */ - ctx.render_queue_sprites = NULL; - ctx.render_queue_rectangles = NULL; - ctx.render_queue_circles = NULL; + ctx.render_queue_2d = NULL; ctx.circle_radius_hash = NULL; textures_cache_init(&ctx.texture_cache, ctx.window); @@ -271,7 +303,7 @@ static bool initialize(void) { } /* input */ - input_state_init(&ctx.input, ctx.renderer); + input_state_init(&ctx.input); /* scripting */ /* @@ -296,8 +328,7 @@ static void clean_up(void) { input_state_deinit(&ctx.input); - arrfree(ctx.render_queue_sprites); - arrfree(ctx.render_queue_rectangles); + arrfree(ctx.render_queue_2d); textures_cache_deinit(&ctx.texture_cache); PHYSFS_deinit(); diff --git a/src/private/audio.h b/src/private/audio.h index d94589b..696cc76 100644 --- a/src/private/audio.h +++ b/src/private/audio.h @@ -44,7 +44,7 @@ struct audio_channel { }; -struct audio_channel_pair { +struct audio_channel_item { char *key; struct audio_channel value; }; diff --git a/src/private/rendering.h b/src/private/rendering.h index 2c1dee4..d15467c 100644 --- a/src/private/rendering.h +++ b/src/private/rendering.h @@ -2,22 +2,22 @@ #define PRIVATE_RENDERING_H #include "../rendering.h" +#include "../textures.h" #include "../util.h" #include +#include #include struct sprite_primitive { t_frect rect; t_color color; - double rotation; - char *path; - SDL_BlendMode blend_mode; - int atlas_index; - int layer; + float rotation; + t_texture_key texture_key; bool flip_x; bool flip_y; + bool blend; /* must be explicitly stated, textures having alpha channel isn't enough */ }; struct rect_primitive { @@ -31,4 +31,54 @@ struct circle_primitive { t_fvec2 position; }; +enum primitive_2d_type { + PRIMITIVE_2D_SPRITE, + PRIMITIVE_2D_RECT, + PRIMITIVE_2D_CIRCLE, +}; + +struct primitive_2d { + enum primitive_2d_type type; + + union { + struct sprite_primitive sprite; + struct rect_primitive rect; + struct circle_primitive circle; + }; +}; + +/* union for in-place recalculation of texture coordinates */ +union uncolored_space_triangle { + /* pending for sending, uvs are not final as texture atlases could update */ + struct uncolored_space_triangle_primitive { + t_fvec3 v0; + t_fvec2 uv0; /* in pixels */ + t_fvec3 v1; + t_fvec2 uv1; /* in pixels */ + t_fvec3 v2; + t_fvec2 uv2; /* in pixels */ + } primitive; + + /* structure that is passed in opengl vertex array */ + struct uncolored_space_triangle_payload { + t_fvec3 v0; + t_fvec2 uv0; + t_fvec3 v1; + t_fvec2 uv1; + t_fvec3 v2; + t_fvec2 uv2; + } payload; +}; + +/* batch of primitives with overlapping properties */ +struct mesh_batch { + GLuint buffer; /* server side storage */ + uint8_t *primitives; +}; + +struct mesh_batch_item { + t_texture_key key; + struct mesh_batch value; +}; + #endif diff --git a/src/private/textures.h b/src/private/textures.h new file mode 100644 index 0000000..3ea2ca3 --- /dev/null +++ b/src/private/textures.h @@ -0,0 +1,40 @@ +#ifndef PRIVATE_TEXTURES_H +#define PRIVATE_TEXTURES_H + +#include "../util.h" + +#include +#include +#include + +#include + +struct texture { + t_rect srcrect; /* position in atlas */ + SDL_Surface *data; /* original image data */ + int atlas_index; + GLuint loner_texture; /* stored directly for loners, == 0 means atlas_index should be used*/ +}; + + +struct texture_cache_item { + char *key; + struct texture value; +}; + + +struct texture_cache { + SDL_Window *window; /* from context */ + + struct texture_cache_item *hash; + + stbrp_node *node_buffer; /* used internally by stb_rect_pack */ + + SDL_Surface **atlas_surfaces; + GLuint *atlas_textures; /* shared by atlas textures */ + int atlas_index; /* atlas that is currently being built */ + + bool is_dirty; /* current atlas needs to be recreated */ +}; + +#endif diff --git a/src/rendering.c b/src/rendering.c index 734f679..e3e51ee 100644 --- a/src/rendering.c +++ b/src/rendering.c @@ -1,12 +1,15 @@ #include "private/rendering.h" +#include "rendering/sprites.h" +#include "rendering/triangles.h" +#include "rendering/circles.h" #include "context.h" #include "textures.h" #include -#include #include +#include -#include +#include #include @@ -14,57 +17,10 @@ void render_queue_clear(void) { /* since i don't intend to free the queues, */ /* it's faster and simpler to just "start over" */ /* and start overwriting the existing data */ - arrsetlen(ctx.render_queue_sprites, 0); - arrsetlen(ctx.render_queue_rectangles, 0); - arrsetlen(ctx.render_queue_circles, 0); -} + arrsetlen(ctx.render_queue_2d, 0); - -/* - * an implementation note: - * try to avoid doing expensive work in the push functions, - * because they will be called multiple times in the main loop - * before anything is really rendered - */ - -/* sprite */ -void push_sprite(char *path, t_frect rect) { - textures_load(&ctx.texture_cache, path); - - struct sprite_primitive sprite = { - .rect = rect, - .color = (t_color) { 255, 255, 255, 255 }, - .path = path, - .rotation = 0.0, - .blend_mode = SDL_BLENDMODE_BLEND, - .atlas_index = - textures_get_atlas_index(&ctx.texture_cache, path), - .layer = 0, - .flip_x = false, - .flip_y = false, - }; - - arrput(ctx.render_queue_sprites, sprite); -} - - -void push_sprite_ex(t_frect rect, t_push_sprite_args args) { - textures_load(&ctx.texture_cache, args.path); - - struct sprite_primitive sprite = { - .rect = rect, - .color = args.color, - .path = args.path, - .rotation = args.rotation, - .blend_mode = args.blend_mode, - .atlas_index = - textures_get_atlas_index(&ctx.texture_cache, args.path), - .layer = args.layer, - .flip_x = args.flip_x, - .flip_y = args.flip_y, - }; - - arrput(ctx.render_queue_sprites, sprite); + for (size_t i = 0; i < hmlenu(ctx.uncolored_mesh_batches); ++i) + arrsetlen(ctx.uncolored_mesh_batches[i].value.primitives, 0); } @@ -75,298 +31,153 @@ void push_rectangle(t_frect rect, t_color color) { .color = color, }; - arrput(ctx.render_queue_rectangles, rectangle); -} - - -/* circle */ -void push_circle(t_fvec2 position, float radius, t_color color) { - struct circle_primitive circle = { - .radius = radius, - .color = color, - .position = position, + struct primitive_2d primitive = { + .type = PRIMITIVE_2D_RECT, + .rect = rectangle, }; - arrput(ctx.render_queue_circles, circle); + arrput(ctx.render_queue_2d, primitive); } -/* compare functions for the sort in render_sprites */ -static int cmp_atlases(const void *a, const void *b) { - int index_a = ((const struct sprite_primitive *)a)->atlas_index; - int index_b = ((const struct sprite_primitive *)b)->atlas_index; +static void upload_quad_vertices(t_frect rect) { + /* client memory needs to be reachable on glDraw*, so */ + static float vertices[6 * 2]; - return (index_a > index_b) - (index_a < index_b); + vertices[0] = rect.x; vertices[1] = rect.y; + vertices[2] = rect.x; vertices[3] = rect.y + rect.h; + vertices[4] = rect.x + rect.w; vertices[5] = rect.y + rect.h; + vertices[6] = rect.x + rect.w; vertices[7] = rect.y + rect.h; + vertices[8] = rect.x + rect.w; vertices[9] = rect.y; + vertices[10] = rect.x; vertices[11] = rect.y; + + glVertexPointer(2, GL_FLOAT, 0, (void *)&vertices); } -static int cmp_blend_modes(const void *a, const void *b) { - SDL_BlendMode mode_a = ((const struct sprite_primitive *)a)->blend_mode; - SDL_BlendMode mode_b = ((const struct sprite_primitive *)b)->blend_mode; +static void render_rectangle(const struct rect_primitive *rectangle) { + glColor4ub(rectangle->color.r, rectangle->color.g, + rectangle->color.b, rectangle->color.a); - return (mode_a > mode_b) - (mode_a < mode_b); + glEnableClientState(GL_VERTEX_ARRAY); + upload_quad_vertices(rectangle->rect); + + glDrawArrays(GL_TRIANGLES, 0, 6); + glDisableClientState(GL_VERTEX_ARRAY); } -static int cmp_colors(const void *a, const void *b) { - t_color color_a = ((const struct sprite_primitive *)a)->color; - t_color color_b = ((const struct sprite_primitive *)b)->color; +static void render_2d(void) { + glEnable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + glDisable(GL_CULL_FACE); + glDisable(GL_ALPHA_TEST); + glEnable(GL_DEPTH_TEST); - /* check reds */ - if (color_a.r < color_b.r) - return -1; - else if (color_a.r > color_b.r) - return 1; + const size_t render_queue_len = arrlenu(ctx.render_queue_2d); - /* reds were equal, check greens */ - else if (color_a.g < color_b.g) - return -1; - else if (color_a.g > color_b.g) - return 1; + size_t batch_count = 0; - /* greens were equal, check blues */ - else if (color_a.b < color_b.b) - return -1; - else if (color_a.b > color_b.b) - return 1; + for (size_t i = 0; i < render_queue_len; ++i) { + const struct primitive_2d *current = &ctx.render_queue_2d[i]; - /* blues were equal, check alphas */ - else if (color_a.a < color_b.a) - return -1; - else if (color_a.a > color_b.a) - return 1; + switch (current->type) { + case PRIMITIVE_2D_SPRITE: { + const struct sprite_batch batch = + collect_sprite_batch(current, render_queue_len - i); - /* entirely equal */ - else - return 0; -} + glDepthRange((double)batch_count / UINT16_MAX, 1.0); + render_sprites(current, batch); -static int cmp_layers(const void *a, const void *b) { - int layer_a = ((const struct sprite_primitive *)a)->layer; - int layer_b = ((const struct sprite_primitive *)b)->layer; - - return (layer_a > layer_b) - (layer_a < layer_b); -} - - -/* necessary to allow the renderer to draw in batches in the best case */ -static void sort_sprites(struct sprite_primitive *sprites) { - size_t sprites_len = arrlenu(sprites); - qsort(sprites, sprites_len, sizeof *sprites, cmp_atlases); - qsort(sprites, sprites_len, sizeof *sprites, cmp_blend_modes); - qsort(sprites, sprites_len, sizeof *sprites, cmp_colors); - qsort(sprites, sprites_len, sizeof *sprites, cmp_layers); -} - - -static void render_sprite(struct sprite_primitive *sprite) { - SDL_Rect srcrect_value = { 0 }; - SDL_Rect *srcrect = &srcrect_value; - SDL_Texture *texture = NULL; - - /* loner */ - if (sprite->atlas_index == -1) { - srcrect = NULL; - texture = textures_get_loner(&ctx.texture_cache, sprite->path); - } else { - *srcrect = textures_get_srcrect(&ctx.texture_cache, sprite->path); - texture = textures_get_atlas(&ctx.texture_cache, sprite->atlas_index); - } - - SDL_RendererFlip flip = SDL_FLIP_NONE; - if (sprite->flip_x) - flip |= SDL_FLIP_HORIZONTAL; - if (sprite->flip_y) - flip |= SDL_FLIP_VERTICAL; - - SDL_SetTextureColorMod(texture, - sprite->color.r, - sprite->color.g, - sprite->color.b); - SDL_SetTextureAlphaMod(texture, sprite->color.a); - SDL_SetTextureBlendMode(texture, sprite->blend_mode); - - SDL_Rect rect = { - (int)sprite->rect.x, - (int)sprite->rect.y, - (int)sprite->rect.w, - (int)sprite->rect.h - }; - - SDL_RenderCopyEx(ctx.renderer, - texture, - srcrect, - &rect, - sprite->rotation, - NULL, - flip); -} - - -static void render_rectangle(struct rect_primitive *rectangle) { - SDL_SetRenderDrawColor(ctx.renderer, - rectangle->color.r, - rectangle->color.g, - rectangle->color.b, - rectangle->color.a); - - SDL_Rect rect = { - (int)rectangle->rect.x, - (int)rectangle->rect.y, - (int)rectangle->rect.w, - (int)rectangle->rect.h - }; - - SDL_RenderFillRect(ctx.renderer, &rect); - SDL_SetRenderDrawColor(ctx.renderer, 255, 255, 255, 255); -} - - -/* vertices_out and indices_out MUST BE FREED */ -static void create_circle_geometry(t_fvec2 position, - t_color color, - float radius, - size_t num_vertices, - SDL_Vertex **vertices_out, - int **indices_out) -{ - SDL_Vertex *vertices = cmalloc(sizeof *vertices * (num_vertices + 1)); - int *indices = cmalloc(sizeof *indices * (num_vertices * 3)); - - /* the angle (in radians) to rotate by on each iteration */ - float seg_rotation_angle = (360.0f / (float)num_vertices) * ((float)M_PI / 180); - - vertices[0].position.x = (float)position.x; - vertices[0].position.y = (float)position.y; - vertices[0].color.r = color.r; - vertices[0].color.g = color.g; - vertices[0].color.b = color.b; - vertices[0].color.a = color.a; - vertices[0].tex_coord = (SDL_FPoint){ 0, 0 }; - - /* this point will rotate around the center */ - float start_x = 0.0f - radius; - float start_y = 0.0f; - - for (size_t i = 1; i < num_vertices + 1; ++i) { - float final_seg_rotation_angle = (float)i * seg_rotation_angle; - - vertices[i].position.x = - cos(final_seg_rotation_angle) * start_x - - sin(final_seg_rotation_angle) * start_y; - vertices[i].position.y = - cos(final_seg_rotation_angle) * start_y + - sin(final_seg_rotation_angle) * start_x; - - vertices[i].position.x += position.x; - vertices[i].position.y += position.y; - - vertices[i].color.r = color.r; - vertices[i].color.g = color.g; - vertices[i].color.b = color.b; - vertices[i].color.a = color.a; - - vertices[i].tex_coord = (SDL_FPoint){ 0, 0 }; - - - size_t triangle_offset = 3 * (i - 1); - - /* center point index */ - indices[triangle_offset] = 0; - /* generated point index */ - indices[triangle_offset + 1] = (int)i; - - size_t index = (i + 1) % num_vertices; - if (index == 0) - index = num_vertices; - indices[triangle_offset + 2] = (int)index; - } - - *vertices_out = vertices; - *indices_out = indices; -} - - -static void render_circle(struct circle_primitive *circle) { - SDL_Vertex *vertices = NULL; - int *indices = NULL; - int num_vertices = (int)circle->radius; - - create_circle_geometry(circle->position, - circle->color, - circle->radius, - num_vertices, - &vertices, - &indices); - - SDL_RenderGeometry(ctx.renderer, NULL, - vertices, - num_vertices + 1, /* vertices + center vertex */ - indices, - num_vertices * 3); - - free(vertices); - free(indices); -} - - -static void render_sprites(void) { - if (ctx.texture_cache.is_dirty) - textures_update_current_atlas(&ctx.texture_cache); - - sort_sprites(ctx.render_queue_sprites); - - for (size_t i = 0; i < arrlenu(ctx.render_queue_sprites); ++i) { - render_sprite(&ctx.render_queue_sprites[i]); - } -} - - -static void render_rectangles(void) { - for (size_t i = 0; i < arrlenu(ctx.render_queue_rectangles); ++i) { - render_rectangle(&ctx.render_queue_rectangles[i]); - } -} - - -static void render_circles(void) { - for (size_t i = 0; i < arrlenu(ctx.render_queue_circles); ++i) { - render_circle(&ctx.render_queue_circles[i]); + i += batch.size - 1; ++batch_count; + break; + } + case PRIMITIVE_2D_RECT: + render_rectangle(¤t->rect); + break; + case PRIMITIVE_2D_CIRCLE: + render_circle(¤t->circle); + break; + } } } static void render_space(void) { - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0, -1, 1); + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + glDisable(GL_ALPHA_TEST); + glDepthFunc(GL_LESS); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); - glBegin(GL_TRIANGLES); - glColor4f(0.0, 1.0, 1.0, 1.0); - glVertex2f(300.0,210.0); - glVertex2f(340.0,215.0); - glVertex2f(320.0,250.0); - glEnd(); + /* solid white, no modulation */ + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glPopMatrix(); + for (size_t i = 0; i < hmlenu(ctx.uncolored_mesh_batches); ++i) { + draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches[i].value, + ctx.uncolored_mesh_batches[i].key); + } } void render(void) { + textures_update_atlas(&ctx.texture_cache); + + /* fit rendering context onto the resizable screen */ + if (ctx.window_size_has_changed) { + if ((float)ctx.window_w / (float)ctx.window_h > RENDER_BASE_RATIO) { + float ratio = (float)ctx.window_h / (float)RENDER_BASE_HEIGHT; + int w = (int)((float)RENDER_BASE_WIDTH * ratio); + glViewport( + ctx.window_w / 2 - w / 2, + 0, + w, + ctx.window_h + ); + } else { + float ratio = (float)ctx.window_w / (float)RENDER_BASE_WIDTH; + int h = (int)((float)RENDER_BASE_HEIGHT * ratio); + glViewport( + 0, + ctx.window_h / 2 - h / 2, + ctx.window_w, + h + ); + } + } + glClearColor((1.0f / 255) * 230, (1.0f / 255) * 230, (1.0f / 255) * 230, 1); - glClear(GL_COLOR_BUFFER_BIT); + glClear(GL_COLOR_BUFFER_BIT | + GL_DEPTH_BUFFER_BIT | + GL_STENCIL_BUFFER_BIT); - render_space(); - render_sprites(); - render_rectangles(); - render_circles(); + { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + render_space(); + } + + { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0, -1, 1); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + render_2d(); + } - // SDL_RenderPresent(ctx.renderer); - SDL_RenderFlush(ctx.renderer); SDL_GL_SwapWindow(ctx.window); } diff --git a/src/rendering.h b/src/rendering.h index 018f79f..7e48cd4 100644 --- a/src/rendering.h +++ b/src/rendering.h @@ -9,12 +9,11 @@ typedef struct push_sprite_args { char *path; - int layer; t_color color; - double rotation; - SDL_BlendMode blend_mode; + float rotation; bool flip_x; bool flip_y; + bool blend; } t_push_sprite_args; /* clears all render queues */ @@ -35,6 +34,36 @@ void push_rectangle(t_frect rect, t_color color); /* pushes a filled circle onto the circle render queue */ void push_circle(t_fvec2 position, float radius, t_color color); +/* pushes a textured 3d triangle onto the render queue */ +/* vertices are in absolute coordinates, relative to world origin */ +/* texture coordinates are in pixels */ +void unfurl_triangle(const char *path, + t_fvec3 v0, + t_fvec3 v1, + t_fvec3 v2, + t_shvec2 uv0, + t_shvec2 uv1, + t_shvec2 uv2); + +/* pushes a colored textured 3d triangle onto the render queue */ +// void unfurl_colored_triangle(const char *path, +// t_fvec3 v0, +// t_fvec3 v1, +// t_fvec3 v2, +// t_shvec2 uv0, +// t_shvec2 uv1, +// t_shvec2 uv2, +// t_color c0, +// t_color c1, +// t_color c2); + +// TODO: +// http://www.lighthouse3d.com/opengl/billboarding/index.php?billCheat2 +// void unfurl_billboard(const char *path, +// t_fvec3 position, +// t_fvec2 scaling, +// t_frect uvs); + /* renders the background, then the primitives in all render queues */ void render(void); diff --git a/src/rendering/circles.h b/src/rendering/circles.h new file mode 100644 index 0000000..d7d62c9 --- /dev/null +++ b/src/rendering/circles.h @@ -0,0 +1,132 @@ +/* a rendering.c mixin */ +#ifndef CIRCLES_H +#define CIRCLES_H + +#include "../util.h" +#include "../private/rendering.h" +#include "../context.h" + +#include +#include + +#include + + +void push_circle(t_fvec2 position, float radius, t_color color) { + struct circle_primitive circle = { + .radius = radius, + .color = color, + .position = position, + }; + + struct primitive_2d primitive = { + .type = PRIMITIVE_2D_CIRCLE, + .circle = circle, + }; + + arrput(ctx.render_queue_2d, primitive); +} + +/* TODO: caching and reuse scheme */ +/* vertices_out and indices_out MUST BE FREED */ +static void create_circle_geometry(t_fvec2 position, + t_color color, + float radius, + size_t num_vertices, + SDL_Vertex **vertices_out, + int **indices_out) +{ + SDL_Vertex *vertices = cmalloc(sizeof *vertices * (num_vertices + 1)); + int *indices = cmalloc(sizeof *indices * (num_vertices * 3)); + + /* the angle (in radians) to rotate by on each iteration */ + float seg_rotation_angle = (360.0f / (float)num_vertices) * ((float)M_PI / 180); + + vertices[0].position.x = (float)position.x; + vertices[0].position.y = (float)position.y; + vertices[0].color.r = color.r; + vertices[0].color.g = color.g; + vertices[0].color.b = color.b; + vertices[0].color.a = color.a; + vertices[0].tex_coord = (SDL_FPoint){ 0, 0 }; + + /* this point will rotate around the center */ + float start_x = 0.0f - radius; + float start_y = 0.0f; + + for (size_t i = 1; i < num_vertices + 1; ++i) { + float final_seg_rotation_angle = (float)i * seg_rotation_angle; + + vertices[i].position.x = + cosf(final_seg_rotation_angle) * start_x - + sinf(final_seg_rotation_angle) * start_y; + vertices[i].position.y = + cosf(final_seg_rotation_angle) * start_y + + sinf(final_seg_rotation_angle) * start_x; + + vertices[i].position.x += position.x; + vertices[i].position.y += position.y; + + vertices[i].color.r = color.r; + vertices[i].color.g = color.g; + vertices[i].color.b = color.b; + vertices[i].color.a = color.a; + + vertices[i].tex_coord = (SDL_FPoint){ 0, 0 }; + + + size_t triangle_offset = 3 * (i - 1); + + /* center point index */ + indices[triangle_offset] = 0; + /* generated point index */ + indices[triangle_offset + 1] = (int)i; + + size_t index = (i + 1) % num_vertices; + if (index == 0) + index = num_vertices; + indices[triangle_offset + 2] = (int)index; + } + + *vertices_out = vertices; + *indices_out = indices; +} + + +static void render_circle(const struct circle_primitive *circle) { + SDL_Vertex *vertices = NULL; + int *indices = NULL; + int num_vertices = (int)circle->radius; + + create_circle_geometry(circle->position, + circle->color, + circle->radius, + num_vertices, + &vertices, + &indices); + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, + GL_FLOAT, + sizeof (SDL_Vertex), + &vertices->position); + + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(4, + GL_UNSIGNED_BYTE, + sizeof (SDL_Vertex), + &vertices->color); + + glDrawElements(GL_TRIANGLES, + num_vertices * 3, + GL_UNSIGNED_INT, + indices); + + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + free(vertices); + free(indices); +} + +#endif diff --git a/src/rendering/quad_element_buffer.h b/src/rendering/quad_element_buffer.h new file mode 100644 index 0000000..4f832ee --- /dev/null +++ b/src/rendering/quad_element_buffer.h @@ -0,0 +1,41 @@ +/* a rendering.c mixin */ +#ifndef QUAD_ELEMENT_BUFFER_H +#define QUAD_ELEMENT_BUFFER_H + +#include + +#include + +#define QUAD_ELEMENT_BUFFER_LENGTH (65536 / 6) + +static void bind_quad_element_buffer(void) { + static GLuint buffer = 0; + + /* it's only generated once at runtime */ + if (buffer == 0) { + glGenBuffers(1, &buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, + QUAD_ELEMENT_BUFFER_LENGTH * 6, + NULL, + GL_STATIC_DRAW); + + uint16_t *const indices = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, + GL_WRITE_ONLY); + + for (uint16_t i = 0; i < QUAD_ELEMENT_BUFFER_LENGTH; ++i) { + indices[i * 6 + 0] = (uint16_t)(i * 4 + 0); + indices[i * 6 + 1] = (uint16_t)(i * 4 + 1); + indices[i * 6 + 2] = (uint16_t)(i * 4 + 2); + indices[i * 6 + 3] = (uint16_t)(i * 4 + 2); + indices[i * 6 + 4] = (uint16_t)(i * 4 + 3); + indices[i * 6 + 5] = (uint16_t)(i * 4 + 0); + } + + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + } else + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); +} + +#endif diff --git a/src/rendering/sprites.h b/src/rendering/sprites.h new file mode 100644 index 0000000..72ef96d --- /dev/null +++ b/src/rendering/sprites.h @@ -0,0 +1,332 @@ +/* a rendering.c mixin */ +#ifndef SPRITES_H +#define SPRITES_H + +#include "../textures.h" +#include "../rendering.h" +#include "../context.h" +#include "../util.h" +#include "quad_element_buffer.h" + +#include + +#include +#include + +/* interleaved vertex array data */ +/* TODO: use int16_t for uvs */ +/* TODO: int16_t could be used for positioning, but we would need to have more CPU calcs */ +struct sprite_primitive_payload { + /* upper-left */ + t_fvec2 v0; + t_fvec2 uv0; + t_color c0; + /* bottom-left */ + t_fvec2 v1; + t_fvec2 uv1; + t_color c1; + /* bottom-right */ + t_fvec2 v2; + t_fvec2 uv2; + t_color c2; + /* upper-right */ + t_fvec2 v3; + t_fvec2 uv3; + t_color c3; +}; + + +struct sprite_primitive_payload_without_color { + /* upper-left */ + t_fvec2 v0; + t_fvec2 uv0; + /* bottom-left */ + t_fvec2 v1; + t_fvec2 uv1; + /* bottom-right */ + t_fvec2 v2; + t_fvec2 uv2; + /* upper-right */ + t_fvec2 v3; + t_fvec2 uv3; +}; + +/* + * an implementation note: + * try to avoid doing expensive work in the push functions, + * because they will be called multiple times in the main loop + * before anything is really rendered + */ +/* TODO: it might make sense to infer alpha channel presence / meaningfulness for textures in atlas */ +/* so that they are rendered with no blend / batched in a way to reduce overdraw automatically */ +void push_sprite(char *path, t_frect rect) { + struct sprite_primitive sprite = { + .rect = rect, + .color = (t_color) { 255, 255, 255, 255 }, + .rotation = 0.0, + .texture_key = textures_get_key(&ctx.texture_cache, path), + .flip_x = false, + .flip_y = false, + .blend = true, + }; + + struct primitive_2d primitive = { + .type = PRIMITIVE_2D_SPRITE, + .sprite = sprite, + }; + + arrput(ctx.render_queue_2d, primitive); +} + + +void push_sprite_ex(t_frect rect, t_push_sprite_args args) { + struct sprite_primitive sprite = { + .rect = rect, + .color = args.color, + .rotation = args.rotation, + .texture_key = textures_get_key(&ctx.texture_cache, args.path), + .flip_x = args.flip_x, + .flip_y = args.flip_y, + .blend = args.blend, + }; + + struct primitive_2d primitive = { + .type = PRIMITIVE_2D_SPRITE, + .sprite = sprite, + }; + + arrput(ctx.render_queue_2d, primitive); +} + + +static struct sprite_batch { + int atlas_id; + size_t size; /* how many primitives are in current batch */ + bool blend; /* whether it's blended or not */ + bool constant_colored; /* whether colored batch is uniformly colored */ +} collect_sprite_batch(const struct primitive_2d *primitives, size_t len) { + /* assumes that first primitive is already a sprite */ + struct sprite_batch batch = { + .atlas_id = + textures_get_atlas_id(&ctx.texture_cache, primitives[0].sprite.texture_key), + .blend = primitives[0].sprite.blend, + .constant_colored = true, + }; + + const t_color uniform_color = primitives[0].sprite.color; + + /* batch size is clamped so that reallocated short indices could be used */ + if (len >= QUAD_ELEMENT_BUFFER_LENGTH) + len = QUAD_ELEMENT_BUFFER_LENGTH; + + for (size_t i = 0; i < len; ++i) { + const struct primitive_2d *const current = &primitives[i]; + + /* don't touch things other than sprites */ + if (current->type != PRIMITIVE_2D_SPRITE) + break; + + /* only collect the same blend modes */ + if (current->sprite.blend != batch.blend) + break; + + /* only collect the same texture atlases */ + if (textures_get_atlas_id(&ctx.texture_cache, current->sprite.texture_key) + != batch.atlas_id) + break; + + /* if all are modulated the same we can skip sending the color data */ + if (batch.constant_colored && (current->sprite.color.r != uniform_color.r || + current->sprite.color.g != uniform_color.g || + current->sprite.color.b != uniform_color.b || + current->sprite.color.a != uniform_color.a )) + batch.constant_colored = false; + + ++batch.size; + } + + return batch; +} + + +/* assumes that orthogonal matrix setup is done already */ +static void render_sprites(const struct primitive_2d primitives[], + const struct sprite_batch batch) +{ + /* single vertex array is used for every batch with NULL glBufferData() trick at the end */ + static GLuint vertex_array = 0; + if (vertex_array == 0) + glGenBuffers(1, &vertex_array); + + if (batch.blend) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthFunc(GL_ALWAYS); + } else { + glDisable(GL_BLEND); + glDepthFunc(GL_LESS); + } + + size_t payload_size; + if (!batch.constant_colored) + payload_size = sizeof (struct sprite_primitive_payload); + else + payload_size = sizeof (struct sprite_primitive_payload_without_color); + + glBindBuffer(GL_ARRAY_BUFFER, vertex_array); + glBufferData(GL_ARRAY_BUFFER, + payload_size * batch.size, + NULL, + GL_STREAM_DRAW); + + const t_rect dims = + textures_get_dims(&ctx.texture_cache, primitives->sprite.texture_key); + + /* vertex population over a mapped buffer */ + { + /* TODO: check errors, ensure alignment ? */ + void *const payload = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + + for (size_t i = 0; i < batch.size; ++i) { + const size_t cur = !batch.blend ? batch.size - i - 1: i; /* render opaques front to back */ + const struct sprite_primitive sprite = primitives[cur].sprite; + + const t_rect srcrect = + textures_get_srcrect(&ctx.texture_cache, primitives[cur].sprite.texture_key); + + const float wr = (float)srcrect.w / (float)dims.w; + const float hr = (float)srcrect.h / (float)dims.h; + const float xr = (float)srcrect.x / (float)dims.w; + const float yr = (float)srcrect.y / (float)dims.h; + + t_fvec2 uv0 = { xr + wr * sprite.flip_x, yr + hr * sprite.flip_y }; + t_fvec2 uv1 = { xr + wr * sprite.flip_x, yr + hr * !sprite.flip_y }; + t_fvec2 uv2 = { xr + wr * !sprite.flip_x, yr + hr * !sprite.flip_y }; + t_fvec2 uv3 = { xr + wr * !sprite.flip_x, yr + hr * sprite.flip_y }; + + t_fvec2 v0, v1, v2, v3; + + /* todo: fast PI/2 degree divisible rotations? */ + if (sprite.rotation == 0.0f) { + /* non-rotated case */ + + v0 = (t_fvec2){ sprite.rect.x, sprite.rect.y }; + v1 = (t_fvec2){ sprite.rect.x, sprite.rect.y + sprite.rect.h }; + v2 = (t_fvec2){ sprite.rect.x + sprite.rect.w, sprite.rect.y + sprite.rect.h }; + v3 = (t_fvec2){ sprite.rect.x + sprite.rect.w, sprite.rect.y }; + + } else if (sprite.rect.w == sprite.rect.h) { + /* rotated square case */ + + const t_fvec2 c = frect_center(sprite.rect); + const t_fvec2 t = fast_cossine(sprite.rotation + (float)M_PI_4); + const t_fvec2 d = { + .x = t.x * sprite.rect.w * (float)M_SQRT1_2, + .y = t.y * sprite.rect.h * (float)M_SQRT1_2, + }; + + v0 = (t_fvec2){ c.x - d.x, c.y - d.y }; + v1 = (t_fvec2){ c.x - d.y, c.y + d.x }; + v2 = (t_fvec2){ c.x + d.x, c.y + d.y }; + v3 = (t_fvec2){ c.x + d.y, c.y - d.x }; + + } else { + /* rotated non-square case*/ + + CRY("Rotation", "Unimplemented"); + } + + if (!batch.constant_colored) + ((struct sprite_primitive_payload *)payload)[i] = (struct sprite_primitive_payload) { + .v0 = v0, + .v1 = v1, + .v2 = v2, + .v3 = v3, + + .uv0 = uv0, + .uv1 = uv1, + .uv2 = uv2, + .uv3 = uv3, + + /* equal for all (flat shaded) */ + .c0 = sprite.color, + .c1 = sprite.color, + .c2 = sprite.color, + .c3 = sprite.color, + }; + else + ((struct sprite_primitive_payload_without_color *)payload)[i] = (struct sprite_primitive_payload_without_color) { + .v0 = v0, + .v1 = v1, + .v2 = v2, + .v3 = v3, + + .uv0 = uv0, + .uv1 = uv1, + .uv2 = uv2, + .uv3 = uv3, + }; + } + + glUnmapBuffer(GL_ARRAY_BUFFER); + } + + GLsizei off; + GLsizei voff; + GLsizei uvoff; + + if (!batch.constant_colored) { + off = offsetof(struct sprite_primitive_payload, v1); + voff = offsetof(struct sprite_primitive_payload, v0); + uvoff = offsetof(struct sprite_primitive_payload, uv0); + } else { + off = offsetof(struct sprite_primitive_payload_without_color, v1); + voff = offsetof(struct sprite_primitive_payload_without_color, v0); + uvoff = offsetof(struct sprite_primitive_payload_without_color, uv0); + } + + /* vertex specification */ + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, + GL_FLOAT, + off, + (void *)(size_t)voff); + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glClientActiveTexture(GL_TEXTURE0); + glTexCoordPointer(2, + GL_FLOAT, + off, + (void *)(size_t)uvoff); + + if (!batch.constant_colored) { + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(4, + GL_UNSIGNED_BYTE, + off, + (void *)offsetof(struct sprite_primitive_payload, c0)); + } else + glColor4ub(primitives[0].sprite.color.r, + primitives[0].sprite.color.g, + primitives[0].sprite.color.b, + primitives[0].sprite.color.a); + + textures_bind(&ctx.texture_cache, primitives->sprite.texture_key, GL_TEXTURE_2D); + + bind_quad_element_buffer(); + + glDrawElements(GL_TRIANGLES, 6 * (GLsizei)batch.size, GL_UNSIGNED_SHORT, NULL); + + /* clear the state */ + glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + glBindTexture(GL_TEXTURE_2D, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +#endif diff --git a/src/rendering/triangles.h b/src/rendering/triangles.h new file mode 100644 index 0000000..8e88b11 --- /dev/null +++ b/src/rendering/triangles.h @@ -0,0 +1,117 @@ +/* a rendering.c mixin */ +#ifndef TRIANGLES_H +#define TRIANGLES_H + +#include "../textures.h" +#include "../context.h" + +#include + +/* TODO: automatic handling of repeating textures */ +/* for that we could allocate a loner texture */ +void unfurl_triangle(const char *path, + t_fvec3 v0, + t_fvec3 v1, + t_fvec3 v2, + t_shvec2 uv0, + t_shvec2 uv1, + t_shvec2 uv2) +{ + const t_texture_key texture_key = textures_get_key(&ctx.texture_cache, path); + + struct mesh_batch_item *batch_p = hmgetp_null(ctx.uncolored_mesh_batches, texture_key); + if (!batch_p) { + struct mesh_batch item = {0}; + hmput(ctx.uncolored_mesh_batches, texture_key, item); + batch_p = &ctx.uncolored_mesh_batches[hmlenu(ctx.uncolored_mesh_batches) - 1]; /* TODO: can last index be used? */ + } + + union uncolored_space_triangle triangle = { .primitive = { + .v0 = v0, + .v1 = v1, + .v2 = v2, + .uv1 = m_to_fvec2(uv1), + .uv0 = m_to_fvec2(uv0), + .uv2 = m_to_fvec2(uv2), + }}; + + union uncolored_space_triangle *triangles = + (union uncolored_space_triangle *)batch_p->value.primitives; + arrpush(triangles, triangle); + batch_p->value.primitives = (uint8_t *)triangles; +} + + +static void draw_uncolored_space_traingle_batch(struct mesh_batch *batch, + t_texture_key texture_key) +{ + size_t primitives_len = arrlenu(batch->primitives); + + if (primitives_len == 0) + return; + + /* create vertex array object */ + if (batch->buffer == 0) + glGenBuffers(1, &batch->buffer); + + /* TODO: try using mapped buffers while building batches instead? */ + /* this way we could skip client side copy that is kept until commitment */ + /* alternatively we could commit glBufferSubData based on a threshold */ + + /* update pixel-based uvs to correspond with texture atlases */ + for (size_t i = 0; i < primitives_len; ++i) { + struct uncolored_space_triangle_payload *payload = + &((union uncolored_space_triangle *)batch->primitives)[i].payload; + + t_rect srcrect = textures_get_srcrect(&ctx.texture_cache, texture_key); + t_rect dims = textures_get_dims(&ctx.texture_cache, texture_key); + + const float wr = (float)srcrect.w / (float)dims.w; + const float hr = (float)srcrect.h / (float)dims.h; + const float xr = (float)srcrect.x / (float)dims.w; + const float yr = (float)srcrect.y / (float)dims.h; + + payload->uv0.x = xr + ((float)payload->uv0.x / (float)srcrect.w) * wr; + payload->uv0.y = yr + ((float)payload->uv0.y / (float)srcrect.h) * hr; + payload->uv1.x = xr + ((float)payload->uv1.x / (float)srcrect.w) * wr; + payload->uv1.y = yr + ((float)payload->uv1.y / (float)srcrect.h) * hr; + payload->uv2.x = xr + ((float)payload->uv2.x / (float)srcrect.w) * wr; + payload->uv2.y = yr + ((float)payload->uv2.y / (float)srcrect.h) * hr; + } + + textures_bind(&ctx.texture_cache, texture_key, GL_TEXTURE_2D); + + glBindBuffer(GL_ARRAY_BUFFER, batch->buffer); + + /* upload batched data */ + glBufferData(GL_ARRAY_BUFFER, + primitives_len * sizeof (struct uncolored_space_triangle_payload), + batch->primitives, + GL_STREAM_DRAW); + + /* vertex specification*/ + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, + GL_FLOAT, + offsetof(struct uncolored_space_triangle_payload, v1), + (void *)offsetof(struct uncolored_space_triangle_payload, v0)); + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glClientActiveTexture(GL_TEXTURE0); + glTexCoordPointer(2, + GL_FLOAT, + offsetof(struct uncolored_space_triangle_payload, v1), + (void *)offsetof(struct uncolored_space_triangle_payload, uv0)); + + /* commit for drawing */ + glDrawArrays(GL_TRIANGLES, 0, 3 * (int)primitives_len); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + /* invalidate the buffer immediately */ + glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +#endif diff --git a/src/system/linux/elf.c b/src/system/linux/elf.c new file mode 100644 index 0000000..9696c2f --- /dev/null +++ b/src/system/linux/elf.c @@ -0,0 +1,68 @@ +#include "elf.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +bool infer_elf_section_bounds(const char *const restrict name, + const char **restrict vm_start, + const char **restrict vm_end) +{ + bool result = false; + char buf[PATH_MAX]; + ssize_t l = readlink("/proc/self/exe", buf, PATH_MAX); + if (l == -1) + goto ERR_CANT_READLINK; + buf[l] = 0; /* readlink() doesn't write a terminator */ + + int elf = open(buf, O_RDONLY); + if (elf == -1) + goto ERR_CANT_OPEN_SELF; + + /* elf header */ + Elf64_Ehdr ehdr; + read(elf, &ehdr, sizeof ehdr); + if (ehdr.e_ident[EI_MAG0] != ELFMAG0 || + ehdr.e_ident[EI_MAG1] != ELFMAG1 || + ehdr.e_ident[EI_MAG2] != ELFMAG2 || + ehdr.e_ident[EI_MAG3] != ELFMAG3) + goto ERR_NOT_ELF; + + /* section header string table */ + Elf64_Shdr shstrdr; + lseek(elf, ehdr.e_shoff + ehdr.e_shstrndx * sizeof (Elf64_Shdr), SEEK_SET); + read(elf, &shstrdr, sizeof shstrdr); + char *sh = malloc(shstrdr.sh_size); + lseek(elf, shstrdr.sh_offset, SEEK_SET); + read(elf, sh, shstrdr.sh_size); + + /* walk sections searching for needed name */ + lseek(elf, ehdr.e_shoff, SEEK_SET); + for (size_t s = 0; s < ehdr.e_shnum; ++s) { + Elf64_Shdr shdr; + read(elf, &shdr, sizeof shdr); + + if (strcmp(&sh[shdr.sh_name], name) == 0) { + result = true; + *vm_start = getauxval(AT_ENTRY) - ehdr.e_entry + (char *)shdr.sh_addr; + *vm_end = getauxval(AT_ENTRY) - ehdr.e_entry + (char *)shdr.sh_addr + shdr.sh_size; + break; + } + } + + free(sh); + +ERR_NOT_ELF: + close(elf); + +ERR_CANT_OPEN_SELF: +ERR_CANT_READLINK: + return result; +} diff --git a/src/system/linux/elf.h b/src/system/linux/elf.h new file mode 100644 index 0000000..23790d7 --- /dev/null +++ b/src/system/linux/elf.h @@ -0,0 +1,10 @@ +#ifndef ELF_H +#define ELF_H + +#include + +bool infer_elf_section_bounds(const char *restrict name, + const char **restrict vm_start, + const char **restrict vm_end); + +#endif diff --git a/src/textures.c b/src/textures.c index ef26380..5ba34cf 100644 --- a/src/textures.c +++ b/src/textures.c @@ -1,6 +1,7 @@ -#include "textures.h" +#include "private/textures.h" #include "config.h" #include "util.h" +#include "textures.h" #include #include @@ -9,13 +10,12 @@ #include #include -#include #include #include #include -static SDL_Surface *image_to_surface(char *path) { +static SDL_Surface *image_to_surface(const char *path) { SDL_RWops *handle = PHYSFSRWOPS_openRead(path); if (handle == NULL) goto fail; @@ -35,6 +35,24 @@ fail: } +static GLuint new_gl_texture(void) { + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glBindTexture(GL_TEXTURE_2D, 0); + return texture; +} + + /* adds a new, blank atlas surface to the cache */ static void add_new_atlas(struct texture_cache *cache) { SDL_PixelFormat *native_format = @@ -57,17 +75,58 @@ static void add_new_atlas(struct texture_cache *cache) { a_mask); SDL_FreeFormat(native_format); - SDL_SetSurfaceRLE(new_atlas, true); + SDL_SetSurfaceBlendMode(new_atlas, SDL_BLENDMODE_NONE); + SDL_SetSurfaceRLE(new_atlas, true); arrput(cache->atlas_surfaces, new_atlas); - - SDL_Texture *new_atlas_texture = - SDL_CreateTextureFromSurface(cache->renderer, new_atlas); - arrput(cache->atlas_textures, new_atlas_texture); + arrput(cache->atlas_textures, new_gl_texture()); +} + + +static void upload_texture_from_surface(GLuint texture, SDL_Surface *surface) { + Uint32 rmask, gmask, bmask, amask; + + glBindTexture(GL_TEXTURE_2D, texture); + + // glPixelStorei(GL_PACK_ALIGNMENT, 1); + + #if SDL_BYTEORDER == SDL_BIG_ENDIAN + rmask = 0xff000000; + gmask = 0x00ff0000; + bmask = 0x0000ff00; + amask = 0x000000ff; + #else + rmask = 0x000000ff; + gmask = 0x0000ff00; + bmask = 0x00ff0000; + amask = 0xff000000; + #endif + + /* TODO: don't do it if format is compatible */ + SDL_Surface* intermediate = SDL_CreateRGBSurface(0, + surface->w, surface->h, 32, rmask, gmask, bmask, amask); + SDL_BlitSurface(surface, NULL, intermediate, NULL); + SDL_LockSurface(intermediate); + + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA8, + surface->w, + surface->h, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + intermediate->pixels); + + SDL_UnlockSurface(intermediate); + SDL_FreeSurface(intermediate); + + glBindTexture(GL_TEXTURE_2D, 0); } static void recreate_current_atlas_texture(struct texture_cache *cache) { /* TODO: figure out if SDL_UpdateTexture alone is faster than blitting */ + /* TODO: should surfaces be freed after they cannot be referenced in atlas builing? */ SDL_Surface *atlas_surface = cache->atlas_surfaces[cache->atlas_index]; /* clear */ @@ -75,22 +134,27 @@ static void recreate_current_atlas_texture(struct texture_cache *cache) { /* blit the texture surfaces onto the atlas */ for (size_t i = 0; i < shlenu(cache->hash); ++i) { + /* skip all that aren't part of currently built one */ if (cache->hash[i].value.atlas_index != cache->atlas_index) continue; + /* skip loners */ + if (cache->hash[i].value.loner_texture != 0) + continue; + SDL_BlitSurface(cache->hash[i].value.data, NULL, atlas_surface, - &cache->hash[i].value.srcrect); + &(SDL_Rect){ + .x = cache->hash[i].value.srcrect.x, + .y = cache->hash[i].value.srcrect.y, + .w = cache->hash[i].value.srcrect.w, + .h = cache->hash[i].value.srcrect.h, + }); } /* texturize it! */ - SDL_LockSurface(atlas_surface); - SDL_UpdateTexture(cache->atlas_textures[cache->atlas_index], - NULL, - atlas_surface->pixels, - atlas_surface->pitch); - SDL_UnlockSurface(atlas_surface); + upload_texture_from_surface(cache->atlas_textures[cache->atlas_index], atlas_surface); } @@ -98,7 +162,10 @@ static void recreate_current_atlas_texture(struct texture_cache *cache) { static stbrp_rect *create_rects_from_cache(struct texture_cache *cache) { stbrp_rect *rects = NULL; for (size_t i = 0; i < shlenu(cache->hash); ++i) { - SDL_Surface *surface_data = cache->hash[i].value.data; + if (cache->hash[i].value.loner_texture != 0) + continue; + + const SDL_Surface *surface_data = cache->hash[i].value.data; stbrp_rect new_rect = { .w = surface_data->w, .h = surface_data->h, @@ -159,7 +226,7 @@ static bool update_rects(struct texture_cache *cache, stbrp_rect *rects, stbrp_r /* updates the atlas location of every rect in the cache */ static void update_texture_rects_in_atlas(struct texture_cache *cache, stbrp_rect *rects) { for (size_t i = 0; i < arrlenu(rects); ++i) { - cache->hash[i].value.srcrect = (SDL_Rect) { + cache->hash[i].value.srcrect = (t_rect) { .x = rects[i].x, .y = rects[i].y, .w = rects[i].w, @@ -171,9 +238,7 @@ static void update_texture_rects_in_atlas(struct texture_cache *cache, stbrp_rec void textures_cache_init(struct texture_cache *cache, SDL_Window *window) { cache->window = window; - cache->renderer = SDL_GetRenderer(window); sh_new_arena(cache->hash); - sh_new_arena(cache->loner_hash); cache->node_buffer = cmalloc(sizeof *cache->node_buffer * TEXTURE_ATLAS_SIZE); @@ -185,7 +250,7 @@ void textures_cache_init(struct texture_cache *cache, SDL_Window *window) { void textures_cache_deinit(struct texture_cache *cache) { /* free atlas textures */ for (size_t i = 0; i < arrlenu(cache->atlas_textures); ++i) { - SDL_DestroyTexture(cache->atlas_textures[i]); + glDeleteTextures(1, &cache->atlas_textures[i]); } arrfree(cache->atlas_textures); @@ -201,11 +266,6 @@ void textures_cache_deinit(struct texture_cache *cache) { } shfree(cache->hash); - for (size_t i = 0; i < shlenu(cache->loner_hash); ++i) { - SDL_FreeSurface(cache->loner_hash[i].value.data); - } - shfree(cache->loner_hash); - free(cache->node_buffer); } @@ -230,35 +290,39 @@ void textures_dump_atlases(struct texture_cache *cache) { IMG_SavePNG_RW(cache->atlas_surfaces[i], handle, true); log_info("Dumped atlas %s", buf); } - - size_t num_loners = shlenu(cache->loner_hash); - log_info("%zd atlases dumped. %zd loners left undumped.", i, num_loners); } -void textures_load(struct texture_cache *cache, char *path) { +static t_texture_key textures_load(struct texture_cache *cache, const char *path) { /* no need to do anything if it was loaded already */ - if (shgeti(cache->hash, path) >= 0 || shgeti(cache->loner_hash, path) >= 0) - return; + const ptrdiff_t i = shgeti(cache->hash, path); + if (i >= 0) + return (t_texture_key){ (uint16_t)i }; SDL_Surface *surface = image_to_surface(path); - struct texture new_texture; + struct texture new_texture = {0}; new_texture.data = surface; /* it's a "loner texture," it doesn't fit in an atlas so it's not in one */ - if (surface->w > TEXTURE_ATLAS_SIZE || surface->h > TEXTURE_ATLAS_SIZE) { - new_texture.loner_data = SDL_CreateTextureFromSurface(cache->renderer, surface); - new_texture.atlas_index = -1; - shput(cache->loner_hash, path, new_texture); + if (surface->w >= TEXTURE_ATLAS_SIZE || surface->h >= TEXTURE_ATLAS_SIZE) { + new_texture.loner_texture = new_gl_texture(); + upload_texture_from_surface(new_texture.loner_texture, surface); + new_texture.srcrect = (t_rect) { .w = surface->w, .h = surface->h }; + shput(cache->hash, path, new_texture); + return (t_texture_key){ (uint16_t)shgeti(cache->hash, path) }; } else { new_texture.atlas_index = cache->atlas_index; shput(cache->hash, path, new_texture); cache->is_dirty = true; + return (t_texture_key){ (uint16_t)shgeti(cache->hash, path) }; } } -void textures_update_current_atlas(struct texture_cache *cache) { +void textures_update_atlas(struct texture_cache *cache) { + if (!cache->is_dirty) + return; + /* this function makes a lot more sense if you read stb_rect_pack.h */ stbrp_context pack_ctx; /* target info */ stbrp_init_target(&pack_ctx, @@ -295,63 +359,119 @@ void textures_update_current_atlas(struct texture_cache *cache) { recreate_current_atlas_texture(cache); cache->is_dirty = false; - + arrfree(rects); } +/* EXPERIMANTAL: LIKELY TO BE REMOVED! */ +#ifdef __linux__ /* use rodata elf section for fast lookups of repeating textures */ -SDL_Rect textures_get_srcrect(struct texture_cache *cache, char *path) { - struct texture_cache_item *texture = shgetp_null(cache->hash, path); - if (texture == NULL) { - CRY("Texture lookup failed.", - "Tried to get texture that isn't loaded."); - return (SDL_Rect){ 0, 0, 0, 0 }; - } - return texture->value.srcrect; -} +#include "system/linux/elf.h" +static const char *rodata_start; +static const char *rodata_stop; -int textures_get_atlas_index(struct texture_cache *cache, char *path) { - struct texture_cache_item *texture = shgetp_null(cache->hash, path); +t_texture_key textures_get_key(struct texture_cache *cache, const char *path) { + static const char *last_path = NULL; + static t_texture_key last_texture; + static struct ptr_to_texture { + const void *key; + t_texture_key value; + } *ptr_to_texture; - /* it might be a loner texture */ - if (texture == NULL) { - texture = shgetp_null(cache->loner_hash, path); + if (rodata_stop == NULL) + if (!infer_elf_section_bounds(".rodata", &rodata_start, &rodata_stop)) + CRY("Section inference", ".rodata section lookup failed"); - /* never mind it's just not there at all */ - if (texture == NULL) { - CRY("Texture atlas index lookup failed.", - "Tried to get atlas index of texture that isn't loaded."); - return INT_MIN; + /* the fastest path */ + if (path == last_path) + return last_texture; + else { + /* moderately fast path, by pointer hashing */ + const ptrdiff_t texture = hmgeti(ptr_to_texture, path); + if (texture != -1) { + if (path >= rodata_start && path < rodata_stop) + last_path = path; + last_texture = ptr_to_texture[texture].value; + return last_texture; } } - return texture->value.atlas_index; + /* try loading */ + last_texture = textures_load(cache, path); + hmput(ptr_to_texture, path, last_texture); + + if (path >= rodata_start && path < rodata_stop) + last_path = path; + + return last_texture; } +#else +t_texture_key textures_get_key(struct texture_cache *cache, const char *path) { + /* hash tables are assumed to be stable, so we just return indices */ + const ptrdiff_t texture = shgeti(cache->hash, path); -SDL_Texture *textures_get_atlas(struct texture_cache *cache, int index) { - /* out of bounds */ - if (arrlen(cache->atlas_textures) < index + 1 || index < 0) - return NULL; - - return cache->atlas_textures[index]; + /* load it if it isn't */ + if (texture == -1) { + return textures_load(cache, path); + } else + return (t_texture_key){ (uint16_t)texture }; } +#endif /* generic implementation of textures_get_key() */ -SDL_Texture *textures_get_loner(struct texture_cache *cache, char *path) { - struct texture_cache_item *texture = shgetp_null(cache->loner_hash, path); - - if (texture == NULL) { - CRY("Loner texture lookup failed.", - "Tried to get texture that isn't loaded."); - return NULL; +int32_t textures_get_atlas_id(const struct texture_cache *cache, t_texture_key key) { + if (m_texture_key_is_valid(key)) { + if (cache->hash[key.id].value.loner_texture != 0) + return -cache->hash[key.id].value.loner_texture; + else + return cache->hash[key.id].value.atlas_index; + } else { + CRY("Texture lookup failed.", + "Tried to get atlas id that isn't loaded."); + return 0; } +} - return texture->value.loner_data; +t_rect textures_get_srcrect(const struct texture_cache *cache, t_texture_key key) { + if (m_texture_key_is_valid(key)) { + return cache->hash[key.id].value.srcrect; + } else { + CRY("Texture lookup failed.", + "Tried to get texture that isn't loaded."); + return (t_rect){ 0, 0, 0, 0 }; + } } -size_t textures_get_num_atlases(struct texture_cache *cache) { +t_rect textures_get_dims(const struct texture_cache *cache, t_texture_key key) { + if (m_texture_key_is_valid(key)) { + if (cache->hash[key.id].value.loner_texture != 0) + return cache->hash[key.id].value.srcrect; + else + return (t_rect){ .w = TEXTURE_ATLAS_SIZE, .h = TEXTURE_ATLAS_SIZE }; + } else { + CRY("Texture lookup failed.", + "Tried to get texture that isn't loaded."); + return (t_rect){ 0, 0, 0, 0 }; + } +} + + +void textures_bind(const struct texture_cache *cache, t_texture_key key, GLenum target) { + if (m_texture_key_is_valid(key)) { + if (cache->hash[key.id].value.loner_texture == 0) + glBindTexture(target, cache->atlas_textures[cache->hash[key.id].value.atlas_index]); + else + glBindTexture(target, cache->hash[key.id].value.loner_texture); + } else if (key.id == 0) { + CRY("Texture binding failed.", + "Tried to get texture that isn't loaded."); + } +} + + +size_t textures_get_num_atlases(const struct texture_cache *cache) { return cache->atlas_index + 1; } diff --git a/src/textures.h b/src/textures.h index bff3cbb..bf9bf8d 100644 --- a/src/textures.h +++ b/src/textures.h @@ -1,46 +1,17 @@ #ifndef TEXTURES_H #define TEXTURES_H +#include "private/textures.h" +#include "util.h" #include -#include +#include -#include - - -struct texture { - SDL_Rect srcrect; /* position in atlas */ - SDL_Surface *data; /* original image data */ - SDL_Texture *loner_data; /* loner textures store their data directly */ - int atlas_index; /* which atlas the texture is in */ - int8_t layer; -}; - - -struct texture_cache_item { - char *key; - struct texture value; -}; - - -/* use the public API to create and manipulate instances of this structure */ -struct texture_cache { - /* from context */ - SDL_Window *window; - SDL_Renderer *renderer; - - struct texture_cache_item *hash; - struct texture_cache_item *loner_hash; - - stbrp_node *node_buffer; /* used internally by stb_rect_pack */ - - SDL_Surface **atlas_surfaces; - SDL_Texture **atlas_textures; - int atlas_index; - - bool is_dirty; /* current atlas needs to be recreated */ -}; +/* type safe structure for persistent texture handles */ +typedef struct { uint16_t id; } t_texture_key; +/* tests whether given key structure corresponds to any texture */ +#define m_texture_key_is_valid(p_key) ((p_key).id != (uint16_t)-1) void textures_cache_init(struct texture_cache *cache, SDL_Window *window); void textures_cache_deinit(struct texture_cache *cache); @@ -51,28 +22,30 @@ void textures_dump_atlases(struct texture_cache *cache); /* loads an image if it isn't in the cache, otherwise a no-op. */ /* can be called from anywhere at any time after init, useful if you want to */ /* preload textures you know will definitely be used */ -void textures_load(struct texture_cache *cache, char *path); +// void textures_load(struct texture_cache *cache, const char *path); -/* repacks the current texture atlas based on the texture cache */ -void textures_update_current_atlas(struct texture_cache *cache); +/* repacks the current texture atlas based on the texture cache if needed */ +/* any previously returned srcrect results are invalidated after that */ +/* call it every time before rendering */ +void textures_update_atlas(struct texture_cache *cache); -/* returns a rect in a texture cache atlas based on a path, for drawing */ -/* if the texture is not found, returns a zero-filled rect (so check w or h) */ -SDL_Rect textures_get_srcrect(struct texture_cache *cache, char *path); +/* returns a persistent handle to some texture in cache, loading it if needed */ +/* check the result with m_texture_key_is_valid() */ +t_texture_key textures_get_key(struct texture_cache *cache, const char *path); -/* returns which atlas the texture in the path is in, starting from 0 */ -/* if the texture is not found, returns INT_MIN */ -int textures_get_atlas_index(struct texture_cache *cache, char *path); +/* returns a rect in a texture cache of the given key */ +t_rect textures_get_srcrect(const struct texture_cache *cache, t_texture_key key); -/* returns a pointer to the atlas at `index` */ -/* if the index is out of bounds, returns NULL. */ -/* you can get the index via texture_get_atlas_index */ -SDL_Texture *textures_get_atlas(struct texture_cache *cache, int index); +/* returns a rect of dimensions of the whole texture (whole atlas) */ +t_rect textures_get_dims(const struct texture_cache *cache, t_texture_key key); -SDL_Texture *textures_get_loner(struct texture_cache *cache, char *path); +/* returns an identifier that is equal for all textures placed in the same atlas */ +int32_t textures_get_atlas_id(const struct texture_cache *cache, t_texture_key key); + +/* binds atlas texture in opengl state */ +void textures_bind(const struct texture_cache *cache, t_texture_key key, GLenum target); /* returns the number of atlases in the cache */ -size_t textures_get_num_atlases(struct texture_cache *cache); - +size_t textures_get_num_atlases(const struct texture_cache *cache); #endif diff --git a/src/util.c b/src/util.c index f3d4730..b368957 100644 --- a/src/util.c +++ b/src/util.c @@ -186,6 +186,29 @@ t_frect to_frect(t_rect rect) { } +t_fvec2 frect_center(t_frect rect) { + return (t_fvec2){ + .x = rect.x + rect.w / 2, + .y = rect.y + rect.h / 2, + }; +} + + +t_fvec2 fvec2_from_vec2(t_vec2 vec) { + return (t_fvec2) { + .x = (float)vec.x, + .y = (float)vec.y, + }; +} + +t_fvec2 fvec2_from_shvec2(t_shvec2 vec) { + return (t_fvec2) { + .x = (float)vec.x, + .y = (float)vec.y, + }; +} + + void tick_timer(int *value) { *value = MAX(*value - 1, 0); } diff --git a/src/util.h b/src/util.h index bb90b3e..10b02af 100644 --- a/src/util.h +++ b/src/util.h @@ -48,7 +48,9 @@ void *ccalloc(size_t num, size_t size); #define MAX SDL_max #define MIN SDL_min +#ifndef M_PI #define M_PI 3.14159265358979323846264338327950288 /**< pi */ +#endif /* sets buf_out to a pointer to a byte buffer which must be freed. */ /* returns the size of this buffer. */ @@ -75,28 +77,6 @@ typedef struct color { } t_color; -/* a rectangle with the origin at the upper left (integer) */ -typedef struct rect { - int x, y; - int w, h; -} t_rect; - - -bool intersect_rect(const t_rect *a, const t_rect *b, t_rect *result); - - -/* a rectangle with the origin at the upper left (floating point) */ -typedef struct frect { - float x, y; - float w, h; -} t_frect; - - -bool intersect_frect(const t_frect *a, const t_frect *b, t_frect *result); - -t_frect to_frect(t_rect rect); - - /* a point in some space (integer) */ typedef struct vec2 { int x, y; @@ -109,6 +89,54 @@ typedef struct fvec2 { } t_fvec2; +/* a point in some three dimension space (floating point) */ +/* y goes up, x goes to the right */ +typedef struct fvec3 { + float x, y, z; +} t_fvec3; + + +/* a point in some space (short) */ +typedef struct shvec2 { + short x, y; +} t_shvec2; + + +/* a rectangle with the origin at the upper left (integer) */ +typedef struct rect { + int x, y; + int w, h; +} t_rect; + + +/* a rectangle with the origin at the upper left (floating point) */ +typedef struct frect { + float x, y; + float w, h; +} t_frect; + + +bool intersect_rect(const t_rect *a, const t_rect *b, t_rect *result); + +bool intersect_frect(const t_frect *a, const t_frect *b, t_frect *result); + +/* TODO: generics and specials (see m_to_fvec2() for an example)*/ +t_frect to_frect(t_rect rect); + +t_fvec2 frect_center(t_frect rect); + + +/* aren't macros to prevent double evaluation with side effects */ +/* maybe could be inlined? i hope LTO will resolve this */ +t_fvec2 fvec2_from_vec2(t_vec2 vec); +t_fvec2 fvec2_from_shvec2(t_shvec2 vec); + +#define m_to_fvec2(p_any_vec2) (_Generic((p_any_vec2), \ + t_vec2: fvec2_from_vec2, \ + t_shvec2: fvec2_from_shvec2 \ + )(p_any_vec2)) + + /* decrements an lvalue (which should be an int), stopping at 0 */ /* meant for tick-based timers in game logic */ /* @@ -117,7 +145,6 @@ typedef struct fvec2 { */ void tick_timer(int *value); - /* decrements a floating point second-based timer, stopping at 0.0 */ /* meant for poll based real time logic in game logic */ /* note that it should be decremented only on the next tick after its creation */ @@ -127,5 +154,28 @@ void tick_ftimer(float *value); /* returns true if value was cycled */ bool repeat_ftimer(float *value, float at); +/* http://www.azillionmonkeys.com/qed/sqroot.html */ +static inline float fast_sqrt(float x) +{ + union { + float f; + uint32_t u; + } pun = {.f = x}; + + pun.u += 127 << 23; + pun.u >>= 1; + + return pun.f; +} + + +static inline t_fvec2 fast_cossine(float a) { + const float s = sinf(a); + return (t_fvec2){ + .x = fast_sqrt(1.0f - s * s) * (a >= (float)M_PI_2 && a < (float)(M_PI + M_PI_2) ? -1 : 1), + .y = s + }; +} + #endif diff --git a/third-party/glad/include/glad/glad.h b/third-party/glad/include/glad/glad.h index b8291f7..d040d4e 100644 --- a/third-party/glad/include/glad/glad.h +++ b/third-party/glad/include/glad/glad.h @@ -1,22 +1,23 @@ /* - OpenGL loader generated by glad 0.1.36 on Tue Jul 9 02:07:20 2024. + OpenGL loader generated by glad 0.1.36 on Wed Jul 10 12:56:38 2024. Language/Generator: C/C++ Specification: gl APIs: gl=1.5 Profile: compatibility Extensions: - + GL_ARB_shader_objects, + GL_KHR_debug Loader: True Local files: False Omit khrplatform: False Reproducible: False Commandline: - --profile="compatibility" --api="gl=1.5" --generator="c" --spec="gl" --extensions="" + --profile="compatibility" --api="gl=1.5" --generator="c" --spec="gl" --extensions="GL_ARB_shader_objects,GL_KHR_debug" Online: - https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D1.5 + https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D1.5&extensions=GL_ARB_shader_objects&extensions=GL_KHR_debug */ @@ -2278,6 +2279,306 @@ typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, GLAPI PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv; #define glGetBufferPointerv glad_glGetBufferPointerv #endif +#define GL_PROGRAM_OBJECT_ARB 0x8B40 +#define GL_SHADER_OBJECT_ARB 0x8B48 +#define GL_OBJECT_TYPE_ARB 0x8B4E +#define GL_OBJECT_SUBTYPE_ARB 0x8B4F +#define GL_FLOAT_VEC2_ARB 0x8B50 +#define GL_FLOAT_VEC3_ARB 0x8B51 +#define GL_FLOAT_VEC4_ARB 0x8B52 +#define GL_INT_VEC2_ARB 0x8B53 +#define GL_INT_VEC3_ARB 0x8B54 +#define GL_INT_VEC4_ARB 0x8B55 +#define GL_BOOL_ARB 0x8B56 +#define GL_BOOL_VEC2_ARB 0x8B57 +#define GL_BOOL_VEC3_ARB 0x8B58 +#define GL_BOOL_VEC4_ARB 0x8B59 +#define GL_FLOAT_MAT2_ARB 0x8B5A +#define GL_FLOAT_MAT3_ARB 0x8B5B +#define GL_FLOAT_MAT4_ARB 0x8B5C +#define GL_SAMPLER_1D_ARB 0x8B5D +#define GL_SAMPLER_2D_ARB 0x8B5E +#define GL_SAMPLER_3D_ARB 0x8B5F +#define GL_SAMPLER_CUBE_ARB 0x8B60 +#define GL_SAMPLER_1D_SHADOW_ARB 0x8B61 +#define GL_SAMPLER_2D_SHADOW_ARB 0x8B62 +#define GL_SAMPLER_2D_RECT_ARB 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW_ARB 0x8B64 +#define GL_OBJECT_DELETE_STATUS_ARB 0x8B80 +#define GL_OBJECT_COMPILE_STATUS_ARB 0x8B81 +#define GL_OBJECT_LINK_STATUS_ARB 0x8B82 +#define GL_OBJECT_VALIDATE_STATUS_ARB 0x8B83 +#define GL_OBJECT_INFO_LOG_LENGTH_ARB 0x8B84 +#define GL_OBJECT_ATTACHED_OBJECTS_ARB 0x8B85 +#define GL_OBJECT_ACTIVE_UNIFORMS_ARB 0x8B86 +#define GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB 0x8B87 +#define GL_OBJECT_SHADER_SOURCE_LENGTH_ARB 0x8B88 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_BUFFER 0x82E0 +#define GL_SHADER 0x82E1 +#define GL_PROGRAM 0x82E2 +#define GL_QUERY 0x82E3 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_SAMPLER 0x82E6 +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_KHR 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION_KHR 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM_KHR 0x8245 +#define GL_DEBUG_SOURCE_API_KHR 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER_KHR 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY_KHR 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION_KHR 0x824A +#define GL_DEBUG_SOURCE_OTHER_KHR 0x824B +#define GL_DEBUG_TYPE_ERROR_KHR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY_KHR 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE_KHR 0x8250 +#define GL_DEBUG_TYPE_OTHER_KHR 0x8251 +#define GL_DEBUG_TYPE_MARKER_KHR 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP_KHR 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP_KHR 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION_KHR 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH_KHR 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH_KHR 0x826D +#define GL_BUFFER_KHR 0x82E0 +#define GL_SHADER_KHR 0x82E1 +#define GL_PROGRAM_KHR 0x82E2 +#define GL_VERTEX_ARRAY_KHR 0x8074 +#define GL_QUERY_KHR 0x82E3 +#define GL_PROGRAM_PIPELINE_KHR 0x82E4 +#define GL_SAMPLER_KHR 0x82E6 +#define GL_MAX_LABEL_LENGTH_KHR 0x82E8 +#define GL_MAX_DEBUG_MESSAGE_LENGTH_KHR 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_KHR 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_KHR 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_KHR 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_KHR 0x9147 +#define GL_DEBUG_SEVERITY_LOW_KHR 0x9148 +#define GL_DEBUG_OUTPUT_KHR 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT_KHR 0x00000002 +#define GL_STACK_OVERFLOW_KHR 0x0503 +#define GL_STACK_UNDERFLOW_KHR 0x0504 +#define GL_DISPLAY_LIST 0x82E7 +#ifndef GL_ARB_shader_objects +#define GL_ARB_shader_objects 1 +GLAPI int GLAD_GL_ARB_shader_objects; +typedef void (APIENTRYP PFNGLDELETEOBJECTARBPROC)(GLhandleARB obj); +GLAPI PFNGLDELETEOBJECTARBPROC glad_glDeleteObjectARB; +#define glDeleteObjectARB glad_glDeleteObjectARB +typedef GLhandleARB (APIENTRYP PFNGLGETHANDLEARBPROC)(GLenum pname); +GLAPI PFNGLGETHANDLEARBPROC glad_glGetHandleARB; +#define glGetHandleARB glad_glGetHandleARB +typedef void (APIENTRYP PFNGLDETACHOBJECTARBPROC)(GLhandleARB containerObj, GLhandleARB attachedObj); +GLAPI PFNGLDETACHOBJECTARBPROC glad_glDetachObjectARB; +#define glDetachObjectARB glad_glDetachObjectARB +typedef GLhandleARB (APIENTRYP PFNGLCREATESHADEROBJECTARBPROC)(GLenum shaderType); +GLAPI PFNGLCREATESHADEROBJECTARBPROC glad_glCreateShaderObjectARB; +#define glCreateShaderObjectARB glad_glCreateShaderObjectARB +typedef void (APIENTRYP PFNGLSHADERSOURCEARBPROC)(GLhandleARB shaderObj, GLsizei count, const GLcharARB **string, const GLint *length); +GLAPI PFNGLSHADERSOURCEARBPROC glad_glShaderSourceARB; +#define glShaderSourceARB glad_glShaderSourceARB +typedef void (APIENTRYP PFNGLCOMPILESHADERARBPROC)(GLhandleARB shaderObj); +GLAPI PFNGLCOMPILESHADERARBPROC glad_glCompileShaderARB; +#define glCompileShaderARB glad_glCompileShaderARB +typedef GLhandleARB (APIENTRYP PFNGLCREATEPROGRAMOBJECTARBPROC)(void); +GLAPI PFNGLCREATEPROGRAMOBJECTARBPROC glad_glCreateProgramObjectARB; +#define glCreateProgramObjectARB glad_glCreateProgramObjectARB +typedef void (APIENTRYP PFNGLATTACHOBJECTARBPROC)(GLhandleARB containerObj, GLhandleARB obj); +GLAPI PFNGLATTACHOBJECTARBPROC glad_glAttachObjectARB; +#define glAttachObjectARB glad_glAttachObjectARB +typedef void (APIENTRYP PFNGLLINKPROGRAMARBPROC)(GLhandleARB programObj); +GLAPI PFNGLLINKPROGRAMARBPROC glad_glLinkProgramARB; +#define glLinkProgramARB glad_glLinkProgramARB +typedef void (APIENTRYP PFNGLUSEPROGRAMOBJECTARBPROC)(GLhandleARB programObj); +GLAPI PFNGLUSEPROGRAMOBJECTARBPROC glad_glUseProgramObjectARB; +#define glUseProgramObjectARB glad_glUseProgramObjectARB +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMARBPROC)(GLhandleARB programObj); +GLAPI PFNGLVALIDATEPROGRAMARBPROC glad_glValidateProgramARB; +#define glValidateProgramARB glad_glValidateProgramARB +typedef void (APIENTRYP PFNGLUNIFORM1FARBPROC)(GLint location, GLfloat v0); +GLAPI PFNGLUNIFORM1FARBPROC glad_glUniform1fARB; +#define glUniform1fARB glad_glUniform1fARB +typedef void (APIENTRYP PFNGLUNIFORM2FARBPROC)(GLint location, GLfloat v0, GLfloat v1); +GLAPI PFNGLUNIFORM2FARBPROC glad_glUniform2fARB; +#define glUniform2fARB glad_glUniform2fARB +typedef void (APIENTRYP PFNGLUNIFORM3FARBPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI PFNGLUNIFORM3FARBPROC glad_glUniform3fARB; +#define glUniform3fARB glad_glUniform3fARB +typedef void (APIENTRYP PFNGLUNIFORM4FARBPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI PFNGLUNIFORM4FARBPROC glad_glUniform4fARB; +#define glUniform4fARB glad_glUniform4fARB +typedef void (APIENTRYP PFNGLUNIFORM1IARBPROC)(GLint location, GLint v0); +GLAPI PFNGLUNIFORM1IARBPROC glad_glUniform1iARB; +#define glUniform1iARB glad_glUniform1iARB +typedef void (APIENTRYP PFNGLUNIFORM2IARBPROC)(GLint location, GLint v0, GLint v1); +GLAPI PFNGLUNIFORM2IARBPROC glad_glUniform2iARB; +#define glUniform2iARB glad_glUniform2iARB +typedef void (APIENTRYP PFNGLUNIFORM3IARBPROC)(GLint location, GLint v0, GLint v1, GLint v2); +GLAPI PFNGLUNIFORM3IARBPROC glad_glUniform3iARB; +#define glUniform3iARB glad_glUniform3iARB +typedef void (APIENTRYP PFNGLUNIFORM4IARBPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI PFNGLUNIFORM4IARBPROC glad_glUniform4iARB; +#define glUniform4iARB glad_glUniform4iARB +typedef void (APIENTRYP PFNGLUNIFORM1FVARBPROC)(GLint location, GLsizei count, const GLfloat *value); +GLAPI PFNGLUNIFORM1FVARBPROC glad_glUniform1fvARB; +#define glUniform1fvARB glad_glUniform1fvARB +typedef void (APIENTRYP PFNGLUNIFORM2FVARBPROC)(GLint location, GLsizei count, const GLfloat *value); +GLAPI PFNGLUNIFORM2FVARBPROC glad_glUniform2fvARB; +#define glUniform2fvARB glad_glUniform2fvARB +typedef void (APIENTRYP PFNGLUNIFORM3FVARBPROC)(GLint location, GLsizei count, const GLfloat *value); +GLAPI PFNGLUNIFORM3FVARBPROC glad_glUniform3fvARB; +#define glUniform3fvARB glad_glUniform3fvARB +typedef void (APIENTRYP PFNGLUNIFORM4FVARBPROC)(GLint location, GLsizei count, const GLfloat *value); +GLAPI PFNGLUNIFORM4FVARBPROC glad_glUniform4fvARB; +#define glUniform4fvARB glad_glUniform4fvARB +typedef void (APIENTRYP PFNGLUNIFORM1IVARBPROC)(GLint location, GLsizei count, const GLint *value); +GLAPI PFNGLUNIFORM1IVARBPROC glad_glUniform1ivARB; +#define glUniform1ivARB glad_glUniform1ivARB +typedef void (APIENTRYP PFNGLUNIFORM2IVARBPROC)(GLint location, GLsizei count, const GLint *value); +GLAPI PFNGLUNIFORM2IVARBPROC glad_glUniform2ivARB; +#define glUniform2ivARB glad_glUniform2ivARB +typedef void (APIENTRYP PFNGLUNIFORM3IVARBPROC)(GLint location, GLsizei count, const GLint *value); +GLAPI PFNGLUNIFORM3IVARBPROC glad_glUniform3ivARB; +#define glUniform3ivARB glad_glUniform3ivARB +typedef void (APIENTRYP PFNGLUNIFORM4IVARBPROC)(GLint location, GLsizei count, const GLint *value); +GLAPI PFNGLUNIFORM4IVARBPROC glad_glUniform4ivARB; +#define glUniform4ivARB glad_glUniform4ivARB +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVARBPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI PFNGLUNIFORMMATRIX2FVARBPROC glad_glUniformMatrix2fvARB; +#define glUniformMatrix2fvARB glad_glUniformMatrix2fvARB +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVARBPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI PFNGLUNIFORMMATRIX3FVARBPROC glad_glUniformMatrix3fvARB; +#define glUniformMatrix3fvARB glad_glUniformMatrix3fvARB +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVARBPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI PFNGLUNIFORMMATRIX4FVARBPROC glad_glUniformMatrix4fvARB; +#define glUniformMatrix4fvARB glad_glUniformMatrix4fvARB +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERFVARBPROC)(GLhandleARB obj, GLenum pname, GLfloat *params); +GLAPI PFNGLGETOBJECTPARAMETERFVARBPROC glad_glGetObjectParameterfvARB; +#define glGetObjectParameterfvARB glad_glGetObjectParameterfvARB +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVARBPROC)(GLhandleARB obj, GLenum pname, GLint *params); +GLAPI PFNGLGETOBJECTPARAMETERIVARBPROC glad_glGetObjectParameterivARB; +#define glGetObjectParameterivARB glad_glGetObjectParameterivARB +typedef void (APIENTRYP PFNGLGETINFOLOGARBPROC)(GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog); +GLAPI PFNGLGETINFOLOGARBPROC glad_glGetInfoLogARB; +#define glGetInfoLogARB glad_glGetInfoLogARB +typedef void (APIENTRYP PFNGLGETATTACHEDOBJECTSARBPROC)(GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj); +GLAPI PFNGLGETATTACHEDOBJECTSARBPROC glad_glGetAttachedObjectsARB; +#define glGetAttachedObjectsARB glad_glGetAttachedObjectsARB +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONARBPROC)(GLhandleARB programObj, const GLcharARB *name); +GLAPI PFNGLGETUNIFORMLOCATIONARBPROC glad_glGetUniformLocationARB; +#define glGetUniformLocationARB glad_glGetUniformLocationARB +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMARBPROC)(GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +GLAPI PFNGLGETACTIVEUNIFORMARBPROC glad_glGetActiveUniformARB; +#define glGetActiveUniformARB glad_glGetActiveUniformARB +typedef void (APIENTRYP PFNGLGETUNIFORMFVARBPROC)(GLhandleARB programObj, GLint location, GLfloat *params); +GLAPI PFNGLGETUNIFORMFVARBPROC glad_glGetUniformfvARB; +#define glGetUniformfvARB glad_glGetUniformfvARB +typedef void (APIENTRYP PFNGLGETUNIFORMIVARBPROC)(GLhandleARB programObj, GLint location, GLint *params); +GLAPI PFNGLGETUNIFORMIVARBPROC glad_glGetUniformivARB; +#define glGetUniformivARB glad_glGetUniformivARB +typedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC)(GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source); +GLAPI PFNGLGETSHADERSOURCEARBPROC glad_glGetShaderSourceARB; +#define glGetShaderSourceARB glad_glGetShaderSourceARB +#endif +#ifndef GL_KHR_debug +#define GL_KHR_debug 1 +GLAPI int GLAD_GL_KHR_debug; +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl; +#define glDebugMessageControl glad_glDebugMessageControl +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert; +#define glDebugMessageInsert glad_glDebugMessageInsert +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC)(GLDEBUGPROC callback, const void *userParam); +GLAPI PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback; +#define glDebugMessageCallback glad_glDebugMessageCallback +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +GLAPI PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog; +#define glGetDebugMessageLog glad_glGetDebugMessageLog +typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC)(GLenum source, GLuint id, GLsizei length, const GLchar *message); +GLAPI PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup; +#define glPushDebugGroup glad_glPushDebugGroup +typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC)(void); +GLAPI PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup; +#define glPopDebugGroup glad_glPopDebugGroup +typedef void (APIENTRYP PFNGLOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +GLAPI PFNGLOBJECTLABELPROC glad_glObjectLabel; +#define glObjectLabel glad_glObjectLabel +typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel; +#define glGetObjectLabel glad_glGetObjectLabel +typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC)(const void *ptr, GLsizei length, const GLchar *label); +GLAPI PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel; +#define glObjectPtrLabel glad_glObjectPtrLabel +typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel; +#define glGetObjectPtrLabel glad_glGetObjectPtrLabel +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLKHRPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI PFNGLDEBUGMESSAGECONTROLKHRPROC glad_glDebugMessageControlKHR; +#define glDebugMessageControlKHR glad_glDebugMessageControlKHR +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTKHRPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI PFNGLDEBUGMESSAGEINSERTKHRPROC glad_glDebugMessageInsertKHR; +#define glDebugMessageInsertKHR glad_glDebugMessageInsertKHR +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKKHRPROC)(GLDEBUGPROCKHR callback, const void *userParam); +GLAPI PFNGLDEBUGMESSAGECALLBACKKHRPROC glad_glDebugMessageCallbackKHR; +#define glDebugMessageCallbackKHR glad_glDebugMessageCallbackKHR +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGKHRPROC)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +GLAPI PFNGLGETDEBUGMESSAGELOGKHRPROC glad_glGetDebugMessageLogKHR; +#define glGetDebugMessageLogKHR glad_glGetDebugMessageLogKHR +typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPKHRPROC)(GLenum source, GLuint id, GLsizei length, const GLchar *message); +GLAPI PFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR; +#define glPushDebugGroupKHR glad_glPushDebugGroupKHR +typedef void (APIENTRYP PFNGLPOPDEBUGGROUPKHRPROC)(void); +GLAPI PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR; +#define glPopDebugGroupKHR glad_glPopDebugGroupKHR +typedef void (APIENTRYP PFNGLOBJECTLABELKHRPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +GLAPI PFNGLOBJECTLABELKHRPROC glad_glObjectLabelKHR; +#define glObjectLabelKHR glad_glObjectLabelKHR +typedef void (APIENTRYP PFNGLGETOBJECTLABELKHRPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI PFNGLGETOBJECTLABELKHRPROC glad_glGetObjectLabelKHR; +#define glGetObjectLabelKHR glad_glGetObjectLabelKHR +typedef void (APIENTRYP PFNGLOBJECTPTRLABELKHRPROC)(const void *ptr, GLsizei length, const GLchar *label); +GLAPI PFNGLOBJECTPTRLABELKHRPROC glad_glObjectPtrLabelKHR; +#define glObjectPtrLabelKHR glad_glObjectPtrLabelKHR +typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELKHRPROC)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI PFNGLGETOBJECTPTRLABELKHRPROC glad_glGetObjectPtrLabelKHR; +#define glGetObjectPtrLabelKHR glad_glGetObjectPtrLabelKHR +typedef void (APIENTRYP PFNGLGETPOINTERVKHRPROC)(GLenum pname, void **params); +GLAPI PFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR; +#define glGetPointervKHR glad_glGetPointervKHR +#endif #ifdef __cplusplus } diff --git a/third-party/glad/src/glad.c b/third-party/glad/src/glad.c index bb050ff..0a07c64 100644 --- a/third-party/glad/src/glad.c +++ b/third-party/glad/src/glad.c @@ -1,22 +1,23 @@ /* - OpenGL loader generated by glad 0.1.36 on Tue Jul 9 02:07:20 2024. + OpenGL loader generated by glad 0.1.36 on Wed Jul 10 12:56:38 2024. Language/Generator: C/C++ Specification: gl APIs: gl=1.5 Profile: compatibility Extensions: - + GL_ARB_shader_objects, + GL_KHR_debug Loader: True Local files: False Omit khrplatform: False Reproducible: False Commandline: - --profile="compatibility" --api="gl=1.5" --generator="c" --spec="gl" --extensions="" + --profile="compatibility" --api="gl=1.5" --generator="c" --spec="gl" --extensions="GL_ARB_shader_objects,GL_KHR_debug" Online: - https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D1.5 + https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D1.5&extensions=GL_ARB_shader_objects&extensions=GL_KHR_debug */ #include @@ -713,6 +714,68 @@ PFNGLWINDOWPOS3IPROC glad_glWindowPos3i = NULL; PFNGLWINDOWPOS3IVPROC glad_glWindowPos3iv = NULL; PFNGLWINDOWPOS3SPROC glad_glWindowPos3s = NULL; PFNGLWINDOWPOS3SVPROC glad_glWindowPos3sv = NULL; +int GLAD_GL_ARB_shader_objects = 0; +int GLAD_GL_KHR_debug = 0; +PFNGLDELETEOBJECTARBPROC glad_glDeleteObjectARB = NULL; +PFNGLGETHANDLEARBPROC glad_glGetHandleARB = NULL; +PFNGLDETACHOBJECTARBPROC glad_glDetachObjectARB = NULL; +PFNGLCREATESHADEROBJECTARBPROC glad_glCreateShaderObjectARB = NULL; +PFNGLSHADERSOURCEARBPROC glad_glShaderSourceARB = NULL; +PFNGLCOMPILESHADERARBPROC glad_glCompileShaderARB = NULL; +PFNGLCREATEPROGRAMOBJECTARBPROC glad_glCreateProgramObjectARB = NULL; +PFNGLATTACHOBJECTARBPROC glad_glAttachObjectARB = NULL; +PFNGLLINKPROGRAMARBPROC glad_glLinkProgramARB = NULL; +PFNGLUSEPROGRAMOBJECTARBPROC glad_glUseProgramObjectARB = NULL; +PFNGLVALIDATEPROGRAMARBPROC glad_glValidateProgramARB = NULL; +PFNGLUNIFORM1FARBPROC glad_glUniform1fARB = NULL; +PFNGLUNIFORM2FARBPROC glad_glUniform2fARB = NULL; +PFNGLUNIFORM3FARBPROC glad_glUniform3fARB = NULL; +PFNGLUNIFORM4FARBPROC glad_glUniform4fARB = NULL; +PFNGLUNIFORM1IARBPROC glad_glUniform1iARB = NULL; +PFNGLUNIFORM2IARBPROC glad_glUniform2iARB = NULL; +PFNGLUNIFORM3IARBPROC glad_glUniform3iARB = NULL; +PFNGLUNIFORM4IARBPROC glad_glUniform4iARB = NULL; +PFNGLUNIFORM1FVARBPROC glad_glUniform1fvARB = NULL; +PFNGLUNIFORM2FVARBPROC glad_glUniform2fvARB = NULL; +PFNGLUNIFORM3FVARBPROC glad_glUniform3fvARB = NULL; +PFNGLUNIFORM4FVARBPROC glad_glUniform4fvARB = NULL; +PFNGLUNIFORM1IVARBPROC glad_glUniform1ivARB = NULL; +PFNGLUNIFORM2IVARBPROC glad_glUniform2ivARB = NULL; +PFNGLUNIFORM3IVARBPROC glad_glUniform3ivARB = NULL; +PFNGLUNIFORM4IVARBPROC glad_glUniform4ivARB = NULL; +PFNGLUNIFORMMATRIX2FVARBPROC glad_glUniformMatrix2fvARB = NULL; +PFNGLUNIFORMMATRIX3FVARBPROC glad_glUniformMatrix3fvARB = NULL; +PFNGLUNIFORMMATRIX4FVARBPROC glad_glUniformMatrix4fvARB = NULL; +PFNGLGETOBJECTPARAMETERFVARBPROC glad_glGetObjectParameterfvARB = NULL; +PFNGLGETOBJECTPARAMETERIVARBPROC glad_glGetObjectParameterivARB = NULL; +PFNGLGETINFOLOGARBPROC glad_glGetInfoLogARB = NULL; +PFNGLGETATTACHEDOBJECTSARBPROC glad_glGetAttachedObjectsARB = NULL; +PFNGLGETUNIFORMLOCATIONARBPROC glad_glGetUniformLocationARB = NULL; +PFNGLGETACTIVEUNIFORMARBPROC glad_glGetActiveUniformARB = NULL; +PFNGLGETUNIFORMFVARBPROC glad_glGetUniformfvARB = NULL; +PFNGLGETUNIFORMIVARBPROC glad_glGetUniformivARB = NULL; +PFNGLGETSHADERSOURCEARBPROC glad_glGetShaderSourceARB = NULL; +PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl = NULL; +PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert = NULL; +PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback = NULL; +PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog = NULL; +PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup = NULL; +PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup = NULL; +PFNGLOBJECTLABELPROC glad_glObjectLabel = NULL; +PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel = NULL; +PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel = NULL; +PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel = NULL; +PFNGLDEBUGMESSAGECONTROLKHRPROC glad_glDebugMessageControlKHR = NULL; +PFNGLDEBUGMESSAGEINSERTKHRPROC glad_glDebugMessageInsertKHR = NULL; +PFNGLDEBUGMESSAGECALLBACKKHRPROC glad_glDebugMessageCallbackKHR = NULL; +PFNGLGETDEBUGMESSAGELOGKHRPROC glad_glGetDebugMessageLogKHR = NULL; +PFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR = NULL; +PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR = NULL; +PFNGLOBJECTLABELKHRPROC glad_glObjectLabelKHR = NULL; +PFNGLGETOBJECTLABELKHRPROC glad_glGetObjectLabelKHR = NULL; +PFNGLOBJECTPTRLABELKHRPROC glad_glObjectPtrLabelKHR = NULL; +PFNGLGETOBJECTPTRLABELKHRPROC glad_glGetObjectPtrLabelKHR = NULL; +PFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR = NULL; static void load_GL_VERSION_1_0(GLADloadproc load) { if(!GLAD_GL_VERSION_1_0) return; glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace"); @@ -1183,9 +1246,77 @@ static void load_GL_VERSION_1_5(GLADloadproc load) { glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)load("glGetBufferParameteriv"); glad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC)load("glGetBufferPointerv"); } +static void load_GL_ARB_shader_objects(GLADloadproc load) { + if(!GLAD_GL_ARB_shader_objects) return; + glad_glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC)load("glDeleteObjectARB"); + glad_glGetHandleARB = (PFNGLGETHANDLEARBPROC)load("glGetHandleARB"); + glad_glDetachObjectARB = (PFNGLDETACHOBJECTARBPROC)load("glDetachObjectARB"); + glad_glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC)load("glCreateShaderObjectARB"); + glad_glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC)load("glShaderSourceARB"); + glad_glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC)load("glCompileShaderARB"); + glad_glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC)load("glCreateProgramObjectARB"); + glad_glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC)load("glAttachObjectARB"); + glad_glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC)load("glLinkProgramARB"); + glad_glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC)load("glUseProgramObjectARB"); + glad_glValidateProgramARB = (PFNGLVALIDATEPROGRAMARBPROC)load("glValidateProgramARB"); + glad_glUniform1fARB = (PFNGLUNIFORM1FARBPROC)load("glUniform1fARB"); + glad_glUniform2fARB = (PFNGLUNIFORM2FARBPROC)load("glUniform2fARB"); + glad_glUniform3fARB = (PFNGLUNIFORM3FARBPROC)load("glUniform3fARB"); + glad_glUniform4fARB = (PFNGLUNIFORM4FARBPROC)load("glUniform4fARB"); + glad_glUniform1iARB = (PFNGLUNIFORM1IARBPROC)load("glUniform1iARB"); + glad_glUniform2iARB = (PFNGLUNIFORM2IARBPROC)load("glUniform2iARB"); + glad_glUniform3iARB = (PFNGLUNIFORM3IARBPROC)load("glUniform3iARB"); + glad_glUniform4iARB = (PFNGLUNIFORM4IARBPROC)load("glUniform4iARB"); + glad_glUniform1fvARB = (PFNGLUNIFORM1FVARBPROC)load("glUniform1fvARB"); + glad_glUniform2fvARB = (PFNGLUNIFORM2FVARBPROC)load("glUniform2fvARB"); + glad_glUniform3fvARB = (PFNGLUNIFORM3FVARBPROC)load("glUniform3fvARB"); + glad_glUniform4fvARB = (PFNGLUNIFORM4FVARBPROC)load("glUniform4fvARB"); + glad_glUniform1ivARB = (PFNGLUNIFORM1IVARBPROC)load("glUniform1ivARB"); + glad_glUniform2ivARB = (PFNGLUNIFORM2IVARBPROC)load("glUniform2ivARB"); + glad_glUniform3ivARB = (PFNGLUNIFORM3IVARBPROC)load("glUniform3ivARB"); + glad_glUniform4ivARB = (PFNGLUNIFORM4IVARBPROC)load("glUniform4ivARB"); + glad_glUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC)load("glUniformMatrix2fvARB"); + glad_glUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC)load("glUniformMatrix3fvARB"); + glad_glUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC)load("glUniformMatrix4fvARB"); + glad_glGetObjectParameterfvARB = (PFNGLGETOBJECTPARAMETERFVARBPROC)load("glGetObjectParameterfvARB"); + glad_glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC)load("glGetObjectParameterivARB"); + glad_glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC)load("glGetInfoLogARB"); + glad_glGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC)load("glGetAttachedObjectsARB"); + glad_glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC)load("glGetUniformLocationARB"); + glad_glGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC)load("glGetActiveUniformARB"); + glad_glGetUniformfvARB = (PFNGLGETUNIFORMFVARBPROC)load("glGetUniformfvARB"); + glad_glGetUniformivARB = (PFNGLGETUNIFORMIVARBPROC)load("glGetUniformivARB"); + glad_glGetShaderSourceARB = (PFNGLGETSHADERSOURCEARBPROC)load("glGetShaderSourceARB"); +} +static void load_GL_KHR_debug(GLADloadproc load) { + if(!GLAD_GL_KHR_debug) return; + glad_glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC)load("glDebugMessageControl"); + glad_glDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC)load("glDebugMessageInsert"); + glad_glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC)load("glDebugMessageCallback"); + glad_glGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC)load("glGetDebugMessageLog"); + glad_glPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC)load("glPushDebugGroup"); + glad_glPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC)load("glPopDebugGroup"); + glad_glObjectLabel = (PFNGLOBJECTLABELPROC)load("glObjectLabel"); + glad_glGetObjectLabel = (PFNGLGETOBJECTLABELPROC)load("glGetObjectLabel"); + glad_glObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC)load("glObjectPtrLabel"); + glad_glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC)load("glGetObjectPtrLabel"); + glad_glGetPointerv = (PFNGLGETPOINTERVPROC)load("glGetPointerv"); + glad_glDebugMessageControlKHR = (PFNGLDEBUGMESSAGECONTROLKHRPROC)load("glDebugMessageControlKHR"); + glad_glDebugMessageInsertKHR = (PFNGLDEBUGMESSAGEINSERTKHRPROC)load("glDebugMessageInsertKHR"); + glad_glDebugMessageCallbackKHR = (PFNGLDEBUGMESSAGECALLBACKKHRPROC)load("glDebugMessageCallbackKHR"); + glad_glGetDebugMessageLogKHR = (PFNGLGETDEBUGMESSAGELOGKHRPROC)load("glGetDebugMessageLogKHR"); + glad_glPushDebugGroupKHR = (PFNGLPUSHDEBUGGROUPKHRPROC)load("glPushDebugGroupKHR"); + glad_glPopDebugGroupKHR = (PFNGLPOPDEBUGGROUPKHRPROC)load("glPopDebugGroupKHR"); + glad_glObjectLabelKHR = (PFNGLOBJECTLABELKHRPROC)load("glObjectLabelKHR"); + glad_glGetObjectLabelKHR = (PFNGLGETOBJECTLABELKHRPROC)load("glGetObjectLabelKHR"); + glad_glObjectPtrLabelKHR = (PFNGLOBJECTPTRLABELKHRPROC)load("glObjectPtrLabelKHR"); + glad_glGetObjectPtrLabelKHR = (PFNGLGETOBJECTPTRLABELKHRPROC)load("glGetObjectPtrLabelKHR"); + glad_glGetPointervKHR = (PFNGLGETPOINTERVKHRPROC)load("glGetPointervKHR"); +} static int find_extensionsGL(void) { if (!get_exts()) return 0; - (void)&has_ext; + GLAD_GL_ARB_shader_objects = has_ext("GL_ARB_shader_objects"); + GLAD_GL_KHR_debug = has_ext("GL_KHR_debug"); free_exts(); return 1; } @@ -1252,6 +1383,8 @@ int gladLoadGLLoader(GLADloadproc load) { load_GL_VERSION_1_5(load); if (!find_extensionsGL()) return 0; + load_GL_ARB_shader_objects(load); + load_GL_KHR_debug(load); return GLVersion.major != 0 || GLVersion.minor != 0; } diff --git a/third-party/stb/stb_ds.h b/third-party/stb/stb_ds.h index e84c82d..5330d21 100644 --- a/third-party/stb/stb_ds.h +++ b/third-party/stb/stb_ds.h @@ -1121,7 +1121,7 @@ size_t stbds_hash_bytes(void *p, size_t len, size_t seed) unsigned char *d = (unsigned char *) p; if (len == 4) { - unsigned int hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); + uint32_t hash = (uint32_t)d[0] | ((uint32_t)d[1] << 8) | ((uint32_t)d[2] << 16) | ((uint32_t)d[3] << 24); #if 0 // HASH32-A Bob Jenkin's hash function w/o large constants hash ^= seed; @@ -1177,8 +1177,8 @@ size_t stbds_hash_bytes(void *p, size_t len, size_t seed) return (((size_t) hash << 16 << 16) | hash) ^ seed; } else if (len == 8 && sizeof(size_t) == 8) { - size_t hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); - hash |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // avoid warning if size_t == 4 + size_t hash = (size_t)d[0] | ((size_t)d[1] << 8) | ((size_t)d[2] << 16) | ((size_t)d[3] << 24); + hash |= ((size_t)d[4] | ((size_t)d[5] << 8) | ((size_t)d[6] << 16) | ((size_t)d[7] << 24)) << 16 << 16; hash ^= seed; hash = (~hash) + (hash << 21); hash ^= STBDS_ROTATE_RIGHT(hash,24);