diff --git a/odin/sdl-opengl-rendering/flake.nix b/odin/sdl-opengl-rendering/flake.nix index e5eaa3a..b358b50 100644 --- a/odin/sdl-opengl-rendering/flake.nix +++ b/odin/sdl-opengl-rendering/flake.nix @@ -46,11 +46,19 @@ system: let pkgs = inputs.nixpkgs.legacyPackages.${system}.extend inputs.self.overlays.default; - raylib = pkgs.raylib.override { platform = "SDL"; }; + cross = import inputs.nixpkgs { + localSystem = system; + crossSystem.config = "x86_64-w64-mingw32"; + }; + inherit (pkgs) lib; in { - packages = { inherit (pkgs) odin; }; + packages = { + inherit (pkgs) odin; + gcc = cross.callPackage (import ./gcc.nix) { }; + libgl = cross.libGL; + }; # {{{ Shell devShell = pkgs.mkShell rec { @@ -65,18 +73,19 @@ pkgs.gdb # Debugger pkgs.seer # Debugger GUI pkgs.valgrind # Detect memory leaks + pkgs.renderdoc # Graphics debugger ]; buildInputs = [ - # pkgs.libGL - # pkgs.libxkbcommon - # pkgs.xorg.libXi - # pkgs.xorg.libX11 - # pkgs.xorg.libXrandr - # pkgs.xorg.libXinerama - # pkgs.xorg.libXcursor - # pkgs.wayland pkgs.sdl3 + + pkgs.xorg.libX11 + pkgs.xorg.libXScrnSaver + pkgs.xorg.libXcursor + pkgs.xorg.libXext + pkgs.xorg.libXfixes + pkgs.xorg.libXi + pkgs.xorg.libXrandr ]; LD_LIBRARY_PATH = with pkgs; lib.makeLibraryPath buildInputs; diff --git a/odin/sdl-opengl-rendering/src/main.odin b/odin/sdl-opengl-rendering/src/main.odin index 015f77b..9eb33fb 100644 --- a/odin/sdl-opengl-rendering/src/main.odin +++ b/odin/sdl-opengl-rendering/src/main.odin @@ -4,17 +4,16 @@ import "base:runtime" import "core:c" import "core:fmt" import "core:log" +import "core:math" import "vendor:OpenGL" import "vendor:sdl3" State :: struct { - window: ^sdl3.Window, - program: u32, - vertex_pos_location: u32, - vao: u32, - vbo: u32, - ibo: u32, - wireframe: bool, + tick: u32, + window: ^sdl3.Window, + program: u32, + rect_vao: VAO, + wireframe: bool, } init :: proc() -> (state: State, ok: bool) { @@ -67,6 +66,11 @@ init :: proc() -> (state: State, ok: bool) { ) // }}} + sdl3.SetAppMetadata( + "odin-rendering-experiments", + "<hash-here>", + "dev.moonythm.odin-rendering-experiments", + ) or_return sdl3.Init(sdl3.InitFlags{.VIDEO}) or_return sdl3.GL_SetAttribute(.CONTEXT_MAJOR_VERSION, GL_MAJOR) or_return sdl3.GL_SetAttribute(.CONTEXT_MINOR_VERSION, GL_MINOR) or_return @@ -87,60 +91,24 @@ init :: proc() -> (state: State, ok: bool) { (gl_ctx != nil) or_return OpenGL.load_up_to(GL_MAJOR, GL_MINOR, sdl3.gl_set_proc_address) + OpenGL.ClearColor(0, 0, 0, 1) + OpenGL.Enable(OpenGL.DEPTH_TEST) state.program = OpenGL.load_shaders_source( #load("./vert.glsl"), #load("./frag.glsl"), ) or_return - // log.debug("Doing Opengl stuff") - vertex_pos_location := OpenGL.GetAttribLocation(state.program, "aPos") - (vertex_pos_location != -1) or_return - state.vertex_pos_location = u32(vertex_pos_location) + state.rect_vao = create_vao({{-1, -1}, {1, -1}, {1, 1}, {-1, 1}}, {0, 1, 2, 3}) or_return - OpenGL.ClearColor(0, 0, 0, 1) - - // VBO data - vertex_data := 2 * [?]f32{-0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5} - - // IBO data - index_data := [?]u32{0, 1, 2, 3} - - OpenGL.GenVertexArrays(1, &state.vao) - OpenGL.BindVertexArray(state.vao) - - //Create VBO - OpenGL.GenBuffers(1, &state.vbo) - OpenGL.BindBuffer(OpenGL.ARRAY_BUFFER, state.vbo) - OpenGL.BufferData(OpenGL.ARRAY_BUFFER, 2 * 4 * size_of(f32), &vertex_data, OpenGL.STATIC_DRAW) - - OpenGL.EnableVertexAttribArray(state.vertex_pos_location) - OpenGL.VertexAttribPointer( - state.vertex_pos_location, - 2, - OpenGL.FLOAT, - false, - 2 * size_of(f32), - 0, - ) - - OpenGL.GenBuffers(1, auto_cast &state.ibo) - OpenGL.BindBuffer(OpenGL.ELEMENT_ARRAY_BUFFER, state.ibo) - OpenGL.BufferData( - OpenGL.ELEMENT_ARRAY_BUFFER, - 4 * size_of(u32), - &index_data, - OpenGL.STATIC_DRAW, - ) - - OpenGL.BindVertexArray(0) - OpenGL.DisableVertexAttribArray(state.vertex_pos_location) + init_command_queue() return state, true } -render :: proc(state: State) { - OpenGL.Clear(OpenGL.COLOR_BUFFER_BIT) +render :: proc(state: ^State) { + state.tick += 1 + OpenGL.Clear(OpenGL.COLOR_BUFFER_BIT | OpenGL.DEPTH_BUFFER_BIT) OpenGL.UseProgram(state.program) defer OpenGL.UseProgram(0) @@ -151,8 +119,23 @@ render :: proc(state: State) { OpenGL.PolygonMode(OpenGL.FRONT_AND_BACK, OpenGL.FILL) } - OpenGL.BindVertexArray(state.vao) - OpenGL.DrawElements(OpenGL.TRIANGLE_FAN, 4, OpenGL.UNSIGNED_INT, nil) + draw_rect({-0.5, 0}, {0.75, 0.5}, {1, 0, 0, 1}, z = 0.5) + draw_rect({0.5, 0.25}, {0.3, 0.5}, {0, 1, 0, 1}, z = -0.5) + + count := ℝ(32) + for x in ℝ(0) ..< count { + for y in ℝ(0) ..< count { + i := x * count + y + pos := + ℝ²{x + math.sin_f32(2 * math.π * (ℝ(state.tick) + i) / 60), y} * + 2 / + ℝ(count) - + 1 + draw_rect(Rect{pos, 1 / ℝ(count), 0, {(pos.x + 1) / 2, (pos.y + 1) / 2, 1, 1}}) + } + } + + render_queue(state) } close :: proc(state: State) { @@ -194,11 +177,12 @@ main :: proc() { case 'w': state.wireframe = !state.wireframe } - log.debug(event.text) } } - render(state) + render(&state) sdl3.GL_SwapWindow(state.window) + + free_all(context.temp_allocator) } } diff --git a/odin/sdl-opengl-rendering/src/shape.odin b/odin/sdl-opengl-rendering/src/shape.odin new file mode 100644 index 0000000..2ccc6d3 --- /dev/null +++ b/odin/sdl-opengl-rendering/src/shape.odin @@ -0,0 +1,195 @@ +package visuals + +import "core:log" +import "vendor:OpenGL" + +// {{{ Math constants +ℝ :: f32 +ℝ² :: [2]ℝ +ℝ³ :: [3]ℝ +ℕ :: uint +Mat3 :: matrix[3, 3]ℝ +Color :: [4]ℝ +// }}} +// {{{ Command queues +Rect :: struct { + top_left: ℝ², + dimensions: ℝ², + z: ℝ, + fill: Color, +} + +Circle :: struct { + center: ℝ², + radius: ℝ, +} + +Shape :: union { + Rect, + Circle, +} + +Command_Queue :: struct { + rects: [dynamic]Rect, +} + +queue: ^Command_Queue + +init_command_queue :: proc() { + queue = new(Command_Queue) + queue^ = Command_Queue { + rects = make([dynamic]Rect), + } +} + +draw_rect_args :: proc(top_left: ℝ², dimensions: ℝ², color: Color, z: ℝ = 0) { + draw_rect_struct(Rect{top_left, dimensions, z, color}) +} + +draw_rect_struct :: proc(rect: Rect) { + append(&queue.rects, rect) +} + +draw_rect :: proc { + draw_rect_args, + draw_rect_struct, +} + +// }}} + +// {{{ VAO & consts +VAO :: struct { + vao: u32, + ibo: u32, + vertex_pos_buffer: u32, + instance_fill_buffer: u32, + instance_mat_buffer: u32, + index_count: ℕ, +} + +INSTANCES :: 1024 // The number of instances to allocate space in the buffer for +VERTEX_POS_LOCATION :: 0 +INSTANCE_FILL_LOCATION :: 1 +INSTANCE_MAT_LOCATION :: 2 +// }}} +// {{{ Create VAO +create_vao :: proc(vertices: []ℝ², indices: []u32) -> (out: VAO, ok: bool) { + out.index_count = len(indices) + + // Create VAO + OpenGL.GenVertexArrays(1, &out.vao) + OpenGL.BindVertexArray(out.vao) + + // Create instance fill VBO + OpenGL.GenBuffers(1, auto_cast &out.instance_fill_buffer) + OpenGL.BindBuffer(OpenGL.ARRAY_BUFFER, out.instance_fill_buffer) + + OpenGL.EnableVertexAttribArray(INSTANCE_FILL_LOCATION) + OpenGL.VertexAttribPointer(INSTANCE_FILL_LOCATION, 4, OpenGL.FLOAT, false, size_of(Color), 0) + OpenGL.VertexAttribDivisor(INSTANCE_FILL_LOCATION, 1) + + // Create instance mat VBO + OpenGL.GenBuffers(1, auto_cast &out.instance_mat_buffer) + OpenGL.BindBuffer(OpenGL.ARRAY_BUFFER, out.instance_mat_buffer) + + for i in u32(0) ..< 3 { + OpenGL.EnableVertexAttribArray(INSTANCE_MAT_LOCATION + i) + vec3_size :: size_of(ℝ³) + OpenGL.VertexAttribPointer( + INSTANCE_MAT_LOCATION + i, + 3, + OpenGL.FLOAT, + false, + 3 * vec3_size, + uintptr(i * vec3_size), + ) + OpenGL.VertexAttribDivisor(INSTANCE_MAT_LOCATION + i, 1) + } + + //Create position VBO + OpenGL.GenBuffers(1, &out.vertex_pos_buffer) + OpenGL.BindBuffer(OpenGL.ARRAY_BUFFER, out.vertex_pos_buffer) + OpenGL.BufferData( + OpenGL.ARRAY_BUFFER, + len(vertices) * 2 * size_of(ℝ), + raw_data(vertices), + OpenGL.STATIC_DRAW, + ) + + // Put the data into the VBO + OpenGL.EnableVertexAttribArray(VERTEX_POS_LOCATION) + OpenGL.VertexAttribPointer(VERTEX_POS_LOCATION, 2, OpenGL.FLOAT, false, 2 * size_of(ℝ), 0) + + // Create the IBO + OpenGL.GenBuffers(1, auto_cast &out.ibo) + OpenGL.BindBuffer(OpenGL.ELEMENT_ARRAY_BUFFER, out.ibo) + OpenGL.BufferData( + OpenGL.ELEMENT_ARRAY_BUFFER, + len(indices) * size_of(u32), + raw_data(indices), + OpenGL.STATIC_DRAW, + ) + + OpenGL.BindVertexArray(0) + + return out, true +} +// }}} +// {{{ Set rect transforms +set_rect_transforms :: proc(vao: ^VAO, rects: []Rect) -> ℕ { + log.assert(len(rects) <= INSTANCES, "Attempting to send too many rects to the GPU") + matrices := new([INSTANCES]Mat3, context.temp_allocator) + fills := new([INSTANCES]Color, context.temp_allocator) + + for rect, i in rects { + // This matrix must transform the rect [-1, 1]² into the desired rect + mat: Mat3 + + center := rect.top_left + rect.dimensions / 2 + mat[0, 0] = rect.dimensions.x / 2 + mat[1, 1] = rect.dimensions.y / 2 + mat[2].xy = center.xy + mat[2].z = rect.z + + matrices[i] = mat + fills[i] = rect.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(rects) +} +// }}} +// {{{ Render the entire queue +draw_instances :: proc(vao: VAO, instances: ℕ) { + OpenGL.BindVertexArray(vao.vao) + OpenGL.DrawElementsInstanced( + OpenGL.TRIANGLE_FAN, + i32(vao.index_count), + OpenGL.UNSIGNED_INT, + nil, + i32(instances), + ) +} + +render_queue :: proc(state: ^State) { + rect_steps := len(queue.rects) / INSTANCES + + for i := 0; i < len(queue.rects); i += INSTANCES { + slice := queue.rects[i:] + if len(slice) > INSTANCES {slice = slice[:INSTANCES]} + instances := set_rect_transforms(&state.rect_vao, slice) + draw_instances(state.rect_vao, instances) + } + + clear(&queue.rects) +} +// }}} diff --git a/odin/sdl-opengl-rendering/src/vert.glsl b/odin/sdl-opengl-rendering/src/vert.glsl index 3273106..a5761e3 100644 --- a/odin/sdl-opengl-rendering/src/vert.glsl +++ b/odin/sdl-opengl-rendering/src/vert.glsl @@ -1,10 +1,13 @@ #version 330 -in vec2 aPos; +layout (location = 0) in vec2 aPos; +layout (location = 1) in vec4 instanceFill; +layout (location = 2) in mat3 instanceMatrix; out vec4 vertexColor; void main() { - gl_Position = vec4(aPos.x, aPos.y, 0, 1); - vertexColor = vec4((aPos.x + 1) / 2, (aPos.y + 1) / 2, 1, 1); + vec3 pos = instanceMatrix * vec3(aPos.xy, 1); + gl_Position = vec4(pos.xyz, 1); + vertexColor = instanceFill; }