diff --git a/src/xrt/state_trackers/oxr/CMakeLists.txt b/src/xrt/state_trackers/oxr/CMakeLists.txt index 9466e0893..81ebfe2f6 100644 --- a/src/xrt/state_trackers/oxr/CMakeLists.txt +++ b/src/xrt/state_trackers/oxr/CMakeLists.txt @@ -39,6 +39,7 @@ set(OXR_SOURCE_FILES oxr_two_call.h oxr_verify.cpp oxr_vulkan.c + oxr_xdev.c ) # Use OBJECT to not create a archive, since it just gets in the way. diff --git a/src/xrt/state_trackers/oxr/oxr_api_action.c b/src/xrt/state_trackers/oxr/oxr_api_action.c index 38f17701b..6299eb58d 100644 --- a/src/xrt/state_trackers/oxr/oxr_api_action.c +++ b/src/xrt/state_trackers/oxr/oxr_api_action.c @@ -41,12 +41,14 @@ oxr_xrSyncActionData(XrSession session, } for (uint32_t i = 0; i < countActionSets; i++) { - struct oxr_action_set* act_set; + struct oxr_action_set* act_set = NULL; OXR_VERIFY_ARG_TYPE_AND_NULL(&log, (&actionSets[i]), XR_TYPE_ACTIVE_ACTION_SET); OXR_VERIFY_ACTIONSET_NOT_NULL(&log, actionSets[i].actionSet, act_set); - //! @todo verify path. + + oxr_verify_subaction_path_sync(&log, sess->sys->inst, + actionSets[i].subactionPath, i); } return oxr_action_sync_data(&log, sess, countActionSets, actionSets); @@ -187,6 +189,15 @@ oxr_xrCreateAction(XrActionSet actionSet, OXR_VERIFY_ARG_LOCALIZED_NAME(&log, createInfo->localizedActionName); OXR_VERIFY_ARG_NOT_NULL(&log, action); + struct oxr_instance* inst = act_set->sess->sys->inst; + + ret = oxr_verify_subaction_paths_create( + &log, inst, createInfo->countSubactionPaths, + createInfo->subactionPaths, "createInfo->subactionPaths"); + if (ret != XR_SUCCESS) { + return ret; + } + ret = oxr_action_create(&log, act_set, createInfo, &act); if (ret != XR_SUCCESS) { return ret; @@ -213,16 +224,39 @@ oxr_xrGetActionStateBoolean(XrAction action, const XrPath* subactionPaths, XrActionStateBoolean* data) { + XrPath subactionPath = XR_NULL_PATH; + struct oxr_sub_paths sub_paths = {0}; struct oxr_action* act; struct oxr_logger log; + XrResult ret; OXR_VERIFY_ACTION_AND_INIT_LOG(&log, action, act, "xrGetActionStateBoolean"); OXR_VERIFY_ARG_TYPE_AND_NULL(&log, data, XR_TYPE_ACTION_STATE_BOOLEAN); OXR_VERIFY_SUBACTION_PATHS(&log, countSubactionPaths, subactionPaths); - //! @todo verify paths - return oxr_action_get_boolean(&log, act, countSubactionPaths, - subactionPaths, data); + if (act->action_type != XR_INPUT_ACTION_TYPE_BOOLEAN) { + return oxr_error(&log, XR_ERROR_ACTION_TYPE_MISMATCH, + " not created with pose type"); + } + + // Trust me. + if (countSubactionPaths > 1) { + return oxr_error(&log, XR_ERROR_PATH_INVALID, + " can not handle more then one subactionPath"); + } + + if (countSubactionPaths == 1) { + subactionPath = subactionPaths[0]; + } + + ret = oxr_verify_subaction_path_get(&log, act->act_set->sess->sys->inst, + subactionPath, &act->sub_paths, + &sub_paths, "subactionPaths[0]"); + if (ret != XR_SUCCESS) { + return ret; + } + + return oxr_action_get_boolean(&log, act, sub_paths, data); } XrResult @@ -231,16 +265,39 @@ oxr_xrGetActionStateVector1f(XrAction action, const XrPath* subactionPaths, XrActionStateVector1f* data) { + XrPath subactionPath = XR_NULL_PATH; + struct oxr_sub_paths sub_paths = {0}; struct oxr_action* act; struct oxr_logger log; + XrResult ret; OXR_VERIFY_ACTION_AND_INIT_LOG(&log, action, act, "xrGetActionStateVector1f"); OXR_VERIFY_ARG_TYPE_AND_NULL(&log, data, XR_TYPE_ACTION_STATE_VECTOR1F); OXR_VERIFY_SUBACTION_PATHS(&log, countSubactionPaths, subactionPaths); - //! @todo verify paths - return oxr_action_get_vector1f(&log, act, countSubactionPaths, - subactionPaths, data); + if (act->action_type != XR_INPUT_ACTION_TYPE_VECTOR1F) { + return oxr_error(&log, XR_ERROR_ACTION_TYPE_MISMATCH, + " not created with float type"); + } + + // Trust me. + if (countSubactionPaths > 1) { + return oxr_error(&log, XR_ERROR_PATH_INVALID, + " can not handle more then one subactionPath"); + } + + if (countSubactionPaths == 1) { + subactionPath = subactionPaths[0]; + } + + ret = oxr_verify_subaction_path_get(&log, act->act_set->sess->sys->inst, + subactionPath, &act->sub_paths, + &sub_paths, "subactionPaths[0]"); + if (ret != XR_SUCCESS) { + return ret; + } + + return oxr_action_get_vector1f(&log, act, sub_paths, data); } XrResult @@ -249,16 +306,39 @@ oxr_xrGetActionStateVector2f(XrAction action, const XrPath* subactionPaths, XrActionStateVector2f* data) { + XrPath subactionPath = XR_NULL_PATH; + struct oxr_sub_paths sub_paths = {0}; struct oxr_action* act; struct oxr_logger log; + XrResult ret; OXR_VERIFY_ACTION_AND_INIT_LOG(&log, action, act, "xrGetActionStateVector2f"); OXR_VERIFY_ARG_TYPE_AND_NULL(&log, data, XR_TYPE_ACTION_STATE_VECTOR2F); OXR_VERIFY_SUBACTION_PATHS(&log, countSubactionPaths, subactionPaths); - //! @todo verify paths - return oxr_action_get_vector2f(&log, act, countSubactionPaths, - subactionPaths, data); + if (act->action_type != XR_INPUT_ACTION_TYPE_VECTOR2F) { + return oxr_error(&log, XR_ERROR_ACTION_TYPE_MISMATCH, + " not created with float[2] type"); + } + + // Trust me. + if (countSubactionPaths > 1) { + return oxr_error(&log, XR_ERROR_PATH_INVALID, + " can not handle more then one subactionPath"); + } + + if (countSubactionPaths == 1) { + subactionPath = subactionPaths[0]; + } + + ret = oxr_verify_subaction_path_get(&log, act->act_set->sess->sys->inst, + subactionPath, &act->sub_paths, + &sub_paths, "subactionPaths[0]"); + if (ret != XR_SUCCESS) { + return ret; + } + + return oxr_action_get_vector2f(&log, act, sub_paths, data); } XrResult @@ -266,14 +346,27 @@ oxr_xrGetActionStatePose(XrAction action, XrPath subactionPath, XrActionStatePose* data) { + struct oxr_sub_paths sub_paths = {0}; struct oxr_action* act; struct oxr_logger log; + XrResult ret; OXR_VERIFY_ACTION_AND_INIT_LOG(&log, action, act, "xrGetActionStatePose"); OXR_VERIFY_ARG_TYPE_AND_NULL(&log, data, XR_TYPE_ACTION_STATE_POSE); - //! @todo verify path - return oxr_action_get_pose(&log, act, subactionPath, data); + if (act->action_type != XR_INPUT_ACTION_TYPE_POSE) { + return oxr_error(&log, XR_ERROR_ACTION_TYPE_MISMATCH, + " not created with pose type"); + } + + ret = oxr_verify_subaction_path_get(&log, act->act_set->sess->sys->inst, + subactionPath, &act->sub_paths, + &sub_paths, "subactionPath"); + if (ret != XR_SUCCESS) { + return ret; + } + + return oxr_action_get_pose(&log, act, sub_paths, data); } XrResult @@ -311,6 +404,11 @@ oxr_xrApplyHapticFeedback(XrAction hapticAction, OXR_VERIFY_SUBACTION_PATHS(&log, countSubactionPaths, subactionPaths); //! @todo verify paths + if (act->action_type != XR_OUTPUT_ACTION_TYPE_VIBRATION) { + return oxr_error(&log, XR_ERROR_ACTION_TYPE_MISMATCH, + " not created with output vibration type"); + } + return oxr_action_apply_haptic_feedback(&log, act, countSubactionPaths, subactionPaths, hapticEvent); } @@ -327,6 +425,11 @@ oxr_xrStopHapticFeedback(XrAction hapticAction, OXR_VERIFY_SUBACTION_PATHS(&log, countSubactionPaths, subactionPaths); //! @todo verify paths + if (act->action_type != XR_OUTPUT_ACTION_TYPE_VIBRATION) { + return oxr_error(&log, XR_ERROR_ACTION_TYPE_MISMATCH, + " not created with output vibration type"); + } + return oxr_action_stop_haptic_feedback(&log, act, countSubactionPaths, subactionPaths); } diff --git a/src/xrt/state_trackers/oxr/oxr_api_verify.h b/src/xrt/state_trackers/oxr/oxr_api_verify.h index e47df4044..46cbadfc7 100644 --- a/src/xrt/state_trackers/oxr/oxr_api_verify.h +++ b/src/xrt/state_trackers/oxr/oxr_api_verify.h @@ -170,6 +170,36 @@ oxr_verify_localized_name(struct oxr_logger*, uint32_t array_size, const char* name); +/*! + * Verify a set of subaction paths for action creation. + */ +XrResult +oxr_verify_subaction_paths_create(struct oxr_logger* log, + struct oxr_instance* inst, + uint32_t countSubactionPaths, + const XrPath* subactionPaths, + const char* variable); + +/*! + * Verify a set of subaction paths for action sync. + */ +XrResult +oxr_verify_subaction_path_sync(struct oxr_logger* log, + struct oxr_instance* inst, + XrPath path, + uint32_t index); + +/*! + * Verify a set of subaction paths for action state get. + */ +XrResult +oxr_verify_subaction_path_get(struct oxr_logger* log, + struct oxr_instance* inst, + XrPath path, + const struct oxr_sub_paths* act_sub_paths, + struct oxr_sub_paths* out_sub_paths, + const char* variable); + XrResult oxr_verify_XrSessionCreateInfo(struct oxr_logger*, const struct oxr_instance*, diff --git a/src/xrt/state_trackers/oxr/oxr_input.c b/src/xrt/state_trackers/oxr/oxr_input.c index 253feb4fd..2159f5c88 100644 --- a/src/xrt/state_trackers/oxr/oxr_input.c +++ b/src/xrt/state_trackers/oxr/oxr_input.c @@ -23,6 +23,73 @@ #include "oxr_handle.h" +/* + * + * Struct and defines. + * + */ + +enum sub_action_path +{ + // clang-format off + OXR_SUB_ACTION_PATH_USER = 1 << 0, + OXR_SUB_ACTION_PATH_HEAD = 1 << 1, + OXR_SUB_ACTION_PATH_LEFT = 1 << 2, + OXR_SUB_ACTION_PATH_RIGHT = 1 << 3, + OXR_SUB_ACTION_PATH_GAMEPAD = 1 << 4, + // clang-format on +}; + + +/* + * + * Pre declare functions. + * + */ + +static void +oxr_session_get_source_set(struct oxr_session* sess, + XrActionSet actionSet, + struct oxr_source_set** src_set, + struct oxr_action_set** act_set); + +static void +oxr_session_get_source(struct oxr_session* sess, + uint32_t act_key, + struct oxr_source** out_src); + +static void +oxr_source_cache_update(struct oxr_logger* log, + struct oxr_session* sess, + struct oxr_action* act, + struct oxr_source_cache* cache, + int64_t time, + bool select); + +static void +oxr_source_update(struct oxr_logger* log, + struct oxr_session* sess, + struct oxr_action* act, + int64_t time, + struct oxr_sub_paths sub_paths); + +static void +oxr_source_bind_inputs(struct oxr_logger* log, + struct oxr_session* sess, + struct oxr_action* act, + struct oxr_source_cache* cache, + enum sub_action_path sub_path); + +static XrResult +oxr_source_destroy_cb(struct oxr_logger* log, struct oxr_handle_base* hb); + +static XrResult +oxr_source_create(struct oxr_logger* log, + struct oxr_session* sess, + struct oxr_action* act, + struct oxr_source** out_src); + + /* * * Action set functions @@ -32,7 +99,7 @@ static XrResult oxr_action_set_destroy_cb(struct oxr_logger* log, struct oxr_handle_base* hb) { - //! @todo Move to oxr_action.h + //! @todo Move to oxr_objects.h struct oxr_action_set* act_set = (struct oxr_action_set*)hb; free(act_set); @@ -46,10 +113,16 @@ oxr_action_set_create(struct oxr_logger* log, const XrActionSetCreateInfo* createInfo, struct oxr_action_set** out_act_set) { + // Mod music for all! + static uint32_t key_gen = 0; + //! @todo Implement more fully. struct oxr_action_set* act_set = NULL; OXR_ALLOCATE_HANDLE_OR_RETURN(log, act_set, OXR_XR_DEBUG_ACTIONSET, oxr_action_set_destroy_cb, &sess->handle); + + act_set->key = key_gen++; + act_set->generation = 1; act_set->sess = sess; *out_act_set = act_set; @@ -67,9 +140,13 @@ oxr_action_set_create(struct oxr_logger* log, static XrResult oxr_action_destroy_cb(struct oxr_logger* log, struct oxr_handle_base* hb) { - //! @todo Move to oxr_action.h + //! @todo Move to oxr_objects.h struct oxr_action* act = (struct oxr_action*)hb; + // Need to keep track of the generation of this + // action set to rebuild bindings when used. + act->act_set->generation++; + free(act); return XR_SUCCESS; @@ -81,11 +158,29 @@ oxr_action_create(struct oxr_logger* log, const XrActionCreateInfo* createInfo, struct oxr_action** out_act) { - //! @todo Implement more fully. + struct oxr_instance* inst = act_set->sess->sys->inst; + struct oxr_sub_paths sub_paths = {0}; + + // Mod music for all! + static uint32_t key_gen = 0; + + oxr_classify_sub_action_paths(log, inst, + createInfo->countSubactionPaths, + createInfo->subactionPaths, &sub_paths); + struct oxr_action* act = NULL; OXR_ALLOCATE_HANDLE_OR_RETURN(log, act, OXR_XR_DEBUG_ACTION, oxr_action_destroy_cb, &act_set->handle); + act->key = key_gen++; act->act_set = act_set; + act->sub_paths = sub_paths; + act->action_type = createInfo->actionType; + + strncpy(act->name, createInfo->actionName, sizeof(act->name)); + + // Need to keep track of the generation of this + // action set to rebuild bindings when used. + act_set->generation++; *out_act = act; @@ -93,18 +188,517 @@ oxr_action_create(struct oxr_logger* log, } +/* + * + * "Exproted" helper functions. + * + */ + +void +oxr_classify_sub_action_paths(struct oxr_logger* log, + struct oxr_instance* inst, + uint32_t num_subaction_paths, + const XrPath* subaction_paths, + struct oxr_sub_paths* sub_paths) +{ + const char* str = NULL; + size_t length = 0; + + // Reset the sub_paths completely. + memset(sub_paths, 0, sizeof(*sub_paths)); + + if (num_subaction_paths == 0) { + sub_paths->any = true; + return; + } + + for (uint32_t i = 0; i < num_subaction_paths; i++) { + XrPath path = subaction_paths[i]; + + if (path == XR_NULL_PATH) { + sub_paths->any = true; + } else if (path == inst->path_cache.user) { + sub_paths->user = true; + } else if (path == inst->path_cache.head) { + sub_paths->head = true; + } else if (path == inst->path_cache.left) { + sub_paths->left = true; + } else if (path == inst->path_cache.right) { + sub_paths->right = true; + } else if (path == inst->path_cache.gamepad) { + sub_paths->gamepad = true; + } else { + oxr_path_get_string(log, inst, path, &str, &length); + + oxr_warn(log, " unrecognized sub action path '%s'", + str); + } + } +} + +XrResult +oxr_source_get_pose_input(struct oxr_logger* log, + struct oxr_session* sess, + uint32_t act_key, + const struct oxr_sub_paths* sub_paths, + struct oxr_source_input** out_input) +{ + struct oxr_source* src; + + oxr_session_get_source(sess, act_key, &src); + + if (src == NULL) { + return XR_SUCCESS; + } + + // Priority of inputs. + if (src->head.current.active && (sub_paths->head || sub_paths->any)) { + *out_input = src->head.inputs; + return XR_SUCCESS; + } + if (src->left.current.active && (sub_paths->left || sub_paths->any)) { + *out_input = src->left.inputs; + return XR_SUCCESS; + } + if (src->right.current.active && (sub_paths->right || sub_paths->any)) { + *out_input = src->right.inputs; + return XR_SUCCESS; + } + if (src->gamepad.current.active && + (sub_paths->gamepad || sub_paths->any)) { + *out_input = src->gamepad.inputs; + return XR_SUCCESS; + } + if (src->user.current.active && (sub_paths->user || sub_paths->any)) { + *out_input = src->user.inputs; + return XR_SUCCESS; + } + + return XR_SUCCESS; +} + + +/* + * + * MEGA HACK Functions! + * + */ + +static void +MEGA_HACK_get_binding(struct oxr_logger* log, + struct oxr_session* sess, + struct oxr_action* act, + enum sub_action_path sub_path, + struct oxr_source_input inputs[16], + uint32_t* num_inputs, + struct oxr_source_output outputs[16], + uint32_t* num_outputs) +{ + struct xrt_input* input = NULL; + struct xrt_output* output = NULL; + struct xrt_device* xdev = NULL; + + switch (sub_path) { + case OXR_SUB_ACTION_PATH_USER: xdev = NULL; break; + case OXR_SUB_ACTION_PATH_HEAD: xdev = sess->sys->head; break; + case OXR_SUB_ACTION_PATH_LEFT: xdev = sess->sys->left; break; + case OXR_SUB_ACTION_PATH_RIGHT: xdev = sess->sys->right; break; + case OXR_SUB_ACTION_PATH_GAMEPAD: xdev = NULL; break; + default: break; + } + + if (strcmp(act->name, "grip_object") == 0) { + oxr_xdev_find_input(xdev, XRT_INPUT_PSMV_TRIGGER_VALUE, &input); + } else if (strcmp(act->name, "hand_pose") == 0) { + oxr_xdev_find_input(xdev, XRT_INPUT_PSMV_BODY_CENTER_POSE, + &input); + } else if (strcmp(act->name, "vibrate_hand") == 0) { + oxr_xdev_find_output( + xdev, XRT_OUTPUT_NAME_PSMV_RUMBLE_VIBRATION, &output); + } + + if (input != NULL) { + uint32_t index = (*num_inputs)++; + inputs[index].input = input; + inputs[index].xdev = xdev; + } + + if (output != NULL) { + uint32_t index = (*num_outputs)++; + outputs[index].name = output->name; + outputs[index].xdev = xdev; + } +} + + +/* + * + * Source set functions + * + */ + +static XrResult +oxr_source_set_destroy_cb(struct oxr_logger* log, struct oxr_handle_base* hb) +{ + //! @todo Move to oxr_objects.h + struct oxr_source_set* src_set = (struct oxr_source_set*)hb; + + free(src_set); + + return XR_SUCCESS; +} + +static XrResult +oxr_source_set_create(struct oxr_logger* log, + struct oxr_action_set* act_set, + struct oxr_source_set** out_src_set) +{ + struct oxr_session* sess = act_set->sess; + struct oxr_source_set* src_set = NULL; + OXR_ALLOCATE_HANDLE_OR_RETURN(log, src_set, OXR_XR_DEBUG_SOURCESET, + oxr_source_set_destroy_cb, &sess->handle); + + src_set->generation = act_set->generation; + + u_hashmap_int_insert(sess->act_sets, act_set->key, src_set); + + *out_src_set = src_set; + + return XR_SUCCESS; +} + + +/* + * + * Source functions + * + */ + +static XrResult +oxr_source_destroy_cb(struct oxr_logger* log, struct oxr_handle_base* hb) +{ + //! @todo Move to oxr_objects.h + struct oxr_source* src = (struct oxr_source*)hb; + + free(src->user.inputs); + free(src->user.outputs); + free(src->head.inputs); + free(src->head.outputs); + free(src->left.inputs); + free(src->left.outputs); + free(src->right.inputs); + free(src->right.outputs); + free(src->gamepad.inputs); + free(src->gamepad.outputs); + free(src); + + return XR_SUCCESS; +} + +static XrResult +oxr_source_create(struct oxr_logger* log, + struct oxr_session* sess, + struct oxr_action* act, + struct oxr_source** out_src) +{ + struct oxr_source* src = NULL; + OXR_ALLOCATE_HANDLE_OR_RETURN(log, src, OXR_XR_DEBUG_SOURCE, + oxr_source_destroy_cb, &sess->handle); + + u_hashmap_int_insert(sess->sources, act->key, src); + + if (act->sub_paths.user || act->sub_paths.any) { + oxr_source_bind_inputs(log, sess, act, &src->user, + OXR_SUB_ACTION_PATH_USER); + } + + if (act->sub_paths.head || act->sub_paths.any) { + oxr_source_bind_inputs(log, sess, act, &src->head, + OXR_SUB_ACTION_PATH_HEAD); + } + + if (act->sub_paths.left || act->sub_paths.any) { + oxr_source_bind_inputs(log, sess, act, &src->left, + OXR_SUB_ACTION_PATH_LEFT); + } + + if (act->sub_paths.right || act->sub_paths.any) { + oxr_source_bind_inputs(log, sess, act, &src->right, + OXR_SUB_ACTION_PATH_RIGHT); + } + + if (act->sub_paths.gamepad || act->sub_paths.any) { + oxr_source_bind_inputs(log, sess, act, &src->gamepad, + OXR_SUB_ACTION_PATH_GAMEPAD); + } + + *out_src = src; + + return XR_SUCCESS; +} + +static void +oxr_source_cache_stop_output(struct oxr_logger* log, + struct oxr_session* sess, + struct oxr_source_cache* cache) +{ + // Set this as stopped. + cache->stop_output_time = 0; + + union xrt_output_value value = {0}; + + for (uint32_t i = 0; i < cache->num_outputs; i++) { + struct oxr_source_output* output = &cache->outputs[i]; + struct xrt_device* xdev = output->xdev; + + xdev->set_output(xdev, output->name, + sess->sys->inst->timekeeping, &value); + } +} + +static void +oxr_source_cache_update(struct oxr_logger* log, + struct oxr_session* sess, + struct oxr_action* act, + struct oxr_source_cache* cache, + int64_t time, + bool selected) +{ + struct oxr_source_state last = cache->current; + + if (!selected) { + if (cache->stop_output_time > 0) { + oxr_source_cache_stop_output(log, sess, cache); + } + + memset(&cache->current, 0, sizeof(cache->current)); + return; + } + + if (cache->num_outputs > 0 && cache->stop_output_time < time) { + oxr_source_cache_stop_output(log, sess, cache); + } + + if (cache->num_inputs > 0) { + //! @todo Deal with other types and combine sources. + int64_t timestamp = cache->inputs[0].input->timestamp; + float value = cache->inputs[0].input->value.vec1.x; + + bool changed = value != last.vec1.x; + cache->current.vec1.x = value; + + if (last.active && changed) { + cache->current.timestamp = timestamp; + cache->current.changed = true; + } else if (last.active) { + cache->current.timestamp = last.timestamp; + cache->current.changed = false; + } else { + cache->current.timestamp = timestamp; + cache->current.changed = false; + } + } +} + +static void +oxr_source_update(struct oxr_logger* log, + struct oxr_session* sess, + struct oxr_action* act, + int64_t time, + struct oxr_sub_paths sub_paths) +{ + struct oxr_source* src = NULL; + + oxr_session_get_source(sess, act->key, &src); + if (src == NULL) { + oxr_source_create(log, sess, act, &src); + } + + if (src == NULL) { + return; + } + + bool select_head = sub_paths.head || sub_paths.any; + bool select_left = sub_paths.left || sub_paths.any; + bool select_right = sub_paths.right || sub_paths.any; + bool select_gamepad = sub_paths.gamepad || sub_paths.any; + + oxr_source_cache_update(log, sess, act, &src->head, time, select_head); + oxr_source_cache_update(log, sess, act, &src->left, time, select_left); + oxr_source_cache_update(log, sess, act, &src->right, time, + select_right); + oxr_source_cache_update(log, sess, act, &src->gamepad, time, + select_gamepad); +} + +static void +oxr_source_bind_inputs(struct oxr_logger* log, + struct oxr_session* sess, + struct oxr_action* act, + struct oxr_source_cache* cache, + enum sub_action_path sub_path) +{ + struct oxr_source_input inputs[16] = {0}; + uint32_t num_inputs = 0; + struct oxr_source_output outputs[16] = {0}; + uint32_t num_outputs = 0; + + MEGA_HACK_get_binding(log, sess, act, sub_path, inputs, &num_inputs, + outputs, &num_outputs); + + cache->current.active = false; + + if (num_inputs > 0) { + cache->current.active = true; + cache->inputs = + U_TYPED_ARRAY_CALLOC(struct oxr_source_input, num_inputs); + for (uint32_t i = 0; i < num_inputs; i++) { + cache->inputs[i] = inputs[i]; + } + cache->num_inputs = num_inputs; + } + + if (num_outputs > 0) { + cache->current.active = true; + cache->outputs = + U_TYPED_ARRAY_CALLOC(struct oxr_source_output, num_outputs); + for (uint32_t i = 0; i < num_outputs; i++) { + cache->outputs[i] = outputs[i]; + } + cache->num_outputs = num_outputs; + } +} + + /* * * Session functions. * */ +static void +oxr_session_get_source_set(struct oxr_session* sess, + XrActionSet actionSet, + struct oxr_source_set** src_set, + struct oxr_action_set** act_set) +{ + void* ptr = NULL; + *act_set = (struct oxr_action_set*)actionSet; + + int ret = u_hashmap_int_find(sess->act_sets, (*act_set)->key, &ptr); + if (ret == 0) { + *src_set = (struct oxr_source_set*)ptr; + } +} + +static void +oxr_session_get_source(struct oxr_session* sess, + uint32_t act_key, + struct oxr_source** out_src) +{ + void* ptr = NULL; + + int ret = u_hashmap_int_find(sess->sources, act_key, &ptr); + if (ret == 0) { + *out_src = (struct oxr_source*)ptr; + } +} + +/*! + * Loop over all actions in a action set and destroy the sources linked to those + * actions, this is so we can regenerate the bindings. + */ +static void +oxr_session_destroy_all_sources(struct oxr_logger* log, + struct oxr_session* sess, + struct oxr_action_set* act_set) +{ + for (uint32_t k = 0; k < ARRAY_SIZE(act_set->handle.children); k++) { + // This assumes that all children are actions. + struct oxr_action* act = + (struct oxr_action*)act_set->handle.children[k]; + + if (act == NULL) { + continue; + } + + struct oxr_source* src = NULL; + oxr_session_get_source(sess, act->key, &src); + if (src == NULL) { + continue; + } + + u_hashmap_int_erase(sess->sources, act->key); + oxr_handle_destroy(log, &src->handle); + } +} + XrResult oxr_action_sync_data(struct oxr_logger* log, struct oxr_session* sess, uint32_t countActionSets, const XrActiveActionSet* actionSets) { + struct oxr_action_set* act_set = NULL; + struct oxr_source_set* src_set = NULL; + + // Synchronize outputs to this time. + int64_t now = time_state_get_now(sess->sys->inst->timekeeping); + + oxr_xdev_update(sess->sys->head, sess->sys->inst->timekeeping); + oxr_xdev_update(sess->sys->left, sess->sys->inst->timekeeping); + oxr_xdev_update(sess->sys->right, sess->sys->inst->timekeeping); + + //! @todo These semantics below are all wrong! + + // Has any of the bound action sets been updated. + for (uint32_t i = 0; i < countActionSets; i++) { + oxr_session_get_source_set(sess, actionSets[i].actionSet, + &src_set, &act_set); + + // No source set found, create it. This will set it to the + // current action set generation, that's okay since we will + // be creating the sources for the actions below. + if (src_set == NULL) { + oxr_source_set_create(log, act_set, &src_set); + continue; + } + + // Generations match, nothing to do. + if (src_set->generation == act_set->generation) { + continue; + } + + // Found possible out of date bindings, we need to redo these + // bindings. Destroy the actions currently on this action set. + oxr_session_destroy_all_sources(log, sess, act_set); + } + + // Go over all action sets and update them. + for (uint32_t i = 0; i < countActionSets; i++) { + struct oxr_sub_paths sub_paths; + struct oxr_action_set* act_set = + (struct oxr_action_set*)actionSets[i].actionSet; + + oxr_classify_sub_action_paths(log, sess->sys->inst, 1, + &actionSets[i].subactionPath, + &sub_paths); + + for (uint32_t k = 0; k < ARRAY_SIZE(act_set->handle.children); + k++) { + // This assumes that all children of a + // action set are actions. + struct oxr_action* act = + (struct oxr_action*)act_set->handle.children[k]; + + if (act == NULL) { + continue; + } + + oxr_source_update(log, sess, act, now, sub_paths); + } + } + //! @todo Implement return XR_SUCCESS; } @@ -116,6 +710,33 @@ oxr_action_set_interaction_profile_suggested_bindings( const XrInteractionProfileSuggestedBinding* suggestedBindings) { //! @todo Implement + struct oxr_instance* inst = sess->sys->inst; + const char* str; + size_t length; + +#if 0 + fprintf(stderr, "%s\n", __func__); + + oxr_path_get_string(log, inst, suggestedBindings->interactionProfile, + &str, &length); + fprintf(stderr, "\tinteractionProfile: %s\n", str); +#endif + + for (size_t i = 0; i < suggestedBindings->countSuggestedBindings; i++) { + const XrActionSuggestedBinding* s = + &suggestedBindings->suggestedBindings[i]; + struct oxr_action* act = (struct oxr_action*)s->action; +#if 0 + oxr_path_get_string(log, inst, s->binding, &str, &length); + fprintf(stderr, "\t\t%s -> %s\n", act->name, str); +#else + (void)inst; + (void)act; + (void)str; + (void)length; +#endif + } + return XR_SUCCESS; } @@ -151,18 +772,113 @@ oxr_action_get_input_source_localized_name( * */ +static void +get_state_from_state_bool(struct oxr_source_state* state, + XrActionStateBoolean* data) +{ + data->currentState = state->boolean; + data->lastChangeTime = state->timestamp; + data->isActive = XR_TRUE; + +#if 0 + bool value = state->boolean; + + if (data->isActive) { + data->currentState |= value; + data->lastChangeTime = state->timestamp; + data->isActive = XR_TRUE; + } +#endif +} + +static void +get_state_from_state_vec1(struct oxr_source_state* state, + XrActionStateVector1f* data) +{ + data->currentState = state->vec1.x; + data->lastChangeTime = state->timestamp; + data->isActive = XR_TRUE; + +#if 0 + float value = state->vec1.x; + + if (!data->isActive || (data->isActive && value > data->currentState)) { + data->currentState = value; + data->lastChangeTime = state->timestamp; + data->isActive = XR_TRUE; + } +#endif +} + +static void +get_state_from_state_vec2(struct oxr_source_state* state, + XrActionStateVector2f* data) +{ + data->currentState.x = state->vec2.x; + data->currentState.y = state->vec2.y; + data->lastChangeTime = state->timestamp; + data->isActive = XR_TRUE; + +#if 0 + float value_x = state->vec2.x; + float value_y = state->vec2.y; + float distance = value_x * value_x + value_y * value_y; + float old_distance = + data->isActive ? data->currentState.x * data->currentState.x + + data->currentState.y * data->currentState.y + : 0.f; + + if (!data->isActive || (data->isActive && distance > old_distance)) { + data->currentState.x = value_x; + data->currentState.y = value_y; + data->lastChangeTime = state->timestamp; + data->isActive = XR_TRUE; + } +#endif +} + +#define OXR_ACTION_GET_FILLER(TYPE) \ + if (sub_paths.user && src->user.current.active) { \ + get_state_from_state_##TYPE(&src->user.current, data); \ + } \ + if (sub_paths.head && src->head.current.active) { \ + get_state_from_state_##TYPE(&src->head.current, data); \ + } \ + if (sub_paths.left && src->left.current.active) { \ + get_state_from_state_##TYPE(&src->left.current, data); \ + } \ + if (sub_paths.right && src->right.current.active) { \ + get_state_from_state_##TYPE(&src->right.current, data); \ + } \ + if (sub_paths.gamepad && src->gamepad.current.active) { \ + get_state_from_state_##TYPE(&src->gamepad.current, data); \ + } + XrResult oxr_action_get_boolean(struct oxr_logger* log, struct oxr_action* act, - uint32_t countSubactionPaths, - const XrPath* subactionPaths, + struct oxr_sub_paths sub_paths, XrActionStateBoolean* data) { - //! @todo Implement - data->currentState = XR_FALSE; - data->changedSinceLastSync = XR_FALSE; - data->lastChangeTime = 0; + struct oxr_session* sess = act->act_set->sess; + struct oxr_source* src = NULL; + + oxr_session_get_source(sess, act->key, &src); + data->isActive = XR_FALSE; + memset(&data->currentState, 0, sizeof(data->currentState)); + + if (src == NULL) { + return XR_SUCCESS; + } + + //! @todo support any subpath. + if (sub_paths.any) { + return oxr_error(log, XR_ERROR_HANDLE_INVALID, + "any path not implemented!"); + } + + OXR_ACTION_GET_FILLER(bool); return XR_SUCCESS; } @@ -170,15 +886,28 @@ oxr_action_get_boolean(struct oxr_logger* log, XrResult oxr_action_get_vector1f(struct oxr_logger* log, struct oxr_action* act, - uint32_t countSubactionPaths, - const XrPath* subactionPaths, + struct oxr_sub_paths sub_paths, XrActionStateVector1f* data) { - //! @todo Implement - data->currentState = 0.0f; - data->changedSinceLastSync = XR_FALSE; - data->lastChangeTime = 0; + struct oxr_session* sess = act->act_set->sess; + struct oxr_source* src = NULL; + + oxr_session_get_source(sess, act->key, &src); + data->isActive = XR_FALSE; + memset(&data->currentState, 0, sizeof(data->currentState)); + + if (src == NULL) { + return XR_SUCCESS; + } + + //! @todo support any subpath. + if (sub_paths.any) { + return oxr_error(log, XR_ERROR_HANDLE_INVALID, + "any path not implemented!"); + } + + OXR_ACTION_GET_FILLER(vec1); return XR_SUCCESS; } @@ -186,16 +915,28 @@ oxr_action_get_vector1f(struct oxr_logger* log, XrResult oxr_action_get_vector2f(struct oxr_logger* log, struct oxr_action* act, - uint32_t countSubactionPaths, - const XrPath* subactionPaths, + struct oxr_sub_paths sub_paths, XrActionStateVector2f* data) { - //! @todo Implement - data->currentState.x = 0.0f; - data->currentState.y = 0.0f; - data->changedSinceLastSync = XR_FALSE; - data->lastChangeTime = 0; + struct oxr_session* sess = act->act_set->sess; + struct oxr_source* src = NULL; + + oxr_session_get_source(sess, act->key, &src); + data->isActive = XR_FALSE; + memset(&data->currentState, 0, sizeof(data->currentState)); + + if (src == NULL) { + return XR_SUCCESS; + } + + //! @todo support any subpath. + if (sub_paths.any) { + return oxr_error(log, XR_ERROR_HANDLE_INVALID, + "any path not implemented!"); + } + + OXR_ACTION_GET_FILLER(vec2); return XR_SUCCESS; } @@ -203,12 +944,36 @@ oxr_action_get_vector2f(struct oxr_logger* log, XrResult oxr_action_get_pose(struct oxr_logger* log, struct oxr_action* act, - XrPath subactionPath, + struct oxr_sub_paths sub_paths, XrActionStatePose* data) { - //! @todo Implement + struct oxr_session* sess = act->act_set->sess; + struct oxr_source* src = NULL; + + oxr_session_get_source(sess, act->key, &src); + data->isActive = XR_FALSE; + if (src == NULL) { + return XR_SUCCESS; + } + + if (sub_paths.user || sub_paths.any) { + data->isActive |= src->user.current.active; + } + if (sub_paths.head || sub_paths.any) { + data->isActive |= src->head.current.active; + } + if (sub_paths.left || sub_paths.any) { + data->isActive |= src->left.current.active; + } + if (sub_paths.right || sub_paths.any) { + data->isActive |= src->right.current.active; + } + if (sub_paths.gamepad || sub_paths.any) { + data->isActive |= src->gamepad.current.active; + } + return XR_SUCCESS; } @@ -230,6 +995,27 @@ oxr_action_get_bound_sources(struct oxr_logger* log, * */ +static void +set_source_output_vibration(struct oxr_session* sess, + struct oxr_source_cache* cache, + int64_t stop, + const XrHapticVibration* data) +{ + cache->stop_output_time = stop; + + union xrt_output_value value = {0}; + value.vibration.frequency = data->frequency; + value.vibration.amplitude = data->amplitude; + + for (uint32_t i = 0; i < cache->num_outputs; i++) { + struct oxr_source_output* output = &cache->outputs[i]; + struct xrt_device* xdev = output->xdev; + + xdev->set_output(xdev, output->name, + sess->sys->inst->timekeeping, &value); + } +} + XrResult oxr_action_apply_haptic_feedback(struct oxr_logger* log, struct oxr_action* act, @@ -237,8 +1023,43 @@ oxr_action_apply_haptic_feedback(struct oxr_logger* log, const XrPath* subactionPaths, const XrHapticBaseHeader* hapticEvent) { - //! @todo Implement - return oxr_error(log, XR_ERROR_HANDLE_INVALID, " not implemented"); + struct oxr_session* sess = act->act_set->sess; + struct oxr_source* src = NULL; + struct oxr_sub_paths sub_paths = {0}; + + oxr_classify_sub_action_paths(log, sess->sys->inst, countSubactionPaths, + subactionPaths, &sub_paths); + + oxr_session_get_source(sess, act->key, &src); + + if (src == NULL) { + return XR_SUCCESS; + } + + const XrHapticVibration* data = (const XrHapticVibration*)hapticEvent; + + int64_t now = time_state_get_now(sess->sys->inst->timekeeping); + int64_t stop = data->duration <= 0 ? now : now + data->duration; + + // clang-format off + if (src->user.current.active && (sub_paths.user || sub_paths.any)) { + set_source_output_vibration(sess, &src->user, stop, data); + } + if (src->head.current.active && (sub_paths.head || sub_paths.any)) { + set_source_output_vibration(sess, &src->head, stop, data); + } + if (src->left.current.active && (sub_paths.left || sub_paths.any)) { + set_source_output_vibration(sess, &src->left, stop, data); + } + if (src->right.current.active && (sub_paths.right || sub_paths.any)) { + set_source_output_vibration(sess, &src->right, stop, data); + } + if (src->gamepad.current.active && (sub_paths.gamepad || sub_paths.any)) { + set_source_output_vibration(sess, &src->gamepad, stop, data); + } + // clang-format on + + return XR_SUCCESS; } XrResult @@ -247,6 +1068,36 @@ oxr_action_stop_haptic_feedback(struct oxr_logger* log, uint32_t countSubactionPaths, const XrPath* subactionPaths) { - //! @todo Implement - return oxr_error(log, XR_ERROR_HANDLE_INVALID, " not implemented"); + struct oxr_session* sess = act->act_set->sess; + struct oxr_source* src = NULL; + struct oxr_sub_paths sub_paths = {0}; + + oxr_classify_sub_action_paths(log, sess->sys->inst, countSubactionPaths, + subactionPaths, &sub_paths); + + oxr_session_get_source(sess, act->key, &src); + + if (src == NULL) { + return XR_SUCCESS; + } + + // clang-format off + if (src->user.current.active && (sub_paths.user || sub_paths.any)) { + oxr_source_cache_stop_output(log, sess, &src->user); + } + if (src->head.current.active && (sub_paths.head || sub_paths.any)) { + oxr_source_cache_stop_output(log, sess, &src->head); + } + if (src->left.current.active && (sub_paths.left || sub_paths.any)) { + oxr_source_cache_stop_output(log, sess, &src->left); + } + if (src->right.current.active && (sub_paths.right || sub_paths.any)) { + oxr_source_cache_stop_output(log, sess, &src->right); + } + if (src->gamepad.current.active && (sub_paths.gamepad || sub_paths.any)) { + oxr_source_cache_stop_output(log, sess, &src->gamepad); + } + // clang-format on + + return XR_SUCCESS; } diff --git a/src/xrt/state_trackers/oxr/oxr_instance.c b/src/xrt/state_trackers/oxr/oxr_instance.c index 53415c16e..40d2b74c3 100644 --- a/src/xrt/state_trackers/oxr/oxr_instance.c +++ b/src/xrt/state_trackers/oxr/oxr_instance.c @@ -34,12 +34,24 @@ radtodeg_for_display(float radians) return (int32_t)(radians * 180 * M_1_PI); } +static inline void +xdev_destroy(struct xrt_device **xdev_ptr) +{ + struct xrt_device *xdev = *xdev_ptr; + + if (xdev == NULL) { + return; + } + + xdev->destroy(xdev); + *xdev_ptr = NULL; +} + static XrResult oxr_instance_destroy(struct oxr_logger *log, struct oxr_handle_base *hb) { struct oxr_instance *inst = (struct oxr_instance *)hb; - struct xrt_auto_prober *prober = inst->prober; - struct xrt_device *dev = inst->system.device; + struct xrt_prober *prober = inst->prober; oxr_path_destroy_all(log, inst); @@ -47,14 +59,12 @@ oxr_instance_destroy(struct oxr_logger *log, struct oxr_handle_base *hb) u_hashset_destroy(&inst->path_store); } - if (dev != NULL) { - dev->destroy(dev); - inst->system.device = NULL; - } + xdev_destroy(&inst->system.head); + xdev_destroy(&inst->system.left); + xdev_destroy(&inst->system.right); - if (prober != NULL) { - prober->destroy(prober); - inst->prober = NULL; + if (inst->prober != NULL) { + prober->destroy(&inst->prober); } time_state_destroy(inst->timekeeping); @@ -65,13 +75,23 @@ oxr_instance_destroy(struct oxr_logger *log, struct oxr_handle_base *hb) return XR_SUCCESS; } +static void +cache_path(struct oxr_logger *log, + struct oxr_instance *inst, + const char *str, + XrPath *out_path) +{ + oxr_path_get_or_create(log, inst, str, strlen(str), out_path); +} + XrResult oxr_instance_create(struct oxr_logger *log, const XrInstanceCreateInfo *createInfo, struct oxr_instance **out_instance) { struct oxr_instance *inst = NULL; - int h_ret; + struct xrt_device *xdevs[3] = {0}; + int h_ret, p_ret; OXR_ALLOCATE_HANDLE_OR_RETURN(log, inst, OXR_XR_DEBUG_INSTANCE, oxr_instance_destroy, NULL); @@ -83,10 +103,35 @@ oxr_instance_create(struct oxr_logger *log, "Failed to create hashset"); } - inst->prober = xrt_auto_prober_create(); + // Cache certain often looked up paths. + cache_path(log, inst, "/user", &inst->path_cache.user); + cache_path(log, inst, "/user/hand/head", &inst->path_cache.head); + cache_path(log, inst, "/user/hand/left", &inst->path_cache.left); + cache_path(log, inst, "/user/hand/right", &inst->path_cache.right); + cache_path(log, inst, "/user/hand/gamepad", &inst->path_cache.gamepad); - struct xrt_device *dev = - inst->prober->lelo_dallas_autoprobe(inst->prober); + p_ret = xrt_prober_create(&inst->prober); + if (p_ret != 0) { + inst->prober->destroy(&inst->prober); + return oxr_error(log, XR_ERROR_RUNTIME_FAILURE, + "Failed to create prober"); + } + + p_ret = inst->prober->probe(inst->prober); + if (p_ret != 0) { + inst->prober->destroy(&inst->prober); + return oxr_error(log, XR_ERROR_RUNTIME_FAILURE, + "Failed to probe device(s)"); + } + + p_ret = inst->prober->select(inst->prober, xdevs, 3); + if (p_ret != 0) { + inst->prober->destroy(&inst->prober); + return oxr_error(log, XR_ERROR_RUNTIME_FAILURE, + "Failed to select device"); + } + + struct xrt_device *dev = xdevs[0]; const float left_override = debug_get_float_option_lfov_left(); if (left_override != 0.0f) { @@ -127,7 +172,8 @@ oxr_instance_create(struct oxr_logger *log, dev->hmd->views[1].fov.angle_down = down_override; } - oxr_system_fill_in(log, inst, 1, &inst->system, dev); + oxr_system_fill_in(log, inst, 1, &inst->system, xdevs[0], xdevs[1], + xdevs[2]); inst->timekeeping = time_state_create(); diff --git a/src/xrt/state_trackers/oxr/oxr_objects.h b/src/xrt/state_trackers/oxr/oxr_objects.h index e2415d999..d4725a01c 100644 --- a/src/xrt/state_trackers/oxr/oxr_objects.h +++ b/src/xrt/state_trackers/oxr/oxr_objects.h @@ -10,10 +10,12 @@ #pragma once #include "xrt/xrt_device.h" +#include "xrt/xrt_tracking.h" #include "xrt/xrt_compositor.h" #include "xrt/xrt_vulkan_includes.h" #include "xrt/xrt_openxr_includes.h" #include "util/u_hashset.h" +#include "util/u_hashmap.h" #ifdef __cplusplus extern "C" { @@ -48,6 +50,8 @@ extern "C" { #define OXR_XR_DEBUG_SWAPCHAIN (*(uint64_t *)"oxrswap\0") #define OXR_XR_DEBUG_ACTIONSET (*(uint64_t *)"oxraset\0") #define OXR_XR_DEBUG_MESSENGER (*(uint64_t *)"oxrmess\0") +#define OXR_XR_DEBUG_SOURCESET (*(uint64_t *)"oxrsrcs\0") +#define OXR_XR_DEBUG_SOURCE (*(uint64_t *)"oxrsrc_\0") // clang-format on @@ -68,6 +72,11 @@ struct oxr_action_set; struct oxr_action; struct oxr_debug_messenger; struct oxr_handle_base; +struct oxr_sub_paths; +struct oxr_source; +struct oxr_source_set; +struct oxr_source_input; +struct oxr_source_output; #define XRT_MAX_HANDLE_CHILDREN 256 @@ -212,6 +221,26 @@ oxr_path_destroy_all(struct oxr_logger *log, struct oxr_instance *inst); * */ +/*! + * Helper function to classify sub_paths. + */ +void +oxr_classify_sub_action_paths(struct oxr_logger *log, + struct oxr_instance *inst, + uint32_t num_subaction_paths, + const XrPath *subaction_paths, + struct oxr_sub_paths *sub_paths); + +/*! + * Find the pose input for the set of sub_paths + */ +XrResult +oxr_source_get_pose_input(struct oxr_logger *log, + struct oxr_session *sess, + uint32_t key, + const struct oxr_sub_paths *sub_paths, + struct oxr_source_input **out_input); + /*! * To go back to a OpenXR object. */ @@ -274,28 +303,25 @@ oxr_action_get_input_source_localized_name( XrResult oxr_action_get_boolean(struct oxr_logger *log, struct oxr_action *act, - uint32_t countSubactionPaths, - const XrPath *subactionPaths, + struct oxr_sub_paths sub_paths, XrActionStateBoolean *data); XrResult oxr_action_get_vector1f(struct oxr_logger *log, struct oxr_action *act, - uint32_t countSubactionPaths, - const XrPath *subactionPaths, + struct oxr_sub_paths sub_paths, XrActionStateVector1f *data); XrResult oxr_action_get_vector2f(struct oxr_logger *log, struct oxr_action *act, - uint32_t countSubactionPaths, - const XrPath *subactionPaths, + struct oxr_sub_paths sub_paths, XrActionStateVector2f *data); XrResult oxr_action_get_pose(struct oxr_logger *log, struct oxr_action *act, - XrPath subactionPath, + struct oxr_sub_paths sub_paths, XrActionStatePose *data); XrResult @@ -495,7 +521,9 @@ oxr_system_fill_in(struct oxr_logger *log, struct oxr_instance *inst, XrSystemId systemId, struct oxr_system *sys, - struct xrt_device *dev); + struct xrt_device *head, + struct xrt_device *left, + struct xrt_device *right); XrResult oxr_system_verify_id(struct oxr_logger *log, @@ -563,6 +591,34 @@ oxr_event_push_XrEventDataSessionStateChanged(struct oxr_logger *log, XrTime time); +/* + * + * oxr_xdev.c + * + */ + +void +oxr_xdev_update(struct xrt_device* xdev, struct time_state* timekeeping); + +void +oxr_xdev_find_input(struct xrt_device *xdev, + enum xrt_input_name name, + struct xrt_input **out_input); + +void +oxr_xdev_find_output(struct xrt_device *xdev, + enum xrt_output_name name, + struct xrt_output **out_output); + +void +oxr_xdev_get_pose_at(struct oxr_logger *log, + struct oxr_instance *inst, + struct xrt_device *xdev, + enum xrt_input_name name, + struct xrt_pose *pose, + int64_t *timestamp); + + /* * * OpenGL, located in various files. @@ -687,7 +743,11 @@ struct oxr_handle_base struct oxr_system { struct oxr_instance *inst; - struct xrt_device *device; + + struct xrt_device *head; + struct xrt_device *left; + struct xrt_device *right; + XrSystemId systemId; XrFormFactor form_factor; @@ -707,7 +767,7 @@ struct oxr_instance //! Common structure for things referred to by OpenXR handles. struct oxr_handle_base handle; - struct xrt_auto_prober *prober; + struct xrt_prober *prober; // Enabled extensions bool headless; @@ -726,6 +786,16 @@ struct oxr_instance struct oxr_event *last_event; struct oxr_event *next_event; + + struct + { + XrPath user; + XrPath head; + XrPath left; + XrPath right; + XrPath gamepad; + } path_cache; + #ifdef XR_EXT_debug_utils //! Debug messengers struct oxr_debug_messenger *messengers[XRT_MAX_HANDLE_CHILDREN]; @@ -747,6 +817,9 @@ struct oxr_session XrSessionState state; bool frame_started; + struct u_hashmap_int *act_sets; + struct u_hashmap_int *sources; + /*! * IPD, to be expanded to a proper 3D relation. */ @@ -763,6 +836,122 @@ struct oxr_session struct oxr_swapchain **); }; +/*! + * To carry around a sementic selection of sub action paths. + */ +struct oxr_sub_paths +{ + bool any; + bool user; + bool head; + bool left; + bool right; + bool gamepad; +}; + +/*! + * Session input source. + * + * @see oxr_action_set + */ +struct oxr_source_set +{ + //! Common structure for things referred to by OpenXR handles. + struct oxr_handle_base handle; + + //! Which generation of the XrActionSet was this created from. + uint32_t generation; +}; + +/*! + * The state of a action input source. + * + * @see oxr_source + */ +struct oxr_source_state +{ + union { + struct + { + float x; + } vec1; + + struct + { + float x; + float y; + } vec2; + + bool boolean; + }; + + bool active; + + // Was this changed. + bool changed; + + //! When was this last changed. + XrTime timestamp; +}; + +/*! + * A input source pair of a @ref xrt_input and a @ref xrt_device. + * + * @see xrt_device + * @see xrt_input + */ +struct oxr_source_input +{ + struct xrt_device *xdev; + struct xrt_input *input; +}; + +/*! + * A output source pair of a @ref xrt_output_name and a @ref xrt_device. + * + * @see xrt_device + * @see xrt_output_name + */ +struct oxr_source_output +{ + struct xrt_device *xdev; + enum xrt_output_name name; +}; + +/*! + * A set of inputs for a single sub action path. + * + * @see oxr_source + */ +struct oxr_source_cache +{ + struct oxr_source_state current; + + size_t num_inputs; + struct oxr_source_input *inputs; + + int64_t stop_output_time; + size_t num_outputs; + struct oxr_source_output *outputs; +}; + +/*! + * Session input source. + * + * @see oxr_action + */ +struct oxr_source +{ + //! Common structure for things referred to by OpenXR handles. + struct oxr_handle_base handle; + + struct oxr_source_cache user; + struct oxr_source_cache head; + struct oxr_source_cache left; + struct oxr_source_cache right; + struct oxr_source_cache gamepad; +}; + /*! * Can be one of 3 references or a space that are bound to actions. * @@ -782,8 +971,14 @@ struct oxr_space //! What kind of reference space is this, if any. XrReferenceSpaceType type; + //! Action key from which action this space was created from. + uint32_t act_key; + //! Is this a reference space? bool is_reference; + + //! Which sub action path is this? + struct oxr_sub_paths sub_paths; }; /*! @@ -839,6 +1034,15 @@ struct oxr_action_set //! Onwer of this messenger. struct oxr_session *sess; + + /*! + * Every change that is done to a action set will increment this + * counter and trigger a rebinding of inputs when syncing actions. + */ + uint32_t generation; + + //! Unique key for the session hashmap. + uint32_t key; }; /*! @@ -853,6 +1057,18 @@ struct oxr_action //! Onwer of this messenger. struct oxr_action_set *act_set; + + //! Application supplied name of this action. + char name[XR_MAX_ACTION_NAME_SIZE]; + + //! Unique key for the session hashmap. + uint32_t key; + + //! Type this action was created with. + XrActionType action_type; + + //! Which sub action paths that this action was created with. + struct oxr_sub_paths sub_paths; }; /*! diff --git a/src/xrt/state_trackers/oxr/oxr_session.c b/src/xrt/state_trackers/oxr/oxr_session.c index edee5aa4f..0257500fb 100644 --- a/src/xrt/state_trackers/oxr/oxr_session.c +++ b/src/xrt/state_trackers/oxr/oxr_session.c @@ -129,6 +129,7 @@ oxr_session_end(struct oxr_logger *log, struct oxr_session *sess) return XR_SUCCESS; } + XrResult oxr_session_get_view_pose_at(struct oxr_logger *log, struct oxr_session *sess, @@ -145,33 +146,39 @@ oxr_session_get_view_pose_at(struct oxr_logger *log, // @todo If using orientation tracking only implement a neck model to // get at least a slightly better position. - struct xrt_device *xdev = sess->sys->device; + struct xrt_device *xdev = sess->sys->head; + struct xrt_pose *offset = &xdev->tracking->offset; + struct xrt_space_relation relation; int64_t timestamp; xdev->get_tracked_pose(xdev, XRT_INPUT_GENERIC_HEAD_RELATION, sess->sys->inst->timekeeping, ×tamp, &relation); - if ((relation.relation_flags & - XRT_SPACE_RELATION_ORIENTATION_VALID_BIT) != 0) { + + // Add in the offset from the tracking system. + math_relation_accumulate_transform(offset, &relation); + + // clang-format off + bool valid_pos = (relation.relation_flags & XRT_SPACE_RELATION_POSITION_VALID_BIT) != 0; + bool valid_ori = (relation.relation_flags & XRT_SPACE_RELATION_ORIENTATION_VALID_BIT) != 0; + bool valid_vel = (relation.relation_flags & XRT_SPACE_RELATION_ANGULAR_VELOCITY_VALID_BIT) != 0; + // clang-format on + + if (valid_ori) { pose->orientation = relation.pose.orientation; } else { - pose->orientation.x = 0; - pose->orientation.y = 0; - pose->orientation.z = 0; - pose->orientation.w = 1; - } - if ((relation.relation_flags & XRT_SPACE_RELATION_POSITION_VALID_BIT) != - 0) { - pose->position = relation.pose.position; - } else { - // "nominal height" 1.6m - pose->position.x = 0.0f; - pose->position.y = 1.60f; - pose->position.z = 0.0f; + // If the orientation is not valid just use the offset. + pose->orientation = offset->orientation; } - if ((relation.relation_flags & - XRT_SPACE_RELATION_ANGULAR_VELOCITY_VALID_BIT) != 0) { + if (valid_pos) { + pose->position = relation.pose.position; + } else { + // If the position is not valid just use the offset. + pose->position = offset->position; + } + + if (valid_vel) { //! @todo Forcing a fixed amount of prediction for now since //! devices don't tell us timestamps yet. int64_t ns_diff = at_time - timestamp; @@ -187,8 +194,8 @@ oxr_session_get_view_pose_at(struct oxr_logger *log, math_quat_integrate_velocity(&pose->orientation, &relation.angular_velocity, interval, &predicted); - if (debug_get_bool_option_views()) { + if (debug_get_bool_option_views()) { fprintf(stderr, "\toriginal quat = {%f, %f, %f, %f} " "(time requested: %li, Interval %li nsec, with " @@ -197,6 +204,7 @@ oxr_session_get_view_pose_at(struct oxr_logger *log, pose->orientation.z, pose->orientation.w, at_time, ns_diff, interval); } + pose->orientation = predicted; } @@ -238,7 +246,7 @@ oxr_session_views(struct oxr_logger *log, uint32_t *viewCountOutput, XrView *views) { - struct xrt_device *xdev = sess->sys->device; + struct xrt_device *xdev = sess->sys->head; struct oxr_space *baseSpc = (struct oxr_space *)viewLocateInfo->space; uint32_t num_views = 2; @@ -276,9 +284,9 @@ oxr_session_views(struct oxr_logger *log, struct xrt_pose pure = pure_relation.pose; - // @todo the fov information that we get from xdev->views[i].fov is not - // properly filled out in oh_device.c, fix before wasting time on - // debugging weird rendering when adding stuff here. + // @todo the fov information that we get from xdev->hmd->views[i].fov is + // not properly filled out in oh_device.c, fix before wasting time + // on debugging weird rendering when adding stuff here. for (uint32_t i = 0; i < num_views; i++) { //! @todo Do not hardcode IPD. @@ -427,7 +435,7 @@ oxr_session_frame_end(struct oxr_logger *log, "unknown environment blend mode"); } - if ((blend_mode & sess->sys->device->hmd->blend_mode) == 0) { + if ((blend_mode & sess->sys->head->hmd->blend_mode) == 0) { return oxr_error(log, XR_ERROR_ENVIRONMENT_BLEND_MODE_UNSUPPORTED, "(frameEndInfo->environmentBlendMode) " @@ -582,6 +590,9 @@ oxr_session_create(struct oxr_logger *log, log, sess, XR_SESSION_STATE_READY, 0); sess->state = XR_SESSION_STATE_READY; + u_hashmap_int_create(&sess->act_sets); + u_hashmap_int_create(&sess->sources); + *out_session = sess; return ret; diff --git a/src/xrt/state_trackers/oxr/oxr_session_gl.c b/src/xrt/state_trackers/oxr/oxr_session_gl.c index 2c7cc400a..a38a31dd2 100644 --- a/src/xrt/state_trackers/oxr/oxr_session_gl.c +++ b/src/xrt/state_trackers/oxr/oxr_session_gl.c @@ -27,7 +27,7 @@ oxr_session_populate_gl_xlib(struct oxr_logger *log, struct oxr_session *sess) { struct xrt_compositor_gl *xcgl = xrt_gfx_provider_create_gl_xlib( - sys->device, sys->inst->timekeeping, next->xDisplay, next->visualid, + sys->head, sys->inst->timekeeping, next->xDisplay, next->visualid, next->glxFBConfig, next->glxDrawable, next->glxContext); if (xcgl == NULL) { diff --git a/src/xrt/state_trackers/oxr/oxr_session_vk.c b/src/xrt/state_trackers/oxr/oxr_session_vk.c index 92851e3d7..e405ee8dd 100644 --- a/src/xrt/state_trackers/oxr/oxr_session_vk.c +++ b/src/xrt/state_trackers/oxr/oxr_session_vk.c @@ -30,7 +30,7 @@ oxr_session_populate_vk(struct oxr_logger *log, struct oxr_session *sess) { struct xrt_compositor_vk *xcvk = xrt_gfx_vk_provider_create( - sys->device, sys->inst->timekeeping, next->instance, + sys->head, sys->inst->timekeeping, next->instance, vkGetInstanceProcAddr, next->physicalDevice, next->device, next->queueFamilyIndex, next->queueIndex); diff --git a/src/xrt/state_trackers/oxr/oxr_space.c b/src/xrt/state_trackers/oxr/oxr_space.c index cced2ef3f..ba4774d96 100644 --- a/src/xrt/state_trackers/oxr/oxr_space.c +++ b/src/xrt/state_trackers/oxr/oxr_space.c @@ -58,6 +58,10 @@ oxr_space_action_create(struct oxr_logger *log, const XrActionSpaceCreateInfo *createInfo, struct oxr_space **out_space) { + struct oxr_session *sess = act->act_set->sess; + struct oxr_instance *inst = sess->sys->inst; + struct oxr_sub_paths sub_paths = {0}; + struct oxr_space *spc = NULL; OXR_ALLOCATE_HANDLE_OR_RETURN(log, spc, OXR_XR_DEBUG_SPACE, oxr_space_destroy, &act->handle); @@ -65,8 +69,12 @@ oxr_space_action_create(struct oxr_logger *log, //! @todo implement more fully oxr_warn(log, " not fully implemented"); + oxr_classify_sub_action_paths(log, inst, 1, &createInfo->subactionPath, + &sub_paths); + spc->sess = act->act_set->sess; spc->is_reference = false; + spc->sub_paths = sub_paths; memcpy(&spc->pose, &createInfo->poseInActionSpace, sizeof(spc->pose)); *out_space = spc; @@ -112,7 +120,7 @@ static const char * get_ref_space_type_short_str(struct oxr_space *spc) { if (!spc->is_reference) { - return "action?"; + return "action"; } switch (spc->type) { @@ -181,6 +189,82 @@ oxr_space_ref_relation(struct oxr_logger *log, return XR_SUCCESS; } +/*! + * This returns only the relation between two spaces without any of the app + * given relations applied, assumes that only one is a action space. + */ +XrResult +oxr_space_action_relation(struct oxr_logger *log, + struct oxr_session *sess, + struct oxr_space *spc, + struct oxr_space *baseSpc, + XrTime time, + struct xrt_space_relation *out_relation) +{ + struct oxr_source_input *input = NULL; + struct oxr_space *act_spc, *ref_spc = NULL; + int64_t timestamp = 0; + bool invert = false; + + + + // Find the action space + if (baseSpc->is_reference) { + // Note spc, is assumed to be the action space. + act_spc = spc; + ref_spc = baseSpc; + } + + // Find the action space. + if (spc->is_reference) { + // Note baseSpc, is assumed to be the action space. + act_spc = baseSpc; + ref_spc = spc; + invert = true; + } + + // Internal error check. + if (act_spc == NULL || act_spc->is_reference || ref_spc == NULL || + !ref_spc->is_reference) { + return oxr_error(log, XR_ERROR_RUNTIME_FAILURE, "this is bad!"); + } + + // Reset so no relation is returned. + math_relation_reset(out_relation); + + // We treat state and local space as the same. + //! @todo Can not relate to the view space right now. + if (baseSpc->type == XR_REFERENCE_SPACE_TYPE_VIEW) { + //! @todo Error code? + return XR_SUCCESS; + } + + oxr_source_get_pose_input(log, sess, act_spc->act_key, + &act_spc->sub_paths, &input); + + // If the input isn't active. + if (input == NULL) { + out_relation->relation_flags = XRT_SPACE_RELATION_BITMASK_NONE; + return XR_SUCCESS; + } + + oxr_xdev_get_pose_at(log, sess->sys->inst, input->xdev, + input->input->name, &out_relation->pose, + ×tamp); + + out_relation->relation_flags = (enum xrt_space_relation_flags)( + XRT_SPACE_RELATION_POSITION_VALID_BIT | + XRT_SPACE_RELATION_POSITION_TRACKED_BIT | + XRT_SPACE_RELATION_ORIENTATION_VALID_BIT | + XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT); + + if (invert) { + math_pose_invert(&out_relation->pose, &out_relation->pose); + } + + return XR_SUCCESS; +} + /*! * This returns only the relation between two directly-associated spaces without * any of the app given relations applied. @@ -211,8 +295,8 @@ get_pure_space_relation(struct oxr_logger *log, out_relation->relation_flags = XRT_SPACE_RELATION_BITMASK_NONE; return XR_SUCCESS; } else { - // @todo deal with action space poses. - out_relation->relation_flags = XRT_SPACE_RELATION_BITMASK_NONE; + oxr_space_action_relation(log, sess, spc, baseSpc, time, + out_relation); return XR_SUCCESS; } } diff --git a/src/xrt/state_trackers/oxr/oxr_system.c b/src/xrt/state_trackers/oxr/oxr_system.c index b37194d60..1e15c6209 100644 --- a/src/xrt/state_trackers/oxr/oxr_system.c +++ b/src/xrt/state_trackers/oxr/oxr_system.c @@ -93,15 +93,38 @@ oxr_system_fill_in(struct oxr_logger *log, struct oxr_instance *inst, XrSystemId systemId, struct oxr_system *sys, - struct xrt_device *xdev) + struct xrt_device *head, + struct xrt_device *left, + struct xrt_device *right) { - if (xdev == NULL) { + if (head == NULL) { return oxr_error(log, XR_ERROR_INITIALIZATION_FAILED, " failed to probe device"); } + if (head->tracking->type == XRT_TRACKING_TYPE_NONE) { + // "nominal height" 1.6m + head->tracking->offset.position.x = 0.0f; + head->tracking->offset.position.y = 1.6f; + head->tracking->offset.position.z = 0.0f; + } + + if (left != NULL && left->tracking->type == XRT_TRACKING_TYPE_NONE) { + left->tracking->offset.position.x = -0.2f; + left->tracking->offset.position.y = 1.3f; + left->tracking->offset.position.z = -0.5f; + } + + if (right != NULL && right->tracking->type == XRT_TRACKING_TYPE_NONE) { + right->tracking->offset.position.x = 0.2f; + right->tracking->offset.position.y = 1.3f; + right->tracking->offset.position.z = -0.5f; + } + // clang-format off - sys->device = xdev; + sys->head = head; + sys->left = left; + sys->right = right; sys->inst = inst; sys->systemId = systemId; sys->form_factor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; @@ -109,10 +132,11 @@ oxr_system_fill_in(struct oxr_logger *log, double scale = debug_get_num_option_scale_percentage() / 100.0; - uint32_t w0 = (uint32_t)(xdev->hmd->views[0].display.w_pixels * scale); - uint32_t h0 = (uint32_t)(xdev->hmd->views[0].display.w_pixels * scale); - uint32_t w1 = (uint32_t)(xdev->hmd->views[1].display.w_pixels * scale); - uint32_t h1 = (uint32_t)(xdev->hmd->views[1].display.w_pixels * scale); + + uint32_t w0 = (uint32_t)(head->hmd->views[0].display.w_pixels * scale); + uint32_t h0 = (uint32_t)(head->hmd->views[0].display.w_pixels * scale); + uint32_t w1 = (uint32_t)(head->hmd->views[1].display.w_pixels * scale); + uint32_t h1 = (uint32_t)(head->hmd->views[1].display.w_pixels * scale); sys->views[0].recommendedImageRectWidth = w0; sys->views[0].maxImageRectWidth = w0; @@ -130,13 +154,13 @@ oxr_system_fill_in(struct oxr_logger *log, // clang-format on uint32_t i = 0; - if (xdev->hmd->blend_mode & XRT_BLEND_MODE_OPAQUE) { + if (head->hmd->blend_mode & XRT_BLEND_MODE_OPAQUE) { sys->blend_modes[i++] = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; } - if (xdev->hmd->blend_mode & XRT_BLEND_MODE_ADDITIVE) { + if (head->hmd->blend_mode & XRT_BLEND_MODE_ADDITIVE) { sys->blend_modes[i++] = XR_ENVIRONMENT_BLEND_MODE_ADDITIVE; } - if (xdev->hmd->blend_mode & XRT_BLEND_MODE_ALPHA_BLEND) { + if (head->hmd->blend_mode & XRT_BLEND_MODE_ALPHA_BLEND) { sys->blend_modes[i++] = XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND; } sys->num_blend_modes = i; @@ -157,7 +181,7 @@ oxr_system_get_properties(struct oxr_logger *log, properties->graphicsProperties.maxViewCount = 2; // Needed to silence the warnings. - const char *name = sys->device->name; + const char *name = sys->head->name; snprintf(properties->systemName, XR_MAX_SYSTEM_NAME_SIZE, "Monado: %s", name); diff --git a/src/xrt/state_trackers/oxr/oxr_verify.cpp b/src/xrt/state_trackers/oxr/oxr_verify.cpp index 8f1751bae..41ad12465 100644 --- a/src/xrt/state_trackers/oxr/oxr_verify.cpp +++ b/src/xrt/state_trackers/oxr/oxr_verify.cpp @@ -264,6 +264,185 @@ oxr_verify_full_path(struct oxr_logger* log, } +/* + * + * Subaction path functions. + * + */ + +static XrResult +subaction_path_no_dups(struct oxr_logger* log, + struct oxr_instance* inst, + struct oxr_sub_paths& sub_paths, + XrPath path, + const char* variable, + uint32_t index) +{ + bool duplicate = false; + + if (path == XR_NULL_PATH) { + return oxr_error(log, XR_ERROR_PATH_INVALID, + "(%s[%u] == XR_NULL_PATH) not a " + "valid subaction path.", + variable, index); + } else if (path == inst->path_cache.user) { + if (sub_paths.user) { + duplicate = true; + } else { + sub_paths.user = true; + } + } else if (path == inst->path_cache.head) { + if (sub_paths.head) { + duplicate = true; + } else { + sub_paths.head = true; + } + } else if (path == inst->path_cache.left) { + if (sub_paths.left) { + duplicate = true; + } else { + sub_paths.left = true; + } + } else if (path == inst->path_cache.right) { + if (sub_paths.right) { + duplicate = true; + } else { + sub_paths.right = true; + } + } else if (path == inst->path_cache.gamepad) { + if (sub_paths.gamepad) { + duplicate = true; + } else { + sub_paths.gamepad = true; + } + } else { + const char* str = NULL; + size_t length = 0; + + oxr_path_get_string(log, inst, path, &str, &length); + return oxr_error(log, XR_ERROR_PATH_INVALID, + "(%s[%u] == '%s') path is not a " + "valid subaction path.", + variable, index, str); + } + + if (duplicate) { + const char* str = NULL; + size_t length = 0; + + oxr_path_get_string(log, inst, path, &str, &length); + + return oxr_error(log, XR_ERROR_PATH_INVALID, + "(%s[%u] == '%s') duplicate paths", variable, + index, str); + } + + return XR_SUCCESS; +} + + +extern "C" XrResult +oxr_verify_subaction_paths_create(struct oxr_logger* log, + struct oxr_instance* inst, + uint32_t countSubactionPaths, + const XrPath* subactionPaths, + const char* variable) +{ + struct oxr_sub_paths sub_paths = {}; + + for (uint32_t i = 0; i < countSubactionPaths; i++) { + XrPath path = subactionPaths[i]; + + XrResult ret = subaction_path_no_dups(log, inst, sub_paths, + path, variable, i); + if (ret != XR_SUCCESS) { + return ret; + } + } + + return XR_SUCCESS; +} + +extern "C" XrResult +oxr_verify_subaction_path_sync(struct oxr_logger* log, + struct oxr_instance* inst, + XrPath path, + uint32_t index) +{ + if (path == XR_NULL_PATH || path == inst->path_cache.user || + path == inst->path_cache.head || path == inst->path_cache.left || + path == inst->path_cache.right || + path == inst->path_cache.gamepad) { + return XR_SUCCESS; + } else { + const char* str = NULL; + size_t length = 0; + + oxr_path_get_string(log, inst, path, &str, &length); + return oxr_error(log, XR_ERROR_PATH_INVALID, + "(actionSets[%i].subactionPath == '%s') path " + "is not a valid subaction path.", + index, str); + } + + return XR_SUCCESS; +} + +extern "C" XrResult +oxr_verify_subaction_path_get(struct oxr_logger* log, + struct oxr_instance* inst, + XrPath path, + const struct oxr_sub_paths* act_sub_paths, + struct oxr_sub_paths* out_sub_paths, + const char* variable) +{ + struct oxr_sub_paths sub_paths = {}; + + if (path == XR_NULL_PATH) { + sub_paths.any = true; + } else if (path == inst->path_cache.user) { + sub_paths.user = true; + } else if (path == inst->path_cache.head) { + sub_paths.head = true; + } else if (path == inst->path_cache.left) { + sub_paths.left = true; + } else if (path == inst->path_cache.right) { + sub_paths.right = true; + } else if (path == inst->path_cache.gamepad) { + sub_paths.gamepad = true; + } else { + const char* str = NULL; + size_t length = 0; + + oxr_path_get_string(log, inst, path, &str, &length); + return oxr_error(log, XR_ERROR_PATH_INVALID, + "(%s == '%s') path is not " + "a valid subaction path.", + variable, str); + } + + if ((sub_paths.user && !act_sub_paths->user) || + (sub_paths.head && !act_sub_paths->head) || + (sub_paths.left && !act_sub_paths->left) || + (sub_paths.right && !act_sub_paths->right) || + (sub_paths.gamepad && !act_sub_paths->gamepad)) { + const char* str = NULL; + size_t length = 0; + + oxr_path_get_string(log, inst, path, &str, &length); + + return oxr_error(log, XR_ERROR_PATH_INVALID, + "(%s == '%s') the subaction path was " + "not specified at action creation", + variable, str); + } + + *out_sub_paths = sub_paths; + + return XR_SUCCESS; +} + + /* * * Other verification. diff --git a/src/xrt/state_trackers/oxr/oxr_xdev.c b/src/xrt/state_trackers/oxr/oxr_xdev.c new file mode 100644 index 000000000..d1c3c530f --- /dev/null +++ b/src/xrt/state_trackers/oxr/oxr_xdev.c @@ -0,0 +1,96 @@ +// Copyright 2019, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Various helpers for accessing @ref xrt_device. + * @author Jakob Bornecrantz + * @ingroup oxr_main + */ + +#include "math/m_api.h" +#include "oxr_objects.h" + + +void +oxr_xdev_update(struct xrt_device* xdev, struct time_state* timekeeping) +{ + if (xdev != NULL) { + xdev->update_inputs(xdev, timekeeping); + } +} + +void +oxr_xdev_find_input(struct xrt_device *xdev, + enum xrt_input_name name, + struct xrt_input **out_input) +{ + *out_input = NULL; + if (xdev == NULL) { + return; + } + + for (uint32_t i = 0; i < xdev->num_inputs; i++) { + if (xdev->inputs[i].name != name) { + continue; + } + + *out_input = &xdev->inputs[i]; + return; + } +} + +void +oxr_xdev_find_output(struct xrt_device *xdev, + enum xrt_output_name name, + struct xrt_output **out_output) +{ + if (xdev == NULL) { + return; + } + + for (uint32_t i = 0; i < xdev->num_outputs; i++) { + if (xdev->outputs[i].name != name) { + continue; + } + + *out_output = &xdev->outputs[i]; + return; + } +} + +void +oxr_xdev_get_pose_at(struct oxr_logger *log, + struct oxr_instance *inst, + struct xrt_device *xdev, + enum xrt_input_name name, + struct xrt_pose *pose, + int64_t *timestamp) +{ + struct xrt_pose *offset = &xdev->tracking->offset; + + struct xrt_space_relation relation = {0}; + xdev->get_tracked_pose(xdev, name, inst->timekeeping, timestamp, + &relation); + + // Add in the offset from the tracking system. + math_relation_accumulate_transform(offset, &relation); + + // clang-format off + bool valid_pos = (relation.relation_flags & XRT_SPACE_RELATION_POSITION_VALID_BIT) != 0; + bool valid_ori = (relation.relation_flags & XRT_SPACE_RELATION_ORIENTATION_VALID_BIT) != 0; + // clang-format on + + if (valid_ori) { + pose->orientation = relation.pose.orientation; + } else { + // If the orientation is not valid just use the offset. + pose->orientation = offset->orientation; + } + + if (valid_pos) { + pose->position = relation.pose.position; + } else { + // If the position is not valid just use the offset. + pose->position = offset->position; + } +}