awsm-renderer 0.1.7

awsm-renderer
Documentation
use glam::{Quat, Vec3};

use crate::{
    animation::{
        AnimationClip, AnimationData, AnimationKey, AnimationMorphKey, AnimationPlayer,
        AnimationSampler, TransformAnimation, VertexAnimation,
    },
    buffer::helpers::u8_to_f32_vec,
    gltf::{
        buffers::accessor::accessor_to_bytes,
        error::{AwsmGltfError, Result},
    },
    meshes::morphs::{GeometryMorphKey, MaterialMorphKey},
    transforms::TransformKey,
    AwsmRenderer,
};

use super::GltfPopulateContext;

impl AwsmRenderer {
    pub(super) fn populate_gltf_node_animation<'a, 'b: 'a, 'c: 'a>(
        &'a mut self,
        ctx: &'c GltfPopulateContext,
        gltf_node: &'b gltf::Node<'b>,
    ) -> Result<()> {
        let transform_key = ctx
            .key_lookups
            .lock()
            .unwrap()
            .node_index_to_transform
            .get(&gltf_node.index())
            .cloned()
            .unwrap();

        if let Some(node_samplers) = ctx.node_animation_samplers.get(&gltf_node.index()) {
            if let Some(sampler_ref) = node_samplers.translation {
                self.populate_gltf_animation_transform_translation(
                    ctx,
                    ctx.resolve_animation_sampler(sampler_ref)?,
                    transform_key,
                )?;
            }

            if let Some(sampler_ref) = node_samplers.rotation {
                self.populate_gltf_animation_transform_rotation(
                    ctx,
                    ctx.resolve_animation_sampler(sampler_ref)?,
                    transform_key,
                )?;
            }

            if let Some(sampler_ref) = node_samplers.scale {
                self.populate_gltf_animation_transform_scale(
                    ctx,
                    ctx.resolve_animation_sampler(sampler_ref)?,
                    transform_key,
                )?;
            }
        }

        for child in gltf_node.children() {
            self.populate_gltf_node_animation(ctx, &child)?;
        }

        Ok(())
    }

    pub(super) fn populate_gltf_animation_morph<'a, 'b: 'a, 'c: 'a>(
        &'a mut self,
        ctx: &'c GltfPopulateContext,
        gltf_sampler: gltf::animation::Sampler<'b>,
        geometry_morph_key: Option<GeometryMorphKey>,
        material_morph_key: Option<MaterialMorphKey>,
    ) -> Result<Vec<AnimationKey>> {
        let mut morph_keys = Vec::new();
        let mut animation_keys = Vec::new();

        if let Some(morph_key) = geometry_morph_key {
            morph_keys.push(morph_key.into());
        }

        if let Some(morph_key) = material_morph_key {
            morph_keys.push(morph_key.into());
        }

        for morph_key in morph_keys {
            let targets_len = match morph_key {
                AnimationMorphKey::Geometry(morph_key) => {
                    self.meshes
                        .morphs
                        .geometry
                        .get_info(morph_key)
                        .map_err(|_| AwsmGltfError::MissingMorphForAnimation)?
                        .targets_len
                }
                AnimationMorphKey::Material(morph_key) => {
                    self.meshes
                        .morphs
                        .material
                        .get_info(morph_key)
                        .map_err(|_| AwsmGltfError::MissingMorphForAnimation)?
                        .targets_len
                }
            };

            let times = sampler_timestamps(ctx, &gltf_sampler)?;
            let duration = (times.last().copied().unwrap_or(0.0) - times[0]) as f64;
            let values = accessor_to_bytes(&gltf_sampler.output(), &ctx.data.buffers.raw)?;
            let values = u8_to_f32_vec(&values);

            let values = values
                .chunks(targets_len)
                .map(|chunk| AnimationData::Vertex(VertexAnimation::new(chunk.to_vec())))
                .collect();

            let sampler = match gltf_sampler.interpolation() {
                gltf::animation::Interpolation::Linear => {
                    AnimationSampler::Linear { times, values }
                }
                gltf::animation::Interpolation::Step => AnimationSampler::Step { times, values },
                gltf::animation::Interpolation::CubicSpline => {
                    let mut in_tangents = Vec::with_capacity(values.len() / 3);
                    let mut spline_vertices = Vec::with_capacity(values.len() / 3);
                    let mut out_tangents = Vec::with_capacity(values.len() / 3);

                    for x in values.chunks_exact(3) {
                        in_tangents.push(x[0].clone());
                        spline_vertices.push(x[1].clone());
                        out_tangents.push(x[2].clone());
                    }

                    AnimationSampler::CubicSpline {
                        times,
                        in_tangents,
                        values: spline_vertices,
                        out_tangents,
                    }
                }
            };

            let clip = AnimationClip::new(Some("morph".to_string()), duration, sampler);

            let player = AnimationPlayer::new(clip);

            animation_keys.push(self.animations.insert_morph(player, morph_key));
        }

        Ok(animation_keys)
    }

    fn populate_gltf_animation_transform_translation<'a, 'b: 'a, 'c: 'a>(
        &'a mut self,
        ctx: &'c GltfPopulateContext,
        gltf_sampler: gltf::animation::Sampler<'b>,
        transform_key: TransformKey,
    ) -> Result<AnimationKey> {
        let clip = gltf_animation_clip_transform(ctx, &gltf_sampler, TransformTarget::Translation)?;
        let player = AnimationPlayer::new(clip);

        Ok(self.animations.insert_transform(player, transform_key))
    }

    fn populate_gltf_animation_transform_rotation<'a, 'b: 'a, 'c: 'a>(
        &'a mut self,
        ctx: &'c GltfPopulateContext,
        gltf_sampler: gltf::animation::Sampler<'b>,
        transform_key: TransformKey,
    ) -> Result<AnimationKey> {
        let clip = gltf_animation_clip_transform(ctx, &gltf_sampler, TransformTarget::Rotation)?;
        let player = AnimationPlayer::new(clip);

        Ok(self.animations.insert_transform(player, transform_key))
    }

    fn populate_gltf_animation_transform_scale<'a, 'b: 'a, 'c: 'a>(
        &'a mut self,
        ctx: &'c GltfPopulateContext,
        gltf_sampler: gltf::animation::Sampler<'b>,
        transform_key: TransformKey,
    ) -> Result<AnimationKey> {
        let clip = gltf_animation_clip_transform(ctx, &gltf_sampler, TransformTarget::Scale)?;
        let player = AnimationPlayer::new(clip);

        Ok(self.animations.insert_transform(player, transform_key))
    }
}

