Compare commits
2 Commits
c5d594c85b
...
bd4b81d434
| Author | SHA1 | Date | |
|---|---|---|---|
| bd4b81d434 | |||
| 6b2d76bd8a |
118
camera.odin
118
camera.odin
@@ -6,27 +6,95 @@ import "core:math/linalg"
|
||||
Camera :: struct {
|
||||
position: [3]f32,
|
||||
forward, up, right: [3]f32,
|
||||
|
||||
fov_degrees: 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.position = position
|
||||
c.forward = forward
|
||||
c.up = up
|
||||
c.fov_degrees = fov_degrees
|
||||
c.near = near
|
||||
c.far = far
|
||||
c.type = type
|
||||
c.yaw = yaw
|
||||
c.pitch = pitch
|
||||
c.roll = roll
|
||||
|
||||
assert(c.forward != {})
|
||||
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(near > 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) {
|
||||
@@ -48,23 +116,50 @@ get_camera_projection_matrix :: proc(renderer: ^Renderer, camera: ^Camera) -> (l
|
||||
}
|
||||
|
||||
projection_matrix := linalg.MATRIX4F32_IDENTITY
|
||||
|
||||
switch t in camera.type {
|
||||
case Camera_Perspective: {
|
||||
projection_matrix *= linalg.matrix4_perspective(
|
||||
linalg.to_radians(f32(camera.fov_degrees)),
|
||||
linalg.to_radians(f32(t.fov_degrees)),
|
||||
get_aspect_ratio(renderer),
|
||||
camera.near,
|
||||
camera.far,
|
||||
flip_z_axis = true,
|
||||
)
|
||||
}
|
||||
case Camera_Orthographic: {
|
||||
half_h := t.height / 2
|
||||
half_w := half_h * get_aspect_ratio(renderer)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
yaw_rad := linalg.to_radians(yaw)
|
||||
roll_rad := linalg.to_radians(roll)
|
||||
set_camera_rotation(camera, new_yaw, new_pitch, new_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{
|
||||
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}
|
||||
|
||||
camera.right = linalg.normalize(linalg.cross(camera.forward, WORLD_UP))
|
||||
camera.up = linalg.normalize(linalg.cross(camera.right, camera.forward))
|
||||
// TODO: SS - Support 'roll'.
|
||||
}
|
||||
|
||||
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 {
|
||||
assert(renderer != nil)
|
||||
return renderer.active_camera
|
||||
}
|
||||
@@ -1,7 +1,16 @@
|
||||
package renderer
|
||||
|
||||
Material :: struct {
|
||||
shader_program: Shader_Program,
|
||||
texture: Texture, // Diffuse, normal etc later.
|
||||
// 'Name'?
|
||||
shader_program: ^Shader_Program,
|
||||
texture: ^Texture, // Diffuse, normal etc later.
|
||||
// uniforms, textures, etc.
|
||||
}
|
||||
|
||||
create_material :: proc(program: ^Shader_Program, texture0: ^Texture) -> (Material, bool) {
|
||||
m: Material
|
||||
m.shader_program = program
|
||||
m.texture = texture0
|
||||
|
||||
return m, true
|
||||
}
|
||||
@@ -8,8 +8,13 @@ Mesh :: struct {
|
||||
Mesh_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(renderer: ^Renderer, layout: []Mesh_Layout, vertices: []f32, indices: []u32) -> (Mesh, bool) { // TODO: SS - Should probably return a Mesh_Handle.
|
||||
mesh: Mesh
|
||||
|
||||
if len(vertices) == 0 {
|
||||
@@ -23,7 +28,7 @@ create_mesh :: proc(renderer: ^Renderer, vertices: []f32, indices: []u32) -> (Me
|
||||
m.amount_of_indices = u32(len(indices))
|
||||
|
||||
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 {
|
||||
return {}, false
|
||||
}
|
||||
|
||||
146
pass.odin
146
pass.odin
@@ -2,35 +2,155 @@ package renderer
|
||||
|
||||
import "core:fmt"
|
||||
|
||||
MAX_DRAW_COMMANDS_PER_PASS :: 1024
|
||||
MAX_DRAW_COMMANDS_CAPACITY :: 4096
|
||||
|
||||
Pass :: struct {
|
||||
PASS_MAX_INPUT_TEXTURES :: 8
|
||||
|
||||
Scene_Pass :: struct {
|
||||
name: string,
|
||||
clear_color: RGB_Color,
|
||||
|
||||
should_test_depth: bool,
|
||||
should_clear_depth: bool,
|
||||
input_textures: [PASS_MAX_INPUT_TEXTURES]^Texture, // TODO: SS - Make this a map?
|
||||
input_texture_count: u8,
|
||||
|
||||
draw_commands: [MAX_DRAW_COMMANDS_PER_PASS]Draw_Command,
|
||||
draw_command_count: u32,
|
||||
output_rt: ^Render_Target,
|
||||
|
||||
draw_commands: [dynamic]Draw_Command, // Capacity is 'MAX_DRAW_COMMANDS_CAPACITY'.
|
||||
|
||||
clear_color: Maybe(RGB_Color),
|
||||
blend_mode: Blend_Mode,
|
||||
sort_mode: Sort_Mode,
|
||||
}
|
||||
|
||||
Post_Processing_Pass :: struct {
|
||||
name: string,
|
||||
|
||||
input_texture: ^Texture, // TODO: SS - Make this an array of texture-pointers, or maybe a map.
|
||||
input_depth_texture: ^Texture,
|
||||
output_rt: ^Render_Target,
|
||||
|
||||
shader_program: Shader_Program,
|
||||
}
|
||||
|
||||
Draw_Command :: struct {
|
||||
renderer: ^Renderer, // Needed for sorting.
|
||||
|
||||
mesh: Mesh,
|
||||
material: Material,
|
||||
position: [3]f32,
|
||||
rotation: [3]f32,
|
||||
scale: [3]f32,
|
||||
// TODO: SS - Add rotation.
|
||||
}
|
||||
|
||||
add_command_to_pass :: proc(pass: ^Pass, command: Draw_Command) -> bool {
|
||||
if pass.draw_command_count >= len(pass.draw_commands) {
|
||||
return false
|
||||
Blend_Mode :: enum {
|
||||
None,
|
||||
Alpha,
|
||||
Additive,
|
||||
Multiply,
|
||||
}
|
||||
|
||||
pass.draw_commands[pass.draw_command_count] = command
|
||||
pass.draw_command_count += 1
|
||||
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.
|
||||
}
|
||||
|
||||
create_scene_pass :: proc(
|
||||
name: string,
|
||||
input_textures: []^Texture,
|
||||
output_rt: ^Render_Target,
|
||||
clear_color: Maybe(RGB_Color),
|
||||
blend_mode: Blend_Mode,
|
||||
sort_mode: Sort_Mode,
|
||||
) -> Scene_Pass
|
||||
{
|
||||
// if input_depth_texture == nil {
|
||||
// if test_depth || clear_depth || write_depth {
|
||||
// assert(false)
|
||||
// }
|
||||
// }
|
||||
|
||||
p := Scene_Pass {
|
||||
name = name,
|
||||
|
||||
output_rt = output_rt,
|
||||
|
||||
draw_commands = make([dynamic]Draw_Command, 0, MAX_DRAW_COMMANDS_CAPACITY),
|
||||
|
||||
clear_color = clear_color,
|
||||
blend_mode = blend_mode,
|
||||
sort_mode = sort_mode,
|
||||
}
|
||||
|
||||
for texture, i in input_textures {
|
||||
if p.input_texture_count >= PASS_MAX_INPUT_TEXTURES {
|
||||
fmt.printfln("Hit max capacity of textures per pass.")
|
||||
break
|
||||
}
|
||||
|
||||
p.input_textures[p.input_texture_count] = texture
|
||||
p.input_texture_count += 1
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
create_post_processing_pass :: proc(
|
||||
name: string,
|
||||
input_texture, input_depth_texture: ^Texture,
|
||||
output_rt: ^Render_Target,
|
||||
shader_program: Shader_Program,
|
||||
) -> Post_Processing_Pass
|
||||
{
|
||||
return {
|
||||
name = name,
|
||||
input_texture = input_texture,
|
||||
input_depth_texture = input_depth_texture,
|
||||
output_rt = output_rt,
|
||||
shader_program = shader_program,
|
||||
}
|
||||
}
|
||||
|
||||
delete_pass :: proc {
|
||||
delete_scene_pass,
|
||||
}
|
||||
|
||||
@(private) delete_scene_pass :: proc(pass: ^Scene_Pass) {
|
||||
delete(pass.draw_commands)
|
||||
}
|
||||
|
||||
add_command_to_pass :: proc(renderer: ^Renderer, pass: ^Scene_Pass, command: Draw_Command) -> bool {
|
||||
assert(renderer != nil)
|
||||
assert(pass != nil)
|
||||
|
||||
cmd := command
|
||||
cmd.renderer = renderer
|
||||
n, err := append(&pass.draw_commands, cmd)
|
||||
assert(err == .None)
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -3,45 +3,92 @@ package renderer
|
||||
import "core:mem"
|
||||
import "core:log"
|
||||
|
||||
MAX_PASSES_IN_PIPELINE :: 8
|
||||
PIPELINE_MAX_SCENE_PASSES :: 8
|
||||
PIPELINE_MAX_POST_PROCESSING_PASSES :: 8
|
||||
|
||||
Pipeline :: struct {
|
||||
passes: [MAX_PASSES_IN_PIPELINE]^Pass,
|
||||
amount_of_passes: u8,
|
||||
scene_passes: [PIPELINE_MAX_SCENE_PASSES]^Scene_Pass,
|
||||
amount_of_scene_passes: u8,
|
||||
|
||||
post_processing_passes: [PIPELINE_MAX_POST_PROCESSING_PASSES]^Post_Processing_Pass,
|
||||
amount_of_post_processing_passes: u8,
|
||||
|
||||
fullscreen_material: ^Material,
|
||||
fullscreen_mesh: ^Mesh,
|
||||
}
|
||||
|
||||
clear_pipeline :: proc(renderer: ^Renderer) {
|
||||
assert(renderer != nil)
|
||||
pipeline := &renderer.pipeline
|
||||
p := &renderer.pipeline
|
||||
|
||||
pipeline.amount_of_passes = 0;
|
||||
mem.set(&pipeline.passes[0], 0, MAX_PASSES_IN_PIPELINE * size_of(^Pass))
|
||||
p.amount_of_scene_passes = 0;
|
||||
mem.set(&p.scene_passes[0], 0, PIPELINE_MAX_SCENE_PASSES * size_of(^Scene_Pass))
|
||||
|
||||
p.amount_of_post_processing_passes = 0;
|
||||
mem.set(&p.post_processing_passes[0], 0, PIPELINE_MAX_POST_PROCESSING_PASSES * size_of(^Post_Processing_Pass))
|
||||
}
|
||||
|
||||
set_pipeline :: proc(renderer: ^Renderer, passes: []^Pass) {
|
||||
clear_pipeline(renderer)
|
||||
for p in passes {
|
||||
if !add_pass_to_pipeline(renderer, p) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private="file") add_pass_to_pipeline :: proc(renderer: ^Renderer, pass: ^Pass) -> bool {
|
||||
set_pipeline :: proc(
|
||||
renderer: ^Renderer,
|
||||
fullscreen_material: ^Material, fullscreen_mesh: ^Mesh,
|
||||
scene_passes: []^Scene_Pass,
|
||||
post_processing_passes: []^Post_Processing_Pass,
|
||||
){
|
||||
assert(renderer != nil)
|
||||
assert(pass != nil)
|
||||
|
||||
clear_pipeline(renderer)
|
||||
|
||||
renderer.pipeline.fullscreen_material = fullscreen_material
|
||||
renderer.pipeline.fullscreen_mesh = fullscreen_mesh
|
||||
|
||||
for p in scene_passes {
|
||||
if !add_pass_to_pipeline(renderer, p) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for p in post_processing_passes {
|
||||
if !add_pass_to_pipeline(renderer, p) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_pass_to_pipeline :: proc {
|
||||
add_scene_pass_to_pipeline,
|
||||
add_post_processing_pass_to_pipeline,
|
||||
}
|
||||
|
||||
@(private="file") add_scene_pass_to_pipeline :: proc(renderer: ^Renderer, scene_pass: ^Scene_Pass) -> bool {
|
||||
assert(renderer != nil)
|
||||
assert(scene_pass != nil)
|
||||
|
||||
pipeline := &renderer.pipeline
|
||||
|
||||
if pipeline.amount_of_passes == MAX_PASSES_IN_PIPELINE {
|
||||
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_scene_passes == PIPELINE_MAX_SCENE_PASSES {
|
||||
log.warnf("Failed to add scene-pass '%v' to renderer's pipeline; hit max capacity (%v).", scene_pass.name, PIPELINE_MAX_SCENE_PASSES)
|
||||
return false
|
||||
}
|
||||
|
||||
pipeline.passes[pipeline.amount_of_passes] = pass
|
||||
pipeline.amount_of_passes += 1
|
||||
|
||||
// log.infof("Successfully added pass '%v' to pipeline.", pass.name)
|
||||
pipeline.scene_passes[pipeline.amount_of_scene_passes] = scene_pass
|
||||
pipeline.amount_of_scene_passes += 1
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@(private="file") add_post_processing_pass_to_pipeline :: proc(renderer: ^Renderer, post_processing_pass: ^Post_Processing_Pass) -> bool {
|
||||
assert(renderer != nil)
|
||||
assert(post_processing_pass != nil)
|
||||
|
||||
pipeline := &renderer.pipeline
|
||||
|
||||
if pipeline.amount_of_post_processing_passes == PIPELINE_MAX_POST_PROCESSING_PASSES {
|
||||
log.warnf("Failed to add post-processing pass '%v' to renderer's pipeline; hit max capacity (%v).", post_processing_pass.name, PIPELINE_MAX_POST_PROCESSING_PASSES)
|
||||
return false
|
||||
}
|
||||
|
||||
pipeline.post_processing_passes[pipeline.amount_of_post_processing_passes] = post_processing_pass
|
||||
pipeline.amount_of_post_processing_passes += 1
|
||||
|
||||
return true
|
||||
}
|
||||
227
renderer.odin
227
renderer.odin
@@ -1,5 +1,7 @@
|
||||
package renderer
|
||||
|
||||
import "core:slice"
|
||||
import "core:sort"
|
||||
import "core:math/linalg"
|
||||
import "core:fmt"
|
||||
import "core:log"
|
||||
@@ -75,9 +77,9 @@ set_viewport :: proc(renderer: ^Renderer, x, y, width, height: u16) {
|
||||
}
|
||||
}
|
||||
|
||||
@(private="file") clear_screen :: proc(renderer: ^Renderer, clear_depth: bool) {
|
||||
@(private="file") clear_screen :: proc(renderer: ^Renderer, clear_color: bool, clear_depth: bool) {
|
||||
when RENDER_BACKEND_OPENGL {
|
||||
opengl_clear_screen(renderer, clear_depth)
|
||||
opengl_clear_screen(renderer, clear_color, clear_depth)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,54 +89,103 @@ render_frame :: proc(renderer: ^Renderer) {
|
||||
return
|
||||
}
|
||||
|
||||
view_matrix, view_matrix_ok := get_camera_view_matrix(renderer.active_camera)
|
||||
assert(view_matrix_ok)
|
||||
projection_matrix, projection_matrix_ok := get_camera_projection_matrix(renderer, renderer.active_camera)
|
||||
assert(projection_matrix_ok)
|
||||
view_matrix, _ := get_camera_view_matrix(renderer.active_camera)
|
||||
projection_matrix, _ := get_camera_projection_matrix(renderer, renderer.active_camera)
|
||||
|
||||
pipeline := &renderer.pipeline
|
||||
for i in 0 ..< pipeline.amount_of_passes {
|
||||
pass := pipeline.passes[i]
|
||||
|
||||
set_clear_color(renderer, pass.clear_color)
|
||||
clear_screen(renderer, pass.should_clear_depth)
|
||||
enable_depth_testing(renderer, pass.should_test_depth)
|
||||
|
||||
for i in 0..<pass.draw_command_count {
|
||||
command := &pass.draw_commands[i]
|
||||
|
||||
model_matrix := linalg.identity(linalg.Matrix4x4f32)
|
||||
|
||||
// Translate.
|
||||
translation := linalg.matrix4_translate(command.position)
|
||||
|
||||
// Rotate.
|
||||
rot_x := linalg.matrix4_rotate(linalg.to_radians(command.rotation.x), [3]f32 { 1, 0, 0 })
|
||||
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.
|
||||
scale := linalg.matrix4_scale(command.scale)
|
||||
|
||||
model_matrix *= translation * rotation * scale
|
||||
|
||||
activate_material(&command.material, model_matrix, view_matrix, projection_matrix)
|
||||
|
||||
draw_mesh(&command.mesh)
|
||||
for i in 0 ..< pipeline.amount_of_scene_passes {
|
||||
execute_scene_pass(renderer, pipeline.scene_passes[i], view_matrix, projection_matrix)
|
||||
}
|
||||
|
||||
// TODO: SS - "Deactivate" the pass.
|
||||
for i in 0 ..< pipeline.amount_of_post_processing_passes {
|
||||
execute_post_processing_pass(renderer, pipeline.post_processing_passes[i], view_matrix, projection_matrix)
|
||||
}
|
||||
|
||||
// Clear the pass' draw-commands.
|
||||
pass.draw_command_count = 0
|
||||
bind_render_target(renderer, nil) // Det görs ju här..
|
||||
|
||||
if pipeline.fullscreen_material != nil {
|
||||
if pipeline.fullscreen_material.texture != nil {
|
||||
activate_fullscreen_material(renderer)
|
||||
|
||||
if pipeline.fullscreen_mesh != nil {
|
||||
draw_mesh(pipeline.fullscreen_mesh)
|
||||
}
|
||||
else {
|
||||
log.warn("Renderer is missing a fullscreen-mesh.")
|
||||
}
|
||||
|
||||
deactivate_fullscreen_material(renderer)
|
||||
}
|
||||
else {
|
||||
log.warn("Fullscreen-material does not have a texture.")
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.warn("Renderer is missing a fullscreen-material.")
|
||||
}
|
||||
|
||||
when RENDER_BACKEND_OPENGL {
|
||||
opengl_swap_buffers(renderer)
|
||||
}
|
||||
}
|
||||
|
||||
clear_screen(renderer, true)
|
||||
execute_scene_pass :: proc(renderer: ^Renderer, pass: ^Scene_Pass, view_matrix, projection_matrix: linalg.Matrix4x4f32) {
|
||||
// fmt.printfln("Rendering pass '%v' to output_rt %v.", pass.name, pass.output_rt)
|
||||
|
||||
// TODO: SS - Use 'pass.input_textures'
|
||||
|
||||
assert(pass.output_rt != nil)
|
||||
bind_render_target(renderer, pass.output_rt)
|
||||
|
||||
should_write_depth := pass.output_rt.depth_texture != nil
|
||||
should_test_depth := should_write_depth
|
||||
should_clear_depth := should_write_depth
|
||||
|
||||
cc, has_clear_color := pass.clear_color.?
|
||||
if has_clear_color {
|
||||
set_clear_color(renderer, cc)
|
||||
}
|
||||
|
||||
should_clear_color := has_clear_color && pass.output_rt.color_texture != nil
|
||||
|
||||
clear_screen(renderer, should_clear_color, should_clear_depth)
|
||||
apply_depth(renderer, should_test_depth, should_write_depth)
|
||||
|
||||
apply_blend_mode(renderer, pass.blend_mode)
|
||||
|
||||
sort_draw_commands(renderer, pass)
|
||||
|
||||
for &dc in &pass.draw_commands { // TODO: SS - Don't think we need the address of the draw-commands.
|
||||
model_matrix := linalg.identity(linalg.Matrix4x4f32)
|
||||
|
||||
// Translate.
|
||||
translation := linalg.matrix4_translate(dc.position)
|
||||
|
||||
// Rotate.
|
||||
rot_x := linalg.matrix4_rotate(linalg.to_radians(dc.rotation.x), [3]f32 { 1, 0, 0 })
|
||||
rot_y := linalg.matrix4_rotate(linalg.to_radians(dc.rotation.y), [3]f32 { 0, 1, 0 })
|
||||
rot_z := linalg.matrix4_rotate(linalg.to_radians(dc.rotation.z), [3]f32 { 0, 0, 1 })
|
||||
rotation := rot_z * rot_y * rot_x
|
||||
|
||||
// Scale.
|
||||
scale := linalg.matrix4_scale(dc.scale)
|
||||
|
||||
model_matrix *= translation * rotation * scale
|
||||
|
||||
activate_material(&dc.material, model_matrix, view_matrix, projection_matrix)
|
||||
|
||||
draw_mesh(&dc.mesh)
|
||||
}
|
||||
|
||||
// TODO: SS - "Deactivate" the pass.
|
||||
|
||||
// Clear the pass' draw-commands.
|
||||
clear(&pass.draw_commands)
|
||||
}
|
||||
|
||||
execute_post_processing_pass :: proc(renderer: ^Renderer, pass: ^Post_Processing_Pass, view_matrix, projection_matrix: linalg.Matrix4x4f32) {
|
||||
fmt.printfln("TODO: SS - Execute post-processing pass '%v'.", pass.name)
|
||||
}
|
||||
|
||||
destroy :: proc(renderer: ^Renderer) {
|
||||
@@ -154,6 +205,21 @@ destroy :: proc(renderer: ^Renderer) {
|
||||
}
|
||||
}
|
||||
|
||||
@(private) activate_fullscreen_material :: proc(renderer: ^Renderer) {
|
||||
assert(renderer != nil)
|
||||
mat := renderer.pipeline.fullscreen_material
|
||||
when RENDER_BACKEND_OPENGL {
|
||||
opengl_activate_fullscreen_material(mat)
|
||||
}
|
||||
}
|
||||
|
||||
@(private) deactivate_fullscreen_material :: proc(renderer: ^Renderer) {
|
||||
assert(renderer != nil)
|
||||
when RENDER_BACKEND_OPENGL {
|
||||
opengl_deactivate_fullscreen_material()
|
||||
}
|
||||
}
|
||||
|
||||
@(private) draw_mesh :: proc(mesh: ^Mesh) {
|
||||
assert(mesh != nil)
|
||||
|
||||
@@ -162,8 +228,89 @@ 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 {
|
||||
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) sort_draw_commands :: proc(renderer: ^Renderer, pass: ^Scene_Pass) {
|
||||
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_framebuffer :: proc(renderer: ^Renderer, rt: ^Render_Target) -> bool {
|
||||
// when RENDER_BACKEND_OPENGL {
|
||||
// return opengl_bind_framebuffer(renderer, rt)
|
||||
// }
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@(private) bind_depth_texture :: proc(renderer: ^Renderer, rt: ^Render_Target) -> bool {
|
||||
// when RENDER_BACKEND_OPENGL {
|
||||
// return opengl_bind_depth_texture(renderer, rt)
|
||||
// }
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@(private) bind_render_target :: proc(renderer: ^Renderer, rt: ^Render_Target) {
|
||||
when RENDER_BACKEND_OPENGL {
|
||||
opengl_bind_render_target(renderer, rt)
|
||||
}
|
||||
}
|
||||
@@ -101,14 +101,15 @@ when RENDER_BACKEND_OPENGL {
|
||||
)
|
||||
}
|
||||
|
||||
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
|
||||
opengl_clear_screen :: proc(renderer: ^Renderer, clear_color: bool, clear_depth: bool) {
|
||||
flags := u32(0)
|
||||
if clear_color {
|
||||
flags |= gl.COLOR_BUFFER_BIT
|
||||
}
|
||||
|
||||
gl.Clear(mask)
|
||||
if clear_depth {
|
||||
flags |= gl.DEPTH_BUFFER_BIT
|
||||
}
|
||||
gl.Clear(flags)
|
||||
}
|
||||
|
||||
opengl_swap_buffers :: proc(renderer: ^Renderer) {
|
||||
@@ -127,6 +128,7 @@ when RENDER_BACKEND_OPENGL {
|
||||
Shader_Backend :: Shader_OpenGL
|
||||
Shader_Program_Backend :: Shader_Program_OpenGL
|
||||
Texture_Backend :: Texture_OpenGL
|
||||
Render_Target_Backend :: Render_Target_OpenGL
|
||||
|
||||
Mesh_OpenGL :: struct {
|
||||
vbo, vao, ebo: u32,
|
||||
@@ -144,7 +146,11 @@ when RENDER_BACKEND_OPENGL {
|
||||
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)
|
||||
|
||||
m: Mesh_OpenGL
|
||||
@@ -161,14 +167,20 @@ when RENDER_BACKEND_OPENGL {
|
||||
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.VertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 8 * size_of(f32), uintptr(0))
|
||||
gl.EnableVertexAttribArray(0)
|
||||
layout_total_stride := u32(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)))
|
||||
gl.EnableVertexAttribArray(1)
|
||||
offset := u32(0)
|
||||
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.BindBuffer(gl.ARRAY_BUFFER, 0)
|
||||
@@ -260,72 +272,153 @@ when RENDER_BACKEND_OPENGL {
|
||||
gl.UniformMatrix4fv(projection_matrix_loc, 1, false, &projection_matrix_as_f32_array[0])
|
||||
}
|
||||
|
||||
opengl_activate_fullscreen_material :: proc(material: ^Material) {
|
||||
assert(material != nil)
|
||||
gl.UseProgram(material.shader_program.backend.handle)
|
||||
|
||||
gl.ActiveTexture(gl.TEXTURE0)
|
||||
gl.BindTexture(gl.TEXTURE_2D, material.texture.backend.handle)
|
||||
|
||||
loc := gl.GetUniformLocation(material.shader_program.backend.handle, "texture0") // eller "uTex"
|
||||
if loc != -1 {
|
||||
gl.Uniform1i(loc, 0)
|
||||
}
|
||||
|
||||
gl.Disable(gl.DEPTH_TEST)
|
||||
gl.DepthMask(gl.FALSE)
|
||||
}
|
||||
|
||||
opengl_deactivate_fullscreen_material :: proc() {
|
||||
gl.DepthMask(gl.TRUE)
|
||||
gl.Enable(gl.DEPTH_TEST)
|
||||
}
|
||||
|
||||
opengl_draw_mesh :: proc(mesh: ^Mesh) {
|
||||
gl.BindVertexArray(mesh.backend.vao)
|
||||
assert(mesh.amount_of_indices < u32(max(i32)))
|
||||
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 {
|
||||
handle: u32
|
||||
gl.GenTextures(1, &handle)
|
||||
gl.BindTexture(gl.TEXTURE_2D, handle)
|
||||
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
|
||||
switch t.filter_mode {
|
||||
case .Nearest:
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
|
||||
case .Linear:
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||
case .Nearest_MipMap:
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_NEAREST)
|
||||
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)
|
||||
|
||||
type := gl.UNSIGNED_BYTE
|
||||
switch t.depth {
|
||||
case 8: type = gl.UNSIGNED_BYTE
|
||||
case 16: type = gl.UNSIGNED_SHORT
|
||||
case 32: type = gl.UNSIGNED_INT
|
||||
}
|
||||
|
||||
format := gl.RGBA
|
||||
switch t.channels {
|
||||
case 1: format = gl.RED
|
||||
case 2: format = gl.RG
|
||||
case 3: format = gl.RGB
|
||||
case 4: format = gl.RGBA
|
||||
switch t.wrap_mode {
|
||||
case .Clamp:
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP)
|
||||
case .Repeat:
|
||||
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
|
||||
|
||||
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))
|
||||
internal_format, format, type := opengl_texture_format_from_texture(t)
|
||||
|
||||
gl.TexImage2D(
|
||||
target = gl.TEXTURE_2D,
|
||||
level = 0,
|
||||
internalformat = i32(internal),
|
||||
width = i32(t.width),
|
||||
height = i32(t.height),
|
||||
border = 0,
|
||||
format = u32(format),
|
||||
type = u32(type),
|
||||
pixels = pixels,
|
||||
// pixels = &pixels[0]
|
||||
// pixels = &bytes.reverse(bytes.buffer_to_bytes(&img.pixels))[0]
|
||||
gl.TEXTURE_2D,
|
||||
0,
|
||||
i32(internal_format),
|
||||
i32(t.width),
|
||||
i32(t.height),
|
||||
0,
|
||||
u32(format),
|
||||
u32(type),
|
||||
pixels,
|
||||
)
|
||||
|
||||
if !t.is_depth {
|
||||
gl.GenerateMipmap(gl.TEXTURE_2D)
|
||||
}
|
||||
|
||||
t.backend = Texture_OpenGL { handle = handle }
|
||||
return true
|
||||
@@ -335,12 +428,105 @@ when RENDER_BACKEND_OPENGL {
|
||||
gl.DeleteTextures(1, &t.backend.handle)
|
||||
}
|
||||
|
||||
opengl_enable_depth_testing :: proc(renderer: ^Renderer, enable: bool) {
|
||||
if enable {
|
||||
opengl_apply_depth :: proc(renderer: ^Renderer, test_depth, write_depth: bool) {
|
||||
if test_depth {
|
||||
gl.Enable(gl.DEPTH_TEST)
|
||||
}
|
||||
else {
|
||||
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_bind_render_target :: proc(renderer: ^Renderer, rt: ^Render_Target) {
|
||||
if rt == nil {
|
||||
gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
|
||||
|
||||
gl.Viewport(
|
||||
0,
|
||||
0,
|
||||
i32(renderer.viewport.width),
|
||||
i32(renderer.viewport.height),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
gl.BindFramebuffer(gl.FRAMEBUFFER, rt.backend.handle)
|
||||
|
||||
gl.Viewport(
|
||||
0,
|
||||
0,
|
||||
i32(rt.color_texture.width),
|
||||
i32(rt.color_texture.height),
|
||||
)
|
||||
}
|
||||
|
||||
opengl_create_render_target :: proc(renderer: ^Renderer, rt: ^Render_Target) -> bool {
|
||||
fb: u32
|
||||
gl.GenFramebuffers(1, &fb)
|
||||
gl.BindFramebuffer(gl.FRAMEBUFFER, fb)
|
||||
|
||||
gl.FramebufferTexture2D(
|
||||
gl.FRAMEBUFFER,
|
||||
gl.COLOR_ATTACHMENT0,
|
||||
gl.TEXTURE_2D,
|
||||
rt.color_texture.backend.handle,
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
108
texture.odin
108
texture.odin
@@ -6,20 +6,61 @@ import "vendor:stb/image"
|
||||
import "core:fmt"
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
Texture_Format :: enum {
|
||||
RGBA8,
|
||||
RGBA16F,
|
||||
R11G11B10F,
|
||||
|
||||
R8,
|
||||
R16F,
|
||||
R32F,
|
||||
|
||||
Depth16,
|
||||
Depth24,
|
||||
Depth32F,
|
||||
Depth24_Stencil8,
|
||||
Depth32F_Stencil8,
|
||||
}
|
||||
|
||||
Render_Target :: struct {
|
||||
color_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_from_path,
|
||||
create_texture_raw,
|
||||
}
|
||||
|
||||
create_texture_from_path :: proc(renderer: ^Renderer, path: string) -> (Texture, bool) {
|
||||
width, height, depth, channels: c.int
|
||||
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, channels, depth: c.int
|
||||
|
||||
path_cstr := strings.clone_to_cstring(path)
|
||||
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)
|
||||
if data == nil {
|
||||
return {}, false
|
||||
@@ -27,23 +68,72 @@ create_texture_from_path :: proc(renderer: ^Renderer, path: string) -> (Texture,
|
||||
depth = 8
|
||||
|
||||
t: Texture
|
||||
t.width = u32(width)
|
||||
t.height = u32(height)
|
||||
t.channels = u32(channels)
|
||||
t.depth = u32(depth)
|
||||
t.width = u16(width)
|
||||
t.height = u16(height)
|
||||
t.filter_mode = filter_mode
|
||||
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
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
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 {
|
||||
if !opengl_load_texture(renderer, &t, nil) {
|
||||
return {}, false
|
||||
}
|
||||
} else {
|
||||
#assert(false)
|
||||
}
|
||||
|
||||
return t, true
|
||||
}
|
||||
|
||||
create_render_target :: proc(renderer: ^Renderer, color_texture, depth_texture: ^Texture) -> (Render_Target, bool) {
|
||||
rt: Render_Target
|
||||
rt.color_texture = color_texture
|
||||
rt.depth_texture = depth_texture
|
||||
|
||||
when RENDER_BACKEND_OPENGL {
|
||||
if !opengl_create_render_target(renderer, &rt) {
|
||||
return {}, false
|
||||
}
|
||||
}
|
||||
|
||||
return rt, true
|
||||
}
|
||||
|
||||
|
||||
delete_texture :: proc(renderer: ^Renderer, texture: ^Texture) {
|
||||
when RENDER_BACKEND_OPENGL {
|
||||
opengl_delete_texture(renderer, texture)
|
||||
|
||||
Reference in New Issue
Block a user