Refactor node building logic to support recursive child attachment in bridge reification

This commit is contained in:
MayaTheShy
2025-11-16 22:10:14 -05:00
parent bcdbd11c7c
commit 6d952d7569

View File

@@ -134,21 +134,24 @@ impl Reify for BridgeState {
} }
} }
let children = self.nodes.iter().filter_map(|(id, node)| { // Helper to recursively build a node and its children
fn build_node(
id: u64,
nodes: &HashMap<u64, Node>,
downloader: &ModelDownloader,
) -> Option<(u64, ast::elements::Spatial)> {
let node = nodes.get(&id)?;
let dims = glam::Vec3::from(node.dimensions); let dims = glam::Vec3::from(node.dimensions);
if dims.length() < 0.001 { if dims.length() < 0.001 {
eprintln!("[bridge/reify] Skipping node {} (zero dimensions)", id); eprintln!("[bridge/reify] Skipping node {} (zero dimensions)", id);
return None; return None;
} }
let (scale, rot, trans) = node.transform.to_scale_rotation_translation(); let (scale, rot, trans) = node.transform.to_scale_rotation_translation();
let vis_scale = if dims.length() > 0.001 { dims } else { scale }; let vis_scale = if dims.length() > 0.001 { dims } else { scale };
let trans_array = [trans.x, trans.y, trans.z]; let trans_array = [trans.x, trans.y, trans.z];
let rot_array = [rot.x, rot.y, rot.z, rot.w]; let rot_array = [rot.x, rot.y, rot.z, rot.w];
let scale_array = [vis_scale.x, vis_scale.y, vis_scale.z]; let scale_array = [vis_scale.x, vis_scale.y, vis_scale.z];
let transform = stardust_xr_fusion::spatial::Transform::from_translation_rotation_scale(trans_array, rot_array, scale_array); let transform = stardust_xr_fusion::spatial::Transform::from_translation_rotation_scale(trans_array, rot_array, scale_array);
// Try to load the appropriate model based on entity type and model URL // Try to load the appropriate model based on entity type and model URL
let model_child = if let Some(model_path) = get_model_path(node.entity_type, &node.model_url, downloader) { let model_child = if let Some(model_path) = get_model_path(node.entity_type, &node.model_url, downloader) {
let entity_type_name = match node.entity_type { let entity_type_name = match node.entity_type {
@@ -160,18 +163,14 @@ impl Reify for BridgeState {
6 => "light", 6 => "light",
_ => "unknown" _ => "unknown"
}; };
let model_source = if !node.model_url.is_empty() { let model_source = if !node.model_url.is_empty() {
format!("from URL: {}", node.model_url) format!("from URL: {}", node.model_url)
} else { } else {
format!("primitive from {}", model_path.display()) format!("primitive from {}", model_path.display())
}; };
eprintln!("[bridge/reify] Loading {} for node {} {}", entity_type_name, id, model_source); eprintln!("[bridge/reify] Loading {} for node {} {}", entity_type_name, id, model_source);
match node.entity_type { match node.entity_type {
4 => { 4 => {
// Text entity: use node.name as text for now
let text = ast::elements::Text::new(&node.name) let text = ast::elements::Text::new(&node.name)
.character_height(node.dimensions[1].max(0.01)) .character_height(node.dimensions[1].max(0.01))
.color(ast::elements::RgbaLinear::new( .color(ast::elements::RgbaLinear::new(
@@ -180,18 +179,15 @@ impl Reify for BridgeState {
Some(text.build()) Some(text.build())
} }
5 => { 5 => {
// Image entity: use PanelUI as a placeholder for now
eprintln!("[bridge/reify] Image entity type detected for node {}. Using PanelUI as placeholder.", id); eprintln!("[bridge/reify] Image entity type detected for node {}. Using PanelUI as placeholder.", id);
let panel = ast::elements::PanelUI::default(); let panel = ast::elements::PanelUI::default();
Some(panel.build()) Some(panel.build())
} }
6 => { 6 => {
// Light entity: use asteroids Light element
eprintln!("[bridge/reify] Light entity type detected for node {}.", id); eprintln!("[bridge/reify] Light entity type detected for node {}.", id);
let color = ast::elements::RgbaLinear::new( let color = ast::elements::RgbaLinear::new(
node.color[0], node.color[1], node.color[2], node.color[3] node.color[0], node.color[1], node.color[2], node.color[3]
); );
// Use y dimension as intensity if available, else default to 1.0
let intensity = node.dimensions.get(1).copied().unwrap_or(1.0).max(0.01); let intensity = node.dimensions.get(1).copied().unwrap_or(1.0).max(0.01);
let light = ast::elements::Light::new() let light = ast::elements::Light::new()
.color(color) .color(color)
@@ -199,7 +195,6 @@ impl Reify for BridgeState {
Some(light.build()) Some(light.build())
} }
7 => { 7 => {
// Zone entity: use asteroids Zone element
eprintln!("[bridge/reify] Zone entity type detected for node {}.", id); eprintln!("[bridge/reify] Zone entity type detected for node {}.", id);
let color = ast::elements::RgbaLinear::new( let color = ast::elements::RgbaLinear::new(
node.color[0], node.color[1], node.color[2], node.color[3] node.color[0], node.color[1], node.color[2], node.color[3]
@@ -238,14 +233,30 @@ impl Reify for BridgeState {
eprintln!("[bridge/reify] No model available for entity type {} (node {})", node.entity_type, id); eprintln!("[bridge/reify] No model available for entity type {} (node {})", node.entity_type, id);
None None
}; };
// Recursively build children
Some((*id, Spatial::default() let children: Vec<_> = nodes.iter()
.filter_map(|(child_id, child_node)| {
if child_node.parent == Some(id) {
build_node(*child_id, nodes, downloader)
} else {
None
}
})
.collect();
let mut spatial = Spatial::default()
.transform(transform) .transform(transform)
.build() .build()
.maybe_child(model_child))) .maybe_child(model_child);
}); for (_cid, child_spatial) in children {
spatial = spatial.child(child_spatial);
PlaySpace.build().stable_children(children) }
Some((id, spatial))
}
// Only attach root nodes (no parent) to PlaySpace
let root_nodes: Vec<_> = self.nodes.iter()
.filter_map(|(id, node)| if node.parent.is_none() { build_node(*id, &self.nodes, downloader) } else { None })
.collect();
PlaySpace.build().stable_children(root_nodes)
} }
} }