1use 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
15pub mod arcore;
17pub mod arkit;
18pub mod generic;
19pub mod oculus;
20pub mod steamvr;
21pub mod wmr;
22
23pub 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#[async_trait]
34pub trait PlatformIntegration: Send + Sync {
35 async fn initialize(&mut self) -> Result<()>;
37
38 async fn get_tracking_data(&self) -> Result<PlatformTrackingData>;
40
41 async fn is_available(&self) -> bool;
43
44 fn get_capabilities(&self) -> PlatformCapabilities;
46
47 async fn configure_tracking(&mut self, config: TrackingConfig) -> Result<()>;
49
50 fn get_device_info(&self) -> DeviceInfo;
52
53 async fn start_tracking(&mut self) -> Result<()>;
55
56 async fn stop_tracking(&mut self) -> Result<()>;
58
59 async fn get_hand_tracking(&self) -> Result<Option<HandTrackingData>>;
61
62 async fn get_eye_tracking(&self) -> Result<Option<EyeTrackingData>>;
64}
65
66#[derive(Debug, Clone)]
68pub struct PlatformTrackingData {
69 pub head_pose: PoseData,
71 pub left_controller: Option<PoseData>,
73 pub right_controller: Option<PoseData>,
75 pub quality: TrackingQuality,
77 pub timestamp: Instant,
79 pub raw_data: PlatformData,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct PoseData {
86 pub position: Position3D,
88 pub orientation: (f32, f32, f32, f32),
90 pub linear_velocity: Position3D,
92 pub angular_velocity: Position3D,
94 pub confidence: f32,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct TrackingQuality {
101 pub overall_quality: f32,
103 pub position_quality: f32,
105 pub orientation_quality: f32,
107 pub feature_count: u32,
109 pub state: TrackingState,
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
115pub enum TrackingState {
116 NotTracking,
118 Limited,
120 Full,
122 Lost,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct PlatformCapabilities {
129 pub head_tracking_6dof: bool,
131 pub hand_tracking: bool,
133 pub eye_tracking: bool,
135 pub controller_tracking: bool,
137 pub room_scale: bool,
139 pub passthrough: bool,
141 pub refresh_rates: Vec<f32>,
143 pub tracking_range: f32,
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct TrackingConfig {
150 pub enable_prediction: bool,
152 pub prediction_time_ms: f32,
154 pub position_smoothing: f32,
156 pub orientation_smoothing: f32,
158 pub enable_hand_tracking: bool,
160 pub enable_eye_tracking: bool,
162 pub target_refresh_rate: f32,
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct DeviceInfo {
169 pub name: String,
171 pub manufacturer: String,
173 pub model: String,
175 pub serial_number: String,
177 pub firmware_version: String,
179 pub platform_version: String,
181}
182
183#[derive(Debug, Clone)]
185pub struct HandTrackingData {
186 pub left_hand: Option<HandData>,
188 pub right_hand: Option<HandData>,
190 pub timestamp: Instant,
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct HandData {
197 pub joints: Vec<Position3D>,
199 pub orientations: Vec<(f32, f32, f32, f32)>,
201 pub confidences: Vec<f32>,
203 pub gesture: HandGesture,
205 pub is_tracked: bool,
207}
208
209#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
211pub enum HandGesture {
212 Unknown,
214 Open,
216 Fist,
218 Point,
220 Pinch,
222 ThumbUp,
224 Peace,
226 Ok,
228}
229
230#[derive(Debug, Clone)]
232pub struct EyeTrackingData {
233 pub left_eye: EyeData,
235 pub right_eye: EyeData,
237 pub combined_gaze: Position3D,
239 pub pupil_dilation: f32,
241 pub confidence: f32,
243 pub timestamp: Instant,
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct EyeData {
250 pub position: Position3D,
252 pub gaze_direction: Position3D,
254 pub openness: f32,
256 pub pupil_position: (f32, f32),
258 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, 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 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 pub fn get_euler_angles(&self) -> (f32, f32, f32) {
290 let (x, y, z, w) = self.orientation;
291
292 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 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 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
314pub struct PlatformFactory;
316
317impl PlatformFactory {
318 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 pub async fn detect_platforms() -> Vec<PlatformType> {
340 let mut available = Vec::new();
341
342 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 available.push(PlatformType::Generic);
362
363 available
364 }
365
366 pub async fn create_best_available() -> Result<Box<dyn PlatformIntegration>> {
368 let available_platforms = Self::detect_platforms().await;
369
370 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 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 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)); 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}