util_idraw2d.c (14835B)
1 ////////////////////////////////////////////////////////////////// 2 // util_idraw2d.c 3 4 ////////////////////////////////////////////////////////////////// 5 // data 6 7 RV_INTERNAL rv_str8 rv_idraw2d_v_src = S("#version 330 core\n" rv_strify( 8 layout(location = 0) in vec2 a_pos; 9 layout(location = 1) in vec2 a_uv; 10 layout(location = 2) in vec4 a_color; 11 precision mediump float; 12 13 uniform vec2 u_resolution; 14 15 out vec4 f_color; 16 out vec2 f_uv; 17 18 void main() 19 { 20 vec2 xy_pos = a_pos/u_resolution * 2.0; 21 xy_pos.y = -xy_pos.y; 22 xy_pos += vec2(-1.0, 1.0); 23 gl_Position = vec4(xy_pos, 0.0, 1.0); 24 f_color = a_color; 25 f_uv = a_uv; 26 } 27 )); 28 29 RV_INTERNAL rv_str8 rv_idraw2d_f_src = S("#version 330 core\n" rv_strify( 30 precision mediump float; 31 in vec4 f_color; 32 in vec2 f_uv; 33 out vec4 frag_color; 34 uniform sampler2D u_tex; 35 uniform float u_omit_tex; 36 void main() 37 { 38 vec4 color = f_color; 39 if (u_omit_tex < 0.0) { 40 color.a *= texture(u_tex, f_uv).r; 41 } else if (u_omit_tex < 1.0) { 42 color *= texture(u_tex, f_uv); 43 } 44 frag_color = color; 45 } 46 )); 47 48 ////////////////////////////////////////////////////////////////// 49 // init 50 51 RV_GLOBAL void rvi2d_create(rv_idraw2d_ctx* ctx, rv_arena* arena, rv_command_list_t* create_commands) 52 { 53 rvi2d_destroy(ctx, arena, create_commands); 54 55 ctx->arena = rv_arena_alloc(); 56 ctx->permanent_arena = rv_arena_alloc(.commit_size = KB(4), .reserve_size = KB(4)); 57 58 // make render objects 59 ctx->vbo = rv_push_compound(ctx->permanent_arena, rv_vbo_t, {.usage = RV_BUFFER_USAGE_DYNAMIC}); 60 ctx->ibo = rv_push_compound(ctx->permanent_arena, rv_ibo_t, {.usage = RV_BUFFER_USAGE_DYNAMIC, .elem_size = sizeof(u32)}); 61 rv_shader_t* vert = rv_push_compound(ctx->permanent_arena, rv_shader_t, {.source = rv_idraw2d_v_src, .type = RV_SHADER_TYPE_VERTEX}); 62 rv_shader_t* frag = rv_push_compound(ctx->permanent_arena, rv_shader_t, {.source = rv_idraw2d_f_src, .type = RV_SHADER_TYPE_FRAGMENT}); 63 ctx->pip = rv_push(ctx->permanent_arena, rv_pipeline_t); 64 65 // construct pipeline 66 rv_pipeline_push_shader(arena, ctx->pip, vert); 67 rv_pipeline_push_shader(arena, ctx->pip, frag); 68 rv_pipeline_push_vattr(arena, ctx->pip, RV_VATTR_TYPE_FLOAT2, 0, 0); 69 rv_pipeline_push_vattr(arena, ctx->pip, RV_VATTR_TYPE_FLOAT2, 0, 0); 70 rv_pipeline_push_vattr(arena, ctx->pip, RV_VATTR_TYPE_FLOAT4, 0, 0); 71 ctx->u_tex = rv_pipeline_push_uniform(ctx->permanent_arena, ctx->pip, S("u_tex"), RV_UNIFORM_TEX); 72 ctx->u_omit_tex = rv_pipeline_push_uniform(ctx->permanent_arena, ctx->pip, S("u_omit_tex"), RV_UNIFORM_F32); 73 ctx->u_resolution = rv_pipeline_push_uniform(ctx->permanent_arena, ctx->pip, S("u_resolution"), RV_UNIFORM_VEC2); 74 75 // add creation commands 76 rv_cmd_push_obj(arena, create_commands, RV_COMMAND_OBJ_VERTEX, RV_RENDER_OBJ_OP_CREATE, ctx->vbo); 77 rv_cmd_push_obj(arena, create_commands, RV_COMMAND_OBJ_INDEX, RV_RENDER_OBJ_OP_CREATE, ctx->ibo); 78 rv_cmd_push_obj(arena, create_commands, RV_COMMAND_OBJ_SHADER, RV_RENDER_OBJ_OP_CREATE, vert); 79 rv_cmd_push_obj(arena, create_commands, RV_COMMAND_OBJ_SHADER, RV_RENDER_OBJ_OP_CREATE, frag); 80 rv_cmd_push_obj(arena, create_commands, RV_COMMAND_OBJ_PIPELINE, RV_RENDER_OBJ_OP_CREATE, ctx->pip); 81 rv_cmd_push_obj(arena, create_commands, RV_COMMAND_OBJ_SHADER, RV_RENDER_OBJ_OP_DESTROY, frag); // don't need anymore 82 rv_cmd_push_obj(arena, create_commands, RV_COMMAND_OBJ_SHADER, RV_RENDER_OBJ_OP_DESTROY, vert); // don't need anymore 83 { // set default uniforms 84 rv_cmd_push_obj(arena, create_commands, RV_COMMAND_OBJ_PIPELINE, RV_RENDER_OBJ_OP_BIND, ctx->pip); 85 rv_cmd_push_uniform_update(arena, create_commands, ctx->u_omit_tex, {.v_f32 = 1.0f}); 86 ctx->u_omit_tex_last = 1.0f; 87 rv_cmd_push_uniform_update(arena, create_commands, ctx->u_resolution, {.v_vec2 = rv_v2(1,1)}); 88 ctx->u_resolution_last = rv_v2(1,1); 89 rv_cmd_push_obj(arena, create_commands, RV_COMMAND_OBJ_PIPELINE, RV_RENDER_OBJ_OP_BIND, NULL); 90 } 91 92 rv_bump_cache_create(&ctx->vertex, sizeof(rv_vec2) + sizeof(rv_vec2) + sizeof(rv_vec4)); 93 rv_bump_cache_create(&ctx->index, sizeof(u32)); 94 95 ctx->default_binds = rv_push(ctx->permanent_arena, rv_command_list_node_t); 96 rv_cmd_push_obj(ctx->permanent_arena, &ctx->default_binds->commands, RV_COMMAND_OBJ_PIPELINE, RV_RENDER_OBJ_OP_BIND, ctx->pip); 97 98 rv_llist_stack_push(ctx->custom_binds_stack, ctx->default_binds); 99 } 100 101 RV_GLOBAL void rvi2d_destroy(rv_idraw2d_ctx* ctx, rv_arena* arena, rv_command_list_t* destroy_commands) 102 { 103 // just use something to check if we're already destroyed 104 if (ctx->pip) { 105 // make copies, the memory will be invalid otherwise 106 rv_vbo_t* vbo_vertex_copy = rv_push_copy(arena, rv_vbo_t, ctx->vbo); 107 rv_ibo_t* ibo_copy = rv_push_copy(arena, rv_ibo_t, ctx->ibo); 108 rv_pipeline_t* pip_copy = rv_push_copy(arena, rv_pipeline_t, ctx->ibo); 109 110 rv_cmd_push_obj(arena, destroy_commands, RV_COMMAND_OBJ_VERTEX, RV_RENDER_OBJ_OP_DESTROY, vbo_vertex_copy); 111 rv_cmd_push_obj(arena, destroy_commands, RV_COMMAND_OBJ_INDEX, RV_RENDER_OBJ_OP_DESTROY, ibo_copy); 112 rv_cmd_push_obj(arena, destroy_commands, RV_COMMAND_OBJ_PIPELINE, RV_RENDER_OBJ_OP_DESTROY, pip_copy); 113 114 rv_arena_release(ctx->arena); 115 rv_arena_release(ctx->permanent_arena); 116 rv_bump_cache_destroy(&ctx->vertex); 117 rv_bump_cache_destroy(&ctx->index); 118 119 *ctx = (rv_idraw2d_ctx){0}; 120 } 121 } 122 123 ////////////////////////////////////////////////////////////////// 124 // draw 125 126 RV_GLOBAL rv_command_t* rvi2d_draw_and_reset(rv_idraw2d_ctx* ctx, rv_arena* arena, rv_command_list_t* render_commands) 127 { 128 rv_command_t* res = rvi2d_break(ctx); 129 130 { // update vbo 131 rv_command_t update_vbo_cmd = rv_cmd_obj(RV_COMMAND_OBJ_VERTEX, RV_RENDER_OBJ_OP_UPDATE, ctx->vbo); 132 rv_bump_cache_upload_and_reset(&ctx->vertex, arena, &update_vbo_cmd.obj.vbo_update.data, &update_vbo_cmd.obj.vbo_update.size); 133 rv_cmd_push_copy(arena, render_commands, update_vbo_cmd); 134 } 135 136 { // update ibo 137 rv_command_t update_ibo_cmd = rv_cmd_obj(RV_COMMAND_OBJ_INDEX, RV_RENDER_OBJ_OP_UPDATE, ctx->ibo); 138 rv_bump_cache_upload_and_reset(&ctx->index, arena, &update_ibo_cmd.obj.ibo_update.data, &update_ibo_cmd.obj.ibo_update.size); 139 rv_cmd_push_copy(arena, render_commands, update_ibo_cmd); 140 } 141 142 // push draw commands 143 rv_render_copy_commands(arena, render_commands, &ctx->commands); 144 145 // check that custom binds steck is where it started 146 if (ctx->custom_binds_stack->next) { 147 rv_unreachable(); 148 ctx->custom_binds_stack = NULL; 149 rv_llist_stack_push(ctx->custom_binds_stack, ctx->default_binds); 150 } 151 152 ctx->commands = (rv_command_list_t){0}; 153 ctx->last_commited_command = NULL; 154 ctx->last_bind = NULL; 155 rv_arena_clear(ctx->arena); 156 157 return res; 158 } 159 160 ////////////////////////////////////////////////////////////////// 161 // core update functions 162 163 164 RV_GLOBAL rv_command_t* rvi2d_break(rv_idraw2d_ctx* ctx) 165 { 166 if (ctx->last_bind != ctx->custom_binds_stack) { 167 rv_render_copy_commands(ctx->arena, &ctx->commands, &ctx->custom_binds_stack->commands); 168 169 // force rebind vbo and ibo 170 rv_cmd_push_obj(ctx->arena, &ctx->commands, RV_COMMAND_OBJ_VERTEX, RV_RENDER_OBJ_OP_BIND, ctx->vbo); 171 rv_cmd_push_obj(ctx->arena, &ctx->commands, RV_COMMAND_OBJ_INDEX, RV_RENDER_OBJ_OP_BIND, ctx->ibo); 172 // force update all uniforms 173 rv_cmd_push_uniform_update(ctx->arena, &ctx->commands, ctx->u_resolution, {.v_vec2 = ctx->u_resolution_last}); 174 rv_cmd_push_uniform_update(ctx->arena, &ctx->commands, ctx->u_tex, {.v_tex = ctx->u_tex_last}); 175 rv_cmd_push_uniform_update(ctx->arena, &ctx->commands, ctx->u_omit_tex, {.v_f32 = ctx->u_omit_tex_last}); 176 177 ctx->last_bind = ctx->custom_binds_stack; 178 } 179 180 s64 first, count; 181 rv_bump_cache_break(&ctx->vertex, NULL, NULL); 182 rv_bump_cache_break(&ctx->index, &first, &count); 183 184 if (count > 0) { 185 rv_command_t draw_cmd = { 186 .type = RV_COMMAND_DRAW, 187 .draw = { 188 .first = first, 189 .count = count, 190 }}; 191 rv_cmd_push_copy(ctx->arena, &ctx->commands, draw_cmd); 192 } 193 194 rv_command_t* first_cmd; 195 if (ctx->last_commited_command == NULL) { 196 first_cmd = ctx->commands.first; 197 } else { 198 first_cmd = ctx->last_commited_command->next; 199 } 200 ctx->last_commited_command = ctx->commands.last; 201 202 return first_cmd; 203 } 204 205 RV_GLOBAL rv_command_t* rvi2d_camera(rv_idraw2d_ctx* ctx, rv_vec2 screen_size) 206 { 207 rv_command_t* res = rvi2d_break(ctx); 208 rv_cmd_push_uniform_update(ctx->arena, &ctx->commands, ctx->u_resolution, {.v_vec2 = screen_size}); 209 ctx->u_resolution_last = screen_size; 210 211 return res; 212 } 213 214 RV_GLOBAL rv_command_t* rvi2d_tex(rv_idraw2d_ctx* ctx, rv_texture_t* tex) 215 { 216 return rvi2d_tex_ex(ctx, tex, false); 217 } 218 219 RV_GLOBAL rv_command_t* rvi2d_tex_ex(rv_idraw2d_ctx* ctx, rv_texture_t* tex, bool32 is_red_channel) 220 { 221 rv_command_t* res = NULL; 222 if (ctx->last_tex == tex) { 223 // nothing to change 224 } else { 225 res = rvi2d_break(ctx); 226 227 rv_cmd_push_uniform_update(ctx->arena, &ctx->commands, ctx->u_tex, {.v_tex = tex}); 228 ctx->u_tex_last = tex; 229 230 if (tex) { 231 rv_cmd_push_uniform_update(ctx->arena, &ctx->commands, ctx->u_omit_tex, {.v_f32 = is_red_channel ? -1.0f : 0.0f}); 232 ctx->u_omit_tex_last = is_red_channel ? -1.0f : 0.0f; 233 } else { 234 rv_cmd_push_uniform_update(ctx->arena, &ctx->commands, ctx->u_omit_tex, {.v_f32 = 1.0f}); 235 ctx->u_omit_tex_last = 1.0f; 236 } 237 238 ctx->last_tex = tex; 239 } 240 241 return res; 242 } 243 244 RV_GLOBAL rv_command_t* rvi2d_custom_binds_push(rv_idraw2d_ctx* ctx, rv_command_list_t* copy_binds) 245 { 246 rv_command_t* res = rvi2d_break(ctx); 247 248 rv_command_list_node_t* n = rv_push(ctx->arena, rv_command_list_node_t); 249 rv_render_copy_commands(ctx->arena, &ctx->commands, copy_binds); 250 rv_llist_stack_push(ctx->custom_binds_stack, n); 251 252 return res; 253 254 } 255 256 RV_GLOBAL rv_command_t* rvi2d_custom_binds_pop(rv_idraw2d_ctx* ctx) 257 { 258 rv_command_t* res = rvi2d_break(ctx); 259 260 rv_command_list_node_t* tmp = ctx->custom_binds_stack; 261 rv_llist_stack_pop(ctx->custom_binds_stack); 262 tmp->next = NULL; 263 264 if (!ctx->custom_binds_stack) { 265 rv_unreachable(); 266 rv_llist_stack_push(ctx->custom_binds_stack, ctx->default_binds); 267 } 268 269 return res; 270 } 271 272 RV_GLOBAL void rvi2d_triangle(rv_idraw2d_ctx* ctx, 273 rv_vec2 pos_a, rv_vec2 pos_b, rv_vec2 pos_c, 274 rv_vec2 uv_a, rv_vec2 uv_b, rv_vec2 uv_c, 275 rv_color_t color) 276 { 277 u32 last_index = ctx->vertex.total_count; 278 279 rv_vec4 c = rv_vec4_from_color(color); 280 struct { 281 rv_vec2 pos, uv; 282 rv_vec4 color; 283 } copy_from[3] = { 284 {.pos = pos_a, .uv = uv_a, .color = c}, 285 {.pos = pos_b, .uv = uv_b, .color = c}, 286 {.pos = pos_c, .uv = uv_c, .color = c}, 287 }; 288 rv_bump_cache_push_many(&ctx->vertex, ©_from, 3); 289 290 u32 indices[3] = {last_index + 0, last_index + 1, last_index + 2}; 291 rv_bump_cache_push_many(&ctx->index, &indices, 3); 292 } 293 294 RV_GLOBAL void rvi2d_quad(rv_idraw2d_ctx* ctx, 295 rv_vec2 pos_tl, rv_vec2 pos_tr, rv_vec2 pos_bl, rv_vec2 pos_br, 296 rv_vec2 uv_tl, rv_vec2 uv_tr, rv_vec2 uv_bl, rv_vec2 uv_br, 297 rv_color_t color) 298 { 299 u32 index_begin = ctx->vertex.total_count; 300 301 rv_vec4 c = rv_vec4_from_color(color); 302 struct { 303 rv_vec2 pos, uv; 304 rv_vec4 color; 305 } copy_from[4] = { 306 {.pos = pos_tl, .uv = uv_tl, .color = c}, 307 {.pos = pos_tr, .uv = uv_tr, .color = c}, 308 {.pos = pos_bl, .uv = uv_bl, .color = c}, 309 {.pos = pos_br, .uv = uv_br, .color = c}, 310 }; 311 rv_bump_cache_push_many(&ctx->vertex, ©_from, 4); 312 313 u32 indices[6] = { 314 index_begin + 0, // tl +--+ 315 index_begin + 1, // tr | / 316 index_begin + 2, // bl +/ 317 index_begin + 1, // tr /+ 318 index_begin + 2, // bl / | 319 index_begin + 3, // br +--+ 320 }; 321 rv_bump_cache_push_many(&ctx->index, &indices, 6); 322 } 323 324 ////////////////////////////////////////////////////////////////// 325 // helpers functions 326 327 RV_GLOBAL void rvi2d_rect(rv_idraw2d_ctx* ctx, rv_rect rect, rv_color_t color) 328 { 329 rvi2d_quad(ctx, 330 rv_vec2_add(rect.xy, rv_v2(0, 0)), 331 rv_vec2_add(rect.xy, rv_v2(rect.w, 0)), 332 rv_vec2_add(rect.xy, rv_v2(0, rect.h)), 333 rv_vec2_add(rect.xy, rv_v2(rect.w, rect.h)), 334 rv_v2(0,0), rv_v2(1,0), rv_v2(0,1), rv_v2(1,1), color); 335 } 336 RV_GLOBAL void rvi2d_line(rv_idraw2d_ctx* ctx, rv_vec2 a, rv_vec2 b, f32 thickness, rv_color_t color) 337 { 338 rv_unreachable(); // not implemented 339 } 340 RV_GLOBAL void rvi2d_circle(rv_idraw2d_ctx* ctx, rv_vec2 center, f32 radius, s32 segments) 341 { 342 rv_unreachable(); // not implemented 343 } 344 345 RV_GLOBAL rv_command_t* rvi2d_text(rv_idraw2d_ctx* ctx, rv_ascii_font_t* font, rv_vec2 bl_first, rv_str8 str, rv_color_t color) 346 { 347 rv_command_t* res = rvi2d_tex_ex(ctx, font->tex, true); 348 349 rv_vec2 cursor = bl_first; 350 for (s32 i = 0; i < str.len; i++) { 351 if (str.str[i] == '\n') { 352 cursor.x = bl_first.x; 353 cursor.y += font->linedist; 354 continue; 355 } 356 357 if (str.str[i] < 32 || str.str[i] > 128) continue; 358 359 stbtt_packedchar c = font->cdata[str.str[i] - 32]; 360 361 rv_vec2 off1 = rv_v2(c.xoff, c.yoff); 362 rv_vec2 off2 = rv_v2(c.xoff2, c.yoff2); 363 364 rv_rect r = {0}; 365 r.x = rv_min(off1.x, off2.x); 366 r.y = rv_min(off1.y, off2.y); 367 r.w = fabsf(off1.x - off2.x); 368 r.h = fabsf(off1.y - off2.y); 369 r.xy = rv_vec2_add(r.xy, cursor); 370 371 rv_vec2 uv_tr = rv_vec2_div(rv_v2(c.x1, c.y1), font->tex->size); 372 rv_vec2 uv_bl = rv_vec2_div(rv_v2(c.x0, c.y0), font->tex->size); 373 rv_vec2 uv_tl = rv_v2(uv_bl.x, uv_tr.y); 374 rv_vec2 uv_br = rv_v2(uv_tr.x, uv_bl.y); 375 376 rvi2d_quad(ctx, 377 rv_vec2_add(r.xy, rv_v2(0, 0)), 378 rv_vec2_add(r.xy, rv_v2(r.w, 0)), 379 rv_vec2_add(r.xy, rv_v2(0, r.h)), 380 rv_vec2_add(r.xy, rv_v2(r.w, r.h)), 381 uv_bl, uv_br, uv_tl, uv_tr, 382 color); 383 384 cursor.x += c.xadvance; 385 } 386 387 return res; 388 }