package render import rl "libs:raylib" import gl "vendor:OpenGL" MAX_VERTICES :: 4096 MAX_INDICES :: 4096 MAX_DRAWS :: 4096 Vec3 :: [3]f32 Draw_Call :: struct { color: rl.Color, first_index, num_indices: i32, base_vertex: i32, } Immediate_Buffer :: enum { Vertices = 0, Indices, } Immediate_Draw_State :: struct { vertices: [MAX_VERTICES]Vec3, indices: [MAX_INDICES]u16, draws: [MAX_DRAWS]Draw_Call, num_vertices, num_indices, num_draws: int, vbo_ids: [Immediate_Buffer]u32, vao_id: u32, } push_mesh :: proc(ds: ^Immediate_Draw_State, vertices: []Vec3, indices: []u16, color: rl.Color) { copy(ds.vertices[ds.num_vertices:][:len(vertices)], vertices) copy(ds.indices[ds.num_indices:][:len(indices)], indices) ds.draws[ds.num_draws] = { color = color, first_index = i32(ds.num_indices), num_indices = i32(len(indices)), base_vertex = i32(ds.num_vertices), } ds.num_vertices += len(vertices) ds.num_indices += len(indices) ds.num_draws += 1 } // rudimentary batching find_or_push_cmd :: proc(ds: ^Immediate_Draw_State, color: rl.Color) -> (cmd: ^Draw_Call) { if ds.num_draws > 0 && ds.draws[ds.num_draws - 1].color == color { cmd = &ds.draws[ds.num_draws - 1] assert(i32(ds.num_indices) == cmd.first_index + cmd.num_indices) } else { ds.draws[ds.num_draws] = { color = color, first_index = i32(ds.num_indices), num_indices = 0, base_vertex = i32(ds.num_vertices), } cmd = &ds.draws[ds.num_draws] ds.num_draws += 1 } return } push_tri :: proc(ds: ^Immediate_Draw_State, v1, v2, v3: Vec3, color: rl.Color) { draw := find_or_push_cmd(ds, color) ds.vertices[ds.num_vertices + 0] = v1 ds.vertices[ds.num_vertices + 1] = v2 ds.vertices[ds.num_vertices + 2] = v3 ds.indices[ds.num_indices + 0] = u16(ds.num_vertices + 0) ds.indices[ds.num_indices + 1] = u16(ds.num_vertices + 1) ds.indices[ds.num_indices + 2] = u16(ds.num_vertices + 2) ds.num_vertices += 3 ds.num_indices += 3 draw.num_indices += 3 } push_quad :: proc(ds: ^Immediate_Draw_State, v1, v2, v3, v4: Vec3, color: rl.Color) { push_tri(ds, v1, v2, v3, color) push_tri(ds, v1, v3, v4, color) } draw_batch :: proc(ds: ^Immediate_Draw_State) { if ds.vbo_ids == {} { gl.GenBuffers(len(ds.vbo_ids), &ds.vbo_ids[.Vertices]) gl.GenVertexArrays(1, &ds.vao_id) gl.BindVertexArray(ds.vao_id) gl.EnableVertexAttribArray(u32(rl.ShaderLocationIndex.VERTEX_POSITION)) gl.BindBuffer(gl.ARRAY_BUFFER, ds.vbo_ids[.Vertices]) gl.VertexAttribPointer( u32(rl.ShaderLocationIndex.VERTEX_POSITION), 3, gl.FLOAT, false, 0, 0, ) gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, ds.vbo_ids[.Indices]) } else { gl.BindVertexArray(ds.vao_id) } gl.BindVertexArray(0) defer gl.BindBuffer(gl.ARRAY_BUFFER, 0) gl.BufferData(gl.ARRAY_BUFFER, MAX_VERTICES * size_of(Vec3), nil, gl.STREAM_DRAW) gl.BufferSubData(gl.ARRAY_BUFFER, 0, ds.num_vertices * size_of(Vec3), raw_data(ds.vertices[:])) gl.VertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 0, 0) gl.EnableVertexAttribArray(0) defer gl.DisableVertexAttribArray(0) gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, ds.vbo_ids[.Indices]) defer gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0) gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, MAX_INDICES * size_of(u16), nil, gl.STREAM_DRAW) gl.BufferSubData( gl.ELEMENT_ARRAY_BUFFER, 0, ds.num_indices * size_of(u16), raw_data(ds.indices[:]), ) shader := rl.LoadMaterialDefault().shader rl.BeginShaderMode(shader) defer rl.EndShaderMode() for draw in ds.draws[:ds.num_draws] { color: [4]f32 = { f32(draw.color.r) / 255.0, f32(draw.color.g) / 255.0, f32(draw.color.b) / 255.0, f32(draw.color.a) / 255.0, } gl.Uniform4fv(shader.locs[rl.ShaderLocationIndex.COLOR_DIFFUSE], 1, raw_data(color[:])) gl.DrawElementsBaseVertex( gl.TRIANGLES, i32(ds.num_indices / 3), gl.UNSIGNED_SHORT, rawptr(uintptr(draw.first_index) * size_of(u16)), draw.base_vertex, ) } }