const Engine = @This(); const std = @import("std"); const Recti = @import("geometry/Recti.zig"); const Matrix4f = @import("geometry/Matrix4f.zig"); const shaders = @import("shaders.zig"); const Texture = @import("Texture.zig"); const Sprite = @import("Sprite.zig"); const Layer = @import("Layer.zig"); const Color = @import("Color.zig"); const Scene = @import("Scene.zig"); const assets = @import("assets.zig"); const c = @cImport({ @cInclude("glad/glad.h"); @cInclude("GLFW/glfw3.h"); }); const AutoHashMap = std.AutoHashMap; var store: AutoHashMap(*c.GLFWwindow, *Engine) = AutoHashMap(*c.GLFWwindow, *Engine).init(std.heap.page_allocator); // export const IGame = struct { // render: fn () void, // update: fn (f32) void, // }; current_scene: Scene, 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 { const desc: *const u8 = @ptrCast(desc_c); std.log.err("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 }); 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); } 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); } // export fn run(game: *const IGame) !void { pub fn run(self: *Engine) !void { // in case of errors, set callback early! // for some reason this returns an error function as well?? _ = c.glfwSetErrorCallback(errorCallback); // 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); c.glfwDefaultWindowHints(); c.glfwWindowHint(c.GLFW_SAMPLES, 4); // c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MINOR, 3); // 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 { 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); if (c.gladLoadGL() != 1) { std.log.err("Failed to load GL context", .{}); return error.Initialization; } // configure the context! c.glfwSwapInterval(1); // compile shaders... try shaders.load(); try assets.load(); _ = c.glfwSetWindowSizeCallback(window, on_resize); const clearBrightness: f32 = 0.09; c.glClearColor(clearBrightness, clearBrightness, clearBrightness, 1.0); // c.glEnable(c.GL_MULTISAMPLE); c.glDisable(c.GL_MULTISAMPLE); c.glEnable(c.GL_BLEND); c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA); c.glEnable(c.GL_DEPTH_TEST); c.glDepthFunc(c.GL_LEQUAL); c.glDepthMask(c.GL_TRUE); 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(); // run the main loop while (c.glfwWindowShouldClose(window) == 0) { 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); c.glfwSwapBuffers(window); c.glfwPollEvents(); self.current_scene.update(0.0042); } }