diff --git a/examples/app_grid.rs b/examples/app_grid.rs index 80d394f..f892d58 100644 --- a/examples/app_grid.rs +++ b/examples/app_grid.rs @@ -95,7 +95,8 @@ impl App { text_align: Alignment::XCenter | Alignment::YCenter, ..Default::default() }; - let protostar = ProtoStar::create_from_desktop_file(parent, position, desktop_file.clone()).ok()?; + let protostar = + ProtoStar::create_from_desktop_file(parent, position, desktop_file.clone()).ok()?; let text = Text::create( protostar.content_parent(), Transform::from_position_rotation( diff --git a/examples/hexagon_launcher.rs b/examples/hexagon_launcher.rs index 0b8331c..7d00192 100644 --- a/examples/hexagon_launcher.rs +++ b/examples/hexagon_launcher.rs @@ -9,9 +9,12 @@ use protostar::{ use stardust_xr_fusion::{ client::{Client, FrameInfo, RootHandler}, core::values::Transform, - drawable::{Alignment, Bounds, Text, TextFit, TextStyle}, + drawable::{Alignment, Bounds, MaterialParameter, Model, ResourceID, Text, TextFit, TextStyle}, + node::NodeError, spatial::Spatial, }; +use stardust_xr_molecules::touch_plane::TouchPlane; +use std::f32::consts::PI; use tween::TweenTime; const APP_SIZE: f32 = 0.065; @@ -34,7 +37,7 @@ const HEX_DIRECTION_VECTORS: [Hex; 6] = [ impl Hex { fn new(q: isize, r: isize, s: isize) -> Self { - Hex { q: q, r: r, s: s } + Hex { q, r, s } } fn get_coords(&self) -> [f32; 3] { @@ -78,9 +81,11 @@ async fn main() -> Result<()> { struct AppHexGrid { apps: Vec, + button: Button, } impl AppHexGrid { fn new(client: &Client) -> Self { + let button = Button::new(client).unwrap(); let mut desktop_files: Vec = get_desktop_files() .into_iter() .filter_map(|d| parse_desktop_file(d).ok()) @@ -114,11 +119,29 @@ impl AppHexGrid { } radius += 1; } - AppHexGrid { apps } + AppHexGrid { apps, button } } } impl RootHandler for AppHexGrid { fn frame(&mut self, info: FrameInfo) { + self.button.touch_plane.update(); + if self.button.touch_plane.touch_started() { + dbg!("Touch started"); + let color = [0.0, 1.0, 0.0, 1.0]; + self.button + .model + .set_material_parameter(1, "color", MaterialParameter::Color(color)) + .unwrap(); + for app in &mut self.apps { + app.protostar.toggle(); + } + } else if self.button.touch_plane.touch_stopped() { + let color = [0.0, 0.0, 1.0, 1.0]; + self.button + .model + .set_material_parameter(1, "color", MaterialParameter::Color(color)) + .unwrap(); + } for app in &mut self.apps { app.frame(info); } @@ -147,7 +170,8 @@ impl App { text_align: Alignment::XCenter | Alignment::YCenter, ..Default::default() }; - let protostar = ProtoStar::create_from_desktop_file(parent, position, desktop_file.clone()).ok()?; + let protostar = + ProtoStar::create_from_desktop_file(parent, position, desktop_file.clone()).ok()?; let text = Text::create( protostar.content_parent(), Transform::from_position_rotation([0.0, 0.0, 0.004], Quat::from_rotation_y(3.14)), @@ -167,3 +191,33 @@ impl RootHandler for App { self.protostar.frame(info); } } + +struct Button { + touch_plane: TouchPlane, + model: Model, +} +impl Button { + fn new(client: &Client) -> Result { + let touch_plane = TouchPlane::new( + client.get_root(), + Transform::default(), + [APP_SIZE / 2.0, APP_SIZE / 2.0], + APP_SIZE / 2.0, + )?; + let model = Model::create( + client.get_root(), + Transform::from_rotation_scale( + Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI), + [0.03, 0.03, 0.03], + ), + &ResourceID::new_namespaced("protostar", "hexagon/hexagon"), + )?; + model + .set_material_parameter(1, "color", MaterialParameter::Color([0.0, 0.0, 1.0, 1.0])) + .unwrap(); + Ok(Button { touch_plane, model }) + } +} +impl RootHandler for Button { + fn frame(&mut self, _info: FrameInfo) {} +} diff --git a/src/main.rs b/src/main.rs index 6e00034..f59b05a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,11 +38,11 @@ async fn main() -> Result<()> { let protostar = if let Some(desktop_file) = args.desktop_file { ProtoStar::create_from_desktop_file( client.get_root(), - [0.0,0.0,0.0], + [0.0, 0.0, 0.0], parse_desktop_file(desktop_file).map_err(|e| Report::msg(e))?, )? } else if let Some(command) = args.command { - ProtoStar::new_raw(client.get_root(), [0.0,0.0,0.0],None, command)? + ProtoStar::new_raw(client.get_root(), [0.0, 0.0, 0.0], None, command)? } else { bail!("No command or desktop file, nothing to launch."); }; diff --git a/src/protostar.rs b/src/protostar.rs index e73d2be..8baedec 100644 --- a/src/protostar.rs +++ b/src/protostar.rs @@ -4,13 +4,13 @@ use glam::Quat; use mint::Vector3; use nix::unistd::setsid; use stardust_xr_fusion::{ - client::{Client, FrameInfo, RootHandler}, - core::values::Transform, - drawable::{MaterialParameter, Model, ResourceID}, - fields::BoxField, - node::NodeType, - spatial::Spatial, - startup_settings::StartupSettings, + client::{Client, FrameInfo, RootHandler}, + core::values::Transform, + drawable::{MaterialParameter, Model, ResourceID}, + fields::BoxField, + node::NodeType, + spatial::Spatial, + startup_settings::StartupSettings, }; use stardust_xr_molecules::{GrabData, Grabbable}; use std::os::unix::process::CommandExt; @@ -58,12 +58,17 @@ pub struct ProtoStar { grabbable: Grabbable, field: BoxField, icon: Model, - icon_shrink: Option>, - icon_grow: Option>, + grabbable_shrink: Option>, + grabbable_grow: Option>, execute_command: String, + currently_shown: bool, } impl ProtoStar { - pub fn create_from_desktop_file(parent: &Spatial, position: impl Into>, desktop_file: DesktopFile) -> Result { + pub fn create_from_desktop_file( + parent: &Spatial, + position: impl Into>, + desktop_file: DesktopFile, + ) -> Result { // dbg!(&desktop_file); let raw_icons = desktop_file.get_raw_icons(); let mut icon = raw_icons @@ -92,7 +97,12 @@ impl ProtoStar { desktop_file.command.ok_or_else(|| eyre!("No command"))?, ) } - pub fn new_raw(parent: &Spatial, position: impl Into>, icon: Option, execute_command: String) -> Result { + pub fn new_raw( + parent: &Spatial, + position: impl Into>, + icon: Option, + execute_command: String, + ) -> Result { let position = position.into(); let field = BoxField::create( parent, @@ -130,69 +140,89 @@ impl ProtoStar { grabbable, field, icon, - icon_shrink: None, - icon_grow: None, + grabbable_shrink: None, + grabbable_grow: None, execute_command, + currently_shown: true, }) } pub fn content_parent(&self) -> &Spatial { self.grabbable.content_parent() } + pub fn toggle(&mut self) { + if self.currently_shown { + self.grabbable_shrink = Some(Tweener::quart_in_out(1.0, 0.0001, 0.25)); //TODO make the scale a parameter + } else { + self.grabbable_grow = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25)); + } + self.currently_shown = !self.currently_shown; + } } impl RootHandler for ProtoStar { fn frame(&mut self, info: FrameInfo) { self.grabbable.update(&info); - if let Some(icon_shrink) = &mut self.icon_shrink { - if !icon_shrink.is_finished() { - let scale = icon_shrink.move_by(info.delta); - self.icon + + if let Some(grabbable_shrink) = &mut self.grabbable_shrink { + if !grabbable_shrink.is_finished() { + let scale = grabbable_shrink.move_by(info.delta); + self.grabbable + .content_parent() .set_scale(None, Vector3::from([scale; 3])) .unwrap(); } else { - self.icon_grow = Some(Tweener::quart_in_out(0.0001, 0.03, 0.25)); //TODO make the scale a parameter - self.icon_shrink = None; - self.grabbable.content_parent().set_position(Some(self.client.get_root()) , self.position).unwrap(); - self.grabbable.content_parent().set_rotation(Some(self.client.get_root()), Quat::default()).unwrap(); - self.icon.set_rotation(None, Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI) ).unwrap(); - } - } else if let Some(icon_grow) = &mut self.icon_grow { - if !icon_grow.is_finished() { - let scale = icon_grow.move_by(info.delta); + if self.currently_shown { + self.grabbable_grow = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25)); //TODO make the scale a parameter + } + self.grabbable_shrink = None; + self.grabbable + .content_parent() + .set_position(Some(self.client.get_root()), self.position) + .unwrap(); + self.grabbable + .content_parent() + .set_rotation(Some(self.client.get_root()), Quat::default()) + .unwrap(); self.icon + .set_rotation( + None, + Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI), + ) + .unwrap(); + } + } else if let Some(grabbable_grow) = &mut self.grabbable_grow { + if !grabbable_grow.is_finished() { + let scale = grabbable_grow.move_by(info.delta); + self.grabbable + .content_parent() .set_scale(None, Vector3::from([scale; 3])) .unwrap(); } else { - self.icon_grow = None; + self.grabbable_grow = None; } - }else if self.grabbable.grab_action().actor_stopped() { + } 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::quart_in_out(0.03, 0.0001, 0.25)); //TODO make the scale a parameter - let distance_future = self.grabbable.content_parent().get_position_rotation_scale(self.client.get_root()).unwrap(); - + self.grabbable_shrink = Some(Tweener::quart_in_out(0.03, 0.0001, 0.25)); //TODO make the scale a parameter + let distance_future = self + .grabbable + .content_parent() + .get_position_rotation_scale(self.client.get_root()) + .unwrap(); + let executable = dbg!(self.execute_command.clone()); //TODO: split the executable string for the args tokio::task::spawn(async move { let distance_vector = distance_future.await.ok().unwrap().0; - let distance = distance_vector.x.abs() + distance_vector.y.abs() + distance_vector.z.abs(); - if dbg!(distance) > 1.0 { + let distance = + distance_vector.x.abs() + distance_vector.y.abs() + distance_vector.z.abs(); + if dbg!(distance) > 1.0 { let future = startup_settings.generate_startup_token().unwrap(); - + std::env::set_var("STARDUST_STARTUP_TOKEN", future.await.unwrap()); - + unsafe { Command::new(executable) .stdin(Stdio::null())