smpl_core/codec/
gltf.rs

1use super::scene::CameraTrack;
2use crate::{
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(Debug, 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 camera_track: Option<CameraTrack>,
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            camera_track: 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:?}");
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:?}");
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(camera_track) = &self.camera_track {
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: camera_track.yfov,
310                    znear: camera_track.znear,
311                    zfar: camera_track.zfar,
312                    aspect_ratio: camera_track.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 camera_track_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(camera_track_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.camera_track.is_some() {
765            let (cam_track_buffer_views, cam_track_buffer_data) = self
766                .create_camera_animation_buffer_views(&mut full_buffer_data.len(), compatibility_mode)
767                .unwrap();
768            let cam_track_accessors = self.create_camera_animation_accessors(buffer_views.len() as u32).unwrap();
769            let (cam_track_channels, cam_track_samplers) = self
770                .create_camera_animation_channels_and_samplers(accessors.len() as u32, 1, samplers.len() as u32)
771                .unwrap();
772            buffer_views.extend(cam_track_buffer_views);
773            full_buffer_data.extend(cam_track_buffer_data);
774            accessors.extend(cam_track_accessors);
775            channels.extend(cam_track_channels);
776            samplers.extend(cam_track_samplers);
777        }
778        for prop_idx in 0..self.props.len() {
779            let prop = self.props[prop_idx].clone();
780            let prop_mesh_index = self.per_body_data.len() + prop_idx;
781            info!("Writing prop {prop_idx} to GLTF");
782            let mut inverse_bind_matrices: Vec<f32> = vec![];
783            let joint_rotations = batch_rodrigues(&prop.default_joint_poses);
784            let joint_translations = prop.default_translation.clone();
785            let joint_count = 1;
786            let parents = [0];
787            let bind_matrices = self.create_bind_matrices(&joint_rotations, &joint_translations, &parents);
788            for j_idx in 0..joint_count {
789                let mut inverse_bind_matrix = nd::Array2::<f32>::zeros((4, 4));
790                let inverse_rotation_matrix: ArrayBase<ndarray::ViewRepr<&f32>, Dim<[usize; 2]>> =
791                    bind_matrices.slice(s![j_idx, 0..3, 0..3]).reversed_axes();
792                let translation: nd::Array1<f32> = bind_matrices.slice(s![j_idx, 0..3, 3]).to_owned();
793                let inverse_translation = -inverse_rotation_matrix.dot(&translation);
794                inverse_bind_matrix.slice_mut(s![0..3, 0..3]).assign(&inverse_rotation_matrix);
795                inverse_bind_matrix.slice_mut(s![0..3, 3]).assign(&inverse_translation);
796                inverse_bind_matrix[(3, 3)] = 1.0;
797                inverse_bind_matrices.extend(inverse_bind_matrix.t().iter());
798            }
799            let has_uv = prop.uvs.is_some();
800            println!("has uv: {has_uv}");
801            let vertex_data = if let Some(uvs) = prop.uvs.as_ref() {
802                println!("uv size: {:?}", uvs.shape());
803                println!("prop.positions size: {:?}", prop.positions.shape());
804                let mut vertex_attributes_array: Vec<VertexPropWithUV> = vec![];
805                for (position, uv) in izip!(prop.positions.row_iter(), uvs.row_iter()) {
806                    vertex_attributes_array.push(VertexPropWithUV {
807                        position: Vector3f::new(position[0], position[1], position[2]).into(),
808                        uv: Vector2f::new(uv[0], 1.0 - uv[1]).into(),
809                        joint_index: Vector4s::new(0, 0, 0, 0).into(),
810                        joint_weight: Vector4f::new(1.0, 0.0, 0.0, 0.0).into(),
811                    });
812                }
813                to_padded_byte_vector(&vertex_attributes_array)
814            } else {
815                println!("no uv found");
816                let mut vertex_attributes_array: Vec<VertexProp> = vec![];
817                for position in izip!(prop.positions.row_iter(),) {
818                    vertex_attributes_array.push(VertexProp {
819                        position: Vector3f::new(position[0], position[1], position[2]).into(),
820                        joint_index: Vector4s::new(0, 0, 0, 0).into(),
821                        joint_weight: Vector4f::new(1.0, 0.0, 0.0, 0.0).into(),
822                    });
823                }
824                to_padded_byte_vector(&vertex_attributes_array)
825            };
826            let mut indices_array: Vec<u32> = vec![];
827            for row in prop.faces.row_iter() {
828                indices_array.extend_from_slice(&[row[0], row[1], row[2]]);
829            }
830            let index_data = to_padded_byte_vector(&indices_array);
831            let inv_bind_mat_data = to_padded_byte_vector(&inverse_bind_matrices);
832            let has_diffuse = prop.diffuse_texture.is_some();
833            let mut texture_infos: Vec<GltfTextureInfo> = vec![];
834            let mut diffuse_prop_texture_id = None;
835            let tex_offset = self.per_body_data.len() * 3;
836            if let Some(img) = prop.diffuse_texture.as_ref() {
837                let diffuse_tex = self.add_texture(img, texture_infos.len() + tex_offset, "diffuse_prop");
838                if let Some(diffuse_tex) = diffuse_tex {
839                    diffuse_prop_texture_id = Some(texture_infos.len());
840                    texture_infos.push(diffuse_tex);
841                }
842            }
843            diffuse_prop_texture_id = diffuse_prop_texture_id.map(|id| id + tex_offset);
844            let mut base_color_texture: Option<gltf_json::texture::Info> = None;
845            if let Some(diffuse_texture_index) = diffuse_prop_texture_id {
846                base_color_texture = Some(gltf_json::texture::Info {
847                    index: gltf_json::Index::new(u32::try_from(diffuse_texture_index).expect("Could not convert to u32!")),
848                    tex_coord: 0,
849                    extensions: None,
850                    extras: None,
851                });
852            }
853            let material = gltf_json::Material {
854                alpha_cutoff: None,
855                alpha_mode: gltf_json::validation::Checked::<AlphaMode>::Valid(AlphaMode::Opaque),
856                double_sided: false,
857                name: Some("prop_material".to_string()),
858                pbr_metallic_roughness: gltf_json::material::PbrMetallicRoughness {
859                    base_color_factor: gltf_json::material::PbrBaseColorFactor([1., 1., 1., 1.]),
860                    base_color_texture,
861                    ..Default::default()
862                },
863                normal_texture: None,
864                occlusion_texture: None,
865                emissive_texture: None,
866                emissive_factor: gltf_json::material::EmissiveFactor([0., 0., 0.]),
867                extensions: None,
868                extras: None,
869            };
870            materials.push(material);
871            let primitive_offset = accessors.len() as u32;
872            let materials_selected = if has_diffuse {
873                Some(gltf_json::Index::new(prop_mesh_index as u32))
874            } else {
875                None
876            };
877            let primitive = gltf_json::mesh::Primitive {
878                #[allow(unused_assignments)]
879                attributes: {
880                    let mut map = std::collections::BTreeMap::new();
881                    let mut attr_idx = 1;
882                    map.insert(
883                        Valid(gltf_json::mesh::Semantic::Positions),
884                        gltf_json::Index::new(attr_idx + primitive_offset),
885                    );
886                    attr_idx += 1;
887                    if has_uv {
888                        map.insert(
889                            Valid(gltf_json::mesh::Semantic::TexCoords(0)),
890                            gltf_json::Index::new(attr_idx + primitive_offset),
891                        );
892                        attr_idx += 1;
893                    }
894                    map.insert(
895                        Valid(gltf_json::mesh::Semantic::Joints(0)),
896                        gltf_json::Index::new(attr_idx + primitive_offset),
897                    );
898                    attr_idx += 1;
899                    map.insert(
900                        Valid(gltf_json::mesh::Semantic::Weights(0)),
901                        gltf_json::Index::new(attr_idx + primitive_offset),
902                    );
903                    attr_idx += 1;
904                    map
905                },
906                extensions: Option::default(),
907                extras: Option::default(),
908                indices: Some(gltf_json::Index::new(PrimitiveAttrIDs::Indices as u32 + primitive_offset)),
909                material: materials_selected,
910                mode: Valid(gltf_json::mesh::Mode::Triangles),
911                targets: None,
912            };
913            let mesh = gltf_json::Mesh {
914                extensions: Option::default(),
915                extras: Option::default(),
916                name: Some(format!("prop_{prop_idx}")),
917                primitives: vec![primitive],
918                weights: None,
919            };
920            meshes.push(mesh);
921            let vertex_count = prop.positions.shape().0;
922            let face_count = prop.faces.shape().0;
923            let current_buffer_view_offset = buffer_views.len() as u32;
924            let mut per_view_running_offset: [usize; 6] = [0, 0, 0, 0, 0, 0];
925            let accessor = self.create_accessors_props(
926                prop_idx,
927                vertex_count,
928                face_count,
929                current_buffer_view_offset,
930                &mut per_view_running_offset,
931                compatibility_mode,
932                has_uv,
933            );
934            accessors.extend(accessor);
935            let mut current_buffer_views = vec![];
936            self.create_buffer_views_props(
937                prop_idx as u32,
938                full_buffer_data.len(),
939                vertex_count,
940                face_count,
941                &mut current_buffer_views,
942                compatibility_mode,
943                has_uv,
944            );
945            let (buffer_data, composed_buffer_views, _buffer_offset, _buffer_index) = self.compose_buffer_views_props(
946                &self.props[prop_idx],
947                current_buffer_views.clone(),
948                index_data.as_slice(),
949                vertex_data.as_slice(),
950                inv_bind_mat_data.as_slice(),
951                &mut texture_infos,
952                compatibility_mode,
953                global_buffer_offset,
954                global_buffer_index,
955            );
956            full_buffer_data.extend(buffer_data);
957            buffer_views.extend(composed_buffer_views);
958            for texture in texture_infos {
959                images.push(texture.image);
960                textures.push(texture.texture);
961                texture_samplers.push(texture.sampler);
962            }
963            let prop_node_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
964            let armature_node = Node {
965                name: Some(format!("Armature_Prop_{prop_idx}")),
966                children: Some(vec![]),
967                ..Default::default()
968            };
969            nodes.push(armature_node);
970            if let Some(ref mut scene_root_node_children) = nodes[0].children {
971                scene_root_node_children.push(gltf_json::Index::new(prop_node_index));
972            }
973            let mesh_skin_binding_node_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
974            let mesh_skin_binding_node = Node {
975                mesh: Some(gltf_json::Index::new(prop_mesh_index as u32)),
976                skin: Some(gltf_json::Index::new(prop_mesh_index as u32)),
977                name: Some(format!("MeshSkinBinding_{prop_idx}")),
978                children: None,
979                ..Default::default()
980            };
981            nodes.push(mesh_skin_binding_node);
982            let root_node_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
983            let mut joints = vec![];
984            let skeleton_root_index = self.add_skin_prop(
985                format!("Skin_prop_{prop_idx}"),
986                &prop,
987                prop_node_index,
988                accessors.len().try_into().unwrap(),
989                &mut nodes,
990                &mut skins,
991                &mut joints,
992                compatibility_mode,
993            );
994            if let Some(ref mut armature_children) = nodes[prop_node_index as usize].children {
995                armature_children.push(gltf_json::Index::new(mesh_skin_binding_node_index));
996                armature_children.push(gltf_json::Index::new(root_node_index));
997            }
998            let animation_channels = self.create_animation_channels_prop(
999                joint_count,
1000                root_node_index,
1001                skeleton_root_index.value(),
1002                samplers.len(),
1003                compatibility_mode,
1004            );
1005            let animation_samplers = self.create_animation_samplers_prop(joint_count, accessors.len().try_into().unwrap(), compatibility_mode);
1006            channels.extend(animation_channels);
1007            samplers.extend(animation_samplers);
1008        }
1009        let buffer = gltf_json::Buffer {
1010            byte_length: USize64::from(full_buffer_data.len()),
1011            extensions: Option::default(),
1012            extras: Option::default(),
1013            name: Some("scene_buffer".to_string()),
1014            uri: if binary { None } else { Some("buffer0.bin".into()) },
1015        };
1016        buffers.push(buffer);
1017        let mut animations: Vec<gltf_json::Animation> = vec![];
1018        if self.is_animated() {
1019            let animation = gltf_json::Animation {
1020                extensions: Option::default(),
1021                extras: Option::default(),
1022                channels,
1023                name: Some("Scene_animation".to_string()),
1024                samplers,
1025            };
1026            animations.push(animation);
1027        }
1028        let root = gltf_json::Root {
1029            accessors,
1030            animations,
1031            buffers,
1032            buffer_views,
1033            cameras,
1034            images,
1035            materials,
1036            meshes,
1037            nodes,
1038            samplers: texture_samplers,
1039            scenes,
1040            skins,
1041            textures,
1042            ..Default::default()
1043        };
1044        (full_buffer_data, root)
1045    }
1046    /// Function for creating buffer view definitions
1047    #[allow(clippy::too_many_arguments)]
1048    fn create_buffer_views(
1049        &self,
1050        body_idx: u32,
1051        mut running_offset: usize,
1052        vertex_count: usize,
1053        face_count: usize,
1054        joint_count: usize,
1055        num_extra_joints: usize,
1056        buffer_views: &mut Vec<gltf_json::buffer::View>,
1057        compatibility_mode: GltfCompatibilityMode,
1058    ) {
1059        let index_buffer_size = face_count * 3 * mem::size_of::<u32>();
1060        let index_buffer_view = gltf_json::buffer::View {
1061            buffer: gltf_json::Index::new(0),
1062            byte_length: USize64::from(index_buffer_size),
1063            byte_offset: Some(USize64::from(running_offset)),
1064            byte_stride: None,
1065            extensions: Option::default(),
1066            extras: Option::default(),
1067            name: Some(format!("index_buffer_view_{body_idx}")),
1068            target: Some(Valid(gltf_json::buffer::Target::ElementArrayBuffer)),
1069        };
1070        buffer_views.push(index_buffer_view);
1071        running_offset += index_buffer_size;
1072        let vertex_buffer_size = vertex_count * mem::size_of::<Vertex>();
1073        let vertex_buffer_view = gltf_json::buffer::View {
1074            buffer: gltf_json::Index::new(0),
1075            byte_length: USize64::from(vertex_buffer_size),
1076            byte_offset: Some(USize64::from(running_offset)),
1077            byte_stride: Some(gltf_json::buffer::Stride(mem::size_of::<Vertex>())),
1078            extensions: Option::default(),
1079            extras: Option::default(),
1080            name: Some(format!("vertex_buffer_view_{body_idx}")),
1081            target: Some(Valid(gltf_json::buffer::Target::ArrayBuffer)),
1082        };
1083        buffer_views.push(vertex_buffer_view);
1084        running_offset += vertex_buffer_size;
1085        let inv_bind_matrix_buffer_size = (joint_count + num_extra_joints) * 16 * mem::size_of::<f32>();
1086        let inverse_bind_mat_buffer_view = gltf_json::buffer::View {
1087            buffer: gltf_json::Index::new(0),
1088            byte_length: USize64::from(inv_bind_matrix_buffer_size),
1089            byte_offset: Some(USize64::from(running_offset)),
1090            byte_stride: None,
1091            extensions: Option::default(),
1092            extras: Option::default(),
1093            name: Some(format!("inv_bind_matrix_buffer_view_{body_idx}")),
1094            target: None,
1095        };
1096        buffer_views.push(inverse_bind_mat_buffer_view);
1097        running_offset += inv_bind_matrix_buffer_size;
1098        if self.is_animated() {
1099            let rotation_animation_buffer_size = self.frame_count.unwrap() * 4 * mem::size_of::<f32>();
1100            let translation_animation_buffer_size = self.frame_count.unwrap() * 3 * mem::size_of::<f32>();
1101            let scale_animation_buffer_size = self.frame_count.unwrap() * 3 * mem::size_of::<f32>();
1102            let animation_buffer_views = self.create_animation_buffer_views(
1103                body_idx,
1104                joint_count,
1105                rotation_animation_buffer_size,
1106                translation_animation_buffer_size,
1107                scale_animation_buffer_size,
1108                &mut running_offset,
1109                compatibility_mode,
1110            );
1111            buffer_views.extend(animation_buffer_views);
1112            if self.num_morph_targets() > 0 && body_idx == 0 {
1113                let morph_target_buffer_size = vertex_count * 3 * mem::size_of::<f32>();
1114                let morph_target_buffer_views = self.create_morph_target_buffer_views(morph_target_buffer_size, &mut running_offset);
1115                buffer_views.extend(morph_target_buffer_views);
1116            }
1117        }
1118    }
1119    /// Function for creating buffer view definitions
1120    #[allow(clippy::too_many_arguments)]
1121    fn create_buffer_views_props(
1122        &self,
1123        prop_idx: u32,
1124        mut running_offset: usize,
1125        vertex_count: usize,
1126        face_count: usize,
1127        buffer_views: &mut Vec<gltf_json::buffer::View>,
1128        compatibility_mode: GltfCompatibilityMode,
1129        with_uv: bool,
1130    ) {
1131        let joint_count = 1;
1132        let index_buffer_size = face_count * 3 * mem::size_of::<u32>();
1133        let index_buffer_view = gltf_json::buffer::View {
1134            buffer: gltf_json::Index::new(0),
1135            byte_length: USize64::from(index_buffer_size),
1136            byte_offset: Some(USize64::from(running_offset)),
1137            byte_stride: None,
1138            extensions: Option::default(),
1139            extras: Option::default(),
1140            name: Some(format!("index_buffer_view_prop_{prop_idx}")),
1141            target: Some(Valid(gltf_json::buffer::Target::ElementArrayBuffer)),
1142        };
1143        buffer_views.push(index_buffer_view);
1144        running_offset += index_buffer_size;
1145        let vertex_size = if with_uv {
1146            mem::size_of::<VertexPropWithUV>()
1147        } else {
1148            mem::size_of::<VertexProp>()
1149        };
1150        let vertex_buffer_size = vertex_count * vertex_size;
1151        let vertex_buffer_view = gltf_json::buffer::View {
1152            buffer: gltf_json::Index::new(0),
1153            byte_length: USize64::from(vertex_buffer_size),
1154            byte_offset: Some(USize64::from(running_offset)),
1155            byte_stride: Some(gltf_json::buffer::Stride(vertex_size)),
1156            extensions: Option::default(),
1157            extras: Option::default(),
1158            name: Some(format!("vertex_buffer_view_prop_{prop_idx}")),
1159            target: Some(Valid(gltf_json::buffer::Target::ArrayBuffer)),
1160        };
1161        buffer_views.push(vertex_buffer_view);
1162        running_offset += vertex_buffer_size;
1163        let inv_bind_matrix_buffer_size = (joint_count) * 16 * mem::size_of::<f32>();
1164        let inverse_bind_mat_buffer_view = gltf_json::buffer::View {
1165            buffer: gltf_json::Index::new(0),
1166            byte_length: USize64::from(inv_bind_matrix_buffer_size),
1167            byte_offset: Some(USize64::from(running_offset)),
1168            byte_stride: None,
1169            extensions: Option::default(),
1170            extras: Option::default(),
1171            name: Some(format!("inv_bind_matrix_buffer_view_{prop_idx}")),
1172            target: None,
1173        };
1174        buffer_views.push(inverse_bind_mat_buffer_view);
1175        running_offset += inv_bind_matrix_buffer_size;
1176        let rotation_animation_buffer_size = self.frame_count.unwrap() * 4 * mem::size_of::<f32>();
1177        let translation_animation_buffer_size = self.frame_count.unwrap() * 3 * mem::size_of::<f32>();
1178        let scale_animation_buffer_size = self.frame_count.unwrap() * 3 * mem::size_of::<f32>();
1179        let animation_buffer_views = self.create_animation_buffer_views_prop(
1180            prop_idx,
1181            joint_count,
1182            rotation_animation_buffer_size,
1183            translation_animation_buffer_size,
1184            scale_animation_buffer_size,
1185            &mut running_offset,
1186            compatibility_mode,
1187        );
1188        buffer_views.extend(animation_buffer_views);
1189    }
1190    /// Function for creating animation based buffer views
1191    #[allow(clippy::too_many_arguments)]
1192    fn create_animation_buffer_views(
1193        &self,
1194        body_idx: u32,
1195        joint_count: usize,
1196        rotation_buffer_size: usize,
1197        translation_buffer_size: usize,
1198        scale_buffer_size: usize,
1199        running_offset: &mut usize,
1200        compatibility_mode: GltfCompatibilityMode,
1201    ) -> Vec<gltf_json::buffer::View> {
1202        let mut animation_buffer_views: Vec<gltf_json::buffer::View> = vec![];
1203        let keyframe_buffer_size = self.frame_count.unwrap() * mem::size_of::<f32>();
1204        let keyframe_buffer_view = gltf_json::buffer::View {
1205            buffer: gltf_json::Index::new(0),
1206            byte_length: USize64::from(keyframe_buffer_size),
1207            byte_offset: Some(USize64::from(*running_offset)),
1208            byte_stride: None,
1209            extensions: Option::default(),
1210            extras: Option::default(),
1211            name: Some(format!("keyframe_buffer_view_{body_idx}")),
1212            target: None,
1213        };
1214        animation_buffer_views.push(keyframe_buffer_view);
1215        *running_offset += keyframe_buffer_size;
1216        for j_idx in 0..joint_count {
1217            let buffer_view_name = format!("joint_{j_idx}_animations_buffer_view_{body_idx}");
1218            let animation_buffer_view = gltf_json::buffer::View {
1219                buffer: gltf_json::Index::new(0),
1220                byte_length: USize64::from(rotation_buffer_size),
1221                byte_offset: Some(USize64::from(*running_offset)),
1222                byte_stride: None,
1223                extensions: Option::default(),
1224                extras: Option::default(),
1225                name: Some(buffer_view_name),
1226                target: None,
1227            };
1228            animation_buffer_views.push(animation_buffer_view);
1229            *running_offset += rotation_buffer_size;
1230        }
1231        let buffer_view_name = format!("root_translation_animations_buffer_view_{body_idx}");
1232        let animation_buffer_view = gltf_json::buffer::View {
1233            buffer: gltf_json::Index::new(0),
1234            byte_length: USize64::from(translation_buffer_size),
1235            byte_offset: Some(USize64::from(*running_offset)),
1236            byte_stride: None,
1237            extensions: Option::default(),
1238            extras: Option::default(),
1239            name: Some(buffer_view_name),
1240            target: None,
1241        };
1242        animation_buffer_views.push(animation_buffer_view);
1243        *running_offset += translation_buffer_size;
1244        let buffer_view_name = format!("root_scale_animations_buffer_view_{body_idx}");
1245        let animation_buffer_view = gltf_json::buffer::View {
1246            buffer: gltf_json::Index::new(0),
1247            byte_length: USize64::from(scale_buffer_size),
1248            byte_offset: Some(USize64::from(*running_offset)),
1249            byte_stride: None,
1250            extensions: Option::default(),
1251            extras: Option::default(),
1252            name: Some(buffer_view_name),
1253            target: None,
1254        };
1255        animation_buffer_views.push(animation_buffer_view);
1256        *running_offset += scale_buffer_size;
1257        if compatibility_mode == GltfCompatibilityMode::Unreal {
1258            let buffer_view_name = format!("pelvis_rel_translation_animations_buffer_view_{body_idx}");
1259            let animation_buffer_view = gltf_json::buffer::View {
1260                buffer: gltf_json::Index::new(0),
1261                byte_length: USize64::from(translation_buffer_size),
1262                byte_offset: Some(USize64::from(*running_offset)),
1263                byte_stride: None,
1264                extensions: Option::default(),
1265                extras: Option::default(),
1266                name: Some(buffer_view_name),
1267                target: None,
1268            };
1269            animation_buffer_views.push(animation_buffer_view);
1270            *running_offset += translation_buffer_size;
1271        }
1272        if self.num_morph_targets() > 0 {
1273            let morph_weights_buffer_size = self.frame_count.unwrap() * self.num_morph_targets() * mem::size_of::<f32>();
1274            let buffer_view_name = format!("morph_target_weights_{body_idx}");
1275            let morph_weights_buffer_view = gltf_json::buffer::View {
1276                buffer: gltf_json::Index::new(0),
1277                byte_length: USize64::from(morph_weights_buffer_size),
1278                byte_offset: Some(USize64::from(*running_offset)),
1279                byte_stride: None,
1280                extensions: Option::default(),
1281                extras: Option::default(),
1282                name: Some(buffer_view_name),
1283                target: None,
1284            };
1285            animation_buffer_views.push(morph_weights_buffer_view);
1286            *running_offset += morph_weights_buffer_size;
1287        }
1288        animation_buffer_views
1289    }
1290    #[allow(clippy::too_many_arguments)]
1291    fn create_animation_buffer_views_prop(
1292        &self,
1293        prop_idx: u32,
1294        joint_count: usize,
1295        rotation_buffer_size: usize,
1296        translation_buffer_size: usize,
1297        scale_buffer_size: usize,
1298        running_offset: &mut usize,
1299        _compatibility_mode: GltfCompatibilityMode,
1300    ) -> Vec<gltf_json::buffer::View> {
1301        let mut animation_buffer_views: Vec<gltf_json::buffer::View> = vec![];
1302        let keyframe_buffer_size = self.frame_count.unwrap() * mem::size_of::<f32>();
1303        let keyframe_buffer_view = gltf_json::buffer::View {
1304            buffer: gltf_json::Index::new(0),
1305            byte_length: USize64::from(keyframe_buffer_size),
1306            byte_offset: Some(USize64::from(*running_offset)),
1307            byte_stride: None,
1308            extensions: Option::default(),
1309            extras: Option::default(),
1310            name: Some(format!("keyframe_buffer_view_prop_{prop_idx}")),
1311            target: None,
1312        };
1313        animation_buffer_views.push(keyframe_buffer_view);
1314        *running_offset += keyframe_buffer_size;
1315        for j_idx in 0..joint_count {
1316            let buffer_view_name = format!("joint_{j_idx}_animations_buffer_view_prop_{prop_idx}");
1317            let animation_buffer_view = gltf_json::buffer::View {
1318                buffer: gltf_json::Index::new(0),
1319                byte_length: USize64::from(rotation_buffer_size),
1320                byte_offset: Some(USize64::from(*running_offset)),
1321                byte_stride: None,
1322                extensions: Option::default(),
1323                extras: Option::default(),
1324                name: Some(buffer_view_name),
1325                target: None,
1326            };
1327            animation_buffer_views.push(animation_buffer_view);
1328            *running_offset += rotation_buffer_size;
1329        }
1330        let buffer_view_name = format!("root_translation_animations_buffer_view_prop_{prop_idx}");
1331        let animation_buffer_view = gltf_json::buffer::View {
1332            buffer: gltf_json::Index::new(0),
1333            byte_length: USize64::from(translation_buffer_size),
1334            byte_offset: Some(USize64::from(*running_offset)),
1335            byte_stride: None,
1336            extensions: Option::default(),
1337            extras: Option::default(),
1338            name: Some(buffer_view_name),
1339            target: None,
1340        };
1341        animation_buffer_views.push(animation_buffer_view);
1342        *running_offset += translation_buffer_size;
1343        let buffer_view_name = format!("root_scale_animations_buffer_view_prop_{prop_idx}");
1344        let animation_buffer_view = gltf_json::buffer::View {
1345            buffer: gltf_json::Index::new(0),
1346            byte_length: USize64::from(scale_buffer_size),
1347            byte_offset: Some(USize64::from(*running_offset)),
1348            byte_stride: None,
1349            extensions: Option::default(),
1350            extras: Option::default(),
1351            name: Some(buffer_view_name),
1352            target: None,
1353        };
1354        animation_buffer_views.push(animation_buffer_view);
1355        *running_offset += scale_buffer_size;
1356        animation_buffer_views
1357    }
1358    /// Function for creating buffer views for morph targets
1359    fn create_morph_target_buffer_views(&self, morph_target_buffer_size: usize, running_offset: &mut usize) -> Vec<gltf_json::buffer::View> {
1360        let mut morph_targets_buffer_views: Vec<gltf_json::buffer::View> = vec![];
1361        for morph_target_idx in 0..self.num_morph_targets() {
1362            let buffer_view_name = format!("morph_{morph_target_idx}_buffer_view");
1363            let morph_target_buffer_view = gltf_json::buffer::View {
1364                buffer: gltf_json::Index::new(0),
1365                byte_length: USize64::from(morph_target_buffer_size),
1366                byte_offset: Some(USize64::from(*running_offset)),
1367                byte_stride: None,
1368                extensions: Option::default(),
1369                extras: Option::default(),
1370                name: Some(buffer_view_name),
1371                target: Some(Valid(gltf_json::buffer::Target::ArrayBuffer)),
1372            };
1373            morph_targets_buffer_views.push(morph_target_buffer_view);
1374            *running_offset += morph_target_buffer_size;
1375        }
1376        morph_targets_buffer_views
1377    }
1378    /// Function fo creating all the GLTF accessors
1379    #[allow(clippy::too_many_lines)]
1380    #[allow(clippy::too_many_arguments)]
1381    fn create_accessors(
1382        &self,
1383        body_idx: usize,
1384        vertex_count: usize,
1385        face_count: usize,
1386        joint_count: usize,
1387        current_buffer_view_offset: u32,
1388        per_view_running_offset: &mut [usize; 6],
1389        num_extra_joints: usize,
1390        compatibility_mode: GltfCompatibilityMode,
1391    ) -> Vec<gltf_json::Accessor> {
1392        let (min, max) = geom::get_bounding_points(self.per_body_data[body_idx].positions.as_ref().unwrap(), None);
1393        let (min_vec, max_vec): (Vec<f32>, Vec<f32>) = (min.iter().copied().collect(), max.iter().copied().collect());
1394        let mut accessors: Vec<gltf_json::Accessor> = vec![];
1395        let indices = gltf_json::Accessor {
1396            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::Index as u32 + current_buffer_view_offset)),
1397            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Index as usize])),
1398            count: USize64::from(face_count * 3),
1399            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::U32)),
1400            extensions: Option::default(),
1401            extras: Option::default(),
1402            type_: Valid(gltf_json::accessor::Type::Scalar),
1403            min: Some(gltf_json::Value::from(Vec::from([self.faces.as_ref().unwrap().min()]))),
1404            max: Some(gltf_json::Value::from(Vec::from([self.faces.as_ref().unwrap().max()]))),
1405            name: Some(format!("index_accessor_{body_idx}")),
1406            normalized: false,
1407            sparse: None,
1408        };
1409        accessors.push(indices);
1410        let position_element_size = 3 * mem::size_of::<f32>();
1411        let positions = gltf_json::Accessor {
1412            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
1413            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
1414            count: USize64::from(vertex_count),
1415            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1416            extensions: Option::default(),
1417            extras: Option::default(),
1418            type_: Valid(gltf_json::accessor::Type::Vec3),
1419            min: Some(gltf_json::Value::from(min_vec)),
1420            max: Some(gltf_json::Value::from(max_vec)),
1421            name: Some(format!("position_accessor_{body_idx}")),
1422            normalized: false,
1423            sparse: None,
1424        };
1425        per_view_running_offset[BufferViewIDs::VertexAttr as usize] += position_element_size;
1426        accessors.push(positions);
1427        let normal_element_size = 3 * mem::size_of::<f32>();
1428        let normals = gltf_json::Accessor {
1429            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
1430            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
1431            count: USize64::from(vertex_count),
1432            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1433            extensions: Option::default(),
1434            extras: Option::default(),
1435            type_: Valid(gltf_json::accessor::Type::Vec3),
1436            min: None,
1437            max: None,
1438            name: Some(format!("normal_accessor_{body_idx}")),
1439            normalized: false,
1440            sparse: None,
1441        };
1442        per_view_running_offset[BufferViewIDs::VertexAttr as usize] += normal_element_size;
1443        accessors.push(normals);
1444        let uv_element_size = 2 * mem::size_of::<f32>();
1445        let uvs = gltf_json::Accessor {
1446            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
1447            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
1448            count: USize64::from(vertex_count),
1449            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1450            extensions: Option::default(),
1451            extras: Option::default(),
1452            type_: Valid(gltf_json::accessor::Type::Vec2),
1453            min: None,
1454            max: None,
1455            name: Some("uv_accessor".to_string()),
1456            normalized: false,
1457            sparse: None,
1458        };
1459        per_view_running_offset[BufferViewIDs::VertexAttr as usize] += uv_element_size;
1460        accessors.push(uvs);
1461        let joint_index_element_size = 4 * mem::size_of::<u16>();
1462        let joint_indices = gltf_json::Accessor {
1463            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
1464            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
1465            count: USize64::from(vertex_count),
1466            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::U16)),
1467            extensions: Option::default(),
1468            extras: Option::default(),
1469            type_: Valid(gltf_json::accessor::Type::Vec4),
1470            min: None,
1471            max: None,
1472            name: Some("joint_index_accessor".to_string()),
1473            normalized: false,
1474            sparse: None,
1475        };
1476        per_view_running_offset[BufferViewIDs::VertexAttr as usize] += joint_index_element_size;
1477        accessors.push(joint_indices);
1478        let joint_weight_element_size = 4 * mem::size_of::<f32>();
1479        let joint_weights = gltf_json::Accessor {
1480            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
1481            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
1482            count: USize64::from(vertex_count),
1483            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1484            extensions: Option::default(),
1485            extras: Option::default(),
1486            type_: Valid(gltf_json::accessor::Type::Vec4),
1487            min: None,
1488            max: None,
1489            name: Some("joint_index_accessor".to_string()),
1490            normalized: false,
1491            sparse: None,
1492        };
1493        per_view_running_offset[BufferViewIDs::VertexAttr as usize] += joint_weight_element_size;
1494        accessors.push(joint_weights);
1495        let inv_bind_matrices = gltf_json::Accessor {
1496            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::InvBindMat as u32 + current_buffer_view_offset)),
1497            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::InvBindMat as usize])),
1498            count: USize64::from(joint_count + num_extra_joints),
1499            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1500            extensions: Option::default(),
1501            extras: Option::default(),
1502            type_: Valid(gltf_json::accessor::Type::Mat4),
1503            min: None,
1504            max: None,
1505            name: Some("inv_bind_matrices_accessor".to_string()),
1506            normalized: false,
1507            sparse: None,
1508        };
1509        accessors.push(inv_bind_matrices);
1510        if self.is_animated() {
1511            let animation_accessors =
1512                self.create_animation_accessors(joint_count, current_buffer_view_offset, per_view_running_offset, compatibility_mode);
1513            accessors.extend(animation_accessors);
1514            if self.num_morph_targets() > 0 && body_idx == 0 {
1515                let morph_target_accessors =
1516                    self.create_morph_target_accessors(vertex_count, current_buffer_view_offset, per_view_running_offset, compatibility_mode);
1517                accessors.extend(morph_target_accessors);
1518            }
1519        }
1520        accessors
1521    }
1522    /// Function fo creating all the GLTF accessors for props
1523    #[allow(clippy::too_many_lines)]
1524    #[allow(clippy::too_many_arguments)]
1525    fn create_accessors_props(
1526        &self,
1527        prop_idx: usize,
1528        vertex_count: usize,
1529        face_count: usize,
1530        current_buffer_view_offset: u32,
1531        per_view_running_offset: &mut [usize; 6],
1532        compatibility_mode: GltfCompatibilityMode,
1533        with_uv: bool,
1534    ) -> Vec<gltf_json::Accessor> {
1535        let joint_count = 1;
1536        let (min, max) = geom::get_bounding_points(&self.props[prop_idx].positions, None);
1537        let (min_vec, max_vec): (Vec<f32>, Vec<f32>) = (min.iter().copied().collect(), max.iter().copied().collect());
1538        let mut accessors: Vec<gltf_json::Accessor> = vec![];
1539        let indices = gltf_json::Accessor {
1540            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::Index as u32 + current_buffer_view_offset)),
1541            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Index as usize])),
1542            count: USize64::from(face_count * 3),
1543            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::U32)),
1544            extensions: Option::default(),
1545            extras: Option::default(),
1546            type_: Valid(gltf_json::accessor::Type::Scalar),
1547            min: Some(gltf_json::Value::from(Vec::from([self.props[prop_idx].faces.min()]))),
1548            max: Some(gltf_json::Value::from(Vec::from([self.props[prop_idx].faces.max()]))),
1549            name: Some(format!("index_accessor_prop_{prop_idx}")),
1550            normalized: false,
1551            sparse: None,
1552        };
1553        accessors.push(indices);
1554        let position_element_size = 3 * mem::size_of::<f32>();
1555        let positions = gltf_json::Accessor {
1556            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
1557            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
1558            count: USize64::from(vertex_count),
1559            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1560            extensions: Option::default(),
1561            extras: Option::default(),
1562            type_: Valid(gltf_json::accessor::Type::Vec3),
1563            min: Some(gltf_json::Value::from(min_vec)),
1564            max: Some(gltf_json::Value::from(max_vec)),
1565            name: Some(format!("position_accessor_prop_{prop_idx}")),
1566            normalized: false,
1567            sparse: None,
1568        };
1569        per_view_running_offset[BufferViewIDs::VertexAttr as usize] += position_element_size;
1570        accessors.push(positions);
1571        if with_uv {
1572            let uv_element_size = 2 * mem::size_of::<f32>();
1573            let uvs = gltf_json::Accessor {
1574                buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
1575                byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
1576                count: USize64::from(vertex_count),
1577                component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1578                extensions: Option::default(),
1579                extras: Option::default(),
1580                type_: Valid(gltf_json::accessor::Type::Vec2),
1581                min: None,
1582                max: None,
1583                name: Some(format!("uv_accessor_prop_{prop_idx}")),
1584                normalized: false,
1585                sparse: None,
1586            };
1587            per_view_running_offset[BufferViewIDs::VertexAttr as usize] += uv_element_size;
1588            accessors.push(uvs);
1589        }
1590        let joint_index_element_size = 4 * mem::size_of::<u16>();
1591        let joint_indices = gltf_json::Accessor {
1592            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
1593            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
1594            count: USize64::from(vertex_count),
1595            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::U16)),
1596            extensions: Option::default(),
1597            extras: Option::default(),
1598            type_: Valid(gltf_json::accessor::Type::Vec4),
1599            min: None,
1600            max: None,
1601            name: Some("joint_index_accessor_prop".to_string()),
1602            normalized: false,
1603            sparse: None,
1604        };
1605        per_view_running_offset[BufferViewIDs::VertexAttr as usize] += joint_index_element_size;
1606        accessors.push(joint_indices);
1607        let joint_weight_element_size = 4 * mem::size_of::<f32>();
1608        let joint_weights = gltf_json::Accessor {
1609            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::VertexAttr as u32 + current_buffer_view_offset)),
1610            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::VertexAttr as usize])),
1611            count: USize64::from(vertex_count),
1612            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1613            extensions: Option::default(),
1614            extras: Option::default(),
1615            type_: Valid(gltf_json::accessor::Type::Vec4),
1616            min: None,
1617            max: None,
1618            name: Some("joint_weight_accessor_prop".to_string()),
1619            normalized: false,
1620            sparse: None,
1621        };
1622        per_view_running_offset[BufferViewIDs::VertexAttr as usize] += joint_weight_element_size;
1623        accessors.push(joint_weights);
1624        let inv_bind_matrices = gltf_json::Accessor {
1625            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::InvBindMat as u32 + current_buffer_view_offset)),
1626            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::InvBindMat as usize])),
1627            count: USize64::from(joint_count),
1628            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1629            extensions: Option::default(),
1630            extras: Option::default(),
1631            type_: Valid(gltf_json::accessor::Type::Mat4),
1632            min: None,
1633            max: None,
1634            name: Some("inv_bind_matrices_accessor_prop".to_string()),
1635            normalized: false,
1636            sparse: None,
1637        };
1638        accessors.push(inv_bind_matrices);
1639        let animation_accessors =
1640            self.create_animation_accessors_props(joint_count, current_buffer_view_offset, per_view_running_offset, compatibility_mode);
1641        accessors.extend(animation_accessors);
1642        accessors
1643    }
1644    /// Function for creating the animation accessors
1645    #[allow(clippy::too_many_lines)]
1646    fn create_animation_accessors(
1647        &self,
1648        joint_count: usize,
1649        current_buffer_view_offset: u32,
1650        per_view_running_offset: &mut [usize; 6],
1651        compatibility_mode: GltfCompatibilityMode,
1652    ) -> Vec<gltf_json::Accessor> {
1653        let mut animation_accessors: Vec<gltf_json::Accessor> = vec![];
1654        let min_keyframe = self
1655            .keyframe_times
1656            .as_ref()
1657            .expect("keyframe_times should exist")
1658            .iter()
1659            .copied()
1660            .min_by(|a, b| a.partial_cmp(b).expect("Tried to compare a NaN"))
1661            .expect("keyframe_times should have elements in the vector");
1662        let max_keyframe = self
1663            .keyframe_times
1664            .as_ref()
1665            .unwrap()
1666            .iter()
1667            .copied()
1668            .max_by(|a, b| a.partial_cmp(b).expect("Tried to compare a NaN"))
1669            .unwrap();
1670        let keyframe_times = gltf_json::Accessor {
1671            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::Keyframe as u32 + current_buffer_view_offset)),
1672            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Keyframe as usize])),
1673            count: USize64::from(self.frame_count.unwrap()),
1674            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1675            extensions: Option::default(),
1676            extras: Option::default(),
1677            type_: Valid(gltf_json::accessor::Type::Scalar),
1678            min: Some(gltf_json::Value::from(Vec::from([min_keyframe]))),
1679            max: Some(gltf_json::Value::from(Vec::from([max_keyframe]))),
1680            name: Some("keyframes_accessor".to_string()),
1681            normalized: false,
1682            sparse: None,
1683        };
1684        animation_accessors.push(keyframe_times);
1685        let mut running_buffer_view = BufferViewIDs::Animation as u32 + current_buffer_view_offset;
1686        for j_idx in 0..joint_count {
1687            let accessor_name = format!("joint_{j_idx}_animations_accessor");
1688            let joint_animation_accessor = gltf_json::Accessor {
1689                buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1690                byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1691                count: USize64::from(self.frame_count.unwrap()),
1692                component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1693                extensions: Option::default(),
1694                extras: Option::default(),
1695                type_: Valid(gltf_json::accessor::Type::Vec4),
1696                min: None,
1697                max: None,
1698                name: Some(accessor_name),
1699                normalized: false,
1700                sparse: None,
1701            };
1702            animation_accessors.push(joint_animation_accessor);
1703            running_buffer_view += 1;
1704        }
1705        let accessor_name = "root_translation_animations_accessor".to_string();
1706        let body_animation_accessor = gltf_json::Accessor {
1707            buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1708            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1709            count: USize64::from(self.frame_count.unwrap()),
1710            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1711            extensions: Option::default(),
1712            extras: Option::default(),
1713            type_: Valid(gltf_json::accessor::Type::Vec3),
1714            min: None,
1715            max: None,
1716            name: Some(accessor_name),
1717            normalized: false,
1718            sparse: None,
1719        };
1720        animation_accessors.push(body_animation_accessor);
1721        running_buffer_view += 1;
1722        let accessor_name = "root_scale_animations_accessor".to_string();
1723        let vis_animation_accessor = gltf_json::Accessor {
1724            buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1725            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1726            count: USize64::from(self.frame_count.unwrap()),
1727            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1728            extensions: Option::default(),
1729            extras: Option::default(),
1730            type_: Valid(gltf_json::accessor::Type::Vec3),
1731            min: None,
1732            max: None,
1733            name: Some(accessor_name),
1734            normalized: false,
1735            sparse: None,
1736        };
1737        animation_accessors.push(vis_animation_accessor);
1738        running_buffer_view += 1;
1739        if compatibility_mode == GltfCompatibilityMode::Unreal {
1740            let accessor_name = "pelvis_rel_translation_animations_accessor".to_string();
1741            let pelvis_animation_accessor = gltf_json::Accessor {
1742                buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1743                byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1744                count: USize64::from(self.frame_count.unwrap()),
1745                component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1746                extensions: Option::default(),
1747                extras: Option::default(),
1748                type_: Valid(gltf_json::accessor::Type::Vec3),
1749                min: None,
1750                max: None,
1751                name: Some(accessor_name),
1752                normalized: false,
1753                sparse: None,
1754            };
1755            animation_accessors.push(pelvis_animation_accessor);
1756            running_buffer_view += 1;
1757        }
1758        if self.num_morph_targets() > 0 {
1759            let accessor_name = "morph_targets_weights_accessor".to_string();
1760            let morph_targets_weights_accessor = gltf_json::Accessor {
1761                buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1762                byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1763                count: USize64::from(self.frame_count.unwrap() * self.num_morph_targets()),
1764                component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1765                extensions: Option::default(),
1766                extras: Option::default(),
1767                type_: Valid(gltf_json::accessor::Type::Scalar),
1768                min: None,
1769                max: None,
1770                name: Some(accessor_name),
1771                normalized: false,
1772                sparse: None,
1773            };
1774            animation_accessors.push(morph_targets_weights_accessor);
1775            running_buffer_view += 1;
1776        }
1777        per_view_running_offset[BufferViewIDs::Animation as usize] += running_buffer_view as usize;
1778        animation_accessors
1779    }
1780    #[allow(clippy::too_many_lines)]
1781    fn create_animation_accessors_props(
1782        &self,
1783        joint_count: usize,
1784        current_buffer_view_offset: u32,
1785        per_view_running_offset: &mut [usize; 6],
1786        _compatibility_mode: GltfCompatibilityMode,
1787    ) -> Vec<gltf_json::Accessor> {
1788        let mut animation_accessors: Vec<gltf_json::Accessor> = vec![];
1789        let min_keyframe = self
1790            .keyframe_times
1791            .as_ref()
1792            .expect("keyframe_times should exist")
1793            .iter()
1794            .copied()
1795            .min_by(|a, b| a.partial_cmp(b).expect("Tried to compare a NaN"))
1796            .expect("keyframe_times should have elements in the vector");
1797        let max_keyframe = self
1798            .keyframe_times
1799            .as_ref()
1800            .unwrap()
1801            .iter()
1802            .copied()
1803            .max_by(|a, b| a.partial_cmp(b).expect("Tried to compare a NaN"))
1804            .unwrap();
1805        let keyframe_times = gltf_json::Accessor {
1806            buffer_view: Some(gltf_json::Index::new(BufferViewIDs::Keyframe as u32 + current_buffer_view_offset)),
1807            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Keyframe as usize])),
1808            count: USize64::from(self.frame_count.unwrap()),
1809            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1810            extensions: Option::default(),
1811            extras: Option::default(),
1812            type_: Valid(gltf_json::accessor::Type::Scalar),
1813            min: Some(gltf_json::Value::from(Vec::from([min_keyframe]))),
1814            max: Some(gltf_json::Value::from(Vec::from([max_keyframe]))),
1815            name: Some("keyframes_accessor_prop".to_string()),
1816            normalized: false,
1817            sparse: None,
1818        };
1819        animation_accessors.push(keyframe_times);
1820        let mut running_buffer_view = BufferViewIDs::Animation as u32 + current_buffer_view_offset;
1821        for j_idx in 0..joint_count {
1822            let accessor_name = format!("joint_{j_idx}_animations_accessor_prop");
1823            let joint_animation_accessor = gltf_json::Accessor {
1824                buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1825                byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1826                count: USize64::from(self.frame_count.unwrap()),
1827                component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1828                extensions: Option::default(),
1829                extras: Option::default(),
1830                type_: Valid(gltf_json::accessor::Type::Vec4),
1831                min: None,
1832                max: None,
1833                name: Some(accessor_name),
1834                normalized: false,
1835                sparse: None,
1836            };
1837            animation_accessors.push(joint_animation_accessor);
1838            running_buffer_view += 1;
1839        }
1840        let accessor_name = "root_translation_animations_accessor_prop".to_string();
1841        let body_animation_accessor = gltf_json::Accessor {
1842            buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1843            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1844            count: USize64::from(self.frame_count.unwrap()),
1845            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1846            extensions: Option::default(),
1847            extras: Option::default(),
1848            type_: Valid(gltf_json::accessor::Type::Vec3),
1849            min: None,
1850            max: None,
1851            name: Some(accessor_name),
1852            normalized: false,
1853            sparse: None,
1854        };
1855        animation_accessors.push(body_animation_accessor);
1856        running_buffer_view += 1;
1857        let accessor_name = "root_scale_animations_accessor_prop".to_string();
1858        let vis_animation_accessor = gltf_json::Accessor {
1859            buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1860            byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Animation as usize])),
1861            count: USize64::from(self.frame_count.unwrap()),
1862            component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1863            extensions: Option::default(),
1864            extras: Option::default(),
1865            type_: Valid(gltf_json::accessor::Type::Vec3),
1866            min: None,
1867            max: None,
1868            name: Some(accessor_name),
1869            normalized: false,
1870            sparse: None,
1871        };
1872        animation_accessors.push(vis_animation_accessor);
1873        running_buffer_view += 1;
1874        per_view_running_offset[BufferViewIDs::Animation as usize] += running_buffer_view as usize;
1875        animation_accessors
1876    }
1877    /// Function for creating accessors for morph targets
1878    fn create_morph_target_accessors(
1879        &self,
1880        vertex_count: usize,
1881        current_buffer_view_offset: u32,
1882        per_view_running_offset: &mut [usize; 6],
1883        compatibility_mode: GltfCompatibilityMode,
1884    ) -> Vec<gltf_json::Accessor> {
1885        let mut morph_target_accessors: Vec<gltf_json::Accessor> = vec![];
1886        let mut running_buffer_view = u32::try_from(per_view_running_offset[BufferViewIDs::Animation as usize]).expect("Could not convert to U32!")
1887            + current_buffer_view_offset;
1888        let num_pose_morph_targets = self.num_pose_morph_targets.saturating_sub(1);
1889        let num_expression_morph_targets = self.num_expression_morph_targets.saturating_sub(1);
1890        for morph_target_idx in 0..self.num_morph_targets() {
1891            let accessor_name = format!("morph_{morph_target_idx}_accessor");
1892            let current_morph_target = self.morph_targets.as_ref().unwrap().slice(s![morph_target_idx, .., ..]);
1893            let current_morph_target_na = current_morph_target.to_owned().clone().into_nalgebra();
1894            let (min, max) = geom::get_bounding_points(&current_morph_target_na, None);
1895            let (mut min_vec, mut max_vec): (Vec<f32>, Vec<f32>) = (min.iter().copied().collect(), max.iter().copied().collect());
1896            if compatibility_mode == GltfCompatibilityMode::Smpl && (morph_target_idx < num_pose_morph_targets) {
1897                max_vec = max_vec.iter().map(|x| x * 2.0 * PI).collect();
1898                min_vec = min_vec.iter().map(|x| x * 2.0 * PI).collect();
1899            }
1900            if compatibility_mode == GltfCompatibilityMode::Smpl
1901                && (morph_target_idx > num_pose_morph_targets && morph_target_idx < self.num_pose_morph_targets + num_expression_morph_targets)
1902            {
1903                max_vec = max_vec.iter().map(|x| x * 2.0 * 7.0).collect();
1904                min_vec = min_vec.iter().map(|x| x * 2.0 * 7.0).collect();
1905            }
1906            let morph_target_accessor = gltf_json::Accessor {
1907                buffer_view: Some(gltf_json::Index::new(running_buffer_view)),
1908                byte_offset: Some(USize64::from(per_view_running_offset[BufferViewIDs::Deformation as usize])),
1909                count: USize64::from(vertex_count),
1910                component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
1911                extensions: Option::default(),
1912                extras: Option::default(),
1913                type_: Valid(gltf_json::accessor::Type::Vec3),
1914                min: Some(gltf_json::Value::from(min_vec)),
1915                max: Some(gltf_json::Value::from(max_vec)),
1916                name: Some(accessor_name),
1917                normalized: false,
1918                sparse: None,
1919            };
1920            morph_target_accessors.push(morph_target_accessor);
1921            running_buffer_view += 1;
1922        }
1923        per_view_running_offset[BufferViewIDs::Deformation as usize] += running_buffer_view as usize;
1924        morph_target_accessors
1925    }
1926    /// Function for creating animation channels
1927    #[allow(clippy::cast_possible_truncation)]
1928    fn create_animation_channels(
1929        &self,
1930        joint_count: usize,
1931        root_idx: u32,
1932        skeleton_root_idx: usize,
1933        sampler_start_idx: usize,
1934        compatibility_mode: GltfCompatibilityMode,
1935    ) -> Vec<gltf_json::animation::Channel> {
1936        let mut animation_channels: Vec<gltf_json::animation::Channel> = vec![];
1937        let mut sampler_idx = sampler_start_idx;
1938        for j_idx in 0..joint_count {
1939            let animation_target = gltf_json::animation::Target {
1940                extensions: Option::default(),
1941                extras: Option::default(),
1942                node: gltf_json::Index::new(u32::try_from(j_idx + skeleton_root_idx).expect("Could not convert to u32!")),
1943                path: gltf_json::validation::Checked::Valid(gltf_json::animation::Property::Rotation),
1944            };
1945            let channel = gltf_json::animation::Channel {
1946                sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
1947                target: animation_target,
1948                extensions: Option::default(),
1949                extras: Option::default(),
1950            };
1951            animation_channels.push(channel);
1952            sampler_idx += 1;
1953        }
1954        let animation_target = gltf_json::animation::Target {
1955            extensions: Option::default(),
1956            extras: Option::default(),
1957            node: gltf_json::Index::new(root_idx),
1958            path: gltf_json::validation::Checked::Valid(gltf_json::animation::Property::Translation),
1959        };
1960        let channel = gltf_json::animation::Channel {
1961            sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
1962            target: animation_target,
1963            extensions: Option::default(),
1964            extras: Option::default(),
1965        };
1966        animation_channels.push(channel);
1967        sampler_idx += 1;
1968        let animation_target = gltf_json::animation::Target {
1969            extensions: Option::default(),
1970            extras: Option::default(),
1971            node: gltf_json::Index::new(root_idx),
1972            path: gltf_json::validation::Checked::Valid(gltf_json::animation::Property::Scale),
1973        };
1974        let channel = gltf_json::animation::Channel {
1975            sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
1976            target: animation_target,
1977            extensions: Option::default(),
1978            extras: Option::default(),
1979        };
1980        animation_channels.push(channel);
1981        sampler_idx += 1;
1982        let mesh_skin_binding_node_idx = root_idx - 1;
1983        if compatibility_mode == GltfCompatibilityMode::Unreal {
1984            let animation_target = gltf_json::animation::Target {
1985                extensions: Option::default(),
1986                extras: Option::default(),
1987                node: gltf_json::Index::new(u32::try_from(skeleton_root_idx).expect("Could not convert to u32!")),
1988                path: gltf_json::validation::Checked::Valid(gltf_json::animation::Property::Translation),
1989            };
1990            let channel = gltf_json::animation::Channel {
1991                sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
1992                target: animation_target,
1993                extensions: Option::default(),
1994                extras: Option::default(),
1995            };
1996            animation_channels.push(channel);
1997            sampler_idx += 1;
1998        }
1999        if self.num_morph_targets() > 0 {
2000            let mtw_animation_target = gltf_json::animation::Target {
2001                extensions: Option::default(),
2002                extras: Option::default(),
2003                node: gltf_json::Index::new(mesh_skin_binding_node_idx),
2004                path: Valid(gltf_json::animation::Property::MorphTargetWeights),
2005            };
2006            let channel = gltf_json::animation::Channel {
2007                sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
2008                target: mtw_animation_target,
2009                extensions: Option::default(),
2010                extras: Option::default(),
2011            };
2012            animation_channels.push(channel);
2013        }
2014        animation_channels
2015    }
2016    fn create_animation_channels_prop(
2017        &self,
2018        joint_count: usize,
2019        root_idx: u32,
2020        skeleton_root_idx: usize,
2021        sampler_start_idx: usize,
2022        _compatibility_mode: GltfCompatibilityMode,
2023    ) -> Vec<gltf_json::animation::Channel> {
2024        let mut animation_channels: Vec<gltf_json::animation::Channel> = vec![];
2025        let mut sampler_idx = sampler_start_idx;
2026        for j_idx in 0..joint_count {
2027            let animation_target = gltf_json::animation::Target {
2028                extensions: Option::default(),
2029                extras: Option::default(),
2030                node: gltf_json::Index::new(u32::try_from(j_idx + skeleton_root_idx).expect("Could not convert to u32!")),
2031                path: gltf_json::validation::Checked::Valid(gltf_json::animation::Property::Rotation),
2032            };
2033            let channel = gltf_json::animation::Channel {
2034                sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
2035                target: animation_target,
2036                extensions: Option::default(),
2037                extras: Option::default(),
2038            };
2039            animation_channels.push(channel);
2040            sampler_idx += 1;
2041        }
2042        let animation_target = gltf_json::animation::Target {
2043            extensions: Option::default(),
2044            extras: Option::default(),
2045            node: gltf_json::Index::new(root_idx),
2046            path: gltf_json::validation::Checked::Valid(gltf_json::animation::Property::Translation),
2047        };
2048        let channel = gltf_json::animation::Channel {
2049            sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
2050            target: animation_target,
2051            extensions: Option::default(),
2052            extras: Option::default(),
2053        };
2054        animation_channels.push(channel);
2055        sampler_idx += 1;
2056        let animation_target = gltf_json::animation::Target {
2057            extensions: Option::default(),
2058            extras: Option::default(),
2059            node: gltf_json::Index::new(root_idx),
2060            path: gltf_json::validation::Checked::Valid(gltf_json::animation::Property::Scale),
2061        };
2062        let channel = gltf_json::animation::Channel {
2063            sampler: gltf_json::Index::new(u32::try_from(sampler_idx).expect("Could not convert to u32!")),
2064            target: animation_target,
2065            extensions: Option::default(),
2066            extras: Option::default(),
2067        };
2068        animation_channels.push(channel);
2069        animation_channels
2070    }
2071    /// Function for creating animation samplers
2072    fn create_animation_samplers(
2073        &self,
2074        joint_count: usize,
2075        current_buffer_view_offset: u32,
2076        compatibility_mode: GltfCompatibilityMode,
2077    ) -> Vec<gltf_json::animation::Sampler> {
2078        let mut animation_samplers: Vec<gltf_json::animation::Sampler> = vec![];
2079        let mut current_accessor = 8 + current_buffer_view_offset;
2080        for _ in 0..joint_count {
2081            let sampler = gltf_json::animation::Sampler {
2082                extensions: Option::default(),
2083                extras: Option::default(),
2084                input: gltf_json::Index::new(7 + current_buffer_view_offset),
2085                interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Linear),
2086                output: gltf_json::Index::new(current_accessor),
2087            };
2088            animation_samplers.push(sampler);
2089            current_accessor += 1;
2090        }
2091        let sampler = gltf_json::animation::Sampler {
2092            extensions: Option::default(),
2093            extras: Option::default(),
2094            input: gltf_json::Index::new(7 + current_buffer_view_offset),
2095            interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Linear),
2096            output: gltf_json::Index::new(current_accessor),
2097        };
2098        animation_samplers.push(sampler);
2099        current_accessor += 1;
2100        let sampler = gltf_json::animation::Sampler {
2101            extensions: Option::default(),
2102            extras: Option::default(),
2103            input: gltf_json::Index::new(7 + current_buffer_view_offset),
2104            interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Step),
2105            output: gltf_json::Index::new(current_accessor),
2106        };
2107        animation_samplers.push(sampler);
2108        current_accessor += 1;
2109        if compatibility_mode == GltfCompatibilityMode::Unreal {
2110            let sampler = gltf_json::animation::Sampler {
2111                extensions: Option::default(),
2112                extras: Option::default(),
2113                input: gltf_json::Index::new(7 + current_buffer_view_offset),
2114                interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Linear),
2115                output: gltf_json::Index::new(current_accessor),
2116            };
2117            animation_samplers.push(sampler);
2118            current_accessor += 1;
2119        }
2120        if self.num_morph_targets() > 0 {
2121            let sampler = gltf_json::animation::Sampler {
2122                extensions: Option::default(),
2123                extras: Option::default(),
2124                input: gltf_json::Index::new(7 + current_buffer_view_offset),
2125                interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Linear),
2126                output: gltf_json::Index::new(current_accessor),
2127            };
2128            animation_samplers.push(sampler);
2129        }
2130        animation_samplers
2131    }
2132    fn create_animation_samplers_prop(
2133        &self,
2134        joint_count: usize,
2135        current_buffer_view_offset: u32,
2136        _compatibility_mode: GltfCompatibilityMode,
2137    ) -> Vec<gltf_json::animation::Sampler> {
2138        let mut animation_samplers: Vec<gltf_json::animation::Sampler> = vec![];
2139        let mut current_accessor = current_buffer_view_offset - 3;
2140        for _ in 0..joint_count {
2141            let sampler = gltf_json::animation::Sampler {
2142                extensions: Option::default(),
2143                extras: Option::default(),
2144                input: gltf_json::Index::new(current_buffer_view_offset - 4),
2145                interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Linear),
2146                output: gltf_json::Index::new(current_accessor),
2147            };
2148            animation_samplers.push(sampler);
2149            current_accessor += 1;
2150        }
2151        let sampler = gltf_json::animation::Sampler {
2152            extensions: Option::default(),
2153            extras: Option::default(),
2154            input: gltf_json::Index::new(current_buffer_view_offset - 4),
2155            interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Linear),
2156            output: gltf_json::Index::new(current_accessor),
2157        };
2158        animation_samplers.push(sampler);
2159        current_accessor += 1;
2160        let sampler = gltf_json::animation::Sampler {
2161            extensions: Option::default(),
2162            extras: Option::default(),
2163            input: gltf_json::Index::new(current_buffer_view_offset - 4),
2164            interpolation: gltf_json::validation::Checked::Valid(gltf_json::animation::Interpolation::Step),
2165            output: gltf_json::Index::new(current_accessor),
2166        };
2167        animation_samplers.push(sampler);
2168        animation_samplers
2169    }
2170    /// Function for creating morph targets
2171    pub fn create_morph_targets(&self, pose_dirs_accessors_start_idx: usize) -> Vec<gltf_json::mesh::MorphTarget> {
2172        let mut pose_dirs_morph_targets: Vec<gltf_json::mesh::MorphTarget> = vec![];
2173        let mut running_pose_dirs_accessor = u32::try_from(pose_dirs_accessors_start_idx).expect("Not able to convert to u32");
2174        for _ in 0..self.num_morph_targets() {
2175            let morph_target = gltf_json::mesh::MorphTarget {
2176                positions: Some(gltf_json::Index::new(running_pose_dirs_accessor)),
2177                normals: None,
2178                tangents: None,
2179            };
2180            pose_dirs_morph_targets.push(morph_target);
2181            running_pose_dirs_accessor += 1;
2182        }
2183        pose_dirs_morph_targets
2184    }
2185    /// General purpose bind matrix computation from joint transformations
2186    pub fn create_bind_matrices(&self, rot_mat: &nd::Array3<f32>, joint_trans: &nd::Array2<f32>, joint_parents: &[u32]) -> nd::Array3<f32> {
2187        assert!(
2188            rot_mat.shape()[0] == joint_trans.shape()[0],
2189            "Number of rotation matrices dont match number of translation matrices!"
2190        );
2191        let num_joints = rot_mat.shape()[0];
2192        let mut bind_matrices = ndarray::Array3::<f32>::zeros((num_joints, 4, 4));
2193        bind_matrices.slice_mut(s![0, 0..3, 0..3]).assign(&rot_mat.slice(s![0, .., ..]));
2194        bind_matrices.slice_mut(s![0, 0..3, 3]).assign(&joint_trans.slice(s![0, ..]));
2195        bind_matrices[[0, 3, 3]] = 1.0;
2196        for j_idx in 1..num_joints {
2197            let parent_index = joint_parents[j_idx] as usize;
2198            let parent_transform = bind_matrices.index_axis(nd::Axis(0), parent_index);
2199            let mut local_transform = ndarray::Array2::<f32>::zeros((4, 4));
2200            local_transform.slice_mut(s![0..3, 0..3]).assign(&rot_mat.slice(s![j_idx, .., ..]));
2201            let local_translation = Array::from_vec(vec_to_vec(&compute_local_translation(j_idx, joint_parents, joint_trans)));
2202            local_transform.slice_mut(s![0..3, 3]).assign(&local_translation);
2203            local_transform[[3, 3]] = 1.0;
2204            let global_transform = parent_transform.dot(&local_transform);
2205            bind_matrices.slice_mut(s![j_idx, .., ..]).assign(&global_transform);
2206        }
2207        bind_matrices
2208    }
2209    /// Function to create animation buffer data
2210    pub fn create_animation_data(&self, current_body: &PerBodyData, compatibility_mode: GltfCompatibilityMode) -> Vec<u8> {
2211        let mut animation_data: Vec<u8> = vec![];
2212        let keyframe_data = to_padded_byte_vector(self.keyframe_times.as_ref().unwrap());
2213        let rotation_animation_data = current_body.body_rotations.as_ref().unwrap();
2214        let mut translation_animation_data = current_body.body_translations.as_ref().unwrap().clone();
2215        let scale_animation_data = current_body.body_scales.as_ref().unwrap().clone();
2216        animation_data.extend_from_slice(keyframe_data.as_slice());
2217        assert_eq!(rotation_animation_data.shape()[1], translation_animation_data.shape()[0]);
2218        for j_idx in 0..rotation_animation_data.shape()[0] {
2219            let mut quaternions: Vec<f32> = vec![];
2220            for r_idx in 0..rotation_animation_data.shape()[1] {
2221                let rotation = rotation_animation_data.slice(s![j_idx, r_idx, ..]);
2222                let axis_angle_rotation = na::Vector3::new(rotation[0], rotation[1], rotation[2]);
2223                let mut quaternion_rotation =
2224                    na::UnitQuaternion::from_axis_angle(&na::UnitVector3::new_normalize(axis_angle_rotation), axis_angle_rotation.norm());
2225                if axis_angle_rotation.norm() == 0.0 {
2226                    quaternion_rotation = na::UnitQuaternion::default();
2227                }
2228                quaternions.extend(quaternion_rotation.as_vector().data.as_slice());
2229            }
2230            let joint_anim_data = to_padded_byte_vector(&quaternions);
2231            animation_data.append(&mut joint_anim_data.clone());
2232        }
2233        if compatibility_mode == GltfCompatibilityMode::Unreal {
2234            let mut pelvis_relative_trans = translation_animation_data.clone();
2235            for mut row in translation_animation_data.axis_iter_mut(Axis(0)) {
2236                row[1] = 0.0;
2237            }
2238            for mut row in pelvis_relative_trans.axis_iter_mut(Axis(0)) {
2239                row[0] = 0.0;
2240                row[2] = 0.0;
2241            }
2242            let trans_anim_data = to_padded_byte_vector(&translation_animation_data.to_owned().into_raw_vec_and_offset().0);
2243            animation_data.append(&mut trans_anim_data.clone());
2244            let scale_anim_data = to_padded_byte_vector(&scale_animation_data.to_owned().into_raw_vec_and_offset().0);
2245            animation_data.append(&mut scale_anim_data.clone());
2246            let pelvis_rel_anim_data = to_padded_byte_vector(&pelvis_relative_trans.to_owned().into_raw_vec_and_offset().0);
2247            animation_data.append(&mut pelvis_rel_anim_data.clone());
2248        } else {
2249            let trans_anim_data = to_padded_byte_vector(&translation_animation_data.to_owned().into_raw_vec_and_offset().0);
2250            animation_data.append(&mut trans_anim_data.clone());
2251            let scale_anim_data = to_padded_byte_vector(&scale_animation_data.to_owned().into_raw_vec_and_offset().0);
2252            animation_data.append(&mut scale_anim_data.clone());
2253        }
2254        if self.num_morph_targets() > 0 {
2255            let morph_target_weights_data = current_body.per_frame_blend_weights.as_ref().unwrap();
2256            let weights_anim_data = to_padded_byte_vector(&morph_target_weights_data.to_owned().into_raw_vec_and_offset().0);
2257            animation_data.append(&mut weights_anim_data.clone());
2258        }
2259        animation_data
2260    }
2261    pub fn create_animation_data_prop(&self, current_prop: &PropData, _compatibility_mode: GltfCompatibilityMode) -> Vec<u8> {
2262        let mut animation_data: Vec<u8> = vec![];
2263        let keyframe_data = to_padded_byte_vector(self.keyframe_times.as_ref().unwrap());
2264        let rotation_animation_data = current_prop.rotations.clone();
2265        let translation_animation_data = current_prop.translations.clone();
2266        let scale_animation_data = current_prop.scales.clone();
2267        animation_data.extend_from_slice(keyframe_data.as_slice());
2268        assert_eq!(rotation_animation_data.shape()[1], translation_animation_data.shape()[0]);
2269        for j_idx in 0..rotation_animation_data.shape()[0] {
2270            let mut quaternions: Vec<f32> = vec![];
2271            for r_idx in 0..rotation_animation_data.shape()[1] {
2272                let rotation = rotation_animation_data.slice(s![j_idx, r_idx, ..]);
2273                let axis_angle_rotation = na::Vector3::new(rotation[0], rotation[1], rotation[2]);
2274                let mut quaternion_rotation =
2275                    na::UnitQuaternion::from_axis_angle(&na::UnitVector3::new_normalize(axis_angle_rotation), axis_angle_rotation.norm());
2276                if axis_angle_rotation.norm() == 0.0 {
2277                    quaternion_rotation = na::UnitQuaternion::default();
2278                }
2279                quaternions.extend(quaternion_rotation.as_vector().data.as_slice());
2280            }
2281            let joint_anim_data = to_padded_byte_vector(&quaternions);
2282            animation_data.append(&mut joint_anim_data.clone());
2283        }
2284        let trans_anim_data = to_padded_byte_vector(&translation_animation_data.to_owned().into_raw_vec_and_offset().0);
2285        animation_data.append(&mut trans_anim_data.clone());
2286        let scale_anim_data = to_padded_byte_vector(&scale_animation_data.to_owned().into_raw_vec_and_offset().0);
2287        animation_data.append(&mut scale_anim_data.clone());
2288        animation_data
2289    }
2290    /// Function to compose all present buffer views and buffers
2291    #[allow(clippy::too_many_arguments)]
2292    fn compose_buffer_views(
2293        &self,
2294        body_idx: usize,
2295        current_body: &PerBodyData,
2296        buffer_views: Vec<gltf_json::buffer::View>,
2297        index_data: &[u8],
2298        vertex_data: &[u8],
2299        inv_bind_mat_data: &[u8],
2300        textures: &mut [GltfTextureInfo],
2301        compatibility_mode: GltfCompatibilityMode,
2302    ) -> (Vec<u8>, Vec<gltf_json::buffer::View>, usize, usize) {
2303        let mut out_data: Vec<u8> = vec![];
2304        let mut out_buffer_views: Vec<gltf_json::buffer::View> = vec![];
2305        out_data.append(&mut index_data.to_owned());
2306        out_data.append(&mut vertex_data.to_owned());
2307        out_data.append(&mut inv_bind_mat_data.to_owned());
2308        if self.is_animated() {
2309            let mut animation_data = self.create_animation_data(current_body, compatibility_mode);
2310            out_data.append(&mut animation_data);
2311            if self.num_morph_targets() > 0 && body_idx == 0 {
2312                for morph_target_idx in 0..self.num_morph_targets() {
2313                    let mut posedir = self.morph_targets.as_ref().unwrap().slice(s![morph_target_idx, .., ..]).to_owned();
2314                    let num_pose_morph_targets = self.num_pose_morph_targets.saturating_sub(1);
2315                    let num_expression_morph_targets = self.num_expression_morph_targets.saturating_sub(1);
2316                    if compatibility_mode == GltfCompatibilityMode::Smpl
2317                        && self.num_pose_morph_targets > 0
2318                        && morph_target_idx < num_pose_morph_targets
2319                    {
2320                        posedir *= 2.0 * PI;
2321                    }
2322                    if compatibility_mode == GltfCompatibilityMode::Smpl
2323                        && self.num_expression_morph_targets > 0
2324                        && morph_target_idx > num_pose_morph_targets
2325                        && morph_target_idx < self.num_pose_morph_targets + num_expression_morph_targets
2326                    {
2327                        posedir *= 2.0 * 7.0;
2328                    }
2329                    let posedir_data = to_padded_byte_vector(&posedir.to_owned().into_raw_vec_and_offset().0);
2330                    out_data.append(&mut posedir_data.clone());
2331                }
2332            }
2333        }
2334        out_buffer_views.extend(buffer_views);
2335        let mut buffer_offset = out_data.len();
2336        let mut buffer_index: usize = out_buffer_views.len();
2337        for (sampler_index, texture) in textures.iter_mut().enumerate() {
2338            let mut buffer_view = texture.buffer_view.clone();
2339            buffer_view.byte_offset = Some(USize64::from(buffer_offset));
2340            out_buffer_views.push(buffer_view);
2341            texture.buffer_index = buffer_index;
2342            texture.image.buffer_view = Some(gltf_json::Index::new(u32::try_from(buffer_index).expect("Issue converting to u32!")));
2343            texture.texture.sampler = Some(gltf_json::Index::new(u32::try_from(sampler_index).expect("Issue converting to u32!")));
2344            out_data.append(&mut texture.image_data.clone());
2345            buffer_offset += texture.buffer_size;
2346            buffer_index += 1;
2347        }
2348        (out_data, out_buffer_views, buffer_offset, buffer_index)
2349    }
2350    /// Function to compose all present buffer views and buffers
2351    #[allow(clippy::too_many_arguments)]
2352    fn compose_buffer_views_props(
2353        &self,
2354        current_prop: &PropData,
2355        buffer_views: Vec<gltf_json::buffer::View>,
2356        index_data: &[u8],
2357        vertex_data: &[u8],
2358        inv_bind_mat_data: &[u8],
2359        textures: &mut [GltfTextureInfo],
2360        compatibility_mode: GltfCompatibilityMode,
2361        global_buffer_offset: usize,
2362        global_buffer_index: usize,
2363    ) -> (Vec<u8>, Vec<gltf_json::buffer::View>, usize, usize) {
2364        let mut out_data: Vec<u8> = vec![];
2365        let mut out_buffer_views: Vec<gltf_json::buffer::View> = vec![];
2366        out_data.append(&mut index_data.to_owned());
2367        out_data.append(&mut vertex_data.to_owned());
2368        out_data.append(&mut inv_bind_mat_data.to_owned());
2369        let mut animation_data = self.create_animation_data_prop(current_prop, compatibility_mode);
2370        out_data.append(&mut animation_data);
2371        out_buffer_views.extend(buffer_views);
2372        let mut buffer_offset = out_data.len() + global_buffer_offset;
2373        let mut buffer_index: usize = out_buffer_views.len() + global_buffer_index;
2374        for (sampler_index, texture) in textures.iter_mut().enumerate() {
2375            let mut buffer_view = texture.buffer_view.clone();
2376            buffer_view.byte_offset = Some(USize64::from(buffer_offset));
2377            out_buffer_views.push(buffer_view);
2378            texture.buffer_index = buffer_index;
2379            texture.image.buffer_view = Some(gltf_json::Index::new(u32::try_from(buffer_index).expect("Issue converting to u32!")));
2380            texture.texture.sampler = Some(gltf_json::Index::new(u32::try_from(sampler_index).expect("Issue converting to u32!")));
2381            out_data.append(&mut texture.image_data.clone());
2382            buffer_offset += texture.buffer_size;
2383            buffer_index += 1;
2384        }
2385        (out_data, out_buffer_views, buffer_offset, buffer_index)
2386    }
2387    /// Add a GLTF texture
2388    fn add_texture(&self, img: &DynImage, index: usize, name: &str) -> Option<GltfTextureInfo> {
2389        let mut image_data: Vec<u8> = vec![];
2390        let mut target = Cursor::new(&mut image_data);
2391        let image_data_buffer = img.write_to(&mut target, image::ImageFormat::Png);
2392        if image_data_buffer.is_ok() {
2393            let _ = target.flush();
2394            while image_data.len() % 4 != 0 {
2395                image_data.push(0);
2396            }
2397            let mut image_buffer_size = image_data.len();
2398            align_to_multiple_of_four(&mut image_buffer_size);
2399            let image_buffer_view = gltf_json::buffer::View {
2400                buffer: gltf_json::Index::new(0),
2401                byte_length: USize64::from(image_buffer_size),
2402                byte_offset: Some(USize64::from(0_usize)),
2403                byte_stride: Option::default(),
2404                extensions: Option::default(),
2405                extras: Option::default(),
2406                name: Some(name.to_string()),
2407                target: None,
2408            };
2409            let image = gltf_json::image::Image {
2410                buffer_view: Some(gltf_json::Index::new(u32::try_from(index).expect("Issue converting to u32!"))),
2411                mime_type: Some(gltf_json::image::MimeType("image/png".to_string())),
2412                name: Some(name.to_string()),
2413                uri: None,
2414                extensions: None,
2415                extras: None,
2416            };
2417            let texture = gltf_json::Texture {
2418                name: Some(name.to_string()),
2419                sampler: Some(gltf_json::Index::new(u32::try_from(index).expect("Issue converting to u32!"))),
2420                source: gltf_json::Index::new(u32::try_from(index).expect("Issue converting to u32!")),
2421                extensions: None,
2422                extras: None,
2423            };
2424            let sampler = gltf_json::texture::Sampler {
2425                name: Some(name.to_string()),
2426                mag_filter: Some(gltf_json::validation::Checked::Valid(gltf_json::texture::MagFilter::Linear)),
2427                min_filter: Some(gltf_json::validation::Checked::Valid(gltf_json::texture::MinFilter::Linear)),
2428                wrap_s: gltf_json::validation::Checked::Valid(gltf_json::texture::WrappingMode::ClampToEdge),
2429                wrap_t: gltf_json::validation::Checked::Valid(gltf_json::texture::WrappingMode::ClampToEdge),
2430                extensions: None,
2431                extras: None,
2432            };
2433            let texture_info = GltfTextureInfo {
2434                buffer_size: image_data.len(),
2435                image_data,
2436                image,
2437                buffer_view: image_buffer_view,
2438                buffer_index: 0,
2439                texture,
2440                sampler,
2441            };
2442            return Some(texture_info);
2443        }
2444        log!("add_texture FAILED: {}", name);
2445        None
2446    }
2447    /// Prepare normal map for GLTF
2448    #[allow(clippy::cast_sign_loss)]
2449    fn prepare_normals(&self, smpl_textures: &mut SmplTextures, texture_infos: &mut Vec<GltfTextureInfo>, normals_tex: Option<&DynImage>) {
2450        if let Some(img) = normals_tex {
2451            let normals_tex = self.add_texture(img, texture_infos.len(), "normals");
2452            if let Some(normals_tex) = normals_tex {
2453                smpl_textures.normals_index = Some(texture_infos.len());
2454                texture_infos.push(normals_tex);
2455            }
2456        }
2457    }
2458    /// Prepare metallic-roughness map for GLTF
2459    fn prepare_metallic_roughness(
2460        &self,
2461        smpl_textures: &mut SmplTextures,
2462        texture_infos: &mut Vec<GltfTextureInfo>,
2463        metalness_tex: Option<&DynImage>,
2464        roughness_tex: Option<&DynImage>,
2465    ) {
2466        let mut w: u32 = 0;
2467        let mut h: u32 = 0;
2468        if let Some(img) = metalness_tex {
2469            w = img.width();
2470            h = img.height();
2471        }
2472        if let Some(img) = roughness_tex {
2473            w = w.max(img.width());
2474            h = h.max(img.height());
2475        }
2476        let mut metalness: Option<Vec<u8>> = None;
2477        if let Some(img) = metalness_tex {
2478            if img.width() != w || img.height() != h {
2479                let resized_img = img.resize(w, h, FilterType::Gaussian);
2480                metalness = Some(resized_img.as_luma8().unwrap().to_vec());
2481            } else {
2482                metalness = Some(img.as_bytes().to_vec());
2483            }
2484        }
2485        let mut roughness: Option<Vec<u8>> = None;
2486        if let Some(img) = roughness_tex {
2487            if img.width() != w || img.height() != h {
2488                let resized_img = img.resize(w, h, FilterType::Gaussian);
2489                roughness = Some(resized_img.as_luma8().unwrap().to_vec());
2490            } else {
2491                roughness = Some(img.as_bytes().to_vec());
2492            }
2493        }
2494        let num_pixels: usize = (w * h) as usize;
2495        let mut metal_roughness_pixels: Vec<u8> = vec![];
2496        if let Some(metalness_pixels) = metalness {
2497            if let Some(roughness_pixels) = roughness {
2498                for (m, r) in metalness_pixels.iter().zip(roughness_pixels.iter()).take(num_pixels) {
2499                    metal_roughness_pixels.push(0);
2500                    metal_roughness_pixels.push(*r);
2501                    metal_roughness_pixels.push(*m);
2502                }
2503            } else {
2504                for &m in metalness_pixels.iter().take(num_pixels) {
2505                    metal_roughness_pixels.push(0);
2506                    metal_roughness_pixels.push(0);
2507                    metal_roughness_pixels.push(m);
2508                }
2509            }
2510        } else if let Some(roughness_pixels) = roughness {
2511            for &r in roughness_pixels.iter().take(num_pixels) {
2512                metal_roughness_pixels.push(0);
2513                metal_roughness_pixels.push(r);
2514                metal_roughness_pixels.push(0);
2515            }
2516        }
2517        if !metal_roughness_pixels.is_empty() {
2518            let metal_roughness_image = RgbImage::from_vec(w, h, metal_roughness_pixels);
2519            if let Some(image) = metal_roughness_image {
2520                let image = DynImage::from(image);
2521                let metallic_roughness = self.add_texture(&image, texture_infos.len(), "metal_roughness");
2522                if let Some(metallic_roughness) = metallic_roughness {
2523                    smpl_textures.metalic_roughtness_index = Some(texture_infos.len());
2524                    texture_infos.push(metallic_roughness);
2525                }
2526            }
2527        }
2528    }
2529    /// Create a new joint with the given transformations
2530    fn create_joint(&self, name: String, translation: &[f32], rotation: &Vector3f, children: Option<Vec<gltf_json::Index<Node>>>) -> Node {
2531        let cur_vec = na::Vector3::new(rotation.x, rotation.y, rotation.z);
2532        let mut cur_q = na::UnitQuaternion::from_axis_angle(&na::UnitVector3::new_normalize(cur_vec), rotation.norm());
2533        if rotation.norm() == 0.0 {
2534            cur_q = na::UnitQuaternion::default();
2535        }
2536        let translation: [f32; 3] = [translation[0], translation[1], translation[2]];
2537        let unit_quaternion = [cur_q[0], cur_q[1], cur_q[2], cur_q[3]];
2538        Node {
2539            children,
2540            mesh: None,
2541            skin: None,
2542            name: Some(name),
2543            rotation: Some(UnitQuaternion(unit_quaternion)),
2544            translation: Some(translation),
2545            ..Default::default()
2546        }
2547    }
2548    fn gather_children(&self, id: u32, parent_ids: &[u32], offset: u32) -> Option<Vec<gltf_json::Index<Node>>> {
2549        let mut children: Vec<gltf_json::Index<Node>> = vec![];
2550        for (p, &parent_id) in parent_ids.iter().enumerate() {
2551            if parent_id == id {
2552                let index = u32::try_from(p).expect("Index conversion error: usize value is too large to fit in a u32");
2553                children.push(gltf_json::Index::<Node>::new(index + offset));
2554            }
2555        }
2556        if !children.is_empty() {
2557            return Some(children);
2558        }
2559        None
2560    }
2561    /// Add a skin to GLTF
2562    #[allow(clippy::too_many_arguments)]
2563    fn add_skin(
2564        &mut self,
2565        name: String,
2566        current_body: &PerBodyData,
2567        current_armature_idx: u32,
2568        accessor_offset: u32,
2569        nodes: &mut Vec<Node>,
2570        skins: &mut Vec<gltf_json::Skin>,
2571        joints: &mut Vec<gltf_json::Index<Node>>,
2572        compatibility_mode: GltfCompatibilityMode,
2573    ) -> gltf_json::Index<Node> {
2574        let metadata = crate::common::metadata::smpl_metadata(&self.smpl_type);
2575        let joint_translations = current_body.default_joint_translations.as_ref().unwrap();
2576        let skeleton_root_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
2577        let global_translation = vec_from_array0_f(current_body.body_translation.as_ref().unwrap());
2578        let mut skeleton_root_translation = compute_local_translation(0, &metadata.joint_parents, joint_translations);
2579        if compatibility_mode == GltfCompatibilityMode::Smpl {
2580            skeleton_root_translation = addv3f(&skeleton_root_translation, &global_translation);
2581        }
2582        let mut joint_rotation = Vector3f::zeros();
2583        if let Some(joint_poses) = current_body.joint_poses.as_ref() {
2584            let rot = joint_poses.row(0).to_owned();
2585            joint_rotation = Vector3f::new(rot[0], rot[1], rot[2]);
2586        }
2587        let skeleton_root = self.create_joint(
2588            "pelvis".to_string(),
2589            vec_to_vec(&skeleton_root_translation).as_slice(),
2590            &joint_rotation,
2591            self.gather_children(0, &metadata.joint_parents, skeleton_root_index),
2592        );
2593        nodes.push(skeleton_root);
2594        joints.push(gltf_json::Index::new(skeleton_root_index));
2595        let joint_names = if compatibility_mode == GltfCompatibilityMode::Unreal {
2596            smpl_x::JOINT_NAMES_UNREAL.map(std::string::ToString::to_string).to_vec()
2597        } else {
2598            metadata.joint_names
2599        };
2600        for (j, name) in joint_names.iter().enumerate().take(metadata.num_joints + 1).skip(1) {
2601            if let Some(joint_poses) = current_body.joint_poses.as_ref() {
2602                let rot = joint_poses.row(j).to_owned();
2603                joint_rotation = Vector3f::new(rot[0], rot[1], rot[2]);
2604            }
2605            let joint = self.create_joint(
2606                name.clone(),
2607                vec_to_vec(&compute_local_translation(j, &metadata.joint_parents, joint_translations)).as_slice(),
2608                &joint_rotation,
2609                self.gather_children(
2610                    u32::try_from(j).expect("Issue converting Joint idx to u32"),
2611                    &metadata.joint_parents,
2612                    skeleton_root_index,
2613                ),
2614            );
2615            let joint_index = gltf_json::Index::<Node>::new(u32::try_from(nodes.len()).expect("Issue converting Joint idx to u32"));
2616            nodes.push(joint);
2617            joints.push(joint_index);
2618        }
2619        let skin = gltf_json::Skin {
2620            name: Some(name),
2621            inverse_bind_matrices: Some(gltf_json::Index::new(accessor_offset + 6)),
2622            joints: joints.clone(),
2623            skeleton: Some(gltf_json::Index::new(current_armature_idx)),
2624            extensions: None,
2625            extras: None,
2626        };
2627        skins.push(skin);
2628        gltf_json::Index::<Node>::new(skeleton_root_index)
2629    }
2630    #[allow(clippy::too_many_arguments)]
2631    fn add_skin_prop(
2632        &mut self,
2633        name: String,
2634        current_prop: &PropData,
2635        current_armature_idx: u32,
2636        accessor_offset: u32,
2637        nodes: &mut Vec<Node>,
2638        skins: &mut Vec<gltf_json::Skin>,
2639        joints: &mut Vec<gltf_json::Index<Node>>,
2640        _compatibility_mode: GltfCompatibilityMode,
2641    ) -> gltf_json::Index<Node> {
2642        let joint_translations = &current_prop.default_translation;
2643        let skeleton_root_index = u32::try_from(nodes.len()).expect("Issue converting Node idx to u32");
2644        let skeleton_root_translation = vec_from_vec(&joint_translations.row(0).to_vec());
2645        let joint_rotation = Vector3f::zeros();
2646        let skeleton_root = self.create_joint(
2647            "root_prop".to_string(),
2648            vec_to_vec(&skeleton_root_translation).as_slice(),
2649            &joint_rotation,
2650            None,
2651        );
2652        nodes.push(skeleton_root);
2653        joints.push(gltf_json::Index::new(skeleton_root_index));
2654        let skin = gltf_json::Skin {
2655            name: Some(name),
2656            inverse_bind_matrices: Some(gltf_json::Index::new(accessor_offset - 5)),
2657            joints: joints.clone(),
2658            skeleton: Some(gltf_json::Index::new(current_armature_idx)),
2659            extensions: None,
2660            extras: None,
2661        };
2662        skins.push(skin);
2663        gltf_json::Index::<Node>::new(skeleton_root_index)
2664    }
2665    /// Create camera animation buffer views
2666    #[allow(clippy::cast_precision_loss)]
2667    fn create_camera_animation_buffer_views(
2668        &self,
2669        running_offset: &mut usize,
2670        compatibility_mode: GltfCompatibilityMode,
2671    ) -> Option<(Vec<gltf_json::buffer::View>, Vec<u8>)> {
2672        let camera_track = self.camera_track.as_ref()?;
2673        let mut buffer_views = Vec::new();
2674        let mut buffer_data = Vec::new();
2675        if let Some(translations) = camera_track.per_frame_translations.as_ref() {
2676            let trans_data = to_padded_byte_vector(translations.as_slice().unwrap());
2677            let trans_len = trans_data.len();
2678            let trans_view = gltf_json::buffer::View {
2679                buffer: gltf_json::Index::new(0),
2680                byte_length: USize64::from(trans_len),
2681                byte_offset: Some(USize64::from(*running_offset)),
2682                byte_stride: None,
2683                extensions: None,
2684                extras: Option::default(),
2685                name: Some("camera_translations".to_string()),
2686                target: None,
2687            };
2688            buffer_data.extend(trans_data);
2689            *running_offset += trans_len;
2690            buffer_views.push(trans_view);
2691        }
2692        if let Some(rotations) = camera_track.per_frame_rotations.as_ref() {
2693            let rotated_rots = if compatibility_mode == GltfCompatibilityMode::Unreal {
2694                let angle = 90.0;
2695                let axis = [0.0, 1.0, 0.0];
2696                self.rotate_camera_quaternions(angle, axis).as_ref().unwrap().clone()
2697            } else {
2698                rotations.clone()
2699            };
2700            let rot_data = to_padded_byte_vector(rotated_rots.as_slice().unwrap());
2701            let rot_len = rot_data.len();
2702            let rot_view = gltf_json::buffer::View {
2703                buffer: gltf_json::Index::new(0),
2704                byte_length: USize64::from(rot_len),
2705                byte_offset: Some(USize64::from(*running_offset)),
2706                byte_stride: None,
2707                extensions: None,
2708                extras: Option::default(),
2709                name: Some("camera_rotations".to_string()),
2710                target: None,
2711            };
2712            buffer_data.extend(rot_data);
2713            *running_offset += rot_len;
2714            buffer_views.push(rot_view);
2715        }
2716        Some((buffer_views, buffer_data))
2717    }
2718    /// Create camera animation accessors
2719    fn create_camera_animation_accessors(&self, current_buffer_view_offset: u32) -> Option<Vec<gltf_json::Accessor>> {
2720        let camera_track = self.camera_track.as_ref()?;
2721        let mut accessors = Vec::new();
2722        let mut current_view = current_buffer_view_offset;
2723        if let Some(translations) = camera_track.per_frame_translations.as_ref() {
2724            accessors.push(gltf_json::Accessor {
2725                buffer_view: Some(gltf_json::Index::new(current_view)),
2726                byte_offset: None,
2727                component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
2728                count: USize64::from(translations.shape()[0]),
2729                type_: Valid(gltf_json::accessor::Type::Vec3),
2730                min: None,
2731                max: None,
2732                name: Some("camera_translations".to_string()),
2733                normalized: false,
2734                sparse: None,
2735                extensions: None,
2736                extras: Option::default(),
2737            });
2738            current_view += 1;
2739        }
2740        if let Some(rotations) = camera_track.per_frame_rotations.as_ref() {
2741            accessors.push(gltf_json::Accessor {
2742                buffer_view: Some(gltf_json::Index::new(current_view)),
2743                byte_offset: None,
2744                component_type: Valid(gltf_json::accessor::GenericComponentType(gltf_json::accessor::ComponentType::F32)),
2745                count: USize64::from(rotations.shape()[0]),
2746                type_: Valid(gltf_json::accessor::Type::Vec4),
2747                min: None,
2748                max: None,
2749                name: Some("camera_rotations".to_string()),
2750                normalized: false,
2751                sparse: None,
2752                extensions: None,
2753                extras: Option::default(),
2754            });
2755        }
2756        Some(accessors)
2757    }
2758    /// Create camera animation channels and samplers
2759    #[allow(clippy::cast_possible_truncation)]
2760    fn create_camera_animation_channels_and_samplers(
2761        &self,
2762        current_accessor_offset: u32,
2763        camera_node_index: u32,
2764        sampler_start_idx: u32,
2765    ) -> Option<(Vec<gltf_json::animation::Channel>, Vec<gltf_json::animation::Sampler>)> {
2766        let camera_track = self.camera_track.as_ref()?;
2767        let mut channels = Vec::new();
2768        let mut samplers = Vec::new();
2769        let mut current_accessor = current_accessor_offset;
2770        let times_accessor_index = 7;
2771        if camera_track.per_frame_translations.is_some() {
2772            samplers.push(gltf_json::animation::Sampler {
2773                input: gltf_json::Index::new(times_accessor_index),
2774                interpolation: Valid(gltf_json::animation::Interpolation::Linear),
2775                output: gltf_json::Index::new(current_accessor),
2776                extensions: None,
2777                extras: Option::default(),
2778            });
2779            channels.push(gltf_json::animation::Channel {
2780                sampler: gltf_json::Index::new(sampler_start_idx + samplers.len() as u32 - 1),
2781                target: gltf_json::animation::Target {
2782                    node: gltf_json::Index::new(camera_node_index),
2783                    path: Valid(gltf_json::animation::Property::Translation),
2784                    extensions: None,
2785                    extras: Option::default(),
2786                },
2787                extensions: None,
2788                extras: Option::default(),
2789            });
2790            current_accessor += 1;
2791        }
2792        if camera_track.per_frame_rotations.is_some() {
2793            samplers.push(gltf_json::animation::Sampler {
2794                input: gltf_json::Index::new(times_accessor_index),
2795                interpolation: Valid(gltf_json::animation::Interpolation::Linear),
2796                output: gltf_json::Index::new(current_accessor),
2797                extensions: None,
2798                extras: Option::default(),
2799            });
2800            channels.push(gltf_json::animation::Channel {
2801                sampler: gltf_json::Index::new(sampler_start_idx + samplers.len() as u32 - 1),
2802                target: gltf_json::animation::Target {
2803                    node: gltf_json::Index::new(camera_node_index),
2804                    path: Valid(gltf_json::animation::Property::Rotation),
2805                    extensions: None,
2806                    extras: Option::default(),
2807                },
2808                extensions: None,
2809                extras: Option::default(),
2810            });
2811        }
2812        Some((channels, samplers))
2813    }
2814    fn num_morph_targets(&self) -> usize {
2815        self.morph_targets.as_ref().map_or(0, |x| x.shape()[0])
2816    }
2817    /// Rotate camera rotation quaternions by a given angle and axis
2818    fn rotate_camera_quaternions(&self, angle_degrees: f32, axis: [f32; 3]) -> Option<nd::Array2<f32>> {
2819        if let Some(camera_track) = &self.camera_track {
2820            if let Some(rotations) = &camera_track.per_frame_rotations {
2821                let mut rotated_quaternions = rotations.clone();
2822                let axis_vec = na::Vector3::new(axis[0], axis[1], axis[2]);
2823                let rotation_quat = na::UnitQuaternion::from_axis_angle(&na::UnitVector3::new_normalize(axis_vec), angle_degrees.to_radians());
2824                for mut row in rotated_quaternions.axis_iter_mut(nd::Axis(0)) {
2825                    let q = na::Quaternion::new(row[3], row[0], row[1], row[2]);
2826                    let unit_q = na::UnitQuaternion::from_quaternion(q);
2827                    let result = rotation_quat * unit_q;
2828                    row[0] = result.i;
2829                    row[1] = result.j;
2830                    row[2] = result.k;
2831                    row[3] = result.w;
2832                }
2833                return Some(rotated_quaternions);
2834            }
2835        }
2836        None
2837    }
2838}
2839pub fn compute_local_translation(id: usize, parent_ids: &[u32], joint_translations: &nd::Array2<f32>) -> Vector3f {
2840    let trans = vec_from_vec(&joint_translations.row(id).to_vec());
2841    if id == 0 {
2842        return trans;
2843    }
2844    let parent_id = parent_ids[id] as usize;
2845    let parent_trans = vec_from_vec(&joint_translations.row(parent_id).to_vec());
2846    subv3f(&trans, &parent_trans)
2847}