feat: upgrade hexagon_launcher with thinner outlines and better colors

This commit is contained in:
Nova
2023-11-16 08:42:59 -05:00
parent 6e322aaf1d
commit e6d51a36a8
16 changed files with 367 additions and 306 deletions

36
Cargo.lock generated
View File

@@ -141,6 +141,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"clap", "clap",
"color-eyre", "color-eyre",
"color-rs",
"glam", "glam",
"manifest-dir-macros", "manifest-dir-macros",
"mint", "mint",
@@ -702,7 +703,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e31b487427ff5df1386fa4721fcf41923140eb72fc20d776ac95ccbc21fa7bbb" checksum = "e31b487427ff5df1386fa4721fcf41923140eb72fc20d776ac95ccbc21fa7bbb"
dependencies = [ dependencies = [
"dirs 4.0.0", "dirs 4.0.0",
"itertools", "itertools 0.11.0",
"once_cell", "once_cell",
"rust-ini 0.18.0", "rust-ini 0.18.0",
"thiserror", "thiserror",
@@ -849,6 +850,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"clap", "clap",
"color-eyre", "color-eyre",
"color-rs",
"glam", "glam",
"manifest-dir-macros", "manifest-dir-macros",
"mint", "mint",
@@ -960,6 +962,15 @@ dependencies = [
"either", "either",
] ]
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.9" version = "1.0.9"
@@ -1337,7 +1348,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cad0c4b129e9696e37cb712b243777b90ef489a0bfaa0ac34e7d9b860e4f134" checksum = "8cad0c4b129e9696e37cb712b243777b90ef489a0bfaa0ac34e7d9b860e4f134"
dependencies = [ dependencies = [
"heck", "heck",
"itertools", "itertools 0.11.0",
"proc-macro-error", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1516,7 +1527,7 @@ dependencies = [
"freedesktop-icons-greedy", "freedesktop-icons-greedy",
"glam", "glam",
"image", "image",
"itertools", "itertools 0.12.0",
"lazy_static", "lazy_static",
"linicon-theme", "linicon-theme",
"manifest-dir-macros", "manifest-dir-macros",
@@ -1920,9 +1931,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_with" name = "serde_with"
version = "3.3.0" version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ca3b16a3d82c4088f343b7480a93550b3eabe1a358569c2dfe38bbcead07237" checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23"
dependencies = [ dependencies = [
"base64", "base64",
"chrono", "chrono",
@@ -1937,9 +1948,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_with_macros" name = "serde_with_macros"
version = "3.3.0" version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e6be15c453eb305019bfa438b1593c731f36a289a7853f7707ee29e870b3b3c" checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788"
dependencies = [ dependencies = [
"darling", "darling",
"proc-macro2", "proc-macro2",
@@ -1986,6 +1997,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"clap", "clap",
"color-eyre", "color-eyre",
"color-rs",
"glam", "glam",
"manifest-dir-macros", "manifest-dir-macros",
"mint", "mint",
@@ -2009,6 +2021,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"clap", "clap",
"color-eyre", "color-eyre",
"color-rs",
"glam", "glam",
"manifest-dir-macros", "manifest-dir-macros",
"mint", "mint",
@@ -2049,7 +2062,7 @@ dependencies = [
[[package]] [[package]]
name = "stardust-xr" name = "stardust-xr"
version = "0.14.1" version = "0.14.1"
source = "git+https://github.com/StardustXR/core.git#7b6c7b1b77075a7035f8a3ef07b430863ed9d60e" source = "git+https://github.com/StardustXR/core.git#975331f9739a9f6cb90ec75813b7720c86225848"
dependencies = [ dependencies = [
"cluFlock", "cluFlock",
"color-rs", "color-rs",
@@ -2069,7 +2082,7 @@ dependencies = [
[[package]] [[package]]
name = "stardust-xr-fusion" name = "stardust-xr-fusion"
version = "0.43.2" version = "0.43.2"
source = "git+https://github.com/StardustXR/core.git#7b6c7b1b77075a7035f8a3ef07b430863ed9d60e" source = "git+https://github.com/StardustXR/core.git#975331f9739a9f6cb90ec75813b7720c86225848"
dependencies = [ dependencies = [
"color-eyre", "color-eyre",
"color-rs", "color-rs",
@@ -2081,6 +2094,7 @@ dependencies = [
"rustc-hash", "rustc-hash",
"serde", "serde",
"serde_repr", "serde_repr",
"serde_with",
"stardust-xr", "stardust-xr",
"thiserror", "thiserror",
"tokio", "tokio",
@@ -2090,7 +2104,7 @@ dependencies = [
[[package]] [[package]]
name = "stardust-xr-molecules" name = "stardust-xr-molecules"
version = "0.29.0" version = "0.29.0"
source = "git+https://github.com/StardustXR/molecules.git#6e389f837375a34a10dfd35a633072a77a9f14d8" source = "git+https://github.com/StardustXR/molecules.git#2e60ed7f75a1a79d89b83237dea4d208bc1ab01c"
dependencies = [ dependencies = [
"color-rs", "color-rs",
"glam", "glam",
@@ -2109,7 +2123,7 @@ dependencies = [
[[package]] [[package]]
name = "stardust-xr-schemas" name = "stardust-xr-schemas"
version = "1.5.3" version = "1.5.3"
source = "git+https://github.com/StardustXR/core.git#7b6c7b1b77075a7035f8a3ef07b430863ed9d60e" source = "git+https://github.com/StardustXR/core.git#975331f9739a9f6cb90ec75813b7720c86225848"
dependencies = [ dependencies = [
"flatbuffers", "flatbuffers",
"flexbuffers", "flexbuffers",

View File

@@ -7,6 +7,7 @@ edition = "2021"
tokio = { version = "1.32.0", features = ["rt", "tokio-macros", "sync"] } tokio = { version = "1.32.0", features = ["rt", "tokio-macros", "sync"] }
protostar = { path = "../protostar" } protostar = { path = "../protostar" }
color-eyre = "0.6.2" color-eyre = "0.6.2"
color-rs = "0.8.0"
clap = "4.4.6" clap = "4.4.6"
manifest-dir-macros = "0.1.18" manifest-dir-macros = "0.1.18"
glam = "0.24.2" glam = "0.24.2"

View File

@@ -1,3 +1,4 @@
use color::rgba_linear;
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use glam::{Quat, Vec3}; use glam::{Quat, Vec3};
use manifest_dir_macros::directory_relative_path; use manifest_dir_macros::directory_relative_path;
@@ -93,9 +94,10 @@ fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result<Model> {
Transform::from_rotation(Quat::from_rotation_y(PI)), Transform::from_rotation(Quat::from_rotation_y(PI)),
&ResourceID::new_namespaced("protostar", "cartridge"), &ResourceID::new_namespaced("protostar", "cartridge"),
)?; )?;
model model.model_part("Cartridge")?.set_material_parameter(
.model_part("Cartridge")? "color",
.set_material_parameter("color", MaterialParameter::Color([0.0, 1.0, 1.0, 1.0]))?; MaterialParameter::Color(rgba_linear!(0.0, 1.0, 1.0, 1.0)),
)?;
model.model_part("Icon")?.set_material_parameter( model.model_part("Icon")?.set_material_parameter(
"diffuse", "diffuse",
MaterialParameter::Texture(ResourceID::Direct(icon.path.clone())), MaterialParameter::Texture(ResourceID::Direct(icon.path.clone())),
@@ -182,12 +184,12 @@ impl App {
self.grabbable.content_parent() self.grabbable.content_parent()
} }
fn bring_back(&self) { // fn bring_back(&self) {
self.grabbable // self.grabbable
.content_parent() // .content_parent()
.set_transform(Some(&self.root), Transform::identity()) // .set_transform(Some(&self.root), Transform::identity())
.unwrap(); // .unwrap();
} // }
fn frame(&mut self, info: FrameInfo) { fn frame(&mut self, info: FrameInfo) {
let _ = self.grabbable.update(&info); let _ = self.grabbable.update(&info);
@@ -196,23 +198,29 @@ impl App {
self.grabbable.cancel_angular_velocity(); self.grabbable.cancel_angular_velocity();
self.grabbable.cancel_linear_velocity(); self.grabbable.cancel_linear_velocity();
if !self.grabbable.valid() { // if !self.grabbable.valid() {
self.bring_back(); // self.bring_back();
return; // return;
} // }
let Ok(distance_future) = self.grabbable let Ok(distance_future) = self
.grabbable
.content_parent() .content_parent()
.get_position_rotation_scale(&self.root) .get_position_rotation_scale(&self.root)
else {return}; else {
return;
};
let application = self.application.clone(); let application = self.application.clone();
let space = self.content_parent().alias(); let space = self.content_parent().alias();
let root = self.root.alias(); let root = self.root.alias();
tokio::task::spawn(async move { tokio::task::spawn(async move {
let Ok((distance, _rotation, _scale)) = distance_future.await else { space let Ok((distance, _rotation, _scale)) = distance_future.await else {
.set_transform(Some(&root), Transform::identity()) space
.unwrap(); return}; .set_transform(Some(&root), Transform::identity())
.unwrap();
return;
};
let distance = Vec3::from(distance).length_squared(); let distance = Vec3::from(distance).length_squared();
if distance > ACTIVATION_DISTANCE.powi(2) { if distance > ACTIVATION_DISTANCE.powi(2) {

Binary file not shown.

Binary file not shown.

View File

@@ -6,6 +6,7 @@ edition = "2021"
[dependencies] [dependencies]
tokio = { version = "1.32.0", features = ["rt", "tokio-macros", "sync"] } tokio = { version = "1.32.0", features = ["rt", "tokio-macros", "sync"] }
protostar = { path = "../protostar" } protostar = { path = "../protostar" }
color-rs = "0.8.0"
color-eyre = "0.6.2" color-eyre = "0.6.2"
clap = "4.4.6" clap = "4.4.6"
manifest-dir-macros = "0.1.18" manifest-dir-macros = "0.1.18"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@@ -1,6 +1,6 @@
{ {
"asset":{ "asset":{
"generator":"Khronos glTF Blender I/O v3.5.30", "generator":"Khronos glTF Blender I/O v3.6.6",
"version":"2.0" "version":"2.0"
}, },
"extensionsUsed":[ "extensionsUsed":[
@@ -51,9 +51,9 @@
"name":"Hex", "name":"Hex",
"pbrMetallicRoughness":{ "pbrMetallicRoughness":{
"baseColorFactor":[ "baseColorFactor":[
1, 0.9720873236656189,
0, 0.018567396327853203,
0, 0.017742767930030823,
1 1
], ],
"baseColorTexture":{ "baseColorTexture":{
@@ -173,14 +173,14 @@
"componentType":5126, "componentType":5126,
"count":54, "count":54,
"max":[ "max":[
1.0441828966140747, 1.0166369676589966,
0.05000000074505806, 0.021040409803390503,
0.9042890667915344 0.8804334998130798
], ],
"min":[ "min":[
-1.0441828966140747, -1.0166369676589966,
0, -7.071532309055328e-06,
-0.9042890667915344 -0.8804334998130798
], ],
"type":"VEC3" "type":"VEC3"
}, },

256
hexagon_launcher/src/app.rs Normal file
View File

@@ -0,0 +1,256 @@
use color_eyre::eyre::Result;
use glam::{EulerRot, Quat, Vec3};
use mint::Vector3;
use protostar::{
application::Application,
xdg::{DesktopFile, Icon, IconType},
};
use stardust_xr_fusion::{
client::FrameInfo,
core::values::Transform,
drawable::{Alignment, Bounds, MaterialParameter, Model, ResourceID, Text, TextFit, TextStyle},
fields::BoxField,
node::NodeType,
spatial::Spatial,
};
use stardust_xr_molecules::{Grabbable, GrabbableSettings};
use std::f32::consts::PI;
use tween::{QuartInOut, Tweener};
use crate::{ACTIVATION_DISTANCE, APP_SIZE, DEFAULT_HEX_COLOR};
// Model handling
fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result<Model> {
match &icon.icon_type {
IconType::Png => {
let t = Transform::from_rotation_scale(
Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI),
[APP_SIZE / 2.0; 3],
);
let model = Model::create(
parent,
t,
&ResourceID::new_namespaced("protostar", "hexagon/hexagon"),
)?;
model
.model_part("Hex")?
.set_material_parameter("color", MaterialParameter::Color(DEFAULT_HEX_COLOR))?;
model.model_part("Icon")?.set_material_parameter(
"diffuse",
MaterialParameter::Texture(ResourceID::Direct(icon.path.clone())),
)?;
Ok(model)
}
IconType::Gltf => Ok(Model::create(
parent,
Transform::from_scale([0.05; 3]),
&ResourceID::new_direct(icon.path.clone())?,
)?),
_ => panic!("Invalid Icon Type"),
}
}
pub struct App {
application: Application,
parent: Spatial,
position: Vector3<f32>,
grabbable: Grabbable,
_field: BoxField,
icon: Model,
label: Option<Text>,
grabbable_shrink: Option<Tweener<f32, f64, QuartInOut>>,
grabbable_grow: Option<Tweener<f32, f64, QuartInOut>>,
grabbable_move: Option<Tweener<f32, f64, QuartInOut>>,
currently_shown: bool,
}
impl App {
pub fn create_from_desktop_file(
parent: &Spatial,
position: impl Into<Vector3<f32>>,
desktop_file: DesktopFile,
) -> Result<Self> {
let position = position.into();
let field = BoxField::create(parent, Transform::default(), [APP_SIZE; 3])?;
let application = Application::create(desktop_file)?;
let icon = application.icon(128, false);
let grabbable = Grabbable::create(
parent,
Transform::from_position(position),
&field,
GrabbableSettings {
max_distance: 0.01,
..Default::default()
},
)?;
grabbable.content_parent().set_spatial_parent(parent)?;
field.set_spatial_parent(grabbable.content_parent())?;
let icon = icon
.map(|i| model_from_icon(grabbable.content_parent(), &i))
.unwrap_or_else(|| {
Ok(Model::create(
grabbable.content_parent(),
Transform::from_rotation_scale(
Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI),
[APP_SIZE * 0.5; 3],
),
&ResourceID::new_namespaced("protostar", "hexagon/hexagon"),
)?)
})?;
let label_style = TextStyle {
character_height: APP_SIZE * 2.0,
bounds: Some(Bounds {
bounds: [1.0; 2].into(),
fit: TextFit::Wrap,
bounds_align: Alignment::XCenter | Alignment::YCenter,
}),
text_align: Alignment::Center.into(),
..Default::default()
};
let label = application.name().and_then(|name| {
Text::create(
&icon,
Transform::from_position_rotation(
[0.0, 0.1, -(APP_SIZE * 4.0)],
Quat::from_rotation_x(PI * 0.5),
),
name,
label_style,
)
.ok()
});
Ok(App {
parent: parent.alias(),
position,
grabbable,
_field: field,
label,
application,
icon,
grabbable_shrink: None,
grabbable_grow: None,
grabbable_move: None,
currently_shown: true,
})
}
pub fn content_parent(&self) -> &Spatial {
self.grabbable.content_parent()
}
pub fn toggle(&mut self) {
self.grabbable.set_enabled(!self.currently_shown).unwrap();
if self.currently_shown {
self.grabbable_move = Some(Tweener::quart_in_out(1.0, 0.0001, 0.25)); //TODO make the scale a parameter
} else {
self.icon.set_enabled(true).unwrap();
if let Some(label) = self.label.as_ref() {
label.set_enabled(true).unwrap()
}
self.grabbable_move = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25));
}
self.currently_shown = !self.currently_shown;
}
pub fn frame(&mut self, info: FrameInfo) {
let _ = self.grabbable.update(&info);
if let Some(grabbable_move) = &mut self.grabbable_move {
if !grabbable_move.is_finished() {
let scale = grabbable_move.move_by(info.delta);
self.grabbable
.content_parent()
.set_position(Some(&self.parent), Vec3::from(self.position) * scale)
.unwrap();
} else {
if grabbable_move.final_value() == 0.0001 {
self.icon.set_enabled(false).unwrap();
if let Some(label) = self.label.as_ref() {
label.set_enabled(false).unwrap()
}
}
self.grabbable_move = None;
}
}
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(Some(&self.parent), Vector3::from([scale; 3]))
.unwrap();
} else {
self.grabbable
.content_parent()
.set_spatial_parent(&self.parent)
.unwrap();
if self.currently_shown {
self.grabbable_grow = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25));
self.grabbable.cancel_angular_velocity();
self.grabbable.cancel_linear_velocity();
}
self.grabbable_shrink = None;
self.grabbable
.content_parent()
.set_position(Some(&self.parent), self.position)
.unwrap();
self.grabbable
.content_parent()
.set_rotation(Some(&self.parent), 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(Some(&self.parent), Vector3::from([scale; 3]))
.unwrap();
} else {
self.grabbable
.content_parent()
.set_spatial_parent(&self.parent)
.unwrap();
self.grabbable_grow = None;
}
} else if self.grabbable.grab_action().actor_stopped() {
self.grabbable_shrink = Some(Tweener::quart_in_out(APP_SIZE * 0.5, 0.0001, 0.25));
let Ok(distance_future) = self
.grabbable
.content_parent()
.get_position_rotation_scale(&self.parent)
else {
return;
};
let application = self.application.clone();
let space = self.content_parent().alias();
//TODO: split the executable string for the args
tokio::task::spawn(async move {
let distance_vector = distance_future.await.ok().unwrap().0;
let distance = Vec3::from(distance_vector).length_squared();
if distance > ACTIVATION_DISTANCE {
let client = space.node().client().unwrap();
let (_, space_rot, _) = space
.get_position_rotation_scale(&client.get_root())
.unwrap()
.await
.unwrap();
let (_, y_rot, _) = Quat::from(space_rot).to_euler(EulerRot::XYZ);
let _ = space.set_transform(
Some(client.get_root()),
Transform::from_rotation_scale(Quat::from_rotation_y(y_rot), [1.0; 3]),
);
let _ = application.launch(&space);
}
});
}
}
}

View File

@@ -1,35 +1,31 @@
pub mod app;
pub mod hex; pub mod hex;
use app::App;
use color::{color_space::LinearRgb, rgba_linear, Rgba};
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use glam::{EulerRot, Quat, Vec3}; use glam::Quat;
use hex::{HEX_CENTER, HEX_DIRECTION_VECTORS}; use hex::{HEX_CENTER, HEX_DIRECTION_VECTORS};
use manifest_dir_macros::directory_relative_path; use manifest_dir_macros::directory_relative_path;
use mint::Vector3; use protostar::xdg::{get_desktop_files, parse_desktop_file, DesktopFile};
use protostar::{
application::Application,
xdg::{get_desktop_files, parse_desktop_file, DesktopFile, Icon, IconType},
};
use stardust_xr_fusion::{ use stardust_xr_fusion::{
client::{Client, ClientState, FrameInfo, RootHandler}, client::{Client, ClientState, FrameInfo, RootHandler},
core::values::Transform, core::values::Transform,
drawable::{Alignment, Bounds, MaterialParameter, Model, ResourceID, Text, TextFit, TextStyle}, drawable::{MaterialParameter, Model, ResourceID},
fields::BoxField, fields::BoxField,
node::NodeError, node::NodeError,
node::NodeType,
spatial::Spatial,
}; };
use stardust_xr_molecules::{touch_plane::TouchPlane, Grabbable, GrabbableSettings}; use stardust_xr_molecules::{touch_plane::TouchPlane, Grabbable, GrabbableSettings, PointerMode};
use std::f32::consts::PI; use std::f32::consts::PI;
use tween::{QuartInOut, Tweener};
const APP_SIZE: f32 = 0.06; const APP_SIZE: f32 = 0.06;
const PADDING: f32 = 0.005; const PADDING: f32 = 0.005;
const MODEL_SCALE: f32 = 0.03; const MODEL_SCALE: f32 = 0.03;
const ACTIVATION_DISTANCE: f32 = 0.05; const ACTIVATION_DISTANCE: f32 = 0.05;
const CYAN: [f32; 4] = [0.0, 1.0, 1.0, 1.0]; const DEFAULT_HEX_COLOR: Rgba<f32, LinearRgb> = rgba_linear!(0.211, 0.937, 0.588, 1.0);
const BTN_SELECTED_COLOR: [f32; 4] = [0.0, 1.0, 0.0, 1.0]; const BTN_SELECTED_COLOR: Rgba<f32, LinearRgb> = rgba_linear!(0.0, 1.0, 0.0, 1.0);
const BTN_COLOR: [f32; 4] = [1.0, 1.0, 0.0, 1.0]; const BTN_COLOR: Rgba<f32, LinearRgb> = rgba_linear!(1.0, 1.0, 0.0, 1.0);
#[tokio::main(flavor = "current_thread")] #[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> { async fn main() -> Result<()> {
@@ -137,6 +133,8 @@ impl Button {
&field, &field,
GrabbableSettings { GrabbableSettings {
max_distance: 0.01, max_distance: 0.01,
pointer_mode: PointerMode::Align,
magnet: false,
..Default::default() ..Default::default()
}, },
)?; )?;
@@ -179,236 +177,3 @@ impl Button {
self.touch_plane.update(); self.touch_plane.update();
} }
} }
// Model handling
fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result<Model> {
match &icon.icon_type {
IconType::Png => {
let t = Transform::from_rotation_scale(
Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI),
[APP_SIZE / 2.0; 3],
);
let model = Model::create(
parent,
t,
&ResourceID::new_namespaced("protostar", "hexagon/hexagon"),
)?;
model
.model_part("Hex")?
.set_material_parameter("color", MaterialParameter::Color(CYAN))?;
model.model_part("Icon")?.set_material_parameter(
"diffuse",
MaterialParameter::Texture(ResourceID::Direct(icon.path.clone())),
)?;
Ok(model)
}
IconType::Gltf => Ok(Model::create(
parent,
Transform::from_scale([0.05; 3]),
&ResourceID::new_direct(icon.path.clone())?,
)?),
_ => panic!("Invalid Icon Type"),
}
}
pub struct App {
application: Application,
parent: Spatial,
position: Vector3<f32>,
grabbable: Grabbable,
_field: BoxField,
icon: Model,
label: Option<Text>,
grabbable_shrink: Option<Tweener<f32, f64, QuartInOut>>,
grabbable_grow: Option<Tweener<f32, f64, QuartInOut>>,
grabbable_move: Option<Tweener<f32, f64, QuartInOut>>,
currently_shown: bool,
}
impl App {
pub fn create_from_desktop_file(
parent: &Spatial,
position: impl Into<Vector3<f32>>,
desktop_file: DesktopFile,
) -> Result<Self> {
let position = position.into();
let field = BoxField::create(parent, Transform::default(), [APP_SIZE; 3])?;
let application = Application::create(desktop_file)?;
let icon = application.icon(128, false);
let grabbable = Grabbable::create(
parent,
Transform::from_position(position),
&field,
GrabbableSettings {
max_distance: 0.01,
..Default::default()
},
)?;
grabbable.content_parent().set_spatial_parent(parent)?;
field.set_spatial_parent(grabbable.content_parent())?;
let icon = icon
.map(|i| model_from_icon(grabbable.content_parent(), &i))
.unwrap_or_else(|| {
Ok(Model::create(
grabbable.content_parent(),
Transform::from_rotation_scale(
Quat::from_rotation_x(PI / 2.0) * Quat::from_rotation_y(PI),
[APP_SIZE * 0.5; 3],
),
&ResourceID::new_namespaced("protostar", "hexagon/hexagon"),
)?)
})?;
let label_style = TextStyle {
character_height: APP_SIZE * 2.0,
bounds: Some(Bounds {
bounds: [1.0; 2].into(),
fit: TextFit::Wrap,
bounds_align: Alignment::XCenter | Alignment::YCenter,
}),
text_align: Alignment::Center.into(),
..Default::default()
};
let label = application.name().and_then(|name| {
Text::create(
&icon,
Transform::from_position_rotation(
[0.0, 0.1, -(APP_SIZE * 4.0)],
Quat::from_rotation_x(PI * 0.5),
),
name,
label_style,
)
.ok()
});
Ok(App {
parent: parent.alias(),
position,
grabbable,
_field: field,
label,
application,
icon,
grabbable_shrink: None,
grabbable_grow: None,
grabbable_move: None,
currently_shown: true,
})
}
pub fn content_parent(&self) -> &Spatial {
self.grabbable.content_parent()
}
pub fn toggle(&mut self) {
self.grabbable.set_enabled(!self.currently_shown).unwrap();
if self.currently_shown {
self.grabbable_move = Some(Tweener::quart_in_out(1.0, 0.0001, 0.25)); //TODO make the scale a parameter
} else {
self.icon.set_enabled(true).unwrap();
if let Some(label) = self.label.as_ref() {
label.set_enabled(true).unwrap()
}
self.grabbable_move = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25));
}
self.currently_shown = !self.currently_shown;
}
fn frame(&mut self, info: FrameInfo) {
let _ = self.grabbable.update(&info);
if let Some(grabbable_move) = &mut self.grabbable_move {
if !grabbable_move.is_finished() {
let scale = grabbable_move.move_by(info.delta);
self.grabbable
.content_parent()
.set_position(Some(&self.parent), Vec3::from(self.position) * scale)
.unwrap();
} else {
if grabbable_move.final_value() == 0.0001 {
self.icon.set_enabled(false).unwrap();
if let Some(label) = self.label.as_ref() {
label.set_enabled(false).unwrap()
}
}
self.grabbable_move = None;
}
}
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(Some(&self.parent), Vector3::from([scale; 3]))
.unwrap();
} else {
self.grabbable
.content_parent()
.set_spatial_parent(&self.parent)
.unwrap();
if self.currently_shown {
self.grabbable_grow = Some(Tweener::quart_in_out(0.0001, 1.0, 0.25));
self.grabbable.cancel_angular_velocity();
self.grabbable.cancel_linear_velocity();
}
self.grabbable_shrink = None;
self.grabbable
.content_parent()
.set_position(Some(&self.parent), self.position)
.unwrap();
self.grabbable
.content_parent()
.set_rotation(Some(&self.parent), 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(Some(&self.parent), Vector3::from([scale; 3]))
.unwrap();
} else {
self.grabbable
.content_parent()
.set_spatial_parent(&self.parent)
.unwrap();
self.grabbable_grow = None;
}
} else if self.grabbable.valid() && self.grabbable.grab_action().actor_stopped() {
self.grabbable_shrink = Some(Tweener::quart_in_out(APP_SIZE * 0.5, 0.0001, 0.25));
let Ok(distance_future) = self.grabbable
.content_parent()
.get_position_rotation_scale(&self.parent)
else {return};
let application = self.application.clone();
let space = self.content_parent().alias();
//TODO: split the executable string for the args
tokio::task::spawn(async move {
let distance_vector = distance_future.await.ok().unwrap().0;
let distance = Vec3::from(distance_vector).length_squared();
if distance > ACTIVATION_DISTANCE {
let client = space.node().client().unwrap();
let (_, space_rot, _) = space
.get_position_rotation_scale(&client.get_root())
.unwrap()
.await
.unwrap();
let (_, y_rot, _) = Quat::from(space_rot).to_euler(EulerRot::XYZ);
let _ = space.set_transform(
Some(client.get_root()),
Transform::from_rotation_scale(Quat::from_rotation_y(y_rot), [1.0; 3]),
);
let _ = application.launch(&space);
}
});
}
}
}

View File

@@ -12,7 +12,7 @@ ez-pixmap = "0.2.2"
freedesktop-icons-greedy = "0.2.6" freedesktop-icons-greedy = "0.2.6"
glam = { version = "0.24.0", features = ["mint"] } glam = { version = "0.24.0", features = ["mint"] }
image = "0.24.5" image = "0.24.5"
itertools = "0.11.0" itertools = "0.12.0"
lazy_static = "1.4.0" lazy_static = "1.4.0"
linicon-theme = "1.2.0" linicon-theme = "1.2.0"
manifest-dir-macros = "0.1.16" manifest-dir-macros = "0.1.16"
@@ -22,7 +22,7 @@ regex = "1.7.1"
resvg = "0.29.0" resvg = "0.29.0"
rustc-hash = "1.1.0" rustc-hash = "1.1.0"
serde = "1.0.155" serde = "1.0.155"
serde_with = "3.3.0" serde_with = "3.4.0"
tokio = { version = "1.24.1", features = ["full"] } tokio = { version = "1.24.1", features = ["full"] }
toml = "0.8.2" toml = "0.8.2"
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }

View File

@@ -7,6 +7,7 @@ edition = "2021"
tokio = { version = "1.32.0", features = ["rt", "tokio-macros", "sync"] } tokio = { version = "1.32.0", features = ["rt", "tokio-macros", "sync"] }
protostar = { path = "../protostar" } protostar = { path = "../protostar" }
color-eyre = "0.6.2" color-eyre = "0.6.2"
color-rs = "0.8.0"
clap = "4.4.6" clap = "4.4.6"
manifest-dir-macros = "0.1.18" manifest-dir-macros = "0.1.18"
glam = "0.24.2" glam = "0.24.2"

View File

@@ -1,3 +1,4 @@
use color::rgba_linear;
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use glam::{Quat, Vec3}; use glam::{Quat, Vec3};
use mint::Vector3; use mint::Vector3;
@@ -33,9 +34,10 @@ fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result<Model> {
t, t,
&ResourceID::new_namespaced("protostar", "hexagon/hexagon"), &ResourceID::new_namespaced("protostar", "hexagon/hexagon"),
)?; )?;
model model.model_part("Hex")?.set_material_parameter(
.model_part("Hex")? "color",
.set_material_parameter("color", MaterialParameter::Color([0.0, 1.0, 1.0, 1.0]))?; MaterialParameter::Color(rgba_linear!(0.0, 1.0, 1.0, 1.0)),
)?;
model.model_part("Icon")?.set_material_parameter( model.model_part("Icon")?.set_material_parameter(
"diffuse", "diffuse",
MaterialParameter::Texture(ResourceID::Direct(icon.path.clone())), MaterialParameter::Texture(ResourceID::Direct(icon.path.clone())),
@@ -211,12 +213,15 @@ impl RootHandler for Single {
.unwrap(); .unwrap();
self.grabbable_grow = None; self.grabbable_grow = None;
} }
} else if self.grabbable.valid() && self.grabbable.grab_action().actor_stopped() { } else if self.grabbable.grab_action().actor_stopped() {
self.grabbable_shrink = Some(Tweener::quart_in_out(MODEL_SCALE, 0.0001, 0.25)); self.grabbable_shrink = Some(Tweener::quart_in_out(MODEL_SCALE, 0.0001, 0.25));
let Ok(distance_future) = self.grabbable let Ok(distance_future) = self
.grabbable
.content_parent() .content_parent()
.get_position_rotation_scale(&self.parent) .get_position_rotation_scale(&self.parent)
else {return}; else {
return;
};
let application = self.application.clone(); let application = self.application.clone();
let space = self.content_parent().alias(); let space = self.content_parent().alias();

View File

@@ -7,6 +7,7 @@ edition = "2021"
tokio = { version = "1.32.0", features = ["rt", "tokio-macros", "sync"] } tokio = { version = "1.32.0", features = ["rt", "tokio-macros", "sync"] }
protostar = { path = "../protostar" } protostar = { path = "../protostar" }
color-eyre = "0.6.2" color-eyre = "0.6.2"
color-rs = "0.8.0"
clap = "4.4.6" clap = "4.4.6"
manifest-dir-macros = "0.1.18" manifest-dir-macros = "0.1.18"
glam = "0.24.2" glam = "0.24.2"

View File

@@ -1,4 +1,5 @@
use clap::{self, Parser}; use clap::{self, Parser};
use color::rgba_linear;
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use glam::{Quat, Vec3}; use glam::{Quat, Vec3};
use manifest_dir_macros::directory_relative_path; use manifest_dir_macros::directory_relative_path;
@@ -164,36 +165,40 @@ impl RootHandler for Sirius {
} }
} }
} }
let color = [0.0, 1.0, 0.0, 1.0];
self.model self.model
.model_part("?????") .model_part("?????")
.unwrap() .unwrap()
.set_material_parameter("color", MaterialParameter::Color(color)) .set_material_parameter(
"color",
MaterialParameter::Color(rgba_linear!(0.0, 1.0, 0.0, 1.0)),
)
.unwrap(); .unwrap();
self.model self.model
.model_part("?????") .model_part("?????")
.unwrap() .unwrap()
.set_material_parameter( .set_material_parameter(
"emission_factor", "emission_factor",
MaterialParameter::Color(color.map(|c| c * 0.75)), MaterialParameter::Color(rgba_linear!(0.0, 0.75, 0.0, 0.75)),
) )
.unwrap(); .unwrap();
} }
if self.touch_plane.touch_stopped() { if self.touch_plane.touch_stopped() {
println!("Touch ended"); println!("Touch ended");
let color = [1.0, 0.0, 0.0, 1.0];
self.model self.model
.model_part("?????") .model_part("?????")
.unwrap() .unwrap()
.set_material_parameter("color", MaterialParameter::Color(color)) .set_material_parameter(
"color",
MaterialParameter::Color(rgba_linear!(1.0, 0.0, 0.0, 1.0)),
)
.unwrap(); .unwrap();
self.model self.model
.model_part("?????") .model_part("?????")
.unwrap() .unwrap()
.set_material_parameter( .set_material_parameter(
"emission_factor", "emission_factor",
MaterialParameter::Color(color.map(|c| c * 0.5)), MaterialParameter::Color(rgba_linear!(0.5, 0.0, 0.0, 0.5)),
) )
.unwrap(); .unwrap();
} }
@@ -217,9 +222,10 @@ fn model_from_icon(parent: &Spatial, icon: &Icon) -> Result<Model> {
t, t,
&ResourceID::new_namespaced("protostar", "hexagon/hexagon"), &ResourceID::new_namespaced("protostar", "hexagon/hexagon"),
)?; )?;
model model.model_part("Hex")?.set_material_parameter(
.model_part("Hex")? "color",
.set_material_parameter("color", MaterialParameter::Color([0.0, 1.0, 1.0, 1.0]))?; MaterialParameter::Color(rgba_linear!(0.0, 1.0, 1.0, 1.0)),
)?;
model.model_part("Icon")?.set_material_parameter( model.model_part("Icon")?.set_material_parameter(
"diffuse", "diffuse",
MaterialParameter::Texture(ResourceID::Direct(icon.path.clone())), MaterialParameter::Texture(ResourceID::Direct(icon.path.clone())),
@@ -402,12 +408,15 @@ impl App {
.unwrap(); .unwrap();
self.grabbable_grow = None; self.grabbable_grow = None;
} }
} else if self.grabbable.valid() && self.grabbable.grab_action().actor_stopped() { } else if self.grabbable.grab_action().actor_stopped() {
self.grabbable_shrink = Some(Tweener::quart_in_out(APP_SIZE * 0.5, 0.0001, 0.25)); self.grabbable_shrink = Some(Tweener::quart_in_out(APP_SIZE * 0.5, 0.0001, 0.25));
let Ok(distance_future) = self.grabbable let Ok(distance_future) = self
.grabbable
.content_parent() .content_parent()
.get_position_rotation_scale(&self.parent) .get_position_rotation_scale(&self.parent)
else {return}; else {
return;
};
let application = self.application.clone(); let application = self.application.clone();
let space = self.content_parent().alias(); let space = self.content_parent().alias();