feat: add entity handles and a bevy channel abstraction
Signed-off-by: Schmarni <marnistromer@gmail.com>
This commit is contained in:
32
src/core/bevy_channel.rs
Normal file
32
src/core/bevy_channel.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use tokio::sync::mpsc::{self, error::TryRecvError};
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct BevyChannelReader<T: Send + Sync + 'static>(mpsc::UnboundedReceiver<T>);
|
||||
pub struct BevyChannel<T: Send + Sync + 'static>(OnceLock<mpsc::UnboundedSender<T>>);
|
||||
impl<T: Send + Sync + 'static> BevyChannel<T> {
|
||||
pub const fn new() -> Self {
|
||||
Self(OnceLock::new())
|
||||
}
|
||||
pub fn init(&self, app: &mut App) {
|
||||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
self.0.set(tx).unwrap();
|
||||
app.insert_resource(BevyChannelReader(rx));
|
||||
}
|
||||
pub fn send(&self, msg: T) -> Option<()> {
|
||||
self.0.get()?.send(msg).ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> BevyChannelReader<T> {
|
||||
pub fn read(&mut self) -> Option<T> {
|
||||
match self.0.try_recv() {
|
||||
Ok(v) => Some(v),
|
||||
Err(TryRecvError::Disconnected) => panic!("bevy channel should never disconnect"),
|
||||
Err(TryRecvError::Empty) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
src/core/color.rs
Normal file
12
src/core/color.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
use stardust_xr::values::color::{AlphaColor, Rgb, color_space::LinearRgb};
|
||||
|
||||
pub trait ColorConvert {
|
||||
fn to_bevy(&self) -> bevy::color::Color;
|
||||
}
|
||||
// even tho its supposed to be linear the values have to be interpreted as Srgba to produce the
|
||||
// correct result because StereoKit used Srgba while it was assumed that is uses linear rgba
|
||||
impl ColorConvert for AlphaColor<f32, Rgb<f32, LinearRgb>> {
|
||||
fn to_bevy(&self) -> bevy::color::Color {
|
||||
bevy::color::Color::srgba(self.c.r, self.c.g, self.c.b, self.a)
|
||||
}
|
||||
}
|
||||
33
src/core/entity_handle.rs
Normal file
33
src/core/entity_handle.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
use super::bevy_channel::{BevyChannel, BevyChannelReader};
|
||||
pub struct EntityHandlePlugin;
|
||||
|
||||
impl Plugin for EntityHandlePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
DESTROY.init(app);
|
||||
app.add_systems(PreUpdate, despawn);
|
||||
}
|
||||
}
|
||||
|
||||
fn despawn(mut cmds: Commands, mut reader: ResMut<BevyChannelReader<Entity>>) {
|
||||
while let Some(e) = reader.read() {
|
||||
cmds.entity(e).despawn();
|
||||
}
|
||||
}
|
||||
|
||||
static DESTROY: BevyChannel<Entity> = BevyChannel::new();
|
||||
#[derive(Deref, DerefMut, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct EntityHandle(pub Entity);
|
||||
impl Drop for EntityHandle {
|
||||
fn drop(&mut self) {
|
||||
if DESTROY.send(self.0).is_none() {
|
||||
error!("Entity Destroy channel not open");
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<Entity> for EntityHandle {
|
||||
fn from(value: Entity) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
@@ -8,3 +8,5 @@ pub mod resource;
|
||||
pub mod scenegraph;
|
||||
pub mod task;
|
||||
pub mod color;
|
||||
pub mod entity_handle;
|
||||
pub mod bevy_channel;
|
||||
|
||||
37
src/main.rs
37
src/main.rs
@@ -16,9 +16,6 @@ use bevy::app::{App, TerminalCtrlCHandlerPlugin};
|
||||
use bevy::asset::{AssetMetaCheck, UnapprovedPathMode};
|
||||
use bevy::audio::AudioPlugin;
|
||||
use bevy::core_pipeline::CorePipelinePlugin;
|
||||
use bevy::core_pipeline::oit::{
|
||||
OrderIndependentTransparencyPlugin, OrderIndependentTransparencySettings,
|
||||
};
|
||||
use bevy::diagnostic::DiagnosticsPlugin;
|
||||
use bevy::ecs::schedule::{ExecutorKind, ScheduleLabel};
|
||||
use bevy::gizmos::GizmoPlugin;
|
||||
@@ -46,6 +43,7 @@ use bevy_mod_xr::hand_debug_gizmos::HandGizmosPlugin;
|
||||
use bevy_mod_xr::session::{XrFirst, XrHandleEvents};
|
||||
use clap::Parser;
|
||||
use core::client::{Client, tick_internal_client};
|
||||
use core::entity_handle::EntityHandlePlugin;
|
||||
use core::task;
|
||||
use directories::ProjectDirs;
|
||||
use nodes::drawable::lines::LinesNodePlugin;
|
||||
@@ -283,18 +281,11 @@ fn bevy_loop(
|
||||
..default()
|
||||
});
|
||||
let mut plugins = MinimalPlugins
|
||||
.build().add(DiagnosticsPlugin)
|
||||
.build()
|
||||
.add(DiagnosticsPlugin)
|
||||
.add(TransformPlugin)
|
||||
.add(InputPlugin)
|
||||
/* .add(AccessibilityPlugin) */;
|
||||
// TODO: figure out headless
|
||||
// {
|
||||
// plugins = plugins.add(WindowPlugin::default()).add({
|
||||
// let mut winit = WinitPlugin::<WakeUp>::default();
|
||||
// winit.run_on_any_thread = true;
|
||||
// winit
|
||||
// });
|
||||
// }
|
||||
.add(AccessibilityPlugin);
|
||||
plugins = plugins
|
||||
.add(TerminalCtrlCHandlerPlugin)
|
||||
// bevy_mod_openxr will replace this, TODO: figure out how to mix this with
|
||||
@@ -315,8 +306,7 @@ fn bevy_loop(
|
||||
.add(ScenePlugin)
|
||||
.add(GltfPlugin::default())
|
||||
.add(AudioPlugin::default())
|
||||
.add(GizmoPlugin)
|
||||
.add(AccessibilityPlugin);
|
||||
.add(GizmoPlugin);
|
||||
let mut task_pool_plugin = TaskPoolPlugin::default();
|
||||
// make tokio work
|
||||
let handle = tokio::runtime::Handle::current();
|
||||
@@ -396,15 +386,17 @@ fn bevy_loop(
|
||||
app.insert_resource(ObjectRegistryRes(object_registry));
|
||||
app.add_plugins((RemotePlugin::default(), RemoteHttpPlugin::default()));
|
||||
// the Stardust server plugins
|
||||
// infra plugins
|
||||
app.add_plugins(EntityHandlePlugin);
|
||||
// node plugins
|
||||
app.add_plugins((
|
||||
SpatialNodePlugin,
|
||||
ModelNodePlugin,
|
||||
TextNodePlugin,
|
||||
LinesNodePlugin,
|
||||
PlaySpacePlugin,
|
||||
HandPlugin,
|
||||
ControllerPlugin,
|
||||
));
|
||||
// object plugins
|
||||
app.add_plugins((PlaySpacePlugin, HandPlugin, ControllerPlugin));
|
||||
app.add_systems(PostStartup, move || {
|
||||
ready_notifier.notify_waiters();
|
||||
});
|
||||
@@ -418,14 +410,7 @@ fn bevy_loop(
|
||||
|
||||
app.run();
|
||||
}
|
||||
fn update_cameras(
|
||||
mut camera: Query<
|
||||
&mut Projection,
|
||||
(
|
||||
With<Camera3d>,
|
||||
),
|
||||
>,
|
||||
) {
|
||||
fn update_cameras(mut camera: Query<&mut Projection, (With<Camera3d>,)>) {
|
||||
for mut projection in &mut camera {
|
||||
match projection.deref_mut() {
|
||||
Projection::Perspective(perspective_projection) => perspective_projection.near = 0.003,
|
||||
|
||||
@@ -14,12 +14,20 @@ use std::ops::DerefMut;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use std::{ffi::OsStr, path::PathBuf};
|
||||
use stereokit_rust::sound::{Sound as SkSound, SoundInst};
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub struct AudioNodePlugin;
|
||||
impl Plugin for AudioNodePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
static SOUND_REGISTRY: Registry<Sound> = Registry::new();
|
||||
|
||||
stardust_xr_server_codegen::codegen_audio_protocol!();
|
||||
pub struct Sound {
|
||||
space: Arc<Spatial>,
|
||||
spatial: Arc<Spatial>,
|
||||
|
||||
volume: f32,
|
||||
pending_audio_path: PathBuf,
|
||||
@@ -37,7 +45,7 @@ impl Sound {
|
||||
)
|
||||
.ok_or_else(|| eyre!("Resource not found"))?;
|
||||
let sound = Sound {
|
||||
space: node.get_aspect::<Spatial>().unwrap().clone(),
|
||||
spatial: node.get_aspect::<Spatial>().unwrap().clone(),
|
||||
volume: 1.0,
|
||||
pending_audio_path,
|
||||
sk_sound: OnceLock::new(),
|
||||
@@ -64,7 +72,7 @@ impl Sound {
|
||||
self.instance.lock().replace(instance);
|
||||
}
|
||||
if let Some(instance) = self.instance.lock().deref_mut() {
|
||||
instance.position(self.space.global_transform().w_axis.xyz());
|
||||
instance.position(self.spatial.global_transform().w_axis.xyz());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use super::{Line, LinesAspect};
|
||||
use crate::{
|
||||
core::{client::Client, color::ColorConvert, error::Result, registry::Registry},
|
||||
core::{
|
||||
client::Client, color::ColorConvert, entity_handle::EntityHandle, error::Result,
|
||||
registry::Registry,
|
||||
},
|
||||
nodes::{
|
||||
Node,
|
||||
spatial::{Spatial, SpatialNode},
|
||||
@@ -26,7 +29,25 @@ pub struct LinesNodePlugin;
|
||||
|
||||
impl Plugin for LinesNodePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Update, build_line_mesh);
|
||||
app.add_systems(Update, (build_line_mesh, update_visibillity).chain());
|
||||
}
|
||||
}
|
||||
|
||||
fn update_visibillity(mut cmds: Commands) {
|
||||
for lines in LINES_REGISTRY.get_valid_contents().into_iter() {
|
||||
let Some(entity) = lines.entity.get().map(|e| **e) else {
|
||||
continue;
|
||||
};
|
||||
match lines.spatial.node().map(|n| n.enabled()).unwrap_or(false) {
|
||||
true => {
|
||||
cmds.entity(entity)
|
||||
.insert_recursive::<Children>(Visibility::Visible);
|
||||
}
|
||||
false => {
|
||||
cmds.entity(entity)
|
||||
.insert_recursive::<Children>(Visibility::Hidden);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,8 +153,12 @@ fn build_line_mesh(
|
||||
}
|
||||
|
||||
match lines.entity.get() {
|
||||
Some(e) => cmds.entity(*e),
|
||||
None => cmds.spawn(SpatialNode(Arc::downgrade(&lines.spatial))),
|
||||
Some(e) => cmds.entity(**e),
|
||||
None => {
|
||||
let e = cmds.spawn(SpatialNode(Arc::downgrade(&lines.spatial)));
|
||||
_ = lines.entity.set(e.id().into());
|
||||
e
|
||||
}
|
||||
}
|
||||
.insert((
|
||||
Mesh3d(meshes.add(mesh)),
|
||||
@@ -182,7 +207,7 @@ pub struct Lines {
|
||||
spatial: Arc<Spatial>,
|
||||
data: Mutex<Vec<Line>>,
|
||||
gen_mesh: AtomicBool,
|
||||
entity: OnceLock<Entity>,
|
||||
entity: OnceLock<EntityHandle>,
|
||||
bounds: Mutex<Aabb>,
|
||||
}
|
||||
impl Lines {
|
||||
@@ -194,7 +219,7 @@ impl Lines {
|
||||
.set(|node| {
|
||||
node.get_aspect::<Lines>()
|
||||
.ok()
|
||||
.map(|v| v.bounds.lock().clone())
|
||||
.map(|v| *v.bounds.lock())
|
||||
.unwrap_or_default()
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use super::{MODEL_PART_ASPECT_ALIAS_INFO, MaterialParameter, ModelAspect, ModelPartAspect};
|
||||
use crate::bail;
|
||||
use crate::core::bevy_channel::{BevyChannel, BevyChannelReader};
|
||||
use crate::core::client::Client;
|
||||
use crate::core::color::ColorConvert as _;
|
||||
use crate::core::entity_handle::EntityHandle;
|
||||
use crate::core::error::Result;
|
||||
use crate::core::registry::Registry;
|
||||
use crate::core::resource::get_resource_file;
|
||||
@@ -19,17 +21,14 @@ use std::ffi::OsStr;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, OnceLock, Weak};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
static LOAD_MODEL: OnceLock<mpsc::UnboundedSender<(Arc<Model>, PathBuf)>> = OnceLock::new();
|
||||
static LOAD_MODEL: BevyChannel<(Arc<Model>, PathBuf)> = BevyChannel::new();
|
||||
|
||||
pub struct ModelNodePlugin;
|
||||
impl Plugin for ModelNodePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
LOAD_MODEL.set(tx).unwrap();
|
||||
LOAD_MODEL.init(app);
|
||||
app.init_resource::<MaterialRegistry>();
|
||||
app.insert_resource(MpscReceiver(rx));
|
||||
app.add_systems(Update, load_models);
|
||||
app.add_systems(
|
||||
PostUpdate,
|
||||
@@ -38,23 +37,21 @@ impl Plugin for ModelNodePlugin {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct MpscReceiver<T>(mpsc::UnboundedReceiver<T>);
|
||||
#[derive(Component)]
|
||||
struct ModelNode(Weak<Model>);
|
||||
|
||||
fn update_visibillity(mut cmds: Commands) {
|
||||
for model in MODEL_REGISTRY.get_valid_contents().into_iter() {
|
||||
let Some(entity) = model.bevy_scene_entity.get().copied() else {
|
||||
let Some(entity) = model.bevy_scene_entity.get() else {
|
||||
continue;
|
||||
};
|
||||
match model.spatial.node().map(|n| n.enabled()).unwrap_or(false) {
|
||||
true => {
|
||||
cmds.entity(entity)
|
||||
cmds.entity(entity.0)
|
||||
.insert_recursive::<Children>(Visibility::Visible);
|
||||
}
|
||||
false => {
|
||||
cmds.entity(entity)
|
||||
cmds.entity(entity.0)
|
||||
.insert_recursive::<Children>(Visibility::Hidden);
|
||||
}
|
||||
}
|
||||
@@ -64,9 +61,9 @@ fn update_visibillity(mut cmds: Commands) {
|
||||
fn load_models(
|
||||
asset_server: Res<AssetServer>,
|
||||
mut cmds: Commands,
|
||||
mut mpsc_receiver: ResMut<MpscReceiver<(Arc<Model>, PathBuf)>>,
|
||||
mut mpsc_receiver: ResMut<BevyChannelReader<(Arc<Model>, PathBuf)>>,
|
||||
) {
|
||||
while let Ok((model, path)) = mpsc_receiver.0.try_recv() {
|
||||
while let Some((model, path)) = mpsc_receiver.read() {
|
||||
// idk of the asset label is the correct approach here
|
||||
let handle = asset_server.load(GltfAssetLabel::Scene(0).from_asset(path));
|
||||
let entity = cmds
|
||||
@@ -76,7 +73,7 @@ fn load_models(
|
||||
SpatialNode(Arc::downgrade(&model.spatial)),
|
||||
))
|
||||
.id();
|
||||
model.bevy_scene_entity.set(entity).unwrap();
|
||||
model.bevy_scene_entity.set(entity.into()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +89,7 @@ fn apply_materials(
|
||||
.filter_map(|p| p.parts.get())
|
||||
.flatten()
|
||||
{
|
||||
let Ok(mut mesh_mat) = query.get_mut(*model_part.mesh_entity.get().unwrap()) else {
|
||||
let Ok(mut mesh_mat) = query.get_mut(**model_part.mesh_entity.get().unwrap()) else {
|
||||
continue;
|
||||
};
|
||||
if let Some(material) = model_part.pending_material_replacement.lock().take() {
|
||||
@@ -214,8 +211,8 @@ fn gen_model_parts(
|
||||
.flat_map(|v| v.iter())
|
||||
.find(|e| has_mesh.get(*e).unwrap_or(false))?;
|
||||
_ = model_part.bounds.set(aabb);
|
||||
_ = model_part.entity.set(entity);
|
||||
_ = model_part.mesh_entity.set(mesh_entity);
|
||||
_ = model_part.entity.set(entity.into());
|
||||
_ = model_part.mesh_entity.set(mesh_entity.into());
|
||||
parts.push(model_part.clone());
|
||||
Some(model_part)
|
||||
},
|
||||
@@ -460,8 +457,8 @@ impl Material {
|
||||
}
|
||||
|
||||
pub struct ModelPart {
|
||||
entity: OnceLock<Entity>,
|
||||
mesh_entity: OnceLock<Entity>,
|
||||
entity: OnceLock<EntityHandle>,
|
||||
mesh_entity: OnceLock<EntityHandle>,
|
||||
path: String,
|
||||
space: Arc<Spatial>,
|
||||
_model: Weak<Model>,
|
||||
@@ -541,7 +538,7 @@ impl MaterialRegistry {
|
||||
pub struct Model {
|
||||
spatial: Arc<Spatial>,
|
||||
_resource_id: ResourceID,
|
||||
bevy_scene_entity: OnceLock<Entity>,
|
||||
bevy_scene_entity: OnceLock<EntityHandle>,
|
||||
parts: OnceLock<Vec<Arc<ModelPart>>>,
|
||||
pre_bound_parts: Mutex<Vec<Arc<ModelPart>>>,
|
||||
}
|
||||
@@ -562,8 +559,6 @@ impl Model {
|
||||
parts: OnceLock::new(),
|
||||
});
|
||||
LOAD_MODEL
|
||||
.get()
|
||||
.unwrap()
|
||||
.send((model.clone(), pending_model_path))
|
||||
.unwrap();
|
||||
MODEL_REGISTRY.add_raw(&model);
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
use crate::{
|
||||
core::{
|
||||
client::Client, color::ColorConvert, error::Result, registry::Registry,
|
||||
resource::get_resource_file,
|
||||
bevy_channel::{BevyChannel, BevyChannelReader}, client::Client, color::ColorConvert, entity_handle::EntityHandle, error::Result, registry::Registry, resource::get_resource_file
|
||||
},
|
||||
nodes::{
|
||||
Node,
|
||||
drawable::XAlign,
|
||||
spatial::{Spatial, SpatialNode},
|
||||
drawable::XAlign, spatial::{Spatial, SpatialNode}, Node
|
||||
},
|
||||
};
|
||||
use bevy::{platform::collections::HashMap, prelude::*};
|
||||
@@ -19,31 +16,15 @@ use color_eyre::eyre::eyre;
|
||||
use core::f32;
|
||||
use cosmic_text::Metrics;
|
||||
use parking_lot::Mutex;
|
||||
use std::{
|
||||
ffi::OsStr,
|
||||
mem,
|
||||
path::PathBuf,
|
||||
sync::{Arc, OnceLock},
|
||||
};
|
||||
use tokio::sync::mpsc;
|
||||
use std::{ffi::OsStr, mem, path::PathBuf, sync::Arc};
|
||||
|
||||
static SPAWN_TEXT: OnceLock<mpsc::UnboundedSender<Arc<Text>>> = OnceLock::new();
|
||||
|
||||
#[derive(Resource)]
|
||||
struct MpscReceiver<T>(mpsc::UnboundedReceiver<T>);
|
||||
static SPAWN_TEXT: BevyChannel<Arc<Text>> = BevyChannel::new();
|
||||
|
||||
pub struct TextNodePlugin;
|
||||
|
||||
impl Plugin for TextNodePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
// Text init stuff
|
||||
// app.init_asset::<Font>().init_asset_loader::<FontLoader>();
|
||||
// load_internal_binary_asset!(
|
||||
// app,
|
||||
// Handle::default(),
|
||||
// "assets/FiraMono-subset.ttf",
|
||||
// |bytes: &[u8], _path: String| { Font::try_from_bytes(bytes.to_vec()).unwrap() }
|
||||
// );
|
||||
// 1.0 for font size in meters
|
||||
app.add_plugins(MeshTextPlugin::new(1.0));
|
||||
app.world_mut()
|
||||
@@ -52,17 +33,15 @@ impl Plugin for TextNodePlugin {
|
||||
.db_mut()
|
||||
.load_system_fonts();
|
||||
|
||||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
SPAWN_TEXT.set(tx).unwrap();
|
||||
SPAWN_TEXT.init(app);
|
||||
app.init_resource::<MaterialRegistry>();
|
||||
app.insert_resource(MpscReceiver(rx));
|
||||
app.add_systems(Update, (spawn_text, update_visibillity).chain());
|
||||
}
|
||||
}
|
||||
|
||||
fn update_visibillity(mut cmds: Commands) {
|
||||
for text in TEXT_REGISTRY.get_valid_contents().into_iter() {
|
||||
let Some(entity) = text.entity.lock().as_ref().copied() else {
|
||||
let Some(entity) = text.entity.lock().as_deref().copied() else {
|
||||
continue;
|
||||
};
|
||||
match text.spatial.node().map(|n| n.enabled()).unwrap_or(false) {
|
||||
@@ -79,7 +58,7 @@ fn update_visibillity(mut cmds: Commands) {
|
||||
}
|
||||
|
||||
fn spawn_text(
|
||||
mut mpsc: ResMut<MpscReceiver<Arc<Text>>>,
|
||||
mut mpsc: ResMut<BevyChannelReader<Arc<Text>>>,
|
||||
mut cmds: Commands,
|
||||
mut font_settings: ResMut<FontSettings>,
|
||||
mut material_registry: ResMut<MaterialRegistry>,
|
||||
@@ -87,9 +66,9 @@ fn spawn_text(
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut font_registry: Local<FontDatabaseRegistry>,
|
||||
) {
|
||||
while let Ok(text) = mpsc.0.try_recv() {
|
||||
while let Some(text) = mpsc.read() {
|
||||
if let Some(entity) = text.entity.lock().take() {
|
||||
cmds.entity(entity).despawn();
|
||||
cmds.entity(*entity).despawn();
|
||||
}
|
||||
let style = text.data.lock();
|
||||
let old_db = text.font_path.clone().map(|p| {
|
||||
@@ -157,17 +136,11 @@ fn spawn_text(
|
||||
};
|
||||
let dist = meshes.iter().fold(f32::MAX, |dist, v| {
|
||||
dist.min(v.transform.translation.x)
|
||||
// if dist > v.transform.translation.x {
|
||||
// v.transform.translation.x
|
||||
// } else {
|
||||
// dist
|
||||
// }
|
||||
});
|
||||
// TODO: text align
|
||||
let letters = meshes
|
||||
.into_iter()
|
||||
.map(|v| {
|
||||
// info!("{:?}", v.transform);
|
||||
cmds.spawn((
|
||||
Mesh3d(v.mesh),
|
||||
MeshMaterial3d(v.material),
|
||||
@@ -177,7 +150,7 @@ fn spawn_text(
|
||||
-dist
|
||||
+ match style.bounds.as_ref().map(|v| v.anchor_align_x) {
|
||||
Some(XAlign::Center) => width * -0.5,
|
||||
Some(XAlign::Right) => width * -1.0,
|
||||
Some(XAlign::Right) => -width,
|
||||
Some(XAlign::Left) => 0.0,
|
||||
None => 0.0,
|
||||
},
|
||||
@@ -192,7 +165,7 @@ fn spawn_text(
|
||||
.spawn((SpatialNode(Arc::downgrade(&text.spatial)),))
|
||||
.add_children(&letters)
|
||||
.id();
|
||||
text.entity.lock().replace(entity);
|
||||
text.entity.lock().replace(EntityHandle(entity));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +190,7 @@ static TEXT_REGISTRY: Registry<Text> = Registry::new();
|
||||
pub struct Text {
|
||||
spatial: Arc<Spatial>,
|
||||
font_path: Option<PathBuf>,
|
||||
entity: Mutex<Option<Entity>>,
|
||||
entity: Mutex<Option<EntityHandle>>,
|
||||
text: Mutex<String>,
|
||||
data: Mutex<TextStyle>,
|
||||
}
|
||||
@@ -235,7 +208,7 @@ impl Text {
|
||||
data: Mutex::new(style),
|
||||
});
|
||||
node.add_aspect_raw(text.clone());
|
||||
_ = SPAWN_TEXT.get().unwrap().send(text.clone());
|
||||
_ = SPAWN_TEXT.send(text.clone());
|
||||
|
||||
Ok(text)
|
||||
}
|
||||
@@ -248,14 +221,14 @@ impl TextAspect for Text {
|
||||
) -> Result<()> {
|
||||
let this_text = node.get_aspect::<Text>()?;
|
||||
this_text.data.lock().character_height = height;
|
||||
_ = SPAWN_TEXT.get().unwrap().send(this_text.clone());
|
||||
_ = SPAWN_TEXT.send(this_text);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_text(node: Arc<Node>, _calling_client: Arc<Client>, text: String) -> Result<()> {
|
||||
let this_text = node.get_aspect::<Text>()?;
|
||||
*this_text.text.lock() = text;
|
||||
_ = SPAWN_TEXT.get().unwrap().send(this_text.clone());
|
||||
_ = SPAWN_TEXT.send(this_text);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user