diff --git a/src/nodes/model.rs b/src/nodes/model.rs index 775a88b..bc9d1ff 100644 --- a/src/nodes/model.rs +++ b/src/nodes/model.rs @@ -2,13 +2,15 @@ use super::core::Node; use super::spatial::{get_spatial_parent_flex, Spatial}; use crate::core::client::Client; use crate::core::registry::Registry; -use anyhow::{anyhow, ensure, Result}; +use anyhow::{anyhow, bail, ensure, Result}; +use flexbuffers::FlexBufferType; use glam::Mat4; use lazy_static::lazy_static; use libstardustxr::{flex_to_quat, flex_to_vec3}; use once_cell::sync::OnceCell; use parking_lot::Mutex; use prisma::{Rgb, Rgba}; +use rustc_hash::FxHashMap; use send_wrapper::SendWrapper; use std::fmt::Error; use std::path::PathBuf; @@ -16,6 +18,7 @@ use std::sync::Arc; use stereokit::enums::RenderLayer; use stereokit::lifecycle::DrawContext; use stereokit::model::Model as SKModel; +use stereokit::texture::Texture; use stereokit::StereoKit; pub static MODEL_REGISTRY: Registry = Registry::new(); @@ -23,9 +26,14 @@ lazy_static! { pub static ref MODELS_TO_DROP: Mutex>> = Default::default(); } +pub enum MaterialParameter { + Texture(PathBuf), +} + pub struct Model { space: Arc, pending_model_path: OnceCell, + pending_material_parameters: Mutex>, sk_model: OnceCell>, } @@ -42,9 +50,10 @@ impl Model { let model = Model { space: node.spatial.get().unwrap().clone(), pending_model_path: OnceCell::new(), + pending_material_parameters: Mutex::new(FxHashMap::default()), sk_model: OnceCell::new(), }; - // node.add_local_method("", Spatial::get_transform_flex); + node.add_local_signal("setMaterialParameter", Model::set_material_parameter); let model_arc = MODEL_REGISTRY.add(model); let _ = model_arc.pending_model_path.set(PathBuf::from(path)); let _ = node.model.set(model_arc.clone()); @@ -64,6 +73,21 @@ impl Model { .ok(); if let Some(sk_model) = sk_model { + for ((material_idx, parameter_name), parameter_value) in + self.pending_material_parameters.lock().iter() + { + if let Some(material) = sk_model.get_material(sk, *material_idx as i32) { + match parameter_value { + MaterialParameter::Texture(path) => { + if let Some(tex) = Texture::from_file(sk, path.as_path(), true, 0) { + material.set_parameter(parameter_name.as_str(), &tex); + } + } + } + } + } + self.pending_material_parameters.lock().clear(); + sk_model.draw( draw_ctx, self.space.global_transform().into(), @@ -72,6 +96,38 @@ impl Model { ); } } + + fn set_material_parameter( + node: &Node, + _calling_client: Arc, + data: &[u8], + ) -> Result<()> { + let model = node.model.get().unwrap(); + let flex_vec = flexbuffers::Reader::get_root(data)?.get_vector()?; + let material_idx = flex_vec + .idx(0) + .get_u64() + .map_err(|_| anyhow!("Material ID is not a number!"))? as u32; + let parameter_name = flex_vec + .idx(1) + .get_str() + .map_err(|_| anyhow!("Parameter name is not a string!"))?; + + let flex_parameter_value = flex_vec.idx(2); + let parameter_value = match flex_parameter_value.flexbuffer_type() { + FlexBufferType::String => { + MaterialParameter::Texture(PathBuf::from(flex_parameter_value.as_str())) + } + _ => bail!("Invalid parameter value type"), + }; + + model + .pending_material_parameters + .lock() + .insert((material_idx, parameter_name.to_string()), parameter_value); + + Ok(()) + } } impl Drop for Model { fn drop(&mut self) {