switch from capy to sokol + imgui

This commit is contained in:
Fabien Freling 2024-07-04 15:09:23 +02:00
parent 10f3c25f80
commit 683756900c
6 changed files with 252 additions and 44 deletions

66
3rd-party/cimgui/build.zig vendored Normal file
View file

@ -0,0 +1,66 @@
const std = @import("std");
const builtin = @import("builtin");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const dep_cimgui = b.dependency("cimgui", .{});
const dep_imgui = b.dependency("imgui", .{});
// create file tree for cimgui and imgui
const wf = b.addNamedWriteFiles("cimgui");
_ = wf.addCopyDirectory(dep_cimgui.path(""), "", .{});
_ = wf.addCopyDirectory(dep_imgui.path(""), "imgui", .{});
const root = wf.getDirectory();
// build cimgui as C/C++ library
const lib_cimgui = b.addStaticLibrary(.{
.name = "cimgui_clib",
.target = target,
.optimize = optimize,
.link_libc = true,
});
lib_cimgui.linkLibCpp();
lib_cimgui.addCSourceFiles(.{
.root = root,
.files = &.{
b.pathJoin(&.{"cimgui.cpp"}),
b.pathJoin(&.{ "imgui", "imgui.cpp" }),
b.pathJoin(&.{ "imgui", "imgui_widgets.cpp" }),
b.pathJoin(&.{ "imgui", "imgui_draw.cpp" }),
b.pathJoin(&.{ "imgui", "imgui_tables.cpp" }),
b.pathJoin(&.{ "imgui", "imgui_demo.cpp" }),
},
});
lib_cimgui.addIncludePath(root);
// make cimgui available as artifact, this then allows to inject
// the Emscripten include path in another build.zig
b.installArtifact(lib_cimgui);
// lib compilation depends on file tree
lib_cimgui.step.dependOn(&wf.step);
// translate-c the cimgui.h file
// NOTE: always run this with the host target, that way we don't need to inject
// the Emscripten SDK include path into the translate-C step when building for WASM
const cimgui_h = dep_cimgui.path("cimgui.h");
const translateC = b.addTranslateC(.{
.root_source_file = cimgui_h,
.target = b.host,
.optimize = optimize,
});
translateC.defineCMacroRaw("CIMGUI_DEFINE_ENUMS_AND_STRUCTS=\"\"");
const entrypoint = translateC.getOutput();
// build cimgui as a module with the header file as the entrypoint
const mod_cimgui = b.addModule("cimgui", .{
.root_source_file = entrypoint,
.target = target,
.optimize = optimize,
.link_libc = true,
.link_libcpp = true,
});
mod_cimgui.linkLibrary(lib_cimgui);
}

18
3rd-party/cimgui/build.zig.zon vendored Normal file
View file

@ -0,0 +1,18 @@
.{
.name = "cimgui",
.version = "0.0.0",
.dependencies = .{
.cimgui = .{
.url = "git+https://github.com/cimgui/cimgui.git#1.90.7",
.hash = "1220d925a8374fbc3a21a5b46025fb867672d85b4099d2151bed607618e08b0cd71c",
},
.imgui = .{
.url = "git+https://github.com/ocornut/imgui.git#v1.90.7",
.hash = "122072b125179c7cbdbbee8fa81d22a1050a950ad61cfeefee8dc0dca5423b5d05b9",
},
},
.paths = .{
"",
},
}

102
build.zig
View file

