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": {
|
||||
"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": {
|
||||
|
|
|
@ -46,7 +46,6 @@
|
|||
|
||||
buildInputs = [
|
||||
pkgs.sdl3
|
||||
|
||||
pkgs.xorg.libX11
|
||||
pkgs.xorg.libXScrnSaver
|
||||
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 :: proc(state: ^State) {
|
||||
state.tick += 1
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
out.instance_mat_buffer = gen_buffer()
|
||||
setup_vbo(
|
||||
out.instance_mat_buffer,
|
||||
INSTANCE_MAT_LOCATION,
|
||||
ty = .Float,
|
||||
rows = 3,
|
||||
cols = 3,
|
||||
divisor = 1,
|
||||
)
|
||||
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.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)
|
||||
|
||||
// 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)
|
||||
// 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)
|
||||
}
|
||||
|
|
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