split engine into lib and runtime
This commit is contained in:
parent
e5fccc21f2
commit
6407263540
@ -23,13 +23,6 @@ const InitOptions = struct {
|
||||
pub fn init(outer_builder: *std.Build, opts: InitOptions) void {
|
||||
const b = opts.dep_engine.builder;
|
||||
|
||||
const engine_module = createEngineModule(b, .{
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.has_tracy = opts.has_tracy,
|
||||
.has_imgui = opts.has_imgui
|
||||
});
|
||||
|
||||
opts.root_module.resolved_target = opts.target;
|
||||
opts.root_module.optimize = opts.optimize;
|
||||
const game_lib = b.addLibrary(.{
|
||||
@ -37,14 +30,25 @@ pub fn init(outer_builder: *std.Build, opts: InitOptions) void {
|
||||
.root_module = opts.root_module,
|
||||
.linkage = .static,
|
||||
});
|
||||
opts.root_module.addImport("engine", engine_module.root_module);
|
||||
const engine_lib = b.createModule(.{
|
||||
.root_source_file = b.path("src/lib/root.zig")
|
||||
});
|
||||
opts.root_module.addImport("engine", engine_lib);
|
||||
|
||||
const engine_module = createEngineModule(b, .{
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.has_tracy = opts.has_tracy,
|
||||
.has_imgui = opts.has_imgui
|
||||
});
|
||||
|
||||
const mod_main = b.createModule(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.root_source_file = b.path("src/runtime/main.zig"),
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.link_libc = true
|
||||
});
|
||||
mod_main.addImport("lib", engine_lib);
|
||||
mod_main.addImport("engine", engine_module.root_module);
|
||||
mod_main.linkLibrary(game_lib);
|
||||
|
||||
@ -110,11 +114,15 @@ fn createEngineModule(b: *std.Build, opts: EngineModule.Options) EngineModule {
|
||||
}
|
||||
|
||||
const mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/root.zig"),
|
||||
.root_source_file = b.path("src/runtime/root.zig"),
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.link_libc = true
|
||||
});
|
||||
const lib = b.createModule(.{
|
||||
.root_source_file = b.path("src/lib/root.zig")
|
||||
});
|
||||
mod.addImport("lib", lib);
|
||||
|
||||
const dep_sokol = b.dependency("sokol", .{
|
||||
.target = opts.target,
|
||||
@ -169,7 +177,7 @@ fn createEngineModule(b: *std.Build, opts: EngineModule.Options) EngineModule {
|
||||
|
||||
mod.addIncludePath(dep_sokol_c.path("util"));
|
||||
mod.addCSourceFile(.{
|
||||
.file = b.path("src/fontstash/sokol_fontstash_impl.c"),
|
||||
.file = b.path("src/runtime/fontstash/sokol_fontstash_impl.c"),
|
||||
.flags = cflags.items
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,291 +0,0 @@
|
||||
const std = @import("std");
|
||||
const build_options = @import("build_options");
|
||||
const log = std.log.scoped(.engine);
|
||||
|
||||
const InputSystem = @import("./input.zig");
|
||||
const KeyCode = InputSystem.KeyCode;
|
||||
const MouseButton = InputSystem.MouseButton;
|
||||
|
||||
const AudioSystem = @import("./audio/root.zig");
|
||||
const AudioData = AudioSystem.Data;
|
||||
const AudioCommand = AudioSystem.Command;
|
||||
|
||||
const GraphicsSystem = @import("./graphics.zig");
|
||||
const TextureId = GraphicsSystem.TextureId;
|
||||
const GraphicsCommand = GraphicsSystem.Command;
|
||||
const Font = GraphicsSystem.Font;
|
||||
const Sprite = GraphicsSystem.Sprite;
|
||||
|
||||
const Math = @import("./math.zig");
|
||||
const Rect = Math.Rect;
|
||||
const Vec4 = Math.Vec4;
|
||||
const Vec2 = Math.Vec2;
|
||||
const rgb = Math.rgb;
|
||||
|
||||
pub const Nanoseconds = u64;
|
||||
|
||||
const Frame = @This();
|
||||
|
||||
pub const Input = struct {
|
||||
keyboard: InputSystem.ButtonStateSet(KeyCode),
|
||||
mouse_button: InputSystem.ButtonStateSet(MouseButton),
|
||||
mouse_position: ?Vec2,
|
||||
|
||||
pub const empty = Input{
|
||||
.keyboard = .empty,
|
||||
.mouse_button = .empty,
|
||||
.mouse_position = null,
|
||||
};
|
||||
};
|
||||
|
||||
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 Audio = struct {
|
||||
commands: std.ArrayList(AudioCommand),
|
||||
|
||||
pub const empty = Audio{
|
||||
.commands = .empty
|
||||
};
|
||||
};
|
||||
|
||||
pub const Graphics = struct {
|
||||
clear_color: Vec4,
|
||||
screen_size: Vec2,
|
||||
canvas_size: ?Vec2,
|
||||
|
||||
scissor_stack: std.ArrayList(Rect),
|
||||
commands: std.ArrayList(GraphicsCommand),
|
||||
|
||||
pub const empty = Graphics{
|
||||
.clear_color = rgb(0, 0, 0),
|
||||
.screen_size = .init(0, 0),
|
||||
.canvas_size = null,
|
||||
.scissor_stack = .empty,
|
||||
.commands = .empty
|
||||
};
|
||||
};
|
||||
|
||||
arena: std.heap.ArenaAllocator,
|
||||
|
||||
time_ns: Nanoseconds,
|
||||
dt_ns: Nanoseconds,
|
||||
input: Input,
|
||||
audio: Audio,
|
||||
graphics: Graphics,
|
||||
|
||||
show_debug: bool,
|
||||
hide_cursor: bool,
|
||||
|
||||
pub fn init(self: *Frame, gpa: std.mem.Allocator) void {
|
||||
self.* = Frame{
|
||||
.arena = std.heap.ArenaAllocator.init(gpa),
|
||||
.time_ns = 0,
|
||||
.dt_ns = 0,
|
||||
.input = .empty,
|
||||
.audio = .empty,
|
||||
.graphics = .empty,
|
||||
.show_debug = false,
|
||||
.hide_cursor = false
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Frame) void {
|
||||
self.arena.deinit();
|
||||
}
|
||||
|
||||
pub fn deltaTime(self: Frame) f32 {
|
||||
return @as(f32, @floatFromInt(self.dt_ns)) / std.time.ns_per_s;
|
||||
}
|
||||
|
||||
pub fn time(self: Frame) f64 {
|
||||
return @as(f64, @floatFromInt(self.time_ns)) / std.time.ns_per_s;
|
||||
}
|
||||
|
||||
pub fn isKeyDown(self: Frame, key_code: KeyCode) bool {
|
||||
const keyboard = &self.input.keyboard;
|
||||
return keyboard.down.contains(key_code);
|
||||
}
|
||||
|
||||
pub fn getKeyDownDuration(self: Frame, key_code: KeyCode) ?f32 {
|
||||
if (!self.isKeyDown(key_code)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const keyboard = &self.input.keyboard;
|
||||
const pressed_at_ns = keyboard.pressed_at.get(key_code).?;
|
||||
const duration_ns = self.time_ns - pressed_at_ns;
|
||||
|
||||
return @as(f32, @floatFromInt(duration_ns)) / std.time.ns_per_s;
|
||||
}
|
||||
|
||||
pub fn isKeyPressed(self: Frame, key_code: KeyCode) bool {
|
||||
const keyboard = &self.input.keyboard;
|
||||
return keyboard.pressed.contains(key_code);
|
||||
}
|
||||
|
||||
pub fn isKeyReleased(self: Frame, key_code: KeyCode) bool {
|
||||
const keyboard = &self.input.keyboard;
|
||||
return keyboard.released.contains(key_code);
|
||||
}
|
||||
|
||||
pub fn getKeyState(self: 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(key_code)
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isMousePressed(self: Frame, button: MouseButton) bool {
|
||||
return self.input.mouse_button.pressed.contains(button);
|
||||
}
|
||||
|
||||
pub fn isMouseDown(self: Frame, button: MouseButton) bool {
|
||||
return self.input.mouse_button.down.contains(button);
|
||||
}
|
||||
|
||||
fn pushAudioCommand(self: *Frame, command: AudioCommand) void {
|
||||
const arena = self.arena.allocator();
|
||||
|
||||
self.audio.commands.append(arena, command) catch |e| {
|
||||
log.warn("Failed to play audio: {}", .{e});
|
||||
};
|
||||
}
|
||||
|
||||
pub fn pushGraphicsCommand(self: *Frame, command: GraphicsCommand) void {
|
||||
const arena = self.arena.allocator();
|
||||
|
||||
self.graphics.commands.append(arena, command) catch |e|{
|
||||
log.warn("Failed to push graphics command: {}", .{e});
|
||||
};
|
||||
}
|
||||
|
||||
pub fn prependGraphicsCommand(self: *Frame, command: GraphicsCommand) void {
|
||||
const arena = self.arena.allocator();
|
||||
|
||||
self.graphics.commands.insert(arena, 0, command) catch |e|{
|
||||
log.warn("Failed to push graphics command: {}", .{e});
|
||||
};
|
||||
}
|
||||
|
||||
pub fn playAudio(self: *Frame, options: AudioCommand.Play) void {
|
||||
self.pushAudioCommand(.{
|
||||
.play = options,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn pushScissor(self: *Frame, rect: Rect) void {
|
||||
const arena = self.arena.allocator();
|
||||
self.graphics.scissor_stack.append(arena, rect) catch |e| {
|
||||
log.warn("Failed to push scissor region: {}", .{e});
|
||||
return;
|
||||
};
|
||||
|
||||
self.pushGraphicsCommand(.{
|
||||
.set_scissor = rect
|
||||
});
|
||||
}
|
||||
|
||||
pub fn popScissor(self: *Frame) void {
|
||||
_ = self.graphics.scissor_stack.pop().?;
|
||||
const rect = self.graphics.scissor_stack.getLast();
|
||||
|
||||
self.pushGraphicsCommand(.{
|
||||
.set_scissor = rect
|
||||
});
|
||||
}
|
||||
|
||||
pub fn drawRectangle(self: *Frame, opts: GraphicsCommand.DrawRectangle) void {
|
||||
self.pushGraphicsCommand(.{ .draw_rectangle = opts });
|
||||
}
|
||||
|
||||
pub fn drawLine(self: *Frame, pos1: Vec2, pos2: Vec2, color: Vec4, width: f32) void {
|
||||
self.pushGraphicsCommand(.{
|
||||
.draw_line = .{
|
||||
.pos1 = pos1,
|
||||
.pos2 = pos2,
|
||||
.width = width,
|
||||
.color = color
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn drawRectanglOutline(self: *Frame, pos: Vec2, size: Vec2, color: Vec4, width: f32) void {
|
||||
// TODO: Don't use line segments
|
||||
self.drawLine(pos, pos.add(.{ .x = size.x, .y = 0 }), color, width);
|
||||
self.drawLine(pos, pos.add(.{ .x = 0, .y = size.y }), color, width);
|
||||
self.drawLine(pos.add(.{ .x = 0, .y = size.y }), pos.add(size), color, width);
|
||||
self.drawLine(pos.add(.{ .x = size.x, .y = 0 }), pos.add(size), color, width);
|
||||
}
|
||||
|
||||
pub const DrawTextOptions = struct {
|
||||
font: Font.Id,
|
||||
size: f32 = 16,
|
||||
color: Vec4 = rgb(255, 255, 255),
|
||||
};
|
||||
|
||||
pub fn drawText(self: *Frame, position: Vec2, text: []const u8, opts: DrawTextOptions) void {
|
||||
const arena = self.arena.allocator();
|
||||
const text_dupe = arena.dupe(u8, text) catch |e| {
|
||||
log.warn("Failed to draw text: {}", .{e});
|
||||
return;
|
||||
};
|
||||
|
||||
self.pushGraphicsCommand(.{
|
||||
.draw_text = .{
|
||||
.pos = position,
|
||||
.text = text_dupe,
|
||||
.size = opts.size,
|
||||
.font = opts.font,
|
||||
.color = opts.color,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn pushTransform(self: *Frame, translation: Vec2, scale: Vec2) void {
|
||||
self.pushGraphicsCommand(.{
|
||||
.push_transformation = .{
|
||||
.translation = translation,
|
||||
.scale = scale
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn popTransform(self: *Frame) void {
|
||||
self.pushGraphicsCommand(.{
|
||||
.pop_transformation = {}
|
||||
});
|
||||
}
|
||||
@ -1,124 +0,0 @@
|
||||
const std = @import("std");
|
||||
const sokol = @import("sokol");
|
||||
|
||||
const Frame = @import("./frame.zig");
|
||||
const Nanoseconds = Frame.Nanoseconds;
|
||||
|
||||
const Math = @import("./math.zig");
|
||||
const Vec2 = Math.Vec2;
|
||||
|
||||
const Input = @This();
|
||||
|
||||
const SokolKeyCodeInfo = @typeInfo(sokol.app.Keycode);
|
||||
pub const KeyCode = @Type(.{
|
||||
.@"enum" = .{
|
||||
.tag_type = std.math.IntFittingRange(0, sokol.app.max_keycodes-1),
|
||||
.decls = SokolKeyCodeInfo.@"enum".decls,
|
||||
.fields = SokolKeyCodeInfo.@"enum".fields,
|
||||
.is_exhaustive = false
|
||||
}
|
||||
});
|
||||
|
||||
pub const MouseButton = enum(i3) {
|
||||
left = @intFromEnum(sokol.app.Mousebutton.LEFT),
|
||||
right = @intFromEnum(sokol.app.Mousebutton.RIGHT),
|
||||
middle = @intFromEnum(sokol.app.Mousebutton.MIDDLE),
|
||||
_
|
||||
};
|
||||
|
||||
pub fn ButtonStateSet(E: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
down: std.EnumSet(E),
|
||||
pressed: std.EnumSet(E),
|
||||
released: std.EnumSet(E),
|
||||
pressed_at: std.EnumMap(E, Nanoseconds),
|
||||
|
||||
pub const empty = Self{
|
||||
.down = .initEmpty(),
|
||||
.pressed = .initEmpty(),
|
||||
.released = .initEmpty(),
|
||||
.pressed_at = .init(.{}),
|
||||
};
|
||||
|
||||
fn press(self: *Self, button: E, now: Nanoseconds) void {
|
||||
self.pressed_at.put(button, now);
|
||||
self.pressed.insert(button);
|
||||
self.down.insert(button);
|
||||
}
|
||||
|
||||
fn release(self: *Self, button: E) void {
|
||||
self.down.remove(button);
|
||||
self.released.insert(button);
|
||||
self.pressed_at.remove(button);
|
||||
}
|
||||
|
||||
fn releaseAll(self: *Self) void {
|
||||
var iter = self.down.iterator();
|
||||
while (iter.next()) |key_code| {
|
||||
self.released.insert(key_code);
|
||||
}
|
||||
self.down = .initEmpty();
|
||||
self.pressed_at = .init(.{});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const Event = union(enum) {
|
||||
mouse_pressed: struct {
|
||||
button: MouseButton,
|
||||
position: Vec2,
|
||||
},
|
||||
mouse_released: struct {
|
||||
button: MouseButton,
|
||||
position: Vec2,
|
||||
},
|
||||
mouse_move: Vec2,
|
||||
mouse_enter: Vec2,
|
||||
mouse_leave,
|
||||
mouse_scroll: Vec2,
|
||||
key_pressed: struct {
|
||||
code: KeyCode,
|
||||
repeat: bool
|
||||
},
|
||||
key_released: Input.KeyCode,
|
||||
window_resize,
|
||||
char: u21,
|
||||
};
|
||||
|
||||
pub fn processEvent(frame: *Frame, event: Event) void {
|
||||
const input = &frame.input;
|
||||
|
||||
switch (event) {
|
||||
.key_pressed => |opts| {
|
||||
if (!opts.repeat) {
|
||||
input.keyboard.press(opts.code, frame.time_ns);
|
||||
}
|
||||
},
|
||||
.key_released => |key_code| {
|
||||
input.keyboard.release(key_code);
|
||||
},
|
||||
.mouse_leave => {
|
||||
input.keyboard.releaseAll();
|
||||
|
||||
input.mouse_position = null;
|
||||
input.mouse_button = .empty;
|
||||
},
|
||||
.mouse_enter => |pos| {
|
||||
input.mouse_position = pos;
|
||||
},
|
||||
.mouse_move => |pos| {
|
||||
input.mouse_position = pos;
|
||||
},
|
||||
.mouse_pressed => |opts| {
|
||||
input.mouse_position = opts.position;
|
||||
input.mouse_button.press(opts.button, frame.time_ns);
|
||||
},
|
||||
.mouse_released => |opts| {
|
||||
input.mouse_position = opts.position;
|
||||
input.mouse_button.release(opts.button);
|
||||
},
|
||||
else => {}
|
||||
}
|
||||
}
|
||||
37
engine/src/lib/imgui.zig
Normal file
37
engine/src/lib/imgui.zig
Normal file
@ -0,0 +1,37 @@
|
||||
const std = @import("std");
|
||||
const log = std.log.scoped(.imgui);
|
||||
|
||||
const ImGui = @This();
|
||||
|
||||
const Command = union(enum) {
|
||||
text: [:0]const u8
|
||||
};
|
||||
|
||||
arena_state: std.heap.ArenaAllocator,
|
||||
commands: std.ArrayList(Command),
|
||||
|
||||
pub fn init(gpa: std.mem.Allocator) ImGui {
|
||||
return ImGui{
|
||||
.arena_state = .init(gpa),
|
||||
.commands = .empty
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *ImGui) void {
|
||||
self.arena_state.deinit();
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn text(self: *ImGui, comptime fmt: []const u8, args: anytype) void {
|
||||
const arena = self.arena_state.allocator();
|
||||
|
||||
const formatted = std.fmt.allocPrintSentinel(arena, fmt, args, 0) catch |e| {
|
||||
log.warn("formatting text failed: {}", .{e});
|
||||
return;
|
||||
};
|
||||
|
||||
self.commands.append(arena, .{ .text = formatted }) catch |e| {
|
||||
log.warn("appending command failed: {}", .{e});
|
||||
return;
|
||||
};
|
||||
}
|
||||
473
engine/src/lib/root.zig
Normal file
473
engine/src/lib/root.zig
Normal file
@ -0,0 +1,473 @@
|
||||
const std = @import("std");
|
||||
const log = std.log.scoped(.engine);
|
||||
|
||||
pub const ImGui = @import("./imgui.zig");
|
||||
pub const Math = @import("./math.zig");
|
||||
|
||||
const rgb = Math.rgb;
|
||||
const Rect = Math.Rect;
|
||||
const Vec2 = Math.Vec2;
|
||||
const Vec4 = Math.Vec4;
|
||||
|
||||
pub const Nanoseconds = u64;
|
||||
|
||||
pub const KeyCode = enum {
|
||||
SPACE,
|
||||
APOSTROPHE,
|
||||
COMMA,
|
||||
MINUS,
|
||||
PERIOD,
|
||||
SLASH,
|
||||
_0,
|
||||
_1,
|
||||
_2,
|
||||
_3,
|
||||
_4,
|
||||
_5,
|
||||
_6,
|
||||
_7,
|
||||
_8,
|
||||
_9,
|
||||
SEMICOLON,
|
||||
EQUAL,
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
E,
|
||||
F,
|
||||
G,
|
||||
H,
|
||||
I,
|
||||
J,
|
||||
K,
|
||||
L,
|
||||
M,
|
||||
N,
|
||||
O,
|
||||
P,
|
||||
Q,
|
||||
R,
|
||||
S,
|
||||
T,
|
||||
U,
|
||||
V,
|
||||
W,
|
||||
X,
|
||||
Y,
|
||||
Z,
|
||||
LEFT_BRACKET,
|
||||
BACKSLASH,
|
||||
RIGHT_BRACKET,
|
||||
GRAVE_ACCENT,
|
||||
WORLD_1,
|
||||
WORLD_2,
|
||||
ESCAPE,
|
||||
ENTER,
|
||||
TAB,
|
||||
BACKSPACE,
|
||||
INSERT,
|
||||
DELETE,
|
||||
RIGHT,
|
||||
LEFT,
|
||||
DOWN,
|
||||
UP,
|
||||
PAGE_UP,
|
||||
PAGE_DOWN,
|
||||
HOME,
|
||||
END,
|
||||
CAPS_LOCK,
|
||||
SCROLL_LOCK,
|
||||
NUM_LOCK,
|
||||
PRINT_SCREEN,
|
||||
PAUSE,
|
||||
F1,
|
||||
F2,
|
||||
F3,
|
||||
F4,
|
||||
F5,
|
||||
F6,
|
||||
F7,
|
||||
F8,
|
||||
F9,
|
||||
F10,
|
||||
F11,
|
||||
F12,
|
||||
F13,
|
||||
F14,
|
||||
F15,
|
||||
F16,
|
||||
F17,
|
||||
F18,
|
||||
F19,
|
||||
F20,
|
||||
F21,
|
||||
F22,
|
||||
F23,
|
||||
F24,
|
||||
F25,
|
||||
KP_0,
|
||||
KP_1,
|
||||
KP_2,
|
||||
KP_3,
|
||||
KP_4,
|
||||
KP_5,
|
||||
KP_6,
|
||||
KP_7,
|
||||
KP_8,
|
||||
KP_9,
|
||||
KP_DECIMAL,
|
||||
KP_DIVIDE,
|
||||
KP_MULTIPLY,
|
||||
KP_SUBTRACT,
|
||||
KP_ADD,
|
||||
KP_ENTER,
|
||||
KP_EQUAL,
|
||||
LEFT_SHIFT,
|
||||
LEFT_CONTROL,
|
||||
LEFT_ALT,
|
||||
LEFT_SUPER,
|
||||
RIGHT_SHIFT,
|
||||
RIGHT_CONTROL,
|
||||
RIGHT_ALT,
|
||||
RIGHT_SUPER,
|
||||
MENU,
|
||||
};
|
||||
|
||||
pub const MouseButton = enum {
|
||||
left,
|
||||
right,
|
||||
middle,
|
||||
};
|
||||
|
||||
pub const TextureId = enum(u16) { _ };
|
||||
pub const FontId = enum(u16) { _ };
|
||||
pub const AudioId = enum(u16) { _ };
|
||||
|
||||
pub const Sprite = struct {
|
||||
texture: TextureId,
|
||||
uv: Rect
|
||||
};
|
||||
|
||||
fn ButtonStateSet(E: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
down: std.EnumSet(E),
|
||||
pressed: std.EnumSet(E),
|
||||
released: std.EnumSet(E),
|
||||
pressed_at: std.EnumMap(E, Nanoseconds),
|
||||
|
||||
pub const empty = Self{
|
||||
.down = .initEmpty(),
|
||||
.pressed = .initEmpty(),
|
||||
.released = .initEmpty(),
|
||||
.pressed_at = .init(.{}),
|
||||
};
|
||||
|
||||
pub fn press(self: *Self, button: E, now: Nanoseconds) void {
|
||||
self.pressed_at.put(button, now);
|
||||
self.pressed.insert(button);
|
||||
self.down.insert(button);
|
||||
}
|
||||
|
||||
pub fn release(self: *Self, button: E) void {
|
||||
self.down.remove(button);
|
||||
self.released.insert(button);
|
||||
self.pressed_at.remove(button);
|
||||
}
|
||||
|
||||
pub fn releaseAll(self: *Self) void {
|
||||
var iter = self.down.iterator();
|
||||
while (iter.next()) |key_code| {
|
||||
self.released.insert(key_code);
|
||||
}
|
||||
self.down = .initEmpty();
|
||||
self.pressed_at = .init(.{});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const GraphicsCommand = union(enum) {
|
||||
pub const DrawRectangle = struct {
|
||||
rect: Rect,
|
||||
color: Vec4,
|
||||
sprite: ?Sprite = null,
|
||||
rotation: f32 = 0,
|
||||
origin: Vec2 = .init(0, 0),
|
||||
};
|
||||
|
||||
pub const DrawLine = struct {
|
||||
pos1: Vec2,
|
||||
pos2: Vec2,
|
||||
color: Vec4,
|
||||
width: f32
|
||||
};
|
||||
|
||||
pub const DrawText = struct {
|
||||
pos: Vec2,
|
||||
text: []const u8,
|
||||
font: FontId,
|
||||
size: f32,
|
||||
color: Vec4,
|
||||
};
|
||||
|
||||
push_scissor: Rect,
|
||||
pop_scissor: void,
|
||||
draw_rectangle: DrawRectangle,
|
||||
draw_line: DrawLine,
|
||||
draw_text: DrawText,
|
||||
push_transformation: struct {
|
||||
translation: Vec2,
|
||||
scale: Vec2
|
||||
},
|
||||
pop_transformation: void
|
||||
};
|
||||
|
||||
pub const AudioCommand = union(enum) {
|
||||
pub const Play = struct {
|
||||
id: AudioId,
|
||||
volume: f32 = 1
|
||||
};
|
||||
|
||||
play: Play,
|
||||
};
|
||||
|
||||
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 Frame = struct {
|
||||
arena: std.heap.ArenaAllocator,
|
||||
time_ns: Nanoseconds,
|
||||
dt_ns: Nanoseconds,
|
||||
|
||||
char_mapping: std.EnumMap(KeyCode, u21),
|
||||
keyboard: ButtonStateSet(KeyCode),
|
||||
mouse_button: ButtonStateSet(MouseButton),
|
||||
mouse_position: ?Vec2,
|
||||
hide_cursor: bool,
|
||||
|
||||
audio_commands: std.ArrayList(AudioCommand),
|
||||
|
||||
graphics_commands: std.ArrayList(GraphicsCommand),
|
||||
canvas_size: ?Vec2,
|
||||
screen_size: Vec2,
|
||||
clear_color: Vec4,
|
||||
|
||||
show_debug: bool,
|
||||
|
||||
pub fn init(self: *Frame, gpa: std.mem.Allocator) void {
|
||||
self.* = Frame{
|
||||
.arena = std.heap.ArenaAllocator.init(gpa),
|
||||
.time_ns = 0,
|
||||
.dt_ns = 0,
|
||||
.char_mapping = .{},
|
||||
.keyboard = .empty,
|
||||
.mouse_button = .empty,
|
||||
.mouse_position = null,
|
||||
.graphics_commands = .empty,
|
||||
.canvas_size = null,
|
||||
.clear_color = rgb(0, 0, 0),
|
||||
.show_debug = false,
|
||||
.audio_commands = .empty,
|
||||
.hide_cursor = false,
|
||||
.screen_size = .init(0, 0),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Frame) void {
|
||||
self.arena.deinit();
|
||||
}
|
||||
|
||||
pub fn deltaTime(self: Frame) f32 {
|
||||
return @as(f32, @floatFromInt(self.dt_ns)) / std.time.ns_per_s;
|
||||
}
|
||||
|
||||
pub fn time(self: Frame) f64 {
|
||||
return @as(f64, @floatFromInt(self.time_ns)) / std.time.ns_per_s;
|
||||
}
|
||||
|
||||
// --------------------- Audio ----------------------- //
|
||||
|
||||
fn pushAudioCommand(self: *Frame, command: AudioCommand) void {
|
||||
const arena = self.arena.allocator();
|
||||
|
||||
self.audio_commands.append(arena, command) catch |e| {
|
||||
log.warn("Failed to play audio: {}", .{e});
|
||||
};
|
||||
}
|
||||
|
||||
pub fn playAudio(self: *Frame, options: AudioCommand.Play) void {
|
||||
self.pushAudioCommand(.{
|
||||
.play = options,
|
||||
});
|
||||
}
|
||||
|
||||
// --------------------- Graphics ----------------------- //
|
||||
|
||||
fn pushGraphicsCommand(self: *Frame, command: GraphicsCommand) void {
|
||||
const arena = self.arena.allocator();
|
||||
|
||||
self.graphics_commands.append(arena, command) catch |e|{
|
||||
log.warn("Failed to push graphics command: {}", .{e});
|
||||
};
|
||||
}
|
||||
|
||||
pub fn drawRectangle(self: *Frame, opts: GraphicsCommand.DrawRectangle) void {
|
||||
self.pushGraphicsCommand(.{ .draw_rectangle = opts });
|
||||
}
|
||||
|
||||
pub fn drawLine(self: *Frame, pos1: Vec2, pos2: Vec2, color: Vec4, width: f32) void {
|
||||
self.pushGraphicsCommand(.{
|
||||
.draw_line = .{
|
||||
.pos1 = pos1,
|
||||
.pos2 = pos2,
|
||||
.width = width,
|
||||
.color = color
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn drawRectanglOutline(self: *Frame, pos: Vec2, size: Vec2, color: Vec4, width: f32) void {
|
||||
// TODO: Don't use line segments
|
||||
self.drawLine(pos, pos.add(.{ .x = size.x, .y = 0 }), color, width);
|
||||
self.drawLine(pos, pos.add(.{ .x = 0, .y = size.y }), color, width);
|
||||
self.drawLine(pos.add(.{ .x = 0, .y = size.y }), pos.add(size), color, width);
|
||||
self.drawLine(pos.add(.{ .x = size.x, .y = 0 }), pos.add(size), color, width);
|
||||
}
|
||||
|
||||
pub fn pushScissor(self: *Frame, rect: Rect) void {
|
||||
self.pushGraphicsCommand(.{
|
||||
.push_scissor = rect
|
||||
});
|
||||
}
|
||||
|
||||
pub fn popScissor(self: *Frame) void {
|
||||
self.pushGraphicsCommand(.{
|
||||
.pop_scissor = {}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn pushTransform(self: *Frame, translation: Vec2, scale: Vec2) void {
|
||||
self.pushGraphicsCommand(.{
|
||||
.push_transformation = .{
|
||||
.translation = translation,
|
||||
.scale = scale
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn popTransform(self: *Frame) void {
|
||||
self.pushGraphicsCommand(.{
|
||||
.pop_transformation = {}
|
||||
});
|
||||
}
|
||||
|
||||
pub const DrawTextOptions = struct {
|
||||
font: FontId,
|
||||
size: f32 = 16,
|
||||
color: Vec4 = rgb(255, 255, 255),
|
||||
};
|
||||
|
||||
pub fn drawText(self: *Frame, position: Vec2, text: []const u8, opts: DrawTextOptions) void {
|
||||
const arena = self.arena.allocator();
|
||||
const text_dupe = arena.dupe(u8, text) catch |e| {
|
||||
log.warn("Failed to draw text: {}", .{e});
|
||||
return;
|
||||
};
|
||||
|
||||
self.pushGraphicsCommand(.{
|
||||
.draw_text = .{
|
||||
.pos = position,
|
||||
.text = text_dupe,
|
||||
.size = opts.size,
|
||||
.font = opts.font,
|
||||
.color = opts.color,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --------------------- Mouse ----------------------- //
|
||||
|
||||
pub fn isMousePressed(self: Frame, button: MouseButton) bool {
|
||||
return self.mouse_button.pressed.contains(button);
|
||||
}
|
||||
|
||||
pub fn isMouseDown(self: Frame, button: MouseButton) bool {
|
||||
return self.mouse_button.down.contains(button);
|
||||
}
|
||||
|
||||
pub fn isMouseReleased(self: Frame, button: MouseButton) bool {
|
||||
return self.mouse_button.released.contains(button);
|
||||
}
|
||||
|
||||
// --------------------- Keyboard ----------------------- //
|
||||
|
||||
pub fn isKeyDown(self: Frame, key_code: KeyCode) bool {
|
||||
return self.keyboard.down.contains(key_code);
|
||||
}
|
||||
|
||||
pub fn isKeyPressed(self: Frame, key_code: KeyCode) bool {
|
||||
return self.keyboard.pressed.contains(key_code);
|
||||
}
|
||||
|
||||
pub fn isKeyReleased(self: Frame, key_code: KeyCode) bool {
|
||||
return self.keyboard.released.contains(key_code);
|
||||
}
|
||||
|
||||
pub fn getKeyDownDuration(self: Frame, key_code: KeyCode) ?f32 {
|
||||
if (!self.isKeyDown(key_code)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const pressed_at_ns = self.keyboard.pressed_at.get(key_code).?;
|
||||
const duration_ns = self.time_ns - pressed_at_ns;
|
||||
|
||||
return @as(f32, @floatFromInt(duration_ns)) / std.time.ns_per_s;
|
||||
}
|
||||
|
||||
pub fn getKeyState(self: 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(key_code)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const Init = struct {
|
||||
gpa: std.mem.Allocator,
|
||||
seed: u64
|
||||
};
|
||||
@ -2,6 +2,7 @@ const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const STBVorbis = @import("stb_vorbis");
|
||||
const Lib = @import("lib");
|
||||
|
||||
pub const Data = union(enum) {
|
||||
raw: struct {
|
||||
@ -70,5 +71,5 @@ pub const Data = union(enum) {
|
||||
};
|
||||
}
|
||||
|
||||
pub const Id = enum (u16) { _ };
|
||||
pub const Id = Lib.AudioId;
|
||||
};
|
||||
@ -6,66 +6,60 @@ const Allocator = std.mem.Allocator;
|
||||
const sokol = @import("sokol");
|
||||
const saudio = sokol.audio;
|
||||
|
||||
const Lib = @import("lib");
|
||||
const AudioData = @import("./data.zig").Data;
|
||||
pub const Store = @import("./store.zig");
|
||||
|
||||
const Mixer = @This();
|
||||
|
||||
pub const Instance = struct {
|
||||
data_id: AudioData.Id,
|
||||
data_id: Lib.AudioId,
|
||||
volume: f32 = 0,
|
||||
cursor: u32 = 0,
|
||||
};
|
||||
|
||||
pub const Command = union(enum) {
|
||||
pub const Play = struct {
|
||||
id: AudioData.Id,
|
||||
volume: f32 = 1
|
||||
};
|
||||
pub const Command = Lib.AudioCommand;
|
||||
|
||||
play: Play,
|
||||
pub const RingBuffer = struct {
|
||||
// TODO: This ring buffer will work in a single producer single consumer configuration
|
||||
// For my game this will be good enough
|
||||
|
||||
pub const RingBuffer = struct {
|
||||
// TODO: This ring buffer will work in a single producer single consumer configuration
|
||||
// For my game this will be good enough
|
||||
items: []Command,
|
||||
head: std.atomic.Value(usize) = .init(0),
|
||||
tail: std.atomic.Value(usize) = .init(0),
|
||||
|
||||
items: []Command,
|
||||
head: std.atomic.Value(usize) = .init(0),
|
||||
tail: std.atomic.Value(usize) = .init(0),
|
||||
pub fn push(self: *RingBuffer, command: Command) error{OutOfMemory}!void {
|
||||
const head = self.head.load(.monotonic);
|
||||
const tail = self.tail.load(.monotonic);
|
||||
const next_head = @mod(head + 1, self.items.len);
|
||||
|
||||
pub fn push(self: *RingBuffer, command: Command) error{OutOfMemory}!void {
|
||||
const head = self.head.load(.monotonic);
|
||||
const tail = self.tail.load(.monotonic);
|
||||
const next_head = @mod(head + 1, self.items.len);
|
||||
|
||||
// A single slot in the .items array will always not be used.
|
||||
if (next_head == tail) {
|
||||
return error.OutOfMemory;
|
||||
}
|
||||
|
||||
self.items[head] = command;
|
||||
self.head.store(next_head, .monotonic);
|
||||
// A single slot in the .items array will always not be used.
|
||||
if (next_head == tail) {
|
||||
return error.OutOfMemory;
|
||||
}
|
||||
|
||||
pub fn pop(self: *RingBuffer) ?Command {
|
||||
const head = self.head.load(.monotonic);
|
||||
const tail = self.tail.load(.monotonic);
|
||||
self.items[head] = command;
|
||||
self.head.store(next_head, .monotonic);
|
||||
}
|
||||
|
||||
if (head == tail) {
|
||||
return null;
|
||||
}
|
||||
pub fn pop(self: *RingBuffer) ?Command {
|
||||
const head = self.head.load(.monotonic);
|
||||
const tail = self.tail.load(.monotonic);
|
||||
|
||||
const result = self.items[tail];
|
||||
self.tail.store(@mod(tail + 1, self.items.len), .monotonic);
|
||||
return result;
|
||||
if (head == tail) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const result = self.items[tail];
|
||||
self.tail.store(@mod(tail + 1, self.items.len), .monotonic);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Tracks
|
||||
|
||||
instances: std.ArrayList(Instance),
|
||||
commands: Command.RingBuffer,
|
||||
commands: RingBuffer,
|
||||
working_buffer: []f32,
|
||||
|
||||
pub fn init(
|
||||
@ -4,7 +4,7 @@ const assert = std.debug.assert;
|
||||
|
||||
const tracy = @import("tracy");
|
||||
|
||||
const Math = @import("../math.zig");
|
||||
const Math = @import("lib").Math;
|
||||
const STBVorbis = @import("stb_vorbis");
|
||||
|
||||
pub const Data = @import("./data.zig").Data;
|
||||
@ -1,7 +1,7 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const Math = @import("../math.zig");
|
||||
const Math = @import("lib").Math;
|
||||
const STBVorbis = @import("stb_vorbis");
|
||||
|
||||
const AudioData = @import("./data.zig").Data;
|
||||
@ -6,7 +6,7 @@ const sapp = sokol.app;
|
||||
const simgui = sokol.imgui;
|
||||
const sgl = sokol.gl;
|
||||
|
||||
const Math = @import("./math.zig");
|
||||
const Math = @import("lib").Math;
|
||||
const Vec2 = Math.Vec2;
|
||||
const Vec4 = Math.Vec4;
|
||||
const rgb = Math.rgb;
|
||||
@ -21,7 +21,8 @@ const tracy = @import("tracy");
|
||||
const fontstash = @import("./fontstash/root.zig");
|
||||
pub const Font = fontstash.Font;
|
||||
|
||||
const GraphicsFrame = @import("./frame.zig").Graphics;
|
||||
const Lib = @import("lib");
|
||||
const Command = Lib.GraphicsCommand;
|
||||
|
||||
const Graphics = @This();
|
||||
|
||||
@ -43,37 +44,6 @@ const Options = struct {
|
||||
imgui_font: ?ImguiFont = null
|
||||
};
|
||||
|
||||
pub const Command = union(enum) {
|
||||
pub const DrawRectangle = struct {
|
||||
rect: Rect,
|
||||
color: Vec4,
|
||||
sprite: ?Sprite = null,
|
||||
rotation: f32 = 0,
|
||||
origin: Vec2 = .init(0, 0),
|
||||
};
|
||||
|
||||
set_scissor: Rect,
|
||||
draw_rectangle: DrawRectangle,
|
||||
draw_line: struct {
|
||||
pos1: Vec2,
|
||||
pos2: Vec2,
|
||||
color: Vec4,
|
||||
width: f32
|
||||
},
|
||||
draw_text: struct {
|
||||
pos: Vec2,
|
||||
text: []const u8,
|
||||
font: Font.Id,
|
||||
size: f32,
|
||||
color: Vec4,
|
||||
},
|
||||
push_transformation: struct {
|
||||
translation: Vec2,
|
||||
scale: Vec2
|
||||
},
|
||||
pop_transformation: void
|
||||
};
|
||||
|
||||
const Texture = struct {
|
||||
image: sg.Image,
|
||||
view: sg.View,
|
||||
@ -84,20 +54,16 @@ const Texture = struct {
|
||||
height: u32,
|
||||
};
|
||||
|
||||
const Id = enum(u32) { _ };
|
||||
const Data = struct {
|
||||
width: u32,
|
||||
height: u32,
|
||||
rgba: [*]u8
|
||||
};
|
||||
};
|
||||
pub const TextureId = Texture.Id;
|
||||
pub const TextureId = Lib.TextureId;
|
||||
pub const TextureInfo = Texture.Info;
|
||||
|
||||
pub const Sprite = struct {
|
||||
texture: TextureId,
|
||||
uv: Rect,
|
||||
};
|
||||
pub const Sprite = Lib.Sprite;
|
||||
|
||||
main_pipeline: sgl.Pipeline,
|
||||
linear_sampler: sg.Sampler,
|
||||
@ -108,6 +74,11 @@ textures: std.ArrayList(Texture) = .empty,
|
||||
scale_stack_buffer: [32]Vec2 = undefined,
|
||||
scale_stack: std.ArrayList(Vec2) = .empty,
|
||||
|
||||
scissor_stack_buffer: [32]Rect = undefined,
|
||||
scissor_stack: std.ArrayList(Rect) = .empty,
|
||||
|
||||
font_id_lookup: std.AutoArrayHashMapUnmanaged(Lib.FontId, fontstash.Font.Id) = .empty,
|
||||
|
||||
pub fn init(self: *Graphics, options: Options) !void {
|
||||
sg.setup(.{
|
||||
.logger = options.logger,
|
||||
@ -187,6 +158,7 @@ pub fn init(self: *Graphics, options: Options) !void {
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Graphics, gpa: std.mem.Allocator) void {
|
||||
self.font_id_lookup.deinit(gpa);
|
||||
self.textures.deinit(gpa);
|
||||
imgui.shutdown();
|
||||
self.font_context.deinit();
|
||||
@ -206,12 +178,29 @@ pub fn drawCommand(self: *Graphics, command: Command) void {
|
||||
.draw_rectangle => |opts| {
|
||||
self.drawRectangle(opts);
|
||||
},
|
||||
.set_scissor => |opts| {
|
||||
.push_scissor => |rect| {
|
||||
self.scissor_stack.appendBounded(rect) catch |e| {
|
||||
log.warn("Failed to push scissor region: {}", .{e});
|
||||
return;
|
||||
};
|
||||
|
||||
sgl.scissorRectf(
|
||||
opts.pos.x,
|
||||
opts.pos.y,
|
||||
opts.size.x,
|
||||
opts.size.y,
|
||||
rect.pos.x,
|
||||
rect.pos.y,
|
||||
rect.size.x,
|
||||
rect.size.y,
|
||||
true
|
||||
);
|
||||
},
|
||||
.pop_scissor => {
|
||||
_ = self.scissor_stack.pop().?;
|
||||
const rect = self.scissor_stack.getLast();
|
||||
|
||||
sgl.scissorRectf(
|
||||
rect.pos.x,
|
||||
rect.pos.y,
|
||||
rect.size.x,
|
||||
rect.size.y,
|
||||
true
|
||||
);
|
||||
},
|
||||
@ -231,7 +220,11 @@ pub fn drawCommand(self: *Graphics, command: Command) void {
|
||||
|
||||
sgl.scale(1/font_resolution_scale.x, 1/font_resolution_scale.y, 1);
|
||||
|
||||
self.font_context.setFont(opts.font);
|
||||
const font_id = self.font_id_lookup.get(opts.font) orelse {
|
||||
log.warn("Attempt to use font that doesn't exist", .{});
|
||||
return;
|
||||
};
|
||||
self.font_context.setFont(font_id);
|
||||
self.font_context.setSize(opts.size * font_resolution_scale.y);
|
||||
self.font_context.setAlign(.{ .x = .left, .y = .top });
|
||||
self.font_context.setSpacing(0);
|
||||
@ -271,6 +264,9 @@ pub fn beginFrame(self: *Graphics) void {
|
||||
self.scale_stack = .initBuffer(&self.scale_stack_buffer);
|
||||
self.scale_stack.appendAssumeCapacity(.init(1, 1));
|
||||
|
||||
self.scissor_stack = .initBuffer(&self.scissor_stack_buffer);
|
||||
self.scissor_stack.appendAssumeCapacity(.init(0, 0, sapp.widthf(), sapp.heightf()));
|
||||
|
||||
self.font_context.clearState();
|
||||
sgl.defaults();
|
||||
sgl.matrixModeProjection();
|
||||
@ -310,7 +306,7 @@ pub fn endFrame(self: *Graphics, clear_color: Vec4) void {
|
||||
sg.commit();
|
||||
}
|
||||
|
||||
fn pushTransform(self: *Graphics, translation: Vec2, scale: Vec2) void {
|
||||
pub fn pushTransform(self: *Graphics, translation: Vec2, scale: Vec2) void {
|
||||
sgl.pushMatrix();
|
||||
sgl.translate(translation.x, translation.y, 0);
|
||||
sgl.scale(scale.x, scale.y, 1);
|
||||
@ -318,7 +314,7 @@ fn pushTransform(self: *Graphics, translation: Vec2, scale: Vec2) void {
|
||||
self.scale_stack.appendAssumeCapacity(self.scale_stack.getLast().multiply(scale));
|
||||
}
|
||||
|
||||
fn popTransform(self: *Graphics) void {
|
||||
pub fn popTransform(self: *Graphics) void {
|
||||
sgl.popMatrix();
|
||||
_ = self.scale_stack.pop().?;
|
||||
}
|
||||
@ -357,7 +353,7 @@ fn drawQuadNoUVs(quad: [4]Vec2, color: Vec4) void {
|
||||
}
|
||||
}
|
||||
|
||||
fn drawRectangle(self: *Graphics, opts: Command.DrawRectangle) void {
|
||||
pub fn drawRectangle(self: *Graphics, opts: Command.DrawRectangle) void {
|
||||
const pos = opts.rect.pos;
|
||||
const size = opts.rect.size;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
const std = @import("std");
|
||||
const Math = @import("./math.zig");
|
||||
const Lib = @import("lib");
|
||||
const Math = Lib.Math;
|
||||
const build_options = @import("build_options");
|
||||
pub const ig = @import("cimgui");
|
||||
const Vec2 = Math.Vec2;
|
||||
210
engine/src/runtime/input.zig
Normal file
210
engine/src/runtime/input.zig
Normal file
@ -0,0 +1,210 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const sokol = @import("sokol");
|
||||
|
||||
const lib = @import("lib");
|
||||
const Frame = lib.Frame;
|
||||
const Nanoseconds = lib.Nanoseconds;
|
||||
const Vec2 = lib.Math.Vec2;
|
||||
|
||||
const Input = @This();
|
||||
|
||||
const SokolKeyCodeInfo = @typeInfo(sokol.app.Keycode);
|
||||
|
||||
pub const Event = union(enum) {
|
||||
mouse_pressed: struct {
|
||||
button: lib.MouseButton,
|
||||
position: Vec2,
|
||||
},
|
||||
mouse_released: struct {
|
||||
button: lib.MouseButton,
|
||||
position: Vec2,
|
||||
},
|
||||
mouse_move: Vec2,
|
||||
mouse_enter: Vec2,
|
||||
mouse_leave,
|
||||
mouse_scroll: Vec2,
|
||||
key_pressed: struct {
|
||||
code: lib.KeyCode,
|
||||
repeat: bool
|
||||
},
|
||||
key_released: lib.KeyCode,
|
||||
window_resize,
|
||||
char: u21,
|
||||
};
|
||||
|
||||
mouse_position: ?Vec2 = null,
|
||||
|
||||
pub fn processEvent(self: *Input, frame: *Frame, event: Event) void {
|
||||
switch (event) {
|
||||
.key_pressed => |opts| {
|
||||
if (!opts.repeat) {
|
||||
frame.keyboard.press(opts.code, frame.time_ns);
|
||||
}
|
||||
},
|
||||
.key_released => |key_code| {
|
||||
frame.keyboard.release(key_code);
|
||||
},
|
||||
.mouse_leave => {
|
||||
frame.keyboard.releaseAll();
|
||||
|
||||
assert(self.mouse_position != null);
|
||||
self.mouse_position = null;
|
||||
frame.mouse_button = .empty;
|
||||
},
|
||||
.mouse_enter => |pos| {
|
||||
assert(self.mouse_position == null);
|
||||
self.mouse_position = pos;
|
||||
},
|
||||
.mouse_move => |pos| {
|
||||
assert(self.mouse_position != null);
|
||||
self.mouse_position = pos;
|
||||
},
|
||||
.mouse_pressed => |opts| {
|
||||
assert(self.mouse_position != null);
|
||||
self.mouse_position = opts.position;
|
||||
frame.mouse_button.press(opts.button, frame.time_ns);
|
||||
},
|
||||
.mouse_released => |opts| {
|
||||
assert(self.mouse_position != null);
|
||||
self.mouse_position = opts.position;
|
||||
frame.mouse_button.release(opts.button);
|
||||
},
|
||||
else => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getKeyCodeFromSokol(key_code: sokol.app.Keycode) ?lib.KeyCode {
|
||||
return switch (key_code) {
|
||||
.SPACE => .SPACE,
|
||||
.APOSTROPHE => .APOSTROPHE,
|
||||
.COMMA => .COMMA,
|
||||
.MINUS => .MINUS,
|
||||
.PERIOD => .PERIOD,
|
||||
.SLASH => .SLASH,
|
||||
._0 => ._0,
|
||||
._1 => ._1,
|
||||
._2 => ._2,
|
||||
._3 => ._3,
|
||||
._4 => ._4,
|
||||
._5 => ._5,
|
||||
._6 => ._6,
|
||||
._7 => ._7,
|
||||
._8 => ._8,
|
||||
._9 => ._9,
|
||||
.SEMICOLON => .SEMICOLON,
|
||||
.EQUAL => .EQUAL,
|
||||
.A => .A,
|
||||
.B => .B,
|
||||
.C => .C,
|
||||
.D => .D,
|
||||
.E => .E,
|
||||
.F => .F,
|
||||
.G => .G,
|
||||
.H => .H,
|
||||
.I => .I,
|
||||
.J => .J,
|
||||
.K => .K,
|
||||
.L => .L,
|
||||
.M => .M,
|
||||
.N => .N,
|
||||
.O => .O,
|
||||
.P => .P,
|
||||
.Q => .Q,
|
||||
.R => .R,
|
||||
.S => .S,
|
||||
.T => .T,
|
||||
.U => .U,
|
||||
.V => .V,
|
||||
.W => .W,
|
||||
.X => .X,
|
||||
.Y => .Y,
|
||||
.Z => .Z,
|
||||
.LEFT_BRACKET => .LEFT_BRACKET,
|
||||
.BACKSLASH => .BACKSLASH,
|
||||
.RIGHT_BRACKET => .RIGHT_BRACKET,
|
||||
.GRAVE_ACCENT => .GRAVE_ACCENT,
|
||||
.WORLD_1 => .WORLD_1,
|
||||
.WORLD_2 => .WORLD_2,
|
||||
.ESCAPE => .ESCAPE,
|
||||
.ENTER => .ENTER,
|
||||
.TAB => .TAB,
|
||||
.BACKSPACE => .BACKSPACE,
|
||||
.INSERT => .INSERT,
|
||||
.DELETE => .DELETE,
|
||||
.RIGHT => .RIGHT,
|
||||
.LEFT => .LEFT,
|
||||
.DOWN => .DOWN,
|
||||
.UP => .UP,
|
||||
.PAGE_UP => .PAGE_UP,
|
||||
.PAGE_DOWN => .PAGE_DOWN,
|
||||
.HOME => .HOME,
|
||||
.END => .END,
|
||||
.CAPS_LOCK => .CAPS_LOCK,
|
||||
.SCROLL_LOCK => .SCROLL_LOCK,
|
||||
.NUM_LOCK => .NUM_LOCK,
|
||||
.PRINT_SCREEN => .PRINT_SCREEN,
|
||||
.PAUSE => .PAUSE,
|
||||
.F1 => .F1,
|
||||
.F2 => .F2,
|
||||
.F3 => .F3,
|
||||
.F4 => .F4,
|
||||
.F5 => .F5,
|
||||
.F6 => .F6,
|
||||
.F7 => .F7,
|
||||
.F8 => .F8,
|
||||
.F9 => .F9,
|
||||
.F10 => .F10,
|
||||
.F11 => .F11,
|
||||
.F12 => .F12,
|
||||
.F13 => .F13,
|
||||
.F14 => .F14,
|
||||
.F15 => .F15,
|
||||
.F16 => .F16,
|
||||
.F17 => .F17,
|
||||
.F18 => .F18,
|
||||
.F19 => .F19,
|
||||
.F20 => .F20,
|
||||
.F21 => .F21,
|
||||
.F22 => .F22,
|
||||
.F23 => .F23,
|
||||
.F24 => .F24,
|
||||
.F25 => .F25,
|
||||
.KP_0 => .KP_0,
|
||||
.KP_1 => .KP_1,
|
||||
.KP_2 => .KP_2,
|
||||
.KP_3 => .KP_3,
|
||||
.KP_4 => .KP_4,
|
||||
.KP_5 => .KP_5,
|
||||
.KP_6 => .KP_6,
|
||||
.KP_7 => .KP_7,
|
||||
.KP_8 => .KP_8,
|
||||
.KP_9 => .KP_9,
|
||||
.KP_DECIMAL => .KP_DECIMAL,
|
||||
.KP_DIVIDE => .KP_DIVIDE,
|
||||
.KP_MULTIPLY => .KP_MULTIPLY,
|
||||
.KP_SUBTRACT => .KP_SUBTRACT,
|
||||
.KP_ADD => .KP_ADD,
|
||||
.KP_ENTER => .KP_ENTER,
|
||||
.KP_EQUAL => .KP_EQUAL,
|
||||
.LEFT_SHIFT => .LEFT_SHIFT,
|
||||
.LEFT_CONTROL => .LEFT_CONTROL,
|
||||
.LEFT_ALT => .LEFT_ALT,
|
||||
.LEFT_SUPER => .LEFT_SUPER,
|
||||
.RIGHT_SHIFT => .RIGHT_SHIFT,
|
||||
.RIGHT_CONTROL => .RIGHT_CONTROL,
|
||||
.RIGHT_ALT => .RIGHT_ALT,
|
||||
.RIGHT_SUPER => .RIGHT_SUPER,
|
||||
.MENU => .MENU,
|
||||
else => null
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getMouseButtonFromSokol(mouse_button: sokol.app.Mousebutton) ?lib.MouseButton {
|
||||
return switch (mouse_button) {
|
||||
.RIGHT => .right,
|
||||
.MIDDLE => .middle,
|
||||
.LEFT => .left,
|
||||
else => null
|
||||
};
|
||||
}
|
||||
@ -5,14 +5,8 @@ const assert = std.debug.assert;
|
||||
const sokol = @import("sokol");
|
||||
const sapp = sokol.app;
|
||||
|
||||
pub const Math = @import("./math.zig");
|
||||
pub const Vec2 = Math.Vec2;
|
||||
const rgb = Math.rgb;
|
||||
|
||||
pub const Input = @import("./input.zig");
|
||||
|
||||
pub const Frame = @import("./frame.zig");
|
||||
|
||||
const ScreenScalar = @import("./screen_scaler.zig");
|
||||
pub const imgui = @import("./imgui.zig");
|
||||
pub const Graphics = @import("./graphics.zig");
|
||||
@ -23,37 +17,44 @@ const STBImage = @import("stb_image");
|
||||
|
||||
const Gfx = Graphics;
|
||||
|
||||
const Lib = @import("lib");
|
||||
pub const Math = Lib.Math;
|
||||
pub const Vec2 = Math.Vec2;
|
||||
const rgb = Math.rgb;
|
||||
|
||||
const GameCallbacks = struct {
|
||||
init: *const fn(opts: *const Engine.InitOptions) callconv(.c) void,
|
||||
tick: *const fn(frame: *Frame) callconv(.c) void,
|
||||
init: *const fn(opts: *const Lib.Init) callconv(.c) void,
|
||||
tick: *const fn(frame: *Lib.Frame) callconv(.c) void,
|
||||
deinit: *const fn() callconv(.c) void,
|
||||
debug: *const fn() callconv(.c) void,
|
||||
debug: *const fn(imgui: *Lib.ImGui) callconv(.c) void,
|
||||
};
|
||||
|
||||
extern fn init(opts: *const Engine.InitOptions) void;
|
||||
extern fn tick(frame: *Frame) void;
|
||||
extern fn init(opts: *const Lib.Init) void;
|
||||
extern fn tick(frame: *Lib.Frame) void;
|
||||
extern fn deinit() void;
|
||||
extern fn debug() void;
|
||||
extern fn debug(imgui: *Lib.ImGui) void;
|
||||
const static_game_callbacks = GameCallbacks{
|
||||
.init = init,
|
||||
.deinit = deinit,
|
||||
.tick = tick,
|
||||
.debug = debug,
|
||||
};
|
||||
|
||||
|
||||
const Engine = @This();
|
||||
|
||||
pub const Nanoseconds = u64;
|
||||
|
||||
pub const InitOptions = struct {
|
||||
gpa: std.mem.Allocator,
|
||||
seed: u64
|
||||
};
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
started_at: std.time.Instant,
|
||||
mouse_inside: bool,
|
||||
last_frame_at: Nanoseconds,
|
||||
graphics: Graphics,
|
||||
input: Input,
|
||||
game_callbacks: GameCallbacks,
|
||||
|
||||
frame: Frame,
|
||||
frame: Lib.Frame,
|
||||
|
||||
canvas_size: ?Vec2 = null,
|
||||
previous_tick_canvas_size: ?Vec2 = null,
|
||||
|
||||
const RunOptions = struct {
|
||||
window_title: [*:0]const u8 = "Game",
|
||||
@ -65,16 +66,11 @@ pub fn run(self: *Engine, opts: RunOptions) !void {
|
||||
self.* = Engine{
|
||||
.allocator = undefined,
|
||||
.started_at = std.time.Instant.now() catch @panic("Instant.now() unsupported"),
|
||||
.mouse_inside = false,
|
||||
.last_frame_at = 0,
|
||||
.frame = undefined,
|
||||
.graphics = undefined,
|
||||
.game_callbacks = .{
|
||||
.init = init,
|
||||
.deinit = deinit,
|
||||
.tick = tick,
|
||||
.debug = debug,
|
||||
}
|
||||
.game_callbacks = static_game_callbacks,
|
||||
.input = .{}
|
||||
};
|
||||
|
||||
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
|
||||
@ -157,7 +153,7 @@ fn sokolInit(self: *Engine) !void {
|
||||
|
||||
const seed: u64 = @bitCast(std.time.milliTimestamp());
|
||||
|
||||
const opts = InitOptions{
|
||||
const opts = Lib.Init{
|
||||
.gpa = self.allocator,
|
||||
.seed = seed,
|
||||
};
|
||||
@ -188,83 +184,71 @@ fn sokolFrame(self: *Engine) !void {
|
||||
|
||||
const screen_size = Vec2.init(sapp.widthf(), sapp.heightf());
|
||||
|
||||
var revert_mouse_position: ?Vec2 = null;
|
||||
if (self.canvas_size) |canvas_size| {
|
||||
if (self.frame.input.mouse_position) |mouse| {
|
||||
const transform = ScreenScalar.init(screen_size, canvas_size);
|
||||
|
||||
revert_mouse_position = mouse;
|
||||
self.frame.input.mouse_position = mouse.sub(transform.translation).divideScalar(transform.scale);
|
||||
}
|
||||
var maybe_screen_scaler: ?ScreenScalar = null;
|
||||
if (self.previous_tick_canvas_size) |canvas_size| {
|
||||
maybe_screen_scaler = ScreenScalar.init(screen_size, canvas_size);
|
||||
}
|
||||
|
||||
{
|
||||
_ = frame.arena.reset(.retain_capacity);
|
||||
const arena = frame.arena.allocator();
|
||||
|
||||
const audio_commands_capacity = frame.audio.commands.capacity;
|
||||
frame.audio = .empty;
|
||||
try frame.audio.commands.ensureTotalCapacity(arena, audio_commands_capacity);
|
||||
const audio_commands_capacity = frame.audio_commands.capacity;
|
||||
frame.audio_commands = .empty;
|
||||
try frame.audio_commands.ensureTotalCapacity(arena, audio_commands_capacity);
|
||||
|
||||
const graphics_commands_capacity = frame.graphics.commands.capacity;
|
||||
const scissor_stack_capacity = frame.graphics.scissor_stack.capacity;
|
||||
frame.graphics = .empty;
|
||||
frame.graphics.screen_size = screen_size;
|
||||
try frame.graphics.commands.ensureTotalCapacity(arena, graphics_commands_capacity);
|
||||
try frame.graphics.scissor_stack.ensureTotalCapacity(arena, scissor_stack_capacity);
|
||||
frame.pushScissor(.init(0, 0, sapp.widthf(), sapp.heightf()));
|
||||
const graphics_commands_capacity = frame.graphics_commands.capacity;
|
||||
frame.graphics_commands = .empty;
|
||||
try frame.graphics_commands.ensureTotalCapacity(arena, graphics_commands_capacity);
|
||||
|
||||
frame.screen_size = screen_size;
|
||||
frame.time_ns = time_passed;
|
||||
frame.dt_ns = time_passed - self.last_frame_at;
|
||||
frame.hide_cursor = false;
|
||||
|
||||
self.game_callbacks.tick(&self.frame);
|
||||
frame.mouse_position = self.input.mouse_position;
|
||||
if (maybe_screen_scaler) |screen_scaler| {
|
||||
screen_scaler.push(frame);
|
||||
|
||||
frame.input.keyboard.pressed = .initEmpty();
|
||||
frame.input.keyboard.released = .initEmpty();
|
||||
frame.input.mouse_button.pressed = .initEmpty();
|
||||
frame.input.mouse_button.released = .initEmpty();
|
||||
if (frame.mouse_position) |mouse_position| {
|
||||
frame.mouse_position = mouse_position.sub(screen_scaler.translation).divideScalar(screen_scaler.scale);
|
||||
}
|
||||
}
|
||||
|
||||
self.game_callbacks.tick(frame);
|
||||
|
||||
if (maybe_screen_scaler) |screen_scaler| {
|
||||
screen_scaler.pop(frame, frame.clear_color);
|
||||
}
|
||||
|
||||
frame.keyboard.pressed = .initEmpty();
|
||||
frame.keyboard.released = .initEmpty();
|
||||
frame.mouse_button.pressed = .initEmpty();
|
||||
frame.mouse_button.released = .initEmpty();
|
||||
}
|
||||
|
||||
if (self.canvas_size) |canvas_size| {
|
||||
const transform = ScreenScalar.init(
|
||||
screen_size,
|
||||
canvas_size
|
||||
);
|
||||
transform.apply(
|
||||
screen_size,
|
||||
&self.frame,
|
||||
rgb(0, 0, 0)
|
||||
);
|
||||
}
|
||||
|
||||
sapp.showMouse(!self.frame.hide_cursor);
|
||||
|
||||
// Canvas size modification must always be applied a frame later.
|
||||
// So that mouse coordinate transformations are consistent.
|
||||
self.canvas_size = self.frame.graphics.canvas_size;
|
||||
self.previous_tick_canvas_size = self.frame.canvas_size;
|
||||
|
||||
sapp.showMouse(!self.frame.hide_cursor);
|
||||
|
||||
{
|
||||
self.graphics.beginFrame();
|
||||
defer self.graphics.endFrame(frame.graphics.clear_color);
|
||||
defer self.graphics.endFrame(frame.clear_color);
|
||||
|
||||
self.graphics.drawCommands(frame.graphics.commands.items);
|
||||
self.graphics.drawCommands(frame.graphics_commands.items);
|
||||
|
||||
if (frame.show_debug) {
|
||||
try self.showDebugWindow(&self.frame);
|
||||
try self.showDebugWindow(frame);
|
||||
}
|
||||
}
|
||||
|
||||
for (frame.audio.commands.items) |command| {
|
||||
for (frame.audio_commands.items) |command| {
|
||||
try Audio.mixer.commands.push(command);
|
||||
}
|
||||
|
||||
if (revert_mouse_position) |pos| {
|
||||
self.frame.input.mouse_position = pos;
|
||||
}
|
||||
}
|
||||
|
||||
fn showDebugWindow(self: *Engine, frame: *Frame) !void {
|
||||
fn showDebugWindow(self: *Engine, frame: *Lib.Frame) !void {
|
||||
if (!imgui.beginWindow(.{
|
||||
.name = "Debug",
|
||||
.pos = Vec2.init(20, 20),
|
||||
@ -274,24 +258,31 @@ fn showDebugWindow(self: *Engine, frame: *Frame) !void {
|
||||
}
|
||||
defer imgui.endWindow();
|
||||
|
||||
if (imgui.beginTabBar("debug")) {
|
||||
defer imgui.endTabBar();
|
||||
_ = imgui.beginTabBar("debug");
|
||||
|
||||
if (imgui.beginTabItem("Game")) {
|
||||
defer imgui.endTabItem();
|
||||
self.game_callbacks.debug();
|
||||
}
|
||||
if (imgui.beginTabItem("Engine")) {
|
||||
defer imgui.endTabItem();
|
||||
imgui.textFmt("Draw commands: {}\n", .{
|
||||
frame.graphics.commands.items.len,
|
||||
});
|
||||
imgui.textFmt("Audio instances: {}/{}\n", .{
|
||||
Audio.mixer.instances.items.len,
|
||||
Audio.mixer.instances.capacity
|
||||
});
|
||||
if (imgui.beginTabItem("Game")) {
|
||||
defer imgui.endTabItem();
|
||||
|
||||
var imgui_ctx = Lib.ImGui.init(self.allocator);
|
||||
defer imgui_ctx.deinit();
|
||||
self.game_callbacks.debug(&imgui_ctx);
|
||||
for (imgui_ctx.commands.items) |cmd| {
|
||||
switch (cmd) {
|
||||
.text => |str| imgui.text(str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (imgui.beginTabItem("Engine")) {
|
||||
defer imgui.endTabItem();
|
||||
imgui.textFmt("Draw commands: {}\n", .{
|
||||
frame.graphics_commands.items.len,
|
||||
});
|
||||
imgui.textFmt("Audio instances: {}/{}\n", .{
|
||||
Audio.mixer.instances.items.len,
|
||||
Audio.mixer.instances.capacity
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn sokolEvent(self: *Engine, e_ptr: [*c]const sapp.Event) !bool {
|
||||
@ -301,20 +292,22 @@ fn sokolEvent(self: *Engine, e_ptr: [*c]const sapp.Event) !bool {
|
||||
const e = e_ptr.*;
|
||||
|
||||
if (imgui.handleEvent(e)) {
|
||||
if (self.mouse_inside) {
|
||||
Input.processEvent(&self.frame, .{
|
||||
if (self.input.mouse_position != null) {
|
||||
self.input.processEvent(&self.frame, .{
|
||||
.mouse_leave = {}
|
||||
});
|
||||
}
|
||||
self.mouse_inside = false;
|
||||
self.input.mouse_position = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (e.type) {
|
||||
blk: switch (e.type) {
|
||||
.MOUSE_DOWN => {
|
||||
Input.processEvent(&self.frame, .{
|
||||
const mouse_button = Input.getMouseButtonFromSokol(e.mouse_button) orelse break :blk;
|
||||
|
||||
self.input.processEvent(&self.frame, .{
|
||||
.mouse_pressed = .{
|
||||
.button = @enumFromInt(@intFromEnum(e.mouse_button)),
|
||||
.button = mouse_button,
|
||||
.position = Vec2.init(e.mouse_x, e.mouse_y)
|
||||
}
|
||||
});
|
||||
@ -322,9 +315,11 @@ fn sokolEvent(self: *Engine, e_ptr: [*c]const sapp.Event) !bool {
|
||||
return true;
|
||||
},
|
||||
.MOUSE_UP => {
|
||||
Input.processEvent(&self.frame, .{
|
||||
const mouse_button = Input.getMouseButtonFromSokol(e.mouse_button) orelse break :blk;
|
||||
|
||||
self.input.processEvent(&self.frame, .{
|
||||
.mouse_released = .{
|
||||
.button = @enumFromInt(@intFromEnum(e.mouse_button)),
|
||||
.button = mouse_button,
|
||||
.position = Vec2.init(e.mouse_x, e.mouse_y)
|
||||
}
|
||||
});
|
||||
@ -332,64 +327,59 @@ fn sokolEvent(self: *Engine, e_ptr: [*c]const sapp.Event) !bool {
|
||||
return true;
|
||||
},
|
||||
.MOUSE_MOVE => {
|
||||
if (!self.mouse_inside) {
|
||||
Input.processEvent(&self.frame, .{
|
||||
if (self.input.mouse_position == null) {
|
||||
self.input.processEvent(&self.frame, .{
|
||||
.mouse_enter = Vec2.init(e.mouse_x, e.mouse_y)
|
||||
});
|
||||
} else {
|
||||
Input.processEvent(&self.frame, .{
|
||||
self.input.processEvent(&self.frame, .{
|
||||
.mouse_move = Vec2.init(e.mouse_x, e.mouse_y)
|
||||
});
|
||||
}
|
||||
|
||||
self.mouse_inside = true;
|
||||
return true;
|
||||
},
|
||||
.MOUSE_ENTER => {
|
||||
if (!self.mouse_inside) {
|
||||
Input.processEvent(&self.frame, .{
|
||||
if (self.input.mouse_position == null) {
|
||||
self.input.processEvent(&self.frame, .{
|
||||
.mouse_enter = Vec2.init(e.mouse_x, e.mouse_y)
|
||||
});
|
||||
}
|
||||
|
||||
self.mouse_inside = true;
|
||||
return true;
|
||||
},
|
||||
.RESIZED => {
|
||||
if (self.mouse_inside) {
|
||||
Input.processEvent(&self.frame, .{
|
||||
if (self.input.mouse_position != null) {
|
||||
self.input.processEvent(&self.frame, .{
|
||||
.mouse_leave = {}
|
||||
});
|
||||
}
|
||||
|
||||
Input.processEvent(&self.frame, .{
|
||||
self.input.processEvent(&self.frame, .{
|
||||
.window_resize = {}
|
||||
});
|
||||
|
||||
self.mouse_inside = false;
|
||||
return true;
|
||||
},
|
||||
.MOUSE_LEAVE => {
|
||||
if (self.mouse_inside) {
|
||||
Input.processEvent(&self.frame, .{
|
||||
if (self.input.mouse_position != null) {
|
||||
self.input.processEvent(&self.frame, .{
|
||||
.mouse_leave = {}
|
||||
});
|
||||
}
|
||||
|
||||
self.mouse_inside = false;
|
||||
return true;
|
||||
},
|
||||
.MOUSE_SCROLL => {
|
||||
Input.processEvent(&self.frame, .{
|
||||
self.input.processEvent(&self.frame, .{
|
||||
.mouse_scroll = Vec2.init(e.scroll_x, e.scroll_y)
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
.KEY_DOWN => {
|
||||
Input.processEvent(&self.frame, .{
|
||||
const key_code = Input.getKeyCodeFromSokol(e.key_code) orelse break :blk;
|
||||
|
||||
self.input.processEvent(&self.frame, .{
|
||||
.key_pressed = .{
|
||||
.code = @enumFromInt(@intFromEnum(e.key_code)),
|
||||
.code = key_code,
|
||||
.repeat = e.key_repeat
|
||||
}
|
||||
});
|
||||
@ -397,14 +387,16 @@ fn sokolEvent(self: *Engine, e_ptr: [*c]const sapp.Event) !bool {
|
||||
return true;
|
||||
},
|
||||
.KEY_UP => {
|
||||
Input.processEvent(&self.frame, .{
|
||||
.key_released = @enumFromInt(@intFromEnum(e.key_code))
|
||||
const key_code = Input.getKeyCodeFromSokol(e.key_code) orelse break :blk;
|
||||
|
||||
self.input.processEvent(&self.frame, .{
|
||||
.key_released = key_code
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
.CHAR => {
|
||||
Input.processEvent(&self.frame, .{
|
||||
self.input.processEvent(&self.frame, .{
|
||||
.char = @intCast(e.char_code)
|
||||
});
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
const Gfx = @import("./graphics.zig");
|
||||
|
||||
const Math = @import("./math.zig");
|
||||
const Math = @import("lib").Math;
|
||||
const Vec2 = Math.Vec2;
|
||||
const Vec4 = Math.Vec4;
|
||||
const rgb = Math.rgb;
|
||||
|
||||
const Frame = @import("./frame.zig");
|
||||
const Lib = @import("lib");
|
||||
const Frame = Lib.Frame;
|
||||
|
||||
const ScreenScalar = @This();
|
||||
|
||||
@ -34,30 +35,30 @@ pub fn init(window_size: Vec2, canvas_size: Vec2) ScreenScalar {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn apply(self: ScreenScalar, window_size: Vec2, frame: *Frame, color: Vec4) void {
|
||||
pub fn push(self: ScreenScalar, frame: *Frame) void {
|
||||
const scale = self.scale;
|
||||
const translation = self.translation;
|
||||
|
||||
frame.prependGraphicsCommand(.{
|
||||
.push_transformation = .{
|
||||
.translation = translation,
|
||||
.scale = .init(scale, scale)
|
||||
}
|
||||
});
|
||||
frame.pushTransform(translation, .init(scale, scale));
|
||||
}
|
||||
|
||||
pub fn pop(self: ScreenScalar, frame: *Frame, color: Vec4) void {
|
||||
const translation = self.translation;
|
||||
|
||||
frame.popTransform();
|
||||
|
||||
frame.drawRectangle(.{
|
||||
.rect = .{
|
||||
.pos = .init(0, 0),
|
||||
.size = .init(window_size.x, translation.y),
|
||||
.size = .init(frame.screen_size.x, translation.y),
|
||||
},
|
||||
.color = color
|
||||
});
|
||||
|
||||
frame.drawRectangle(.{
|
||||
.rect = .{
|
||||
.pos = .init(0, window_size.y - translation.y),
|
||||
.size = .init(window_size.x, translation.y),
|
||||
.pos = .init(0, frame.screen_size.y - translation.y),
|
||||
.size = .init(frame.screen_size.x, translation.y),
|
||||
},
|
||||
.color = color
|
||||
});
|
||||
@ -65,15 +66,15 @@ pub fn apply(self: ScreenScalar, window_size: Vec2, frame: *Frame, color: Vec4)
|
||||
frame.drawRectangle(.{
|
||||
.rect = .{
|
||||
.pos = .init(0, 0),
|
||||
.size = .init(translation.x, window_size.y),
|
||||
.size = .init(translation.x, frame.screen_size.y),
|
||||
},
|
||||
.color = color
|
||||
});
|
||||
|
||||
frame.drawRectangle(.{
|
||||
.rect = .{
|
||||
.pos = .init(window_size.x - translation.x, 0),
|
||||
.size = .init(translation.x, window_size.y),
|
||||
.pos = .init(frame.screen_size.x - translation.x, 0),
|
||||
.size = .init(translation.x, frame.screen_size.y),
|
||||
},
|
||||
.color = color
|
||||
});
|
||||
16
src/game.zig
16
src/game.zig
@ -3,11 +3,9 @@ const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const Engine = @import("engine");
|
||||
const imgui = Engine.imgui;
|
||||
const Vec2 = Engine.Vec2;
|
||||
const FontId = Engine.FontId;
|
||||
const Vec2 = Engine.Math.Vec2;
|
||||
const rgb = Engine.Math.rgb;
|
||||
const Gfx = Engine.Graphics;
|
||||
const Audio = Engine.Audio;
|
||||
|
||||
const Game = @This();
|
||||
|
||||
@ -16,7 +14,7 @@ const FontName = enum {
|
||||
bold,
|
||||
italic,
|
||||
|
||||
const EnumArray = std.EnumArray(FontName, Gfx.Font.Id);
|
||||
const EnumArray = std.EnumArray(FontName, FontId);
|
||||
};
|
||||
|
||||
const State = struct {
|
||||
@ -29,7 +27,7 @@ const State = struct {
|
||||
|
||||
var g_state: *State = undefined;
|
||||
|
||||
pub export fn init(opts: *const Engine.InitOptions) void {
|
||||
pub export fn init(opts: *const Engine.Init) void {
|
||||
const gpa = opts.gpa;
|
||||
|
||||
// const font_id_array: FontName.EnumArray = .init(.{
|
||||
@ -63,7 +61,7 @@ pub export fn tick(frame: *Engine.Frame) void {
|
||||
const dt = frame.deltaTime();
|
||||
|
||||
const canvas_size = Vec2.init(100, 100);
|
||||
frame.graphics.canvas_size = canvas_size;
|
||||
frame.canvas_size = canvas_size;
|
||||
|
||||
if (frame.isKeyPressed(.F3)) {
|
||||
frame.show_debug = !frame.show_debug;
|
||||
@ -116,6 +114,6 @@ pub export fn tick(frame: *Engine.Frame) void {
|
||||
// });
|
||||
}
|
||||
|
||||
pub export fn debug() void {
|
||||
imgui.text("Hello World!\n");
|
||||
pub export fn debug(imgui: *Engine.ImGui) void {
|
||||
imgui.text("Hello World!\n", .{});
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user