Something went wrong. Try again.
The open source OpenXR runtime
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486// Copyright 2026, NVIDIA CORPORATION.// SPDX-License-Identifier: BSL-1.0/*! * @file * @brief Base implementation of @ref xrt_hand_tracker. * @ingroup base */
#include "b_hand_tracker.h"#include "b_hand_tracker.hpp"
#include "xrt/xrt_device.h"#include "xrt/xrt_space.h"
#include "math/m_space.h"
#include "util/u_debug.h"#include "util/u_logging.h"#include "util/u_misc.h"#include "util/u_pretty_print.h"
#include <cassert>#include <memory>
DEBUG_GET_ONCE_LOG_OPTION(hand_tracker_log, "B_HAND_TRACKER_LOG", U_LOGGING_INFO)
#define BHT_TRACE(...) U_LOG_IFL_T(debug_get_log_option_hand_tracker_log(), __VA_ARGS__)#define BHT_DEBUG(...) U_LOG_IFL_D(debug_get_log_option_hand_tracker_log(), __VA_ARGS__)#define BHT_INFO(...) U_LOG_IFL_I(debug_get_log_option_hand_tracker_log(), __VA_ARGS__)#define BHT_WARN(...) U_LOG_IFL_W(debug_get_log_option_hand_tracker_log(), __VA_ARGS__)#define BHT_ERROR(...) U_LOG_IFL_E(debug_get_log_option_hand_tracker_log(), __VA_ARGS__)
namespace {
static xrt_input_nameget_unobstructed_input_name(xrt_hand hand){ return hand == XRT_HAND_LEFT ? XRT_INPUT_HT_UNOBSTRUCTED_LEFT : XRT_INPUT_HT_UNOBSTRUCTED_RIGHT;}
static xrt_input_nameget_conforming_input_name(xrt_hand hand){ return hand == XRT_HAND_LEFT ? XRT_INPUT_HT_CONFORMING_LEFT : XRT_INPUT_HT_CONFORMING_RIGHT;}
static boolis_hand_tracker_input_name(xrt_input_name input_name, xrt_hand hand){ return input_name == get_unobstructed_input_name(hand) || input_name == get_conforming_input_name(hand);}
static const char *mode_to_string(HandTrackerMode mode){ switch (mode) { case HandTrackerMode::System: return "system"; case HandTrackerMode::DeviceLocked: return "device_locked"; case HandTrackerMode::Callback: return "callback"; default: return "unknown"; }}
static const char *hand_to_string(xrt_hand hand){ switch (hand) { case XRT_HAND_LEFT: return "left"; case XRT_HAND_RIGHT: return "right"; default: return "unknown"; }}
static const char *xdev_name(xrt_device *xdev){ return xdev != nullptr ? xdev->str : "<none>";}
static xrt_device *get_role_xdev(xrt_system_devices *xsysd, xrt_input_name input_name){ switch (input_name) { case XRT_INPUT_HT_UNOBSTRUCTED_LEFT: return xsysd->static_roles.hand_tracking.unobstructed.left; case XRT_INPUT_HT_UNOBSTRUCTED_RIGHT: return xsysd->static_roles.hand_tracking.unobstructed.right; case XRT_INPUT_HT_CONFORMING_LEFT: return xsysd->static_roles.hand_tracking.conforming.left; case XRT_INPUT_HT_CONFORMING_RIGHT: return xsysd->static_roles.hand_tracking.conforming.right; default: return nullptr; }}
static boolxdev_has_input(struct xrt_device *xdev, enum xrt_input_name input_name){ if (xdev == nullptr || !xdev->supported.hand_tracking) { return false; }
for (uint32_t i = 0; i < xdev->input_count; i++) { if (xdev->inputs[i].name == input_name) { return true; } }
return false;}
static voidresolve_joint_relation(const xrt_space_relation &joint_relation, const xrt_space_relation &base_hand_relation, xrt_space_relation &out_relation){ xrt_relation_chain chain = {}; m_relation_chain_push_relation(&chain, &joint_relation); m_relation_chain_push_relation(&chain, &base_hand_relation); m_relation_chain_resolve(&chain, &out_relation);}
static xrt_result_tcreate_hand_tracker(xrt_system_devices *xsysd, const xrt_hand_tracker_create_info *info, xrt_hand_tracker **out_xht, b_hand_tracker_get_devices_func_t getDevices, void *data){ assert(info != nullptr); assert(out_xht != nullptr);
*out_xht = nullptr;
auto hand_tracker = std::make_unique<HandTracker>(xsysd, *info, getDevices, data); xrt_result_t xret = hand_tracker->init(); if (xret != XRT_SUCCESS) { return xret; }
*out_xht = hand_tracker.release()->getXHT();
return XRT_SUCCESS;}
} // namespace
HandTracker::HandTracker(xrt_system_devices *xsysd, const xrt_hand_tracker_create_info &info, b_hand_tracker_get_devices_func_t getDevices, void *callbackData) : mSystemDevices(xsysd), mInfo(info), mGetDevices(getDevices), mCallbackData(callbackData){ if (info.locked_xdev != nullptr) { mMode = HandTrackerMode::DeviceLocked; } else if (getDevices != nullptr) { mMode = HandTrackerMode::Callback; } else { mMode = HandTrackerMode::System; }}
xrt_result_tHandTracker::init(){ assert(mSystemDevices != nullptr); assert(mInfo.hand == XRT_HAND_LEFT || mInfo.hand == XRT_HAND_RIGHT); assert(mInfo.requested_source_count <= ARRAY_SIZE(mInfo.requested_sources));
if (mInfo.requested_source_count == 0) { mInfo.requested_sources[mInfo.requested_source_count++] = get_unobstructed_input_name(mInfo.hand); mInfo.requested_sources[mInfo.requested_source_count++] = get_conforming_input_name(mInfo.hand); }
for (uint32_t i = 0; i < mInfo.requested_source_count; i++) { xrt_input_name input_name = mInfo.requested_sources[i]; if (!is_hand_tracker_input_name(input_name, mInfo.hand)) { return XRT_ERROR_INVALID_ARGUMENT; }
xrt_result_t xret = XRT_SUCCESS; if (mMode == HandTrackerMode::Callback) { xret = addCallbackInputs(input_name); } else { Source source{}; xret = resolveSource(input_name, source); if (xret == XRT_SUCCESS) { xret = addInput(source); addOutput(source.xdev); } }
if (xret != XRT_SUCCESS) { return xret; } }
if (mInputCount == 0) { return XRT_ERROR_FEATURE_NOT_SUPPORTED; }
u_pp_sink_stack_only sink; u_pp_delegate_t dg = u_pp_sink_stack_only_init(&sink);
u_pp(dg, "Created hand tracker:" "\n\tmode: %s" "\n\thand: %s" "\n\tlocked xdev: %s" "\n\tinput count: %u" "\n\toutput count: %u", mode_to_string(mMode), // hand_to_string(mInfo.hand), // xdev_name(mInfo.locked_xdev), // mInputCount, // mOutputCount); //
for (uint32_t i = 0; i < mInputCount; i++) { const Source &source = mInputs[i]; u_pp(dg, "\n\tinput[%u]: ", i); u_pp_xrt_input_name(dg, source.input_name); u_pp(dg, " xdev='%s'", xdev_name(source.xdev)); }
BHT_INFO("%s", sink.buffer);
return XRT_SUCCESS;}
xrt_result_tHandTracker::locate(xrt_space_overseer *xso, xrt_space *base_space, const xrt_pose *base_offset, int64_t at_timestamp_ns, xrt_hand_tracker_location *out_location){ assert(xso != nullptr); assert(base_space != nullptr); assert(out_location != nullptr);
*out_location = {};
xrt_hand_joint_set value = {}; const Source *active_source = nullptr; for (uint32_t i = 0; i < mInputCount; i++) { const Source &source = mInputs[i]; if (source.xdev == nullptr) { continue; }
int64_t ignored = 0; value = {}; xrt_result_t xret = xrt_device_get_hand_tracking( // source.xdev, // source.input_name, // at_timestamp_ns, // &value, // &ignored); // if (xret != XRT_SUCCESS) { return xret; }
if (value.is_active) { active_source = &source; break; } }
if (active_source == nullptr) { return XRT_SUCCESS; }
const struct xrt_pose identity = XRT_POSE_IDENTITY; const struct xrt_pose *offset = base_offset != nullptr ? base_offset : &identity;
struct xrt_space_relation T_base_xdev = XRT_SPACE_RELATION_ZERO; xrt_result_t xret = xrt_space_overseer_locate_device( // xso, // base_space, // offset, // at_timestamp_ns, // active_source->xdev, // &T_base_xdev); // if (xret != XRT_SUCCESS) { return xret; }
if (T_base_xdev.relation_flags == 0) { return XRT_SUCCESS; }
struct xrt_space_relation T_base_hand = XRT_SPACE_RELATION_ZERO; { struct xrt_relation_chain chain = {}; m_relation_chain_push_relation(&chain, &value.hand_pose); m_relation_chain_push_relation(&chain, &T_base_xdev); m_relation_chain_resolve(&chain, &T_base_hand); }
if (T_base_hand.relation_flags == 0) { return XRT_SUCCESS; }
out_location->hand_joint_set = value; out_location->hand_joint_set.hand_pose = T_base_hand; out_location->hand_joint_set.is_active = true; out_location->source = active_source->input_name; out_location->is_active = true;
for (uint32_t i = 0; i < XRT_HAND_JOINT_COUNT; i++) { struct xrt_space_relation joint_relation = value.values.hand_joint_set_default[i].relation; struct xrt_space_relation result = XRT_SPACE_RELATION_ZERO; resolve_joint_relation(joint_relation, T_base_hand, result); out_location->hand_joint_set.values.hand_joint_set_default[i].relation = result; }
return XRT_SUCCESS;}
xrt_result_tHandTracker::setOutput(xrt_output_name name, const xrt_output_value *value){ assert(value != nullptr);
for (xrt_device *xdev : mOutputs) { if (xdev == nullptr) { continue; }
xrt_result_t xret = xrt_device_set_output(xdev, name, value); if (xret != XRT_SUCCESS) { return xret; } }
return XRT_SUCCESS;}
voidHandTracker::destroyHandTracker(struct xrt_hand_tracker *xht){ delete HandTracker::fromXHT(xht);}
/* * * Private functions. * */
xrt_result_tHandTracker::resolveSource(xrt_input_name input_name, Source &out_source){ assert(is_hand_tracker_input_name(input_name, mInfo.hand));
xrt_device *xdev{nullptr}; switch (mMode) { case HandTrackerMode::DeviceLocked: xdev = mInfo.locked_xdev; break; case HandTrackerMode::System: xdev = get_role_xdev(mSystemDevices, input_name); break; case HandTrackerMode::Callback: assert(false); return XRT_ERROR_INVALID_ARGUMENT; }
// Does null check for xdev. if (!xdev_has_input(xdev, input_name)) { out_source = {}; return XRT_SUCCESS; }
out_source = { .xdev = xdev, .input_name = input_name, };
return XRT_SUCCESS;}
xrt_result_tHandTracker::addCallbackInputs(xrt_input_name input_name){ assert(is_hand_tracker_input_name(input_name, mInfo.hand)); assert(mGetDevices != nullptr);
std::array<xrt_device *, XRT_SYSTEM_MAX_DEVICES> xdevs{}; uint32_t xdev_count = 0; xrt_result_t xret = mGetDevices( // input_name, // mSystemDevices, // mCallbackData, // (uint32_t)xdevs.size(), // xdevs.data(), // &xdev_count); // if (xret != XRT_SUCCESS) { return xret; }
if (xdev_count > xdevs.size()) { BHT_ERROR("xdev_count > xdevs.size() should not happen!"); assert(false); return XRT_ERROR_INVALID_ARGUMENT; }
for (uint32_t i = 0; i < xdev_count; i++) { xrt_device *xdev = xdevs[i]; if (!xdev_has_input(xdev, input_name)) { continue; }
xret = addInput({ .xdev = xdev, .input_name = input_name, }); if (xret != XRT_SUCCESS) { return xret; } }
return XRT_SUCCESS;}
xrt_result_tHandTracker::addInput(const Source &source){ if (source.xdev == nullptr) { return XRT_SUCCESS; }
// Remove duplicates for (uint32_t i = 0; i < mInputCount; i++) { if (mInputs[i].xdev == source.xdev && mInputs[i].input_name == source.input_name) { return XRT_SUCCESS; } }
if (mInputCount >= mInputs.size()) { BHT_WARN("Dropping xdev '%s' from hand tracking input array", source.xdev->str); return XRT_ERROR_INVALID_ARGUMENT; }
mInputs[mInputCount++] = source;
return XRT_SUCCESS;}
voidHandTracker::addOutput(xrt_device *xdev){ if (xdev == nullptr) { return; }
// De-duplicate for (uint32_t i = 0; i < mOutputCount; i++) { if (mOutputs[i] == xdev) { return; } }
if (mOutputCount >= mOutputs.size()) { BHT_WARN("Dropping xdev '%s' from hand tracking output array", xdev->str); return; }
mOutputs[mOutputCount++] = xdev;}
/* * * 'Exported' functions. * */
extern "C" xrt_result_tb_hand_tracker_create(xrt_system_devices *xsysd, const xrt_hand_tracker_create_info *info, xrt_hand_tracker **out_xht){ return create_hand_tracker(xsysd, info, out_xht, nullptr, nullptr);}
extern "C" xrt_result_tb_hand_tracker_create_with_cb(xrt_system_devices *xsysd, const xrt_hand_tracker_create_info *info, b_hand_tracker_get_devices_func_t get_devices, void *data, xrt_hand_tracker **out_xht){ return create_hand_tracker(xsysd, info, out_xht, get_devices, data);}