XR: Action Binding Improvements

Provides several important improvements to the runtime action
bindings operation and internal API.

Moves input-specific action data (input thresholds, input regions,
pose offsets/spaces) from actions to more granular action bindings.
This allows a single action to be mapped to a variety of inputs,
without having to share a single input threshold, region, or space.

Also removes the need for action space creation API, as spaces for
pose actions will be automatically created with the bindings.

The correct action data for the current inputs is set by calling
xrGetCurrentInteractionProfile() to get the current profile and then
retrieving the corresponding mapped data.

Does not bring about any changes for users since only internal
runtime functionality is currently affected.

Reviewed By: Julian Eisel

Differential Revision: http://developer.blender.org/D12077
This commit is contained in:
Peter Kim 2021-08-05 13:14:26 +09:00
parent d1c5e2e050
commit d3d4be1db3
Notes: blender-bot 2023-05-31 04:43:10 +02:00
Referenced by commit cb9c0aa7d0, Fix: XR action map memory leaks
12 changed files with 258 additions and 329 deletions

View File

@ -1066,22 +1066,6 @@ void GHOST_XrDestroyActions(GHOST_XrContextHandle xr_context,
uint32_t count,
const char *const *action_names);
/**
* Create spaces for pose-based OpenXR actions.
*/
int GHOST_XrCreateActionSpaces(GHOST_XrContextHandle xr_context,
const char *action_set_name,
uint32_t count,
const GHOST_XrActionSpaceInfo *infos);
/**
* Destroy previously created spaces for OpenXR actions.
*/
void GHOST_XrDestroyActionSpaces(GHOST_XrContextHandle xr_context,
const char *action_set_name,
uint32_t count,
const GHOST_XrActionSpaceInfo *infos);
/**
* Create input/output path bindings for OpenXR actions.
*/
@ -1096,7 +1080,8 @@ int GHOST_XrCreateActionBindings(GHOST_XrContextHandle xr_context,
void GHOST_XrDestroyActionBindings(GHOST_XrContextHandle xr_context,
const char *action_set_name,
uint32_t count,
const GHOST_XrActionProfileInfo *infos);
const char *const *action_names,
const char *const *profile_paths);
/**
* Attach all created action sets to the current OpenXR session.

View File

@ -719,29 +719,27 @@ typedef struct GHOST_XrActionInfo {
const char **subaction_paths;
/** States for each subaction path. */
void *states;
/** Input thresholds/regions for each subaction path. */
float *float_thresholds;
int16_t *axis_flags;
GHOST_XrCustomdataFreeFn customdata_free_fn;
void *customdata; /* wmXrAction */
} GHOST_XrActionInfo;
typedef struct GHOST_XrActionSpaceInfo {
const char *action_name;
uint32_t count_subaction_paths;
const char **subaction_paths;
/** Poses for each subaction path. */
const GHOST_XrPose *poses;
} GHOST_XrActionSpaceInfo;
typedef struct GHOST_XrActionBindingInfo {
const char *action_name;
uint32_t count_interaction_paths;
/** Interaction path: User (sub-action) path + component path. */
const char **interaction_paths;
const char *component_path;
float float_threshold;
int16_t axis_flag;
GHOST_XrPose pose;
} GHOST_XrActionBindingInfo;
typedef struct GHOST_XrActionProfileInfo {
const char *action_name;
const char *profile_path;
uint32_t count_bindings;
uint32_t count_subaction_paths;
const char **subaction_paths;
/* Bindings for each subaction path. */
const GHOST_XrActionBindingInfo *bindings;
} GHOST_XrActionProfileInfo;

View File

