summaryrefslogtreecommitdiff
path: root/world/src/animation.rs
blob: 34733715097e4eb6e6658bbcb8d49ff8b9064b3b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/*
    wearechat - generic multiplayer game with voip
    Copyright (C) 2025 metamuffin

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, version 3 of the License only.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/
use anyhow::Result;
use glam::Quat;
use gltf::{
    Animation,
    animation::{Property, util::ReadOutputs},
    buffer::Data,
};
use log::{debug, info};
use std::collections::BTreeMap;
use weareshared::{
    Affine3A,
    packets::Resource,
    resources::{AnimationChannel, AnimationPart},
    store::ResourceStore,
};

pub fn import_animation<'a>(
    a: Animation<'a>,
    store: &ResourceStore,
    joint_index_to_ibm: &BTreeMap<usize, Affine3A>,
    joint_index_to_arm_index: &BTreeMap<usize, usize>,
    node_to_meshes: &BTreeMap<usize, Vec<usize>>,
    buffers: &[Data],
) -> Result<Resource<AnimationPart>> {
    let mut max_time = 0f32;
    let mut channels = Vec::new();
    for c in a.channels() {
        let node = c.target().node().index();
        let reader = c.reader(|i| Some(&buffers[i.index()].0));
        let inputs: Vec<f32> = reader.read_inputs().unwrap().collect::<Vec<f32>>();
        let outputs: Vec<f32> = if let Some(ibm) = joint_index_to_ibm.get(&node) {
            debug!("pre-applying inverse bind matricies");
            let ibm = ibm.inverse();
            // eprintln!("{:#?}", ibm);
            let (_, ibm_rot, _) = ibm.to_scale_rotation_translation();
            match reader.read_outputs().unwrap() {
                // ReadOutputs::Translations(iter) => iter
                //     .flat_map(|[x, y, z]| (ibm.matrix3 * vec3a(x, y, z)).to_array())
                //     .collect(),
                ReadOutputs::Rotations(iter) => iter
                    .into_f32()
                    .map(Quat::from_array)
                    .map(|q| ibm_rot.mul_quat(q))
                    .map(|q| q.to_array())
                    .flatten()
                    .collect(),
                // ReadOutputs::Scales(iter) => iter
                //     .flat_map(|[x, y, z]| (ibm.matrix3 * vec3a(x, y, z)).to_array())
                //     .collect(),
                ReadOutputs::MorphTargetWeights(iter) => iter.into_f32().collect(),
                _ => continue,
            }
        } else {
            match reader.read_outputs().unwrap() {
                ReadOutputs::Translations(iter) => iter.flatten().collect(),
                ReadOutputs::Rotations(iter) => iter.into_f32().flatten().collect(),
                ReadOutputs::Scales(iter) => iter.flatten().collect(),
                ReadOutputs::MorphTargetWeights(iter) => iter.into_f32().collect(),
            }
        };
        for x in &inputs {
            max_time = max_time.max(*x)
        }
        let time = store.set(&inputs)?;
        let value = store.set(&outputs)?;

        if let Some(&m) = joint_index_to_arm_index.get(&node) {
            let a = 0; // TODO
            let mut ch = AnimationChannel::default();
            match c.target().property() {
                Property::Translation => ch.t_joint_translation = Some((a, m as u32)),
                Property::Rotation => ch.t_joint_rotation = Some((a, m as u32)),
                Property::Scale => ch.t_joint_scale = Some((a, m as u32)),
                Property::MorphTargetWeights => continue,
            }
            ch.time = Some(time.clone());
            ch.value = Some(value.clone());
            debug!(
                "animation channel {:?} of joint {m} of armature {a} with {} time and {} component values",
                c.target().property(),
                inputs.len(),
                outputs.len()
            );
            channels.push(ch);
        }
        if let Some(meshes) = node_to_meshes.get(&node) {
            for &m in meshes {
                let mut ch = AnimationChannel::default();
                match c.target().property() {
                    Property::Translation => ch.t_mesh_translation = Some(m as u32),
                    Property::Rotation => ch.t_mesh_rotation = Some(m as u32),
                    Property::Scale => ch.t_mesh_scale = Some(m as u32),
                    Property::MorphTargetWeights => continue,
                }
                ch.time = Some(time.clone());
                ch.value = Some(value.clone());
                debug!(
                    "animation channel {:?} of mesh {m} with {} time and {} component values",
                    c.target().property(),
                    inputs.len(),
                    outputs.len()
                );
                channels.push(ch);
            }
        }
    }
    info!("adding animation with {} channels", channels.len());
    store.set(&AnimationPart {
        name: a.name().map(|n| n.to_string()),
        channel: channels,
        duration: Some(max_time),
    })
}