@ -1,30 +1,90 @@
const std = @import("std");
const capy = @import("capy");
const Build = std.Build;
const OptimizeMode = std.builtin.OptimizeMode;
const ResolvedTarget = Build.ResolvedTarget;
const Dependency = Build.Dependency;
const sokol = @import("sokol");
pub fn build(b: *std.Build) !void {
pub fn build(b: *Build) !void {
const target = b.standardTargetOptions(.{});
const mode = b.standardOptimizeOption(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "doggo",
.root_source_file = .{ .path = "src/main.zig" },
// note that the sokol dependency is built with `.with_imgui_sokol = true`
const dep_sokol = b.dependency("sokol", .{
.target = target,
.optimize = mode,
.optimize = optimize,
.with_sokol_imgui = true,
});
const dep_cimgui = b.dependency("cimgui", .{
.target = target,
.optimize = optimize,
});
b.installArtifact(exe);
// inject the cimgui header search path into the sokol C library compile step
const cimgui_root = dep_cimgui.namedWriteFiles("cimgui").getDirectory();
dep_sokol.artifact("sokol_clib").addIncludePath(cimgui_root);
const run_cmd = try capy.install(exe, .{ .args = b.args });
const run_step = b.step("run", "Run the app");
run_step.dependOn(run_cmd);
const exe_tests = b.addTest(.{
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = mode,
});
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&exe_tests.step);
// from here on different handling for native vs wasm builds
if (target.result.isWasm()) {
try buildWasm(b, target, optimize, dep_sokol, dep_cimgui);
} else {
try buildNative(b, target, optimize, dep_sokol, dep_cimgui);
}
}
fn buildNative(b: *Build, target: ResolvedTarget, optimize: OptimizeMode, dep_sokol: *Dependency, dep_cimgui: *Dependency) !void {
const demo = b.addExecutable(.{
.name = "demo",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
demo.root_module.addImport("sokol", dep_sokol.module("sokol"));
demo.root_module.addImport("cimgui", dep_cimgui.module("cimgui"));
b.installArtifact(demo);
b.step("run", "Run demo").dependOn(&b.addRunArtifact(demo).step);
}
fn buildWasm(b: *Build, target: ResolvedTarget, optimize: OptimizeMode, dep_sokol: *Dependency, dep_cimgui: *Dependency) !void {
// 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 demo = b.addStaticLibrary(.{
.name = "demo",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
demo.root_module.addImport("sokol", dep_sokol.module("sokol"));
demo.root_module.addImport("cimgui", dep_cimgui.module("cimgui"));
// get the Emscripten SDK dependency from the sokol dependency
const dep_emsdk = dep_sokol.builder.dependency("emsdk", .{});
// 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
const emsdk_incl_path = dep_emsdk.path("upstream/emscripten/cache/sysroot/include");
dep_cimgui.artifact("cimgui_clib").addSystemIncludePath(emsdk_incl_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)
dep_cimgui.artifact("cimgui_clib").step.dependOn(&dep_sokol.artifact("sokol_clib").step);
// create a build step which invokes the Emscripten linker
const link_step = try sokol.emLinkStep(b, .{
.lib_main = demo,
.target = target,
.optimize = optimize,
.emsdk = dep_emsdk,
.use_webgl2 = true,
.use_emmalloc = true,
.use_filesystem = false,
.shell_file_path = dep_sokol.path("src/sokol/web/shell.html").getPath(b),
});
// ...and a special run step to start the web build output via 'emrun'
const run = sokol.emRunStep(b, .{ .name = "demo", .emsdk = dep_emsdk });
run.step.dependOn(&link_step.step);
b.step("run", "Run demo").dependOn(&run.step);
}

View file

@ -7,13 +7,12 @@
"src",
},
.dependencies = .{
.capy = .{
.url = "https://github.com/ffreling/capy/archive/refs/heads/zig-0.12.0.tar.gz",
.hash = "12207fd2f0d02eb929d1e8a05843de4dc3f4b362055e9e47a014fb6b806a592d9808",
.sokol = .{
.url = "git+https://github.com/floooh/sokol-zig.git#d9f3ef983b7021417cc91bd5788cd925ca06aa83",
.hash = "12200a472b4f1a3a6db1002f39f10ae0cccf58ada45bf25ddc5fd48384dd187f55ea",
},
.zigimg = .{
.url = "https://github.com/zigimg/zigimg/archive/8873f29fc449e1b63400e9f4ad86d3c76204f962.tar.gz",
.hash = "122019f6439545235af116d0d8eb81fde1ff05fdb070da57c723772c557f84c5bf39",
.cimgui = .{
.path = "3rd-party/cimgui",
},
},
}

View file

@ -12,7 +12,15 @@
zls
];
buildInputs = [
gtk4
alsa-lib
xorg.libX11
xorg.libXcursor
xorg.libXi
xorg.libXext
xorg.libXrandr
xorg.libXinerama
libGL
libGLU
];
};
};

View file

@ -1,20 +1,77 @@
const std = @import("std");
const capy = @import("capy");
const ig = @import("cimgui");
const sokol = @import("sokol");
const slog = sokol.log;
const sg = sokol.gfx;
const sapp = sokol.app;
const sglue = sokol.glue;
const simgui = sokol.imgui;
// This is required for your app to build to WebAssembly and other particular architectures
pub usingnamespace capy.cross_platform;
const state = struct {
var pass_action: sg.PassAction = .{};
};
pub fn main() !void {
try capy.backend.init();
export fn init() void {
// initialize sokol-gfx
sg.setup(.{
.environment = sglue.environment(),
.logger = .{ .func = slog.func },
});
// initialize sokol-imgui
simgui.setup(.{
.logger = .{ .func = slog.func },
});
var window = try capy.Window.init();
try window.set(
capy.label(.{ .text = "Hello, World", .alignment = .Center }),
);
window.setTitle("Hello");
window.setPreferredSize(250, 100);
window.show();
capy.runEventLoop();
// initial clear color
state.pass_action.colors[0] = .{
.load_action = .CLEAR,
.clear_value = .{ .r = 0.0, .g = 0.5, .b = 1.0, .a = 1.0 },
};
}
export fn frame() void {
// call simgui.newFrame() before any ImGui calls
simgui.newFrame(.{
.width = sapp.width(),
.height = sapp.height(),
.delta_time = sapp.frameDuration(),
.dpi_scale = sapp.dpiScale(),
});
//=== UI CODE STARTS HERE
ig.igSetNextWindowPos(.{ .x = 10, .y = 10 }, ig.ImGuiCond_Once, .{ .x = 0, .y = 0 });
ig.igSetNextWindowSize(.{ .x = 400, .y = 100 }, ig.ImGuiCond_Once);
_ = ig.igBegin("Hello Dear ImGui!", 0, ig.ImGuiWindowFlags_None);
_ = ig.igColorEdit3("Background", &state.pass_action.colors[0].clear_value.r, ig.ImGuiColorEditFlags_None);
ig.igEnd();
//=== UI CODE ENDS HERE
// call simgui.render() inside a sokol-gfx pass
sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() });
simgui.render();
sg.endPass();
sg.commit();
}
export fn cleanup() void {
simgui.shutdown();
sg.shutdown();
}
export fn event(ev: [*c]const sapp.Event) void {
// forward input events to sokol-imgui
_ = simgui.handleEvent(ev.*);
}
pub fn main() void {
sapp.run(.{
.init_cb = init,
.frame_cb = frame,
.cleanup_cb = cleanup,
.event_cb = event,
.window_title = "sokol-zig + Dear Imgui",
.width = 800,
.height = 600,
.icon = .{ .sokol_default = true },
.logger = .{ .func = slog.func },
});
}