From 3b32097c565b5fc073861b65bc1ceb95de92b6b9 Mon Sep 17 00:00:00 2001 From: samstalhandske Date: Thu, 5 Feb 2026 00:17:33 +0100 Subject: [PATCH] Spheres/primitive built-in meshes, cull-modes, vec4 uniforms --- color.odin | 3 +- mesh.odin | 196 ++++++++++++++++++++++++++- pass.odin | 155 ++++++++++++++++++++- renderer.odin | 177 +++++------------------- renderer_backend_opengl_windows.odin | 45 +++++- shader.odin | 106 +++++++++++---- 6 files changed, 510 insertions(+), 172 deletions(-) diff --git a/color.odin b/color.odin index 7eb782f..fd35927 100644 --- a/color.odin +++ b/color.odin @@ -1,7 +1,8 @@ package renderer import "core:math/rand" -Color :: union { + +Color :: union #no_nil { RGB_Color, RGBA_Color, } diff --git a/mesh.odin b/mesh.odin index 15fd599..baf6ad1 100644 --- a/mesh.odin +++ b/mesh.odin @@ -1,5 +1,7 @@ package renderer +import "core:math" +import "core:log" Mesh :: struct { amount_of_indices: u32, backend: Mesh_Backend, @@ -14,7 +16,12 @@ Mesh_Layout :: struct { 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 if len(vertices) == 0 { @@ -39,4 +46,191 @@ create_mesh :: proc(renderer: ^Renderer, layout: []Mesh_Layout, vertices: []f32, } return m, true +} + +Primitive_Mesh_Type :: enum { + Cube, + Quad, + 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 .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 .Sphere: { + X_SEGMENTS :: 64 + Y_SEGMENTS :: 32 + + vertices := make([dynamic]f32, 0) + indices := make([dynamic]u32, 0) + + 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.. 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 { None, Alpha, @@ -117,10 +185,18 @@ Sort_Mode :: enum { 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( name: string, blend_mode: Blend_Mode, sort_mode: Sort_Mode, + cull_mode: Cull_Mode, output_rt: ^Render_Target, ) -> Pass { @@ -131,6 +207,7 @@ create_scene_pass :: proc( p.type = Scene_Pass { blend_mode = blend_mode, sort_mode = sort_mode, + cull_mode = cull_mode, draw_commands = make([dynamic]Draw_Command, 0, MAX_DRAW_COMMANDS_CAPACITY), @@ -194,7 +271,83 @@ add_command_to_pass :: proc(renderer: ^Renderer, pass: ^Pass, command: Draw_Comm assert(err == .None) } } - return true +} + +execute_pass :: proc(renderer: ^Renderer, pass: ^Pass, view_matrix, projection_matrix: linalg.Matrix4x4f32) { // TODO: SS - Move to 'pass.odin'. + // fmt.printfln("Executing pass '%v'.", pass.name) + + assert(renderer != nil) + assert(pass != nil) + + switch &t in &pass.type { + case Scene_Pass: { + 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 + set_clear_color(renderer, RGBA_Color { 0, 0, 0, 0 }) + + clear_screen(renderer, should_clear_color, should_clear_depth) + 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) + + 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) + } + + // Clear the pass' draw-commands. + clear(&t.draw_commands) + + // TODO: SS - "Deactivate" the pass? + + apply_polygon_mode(renderer, .Fill) + } + 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? + } + } } \ No newline at end of file diff --git a/renderer.odin b/renderer.odin index 2a8ec01..6e01087 100644 --- a/renderer.odin +++ b/renderer.odin @@ -23,6 +23,9 @@ Renderer :: struct { 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_mesh, default_sphere_mesh: Mesh, + fullscreen_vertex_shader, fullscreen_fragment_shader: Shader, fullscreen_shader_program: Shader_Program, fullscreen_mesh: Mesh, @@ -65,6 +68,20 @@ create :: proc(surface_ptr: rawptr) -> (^Renderer, bool) { set_vsync(renderer, true) + { // Create the default mesh-primitives. + // default_cube_mesh, default_quad_mesh, default_sphere_mesh: Mesh, + + if m, ok := create_mesh(renderer, .Cube); ok { + renderer.default_cube_mesh = m + } + if m, ok := create_mesh(renderer, .Quad); ok { + renderer.default_quad_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) @@ -135,13 +152,13 @@ set_vsync :: proc(renderer: ^Renderer, on: bool) { } } -@(private="file") set_clear_color :: proc(renderer: ^Renderer, color: Color) { +@(private) set_clear_color :: proc(renderer: ^Renderer, color: Color) { when RENDER_BACKEND_OPENGL { 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: bool, clear_depth: bool) { when RENDER_BACKEND_OPENGL { opengl_clear_screen(renderer, clear_color, clear_depth) } @@ -202,75 +219,6 @@ render_frame :: proc(renderer: ^Renderer, texture_to_present: ^Texture, clear_co } } -execute_pass :: proc(renderer: ^Renderer, pass: ^Pass, view_matrix, projection_matrix: linalg.Matrix4x4f32) { // TODO: SS - Move to 'pass.odin'. - // fmt.printfln("Executing pass '%v'.", pass.name) - - assert(renderer != nil) - assert(pass != nil) - - switch &t in &pass.type { - case Scene_Pass: { - 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 - set_clear_color(renderer, RGBA_Color { 0, 0, 0, 0 }) - - clear_screen(renderer, should_clear_color, should_clear_depth) - apply_depth(renderer, should_test_depth, should_write_depth) - - apply_blend_mode(renderer, t.blend_mode) - defer apply_blend_mode(renderer, .None) - - sort_draw_commands(renderer, &t) - - 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 - - activate_material(&dc.material, model_matrix, view_matrix, projection_matrix) - - draw_mesh(&dc.mesh) - } - - // Clear the pass' draw-commands. - clear(&t.draw_commands) - - // TODO: SS - "Deactivate" the pass? - - apply_polygon_mode(renderer, .Fill) - } - 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? - } - } -} - execute_post_processing_node :: proc(renderer: ^Renderer, node: ^Post_Processing_Node, view_matrix, projection_matrix: linalg.Matrix4x4f32) { assert(renderer != nil) assert(node != nil) @@ -286,46 +234,16 @@ execute_post_processing_node :: proc(renderer: ^Renderer, node: ^Post_Processing clear_screen(renderer, true, false) // fmt.printfln("TODO: SS - Execute post-processing node '%v' (VS: '%v', FS: '%v').", "NAME", node.program.vertex_shader.path, node.program.fragment_shader.path) + + set_shader_uniforms(node.program, node.uniforms) mat: Material mat.shader_program = node.program - fs_path := node.program.fragment_shader != nil ? node.program.fragment_shader.path : "nil" - vs_path := node.program.vertex_shader != nil ? node.program.vertex_shader.path : "nil" - - for u, i in node.uniforms { - switch &t in u { - case Uniform_Texture: { - if mat.texture_count > MATERIAL_MAX_TEXTURES { - continue - } - - mat.textures[mat.texture_count] = t.value - mat.texture_count += 1 - if !set_shader_uniform(node.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(node.program, t) { - fmt.printfln("Failed to set uniform-float '%s' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) - } - } - case Uniform_Matrix4f32: { - if !set_shader_uniform(node.program, t) { - fmt.printfln("Failed to set uniform-matrix4f32 '%s' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) - } - } - case Uniform_Vector3: { - if !set_shader_uniform(node.program, t) { - fmt.printfln("Failed to set uniform-vector3 '%s' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) - } - } - case Uniform_Color: { - if !set_shader_uniform(node.program, t) { - fmt.printfln("Failed to set uniform-color '%s' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) - } - } + 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 } } @@ -337,6 +255,10 @@ destroy :: proc(renderer: ^Renderer) { when RENDER_BACKEND_OPENGL { opengl_destroy(renderer) } + + // TODO: SS - Destroy 'default_cube_mesh' + // TODO: SS - Destroy 'default_quad_mesh' + // TODO: SS - Destroy 'default_sphere_mesh' assert(renderer != nil) free(renderer) @@ -354,40 +276,8 @@ destroy :: proc(renderer: ^Renderer) { p := material.shader_program activate_shader_program(p) - - fs_path := p.fragment_shader != nil ? p.fragment_shader.path : "nil" - vs_path := p.vertex_shader != nil ? p.vertex_shader.path : "nil" + set_shader_uniforms(material.shader_program, material.uniforms) - for u in material.uniforms { - switch &t in u { - case Uniform_Texture: { - if !set_shader_uniform(p, 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(p, t) { - fmt.printfln("Failed to set uniform-float '%s' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) - } - } - case Uniform_Matrix4f32: { - if !set_shader_uniform(p, t) { - fmt.printfln("Failed to set uniform-matrix4f32 '%s' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) - } - } - case Uniform_Vector3: { - if !set_shader_uniform(p, t) { - fmt.printfln("Failed to set uniform-vector3 '%s' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) - } - } - case Uniform_Color: { - if !set_shader_uniform(p, t) { - fmt.printfln("Failed to set uniform-color '%s' in program (vs: '%s', fs: '%s').", t.name, vs_path, fs_path) - } - } - } - } - when RENDER_BACKEND_OPENGL { opengl_activate_material(material, model_matrix, view_matrix, projection_matrix) } @@ -415,7 +305,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) { + // TODO: SS - Set the pass' 'renderer' variable here instead? switch pass.sort_mode { case .None: {} case .Back_To_Front: { diff --git a/renderer_backend_opengl_windows.odin b/renderer_backend_opengl_windows.odin index d51c41a..feb3c1c 100644 --- a/renderer_backend_opengl_windows.odin +++ b/renderer_backend_opengl_windows.odin @@ -517,6 +517,29 @@ 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) { if rt == nil { gl.BindFramebuffer(gl.FRAMEBUFFER, 0) @@ -629,7 +652,7 @@ when RENDER_BACKEND_OPENGL { return false } - gl.Uniform1f(loc, uniform.value^) + gl.Uniform1f(loc, uniform.value) return true } @@ -644,7 +667,7 @@ when RENDER_BACKEND_OPENGL { return false } - data := transmute([16]f32)(uniform.value^) + data := transmute([16]f32)(uniform.value) gl.UniformMatrix4fv(loc, 1, gl.FALSE, &data[0]) return true @@ -659,7 +682,23 @@ when RENDER_BACKEND_OPENGL { return false } - gl.Uniform3fv(loc, 1, &uniform.value[0]) + 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 } diff --git a/shader.odin b/shader.odin index 1a0833a..c608a4a 100644 --- a/shader.odin +++ b/shader.odin @@ -123,12 +123,39 @@ reload_shader_program :: proc(renderer: ^Renderer, p: ^Shader_Program) -> bool { 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_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_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 { @@ -143,13 +170,15 @@ set_shader_uniform_texture :: proc(program: ^Shader_Program, uniform: Uniform_Te return false } -set_shader_uniform_float :: proc(program: ^Shader_Program, uniform: Uniform_Float) -> bool { - assert(program != nil) - assert(len(uniform.name) > 0) - +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)? @@ -160,13 +189,15 @@ set_shader_uniform_float :: proc(program: ^Shader_Program, uniform: Uniform_Floa return false } -set_shader_uniform_matrix4f32 :: proc(program: ^Shader_Program, uniform: Uniform_Matrix4f32) -> bool { - assert(program != nil) - assert(len(uniform.name) > 0) - +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)? @@ -177,13 +208,16 @@ set_shader_uniform_matrix4f32 :: proc(program: ^Shader_Program, uniform: Uniform return false } -set_shader_uniform_vector3 :: proc(program: ^Shader_Program, uniform: Uniform_Vector3) -> bool { - assert(program != nil) - assert(len(uniform.name) > 0) - +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)? @@ -194,13 +228,35 @@ set_shader_uniform_vector3 :: proc(program: ^Shader_Program, uniform: Uniform_Ve return false } -set_shader_uniform_color :: proc(program: ^Shader_Program, uniform: Uniform_Color) -> bool { - assert(program != nil) - assert(len(uniform.name) > 0) - +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)? @@ -210,7 +266,7 @@ set_shader_uniform_color :: proc(program: ^Shader_Program, uniform: Uniform_Colo u := Uniform_Vector3 { name = uniform.name, - value = &rgb, + value = rgb, } when RENDER_BACKEND_OPENGL { @@ -220,16 +276,14 @@ set_shader_uniform_color :: proc(program: ^Shader_Program, uniform: Uniform_Colo case RGBA_Color: { rgba := color_to_f32(c) - // u := Uniform_Vector4 { - // name = uniform.name, - // value = &rgba, - // } + u := Uniform_Vector4 { + name = uniform.name, + value = rgba, + } - // TODO: SS - Set vector4. - // when RENDER_BACKEND_OPENGL { - // return opengl_set_shader_uniform_vector4(program, uniform) - // } - assert(false, "TODO: SS - Allow setting rgba in uniform.") + when RENDER_BACKEND_OPENGL { + return opengl_set_shader_uniform_vector4(program, u) + } } }