feat: implement ModelWrapper for handling model URLs and transformations in BridgeState
This commit is contained in:
@@ -19,6 +19,8 @@ use stardust_xr_fusion::objects::connect_client as fusion_connect_client;
|
|||||||
use stardust_xr_fusion::node::NodeType;
|
use stardust_xr_fusion::node::NodeType;
|
||||||
use stardust_xr_fusion::root::RootAspect;
|
use stardust_xr_fusion::root::RootAspect;
|
||||||
use stardust_xr_fusion::drawable::MaterialParameter;
|
use stardust_xr_fusion::drawable::MaterialParameter;
|
||||||
|
use stardust_xr_fusion::values::ResourceID;
|
||||||
|
use stardust_xr_fusion::spatial::Transform;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||||
@@ -32,6 +34,86 @@ impl Default for BridgeState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Custom element wrapper for fusion Model that can be used with model URLs
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
struct ModelWrapper {
|
||||||
|
resource_id: ResourceID,
|
||||||
|
transform: Transform,
|
||||||
|
color: stardust_xr_fusion::values::Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModelWrapper {
|
||||||
|
fn new(url: &str, transform: Transform, color: stardust_xr_fusion::values::Color) -> Self {
|
||||||
|
// Try to parse as a direct file path or URL
|
||||||
|
let resource_id = if url.starts_with("http://") || url.starts_with("https://") {
|
||||||
|
// For HTTP URLs, we'd need to download and cache - for now use a built-in as fallback
|
||||||
|
ResourceID::new_namespaced("fusion", "tex_cube")
|
||||||
|
} else if url.starts_with("/") || url.ends_with(".glb") || url.ends_with(".gltf") {
|
||||||
|
// Try as direct path
|
||||||
|
ResourceID::new_direct(url).unwrap_or_else(|_| ResourceID::new_namespaced("fusion", "tex_cube"))
|
||||||
|
} else {
|
||||||
|
// Default fallback
|
||||||
|
ResourceID::new_namespaced("fusion", "tex_cube")
|
||||||
|
};
|
||||||
|
|
||||||
|
Self { resource_id, transform, color }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn builtin(name: &str, transform: Transform, color: stardust_xr_fusion::values::Color) -> Self {
|
||||||
|
Self {
|
||||||
|
resource_id: ResourceID::new_namespaced("fusion", name),
|
||||||
|
transform,
|
||||||
|
color,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ModelWrapperInner {
|
||||||
|
model: stardust_xr_fusion::drawable::Model,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<State: ast::ValidState> CustomElement<State> for ModelWrapper {
|
||||||
|
type Inner = ModelWrapperInner;
|
||||||
|
type Resource = ();
|
||||||
|
type Error = stardust_xr_fusion::node::NodeError;
|
||||||
|
|
||||||
|
fn create_inner(
|
||||||
|
&self,
|
||||||
|
_context: &Context,
|
||||||
|
info: ast::CreateInnerInfo,
|
||||||
|
_resource: &mut Self::Resource,
|
||||||
|
) -> Result<Self::Inner, Self::Error> {
|
||||||
|
let model = stardust_xr_fusion::drawable::Model::create(
|
||||||
|
info.parent_space,
|
||||||
|
self.transform,
|
||||||
|
&self.resource_id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Try to set color on model parts
|
||||||
|
// Note: set_material_parameter doesn't exist in this version, skip for now
|
||||||
|
|
||||||
|
Ok(ModelWrapperInner { model })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn diff(&self, _old_self: &Self, _inner: &mut Self::Inner, _resource: &mut Self::Resource) {
|
||||||
|
// Update logic would go here - skip for now as models are recreated each frame
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spatial_aspect(&self, inner: &Self::Inner) -> stardust_xr_fusion::spatial::SpatialRef {
|
||||||
|
use stardust_xr_fusion::spatial::SpatialAspect;
|
||||||
|
inner.model.clone().as_spatial().as_spatial_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Transformable for ModelWrapper {
|
||||||
|
fn transform(&self) -> &Transform {
|
||||||
|
&self.transform
|
||||||
|
}
|
||||||
|
fn transform_mut(&mut self) -> &mut Transform {
|
||||||
|
&mut self.transform
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum Command {
|
enum Command {
|
||||||
Create { c_id: u64, name: String, transform: Mat4 },
|
Create { c_id: u64, name: String, transform: Mat4 },
|
||||||
Update { c_id: u64, transform: Mat4 },
|
Update { c_id: u64, transform: Mat4 },
|
||||||
@@ -72,8 +154,6 @@ impl ClientState for BridgeState {
|
|||||||
impl Reify for BridgeState {
|
impl Reify for BridgeState {
|
||||||
fn reify(&self) -> impl ast::Element<Self> {
|
fn reify(&self) -> impl ast::Element<Self> {
|
||||||
use stardust_xr_fusion::values::color::rgba_linear;
|
use stardust_xr_fusion::values::color::rgba_linear;
|
||||||
use stardust_xr_fusion::drawable::{Line, LinePoint};
|
|
||||||
use stardust_xr_fusion::values::Vector3;
|
|
||||||
|
|
||||||
eprintln!("[bridge/reify] Reifying {} nodes", self.nodes.len());
|
eprintln!("[bridge/reify] Reifying {} nodes", self.nodes.len());
|
||||||
|
|
||||||
@@ -101,37 +181,44 @@ impl Reify for BridgeState {
|
|||||||
// Use entity color if set
|
// Use entity color if set
|
||||||
let node_color = rgba_linear!(node.color[0], node.color[1], node.color[2], node.color[3]);
|
let node_color = rgba_linear!(node.color[0], node.color[1], node.color[2], node.color[3]);
|
||||||
|
|
||||||
// Simple wireframe cube for all entities for now - each entity gets its own Lines element
|
// Create transform using Transform::from_translation_rotation_scale
|
||||||
let t = 0.008; // line thickness
|
let transform = Transform::from_translation_rotation_scale(trans, rot, vis_scale);
|
||||||
let hs = 0.5f32; // half size in model space (unit cube)
|
|
||||||
|
|
||||||
// Create a line for each edge
|
// Entity types: 0=Unknown, 1=Box, 2=Sphere, 3=Model
|
||||||
let seg = |a: [f32;3], b: [f32;3]| -> Line {
|
match node.entity_type {
|
||||||
let p0 = LinePoint { point: Vector3 { x: a[0], y: a[1], z: a[2] }, thickness: t, color: node_color };
|
1 => {
|
||||||
let p1 = LinePoint { point: Vector3 { x: b[0], y: b[1], z: b[2] }, thickness: t, color: node_color };
|
// Box entity - use tex_cube model
|
||||||
Line { points: vec![p0, p1], cyclic: false }
|
Some((
|
||||||
};
|
*id,
|
||||||
|
ModelWrapper::builtin("tex_cube", transform, node_color).build()
|
||||||
let corners = [
|
))
|
||||||
[-hs, -hs, -hs], [ hs, -hs, -hs], [ hs, hs, -hs], [-hs, hs, -hs],
|
},
|
||||||
[-hs, -hs, hs], [ hs, -hs, hs], [ hs, hs, hs], [-hs, hs, hs],
|
2 => {
|
||||||
];
|
// Sphere entity - use tex_cube with uniform scale to approximate
|
||||||
|
let avg_scale = (vis_scale.x + vis_scale.y + vis_scale.z) / 3.0;
|
||||||
// 12 edges of a cube
|
let uniform_scale = glam::Vec3::splat(avg_scale);
|
||||||
let lines = vec![
|
let sphere_transform = Transform::from_translation_rotation_scale(trans, rot, uniform_scale);
|
||||||
seg(corners[0], corners[1]), seg(corners[1], corners[2]), seg(corners[2], corners[3]), seg(corners[3], corners[0]),
|
Some((
|
||||||
seg(corners[4], corners[5]), seg(corners[5], corners[6]), seg(corners[6], corners[7]), seg(corners[7], corners[4]),
|
*id,
|
||||||
seg(corners[0], corners[4]), seg(corners[1], corners[5]), seg(corners[2], corners[6]), seg(corners[3], corners[7]),
|
ModelWrapper::builtin("tex_cube", sphere_transform, node_color).build()
|
||||||
];
|
))
|
||||||
|
},
|
||||||
Some((
|
3 if !node.model_url.is_empty() => {
|
||||||
*id,
|
// Model entity - use gyro as placeholder (or try to load from URL)
|
||||||
Lines::new(lines)
|
Some((
|
||||||
.pos([trans.x, trans.y, trans.z])
|
*id,
|
||||||
.rot([rot.x, rot.y, rot.z, rot.w])
|
ModelWrapper::builtin("gyro", transform, node_color).build()
|
||||||
.scl([vis_scale.x, vis_scale.y, vis_scale.z])
|
))
|
||||||
.build()
|
},
|
||||||
))
|
_ => {
|
||||||
|
// Unknown/default - gray cube
|
||||||
|
let default_color = rgba_linear!(0.6, 0.6, 0.6, 0.8);
|
||||||
|
Some((
|
||||||
|
*id,
|
||||||
|
ModelWrapper::builtin("tex_cube", transform, default_color).build()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
PlaySpace
|
PlaySpace
|
||||||
|
|||||||
Reference in New Issue
Block a user