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