diff --git a/engine/core/src/core.zig b/engine/core/src/core.zig index 0f4ec09..73ef94b 100644 --- a/engine/core/src/core.zig +++ b/engine/core/src/core.zig @@ -139,6 +139,11 @@ pub fn getSessionStamp() i64 { return gEngine.sessionStamp; } +// a struct can be used like a list of types in this way +pub const ComponentList = struct { + pub const Scene = scene.Scene; +}; + pub fn start_module(map: *SpecVariantMap, args: anytype, allocator: std.mem.Allocator) !void { staticsInitialized = true; @@ -190,9 +195,11 @@ pub fn start_module(map: *SpecVariantMap, args: anytype, allocator: std.mem.Allo try algorithm.string_pool.setup(allocator); _ = try gEngine.createObject(script_bindings.ScriptTicks, .{ .can_tick = true }); - _ = try inputs.initInputStack(); + // components define + try ecs.defineComponentList(ComponentList, allocator); + return; } @@ -203,11 +210,19 @@ pub fn setupFromModule(__args: ModuleLoaderArgs) !void { algorithm.names.gRegistry = __args.nameRegistry; logging.setupLoggingFromModule(); staticsInitialized = true; + + // walk through and patch all ecs containers + ecs.patchComponentList(ComponentList); + + if (getEngineObject(SceneSystem)) |system| { + scene.Scene.SceneObjectContainer = system.sceneObjectContainer; + } } pub fn shutdown_module(_: std.mem.Allocator) void { MemoryTracker.MTPrintStatsDelta(); + ecs.undefineComponentList(ComponentList); logging.shutdownLogging(); debug_draw.shutdownDrawInterface(); @@ -368,3 +383,31 @@ pub fn modulePreamble(p_allocator: *anyopaque, p_a: ?*anyopaque) !std.mem.Alloca try setupFromModule(args); return allocator; } + +// a wrapper around a struct +// which adds slack up to a specific size +// +// engine objects must be allocated with this if they support hot patching +// +// if pub const Slack is a decl in the struct- EngineObjectVTable will generate with a +// fields remapping list. + +pub fn SlackStruct(comptime T: type, comptime SlackSize: usize) type { + return struct { + inner: T, + slack: [SlackSize - @sizeOf(T)]u8 = undefined, + + pub fn create(allocator: std.mem.Allocator) !*T { + return (try allocator.create(@This())).getInner(); + } + + pub fn getInner(self: *@This()) *T { + return &self.inner; + } + + pub fn fromPtr(inner: *T) *@This() { + const p: *anyopaque = inner; + return @as(*@This(), @ptrCast(@alignCast(p))); + } + }; +} diff --git a/engine/core/src/ecs.zig b/engine/core/src/ecs.zig index 545012a..97b7798 100644 --- a/engine/core/src/ecs.zig +++ b/engine/core/src/ecs.zig @@ -208,6 +208,10 @@ pub const EcsRegistry = struct { var containerName = _containerName; const newid = self.containers.items.len; + if (self.containersByName.contains(containerName.handle())) { + try core.assertf(false, "container already exists??", .{}); + } + try self.containers.append(self.allocator, ref); try self.containerNames.append(self.allocator, containerName); try self.containersByName.put(self.allocator, containerName.handle(), @intCast(newid)); @@ -283,13 +287,47 @@ pub const EcsRegistry = struct { } }; +pub fn defineComponentList(comptime ComponentList: type, allocator: std.mem.Allocator) !void { + const typeInfo = @typeInfo(ComponentList).@"struct"; + + inline for (typeInfo.decls) |decl| { + core.engine_logs("defining component" ++ decl.name); + try defineComponent(@field(ComponentList, decl.name), allocator); + } +} + +pub fn undefineComponentList(comptime ComponentList: type) void { + const typeInfo = @typeInfo(ComponentList).@"struct"; + inline for (typeInfo.decls) |decl| { + undefineComponent(@field(ComponentList, decl.name)); + } +} + +// this doesn't do any behaviour patching right now, +// only BaseContainer pointers +pub fn patchComponentList(comptime ComponentList: type) void { + const typeInfo = @typeInfo(ComponentList).@"struct"; + if (core.getEngineObject(EcsRegistry)) |registry| { + inline for (typeInfo.decls) |decl| { + const T = @field(ComponentList, decl.name); + var name = core.MakeName(T.ComponentName); + + if (registry.containersByName.get(name.handle())) |offset| { + core.engine_log("Patching component {s}", .{name.utf8()}); + const ref = registry.containers.items[offset]; + T.BaseContainer = @ptrCast(@alignCast(ref.ptr)); + } + } + } +} + pub fn defineComponent(comptime Component: type, allocator: std.mem.Allocator) !void { const ContainerType = @TypeOf(Component.BaseContainer.*); Component.BaseContainer = try ContainerType.create(allocator); core.engine_log("Component container created " ++ @typeName(Component) ++ " @{x}", .{@intFromPtr(Component.BaseContainer)}); const container = makeEcsContainerRef(Component.BaseContainer); - try registerEcsContainer(container, core.MakeName(@typeName(Component))); + try registerEcsContainer(container, core.MakeName(Component.ComponentName)); try script.registerComponent(Component, container); } diff --git a/engine/core/src/engine.zig b/engine/core/src/engine.zig index 90b7fa3..d440043 100644 --- a/engine/core/src/engine.zig +++ b/engine/core/src/engine.zig @@ -163,7 +163,7 @@ pub const Engine = struct { self.createObjectLock = true; defer self.createObjectLock = false; const newIndex = self.engineObjects.items.len; - const newObjectPtr = try vtable.init_func(self.allocator); + const newObjectPtr = try vtable.init_func(self.allocator); // call this thing with the special allocator that adds vtable. slackSize to it. const newObjectRef = EngineObjectRef{ .ptr = @as(*anyopaque, @ptrCast(newObjectPtr)), diff --git a/engine/core/src/engineObject.zig b/engine/core/src/engineObject.zig index d666e2e..6550854 100644 --- a/engine/core/src/engineObject.zig +++ b/engine/core/src/engineObject.zig @@ -42,6 +42,45 @@ pub const EngineDataEventError = error{ OutOfMemory, }; +pub const FieldInfo = struct { + name: []const u8, + size: u32, + offset: u32, + alignment: u32, +}; + +pub fn PatchStruct(comptime T: type, p: *T, old: []const FieldInfo) void { + const t = @typeInfo(T).@"struct"; + + var new: T = .{}; + + inline for (t.fields) |field| { + var oldField: ?FieldInfo = null; + + for (old) |oldFieldSearch| { + if (std.mem.eql(u8, field.name, oldFieldSearch.name)) { + oldField = oldFieldSearch; + break; + } + } + + if (oldField) |of| { + var src: []const u8 = undefined; + src.ptr = @ptrCast(p); + src.ptr += of.offset; + src.len = @sizeOf(@TypeOf(@field(p, field.name))); + + var dest: []u8 = undefined; + dest.ptr = @ptrCast(&@field(new, field.name)); + dest.len = @sizeOf(@TypeOf(@field(new, field.name))); + + std.mem.copyForwards(u8, dest, src); + } + } + + p.* = new; +} + pub const EngineObjectVTable = struct { typeName: []const u8, typeSize: usize, @@ -60,6 +99,37 @@ pub const EngineObjectVTable = struct { readyToExit_func: ?*const fn (*anyopaque) bool = null, prepare_func: ?*const fn (*anyopaque) EngineDataEventError!void = null, + fieldListHash: ?usize = null, + fieldList: ?[]const FieldInfo = null, + slackSize: ?usize = null, + + fn fieldInfoCompare(_: void, a: FieldInfo, b: FieldInfo) bool { + return a.offset < b.offset; + } + + pub fn addFieldList(self: *@This(), comptime TargetType: type) void { + if (!@hasDecl(TargetType, "Slack")) { + return; + } + + self.slackSize = @sizeOf(TargetType.Slack); + const typeinfo = @typeInfo(TargetType).@"struct"; + const fieldList = blk: { + comptime var f: []const FieldInfo = &.{}; + inline for (typeinfo.fields) |field| { + f = f ++ .{FieldInfo{ + .name = field.name, + .offset = @intCast(@offsetOf(TargetType, field.name)), + .size = @intCast(@sizeOf(field.type)), + .alignment = @alignOf(field.type), + }}; + } + + break :blk f; + }; + + self.fieldList = fieldList; + } pub fn from(comptime TargetType: type, comptime engineObjectName: ?[]const u8) EngineObjectVTable { var self = EngineObjectVTable{ @@ -68,6 +138,7 @@ pub const EngineObjectVTable = struct { .typeAlign = @alignOf(TargetType), .init_func = undefined, }; + self.addFieldList(TargetType); if (engineObjectName) |eon| { self.singletonName = eon; diff --git a/engine/core/src/extern/externModule.zig b/engine/core/src/extern/externModule.zig index c1106b6..66099cd 100644 --- a/engine/core/src/extern/externModule.zig +++ b/engine/core/src/extern/externModule.zig @@ -25,6 +25,8 @@ pub const LoadedModule = struct { lastLoad: i64 = 0, lastModification: i64 = 0, + pdbPath: ?[]const u8 = null, + startOnLoad: bool = true, started: bool = false, initialLoad: bool = true, @@ -51,6 +53,11 @@ pub const LoadedModule = struct { try std.fs.cwd().copyFile(self.baseModulePath, std.fs.cwd(), stagedPath, .{}); + if (self.pdbPath) |pdbPath| { + const stagedPdbPath = try std.fmt.allocPrint(allocator, "{s}/{s}.pdb", .{ stagingDirectoryPath, self.moduleName }); + try std.fs.cwd().copyFile(pdbPath, std.fs.cwd(), stagedPdbPath, .{}); + } + self.stagedPaths.append(allocator, stagedPath) catch unreachable; } @@ -139,6 +146,10 @@ pub const ModuleLoader = struct { .lastModification = std.time.microTimestamp(), }; + if (@import("builtin").os.tag == .windows) { + loaded.pdbPath = try std.fmt.allocPrint(self.arena.allocator(), "zig-out/modules/{s}.pdb", .{libFileName[0 .. libFileName.len - 4]}); + } + try core.fs().addFileChangedCallback(libFileName, dllChangedCallback, loaded); try self.loadedModules.append(self.arena.allocator(), loaded); diff --git a/engine/core/src/logging.zig b/engine/core/src/logging.zig index b90126b..5002623 100644 --- a/engine/core/src/logging.zig +++ b/engine/core/src/logging.zig @@ -10,6 +10,31 @@ const LogBufferSize = 1 * 1024 * 1024; // 1Mb buffer log var gLoggerSys: ?*LoggerSys = null; +pub const LogBuffer = struct { + lock: std.Thread.Mutex = .{}, + buffer: std.ArrayList(u8), + + pub fn init(allocator: std.mem.Allocator) @This() { + return .{ + .buffer = std.ArrayList(u8).init(allocator), + }; + } + + pub fn lockWriter(self: *@This()) std.ArrayList(u8).Writer { + self.lock.lock(); + return self.buffer.writer(); + } + + pub fn unlock(self: *@This()) void { + self.lock.unlock(); + } + + pub fn deinit(self: *@This()) void { + self.lock.lock(); + self.buffer.deinit(); + } +}; + pub fn printRaw(comptime fmt: []const u8, args: anytype) void { if (zero_logging) { return; @@ -42,9 +67,13 @@ pub fn printInner(comptime fmt: []const u8, args: anytype) void { } } +// new logging API +pub fn logDisplay(comptime prefix: []const u8, comptime fmt: []const u8, args: anytype) void { + printInner("[" ++ prefix ++ "]: " ++ fmt ++ "\n", args); +} + pub fn game_log(comptime fmt: []const u8, args: anytype) void { printInner("[GAME ]: " ++ fmt ++ "\n", args); - printInner("[SCRIPT ]: " ++ fmt ++ "\n", args); } pub fn game_logs(comptime fmt: []const u8) void { @@ -168,6 +197,8 @@ pub const LoggerSys = struct { lock: std.Thread.Mutex = .{}, flushing: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), + sessionBuffer: ?*LogBuffer = null, // disabled in release modes + pub fn flush(self: *@This()) !void { var z = tracy.ZoneN(@src(), "Trying to flush"); defer z.End(); @@ -235,6 +266,11 @@ pub const LoggerSys = struct { pub fn print(self: *@This(), comptime fmt: []const u8, args: anytype) !void { self.lock.lock(); try self.writeOutBuffer.writer().print(fmt, args); + if (self.sessionBuffer != null) { + try self.sessionBuffer.?.lockWriter().print(fmt, args); + self.sessionBuffer.?.unlock(); + } + self.lock.unlock(); if (self.writeOutBuffer.items.len > LogBufferSize) { diff --git a/engine/core/src/scene.zig b/engine/core/src/scene.zig index b27d504..c42588b 100644 --- a/engine/core/src/scene.zig +++ b/engine/core/src/scene.zig @@ -281,6 +281,7 @@ pub const SceneSystem = struct { dynamicObjects: ArrayListUnmanaged(core.ObjectHandle) = .{}, childrenArena: std.heap.ArenaAllocator, tickCount: u32 = 0, + sceneObjectContainer: *SceneObjectSet = undefined, pub const Field = SceneObjectSet.Field; pub const FieldType = SceneObjectSet.FieldType; @@ -365,8 +366,10 @@ pub const SceneSystem = struct { .childrenArena = std.heap.ArenaAllocator.init(allocator), }; core.EngineObject(@This()).gInstance = self; - try core.defineComponent(Scene, allocator); + // try core.defineComponent(Scene, allocator); Scene.SceneObjectContainer = try SceneObjectSet.create(allocator); + self.sceneObjectContainer = Scene.SceneObjectContainer; + return self; } @@ -388,7 +391,7 @@ pub const SceneSystem = struct { pub fn deinit(self: *@This()) void { self.dynamicObjects.deinit(self.allocator); self.childrenArena.deinit(); - core.undefineComponent(Scene); + // core.undefineComponent(Scene); Scene.SceneObjectContainer.destroy(); self.allocator.destroy(self); } diff --git a/engine/imgui/src/imgui.zig b/engine/imgui/src/imgui.zig index 2694022..dae4ba4 100644 --- a/engine/imgui/src/imgui.zig +++ b/engine/imgui/src/imgui.zig @@ -16,6 +16,8 @@ pub fn start_module(spec: *core.SpecVariantMap, args: anytype, allocator: std.me const gimgui = try core.createObject(Impl, .{}); try gimgui.setup(); + + _ = try core.createObject(utils.TopBar, .{}); } pub fn setupFromModule() void { diff --git a/engine/imgui/src/utils/TopBar.zig b/engine/imgui/src/utils/TopBar.zig new file mode 100644 index 0000000..04edc40 --- /dev/null +++ b/engine/imgui/src/utils/TopBar.zig @@ -0,0 +1,92 @@ +pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "game.ExternGameObject"); +pub const Slack = core.SlackStruct(@This(), 256); + +allocator: std.mem.Allocator, +arena: std.heap.ArenaAllocator, +windowsMenu: std.ArrayListUnmanaged(*MenuEntry) = .{}, +entriesByName: std.AutoHashMapUnmanaged(u32, *MenuEntry) = .{}, + +pub fn create(allocator: std.mem.Allocator) !*@This() { + const self = try Slack.create(allocator); + self.* = .{ + .allocator = allocator, + .arena = std.heap.ArenaAllocator.init(allocator), + }; + + try self.addWindowsMenu(.{ + .name = "sample window", + .open = false, + .ctx = self, + .windowFunction = windowOpen, + }); + + return self; +} + +pub fn addMenuObject(self: *@This(), ptr: anytype, comptime name: []const u8) !void { + const T = @TypeOf(ptr.*); + try self.addWindowsMenu(.{ + .name = name, + .ctx = ptr, + .windowFunction = T.windowOpen, + }); +} + +pub fn windowOpen(entry: *MenuEntry, dt: f64) void { + _ = dt; + + if (ig.begin("sample window", &entry.open, .{})) {} + ig.end(); +} + +pub fn addWindowsMenu(self: *@This(), entry: MenuEntry) !void { + const new = try self.arena.allocator().create(MenuEntry); + new.* = entry; + var name = core.MakeName(entry.name); + + if (self.entriesByName.getEntry(name.handle())) |e| { + core.logDisplay("TopBar", "{s} already registered, updating menu entry instead", .{entry.name}); + e.value_ptr.*.* = entry; + } else { + try self.windowsMenu.append(self.arena.allocator(), new); + try self.entriesByName.put(self.arena.allocator(), name.handle(), new); + } +} + +pub fn tick(self: *@This(), dt: f64) void { + ig.showDemoWindow(null); + + if (ig.beginMainMenuBar()) { + if (ig.beginMenu("Windows..", true)) { + for (self.windowsMenu.items) |entry| { + if (ig.menuItem_Bool(entry.name.ptr, null, entry.open, true)) { + entry.open = !entry.open; + } + } + ig.endMenu(); + } + ig.endMainMenuBar(); + + for (self.windowsMenu.items) |entry| { + if (entry.open) { + entry.windowFunction(entry, dt); + } + } + } +} + +pub fn destroy(self: *@This()) void { + self.arena.deinit(); + self.allocator.destroy(Slack.fromPtr(self)); +} + +pub const MenuEntry = struct { + name: []const u8, + open: bool = false, + ctx: ?*anyopaque, + windowFunction: *const fn (*MenuEntry, f64) void, +}; + +const ig = @import("../imgui.zig").api; +const std = @import("std"); +const core = @import("core"); diff --git a/engine/imgui/src/utils/consoleWindow.zig b/engine/imgui/src/utils/consoleWindow.zig new file mode 100644 index 0000000..ab693d9 --- /dev/null +++ b/engine/imgui/src/utils/consoleWindow.zig @@ -0,0 +1,46 @@ +buffer: core.LogBuffer, +allocator: std.mem.Allocator, +lastLength: usize = 0, + +pub fn init(allocator: std.mem.Allocator) @This() { + return .{ + .allocator = allocator, + .buffer = core.LogBuffer.init(allocator), + }; +} + +pub fn setup(self: *@This()) void { + if (core.getLogger()) |logger| { + logger.sessionBuffer = &self.buffer; + } + + if (core.getEngineObject(imgui.utils.TopBar)) |topBar| { + topBar.addMenuObject(self, "Console") catch {}; + } +} + +pub fn windowOpen(entry: *imgui.utils.MenuEntry, dt: f64) void { + _ = dt; + const self: *@This() = @ptrCast(@alignCast(entry.ctx)); + + self.buffer.lock.lock(); + defer self.buffer.lock.unlock(); + + if (ig.begin("Console Output", &entry.open, .{})) { + ig.textSlice(self.buffer.buffer.items); + if (self.lastLength != self.buffer.buffer.items.len) + ig.setScrollHereY(1.0); + } + ig.end(); + + self.lastLength = self.buffer.buffer.items.len; +} + +pub fn deinit(self: *@This()) void { + self.buffer.deinit(); +} + +const imgui = @import("../imgui.zig"); +const ig = imgui.api; +const std = @import("std"); +const core = @import("core"); diff --git a/engine/imgui/src/utils/structDebugger.zig b/engine/imgui/src/utils/structDebugger.zig index 078d1c8..9473bcb 100644 --- a/engine/imgui/src/utils/structDebugger.zig +++ b/engine/imgui/src/utils/structDebugger.zig @@ -79,5 +79,42 @@ pub inline fn displayStruct(s: anytype, comptime depth: u32, comptime maxDepth: } } +var staticText: [2048]u8 = undefined; + +pub fn hexText(bytes: []const u8) void { + var stream = std.io.fixedBufferStream(&staticText); + var writer = stream.writer(); + + var b = bytes; + if (b.len > 2040) + b.len = 2040; + core.xxdWrite(writer, bytes, .{}) catch return; + + const offset = writer.context.getPos() catch return; + const text = staticText[0..offset]; + + ig.textSlice(text); +} + +pub fn structHexView(ptr: anytype) void { + const T = @typeInfo(@TypeOf(ptr)).pointer.child; + + if (ig.begin("struct debugger hexview: " ++ @typeName(T), null, .{})) { + const typeInfo = @typeInfo(T).@"struct"; + if (@hasDecl(T, "Slack")) { + ig.textf("size: {d}/{d} bytes", .{ @sizeOf(T), @sizeOf(T.Slack) }); + } + inline for (typeInfo.fields) |field| { + const offset = @offsetOf(T, field.name); + ig.textf("+{d}(0x{x}) size: {d}> {s} ", .{ offset, offset, @sizeOf(field.type), field.name }); + hexText(&std.mem.toBytes(@field(ptr, field.name))); + ig.separator(); + } + } + + ig.end(); +} + const ig = @import("../imgui.zig").api; const std = @import("std"); +const core = @import("core"); diff --git a/engine/imgui/src/utils/utils.zig b/engine/imgui/src/utils/utils.zig index 09a8955..690986c 100644 --- a/engine/imgui/src/utils/utils.zig +++ b/engine/imgui/src/utils/utils.zig @@ -1 +1,15 @@ -pub const structDebugWindow = @import("structDebugger.zig").structDebugWindow; +pub const structDebugger = @import("structDebugger.zig"); +pub const structDebugWindow = structDebugger.structDebugWindow; +pub const structHexView = structDebugger.structHexView; +pub const ConsoleWindow = @import("consoleWindow.zig"); + +pub const TopBar = @import("TopBar.zig"); +pub const MenuEntry = TopBar.MenuEntry; + +pub fn addMenuFunc(ctx: ?*anyopaque, name: []const u8, func: *const fn (*MenuEntry, f64) void) void { + if (core.getEngineObject(TopBar)) |topbar| { + topbar.addWindowsMenu(.{ .name = name, .ctx = ctx, .windowFunction = func }) catch {}; + } +} + +const core = @import("core"); diff --git a/engine/physics/src/physics.zig b/engine/physics/src/physics.zig index 927f7c4..2f0456d 100644 --- a/engine/physics/src/physics.zig +++ b/engine/physics/src/physics.zig @@ -36,6 +36,11 @@ pub const PrimitiveType = enum { sphere, }; +pub const ComponentList = struct { + pub const _PhysicsCharacter = PhysicsCharacter; + pub const _PhysicsCollider = PhysicsCollider; +}; + // low level helpers - old api pub fn addPrimitiveBody(primitive: PrimitiveType, settings: BodyCreationSettings, activationMode: Activation) !BodyId { const interface = context().system.getBodyInterfaceMut(); @@ -78,11 +83,18 @@ pub fn start_module(spec: *core.SpecVariantMap, args: anytype, allocator: std.me _ = args; _ = spec; + core.engine_logs("starting physics"); + try core.defineComponentList(ComponentList, allocator); try zphysics.init(allocator, .{}); _ = try core.createObject(runtime.PhysicsRuntime, .{ .can_tick = true }); } +pub fn setupFromModule() void { + core.ecs.patchComponentList(ComponentList); +} + pub fn shutdown_module(allocator: std.mem.Allocator) void { + core.undefineComponentList(ComponentList); _ = allocator; zphysics.deinit(); } diff --git a/engine/physics/src/physicsSystem.zig b/engine/physics/src/physicsSystem.zig index 226b335..41f54b5 100644 --- a/engine/physics/src/physicsSystem.zig +++ b/engine/physics/src/physicsSystem.zig @@ -172,8 +172,8 @@ pub const PhysicsRuntime = struct { self.system = system; - try core.defineComponent(PhysicsCharacter, self.allocator); - try core.defineComponent(PhysicsCollider, self.allocator); + //try core.defineComponent(PhysicsCharacter, self.allocator); + // try core.defineComponent(PhysicsCollider, self.allocator); return self; } @@ -227,8 +227,8 @@ pub const PhysicsRuntime = struct { physChar.deinit(); } self.idToEntity.deinit(self.allocator); - core.undefineComponent(PhysicsCharacter); - core.undefineComponent(PhysicsCollider); + // core.undefineComponent(PhysicsCharacter); + // core.undefineComponent(PhysicsCollider); self.shapes.deinit(self.allocator); self.system.destroy(); allocator.destroy(self); diff --git a/engine/rend/src/rend.zig b/engine/rend/src/rend.zig index 0012715..e9fd61a 100644 --- a/engine/rend/src/rend.zig +++ b/engine/rend/src/rend.zig @@ -56,11 +56,25 @@ pub fn start_module(spec: *core.SpecVariantMap, args: anytype, allocator: std.me rendAllocator = allocator; try renderer.createInstance(); try renderer.start(); + + try core.defineComponentList(ComponentList, allocator); +} + +pub const ComponentList = struct { + pub const _MeshComponent = MeshComponent; + pub const _CameraComponent = CameraComponent; +}; + +pub fn setupFromModule() void { + if (core.getEngineObject(renderer.Renderer)) |r| { + rendAllocator = r.allocator; + } + + core.ecs.patchComponentList(ComponentList); } pub fn shutdown_module(allocator: std.mem.Allocator) void { - core.undefineComponent(MeshComponent); - core.undefineComponent(CameraComponent); + core.undefineComponentList(ComponentList); _ = allocator; renderer.shutdown(); } diff --git a/engine/rend/src/sgpu/mesh-pool.zig b/engine/rend/src/sgpu/mesh-pool.zig index b67c8f0..e2806c4 100644 --- a/engine/rend/src/sgpu/mesh-pool.zig +++ b/engine/rend/src/sgpu/mesh-pool.zig @@ -20,8 +20,6 @@ pub const MeshPool = struct { pub fn create(device: *gpu.GPUDevice, allocator: std.mem.Allocator, settings: MeshPoolCreationSettings) !*@This() { const self = try allocator.create(@This()); - gMeshPool = self; - self.* = .{ .allocator = allocator, .indexSpans = try core.MergedSpans.init(allocator, settings.indexCount), @@ -185,19 +183,17 @@ pub const MeshPool = struct { }; /// === renderer interface implementation === -var gMeshPool: *MeshPool = undefined; - pub fn getMesh(name: []const u8) ?rend.IndexedMesh { var n = core.MakeName(name); return getMeshByName(&n); } pub fn getMeshByName(name: *core.Name) ?rend.IndexedMesh { - return gMeshPool.installedMeshes.get(name.handle()); + return rend.context().meshPool.installedMeshes.get(name.handle()); } pub fn pushMeshUpdate(meshUpdate: rend.MeshUpdate) !void { - try gMeshPool.pushMeshUpdate(meshUpdate); + try rend.context().meshPool.pushMeshUpdate(meshUpdate); } const MeshAssetLoader = @import("MeshAssetLoader.zig"); diff --git a/engine/rend/src/sgpu/renderer.zig b/engine/rend/src/sgpu/renderer.zig index fae2d39..73ea6e3 100644 --- a/engine/rend/src/sgpu/renderer.zig +++ b/engine/rend/src/sgpu/renderer.zig @@ -96,9 +96,6 @@ pub const Renderer = struct { .allocator = allocator, }; - try core.defineComponent(rend.MeshComponent, allocator); - try core.defineComponent(rend.CameraComponent, allocator); - self.hdrTextureFormat = .textureformatR16g16b16a16Float; return self; } diff --git a/extras/gameExtras/src/EngineTool.zig b/extras/gameExtras/src/EngineTool.zig index 2fc2be6..190ed41 100644 --- a/extras/gameExtras/src/EngineTool.zig +++ b/extras/gameExtras/src/EngineTool.zig @@ -1,5 +1,4 @@ allocator: std.mem.Allocator, -windowOpen: bool = true, lastUpdateFrame: u64 = 0, arena: std.heap.ArenaAllocator, @@ -13,6 +12,10 @@ pub fn create(allocator: std.mem.Allocator) !*@This() { .arena = std.heap.ArenaAllocator.init(allocator), }; + if (core.getEngineObject(igutils.TopBar)) |topbar| { + topbar.addMenuObject(self, "Engine Object Browser") catch {}; + } + return self; } @@ -42,19 +45,29 @@ pub fn maybeUpdate(self: *@This()) !void { } } -pub fn tick(self: *@This()) void { - if (self.windowOpen) { - if (ig.begin("EngineTool", &self.windowOpen, .{})) { - self.maybeUpdate() catch { - ig.textSlice("Unable to update engine tools list"); - return; - }; - for (self.strings.items) |slice| { - ig.textSlice(slice); +pub fn windowOpen(entry: *igutils.MenuEntry, dt: f64) void { + _ = dt; + const self: *@This() = @ptrCast(@alignCast(entry.ctx)); + + if (ig.begin("EngineTool", &entry.open, .{})) { + self.maybeUpdate() catch { + ig.textSlice("Unable to update engine tools list"); + return; + }; + for (self.strings.items) |slice| { + ig.textSlice(slice); + } + + if (core.getEngineObject(core.EcsRegistry)) |ecsReg| { + ig.separator(); + + for (ecsReg.containerNames.items, 0..) |*name, i| { + _ = i; + ig.textf("{s}", .{name.utf8()}); } } - ig.end(); } + ig.end(); } pub fn destroy(self: *@This()) void { @@ -67,3 +80,4 @@ const backlog = @import("Backlog"); const core = backlog.core; const rend = backlog.rend; const ig = backlog.imgui.api; +const igutils = backlog.imgui.utils; diff --git a/extras/gameExtras/src/RendererDebug.zig b/extras/gameExtras/src/RendererDebug.zig index b3b6bcf..8da300c 100644 --- a/extras/gameExtras/src/RendererDebug.zig +++ b/extras/gameExtras/src/RendererDebug.zig @@ -1,17 +1,28 @@ dtAverage: f64 = 0.0, allocator: std.mem.Allocator, -pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), null); +pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "extras.RendererDebug"); pub fn create(allocator: std.mem.Allocator) !*@This() { const self = try allocator.create(@This()); self.* = .{ .allocator = allocator }; + self.setup(); + return self; } -pub fn tick(self: *@This(), dt: f64) void { +// setups are a non-failable functions that get reran after patchStruct() is called +pub fn setup(self: *@This()) void { + if (core.getEngineObject(igu.TopBar)) |topbar| { + topbar.addMenuObject(self, "Renderer Debug") catch {}; + } +} + +pub fn windowOpen(entry: *igu.MenuEntry, dt: f64) void { + const self: *@This() = @ptrCast(@alignCast(entry.ctx)); + core.rollingAverage(&self.dtAverage, dt, 50); if (ig.begin("renderer debug", null, .{})) { @@ -43,4 +54,5 @@ const backlog = @import("Backlog"); const core = backlog.core; const rend = backlog.rend; const ig = backlog.imgui.api; +const igu = backlog.imgui.utils; const std = @import("std"); diff --git a/extras/gameExtras/src/inputDebugger.zig b/extras/gameExtras/src/inputDebugger.zig index ad5475a..ecd17b7 100644 --- a/extras/gameExtras/src/inputDebugger.zig +++ b/extras/gameExtras/src/inputDebugger.zig @@ -8,9 +8,13 @@ fn debugShowKeys(binding: anytype) void { ig.textFmt("{s} ({s}) keysDown count: {d}", .{ binding.data.name.utf8(), @typeName(@TypeOf(binding)), binding.data.keysDown.count() }) catch return; } -pub fn tick() void { +pub fn windowOpen(entry: *igutils.MenuEntry, dt: f64) void { + const self: *@This() = @ptrCast(@alignCast(entry.ctx)); + _ = dt; + _ = self; + const stack = core.inputs.getInputStack(); - if (ig.begin("input stack", null, .{})) { + if (ig.begin("input stack", &entry.open, .{})) { for (stack.active.bindingStack.items) |binding| { switch (binding) { .action => |b| { @@ -33,3 +37,4 @@ const backlog = @import("Backlog"); const core = backlog.core; const rend = backlog.rend; const ig = backlog.imgui.api; +const igutils = backlog.imgui.utils; diff --git a/extras/gameExtras/src/objectSpawner.zig b/extras/gameExtras/src/objectSpawner.zig index 74fd33c..acf2443 100644 --- a/extras/gameExtras/src/objectSpawner.zig +++ b/extras/gameExtras/src/objectSpawner.zig @@ -15,6 +15,8 @@ pub const Options = struct { absoluteSpawnposition: bool = false, // if set, won't move the object to where the spawn rotation and position is }; +pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "extras.objectSpawner"); + pub fn create(allocator: std.mem.Allocator) !*@This() { const self = try allocator.create(@This()); self.* = .{ diff --git a/projects/build.zig b/projects/build.zig index bea7878..6ede83e 100644 --- a/projects/build.zig +++ b/projects/build.zig @@ -31,7 +31,7 @@ pub fn build(b: *std.Build) void { }); blbuild.addExtraModule(sampleGameExtern.root_module, "gameExtras"); - + blbuild.addExtraModule(sampleGameExtern.root_module, "bsp"); sampleGameExtern.root_module.addImport("backlog", blbuild.nw_mod); const installExtern = b.addInstallArtifact(sampleGameExtern, .{ diff --git a/projects/sampleGame/externGame/externGame.zig b/projects/sampleGame/externGame/externGame.zig index 6be0d0d..890ec03 100644 --- a/projects/sampleGame/externGame/externGame.zig +++ b/projects/sampleGame/externGame/externGame.zig @@ -3,6 +3,8 @@ pub export fn startup(p_allocator: *anyopaque, p_a: ?*anyopaque) bool { _ = allocator; imgui.setupFromModule(); platform.setupFromModule(); + rend.setupFromModule(); + backlog.physics.setupFromModule(); // TODO backlog.setupFromModule start_module(core.startup_getArgs(p_a.?)) catch return false; @@ -20,44 +22,96 @@ pub export fn subtract(a: i32, b: i32) i32 { var gAllocator: std.mem.Allocator = undefined; -pub fn log(comptime fmt: []const u8, args: anytype) void { - core.engine_log("[ExternGame]: " ++ fmt, args); -} - fn start_module(args: core.ModuleLoaderArgs) !void { if (args.firstLoad) { _ = core.createObject(ExternGameObject, .{}) catch {}; return; } - log("extern game reloaded ", .{}); - log("hello mother fucker", .{}); - log("accessing old object at {x} same object? {d}", .{ @intFromPtr(core.EngineObject(ExternGameObject).get()), @sizeOf(ExternGameObject) }); + core.logDisplay("externGame", "extern game reloaded ", .{}); + core.logDisplay("externGame", "hello mother fucker", .{}); + core.logDisplay("externGame", "accessing old object at {x} same object? {d}", .{ @intFromPtr(core.EngineObject(ExternGameObject).get()), @sizeOf(ExternGameObject) }); - log("gonna try something dumb- lets patch the vtable of that old object with my vtable's values", .{}); + // getting rid of dumb comment + //log("gonna try something dumb- lets patch the vtable of that old object with my vtable's values", .{}); const ref = core.getEngineObjectRef(ExternGameObject).?; - ref.vtable.tick_func = ExternGameObject.NeonObjectTable.tick_func; + + core.PatchStruct(ExternGameObject, @ptrCast(@alignCast(ref.ptr)), ref.vtable.fieldList.?); } pub const ExternGameObject = struct { pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "game.ExternGameObject"); + pub const Slack = core.SlackStruct(@This(), 512); - allocator: std.mem.Allocator, + allocator: std.mem.Allocator = undefined, + consoleWindow: imgui.utils.ConsoleWindow = undefined, + tbMap: ?*bsp.maploader.TBMap = null, + + //lmao: bool = false, + //lmao2: bool = true, + a: bool = false, + // lib: bool = false, + //s: u32 = 0x42, + + pub fn loadMap2(self: *@This()) !void { + if (self.tbMap) |tbMap| { + core.engine_log("killing the map", .{}); + tbMap.destroy(); + } + + self.tbMap = try bsp.maploader.LoadTrenchbroomMap(self.allocator, .{ + .rotation = core.Rotation.eulerX(core.radians(-90.0)), + .mapName = "bsp/testmap.map", + }); + + core.engine_log("map loaded", .{}); + } pub fn create(allocator: std.mem.Allocator) !*@This() { - const self = try allocator.create(@This()); - self.* = .{ .allocator = allocator }; + const self = try Slack.create(allocator); + self.* = .{ + .allocator = allocator, + .consoleWindow = imgui.utils.ConsoleWindow.init(allocator), + }; + + imgui.utils.addMenuFunc(self, "Input Stack Viewer", extras.inputDebugger.windowOpen); + self.consoleWindow.setup(); return self; } pub fn tick(self: *@This(), dt: f64) void { - _ = dt; - _ = self; const rctx = rend.context(); - imgui.utils.structDebugWindow(rctx); + _ = dt; + if (ig.begin("meh", null, .{})) { + // if (ig.checkbox("move lights ", null)) {} + + ig.textFmt("info: - WASD to move, mouse to look,\n- Q and E to go up and down", .{}) catch return; + ig.textFmt("- shift to slow down camera speed", .{}) catch return; + ig.textFmt("- T to enable/disable mouse cursor", .{}) catch return; + ig.textFmt("- f2 to route all inputs to the doom player", .{}) catch return; + ig.textFmt("- movingLight checkbox makes the light stop moving with you", .{}) catch return; + + if (ig.smallButton("reload map")) { + self.loadMap2() catch unreachable; + } + + if (ig.smallButton("destroy map")) { + if (self.tbMap) |tbMap| { + core.engine_log("killing the map", .{}); + tbMap.destroy(); + self.tbMap = null; + } + } + } + ig.end(); + + //_ = self; + _ = rctx; + // imgui.utils.structHexView(self); + // imgui.utils.structHexView(rctx.activeCamera.?); // if (ig.begin("external game object", null, .{})) { // ig.textf("sup", .{}); @@ -89,7 +143,7 @@ pub const ExternGameObject = struct { } pub fn destroy(self: *@This()) void { - self.allocator.destroy(self); + self.allocator.destroy(Slack.fromPtr(self)); } }; @@ -102,3 +156,5 @@ const imgui = backlog.imgui; const rend = backlog.rend; const ig = imgui.api; const platform = backlog.platform; +const extras = @import("gameExtras"); +const bsp = @import("bsp"); diff --git a/projects/sampleGame/main.zig b/projects/sampleGame/main.zig index c93de79..b5a2309 100644 --- a/projects/sampleGame/main.zig +++ b/projects/sampleGame/main.zig @@ -24,8 +24,6 @@ videoFullbright: bool = false, moveLight: bool = true, -rendererDebugger: *extras.RendererDebug = undefined, - tbMap: ?*bsp.maploader.TBMap = null, // addFunc: ?*const fn (i32, i32) callconv(.C) i32 = undefined, @@ -150,40 +148,6 @@ pub const DamagedHelmet = struct { } }; -pub fn loadMap2(self: *@This()) !void { - if (self.tbMap) |tbMap| { - core.engine_log("killing the map", .{}); - tbMap.destroy(); - } - - self.tbMap = try bsp.maploader.LoadTrenchbroomMap(self.allocator, .{ - .rotation = core.Rotation.eulerX(core.radians(-90.0)), - .mapName = "bsp/testmap.map", - }); - - core.engine_log("map loaded", .{}); -} - -pub fn loadMap(self: *@This()) void { - self.tbMap = bsp.maploader.LoadTrenchbroomMap(self.allocator, .{ - .rotation = core.Rotation.eulerX(core.radians(-90.0)), - .mapName = "bsp/testmap.map", - }) catch |err| { - core.engine_log("unable to load map, error: {any}", .{err}); - self.tbMap = null; - return; - }; - core.engine_log("map loaded", .{}); -} - -fn moduleChangedCallback(pathChanged: []const u8, ctx: ?*anyopaque) void { - const self: *@This() = @ptrCast(@alignCast(ctx)); - - core.engine_log("moduleChanged {s}", .{pathChanged}); - var name = core.MakeName(pathChanged); - self.modules.put(self.allocator, name.handle(), name.utf8()) catch {}; -} - pub fn prepare(self: *@This()) !void { core.engine_log(">>>>>>> game prepare", .{}); var z = core.tracy.ZoneN(@src(), "PREPARING GAME"); @@ -201,9 +165,11 @@ pub fn prepare(self: *@This()) !void { // try self.tryLoadExtern("externGame"); - self.rendererDebugger = try extras.RendererDebug.create(self.allocator); //try core.createObject(extras.RendererDebug, .{}); + // self.rendererDebugger = try extras.RendererDebug.create(self.allocator); //try core.createObject(extras.RendererDebug, .{}); + _ = try core.createObject(extras.RendererDebug, .{}); - self.objectSpawner = try extras.ObjectSpawner.create(self.allocator); + //self.objectSpawner = try extras.ObjectSpawner.create(self.allocator); + self.objectSpawner = try core.createObject(extras.ObjectSpawner, .{}); try self.objectSpawner.addSpawnFunction("fox", FoxObject); try self.objectSpawner.addSpawnFunction("doomplayer", DoomPlayer.DoomCanvas); try self.objectSpawner.addSpawnFunction("empire", @import("empire.zig")); @@ -464,78 +430,78 @@ pub fn tick(self: *@This(), dt: f64) void { // show a window with the current camera's position if (!self.mouseLook) { - self.engineTool.tick(); + // self.engineTool.tick(); self.objectSpawner.windowOpen = !self.mouseLook; - self.objectSpawner.tick(dt); - extras.inputDebugger.tick(); - self.rendererDebugger.tick(dt); - if (ig.begin("meh", null, .{})) { - ig.textFmt("x:{d:.03} y:{d:.03} z:{d:.03}", .{ pos.x, pos.y, pos.z }) catch return; - // if (ig.checkbox("video fullbright", &self.videoFullbright)) { - // //if (self.videoplayerObject.fetch(rend.MeshComponent)) |mesh| { - // // mesh.textureMode.fullbright = self.videoFullbright; - // //} - // } + // self.objectSpawner.tick(dt); + // self.rendererDebugger.tick(dt); + // if (ig.begin("meh", null, .{})) { + // ig.textFmt("x:{d:.03} y:{d:.03} z:{d:.03}", .{ pos.x, pos.y, pos.z }) catch return; + // // if (ig.checkbox("video fullbright", &self.videoFullbright)) { + // // //if (self.videoplayerObject.fetch(rend.MeshComponent)) |mesh| { + // // // mesh.textureMode.fullbright = self.videoFullbright; + // // //} + // // } - if (ig.checkbox("move lights ", &self.moveLight)) {} + // if (ig.checkbox("move lights ", &self.moveLight)) {} - ig.textFmt("info: - WASD to move, mouse to look,\n- Q and E to go up and down", .{}) catch return; - ig.textFmt("- shift to slow down camera speed", .{}) catch return; - ig.textFmt("- T to enable/disable mouse cursor", .{}) catch return; - ig.textFmt("- f2 to route all inputs to the doom player", .{}) catch return; - ig.textFmt("- movingLight checkbox makes the light stop moving with you", .{}) catch return; + // ig.textFmt("info: - WASD to move, mouse to look,\n- Q and E to go up and down", .{}) catch return; + // ig.textFmt("- shift to slow down camera speed", .{}) catch return; + // ig.textFmt("- T to enable/disable mouse cursor", .{}) catch return; + // ig.textFmt("- f2 to route all inputs to the doom player", .{}) catch return; + // ig.textFmt("- movingLight checkbox makes the light stop moving with you", .{}) catch return; - //ig.textf("1 + 2 = {d}", .{self.addFunc.?(1, 2)}); + // //ig.textf("1 + 2 = {d}", .{self.addFunc.?(1, 2)}); - { - ig.textf("modules dirty: ", .{}); - var i = self.modules.iterator(); - while (i.next()) |n| { - ig.textf("{s} pending reload", .{n.value_ptr.*}); - } - } - if (ig.smallButton("reload map")) { - self.loadMap2() catch unreachable; - } + // { + // ig.textf("modules dirty: ", .{}); + // var i = self.modules.iterator(); + // while (i.next()) |n| { + // ig.textf("{s} pending reload", .{n.value_ptr.*}); + // } + // } - if (ig.smallButton("destroy map")) { - if (self.tbMap) |tbMap| { - core.engine_log("killing the map", .{}); - tbMap.destroy(); - self.tbMap = null; - } - } - } - ig.end(); + // if (ig.smallButton("reload map")) { + // self.loadMap2() catch unreachable; + // } - if (ig.begin("mesh components", null, .{})) { - for (rend.MeshComponent.BaseContainer.dense.items) |*v| { - ig.textFmt("mesh: {s}", .{v.value.meshName.utf8()}) catch unreachable; - } - } - ig.end(); + // if (ig.smallButton("destroy map")) { + // if (self.tbMap) |tbMap| { + // core.engine_log("killing the map", .{}); + // tbMap.destroy(); + // self.tbMap = null; + // } + // } + // } + // ig.end(); - if (ig.begin("scene components", null, .{})) { - for (core.Scene.BaseContainer.dense.items) |*v| { - ig.textFmt("scene entity: {d}", .{v.value.handle.index}) catch unreachable; - } + // if (ig.begin("mesh components", null, .{})) { + // for (rend.MeshComponent.BaseContainer.dense.items) |*v| { + // ig.textFmt("mesh: {s}", .{v.value.meshName.utf8()}) catch unreachable; + // } + // } + // ig.end(); - ig.textf("window at 0x{x}", .{@intFromPtr(backlog.platform.context().window)}); - if (ig.smallButton("click to show sdl messagebox")) { - // _ = backlog.platform.windowing.sdl3.c.SDL_ShowSimpleMessageBox(0, "lmao", "you lmaoed your last uwu", backlog.platform.context().window); - } - } - ig.end(); + // if (ig.begin("scene components", null, .{})) { + // for (core.Scene.BaseContainer.dense.items) |*v| { + // ig.textFmt("scene entity: {d}", .{v.value.handle.index}) catch unreachable; + // } + + // ig.textf("window at 0x{x}", .{@intFromPtr(backlog.platform.context().window)}); + // if (ig.smallButton("click to show sdl messagebox")) { + // // _ = backlog.platform.windowing.sdl3.c.SDL_ShowSimpleMessageBox(0, "lmao", "you lmaoed your last uwu", backlog.platform.context().window); + // } + // } + // ig.end(); } } pub fn deinit(self: *@This()) void { self.modules.deinit(self.allocator); - self.rendererDebugger.destroy(); + // self.rendererDebugger.destroy(); DoomPlayer.DoomCanvas.cleanupDoom(); self.fpcamera.destroy(); - self.objectSpawner.destroy(); + // self.objectSpawner.destroy(); // self.videoplayer.destroy(); self.allocator.destroy(self); }