scena 1.0.0

A Rust-native scene-graph renderer with typed scene state, glTF assets, and explicit prepare/render lifecycles.
Documentation
use crate::animation::{
    AnimationChannel, AnimationLoopMode, AnimationMixer, AnimationMixerKey, AnimationPlaybackState,
    AnimationTarget,
};
use crate::diagnostics::AnimationError;

use super::{Scene, SceneImport, Transform};

impl Scene {
    pub fn create_animation_mixer(
        &mut self,
        import: &SceneImport,
        clip_name: &str,
    ) -> Result<AnimationMixerKey, AnimationError> {
        let clip = import
            .clip(clip_name)
            .map_err(|_| AnimationError::ClipNotFound {
                name: clip_name.to_string(),
            })?
            .clip();
        Ok(self
            .animation_mixers
            .insert(AnimationMixer::new(clip, import.live_flag())))
    }

    pub fn animation_mixer(
        &self,
        mixer: AnimationMixerKey,
    ) -> Result<&AnimationMixer, AnimationError> {
        self.animation_mixers
            .get(mixer)
            .ok_or(AnimationError::MixerNotFound(mixer))
    }

    pub fn play_animation(&mut self, mixer: AnimationMixerKey) -> Result<(), AnimationError> {
        self.animation_mixer_mut(mixer)?.play();
        Ok(())
    }

    pub fn pause_animation(&mut self, mixer: AnimationMixerKey) -> Result<(), AnimationError> {
        self.animation_mixer_mut(mixer)?.pause();
        Ok(())
    }

    pub fn stop_animation(&mut self, mixer: AnimationMixerKey) -> Result<(), AnimationError> {
        let clip = {
            let mixer = self.animation_mixer_mut(mixer)?;
            mixer.stop();
            mixer.clip().clone()
        };
        self.apply_animation_clip(&clip, 0.0);
        Ok(())
    }

    pub fn seek_animation(
        &mut self,
        mixer: AnimationMixerKey,
        time_seconds: f32,
    ) -> Result<(), AnimationError> {
        let (clip, time_seconds) = {
            let mixer = self.animation_mixer_mut(mixer)?;
            mixer.seek(time_seconds);
            (mixer.clip().clone(), mixer.time_seconds())
        };
        self.apply_animation_clip(&clip, time_seconds);
        Ok(())
    }

    pub fn set_animation_speed(
        &mut self,
        mixer: AnimationMixerKey,
        speed: f32,
    ) -> Result<(), AnimationError> {
        self.animation_mixer_mut(mixer)?.set_speed(speed);
        Ok(())
    }

    pub fn set_animation_loop_mode(
        &mut self,
        mixer: AnimationMixerKey,
        loop_mode: AnimationLoopMode,
    ) -> Result<(), AnimationError> {
        self.animation_mixer_mut(mixer)?.set_loop_mode(loop_mode);
        Ok(())
    }

    pub fn update_animation(
        &mut self,
        mixer: AnimationMixerKey,
        delta_seconds: f32,
    ) -> Result<(), AnimationError> {
        let (clip, time_seconds, was_playing) = {
            let mixer = self.animation_mixer_mut(mixer)?;
            let was_playing = mixer.state() == AnimationPlaybackState::Playing;
            mixer.advance(delta_seconds);
            (mixer.clip().clone(), mixer.time_seconds(), was_playing)
        };
        if was_playing {
            self.apply_animation_clip(&clip, time_seconds);
        }
        Ok(())
    }

    fn animation_mixer_mut(
        &mut self,
        mixer: AnimationMixerKey,
    ) -> Result<&mut AnimationMixer, AnimationError> {
        let mixer_state = self
            .animation_mixers
            .get_mut(mixer)
            .ok_or(AnimationError::MixerNotFound(mixer))?;
        if mixer_state.is_stale() {
            return Err(AnimationError::StaleMixer(mixer));
        }
        Ok(mixer_state)
    }

    fn apply_animation_clip(&mut self, clip: &crate::animation::AnimationClip, time_seconds: f32) {
        let mut changed = false;
        for channel in clip.channels() {
            changed |= self.apply_animation_channel(channel, time_seconds);
        }
        if changed {
            self.structure_revision = self.structure_revision.saturating_add(1);
        }
    }

    fn apply_animation_channel(&mut self, channel: &AnimationChannel, time_seconds: f32) -> bool {
        let Some(node) = self.nodes.get_mut(channel.target_node()) else {
            return false;
        };
        let before = node.transform;
        let mut transform = before;
        match channel.target() {
            AnimationTarget::Translation => {
                let Some(value) = channel.sample_vec3(time_seconds) else {
                    return false;
                };
                transform.translation = value;
            }
            AnimationTarget::Scale => {
                let Some(value) = channel.sample_vec3(time_seconds) else {
                    return false;
                };
                transform.scale = value;
            }
            AnimationTarget::Rotation => {
                let Some(value) = channel.sample_quat(time_seconds) else {
                    return false;
                };
                transform.rotation = value;
            }
            AnimationTarget::Weights => {
                let Some(weights) = channel.sample_weights(time_seconds) else {
                    return false;
                };
                return self.set_morph_weights_unchecked(channel.target_node(), weights);
            }
        }
        if before == transform {
            return false;
        }
        node.transform = Transform { ..transform };
        true
    }
}