1use std::ffi::{CString, c_char};
8use std::ptr;
9
10use openxr_sys::{RenderModelCapabilitiesRequestFB,
11 Instance, Path, RenderModelBufferFB, RenderModelFlagsFB, RenderModelKeyFB, RenderModelLoadInfoFB,
12 RenderModelPathInfoFB, RenderModelPropertiesFB, Result as XrResult, Session, StructureType,
13 pfn::{EnumerateRenderModelPathsFB, GetRenderModelPropertiesFB, LoadRenderModelFB, PathToString, StringToPath},
14};
15
16use crate::maths::units::CM;
17use crate::maths::{Matrix, Quat, Vec3};
18use crate::model::AnimMode;
19use crate::{
20 model::Model,
21 prelude::*,
22 system::{Backend, BackendOpenXR, BackendXRType, Handed, Input, Log},
23};
24
25
26pub const XR_FB_RENDER_MODEL_EXTENSION_NAME: &str = "XR_FB_render_model";
28
29#[derive(Debug, Clone)]
31pub struct RenderModelProperties {
32 pub vendor_id: u32,
33 pub model_name: String,
34 pub model_version: u32,
35 pub flags: u64,
36}
37
38pub struct XrFbRenderModel {
40 with_log: bool,
41 xr_enumerate_render_model_paths: Option<EnumerateRenderModelPathsFB>,
42 xr_get_render_model_properties: Option<GetRenderModelPropertiesFB>,
43 xr_load_render_model: Option<LoadRenderModelFB>,
44 xr_string_to_path: Option<StringToPath>,
45 xr_path_to_string: Option<PathToString>,
46 instance: Instance,
47 session: Session,
48
49 left_controller_data: Option<Model>,
51 right_controller_data: Option<Model>,
52}
53
54impl XrFbRenderModel {
55 pub fn new(with_log: bool) -> Option<Self> {
57 if Backend::xr_type() != BackendXRType::OpenXR || !BackendOpenXR::ext_enabled(XR_FB_RENDER_MODEL_EXTENSION_NAME)
58 {
59 return None;
60 }
61
62 let instance = Instance::from_raw(BackendOpenXR::instance());
63 let session = Session::from_raw(BackendOpenXR::session());
64
65 let xr_enumerate_render_model_paths =
66 BackendOpenXR::get_function::<EnumerateRenderModelPathsFB>("xrEnumerateRenderModelPathsFB");
67 let xr_get_render_model_properties =
68 BackendOpenXR::get_function::<GetRenderModelPropertiesFB>("xrGetRenderModelPropertiesFB");
69 let xr_load_render_model = BackendOpenXR::get_function::<LoadRenderModelFB>("xrLoadRenderModelFB");
70 let xr_string_to_path = BackendOpenXR::get_function::<StringToPath>("xrStringToPath");
71 let xr_path_to_string = BackendOpenXR::get_function::<PathToString>("xrPathToString");
72
73 if xr_enumerate_render_model_paths.is_none()
74 || xr_get_render_model_properties.is_none()
75 || xr_load_render_model.is_none()
76 || xr_string_to_path.is_none()
77 || xr_path_to_string.is_none()
78 {
79 Log::warn("❌ Failed to load all XR_FB_render_model functions");
80 return None;
81 }
82
83 Some(Self {
84 with_log,
85 xr_enumerate_render_model_paths,
86 xr_get_render_model_properties,
87 xr_load_render_model,
88 xr_string_to_path,
89 xr_path_to_string,
90 instance,
91 session,
92 left_controller_data: None,
93 right_controller_data: None,
94 })
95 }
96
97 fn path_to_string(&self, path: Path) -> Option<String> {
98 let path_to_string_fn = self.xr_path_to_string?;
99
100 let mut buffer_count_output = 0u32;
101 let result = unsafe { path_to_string_fn(self.instance, path, 0, &mut buffer_count_output, ptr::null_mut()) };
102
103 if result != XrResult::SUCCESS || buffer_count_output == 0 {
104 return None;
105 }
106
107 let mut buffer = vec![0u8; buffer_count_output as usize];
108 let result = unsafe {
109 path_to_string_fn(
110 self.instance,
111 path,
112 buffer_count_output,
113 &mut buffer_count_output,
114 buffer.as_mut_ptr() as *mut c_char,
115 )
116 };
117
118 if result == XrResult::SUCCESS {
119 if let Some(&0) = buffer.last() {
120 buffer.pop();
121 }
122 String::from_utf8(buffer).ok()
123 } else {
124 None
125 }
126 }
127
128 pub fn enumerate_render_model_paths(&self) -> Result<Vec<String>, XrResult> {
130 let enumerate_fn = self.xr_enumerate_render_model_paths.ok_or(XrResult::ERROR_FUNCTION_UNSUPPORTED)?;
131
132 let mut path_count = 0u32;
133 let result = unsafe { enumerate_fn(self.session, 0, &mut path_count, ptr::null_mut()) };
134
135 if result != XrResult::SUCCESS {
136 return Err(result);
137 }
138
139 if path_count == 0 {
140 return Ok(Vec::new());
141 }
142
143 let mut path_infos = vec![
144 RenderModelPathInfoFB {
145 ty: StructureType::RENDER_MODEL_PATH_INFO_FB,
146 next: ptr::null_mut(),
147 path: Path::from_raw(0),
148 };
149 path_count as usize
150 ];
151
152 let result = unsafe { enumerate_fn(self.session, path_count, &mut path_count, path_infos.as_mut_ptr()) };
153
154 if result != XrResult::SUCCESS {
155 return Err(result);
156 }
157
158 let mut paths = Vec::new();
159 for path_info in path_infos {
160 if let Some(path_string) = self.path_to_string(path_info.path) {
161 paths.push(path_string);
162 }
163 }
164
165 Ok(paths)
166 }
167
168 pub fn get_render_model_properties(&self, model_path: &str) -> Result<RenderModelProperties, XrResult> {
170 let get_properties_fn = self.xr_get_render_model_properties.ok_or(XrResult::ERROR_FUNCTION_UNSUPPORTED)?;
171 let string_to_path_fn = self.xr_string_to_path.ok_or(XrResult::ERROR_FUNCTION_UNSUPPORTED)?;
172
173 let c_string = CString::new(model_path).map_err(|_| XrResult::ERROR_VALIDATION_FAILURE)?;
174 let mut path = Path::from_raw(0);
175 let result = unsafe { string_to_path_fn(self.instance, c_string.as_ptr(), &mut path) };
176
177 if result != XrResult::SUCCESS {
178 return Err(result);
179 }
180
181 let mut properties = RenderModelPropertiesFB {
182 ty: StructureType::RENDER_MODEL_PROPERTIES_FB,
183 next: ptr::null_mut(),
184 vendor_id: 0,
185 model_name: [0; 64],
186 model_key: RenderModelKeyFB::from_raw(0),
187 model_version: 0,
188 flags: RenderModelFlagsFB::from_raw(0),
189 };
190
191 let mut cap_req = RenderModelCapabilitiesRequestFB {
192 ty: StructureType::RENDER_MODEL_CAPABILITIES_REQUEST_FB,
193 next: ptr::null_mut(),
194 flags: RenderModelFlagsFB::SUPPORTS_GLTF_2_0_SUBSET_2,
195
196 };
197
198 properties.next = &mut cap_req as *mut _ as *mut _;
199
200 let result = unsafe { get_properties_fn(self.session, path, &mut properties) };
201
202 if result != XrResult::SUCCESS {
206 return Err(result);
207 }
208
209 let model_name = unsafe {
210 let name_ptr = properties.model_name.as_ptr() as *const c_char;
211 let c_str = std::ffi::CStr::from_ptr(name_ptr);
212 c_str.to_string_lossy().into_owned()
213 };
214
215 Ok(RenderModelProperties {
216 vendor_id: properties.vendor_id,
217 model_name,
218 model_version: properties.model_version,
219 flags: properties.flags.into_raw(),
220 })
221 }
222
223
224 pub fn load_render_model(&self, model_path: &str) -> Result<Vec<u8>, XrResult> {
226 let load_fn = self.xr_load_render_model.ok_or(XrResult::ERROR_FUNCTION_UNSUPPORTED)?;
227 let get_properties_fn = self.xr_get_render_model_properties.ok_or(XrResult::ERROR_FUNCTION_UNSUPPORTED)?;
228 let string_to_path_fn = self.xr_string_to_path.ok_or(XrResult::ERROR_FUNCTION_UNSUPPORTED)?;
229
230 let c_string = CString::new(model_path).map_err(|_| XrResult::ERROR_VALIDATION_FAILURE)?;
231 let mut path = Path::from_raw(0);
232 let result = unsafe { string_to_path_fn(self.instance, c_string.as_ptr(), &mut path) };
233
234 if result != XrResult::SUCCESS {
235 return Err(result);
236 }
237
238 let mut properties = RenderModelPropertiesFB {
239 ty: StructureType::RENDER_MODEL_PROPERTIES_FB,
240 next: ptr::null_mut(),
241 vendor_id: 0,
242 model_name: [0; 64],
243 model_key: RenderModelKeyFB::from_raw(0),
244 model_version: 0,
245 flags: RenderModelFlagsFB::from_raw(0),
246 };
247
248 let mut cap_req = RenderModelCapabilitiesRequestFB {
249 ty: StructureType::RENDER_MODEL_CAPABILITIES_REQUEST_FB,
250 next: ptr::null_mut(),
251 flags: RenderModelFlagsFB::SUPPORTS_GLTF_2_0_SUBSET_2,
252
253 };
254
255 properties.next = &mut cap_req as *mut _ as *mut _;
256
257 let result = unsafe { get_properties_fn(self.session, path, &mut properties) };
258 if result != XrResult::SUCCESS && result != XrResult::RENDER_MODEL_UNAVAILABLE_FB && result != XrResult::SESSION_LOSS_PENDING {
259 return Err(result);
260 }
261
262 let model_key = properties.model_key;
263
264 let load_info =
265 RenderModelLoadInfoFB { ty: StructureType::RENDER_MODEL_LOAD_INFO_FB, next: ptr::null_mut(), model_key };
266
267 let mut buffer = RenderModelBufferFB {
268 ty: StructureType::RENDER_MODEL_BUFFER_FB,
269 next: ptr::null_mut(),
270 buffer_capacity_input: 0,
271 buffer_count_output: 0,
272 buffer: ptr::null_mut(),
273 };
274
275 let result = unsafe { load_fn(self.session, &load_info, &mut buffer) };
276 if result != XrResult::SUCCESS {
277 return Err(result);
278 }
279
280 let buffer_size = buffer.buffer_count_output;
281 if buffer_size == 0 {
282 return Ok(Vec::new());
283 }
284
285 let mut data_buffer = vec![0u8; buffer_size as usize];
286 buffer.buffer_capacity_input = buffer_size;
287 buffer.buffer = data_buffer.as_mut_ptr();
288
289 let result = unsafe { load_fn(self.session, &load_info, &mut buffer) };
290 if result != XrResult::SUCCESS {
291 return Err(result);
292 }
293
294 data_buffer.truncate(buffer.buffer_count_output as usize);
295
296 Ok(data_buffer)
297 }
298
299 pub fn get_controller_model(
301 &mut self,
302 handed: Handed,
303 model_path: &str,
304 ) -> Result<&Model, Box<dyn std::error::Error>> {
305 let needs_loading = match handed {
306 Handed::Left => self.left_controller_data.is_none(),
307 Handed::Right => self.right_controller_data.is_none(),
308 Handed::Max => return Err("Invalid handed value: Max is not a valid controller".into()),
309 };
310
311 if needs_loading {
312 let data = self.load_render_model(model_path)?;
313 let model = Model::from_memory(format!("{model_path}.gltf"), &data, None)?;
314
315 if let Some(mut n) = model.get_nodes().get_root_node() {
316 let new_rotation = Quat::from_angles(0.0, 0.0, 0.0);
317 let transf = Matrix::t_r(Vec3::new(0.0, 0.0 * CM, 0.0 * CM), new_rotation);
318 n.local_transform(transf);
319 }
320
321 match handed {
322 Handed::Left => self.left_controller_data = Some(model),
323 Handed::Right => self.right_controller_data = Some(model),
324 Handed::Max => unreachable!(),
325 }
326 }
327
328 match handed {
329 Handed::Left => Ok(self.left_controller_data.as_ref().unwrap()),
330 Handed::Right => Ok(self.right_controller_data.as_ref().unwrap()),
331 Handed::Max => unreachable!(),
332 }
333 }
334
335 pub fn setup_controller_models(&mut self, left_path: &str, right_path: &str, with_animation: bool) -> Result<(), XrResult> {
337 let with_log = self.with_log;
339 if let Ok(right_model) = self.get_controller_model(Handed::Right, right_path) {
340 Input::set_controller_model(Handed::Right, Some(right_model));
341 if with_log {
342 Log::info(format!(" Right controller model loaded and configured from path: {}", right_path));
343 }
344
345 if with_animation {
347 right_model.get_anims().play_anim_idx(0, AnimMode::Loop);
348 if right_model.get_anims().get_count() > 1 {
349 Log::warn("⚠️ Right controller model has more than one animation, only the first will be played in loop");
350 }
351 if with_log {
352 Log::info("✅ Right controller animation started");
353 }
354 }
355 } else {
356 Log::warn(format!("❌ Failed to load right controller model from path: {}", right_path));
357 return Err(XrResult::ERROR_RUNTIME_FAILURE);
358 }
359
360 if let Ok(left_model) = self.get_controller_model(Handed::Left, left_path) {
362 Input::set_controller_model(Handed::Left, Some(left_model));
363 if with_log {
364 Log::info(format!(" Left controller model loaded and configured from path: {}", left_path));
365 }
366
367 if with_animation {
369 left_model.get_anims().play_anim_idx(0, AnimMode::Loop);
370 if with_log {
371 Log::info("✅ Left controller animation started");
372 }
373 }
374 } else {
375 Log::warn(format!("❌ Failed to load left controller model from path: {}", left_path));
376 return Err(XrResult::ERROR_RUNTIME_FAILURE);
377 }
378
379 Ok(())
380 }
381
382 pub fn disable_controller_models(&mut self) {
384 use crate::system::{Handed, Input};
385
386 Input::set_controller_model(Handed::Right, None);
387 Input::set_controller_model(Handed::Left, None);
388 self.left_controller_data = None;
389 self.right_controller_data = None;
390 }
391
392 pub fn explore_render_models(&self) -> Result<(), XrResult> {
394 if let Ok(paths) = self.enumerate_render_model_paths() {
395 for path in paths {
396 Log::diag(format!(" Render model: <{}>", path));
397 match self.get_render_model_properties(&path) {
398 Ok(properties) => {
399 Log::diag(format!(" Model: {:?}", properties.model_name));
400 Log::diag(format!(" Vendor ID: {}", properties.vendor_id));
401 Log::diag(format!(" Model version: {}", properties.model_version));
402 Log::diag(format!(" Model flags: 0x{:?}", properties.flags));
403 }
404 Err(e) => {
405 Log::diag(format!(" No properties for model: {}: {:?}", path, e));
406 }
407 }
408 }
409 }
410 Ok(())
411 }
412
413 #[allow(unused, dead_code)]
419 pub fn set_controller_anim_time(&mut self, handed: Handed, time: f32) {
420 match handed {
421 Handed::Left => {
422 if let Some(ref left_model) = self.left_controller_data {
423 left_model.get_anims().anim_time(time);
424 }
425 }
426 Handed::Right => {
427 if let Some(ref right_model) = self.right_controller_data {
428 right_model.get_anims().anim_time(time);
429 }
430 }
431 Handed::Max => {}
432 }
433 }
434}
435
436pub fn is_fb_render_model_extension_available() -> bool {
438 Backend::xr_type() == BackendXRType::OpenXR && BackendOpenXR::ext_enabled(XR_FB_RENDER_MODEL_EXTENSION_NAME)
439}
440
441pub const DRAW_CONTROLLER: &str = "draw_controller";
443
444const LEFT_SHIFT: f32 = 0.04; #[derive(IStepper)]
499pub struct XrFbRenderModelStepper {
500 id: StepperId,
501 sk_info: Option<Rc<RefCell<SkInfo>>>,
502 enabled: bool,
503 shutdown_completed: bool,
504
505 pub left_controller_model_path: String,
508
509 pub right_controller_model_path: String,
512
513 xr_render_model: Option<XrFbRenderModel>,
514 is_enabled: bool,
515
516 pub animation_time_code: f32,
519
520 pub with_animation: bool,
523}
524
525impl Default for XrFbRenderModelStepper {
526 fn default() -> Self {
527 Self {
528 id: "XrFbRenderModelStepper".to_string(),
529 sk_info: None,
530 enabled: true,
531 shutdown_completed: false,
532
533 xr_render_model: None,
534 is_enabled: false,
535 animation_time_code: 0.0,
536 with_animation: true,
537
538 left_controller_model_path: "/model_fb/controller/left".to_string(),
540 right_controller_model_path: "/model_fb/controller/right".to_string(),
541 }
542 }
543}
544
545unsafe impl Send for XrFbRenderModelStepper {}
546
547impl XrFbRenderModelStepper {
548 fn start(&mut self) -> bool {
550 if !is_fb_render_model_extension_available() {
553 Log::err("⚠️ XR_FB_render_model extension not available");
554 return false;
555 }
556 match XrFbRenderModel::new(false) {
557 Some(xr_model) => {
558 self.xr_render_model = Some(xr_model);
563 true
564 }
565 None => {
566 Log::err("❌ XR_FB_render_model extension not available");
567 false
568 }
569 }
570 }
571
572 fn check_event(&mut self, _id: &StepperId, key: &str, value: &str) {
574 if key == DRAW_CONTROLLER {
575 match value {
576 "true" => {
577 if !self.is_enabled {
578 self.is_enabled = true;
579
580 if let Some(ref mut xr_model) = self.xr_render_model {
582 if let Err(e) = xr_model.setup_controller_models(
584 &self.left_controller_model_path,
585 &self.right_controller_model_path,
586 self.with_animation,
587 ) {
588 Log::warn(format!("❌ DRAW_CONTROLLER Failed to setup controller models: {:?}", e));
589 } else {
590 Log::info("DRAW_CONTROLLER `true`: Controller models setup completed");
591 }
592 } else {
593 Log::warn("❌ DRAW_CONTROLLER `true` error: XR_FB_render_model not initialized");
594 }
595 }
596 }
597 _=> {
598 self.is_enabled = false;
599
600 if let Some(ref mut xr_model) = self.xr_render_model {
602 xr_model.disable_controller_models();
603 Log::info("DRAW_CONTROLLER `false`: Controller drawing disabled");
604 } else {
605 Log::warn("❌ DRAW_CONTROLLER `false` error: XR_FB_render_model not initialized");
606 }
607 }
608
609 }
610 }
611 }
612
613 fn set_animation(&mut self, handed: Handed, controller: &crate::system::Controller, shift: f32) {
629 let mut animation_times = vec![];
630
631 if handed == Handed::Left {
632 self.animation_time_code = (self.animation_time_code.max(0.0) + 1.0) % 4.0;
633 }
634
635 if let Some(ref mut xr_render_model) = self.xr_render_model {
636 let stick_threshold = 0.25; if controller.stick.magnitude() > stick_threshold {
639 let x = controller.stick.x;
640 let y = controller.stick.y;
641
642 let animation_time =
644 if x > 0.3 {
646 if y > 0.3 {
648 1.58 } else if y < -0.3 {
650 1.64 } else {
652 1.38 }
654 } else if x < -0.3 {
655 if y > 0.3 {
657 1.52 } else if y < -0.3 {
659 1.46 } else {
661 1.32 }
663 } else {
664 if y > 0.3 {
666 1.18 } else if y < -0.3 {
668 1.26 } else {
670 -1.0 }
672 };
673 if animation_time > 0.0 { animation_times.push(animation_time); }
674 }
675 if controller.trigger > 0.1 {
677 let animation_time = 0.6 + 0.06 * controller.trigger; animation_times.push(animation_time);
679 }
680 if controller.grip > 0.1 {
681 let animation_time = 0.82 + 0.06 * controller.grip; animation_times.push(animation_time);
683 }
684
685 let mut animation_time= -1.0;
687 if controller.is_x1_pressed() && controller.is_x2_pressed() {
688 animation_time = 0.46; } else if controller.is_x1_pressed() {
690 animation_time = 0.18; } else if controller.is_x2_pressed() {
692 animation_time = 0.32; } else if controller.is_stick_clicked() {
694 animation_time = -1.0; } else if Input::get_controller_menu_button().is_active() {
696 animation_time = 0.98; }
698
699 if animation_time > 0.0 { animation_times.push(animation_time); }
700
701 if animation_times.is_empty() {
702 xr_render_model.set_controller_anim_time(handed, 4.4);
704 } else {
705 let step_sel = self.animation_time_code % animation_times.len() as f32;
707 for (i, animation_time) in animation_times.into_iter().enumerate() {
708 if i as f32 == step_sel{
709 xr_render_model.set_controller_anim_time(handed, animation_time + shift);
710 break;
711 }
712 }
713 }
714 }
715 }
716
717 fn draw(&mut self, _token: &MainThreadToken) {
719 if !self.is_enabled {
720 return;
721 }
722
723 if false {
725 self.animation_analyser(_token);
726 } else if self.with_animation {
727 if self.xr_render_model.is_some() {
729 let left_controller = Input::controller(Handed::Left);
731 self.set_animation(Handed::Left, &left_controller, LEFT_SHIFT);
732
733 let right_controller = Input::controller(Handed::Right);
734 self.set_animation(Handed::Right, &right_controller, 0.0);
735 }
736 }
737 }
738
739 fn animation_analyser(&mut self, _token: &MainThreadToken) {
748 if Input::controller(Handed::Right).stick_click.is_just_active()
750 || Input::controller(Handed::Left).stick_click.is_just_active()
751 {
752 self.animation_time_code += 0.02;
753
754 if self.animation_time_code >= 6.0 {
756 self.animation_time_code = 0.0;
757 }
758
759 Log::diag(format!("Animation time code: {:.1}s", self.animation_time_code));
760 }
761
762 use crate::maths::{Matrix, Quat, Vec3};
764 use crate::system::Text;
765
766 let text_content = format!("Animation Time: {:.2}s\nPress joystick to advance", self.animation_time_code);
767 let position = Vec3::new(0.0, 1.5, -0.8);
768 let rotation = Quat::from_angles(0.0, 180.0, 0.0); let transform = Matrix::t_r(position, rotation);
770
771 Text::add_at(_token, &text_content, transform, None, None, None, None, None, None, None);
772
773 if let Some(ref mut xr_render_model) = self.xr_render_model {
775 xr_render_model.set_controller_anim_time(Handed::Left, self.animation_time_code);
776 xr_render_model.set_controller_anim_time(Handed::Right, self.animation_time_code);
777 }
778 }
779
780 fn close(&mut self, triggering: bool) -> bool {
783 if triggering {
784 if let Some(ref mut xr_model) = self.xr_render_model {
786 xr_model.disable_controller_models();
787 }
788
789 self.xr_render_model = None;
790 self.shutdown_completed = true;
791 true
792 } else {
793 self.shutdown_completed
794 }
795 }
796}