oxygengine_composite_renderer/
mesh_animation_asset_protocol.rs

1use anims::phase::Phase;
2use core::{
3    assets::protocol::{AssetLoadResult, AssetProtocol},
4    Ignite, Scalar,
5};
6use serde::{Deserialize, Serialize};
7use std::{collections::HashMap, str::from_utf8};
8
9#[derive(Ignite, Debug, Clone, Serialize, Deserialize)]
10pub struct MeshAnimationSequence {
11    #[serde(default)]
12    pub submesh_alpha: HashMap<usize, Phase>,
13    #[serde(default)]
14    pub submesh_order: HashMap<usize, Phase>,
15    #[serde(default)]
16    pub bone_position_x: HashMap<String, Phase>,
17    #[serde(default)]
18    pub bone_position_y: HashMap<String, Phase>,
19    #[serde(default)]
20    pub bone_rotation: HashMap<String, Phase>,
21    #[serde(default)]
22    pub bone_scale_x: HashMap<String, Phase>,
23    #[serde(default)]
24    pub bone_scale_y: HashMap<String, Phase>,
25    #[serde(skip)]
26    #[ignite(ignore)]
27    length: Scalar,
28}
29
30impl MeshAnimationSequence {
31    pub fn initialize(&mut self) {
32        self.length = 0.0;
33        for key_frames in self.submesh_alpha.values() {
34            self.length = self.length.max(key_frames.duration());
35        }
36        for key_frames in self.submesh_order.values() {
37            self.length = self.length.max(key_frames.duration());
38        }
39        for key_frames in self.bone_position_x.values() {
40            self.length = self.length.max(key_frames.duration());
41        }
42        for key_frames in self.bone_position_y.values() {
43            self.length = self.length.max(key_frames.duration());
44        }
45        for key_frames in self.bone_rotation.values() {
46            self.length = self.length.max(key_frames.duration());
47        }
48        for key_frames in self.bone_scale_x.values() {
49            self.length = self.length.max(key_frames.duration());
50        }
51        for key_frames in self.bone_scale_y.values() {
52            self.length = self.length.max(key_frames.duration());
53        }
54    }
55
56    pub fn length(&self) -> Scalar {
57        self.length
58    }
59
60    pub fn sample_submesh_alpha(&self, time: Scalar, index: usize, current: Scalar) -> Scalar {
61        Self::sample_indexed(&self.submesh_alpha, time, index, current)
62    }
63
64    pub fn sample_submesh_order(&self, time: Scalar, index: usize, current: Scalar) -> Scalar {
65        Self::sample_indexed(&self.submesh_order, time, index, current)
66    }
67
68    pub fn sample_bone_position_x(&self, time: Scalar, name: &str, current: Scalar) -> Scalar {
69        Self::sample_named(&self.bone_position_x, time, name, current)
70    }
71
72    pub fn sample_bone_position_y(&self, time: Scalar, name: &str, current: Scalar) -> Scalar {
73        Self::sample_named(&self.bone_position_y, time, name, current)
74    }
75
76    pub fn sample_bone_rotation(&self, time: Scalar, name: &str, current: Scalar) -> Scalar {
77        Self::sample_named(&self.bone_rotation, time, name, current)
78    }
79
80    pub fn sample_bone_scale_x(&self, time: Scalar, name: &str, current: Scalar) -> Scalar {
81        Self::sample_named(&self.bone_scale_x, time, name, current)
82    }
83
84    pub fn sample_bone_scale_y(&self, time: Scalar, name: &str, current: Scalar) -> Scalar {
85        Self::sample_named(&self.bone_scale_y, time, name, current)
86    }
87
88    fn sample_key_frames(key_frames: &Phase, time: Scalar, current: Scalar) -> Scalar {
89        if key_frames.points().is_empty() {
90            current
91        } else {
92            key_frames.sample(time)
93        }
94    }
95
96    fn sample_indexed(
97        database: &HashMap<usize, Phase>,
98        time: Scalar,
99        index: usize,
100        current: Scalar,
101    ) -> Scalar {
102        database
103            .get(&index)
104            .map(|key_frames| Self::sample_key_frames(key_frames, time, current))
105            .unwrap_or(current)
106    }
107
108    fn sample_named(
109        database: &HashMap<String, Phase>,
110        time: Scalar,
111        name: &str,
112        current: Scalar,
113    ) -> Scalar {
114        database
115            .get(name)
116            .map(|key_frames| Self::sample_key_frames(key_frames, time, current))
117            .unwrap_or(current)
118    }
119}
120
121#[derive(Ignite, Debug, Clone, Serialize, Deserialize)]
122pub struct MeshAnimation {
123    pub sequences: HashMap<String, MeshAnimationSequence>,
124}
125
126impl MeshAnimation {
127    pub fn initialize(&mut self) {
128        for seq in self.sequences.values_mut() {
129            seq.initialize();
130        }
131    }
132}
133
134pub struct MeshAnimationAsset(MeshAnimation);
135
136impl MeshAnimationAsset {
137    pub fn animation(&self) -> &MeshAnimation {
138        &self.0
139    }
140}
141
142pub struct MeshAnimationAssetProtocol;
143
144impl AssetProtocol for MeshAnimationAssetProtocol {
145    fn name(&self) -> &str {
146        "mesh-anim"
147    }
148
149    fn on_load_with_path(&mut self, path: &str, data: Vec<u8>) -> AssetLoadResult {
150        let mut anim = if path.ends_with(".json") {
151            let data = from_utf8(&data).unwrap();
152            serde_json::from_str::<MeshAnimation>(data).unwrap()
153        } else if path.ends_with(".yaml") {
154            let data = from_utf8(&data).unwrap();
155            serde_yaml::from_str::<MeshAnimation>(data).unwrap()
156        } else {
157            bincode::deserialize::<MeshAnimation>(&data).unwrap()
158        };
159        anim.initialize();
160        AssetLoadResult::Data(Box::new(MeshAnimationAsset(anim)))
161    }
162
163    // on_load_with_path() handles loading so this is not needed, so we just make it unreachable.
164    fn on_load(&mut self, _data: Vec<u8>) -> AssetLoadResult {
165        unreachable!()
166    }
167}