diff --git a/src/Engine.zig b/src/Engine.zig index 9129ef4..030b790 100644 --- a/src/Engine.zig +++ b/src/Engine.zig @@ -1,5 +1,3 @@ -const Engine = @This(); - const std = @import("std"); const Recti = @import("geometry/Recti.zig"); const Matrix4f = @import("geometry/Matrix4f.zig"); @@ -17,52 +15,50 @@ const c = @cImport({ }); const AutoHashMap = std.AutoHashMap; -var store: AutoHashMap(*c.GLFWwindow, *Engine) = AutoHashMap(*c.GLFWwindow, *Engine).init(std.heap.page_allocator); +const Error = error { + WindowNotInitialized, + WindowAlreadyInitialized, + NoScene, + AlreadyRunning +}; -// export const IGame = struct { -// render: fn () void, -// update: fn (f32) void, -// }; -current_scene: Scene, -projection: Matrix4f = undefined, +var window: ?*c.GLFWwindow = null; +var current_scene: ?Scene = null; +var next_scene: ?Scene = null; +var projection: Matrix4f = undefined; -pub fn create(initial_scene: Scene) Engine { - return .{ - .current_scene = initial_scene - }; -} - -fn errorCallback(err: c_int, desc_c: [*c]const u8) callconv(.C) void { +fn glfw_on_error(err: c_int, desc_c: [*c]const u8) callconv(.C) void { const desc: *const u8 = @ptrCast(desc_c); - std.log.err("glfw error {x:0>8}: {s}", .{ err, desc }); + std.log.err("[Engine:glfw_on_error] glfw error {x:0>8}: {s}", .{ err, desc }); } -fn on_resize (window: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void { - std.debug.print("[Engine:on_resize] {?}: {d} x {d}\n", .{ window, width, height }); +fn glfw_on_resize (_: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void { + std.debug.print("[Engine:glfw_on_resize] {?}: {d} x {d}\n", .{ window, width, height }); c.glViewport(0, 0, width, height); - const engine_instance = store.get(window.?).?; - engine_instance.set_matrices(width, height); - engine_instance.current_scene.resize(width, height); + projection = Matrix4f.orthographic(0, @floatFromInt(width), @floatFromInt(height), 0, 0, 100); + shaders.set_projection_matrix(&projection); + if (current_scene != null) current_scene.?.resize(width, height); } -pub fn set_matrices(self: *Engine, width: i32, height: i32) void { - self.projection = Matrix4f.orthographic(0, @floatFromInt(width), @floatFromInt(height), 0, 0, 100); - shaders.set_projection_matrix(&self.projection); +fn get_size() struct { i32, i32 } { + var width: c_int = undefined; + var height: c_int = undefined; + c.glfwGetFramebufferSize(window, @ptrCast(&width), @ptrCast(&height)); + return .{ width, height }; } -// export fn run(game: *const IGame) !void { -pub fn run(self: *Engine) !void { +pub fn setup() !void { + if (window != null) return Error.WindowAlreadyInitialized; // in case of errors, set callback early! // for some reason this returns an error function as well?? - _ = c.glfwSetErrorCallback(errorCallback); + _ = c.glfwSetErrorCallback(glfw_on_error); // initialize glfw capabilities & cleanup if (c.glfwInit() != 1) { std.log.err("Failed to init glfw", .{}); return error.Initialization; } - defer c.glfwTerminate(); // ensure glfw knows what we're about // c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 3); @@ -72,13 +68,10 @@ pub fn run(self: *Engine) !void { // c.glfwWindowHint(c.GLFW_OPENGL_PROFILE, c.GLFW_OPENGL_CORE_PROFILE); // create the window & cleanup - const window = c.glfwCreateWindow(640, 480, "My Title", null, null) orelse { + window = c.glfwCreateWindow(1920, 1080, "My Title", null, null) orelse { std.log.err("Failed to open window", .{}); return error.Initialization; }; - defer c.glfwDestroyWindow(window); - - try store.put(window, self); // load opengl into the window c.glfwMakeContextCurrent(window); @@ -92,11 +85,9 @@ pub fn run(self: *Engine) !void { // compile shaders... try shaders.load(); - try assets.load(); - _ = c.glfwSetWindowSizeCallback(window, on_resize); - + _ = c.glfwSetWindowSizeCallback(window, glfw_on_resize); const clearBrightness: f32 = 0.09; c.glClearColor(clearBrightness, clearBrightness, clearBrightness, 1.0); @@ -107,23 +98,47 @@ pub fn run(self: *Engine) !void { c.glEnable(c.GL_DEPTH_TEST); c.glDepthFunc(c.GL_LEQUAL); c.glDepthMask(c.GL_TRUE); + + const width, const height = get_size(); + c.glViewport(0, 0, width, height); + projection = Matrix4f.orthographic(0, @floatFromInt(width), @floatFromInt(height), 0, 0, 100); + shaders.set_projection_matrix(&projection); +} - var width: c_int = undefined; - var height: c_int = undefined; - c.glfwGetFramebufferSize(window, @ptrCast(&width), @ptrCast(&height)); - std.debug.print("[Engine:run] window size {d} x {d}\n", .{ width, height }); - self.set_matrices(width, height); - try self.current_scene.start(); +pub fn destroy() void { + c.glfwDestroyWindow(window); + c.glfwTerminate(); +} + +pub fn set_scene(scene: Scene) void { + next_scene = scene; +} + +pub fn run() Error!void { + if (window == null) return Error.WindowNotInitialized; + if (current_scene != null) return Error.AlreadyRunning; + if (next_scene == null) return Error.NoScene; // run the main loop while (c.glfwWindowShouldClose(window) == 0) { + // switch scenes if need be + if (next_scene != null) { + if (current_scene != null) current_scene.?.destroy(); + current_scene = next_scene; + next_scene = null; + // start our scene + try current_scene.?.start(); + // and send it the appropriate resize. + const width, const height = get_size(); + current_scene.?.resize(width, height); + } + // setup for drawing c.glClear(c.GL_COLOR_BUFFER_BIT | c.GL_DEPTH_BUFFER_BIT); - self.current_scene.draw(); - assets.terrain[0][0].draw(Recti.from_xywh(-1000, -1000, 50000, 50000), Layer.FLOOR, Color.WHITE); + current_scene.?.draw(); c.glfwSwapBuffers(window); c.glfwPollEvents(); - self.current_scene.update(0.0042); + current_scene.?.update(0.0042); } } diff --git a/src/Scene.zig b/src/Scene.zig index ea56039..260fe7d 100644 --- a/src/Scene.zig +++ b/src/Scene.zig @@ -11,13 +11,15 @@ const std = @import("std"); const ArrayList = std.ArrayList; const AutoHashMap = std.AutoHashMap; +// cache and list bothe store entities. treat entities as fat pointers. +// Pointers into an arraylist WILL become invalid. entities: ArrayList(Entity) = undefined, -tagged_entities: AutoHashMap(Tag.ID, *Entity) = undefined, +tagged_entities: AutoHashMap(Tag.ID, Entity) = undefined, pub fn create() Scene { var self = Scene {}; self.entities = ArrayList(Entity).init(std.heap.page_allocator); - self.tagged_entities = AutoHashMap(Tag.ID, *Entity).init(std.heap.page_allocator); + self.tagged_entities = AutoHashMap(Tag.ID, Entity).init(std.heap.page_allocator); return self; } @@ -32,33 +34,22 @@ pub fn start(self: *Scene) !void { std.debug.print("[Scene:start] Scene starting...\n", .{}); std.debug.print("[Scene:start] entities: {}\n", .{ self.entities.items.len }); std.debug.print("[Scene:start] tagged entities: {}\n", .{ self.tagged_entities.count() }); + var tagged_entities_iterator = self.tagged_entities.iterator(); + while (tagged_entities_iterator.next()) |entry| { + std.debug.print("[Scene:start] - {s}: {*}\n", .{ entry.key_ptr.*, entry.value_ptr }); + } for (self.entities.items) |entity| { entity.start(self); } } pub fn get(self: *const Scene, tag: Tag, comptime T: type) *T { + if (tag.id == Tag.NONE.id) @panic("Cannot find by Tag.NONE"); const tag_id = tag.id; - const entity_ptr: *Entity = self.tagged_entities.get(tag_id) orelse @panic("Could not find entity"); - // @compileLog(entity_ptr); - const opaque_ptr = entity_ptr.ptr; + const entity: Entity = self.tagged_entities.get(tag_id) orelse @panic("Could not find entity"); + const opaque_ptr = entity.ptr; const real_ptr: *T = @ptrCast(@alignCast(opaque_ptr)); return real_ptr; - // var iterator = self.tagged_entities.iterator(); - // while (iterator.next()) |entry| { - // const id_ptr: *Tag.ID = entry.key_ptr; - // const entity_ptr_ptr: **Entity = entry.value_ptr; - // const entity_ptr: *Entity = entity_ptr_ptr.*; - // - // const other_id = entity_ptr.tag.id; - // if (tag_id == other_id) { - // const opaque_ptr: *anyopaque = entity_ptr.ptr; - // const real_ptr: *T = @ptrCast(@alignCast(opaque_ptr)); - // // @compileLog(r); - // return real_ptr; - // } - // } - // @panic("Could not find entity"); } pub fn add(self: *Scene, instance_ptr: anytype) !void { @@ -81,13 +72,11 @@ pub fn add(self: *Scene, instance_ptr: anytype) !void { if (!@hasDecl(instance_type, "entity")) @compileError("Pointer must be to a struct with fn entity() Entity"); + // again, entities are fat pointers. they are okay to be copied const entity: Entity = instance_ptr.entity(); - - const entity_ptr = try self.entities.addOne(); - entity_ptr.* = entity; - - if (entity_ptr.tag.id != Tag.NONE.id) { - try self.tagged_entities.put(entity.tag.id, entity_ptr); + try self.entities.append(entity); + if (entity.tag.id != Tag.NONE.id) { + try self.tagged_entities.put(entity.tag.id, entity); } } diff --git a/src/main.zig b/src/main.zig index d144400..58272f3 100644 --- a/src/main.zig +++ b/src/main.zig @@ -6,6 +6,8 @@ const scenes = @import("scenes.zig"); pub fn main() !void { // engine.preload(assets.load); - var engine = Engine.create(try scenes.game()); - try engine.run(); + try Engine.setup(); + Engine.set_scene(try scenes.game()); + try Engine.run(); + Engine.destroy(); } diff --git a/src/scenes.zig b/src/scenes.zig index 5cb5457..b9ffff6 100644 --- a/src/scenes.zig +++ b/src/scenes.zig @@ -10,7 +10,9 @@ pub fn game() !Scene { // second try is for allocating terrain on the heap... try scene.add(try Camera.create()); try scene.add(try Terrain.create(123)); - try scene.add(try Pawn.random()); + for (0..5) |_| { + try scene.add(try Pawn.random()); + } return scene; }