allocator: std.mem.Allocator, gltfs: std.StringHashMapUnmanaged(GltfData) = .{}, stringArena: std.heap.ArenaAllocator, filesToProcess: std.ArrayListUnmanaged(FileToProcess) = .{}, pub const FileToProcess = struct { path: []const u8, }; pub const GltfAnimationData = struct { name: []const u8, }; pub const GltfData = struct { filePath: []u8, animations: std.ArrayListUnmanaged(GltfAnimationData) = .{}, }; pub fn create(allocator: std.mem.Allocator) !*@This() { const self = try allocator.create(@This()); self.* = .{ .allocator = allocator, .stringArena = std.heap.ArenaAllocator.init(allocator), }; try self.setupCallbacks(); try self.initialLoad(); return self; } pub fn setupCallbacks(self: *@This()) !void { core.fs().watchPath("content/"); try core.fs().addAnyWatchCallback(fileChangedCallback, self); } pub fn scanAll(self: *@This(), dir_path: []const u8) !void { var dir = try std.fs.cwd().openDir(dir_path, .{ .iterate = true }); defer dir.close(); var it = dir.iterate(); while (try it.next()) |entry| { const full_path = try std.fs.path.join(self.allocator, &.{ dir_path, entry.name }); defer self.allocator.free(full_path); if (entry.kind == .directory) { if (std.mem.eql(u8, entry.name, "_shaders")) { continue; } try self.scanAll(full_path); // Recursive call for subdirectories } else if (entry.kind == .file) { // core.engine_log("processing file: {s}", .{entry.name}); try self.filesToProcess.append(self.allocator, .{ .path = entry.name }); } } } pub fn initialLoad(self: *@This()) !void { try self.scanAll("content/"); } pub fn fileChangedCallback(path: []const u8, ctx: ?*anyopaque) void { const self: *@This() = @ptrCast(@alignCast(ctx.?)); // core.engine_log("file change seen {s}", .{path}); // self.filesToProcess.append(self.allocator, .{ .path = path }) catch {}; // self.updatePath(path) catch return; } fn salloc(self: *@This()) std.mem.Allocator { return self.stringArena.allocator(); } pub fn updatePath(self: *@This(), path: []const u8) !void { core.engine_log("updating Path: {s}", .{path}); if (std.mem.endsWith(u8, path, ".ozz")) {} if (std.mem.endsWith(u8, path, ".gltf")) { // 1. check if there is a .cook file // if there is a .cook file // then dont load, no further actions needed const pathstr = try std.fmt.allocPrint(self.salloc(), "{s}.cook", .{path[0 .. path.len - 5]}); defer self.salloc().free(pathstr); core.engine_log("checking path: {s}", pathstr); if (!core.fs().fileExists(pathstr)) { // create a cook file under the pathstr, the file watcher should see it.. core.engine_log("creating cooker {s}", .{pathstr}); } //var name = core.MakeName(pathstr); // if there is a cook file // var mapping = try core.fs().loadFile(path); // zgltf.init(self.allocator); // core.fs().unmap(mapping); } } pub fn tick(self: *@This()) void { const start = core.getEngineTime(); while (self.filesToProcess.pop()) |pop| { const now = core.getEngineTime(); if (now - start > 0.050) { break; } self.updatePath(pop.path) catch {}; } } pub fn destroy(self: *@This()) void { self.allocator.destroy(self); } const std = @import("std"); const api = @import("backlog"); const imgui = api.imgui; const ig = api.imgui.api; const core = api.core; const sys = api.sys; const zgltf = core.zgltf;