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