Compare commits

...

10 Commits

10 changed files with 1446 additions and 330 deletions

View File

@@ -121,7 +121,7 @@ get_camera_projection_matrix :: proc(renderer: ^Renderer, camera: ^Camera) -> (l
case Camera_Perspective: { case Camera_Perspective: {
projection_matrix *= linalg.matrix4_perspective( projection_matrix *= linalg.matrix4_perspective(
linalg.to_radians(f32(t.fov_degrees)), linalg.to_radians(f32(t.fov_degrees)),
get_aspect_ratio(renderer), get_aspect_ratio(renderer, .Render),
camera.near, camera.near,
camera.far, camera.far,
flip_z_axis = true, flip_z_axis = true,
@@ -129,7 +129,7 @@ get_camera_projection_matrix :: proc(renderer: ^Renderer, camera: ^Camera) -> (l
} }
case Camera_Orthographic: { case Camera_Orthographic: {
half_h := t.height / 2 half_h := t.height / 2
half_w := half_h * get_aspect_ratio(renderer) half_w := half_h * get_aspect_ratio(renderer, .Render)
projection_matrix *= linalg.matrix_ortho3d( projection_matrix *= linalg.matrix_ortho3d(
-half_w, half_w, -half_w, half_w,

48
color.odin Normal file
View File

@@ -0,0 +1,48 @@
package renderer
import "core:math/rand"
Color :: union #no_nil {
RGB_Color,
RGBA_Color,
}
RGB_Color :: [3]u8
RGBA_Color :: [4]u8
color_to_f32 :: proc {
rgb_color_to_f32_array,
rgba_color_to_f32_array,
}
rgb_color_to_f32_array :: proc(color: RGB_Color) -> [3]f32 {
return {
f32(color.r) / f32(max(u8)),
f32(color.g) / f32(max(u8)),
f32(color.b) / f32(max(u8)),
}
}
rgba_color_to_f32_array :: proc(color: RGBA_Color) -> [4]f32 {
return {
f32(color.r) / f32(max(u8)),
f32(color.g) / f32(max(u8)),
f32(color.b) / f32(max(u8)),
f32(color.a) / f32(max(u8)),
}
}
get_random_color_rgb :: proc() -> RGB_Color {
r := u8(rand.uint32() / u32(max(u8)))
g := u8(rand.uint32() / u32(max(u8)))
b := u8(rand.uint32() / u32(max(u8)))
return { r, g, b }
}
get_random_color_rgba :: proc() -> RGBA_Color {
c := get_random_color_rgb()
a := u8(rand.uint32() / u32(max(u8)))
return { c.r, c.g, c.b, a }
}

View File

@@ -1,16 +1,51 @@
package renderer package renderer
import "core:log"
import "core:fmt"
MATERIAL_MAX_TEXTURES :: 32
Material :: struct { Material :: struct {
// 'Name'? // TODO: SS - 'Name'?
shader_program: ^Shader_Program, shader_program: ^Shader_Program,
texture: ^Texture, // Diffuse, normal etc later.
// uniforms, textures, etc. textures: [MATERIAL_MAX_TEXTURES]^Texture,
texture_count: u8,
uniforms: []Uniform,
casts_shadow: bool,
uv_scale: [2]f32, // TODO: SS - Move?
} }
create_material :: proc(program: ^Shader_Program, texture0: ^Texture) -> (Material, bool) { create_material :: proc(program: ^Shader_Program, textures: []^Texture, uniforms: []Uniform, casts_shadow: bool) -> (Material, bool) {
m: Material m: Material
m.shader_program = program m.shader_program = program
m.texture = texture0
for t, i in textures {
if t == nil {
log.warnf("Found nil texture at index %v in when creating material.", i)
continue
}
if m.texture_count >= MATERIAL_MAX_TEXTURES {
log.warnf("Too many textures passed when creating material. Max is %v.", MATERIAL_MAX_TEXTURES)
break
}
m.textures[m.texture_count] = t
m.texture_count += 1
}
if m.texture_count == 0 {
log.warn("No textures passed when creating material.")
// TODO: SS - Should we return false here?
}
m.uniforms = uniforms
m.casts_shadow = casts_shadow
m.uv_scale = { 1.0, 1.0 } // NOTE: SS - Hardcoded.
return m, true return m, true
} }

228
mesh.odin
View File

@@ -1,5 +1,7 @@
package renderer package renderer
import "core:math"
import "core:log"
Mesh :: struct { Mesh :: struct {
amount_of_indices: u32, amount_of_indices: u32,
backend: Mesh_Backend, backend: Mesh_Backend,
@@ -14,7 +16,12 @@ Mesh_Layout :: struct {
type_size: u32, type_size: u32,
} }
create_mesh :: proc(renderer: ^Renderer, layout: []Mesh_Layout, vertices: []f32, indices: []u32) -> (Mesh, bool) { // TODO: SS - Should probably return a Mesh_Handle. create_mesh :: proc {
create_mesh_layout_vertices_indices,
create_mesh_from_primitive,
}
create_mesh_layout_vertices_indices :: proc(renderer: ^Renderer, layout: []Mesh_Layout, vertices: []f32, indices: []u32) -> (Mesh, bool) { // TODO: SS - Should probably return a Mesh_Handle.
mesh: Mesh mesh: Mesh
if len(vertices) == 0 { if len(vertices) == 0 {
@@ -40,3 +47,222 @@ create_mesh :: proc(renderer: ^Renderer, layout: []Mesh_Layout, vertices: []f32,
return m, true return m, true
} }
Primitive_Mesh_Type :: enum {
Cube,
UI_Quad, World_Quad, // NOTE: SS - Not sure I like two quads but it's probably alright.
Sphere,
}
create_mesh_from_primitive :: proc(renderer: ^Renderer, primitive_mesh_type: Primitive_Mesh_Type) -> (Mesh, bool) {
switch primitive_mesh_type {
case .Cube: {
m, ok := create_mesh(
renderer = renderer,
layout = {
{ "position", 3, size_of(f32), },
{ "normal", 3, size_of(f32), },
{ "texture_coords", 2, size_of(f32), },
{ "tangent", 3, size_of(f32), },
},
vertices = []f32{
// Positions // Normals // UV // Tangent
// Back face
-0.5, -0.5, -0.5, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
0.5, -0.5, -0.5, 0.0, 0.0, -1.0, 1.0, 0.0, 1.0, 0.0, 0.0,
0.5, 0.5, -0.5, 0.0, 0.0, -1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
-0.5, 0.5, -0.5, 0.0, 0.0, -1.0, 0.0, 1.0, 1.0, 0.0, 0.0,
// Front face
-0.5, -0.5, 0.5, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
0.5, -0.5, 0.5, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0,
0.5, 0.5, 0.5, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
-0.5, 0.5, 0.5, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0,
// Left face
-0.5, 0.5, 0.5, -1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0,
-0.5, 0.5, -0.5, -1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0,
-0.5, -0.5, -0.5, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0,
-0.5, -0.5, 0.5, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0,
// Right face
0.5, 0.5, 0.5, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, -1.0,
0.5, -0.5, 0.5, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0,
0.5, -0.5, -0.5, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, -1.0,
0.5, 0.5, -0.5, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, -1.0,
// Bottom face
-0.5, -0.5, -0.5, 0.0,-1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0,
0.5, -0.5, -0.5, 0.0,-1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0,
0.5, -0.5, 0.5, 0.0,-1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0,
-0.5, -0.5, 0.5, 0.0,-1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
// Top face
-0.5, 0.5, -0.5, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0,
0.5, 0.5, -0.5, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0,
0.5, 0.5, 0.5, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0,
-0.5, 0.5, 0.5, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
},
indices = []u32 {
// Back face
0, 3, 1,
1, 3, 2,
// Front face
4, 5, 7,
5, 6, 7,
// Left face
8, 9, 11,
9, 10, 11,
// Right face
12, 13, 15,
13, 14, 15,
// Bottom face
16, 17, 18,
16, 18, 19,
// Top face
20, 22, 21,
20, 23, 22
}
)
return m, ok
}
case .World_Quad: {
m, ok := create_mesh(
renderer = renderer,
layout = {
{ "position", 3, size_of(f32), },
{ "normal", 3, size_of(f32), },
{ "texture_coords", 2, size_of(f32), },
{ "tangent", 3, size_of(f32), },
},
vertices = []f32 {
// Positions. // Normals. // Texture Coordinates. // Tangent.
0.5, 0.5, 0.0, 0, 0, 0, 1.0, 1.0, 1, 0, 0, // Top right.
0.5, -0.5, 0.0, 0, 0, 0, 1.0, 0.0, 1, 0, 0, // Bottom right.
-0.5, -0.5, 0.0, 0, 0, 0, 0.0, 0.0, 1, 0, 0, // Bottom left.
-0.5, 0.5, 0.0, 0, 0, 0, 0.0, 1.0, 1, 0, 0, // Top left.
},
indices = []u32 {
0, 1, 3,
1, 2, 3,
}
)
return m, ok
}
case .UI_Quad: {
m, ok := create_mesh(
renderer = renderer,
layout = {
{ "position", 3, size_of(f32), },
{ "texture_coords", 2, size_of(f32), },
},
vertices = []f32 {
// Positions. // UV.
0.0, 0.0, 0.0, 0.0, 1.0,
1.0, 0.0, 0.0, 1.0, 1.0,
1.0, 1.0, 0.0, 1.0, 0.0,
0.0, 1.0, 0.0, 0.0, 0.0,
},
indices = []u32 {
0, 1, 2,
0, 2, 3,
}
)
return m, ok
}
case .Sphere: {
X_SEGMENTS :: 64
Y_SEGMENTS :: 32
vertices := make([dynamic]f32, 0)
indices := make([dynamic]u32, 0)
defer delete(vertices)
defer delete(indices)
for y in 0..=Y_SEGMENTS {
y_segment := f32(y) / f32(Y_SEGMENTS)
phi := y_segment * math.PI
for x in 0..=X_SEGMENTS {
x_segment := f32(x) / f32(X_SEGMENTS)
theta := x_segment * math.TAU
px := math.cos(theta) * math.sin(phi)
py := math.cos(phi)
pz := math.sin(theta) * math.sin(phi)
// Position.
append(&vertices, px * 0.5)
append(&vertices, py * 0.5)
append(&vertices, pz * 0.5)
// Normal.
append(&vertices, px)
append(&vertices, py)
append(&vertices, pz)
// UV.
append(&vertices, x_segment)
append(&vertices, 1.0 - y_segment)
// Tangent.
tx := -math.sin(theta)
ty := f32(0.0)
tz := math.cos(theta)
append(&vertices, tx)
append(&vertices, ty)
append(&vertices, tz)
}
}
for y in 0..<Y_SEGMENTS {
for x in 0..<X_SEGMENTS {
i0 := u32(y * (X_SEGMENTS + 1) + x)
i1 := u32((y + 1) * (X_SEGMENTS + 1) + x)
i2 := u32((y + 1) * (X_SEGMENTS + 1) + x + 1)
i3 := u32(y * (X_SEGMENTS + 1) + x + 1)
append(&indices, i0)
append(&indices, i3)
append(&indices, i1)
append(&indices, i1)
append(&indices, i3)
append(&indices, i2)
}
}
m, ok := create_mesh(
renderer = renderer,
layout = {
{ "position", 3, size_of(f32), },
{ "normal", 3, size_of(f32), },
{ "texture_coords", 2, size_of(f32), },
{ "tangent", 3, size_of(f32), },
},
vertices = vertices[:],
indices = indices[:],
)
return m, ok
}
}
return {}, false
}
delete_mesh :: proc(renderer: ^Renderer, mesh: ^Mesh) {
assert(renderer != nil)
assert(mesh != nil)
when RENDER_BACKEND_OPENGL {
opengl_delete_mesh(renderer, mesh)
}
}

340
pass.odin
View File

@@ -1,46 +1,162 @@
package renderer package renderer
import "core:math/linalg"
import "core:fmt" import "core:fmt"
import "core:log"
MAX_DRAW_COMMANDS_CAPACITY :: 4096 MAX_DRAW_COMMANDS_CAPACITY :: 4096
MAX_POST_PROCESS_NODES_PER_PASS :: 8
PASS_MAX_INPUT_TEXTURES :: 8 Pass :: struct {
name: string,
type: Pass_Type,
}
Pass_Type :: union {
Scene_Pass,
Post_Processing_Pass,
}
Scene_Pass :: struct { Scene_Pass :: struct {
name: string, blend_mode: Blend_Mode,
sort_mode: Sort_Mode,
input_textures: [PASS_MAX_INPUT_TEXTURES]^Texture, // TODO: SS - Make this a map? cull_mode: Cull_Mode,
input_texture_count: u8,
output_rt: ^Render_Target,
draw_commands: [dynamic]Draw_Command, // Capacity is 'MAX_DRAW_COMMANDS_CAPACITY'. draw_commands: [dynamic]Draw_Command, // Capacity is 'MAX_DRAW_COMMANDS_CAPACITY'.
clear_color: Maybe(RGB_Color), output_rt: ^Render_Target, // Commands draw to this render-target.
blend_mode: Blend_Mode,
sort_mode: Sort_Mode,
} }
Post_Processing_Pass :: struct { Post_Processing_Pass :: struct {
name: string, post_processing_nodes: [dynamic]Post_Processing_Node, // These nodes are executed after all commands have been drawn onto the render-target.
input_texture: ^Texture, // TODO: SS - Make this an array of texture-pointers, or maybe a map.
input_depth_texture: ^Texture,
output_rt: ^Render_Target,
shader_program: Shader_Program,
} }
Uniform :: union {
Uniform_Texture,
Uniform_Float,
Uniform_Float_Pointer,
Uniform_Matrix4f32,
Uniform_Matrix4f32_Pointer,
Uniform_Vector2,
Uniform_Vector2_Pointer,
Uniform_Vector3,
Uniform_Vector3_Pointer,
Uniform_Vector4,
Uniform_Vector4_Pointer,
Uniform_Color,
Uniform_Color_Pointer,
}
Uniform_Texture :: struct {
index: u8,
value: ^Texture,
}
Uniform_Float :: struct {
name: string,
value: f32,
}
Uniform_Float_Pointer :: struct {
name: string,
value: ^f32,
}
Uniform_Matrix4f32 :: struct {
name: string,
value: linalg.Matrix4f32,
}
Uniform_Matrix4f32_Pointer :: struct {
name: string,
value: ^linalg.Matrix4f32,
}
Uniform_Vector2 :: struct {
name: string,
value: [2]f32,
}
Uniform_Vector2_Pointer :: struct {
name: string,
value: ^[2]f32,
}
Uniform_Vector3 :: struct {
name: string,
value: [3]f32,
}
Uniform_Vector3_Pointer :: struct {
name: string,
value: ^[3]f32,
}
Uniform_Vector4 :: struct {
name: string,
value: [4]f32,
}
Uniform_Vector4_Pointer :: struct {
name: string,
value: ^[4]f32,
}
Uniform_Color :: struct {
name: string,
value: Color,
}
Uniform_Color_Pointer :: struct {
name: string,
value: ^Color,
}
Post_Processing_Node :: struct {
uniforms: []Uniform,
output: ^Render_Target,
program: ^Shader_Program,
}
// TODO: SS - Create a pool of 'Draw_Command's and reuse them.
MAX_UNIFORMS_PER_DRAW_COMMAND :: 8
Draw_Command :: struct { Draw_Command :: struct {
renderer: ^Renderer, // Needed for sorting. renderer: ^Renderer, // Needed for sorting.
mesh: Mesh, mesh: Mesh,
material: Material, material: Material,
uniforms: [MAX_UNIFORMS_PER_DRAW_COMMAND]Uniform,
uniform_count: u8,
position: [3]f32, position: [3]f32,
rotation: [3]f32, rotation: [3]f32,
scale: [3]f32, scale: [3]f32,
} }
create_draw_command :: proc(renderer: ^Renderer, mesh: Mesh, material: Material, uniforms: []Uniform, position, rotation, scale: [3]f32, loc := #caller_location) -> Draw_Command {
dc: Draw_Command
dc.renderer = renderer
dc.mesh = mesh
dc.material = material
dc.position = position
dc.rotation = rotation
dc.scale = scale
for u in uniforms {
if dc.uniform_count >= MAX_UNIFORMS_PER_DRAW_COMMAND {
log.warnf("Hit max capacity (%v) of uniforms per draw command! %v", MAX_UNIFORMS_PER_DRAW_COMMAND, loc)
break
}
dc.uniforms[dc.uniform_count] = u
dc.uniform_count += 1
}
return dc
}
Blend_Mode :: enum { Blend_Mode :: enum {
None, None,
Alpha, Alpha,
@@ -79,78 +195,180 @@ Sort_Mode :: enum {
Front_To_Back, // Sorts the commands in the 'draw_commmands' array (from front to back) before drawing them. Front_To_Back, // Sorts the commands in the 'draw_commmands' array (from front to back) before drawing them.
} }
Cull_Mode :: enum {
None,
Back,
Front,
Front_Back,
}
create_scene_pass :: proc( create_scene_pass :: proc(
name: string, name: string,
input_textures: []^Texture,
output_rt: ^Render_Target,
clear_color: Maybe(RGB_Color),
blend_mode: Blend_Mode, blend_mode: Blend_Mode,
sort_mode: Sort_Mode, sort_mode: Sort_Mode,
) -> Scene_Pass cull_mode: Cull_Mode,
output_rt: ^Render_Target,
) -> Pass
{ {
// if input_depth_texture == nil { assert(len(name) > 0)
// if test_depth || clear_depth || write_depth {
// assert(false)
// }
// }
p := Scene_Pass { p: Pass
name = name, p.name = name
p.type = Scene_Pass {
output_rt = output_rt, blend_mode = blend_mode,
sort_mode = sort_mode,
cull_mode = cull_mode,
draw_commands = make([dynamic]Draw_Command, 0, MAX_DRAW_COMMANDS_CAPACITY), draw_commands = make([dynamic]Draw_Command, 0, MAX_DRAW_COMMANDS_CAPACITY),
clear_color = clear_color, output_rt = output_rt,
blend_mode = blend_mode,
sort_mode = sort_mode,
}
for texture, i in input_textures {
if p.input_texture_count >= PASS_MAX_INPUT_TEXTURES {
fmt.printfln("Hit max capacity of textures per pass.")
break
}
p.input_textures[p.input_texture_count] = texture
p.input_texture_count += 1
} }
return p return p
} }
create_post_processing_pass :: proc( create_post_processing_pass :: proc(name: string, post_processing_nodes: []Post_Processing_Node) -> Pass {
name: string, assert(len(name) > 0)
input_texture, input_depth_texture: ^Texture,
output_rt: ^Render_Target, p: Pass
shader_program: Shader_Program, p.name = name
) -> Post_Processing_Pass
{ ppp := Post_Processing_Pass {
return { post_processing_nodes = make([dynamic]Post_Processing_Node, 0, MAX_POST_PROCESS_NODES_PER_PASS)
name = name,
input_texture = input_texture,
input_depth_texture = input_depth_texture,
output_rt = output_rt,
shader_program = shader_program,
}
} }
delete_pass :: proc { append(&ppp.post_processing_nodes, ..post_processing_nodes)
delete_scene_pass,
p.type = ppp
return p
}
delete_pass :: proc(pass: ^Pass) {
assert(pass != nil)
switch &t in &pass.type {
case Scene_Pass: delete_scene_pass(&t)
case Post_Processing_Pass: delete_post_processing_pass(&t)
}
} }
@(private) delete_scene_pass :: proc(pass: ^Scene_Pass) { @(private) delete_scene_pass :: proc(pass: ^Scene_Pass) {
assert(pass.draw_commands != nil)
delete(pass.draw_commands) delete(pass.draw_commands)
} }
add_command_to_pass :: proc(renderer: ^Renderer, pass: ^Scene_Pass, command: Draw_Command) -> bool { @(private) delete_post_processing_pass :: proc(pass: ^Post_Processing_Pass) {
assert(pass.post_processing_nodes != nil)
delete(pass.post_processing_nodes)
}
add_command_to_pass :: proc(renderer: ^Renderer, pass: ^Pass, command: Draw_Command) -> bool {
assert(renderer != nil) assert(renderer != nil)
assert(pass != nil) assert(pass != nil)
switch &t in &pass.type {
case Post_Processing_Pass: {
fmt.printfln("Can't add commands to a post-processing pass.")
return false
}
case Scene_Pass: {
if t.output_rt == nil {
fmt.printfln("Pass '%v' does not have a output render-target so you're not allowed to add commands to it.", pass.name)
return false
}
cmd := command cmd := command
cmd.renderer = renderer cmd.renderer = renderer
n, err := append(&pass.draw_commands, cmd) n, err := append(&t.draw_commands, cmd)
assert(err == .None) assert(err == .None)
}
}
return true return true
} }
execute_pass :: proc(renderer: ^Renderer, pass: ^Pass, view_matrix, projection_matrix: linalg.Matrix4x4f32) { // TODO: SS - Move to 'pass.odin'.
// log.infof("Executing pass '%v'.", pass.name)
assert(renderer != nil)
assert(pass != nil)
switch &t in &pass.type {
case Scene_Pass: {
// temp_draw_count := 0
apply_polygon_mode(renderer, renderer.polygon_mode)
assert(t.output_rt != nil)
bind_render_target(renderer, t.output_rt)
defer bind_render_target(renderer, nil)
should_write_depth := t.output_rt.depth_texture != nil
should_test_depth := should_write_depth
should_clear_depth := should_write_depth
should_clear_color := true
should_scissor :: false
set_clear_color(renderer, RGBA_Color { 0, 0, 0, 0 })
clear_screen(renderer, should_clear_color, should_clear_depth, should_scissor)
apply_depth(renderer, should_test_depth, should_write_depth)
apply_blend_mode(renderer, t.blend_mode)
defer apply_blend_mode(renderer, .None)
apply_cull_mode(renderer, t.cull_mode)
defer apply_cull_mode(renderer, .None)
sort_draw_commands(renderer, &t)
// TODO: SS - Frustum-culling.
for &dc in &t.draw_commands { // TODO: SS - Don't think we need the address of the draw-commands.
model_matrix := linalg.identity(linalg.Matrix4x4f32)
// Translate.
translation := linalg.matrix4_translate(dc.position)
// Rotate.
rot_x := linalg.matrix4_rotate(linalg.to_radians(dc.rotation.x), [3]f32 { 1, 0, 0 })
rot_y := linalg.matrix4_rotate(linalg.to_radians(dc.rotation.y), [3]f32 { 0, 1, 0 })
rot_z := linalg.matrix4_rotate(linalg.to_radians(dc.rotation.z), [3]f32 { 0, 0, 1 })
rotation := rot_z * rot_y * rot_x
// Scale.
scale := linalg.matrix4_scale(dc.scale)
model_matrix *= translation * rotation * scale
// Apply uniforms.
activate_material(&dc.material, model_matrix, view_matrix, projection_matrix)
if dc.uniform_count > 0 {
set_shader_uniforms(dc.material.shader_program, dc.uniforms[:dc.uniform_count])
}
// Draw the mesh.
draw_mesh(&dc.mesh)
// temp_draw_count += 1
}
// Clear the pass' draw-commands.
clear(&t.draw_commands)
// TODO: SS - "Deactivate" the pass?
apply_polygon_mode(renderer, .Fill)
// log.infof(" Done! Drew %v meshes.", temp_draw_count)
}
case Post_Processing_Pass: {
// Execute the post-processing nodes.
for &pp in &t.post_processing_nodes {
execute_post_processing_node(renderer, &pp, view_matrix, projection_matrix)
}
// TODO: SS - "Deactivate" the pass?
}
}
}

View File

@@ -2,93 +2,52 @@ package renderer
import "core:mem" import "core:mem"
import "core:log" import "core:log"
import "core:fmt"
PIPELINE_MAX_SCENE_PASSES :: 8 PIPELINE_MAX_PASSES :: 16
PIPELINE_MAX_POST_PROCESSING_PASSES :: 8
Pipeline :: struct { Pipeline :: struct {
scene_passes: [PIPELINE_MAX_SCENE_PASSES]^Scene_Pass, passes: [PIPELINE_MAX_PASSES]^Pass,
amount_of_scene_passes: u8, amount_of_passes: u8,
post_processing_passes: [PIPELINE_MAX_POST_PROCESSING_PASSES]^Post_Processing_Pass,
amount_of_post_processing_passes: u8,
fullscreen_material: ^Material,
fullscreen_mesh: ^Mesh,
} }
clear_pipeline :: proc(renderer: ^Renderer) { clear_pipeline :: proc(renderer: ^Renderer) {
assert(renderer != nil) assert(renderer != nil)
p := &renderer.pipeline p := &renderer.pipeline
p.amount_of_scene_passes = 0; p.amount_of_passes = 0;
mem.set(&p.scene_passes[0], 0, PIPELINE_MAX_SCENE_PASSES * size_of(^Scene_Pass)) mem.set(&p.passes[0], 0, PIPELINE_MAX_PASSES * size_of(^Scene_Pass))
p.amount_of_post_processing_passes = 0;
mem.set(&p.post_processing_passes[0], 0, PIPELINE_MAX_POST_PROCESSING_PASSES * size_of(^Post_Processing_Pass))
} }
set_pipeline :: proc( set_pipeline :: proc(
renderer: ^Renderer, renderer: ^Renderer,
fullscreen_material: ^Material, fullscreen_mesh: ^Mesh, passes: []^Pass,
scene_passes: []^Scene_Pass,
post_processing_passes: []^Post_Processing_Pass,
){ ){
assert(renderer != nil) assert(renderer != nil)
clear_pipeline(renderer) clear_pipeline(renderer)
renderer.pipeline.fullscreen_material = fullscreen_material for p in passes {
renderer.pipeline.fullscreen_mesh = fullscreen_mesh
for p in scene_passes {
if !add_pass_to_pipeline(renderer, p) {
break
}
}
for p in post_processing_passes {
if !add_pass_to_pipeline(renderer, p) { if !add_pass_to_pipeline(renderer, p) {
break break
} }
} }
} }
add_pass_to_pipeline :: proc { add_pass_to_pipeline :: proc(renderer: ^Renderer, pass: ^Pass) -> bool {
add_scene_pass_to_pipeline,
add_post_processing_pass_to_pipeline,
}
@(private="file") add_scene_pass_to_pipeline :: proc(renderer: ^Renderer, scene_pass: ^Scene_Pass) -> bool {
assert(renderer != nil) assert(renderer != nil)
assert(scene_pass != nil) assert(pass != nil)
pipeline := &renderer.pipeline pipeline := &renderer.pipeline
if pipeline.amount_of_scene_passes == PIPELINE_MAX_SCENE_PASSES { assert(pipeline.amount_of_passes < PIPELINE_MAX_PASSES)
log.warnf("Failed to add scene-pass '%v' to renderer's pipeline; hit max capacity (%v).", scene_pass.name, PIPELINE_MAX_SCENE_PASSES) // if pipeline.amount_of_passes == PIPELINE_MAX_PASSES {
return false // log.warnf("Failed to add scene-pass '%v' to renderer's pipeline; hit max capacity (%v).", pass.name, PIPELINE_MAX_PASSES)
} // return false
// }
pipeline.scene_passes[pipeline.amount_of_scene_passes] = scene_pass pipeline.passes[pipeline.amount_of_passes] = pass
pipeline.amount_of_scene_passes += 1 pipeline.amount_of_passes += 1
return true
}
@(private="file") add_post_processing_pass_to_pipeline :: proc(renderer: ^Renderer, post_processing_pass: ^Post_Processing_Pass) -> bool {
assert(renderer != nil)
assert(post_processing_pass != nil)
pipeline := &renderer.pipeline
if pipeline.amount_of_post_processing_passes == PIPELINE_MAX_POST_PROCESSING_PASSES {
log.warnf("Failed to add post-processing pass '%v' to renderer's pipeline; hit max capacity (%v).", post_processing_pass.name, PIPELINE_MAX_POST_PROCESSING_PASSES)
return false
}
pipeline.post_processing_passes[pipeline.amount_of_post_processing_passes] = post_processing_pass
pipeline.amount_of_post_processing_passes += 1
return true return true
} }

View File

@@ -12,31 +12,72 @@ RENDER_BACKEND_DIRECTX11 :: #config(RENDER_BACKEND_DIRECTX11, false)
RENDER_BACKEND_METAL :: #config(RENDER_BACKEND_METAL, false) RENDER_BACKEND_METAL :: #config(RENDER_BACKEND_METAL, false)
Renderer :: struct { Renderer :: struct {
render_resolution: Resolution,
viewport: Viewport, viewport: Viewport,
backbuffer: Backbuffer,
surface_ptr: rawptr, surface_ptr: rawptr,
vsync: bool,
backend: rawptr, backend: rawptr,
polygon_mode: Polygon_Mode,
pipeline: Pipeline, pipeline: Pipeline,
active_camera: ^Camera, active_camera: ^Camera, // NOTE: SS - Hardcoded to 1 active camera. Split-screen is likely not possible due to this. Fix(?).
// Primitive meshes.
default_cube_mesh, default_quad_world_mesh, default_quad_ui_mesh, default_sphere_mesh: Mesh,
fullscreen_vertex_shader, fullscreen_fragment_shader: Shader,
fullscreen_shader_program: Shader_Program,
fullscreen_mesh: Mesh,
}
Polygon_Mode :: enum {
Fill,
Line,
Point,
}
Resolution :: struct {
width, height: u16,
}
Backbuffer :: struct {
resolution: Resolution,
} }
Viewport :: struct { Viewport :: struct {
x, y, width, height: u16, x, y: u16,
res: Resolution,
} }
get_aspect_ratio :: proc(renderer: ^Renderer) -> f32 { Aspect_Ratio_Type :: enum { // TODO: SS - Rename?
Render,
Backbuffer,
Viewport,
}
get_aspect_ratio :: proc(renderer: ^Renderer, type: Aspect_Ratio_Type) -> f32 {
assert(renderer != nil) assert(renderer != nil)
viewport := &renderer.viewport res: Resolution
return f32(viewport.width) / f32(viewport.height) switch type {
case .Render: res = renderer.render_resolution
case .Backbuffer: res = renderer.backbuffer.resolution
case .Viewport: res = renderer.viewport.res
} }
RGB_Color :: [3]u8 return f32(res.width) / f32(res.height)
RGBA_Color :: [4]u8 }
create :: proc(render_resolution: Resolution, surface_ptr: rawptr) -> (^Renderer, bool) {
if render_resolution.width == 0 || render_resolution.height == 0 {
return nil, false
}
create :: proc(surface_ptr: rawptr) -> (^Renderer, bool) {
renderer := new(Renderer) renderer := new(Renderer)
renderer.render_resolution = render_resolution
renderer.surface_ptr = surface_ptr renderer.surface_ptr = surface_ptr
when RENDER_BACKEND_OPENGL { when RENDER_BACKEND_OPENGL {
@@ -53,76 +94,197 @@ create :: proc(surface_ptr: rawptr) -> (^Renderer, bool) {
return nil, false return nil, false
} }
set_vsync(renderer, true)
{ // Create the default mesh-primitives.
// default_cube_mesh, default_quad_world_mesh, default_sphere_mesh: Mesh,
if m, ok := create_mesh(renderer, .Cube); ok {
renderer.default_cube_mesh = m
}
if m, ok := create_mesh(renderer, .World_Quad); ok {
renderer.default_quad_world_mesh = m
}
if m, ok := create_mesh(renderer, .UI_Quad); ok {
renderer.default_quad_ui_mesh = m
}
if m, ok := create_mesh(renderer, .Sphere); ok {
renderer.default_sphere_mesh = m
}
}
{ // Create the fullscreen shaders, material and mesh.
fs_vertex_shader, fs_vertex_shader_ok := create_shader(renderer, .Vertex, "fs_vertex.glsl")
assert(fs_vertex_shader_ok)
fs_frag_shader, fs_frag_shader_ok := create_shader(renderer, .Fragment, "fs_frag.glsl")
assert(fs_frag_shader_ok)
fs_program, fs_program_ok := create_shader_program(renderer, &fs_vertex_shader, &fs_frag_shader)
assert(fs_program_ok)
fs_quad_mesh, fs_quad_mesh_created := create_mesh(
renderer = renderer,
layout = {
{ "position", 2, size_of(f32), },
},
vertices = []f32 {
-1, -1,
1, -1,
1, 1,
-1, 1,
},
indices = []u32 {
0, 1, 2,
0, 2, 3,
}
)
assert(fs_quad_mesh_created)
renderer.fullscreen_vertex_shader = fs_vertex_shader
renderer.fullscreen_fragment_shader = fs_frag_shader
renderer.fullscreen_shader_program = fs_program
renderer.fullscreen_mesh = fs_quad_mesh
}
return renderer, true return renderer, true
} }
set_viewport :: proc(renderer: ^Renderer, x, y, width, height: u16) { set_backbuffer_resolution :: proc(renderer: ^Renderer, res: Resolution) {
log.infof("Setting viewport to %v:%v, %vx%v.", x, y, width, height) assert(renderer != nil)
renderer.backbuffer.resolution = res
renderer.viewport = {
x = x,
y = y,
width = width,
height = height,
} }
set_viewport :: proc {
set_viewport_with_viewport,
set_viewport_with_x_y_w_h,
}
set_viewport_with_viewport :: proc(renderer: ^Renderer, viewport: Viewport) {
assert(renderer != nil)
// log.infof("Setting viewport to %v:%v, %vx%v.", viewport.x, viewport.y, viewport.res.width, viewport.res.height)
renderer.viewport = viewport
when RENDER_BACKEND_OPENGL { when RENDER_BACKEND_OPENGL {
opengl_viewport_changed(renderer) opengl_viewport_changed(renderer)
} }
} }
@(private="file") set_clear_color :: proc(renderer: ^Renderer, color: RGB_Color) { set_viewport_with_x_y_w_h :: proc(renderer: ^Renderer, x, y, width, height: u16) {
assert(renderer != nil)
set_viewport_with_viewport(renderer, Viewport {
x, y,
{ width, height }
})
}
set_vsync :: proc(renderer: ^Renderer, on: bool) {
assert(renderer != nil)
if renderer.vsync == on {
return
}
success := false
when RENDER_BACKEND_OPENGL {
success = opengl_set_vsync(renderer, on)
}
if success {
renderer.vsync = on
}
}
@(private) set_clear_color :: proc(renderer: ^Renderer, color: Color) {
when RENDER_BACKEND_OPENGL { when RENDER_BACKEND_OPENGL {
opengl_set_clear_color(renderer, color) opengl_set_clear_color(renderer, color)
} }
} }
@(private="file") clear_screen :: proc(renderer: ^Renderer, clear_color: bool, clear_depth: bool) { @(private) clear_screen :: proc(renderer: ^Renderer, clear_color, clear_depth, scissor: bool) {
when RENDER_BACKEND_OPENGL { when RENDER_BACKEND_OPENGL {
opengl_clear_screen(renderer, clear_color, clear_depth) opengl_clear_screen(renderer, clear_color, clear_depth, scissor)
} }
} }
render_frame :: proc(renderer: ^Renderer) { render_frame :: proc(renderer: ^Renderer, texture_to_present: ^Texture, clear_color: Color) {
if renderer.active_camera == nil { if renderer.active_camera == nil {
fmt.printfln("No active camera!") fmt.printfln("No active camera!")
return return
} }
view_matrix, _ := get_camera_view_matrix(renderer.active_camera) camera_view_matrix, _ := get_camera_view_matrix(renderer.active_camera)
projection_matrix, _ := get_camera_projection_matrix(renderer, renderer.active_camera) camera_projection_matrix, _ := get_camera_projection_matrix(renderer, renderer.active_camera)
pipeline := &renderer.pipeline for i in 0 ..< renderer.pipeline.amount_of_passes {
execute_pass(renderer, renderer.pipeline.passes[i], camera_view_matrix, camera_projection_matrix)
for i in 0 ..< pipeline.amount_of_scene_passes {
execute_scene_pass(renderer, pipeline.scene_passes[i], view_matrix, projection_matrix)
} }
for i in 0 ..< pipeline.amount_of_post_processing_passes { apply_polygon_mode(renderer, .Fill)
execute_post_processing_pass(renderer, pipeline.post_processing_passes[i], view_matrix, projection_matrix)
if texture_to_present != nil { // Present.
// Bind to the screen.
bind_render_target(renderer, nil)
// Disable depth
apply_depth(renderer, false, false)
compute_letterboxed_viewport :: proc(backbuffer: Resolution, render: Resolution) -> Viewport {
bb_w, bb_h := expand_values(backbuffer)
r_w, r_h := expand_values(render)
bb_aspect := f32(bb_w) / f32(bb_h)
r_aspect := f32(r_w) / f32(r_h)
if bb_aspect > r_aspect {
// Pillarbox.
h := bb_h
w := u16(f32(h) * r_aspect)
x := (bb_w - w) / 2
return { x, 0, { w, h } }
} else {
// Letterbox.
w := bb_w
h := u16(f32(w) / r_aspect)
y := (bb_h - h) / 2
return { 0, y, { w, h } }
}
} }
bind_render_target(renderer, nil) // Det görs ju här.. // Clear
set_viewport(renderer, 0, 0, renderer.backbuffer.resolution.width, renderer.backbuffer.resolution.height)
set_clear_color(renderer, RGB_Color{ 0, 0, 0 })
clear_screen(renderer, true, false, false)
if pipeline.fullscreen_material != nil { set_viewport(renderer, compute_letterboxed_viewport(renderer.backbuffer.resolution, renderer.render_resolution))
if pipeline.fullscreen_material.texture != nil { set_clear_color(renderer, clear_color)
activate_fullscreen_material(renderer) clear_screen(renderer, true, true, true)
if pipeline.fullscreen_mesh != nil { // Create a temporary Material.
draw_mesh(pipeline.fullscreen_mesh) mat := Material {
} shader_program = &renderer.fullscreen_shader_program,
else { textures = {
log.warn("Renderer is missing a fullscreen-mesh.") 0 = texture_to_present,
},
texture_count = 1,
} }
deactivate_fullscreen_material(renderer) apply_blend_mode(renderer, .Alpha)
}
else { // Activate.
log.warn("Fullscreen-material does not have a texture.") activate_fullscreen_material(renderer, &mat)
} defer deactivate_fullscreen_material(renderer)
}
else { // Draw.
log.warn("Renderer is missing a fullscreen-material.") draw_mesh(&renderer.fullscreen_mesh)
apply_blend_mode(renderer, .None)
} }
when RENDER_BACKEND_OPENGL { when RENDER_BACKEND_OPENGL {
@@ -130,65 +292,41 @@ render_frame :: proc(renderer: ^Renderer) {
} }
} }
execute_scene_pass :: proc(renderer: ^Renderer, pass: ^Scene_Pass, view_matrix, projection_matrix: linalg.Matrix4x4f32) { execute_post_processing_node :: proc(renderer: ^Renderer, node: ^Post_Processing_Node, view_matrix, projection_matrix: linalg.Matrix4x4f32) {
// fmt.printfln("Rendering pass '%v' to output_rt %v.", pass.name, pass.output_rt) assert(renderer != nil)
assert(node != nil)
assert(node.program != nil)
assert(node.output != nil)
// TODO: SS - Use 'pass.input_textures' assert(node.output != nil)
bind_render_target(renderer, node.output)
defer bind_render_target(renderer, nil)
assert(pass.output_rt != nil) apply_depth(renderer, false, false)
bind_render_target(renderer, pass.output_rt)
should_write_depth := pass.output_rt.depth_texture != nil // fmt.printfln("TODO: SS - Execute post-processing node '%v' (VS: '%v', FS: '%v').", "NAME", node.program.vertex_shader.path, node.program.fragment_shader.path)
should_test_depth := should_write_depth
should_clear_depth := should_write_depth
cc, has_clear_color := pass.clear_color.? set_shader_uniforms(node.program, node.uniforms)
if has_clear_color {
set_clear_color(renderer, cc) mat: Material
mat.shader_program = node.program
for u in node.uniforms {
if t, is_texture := u.(Uniform_Texture); is_texture {
mat.textures[mat.texture_count] = t.value
mat.texture_count += 1
}
} }
should_clear_color := has_clear_color && pass.output_rt.color_texture != nil activate_fullscreen_material(renderer, &mat)
draw_mesh(&renderer.fullscreen_mesh)
clear_screen(renderer, should_clear_color, should_clear_depth)
apply_depth(renderer, should_test_depth, should_write_depth)
apply_blend_mode(renderer, pass.blend_mode)
sort_draw_commands(renderer, pass)
for &dc in &pass.draw_commands { // TODO: SS - Don't think we need the address of the draw-commands.
model_matrix := linalg.identity(linalg.Matrix4x4f32)
// Translate.
translation := linalg.matrix4_translate(dc.position)
// Rotate.
rot_x := linalg.matrix4_rotate(linalg.to_radians(dc.rotation.x), [3]f32 { 1, 0, 0 })
rot_y := linalg.matrix4_rotate(linalg.to_radians(dc.rotation.y), [3]f32 { 0, 1, 0 })
rot_z := linalg.matrix4_rotate(linalg.to_radians(dc.rotation.z), [3]f32 { 0, 0, 1 })
rotation := rot_z * rot_y * rot_x
// Scale.
scale := linalg.matrix4_scale(dc.scale)
model_matrix *= translation * rotation * scale
activate_material(&dc.material, model_matrix, view_matrix, projection_matrix)
draw_mesh(&dc.mesh)
}
// TODO: SS - "Deactivate" the pass.
// Clear the pass' draw-commands.
clear(&pass.draw_commands)
}
execute_post_processing_pass :: proc(renderer: ^Renderer, pass: ^Post_Processing_Pass, view_matrix, projection_matrix: linalg.Matrix4x4f32) {
fmt.printfln("TODO: SS - Execute post-processing pass '%v'.", pass.name)
} }
destroy :: proc(renderer: ^Renderer) { destroy :: proc(renderer: ^Renderer) {
delete_mesh(renderer, &renderer.default_cube_mesh)
delete_mesh(renderer, &renderer.default_quad_world_mesh)
delete_mesh(renderer, &renderer.default_sphere_mesh)
when RENDER_BACKEND_OPENGL { when RENDER_BACKEND_OPENGL {
opengl_destroy(renderer) opengl_destroy(renderer)
} }
@@ -197,29 +335,25 @@ destroy :: proc(renderer: ^Renderer) {
free(renderer) free(renderer)
} }
@(private) activate_material :: proc(material: ^Material, model_matrix, view_matrix, projection_matrix: linalg.Matrix4x4f32) { @(private) activate_shader_program :: proc(program: ^Shader_Program) {
when RENDER_BACKEND_OPENGL {
opengl_activate_shader_program(program)
}
}
@(private) activate_material :: proc(material: ^Material, model_matrix, view_matrix, projection_matrix: linalg.Matrix4x4f32, uv_scale: [2]f32 = { 1.0, 1.0 }) {
assert(material != nil) assert(material != nil)
p := material.shader_program
activate_shader_program(p)
set_shader_uniforms(material.shader_program, material.uniforms)
when RENDER_BACKEND_OPENGL { when RENDER_BACKEND_OPENGL {
opengl_activate_material(material, model_matrix, view_matrix, projection_matrix) opengl_activate_material(material, model_matrix, view_matrix, projection_matrix)
} }
} }
@(private) activate_fullscreen_material :: proc(renderer: ^Renderer) {
assert(renderer != nil)
mat := renderer.pipeline.fullscreen_material
when RENDER_BACKEND_OPENGL {
opengl_activate_fullscreen_material(mat)
}
}
@(private) deactivate_fullscreen_material :: proc(renderer: ^Renderer) {
assert(renderer != nil)
when RENDER_BACKEND_OPENGL {
opengl_deactivate_fullscreen_material()
}
}
@(private) draw_mesh :: proc(mesh: ^Mesh) { @(private) draw_mesh :: proc(mesh: ^Mesh) {
assert(mesh != nil) assert(mesh != nil)
@@ -242,7 +376,14 @@ destroy :: proc(renderer: ^Renderer) {
} }
} }
@(private) apply_cull_mode :: proc(renderer: ^Renderer, cull_mode: Cull_Mode) {
when RENDER_BACKEND_OPENGL {
opengl_set_cull_mode(renderer, cull_mode)
}
}
@(private) sort_draw_commands :: proc(renderer: ^Renderer, pass: ^Scene_Pass) { @(private) sort_draw_commands :: proc(renderer: ^Renderer, pass: ^Scene_Pass) {
// TODO: SS - Set the pass' 'renderer' variable here instead?
switch pass.sort_mode { switch pass.sort_mode {
case .None: {} case .None: {}
case .Back_To_Front: { case .Back_To_Front: {
@@ -293,24 +434,36 @@ distance_to_camera :: proc(camera: ^Camera, position: [3]f32) -> f32 {
return linalg.distance(camera.position, position) return linalg.distance(camera.position, position)
} }
@(private) bind_framebuffer :: proc(renderer: ^Renderer, rt: ^Render_Target) -> bool {
// when RENDER_BACKEND_OPENGL {
// return opengl_bind_framebuffer(renderer, rt)
// }
return false
}
@(private) bind_depth_texture :: proc(renderer: ^Renderer, rt: ^Render_Target) -> bool {
// when RENDER_BACKEND_OPENGL {
// return opengl_bind_depth_texture(renderer, rt)
// }
return false
}
@(private) bind_render_target :: proc(renderer: ^Renderer, rt: ^Render_Target) { @(private) bind_render_target :: proc(renderer: ^Renderer, rt: ^Render_Target) {
when RENDER_BACKEND_OPENGL { when RENDER_BACKEND_OPENGL {
opengl_bind_render_target(renderer, rt) opengl_bind_render_target(renderer, rt)
} }
} }
@(private) activate_fullscreen_material :: proc(renderer: ^Renderer, material: ^Material) { // TODO: SS - Maybe remove.
assert(renderer != nil)
assert(material != nil)
assert(material.shader_program != nil)
activate_shader_program(material.shader_program)
when RENDER_BACKEND_OPENGL {
opengl_activate_fullscreen_material(material)
}
}
@(private) deactivate_fullscreen_material :: proc(renderer: ^Renderer) { // TODO: SS - Maybe remove.
assert(renderer != nil)
when RENDER_BACKEND_OPENGL {
opengl_deactivate_fullscreen_material()
}
}
@(private) apply_polygon_mode :: proc(renderer: ^Renderer, mode: Polygon_Mode) {
assert(renderer != nil)
when RENDER_BACKEND_OPENGL {
opengl_apply_polygon_mode(renderer, mode)
}
}

View File

@@ -1,6 +1,7 @@
#+private #+private
package renderer package renderer
import "core:log"
import "core:math/linalg" import "core:math/linalg"
import "core:bytes" import "core:bytes"
import "core:slice" import "core:slice"
@@ -79,29 +80,60 @@ when RENDER_BACKEND_OPENGL {
}) })
opengl_swap_buffers(renderer) opengl_swap_buffers(renderer)
win.wglSwapIntervalEXT = transmute(win.SwapIntervalEXTType)win.wglGetProcAddress("wglSwapIntervalEXT")
if win.wglSwapIntervalEXT == nil {
opengl_destroy(renderer)
return false
}
return true return true
} }
@(private) opengl_apply_renderer_viewport :: proc(renderer: ^Renderer) {
gl.Viewport(
i32(renderer.viewport.x), i32(renderer.viewport.y),
i32(renderer.viewport.res.width), i32(renderer.viewport.res.height)
)
}
opengl_viewport_changed :: proc(renderer: ^Renderer) { opengl_viewport_changed :: proc(renderer: ^Renderer) {
gl.Viewport( opengl_apply_renderer_viewport(renderer)
}
opengl_set_vsync :: proc(renderer: ^Renderer, on: bool) -> bool {
if win.wglSwapIntervalEXT == nil {
fmt.printfln("'wglSwapIntervalEXT' is nil.")
return false
}
win.wglSwapIntervalEXT(on ? 1 : 0)
return true
}
opengl_set_clear_color :: proc(renderer: ^Renderer, color: Color) {
r, g, b, a: f32 = 1.0, 1.0, 1.0, 1.0
switch &c in color {
case RGB_Color: r, g, b = expand_values(color_to_f32(c))
case RGBA_Color: r, g, b, a = expand_values(color_to_f32(c))
}
gl.ClearColor(r, g, b, a)
}
opengl_clear_screen :: proc(renderer: ^Renderer, clear_color, clear_depth, scissor: bool) {
if scissor {
gl.Enable(gl.SCISSOR_TEST)
gl.Scissor(
i32(renderer.viewport.x), i32(renderer.viewport.x),
i32(renderer.viewport.y), i32(renderer.viewport.y),
i32(renderer.viewport.width), i32(renderer.viewport.res.width),
i32(renderer.viewport.height) i32(renderer.viewport.res.height)
) )
} }
opengl_set_clear_color :: proc(renderer: ^Renderer, color: RGB_Color) {
gl.ClearColor(
f32(color.r) / f32(max(u8)),
f32(color.g) / f32(max(u8)),
f32(color.b) / f32(max(u8)),
1.0
)
}
opengl_clear_screen :: proc(renderer: ^Renderer, clear_color: bool, clear_depth: bool) {
flags := u32(0) flags := u32(0)
if clear_color { if clear_color {
flags |= gl.COLOR_BUFFER_BIT flags |= gl.COLOR_BUFFER_BIT
@@ -110,6 +142,10 @@ when RENDER_BACKEND_OPENGL {
flags |= gl.DEPTH_BUFFER_BIT flags |= gl.DEPTH_BUFFER_BIT
} }
gl.Clear(flags) gl.Clear(flags)
if scissor {
gl.Disable(gl.SCISSOR_TEST)
}
} }
opengl_swap_buffers :: proc(renderer: ^Renderer) { opengl_swap_buffers :: proc(renderer: ^Renderer) {
@@ -188,7 +224,19 @@ when RENDER_BACKEND_OPENGL {
return m, true return m, true
} }
opengl_create_shader :: proc(renderer: ^Renderer, type: Shader_Type, data: []u8) -> (Shader_OpenGL, bool) { opengl_delete_mesh :: proc(renderer: ^Renderer, mesh: ^Mesh) {
if mesh == nil { return }
gl.DeleteBuffers(1, &mesh.backend.vbo)
gl.DeleteBuffers(1, &mesh.backend.ebo)
gl.DeleteVertexArrays(1, &mesh.backend.vao)
mesh.backend.vbo = 0
mesh.backend.ebo = 0
mesh.backend.vao = 0
}
opengl_create_shader :: proc(renderer: ^Renderer, type: Shader_Type, path: string, data: []u8) -> (Shader_OpenGL, bool) {
handle: u32 handle: u32
switch type { switch type {
@@ -213,7 +261,10 @@ when RENDER_BACKEND_OPENGL {
gl.GetShaderiv(handle, gl.COMPILE_STATUS, &success) gl.GetShaderiv(handle, gl.COMPILE_STATUS, &success)
if success != 1 { if success != 1 {
gl.GetShaderInfoLog(handle, 512, nil, raw_data(&info_log)) gl.GetShaderInfoLog(handle, 512, nil, raw_data(&info_log))
fmt.printfln("OPENGL: Failed to compile shader of type %v. Log: '%v'.", type, info_log) str := string(transmute(cstring)(&info_log))
b := transmute([]u8)str
b = b[:len(b) - 1]
log.errorf("OPENGL: Failed to compile shader '%v' of type %v. Log: '%v'.", path, type, string(b))
return {}, false return {}, false
} }
@@ -239,7 +290,10 @@ when RENDER_BACKEND_OPENGL {
gl.GetProgramiv(handle, gl.LINK_STATUS, &success) gl.GetProgramiv(handle, gl.LINK_STATUS, &success)
if success != 1 { if success != 1 {
gl.GetProgramInfoLog(handle, 512, nil, raw_data(&info_log)) gl.GetProgramInfoLog(handle, 512, nil, raw_data(&info_log))
fmt.printfln("OPENGL: Failed to create shader-program. Log: '%v'.", info_log) str := string(transmute(cstring)(&info_log))
b := transmute([]u8)str
b = b[:len(b) - 1]
log.errorf("OPENGL: Failed to create shader-program. Log: '%v'.", string(b))
return {}, false return {}, false
} }
@@ -253,42 +307,62 @@ when RENDER_BACKEND_OPENGL {
gl.DeleteProgram(shader_program.backend.handle) gl.DeleteProgram(shader_program.backend.handle)
} }
opengl_activate_material :: proc(material: ^Material, model_matrix, view_matrix, projection_matrix: linalg.Matrix4x4f32) { opengl_activate_bind_textures_in_material :: proc(material: ^Material) {
gl.UseProgram(material.shader_program.backend.handle) assert(material != nil)
gl.ActiveTexture(gl.TEXTURE0) for t, i in material.textures[:material.texture_count] {
gl.BindTexture(gl.TEXTURE_2D, material.texture.backend.handle) assert(t != nil)
gl.ActiveTexture(gl.TEXTURE0 + u32(i))
gl.BindTexture(gl.TEXTURE_2D, t.backend.handle)
loc := gl.GetUniformLocation(material.shader_program.backend.handle, fmt.ctprintf("texture%d", i))
gl.Uniform1i(loc, i32(i))
}
}
opengl_activate_shader_program :: proc(program: ^Shader_Program) {
gl.UseProgram(program.backend.handle)
}
opengl_activate_material :: proc(material: ^Material, model_matrix, view_matrix, projection_matrix: linalg.Matrix4x4f32) {
opengl_activate_bind_textures_in_material(material)
// TODO: SS - Move uniforms up one step to the renderer-agnostic part of the codebase.
model_matrix_loc := gl.GetUniformLocation(material.shader_program.backend.handle, "in_model_matrix") model_matrix_loc := gl.GetUniformLocation(material.shader_program.backend.handle, "in_model_matrix")
if model_matrix_loc >= 0 {
model_matrix_as_f32_array := transmute([16]f32)(model_matrix) model_matrix_as_f32_array := transmute([16]f32)(model_matrix)
gl.UniformMatrix4fv(model_matrix_loc, 1, false, &model_matrix_as_f32_array[0]) gl.UniformMatrix4fv(model_matrix_loc, 1, false, &model_matrix_as_f32_array[0])
}
view_matrix_loc := gl.GetUniformLocation(material.shader_program.backend.handle, "in_view_matrix") view_matrix_loc := gl.GetUniformLocation(material.shader_program.backend.handle, "in_view_matrix")
if view_matrix_loc >= 0 {
view_matrix_as_f32_array := transmute([16]f32)(view_matrix) view_matrix_as_f32_array := transmute([16]f32)(view_matrix)
gl.UniformMatrix4fv(view_matrix_loc, 1, false, &view_matrix_as_f32_array[0]) gl.UniformMatrix4fv(view_matrix_loc, 1, false, &view_matrix_as_f32_array[0])
}
projection_matrix_loc := gl.GetUniformLocation(material.shader_program.backend.handle, "in_projection_matrix") projection_matrix_loc := gl.GetUniformLocation(material.shader_program.backend.handle, "in_projection_matrix")
if projection_matrix_loc >= 0 {
projection_matrix_as_f32_array := transmute([16]f32)(projection_matrix) projection_matrix_as_f32_array := transmute([16]f32)(projection_matrix)
gl.UniformMatrix4fv(projection_matrix_loc, 1, false, &projection_matrix_as_f32_array[0]) gl.UniformMatrix4fv(projection_matrix_loc, 1, false, &projection_matrix_as_f32_array[0])
} }
opengl_activate_fullscreen_material :: proc(material: ^Material) { uv_scale_loc := gl.GetUniformLocation(material.shader_program.backend.handle, "in_uv_scale")
assert(material != nil) if uv_scale_loc >= 0 {
gl.UseProgram(material.shader_program.backend.handle) gl.Uniform2fv(uv_scale_loc, 1, &material.uv_scale[0])
gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, material.texture.backend.handle)
loc := gl.GetUniformLocation(material.shader_program.backend.handle, "texture0") // eller "uTex"
if loc != -1 {
gl.Uniform1i(loc, 0)
} }
}
opengl_activate_fullscreen_material :: proc(material: ^Material) { // TODO: SS - Maybe remove.
assert(material != nil)
opengl_activate_bind_textures_in_material(material)
gl.Disable(gl.DEPTH_TEST) gl.Disable(gl.DEPTH_TEST)
gl.DepthMask(gl.FALSE) gl.DepthMask(gl.FALSE)
} }
opengl_deactivate_fullscreen_material :: proc() { opengl_deactivate_fullscreen_material :: proc() { // TODO: SS - Maybe remove.
gl.DepthMask(gl.TRUE) gl.DepthMask(gl.TRUE)
gl.Enable(gl.DEPTH_TEST) gl.Enable(gl.DEPTH_TEST)
} }
@@ -471,27 +545,56 @@ when RENDER_BACKEND_OPENGL {
) )
} }
opengl_set_cull_mode :: proc(renderer: ^Renderer, cull_mode: Cull_Mode) {
switch cull_mode {
case .None: {
gl.Disable(gl.CULL_FACE)
}
case .Back: {
gl.Enable(gl.CULL_FACE)
gl.CullFace(gl.BACK)
gl.FrontFace(gl.CCW)
}
case .Front: {
gl.Enable(gl.CULL_FACE)
gl.CullFace(gl.FRONT)
gl.FrontFace(gl.CCW)
}
case .Front_Back: {
gl.Enable(gl.CULL_FACE)
gl.CullFace(gl.FRONT_AND_BACK)
gl.FrontFace(gl.CCW)
}
}
}
opengl_bind_render_target :: proc(renderer: ^Renderer, rt: ^Render_Target) { opengl_bind_render_target :: proc(renderer: ^Renderer, rt: ^Render_Target) {
if rt == nil { if rt == nil {
gl.BindFramebuffer(gl.FRAMEBUFFER, 0) gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
gl.Viewport( opengl_apply_renderer_viewport(renderer)
0,
0,
i32(renderer.viewport.width),
i32(renderer.viewport.height),
)
return return
} }
gl.BindFramebuffer(gl.FRAMEBUFFER, rt.backend.handle) gl.BindFramebuffer(gl.FRAMEBUFFER, rt.backend.handle)
status := gl.CheckFramebufferStatus(gl.FRAMEBUFFER)
assert(status == gl.FRAMEBUFFER_COMPLETE)
texture: ^Texture = nil
if len(rt.color_textures) > 0 {
texture = rt.color_textures[0]
}
else {
texture = rt.depth_texture
}
assert(texture != nil)
gl.Viewport( gl.Viewport(
0, 0,
0, 0,
i32(rt.color_texture.width), i32(texture.width),
i32(rt.color_texture.height), i32(texture.height),
) )
} }
@@ -500,13 +603,24 @@ when RENDER_BACKEND_OPENGL {
gl.GenFramebuffers(1, &fb) gl.GenFramebuffers(1, &fb)
gl.BindFramebuffer(gl.FRAMEBUFFER, fb) gl.BindFramebuffer(gl.FRAMEBUFFER, fb)
for texture, i in rt.color_textures {
gl.FramebufferTexture2D( gl.FramebufferTexture2D(
gl.FRAMEBUFFER, gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT0 + u32(i),
gl.TEXTURE_2D, gl.TEXTURE_2D,
rt.color_texture.backend.handle, texture.backend.handle,
0 0
) )
}
attachments: [MAX_COLOR_TEXTURES_IN_RENDER_TARGET]u32
attachment_count := i32(0)
for i in 0..<len(rt.color_textures) {
attachments[i] = gl.COLOR_ATTACHMENT0 + u32(i)
attachment_count += 1
}
gl.DrawBuffers(attachment_count, &attachments[0])
if rt.depth_texture != nil { if rt.depth_texture != nil {
gl.FramebufferTexture2D( gl.FramebufferTexture2D(
@@ -529,4 +643,114 @@ when RENDER_BACKEND_OPENGL {
return true return true
} }
opengl_destroy_render_target :: proc(renderer: ^Renderer, render_target: ^Render_Target) {
gl.DeleteFramebuffers(1, &render_target.backend.handle)
}
opengl_set_shader_uniform_texture :: proc(program: ^Shader_Program, uniform: Uniform_Texture) -> bool {
opengl_activate_shader_program(program)
i := uniform.index
gl.ActiveTexture(gl.TEXTURE0 + u32(i))
gl.BindTexture(gl.TEXTURE_2D, uniform.value.backend.handle)
loc := gl.GetUniformLocation(program.backend.handle, fmt.ctprintf("texture%d", i))
if loc < 0 {
fmt.printfln("texture Loc: %v", loc)
return false
}
gl.Uniform1i(loc, i32(i))
return true
}
opengl_set_shader_uniform_float :: proc(program: ^Shader_Program, uniform: Uniform_Float) -> bool {
opengl_activate_shader_program(program)
loc := gl.GetUniformLocation(program.backend.handle, fmt.ctprintf("%v", uniform.name))
if loc < 0 {
fmt.printfln("float Loc: %v", loc)
return false
}
gl.Uniform1f(loc, uniform.value)
return true
}
// TODO: SS - This procedure and _float ^^ are very similar and will do pretty much the same thing but with calls to different gl.Uniform-- procedures. Make it generic instead then switch on the type?
opengl_set_shader_uniform_matrix4f32 :: proc(program: ^Shader_Program, uniform: Uniform_Matrix4f32) -> bool {
opengl_activate_shader_program(program)
loc := gl.GetUniformLocation(program.backend.handle, fmt.ctprintf("%v", uniform.name))
if loc < 0 {
fmt.printfln("matrixf32 loc: %v", loc)
return false
}
data := transmute([16]f32)(uniform.value)
gl.UniformMatrix4fv(loc, 1, gl.FALSE, &data[0])
return true
}
opengl_set_shader_uniform_vector2 :: proc(program: ^Shader_Program, uniform: Uniform_Vector2) -> bool {
opengl_activate_shader_program(program)
loc := gl.GetUniformLocation(program.backend.handle, fmt.ctprintf("%v", uniform.name))
if loc < 0 {
fmt.printfln("vector2 Loc: %v", loc)
return false
}
v := transmute([2]f32)uniform.value
gl.Uniform2fv(loc, 1, &v[0])
return true
}
opengl_set_shader_uniform_vector3 :: proc(program: ^Shader_Program, uniform: Uniform_Vector3) -> bool {
opengl_activate_shader_program(program)
loc := gl.GetUniformLocation(program.backend.handle, fmt.ctprintf("%v", uniform.name))
if loc < 0 {
fmt.printfln("vector3 Loc: %v", loc)
return false
}
v := transmute([3]f32)uniform.value
gl.Uniform3fv(loc, 1, &v[0])
return true
}
opengl_set_shader_uniform_vector4 :: proc(program: ^Shader_Program, uniform: Uniform_Vector4) -> bool {
opengl_activate_shader_program(program)
loc := gl.GetUniformLocation(program.backend.handle, fmt.ctprintf("%v", uniform.name))
if loc < 0 {
fmt.printfln("vector4 Loc: %v", loc)
return false
}
v := transmute([4]f32)uniform.value
gl.Uniform4fv(loc, 1, &v[0])
return true
}
opengl_apply_polygon_mode :: proc(renderer: ^Renderer, mode: Polygon_Mode) {
assert(renderer != nil)
m: u32 = gl.FILL
switch mode {
case .Fill: m = gl.FILL
case .Line: m = gl.LINE
case .Point: m = gl.POINT
}
gl.PolygonMode(gl.FRONT_AND_BACK, m)
}
} }

View File

@@ -1,14 +1,16 @@
package renderer package renderer
import "core:fmt"
import os "core:os/os2" import os "core:os/os2"
Shader :: struct { Shader :: struct {
path: string,
type: Shader_Type, type: Shader_Type,
backend: Shader_Backend, backend: Shader_Backend,
} }
Shader_Program :: struct { Shader_Program :: struct {
vertex_shader, fragment_shader: Shader, vertex_shader, fragment_shader: ^Shader,
backend: Shader_Program_Backend, backend: Shader_Program_Backend,
} }
@@ -19,6 +21,7 @@ Shader_Type :: enum {
} }
create_shader :: proc(renderer: ^Renderer, type: Shader_Type, path: string) -> (Shader, bool) { create_shader :: proc(renderer: ^Renderer, type: Shader_Type, path: string) -> (Shader, bool) {
fmt.printfln("Creating shader")
bytes, error := os.read_entire_file_from_path(path, context.allocator) bytes, error := os.read_entire_file_from_path(path, context.allocator)
if error != nil { if error != nil {
return {}, false return {}, false
@@ -27,13 +30,15 @@ create_shader :: proc(renderer: ^Renderer, type: Shader_Type, path: string) -> (
s: Shader s: Shader
s.type = type s.type = type
s.path = path
when RENDER_BACKEND_OPENGL { when RENDER_BACKEND_OPENGL {
shader_opengl, ok := opengl_create_shader(renderer, type, bytes) shader_opengl, ok := opengl_create_shader(renderer, type, path, bytes)
if !ok { if !ok {
return {}, false return {}, false
} }
s.backend = shader_opengl s.backend = shader_opengl
fmt.printfln("Backend id: %v", s.backend)
} }
else { else {
#assert(false) #assert(false)
@@ -55,8 +60,8 @@ create_shader_program :: proc(renderer: ^Renderer, vertex_shader, fragment_shade
assert(fragment_shader != nil) assert(fragment_shader != nil)
p: Shader_Program p: Shader_Program
p.vertex_shader = vertex_shader^ p.vertex_shader = vertex_shader
p.fragment_shader = fragment_shader^ p.fragment_shader = fragment_shader
when RENDER_BACKEND_OPENGL { when RENDER_BACKEND_OPENGL {
program_opengl, ok := opengl_create_shader_program(renderer, vertex_shader, fragment_shader) program_opengl, ok := opengl_create_shader_program(renderer, vertex_shader, fragment_shader)
@@ -79,3 +84,230 @@ delete_shader_program :: proc(renderer: ^Renderer, shader_program: ^Shader_Progr
opengl_delete_shader_program(renderer, shader_program) opengl_delete_shader_program(renderer, shader_program)
} }
} }
reload_shader_program :: proc(renderer: ^Renderer, p: ^Shader_Program) -> bool {
assert(renderer != nil)
assert(p != nil)
assert(p.vertex_shader != nil)
assert(p.fragment_shader != nil)
new_vs, vs_ok := create_shader(renderer, p.vertex_shader.type, p.vertex_shader.path)
if !vs_ok {
fmt.printfln("Failed to reload vertex shader")
return false
}
new_fs, fs_ok := create_shader(renderer, p.fragment_shader.type, p.fragment_shader.path)
if !fs_ok {
fmt.printfln("Failed to reload fragment shader")
delete_shader(renderer, &new_vs)
return false
}
new_program, program_ok := create_shader_program(renderer, &new_vs, &new_fs)
if !program_ok {
fmt.printfln("Failed to relink shader program")
delete_shader(renderer, &new_vs)
delete_shader(renderer, &new_fs)
return false
}
delete_shader_program(renderer, p)
delete_shader(renderer, p.vertex_shader)
delete_shader(renderer, p.fragment_shader)
p.backend = new_program.backend
p.vertex_shader^ = new_vs
p.fragment_shader^ = new_fs
return true
}
set_shader_uniforms :: proc(program: ^Shader_Program, uniforms: []Uniform) {
fs_path := program.fragment_shader != nil ? program.fragment_shader.path : "nil"
vs_path := program.vertex_shader != nil ? program.vertex_shader.path : "nil"
for u in uniforms {
switch &t in u {
case Uniform_Texture: { if !set_shader_uniform(program, t) { fmt.printfln("Failed to set uniform (texture) %v in program (vs: '%s', fs: '%s').", t.index, vs_path, fs_path) }}
case Uniform_Float: { if !set_shader_uniform(program, t) { fmt.printfln("Failed to set uniform (float) '%v' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) }}
case Uniform_Float_Pointer: { if !set_shader_uniform(program, t) { fmt.printfln("Failed to set uniform (float pointer) '%v' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) }}
case Uniform_Matrix4f32: { if !set_shader_uniform(program, t) { fmt.printfln("Failed to set uniform (matrix4f32) '%v' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) }}
case Uniform_Matrix4f32_Pointer: { if !set_shader_uniform(program, t) { fmt.printfln("Failed to set uniform (matrix4f32 pointer) '%v' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) }}
case Uniform_Vector2: { if !set_shader_uniform(program, t) { fmt.printfln("Failed to set uniform (vector2) '%v' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) }}
case Uniform_Vector2_Pointer: { if !set_shader_uniform(program, t) { fmt.printfln("Failed to set uniform (vector2 pointer) '%v' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) }}
case Uniform_Vector3: { if !set_shader_uniform(program, t) { fmt.printfln("Failed to set uniform (vector3) '%v' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) }}
case Uniform_Vector3_Pointer: { if !set_shader_uniform(program, t) { fmt.printfln("Failed to set uniform (vector3 pointer) '%v' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) }}
case Uniform_Vector4: { if !set_shader_uniform(program, t) { fmt.printfln("Failed to set uniform (vector4) '%v' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) }}
case Uniform_Vector4_Pointer: { if !set_shader_uniform(program, t) { fmt.printfln("Failed to set uniform (vector4 pointer) '%v' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) }}
case Uniform_Color: { if !set_shader_uniform(program, t) { fmt.printfln("Failed to set uniform (color) '%v' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) }}
case Uniform_Color_Pointer: { if !set_shader_uniform(program, t) { fmt.printfln("Failed to set uniform (color pointer) '%v' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) }}
}
}
}
set_shader_uniform :: proc { // TODO: SS - Improve setting shader uniforms. A bit bug-prone and annoying to explicitly add code-paths for every 'Uniform' type needed.
set_shader_uniform_texture,
set_shader_uniform_float,
set_shader_uniform_float_pointer,
set_shader_uniform_matrix4f32,
set_shader_uniform_matrix4f32_pointer,
set_shader_uniform_vector2,
set_shader_uniform_vector2_pointer,
set_shader_uniform_vector3,
set_shader_uniform_vector3_pointer,
set_shader_uniform_vector4,
set_shader_uniform_vector4_pointer,
set_shader_uniform_color,
set_shader_uniform_color_pointer,
}
set_shader_uniform_texture :: proc(program: ^Shader_Program, uniform: Uniform_Texture) -> bool {
assert(program != nil)
assert(uniform.index < MATERIAL_MAX_TEXTURES)
assert(uniform.value != nil)
when RENDER_BACKEND_OPENGL {
return opengl_set_shader_uniform_texture(program, uniform)
}
return false
}
set_shader_uniform_float_pointer :: proc(program: ^Shader_Program, uniform: Uniform_Float_Pointer) -> bool {
if uniform.value == nil {
return false
}
return set_shader_uniform(program, Uniform_Float { uniform.name, uniform.value^ })
}
set_shader_uniform_float :: proc(program: ^Shader_Program, uniform: Uniform_Float) -> bool {
assert(program != nil)
assert(len(uniform.name) > 0)
// TODO: SS - Somehow verify that 'uniform.name' exists in the shader(s)?
when RENDER_BACKEND_OPENGL {
return opengl_set_shader_uniform_float(program, uniform)
}
return false
}
set_shader_uniform_matrix4f32_pointer :: proc(program: ^Shader_Program, uniform: Uniform_Matrix4f32_Pointer) -> bool {
if uniform.value == nil {
return false
}
return set_shader_uniform(program, Uniform_Matrix4f32 { uniform.name, uniform.value^ })
}
set_shader_uniform_matrix4f32 :: proc(program: ^Shader_Program, uniform: Uniform_Matrix4f32) -> bool {
assert(program != nil)
assert(len(uniform.name) > 0)
// TODO: SS - Somehow verify that 'uniform.name' exists in the shader(s)?
when RENDER_BACKEND_OPENGL {
return opengl_set_shader_uniform_matrix4f32(program, uniform)
}
return false
}
set_shader_uniform_vector2_pointer :: proc(program: ^Shader_Program, uniform: Uniform_Vector2_Pointer) -> bool {
if uniform.value == nil {
return false
}
return set_shader_uniform(program, Uniform_Vector2 { uniform.name, uniform.value^ })
}
set_shader_uniform_vector2 :: proc(program: ^Shader_Program, uniform: Uniform_Vector2) -> bool {
assert(program != nil)
assert(len(uniform.name) > 0)
// TODO: SS - Somehow verify that 'uniform.name' exists in the shader(s)?
when RENDER_BACKEND_OPENGL {
return opengl_set_shader_uniform_vector2(program, uniform)
}
return false
}
set_shader_uniform_vector3_pointer :: proc(program: ^Shader_Program, uniform: Uniform_Vector3_Pointer) -> bool {
if uniform.value == nil {
return false
}
return set_shader_uniform(program, Uniform_Vector3 { uniform.name, uniform.value^ })
}
set_shader_uniform_vector3 :: proc(program: ^Shader_Program, uniform: Uniform_Vector3) -> bool {
assert(program != nil)
assert(len(uniform.name) > 0)
// TODO: SS - Somehow verify that 'uniform.name' exists in the shader(s)?
when RENDER_BACKEND_OPENGL {
return opengl_set_shader_uniform_vector3(program, uniform)
}
return false
}
set_shader_uniform_vector4_pointer :: proc(program: ^Shader_Program, uniform: Uniform_Vector4_Pointer) -> bool {
if uniform.value == nil {
return false
}
return set_shader_uniform(program, Uniform_Vector4 { uniform.name, uniform.value^ })
}
set_shader_uniform_vector4 :: proc(program: ^Shader_Program, uniform: Uniform_Vector4) -> bool {
assert(program != nil)
assert(len(uniform.name) > 0)
// TODO: SS - Somehow verify that 'uniform.name' exists in the shader(s)?
when RENDER_BACKEND_OPENGL {
return opengl_set_shader_uniform_vector4(program, uniform)
}
return false
}
set_shader_uniform_color_pointer :: proc(program: ^Shader_Program, uniform: Uniform_Color_Pointer) -> bool {
if uniform.value == nil {
return false
}
return set_shader_uniform(program, Uniform_Color { uniform.name, uniform.value^ })
}
set_shader_uniform_color :: proc(program: ^Shader_Program, uniform: Uniform_Color) -> bool {
assert(program != nil)
assert(len(uniform.name) > 0)
// TODO: SS - Somehow verify that 'uniform.name' exists in the shader(s)?
switch &c in uniform.value {
case RGB_Color: {
rgb := color_to_f32(c)
u := Uniform_Vector3 {
name = uniform.name,
value = rgb,
}
when RENDER_BACKEND_OPENGL {
return opengl_set_shader_uniform_vector3(program, u)
}
}
case RGBA_Color: {
rgba := color_to_f32(c)
u := Uniform_Vector4 {
name = uniform.name,
value = rgba,
}
when RENDER_BACKEND_OPENGL {
return opengl_set_shader_uniform_vector4(program, u)
}
}
}
return false
}

View File

@@ -32,8 +32,12 @@ Texture_Format :: enum {
Depth32F_Stencil8, Depth32F_Stencil8,
} }
MAX_COLOR_TEXTURES_IN_RENDER_TARGET :: 32
Render_Target :: struct { Render_Target :: struct {
color_texture, depth_texture: ^Texture, color_textures: [dynamic]^Texture,
depth_texture: ^Texture,
backend: Render_Target_Backend, backend: Render_Target_Backend,
} }
@@ -119,10 +123,15 @@ create_texture_raw :: proc(renderer: ^Renderer, width, height: u16, format: Text
return t, true return t, true
} }
create_render_target :: proc(renderer: ^Renderer, color_texture, depth_texture: ^Texture) -> (Render_Target, bool) { create_render_target :: proc(renderer: ^Renderer, color_textures: []^Texture, depth_texture: ^Texture) -> (Render_Target, bool) {
rt: Render_Target rt: Render_Target
rt.color_texture = color_texture
rt.depth_texture = depth_texture rt.depth_texture = depth_texture
rt.color_textures = make([dynamic]^Texture, 0, MAX_COLOR_TEXTURES_IN_RENDER_TARGET)
_, err := append(&rt.color_textures, ..color_textures)
if err != .None {
return {}, false
}
when RENDER_BACKEND_OPENGL { when RENDER_BACKEND_OPENGL {
if !opengl_create_render_target(renderer, &rt) { if !opengl_create_render_target(renderer, &rt) {
@@ -133,6 +142,18 @@ create_render_target :: proc(renderer: ^Renderer, color_texture, depth_texture:
return rt, true return rt, true
} }
destroy_render_target :: proc(renderer: ^Renderer, render_target: ^Render_Target) {
assert(renderer != nil)
assert(render_target != nil)
assert(render_target.color_textures != nil)
delete(render_target.color_textures)
when RENDER_BACKEND_OPENGL {
opengl_destroy_render_target(renderer, render_target)
}
}
delete_texture :: proc(renderer: ^Renderer, texture: ^Texture) { delete_texture :: proc(renderer: ^Renderer, texture: ^Texture) {
when RENDER_BACKEND_OPENGL { when RENDER_BACKEND_OPENGL {