bevy_oxr 0.3.0

Community crate for OpenXR in Bevy
Documentation
use bevy::prelude::*;
use openxr::FormFactor;

use crate::{
    resources::{XrInstance, XrSession},
    xr_init::{XrCleanup, XrPreSetup, XrSetup},
};

use self::{
    common::{spawn_hand_entities, HandBoneRadius, HandsResource},
    hand_tracking::{DisableHandTracking, HandTrackingData},
};

use super::{trackers::OpenXRTracker, Hand};

pub mod common;
pub mod emulated;
pub mod hand_tracking;

pub struct HandPlugin;

impl Plugin for HandPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(XrPreSetup, check_for_handtracking);
        app.add_systems(XrSetup, spawn_hand_entities);
        app.add_systems(XrCleanup, despawn_hand_entities);
    }
}

#[allow(clippy::type_complexity)]
fn despawn_hand_entities(
    mut commands: Commands,
    hand_entities: Query<
        Entity,
        (
            With<OpenXRTracker>,
            With<HandBone>,
            With<BoneTrackingStatus>,
        ),
    >,
) {
    for e in &hand_entities {
        commands.entity(e).despawn_recursive();
    }
    commands.remove_resource::<HandsResource>()
}

fn check_for_handtracking(
    mut commands: Commands,
    instance: Res<XrInstance>,
    session: Res<XrSession>,
) {
    let hands = instance.exts().ext_hand_tracking.is_some()
        && instance
            .supports_hand_tracking(instance.system(FormFactor::HEAD_MOUNTED_DISPLAY).unwrap())
            .is_ok_and(|v| v);
    if hands {
        info!("handtracking!");
        commands.insert_resource(HandTrackingData::new(&session).unwrap());
    } else {
        commands.insert_resource(DisableHandTracking::Both);
    }
}

#[derive(Component, Debug, Clone, Copy, PartialEq)]
pub enum BoneTrackingStatus {
    Emulated,
    Tracked,
}

#[derive(Component, Debug, Clone, Copy)]
pub enum HandBone {
    Palm,
    Wrist,
    ThumbMetacarpal,
    ThumbProximal,
    ThumbDistal,
    ThumbTip,
    IndexMetacarpal,
    IndexProximal,
    IndexIntermediate,
    IndexDistal,
    IndexTip,
    MiddleMetacarpal,
    MiddleProximal,
    MiddleIntermediate,
    MiddleDistal,
    MiddleTip,
    RingMetacarpal,
    RingProximal,
    RingIntermediate,
    RingDistal,
    RingTip,
    LittleMetacarpal,
    LittleProximal,
    LittleIntermediate,
    LittleDistal,
    LittleTip,
}
impl HandBone {
    pub fn is_finger(&self) -> bool {
        !matches!(self, HandBone::Wrist | HandBone::Palm)
    }
    pub fn is_metacarpal(&self) -> bool {
        matches!(
            self,
            HandBone::ThumbMetacarpal
                | HandBone::IndexMetacarpal
                | HandBone::MiddleMetacarpal
                | HandBone::RingMetacarpal
                | HandBone::LittleMetacarpal
        )
    }
    pub const fn get_all_bones() -> [HandBone; 26] {
        [
            HandBone::Palm,
            HandBone::Wrist,
            HandBone::ThumbMetacarpal,
            HandBone::ThumbProximal,
            HandBone::ThumbDistal,
            HandBone::ThumbTip,
            HandBone::IndexMetacarpal,
            HandBone::IndexProximal,
            HandBone::IndexIntermediate,
            HandBone::IndexDistal,
            HandBone::IndexTip,
            HandBone::MiddleMetacarpal,
            HandBone::MiddleProximal,
            HandBone::MiddleIntermediate,
            HandBone::MiddleDistal,
            HandBone::MiddleTip,
            HandBone::RingMetacarpal,
            HandBone::RingProximal,
            HandBone::RingIntermediate,
            HandBone::RingDistal,
            HandBone::RingTip,
            HandBone::LittleMetacarpal,
            HandBone::LittleProximal,
            HandBone::LittleIntermediate,
            HandBone::LittleDistal,
            HandBone::LittleTip,
        ]
    }
    pub fn get_index_from_bone(&self) -> usize {
        match &self {
            HandBone::Palm => 0,
            HandBone::Wrist => 1,
            HandBone::ThumbMetacarpal => 2,
            HandBone::ThumbProximal => 3,
            HandBone::ThumbDistal => 4,
            HandBone::ThumbTip => 5,
            HandBone::IndexMetacarpal => 6,
            HandBone::IndexProximal => 7,
            HandBone::IndexIntermediate => 8,
            HandBone::IndexDistal => 9,
            HandBone::IndexTip => 10,
            HandBone::MiddleMetacarpal => 11,
            HandBone::MiddleProximal => 12,
            HandBone::MiddleIntermediate => 13,
            HandBone::MiddleDistal => 14,
            HandBone::MiddleTip => 15,
            HandBone::RingMetacarpal => 16,
            HandBone::RingProximal => 17,
            HandBone::RingIntermediate => 18,
            HandBone::RingDistal => 19,
            HandBone::RingTip => 20,
            HandBone::LittleMetacarpal => 21,
            HandBone::LittleProximal => 22,
            HandBone::LittleIntermediate => 23,
            HandBone::LittleDistal => 24,
            HandBone::LittleTip => 25,
        }
    }
}