add game code loading
This commit is contained in:
parent
6407263540
commit
9d733902b5
@ -20,7 +20,7 @@ pub fn build(b: *std.Build) !void {
|
||||
.has_imgui = b.option(bool, "imgui", "ImGui integration"),
|
||||
.has_tracy = b.option(bool, "tracy", "Tracy integration"),
|
||||
.win32_has_console = b.option(bool, "console", "Show console (Window only)"),
|
||||
.win32_png_icon = b.path("src/assets/icon.png")
|
||||
.win32_png_icon = b.path("src/assets/icon.png"),
|
||||
});
|
||||
|
||||
{
|
||||
|
||||
@ -14,6 +14,7 @@ const InitOptions = struct {
|
||||
|
||||
dep_engine: *std.Build.Dependency,
|
||||
|
||||
hot_reload: ?bool = null,
|
||||
has_imgui: ?bool = null,
|
||||
has_tracy: ?bool = null,
|
||||
win32_has_console: ?bool = null,
|
||||
@ -23,12 +24,19 @@ const InitOptions = struct {
|
||||
pub fn init(outer_builder: *std.Build, opts: InitOptions) void {
|
||||
const b = opts.dep_engine.builder;
|
||||
|
||||
const isWasm = opts.target.result.cpu.arch.isWasm();
|
||||
|
||||
var hot_reload = false;
|
||||
if (!isWasm) {
|
||||
hot_reload = opts.hot_reload orelse (opts.optimize == .Debug);
|
||||
}
|
||||
|
||||
opts.root_module.resolved_target = opts.target;
|
||||
opts.root_module.optimize = opts.optimize;
|
||||
const game_lib = b.addLibrary(.{
|
||||
.name = "game",
|
||||
.root_module = opts.root_module,
|
||||
.linkage = .static,
|
||||
.linkage = if (hot_reload) .dynamic else .static,
|
||||
});
|
||||
const engine_lib = b.createModule(.{
|
||||
.root_source_file = b.path("src/lib/root.zig")
|
||||
@ -39,7 +47,8 @@ pub fn init(outer_builder: *std.Build, opts: InitOptions) void {
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.has_tracy = opts.has_tracy,
|
||||
.has_imgui = opts.has_imgui
|
||||
.has_imgui = opts.has_imgui,
|
||||
.hot_reload = hot_reload
|
||||
});
|
||||
|
||||
const mod_main = b.createModule(.{
|
||||
@ -50,10 +59,12 @@ pub fn init(outer_builder: *std.Build, opts: InitOptions) void {
|
||||
});
|
||||
mod_main.addImport("lib", engine_lib);
|
||||
mod_main.addImport("engine", engine_module.root_module);
|
||||
mod_main.linkLibrary(game_lib);
|
||||
if (!hot_reload) {
|
||||
mod_main.linkLibrary(game_lib);
|
||||
}
|
||||
|
||||
var run_cmd_step: *std.Build.Step = undefined;
|
||||
if (opts.target.result.cpu.arch.isWasm()) {
|
||||
if (isWasm) {
|
||||
const build_step = buildWasm(b, .{
|
||||
.name = "index",
|
||||
.root_module = mod_main,
|
||||
@ -83,6 +94,9 @@ pub fn init(outer_builder: *std.Build, opts: InitOptions) void {
|
||||
if (outer_builder.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
if (hot_reload) {
|
||||
run_cmd.addFileArg(game_lib.getEmittedBin());
|
||||
}
|
||||
run_cmd_step = &run_cmd.step;
|
||||
}
|
||||
}
|
||||
@ -101,6 +115,7 @@ const EngineModule = struct {
|
||||
|
||||
has_imgui: ?bool = null,
|
||||
has_tracy: ?bool = null,
|
||||
hot_reload: bool
|
||||
};
|
||||
};
|
||||
|
||||
@ -197,6 +212,7 @@ fn createEngineModule(b: *std.Build, opts: EngineModule.Options) EngineModule {
|
||||
var options = b.addOptions();
|
||||
options.addOption(bool, "has_imgui", has_imgui);
|
||||
options.addOption(bool, "has_tracy", has_tracy);
|
||||
options.addOption(bool, "hot_reload", opts.hot_reload);
|
||||
mod.addOptions("build_options", options);
|
||||
|
||||
return EngineModule{
|
||||
|
||||
@ -1,8 +1,34 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Engine = @import("engine");
|
||||
|
||||
var engine: Engine = undefined;
|
||||
|
||||
pub fn main() !void {
|
||||
try engine.run(.{});
|
||||
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()) {
|
||||
allocator = std.heap.wasm_allocator;
|
||||
} else if (builtin.mode == .Debug) {
|
||||
allocator = debug_allocator.allocator();
|
||||
} else {
|
||||
allocator = std.heap.smp_allocator;
|
||||
}
|
||||
|
||||
const args = try std.process.argsAlloc(allocator);
|
||||
defer std.process.argsFree(allocator, args);
|
||||
|
||||
var game_library_path: ?[]const u8 = null;
|
||||
if (args.len >= 2) {
|
||||
game_library_path = args[1];
|
||||
}
|
||||
|
||||
try engine.run(.{
|
||||
.allocator = allocator,
|
||||
.game_library_path = game_library_path
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -17,39 +17,72 @@ const STBImage = @import("stb_image");
|
||||
|
||||
const Gfx = Graphics;
|
||||
|
||||
const build_options = @import("build_options");
|
||||
|
||||
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 Lib.Init) callconv(.c) void,
|
||||
tick: *const fn(frame: *Lib.Frame) callconv(.c) void,
|
||||
deinit: *const fn() callconv(.c) void,
|
||||
debug: *const fn(imgui: *Lib.ImGui) callconv(.c) void,
|
||||
const Init = *const fn(opts: *const Lib.Init) callconv(.c) void;
|
||||
const Tick = *const fn(frame: *Lib.Frame) callconv(.c) void;
|
||||
const Deinit = *const fn() callconv(.c) void;
|
||||
const Debug = *const fn(imgui: *Lib.ImGui) callconv(.c) void;
|
||||
|
||||
init: Init,
|
||||
tick: Tick,
|
||||
deinit: Deinit,
|
||||
debug: if (build_options.has_imgui) Debug else void
|
||||
};
|
||||
|
||||
extern fn init(opts: *const Lib.Init) void;
|
||||
extern fn tick(frame: *Lib.Frame) void;
|
||||
extern fn deinit() void;
|
||||
extern fn debug(imgui: *Lib.ImGui) void;
|
||||
const static_game_callbacks = GameCallbacks{
|
||||
.init = init,
|
||||
.deinit = deinit,
|
||||
.tick = tick,
|
||||
.debug = debug,
|
||||
};
|
||||
|
||||
const GameLinking = union(enum) {
|
||||
static,
|
||||
dynamic: struct {
|
||||
lib: std.DynLib
|
||||
},
|
||||
|
||||
pub fn getCallbacks(self: *GameLinking) !GameCallbacks {
|
||||
switch (self.*) {
|
||||
.static => {
|
||||
if (build_options.hot_reload) {
|
||||
return error.StaticLinkingDisabled;
|
||||
}
|
||||
|
||||
return GameCallbacks{
|
||||
.init = init,
|
||||
.deinit = deinit,
|
||||
.tick = tick,
|
||||
.debug = if (build_options.has_imgui) debug else {},
|
||||
};
|
||||
},
|
||||
.dynamic => |*opts| {
|
||||
const lib = &opts.lib;
|
||||
return GameCallbacks{
|
||||
.init = lib.lookup(GameCallbacks.Init, "init") orelse return error.MissingFunction,
|
||||
.tick = lib.lookup(GameCallbacks.Tick, "tick") orelse return error.MissingFunction,
|
||||
.deinit = lib.lookup(GameCallbacks.Deinit, "deinit") orelse return error.MissingFunction,
|
||||
.debug = if (build_options.has_imgui) lib.lookup(GameCallbacks.Debug, "debug") orelse return error.MissingFunction else {},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const Engine = @This();
|
||||
|
||||
pub const Nanoseconds = u64;
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
started_at: std.time.Instant,
|
||||
last_frame_at: Nanoseconds,
|
||||
last_frame_at: Lib.Nanoseconds,
|
||||
graphics: Graphics,
|
||||
input: Input,
|
||||
|
||||
game_linking: GameLinking,
|
||||
game_callbacks: GameCallbacks,
|
||||
|
||||
frame: Lib.Frame,
|
||||
@ -57,34 +90,34 @@ frame: Lib.Frame,
|
||||
previous_tick_canvas_size: ?Vec2 = null,
|
||||
|
||||
const RunOptions = struct {
|
||||
window_title: [*:0]const u8 = "Game",
|
||||
window_width: u31 = 640,
|
||||
window_height: u31 = 480,
|
||||
allocator: std.mem.Allocator,
|
||||
game_library_path: ?[]const u8 = null
|
||||
};
|
||||
|
||||
pub fn run(self: *Engine, opts: RunOptions) !void {
|
||||
self.* = Engine{
|
||||
.allocator = undefined,
|
||||
.allocator = opts.allocator,
|
||||
.started_at = std.time.Instant.now() catch @panic("Instant.now() unsupported"),
|
||||
.last_frame_at = 0,
|
||||
.frame = undefined,
|
||||
.graphics = undefined,
|
||||
.game_callbacks = static_game_callbacks,
|
||||
.game_linking = undefined,
|
||||
.game_callbacks = undefined,
|
||||
.input = .{}
|
||||
};
|
||||
|
||||
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
|
||||
defer _ = debug_allocator.deinit();
|
||||
|
||||
// TODO: Use tracy TracingAllocator
|
||||
if (builtin.cpu.arch.isWasm()) {
|
||||
self.allocator = std.heap.wasm_allocator;
|
||||
} else if (builtin.mode == .Debug) {
|
||||
self.allocator = debug_allocator.allocator();
|
||||
if (opts.game_library_path) |game_library_path| {
|
||||
self.game_linking = .{
|
||||
.dynamic = .{
|
||||
.lib = try std.DynLib.open(game_library_path)
|
||||
}
|
||||
};
|
||||
} else {
|
||||
self.allocator = std.heap.smp_allocator;
|
||||
self.game_linking = .static;
|
||||
}
|
||||
|
||||
self.game_callbacks = try self.game_linking.getCallbacks();
|
||||
|
||||
self.frame.init(self.allocator);
|
||||
|
||||
tracy.setThreadName("Main");
|
||||
@ -120,10 +153,10 @@ pub fn run(self: *Engine, opts: RunOptions) !void {
|
||||
.cleanup_userdata_cb = sokolCleanupCallback,
|
||||
.event_userdata_cb = sokolEventCallback,
|
||||
.user_data = self,
|
||||
.width = opts.window_width,
|
||||
.height = opts.window_height,
|
||||
.width = 640,
|
||||
.height = 480,
|
||||
.icon = icon,
|
||||
.window_title = opts.window_title,
|
||||
.window_title = "Game",
|
||||
.logger = .{ .func = sokolLogCallback },
|
||||
.win32 = .{
|
||||
.console_utf8 = true
|
||||
@ -238,7 +271,7 @@ fn sokolFrame(self: *Engine) !void {
|
||||
|
||||
self.graphics.drawCommands(frame.graphics_commands.items);
|
||||
|
||||
if (frame.show_debug) {
|
||||
if (frame.show_debug and build_options.has_imgui) {
|
||||
try self.showDebugWindow(frame);
|
||||
}
|
||||
}
|
||||
@ -259,6 +292,7 @@ fn showDebugWindow(self: *Engine, frame: *Lib.Frame) !void {
|
||||
defer imgui.endWindow();
|
||||
|
||||
_ = imgui.beginTabBar("debug");
|
||||
defer imgui.endTabBar();
|
||||
|
||||
if (imgui.beginTabItem("Game")) {
|
||||
defer imgui.endTabItem();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user