@ -961,28 +961,6 @@ void GHOST_XrDestroyActions(GHOST_XrContextHandle xr_contexthandle,
GHOST_XR_CAPI_CALL(xr_session->destroyActions(action_set_name, count, action_names), xr_context);
}
int GHOST_XrCreateActionSpaces(GHOST_XrContextHandle xr_contexthandle,
const char *action_set_name,
uint32_t count,
const GHOST_XrActionSpaceInfo *infos)
{
GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
GHOST_XrSession *xr_session = xr_context->getSession();
GHOST_XR_CAPI_CALL_RET(xr_session->createActionSpaces(action_set_name, count, infos),
xr_context);
return 0;
}
void GHOST_XrDestroyActionSpaces(GHOST_XrContextHandle xr_contexthandle,
const char *action_set_name,
uint32_t count,
const GHOST_XrActionSpaceInfo *infos)
{
GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
GHOST_XrSession *xr_session = xr_context->getSession();
GHOST_XR_CAPI_CALL(xr_session->destroyActionSpaces(action_set_name, count, infos), xr_context);
}
int GHOST_XrCreateActionBindings(GHOST_XrContextHandle xr_contexthandle,
const char *action_set_name,
uint32_t count,
@ -998,11 +976,14 @@ int GHOST_XrCreateActionBindings(GHOST_XrContextHandle xr_contexthandle,
void GHOST_XrDestroyActionBindings(GHOST_XrContextHandle xr_contexthandle,
const char *action_set_name,
uint32_t count,
const GHOST_XrActionProfileInfo *infos)
const char *const *action_names,
const char *const *profile_paths)
{
GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
GHOST_XrSession *xr_session = xr_context->getSession();
GHOST_XR_CAPI_CALL(xr_session->destroyActionBindings(action_set_name, count, infos), xr_context);
GHOST_XR_CAPI_CALL(
xr_session->destroyActionBindings(action_set_name, count, action_names, profile_paths),
xr_context);
}
int GHOST_XrAttachActionSets(GHOST_XrContextHandle xr_contexthandle)

View File

@ -33,24 +33,22 @@
*
* \{ */
GHOST_XrActionSpace::GHOST_XrActionSpace(XrInstance instance,
XrSession session,
GHOST_XrActionSpace::GHOST_XrActionSpace(XrSession session,
XrAction action,
const GHOST_XrActionSpaceInfo &info,
uint32_t subaction_idx)
const char *action_name,
const char *profile_path,
XrPath subaction_path,
const char *subaction_path_str,
const GHOST_XrPose &pose)
{
const char *subaction_path = info.subaction_paths[subaction_idx];
CHECK_XR(xrStringToPath(instance, subaction_path, &m_subaction_path),
(std::string("Failed to get user path \"") + subaction_path + "\".").data());
XrActionSpaceCreateInfo action_space_info{XR_TYPE_ACTION_SPACE_CREATE_INFO};
action_space_info.action = action;
action_space_info.subactionPath = m_subaction_path;
copy_ghost_pose_to_openxr_pose(info.poses[subaction_idx], action_space_info.poseInActionSpace);
action_space_info.subactionPath = subaction_path;
copy_ghost_pose_to_openxr_pose(pose, action_space_info.poseInActionSpace);
CHECK_XR(xrCreateActionSpace(session, &action_space_info, &m_space),
(std::string("Failed to create space \"") + subaction_path + "\" for action \"" +
info.action_name + "\".")
(std::string("Failed to create space \"") + subaction_path_str + "\" for action \"" +
action_name + "\" and profile \"" + profile_path + "\".")
.data());
}
@ -66,11 +64,6 @@ XrSpace GHOST_XrActionSpace::getSpace() const
return m_space;
}
const XrPath &GHOST_XrActionSpace::getSubactionPath() const
{
return m_subaction_path;
}
/** \} */
/* -------------------------------------------------------------------- */
@ -79,13 +72,19 @@ const XrPath &GHOST_XrActionSpace::getSubactionPath() const
* \{ */
GHOST_XrActionProfile::GHOST_XrActionProfile(XrInstance instance,
XrSession session,
XrAction action,
const char *profile_path,
const GHOST_XrActionBindingInfo &info)
GHOST_XrActionType type,
const GHOST_XrActionProfileInfo &info)
{
CHECK_XR(
xrStringToPath(instance, profile_path, &m_profile),
(std::string("Failed to get interaction profile path \"") + profile_path + "\".").data());
CHECK_XR(xrStringToPath(instance, info.profile_path, &m_profile),
(std::string("Failed to get interaction profile path \"") + info.profile_path + "\".")
.data());
const bool is_float_action = (type == GHOST_kXrActionTypeFloatInput ||
type == GHOST_kXrActionTypeVector2fInput);
const bool is_button_action = (is_float_action || type == GHOST_kXrActionTypeBooleanInput);
const bool is_pose_action = (type == GHOST_kXrActionTypePoseInput);
/* Create bindings. */
XrInteractionProfileSuggestedBinding bindings_info{
@ -93,31 +92,80 @@ GHOST_XrActionProfile::GHOST_XrActionProfile(XrInstance instance,
bindings_info.interactionProfile = m_profile;
bindings_info.countSuggestedBindings = 1;
for (uint32_t interaction_idx = 0; interaction_idx < info.count_interaction_paths;
++interaction_idx) {
const char *interaction_path = info.interaction_paths[interaction_idx];
for (uint32_t subaction_idx = 0; subaction_idx < info.count_subaction_paths; ++subaction_idx) {
const char *subaction_path_str = info.subaction_paths[subaction_idx];
const GHOST_XrActionBindingInfo &binding_info = info.bindings[subaction_idx];
const std::string interaction_path = std::string(subaction_path_str) +
binding_info.component_path;
if (m_bindings.find(interaction_path) != m_bindings.end()) {
continue;
}
XrActionSuggestedBinding sbinding;
sbinding.action = action;
CHECK_XR(xrStringToPath(instance, interaction_path, &sbinding.binding),
CHECK_XR(xrStringToPath(instance, interaction_path.data(), &sbinding.binding),
(std::string("Failed to get interaction path \"") + interaction_path + "\".").data());
bindings_info.suggestedBindings = &sbinding;
/* Although the bindings will be re-suggested in GHOST_XrSession::attachActionSets(), it
* greatly improves error checking to suggest them here first. */
CHECK_XR(xrSuggestInteractionProfileBindings(instance, &bindings_info),
(std::string("Failed to create binding for profile \"") + profile_path +
"\" and action \"" + info.action_name +
"\". Are the profile and action paths correct?")
(std::string("Failed to create binding for action \"") + info.action_name +
"\" and profile \"" + info.profile_path +
"\". Are the action and profile paths correct?")
.data());
m_bindings.insert({interaction_path, sbinding.binding});
if (m_subaction_data.find(subaction_path_str) == m_subaction_data.end()) {
std::map<std::string, GHOST_XrSubactionData>::iterator it =
m_subaction_data
.emplace(
std::piecewise_construct, std::make_tuple(subaction_path_str), std::make_tuple())
.first;
GHOST_XrSubactionData &subaction = it->second;
CHECK_XR(xrStringToPath(instance, subaction_path_str, &subaction.subaction_path),
(std::string("Failed to get user path \"") + subaction_path_str + "\".").data());
if (is_float_action || is_button_action) {
if (is_float_action) {
subaction.float_threshold = binding_info.float_threshold;
}
if (is_button_action) {
subaction.axis_flag = binding_info.axis_flag;
}
}
else if (is_pose_action) {
/* Create action space for pose bindings. */
subaction.space = std::make_unique<GHOST_XrActionSpace>(session,
action,
info.action_name,
info.profile_path,
subaction.subaction_path,
subaction_path_str,
binding_info.pose);
}
}
}
}
XrPath GHOST_XrActionProfile::getProfile() const
{
return m_profile;
}
const GHOST_XrSubactionData *GHOST_XrActionProfile::getSubaction(XrPath subaction_path) const
{
for (auto &[subaction_path_str, subaction] : m_subaction_data) {
if (subaction.subaction_path == subaction_path) {
return &subaction;
}
}
return nullptr;
}
void GHOST_XrActionProfile::getBindings(
XrAction action, std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const
{
@ -152,6 +200,8 @@ GHOST_XrAction::GHOST_XrAction(XrInstance instance,
const GHOST_XrActionInfo &info)
: m_type(info.type),
m_states(info.states),
m_float_thresholds(info.float_thresholds),
m_axis_flags(info.axis_flags),
m_custom_data_(
std::make_unique<GHOST_C_CustomDataWrapper>(info.customdata, info.customdata_free_fn))
{
@ -201,52 +251,25 @@ GHOST_XrAction::~GHOST_XrAction()
}
}
bool GHOST_XrAction::createSpace(XrInstance instance,
XrSession session,
const GHOST_XrActionSpaceInfo &info)
{
uint32_t subaction_idx = 0;
for (; subaction_idx < info.count_subaction_paths; ++subaction_idx) {
if (m_spaces.find(info.subaction_paths[subaction_idx]) != m_spaces.end()) {
return false;
}
}
for (subaction_idx = 0; subaction_idx < info.count_subaction_paths; ++subaction_idx) {
m_spaces.emplace(std::piecewise_construct,
std::make_tuple(info.subaction_paths[subaction_idx]),
std::make_tuple(instance, session, m_action, info, subaction_idx));
}
return true;
}
void GHOST_XrAction::destroySpace(const char *subaction_path)
{
if (m_spaces.find(subaction_path) != m_spaces.end()) {
m_spaces.erase(subaction_path);
}
}
bool GHOST_XrAction::createBinding(XrInstance instance,
const char *profile_path,
const GHOST_XrActionBindingInfo &info)
XrSession session,
const GHOST_XrActionProfileInfo &info)
{
if (m_profiles.find(profile_path) != m_profiles.end()) {
if (m_profiles.find(info.profile_path) != m_profiles.end()) {
return false;
}
m_profiles.emplace(std::piecewise_construct,
std::make_tuple(profile_path),
std::make_tuple(instance, m_action, profile_path, info));
std::make_tuple(info.profile_path),
std::make_tuple(instance, session, m_action, m_type, info));
return true;
}
void GHOST_XrAction::destroyBinding(const char *interaction_profile_path)
void GHOST_XrAction::destroyBinding(const char *profile_path)
{
if (m_profiles.find(interaction_profile_path) != m_profiles.end()) {
m_profiles.erase(interaction_profile_path);
if (m_profiles.find(profile_path) != m_profiles.end()) {
m_profiles.erase(profile_path);
}
}
@ -255,6 +278,10 @@ void GHOST_XrAction::updateState(XrSession session,
XrSpace reference_space,
const XrTime &predicted_display_time)
{
const bool is_float_action = (m_type == GHOST_kXrActionTypeFloatInput ||
m_type == GHOST_kXrActionTypeVector2fInput);
const bool is_button_action = (is_float_action || m_type == GHOST_kXrActionTypeBooleanInput);
XrActionStateGetInfo state_info{XR_TYPE_ACTION_STATE_GET_INFO};
state_info.action = m_action;
@ -262,6 +289,28 @@ void GHOST_XrAction::updateState(XrSession session,
for (size_t subaction_idx = 0; subaction_idx < count_subaction_paths; ++subaction_idx) {
state_info.subactionPath = m_subaction_paths[subaction_idx];
/* Set subaction data based on current interaction profile. */
XrInteractionProfileState profile_state{XR_TYPE_INTERACTION_PROFILE_STATE};
CHECK_XR(xrGetCurrentInteractionProfile(session, state_info.subactionPath, &profile_state),
"Failed to get current interaction profile.");
const GHOST_XrSubactionData *subaction = nullptr;
for (auto &[profile_path, profile] : m_profiles) {
if (profile.getProfile() == profile_state.interactionProfile) {
subaction = profile.getSubaction(state_info.subactionPath);
break;
}
}
if (subaction != nullptr) {
if (is_float_action) {
m_float_thresholds[subaction_idx] = subaction->float_threshold;
}
if (is_button_action) {
m_axis_flags[subaction_idx] = subaction->axis_flag;
}
}
switch (m_type) {
case GHOST_kXrActionTypeBooleanInput: {
XrActionStateBoolean state{XR_TYPE_ACTION_STATE_BOOLEAN};
@ -299,14 +348,9 @@ void GHOST_XrAction::updateState(XrSession session,
xrGetActionStatePose(session, &state_info, &state),
(std::string("Failed to get state for pose action \"") + action_name + "\".").data());
if (state.isActive) {
XrSpace pose_space = XR_NULL_HANDLE;
for (auto &[path, space] : m_spaces) {
if (space.getSubactionPath() == state_info.subactionPath) {
pose_space = space.getSpace();
break;
}
}
XrSpace pose_space = ((subaction != nullptr) && (subaction->space != nullptr)) ?
subaction->space->getSpace() :
XR_NULL_HANDLE;
if (pose_space != XR_NULL_HANDLE) {
XrSpaceLocation space_location{XR_TYPE_SPACE_LOCATION};
CHECK_XR(

View File

@ -34,38 +34,53 @@
class GHOST_XrActionSpace {
public:
GHOST_XrActionSpace() = delete; /* Default constructor for map storage. */
GHOST_XrActionSpace(XrInstance instance,
XrSession session,
GHOST_XrActionSpace(XrSession session,
XrAction action,
const GHOST_XrActionSpaceInfo &info,
uint32_t subaction_idx);
const char *action_name,
const char *profile_path,
XrPath subaction_path,
const char *subaction_path_str,
const GHOST_XrPose &pose);
~GHOST_XrActionSpace();
XrSpace getSpace() const;
const XrPath &getSubactionPath() const;
private:
XrSpace m_space = XR_NULL_HANDLE;
XrPath m_subaction_path = XR_NULL_PATH;
};
/* -------------------------------------------------------------------- */
typedef struct GHOST_XrSubactionData {
XrPath subaction_path = XR_NULL_PATH;
float float_threshold;
int16_t axis_flag;
std::unique_ptr<GHOST_XrActionSpace> space = nullptr;
} GHOST_XrSubactionData;
/* -------------------------------------------------------------------- */
class GHOST_XrActionProfile {
public:
GHOST_XrActionProfile() = delete; /* Default constructor for map storage. */
GHOST_XrActionProfile(XrInstance instance,
XrSession session,
XrAction action,
const char *profile_path,
const GHOST_XrActionBindingInfo &info);
GHOST_XrActionType type,
const GHOST_XrActionProfileInfo &info);
~GHOST_XrActionProfile() = default;
XrPath getProfile() const;
const GHOST_XrSubactionData *getSubaction(XrPath subaction_path) const;
void getBindings(XrAction action,
std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const;
private:
XrPath m_profile = XR_NULL_PATH;
/* Bindings identified by interaction (user (subaction) + component) path. */
/** Subaction data identified by user (subaction) path. */
std::map<std::string, GHOST_XrSubactionData> m_subaction_data;
/** Bindings identified by interaction (user (subaction) + component) path. */
std::map<std::string, XrPath> m_bindings;
};
@ -77,12 +92,9 @@ class GHOST_XrAction {
GHOST_XrAction(XrInstance instance, XrActionSet action_set, const GHOST_XrActionInfo &info);
~GHOST_XrAction();
bool createSpace(XrInstance instance, XrSession session, const GHOST_XrActionSpaceInfo &info);
void destroySpace(const char *subaction_path);
bool createBinding(XrInstance instance,
const char *profile_path,
const GHOST_XrActionBindingInfo &info);
XrSession session,
const GHOST_XrActionProfileInfo &info);
void destroyBinding(const char *profile_path);
void updateState(XrSession session,
@ -105,12 +117,13 @@ class GHOST_XrAction {
std::vector<XrPath> m_subaction_paths;
/** States for each subaction path. */
void *m_states;
/** Input thresholds/regions for each subaction path. */
float *m_float_thresholds;
int16_t *m_axis_flags;
std::unique_ptr<GHOST_C_CustomDataWrapper> m_custom_data_ = nullptr; /* wmXrAction */
/* Spaces identified by user (subaction) path. */
std::map<std::string, GHOST_XrActionSpace> m_spaces;
/* Profiles identified by interaction profile path. */
/** Profiles identified by interaction profile path. */
std::map<std::string, GHOST_XrActionProfile> m_profiles;
};

View File

@ -610,57 +610,6 @@ void GHOST_XrSession::destroyActions(const char *action_set_name,
}
}
bool GHOST_XrSession::createActionSpaces(const char *action_set_name,
uint32_t count,
const GHOST_XrActionSpaceInfo *infos)
{
GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
if (action_set == nullptr) {
return false;
}
XrInstance instance = m_context->getInstance();
XrSession session = m_oxr->session;
for (uint32_t action_idx = 0; action_idx < count; ++action_idx) {
const GHOST_XrActionSpaceInfo &info = infos[action_idx];
GHOST_XrAction *action = action_set->findAction(info.action_name);
if (action == nullptr) {
continue;
}
if (!action->createSpace(instance, session, info)) {
return false;
}
}
return true;
}
void GHOST_XrSession::destroyActionSpaces(const char *action_set_name,
uint32_t count,
const GHOST_XrActionSpaceInfo *infos)
{
GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
if (action_set == nullptr) {
return;
}
for (uint32_t action_idx = 0; action_idx < count; ++action_idx) {
const GHOST_XrActionSpaceInfo &info = infos[action_idx];
GHOST_XrAction *action = action_set->findAction(info.action_name);
if (action == nullptr) {
continue;
}
for (uint32_t subaction_idx = 0; subaction_idx < info.count_subaction_paths; ++subaction_idx) {
action->destroySpace(info.subaction_paths[subaction_idx]);
}
}
}
bool GHOST_XrSession::createActionBindings(const char *action_set_name,
uint32_t count,
const GHOST_XrActionProfileInfo *infos)
@ -671,21 +620,17 @@ bool GHOST_XrSession::createActionBindings(const char *action_set_name,
}
XrInstance instance = m_context->getInstance();
XrSession session = m_oxr->session;
for (uint32_t profile_idx = 0; profile_idx < count; ++profile_idx) {
const GHOST_XrActionProfileInfo &info = infos[profile_idx];
const char *profile_path = info.profile_path;
for (uint32_t binding_idx = 0; binding_idx < info.count_bindings; ++binding_idx) {
const GHOST_XrActionBindingInfo &binding = info.bindings[binding_idx];
GHOST_XrAction *action = action_set->findAction(binding.action_name);
if (action == nullptr) {
continue;
}
action->createBinding(instance, profile_path, binding);
GHOST_XrAction *action = action_set->findAction(info.action_name);
if (action == nullptr) {
continue;
}
action->createBinding(instance, session, info);
}
return true;
@ -693,27 +638,21 @@ bool GHOST_XrSession::createActionBindings(const char *action_set_name,
void GHOST_XrSession::destroyActionBindings(const char *action_set_name,
uint32_t count,
const GHOST_XrActionProfileInfo *infos)
const char *const *action_names,
const char *const *profile_paths)
{
GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
if (action_set == nullptr) {
return;
}
for (uint32_t profile_idx = 0; profile_idx < count; ++profile_idx) {
const GHOST_XrActionProfileInfo &info = infos[profile_idx];
const char *profile_path = info.profile_path;
for (uint32_t binding_idx = 0; binding_idx < info.count_bindings; ++binding_idx) {
const GHOST_XrActionBindingInfo &binding = info.bindings[binding_idx];
GHOST_XrAction *action = action_set->findAction(binding.action_name);
if (action == nullptr) {
continue;
}
action->destroyBinding(profile_path);
for (uint32_t i = 0; i < count; ++i) {
GHOST_XrAction *action = action_set->findAction(action_names[i]);
if (action == nullptr) {
continue;
}
action->destroyBinding(profile_paths[i]);
}
}

View File

@ -60,18 +60,13 @@ class GHOST_XrSession {
void destroyActions(const char *action_set_name,
uint32_t count,
const char *const *action_names);
bool createActionSpaces(const char *action_set_name,
uint32_t count,
const GHOST_XrActionSpaceInfo *infos);
void destroyActionSpaces(const char *action_set_name,
uint32_t count,
const GHOST_XrActionSpaceInfo *infos);
bool createActionBindings(const char *action_set_name,
uint32_t count,
const GHOST_XrActionProfileInfo *infos);
void destroyActionBindings(const char *action_set_name,
uint32_t count,
const GHOST_XrActionProfileInfo *infos);
const char *const *action_names,
const char *const *profile_paths);
bool attachActionSets();
/**

View File

@ -74,6 +74,15 @@ typedef enum eXrOpFlag {
XR_OP_MODAL = 2,
} eXrOpFlag;
typedef enum eXrAxisFlag {
/** For axis-based inputs (thumbstick/trackpad/etc). Determines the region for action execution
(mutually exclusive per axis). */
XR_AXIS0_POS = (1 << 0),
XR_AXIS0_NEG = (1 << 1),
XR_AXIS1_POS = (1 << 2),
XR_AXIS1_NEG = (1 << 3),
} eXrAxisFlag;
#ifdef __cplusplus
}
#endif

View File

@ -203,7 +203,7 @@ if(WITH_XR_OPENXR)
list(APPEND SRC
xr/intern/wm_xr.c
xr/intern/wm_xr_actions.c
xr/intern/wm_xr_action.c
xr/intern/wm_xr_draw.c
xr/intern/wm_xr_session.c

View File

@ -978,34 +978,24 @@ bool WM_xr_action_create(wmXrData *xr,
eXrActionType type,
unsigned int count_subaction_paths,
const char **subaction_paths,
const float *float_threshold,
struct wmOperatorType *ot,
struct IDProperty *op_properties,
eXrOpFlag op_flag);
void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name);
bool WM_xr_action_space_create(wmXrData *xr,
const char *action_set_name,
const char *action_name,
unsigned int count_subaction_paths,
const char **subaction_paths,
const struct wmXrPose *poses);
void WM_xr_action_space_destroy(wmXrData *xr,
const char *action_set_name,
const char *action_name,
unsigned int count_subaction_paths,
const char **subaction_paths);
bool WM_xr_action_binding_create(wmXrData *xr,
const char *action_set_name,
const char *profile_path,
const char *action_name,
unsigned int count_interaction_paths,
const char **interaction_paths);
const char *profile_path,
unsigned int count_subaction_paths,
const char **subaction_paths,
const char **component_paths,
const float *float_thresholds,
const eXrAxisFlag *axis_flags,
const struct wmXrPose *poses);
void WM_xr_action_binding_destroy(wmXrData *xr,
const char *action_set_name,
const char *profile_path,
const char *action_name,
unsigned int count_interaction_paths,
const char **interaction_paths);
const char *profile_path);
/* If action_set_name is NULL, then all action sets will be treated as active. */
bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name);

View File

@ -68,7 +68,6 @@ static wmXrAction *action_create(const char *action_name,
eXrActionType type,
unsigned int count_subaction_paths,
const char **subaction_paths,
const float *float_threshold,
wmOperatorType *ot,
IDProperty *op_properties,
eXrOpFlag op_flag)
@ -109,10 +108,15 @@ static wmXrAction *action_create(const char *action_name,
action->states = MEM_calloc_arrayN(count, size, "XrAction_States");
action->states_prev = MEM_calloc_arrayN(count, size, "XrAction_StatesPrev");
if (float_threshold) {
BLI_assert(type == XR_FLOAT_INPUT || type == XR_VECTOR2F_INPUT);
action->float_threshold = *float_threshold;
CLAMP(action->float_threshold, 0.0f, 1.0f);
const bool is_float_action = (type == XR_FLOAT_INPUT || type == XR_VECTOR2F_INPUT);
const bool is_button_action = (is_float_action || type == XR_BOOLEAN_INPUT);
if (is_float_action) {
action->float_thresholds = MEM_calloc_arrayN(
count, sizeof(*action->float_thresholds), "XrAction_FloatThresholds");
}
if (is_button_action) {
action->axis_flags = MEM_calloc_arrayN(
count, sizeof(*action->axis_flags), "XrAction_AxisFlags");
}
action->ot = ot;
@ -140,6 +144,9 @@ static void action_destroy(void *val)
MEM_SAFE_FREE(action->states);
MEM_SAFE_FREE(action->states_prev);
MEM_SAFE_FREE(action->float_thresholds);
MEM_SAFE_FREE(action->axis_flags);
MEM_freeN(action);
}
@ -198,7 +205,6 @@ bool WM_xr_action_create(wmXrData *xr,
eXrActionType type,
unsigned int count_subaction_paths,
const char **subaction_paths,
const float *float_threshold,
wmOperatorType *ot,
IDProperty *op_properties,
eXrOpFlag op_flag)
@ -211,7 +217,6 @@ bool WM_xr_action_create(wmXrData *xr,
type,
count_subaction_paths,
subaction_paths,
float_threshold,
ot,
op_properties,
op_flag);
@ -221,6 +226,8 @@ bool WM_xr_action_create(wmXrData *xr,
.count_subaction_paths = count_subaction_paths,
.subaction_paths = subaction_paths,
.states = action->states,
.float_thresholds = action->float_thresholds,
.axis_flags = (int16_t *)action->axis_flags,
.customdata_free_fn = action_destroy,
.customdata = action,
};
@ -257,6 +264,11 @@ void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char
return;
}
wmXrAction *action = action_find(xr, action_set_name, action_name);
if (!action) {
return;
}
if (action_set->controller_pose_action &&
STREQ(action_set->controller_pose_action->name, action_name)) {
if (action_set == xr->runtime->session_state.active_action_set) {
@ -269,98 +281,60 @@ void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char
action_set->active_modal_action = NULL;
}
wmXrAction *action = action_find(xr, action_set_name, action_name);
if (!action) {
return;
}
GHOST_XrDestroyActions(xr->runtime->context, action_set_name, 1, &action_name);
}
bool WM_xr_action_space_create(wmXrData *xr,
const char *action_set_name,
const char *action_name,
unsigned int count_subaction_paths,
const char **subaction_paths,
const wmXrPose *poses)
{
GHOST_XrActionSpaceInfo info = {
.action_name = action_name,
.count_subaction_paths = count_subaction_paths,
.subaction_paths = subaction_paths,
};
GHOST_XrPose *ghost_poses = MEM_malloc_arrayN(
count_subaction_paths, sizeof(*ghost_poses), __func__);
for (unsigned int i = 0; i < count_subaction_paths; ++i) {
const wmXrPose *pose = &poses[i];
GHOST_XrPose *ghost_pose = &ghost_poses[i];
copy_v3_v3(ghost_pose->position, pose->position);
copy_qt_qt(ghost_pose->orientation_quat, pose->orientation_quat);
}
info.poses = ghost_poses;
bool ret = GHOST_XrCreateActionSpaces(xr->runtime->context, action_set_name, 1, &info) ? true :
false;
MEM_freeN(ghost_poses);
return ret;
}
void WM_xr_action_space_destroy(wmXrData *xr,
const char *action_set_name,
const char *action_name,
unsigned int count_subaction_paths,
const char **subaction_paths)
{
GHOST_XrActionSpaceInfo info = {
.action_name = action_name,
.count_subaction_paths = count_subaction_paths,
.subaction_paths = subaction_paths,
};
GHOST_XrDestroyActionSpaces(xr->runtime->context, action_set_name, 1, &info);
}
bool WM_xr_action_binding_create(wmXrData *xr,
const char *action_set_name,
const char *profile_path,
const char *action_name,
unsigned int count_interaction_paths,
const char **interaction_paths)
const char *profile_path,
unsigned int count_subaction_paths,
const char **subaction_paths,
const char **component_paths,
const float *float_thresholds,
const eXrAxisFlag *axis_flags,
const struct wmXrPose *poses)
{
GHOST_XrActionBindingInfo binding_info = {
.action_name = action_name,
.count_interaction_paths = count_interaction_paths,
.interaction_paths = interaction_paths,
};
GHOST_XrActionBindingInfo *binding_infos = MEM_calloc_arrayN(
count_subaction_paths, sizeof(*binding_infos), __func__);
for (unsigned int i = 0; i < count_subaction_paths; ++i) {
GHOST_XrActionBindingInfo *binding_info = &binding_infos[i];
binding_info->component_path = component_paths[i];
if (float_thresholds) {
binding_info->float_threshold = float_thresholds[i];
}
if (axis_flags) {
binding_info->axis_flag = axis_flags[i];
}
if (poses) {
copy_v3_v3(binding_info->pose.position, poses[i].position);
copy_qt_qt(binding_info->pose.orientation_quat, poses[i].orientation_quat);
}
}
GHOST_XrActionProfileInfo profile_info = {
.action_name = action_name,
.profile_path = profile_path,
.count_bindings = 1,
.bindings = &binding_info,
.count_subaction_paths = count_subaction_paths,
.subaction_paths = subaction_paths,
.bindings = binding_infos,
};
return GHOST_XrCreateActionBindings(xr->runtime->context, action_set_name, 1, &profile_info);
bool ret = GHOST_XrCreateActionBindings(xr->runtime->context, action_set_name, 1, &profile_info);
MEM_freeN(binding_infos);
return ret;
}
void WM_xr_action_binding_destroy(wmXrData *xr,
const char *action_set_name,
const char *profile_path,
const char *action_name,
unsigned int count_interaction_paths,
const char **interaction_paths)
const char *profile_path)
{
GHOST_XrActionBindingInfo binding_info = {
.action_name = action_name,
.count_interaction_paths = count_interaction_paths,
.interaction_paths = interaction_paths,
};
GHOST_XrActionProfileInfo profile_info = {
.profile_path = profile_path,
.count_bindings = 1,
.bindings = &binding_info,
};
GHOST_XrDestroyActionBindings(xr->runtime->context, action_set_name, 1, &profile_info);
GHOST_XrDestroyActionBindings(
xr->runtime->context, action_set_name, 1, &action_name, &profile_path);
}
bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name)
@ -427,12 +401,12 @@ bool WM_xr_action_state_get(const wmXrData *xr,
return false;
}
BLI_assert(action->type == (eXrActionType)r_state->type);
r_state->type = (int)action->type;
/* Find the action state corresponding to the subaction path. */
for (unsigned int i = 0; i < action->count_subaction_paths; ++i) {
if (STREQ(subaction_path, action->subaction_paths[i])) {
switch ((eXrActionType)r_state->type) {
switch (action->type) {
case XR_BOOLEAN_INPUT:
r_state->state_boolean = ((bool *)action->states)[i];
break;

View File

@ -122,8 +122,9 @@ typedef struct wmXrAction {
/** Previous states, stored to determine XR events. */
void *states_prev;
/** Input threshold for float/vector2f actions. */
float float_threshold;
/** Input thresholds/regions for each subaction path. */
float *float_thresholds;
eXrAxisFlag *axis_flags;
/** The currently active subaction path (if any) for modal actions. */
char **active_modal_path;