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), } }