summaryrefslogtreecommitdiff
path: root/world/src/animation.rs
diff options
context:
space:
mode:
Diffstat (limited to 'world/src/animation.rs')
-rw-r--r--world/src/animation.rs102
1 files changed, 102 insertions, 0 deletions
diff --git a/world/src/animation.rs b/world/src/animation.rs
new file mode 100644
index 0000000..00c716f
--- /dev/null
+++ b/world/src/animation.rs
@@ -0,0 +1,102 @@
+/*
+ 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 gltf::{
+ Animation,
+ animation::{Property, util::ReadOutputs},
+ buffer::Data,
+};
+use log::{debug, info};
+use std::collections::BTreeMap;
+use weareshared::{
+ packets::Resource,
+ resources::{AnimationChannel, AnimationPart},
+ store::ResourceStore,
+};
+
+pub fn import_animation<'a>(
+ a: Animation<'a>,
+ store: &ResourceStore,
+ 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> = 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),
+ })
+}