use crate::position::{CalibrationData, PlatformData, PlatformType};
use crate::types::Position3D;
use crate::{Error, Result};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::Duration;
use tokio::time::Instant;
pub mod arcore;
pub mod arkit;
pub mod generic;
pub mod oculus;
pub mod steamvr;
pub mod wmr;
pub use arcore::ARCorePlatform;
pub use arkit::ARKitPlatform;
pub use generic::GenericPlatform;
pub use oculus::OculusPlatform;
#[cfg(feature = "steamvr")]
pub use steamvr::SteamVRPlatform;
pub use wmr::WMRPlatform;
#[async_trait]
pub trait PlatformIntegration: Send + Sync {
async fn initialize(&mut self) -> Result<()>;
async fn get_tracking_data(&self) -> Result<PlatformTrackingData>;
async fn is_available(&self) -> bool;
fn get_capabilities(&self) -> PlatformCapabilities;
async fn configure_tracking(&mut self, config: TrackingConfig) -> Result<()>;
fn get_device_info(&self) -> DeviceInfo;
async fn start_tracking(&mut self) -> Result<()>;
async fn stop_tracking(&mut self) -> Result<()>;
async fn get_hand_tracking(&self) -> Result<Option<HandTrackingData>>;
async fn get_eye_tracking(&self) -> Result<Option<EyeTrackingData>>;
}
#[derive(Debug, Clone)]
pub struct PlatformTrackingData {
pub head_pose: PoseData,
pub left_controller: Option<PoseData>,
pub right_controller: Option<PoseData>,
pub quality: TrackingQuality,
pub timestamp: Instant,
pub raw_data: PlatformData,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PoseData {
pub position: Position3D,
pub orientation: (f32, f32, f32, f32),
pub linear_velocity: Position3D,
pub angular_velocity: Position3D,
pub confidence: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrackingQuality {
pub overall_quality: f32,
pub position_quality: f32,
pub orientation_quality: f32,
pub feature_count: u32,
pub state: TrackingState,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum TrackingState {
NotTracking,
Limited,
Full,
Lost,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlatformCapabilities {
pub head_tracking_6dof: bool,
pub hand_tracking: bool,
pub eye_tracking: bool,
pub controller_tracking: bool,
pub room_scale: bool,
pub passthrough: bool,
pub refresh_rates: Vec<f32>,
pub tracking_range: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrackingConfig {
pub enable_prediction: bool,
pub prediction_time_ms: f32,
pub position_smoothing: f32,
pub orientation_smoothing: f32,
pub enable_hand_tracking: bool,
pub enable_eye_tracking: bool,
pub target_refresh_rate: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeviceInfo {
pub name: String,
pub manufacturer: String,
pub model: String,
pub serial_number: String,
pub firmware_version: String,
pub platform_version: String,
}
#[derive(Debug, Clone)]
pub struct HandTrackingData {
pub left_hand: Option<HandData>,
pub right_hand: Option<HandData>,
pub timestamp: Instant,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HandData {
pub joints: Vec<Position3D>,
pub orientations: Vec<(f32, f32, f32, f32)>,
pub confidences: Vec<f32>,
pub gesture: HandGesture,
pub is_tracked: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum HandGesture {
Unknown,
Open,
Fist,
Point,
Pinch,
ThumbUp,
Peace,
Ok,
}
#[derive(Debug, Clone)]
pub struct EyeTrackingData {
pub left_eye: EyeData,
pub right_eye: EyeData,
pub combined_gaze: Position3D,
pub pupil_dilation: f32,
pub confidence: f32,
pub timestamp: Instant,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EyeData {
pub position: Position3D,
pub gaze_direction: Position3D,
pub openness: f32,
pub pupil_position: (f32, f32),
pub confidence: f32,
}
impl Default for TrackingConfig {
fn default() -> Self {
Self {
enable_prediction: true,
prediction_time_ms: 15.0, position_smoothing: 0.1,
orientation_smoothing: 0.05,
enable_hand_tracking: false,
enable_eye_tracking: false,
target_refresh_rate: 90.0,
}
}
}
impl PoseData {
pub fn new(position: Position3D, orientation: (f32, f32, f32, f32)) -> Self {
Self {
position,
orientation,
linear_velocity: Position3D::new(0.0, 0.0, 0.0),
angular_velocity: Position3D::new(0.0, 0.0, 0.0),
confidence: 1.0,
}
}
pub fn get_euler_angles(&self) -> (f32, f32, f32) {
let (x, y, z, w) = self.orientation;
let sinr_cosp = 2.0 * (w * x + y * z);
let cosr_cosp = 1.0 - 2.0 * (x * x + y * y);
let roll = sinr_cosp.atan2(cosr_cosp);
let sinp = 2.0 * (w * y - z * x);
let pitch = if sinp.abs() >= 1.0 {
std::f32::consts::PI / 2.0 * sinp.signum()
} else {
sinp.asin()
};
let siny_cosp = 2.0 * (w * z + x * y);
let cosy_cosp = 1.0 - 2.0 * (y * y + z * z);
let yaw = siny_cosp.atan2(cosy_cosp);
(yaw, pitch, roll)
}
}
pub struct PlatformFactory;
impl PlatformFactory {
pub fn create_platform(platform_type: PlatformType) -> Result<Box<dyn PlatformIntegration>> {
match platform_type {
PlatformType::Oculus => Ok(Box::new(OculusPlatform::new())),
#[cfg(feature = "steamvr")]
PlatformType::SteamVR => Ok(Box::new(SteamVRPlatform::new())),
#[cfg(not(feature = "steamvr"))]
PlatformType::SteamVR => Err(Error::LegacyConfig(
"SteamVR support not compiled in".to_string(),
)),
PlatformType::ARKit => Ok(Box::new(ARKitPlatform::new())),
PlatformType::ARCore => Ok(Box::new(ARCorePlatform::new())),
PlatformType::WMR => Ok(Box::new(WMRPlatform::new())),
PlatformType::Generic => Ok(Box::new(GenericPlatform::new())),
PlatformType::Custom => Err(Error::LegacyConfig(
"Custom platform requires specific implementation".to_string(),
)),
}
}
pub async fn detect_platforms() -> Vec<PlatformType> {
let mut available = Vec::new();
let platforms = [
PlatformType::Oculus,
#[cfg(feature = "steamvr")]
PlatformType::SteamVR,
PlatformType::ARKit,
PlatformType::ARCore,
PlatformType::WMR,
];
for platform_type in platforms {
if let Ok(mut platform) = Self::create_platform(platform_type) {
if platform.is_available().await {
available.push(platform_type);
}
}
}
available.push(PlatformType::Generic);
available
}
pub async fn create_best_available() -> Result<Box<dyn PlatformIntegration>> {
let available_platforms = Self::detect_platforms().await;
let priority_order = [
#[cfg(feature = "steamvr")]
PlatformType::SteamVR,
PlatformType::Oculus,
PlatformType::WMR,
PlatformType::ARKit,
PlatformType::ARCore,
PlatformType::Generic,
];
for platform_type in priority_order {
if available_platforms.contains(&platform_type) {
return Self::create_platform(platform_type);
}
}
Self::create_platform(PlatformType::Generic)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_platform_factory() {
let platform = PlatformFactory::create_platform(PlatformType::Generic).unwrap();
let capabilities = platform.get_capabilities();
assert!(capabilities.head_tracking_6dof);
}
#[tokio::test]
async fn test_platform_detection() {
let platforms = PlatformFactory::detect_platforms().await;
assert!(platforms.contains(&PlatformType::Generic));
}
#[tokio::test]
async fn test_best_available_platform() {
let platform = PlatformFactory::create_best_available().await;
assert!(platform.is_ok());
}
#[tokio::test]
async fn test_tracking_config_default() {
let config = TrackingConfig::default();
assert!(config.enable_prediction);
assert_eq!(config.prediction_time_ms, 15.0);
assert_eq!(config.target_refresh_rate, 90.0);
}
#[tokio::test]
async fn test_pose_data() {
let pose = PoseData::new(Position3D::new(1.0, 2.0, 3.0), (0.0, 0.0, 0.0, 1.0));
let euler = pose.get_euler_angles();
assert_eq!(euler, (0.0, 0.0, 0.0));
assert_eq!(pose.position.x, 1.0);
assert_eq!(pose.position.y, 2.0);
assert_eq!(pose.position.z, 3.0);
}
#[tokio::test]
async fn test_platform_capabilities() {
let capabilities = PlatformCapabilities {
head_tracking_6dof: true,
hand_tracking: false,
eye_tracking: false,
controller_tracking: true,
room_scale: true,
passthrough: false,
refresh_rates: vec![90.0, 120.0],
tracking_range: 10.0,
};
assert!(capabilities.head_tracking_6dof);
assert!(capabilities.controller_tracking);
assert!(capabilities.room_scale);
assert!(!capabilities.hand_tracking);
assert!(!capabilities.eye_tracking);
assert!(!capabilities.passthrough);
}
}