diff options
author | talha <sarcxd@gmail.com> | 2024-12-11 00:53:05 +0500 |
---|---|---|
committer | talha <sarcxd@gmail.com> | 2024-12-11 00:53:05 +0500 |
commit | 9022d6c1fa64689ab9203d967a8cd31b406155da (patch) | |
tree | 5a85440c894545fc0e8dd823b6cc626c2be01fbc /source | |
parent | 60085c01f190c01adaf24b6f337e2528d1de23b7 (diff) |
Added batch rendering (however basic it might be)
Diffstat (limited to 'source')
-rwxr-xr-x | source/main.cpp | 349 | ||||
-rwxr-xr-x | source/math.h | 228 | ||||
-rw-r--r-- | source/memory/arena.h | 78 | ||||
-rwxr-xr-x | source/memory/memory.c | 246 | ||||
-rwxr-xr-x | source/memory/memory.h | 98 | ||||
-rw-r--r-- | source/shaders/cq_batched.fs.glsl | 9 | ||||
-rw-r--r-- | source/shaders/cq_batched.vs.glsl | 10 |
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); +} |