animgraph 0.1.0

Animation data flow library using hierarchical state machines
Documentation
use std::ops::Range;

use serde_derive::{Deserialize, Serialize};

use crate::{
    state_machine::NodeIndex, BlendSampleId, BlendTree, Graph, GraphNodeConstructor, IndexType,
    PoseGraph, PoseNode, PoseParent,
};

use super::{GraphNode, GraphVisitor};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TreeNode {
    pub children: Range<IndexType>,
}

impl GraphNodeConstructor for TreeNode {
    fn identity() -> &'static str {
        "tree"
    }

    fn construct_entry(self, metrics: &crate::GraphMetrics) -> anyhow::Result<crate::GraphNodeEntry> {
        metrics.validate_children(&self.children, Self::identity())?;
        Ok(crate::GraphNodeEntry::PoseParent(Box::new(self)))
    }
}

impl Default for TreeNode {
    fn default() -> Self {
        Self {
            children: Default::default(),
        }
    }
}

impl GraphNode for TreeNode {
    fn visit(&self, visitor: &mut GraphVisitor) {
        visitor.visit(self.children.clone());
    }
}

impl PoseParent for TreeNode {
    fn apply_parent(
        &self,
        tasks: &mut BlendTree,
        mut child_task: BlendSampleId,
        graph: &Graph,
    ) -> anyhow::Result<BlendSampleId> {
        for child in self.children.clone() {
            if let Some(parent) = graph.pose_parent(NodeIndex(child)) {
                child_task = parent.apply_parent(tasks, child_task, graph)?;
            }
        }

        Ok(child_task)
    }

    fn as_pose(&self) -> Option<&dyn PoseNode> {
        Some(self)
    }
}

impl PoseNode for TreeNode {
    fn sample(
        &self,
        tasks: &mut BlendTree,
        graph: &Graph,
    ) -> anyhow::Result<Option<BlendSampleId>> {
        for index in self.children.clone() {
            if let Some(mut child_task) = graph.sample_pose(tasks, NodeIndex(index))? {
                for sibling in index+1..self.children.end {
                    if let Some(parent) = graph.pose_parent(NodeIndex(sibling)) {
                        child_task = parent.apply_parent(tasks, child_task, graph)?;
                    }
                }
                return Ok(Some(child_task));
            }
        }
        Ok(None)
    }
}

#[cfg(feature = "compiler")]
pub mod compile {

    use serde_json::Value;

    use crate::{
        compiler::prelude::{
            Extras, IOType, Node, NodeCompilationError, NodeCompiler, NodeSerializationContext,
            NodeSettings,
        },
        GraphNodeConstructor, IndexType, model::NodeChildRange,
    };

    pub use super::TreeNode;

    #[derive(Debug, Clone, Default)]
    pub struct TreeSettings;

    impl NodeSettings for TreeSettings {
        fn name() -> &'static str {
            TreeNode::identity()
        }

        fn input() -> &'static [(&'static str, IOType)] {
            &[]
        }

        fn output() -> &'static [(&'static str, IOType)] {
            &[]
        }

        fn build(self) -> anyhow::Result<Extras> {
            Ok(Extras::default())
        }

        fn child_range() -> NodeChildRange {
            NodeChildRange::UpTo(IndexType::MAX as usize)
        }

    }

    impl NodeCompiler for TreeNode {
        type Settings = TreeSettings;

        fn build<'a>(
            context: &NodeSerializationContext<'a>,
        ) -> Result<Value, NodeCompilationError> {
            context.serialize_node(TreeNode {
                children: context.children(),
            })
        }
    }

    pub fn tree(nodes: impl Into<Vec<Node>>) -> Node {
        Node::new(vec![], nodes.into(), TreeSettings).expect("Valid")
    }
}