summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authortalha <sarcxd@gmail.com>2024-12-11 00:53:05 +0500
committertalha <sarcxd@gmail.com>2024-12-11 00:53:05 +0500
commit9022d6c1fa64689ab9203d967a8cd31b406155da (patch)
tree5a85440c894545fc0e8dd823b6cc626c2be01fbc /source
parent60085c01f190c01adaf24b6f337e2528d1de23b7 (diff)
Added batch rendering (however basic it might be)
Diffstat (limited to 'source')
-rwxr-xr-xsource/main.cpp349
-rwxr-xr-xsource/math.h228
-rw-r--r--source/memory/arena.h78
-rwxr-xr-xsource/memory/memory.c246
-rwxr-xr-xsource/memory/memory.h98
-rw-r--r--source/shaders/cq_batched.fs.glsl9
-rw-r--r--source/shaders/cq_batched.vs.glsl10
7 files changed, 775 insertions, 243 deletions
diff --git a/source/main.cpp b/source/main.cpp
index 685e144..c72ad53 100755
--- a/source/main.cpp
+++ b/source/main.cpp
@@ -26,16 +26,16 @@
* - Jumping
* - Fixed framerate to prevent weird quirks with movement
* - Basic camera follower
-* TODO:
* - move from row-major to column major setup for math library
+* TODO:
* - Efficient Quad Renderer
* - Some way to make and define levels
+* - Level Creation
* - Update camera follower for centering player in view (with limits) after
* a few seconds (maybe like 2 seconds)
* - Level completion Object
* - Implement Broad Phase Collision for efficient collision handling
* - Audio
-* - Level Creation
*/
typedef uint8_t u8;
@@ -53,6 +53,7 @@ typedef double r64;
typedef u8 b8;
+#include "memory/arena.h"
#include "math.h"
enum PMoveState {
@@ -87,6 +88,41 @@ struct TextState {
TextChar* char_map;
};
+#define BATCH_SIZE 500
+#define MAT4_ELE 4*4
+
+struct r32_array {
+ r32* buffer;
+ u32 size;
+ u32 capacity;
+};
+
+void array_init(Arena* a, r32_array* arr, u32 capacity) {
+ arr->buffer = (r32*)arena_alloc(a, capacity*sizeof(r32));
+
+ assert(arr->buffer != NULL);
+
+ arr->size = 0;
+ arr->capacity = capacity;
+}
+
+void array_insert(r32_array* arr, r32* ele, u32 ele_size) {
+ b8 assert_cond = arr->size + ele_size <= arr->capacity;
+ if (!assert_cond) {
+ SDL_Log("arr->size: %d, arr->capacity: %d", arr->size, arr->capacity);
+ }
+ assert(assert_cond);
+
+ void* ptr = &arr->buffer[arr->size];
+ memcpy(ptr, ele, sizeof(r32)*ele_size);
+ arr->size += ele_size;
+}
+
+void array_clear(r32_array* arr) {
+ memset(arr->buffer, 0, sizeof(r32)*arr->capacity);
+ arr->size = 0;
+}
+
struct GLRenderer {
// colored quad
b8 cq_init;
@@ -99,6 +135,16 @@ struct GLRenderer {
Vec3 cam_look;
Mat4 cam_view;
Mat4 cam_proj;
+ // Batched cq
+ // batching buffer
+ u32 cq_batch_sp;
+ u32 cq_batch_vao;
+ u32 cq_batch_vbo;
+ u32 cq_batch_count;
+ r32_array cq_pos_batch;
+ r32_array cq_mvp_batch;
+ r32_array cq_color_batch;
+
// ui text
TextState ui_text;
};
@@ -177,8 +223,8 @@ FrameTimer frametimer() {
}
void update_frame_timer(FrameTimer *ft) {
- ft->tCurr = SDL_GetTicks64();
ft->tPrev = ft->tCurr;
+ ft->tCurr = SDL_GetTicks64();
ft->tDeltaMS = ft->tCurr - ft->tPrev;
ft->tDelta = ft->tDeltaMS / 1000.0f;
}
@@ -288,7 +334,6 @@ u32 gl_shader_program_from_path(const char* vspath, const char* fspath)
return shader_program;
}
-#if DISABLE_MAIN_GAME
u32 gl_setup_colored_quad(u32 sp)
{
// @todo: make this use index buffer maybe?
@@ -316,39 +361,157 @@ u32 gl_setup_colored_quad(u32 sp)
return vao;
}
+void gl_setup_colored_quad_optimized(
+ GLRenderer* renderer,
+ u32 sp
+) {
+ // @todo: make this use index buffer maybe?
+ glGenVertexArrays(1, &renderer->cq_batch_vao);
+ glGenBuffers(1, &renderer->cq_batch_vbo);
+
+ glBindVertexArray(renderer->cq_batch_vao);
+ glBindBuffer(GL_ARRAY_BUFFER, renderer->cq_batch_vbo);
+ glBufferData(
+ GL_ARRAY_BUFFER, (
+ renderer->cq_pos_batch.capacity + renderer->cq_color_batch.capacity
+ ) * sizeof(r32), NULL, GL_DYNAMIC_DRAW
+ );
+
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(r32), (void*)0);
+
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(
+ 1, 3, GL_FLOAT, GL_FALSE,
+ 3 * sizeof(r32), (void*)(renderer->cq_pos_batch.capacity*sizeof(r32))
+ );
+
+ glBindVertexArray(0);
+}
+
+void gl_cq_flush(GLRenderer* renderer) {
+ glUseProgram(renderer->cq_batch_sp);
+
+ glBindBuffer(GL_ARRAY_BUFFER, renderer->cq_batch_vbo);
+ // fill batch data
+ // position batch
+ glBufferSubData(
+ GL_ARRAY_BUFFER,
+ 0,
+ renderer->cq_pos_batch.size*sizeof(r32),
+ renderer->cq_pos_batch.buffer
+ );
+ // color batch
+ glBufferSubData(
+ GL_ARRAY_BUFFER,
+ renderer->cq_pos_batch.size*sizeof(r32),
+ renderer->cq_color_batch.size*sizeof(r32),
+ renderer->cq_color_batch.buffer
+ );
+
+ glBindVertexArray(renderer->cq_batch_vao);
+ glDrawArrays(GL_TRIANGLES, 0, renderer->cq_batch_count*6);
+
+ array_clear(&renderer->cq_pos_batch);
+ array_clear(&renderer->cq_color_batch);
+ renderer->cq_batch_count = 0;
+}
+
+void gl_draw_colored_quad_optimized(
+ GLRenderer* renderer,
+ Vec3 position,
+ Vec2 size,
+ Vec3 color
+) {
+ Vec4 vertices[6] = {
+ init4v(-1.0f, -1.0f, 0.0f, 1.0f),// bottom-left
+ init4v( 1.0f, -1.0f, 0.0f, 1.0f),// bottom-right
+ init4v( 1.0f, 1.0f, 0.0f, 1.0f),// top-right
+ init4v( 1.0f, 1.0f, 0.0f, 1.0f),// top-right
+ init4v(-1.0f, 1.0f, 0.0f, 1.0f),// top-left
+ init4v(-1.0f, -1.0f, 0.0f, 1.0f) // bottom-left
+ };
+
+ // setting quad size
+ Mat4 model = init_value4m(1.0);
+ Mat4 scale = scaling_matrix4m(size.x, size.y, 0.0f);
+ model = multiply4m(scale, model);
+ // setting quad position
+ Mat4 translation = translation_matrix4m(position.x, position.y, position.z);
+ model = multiply4m(translation, model);
+
+ Mat4 mvp = calculate_mvp4m(model, renderer->cam_view, renderer->cam_proj);
+
+ Vec4 mvp_pos;
+ mvp_pos = multiply4mv(mvp, vertices[0]);
+ vertices[0] = mvp_pos;
+ mvp_pos = multiply4mv(mvp, vertices[1]);
+ vertices[1] = mvp_pos;
+ mvp_pos = multiply4mv(mvp, vertices[2]);
+ vertices[2] = mvp_pos;
+ mvp_pos = multiply4mv(mvp, vertices[3]);
+ vertices[3] = mvp_pos;
+ mvp_pos = multiply4mv(mvp, vertices[4]);
+ vertices[4] = mvp_pos;
+ mvp_pos = multiply4mv(mvp, vertices[5]);
+ vertices[5] = mvp_pos;
+
+ array_insert(&renderer->cq_pos_batch, vertices[0].data, 4);
+ array_insert(&renderer->cq_pos_batch, vertices[1].data, 4);
+ array_insert(&renderer->cq_pos_batch, vertices[2].data, 4);
+ array_insert(&renderer->cq_pos_batch, vertices[3].data, 4);
+ array_insert(&renderer->cq_pos_batch, vertices[4].data, 4);
+ array_insert(&renderer->cq_pos_batch, vertices[5].data, 4);
+
+ // initialise color to be per vertex to allow batching
+ // @todo: really need to optimise this
+ array_insert(&renderer->cq_color_batch, color.data, 3);
+ array_insert(&renderer->cq_color_batch, color.data, 3);
+ array_insert(&renderer->cq_color_batch, color.data, 3);
+ array_insert(&renderer->cq_color_batch, color.data, 3);
+ array_insert(&renderer->cq_color_batch, color.data, 3);
+ array_insert(&renderer->cq_color_batch, color.data, 3);
+
+ renderer->cq_batch_count++;
+
+ if(renderer->cq_batch_count == BATCH_SIZE) {
+ gl_cq_flush(renderer);
+ }
+}
+
void gl_draw_colored_quad(
GLRenderer* renderer,
Vec3 position,
Vec2 size,
Vec3 color
) {
- //glEnable(GL_DEPTH_TEST);
+ glEnable(GL_DEPTH_TEST);
glUseProgram(renderer->cq_sp);
if (renderer->cq_init == 0)
{
glUniformMatrix4fv(
glGetUniformLocation(renderer->cq_sp, "Projection"),
- 1, GL_TRUE, (renderer->cam_proj).buffer
+ 1, GL_FALSE, (renderer->cam_proj).buffer
);
renderer->cq_init = 1;
}
// setting quad size
Mat4 model = init_value4m(1.0);
Mat4 scale = scaling_matrix4m(size.x, size.y, 0.0f);
- model = multiply4m_rm(scale, model);
+ model = multiply4m(scale, model);
// setting quad position
- Mat4 translation = translation_matrix4m_rm(position.x, position.y, position.z);
- model = multiply4m_rm(translation, model);
+ Mat4 translation = translation_matrix4m(position.x, position.y, position.z);
+ model = multiply4m(translation, model);
// setting color
glUniform3fv(glGetUniformLocation(renderer->cq_sp, "Color"), 1, color.data);
glUniformMatrix4fv(
glGetUniformLocation(renderer->cq_sp, "Model"),
- 1, GL_TRUE, model.buffer
+ 1, GL_FALSE, model.buffer
);
glUniformMatrix4fv(
glGetUniformLocation(renderer->cq_sp, "View"),
- 1, GL_TRUE, (renderer->cam_view).buffer
+ 1, GL_FALSE, (renderer->cam_view).buffer
);
glBindVertexArray(renderer->cq_vao);
@@ -453,9 +616,9 @@ void gl_render_text(GLRenderer *renderer, char* text, Vec2 position, r32 size, V
glUseProgram(renderer->ui_text.sp);
glUniformMatrix4fv(glGetUniformLocation(renderer->ui_text.sp, "View"),
- 1, GL_TRUE, renderer->cam_view.buffer);
+ 1, GL_FALSE, renderer->cam_view.buffer);
glUniformMatrix4fv(glGetUniformLocation(renderer->ui_text.sp, "Projection"),
- 1, GL_TRUE, renderer->cam_proj.buffer);
+ 1, GL_FALSE, renderer->cam_proj.buffer);
glUniform3fv(glGetUniformLocation(renderer->ui_text.sp, "TextColor"), 1, color.data);
glBindVertexArray(renderer->ui_text.vao);
glBindTexture(GL_TEXTURE_2D_ARRAY, renderer->ui_text.texture_atlas_id);
@@ -492,8 +655,8 @@ void gl_render_text(GLRenderer *renderer, char* text, Vec2 position, r32 size, V
r32 h = scale * renderer->ui_text.pixel_size;
Mat4 sm = scaling_matrix4m(w, h, 0);
- Mat4 tm = translation_matrix4m_rm(xpos, ypos, 0);
- Mat4 model = multiply4m_rm(tm, sm);
+ Mat4 tm = translation_matrix4m(xpos, ypos, 0);
+ Mat4 model = multiply4m(tm, sm);
renderer->ui_text.transforms[running_index] = model;
renderer->ui_text.char_indexes[running_index] = int(*char_iter);
@@ -503,7 +666,7 @@ void gl_render_text(GLRenderer *renderer, char* text, Vec2 position, r32 size, V
{
r32 transform_loc = glGetUniformLocation(renderer->ui_text.sp, "LetterTransforms");
glUniformMatrix4fv(transform_loc, renderer->ui_text.chunk_size,
- GL_TRUE, &(renderer->ui_text.transforms[0].buffer[0]));
+ GL_FALSE, &(renderer->ui_text.transforms[0].buffer[0]));
r32 texture_map_loc = glGetUniformLocation(renderer->ui_text.sp, "TextureMap");
glUniform1iv(texture_map_loc, renderer->ui_text.chunk_size, renderer->ui_text.char_indexes);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, renderer->ui_text.chunk_size);
@@ -519,7 +682,7 @@ void gl_render_text(GLRenderer *renderer, char* text, Vec2 position, r32 size, V
u32 render_count = running_index < renderer->ui_text.chunk_size ? running_index : renderer->ui_text.chunk_size;
r32 transform_loc = glGetUniformLocation(renderer->ui_text.sp, "LetterTransforms");
glUniformMatrix4fv(transform_loc, render_count,
- GL_TRUE, &(renderer->ui_text.transforms[0].buffer[0]));
+ GL_FALSE, &(renderer->ui_text.transforms[0].buffer[0]));
r32 texture_map_loc = glGetUniformLocation(renderer->ui_text.sp, "TextureMap");
glUniform1iv(texture_map_loc, render_count, renderer->ui_text.char_indexes);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, render_count);
@@ -596,85 +759,9 @@ Vec3 get_screen_position_from_percent(GameState state, Vec3 v) {
return screen_pos;
}
-#endif
int main(int argc, char* argv[])
{
- // @matrix testing
- Mat4 a = {0};
- Mat4 b = {0};
-
- a.data[0][0] = 4;
- a.data[0][1] = 0;
- a.data[0][2] = 0;
- a.data[0][3] = 0;
-
- a.data[1][0] = 2;
- a.data[1][1] = 8;
- a.data[1][2] = 1;
- a.data[1][3] = 0;
-
- a.data[2][0] = 0;
- a.data[2][1] = 1;
- a.data[2][2] = 0;
- a.data[2][3] = 0;
-
- a.data[3][0] = 0;
- a.data[3][1] = 0;
- a.data[3][2] = 0;
- a.data[3][3] = 0;
-
- b.data[0][0] = 4;
- b.data[0][1] = 2;
- b.data[0][2] = 9;
- b.data[0][3] = 0;
-
- b.data[1][0] = 2;
- b.data[1][1] = 0;
- b.data[1][2] = 4;
- b.data[1][3] = 0;
-
- b.data[2][0] = 1;
- b.data[2][1] = 4;
- b.data[2][2] = 2;
- b.data[2][3] = 0;
-
- b.data[3][0] = 0;
- b.data[3][1] = 0;
- b.data[3][2] = 0;
- b.data[3][3] = 0;
-
- Mat4 product = multiply4m(a,b);
-
- // translation
- Mat4 t1 = {0};
- t1.data[0][0] = 1;
- t1.data[0][1] = 0;
- t1.data[0][2] = 0;
- t1.data[0][3] = 0;
-
- t1.data[1][0] = 0;
- t1.data[1][1] = 1;
- t1.data[1][2] = 0;
- t1.data[1][3] = 0;
-
- t1.data[2][0] = 0;
- t1.data[2][1] = 0;
- t1.data[2][2] = 1;
- t1.data[2][3] = 0;
-
- t1.data[3][0] = 1;
- t1.data[3][1] = 2;
- t1.data[3][2] = 3;
- t1.data[3][3] = 1;
-
- Vec4 v = Vec4{1,1,1,1};
-
- Vec4 trans_mat = multiply4vm(v, t1);
-
- int dbg = 1;
- return 0;
-#if DISABLE_MAIN_GAME
u32 scr_width = 1024;
u32 scr_height = 768;
@@ -684,8 +771,8 @@ int main(int argc, char* argv[])
return -1;
}
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_Window* window = SDL_CreateWindow("simple platformer",
@@ -707,9 +794,31 @@ int main(int argc, char* argv[])
}
// vsync controls: 0 = OFF | 1 = ON (Default)
- SDL_GL_SetSwapInterval(1);
+ SDL_GL_SetSwapInterval(0);
- size_t read_count;
+ GLRenderer *renderer = new GLRenderer();
+
+ // @resume: I am working on creating an efficient quad renderer via
+ // instancing, in order to achieve that I have absolutely butchered the
+ // performance of my program by making it render 200,000 quads
+ // the goal is to use this to see when performance plummets
+ //
+ // so far the progress is that, I have somewhat implemented a jank system
+ // I need to write shaders, it compiles and runs but it is useless
+ // @todo: fix the renderer.
+
+ // @note: batch rendering
+ // 3 columns, 6 rows
+ u32 pos_ele_count = BATCH_SIZE * 4*6;
+ u32 color_ele_count = BATCH_SIZE * 3*6;
+ u32 mem_size = pos_ele_count + color_ele_count;
+ void* batch_memory = calloc(mem_size, sizeof(r32));
+ Arena batch_arena = {0};
+ arena_init(&batch_arena, (unsigned char*)batch_memory, mem_size*sizeof(r32));
+ array_init(&batch_arena, &(renderer->cq_pos_batch), pos_ele_count);
+ array_init(&batch_arena, &(renderer->cq_color_batch), color_ele_count);
+
+
u32 quad_sp = gl_shader_program_from_path(
"./source/shaders/colored_quad.vs.glsl",
"./source/shaders/colored_quad.fs.glsl"
@@ -718,12 +827,19 @@ int main(int argc, char* argv[])
"./source/shaders/ui_text.vs.glsl",
"./source/shaders/ui_text.fs.glsl"
);
+ u32 cq_batch_sp = gl_shader_program_from_path(
+ "./source/shaders/cq_batched.vs.glsl",
+ "./source/shaders/cq_batched.fs.glsl"
+ );
u32 quad_vao = gl_setup_colored_quad(quad_sp);
-
- GLRenderer *renderer = new GLRenderer();
renderer->cq_sp = quad_sp;
renderer->cq_vao = quad_vao;
- r32 render_scale = 2.0f;
+
+ renderer->cq_batch_sp = cq_batch_sp;
+ gl_setup_colored_quad_optimized(renderer, cq_batch_sp);
+
+
+ r32 render_scale = 8.0f;
// ==========
// setup text
// 1. setup free type library stuff
@@ -778,7 +894,7 @@ int main(int argc, char* argv[])
renderer->cam_pos,
add3v(renderer->cam_pos, renderer->cam_look), renderer->preset_up_dir
);
- renderer->cam_proj = orthographic_projection4m(
+ renderer->cam_proj = orthographic4m(
0.0f, (r32)scr_width*render_scale,
0.0f, (r32)scr_height*render_scale,
0.1f, 10.0f
@@ -849,7 +965,7 @@ int main(int argc, char* argv[])
while (game_running)
{
update_frame_timer(&timer);
- enforce_frame_rate(&timer, 60);
+ //enforce_frame_rate(&timer, 60);
controller.jump = 0;
controller.toggle_gravity = 0;
@@ -1074,7 +1190,7 @@ int main(int argc, char* argv[])
else
{
Vec2 dir = get_move_dir(controller);
- pd_1 = dir * 8.0f;
+ pd_1 = dir * 1.0f;
if (pd_1.x < 0.0f) {
p_motion_dir.x = -1.0f;
} else if (pd_1.x > 0.0f) {
@@ -1221,6 +1337,27 @@ int main(int argc, char* argv[])
state.wall.size,
Vec3{1.0f, 0.0f, 0.0f});
+ for (int i=0;i<4000;i++) {
+ u32 max_row_ele = 100;
+ Vec2 based_size = div2vf(state.render_scale*state.screen_size, max_row_ele);
+ Vec3 pos_i = Vec3{
+ (atom_size.x+based_size.x)*(r32)(i%max_row_ele),
+ (atom_size.y+based_size.y)*(r32)(i/max_row_ele),
+ -5.0f
+ };
+ r32 color_factor = (r32)(1000-i)/1000.0f;
+ gl_draw_colored_quad_optimized(
+ renderer,
+ pos_i,
+ atom_size,
+ Vec3{color_factor, color_factor, color_factor}
+ );
+ }
+ gl_cq_flush(renderer);
+
+ array_clear(&renderer->cq_pos_batch);
+ array_clear(&renderer->cq_color_batch);
+ renderer->cq_batch_count = 0;
// render ui text
if (is_collide_x || is_collide_y)
@@ -1265,11 +1402,11 @@ int main(int argc, char* argv[])
28.0f, // size
Vec3{0.0f, 0.0f, 0.0f}); // color
- sprintf(fmt_buffer, "frametime: %f", timer.tDeltaMS);
+ sprintf(fmt_buffer, "frametime: %f", timer.tDelta);
gl_render_text(renderer,
fmt_buffer,
Vec2{900.0f, 90.0f}, // position
- 28.0f, // size
+ 280.0f, // size
Vec3{0.0f, 0.0f, 0.0f}); // color
sprintf(fmt_buffer, "%f pixels", pd_1.x);
@@ -1290,6 +1427,7 @@ int main(int argc, char* argv[])
}
+ arena_clear(&batch_arena);
free(renderer->ui_text.transforms);
free(renderer->ui_text.char_indexes);
free(renderer->ui_text.char_map);
@@ -1297,5 +1435,4 @@ int main(int argc, char* argv[])
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
-#endif
}
diff --git a/source/math.h b/source/math.h
index e516569..22966ec 100755
--- a/source/math.h
+++ b/source/math.h
@@ -117,12 +117,6 @@ union Mat4 {
r32 buffer[16];
};
-union Mat4RowMajor {
- Vec4 xyzw[4];
- r32 data[4][4];
- r32 buffer[16];
-};
-
// ==== Vec2 ====
Vec2 v2(r32 v) {
return Vec2{v, v};
@@ -355,47 +349,7 @@ Mat4 subtract4m(Mat4 a, Mat4 b)
return res;
}
-#if disabled
-Vec4 multiply4mv_rm(Mat4RowMajor mat, Vec4 vec)
-{
- /*
- * @note: Incase I get confused about this in the future.
- *
- * Everything is row-order, which means that things in memory are laid out row first. So with a sample matrix
- * we have this order in memory: r1c1 r1c2 r1c3 r1c4 r2c1 ... (r = row, c = column). The same holds true for
- * vectors. (maybe move this explanation to the top)
- *
- * Now, multiply4vm will multiply a vector with a matrix. Conventionally that does not make any sense as
- * a vector is usually 4x1 and a matrix ix 4x4.
- * What this function considers a vector, while it is a vector, it is infact a row from a matrix, which
- * means that the vector is 1x4 and the matrix is 4x4.
- *
- * The function is meant to supplement the matrix multiplication process to alleviate the multiple lines of code
- * we have to write when multiplying the row of a left matrix to each column of the right matrix
- */
- Vec4 res = { 0 };
- res.x = (mat.data[0][0] * vec.x) + (mat.data[0][1] * vec.y) + (mat.data[0][2] * vec.z) + (mat.data[0][3] * vec.w);
- res.y = (mat.data[1][0] * vec.x) + (mat.data[1][1] * vec.y) + (mat.data[1][2] * vec.z) + (mat.data[1][3] * vec.w);
- res.z = (mat.data[2][0] * vec.x) + (mat.data[2][1] * vec.y) + (mat.data[2][2] * vec.z) + (mat.data[2][3] * vec.w);
- res.w = (mat.data[3][0] * vec.x) + (mat.data[3][1] * vec.y) + (mat.data[3][2] * vec.z) + (mat.data[3][3] * vec.w);
-
- return res;
-}
-
-Mat4RowMajor multiply4m_rm(Mat4RowMajor a, Mat4RowMajor b)
-{
- Mat4RowMajor res = { 0 };
-
- res.xyzw[0] = multiply4mv_rm(b, a.xyzw[0]);
- res.xyzw[1] = multiply4mv_rm(b, a.xyzw[1]);
- res.xyzw[2] = multiply4mv_rm(b, a.xyzw[2]);
- res.xyzw[3] = multiply4mv_rm(b, a.xyzw[3]);
-
- return res;
-}
-#endif
-
-Vec4 multiply4vm(Vec4 v, Mat4 m)
+Vec4 multiply4mv(Mat4 m, Vec4 v)
{
Vec4 res = {0};
@@ -426,10 +380,10 @@ Mat4 multiply4m(Mat4 a, Mat4 b)
{
Mat4 res = { 0 };
- res.row[0] = multiply4vm(b.row[0], a);
- res.row[1] = multiply4vm(b.row[1], a);
- res.row[2] = multiply4vm(b.row[2], a);
- res.row[3] = multiply4vm(b.row[3], a);
+ res.row[0] = multiply4mv(a, b.row[0]);
+ res.row[1] = multiply4mv(a, b.row[1]);
+ res.row[2] = multiply4mv(a, b.row[2]);
+ res.row[3] = multiply4mv(a, b.row[3]);
return res;
}
@@ -446,20 +400,7 @@ Mat4 scaling_matrix4m(r32 x, r32 y, r32 z)
return res;
}
-#if Disabled
-Mat4RowMajor translation_matrix4m_rm(r32 x, r32 y, r32 z)
-{
- // generates a 4x4 translation matrix for translation along each of the x,y,z axis
- Mat4RowMajor res = init_value4m(1.0f);
- res.data[0][3] = x;
- res.data[1][3] = y;
- res.data[2][3] = z;
-
- return res;
-}
-#endif
-
-Mat4 translation_matrix4m_cm(r32 x, r32 y, r32 z)
+Mat4 translation_matrix4m(r32 x, r32 y, r32 z)
{
Mat4 res = init_value4m(1.0f);
res.row[3] = Vec4{x, y, z, 1.0f};
@@ -467,80 +408,46 @@ Mat4 translation_matrix4m_cm(r32 x, r32 y, r32 z)
return res;
}
-#if Disabled
-Mat4RowMajor rotation_matrix4m(r32 angle_radians, Vec3 axis) // generates a 4x4 rotation matrix for rotation along each of the x,y,z axis
+Mat4 rotation_matrix4m(r32 angle_radians, Vec3 axis)
{
- Mat4RowMajor res = init_value4m(1.0f);
- axis = normalize3v(axis);
-
- r32 cos_theta = cosf(angle_radians);
- r32 sin_theta = sinf(angle_radians);
- r32 cos_value = 1.0f - cos_theta;
-
- res.data[0][0] = (axis.x * axis.x * cos_value) + cos_theta;
- res.data[0][1] = (axis.x * axis.y * cos_value) + (axis.z * sin_theta);
- res.data[0][2] = (axis.x * axis.z * cos_value) - (axis.y * sin_theta);
-
- res.data[1][0] = (axis.x * axis.y * cos_value) - (axis.z * sin_theta);
- res.data[1][1] = (axis.y * axis.y * cos_value) + cos_theta;
- res.data[1][2] = (axis.y * axis.z * cos_value) + (axis.x * sin_theta);
-
- res.data[2][0] = (axis.x * axis.z * cos_value) + (axis.y * sin_theta);
- res.data[2][1] = (axis.z * axis.y * cos_value) - (axis.x * sin_theta);
- res.data[2][2] = (axis.z * axis.z * cos_value) + cos_theta;
-
- return res;
-}
+ Mat4 res = init_value4m(1.0f);
+ axis = normalize3v(axis);
-Mat4RowMajor perspective_projection4m(r32 left, r32 right, r32 bottom, r32 top, r32 near, r32 far)
-{
- Mat4RowMajor res = { 0 };
-
- res.data[0][0] = (2.0 * near)/(right - left);
- res.data[0][2] = (right + left)/(right - left);
-
- res.data[1][1] = (2.0 * near)/(top - bottom);
- res.data[1][2] = (top + bottom)/(top - bottom);
-
- res.data[2][2] = -(far + near)/(far - near);
- res.data[2][3] = -2.0*far*near/(far - near);
-
- res.data[3][2] = -1.0;
-
- return res;
-}
+ r32 cos_theta = cosf(angle_radians);
+ r32 sin_theta = sinf(angle_radians);
+ r32 cos_value = 1.0f - cos_theta;
-Mat4RowMajor perspective4m(r32 fov, r32 aspect_ratio, r32 near, r32 far)
-{
- r32 cotangent = 1.0f / tanf(fov / 2.0f);
-
- Mat4RowMajor res = { 0 };
-
- res.data[0][0] = cotangent / aspect_ratio;
-
- res.data[1][1] = cotangent;
-
- res.data[2][2] = -(far + near) / (far - near);
- res.data[2][3] = -2.0f * far * near / (far - near);
-
- res.data[3][2] = -1.0f;
-
- return res;
+ res.data[0][0] = (axis.x * axis.x * cos_value) + cos_theta;
+ res.data[0][1] = (axis.x * axis.y * cos_value) + (axis.z * sin_theta);
+ res.data[0][2] = (axis.x * axis.z * cos_value) - (axis.y * sin_theta);
+
+ res.data[1][0] = (axis.x * axis.y * cos_value) - (axis.z * sin_theta);
+ res.data[1][1] = (axis.y * axis.y * cos_value) + cos_theta;
+ res.data[1][2] = (axis.y * axis.z * cos_value) + (axis.x * sin_theta);
+
+ res.data[2][0] = (axis.x * axis.z * cos_value) + (axis.y * sin_theta);
+ res.data[2][1] = (axis.y * axis.z * cos_value) - (axis.x * sin_theta);
+ res.data[2][2] = (axis.z * axis.z * cos_value) + cos_value;
+
+ return res;
}
-Mat4RowMajor orthographic_projection4m(r32 left, r32 right, r32 bottom, r32 top, r32 near, r32 far)
+Mat4 orthographic4m(r32 left, r32 right, r32 bottom, r32 top, r32 near, r32 far)
{
- // @todo: understand the derivation once I am done experimenting
- Mat4RowMajor res = { 0 };
- res.data[0][0] = 2.0f/(right - left); res.data[0][3] = -(right + left)/(right - left);
- res.data[1][1] = 2.0f/(top - bottom); res.data[1][3] = -(top + bottom)/(top - bottom);
- res.data[2][2] = -2.0f/(far - near); res.data[2][3] = -(far + near)/(far - near);
+ Mat4 res = init_value4m(0);
+
+ res.data[0][0] = 2.0f/(right - left);
+ res.data[1][1] = 2.0f/(top - bottom);
+ res.data[2][2] = 2.0f/(near - far);
+ res.data[3][0] = (right + left)/(left - right);
+ res.data[3][1] = (top + bottom)/(bottom - top);
+ res.data[3][2] = (far + near)/(near - far);
res.data[3][3] = 1.0f;
return res;
}
-Mat4RowMajor lookat4m(Vec3 up, Vec3 forward, Vec3 right, Vec3 position)
+Mat4 lookat4m(Vec3 up, Vec3 forward, Vec3 right, Vec3 position)
{
/*
* @note: The construction of the lookat matrix is not obvious. For that reason here is the supplemental matrial I have used to understand
@@ -549,14 +456,16 @@ Mat4RowMajor lookat4m(Vec3 up, Vec3 forward, Vec3 right, Vec3 position)
* It is because, we are moving from the position matrix which is a global to the view matrix which
* is a local. It won't be very clear from this illustration alone, so you would be best served watching the video and recollecting and understanding from there.
* 2. This article (https://twodee.org/blog/17560) derives (or rather shows), in a very shallow way how we get to the look at matrix.
- *
- * I am guessing this is not useful for 2D stuff
*/
- Mat4RowMajor res = init_value4m(1.0);
- res.xyzw[0] = Vec4{ right.x, right.y, right.z, -dot_multiply3v(right, position) };
- res.xyzw[1] = Vec4{ up.x, up.y, up.z, -dot_multiply3v(up, position) };
- res.xyzw[2] = Vec4{ forward.x, forward.y, forward.z, -dot_multiply3v(forward, position) };
- res.xyzw[3] = Vec4{ 0.0f, 0.0f, 0.0f, 1.0f };
+ Mat4 res = init_value4m(1.0f);
+ res.row[0] = Vec4{ right.x, up.x, forward.x, 0.0f };
+ res.row[1] = Vec4{ right.y, up.y, forward.y, 0.0f };
+ res.row[2] = Vec4{ right.z, up.z, forward.z, 0.0f };
+
+ res.data[3][0] = -dot_multiply3v(right, position);
+ res.data[3][1] = -dot_multiply3v(up, position);
+ res.data[3][2] = -dot_multiply3v(forward, position);
+ res.data[3][3] = 1.0f;
return res;
}
@@ -572,7 +481,7 @@ Vec3 camera_look_around(r32 angle_pitch, r32 angle_yaw)
return camera_look;
}
-Mat4RowMajor camera_create4m(Vec3 camera_pos, Vec3 camera_look, Vec3 camera_up)
+Mat4 camera_create4m(Vec3 camera_pos, Vec3 camera_look, Vec3 camera_up)
{
// @note: We do this because this allows the camera to have the axis it looks at
// inwards be the +z axis.
@@ -582,7 +491,52 @@ Mat4RowMajor camera_create4m(Vec3 camera_pos, Vec3 camera_look, Vec3 camera_up)
Vec3 camera_right_dir = normalize3v(cross_multiply3v(camera_up, camera_forward_dir));
Vec3 camera_up_dir = normalize3v(cross_multiply3v(camera_forward_dir, camera_right_dir));
- Mat4RowMajor res = lookat4m(camera_up_dir, camera_forward_dir, camera_right_dir, camera_pos);
+ Mat4 res = lookat4m(camera_up_dir, camera_forward_dir, camera_right_dir, camera_pos);
+
+ return res;
+}
+
+Mat4 calculate_mvp4m(Mat4 Model, Mat4 View, Mat4 Projection)
+{
+ Mat4 mv = multiply4m(View, Model);
+ Mat4 mvp = multiply4m(Projection, mv);
+
+ return mvp;
+}
+
+#if Disabled
+Mat4RowMajor perspective_projection4m(r32 left, r32 right, r32 bottom, r32 top, r32 near, r32 far)
+{
+ Mat4RowMajor res = { 0 };
+
+ res.data[0][0] = (2.0 * near)/(right - left);
+ res.data[0][2] = (right + left)/(right - left);
+
+ res.data[1][1] = (2.0 * near)/(top - bottom);
+ res.data[1][2] = (top + bottom)/(top - bottom);
+
+ res.data[2][2] = -(far + near)/(far - near);
+ res.data[2][3] = -2.0*far*near/(far - near);
+
+ res.data[3][2] = -1.0;
+
+ return res;
+}
+
+Mat4RowMajor perspective4m(r32 fov, r32 aspect_ratio, r32 near, r32 far)
+{
+ r32 cotangent = 1.0f / tanf(fov / 2.0f);
+
+ Mat4RowMajor res = { 0 };
+
+ res.data[0][0] = cotangent / aspect_ratio;
+
+ res.data[1][1] = cotangent;
+
+ res.data[2][2] = -(far + near) / (far - near);
+ res.data[2][3] = -2.0f * far * near / (far - near);
+
+ res.data[3][2] = -1.0f;
return res;
}
diff --git a/source/memory/arena.h b/source/memory/arena.h
new file mode 100644
index 0000000..83ff277
--- /dev/null
+++ b/source/memory/arena.h
@@ -0,0 +1,78 @@
+#pragma once
+
+#ifndef ALIGNMENT
+#define ALIGNMENT (2*sizeof(void*))
+#endif
+
+struct Arena {
+ unsigned char* buffer;
+ size_t prev_offset;
+ size_t curr_offset;
+ size_t capacity;
+};
+
+// definition
+b8 is_power_of_two(uintptr_t x);
+uintptr_t fast_modulo(uintptr_t p, uintptr_t a);
+uintptr_t align_forward(uintptr_t ptr, uintptr_t alignment);
+void arena_init(Arena* a, unsigned char *memory, size_t capacity);
+void* arena_alloc(Arena* a, size_t size);
+void arena_clear(Arena* a);
+
+
+// implementation
+b8 is_power_of_two(uintptr_t x) {
+ return (x & (x-1)) == 0;
+}
+
+uintptr_t fast_modulo(uintptr_t p, uintptr_t a) {
+ return (p & (a - 1));
+}
+
+uintptr_t align_forward(uintptr_t ptr, size_t alignment) {
+ uintptr_t p, a, modulo;
+
+ assert(is_power_of_two(alignment));
+
+ p = ptr;
+ a = (uintptr_t)alignment;
+ modulo = fast_modulo(p, a);
+
+ if (modulo != 0) {
+ p += (a - modulo);
+ }
+
+ return p;
+}
+
+void arena_init(Arena* a, unsigned char *memory, size_t capacity) {
+ a->buffer = memory;
+ a->prev_offset = 0;
+ a->curr_offset = 0;
+ a->capacity = capacity;
+}
+
+void* arena_alloc(Arena* a, size_t size) {
+ void *ptr = NULL;
+
+ assert(is_power_of_two(ALIGNMENT));
+
+ uintptr_t curr_ptr = (uintptr_t)a->buffer + a->curr_offset;
+ uintptr_t offset = align_forward(curr_ptr, ALIGNMENT);
+ offset = offset - (uintptr_t)a->buffer;
+ size_t next_offset = offset + size;
+
+ assert(next_offset <= a->capacity);
+
+ ptr = &a->buffer[offset];
+ a->prev_offset = a->curr_offset;
+ a->curr_offset = next_offset;
+ memset(ptr, 0, size);
+
+ return ptr;
+}
+
+void arena_clear(Arena* a) {
+ a->curr_offset = 0;
+ a->prev_offset = 0;
+}
diff --git a/source/memory/memory.c b/source/memory/memory.c
new file mode 100755
index 0000000..1d521e4
--- /dev/null
+++ b/source/memory/memory.c
@@ -0,0 +1,246 @@
+#include "memory.h"
+
+b8 is_power_of_two(uintptr_t x) { return (x & (x - 1)) == 0; }
+
+uintptr_t fast_modulo(uintptr_t p, uintptr_t a) { return (p & (a - 1)); }
+
+uintptr_t align_forward(uintptr_t ptr, size_t alignment) {
+ uintptr_t p, a, modulo;
+
+ assert(is_power_of_two(alignment));
+
+ p = ptr;
+ a = (uintptr_t)alignment;
+ modulo = fast_modulo(p, a);
+
+ if (modulo != 0) {
+ p += (a - modulo);
+ }
+
+ return p;
+}
+
+//===========================================================================================
+// ---------------------------------- Arena
+// -------------------------------------------------
+//===========================================================================================
+
+/*
+ A cases where arena allocation WILL fail:
+ | size = size_t + ${some_number_that_comes_up_higher_than_offset}
+
+ This is because there is no check being made
+*/
+void arena_init(struct Arena *a, unsigned char *backing_store,
+ size_t capacity) {
+ a->buffer = backing_store;
+ a->curr_offset = 0;
+ a->prev_offset = 0;
+ a->capacity = capacity;
+}
+
+void *arena_alloc_aligned(struct Arena *a, size_t size, size_t alignment) {
+ void *ptr = NULL;
+
+ assert(is_power_of_two(alignment));
+
+ uintptr_t curr_ptr = (uintptr_t)a->buffer + a->curr_offset;
+ uintptr_t offset = align_forward(curr_ptr, alignment);
+ offset = offset - (uintptr_t)a->buffer;
+
+ if (size <= a->capacity - offset) {
+ ptr = &a->buffer[offset];
+ a->prev_offset = a->curr_offset;
+ a->curr_offset = offset + size;
+ memset(ptr, 0, size);
+ }
+
+ return ptr;
+}
+
+void *arena_alloc(struct Arena *a, size_t size) {
+ return arena_alloc_aligned(a, size, DEFAULT_ALIGNMENT);
+}
+
+void *arena_resize_aligned(struct Arena *a, void *old_memory, size_t old_size,
+ size_t new_size, size_t alignment) {
+ unsigned char *old = (unsigned char *)old_memory;
+ void *ptr = NULL;
+
+ assert(is_power_of_two(alignment));
+
+ if (old >= a->buffer && old < a->buffer + a->capacity) {
+ if (a->buffer + a->prev_offset == old) {
+ // extend_last_element
+ if (new_size > old_size) {
+ size_t size_increase = new_size - old_size;
+ if (size_increase > (a->capacity - a->curr_offset)) {
+ new_size = old_size;
+ size_increase = 0;
+ }
+ memset(&a->buffer[a->curr_offset], 0, size_increase);
+ }
+ a->curr_offset = a->prev_offset + new_size;
+ ptr = old_memory;
+ } else {
+ ptr = arena_alloc_aligned(a, new_size, alignment);
+ if (ptr != NULL) {
+ size_t copy_size = old_size < new_size ? old_size : new_size;
+ memmove(ptr, old_memory, copy_size);
+ }
+ }
+ }
+
+ return ptr;
+}
+
+void *arena_resize(struct Arena *a, void *old_mem, size_t old_size,
+ size_t new_size) {
+ return arena_resize_aligned(a, old_mem, old_size, new_size,
+ DEFAULT_ALIGNMENT);
+}
+
+void arena_clear(struct Arena *a) {
+ a->curr_offset = 0;
+ a->prev_offset = 0;
+}
+
+//===========================================================================================
+// ---------------------------------- STACK
+// -------------------------------------------------
+//===========================================================================================
+
+void stack_init(struct stack *s, void *backing_store, size_t capacity) {
+ s->buffer = (unsigned char *)backing_store;
+ s->prev_offset = 0;
+ s->curr_offset = 0;
+ s->capacity = capacity;
+}
+
+size_t calc_padding_with_header(uintptr_t ptr, uintptr_t alignment,
+ size_t hdr_sz) {
+ uintptr_t p, a, modulo, padding, space_needed;
+
+ assert(is_power_of_two(alignment));
+
+ padding = space_needed = 0;
+
+ p = ptr;
+ a = alignment;
+ modulo = fast_modulo(p, a);
+
+ if (modulo != 0) {
+ padding = a - modulo;
+ }
+
+ space_needed = (uintptr_t)hdr_sz;
+ if (padding < space_needed) {
+ space_needed -= padding;
+ if (fast_modulo(space_needed, a) != 0) {
+ padding = padding + space_needed + a;
+ } else {
+ padding = padding + space_needed;
+ }
+ }
+
+ return (size_t)padding;
+}
+
+struct ResVoid stack_alloc_aligned(struct stack *s, size_t size,
+ size_t alignment) {
+ uintptr_t curr_addr, next_addr;
+ size_t padding;
+ struct stack_hdr *header;
+
+ assert(is_power_of_two(alignment));
+ if (alignment > 128) {
+ alignment = 128;
+ }
+
+ struct ResVoid result = {.status = MEM_OK, .bytes_count = 0, .memory = 0};
+
+ curr_addr = (uintptr_t)s->buffer + (uintptr_t)s->curr_offset;
+ padding = calc_padding_with_header(curr_addr, (uintptr_t)alignment,
+ sizeof(struct stack_hdr));
+
+ if (size > s->capacity - (s->curr_offset + padding)) {
+ result.status = MEM_FULL;
+ return result;
+ }
+
+ next_addr = curr_addr + (uintptr_t)padding;
+ header = (struct stack_hdr *)(next_addr - sizeof(struct stack_hdr));
+ header->prev_offset = s->prev_offset;
+ header->padding = padding;
+
+ s->prev_offset = s->curr_offset + padding;
+ s->curr_offset = s->prev_offset + size;
+
+ result.memory = memset((void *)next_addr, 0, size);
+ result.bytes_count = size;
+
+ return result;
+}
+
+struct ResVoid stack_alloc(struct stack *s, size_t size) {
+ return stack_alloc_aligned(s, size, DEFAULT_ALIGNMENT);
+}
+
+enum MemStatus stack_free(struct stack *s) {
+ uintptr_t last_ele = (uintptr_t)s->buffer + (uintptr_t)s->prev_offset;
+ struct stack_hdr *header =
+ (struct stack_hdr *)(last_ele - sizeof(struct stack_hdr));
+
+ uintptr_t prev_ele = (uintptr_t)s->buffer + (uintptr_t)header->prev_offset;
+ s->curr_offset =
+ (size_t)((last_ele - (uintptr_t)header->padding) - (uintptr_t)s->buffer);
+ s->prev_offset = (size_t)(prev_ele - (uintptr_t)s->buffer);
+
+ return MEM_OK;
+}
+
+struct ResVoid stack_resize_aligned(struct stack *s, void *old_memory,
+ size_t old_size, size_t new_size,
+ size_t alignment) {
+ struct ResVoid result = {.status = MEM_OK, .bytes_count = 0, .memory = 0};
+
+ if (old_memory < s->buffer || old_memory > s->buffer + s->capacity) {
+ result.status = MEM_OUT_OF_BOUNDS;
+ return result;
+ }
+
+ // is_last_element()
+ if (s->buffer + s->prev_offset == old_memory) {
+ if (new_size > old_size) {
+ size_t size_difference = new_size - old_size;
+ if (size_difference > s->capacity - s->curr_offset) {
+ result.status = MEM_FULL;
+ return result;
+ }
+
+ memset(&s->buffer[s->curr_offset], 0, size_difference);
+ }
+ s->curr_offset = s->prev_offset + new_size;
+
+ result.memory = old_memory;
+ return result;
+ }
+
+ result = stack_alloc_aligned(s, new_size, alignment);
+ size_t min_size =
+ old_size < result.bytes_count ? old_size : result.bytes_count;
+ memmove(result.memory, old_memory, min_size);
+
+ return result;
+}
+
+struct ResVoid stack_resize(struct stack *s, void *old_memory, size_t old_size,
+ size_t new_size) {
+ return stack_resize_aligned(s, old_memory, old_size, new_size,
+ DEFAULT_ALIGNMENT);
+}
+
+void stack_clear(struct stack *s) {
+ s->prev_offset = 0;
+ s->curr_offset = 0;
+}
diff --git a/source/memory/memory.h b/source/memory/memory.h
new file mode 100755
index 0000000..e402c0b
--- /dev/null
+++ b/source/memory/memory.h
@@ -0,0 +1,98 @@
+#ifndef AMR_MEMORY_H
+#define AMR_MEMORY_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <assert.h>
+#include <string.h>
+
+#ifndef AMR_TYPES_H
+#define AMR_TYPES_H
+
+typedef int8_t s8;
+typedef int16_t s16;
+typedef int32_t s32;
+typedef int64_t s64;
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+typedef u8 b8;
+
+#endif // AMR_TYPES_H
+
+
+#ifndef DEFAULT_ALIGNMENT
+#define DEFAULT_ALIGNMENT (2*sizeof(void *))
+#endif
+
+// @todo: build a logging mechanism for handling errors
+// maybe read about that
+
+enum MemStatus { MEM_OK=0, MEM_OUT_OF_BOUNDS, MEM_FULL };
+
+struct ResVoid {
+ enum MemStatus status;
+ size_t bytes_count;
+ void* memory;
+};
+
+b8 is_power_of_two(uintptr_t x);
+uintptr_t fast_modulo(uintptr_t p, uintptr_t a);
+uintptr_t align_forward(uintptr_t ptr, size_t align);
+
+//===========================================================================================
+// ---------------------------------- ARENA -------------------------------------------------
+//===========================================================================================
+
+struct Arena {
+ unsigned char* buffer;
+ size_t prev_offset;
+ size_t curr_offset;
+ size_t capacity;
+};
+
+void arena_init(struct Arena *a, unsigned char *backing_store, size_t capacity);
+void* arena_alloc_aligned(struct Arena* a, size_t size, size_t align);
+void* arena_alloc(struct Arena* a, size_t size);
+void* arena_resize_aligned(struct Arena* a, void* old_memory, size_t old_size,
+ size_t new_size, size_t align);
+void* arena_resize(struct Arena* a, void* old_mem, size_t old_size,
+ size_t new_size);
+void arena_clear(struct Arena *a);
+
+//===========================================================================================
+// ---------------------------------- STACK -------------------------------------------------
+//===========================================================================================
+
+/*
+* @todo: stack needs to be updated, it's really just a work in progress right now.
+* The main thing is minimizing the use of compound types, since that is pretty annoying to deal with.
+* I would rather write code that makes sure to collapse all possible cases and lets me just not worry about code.
+* Would rather stick to worrying about data being data
+*/
+
+struct stack {
+ unsigned char* buffer;
+ size_t prev_offset;
+ size_t curr_offset;
+ size_t capacity;
+};
+
+struct stack_hdr {
+ size_t prev_offset;
+ size_t padding;
+};
+
+void stack_init(struct stack* s, void *backing_store, size_t capacity);
+struct ResVoid stack_alloc_aligned(struct stack* s, size_t size, size_t alignment);
+struct ResVoid stack_alloc(struct stack* s, size_t size);
+enum MemStatus stack_free(struct stack* s);
+struct ResVoid stack_resize_aligned(struct stack* s, void* old_memory, size_t old_size,
+ size_t new_size, size_t alignment);
+struct ResVoid stack_resize(struct stack* s, void* old_memory, size_t old_size, size_t new_size);
+void stack_clear(struct stack* s);
+
+#endif
diff --git a/source/shaders/cq_batched.fs.glsl b/source/shaders/cq_batched.fs.glsl
new file mode 100644
index 0000000..7f0467a
--- /dev/null
+++ b/source/shaders/cq_batched.fs.glsl
@@ -0,0 +1,9 @@
+#version 330 core
+
+in vec4 vertexColor;
+out vec4 FragColor;
+
+void main() {
+ FragColor = vertexColor;
+}
+
diff --git a/source/shaders/cq_batched.vs.glsl b/source/shaders/cq_batched.vs.glsl
new file mode 100644
index 0000000..84180e9
--- /dev/null
+++ b/source/shaders/cq_batched.vs.glsl
@@ -0,0 +1,10 @@
+#version 330 core
+layout(location=0) in vec4 aPos;
+layout(location=1) in vec3 aColor;
+
+out vec4 vertexColor;
+
+void main() {
+ gl_Position = aPos;
+ vertexColor = vec4(aColor, 1.0);
+}