From 3c708d1aafd2908198a744c0df52ad99a7aa0bf1 Mon Sep 17 00:00:00 2001 From: Nova Date: Mon, 21 Nov 2022 16:39:28 -0500 Subject: [PATCH] feat(drawable): lines --- Cargo.toml | 2 +- src/nodes/drawable/lines.rs | 125 ++++++++++++++++++++++++++++++++++++ src/nodes/drawable/mod.rs | 3 + src/nodes/mod.rs | 3 + 4 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 src/nodes/drawable/lines.rs diff --git a/Cargo.toml b/Cargo.toml index cae87f0..3a639c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ serde = { version = "1.0.145", features = ["derive"] } [dependencies.stereokit] default-features = false features = ["linux-egl"] -version = "0.7.1" +version = "0.9.0" [dependencies.smithay] git = "https://github.com/Smithay/smithay.git" diff --git a/src/nodes/drawable/lines.rs b/src/nodes/drawable/lines.rs new file mode 100644 index 0000000..86359ff --- /dev/null +++ b/src/nodes/drawable/lines.rs @@ -0,0 +1,125 @@ +use crate::{ + core::{client::Client, registry::Registry}, + nodes::{ + spatial::{find_spatial_parent, parse_transform, Spatial}, + Node, + }, +}; +use anyhow::{ensure, Result}; +use glam::Vec3A; +use mint::Vector3; +use parking_lot::Mutex; +use prisma::{Flatten, Lerp}; +use serde::Deserialize; +use stardust_xr::{schemas::flex::deserialize, values::Transform}; +use std::{collections::VecDeque, sync::Arc}; +use stereokit::{lifecycle::DrawContext, lines::LinePoint as SkLinePoint, values::Color32}; + +static LINES_REGISTRY: Registry = Registry::new(); + +#[derive(Debug, Clone, Deserialize)] +struct LinePointRaw { + point: Vector3, + thickness: f32, + color: [u8; 4], +} + +#[derive(Debug, Clone)] +struct LineData { + points: Vec, + cyclic: bool, +} + +pub struct Lines { + space: Arc, + data: Mutex, +} +impl Lines { + fn add_to(node: &Arc, points: Vec, cyclic: bool) -> Result> { + ensure!( + node.model.get().is_none(), + "Internal: Node already has lines attached!" + ); + + let lines = LINES_REGISTRY.add(Lines { + space: node.get_aspect("Lines", "spatial", |n| &n.spatial)?.clone(), + data: Mutex::new(LineData { points, cyclic }), + }); + node.add_local_signal("set_points", Lines::set_points_flex); + node.add_local_signal("set_cyclic", Lines::set_cyclic_flex); + let _ = node.lines.set(lines.clone()); + + Ok(lines) + } + + fn draw(&self, draw_ctx: &DrawContext) { + let transform_mat = self.space.global_transform(); + let data = self.data.lock().clone(); + let mut points: VecDeque = data + .points + .into_iter() + .map(|p| SkLinePoint { + point: transform_mat.transform_point3a(Vec3A::from(p.point)).into(), + thickness: p.thickness, + color: Color32::from_slice(&p.color), + }) + .collect(); + if data.cyclic && !points.is_empty() { + let first = points.front().unwrap(); + let last = points.back().unwrap(); + let connect_point = SkLinePoint { + point: Vector3 { + x: (first.point.x + last.point.x) * 0.5, + y: (first.point.y + last.point.y) * 0.5, + z: (first.point.z + last.point.z) * 0.5, + }, + thickness: (first.thickness + last.thickness) * 0.5, + color: first.color.lerp(&last.color, 0.5), + }; + points.push_front(connect_point.clone()); + points.push_back(connect_point); + } + stereokit::lines::line_add_listv(draw_ctx, points.make_contiguous()); + } + + pub fn set_points_flex(node: &Node, _calling_client: Arc, data: &[u8]) -> Result<()> { + let points: Vec = deserialize(data)?; + node.lines.get().unwrap().data.lock().points = points; + Ok(()) + } + pub fn set_cyclic_flex(node: &Node, _calling_client: Arc, data: &[u8]) -> Result<()> { + node.lines.get().unwrap().data.lock().cyclic = deserialize(data)?; + Ok(()) + } +} +impl Drop for Lines { + fn drop(&mut self) { + LINES_REGISTRY.remove(self); + } +} + +pub fn draw_all(draw_ctx: &DrawContext) { + for lines in LINES_REGISTRY.get_valid_contents() { + lines.draw(draw_ctx); + } +} + +pub fn create_flex(_node: &Node, calling_client: Arc, data: &[u8]) -> Result<()> { + #[derive(Deserialize)] + struct CreateTextInfo<'a> { + name: &'a str, + parent_path: &'a str, + transform: Transform, + points: Vec, + cyclic: bool, + } + let info: CreateTextInfo = deserialize(data)?; + let node = Node::create(&calling_client, "/drawable/lines", info.name, true); + let parent = find_spatial_parent(&calling_client, info.parent_path)?; + let transform = parse_transform(info.transform, true, true, true)?; + + let node = node.add_to_scenegraph(); + Spatial::add_to(&node, Some(parent), transform, false)?; + Lines::add_to(&node, info.points, info.cyclic)?; + Ok(()) +} diff --git a/src/nodes/drawable/mod.rs b/src/nodes/drawable/mod.rs index 8c19d1d..a4c51f0 100644 --- a/src/nodes/drawable/mod.rs +++ b/src/nodes/drawable/mod.rs @@ -1,3 +1,4 @@ +pub mod lines; pub mod model; pub mod text; @@ -12,6 +13,7 @@ use stereokit::{lifecycle::DrawContext, texture::Texture, StereoKit}; pub fn create_interface(client: &Arc) { let node = Node::create(client, "", "drawable", false); + node.add_local_signal("create_lines", lines::create_flex); node.add_local_signal("create_model", model::create_flex); node.add_local_signal("create_text", text::create_flex); node.add_local_signal("set_sky_file", set_sky_file_flex); @@ -19,6 +21,7 @@ pub fn create_interface(client: &Arc) { } pub fn draw(sk: &mut StereoKit, draw_ctx: &DrawContext) { + lines::draw_all(draw_ctx); model::draw_all(sk, draw_ctx); text::draw_all(sk, draw_ctx); diff --git a/src/nodes/mod.rs b/src/nodes/mod.rs index 3b63c24..f6962f9 100644 --- a/src/nodes/mod.rs +++ b/src/nodes/mod.rs @@ -28,6 +28,7 @@ use crate::core::registry::Registry; use self::alias::Alias; use self::data::{PulseReceiver, PulseSender}; +use self::drawable::lines::Lines; use self::drawable::model::Model; use self::drawable::text::Text; use self::fields::Field; @@ -60,6 +61,7 @@ pub struct Node { pub pulse_receiver: OnceCell>, // Drawable + pub lines: OnceCell>, pub model: OnceCell>, pub text: OnceCell>, @@ -113,6 +115,7 @@ impl Node { zone: OnceCell::new(), pulse_sender: OnceCell::new(), pulse_receiver: OnceCell::new(), + lines: OnceCell::new(), model: OnceCell::new(), text: OnceCell::new(), input_method: OnceCell::new(),