scena 1.7.2

A Rust-native scene-graph renderer with typed scene state, glTF assets, and explicit prepare/render lifecycles.
Documentation
use serde::{Deserialize, Serialize};

use super::{SceneHostCore, SceneHostError};
use crate::{AssetFetcher, Callout, Color, Vec3};

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SceneHostCalloutReportV1 {
    pub id: String,
    pub anchor_id: String,
    pub leader_line_node: u64,
    pub label_node: u64,
    pub text: String,
}

impl<F: AssetFetcher> SceneHostCore<F> {
    pub fn add_node_callout(
        &mut self,
        id: &str,
        node: u64,
        local_offset: [f32; 3],
        label_offset: [f32; 3],
        text: &str,
    ) -> Result<SceneHostCalloutReportV1, SceneHostError> {
        let node_key = self.resolve_node(node)?;
        let report = self.scene.add_callout(
            &self.assets,
            Callout::node(id, node_key, vec3(local_offset), text)
                .with_label_offset(vec3(label_offset))
                .with_color(Color::CYAN),
        )?;
        Ok(SceneHostCalloutReportV1 {
            id: report.id,
            anchor_id: report.anchor_id,
            leader_line_node: self.register_node(report.leader_line_node),
            label_node: self.register_node(report.label_node),
            text: report.text,
        })
    }

    pub fn add_world_callout(
        &mut self,
        id: &str,
        position: [f32; 3],
        label_offset: [f32; 3],
        text: &str,
    ) -> Result<SceneHostCalloutReportV1, SceneHostError> {
        let report = self.scene.add_callout(
            &self.assets,
            Callout::world(id, vec3(position), text).with_label_offset(vec3(label_offset)),
        )?;
        Ok(SceneHostCalloutReportV1 {
            id: report.id,
            anchor_id: report.anchor_id,
            leader_line_node: self.register_node(report.leader_line_node),
            label_node: self.register_node(report.label_node),
            text: report.text,
        })
    }

    pub fn clear_callout(&mut self, id: &str) -> bool {
        let Some(callout) = self.scene.callout_state(id).cloned() else {
            return false;
        };
        let removed = self.scene.clear_callout(id);
        if removed {
            self.invalidate_node_handles(&[callout.leader_line_node(), callout.label_node()]);
        }
        removed
    }
}

const fn vec3(value: [f32; 3]) -> Vec3 {
    Vec3::new(value[0], value[1], value[2])
}