summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2021-04-12 12:13:39 +0000
committers-ol <s+removethis@s-ol.nu>2021-04-12 12:13:39 +0000
commite08d4514f8fc489e698261a0dd3cc53f538e36fd (patch)
tree24a79bb22f53c07f1e4108490311720d601b80e0
parentmultiple GLTF Models, EXT_MSFT_controller_model support (diff)
downloadopenxPriments-main.tar.gz
openxPriments-main.zip
wipmain
-rw-r--r--assets/controller_left.glbbin0 -> 3411132 bytes
-rw-r--r--assets/controller_right.glbbin0 -> 3418376 bytes
m---------lib/DiligentEngine0
m---------lib/glm0
m---------lib/openxr0
-rw-r--r--src/graphics.zig20
-rw-r--r--src/main.zig401
-rw-r--r--src/math.zig58
-rw-r--r--src/scene.zig88
-rw-r--r--src/xrvk.zig15
10 files changed, 486 insertions, 96 deletions
diff --git a/assets/controller_left.glb b/assets/controller_left.glb
new file mode 100644
index 0000000..3a81606
--- /dev/null
+++ b/assets/controller_left.glb
Binary files differ
diff --git a/assets/controller_right.glb b/assets/controller_right.glb
new file mode 100644
index 0000000..d9fa7ce
--- /dev/null
+++ b/assets/controller_right.glb
Binary files differ
diff --git a/lib/DiligentEngine b/lib/DiligentEngine
-Subproject 79d350ef4f60753f0eaf4ca1e331f2f4f4dd40c
+Subproject f5dafeeceb651f55808dac06e84dcd3f921947b
diff --git a/lib/glm b/lib/glm
-Subproject 5bd607beac620e3c3246bf25ed7c4fdb37cc9b0
+Subproject 89abad9f5b093193da15f7b920ae7fba3085a0a
diff --git a/lib/openxr b/lib/openxr
-Subproject 360affef88584fc85ea055664799f27ff102122
+Subproject e5b185d2709e30268c3ec5e3bd2c9f61097a9e4
diff --git a/src/graphics.zig b/src/graphics.zig
index 5d875f7..8cbb3f7 100644
--- a/src/graphics.zig
+++ b/src/graphics.zig
@@ -1,22 +1,8 @@
const std = @import("std");
-const mem = std.mem;
usingnamespace @import("xrvk.zig");
-const Allocator = std.mem.Allocator;
const dg = @import("diligent");
-
-fn quat2euler(q: xr.Quaternionf) xr.Vector3f {
- const sinr_cosp = 2 * (q.w * q.x + q.y * q.z);
- const siny_cosp = 2 * (q.w * q.z + q.x * q.y);
- const cosr_cosp = 1 - 2 * (q.x * q.x + q.y * q.y);
- const cosy_cosp = 1 - 2 * (q.y * q.y + q.z * q.z);
- const sinp = 2 * (q.w * q.y - q.z * q.x);
-
- return .{
- .x = std.math.atan2(f32, sinr_cosp, cosr_cosp),
- .y = if (std.math.fabs(sinp) >= 1) std.math.pi / 2.0 else std.math.asin(sinp),
- .z = std.math.atan2(f32, siny_cosp, cosy_cosp),
- };
-}
+const mem = std.mem;
+const Allocator = mem.Allocator;
pub const Graphics = struct {
xri: xr.InstanceDispatch,
@@ -404,7 +390,7 @@ pub const Swapchain = struct {
// get images
const image_count = try gfx.xri.enumerateSwapchainImages(swapchain, 0, null);
const xr_images = try gfx.allocator.alloc(xr.SwapchainImageVulkan2KHR, image_count);
- std.mem.set(xr.SwapchainImageVulkan2KHR, xr_images, xr.SwapchainImageVulkan2KHR.empty());
+ mem.set(xr.SwapchainImageVulkan2KHR, xr_images, xr.SwapchainImageVulkan2KHR.empty());
defer gfx.allocator.free(xr_images);
_ = try gfx.xri.enumerateSwapchainImages(swapchain, image_count, @ptrCast(
diff --git a/src/main.zig b/src/main.zig
index 67ce8f0..6bf546d 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -2,9 +2,11 @@ usingnamespace @import("xrvk.zig");
const std = @import("std");
const glm = @import("glm");
const gfx = @import("graphics.zig");
+const math = @import("math.zig");
const scene = @import("scene.zig");
const renderdoc = @import("renderdoc.zig");
-const Allocator = std.mem.Allocator;
+const mem = std.mem;
+const Allocator = mem.Allocator;
pub fn main() !void {
try renderdoc.load_api();
@@ -13,29 +15,259 @@ pub fn main() !void {
session.deinit();
}
-const Paths = struct {
- user_hand_right: xr.Path,
- user_hand_left: xr.Path,
- user_hand_head: xr.Path,
+const HandState = struct {
+ subaction_path: xr.Path,
+ handedness: []const u8,
+ space: xr.Space,
+
+ active: bool,
+ grab: ?f32,
+ pose: ?glm.Mat4,
+ quit: ?bool,
+ vibrate: ?xr.HapticVibration,
+
+ pub fn init(path: xr.Path, handedness: []const u8, space: xr.Space) HandState {
+ return .{
+ .subaction_path = path,
+ .space = space,
+ .handedness = handedness,
+
+ .active = false,
+ .grab = null,
+ .pose = null,
+ .quit = null,
+ .vibrate = null,
+ };
+ }
+
+ pub fn sync(self: *HandState, actions: *const Actions) !void {
+ const xri = actions.xri;
+ const session = actions.session;
+
+ var grab = xr.ActionStateFloat.empty();
+ var pose = xr.ActionStatePose.empty();
+ var quit = xr.ActionStateBoolean.empty();
+
+ try xri.getActionStateFloat(session, .{
+ .action = actions.grab_object,
+ .subaction_path = self.subaction_path,
+ }, &grab);
+ self.grab = if (grab.is_active == xr.TRUE) grab.current_state else null;
+
+ try xri.getActionStatePose(session, .{
+ .action = actions.grip_pose,
+ .subaction_path = self.subaction_path,
+ }, &pose);
+ self.active = pose.is_active == xr.TRUE;
+
+ try xri.getActionStateBoolean(session, .{
+ .action = actions.quit_session,
+ .subaction_path = self.subaction_path,
+ }, &quit);
+ self.quit = if (quit.is_active == xr.TRUE) quit.current_state == xr.TRUE else null;
+ }
+
+ pub fn locate(
+ self: *HandState,
+ xri: xr.InstanceDispatch,
+ reference_space: xr.Space,
+ query_time: xr.Time,
+ ) !void {
+ if (!self.active) {
+ self.pose = null;
+ return;
+ }
+
+ var location = xr.SpaceLocation.empty();
+ try xri.locateSpace(self.space, reference_space, query_time, &location);
+
+ if (!location.location_flags.contains(.{
+ .position_valid_bit = true,
+ .orientation_valid_bit = true,
+ })) {
+ self.pose = null;
+ return;
+ }
+
+ self.pose = math.pose2mat(location.pose);
+ }
+
+ pub fn applyOutput(self: *const HandState, actions: *const Actions) !void {
+ if (self.vibrate) |vibration| {
+ try xri.applyHapticFeedback(
+ session,
+ .{
+ .action = self.actions.vibrate,
+ .subaction_path = self.subaction_path,
+ },
+ @ptrCast(*const xr.HapticBaseHeader, &vibration),
+ );
+ }
+ }
+};
- pub fn init(xri: xr.InstanceDispatch, inst: xr.Instance) !Paths {
- var self: Paths = undefined;
+const Actions = struct {
+ xri: xr.InstanceDispatch,
+ session: xr.Session,
+
+ set: xr.ActionSet,
+ grab_object: xr.Action,
+ grip_pose: xr.Action,
+ quit_session: xr.Action,
+ vibrate: xr.Action,
+
+ hands: [2]HandState,
+
+ pub fn init(
+ xri: xr.InstanceDispatch,
+ instance: xr.Instance,
+ session: xr.Session,
+ ) !Actions {
+ var self: Actions = undefined;
+ self.xri = xri;
+ self.session = session;
+
+ const subaction_strs = [_][:0]const u8{ "/user/hand/right", "/user/hand/left" };
+ const handedness_strs = [_][:0]const u8{ "right", "left" };
+ var subaction_paths: [subaction_strs.len]xr.Path = undefined;
+ for (subaction_strs) |str, i| {
+ subaction_paths[i] = try xri.stringToPath(instance, str);
+ }
+
+ var set_ci = xr.ActionSetCreateInfo{
+ .action_set_name = undefined,
+ .localized_action_set_name = undefined,
+ .priority = 0,
+ };
+ mem.copy(u8, set_ci.action_set_name[0..], "gameplay\x00");
+ mem.copy(u8, set_ci.localized_action_set_name[0..], "Gameplay\x00");
+ self.set = try xri.createActionSet(instance, set_ci);
+
+ {
+ // create actions
+ var action_ci = xr.ActionCreateInfo{
+ .action_type = undefined,
+ .action_name = undefined,
+ .localized_action_name = undefined,
+ .count_subaction_paths = subaction_paths.len,
+ .subaction_paths = &subaction_paths,
+ };
+
+ action_ci.action_type = .float_input;
+ mem.copy(u8, action_ci.action_name[0..], "grab_object\x00");
+ mem.copy(u8, action_ci.localized_action_name[0..], "Grab Object\x00");
+ self.grab_object = try xri.createAction(self.set, action_ci);
+
+ action_ci.action_type = .pose_input;
+ mem.copy(u8, action_ci.action_name[0..], "grip_pose\x00");
+ mem.copy(u8, action_ci.localized_action_name[0..], "Grip Pose\x00");
+ self.grip_pose = try xri.createAction(self.set, action_ci);
+
+ action_ci.action_type = .boolean_input;
+ mem.copy(u8, action_ci.action_name[0..], "quit_session\x00");
+ mem.copy(u8, action_ci.localized_action_name[0..], "Quit Session\x00");
+ self.quit_session = try xri.createAction(self.set, action_ci);
+
+ action_ci.action_type = .vibration_output;
+ mem.copy(u8, action_ci.action_name[0..], "vibrate_hand\x00");
+ mem.copy(u8, action_ci.localized_action_name[0..], "Vibrate Hand\x00");
+ self.vibrate = try xri.createAction(self.set, action_ci);
+ }
+
+ {
+ // suggested bindings
+ const PathStruct = struct {
+ squeeze_force: xr.Path,
+ pose: xr.Path,
+ b_click: xr.Path,
+ haptic: xr.Path,
+ };
+
+ var paths: [subaction_strs.len]PathStruct = undefined;
+ for (subaction_strs) |subaction, i| {
+ var path_buffer = [_]u8{0} ** 2048;
+ var string: [:0]const u8 = undefined;
+
+ string = try std.fmt.bufPrintZ(path_buffer[0..], "{}/input/squeeze/force", .{subaction});
+ const squeeze_force = try xri.stringToPath(instance, string.ptr);
+
+ string = try std.fmt.bufPrintZ(path_buffer[0..], "{}/input/grip/pose", .{subaction});
+ const pose = try xri.stringToPath(instance, string.ptr);
+
+ string = try std.fmt.bufPrintZ(path_buffer[0..], "{}/input/b/click", .{subaction});
+ const b_click = try xri.stringToPath(instance, string.ptr);
+
+ string = try std.fmt.bufPrintZ(path_buffer[0..], "{}/output/haptic", .{subaction});
+ const haptic = try xri.stringToPath(instance, string.ptr);
+
+ paths[i] = .{
+ .squeeze_force = squeeze_force,
+ .pose = pose,
+ .b_click = b_click,
+ .haptic = haptic,
+ };
+ }
+
+ const index_profile = try xri.stringToPath(instance, "/interaction_profiles/valve/index_controller");
+ const bindings = [_]xr.ActionSuggestedBinding{
+ .{ .action = self.grab_object, .binding = paths[0].squeeze_force },
+ .{ .action = self.grab_object, .binding = paths[1].squeeze_force },
+ .{ .action = self.grip_pose, .binding = paths[0].pose },
+ .{ .action = self.grip_pose, .binding = paths[1].pose },
+ .{ .action = self.quit_session, .binding = paths[0].b_click },
+ .{ .action = self.quit_session, .binding = paths[1].b_click },
+ .{ .action = self.vibrate, .binding = paths[0].haptic },
+ .{ .action = self.vibrate, .binding = paths[1].haptic },
+ };
+ try xri.suggestInteractionProfileBindings(instance, .{
+ .interaction_profile = index_profile,
+ .count_suggested_bindings = bindings.len,
+ .suggested_bindings = &bindings,
+ });
+ }
- inline for (@typeInfo(Paths).Struct.fields) |field| {
- var buffer: [field.name.len + 2:0]u8 = undefined;
- _ = std.mem.replace(u8, field.name, "_", "/", buffer[1..]);
- buffer[0] = '/';
- buffer[buffer.len - 1] = 0;
- @field(self, field.name) = try xri.stringToPath(inst, buffer[0..]);
+ for (subaction_paths) |path, i| {
+ const handedness = handedness_strs[i];
+ const space = try xri.createActionSpace(session, .{
+ .action = self.grip_pose,
+ .pose_in_action_space = .{
+ // "origin": [-0.005154, 0.013042, 0.107171],
+ // "rotate_xyz" : [93.782, 0.0, 0.0]
+ // .position = .{ .x = 0.005154, .y = -0.013042, .z = -0.107171 },
+ // .orientation = .{ .x = 0.7300549, .y = 0, .z = 0, .w = 0.6833885 },
+ },
+ .subaction_path = path,
+ });
+ self.hands[i] = HandState.init(path, handedness, space);
}
+ try xri.attachSessionActionSets(session, .{
+ .count_action_sets = 1,
+ .action_sets = @ptrCast([*]xr.ActionSet, &self.set),
+ });
+
return self;
}
+
+ pub fn sync(self: *Actions) !void {
+ const active_sets = [_]xr.ActiveActionSet{
+ .{ .action_set = self.set, .subaction_path = 0 },
+ };
+ _ = try self.xri.syncActions(self.session, .{
+ .count_active_action_sets = active_sets.len,
+ .active_action_sets = &active_sets,
+ });
+
+ for (self.hands) |*hand| {
+ try hand.sync(self);
+ }
+ }
};
const Session = struct {
xrb: xr.BaseDispatch,
xri: xr.InstanceDispatch,
+ xrcmi: ?xr.CMInstanceDispatch,
instance: xr.Instance,
session: xr.Session,
@@ -43,14 +275,14 @@ const Session = struct {
allocator: *Allocator,
+ actions: Actions,
state: xr.SessionState,
views: ?ViewInfo,
- paths: Paths,
graphics: gfx.Graphics,
layers: []*const xr.CompositionLayerBaseHeader,
scene_space: xr.Space,
- first_render: bool = true,
+ hand_models: [2]?*scene.Model,
const ViewInfo = struct {
configurations: []xr.ViewConfigurationView,
@@ -59,7 +291,23 @@ const Session = struct {
pub fn init(allocator: *Allocator) !Session {
var name: [128]u8 = undefined;
- std.mem.copy(u8, name[0..], "openxr-zig-test" ++ [_]u8{0});
+ mem.copy(u8, name[0..], "openxr-zig-test" ++ [_]u8{0});
+
+ const xrb = try xr.BaseDispatch.load(xr.getProcAddr);
+
+ const have_controller_model_ext = blk: {
+ var extensions = [_]xr.ExtensionProperties{ xr.ExtensionProperties.empty() } ** 512;
+ const extension_count = try xrb.enumerateInstanceExtensionProperties(null, extensions.len, &extensions);
+ for (extensions[0..extension_count]) |ext| {
+ const ext_name = @ptrCast([*:0]const u8, ext.extension_name[0..]);
+ if (mem.eql(u8, mem.spanZ(ext_name), "XR_MSFT_controller_model")) {
+ std.debug.print("found XR_MSFT_controller_model extension version {}\n", .{ ext.extension_version },);
+ break :blk true;
+ }
+ }
+
+ break :blk false;
+ };
const zero = [_:0]u8{};
const extensions = [_][*:0]const u8{
@@ -67,7 +315,6 @@ const Session = struct {
"XR_MSFT_controller_model",
};
- const xrb = try xr.BaseDispatch.load(xr.getProcAddr);
const instance = try xrb.createInstance(.{
.create_flags = .{},
.application_info = .{
@@ -79,14 +326,17 @@ const Session = struct {
},
.enabled_api_layer_count = 0,
.enabled_api_layer_names = @ptrCast([*]const [*:0]const u8, &zero),
- .enabled_extension_count = extensions.len,
+ .enabled_extension_count = if (have_controller_model_ext) 2 else 1,
.enabled_extension_names = &extensions,
});
const xri = try xr.InstanceDispatch.load(instance, xr.getProcAddr);
errdefer xri.destroyInstance(instance) catch unreachable;
- const paths = try Paths.init(xri, instance);
+ var xrcmi: ?xr.CMInstanceDispatch = null;
+ if (have_controller_model_ext) {
+ xrcmi = try xr.CMInstanceDispatch.load(instance, xr.getProcAddr);
+ }
const system = try xri.getSystem(instance, .{ .form_factor = .head_mounted_display });
@@ -126,6 +376,8 @@ const Session = struct {
});
errdefer xri.destroySession(session) catch unreachable;
+ const actions = try Actions.init(xri, instance, session);
+
const scene_space = try xri.createReferenceSpace(session, .{
.reference_space_type = .stage,
.pose_in_reference_space = .{
@@ -136,18 +388,21 @@ const Session = struct {
return Session{
.xrb = xrb,
.xri = xri,
+ .xrcmi = xrcmi,
.instance = instance,
.session = session,
.system = system,
.allocator = allocator,
+ .actions = actions,
.state = .idle,
.views = null,
- .paths = paths,
.graphics = graphics,
.scene_space = scene_space,
.layers = &[_]*const xr.CompositionLayerBaseHeader{},
+
+ .hand_models = [_]?*scene.Model{ null, null },
};
}
@@ -225,12 +480,12 @@ const Session = struct {
.projections = try self.allocator.alloc(xr.CompositionLayerProjectionView, view_count),
};
- std.mem.set(
+ mem.set(
xr.ViewConfigurationView,
views.configurations,
xr.ViewConfigurationView.empty(),
);
- std.mem.set(
+ mem.set(
xr.CompositionLayerProjectionView,
views.projections,
xr.CompositionLayerProjectionView.empty(),
@@ -251,30 +506,18 @@ const Session = struct {
try self.graphics.createSwapchain(self.session, views.configurations);
var gfx_scene = try scene.Scene.init(&self.graphics);
- var helmet = scene.Model.initFile(&gfx_scene, "assets/DamagedHelmet.gltf");
+ var helmet = scene.Model.initFile(self.allocator, &gfx_scene, "assets/DamagedHelmet.gltf");
helmet.setTransform(glm.translation(glm.Vec3.init([_]f32{ 0, 1.5, -2 })));
try gfx_scene.models.append(helmet);
- for ([_]xr.Path{ self.paths.user_hand_left, self.paths.user_hand_right }) |path, i| {
- var model_state = xr.ControllerModelKeyStateMSFT.empty();
- try self.xri.getControllerModelKeyMSFT(self.session, path, &model_state);
-
- if (model_state.model_key == 0)
- continue;
-
- const size = try self.xri.loadControllerModelMSFT(self.session, model_state.model_key, 0, null);
- const buffer = try self.allocator.alloc(u8, size);
- defer self.allocator.free(buffer);
-
- _ = try self.xri.loadControllerModelMSFT(self.session, model_state.model_key, size, buffer.ptr);
- var model = scene.Model.initMemory(&gfx_scene, buffer);
-
- var transform = glm.Mat4.IDENTITY;
- transform = transform.mul(glm.translation(glm.Vec3.init([_]f32{ 0.6 * (@intToFloat(f32, i) - 0.5), 1.5, -0.5 })));
- transform = transform.mul(glm.rotation(0.9, glm.Vec3.init([_]f32{ 1, 0, 0 })));
- model.setTransform(transform);
- try gfx_scene.models.append(model);
- }
+ helmet = scene.Model.initFile(self.allocator, &gfx_scene, "assets/controller_right.glb");
+ helmet.setTransform(
+ glm.translation(glm.Vec3.init([_]f32{ -0.07, 1.6, -0.2 }))
+ .mul(glm.rotation(0.8, glm.Vec3.init([_]f32{ 1, 0, 0 })))
+ );
+ try helmet.loadNodeTransform("r_button_b");
+ try helmet.loadNodeTransform("r_trackpad_touch");
+ try gfx_scene.models.append(helmet);
const composition_layer = try self.allocator.create(xr.CompositionLayerProjection);
composition_layer.* = .{
@@ -291,8 +534,12 @@ const Session = struct {
while (true) {
try self.pollEvents();
if (self.state == .stopping) break;
+ try self.actions.sync();
- if (!try self.renderFrame(&gfx_scene)) {
+ const did_render = try self.renderFrame(&gfx_scene);
+ if (!did_render) {
+ // no frame submitted, so compositor won't throttle by waiting for GPU
+ // sleep to prevent busy-loop
std.os.nanosleep(0, 250_000_000);
}
}
@@ -313,6 +560,38 @@ const Session = struct {
transform = transform.mul(glm.rotation(predicted_seconds, glm.Vec3.init([_]f32{ 0, 1, 0 })));
gfx_scene.models.items[0].setTransform(transform);
+ {
+ const model = &gfx_scene.models.items[1];
+ var txm = &model.node_transforms.items[0];
+ // motion.type == translate:l
+ // apply translation axis * lerp(in, value_mapping)
+ txm.position = glm.Vec3.init([_]f32{0, -0.927, 0.0375}).mulScalar(
+ (std.math.sin(predicted_seconds * 3) / 2 + 0.5) * 0.002
+ );
+
+ var tp_mot_rot = glm.Mat4.IDENTITY;
+ // motion.type == trackpad: only apply negative component_local transform
+ // apply translation axis * lerp(in, value_mapping)
+ tp_mot_rot.mulAssign(glm.rotation(1.18682389, glm.Vec3.init([_]f32{ 1, 0, 0 })));
+ tp_mot_rot.mulAssign(math.quat2mat(xr.Quaternionf{ .x=-0.17349398, .y=-0.79472193, .z=0.32537197, .w=-0.48213067 }));
+ tp_mot_rot.mulAssign(glm.rotation(-1.18682389, glm.Vec3.init([_]f32{ 1, 0, 0 })));
+
+ txm = &model.node_transforms.items[1];
+ txm.position =
+ tp_mot_rot.apply(
+ glm.Vec4.init([_]f32{
+ std.math.clamp(std.math.sin(predicted_seconds * 3) * 2, -1, 1) * 0.009,
+ std.math.clamp(std.math.cos(predicted_seconds * 3) * 2, -1, 1) * 0.015,
+ 0,
+ 1
+ })
+ ).shrink(3);
+ // txm.rotation = glm.Vec4.init([_]f32{ -0.17349398, -0.79472193, 0.32537197, -0.48213067 });
+
+ model.flushTransforms();
+ std.debug.print("transform pos: {}\n", .{ model.node_transforms.items[0].position });
+ }
+
var layer_count: u32 = 0;
if (frame_state.should_render > 0) {
@@ -331,6 +610,40 @@ const Session = struct {
return error.InvalidPositionOrOrientation;
}
+ for (self.actions.hands) |*hand, i| {
+ try hand.locate(self.xri, self.scene_space, frame_state.predicted_display_time);
+ if (hand.active and self.hand_models[i] == null) {
+ var model = if (self.xrcmi) |xri| model: {
+ var model_state = xr.ControllerModelKeyStateMSFT.empty();
+ try xri.getControllerModelKeyMSFT(self.session, hand.subaction_path, &model_state);
+
+ if (model_state.model_key == 0)
+ continue;
+
+ const size = try xri.loadControllerModelMSFT(self.session, model_state.model_key, 0, null);
+ const buffer = try self.allocator.alloc(u8, size);
+ defer self.allocator.free(buffer);
+
+ _ = try xri.loadControllerModelMSFT(self.session, model_state.model_key, size, buffer.ptr);
+ break :model scene.Model.initMemory(self.allocator, gfx_scene, buffer);
+ } else model: {
+ var path_buffer = [_]u8{0} ** 128;
+ const path = try std.fmt.bufPrintZ(path_buffer[0..], "assets/controller_{}.glb", .{hand.handedness});
+ break :model scene.Model.initFile(self.allocator, gfx_scene, path);
+ };
+
+ const ptr = try gfx_scene.models.addOne();
+ ptr.* = model;
+ self.hand_models[i] = ptr;
+ }
+
+ if (hand.pose) |pose| {
+ if (self.hand_models[i]) |model| {
+ model.setTransform(pose);
+ }
+ }
+ }
+
const swapchain = self.graphics.swapchain;
try swapchain.acquireImage();
diff --git a/src/math.zig b/src/math.zig
new file mode 100644
index 0000000..4666cfc
--- /dev/null
+++ b/src/math.zig
@@ -0,0 +1,58 @@
+usingnamespace @import("xrvk.zig");
+const glm = @import("glm");
+const math = @import("std").math;
+
+pub fn vec2vec(v: xr.Vector3f) glm.Vec3 {
+ return glm.Vec3.init([_]f32{ v.x, v.y, v.z });
+}
+
+pub fn quat2mat(q: xr.Quaternionf) glm.Mat4 {
+ const x = -q.x;
+ const y = -q.y;
+ const z = -q.z;
+ const w = q.w;
+ const x2 = q.x * q.x;
+ const y2 = q.y * q.y;
+ const z2 = q.z * q.z;
+
+ // zig fmt: off
+ return glm.Mat4.init([_][4]f32{
+ [_]f32{ 1 - 2 * y2 - 2 * z2, 2 * x * y - 2 * z * w, 2 * x * z + 2 * y * w, 0 },
+ [_]f32{ 2 * x * y + 2 * z * w, 1 - 2 * x2 - 2 * z2, 2 * y * z - 2 * x * w, 0 },
+ [_]f32{ 2 * x * z - 2 * y * w, 2 * y * z + 2 * x * w, 1 - 2 * x2 - 2 * y2, 0 },
+ [_]f32{ 0, 0, 0, 1 },
+ });
+ // zig fmt: on
+}
+
+pub fn pose2mat(pose: xr.Posef) glm.Mat4 {
+ return glm.translation(vec2vec(pose.position)).mul(quat2mat(pose.orientation));
+}
+
+pub fn pose2matInverse(pose: xr.Posef) glm.Mat4 {
+ var inverse = pose;
+ inverse.orientation.x *= -1;
+ inverse.orientation.y *= -1;
+ inverse.orientation.z *= -1;
+ inverse.position.x *= -1;
+ inverse.position.y *= -1;
+ inverse.position.z *= -1;
+ return quat2mat(inverse.orientation).mul(glm.translation(vec2vec(inverse.position)));
+}
+
+
+pub fn projection(fov: xr.Fovf, near: f32, far: f32) glm.Mat4 {
+ const tanUp = math.tan(fov.angle_up);
+ const tanDown = math.tan(fov.angle_down);
+ const tanLeft = math.tan(fov.angle_left);
+ const tanRight = math.tan(fov.angle_right);
+ const tanW = tanRight - tanLeft;
+ const tanH = tanUp - tanDown ;
+
+ return glm.Mat4.init([_][4]f32{
+ [_]f32{ 2 / tanW, 0, 0, 0 },
+ [_]f32{ 0, 2 / tanH, 0, 0 },
+ [_]f32{ (tanRight + tanLeft) / tanW, (tanUp + tanDown) / tanH, -far / (far - near), -1 },
+ [_]f32{ 0, 0, -far * near / (far - near), 0 },
+ });
+}
diff --git a/src/scene.zig b/src/scene.zig
index e7fbb5c..b4201b3 100644
--- a/src/scene.zig
+++ b/src/scene.zig
@@ -1,8 +1,11 @@
const std = @import("std");
+const mem = std.mem;
+const Allocator = mem.Allocator;
const dg = @import("diligent");
const glm = @import("glm");
const graphics = @import("graphics.zig");
usingnamespace @import("xrvk.zig");
+const math = @import("math.zig");
const geometry_source =
\\layout(triangles, invocations = 2) in;
@@ -66,9 +69,9 @@ const CameraAttribs = packed struct {
.view = view,
.proj = proj,
.viewproj = viewproj,
- .view_inv = view.transpose(),
- .proj_inv = proj.transpose(),
- .viewproj_inv = viewproj.transpose(),
+ .view_inv = view.invert() orelse unreachable,
+ .proj_inv = proj.invert() orelse unreachable,
+ .viewproj_inv = viewproj.invert() orelse unreachable,
};
}
@@ -126,29 +129,6 @@ const LightAttribs = packed struct {
shadow_attribs: ShadowMapAttribs = .{},
};
-fn vec2vec(v: xr.Vector3f) glm.Vec3 {
- return glm.Vec3.init([_]f32{ v.x, v.y, v.z });
-}
-
-fn quat2mat(q: xr.Quaternionf) glm.Mat4 {
- const x = q.x;
- const y = q.y;
- const z = q.z;
- const w = q.w;
- const x2 = q.x * q.x;
- const y2 = q.y * q.y;
- const z2 = q.z * q.z;
-
- // zig fmt: off
- return glm.Mat4.init([_][4]f32{
- [_]f32{ 1 - 2 * y2 - 2 * z2, 2 * x * y - 2 * z * w, 2 * x * z + 2 * y * w, 0 },
- [_]f32{ 2 * x * y + 2 * z * w, 1 - 2 * x2 - 2 * z2, 2 * y * z - 2 * x * w, 0 },
- [_]f32{ 2 * x * z - 2 * y * w, 2 * y * z + 2 * x * w, 1 - 2 * x2 - 2 * y2, 0 },
- [_]f32{ 0, 0, 0, 1 },
- });
- // zig fmt: on
-}
-
const defaultSampler = dg.SamplerDesc{
._DeviceObjectAttribs = .{ .Name = "Default Sampler" },
.MinFilter = dg.FILTER_TYPE_LINEAR,
@@ -174,6 +154,9 @@ pub const Model = struct {
bindings: dg.GLTF_ModelResourceBindings,
params: dg.GLTF_RenderInfo,
+ node_indices: std.ArrayList(u32),
+ node_transforms: std.ArrayList(Transform),
+
const DEFAULT_PARAMS = dg.GLTF_RenderInfo{
.ModelTransform = @bitCast([16]f32, glm.Mat4.IDENTITY),
.AlphaModes = dg.ALPHA_MODE_FLAG_ALL,
@@ -186,7 +169,14 @@ pub const Model = struct {
.WhitePoint = 3,
};
- pub fn initMemory(scene: *const Scene, data: []const u8) Model {
+ pub const Transform = extern struct {
+ position: glm.Vec3,
+ scale: glm.Vec3,
+ rotation: glm.Vec4,
+ matrix: glm.Mat4,
+ };
+
+ pub fn initMemory(allocator: *Allocator, scene: *const Scene, data: []const u8) Model {
const model = dg.Diligent_IGLTFModel_Create(
@ptrCast(*dg.IRenderDevice, scene.dev.ptr),
scene.ctx.ptr,
@@ -209,10 +199,13 @@ pub const Model = struct {
scene.light_ubo.ptr,
),
.params = DEFAULT_PARAMS,
+
+ .node_indices = std.ArrayList(u32).init(allocator),
+ .node_transforms = std.ArrayList(Transform).init(allocator),
};
}
- pub fn initFile(scene: *const Scene, path: [:0]const u8) Model {
+ pub fn initFile(allocator: *Allocator, scene: *const Scene, path: [:0]const u8) Model {
const model = dg.Diligent_IGLTFModel_Create(
@ptrCast(*dg.IRenderDevice, scene.dev.ptr),
scene.ctx.ptr,
@@ -235,12 +228,17 @@ pub const Model = struct {
scene.light_ubo.ptr,
),
.params = DEFAULT_PARAMS,
+
+ .node_indices = std.ArrayList(u32).init(allocator),
+ .node_transforms = std.ArrayList(Transform).init(allocator),
};
}
pub fn deinit(self: *Model) void {
self.bindings.Release();
self.model.Release();
+ self.node_transforms.deinit();
+ self.node_indices.deinit();
}
pub fn setTransform(self: *Model, mat: glm.Mat4) void {
@@ -257,6 +255,26 @@ pub const Model = struct {
scene.resource_binding.ptr,
);
}
+
+ pub fn loadNodeTransform(self: *Model, name: [:0]const u8) !void {
+ var index: u32 = undefined;
+ if (self.model.GetNodeIndex(name.ptr, &index)) {
+ try self.node_indices.append(index);
+ const transform = try self.node_transforms.addOne();
+ const ptr = @intToPtr(?*dg.GLTF_Transform, @ptrToInt(transform));
+ self.model.GetNodeTransform(index, ptr);
+ } else {
+ return error.NodeNotFound;
+ }
+ }
+
+ pub fn flushTransforms(self: *Model) void {
+ for (self.node_indices.items) |index, i| {
+ const ptr = @intToPtr(?*dg.GLTF_Transform, @ptrToInt(&self.node_transforms.items[i]));
+ self.model.SetNodeTransform(index, ptr);
+ }
+ self.model.UpdateTransforms();
+ }
};
pub const Scene = struct {
@@ -493,12 +511,12 @@ pub const Scene = struct {
for (self.views) |*view, i| {
const fov = views[i].fov;
const viewport = self.viewports[i];
- const fovY = fov.angle_up - fov.angle_down;
- const aspect = viewport.Width / viewport.Height;
- view.projection = glm.perspective(fovY, aspect, 0.1, 100);
-
- const pose = views[i].pose;
- view.modelview = quat2mat(pose.orientation).mul(glm.translation(vec2vec(pose.position).mulScalar(-1)));
+ // const fovY = fov.angle_up - fov.angle_down;
+ // const fovX = fov.angle_right - fov.angle_left;
+ // const aspect = viewport.Width / viewport.Height;
+ // view.projection = glm.perspective(fovX, aspect, 0.1, 100);
+ view.projection = math.projection(fov, 0.1, 100);
+ view.modelview = math.pose2matInverse(views[i].pose);
}
try self.updateBufferSlice(ViewUBO, self.views_ubo, self.views);
@@ -676,7 +694,7 @@ pub const Scene = struct {
var data: [*c]T = null;
self.ctx.MapBuffer(buffer.ptr, dg.MAP_WRITE, dg.MAP_FLAG_DISCARD, @ptrCast([*c]?*c_void, &data));
if (data) |ptr| {
- std.mem.copy(T, data[0..values.len], values);
+ mem.copy(T, data[0..values.len], values);
self.ctx.UnmapBuffer(buffer.ptr, dg.MAP_WRITE);
} else {
return error.MapFailed;
diff --git a/src/xrvk.zig b/src/xrvk.zig
index 64428bc..b2521d5 100644
--- a/src/xrvk.zig
+++ b/src/xrvk.zig
@@ -44,6 +44,7 @@ pub const xr = struct {
usingnamespace rxr;
pub const BaseDispatch = struct {
+ xrEnumerateInstanceExtensionProperties: PfnEnumerateInstanceExtensionProperties,
xrCreateInstance: PfnCreateInstance,
usingnamespace BaseWrapper(@This());
};
@@ -53,6 +54,15 @@ pub const xr = struct {
xrGetSystem: PfnGetSystem,
xrGetSystemProperties: PfnGetSystemProperties,
xrStringToPath: PfnStringToPath,
+ xrCreateActionSet: PfnCreateActionSet,
+ xrCreateAction: PfnCreateAction,
+ xrCreateActionSpace: PfnCreateActionSpace,
+ xrSuggestInteractionProfileBindings: PfnSuggestInteractionProfileBindings,
+ xrSyncActions: PfnSyncActions,
+ xrAttachSessionActionSets: PfnAttachSessionActionSets,
+ xrGetActionStateFloat: PfnGetActionStateFloat,
+ xrGetActionStatePose: PfnGetActionStatePose,
+ xrGetActionStateBoolean: PfnGetActionStateBoolean,
xrCreateSession: PfnCreateSession,
xrDestroySession: PfnDestroySession,
xrBeginSession: PfnBeginSession,
@@ -62,6 +72,7 @@ pub const xr = struct {
xrEndFrame: PfnEndFrame,
xrPollEvent: PfnPollEvent,
xrLocateViews: PfnLocateViews,
+ xrLocateSpace: PfnLocateSpace,
xrCreateReferenceSpace: PfnCreateReferenceSpace,
xrCreateVulkanInstanceKHR: PfnCreateVulkanInstanceKHR,
xrGetVulkanGraphicsDevice2KHR: PfnGetVulkanGraphicsDevice2KHR,
@@ -74,6 +85,10 @@ pub const xr = struct {
xrAcquireSwapchainImage: PfnAcquireSwapchainImage,
xrWaitSwapchainImage: PfnWaitSwapchainImage,
xrReleaseSwapchainImage: PfnReleaseSwapchainImage,
+ usingnamespace InstanceWrapper(@This());
+ };
+
+ pub const CMInstanceDispatch = struct {
xrGetControllerModelKeyMSFT: PfnGetControllerModelKeyMSFT,
xrLoadControllerModelMSFT: PfnLoadControllerModelMSFT,
usingnamespace InstanceWrapper(@This());