const gfx = @import("gfx"); const hid = @import("hid"); const ona = @import("ona"); const std = @import("std"); const Spawned = struct { visual: struct { color: gfx.Color, transform: gfx.Transform2D, }, lifetime_seconds: f32, }; const Visuals = struct { spawned: ona.stack.Parallel(Spawned) = .{}, random: std.Random.Xoroshiro128, mouse_position: @Vector(2, f32) = @splat(0), }; fn cleanup(visuals: ona.Write(Visuals)) !void { visuals.state.spawned.deinit(); visuals.state.spawned.deinit(); } fn load(visuals: ona.Write(Visuals)) !void { const initial_spawn_capacity = 1024; try visuals.state.spawned.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); 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.push_grow(.{ .lifetime_seconds = 2.5 + (5 * random.float(f32)), .visual = .{ .color = .{random.float(f32), random.float(f32), random.float(f32), 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.push_grow(.{ .lifetime_seconds = 2.5 + (5 * random.float(f32)), .visual = .{ .color = .{random.float(f32), random.float(f32), random.float(f32), 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(.visual)) |*visual| { visual.transform = visual.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.values.slice(.visual)) |visual| { try commands.draw_rect(.{ .transform = visual.transform, .color = visual.color, }); } }