feat: model nodes
This commit is contained in:
@@ -2,6 +2,7 @@ use crate::nodes::Node;
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
hash::Hash,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
@@ -41,10 +42,27 @@ impl<K: Hash + Eq> LifeLinkedNodeMap<K> {
|
||||
pub fn add(&self, key: K, node: &Arc<Node>) {
|
||||
self.nodes.lock().insert(key, Arc::downgrade(node));
|
||||
}
|
||||
pub fn get(&self, key: &K) -> Option<Arc<Node>> {
|
||||
pub fn get<Q>(&self, key: &Q) -> Option<Arc<Node>>
|
||||
where
|
||||
Q: ?Sized,
|
||||
K: Borrow<Q>,
|
||||
Q: Hash + Eq,
|
||||
{
|
||||
self.nodes.lock().get(key).and_then(|n| n.upgrade())
|
||||
}
|
||||
pub fn remove(&self, key: &K) -> Option<Arc<Node>> {
|
||||
pub fn nodes(&self) -> Vec<Arc<Node>> {
|
||||
self.nodes
|
||||
.lock()
|
||||
.values()
|
||||
.filter_map(|v| v.upgrade())
|
||||
.collect()
|
||||
}
|
||||
pub fn remove<Q>(&self, key: &Q) -> Option<Arc<Node>>
|
||||
where
|
||||
Q: ?Sized,
|
||||
K: Borrow<Q>,
|
||||
Q: Hash + Eq,
|
||||
{
|
||||
self.nodes.lock().remove(key).and_then(|n| n.upgrade())
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,11 @@ pub mod lines;
|
||||
pub mod model;
|
||||
pub mod text;
|
||||
|
||||
use self::{lines::Lines, model::Model, text::Text};
|
||||
use self::{
|
||||
lines::Lines,
|
||||
model::{Model, ModelPart},
|
||||
text::Text,
|
||||
};
|
||||
|
||||
use super::Node;
|
||||
use crate::core::client::Client;
|
||||
@@ -26,6 +30,7 @@ pub fn create_interface(client: &Arc<Client>) -> Result<()> {
|
||||
pub enum Drawable {
|
||||
Lines(Arc<Lines>),
|
||||
Model(Arc<Model>),
|
||||
ModelPart(Arc<ModelPart>),
|
||||
Text(Arc<Text>),
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::Node;
|
||||
use crate::core::client::Client;
|
||||
use crate::core::destroy_queue;
|
||||
use crate::core::node_collections::LifeLinkedNodeMap;
|
||||
use crate::core::registry::Registry;
|
||||
use crate::core::resource::ResourceID;
|
||||
use crate::nodes::drawable::Drawable;
|
||||
@@ -19,7 +20,7 @@ use stardust_xr::values::Transform;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt::Error;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Weak};
|
||||
use stereokit::named_colors::WHITE;
|
||||
use stereokit::{
|
||||
Color128, Material, Model as SKModel, RenderLayer, Shader, StereoKitDraw, StereoKitMultiThread,
|
||||
@@ -114,14 +115,156 @@ impl MaterialParameter {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ModelPart {
|
||||
id: i32,
|
||||
path: PathBuf,
|
||||
space: Arc<Spatial>,
|
||||
model: Weak<Model>,
|
||||
pending_material_parameters: Mutex<FxHashMap<String, MaterialParameter>>,
|
||||
pending_material_replacement: Mutex<Option<Arc<SendWrapper<Material>>>>,
|
||||
}
|
||||
impl ModelPart {
|
||||
fn create_for_model(sk: &impl StereoKitDraw, model: &Arc<Model>, sk_model: &SKModel) {
|
||||
let first_root_part = sk.model_node_get_root(sk_model);
|
||||
let mut current_option_part = Some(first_root_part);
|
||||
|
||||
while let Some(current_part) = &mut current_option_part {
|
||||
ModelPart::create(sk, model, sk_model, *current_part);
|
||||
|
||||
if let Some(child) = sk.model_node_child(sk_model, *current_part) {
|
||||
*current_part = child;
|
||||
} else if let Some(sibling) = sk.model_node_sibling(sk_model, *current_part) {
|
||||
*current_part = sibling;
|
||||
} else {
|
||||
while let Some(current_part) = &mut current_option_part {
|
||||
if let Some(sibling) = sk.model_node_sibling(sk_model, *current_part) {
|
||||
*current_part = sibling;
|
||||
break;
|
||||
}
|
||||
current_option_part = sk.model_node_parent(sk_model, *current_part);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create(
|
||||
sk: &impl StereoKitDraw,
|
||||
model: &Arc<Model>,
|
||||
sk_model: &SKModel,
|
||||
id: i32,
|
||||
) -> Option<Arc<Self>> {
|
||||
let parent_node = sk
|
||||
.model_node_parent(sk_model, id)
|
||||
.and_then(|id| model.parts.get(&id));
|
||||
let parent_part = parent_node
|
||||
.as_ref()
|
||||
.and_then(|node| match node.drawable.get() {
|
||||
Some(Drawable::ModelPart(model_part)) => Some(model_part),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let stardust_model_part = model.space.node()?;
|
||||
let client = stardust_model_part.get_client()?;
|
||||
let mut part_path = parent_part.map(|n| n.path.clone()).unwrap_or_default();
|
||||
part_path.push(sk.model_node_get_name(sk_model, id)?);
|
||||
let node = client.scenegraph.add_node(Node::create(
|
||||
&client,
|
||||
stardust_model_part.get_path(),
|
||||
dbg!(part_path.to_str()?),
|
||||
false,
|
||||
));
|
||||
let spatial_parent = parent_node
|
||||
.and_then(|n| n.spatial.get().cloned())
|
||||
.unwrap_or_else(|| model.space.clone());
|
||||
let space = Spatial::add_to(
|
||||
&node,
|
||||
Some(spatial_parent),
|
||||
sk.model_node_get_transform_local(sk_model, id),
|
||||
false,
|
||||
)
|
||||
.ok()?;
|
||||
let model_part = Arc::new(ModelPart {
|
||||
id,
|
||||
path: part_path,
|
||||
space,
|
||||
model: Arc::downgrade(model),
|
||||
pending_material_parameters: Mutex::new(FxHashMap::default()),
|
||||
pending_material_replacement: Mutex::new(None),
|
||||
});
|
||||
node.add_local_signal(
|
||||
"set_material_parameter",
|
||||
ModelPart::set_material_parameter_flex,
|
||||
);
|
||||
let _ = node.drawable.set(Drawable::ModelPart(model_part.clone()));
|
||||
model.parts.add(id, &node);
|
||||
Some(model_part)
|
||||
}
|
||||
|
||||
fn set_material_parameter_flex(
|
||||
node: &Node,
|
||||
_calling_client: Arc<Client>,
|
||||
data: &[u8],
|
||||
) -> Result<()> {
|
||||
let Some(Drawable::ModelPart(model_part)) = node.drawable.get() else {bail!("Not a drawable??")};
|
||||
|
||||
let (name, value): (String, MaterialParameter) = deserialize(data)?;
|
||||
|
||||
model_part
|
||||
.pending_material_parameters
|
||||
.lock()
|
||||
.insert(name, value);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn replace_material(&self, replacement: Arc<SendWrapper<Material>>) {
|
||||
self.pending_material_replacement
|
||||
.lock()
|
||||
.replace(replacement);
|
||||
}
|
||||
|
||||
fn update(&self, sk: &impl StereoKitDraw) {
|
||||
let Some(model) = self.model.upgrade() else {return};
|
||||
let Some(node) = model.space.node() else {return};
|
||||
let Some(client) = node.get_client() else {return};
|
||||
let Some(sk_model) = model.sk_model.get() else {return};
|
||||
if let Some(material_replacement) = self.pending_material_replacement.lock().take() {
|
||||
sk.model_node_set_material(
|
||||
sk_model.as_ref(),
|
||||
self.id,
|
||||
material_replacement.as_ref().as_ref(),
|
||||
);
|
||||
}
|
||||
|
||||
let mut material_parameters = self.pending_material_parameters.lock();
|
||||
for (parameter_name, parameter_value) in material_parameters.drain() {
|
||||
let Some(material) = sk.model_node_get_material(sk_model.as_ref(), self.id) else {continue};
|
||||
let new_material = sk.material_copy(material);
|
||||
parameter_value.apply_to_material(&client, sk, &new_material, parameter_name.as_str());
|
||||
sk.model_node_set_material(sk_model.as_ref(), self.id, &new_material);
|
||||
}
|
||||
|
||||
// sk.model_node_set_transform_model(
|
||||
// sk_model.as_ref(),
|
||||
// self.id,
|
||||
// self.space.global_transform(), //Spatial::space_to_space_matrix(Some(&self.space), Some(&model.space)),
|
||||
// );
|
||||
sk.model_node_set_transform_local(
|
||||
sk_model.as_ref(),
|
||||
self.id,
|
||||
sk.model_node_get_transform_local(sk_model.as_ref(), self.id),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Model {
|
||||
self_ref: Weak<Model>,
|
||||
enabled: Arc<AtomicBool>,
|
||||
space: Arc<Spatial>,
|
||||
resource_id: ResourceID,
|
||||
pending_model_path: OnceCell<PathBuf>,
|
||||
pending_material_parameters: Mutex<FxHashMap<(i32, String), MaterialParameter>>,
|
||||
pub pending_material_replacements: Mutex<FxHashMap<u32, Arc<SendWrapper<Material>>>>,
|
||||
sk_model: OnceCell<SendWrapper<SKModel>>,
|
||||
parts: LifeLinkedNodeMap<i32>,
|
||||
}
|
||||
|
||||
impl Model {
|
||||
@@ -134,19 +277,18 @@ impl Model {
|
||||
node.drawable.get().is_none(),
|
||||
"Internal: Node already has a drawable attached!"
|
||||
);
|
||||
let model = Model {
|
||||
let model = Arc::new_cyclic(|self_ref| Model {
|
||||
self_ref: self_ref.clone(),
|
||||
enabled: node.enabled.clone(),
|
||||
space: node.spatial.get().unwrap().clone(),
|
||||
resource_id,
|
||||
pending_model_path: OnceCell::new(),
|
||||
pending_material_parameters: Mutex::new(FxHashMap::default()),
|
||||
pending_material_replacements: Mutex::new(FxHashMap::default()),
|
||||
sk_model: OnceCell::new(),
|
||||
};
|
||||
node.add_local_signal("set_material_parameter", Model::set_material_parameter_flex);
|
||||
let model_arc = MODEL_REGISTRY.add(model);
|
||||
let _ = model_arc.pending_model_path.set(
|
||||
model_arc
|
||||
parts: LifeLinkedNodeMap::default(),
|
||||
});
|
||||
MODEL_REGISTRY.add_raw(&model);
|
||||
let _ = model.pending_model_path.set(
|
||||
model
|
||||
.resource_id
|
||||
.get_file(
|
||||
&node
|
||||
@@ -159,88 +301,37 @@ impl Model {
|
||||
)
|
||||
.ok_or_else(|| eyre!("Resource not found"))?,
|
||||
);
|
||||
let _ = node.drawable.set(Drawable::Model(model_arc.clone()));
|
||||
Ok(model_arc)
|
||||
}
|
||||
|
||||
fn set_material_parameter_flex(
|
||||
node: &Node,
|
||||
_calling_client: Arc<Client>,
|
||||
data: &[u8],
|
||||
) -> Result<()> {
|
||||
let Some(Drawable::Model(model)) = node.drawable.get() else {bail!("Not a drawable??")};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct MaterialParameterInfo {
|
||||
idx: u32,
|
||||
name: String,
|
||||
value: MaterialParameter,
|
||||
}
|
||||
let info: MaterialParameterInfo = deserialize(data)?;
|
||||
|
||||
model
|
||||
.pending_material_parameters
|
||||
.lock()
|
||||
.insert((info.idx as i32, info.name), info.value);
|
||||
|
||||
Ok(())
|
||||
let _ = node.drawable.set(Drawable::Model(model.clone()));
|
||||
Ok(model)
|
||||
}
|
||||
|
||||
fn draw(&self, sk: &impl StereoKitDraw) {
|
||||
let sk_model = self
|
||||
let Ok(sk_model) = self
|
||||
.sk_model
|
||||
.get_or_try_init(|| -> color_eyre::eyre::Result<SendWrapper<SKModel>> {
|
||||
let pending_model_path = self.pending_model_path.get().ok_or(Error)?;
|
||||
let model =
|
||||
sk.model_create_file(pending_model_path.to_str().unwrap(), None::<Shader>)?;
|
||||
|
||||
ModelPart::create_for_model(sk, &self.self_ref.upgrade().unwrap(), &model);
|
||||
|
||||
Ok(SendWrapper::new(sk.model_copy(model)))
|
||||
})
|
||||
.ok();
|
||||
}) else {return};
|
||||
|
||||
if let Some(sk_model) = sk_model {
|
||||
{
|
||||
let mut material_replacements = self.pending_material_replacements.lock();
|
||||
for (material_idx, replacement_material) in material_replacements.iter() {
|
||||
if sk
|
||||
.model_get_material(sk_model.as_ref(), *material_idx as i32)
|
||||
.is_some()
|
||||
{
|
||||
sk.model_set_material(
|
||||
sk_model.as_ref(),
|
||||
*material_idx as i32,
|
||||
replacement_material.as_ref().as_ref(),
|
||||
);
|
||||
}
|
||||
}
|
||||
material_replacements.clear();
|
||||
}
|
||||
|
||||
if let Some(client) = self.space.node.upgrade().and_then(|n| n.client.upgrade()) {
|
||||
let mut material_parameters = self.pending_material_parameters.lock();
|
||||
for ((material_idx, parameter_name), parameter_value) in material_parameters.drain()
|
||||
{
|
||||
let Some(material) = sk.model_get_material(sk_model.as_ref(), material_idx) else {continue};
|
||||
let new_material = sk.material_copy(material);
|
||||
parameter_value.apply_to_material(
|
||||
&client,
|
||||
sk,
|
||||
&new_material,
|
||||
parameter_name.as_str(),
|
||||
);
|
||||
sk.model_set_material(sk_model.as_ref(), material_idx, &new_material);
|
||||
}
|
||||
}
|
||||
|
||||
sk.model_draw(
|
||||
sk_model.as_ref(),
|
||||
self.space.global_transform(),
|
||||
WHITE,
|
||||
RenderLayer::LAYER0,
|
||||
);
|
||||
for model_node_node in self.parts.nodes() {
|
||||
let Some(Drawable::ModelPart(model_node)) = model_node_node.drawable.get() else {continue};
|
||||
model_node.update(sk);
|
||||
}
|
||||
|
||||
sk.model_draw(
|
||||
sk_model.as_ref(),
|
||||
self.space.global_transform(),
|
||||
WHITE,
|
||||
RenderLayer::LAYER0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Model {
|
||||
fn drop(&mut self) {
|
||||
if let Some(model) = self.sk_model.take() {
|
||||
|
||||
@@ -72,6 +72,10 @@ impl Spatial {
|
||||
Ok(spatial)
|
||||
}
|
||||
|
||||
pub fn node(&self) -> Option<Arc<Node>> {
|
||||
self.node.upgrade()
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub fn space_to_space_matrix(from: Option<&Spatial>, to: Option<&Spatial>) -> Mat4 {
|
||||
let space_to_world_matrix = from.map_or(Mat4::IDENTITY, |from| from.global_transform());
|
||||
|
||||
@@ -300,8 +300,7 @@ impl PanelItem {
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct SurfaceMaterialInfo<'a> {
|
||||
surface: SurfaceID,
|
||||
model_path: &'a str,
|
||||
idx: u32,
|
||||
model_node_path: &'a str,
|
||||
}
|
||||
|
||||
let info: SurfaceMaterialInfo = deserialize(data)?;
|
||||
@@ -311,12 +310,12 @@ impl PanelItem {
|
||||
|
||||
let model_node = calling_client
|
||||
.scenegraph
|
||||
.get_node(info.model_path)
|
||||
.get_node(info.model_node_path)
|
||||
.ok_or_else(|| eyre!("Model node not found"))?;
|
||||
let Some(Drawable::Model(model)) = model_node.drawable.get() else {bail!("Node is not a model")};
|
||||
let Some(Drawable::ModelPart(model_node)) = model_node.drawable.get() else {bail!("Node is not a model")};
|
||||
debug!(?info, "Apply surface material");
|
||||
|
||||
core_surface.apply_material(model.clone(), info.idx);
|
||||
core_surface.apply_material(model_node);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::{shaders::PANEL_SHADER_BYTES, state::WaylandState};
|
||||
use crate::{
|
||||
core::{delta::Delta, destroy_queue, registry::Registry},
|
||||
nodes::drawable::model::Model,
|
||||
nodes::drawable::model::ModelPart,
|
||||
};
|
||||
use mint::Vector2;
|
||||
use once_cell::sync::OnceCell;
|
||||
@@ -51,7 +51,7 @@ pub struct CoreSurface {
|
||||
sk_mat: OnceCell<Arc<SendWrapper<Material>>>,
|
||||
material_offset: Mutex<Delta<u32>>,
|
||||
on_commit: Box<dyn Fn(u32) + Send + Sync>,
|
||||
pub pending_material_applications: Mutex<Vec<(Arc<Model>, u32)>>,
|
||||
pub pending_material_applications: Registry<ModelPart>,
|
||||
}
|
||||
|
||||
impl CoreSurface {
|
||||
@@ -72,7 +72,7 @@ impl CoreSurface {
|
||||
sk_mat: OnceCell::new(),
|
||||
material_offset: Mutex::new(Delta::new(0)),
|
||||
on_commit: Box::new(on_commit) as Box<dyn Fn(u32) + Send + Sync>,
|
||||
pending_material_applications: Mutex::new(Vec::new()),
|
||||
pending_material_applications: Registry::new(),
|
||||
})
|
||||
});
|
||||
});
|
||||
@@ -180,19 +180,15 @@ impl CoreSurface {
|
||||
*self.material_offset.lock().value_mut() = material_offset;
|
||||
}
|
||||
|
||||
pub fn apply_material(&self, model: Arc<Model>, material_idx: u32) {
|
||||
self.pending_material_applications
|
||||
.lock()
|
||||
.push((model, material_idx));
|
||||
pub fn apply_material(&self, model_node: &Arc<ModelPart>) {
|
||||
self.pending_material_applications.add_raw(model_node)
|
||||
}
|
||||
|
||||
fn apply_surface_materials(&self) {
|
||||
for (model, material_idx) in self.pending_material_applications.lock().drain(0..) {
|
||||
model
|
||||
.pending_material_replacements
|
||||
.lock()
|
||||
.insert(material_idx, self.sk_mat.get().unwrap().clone());
|
||||
for model_node in self.pending_material_applications.get_valid_contents() {
|
||||
model_node.replace_material(self.sk_mat.clone().get().unwrap().clone());
|
||||
}
|
||||
self.pending_material_applications.clear();
|
||||
}
|
||||
|
||||
pub fn wl_surface(&self) -> Option<WlSurface> {
|
||||
|
||||
Reference in New Issue
Block a user