Merge pull request 'opengl moment' (#1) from opengl into main

Reviewed-on: wanp/salesman#1
This commit is contained in:
veclav talica 2024-07-28 14:50:35 +00:00
commit bf65f83806
32 changed files with 1867 additions and 524 deletions

2
.clangd Normal file
View File

@ -0,0 +1,2 @@
CompileFlags:
CompilationDatabase: "./.build/"

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
.vs/ .vs/
.vscode/ .vscode/
.idea/ .idea/
.cache/
.build/ .build/
build/ build/
out/ out/

View File

@ -23,6 +23,14 @@ add_subdirectory(third-party/physfs)
add_subdirectory(third-party/libxm) 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 set(SOURCE_FILES
third-party/physfs/extras/physfsrwops.c third-party/physfs/extras/physfsrwops.c
third-party/stb/stb_vorbis.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/scene.c src/game/scenes/scene.h
src/game/scenes/title.c src/game/scenes/title.h src/game/scenes/title.c src/game/scenes/title.h
src/game/scenes/ingame.c src/game/scenes/ingame.h src/game/scenes/ingame.c src/game/scenes/ingame.h
${SYSTEM_SOURCE_FILES}
) )
# target # target
@ -58,7 +68,7 @@ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCE_FILES})
set_target_properties(${PROJECT_NAME} PROPERTIES set_target_properties(${PROJECT_NAME} PROPERTIES
C_STANDARD 11 C_STANDARD 11
C_STANDARD_REQUIRED ON C_STANDARD_REQUIRED ON
C_EXTENSIONS OFF) C_EXTENSIONS ON) # extensions are required by stb_ds.h
# distribution definitions # distribution definitions
set(ORGANIZATION_NAME "wanp" CACHE STRING set(ORGANIZATION_NAME "wanp" CACHE STRING
@ -94,11 +104,22 @@ else()
-fno-trapping-math -fno-trapping-math
-freciprocal-math) -freciprocal-math)
set(BUILD_FLAGS_RELEASE 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 set(BUILD_FLAGS_DEBUG
-O0
-g3 -g3
-gdwarf -gdwarf
-fsanitize-trap=undefined) -fno-omit-frame-pointer
-fstack-protector-all
-fsanitize=undefined
-fsanitize=address)
target_compile_options(${PROJECT_NAME} PRIVATE target_compile_options(${PROJECT_NAME} PRIVATE
${WARNING_FLAGS} ${WARNING_FLAGS}
${BUILD_FLAGS} ${BUILD_FLAGS}

BIN
data/assets/big-violet.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
data/assets/light.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -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) { 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) if (!pair)
play_audio_ex(path, channel, get_default_audio_args()); play_audio_ex(path, channel, get_default_audio_args());
else 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) { 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 */ /* create a channel if it doesn't exist */
if (!pair) { 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) { 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) if (!pair)
return NULL; return NULL;
@ -244,7 +244,7 @@ static void audio_sample_and_mixin_channel(const struct audio_channel *channel,
static uint8_t buffer[16384]; static uint8_t buffer[16384];
const int int16_buffer_frames = sizeof (buffer) / sizeof (int16_t); const int int16_buffer_frames = sizeof (buffer) / sizeof (int16_t);
const int float_buffer_frames = sizeof (buffer) / sizeof (float); 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) { switch (channel->file_type) {
case audio_file_type_ogg: { case audio_file_type_ogg: {

View File

@ -17,6 +17,7 @@
#define RENDER_BASE_WIDTH 640 #define RENDER_BASE_WIDTH 640
#define RENDER_BASE_HEIGHT 360 #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_SIZE 2048
#define TEXTURE_ATLAS_BIT_DEPTH 32 #define TEXTURE_ATLAS_BIT_DEPTH 32

View File

@ -20,11 +20,10 @@ typedef struct context {
struct texture_cache texture_cache; struct texture_cache texture_cache;
struct input_state input; struct input_state input;
struct sprite_primitive *render_queue_sprites; struct primitive_2d *render_queue_2d;
struct rect_primitive *render_queue_rectangles; struct mesh_batch_item *uncolored_mesh_batches;
struct circle_primitive *render_queue_circles;
struct audio_channel_pair *audio_channels; struct audio_channel_item *audio_channels;
SDL_AudioDeviceID audio_device; SDL_AudioDeviceID audio_device;
int audio_stream_frequency; int audio_stream_frequency;
SDL_AudioFormat audio_stream_format; SDL_AudioFormat audio_stream_format;
@ -51,7 +50,6 @@ typedef struct context {
unsigned int update_multiplicity; unsigned int update_multiplicity;
SDL_GLContext *gl_context; SDL_GLContext *gl_context;
SDL_Renderer *renderer;
SDL_Window *window; SDL_Window *window;
uint32_t window_id; uint32_t window_id;
int window_w; int window_w;
@ -64,6 +62,8 @@ typedef struct context {
bool debug; bool debug;
bool is_running; bool is_running;
bool resync_flag; bool resync_flag;
bool window_size_has_changed;
} t_ctx; } t_ctx;
extern t_ctx ctx; extern t_ctx ctx;

View File

@ -11,7 +11,54 @@ static void ingame_tick(struct state *state) {
world_drawdef(scn->world); world_drawdef(scn->world);
player_calc(scn->player); 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; struct scene_ingame *scn = (struct scene_ingame *)state->scene;
player_destroy(scn->player); player_destroy(scn->player);
world_destroy(scn->world); world_destroy(scn->world);
free(state->scene);
} }

View File

@ -24,6 +24,7 @@ static void title_end(struct state *state) {
struct scene_title *scn = (struct scene_title *)state->scene; struct scene_title *scn = (struct scene_title *)state->scene;
player_destroy(scn->player); player_destroy(scn->player);
world_destroy(scn->world); world_destroy(scn->world);
free(state->scene);
} }

View File

@ -5,6 +5,7 @@
#include "context.h" #include "context.h"
#include "rendering.h" #include "rendering.h"
#include "audio.h"
#include "util.h" #include "util.h"
#include <SDL2/SDL.h> #include <SDL2/SDL.h>

View File

@ -36,18 +36,21 @@ static void update_action_pressed_state(struct input_state *input, struct action
else { else {
action->just_changed = !action->is_pressed; action->just_changed = !action->is_pressed;
action->is_pressed = true; 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 * SDL_RenderWindowToLogical will turn window mouse
* coords into a position inside the logical render * coords into a position inside the logical render
* area. this has to be done to get an accurate point * area. this has to be done to get an accurate point
* that can actually be used in game logic * that can actually be used in game logic
*/ */
SDL_RenderWindowToLogical(input->renderer, // SDL_RenderWindowToLogical(input->renderer,
input->mouse_window_position.x, // input->mouse_window_position.x,
input->mouse_window_position.y, // input->mouse_window_position.y,
&action->position.x, // &action->position.x,
&action->position.y); // &action->position.y);
return; return;
} }
break; break;
@ -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); sh_new_strdup(input->action_hash);
input->renderer = renderer;
} }

View File

@ -54,7 +54,6 @@ struct action_hash_item {
struct input_state { struct input_state {
struct action_hash_item *action_hash; 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 */ const uint8_t *keyboard_state; /* array of booleans indexed by scancode */
uint32_t mouse_state; /* SDL mouse button bitmask */ uint32_t mouse_state; /* SDL mouse button bitmask */
t_vec2 mouse_window_position; 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_deinit(struct input_state *input);
void input_state_update(struct input_state *input); void input_state_update(struct input_state *input);

View File

@ -23,6 +23,8 @@
static void poll_events(void) { static void poll_events(void) {
SDL_Event e; SDL_Event e;
ctx.window_size_has_changed = false;
while (SDL_PollEvent(&e)) { while (SDL_PollEvent(&e)) {
switch (e.type) { switch (e.type) {
case SDL_QUIT: case SDL_QUIT:
@ -35,6 +37,10 @@ static void poll_events(void) {
switch (e.window.event) { switch (e.window.event) {
case SDL_WINDOWEVENT_RESIZED: 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; 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) { void main_loop(void) {
/* /*
if (!ctx.is_running) { if (!ctx.is_running) {
@ -145,10 +169,12 @@ static bool initialize(void) {
return false; return false;
} }
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5); 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 */ /* init got far enough to create a window */
ctx.window = SDL_CreateWindow("emerald", ctx.window = SDL_CreateWindow("emerald",
@ -156,7 +182,7 @@ static bool initialize(void) {
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
RENDER_BASE_WIDTH, RENDER_BASE_WIDTH,
RENDER_BASE_HEIGHT, RENDER_BASE_HEIGHT,
SDL_WINDOW_ALLOW_HIGHDPI | // SDL_WINDOW_ALLOW_HIGHDPI |
SDL_WINDOW_RESIZABLE | SDL_WINDOW_RESIZABLE |
SDL_WINDOW_OPENGL); SDL_WINDOW_OPENGL);
if (ctx.window == NULL) { if (ctx.window == NULL) {
@ -171,6 +197,7 @@ static bool initialize(void) {
} }
SDL_GL_MakeCurrent(ctx.window, ctx.gl_context); SDL_GL_MakeCurrent(ctx.window, ctx.gl_context);
SDL_GL_SetSwapInterval(1);
int glad_status = gladLoadGL(); int glad_status = gladLoadGL();
if (glad_status == 0) { if (glad_status == 0) {
@ -178,16 +205,17 @@ static bool initialize(void) {
goto fail; goto fail;
} }
log_info("OpenGL context: %s\n", glGetString(GL_VERSION));
/* might need this to have multiple windows */ /* might need this to have multiple windows */
ctx.window_id = SDL_GetWindowID(ctx.window); ctx.window_id = SDL_GetWindowID(ctx.window);
/* now that we have a window, we know a renderer can be created */ glViewport(0, 0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT);
ctx.renderer = SDL_CreateRenderer(ctx.window, -1, SDL_RENDERER_PRESENTVSYNC);
/* SDL_SetHint(SDL_HINT_RENDER_LOGICAL_SIZE_MODE, "overscan"); */ /* TODO: */
/* SDL_RenderSetIntegerScale(ctx.renderer, SDL_TRUE); */ // SDL_GetRendererOutputSize(ctx.renderer, &ctx.window_w, &ctx.window_h);
SDL_RenderSetLogicalSize(ctx.renderer, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT); ctx.window_w = RENDER_BASE_WIDTH;
SDL_GetRendererOutputSize(ctx.renderer, &ctx.window_w, &ctx.window_h); ctx.window_h = RENDER_BASE_HEIGHT;
/* audio initialization */ /* audio initialization */
{ {
@ -235,6 +263,12 @@ static bool initialize(void) {
ctx.debug = false; ctx.debug = false;
#endif #endif
/* hook up opengl debugging callback */
if (ctx.debug) {
glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(opengl_log, NULL);
}
/* random seeding */ /* random seeding */
/* SDL_GetPerformanceCounter returns some platform-dependent number. */ /* SDL_GetPerformanceCounter returns some platform-dependent number. */
/* it should vary between game instances. i checked! random enough for me. */ /* it should vary between game instances. i checked! random enough for me. */
@ -259,9 +293,7 @@ static bool initialize(void) {
/* rendering */ /* rendering */
/* these are dynamic arrays and will be allocated lazily by stb_ds */ /* these are dynamic arrays and will be allocated lazily by stb_ds */
ctx.render_queue_sprites = NULL; ctx.render_queue_2d = NULL;
ctx.render_queue_rectangles = NULL;
ctx.render_queue_circles = NULL;
ctx.circle_radius_hash = NULL; ctx.circle_radius_hash = NULL;
textures_cache_init(&ctx.texture_cache, ctx.window); textures_cache_init(&ctx.texture_cache, ctx.window);
@ -271,7 +303,7 @@ static bool initialize(void) {
} }
/* input */ /* input */
input_state_init(&ctx.input, ctx.renderer); input_state_init(&ctx.input);
/* scripting */ /* scripting */
/* /*
@ -296,8 +328,7 @@ static void clean_up(void) {
input_state_deinit(&ctx.input); input_state_deinit(&ctx.input);
arrfree(ctx.render_queue_sprites); arrfree(ctx.render_queue_2d);
arrfree(ctx.render_queue_rectangles);
textures_cache_deinit(&ctx.texture_cache); textures_cache_deinit(&ctx.texture_cache);
PHYSFS_deinit(); PHYSFS_deinit();

View File

@ -44,7 +44,7 @@ struct audio_channel {
}; };
struct audio_channel_pair { struct audio_channel_item {
char *key; char *key;
struct audio_channel value; struct audio_channel value;
}; };

View File

@ -2,22 +2,22 @@
#define PRIVATE_RENDERING_H #define PRIVATE_RENDERING_H
#include "../rendering.h" #include "../rendering.h"
#include "../textures.h"
#include "../util.h" #include "../util.h"
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <glad/glad.h>
#include <stdbool.h> #include <stdbool.h>
struct sprite_primitive { struct sprite_primitive {
t_frect rect; t_frect rect;
t_color color; t_color color;
double rotation; float rotation;
char *path; t_texture_key texture_key;
SDL_BlendMode blend_mode;
int atlas_index;
int layer;
bool flip_x; bool flip_x;
bool flip_y; bool flip_y;
bool blend; /* must be explicitly stated, textures having alpha channel isn't enough */
}; };
struct rect_primitive { struct rect_primitive {
@ -31,4 +31,54 @@ struct circle_primitive {
t_fvec2 position; 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 #endif

40
src/private/textures.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef PRIVATE_TEXTURES_H
#define PRIVATE_TEXTURES_H
#include "../util.h"
#include <SDL2/SDL.h>
#include <stb_rect_pack.h>
#include <glad/glad.h>
#include <stdbool.h>
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

View File

@ -1,12 +1,15 @@
#include "private/rendering.h" #include "private/rendering.h"
#include "rendering/sprites.h"
#include "rendering/triangles.h"
#include "rendering/circles.h"
#include "context.h" #include "context.h"
#include "textures.h" #include "textures.h"
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <stb_ds.h>
#include <glad/glad.h> #include <glad/glad.h>
#include <stb_ds.h>
#include <stdlib.h> #include <stddef.h>
#include <tgmath.h> #include <tgmath.h>
@ -14,57 +17,10 @@ void render_queue_clear(void) {
/* since i don't intend to free the queues, */ /* since i don't intend to free the queues, */
/* it's faster and simpler to just "start over" */ /* it's faster and simpler to just "start over" */
/* and start overwriting the existing data */ /* and start overwriting the existing data */
arrsetlen(ctx.render_queue_sprites, 0); arrsetlen(ctx.render_queue_2d, 0);
arrsetlen(ctx.render_queue_rectangles, 0);
arrsetlen(ctx.render_queue_circles, 0);
}
for (size_t i = 0; i < hmlenu(ctx.uncolored_mesh_batches); ++i)
/* arrsetlen(ctx.uncolored_mesh_batches[i].value.primitives, 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);
} }
@ -75,298 +31,153 @@ void push_rectangle(t_frect rect, t_color color) {
.color = color, .color = color,
}; };
arrput(ctx.render_queue_rectangles, rectangle); struct primitive_2d primitive = {
} .type = PRIMITIVE_2D_RECT,
.rect = rectangle,
/* circle */
void push_circle(t_fvec2 position, float radius, t_color color) {
struct circle_primitive circle = {
.radius = radius,
.color = color,
.position = position,
}; };
arrput(ctx.render_queue_circles, circle); arrput(ctx.render_queue_2d, primitive);
} }
/* compare functions for the sort in render_sprites */ static void upload_quad_vertices(t_frect rect) {
static int cmp_atlases(const void *a, const void *b) { /* client memory needs to be reachable on glDraw*, so */
int index_a = ((const struct sprite_primitive *)a)->atlas_index; static float vertices[6 * 2];
int index_b = ((const struct sprite_primitive *)b)->atlas_index;
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) { static void render_rectangle(const struct rect_primitive *rectangle) {
SDL_BlendMode mode_a = ((const struct sprite_primitive *)a)->blend_mode; glColor4ub(rectangle->color.r, rectangle->color.g,
SDL_BlendMode mode_b = ((const struct sprite_primitive *)b)->blend_mode; 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) { static void render_2d(void) {
t_color color_a = ((const struct sprite_primitive *)a)->color; glEnable(GL_TEXTURE_2D);
t_color color_b = ((const struct sprite_primitive *)b)->color; glActiveTexture(GL_TEXTURE0);
glDisable(GL_CULL_FACE);
glDisable(GL_ALPHA_TEST);
glEnable(GL_DEPTH_TEST);
/* check reds */ const size_t render_queue_len = arrlenu(ctx.render_queue_2d);
if (color_a.r < color_b.r)
return -1;
else if (color_a.r > color_b.r)
return 1;
/* reds were equal, check greens */ size_t batch_count = 0;
else if (color_a.g < color_b.g)
return -1;
else if (color_a.g > color_b.g)
return 1;
/* greens were equal, check blues */ for (size_t i = 0; i < render_queue_len; ++i) {
else if (color_a.b < color_b.b) const struct primitive_2d *current = &ctx.render_queue_2d[i];
return -1;
else if (color_a.b > color_b.b)
return 1;
/* blues were equal, check alphas */ switch (current->type) {
else if (color_a.a < color_b.a) case PRIMITIVE_2D_SPRITE: {
return -1; const struct sprite_batch batch =
else if (color_a.a > color_b.a) collect_sprite_batch(current, render_queue_len - i);
return 1;
/* entirely equal */ glDepthRange((double)batch_count / UINT16_MAX, 1.0);
else
return 0;
}
render_sprites(current, batch);
static int cmp_layers(const void *a, const void *b) { i += batch.size - 1; ++batch_count;
int layer_a = ((const struct sprite_primitive *)a)->layer; break;
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);
} }
case PRIMITIVE_2D_RECT:
SDL_RendererFlip flip = SDL_FLIP_NONE; render_rectangle(&current->rect);
if (sprite->flip_x) break;
flip |= SDL_FLIP_HORIZONTAL; case PRIMITIVE_2D_CIRCLE:
if (sprite->flip_y) render_circle(&current->circle);
flip |= SDL_FLIP_VERTICAL; break;
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]);
} }
} }
static void render_space(void) { static void render_space(void) {
glMatrixMode(GL_PROJECTION); glEnable(GL_CULL_FACE);
glPushMatrix(); glEnable(GL_DEPTH_TEST);
glLoadIdentity(); glDisable(GL_ALPHA_TEST);
glOrtho(0, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT, 0, -1, 1); 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); /* solid white, no modulation */
glColor4f(0.0, 1.0, 1.0, 1.0); glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glVertex2f(300.0,210.0);
glVertex2f(340.0,215.0);
glVertex2f(320.0,250.0);
glEnd();
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) { 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, glClearColor((1.0f / 255) * 230,
(1.0f / 255) * 230, (1.0f / 255) * 230,
(1.0f / 255) * 230, 1); (1.0f / 255) * 230, 1);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT |
GL_DEPTH_BUFFER_BIT |
GL_STENCIL_BUFFER_BIT);
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
render_space(); render_space();
render_sprites(); }
render_rectangles();
render_circles(); {
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); SDL_GL_SwapWindow(ctx.window);
} }

View File

@ -9,12 +9,11 @@
typedef struct push_sprite_args { typedef struct push_sprite_args {
char *path; char *path;
int layer;
t_color color; t_color color;
double rotation; float rotation;
SDL_BlendMode blend_mode;
bool flip_x; bool flip_x;
bool flip_y; bool flip_y;
bool blend;
} t_push_sprite_args; } t_push_sprite_args;
/* clears all render queues */ /* 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 */ /* pushes a filled circle onto the circle render queue */
void push_circle(t_fvec2 position, float radius, t_color color); 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 */ /* renders the background, then the primitives in all render queues */
void render(void); void render(void);

132
src/rendering/circles.h Normal file
View File

@ -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 <SDL2/SDL.h>
#include <stb_ds.h>
#include <stdlib.h>
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

View File

@ -0,0 +1,41 @@
/* a rendering.c mixin */
#ifndef QUAD_ELEMENT_BUFFER_H
#define QUAD_ELEMENT_BUFFER_H
#include <glad/glad.h>
#include <stddef.h>
#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

332
src/rendering/sprites.h Normal file
View File

@ -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 <stb_ds.h>
#include <stdbool.h>
#include <stddef.h>
/* 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

117
src/rendering/triangles.h Normal file
View File

@ -0,0 +1,117 @@
/* a rendering.c mixin */
#ifndef TRIANGLES_H
#define TRIANGLES_H
#include "../textures.h"
#include "../context.h"
#include <stb_ds.h>
/* 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

68
src/system/linux/elf.c Normal file
View File

@ -0,0 +1,68 @@
#include "elf.h"
#include <fcntl.h>
#include <unistd.h>
#include <sys/auxv.h>
#include <elf.h>
#include <linux/limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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;
}

10
src/system/linux/elf.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef ELF_H
#define ELF_H
#include <stdbool.h>
bool infer_elf_section_bounds(const char *restrict name,
const char **restrict vm_start,
const char **restrict vm_end);
#endif

View File

@ -1,6 +1,7 @@
#include "textures.h" #include "private/textures.h"
#include "config.h" #include "config.h"
#include "util.h" #include "util.h"
#include "textures.h"
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <SDL2/SDL_image.h> #include <SDL2/SDL_image.h>
@ -9,13 +10,12 @@
#include <stb_ds.h> #include <stb_ds.h>
#include <stb_rect_pack.h> #include <stb_rect_pack.h>
#include <limits.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
static SDL_Surface *image_to_surface(char *path) { static SDL_Surface *image_to_surface(const char *path) {
SDL_RWops *handle = PHYSFSRWOPS_openRead(path); SDL_RWops *handle = PHYSFSRWOPS_openRead(path);
if (handle == NULL) if (handle == NULL)
goto fail; 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 */ /* adds a new, blank atlas surface to the cache */
static void add_new_atlas(struct texture_cache *cache) { static void add_new_atlas(struct texture_cache *cache) {
SDL_PixelFormat *native_format = SDL_PixelFormat *native_format =
@ -57,17 +75,58 @@ static void add_new_atlas(struct texture_cache *cache) {
a_mask); a_mask);
SDL_FreeFormat(native_format); SDL_FreeFormat(native_format);
SDL_SetSurfaceBlendMode(new_atlas, SDL_BLENDMODE_NONE);
SDL_SetSurfaceRLE(new_atlas, true); SDL_SetSurfaceRLE(new_atlas, true);
arrput(cache->atlas_surfaces, new_atlas); arrput(cache->atlas_surfaces, new_atlas);
arrput(cache->atlas_textures, new_gl_texture());
}
SDL_Texture *new_atlas_texture =
SDL_CreateTextureFromSurface(cache->renderer, new_atlas); static void upload_texture_from_surface(GLuint texture, SDL_Surface *surface) {
arrput(cache->atlas_textures, new_atlas_texture); 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) { static void recreate_current_atlas_texture(struct texture_cache *cache) {
/* TODO: figure out if SDL_UpdateTexture alone is faster than blitting */ /* 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]; SDL_Surface *atlas_surface = cache->atlas_surfaces[cache->atlas_index];
/* clear */ /* clear */
@ -75,22 +134,27 @@ static void recreate_current_atlas_texture(struct texture_cache *cache) {
/* blit the texture surfaces onto the atlas */ /* blit the texture surfaces onto the atlas */
for (size_t i = 0; i < shlenu(cache->hash); ++i) { 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) if (cache->hash[i].value.atlas_index != cache->atlas_index)
continue; continue;
/* skip loners */
if (cache->hash[i].value.loner_texture != 0)
continue;
SDL_BlitSurface(cache->hash[i].value.data, SDL_BlitSurface(cache->hash[i].value.data,
NULL, NULL,
atlas_surface, 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! */ /* texturize it! */
SDL_LockSurface(atlas_surface); upload_texture_from_surface(cache->atlas_textures[cache->atlas_index], atlas_surface);
SDL_UpdateTexture(cache->atlas_textures[cache->atlas_index],
NULL,
atlas_surface->pixels,
atlas_surface->pitch);
SDL_UnlockSurface(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) { static stbrp_rect *create_rects_from_cache(struct texture_cache *cache) {
stbrp_rect *rects = NULL; stbrp_rect *rects = NULL;
for (size_t i = 0; i < shlenu(cache->hash); ++i) { 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 = { stbrp_rect new_rect = {
.w = surface_data->w, .w = surface_data->w,
.h = surface_data->h, .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 */ /* updates the atlas location of every rect in the cache */
static void update_texture_rects_in_atlas(struct texture_cache *cache, stbrp_rect *rects) { static void update_texture_rects_in_atlas(struct texture_cache *cache, stbrp_rect *rects) {
for (size_t i = 0; i < arrlenu(rects); ++i) { 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, .x = rects[i].x,
.y = rects[i].y, .y = rects[i].y,
.w = rects[i].w, .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) { void textures_cache_init(struct texture_cache *cache, SDL_Window *window) {
cache->window = window; cache->window = window;
cache->renderer = SDL_GetRenderer(window);
sh_new_arena(cache->hash); sh_new_arena(cache->hash);
sh_new_arena(cache->loner_hash);
cache->node_buffer = cmalloc(sizeof *cache->node_buffer * TEXTURE_ATLAS_SIZE); 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) { void textures_cache_deinit(struct texture_cache *cache) {
/* free atlas textures */ /* free atlas textures */
for (size_t i = 0; i < arrlenu(cache->atlas_textures); ++i) { 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); arrfree(cache->atlas_textures);
@ -201,11 +266,6 @@ void textures_cache_deinit(struct texture_cache *cache) {
} }
shfree(cache->hash); 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); 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); IMG_SavePNG_RW(cache->atlas_surfaces[i], handle, true);
log_info("Dumped atlas %s", buf); 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 */ /* no need to do anything if it was loaded already */
if (shgeti(cache->hash, path) >= 0 || shgeti(cache->loner_hash, path) >= 0) const ptrdiff_t i = shgeti(cache->hash, path);
return; if (i >= 0)
return (t_texture_key){ (uint16_t)i };
SDL_Surface *surface = image_to_surface(path); SDL_Surface *surface = image_to_surface(path);
struct texture new_texture; struct texture new_texture = {0};
new_texture.data = surface; new_texture.data = surface;
/* it's a "loner texture," it doesn't fit in an atlas so it's not in one */ /* 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) { if (surface->w >= TEXTURE_ATLAS_SIZE || surface->h >= TEXTURE_ATLAS_SIZE) {
new_texture.loner_data = SDL_CreateTextureFromSurface(cache->renderer, surface); new_texture.loner_texture = new_gl_texture();
new_texture.atlas_index = -1; upload_texture_from_surface(new_texture.loner_texture, surface);
shput(cache->loner_hash, path, new_texture); 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 { } else {
new_texture.atlas_index = cache->atlas_index; new_texture.atlas_index = cache->atlas_index;
shput(cache->hash, path, new_texture); shput(cache->hash, path, new_texture);
cache->is_dirty = true; 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 */ /* this function makes a lot more sense if you read stb_rect_pack.h */
stbrp_context pack_ctx; /* target info */ stbrp_context pack_ctx; /* target info */
stbrp_init_target(&pack_ctx, stbrp_init_target(&pack_ctx,
@ -299,59 +363,115 @@ void textures_update_current_atlas(struct texture_cache *cache) {
arrfree(rects); 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) { #include "system/linux/elf.h"
struct texture_cache_item *texture = shgetp_null(cache->hash, path);
if (texture == NULL) { static const char *rodata_start;
static const char *rodata_stop;
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;
if (rodata_stop == NULL)
if (!infer_elf_section_bounds(".rodata", &rodata_start, &rodata_stop))
CRY("Section inference", ".rodata section lookup failed");
/* 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;
}
}
/* 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);
/* 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() */
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;
}
}
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.", CRY("Texture lookup failed.",
"Tried to get texture that isn't loaded."); "Tried to get texture that isn't loaded.");
return (SDL_Rect){ 0, 0, 0, 0 }; return (t_rect){ 0, 0, 0, 0 };
} }
return texture->value.srcrect;
} }
int textures_get_atlas_index(struct texture_cache *cache, char *path) { t_rect textures_get_dims(const struct texture_cache *cache, t_texture_key key) {
struct texture_cache_item *texture = shgetp_null(cache->hash, path); if (m_texture_key_is_valid(key)) {
if (cache->hash[key.id].value.loner_texture != 0)
/* it might be a loner texture */ return cache->hash[key.id].value.srcrect;
if (texture == NULL) { else
texture = shgetp_null(cache->loner_hash, path); return (t_rect){ .w = TEXTURE_ATLAS_SIZE, .h = TEXTURE_ATLAS_SIZE };
} else {
/* never mind it's just not there at all */ CRY("Texture lookup failed.",
if (texture == NULL) {
CRY("Texture atlas index lookup failed.",
"Tried to get atlas index of texture that isn't loaded.");
return INT_MIN;
}
}
return texture->value.atlas_index;
}
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];
}
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."); "Tried to get texture that isn't loaded.");
return NULL; return (t_rect){ 0, 0, 0, 0 };
} }
return texture->value.loner_data;
} }
size_t textures_get_num_atlases(struct texture_cache *cache) { 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; return cache->atlas_index + 1;
} }

View File

@ -1,46 +1,17 @@
#ifndef TEXTURES_H #ifndef TEXTURES_H
#define TEXTURES_H #define TEXTURES_H
#include "private/textures.h"
#include "util.h"
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <stb_rect_pack.h> #include <glad/glad.h>
#include <stdbool.h> /* type safe structure for persistent texture handles */
typedef struct { uint16_t id; } t_texture_key;
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 */
};
/* 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_init(struct texture_cache *cache, SDL_Window *window);
void textures_cache_deinit(struct texture_cache *cache); 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. */ /* 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 */ /* can be called from anywhere at any time after init, useful if you want to */
/* preload textures you know will definitely be used */ /* 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 */ /* repacks the current texture atlas based on the texture cache if needed */
void textures_update_current_atlas(struct texture_cache *cache); /* 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 */ /* returns a persistent handle to some texture in cache, loading it if needed */
/* if the texture is not found, returns a zero-filled rect (so check w or h) */ /* check the result with m_texture_key_is_valid() */
SDL_Rect textures_get_srcrect(struct texture_cache *cache, char *path); 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 */ /* returns a rect in a texture cache of the given key */
/* if the texture is not found, returns INT_MIN */ t_rect textures_get_srcrect(const struct texture_cache *cache, t_texture_key key);
int textures_get_atlas_index(struct texture_cache *cache, char *path);
/* returns a pointer to the atlas at `index` */ /* returns a rect of dimensions of the whole texture (whole atlas) */
/* if the index is out of bounds, returns NULL. */ t_rect textures_get_dims(const struct texture_cache *cache, t_texture_key key);
/* you can get the index via texture_get_atlas_index */
SDL_Texture *textures_get_atlas(struct texture_cache *cache, int index);
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 */ /* 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 #endif

View File

@ -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) { void tick_timer(int *value) {
*value = MAX(*value - 1, 0); *value = MAX(*value - 1, 0);
} }

View File

@ -48,7 +48,9 @@ void *ccalloc(size_t num, size_t size);
#define MAX SDL_max #define MAX SDL_max
#define MIN SDL_min #define MIN SDL_min
#ifndef M_PI
#define M_PI 3.14159265358979323846264338327950288 /**< pi */ #define M_PI 3.14159265358979323846264338327950288 /**< pi */
#endif
/* sets buf_out to a pointer to a byte buffer which must be freed. */ /* sets buf_out to a pointer to a byte buffer which must be freed. */
/* returns the size of this buffer. */ /* returns the size of this buffer. */
@ -75,28 +77,6 @@ typedef struct color {
} t_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) */ /* a point in some space (integer) */
typedef struct vec2 { typedef struct vec2 {
int x, y; int x, y;
@ -109,6 +89,54 @@ typedef struct fvec2 {
} t_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 */ /* decrements an lvalue (which should be an int), stopping at 0 */
/* meant for tick-based timers in game logic */ /* meant for tick-based timers in game logic */
/* /*
@ -117,7 +145,6 @@ typedef struct fvec2 {
*/ */
void tick_timer(int *value); void tick_timer(int *value);
/* decrements a floating point second-based timer, stopping at 0.0 */ /* decrements a floating point second-based timer, stopping at 0.0 */
/* meant for poll based real time logic in game logic */ /* meant for poll based real time logic in game logic */
/* note that it should be decremented only on the next tick after its creation */ /* 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 */ /* returns true if value was cycled */
bool repeat_ftimer(float *value, float at); 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 #endif

View File

@ -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++ Language/Generator: C/C++
Specification: gl Specification: gl
APIs: gl=1.5 APIs: gl=1.5
Profile: compatibility Profile: compatibility
Extensions: Extensions:
GL_ARB_shader_objects,
GL_KHR_debug
Loader: True Loader: True
Local files: False Local files: False
Omit khrplatform: False Omit khrplatform: False
Reproducible: False Reproducible: False
Commandline: 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: 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; GLAPI PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv;
#define glGetBufferPointerv glad_glGetBufferPointerv #define glGetBufferPointerv glad_glGetBufferPointerv
#endif #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 #ifdef __cplusplus
} }

View File

@ -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++ Language/Generator: C/C++
Specification: gl Specification: gl
APIs: gl=1.5 APIs: gl=1.5
Profile: compatibility Profile: compatibility
Extensions: Extensions:
GL_ARB_shader_objects,
GL_KHR_debug
Loader: True Loader: True
Local files: False Local files: False
Omit khrplatform: False Omit khrplatform: False
Reproducible: False Reproducible: False
Commandline: 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: 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 <stdio.h> #include <stdio.h>
@ -713,6 +714,68 @@ PFNGLWINDOWPOS3IPROC glad_glWindowPos3i = NULL;
PFNGLWINDOWPOS3IVPROC glad_glWindowPos3iv = NULL; PFNGLWINDOWPOS3IVPROC glad_glWindowPos3iv = NULL;
PFNGLWINDOWPOS3SPROC glad_glWindowPos3s = NULL; PFNGLWINDOWPOS3SPROC glad_glWindowPos3s = NULL;
PFNGLWINDOWPOS3SVPROC glad_glWindowPos3sv = 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) { static void load_GL_VERSION_1_0(GLADloadproc load) {
if(!GLAD_GL_VERSION_1_0) return; if(!GLAD_GL_VERSION_1_0) return;
glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace"); glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace");
@ -1183,9 +1246,77 @@ static void load_GL_VERSION_1_5(GLADloadproc load) {
glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)load("glGetBufferParameteriv"); glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)load("glGetBufferParameteriv");
glad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC)load("glGetBufferPointerv"); 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) { static int find_extensionsGL(void) {
if (!get_exts()) return 0; 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(); free_exts();
return 1; return 1;
} }
@ -1252,6 +1383,8 @@ int gladLoadGLLoader(GLADloadproc load) {
load_GL_VERSION_1_5(load); load_GL_VERSION_1_5(load);
if (!find_extensionsGL()) return 0; if (!find_extensionsGL()) return 0;
load_GL_ARB_shader_objects(load);
load_GL_KHR_debug(load);
return GLVersion.major != 0 || GLVersion.minor != 0; return GLVersion.major != 0 || GLVersion.minor != 0;
} }

View File

@ -1121,7 +1121,7 @@ size_t stbds_hash_bytes(void *p, size_t len, size_t seed)
unsigned char *d = (unsigned char *) p; unsigned char *d = (unsigned char *) p;
if (len == 4) { 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 #if 0
// HASH32-A Bob Jenkin's hash function w/o large constants // HASH32-A Bob Jenkin's hash function w/o large constants
hash ^= seed; 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; return (((size_t) hash << 16 << 16) | hash) ^ seed;
} else if (len == 8 && sizeof(size_t) == 8) { } else if (len == 8 && sizeof(size_t) == 8) {
size_t hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); 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] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // avoid warning if size_t == 4 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 ^= seed;
hash = (~hash) + (hash << 21); hash = (~hash) + (hash << 21);
hash ^= STBDS_ROTATE_RIGHT(hash,24); hash ^= STBDS_ROTATE_RIGHT(hash,24);