diff options
| author | s-ol <s+removethis@s-ol.nu> | 2021-04-12 12:13:39 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2021-04-12 12:13:39 +0000 |
| commit | e08d4514f8fc489e698261a0dd3cc53f538e36fd (patch) | |
| tree | 24a79bb22f53c07f1e4108490311720d601b80e0 | |
| parent | multiple GLTF Models, EXT_MSFT_controller_model support (diff) | |
| download | openxPriments-main.tar.gz openxPriments-main.zip | |
wipmain
| -rw-r--r-- | assets/controller_left.glb | bin | 0 -> 3411132 bytes | |||
| -rw-r--r-- | assets/controller_right.glb | bin | 0 -> 3418376 bytes | |||
| m--------- | lib/DiligentEngine | 0 | ||||
| m--------- | lib/glm | 0 | ||||
| m--------- | lib/openxr | 0 | ||||
| -rw-r--r-- | src/graphics.zig | 20 | ||||
| -rw-r--r-- | src/main.zig | 401 | ||||
| -rw-r--r-- | src/math.zig | 58 | ||||
| -rw-r--r-- | src/scene.zig | 88 | ||||
| -rw-r--r-- | src/xrvk.zig | 15 |
10 files changed, 486 insertions, 96 deletions
diff --git a/assets/controller_left.glb b/assets/controller_left.glb Binary files differnew file mode 100644 index 0000000..3a81606 --- /dev/null +++ b/assets/controller_left.glb diff --git a/assets/controller_right.glb b/assets/controller_right.glb Binary files differnew file mode 100644 index 0000000..d9fa7ce --- /dev/null +++ b/assets/controller_right.glb 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()); |
