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