#pragma once #include "gl.h" // this is maybe a bit too much c++ voodoo for my liking const U32 BATCH_VERTICES_MAX = 16384; template struct GL_BATCH; // func for setting sampler idx inside of vertex data template using GL_BATCH_VERTEX_SAMPLER_IDX_FN = void (*)(VERTEX* vertex, U8 idx); template using GL_BATCH_SETUP_FN = void (*)(GL_BATCH* batch); template struct GL_BATCH_CALL { LIST vertices{}; VEC2 clip_start; VEC2 clip_dim; VEC2 viewport_start; VEC2 viewport_dim; U16 primitive; LIST textures{}; }; template struct GL_BATCH { GL_DATA* gl; GL_SHADER_PROGRAM* shader; VEC2 clip_start; VEC2 clip_dim; VEC2 viewport_start; VEC2 viewport_dim; GLuint vbuffer; LIST> calls{}; // func for setting up vertex attribs GL_BATCH_SETUP_FN setup_cb; }; /* * takes in a callback func that gets called before the batch is drawn * * should set up vertex attribs correctly according to vertex fmt */ template inline GL_BATCH* gl_batch_create( GL_DATA* gl, GL_SHADER_PROGRAM* shader, GL_BATCH_SETUP_FN setup_cb ) { GL_BATCH* batch = new GL_BATCH; batch->gl = gl; batch->shader = shader; batch->clip_start = gl->clip_start; batch->clip_dim = gl->clip_dim; batch->viewport_start = gl->viewport_start; batch->viewport_dim = gl->viewport_dim; batch->setup_cb = setup_cb; glGenBuffers( 1, &batch->vbuffer ); glBindBuffer( GL_ARRAY_BUFFER, batch->vbuffer ); // make sure we have enough vram glBufferData( GL_ARRAY_BUFFER, sizeof( VERTEX ) * BATCH_VERTICES_MAX, 0, GL_DYNAMIC_DRAW ); return batch; } template inline void gl_batch_destroy( GL_BATCH* batch ) { glDeleteBuffers( &batch->vbuffer ); delete batch; } template inline void gl_batch_empty( GL_BATCH* batch ) { batch->calls.clear(); } template inline void gl_batch_draw( GL_BATCH* batch ) { glUseProgram( batch->shader->id ); VEC2 vp_start, vp_dim, clip_start, clip_dim; gl_get_viewport( batch->gl, &vp_start, &vp_dim ); gl_get_clip( batch->gl, &clip_start, &clip_dim ); batch->setup_cb( batch ); batch->calls.each( fn( GL_BATCH_CALL* call ) { for( U32 i = 0; i < call->textures.size; ++i ) { glActiveTexture( GL_TEXTURE0 + i ); glBindTexture( GL_TEXTURE_2D, call->textures.data[i] ? call->textures.data[i]->id : 0 ); } glBindBuffer( GL_ARRAY_BUFFER, batch->vbuffer ); glBufferData( GL_ARRAY_BUFFER, sizeof(VERTEX) * call->vertices.size, call->vertices.data, GL_DYNAMIC_DRAW ); glBindBuffer( GL_ARRAY_BUFFER, 0 ); glDrawArrays( call->primitive, 0, call->vertices.size ); } ); gl_set_viewport( batch->gl, vp_start, vp_dim ); gl_set_clip( batch->gl, clip_start, clip_dim ); } template inline GL_BATCH_CALL* gl_batch_get_call( GL_BATCH* batch, GL_TEX2D* tex, U16 primitive, I32* texid ) { U32 n = batch->calls.size; VEC2 vp_start = batch->viewport_start; VEC2 vp_dim = batch->viewport_dim; VEC2 clip_start = batch->clip_start; VEC2 clip_dim = batch->clip_dim; if( n ) { GL_BATCH_CALL* last = &batch->calls.data[n - 1]; if( last->primitive == primitive && vp_start == last->clip_start && vp_dim == last->viewport_dim && clip_start == last->clip_start && clip_dim == last->clip_dim ) { I32 idx = last->textures.idx_of( tex ); if( idx != -1 ) { *texid = idx; return last; } else if( last->textures.size < batch->gl->shader_texture_limit ) { *texid = last->textures.size; last->textures.push( tex ); return last; } } } GL_BATCH_CALL call{ .vertices = {}, .clip_start = batch->clip_start, .clip_dim = batch->clip_dim, .viewport_start = batch->viewport_start, .viewport_dim = batch->viewport_dim, .primitive = primitive, .textures = {}, }; *texid = 0; call.textures.push( tex ); return batch->calls.push( call ); } template inline void gl_batch_insert( GL_BATCH* batch, VERTEX* vertices, U32 count, GL_TEX2D* tex, U16 primitive = GL_TRIANGLES ) { if( !count ) return; GL_BATCH_CALL* call; I32 texid; VEC2 vp_start, vp_dim; VEC2 clip_start, clip_dim; gl_get_viewport( batch->gl, &vp_start, &vp_dim ); gl_get_clip( batch->gl, &clip_start, &clip_dim ); batch->viewport_start = vp_start; batch->viewport_dim = vp_dim; batch->clip_start = clip_start; batch->clip_dim = clip_dim; call = gl_batch_get_call( batch, tex, primitive, &texid ); for( U32 i = 0; i < count; ++i ) { vertices[i].sampler = (!!tex)? texid : SAMPLER_ID_NONE; call->vertices.push( vertices[i] ); } }