Skip to main content

voirs_spatial/platforms/
mod.rs

1//! VR/AR Platform Integration Module
2//!
3//! This module provides comprehensive integration with major VR/AR platforms,
4//! including Oculus/Meta, SteamVR, ARKit, ARCore, and Windows Mixed Reality.
5
6use crate::position::{CalibrationData, PlatformData, PlatformType};
7use crate::types::Position3D;
8use crate::{Error, Result};
9use async_trait::async_trait;
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12use std::time::Duration;
13use tokio::time::Instant;
14
15// Platform-specific implementations
16pub mod arcore;
17pub mod arkit;
18pub mod generic;
19pub mod oculus;
20pub mod steamvr;
21pub mod wmr;
22
23// Re-export platform implementations
24pub use arcore::ARCorePlatform;
25pub use arkit::ARKitPlatform;
26pub use generic::GenericPlatform;
27pub use oculus::OculusPlatform;
28#[cfg(feature = "steamvr")]
29pub use steamvr::SteamVRPlatform;
30pub use wmr::WMRPlatform;
31
32/// Platform integration trait for VR/AR platforms
33#[async_trait]
34pub trait PlatformIntegration: Send + Sync {
35    /// Initialize the platform integration
36    async fn initialize(&mut self) -> Result<()>;
37
38    /// Get current tracking data
39    async fn get_tracking_data(&self) -> Result<PlatformTrackingData>;
40
41    /// Check if platform is available
42    async fn is_available(&self) -> bool;
43
44    /// Get platform capabilities
45    fn get_capabilities(&self) -> PlatformCapabilities;
46
47    /// Set tracking configuration
48    async fn configure_tracking(&mut self, config: TrackingConfig) -> Result<()>;
49
50    /// Get device information
51    fn get_device_info(&self) -> DeviceInfo;
52
53    /// Start tracking session
54    async fn start_tracking(&mut self) -> Result<()>;
55
56    /// Stop tracking session  
57    async fn stop_tracking(&mut self) -> Result<()>;
58
59    /// Get hand tracking data if available
60    async fn get_hand_tracking(&self) -> Result<Option<HandTrackingData>>;
61
62    /// Get eye tracking data if available
63    async fn get_eye_tracking(&self) -> Result<Option<EyeTrackingData>>;
64}
65
66/// Complete tracking data from platform
67#[derive(Debug, Clone)]
68pub struct PlatformTrackingData {
69    /// Head pose (position + orientation)
70    pub head_pose: PoseData,
71    /// Left controller pose (if available)
72    pub left_controller: Option<PoseData>,
73    /// Right controller pose (if available)
74    pub right_controller: Option<PoseData>,
75    /// Tracking quality metrics
76    pub quality: TrackingQuality,
77    /// Platform timestamp
78    pub timestamp: Instant,
79    /// Raw platform data
80    pub raw_data: PlatformData,
81}
82
83/// Pose data (position + orientation)
84#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct PoseData {
86    /// Position in 3D space
87    pub position: Position3D,
88    /// Orientation quaternion (x, y, z, w)
89    pub orientation: (f32, f32, f32, f32),
90    /// Linear velocity
91    pub linear_velocity: Position3D,
92    /// Angular velocity
93    pub angular_velocity: Position3D,
94    /// Pose confidence (0.0 - 1.0)
95    pub confidence: f32,
96}
97
98/// Tracking quality metrics
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct TrackingQuality {
101    /// Overall tracking quality (0.0 - 1.0)
102    pub overall_quality: f32,
103    /// Position tracking quality
104    pub position_quality: f32,
105    /// Orientation tracking quality
106    pub orientation_quality: f32,
107    /// Number of tracking features
108    pub feature_count: u32,
109    /// Tracking state
110    pub state: TrackingState,
111}
112
113/// Tracking state enumeration
114#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
115pub enum TrackingState {
116    /// Not tracking
117    NotTracking,
118    /// Limited tracking (3DOF)
119    Limited,
120    /// Full tracking (6DOF)
121    Full,
122    /// Tracking lost
123    Lost,
124}
125
126/// Platform capabilities
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct PlatformCapabilities {
129    /// Supports 6DOF head tracking
130    pub head_tracking_6dof: bool,
131    /// Supports hand tracking
132    pub hand_tracking: bool,
133    /// Supports eye tracking
134    pub eye_tracking: bool,
135    /// Supports controller tracking
136    pub controller_tracking: bool,
137    /// Supports room scale tracking
138    pub room_scale: bool,
139    /// Supports passthrough/AR
140    pub passthrough: bool,
141    /// Supported refresh rates
142    pub refresh_rates: Vec<f32>,
143    /// Maximum tracking range
144    pub tracking_range: f32,
145}
146
147/// Tracking configuration
148#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct TrackingConfig {
150    /// Enable prediction for latency compensation
151    pub enable_prediction: bool,
152    /// Prediction time (milliseconds)
153    pub prediction_time_ms: f32,
154    /// Smoothing factor for position (0.0 - 1.0)
155    pub position_smoothing: f32,
156    /// Smoothing factor for orientation (0.0 - 1.0)
157    pub orientation_smoothing: f32,
158    /// Enable hand tracking
159    pub enable_hand_tracking: bool,
160    /// Enable eye tracking
161    pub enable_eye_tracking: bool,
162    /// Target refresh rate
163    pub target_refresh_rate: f32,
164}
165
166/// Device information
167#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct DeviceInfo {
169    /// Device name
170    pub name: String,
171    /// Manufacturer
172    pub manufacturer: String,
173    /// Model number
174    pub model: String,
175    /// Serial number
176    pub serial_number: String,
177    /// Firmware version
178    pub firmware_version: String,
179    /// Platform version/SDK version
180    pub platform_version: String,
181}
182
183/// Hand tracking data
184#[derive(Debug, Clone)]
185pub struct HandTrackingData {
186    /// Left hand data
187    pub left_hand: Option<HandData>,
188    /// Right hand data
189    pub right_hand: Option<HandData>,
190    /// Timestamp
191    pub timestamp: Instant,
192}
193
194/// Individual hand data
195#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct HandData {
197    /// Joint positions (25 joints)
198    pub joints: Vec<Position3D>,
199    /// Joint orientations (quaternions)
200    pub orientations: Vec<(f32, f32, f32, f32)>,
201    /// Tracking confidence per joint
202    pub confidences: Vec<f32>,
203    /// Hand gesture recognition
204    pub gesture: HandGesture,
205    /// Is hand tracked
206    pub is_tracked: bool,
207}
208
209/// Hand gesture enumeration
210#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
211pub enum HandGesture {
212    /// Unknown gesture
213    Unknown,
214    /// Open hand
215    Open,
216    /// Closed fist
217    Fist,
218    /// Pointing gesture
219    Point,
220    /// Pinch gesture
221    Pinch,
222    /// Thumb up
223    ThumbUp,
224    /// Peace sign
225    Peace,
226    /// OK gesture
227    Ok,
228}
229
230/// Eye tracking data
231#[derive(Debug, Clone)]
232pub struct EyeTrackingData {
233    /// Left eye data
234    pub left_eye: EyeData,
235    /// Right eye data
236    pub right_eye: EyeData,
237    /// Combined gaze direction
238    pub combined_gaze: Position3D,
239    /// Pupil dilation
240    pub pupil_dilation: f32,
241    /// Eye tracking confidence
242    pub confidence: f32,
243    /// Timestamp
244    pub timestamp: Instant,
245}
246
247/// Individual eye data
248#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct EyeData {
250    /// Eye position in world space
251    pub position: Position3D,
252    /// Gaze direction vector
253    pub gaze_direction: Position3D,
254    /// Eye openness (0.0 - 1.0)
255    pub openness: f32,
256    /// Pupil position in eye
257    pub pupil_position: (f32, f32),
258    /// Eye tracking confidence
259    pub confidence: f32,
260}
261
262impl Default for TrackingConfig {
263    fn default() -> Self {
264        Self {
265            enable_prediction: true,
266            prediction_time_ms: 15.0, // 15ms for VR
267            position_smoothing: 0.1,
268            orientation_smoothing: 0.05,
269            enable_hand_tracking: false,
270            enable_eye_tracking: false,
271            target_refresh_rate: 90.0,
272        }
273    }
274}
275
276impl PoseData {
277    /// Create new pose data
278    pub fn new(position: Position3D, orientation: (f32, f32, f32, f32)) -> Self {
279        Self {
280            position,
281            orientation,
282            linear_velocity: Position3D::new(0.0, 0.0, 0.0),
283            angular_velocity: Position3D::new(0.0, 0.0, 0.0),
284            confidence: 1.0,
285        }
286    }
287
288    /// Get orientation as euler angles (yaw, pitch, roll)
289    pub fn get_euler_angles(&self) -> (f32, f32, f32) {
290        let (x, y, z, w) = self.orientation;
291
292        // Roll (x-axis rotation)
293        let sinr_cosp = 2.0 * (w * x + y * z);
294        let cosr_cosp = 1.0 - 2.0 * (x * x + y * y);
295        let roll = sinr_cosp.atan2(cosr_cosp);
296
297        // Pitch (y-axis rotation)
298        let sinp = 2.0 * (w * y - z * x);
299        let pitch = if sinp.abs() >= 1.0 {
300            std::f32::consts::PI / 2.0 * sinp.signum()
301        } else {
302            sinp.asin()
303        };
304
305        // Yaw (z-axis rotation)
306        let siny_cosp = 2.0 * (w * z + x * y);
307        let cosy_cosp = 1.0 - 2.0 * (y * y + z * z);
308        let yaw = siny_cosp.atan2(cosy_cosp);
309
310        (yaw, pitch, roll)
311    }
312}
313
314/// Platform factory for creating platform integrations
315pub struct PlatformFactory;
316
317impl PlatformFactory {
318    /// Create platform integration based on platform type
319    pub fn create_platform(platform_type: PlatformType) -> Result<Box<dyn PlatformIntegration>> {
320        match platform_type {
321            PlatformType::Oculus => Ok(Box::new(OculusPlatform::new())),
322            #[cfg(feature = "steamvr")]
323            PlatformType::SteamVR => Ok(Box::new(SteamVRPlatform::new())),
324            #[cfg(not(feature = "steamvr"))]
325            PlatformType::SteamVR => Err(Error::LegacyConfig(
326                "SteamVR support not compiled in".to_string(),
327            )),
328            PlatformType::ARKit => Ok(Box::new(ARKitPlatform::new())),
329            PlatformType::ARCore => Ok(Box::new(ARCorePlatform::new())),
330            PlatformType::WMR => Ok(Box::new(WMRPlatform::new())),
331            PlatformType::Generic => Ok(Box::new(GenericPlatform::new())),
332            PlatformType::Custom => Err(Error::LegacyConfig(
333                "Custom platform requires specific implementation".to_string(),
334            )),
335        }
336    }
337
338    /// Auto-detect available platforms
339    pub async fn detect_platforms() -> Vec<PlatformType> {
340        let mut available = Vec::new();
341
342        // Check each platform type
343        let platforms = [
344            PlatformType::Oculus,
345            #[cfg(feature = "steamvr")]
346            PlatformType::SteamVR,
347            PlatformType::ARKit,
348            PlatformType::ARCore,
349            PlatformType::WMR,
350        ];
351
352        for platform_type in platforms {
353            if let Ok(mut platform) = Self::create_platform(platform_type) {
354                if platform.is_available().await {
355                    available.push(platform_type);
356                }
357            }
358        }
359
360        // Generic platform is always available as fallback
361        available.push(PlatformType::Generic);
362
363        available
364    }
365
366    /// Create the best available platform
367    pub async fn create_best_available() -> Result<Box<dyn PlatformIntegration>> {
368        let available_platforms = Self::detect_platforms().await;
369
370        // Priority order: SteamVR > Oculus > WMR > ARKit > ARCore > Generic
371        let priority_order = [
372            #[cfg(feature = "steamvr")]
373            PlatformType::SteamVR,
374            PlatformType::Oculus,
375            PlatformType::WMR,
376            PlatformType::ARKit,
377            PlatformType::ARCore,
378            PlatformType::Generic,
379        ];
380
381        for platform_type in priority_order {
382            if available_platforms.contains(&platform_type) {
383                return Self::create_platform(platform_type);
384            }
385        }
386
387        // Fallback to generic platform
388        Self::create_platform(PlatformType::Generic)
389    }
390}
391
392#[cfg(test)]
393mod tests {
394    use super::*;
395
396    #[tokio::test]
397    async fn test_platform_factory() {
398        let platform = PlatformFactory::create_platform(PlatformType::Generic).unwrap();
399        let capabilities = platform.get_capabilities();
400        assert!(capabilities.head_tracking_6dof);
401    }
402
403    #[tokio::test]
404    async fn test_platform_detection() {
405        let platforms = PlatformFactory::detect_platforms().await;
406        // Generic platform should always be available
407        assert!(platforms.contains(&PlatformType::Generic));
408    }
409
410    #[tokio::test]
411    async fn test_best_available_platform() {
412        let platform = PlatformFactory::create_best_available().await;
413        assert!(platform.is_ok());
414    }
415
416    #[tokio::test]
417    async fn test_tracking_config_default() {
418        let config = TrackingConfig::default();
419        assert!(config.enable_prediction);
420        assert_eq!(config.prediction_time_ms, 15.0);
421        assert_eq!(config.target_refresh_rate, 90.0);
422    }
423
424    #[tokio::test]
425    async fn test_pose_data() {
426        let pose = PoseData::new(Position3D::new(1.0, 2.0, 3.0), (0.0, 0.0, 0.0, 1.0));
427
428        let euler = pose.get_euler_angles();
429        assert_eq!(euler, (0.0, 0.0, 0.0)); // Identity quaternion
430
431        assert_eq!(pose.position.x, 1.0);
432        assert_eq!(pose.position.y, 2.0);
433        assert_eq!(pose.position.z, 3.0);
434    }
435
436    #[tokio::test]
437    async fn test_platform_capabilities() {
438        let capabilities = PlatformCapabilities {
439            head_tracking_6dof: true,
440            hand_tracking: false,
441            eye_tracking: false,
442            controller_tracking: true,
443            room_scale: true,
444            passthrough: false,
445            refresh_rates: vec![90.0, 120.0],
446            tracking_range: 10.0,
447        };
448
449        assert!(capabilities.head_tracking_6dof);
450        assert!(capabilities.controller_tracking);
451        assert!(capabilities.room_scale);
452        assert!(!capabilities.hand_tracking);
453        assert!(!capabilities.eye_tracking);
454        assert!(!capabilities.passthrough);
455    }
456}