1 | 1 |
const std = @import("std");
|
2 | 2 |
const glm = @import("glm");
|
3 | 3 |
const gfx = @import("graphics.zig");
|
|
4 |
const math = @import("math.zig");
|
4 | 5 |
const scene = @import("scene.zig");
|
5 | 6 |
const renderdoc = @import("renderdoc.zig");
|
6 | |
const Allocator = std.mem.Allocator;
|
|
7 |
const mem = std.mem;
|
|
8 |
const Allocator = mem.Allocator;
|
7 | 9 |
|
8 | 10 |
pub fn main() !void {
|
9 | 11 |
try renderdoc.load_api();
|
|
12 | 14 |
session.deinit();
|
13 | 15 |
}
|
14 | 16 |
|
15 | |
const Paths = struct {
|
16 | |
user_hand_right: xr.Path,
|
17 | |
user_hand_left: xr.Path,
|
18 | |
user_hand_head: xr.Path,
|
19 | |
|
20 | |
pub fn init(xri: xr.InstanceDispatch, inst: xr.Instance) !Paths {
|
21 | |
var self: Paths = undefined;
|
22 | |
|
23 | |
inline for (@typeInfo(Paths).Struct.fields) |field| {
|
24 | |
var buffer: [field.name.len + 2:0]u8 = undefined;
|
25 | |
_ = std.mem.replace(u8, field.name, "_", "/", buffer[1..]);
|
26 | |
buffer[0] = '/';
|
27 | |
buffer[buffer.len - 1] = 0;
|
28 | |
@field(self, field.name) = try xri.stringToPath(inst, buffer[0..]);
|
29 | |
}
|
|
17 |
const HandState = struct {
|
|
18 |
subaction_path: xr.Path,
|
|
19 |
handedness: []const u8,
|
|
20 |
space: xr.Space,
|
|
21 |
|
|
22 |
active: bool,
|
|
23 |
grab: ?f32,
|
|
24 |
pose: ?glm.Mat4,
|
|
25 |
quit: ?bool,
|
|
26 |
vibrate: ?xr.HapticVibration,
|
|
27 |
|
|
28 |
pub fn init(path: xr.Path, handedness: []const u8, space: xr.Space) HandState {
|
|
29 |
return .{
|
|
30 |
.subaction_path = path,
|
|
31 |
.space = space,
|
|
32 |
.handedness = handedness,
|
|
33 |
|
|
34 |
.active = false,
|
|
35 |
.grab = null,
|
|
36 |
.pose = null,
|
|
37 |
.quit = null,
|
|
38 |
.vibrate = null,
|
|
39 |
};
|
|
40 |
}
|
|
41 |
|
|
42 |
pub fn sync(self: *HandState, actions: *const Actions) !void {
|
|
43 |
const xri = actions.xri;
|
|
44 |
const session = actions.session;
|
|
45 |
|
|
46 |
var grab = xr.ActionStateFloat.empty();
|
|
47 |
var pose = xr.ActionStatePose.empty();
|
|
48 |
var quit = xr.ActionStateBoolean.empty();
|
|
49 |
|
|
50 |
try xri.getActionStateFloat(session, .{
|
|
51 |
.action = actions.grab_object,
|
|
52 |
.subaction_path = self.subaction_path,
|
|
53 |
}, &grab);
|
|
54 |
self.grab = if (grab.is_active == xr.TRUE) grab.current_state else null;
|
|
55 |
|
|
56 |
try xri.getActionStatePose(session, .{
|
|
57 |
.action = actions.grip_pose,
|
|
58 |
.subaction_path = self.subaction_path,
|
|
59 |
}, &pose);
|
|
60 |
self.active = pose.is_active == xr.TRUE;
|
|
61 |
|
|
62 |
try xri.getActionStateBoolean(session, .{
|
|
63 |
.action = actions.quit_session,
|
|
64 |
.subaction_path = self.subaction_path,
|
|
65 |
}, &quit);
|
|
66 |
self.quit = if (quit.is_active == xr.TRUE) quit.current_state == xr.TRUE else null;
|
|
67 |
}
|
|
68 |
|
|
69 |
pub fn locate(
|
|
70 |
self: *HandState,
|
|
71 |
xri: xr.InstanceDispatch,
|
|
72 |
reference_space: xr.Space,
|
|
73 |
query_time: xr.Time,
|
|
74 |
) !void {
|
|
75 |
if (!self.active) {
|
|
76 |
self.pose = null;
|
|
77 |
return;
|
|
78 |
}
|
|
79 |
|
|
80 |
var location = xr.SpaceLocation.empty();
|
|
81 |
try xri.locateSpace(self.space, reference_space, query_time, &location);
|
|
82 |
|
|
83 |
if (!location.location_flags.contains(.{
|
|
84 |
.position_valid_bit = true,
|
|
85 |
.orientation_valid_bit = true,
|
|
86 |
})) {
|
|
87 |
self.pose = null;
|
|
88 |
return;
|
|
89 |
}
|
|
90 |
|
|
91 |
self.pose = math.pose2mat(location.pose);
|
|
92 |
}
|
|
93 |
|
|
94 |
pub fn applyOutput(self: *const HandState, actions: *const Actions) !void {
|
|
95 |
if (self.vibrate) |vibration| {
|
|
96 |
try xri.applyHapticFeedback(
|
|
97 |
session,
|
|
98 |
.{
|
|
99 |
.action = self.actions.vibrate,
|
|
100 |
.subaction_path = self.subaction_path,
|
|
101 |
},
|
|
102 |
@ptrCast(*const xr.HapticBaseHeader, &vibration),
|
|
103 |
);
|
|
104 |
}
|
|
105 |
}
|
|
106 |
};
|
|
107 |
|
|
108 |
const Actions = struct {
|
|
109 |
xri: xr.InstanceDispatch,
|
|
110 |
session: xr.Session,
|
|
111 |
|
|
112 |
set: xr.ActionSet,
|
|
113 |
grab_object: xr.Action,
|
|
114 |
grip_pose: xr.Action,
|
|
115 |
quit_session: xr.Action,
|
|
116 |
vibrate: xr.Action,
|
|
117 |
|
|
118 |
hands: [2]HandState,
|
|
119 |
|
|
120 |
pub fn init(
|
|
121 |
xri: xr.InstanceDispatch,
|
|
122 |
instance: xr.Instance,
|
|
123 |
session: xr.Session,
|
|
124 |
) !Actions {
|
|
125 |
var self: Actions = undefined;
|
|
126 |
self.xri = xri;
|
|
127 |
self.session = session;
|
|
128 |
|
|
129 |
const subaction_strs = [_][:0]const u8{ "/user/hand/right", "/user/hand/left" };
|
|
130 |
const handedness_strs = [_][:0]const u8{ "right", "left" };
|
|
131 |
var subaction_paths: [subaction_strs.len]xr.Path = undefined;
|
|
132 |
for (subaction_strs) |str, i| {
|
|
133 |
subaction_paths[i] = try xri.stringToPath(instance, str);
|
|
134 |
}
|
|
135 |
|
|
136 |
var set_ci = xr.ActionSetCreateInfo{
|
|
137 |
.action_set_name = undefined,
|
|
138 |
.localized_action_set_name = undefined,
|
|
139 |
.priority = 0,
|
|
140 |
};
|
|
141 |
mem.copy(u8, set_ci.action_set_name[0..], "gameplay\x00");
|
|
142 |
mem.copy(u8, set_ci.localized_action_set_name[0..], "Gameplay\x00");
|
|
143 |
self.set = try xri.createActionSet(instance, set_ci);
|
|
144 |
|
|
145 |
{
|
|
146 |
// create actions
|
|
147 |
var action_ci = xr.ActionCreateInfo{
|
|
148 |
.action_type = undefined,
|
|
149 |
.action_name = undefined,
|
|
150 |
.localized_action_name = undefined,
|
|
151 |
.count_subaction_paths = subaction_paths.len,
|
|
152 |
.subaction_paths = &subaction_paths,
|
|
153 |
};
|
|
154 |
|
|
155 |
action_ci.action_type = .float_input;
|
|
156 |
mem.copy(u8, action_ci.action_name[0..], "grab_object\x00");
|
|
157 |
mem.copy(u8, action_ci.localized_action_name[0..], "Grab Object\x00");
|
|
158 |
self.grab_object = try xri.createAction(self.set, action_ci);
|
|
159 |
|
|
160 |
action_ci.action_type = .pose_input;
|
|
161 |
mem.copy(u8, action_ci.action_name[0..], "grip_pose\x00");
|
|
162 |
mem.copy(u8, action_ci.localized_action_name[0..], "Grip Pose\x00");
|
|
163 |
self.grip_pose = try xri.createAction(self.set, action_ci);
|
|
164 |
|
|
165 |
action_ci.action_type = .boolean_input;
|
|
166 |
mem.copy(u8, action_ci.action_name[0..], "quit_session\x00");
|
|
167 |
mem.copy(u8, action_ci.localized_action_name[0..], "Quit Session\x00");
|
|
168 |
self.quit_session = try xri.createAction(self.set, action_ci);
|
|
169 |
|
|
170 |
action_ci.action_type = .vibration_output;
|
|
171 |
mem.copy(u8, action_ci.action_name[0..], "vibrate_hand\x00");
|
|
172 |
mem.copy(u8, action_ci.localized_action_name[0..], "Vibrate Hand\x00");
|
|
173 |
self.vibrate = try xri.createAction(self.set, action_ci);
|
|
174 |
}
|
|
175 |
|
|
176 |
{
|
|
177 |
// suggested bindings
|
|
178 |
const PathStruct = struct {
|
|
179 |
squeeze_force: xr.Path,
|
|
180 |
pose: xr.Path,
|
|
181 |
b_click: xr.Path,
|
|
182 |
haptic: xr.Path,
|
|
183 |
};
|
|
184 |
|
|
185 |
var paths: [subaction_strs.len]PathStruct = undefined;
|
|
186 |
for (subaction_strs) |subaction, i| {
|
|
187 |
var path_buffer = [_]u8{0} ** 2048;
|
|
188 |
var string: [:0]const u8 = undefined;
|
|
189 |
|
|
190 |
string = try std.fmt.bufPrintZ(path_buffer[0..], "{}/input/squeeze/force", .{subaction});
|
|
191 |
const squeeze_force = try xri.stringToPath(instance, string.ptr);
|
|
192 |
|
|
193 |
string = try std.fmt.bufPrintZ(path_buffer[0..], "{}/input/grip/pose", .{subaction});
|
|
194 |
const pose = try xri.stringToPath(instance, string.ptr);
|
|
195 |
|
|
196 |
string = try std.fmt.bufPrintZ(path_buffer[0..], "{}/input/b/click", .{subaction});
|
|
197 |
const b_click = try xri.stringToPath(instance, string.ptr);
|
|
198 |
|
|
199 |
string = try std.fmt.bufPrintZ(path_buffer[0..], "{}/output/haptic", .{subaction});
|
|
200 |
const haptic = try xri.stringToPath(instance, string.ptr);
|
|
201 |
|
|
202 |
paths[i] = .{
|
|
203 |
.squeeze_force = squeeze_force,
|
|
204 |
.pose = pose,
|
|
205 |
.b_click = b_click,
|
|
206 |
.haptic = haptic,
|
|
207 |
};
|
|
208 |
}
|
|
209 |
|
|
210 |
const index_profile = try xri.stringToPath(instance, "/interaction_profiles/valve/index_controller");
|
|
211 |
const bindings = [_]xr.ActionSuggestedBinding{
|
|
212 |
.{ .action = self.grab_object, .binding = paths[0].squeeze_force },
|
|
213 |
.{ .action = self.grab_object, .binding = paths[1].squeeze_force },
|
|
214 |
.{ .action = self.grip_pose, .binding = paths[0].pose },
|
|
215 |
.{ .action = self.grip_pose, .binding = paths[1].pose },
|
|
216 |
.{ .action = self.quit_session, .binding = paths[0].b_click },
|
|
217 |
.{ .action = self.quit_session, .binding = paths[1].b_click },
|
|
218 |
.{ .action = self.vibrate, .binding = paths[0].haptic },
|
|
219 |
.{ .action = self.vibrate, .binding = paths[1].haptic },
|
|
220 |
};
|
|
221 |
try xri.suggestInteractionProfileBindings(instance, .{
|
|
222 |
.interaction_profile = index_profile,
|
|
223 |
.count_suggested_bindings = bindings.len,
|
|
224 |
.suggested_bindings = &bindings,
|
|
225 |
});
|
|
226 |
}
|
|
227 |
|
|
228 |
for (subaction_paths) |path, i| {
|
|
229 |
const handedness = handedness_strs[i];
|
|
230 |
const space = try xri.createActionSpace(session, .{
|
|
231 |
.action = self.grip_pose,
|
|
232 |
.pose_in_action_space = .{
|
|
233 |
// "origin": [-0.005154, 0.013042, 0.107171],
|
|
234 |
// "rotate_xyz" : [93.782, 0.0, 0.0]
|
|
235 |
// .position = .{ .x = 0.005154, .y = -0.013042, .z = -0.107171 },
|
|
236 |
// .orientation = .{ .x = 0.7300549, .y = 0, .z = 0, .w = 0.6833885 },
|
|
237 |
},
|
|
238 |
.subaction_path = path,
|
|
239 |
});
|
|
240 |
self.hands[i] = HandState.init(path, handedness, space);
|
|
241 |
}
|
|
242 |
|
|
243 |
try xri.attachSessionActionSets(session, .{
|
|
244 |
.count_action_sets = 1,
|
|
245 |
.action_sets = @ptrCast([*]xr.ActionSet, &self.set),
|
|
246 |
});
|
30 | 247 |
|
31 | 248 |
return self;
|
|
249 |
}
|
|
250 |
|
|
251 |
pub fn sync(self: *Actions) !void {
|
|
252 |
const active_sets = [_]xr.ActiveActionSet{
|
|
253 |
.{ .action_set = self.set, .subaction_path = 0 },
|
|
254 |
};
|
|
255 |
_ = try self.xri.syncActions(self.session, .{
|
|
256 |
.count_active_action_sets = active_sets.len,
|
|
257 |
.active_action_sets = &active_sets,
|
|
258 |
});
|
|
259 |
|
|
260 |
for (self.hands) |*hand| {
|
|
261 |
try hand.sync(self);
|
|
262 |
}
|
32 | 263 |
}
|
33 | 264 |
};
|
34 | 265 |
|
35 | 266 |
const Session = struct {
|
36 | 267 |
xrb: xr.BaseDispatch,
|
37 | 268 |
xri: xr.InstanceDispatch,
|
|
269 |
xrcmi: ?xr.CMInstanceDispatch,
|
38 | 270 |
|
39 | 271 |
instance: xr.Instance,
|
40 | 272 |
session: xr.Session,
|
|
42 | 274 |
|
43 | 275 |
allocator: *Allocator,
|
44 | 276 |
|
|
277 |
actions: Actions,
|
45 | 278 |
state: xr.SessionState,
|
46 | 279 |
views: ?ViewInfo,
|
47 | |
paths: Paths,
|
48 | 280 |
graphics: gfx.Graphics,
|
49 | 281 |
layers: []*const xr.CompositionLayerBaseHeader,
|
50 | 282 |
scene_space: xr.Space,
|
51 | 283 |
|
52 | |
first_render: bool = true,
|
|
284 |
hand_models: [2]?*scene.Model,
|
53 | 285 |
|
54 | 286 |
const ViewInfo = struct {
|
55 | 287 |
configurations: []xr.ViewConfigurationView,
|
|
58 | 290 |
|
59 | 291 |
pub fn init(allocator: *Allocator) !Session {
|
60 | 292 |
var name: [128]u8 = undefined;
|
61 | |
std.mem.copy(u8, name[0..], "openxr-zig-test" ++ [_]u8{0});
|
|
293 |
mem.copy(u8, name[0..], "openxr-zig-test" ++ [_]u8{0});
|
|
294 |
|
|
295 |
const xrb = try xr.BaseDispatch.load(xr.getProcAddr);
|
|
296 |
|
|
297 |
const have_controller_model_ext = blk: {
|
|
298 |
var extensions = [_]xr.ExtensionProperties{ xr.ExtensionProperties.empty() } ** 512;
|
|
299 |
const extension_count = try xrb.enumerateInstanceExtensionProperties(null, extensions.len, &extensions);
|
|
300 |
for (extensions[0..extension_count]) |ext| {
|
|
301 |
const ext_name = @ptrCast([*:0]const u8, ext.extension_name[0..]);
|
|
302 |
if (mem.eql(u8, mem.spanZ(ext_name), "XR_MSFT_controller_model")) {
|
|
303 |
std.debug.print("found XR_MSFT_controller_model extension version {}\n", .{ ext.extension_version },);
|
|
304 |
break :blk true;
|
|
305 |
}
|
|
306 |
}
|
|
307 |
|
|
308 |
break :blk false;
|
|
309 |
};
|
62 | 310 |
|
63 | 311 |
const zero = [_:0]u8{};
|
64 | 312 |
const extensions = [_][*:0]const u8{
|
|
66 | 314 |
"XR_MSFT_controller_model",
|
67 | 315 |
};
|
68 | 316 |
|
69 | |
const xrb = try xr.BaseDispatch.load(xr.getProcAddr);
|
70 | 317 |
const instance = try xrb.createInstance(.{
|
71 | 318 |
.create_flags = .{},
|
72 | 319 |
.application_info = .{
|
|
78 | 325 |
},
|
79 | 326 |
.enabled_api_layer_count = 0,
|
80 | 327 |
.enabled_api_layer_names = @ptrCast([*]const [*:0]const u8, &zero),
|
81 | |
.enabled_extension_count = extensions.len,
|
|
328 |
.enabled_extension_count = if (have_controller_model_ext) 2 else 1,
|
82 | 329 |
.enabled_extension_names = &extensions,
|
83 | 330 |
});
|
84 | 331 |
|
85 | 332 |
const xri = try xr.InstanceDispatch.load(instance, xr.getProcAddr);
|
86 | 333 |
errdefer xri.destroyInstance(instance) catch unreachable;
|
87 | 334 |
|
88 | |
const paths = try Paths.init(xri, instance);
|
|
335 |
var xrcmi: ?xr.CMInstanceDispatch = null;
|
|
336 |
if (have_controller_model_ext) {
|
|
337 |
xrcmi = try xr.CMInstanceDispatch.load(instance, xr.getProcAddr);
|
|
338 |
}
|
89 | 339 |
|
90 | 340 |
const system = try xri.getSystem(instance, .{ .form_factor = .head_mounted_display });
|
91 | 341 |
|
|
125 | 375 |
});
|
126 | 376 |
errdefer xri.destroySession(session) catch unreachable;
|
127 | 377 |
|
|
378 |
const actions = try Actions.init(xri, instance, session);
|
|
379 |
|
128 | 380 |
const scene_space = try xri.createReferenceSpace(session, .{
|
129 | 381 |
.reference_space_type = .stage,
|
130 | 382 |
.pose_in_reference_space = .{
|
|
135 | 387 |
return Session{
|
136 | 388 |
.xrb = xrb,
|
137 | 389 |
.xri = xri,
|
|
390 |
.xrcmi = xrcmi,
|
138 | 391 |
|
139 | 392 |
.instance = instance,
|
140 | 393 |
.session = session,
|
141 | 394 |
.system = system,
|
142 | 395 |
.allocator = allocator,
|
143 | 396 |
|
|
397 |
.actions = actions,
|
144 | 398 |
.state = .idle,
|
145 | 399 |
.views = null,
|
146 | |
.paths = paths,
|
147 | 400 |
.graphics = graphics,
|
148 | 401 |
.scene_space = scene_space,
|
149 | 402 |
.layers = &[_]*const xr.CompositionLayerBaseHeader{},
|
|
403 |
|
|
404 |
.hand_models = [_]?*scene.Model{ null, null },
|
150 | 405 |
};
|
151 | 406 |
}
|
152 | 407 |
|
|
224 | 479 |
.projections = try self.allocator.alloc(xr.CompositionLayerProjectionView, view_count),
|
225 | 480 |
};
|
226 | 481 |
|
227 | |
std.mem.set(
|
|
482 |
mem.set(
|
228 | 483 |
xr.ViewConfigurationView,
|
229 | 484 |
views.configurations,
|
230 | 485 |
xr.ViewConfigurationView.empty(),
|
231 | 486 |
);
|
232 | |
std.mem.set(
|
|
487 |
mem.set(
|
233 | 488 |
xr.CompositionLayerProjectionView,
|
234 | 489 |
views.projections,
|
235 | 490 |
xr.CompositionLayerProjectionView.empty(),
|
|
250 | 505 |
try self.graphics.createSwapchain(self.session, views.configurations);
|
251 | 506 |
var gfx_scene = try scene.Scene.init(&self.graphics);
|
252 | 507 |
|
253 | |
var helmet = scene.Model.initFile(&gfx_scene, "assets/DamagedHelmet.gltf");
|
|
508 |
var helmet = scene.Model.initFile(self.allocator, &gfx_scene, "assets/DamagedHelmet.gltf");
|
254 | 509 |
helmet.setTransform(glm.translation(glm.Vec3.init([_]f32{ 0, 1.5, -2 })));
|
255 | 510 |
try gfx_scene.models.append(helmet);
|
256 | 511 |
|
257 | |
for ([_]xr.Path{ self.paths.user_hand_left, self.paths.user_hand_right }) |path, i| {
|
258 | |
var model_state = xr.ControllerModelKeyStateMSFT.empty();
|
259 | |
try self.xri.getControllerModelKeyMSFT(self.session, path, &model_state);
|
260 | |
|
261 | |
if (model_state.model_key == 0)
|
262 | |
continue;
|
263 | |
|
264 | |
const size = try self.xri.loadControllerModelMSFT(self.session, model_state.model_key, 0, null);
|
265 | |
const buffer = try self.allocator.alloc(u8, size);
|
266 | |
defer self.allocator.free(buffer);
|
267 | |
|
268 | |
_ = try self.xri.loadControllerModelMSFT(self.session, model_state.model_key, size, buffer.ptr);
|
269 | |
var model = scene.Model.initMemory(&gfx_scene, buffer);
|
270 | |
|
271 | |
var transform = glm.Mat4.IDENTITY;
|
272 | |
transform = transform.mul(glm.translation(glm.Vec3.init([_]f32{ 0.6 * (@intToFloat(f32, i) - 0.5), 1.5, -0.5 })));
|
273 | |
transform = transform.mul(glm.rotation(0.9, glm.Vec3.init([_]f32{ 1, 0, 0 })));
|
274 | |
model.setTransform(transform);
|
275 | |
try gfx_scene.models.append(model);
|
276 | |
}
|
|
512 |
helmet = scene.Model.initFile(self.allocator, &gfx_scene, "assets/controller_right.glb");
|
|
513 |
helmet.setTransform(
|
|
514 |
glm.translation(glm.Vec3.init([_]f32{ -0.07, 1.6, -0.2 }))
|
|
515 |
.mul(glm.rotation(0.8, glm.Vec3.init([_]f32{ 1, 0, 0 })))
|
|
516 |
);
|
|
517 |
try helmet.loadNodeTransform("r_button_b");
|
|
518 |
try helmet.loadNodeTransform("r_trackpad_touch");
|
|
519 |
try gfx_scene.models.append(helmet);
|
277 | 520 |
|
278 | 521 |
const composition_layer = try self.allocator.create(xr.CompositionLayerProjection);
|
279 | 522 |
composition_layer.* = .{
|
|
290 | 533 |
while (true) {
|
291 | 534 |
try self.pollEvents();
|
292 | 535 |
if (self.state == .stopping) break;
|
293 | |
|
294 | |
if (!try self.renderFrame(&gfx_scene)) {
|
|
536 |
try self.actions.sync();
|
|
537 |
|
|
538 |
const did_render = try self.renderFrame(&gfx_scene);
|
|
539 |
if (!did_render) {
|
|
540 |
// no frame submitted, so compositor won't throttle by waiting for GPU
|
|
541 |
// sleep to prevent busy-loop
|
295 | 542 |
std.os.nanosleep(0, 250_000_000);
|
296 | 543 |
}
|
297 | 544 |
}
|
|
312 | 559 |
transform = transform.mul(glm.rotation(predicted_seconds, glm.Vec3.init([_]f32{ 0, 1, 0 })));
|
313 | 560 |
gfx_scene.models.items[0].setTransform(transform);
|
314 | 561 |
|
|
562 |
{
|
|
563 |
const model = &gfx_scene.models.items[1];
|
|
564 |
var txm = &model.node_transforms.items[0];
|
|
565 |
// motion.type == translate:l
|
|
566 |
// apply translation axis * lerp(in, value_mapping)
|
|
567 |
txm.position = glm.Vec3.init([_]f32{0, -0.927, 0.0375}).mulScalar(
|
|
568 |
(std.math.sin(predicted_seconds * 3) / 2 + 0.5) * 0.002
|
|
569 |
);
|
|
570 |
|
|
571 |
var tp_mot_rot = glm.Mat4.IDENTITY;
|
|
572 |
// motion.type == trackpad: only apply negative component_local transform
|
|
573 |
// apply translation axis * lerp(in, value_mapping)
|
|
574 |
tp_mot_rot.mulAssign(glm.rotation(1.18682389, glm.Vec3.init([_]f32{ 1, 0, 0 })));
|
|
575 |
tp_mot_rot.mulAssign(math.quat2mat(xr.Quaternionf{ .x=-0.17349398, .y=-0.79472193, .z=0.32537197, .w=-0.48213067 }));
|
|
576 |
tp_mot_rot.mulAssign(glm.rotation(-1.18682389, glm.Vec3.init([_]f32{ 1, 0, 0 })));
|
|
577 |
|
|
578 |
txm = &model.node_transforms.items[1];
|
|
579 |
txm.position =
|
|
580 |
tp_mot_rot.apply(
|
|
581 |
glm.Vec4.init([_]f32{
|
|
582 |
std.math.clamp(std.math.sin(predicted_seconds * 3) * 2, -1, 1) * 0.009,
|
|
583 |
std.math.clamp(std.math.cos(predicted_seconds * 3) * 2, -1, 1) * 0.015,
|
|
584 |
0,
|
|
585 |
1
|
|
586 |
})
|
|
587 |
).shrink(3);
|
|
588 |
// txm.rotation = glm.Vec4.init([_]f32{ -0.17349398, -0.79472193, 0.32537197, -0.48213067 });
|
|
589 |
|
|
590 |
model.flushTransforms();
|
|
591 |
std.debug.print("transform pos: {}\n", .{ model.node_transforms.items[0].position });
|
|
592 |
}
|
|
593 |
|
315 | 594 |
var layer_count: u32 = 0;
|
316 | 595 |
|
317 | 596 |
if (frame_state.should_render > 0) {
|
|
328 | 607 |
|
329 | 608 |
if (!view_state.view_state_flags.contains(.{ .position_valid_bit = true, .orientation_valid_bit = true })) {
|
330 | 609 |
return error.InvalidPositionOrOrientation;
|
|
610 |
}
|
|
611 |
|
|
612 |
for (self.actions.hands) |*hand, i| {
|
|
613 |
try hand.locate(self.xri, self.scene_space, frame_state.predicted_display_time);
|
|
614 |
if (hand.active and self.hand_models[i] == null) {
|
|
615 |
var model = if (self.xrcmi) |xri| model: {
|
|
616 |
var model_state = xr.ControllerModelKeyStateMSFT.empty();
|
|
617 |
try xri.getControllerModelKeyMSFT(self.session, hand.subaction_path, &model_state);
|
|
618 |
|
|
619 |
if (model_state.model_key == 0)
|
|
620 |
continue;
|
|
621 |
|
|
622 |
const size = try xri.loadControllerModelMSFT(self.session, model_state.model_key, 0, null);
|
|
623 |
const buffer = try self.allocator.alloc(u8, size);
|
|
624 |
defer self.allocator.free(buffer);
|
|
625 |
|
|
626 |
_ = try xri.loadControllerModelMSFT(self.session, model_state.model_key, size, buffer.ptr);
|
|
627 |
break :model scene.Model.initMemory(self.allocator, gfx_scene, buffer);
|
|
628 |
} else model: {
|
|
629 |
var path_buffer = [_]u8{0} ** 128;
|
|
630 |
const path = try std.fmt.bufPrintZ(path_buffer[0..], "assets/controller_{}.glb", .{hand.handedness});
|
|
631 |
break :model scene.Model.initFile(self.allocator, gfx_scene, path);
|
|
632 |
};
|
|
633 |
|
|
634 |
const ptr = try gfx_scene.models.addOne();
|
|
635 |
ptr.* = model;
|
|
636 |
self.hand_models[i] = ptr;
|
|
637 |
}
|
|
638 |
|
|
639 |
if (hand.pose) |pose| {
|
|
640 |
if (self.hand_models[i]) |model| {
|
|
641 |
model.setTransform(pose);
|
|
642 |
}
|
|
643 |
}
|
331 | 644 |
}
|
332 | 645 |
|
333 | 646 |
const swapchain = self.graphics.swapchain;
|