ecs-test/ecs/render_system.odin
2025-08-27 21:03:35 +02:00

328 lines
9 KiB
Odin

package ecs
import "base:runtime"
import "base:intrinsics"
import "core:log"
import "core:mem"
import "core:time"
import "core:fmt"
import "core:testing"
import "core:strings"
import "core:path/filepath"
import "core:math"
import "core:math/linalg"
import "core:os"
import stbi "vendor:stb/image"
import sa "../sokol/app"
import sh "../sokol/helpers"
import sg "../sokol/gfx"
import program_config "../config"
import shaders "../shaders/out"
CubeVertexData :: struct {
pos: Vec3,
}
OutlineVertexData :: struct {
pos: Vec3,
}
Mesh :: struct {
vertex_buffer: sg.Buffer,
index_buffer: sg.Buffer,
}
Material :: struct {
pipeline: sg.Pipeline,
shader: sg.Shader,
}
RenderSystem :: struct {
using base: SystemBase,
camera_entity: EntityID,
materials: map[MaterialID]Material,
meshes : map[MeshID]Mesh,
}
@(private)
init_outline_material :: proc(render_system: ^RenderSystem) {
shader := sg.make_shader(shaders.outline_shader_desc(sg.query_backend()))
pipeline := sg.make_pipeline({
shader = shader,
layout = {
attrs = {
shaders.ATTR_outline_pos = { format = .FLOAT3 },
},
},
index_type = .UINT16,
cull_mode = .FRONT,
depth = {
pixel_format = .DEFAULT,
write_enabled = true,
bias = 0.001,
bias_clamp = 0.0,
bias_slope_scale = 1.0,
compare = .LESS_EQUAL,
},
sample_count = 4,
})
render_system.materials[.Outline] = Material{
shader = shader,
pipeline = pipeline,
}
}
@(private)
init_cube_material :: proc(render_system: ^RenderSystem) {
shader := sg.make_shader(shaders.cube_shader_desc(sg.query_backend()))
pipeline := sg.make_pipeline({
shader = shader,
layout = {
attrs = {
shaders.ATTR_cube_pos = { format = .FLOAT3 },
},
},
index_type = .UINT16,
cull_mode = .BACK,
depth = {
pixel_format = .DEFAULT,
write_enabled = true,
bias = 0.001,
bias_clamp = 0.0,
bias_slope_scale = 1.0,
compare = .LESS_EQUAL,
},
sample_count = 4,
})
render_system.materials[.Cube] = Material{
shader = shader,
pipeline = pipeline,
}
}
@(private)
init_cube_mesh :: proc (render_system: ^RenderSystem) {
cube_vertices := []CubeVertexData {
{ pos = { -0.5, -0.5, 0.5 } },
{ pos = { 0.5, -0.5, 0.5 } },
{ pos = { 0.5, 0.5, 0.5 } },
{ pos = { -0.5, 0.5, 0.5 } },
{ pos = { -0.5, -0.5, -0.5 } },
{ pos = { 0.5, -0.5, -0.5 } },
{ pos = { 0.5, 0.5, -0.5 } },
{ pos = { -0.5, 0.5, -0.5 } },
{ pos = { -0.5, 0.5, 0.5 } },
{ pos = { 0.5, 0.5, 0.5 } },
{ pos = { 0.5, 0.5, -0.5 } },
{ pos = { -0.5, 0.5, -0.5 } },
{ pos = { -0.5, -0.5, 0.5 } },
{ pos = { 0.5, -0.5, 0.5 } },
{ pos = { 0.5, -0.5, -0.5 } },
{ pos = { -0.5, -0.5, -0.5 } },
{ pos = { 0.5, -0.5, 0.5 } },
{ pos = { 0.5, -0.5, -0.5 } },
{ pos = { 0.5, 0.5, -0.5 } },
{ pos = { 0.5, 0.5, 0.5 } },
{ pos = { -0.5, -0.5, 0.5 } },
{ pos = { -0.5, -0.5, -0.5 } },
{ pos = { -0.5, 0.5, -0.5 } },
{ pos = { -0.5, 0.5, 0.5 } },
}
vertex_buffer := sg.make_buffer({
data = sg_range(cube_vertices)
})
cube_indices := []u16 {
1, 0, 2,
3, 2, 0,
7, 4, 6,
5, 6, 4,
9, 8, 10,
11, 10, 8,
15, 12, 14,
13, 14, 12,
17, 16, 18,
19, 18, 16,
23, 20, 22,
21, 22, 20,
}
index_buffer := sg.make_buffer({
usage = { index_buffer = true },
data = sg_range(cube_indices),
})
render_system.meshes[.Cube] = Mesh{
vertex_buffer = vertex_buffer,
index_buffer = index_buffer,
}
}
render_system_init :: proc(render_system: ^RenderSystem, camera_entity: EntityID) {
default_context := runtime.default_context()
sg.setup({
environment = sh.glue_environment(),
allocator = sg.Allocator(sh.allocator(&default_context)),
logger = sg.Logger(sh.logger(&default_context)),
})
render_system.materials = make(map[MaterialID]Material)
render_system.meshes = make(map[MeshID]Mesh)
render_system.camera_entity = camera_entity
init_cube_mesh(render_system)
init_cube_material(render_system)
init_outline_material(render_system)
}
render_system_delete :: proc(render_system: ^RenderSystem, coordinator: ^Coordinator) {
cube_mesh := render_system.meshes[.Cube]
sg.destroy_buffer(cube_mesh.index_buffer)
sg.destroy_buffer(cube_mesh.vertex_buffer)
cube_material := render_system.materials[.Cube]
sg.destroy_pipeline(cube_material.pipeline)
sg.destroy_shader(cube_material.shader)
outline_material := render_system.materials[.Outline]
sg.destroy_pipeline(outline_material.pipeline)
sg.destroy_shader(outline_material.shader)
delete(render_system.materials)
delete(render_system.meshes)
sg.shutdown()
}
@(private)
CLEAR_SCREEN_ACTION :: sg.Pass_Action{
colors = {
0 = {
load_action = .CLEAR,
clear_value = program_config.BACKGROUND_COLOR,
}
}
}
outline_pass :: proc(render_system: ^RenderSystem, coordinator: ^Coordinator, proj: Mat4, view: Mat4) {
for entity in render_system.entities {
transform := coordinator_get_component(TransformComponent, coordinator, entity)
mesh_id := coordinator_get_component(MeshComponent, coordinator, entity).mesh_id
mesh := render_system.meshes[mesh_id]
scale_mat := linalg.matrix4_scale_f32(transform.scale * 1.2)
rot_mat := linalg.matrix4_from_yaw_pitch_roll_f32(
transform.rotation.y,
transform.rotation.x,
transform.rotation.z
)
pos_mat := linalg.matrix4_translate_f32(transform.position)
model := pos_mat * rot_mat * scale_mat
mvp := proj * view * model
outline_material := render_system.materials[.Outline]
sg.apply_pipeline(outline_material.pipeline)
sg.apply_bindings({
vertex_buffers = { 0 = mesh.vertex_buffer },
index_buffer = mesh.index_buffer,
})
sg.apply_uniforms(shaders.UB_VsParamsOutline, sg_range(&shaders.Vsparamsoutline{
mvp = mvp,
}))
sg.apply_uniforms(shaders.UB_FsParamsOutline, sg_range(&shaders.Fsparamsoutline{
col = program_config.BORDER_COLOR,
}))
sg.draw(0, 36, 1)
}
}
cube_pass :: proc(render_system: ^RenderSystem, coordinator: ^Coordinator, proj: Mat4, view: Mat4) {
for entity in render_system.entities {
transform := coordinator_get_component(TransformComponent, coordinator, entity)
mesh_id := coordinator_get_component(MeshComponent, coordinator, entity).mesh_id
mesh := render_system.meshes[mesh_id]
material_id := coordinator_get_component(MaterialComponent, coordinator, entity).material_id
material := render_system.materials[material_id]
color := coordinator_get_component(ColorComponent, coordinator, entity)
scale_mat := linalg.matrix4_scale_f32(transform.scale)
rot_mat := linalg.matrix4_from_yaw_pitch_roll_f32(
transform.rotation.y,
transform.rotation.x,
transform.rotation.z
)
pos_mat := linalg.matrix4_translate_f32(transform.position)
model := pos_mat * rot_mat * scale_mat
mvp := proj * view * model
sg.apply_pipeline(material.pipeline)
sg.apply_bindings({
vertex_buffers = { 0 = mesh.vertex_buffer },
index_buffer = mesh.index_buffer,
})
sg.apply_uniforms(shaders.UB_VsParamsCube, sg_range(&shaders.Vsparamscube{
mvp = mvp,
col = color.color,
}))
sg.draw(0, 36, 1)
}
}
render_system_update :: proc(render_system: ^RenderSystem, coordinator: ^Coordinator) {
camera := coordinator_get_component(CameraComponent, coordinator, render_system.camera_entity)
proj := linalg.matrix4_perspective_f32(70, sa.widthf() / sa.heightf(), 0.001, 1000)
view := linalg.matrix4_look_at_f32(camera.position, camera.target, { 0, 1, 0 } )
sg.begin_pass({ swapchain = sh.glue_swapchain(), action = CLEAR_SCREEN_ACTION })
outline_pass(render_system, coordinator, proj, view)
cube_pass(render_system, coordinator, proj, view)
sg.end_pass()
sg.commit()
}
sg_range :: proc {
sg_range_from_slice,
sg_range_from_struct,
}
sg_range_from_slice :: proc(s: []$T) -> sg.Range {
return {
ptr = raw_data(s),
size = len(s) * size_of(T),
}
}
sg_range_from_struct :: proc(s: ^$T) -> sg.Range where intrinsics.type_is_struct(T) {
return {
ptr = s,
size = size_of(T),
}
}