Skip to main content

smpl_core/codec/
gltf.rs

1use crate::{
2    codec::scene::SmplCamera,
3    common::types::{ChunkHeader, FaceType, GltfCompatibilityMode, GltfOutputType, SmplType},
4    smpl_x::smpl_x,
5};
6use gloss_geometry::geom;
7use gloss_img::dynamic_image::DynImage;
8use gloss_utils::nshare::ToNalgebra;
9use gltf::binary::Header;
10use gltf_json::validation::{Checked::Valid, USize64};
11use gltf_json::{material::AlphaMode, scene::UnitQuaternion, Node};
12use image::imageops::FilterType;
13use image::RgbImage;
14use itertools::izip;
15use log::info;
16use nalgebra as na;
17use nalgebra::DMatrix;
18use ndarray as nd;
19use ndarray::prelude::*;
20use smpl_utils::numerical::batch_rodrigues;
21use smpl_utils::{
22    log,
23    vector::{
24        addv3f, align_to_multiple_of_four, subv3f, to_padded_byte_vector, vec_from_array0_f, vec_from_vec, vec_to_vec, Vector2f, Vector3f, Vector4f,
25        Vector4s,
26    },
27};
28use std::borrow::Cow;
29use std::{
30    f32::consts::PI,
31    io::{Cursor, Write},
32    path::Path,
33};
34use std::{fs, mem};
35/// Enum for attribute ID's of a GLTF primitive (to avoid working with arbitrary
36/// numbers)
37#[repr(u32)]
38#[derive(Debug, Clone, Copy)]
39enum PrimitiveAttrIDs {
40    Indices = 0,
41    Positions = 1,
42    Normals = 2,
43    TexCoords = 3,
44    Joints = 4,
45    Weights = 5,
46}
47/// Enum for buffer view ID's (to avoid working with arbitrary numbers)
48#[repr(u32)]
49#[derive(Debug, Clone, Copy)]
50enum BufferViewIDs {
51    Index = 0,
52    VertexAttr = 1,
53    InvBindMat = 2,
54    Keyframe = 3,
55    Animation = 4,
56    Deformation = 5,
57}
58/// Vertex definition for position and other Vertex attributes
59#[derive(Copy, Clone, Debug, bytemuck::NoUninit)]
60#[repr(C)]
61struct Vertex {
62    position: [f32; 3],
63    normal: [f32; 3],
64    uv: [f32; 2],
65    joint_index: [u16; 4],
66    joint_weight: [f32; 4],
67}
68/// Vertex definition for props( which don't have skinning)
69#[derive(Copy, Clone, Debug, bytemuck::NoUninit)]
70#[repr(C)]
71struct VertexProp {
72    position: [f32; 3],
73    joint_index: [u16; 4],
74    joint_weight: [f32; 4],
75}
76/// Vertex definition for props( which don't have skinning)
77#[derive(Copy, Clone, Debug, bytemuck::NoUninit)]
78#[repr(C)]
79struct VertexPropWithUV {
80    position: [f32; 3],
81    uv: [f32; 2],
82    joint_index: [u16; 4],
83    joint_weight: [f32; 4],
84}
85/// Struct for holding GLTF texture information
86#[derive(Clone, Debug)]
87struct GltfTextureInfo {
88    buffer_size: usize,
89    image_data: Vec<u8>,
90    image: gltf_json::image::Image,
91    buffer_view: gltf_json::buffer::View,
92    buffer_index: usize,
93    texture: gltf_json::texture::Texture,
94    sampler: gltf_json::texture::Sampler,
95}
96/// Texture indices
97#[allow(clippy::struct_field_names)]
98struct SmplTextures {
99    diffuse_index: Option<usize>,
100    normals_index: Option<usize>,
101    metalic_roughtness_index: Option<usize>,
102}
103/// `PropData` contains data for an individual object in the scene
104#[derive(Clone, Debug)]
105pub struct PropData {
106    pub diffuse_texture: Option<DynImage>,
107    pub normals_texture: Option<DynImage>,
108    pub metalness_texture: Option<DynImage>,
109    pub roughness_texture: Option<DynImage>,
110    pub positions: DMatrix<f32>,
111    pub faces: DMatrix<u32>,
112    pub normals: Option<DMatrix<f32>>,
113    pub uvs: Option<DMatrix<f32>>,
114    pub translations: nd::Array2<f32>,
115    pub rotations: nd::Array3<f32>,
116    pub scales: nd::Array2<f32>,
117    pub default_translation: nd::Array2<f32>,
118    pub default_joint_poses: nd::Array2<f32>,
119}
120/// `PerBodyData` contains data for individual bodies
121#[derive(Clone, Default, Debug)]
122pub struct PerBodyData {
123    pub diffuse_textures: Option<DynImage>,
124    pub normals_textures: Option<DynImage>,
125    pub metalness_textures: Option<DynImage>,
126    pub roughness_textures: Option<DynImage>,
127    pub positions: Option<DMatrix<f32>>,
128    pub normals: Option<DMatrix<f32>>,
129    pub default_joint_translations: Option<nd::Array2<f32>>,
130    pub body_translation: Option<nd::Array2<f32>>,
131    pub joint_poses: Option<nd::Array2<f32>>,
132    pub body_translations: Option<nd::Array2<f32>>,
133    pub body_rotations: Option<nd::Array3<f32>>,
134    pub body_scales: Option<nd::Array2<f32>>,
135    pub per_frame_blend_weights: Option<nd::Array2<f32>>,
136}
137pub struct GltfExportOptions {
138    pub out_type: GltfOutputType,
139    pub compatibility_mode: GltfCompatibilityMode,
140    pub face_type: FaceType,
141}
142impl Default for GltfExportOptions {
143    fn default() -> Self {
144        Self {
145            out_type: GltfOutputType::Binary,
146            compatibility_mode: GltfCompatibilityMode::Smpl,
147            face_type: FaceType::SmplX,
148        }
149    }
150}
151/// The ``GltfCodec`` contains all the contents of the exported GLTF
152#[derive(Clone)]
153pub struct GltfCodec {
154    pub num_bodies: usize,
155    pub smpl_type: SmplType,
156    pub gender: i32,
157    pub faces: Option<DMatrix<u32>>,
158    pub uvs: Option<DMatrix<f32>>,
159    pub joint_index: Option<DMatrix<u32>>,
160    pub joint_weight: Option<DMatrix<f32>>,
161    pub default_joint_poses: Option<nd::Array2<f32>>,
162    pub frame_count: Option<usize>,
163    pub keyframe_times: Option<Vec<f32>>,
164    pub morph_targets: Option<nd::Array3<f32>>,
165    pub num_pose_morph_targets: usize,
166    pub num_expression_morph_targets: usize,
167    pub per_body_data: Vec<PerBodyData>,
168    pub smpl_camera: Option<SmplCamera>,
169    pub props: Vec<PropData>,
170}
171impl Default for GltfCodec {
172    fn default() -> Self {
173        Self {
174            num_bodies: 1,
175            smpl_type: SmplType::SmplX,
176            gender: 0,
177            faces: None,
178            uvs: None,
179            joint_index: None,
180            joint_weight: None,
181            default_joint_poses: None,
182            frame_count: None,
183            keyframe_times: None,
184            morph_targets: None,
185            num_pose_morph_targets: 0,
186            num_expression_morph_targets: 0,
187            per_body_data: Vec::new(),
188            smpl_camera: None,
189            props: Vec::new(),
190        }
191    }
192}
193impl GltfCodec {
194    /// Export ``GltfCodec`` to a file (as a ``.gltf`` or ``.glb``)
195    pub fn to_file(&mut self, name: &str, path: &str, options: &GltfExportOptions) {
196        let parent_path = Path::new(path).parent();
197        let file_name = Path::new(path).file_name();
198        let Some(parent_path) = parent_path else {
199            log!("Error: Exporting GLTF - no directory name found: {}", path);
200            return;
201        };
202        let Some(file_name) = file_name else {
203            log!("Error: Exporting GLTF - no file name found: {}", path);
204            return;
205        };
206        let _ = fs::create_dir(parent_path);
207        let target_extension: &str = match options.out_type {
208            GltfOutputType::Standard => "gltf",
209            GltfOutputType::Binary => "glb",
210        };
211        let file_name_with_suffix = Path::new(file_name).with_extension(target_extension);
212        log!("Exporting GLTF: {}/{}", path, file_name_with_suffix.to_string_lossy());
213        let binary = matches!(options.out_type, GltfOutputType::Binary);
214        let (buffer_data, root) = self.create_buffer(name, binary, options.compatibility_mode, options.face_type);
215        match options.out_type {
216            GltfOutputType::Standard => {
217                let json_path = parent_path.join(file_name_with_suffix.clone());
218                let bin_path = parent_path.join("buffer0.bin");
219                let writer = fs::File::create(json_path).expect("I/O error");
220                gltf_json::serialize::to_writer_pretty(writer, &root).expect("Serialization error");
221                let bin = to_padded_byte_vector(&buffer_data);
222                let mut writer = fs::File::create(bin_path).expect("I/O error");
223                writer.write_all(&bin).expect("I/O error");
224                info!("Written glTF json + bin to {}", parent_path.display());
225            }
226            GltfOutputType::Binary => {
227                let json_string = gltf_json::serialize::to_string(&root).expect("Serialization error");
228                let mut length = mem::size_of::<Header>() + mem::size_of::<ChunkHeader>() + json_string.len();
229                align_to_multiple_of_four(&mut length);
230                length += mem::size_of::<ChunkHeader>() + buffer_data.len();
231                align_to_multiple_of_four(&mut length);
232                let glb = gltf::binary::Glb {
233                    header: gltf::binary::Header {
234                        magic: *b"glTF",
235                        version: 2,
236                        length: length.try_into().expect("file size exceeds binary glTF limit"),
237                    },
238                    bin: Some(Cow::Owned(buffer_data)),
239                    json: Cow::Owned(json_string.into_bytes()),
240                };
241                let glb_path = parent_path.join(file_name_with_suffix.clone());
242                let writer = std::fs::File::create(glb_path.clone()).expect("I/O error");
243                glb.to_writer(writer).expect("glTF binary output error");
244                info!("Written binary glB to {}", glb_path.display());
245            }
246        }
247    }
248    /// Get the ``GltfCodec`` as a u8 buffer
249    pub fn to_buf(&mut self, compatibility_mode: GltfCompatibilityMode, face_type: FaceType) -> Vec<u8> {
250        let (buffer_data, root) = self.create_buffer("Meshcapade Avatar", true, compatibility_mode, face_type);
251        let json_string = gltf_json::serialize::to_string(&root).expect("Serialization error");
252        let mut length = mem::size_of::<Header>() + mem::size_of::<ChunkHeader>() + json_string.len();
253        align_to_multiple_of_four(&mut length);
254        length += mem::size_of::<ChunkHeader>() + buffer_data.len();
255        align_to_multiple_of_four(&mut length);
256        let glb = gltf::binary::Glb {
257            header: gltf::binary::Header {
258                magic: *b"glTF",
259                version: 2,
260                length: length.try_into().expect("file size exceeds binary glTF limit"),
261            },
262            bin: Some(Cow::Owned(buffer_data)),
263            json: Cow::Owned(json_string.into_bytes()),
264        };
265        glb.to_vec().expect("glTF binary output error")
266    }
267    fn is_animated(&self) -> bool {
268        self.frame_count.is_some()
269    }
270    /// Creates the buffer data for the GLTF
271    #[allow(clippy::too_many_lines)]
272    #[allow(clippy::cast_possible_truncation)]
273    #[allow(clippy::cast_possible_wrap)]
274    fn create_buffer(
275        &mut self,
276        name: &str,
277        binary: bool,
278        compatibility_mode: GltfCompatibilityMode,
279        face_type: FaceType,
280    ) -> (Vec<u8>, gltf_json::Root) {
281        let mut full_buffer_data = vec![];
282        let mut accessors = vec![];
283        let mut buffers = vec![];
284        let mut buffer_views = vec![];
285        let mut meshes = vec![];
286        let mut nodes = vec![];
287        let mut skins = vec![];
288        let mut materials = vec![];
289        let mut channels = vec![];
290        let mut samplers = vec![];
291        let mut images = vec![];
292        let mut textures = vec![];
293        let mut texture_samplers: Vec<gltf_json::texture::Sampler> = vec![];
294        let mut cameras: Vec<gltf_json::camera::Camera> = vec![];
295        let mut global_buffer_offset = 0;
296        let mut global_buffer_index = 0;
297        let scene_root_node_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
298        let scene_root_node = Node {
299            name: Some("SceneRoot".to_string()),
300            children: Some(vec![]),
301            ..Default::default()
302        };
303        nodes.push(scene_root_node);
304        if let Some(smpl_camera) = &self.smpl_camera {
305            let camera = gltf_json::camera::Camera {
306                name: Some("MoCapadeCamera".to_string()),
307                type_: gltf_json::validation::Checked::Valid(gltf_json::camera::Type::Perspective),
308                perspective: Some(gltf_json::camera::Perspective {
309                    yfov: smpl_camera.projection.fovy,
310                    znear: smpl_camera.projection.near,
311                    zfar: Some(smpl_camera.projection.far),
312                    aspect_ratio: Some(smpl_camera.projection.aspect_ratio),
313                    extensions: None,
314                    extras: Option::default(),
315                }),
316                orthographic: None,
317                extensions: None,
318                extras: Option::default(),
319            };
320            cameras.push(camera);
321            let smpl_camera_node = Node {
322                name: Some("AnimatedCamera".to_string()),
323                camera: Some(gltf_json::Index::new(0)),
324                ..Default::default()
325            };
326            let camera_node_idx = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
327            if let Some(ref mut scene_root_node_children) = nodes[0].children {
328                scene_root_node_children.push(gltf_json::Index::new(camera_node_idx));
329            }
330            nodes.push(smpl_camera_node);
331        }
332        let node_indices: Vec<gltf_json::Index<Node>> = vec![gltf_json::Index::new(scene_root_node_index)];
333        let scene: gltf_json::Scene = gltf_json::Scene {
334            extensions: Option::default(),
335            extras: Option::default(),
336            name: Some(name.to_string()),
337            nodes: node_indices,
338        };
339        let scenes = vec![scene];
340        for body_idx in 0..self.per_body_data.len() {
341            assert!(self.faces.is_some(), "GltfCodec: no faces!");
342            assert!(self.uvs.is_some(), "GltfCodec: no uvs!");
343            let mut current_body = self.per_body_data[body_idx].clone();
344            if compatibility_mode == GltfCompatibilityMode::Smpl && self.num_pose_morph_targets > 0 {
345                let num_pose_morph_targets = self.num_pose_morph_targets.saturating_sub(1);
346                current_body
347                    .per_frame_blend_weights
348                    .as_mut()
349                    .unwrap()
350                    .slice_mut(s![.., 0..num_pose_morph_targets])
351                    .mapv_inplace(|elem| (elem + PI) / (2.0 * PI));
352                current_body
353                    .per_frame_blend_weights
354                    .as_mut()
355                    .unwrap()
356                    .slice_mut(s![.., num_pose_morph_targets])
357                    .assign(&nd::Array1::<f32>::from_elem(self.frame_count.unwrap(), 1.0));
358            }
359            if face_type == FaceType::SmplX && self.num_expression_morph_targets > 0 {
360                let num_expression_morph_targets = self.num_expression_morph_targets.saturating_sub(1);
361                current_body
362                    .per_frame_blend_weights
363                    .as_mut()
364                    .unwrap()
365                    .slice_mut(s![
366                        ..,
367                        self.num_pose_morph_targets..self.num_pose_morph_targets + num_expression_morph_targets
368                    ])
369                    .mapv_inplace(|elem| (elem + 7.0) / (2.0 * 7.0));
370                current_body
371                    .per_frame_blend_weights
372                    .as_mut()
373                    .unwrap()
374                    .slice_mut(s![.., self.num_pose_morph_targets + num_expression_morph_targets])
375                    .assign(&nd::Array1::<f32>::from_elem(self.frame_count.unwrap(), 1.0));
376            }
377
378            assert!(current_body.positions.is_some(), "GltfCodec: no vertices for body {body_idx}!");
379            assert!(current_body.normals.is_some(), "GltfCodec: no normals for body {body_idx}!");
380            let mut positions = current_body.positions.clone().unwrap();
381            let normals = current_body.normals.clone().unwrap();
382            let faces = self.faces.as_ref().unwrap();
383            let uvs = self.uvs.as_ref().unwrap();
384            let joint_index = self.joint_index.as_ref().unwrap();
385            let joint_weight = self.joint_weight.as_ref().unwrap();
386            let diffuse_tex = current_body.diffuse_textures.as_ref();
387            let normals_tex = current_body.normals_textures.as_ref();
388            let metalness_tex = current_body.metalness_textures.as_ref();
389            let roughness_tex = current_body.roughness_textures.as_ref();
390            let mut vertex_attributes_array: Vec<Vertex> = vec![];
391            let mut indices_array: Vec<u32> = vec![];
392            let mut inverse_bind_matrices: Vec<f32> = vec![];
393            let face_count = faces.shape().0;
394            let vertex_count = positions.shape().0;
395            let joint_count = current_body.default_joint_translations.as_ref().unwrap().shape()[0];
396            for row in faces.row_iter() {
397                indices_array.extend_from_slice(&[row[0], row[1], row[2]]);
398            }
399            let joint_rotations = batch_rodrigues(self.default_joint_poses.as_ref().unwrap());
400            let mut joint_translations = current_body.default_joint_translations.clone().unwrap();
401            if compatibility_mode == GltfCompatibilityMode::Unreal {
402                let (min, _) = geom::get_bounding_points(&positions, None);
403                let min_vec: Vec<f32> = min.iter().copied().collect();
404                let min_y = min_vec[1];
405                let offset = na::RowVector3::new(0.0, min_y, 0.0);
406                for i in 0..positions.nrows() {
407                    let mut row = positions.row_mut(i);
408                    row -= offset;
409                }
410                let offset_nd = ndarray::Array1::from_vec(vec![0.0, min_y, 0.0]);
411                for mut row in joint_translations.axis_iter_mut(Axis(0)) {
412                    row -= &offset_nd;
413                }
414                current_body.positions = Some(positions.clone());
415                current_body.default_joint_translations = Some(joint_translations.clone());
416            }
417            let metadata = crate::common::metadata::smpl_metadata(&self.smpl_type);
418            let bind_matrices = self.create_bind_matrices(&joint_rotations, &joint_translations, &metadata.joint_parents);
419            let unreal_mapping: [usize; 10] = [0, 0, 0, 8, 7, 0, 21, 21, 20, 0];
420            if compatibility_mode == GltfCompatibilityMode::Unreal {
421                for j_idx in unreal_mapping {
422                    let mut inverse_bind_matrix = nd::Array2::<f32>::zeros((4, 4));
423                    let inverse_rotation_matrix = bind_matrices.slice(s![j_idx, 0..3, 0..3]).reversed_axes();
424                    let translation: nd::Array1<f32> = if j_idx == 0 {
425                        nd::Array1::from_vec(vec![0.0, 0.0, 0.0])
426                    } else {
427                        bind_matrices.slice(s![j_idx, 0..3, 3]).to_owned()
428                    };
429                    let inverse_translation = -inverse_rotation_matrix.dot(&translation);
430                    inverse_bind_matrix.slice_mut(s![0..3, 0..3]).assign(&inverse_rotation_matrix);
431                    inverse_bind_matrix.slice_mut(s![0..3, 3]).assign(&inverse_translation);
432                    inverse_bind_matrix[(3, 3)] = 1.0;
433                    inverse_bind_matrices.extend(inverse_bind_matrix.t().iter());
434                }
435            }
436            for j_idx in 0..joint_count {
437                let mut inverse_bind_matrix = nd::Array2::<f32>::zeros((4, 4));
438                let inverse_rotation_matrix = bind_matrices.slice(s![j_idx, 0..3, 0..3]).reversed_axes();
439                let translation: nd::Array1<f32> = bind_matrices.slice(s![j_idx, 0..3, 3]).to_owned();
440                let inverse_translation = -inverse_rotation_matrix.dot(&translation);
441                inverse_bind_matrix.slice_mut(s![0..3, 0..3]).assign(&inverse_rotation_matrix);
442                inverse_bind_matrix.slice_mut(s![0..3, 3]).assign(&inverse_translation);
443                inverse_bind_matrix[(3, 3)] = 1.0;
444                inverse_bind_matrices.extend(inverse_bind_matrix.t().iter());
445            }
446            let num_extra_joints: usize = if compatibility_mode == GltfCompatibilityMode::Unreal { 10 } else { 0 };
447            for (position, normal, uv, joint_index, joint_weight) in izip!(
448                positions.row_iter(),
449                normals.row_iter(),
450                uvs.row_iter(),
451                joint_index.row_iter(),
452                joint_weight.row_iter(),
453            ) {
454                let jw_sum = joint_weight.iter().sum::<f32>();
455                let indices = [
456                    if joint_weight[0] > 0.0 {
457                        joint_index[0] + u32::try_from(num_extra_joints).unwrap()
458                    } else {
459                        0
460                    },
461                    if joint_weight[1] > 0.0 {
462                        joint_index[1] + u32::try_from(num_extra_joints).unwrap()
463                    } else {
464                        0
465                    },
466                    if joint_weight[2] > 0.0 {
467                        joint_index[2] + u32::try_from(num_extra_joints).unwrap()
468                    } else {
469                        0
470                    },
471                    if joint_weight[3] > 0.0 {
472                        joint_index[3] + u32::try_from(num_extra_joints).unwrap()
473                    } else {
474                        0
475                    },
476                ]
477                .map(|idx| u16::try_from(idx).expect("Could not convert to u16!"));
478                vertex_attributes_array.push(Vertex {
479                    position: Vector3f::new(position[0], position[1], position[2]).into(),
480                    normal: Vector3f::new(normal[0], normal[1], normal[2]).into(),
481                    uv: Vector2f::new(uv[0], 1.0 - uv[1]).into(),
482                    joint_index: Vector4s::new(indices[0], indices[1], indices[2], indices[3]).into(),
483                    joint_weight: Vector4f::new(
484                        joint_weight[0] / jw_sum,
485                        joint_weight[1] / jw_sum,
486                        joint_weight[2] / jw_sum,
487                        joint_weight[3] / jw_sum,
488                    )
489                    .into(),
490                });
491            }
492            let mut texture_infos: Vec<GltfTextureInfo> = vec![];
493            let mut smpl_textures = SmplTextures {
494                diffuse_index: None,
495                normals_index: None,
496                metalic_roughtness_index: None,
497            };
498            if let Some(img) = diffuse_tex {
499                let diffuse_tex = self.add_texture(img, texture_infos.len(), "diffuse");
500                if let Some(diffuse_tex) = diffuse_tex {
501                    smpl_textures.diffuse_index = Some(texture_infos.len());
502                    texture_infos.push(diffuse_tex);
503                }
504            }
505            self.prepare_normals(&mut smpl_textures, &mut texture_infos, normals_tex);
506            self.prepare_metallic_roughness(&mut smpl_textures, &mut texture_infos, metalness_tex, roughness_tex);
507            let mut base_color_texture: Option<gltf_json::texture::Info> = None;
508            let mut normal_texture: Option<gltf_json::material::NormalTexture> = None;
509            let mut metallic_roughness_texture: Option<gltf_json::texture::Info> = None;
510            if let Some(diffuse_texture_index) = smpl_textures.diffuse_index {
511                base_color_texture = Some(gltf_json::texture::Info {
512                    index: gltf_json::Index::new(u32::try_from(diffuse_texture_index).expect("Could not convert to u32!")),
513                    tex_coord: 0,
514                    extensions: None,
515                    extras: None,
516                });
517            }
518            if let Some(normal_texture_index) = smpl_textures.normals_index {
519                normal_texture = Some(gltf_json::material::NormalTexture {
520                    scale: 1.,
521                    index: gltf_json::Index::new(u32::try_from(normal_texture_index).expect("Could not convert to u32!")),
522                    tex_coord: 0,
523                    extensions: None,
524                    extras: None,
525                });
526            }
527            if let Some(metallic_roughness_texture_index) = smpl_textures.metalic_roughtness_index {
528                metallic_roughness_texture = Some(gltf_json::texture::Info {
529                    index: gltf_json::Index::new(u32::try_from(metallic_roughness_texture_index).expect("Could not convert to u32!")),
530                    tex_coord: 0,
531                    extensions: None,
532                    extras: None,
533                });
534            }
535            let material = gltf_json::Material {
536                alpha_cutoff: None,
537                alpha_mode: gltf_json::validation::Checked::<AlphaMode>::Valid(AlphaMode::Opaque),
538                double_sided: false,
539                name: Some("SMPL_material".to_string()),
540                pbr_metallic_roughness: gltf_json::material::PbrMetallicRoughness {
541                    base_color_factor: gltf_json::material::PbrBaseColorFactor([1., 1., 1., 1.]),
542                    base_color_texture,
543                    metallic_roughness_texture,
544                    ..Default::default()
545                },
546                normal_texture,
547                occlusion_texture: None,
548                emissive_texture: None,
549                emissive_factor: gltf_json::material::EmissiveFactor([0., 0., 0.]),
550                extensions: None,
551                extras: None,
552            };
553            materials.push(material);
554            let mut morph_targets: Option<Vec<gltf_json::mesh::MorphTarget>> = None;
555            if self.num_morph_targets() > 0 && self.is_animated() {
556                let mut morph_target_accessors_start_idx = 7 + self.per_body_data[0].default_joint_translations.as_ref().unwrap().shape()[0] + 4;
557                if compatibility_mode == GltfCompatibilityMode::Unreal {
558                    morph_target_accessors_start_idx += 1;
559                }
560                morph_targets = Some(self.create_morph_targets(morph_target_accessors_start_idx));
561            }
562            let primitive_offset = accessors.len() as u32;
563            let primitive = gltf_json::mesh::Primitive {
564                attributes: {
565                    let mut map = std::collections::BTreeMap::new();
566                    map.insert(
567                        Valid(gltf_json::mesh::Semantic::Positions),
568                        gltf_json::Index::new(PrimitiveAttrIDs::Positions as u32 + primitive_offset),
569                    );
570                    map.insert(
571                        Valid(gltf_json::mesh::Semantic::Normals),
572                        gltf_json::Index::new(PrimitiveAttrIDs::Normals as u32 + primitive_offset),
573                    );
574                    map.insert(
575                        Valid(gltf_json::mesh::Semantic::TexCoords(0)),
576                        gltf_json::Index::new(PrimitiveAttrIDs::TexCoords as u32 + primitive_offset),
577                    );
578                    map.insert(
579                        Valid(gltf_json::mesh::Semantic::Joints(0)),
580                        gltf_json::Index::new(PrimitiveAttrIDs::Joints as u32 + primitive_offset),
581                    );
582                    map.insert(
583                        Valid(gltf_json::mesh::Semantic::Weights(0)),
584                        gltf_json::Index::new(PrimitiveAttrIDs::Weights as u32 + primitive_offset),
585                    );
586                    map
587                },
588                extensions: Option::default(),
589                extras: Option::default(),
590                indices: Some(gltf_json::Index::new(PrimitiveAttrIDs::Indices as u32 + primitive_offset)),
591                material: Some(gltf_json::Index::new(body_idx as u32)),
592                mode: Valid(gltf_json::mesh::Mode::Triangles),
593                targets: morph_targets,
594            };
595            let mut morph_target_weights: Option<Vec<f32>> = None;
596            if self.num_morph_targets() > 0 && self.is_animated() {
597                morph_target_weights = Some(vec![0.0; self.num_morph_targets()]);
598            }
599            let mesh = gltf_json::Mesh {
600                extensions: Option::default(),
601                extras: Option::default(),
602                name: Some(format!("SMPL_mesh_{body_idx}")),
603                primitives: vec![primitive],
604                weights: morph_target_weights,
605            };
606            meshes.push(mesh);
607            let vertex_data = to_padded_byte_vector(&vertex_attributes_array);
608            let index_data = to_padded_byte_vector(&indices_array);
609            let inv_bind_mat_data = to_padded_byte_vector(&inverse_bind_matrices);
610            let mut per_view_running_offset: [usize; 6] = [0, 0, 0, 0, 0, 0];
611            let current_buffer_view_offset = buffer_views.len() as u32;
612            let current_accessor_offset = accessors.len() as u32;
613            let accessor = self.create_accessors(
614                body_idx,
615                vertex_count,
616                face_count,
617                joint_count,
618                current_buffer_view_offset,
619                &mut per_view_running_offset,
620                num_extra_joints,
621                compatibility_mode,
622            );
623            accessors.extend(accessor);
624            let mut current_buffer_views = vec![];
625            self.create_buffer_views(
626                body_idx as u32,
627                full_buffer_data.len(),
628                vertex_count,
629                face_count,
630                joint_count,
631                num_extra_joints,
632                &mut current_buffer_views,
633                compatibility_mode,
634            );
635            let (buffer_data, composed_buffer_views, buffer_offset, buffer_index) = self.compose_buffer_views(
636                body_idx,
637                &current_body,
638                current_buffer_views.clone(),
639                index_data.as_slice(),
640                vertex_data.as_slice(),
641                inv_bind_mat_data.as_slice(),
642                &mut texture_infos,
643                compatibility_mode,
644            );
645            global_buffer_offset += buffer_offset;
646            global_buffer_index += buffer_index;
647            full_buffer_data.extend(buffer_data);
648            buffer_views.extend(composed_buffer_views);
649            for texture in texture_infos {
650                images.push(texture.image);
651                textures.push(texture.texture);
652                texture_samplers.push(texture.sampler);
653            }
654            let armature_node_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
655            let armature_node = Node {
656                name: Some(format!("Armature_{body_idx}")),
657                children: Some(vec![]),
658                ..Default::default()
659            };
660            nodes.push(armature_node);
661            if let Some(ref mut scene_armatures) = nodes[0].children {
662                scene_armatures.push(gltf_json::Index::new(armature_node_index));
663            }
664            let mesh_skin_binding_node_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
665            let mesh_skin_binding_node = Node {
666                mesh: Some(gltf_json::Index::new(body_idx as u32)),
667                skin: Some(gltf_json::Index::new(body_idx as u32)),
668                name: Some(format!("MeshSkinBinding_{body_idx}")),
669                children: None,
670                ..Default::default()
671            };
672            nodes.push(mesh_skin_binding_node);
673            let root_node_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
674            let mut joints = vec![];
675            if compatibility_mode == GltfCompatibilityMode::Unreal {
676                let root_node = Node {
677                    name: Some("root".to_string()),
678                    translation: Some([0.0, 0.0, 0.0]),
679                    children: Some(vec![]),
680                    ..Default::default()
681                };
682                let joint_index = gltf_json::Index::<Node>::new(u32::try_from(nodes.len()).expect("Issue converting Node idx to u32"));
683                nodes.push(root_node);
684                joints.push(joint_index);
685                let add_empty_node = |nodes: &mut Vec<Node>,
686                                      joints: &mut Vec<gltf_json::Index<Node>>,
687                                      name: &str,
688                                      parent_index: u32,
689                                      has_children: bool,
690                                      reference_bone: usize|
691                 -> u32 {
692                    let relative_parent_idx = parent_index - root_node_index;
693                    let node_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
694                    let unit_quaternion = [0.0, 0.0, 0.0, 1.0];
695                    let trans: Vec<f32> = if reference_bone == 0 {
696                        vec![0.0, 0.0, 0.0]
697                    } else {
698                        let trans = vec_from_vec(&joint_translations.row(reference_bone).to_vec());
699                        let parent_trans = if unreal_mapping[relative_parent_idx as usize] != 0 {
700                            vec_from_vec(&joint_translations.row(unreal_mapping[relative_parent_idx as usize]).to_vec())
701                        } else {
702                            vec_from_vec(&[0.0, 0.0, 0.0])
703                        };
704                        vec_to_vec(&subv3f(&trans, &parent_trans))
705                    };
706                    let translation = [trans[0], trans[1], trans[2]];
707                    let new_node = Node {
708                        name: Some(name.to_string()),
709                        rotation: Some(UnitQuaternion(unit_quaternion)),
710                        translation: Some(translation),
711                        children: if has_children { Some(vec![]) } else { None },
712                        ..Default::default()
713                    };
714                    if let Some(ref mut parent_children) = nodes[parent_index as usize].children {
715                        parent_children.push(gltf_json::Index::new(node_index));
716                    }
717                    let joint_index = gltf_json::Index::<Node>::new(node_index);
718                    nodes.push(new_node);
719                    joints.push(joint_index);
720                    node_index
721                };
722                add_empty_node(&mut nodes, &mut joints, "center_of_mass", root_node_index, false, 0);
723                let ik_foot_root_index = add_empty_node(&mut nodes, &mut joints, "ik_foot_root", root_node_index, true, 0);
724                add_empty_node(&mut nodes, &mut joints, "ik_foot_r", ik_foot_root_index, false, 8);
725                add_empty_node(&mut nodes, &mut joints, "ik_foot_l", ik_foot_root_index, false, 7);
726                let ik_hand_root_index = add_empty_node(&mut nodes, &mut joints, "ik_hand_root", root_node_index, true, 0);
727                let ik_hand_gun_index = add_empty_node(&mut nodes, &mut joints, "ik_hand_gun", ik_hand_root_index, true, 21);
728                add_empty_node(&mut nodes, &mut joints, "ik_hand_r", ik_hand_gun_index, false, 21);
729                add_empty_node(&mut nodes, &mut joints, "ik_hand_l", ik_hand_gun_index, false, 20);
730                add_empty_node(&mut nodes, &mut joints, "interaction", root_node_index, false, 0);
731            }
732            let skeleton_root_index = self.add_skin(
733                format!("Skin_{body_idx}"),
734                &current_body,
735                armature_node_index,
736                current_accessor_offset,
737                &mut nodes,
738                &mut skins,
739                &mut joints,
740                compatibility_mode,
741            );
742            if compatibility_mode == GltfCompatibilityMode::Unreal {
743                if let Some(ref mut root) = nodes[root_node_index as usize].children {
744                    root.push(skeleton_root_index);
745                }
746            }
747            if let Some(ref mut armature_children) = nodes[armature_node_index as usize].children {
748                armature_children.push(gltf_json::Index::new(mesh_skin_binding_node_index));
749                armature_children.push(gltf_json::Index::new(root_node_index));
750            }
751            if self.is_animated() {
752                let animation_channels = self.create_animation_channels(
753                    joint_count,
754                    root_node_index,
755                    skeleton_root_index.value(),
756                    samplers.len(),
757                    compatibility_mode,
758                );
759                let animation_samplers = self.create_animation_samplers(joint_count, current_accessor_offset, compatibility_mode);
760                channels.extend(animation_channels);
761                samplers.extend(animation_samplers);
762            }
763        }
764        if self.smpl_camera.is_some() {
765            let (cam_track_buffer_views, cam_track_buffer_data) =
766                self.create_camera_animation_buffer_views(&mut full_buffer_data.len(), compatibility_mode);
767            let cam_track_accessors = self.create_camera_animation_accessors(buffer_views.len() as u32);
768            let (cam_track_channels, cam_track_samplers) =
769                self.create_camera_animation_channels_and_samplers(accessors.len() as u32, 1, samplers.len() as u32);
770            buffer_views.extend(cam_track_buffer_views);
771            full_buffer_data.extend(cam_track_buffer_data);
772            accessors.extend(cam_track_accessors);
773            channels.extend(cam_track_channels);
774            samplers.extend(cam_track_samplers);
775        }
776        for prop_idx in 0..self.props.len() {
777            let prop = self.props[prop_idx].clone();
778            let prop_mesh_index = self.per_body_data.len() + prop_idx;
779            info!("Writing prop {prop_idx} to GLTF");
780            let mut inverse_bind_matrices: Vec<f32> = vec![];
781            let joint_rotations = batch_rodrigues(&prop.default_joint_poses);
782            let joint_translations = prop.default_translation.clone();
783            let joint_count = 1;
784            let parents = [0];
785            let bind_matrices = self.create_bind_matrices(&joint_rotations, &joint_translations, &parents);
786            for j_idx in 0..joint_count {
787                let mut inverse_bind_matrix = nd::Array2::<f32>::zeros((4, 4));
788                let inverse_rotation_matrix: ArrayBase<ndarray::ViewRepr<&f32>, Dim<[usize; 2]>> =
789                    bind_matrices.slice(s![j_idx, 0..3, 0..3]).reversed_axes();
790                let translation: nd::Array1<f32> = bind_matrices.slice(s![j_idx, 0..3, 3]).to_owned();
791                let inverse_translation = -inverse_rotation_matrix.dot(&translation);
792                inverse_bind_matrix.slice_mut(s![0..3, 0..3]).assign(&inverse_rotation_matrix);
793                inverse_bind_matrix.slice_mut(s![0..3, 3]).assign(&inverse_translation);
794                inverse_bind_matrix[(3, 3)] = 1.0;
795                inverse_bind_matrices.extend(inverse_bind_matrix.t().iter());
796            }
797            let has_uv = prop.uvs.is_some();
798            info!("has uv: {has_uv}");
799            let vertex_data = if let Some(uvs) = prop.uvs.as_ref() {
800                info!("uv size: {:?}", uvs.shape());
801                info!("prop.positions size: {:?}", prop.positions.shape());
802                let mut vertex_attributes_array: Vec<VertexPropWithUV> = vec![];
803                for (position, uv) in izip!(prop.positions.row_iter(), uvs.row_iter()) {
804                    vertex_attributes_array.push(VertexPropWithUV {
805                        position: Vector3f::new(position[0], position[1], position[2]).into(),
806                        uv: Vector2f::new(uv[0], 1.0 - uv[1]).into(),
807                        joint_index: Vector4s::new(0, 0, 0, 0).into(),
808                        joint_weight: Vector4f::new(1.0, 0.0, 0.0, 0.0).into(),
809                    });
810                }
811                to_padded_byte_vector(&vertex_attributes_array)
812            } else {
813                println!("no uv found");
814                let mut vertex_attributes_array: Vec<VertexProp> = vec![];
815                for position in izip!(prop.positions.row_iter(),) {
816                    vertex_attributes_array.push(VertexProp {
817                        position: Vector3f::new(position[0], position[1], position[2]).into(),
818                        joint_index: Vector4s::new(0, 0, 0, 0).into(),
819                        joint_weight: Vector4f::new(1.0, 0.0, 0.0, 0.0).into(),
820                    });
821                }
822                to_padded_byte_vector(&vertex_attributes_array)
823            };
824            let mut indices_array: Vec<u32> = vec![];
825            for row in prop.faces.row_iter() {
826                indices_array.extend_from_slice(&[row[0], row[1], row[2]]);
827            }
828            let index_data = to_padded_byte_vector(&indices_array);
829            let inv_bind_mat_data = to_padded_byte_vector(&inverse_bind_matrices);
830            let has_diffuse = prop.diffuse_texture.is_some();
831            let mut texture_infos: Vec<GltfTextureInfo> = vec![];
832            let mut diffuse_prop_texture_id = None;
833            let tex_offset = self.per_body_data.len() * 3;
834            if let Some(img) = prop.diffuse_texture.as_ref() {
835                let diffuse_tex = self.add_texture(img, texture_infos.len() + tex_offset, "diffuse_prop");
836                if let Some(diffuse_tex) = diffuse_tex {
837                    diffuse_prop_texture_id = Some(texture_infos.len());
838                    texture_infos.push(diffuse_tex);
839                }
840            }
841            diffuse_prop_texture_id = diffuse_prop_texture_id.map(|id| id + tex_offset);
842            let mut base_color_texture: Option<gltf_json::texture::Info> = None;
843            if let Some(diffuse_texture_index) = diffuse_prop_texture_id {
844                base_color_texture = Some(gltf_json::texture::Info {
845                    index: gltf_json::Index::new(u32::try_from(diffuse_texture_index).expect("Could not convert to u32!")),
846                    tex_coord: 0,
847                    extensions: None,
848                    extras: None,
849                });
850            }
851            let material = gltf_json::Material {
852                alpha_cutoff: None,
853                alpha_mode: gltf_json::validation::Checked::<AlphaMode>::Valid(AlphaMode::Opaque),
854                double_sided: false,
855                name: Some("prop_material".to_string()),
856                pbr_metallic_roughness: gltf_json::material::PbrMetallicRoughness {
857                    base_color_factor: gltf_json::material::PbrBaseColorFactor([1., 1., 1., 1.]),
858                    base_color_texture,
859                    ..Default::default()
860                },
861                normal_texture: None,
862                occlusion_texture: None,
863                emissive_texture: None,
864                emissive_factor: gltf_json::material::EmissiveFactor([0., 0., 0.]),
865                extensions: None,
866                extras: None,
867            };
868            materials.push(material);
869            let primitive_offset = accessors.len() as u32;
870            let materials_selected = if has_diffuse {
871                Some(gltf_json::Index::new(prop_mesh_index as u32))
872            } else {
873                None
874            };
875            let primitive = gltf_json::mesh::Primitive {
876                #[allow(unused_assignments)]
877                attributes: {
878                    let mut map = std::collections::BTreeMap::new();
879                    let mut attr_idx = 1;
880                    map.insert(
881                        Valid(gltf_json::mesh::Semantic::Positions),
882                        gltf_json::Index::new(attr_idx + primitive_offset),
883                    );
884                    attr_idx += 1;
885                    if has_uv {
886                        map.insert(
887                            Valid(gltf_json::mesh::Semantic::TexCoords(0)),
888                            gltf_json::Index::new(attr_idx + primitive_offset),
889                        );
890                        attr_idx += 1;
891                    }
892                    map.insert(
893                        Valid(gltf_json::mesh::Semantic::Joints(0)),
894                        gltf_json::Index::new(attr_idx + primitive_offset),
895                    );
896                    attr_idx += 1;
897                    map.insert(
898                        Valid(gltf_json::mesh::Semantic::Weights(0)),
899                        gltf_json::Index::new(attr_idx + primitive_offset),
900                    );
901                    attr_idx += 1;
902                    map
903                },
904                extensions: Option::default(),
905                extras: Option::default(),
906                indices: Some(gltf_json::Index::new(PrimitiveAttrIDs::Indices as u32 + primitive_offset)),
907                material: materials_selected,
908                mode: Valid(gltf_json::mesh::Mode::Triangles),
909                targets: None,
910            };
911            let mesh = gltf_json::Mesh {
912                extensions: Option::default(),
913                extras: Option::default(),
914                name: Some(format!("prop_{prop_idx}")),
915                primitives: vec![primitive],
916                weights: None,
917            };
918            meshes.push(mesh);
919            let vertex_count = prop.positions.shape().0;
920            let face_count = prop.faces.shape().0;
921            let current_buffer_view_offset = buffer_views.len() as u32;
922            let mut per_view_running_offset: [usize; 6] = [0, 0, 0, 0, 0, 0];
923            let accessor = self.create_accessors_props(
924                prop_idx,
925                vertex_count,
926                face_count,
927                current_buffer_view_offset,
928                &mut per_view_running_offset,
929                compatibility_mode,
930                has_uv,
931            );
932            accessors.extend(accessor);
933            let mut current_buffer_views = vec![];
934            self.create_buffer_views_props(
935                prop_idx as u32,
936                full_buffer_data.len(),
937                vertex_count,
938                face_count,
939                &mut current_buffer_views,
940                compatibility_mode,
941                has_uv,
942            );
943            let (buffer_data, composed_buffer_views, _buffer_offset, _buffer_index) = self.compose_buffer_views_props(
944                &self.props[prop_idx],
945                current_buffer_views.clone(),
946                index_data.as_slice(),
947                vertex_data.as_slice(),
948                inv_bind_mat_data.as_slice(),
949                &mut texture_infos,
950                compatibility_mode,
951                global_buffer_offset,
952                global_buffer_index,
953            );
954            full_buffer_data.extend(buffer_data);
955            buffer_views.extend(composed_buffer_views);
956            for texture in texture_infos {
957                images.push(texture.image);
958                textures.push(texture.texture);
959                texture_samplers.push(texture.sampler);
960            }
961            let prop_node_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
962            let armature_node = Node {
963                name: Some(format!("Armature_Prop_{prop_idx}")),
964                children: Some(vec![]),
965                ..Default::default()
966            };
967            nodes.push(armature_node);
968            if let Some(ref mut scene_root_node_children) = nodes[0].children {
969                scene_root_node_children.push(gltf_json::Index::new(prop_node_index));
970            }
971            let mesh_skin_binding_node_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
972            let mesh_skin_binding_node = Node {
973                mesh: Some(gltf_json::Index::new(prop_mesh_index as u32)),
974                skin: Some(gltf_json::Index::new(prop_mesh_index as u32)),
975                name: Some(format!("MeshSkinBinding_{prop_idx}")),
976                children: None,
977                ..Default::default()
978            };
979            nodes.push(mesh_skin_binding_node);
980            let root_node_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
981            let mut joints = vec![];
982            let skeleton_root_index = self.add_skin_prop(
983                format!("Skin_prop_{prop_idx}"),
984                &prop,
985                prop_node_index,
986                accessors.len().try_into().unwrap(),
987                &mut nodes,
988                &mut skins,
989                &mut joints,
990                compatibility_mode,
991            );
992            if let Some(ref mut armature_children) = nodes[prop_node_index as usize].children {
993                armature_children.push(gltf_json::Index::new(mesh_skin_binding_node_index));
994                armature_children.push(gltf_json::Index::new(root_node_index));
995            }
996            let animation_channels = self.create_animation_channels_prop(
997                joint_count,
998                root_node_index,
999                skeleton_root_index.value(),
1000                samplers.len(),
1001                compatibility_mode,
1002            );
1003            let animation_samplers = self.create_animation_samplers_prop(joint_count, accessors.len().try_into().unwrap(), compatibility_mode);
1004            channels.extend(animation_channels);
1005            samplers.extend(animation_samplers);
1006        }
1007        let buffer = gltf_json::Buffer {
1008            byte_length: USize64::from(full_buffer_data.len()),
1009            extensions: Option::default(),
1010            extras: Option::default(),
1011            name: Some("scene_buffer".to_string()),
1012            uri: if binary { None } else { Some("buffer0.bin".into()) },
1013        };
1014        buffers.push(buffer);
1015        let mut animations: Vec<gltf_json::Animation> = vec![];
1016        if self.is_animated() {
1017            let animation = gltf_json::Animation {
1018                extensions: Option::default(),
1019                extras: Option::default(),
1020                channels,
1021                name: Some("Scene_animation".to_string()),
1022                samplers,
1023            };
1024            animations.push(animation);
1025        }
1026        let root = gltf_json::Root {
1027            accessors,
1028            animations,
1029            buffers,
1030            buffer_views,
1031            cameras,
1032            images,
1033            materials,
1034            meshes,
1035            nodes,
1036            samplers: texture_samplers,
1037            scenes,
1038            skins,
1039            textures,
1040            ..Default::default()
1041        };
1042        (full_buffer_data, root)
1043    }
1044    /// Function for creating buffer view definitions
1045    #[allow(clippy::too_many_arguments)]
1046    fn create_buffer_views(
1047        &self,
1048        body_idx: u32,
1049        mut running_offset: usize,
1050        vertex_count: usize,
1051        face_count: usize,
1052        joint_count: usize,
1053        num_extra_joints: usize,
1054        buffer_views: &mut Vec<gltf_json::buffer::View>,
1055        compatibility_mode: GltfCompatibilityMode,
1056    ) {
1057        let index_buffer_size = face_count * 3 * mem::size_of::<u32>();
1058        let index_buffer_view = gltf_json::buffer::View {
1059            buffer: gltf_json::Index::new(0),
1060            byte_length: USize64::from(index_buffer_size),
1061            byte_offset: Some(USize64::from(running_offset)),
1062            byte_stride: None,
1063            extensions: Option::default(),
1064            extras: Option::default(),
1065            name: Some(format!("index_buffer_view_{body_idx}")),
1066            target: Some(Valid(gltf_json::buffer::Target::ElementArrayBuffer)),
1067        };
1068        buffer_views.push(index_buffer_view);
1069        running_offset += index_buffer_size;
1070        let vertex_buffer_size = vertex_count * mem::size_of::<Vertex>();
1071        let vertex_buffer_view = gltf_json::buffer::View {
1072            buffer: gltf_json::Index::new(0),
1073            byte_length: USize64::from(vertex_buffer_size),
1074            byte_offset: Some(USize64::from(running_offset)),
1075            byte_stride: Some(gltf_json::buffer::Stride(mem::size_of::<Vertex>())),
1076            extensions: Option::default(),
1077            extras: Option::default(),
1078            name: Some(format!("vertex_buffer_view_{body_idx}")),
1079            target: Some(Valid(gltf_json::buffer::Target::ArrayBuffer)),
1080        };
1081        buffer_views.push(vertex_buffer_view);
1082        running_offset += vertex_buffer_size;
1083        let inv_bind_matrix_buffer_size = (joint_count + num_extra_joints) * 16 * mem::size_of::<f32>();
1084        let inverse_bind_mat_buffer_view = gltf_json::buffer::View {
1085            buffer: gltf_json::Index::new(0),
1086            byte_length: USize64::from(inv_bind_matrix_buffer_size),
1087            byte_offset: Some(USize64::from(running_offset)),
1088            byte_stride: None,
1089            extensions: Option::default(),
1090            extras: Option::default(),
1091            name: Some(format!("inv_bind_matrix_buffer_view_{body_idx}")),
1092            target: None,
1093        };
1094        buffer_views.push(inverse_bind_mat_buffer_view);
1095        running_offset += inv_bind_matrix_buffer_size;
1096        if self.is_animated() {
1097            let rotation_animation_buffer_size = self.frame_count.unwrap() * 4 * mem::size_of::<f32>();
1098            let translation_animation_buffer_size = self.frame_count.unwrap() * 3 * mem::size_of::<f32>();
1099            let scale_animation_buffer_size = self.frame_count.unwrap() * 3 * mem::size_of::<f32>();
1100            let animation_buffer_views = self.create_animation_buffer_views(
1101                body_idx,
1102                joint_count,
1103                rotation_animation_buffer_size,
1104                translation_animation_buffer_size,
1105                scale_animation_buffer_size,
1106                &mut running_offset,
1107                compatibility_mode,
1108            );
1109            buffer_views.extend(animation_buffer_views);
1110            if self.num_morph_targets() > 0 && body_idx == 0 {
1111                let morph_target_buffer_size = vertex_count * 3 * mem::size_of::<f32>();
1112                let morph_target_buffer_views = self.create_morph_target_buffer_views(morph_target_buffer_size, &mut running_offset);
1113                buffer_views.extend(morph_target_buffer_views);
1114            }
1115        }
1116    }
1117    /// Function for creating buffer view definitions
1118    #[allow(clippy::too_many_arguments)]
1119    fn create_buffer_views_props(
1120        &self,
1121        prop_idx: u32,
1122        mut running_offset: usize,
1123        vertex_count: usize,
1124        face_count: usize,
1125        buffer_views: &mut Vec<gltf_json::buffer::View>,
1126        compatibility_mode: GltfCompatibilityMode,
1127        with_uv: bool,
1128    ) {
1129        let joint_count = 1;
1130        let index_buffer_size = face_count * 3 * mem::size_of::<u32>();
1131        let index_buffer_view = gltf_json::buffer::View {
1132            buffer: gltf_json::Index::new(0),
1133            byte_length: USize64::from(index_buffer_size),
1134            byte_offset: Some(USize64::from(running_offset)),
1135            byte_stride: None,
1136            extensions: Option::default(),
1137            extras: Option::default(),
1138            name: Some(format!("index_buffer_view_prop_{prop_idx}")),
1139            target: Some(Valid(gltf_json::buffer::Target::ElementArrayBuffer)),
1140        };
1141        buffer_views.push(index_buffer_view);
1142        running_offset += index_buffer_size;
1143        let vertex_size = if with_uv {
1144            mem::size_of::<VertexPropWithUV>()
1145        } else {
1146            mem::size_of::<VertexProp>()
1147        };
1148        let vertex_buffer_size = vertex_count * vertex_size;
1149        let vertex_buffer_view = gltf_json::buffer::View {
1150            buffer: gltf_json::Index::new(0),
1151            byte_length: USize64::from(vertex_buffer_size),
1152            byte_offset: Some(USize64::from(running_offset)),
1153            byte_stride: Some(gltf_json::buffer::Stride(vertex_size)),
1154            extensions: Option::default(),
1155            extras: Option::default(),
1156            name: Some(format!("vertex_buffer_view_prop_{prop_idx}")),
1157            target: Some(Valid(gltf_json::buffer::Target::ArrayBuffer)),
1158        };
1159        buffer_views.push(vertex_buffer_view);
1160        running_offset += vertex_buffer_size;
1161        let inv_bind_matrix_buffer_size = (joint_count) * 16 * mem::size_of::<f32>();
1162        let inverse_bind_mat_buffer_view = gltf_json::buffer::View {
1163            buffer: gltf_json::Index::new(0),
1164            byte_length: USize64::from(inv_bind_matrix_buffer_size),
1165            byte_offset: Some(USize64::from(running_offset)),
1166            byte_stride: None,
1167            extensions: Option::default(),
1168            extras: Option::default(),
1169            name: Some(format!("inv_bind_matrix_buffer_view_{prop_idx}")),
1170            target: None,
1171        };
1172        buffer_views.push(inverse_bind_mat_buffer_view);
1173        running_offset += inv_bind_matrix_buffer_size;
1174        let rotation_animation_buffer_size = self.frame_count.unwrap() * 4 * mem::size_of::<f32>();
1175        let translation_animation_buffer_size = self.frame_count.unwrap() * 3 * mem::size_of::<f32>();
1176        let scale_animation_buffer_size = self.frame_count.unwrap() * 3 * mem::size_of::<f32>();
1177        let animation_buffer_views = self.create_animation_buffer_views_prop(
1178            prop_idx,
1179            joint_count,
1180            rotation_animation_buffer_size,
1181            translation_animation_buffer_size,
1182            scale_animation_buffer_size,
1183            &mut running_offset,
1184            compatibility_mode,
1185        );
1186        buffer_views.extend(animation_buffer_views);
1187    }
1188    /// Function for creating animation based buffer views
1189    #[allow(clippy::too_many_arguments)]
1190    fn create_animation_buffer_views(
1191        &self,
1192        body_idx: u32,
1193        joint_count: usize,
1194        rotation_buffer_size: usize,
1195        translation_buffer_size: usize,
1196        scale_buffer_size: usize,
1197        running_offset: &mut usize,
1198        compatibility_mode: GltfCompatibilityMode,
1199    ) -> Vec<gltf_json::buffer::View> {
1200        let mut animation_buffer_views: Vec<gltf_json::buffer::View> = vec![];
1201        let keyframe_buffer_size = self.frame_count.unwrap() * mem::size_of::<f32>();
1202        let keyframe_buffer_view = gltf_json::buffer::View {
1203            buffer: gltf_json::Index::new(0),
1204            byte_length: USize64::from(keyframe_buffer_size),
1205            byte_offset: Some(USize64::from(*running_offset)),
1206            byte_stride: None,
1207            extensions: Option::default(),
1208            extras: Option::default(),
1209            name: Some(format!("keyframe_buffer_view_{body_idx}")),
1210            target: None,
1211        };
1212        animation_buffer_views.push(keyframe_buffer_view);
1213        *running_offset += keyframe_buffer_size;
1214        for j_idx in 0..joint_count {
1215            let buffer_view_name = format!("joint_{j_idx}_animations_buffer_view_{body_idx}");
1216            let animation_buffer_view = gltf_json::buffer::View {
1217                buffer: gltf_json::Index::new(0),
1218                byte_length: USize64::from(rotation_buffer_size),
1219                byte_offset: Some(USize64::from(*running_offset)),
1220                byte_stride: None,
1221                extensions: Option::default(),
1222                extras: Option::default(),
1223                name: Some(buffer_view_name),
1224                target: None,
1225            };
1226            animation_buffer_views.push(animation_buffer_view);
1227            *running_offset += rotation_buffer_size;
1228        }
1229        let buffer_view_name = format!("root_translation_animations_buffer_view_{body_idx}");
1230        let animation_buffer_view = gltf_json::buffer::View {
1231            buffer: gltf_json::Index::new(0),
1232            byte_length: USize64::from(translation_buffer_size),
1233            byte_offset: Some(USize64::from(*running_offset)),
1234            byte_stride: None,
1235            extensions: Option::default(),
1236            extras: Option::default(),
1237            name: Some(buffer_view_name),
1238            target: None,
1239        };
1240        animation_buffer_views.push(animation_buffer_view);
1241        *running_offset += translation_buffer_size;
1242        let buffer_view_name = format!("root_scale_animations_buffer_view_{body_idx}");
1243        let animation_buffer_view = gltf_json::buffer::View {
1244            buffer: gltf_json::Index::new(0),
1245            byte_length: USize64::from(scale_buffer_size),
1246            byte_offset: Some(USize64::from(*running_offset)),
1247            byte_stride: None,
1248            extensions: Option::default(),
1249            extras: Option::default(),
1250            name: Some(buffer_view_name),
1251            target: None,
1252        };
1253        animation_buffer_views.push(animation_buffer_view);
1254        *running_offset += scale_buffer_size;
1255        if compatibility_mode == GltfCompatibilityMode::Unreal {
1256            let buffer_view_name = format!("pelvis_rel_translation_animations_buffer_view_{body_idx}");
1257            let animation_buffer_view = gltf_json::buffer::View {
1258                buffer: gltf_json::Index::new(0),
1259                byte_length: USize64::from(translation_buffer_size),
1260                byte_offset: Some(USize64::from(*running_offset)),
1261                byte_stride: None,
1262                extensions: Option::default(),
1263                extras: Option::default(),
1264                name: Some(buffer_view_name),
1265                target: None,
1266            };
1267            animation_buffer_views.push(animation_buffer_view);
1268            *running_offset += translation_buffer_size;
1269        }
1270        if self.num_morph_targets() > 0 {
1271            let morph_weights_buffer_size = self.frame_count.unwrap() * self.num_morph_targets() * mem::size_of::<f32>();
1272            let buffer_view_name = format!("morph_target_weights_{body_idx}");
1273            let morph_weights_buffer_view = gltf_json::buffer::View {
1274                buffer: gltf_json::Index::new(0),
1275                byte_length: USize64::from(morph_weights_buffer_size),
1276                byte_offset: Some(USize64::from(*running_offset)),
1277                byte_stride: None,
1278                extensions: Option::default(),
1279                extras: Option::default(),
1280                name: Some(buffer_view_name),
1281                target: None,
1282            };
1283            animation_buffer_views.push(morph_weights_buffer_view);
1284            *running_offset += morph_weights_buffer_size;
1285        }
1286        animation_buffer_views
1287    }
1288    #[allow(clippy::too_many_arguments)]
1289    fn create_animation_buffer_views_prop(
1290        &self,
1291        prop_idx: u32,
1292        joint_count: usize,
1293        rotation_buffer_size: usize,
1294        translation_buffer_size: usize,
1295        scale_buffer_size: usize,
1296        running_offset: &mut usize,
1297        _compatibility_mode: GltfCompatibilityMode,
1298    ) -> Vec<gltf_json::buffer::View> {
1299        let mut animation_buffer_views: Vec<gltf_json::buffer::View> = vec![];
1300        let keyframe_buffer_size = self.frame_count.unwrap() * mem::size_of::<f32>();
1301        let keyframe_buffer_view = gltf_json::buffer::View {
1302            buffer: gltf_json::Index::new(0),
1303            byte_length: USize64::from(keyframe_buffer_size),
1304            byte_offset: Some(USize64::from(*running_offset)),
1305            byte_stride: None,
1306            extensions: Option::default(),
1307            extras: Option::default(),
1308            name: Some(format!("keyframe_buffer_view_prop_{prop_idx}")),
1309            target: None,
1310        };
1311        animation_buffer_views.push(keyframe_buffer_view);
1312        *running_offset += keyframe_buffer_size;
1313        for j_idx in 0..joint_count {
1314            let buffer_view_name = format!("joint_{j_idx}_animations_buffer_view_prop_{prop_idx}");
1315            let animation_buffer_view = gltf_json::buffer::View {
1316                buffer: gltf_json::Index::new(0),
1317                byte_length: USize64::from(rotation_buffer_size),
1318                byte_offset: Some(USize64::from(*running_offset)),
1319                byte_stride: None,
1320                extensions: Option::default(),
1321                extras: Option::default(),
1322                name: Some(buffer_view_name),
1323                target: None,
1324            };
1325            animation_buffer_views.push(animation_buffer_view);
1326            *running_offset += rotation_buffer_size;
1327        }
1328        let buffer_view_name = format!("root_translation_animations_buffer_view_prop_{prop_idx}");
1329        let animation_buffer_view = gltf_json::buffer::View {
1330            buffer: gltf_json::Index::new(0),
1331            byte_length: USize64::from(translation_buffer_size),
1332            byte_offset: Some(USize64::from(*running_offset)),
1333            byte_stride: None,
1334            extensions: Option::default(),
1335            extras: Option::default(),
1336            name: Some(buffer_view_name),
1337            target: None,
1338        };
1339        animation_buffer_views.push(animation_buffer_view);
1340        *running_offset += translation_buffer_size;
1341        let buffer_view_name = format!("root_scale_animations_buffer_view_prop_{prop_idx}");
1342        let animation_buffer_view = gltf_json::buffer::View {
1343            buffer: gltf_json::Index::new(0),
1344            byte_length: USize64::from(scale_buffer_size),
1345            byte_offset: Some(USize64::from(*running_offset)),
1346            byte_stride: None,
1347            extensions: Option::default(),
1348            extras: Option::default(),
1349            name: Some(buffer_view_name),
1350            target: None,
1351        };
1352        animation_buffer_views.push(animation_buffer_view);
1353        *running_offset += scale_buffer_size;
1354        animation_buffer_views
1355    }
1356    /// Function for creating buffer views for morph targets
1357    fn create_morph_target_buffer_views(&self, morph_target_buffer_size: usize, running_offset: &mut usize) -> Vec<gltf_json::buffer::View> {
1358        let mut morph_targets_buffer_views: Vec<gltf_json::buffer::View> = vec![];
1359        for morph_target_idx in 0..self.num_morph_targets() {
1360            let buffer_view_name = format!("morph_{morph_target_idx}_buffer_view");
1361            let morph_target_buffer_view = gltf_json::buffer::View {
1362                buffer: gltf_json::Index::new(0),
1363                byte_length: USize64::from(morph_target_buffer_size),
1364                byte_offset: Some(USize64::from(*running_offset)),
1365                byte_stride: None,
1366                extensions: Option::default(),
1367                extras: Option::default(),
1368                name: Some(buffer_view_name),
1369                target: Some(Valid(gltf_json::buffer::Target::ArrayBuffer)),
1370            };
1371            morph_targets_buffer_views.push(morph_target_buffer_view);
1372            *running_offset += morph_target_buffer_size;
1373        }
1374        morph_targets_buffer_views
1375    }
1376    /// Function fo creating all the GLTF accessors
1377    #[allow(clippy::too_many_lines)]
1378    #[allow(clippy::too_many_arguments)]
1379    fn create_accessors(
1380        &self,
1381        body_idx: usize,
1382        vertex_count: usize,
1383        face_count: usize,
1384        joint_count: usize,
1385        current_buffer_view_offset: u32,
1386        per_view_running_offset: &mut [usize; 6],
1387        num_extra_joints: usize,
1388        compatibility_mode: GltfCompatibilityMode,
1389    ) -> Vec<gltf_json::Accessor> {
1390        let (min, max) = geom::get_bounding_points(self.per_body_data[body_idx].positions.as_ref().unwrap(), None);
1391        let (min_vec, max_vec): (Vec<f32>, Vec<f32>) = (min.iter().copied().collect(), max.iter().copied().collect());
1392        let mut accessors: Vec<gltf_json::Accessor> = vec![];
1393        let indices = gltf_json::Accessor {
1394            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::Index as u32 + current_buffer_view_offset)),
1395            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Index as usize])),
1396            count: USize64::from(face_count * 3),
1397            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::U32)),
1398            extensions: Option::default(),
1399            extras: Option::default(),
1400            type_: Valid(gltf_json::accessor::Type::Scalar),
1401            min: Some(gltf_json::Value::from(Vec::from([self.faces.as_ref().unwrap().min()]))),
1402            max: Some(gltf_json::Value::from(Vec::from([self.faces.as_ref().unwrap().max()]))),
1403            name: Some(format!("index_accessor_{body_idx}")),
1404            normalized: false,
1405            sparse: None,
1406        };
1407        accessors.push(indices);
1408        let position_element_size = 3 * mem::size_of::<f32>();
1409        let positions = gltf_json::Accessor {
1410            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
1411            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
1412            count: USize64::from(vertex_count),
1413            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1414            extensions: Option::default(),
1415            extras: Option::default(),
1416            type_: Valid(gltf_json::accessor::Type::Vec3),
1417            min: Some(gltf_json::Value::from(min_vec)),
1418            max: Some(gltf_json::Value::from(max_vec)),
1419            name: Some(format!("position_accessor_{body_idx}")),
1420            normalized: false,
1421            sparse: None,
1422        };
1423        per_view_running_offset[BufferViewIDs::VertexAttr as usize] += position_element_size;
1424        accessors.push(positions);
1425        let normal_element_size = 3 * mem::size_of::<f32>();
1426        let normals = gltf_json::Accessor {
1427            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
1428            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
1429            count: USize64::from(vertex_count),
1430            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1431            extensions: Option::default(),
1432            extras: Option::default(),
1433            type_: Valid(gltf_json::accessor::Type::Vec3),
1434            min: None,
1435            max: None,
1436            name: Some(format!("normal_accessor_{body_idx}")),
1437            normalized: false,
1438            sparse: None,
1439        };
1440        per_view_running_offset[BufferViewIDs::VertexAttr as usize] += normal_element_size;
1441        accessors.push(normals);
1442        let uv_element_size = 2 * mem::size_of::<f32>();
1443        let uvs = gltf_json::Accessor {
1444            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
1445            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
1446            count: USize64::from(vertex_count),
1447            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1448            extensions: Option::default(),
1449            extras: Option::default(),
1450            type_: Valid(gltf_json::accessor::Type::Vec2),
1451            min: None,
1452            max: None,
1453            name: Some("uv_accessor".to_string()),
1454            normalized: false,
1455            sparse: None,
1456        };
1457        per_view_running_offset[BufferViewIDs::VertexAttr as usize] += uv_element_size;
1458        accessors.push(uvs);
1459        let joint_index_element_size = 4 * mem::size_of::<u16>();
1460        let joint_indices = gltf_json::Accessor {
1461            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
1462            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
1463            count: USize64::from(vertex_count),
1464            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::U16)),
1465            extensions: Option::default(),
1466            extras: Option::default(),
1467            type_: Valid(gltf_json::accessor::Type::Vec4),
1468            min: None,
1469            max: None,
1470            name: Some("joint_index_accessor".to_string()),
1471            normalized: false,
1472            sparse: None,
1473        };
1474        per_view_running_offset[BufferViewIDs::VertexAttr as usize] += joint_index_element_size;
1475        accessors.push(joint_indices);
1476        let joint_weight_element_size = 4 * mem::size_of::<f32>();
1477        let joint_weights = gltf_json::Accessor {
1478            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
1479            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
1480            count: USize64::from(vertex_count),
1481            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1482            extensions: Option::default(),
1483            extras: Option::default(),
1484            type_: Valid(gltf_json::accessor::Type::Vec4),
1485            min: None,
1486            max: None,
1487            name: Some("joint_index_accessor".to_string()),
1488            normalized: false,
1489            sparse: None,
1490        };
1491        per_view_running_offset[BufferViewIDs::VertexAttr as usize] += joint_weight_element_size;
1492        accessors.push(joint_weights);
1493        let inv_bind_matrices = gltf_json::Accessor {
1494            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::InvBindMat as u32 + current_buffer_view_offset)),
1495            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::InvBindMat as usize])),
1496            count: USize64::from(joint_count + num_extra_joints),
1497            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1498            extensions: Option::default(),
1499            extras: Option::default(),
1500            type_: Valid(gltf_json::accessor::Type::Mat4),
1501            min: None,
1502            max: None,
1503            name: Some("inv_bind_matrices_accessor".to_string()),
1504            normalized: false,
1505            sparse: None,
1506        };
1507        accessors.push(inv_bind_matrices);
1508        if self.is_animated() {
1509            let animation_accessors =
1510                self.create_animation_accessors(joint_count, current_buffer_view_offset, per_view_running_offset, compatibility_mode);
1511            accessors.extend(animation_accessors);
1512            if self.num_morph_targets() > 0 && body_idx == 0 {
1513                let morph_target_accessors =
1514                    self.create_morph_target_accessors(vertex_count, current_buffer_view_offset, per_view_running_offset, compatibility_mode);
1515                accessors.extend(morph_target_accessors);
1516            }
1517        }
1518        accessors
1519    }
1520    /// Function fo creating all the GLTF accessors for props
1521    #[allow(clippy::too_many_lines)]
1522    #[allow(clippy::too_many_arguments)]
1523    fn create_accessors_props(
1524        &self,
1525        prop_idx: usize,
1526        vertex_count: usize,
1527        face_count: usize,
1528        current_buffer_view_offset: u32,
1529        per_view_running_offset: &mut [usize; 6],
1530        compatibility_mode: GltfCompatibilityMode,
1531        with_uv: bool,
1532    ) -> Vec<gltf_json::Accessor> {
1533        let joint_count = 1;
1534        let (min, max) = geom::get_bounding_points(&self.props[prop_idx].positions, None);
1535        let (min_vec, max_vec): (Vec<f32>, Vec<f32>) = (min.iter().copied().collect(), max.iter().copied().collect());
1536        let mut accessors: Vec<gltf_json::Accessor> = vec![];
1537        let indices = gltf_json::Accessor {
1538            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::Index as u32 + current_buffer_view_offset)),
1539            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Index as usize])),
1540            count: USize64::from(face_count * 3),
1541            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::U32)),
1542            extensions: Option::default(),
1543            extras: Option::default(),
1544            type_: Valid(gltf_json::accessor::Type::Scalar),
1545            min: Some(gltf_json::Value::from(Vec::from([self.props[prop_idx].faces.min()]))),
1546            max: Some(gltf_json::Value::from(Vec::from([self.props[prop_idx].faces.max()]))),
1547            name: Some(format!("index_accessor_prop_{prop_idx}")),
1548            normalized: false,
1549            sparse: None,
1550        };
1551        accessors.push(indices);
1552        let position_element_size = 3 * mem::size_of::<f32>();
1553        let positions = gltf_json::Accessor {
1554            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
1555            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
1556            count: USize64::from(vertex_count),
1557            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1558            extensions: Option::default(),
1559            extras: Option::default(),
1560            type_: Valid(gltf_json::accessor::Type::Vec3),
1561            min: Some(gltf_json::Value::from(min_vec)),
1562            max: Some(gltf_json::Value::from(max_vec)),
1563            name: Some(format!("position_accessor_prop_{prop_idx}")),
1564            normalized: false,
1565            sparse: None,
1566        };
1567        per_view_running_offset[BufferViewIDs::VertexAttr as usize] += position_element_size;
1568        accessors.push(positions);
1569        if with_uv {
1570            let uv_element_size = 2 * mem::size_of::<f32>();
1571            let uvs = gltf_json::Accessor {
1572                buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
1573                byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
1574                count: USize64::from(vertex_count),
1575                component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1576                extensions: Option::default(),
1577                extras: Option::default(),
1578                type_: Valid(gltf_json::accessor::Type::Vec2),
1579                min: None,
1580                max: None,
1581                name: Some(format!("uv_accessor_prop_{prop_idx}")),
1582                normalized: false,
1583                sparse: None,
1584            };
1585            per_view_running_offset[BufferViewIDs::VertexAttr as usize] += uv_element_size;
1586            accessors.push(uvs);
1587        }
1588        let joint_index_element_size = 4 * mem::size_of::<u16>();
1589        let joint_indices = gltf_json::Accessor {
1590            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
1591            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
1592            count: USize64::from(vertex_count),
1593            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::U16)),
1594            extensions: Option::default(),
1595            extras: Option::default(),
1596            type_: Valid(gltf_json::accessor::Type::Vec4),
1597            min: None,
1598            max: None,
1599            name: Some("joint_index_accessor_prop".to_string()),
1600            normalized: false,
1601            sparse: None,
1602        };
1603        per_view_running_offset[BufferViewIDs::VertexAttr as usize] += joint_index_element_size;
1604        accessors.push(joint_indices);
1605        let joint_weight_element_size = 4 * mem::size_of::<f32>();
1606        let joint_weights = gltf_json::Accessor {
1607            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
1608            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
1609            count: USize64::from(vertex_count),
1610            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1611            extensions: Option::default(),
1612            extras: Option::default(),
1613            type_: Valid(gltf_json::accessor::Type::Vec4),
1614            min: None,
1615            max: None,
1616            name: Some("joint_weight_accessor_prop".to_string()),
1617            normalized: false,
1618            sparse: None,
1619        };
1620        per_view_running_offset[BufferViewIDs::VertexAttr as usize] += joint_weight_element_size;
1621        accessors.push(joint_weights);
1622        let inv_bind_matrices = gltf_json::Accessor {
1623            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::InvBindMat as u32 + current_buffer_view_offset)),
1624            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::InvBindMat as usize])),
1625            count: USize64::from(joint_count),
1626            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1627            extensions: Option::default(),
1628            extras: Option::default(),
1629            type_: Valid(gltf_json::accessor::Type::Mat4),
1630            min: None,
1631            max: None,
1632            name: Some("inv_bind_matrices_accessor_prop".to_string()),
1633            normalized: false,
1634            sparse: None,
1635        };
1636        accessors.push(inv_bind_matrices);
1637        let animation_accessors =
1638            self.create_animation_accessors_props(joint_count, current_buffer_view_offset, per_view_running_offset, compatibility_mode);
1639        accessors.extend(animation_accessors);
1640        accessors
1641    }
1642    /// Function for creating the animation accessors
1643    #[allow(clippy::too_many_lines)]
1644    fn create_animation_accessors(
1645        &self,
1646        joint_count: usize,
1647        current_buffer_view_offset: u32,
1648        per_view_running_offset: &mut [usize; 6],
1649        compatibility_mode: GltfCompatibilityMode,
1650    ) -> Vec<gltf_json::Accessor> {
1651        let mut animation_accessors: Vec<gltf_json::Accessor> = vec![];
1652        let min_keyframe = self
1653            .keyframe_times
1654            .as_ref()
1655            .expect("keyframe_times should exist")
1656            .iter()
1657            .copied()
1658            .min_by(|a, b| a.partial_cmp(b).expect("Tried to compare a NaN"))
1659            .expect("keyframe_times should have elements in the vector");
1660        let max_keyframe = self
1661            .keyframe_times
1662            .as_ref()
1663            .unwrap()
1664            .iter()
1665            .copied()
1666            .max_by(|a, b| a.partial_cmp(b).expect("Tried to compare a NaN"))
1667            .unwrap();
1668        let keyframe_times = gltf_json::Accessor {
1669            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::Keyframe as u32 + current_buffer_view_offset)),
1670            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Keyframe as usize])),
1671            count: USize64::from(self.frame_count.unwrap()),
1672            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1673            extensions: Option::default(),
1674            extras: Option::default(),
1675            type_: Valid(gltf_json::accessor::Type::Scalar),
1676            min: Some(gltf_json::Value::from(Vec::from([min_keyframe]))),
1677            max: Some(gltf_json::Value::from(Vec::from([max_keyframe]))),
1678            name: Some("keyframes_accessor".to_string()),
1679            normalized: false,
1680            sparse: None,
1681        };
1682        animation_accessors.push(keyframe_times);
1683        let mut running_buffer_view = BufferViewIDs::Animation as u32 + current_buffer_view_offset;
1684        for j_idx in 0..joint_count {
1685            let accessor_name = format!("joint_{j_idx}_animations_accessor");
1686            let joint_animation_accessor = gltf_json::Accessor {
1687                buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1688                byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1689                count: USize64::from(self.frame_count.unwrap()),
1690                component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1691                extensions: Option::default(),
1692                extras: Option::default(),
1693                type_: Valid(gltf_json::accessor::Type::Vec4),
1694                min: None,
1695                max: None,
1696                name: Some(accessor_name),
1697                normalized: false,
1698                sparse: None,
1699            };
1700            animation_accessors.push(joint_animation_accessor);
1701            running_buffer_view += 1;
1702        }
1703        let accessor_name = "root_translation_animations_accessor".to_string();
1704        let body_animation_accessor = gltf_json::Accessor {
1705            buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1706            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1707            count: USize64::from(self.frame_count.unwrap()),
1708            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1709            extensions: Option::default(),
1710            extras: Option::default(),
1711            type_: Valid(gltf_json::accessor::Type::Vec3),
1712            min: None,
1713            max: None,
1714            name: Some(accessor_name),
1715            normalized: false,
1716            sparse: None,
1717        };
1718        animation_accessors.push(body_animation_accessor);
1719        running_buffer_view += 1;
1720        let accessor_name = "root_scale_animations_accessor".to_string();
1721        let vis_animation_accessor = gltf_json::Accessor {
1722            buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1723            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1724            count: USize64::from(self.frame_count.unwrap()),
1725            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1726            extensions: Option::default(),
1727            extras: Option::default(),
1728            type_: Valid(gltf_json::accessor::Type::Vec3),
1729            min: None,
1730            max: None,
1731            name: Some(accessor_name),
1732            normalized: false,
1733            sparse: None,
1734        };
1735        animation_accessors.push(vis_animation_accessor);
1736        running_buffer_view += 1;
1737        if compatibility_mode == GltfCompatibilityMode::Unreal {
1738            let accessor_name = "pelvis_rel_translation_animations_accessor".to_string();
1739            let pelvis_animation_accessor = gltf_json::Accessor {
1740                buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1741                byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1742                count: USize64::from(self.frame_count.unwrap()),
1743                component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1744                extensions: Option::default(),
1745                extras: Option::default(),
1746                type_: Valid(gltf_json::accessor::Type::Vec3),
1747                min: None,
1748                max: None,
1749                name: Some(accessor_name),
1750                normalized: false,
1751                sparse: None,
1752            };
1753            animation_accessors.push(pelvis_animation_accessor);
1754            running_buffer_view += 1;
1755        }
1756        if self.num_morph_targets() > 0 {
1757            let accessor_name = "morph_targets_weights_accessor".to_string();
1758            let morph_targets_weights_accessor = gltf_json::Accessor {
1759                buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1760                byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1761                count: USize64::from(self.frame_count.unwrap() * self.num_morph_targets()),
1762                component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1763                extensions: Option::default(),
1764                extras: Option::default(),
1765                type_: Valid(gltf_json::accessor::Type::Scalar),
1766                min: None,
1767                max: None,
1768                name: Some(accessor_name),
1769                normalized: false,
1770                sparse: None,
1771            };
1772            animation_accessors.push(morph_targets_weights_accessor);
1773            running_buffer_view += 1;
1774        }
1775        per_view_running_offset[BufferViewIDs::Animation as usize] += running_buffer_view as usize;
1776        animation_accessors
1777    }
1778    #[allow(clippy::too_many_lines)]
1779    fn create_animation_accessors_props(
1780        &self,
1781        joint_count: usize,
1782        current_buffer_view_offset: u32,
1783        per_view_running_offset: &mut [usize; 6],
1784        _compatibility_mode: GltfCompatibilityMode,
1785    ) -> Vec<gltf_json::Accessor> {
1786        let mut animation_accessors: Vec<gltf_json::Accessor> = vec![];
1787        let min_keyframe = self
1788            .keyframe_times
1789            .as_ref()
1790            .expect("keyframe_times should exist")
1791            .iter()
1792            .copied()
1793            .min_by(|a, b| a.partial_cmp(b).expect("Tried to compare a NaN"))
1794            .expect("keyframe_times should have elements in the vector");
1795        let max_keyframe = self
1796            .keyframe_times
1797            .as_ref()
1798            .unwrap()
1799            .iter()
1800            .copied()
1801            .max_by(|a, b| a.partial_cmp(b).expect("Tried to compare a NaN"))
1802            .unwrap();
1803        let keyframe_times = gltf_json::Accessor {
1804            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::Keyframe as u32 + current_buffer_view_offset)),
1805            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Keyframe as usize])),
1806            count: USize64::from(self.frame_count.unwrap()),
1807            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1808            extensions: Option::default(),
1809            extras: Option::default(),
1810            type_: Valid(gltf_json::accessor::Type::Scalar),
1811            min: Some(gltf_json::Value::from(Vec::from([min_keyframe]))),
1812            max: Some(gltf_json::Value::from(Vec::from([max_keyframe]))),
1813            name: Some("keyframes_accessor_prop".to_string()),
1814            normalized: false,
1815            sparse: None,
1816        };
1817        animation_accessors.push(keyframe_times);
1818        let mut running_buffer_view = BufferViewIDs::Animation as u32 + current_buffer_view_offset;
1819        for j_idx in 0..joint_count {
1820            let accessor_name = format!("joint_{j_idx}_animations_accessor_prop");
1821            let joint_animation_accessor = gltf_json::Accessor {
1822                buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1823                byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1824                count: USize64::from(self.frame_count.unwrap()),
1825                component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1826                extensions: Option::default(),
1827                extras: Option::default(),
1828                type_: Valid(gltf_json::accessor::Type::Vec4),
1829                min: None,
1830                max: None,
1831                name: Some(accessor_name),
1832                normalized: false,
1833                sparse: None,
1834            };
1835            animation_accessors.push(joint_animation_accessor);
1836            running_buffer_view += 1;
1837        }
1838        let accessor_name = "root_translation_animations_accessor_prop".to_string();
1839        let body_animation_accessor = gltf_json::Accessor {
1840            buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1841            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1842            count: USize64::from(self.frame_count.unwrap()),
1843            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1844            extensions: Option::default(),
1845            extras: Option::default(),
1846            type_: Valid(gltf_json::accessor::Type::Vec3),
1847            min: None,
1848            max: None,
1849            name: Some(accessor_name),
1850            normalized: false,
1851            sparse: None,
1852        };
1853        animation_accessors.push(body_animation_accessor);
1854        running_buffer_view += 1;
1855        let accessor_name = "root_scale_animations_accessor_prop".to_string();
1856        let vis_animation_accessor = gltf_json::Accessor {
1857            buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1858            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1859            count: USize64::from(self.frame_count.unwrap()),
1860            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1861            extensions: Option::default(),
1862            extras: Option::default(),
1863            type_: Valid(gltf_json::accessor::Type::Vec3),
1864            min: None,
1865            max: None,
1866            name: Some(accessor_name),
1867            normalized: false,
1868            sparse: None,
1869        };
1870        animation_accessors.push(vis_animation_accessor);
1871        running_buffer_view += 1;
1872        per_view_running_offset[BufferViewIDs::Animation as usize] += running_buffer_view as usize;
1873        animation_accessors
1874    }
1875    /// Function for creating accessors for morph targets
1876    fn create_morph_target_accessors(
1877        &self,
1878        vertex_count: usize,
1879        current_buffer_view_offset: u32,
1880        per_view_running_offset: &mut [usize; 6],
1881        compatibility_mode: GltfCompatibilityMode,
1882    ) -> Vec<gltf_json::Accessor> {
1883        let mut morph_target_accessors: Vec<gltf_json::Accessor> = vec![];
1884        let mut running_buffer_view = u32::try_from(per_view_running_offset[BufferViewIDs::Animation as usize]).expect("Could not convert to U32!")
1885            + current_buffer_view_offset;
1886        let num_pose_morph_targets = self.num_pose_morph_targets.saturating_sub(1);
1887        let num_expression_morph_targets = self.num_expression_morph_targets.saturating_sub(1);
1888        for morph_target_idx in 0..self.num_morph_targets() {
1889            let accessor_name = format!("morph_{morph_target_idx}_accessor");
1890            let current_morph_target = self.morph_targets.as_ref().unwrap().slice(s![morph_target_idx, .., ..]);
1891            let current_morph_target_na = current_morph_target.to_owned().clone().into_nalgebra();
1892            let (min, max) = geom::get_bounding_points(&current_morph_target_na, None);
1893            let (mut min_vec, mut max_vec): (Vec<f32>, Vec<f32>) = (min.iter().copied().collect(), max.iter().copied().collect());
1894            if compatibility_mode == GltfCompatibilityMode::Smpl && (morph_target_idx < num_pose_morph_targets) {
1895                max_vec = max_vec.iter().map(|x| x * 2.0 * PI).collect();
1896                min_vec = min_vec.iter().map(|x| x * 2.0 * PI).collect();
1897            }
1898            if compatibility_mode == GltfCompatibilityMode::Smpl
1899                && (morph_target_idx > num_pose_morph_targets && morph_target_idx < self.num_pose_morph_targets + num_expression_morph_targets)
1900            {
1901                max_vec = max_vec.iter().map(|x| x * 2.0 * 7.0).collect();
1902                min_vec = min_vec.iter().map(|x| x * 2.0 * 7.0).collect();
1903            }
1904            let morph_target_accessor = gltf_json::Accessor {
1905                buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1906                byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Deformation as usize])),
1907                count: USize64::from(vertex_count),
1908                component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1909                extensions: Option::default(),
1910                extras: Option::default(),
1911                type_: Valid(gltf_json::accessor::Type::Vec3),
1912                min: Some(gltf_json::Value::from(min_vec)),
1913                max: Some(gltf_json::Value::from(max_vec)),
1914                name: Some(accessor_name),
1915                normalized: false,
1916                sparse: None,
1917            };
1918            morph_target_accessors.push(morph_target_accessor);
1919            running_buffer_view += 1;
1920        }
1921        per_view_running_offset[BufferViewIDs::Deformation as usize] += running_buffer_view as usize;
1922        morph_target_accessors
1923    }
1924    /// Function for creating animation channels
1925    #[allow(clippy::cast_possible_truncation)]
1926    fn create_animation_channels(
1927        &self,
1928        joint_count: usize,
1929        root_idx: u32,
1930        skeleton_root_idx: usize,
1931        sampler_start_idx: usize,
1932        compatibility_mode: GltfCompatibilityMode,
1933    ) -> Vec<gltf_json::animation::Channel> {
1934        let mut animation_channels: Vec<gltf_json::animation::Channel> = vec![];
1935        let mut sampler_idx = sampler_start_idx;
1936        for j_idx in 0..joint_count {
1937            let animation_target = gltf_json::animation::Target {
1938                extensions: Option::default(),
1939                extras: Option::default(),
1940                node: gltf_json::Index::new(u32::try_from(j_idx + skeleton_root_idx).expect("Could not convert to u32!")),
1941                path: gltf_json::validation::Checked::Valid(gltf_json::animation::Property::Rotation),
1942            };
1943            let channel = gltf_json::animation::Channel {
1944                sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
1945                target: animation_target,
1946                extensions: Option::default(),
1947                extras: Option::default(),
1948            };
1949            animation_channels.push(channel);
1950            sampler_idx += 1;
1951        }
1952        let animation_target = gltf_json::animation::Target {
1953            extensions: Option::default(),
1954            extras: Option::default(),
1955            node: gltf_json::Index::new(root_idx),
1956            path: gltf_json::validation::Checked::Valid(gltf_json::animation::Property::Translation),
1957        };
1958        let channel = gltf_json::animation::Channel {
1959            sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
1960            target: animation_target,
1961            extensions: Option::default(),
1962            extras: Option::default(),
1963        };
1964        animation_channels.push(channel);
1965        sampler_idx += 1;
1966        let animation_target = gltf_json::animation::Target {
1967            extensions: Option::default(),
1968            extras: Option::default(),
1969            node: gltf_json::Index::new(root_idx),
1970            path: gltf_json::validation::Checked::Valid(gltf_json::animation::Property::Scale),
1971        };
1972        let channel = gltf_json::animation::Channel {
1973            sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
1974            target: animation_target,
1975            extensions: Option::default(),
1976            extras: Option::default(),
1977        };
1978        animation_channels.push(channel);
1979        sampler_idx += 1;
1980        let mesh_skin_binding_node_idx = root_idx - 1;
1981        if compatibility_mode == GltfCompatibilityMode::Unreal {
1982            let animation_target = gltf_json::animation::Target {
1983                extensions: Option::default(),
1984                extras: Option::default(),
1985                node: gltf_json::Index::new(u32::try_from(skeleton_root_idx).expect("Could not convert to u32!")),
1986                path: gltf_json::validation::Checked::Valid(gltf_json::animation::Property::Translation),
1987            };
1988            let channel = gltf_json::animation::Channel {
1989                sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
1990                target: animation_target,
1991                extensions: Option::default(),
1992                extras: Option::default(),
1993            };
1994            animation_channels.push(channel);
1995            sampler_idx += 1;
1996        }
1997        if self.num_morph_targets() > 0 {
1998            let mtw_animation_target = gltf_json::animation::Target {
1999                extensions: Option::default(),
2000                extras: Option::default(),
2001                node: gltf_json::Index::new(mesh_skin_binding_node_idx),
2002                path: Valid(gltf_json::animation::Property::MorphTargetWeights),
2003            };
2004            let channel = gltf_json::animation::Channel {
2005                sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
2006                target: mtw_animation_target,
2007                extensions: Option::default(),
2008                extras: Option::default(),
2009            };
2010            animation_channels.push(channel);
2011        }
2012        animation_channels
2013    }
2014    fn create_animation_channels_prop(
2015        &self,
2016        joint_count: usize,
2017        root_idx: u32,
2018        skeleton_root_idx: usize,
2019        sampler_start_idx: usize,
2020        _compatibility_mode: GltfCompatibilityMode,
2021    ) -> Vec<gltf_json::animation::Channel> {
2022        let mut animation_channels: Vec<gltf_json::animation::Channel> = vec![];
2023        let mut sampler_idx = sampler_start_idx;
2024        for j_idx in 0..joint_count {
2025            let animation_target = gltf_json::animation::Target {
2026                extensions: Option::default(),
2027                extras: Option::default(),
2028                node: gltf_json::Index::new(u32::try_from(j_idx + skeleton_root_idx).expect("Could not convert to u32!")),
2029                path: gltf_json::validation::Checked::Valid(gltf_json::animation::Property::Rotation),
2030            };
2031            let channel = gltf_json::animation::Channel {
2032                sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
2033                target: animation_target,
2034                extensions: Option::default(),
2035                extras: Option::default(),
2036            };
2037            animation_channels.push(channel);
2038            sampler_idx += 1;
2039        }
2040        let animation_target = gltf_json::animation::Target {
2041            extensions: Option::default(),
2042            extras: Option::default(),
2043            node: gltf_json::Index::new(root_idx),
2044            path: gltf_json::validation::Checked::Valid(gltf_json::animation::Property::Translation),
2045        };
2046        let channel = gltf_json::animation::Channel {
2047            sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
2048            target: animation_target,
2049            extensions: Option::default(),
2050            extras: Option::default(),
2051        };
2052        animation_channels.push(channel);
2053        sampler_idx += 1;
2054        let animation_target = gltf_json::animation::Target {
2055            extensions: Option::default(),
2056            extras: Option::default(),
2057            node: gltf_json::Index::new(root_idx),
2058            path: gltf_json::validation::Checked::Valid(gltf_json::animation::Property::Scale),
2059        };
2060        let channel = gltf_json::animation::Channel {
2061            sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
2062            target: animation_target,
2063            extensions: Option::default(),
2064            extras: Option::default(),
2065        };
2066        animation_channels.push(channel);
2067        animation_channels
2068    }
2069    /// Function for creating animation samplers
2070    fn create_animation_samplers(
2071        &self,
2072        joint_count: usize,
2073        current_buffer_view_offset: u32,
2074        compatibility_mode: GltfCompatibilityMode,
2075    ) -> Vec<gltf_json::animation::Sampler> {
2076        let mut animation_samplers: Vec<gltf_json::animation::Sampler> = vec![];
2077        let mut current_accessor = 8 + current_buffer_view_offset;
2078        for _ in 0..joint_count {
2079            let sampler = gltf_json::animation::Sampler {
2080                extensions: Option::default(),
2081                extras: Option::default(),
2082                input: gltf_json::Index::new(7 + current_buffer_view_offset),
2083                interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Linear),
2084                output: gltf_json::Index::new(current_accessor),
2085            };
2086            animation_samplers.push(sampler);
2087            current_accessor += 1;
2088        }
2089        let sampler = gltf_json::animation::Sampler {
2090            extensions: Option::default(),
2091            extras: Option::default(),
2092            input: gltf_json::Index::new(7 + current_buffer_view_offset),
2093            interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Linear),
2094            output: gltf_json::Index::new(current_accessor),
2095        };
2096        animation_samplers.push(sampler);
2097        current_accessor += 1;
2098        let sampler = gltf_json::animation::Sampler {
2099            extensions: Option::default(),
2100            extras: Option::default(),
2101            input: gltf_json::Index::new(7 + current_buffer_view_offset),
2102            interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Step),
2103            output: gltf_json::Index::new(current_accessor),
2104        };
2105        animation_samplers.push(sampler);
2106        current_accessor += 1;
2107        if compatibility_mode == GltfCompatibilityMode::Unreal {
2108            let sampler = gltf_json::animation::Sampler {
2109                extensions: Option::default(),
2110                extras: Option::default(),
2111                input: gltf_json::Index::new(7 + current_buffer_view_offset),
2112                interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Linear),
2113                output: gltf_json::Index::new(current_accessor),
2114            };
2115            animation_samplers.push(sampler);
2116            current_accessor += 1;
2117        }
2118        if self.num_morph_targets() > 0 {
2119            let sampler = gltf_json::animation::Sampler {
2120                extensions: Option::default(),
2121                extras: Option::default(),
2122                input: gltf_json::Index::new(7 + current_buffer_view_offset),
2123                interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Linear),
2124                output: gltf_json::Index::new(current_accessor),
2125            };
2126            animation_samplers.push(sampler);
2127        }
2128        animation_samplers
2129    }
2130    fn create_animation_samplers_prop(
2131        &self,
2132        joint_count: usize,
2133        current_buffer_view_offset: u32,
2134        _compatibility_mode: GltfCompatibilityMode,
2135    ) -> Vec<gltf_json::animation::Sampler> {
2136        let mut animation_samplers: Vec<gltf_json::animation::Sampler> = vec![];
2137        let mut current_accessor = current_buffer_view_offset - 3;
2138        for _ in 0..joint_count {
2139            let sampler = gltf_json::animation::Sampler {
2140                extensions: Option::default(),
2141                extras: Option::default(),
2142                input: gltf_json::Index::new(current_buffer_view_offset - 4),
2143                interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Linear),
2144                output: gltf_json::Index::new(current_accessor),
2145            };
2146            animation_samplers.push(sampler);
2147            current_accessor += 1;
2148        }
2149        let sampler = gltf_json::animation::Sampler {
2150            extensions: Option::default(),
2151            extras: Option::default(),
2152            input: gltf_json::Index::new(current_buffer_view_offset - 4),
2153            interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Linear),
2154            output: gltf_json::Index::new(current_accessor),
2155        };
2156        animation_samplers.push(sampler);
2157        current_accessor += 1;
2158        let sampler = gltf_json::animation::Sampler {
2159            extensions: Option::default(),
2160            extras: Option::default(),
2161            input: gltf_json::Index::new(current_buffer_view_offset - 4),
2162            interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Step),
2163            output: gltf_json::Index::new(current_accessor),
2164        };
2165        animation_samplers.push(sampler);
2166        animation_samplers
2167    }
2168    /// Function for creating morph targets
2169    pub fn create_morph_targets(&self, pose_dirs_accessors_start_idx: usize) -> Vec<gltf_json::mesh::MorphTarget> {
2170        let mut pose_dirs_morph_targets: Vec<gltf_json::mesh::MorphTarget> = vec![];
2171        let mut running_pose_dirs_accessor = u32::try_from(pose_dirs_accessors_start_idx).expect("Not able to convert to u32");
2172        for _ in 0..self.num_morph_targets() {
2173            let morph_target = gltf_json::mesh::MorphTarget {
2174                positions: Some(gltf_json::Index::new(running_pose_dirs_accessor)),
2175                normals: None,
2176                tangents: None,
2177            };
2178            pose_dirs_morph_targets.push(morph_target);
2179            running_pose_dirs_accessor += 1;
2180        }
2181        pose_dirs_morph_targets
2182    }
2183    /// General purpose bind matrix computation from joint transformations
2184    pub fn create_bind_matrices(&self, rot_mat: &nd::Array3<f32>, joint_trans: &nd::Array2<f32>, joint_parents: &[u32]) -> nd::Array3<f32> {
2185        assert!(
2186            rot_mat.shape()[0] == joint_trans.shape()[0],
2187            "Number of rotation matrices dont match number of translation matrices!"
2188        );
2189        let num_joints = rot_mat.shape()[0];
2190        let mut bind_matrices = ndarray::Array3::<f32>::zeros((num_joints, 4, 4));
2191        bind_matrices.slice_mut(s![0, 0..3, 0..3]).assign(&rot_mat.slice(s![0, .., ..]));
2192        bind_matrices.slice_mut(s![0, 0..3, 3]).assign(&joint_trans.slice(s![0, ..]));
2193        bind_matrices[[0, 3, 3]] = 1.0;
2194        for j_idx in 1..num_joints {
2195            let parent_index = joint_parents[j_idx] as usize;
2196            let parent_transform = bind_matrices.index_axis(nd::Axis(0), parent_index);
2197            let mut local_transform = ndarray::Array2::<f32>::zeros((4, 4));
2198            local_transform.slice_mut(s![0..3, 0..3]).assign(&rot_mat.slice(s![j_idx, .., ..]));
2199            let local_translation = Array::from_vec(vec_to_vec(&compute_local_translation(j_idx, joint_parents, joint_trans)));
2200            local_transform.slice_mut(s![0..3, 3]).assign(&local_translation);
2201            local_transform[[3, 3]] = 1.0;
2202            let global_transform = parent_transform.dot(&local_transform);
2203            bind_matrices.slice_mut(s![j_idx, .., ..]).assign(&global_transform);
2204        }
2205        bind_matrices
2206    }
2207    /// Function to create animation buffer data
2208    pub fn create_animation_data(&self, current_body: &PerBodyData, compatibility_mode: GltfCompatibilityMode) -> Vec<u8> {
2209        let mut animation_data: Vec<u8> = vec![];
2210        let keyframe_data = to_padded_byte_vector(self.keyframe_times.as_ref().unwrap());
2211        let rotation_animation_data = current_body.body_rotations.as_ref().unwrap();
2212        let mut translation_animation_data = current_body.body_translations.as_ref().unwrap().clone();
2213        let scale_animation_data = current_body.body_scales.as_ref().unwrap().clone();
2214        animation_data.extend_from_slice(keyframe_data.as_slice());
2215        assert_eq!(rotation_animation_data.shape()[1], translation_animation_data.shape()[0]);
2216        for j_idx in 0..rotation_animation_data.shape()[0] {
2217            let mut quaternions: Vec<f32> = vec![];
2218            for r_idx in 0..rotation_animation_data.shape()[1] {
2219                let rotation = rotation_animation_data.slice(s![j_idx, r_idx, ..]);
2220                let axis_angle_rotation = na::Vector3::new(rotation[0], rotation[1], rotation[2]);
2221                let mut quaternion_rotation =
2222                    na::UnitQuaternion::from_axis_angle(&na::UnitVector3::new_normalize(axis_angle_rotation), axis_angle_rotation.norm());
2223                if axis_angle_rotation.norm() == 0.0 {
2224                    quaternion_rotation = na::UnitQuaternion::default();
2225                }
2226                quaternions.extend(quaternion_rotation.as_vector().data.as_slice());
2227            }
2228            let joint_anim_data = to_padded_byte_vector(&quaternions);
2229            animation_data.append(&mut joint_anim_data.clone());
2230        }
2231        if compatibility_mode == GltfCompatibilityMode::Unreal {
2232            let mut pelvis_relative_trans = translation_animation_data.clone();
2233            for mut row in translation_animation_data.axis_iter_mut(Axis(0)) {
2234                row[1] = 0.0;
2235            }
2236            for mut row in pelvis_relative_trans.axis_iter_mut(Axis(0)) {
2237                row[0] = 0.0;
2238                row[2] = 0.0;
2239            }
2240            let trans_anim_data = to_padded_byte_vector(&translation_animation_data.to_owned().into_raw_vec());
2241            animation_data.append(&mut trans_anim_data.clone());
2242            let scale_anim_data = to_padded_byte_vector(&scale_animation_data.to_owned().into_raw_vec());
2243            animation_data.append(&mut scale_anim_data.clone());
2244            let pelvis_rel_anim_data = to_padded_byte_vector(&pelvis_relative_trans.to_owned().into_raw_vec());
2245            animation_data.append(&mut pelvis_rel_anim_data.clone());
2246        } else {
2247            let trans_anim_data = to_padded_byte_vector(&translation_animation_data.to_owned().into_raw_vec());
2248            animation_data.append(&mut trans_anim_data.clone());
2249            let scale_anim_data = to_padded_byte_vector(&scale_animation_data.to_owned().into_raw_vec());
2250            animation_data.append(&mut scale_anim_data.clone());
2251        }
2252        if self.num_morph_targets() > 0 {
2253            let morph_target_weights_data = current_body.per_frame_blend_weights.as_ref().unwrap();
2254            let weights_anim_data = to_padded_byte_vector(&morph_target_weights_data.to_owned().into_raw_vec());
2255            animation_data.append(&mut weights_anim_data.clone());
2256        }
2257        animation_data
2258    }
2259    pub fn create_animation_data_prop(&self, current_prop: &PropData, _compatibility_mode: GltfCompatibilityMode) -> Vec<u8> {
2260        let mut animation_data: Vec<u8> = vec![];
2261        let keyframe_data = to_padded_byte_vector(self.keyframe_times.as_ref().unwrap());
2262        let rotation_animation_data = current_prop.rotations.clone();
2263        let translation_animation_data = current_prop.translations.clone();
2264        let scale_animation_data = current_prop.scales.clone();
2265        animation_data.extend_from_slice(keyframe_data.as_slice());
2266        assert_eq!(rotation_animation_data.shape()[1], translation_animation_data.shape()[0]);
2267        for j_idx in 0..rotation_animation_data.shape()[0] {
2268            let mut quaternions: Vec<f32> = vec![];
2269            for r_idx in 0..rotation_animation_data.shape()[1] {
2270                let rotation = rotation_animation_data.slice(s![j_idx, r_idx, ..]);
2271                let axis_angle_rotation = na::Vector3::new(rotation[0], rotation[1], rotation[2]);
2272                let mut quaternion_rotation =
2273                    na::UnitQuaternion::from_axis_angle(&na::UnitVector3::new_normalize(axis_angle_rotation), axis_angle_rotation.norm());
2274                if axis_angle_rotation.norm() == 0.0 {
2275                    quaternion_rotation = na::UnitQuaternion::default();
2276                }
2277                quaternions.extend(quaternion_rotation.as_vector().data.as_slice());
2278            }
2279            let joint_anim_data = to_padded_byte_vector(&quaternions);
2280            animation_data.append(&mut joint_anim_data.clone());
2281        }
2282        let trans_anim_data = to_padded_byte_vector(&translation_animation_data.to_owned().into_raw_vec());
2283        animation_data.append(&mut trans_anim_data.clone());
2284        let scale_anim_data = to_padded_byte_vector(&scale_animation_data.to_owned().into_raw_vec());
2285        animation_data.append(&mut scale_anim_data.clone());
2286        animation_data
2287    }
2288    /// Function to compose all present buffer views and buffers
2289    #[allow(clippy::too_many_arguments)]
2290    fn compose_buffer_views(
2291        &self,
2292        body_idx: usize,
2293        current_body: &PerBodyData,
2294        buffer_views: Vec<gltf_json::buffer::View>,
2295        index_data: &[u8],
2296        vertex_data: &[u8],
2297        inv_bind_mat_data: &[u8],
2298        textures: &mut [GltfTextureInfo],
2299        compatibility_mode: GltfCompatibilityMode,
2300    ) -> (Vec<u8>, Vec<gltf_json::buffer::View>, usize, usize) {
2301        let mut out_data: Vec<u8> = vec![];
2302        let mut out_buffer_views: Vec<gltf_json::buffer::View> = vec![];
2303        out_data.append(&mut index_data.to_owned());
2304        out_data.append(&mut vertex_data.to_owned());
2305        out_data.append(&mut inv_bind_mat_data.to_owned());
2306        if self.is_animated() {
2307            let mut animation_data = self.create_animation_data(current_body, compatibility_mode);
2308            out_data.append(&mut animation_data);
2309            if self.num_morph_targets() > 0 && body_idx == 0 {
2310                for morph_target_idx in 0..self.num_morph_targets() {
2311                    let mut posedir = self.morph_targets.as_ref().unwrap().slice(s![morph_target_idx, .., ..]).to_owned();
2312                    let num_pose_morph_targets = self.num_pose_morph_targets.saturating_sub(1);
2313                    let num_expression_morph_targets = self.num_expression_morph_targets.saturating_sub(1);
2314                    if compatibility_mode == GltfCompatibilityMode::Smpl
2315                        && self.num_pose_morph_targets > 0
2316                        && morph_target_idx < num_pose_morph_targets
2317                    {
2318                        posedir *= 2.0 * PI;
2319                    }
2320                    if compatibility_mode == GltfCompatibilityMode::Smpl
2321                        && self.num_expression_morph_targets > 0
2322                        && morph_target_idx > num_pose_morph_targets
2323                        && morph_target_idx < self.num_pose_morph_targets + num_expression_morph_targets
2324                    {
2325                        posedir *= 2.0 * 7.0;
2326                    }
2327                    let posedir_data = to_padded_byte_vector(&posedir.to_owned().into_raw_vec());
2328                    out_data.append(&mut posedir_data.clone());
2329                }
2330            }
2331        }
2332        out_buffer_views.extend(buffer_views);
2333        let mut buffer_offset = out_data.len();
2334        let mut buffer_index: usize = out_buffer_views.len();
2335        for (sampler_index, texture) in textures.iter_mut().enumerate() {
2336            let mut buffer_view = texture.buffer_view.clone();
2337            buffer_view.byte_offset = Some(USize64::from(buffer_offset));
2338            out_buffer_views.push(buffer_view);
2339            texture.buffer_index = buffer_index;
2340            texture.image.buffer_view = Some(gltf_json::Index::new(u32::try_from(buffer_index).expect("Issue converting to u32!")));
2341            texture.texture.sampler = Some(gltf_json::Index::new(u32::try_from(sampler_index).expect("Issue converting to u32!")));
2342            out_data.append(&mut texture.image_data.clone());
2343            buffer_offset += texture.buffer_size;
2344            buffer_index += 1;
2345        }
2346        (out_data, out_buffer_views, buffer_offset, buffer_index)
2347    }
2348    /// Function to compose all present buffer views and buffers
2349    #[allow(clippy::too_many_arguments)]
2350    fn compose_buffer_views_props(
2351        &self,
2352        current_prop: &PropData,
2353        buffer_views: Vec<gltf_json::buffer::View>,
2354        index_data: &[u8],
2355        vertex_data: &[u8],
2356        inv_bind_mat_data: &[u8],
2357        textures: &mut [GltfTextureInfo],
2358        compatibility_mode: GltfCompatibilityMode,
2359        global_buffer_offset: usize,
2360        global_buffer_index: usize,
2361    ) -> (Vec<u8>, Vec<gltf_json::buffer::View>, usize, usize) {
2362        let mut out_data: Vec<u8> = vec![];
2363        let mut out_buffer_views: Vec<gltf_json::buffer::View> = vec![];
2364        out_data.append(&mut index_data.to_owned());
2365        out_data.append(&mut vertex_data.to_owned());
2366        out_data.append(&mut inv_bind_mat_data.to_owned());
2367        let mut animation_data = self.create_animation_data_prop(current_prop, compatibility_mode);
2368        out_data.append(&mut animation_data);
2369        out_buffer_views.extend(buffer_views);
2370        let mut buffer_offset = out_data.len() + global_buffer_offset;
2371        let mut buffer_index: usize = out_buffer_views.len() + global_buffer_index;
2372        for (sampler_index, texture) in textures.iter_mut().enumerate() {
2373            let mut buffer_view = texture.buffer_view.clone();
2374            buffer_view.byte_offset = Some(USize64::from(buffer_offset));
2375            out_buffer_views.push(buffer_view);
2376            texture.buffer_index = buffer_index;
2377            texture.image.buffer_view = Some(gltf_json::Index::new(u32::try_from(buffer_index).expect("Issue converting to u32!")));
2378            texture.texture.sampler = Some(gltf_json::Index::new(u32::try_from(sampler_index).expect("Issue converting to u32!")));
2379            out_data.append(&mut texture.image_data.clone());
2380            buffer_offset += texture.buffer_size;
2381            buffer_index += 1;
2382        }
2383        (out_data, out_buffer_views, buffer_offset, buffer_index)
2384    }
2385    /// Add a GLTF texture
2386    fn add_texture(&self, img: &DynImage, index: usize, name: &str) -> Option<GltfTextureInfo> {
2387        let mut image_data: Vec<u8> = vec![];
2388        let mut target = Cursor::new(&mut image_data);
2389        let image_data_buffer = img.write_to(&mut target, image::ImageFormat::Png);
2390        if image_data_buffer.is_ok() {
2391            let _ = target.flush();
2392            while image_data.len() % 4 != 0 {
2393                image_data.push(0);
2394            }
2395            let mut image_buffer_size = image_data.len();
2396            align_to_multiple_of_four(&mut image_buffer_size);
2397            let image_buffer_view = gltf_json::buffer::View {
2398                buffer: gltf_json::Index::new(0),
2399                byte_length: USize64::from(image_buffer_size),
2400                byte_offset: Some(USize64::from(0_usize)),
2401                byte_stride: Option::default(),
2402                extensions: Option::default(),
2403                extras: Option::default(),
2404                name: Some(name.to_string()),
2405                target: None,
2406            };
2407            let image = gltf_json::image::Image {
2408                buffer_view: Some(gltf_json::Index::new(u32::try_from(index).expect("Issue converting to u32!"))),
2409                mime_type: Some(gltf_json::image::MimeType("image/png".to_string())),
2410                name: Some(name.to_string()),
2411                uri: None,
2412                extensions: None,
2413                extras: None,
2414            };
2415            let texture = gltf_json::Texture {
2416                name: Some(name.to_string()),
2417                sampler: Some(gltf_json::Index::new(u32::try_from(index).expect("Issue converting to u32!"))),
2418                source: gltf_json::Index::new(u32::try_from(index).expect("Issue converting to u32!")),
2419                extensions: None,
2420                extras: None,
2421            };
2422            let sampler = gltf_json::texture::Sampler {
2423                name: Some(name.to_string()),
2424                mag_filter: Some(gltf_json::validation::Checked::Valid(gltf_json::texture::MagFilter::Linear)),
2425                min_filter: Some(gltf_json::validation::Checked::Valid(gltf_json::texture::MinFilter::Linear)),
2426                wrap_s: gltf_json::validation::Checked::Valid(gltf_json::texture::WrappingMode::ClampToEdge),
2427                wrap_t: gltf_json::validation::Checked::Valid(gltf_json::texture::WrappingMode::ClampToEdge),
2428                extensions: None,
2429                extras: None,
2430            };
2431            let texture_info = GltfTextureInfo {
2432                buffer_size: image_data.len(),
2433                image_data,
2434                image,
2435                buffer_view: image_buffer_view,
2436                buffer_index: 0,
2437                texture,
2438                sampler,
2439            };
2440            return Some(texture_info);
2441        }
2442        log!("add_texture FAILED: {}", name);
2443        None
2444    }
2445    /// Prepare normal map for GLTF
2446    #[allow(clippy::cast_sign_loss)]
2447    fn prepare_normals(&self, smpl_textures: &mut SmplTextures, texture_infos: &mut Vec<GltfTextureInfo>, normals_tex: Option<&DynImage>) {
2448        if let Some(img) = normals_tex {
2449            let normals_tex = self.add_texture(img, texture_infos.len(), "normals");
2450            if let Some(normals_tex) = normals_tex {
2451                smpl_textures.normals_index = Some(texture_infos.len());
2452                texture_infos.push(normals_tex);
2453            }
2454        }
2455    }
2456    /// Prepare metallic-roughness map for GLTF
2457    fn prepare_metallic_roughness(
2458        &self,
2459        smpl_textures: &mut SmplTextures,
2460        texture_infos: &mut Vec<GltfTextureInfo>,
2461        metalness_tex: Option<&DynImage>,
2462        roughness_tex: Option<&DynImage>,
2463    ) {
2464        let mut w: u32 = 0;
2465        let mut h: u32 = 0;
2466        if let Some(img) = metalness_tex {
2467            w = img.width();
2468            h = img.height();
2469        }
2470        if let Some(img) = roughness_tex {
2471            w = w.max(img.width());
2472            h = h.max(img.height());
2473        }
2474        let mut metalness: Option<Vec<u8>> = None;
2475        if let Some(img) = metalness_tex {
2476            if img.width() != w || img.height() != h {
2477                let resized_img = img.resize(w, h, FilterType::Gaussian);
2478                metalness = Some(resized_img.as_luma8().unwrap().to_vec());
2479            } else {
2480                metalness = Some(img.as_bytes().to_vec());
2481            }
2482        }
2483        let mut roughness: Option<Vec<u8>> = None;
2484        if let Some(img) = roughness_tex {
2485            if img.width() != w || img.height() != h {
2486                let resized_img = img.resize(w, h, FilterType::Gaussian);
2487                roughness = Some(resized_img.as_luma8().unwrap().to_vec());
2488            } else {
2489                roughness = Some(img.as_bytes().to_vec());
2490            }
2491        }
2492        let num_pixels: usize = (w * h) as usize;
2493        let mut metal_roughness_pixels: Vec<u8> = vec![];
2494        if let Some(metalness_pixels) = metalness {
2495            if let Some(roughness_pixels) = roughness {
2496                for (m, r) in metalness_pixels.iter().zip(roughness_pixels.iter()).take(num_pixels) {
2497                    metal_roughness_pixels.push(0);
2498                    metal_roughness_pixels.push(*r);
2499                    metal_roughness_pixels.push(*m);
2500                }
2501            } else {
2502                for &m in metalness_pixels.iter().take(num_pixels) {
2503                    metal_roughness_pixels.push(0);
2504                    metal_roughness_pixels.push(0);
2505                    metal_roughness_pixels.push(m);
2506                }
2507            }
2508        } else if let Some(roughness_pixels) = roughness {
2509            for &r in roughness_pixels.iter().take(num_pixels) {
2510                metal_roughness_pixels.push(0);
2511                metal_roughness_pixels.push(r);
2512                metal_roughness_pixels.push(0);
2513            }
2514        }
2515        if !metal_roughness_pixels.is_empty() {
2516            let metal_roughness_image = RgbImage::from_vec(w, h, metal_roughness_pixels);
2517            if let Some(image) = metal_roughness_image {
2518                let image = DynImage::from(image);
2519                let metallic_roughness = self.add_texture(&image, texture_infos.len(), "metal_roughness");
2520                if let Some(metallic_roughness) = metallic_roughness {
2521                    smpl_textures.metalic_roughtness_index = Some(texture_infos.len());
2522                    texture_infos.push(metallic_roughness);
2523                }
2524            }
2525        }
2526    }
2527    /// Create a new joint with the given transformations
2528    fn create_joint(&self, name: String, translation: &[f32], rotation: &Vector3f, children: Option<Vec<gltf_json::Index<Node>>>) -> Node {
2529        let cur_vec = na::Vector3::new(rotation.x, rotation.y, rotation.z);
2530        let mut cur_q = na::UnitQuaternion::from_axis_angle(&na::UnitVector3::new_normalize(cur_vec), rotation.norm());
2531        if rotation.norm() == 0.0 {
2532            cur_q = na::UnitQuaternion::default();
2533        }
2534        let translation: [f32; 3] = [translation[0], translation[1], translation[2]];
2535        let unit_quaternion = [cur_q[0], cur_q[1], cur_q[2], cur_q[3]];
2536        Node {
2537            children,
2538            mesh: None,
2539            skin: None,
2540            name: Some(name),
2541            rotation: Some(UnitQuaternion(unit_quaternion)),
2542            translation: Some(translation),
2543            ..Default::default()
2544        }
2545    }
2546    fn gather_children(&self, id: u32, parent_ids: &[u32], offset: u32) -> Option<Vec<gltf_json::Index<Node>>> {
2547        let mut children: Vec<gltf_json::Index<Node>> = vec![];
2548        for (p, &parent_id) in parent_ids.iter().enumerate() {
2549            if parent_id == id {
2550                let index = u32::try_from(p).expect("Index conversion error: usize value is too large to fit in a u32");
2551                children.push(gltf_json::Index::<Node>::new(index + offset));
2552            }
2553        }
2554        if !children.is_empty() {
2555            return Some(children);
2556        }
2557        None
2558    }
2559    /// Add a skin to GLTF
2560    #[allow(clippy::too_many_arguments)]
2561    fn add_skin(
2562        &mut self,
2563        name: String,
2564        current_body: &PerBodyData,
2565        current_armature_idx: u32,
2566        accessor_offset: u32,
2567        nodes: &mut Vec<Node>,
2568        skins: &mut Vec<gltf_json::Skin>,
2569        joints: &mut Vec<gltf_json::Index<Node>>,
2570        compatibility_mode: GltfCompatibilityMode,
2571    ) -> gltf_json::Index<Node> {
2572        let metadata = crate::common::metadata::smpl_metadata(&self.smpl_type);
2573        let joint_translations = current_body.default_joint_translations.as_ref().unwrap();
2574        let skeleton_root_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
2575        let global_translation = vec_from_array0_f(current_body.body_translation.as_ref().unwrap());
2576        let mut skeleton_root_translation = compute_local_translation(0, &metadata.joint_parents, joint_translations);
2577        if compatibility_mode == GltfCompatibilityMode::Smpl {
2578            skeleton_root_translation = addv3f(&skeleton_root_translation, &global_translation);
2579        }
2580        let mut joint_rotation = Vector3f::zeros();
2581        if let Some(joint_poses) = current_body.joint_poses.as_ref() {
2582            let rot = joint_poses.row(0).to_owned();
2583            joint_rotation = Vector3f::new(rot[0], rot[1], rot[2]);
2584        }
2585        let skeleton_root = self.create_joint(
2586            "pelvis".to_string(),
2587            vec_to_vec(&skeleton_root_translation).as_slice(),
2588            &joint_rotation,
2589            self.gather_children(0, &metadata.joint_parents, skeleton_root_index),
2590        );
2591        nodes.push(skeleton_root);
2592        joints.push(gltf_json::Index::new(skeleton_root_index));
2593        let joint_names = if compatibility_mode == GltfCompatibilityMode::Unreal {
2594            smpl_x::JOINT_NAMES_UNREAL.map(std::string::ToString::to_string).to_vec()
2595        } else {
2596            metadata.joint_names
2597        };
2598        for (j, name) in joint_names.iter().enumerate().take(metadata.num_joints + 1).skip(1) {
2599            if let Some(joint_poses) = current_body.joint_poses.as_ref() {
2600                let rot = joint_poses.row(j).to_owned();
2601                joint_rotation = Vector3f::new(rot[0], rot[1], rot[2]);
2602            }
2603            let joint = self.create_joint(
2604                name.clone(),
2605                vec_to_vec(&compute_local_translation(j, &metadata.joint_parents, joint_translations)).as_slice(),
2606                &joint_rotation,
2607                self.gather_children(
2608                    u32::try_from(j).expect("Issue converting Joint idx to u32"),
2609                    &metadata.joint_parents,
2610                    skeleton_root_index,
2611                ),
2612            );
2613            let joint_index = gltf_json::Index::<Node>::new(u32::try_from(nodes.len()).expect("Issue converting Joint idx to u32"));
2614            nodes.push(joint);
2615            joints.push(joint_index);
2616        }
2617        let skin = gltf_json::Skin {
2618            name: Some(name),
2619            inverse_bind_matrices: Some(gltf_json::Index::new(accessor_offset + 6)),
2620            joints: joints.clone(),
2621            skeleton: Some(gltf_json::Index::new(current_armature_idx)),
2622            extensions: None,
2623            extras: None,
2624        };
2625        skins.push(skin);
2626        gltf_json::Index::<Node>::new(skeleton_root_index)
2627    }
2628    #[allow(clippy::too_many_arguments)]
2629    fn add_skin_prop(
2630        &mut self,
2631        name: String,
2632        current_prop: &PropData,
2633        current_armature_idx: u32,
2634        accessor_offset: u32,
2635        nodes: &mut Vec<Node>,
2636        skins: &mut Vec<gltf_json::Skin>,
2637        joints: &mut Vec<gltf_json::Index<Node>>,
2638        _compatibility_mode: GltfCompatibilityMode,
2639    ) -> gltf_json::Index<Node> {
2640        let joint_translations = &current_prop.default_translation;
2641        let skeleton_root_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
2642        let skeleton_root_translation = vec_from_vec(&joint_translations.row(0).to_vec());
2643        let joint_rotation = Vector3f::zeros();
2644        let skeleton_root = self.create_joint(
2645            "root_prop".to_string(),
2646            vec_to_vec(&skeleton_root_translation).as_slice(),
2647            &joint_rotation,
2648            None,
2649        );
2650        nodes.push(skeleton_root);
2651        joints.push(gltf_json::Index::new(skeleton_root_index));
2652        let skin = gltf_json::Skin {
2653            name: Some(name),
2654            inverse_bind_matrices: Some(gltf_json::Index::new(accessor_offset - 5)),
2655            joints: joints.clone(),
2656            skeleton: Some(gltf_json::Index::new(current_armature_idx)),
2657            extensions: None,
2658            extras: None,
2659        };
2660        skins.push(skin);
2661        gltf_json::Index::<Node>::new(skeleton_root_index)
2662    }
2663    /// Create camera animation buffer views
2664    #[allow(clippy::cast_precision_loss)]
2665    fn create_camera_animation_buffer_views(
2666        &self,
2667        running_offset: &mut usize,
2668        compatibility_mode: GltfCompatibilityMode,
2669    ) -> (Vec<gltf_json::buffer::View>, Vec<u8>) {
2670        let mut buffer_views = Vec::new();
2671        let mut buffer_data = Vec::new();
2672        if let Some(smpl_camera) = &self.smpl_camera {
2673            let trans_data = to_padded_byte_vector(smpl_camera.transform_sequence.translations.as_slice().unwrap());
2674            let trans_len = trans_data.len();
2675            let trans_view = gltf_json::buffer::View {
2676                buffer: gltf_json::Index::new(0),
2677                byte_length: USize64::from(trans_len),
2678                byte_offset: Some(USize64::from(*running_offset)),
2679                byte_stride: None,
2680                extensions: None,
2681                extras: Option::default(),
2682                name: Some("camera_translations".to_string()),
2683                target: None,
2684            };
2685            buffer_data.extend(trans_data);
2686            *running_offset += trans_len;
2687            buffer_views.push(trans_view);
2688            let rotations = smpl_camera.transform_sequence.get_rotations_as_quaternions();
2689            let rotated_rots = if compatibility_mode == GltfCompatibilityMode::Unreal {
2690                let angle = 90.0;
2691                let axis = [0.0, 1.0, 0.0];
2692                self.rotate_camera_quaternions(angle, axis).as_ref().unwrap().clone()
2693            } else {
2694                rotations.clone()
2695            };
2696            let rot_data = to_padded_byte_vector(rotated_rots.as_slice().unwrap());
2697            let rot_len = rot_data.len();
2698            let rot_view = gltf_json::buffer::View {
2699                buffer: gltf_json::Index::new(0),
2700                byte_length: USize64::from(rot_len),
2701                byte_offset: Some(USize64::from(*running_offset)),
2702                byte_stride: None,
2703                extensions: None,
2704                extras: Option::default(),
2705                name: Some("camera_rotations".to_string()),
2706                target: None,
2707            };
2708            buffer_data.extend(rot_data);
2709            *running_offset += rot_len;
2710            buffer_views.push(rot_view);
2711        }
2712        (buffer_views, buffer_data)
2713    }
2714    /// Create camera animation accessors
2715    fn create_camera_animation_accessors(&self, current_buffer_view_offset: u32) -> Vec<gltf_json::Accessor> {
2716        let mut accessors = Vec::new();
2717        let mut current_view = current_buffer_view_offset;
2718        if let Some(smpl_camera) = &self.smpl_camera {
2719            let translations = smpl_camera.transform_sequence.translations.clone();
2720            accessors.push(gltf_json::Accessor {
2721                buffer_view: Some(gltf_json::Index::new(current_view)),
2722                byte_offset: None,
2723                component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
2724                count: USize64::from(translations.shape()[0]),
2725                type_: Valid(gltf_json::accessor::Type::Vec3),
2726                min: None,
2727                max: None,
2728                name: Some("camera_translations".to_string()),
2729                normalized: false,
2730                sparse: None,
2731                extensions: None,
2732                extras: Option::default(),
2733            });
2734            current_view += 1;
2735            let rotations = smpl_camera.transform_sequence.get_rotations_as_quaternions();
2736            accessors.push(gltf_json::Accessor {
2737                buffer_view: Some(gltf_json::Index::new(current_view)),
2738                byte_offset: None,
2739                component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
2740                count: USize64::from(rotations.shape()[0]),
2741                type_: Valid(gltf_json::accessor::Type::Vec4),
2742                min: None,
2743                max: None,
2744                name: Some("camera_rotations".to_string()),
2745                normalized: false,
2746                sparse: None,
2747                extensions: None,
2748                extras: Option::default(),
2749            });
2750        }
2751        accessors
2752    }
2753    /// Create camera animation channels and samplers
2754    #[allow(clippy::cast_possible_truncation)]
2755    fn create_camera_animation_channels_and_samplers(
2756        &self,
2757        current_accessor_offset: u32,
2758        camera_node_index: u32,
2759        sampler_start_idx: u32,
2760    ) -> (Vec<gltf_json::animation::Channel>, Vec<gltf_json::animation::Sampler>) {
2761        let mut channels = Vec::new();
2762        let mut samplers = Vec::new();
2763        let mut current_accessor = current_accessor_offset;
2764        let times_accessor_index = 7;
2765        if self.smpl_camera.is_some() {
2766            samplers.push(gltf_json::animation::Sampler {
2767                input: gltf_json::Index::new(times_accessor_index),
2768                interpolation: Valid(gltf_json::animation::Interpolation::Linear),
2769                output: gltf_json::Index::new(current_accessor),
2770                extensions: None,
2771                extras: Option::default(),
2772            });
2773            channels.push(gltf_json::animation::Channel {
2774                sampler: gltf_json::Index::new(sampler_start_idx + samplers.len() as u32 - 1),
2775                target: gltf_json::animation::Target {
2776                    node: gltf_json::Index::new(camera_node_index),
2777                    path: Valid(gltf_json::animation::Property::Translation),
2778                    extensions: None,
2779                    extras: Option::default(),
2780                },
2781                extensions: None,
2782                extras: Option::default(),
2783            });
2784            current_accessor += 1;
2785            samplers.push(gltf_json::animation::Sampler {
2786                input: gltf_json::Index::new(times_accessor_index),
2787                interpolation: Valid(gltf_json::animation::Interpolation::Linear),
2788                output: gltf_json::Index::new(current_accessor),
2789                extensions: None,
2790                extras: Option::default(),
2791            });
2792            channels.push(gltf_json::animation::Channel {
2793                sampler: gltf_json::Index::new(sampler_start_idx + samplers.len() as u32 - 1),
2794                target: gltf_json::animation::Target {
2795                    node: gltf_json::Index::new(camera_node_index),
2796                    path: Valid(gltf_json::animation::Property::Rotation),
2797                    extensions: None,
2798                    extras: Option::default(),
2799                },
2800                extensions: None,
2801                extras: Option::default(),
2802            });
2803        }
2804        (channels, samplers)
2805    }
2806    fn num_morph_targets(&self) -> usize {
2807        self.morph_targets.as_ref().map_or(0, |x| x.shape()[0])
2808    }
2809    /// Rotate camera rotation quaternions by a given angle and axis in local (camera) space.
2810    fn rotate_camera_quaternions(&self, angle_degrees: f32, axis: [f32; 3]) -> Option<nd::Array2<f32>> {
2811        if let Some(smpl_camera) = &self.smpl_camera {
2812            let rotations = smpl_camera.transform_sequence.get_rotations_as_quaternions();
2813            let mut rotated_quaternions = rotations.clone();
2814            let axis_vec = na::Vector3::new(axis[0], axis[1], axis[2]);
2815            let rotation_quat = na::UnitQuaternion::from_axis_angle(&na::UnitVector3::new_normalize(axis_vec), angle_degrees.to_radians());
2816            for mut row in rotated_quaternions.axis_iter_mut(nd::Axis(0)) {
2817                let q = na::Quaternion::new(row[3], row[0], row[1], row[2]);
2818                let unit_q = na::UnitQuaternion::from_quaternion(q);
2819                let result = unit_q * rotation_quat;
2820                row[0] = result.i;
2821                row[1] = result.j;
2822                row[2] = result.k;
2823                row[3] = result.w;
2824            }
2825            return Some(rotated_quaternions);
2826        }
2827        None
2828    }
2829}
2830pub fn compute_local_translation(id: usize, parent_ids: &[u32], joint_translations: &nd::Array2<f32>) -> Vector3f {
2831    let trans = vec_from_vec(&joint_translations.row(id).to_vec());
2832    if id == 0 {
2833        return trans;
2834    }
2835    let parent_id = parent_ids[id] as usize;
2836    let parent_trans = vec_from_vec(&joint_translations.row(parent_id).to_vec());
2837    subv3f(&trans, &parent_trans)
2838}