package renderer import "core:fmt" import os "core:os/os2" Shader :: struct { path: string, type: Shader_Type, backend: Shader_Backend, } Shader_Program :: struct { vertex_shader, fragment_shader: ^Shader, backend: Shader_Program_Backend, } Shader_Type :: enum { Vertex, Fragment, // .. } 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) if error != nil { return {}, false } defer delete(bytes) s: Shader s.type = type s.path = path when RENDER_BACKEND_OPENGL { shader_opengl, ok := opengl_create_shader(renderer, type, path, bytes) if !ok { return {}, false } s.backend = shader_opengl fmt.printfln("Backend id: %v", s.backend) } else { #assert(false) } return s, true } delete_shader :: proc(renderer: ^Renderer, shader: ^Shader) { assert(shader != nil) when RENDER_BACKEND_OPENGL { opengl_delete_shader(renderer, shader) } } create_shader_program :: proc(renderer: ^Renderer, vertex_shader, fragment_shader: ^Shader) -> (Shader_Program, bool) { assert(vertex_shader != nil) assert(fragment_shader != nil) p: Shader_Program p.vertex_shader = vertex_shader p.fragment_shader = fragment_shader when RENDER_BACKEND_OPENGL { program_opengl, ok := opengl_create_shader_program(renderer, vertex_shader, fragment_shader) if !ok { return {}, false } p.backend = program_opengl } else { #assert(false) } return p, true } delete_shader_program :: proc(renderer: ^Renderer, shader_program: ^Shader_Program) { assert(shader_program != nil) when RENDER_BACKEND_OPENGL { 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_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 { 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_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 }