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, } RenderSystem :: struct { using base: SystemBase, shader: sg.Shader, pipeline: sg.Pipeline, vertex_buffer: sg.Buffer, index_buffer: sg.Buffer, image: sg.Image, sampler: sg.Sampler, camera: struct { pos: Vec3, target: Vec3, look: Vec2, }, } init_render_system :: proc(render_system: ^RenderSystem) { 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)), }) 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, }, }) 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(vertices) }) 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(indices), }) 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.sampler = sampler render_system.shader = shader render_system.image = image render_system.pipeline = pipeline render_system.vertex_buffer = vertex_buffer render_system.index_buffer = index_buffer render_system.camera = { pos = { 30, 0, 60 }, target = { 0, 0, 1 }, } } delete_render_system :: proc(render_system: ^RenderSystem) { sg.destroy_buffer(render_system.index_buffer) sg.destroy_buffer(render_system.vertex_buffer) sg.destroy_image(render_system.image) sg.destroy_sampler(render_system.sampler) sg.destroy_pipeline(render_system.pipeline) sg.destroy_shader(render_system.shader) sg.shutdown() } render_system_update :: proc(render_system: ^RenderSystem, coordinator: ^Coordinator, dt: f32, key_down: #sparse[sa.Keycode]bool, mouse_move: Vec2) { update_camera(render_system, dt, key_down, mouse_move) proj := linalg.matrix4_perspective_f32(70, sa.widthf() / sa.heightf(), 0.001, 1000) view := linalg.matrix4_look_at_f32(render_system.camera.pos, render_system.camera.target, { 0, 1, 0 } ) sg.begin_pass({ swapchain = sh.glue_swapchain() }) sg.apply_pipeline(render_system.pipeline) sg.apply_bindings({ vertex_buffers = { 0 = render_system.vertex_buffer }, index_buffer = render_system.index_buffer, images = { shaders.IMG_tex = render_system.image }, samplers = { shaders.SMP_smp = render_system.sampler }, }) for entity in render_system.entities { transform := coordinator_get_component(Transform, coordinator, entity) color := coordinator_get_component(Color, coordinator, entity) 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_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() } @(private) update_camera :: proc(render_system: ^RenderSystem, dt: f32, key_down: #sparse[sa.Keycode]bool, mouse_move: Vec2) { MOVE_SPEED :: 3 LOOK_SENSITIVITY :: 0.15 move_input: Vec3 if key_down[.W] do move_input.y = 1 else if key_down[.S] do move_input.y = -1 if key_down[.D] do move_input.x = 1 else if key_down[.A] do move_input.x = -1 if key_down[.SPACE] do move_input.z = 1 else if key_down[.LEFT_SHIFT] do move_input.z = -1 look_input: Vec2 = -mouse_move * LOOK_SENSITIVITY render_system.camera.look += look_input render_system.camera.look.x = math.wrap(render_system.camera.look.x, 360) render_system.camera.look.y = math.clamp(render_system.camera.look.y, -89.5, 89.5) look_mat := linalg.matrix4_from_yaw_pitch_roll_f32( linalg.to_radians(render_system.camera.look.x), linalg.to_radians(render_system.camera.look.y), 0, ) forward := (look_mat * Vec4{0, 0, -1, 1}).xyz right := (look_mat * Vec4{1, 0, 0, 1}).xyz up := (look_mat * Vec4{0, 1, 0, 1}).xyz move_dir := forward * move_input.y + right * move_input.x + up * move_input.z motion := linalg.normalize0(move_dir) * MOVE_SPEED * dt render_system.camera.pos += motion render_system.camera.target = render_system.camera.pos + forward } 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), } }