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_core::common::animation::AnimationConfig;
33use smpl_core::common::smpl_model::{SmplCache, SmplModel};
34use smpl_core::common::types::{GltfOutputType, UpAxis};
35use smpl_core::common::{
36 animation::Animation,
37 betas::Betas,
38 expression::{Expression, ExpressionOffsets},
39 smpl_model::SmplCacheDynamic,
40};
41use smpl_core::common::{
42 outputs::{SmplOutputPoseTDynamic, SmplOutputPosedDynamic},
43 pose::Pose,
44 pose_corrective::PoseCorrectiveDynamic,
45 pose_override::PoseOverride,
46 smpl_params::SmplParams,
47};
48use smpl_core::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 {
850 if !scene.world.has::<Renderable>(floor.entity).unwrap() {
851 scene.world.insert_one(floor.entity, Renderable).unwrap();
852 }
853 }
854 }
855}
856#[allow(missing_docs)]
857#[cfg(feature = "with-gui")]
858#[allow(clippy::semicolon_if_nothing_returned)]
859#[allow(clippy::too_many_lines)]
860#[cfg_attr(target_arch = "wasm32", allow(improper_ctypes_definitions))]
861pub extern "C" fn smpl_params_gui(selected_entity: ROption<Entity>, scene: &mut Scene) -> GuiWindow {
862 use gloss_renderer::plugin_manager::gui::Button;
863 use gloss_utils::abi_stable_aliases::std_types::ROption::RSome;
864 use smpl_core::{
865 codec::{codec::SmplCodec, gltf::GltfCodec},
866 common::types::{Gender, GltfCompatibilityMode},
867 };
868 extern "C" fn enable_pose_corrective_toggle(new_val: bool, _widget_name: RString, entity: Entity, scene: &mut Scene) {
869 if let Ok(mut smpl_params) = scene.world.get::<&mut SmplParams>(entity) {
870 smpl_params.enable_pose_corrective = new_val;
871 }
872 }
873 extern "C" fn save_smpl(_widget_name: RString, entity: Entity, scene: &mut Scene) {
874 let codec = SmplCodec::from_entity(&entity, scene, None);
875 codec.to_file("./saved.smpl");
876 }
877 extern "C" fn save_gltf_smpl(_widget_name: RString, _entity: Entity, scene: &mut Scene) {
878 let mut codec = GltfCodec::from_scene(scene, None, true);
879 let now = wasm_timer::Instant::now();
880 codec.to_file(
881 "Meshcapade Avatar",
882 "./saved/output.gltf",
883 GltfOutputType::Standard,
884 GltfCompatibilityMode::Smpl,
885 );
886 codec.to_file(
887 "Meshcapade Avatar",
888 "./saved/output.glb",
889 GltfOutputType::Binary,
890 GltfCompatibilityMode::Smpl,
891 );
892 println!("Smpl mode `.gltf` export took {:?} seconds", now.elapsed());
893 }
894 extern "C" fn save_gltf_unreal(_widget_name: RString, _entity: Entity, scene: &mut Scene) {
895 let mut codec = GltfCodec::from_scene(scene, None, true);
896 let now = wasm_timer::Instant::now();
897 codec.to_file(
898 "Meshcapade Avatar",
899 "./saved/output.gltf",
900 GltfOutputType::Standard,
901 GltfCompatibilityMode::Unreal,
902 );
903 codec.to_file(
904 "Meshcapade Avatar",
905 "./saved/output.glb",
906 GltfOutputType::Binary,
907 GltfCompatibilityMode::Unreal,
908 );
909 println!("Unreal mode `.gltf` export took {:?} seconds", now.elapsed());
910 }
911 extern "C" fn change_gender(_val: bool, widget_name: RString, entity: Entity, scene: &mut Scene) {
912 if let Ok(mut smpl_params) = scene.world.get::<&mut SmplParams>(entity) {
913 match widget_name.as_str() {
914 "neutral" => smpl_params.gender = Gender::Neutral,
915 "female" => smpl_params.gender = Gender::Female,
916 "male" => smpl_params.gender = Gender::Male,
917 _ => {}
918 }
919 }
920 }
921 let mut widgets = RVec::new();
922 if let RSome(entity) = selected_entity {
923 if let Ok(smpl_params) = scene.world.get::<&SmplParams>(entity) {
924 let checkbox = Checkbox::new(
925 "enable_pose_corrective",
926 smpl_params.enable_pose_corrective,
927 enable_pose_corrective_toggle,
928 );
929 let is_neutral = smpl_params.gender == Gender::Neutral;
930 let is_female = smpl_params.gender == Gender::Female;
931 let is_male = smpl_params.gender == Gender::Male;
932 let chk_neutral = Checkbox::new("neutral", is_neutral, change_gender);
933 let chk_female = Checkbox::new("female", is_female, change_gender);
934 let chk_male = Checkbox::new("male", is_male, change_gender);
935 let button_save_smpl = Button::new("Save as .smpl", save_smpl);
936 let button_save_gltf_smpl = Button::new("Save as .gltf (SMPL)", save_gltf_smpl);
937 let button_save_gltf_unreal = Button::new("Save as .gltf (UNREAL)", save_gltf_unreal);
938 widgets.push(Widgets::Checkbox(chk_neutral));
939 widgets.push(Widgets::Checkbox(chk_female));
940 widgets.push(Widgets::Checkbox(chk_male));
941 widgets.push(Widgets::Checkbox(checkbox));
942 widgets.push(Widgets::Button(button_save_smpl));
943 widgets.push(Widgets::Button(button_save_gltf_smpl));
944 widgets.push(Widgets::Button(button_save_gltf_unreal));
945 }
946 }
947 GuiWindow {
948 window_name: RString::from("SmplParams"),
949 window_type: GuiWindowType::Sidebar,
950 widgets,
951 }
952}
953#[allow(missing_docs)]
954#[cfg(feature = "with-gui")]
955#[allow(clippy::semicolon_if_nothing_returned)]
956#[cfg_attr(target_arch = "wasm32", allow(improper_ctypes_definitions))]
957pub extern "C" fn smpl_betas_gui(selected_entity: ROption<Entity>, scene: &mut Scene) -> GuiWindow {
958 use gloss_utils::abi_stable_aliases::std_types::ROption::RSome;
959 extern "C" fn beta_slider_change(new_val: f32, widget_name: RString, entity: Entity, scene: &mut Scene) {
960 let beta_idx: usize = widget_name.split('_').last().unwrap().parse().unwrap();
961 if let Ok(mut betas) = scene.world.get::<&mut Betas>(entity) {
962 betas.betas[beta_idx] = new_val;
963 }
964 }
965 let mut widgets = RVec::new();
966 if let RSome(entity) = selected_entity {
967 if let Ok(betas) = scene.world.get::<&Betas>(entity) {
968 for i in 0..betas.betas.len() {
969 let slider = Slider::new(
970 ("Beta_".to_owned() + &i.to_string()).as_str(),
971 betas.betas[i],
972 -5.0,
973 5.0,
974 RSome(80.0),
975 beta_slider_change,
976 RNone,
977 );
978 widgets.push(Widgets::Slider(slider));
979 }
980 }
981 }
982 GuiWindow {
983 window_name: RString::from("Betas"),
984 window_type: GuiWindowType::Sidebar,
985 widgets,
986 }
987}
988#[allow(missing_docs)]
989#[cfg(feature = "with-gui")]
990#[allow(clippy::semicolon_if_nothing_returned)]
991#[cfg_attr(target_arch = "wasm32", allow(improper_ctypes_definitions))]
992pub extern "C" fn smpl_expression_gui(selected_entity: ROption<Entity>, scene: &mut Scene) -> GuiWindow {
993 use gloss_utils::abi_stable_aliases::std_types::ROption::RSome;
994 extern "C" fn expr_slider_change(new_val: f32, widget_name: RString, entity: Entity, scene: &mut Scene) {
995 let coeff_idx: usize = widget_name.split('_').last().unwrap().parse().unwrap();
996 if let Ok(mut coeffs) = scene.world.get::<&mut Expression>(entity) {
997 coeffs.expr_coeffs[coeff_idx] = new_val;
998 }
999 }
1000 let mut widgets = RVec::new();
1001 if let RSome(entity) = selected_entity {
1002 if let Ok(expression) = scene.world.get::<&Expression>(entity) {
1003 for i in 0..expression.expr_coeffs.len() {
1004 let slider = Slider::new(
1005 ("Coeff_".to_owned() + &i.to_string()).as_str(),
1006 expression.expr_coeffs[i],
1007 -5.0,
1008 5.0,
1009 RSome(80.0),
1010 expr_slider_change,
1011 RNone,
1012 );
1013 widgets.push(Widgets::Slider(slider));
1014 }
1015 }
1016 }
1017 GuiWindow {
1018 window_name: RString::from("Expression"),
1019 window_type: GuiWindowType::Sidebar,
1020 widgets,
1021 }
1022}
1023#[allow(missing_docs)]
1024#[cfg(feature = "with-gui")]
1025#[allow(clippy::semicolon_if_nothing_returned)]
1026#[cfg_attr(target_arch = "wasm32", allow(improper_ctypes_definitions))]
1027pub extern "C" fn smpl_anim_scroll_gui(_selected_entity: ROption<Entity>, scene: &mut Scene) -> GuiWindow {
1028 use gloss_renderer::plugin_manager::gui::{Button, WindowPivot, WindowPosition, WindowPositionType};
1029 use gloss_utils::abi_stable_aliases::std_types::ROption::RSome;
1030 extern "C" fn scene_anim_slider_change(new_val: f32, _widget_name: RString, _entity: Entity, scene: &mut Scene) {
1031 if let Ok(mut scene_anim) = scene.get_resource::<&mut SceneAnimation>() {
1032 scene_anim.set_cur_time_as_sec(new_val);
1033 scene_anim.runner.temporary_pause = true;
1034 }
1035 }
1036 extern "C" fn scene_anim_slider_no_change(_widget_name: RString, _entity: Entity, scene: &mut Scene) {
1037 if let Ok(mut scene_anim) = scene.get_resource::<&mut SceneAnimation>() {
1038 scene_anim.runner.temporary_pause = false;
1039 }
1040 }
1041 extern "C" fn scene_button_play_pause(_widget_name: RString, _entity: Entity, scene: &mut Scene) {
1042 if let Ok(mut scene_anim) = scene.get_resource::<&mut SceneAnimation>() {
1043 scene_anim.runner.paused = !scene_anim.runner.paused;
1044 }
1045 }
1046 #[allow(clippy::cast_possible_truncation)]
1047 #[allow(clippy::cast_sign_loss)]
1048 extern "C" fn scene_button_next_frame(_widget_name: RString, _entity: Entity, scene: &mut Scene) {
1049 if let Ok(mut scene_anim) = scene.get_resource::<&mut SceneAnimation>() {
1050 let nr_frames = scene_anim.num_frames;
1051 let duration = scene_anim.duration();
1052 let dt_between_frames = duration / nr_frames as u32;
1053 scene_anim.advance(dt_between_frames, false);
1054 }
1055 }
1056 extern "C" fn scene_fps_slider_change(new_val: f32, _widget_name: RString, _entity: Entity, scene: &mut Scene) {
1057 if let Ok(mut scene_anim) = scene.get_resource::<&mut SceneAnimation>() {
1058 let cur_time = scene_anim.get_cur_time();
1059 let prev_fps = scene_anim.config.fps;
1060 let multiplier_duration = prev_fps / new_val;
1061 scene_anim.set_cur_time_as_sec(cur_time.as_secs_f32() * multiplier_duration);
1062 scene_anim.config.fps = new_val;
1063 }
1064 }
1065 extern "C" fn follow_anim(new_val: bool, _widget_name: RString, _entity: Entity, scene: &mut Scene) {
1066 if new_val {
1067 scene.add_resource(Follower::new(FollowParams::default()));
1068 } else {
1069 let _ = scene.remove_resource::<Follower>();
1070 }
1071 }
1072 let mut widgets = RVec::new();
1073 if let Ok(scene_anim) = scene.get_resource::<&mut SceneAnimation>() {
1074 if scene_anim.num_frames != 0 {
1075 let max_duration = scene_anim.duration().as_secs_f32();
1076 let cur_time = scene_anim.get_cur_time().as_secs_f32();
1077 let slider_anim = Slider::new(
1078 "AnimTime",
1079 cur_time,
1080 0.0,
1081 max_duration,
1082 RSome(400.0),
1083 scene_anim_slider_change,
1084 RSome(scene_anim_slider_no_change as extern "C" fn(RString, Entity, &mut Scene)),
1085 );
1086 let button_play_pause = Button::new("play/pause", scene_button_play_pause);
1087 let button_next_frame = Button::new("next_frame", scene_button_next_frame);
1088 let slider_fps = Slider::new("FPS", scene_anim.config.fps, 1.0, 120.0, RSome(100.0), scene_fps_slider_change, RNone);
1089 let chk_follow_anim = Checkbox::new("follow", scene.has_resource::<Follower>(), follow_anim);
1090 widgets.push(Widgets::Slider(slider_anim));
1091 widgets.push(Widgets::Horizontal(RVec::from(vec![
1092 Widgets::Button(button_play_pause),
1093 Widgets::Button(button_next_frame),
1094 Widgets::Slider(slider_fps),
1095 ])));
1096 widgets.push(Widgets::Horizontal(RVec::from(vec![Widgets::Checkbox(chk_follow_anim)])));
1097 }
1098 }
1099 GuiWindow {
1100 window_name: RString::from("Scene"),
1101 window_type: GuiWindowType::FloatWindow(WindowPivot::CenterBottom, WindowPosition([0.5, 1.0]), WindowPositionType::Fixed),
1102 widgets,
1103 }
1104}
1105#[allow(missing_docs)]
1106#[cfg(feature = "with-gui")]
1107#[allow(clippy::semicolon_if_nothing_returned)]
1108#[cfg_attr(target_arch = "wasm32", allow(improper_ctypes_definitions))]
1109pub extern "C" fn smpl_hand_pose_gui(selected_entity: ROption<Entity>, scene: &mut Scene) -> GuiWindow {
1110 use gloss_renderer::plugin_manager::gui::SelectableList;
1111 use gloss_utils::abi_stable_aliases::std_types::ROption::RSome;
1112 use log::warn;
1113 use smpl_core::common::pose_hands::HandType;
1114 extern "C" fn set_hand_pose_type(widget_name: RString, entity: Entity, scene: &mut Scene) {
1115 let hand_type = match widget_name.to_string().as_str() {
1116 "Flat" => Some(HandType::Flat),
1117 "Relaxed" => Some(HandType::Relaxed),
1118 "Curled" => Some(HandType::Curled),
1119 "Fist" => Some(HandType::Fist),
1120 _ => {
1121 warn!("HandType not known");
1122 None
1123 }
1124 };
1125 let mut command_buffer = CommandBuffer::new();
1126 if let Some(hand_type) = hand_type {
1127 info!("setting to {hand_type:?}");
1128 if let Ok(mut pose_mask) = scene.world.get::<&mut PoseOverride>(entity) {
1129 info!("we already have a pose mask");
1130 pose_mask.set_overwrite_hands(hand_type);
1131 } else {
1132 info!("inserting a new pose mask");
1133 let pose_mask = PoseOverride::allow_all().overwrite_hands(hand_type).build();
1134 command_buffer.insert_one(entity, pose_mask);
1135 }
1136 } else {
1137 info!("removing overwrite");
1138 if let Ok(mut pose_mask) = scene.world.get::<&mut PoseOverride>(entity) {
1139 info!("removing overwrite and we have posemask");
1140 if pose_mask.get_overwrite_hands_type().is_some() {
1141 pose_mask.remove_overwrite_hands();
1142 }
1143 }
1144 }
1145 command_buffer.run_on(&mut scene.world);
1146 }
1147 let mut widgets = RVec::new();
1148 if let RSome(entity) = selected_entity {
1149 if let Ok(pose_mask) = scene.world.get::<&PoseOverride>(entity) {
1150 let hand_type_overwrite = pose_mask.get_overwrite_hands_type();
1151 let mut selectable_vec = RVec::new();
1152 selectable_vec.push(Selectable::new("None", hand_type_overwrite.is_none(), set_hand_pose_type));
1153 selectable_vec.push(Selectable::new("Flat", hand_type_overwrite == Some(HandType::Flat), set_hand_pose_type));
1154 selectable_vec.push(Selectable::new(
1155 "Relaxed",
1156 hand_type_overwrite == Some(HandType::Relaxed),
1157 set_hand_pose_type,
1158 ));
1159 selectable_vec.push(Selectable::new(
1160 "Curled",
1161 hand_type_overwrite == Some(HandType::Curled),
1162 set_hand_pose_type,
1163 ));
1164 selectable_vec.push(Selectable::new("Fist", hand_type_overwrite == Some(HandType::Fist), set_hand_pose_type));
1165 let selectable_list = SelectableList::new(selectable_vec, true);
1166 widgets.push(Widgets::SelectableList(selectable_list));
1167 }
1168 }
1169 GuiWindow {
1170 window_name: RString::from("HandOverwrite"),
1171 window_type: GuiWindowType::Sidebar,
1172 widgets,
1173 }
1174}
1175#[allow(missing_docs)]
1176#[cfg(feature = "with-gui")]
1177#[allow(clippy::cast_precision_loss)]
1178#[cfg_attr(target_arch = "wasm32", allow(improper_ctypes_definitions))]
1179pub extern "C" fn smpl_event_dropfile(scene: &mut Scene, _runner: &mut RunnerState, event: &Event) -> bool {
1180 use crate::scene::McsCodecGloss;
1181 use log::warn;
1182 use smpl_core::codec::{codec::SmplCodec, scene::McsCodec};
1183 use std::path::PathBuf;
1184 let mut handled = false;
1185 match event {
1186 Event::DroppedFile(path) => {
1187 let path_buf = PathBuf::from(path.to_string());
1188 let filetype = match path_buf.extension() {
1189 Some(extension) => FileType::find_match(extension.to_str().unwrap_or("")),
1190 None => FileType::Unknown,
1191 };
1192 if scene.has_resource::<SceneAnimation>() {
1193 scene.remove_resource::<SceneAnimation>().unwrap();
1194 }
1195 match filetype {
1196 FileType::Smpl => {
1197 info!("handling dropped smpl file {}", path);
1198 let codec = SmplCodec::from_file(path);
1199 let mut builder = codec.to_entity_builder();
1200 if !builder.has::<Betas>() {
1201 warn!("The .smpl file didn't have any shape_parameters associated, we are defaulting to the mean smpl shape");
1202 builder.add(Betas::default());
1203 }
1204 let gloss_interop = GlossInterop::default();
1205 let name = scene.get_unused_name();
1206 scene.get_or_create_entity(&name).insert_builder(builder).insert(gloss_interop);
1207 handled = true;
1208 }
1209 FileType::Mcs => {
1210 info!("handling dropped mcs file {}", path);
1211 let mut codec = McsCodec::from_file(path);
1212 let builders = codec.to_entity_builders();
1213 for mut builder in builders {
1214 if !builder.has::<Betas>() {
1215 warn!("The .smpl file didn't have any shape_parameters associated, we are defaulting to the mean smpl shape");
1216 builder.add(Betas::default());
1217 }
1218 let gloss_interop = GlossInterop::default();
1219 let name = scene.get_unused_name();
1220 scene.get_or_create_entity(&name).insert_builder(builder).insert(gloss_interop);
1221 handled = true;
1222 }
1223 let smpl_scene = SceneAnimation::new_with_fps(codec.num_frames, codec.frame_rate);
1224 scene.add_resource(smpl_scene);
1225 }
1226 _ => {
1227 info!("No known filetype {}", path);
1228 }
1229 }
1230 }
1231 }
1232 handled
1233}