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 {
|
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
|
||||||
|
|
||||||
|
switch t in camera.type {
|
||||||
|
case Camera_Perspective: {
|
||||||
projection_matrix *= linalg.matrix4_perspective(
|
projection_matrix *= linalg.matrix4_perspective(
|
||||||
linalg.to_radians(f32(camera.fov_degrees)),
|
linalg.to_radians(f32(t.fov_degrees)),
|
||||||
get_aspect_ratio(renderer),
|
get_aspect_ratio(renderer),
|
||||||
camera.near,
|
camera.near,
|
||||||
camera.far,
|
camera.far,
|
||||||
flip_z_axis = true,
|
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
|
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
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,16 @@
|
|||||||
package renderer
|
package renderer
|
||||||
|
|
||||||
Material :: struct {
|
Material :: struct {
|
||||||
shader_program: Shader_Program,
|
// 'Name'?
|
||||||
texture: Texture, // Diffuse, normal etc later.
|
shader_program: ^Shader_Program,
|
||||||
|
texture: ^Texture, // Diffuse, normal etc later.
|
||||||
// uniforms, textures, etc.
|
// 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
|
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(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 +28,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
|
||||||
}
|
}
|
||||||
|
|||||||
146
pass.odin
146
pass.odin
@@ -2,35 +2,155 @@ package renderer
|
|||||||
|
|
||||||
import "core:fmt"
|
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,
|
name: string,
|
||||||
clear_color: RGB_Color,
|
|
||||||
|
|
||||||
should_test_depth: bool,
|
input_textures: [PASS_MAX_INPUT_TEXTURES]^Texture, // TODO: SS - Make this a map?
|
||||||
should_clear_depth: bool,
|
input_texture_count: u8,
|
||||||
|
|
||||||
draw_commands: [MAX_DRAW_COMMANDS_PER_PASS]Draw_Command,
|
output_rt: ^Render_Target,
|
||||||
draw_command_count: u32,
|
|
||||||
|
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 {
|
Draw_Command :: struct {
|
||||||
|
renderer: ^Renderer, // Needed for sorting.
|
||||||
|
|
||||||
mesh: Mesh,
|
mesh: Mesh,
|
||||||
material: Material,
|
material: Material,
|
||||||
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 {
|
Blend_Mode :: enum {
|
||||||
if pass.draw_command_count >= len(pass.draw_commands) {
|
None,
|
||||||
return false
|
Alpha,
|
||||||
|
Additive,
|
||||||
|
Multiply,
|
||||||
}
|
}
|
||||||
|
|
||||||
pass.draw_commands[pass.draw_command_count] = command
|
Blend_Factor :: enum {
|
||||||
pass.draw_command_count += 1
|
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
|
return true
|
||||||
}
|
}
|
||||||
@@ -3,45 +3,92 @@ package renderer
|
|||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:log"
|
import "core:log"
|
||||||
|
|
||||||
MAX_PASSES_IN_PIPELINE :: 8
|
PIPELINE_MAX_SCENE_PASSES :: 8
|
||||||
|
PIPELINE_MAX_POST_PROCESSING_PASSES :: 8
|
||||||
|
|
||||||
Pipeline :: struct {
|
Pipeline :: struct {
|
||||||
passes: [MAX_PASSES_IN_PIPELINE]^Pass,
|
scene_passes: [PIPELINE_MAX_SCENE_PASSES]^Scene_Pass,
|
||||||
amount_of_passes: u8,
|
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) {
|
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_scene_passes = 0;
|
||||||
mem.set(&pipeline.passes[0], 0, MAX_PASSES_IN_PIPELINE * size_of(^Pass))
|
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) {
|
set_pipeline :: proc(
|
||||||
clear_pipeline(renderer)
|
renderer: ^Renderer,
|
||||||
for p in passes {
|
fullscreen_material: ^Material, fullscreen_mesh: ^Mesh,
|
||||||
if !add_pass_to_pipeline(renderer, p) {
|
scene_passes: []^Scene_Pass,
|
||||||
return
|
post_processing_passes: []^Post_Processing_Pass,
|
||||||
}
|
){
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@(private="file") add_pass_to_pipeline :: proc(renderer: ^Renderer, pass: ^Pass) -> bool {
|
|
||||||
assert(renderer != nil)
|
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
|
pipeline := &renderer.pipeline
|
||||||
|
|
||||||
if pipeline.amount_of_passes == MAX_PASSES_IN_PIPELINE {
|
if pipeline.amount_of_scene_passes == PIPELINE_MAX_SCENE_PASSES {
|
||||||
log.warnf("Failed to add pass '%v' to renderer's pipeline; hit max capacity (%v).", pass.name, MAX_PASSES_IN_PIPELINE)
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
pipeline.passes[pipeline.amount_of_passes] = pass
|
pipeline.scene_passes[pipeline.amount_of_scene_passes] = scene_pass
|
||||||
pipeline.amount_of_passes += 1
|
pipeline.amount_of_scene_passes += 1
|
||||||
|
|
||||||
// log.infof("Successfully added pass '%v' to pipeline.", pass.name)
|
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
|
return true
|
||||||
}
|
}
|
||||||
227
renderer.odin
227
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"
|
||||||
@@ -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 {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
view_matrix, view_matrix_ok := get_camera_view_matrix(renderer.active_camera)
|
view_matrix, _ := get_camera_view_matrix(renderer.active_camera)
|
||||||
assert(view_matrix_ok)
|
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
|
pipeline := &renderer.pipeline
|
||||||
for i in 0 ..< pipeline.amount_of_passes {
|
|
||||||
pass := pipeline.passes[i]
|
|
||||||
|
|
||||||
set_clear_color(renderer, pass.clear_color)
|
for i in 0 ..< pipeline.amount_of_scene_passes {
|
||||||
clear_screen(renderer, pass.should_clear_depth)
|
execute_scene_pass(renderer, pipeline.scene_passes[i], view_matrix, projection_matrix)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
bind_render_target(renderer, nil) // Det görs ju här..
|
||||||
pass.draw_command_count = 0
|
|
||||||
|
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 {
|
when RENDER_BACKEND_OPENGL {
|
||||||
opengl_swap_buffers(renderer)
|
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) {
|
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) {
|
@(private) draw_mesh :: proc(mesh: ^Mesh) {
|
||||||
assert(mesh != nil)
|
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 {
|
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) {
|
opengl_clear_screen :: proc(renderer: ^Renderer, clear_color: bool, clear_depth: bool) {
|
||||||
mask := u32(gl.COLOR_BUFFER_BIT)
|
flags := u32(0)
|
||||||
if clear_depth {
|
if clear_color {
|
||||||
gl.ClearDepth(1.0)
|
flags |= gl.COLOR_BUFFER_BIT
|
||||||
mask |= gl.DEPTH_BUFFER_BIT
|
|
||||||
}
|
}
|
||||||
|
if clear_depth {
|
||||||
gl.Clear(mask)
|
flags |= gl.DEPTH_BUFFER_BIT
|
||||||
|
}
|
||||||
|
gl.Clear(flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
opengl_swap_buffers :: proc(renderer: ^Renderer) {
|
opengl_swap_buffers :: proc(renderer: ^Renderer) {
|
||||||
@@ -127,6 +128,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 +146,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 +167,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)
|
||||||
@@ -260,72 +272,153 @@ when RENDER_BACKEND_OPENGL {
|
|||||||
gl.UniformMatrix4fv(projection_matrix_loc, 1, false, &projection_matrix_as_f32_array[0])
|
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) {
|
opengl_draw_mesh :: proc(mesh: ^Mesh) {
|
||||||
gl.BindVertexArray(mesh.backend.vao)
|
gl.BindVertexArray(mesh.backend.vao)
|
||||||
assert(mesh.amount_of_indices < u32(max(i32)))
|
assert(mesh.amount_of_indices < u32(max(i32)))
|
||||||
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.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_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
|
||||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.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.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]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if !t.is_depth {
|
||||||
gl.GenerateMipmap(gl.TEXTURE_2D)
|
gl.GenerateMipmap(gl.TEXTURE_2D)
|
||||||
|
}
|
||||||
|
|
||||||
t.backend = Texture_OpenGL { handle = handle }
|
t.backend = Texture_OpenGL { handle = handle }
|
||||||
return true
|
return true
|
||||||
@@ -335,12 +428,105 @@ 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_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"
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 :: 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
|
||||||
@@ -27,23 +68,72 @@ create_texture_from_path :: proc(renderer: ^Renderer, path: string) -> (Texture,
|
|||||||
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 {
|
when RENDER_BACKEND_OPENGL {
|
||||||
if !opengl_load_texture(renderer, &t, data) {
|
if !opengl_load_texture(renderer, &t, data) {
|
||||||
return {}, false
|
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)
|
#assert(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
return t, true
|
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) {
|
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