package main 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 "ecs" Vec2 :: [2]f32 Vec3 :: [3]f32 Vec4 :: [4]f32 Mat4 :: matrix[4, 4]f32 VertexData :: struct { pos: Vec3, col: sg.Color, uv: Vec2, } Scene :: struct { physics_system: ^ecs.PhysicsSystem, entities: []ecs.EntityID, } Globals :: struct { shader: sg.Shader, pipeline: sg.Pipeline, vertex_buffer: sg.Buffer, index_buffer: sg.Buffer, image: sg.Image, sampler: sg.Sampler, rotation: f32, camera: struct { pos: Vec3, target: Vec3, look: Vec2, }, coordinator: ecs.Coordinator, scene: Scene, dt: f32, } g: ^Globals mouse_move: Vec2 key_down: #sparse[sa.Keycode]bool default_context: runtime.Context main :: proc() { context.logger = log.create_console_logger() tracking_allocator: mem.Tracking_Allocator mem.tracking_allocator_init(&tracking_allocator, context.allocator) context.allocator = mem.tracking_allocator(&tracking_allocator) defer reset_tracking_allocator(&tracking_allocator) default_context = context sa.run({ window_title = "Ecs Test", allocator = sa.Allocator(sh.allocator(&default_context)), logger = sa.Logger(sh.logger(&default_context)), init_cb = init_cb, frame_cb = frame_cb, cleanup_cb = cleanup_cb, event_cb = event_cb, }) g.coordinator = ecs.coordinator_create() defer ecs.coordinator_destroy(&g.coordinator) g.scene = create_scene(&g.coordinator) defer delete(g.scene.entities) } init_cb :: proc "c" () { context = default_context sg.setup({ environment = sh.glue_environment(), allocator = sg.Allocator(sh.allocator(&default_context)), logger = sg.Logger(sh.logger(&default_context)), }) sa.show_mouse(false) sa.lock_mouse(true) g = new(Globals) g.camera = { pos = { 0, 0, 2 }, target = { 0, 0, 1 }, } g.shader = sg.make_shader(main_shader_desc(sg.query_backend())) g.pipeline = sg.make_pipeline({ shader = g.shader, layout = { attrs = { ATTR_main_pos = { format = .FLOAT3 }, ATTR_main_col = { format = .FLOAT4 }, 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 }, col = { 1, 1, 1, 1 }, uv = { 0, 0 } }, { pos = { 0.5, -0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 0 } }, { pos = { 0.5, 0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 1 } }, { pos = { -0.5, 0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 1 } }, { pos = { -0.5, -0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 0 } }, { pos = { 0.5, -0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 0 } }, { pos = { 0.5, 0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 1 } }, { pos = { -0.5, 0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 1 } }, { pos = { -0.5, 0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 0 } }, { pos = { 0.5, 0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 0 } }, { pos = { 0.5, 0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 1 } }, { pos = { -0.5, 0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 1 } }, { pos = { -0.5, -0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 0 } }, { pos = { 0.5, -0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 0 } }, { pos = { 0.5, -0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 1 } }, { pos = { -0.5, -0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 1 } }, { pos = { 0.5, -0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 0 } }, { pos = { 0.5, -0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 0 } }, { pos = { 0.5, 0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 1 } }, { pos = { 0.5, 0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 1 } }, { pos = { -0.5, -0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 0 } }, { pos = { -0.5, -0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 0 } }, { pos = { -0.5, 0.5, -0.5 }, col = { 1, 1, 1, 1 }, uv = { 0, 1 } }, { pos = { -0.5, 0.5, 0.5 }, col = { 1, 1, 1, 1 }, uv = { 1, 1 } }, } g.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, } g.index_buffer = sg.make_buffer({ usage = { index_buffer = true }, data = sg_range(indices), }) w, h: i32 pixels := stbi.load("res/texture_blue.png", &w, &h, nil, 4) assert(pixels != nil) defer(stbi.image_free(pixels)) g.image = sg.make_image({ width = w, height = h, pixel_format = .RGBA8, data = { subimage = { 0 = { 0 = { ptr = pixels, size = uint(w * h * 4) } } } } }) g.sampler = sg.make_sampler({}) } frame_cb:: proc "c" () { context = default_context if key_down[.ESCAPE] { sa.quit() return } start_time := time.now() //ecs.physics_system_update(g.scene.physics_system, &g.coordinator, g.dt) // seg fault g.dt = f32(sa.frame_duration()) update_camera(g.dt) g.rotation += linalg.to_radians(50 * g.dt) proj := linalg.matrix4_perspective_f32(70, sa.widthf() / sa.heightf(), 0.001, 1000) view := linalg.matrix4_look_at_f32(g.camera.pos, g.camera.target, { 0, 1, 0 } ) Object :: struct { pos: Vec3, rot: Vec3, } objects := []Object { { { -2, 0, 0 }, { 0, 0, 0 } }, { { 0, 0, 0 }, { 0, 0, 0 } }, { { 2, 0, 0 }, { 0, 0, 0 } }, } sg.begin_pass({ swapchain = sh.glue_swapchain() }) sg.apply_pipeline(g.pipeline) sg.apply_bindings({ vertex_buffers = { 0 = g.vertex_buffer }, index_buffer = g.index_buffer, images = { IMG_tex = g.image }, samplers = { SMP_smp = g.sampler }, }) for obj in objects { model := linalg.matrix4_translate_f32(obj.pos) * linalg.matrix4_from_yaw_pitch_roll_f32(obj.rot.y, obj.rot.x, obj.rot.z) sg.apply_uniforms(UB_VsParams, sg_range(&Vsparams { mvp = proj * view * model })) sg.draw(0, 36, 1) } sg.end_pass() sg.commit() mouse_move = {} } update_camera :: proc(dt: f32) { 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 g.camera.look += look_input g.camera.look.x = math.wrap(g.camera.look.x, 360) g.camera.look.y = math.clamp(g.camera.look.y, -89.5, 89.5) look_mat := linalg.matrix4_from_yaw_pitch_roll_f32( linalg.to_radians(g.camera.look.x), linalg.to_radians(g.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 g.camera.pos += motion g.camera.target = g.camera.pos + forward } cleanup_cb :: proc "c" () { context = default_context sg.destroy_buffer(g.index_buffer) sg.destroy_buffer(g.vertex_buffer) sg.destroy_image(g.image) sg.destroy_sampler(g.sampler) sg.destroy_pipeline(g.pipeline) sg.destroy_shader(g.shader) free(g) sg.shutdown() } event_cb :: proc "c" (event: ^sa.Event) { context = default_context #partial switch event.type { case .MOUSE_MOVE: mouse_move += {event.mouse_dx, event.mouse_dy} case .KEY_DOWN: key_down[event.key_code] = true case .KEY_UP: key_down[event.key_code] = false } } create_scene :: proc(coordinator: ^ecs.Coordinator) -> Scene { scene := Scene{} ecs.coordinator_register_component(ecs.Gravity, coordinator) ecs.coordinator_register_component(ecs.RigidBody, coordinator) ecs.coordinator_register_component(ecs.Transform, coordinator) scene.physics_system = ecs.coordinator_register_system(ecs.PhysicsSystem, coordinator) signature := ecs.signature_create() ecs.signature_set(&signature, ecs.coordinator_get_component_type(ecs.Gravity, coordinator)) ecs.signature_set(&signature, ecs.coordinator_get_component_type(ecs.RigidBody, coordinator)) ecs.signature_set(&signature, ecs.coordinator_get_component_type(ecs.Transform, coordinator)) ecs.coordinator_set_system_signature(ecs.PhysicsSystem, coordinator, signature) scene.entities = make([]ecs.EntityID, ecs.ENTITY_MAX) for &entity in scene.entities { entity = ecs.coordinator_create_entity(coordinator) ecs.coordinator_add_component( ecs.Gravity, coordinator, entity, ecs.Gravity{ ecs.Vec3{0.0, -9.82, 0.0} }) ecs.coordinator_add_component( ecs.RigidBody, coordinator, entity, ecs.RigidBody{ velocity = ecs.Vec3{0.0, 0.0, 0.0}, acceleration = ecs.Vec3{0.0, 0.0, 0.0}, }) ecs.coordinator_add_component( ecs.Transform, coordinator, entity, ecs.Transform{ position = ecs.Vec3{0.0, 0.0, 0.0}, rotation = ecs.Vec3{0.0, 0.0, 0.0}, scale = ecs.Vec3{1.0, 1.0, 1.0}, }) } return scene } 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), } } reset_tracking_allocator :: proc(track: ^mem.Tracking_Allocator) { if len(track.allocation_map) > 0 { fmt.eprintf("=== %v allocations not freed: ===\n", len(track.allocation_map)) for _, entry in track.allocation_map { fmt.eprintf("- %v bytes @ %v\n", entry.size, entry.location) } } if len(track.bad_free_array) > 0 { fmt.eprintf("=== %v incorrect frees: ===\n", len(track.bad_free_array)) for entry in track.bad_free_array { fmt.eprintf("- %p @ %v\n", entry.memory, entry.location) } } mem.tracking_allocator_destroy(track) }