1
Fork 0

odin(sdl-opengl-rendering): set up some gl abstractions

This commit is contained in:
prescientmoon 2025-06-03 21:20:03 +02:00
parent 45c6452532
commit cd1e066dcd
Signed by: prescientmoon
SSH key fingerprint: SHA256:WFp/cO76nbarETAoQcQXuV+0h7XJsEsOCI0UsyPIy6U
8 changed files with 560 additions and 225 deletions

View file

@ -44,11 +44,11 @@
]
},
"locked": {
"lastModified": 1747681085,
"narHash": "sha256-tG177/u/vDUnxXeIFjJWYTRSuvYRfSCDYv3tjgdf+Ec=",
"lastModified": 1748975148,
"narHash": "sha256-Gxp3ycjTnGSlkb/MpMAAbRY7bjzVjXaeaO+TBtx++/E=",
"owner": "starlitcanopy",
"repo": "odin",
"rev": "612433442cc03297474a31d3d40fce74ce3f5331",
"rev": "d2cc7127df2555ed57d71575a235bd1a3eebd26a",
"type": "github"
},
"original": {
@ -70,11 +70,11 @@
]
},
"locked": {
"lastModified": 1748918175,
"narHash": "sha256-fv5w3riPVpAhMArOVR2O6jXekdiPV7KhvFLD6fE8q60=",
"lastModified": 1748975728,
"narHash": "sha256-7/AZrVRz8bLOToQjlvz36m4yPb1ZXDm3hhM7HJ8VbcY=",
"owner": "starlitcanopy",
"repo": "ols",
"rev": "6afccf760e9f519e12ed4b155550c8896adef61b",
"rev": "f0ae178c1e600d04e2fd16dc810e41427b290c69",
"type": "github"
},
"original": {

View file

@ -46,7 +46,6 @@
buildInputs = [
pkgs.sdl3
pkgs.xorg.libX11
pkgs.xorg.libXScrnSaver
pkgs.xorg.libXcursor

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1,120 @@
package visuals
import "base:intrinsics"
import "base:runtime"
import "core:reflect"
import "vendor:OpenGL"
GL_Buffer_Usage :: enum u32 {
Static = OpenGL.STATIC_DRAW,
Dynamic = OpenGL.DYNAMIC_DRAW,
}
GL_Buffer_Kind :: enum u32 {
Array = OpenGL.ARRAY_BUFFER,
Element_Array = OpenGL.ELEMENT_ARRAY_BUFFER,
Uniform = OpenGL.UNIFORM_BUFFER,
}
// {{{ Set array buffer
@(private)
gl_sizeof_ptr :: proc(ptr: ^$T) -> int {
return size_of(T)
}
@(private)
gl_sizeof_slice :: proc(slice: []$T) -> int {
return len(slice) * size_of(T)
}
@(private)
gl_sizeof :: proc {
gl_sizeof_ptr,
gl_sizeof_slice,
}
@(private)
gl_to_rawptr_ptr :: proc(ptr: ^$T) -> rawptr {
return ptr
}
@(private)
gl_to_rawptr_slice :: proc(slice: []$T) -> rawptr {
return raw_data(slice)
}
@(private)
gl_to_rawptr :: proc {
gl_to_rawptr_ptr,
gl_to_rawptr_slice,
}
bind_buffer :: proc(id: u32, buffer: GL_Buffer_Kind = .Array) {
OpenGL.BindBuffer(u32(buffer), id)
}
set_buffer :: proc(
id: u32,
data: $T,
buffer: GL_Buffer_Kind = .Array,
usage: GL_Buffer_Usage = .Dynamic,
) {
bind_buffer(id, buffer)
OpenGL.BufferData(u32(buffer), gl_sizeof(data), gl_to_rawptr(data), u32(usage))
}
gen_buffer :: proc() -> (out: u32) {
OpenGL.GenBuffers(1, &out)
return out
}
// }}}
// {{{ Drawing commands
clear_screen :: proc() {
OpenGL.Clear(OpenGL.COLOR_BUFFER_BIT | OpenGL.DEPTH_BUFFER_BIT)
}
// }}}
GL_Base_Type :: enum {
Float,
}
setup_vbo :: proc(
id: u32,
location: u32,
ty: GL_Base_Type = .Float,
rows: i32 = 1,
cols: i32 = 1,
divisor := 0,
) {
bind_buffer(id, .Array)
elem_size: i32
base: u32
switch ty {
case .Float:
elem_size = i32(size_of(f32))
base = OpenGL.FLOAT
}
offset: i32 = 0
for i in u32(0) ..< u32(cols) {
OpenGL.EnableVertexAttribArray(location + i)
OpenGL.VertexAttribPointer(
location + i,
rows,
OpenGL.FLOAT,
false,
i32(cols * rows) * elem_size,
uintptr(offset),
)
offset += rows * elem_size
if divisor != 0 {
OpenGL.VertexAttribDivisor(location + i, 1)
}
}
}

View file

@ -170,7 +170,6 @@ on_resize :: proc(state: ^State, width, height: i32) {
}
// }}}
// {{{ Render
render :: proc(state: ^State) {
state.tick += 1

View file

@ -12,10 +12,16 @@ layout(std140, binding = 0) uniform Globals {
float aaWidth; // Anti aliasing width
};
// struct Line {
// vec2 from;
// vec2 to;
// };
//
// layout (location = 5) in Line instanceLine;
void main() {
vec4 pos = viewportMatrix * vec4(instanceMatrix * vec3(aPos.xy, 1), 1);
// vec3 pos = vec3(aPos.xy, 1);
vec4 pos = viewportMatrix * vec4(instanceMatrix * vec3(aPos, 1), 1);
gl_Position = vec4(pos.xyz, 1);
vertexColor = instanceFill;
vertexPos = aPos.xy;
vertexPos = aPos;
}

View file

@ -4,7 +4,8 @@ import "core:log"
import "core:math/linalg"
import "vendor:OpenGL"
// {{{ Command queues
// {{{ Shape Types
Shape :: struct {
z: ,
fill: Color,
@ -29,11 +30,14 @@ Line :: struct {
thickness: ,
}
Rounded_Line :: distinct Line
// }}}
// {{{ Command queues
Command_Queue :: struct {
rects: [dynamic]Rect,
circles: [dynamic]Circle,
lines: [dynamic]Line,
rounded_lines: [dynamic]Line,
rounded_lines: [dynamic]Rounded_Line,
}
queue: ^Command_Queue
@ -44,7 +48,7 @@ init_command_queue :: proc() {
rects = make([dynamic]Rect),
circles = make([dynamic]Circle),
lines = make([dynamic]Line),
rounded_lines = make([dynamic]Line),
rounded_lines = make([dynamic]Rounded_Line),
}
}
@ -99,8 +103,8 @@ draw_rounded_line_args :: proc(from, to: ℝ², thickness: , color: Color, z:
)
}
draw_rounded_line_struct :: proc(line: Line) {
append(&queue.rounded_lines, line)
draw_rounded_line_struct :: proc(line: $T/Line) {
append(&queue.rounded_lines, Rounded_Line(line))
}
draw_rounded_line :: proc {
@ -108,11 +112,57 @@ draw_rounded_line :: proc {
draw_rounded_line_args,
}
// }}}
// {{{ Shape -> Transform
to_transform_rect :: proc(rect: 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, 2] = 1
mat[2].xy = center.xy
return mat
}
to_transform_circle :: proc(circle: Circle) -> (mat: Mat3) {
mat[0, 0] = circle.radius
mat[1, 1] = circle.radius
mat[2, 2] = 1
mat[2].xy = circle.center.xy
return mat
}
to_transform_line :: proc(line: Line) -> (mat: Mat3) {
dir := line.to - line.from
mat[0].xy = dir / 2
mat[1].xy = vec2_perp(linalg.normalize0(dir)) * line.thickness
mat[2].xy = (line.from + line.to) / 2
mat[2].z = line.z
return mat
}
to_transform_rounded_line :: proc(line: Rounded_Line) -> (mat: Mat3) {
dir := line.to - line.from
mat[0].xy = dir / 2
mat[1].xy = vec2_perp(linalg.normalize0(dir)) * line.thickness
mat[2].xy = (line.from + line.to) / 2
mat[2].z = line.z
mat[0, 0] /= 2
mat[0, 1] /= 2
return mat
}
// This matrix must transform [-1, 1]² into the desired rect
to_transform :: proc {
to_transform_rect,
to_transform_circle,
to_transform_line,
to_transform_rounded_line,
}
// }}}
// {{{ GPU data types
VAO :: struct {
vao: u32,
ibo: u32,
vertex_ind_buffer: u32,
vertex_pos_buffer: u32,
instance_fill_buffer: u32,
instance_mat_buffer: u32,
@ -139,17 +189,13 @@ INSTANCE_MAT_LOCATION :: 2
GLOBALS_UBO_BINDING :: 0
// }}}
// {{{ Create globals UBO
create_globals_ubo :: proc() -> UBO {
id := gen_buffer()
create_globals_ubo :: proc() -> (out: UBO) {
OpenGL.GenBuffers(1, &out)
OpenGL.BindBuffer(OpenGL.UNIFORM_BUFFER, out)
defer OpenGL.BindBuffer(OpenGL.UNIFORM_BUFFER, 0)
set_buffer(id, &Global_Uniforms{}, buffer = .Uniform)
OpenGL.BindBufferBase(OpenGL.UNIFORM_BUFFER, GLOBALS_UBO_BINDING, id)
// Allocate buffer data
OpenGL.BufferData(OpenGL.UNIFORM_BUFFER, size_of(Global_Uniforms), {}, OpenGL.STATIC_DRAW)
OpenGL.BindBufferBase(OpenGL.UNIFORM_BUFFER, GLOBALS_UBO_BINDING, out)
return out
return id
}
// }}}
// {{{ Create VAO
@ -159,234 +205,86 @@ create_vao :: proc(vertices: []ℝ², indices: []u32) -> (out: VAO, ok: bool) {
// Create VAO
OpenGL.GenVertexArrays(1, &out.vao)
OpenGL.BindVertexArray(out.vao)
defer OpenGL.BindVertexArray(0)
// 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)
out.instance_fill_buffer = gen_buffer()
bind_buffer(out.instance_fill_buffer, .Array)
setup_vbo(out.instance_fill_buffer, INSTANCE_FILL_LOCATION, rows = 4, divisor = 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,
out.instance_mat_buffer = gen_buffer()
setup_vbo(
out.instance_mat_buffer,
INSTANCE_MAT_LOCATION,
ty = .Float,
rows = 3,
cols = 3,
divisor = 1,
)
// 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 position VBO
out.vertex_pos_buffer = gen_buffer()
set_buffer(out.vertex_pos_buffer, vertices, usage = .Static)
setup_vbo(out.vertex_pos_buffer, VERTEX_POS_LOCATION, ty = .Float, rows = 2)
// 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)
// Create the vertex_ind_buffer
out.vertex_ind_buffer = gen_buffer()
set_buffer(out.vertex_ind_buffer, indices, buffer = .Element_Array, usage = .Static)
return out, true
}
// }}}
// {{{ Set transforms
// Commit the contents of `state.buf_matrices` and `state.buf_colors` to the
// GPU.
commit_buffers :: proc(state: ^State, vao: ^VAO) {
OpenGL.BindBuffer(OpenGL.ARRAY_BUFFER, vao.instance_mat_buffer)
OpenGL.BufferData(
OpenGL.ARRAY_BUFFER,
INSTANCES * size_of(Mat3),
&state.buf_matrices,
OpenGL.DYNAMIC_DRAW,
)
OpenGL.BindBuffer(OpenGL.ARRAY_BUFFER, vao.instance_fill_buffer)
OpenGL.BufferData(
OpenGL.ARRAY_BUFFER,
INSTANCES * size_of(Color),
&state.buf_colors,
OpenGL.DYNAMIC_DRAW,
)
}
set_rect_transforms :: proc(state: ^State, vao: ^VAO, rects: []Rect) -> {
log.assert(len(rects) <= INSTANCES, "Attempting to send too many rects to the GPU")
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
state.buf_matrices[i] = mat
state.buf_colors[i] = rect.fill
}
return len(rects)
}
set_circle_transforms :: proc(state: ^State, vao: ^VAO, circles: []Circle) -> {
log.assert(len(circles) <= INSTANCES, "Attempting to send too many circles to the GPU")
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
state.buf_matrices[i] = mat
state.buf_colors[i] = circle.fill
}
return len(circles)
}
set_line_transforms :: proc(state: ^State, vao: ^VAO, lines: []Line) -> {
log.assert(len(lines) <= INSTANCES, "Attempting to send too many lines to the GPU")
for line, i in lines {
mat: Mat3
dir := line.to - line.from
mat[0].xy = dir / 2
mat[1].xy = vec2_perp(linalg.normalize0(dir)) * line.thickness
mat[2].xy = (line.from + line.to) / 2
mat[2].z = line.z
state.buf_matrices[i] = mat
state.buf_colors[i] = line.fill
}
return len(lines)
}
set_rounded_line_transforms :: proc(state: ^State, vao: ^VAO, lines: []Line) -> {
log.assert(len(lines) <= INSTANCES, "Attempting to send too many lines to the GPU")
for line, i in lines {
mat: Mat3
dir := line.to - line.from
mat[0].xy = dir / 2
mat[1].xy = vec2_perp(linalg.normalize0(dir)) * line.thickness
mat[2].xy = (line.from + line.to) / 2
mat[2].z = line.z
mat[0, 0] /= 2
mat[0, 1] /= 2
state.buf_matrices[i] = mat
state.buf_colors[i] = line.fill
}
return len(lines)
}
// }}}
// {{{ Render the entire queue
draw_instances :: proc(vao: VAO, instances: ) {
render_instanced :: proc(state: ^State, vao: VAO, shapes: ^[dynamic]$T) {
OpenGL.BindVertexArray(vao.vao)
steps := len(shapes) / INSTANCES
for i := 0; i < len(shapes); i += INSTANCES {
slice := shapes[i:]
if len(slice) > INSTANCES {slice = slice[:INSTANCES]}
for shape, i in shapes {
state.buf_matrices[i] = to_transform(shape)
state.buf_matrices[i][2, 2] = shape.z
state.buf_colors[i] = shape.fill
}
set_buffer(vao.instance_mat_buffer, &state.buf_matrices)
set_buffer(vao.instance_fill_buffer, &state.buf_colors)
OpenGL.DrawElementsInstanced(
OpenGL.TRIANGLE_FAN,
i32(vao.index_count),
OpenGL.UNSIGNED_INT,
nil,
i32(instances),
i32(len(shapes)),
)
}
}
clear_screen :: proc() {
OpenGL.Clear(OpenGL.COLOR_BUFFER_BIT | OpenGL.DEPTH_BUFFER_BIT)
clear(shapes)
}
render_queue :: proc(state: ^State) {
// Update uniform data
OpenGL.BindBuffer(OpenGL.UNIFORM_BUFFER, state.globals_ubo)
OpenGL.BufferData(
OpenGL.UNIFORM_BUFFER,
size_of(Global_Uniforms),
&state.globals,
OpenGL.DYNAMIC_DRAW,
)
OpenGL.BindBuffer(OpenGL.UNIFORM_BUFFER, 0)
set_buffer(state.globals_ubo, &state.globals, buffer = .Uniform)
// Toggle the wireframe
if state.wireframe {
OpenGL.PolygonMode(OpenGL.FRONT_AND_BACK, OpenGL.LINE)
} else {
OpenGL.PolygonMode(OpenGL.FRONT_AND_BACK, OpenGL.FILL)
}
OpenGL.PolygonMode(OpenGL.FRONT_AND_BACK, state.wireframe ? OpenGL.LINE : OpenGL.FILL)
OpenGL.UseProgram(state.rect_program)
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, &state.rect_vao, slice)
commit_buffers(state, &state.rect_vao)
draw_instances(state.rect_vao, instances)
}
clear(&queue.rects)
render_instanced(state, state.rect_vao, &queue.rects)
OpenGL.UseProgram(state.circle_program)
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, &state.rect_vao, slice)
commit_buffers(state, &state.rect_vao)
draw_instances(state.rect_vao, instances)
}
clear(&queue.circles)
render_instanced(state, state.rect_vao, &queue.circles)
OpenGL.UseProgram(state.line_program)
for i := 0; i < len(queue.lines); i += INSTANCES {
slice := queue.lines[i:]
if len(slice) > INSTANCES {slice = slice[:INSTANCES]}
instances := set_line_transforms(state, &state.rect_vao, slice)
commit_buffers(state, &state.rect_vao)
draw_instances(state.rect_vao, instances)
}
clear(&queue.lines)
render_instanced(state, state.rect_vao, &queue.lines)
OpenGL.UseProgram(state.rounded_line_program)
for i := 0; i < len(queue.rounded_lines); i += INSTANCES {
slice := queue.rounded_lines[i:]
if len(slice) > INSTANCES {slice = slice[:INSTANCES]}
instances := set_rounded_line_transforms(state, &state.rect_vao, slice)
commit_buffers(state, &state.rect_vao)
draw_instances(state.rect_vao, instances)
}
clear(&queue.rounded_lines)
render_instanced(state, state.rect_vao, &queue.rounded_lines)
OpenGL.UseProgram(0)
}

View file

@ -0,0 +1,312 @@
package visuals
import "core:log"
import "core:math/linalg"
import "vendor:OpenGL"
// {{{ Shape Types
Shape :: struct {
z: ,
fill: Color,
}
Rect :: struct {
using shape: Shape,
top_left: ²,
dimensions: ²,
}
Circle :: struct {
using shape: Shape,
center: ²,
radius: ,
}
Line :: struct {
using shape: Shape,
from: ²,
to: ²,
thickness: ,
}
Rounded_Line :: distinct Line
// }}}
// {{{ Command queues
Command_Queue :: struct {
rects: [dynamic]Rect,
circles: [dynamic]Circle,
lines: [dynamic]Line,
rounded_lines: [dynamic]Rounded_Line,
}
queue: ^Command_Queue
init_command_queue :: proc() {
queue = new(Command_Queue)
queue^ = Command_Queue {
rects = make([dynamic]Rect),
circles = make([dynamic]Circle),
lines = make([dynamic]Line),
rounded_lines = make([dynamic]Rounded_Line),
}
}
draw_rect_args :: proc(top_left: ², dimensions: ², color: Color, z: = 0) {
draw_rect_struct(
Rect{top_left = top_left, dimensions = dimensions, shape = Shape{z = z, fill = color}},
)
}
draw_rect_struct :: proc(rect: Rect) {
append(&queue.rects, rect)
}
draw_rect :: proc {
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,
}
draw_line_args :: proc(from, to: ², thickness: , color: Color, z: = 0) {
draw_line_struct(
Line{from = from, to = to, thickness = thickness, shape = Shape{z = z, fill = color}},
)
}
draw_line_struct :: proc(line: Line) {
append(&queue.lines, line)
}
draw_line :: proc {
draw_line_struct,
draw_line_args,
}
draw_rounded_line_args :: proc(from, to: ², thickness: , color: Color, z: = 0) {
draw_rounded_line_struct(
Line{from = from, to = to, thickness = thickness, shape = Shape{z = z, fill = color}},
)
}
draw_rounded_line_struct :: proc(line: $T/Line) {
append(&queue.rounded_lines, Rounded_Line(line))
}
draw_rounded_line :: proc {
draw_rounded_line_struct,
draw_rounded_line_args,
}
// }}}
// {{{ Shape -> Transform
to_transform_rect :: proc(rect: 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, 2] = 1
mat[2].xy = center.xy
return mat
}
to_transform_circle :: proc(circle: Circle) -> (mat: Mat3) {
mat[0, 0] = circle.radius
mat[1, 1] = circle.radius
mat[2, 2] = 1
mat[2].xy = circle.center.xy
return mat
}
to_transform_line :: proc(line: Line) -> (mat: Mat3) {
dir := line.to - line.from
mat[0].xy = dir / 2
mat[1].xy = vec2_perp(linalg.normalize0(dir)) * line.thickness
mat[2].xy = (line.from + line.to) / 2
mat[2].z = line.z
return mat
}
to_transform_rounded_line :: proc(line: Rounded_Line) -> (mat: Mat3) {
dir := line.to - line.from
mat[0].xy = dir / 2
mat[1].xy = vec2_perp(linalg.normalize0(dir)) * line.thickness
mat[2].xy = (line.from + line.to) / 2
mat[2].z = line.z
mat[0, 0] /= 2
mat[0, 1] /= 2
return mat
}
// This matrix must transform [-1, 1]² into the desired rect
to_transform :: proc {
to_transform_rect,
to_transform_circle,
to_transform_line,
to_transform_rounded_line,
}
// }}}
// {{{ GPU data types
VAO :: struct {
vao: u32,
vertex_ind_buffer: u32,
vertex_pos_buffer: u32,
instance_fill_buffer: u32,
instance_mat_buffer: u32,
index_count: ,
}
// odinfmt: disable
RMat3 :: matrix[4, 4]f32
// odinfmt: enable
UBO :: u32
Global_Uniforms :: struct {
viewport_matrix: RMat3,
aaWidth: f32,
}
Program :: u32
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
GLOBALS_UBO_BINDING :: 0
// }}}
// {{{ Create globals UBO
create_globals_ubo :: proc() -> UBO {
id := gen_buffer()
set_buffer(id, &Global_Uniforms{}, buffer = .Uniform)
OpenGL.BindBufferBase(OpenGL.UNIFORM_BUFFER, GLOBALS_UBO_BINDING, id)
return id
}
// }}}
// {{{ 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)
defer OpenGL.BindVertexArray(0)
// Create instance fill VBO
out.instance_fill_buffer = gen_buffer()
bind_buffer(out.instance_fill_buffer, .Array)
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
out.instance_mat_buffer = gen_buffer()
bind_buffer(out.instance_mat_buffer, .Array)
setup_vbo(
out.instance_mat_buffer,
INSTANCE_MAT_LOCATION,
ty = .Float,
rows = 3,
cols = 3,
divisor = 1,
)
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
out.vertex_pos_buffer = gen_buffer()
set_buffer(out.vertex_pos_buffer, vertices, usage = .Static)
OpenGL.EnableVertexAttribArray(VERTEX_POS_LOCATION)
OpenGL.VertexAttribPointer(VERTEX_POS_LOCATION, 2, OpenGL.FLOAT, false, 2 * size_of(), 0)
// Create the vertex_ind_buffer
out.vertex_ind_buffer = gen_buffer()
set_buffer(out.vertex_ind_buffer, indices, buffer = .Element_Array, usage = .Static)
return out, true
}
// }}}
// {{{ Render the entire queue
render_instanced :: proc(state: ^State, vao: VAO, shapes: ^[dynamic]$T) {
OpenGL.BindVertexArray(vao.vao)
steps := len(shapes) / INSTANCES
for i := 0; i < len(shapes); i += INSTANCES {
slice := shapes[i:]
if len(slice) > INSTANCES {slice = slice[:INSTANCES]}
for shape, i in shapes {
state.buf_matrices[i] = to_transform(shape)
state.buf_matrices[i][2, 2] = shape.z
state.buf_colors[i] = shape.fill
}
set_buffer(vao.instance_mat_buffer, &state.buf_matrices)
set_buffer(vao.instance_fill_buffer, &state.buf_colors)
OpenGL.DrawElementsInstanced(
OpenGL.TRIANGLE_FAN,
i32(vao.index_count),
OpenGL.UNSIGNED_INT,
nil,
i32(len(shapes)),
)
}
clear(shapes)
}
render_queue :: proc(state: ^State) {
// Update uniform data
set_buffer(state.globals_ubo, &state.globals, buffer = .Uniform)
// Toggle the wireframe
OpenGL.PolygonMode(OpenGL.FRONT_AND_BACK, state.wireframe ? OpenGL.LINE : OpenGL.FILL)
OpenGL.UseProgram(state.rect_program)
render_instanced(state, state.rect_vao, &queue.rects)
OpenGL.UseProgram(state.circle_program)
render_instanced(state, state.rect_vao, &queue.circles)
OpenGL.UseProgram(state.line_program)
render_instanced(state, state.rect_vao, &queue.lines)
OpenGL.UseProgram(state.rounded_line_program)
render_instanced(state, state.rect_vao, &queue.rounded_lines)
OpenGL.UseProgram(0)
}
// }}}