const std = @import("std"); const GeneralPurposeAllocator = std.heap.GeneralPurposeAllocator(.{}); const core = @import("mach").core; const Renderer = @import("./renderer.zig"); const flecszigble = @import("flecs-zig-ble"); const Context = flecszigble.Context(void); const World = Context.World; const Iter = Context.Iter; const flecs = flecszigble.flecs; const OnLoad = flecs.pipeline.OnLoad; const OnUpdate = flecs.pipeline.OnUpdate; const OnStore = flecs.pipeline.OnStore; pub const App = @This(); gpa: GeneralPurposeAllocator, allocator: std.mem.Allocator, random: std.Random, world: *World, renderer: *Renderer, pub fn init(app: *App) !void { try core.init(.{ // Request high performance = prefer dedicated GPU when available. .power_preference = .high_performance, }); // Set up a "general purpose allocator" that will handle allocations for // the lifetime of the application, for which a more specific allocation // strategy is not necessary. // // Here, `gpa` is an instance of this allocator, which handles the // allocation logic, while `allocator` is the interface through which // functions such as `alloc` and `free` are called. app.gpa = GeneralPurposeAllocator{}; app.allocator = app.gpa.allocator(); // Create a pseudo-random number generator, but initialize it with // a constant seed so we always get the same result when launching. var prng = std.Random.DefaultPrng.init(0); app.random = prng.random(); // Initialize flecs-zig-ble and create a new Flecs world. // // Flecs is a library for using Entity Component System (ECS) design // intuitively and efficiently. Entities can be created and have various // components – and relationships to other entities – added to them. // // This data is stored in an in-memory database, which can be queried // and modified. For example by using systems, you are able to get // entities that match a set of components, and modify their values. flecszigble.init(app.allocator); const world = try World.init(); app.world = world; // Create a singleton component for accessing the `App` from ECS. _ = try world.singleton("App", *App, app); // TODO: The way we register systems using flecs-zig-ble is still very WIP. _ = try world.system("PollEvents", pollEvents, OnLoad, "App"); const s = try world.system("UpdateWindowTitle", updateWindowTitle, OnStore, ""); // Set the update interval of the `UpdateWindowTitle` system to 1 second. _ = flecszigble.c.ecs_set_interval(world.raw, s.raw, 1.0); app.renderer = try Renderer.init(app); } pub fn deinit(app: *App) void { // Using `defer` here, so we can specify them // in the order they were created in `init`. defer core.deinit(); defer _ = app.gpa.deinit(); defer app.world.deinit(); defer app.renderer.deinit(); } /// Update function called by Mach Core, which we'll just use to update Flecs. /// This will then process all the systems we've registered in our pipeline. pub fn update(app: *App) !bool { return !app.world.progress(0.0); } /// Read events from the OS such as input. pub fn pollEvents(it: Iter) void { const app = it.field(*App, 1)[0]; var pollIter = core.pollEvents(); while (pollIter.next()) |event| { switch (event) { // Allow the renderer to act on the window being resized. // This is required so we can resize necessary buffers. .framebuffer_resize => |_| app.renderer.resize(), // Close the window when requested, such as when // pressing the X button in the window title bar. .close => it.world.quit(), else => {}, } } } /// Update the window title to show FPS and input frequency. pub fn updateWindowTitle(_: Iter) void { core.printTitle( "Triangle [ {d}fps ] [ Input {d}hz ]", .{ core.frameRate(), core.inputRate() }, ) catch @panic("Title too long!"); }