Blender  V3.3
GHOST_XrAction.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include <cassert>
8 #include <cstring>
9 
10 #include "GHOST_Types.h"
11 
12 #include "GHOST_XrException.h"
13 #include "GHOST_Xr_intern.h"
14 
15 #include "GHOST_XrAction.h"
16 
17 /* -------------------------------------------------------------------- */
23  XrAction action,
24  const char *action_name,
25  const char *profile_path,
26  XrPath subaction_path,
27  const char *subaction_path_str,
28  const GHOST_XrPose &pose)
29 {
30  XrActionSpaceCreateInfo action_space_info{XR_TYPE_ACTION_SPACE_CREATE_INFO};
31  action_space_info.action = action;
32  action_space_info.subactionPath = subaction_path;
33  copy_ghost_pose_to_openxr_pose(pose, action_space_info.poseInActionSpace);
34 
35  CHECK_XR(xrCreateActionSpace(session, &action_space_info, &m_space),
36  (std::string("Failed to create space \"") + subaction_path_str + "\" for action \"" +
37  action_name + "\" and profile \"" + profile_path + "\".")
38  .data());
39 }
40 
42 {
43  if (m_space != XR_NULL_HANDLE) {
44  CHECK_XR_ASSERT(xrDestroySpace(m_space));
45  }
46 }
47 
49 {
50  return m_space;
51 }
52 
55 /* -------------------------------------------------------------------- */
61  XrSession session,
62  XrAction action,
63  GHOST_XrActionType type,
64  const GHOST_XrActionProfileInfo &info)
65 {
66  CHECK_XR(xrStringToPath(instance, info.profile_path, &m_profile),
67  (std::string("Failed to get interaction profile path \"") + info.profile_path + "\".")
68  .data());
69 
70  const bool is_float_action = (type == GHOST_kXrActionTypeFloatInput ||
71  type == GHOST_kXrActionTypeVector2fInput);
72  const bool is_button_action = (is_float_action || type == GHOST_kXrActionTypeBooleanInput);
73  const bool is_pose_action = (type == GHOST_kXrActionTypePoseInput);
74 
75  /* Create bindings. */
76  XrInteractionProfileSuggestedBinding bindings_info{
77  XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING};
78  bindings_info.interactionProfile = m_profile;
79  bindings_info.countSuggestedBindings = 1;
80 
81  for (uint32_t subaction_idx = 0; subaction_idx < info.count_subaction_paths; ++subaction_idx) {
82  const char *subaction_path_str = info.subaction_paths[subaction_idx];
83  const GHOST_XrActionBindingInfo &binding_info = info.bindings[subaction_idx];
84 
85  const std::string interaction_path = std::string(subaction_path_str) +
86  binding_info.component_path;
87  if (m_bindings.find(interaction_path) != m_bindings.end()) {
88  continue;
89  }
90 
91  XrActionSuggestedBinding sbinding;
92  sbinding.action = action;
93  CHECK_XR(xrStringToPath(instance, interaction_path.data(), &sbinding.binding),
94  (std::string("Failed to get interaction path \"") + interaction_path + "\".").data());
95  bindings_info.suggestedBindings = &sbinding;
96 
97  /* Although the bindings will be re-suggested in GHOST_XrSession::attachActionSets(), it
98  * greatly improves error checking to suggest them here first. */
99  CHECK_XR(xrSuggestInteractionProfileBindings(instance, &bindings_info),
100  (std::string("Failed to create binding for action \"") + info.action_name +
101  "\" and profile \"" + info.profile_path +
102  "\". Are the action and profile paths correct?")
103  .data());
104 
105  m_bindings.insert({interaction_path, sbinding.binding});
106 
107  if (m_subaction_data.find(subaction_path_str) == m_subaction_data.end()) {
108  std::map<std::string, GHOST_XrSubactionData>::iterator it =
109  m_subaction_data
110  .emplace(
111  std::piecewise_construct, std::make_tuple(subaction_path_str), std::make_tuple())
112  .first;
113  GHOST_XrSubactionData &subaction = it->second;
114 
115  CHECK_XR(xrStringToPath(instance, subaction_path_str, &subaction.subaction_path),
116  (std::string("Failed to get user path \"") + subaction_path_str + "\".").data());
117 
118  if (is_float_action || is_button_action) {
119  if (is_float_action) {
120  subaction.float_threshold = binding_info.float_threshold;
121  }
122  if (is_button_action) {
123  subaction.axis_flag = binding_info.axis_flag;
124  }
125  }
126  else if (is_pose_action) {
127  /* Create action space for pose bindings. */
128  subaction.space = std::make_unique<GHOST_XrActionSpace>(session,
129  action,
130  info.action_name,
131  info.profile_path,
132  subaction.subaction_path,
133  subaction_path_str,
134  binding_info.pose);
135  }
136  }
137  }
138 }
139 
141 {
142  return m_profile;
143 }
144 
146 {
147  for (auto &[subaction_path_str, subaction] : m_subaction_data) {
148  if (subaction.subaction_path == subaction_path) {
149  return &subaction;
150  }
151  }
152  return nullptr;
153 }
154 
156  XrAction action, std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const
157 {
158  std::map<XrPath, std::vector<XrActionSuggestedBinding>>::iterator it = r_bindings.find(
159  m_profile);
160  if (it == r_bindings.end()) {
161  it = r_bindings
162  .emplace(std::piecewise_construct, std::make_tuple(m_profile), std::make_tuple())
163  .first;
164  }
165 
166  std::vector<XrActionSuggestedBinding> &sbindings = it->second;
167 
168  for (auto &[path, binding] : m_bindings) {
169  XrActionSuggestedBinding sbinding;
170  sbinding.action = action;
171  sbinding.binding = binding;
172 
173  sbindings.push_back(std::move(sbinding));
174  }
175 }
176 
179 /* -------------------------------------------------------------------- */
185  XrActionSet action_set,
186  const GHOST_XrActionInfo &info)
187  : m_type(info.type),
188  m_states(info.states),
189  m_float_thresholds(info.float_thresholds),
190  m_axis_flags(info.axis_flags),
191  m_custom_data_(
192  std::make_unique<GHOST_C_CustomDataWrapper>(info.customdata, info.customdata_free_fn))
193 {
194  m_subaction_paths.resize(info.count_subaction_paths);
195 
196  for (uint32_t i = 0; i < info.count_subaction_paths; ++i) {
197  const char *subaction_path_str = info.subaction_paths[i];
198  CHECK_XR(xrStringToPath(instance, subaction_path_str, &m_subaction_paths[i]),
199  (std::string("Failed to get user path \"") + subaction_path_str + "\".").data());
200  m_subaction_indices.insert({subaction_path_str, i});
201  }
202 
203  XrActionCreateInfo action_info{XR_TYPE_ACTION_CREATE_INFO};
204  strcpy(action_info.actionName, info.name);
205 
206  /* Just use same name for localized. This can be changed in the future if necessary. */
207  strcpy(action_info.localizedActionName, info.name);
208 
209  switch (info.type) {
210  case GHOST_kXrActionTypeBooleanInput:
211  action_info.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
212  break;
213  case GHOST_kXrActionTypeFloatInput:
214  action_info.actionType = XR_ACTION_TYPE_FLOAT_INPUT;
215  break;
216  case GHOST_kXrActionTypeVector2fInput:
217  action_info.actionType = XR_ACTION_TYPE_VECTOR2F_INPUT;
218  break;
219  case GHOST_kXrActionTypePoseInput:
220  action_info.actionType = XR_ACTION_TYPE_POSE_INPUT;
221  break;
222  case GHOST_kXrActionTypeVibrationOutput:
223  action_info.actionType = XR_ACTION_TYPE_VIBRATION_OUTPUT;
224  break;
225  }
226  action_info.countSubactionPaths = info.count_subaction_paths;
227  action_info.subactionPaths = m_subaction_paths.data();
228 
229  CHECK_XR(xrCreateAction(action_set, &action_info, &m_action),
230  (std::string("Failed to create action \"") + info.name +
231  "\". Action name and/or paths are invalid. Name must not contain upper "
232  "case letters or special characters other than '-', '_', or '.'.")
233  .data());
234 }
235 
237 {
238  if (m_action != XR_NULL_HANDLE) {
239  CHECK_XR_ASSERT(xrDestroyAction(m_action));
240  }
241 }
242 
244  XrSession session,
245  const GHOST_XrActionProfileInfo &info)
246 {
247  if (m_profiles.find(info.profile_path) != m_profiles.end()) {
248  return false;
249  }
250 
251  m_profiles.emplace(std::piecewise_construct,
252  std::make_tuple(info.profile_path),
253  std::make_tuple(instance, session, m_action, m_type, info));
254 
255  return true;
256 }
257 
258 void GHOST_XrAction::destroyBinding(const char *profile_path)
259 {
260  if (m_profiles.find(profile_path) != m_profiles.end()) {
261  m_profiles.erase(profile_path);
262  }
263 }
264 
265 void GHOST_XrAction::updateState(XrSession session,
266  const char *action_name,
267  XrSpace reference_space,
268  const XrTime &predicted_display_time)
269 {
270  const bool is_float_action = (m_type == GHOST_kXrActionTypeFloatInput ||
271  m_type == GHOST_kXrActionTypeVector2fInput);
272  const bool is_button_action = (is_float_action || m_type == GHOST_kXrActionTypeBooleanInput);
273 
274  XrActionStateGetInfo state_info{XR_TYPE_ACTION_STATE_GET_INFO};
275  state_info.action = m_action;
276 
277  const size_t count_subaction_paths = m_subaction_paths.size();
278  for (size_t subaction_idx = 0; subaction_idx < count_subaction_paths; ++subaction_idx) {
279  state_info.subactionPath = m_subaction_paths[subaction_idx];
280 
281  /* Set subaction data based on current interaction profile. */
282  XrInteractionProfileState profile_state{XR_TYPE_INTERACTION_PROFILE_STATE};
283  CHECK_XR(xrGetCurrentInteractionProfile(session, state_info.subactionPath, &profile_state),
284  "Failed to get current interaction profile.");
285 
286  const GHOST_XrSubactionData *subaction = nullptr;
287  for (auto &[profile_path, profile] : m_profiles) {
288  if (profile.getProfile() == profile_state.interactionProfile) {
289  subaction = profile.getSubaction(state_info.subactionPath);
290  break;
291  }
292  }
293 
294  if (subaction != nullptr) {
295  if (is_float_action) {
296  m_float_thresholds[subaction_idx] = subaction->float_threshold;
297  }
298  if (is_button_action) {
299  m_axis_flags[subaction_idx] = subaction->axis_flag;
300  }
301  }
302 
303  switch (m_type) {
304  case GHOST_kXrActionTypeBooleanInput: {
305  XrActionStateBoolean state{XR_TYPE_ACTION_STATE_BOOLEAN};
306  CHECK_XR(xrGetActionStateBoolean(session, &state_info, &state),
307  (std::string("Failed to get state for boolean action \"") + action_name + "\".")
308  .data());
309  if (state.isActive) {
310  ((bool *)m_states)[subaction_idx] = state.currentState;
311  }
312  break;
313  }
314  case GHOST_kXrActionTypeFloatInput: {
315  XrActionStateFloat state{XR_TYPE_ACTION_STATE_FLOAT};
316  CHECK_XR(
317  xrGetActionStateFloat(session, &state_info, &state),
318  (std::string("Failed to get state for float action \"") + action_name + "\".").data());
319  if (state.isActive) {
320  ((float *)m_states)[subaction_idx] = state.currentState;
321  }
322  break;
323  }
324  case GHOST_kXrActionTypeVector2fInput: {
325  XrActionStateVector2f state{XR_TYPE_ACTION_STATE_VECTOR2F};
326  CHECK_XR(xrGetActionStateVector2f(session, &state_info, &state),
327  (std::string("Failed to get state for vector2f action \"") + action_name + "\".")
328  .data());
329  if (state.isActive) {
330  memcpy(((float(*)[2])m_states)[subaction_idx], &state.currentState, sizeof(float[2]));
331  }
332  break;
333  }
334  case GHOST_kXrActionTypePoseInput: {
335  /* Check for valid display time to avoid an error in #xrLocateSpace(). */
336  if (predicted_display_time > 0) {
337  XrActionStatePose state{XR_TYPE_ACTION_STATE_POSE};
338  CHECK_XR(xrGetActionStatePose(session, &state_info, &state),
339  (std::string("Failed to get state for pose action \"") + action_name + "\".")
340  .data());
341  if (state.isActive) {
342  XrSpace pose_space = ((subaction != nullptr) && (subaction->space != nullptr)) ?
343  subaction->space->getSpace() :
344  XR_NULL_HANDLE;
345  if (pose_space != XR_NULL_HANDLE) {
346  XrSpaceLocation space_location{XR_TYPE_SPACE_LOCATION};
347  CHECK_XR(
348  xrLocateSpace(
349  pose_space, reference_space, predicted_display_time, &space_location),
350  (std::string("Failed to query pose space for action \"") + action_name + "\".")
351  .data());
352  copy_openxr_pose_to_ghost_pose(space_location.pose,
353  ((GHOST_XrPose *)m_states)[subaction_idx]);
354  }
355  }
356  }
357  break;
358  }
359  case GHOST_kXrActionTypeVibrationOutput: {
360  break;
361  }
362  }
363  }
364 }
365 
366 void GHOST_XrAction::applyHapticFeedback(XrSession session,
367  const char *action_name,
368  const char *subaction_path_str,
369  const int64_t &duration,
370  const float &frequency,
371  const float &amplitude)
372 {
373  XrHapticVibration vibration{XR_TYPE_HAPTIC_VIBRATION};
374  vibration.duration = (duration == 0) ? XR_MIN_HAPTIC_DURATION :
375  static_cast<XrDuration>(duration);
376  vibration.frequency = frequency;
377  vibration.amplitude = amplitude;
378 
379  XrHapticActionInfo haptic_info{XR_TYPE_HAPTIC_ACTION_INFO};
380  haptic_info.action = m_action;
381 
382  if (subaction_path_str != nullptr) {
383  SubactionIndexMap::iterator it = m_subaction_indices.find(subaction_path_str);
384  if (it != m_subaction_indices.end()) {
385  haptic_info.subactionPath = m_subaction_paths[it->second];
386  CHECK_XR(
387  xrApplyHapticFeedback(session, &haptic_info, (const XrHapticBaseHeader *)&vibration),
388  (std::string("Failed to apply haptic action \"") + action_name + "\".").data());
389  }
390  }
391  else {
392  for (const XrPath &subaction_path : m_subaction_paths) {
393  haptic_info.subactionPath = subaction_path;
394  CHECK_XR(
395  xrApplyHapticFeedback(session, &haptic_info, (const XrHapticBaseHeader *)&vibration),
396  (std::string("Failed to apply haptic action \"") + action_name + "\".").data());
397  }
398  }
399 }
400 
401 void GHOST_XrAction::stopHapticFeedback(XrSession session,
402  const char *action_name,
403  const char *subaction_path_str)
404 {
405  XrHapticActionInfo haptic_info{XR_TYPE_HAPTIC_ACTION_INFO};
406  haptic_info.action = m_action;
407 
408  if (subaction_path_str != nullptr) {
409  SubactionIndexMap::iterator it = m_subaction_indices.find(subaction_path_str);
410  if (it != m_subaction_indices.end()) {
411  haptic_info.subactionPath = m_subaction_paths[it->second];
412  CHECK_XR(xrStopHapticFeedback(session, &haptic_info),
413  (std::string("Failed to stop haptic action \"") + action_name + "\".").data());
414  }
415  }
416  else {
417  for (const XrPath &subaction_path : m_subaction_paths) {
418  haptic_info.subactionPath = subaction_path;
419  CHECK_XR(xrStopHapticFeedback(session, &haptic_info),
420  (std::string("Failed to stop haptic action \"") + action_name + "\".").data());
421  }
422  }
423 }
424 
426 {
427  if (m_custom_data_ == nullptr) {
428  return nullptr;
429  }
430  return m_custom_data_->custom_data_;
431 }
432 
434  std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const
435 {
436  for (auto &[path, profile] : m_profiles) {
437  profile.getBindings(m_action, r_bindings);
438  }
439 }
440 
443 /* -------------------------------------------------------------------- */
448 GHOST_XrActionSet::GHOST_XrActionSet(XrInstance instance, const GHOST_XrActionSetInfo &info)
449  : m_custom_data_(
450  std::make_unique<GHOST_C_CustomDataWrapper>(info.customdata, info.customdata_free_fn))
451 {
452  XrActionSetCreateInfo action_set_info{XR_TYPE_ACTION_SET_CREATE_INFO};
453  strcpy(action_set_info.actionSetName, info.name);
454 
455  /* Just use same name for localized. This can be changed in the future if necessary. */
456  strcpy(action_set_info.localizedActionSetName, info.name);
457 
458  action_set_info.priority = 0; /* Use same (default) priority for all action sets. */
459 
460  CHECK_XR(xrCreateActionSet(instance, &action_set_info, &m_action_set),
461  (std::string("Failed to create action set \"") + info.name +
462  "\". Name must not contain upper case letters or special characters "
463  "other than '-', '_', or '.'.")
464  .data());
465 }
466 
468 {
469  /* This needs to be done before xrDestroyActionSet() to avoid an assertion in the GHOST_XrAction
470  * destructor (which calls xrDestroyAction()). */
471  m_actions.clear();
472 
473  if (m_action_set != XR_NULL_HANDLE) {
474  CHECK_XR_ASSERT(xrDestroyActionSet(m_action_set));
475  }
476 }
477 
478 bool GHOST_XrActionSet::createAction(XrInstance instance, const GHOST_XrActionInfo &info)
479 {
480  if (m_actions.find(info.name) != m_actions.end()) {
481  return false;
482  }
483 
484  m_actions.emplace(std::piecewise_construct,
485  std::make_tuple(info.name),
486  std::make_tuple(instance, m_action_set, info));
487 
488  return true;
489 }
490 
491 void GHOST_XrActionSet::destroyAction(const char *action_name)
492 {
493  if (m_actions.find(action_name) != m_actions.end()) {
494  m_actions.erase(action_name);
495  }
496 }
497 
499 {
500  std::map<std::string, GHOST_XrAction>::iterator it = m_actions.find(action_name);
501  if (it == m_actions.end()) {
502  return nullptr;
503  }
504  return &it->second;
505 }
506 
507 void GHOST_XrActionSet::updateStates(XrSession session,
508  XrSpace reference_space,
509  const XrTime &predicted_display_time)
510 {
511  for (auto &[name, action] : m_actions) {
512  action.updateState(session, name.data(), reference_space, predicted_display_time);
513  }
514 }
515 
517 {
518  return m_action_set;
519 }
520 
522 {
523  if (m_custom_data_ == nullptr) {
524  return nullptr;
525  }
526  return m_custom_data_->custom_data_;
527 }
528 
530 {
531  return (uint32_t)m_actions.size();
532 }
533 
534 void GHOST_XrActionSet::getActionCustomdataArray(void **r_customdata_array)
535 {
536  uint32_t i = 0;
537  for (auto &[name, action] : m_actions) {
538  r_customdata_array[i++] = action.getCustomdata();
539  }
540 }
541 
543  std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const
544 {
545  for (auto &[name, action] : m_actions) {
546  action.getBindings(r_bindings);
547  }
548 }
549 
void copy_openxr_pose_to_ghost_pose(const XrPosef &oxr_pose, GHOST_XrPose &r_ghost_pose)
void copy_ghost_pose_to_openxr_pose(const GHOST_XrPose &ghost_pose, XrPosef &r_oxr_pose)
#define CHECK_XR_ASSERT(call)
#define CHECK_XR(call, error_msg)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object instance
int m_type
void getBindings(XrAction action, std::map< XrPath, std::vector< XrActionSuggestedBinding >> &r_bindings) const
const GHOST_XrSubactionData * getSubaction(XrPath subaction_path) const
XrPath getProfile() const
GHOST_XrActionProfile()=delete
GHOST_XrActionSet()=delete
void updateStates(XrSession session, XrSpace reference_space, const XrTime &predicted_display_time)
GHOST_XrAction * findAction(const char *action_name)
void getBindings(std::map< XrPath, std::vector< XrActionSuggestedBinding >> &r_bindings) const
bool createAction(XrInstance instance, const GHOST_XrActionInfo &info)
XrActionSet getActionSet() const
void getActionCustomdataArray(void **r_customdata_array)
uint32_t getActionCount() const
void destroyAction(const char *action_name)
XrSpace getSpace() const
GHOST_XrActionSpace()=delete
bool createBinding(XrInstance instance, XrSession session, const GHOST_XrActionProfileInfo &info)
void updateState(XrSession session, const char *action_name, XrSpace reference_space, const XrTime &predicted_display_time)
void getBindings(std::map< XrPath, std::vector< XrActionSuggestedBinding >> &r_bindings) const
void stopHapticFeedback(XrSession session, const char *action_name, const char *subaction_path)
void applyHapticFeedback(XrSession session, const char *action_name, const char *subaction_path, const int64_t &duration, const float &frequency, const float &amplitude)
void destroyBinding(const char *profile_path)
void * getCustomdata()
GHOST_XrAction()=delete
const int state
SocketIndexByIdentifierMap * map
unsigned int uint32_t
Definition: stdint.h:80
__int64 int64_t
Definition: stdint.h:89
std::unique_ptr< GHOST_XrActionSpace > space