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 shaders "../shaders/out" VertexData :: struct { pos: Vec3, uv: Vec2, } Mesh :: struct { vertex_buffer: sg.Buffer, index_buffer: sg.Buffer, } Material :: struct { pipeline: sg.Pipeline, shader: sg.Shader, image: sg.Image, sampler: sg.Sampler, } RenderSystem :: struct { using base: SystemBase, camera_entity: EntityID, materials: map[MaterialID]Material, meshes : map[MeshID]Mesh, } @(private) init_grid_material :: proc(render_system: ^RenderSystem) { shader := sg.make_shader(shaders.main_shader_desc(sg.query_backend())) pipeline := sg.make_pipeline({ shader = shader, layout = { attrs = { shaders.ATTR_main_pos = { format = .FLOAT3 }, shaders.ATTR_main_uv = { format = .FLOAT2 }, }, }, 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, }, }) w, h: i32 pixels := stbi.load("res/white.png", &w, &h, nil, 4) assert(pixels != nil) defer(stbi.image_free(pixels)) image := sg.make_image({ width = w, height = h, pixel_format = .RGBA8, data = { subimage = { 0 = { 0 = { ptr = pixels, size = uint(w * h * 4) } } } } }) sampler := sg.make_sampler({}) render_system.materials[.Grid] = Material{ shader = shader, pipeline = pipeline, image = image, sampler = sampler, } } @(private) init_cube_mesh :: proc (render_system: ^RenderSystem) { cube_vertices := []VertexData { { pos = { -0.5, -0.5, 0.5 }, uv = { 0, 0 } }, { pos = { 0.5, -0.5, 0.5 }, uv = { 1, 0 } }, { pos = { 0.5, 0.5, 0.5 }, uv = { 1, 1 } }, { pos = { -0.5, 0.5, 0.5 }, uv = { 0, 1 } }, { pos = { -0.5, -0.5, -0.5 }, uv = { 1, 0 } }, { pos = { 0.5, -0.5, -0.5 }, uv = { 0, 0 } }, { pos = { 0.5, 0.5, -0.5 }, uv = { 0, 1 } }, { pos = { -0.5, 0.5, -0.5 }, uv = { 1, 1 } }, { pos = { -0.5, 0.5, 0.5 }, uv = { 0, 0 } }, { pos = { 0.5, 0.5, 0.5 }, uv = { 1, 0 } }, { pos = { 0.5, 0.5, -0.5 }, uv = { 1, 1 } }, { pos = { -0.5, 0.5, -0.5 }, uv = { 0, 1 } }, { pos = { -0.5, -0.5, 0.5 }, uv = { 0, 0 } }, { pos = { 0.5, -0.5, 0.5 }, uv = { 1, 0 } }, { pos = { 0.5, -0.5, -0.5 }, uv = { 1, 1 } }, { pos = { -0.5, -0.5, -0.5 }, uv = { 0, 1 } }, { pos = { 0.5, -0.5, 0.5 }, uv = { 0, 0 } }, { pos = { 0.5, -0.5, -0.5 }, uv = { 1, 0 } }, { pos = { 0.5, 0.5, -0.5 }, uv = { 1, 1 } }, { pos = { 0.5, 0.5, 0.5 }, uv = { 0, 1 } }, { pos = { -0.5, -0.5, 0.5 }, uv = { 1, 0 } }, { pos = { -0.5, -0.5, -0.5 }, uv = { 0, 0 } }, { pos = { -0.5, 0.5, -0.5 }, uv = { 0, 1 } }, { pos = { -0.5, 0.5, 0.5 }, uv = { 1, 1 } }, } 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_grid_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) grid_material := render_system.materials[.Grid] sg.destroy_image(grid_material.image) sg.destroy_sampler(grid_material.sampler) sg.destroy_pipeline(grid_material.pipeline) sg.destroy_shader(grid_material.shader) delete(render_system.materials) delete(render_system.meshes) sg.shutdown() } render_system_update :: proc(render_system: ^RenderSystem, coordinator: ^Coordinator, dt: f32) { 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 } ) BACKGROUND_COLOR :: sg.Color{ 0, 0, 0, 1 } CLEAR_SCREEN_ACTION :: sg.Pass_Action{ colors = { 0 = { load_action = .CLEAR, clear_value = BACKGROUND_COLOR, } } } sg.begin_pass({ swapchain = sh.glue_swapchain(), action = CLEAR_SCREEN_ACTION }) for entity in render_system.entities { transform := coordinator_get_component(TransformComponent, coordinator, entity) color := coordinator_get_component(ColorComponent, 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] pos_mat := linalg.matrix4_translate_f32(transform.position) rot_mat := linalg.matrix4_from_yaw_pitch_roll_f32(transform.rotation.y, transform.rotation.x, transform.rotation.z) model := pos_mat * rot_mat sg.apply_pipeline(material.pipeline) sg.apply_bindings({ images = { shaders.IMG_tex = material.image }, samplers = { shaders.SMP_smp = material.sampler }, vertex_buffers = { 0 = mesh.vertex_buffer }, index_buffer = mesh.index_buffer, }) sg.apply_uniforms(shaders.UB_VsParams, sg_range(&shaders.Vsparams{ mvp = proj * view * model, col = color.color })) sg.draw(0, 36, 1) } 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), } }