Files
renderer/shader.odin

292 lines
10 KiB
Odin

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
}