1use crate::components::{Follow, FollowParams, Follower, FollowerType, GlossInterop};
2use crate::conversions::update_entity_on_backend;
3use crate::scene::SceneAnimation;
4use crate::{codec::SmplCodecGloss, gltf::GltfCodecGloss};
5use burn::backend::{Candle, NdArray, Wgpu};
6use burn::{
7 prelude::*,
8 tensor::{Float, Int, Tensor},
9};
10use core::f32;
11use gloss_hecs::Entity;
12use gloss_hecs::{Changed, CommandBuffer};
13use gloss_renderer::plugin_manager::gui::{GuiWindow, GuiWindowType};
14use gloss_renderer::{
15 components::{ConfigChanges, ModelMatrix, PosLookat, Renderable, Verts},
16 geom::{Geom, PerVertexNormalsWeightingType},
17 plugin_manager::{
18 gui::{Checkbox, Selectable, Slider, Widgets},
19 Event, RunnerState,
20 },
21 scene::Scene,
22};
23use gloss_utils::abi_stable_aliases::std_types::{RNone, ROption, RString, RVec};
24use gloss_utils::tensor::BurnBackend;
25use gloss_utils::{
26 bshare::{ToBurn, ToNalgebraFloat, ToNalgebraInt, ToNdArray},
27 nshare::ToNalgebra,
28 tensor::{DynamicMatrixOps, DynamicTensorFloat2D, DynamicTensorInt2D},
29};
30use log::{info, warn};
31use nalgebra::{self as na};
32use smpl_rs::common::animation::AnimationConfig;
33use smpl_rs::common::smpl_model::{SmplCache, SmplModel};
34use smpl_rs::common::types::{GltfOutputType, UpAxis};
35use smpl_rs::common::{
36 animation::Animation,
37 betas::Betas,
38 expression::{Expression, ExpressionOffsets},
39 smpl_model::SmplCacheDynamic,
40};
41use smpl_rs::common::{
42 outputs::{SmplOutputPoseTDynamic, SmplOutputPosedDynamic},
43 pose::Pose,
44 pose_corrective::PoseCorrectiveDynamic,
45 pose_override::PoseOverride,
46 smpl_params::SmplParams,
47};
48use smpl_rs::conversions::pose_remap::PoseRemap;
49use smpl_utils::io::FileType;
50#[cfg(not(target_arch = "wasm32"))]
56pub extern "C" fn smpl_lazy_load_model(scene: &mut Scene, _runner: &mut RunnerState) {
57 let mut command_buffer = CommandBuffer::new();
58 {
59 let mut needs_loading = false;
60 let mut query_state = scene.world.query::<&SmplParams>();
61 for (_entity, smpl_params) in query_state.iter() {
62 let smpl_models = scene.get_resource::<&SmplCacheDynamic>().unwrap();
63 if !smpl_models.has_model(smpl_params.smpl_type, smpl_params.gender)
64 && smpl_models.has_lazy_loading(smpl_params.smpl_type, smpl_params.gender)
65 {
66 needs_loading = true;
67 }
68 }
69 if needs_loading {
70 let mut query_state = scene.world.query::<&SmplParams>();
71 for (_entity, smpl_params) in query_state.iter() {
72 let mut smpl_models = scene.get_resource::<&mut SmplCacheDynamic>().unwrap();
73 if !smpl_models.has_model(smpl_params.smpl_type, smpl_params.gender)
74 && smpl_models.has_lazy_loading(smpl_params.smpl_type, smpl_params.gender)
75 {
76 if let Some(path) = smpl_models.get_lazy_loading(smpl_params.smpl_type, smpl_params.gender).as_ref() {
77 smpl_models.add_model_from_type(smpl_params.smpl_type, path, smpl_params.gender, 300, 100);
78 }
79 }
80 }
81 }
82 }
83 command_buffer.run_on(&mut scene.world);
84}
85pub extern "C" fn smpl_auto_add_scene(scene: &mut Scene, _runner: &mut RunnerState) {
87 if !scene.has_resource::<SceneAnimation>() {
88 let mut selected_num_frames = 0;
89 let mut selected_fps = f32::MAX;
90 let mut anim_config = AnimationConfig::default();
91 let num_ents: usize;
92 {
93 let mut query_state = scene.world.query::<&Animation>().with::<&Renderable>();
94 num_ents = query_state.iter().len();
95 for (_, smpl_anim) in query_state.iter() {
96 let last_frame_idx = smpl_anim.num_animation_frames() + smpl_anim.start_offset;
97 selected_num_frames = selected_num_frames.max(last_frame_idx);
98 selected_fps = selected_fps.min(smpl_anim.config.fps);
99 anim_config = smpl_anim.config.clone();
100 }
101 }
102 let scene_anim = match num_ents {
103 0 => None,
104 1 => Some(SceneAnimation::new_with_config(selected_num_frames, anim_config)),
105 _ => Some(SceneAnimation::new_with_fps(selected_num_frames, selected_fps)),
106 };
107 if let Some(scene_anim) = scene_anim {
108 scene.add_resource(scene_anim);
109 }
110 }
111}
112pub extern "C" fn smpl_auto_add_follow(scene: &mut Scene, _runner: &mut RunnerState) {
114 let mut command_buffer = CommandBuffer::new();
115 {
116 let Ok(follower) = scene.get_resource::<&Follower>() else {
117 return;
118 };
119 let follow_all = follower.params.follow_all;
120 if !follow_all {
121 return;
122 }
123 let mut query_state = scene.world.query::<&GlossInterop>().without::<&Follow>();
124 for (entity, _) in query_state.iter() {
125 command_buffer.insert_one(entity, Follow);
126 }
127 }
128 command_buffer.run_on(&mut scene.world);
129}
130#[allow(clippy::cast_precision_loss)]
132pub extern "C" fn smpl_advance_anim(scene: &mut Scene, runner: &mut RunnerState) {
133 let mut command_buffer = CommandBuffer::new();
134 {
135 if !scene.has_resource::<SceneAnimation>() {
136 return;
137 }
138 let mut entities_with_anim = Vec::new();
139 {
140 let mut query_state = scene.world.query::<(&Animation, Changed<Animation>)>().with::<&SmplParams>();
141 for (entity, (smpl_anim, changed_anim)) in query_state.iter() {
142 if (!smpl_anim.runner.paused && !smpl_anim.runner.temporary_pause) || changed_anim {
143 entities_with_anim.push(entity);
144 }
145 }
146 }
147 let mut entities_within_interval = Vec::new();
148 let mut entities_outside_interval = Vec::new();
149 {
150 let mut scene_anim = scene.get_resource::<&mut SceneAnimation>().unwrap();
151 let current_global_time = scene_anim.runner.anim_current_time.as_secs_f32();
152 for entity in entities_with_anim.iter() {
153 let mut smpl_anim = scene.get_comp::<&mut Animation>(entity).unwrap();
154 let global_fps = scene_anim.config.fps;
155 smpl_anim.config.fps = global_fps;
156 let start_offset = smpl_anim.start_offset;
157 let is_within_interval = current_global_time >= (smpl_anim.start_offset as f32 / scene_anim.config.fps)
158 && current_global_time <= ((smpl_anim.start_offset + smpl_anim.num_animation_frames()) as f32 / scene_anim.config.fps);
159 if is_within_interval {
160 smpl_anim.set_cur_time_as_sec(current_global_time - (start_offset as f32 / global_fps));
161 entities_within_interval.push(*entity);
162 } else {
163 entities_outside_interval.push(*entity);
164 continue;
165 }
166 if !scene_anim.runner.paused && !scene_anim.runner.temporary_pause {
167 let is_added = smpl_anim.is_added();
168 smpl_anim.advance(runner.dt(), runner.is_first_time() || is_added);
169 }
170 let anim_frame = smpl_anim.get_current_pose();
171 command_buffer.insert_one(*entity, anim_frame);
172 if let Some(expression) = smpl_anim.get_current_expression() {
173 command_buffer.insert_one(*entity, expression);
174 }
175 }
176 if !scene_anim.runner.paused && !scene_anim.runner.temporary_pause && scene_anim.num_frames != 0 {
177 let is_added = scene_anim.is_added();
178 scene_anim.advance(runner.dt(), runner.is_first_time() || is_added);
179 }
180 }
181 for entity in entities_within_interval {
182 if !scene.world.has::<Renderable>(entity).unwrap() {
183 scene.world.insert_one(entity, Renderable).unwrap();
184 }
185 }
186 for entity in entities_outside_interval {
187 if scene.world.has::<Renderable>(entity).unwrap() {
188 scene.world.remove_one::<Renderable>(entity).unwrap();
189 }
190 }
191 runner.request_redraw();
192 }
193 command_buffer.run_on(&mut scene.world);
194}
195#[allow(clippy::similar_names)]
199#[allow(clippy::too_many_lines)]
200pub extern "C" fn smpl_betas_to_verts(scene: &mut Scene, _runner: &mut RunnerState) {
201 let mut command_buffer = CommandBuffer::new();
202 {
203 let smpl_models_dynamic = scene.get_resource::<&SmplCacheDynamic>().unwrap();
204 let changed_models = smpl_models_dynamic.is_changed();
205 let mut query_state = scene.world.query::<(&SmplParams, &Betas, Changed<Betas>, Changed<SmplParams>)>();
206 for (entity, (smpl_params, smpl_betas, changed_betas, changed_smpl_params)) in query_state.iter() {
207 if !changed_betas && !changed_smpl_params && !changed_models {
208 continue;
209 }
210 match &*smpl_models_dynamic {
211 SmplCacheDynamic::NdArray(model) => betas_to_verts_on_backend::<NdArray>(&mut command_buffer, entity, smpl_params, smpl_betas, model),
212 SmplCacheDynamic::Wgpu(model) => betas_to_verts_on_backend::<Wgpu>(&mut command_buffer, entity, smpl_params, smpl_betas, model),
213 SmplCacheDynamic::Candle(model) => betas_to_verts_on_backend::<Candle>(&mut command_buffer, entity, smpl_params, smpl_betas, model),
214 }
215 }
216 }
217 command_buffer.run_on(&mut scene.world);
218}
219#[allow(clippy::too_many_arguments)]
222fn betas_to_verts_on_backend<B: Backend>(
223 command_buffer: &mut CommandBuffer,
224 entity: Entity,
225 smpl_params: &SmplParams,
226 smpl_betas: &Betas,
227 smpl_models: &SmplCache<B>,
228) where
229 <B as Backend>::FloatTensorPrimitive<2>: Sync,
230 <B as Backend>::IntTensorPrimitive<2>: Sync,
231 B::QuantizedTensorPrimitive<1>: Sync,
232 B::QuantizedTensorPrimitive<2>: Sync,
233 B::QuantizedTensorPrimitive<3>: Sync,
234{
235 let smpl_model = smpl_models.get_model_ref(smpl_params.smpl_type, smpl_params.gender).unwrap();
236 let v_burn_merged = smpl_model.betas2verts(smpl_betas);
237 let joints_t_pose = smpl_model.verts2joints(v_burn_merged.clone());
238 let smpl_output = SmplOutputPoseTDynamic {
239 verts: v_burn_merged.clone(),
240 verts_without_expression: v_burn_merged,
241 joints: joints_t_pose,
242 };
243 command_buffer.insert_one(entity, smpl_output);
244}
245#[allow(clippy::similar_names)]
249pub extern "C" fn smpl_expression_offsets(scene: &mut Scene, _runner: &mut RunnerState) {
250 let mut command_buffer = CommandBuffer::new();
251 {
252 let smpl_models_dynamic = scene.get_resource::<&SmplCacheDynamic>().unwrap();
253 let changed_models = smpl_models_dynamic.is_changed();
254 let mut query_state = scene
255 .world
256 .query::<(&SmplParams, &Expression, Changed<Expression>, Changed<SmplParams>)>();
257 for (entity, (smpl_params, expression, changed_expression, changed_smpl_params)) in query_state.iter() {
258 if !changed_expression && !changed_smpl_params && !changed_models {
259 continue;
260 }
261 match &*smpl_models_dynamic {
262 SmplCacheDynamic::NdArray(smpl_models_ndarray) => {
263 expression_offsets_on_backend::<NdArray>(&mut command_buffer, entity, smpl_params, expression, smpl_models_ndarray);
264 }
265 SmplCacheDynamic::Wgpu(smpl_models_wgpu) => {
266 expression_offsets_on_backend::<Wgpu>(&mut command_buffer, entity, smpl_params, expression, smpl_models_wgpu);
267 }
268 SmplCacheDynamic::Candle(smpl_models_candle) => {
269 expression_offsets_on_backend::<Candle>(&mut command_buffer, entity, smpl_params, expression, smpl_models_candle);
270 }
271 }
272 }
273 }
274 command_buffer.run_on(&mut scene.world);
275}
276#[allow(clippy::too_many_arguments)]
279fn expression_offsets_on_backend<B: Backend>(
280 command_buffer: &mut CommandBuffer,
281 entity: Entity,
282 smpl_params: &SmplParams,
283 expression: &Expression,
284 smpl_models: &SmplCache<B>,
285) where
286 <B as Backend>::FloatTensorPrimitive<2>: Sync,
287 <B as Backend>::IntTensorPrimitive<2>: Sync,
288 B::QuantizedTensorPrimitive<1>: Sync,
289 B::QuantizedTensorPrimitive<2>: Sync,
290 B::QuantizedTensorPrimitive<3>: Sync,
291{
292 let smpl_model = smpl_models.get_model_ref(smpl_params.smpl_type, smpl_params.gender).unwrap();
293 let verts_offsets_merged = smpl_model.expression2offsets(expression);
294 let expr_offsets = ExpressionOffsets {
295 offsets: verts_offsets_merged,
296 };
297 command_buffer.insert_one(entity, expr_offsets);
298}
299#[allow(clippy::too_many_lines)]
303pub extern "C" fn smpl_expression_apply(scene: &mut Scene, _runner: &mut RunnerState) {
304 let mut command_buffer = CommandBuffer::new();
305 {
306 let smpl_models_dynamic = scene.get_resource::<&SmplCacheDynamic>().unwrap();
307 match &*smpl_models_dynamic {
308 SmplCacheDynamic::NdArray(smpl_models_nd) => {
309 apply_expression_on_backend::<NdArray>(scene, &mut command_buffer, smpl_models_nd);
310 }
311 SmplCacheDynamic::Wgpu(smpl_models_wgpu) => {
312 apply_expression_on_backend::<Wgpu>(scene, &mut command_buffer, smpl_models_wgpu);
313 }
314 SmplCacheDynamic::Candle(smpl_models_cndl) => {
315 apply_expression_on_backend::<Candle>(scene, &mut command_buffer, smpl_models_cndl);
316 }
317 }
318 }
319 command_buffer.run_on(&mut scene.world);
320}
321#[allow(clippy::too_many_arguments)]
324fn apply_expression_on_backend<B: Backend>(scene: &Scene, command_buffer: &mut CommandBuffer, smpl_models: &SmplCache<B>)
325where
326 <B as Backend>::FloatTensorPrimitive<2>: Sync,
327 <B as Backend>::IntTensorPrimitive<2>: Sync,
328 B::QuantizedTensorPrimitive<1>: Sync,
329 B::QuantizedTensorPrimitive<2>: Sync,
330 B::QuantizedTensorPrimitive<3>: Sync,
331{
332 let mut query_state = scene.world.query::<(
333 &SmplParams,
334 &mut SmplOutputPoseTDynamic<B>,
335 &ExpressionOffsets<B>,
336 Changed<SmplOutputPoseTDynamic<B>>,
337 Changed<ExpressionOffsets<B>>,
338 Changed<SmplParams>,
339 )>();
340 for (entity, (smpl_params, mut smpl_t_output, expression_offsets, changed_smpl_t, changed_expression, changed_params)) in query_state.iter() {
341 if !changed_smpl_t && !changed_expression && !changed_params {
342 continue;
343 }
344 let smpl_model = smpl_models.get_model_ref(smpl_params.smpl_type, smpl_params.gender).unwrap();
345 smpl_t_output.verts = smpl_t_output.verts_without_expression.clone() + expression_offsets.offsets.clone();
346 smpl_t_output.joints = smpl_model.verts2joints(smpl_t_output.verts.clone());
347 command_buffer.insert_one(entity, smpl_t_output.clone());
348 }
349}
350pub extern "C" fn smpl_pose_remap(scene: &mut Scene, _runner: &mut RunnerState) {
353 let mut command_buffer = CommandBuffer::new();
354 {
355 let mut query_state = scene.world.query::<(&Pose, &SmplParams, Changed<Pose>)>();
356 for (entity, (smpl_pose, smpl_params, changed_pose)) in query_state.iter() {
357 if !changed_pose {
358 continue;
359 }
360 let pose_remap = PoseRemap::new(smpl_pose.smpl_type, smpl_params.smpl_type);
361 let new_pose = pose_remap.remap(smpl_pose);
362 command_buffer.insert_one(entity, new_pose);
363 }
364 }
365 command_buffer.run_on(&mut scene.world);
366}
367pub extern "C" fn smpl_mask_pose(scene: &mut Scene, _runner: &mut RunnerState) {
369 let mut command_buffer = CommandBuffer::new();
370 {
371 let mut query_state = scene
372 .world
373 .query::<(&mut Pose, &mut PoseOverride, Changed<Pose>, Changed<PoseOverride>)>()
374 .with::<&SmplParams>();
375 for (_entity, (mut smpl_pose, mut pose_mask, changed_pose, changed_pose_mask)) in query_state.iter() {
376 if !changed_pose && !changed_pose_mask {
377 continue;
378 }
379 smpl_pose.apply_mask(&mut pose_mask);
380 }
381 }
382 command_buffer.run_on(&mut scene.world);
383}
384pub extern "C" fn smpl_make_dummy_pose(scene: &mut Scene, _runner: &mut RunnerState) {
387 let mut command_buffer = CommandBuffer::new();
388 {
389 let mut query_state = scene.world.query::<&SmplParams>().without::<&Pose>().without::<&Animation>();
390 for (entity, smpl_params) in query_state.iter() {
391 let pose = Pose::new_empty(UpAxis::Y, smpl_params.smpl_type);
392 command_buffer.insert_one(entity, pose);
393 }
394 }
395 command_buffer.run_on(&mut scene.world);
396}
397#[allow(clippy::similar_names)]
401#[allow(clippy::too_many_lines)]
402pub extern "C" fn smpl_compute_pose_correctives(scene: &mut Scene, _runner: &mut RunnerState) {
403 let mut command_buffer = CommandBuffer::new();
404 {
405 let smpl_models_dynamic = scene.get_resource::<&SmplCacheDynamic>().unwrap();
406 let smpl_models_changed = smpl_models_dynamic.is_changed();
407 match &*smpl_models_dynamic {
408 SmplCacheDynamic::NdArray(smpl_models_ndarray) => {
409 compute_pose_correctives_on_backend::<NdArray>(scene, &mut command_buffer, smpl_models_ndarray, smpl_models_changed);
410 }
411 SmplCacheDynamic::Wgpu(smpl_models_wgpu) => {
412 compute_pose_correctives_on_backend::<Wgpu>(scene, &mut command_buffer, smpl_models_wgpu, smpl_models_changed);
413 }
414 SmplCacheDynamic::Candle(smpl_models_candle) => {
415 compute_pose_correctives_on_backend::<Candle>(scene, &mut command_buffer, smpl_models_candle, smpl_models_changed);
416 }
417 }
418 }
419 command_buffer.run_on(&mut scene.world);
420}
421#[allow(clippy::too_many_arguments)]
424fn compute_pose_correctives_on_backend<B: Backend>(
425 scene: &Scene,
426 command_buffer: &mut CommandBuffer,
427 smpl_models: &SmplCache<B>,
428 smpl_models_changed: bool,
429) where
430 <B as Backend>::FloatTensorPrimitive<2>: Sync,
431 <B as Backend>::IntTensorPrimitive<2>: Sync,
432 B::QuantizedTensorPrimitive<1>: Sync,
433 B::QuantizedTensorPrimitive<2>: Sync,
434 B::QuantizedTensorPrimitive<3>: Sync,
435{
436 let mut query_state = scene.world.query::<(&SmplParams, &mut Pose, Changed<Pose>, Changed<SmplParams>)>();
437 for (entity, (smpl_params, smpl_pose, changed_pose, changed_smpl_params)) in query_state.iter() {
438 if (!changed_pose && !changed_smpl_params && !smpl_models_changed) || !smpl_params.enable_pose_corrective {
439 continue;
440 }
441 let smpl_model = smpl_models.get_model_ref(smpl_params.smpl_type, smpl_params.gender).unwrap();
442 let verts_offset = smpl_model.compute_pose_correctives(&smpl_pose);
443 command_buffer.insert_one(entity, PoseCorrectiveDynamic::<B> { verts_offset });
444 }
445}
446#[allow(clippy::too_many_lines)]
450pub extern "C" fn smpl_apply_pose(scene: &mut Scene, _runner: &mut RunnerState) {
451 let mut command_buffer = CommandBuffer::new();
452 {
453 let smpl_models_dynamic = scene.get_resource::<&SmplCacheDynamic>().unwrap();
454 match &*smpl_models_dynamic {
455 SmplCacheDynamic::NdArray(smpl_models_ndarray) => {
456 apply_pose_on_backend::<NdArray>(scene, &mut command_buffer, smpl_models_ndarray);
457 }
458 SmplCacheDynamic::Wgpu(smpl_models_wgpu) => {
459 apply_pose_on_backend::<Wgpu>(scene, &mut command_buffer, smpl_models_wgpu);
460 }
461 SmplCacheDynamic::Candle(smpl_models_candle) => {
462 apply_pose_on_backend::<Candle>(scene, &mut command_buffer, smpl_models_candle);
463 }
464 }
465 }
466 command_buffer.run_on(&mut scene.world);
467}
468#[allow(clippy::too_many_arguments)]
471fn apply_pose_on_backend<B: Backend>(scene: &Scene, command_buffer: &mut CommandBuffer, smpl_models: &SmplCache<B>)
472where
473 <B as Backend>::FloatTensorPrimitive<2>: Sync,
474 <B as Backend>::IntTensorPrimitive<2>: Sync,
475 B::QuantizedTensorPrimitive<1>: Sync,
476 B::QuantizedTensorPrimitive<2>: Sync,
477 B::QuantizedTensorPrimitive<3>: Sync,
478{
479 let mut query_state = scene.world.query::<(
480 &SmplParams,
481 &mut SmplOutputPoseTDynamic<B>,
482 &mut Pose,
483 Option<&PoseCorrectiveDynamic<B>>,
484 Changed<Pose>,
485 Changed<SmplOutputPoseTDynamic<B>>,
486 Changed<SmplParams>,
487 )>();
488 for (entity, (smpl_params, smpl_t_output, smpl_pose, pose_corrective, changed_pose, changed_t_output, changed_smpl_params)) in query_state.iter()
489 {
490 if !changed_pose && !changed_t_output && !changed_smpl_params {
491 continue;
492 }
493 let smpl_model = smpl_models.get_model_ref(smpl_params.smpl_type, smpl_params.gender).unwrap();
494 let lbs_weights_merged = smpl_model.lbs_weights();
495 let mut verts_burn_merged = smpl_t_output.verts.clone();
496 let joints_t_pose = &smpl_t_output.joints;
497 let new_pose = smpl_pose.clone();
498 if let Some(pose_corrective) = pose_corrective {
499 if smpl_params.enable_pose_corrective {
500 let v_offset_merged = &pose_corrective.verts_offset;
501 verts_burn_merged = verts_burn_merged.add(v_offset_merged.clone());
502 }
503 }
504 let (verts_posed_nd, _, _, joints_posed) =
505 smpl_model.apply_pose(&verts_burn_merged, None, None, joints_t_pose, &lbs_weights_merged, &new_pose);
506 command_buffer.insert_one(
507 entity,
508 SmplOutputPosedDynamic {
509 verts: verts_posed_nd,
510 joints: joints_posed,
511 },
512 );
513 }
514}
515#[allow(clippy::too_many_lines)]
518pub extern "C" fn smpl_to_gloss_mesh(scene: &mut Scene, _runner: &mut RunnerState) {
519 let mut command_buffer = CommandBuffer::new();
520 {
521 let smpl_models_dynamic = scene.get_resource::<&SmplCacheDynamic>().unwrap();
522 match &*smpl_models_dynamic {
523 SmplCacheDynamic::NdArray(smpl_models_ndarray) => {
524 let mut query_state = scene.world.query::<(
525 &SmplParams,
526 &SmplOutputPosedDynamic<NdArray>,
527 Changed<SmplOutputPosedDynamic<NdArray>>,
528 &GlossInterop,
529 )>();
530 for (entity, (smpl_params, smpl_output, changed_output, gloss_interop)) in query_state.iter() {
531 if !changed_output && !smpl_models_dynamic.is_changed() {
532 continue;
533 }
534 let smpl_model = smpl_models_ndarray.get_model_ref(smpl_params.smpl_type, smpl_params.gender).unwrap();
535 let (verts, uv, normals, tangents, faces) = compute_common_mesh_data(smpl_model, &smpl_output.verts, gloss_interop.with_uv);
536 update_entity_on_backend(
537 entity,
538 scene,
539 &mut command_buffer,
540 gloss_interop.with_uv,
541 &DynamicTensorFloat2D::NdArray(verts),
542 &DynamicTensorFloat2D::NdArray(normals),
543 tangents.map(DynamicTensorFloat2D::NdArray),
544 DynamicTensorFloat2D::NdArray(uv),
545 DynamicTensorInt2D::NdArray(faces),
546 smpl_model,
547 );
548 }
549 }
550 SmplCacheDynamic::Wgpu(smpl_models_wgpu) => {
551 let mut query_state = scene.world.query::<(
552 &SmplParams,
553 &SmplOutputPosedDynamic<Wgpu>,
554 Changed<SmplOutputPosedDynamic<Wgpu>>,
555 &GlossInterop,
556 )>();
557 for (entity, (smpl_params, smpl_output, changed_output, gloss_interop)) in query_state.iter() {
558 if !changed_output && !smpl_models_dynamic.is_changed() {
559 continue;
560 }
561 let smpl_model = smpl_models_wgpu.get_model_ref(smpl_params.smpl_type, smpl_params.gender).unwrap();
562 let (verts, uv, normals, tangents, faces) = compute_common_mesh_data(smpl_model, &smpl_output.verts, gloss_interop.with_uv);
563 update_entity_on_backend(
564 entity,
565 scene,
566 &mut command_buffer,
567 gloss_interop.with_uv,
568 &DynamicTensorFloat2D::Wgpu(verts),
569 &DynamicTensorFloat2D::Wgpu(normals),
570 tangents.map(DynamicTensorFloat2D::Wgpu),
571 DynamicTensorFloat2D::Wgpu(uv),
572 DynamicTensorInt2D::Wgpu(faces),
573 smpl_model,
574 );
575 }
576 }
577 SmplCacheDynamic::Candle(smpl_models_candle) => {
578 let mut query_state = scene.world.query::<(
579 &SmplParams,
580 &SmplOutputPosedDynamic<Candle>,
581 Changed<SmplOutputPosedDynamic<Candle>>,
582 &GlossInterop,
583 )>();
584 for (entity, (smpl_params, smpl_output, changed_output, gloss_interop)) in query_state.iter() {
585 if !changed_output && !smpl_models_dynamic.is_changed() {
586 continue;
587 }
588 let smpl_model = smpl_models_candle.get_model_ref(smpl_params.smpl_type, smpl_params.gender).unwrap();
589 let (verts, uv, normals, tangents, faces) = compute_common_mesh_data(smpl_model, &smpl_output.verts, gloss_interop.with_uv);
590 update_entity_on_backend(
591 entity,
592 scene,
593 &mut command_buffer,
594 gloss_interop.with_uv,
595 &DynamicTensorFloat2D::Candle(verts),
596 &DynamicTensorFloat2D::Candle(normals),
597 tangents.map(DynamicTensorFloat2D::Candle),
598 DynamicTensorFloat2D::Candle(uv),
599 DynamicTensorInt2D::Candle(faces),
600 smpl_model,
601 );
602 }
603 }
604 }
605 }
606 command_buffer.run_on(&mut scene.world);
607}
608type MeshDataResult<B> = (
610 Tensor<B, 2, Float>,
611 Tensor<B, 2, Float>,
612 Tensor<B, 2, Float>,
613 Option<Tensor<B, 2, Float>>,
614 Tensor<B, 2, Int>,
615);
616fn compute_common_mesh_data<B: Backend>(smpl_model: &dyn SmplModel<B>, verts_burn: &Tensor<B, 2, Float>, with_uv: bool) -> MeshDataResult<B> {
619 let device_str = format!("{:?}", verts_burn.device());
620 let mapping = smpl_model.idx_split_2_merged();
621 let verts_final_burn = if with_uv {
622 verts_burn.clone().select(0, mapping.clone())
623 } else {
624 verts_burn.clone()
625 };
626 let uv_burn = smpl_model.uv().clone();
627 let faces_burn = smpl_model.faces();
628 let normals_merged_burn = match device_str.as_str() {
629 "Cpu" => Geom::compute_per_vertex_normals(
630 &verts_burn.to_nalgebra(),
631 &faces_burn.to_nalgebra(),
632 &PerVertexNormalsWeightingType::Uniform,
633 )
634 .to_burn(&verts_burn.device()),
635 _ => Geom::compute_per_vertex_normals_burn(verts_burn, faces_burn, &PerVertexNormalsWeightingType::Uniform),
636 };
637 let normals_final_burn = if with_uv {
638 normals_merged_burn.clone().select(0, mapping.clone())
639 } else {
640 normals_merged_burn
641 };
642 let tangents_burn = if with_uv {
643 match device_str.as_str() {
644 "Cpu" => Some(
645 Geom::compute_tangents(
646 &verts_final_burn.to_nalgebra(),
647 &smpl_model.faces_uv().to_nalgebra(),
648 &normals_final_burn.to_nalgebra(),
649 &smpl_model.uv().to_nalgebra(),
650 )
651 .to_burn(&verts_burn.device()),
652 ),
653 _ => Some(Geom::compute_tangents_burn(
654 &verts_final_burn,
655 smpl_model.faces_uv(),
656 &normals_final_burn,
657 smpl_model.uv(),
658 )),
659 }
660 } else {
661 None
662 };
663 let faces_burn = if with_uv { smpl_model.faces_uv() } else { faces_burn };
664 (verts_final_burn, uv_burn, normals_final_burn, tangents_burn, faces_burn.clone())
665}
666#[allow(clippy::too_many_lines)]
670pub extern "C" fn smpl_align_vertical(scene: &mut Scene, _runner: &mut RunnerState) {
671 let mut command_buffer = CommandBuffer::new();
672 let backend = {
673 let smpl_models_dynamic = scene.get_resource::<&SmplCacheDynamic>().unwrap();
674 smpl_models_dynamic.get_backend()
675 };
676 match backend {
677 BurnBackend::NdArray => align_vertical_on_backend::<NdArray>(scene),
678 BurnBackend::Wgpu => align_vertical_on_backend::<Wgpu>(scene),
679 BurnBackend::Candle => align_vertical_on_backend::<Candle>(scene),
680 }
681 command_buffer.run_on(&mut scene.world);
682}
683fn align_vertical_on_backend<B: Backend>(scene: &Scene)
684where
685 <B as Backend>::FloatTensorPrimitive<2>: Sync,
686 B::QuantizedTensorPrimitive<1>: Sync,
687 B::QuantizedTensorPrimitive<2>: Sync,
688 B::QuantizedTensorPrimitive<3>: Sync,
689{
690 let mut query_state = scene
691 .world
692 .query::<(&Verts, &mut ModelMatrix, Changed<SmplOutputPoseTDynamic<B>>)>()
693 .with::<&SmplParams>();
694 for (_entity, (verts, mut model_matrix, changed_t_pose)) in query_state.iter() {
695 if changed_t_pose {
696 let verts_world = Geom::transform_verts(&verts.0.to_dmatrix(), &model_matrix.0);
697 let min_y = verts_world.column(1).min();
698 model_matrix.0.append_translation_mut(&na::Translation3::<f32>::new(0.0, -min_y, 0.0));
699 }
700 }
701}
702#[allow(clippy::cast_precision_loss)]
704pub extern "C" fn smpl_follow_anim(scene: &mut Scene, runner: &mut RunnerState) {
705 let mut command_buffer = CommandBuffer::new();
706 {
707 let backend = {
708 let smpl_models_dynamic = scene.get_resource::<&SmplCacheDynamic>().unwrap();
709 smpl_models_dynamic.get_backend()
710 };
711 let Ok(mut follow) = scene.get_resource::<&mut Follower>() else {
712 return;
713 };
714 let mut query_state = scene.world.query::<&Follow>().with::<&Renderable>();
715 let mut goal = na::Point3::new(0.0, 0.0, 0.0);
716 let mut num_ents = 0;
717 for (entity, _) in query_state.iter() {
718 let model_matrix = if let Ok(mm) = scene.world.get::<&mut ModelMatrix>(entity) {
719 mm.0
720 } else {
721 ModelMatrix::default().0
722 };
723 let ent_goal = match backend {
724 BurnBackend::NdArray => {
725 if let Some(point) = handle_goal_for_backend::<NdArray>(scene, entity, model_matrix) {
726 point
727 } else {
728 continue;
729 }
730 }
731 BurnBackend::Wgpu => {
732 if let Some(point) = handle_goal_for_backend::<Wgpu>(scene, entity, model_matrix) {
733 point
734 } else {
735 continue;
736 }
737 }
738 BurnBackend::Candle => {
739 if let Some(point) = handle_goal_for_backend::<Candle>(scene, entity, model_matrix) {
740 point
741 } else {
742 continue;
743 }
744 }
745 };
746 goal.coords += ent_goal.coords;
747 num_ents += 1;
748 }
749 if num_ents > 0 {
750 goal.coords /= num_ents as f32;
751 }
752 #[allow(clippy::match_wildcard_for_single_variants)]
753 match follow.params.follower_type {
754 FollowerType::Cam | FollowerType::CamAndLights => {
755 let cam = scene.get_current_cam().unwrap();
756 if !cam.is_initialized(scene) {
757 return;
758 }
759 if let Ok(mut poslookat) = scene.world.get::<&mut PosLookat>(cam.entity) {
760 follow.update(&goal, &poslookat.lookat, runner.dt().as_secs_f32());
761 let point_lookat = follow.get_point_follow("cam");
762 let diff = (poslookat.lookat - point_lookat).norm();
763 if diff > 1e-7 {
764 runner.request_redraw();
765 poslookat.shift_lookat(point_lookat);
766 }
767 }
768 }
769 _ => {}
770 }
771 #[allow(clippy::match_wildcard_for_single_variants)]
772 match follow.params.follower_type {
773 FollowerType::Lights | FollowerType::CamAndLights => {
774 let lights = scene.get_lights(false);
775 for light in lights.iter() {
776 if let Ok(mut poslookat) = scene.world.get::<&mut PosLookat>(*light) {
777 let point_lookat = goal;
778 let diff = (poslookat.lookat - point_lookat).norm();
779 if diff > 1e-7 {
780 runner.request_redraw();
781 poslookat.shift_lookat(point_lookat);
782 }
783 }
784 }
785 let point_dist_fade_center = goal;
786 command_buffer.insert_one(
787 scene.get_entity_resource(),
788 ConfigChanges {
789 new_distance_fade_center: point_dist_fade_center,
790 },
791 );
792 }
793 _ => {}
794 }
795 }
796 command_buffer.run_on(&mut scene.world);
797}
798fn handle_goal_for_backend<B: Backend>(scene: &Scene, entity: Entity, model_matrix: na::SimilarityMatrix3<f32>) -> Option<na::Point3<f32>>
799where
800 <B as Backend>::FloatTensorPrimitive<2>: Sync,
801 <B as Backend>::IntTensorPrimitive<2>: Sync,
802 B::QuantizedTensorPrimitive<1>: Sync,
803 B::QuantizedTensorPrimitive<2>: Sync,
804 B::QuantizedTensorPrimitive<3>: Sync,
805{
806 if scene.world.has::<SmplOutputPosedDynamic<B>>(entity).unwrap() {
807 let output_posed = scene.world.get::<&SmplOutputPosedDynamic<B>>(entity).unwrap();
808 let joints_ndarray = output_posed.joints.to_ndarray();
809 let pose_trans = joints_ndarray.row(0).into_nalgebra();
810 let point = pose_trans.fixed_rows::<3>(0).clone_owned();
811 let mut point = na::Point3::<f32> { coords: point };
812 point = model_matrix * point;
813 Some(point)
814 } else if scene.world.has::<Verts>(entity).unwrap() && scene.world.has::<ModelMatrix>(entity).unwrap() {
815 let verts = scene.world.get::<&Verts>(entity).unwrap();
816 let model_matrix = scene.world.get::<&ModelMatrix>(entity).unwrap();
817 Some(Geom::get_centroid(&verts.0.to_dmatrix(), Some(model_matrix.0)))
818 } else {
819 None
820 }
821}
822pub extern "C" fn hide_floor_when_viewed_from_below(scene: &mut Scene, _runner: &mut RunnerState) {
824 let camera = scene.get_current_cam().unwrap();
825 let pos = {
826 let Ok(pos_lookat) = scene.world.get::<&PosLookat>(camera.entity) else {
827 warn!("rs: hide_floor_when_viewed_from_below: No PosLookat yet, camera is not initialized. Auto adding default");
828 return;
829 };
830 pos_lookat.position
831 };
832 if let Some(floor) = scene.get_floor() {
833 let min_y = {
834 let Ok(verts) = scene.world.get::<&Verts>(floor.entity) else {
835 warn!("rs: hide_floor_when_viewed_from_below: No Verts on floor");
836 return;
837 };
838 let Ok(model_matrix) = scene.world.get::<&ModelMatrix>(floor.entity) else {
839 warn!("rs: hide_floor_when_viewed_from_below: No ModelMatrix on floor");
840 return;
841 };
842 let verts_world = Geom::transform_verts(&verts.0.to_dmatrix(), &model_matrix.0);
843 verts_world.column(1).min()
844 };
845 if pos.coords.y < min_y {
846 if scene.world.has::<Renderable>(floor.entity).unwrap() {
847 scene.world.remove_one::<Renderable>(floor.entity).unwrap();
848 }
849 } else if !scene.world.has::<Renderable>(floor.entity).unwrap() {
850 scene.world.insert_one(floor.entity, Renderable).unwrap();
851 }
852 }
853}
854#[allow(missing_docs)]
855#[cfg(feature = "with-gui")]
856#[allow(clippy::semicolon_if_nothing_returned)]
857#[allow(clippy::too_many_lines)]
858#[cfg_attr(target_arch = "wasm32", allow(improper_ctypes_definitions))]
859pub extern "C" fn smpl_params_gui(selected_entity: ROption<Entity>, scene: &mut Scene) -> GuiWindow {
860 use gloss_renderer::plugin_manager::gui::Button;
861 use gloss_utils::abi_stable_aliases::std_types::ROption::RSome;
862 use smpl_rs::{
863 codec::{codec::SmplCodec, gltf::GltfCodec},
864 common::types::{Gender, GltfCompatibilityMode},
865 };
866 extern "C" fn enable_pose_corrective_toggle(new_val: bool, _widget_name: RString, entity: Entity, scene: &mut Scene) {
867 if let Ok(mut smpl_params) = scene.world.get::<&mut SmplParams>(entity) {
868 smpl_params.enable_pose_corrective = new_val;
869 }
870 }
871 extern "C" fn save_smpl(_widget_name: RString, entity: Entity, scene: &mut Scene) {
872 let codec = SmplCodec::from_entity(&entity, scene, None);
873 codec.to_file("./saved.smpl");
874 }
875 extern "C" fn save_gltf_smpl(_widget_name: RString, _entity: Entity, scene: &mut Scene) {
876 let mut codec = GltfCodec::from_scene(scene, None, None);
877 let now = wasm_timer::Instant::now();
878 codec.to_file(
879 "Meshcapade Avatar",
880 "./saved/output.gltf",
881 GltfOutputType::Standard,
882 GltfCompatibilityMode::Smpl,
883 );
884 codec.to_file(
885 "Meshcapade Avatar",
886 "./saved/output.glb",
887 GltfOutputType::Binary,
888 GltfCompatibilityMode::Smpl,
889 );
890 println!("Smpl mode `.gltf` export took {:?} seconds", now.elapsed());
891 }
892 extern "C" fn save_gltf_unreal(_widget_name: RString, _entity: Entity, scene: &mut Scene) {
893 let mut codec = GltfCodec::from_scene(scene, None, None);
894 let now = wasm_timer::Instant::now();
895 codec.to_file(
896 "Meshcapade Avatar",
897 "./saved/output.gltf",
898 GltfOutputType::Standard,
899 GltfCompatibilityMode::Unreal,
900 );
901 codec.to_file(
902 "Meshcapade Avatar",
903 "./saved/output.glb",
904 GltfOutputType::Binary,
905 GltfCompatibilityMode::Unreal,
906 );
907 println!("Unreal mode `.gltf` export took {:?} seconds", now.elapsed());
908 }
909 extern "C" fn change_gender(_val: bool, widget_name: RString, entity: Entity, scene: &mut Scene) {
910 if let Ok(mut smpl_params) = scene.world.get::<&mut SmplParams>(entity) {
911 match widget_name.as_str() {
912 "neutral" => smpl_params.gender = Gender::Neutral,
913 "female" => smpl_params.gender = Gender::Female,
914 "male" => smpl_params.gender = Gender::Male,
915 _ => {}
916 }
917 }
918 }
919 let mut widgets = RVec::new();
920 if let RSome(entity) = selected_entity {
921 if let Ok(smpl_params) = scene.world.get::<&SmplParams>(entity) {
922 let checkbox = Checkbox::new(
923 "enable_pose_corrective",
924 smpl_params.enable_pose_corrective,
925 enable_pose_corrective_toggle,
926 );
927 let is_neutral = smpl_params.gender == Gender::Neutral;
928 let is_female = smpl_params.gender == Gender::Female;
929 let is_male = smpl_params.gender == Gender::Male;
930 let chk_neutral = Checkbox::new("neutral", is_neutral, change_gender);
931 let chk_female = Checkbox::new("female", is_female, change_gender);
932 let chk_male = Checkbox::new("male", is_male, change_gender);
933 let button_save_smpl = Button::new("Save as .smpl", save_smpl);
934 let button_save_gltf_smpl = Button::new("Save as .gltf (SMPL)", save_gltf_smpl);
935 let button_save_gltf_unreal = Button::new("Save as .gltf (UNREAL)", save_gltf_unreal);
936 widgets.push(Widgets::Checkbox(chk_neutral));
937 widgets.push(Widgets::Checkbox(chk_female));
938 widgets.push(Widgets::Checkbox(chk_male));
939 widgets.push(Widgets::Checkbox(checkbox));
940 widgets.push(Widgets::Button(button_save_smpl));
941 widgets.push(Widgets::Button(button_save_gltf_smpl));
942 widgets.push(Widgets::Button(button_save_gltf_unreal));
943 }
944 }
945 GuiWindow {
946 window_name: RString::from("SmplParams"),
947 window_type: GuiWindowType::Sidebar,
948 widgets,
949 }
950}
951#[allow(missing_docs)]
952#[cfg(feature = "with-gui")]
953#[allow(clippy::semicolon_if_nothing_returned)]
954#[cfg_attr(target_arch = "wasm32", allow(improper_ctypes_definitions))]
955pub extern "C" fn smpl_betas_gui(selected_entity: ROption<Entity>, scene: &mut Scene) -> GuiWindow {
956 use gloss_utils::abi_stable_aliases::std_types::ROption::RSome;
957 extern "C" fn beta_slider_change(new_val: f32, widget_name: RString, entity: Entity, scene: &mut Scene) {
958 let beta_idx: usize = widget_name.split('_').last().unwrap().parse().unwrap();
959 if let Ok(mut betas) = scene.world.get::<&mut Betas>(entity) {
960 betas.betas[beta_idx] = new_val;
961 }
962 }
963 let mut widgets = RVec::new();
964 if let RSome(entity) = selected_entity {
965 if let Ok(betas) = scene.world.get::<&Betas>(entity) {
966 for i in 0..betas.betas.len() {
967 let slider = Slider::new(
968 ("Beta_".to_owned() + &i.to_string()).as_str(),
969 betas.betas[i],
970 -5.0,
971 5.0,
972 RSome(80.0),
973 beta_slider_change,
974 RNone,
975 );
976 widgets.push(Widgets::Slider(slider));
977 }
978 }
979 }
980 GuiWindow {
981 window_name: RString::from("Betas"),
982 window_type: GuiWindowType::Sidebar,
983 widgets,
984 }
985}
986#[allow(missing_docs)]
987#[cfg(feature = "with-gui")]
988#[allow(clippy::semicolon_if_nothing_returned)]
989#[cfg_attr(target_arch = "wasm32", allow(improper_ctypes_definitions))]
990pub extern "C" fn smpl_expression_gui(selected_entity: ROption<Entity>, scene: &mut Scene) -> GuiWindow {
991 use gloss_utils::abi_stable_aliases::std_types::ROption::RSome;
992 extern "C" fn expr_slider_change(new_val: f32, widget_name: RString, entity: Entity, scene: &mut Scene) {
993 let coeff_idx: usize = widget_name.split('_').last().unwrap().parse().unwrap();
994 if let Ok(mut coeffs) = scene.world.get::<&mut Expression>(entity) {
995 coeffs.expr_coeffs[coeff_idx] = new_val;
996 }
997 }
998 let mut widgets = RVec::new();
999 if let RSome(entity) = selected_entity {
1000 if let Ok(expression) = scene.world.get::<&Expression>(entity) {
1001 for i in 0..expression.expr_coeffs.len() {
1002 let slider = Slider::new(
1003 ("Coeff_".to_owned() + &i.to_string()).as_str(),
1004 expression.expr_coeffs[i],
1005 -5.0,
1006 5.0,
1007 RSome(80.0),
1008 expr_slider_change,
1009 RNone,
1010 );
1011 widgets.push(Widgets::Slider(slider));
1012 }
1013 }
1014 }
1015 GuiWindow {
1016 window_name: RString::from("Expression"),
1017 window_type: GuiWindowType::Sidebar,
1018 widgets,
1019 }
1020}
1021#[allow(missing_docs)]
1022#[cfg(feature = "with-gui")]
1023#[allow(clippy::semicolon_if_nothing_returned)]
1024#[cfg_attr(target_arch = "wasm32", allow(improper_ctypes_definitions))]
1025pub extern "C" fn smpl_anim_scroll_gui(_selected_entity: ROption<Entity>, scene: &mut Scene) -> GuiWindow {
1026 use gloss_renderer::plugin_manager::gui::{Button, WindowPivot, WindowPosition, WindowPositionType};
1027 use gloss_utils::abi_stable_aliases::std_types::ROption::RSome;
1028 extern "C" fn scene_anim_slider_change(new_val: f32, _widget_name: RString, _entity: Entity, scene: &mut Scene) {
1029 if let Ok(mut scene_anim) = scene.get_resource::<&mut SceneAnimation>() {
1030 scene_anim.set_cur_time_as_sec(new_val);
1031 scene_anim.runner.temporary_pause = true;
1032 }
1033 }
1034 extern "C" fn scene_anim_slider_no_change(_widget_name: RString, _entity: Entity, scene: &mut Scene) {
1035 if let Ok(mut scene_anim) = scene.get_resource::<&mut SceneAnimation>() {
1036 scene_anim.runner.temporary_pause = false;
1037 }
1038 }
1039 extern "C" fn scene_button_play_pause(_widget_name: RString, _entity: Entity, scene: &mut Scene) {
1040 if let Ok(mut scene_anim) = scene.get_resource::<&mut SceneAnimation>() {
1041 scene_anim.runner.paused = !scene_anim.runner.paused;
1042 }
1043 }
1044 #[allow(clippy::cast_possible_truncation)]
1045 #[allow(clippy::cast_sign_loss)]
1046 extern "C" fn scene_button_next_frame(_widget_name: RString, _entity: Entity, scene: &mut Scene) {
1047 if let Ok(mut scene_anim) = scene.get_resource::<&mut SceneAnimation>() {
1048 let nr_frames = scene_anim.num_frames;
1049 let duration = scene_anim.duration();
1050 let dt_between_frames = duration / nr_frames as u32;
1051 scene_anim.advance(dt_between_frames, false);
1052 }
1053 }
1054 extern "C" fn scene_fps_slider_change(new_val: f32, _widget_name: RString, _entity: Entity, scene: &mut Scene) {
1055 if let Ok(mut scene_anim) = scene.get_resource::<&mut SceneAnimation>() {
1056 let cur_time = scene_anim.get_cur_time();
1057 let prev_fps = scene_anim.config.fps;
1058 let multiplier_duration = prev_fps / new_val;
1059 scene_anim.set_cur_time_as_sec(cur_time.as_secs_f32() * multiplier_duration);
1060 scene_anim.config.fps = new_val;
1061 }
1062 }
1063 extern "C" fn follow_anim(new_val: bool, _widget_name: RString, _entity: Entity, scene: &mut Scene) {
1064 if new_val {
1065 scene.add_resource(Follower::new(FollowParams::default()));
1066 } else {
1067 let _ = scene.remove_resource::<Follower>();
1068 }
1069 }
1070 let mut widgets = RVec::new();
1071 if let Ok(scene_anim) = scene.get_resource::<&mut SceneAnimation>() {
1072 if scene_anim.num_frames != 0 {
1073 let max_duration = scene_anim.duration().as_secs_f32();
1074 let cur_time = scene_anim.get_cur_time().as_secs_f32();
1075 let slider_anim = Slider::new(
1076 "AnimTime",
1077 cur_time,
1078 0.0,
1079 max_duration,
1080 RSome(400.0),
1081 scene_anim_slider_change,
1082 RSome(scene_anim_slider_no_change as extern "C" fn(RString, Entity, &mut Scene)),
1083 );
1084 let button_play_pause = Button::new("play/pause", scene_button_play_pause);
1085 let button_next_frame = Button::new("next_frame", scene_button_next_frame);
1086 let slider_fps = Slider::new("FPS", scene_anim.config.fps, 1.0, 120.0, RSome(100.0), scene_fps_slider_change, RNone);
1087 let chk_follow_anim = Checkbox::new("follow", scene.has_resource::<Follower>(), follow_anim);
1088 widgets.push(Widgets::Slider(slider_anim));
1089 widgets.push(Widgets::Horizontal(RVec::from(vec![
1090 Widgets::Button(button_play_pause),
1091 Widgets::Button(button_next_frame),
1092 Widgets::Slider(slider_fps),
1093 ])));
1094 widgets.push(Widgets::Horizontal(RVec::from(vec![Widgets::Checkbox(chk_follow_anim)])));
1095 }
1096 }
1097 GuiWindow {
1098 window_name: RString::from("Scene"),
1099 window_type: GuiWindowType::FloatWindow(WindowPivot::CenterBottom, WindowPosition([0.5, 1.0]), WindowPositionType::Fixed),
1100 widgets,
1101 }
1102}
1103#[allow(missing_docs)]
1104#[cfg(feature = "with-gui")]
1105#[allow(clippy::semicolon_if_nothing_returned)]
1106#[cfg_attr(target_arch = "wasm32", allow(improper_ctypes_definitions))]
1107pub extern "C" fn smpl_hand_pose_gui(selected_entity: ROption<Entity>, scene: &mut Scene) -> GuiWindow {
1108 use gloss_renderer::plugin_manager::gui::SelectableList;
1109 use gloss_utils::abi_stable_aliases::std_types::ROption::RSome;
1110 use log::warn;
1111 use smpl_rs::common::pose_hands::HandType;
1112 extern "C" fn set_hand_pose_type(widget_name: RString, entity: Entity, scene: &mut Scene) {
1113 let hand_type = match widget_name.to_string().as_str() {
1114 "Flat" => Some(HandType::Flat),
1115 "Relaxed" => Some(HandType::Relaxed),
1116 "Curled" => Some(HandType::Curled),
1117 "Fist" => Some(HandType::Fist),
1118 _ => {
1119 warn!("HandType not known");
1120 None
1121 }
1122 };
1123 let mut command_buffer = CommandBuffer::new();
1124 if let Some(hand_type) = hand_type {
1125 info!("setting to {hand_type:?}");
1126 if let Ok(mut pose_mask) = scene.world.get::<&mut PoseOverride>(entity) {
1127 info!("we already have a pose mask");
1128 pose_mask.set_overwrite_hands(hand_type);
1129 } else {
1130 info!("inserting a new pose mask");
1131 let pose_mask = PoseOverride::allow_all().overwrite_hands(hand_type).build();
1132 command_buffer.insert_one(entity, pose_mask);
1133 }
1134 } else {
1135 info!("removing overwrite");
1136 if let Ok(mut pose_mask) = scene.world.get::<&mut PoseOverride>(entity) {
1137 info!("removing overwrite and we have posemask");
1138 if pose_mask.get_overwrite_hands_type().is_some() {
1139 pose_mask.remove_overwrite_hands();
1140 }
1141 }
1142 }
1143 command_buffer.run_on(&mut scene.world);
1144 }
1145 let mut widgets = RVec::new();
1146 if let RSome(entity) = selected_entity {
1147 if let Ok(pose_mask) = scene.world.get::<&PoseOverride>(entity) {
1148 let hand_type_overwrite = pose_mask.get_overwrite_hands_type();
1149 let mut selectable_vec = RVec::new();
1150 selectable_vec.push(Selectable::new("None", hand_type_overwrite.is_none(), set_hand_pose_type));
1151 selectable_vec.push(Selectable::new("Flat", hand_type_overwrite == Some(HandType::Flat), set_hand_pose_type));
1152 selectable_vec.push(Selectable::new(
1153 "Relaxed",
1154 hand_type_overwrite == Some(HandType::Relaxed),
1155 set_hand_pose_type,
1156 ));
1157 selectable_vec.push(Selectable::new(
1158 "Curled",
1159 hand_type_overwrite == Some(HandType::Curled),
1160 set_hand_pose_type,
1161 ));
1162 selectable_vec.push(Selectable::new("Fist", hand_type_overwrite == Some(HandType::Fist), set_hand_pose_type));
1163 let selectable_list = SelectableList::new(selectable_vec, true);
1164 widgets.push(Widgets::SelectableList(selectable_list));
1165 }
1166 }
1167 GuiWindow {
1168 window_name: RString::from("HandOverwrite"),
1169 window_type: GuiWindowType::Sidebar,
1170 widgets,
1171 }
1172}
1173#[allow(missing_docs)]
1174#[cfg(feature = "with-gui")]
1175#[allow(clippy::cast_precision_loss)]
1176#[cfg_attr(target_arch = "wasm32", allow(improper_ctypes_definitions))]
1177pub extern "C" fn smpl_event_dropfile(scene: &mut Scene, _runner: &mut RunnerState, event: &Event) -> bool {
1178 use crate::scene::McsCodecGloss;
1179 use log::warn;
1180 use smpl_rs::codec::{codec::SmplCodec, scene::McsCodec};
1181 use std::path::PathBuf;
1182 let mut handled = false;
1183 match event {
1184 Event::DroppedFile(path) => {
1185 let path_buf = PathBuf::from(path.to_string());
1186 let filetype = match path_buf.extension() {
1187 Some(extension) => FileType::find_match(extension.to_str().unwrap_or("")),
1188 None => FileType::Unknown,
1189 };
1190 if scene.has_resource::<SceneAnimation>() {
1191 scene.remove_resource::<SceneAnimation>().unwrap();
1192 }
1193 match filetype {
1194 FileType::Smpl => {
1195 info!("handling dropped smpl file {}", path);
1196 let codec = SmplCodec::from_file(path);
1197 let mut builder = codec.to_entity_builder();
1198 if !builder.has::<Betas>() {
1199 warn!("The .smpl file didn't have any shape_parameters associated, we are defaulting to the mean smpl shape");
1200 builder.add(Betas::default());
1201 }
1202 let gloss_interop = GlossInterop::default();
1203 let name = scene.get_unused_name();
1204 scene.get_or_create_entity(&name).insert_builder(builder).insert(gloss_interop);
1205 handled = true;
1206 }
1207 FileType::Mcs => {
1208 info!("handling dropped mcs file {}", path);
1209 let mut codec = McsCodec::from_file(path);
1210 let builders = codec.to_entity_builders();
1211 for mut builder in builders {
1212 if !builder.has::<Betas>() {
1213 warn!("The .smpl file didn't have any shape_parameters associated, we are defaulting to the mean smpl shape");
1214 builder.add(Betas::default());
1215 }
1216 let gloss_interop = GlossInterop::default();
1217 let name = scene.get_unused_name();
1218 scene.get_or_create_entity(&name).insert_builder(builder).insert(gloss_interop);
1219 handled = true;
1220 }
1221 let smpl_scene = SceneAnimation::new_with_fps(codec.num_frames, codec.frame_rate);
1222 scene.add_resource(smpl_scene);
1223 }
1224 _ => {
1225 info!("No known filetype {}", path);
1226 }
1227 }
1228 }
1229 }
1230 handled
1231}