Compare commits
12 Commits
c5d594c85b
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| c1002d5db2 | |||
| cb19295575 | |||
| 3b32097c56 | |||
| efa2c274f3 | |||
| 4394179a71 | |||
| b80c321e84 | |||
| 32a6d498b9 | |||
| 991d394098 | |||
| abe245294d | |||
| b6a32ebe7f | |||
| bd4b81d434 | |||
| 6b2d76bd8a |
130
camera.odin
130
camera.odin
@@ -6,27 +6,95 @@ import "core:math/linalg"
|
|||||||
Camera :: struct {
|
Camera :: struct {
|
||||||
position: [3]f32,
|
position: [3]f32,
|
||||||
forward, up, right: [3]f32,
|
forward, up, right: [3]f32,
|
||||||
|
|
||||||
fov_degrees: f32,
|
|
||||||
near, far: f32,
|
near, far: f32,
|
||||||
|
yaw, pitch, roll: f32,
|
||||||
|
|
||||||
|
type: Camera_Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
create_camera :: proc(renderer: ^Renderer, position: [3]f32, forward, up: [3]f32, fov_degrees, near, far: f32) -> Camera {
|
Camera_Type :: union {
|
||||||
|
Camera_Perspective,
|
||||||
|
Camera_Orthographic,
|
||||||
|
}
|
||||||
|
|
||||||
|
Camera_Perspective :: struct {
|
||||||
|
fov_degrees: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
Camera_Orthographic :: struct {
|
||||||
|
height: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private) create_camera_base :: proc(
|
||||||
|
renderer: ^Renderer,
|
||||||
|
position: [3]f32,
|
||||||
|
forward, up: [3]f32,
|
||||||
|
near, far: f32,
|
||||||
|
type: Camera_Type,
|
||||||
|
yaw: f32 = 0, pitch: f32 = 0, roll: f32 = 0
|
||||||
|
) -> Camera
|
||||||
|
{
|
||||||
c: Camera
|
c: Camera
|
||||||
c.position = position
|
c.position = position
|
||||||
c.forward = forward
|
c.forward = forward
|
||||||
c.up = up
|
c.up = up
|
||||||
c.fov_degrees = fov_degrees
|
|
||||||
c.near = near
|
c.near = near
|
||||||
c.far = far
|
c.far = far
|
||||||
|
c.type = type
|
||||||
|
c.yaw = yaw
|
||||||
|
c.pitch = pitch
|
||||||
|
c.roll = roll
|
||||||
|
|
||||||
assert(c.forward != {})
|
assert(c.forward != {})
|
||||||
assert(c.up != {})
|
assert(c.up != {})
|
||||||
|
assert(c.near > 0)
|
||||||
|
assert(c.far > 0)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
create_camera_perspective :: proc(
|
||||||
|
renderer: ^Renderer,
|
||||||
|
position: [3]f32,
|
||||||
|
forward, up: [3]f32,
|
||||||
|
near, far: f32,
|
||||||
|
fov_degrees: f32,
|
||||||
|
yaw: f32 = 0, pitch: f32 = 0, roll: f32 = 0
|
||||||
|
) -> Camera
|
||||||
|
{
|
||||||
assert(fov_degrees > 0)
|
assert(fov_degrees > 0)
|
||||||
assert(near > 0)
|
assert(near > 0)
|
||||||
assert(far > 0)
|
assert(far > 0)
|
||||||
|
|
||||||
return c
|
return create_camera_base(
|
||||||
|
renderer, position, forward, up,
|
||||||
|
near, far,
|
||||||
|
Camera_Perspective {
|
||||||
|
fov_degrees = fov_degrees,
|
||||||
|
},
|
||||||
|
yaw, pitch, roll
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
create_camera_orthographic :: proc(
|
||||||
|
renderer: ^Renderer,
|
||||||
|
position: [3]f32,
|
||||||
|
forward, up: [3]f32,
|
||||||
|
near, far: f32,
|
||||||
|
height: f32,
|
||||||
|
yaw: f32 = 0, pitch: f32 = 0, roll: f32 = 0
|
||||||
|
) -> Camera
|
||||||
|
{
|
||||||
|
assert(height > 0)
|
||||||
|
|
||||||
|
return create_camera_base(
|
||||||
|
renderer, position, forward, up,
|
||||||
|
near, far,
|
||||||
|
Camera_Orthographic {
|
||||||
|
height = height,
|
||||||
|
},
|
||||||
|
yaw, pitch, roll
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
get_camera_view_matrix :: proc(camera: ^Camera) -> (linalg.Matrix4f32, bool) {
|
get_camera_view_matrix :: proc(camera: ^Camera) -> (linalg.Matrix4f32, bool) {
|
||||||
@@ -48,23 +116,50 @@ get_camera_projection_matrix :: proc(renderer: ^Renderer, camera: ^Camera) -> (l
|
|||||||
}
|
}
|
||||||
|
|
||||||
projection_matrix := linalg.MATRIX4F32_IDENTITY
|
projection_matrix := linalg.MATRIX4F32_IDENTITY
|
||||||
projection_matrix *= linalg.matrix4_perspective(
|
|
||||||
linalg.to_radians(f32(camera.fov_degrees)),
|
switch t in camera.type {
|
||||||
get_aspect_ratio(renderer),
|
case Camera_Perspective: {
|
||||||
camera.near,
|
projection_matrix *= linalg.matrix4_perspective(
|
||||||
camera.far,
|
linalg.to_radians(f32(t.fov_degrees)),
|
||||||
flip_z_axis = true,
|
get_aspect_ratio(renderer, .Render),
|
||||||
)
|
camera.near,
|
||||||
|
camera.far,
|
||||||
|
flip_z_axis = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case Camera_Orthographic: {
|
||||||
|
half_h := t.height / 2
|
||||||
|
half_w := half_h * get_aspect_ratio(renderer, .Render)
|
||||||
|
|
||||||
|
projection_matrix *= linalg.matrix_ortho3d(
|
||||||
|
-half_w, half_w,
|
||||||
|
-half_h, half_h,
|
||||||
|
camera.near,
|
||||||
|
camera.far,
|
||||||
|
flip_z_axis = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return projection_matrix, true
|
return projection_matrix, true
|
||||||
}
|
}
|
||||||
|
|
||||||
rotate_camera :: proc(camera: ^Camera, yaw, pitch, roll: f32) {
|
rotate_camera :: proc(camera: ^Camera, yaw, pitch, roll: f32) {
|
||||||
pitch := clamp(pitch, -89.0, 89.0)
|
new_yaw := camera.yaw + yaw
|
||||||
|
new_pitch := camera.pitch + pitch
|
||||||
|
new_roll := camera.roll + roll
|
||||||
|
|
||||||
pitch_rad := linalg.to_radians(pitch)
|
set_camera_rotation(camera, new_yaw, new_pitch, new_roll)
|
||||||
yaw_rad := linalg.to_radians(yaw)
|
}
|
||||||
roll_rad := linalg.to_radians(roll)
|
|
||||||
|
set_camera_rotation :: proc(camera: ^Camera, yaw, pitch, roll: f32) {
|
||||||
|
camera.yaw = yaw
|
||||||
|
camera.pitch = pitch
|
||||||
|
camera.roll = roll
|
||||||
|
|
||||||
|
pitch_rad := linalg.to_radians(camera.pitch)
|
||||||
|
yaw_rad := linalg.to_radians(camera.yaw)
|
||||||
|
roll_rad := linalg.to_radians(camera.roll)
|
||||||
|
|
||||||
camera.forward = [3]f32{
|
camera.forward = [3]f32{
|
||||||
math.cos(pitch_rad) * math.cos(yaw_rad),
|
math.cos(pitch_rad) * math.cos(yaw_rad),
|
||||||
@@ -73,9 +168,9 @@ rotate_camera :: proc(camera: ^Camera, yaw, pitch, roll: f32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WORLD_UP :: [3]f32{0, 1, 0}
|
WORLD_UP :: [3]f32{0, 1, 0}
|
||||||
|
|
||||||
camera.right = linalg.normalize(linalg.cross(camera.forward, WORLD_UP))
|
camera.right = linalg.normalize(linalg.cross(camera.forward, WORLD_UP))
|
||||||
camera.up = linalg.normalize(linalg.cross(camera.right, camera.forward))
|
camera.up = linalg.normalize(linalg.cross(camera.right, camera.forward))
|
||||||
// TODO: SS - Support 'roll'.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set_active_camera :: proc(renderer: ^Renderer, camera: ^Camera) {
|
set_active_camera :: proc(renderer: ^Renderer, camera: ^Camera) {
|
||||||
@@ -83,5 +178,6 @@ set_active_camera :: proc(renderer: ^Renderer, camera: ^Camera) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get_active_camera :: proc(renderer: ^Renderer) -> ^Camera {
|
get_active_camera :: proc(renderer: ^Renderer) -> ^Camera {
|
||||||
|
assert(renderer != nil)
|
||||||
return renderer.active_camera
|
return renderer.active_camera
|
||||||
}
|
}
|
||||||
48
color.odin
Normal file
48
color.odin
Normal 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 }
|
||||||
|
}
|
||||||
@@ -1,7 +1,51 @@
|
|||||||
package renderer
|
package renderer
|
||||||
|
|
||||||
|
import "core:log"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
MATERIAL_MAX_TEXTURES :: 32
|
||||||
|
|
||||||
Material :: struct {
|
Material :: struct {
|
||||||
shader_program: Shader_Program,
|
// TODO: SS - 'Name'?
|
||||||
texture: Texture, // Diffuse, normal etc later.
|
shader_program: ^Shader_Program,
|
||||||
// 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, textures: []^Texture, uniforms: []Uniform, casts_shadow: bool) -> (Material, bool) {
|
||||||
|
m: Material
|
||||||
|
m.shader_program = program
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
235
mesh.odin
235
mesh.odin
@@ -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,
|
||||||
@@ -8,8 +10,18 @@ Mesh :: struct {
|
|||||||
Mesh_Handle :: distinct u32
|
Mesh_Handle :: distinct u32
|
||||||
Buffer_Handle :: distinct u32
|
Buffer_Handle :: distinct u32
|
||||||
|
|
||||||
|
Mesh_Layout :: struct {
|
||||||
|
name: string,
|
||||||
|
amount: u8,
|
||||||
|
type_size: u32,
|
||||||
|
}
|
||||||
|
|
||||||
create_mesh :: proc(renderer: ^Renderer, 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 {
|
||||||
@@ -23,7 +35,7 @@ create_mesh :: proc(renderer: ^Renderer, vertices: []f32, indices: []u32) -> (Me
|
|||||||
m.amount_of_indices = u32(len(indices))
|
m.amount_of_indices = u32(len(indices))
|
||||||
|
|
||||||
when RENDER_BACKEND_OPENGL {
|
when RENDER_BACKEND_OPENGL {
|
||||||
opengl_mesh, ok := opengl_create_mesh(renderer, vertices, indices)
|
opengl_mesh, ok := opengl_create_mesh(renderer, layout, vertices, indices)
|
||||||
if !ok {
|
if !ok {
|
||||||
return {}, false
|
return {}, false
|
||||||
}
|
}
|
||||||
@@ -35,3 +47,222 @@ create_mesh :: proc(renderer: ^Renderer, vertices: []f32, indices: []u32) -> (Me
|
|||||||
|
|
||||||
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
366
pass.odin
366
pass.odin
@@ -1,36 +1,374 @@
|
|||||||
package renderer
|
package renderer
|
||||||
|
|
||||||
|
import "core:math/linalg"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
import "core:log"
|
||||||
|
|
||||||
MAX_DRAW_COMMANDS_PER_PASS :: 1024
|
MAX_DRAW_COMMANDS_CAPACITY :: 4096
|
||||||
|
MAX_POST_PROCESS_NODES_PER_PASS :: 8
|
||||||
|
|
||||||
Pass :: struct {
|
Pass :: struct {
|
||||||
name: string,
|
name: string,
|
||||||
clear_color: RGB_Color,
|
type: Pass_Type,
|
||||||
|
|
||||||
should_test_depth: bool,
|
|
||||||
should_clear_depth: bool,
|
|
||||||
|
|
||||||
draw_commands: [MAX_DRAW_COMMANDS_PER_PASS]Draw_Command,
|
|
||||||
draw_command_count: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Pass_Type :: union {
|
||||||
|
Scene_Pass,
|
||||||
|
Post_Processing_Pass,
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene_Pass :: struct {
|
||||||
|
blend_mode: Blend_Mode,
|
||||||
|
sort_mode: Sort_Mode,
|
||||||
|
cull_mode: Cull_Mode,
|
||||||
|
|
||||||
|
draw_commands: [dynamic]Draw_Command, // Capacity is 'MAX_DRAW_COMMANDS_CAPACITY'.
|
||||||
|
|
||||||
|
output_rt: ^Render_Target, // Commands draw to this render-target.
|
||||||
|
}
|
||||||
|
|
||||||
|
Post_Processing_Pass :: struct {
|
||||||
|
post_processing_nodes: [dynamic]Post_Processing_Node, // These nodes are executed after all commands have been drawn onto the render-target.
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
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,
|
||||||
// TODO: SS - Add rotation.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
add_command_to_pass :: proc(pass: ^Pass, command: Draw_Command) -> bool {
|
create_draw_command :: proc(renderer: ^Renderer, mesh: Mesh, material: Material, uniforms: []Uniform, position, rotation, scale: [3]f32, loc := #caller_location) -> Draw_Command {
|
||||||
if pass.draw_command_count >= len(pass.draw_commands) {
|
dc: Draw_Command
|
||||||
return false
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
pass.draw_commands[pass.draw_command_count] = command
|
return dc
|
||||||
pass.draw_command_count += 1
|
}
|
||||||
|
|
||||||
|
Blend_Mode :: enum {
|
||||||
|
None,
|
||||||
|
Alpha,
|
||||||
|
Additive,
|
||||||
|
Multiply,
|
||||||
|
}
|
||||||
|
|
||||||
|
Blend_Factor :: enum {
|
||||||
|
Zero,
|
||||||
|
One,
|
||||||
|
Src_Color,
|
||||||
|
One_Minus_Src_Color,
|
||||||
|
Dst_Color,
|
||||||
|
One_Minus_Dst_Color,
|
||||||
|
Src_Alpha,
|
||||||
|
One_Minus_Src_Alpha,
|
||||||
|
Dst_Alpha,
|
||||||
|
One_Minus_Dst_Alpha,
|
||||||
|
// ..
|
||||||
|
}
|
||||||
|
|
||||||
|
Blend_Factors :: struct {
|
||||||
|
source, destination: Blend_Factor
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private) BLEND_FACTOR_TABLE :: [Blend_Mode]Blend_Factors {
|
||||||
|
.None = { .One, .Zero },
|
||||||
|
.Alpha = { .Src_Alpha, .One_Minus_Src_Alpha },
|
||||||
|
.Additive = { .Src_Alpha, .One },
|
||||||
|
.Multiply = { .Dst_Color, .Zero },
|
||||||
|
}
|
||||||
|
|
||||||
|
Sort_Mode :: enum {
|
||||||
|
None, // Draws the commands in the order they're placed in the 'draw_commmands' array.
|
||||||
|
Back_To_Front, // Sorts the commands in the 'draw_commmands' array (from back to front) 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(
|
||||||
|
name: string,
|
||||||
|
blend_mode: Blend_Mode,
|
||||||
|
sort_mode: Sort_Mode,
|
||||||
|
cull_mode: Cull_Mode,
|
||||||
|
output_rt: ^Render_Target,
|
||||||
|
) -> Pass
|
||||||
|
{
|
||||||
|
assert(len(name) > 0)
|
||||||
|
|
||||||
|
p: Pass
|
||||||
|
p.name = name
|
||||||
|
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),
|
||||||
|
|
||||||
|
output_rt = output_rt,
|
||||||
|
}
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
create_post_processing_pass :: proc(name: string, post_processing_nodes: []Post_Processing_Node) -> Pass {
|
||||||
|
assert(len(name) > 0)
|
||||||
|
|
||||||
|
p: Pass
|
||||||
|
p.name = name
|
||||||
|
|
||||||
|
ppp := Post_Processing_Pass {
|
||||||
|
post_processing_nodes = make([dynamic]Post_Processing_Node, 0, MAX_POST_PROCESS_NODES_PER_PASS)
|
||||||
|
}
|
||||||
|
|
||||||
|
append(&ppp.post_processing_nodes, ..post_processing_nodes)
|
||||||
|
|
||||||
|
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) {
|
||||||
|
assert(pass.draw_commands != nil)
|
||||||
|
delete(pass.draw_commands)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(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(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.renderer = renderer
|
||||||
|
n, err := append(&t.draw_commands, cmd)
|
||||||
|
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?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,46 +2,52 @@ package renderer
|
|||||||
|
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:log"
|
import "core:log"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
MAX_PASSES_IN_PIPELINE :: 8
|
PIPELINE_MAX_PASSES :: 16
|
||||||
|
|
||||||
Pipeline :: struct {
|
Pipeline :: struct {
|
||||||
passes: [MAX_PASSES_IN_PIPELINE]^Pass,
|
passes: [PIPELINE_MAX_PASSES]^Pass,
|
||||||
amount_of_passes: u8,
|
amount_of_passes: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
clear_pipeline :: proc(renderer: ^Renderer) {
|
clear_pipeline :: proc(renderer: ^Renderer) {
|
||||||
assert(renderer != nil)
|
assert(renderer != nil)
|
||||||
pipeline := &renderer.pipeline
|
p := &renderer.pipeline
|
||||||
|
|
||||||
pipeline.amount_of_passes = 0;
|
p.amount_of_passes = 0;
|
||||||
mem.set(&pipeline.passes[0], 0, MAX_PASSES_IN_PIPELINE * size_of(^Pass))
|
mem.set(&p.passes[0], 0, PIPELINE_MAX_PASSES * size_of(^Scene_Pass))
|
||||||
}
|
}
|
||||||
|
|
||||||
set_pipeline :: proc(renderer: ^Renderer, passes: []^Pass) {
|
set_pipeline :: proc(
|
||||||
|
renderer: ^Renderer,
|
||||||
|
passes: []^Pass,
|
||||||
|
){
|
||||||
|
assert(renderer != nil)
|
||||||
|
|
||||||
clear_pipeline(renderer)
|
clear_pipeline(renderer)
|
||||||
|
|
||||||
for p in passes {
|
for p in passes {
|
||||||
if !add_pass_to_pipeline(renderer, p) {
|
if !add_pass_to_pipeline(renderer, p) {
|
||||||
return
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private="file") add_pass_to_pipeline :: proc(renderer: ^Renderer, pass: ^Pass) -> bool {
|
add_pass_to_pipeline :: proc(renderer: ^Renderer, pass: ^Pass) -> bool {
|
||||||
assert(renderer != nil)
|
assert(renderer != nil)
|
||||||
assert(pass != nil)
|
assert(pass != nil)
|
||||||
|
|
||||||
pipeline := &renderer.pipeline
|
pipeline := &renderer.pipeline
|
||||||
|
|
||||||
if pipeline.amount_of_passes == MAX_PASSES_IN_PIPELINE {
|
assert(pipeline.amount_of_passes < PIPELINE_MAX_PASSES)
|
||||||
log.warnf("Failed to add pass '%v' to renderer's pipeline; hit max capacity (%v).", pass.name, MAX_PASSES_IN_PIPELINE)
|
// 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.passes[pipeline.amount_of_passes] = pass
|
pipeline.passes[pipeline.amount_of_passes] = pass
|
||||||
pipeline.amount_of_passes += 1
|
pipeline.amount_of_passes += 1
|
||||||
|
|
||||||
// log.infof("Successfully added pass '%v' to pipeline.", pass.name)
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
406
renderer.odin
406
renderer.odin
@@ -1,5 +1,7 @@
|
|||||||
package renderer
|
package renderer
|
||||||
|
|
||||||
|
import "core:slice"
|
||||||
|
import "core:sort"
|
||||||
import "core:math/linalg"
|
import "core:math/linalg"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:log"
|
import "core:log"
|
||||||
@@ -10,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
|
||||||
|
}
|
||||||
|
|
||||||
|
return f32(res.width) / f32(res.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
RGB_Color :: [3]u8
|
create :: proc(render_resolution: Resolution, surface_ptr: rawptr) -> (^Renderer, bool) {
|
||||||
RGBA_Color :: [4]u8
|
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 {
|
||||||
@@ -51,93 +94,239 @@ 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 = {
|
set_viewport :: proc {
|
||||||
x = x,
|
set_viewport_with_viewport,
|
||||||
y = y,
|
set_viewport_with_x_y_w_h,
|
||||||
width = width,
|
}
|
||||||
height = height,
|
|
||||||
}
|
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_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_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, view_matrix_ok := get_camera_view_matrix(renderer.active_camera)
|
camera_view_matrix, _ := get_camera_view_matrix(renderer.active_camera)
|
||||||
assert(view_matrix_ok)
|
camera_projection_matrix, _ := get_camera_projection_matrix(renderer, renderer.active_camera)
|
||||||
projection_matrix, projection_matrix_ok := get_camera_projection_matrix(renderer, renderer.active_camera)
|
|
||||||
assert(projection_matrix_ok)
|
|
||||||
|
|
||||||
pipeline := &renderer.pipeline
|
for i in 0 ..< renderer.pipeline.amount_of_passes {
|
||||||
for i in 0 ..< pipeline.amount_of_passes {
|
execute_pass(renderer, renderer.pipeline.passes[i], camera_view_matrix, camera_projection_matrix)
|
||||||
pass := pipeline.passes[i]
|
}
|
||||||
|
|
||||||
set_clear_color(renderer, pass.clear_color)
|
apply_polygon_mode(renderer, .Fill)
|
||||||
clear_screen(renderer, pass.should_clear_depth)
|
|
||||||
enable_depth_testing(renderer, pass.should_test_depth)
|
|
||||||
|
|
||||||
for i in 0..<pass.draw_command_count {
|
if texture_to_present != nil { // Present.
|
||||||
command := &pass.draw_commands[i]
|
// Bind to the screen.
|
||||||
|
bind_render_target(renderer, nil)
|
||||||
|
|
||||||
model_matrix := linalg.identity(linalg.Matrix4x4f32)
|
// Disable depth
|
||||||
|
apply_depth(renderer, false, false)
|
||||||
|
|
||||||
// Translate.
|
compute_letterboxed_viewport :: proc(backbuffer: Resolution, render: Resolution) -> Viewport {
|
||||||
translation := linalg.matrix4_translate(command.position)
|
bb_w, bb_h := expand_values(backbuffer)
|
||||||
|
r_w, r_h := expand_values(render)
|
||||||
|
|
||||||
// Rotate.
|
bb_aspect := f32(bb_w) / f32(bb_h)
|
||||||
rot_x := linalg.matrix4_rotate(linalg.to_radians(command.rotation.x), [3]f32 { 1, 0, 0 })
|
r_aspect := f32(r_w) / f32(r_h)
|
||||||
rot_y := linalg.matrix4_rotate(linalg.to_radians(command.rotation.y), [3]f32 { 0, 1, 0 })
|
|
||||||
rot_z := linalg.matrix4_rotate(linalg.to_radians(command.rotation.z), [3]f32 { 0, 0, 1 })
|
|
||||||
rotation := rot_z * rot_y * rot_x
|
|
||||||
|
|
||||||
// Scale.
|
if bb_aspect > r_aspect {
|
||||||
scale := linalg.matrix4_scale(command.scale)
|
// Pillarbox.
|
||||||
|
h := bb_h
|
||||||
|
w := u16(f32(h) * r_aspect)
|
||||||
|
x := (bb_w - w) / 2
|
||||||
|
|
||||||
model_matrix *= translation * rotation * scale
|
return { x, 0, { w, h } }
|
||||||
|
} else {
|
||||||
|
// Letterbox.
|
||||||
|
w := bb_w
|
||||||
|
h := u16(f32(w) / r_aspect)
|
||||||
|
y := (bb_h - h) / 2
|
||||||
|
|
||||||
activate_material(&command.material, model_matrix, view_matrix, projection_matrix)
|
return { 0, y, { w, h } }
|
||||||
|
}
|
||||||
draw_mesh(&command.mesh)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: SS - "Deactivate" the pass.
|
// 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)
|
||||||
|
|
||||||
// Clear the pass' draw-commands.
|
set_viewport(renderer, compute_letterboxed_viewport(renderer.backbuffer.resolution, renderer.render_resolution))
|
||||||
pass.draw_command_count = 0
|
set_clear_color(renderer, clear_color)
|
||||||
|
clear_screen(renderer, true, true, true)
|
||||||
|
|
||||||
|
// Create a temporary Material.
|
||||||
|
mat := Material {
|
||||||
|
shader_program = &renderer.fullscreen_shader_program,
|
||||||
|
textures = {
|
||||||
|
0 = texture_to_present,
|
||||||
|
},
|
||||||
|
texture_count = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_blend_mode(renderer, .Alpha)
|
||||||
|
|
||||||
|
// Activate.
|
||||||
|
activate_fullscreen_material(renderer, &mat)
|
||||||
|
defer deactivate_fullscreen_material(renderer)
|
||||||
|
|
||||||
|
// Draw.
|
||||||
|
draw_mesh(&renderer.fullscreen_mesh)
|
||||||
|
|
||||||
|
apply_blend_mode(renderer, .None)
|
||||||
}
|
}
|
||||||
|
|
||||||
when RENDER_BACKEND_OPENGL {
|
when RENDER_BACKEND_OPENGL {
|
||||||
opengl_swap_buffers(renderer)
|
opengl_swap_buffers(renderer)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
clear_screen(renderer, true)
|
execute_post_processing_node :: proc(renderer: ^Renderer, node: ^Post_Processing_Node, view_matrix, projection_matrix: linalg.Matrix4x4f32) {
|
||||||
|
assert(renderer != nil)
|
||||||
|
assert(node != nil)
|
||||||
|
assert(node.program != nil)
|
||||||
|
assert(node.output != nil)
|
||||||
|
|
||||||
|
assert(node.output != nil)
|
||||||
|
bind_render_target(renderer, node.output)
|
||||||
|
defer bind_render_target(renderer, nil)
|
||||||
|
|
||||||
|
apply_depth(renderer, false, 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
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activate_fullscreen_material(renderer, &mat)
|
||||||
|
draw_mesh(&renderer.fullscreen_mesh)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
@@ -146,9 +335,20 @@ 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)
|
||||||
}
|
}
|
||||||
@@ -162,8 +362,108 @@ destroy :: proc(renderer: ^Renderer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private) enable_depth_testing :: proc(renderer: ^Renderer, enable: bool) {
|
@(private) apply_depth :: proc(renderer: ^Renderer, test_depth, write_depth: bool) {
|
||||||
when RENDER_BACKEND_OPENGL {
|
when RENDER_BACKEND_OPENGL {
|
||||||
opengl_enable_depth_testing(renderer, true)
|
opengl_apply_depth(renderer, test_depth, write_depth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private) apply_blend_mode :: proc(renderer: ^Renderer, blend_mode: Blend_Mode) {
|
||||||
|
table := BLEND_FACTOR_TABLE
|
||||||
|
|
||||||
|
when RENDER_BACKEND_OPENGL {
|
||||||
|
opengl_set_blending(renderer, blend_mode != .None, table[blend_mode])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(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: {
|
||||||
|
slice.sort_by(
|
||||||
|
pass.draw_commands[:],
|
||||||
|
proc(i, j: Draw_Command) -> bool {
|
||||||
|
assert(i.renderer != nil)
|
||||||
|
assert(j.renderer != nil)
|
||||||
|
assert(i.renderer == j.renderer)
|
||||||
|
|
||||||
|
active_camera := get_active_camera(i.renderer)
|
||||||
|
if active_camera == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
i_dist := distance_to_camera(active_camera, i.position)
|
||||||
|
j_dist := distance_to_camera(active_camera, j.position)
|
||||||
|
|
||||||
|
return i_dist > j_dist
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case .Front_To_Back: {
|
||||||
|
slice.sort_by(
|
||||||
|
pass.draw_commands[:],
|
||||||
|
proc(i, j: Draw_Command) -> bool {
|
||||||
|
assert(i.renderer != nil)
|
||||||
|
assert(j.renderer != nil)
|
||||||
|
assert(i.renderer == j.renderer)
|
||||||
|
|
||||||
|
active_camera := get_active_camera(i.renderer)
|
||||||
|
if active_camera == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
i_dist := distance_to_camera(active_camera, i.position)
|
||||||
|
j_dist := distance_to_camera(active_camera, j.position)
|
||||||
|
|
||||||
|
return i_dist < j_dist
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
distance_to_camera :: proc(camera: ^Camera, position: [3]f32) -> f32 {
|
||||||
|
assert(camera != nil)
|
||||||
|
return linalg.distance(camera.position, position)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private) bind_render_target :: proc(renderer: ^Renderer, rt: ^Render_Target) {
|
||||||
|
when RENDER_BACKEND_OPENGL {
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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,36 +80,72 @@ 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)
|
||||||
i32(renderer.viewport.x),
|
|
||||||
i32(renderer.viewport.y),
|
|
||||||
i32(renderer.viewport.width),
|
|
||||||
i32(renderer.viewport.height)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
opengl_set_clear_color :: proc(renderer: ^Renderer, color: RGB_Color) {
|
opengl_set_vsync :: proc(renderer: ^Renderer, on: bool) -> bool {
|
||||||
gl.ClearColor(
|
if win.wglSwapIntervalEXT == nil {
|
||||||
f32(color.r) / f32(max(u8)),
|
fmt.printfln("'wglSwapIntervalEXT' is nil.")
|
||||||
f32(color.g) / f32(max(u8)),
|
return false
|
||||||
f32(color.b) / f32(max(u8)),
|
|
||||||
1.0
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
opengl_clear_screen :: proc(renderer: ^Renderer, clear_depth: bool) {
|
|
||||||
mask := u32(gl.COLOR_BUFFER_BIT)
|
|
||||||
if clear_depth {
|
|
||||||
gl.ClearDepth(1.0)
|
|
||||||
mask |= gl.DEPTH_BUFFER_BIT
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.Clear(mask)
|
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.y),
|
||||||
|
i32(renderer.viewport.res.width),
|
||||||
|
i32(renderer.viewport.res.height)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := u32(0)
|
||||||
|
if clear_color {
|
||||||
|
flags |= gl.COLOR_BUFFER_BIT
|
||||||
|
}
|
||||||
|
if clear_depth {
|
||||||
|
flags |= gl.DEPTH_BUFFER_BIT
|
||||||
|
}
|
||||||
|
gl.Clear(flags)
|
||||||
|
|
||||||
|
if scissor {
|
||||||
|
gl.Disable(gl.SCISSOR_TEST)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
opengl_swap_buffers :: proc(renderer: ^Renderer) {
|
opengl_swap_buffers :: proc(renderer: ^Renderer) {
|
||||||
@@ -127,6 +164,7 @@ when RENDER_BACKEND_OPENGL {
|
|||||||
Shader_Backend :: Shader_OpenGL
|
Shader_Backend :: Shader_OpenGL
|
||||||
Shader_Program_Backend :: Shader_Program_OpenGL
|
Shader_Program_Backend :: Shader_Program_OpenGL
|
||||||
Texture_Backend :: Texture_OpenGL
|
Texture_Backend :: Texture_OpenGL
|
||||||
|
Render_Target_Backend :: Render_Target_OpenGL
|
||||||
|
|
||||||
Mesh_OpenGL :: struct {
|
Mesh_OpenGL :: struct {
|
||||||
vbo, vao, ebo: u32,
|
vbo, vao, ebo: u32,
|
||||||
@@ -144,7 +182,11 @@ when RENDER_BACKEND_OPENGL {
|
|||||||
handle: u32,
|
handle: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
opengl_create_mesh :: proc(renderer: ^Renderer, vertices: []f32, indices: []u32) -> (Mesh_OpenGL, bool) {
|
Render_Target_OpenGL :: struct {
|
||||||
|
handle: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
opengl_create_mesh :: proc(renderer: ^Renderer, layout: []Mesh_Layout, vertices: []f32, indices: []u32) -> (Mesh_OpenGL, bool) {
|
||||||
// fmt.printfln("OPENGL: Creating mesh from vertices: %v.", vertices)
|
// fmt.printfln("OPENGL: Creating mesh from vertices: %v.", vertices)
|
||||||
|
|
||||||
m: Mesh_OpenGL
|
m: Mesh_OpenGL
|
||||||
@@ -161,14 +203,20 @@ when RENDER_BACKEND_OPENGL {
|
|||||||
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, m.ebo)
|
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, m.ebo)
|
||||||
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(indices) * size_of(u32), raw_data(indices), gl.STATIC_DRAW)
|
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(indices) * size_of(u32), raw_data(indices), gl.STATIC_DRAW)
|
||||||
|
|
||||||
gl.VertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 8 * size_of(f32), uintptr(0))
|
layout_total_stride := u32(0)
|
||||||
gl.EnableVertexAttribArray(0)
|
for l in layout {
|
||||||
|
layout_total_stride += u32(l.amount) * l.type_size;
|
||||||
|
}
|
||||||
|
|
||||||
gl.VertexAttribPointer(1, 3, gl.FLOAT, gl.FALSE, 8 * size_of(f32), uintptr(3 * size_of(f32)))
|
offset := u32(0)
|
||||||
gl.EnableVertexAttribArray(1)
|
for l, i in layout {
|
||||||
|
// NOTE: SS - Hardcoded to 'gl.FLOAT'
|
||||||
|
gl.VertexAttribPointer(u32(i), i32(l.amount), gl.FLOAT, gl.FALSE, i32(layout_total_stride), uintptr(offset))
|
||||||
|
gl.EnableVertexAttribArray(u32(i))
|
||||||
|
|
||||||
|
offset += u32(l.amount) * l.type_size
|
||||||
|
}
|
||||||
|
|
||||||
gl.VertexAttribPointer(2, 2, gl.FLOAT, gl.FALSE, 8 * size_of(f32), uintptr(6 * size_of(f32)))
|
|
||||||
gl.EnableVertexAttribArray(2)
|
|
||||||
}
|
}
|
||||||
gl.BindVertexArray(0)
|
gl.BindVertexArray(0)
|
||||||
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
|
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
|
||||||
@@ -176,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 {
|
||||||
@@ -201,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
|
||||||
}
|
}
|
||||||
@@ -227,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
|
||||||
}
|
}
|
||||||
@@ -241,23 +307,64 @@ 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")
|
||||||
model_matrix_as_f32_array := transmute([16]f32)(model_matrix)
|
if model_matrix_loc >= 0 {
|
||||||
gl.UniformMatrix4fv(model_matrix_loc, 1, false, &model_matrix_as_f32_array[0])
|
model_matrix_as_f32_array := transmute([16]f32)(model_matrix)
|
||||||
|
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")
|
||||||
view_matrix_as_f32_array := transmute([16]f32)(view_matrix)
|
if view_matrix_loc >= 0 {
|
||||||
gl.UniformMatrix4fv(view_matrix_loc, 1, false, &view_matrix_as_f32_array[0])
|
view_matrix_as_f32_array := transmute([16]f32)(view_matrix)
|
||||||
|
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")
|
||||||
projection_matrix_as_f32_array := transmute([16]f32)(projection_matrix)
|
if projection_matrix_loc >= 0 {
|
||||||
gl.UniformMatrix4fv(projection_matrix_loc, 1, false, &projection_matrix_as_f32_array[0])
|
projection_matrix_as_f32_array := transmute([16]f32)(projection_matrix)
|
||||||
|
gl.UniformMatrix4fv(projection_matrix_loc, 1, false, &projection_matrix_as_f32_array[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
uv_scale_loc := gl.GetUniformLocation(material.shader_program.backend.handle, "in_uv_scale")
|
||||||
|
if uv_scale_loc >= 0 {
|
||||||
|
gl.Uniform2fv(uv_scale_loc, 1, &material.uv_scale[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.DepthMask(gl.FALSE)
|
||||||
|
}
|
||||||
|
|
||||||
|
opengl_deactivate_fullscreen_material :: proc() { // TODO: SS - Maybe remove.
|
||||||
|
gl.DepthMask(gl.TRUE)
|
||||||
|
gl.Enable(gl.DEPTH_TEST)
|
||||||
}
|
}
|
||||||
|
|
||||||
opengl_draw_mesh :: proc(mesh: ^Mesh) {
|
opengl_draw_mesh :: proc(mesh: ^Mesh) {
|
||||||
@@ -266,66 +373,126 @@ when RENDER_BACKEND_OPENGL {
|
|||||||
gl.DrawElements(gl.TRIANGLES, i32(mesh.amount_of_indices), gl.UNSIGNED_INT, nil)
|
gl.DrawElements(gl.TRIANGLES, i32(mesh.amount_of_indices), gl.UNSIGNED_INT, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opengl_texture_format_from_texture :: proc(t: ^Texture) -> (u32, u32, u32) {
|
||||||
|
internal, format, type: u32
|
||||||
|
|
||||||
|
switch t.format {
|
||||||
|
case .R8: {
|
||||||
|
internal = gl.R8
|
||||||
|
format = gl.RED
|
||||||
|
type = gl.UNSIGNED_BYTE
|
||||||
|
}
|
||||||
|
case .R16F: {
|
||||||
|
internal = gl.R16F
|
||||||
|
format = gl.RED
|
||||||
|
type = gl.HALF_FLOAT
|
||||||
|
}
|
||||||
|
case .R32F: {
|
||||||
|
internal = gl.R32F
|
||||||
|
format = gl.RED
|
||||||
|
type = gl.FLOAT
|
||||||
|
}
|
||||||
|
case .RGBA8: {
|
||||||
|
internal = gl.RGBA8
|
||||||
|
|
||||||
|
if t.channels == 3 {
|
||||||
|
format = gl.RGB
|
||||||
|
} else {
|
||||||
|
format = gl.RGBA
|
||||||
|
}
|
||||||
|
|
||||||
|
type = gl.UNSIGNED_BYTE
|
||||||
|
}
|
||||||
|
case .RGBA16F: {
|
||||||
|
internal = gl.RGBA16F
|
||||||
|
format = gl.RGBA
|
||||||
|
type = gl.HALF_FLOAT
|
||||||
|
}
|
||||||
|
case .R11G11B10F: {
|
||||||
|
internal = gl.R11F_G11F_B10F
|
||||||
|
format = gl.RGB
|
||||||
|
type = gl.UNSIGNED_INT_10F_11F_11F_REV
|
||||||
|
}
|
||||||
|
case .Depth16: {
|
||||||
|
internal = gl.DEPTH_COMPONENT16
|
||||||
|
format = gl.DEPTH_COMPONENT
|
||||||
|
type = gl.UNSIGNED_SHORT
|
||||||
|
}
|
||||||
|
case .Depth24: {
|
||||||
|
internal = gl.DEPTH_COMPONENT24
|
||||||
|
format = gl.DEPTH_COMPONENT
|
||||||
|
type = gl.UNSIGNED_INT
|
||||||
|
}
|
||||||
|
case .Depth32F: {
|
||||||
|
internal = gl.DEPTH_COMPONENT32F
|
||||||
|
format = gl.DEPTH_COMPONENT
|
||||||
|
type = gl.FLOAT
|
||||||
|
}
|
||||||
|
case .Depth24_Stencil8: {
|
||||||
|
internal = gl.DEPTH24_STENCIL8
|
||||||
|
format = gl.DEPTH_STENCIL
|
||||||
|
type = gl.UNSIGNED_INT_24_8
|
||||||
|
}
|
||||||
|
case .Depth32F_Stencil8: {
|
||||||
|
internal = gl.DEPTH32F_STENCIL8
|
||||||
|
format = gl.DEPTH_STENCIL
|
||||||
|
type = gl.FLOAT_32_UNSIGNED_INT_24_8_REV
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return internal, format, type
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
opengl_load_texture :: proc(renderer: ^Renderer, t: ^Texture, pixels: rawptr) -> bool {
|
opengl_load_texture :: proc(renderer: ^Renderer, t: ^Texture, pixels: rawptr) -> bool {
|
||||||
handle: u32
|
handle: u32
|
||||||
gl.GenTextures(1, &handle)
|
gl.GenTextures(1, &handle)
|
||||||
gl.BindTexture(gl.TEXTURE_2D, handle)
|
gl.BindTexture(gl.TEXTURE_2D, handle)
|
||||||
|
|
||||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
|
switch t.filter_mode {
|
||||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
|
case .Nearest:
|
||||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
|
||||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
|
||||||
|
case .Linear:
|
||||||
type := gl.UNSIGNED_BYTE
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
|
||||||
switch t.depth {
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||||
case 8: type = gl.UNSIGNED_BYTE
|
case .Nearest_MipMap:
|
||||||
case 16: type = gl.UNSIGNED_SHORT
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_NEAREST)
|
||||||
case 32: type = gl.UNSIGNED_INT
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
|
||||||
|
case .Linear_MipMap:
|
||||||
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
|
||||||
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||||
}
|
}
|
||||||
|
|
||||||
format := gl.RGBA
|
switch t.wrap_mode {
|
||||||
switch t.channels {
|
case .Clamp:
|
||||||
case 1: format = gl.RED
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP)
|
||||||
case 2: format = gl.RG
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP)
|
||||||
case 3: format = gl.RGB
|
case .Repeat:
|
||||||
case 4: format = gl.RGBA
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
|
||||||
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
|
||||||
|
case .Mirror:
|
||||||
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT)
|
||||||
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal := gl.RGBA8
|
internal_format, format, type := opengl_texture_format_from_texture(t)
|
||||||
|
|
||||||
if t.depth == 8 {
|
|
||||||
switch t.channels {
|
|
||||||
case 1: internal = gl.R8
|
|
||||||
case 2: internal = gl.RG8
|
|
||||||
case 3: internal = gl.RGB8
|
|
||||||
case 4: internal = gl.RGBA8
|
|
||||||
}
|
|
||||||
} else if t.depth == 16 {
|
|
||||||
switch t.channels {
|
|
||||||
case 1: internal = gl.R16
|
|
||||||
case 2: internal = gl.RG16
|
|
||||||
case 3: internal = gl.RGB16
|
|
||||||
case 4: internal = gl.RGBA16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// bytes.reverse(bytes.buffer_to_bytes(img.pixels))
|
|
||||||
|
|
||||||
gl.TexImage2D(
|
gl.TexImage2D(
|
||||||
target = gl.TEXTURE_2D,
|
gl.TEXTURE_2D,
|
||||||
level = 0,
|
0,
|
||||||
internalformat = i32(internal),
|
i32(internal_format),
|
||||||
width = i32(t.width),
|
i32(t.width),
|
||||||
height = i32(t.height),
|
i32(t.height),
|
||||||
border = 0,
|
0,
|
||||||
format = u32(format),
|
u32(format),
|
||||||
type = u32(type),
|
u32(type),
|
||||||
pixels = pixels,
|
pixels,
|
||||||
// pixels = &pixels[0]
|
|
||||||
// pixels = &bytes.reverse(bytes.buffer_to_bytes(&img.pixels))[0]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
gl.GenerateMipmap(gl.TEXTURE_2D)
|
if !t.is_depth {
|
||||||
|
gl.GenerateMipmap(gl.TEXTURE_2D)
|
||||||
|
}
|
||||||
|
|
||||||
t.backend = Texture_OpenGL { handle = handle }
|
t.backend = Texture_OpenGL { handle = handle }
|
||||||
return true
|
return true
|
||||||
@@ -335,12 +502,255 @@ when RENDER_BACKEND_OPENGL {
|
|||||||
gl.DeleteTextures(1, &t.backend.handle)
|
gl.DeleteTextures(1, &t.backend.handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
opengl_enable_depth_testing :: proc(renderer: ^Renderer, enable: bool) {
|
opengl_apply_depth :: proc(renderer: ^Renderer, test_depth, write_depth: bool) {
|
||||||
if enable {
|
if test_depth {
|
||||||
gl.Enable(gl.DEPTH_TEST)
|
gl.Enable(gl.DEPTH_TEST)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
gl.Disable(gl.DEPTH_TEST)
|
gl.Disable(gl.DEPTH_TEST)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gl.DepthMask(write_depth)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file") OPENGL_BLEND_FACTOR_U32_TABLE :: [Blend_Factor]u32 {
|
||||||
|
.Zero = gl.ZERO,
|
||||||
|
.One = gl.ONE,
|
||||||
|
// Color.
|
||||||
|
.Src_Color = gl.SRC_COLOR,
|
||||||
|
.One_Minus_Src_Color = gl.ONE_MINUS_SRC_COLOR,
|
||||||
|
.Dst_Color = gl.DST_COLOR,
|
||||||
|
.One_Minus_Dst_Color = gl.ONE_MINUS_DST_COLOR,
|
||||||
|
// Alpha.
|
||||||
|
.Src_Alpha = gl.SRC_ALPHA,
|
||||||
|
.One_Minus_Src_Alpha = gl.ONE_MINUS_SRC_ALPHA,
|
||||||
|
.Dst_Alpha = gl.DST_ALPHA,
|
||||||
|
.One_Minus_Dst_Alpha = gl.ONE_MINUS_DST_ALPHA,
|
||||||
|
}
|
||||||
|
|
||||||
|
opengl_set_blending :: proc(renderer: ^Renderer, enable: bool, factors: Blend_Factors) {
|
||||||
|
if enable {
|
||||||
|
gl.Enable(gl.BLEND)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gl.Disable(gl.BLEND)
|
||||||
|
// Not sure that I want to return here.. Pretty nice that the blendfunc gets reset correctly even if we're disabling blending.
|
||||||
|
}
|
||||||
|
|
||||||
|
table := OPENGL_BLEND_FACTOR_U32_TABLE
|
||||||
|
|
||||||
|
gl.BlendFunc(
|
||||||
|
sfactor = table[factors.source],
|
||||||
|
dfactor = table[factors.destination],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
opengl_apply_renderer_viewport(renderer)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
i32(texture.width),
|
||||||
|
i32(texture.height),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
opengl_create_render_target :: proc(renderer: ^Renderer, rt: ^Render_Target) -> bool {
|
||||||
|
fb: u32
|
||||||
|
gl.GenFramebuffers(1, &fb)
|
||||||
|
gl.BindFramebuffer(gl.FRAMEBUFFER, fb)
|
||||||
|
|
||||||
|
for texture, i in rt.color_textures {
|
||||||
|
gl.FramebufferTexture2D(
|
||||||
|
gl.FRAMEBUFFER,
|
||||||
|
gl.COLOR_ATTACHMENT0 + u32(i),
|
||||||
|
gl.TEXTURE_2D,
|
||||||
|
texture.backend.handle,
|
||||||
|
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 {
|
||||||
|
gl.FramebufferTexture2D(
|
||||||
|
gl.FRAMEBUFFER,
|
||||||
|
gl.DEPTH_ATTACHMENT,
|
||||||
|
gl.TEXTURE_2D,
|
||||||
|
rt.depth_texture.backend.handle,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if gl.CheckFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE {
|
||||||
|
fmt.printfln("Framebuffer status: %v", gl.CheckFramebufferStatus(gl.FRAMEBUFFER))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
rt.backend = Render_Target_OpenGL {
|
||||||
|
handle = fb
|
||||||
|
}
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
240
shader.odin
240
shader.odin
@@ -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
|
||||||
|
}
|
||||||
149
texture.odin
149
texture.odin
@@ -6,44 +6,155 @@ import "vendor:stb/image"
|
|||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
|
||||||
Texture :: struct {
|
Texture :: struct {
|
||||||
width, height, channels, depth: u32,
|
width, height, depth, channels: u16,
|
||||||
|
|
||||||
|
format: Texture_Format,
|
||||||
|
filter_mode: Texture_Filter_Mode,
|
||||||
|
wrap_mode: Texture_Wrap_Mode,
|
||||||
|
is_depth: bool,
|
||||||
|
|
||||||
backend: Texture_Backend,
|
backend: Texture_Backend,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Texture_Format :: enum {
|
||||||
|
RGBA8,
|
||||||
|
RGBA16F,
|
||||||
|
R11G11B10F,
|
||||||
|
|
||||||
|
R8,
|
||||||
|
R16F,
|
||||||
|
R32F,
|
||||||
|
|
||||||
|
Depth16,
|
||||||
|
Depth24,
|
||||||
|
Depth32F,
|
||||||
|
Depth24_Stencil8,
|
||||||
|
Depth32F_Stencil8,
|
||||||
|
}
|
||||||
|
|
||||||
|
MAX_COLOR_TEXTURES_IN_RENDER_TARGET :: 32
|
||||||
|
|
||||||
|
Render_Target :: struct {
|
||||||
|
color_textures: [dynamic]^Texture,
|
||||||
|
depth_texture: ^Texture,
|
||||||
|
|
||||||
|
backend: Render_Target_Backend,
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture_Filter_Mode :: enum {
|
||||||
|
Nearest,
|
||||||
|
Linear,
|
||||||
|
Nearest_MipMap,
|
||||||
|
Linear_MipMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture_Wrap_Mode :: enum {
|
||||||
|
Clamp,
|
||||||
|
Repeat,
|
||||||
|
Mirror,
|
||||||
|
}
|
||||||
|
|
||||||
create_texture :: proc {
|
create_texture :: proc {
|
||||||
create_texture_from_path,
|
create_texture_from_path,
|
||||||
|
create_texture_raw,
|
||||||
}
|
}
|
||||||
|
|
||||||
create_texture_from_path :: proc(renderer: ^Renderer, path: string) -> (Texture, bool) {
|
create_texture_from_path :: proc(renderer: ^Renderer, path: string, filter_mode := Texture_Filter_Mode.Linear, wrap_mode := Texture_Wrap_Mode.Repeat) -> (Texture, bool) {
|
||||||
width, height, depth, channels: c.int
|
width, height, channels, depth: c.int
|
||||||
|
|
||||||
path_cstr := strings.clone_to_cstring(path)
|
path_cstr := strings.clone_to_cstring(path)
|
||||||
defer delete(path_cstr)
|
defer delete(path_cstr)
|
||||||
image.set_flip_vertically_on_load(1) // NOTE: SS - This should not necessarily happen on all graphics-apis.
|
image.set_flip_vertically_on_load(1)
|
||||||
data := image.load(path_cstr, &width, &height, &channels, desired_channels = 0)
|
data := image.load(path_cstr, &width, &height, &channels, desired_channels = 0)
|
||||||
if data == nil {
|
if data == nil {
|
||||||
return {}, false
|
return {}, false
|
||||||
}
|
}
|
||||||
depth = 8
|
depth = 8
|
||||||
|
|
||||||
t: Texture
|
t: Texture
|
||||||
t.width = u32(width)
|
t.width = u16(width)
|
||||||
t.height = u32(height)
|
t.height = u16(height)
|
||||||
t.channels = u32(channels)
|
t.filter_mode = filter_mode
|
||||||
t.depth = u32(depth)
|
t.wrap_mode = wrap_mode
|
||||||
|
t.depth = u16(depth)
|
||||||
|
t.channels = u16(channels)
|
||||||
|
|
||||||
|
switch channels {
|
||||||
|
case 1: {
|
||||||
|
t.format = .R8
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
t.format = .RGBA8
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
t.format = .RGBA8
|
||||||
|
}
|
||||||
|
case: {
|
||||||
|
t.format = .RGBA8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when RENDER_BACKEND_OPENGL {
|
||||||
|
if !opengl_load_texture(renderer, &t, data) {
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return t, true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
create_texture_raw :: proc(renderer: ^Renderer, width, height: u16, format: Texture_Format, filter_mode: Texture_Filter_Mode, wrap_mode: Texture_Wrap_Mode, is_depth: bool = false) -> (Texture, bool) { t: Texture
|
||||||
|
t.width = width
|
||||||
|
t.height = height
|
||||||
|
t.format = format
|
||||||
|
t.filter_mode = filter_mode
|
||||||
|
t.wrap_mode = wrap_mode
|
||||||
|
t.is_depth = is_depth
|
||||||
|
|
||||||
when RENDER_BACKEND_OPENGL {
|
when RENDER_BACKEND_OPENGL {
|
||||||
if !opengl_load_texture(renderer, &t, data) {
|
if !opengl_load_texture(renderer, &t, nil) {
|
||||||
return {}, false
|
return {}, false
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
#assert(false)
|
#assert(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
return t, true
|
return t, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
create_render_target :: proc(renderer: ^Renderer, color_textures: []^Texture, depth_texture: ^Texture) -> (Render_Target, bool) {
|
||||||
|
rt: Render_Target
|
||||||
|
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 {
|
||||||
|
if !opengl_create_render_target(renderer, &rt) {
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
opengl_delete_texture(renderer, texture)
|
opengl_delete_texture(renderer, texture)
|
||||||
|
|||||||
Reference in New Issue
Block a user