use glam::Quat; use mint::Vector3; use nix::unistd::{execv, fork}; use stardust_xr_molecules::{ fusion::{ client::{Client, LifeCycleHandler, LogicStepInfo}, core::values::Transform, drawable::Model, fields::SphereField, node::NodeType, resource::NamespacedResource, startup_settings::StartupSettings, }, GrabData, Grabbable, }; use std::{ffi::CString, path::PathBuf, sync::Arc}; use tween::{QuartInOut, Tweener}; use ustr::ustr; pub struct ProtoStar { client: Arc, grabbable: Grabbable, field: SphereField, icon: Model, icon_shrink: Option>>, size: f32, executable_path: PathBuf, } impl ProtoStar { pub fn new(client: Arc, size: f32, executable_path: PathBuf) -> Self { let field = SphereField::create(client.get_root(), Vector3::from([0.0; 3]), size * 0.5).unwrap(); let grabbable = Grabbable::new( client.get_root(), Transform::default(), &field, GrabData { max_distance: 0.05 }, ) .unwrap(); field .set_spatial_parent(grabbable.content_parent()) .unwrap(); let icon = Model::create( grabbable.content_parent(), Transform::from_scale([size; 3]), &NamespacedResource::new("protostar", "default_icon"), ) .unwrap(); ProtoStar { client, grabbable, field, icon, icon_shrink: None, size, executable_path, } } } impl LifeCycleHandler for ProtoStar { fn logic_step(&mut self, info: LogicStepInfo) { self.grabbable.update(); if let Some(icon_shrink) = &mut self.icon_shrink { if let Some(scale) = icon_shrink.update(info.delta) { self.icon .set_scale(None, Vector3::from([scale; 3])) .unwrap(); } else { self.client.stop_loop(); } } else if self.grabbable.grab_action().actor_stopped() { let startup_settings = StartupSettings::create(&self.field.client().unwrap()).unwrap(); self.icon .set_spatial_parent_in_place(self.client.get_root()) .unwrap(); self.grabbable .content_parent() .set_rotation( Some(&self.field.client().unwrap().get_root()), Quat::IDENTITY, ) .unwrap(); startup_settings .set_root(self.grabbable.content_parent()) .unwrap(); self.icon_shrink = Some(Tweener::new(QuartInOut::new(self.size..=0.0, 0.25))); let future = startup_settings.generate_startup_token().unwrap(); let executable = self.executable_path.clone(); tokio::task::spawn(async move { std::env::set_var("STARDUST_STARTUP_TOKEN", future.await.unwrap()); if unsafe { fork() }.unwrap().is_parent() { let executable = ustr(executable.to_str().unwrap()); execv::(executable.as_cstr(), &[]).unwrap(); } }); } } }