smpl_gloss_integration/
scene.rs

1use crate::{codec::SmplCodecGloss, components::GlossInterop};
2use gloss_hecs::EntityBuilder;
3use gloss_renderer::{
4    components::{MeshColorType, Name, VisMesh},
5    scene::Scene,
6};
7use log::info;
8use smpl_core::{
9    codec::{
10        codec::SmplCodec,
11        scene::{CameraTrack, McsCodec, SmplBody},
12    },
13    common::{
14        animation::{AnimWrap, Animation, AnimationConfig, AnimationRunner},
15        betas::Betas,
16        pose::Pose,
17        pose_override::PoseOverride,
18        smpl_params::SmplParams,
19    },
20};
21use smpl_utils::numerical::hex_to_rgb_f32;
22use std::time::Duration;
23const COLOR_CODES: [&str; 4] = ["#BAC2F7", "#ACECE1", "#FFEF9E", "#72B0C5"];
24/// creates a ``Vec<gloss_hecs::EntityBuilder>`` from the ``McsCodec``
25pub trait McsCodecGloss {
26    fn from_scene(scene: &Scene) -> Self;
27    fn to_entity_builders(&mut self, with_colors: bool) -> Vec<EntityBuilder>;
28    fn insert_into_scene(&mut self, scene: &mut Scene, with_colors: bool);
29}
30/// Trait implementation for `McsCodec`
31impl McsCodecGloss for McsCodec {
32    fn from_scene(scene: &Scene) -> Self {
33        let mut smpl_bodies = Vec::new();
34        let mut camera_track_query = scene.world.query::<&CameraTrack>();
35        let camera_track = camera_track_query.iter().next().map(|c| c.1.clone());
36        let (num_frames, frame_rate) = if let Ok(scene_animation) = scene.get_resource::<&SceneAnimation>() {
37            (scene_animation.num_frames, Some(scene_animation.config.fps))
38        } else {
39            (1, None)
40        };
41        let mut query_state = scene.world.query::<&SmplParams>();
42        for (entity, _) in query_state.iter() {
43            let smpl_codec = SmplCodec::from_entity(&entity, scene, None);
44            let smpl_interval = if let Ok(animation) = scene.get_comp::<&Animation>(&entity) {
45                let animation_num_frames = animation.num_animation_frames();
46                let animation_start_offset = animation.start_offset;
47                [animation_start_offset, animation_start_offset + animation_num_frames].to_vec()
48            } else {
49                [0, 1].to_vec()
50            };
51            let smpl_body = SmplBody {
52                frame_presence: smpl_interval,
53                codec: smpl_codec,
54            };
55            smpl_bodies.push(smpl_body);
56        }
57        info!(
58            "Created McsCodec from scene: Num frames - {}, Num bodies - {}, Camera present - {}",
59            num_frames,
60            smpl_bodies.len(),
61            camera_track.is_some()
62        );
63        Self {
64            num_frames,
65            frame_rate,
66            smpl_bodies,
67            camera_track,
68        }
69    }
70    #[allow(clippy::cast_precision_loss)]
71    #[allow(clippy::cast_sign_loss)]
72    fn to_entity_builders(&mut self, with_colors: bool) -> Vec<EntityBuilder> {
73        let mut builders: Vec<EntityBuilder> = Vec::new();
74        if let Some(camera_track) = &self.camera_track {
75            let mut camera_builder = EntityBuilder::new();
76            camera_builder.add(Name("TrackedCamera".to_string()));
77            camera_builder.add(camera_track.clone());
78            builders.push(camera_builder);
79        }
80        let camera_num_frames = self.camera_track.as_ref().map(|camera_track| camera_track.num_frames().max(1));
81        for (i, smpl_body) in self.smpl_bodies.iter().enumerate() {
82            let smpl_num_frames = smpl_body.codec.frame_count as usize;
83            self.frame_rate = smpl_body.codec.frame_rate;
84            let is_static = smpl_num_frames == 1;
85            let start_offset = smpl_body.frame_presence[0];
86            if let Some(camera_num_frames) = camera_num_frames {
87                assert!(
88                    start_offset + smpl_num_frames <= camera_num_frames,
89                    "The number of frames in the smpl file should be less than or equal to the number of frames in the camera track"
90                );
91            }
92            let color = if with_colors {
93                hex_to_rgb_f32(COLOR_CODES[i % COLOR_CODES.len()])
94            } else {
95                (1.0, 1.0, 1.0)
96            };
97            let mut builder = EntityBuilder::new();
98            let smpl_params = SmplParams::new_from_smpl_codec(&smpl_body.codec);
99            if is_static {
100                if let Some(pose) = Pose::new_from_smpl_codec(&smpl_body.codec) {
101                    builder.add(pose);
102                }
103            } else if let Some(mut anim) = Animation::new_from_smpl_codec(&smpl_body.codec, AnimWrap::Clamp) {
104                anim.start_offset = start_offset;
105                builder.add(anim);
106            }
107            info!("Found smpl_params in the .smpl file");
108            builder.add(smpl_params);
109            if let Some(mut betas) = Betas::new_from_smpl_codec(&smpl_body.codec) {
110                info!("Found betas in the .smpl file");
111                let trimmed_betas = betas.betas.clone().slice([..10]);
112                betas.betas = trimmed_betas;
113                builder.add(betas);
114            }
115            let pose_override = PoseOverride::allow_all();
116            builder.add(pose_override);
117            builder.add(GlossInterop { with_uv: true });
118            builder.add(VisMesh {
119                solid_color: nalgebra::Vector4::<f32>::new(color.0, color.1, color.2, 1.0),
120                color_type: MeshColorType::Texture,
121                ..Default::default()
122            });
123            builder.add(Name(format!("avatar-{:02}", i + 1)));
124            builders.push(builder);
125        }
126        builders
127    }
128    fn insert_into_scene(&mut self, scene: &mut Scene, with_colors: bool) {
129        let builders = self.to_entity_builders(with_colors);
130        for mut builder in builders {
131            if !builder.has::<Betas>() {
132                builder.add(Betas::default());
133            }
134            let gloss_interop = GlossInterop::default();
135            let name = builder.get::<&Name>().unwrap().0.clone();
136            scene.get_or_create_entity(&name).insert_builder(builder).insert(gloss_interop);
137        }
138    }
139}
140#[derive(Clone, Default)]
141pub struct SceneAnimation {
142    pub num_frames: usize,
143    pub runner: AnimationRunner,
144    pub config: AnimationConfig,
145}
146impl SceneAnimation {
147    pub fn new(num_frames: usize) -> Self {
148        Self {
149            num_frames,
150            runner: AnimationRunner::default(),
151            config: AnimationConfig::default(),
152        }
153    }
154    pub fn new_with_fps(num_frames: usize, fps: f32) -> Self {
155        Self {
156            num_frames,
157            runner: AnimationRunner::default(),
158            config: AnimationConfig { fps, ..Default::default() },
159        }
160    }
161    pub fn new_with_config(num_frames: usize, config: AnimationConfig) -> Self {
162        Self {
163            num_frames,
164            runner: AnimationRunner::default(),
165            config,
166        }
167    }
168    #[allow(clippy::cast_precision_loss)]
169    pub fn duration(&self) -> Duration {
170        Duration::from_secs_f32(self.num_frames as f32 / self.config.fps)
171    }
172    pub fn get_cur_time(&self) -> Duration {
173        self.runner.anim_current_time
174    }
175    pub fn set_cur_time_as_sec(&mut self, time_sec: f32) {
176        self.runner.anim_current_time = Duration::from_secs_f32(time_sec);
177    }
178    pub fn is_finished(&self) -> bool {
179        self.config.wrap_behaviour == AnimWrap::Clamp && self.runner.anim_current_time >= self.duration()
180    }
181    /// Advances the animation by the amount of time elapsed since last time we
182    /// got the current pose
183    pub fn advance(&mut self, dt_raw: Duration, first_time: bool) {
184        let duration = self.duration();
185        let runner = &mut self.runner;
186        let config = &self.config;
187        let mut dt = dt_raw;
188        if first_time {
189            dt = Duration::ZERO;
190        }
191        let will_overflow = runner.anim_current_time + dt > duration;
192        let will_underflow = runner.anim_current_time < dt && runner.anim_reversed;
193        if will_overflow || will_underflow {
194            if will_overflow {
195                match config.wrap_behaviour {
196                    AnimWrap::Clamp => {
197                        dt = Duration::ZERO;
198                        runner.anim_current_time = duration;
199                    }
200                    AnimWrap::Loop => {
201                        dt = Duration::from_secs_f64(dt.as_secs_f64() % duration.as_secs_f64());
202                        runner.anim_current_time = Duration::ZERO;
203                        runner.nr_repetitions += 1;
204                    }
205                    AnimWrap::Reverse => {
206                        dt = Duration::from_secs_f64(dt.as_secs_f64() % duration.as_secs_f64());
207                        runner.anim_current_time = duration;
208                        runner.anim_reversed = !runner.anim_reversed;
209                        runner.nr_repetitions += 1;
210                    }
211                }
212            } else {
213                match config.wrap_behaviour {
214                    AnimWrap::Clamp => {
215                        dt = Duration::ZERO;
216                        runner.anim_current_time = Duration::ZERO;
217                    }
218                    AnimWrap::Loop => {
219                        dt = Duration::from_secs_f64(dt.as_secs_f64() % duration.as_secs_f64());
220                        runner.anim_current_time = duration;
221                        runner.nr_repetitions += 1;
222                    }
223                    AnimWrap::Reverse => {
224                        dt = Duration::from_secs_f64(dt.as_secs_f64() % duration.as_secs_f64());
225                        runner.anim_reversed = !runner.anim_reversed;
226                        runner.nr_repetitions += 1;
227                    }
228                }
229            }
230        }
231        if runner.anim_reversed {
232            runner.anim_current_time = runner.anim_current_time.saturating_sub(dt);
233        } else {
234            runner.anim_current_time = runner.anim_current_time.saturating_add(dt);
235        }
236    }
237}