Backlog/projects/tools/toolbox.zig

302 lines
9.2 KiB
Zig

pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "main");
const GameContext = @This();
allocator: std.mem.Allocator,
commandQueue: core.RingQueue(Task),
activeCommand: ?*sys.SubprocessTask = null,
showInstructions: bool = false,
dockingInitialized: bool = false,
animationStore: *AnimationStore = undefined,
const Task = union(enum(u8)) {
subprocess: *sys.SubprocessTask,
func: *const fn (*GameContext) void,
};
pub fn init(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.allocator = allocator,
.commandQueue = try core.RingQueue(Task).init(self.allocator, 4096),
};
return self;
}
pub fn prepare(self: *@This()) !void {
core.engine_log("program ready", .{});
try assets.initCooking();
if (core.getEngineObject(imgui.utils.TopBar)) |topbar| {
topbar.menuOpen = true;
}
self.animationStore = try AnimationStore.create(self.allocator);
}
pub fn tick(self: *@This(), dt: f64) void {
_ = dt;
std.Thread.sleep(10 * 1000 * 1000);
self.tickTasks() catch {};
self.animationStore.tick();
if (self.activeCommand != null or self.commandQueue.count() > 0) {
//ig.setNextWindowPos(.{ .x = 20, .y = 20 }, .{}, .{});
// if (ig.begin("tasks", null, .{
// .always_auto_resize = true,
// .no_title_bar = true,
// .no_move = true,
// .no_resize = true,
// .no_collapse = true,
// })) {}
// ig.end();
}
// Initialize docking layout on first run
if (!self.dockingInitialized) {
// Get the main viewport dockspace ID (created by dockSpaceOverViewport)
const main_dockspace_id = ig.getID_Str("##DockSpaceViewport_11111111");
// Dock our toolbox window to the center of the main dockspace
ig.dockBuilderDockWindow("Toolbox Window", main_dockspace_id);
ig.dockBuilderFinish(main_dockspace_id);
self.dockingInitialized = true;
}
// Create a docked window
if (ig.begin("Toolbox Window", null, .{})) {
if (ig.button("Compile Shaders", .{})) {
start_CompileShaders() catch {};
}
if (ig.button("Launch TrenchBroom", .{})) {
start_TrenchBroom() catch {};
}
ig.separator();
ig.textf("animation store", .{});
self.animationStore.tickDisplay();
if (self.activeCommand != null or self.commandQueue.count() > 0) {
ig.textf("outstanding tasks: {d}", .{self.commandQueue.count() + 1});
if (self.activeCommand) |cmd| {
ig.textf("current command: ", .{});
for (cmd.argsOwned.items) |arg| {
ig.textf("{s}", .{arg});
ig.sameLine(0, 4);
}
}
}
}
ig.end();
}
fn findRootDir(allocator: std.mem.Allocator) ![]u8 {
var current_dir = std.fs.cwd();
// Start from current directory and walk up
var path_components = std.ArrayList([]const u8){};
defer path_components.deinit(allocator);
// Try current directory first
current_dir.access("content.txt", .{}) catch {
// content.txt not found, need to walk up
var temp_dir = current_dir;
while (true) {
// Try to go up one directory
const parent_dir = temp_dir.openDir("..", .{}) catch return error.RootNotFound;
temp_dir = parent_dir;
// Check if content.txt exists in parent directory
temp_dir.access("content.txt", .{}) catch {
continue;
};
// Found content.txt, get the absolute path
return try temp_dir.realpathAlloc(allocator, ".");
}
};
// content.txt found in current directory
return try current_dir.realpathAlloc(allocator, ".");
}
fn start_CompileShaders() !void {
const self = core.getEngineObject(@This()) orelse return;
const root_dir = findRootDir(self.allocator) catch {
core.engine_log("Could not find BacklogEngine root directory (content.txt not found)", .{});
return;
};
defer self.allocator.free(root_dir);
core.engine_log("Found root directory: {s}", .{root_dir});
const compile_shaders_dir = try std.fs.path.join(self.allocator, &.{ root_dir, "tools", "compileShaders" });
defer self.allocator.free(compile_shaders_dir);
var compile_shaders_name = core.MakeName(compile_shaders_dir);
core.engine_log("CompileShaders directory: {s}", .{compile_shaders_dir});
const exe_path = try std.fs.path.join(self.allocator, &.{ compile_shaders_dir, "zig-out", "bin", "compileShaders.exe" });
defer self.allocator.free(exe_path);
core.engine_log("Looking for executable at: {s}", .{exe_path});
// Check if compileShaders.exe exists
if (std.fs.cwd().access(exe_path, .{})) |_| {
// File exists, run it directly
core.engine_log("CompileShaders executable found, running directly", .{});
try self.commandQueue.pushLocked(.{ .subprocess = try sys.runCommand(
self.allocator,
&.{"zig-out/bin/compileShaders.exe"},
compile_shaders_name.utf8(),
) });
} else |_| {
// File doesn't exist, build it first
core.engine_log("CompileShaders executable not found, building first", .{});
try self.commandQueue.pushLocked(.{ .subprocess = try sys.runCommand(
self.allocator,
&.{ "zig", "build", "install" },
compile_shaders_name.utf8(),
) });
// Then run it
core.engine_log("Queuing compileShaders execution after build", .{});
try self.commandQueue.pushLocked(.{ .subprocess = try sys.runCommand(
self.allocator,
&.{"zig-out/bin/compileShaders.exe"},
compile_shaders_name.utf8(),
) });
}
}
pub fn launchDetachedProcess(allocator: std.mem.Allocator, argv: []const []const u8, cwd: ?[]const u8) !void {
var child = std.process.Child.init(argv, allocator);
child.cwd = cwd;
child.stdin_behavior = .Ignore;
child.stdout_behavior = .Ignore;
child.stderr_behavior = .Ignore;
try child.spawn();
}
fn start_TrenchBroom() !void {
const self = core.getEngineObject(@This()) orelse return;
const root_dir = findRootDir(self.allocator) catch {
core.engine_log("Could not find BacklogEngine root directory (content.txt not found)", .{});
return;
};
defer self.allocator.free(root_dir);
const trenchbroom_dir = try std.fs.path.join(self.allocator, &.{ root_dir, "tools", "trenchbroom" });
defer self.allocator.free(trenchbroom_dir);
var trenchbroom_name = core.MakeName(trenchbroom_dir);
core.engine_log("Launching TrenchBroom from: {s}", .{trenchbroom_dir});
try launchDetachedProcess(
self.allocator,
&.{"TrenchBroom.exe"},
trenchbroom_name.utf8(),
);
}
fn tickTasks(self: *@This()) !void {
if (self.activeCommand == null) {
if (self.commandQueue.popFromLocked()) |task| {
switch (task) {
.subprocess => |t| {
self.activeCommand = t;
// core.engine_log("starting task {any}", .{t.args});
try t.run();
},
.func => |f| {
f(self);
},
}
}
}
if (self.activeCommand) |active| {
if (active.checkComplete()) {
active.destroy();
core.engine_logs("task complete");
self.activeCommand = null;
}
}
}
fn loadFileAllocFromCwd(allocator: std.mem.Allocator, dir: std.fs.Dir, path: []const u8) ![]u8 {
var file = try dir.openFile(path, .{});
defer file.close();
const filesize = (try file.stat()).size + 1; // add null byte
const buffer: []u8 = try allocator.alloc(u8, filesize);
errdefer allocator.free(buffer);
try file.reader().readNoEof(buffer[0 .. buffer.len - 1]);
buffer[buffer.len - 1] = 0;
return buffer;
}
fn displayInstructions(self: *@This()) void {
self.showInstructions = true;
}
pub fn deinit(self: *@This()) void {
self.animationStore.destroy();
self.allocator.destroy(self);
}
fn copyRecursiveDir(
allocator: std.mem.Allocator,
src_dir: *std.fs.Dir,
dest_dir: *std.fs.Dir,
) !void {
var walker = try src_dir.walk(allocator);
defer walker.deinit();
while (try walker.next()) |entry| {
switch (entry.kind) {
.file => {
try entry.dir.copyFile(entry.basename, dest_dir.*, entry.path, .{});
},
.directory => {
try dest_dir.makeDir(entry.path);
},
else => {},
}
}
}
pub fn main() anyerror!void {
var spec = try api.getSpec("New Project Dialogue");
try spec.put("useGPA", .{ .boolean = false });
try spec.put("configName", .{ .string = "toolbox" });
_ = api.startEngine(&NeonObjectTable, &spec);
}
const AnimationStore = @import("toolbox/animationStore.zig");
const builtin = @import("builtin");
const std = @import("std");
const api = @import("backlog");
const imgui = api.imgui;
const assets = api.assets;
const ig = api.imgui.api;
const core = api.core;
const sys = api.sys;