diff --git a/odin/sdl-opengl-rendering/src/main.odin b/odin/sdl-opengl-rendering/src/main.odin index 9eb33fb..b4a7fa6 100644 --- a/odin/sdl-opengl-rendering/src/main.odin +++ b/odin/sdl-opengl-rendering/src/main.odin @@ -9,13 +9,15 @@ import "vendor:OpenGL" import "vendor:sdl3" State :: struct { - tick: u32, - window: ^sdl3.Window, - program: u32, - rect_vao: VAO, - wireframe: bool, + tick: u32, + window: ^sdl3.Window, + program: u32, + rect_vao: VAO, + circle_vao: VAO, + wireframe: bool, } +// {{{ Initialization init :: proc() -> (state: State, ok: bool) { GL_MAJOR :: 3 GL_MINOR :: 3 @@ -93,6 +95,8 @@ init :: proc() -> (state: State, ok: bool) { OpenGL.load_up_to(GL_MAJOR, GL_MINOR, sdl3.gl_set_proc_address) OpenGL.ClearColor(0, 0, 0, 1) OpenGL.Enable(OpenGL.DEPTH_TEST) + OpenGL.Enable(OpenGL.BLEND) + OpenGL.BlendFunc(OpenGL.SRC_ALPHA, OpenGL.ONE_MINUS_SRC_ALPHA) state.program = OpenGL.load_shaders_source( #load("./vert.glsl"), @@ -101,11 +105,35 @@ init :: proc() -> (state: State, ok: bool) { state.rect_vao = create_vao({{-1, -1}, {1, -1}, {1, 1}, {-1, 1}}, {0, 1, 2, 3}) or_return + CIRCLE_POINTS :: 127 + circle_vertices: [CIRCLE_POINTS + 1]ℝ² + circle_indices: [CIRCLE_POINTS + 2]u32 + + for i in 0 ..< CIRCLE_POINTS { + θ := ℝ(i) * 2 * math.π / CIRCLE_POINTS + circle_vertices[i + 1] = {math.cos(θ), math.sin(θ)} + circle_indices[i + 1] = u32(i + 1) + } + + circle_indices[CIRCLE_POINTS + 1] = circle_indices[1] + + state.circle_vao = create_vao(circle_vertices[:], circle_indices[:]) or_return + init_command_queue() return state, true } +// }}} +// {{{ Close +close :: proc(state: State) { + OpenGL.DeleteProgram(state.program) + _ = sdl3.StopTextInput(state.window) + sdl3.DestroyWindow(state.window) + sdl3.Quit() +} +// }}} +// {{{ Render render :: proc(state: ^State) { state.tick += 1 OpenGL.Clear(OpenGL.COLOR_BUFFER_BIT | OpenGL.DEPTH_BUFFER_BIT) @@ -131,20 +159,22 @@ render :: proc(state: ^State) { 2 / ℝ(count) - 1 - draw_rect(Rect{pos, 1 / ℝ(count), 0, {(pos.x + 1) / 2, (pos.y + 1) / 2, 1, 1}}) + color := Color{(pos.x + 1) / 2, (pos.y + 1) / 2, 1, 1} + + if x > y { + draw_rect(pos, 1 / ℝ(count), color) + } else { + r := 1 / ℝ(count) / 2 + draw_circle(pos + r, r, color) + } } } + draw_circle({-0.25, -0.3}, 0.6, Color{0, 0, 0.5, 0.75}, z = -0.1) render_queue(state) } - -close :: proc(state: State) { - OpenGL.DeleteProgram(state.program) - _ = sdl3.StopTextInput(state.window) - sdl3.DestroyWindow(state.window) - sdl3.Quit() -} - +// }}} +// {{{ Main main :: proc() { log.Level_Headers = { 0 ..< 10 = "[DEBUG] ", @@ -186,3 +216,4 @@ main :: proc() { free_all(context.temp_allocator) } } +// }}} diff --git a/odin/sdl-opengl-rendering/src/shape.odin b/odin/sdl-opengl-rendering/src/shape.odin index 2ccc6d3..34932e7 100644 --- a/odin/sdl-opengl-rendering/src/shape.odin +++ b/odin/sdl-opengl-rendering/src/shape.odin @@ -12,25 +12,25 @@ Mat3 :: matrix[3, 3]ℝ Color :: [4]ℝ // }}} // {{{ Command queues +Shape :: struct { + z: ℝ, + fill: Color, +} Rect :: struct { - top_left: ℝ², - dimensions: ℝ², - z: ℝ, - fill: Color, + using shape: Shape, + top_left: ℝ², + dimensions: ℝ², } Circle :: struct { - center: ℝ², - radius: ℝ, -} - -Shape :: union { - Rect, - Circle, + using shape: Shape, + center: ℝ², + radius: ℝ, } Command_Queue :: struct { - rects: [dynamic]Rect, + rects: [dynamic]Rect, + circles: [dynamic]Circle, } queue: ^Command_Queue @@ -43,7 +43,9 @@ init_command_queue :: proc() { } draw_rect_args :: proc(top_left: ℝ², dimensions: ℝ², color: Color, z: ℝ = 0) { - draw_rect_struct(Rect{top_left, dimensions, z, color}) + draw_rect_struct( + Rect{top_left = top_left, dimensions = dimensions, shape = Shape{z = z, fill = color}}, + ) } draw_rect_struct :: proc(rect: Rect) { @@ -51,10 +53,24 @@ draw_rect_struct :: proc(rect: Rect) { } draw_rect :: proc { - draw_rect_args, draw_rect_struct, + draw_rect_args, } +draw_circle_args :: proc(center: ℝ², radius: ℝ, color: Color, z: ℝ = 0) { + draw_circle_struct( + Circle{center = center, radius = radius, shape = Shape{z = z, fill = color}}, + ) +} + +draw_circle_struct :: proc(circle: Circle) { + append(&queue.circles, circle) +} + +draw_circle :: proc { + draw_circle_struct, + draw_circle_args, +} // }}} // {{{ VAO & consts @@ -167,6 +183,36 @@ set_rect_transforms :: proc(vao: ^VAO, rects: []Rect) -> ℕ { return len(rects) } + +set_circle_transforms :: proc(vao: ^VAO, circles: []Circle) -> ℕ { + log.assert(len(circles) <= INSTANCES, "Attempting to send too many circles to the GPU") + matrices := new([INSTANCES]Mat3, context.temp_allocator) + fills := new([INSTANCES]Color, context.temp_allocator) + + for circle, i in circles { + mat: Mat3 + + mat[0, 0] = circle.radius + mat[1, 1] = circle.radius + mat[2].xy = circle.center.xy + mat[2].z = circle.z + + matrices[i] = mat + fills[i] = circle.fill + } + + OpenGL.BindBuffer(OpenGL.ARRAY_BUFFER, vao.instance_mat_buffer) + OpenGL.BufferData( + OpenGL.ARRAY_BUFFER, + INSTANCES * size_of(Mat3), + matrices, + OpenGL.DYNAMIC_DRAW, + ) + OpenGL.BindBuffer(OpenGL.ARRAY_BUFFER, vao.instance_fill_buffer) + OpenGL.BufferData(OpenGL.ARRAY_BUFFER, INSTANCES * size_of(Color), fills, OpenGL.DYNAMIC_DRAW) + + return len(circles) +} // }}} // {{{ Render the entire queue draw_instances :: proc(vao: VAO, instances: ℕ) { @@ -191,5 +237,15 @@ render_queue :: proc(state: ^State) { } clear(&queue.rects) + circle_steps := len(queue.circles) / INSTANCES + + for i := 0; i < len(queue.circles); i += INSTANCES { + slice := queue.circles[i:] + if len(slice) > INSTANCES {slice = slice[:INSTANCES]} + instances := set_circle_transforms(&state.circle_vao, slice) + draw_instances(state.circle_vao, instances) + } + + clear(&queue.circles) } // }}}