split engine to separate library
This commit is contained in:
parent
8434ea6a64
commit
e5fccc21f2
299
build.zig
299
build.zig
@ -1,161 +1,32 @@
|
||||
const std = @import("std");
|
||||
const sokol = @import("sokol");
|
||||
const builtin = @import("builtin");
|
||||
const Engine = @import("engine");
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const has_imgui = b.option(bool, "imgui", "ImGui integration") orelse (optimize == .Debug);
|
||||
var has_tracy = b.option(bool, "tracy", "Tracy integration") orelse (optimize == .Debug);
|
||||
const has_console = b.option(bool, "console", "Show console (Window only)") orelse (optimize == .Debug);
|
||||
|
||||
const isWasm = target.result.cpu.arch.isWasm();
|
||||
|
||||
if (isWasm) {
|
||||
has_tracy = false;
|
||||
}
|
||||
|
||||
const mod_main = b.createModule(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.link_libc = true
|
||||
const mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/game.zig"),
|
||||
});
|
||||
|
||||
const dep_sokol = b.dependency("sokol", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.with_sokol_imgui = has_imgui,
|
||||
});
|
||||
mod_main.linkLibrary(dep_sokol.artifact("sokol_clib"));
|
||||
mod_main.addImport("sokol", dep_sokol.module("sokol"));
|
||||
|
||||
if (has_imgui) {
|
||||
if (b.lazyDependency("cimgui", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
})) |dep_cimgui| {
|
||||
const cimgui = b.lazyImport(@This(), "cimgui").?;
|
||||
const cimgui_conf = cimgui.getConfig(false);
|
||||
mod_main.addImport("cimgui", dep_cimgui.module(cimgui_conf.module_name));
|
||||
dep_sokol.artifact("sokol_clib").addIncludePath(dep_cimgui.path(cimgui_conf.include_dir));
|
||||
}
|
||||
}
|
||||
|
||||
const dep_tracy = b.dependency("tracy", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.tracy_enable = has_tracy,
|
||||
.tracy_only_localhost = true
|
||||
});
|
||||
if (has_tracy) {
|
||||
mod_main.linkLibrary(dep_tracy.artifact("tracy"));
|
||||
}
|
||||
mod_main.addImport("tracy", dep_tracy.module("tracy"));
|
||||
|
||||
const dep_tiled = b.dependency("tiled", .{});
|
||||
mod_main.addImport("tiled", dep_tiled.module("tiled"));
|
||||
mod.addImport("tiled", dep_tiled.module("tiled"));
|
||||
|
||||
const dep_stb = b.dependency("stb", .{});
|
||||
mod_main.addImport("stb_image", dep_stb.module("stb_image"));
|
||||
mod_main.addImport("stb_vorbis", dep_stb.module("stb_vorbis"));
|
||||
|
||||
const dep_fontstash_c = b.dependency("fontstash_c", .{});
|
||||
mod_main.addIncludePath(dep_fontstash_c.path("src"));
|
||||
|
||||
const dep_sokol_c = b.dependency("sokol_c", .{});
|
||||
{
|
||||
var cflags_buffer: [64][]const u8 = undefined;
|
||||
var cflags = std.ArrayListUnmanaged([]const u8).initBuffer(&cflags_buffer);
|
||||
switch (sokol.resolveSokolBackend(.auto, target.result)) {
|
||||
.d3d11 => try cflags.appendBounded("-DSOKOL_D3D11"),
|
||||
.metal => try cflags.appendBounded("-DSOKOL_METAL"),
|
||||
.gl => try cflags.appendBounded("-DSOKOL_GLCORE"),
|
||||
.gles3 => try cflags.appendBounded("-DSOKOL_GLES3"),
|
||||
.wgpu => try cflags.appendBounded("-DSOKOL_WGPU"),
|
||||
else => @panic("unknown sokol backend"),
|
||||
}
|
||||
|
||||
mod_main.addIncludePath(dep_sokol_c.path("util"));
|
||||
mod_main.addCSourceFile(.{
|
||||
.file = b.path("src/engine/fontstash/sokol_fontstash_impl.c"),
|
||||
.flags = cflags.items
|
||||
});
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// const sdl = b.dependency("sdl", .{
|
||||
// .optimize = optimize,
|
||||
// .target = target,
|
||||
// .linkage = .static,
|
||||
// .default_target_config = !isWasm
|
||||
// });
|
||||
// mod_main.linkLibrary(sdl.artifact("SDL3"));
|
||||
// if (isWasm) {
|
||||
// // TODO: Define buid config for wasm
|
||||
// }
|
||||
|
||||
var options = b.addOptions();
|
||||
options.addOption(bool, "has_imgui", has_imgui);
|
||||
options.addOption(bool, "has_tracy", has_tracy);
|
||||
mod_main.addOptions("build_options", options);
|
||||
|
||||
// from here on different handling for native vs wasm builds
|
||||
if (target.result.cpu.arch.isWasm()) {
|
||||
try buildWasm(b, .{
|
||||
.name = "sokol_template",
|
||||
.mod_main = mod_main,
|
||||
.dep_sokol = dep_sokol,
|
||||
});
|
||||
} else {
|
||||
try buildNative(b, "sokol_template", mod_main, has_console);
|
||||
}
|
||||
}
|
||||
|
||||
fn buildNative(b: *std.Build, name: []const u8, mod: *std.Build.Module, has_console: bool) !void {
|
||||
const exe = b.addExecutable(.{
|
||||
.name = name,
|
||||
.root_module = mod
|
||||
Engine.init(b, .{
|
||||
.name = "sokol_template",
|
||||
.root_module = mod,
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.dep_engine = b.dependency("engine", .{}),
|
||||
.has_imgui = b.option(bool, "imgui", "ImGui integration"),
|
||||
.has_tracy = b.option(bool, "tracy", "Tracy integration"),
|
||||
.win32_has_console = b.option(bool, "console", "Show console (Window only)"),
|
||||
.win32_png_icon = b.path("src/assets/icon.png")
|
||||
});
|
||||
const target = mod.resolved_target.?;
|
||||
if (target.result.os.tag == .windows) {
|
||||
exe.subsystem = if (has_console) .Console else .Windows;
|
||||
|
||||
const png_to_icon_tool = b.addExecutable(.{
|
||||
.name = "png-to-icon",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.root_source_file = b.path("tools/png-to-icon.zig"),
|
||||
}),
|
||||
});
|
||||
const dep_stb_image = b.dependency("stb_image", .{});
|
||||
png_to_icon_tool.root_module.addImport("stb_image", dep_stb_image.module("stb_image"));
|
||||
|
||||
const png_to_icon_step = b.addRunArtifact(png_to_icon_tool);
|
||||
png_to_icon_step.addFileArg(b.path("src/assets/icon.png"));
|
||||
const icon_file = png_to_icon_step.addOutputFileArg("icon.ico");
|
||||
|
||||
const add_icon_step = AddExecutableIcon.init(exe, icon_file);
|
||||
exe.step.dependOn(&add_icon_step.step);
|
||||
}
|
||||
b.installArtifact(exe);
|
||||
|
||||
{
|
||||
const run_step = b.step("run", "Run game");
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
mod.resolved_target = target;
|
||||
const exe_tests = b.addTest(.{
|
||||
.root_module = exe.root_module,
|
||||
.root_module = mod,
|
||||
});
|
||||
|
||||
const run_exe_tests = b.addRunArtifact(exe_tests);
|
||||
@ -164,141 +35,3 @@ fn buildNative(b: *std.Build, name: []const u8, mod: *std.Build.Module, has_cons
|
||||
test_step.dependOn(&run_exe_tests.step);
|
||||
}
|
||||
}
|
||||
|
||||
const BuildWasmOptions = struct {
|
||||
name: []const u8,
|
||||
mod_main: *std.Build.Module,
|
||||
dep_sokol: *std.Build.Dependency,
|
||||
};
|
||||
|
||||
fn patchWasmIncludeDirs(
|
||||
module: *std.Build.Module,
|
||||
path: std.Build.LazyPath,
|
||||
depend_step: *std.Build.Step
|
||||
) void {
|
||||
if (module.link_libc != null and module.link_libc.?) {
|
||||
// need to inject the Emscripten system header include path into
|
||||
// the cimgui C library otherwise the C/C++ code won't find
|
||||
// C stdlib headers
|
||||
module.addSystemIncludePath(path);
|
||||
}
|
||||
|
||||
for (module.import_table.values()) |imported_module| {
|
||||
patchWasmIncludeDirs(imported_module, path, depend_step);
|
||||
}
|
||||
|
||||
for (module.link_objects.items) |link_object| {
|
||||
if (link_object != .other_step) {
|
||||
continue;
|
||||
}
|
||||
const lib = link_object.other_step;
|
||||
if (&lib.step == depend_step) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lib.root_module.link_libc != null and lib.root_module.link_libc.?) {
|
||||
// need to inject the Emscripten system header include path into
|
||||
// the cimgui C library otherwise the C/C++ code won't find
|
||||
// C stdlib headers
|
||||
lib.root_module.addSystemIncludePath(path);
|
||||
|
||||
// all C libraries need to depend on the sokol library, when building for
|
||||
// WASM this makes sure that the Emscripten SDK has been setup before
|
||||
// C compilation is attempted (since the sokol C library depends on the
|
||||
// Emscripten SDK setup step)
|
||||
lib.step.dependOn(depend_step);
|
||||
}
|
||||
patchWasmIncludeDirs(lib.root_module, path, depend_step);
|
||||
}
|
||||
}
|
||||
|
||||
fn buildWasm(b: *std.Build, opts: BuildWasmOptions) !void {
|
||||
opts.mod_main.sanitize_c = .off;
|
||||
|
||||
// build the main file into a library, this is because the WASM 'exe'
|
||||
// needs to be linked in a separate build step with the Emscripten linker
|
||||
const main_lib = b.addLibrary(.{
|
||||
.name = "index",
|
||||
.root_module = opts.mod_main,
|
||||
});
|
||||
|
||||
const dep_emsdk = opts.dep_sokol.builder.dependency("emsdk", .{});
|
||||
|
||||
patchWasmIncludeDirs(
|
||||
opts.mod_main,
|
||||
dep_emsdk.path("upstream/emscripten/cache/sysroot/include"),
|
||||
&(opts.dep_sokol.artifact("sokol_clib").step)
|
||||
);
|
||||
|
||||
// create a build step which invokes the Emscripten linker
|
||||
const link_step = try sokol.emLinkStep(b, .{
|
||||
.lib_main = main_lib,
|
||||
.target = opts.mod_main.resolved_target.?,
|
||||
.optimize = opts.mod_main.optimize.?,
|
||||
.emsdk = dep_emsdk,
|
||||
.use_webgl2 = true,
|
||||
.use_emmalloc = true,
|
||||
.use_filesystem = false,
|
||||
.shell_file_path = b.path("src/engine/shell.html"),
|
||||
});
|
||||
// attach to default target
|
||||
b.getInstallStep().dependOn(&link_step.step);
|
||||
// ...and a special run step to start the web build output via 'emrun'
|
||||
const run = sokol.emRunStep(b, .{ .name = "index", .emsdk = dep_emsdk });
|
||||
run.step.dependOn(&link_step.step);
|
||||
b.step("run", "Run game").dependOn(&run.step);
|
||||
|
||||
// TODO: Create a zip archive of all of the files. Would be useful for easier itch.io upload
|
||||
}
|
||||
|
||||
const AddExecutableIcon = struct {
|
||||
obj: *std.Build.Step.Compile,
|
||||
step: std.Build.Step,
|
||||
icon_file: std.Build.LazyPath,
|
||||
resource_file: std.Build.LazyPath,
|
||||
|
||||
fn init(obj: *std.Build.Step.Compile, icon_file: std.Build.LazyPath) *AddExecutableIcon {
|
||||
const b = obj.step.owner;
|
||||
const self = b.allocator.create(AddExecutableIcon) catch @panic("OOM");
|
||||
|
||||
self.obj = obj;
|
||||
self.step = std.Build.Step.init(.{
|
||||
.id = .custom,
|
||||
.name = "add executable icon",
|
||||
.owner = b,
|
||||
.makeFn = make
|
||||
});
|
||||
|
||||
self.icon_file = icon_file;
|
||||
icon_file.addStepDependencies(&self.step);
|
||||
|
||||
const write_files = b.addWriteFiles();
|
||||
self.resource_file = write_files.add("resource-file.rc", "");
|
||||
self.step.dependOn(&write_files.step);
|
||||
|
||||
self.obj.addWin32ResourceFile(.{
|
||||
.file = self.resource_file,
|
||||
});
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
|
||||
const b = step.owner;
|
||||
const self: *AddExecutableIcon = @fieldParentPtr("step", step);
|
||||
|
||||
const resource_file = try std.fs.cwd().createFile(self.resource_file.getPath(b), .{ });
|
||||
defer resource_file.close();
|
||||
|
||||
const relative_icon_path = try std.fs.path.relative(
|
||||
b.allocator,
|
||||
self.resource_file.dirname().getPath(b),
|
||||
self.icon_file.getPath(b)
|
||||
);
|
||||
std.mem.replaceScalar(u8, relative_icon_path, '\\', '/');
|
||||
|
||||
try resource_file.writeAll("IDI_ICON ICON \"");
|
||||
try resource_file.writeAll(relative_icon_path);
|
||||
try resource_file.writeAll("\"");
|
||||
}
|
||||
};
|
||||
|
||||
@ -4,37 +4,12 @@
|
||||
.fingerprint = 0x60a8e079a691c8d9, // Changing this has security and trust implications.
|
||||
.minimum_zig_version = "0.15.2",
|
||||
.dependencies = .{
|
||||
.sokol = .{
|
||||
.url = "git+https://github.com/floooh/sokol-zig.git#aaf291ca2d3d1cedc05d65f5a1cacae0f53d934a",
|
||||
.hash = "sokol-0.1.0-pb1HK4iDNgCom5dkY66eUBm_bYBHEl8KWFDAiqwWgpEy",
|
||||
},
|
||||
.sokol_c = .{
|
||||
.url = "git+https://github.com/floooh/sokol.git#c66a1f04e6495d635c5e913335ab2308281e0492",
|
||||
.hash = "N-V-__8AAC3eYABB1DVLb4dkcEzq_xVeEZZugVfQ6DoNQBDN",
|
||||
},
|
||||
.fontstash_c = .{
|
||||
.url = "git+https://github.com/memononen/fontstash.git#b5ddc9741061343740d85d636d782ed3e07cf7be",
|
||||
.hash = "N-V-__8AAA9xHgAxdLYPmlNTy6qzv9IYqiIePEHQUOPWYQ_6",
|
||||
},
|
||||
.cimgui = .{
|
||||
.url = "git+https://github.com/floooh/dcimgui.git#33c99ef426b68030412b5a4b11487a23da9d4f13",
|
||||
.hash = "cimgui-0.1.0-44ClkQRJlABdFMKRqIG8KDD6jy1eQbgPO335NziPYjmL",
|
||||
.lazy = true,
|
||||
},
|
||||
.tracy = .{
|
||||
.url = "git+https://github.com/sagehane/zig-tracy.git#80933723efe9bf840fe749b0bfc0d610f1db1669",
|
||||
.hash = "zig_tracy-0.0.5-aOIqsX1tAACKaRRB-sraMLuNiMASXi_y-4FtRuw4cTpx",
|
||||
},
|
||||
.tiled = .{
|
||||
.path = "./libs/tiled",
|
||||
},
|
||||
.stb = .{
|
||||
.path = "./libs/stb",
|
||||
.engine = .{
|
||||
.path = "./engine/",
|
||||
},
|
||||
// .sdl = .{
|
||||
// .url = "git+https://github.com/allyourcodebase/SDL3.git#f85824b0db782b7d01c60aaad8bcb537892394e8",
|
||||
// .hash = "sdl-0.0.0-i4QD0UuFqADRQysNyJ1OvCOZnq-clcVhq3BfPcBOf9zr",
|
||||
// },
|
||||
},
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
|
||||
385
engine/build.zig
Normal file
385
engine/build.zig
Normal file
@ -0,0 +1,385 @@
|
||||
const std = @import("std");
|
||||
const sokol = @import("sokol");
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
_ = b; // autofix
|
||||
}
|
||||
|
||||
const InitOptions = struct {
|
||||
name: []const u8,
|
||||
root_module: *std.Build.Module,
|
||||
|
||||
target: std.Build.ResolvedTarget,
|
||||
optimize: std.builtin.OptimizeMode,
|
||||
|
||||
dep_engine: *std.Build.Dependency,
|
||||
|
||||
has_imgui: ?bool = null,
|
||||
has_tracy: ?bool = null,
|
||||
win32_has_console: ?bool = null,
|
||||
win32_png_icon: ?std.Build.LazyPath = null
|
||||
};
|
||||
|
||||
pub fn init(outer_builder: *std.Build, opts: InitOptions) void {
|
||||
const b = opts.dep_engine.builder;
|
||||
|
||||
const engine_module = createEngineModule(b, .{
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.has_tracy = opts.has_tracy,
|
||||
.has_imgui = opts.has_imgui
|
||||
});
|
||||
|
||||
opts.root_module.resolved_target = opts.target;
|
||||
opts.root_module.optimize = opts.optimize;
|
||||
const game_lib = b.addLibrary(.{
|
||||
.name = "game",
|
||||
.root_module = opts.root_module,
|
||||
.linkage = .static,
|
||||
});
|
||||
opts.root_module.addImport("engine", engine_module.root_module);
|
||||
|
||||
const mod_main = b.createModule(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.link_libc = true
|
||||
});
|
||||
mod_main.addImport("engine", engine_module.root_module);
|
||||
mod_main.linkLibrary(game_lib);
|
||||
|
||||
var run_cmd_step: *std.Build.Step = undefined;
|
||||
if (opts.target.result.cpu.arch.isWasm()) {
|
||||
const build_step = buildWasm(b, .{
|
||||
.name = "index",
|
||||
.root_module = mod_main,
|
||||
.dep_sokol = engine_module.dep_sokol,
|
||||
.outer_builder = outer_builder
|
||||
});
|
||||
outer_builder.getInstallStep().dependOn(build_step);
|
||||
|
||||
const dep_sokol = engine_module.dep_sokol;
|
||||
const dep_emsdk = dep_sokol.builder.dependency("emsdk", .{});
|
||||
const emrun_step = sokol.emRunStep(b, .{ .name = "index", .emsdk = dep_emsdk });
|
||||
emrun_step.step.dependOn(build_step);
|
||||
run_cmd_step = &emrun_step.step;
|
||||
|
||||
// TODO: Create a zip archive of all of the files. Would be useful for easier itch.io upload
|
||||
} else {
|
||||
const exe = buildNative(b, .{
|
||||
.name = opts.name,
|
||||
.root_module = mod_main,
|
||||
.win32_has_console = opts.win32_has_console,
|
||||
.win32_png_icon = opts.win32_png_icon
|
||||
});
|
||||
outer_builder.installArtifact(exe);
|
||||
|
||||
{
|
||||
const run_cmd = outer_builder.addRunArtifact(exe);
|
||||
if (outer_builder.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
run_cmd_step = &run_cmd.step;
|
||||
}
|
||||
}
|
||||
|
||||
const run_step = outer_builder.step("run", "Run game");
|
||||
run_step.dependOn(run_cmd_step);
|
||||
}
|
||||
|
||||
const EngineModule = struct {
|
||||
root_module: *std.Build.Module,
|
||||
dep_sokol: *std.Build.Dependency,
|
||||
|
||||
const Options = struct {
|
||||
target: std.Build.ResolvedTarget,
|
||||
optimize: std.builtin.OptimizeMode,
|
||||
|
||||
has_imgui: ?bool = null,
|
||||
has_tracy: ?bool = null,
|
||||
};
|
||||
};
|
||||
|
||||
fn createEngineModule(b: *std.Build, opts: EngineModule.Options) EngineModule {
|
||||
const has_imgui = opts.has_imgui orelse (opts.optimize == .Debug);
|
||||
var has_tracy = opts.has_tracy orelse (opts.optimize == .Debug);
|
||||
const isWasm = opts.target.result.cpu.arch.isWasm();
|
||||
|
||||
if (isWasm) {
|
||||
has_tracy = false;
|
||||
}
|
||||
|
||||
const mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/root.zig"),
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.link_libc = true
|
||||
});
|
||||
|
||||
const dep_sokol = b.dependency("sokol", .{
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.with_sokol_imgui = has_imgui,
|
||||
});
|
||||
mod.linkLibrary(dep_sokol.artifact("sokol_clib"));
|
||||
mod.addImport("sokol", dep_sokol.module("sokol"));
|
||||
|
||||
if (has_imgui) {
|
||||
if (b.lazyDependency("cimgui", .{
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
})) |dep_cimgui| {
|
||||
const cimgui = b.lazyImport(@This(), "cimgui").?;
|
||||
const cimgui_conf = cimgui.getConfig(false);
|
||||
mod.addImport("cimgui", dep_cimgui.module(cimgui_conf.module_name));
|
||||
dep_sokol.artifact("sokol_clib").addIncludePath(dep_cimgui.path(cimgui_conf.include_dir));
|
||||
}
|
||||
}
|
||||
|
||||
const dep_tracy = b.dependency("tracy", .{
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.tracy_enable = has_tracy,
|
||||
.tracy_only_localhost = true
|
||||
});
|
||||
if (has_tracy) {
|
||||
mod.linkLibrary(dep_tracy.artifact("tracy"));
|
||||
}
|
||||
mod.addImport("tracy", dep_tracy.module("tracy"));
|
||||
|
||||
const dep_stb = b.dependency("stb", .{});
|
||||
mod.addImport("stb_image", dep_stb.module("stb_image"));
|
||||
mod.addImport("stb_vorbis", dep_stb.module("stb_vorbis"));
|
||||
|
||||
const dep_fontstash_c = b.dependency("fontstash_c", .{});
|
||||
mod.addIncludePath(dep_fontstash_c.path("src"));
|
||||
|
||||
const dep_sokol_c = b.dependency("sokol_c", .{});
|
||||
{
|
||||
var cflags_buffer: [64][]const u8 = undefined;
|
||||
var cflags = std.ArrayListUnmanaged([]const u8).initBuffer(&cflags_buffer);
|
||||
switch (sokol.resolveSokolBackend(.auto, opts.target.result)) {
|
||||
.d3d11 => cflags.appendAssumeCapacity("-DSOKOL_D3D11"),
|
||||
.metal => cflags.appendAssumeCapacity("-DSOKOL_METAL"),
|
||||
.gl => cflags.appendAssumeCapacity("-DSOKOL_GLCORE"),
|
||||
.gles3 => cflags.appendAssumeCapacity("-DSOKOL_GLES3"),
|
||||
.wgpu => cflags.appendAssumeCapacity("-DSOKOL_WGPU"),
|
||||
else => @panic("unknown sokol backend"),
|
||||
}
|
||||
|
||||
mod.addIncludePath(dep_sokol_c.path("util"));
|
||||
mod.addCSourceFile(.{
|
||||
.file = b.path("src/fontstash/sokol_fontstash_impl.c"),
|
||||
.flags = cflags.items
|
||||
});
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// const sdl = b.dependency("sdl", Run game.{
|
||||
// .optimize = optimize,
|
||||
// .target = target,
|
||||
// .linkage = .static,
|
||||
// .default_target_config = !isWasm
|
||||
// });
|
||||
// mod_main.linkLibrary(sdl.artifact("SDL3"));
|
||||
// if (isWasm) {
|
||||
// // TODO: Define buid config for wasm
|
||||
// }
|
||||
|
||||
var options = b.addOptions();
|
||||
options.addOption(bool, "has_imgui", has_imgui);
|
||||
options.addOption(bool, "has_tracy", has_tracy);
|
||||
mod.addOptions("build_options", options);
|
||||
|
||||
return EngineModule{
|
||||
.root_module = mod,
|
||||
.dep_sokol = dep_sokol
|
||||
};
|
||||
}
|
||||
|
||||
fn buildPngToIconTool(
|
||||
b: *std.Build,
|
||||
target: std.Build.ResolvedTarget,
|
||||
optimize: std.builtin.OptimizeMode,
|
||||
) *std.Build.Step.Compile {
|
||||
const mod = b.createModule(.{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = b.path("tools/png-to-icon.zig"),
|
||||
});
|
||||
|
||||
const dep_stb = b.dependency("stb", .{});
|
||||
mod.addImport("stb_image", dep_stb.module("stb_image"));
|
||||
|
||||
return b.addExecutable(.{
|
||||
.name = "png-to-icon",
|
||||
.root_module = mod,
|
||||
});
|
||||
}
|
||||
|
||||
const BuildNativeOptions = struct {
|
||||
name: []const u8,
|
||||
root_module: *std.Build.Module,
|
||||
win32_has_console: ?bool = null,
|
||||
win32_png_icon: ?std.Build.LazyPath = null
|
||||
};
|
||||
|
||||
fn buildNative(b: *std.Build, opts: BuildNativeOptions) *std.Build.Step.Compile {
|
||||
const exe = b.addExecutable(.{
|
||||
.name = opts.name,
|
||||
.root_module = opts.root_module
|
||||
});
|
||||
|
||||
const target = opts.root_module.resolved_target.?;
|
||||
if (target.result.os.tag == .windows) {
|
||||
const optimize = opts.root_module.optimize.?;
|
||||
const has_console = opts.win32_has_console orelse (optimize == .Debug);
|
||||
exe.subsystem = if (has_console) .Console else .Windows;
|
||||
|
||||
if (opts.win32_png_icon) |win32_png_icon| {
|
||||
const png_to_icon_tool = buildPngToIconTool(b, b.graph.host, .ReleaseSafe);
|
||||
|
||||
const png_to_icon_step = b.addRunArtifact(png_to_icon_tool);
|
||||
png_to_icon_step.addFileArg(win32_png_icon);
|
||||
const icon_file = png_to_icon_step.addOutputFileArg("icon.ico");
|
||||
|
||||
const add_icon_step = AddExecutableIcon.init(exe, icon_file);
|
||||
exe.step.dependOn(&add_icon_step.step);
|
||||
}
|
||||
}
|
||||
|
||||
return exe;
|
||||
}
|
||||
|
||||
const BuildWasmOptions = struct {
|
||||
name: []const u8,
|
||||
root_module: *std.Build.Module,
|
||||
dep_sokol: *std.Build.Dependency,
|
||||
outer_builder: *std.Build
|
||||
};
|
||||
|
||||
fn buildWasm(b: *std.Build, opts: BuildWasmOptions) *std.Build.Step {
|
||||
opts.root_module.sanitize_c = .off;
|
||||
|
||||
// build the main file into a library, this is because the WASM 'exe'
|
||||
// needs to be linked in a separate build step with the Emscripten linker
|
||||
const main_lib = b.addLibrary(.{
|
||||
.name = opts.name,
|
||||
.root_module = opts.root_module,
|
||||
});
|
||||
|
||||
const dep_sokol = opts.dep_sokol;
|
||||
const dep_emsdk = dep_sokol.builder.dependency("emsdk", .{});
|
||||
|
||||
patchWasmIncludeDirs(
|
||||
opts.root_module,
|
||||
dep_emsdk.path("upstream/emscripten/cache/sysroot/include"),
|
||||
&(dep_sokol.artifact("sokol_clib").step)
|
||||
);
|
||||
|
||||
const link_step = sokol.emLinkStep(opts.outer_builder, .{
|
||||
.lib_main = main_lib,
|
||||
.target = opts.root_module.resolved_target.?,
|
||||
.optimize = opts.root_module.optimize.?,
|
||||
.emsdk = dep_emsdk,
|
||||
.use_webgl2 = true,
|
||||
.use_emmalloc = true,
|
||||
.use_filesystem = false,
|
||||
.shell_file_path = b.path("src/shell.html"),
|
||||
}) catch unreachable;
|
||||
|
||||
return &link_step.step;
|
||||
}
|
||||
|
||||
fn patchWasmIncludeDirs(
|
||||
module: *std.Build.Module,
|
||||
path: std.Build.LazyPath,
|
||||
depend_step: *std.Build.Step
|
||||
) void {
|
||||
if (module.link_libc != null and module.link_libc.?) {
|
||||
// need to inject the Emscripten system header include path into
|
||||
// the cimgui C library otherwise the C/C++ code won't find
|
||||
// C stdlib headers
|
||||
module.addSystemIncludePath(path);
|
||||
}
|
||||
|
||||
for (module.import_table.values()) |imported_module| {
|
||||
patchWasmIncludeDirs(imported_module, path, depend_step);
|
||||
}
|
||||
|
||||
for (module.link_objects.items) |link_object| {
|
||||
if (link_object != .other_step) {
|
||||
continue;
|
||||
}
|
||||
const lib = link_object.other_step;
|
||||
if (&lib.step == depend_step) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lib.root_module.link_libc != null and lib.root_module.link_libc.?) {
|
||||
// need to inject the Emscripten system header include path into
|
||||
// the cimgui C library otherwise the C/C++ code won't find
|
||||
// C stdlib headers
|
||||
lib.root_module.addSystemIncludePath(path);
|
||||
|
||||
// all C libraries need to depend on the sokol library, when building for
|
||||
// WASM this makes sure that the Emscripten SDK has been setup before
|
||||
// C compilation is attempted (since the sokol C library depends on the
|
||||
// Emscripten SDK setup step)
|
||||
lib.step.dependOn(depend_step);
|
||||
}
|
||||
patchWasmIncludeDirs(lib.root_module, path, depend_step);
|
||||
}
|
||||
}
|
||||
|
||||
const AddExecutableIcon = struct {
|
||||
obj: *std.Build.Step.Compile,
|
||||
step: std.Build.Step,
|
||||
icon_file: std.Build.LazyPath,
|
||||
resource_file: std.Build.LazyPath,
|
||||
|
||||
fn init(obj: *std.Build.Step.Compile, icon_file: std.Build.LazyPath) *AddExecutableIcon {
|
||||
const b = obj.step.owner;
|
||||
const self = b.allocator.create(AddExecutableIcon) catch @panic("OOM");
|
||||
|
||||
self.obj = obj;
|
||||
self.step = std.Build.Step.init(.{
|
||||
.id = .custom,
|
||||
.name = "add executable icon",
|
||||
.owner = b,
|
||||
.makeFn = make
|
||||
});
|
||||
|
||||
self.icon_file = icon_file;
|
||||
icon_file.addStepDependencies(&self.step);
|
||||
|
||||
const write_files = b.addWriteFiles();
|
||||
self.resource_file = write_files.add("resource-file.rc", "");
|
||||
self.step.dependOn(&write_files.step);
|
||||
|
||||
self.obj.addWin32ResourceFile(.{
|
||||
.file = self.resource_file,
|
||||
});
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
|
||||
const b = step.owner;
|
||||
const self: *AddExecutableIcon = @fieldParentPtr("step", step);
|
||||
|
||||
const resource_file = try std.fs.cwd().createFile(self.resource_file.getPath(b), .{ });
|
||||
defer resource_file.close();
|
||||
|
||||
const relative_icon_path = try std.fs.path.relative(
|
||||
b.allocator,
|
||||
self.resource_file.dirname().getPath(b),
|
||||
self.icon_file.getPath(b)
|
||||
);
|
||||
std.mem.replaceScalar(u8, relative_icon_path, '\\', '/');
|
||||
|
||||
try resource_file.writeAll("IDI_ICON ICON \"");
|
||||
try resource_file.writeAll(relative_icon_path);
|
||||
try resource_file.writeAll("\"");
|
||||
}
|
||||
};
|
||||
42
engine/build.zig.zon
Normal file
42
engine/build.zig.zon
Normal file
@ -0,0 +1,42 @@
|
||||
.{
|
||||
.name = .engine,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0xe8a81a8d6fb6f0fb,
|
||||
.minimum_zig_version = "0.15.2",
|
||||
.dependencies = .{
|
||||
.sokol = .{
|
||||
.url = "git+https://github.com/floooh/sokol-zig.git#aaf291ca2d3d1cedc05d65f5a1cacae0f53d934a",
|
||||
.hash = "sokol-0.1.0-pb1HK4iDNgCom5dkY66eUBm_bYBHEl8KWFDAiqwWgpEy",
|
||||
},
|
||||
.sokol_c = .{
|
||||
.url = "git+https://github.com/floooh/sokol.git#c66a1f04e6495d635c5e913335ab2308281e0492",
|
||||
.hash = "N-V-__8AAC3eYABB1DVLb4dkcEzq_xVeEZZugVfQ6DoNQBDN",
|
||||
},
|
||||
.fontstash_c = .{
|
||||
.url = "git+https://github.com/memononen/fontstash.git#b5ddc9741061343740d85d636d782ed3e07cf7be",
|
||||
.hash = "N-V-__8AAA9xHgAxdLYPmlNTy6qzv9IYqiIePEHQUOPWYQ_6",
|
||||
},
|
||||
.cimgui = .{
|
||||
.url = "git+https://github.com/floooh/dcimgui.git#33c99ef426b68030412b5a4b11487a23da9d4f13",
|
||||
.hash = "cimgui-0.1.0-44ClkQRJlABdFMKRqIG8KDD6jy1eQbgPO335NziPYjmL",
|
||||
.lazy = true,
|
||||
},
|
||||
.tracy = .{
|
||||
.url = "git+https://github.com/sagehane/zig-tracy.git#80933723efe9bf840fe749b0bfc0d610f1db1669",
|
||||
.hash = "zig_tracy-0.0.5-aOIqsX1tAACKaRRB-sraMLuNiMASXi_y-4FtRuw4cTpx",
|
||||
},
|
||||
.stb = .{
|
||||
.path = "./libs/stb",
|
||||
},
|
||||
// .sdl = .{
|
||||
// .url = "git+https://github.com/allyourcodebase/SDL3.git#f85824b0db782b7d01c60aaad8bcb537892394e8",
|
||||
// .hash = "sdl-0.0.0-i4QD0UuFqADRQysNyJ1OvCOZnq-clcVhq3BfPcBOf9zr",
|
||||
// },
|
||||
},
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"src",
|
||||
"tools",
|
||||
},
|
||||
}
|
||||
@ -23,6 +23,8 @@ pub const Font = fontstash.Font;
|
||||
|
||||
const GraphicsFrame = @import("./frame.zig").Graphics;
|
||||
|
||||
const Graphics = @This();
|
||||
|
||||
// TODO: Seems that there is a vertical jitter bug when resizing a window in OpenGL. Seems like a driver bug.
|
||||
// From other peoples research it seems that disabling vsync when a resize event occurs fixes it.
|
||||
// Maybe a patch for sokol could be made?
|
||||
@ -97,20 +99,16 @@ pub const Sprite = struct {
|
||||
uv: Rect,
|
||||
};
|
||||
|
||||
var gpa: std.mem.Allocator = undefined;
|
||||
main_pipeline: sgl.Pipeline,
|
||||
linear_sampler: sg.Sampler,
|
||||
nearest_sampler: sg.Sampler,
|
||||
font_context: fontstash.Context,
|
||||
textures: std.ArrayList(Texture) = .empty,
|
||||
|
||||
var main_pipeline: sgl.Pipeline = .{};
|
||||
var linear_sampler: sg.Sampler = .{};
|
||||
var nearest_sampler: sg.Sampler = .{};
|
||||
var font_context: fontstash.Context = undefined;
|
||||
var textures: std.ArrayList(Texture) = .empty;
|
||||
|
||||
var scale_stack_buffer: [32]Vec2 = undefined;
|
||||
var scale_stack: std.ArrayList(Vec2) = .empty;
|
||||
|
||||
pub fn init(options: Options) !void {
|
||||
gpa = options.allocator;
|
||||
scale_stack_buffer: [32]Vec2 = undefined,
|
||||
scale_stack: std.ArrayList(Vec2) = .empty,
|
||||
|
||||
pub fn init(self: *Graphics, options: Options) !void {
|
||||
sg.setup(.{
|
||||
.logger = options.logger,
|
||||
.environment = sglue.environment(),
|
||||
@ -123,7 +121,7 @@ pub fn init(options: Options) !void {
|
||||
}
|
||||
});
|
||||
|
||||
main_pipeline = sgl.makePipeline(.{
|
||||
const main_pipeline = sgl.makePipeline(.{
|
||||
.colors = init: {
|
||||
var colors: [8]sg.ColorTargetState = @splat(.{});
|
||||
colors[0] = .{
|
||||
@ -158,14 +156,14 @@ pub fn init(options: Options) !void {
|
||||
imgui.addFont(imgui_font.ttf_data, imgui_font.size);
|
||||
}
|
||||
|
||||
linear_sampler = sg.makeSampler(.{
|
||||
const linear_sampler = sg.makeSampler(.{
|
||||
.min_filter = .LINEAR,
|
||||
.mag_filter = .LINEAR,
|
||||
.mipmap_filter = .LINEAR,
|
||||
.label = "linear-sampler",
|
||||
});
|
||||
|
||||
nearest_sampler = sg.makeSampler(.{
|
||||
const nearest_sampler = sg.makeSampler(.{
|
||||
.min_filter = .NEAREST,
|
||||
.mag_filter = .NEAREST,
|
||||
.mipmap_filter = .NEAREST,
|
||||
@ -175,31 +173,38 @@ pub fn init(options: Options) !void {
|
||||
const dpi_scale = sapp.dpiScale();
|
||||
const atlas_size = 512;
|
||||
const atlas_dim = std.math.ceilPowerOfTwoAssert(u32, @intFromFloat(atlas_size * dpi_scale));
|
||||
font_context = try fontstash.Context.init(.{
|
||||
const font_context = try fontstash.Context.init(.{
|
||||
.width = @intCast(atlas_dim),
|
||||
.height = @intCast(atlas_dim),
|
||||
});
|
||||
|
||||
self.* = Graphics{
|
||||
.main_pipeline = main_pipeline,
|
||||
.linear_sampler = linear_sampler,
|
||||
.nearest_sampler = nearest_sampler,
|
||||
.font_context = font_context,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit() void {
|
||||
textures.deinit(gpa);
|
||||
pub fn deinit(self: *Graphics, gpa: std.mem.Allocator) void {
|
||||
self.textures.deinit(gpa);
|
||||
imgui.shutdown();
|
||||
font_context.deinit();
|
||||
self.font_context.deinit();
|
||||
sgl.shutdown();
|
||||
sg.shutdown();
|
||||
}
|
||||
|
||||
pub fn drawCommand(command: Command) void {
|
||||
pub fn drawCommand(self: *Graphics, command: Command) void {
|
||||
switch(command) {
|
||||
.push_transformation => |opts| {
|
||||
pushTransform(opts.translation, opts.scale);
|
||||
self.pushTransform(opts.translation, opts.scale);
|
||||
// font_resolution_scale = font_resolution_scale.multiply(opts.scale);
|
||||
},
|
||||
.pop_transformation => {
|
||||
popTransform();
|
||||
self.popTransform();
|
||||
},
|
||||
.draw_rectangle => |opts| {
|
||||
drawRectangle(opts);
|
||||
self.drawRectangle(opts);
|
||||
},
|
||||
.set_scissor => |opts| {
|
||||
sgl.scissorRectf(
|
||||
@ -219,25 +224,25 @@ pub fn drawCommand(command: Command) void {
|
||||
);
|
||||
},
|
||||
.draw_text => |opts| {
|
||||
const font_resolution_scale = scale_stack.getLast();
|
||||
const font_resolution_scale = self.scale_stack.getLast();
|
||||
|
||||
sgl.pushMatrix();
|
||||
defer sgl.popMatrix();
|
||||
|
||||
sgl.scale(1/font_resolution_scale.x, 1/font_resolution_scale.y, 1);
|
||||
|
||||
font_context.setFont(opts.font);
|
||||
font_context.setSize(opts.size * font_resolution_scale.y);
|
||||
font_context.setAlign(.{ .x = .left, .y = .top });
|
||||
font_context.setSpacing(0);
|
||||
self.font_context.setFont(opts.font);
|
||||
self.font_context.setSize(opts.size * font_resolution_scale.y);
|
||||
self.font_context.setAlign(.{ .x = .left, .y = .top });
|
||||
self.font_context.setSpacing(0);
|
||||
|
||||
const r: u8 = @intFromFloat(opts.color.x * 255);
|
||||
const g: u8 = @intFromFloat(opts.color.y * 255);
|
||||
const b: u8 = @intFromFloat(opts.color.z * 255);
|
||||
const a: u8 = @intFromFloat(opts.color.w * 255);
|
||||
const color: u32 = r | (@as(u32, g) << 8) | (@as(u32, b) << 16) | (@as(u32, a) << 24);
|
||||
font_context.setColor(color);
|
||||
font_context.drawText(
|
||||
self.font_context.setColor(color);
|
||||
self.font_context.drawText(
|
||||
opts.pos.x * font_resolution_scale.x,
|
||||
opts.pos.y * font_resolution_scale.y,
|
||||
opts.text
|
||||
@ -246,13 +251,13 @@ pub fn drawCommand(command: Command) void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drawCommands(commands: []const Command) void {
|
||||
pub fn drawCommands(self: *Graphics, commands: []const Command) void {
|
||||
for (commands) |command| {
|
||||
drawCommand(command);
|
||||
self.drawCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn beginFrame() void {
|
||||
pub fn beginFrame(self: *Graphics) void {
|
||||
const zone = tracy.initZone(@src(), .{ });
|
||||
defer zone.deinit();
|
||||
|
||||
@ -263,17 +268,17 @@ pub fn beginFrame() void {
|
||||
.dpi_scale = sapp.dpiScale()
|
||||
});
|
||||
|
||||
scale_stack = .initBuffer(&scale_stack_buffer);
|
||||
scale_stack.appendAssumeCapacity(.init(1, 1));
|
||||
self.scale_stack = .initBuffer(&self.scale_stack_buffer);
|
||||
self.scale_stack.appendAssumeCapacity(.init(1, 1));
|
||||
|
||||
font_context.clearState();
|
||||
self.font_context.clearState();
|
||||
sgl.defaults();
|
||||
sgl.matrixModeProjection();
|
||||
sgl.ortho(0, sapp.widthf(), sapp.heightf(), 0, -1, 1);
|
||||
sgl.loadPipeline(main_pipeline);
|
||||
sgl.loadPipeline(self.main_pipeline);
|
||||
}
|
||||
|
||||
pub fn endFrame(clear_color: Vec4) void {
|
||||
pub fn endFrame(self: *Graphics, clear_color: Vec4) void {
|
||||
const zone = tracy.initZone(@src(), .{ });
|
||||
defer zone.deinit();
|
||||
|
||||
@ -289,7 +294,7 @@ pub fn endFrame(clear_color: Vec4) void {
|
||||
}
|
||||
};
|
||||
|
||||
font_context.flush();
|
||||
self.font_context.flush();
|
||||
|
||||
{
|
||||
sg.beginPass(.{
|
||||
@ -305,17 +310,17 @@ pub fn endFrame(clear_color: Vec4) void {
|
||||
sg.commit();
|
||||
}
|
||||
|
||||
fn pushTransform(translation: Vec2, scale: Vec2) void {
|
||||
fn pushTransform(self: *Graphics, translation: Vec2, scale: Vec2) void {
|
||||
sgl.pushMatrix();
|
||||
sgl.translate(translation.x, translation.y, 0);
|
||||
sgl.scale(scale.x, scale.y, 1);
|
||||
|
||||
scale_stack.appendAssumeCapacity(scale_stack.getLast().multiply(scale));
|
||||
self.scale_stack.appendAssumeCapacity(self.scale_stack.getLast().multiply(scale));
|
||||
}
|
||||
|
||||
fn popTransform() void {
|
||||
fn popTransform(self: *Graphics) void {
|
||||
sgl.popMatrix();
|
||||
_ = scale_stack.pop().?;
|
||||
_ = self.scale_stack.pop().?;
|
||||
}
|
||||
|
||||
const Vertex = struct {
|
||||
@ -323,13 +328,13 @@ const Vertex = struct {
|
||||
uv: Vec2
|
||||
};
|
||||
|
||||
fn drawQuad(quad: [4]Vertex, color: Vec4, texture_id: TextureId) void {
|
||||
fn drawQuad(self: *Graphics, quad: [4]Vertex, color: Vec4, texture_id: TextureId) void {
|
||||
sgl.enableTexture();
|
||||
defer sgl.disableTexture();
|
||||
|
||||
const view = textures.items[@intFromEnum(texture_id)].view;
|
||||
const view = self.textures.items[@intFromEnum(texture_id)].view;
|
||||
// TODO: Make sampler configurable
|
||||
sgl.texture(view, nearest_sampler);
|
||||
sgl.texture(view, self.nearest_sampler);
|
||||
|
||||
sgl.beginQuads();
|
||||
defer sgl.end();
|
||||
@ -352,7 +357,7 @@ fn drawQuadNoUVs(quad: [4]Vec2, color: Vec4) void {
|
||||
}
|
||||
}
|
||||
|
||||
fn drawRectangle(opts: Command.DrawRectangle) void {
|
||||
fn drawRectangle(self: *Graphics, opts: Command.DrawRectangle) void {
|
||||
const pos = opts.rect.pos;
|
||||
const size = opts.rect.size;
|
||||
|
||||
@ -381,7 +386,7 @@ fn drawRectangle(opts: Command.DrawRectangle) void {
|
||||
.uv = .init(uv.left(), uv.bottom())
|
||||
}
|
||||
};
|
||||
drawQuad(quad, opts.color, sprite.texture);
|
||||
self.drawQuad(quad, opts.color, sprite.texture);
|
||||
} else {
|
||||
const quad = .{
|
||||
pos.add(top_left),
|
||||
@ -407,8 +412,8 @@ fn drawLine(from: Vec2, to: Vec2, color: Vec4, width: f32) void {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn addFont(name: [*c]const u8, data: []const u8) !Font.Id {
|
||||
return try font_context.addFont(name, data);
|
||||
fn addFont(self: *Graphics, name: [*c]const u8, data: []const u8) !Font.Id {
|
||||
return try self.font_context.addFont(name, data);
|
||||
}
|
||||
|
||||
fn makeView(image: sg.Image) !sg.View {
|
||||
@ -453,7 +458,7 @@ fn makeImageWithMipMaps(mipmaps: []const Texture.Data) !sg.Image {
|
||||
return image;
|
||||
}
|
||||
|
||||
pub fn addTexture(mipmaps: []const Texture.Data) !TextureId {
|
||||
pub fn addTexture(self: *Graphics, gpa: std.mem.Allocator, mipmaps: []const Texture.Data) !TextureId {
|
||||
const image = try makeImageWithMipMaps(mipmaps);
|
||||
errdefer sg.deallocImage(image);
|
||||
|
||||
@ -461,8 +466,8 @@ pub fn addTexture(mipmaps: []const Texture.Data) !TextureId {
|
||||
errdefer sg.deallocView(view);
|
||||
|
||||
assert(mipmaps.len > 0);
|
||||
const index = textures.items.len;
|
||||
try textures.append(gpa, .{
|
||||
const index = self.textures.items.len;
|
||||
try self.textures.append(gpa, .{
|
||||
.image = image,
|
||||
.view = view,
|
||||
.info = .{
|
||||
@ -474,7 +479,7 @@ pub fn addTexture(mipmaps: []const Texture.Data) !TextureId {
|
||||
return @enumFromInt(index);
|
||||
}
|
||||
|
||||
pub fn getTextureInfo(id: TextureId) TextureInfo {
|
||||
const texture = textures.items[@intFromEnum(id)];
|
||||
pub fn getTextureInfo(self: *Graphics, id: TextureId) TextureInfo {
|
||||
const texture = self.textures.items[@intFromEnum(id)];
|
||||
return texture.info;
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
const Engine = @import("./engine/root.zig");
|
||||
const Engine = @import("engine");
|
||||
|
||||
var engine: Engine = undefined;
|
||||
|
||||
@ -23,20 +23,34 @@ const STBImage = @import("stb_image");
|
||||
|
||||
const Gfx = Graphics;
|
||||
|
||||
const Game = @import("../game.zig");
|
||||
const Assets = @import("../assets.zig");
|
||||
const GameCallbacks = struct {
|
||||
init: *const fn(opts: *const Engine.InitOptions) callconv(.c) void,
|
||||
tick: *const fn(frame: *Frame) callconv(.c) void,
|
||||
deinit: *const fn() callconv(.c) void,
|
||||
debug: *const fn() callconv(.c) void,
|
||||
};
|
||||
|
||||
extern fn init(opts: *const Engine.InitOptions) void;
|
||||
extern fn tick(frame: *Frame) void;
|
||||
extern fn deinit() void;
|
||||
extern fn debug() void;
|
||||
|
||||
const Engine = @This();
|
||||
|
||||
pub const Nanoseconds = u64;
|
||||
|
||||
pub const InitOptions = struct {
|
||||
gpa: std.mem.Allocator,
|
||||
seed: u64
|
||||
};
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
started_at: std.time.Instant,
|
||||
mouse_inside: bool,
|
||||
last_frame_at: Nanoseconds,
|
||||
graphics: Graphics,
|
||||
game_callbacks: GameCallbacks,
|
||||
|
||||
game: Game,
|
||||
assets: Assets,
|
||||
frame: Frame,
|
||||
|
||||
canvas_size: ?Vec2 = null,
|
||||
@ -53,9 +67,14 @@ pub fn run(self: *Engine, opts: RunOptions) !void {
|
||||
.started_at = std.time.Instant.now() catch @panic("Instant.now() unsupported"),
|
||||
.mouse_inside = false,
|
||||
.last_frame_at = 0,
|
||||
.assets = undefined,
|
||||
.game = undefined,
|
||||
.frame = undefined
|
||||
.frame = undefined,
|
||||
.graphics = undefined,
|
||||
.game_callbacks = .{
|
||||
.init = init,
|
||||
.deinit = deinit,
|
||||
.tick = tick,
|
||||
.debug = debug,
|
||||
}
|
||||
};
|
||||
|
||||
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
|
||||
@ -84,19 +103,20 @@ pub fn run(self: *Engine, opts: RunOptions) !void {
|
||||
}
|
||||
|
||||
// TODO: Don't hard code icon path, allow changing through options
|
||||
var icon_data = try STBImage.load(@embedFile("../assets/icon.png"));
|
||||
defer icon_data.deinit();
|
||||
// var icon_data = try STBImage.load(@embedFile("../assets/icon.png"));
|
||||
// defer icon_data.deinit();
|
||||
|
||||
var icon: sapp.IconDesc = .{};
|
||||
icon.sokol_default = false;
|
||||
icon.images[0] = .{
|
||||
.width = @intCast(icon_data.width),
|
||||
.height = @intCast(icon_data.height),
|
||||
.pixels = .{
|
||||
.ptr = icon_data.rgba8_pixels,
|
||||
.size = icon_data.width * icon_data.height * 4
|
||||
}
|
||||
};
|
||||
icon.sokol_default = true;
|
||||
// TODO:
|
||||
// icon.images[0] = .{
|
||||
// .width = @intCast(icon_data.width),
|
||||
// .height = @intCast(icon_data.height),
|
||||
// .pixels = .{
|
||||
// .ptr = icon_data.rgba8_pixels,
|
||||
// .size = icon_data.width * icon_data.height * 4
|
||||
// }
|
||||
// };
|
||||
|
||||
sapp.run(.{
|
||||
.init_userdata_cb = sokolInitCallback,
|
||||
@ -119,12 +139,15 @@ fn sokolInit(self: *Engine) !void {
|
||||
const zone = tracy.initZone(@src(), .{ });
|
||||
defer zone.deinit();
|
||||
|
||||
try Gfx.init(.{
|
||||
log.debug("Init", .{});
|
||||
|
||||
try self.graphics.init(.{
|
||||
.allocator = self.allocator,
|
||||
.logger = .{ .func = sokolLogCallback },
|
||||
.imgui_font = .{
|
||||
.ttf_data = @embedFile("../assets/roboto-font/Roboto-Regular.ttf"),
|
||||
}
|
||||
// TODO:
|
||||
// .imgui_font = .{
|
||||
// .ttf_data = @embedFile("../assets/roboto-font/Roboto-Regular.ttf"),
|
||||
// }
|
||||
});
|
||||
|
||||
try Audio.init(.{
|
||||
@ -134,8 +157,11 @@ fn sokolInit(self: *Engine) !void {
|
||||
|
||||
const seed: u64 = @bitCast(std.time.milliTimestamp());
|
||||
|
||||
self.assets = try Assets.init(self.allocator);
|
||||
self.game = try Game.init(self.allocator, seed, &self.assets);
|
||||
const opts = InitOptions{
|
||||
.gpa = self.allocator,
|
||||
.seed = seed,
|
||||
};
|
||||
self.game_callbacks.init(&opts);
|
||||
}
|
||||
|
||||
fn sokolCleanup(self: *Engine) void {
|
||||
@ -144,9 +170,8 @@ fn sokolCleanup(self: *Engine) void {
|
||||
|
||||
self.frame.deinit();
|
||||
Audio.deinit();
|
||||
self.game.deinit();
|
||||
self.assets.deinit(self.allocator);
|
||||
Gfx.deinit();
|
||||
self.game_callbacks.deinit();
|
||||
self.graphics.deinit(self.allocator);
|
||||
}
|
||||
|
||||
fn sokolFrame(self: *Engine) !void {
|
||||
@ -193,7 +218,7 @@ fn sokolFrame(self: *Engine) !void {
|
||||
frame.dt_ns = time_passed - self.last_frame_at;
|
||||
frame.hide_cursor = false;
|
||||
|
||||
try self.game.tick(&self.frame);
|
||||
self.game_callbacks.tick(&self.frame);
|
||||
|
||||
frame.input.keyboard.pressed = .initEmpty();
|
||||
frame.input.keyboard.released = .initEmpty();
|
||||
@ -220,14 +245,13 @@ fn sokolFrame(self: *Engine) !void {
|
||||
self.canvas_size = self.frame.graphics.canvas_size;
|
||||
|
||||
{
|
||||
Gfx.beginFrame();
|
||||
defer Gfx.endFrame(frame.graphics.clear_color);
|
||||
self.graphics.beginFrame();
|
||||
defer self.graphics.endFrame(frame.graphics.clear_color);
|
||||
|
||||
Gfx.drawCommands(frame.graphics.commands.items);
|
||||
self.graphics.drawCommands(frame.graphics.commands.items);
|
||||
|
||||
if (frame.show_debug) {
|
||||
try self.game.debug();
|
||||
try showDebugWindow(&self.frame);
|
||||
try self.showDebugWindow(&self.frame);
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,23 +264,34 @@ fn sokolFrame(self: *Engine) !void {
|
||||
}
|
||||
}
|
||||
|
||||
fn showDebugWindow(frame: *Frame) !void {
|
||||
fn showDebugWindow(self: *Engine, frame: *Frame) !void {
|
||||
if (!imgui.beginWindow(.{
|
||||
.name = "Engine",
|
||||
.pos = Vec2.init(240, 20),
|
||||
.name = "Debug",
|
||||
.pos = Vec2.init(20, 20),
|
||||
.size = Vec2.init(200, 200),
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
defer imgui.endWindow();
|
||||
|
||||
imgui.textFmt("Draw commands: {}\n", .{
|
||||
frame.graphics.commands.items.len,
|
||||
});
|
||||
imgui.textFmt("Audio instances: {}/{}\n", .{
|
||||
Audio.mixer.instances.items.len,
|
||||
Audio.mixer.instances.capacity
|
||||
});
|
||||
if (imgui.beginTabBar("debug")) {
|
||||
defer imgui.endTabBar();
|
||||
|
||||
if (imgui.beginTabItem("Game")) {
|
||||
defer imgui.endTabItem();
|
||||
self.game_callbacks.debug();
|
||||
}
|
||||
if (imgui.beginTabItem("Engine")) {
|
||||
defer imgui.endTabItem();
|
||||
imgui.textFmt("Draw commands: {}\n", .{
|
||||
frame.graphics.commands.items.len,
|
||||
});
|
||||
imgui.textFmt("Audio instances: {}/{}\n", .{
|
||||
Audio.mixer.instances.items.len,
|
||||
Audio.mixer.instances.capacity
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sokolEvent(self: *Engine, e_ptr: [*c]const sapp.Event) !bool {
|
||||
@ -1,44 +0,0 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Math = @import("./engine/math.zig");
|
||||
const Engine = @import("./engine/root.zig");
|
||||
const Gfx = Engine.Graphics;
|
||||
const Audio = Engine.Audio;
|
||||
|
||||
const Assets = @This();
|
||||
|
||||
const FontName = enum {
|
||||
regular,
|
||||
bold,
|
||||
italic,
|
||||
|
||||
const EnumArray = std.EnumArray(FontName, Gfx.Font.Id);
|
||||
};
|
||||
|
||||
font_id: FontName.EnumArray,
|
||||
|
||||
wood01: Audio.Data.Id,
|
||||
|
||||
pub fn init(gpa: std.mem.Allocator) !Assets {
|
||||
_ = gpa; // autofix
|
||||
const font_id_array: FontName.EnumArray = .init(.{
|
||||
.regular = try Gfx.addFont("regular", @embedFile("assets/roboto-font/Roboto-Regular.ttf")),
|
||||
.bold = try Gfx.addFont("bold", @embedFile("assets/roboto-font/Roboto-Bold.ttf")),
|
||||
.italic = try Gfx.addFont("italic", @embedFile("assets/roboto-font/Roboto-Italic.ttf")),
|
||||
});
|
||||
|
||||
const wood01 = try Audio.load(.{
|
||||
.format = .vorbis,
|
||||
.data = @embedFile("assets/wood01.ogg"),
|
||||
});
|
||||
|
||||
return Assets{
|
||||
.font_id = font_id_array,
|
||||
.wood01 = wood01
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Assets, gpa: std.mem.Allocator) void {
|
||||
_ = self; // autofix
|
||||
_ = gpa; // autofix
|
||||
}
|
||||
97
src/game.zig
97
src/game.zig
@ -2,9 +2,7 @@ const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const Assets = @import("./assets.zig");
|
||||
|
||||
const Engine = @import("./engine/root.zig");
|
||||
const Engine = @import("engine");
|
||||
const imgui = Engine.imgui;
|
||||
const Vec2 = Engine.Vec2;
|
||||
const rgb = Engine.Math.rgb;
|
||||
@ -13,25 +11,55 @@ const Audio = Engine.Audio;
|
||||
|
||||
const Game = @This();
|
||||
|
||||
gpa: Allocator,
|
||||
assets: *Assets,
|
||||
const FontName = enum {
|
||||
regular,
|
||||
bold,
|
||||
italic,
|
||||
|
||||
player: Vec2,
|
||||
const EnumArray = std.EnumArray(FontName, Gfx.Font.Id);
|
||||
};
|
||||
|
||||
pub fn init(gpa: Allocator, seed: u64, assets: *Assets) !Game {
|
||||
_ = seed; // autofix
|
||||
return Game{
|
||||
const State = struct {
|
||||
gpa: Allocator,
|
||||
player: Vec2,
|
||||
|
||||
// font_id: FontName.EnumArray,
|
||||
// wood01: Audio.Data.Id,
|
||||
};
|
||||
|
||||
var g_state: *State = undefined;
|
||||
|
||||
pub export fn init(opts: *const Engine.InitOptions) void {
|
||||
const gpa = opts.gpa;
|
||||
|
||||
// const font_id_array: FontName.EnumArray = .init(.{
|
||||
// .regular = try Gfx.addFont("regular", @embedFile("assets/roboto-font/Roboto-Regular.ttf")),
|
||||
// .bold = try Gfx.addFont("bold", @embedFile("assets/roboto-font/Roboto-Bold.ttf")),
|
||||
// .italic = try Gfx.addFont("italic", @embedFile("assets/roboto-font/Roboto-Italic.ttf")),
|
||||
// });
|
||||
|
||||
// const wood01 = try Audio.load(.{
|
||||
// .format = .vorbis,
|
||||
// .data = @embedFile("assets/wood01.ogg"),
|
||||
// });
|
||||
|
||||
g_state = gpa.create(State) catch @panic("OOM");
|
||||
g_state.* = State{
|
||||
.gpa = gpa,
|
||||
.assets = assets,
|
||||
.player = .init(50, 50)
|
||||
.player = .init(50, 50),
|
||||
// .font_id = font_id_array,
|
||||
// .wood01 = wood01
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Game) void {
|
||||
_ = self; // autofix
|
||||
pub export fn deinit() void {
|
||||
const gpa = g_state.gpa;
|
||||
|
||||
gpa.destroy(g_state);
|
||||
g_state = undefined;
|
||||
}
|
||||
|
||||
pub fn tick(self: *Game, frame: *Engine.Frame) !void {
|
||||
pub export fn tick(frame: *Engine.Frame) void {
|
||||
const dt = frame.deltaTime();
|
||||
|
||||
const canvas_size = Vec2.init(100, 100);
|
||||
@ -57,15 +85,13 @@ pub fn tick(self: *Game, frame: *Engine.Frame) !void {
|
||||
dir = dir.normalized();
|
||||
|
||||
if (dir.x != 0 or dir.y != 0) {
|
||||
frame.playAudio(.{
|
||||
.id = self.assets.wood01,
|
||||
.volume = 0.1
|
||||
});
|
||||
// frame.playAudio(.{
|
||||
// .id = g_state.wood01,
|
||||
// .volume = 0.1
|
||||
// });
|
||||
}
|
||||
|
||||
self.player = self.player.add(dir.multiplyScalar(50 * dt));
|
||||
|
||||
const regular_font = self.assets.font_id.get(.regular);
|
||||
g_state.player = g_state.player.add(dir.multiplyScalar(50 * dt));
|
||||
|
||||
frame.drawRectangle(.{
|
||||
.rect = .{ .pos = .init(0, 0), .size = canvas_size },
|
||||
@ -74,35 +100,22 @@ pub fn tick(self: *Game, frame: *Engine.Frame) !void {
|
||||
const size = Vec2.init(20, 20);
|
||||
frame.drawRectangle(.{
|
||||
.rect = .{
|
||||
.pos = self.player.sub(size.divideScalar(2)),
|
||||
.pos = g_state.player.sub(size.divideScalar(2)),
|
||||
.size = size
|
||||
},
|
||||
.color = rgb(200, 20, 20)
|
||||
});
|
||||
if (dir.x != 0 or dir.y != 0) {
|
||||
frame.drawRectanglOutline(self.player.sub(size.divideScalar(2)), size, rgb(20, 200, 20), 3);
|
||||
frame.drawRectanglOutline(g_state.player.sub(size.divideScalar(2)), size, rgb(20, 200, 20), 3);
|
||||
}
|
||||
|
||||
frame.drawText(self.player, "Player", .{
|
||||
.font = regular_font,
|
||||
.size = 10
|
||||
});
|
||||
// const regular_font = g_state.font_id.get(.regular);
|
||||
// frame.drawText(g_state.player, "Player", .{
|
||||
// .font = regular_font,
|
||||
// .size = 10
|
||||
// });
|
||||
}
|
||||
|
||||
pub fn debug(self: *Game) !void {
|
||||
_ = self; // autofix
|
||||
if (!imgui.beginWindow(.{
|
||||
.name = "Debug",
|
||||
.pos = Vec2.init(20, 20),
|
||||
.size = Vec2.init(400, 200),
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
defer imgui.endWindow();
|
||||
|
||||
pub export fn debug() void {
|
||||
imgui.text("Hello World!\n");
|
||||
imgui.textFmt("Audio: {}/{}\n", .{
|
||||
Audio.mixer.instances.items.len,
|
||||
Audio.mixer.instances.capacity
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user