From 61fb60ff1c6cf38b31c7f7e5fb74a256f26a22f9 Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Sun, 18 Jan 2026 10:30:43 +0200 Subject: [PATCH] merge input subsystem into engine --- .gitignore | 2 + build.zig | 1 + libs/stb/build.zig | 15 + libs/stb/src/stb_rect_pack.zig | 52 +++ libs/stb/src/stb_rect_pack_impl.c | 2 + src/assets.zig | 23 +- src/engine/input.zig | 239 -------------- src/engine/root.zig | 521 ++++++++++++++++++++---------- src/game.zig | 46 ++- src/main.zig | 5 +- 10 files changed, 456 insertions(+), 450 deletions(-) create mode 100644 .gitignore create mode 100644 libs/stb/src/stb_rect_pack.zig create mode 100644 libs/stb/src/stb_rect_pack_impl.c delete mode 100644 src/engine/input.zig diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..880cd5d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +zig-out +.zig-cache diff --git a/build.zig b/build.zig index 004e8b7..db5ef6e 100644 --- a/build.zig +++ b/build.zig @@ -60,6 +60,7 @@ pub fn build(b: *std.Build) !void { const dep_stb = b.dependency("stb", .{}); mod_main.addImport("stb_image", dep_stb.module("stb_image")); mod_main.addImport("stb_vorbis", dep_stb.module("stb_vorbis")); + mod_main.addImport("stb_rect_pack", dep_stb.module("stb_rect_pack")); const dep_fontstash_c = b.dependency("fontstash_c", .{}); mod_main.addIncludePath(dep_fontstash_c.path("src")); diff --git a/libs/stb/build.zig b/libs/stb/build.zig index 963b76f..54a79ed 100644 --- a/libs/stb/build.zig +++ b/libs/stb/build.zig @@ -35,4 +35,19 @@ pub fn build(b: *std.Build) void { .flags = &.{} }); } + + { + const mod_stb_rect_pack = b.addModule("stb_rect_pack", .{ + .target = target, + .optimize = optimize, + .root_source_file = b.path("src/stb_rect_pack.zig"), + .link_libc = true, + }); + + mod_stb_rect_pack.addIncludePath(stb_dependency.path(".")); + mod_stb_rect_pack.addCSourceFile(.{ + .file = b.path("src/stb_rect_pack_impl.c"), + .flags = &.{} + }); + } } diff --git a/libs/stb/src/stb_rect_pack.zig b/libs/stb/src/stb_rect_pack.zig new file mode 100644 index 0000000..77de6d1 --- /dev/null +++ b/libs/stb/src/stb_rect_pack.zig @@ -0,0 +1,52 @@ + +const c = @cImport({ + @cInclude("stb_rect_pack.h"); +}); + +const STBRectPack = @This(); + +pub const Node = c.stbrp_node; +pub const Rect = c.stbrp_rect; + +pub const Options = struct { + const Heuristic = enum(i32) { + bl_sort_height = c.STBRP_HEURISTIC_Skyline_BL_sortHeight, + bf_sort_height = c.STBRP_HEURISTIC_Skyline_BF_sortHeight, + + pub const default: Heuristic = @enumFromInt(c.STBRP_HEURISTIC_Skyline_default); + }; + + width: u31, + height: u31, + nodes: []Node, + allow_out_of_memory: bool = false, + heuristic: Heuristic = .default, +}; + +ctx: c.stbrp_context, + +pub fn init(options: Options) STBRectPack { + var ctx: c.stbrp_context = undefined; + c.stbrp_init_target( + &ctx, + options.width, + options.height, + options.nodes.ptr, + options.nodes.len + ); + + if (options.allow_out_of_memory) { + c.stbrp_setup_allow_out_of_mem(&ctx, 1); + } + + if (options.heuristic != .default) { + c.stbrp_setup_heuristic(&ctx, options.heuristic); + } + + return STBRectPack{ .ctx = ctx }; +} + +pub fn packRects(self: *STBRectPack, rects: []Rect) bool { + const success = c.stbrp_pack_rects(&self.ctx, rects.ptr, rects.len); + return success != 0; +} diff --git a/libs/stb/src/stb_rect_pack_impl.c b/libs/stb/src/stb_rect_pack_impl.c new file mode 100644 index 0000000..31c4106 --- /dev/null +++ b/libs/stb/src/stb_rect_pack_impl.c @@ -0,0 +1,2 @@ +#define STB_RECT_PACK_IMPLEMENTATION +#include "stb_rect_pack.h" diff --git a/src/assets.zig b/src/assets.zig index 8ea3e31..de1ce1d 100644 --- a/src/assets.zig +++ b/src/assets.zig @@ -5,6 +5,8 @@ const Engine = @import("./engine/root.zig"); const Gfx = Engine.Graphics; const Audio = Engine.Audio; +const STBRectPack = @import("stb_rect_pack"); + const Assets = @This(); const FontName = enum { @@ -15,30 +17,21 @@ const FontName = enum { const EnumArray = std.EnumArray(FontName, Gfx.Font.Id); }; -font_id: FontName.EnumArray, +pub var font_id: FontName.EnumArray = undefined; +pub var wood01: Audio.Data.Id = undefined; -wood01: Audio.Data.Id, - -pub fn init(gpa: std.mem.Allocator) !Assets { - _ = gpa; // autofix - const font_id_array: FontName.EnumArray = .init(.{ +pub fn init() !void { + font_id = .init(.{ .regular = try Gfx.addFont("regular", @embedFile("assets/roboto-font/Roboto-Regular.ttf")), .bold = try Gfx.addFont("bold", @embedFile("assets/roboto-font/Roboto-Bold.ttf")), .italic = try Gfx.addFont("italic", @embedFile("assets/roboto-font/Roboto-Italic.ttf")), }); - const wood01 = try Audio.load(.{ + wood01 = try Audio.load(.{ .format = .vorbis, .data = @embedFile("assets/wood01.ogg"), }); - - return Assets{ - .font_id = font_id_array, - .wood01 = wood01 - }; } -pub fn deinit(self: *Assets, gpa: std.mem.Allocator) void { - _ = self; // autofix - _ = gpa; // autofix +pub fn deinit() void { } diff --git a/src/engine/input.zig b/src/engine/input.zig deleted file mode 100644 index 0cf6518..0000000 --- a/src/engine/input.zig +++ /dev/null @@ -1,239 +0,0 @@ -const std = @import("std"); -const sokol = @import("sokol"); - -const Engine = @import("./root.zig"); -const Nanoseconds = Engine.Nanoseconds; - -const Math = @import("./math.zig"); -const Vec2 = Math.Vec2; - -const Input = @This(); - -pub const KeyCode = enum(std.math.IntFittingRange(0, sokol.app.max_keycodes-1)) { - SPACE = 32, - APOSTROPHE = 39, - COMMA = 44, - MINUS = 45, - PERIOD = 46, - SLASH = 47, - _0 = 48, - _1 = 49, - _2 = 50, - _3 = 51, - _4 = 52, - _5 = 53, - _6 = 54, - _7 = 55, - _8 = 56, - _9 = 57, - SEMICOLON = 59, - EQUAL = 61, - A = 65, - B = 66, - C = 67, - D = 68, - E = 69, - F = 70, - G = 71, - H = 72, - I = 73, - J = 74, - K = 75, - L = 76, - M = 77, - N = 78, - O = 79, - P = 80, - Q = 81, - R = 82, - S = 83, - T = 84, - U = 85, - V = 86, - W = 87, - X = 88, - Y = 89, - Z = 90, - LEFT_BRACKET = 91, - BACKSLASH = 92, - RIGHT_BRACKET = 93, - GRAVE_ACCENT = 96, - WORLD_1 = 161, - WORLD_2 = 162, - ESCAPE = 256, - ENTER = 257, - TAB = 258, - BACKSPACE = 259, - INSERT = 260, - DELETE = 261, - RIGHT = 262, - LEFT = 263, - DOWN = 264, - UP = 265, - PAGE_UP = 266, - PAGE_DOWN = 267, - HOME = 268, - END = 269, - CAPS_LOCK = 280, - SCROLL_LOCK = 281, - NUM_LOCK = 282, - PRINT_SCREEN = 283, - PAUSE = 284, - F1 = 290, - F2 = 291, - F3 = 292, - F4 = 293, - F5 = 294, - F6 = 295, - F7 = 296, - F8 = 297, - F9 = 298, - F10 = 299, - F11 = 300, - F12 = 301, - F13 = 302, - F14 = 303, - F15 = 304, - F16 = 305, - F17 = 306, - F18 = 307, - F19 = 308, - F20 = 309, - F21 = 310, - F22 = 311, - F23 = 312, - F24 = 313, - F25 = 314, - KP_0 = 320, - KP_1 = 321, - KP_2 = 322, - KP_3 = 323, - KP_4 = 324, - KP_5 = 325, - KP_6 = 326, - KP_7 = 327, - KP_8 = 328, - KP_9 = 329, - KP_DECIMAL = 330, - KP_DIVIDE = 331, - KP_MULTIPLY = 332, - KP_SUBTRACT = 333, - KP_ADD = 334, - KP_ENTER = 335, - KP_EQUAL = 336, - LEFT_SHIFT = 340, - LEFT_CONTROL = 341, - LEFT_ALT = 342, - LEFT_SUPER = 343, - RIGHT_SHIFT = 344, - RIGHT_CONTROL = 345, - RIGHT_ALT = 346, - RIGHT_SUPER = 347, - MENU = 348, -}; - -pub const KeyState = struct { - down: bool, - pressed: bool, - released: bool, - down_duration: ?f64, - - pub const RepeatOptions = struct { - first_at: f64 = 0, - period: f64 - }; - - pub fn repeat(self: KeyState, last_repeat_at: *?f64, opts: RepeatOptions) bool { - if (!self.down) { - last_repeat_at.* = null; - return false; - } - - const down_duration = self.down_duration.?; - if (last_repeat_at.* != null) { - if (down_duration >= last_repeat_at.*.? + opts.period) { - last_repeat_at.* = last_repeat_at.*.? + opts.period; - return true; - } - } else { - if (down_duration >= opts.first_at) { - last_repeat_at.* = opts.first_at; - return true; - } - } - - return false; - } -}; - -pub const Mouse = struct { - pub const Button = enum { - left, - right, - middle, - - pub fn fromSokol(mouse_button: sokol.app.Mousebutton) ?Button { - return switch(mouse_button) { - .LEFT => Button.left, - .RIGHT => Button.right, - .MIDDLE => Button.middle, - else => null - }; - } - }; - - position: ?Vec2, - buttons: std.EnumSet(Button), - - pub const empty = Mouse{ - .position = null, - .buttons = .initEmpty() - }; -}; - -down_keys: std.EnumSet(KeyCode), -pressed_keys: std.EnumSet(KeyCode), -released_keys: std.EnumSet(KeyCode), -pressed_keys_at: std.EnumMap(KeyCode, Nanoseconds), - -mouse: Mouse, - -pub const empty = Input{ - .down_keys = .initEmpty(), - .pressed_keys = .initEmpty(), - .released_keys = .initEmpty(), - .pressed_keys_at = .init(.{}), - .mouse = .empty -}; - -pub fn isKeyDown(self: *Input, key_code: KeyCode) bool { - return self.down_keys.contains(key_code); -} - -pub fn getKeyDownDuration(self: *Input, frame: Engine.Frame, key_code: KeyCode) ?f64 { - if (!self.isKeyDown(key_code)) { - return null; - } - - const pressed_at_ns = self.pressed_keys_at.get(key_code).?; - const duration_ns = frame.time_ns - pressed_at_ns; - - return @as(f64, @floatFromInt(duration_ns)) / std.time.ns_per_s; -} - -pub fn isKeyPressed(self: *Input, key_code: KeyCode) bool { - return self.pressed_keys.contains(key_code); -} - -pub fn isKeyReleased(self: *Input, key_code: KeyCode) bool { - return self.released_keys.contains(key_code); -} - -pub fn getKeyState(self: *Input, frame: Engine.Frame, key_code: KeyCode) KeyState { - return KeyState{ - .down = self.isKeyDown(key_code), - .released = self.isKeyReleased(key_code), - .pressed = self.isKeyPressed(key_code), - .down_duration = self.getKeyDownDuration(frame, key_code) - }; -} diff --git a/src/engine/root.zig b/src/engine/root.zig index 1a7bfe2..28ddb64 100644 --- a/src/engine/root.zig +++ b/src/engine/root.zig @@ -8,8 +8,6 @@ const sapp = sokol.app; pub const Math = @import("./math.zig"); pub const Vec2 = Math.Vec2; -pub const Input = @import("./input.zig"); - const ScreenScalar = @import("./screen_scaler.zig"); pub const imgui = @import("./imgui.zig"); pub const Graphics = @import("./graphics.zig"); @@ -27,43 +25,240 @@ const Engine = @This(); pub const Nanoseconds = u64; -pub const Event = union(enum) { +const Event = union(enum) { mouse_pressed: struct { - button: Input.Mouse.Button, + button: Mouse.Button, position: Vec2, }, mouse_released: struct { - button: Input.Mouse.Button, + button: Mouse.Button, position: Vec2, }, mouse_move: Vec2, mouse_enter: Vec2, mouse_leave, mouse_scroll: Vec2, - key_pressed: struct { - code: Input.KeyCode, - repeat: bool - }, - key_released: Input.KeyCode, + key_pressed: struct { code: KeyCode, repeat: bool }, + key_released: KeyCode, window_resize, char: u21, }; -pub const Frame = struct { - time_ns: Nanoseconds, - dt_ns: Nanoseconds, - dt: f32, - input: *Input +pub const KeyCode = enum(std.math.IntFittingRange(0, sokol.app.max_keycodes-1)) { + SPACE = 32, + APOSTROPHE = 39, + COMMA = 44, + MINUS = 45, + PERIOD = 46, + SLASH = 47, + _0 = 48, + _1 = 49, + _2 = 50, + _3 = 51, + _4 = 52, + _5 = 53, + _6 = 54, + _7 = 55, + _8 = 56, + _9 = 57, + SEMICOLON = 59, + EQUAL = 61, + A = 65, + B = 66, + C = 67, + D = 68, + E = 69, + F = 70, + G = 71, + H = 72, + I = 73, + J = 74, + K = 75, + L = 76, + M = 77, + N = 78, + O = 79, + P = 80, + Q = 81, + R = 82, + S = 83, + T = 84, + U = 85, + V = 86, + W = 87, + X = 88, + Y = 89, + Z = 90, + LEFT_BRACKET = 91, + BACKSLASH = 92, + RIGHT_BRACKET = 93, + GRAVE_ACCENT = 96, + WORLD_1 = 161, + WORLD_2 = 162, + ESCAPE = 256, + ENTER = 257, + TAB = 258, + BACKSPACE = 259, + INSERT = 260, + DELETE = 261, + RIGHT = 262, + LEFT = 263, + DOWN = 264, + UP = 265, + PAGE_UP = 266, + PAGE_DOWN = 267, + HOME = 268, + END = 269, + CAPS_LOCK = 280, + SCROLL_LOCK = 281, + NUM_LOCK = 282, + PRINT_SCREEN = 283, + PAUSE = 284, + F1 = 290, + F2 = 291, + F3 = 292, + F4 = 293, + F5 = 294, + F6 = 295, + F7 = 296, + F8 = 297, + F9 = 298, + F10 = 299, + F11 = 300, + F12 = 301, + F13 = 302, + F14 = 303, + F15 = 304, + F16 = 305, + F17 = 306, + F18 = 307, + F19 = 308, + F20 = 309, + F21 = 310, + F22 = 311, + F23 = 312, + F24 = 313, + F25 = 314, + KP_0 = 320, + KP_1 = 321, + KP_2 = 322, + KP_3 = 323, + KP_4 = 324, + KP_5 = 325, + KP_6 = 326, + KP_7 = 327, + KP_8 = 328, + KP_9 = 329, + KP_DECIMAL = 330, + KP_DIVIDE = 331, + KP_MULTIPLY = 332, + KP_SUBTRACT = 333, + KP_ADD = 334, + KP_ENTER = 335, + KP_EQUAL = 336, + LEFT_SHIFT = 340, + LEFT_CONTROL = 341, + LEFT_ALT = 342, + LEFT_SUPER = 343, + RIGHT_SHIFT = 344, + RIGHT_CONTROL = 345, + RIGHT_ALT = 346, + RIGHT_SUPER = 347, + MENU = 348, }; -allocator: std.mem.Allocator, -started_at: std.time.Instant, -mouse_inside: bool, -last_frame_at: Nanoseconds, -input: Input, +pub const KeyState = struct { + down: bool, + pressed: bool, + released: bool, + down_duration: ?f64, -game: Game, -assets: Assets, + pub const RepeatOptions = struct { + first_at: f64 = 0, + period: f64 + }; + + pub fn repeat(self: KeyState, last_repeat_at: *?f64, opts: RepeatOptions) bool { + if (!self.down) { + last_repeat_at.* = null; + return false; + } + + const down_duration = self.down_duration.?; + if (last_repeat_at.* != null) { + if (down_duration >= last_repeat_at.*.? + opts.period) { + last_repeat_at.* = last_repeat_at.*.? + opts.period; + return true; + } + } else { + if (down_duration >= opts.first_at) { + last_repeat_at.* = opts.first_at; + return true; + } + } + + return false; + } +}; + +pub const Mouse = struct { + pub const Button = enum { + left, + right, + middle, + + pub fn fromSokol(mouse_button: sokol.app.Mousebutton) ?Button { + return switch(mouse_button) { + .LEFT => Button.left, + .RIGHT => Button.right, + .MIDDLE => Button.middle, + else => null + }; + } + }; + + position: ?Vec2, + buttons: std.EnumSet(Button), + + pub const empty = Mouse{ + .position = null, + .buttons = .initEmpty() + }; +}; + +const State = struct { + const Input = struct { + down_keys: std.EnumSet(KeyCode), + pressed_keys: std.EnumSet(KeyCode), + released_keys: std.EnumSet(KeyCode), + pressed_keys_at: std.EnumMap(KeyCode, Nanoseconds), + mouse: Mouse, + + pub const empty = Input{ + .down_keys = .initEmpty(), + .pressed_keys = .initEmpty(), + .released_keys = .initEmpty(), + .pressed_keys_at = .init(.{}), + .mouse = .empty + }; + }; + + allocator: std.mem.Allocator, + started_at: std.time.Instant, + last_frame_at_ns: Nanoseconds, + frame_delta_ns: Nanoseconds = 0, + + // Input + input: Input = .empty, + mouse_inside: bool = false, + + // Graphics + canvas_size: Vec2 = .init(100, 100), + + // Audio +}; + +var state: State = undefined; const RunOptions = struct { window_title: [*:0]const u8 = "Game", @@ -71,29 +266,28 @@ const RunOptions = struct { window_height: u31 = 480, }; -pub fn run(self: *Engine, opts: RunOptions) !void { - self.* = Engine{ - .allocator = undefined, - .started_at = std.time.Instant.now() catch @panic("Instant.now() unsupported"), - .input = .empty, - .mouse_inside = false, - .last_frame_at = 0, - .assets = undefined, - .game = undefined - }; - +pub fn run(opts: RunOptions) !void { var debug_allocator: std.heap.DebugAllocator(.{}) = .init; defer _ = debug_allocator.deinit(); // TODO: Use tracy TracingAllocator + var allocator: std.mem.Allocator = undefined; if (builtin.cpu.arch.isWasm()) { - self.allocator = std.heap.wasm_allocator; + allocator = std.heap.wasm_allocator; } else if (builtin.mode == .Debug) { - self.allocator = debug_allocator.allocator(); + allocator = debug_allocator.allocator(); } else { - self.allocator = std.heap.smp_allocator; + allocator = std.heap.smp_allocator; } + state = State{ + .allocator = allocator, + .started_at = std.time.Instant.now() catch @panic("Instant.now() unsupported"), + .input = .empty, + .mouse_inside = false, + .last_frame_at_ns = 0, + }; + tracy.setThreadName("Main"); if (builtin.os.tag == .linux) { @@ -103,6 +297,8 @@ pub fn run(self: *Engine, opts: RunOptions) !void { .flags = std.posix.SA.RESTART, }; std.posix.sigaction(std.posix.SIG.INT, &sa, null); + } else { + // TODO: Windows Ctrl+C handler } // TODO: Don't hard code icon path, allow changing through options @@ -121,11 +317,10 @@ pub fn run(self: *Engine, opts: RunOptions) !void { }; sapp.run(.{ - .init_userdata_cb = sokolInitCallback, - .frame_userdata_cb = sokolFrameCallback, - .cleanup_userdata_cb = sokolCleanupCallback, - .event_userdata_cb = sokolEventCallback, - .user_data = self, + .init_cb = sokolInitCallback, + .frame_cb = sokolFrameCallback, + .cleanup_cb = sokolCleanupCallback, + .event_cb = sokolEventCallback, .width = opts.window_width, .height = opts.window_height, .icon = icon, @@ -137,45 +332,90 @@ pub fn run(self: *Engine, opts: RunOptions) !void { }); } -fn sokolInit(self: *Engine) !void { - const zone = tracy.initZone(@src(), .{ }); - defer zone.deinit(); - - try Gfx.init(.{ - .allocator = self.allocator, - .logger = .{ .func = sokolLogCallback }, - .imgui_font = .{ - .ttf_data = @embedFile("../assets/roboto-font/Roboto-Regular.ttf"), - } - }); - - try Audio.init(.{ - .allocator = self.allocator, - .logger = .{ .func = sokolLogCallback }, - }); - - self.assets = try Assets.init(self.allocator); - self.game = try Game.init(self.allocator, &self.assets); +pub fn isKeyDown(key_code: KeyCode) bool { + const input = &state.input; + return input.down_keys.contains(key_code); } -fn sokolCleanup(self: *Engine) void { - const zone = tracy.initZone(@src(), .{ }); +pub fn getKeyDownDuration(frame: Engine.Frame, key_code: KeyCode) ?f64 { + if (!isKeyDown(key_code)) { + return null; + } + + const input = &state.input; + const pressed_at_ns = input.pressed_keys_at.get(key_code).?; + const duration_ns = frame.time_ns - pressed_at_ns; + + return @as(f64, @floatFromInt(duration_ns)) / std.time.ns_per_s; +} + +pub fn isKeyPressed(key_code: KeyCode) bool { + const input = &state.input; + return input.pressed_keys.contains(key_code); +} + +pub fn isKeyReleased(key_code: KeyCode) bool { + const input = &state.input; + return input.released_keys.contains(key_code); +} + +pub fn getKeyState(key_code: KeyCode) KeyState { + return KeyState{ + .down = isKeyDown(key_code), + .released = isKeyReleased(key_code), + .pressed = isKeyPressed(key_code), + .down_duration = getKeyDownDuration(key_code) + }; +} + +pub fn timePassed() Nanoseconds { + const now = std.time.Instant.now() catch @panic("Instant.now() unsupported"); + return now.since(state.started_at); +} + +pub fn deltaTime() f32 { + return @as(f32, @floatFromInt(state.frame_delta_ns)) / std.time.ns_per_s; +} + +pub fn getCanvasSize() Vec2 { + return state.canvas_size; +} + +fn sokolInit() !void { + const zone = tracy.initZone(@src(), .{}); + defer zone.deinit(); + + try Gfx.init(.{ .allocator = state.allocator, .logger = .{ .func = sokolLogCallback }, .imgui_font = .{ + .ttf_data = @embedFile("../assets/roboto-font/Roboto-Regular.ttf"), + } }); + + try Audio.init(.{ + .allocator = state.allocator, + .logger = .{ .func = sokolLogCallback }, + }); + + try Assets.init(); + try Game.init(); +} + +fn sokolCleanup() void { + const zone = tracy.initZone(@src(), .{}); defer zone.deinit(); Audio.deinit(); - self.game.deinit(); - self.assets.deinit(self.allocator); + Game.deinit(); + Assets.deinit(); Gfx.deinit(); } -fn sokolFrame(self: *Engine) !void { +fn sokolFrame() !void { tracy.frameMark(); - const time_passed = self.timePassed(); - defer self.last_frame_at = time_passed; - const dt_ns = time_passed - self.last_frame_at; + const time_passed = timePassed(); + defer state.last_frame_at_ns = time_passed; + state.frame_delta_ns = time_passed - state.last_frame_at_ns; - const zone = tracy.initZone(@src(), .{ }); + const zone = tracy.initZone(@src(), .{}); defer zone.deinit(); Gfx.beginFrame(); @@ -183,37 +423,26 @@ fn sokolFrame(self: *Engine) !void { { const window_size: Vec2 = .init(sapp.widthf(), sapp.heightf()); - const ctx = ScreenScalar.push(window_size, self.game.canvas_size); + const ctx = ScreenScalar.push(window_size, state.canvas_size); defer ctx.pop(); Graphics.font_resolution_scale = ctx.scale; - try self.game.tick(Frame{ - .time_ns = time_passed, - .dt_ns = dt_ns, - .dt = @as(f32, @floatFromInt(dt_ns)) / std.time.ns_per_s, - .input = &self.input - }); + try Game.tick(); } - try self.game.debug(); + try Game.debug(); - self.input.pressed_keys = .initEmpty(); - self.input.released_keys = .initEmpty(); + state.input.pressed_keys = .initEmpty(); + state.input.released_keys = .initEmpty(); } -fn timePassed(self: *Engine) Nanoseconds { - const now = std.time.Instant.now() catch @panic("Instant.now() unsupported"); - return now.since(self.started_at); -} - -fn event(self: *Engine, e: Event) !void { - const input = &self.input; - +fn processEvent(e: Event) !void { + const input = &state.input; switch (e) { .key_pressed => |opts| { if (!opts.repeat) { - input.pressed_keys_at.put(opts.code, self.timePassed()); + input.pressed_keys_at.put(opts.code, timePassed()); input.pressed_keys.insert(opts.code); input.down_keys.insert(opts.code); } @@ -247,32 +476,28 @@ fn event(self: *Engine, e: Event) !void { input.mouse.position = opts.position; input.mouse.buttons.remove(opts.button); }, - else => {} + else => {}, } } -fn sokolEvent(self: *Engine, e_ptr: [*c]const sapp.Event) !bool { - const zone = tracy.initZone(@src(), .{ }); +fn sokolEvent(e_ptr: [*c]const sapp.Event) !bool { + const zone = tracy.initZone(@src(), .{}); defer zone.deinit(); const e = e_ptr.*; - const MouseButton = Input.Mouse.Button; - if (imgui.handleEvent(e)) { - if (self.mouse_inside) { - try self.event(Event{ - .mouse_leave = {} - }); + if (state.mouse_inside) { + try processEvent(Event{ .mouse_leave = {} }); } - self.mouse_inside = false; + state.mouse_inside = false; return true; } blk: switch (e.type) { .MOUSE_DOWN => { - const mouse_button = MouseButton.fromSokol(e.mouse_button) orelse break :blk; + const mouse_button = Mouse.Button.fromSokol(e.mouse_button) orelse break :blk; - try self.event(Event{ + try processEvent(Event{ .mouse_pressed = .{ .button = mouse_button, .position = Vec2.init(e.mouse_x, e.mouse_y) @@ -282,9 +507,9 @@ fn sokolEvent(self: *Engine, e_ptr: [*c]const sapp.Event) !bool { return true; }, .MOUSE_UP => { - const mouse_button = MouseButton.fromSokol(e.mouse_button) orelse break :blk; + const mouse_button = Mouse.Button.fromSokol(e.mouse_button) orelse break :blk; - try self.event(Event{ + try processEvent(Event{ .mouse_released = .{ .button = mouse_button, .position = Vec2.init(e.mouse_x, e.mouse_y) @@ -294,62 +519,54 @@ fn sokolEvent(self: *Engine, e_ptr: [*c]const sapp.Event) !bool { return true; }, .MOUSE_MOVE => { - if (!self.mouse_inside) { - try self.event(Event{ + if (!state.mouse_inside) { + try processEvent(Event{ .mouse_enter = Vec2.init(e.mouse_x, e.mouse_y) }); } else { - try self.event(Event{ + try processEvent(Event{ .mouse_move = Vec2.init(e.mouse_x, e.mouse_y) }); } - self.mouse_inside = true; + state.mouse_inside = true; return true; }, .MOUSE_ENTER => { - if (!self.mouse_inside) { - try self.event(Event{ + if (!state.mouse_inside) { + try processEvent(Event{ .mouse_enter = Vec2.init(e.mouse_x, e.mouse_y) }); } - self.mouse_inside = true; + state.mouse_inside = true; return true; }, .RESIZED => { - if (self.mouse_inside) { - try self.event(Event{ - .mouse_leave = {} - }); + if (state.mouse_inside) { + try processEvent(Event{ .mouse_leave = {} }); } - try self.event(Event{ - .window_resize = {} - }); + try processEvent(Event{ .window_resize = {} }); - self.mouse_inside = false; + state.mouse_inside = false; return true; }, .MOUSE_LEAVE => { - if (self.mouse_inside) { - try self.event(Event{ - .mouse_leave = {} - }); + if (state.mouse_inside) { + try processEvent(Event{ .mouse_leave = {} }); } - self.mouse_inside = false; + state.mouse_inside = false; return true; }, .MOUSE_SCROLL => { - try self.event(Event{ - .mouse_scroll = Vec2.init(e.scroll_x, e.scroll_y) - }); + try processEvent(Event{ .mouse_scroll = Vec2.init(e.scroll_x, e.scroll_y) }); return true; }, .KEY_DOWN => { - try self.event(Event{ + try processEvent(Event{ .key_pressed = .{ .code = @enumFromInt(@intFromEnum(e.key_code)), .repeat = e.key_repeat @@ -359,14 +576,14 @@ fn sokolEvent(self: *Engine, e_ptr: [*c]const sapp.Event) !bool { return true; }, .KEY_UP => { - try self.event(Event{ + try processEvent(Event{ .key_released = @enumFromInt(@intFromEnum(e.key_code)) }); return true; }, .CHAR => { - try self.event(Event{ + try processEvent(Event{ .char = @intCast(e.char_code) }); @@ -375,16 +592,14 @@ fn sokolEvent(self: *Engine, e_ptr: [*c]const sapp.Event) !bool { .QUIT_REQUESTED => { // TODO: handle quit request. Maybe show confirmation window in certain cases. }, - else => {} + else => {}, } return false; } -fn sokolEventCallback(e_ptr: [*c]const sapp.Event, userdata: ?*anyopaque) callconv(.c) void { - const engine: *Engine = @alignCast(@ptrCast(userdata)); - - const consume_event = engine.sokolEvent(e_ptr) catch |e| blk: { +fn sokolEventCallback(e_ptr: [*c]const sapp.Event) callconv(.c) void { + const consume_event = sokolEvent(e_ptr) catch |e| blk: { log.err("sokolEvent() failed: {}", .{e}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); @@ -397,16 +612,12 @@ fn sokolEventCallback(e_ptr: [*c]const sapp.Event, userdata: ?*anyopaque) callco } } -fn sokolCleanupCallback(userdata: ?*anyopaque) callconv(.c) void { - const engine: *Engine = @alignCast(@ptrCast(userdata)); - - engine.sokolCleanup(); +fn sokolCleanupCallback() callconv(.c) void { + sokolCleanup(); } -fn sokolInitCallback(userdata: ?*anyopaque) callconv(.c) void { - const engine: *Engine = @alignCast(@ptrCast(userdata)); - - engine.sokolInit() catch |e| { +fn sokolInitCallback() callconv(.c) void { + sokolInit() catch |e| { log.err("sokolInit() failed: {}", .{e}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); @@ -415,10 +626,8 @@ fn sokolInitCallback(userdata: ?*anyopaque) callconv(.c) void { }; } -fn sokolFrameCallback(userdata: ?*anyopaque) callconv(.c) void { - const engine: *Engine = @alignCast(@ptrCast(userdata)); - - engine.sokolFrame() catch |e| { +fn sokolFrameCallback() callconv(.c) void { + sokolFrame() catch |e| { log.err("sokolFrame() failed: {}", .{e}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); @@ -449,27 +658,9 @@ fn sokolLogCallback(tag: [*c]const u8, log_level: u32, log_item: u32, message: [ _ = user_data; if (filename != null) { - sokolLogFmt( - log_level, - "[{s}][id:{}] {s}:{}: {s}", - .{ - cStrToZig(tag orelse "-"), - log_item, - std.fs.path.basename(cStrToZig(filename orelse "-")), - line_nr, - cStrToZig(message orelse "") - } - ); + sokolLogFmt(log_level, "[{s}][id:{}] {s}:{}: {s}", .{ cStrToZig(tag orelse "-"), log_item, std.fs.path.basename(cStrToZig(filename orelse "-")), line_nr, cStrToZig(message orelse "") }); } else { - sokolLogFmt( - log_level, - "[{s}][id:{}] {s}", - .{ - cStrToZig(tag orelse "-"), - log_item, - cStrToZig(message orelse "") - } - ); + sokolLogFmt(log_level, "[{s}][id:{}] {s}", .{ cStrToZig(tag orelse "-"), log_item, cStrToZig(message orelse "") }); } } diff --git a/src/game.zig b/src/game.zig index a3a1836..6327ec3 100644 --- a/src/game.zig +++ b/src/game.zig @@ -13,67 +13,57 @@ const Audio = Engine.Audio; const Game = @This(); -gpa: Allocator, -assets: *Assets, -canvas_size: Vec2, +var player_position: Vec2 = undefined; -player: Vec2, - -pub fn init(gpa: Allocator, assets: *Assets) !Game { - return Game{ - .gpa = gpa, - .assets = assets, - .canvas_size = Vec2.init(100, 100), - .player = .init(50, 50) - }; +pub fn init() !void { + player_position = .init(50, 50); } -pub fn deinit(self: *Game) void { - _ = self; // autofix +pub fn deinit() void { } -pub fn tick(self: *Game, frame: Engine.Frame) !void { +pub fn tick() !void { + const dt = Engine.deltaTime(); var dir = Vec2.init(0, 0); - if (frame.input.isKeyDown(.W)) { + if (Engine.isKeyDown(.W)) { dir.y -= 1; } - if (frame.input.isKeyDown(.S)) { + if (Engine.isKeyDown(.S)) { dir.y += 1; } - if (frame.input.isKeyDown(.A)) { + if (Engine.isKeyDown(.A)) { dir.x -= 1; } - if (frame.input.isKeyDown(.D)) { + if (Engine.isKeyDown(.D)) { dir.x += 1; } dir = dir.normalized(); if (dir.x != 0 or dir.y != 0) { Audio.play(.{ - .id = self.assets.wood01 + .id = Assets.wood01 }); } - self.player = self.player.add(dir.multiplyScalar(50 * frame.dt)); + player_position = player_position.add(dir.multiplyScalar(50 * dt)); - const regular_font = self.assets.font_id.get(.regular); + const regular_font = Assets.font_id.get(.regular); - Gfx.drawRectangle(.init(0, 0), self.canvas_size, rgb(20, 20, 20)); + Gfx.drawRectangle(.init(0, 0), Engine.getCanvasSize(), rgb(20, 20, 20)); const size = Vec2.init(20, 20); - Gfx.drawRectangle(self.player.sub(size.divideScalar(2)), size, rgb(200, 20, 20)); + Gfx.drawRectangle(player_position.sub(size.divideScalar(2)), size, rgb(200, 20, 20)); if (dir.x != 0 or dir.y != 0) { - Gfx.drawRectanglOutline(self.player.sub(size.divideScalar(2)), size, rgb(20, 200, 20), 3); + Gfx.drawRectanglOutline(player_position.sub(size.divideScalar(2)), size, rgb(20, 200, 20), 3); } - Gfx.drawText(self.player, "Player", .{ + Gfx.drawText(player_position, "Player", .{ .font = regular_font, .size = 10 }); } -pub fn debug(self: *Game) !void { - _ = self; // autofix +pub fn debug() !void { if (!imgui.beginWindow(.{ .name = "Debug", .pos = Vec2.init(20, 20), diff --git a/src/main.zig b/src/main.zig index 9a6aadc..028aace 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,8 +1,7 @@ const Engine = @import("./engine/root.zig"); -var engine: Engine = undefined; - pub fn main() !void { - try engine.run(.{}); + try Engine.run(.{ + }); }