smpl_gloss_integration/
scene.rs1use 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"];
24pub 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}
30impl 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 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}