use super::{ create_item_acceptor_flex, register_item_ui_flex, Item, ItemAcceptor, ItemInterface, ItemType, }; use crate::nodes::items::ITEM_ACCEPTOR_ASPECT_ALIAS_INFO; use crate::nodes::items::ITEM_ASPECT_ALIAS_INFO; use crate::{ core::{client::Client, registry::Registry, scenegraph::MethodResponseSender}, create_interface, nodes::{ drawable::{ model::{MaterialWrapper, ModelPart}, shaders::UNLIT_SHADER_BYTES, }, items::TypeInfo, spatial::{Spatial, Transform}, Message, Node, }, }; use color_eyre::eyre::{bail, eyre, Result}; use glam::Mat4; use lazy_static::lazy_static; use mint::{ColumnMatrix4, Vector2}; use once_cell::sync::OnceCell; use parking_lot::Mutex; use stardust_xr::schemas::flex::{deserialize, serialize}; use std::sync::Arc; use stereokit_rust::{ material::{Material, Transparency}, shader::Shader, sk::MainThreadToken, system::Renderer, tex::{Tex, TexFormat, TexType}, util::Color128, }; use tracing::error; pub struct TexWrapper(pub Tex); unsafe impl Send for TexWrapper {} unsafe impl Sync for TexWrapper {} stardust_xr_server_codegen::codegen_item_camera_protocol!(); lazy_static! { pub(super) static ref ITEM_TYPE_INFO_CAMERA: TypeInfo = TypeInfo { type_name: "camera", alias_info: CAMERA_ITEM_ASPECT_ALIAS_INFO.clone(), ui_node_id: INTERFACE_NODE_ID, ui: Default::default(), items: Registry::new(), acceptors: Registry::new(), new_acceptor_fn: |node, acceptor, acceptor_field| { let _ = camera_item_ui_client::create_acceptor(node, acceptor, acceptor_field); } }; } struct FrameInfo { proj_matrix: Mat4, px_size: Vector2, } pub struct CameraItem { space: Arc, frame_info: Mutex, sk_tex: OnceCell, sk_mat: OnceCell>, applied_to: Registry, apply_to: Registry, } #[allow(unused)] impl CameraItem { pub fn add_to(node: &Arc, proj_matrix: Mat4, px_size: Vector2) { Item::add_to( node, &ITEM_TYPE_INFO_CAMERA, ItemType::Camera(CameraItem { space: node.get_aspect::().unwrap().clone(), frame_info: Mutex::new(FrameInfo { proj_matrix, px_size, }), sk_tex: OnceCell::new(), sk_mat: OnceCell::new(), applied_to: Registry::new(), apply_to: Registry::new(), }), ); // ::node_methods(node); } fn frame_flex( node: Arc, _calling_client: Arc, _message: Message, response: MethodResponseSender, ) { response.wrap_sync(move || { let ItemType::Camera(_camera) = &node.get_aspect::().unwrap().specialization else { return Err(eyre!("Wrong item type?")); }; Ok(serialize(())?.into()) }); } fn apply_preview_material_flex( node: Arc, calling_client: Arc, message: Message, ) -> Result<()> { let ItemType::Camera(camera) = &node.get_aspect::().unwrap().specialization else { bail!("Wrong item type?") }; let model_part_node = calling_client.get_node("Model part", deserialize(&message.data).unwrap())?; let model_part = model_part_node.get_aspect::()?; camera.applied_to.add_raw(&model_part); camera.apply_to.add_raw(&model_part); Ok(()) } pub fn send_ui_item_created(&self, node: &Node, item: &Arc) { let _ = camera_item_ui_client::create_item(node, item); } pub fn send_acceptor_item_created(&self, node: &Node, item: &Arc) { let _ = camera_item_acceptor_client::capture_item(node, item); } pub fn update(&self, token: &MainThreadToken) { let frame_info = self.frame_info.lock(); let sk_tex = self.sk_tex.get_or_init(|| { TexWrapper(Tex::gen_color( Color128::default(), frame_info.px_size.x as i32, frame_info.px_size.y as i32, TexType::Rendertarget, TexFormat::RGBA32Linear, )) }); let sk_mat = self .sk_mat .get_or_try_init(|| -> Result> { let shader = Shader::from_memory(UNLIT_SHADER_BYTES)?; let mut mat = Material::new(&shader, None); mat.get_all_param_info().set_texture("diffuse", &sk_tex.0); mat.transparency(Transparency::Blend); Ok(Arc::new(MaterialWrapper(mat))) }); let Ok(sk_mat) = sk_mat else { error!("unable to make camera item stereokit texture"); return; }; for model_part in self.apply_to.take_valid_contents() { model_part.replace_material(sk_mat.clone()) } if !self.applied_to.is_empty() { Renderer::render_to( token, &sk_tex.0, self.space.global_transform(), frame_info.proj_matrix, None, None, None, ) } } } impl CameraItemAspect for CameraItem {} impl CameraItemAcceptorAspect for ItemAcceptor { fn capture_item(node: Arc, _calling_client: Arc, item: Arc) -> Result<()> { super::acceptor_capture_item_flex(node, item) } } pub fn update(token: &MainThreadToken) { for camera in ITEM_TYPE_INFO_CAMERA.items.get_valid_contents() { let ItemType::Camera(camera) = &camera.specialization else { continue; }; camera.update(token); } } create_interface!(ItemInterface); impl InterfaceAspect for ItemInterface { #[doc = "Create a camera item at a specific location"] fn create_camera_item( _node: Arc, calling_client: Arc, id: u64, parent: Arc, transform: Transform, proj_matrix: ColumnMatrix4, px_size: Vector2, ) -> Result<()> { let space = parent.get_aspect::()?; let transform = transform.to_mat4(true, true, false); let node = Node::from_id(&calling_client, id, false).add_to_scenegraph()?; Spatial::add_to(&node, None, transform * space.global_transform(), false); CameraItem::add_to(&node, proj_matrix.into(), px_size); Ok(()) } #[doc = "Register this client to manage camera items and create default 3D UI for them."] fn register_camera_item_ui(_node: Arc, calling_client: Arc) -> Result<()> { register_item_ui_flex(calling_client, &ITEM_TYPE_INFO_CAMERA) } #[doc = "Create an item acceptor to allow temporary ownership of a given type of item. Creates a node at `/item/camera/acceptor/`."] fn create_camera_item_acceptor( _node: Arc, calling_client: Arc, id: u64, parent: Arc, transform: Transform, field: Arc, ) -> Result<()> { create_item_acceptor_flex( calling_client, id, parent, transform, &ITEM_TYPE_INFO_CAMERA, field, ) } }