Zig NEWS

Pyrolistical
Pyrolistical

Posted on • Updated on

Generating a SDK UI at comptime

OpenVR integration was just added to zig-gamedev and you can try the sample out for yourself

But while I was developing this integration, I was also learning the OpenVR SDK. I needed a way to make API calls without having a full blown VR game.

zig-gamedev already had Dear ImGui integration, I decided to create a sample that exposed the OpenVR SDK API as an interactive UI.

Let's look at one of these API UIs.

For the OpenVR SDK API in the Chaperone module we have:

fn forceBoundsVisible(
  self: OpenVR.Chaperone,
  force: bool,
) void
Enter fullscreen mode Exit fullscreen mode

Since this is a setter, I wanted the actual API call to be a button. The force: bool parameter can be a checkbox.

The UI I ended up with looks like:
screenshotg of forceBoundsVisible UI

At first I was manually creating UI elements for each API, but after awhile I noticed a lot of duplicate code.

I realized since I was already integrating OpenVR SDK as a zig library, I had comptime access to the entire type signature of the APIs. I could generate the UI at comptime!

I ended up implementing the forceBoundsVisible UI as:

force_bounds_visible: bool = false,

...

try ui.setter(
  OpenVR.Chaperone,
  "forceBoundsVisible",
  chaperone,
  .{
    .force = &self.force_bounds_visible
  },
  null,
);
Enter fullscreen mode Exit fullscreen mode

If we peek inside the implementation of setter, we see it is comptime all the way down:

pub fn setter(
  comptime T: type,
  comptime f_name: [:0]const u8,
  self: T,
  arg_ptrs: anytype,
  return_doc: ?[:0]const u8,
) !void {
    zgui.pushStrId(f_name);
    defer zgui.popId();

    const ArgPtrs = @TypeOf(arg_ptrs);
    const arg_ptrs_info = @typeInfo(ArgPtrs).Struct;

    const f_type = fType(T, f_name);

    if (zgui.button(f_name, .{})) {
        const args = args: {
            var args: f_type.Args = undefined;
            args[0] = self;
            fillArgs(arg_ptrs_info, arg_ptrs, 1, f_type.Args, &args);
            break :args args;
        };

        @call(.auto, f_type.f, args);
    }
    zgui.sameLine(.{});
    zgui.text("(", .{});
    try renderParams(null, f_type.arg_types[1..], arg_ptrs_info, arg_ptrs);
    zgui.text(") {s}", .{return_doc orelse f_type.return_name});

    zgui.newLine();
}
Enter fullscreen mode Exit fullscreen mode

This was super neat and saved a lot of time.

Checkout the full UI and other comptime variants in the full running sample https://github.com/zig-gamedev/zig-gamedev/tree/main/samples/openvr_test

zig-gamedev openvr test sample screenshot

Top comments (0)