fn sampler_timestamps(
    ctx: &GltfPopulateContext,
    gltf_sampler: &gltf::animation::Sampler,
) -> Result<Vec<f64>> {
    let bytes = accessor_to_bytes(&gltf_sampler.input(), &ctx.data.buffers.raw)?;
    Ok(u8_to_f32_vec(&bytes)
        .into_iter()
        .map(|v| v as f64)
        .collect())
}

enum TransformTarget {
    Translation,
    Rotation,
    Scale,
}

impl TransformTarget {
    fn as_str(&self) -> &'static str {
        match self {
            TransformTarget::Translation => "translation",
            TransformTarget::Rotation => "rotation",
            TransformTarget::Scale => "scale",
        }
    }

    fn chunk_size(&self) -> usize {
        match self {
            TransformTarget::Translation => 3,
            TransformTarget::Rotation => 4,
            TransformTarget::Scale => 3,
        }
    }
}

fn gltf_animation_clip_transform(
    ctx: &GltfPopulateContext,
    gltf_sampler: &gltf::animation::Sampler,
    target: TransformTarget,
) -> Result<AnimationClip> {
    let times = sampler_timestamps(ctx, gltf_sampler)?;
    let duration = (times.last().copied().unwrap_or(0.0) - times[0]) as f64;
    let values = accessor_to_bytes(&gltf_sampler.output(), &ctx.data.buffers.raw)?;
    let values = u8_to_f32_vec(&values);

    let values = values
        .chunks(target.chunk_size())
        .map(|chunk| {
            AnimationData::Transform(match target {
                TransformTarget::Translation => {
                    TransformAnimation::new_translation(Vec3::from_slice(chunk))
                }
                TransformTarget::Rotation => {
                    TransformAnimation::new_rotation(Quat::from_slice(chunk))
                }
                TransformTarget::Scale => TransformAnimation::new_scale(Vec3::from_slice(chunk)),
            })
        })
        .collect();

    let sampler = match gltf_sampler.interpolation() {
        gltf::animation::Interpolation::Linear => AnimationSampler::Linear { times, values },
        gltf::animation::Interpolation::Step => AnimationSampler::Step { times, values },
        gltf::animation::Interpolation::CubicSpline => {
            let mut in_tangents = Vec::with_capacity(values.len() / 3);
            let mut spline_vertices = Vec::with_capacity(values.len() / 3);
            let mut out_tangents = Vec::with_capacity(values.len() / 3);

            for x in values.chunks_exact(3) {
                in_tangents.push(x[0].clone());
                spline_vertices.push(x[1].clone());
                out_tangents.push(x[2].clone());
            }

            AnimationSampler::CubicSpline {
                times,
                in_tangents,
                values: spline_vertices,
                out_tangents,
            }
        }
    };

    Ok(AnimationClip::new(
        Some(format!("transform {}", target.as_str())),
        duration,
        sampler,
    ))
}