const std = @import("std"); const zaudio = @import("zaudio"); const zglfw = @import("zglfw"); const zgpu = @import("zgpu"); const zgui = @import("zgui"); const ztracy = @import("ztracy"); const game = @import("game.zig"); const Log = @import("Log.zig"); pub const min_framerate = 30.0; pub const min_frametime = 1.0 / min_framerate; pub const min_window_height = 360; pub const min_window_width = 640; pub const temp_allocator_capacity = 16 * 1024 * 1024; pub const window_title = "voxel-game"; pub var allocator: std.mem.Allocator = undefined; pub var audio_engine: *zaudio.Engine = undefined; pub var gctx: *zgpu.GraphicsContext = undefined; pub var logs: std.ArrayListUnmanaged(Log) = undefined; pub var temp_allocator: std.mem.Allocator = undefined; pub var window: *zglfw.Window = undefined; pub const std_options: std.Options = .{ .logFn = logFn, }; pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); var ta = ztracy.TracyAllocator.init(gpa.allocator()); var fba = std.heap.FixedBufferAllocator.init(&struct { pub var buffer: [temp_allocator_capacity]u8 = undefined; }.buffer); allocator = ta.allocator(); temp_allocator = fba.threadSafeAllocator(); defer { for (logs.items) |log| { allocator.free(log.message); } logs.deinit(allocator); } // --- zaudio -------------------------------------------------------------- const zone_init_zaudio = ztracy.ZoneN(@src(), "Init zaudio"); zaudio.init(allocator); defer zaudio.deinit(); audio_engine = try zaudio.Engine.create(null); defer audio_engine.destroy(); zone_init_zaudio.End(); // --- zglfw --------------------------------------------------------------- const zone_init_zglfw = ztracy.ZoneN(@src(), "Init zglfw"); try zglfw.init(); defer zglfw.terminate(); const monitor = zglfw.getPrimaryMonitor() orelse return error.PrimaryMonitorNotFound; const video_mode = try monitor.getVideoMode(); zglfw.windowHint(.client_api, .no_api); window = try zglfw.Window.create(video_mode.width, video_mode.height, window_title, monitor); window.setSizeLimits( min_window_width, min_window_height, -1, -1, ); defer window.destroy(); std.debug.assert(zglfw.setCharCallback(window, game.charCallback) == null); std.debug.assert(zglfw.setCursorPosCallback(window, game.cursorPosCallback) == null); std.debug.assert(zglfw.setKeyCallback(window, game.keyCallback) == null); std.debug.assert(zglfw.setMouseButtonCallback(window, game.mouseButtonCallback) == null); std.debug.assert(zglfw.setScrollCallback(window, game.scrollCallback) == null); zone_init_zglfw.End(); // --- zgpu ---------------------------------------------------------------- const zone_init_zgpu = ztracy.ZoneN(@src(), "Init zgpu"); gctx = try zgpu.GraphicsContext.create(allocator, .{ .window = window, .fn_getTime = @ptrCast(&zglfw.getTime), .fn_getFramebufferSize = @ptrCast(&zglfw.Window.getFramebufferSize), .fn_getWin32Window = @ptrCast(&zglfw.getWin32Window), .fn_getX11Display = @ptrCast(&zglfw.getX11Display), .fn_getX11Window = @ptrCast(&zglfw.getX11Window), .fn_getWaylandDisplay = @ptrCast(&zglfw.getWaylandDisplay), .fn_getWaylandSurface = @ptrCast(&zglfw.getWaylandWindow), .fn_getCocoaWindow = @ptrCast(&zglfw.getCocoaWindow), }, .{}); defer gctx.destroy(allocator); zone_init_zgpu.End(); // --- zgui ---------------------------------------------------------------- const zone_init_zgui = ztracy.ZoneN(@src(), "Init zgui"); zgui.init(allocator); defer zgui.deinit(); zgui.backend.init( window, gctx.device, @intFromEnum(zgpu.GraphicsContext.swapchain_format), @intFromEnum(zgpu.wgpu.TextureFormat.undef), ); defer zgui.backend.deinit(); zgui.io.setIniFilename(null); zone_init_zgui.End(); // --- game ---------------------------------------------------------------- const zone_init_game = ztracy.ZoneN(@src(), "Init game"); try game.init(); defer game.deinit(); zone_init_game.End(); // --- main loop ----------------------------------------------------------- const zone_main_loop = ztracy.ZoneN(@src(), "Main loop"); var t2 = zglfw.getTime(); while (!window.shouldClose()) { ztracy.FrameMark(); fba.reset(); const t1 = zglfw.getTime(); const dt: f32 = @floatCast(@max(t2 - t1, min_frametime)); t2 = t1; zglfw.pollEvents(); zgui.backend.newFrame( gctx.swapchain_descriptor.width, gctx.swapchain_descriptor.height, ); game.update(dt); const swapchain_texture_view = gctx.swapchain.getCurrentTextureView(); defer swapchain_texture_view.release(); const commands = commands: { const encoder = gctx.device.createCommandEncoder(null); defer encoder.release(); { const pass = zgpu.beginRenderPassSimple( encoder, .load, swapchain_texture_view, null, null, null, ); defer zgpu.endReleasePass(pass); zgui.backend.draw(pass); } break :commands encoder.finish(null); }; defer commands.release(); gctx.submit(&.{commands}); _ = gctx.present(); } zone_main_loop.End(); } fn logFn( comptime level: std.log.Level, comptime _: @Type(.enum_literal), comptime fmt: []const u8, args: anytype, ) void { const message = std.fmt.allocPrint(allocator, fmt, args) catch return; logs.append(allocator, Log.init(level, message)) catch { allocator.free(message); return; }; }