From f23222b4a847768225c50a37c9659a58865796c1 Mon Sep 17 00:00:00 2001 From: patrick-scho Date: Fri, 15 Aug 2025 12:59:15 +0200 Subject: [PATCH 1/1] Initial commit --- build.zig | 128 +++++++++++++++++++++++++++++++++++++++++++ build.zig.zon | 26 +++++++++ src/main.zig | 119 ++++++++++++++++++++++++++++++++++++++++ src/shader/quad.glsl | 25 +++++++++ 4 files changed, 298 insertions(+) create mode 100644 build.zig create mode 100644 build.zig.zon create mode 100644 src/main.zig create mode 100644 src/shader/quad.glsl diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..590d913 --- /dev/null +++ b/build.zig @@ -0,0 +1,128 @@ +const std = @import("std"); +const Build = std.Build; +const OptimizeMode = std.builtin.OptimizeMode; +const ResolvedTarget = Build.ResolvedTarget; +const Dependency = Build.Dependency; +const sokol = @import("sokol"); +const cimgui = @import("cimgui"); + +pub fn build(b: *Build) !void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const opt_docking = b.option(bool, "docking", "Build with docking support") orelse false; + + // Get the matching Zig module name, C header search path and C library for + // vanilla imgui vs the imgui docking branch. + const cimgui_conf = cimgui.getConfig(opt_docking); + + // note that the sokol dependency is built with `.with_sokol_imgui = true` + const dep_sokol = b.dependency("sokol", .{ + .target = target, + .optimize = optimize, + .with_sokol_imgui = true, + }); + const dep_cimgui = b.dependency("cimgui", .{ + .target = target, + .optimize = optimize, + }); + + // inject the cimgui header search path into the sokol C library compile step + dep_sokol.artifact("sokol_clib").addIncludePath(dep_cimgui.path(cimgui_conf.include_dir)); + + // shaders + dep_sokol.artifact("sokol_clib").addIncludePath(b.path("ext/cimgui")); + const dep_shdc = dep_sokol.builder.dependency("shdc", .{}); + const shdc_step = try sokol.shdc.createSourceFile(b, .{ + .shdc_dep = dep_shdc, + .input = "src/shader/quad.glsl", + .output = "src/shader/quad.glsl.zig", + .slang = .{ .glsl430 = true }, + }); + + // main module with sokol and cimgui imports + const mod_main = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + .imports = &.{ + .{ .name = "sokol", .module = dep_sokol.module("sokol") }, + .{ .name = cimgui_conf.module_name, .module = dep_cimgui.module(cimgui_conf.module_name) }, + }, + }); + const mod_options = b.addOptions(); + mod_options.addOption(bool, "docking", opt_docking); + mod_main.addOptions("build_options", mod_options); + + // from here on different handling for native vs wasm builds + if (target.result.cpu.arch.isWasm()) { + try buildWasm(b, .{ + .mod_main = mod_main, + .dep_sokol = dep_sokol, + .dep_cimgui = dep_cimgui, + .cimgui_clib_name = cimgui_conf.clib_name, + }); + } else { + const exe = try buildNative(b, mod_main); + exe.step.dependOn(shdc_step); + } +} + +fn buildNative(b: *Build, mod: *Build.Module) !*Build.Step.Compile { + const exe = b.addExecutable(.{ + .name = "demo", + .root_module = mod, + }); + b.installArtifact(exe); + b.step("run", "Run demo").dependOn(&b.addRunArtifact(exe).step); + return exe; +} + +const BuildWasmOptions = struct { + mod_main: *Build.Module, + dep_sokol: *Dependency, + dep_cimgui: *Dependency, + cimgui_clib_name: []const u8, +}; + +fn buildWasm(b: *Build, opts: BuildWasmOptions) !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.addLibrary(.{ + .name = "demo", + .root_module = opts.mod_main, + }); + + // get the Emscripten SDK dependency from the sokol dependency + const dep_emsdk = opts.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"); + opts.dep_cimgui.artifact(opts.cimgui_clib_name).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) + opts.dep_cimgui.artifact(opts.cimgui_clib_name).step.dependOn(&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 = demo, + .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 = opts.dep_sokol.path("src/sokol/web/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 = "demo", .emsdk = dep_emsdk }); + run.step.dependOn(&link_step.step); + b.step("run", "Run demo").dependOn(&run.step); +} diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..bc7ef94 --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,26 @@ +.{ + .name = .sporegirl2, + + .version = "0.0.0", + + .fingerprint = 0x1f84a20503bc235e, + + .minimum_zig_version = "0.14.1", + + .dependencies = .{ + .sokol = .{ + .url = "git+https://github.com/floooh/sokol-zig.git#zig-0.14.1", + .hash = "sokol-0.1.0-pb1HK26VLQC1XOkHDW-5TglgwypAKIHGR2HPOTP6limn", + }, + .cimgui = .{ + .url = "git+https://github.com/floooh/dcimgui#3b98e0a57fc17cc72fdda6934bd932426778a16e", + .hash = "cimgui-0.1.0-44ClkczdkgCE9Z_0ehliUmEJjg-bjQVy1r55RQPw9N10", + }, + }, + + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + }, +} diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..92e1da5 --- /dev/null +++ b/src/main.zig @@ -0,0 +1,119 @@ +const use_docking = @import("build_options").docking; +const ig = if (use_docking) @import("cimgui_docking") else @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; + +const shd = @import("shader/quad.glsl.zig"); + +const state = struct { + var bind: sg.Bindings = .{}; + var pip: sg.Pipeline = .{}; + var pass_action: sg.PassAction = .{}; + var show_first_window: bool = true; + var show_second_window: bool = true; +}; + +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 }, + }); + if (use_docking) { + ig.igGetIO().*.ConfigFlags |= ig.ImGuiConfigFlags_DockingEnable; + } + + // a vertex buffer + state.bind.vertex_buffers[0] = sg.makeBuffer(.{ + .data = sg.asRange(&[_]f32{ + // positions colors + -0.5, 0.5, 0.5, 1.0, 0.0, 0.0, 1.0, + 0.5, 0.5, 0.5, 0.0, 1.0, 0.0, 1.0, + 0.5, -0.5, 0.5, 0.0, 0.0, 1.0, 1.0, + -0.5, -0.5, 0.5, 1.0, 1.0, 0.0, 1.0, + }), + }); + + // an index buffer + state.bind.index_buffer = sg.makeBuffer(.{ + .usage = .{ .index_buffer = true }, + .data = sg.asRange(&[_]u16{ 0, 1, 2, 0, 2, 3 }), + }); + + // a shader and pipeline state object + state.pip = sg.makePipeline(.{ + .shader = sg.makeShader(shd.quadShaderDesc(sg.queryBackend())), + .layout = init: { + var l = sg.VertexLayoutState{}; + l.attrs[shd.ATTR_quad_position].format = .FLOAT3; + l.attrs[shd.ATTR_quad_color0].format = .FLOAT4; + break :init l; + }, + .index_type = .UINT16, + }); + + // 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(), + }); + + if (ig.igBegin("Hello Dear ImGui!", &state.show_first_window, ig.ImGuiWindowFlags_None)) { + _ = ig.igColorEdit3("Background", &state.pass_action.colors[0].clear_value.r, ig.ImGuiColorEditFlags_None); + _ = ig.igText("Dear ImGui Version: %s", ig.IMGUI_VERSION); + ig.igEnd(); + } + + + // call simgui.render() inside a sokol-gfx pass + sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() }); + sg.applyPipeline(state.pip); + sg.applyBindings(state.bind); + sg.draw(0, 6, 1); + 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 }, + }); +} diff --git a/src/shader/quad.glsl b/src/shader/quad.glsl new file mode 100644 index 0000000..b3e7e5c --- /dev/null +++ b/src/shader/quad.glsl @@ -0,0 +1,25 @@ +/* quad vertex shader */ +@vs vs +in vec4 position; +in vec4 color0; +out vec4 color; + +void main() { + gl_Position = position; + color = color0; +} +@end + +/* quad fragment shader */ +@fs fs +in vec4 color; +out vec4 frag_color; + +void main() { + frag_color = color; +} +@end + +/* quad shader program */ +@program quad vs fs + -- 2.50.1