const gfx = @import("gfx"); const hid = @import("hid"); const ona = @import("ona"); const std = @import("std"); const Spawned = struct { transform: gfx.Transform2D, lifetime_seconds: f32, }; const Visuals = struct { spawned_keyboards: ona.stack.Parallel(Spawned) = .{}, spawned_mouses: ona.stack.Parallel(Spawned) = .{}, keyboard_icon: gfx.Texture = .default, mouse_icon: gfx.Texture = .default, random: std.Random.Xoroshiro128, mouse_position: @Vector(2, f32) = @splat(0), }; fn cleanup(visuals: ona.Write(Visuals)) !void { visuals.state.spawned_keyboards.deinit(); visuals.state.spawned_mouses.deinit(); } fn load(visuals: ona.Write(Visuals)) !void { const initial_spawn_capacity = 1024; try visuals.state.spawned_keyboards.grow(initial_spawn_capacity); try visuals.state.spawned_mouses.grow(initial_spawn_capacity); } pub const main = ona.App.setup. with_module(gfx). with_module(hid). with_state(Visuals{.random = std.Random.Xoroshiro128.init(47342563891212)}). with_system(.load, ona.system_fn(load), .{.label = "load visuals"}). with_system(.update, ona.system_fn(update), .{.label = "spawn visuals"}). with_system(.render, ona.system_fn(render), .{.label = "render visuals"}). with_system(.exit, ona.system_fn(cleanup), .{.label = "clean up visuals"}).build(); fn update(visuals: ona.Write(Visuals), events: ona.Receive(hid.Event), display: ona.Read(gfx.Display)) !void { update_spawned(&visuals.state.spawned_keyboards); update_spawned(&visuals.state.spawned_mouses); const random = visuals.state.random.random(); const width: f32 = @floatFromInt(display.state.width); const height: f32 = @floatFromInt(display.state.height); const icon_scale = .{8, 8}; for (events.messages()) |event| { switch (event) { .key_down => { try visuals.state.spawned_keyboards.push_grow(.{ .lifetime_seconds = 2.5 + (5 * random.float(f32)), .transform = gfx.transform_2d(.{ .translation = .{width * random.float(f32), height}, .rotation = std.math.pi * random.float(f32), .scale = icon_scale, }), }); }, .mouse_down => { try visuals.state.spawned_mouses.push_grow(.{ .lifetime_seconds = 2.5 + (5 * random.float(f32)), .transform = gfx.transform_2d(.{ .translation = visuals.state.mouse_position, .rotation = std.math.pi * random.float(f32), .scale = icon_scale, }), }); }, .mouse_motion => |motion| { visuals.state.mouse_position = motion.absolute_position; }, else => {} } } } fn update_spawned(spawned: *ona.stack.Parallel(Spawned)) void { const float_speed = 6; for (spawned.values.slice(.transform)) |*transform| { transform.* = transform.translated(.{0, -float_speed}); } { var range = spawned.len(); var index: usize = 0; while (index < range) : (index += 1) { const lifetime_seconds = spawned.values.get(.lifetime_seconds, index).?; lifetime_seconds.* -= 1.0 / 60.0; if (lifetime_seconds.* <= 0) { range -= 1; std.mem.swap(f32, lifetime_seconds, spawned.values.get(.lifetime_seconds, range).?); } } std.debug.assert(spawned.pop_many(spawned.len() - range)); } } fn render(visuals: ona.Write(Visuals), commands: gfx.Commands) !void { for (visuals.state.spawned_keyboards.values.slice(.transform)) |transform| { try commands.draw_texture(.{ .texture = visuals.state.keyboard_icon, .transform = transform, }); } for (visuals.state.spawned_mouses.values.slice(.transform)) |transform| { try commands.draw_texture(.{ .texture = visuals.state.keyboard_icon, .transform = transform, }); } }