smpl_core/codec/
gltf.rs

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