smpl_core/codec/
gltf.rs

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