odin(sdl-opengl-rendering): set up some gl abstractions
This commit is contained in:
parent
45c6452532
commit
cd1e066dcd
8 changed files with 560 additions and 225 deletions
odin/sdl-opengl-rendering
12
odin/sdl-opengl-rendering/flake.lock
generated
12
odin/sdl-opengl-rendering/flake.lock
generated
|
@ -44,11 +44,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1747681085,
|
"lastModified": 1748975148,
|
||||||
"narHash": "sha256-tG177/u/vDUnxXeIFjJWYTRSuvYRfSCDYv3tjgdf+Ec=",
|
"narHash": "sha256-Gxp3ycjTnGSlkb/MpMAAbRY7bjzVjXaeaO+TBtx++/E=",
|
||||||
"owner": "starlitcanopy",
|
"owner": "starlitcanopy",
|
||||||
"repo": "odin",
|
"repo": "odin",
|
||||||
"rev": "612433442cc03297474a31d3d40fce74ce3f5331",
|
"rev": "d2cc7127df2555ed57d71575a235bd1a3eebd26a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -70,11 +70,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1748918175,
|
"lastModified": 1748975728,
|
||||||
"narHash": "sha256-fv5w3riPVpAhMArOVR2O6jXekdiPV7KhvFLD6fE8q60=",
|
"narHash": "sha256-7/AZrVRz8bLOToQjlvz36m4yPb1ZXDm3hhM7HJ8VbcY=",
|
||||||
"owner": "starlitcanopy",
|
"owner": "starlitcanopy",
|
||||||
"repo": "ols",
|
"repo": "ols",
|
||||||
"rev": "6afccf760e9f519e12ed4b155550c8896adef61b",
|
"rev": "f0ae178c1e600d04e2fd16dc810e41427b290c69",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
@ -46,7 +46,6 @@
|
||||||
|
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
pkgs.sdl3
|
pkgs.sdl3
|
||||||
|
|
||||||
pkgs.xorg.libX11
|
pkgs.xorg.libX11
|
||||||
pkgs.xorg.libXScrnSaver
|
pkgs.xorg.libXScrnSaver
|
||||||
pkgs.xorg.libXcursor
|
pkgs.xorg.libXcursor
|
||||||
|
|
1
odin/sdl-opengl-rendering/ols.json
Normal file
1
odin/sdl-opengl-rendering/ols.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
120
odin/sdl-opengl-rendering/src/gl.odin
Normal file
120
odin/sdl-opengl-rendering/src/gl.odin
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -170,7 +170,6 @@ on_resize :: proc(state: ^State, width, height: i32) {
|
||||||
}
|
}
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
|
|
||||||
// {{{ Render
|
// {{{ Render
|
||||||
render :: proc(state: ^State) {
|
render :: proc(state: ^State) {
|
||||||
state.tick += 1
|
state.tick += 1
|
||||||
|
|
|
@ -12,10 +12,16 @@ layout(std140, binding = 0) uniform Globals {
|
||||||
float aaWidth; // Anti aliasing width
|
float aaWidth; // Anti aliasing width
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// struct Line {
|
||||||
|
// vec2 from;
|
||||||
|
// vec2 to;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// layout (location = 5) in Line instanceLine;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 pos = viewportMatrix * vec4(instanceMatrix * vec3(aPos.xy, 1), 1);
|
vec4 pos = viewportMatrix * vec4(instanceMatrix * vec3(aPos, 1), 1);
|
||||||
// vec3 pos = vec3(aPos.xy, 1);
|
|
||||||
gl_Position = vec4(pos.xyz, 1);
|
gl_Position = vec4(pos.xyz, 1);
|
||||||
vertexColor = instanceFill;
|
vertexColor = instanceFill;
|
||||||
vertexPos = aPos.xy;
|
vertexPos = aPos;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,8 @@ import "core:log"
|
||||||
import "core:math/linalg"
|
import "core:math/linalg"
|
||||||
import "vendor:OpenGL"
|
import "vendor:OpenGL"
|
||||||
|
|
||||||
// {{{ Command queues
|
|
||||||
|
// {{{ Shape Types
|
||||||
Shape :: struct {
|
Shape :: struct {
|
||||||
z: ℝ,
|
z: ℝ,
|
||||||
fill: Color,
|
fill: Color,
|
||||||
|
@ -29,11 +30,14 @@ Line :: struct {
|
||||||
thickness: ℝ,
|
thickness: ℝ,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rounded_Line :: distinct Line
|
||||||
|
// }}}
|
||||||
|
// {{{ Command queues
|
||||||
Command_Queue :: struct {
|
Command_Queue :: struct {
|
||||||
rects: [dynamic]Rect,
|
rects: [dynamic]Rect,
|
||||||
circles: [dynamic]Circle,
|
circles: [dynamic]Circle,
|
||||||
lines: [dynamic]Line,
|
lines: [dynamic]Line,
|
||||||
rounded_lines: [dynamic]Line,
|
rounded_lines: [dynamic]Rounded_Line,
|
||||||
}
|
}
|
||||||
|
|
||||||
queue: ^Command_Queue
|
queue: ^Command_Queue
|
||||||
|
@ -44,7 +48,7 @@ init_command_queue :: proc() {
|
||||||
rects = make([dynamic]Rect),
|
rects = make([dynamic]Rect),
|
||||||
circles = make([dynamic]Circle),
|
circles = make([dynamic]Circle),
|
||||||
lines = make([dynamic]Line),
|
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) {
|
draw_rounded_line_struct :: proc(line: $T/Line) {
|
||||||
append(&queue.rounded_lines, line)
|
append(&queue.rounded_lines, Rounded_Line(line))
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_rounded_line :: proc {
|
draw_rounded_line :: proc {
|
||||||
|
@ -108,11 +112,57 @@ draw_rounded_line :: proc {
|
||||||
draw_rounded_line_args,
|
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
|
// {{{ GPU data types
|
||||||
VAO :: struct {
|
VAO :: struct {
|
||||||
vao: u32,
|
vao: u32,
|
||||||
ibo: u32,
|
vertex_ind_buffer: u32,
|
||||||
vertex_pos_buffer: u32,
|
vertex_pos_buffer: u32,
|
||||||
instance_fill_buffer: u32,
|
instance_fill_buffer: u32,
|
||||||
instance_mat_buffer: u32,
|
instance_mat_buffer: u32,
|
||||||
|
@ -139,17 +189,13 @@ INSTANCE_MAT_LOCATION :: 2
|
||||||
GLOBALS_UBO_BINDING :: 0
|
GLOBALS_UBO_BINDING :: 0
|
||||||
// }}}
|
// }}}
|
||||||
// {{{ Create globals UBO
|
// {{{ Create globals UBO
|
||||||
|
create_globals_ubo :: proc() -> UBO {
|
||||||
|
id := gen_buffer()
|
||||||
|
|
||||||
create_globals_ubo :: proc() -> (out: UBO) {
|
set_buffer(id, &Global_Uniforms{}, buffer = .Uniform)
|
||||||
OpenGL.GenBuffers(1, &out)
|
OpenGL.BindBufferBase(OpenGL.UNIFORM_BUFFER, GLOBALS_UBO_BINDING, id)
|
||||||
OpenGL.BindBuffer(OpenGL.UNIFORM_BUFFER, out)
|
|
||||||
defer OpenGL.BindBuffer(OpenGL.UNIFORM_BUFFER, 0)
|
|
||||||
|
|
||||||
// Allocate buffer data
|
return id
|
||||||
OpenGL.BufferData(OpenGL.UNIFORM_BUFFER, size_of(Global_Uniforms), {}, OpenGL.STATIC_DRAW)
|
|
||||||
OpenGL.BindBufferBase(OpenGL.UNIFORM_BUFFER, GLOBALS_UBO_BINDING, out)
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
}
|
||||||
// }}}
|
// }}}
|
||||||
// {{{ Create VAO
|
// {{{ Create VAO
|
||||||
|
@ -159,234 +205,86 @@ create_vao :: proc(vertices: []ℝ², indices: []u32) -> (out: VAO, ok: bool) {
|
||||||
// Create VAO
|
// Create VAO
|
||||||
OpenGL.GenVertexArrays(1, &out.vao)
|
OpenGL.GenVertexArrays(1, &out.vao)
|
||||||
OpenGL.BindVertexArray(out.vao)
|
OpenGL.BindVertexArray(out.vao)
|
||||||
|
defer OpenGL.BindVertexArray(0)
|
||||||
|
|
||||||
// Create instance fill VBO
|
// Create instance fill VBO
|
||||||
OpenGL.GenBuffers(1, auto_cast &out.instance_fill_buffer)
|
out.instance_fill_buffer = gen_buffer()
|
||||||
OpenGL.BindBuffer(OpenGL.ARRAY_BUFFER, out.instance_fill_buffer)
|
bind_buffer(out.instance_fill_buffer, .Array)
|
||||||
|
setup_vbo(out.instance_fill_buffer, INSTANCE_FILL_LOCATION, rows = 4, divisor = 1)
|
||||||
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
|
// Create instance mat VBO
|
||||||
OpenGL.GenBuffers(1, auto_cast &out.instance_mat_buffer)
|
out.instance_mat_buffer = gen_buffer()
|
||||||
OpenGL.BindBuffer(OpenGL.ARRAY_BUFFER, out.instance_mat_buffer)
|
setup_vbo(
|
||||||
|
out.instance_mat_buffer,
|
||||||
for i in u32(0) ..< 3 {
|
INSTANCE_MAT_LOCATION,
|
||||||
OpenGL.EnableVertexAttribArray(INSTANCE_MAT_LOCATION + i)
|
ty = .Float,
|
||||||
vec3_size :: size_of(ℝ³)
|
rows = 3,
|
||||||
OpenGL.VertexAttribPointer(
|
cols = 3,
|
||||||
INSTANCE_MAT_LOCATION + i,
|
divisor = 1,
|
||||||
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
|
// Create position VBO
|
||||||
OpenGL.EnableVertexAttribArray(VERTEX_POS_LOCATION)
|
out.vertex_pos_buffer = gen_buffer()
|
||||||
OpenGL.VertexAttribPointer(VERTEX_POS_LOCATION, 2, OpenGL.FLOAT, false, 2 * size_of(ℝ), 0)
|
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
|
// Create the vertex_ind_buffer
|
||||||
OpenGL.GenBuffers(1, auto_cast &out.ibo)
|
out.vertex_ind_buffer = gen_buffer()
|
||||||
OpenGL.BindBuffer(OpenGL.ELEMENT_ARRAY_BUFFER, out.ibo)
|
set_buffer(out.vertex_ind_buffer, indices, buffer = .Element_Array, usage = .Static)
|
||||||
OpenGL.BufferData(
|
|
||||||
OpenGL.ELEMENT_ARRAY_BUFFER,
|
|
||||||
len(indices) * size_of(u32),
|
|
||||||
raw_data(indices),
|
|
||||||
OpenGL.STATIC_DRAW,
|
|
||||||
)
|
|
||||||
|
|
||||||
OpenGL.BindVertexArray(0)
|
|
||||||
|
|
||||||
return out, true
|
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
|
// {{{ Render the entire queue
|
||||||
draw_instances :: proc(vao: VAO, instances: ℕ) {
|
render_instanced :: proc(state: ^State, vao: VAO, shapes: ^[dynamic]$T) {
|
||||||
OpenGL.BindVertexArray(vao.vao)
|
OpenGL.BindVertexArray(vao.vao)
|
||||||
OpenGL.DrawElementsInstanced(
|
|
||||||
OpenGL.TRIANGLE_FAN,
|
|
||||||
i32(vao.index_count),
|
|
||||||
OpenGL.UNSIGNED_INT,
|
|
||||||
nil,
|
|
||||||
i32(instances),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
clear_screen :: proc() {
|
steps := len(shapes) / INSTANCES
|
||||||
OpenGL.Clear(OpenGL.COLOR_BUFFER_BIT | OpenGL.DEPTH_BUFFER_BIT)
|
|
||||||
|
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) {
|
render_queue :: proc(state: ^State) {
|
||||||
// Update uniform data
|
// Update uniform data
|
||||||
OpenGL.BindBuffer(OpenGL.UNIFORM_BUFFER, state.globals_ubo)
|
set_buffer(state.globals_ubo, &state.globals, buffer = .Uniform)
|
||||||
OpenGL.BufferData(
|
|
||||||
OpenGL.UNIFORM_BUFFER,
|
|
||||||
size_of(Global_Uniforms),
|
|
||||||
&state.globals,
|
|
||||||
OpenGL.DYNAMIC_DRAW,
|
|
||||||
)
|
|
||||||
OpenGL.BindBuffer(OpenGL.UNIFORM_BUFFER, 0)
|
|
||||||
|
|
||||||
// Toggle the wireframe
|
// Toggle the wireframe
|
||||||
if state.wireframe {
|
OpenGL.PolygonMode(OpenGL.FRONT_AND_BACK, state.wireframe ? OpenGL.LINE : OpenGL.FILL)
|
||||||
OpenGL.PolygonMode(OpenGL.FRONT_AND_BACK, OpenGL.LINE)
|
|
||||||
} else {
|
|
||||||
OpenGL.PolygonMode(OpenGL.FRONT_AND_BACK, OpenGL.FILL)
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenGL.UseProgram(state.rect_program)
|
OpenGL.UseProgram(state.rect_program)
|
||||||
for i := 0; i < len(queue.rects); i += INSTANCES {
|
render_instanced(state, state.rect_vao, &queue.rects)
|
||||||
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)
|
|
||||||
|
|
||||||
OpenGL.UseProgram(state.circle_program)
|
OpenGL.UseProgram(state.circle_program)
|
||||||
for i := 0; i < len(queue.circles); i += INSTANCES {
|
render_instanced(state, state.rect_vao, &queue.circles)
|
||||||
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)
|
|
||||||
|
|
||||||
OpenGL.UseProgram(state.line_program)
|
OpenGL.UseProgram(state.line_program)
|
||||||
for i := 0; i < len(queue.lines); i += INSTANCES {
|
render_instanced(state, state.rect_vao, &queue.lines)
|
||||||
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)
|
|
||||||
|
|
||||||
OpenGL.UseProgram(state.rounded_line_program)
|
OpenGL.UseProgram(state.rounded_line_program)
|
||||||
for i := 0; i < len(queue.rounded_lines); i += INSTANCES {
|
render_instanced(state, state.rect_vao, &queue.rounded_lines)
|
||||||
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)
|
|
||||||
|
|
||||||
OpenGL.UseProgram(0)
|
OpenGL.UseProgram(0)
|
||||||
}
|
}
|
||||||
|
|
312
odin/sdl-opengl-rendering/src/shape.odin~
Normal file
312
odin/sdl-opengl-rendering/src/shape.odin~
Normal 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)
|
||||||
|
}
|
||||||
|
// }}}
|
Loading…
Add table
Add a link
Reference in a new issue