Blender V4.3
wm_xr_action.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
14#include "BLI_listbase.h"
15#include "BLI_math_matrix.h"
16#include "BLI_math_rotation.h"
17#include "BLI_math_vector.h"
18#include "BLI_string.h"
19
20#include "GHOST_C-api.h"
21
22#include "MEM_guardedalloc.h"
23
24#include "WM_api.hh"
25#include "WM_types.hh"
26
27#include "wm_xr_intern.hh"
28
29#include <cstring>
30
31/* -------------------------------------------------------------------- */
38static wmXrActionSet *action_set_create(const char *action_set_name)
39{
40 wmXrActionSet *action_set = static_cast<wmXrActionSet *>(
41 MEM_callocN(sizeof(*action_set), __func__));
42 action_set->name = BLI_strdup(action_set_name);
43 return action_set;
44}
45
46static void action_set_destroy(void *val)
47{
48 wmXrActionSet *action_set = static_cast<wmXrActionSet *>(val);
49
50 MEM_SAFE_FREE(action_set->name);
51
54
55 MEM_freeN(action_set);
56}
57
58static wmXrActionSet *action_set_find(wmXrData *xr, const char *action_set_name)
59{
60 return static_cast<wmXrActionSet *>(
61 GHOST_XrGetActionSetCustomdata(xr->runtime->context, action_set_name));
62}
63
64static wmXrAction *action_create(const char *action_name,
65 eXrActionType type,
66 const ListBase *user_paths,
68 IDProperty *op_properties,
69 const char *haptic_name,
70 const int64_t *haptic_duration,
71 const float *haptic_frequency,
72 const float *haptic_amplitude,
73 eXrOpFlag op_flag,
74 eXrActionFlag action_flag,
75 eXrHapticFlag haptic_flag)
76{
77 wmXrAction *action = static_cast<wmXrAction *>(MEM_callocN(sizeof(*action), __func__));
78 action->name = BLI_strdup(action_name);
79 action->type = type;
80
81 const uint count = uint(BLI_listbase_count(user_paths));
82 uint subaction_idx = 0;
84
85 action->subaction_paths = static_cast<char **>(
86 MEM_mallocN(sizeof(*action->subaction_paths) * count, "XrAction_SubactionPaths"));
87 LISTBASE_FOREACH_INDEX (XrUserPath *, user_path, user_paths, subaction_idx) {
88 action->subaction_paths[subaction_idx] = BLI_strdup(user_path->path);
89 }
90
91 size_t size;
92 switch (type) {
94 size = sizeof(bool);
95 break;
96 case XR_FLOAT_INPUT:
97 size = sizeof(float);
98 break;
100 size = sizeof(float) * 2;
101 break;
102 case XR_POSE_INPUT:
103 size = sizeof(GHOST_XrPose);
104 break;
106 return action;
107 }
108 action->states = MEM_calloc_arrayN(count, size, "XrAction_States");
109 action->states_prev = MEM_calloc_arrayN(count, size, "XrAction_StatesPrev");
110
111 const bool is_float_action = ELEM(type, XR_FLOAT_INPUT, XR_VECTOR2F_INPUT);
112 const bool is_button_action = (is_float_action || type == XR_BOOLEAN_INPUT);
113 if (is_float_action) {
114 action->float_thresholds = static_cast<float *>(
115 MEM_calloc_arrayN(count, sizeof(*action->float_thresholds), "XrAction_FloatThresholds"));
116 }
117 if (is_button_action) {
118 action->axis_flags = static_cast<eXrAxisFlag *>(
119 MEM_calloc_arrayN(count, sizeof(*action->axis_flags), "XrAction_AxisFlags"));
120 }
121
122 action->ot = ot;
123 action->op_properties = op_properties;
124
125 if (haptic_name) {
126 BLI_assert(is_button_action);
127 action->haptic_name = BLI_strdup(haptic_name);
128 action->haptic_duration = *haptic_duration;
129 action->haptic_frequency = *haptic_frequency;
130 action->haptic_amplitude = *haptic_amplitude;
131 }
132
133 action->op_flag = op_flag;
134 action->action_flag = action_flag;
135 action->haptic_flag = haptic_flag;
136
137 return action;
138}
139
140static void action_destroy(void *val)
141{
142 wmXrAction *action = static_cast<wmXrAction *>(val);
143
144 MEM_SAFE_FREE(action->name);
145
146 char **subaction_paths = action->subaction_paths;
147 if (subaction_paths) {
148 for (uint i = 0; i < action->count_subaction_paths; ++i) {
149 MEM_SAFE_FREE(subaction_paths[i]);
150 }
151 MEM_freeN(subaction_paths);
152 }
153
154 MEM_SAFE_FREE(action->states);
155 MEM_SAFE_FREE(action->states_prev);
156
158 MEM_SAFE_FREE(action->axis_flags);
159
160 MEM_SAFE_FREE(action->haptic_name);
161
162 MEM_freeN(action);
163}
164
165static wmXrAction *action_find(wmXrData *xr, const char *action_set_name, const char *action_name)
166{
167 return static_cast<wmXrAction *>(
168 GHOST_XrGetActionCustomdata(xr->runtime->context, action_set_name, action_name));
169}
170
171bool WM_xr_action_set_create(wmXrData *xr, const char *action_set_name)
172{
173 if (action_set_find(xr, action_set_name)) {
174 return false;
175 }
176
177 wmXrActionSet *action_set = action_set_create(action_set_name);
178
179 GHOST_XrActionSetInfo info{};
180 info.name = action_set_name;
181 info.customdata_free_fn = action_set_destroy;
182 info.customdata = action_set;
183
184 if (!GHOST_XrCreateActionSet(xr->runtime->context, &info)) {
185 return false;
186 }
187
188 return true;
189}
190
191void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name)
192{
193 wmXrActionSet *action_set = action_set_find(xr, action_set_name);
194 if (!action_set) {
195 return;
196 }
197
198 wmXrSessionState *session_state = &xr->runtime->session_state;
199
200 if (action_set == session_state->active_action_set) {
201 if (action_set->controller_grip_action || action_set->controller_aim_action) {
203 action_set->controller_grip_action = action_set->controller_aim_action = nullptr;
204 }
205
208
209 session_state->active_action_set = nullptr;
210 }
211
212 GHOST_XrDestroyActionSet(xr->runtime->context, action_set_name);
213}
214
216 const char *action_set_name,
217 const char *action_name,
218 eXrActionType type,
219 const ListBase *user_paths,
221 IDProperty *op_properties,
222 const char *haptic_name,
223 const int64_t *haptic_duration,
224 const float *haptic_frequency,
225 const float *haptic_amplitude,
226 eXrOpFlag op_flag,
227 eXrActionFlag action_flag,
228 eXrHapticFlag haptic_flag)
229{
230 if (action_find(xr, action_set_name, action_name)) {
231 return false;
232 }
233
234 wmXrAction *action = action_create(action_name,
235 type,
236 user_paths,
237 ot,
238 op_properties,
239 haptic_name,
240 haptic_duration,
241 haptic_frequency,
242 haptic_amplitude,
243 op_flag,
244 action_flag,
245 haptic_flag);
246
247 const uint count = uint(BLI_listbase_count(user_paths));
248 uint subaction_idx = 0;
249
250 char **subaction_paths = static_cast<char **>(
251 MEM_calloc_arrayN(count, sizeof(*subaction_paths), "XrAction_SubactionPathPointers"));
252
253 LISTBASE_FOREACH_INDEX (XrUserPath *, user_path, user_paths, subaction_idx) {
254 subaction_paths[subaction_idx] = (char *)user_path->path;
255 }
256
257 GHOST_XrActionInfo info{};
258 info.name = action_name;
259 info.count_subaction_paths = count;
260 info.subaction_paths = (const char **)subaction_paths;
261 info.states = action->states;
262 info.float_thresholds = action->float_thresholds;
263 info.axis_flags = (int16_t *)action->axis_flags;
264 info.customdata_free_fn = action_destroy;
265 info.customdata = action;
266
267 switch (type) {
268 case XR_BOOLEAN_INPUT:
269 info.type = GHOST_kXrActionTypeBooleanInput;
270 break;
271 case XR_FLOAT_INPUT:
272 info.type = GHOST_kXrActionTypeFloatInput;
273 break;
275 info.type = GHOST_kXrActionTypeVector2fInput;
276 break;
277 case XR_POSE_INPUT:
278 info.type = GHOST_kXrActionTypePoseInput;
279 break;
281 info.type = GHOST_kXrActionTypeVibrationOutput;
282 break;
283 }
284
285 const bool success = GHOST_XrCreateActions(xr->runtime->context, action_set_name, 1, &info);
286
287 MEM_freeN(subaction_paths);
288
289 return success;
290}
291
292void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name)
293{
294 wmXrActionSet *action_set = action_set_find(xr, action_set_name);
295 if (!action_set) {
296 return;
297 }
298
299 wmXrAction *action = action_find(xr, action_set_name, action_name);
300 if (!action) {
301 return;
302 }
303
304 if ((action_set->controller_grip_action &&
305 STREQ(action_set->controller_grip_action->name, action_name)) ||
306 (action_set->controller_aim_action &&
307 STREQ(action_set->controller_aim_action->name, action_name)))
308 {
309 if (action_set == xr->runtime->session_state.active_action_set) {
311 }
312 action_set->controller_grip_action = action_set->controller_aim_action = nullptr;
313 }
314
315 LISTBASE_FOREACH (LinkData *, ld, &action_set->active_modal_actions) {
316 wmXrAction *active_modal_action = static_cast<wmXrAction *>(ld->data);
317 if (STREQ(active_modal_action->name, action_name)) {
318 BLI_freelinkN(&action_set->active_modal_actions, ld);
319 break;
320 }
321 }
322
324 if (STREQ(ha->action->name, action_name)) {
325 BLI_freelinkN(&action_set->active_haptic_actions, ha);
326 }
327 }
328
329 GHOST_XrDestroyActions(xr->runtime->context, action_set_name, 1, &action_name);
330}
331
333 const char *action_set_name,
334 const char *action_name,
335 const char *profile_path,
336 const ListBase *user_paths,
337 const ListBase *component_paths,
338 const float *float_thresholds,
339 const eXrAxisFlag *axis_flags,
340 const wmXrPose *poses)
341{
342 const uint count = uint(BLI_listbase_count(user_paths));
343 BLI_assert(count == uint(BLI_listbase_count(component_paths)));
344
345 GHOST_XrActionBindingInfo *binding_infos = static_cast<GHOST_XrActionBindingInfo *>(
346 MEM_calloc_arrayN(count, sizeof(*binding_infos), "XrActionBinding_Infos"));
347
348 char **subaction_paths = static_cast<char **>(
349 MEM_calloc_arrayN(count, sizeof(*subaction_paths), "XrActionBinding_SubactionPathPointers"));
350
351 for (uint i = 0; i < count; ++i) {
352 GHOST_XrActionBindingInfo *binding_info = &binding_infos[i];
353 const XrUserPath *user_path = static_cast<const XrUserPath *>(BLI_findlink(user_paths, i));
354 const XrComponentPath *component_path = static_cast<const XrComponentPath *>(
355 BLI_findlink(component_paths, i));
356
357 subaction_paths[i] = (char *)user_path->path;
358
359 binding_info->component_path = component_path->path;
360 if (float_thresholds) {
361 binding_info->float_threshold = float_thresholds[i];
362 }
363 if (axis_flags) {
364 binding_info->axis_flag = axis_flags[i];
365 }
366 if (poses) {
367 copy_v3_v3(binding_info->pose.position, poses[i].position);
368 copy_qt_qt(binding_info->pose.orientation_quat, poses[i].orientation_quat);
369 }
370 }
371
372 GHOST_XrActionProfileInfo profile_info{};
373 profile_info.action_name = action_name;
374 profile_info.profile_path = profile_path;
375 profile_info.count_subaction_paths = count;
376 profile_info.subaction_paths = (const char **)subaction_paths;
377 profile_info.bindings = binding_infos;
378
379 const bool success = GHOST_XrCreateActionBindings(
380 xr->runtime->context, action_set_name, 1, &profile_info);
381
382 MEM_freeN(subaction_paths);
383 MEM_freeN(binding_infos);
384
385 return success;
386}
387
389 const char *action_set_name,
390 const char *action_name,
391 const char *profile_path)
392{
393 GHOST_XrDestroyActionBindings(
394 xr->runtime->context, action_set_name, 1, &action_name, &profile_path);
395}
396
397bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name, bool delayed)
398{
399 wmXrActionSet *action_set = action_set_find(xr, action_set_name);
400 if (!action_set) {
401 return false;
402 }
403
404 if (delayed) {
405 /* Save name to activate action set later, before next actions sync
406 * (see #wm_xr_session_actions_update()). */
408 return true;
409 }
410
411 {
412 /* Clear any active modal/haptic actions. */
413 wmXrActionSet *active_action_set = xr->runtime->session_state.active_action_set;
414 if (active_action_set) {
415 BLI_freelistN(&active_action_set->active_modal_actions);
416 BLI_freelistN(&active_action_set->active_haptic_actions);
417 }
418 }
419
420 xr->runtime->session_state.active_action_set = action_set;
421
422 if (action_set->controller_grip_action && action_set->controller_aim_action) {
424 action_set->controller_grip_action, action_set->controller_aim_action, xr);
425 }
426 else {
428 }
429
430 return true;
431}
432
434 const char *action_set_name,
435 const char *grip_action_name,
436 const char *aim_action_name)
437{
438 wmXrActionSet *action_set = action_set_find(xr, action_set_name);
439 if (!action_set) {
440 return false;
441 }
442
443 wmXrAction *grip_action = action_find(xr, action_set_name, grip_action_name);
444 if (!grip_action) {
445 return false;
446 }
447
448 wmXrAction *aim_action = action_find(xr, action_set_name, aim_action_name);
449 if (!aim_action) {
450 return false;
451 }
452
453 /* Ensure consistent subaction paths. */
454 const uint count = grip_action->count_subaction_paths;
455 if (count != aim_action->count_subaction_paths) {
456 return false;
457 }
458
459 for (uint i = 0; i < count; ++i) {
460 if (!STREQ(grip_action->subaction_paths[i], aim_action->subaction_paths[i])) {
461 return false;
462 }
463 }
464
465 action_set->controller_grip_action = grip_action;
466 action_set->controller_aim_action = aim_action;
467
468 if (action_set == xr->runtime->session_state.active_action_set) {
469 wm_xr_session_controller_data_populate(grip_action, aim_action, xr);
470 }
471
472 return true;
473}
474
476 const char *action_set_name,
477 const char *action_name,
478 const char *subaction_path,
479 wmXrActionState *r_state)
480{
481 const wmXrAction *action = action_find((wmXrData *)xr, action_set_name, action_name);
482 if (!action) {
483 return false;
484 }
485
486 r_state->type = int(action->type);
487
488 /* Find the action state corresponding to the subaction path. */
489 for (uint i = 0; i < action->count_subaction_paths; ++i) {
490 if (STREQ(subaction_path, action->subaction_paths[i])) {
491 switch (action->type) {
492 case XR_BOOLEAN_INPUT:
493 r_state->state_boolean = ((bool *)action->states)[i];
494 break;
495 case XR_FLOAT_INPUT:
496 r_state->state_float = ((float *)action->states)[i];
497 break;
499 copy_v2_v2(r_state->state_vector2f, ((float(*)[2])action->states)[i]);
500 break;
501 case XR_POSE_INPUT: {
502 const GHOST_XrPose *pose = &((GHOST_XrPose *)action->states)[i];
503 copy_v3_v3(r_state->state_pose.position, pose->position);
504 copy_qt_qt(r_state->state_pose.orientation_quat, pose->orientation_quat);
505 break;
506 }
509 break;
510 }
511 return true;
512 }
513 }
514
515 return false;
516}
517
519 const char *action_set_name,
520 const char *action_name,
521 const char *subaction_path,
522 const int64_t *duration,
523 const float *frequency,
524 const float *amplitude)
525{
526 return GHOST_XrApplyHapticAction(xr->runtime->context,
527 action_set_name,
528 action_name,
529 subaction_path,
530 duration,
531 frequency,
532 amplitude) ?
533 true :
534 false;
535}
536
538 const char *action_set_name,
539 const char *action_name,
540 const char *subaction_path)
541{
542 GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name, subaction_path);
543}
544
545 /* XR-Action API. */
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:269
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void copy_qt_qt(float q[4], const float a[4])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void copy_v3_v3(float r[3], const float a[3])
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
#define STRNCPY(dst, src)
Definition BLI_string.h:593
unsigned int uint
#define ELEM(...)
#define STREQ(a, b)
eXrOpFlag
eXrAxisFlag
eXrActionType
@ XR_FLOAT_INPUT
@ XR_BOOLEAN_INPUT
@ XR_VECTOR2F_INPUT
@ XR_POSE_INPUT
@ XR_VIBRATION_OUTPUT
eXrActionFlag
eXrHapticFlag
GHOST C-API function and type declarations.
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
int count
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void *(* MEM_calloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:43
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
signed short int16_t
Definition stdint.h:76
__int64 int64_t
Definition stdint.h:89
char path[64]
wmXrAction * controller_aim_action
wmXrAction * controller_grip_action
ListBase active_modal_actions
ListBase active_haptic_actions
eXrActionType type
struct wmOperatorType * ot
eXrAxisFlag * axis_flags
char * haptic_name
unsigned int count_subaction_paths
void * states_prev
eXrOpFlag op_flag
float haptic_frequency
int64_t haptic_duration
eXrActionFlag action_flag
char ** subaction_paths
float * float_thresholds
float haptic_amplitude
eXrHapticFlag haptic_flag
IDProperty * op_properties
struct wmXrRuntimeData * runtime
GHOST_XrContextHandle context
wmXrSessionState session_state
char active_action_set_next[64]
struct wmXrActionSet * active_action_set
wmOperatorType * ot
Definition wm_files.cc:4125
bool WM_xr_action_state_get(const wmXrData *xr, const char *action_set_name, const char *action_name, const char *subaction_path, wmXrActionState *r_state)
static wmXrAction * action_create(const char *action_name, eXrActionType type, const ListBase *user_paths, wmOperatorType *ot, IDProperty *op_properties, const char *haptic_name, const int64_t *haptic_duration, const float *haptic_frequency, const float *haptic_amplitude, eXrOpFlag op_flag, eXrActionFlag action_flag, eXrHapticFlag haptic_flag)
bool WM_xr_action_binding_create(wmXrData *xr, const char *action_set_name, const char *action_name, const char *profile_path, const ListBase *user_paths, const ListBase *component_paths, const float *float_thresholds, const eXrAxisFlag *axis_flags, const wmXrPose *poses)
void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name)
void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name, const char *subaction_path)
bool WM_xr_action_set_create(wmXrData *xr, const char *action_set_name)
void WM_xr_action_binding_destroy(wmXrData *xr, const char *action_set_name, const char *action_name, const char *profile_path)
static wmXrActionSet * action_set_find(wmXrData *xr, const char *action_set_name)
bool WM_xr_action_create(wmXrData *xr, const char *action_set_name, const char *action_name, eXrActionType type, const ListBase *user_paths, wmOperatorType *ot, IDProperty *op_properties, const char *haptic_name, const int64_t *haptic_duration, const float *haptic_frequency, const float *haptic_amplitude, eXrOpFlag op_flag, eXrActionFlag action_flag, eXrHapticFlag haptic_flag)
static wmXrActionSet * action_set_create(const char *action_set_name)
void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name)
bool WM_xr_controller_pose_actions_set(wmXrData *xr, const char *action_set_name, const char *grip_action_name, const char *aim_action_name)
static void action_set_destroy(void *val)
static wmXrAction * action_find(wmXrData *xr, const char *action_set_name, const char *action_name)
static void action_destroy(void *val)
bool WM_xr_haptic_action_apply(wmXrData *xr, const char *action_set_name, const char *action_name, const char *subaction_path, const int64_t *duration, const float *frequency, const float *amplitude)
bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name, bool delayed)
void wm_xr_session_controller_data_populate(const wmXrAction *grip_action, const wmXrAction *aim_action, wmXrData *xr)
void wm_xr_session_controller_data_clear(wmXrSessionState *state)