summaryrefslogtreecommitdiff
path: root/import/src/animation.rs
diff options
context:
space:
mode:
Diffstat (limited to 'import/src/animation.rs')
-rw-r--r--import/src/animation.rs133
1 files changed, 133 insertions, 0 deletions
diff --git a/import/src/animation.rs b/import/src/animation.rs
new file mode 100644
index 0000000..ca049e5
--- /dev/null
+++ b/import/src/animation.rs
@@ -0,0 +1,133 @@
+/*
+ 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, vec3a};
+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: Animation<'_>,
+ store: &ResourceStore,
+ transform: Affine3A,
+ 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 joint_index_to_ibm.contains_key(&node) || transform != Affine3A::IDENTITY {
+ let t = transform
+ * joint_index_to_ibm
+ .get(&node)
+ .copied()
+ .unwrap_or_default()
+ .inverse();
+ let (_, rot, _) = t.to_scale_rotation_translation();
+ match reader.read_outputs().unwrap() {
+ ReadOutputs::Translations(iter) => iter
+ .flat_map(|[x, y, z]| (t.matrix3 * vec3a(x, y, z)).to_array())
+ .collect(),
+ ReadOutputs::Rotations(iter) => iter
+ .into_f32()
+ .map(Quat::from_array)
+ .map(|q| q * rot)
+ .flat_map(|q| q.to_array())
+ .collect(),
+ // ReadOutputs::Scales(iter) => iter
+ // .flat_map(|[x, y, z]| (t.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),
+ })
+}