1use super::scene::CameraTrack;
2use crate::{
3 common::{
4 pose::Pose,
5 types::{ChunkHeader, GltfCompatibilityMode, GltfOutputType, SmplType},
6 },
7 smpl_x::smpl_x,
8};
9use gloss_img::dynamic_image::DynImage;
10use gloss_renderer::geom::Geom;
11use gloss_utils::nshare::ToNalgebra;
12use gltf::binary::Header;
13use gltf_json::validation::{Checked::Valid, USize64};
14use gltf_json::{material::AlphaMode, scene::UnitQuaternion, Node};
15use image::imageops::FilterType;
16use image::RgbImage;
17use itertools::izip;
18use log::info;
19use nalgebra as na;
20use nalgebra::DMatrix;
21use ndarray as nd;
22use ndarray::prelude::*;
23use smpl_utils::numerical::batch_rodrigues;
24use smpl_utils::{
25 log,
26 vector::{
27 addv3f, align_to_multiple_of_four, subv3f, to_padded_byte_vector, vec_from_array0_f, vec_from_vec, vec_to_vec, Vector2f, Vector3f, Vector4f,
28 Vector4s,
29 },
30};
31use std::borrow::Cow;
32use std::{fs, mem};
33use std::{
34 io::{Cursor, Write},
35 path::Path,
36};
37#[repr(u32)]
40#[derive(Debug, Clone, Copy)]
41enum PrimitiveAttrIDs {
42 Indices = 0,
43 Positions = 1,
44 Normals = 2,
45 TexCoords = 3,
46 Joints = 4,
47 Weights = 5,
48}
49#[repr(u32)]
51#[derive(Debug, Clone, Copy)]
52enum BufferViewIDs {
53 Index = 0,
54 VertexAttr = 1,
55 InvBindMat = 2,
56 Keyframe = 3,
57 Animation = 4,
58 Deformation = 5,
59}
60#[derive(Copy, Clone, Debug, bytemuck::NoUninit)]
62#[repr(C)]
63struct Vertex {
64 position: [f32; 3],
65 normal: [f32; 3],
66 uv: [f32; 2],
67 joint_index: [u16; 4],
68 joint_weight: [f32; 4],
69}
70#[derive(Clone, Debug)]
72struct GltfTextureInfo {
73 buffer_size: usize,
74 image_data: Vec<u8>,
75 image: gltf_json::image::Image,
76 buffer_view: gltf_json::buffer::View,
77 buffer_index: usize,
78 texture: gltf_json::texture::Texture,
79 sampler: gltf_json::texture::Sampler,
80}
81#[allow(clippy::struct_field_names)]
83struct SmplTextures {
84 diffuse_index: Option<usize>,
85 normals_index: Option<usize>,
86 metalic_roughtness_index: Option<usize>,
87}
88#[derive(Clone, Default, Debug)]
90pub struct PerBodyData {
91 pub diffuse_textures: Option<DynImage>,
92 pub normals_textures: Option<DynImage>,
93 pub metalness_textures: Option<DynImage>,
94 pub roughness_textures: Option<DynImage>,
95 pub positions: Option<DMatrix<f32>>,
96 pub normals: Option<DMatrix<f32>>,
97 pub default_joint_translations: Option<nd::Array2<f32>>,
98 pub body_translation: Option<nd::Array2<f32>>,
99 pub pose: Option<Pose>,
100 pub body_translations: Option<nd::Array2<f32>>,
101 pub body_rotations: Option<nd::Array3<f32>>,
102 pub body_scales: Option<nd::Array2<f32>>,
103 pub per_frame_blend_weights: Option<nd::Array2<f32>>,
104}
105#[derive(Debug, Clone)]
107pub struct GltfCodec {
108 pub num_bodies: usize,
109 pub smpl_type: SmplType,
110 pub gender: i32,
111 pub faces: Option<DMatrix<u32>>,
112 pub uvs: Option<DMatrix<f32>>,
113 pub joint_index: Option<DMatrix<u32>>,
114 pub joint_weight: Option<DMatrix<f32>>,
115 pub default_joint_poses: Option<nd::Array2<f32>>,
116 pub frame_count: Option<usize>,
117 pub keyframe_times: Option<Vec<f32>>,
118 pub morph_targets: Option<nd::Array3<f32>>,
119 pub per_body_data: Vec<PerBodyData>,
120 pub camera_track: Option<CameraTrack>,
121}
122impl Default for GltfCodec {
123 fn default() -> Self {
124 Self {
125 num_bodies: 1,
126 smpl_type: SmplType::SmplX,
127 gender: 0,
128 faces: None,
129 uvs: None,
130 joint_index: None,
131 joint_weight: None,
132 default_joint_poses: None,
133 frame_count: None,
134 keyframe_times: None,
135 morph_targets: None,
136 per_body_data: Vec::new(),
137 camera_track: None,
138 }
139 }
140}
141impl GltfCodec {
142 pub fn to_file(&mut self, name: &str, path: &str, out_type: GltfOutputType, compatibility_mode: GltfCompatibilityMode) {
144 let parent_path = Path::new(path).parent();
145 let file_name = Path::new(path).file_name();
146 let Some(parent_path) = parent_path else {
147 log!("Error: Exporting GLTF - no directory name found: {}", path);
148 return;
149 };
150 let Some(file_name) = file_name else {
151 log!("Error: Exporting GLTF - no file name found: {}", path);
152 return;
153 };
154 let _ = fs::create_dir(parent_path);
155 let target_extension: &str = match out_type {
156 GltfOutputType::Standard => "gltf",
157 GltfOutputType::Binary => "glb",
158 };
159 let file_name_with_suffix = Path::new(file_name).with_extension(target_extension);
160 log!("Exporting GLTF: {}/{}", path, file_name_with_suffix.to_string_lossy());
161 let binary = matches!(out_type, GltfOutputType::Binary);
162 let (buffer_data, root) = self.create_buffer(name, binary, compatibility_mode);
163 match out_type {
164 GltfOutputType::Standard => {
165 let json_path = parent_path.join(file_name_with_suffix.clone());
166 let bin_path = parent_path.join("buffer0.bin");
167 let writer = fs::File::create(json_path).expect("I/O error");
168 gltf_json::serialize::to_writer_pretty(writer, &root).expect("Serialization error");
169 let bin = to_padded_byte_vector(&buffer_data);
170 let mut writer = fs::File::create(bin_path).expect("I/O error");
171 writer.write_all(&bin).expect("I/O error");
172 info!("Written glTF json + bin to {parent_path:?}");
173 }
174 GltfOutputType::Binary => {
175 let json_string = gltf_json::serialize::to_string(&root).expect("Serialization error");
176 let mut length = mem::size_of::<Header>() + mem::size_of::<ChunkHeader>() + json_string.len();
177 align_to_multiple_of_four(&mut length);
178 length += mem::size_of::<ChunkHeader>() + buffer_data.len();
179 align_to_multiple_of_four(&mut length);
180 let glb = gltf::binary::Glb {
181 header: gltf::binary::Header {
182 magic: *b"glTF",
183 version: 2,
184 length: length.try_into().expect("file size exceeds binary glTF limit"),
185 },
186 bin: Some(Cow::Owned(buffer_data)),
187 json: Cow::Owned(json_string.into_bytes()),
188 };
189 let glb_path = parent_path.join(file_name_with_suffix.clone());
190 let writer = std::fs::File::create(glb_path.clone()).expect("I/O error");
191 glb.to_writer(writer).expect("glTF binary output error");
192 info!("Written binary glB to {glb_path:?}");
193 }
194 }
195 }
196 pub fn to_buf(&mut self, compatibility_mode: GltfCompatibilityMode) -> Vec<u8> {
198 let (buffer_data, root) = self.create_buffer("Meshcapade Avatar", true, compatibility_mode);
199 let json_string = gltf_json::serialize::to_string(&root).expect("Serialization error");
200 let mut length = mem::size_of::<Header>() + mem::size_of::<ChunkHeader>() + json_string.len();
201 align_to_multiple_of_four(&mut length);
202 length += mem::size_of::<ChunkHeader>() + buffer_data.len();
203 align_to_multiple_of_four(&mut length);
204 let glb = gltf::binary::Glb {
205 header: gltf::binary::Header {
206 magic: *b"glTF",
207 version: 2,
208 length: length.try_into().expect("file size exceeds binary glTF limit"),
209 },
210 bin: Some(Cow::Owned(buffer_data)),
211 json: Cow::Owned(json_string.into_bytes()),
212 };
213 glb.to_vec().expect("glTF binary output error")
214 }
215 fn is_animated(&self) -> bool {
216 self.frame_count.is_some()
217 }
218 #[allow(clippy::too_many_lines)]
220 #[allow(clippy::cast_possible_truncation)]
221 #[allow(clippy::cast_possible_wrap)]
222 fn create_buffer(&mut self, name: &str, binary: bool, compatibility_mode: GltfCompatibilityMode) -> (Vec<u8>, gltf_json::Root) {
223 assert!(self.faces.is_some(), "GltfCodec: no faces!");
224 assert!(self.uvs.is_some(), "GltfCodec: no uvs!");
225 let mut full_buffer_data = vec![];
226 let mut accessors = vec![];
227 let mut buffers = vec![];
228 let mut buffer_views = vec![];
229 let mut meshes = vec![];
230 let mut nodes = vec![];
231 let mut skins = vec![];
232 let mut materials = vec![];
233 let mut channels = vec![];
234 let mut samplers = vec![];
235 let mut images = vec![];
236 let mut textures = vec![];
237 let mut texture_samplers: Vec<gltf_json::texture::Sampler> = vec![];
238 let mut cameras: Vec<gltf_json::camera::Camera> = vec![];
239 let scene_root_node_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
240 let scene_root_node = Node {
241 name: Some("SceneRoot".to_string()),
242 children: Some(vec![]),
243 ..Default::default()
244 };
245 nodes.push(scene_root_node);
246 if let Some(camera_track) = &self.camera_track {
247 let camera = gltf_json::camera::Camera {
248 name: Some("MoCapadeCamera".to_string()),
249 type_: gltf_json::validation::Checked::Valid(gltf_json::camera::Type::Perspective),
250 perspective: Some(gltf_json::camera::Perspective {
251 yfov: camera_track.yfov,
252 znear: camera_track.znear,
253 zfar: camera_track.zfar,
254 aspect_ratio: camera_track.aspect_ratio,
255 extensions: None,
256 extras: Option::default(),
257 }),
258 orthographic: None,
259 extensions: None,
260 extras: Option::default(),
261 };
262 cameras.push(camera);
263 let camera_track_node = Node {
264 name: Some("AnimatedCamera".to_string()),
265 camera: Some(gltf_json::Index::new(0)),
266 ..Default::default()
267 };
268 let camera_node_idx = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
269 if let Some(ref mut scene_root_node_children) = nodes[0].children {
270 scene_root_node_children.push(gltf_json::Index::new(camera_node_idx));
271 }
272 nodes.push(camera_track_node);
273 }
274 let node_indices: Vec<gltf_json::Index<Node>> = vec![gltf_json::Index::new(scene_root_node_index)];
275 let scene: gltf_json::Scene = gltf_json::Scene {
276 extensions: Option::default(),
277 extras: Option::default(),
278 name: Some(name.to_string()),
279 nodes: node_indices,
280 };
281 let scenes = vec![scene];
282 for (body_idx, current_body) in self.per_body_data.clone().iter_mut().enumerate() {
283 assert!(current_body.positions.is_some(), "GltfCodec: no vertices for body {body_idx}!");
284 assert!(current_body.normals.is_some(), "GltfCodec: no normals for body {body_idx}!");
285 let mut positions = current_body.positions.clone().unwrap();
286 let normals = current_body.normals.as_ref().unwrap();
287 let faces = self.faces.as_ref().unwrap();
288 let uvs = self.uvs.as_ref().unwrap();
289 let joint_index = self.joint_index.as_ref().unwrap();
290 let joint_weight = self.joint_weight.as_ref().unwrap();
291 let diffuse_tex = current_body.diffuse_textures.as_ref();
292 let normals_tex = current_body.normals_textures.as_ref();
293 let metalness_tex = current_body.metalness_textures.as_ref();
294 let roughness_tex = current_body.roughness_textures.as_ref();
295 let mut vertex_attributes_array: Vec<Vertex> = vec![];
296 let mut indices_array: Vec<u32> = vec![];
297 let mut inverse_bind_matrices: Vec<f32> = vec![];
298 let face_count = faces.shape().0;
299 let vertex_count = positions.shape().0;
300 let joint_count = current_body.default_joint_translations.as_ref().unwrap().shape()[0];
301 for row in faces.row_iter() {
302 indices_array.extend_from_slice(&[row[0], row[1], row[2]]);
303 }
304 let joint_rotations = batch_rodrigues(self.default_joint_poses.as_ref().unwrap());
305 let mut joint_translations = current_body.default_joint_translations.clone().unwrap();
306 if compatibility_mode == GltfCompatibilityMode::Unreal {
307 let (min, _) = Geom::get_bounding_points(&positions, None);
308 let min_vec: Vec<f32> = min.iter().copied().collect();
309 let min_y = min_vec[1];
310 let offset = na::RowVector3::new(0.0, min_y, 0.0);
311 for i in 0..positions.nrows() {
312 let mut row = positions.row_mut(i);
313 row -= offset;
314 }
315 let offset_nd = ndarray::Array1::from_vec(vec![0.0, min_y, 0.0]);
316 for mut row in joint_translations.axis_iter_mut(Axis(0)) {
317 row -= &offset_nd;
318 }
319 current_body.positions = Some(positions.clone());
320 current_body.default_joint_translations = Some(joint_translations.clone());
321 }
322 let metadata = crate::common::metadata::smpl_metadata(&self.smpl_type);
323 let bind_matrices = self.create_bind_matrices(&joint_rotations, &joint_translations, &metadata.joint_parents);
324 let unreal_mapping: [usize; 10] = [0, 0, 0, 8, 7, 0, 21, 21, 20, 0];
325 if compatibility_mode == GltfCompatibilityMode::Unreal {
326 for j_idx in unreal_mapping {
327 let mut inverse_bind_matrix = nd::Array2::<f32>::zeros((4, 4));
328 let inverse_rotation_matrix = bind_matrices.slice(s![j_idx, 0..3, 0..3]).reversed_axes();
329 let translation: nd::Array1<f32> = if j_idx == 0 {
330 nd::Array1::from_vec(vec![0.0, 0.0, 0.0])
331 } else {
332 bind_matrices.slice(s![j_idx, 0..3, 3]).to_owned()
333 };
334 let inverse_translation = -inverse_rotation_matrix.dot(&translation);
335 inverse_bind_matrix.slice_mut(s![0..3, 0..3]).assign(&inverse_rotation_matrix);
336 inverse_bind_matrix.slice_mut(s![0..3, 3]).assign(&inverse_translation);
337 inverse_bind_matrix[(3, 3)] = 1.0;
338 inverse_bind_matrices.extend(inverse_bind_matrix.t().iter());
339 }
340 }
341 for j_idx in 0..joint_count {
342 let mut inverse_bind_matrix = nd::Array2::<f32>::zeros((4, 4));
343 let inverse_rotation_matrix = bind_matrices.slice(s![j_idx, 0..3, 0..3]).reversed_axes();
344 let translation: nd::Array1<f32> = bind_matrices.slice(s![j_idx, 0..3, 3]).to_owned();
345 let inverse_translation = -inverse_rotation_matrix.dot(&translation);
346 inverse_bind_matrix.slice_mut(s![0..3, 0..3]).assign(&inverse_rotation_matrix);
347 inverse_bind_matrix.slice_mut(s![0..3, 3]).assign(&inverse_translation);
348 inverse_bind_matrix[(3, 3)] = 1.0;
349 inverse_bind_matrices.extend(inverse_bind_matrix.t().iter());
350 }
351 let num_extra_joints: usize = if compatibility_mode == GltfCompatibilityMode::Unreal { 10 } else { 0 };
352 for (position, normal, uv, joint_index, joint_weight) in izip!(
353 positions.row_iter(),
354 normals.row_iter(),
355 uvs.row_iter(),
356 joint_index.row_iter(),
357 joint_weight.row_iter(),
358 ) {
359 let jw_sum = joint_weight.iter().sum::<f32>();
360 vertex_attributes_array.push(Vertex {
361 position: Vector3f::new(position[0], position[1], position[2]).into(),
362 normal: Vector3f::new(normal[0], normal[1], normal[2]).into(),
363 uv: Vector2f::new(uv[0], 1.0 - uv[1]).into(),
364 joint_index: Vector4s::new(
365 u16::try_from(joint_index[0] + u32::try_from(num_extra_joints).unwrap()).expect("Could not convert to u16!"),
366 u16::try_from(joint_index[1] + u32::try_from(num_extra_joints).unwrap()).expect("Could not convert to u16!"),
367 u16::try_from(joint_index[2] + u32::try_from(num_extra_joints).unwrap()).expect("Could not convert to u16!"),
368 u16::try_from(joint_index[3] + u32::try_from(num_extra_joints).unwrap()).expect("Could not convert to u16!"),
369 )
370 .into(),
371 joint_weight: Vector4f::new(
372 joint_weight[0] / jw_sum,
373 joint_weight[1] / jw_sum,
374 joint_weight[2] / jw_sum,
375 joint_weight[3] / jw_sum,
376 )
377 .into(),
378 });
379 }
380 let mut texture_infos: Vec<GltfTextureInfo> = vec![];
381 let mut smpl_textures = SmplTextures {
382 diffuse_index: None,
383 normals_index: None,
384 metalic_roughtness_index: None,
385 };
386 if let Some(img) = diffuse_tex {
387 let diffuse_tex = self.add_texture(img, texture_infos.len(), "diffuse");
388 if let Some(diffuse_tex) = diffuse_tex {
389 smpl_textures.diffuse_index = Some(texture_infos.len());
390 texture_infos.push(diffuse_tex);
391 }
392 }
393 self.prepare_normals(&mut smpl_textures, &mut texture_infos, normals_tex);
394 self.prepare_metallic_roughness(&mut smpl_textures, &mut texture_infos, metalness_tex, roughness_tex);
395 let mut base_color_texture: Option<gltf_json::texture::Info> = None;
396 let mut normal_texture: Option<gltf_json::material::NormalTexture> = None;
397 let mut metallic_roughness_texture: Option<gltf_json::texture::Info> = None;
398 if let Some(diffuse_texture_index) = smpl_textures.diffuse_index {
399 base_color_texture = Some(gltf_json::texture::Info {
400 index: gltf_json::Index::new(u32::try_from(diffuse_texture_index).expect("Could not convert to u32!")),
401 tex_coord: 0,
402 extensions: None,
403 extras: None,
404 });
405 }
406 if let Some(normal_texture_index) = smpl_textures.normals_index {
407 normal_texture = Some(gltf_json::material::NormalTexture {
408 scale: 1.,
409 index: gltf_json::Index::new(u32::try_from(normal_texture_index).expect("Could not convert to u32!")),
410 tex_coord: 0,
411 extensions: None,
412 extras: None,
413 });
414 }
415 if let Some(metallic_roughness_texture_index) = smpl_textures.metalic_roughtness_index {
416 metallic_roughness_texture = Some(gltf_json::texture::Info {
417 index: gltf_json::Index::new(u32::try_from(metallic_roughness_texture_index).expect("Could not convert to u32!")),
418 tex_coord: 0,
419 extensions: None,
420 extras: None,
421 });
422 }
423 let material = gltf_json::Material {
424 alpha_cutoff: None,
425 alpha_mode: gltf_json::validation::Checked::<AlphaMode>::Valid(AlphaMode::Opaque),
426 double_sided: false,
427 name: Some("SMPL_material".to_string()),
428 pbr_metallic_roughness: gltf_json::material::PbrMetallicRoughness {
429 base_color_factor: gltf_json::material::PbrBaseColorFactor([1., 1., 1., 1.]),
430 base_color_texture,
431 metallic_roughness_texture,
432 ..Default::default()
433 },
434 normal_texture,
435 occlusion_texture: None,
436 emissive_texture: None,
437 emissive_factor: gltf_json::material::EmissiveFactor([0., 0., 0.]),
438 extensions: None,
439 extras: None,
440 };
441 materials.push(material);
442 let mut morph_targets: Option<Vec<gltf_json::mesh::MorphTarget>> = None;
443 if self.num_morph_targets() > 0 && self.is_animated() {
444 let mut morph_target_accessors_start_idx = 7 + self.per_body_data[0].default_joint_translations.as_ref().unwrap().shape()[0] + 4;
445 if compatibility_mode == GltfCompatibilityMode::Unreal {
446 morph_target_accessors_start_idx += 1;
447 }
448 morph_targets = Some(self.create_morph_targets(morph_target_accessors_start_idx));
449 }
450 let primitive_offset = accessors.len() as u32;
451 let primitive = gltf_json::mesh::Primitive {
452 attributes: {
453 let mut map = std::collections::BTreeMap::new();
454 map.insert(
455 Valid(gltf_json::mesh::Semantic::Positions),
456 gltf_json::Index::new(PrimitiveAttrIDs::Positions as u32 + primitive_offset),
457 );
458 map.insert(
459 Valid(gltf_json::mesh::Semantic::Normals),
460 gltf_json::Index::new(PrimitiveAttrIDs::Normals as u32 + primitive_offset),
461 );
462 map.insert(
463 Valid(gltf_json::mesh::Semantic::TexCoords(0)),
464 gltf_json::Index::new(PrimitiveAttrIDs::TexCoords as u32 + primitive_offset),
465 );
466 map.insert(
467 Valid(gltf_json::mesh::Semantic::Joints(0)),
468 gltf_json::Index::new(PrimitiveAttrIDs::Joints as u32 + primitive_offset),
469 );
470 map.insert(
471 Valid(gltf_json::mesh::Semantic::Weights(0)),
472 gltf_json::Index::new(PrimitiveAttrIDs::Weights as u32 + primitive_offset),
473 );
474 map
475 },
476 extensions: Option::default(),
477 extras: Option::default(),
478 indices: Some(gltf_json::Index::new(PrimitiveAttrIDs::Indices as u32 + primitive_offset)),
479 material: Some(gltf_json::Index::new(body_idx as u32)),
480 mode: Valid(gltf_json::mesh::Mode::Triangles),
481 targets: morph_targets,
482 };
483 let mut morph_target_weights: Option<Vec<f32>> = None;
484 if self.num_morph_targets() > 0 && self.is_animated() {
485 morph_target_weights = Some(vec![0.0; self.num_morph_targets()]);
486 }
487 let mesh = gltf_json::Mesh {
488 extensions: Option::default(),
489 extras: Option::default(),
490 name: Some(format!("SMPL_mesh_{body_idx}")),
491 primitives: vec![primitive],
492 weights: morph_target_weights,
493 };
494 meshes.push(mesh);
495 let vertex_data = to_padded_byte_vector(&vertex_attributes_array);
496 let index_data = to_padded_byte_vector(&indices_array);
497 let inv_bind_mat_data = to_padded_byte_vector(&inverse_bind_matrices);
498 let mut per_view_running_offset: [usize; 6] = [0, 0, 0, 0, 0, 0];
499 let current_buffer_view_offset = buffer_views.len() as u32;
500 let current_accessor_offset = accessors.len() as u32;
501 let accessor = self.create_accessors(
502 body_idx,
503 vertex_count,
504 face_count,
505 joint_count,
506 current_buffer_view_offset,
507 &mut per_view_running_offset,
508 num_extra_joints,
509 compatibility_mode,
510 );
511 accessors.extend(accessor);
512 let mut current_buffer_views = vec![];
513 self.create_buffer_views(
514 body_idx as u32,
515 full_buffer_data.len(),
516 vertex_count,
517 face_count,
518 joint_count,
519 num_extra_joints,
520 &mut current_buffer_views,
521 compatibility_mode,
522 );
523 let (buffer_data, composed_buffer_views) = self.compose_buffer_views(
524 body_idx,
525 current_buffer_views.clone(),
526 index_data.as_slice(),
527 vertex_data.as_slice(),
528 inv_bind_mat_data.as_slice(),
529 &mut texture_infos,
530 compatibility_mode,
531 );
532 full_buffer_data.extend(buffer_data);
533 buffer_views.extend(composed_buffer_views);
534 for texture in texture_infos {
535 images.push(texture.image);
536 textures.push(texture.texture);
537 texture_samplers.push(texture.sampler);
538 }
539 let armature_node_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
540 let armature_node = Node {
541 name: Some(format!("Armature_{body_idx}")),
542 children: Some(vec![]),
543 ..Default::default()
544 };
545 nodes.push(armature_node);
546 if let Some(ref mut scene_armatures) = nodes[0].children {
547 scene_armatures.push(gltf_json::Index::new(armature_node_index));
548 }
549 let mesh_skin_binding_node_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
550 let mesh_skin_binding_node = Node {
551 mesh: Some(gltf_json::Index::new(body_idx as u32)),
552 skin: Some(gltf_json::Index::new(body_idx as u32)),
553 name: Some(format!("MeshSkinBinding_{body_idx}")),
554 children: None,
555 ..Default::default()
556 };
557 nodes.push(mesh_skin_binding_node);
558 let root_node_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
559 let mut joints = vec![];
560 if compatibility_mode == GltfCompatibilityMode::Unreal {
561 let root_node = Node {
562 name: Some("root".to_string()),
563 translation: Some([0.0, 0.0, 0.0]),
564 children: Some(vec![]),
565 ..Default::default()
566 };
567 let joint_index = gltf_json::Index::<Node>::new(u32::try_from(nodes.len()).expect("Issue converting Joint idx to u32"));
568 nodes.push(root_node);
569 joints.push(joint_index);
570 let add_empty_node = |nodes: &mut Vec<Node>,
571 joints: &mut Vec<gltf_json::Index<Node>>,
572 name: &str,
573 parent_index: u32,
574 has_children: bool,
575 reference_bone: usize|
576 -> u32 {
577 let relative_parent_idx = parent_index - root_node_index;
578 let node_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
579 let unit_quaternion = [0.0, 0.0, 0.0, 1.0];
580 let trans: Vec<f32> = if reference_bone == 0 {
581 vec![0.0, 0.0, 0.0]
582 } else {
583 let trans = vec_from_vec(&joint_translations.row(reference_bone).to_vec());
584 let parent_trans = if unreal_mapping[relative_parent_idx as usize] != 0 {
585 vec_from_vec(&joint_translations.row(unreal_mapping[relative_parent_idx as usize]).to_vec())
586 } else {
587 vec_from_vec(&[0.0, 0.0, 0.0])
588 };
589 vec_to_vec(&subv3f(&trans, &parent_trans))
590 };
591 let translation = [trans[0], trans[1], trans[2]];
592 let new_node = Node {
593 name: Some(name.to_string()),
594 rotation: Some(UnitQuaternion(unit_quaternion)),
595 translation: Some(translation),
596 children: if has_children { Some(vec![]) } else { None },
597 ..Default::default()
598 };
599 if let Some(ref mut parent_children) = nodes[parent_index as usize].children {
600 parent_children.push(gltf_json::Index::new(node_index));
601 }
602 let joint_index = gltf_json::Index::<Node>::new(node_index);
603 nodes.push(new_node);
604 joints.push(joint_index);
605 node_index
606 };
607 add_empty_node(&mut nodes, &mut joints, "center_of_mass", root_node_index, false, 0);
608 let ik_foot_root_index = add_empty_node(&mut nodes, &mut joints, "ik_foot_root", root_node_index, true, 0);
609 add_empty_node(&mut nodes, &mut joints, "ik_foot_r", ik_foot_root_index, false, 8);
610 add_empty_node(&mut nodes, &mut joints, "ik_foot_l", ik_foot_root_index, false, 7);
611 let ik_hand_root_index = add_empty_node(&mut nodes, &mut joints, "ik_hand_root", root_node_index, true, 0);
612 let ik_hand_gun_index = add_empty_node(&mut nodes, &mut joints, "ik_hand_gun", ik_hand_root_index, true, 21);
613 add_empty_node(&mut nodes, &mut joints, "ik_hand_r", ik_hand_gun_index, false, 21);
614 add_empty_node(&mut nodes, &mut joints, "ik_hand_l", ik_hand_gun_index, false, 20);
615 add_empty_node(&mut nodes, &mut joints, "interaction", root_node_index, false, 0);
616 }
617 let skeleton_root_index = self.add_skin(
618 format!("Skin_{body_idx}"),
619 body_idx,
620 armature_node_index,
621 current_accessor_offset,
622 &mut nodes,
623 &mut skins,
624 &mut joints,
625 compatibility_mode,
626 );
627 if compatibility_mode == GltfCompatibilityMode::Unreal {
628 if let Some(ref mut root) = nodes[root_node_index as usize].children {
629 root.push(skeleton_root_index);
630 }
631 }
632 if let Some(ref mut armature_children) = nodes[armature_node_index as usize].children {
633 armature_children.push(gltf_json::Index::new(mesh_skin_binding_node_index));
634 armature_children.push(gltf_json::Index::new(root_node_index));
635 }
636 if self.is_animated() {
637 let animation_channels = self.create_animation_channels(
638 joint_count,
639 root_node_index,
640 skeleton_root_index.value(),
641 samplers.len(),
642 compatibility_mode,
643 );
644 let animation_samplers = self.create_animation_samplers(joint_count, current_accessor_offset, compatibility_mode);
645 channels.extend(animation_channels);
646 samplers.extend(animation_samplers);
647 }
648 }
649 if self.camera_track.is_some() {
650 let (cam_track_buffer_views, cam_track_buffer_data) = self.create_camera_animation_buffer_views(&mut full_buffer_data.len()).unwrap();
651 let cam_track_accessors = self.create_camera_animation_accessors(buffer_views.len() as u32).unwrap();
652 let (cam_track_channels, cam_track_samplers) = self
653 .create_camera_animation_channels_and_samplers(accessors.len() as u32, 1, samplers.len() as u32)
654 .unwrap();
655 buffer_views.extend(cam_track_buffer_views);
656 full_buffer_data.extend(cam_track_buffer_data);
657 accessors.extend(cam_track_accessors);
658 channels.extend(cam_track_channels);
659 samplers.extend(cam_track_samplers);
660 }
661 let buffer = gltf_json::Buffer {
662 byte_length: USize64::from(full_buffer_data.len()),
663 extensions: Option::default(),
664 extras: Option::default(),
665 name: Some("scene_buffer".to_string()),
666 uri: if binary { None } else { Some("buffer0.bin".into()) },
667 };
668 buffers.push(buffer);
669 let mut animations: Vec<gltf_json::Animation> = vec![];
670 if self.is_animated() {
671 let animation = gltf_json::Animation {
672 extensions: Option::default(),
673 extras: Option::default(),
674 channels,
675 name: Some("Scene_animation".to_string()),
676 samplers,
677 };
678 animations.push(animation);
679 }
680 let root = gltf_json::Root {
681 accessors,
682 animations,
683 buffers,
684 buffer_views,
685 cameras,
686 images,
687 materials,
688 meshes,
689 nodes,
690 samplers: texture_samplers,
691 scenes,
692 skins,
693 textures,
694 ..Default::default()
695 };
696 (full_buffer_data, root)
697 }
698 #[allow(clippy::too_many_arguments)]
700 fn create_buffer_views(
701 &self,
702 body_idx: u32,
703 mut running_offset: usize,
704 vertex_count: usize,
705 face_count: usize,
706 joint_count: usize,
707 num_extra_joints: usize,
708 buffer_views: &mut Vec<gltf_json::buffer::View>,
709 compatibility_mode: GltfCompatibilityMode,
710 ) {
711 let index_buffer_size = face_count * 3 * mem::size_of::<u32>();
712 let index_buffer_view = gltf_json::buffer::View {
713 buffer: gltf_json::Index::new(0),
714 byte_length: USize64::from(index_buffer_size),
715 byte_offset: Some(USize64::from(running_offset)),
716 byte_stride: None,
717 extensions: Option::default(),
718 extras: Option::default(),
719 name: Some(format!("index_buffer_view_{body_idx}")),
720 target: Some(Valid(gltf_json::buffer::Target::ElementArrayBuffer)),
721 };
722 buffer_views.push(index_buffer_view);
723 running_offset += index_buffer_size;
724 let vertex_buffer_size = vertex_count * mem::size_of::<Vertex>();
725 let vertex_buffer_view = gltf_json::buffer::View {
726 buffer: gltf_json::Index::new(0),
727 byte_length: USize64::from(vertex_buffer_size),
728 byte_offset: Some(USize64::from(running_offset)),
729 byte_stride: Some(gltf_json::buffer::Stride(mem::size_of::<Vertex>())),
730 extensions: Option::default(),
731 extras: Option::default(),
732 name: Some(format!("vertex_buffer_view_{body_idx}")),
733 target: Some(Valid(gltf_json::buffer::Target::ArrayBuffer)),
734 };
735 buffer_views.push(vertex_buffer_view);
736 running_offset += vertex_buffer_size;
737 let inv_bind_matrix_buffer_size = (joint_count + num_extra_joints) * 16 * mem::size_of::<f32>();
738 let inverse_bind_mat_buffer_view = gltf_json::buffer::View {
739 buffer: gltf_json::Index::new(0),
740 byte_length: USize64::from(inv_bind_matrix_buffer_size),
741 byte_offset: Some(USize64::from(running_offset)),
742 byte_stride: None,
743 extensions: Option::default(),
744 extras: Option::default(),
745 name: Some(format!("inv_bind_matrix_buffer_view_{body_idx}")),
746 target: None,
747 };
748 buffer_views.push(inverse_bind_mat_buffer_view);
749 running_offset += inv_bind_matrix_buffer_size;
750 if self.is_animated() {
751 let rotation_animation_buffer_size = self.frame_count.unwrap() * 4 * mem::size_of::<f32>();
752 let translation_animation_buffer_size = self.frame_count.unwrap() * 3 * mem::size_of::<f32>();
753 let scale_animation_buffer_size = self.frame_count.unwrap() * 3 * mem::size_of::<f32>();
754 let animation_buffer_views = self.create_animation_buffer_views(
755 body_idx,
756 joint_count,
757 rotation_animation_buffer_size,
758 translation_animation_buffer_size,
759 scale_animation_buffer_size,
760 &mut running_offset,
761 compatibility_mode,
762 );
763 buffer_views.extend(animation_buffer_views);
764 if self.num_morph_targets() > 0 && body_idx == 0 {
765 let morph_target_buffer_size = vertex_count * 3 * mem::size_of::<f32>();
766 let morph_target_buffer_views = self.create_morph_target_buffer_views(morph_target_buffer_size, &mut running_offset);
767 buffer_views.extend(morph_target_buffer_views);
768 }
769 }
770 }
771 #[allow(clippy::too_many_arguments)]
773 fn create_animation_buffer_views(
774 &self,
775 body_idx: u32,
776 joint_count: usize,
777 rotation_buffer_size: usize,
778 translation_buffer_size: usize,
779 scale_buffer_size: usize,
780 running_offset: &mut usize,
781 compatibility_mode: GltfCompatibilityMode,
782 ) -> Vec<gltf_json::buffer::View> {
783 let mut animation_buffer_views: Vec<gltf_json::buffer::View> = vec![];
784 let keyframe_buffer_size = self.frame_count.unwrap() * mem::size_of::<f32>();
785 let keyframe_buffer_view = gltf_json::buffer::View {
786 buffer: gltf_json::Index::new(0),
787 byte_length: USize64::from(keyframe_buffer_size),
788 byte_offset: Some(USize64::from(*running_offset)),
789 byte_stride: None,
790 extensions: Option::default(),
791 extras: Option::default(),
792 name: Some(format!("keyframe_buffer_view_{body_idx}")),
793 target: None,
794 };
795 animation_buffer_views.push(keyframe_buffer_view);
796 *running_offset += keyframe_buffer_size;
797 for j_idx in 0..joint_count {
798 let buffer_view_name = format!("joint_{j_idx}_animations_buffer_view_{body_idx}");
799 let animation_buffer_view = gltf_json::buffer::View {
800 buffer: gltf_json::Index::new(0),
801 byte_length: USize64::from(rotation_buffer_size),
802 byte_offset: Some(USize64::from(*running_offset)),
803 byte_stride: None,
804 extensions: Option::default(),
805 extras: Option::default(),
806 name: Some(buffer_view_name),
807 target: None,
808 };
809 animation_buffer_views.push(animation_buffer_view);
810 *running_offset += rotation_buffer_size;
811 }
812 let buffer_view_name = format!("root_translation_animations_buffer_view_{body_idx}");
813 let animation_buffer_view = gltf_json::buffer::View {
814 buffer: gltf_json::Index::new(0),
815 byte_length: USize64::from(translation_buffer_size),
816 byte_offset: Some(USize64::from(*running_offset)),
817 byte_stride: None,
818 extensions: Option::default(),
819 extras: Option::default(),
820 name: Some(buffer_view_name),
821 target: None,
822 };
823 animation_buffer_views.push(animation_buffer_view);
824 *running_offset += translation_buffer_size;
825 let buffer_view_name = format!("root_scale_animations_buffer_view_{body_idx}");
826 let animation_buffer_view = gltf_json::buffer::View {
827 buffer: gltf_json::Index::new(0),
828 byte_length: USize64::from(scale_buffer_size),
829 byte_offset: Some(USize64::from(*running_offset)),
830 byte_stride: None,
831 extensions: Option::default(),
832 extras: Option::default(),
833 name: Some(buffer_view_name),
834 target: None,
835 };
836 animation_buffer_views.push(animation_buffer_view);
837 *running_offset += scale_buffer_size;
838 if compatibility_mode == GltfCompatibilityMode::Unreal {
839 let buffer_view_name = format!("pelvis_rel_translation_animations_buffer_view_{body_idx}");
840 let animation_buffer_view = gltf_json::buffer::View {
841 buffer: gltf_json::Index::new(0),
842 byte_length: USize64::from(translation_buffer_size),
843 byte_offset: Some(USize64::from(*running_offset)),
844 byte_stride: None,
845 extensions: Option::default(),
846 extras: Option::default(),
847 name: Some(buffer_view_name),
848 target: None,
849 };
850 animation_buffer_views.push(animation_buffer_view);
851 *running_offset += translation_buffer_size;
852 }
853 if self.num_morph_targets() > 0 {
854 let morph_weights_buffer_size = self.frame_count.unwrap() * self.num_morph_targets() * mem::size_of::<f32>();
855 let buffer_view_name = format!("morph_target_weights_{body_idx}");
856 let morph_weights_buffer_view = gltf_json::buffer::View {
857 buffer: gltf_json::Index::new(0),
858 byte_length: USize64::from(morph_weights_buffer_size),
859 byte_offset: Some(USize64::from(*running_offset)),
860 byte_stride: None,
861 extensions: Option::default(),
862 extras: Option::default(),
863 name: Some(buffer_view_name),
864 target: None,
865 };
866 animation_buffer_views.push(morph_weights_buffer_view);
867 *running_offset += morph_weights_buffer_size;
868 }
869 animation_buffer_views
870 }
871 fn create_morph_target_buffer_views(&self, morph_target_buffer_size: usize, running_offset: &mut usize) -> Vec<gltf_json::buffer::View> {
873 let mut morph_targets_buffer_views: Vec<gltf_json::buffer::View> = vec![];
874 for morph_target_idx in 0..self.num_morph_targets() {
875 let buffer_view_name = format!("morph_{morph_target_idx}_buffer_view");
876 let morph_target_buffer_view = gltf_json::buffer::View {
877 buffer: gltf_json::Index::new(0),
878 byte_length: USize64::from(morph_target_buffer_size),
879 byte_offset: Some(USize64::from(*running_offset)),
880 byte_stride: None,
881 extensions: Option::default(),
882 extras: Option::default(),
883 name: Some(buffer_view_name),
884 target: Some(Valid(gltf_json::buffer::Target::ArrayBuffer)),
885 };
886 morph_targets_buffer_views.push(morph_target_buffer_view);
887 *running_offset += morph_target_buffer_size;
888 }
889 morph_targets_buffer_views
890 }
891 #[allow(clippy::too_many_lines)]
893 #[allow(clippy::too_many_arguments)]
894 fn create_accessors(
895 &self,
896 body_idx: usize,
897 vertex_count: usize,
898 face_count: usize,
899 joint_count: usize,
900 current_buffer_view_offset: u32,
901 per_view_running_offset: &mut [usize; 6],
902 num_extra_joints: usize,
903 compatibility_mode: GltfCompatibilityMode,
904 ) -> Vec<gltf_json::Accessor> {
905 let (min, max) = Geom::get_bounding_points(self.per_body_data[body_idx].positions.as_ref().unwrap(), None);
906 let (min_vec, max_vec): (Vec<f32>, Vec<f32>) = (min.iter().copied().collect(), max.iter().copied().collect());
907 let mut accessors: Vec<gltf_json::Accessor> = vec![];
908 let indices = gltf_json::Accessor {
909 buffer_view: Some(gltf_json::Index::new(BufferViewIDs::Index as u32 + current_buffer_view_offset)),
910 byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Index as usize])),
911 count: USize64::from(face_count * 3),
912 component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::U32)),
913 extensions: Option::default(),
914 extras: Option::default(),
915 type_: Valid(gltf_json::accessor::Type::Scalar),
916 min: Some(gltf_json::Value::from(Vec::from([self.faces.as_ref().unwrap().min()]))),
917 max: Some(gltf_json::Value::from(Vec::from([self.faces.as_ref().unwrap().max()]))),
918 name: Some(format!("index_accessor_{body_idx}")),
919 normalized: false,
920 sparse: None,
921 };
922 accessors.push(indices);
923 let position_element_size = 3 * mem::size_of::<f32>();
924 let positions = gltf_json::Accessor {
925 buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
926 byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
927 count: USize64::from(vertex_count),
928 component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
929 extensions: Option::default(),
930 extras: Option::default(),
931 type_: Valid(gltf_json::accessor::Type::Vec3),
932 min: Some(gltf_json::Value::from(min_vec)),
933 max: Some(gltf_json::Value::from(max_vec)),
934 name: Some(format!("position_accessor_{body_idx}")),
935 normalized: false,
936 sparse: None,
937 };
938 per_view_running_offset[BufferViewIDs::VertexAttr as usize] += position_element_size;
939 accessors.push(positions);
940 let normal_element_size = 3 * mem::size_of::<f32>();
941 let normals = gltf_json::Accessor {
942 buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
943 byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
944 count: USize64::from(vertex_count),
945 component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
946 extensions: Option::default(),
947 extras: Option::default(),
948 type_: Valid(gltf_json::accessor::Type::Vec3),
949 min: None,
950 max: None,
951 name: Some(format!("normal_accessor_{body_idx}")),
952 normalized: false,
953 sparse: None,
954 };
955 per_view_running_offset[BufferViewIDs::VertexAttr as usize] += normal_element_size;
956 accessors.push(normals);
957 let uv_element_size = 2 * mem::size_of::<f32>();
958 let uvs = gltf_json::Accessor {
959 buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
960 byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
961 count: USize64::from(vertex_count),
962 component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
963 extensions: Option::default(),
964 extras: Option::default(),
965 type_: Valid(gltf_json::accessor::Type::Vec2),
966 min: None,
967 max: None,
968 name: Some("uv_accessor".to_string()),
969 normalized: false,
970 sparse: None,
971 };
972 per_view_running_offset[BufferViewIDs::VertexAttr as usize] += uv_element_size;
973 accessors.push(uvs);
974 let joint_index_element_size = 4 * mem::size_of::<u16>();
975 let joint_indices = gltf_json::Accessor {
976 buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
977 byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
978 count: USize64::from(vertex_count),
979 component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::U16)),
980 extensions: Option::default(),
981 extras: Option::default(),
982 type_: Valid(gltf_json::accessor::Type::Vec4),
983 min: None,
984 max: None,
985 name: Some("joint_index_accessor".to_string()),
986 normalized: false,
987 sparse: None,
988 };
989 per_view_running_offset[BufferViewIDs::VertexAttr as usize] += joint_index_element_size;
990 accessors.push(joint_indices);
991 let joint_weight_element_size = 4 * mem::size_of::<f32>();
992 let joint_weights = gltf_json::Accessor {
993 buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
994 byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
995 count: USize64::from(vertex_count),
996 component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
997 extensions: Option::default(),
998 extras: Option::default(),
999 type_: Valid(gltf_json::accessor::Type::Vec4),
1000 min: None,
1001 max: None,
1002 name: Some("joint_index_accessor".to_string()),
1003 normalized: false,
1004 sparse: None,
1005 };
1006 per_view_running_offset[BufferViewIDs::VertexAttr as usize] += joint_weight_element_size;
1007 accessors.push(joint_weights);
1008 let inv_bind_matrices = gltf_json::Accessor {
1009 buffer_view: Some(gltf_json::Index::new(BufferViewIDs::InvBindMat as u32 + current_buffer_view_offset)),
1010 byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::InvBindMat as usize])),
1011 count: USize64::from(joint_count + num_extra_joints),
1012 component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1013 extensions: Option::default(),
1014 extras: Option::default(),
1015 type_: Valid(gltf_json::accessor::Type::Mat4),
1016 min: None,
1017 max: None,
1018 name: Some("inv_bind_matrices_accessor".to_string()),
1019 normalized: false,
1020 sparse: None,
1021 };
1022 accessors.push(inv_bind_matrices);
1023 if self.is_animated() {
1024 let animation_accessors =
1025 self.create_animation_accessors(joint_count, current_buffer_view_offset, per_view_running_offset, compatibility_mode);
1026 accessors.extend(animation_accessors);
1027 if self.num_morph_targets() > 0 && body_idx == 0 {
1028 let morph_target_accessors = self.create_morph_target_accessors(vertex_count, current_buffer_view_offset, per_view_running_offset);
1029 accessors.extend(morph_target_accessors);
1030 }
1031 }
1032 accessors
1033 }
1034 #[allow(clippy::too_many_lines)]
1036 fn create_animation_accessors(
1037 &self,
1038 joint_count: usize,
1039 current_buffer_view_offset: u32,
1040 per_view_running_offset: &mut [usize; 6],
1041 compatibility_mode: GltfCompatibilityMode,
1042 ) -> Vec<gltf_json::Accessor> {
1043 let mut animation_accessors: Vec<gltf_json::Accessor> = vec![];
1044 let min_keyframe = self
1045 .keyframe_times
1046 .as_ref()
1047 .expect("keyframe_times should exist")
1048 .iter()
1049 .copied()
1050 .min_by(|a, b| a.partial_cmp(b).expect("Tried to compare a NaN"))
1051 .expect("keyframe_times should have elements in the vector");
1052 let max_keyframe = self
1053 .keyframe_times
1054 .as_ref()
1055 .unwrap()
1056 .iter()
1057 .copied()
1058 .max_by(|a, b| a.partial_cmp(b).expect("Tried to compare a NaN"))
1059 .unwrap();
1060 let keyframe_times = gltf_json::Accessor {
1061 buffer_view: Some(gltf_json::Index::new(BufferViewIDs::Keyframe as u32 + current_buffer_view_offset)),
1062 byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Keyframe as usize])),
1063 count: USize64::from(self.frame_count.unwrap()),
1064 component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1065 extensions: Option::default(),
1066 extras: Option::default(),
1067 type_: Valid(gltf_json::accessor::Type::Scalar),
1068 min: Some(gltf_json::Value::from(Vec::from([min_keyframe]))),
1069 max: Some(gltf_json::Value::from(Vec::from([max_keyframe]))),
1070 name: Some("keyframes_accessor".to_string()),
1071 normalized: false,
1072 sparse: None,
1073 };
1074 animation_accessors.push(keyframe_times);
1075 let mut running_buffer_view = BufferViewIDs::Animation as u32 + current_buffer_view_offset;
1076 for j_idx in 0..joint_count {
1077 let accessor_name = format!("joint_{j_idx}_animations_accessor");
1078 let joint_animation_accessor = gltf_json::Accessor {
1079 buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1080 byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1081 count: USize64::from(self.frame_count.unwrap()),
1082 component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1083 extensions: Option::default(),
1084 extras: Option::default(),
1085 type_: Valid(gltf_json::accessor::Type::Vec4),
1086 min: None,
1087 max: None,
1088 name: Some(accessor_name),
1089 normalized: false,
1090 sparse: None,
1091 };
1092 animation_accessors.push(joint_animation_accessor);
1093 running_buffer_view += 1;
1094 }
1095 let accessor_name = "root_translation_animations_accessor".to_string();
1096 let body_animation_accessor = gltf_json::Accessor {
1097 buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1098 byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1099 count: USize64::from(self.frame_count.unwrap()),
1100 component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1101 extensions: Option::default(),
1102 extras: Option::default(),
1103 type_: Valid(gltf_json::accessor::Type::Vec3),
1104 min: None,
1105 max: None,
1106 name: Some(accessor_name),
1107 normalized: false,
1108 sparse: None,
1109 };
1110 animation_accessors.push(body_animation_accessor);
1111 running_buffer_view += 1;
1112 let accessor_name = "root_scale_animations_accessor".to_string();
1113 let vis_animation_accessor = gltf_json::Accessor {
1114 buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1115 byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1116 count: USize64::from(self.frame_count.unwrap()),
1117 component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1118 extensions: Option::default(),
1119 extras: Option::default(),
1120 type_: Valid(gltf_json::accessor::Type::Vec3),
1121 min: None,
1122 max: None,
1123 name: Some(accessor_name),
1124 normalized: false,
1125 sparse: None,
1126 };
1127 animation_accessors.push(vis_animation_accessor);
1128 running_buffer_view += 1;
1129 if compatibility_mode == GltfCompatibilityMode::Unreal {
1130 let accessor_name = "pelvis_rel_translation_animations_accessor".to_string();
1131 let pelvis_animation_accessor = gltf_json::Accessor {
1132 buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1133 byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1134 count: USize64::from(self.frame_count.unwrap()),
1135 component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1136 extensions: Option::default(),
1137 extras: Option::default(),
1138 type_: Valid(gltf_json::accessor::Type::Vec3),
1139 min: None,
1140 max: None,
1141 name: Some(accessor_name),
1142 normalized: false,
1143 sparse: None,
1144 };
1145 animation_accessors.push(pelvis_animation_accessor);
1146 running_buffer_view += 1;
1147 }
1148 if self.num_morph_targets() > 0 {
1149 let accessor_name = "morph_targets_weights_accessor".to_string();
1150 let morph_targets_weights_accessor = gltf_json::Accessor {
1151 buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1152 byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1153 count: USize64::from(self.frame_count.unwrap() * self.num_morph_targets()),
1154 component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1155 extensions: Option::default(),
1156 extras: Option::default(),
1157 type_: Valid(gltf_json::accessor::Type::Scalar),
1158 min: None,
1159 max: None,
1160 name: Some(accessor_name),
1161 normalized: false,
1162 sparse: None,
1163 };
1164 animation_accessors.push(morph_targets_weights_accessor);
1165 running_buffer_view += 1;
1166 }
1167 per_view_running_offset[BufferViewIDs::Animation as usize] += running_buffer_view as usize;
1168 animation_accessors
1169 }
1170 fn create_morph_target_accessors(
1172 &self,
1173 vertex_count: usize,
1174 current_buffer_view_offset: u32,
1175 per_view_running_offset: &mut [usize; 6],
1176 ) -> Vec<gltf_json::Accessor> {
1177 let mut morph_target_accessors: Vec<gltf_json::Accessor> = vec![];
1178 let mut running_buffer_view = u32::try_from(per_view_running_offset[BufferViewIDs::Animation as usize]).expect("Could not convert to U32!")
1179 + current_buffer_view_offset;
1180 for morph_target_idx in 0..self.num_morph_targets() {
1181 let accessor_name = format!("morph_{morph_target_idx}_accessor");
1182 let current_morph_target = self.morph_targets.as_ref().unwrap().slice(s![morph_target_idx, .., ..]);
1183 let current_morph_target_na = current_morph_target.to_owned().clone().into_nalgebra();
1184 let (min, max) = Geom::get_bounding_points(¤t_morph_target_na, None);
1185 let (min_vec, max_vec): (Vec<f32>, Vec<f32>) = (min.iter().copied().collect(), max.iter().copied().collect());
1186 let morph_target_accessor = gltf_json::Accessor {
1187 buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1188 byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Deformation as usize])),
1189 count: USize64::from(vertex_count),
1190 component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1191 extensions: Option::default(),
1192 extras: Option::default(),
1193 type_: Valid(gltf_json::accessor::Type::Vec3),
1194 min: Some(gltf_json::Value::from(min_vec)),
1195 max: Some(gltf_json::Value::from(max_vec)),
1196 name: Some(accessor_name),
1197 normalized: false,
1198 sparse: None,
1199 };
1200 morph_target_accessors.push(morph_target_accessor);
1201 running_buffer_view += 1;
1202 }
1203 per_view_running_offset[BufferViewIDs::Deformation as usize] += running_buffer_view as usize;
1204 morph_target_accessors
1205 }
1206 #[allow(clippy::cast_possible_truncation)]
1208 fn create_animation_channels(
1209 &self,
1210 joint_count: usize,
1211 root_idx: u32,
1212 skeleton_root_idx: usize,
1213 sampler_start_idx: usize,
1214 compatibility_mode: GltfCompatibilityMode,
1215 ) -> Vec<gltf_json::animation::Channel> {
1216 let mut animation_channels: Vec<gltf_json::animation::Channel> = vec![];
1217 let mut sampler_idx = sampler_start_idx;
1218 for j_idx in 0..joint_count {
1219 let animation_target = gltf_json::animation::Target {
1220 extensions: Option::default(),
1221 extras: Option::default(),
1222 node: gltf_json::Index::new(u32::try_from(j_idx + skeleton_root_idx).expect("Could not convert to u32!")),
1223 path: gltf_json::validation::Checked::Valid(gltf_json::animation::Property::Rotation),
1224 };
1225 let channel = gltf_json::animation::Channel {
1226 sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
1227 target: animation_target,
1228 extensions: Option::default(),
1229 extras: Option::default(),
1230 };
1231 animation_channels.push(channel);
1232 sampler_idx += 1;
1233 }
1234 let animation_target = gltf_json::animation::Target {
1235 extensions: Option::default(),
1236 extras: Option::default(),
1237 node: gltf_json::Index::new(root_idx),
1238 path: gltf_json::validation::Checked::Valid(gltf_json::animation::Property::Translation),
1239 };
1240 let channel = gltf_json::animation::Channel {
1241 sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
1242 target: animation_target,
1243 extensions: Option::default(),
1244 extras: Option::default(),
1245 };
1246 animation_channels.push(channel);
1247 sampler_idx += 1;
1248 let animation_target = gltf_json::animation::Target {
1249 extensions: Option::default(),
1250 extras: Option::default(),
1251 node: gltf_json::Index::new(root_idx),
1252 path: gltf_json::validation::Checked::Valid(gltf_json::animation::Property::Scale),
1253 };
1254 let channel = gltf_json::animation::Channel {
1255 sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
1256 target: animation_target,
1257 extensions: Option::default(),
1258 extras: Option::default(),
1259 };
1260 animation_channels.push(channel);
1261 sampler_idx += 1;
1262 let mesh_skin_binding_node_idx = root_idx - 1;
1263 if compatibility_mode == GltfCompatibilityMode::Unreal {
1264 let animation_target = gltf_json::animation::Target {
1265 extensions: Option::default(),
1266 extras: Option::default(),
1267 node: gltf_json::Index::new(u32::try_from(skeleton_root_idx).expect("Could not convert to u32!")),
1268 path: gltf_json::validation::Checked::Valid(gltf_json::animation::Property::Translation),
1269 };
1270 let channel = gltf_json::animation::Channel {
1271 sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
1272 target: animation_target,
1273 extensions: Option::default(),
1274 extras: Option::default(),
1275 };
1276 animation_channels.push(channel);
1277 sampler_idx += 1;
1278 }
1279 if self.num_morph_targets() > 0 {
1280 let mtw_animation_target = gltf_json::animation::Target {
1281 extensions: Option::default(),
1282 extras: Option::default(),
1283 node: gltf_json::Index::new(mesh_skin_binding_node_idx),
1284 path: gltf_json::validation::Checked::Valid(gltf_json::animation::Property::MorphTargetWeights),
1285 };
1286 let channel = gltf_json::animation::Channel {
1287 sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
1288 target: mtw_animation_target,
1289 extensions: Option::default(),
1290 extras: Option::default(),
1291 };
1292 animation_channels.push(channel);
1293 }
1294 animation_channels
1295 }
1296 fn create_animation_samplers(
1298 &self,
1299 joint_count: usize,
1300 current_buffer_view_offset: u32,
1301 compatibility_mode: GltfCompatibilityMode,
1302 ) -> Vec<gltf_json::animation::Sampler> {
1303 let mut animation_samplers: Vec<gltf_json::animation::Sampler> = vec![];
1304 let mut current_accessor = 8 + current_buffer_view_offset;
1305 for _ in 0..joint_count {
1306 let sampler = gltf_json::animation::Sampler {
1307 extensions: Option::default(),
1308 extras: Option::default(),
1309 input: gltf_json::Index::new(7 + current_buffer_view_offset),
1310 interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Linear),
1311 output: gltf_json::Index::new(current_accessor),
1312 };
1313 animation_samplers.push(sampler);
1314 current_accessor += 1;
1315 }
1316 let sampler = gltf_json::animation::Sampler {
1317 extensions: Option::default(),
1318 extras: Option::default(),
1319 input: gltf_json::Index::new(7 + current_buffer_view_offset),
1320 interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Linear),
1321 output: gltf_json::Index::new(current_accessor),
1322 };
1323 animation_samplers.push(sampler);
1324 current_accessor += 1;
1325 let sampler = gltf_json::animation::Sampler {
1326 extensions: Option::default(),
1327 extras: Option::default(),
1328 input: gltf_json::Index::new(7 + current_buffer_view_offset),
1329 interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Step),
1330 output: gltf_json::Index::new(current_accessor),
1331 };
1332 animation_samplers.push(sampler);
1333 current_accessor += 1;
1334 if compatibility_mode == GltfCompatibilityMode::Unreal {
1335 let sampler = gltf_json::animation::Sampler {
1336 extensions: Option::default(),
1337 extras: Option::default(),
1338 input: gltf_json::Index::new(7 + current_buffer_view_offset),
1339 interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Linear),
1340 output: gltf_json::Index::new(current_accessor),
1341 };
1342 animation_samplers.push(sampler);
1343 current_accessor += 1;
1344 }
1345 if self.num_morph_targets() > 0 {
1346 let sampler = gltf_json::animation::Sampler {
1347 extensions: Option::default(),
1348 extras: Option::default(),
1349 input: gltf_json::Index::new(7 + current_buffer_view_offset),
1350 interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Linear),
1351 output: gltf_json::Index::new(current_accessor),
1352 };
1353 animation_samplers.push(sampler);
1354 }
1355 animation_samplers
1356 }
1357 pub fn create_morph_targets(&self, pose_dirs_accessors_start_idx: usize) -> Vec<gltf_json::mesh::MorphTarget> {
1359 let mut pose_dirs_morph_targets: Vec<gltf_json::mesh::MorphTarget> = vec![];
1360 let mut running_pose_dirs_accessor = u32::try_from(pose_dirs_accessors_start_idx).expect("Not able to convert to u32");
1361 for _ in 0..self.num_morph_targets() {
1362 let morph_target = gltf_json::mesh::MorphTarget {
1363 positions: Some(gltf_json::Index::new(running_pose_dirs_accessor)),
1364 normals: None,
1365 tangents: None,
1366 };
1367 pose_dirs_morph_targets.push(morph_target);
1368 running_pose_dirs_accessor += 1;
1369 }
1370 pose_dirs_morph_targets
1371 }
1372 pub fn create_bind_matrices(&self, rot_mat: &nd::Array3<f32>, joint_trans: &nd::Array2<f32>, joint_parents: &[u32]) -> nd::Array3<f32> {
1374 assert!(
1375 rot_mat.shape()[0] == joint_trans.shape()[0],
1376 "Number of rotation matrices dont match number of translation matrices!"
1377 );
1378 let num_joints = rot_mat.shape()[0];
1379 let mut bind_matrices = ndarray::Array3::<f32>::zeros((num_joints, 4, 4));
1380 bind_matrices.slice_mut(s![0, 0..3, 0..3]).assign(&rot_mat.slice(s![0, .., ..]));
1381 bind_matrices.slice_mut(s![0, 0..3, 3]).assign(&joint_trans.slice(s![0, ..]));
1382 bind_matrices[[0, 3, 3]] = 1.0;
1383 for j_idx in 1..num_joints {
1384 let parent_index = joint_parents[j_idx] as usize;
1385 let parent_transform = bind_matrices.index_axis(nd::Axis(0), parent_index);
1386 let mut local_transform = ndarray::Array2::<f32>::zeros((4, 4));
1387 local_transform.slice_mut(s![0..3, 0..3]).assign(&rot_mat.slice(s![j_idx, .., ..]));
1388 let local_translation = Array::from_vec(vec_to_vec(&compute_local_translation(j_idx, joint_parents, joint_trans)));
1389 local_transform.slice_mut(s![0..3, 3]).assign(&local_translation);
1390 local_transform[[3, 3]] = 1.0;
1391 let global_transform = parent_transform.dot(&local_transform);
1392 bind_matrices.slice_mut(s![j_idx, .., ..]).assign(&global_transform);
1393 }
1394 bind_matrices
1395 }
1396 pub fn create_animation_data(&self, body_idx: usize, compatibility_mode: GltfCompatibilityMode) -> Vec<u8> {
1398 let mut animation_data: Vec<u8> = vec![];
1399 let keyframe_data = to_padded_byte_vector(self.keyframe_times.as_ref().unwrap());
1400 let rotation_animation_data = self.per_body_data[body_idx].body_rotations.as_ref().unwrap();
1401 let mut translation_animation_data = self.per_body_data[body_idx].body_translations.as_ref().unwrap().clone();
1402 let scale_animation_data = self.per_body_data[body_idx].body_scales.as_ref().unwrap().clone();
1403 animation_data.extend_from_slice(keyframe_data.as_slice());
1404 assert_eq!(rotation_animation_data.shape()[1], translation_animation_data.shape()[0]);
1405 for j_idx in 0..rotation_animation_data.shape()[0] {
1406 let mut quaternions: Vec<f32> = vec![];
1407 for r_idx in 0..rotation_animation_data.shape()[1] {
1408 let rotation = rotation_animation_data.slice(s![j_idx, r_idx, ..]);
1409 let axis_angle_rotation = na::Vector3::new(rotation[0], rotation[1], rotation[2]);
1410 let mut quaternion_rotation =
1411 na::UnitQuaternion::from_axis_angle(&na::UnitVector3::new_normalize(axis_angle_rotation), axis_angle_rotation.norm());
1412 if axis_angle_rotation.norm() == 0.0 {
1413 quaternion_rotation = na::UnitQuaternion::default();
1414 }
1415 quaternions.extend(quaternion_rotation.as_vector().data.as_slice());
1416 }
1417 let joint_anim_data = to_padded_byte_vector(&quaternions);
1418 animation_data.append(&mut joint_anim_data.clone());
1419 }
1420 if compatibility_mode == GltfCompatibilityMode::Unreal {
1421 let mut pelvis_relative_trans = translation_animation_data.clone();
1422 for mut row in translation_animation_data.axis_iter_mut(Axis(0)) {
1423 row[1] = 0.0;
1424 }
1425 for mut row in pelvis_relative_trans.axis_iter_mut(Axis(0)) {
1426 row[0] = 0.0;
1427 row[2] = 0.0;
1428 }
1429 let trans_anim_data = to_padded_byte_vector(&translation_animation_data.to_owned().into_raw_vec_and_offset().0);
1430 animation_data.append(&mut trans_anim_data.clone());
1431 let scale_anim_data = to_padded_byte_vector(&scale_animation_data.to_owned().into_raw_vec_and_offset().0);
1432 animation_data.append(&mut scale_anim_data.clone());
1433 let pelvis_rel_anim_data = to_padded_byte_vector(&pelvis_relative_trans.to_owned().into_raw_vec_and_offset().0);
1434 animation_data.append(&mut pelvis_rel_anim_data.clone());
1435 } else {
1436 let trans_anim_data = to_padded_byte_vector(&translation_animation_data.to_owned().into_raw_vec_and_offset().0);
1437 animation_data.append(&mut trans_anim_data.clone());
1438 let scale_anim_data = to_padded_byte_vector(&scale_animation_data.to_owned().into_raw_vec_and_offset().0);
1439 animation_data.append(&mut scale_anim_data.clone());
1440 }
1441 if self.num_morph_targets() > 0 {
1442 let morph_target_weights_data = self.per_body_data[body_idx].per_frame_blend_weights.as_ref().unwrap();
1443 let weights_anim_data = to_padded_byte_vector(&morph_target_weights_data.to_owned().into_raw_vec_and_offset().0);
1444 animation_data.append(&mut weights_anim_data.clone());
1445 }
1446 animation_data
1447 }
1448 #[allow(clippy::too_many_arguments)]
1450 fn compose_buffer_views(
1451 &self,
1452 body_idx: usize,
1453 buffer_views: Vec<gltf_json::buffer::View>,
1454 index_data: &[u8],
1455 vertex_data: &[u8],
1456 inv_bind_mat_data: &[u8],
1457 textures: &mut [GltfTextureInfo],
1458 compatibility_mode: GltfCompatibilityMode,
1459 ) -> (Vec<u8>, Vec<gltf_json::buffer::View>) {
1460 let mut out_data: Vec<u8> = vec![];
1461 let mut out_buffer_views: Vec<gltf_json::buffer::View> = vec![];
1462 out_data.append(&mut index_data.to_owned());
1463 out_data.append(&mut vertex_data.to_owned());
1464 out_data.append(&mut inv_bind_mat_data.to_owned());
1465 if self.is_animated() {
1466 let mut animation_data = self.create_animation_data(body_idx, compatibility_mode);
1467 out_data.append(&mut animation_data);
1468 if self.num_morph_targets() > 0 && body_idx == 0 {
1469 for morph_target_idx in 0..self.num_morph_targets() {
1470 let posedir = self.morph_targets.as_ref().unwrap().slice(s![morph_target_idx, .., ..]).to_owned();
1471 let posedir_data = to_padded_byte_vector(&posedir.to_owned().into_raw_vec_and_offset().0);
1472 out_data.append(&mut posedir_data.clone());
1473 }
1474 }
1475 }
1476 out_buffer_views.extend(buffer_views);
1477 let mut buffer_offset = out_data.len();
1478 let mut buffer_index: usize = out_buffer_views.len();
1479 for (sampler_index, texture) in textures.iter_mut().enumerate() {
1480 let mut buffer_view = texture.buffer_view.clone();
1481 buffer_view.byte_offset = Some(USize64::from(buffer_offset));
1482 out_buffer_views.push(buffer_view);
1483 texture.buffer_index = buffer_index;
1484 texture.image.buffer_view = Some(gltf_json::Index::new(u32::try_from(buffer_index).expect("Issue converting to u32!")));
1485 texture.texture.sampler = Some(gltf_json::Index::new(u32::try_from(sampler_index).expect("Issue converting to u32!")));
1486 out_data.append(&mut texture.image_data.clone());
1487 buffer_offset += texture.buffer_size;
1488 buffer_index += 1;
1489 }
1490 (out_data, out_buffer_views)
1491 }
1492 fn add_texture(&mut self, img: &DynImage, index: usize, name: &str) -> Option<GltfTextureInfo> {
1494 let mut image_data: Vec<u8> = vec![];
1495 let mut target = Cursor::new(&mut image_data);
1496 let image_data_buffer = img.write_to(&mut target, image::ImageFormat::Png);
1497 if image_data_buffer.is_ok() {
1498 let _ = target.flush();
1499 while image_data.len() % 4 != 0 {
1500 image_data.push(0);
1501 }
1502 let mut image_buffer_size = image_data.len();
1503 align_to_multiple_of_four(&mut image_buffer_size);
1504 let image_buffer_view = gltf_json::buffer::View {
1505 buffer: gltf_json::Index::new(0),
1506 byte_length: USize64::from(image_buffer_size),
1507 byte_offset: Some(USize64::from(0_usize)),
1508 byte_stride: Option::default(),
1509 extensions: Option::default(),
1510 extras: Option::default(),
1511 name: Some(name.to_string()),
1512 target: None,
1513 };
1514 let image = gltf_json::image::Image {
1515 buffer_view: Some(gltf_json::Index::new(u32::try_from(index).expect("Issue converting to u32!"))),
1516 mime_type: Some(gltf_json::image::MimeType("image/png".to_string())),
1517 name: Some(name.to_string()),
1518 uri: None,
1519 extensions: None,
1520 extras: None,
1521 };
1522 let texture = gltf_json::Texture {
1523 name: Some(name.to_string()),
1524 sampler: Some(gltf_json::Index::new(u32::try_from(index).expect("Issue converting to u32!"))),
1525 source: gltf_json::Index::new(u32::try_from(index).expect("Issue converting to u32!")),
1526 extensions: None,
1527 extras: None,
1528 };
1529 let sampler = gltf_json::texture::Sampler {
1530 name: Some(name.to_string()),
1531 mag_filter: Some(gltf_json::validation::Checked::Valid(gltf_json::texture::MagFilter::Linear)),
1532 min_filter: Some(gltf_json::validation::Checked::Valid(gltf_json::texture::MinFilter::Linear)),
1533 wrap_s: gltf_json::validation::Checked::Valid(gltf_json::texture::WrappingMode::ClampToEdge),
1534 wrap_t: gltf_json::validation::Checked::Valid(gltf_json::texture::WrappingMode::ClampToEdge),
1535 extensions: None,
1536 extras: None,
1537 };
1538 let texture_info = GltfTextureInfo {
1539 buffer_size: image_data.len(),
1540 image_data,
1541 image,
1542 buffer_view: image_buffer_view,
1543 buffer_index: 0,
1544 texture,
1545 sampler,
1546 };
1547 return Some(texture_info);
1548 }
1549 log!("add_texture FAILED: {}", name);
1550 None
1551 }
1552 #[allow(clippy::cast_sign_loss)]
1554 fn prepare_normals(&mut self, smpl_textures: &mut SmplTextures, texture_infos: &mut Vec<GltfTextureInfo>, normals_tex: Option<&DynImage>) {
1555 if let Some(img) = normals_tex {
1556 let normals_tex = self.add_texture(img, texture_infos.len(), "normals");
1557 if let Some(normals_tex) = normals_tex {
1558 smpl_textures.normals_index = Some(texture_infos.len());
1559 texture_infos.push(normals_tex);
1560 }
1561 }
1562 }
1563 fn prepare_metallic_roughness(
1565 &mut self,
1566 smpl_textures: &mut SmplTextures,
1567 texture_infos: &mut Vec<GltfTextureInfo>,
1568 metalness_tex: Option<&DynImage>,
1569 roughness_tex: Option<&DynImage>,
1570 ) {
1571 let mut w: u32 = 0;
1572 let mut h: u32 = 0;
1573 if let Some(img) = metalness_tex {
1574 w = img.width();
1575 h = img.height();
1576 }
1577 if let Some(img) = roughness_tex {
1578 w = w.max(img.width());
1579 h = h.max(img.height());
1580 }
1581 let mut metalness: Option<Vec<u8>> = None;
1582 if let Some(img) = metalness_tex {
1583 if img.width() != w || img.height() != h {
1584 let resized_img = img.resize(w, h, FilterType::Gaussian);
1585 metalness = Some(resized_img.as_luma8().unwrap().to_vec());
1586 } else {
1587 metalness = Some(img.as_bytes().to_vec());
1588 }
1589 }
1590 let mut roughness: Option<Vec<u8>> = None;
1591 if let Some(img) = roughness_tex {
1592 if img.width() != w || img.height() != h {
1593 let resized_img = img.resize(w, h, FilterType::Gaussian);
1594 roughness = Some(resized_img.as_luma8().unwrap().to_vec());
1595 } else {
1596 roughness = Some(img.as_bytes().to_vec());
1597 }
1598 }
1599 let num_pixels: usize = (w * h) as usize;
1600 let mut metal_roughness_pixels: Vec<u8> = vec![];
1601 if let Some(metalness_pixels) = metalness {
1602 if let Some(roughness_pixels) = roughness {
1603 for (m, r) in metalness_pixels.iter().zip(roughness_pixels.iter()).take(num_pixels) {
1604 metal_roughness_pixels.push(0);
1605 metal_roughness_pixels.push(*r);
1606 metal_roughness_pixels.push(*m);
1607 }
1608 } else {
1609 for &m in metalness_pixels.iter().take(num_pixels) {
1610 metal_roughness_pixels.push(0);
1611 metal_roughness_pixels.push(0);
1612 metal_roughness_pixels.push(m);
1613 }
1614 }
1615 } else if let Some(roughness_pixels) = roughness {
1616 for &r in roughness_pixels.iter().take(num_pixels) {
1617 metal_roughness_pixels.push(0);
1618 metal_roughness_pixels.push(r);
1619 metal_roughness_pixels.push(0);
1620 }
1621 }
1622 if !metal_roughness_pixels.is_empty() {
1623 let metal_roughness_image = RgbImage::from_vec(w, h, metal_roughness_pixels);
1624 if let Some(image) = metal_roughness_image {
1625 let image = DynImage::from(image);
1626 let metallic_roughness = self.add_texture(&image, texture_infos.len(), "metal_roughness");
1627 if let Some(metallic_roughness) = metallic_roughness {
1628 smpl_textures.metalic_roughtness_index = Some(texture_infos.len());
1629 texture_infos.push(metallic_roughness);
1630 }
1631 }
1632 }
1633 }
1634 fn create_joint(&self, name: String, translation: &[f32], rotation: &Vector3f, children: Option<Vec<gltf_json::Index<Node>>>) -> Node {
1636 let cur_vec = na::Vector3::new(rotation.x, rotation.y, rotation.z);
1637 let mut cur_q = na::UnitQuaternion::from_axis_angle(&na::UnitVector3::new_normalize(cur_vec), rotation.norm());
1638 if rotation.norm() == 0.0 {
1639 cur_q = na::UnitQuaternion::default();
1640 }
1641 let translation: [f32; 3] = [translation[0], translation[1], translation[2]];
1642 let unit_quaternion = [cur_q[0], cur_q[1], cur_q[2], cur_q[3]];
1643 Node {
1644 children,
1645 mesh: None,
1646 skin: None,
1647 name: Some(name),
1648 rotation: Some(UnitQuaternion(unit_quaternion)),
1649 translation: Some(translation),
1650 ..Default::default()
1651 }
1652 }
1653 fn gather_children(&self, id: u32, parent_ids: &[u32], offset: u32) -> Option<Vec<gltf_json::Index<Node>>> {
1654 let mut children: Vec<gltf_json::Index<Node>> = vec![];
1655 for (p, &parent_id) in parent_ids.iter().enumerate() {
1656 if parent_id == id {
1657 let index = u32::try_from(p).expect("Index conversion error: usize value is too large to fit in a u32");
1658 children.push(gltf_json::Index::<Node>::new(index + offset));
1659 }
1660 }
1661 if !children.is_empty() {
1662 return Some(children);
1663 }
1664 None
1665 }
1666 #[allow(clippy::too_many_arguments)]
1668 fn add_skin(
1669 &mut self,
1670 name: String,
1671 body_idx: usize,
1672 current_armature_idx: u32,
1673 accessor_offset: u32,
1674 nodes: &mut Vec<Node>,
1675 skins: &mut Vec<gltf_json::Skin>,
1676 joints: &mut Vec<gltf_json::Index<Node>>,
1677 compatibility_mode: GltfCompatibilityMode,
1678 ) -> gltf_json::Index<Node> {
1679 let metadata = crate::common::metadata::smpl_metadata(&self.smpl_type);
1680 let joint_translations = self.per_body_data[body_idx].default_joint_translations.as_ref().unwrap();
1681 let skeleton_root_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
1682 let global_translation = vec_from_array0_f(self.per_body_data[body_idx].body_translation.as_ref().unwrap());
1683 let mut skeleton_root_translation = compute_local_translation(0, &metadata.joint_parents, joint_translations);
1684 if compatibility_mode == GltfCompatibilityMode::Smpl {
1685 skeleton_root_translation = addv3f(&skeleton_root_translation, &global_translation);
1686 }
1687 let mut joint_rotation = Vector3f::zeros();
1688 if let Some(pose) = self.per_body_data[body_idx].pose.as_ref() {
1689 let rot = pose.joint_poses.row(0);
1690 joint_rotation = Vector3f::new(rot[0], rot[1], rot[2]);
1691 }
1692 let skeleton_root = self.create_joint(
1693 "pelvis".to_string(),
1694 vec_to_vec(&skeleton_root_translation).as_slice(),
1695 &joint_rotation,
1696 self.gather_children(0, &metadata.joint_parents, skeleton_root_index),
1697 );
1698 nodes.push(skeleton_root);
1699 joints.push(gltf_json::Index::new(skeleton_root_index));
1700 let joint_names = if compatibility_mode == GltfCompatibilityMode::Unreal {
1701 smpl_x::JOINT_NAMES_UNREAL.map(std::string::ToString::to_string).to_vec()
1702 } else {
1703 metadata.joint_names
1704 };
1705 for (j, name) in joint_names.iter().enumerate().take(metadata.num_joints + 1).skip(1) {
1706 if let Some(pose) = self.per_body_data[body_idx].pose.as_ref() {
1707 let rot = pose.joint_poses.row(j);
1708 joint_rotation = Vector3f::new(rot[0], rot[1], rot[2]);
1709 }
1710 let joint = self.create_joint(
1711 name.clone(),
1712 vec_to_vec(&compute_local_translation(j, &metadata.joint_parents, joint_translations)).as_slice(),
1713 &joint_rotation,
1714 self.gather_children(
1715 u32::try_from(j).expect("Issue converting Joint idx to u32"),
1716 &metadata.joint_parents,
1717 skeleton_root_index,
1718 ),
1719 );
1720 let joint_index = gltf_json::Index::<Node>::new(u32::try_from(nodes.len()).expect("Issue converting Joint idx to u32"));
1721 nodes.push(joint);
1722 joints.push(joint_index);
1723 }
1724 let skin = gltf_json::Skin {
1725 name: Some(name),
1726 inverse_bind_matrices: Some(gltf_json::Index::new(accessor_offset + 6)),
1727 joints: joints.clone(),
1728 skeleton: Some(gltf_json::Index::new(current_armature_idx)),
1729 extensions: None,
1730 extras: None,
1731 };
1732 skins.push(skin);
1733 gltf_json::Index::<Node>::new(skeleton_root_index)
1734 }
1735 #[allow(clippy::cast_precision_loss)]
1737 fn create_camera_animation_buffer_views(&self, running_offset: &mut usize) -> Option<(Vec<gltf_json::buffer::View>, Vec<u8>)> {
1738 let camera_track = self.camera_track.as_ref()?;
1739 let mut buffer_views = Vec::new();
1740 let mut buffer_data = Vec::new();
1741 if let Some(translations) = camera_track.per_frame_translations.as_ref() {
1742 let trans_data = to_padded_byte_vector(translations.as_slice().unwrap());
1743 let trans_len = trans_data.len();
1744 let trans_view = gltf_json::buffer::View {
1745 buffer: gltf_json::Index::new(0),
1746 byte_length: USize64::from(trans_len),
1747 byte_offset: Some(USize64::from(*running_offset)),
1748 byte_stride: None,
1749 extensions: None,
1750 extras: Option::default(),
1751 name: Some("camera_translations".to_string()),
1752 target: None,
1753 };
1754 buffer_data.extend(trans_data);
1755 *running_offset += trans_len;
1756 buffer_views.push(trans_view);
1757 }
1758 if let Some(rotations) = camera_track.per_frame_rotations.as_ref() {
1759 let rot_data = to_padded_byte_vector(rotations.as_slice().unwrap());
1760 let rot_len = rot_data.len();
1761 let rot_view = gltf_json::buffer::View {
1762 buffer: gltf_json::Index::new(0),
1763 byte_length: USize64::from(rot_len),
1764 byte_offset: Some(USize64::from(*running_offset)),
1765 byte_stride: None,
1766 extensions: None,
1767 extras: Option::default(),
1768 name: Some("camera_rotations".to_string()),
1769 target: None,
1770 };
1771 buffer_data.extend(rot_data);
1772 *running_offset += rot_len;
1773 buffer_views.push(rot_view);
1774 }
1775 Some((buffer_views, buffer_data))
1776 }
1777 fn create_camera_animation_accessors(&self, current_buffer_view_offset: u32) -> Option<Vec<gltf_json::Accessor>> {
1779 let camera_track = self.camera_track.as_ref()?;
1780 let mut accessors = Vec::new();
1781 let mut current_view = current_buffer_view_offset;
1782 if let Some(translations) = camera_track.per_frame_translations.as_ref() {
1783 accessors.push(gltf_json::Accessor {
1784 buffer_view: Some(gltf_json::Index::new(current_view)),
1785 byte_offset: None,
1786 component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1787 count: USize64::from(translations.shape()[0]),
1788 type_: Valid(gltf_json::accessor::Type::Vec3),
1789 min: None,
1790 max: None,
1791 name: Some("camera_translations".to_string()),
1792 normalized: false,
1793 sparse: None,
1794 extensions: None,
1795 extras: Option::default(),
1796 });
1797 current_view += 1;
1798 }
1799 if let Some(rotations) = camera_track.per_frame_rotations.as_ref() {
1800 accessors.push(gltf_json::Accessor {
1801 buffer_view: Some(gltf_json::Index::new(current_view)),
1802 byte_offset: None,
1803 component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1804 count: USize64::from(rotations.shape()[0]),
1805 type_: Valid(gltf_json::accessor::Type::Vec4),
1806 min: None,
1807 max: None,
1808 name: Some("camera_rotations".to_string()),
1809 normalized: false,
1810 sparse: None,
1811 extensions: None,
1812 extras: Option::default(),
1813 });
1814 }
1815 Some(accessors)
1816 }
1817 #[allow(clippy::cast_possible_truncation)]
1819 fn create_camera_animation_channels_and_samplers(
1820 &self,
1821 current_accessor_offset: u32,
1822 camera_node_index: u32,
1823 sampler_start_idx: u32,
1824 ) -> Option<(Vec<gltf_json::animation::Channel>, Vec<gltf_json::animation::Sampler>)> {
1825 let camera_track = self.camera_track.as_ref()?;
1826 let mut channels = Vec::new();
1827 let mut samplers = Vec::new();
1828 let mut current_accessor = current_accessor_offset;
1829 let times_accessor_index = 7;
1830 if camera_track.per_frame_translations.is_some() {
1831 samplers.push(gltf_json::animation::Sampler {
1832 input: gltf_json::Index::new(times_accessor_index),
1833 interpolation: Valid(gltf_json::animation::Interpolation::Linear),
1834 output: gltf_json::Index::new(current_accessor),
1835 extensions: None,
1836 extras: Option::default(),
1837 });
1838 channels.push(gltf_json::animation::Channel {
1839 sampler: gltf_json::Index::new(sampler_start_idx + samplers.len() as u32 - 1),
1840 target: gltf_json::animation::Target {
1841 node: gltf_json::Index::new(camera_node_index),
1842 path: Valid(gltf_json::animation::Property::Translation),
1843 extensions: None,
1844 extras: Option::default(),
1845 },
1846 extensions: None,
1847 extras: Option::default(),
1848 });
1849 current_accessor += 1;
1850 }
1851 if camera_track.per_frame_rotations.is_some() {
1852 samplers.push(gltf_json::animation::Sampler {
1853 input: gltf_json::Index::new(times_accessor_index),
1854 interpolation: Valid(gltf_json::animation::Interpolation::Linear),
1855 output: gltf_json::Index::new(current_accessor),
1856 extensions: None,
1857 extras: Option::default(),
1858 });
1859 channels.push(gltf_json::animation::Channel {
1860 sampler: gltf_json::Index::new(sampler_start_idx + samplers.len() as u32 - 1),
1861 target: gltf_json::animation::Target {
1862 node: gltf_json::Index::new(camera_node_index),
1863 path: Valid(gltf_json::animation::Property::Rotation),
1864 extensions: None,
1865 extras: Option::default(),
1866 },
1867 extensions: None,
1868 extras: Option::default(),
1869 });
1870 }
1871 Some((channels, samplers))
1872 }
1873 fn num_morph_targets(&self) -> usize {
1874 self.morph_targets.as_ref().map_or(0, |x| x.shape()[0])
1875 }
1876}
1877pub fn compute_local_translation(id: usize, parent_ids: &[u32], joint_translations: &nd::Array2<f32>) -> Vector3f {
1878 let trans = vec_from_vec(&joint_translations.row(id).to_vec());
1879 if id == 0 {
1880 return trans;
1881 }
1882 let parent_id = parent_ids[id] as usize;
1883 let parent_trans = vec_from_vec(&joint_translations.row(parent_id).to_vec());
1884 subv3f(&trans, &parent_trans)
1885}