allocator: std.mem.Allocator, moveInput: *core.Axis2dBinding = undefined, cameraSpeed: f32 = 10.0, moveVector: core.Vectorf = .{}, mouseMove: core.Vector2f = .{}, verticalMove: f32 = 0.0, centerDist: f32 = 2.0, centerDistMove: f32 = 2.0, mouseLook: bool = true, showWindow: bool = true, fpcamera: *FpCamera = undefined, // videoplayer: *VideoPlayer = undefined, // videoplayerObject: core.Entity = undefined, objectSpawner: *extras.ObjectSpawner = undefined, inputEnabled: bool = true, videoFullbright: bool = false, moveLight: bool = true, rendererDebugger: *extras.RendererDebug = undefined, tbMap: ?*bsp.maploader.TBMap = null, // addFunc: ?*const fn (i32, i32) callconv(.C) i32 = undefined, engineTool: *extras.EngineTool = undefined, modules: std.AutoHashMapUnmanaged(u32, []const u8) = .{}, pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "SampleGame"); pub fn init(allocator: std.mem.Allocator) !*@This() { const self = try allocator.create(@This()); self.* = .{ .allocator = allocator, }; return self; } const assetReferences = [_]assets.AssetImportReference{ assets.MakeImportRefOptions( "Mesh", "m_skybox", .{ .path = "meshes/skybox.obj" }, ), assets.MakeImportRefOptions( "Mesh", "m_fox", .{ .path = "gltf-samples/Fox/glTF/Fox.gltf" }, ), assets.MakeImportRefOptions( "Texture", "t_fox", .{ .path = "gltf-samples/Fox/glTF/Texture.png" }, ), assets.MakeImportRefOptions( "Texture", "t_skybox", .{ .textureCube = true, .textureList = &.{ "sky_air/cube_right.png", // cubemapfacePositivex, right "sky_air/cube_left.png", // cubemapfacePositivex, right "sky_air/cube_up.png", // cubemapfacePositivex, right "sky_air/cube_down.png", // cubemapfacePositivex, right "sky_air/cube_back.png", // cubemapfacePositivex, right "sky_air/cube_front.png", // cubemapfacePositivex, right }, }, ), }; pub const FoxObject = struct { data: core.GameObjectData = undefined, entity: core.Entity, pub fn create(alloc: std.mem.Allocator) !*@This() { const self = try alloc.create(@This()); const fox = try core.createEntity(); const scene = fox.addComponent(core.Scene).?; scene.setScale(0.01, 0.01, 0.01); const mesh = fox.addComponent(rend.MeshComponent).?; mesh.setMesh("m_fox"); mesh.setTexture("t_fox"); self.entity = fox; return self; } pub fn getEntity(self: *@This()) core.Entity { return self.entity; } pub fn destroy(self: *@This()) void { self.entity.destroy(); self.data.release(self); } }; pub const DamagedHelmet = struct { data: core.GameObjectData = undefined, entity: core.Entity, var first: bool = true; pub fn create(alloc: std.mem.Allocator) !*@This() { const refs = [_]assets.AssetImportReference{ assets.MakeImportRefOptions( "Mesh", "m_damagedHelmet", .{ .path = "gltf-samples/DamagedHelmet/glTF/DamagedHelmet.gltf" }, ), assets.MakeImportRefOptions( "Texture", "t_damagedHelmet", .{ .path = "gltf-samples/DamagedHelmet/glTF/Default_albedo.png" }, ) }; if (first) { first = false; try assets.loadList(refs); } const self = try alloc.create(@This()); self.entity = try core.createEntity(); const scene = self.entity.addComponent(core.Scene).?; _ = scene; // scene.setScale(0.01, 0.01, 0.01); const mesh = self.entity.addComponent(rend.MeshComponent).?; mesh.setMesh("m_damagedHelmet"); mesh.setTexture("t_damagedHelmet"); return self; } pub fn getEntity(self: *@This()) core.Entity { return self.entity; } pub fn destroy(self: *@This()) void { self.entity.destroy(); self.data.release(self); } }; 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"); defer z.End(); try core.fs().addContentPath("sampleGame"); try script.loadTypes("scripts"); try script.runScriptFile("scripts/prepare.lua"); try core.loadModule("externGame", true); self.engineTool = try extras.EngineTool.create(self.allocator); // core.fs().watchPath("zig-out/modules"); // try core.fs().addFileChangedCallback(core.sharedLibName("externGame"), moduleChangedCallback, self); // try self.tryLoadExtern("externGame"); self.rendererDebugger = try extras.RendererDebug.create(self.allocator); //try core.createObject(extras.RendererDebug, .{}); self.objectSpawner = try extras.ObjectSpawner.create(self.allocator); try self.objectSpawner.addSpawnFunction("fox", FoxObject); try self.objectSpawner.addSpawnFunction("doomplayer", DoomPlayer.DoomCanvas); try self.objectSpawner.addSpawnFunction("empire", @import("empire.zig")); try self.objectSpawner.addSpawnFunction("DamagedHelmet", DamagedHelmet); ui.context().drawDebug = true; try assets.loadList(assetReferences); // self.videoplayer = try VideoPlayer.create(self.allocator); // try self.videoplayer.startPlayback("LAPWING2.ogv"); // self.videoplayerObject = try self.videoplayer.createVideoPlayerEntity(); rend.setSkyboxTexture("t_skybox"); const exitInput = try core.ActionBinding.create(core.MakeName("exit")); exitInput.addKey(.escape, .keyDown); _ = exitInput.data.addListener(null, onExit); exitInput.activate(); { const inp = try core.ActionBinding.create(core.MakeName("slow")); inp.addKey(.lshift, .keyDown); inp.addKey(.lshift, .keyUp); _ = inp.data.addListener(self, slowDown); inp.activate(); } self.fpcamera = try FpCamera.create(self.allocator); self.fpcamera.setPosition(.{ .z = 15 }); self.fpcamera.activateCamera(); const moveInput = try core.Axis2dBinding.create(core.MakeName("movement")); moveInput.addKey(.w, 1.0, .y); moveInput.addKey(.s, -1.0, .y); moveInput.addKey(.d, 1.0, .x); moveInput.addKey(.a, -1.0, .x); _ = moveInput.data.addListener(self, onMove); moveInput.activate(); { const i = try core.Axis1dBinding.create(core.MakeName("rotPitch")); i.addKey(.f, 1.0); i.addKey(.v, -1.0); _ = i.data.addListener(self, distanceMove); i.activate(); } { const verticalInput = try core.Axis1dBinding.create(core.MakeName("movementVertical")); verticalInput.addKey(.e, 1.0); verticalInput.addKey(.q, -1.0); _ = verticalInput.data.addListener(self, verticalMovement); verticalInput.activate(); } { const mouseLook = try core.Axis2dBinding.create(core.MakeName("mouseLook")); mouseLook.enableMouseRelative(); _ = mouseLook.data.addListener(self, onMouseLook); mouseLook.activate(); } { const shaderReload = try core.ActionBinding.create(core.MakeName("shaderReload")); shaderReload.addKey(.r, .keyDown); _ = shaderReload.data.addListener(self, onShaderReload); shaderReload.activate(); } { const input = try core.ActionBinding.create(core.MakeName("toggleDoom")); input.addKey(.f2, .keyDown); _ = input.data.addListener(self, toggleDoom); input.activate(); } { const input = try core.ActionBinding.create(core.MakeName("toggleMouseLook")); input.addKey(.t, .keyDown); _ = input.data.addListener(self, toggleMouseLook); input.activate(); } self.updateMouseLook(); } fn onMouseLook(ctx: ?*anyopaque, axis: core.Vector2f) void { const self: *@This() = @ptrCast(@alignCast(ctx)); if (self.inputEnabled) { self.fpcamera.addMouseMovement(axis); } } fn toggleDoom(ctx: ?*anyopaque, action: core.ActionEvent) void { const self: *@This() = @ptrCast(@alignCast(ctx)); _ = action; self.inputEnabled = !self.inputEnabled; if (DoomPlayer.gDoom) |c| { c.inputEnabled = !self.inputEnabled; } } fn toggleMouseLook(ctx: ?*anyopaque, action: core.ActionEvent) void { const self: *@This() = @ptrCast(@alignCast(ctx)); _ = action; self.mouseLook = !self.mouseLook; if (!self.mouseLook) self.showWindow = true; self.updateMouseLook(); } fn updateMouseLook(self: *@This()) void { self.fpcamera.mouseLookEnabled = self.mouseLook; platform.setMouseRelativeMode(self.mouseLook); // platform.setImguiVisible(!self.mouseLook); } fn onShaderReload(ctx: ?*anyopaque, action: core.ActionEvent) void { _ = ctx; _ = action; core.engine_log("reloading shaders, this is gonna cause some leaks but thats ok", .{}); rend.renderer.reloadShaders() catch unreachable; } fn slowDown(ctx: ?*anyopaque, action: core.ActionEvent) void { const self: *@This() = @ptrCast(@alignCast(ctx)); if (action == .keyUp) { self.cameraSpeed = 10.0; } if (action == .keyDown) { self.cameraSpeed = 4.0; } } fn distanceMove(ctx: ?*anyopaque, axis: f32) void { const self: *@This() = @ptrCast(@alignCast(ctx)); self.centerDistMove = axis; } fn cameraRotate(ctx: ?*anyopaque, axis: f32) void { const self: *@This() = @ptrCast(@alignCast(ctx)); self.rotateAxis = axis; } fn verticalMovement(ctx: ?*anyopaque, axis: f32) void { const self: *@This() = @ptrCast(@alignCast(ctx)); self.verticalMove = axis; } fn onMove(ctx: ?*anyopaque, axis: core.Vector2f) void { const self: *@This() = @ptrCast(@alignCast(ctx)); self.moveVector = .{ .x = axis.x, .z = axis.y, .y = 0.0 }; } pub fn onExit(ctx: ?*anyopaque, action: core.ActionEvent) void { _ = ctx; _ = action; core.exitNow(); } pub fn getMouseMovement(self: *@This()) core.Vector2f { const rv = self.mouseMove; self.mouseMove = .{}; return rv; } pub fn tryLoadExtern(self: *@This(), gameName: []const u8) !void { var buf = std.mem.zeroes([256]u8); const os_tag = @import("builtin").os.tag; var suffix: []const u8 = "dll"; var prefix: []const u8 = ""; if (os_tag == .linux) { prefix = "lib"; suffix = "so"; } else if (os_tag == .macos) { suffix = "dylib"; prefix = "lib"; } const path = try std.fmt.bufPrint(&buf, "zig-out/modules/{s}{s}.{s}", .{ prefix, gameName, suffix }); var lib = try std.DynLib.open(path); // std.debug.print("path: {s}", .{path}); self.addFunc = lib.lookup(@TypeOf(self.addFunc.?), "add"); } pub fn tick(self: *@This(), dt: f64) void { const fdt: f32 = @floatCast(dt); // self.loadMap2() catch unreachable; core.loopDelay(@src(), 1.0, dt, struct { s: @TypeOf(self), pub fn func(c: @This()) void { _ = c; // c.s.tryLoadExtern("externGame") catch unreachable; } }, .{ .s = self }); const z1 = tracy.ZoneN(@src(), "inputDebugger"); z1.End(); const z2 = tracy.ZoneN(@src(), "inputDebugger"); z2.End(); const z3 = tracy.ZoneN(@src(), "VideoPlayer"); //self.videoplayer.tick(dt); z3.End(); const z4 = tracy.ZoneN(@src(), "fpCamera"); self.fpcamera.tick(dt); z4.End(); const z5 = tracy.ZoneN(@src(), ""); DoomPlayer.maybeTick(dt); z5.End(); var movement = self.fpcamera.getForwardXZ().fmul(self.moveVector.z * fdt * self.cameraSpeed); movement = movement.add(self.fpcamera.getRightXZ().fmul(self.moveVector.x * fdt * self.cameraSpeed)); movement = movement.add(.{ .y = self.verticalMove * fdt * self.cameraSpeed }); self.centerDist += self.centerDistMove * fdt; const pos = self.fpcamera.getPosition().add(movement); if (self.inputEnabled) { self.fpcamera.setPosition(pos); } self.fpcamera.resolve(); const forward = self.fpcamera.getForward(); const debugCenter = self.fpcamera.getPosition().add(forward.fmul(self.centerDist)); self.objectSpawner.spawnPosition = debugCenter; self.objectSpawner.spawnRotation = core.Rotation.eulerY(core.radians(self.fpcamera.yaw)); if (self.moveLight) { core.debugSphere(debugCenter, 0.3, .{}); core.debugSphere(debugCenter, 0.1, .{ .color = .{ .x = 1.0 } }); rend.context().lightPosition = debugCenter; } // self.loadMap2() catch unreachable; // show a window with the current camera's position if (!self.mouseLook) { 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; // //} // } 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.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; } 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("mesh components", null, .{})) { for (rend.MeshComponent.BaseContainer.dense.items) |*v| { ig.textFmt("mesh: {s}", .{v.value.meshName.utf8()}) catch unreachable; } } 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(); DoomPlayer.DoomCanvas.cleanupDoom(); self.fpcamera.destroy(); self.objectSpawner.destroy(); // self.videoplayer.destroy(); self.allocator.destroy(self); } pub fn main() anyerror!void { var spec = try backlog.createSpecVariant(.{ .name = "sampleGame", .imgui = true, .physics = true, .audio = true, }, std.heap.c_allocator); _ = backlog.initAndRun(&NeonObjectTable, &spec); // try backlog.initializeAndRunStandardProgram(@This(), .{ // .name = "sampleGame", // .enabledModules = .{ // .imgui = true, // .physics = true, // .audio = true, // }, //}); } const DoomPlayer = @import("doomplayer"); //const VideoPlayer = @import("videoplayer"); const extras = @import("gameExtras"); const bsp = @import("bsp"); const FpCamera = extras.FpCamera; const std = @import("std"); const backlog = @import("Backlog"); const assets = backlog.assets; const platform = backlog.platform; const core = backlog.core; const ig = backlog.imgui.api; const rend = backlog.rend; const script = core.script; const tracy = core.tracy; const ui = backlog.ui;