odin(sdl-opengl-rendering): tinker with instancing
This commit is contained in:
parent
f98d2a6baa
commit
b3aad17877
odin/sdl-opengl-rendering
|
@ -46,11 +46,19 @@
|
||||||
system:
|
system:
|
||||||
let
|
let
|
||||||
pkgs = inputs.nixpkgs.legacyPackages.${system}.extend inputs.self.overlays.default;
|
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;
|
inherit (pkgs) lib;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
packages = { inherit (pkgs) odin; };
|
packages = {
|
||||||
|
inherit (pkgs) odin;
|
||||||
|
gcc = cross.callPackage (import ./gcc.nix) { };
|
||||||
|
libgl = cross.libGL;
|
||||||
|
};
|
||||||
|
|
||||||
# {{{ Shell
|
# {{{ Shell
|
||||||
devShell = pkgs.mkShell rec {
|
devShell = pkgs.mkShell rec {
|
||||||
|
@ -65,18 +73,19 @@
|
||||||
pkgs.gdb # Debugger
|
pkgs.gdb # Debugger
|
||||||
pkgs.seer # Debugger GUI
|
pkgs.seer # Debugger GUI
|
||||||
pkgs.valgrind # Detect memory leaks
|
pkgs.valgrind # Detect memory leaks
|
||||||
|
pkgs.renderdoc # Graphics debugger
|
||||||
];
|
];
|
||||||
|
|
||||||
buildInputs = [
|
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.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;
|
LD_LIBRARY_PATH = with pkgs; lib.makeLibraryPath buildInputs;
|
||||||
|
|
|
@ -4,17 +4,16 @@ import "base:runtime"
|
||||||
import "core:c"
|
import "core:c"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:log"
|
import "core:log"
|
||||||
|
import "core:math"
|
||||||
import "vendor:OpenGL"
|
import "vendor:OpenGL"
|
||||||
import "vendor:sdl3"
|
import "vendor:sdl3"
|
||||||
|
|
||||||
State :: struct {
|
State :: struct {
|
||||||
window: ^sdl3.Window,
|
tick: u32,
|
||||||
program: u32,
|
window: ^sdl3.Window,
|
||||||
vertex_pos_location: u32,
|
program: u32,
|
||||||
vao: u32,
|
rect_vao: VAO,
|
||||||
vbo: u32,
|
wireframe: bool,
|
||||||
ibo: u32,
|
|
||||||
wireframe: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init :: proc() -> (state: State, ok: 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.Init(sdl3.InitFlags{.VIDEO}) or_return
|
||||||
sdl3.GL_SetAttribute(.CONTEXT_MAJOR_VERSION, GL_MAJOR) or_return
|
sdl3.GL_SetAttribute(.CONTEXT_MAJOR_VERSION, GL_MAJOR) or_return
|
||||||
sdl3.GL_SetAttribute(.CONTEXT_MINOR_VERSION, GL_MINOR) 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
|
(gl_ctx != nil) or_return
|
||||||
|
|
||||||
OpenGL.load_up_to(GL_MAJOR, GL_MINOR, sdl3.gl_set_proc_address)
|
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(
|
state.program = OpenGL.load_shaders_source(
|
||||||
#load("./vert.glsl"),
|
#load("./vert.glsl"),
|
||||||
#load("./frag.glsl"),
|
#load("./frag.glsl"),
|
||||||
) or_return
|
) or_return
|
||||||
// log.debug("Doing Opengl stuff")
|
|
||||||
|
|
||||||
vertex_pos_location := OpenGL.GetAttribLocation(state.program, "aPos")
|
state.rect_vao = create_vao({{-1, -1}, {1, -1}, {1, 1}, {-1, 1}}, {0, 1, 2, 3}) or_return
|
||||||
(vertex_pos_location != -1) or_return
|
|
||||||
state.vertex_pos_location = u32(vertex_pos_location)
|
|
||||||
|
|
||||||
OpenGL.ClearColor(0, 0, 0, 1)
|
init_command_queue()
|
||||||
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
return state, true
|
return state, true
|
||||||
}
|
}
|
||||||
|
|
||||||
render :: proc(state: State) {
|
render :: proc(state: ^State) {
|
||||||
OpenGL.Clear(OpenGL.COLOR_BUFFER_BIT)
|
state.tick += 1
|
||||||
|
OpenGL.Clear(OpenGL.COLOR_BUFFER_BIT | OpenGL.DEPTH_BUFFER_BIT)
|
||||||
|
|
||||||
OpenGL.UseProgram(state.program)
|
OpenGL.UseProgram(state.program)
|
||||||
defer OpenGL.UseProgram(0)
|
defer OpenGL.UseProgram(0)
|
||||||
|
@ -151,8 +119,23 @@ render :: proc(state: State) {
|
||||||
OpenGL.PolygonMode(OpenGL.FRONT_AND_BACK, OpenGL.FILL)
|
OpenGL.PolygonMode(OpenGL.FRONT_AND_BACK, OpenGL.FILL)
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenGL.BindVertexArray(state.vao)
|
draw_rect({-0.5, 0}, {0.75, 0.5}, {1, 0, 0, 1}, z = 0.5)
|
||||||
OpenGL.DrawElements(OpenGL.TRIANGLE_FAN, 4, OpenGL.UNSIGNED_INT, nil)
|
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) {
|
close :: proc(state: State) {
|
||||||
|
@ -194,11 +177,12 @@ main :: proc() {
|
||||||
case 'w':
|
case 'w':
|
||||||
state.wireframe = !state.wireframe
|
state.wireframe = !state.wireframe
|
||||||
}
|
}
|
||||||
log.debug(event.text)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render(state)
|
render(&state)
|
||||||
sdl3.GL_SwapWindow(state.window)
|
sdl3.GL_SwapWindow(state.window)
|
||||||
|
|
||||||
|
free_all(context.temp_allocator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
195
odin/sdl-opengl-rendering/src/shape.odin
Normal file
195
odin/sdl-opengl-rendering/src/shape.odin
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
// }}}
|
|
@ -1,10 +1,13 @@
|
||||||
#version 330
|
#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;
|
out vec4 vertexColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = vec4(aPos.x, aPos.y, 0, 1);
|
vec3 pos = instanceMatrix * vec3(aPos.xy, 1);
|
||||||
vertexColor = vec4((aPos.x + 1) / 2, (aPos.y + 1) / 2, 1, 1);
|
gl_Position = vec4(pos.xyz, 1);
|
||||||
|
vertexColor = instanceFill;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue