gloss_renderer/
geom.rs

1#![allow(clippy::missing_panics_doc)] //a lot of operations require inserting or removing component from a entity but
2                                      // the entity will for sure exists so it will never panic
3
4use crate::components::{Colors, Edges};
5use gloss_hecs::EntityBuilder;
6use gloss_img::DynImage;
7use gloss_utils::tensor::{DynamicMatrixOps, DynamicTensorFloat2D, DynamicTensorInt2D};
8use image::{GenericImageView, Pixel};
9use log::debug;
10use nalgebra_glm::{Vec2, Vec3};
11
12extern crate nalgebra as na;
13use crate::components::{Faces, ModelMatrix, Normals, UVs, Verts};
14use core::f32;
15use gloss_utils::io::FileType;
16use itertools::izip;
17#[allow(unused_imports)]
18use log::{error, info, warn};
19use na::DMatrix;
20use obj_exporter::{Geometry, ObjSet, Object, Primitive, Shape, TVertex, Vertex};
21use std::{ops::AddAssign, path::Path};
22use tobj;
23
24use ply_rs::{
25    parser,
26    ply::{self, Addable, Ply},
27};
28
29use burn::tensor::{backend::Backend, Float, Int, Tensor};
30use gloss_utils::tensor;
31
32#[derive(PartialEq)]
33pub enum PerVertexNormalsWeightingType {
34    /// Incident face normals have uniform influence on vertex normal
35    Uniform,
36    /// Incident face normals are averaged weighted by area
37    Area,
38}
39
40#[derive(PartialEq)]
41pub enum SplatType {
42    /// Averages all the values that end up in the same index
43    Avg,
44    /// Sums all the values that end up in the same index
45    Sum,
46}
47
48#[derive(PartialEq)]
49pub enum IndirRemovalPolicy {
50    RemoveInvalidRows,
51    RemoveInvalidCols,
52}
53
54/// Geom contains functionality related to mesh-like entities, like reading from
55/// files, or fixing and processing mesh data. Contains mostly static functions
56/// so that in the future this class can be broken into it's own crate for
57/// geometry processing.
58pub struct Geom {
59    // pub entity: Entity,
60}
61
62impl Geom {
63    // pub fn new(name: &str, scene: &mut Scene) -> Self {
64    //     //check if there is an entity with the same name, if no then spawn one
65    //     let entity = scene.get_or_create_hidden_entity(name).entity();
66    //     Self { entity }
67    // }
68
69    // pub fn from_entity(entity: Entity) -> Self {
70    //     Self { entity }
71    // }
72
73    /// Creates a cube
74    #[must_use]
75    pub fn build_cube(center: na::Point3<f32>) -> EntityBuilder {
76        //makes a 1x1x1 vox in NDC. which has Z going into the screen
77        let verts = DMatrix::<f32>::from_row_slice(
78            8,
79            3,
80            &[
81                //behind face (which has negative Z as the camera is now looking in the positive Z direction and this will be the face that is
82                // behind the camera)
83                -1.0, -1.0, -1.0, //bottom-left
84                1.0, -1.0, -1.0, //bottom-right
85                1.0, 1.0, -1.0, //top-right
86                -1.0, 1.0, -1.0, //top-left
87                //front face
88                -1.0, -1.0, 1.0, //bottom-left
89                1.0, -1.0, 1.0, //bottom-right
90                1.0, 1.0, 1.0, //top-right
91                -1.0, 1.0, 1.0, //top-left
92            ],
93        );
94
95        //faces (2 triangles per faces, with 6 faces which makes 12 triangles)
96        let faces = DMatrix::<u32>::from_row_slice(
97            12,
98            3,
99            &[
100                2, 1, 0, //
101                2, 0, 3, //
102                4, 5, 6, //
103                7, 4, 6, //
104                5, 0, 1, //
105                4, 0, 5, //
106                7, 6, 3, //
107                3, 6, 2, //
108                3, 0, 4, //
109                3, 4, 7, //
110                6, 5, 1, //
111                6, 1, 2, //
112            ],
113        );
114
115        //since we want each vertex at the corner to have multiple normals, we just
116        // duplicate them
117        let mut verts_dup = Vec::new();
118        let mut faces_dup = Vec::new();
119        for f in faces.row_iter() {
120            let p1 = verts.row(f[0] as usize);
121            let p2 = verts.row(f[2] as usize);
122            let p3 = verts.row(f[1] as usize);
123            verts_dup.push(p1);
124            verts_dup.push(p2);
125            verts_dup.push(p3);
126
127            #[allow(clippy::cast_possible_truncation)]
128            let v_size = verts_dup.len() as u32;
129            // let new_face = na::RowDVector::<u32>::new(v_size-1, v_size-2,
130            // v_size-3);//corresponds to p3,p2 and p1
131            let new_face = na::RowDVector::<u32>::from_row_slice(&[v_size - 1, v_size - 2, v_size - 3]); //corresponds to p3,p2 and p1
132            faces_dup.push(new_face);
133        }
134
135        let verts = na::DMatrix::<f32>::from_rows(verts_dup.as_slice());
136        let faces = na::DMatrix::<u32>::from_rows(faces_dup.as_slice());
137
138        let mut model_matrix = na::SimilarityMatrix3::<f32>::identity();
139        // model_matrix.append_scaling_mut(0.1);
140        model_matrix.append_translation_mut(&center.coords.into());
141        let verts_tensor = DynamicTensorFloat2D::from_dmatrix(&verts);
142        let faces_tensor = DynamicTensorInt2D::from_dmatrix(&faces);
143
144        let mut builder = EntityBuilder::new();
145        builder.add(Verts(verts_tensor)).add(Faces(faces_tensor)).add(ModelMatrix(model_matrix));
146
147        builder
148    }
149
150    /// Creates a plane based on a center and normal. If `transform_cpu_data` is
151    /// true, then the vertices of the plane are actually translated and rotated
152    /// on the CPU. Otherwise the transformation is done on the GPU using the
153    /// model matrix. This is useful when creating light planes for example when
154    /// we want the model matrix to be consistent with the orientation of the
155    /// light and therefore we set `transform_cpu_data=false`
156    #[must_use]
157    pub fn build_plane(center: na::Point3<f32>, normal: na::Vector3<f32>, size_x: f32, size_y: f32, transform_cpu_data: bool) -> EntityBuilder {
158        //make 4 vertices
159        let mut verts = DMatrix::<f32>::from_row_slice(
160            4,
161            3,
162            &[
163                -1.0 * size_x,
164                0.0,
165                -1.0 * size_y, //
166                1.0 * size_x,
167                0.0,
168                -1.0 * size_y, //
169                1.0 * size_x,
170                0.0,
171                1.0 * size_y, //
172                -1.0 * size_x,
173                0.0,
174                1.0 * size_y, //
175            ],
176        );
177        //make 2 faces
178        let faces = DMatrix::<u32>::from_row_slice(
179            2,
180            3,
181            &[
182                2, 1, 0, //
183                3, 2, 0,
184            ],
185        );
186
187        //uvs
188        let uvs = DMatrix::<f32>::from_row_slice(
189            4,
190            2,
191            &[
192                0.0, 0.0, //
193                1.0, 0.0, //
194                1.0, 1.0, //
195                0.0, 1.0, //
196            ],
197        );
198
199        //make a model matrix
200        let up = na::Vector3::<f32>::new(0.0, 1.0, 0.0);
201        let lookat = center + normal * 1.0;
202        //up and normal are colinear so face_towards would fail, we just set to
203        // identity
204        let mut model_matrix = if up.angle(&normal.normalize()) < 1e-6 {
205            let mut mm = na::SimilarityMatrix3::<f32>::identity();
206            mm.append_translation_mut(&na::Translation3::from(center));
207            mm
208        } else {
209            let mut m = na::SimilarityMatrix3::<f32>::face_towards(&center, &lookat, &up, 1.0);
210            m = m
211                * na::Rotation3::<f32>::from_axis_angle(&na::Vector3::z_axis(), std::f32::consts::FRAC_PI_2)
212                * na::Rotation3::<f32>::from_axis_angle(&na::Vector3::x_axis(), std::f32::consts::FRAC_PI_2); //rotate 90 degrees
213            m
214        };
215
216        if transform_cpu_data {
217            //transform directly the verts
218            for mut vert in verts.row_iter_mut() {
219                let v_modif = model_matrix * na::Point3::from(vert.fixed_columns::<3>(0).transpose());
220                vert.copy_from_slice(v_modif.coords.as_slice());
221            }
222            //reset to identity
223            model_matrix = na::SimilarityMatrix3::<f32>::identity();
224        }
225        let verts_tensor = DynamicTensorFloat2D::from_dmatrix(&verts);
226        let faces_tensor = DynamicTensorInt2D::from_dmatrix(&faces);
227        let uvs_tensor = DynamicTensorFloat2D::from_dmatrix(&uvs);
228
229        let mut builder = EntityBuilder::new();
230        builder
231            .add(Verts(verts_tensor))
232            .add(Faces(faces_tensor))
233            .add(UVs(uvs_tensor))
234            .add(ModelMatrix(model_matrix));
235
236        builder
237    }
238
239    #[must_use]
240    pub fn build_floor() -> EntityBuilder {
241        //make 4 vertices
242        let verts = DMatrix::<f32>::from_row_slice(
243            4,
244            3,
245            &[
246                -1.0, 0.0, -1.0, //
247                1.0, 0.0, -1.0, //
248                1.0, 0.0, 1.0, //
249                -1.0, 0.0, 1.0, //
250            ],
251        );
252        //make 2 faces
253        let faces = DMatrix::<u32>::from_row_slice(
254            2,
255            3,
256            &[
257                2, 1, 0, //
258                3, 2, 0,
259            ],
260        );
261
262        //uvs
263        let uvs = DMatrix::<f32>::from_row_slice(
264            4,
265            2,
266            &[
267                0.0, 0.0, //
268                1.0, 0.0, //
269                1.0, 1.0, //
270                0.0, 1.0, //
271            ],
272        );
273        let verts_tensor = DynamicTensorFloat2D::from_dmatrix(&verts);
274        let faces_tensor = DynamicTensorInt2D::from_dmatrix(&faces);
275        let uvs_tensor = DynamicTensorFloat2D::from_dmatrix(&uvs);
276        let mut builder = EntityBuilder::new();
277        builder.add(Verts(verts_tensor)).add(Faces(faces_tensor)).add(UVs(uvs_tensor));
278
279        builder
280    }
281
282    #[must_use]
283    #[allow(clippy::cast_precision_loss)]
284    pub fn build_grid(
285        center: na::Point3<f32>,
286        normal: na::Vector3<f32>,
287        nr_lines_x: u32,
288        nr_lines_y: u32,
289        size_x: f32,
290        size_y: f32,
291        transform_cpu_data: bool,
292    ) -> EntityBuilder {
293        //a grid has at least 2 lines in each dimension, because each square needs 2
294        // lines, so we cap the number
295        let nr_lines_x = nr_lines_x.max(2);
296        let nr_lines_y = nr_lines_y.max(2);
297
298        //in order to be consistent with build_plane we multiply the size in x and y by
299        // 2 since it's technically the size from the center until the outer edge
300        // let size_x = size_x * 2.0;
301        // let size_y = size_y * 2.0;
302
303        let size_cell_x = size_x / nr_lines_x as f32;
304        let size_cell_y = size_y / nr_lines_y as f32;
305        let grid_half_size_x = size_x / 2.0;
306        let grid_half_size_y = size_y / 2.0;
307
308        // println!("nr_linex_x {}", size_cell_x);
309        // println!("size_cell_x {}", size_cell_x);
310        // println!("grid_half_size_x {}", grid_half_size_x);
311
312        //make points
313        let mut verts = Vec::new();
314        for idx_y in 0..nr_lines_y {
315            for idx_x in 0..nr_lines_x {
316                verts.push(idx_x as f32 * size_cell_x - grid_half_size_x);
317                verts.push(0.0);
318                verts.push(idx_y as f32 * size_cell_y - grid_half_size_y);
319            }
320        }
321
322        //make edges horizontally
323        let mut edges_h = Vec::new();
324        for idx_y in 0..nr_lines_y {
325            for idx_x in 0..nr_lines_x - 1 {
326                let idx_cur = idx_y * nr_lines_x + idx_x;
327                let idx_next = idx_y * nr_lines_x + idx_x + 1;
328                edges_h.push(idx_cur);
329                edges_h.push(idx_next);
330            }
331        }
332
333        //make edges vertically
334        let mut edges_v = Vec::new();
335        for idx_y in 0..nr_lines_y - 1 {
336            for idx_x in 0..nr_lines_x {
337                let idx_cur = idx_y * nr_lines_x + idx_x;
338                let idx_next = (idx_y + 1) * nr_lines_x + idx_x;
339                edges_v.push(idx_cur);
340                edges_v.push(idx_next);
341            }
342        }
343
344        let mut edges = edges_h;
345        edges.extend(edges_v);
346
347        //make nalgebra matrices
348        let mut verts = DMatrix::<f32>::from_row_slice(verts.len() / 3, 3, &verts);
349        let edges = DMatrix::<u32>::from_row_slice(edges.len() / 2, 2, &edges);
350
351        //make a model matrix
352        let up = na::Vector3::<f32>::new(0.0, 1.0, 0.0);
353        let lookat = center + normal * 1.0;
354        //up and normal are colinear so face_towards would fail, we just set to
355        // identity
356        let mut model_matrix = if up.angle(&normal.normalize()) < 1e-6 {
357            let mut mm = na::SimilarityMatrix3::<f32>::identity();
358            mm.append_translation_mut(&na::Translation3::from(center));
359            mm
360        } else {
361            let mut m = na::SimilarityMatrix3::<f32>::face_towards(&center, &lookat, &up, 1.0);
362            m = m
363                * na::Rotation3::<f32>::from_axis_angle(&na::Vector3::z_axis(), std::f32::consts::FRAC_PI_2)
364                * na::Rotation3::<f32>::from_axis_angle(&na::Vector3::x_axis(), std::f32::consts::FRAC_PI_2); //rotate 90 degrees
365            m
366        };
367
368        if transform_cpu_data {
369            //transform directly the verts
370            for mut vert in verts.row_iter_mut() {
371                let v_modif = model_matrix * na::Point3::from(vert.fixed_columns::<3>(0).transpose());
372                vert.copy_from_slice(v_modif.coords.as_slice());
373            }
374            //reset to identity
375            model_matrix = na::SimilarityMatrix3::<f32>::identity();
376        }
377        let verts_tensor = DynamicTensorFloat2D::from_dmatrix(&verts);
378        let edges_tensor = DynamicTensorInt2D::from_dmatrix(&edges);
379
380        let mut builder = EntityBuilder::new();
381        builder.add(Verts(verts_tensor)).add(Edges(edges_tensor)).add(ModelMatrix(model_matrix));
382
383        builder
384    }
385
386    pub fn build_from_file(path: &str) -> EntityBuilder {
387        //get filetype
388        let filetype = match Path::new(path).extension() {
389            Some(extension) => FileType::find_match(extension.to_str().unwrap_or("")),
390            None => FileType::Unknown,
391        };
392
393        #[allow(clippy::single_match_else)]
394        match filetype {
395            FileType::Obj => Self::build_from_obj(Path::new(path)),
396            FileType::Ply => Self::build_from_ply(Path::new(path)),
397            FileType::Unknown => {
398                error!("Could not read file {:?}", path);
399                EntityBuilder::new() //empty builder
400            }
401        }
402    }
403
404    /// # Panics
405    /// Will panic if the path cannot be opened
406    #[cfg(target_arch = "wasm32")]
407    pub async fn build_from_file_async(path: &str) -> EntityBuilder {
408        //get filetype
409        let filetype = match Path::new(path).extension() {
410            Some(extension) => FileType::find_match(extension.to_str().unwrap_or("")),
411            _ => FileType::Unknown,
412        };
413
414        match filetype {
415            FileType::Obj => Self::build_from_obj_async(Path::new(path)).await,
416            // FileType::Ply => (),
417            _ => {
418                error!("Could not read file {:?}", path);
419                EntityBuilder::new() //empty builder
420            }
421        }
422    }
423
424    /// # Panics
425    /// Will panic if the path cannot be opened
426    #[allow(clippy::unused_async)] //uses async for wasm
427    #[allow(clippy::identity_op)] //identity ops makes some things more explixit
428    fn build_from_obj(path: &Path) -> EntityBuilder {
429        info!("reading obj from {path:?}");
430
431        //Native read
432        let (models, _) = tobj::load_obj(path, &tobj::GPU_LOAD_OPTIONS).expect("Failed to OBJ load file");
433
434        Self::model_obj_to_entity_builder(&models)
435    }
436
437    /// # Panics
438    /// Will panic if the path cannot be opened
439    #[allow(clippy::unused_async)] //uses async for wasm
440    #[cfg(target_arch = "wasm32")]
441    #[allow(deprecated)]
442    async fn build_from_obj_async(path: &Path) -> EntityBuilder {
443        //WASM read
444        let mut file_wasm = gloss_utils::io::FileLoader::open(path.to_str().unwrap()).await;
445        let (models, _) = tobj::load_obj_buf_async(&mut file_wasm, &tobj::GPU_LOAD_OPTIONS, move |p| async move {
446            match p.as_str() {
447                _ => unreachable!(),
448            }
449        })
450        .await
451        .expect("Failed to OBJ load file");
452
453        Self::model_obj_to_entity_builder(&models)
454    }
455
456    /// # Panics
457    /// Will panic if the path cannot be opened
458    #[allow(clippy::unused_async)] //uses async for wasm
459    #[allow(clippy::identity_op)] //identity ops makes some things more explixit
460    pub fn build_from_obj_buf(buf: &[u8]) -> EntityBuilder {
461        let mut reader = std::io::BufReader::new(buf);
462
463        //Native read
464        let (models, _) = tobj::load_obj_buf(&mut reader, &tobj::GPU_LOAD_OPTIONS, move |_p| Err(tobj::LoadError::MaterialParseError))
465            .expect("Failed to OBJ load file");
466
467        Self::model_obj_to_entity_builder(&models)
468    }
469
470    #[allow(clippy::identity_op)] //identity ops makes some things more explixit
471    fn model_obj_to_entity_builder(models: &[tobj::Model]) -> EntityBuilder {
472        // fn model_obj_to_entity_builder(model: &ObjData) -> EntityBuilder{
473
474        let mesh = &models[0].mesh;
475        debug!("obj: nr indices {}", mesh.indices.len() / 3);
476        debug!("obj: nr positions {}", mesh.positions.len() / 3);
477        debug!("obj: nr normals {}", mesh.normals.len() / 3);
478        debug!("obj: nr texcoords {}", mesh.texcoords.len() / 2);
479
480        let nr_verts = mesh.positions.len() / 3;
481        let nr_faces = mesh.indices.len() / 3;
482        let nr_normals = mesh.normals.len() / 3;
483        let nr_texcoords = mesh.texcoords.len() / 2;
484
485        let mut builder = EntityBuilder::new();
486
487        if nr_verts > 0 {
488            debug!("read_obj: file has verts");
489            let verts = DMatrix::<f32>::from_row_slice(nr_verts, 3, mesh.positions.as_slice());
490            let verts_tensor = DynamicTensorFloat2D::from_dmatrix(&verts);
491            builder.add(Verts(verts_tensor));
492        }
493
494        if nr_faces > 0 {
495            debug!("read_obj: file has faces");
496            let faces = DMatrix::<u32>::from_row_slice(nr_faces, 3, mesh.indices.as_slice());
497            let faces_tensor = DynamicTensorInt2D::from_dmatrix(&faces);
498            builder.add(Faces(faces_tensor));
499        }
500
501        if nr_normals > 0 {
502            debug!("read_obj: file has normals");
503            let normals = DMatrix::<f32>::from_row_slice(nr_normals, 3, mesh.normals.as_slice());
504            let normals_tensor = DynamicTensorFloat2D::from_dmatrix(&normals);
505            builder.add(Normals(normals_tensor));
506        }
507
508        if nr_texcoords > 0 {
509            debug!("read_obj: file has texcoords");
510            let uv = DMatrix::<f32>::from_row_slice(nr_texcoords, 2, mesh.texcoords.as_slice());
511            let uvs_tensor = DynamicTensorFloat2D::from_dmatrix(&uv);
512            builder.add(UVs(uvs_tensor));
513        }
514
515        // if !mesh.faces_original_index.is_empty() {
516        //     builder.add(FacesOriginalIndex(mesh.faces_original_index.clone()));
517        // }
518
519        builder
520    }
521
522    pub fn save_obj(verts: &DMatrix<f32>, faces: Option<&DMatrix<u32>>, uv: Option<&DMatrix<f32>>, normals: Option<&DMatrix<f32>>, path: &str) {
523        let verts_obj: Vec<Vertex> = verts
524            .row_iter()
525            .map(|row| Vertex {
526                x: f64::from(row[0]),
527                y: f64::from(row[1]),
528                z: f64::from(row[2]),
529            })
530            .collect();
531        let faces_obj: Vec<Shape> = if let Some(faces) = faces {
532            faces
533                .row_iter()
534                .map(|row| Shape {
535                    primitive: Primitive::Triangle(
536                        // (row[0] as usize, Some(row[0] as usize), None),
537                        // (row[1] as usize, Some(row[1] as usize), None),
538                        // (row[2] as usize, Some(row[2] as usize), None),
539                        (row[0] as usize, uv.map(|_| row[0] as usize), normals.map(|_| row[0] as usize)),
540                        (row[1] as usize, uv.map(|_| row[1] as usize), normals.map(|_| row[1] as usize)),
541                        (row[2] as usize, uv.map(|_| row[2] as usize), normals.map(|_| row[2] as usize)),
542                    ),
543                    groups: vec![],
544                    smoothing_groups: vec![],
545                })
546                .collect()
547        } else {
548            Vec::new()
549        };
550        let uv_obj: Vec<TVertex> = if let Some(uv) = uv {
551            uv.row_iter()
552                .map(|row| TVertex {
553                    u: f64::from(row[0]),
554                    v: f64::from(row[1]),
555                    w: 0.0,
556                })
557                .collect()
558        } else {
559            Vec::<TVertex>::new()
560        };
561
562        let normals_obj: Vec<Vertex> = if let Some(normals) = normals {
563            normals
564                .row_iter()
565                .map(|row| Vertex {
566                    x: f64::from(row[0]),
567                    y: f64::from(row[1]),
568                    z: f64::from(row[2]),
569                })
570                .collect()
571        } else {
572            Vec::<Vertex>::new()
573        };
574
575        let set = ObjSet {
576            material_library: None,
577            objects: vec![Object {
578                name: "exported_mesh".to_owned(),
579                vertices: verts_obj,
580                tex_vertices: uv_obj,
581                normals: normals_obj,
582                geometry: vec![Geometry {
583                    material_name: None,
584                    shapes: faces_obj,
585                }],
586            }],
587        };
588
589        obj_exporter::export_to_file(&set, path).unwrap();
590    }
591
592    #[allow(clippy::too_many_lines)]
593    pub fn save_ply(
594        verts: &DMatrix<f32>,
595        faces: Option<&DMatrix<u32>>,
596        uvs: Option<&DMatrix<f32>>,
597        normals: Option<&DMatrix<f32>>,
598        colors: Option<&DMatrix<f32>>,
599        path: &str,
600    ) {
601        let ply_float_prop = ply::PropertyType::Scalar(ply::ScalarType::Float);
602        let ply_uchar_prop = ply::PropertyType::Scalar(ply::ScalarType::UChar);
603
604        #[allow(clippy::approx_constant)]
605        let mut ply = {
606            let mut ply = Ply::<ply::DefaultElement>::new();
607            // ply.header.encoding = ply::Encoding::BinaryLittleEndian;
608            ply.header.encoding = ply::Encoding::Ascii; //for some reason meshlab only opens these type of ply files
609            ply.header.comments.push("Gloss Ply file".to_string());
610
611            // Define the elements we want to write. In our case we write a 2D Point.
612            // When writing, the `count` will be set automatically to the correct value by
613            // calling `make_consistent`
614            let mut point_element = ply::ElementDef::new("vertex".to_string());
615            let p = ply::PropertyDef::new("x".to_string(), ply_float_prop.clone());
616            point_element.properties.add(p);
617            let p = ply::PropertyDef::new("y".to_string(), ply_float_prop.clone());
618            point_element.properties.add(p);
619            let p = ply::PropertyDef::new("z".to_string(), ply_float_prop.clone());
620            point_element.properties.add(p);
621
622            if normals.is_some() {
623                let p = ply::PropertyDef::new("nx".to_string(), ply_float_prop.clone());
624                point_element.properties.add(p);
625                let p = ply::PropertyDef::new("ny".to_string(), ply_float_prop.clone());
626                point_element.properties.add(p);
627                let p = ply::PropertyDef::new("nz".to_string(), ply_float_prop.clone());
628                point_element.properties.add(p);
629            }
630
631            //neds to be called s,t because that's what blender expects
632            if uvs.is_some() {
633                let p = ply::PropertyDef::new("s".to_string(), ply_float_prop.clone());
634                point_element.properties.add(p);
635                let p = ply::PropertyDef::new("t".to_string(), ply_float_prop.clone());
636                point_element.properties.add(p);
637            }
638
639            //neds to be called s,t because that's what blender expects
640            if colors.is_some() {
641                let p = ply::PropertyDef::new("red".to_string(), ply_uchar_prop.clone());
642                point_element.properties.add(p);
643                let p = ply::PropertyDef::new("green".to_string(), ply_uchar_prop.clone());
644                point_element.properties.add(p);
645                let p = ply::PropertyDef::new("blue".to_string(), ply_uchar_prop.clone());
646                point_element.properties.add(p);
647            }
648
649            ply.header.elements.add(point_element);
650
651            //face
652            let mut face_element = ply::ElementDef::new("face".to_string());
653            //x
654            let f = ply::PropertyDef::new(
655                "vertex_indices".to_string(),
656                ply::PropertyType::List(ply::ScalarType::UChar, ply::ScalarType::UInt), //has to be kept as uchar, uint for meshlab to read it
657            );
658            face_element.properties.add(f);
659            ply.header.elements.add(face_element);
660
661            // Add points
662            let mut points_list = Vec::new();
663            for (idx, vert) in verts.row_iter().enumerate() {
664                let mut point_elem = ply::DefaultElement::new();
665                point_elem.insert("x".to_string(), ply::Property::Float(vert[0]));
666                point_elem.insert("y".to_string(), ply::Property::Float(vert[1]));
667                point_elem.insert("z".to_string(), ply::Property::Float(vert[2]));
668
669                if let Some(normals) = normals {
670                    let normal = normals.row(idx);
671                    point_elem.insert("nx".to_string(), ply::Property::Float(normal[0]));
672                    point_elem.insert("ny".to_string(), ply::Property::Float(normal[1]));
673                    point_elem.insert("nz".to_string(), ply::Property::Float(normal[2]));
674                }
675
676                if let Some(uvs) = uvs {
677                    let uv = uvs.row(idx);
678                    point_elem.insert("s".to_string(), ply::Property::Float(uv[0]));
679                    point_elem.insert("t".to_string(), ply::Property::Float(uv[1]));
680                }
681
682                #[allow(clippy::cast_sign_loss)]
683                if let Some(colors) = colors {
684                    let color = colors.row(idx);
685                    #[allow(clippy::cast_possible_truncation)]
686                    point_elem.insert("red".to_string(), ply::Property::UChar((color[0] * 255.0) as u8));
687                    #[allow(clippy::cast_possible_truncation)]
688                    point_elem.insert("green".to_string(), ply::Property::UChar((color[1] * 255.0) as u8));
689                    #[allow(clippy::cast_possible_truncation)]
690                    point_elem.insert("blue".to_string(), ply::Property::UChar((color[2] * 255.0) as u8));
691                }
692
693                points_list.push(point_elem);
694            }
695            ply.payload.insert("vertex".to_string(), points_list);
696
697            // Add faces
698            if let Some(faces) = faces {
699                let mut faces_list = Vec::new();
700                for face in faces.row_iter() {
701                    let mut face_elem = ply::DefaultElement::new();
702                    face_elem.insert("vertex_indices".to_string(), ply::Property::ListUInt(face.iter().copied().collect()));
703                    faces_list.push(face_elem);
704                }
705                ply.payload.insert("face".to_string(), faces_list);
706            }
707
708            // only `write_ply` calls this by itself, for all other methods the client is
709            // responsible to make the data structure consistent.
710            // We do it here for demonstration purpose.
711            ply.make_consistent().unwrap();
712            ply
713        };
714
715        let mut file = std::fs::File::create(path).unwrap();
716        let w = ply_rs::writer::Writer::new();
717        let written = w.write_ply(&mut file, &mut ply).unwrap();
718        println!("{written} bytes written");
719    }
720
721    /// # Panics
722    /// Will panic if the path cannot be opened
723    #[allow(clippy::unused_async)] //uses async for wasm
724    #[allow(clippy::identity_op)] //identity ops makes some things more explixit
725    #[allow(clippy::too_many_lines)] //identity ops makes some things more explixit
726    fn build_from_ply(path: &Path) -> EntityBuilder {
727        #[derive(Debug, Default)]
728        pub struct Vertex {
729            pos: Vec3,
730            color: Vec3,
731            normal: Vec3,
732            uv: Vec2,
733        }
734
735        #[derive(Debug)]
736        pub struct Face {
737            vertex_index: Vec<u32>,
738        }
739
740        // The structs need to implement the PropertyAccess trait, otherwise the parser
741        // doesn't know how to write to them. Most functions have default, hence
742        // you only need to implement, what you expect to need.
743        impl ply::PropertyAccess for Vertex {
744            fn new() -> Self {
745                Self::default()
746            }
747            fn set_property(&mut self, key: String, property: ply::Property) {
748                match (key.as_ref(), property) {
749                    ("x", ply::Property::Float(v)) => self.pos.x = v,
750                    ("y", ply::Property::Float(v)) => self.pos.y = v,
751                    ("z", ply::Property::Float(v)) => self.pos.z = v,
752                    ("red", ply::Property::UChar(v)) => {
753                        self.color.x = f32::from(v) / 255.0;
754                    }
755                    ("green", ply::Property::UChar(v)) => self.color.y = f32::from(v) / 255.0,
756                    ("blue", ply::Property::UChar(v)) => self.color.z = f32::from(v) / 255.0,
757                    //normal
758                    ("nx", ply::Property::Float(v)) => self.normal.x = v,
759                    ("ny", ply::Property::Float(v)) => self.normal.y = v,
760                    ("nz", ply::Property::Float(v)) => self.normal.z = v,
761                    //uv
762                    ("u" | "s", ply::Property::Float(v)) => self.uv.x = v,
763                    ("v" | "t", ply::Property::Float(v)) => self.uv.y = v,
764                    // (k, _) => panic!("Vertex: Unexpected key/value combination: key: {}", k),
765                    // (k, prop) => {println!("unknown key {} of type {:?}", k, prop)},
766                    (k, prop) => {
767                        warn!("unknown key {} of type {:?}", k, prop);
768                    }
769                }
770            }
771        }
772
773        // same thing for Face
774        impl ply::PropertyAccess for Face {
775            fn new() -> Self {
776                Face { vertex_index: Vec::new() }
777            }
778            #[allow(clippy::cast_sign_loss)]
779            fn set_property(&mut self, key: String, property: ply::Property) {
780                match (key.as_ref(), property.clone()) {
781                    ("vertex_indices" | "vertex_index", ply::Property::ListInt(vec)) => {
782                        self.vertex_index = vec.iter().map(|x| *x as u32).collect();
783                    }
784                    ("vertex_indices" | "vertex_index", ply::Property::ListUInt(vec)) => {
785                        self.vertex_index = vec;
786                    }
787                    (k, _) => {
788                        panic!("Face: Unexpected key/value combination: key, val: {k} {property:?}")
789                    }
790                }
791            }
792        }
793
794        info!("reading ply from {path:?}");
795        // set up a reader, in this a file.
796        let f = std::fs::File::open(path).unwrap();
797        // The header of a ply file consists of ascii lines, BufRead provides useful
798        // methods for that.
799        let mut f = std::io::BufReader::new(f);
800
801        // Create a parser for each struct. Parsers are cheap objects.
802        let vertex_parser = parser::Parser::<Vertex>::new();
803        let face_parser = parser::Parser::<Face>::new();
804
805        // lets first consume the header
806        // We also could use `face_parser`, The configuration is a parser's only state.
807        // The reading position only depends on `f`.
808        let header = vertex_parser.read_header(&mut f).unwrap();
809
810        // Depending on the header, read the data into our structs..
811        let mut vertex_list = Vec::new();
812        let mut face_list = Vec::new();
813        for (_ignore_key, element) in &header.elements {
814            // we could also just parse them in sequence, but the file format might change
815            match element.name.as_ref() {
816                "vertex" | "point" => {
817                    vertex_list = vertex_parser.read_payload_for_element(&mut f, element, &header).unwrap();
818                }
819                "face" => {
820                    face_list = face_parser.read_payload_for_element(&mut f, element, &header).unwrap();
821                }
822                unknown_name => panic!("Unexpected element! {unknown_name}"),
823            }
824        }
825
826        let mut builder = EntityBuilder::new();
827
828        //pos
829        let mut verts = DMatrix::<f32>::zeros(vertex_list.len(), 3);
830        for (idx, v) in vertex_list.iter().enumerate() {
831            verts.row_mut(idx)[0] = v.pos.x;
832            verts.row_mut(idx)[1] = v.pos.y;
833            verts.row_mut(idx)[2] = v.pos.z;
834        }
835        let verts_tensor = DynamicTensorFloat2D::from_dmatrix(&verts);
836        builder.add(Verts(verts_tensor));
837        //color
838        let mut colors = DMatrix::<f32>::zeros(vertex_list.len(), 3);
839        for (idx, v) in vertex_list.iter().enumerate() {
840            colors.row_mut(idx)[0] = v.color.x;
841            colors.row_mut(idx)[1] = v.color.y;
842            colors.row_mut(idx)[2] = v.color.z;
843        }
844        //TODO need a better way to detect if we actually have color info
845        if colors.min() != 0.0 || colors.max() != 0.0 {
846            debug!("read_ply: file has colors");
847            let colors_tensor = DynamicTensorFloat2D::from_dmatrix(&colors);
848            builder.add(Colors(colors_tensor));
849        }
850        //normal
851        let mut normals = DMatrix::<f32>::zeros(vertex_list.len(), 3);
852        for (idx, v) in vertex_list.iter().enumerate() {
853            normals.row_mut(idx)[0] = v.normal.x;
854            normals.row_mut(idx)[1] = v.normal.y;
855            normals.row_mut(idx)[2] = v.normal.z;
856        }
857        //TODO need a better way to detect if we actually have normal info
858        if normals.min() != 0.0 || normals.max() != 0.0 {
859            debug!("read_ply: file has normals");
860            let normals_tensor = DynamicTensorFloat2D::from_dmatrix(&normals);
861            builder.add(Normals(normals_tensor));
862        }
863        //uv
864        let mut uvs = DMatrix::<f32>::zeros(vertex_list.len(), 2);
865        for (idx, v) in vertex_list.iter().enumerate() {
866            uvs.row_mut(idx)[0] = v.uv.x;
867            uvs.row_mut(idx)[1] = v.uv.y;
868        }
869        //TODO need a better way to detect if we actually have normal info
870        if uvs.min() != 0.0 || uvs.max() != 0.0 {
871            debug!("read_ply: file has uvs");
872            let uvs_tensor = DynamicTensorFloat2D::from_dmatrix(&uvs);
873            builder.add(UVs(uvs_tensor));
874        }
875
876        if !face_list.is_empty() {
877            debug!("read_ply: file has verts");
878            let mut faces = DMatrix::<u32>::zeros(face_list.len(), 3);
879            #[allow(clippy::cast_sign_loss)]
880            for (idx, f) in face_list.iter().enumerate() {
881                faces.row_mut(idx)[0] = f.vertex_index[0];
882                faces.row_mut(idx)[1] = f.vertex_index[1];
883                faces.row_mut(idx)[2] = f.vertex_index[2];
884            }
885            let faces_tensor = DynamicTensorInt2D::from_dmatrix(&faces);
886
887            builder.add(Faces(faces_tensor));
888        }
889
890        builder
891    }
892
893    /// Computes per vertex normals
894    pub fn compute_per_vertex_normals(
895        verts: &na::DMatrix<f32>,
896        faces: &na::DMatrix<u32>,
897        weighting_type: &PerVertexNormalsWeightingType,
898    ) -> na::DMatrix<f32> {
899        match weighting_type {
900            PerVertexNormalsWeightingType::Uniform => {
901                let faces_row_major = faces.transpose(); //for more efficient row iteration
902                let verts_row_major = verts.transpose(); //for more efficient row iteration
903                let mut per_vertex_normal_row_major = DMatrix::<f32>::zeros(3, verts.nrows());
904                for face in faces_row_major.column_iter() {
905                    // let v0 = verts_row_major.column(face[0] as usize);
906                    // let v1 = verts_row_major.column(face[1] as usize);
907                    // let v2 = verts_row_major.column(face[2] as usize);
908
909                    let v0 = verts_row_major.fixed_view::<3, 1>(0, face[0] as usize);
910                    let v1 = verts_row_major.fixed_view::<3, 1>(0, face[1] as usize);
911                    let v2 = verts_row_major.fixed_view::<3, 1>(0, face[2] as usize);
912                    let d1 = v1 - v0;
913                    let d2 = v2 - v0;
914                    let face_normal = d1.cross(&d2).normalize();
915                    //splat to vertices
916                    per_vertex_normal_row_major.column_mut(face[0] as usize).add_assign(&face_normal);
917                    per_vertex_normal_row_major.column_mut(face[1] as usize).add_assign(&face_normal);
918                    per_vertex_normal_row_major.column_mut(face[2] as usize).add_assign(&face_normal);
919                }
920                // take average via normalization
921                for mut row in per_vertex_normal_row_major.column_iter_mut() {
922                    row /= row.norm();
923                }
924                per_vertex_normal_row_major.transpose()
925            }
926            PerVertexNormalsWeightingType::Area => {
927                //compute per face normals
928                let face_normals = Geom::compute_per_face_normals(verts, faces);
929                //calculate weights for each face that depend on the area.
930                let w = Geom::compute_double_face_areas(verts, faces);
931                //per vertex
932                let mut per_vertex_normal = DMatrix::<f32>::zeros(verts.nrows(), 3);
933                for ((face, face_normal), &face_weight) in faces.row_iter().zip(face_normals.row_iter()).zip(w.iter()) {
934                    for &v_idx in face.iter() {
935                        per_vertex_normal.row_mut(v_idx as usize).add_assign(face_weight * face_normal);
936                    }
937                }
938                // take average via normalization
939                for mut row in per_vertex_normal.row_iter_mut() {
940                    row /= row.norm();
941                }
942                per_vertex_normal
943            }
944        }
945    }
946
947    /// Computes per vertex normals for Burn Tensors
948    pub fn compute_per_vertex_normals_burn<B: Backend>(
949        verts: &Tensor<B, 2, Float>, // Tensor of shape [NUM_VERTS, 3]
950        faces: &Tensor<B, 2, Int>,   // Tensor of shape [NUM_FACES, 3]
951        weighting_type: &PerVertexNormalsWeightingType,
952    ) -> Tensor<B, 2, Float> {
953        let num_verts = verts.shape().dims[0];
954        let num_faces = faces.shape().dims[0];
955        let mut per_vertex_normals = Tensor::<B, 2, Float>::zeros([num_verts, 3], &verts.device());
956        // let now = wasm_timer::Instant::now();
957        match weighting_type {
958            PerVertexNormalsWeightingType::Uniform => {
959                let all_idxs: Tensor<B, 1, Int> = faces.clone().flatten(0, 1);
960                let all_tris_as_verts = verts.clone().select(0, all_idxs).reshape([num_faces, 3, 3]);
961                let all_tris_as_verts_chunks = all_tris_as_verts.chunk(3, 1); // Split the tensor along the second dimension (3 components)
962                                                                              // println!("2 ---- {:?}", now.elapsed());
963
964                // Now assign each chunk to v0, v1, v2
965                let v0 = all_tris_as_verts_chunks[0].clone().squeeze(1); // First chunk (v0) [num_faces, 3]
966                let v1 = all_tris_as_verts_chunks[1].clone().squeeze(1); // Second chunk (v1) [num_faces, 3]
967                let v2 = all_tris_as_verts_chunks[2].clone().squeeze(1); // Third chunk (v2) [num_faces, 3]
968                                                                         // println!("3 ---- {:?}", now.elapsed());
969
970                // Compute v1 - v0 and v2 - v0
971                let d1 = v1.sub(v0.clone()); // Shape: [num_faces, 3]
972                let d2 = v2.sub(v0.clone()); // Shape: [num_faces, 3]
973                                             // println!("4 ---- {:?}", now.elapsed());
974
975                // Perform batch cross product between d1 and d2
976                let cross_product = tensor::cross_product(&d1, &d2); // Shape: [num_faces, 3]
977                let face_normals = tensor::normalize_tensor(cross_product);
978
979                // Scatter the face normals back into the per-vertex normals tensor
980                let face_indices_expanded: Tensor<B, 1, Int> = faces.clone().flatten(0, 1); // Shape [num_faces * 3]
981                let mut face_normals_repeated: Tensor<B, 3> = face_normals.unsqueeze_dim(1);
982
983                face_normals_repeated = face_normals_repeated.repeat(&[1, 3, 1]);
984
985                let face_normals_to_scatter = face_normals_repeated.reshape([num_faces * 3, 3]); // Repeat face normals for each vertex
986                                                                                                 // println!("8 ---- {:?}", now.elapsed());
987
988                per_vertex_normals = per_vertex_normals.select_assign(0, face_indices_expanded, face_normals_to_scatter);
989                // Normalize the per-vertex normals to get the average
990                per_vertex_normals = tensor::normalize_tensor(per_vertex_normals);
991                per_vertex_normals
992            }
993            PerVertexNormalsWeightingType::Area => {
994                ////////////////////////////////////////////////////////////////////////
995                let all_idxs: Tensor<B, 1, Int> = faces.clone().flatten(0, 1);
996                let all_tris_as_verts = verts.clone().select(0, all_idxs).reshape([num_faces, 3, 3]);
997                let all_tris_as_verts_chunks = all_tris_as_verts.chunk(3, 1); // Split into v0, v1, v2
998
999                let v0 = all_tris_as_verts_chunks[0].clone().squeeze(1); // [num_faces, 3]
1000                let v1 = all_tris_as_verts_chunks[1].clone().squeeze(1); // [num_faces, 3]
1001                let v2 = all_tris_as_verts_chunks[2].clone().squeeze(1); // [num_faces, 3]
1002
1003                let d1 = v1.sub(v0.clone()); // [num_faces, 3]
1004                let d2 = v2.sub(v0.clone()); // [num_faces, 3]
1005
1006                let face_normals = tensor::cross_product(&d1, &d2); // [num_faces, 3]
1007
1008                let face_areas = face_normals.clone().powf_scalar(2.0).sum_dim(1).sqrt(); // [num_faces]
1009
1010                let weighted_face_normals = face_normals.div(face_areas); // [num_faces, 3]
1011
1012                let face_indices_expanded: Tensor<B, 1, Int> = faces.clone().flatten(0, 1); // [num_faces * 3]
1013
1014                let mut weighted_face_normals_repeated: Tensor<B, 3> = weighted_face_normals.unsqueeze_dim(1);
1015                weighted_face_normals_repeated = weighted_face_normals_repeated.repeat(&[1, 3, 1]);
1016
1017                let weighted_normals_to_scatter = weighted_face_normals_repeated.reshape([num_faces * 3, 3]);
1018
1019                per_vertex_normals = per_vertex_normals.select_assign(0, face_indices_expanded, weighted_normals_to_scatter);
1020
1021                per_vertex_normals = tensor::normalize_tensor(per_vertex_normals);
1022                per_vertex_normals
1023            }
1024        }
1025    }
1026
1027    /// Computes per face normals
1028    pub fn compute_per_face_normals(verts: &na::DMatrix<f32>, faces: &na::DMatrix<u32>) -> na::DMatrix<f32> {
1029        let verts_row_major = verts.transpose(); //for more efficient row iteration
1030                                                 // let mut face_normals = DMatrix::<f32>::zeros(faces.nrows(), 3);
1031        let mut face_normals = unsafe { DMatrix::<core::mem::MaybeUninit<f32>>::uninit(na::Dyn(faces.nrows()), na::Dyn(3)).assume_init() };
1032
1033        for (face, mut face_normal) in faces.row_iter().zip(face_normals.row_iter_mut()) {
1034            // let v0 = verts.row(face[0] as usize);
1035            // let v1 = verts.row(face[1] as usize);
1036            // let v2 = verts.row(face[2] as usize);
1037            let v0 = verts_row_major.fixed_view::<3, 1>(0, face[0] as usize);
1038            let v1 = verts_row_major.fixed_view::<3, 1>(0, face[1] as usize);
1039            let v2 = verts_row_major.fixed_view::<3, 1>(0, face[2] as usize);
1040            let d1 = v1 - v0;
1041            let d2 = v2 - v0;
1042            let normal = d1.cross(&d2).normalize();
1043            //TODO make the face normals to be row major and transpose after we have
1044            // written to them
1045            face_normal.copy_from(&normal.transpose());
1046        }
1047
1048        face_normals
1049    }
1050
1051    ///computes twice the area for each input triangle[quad]
1052    pub fn compute_double_face_areas(verts: &na::DMatrix<f32>, faces: &na::DMatrix<u32>) -> na::DMatrix<f32> {
1053        //helper function
1054        let proj_doublearea =
1055            // |verts: &na::DMatrix<f32>, faces: &na::DMatrix<f32>, x, y, f| -> f32 { 3.0 };
1056            |x, y, f| -> f32 {
1057                let fx = faces[(f,0)] as usize;
1058                let fy = faces[(f,1)] as usize;
1059                let fz = faces[(f,2)] as usize;
1060                let rx = verts[(fx,x)]-verts[(fz,x)];
1061                let sx = verts[(fy,x)]-verts[(fz,x)];
1062                let ry = verts[(fx,y)]-verts[(fz,y)];
1063                let sy = verts[(fy,y)]-verts[(fz,y)];
1064                rx*sy - ry*sx
1065            };
1066
1067        let mut double_face_areas = DMatrix::<f32>::zeros(faces.nrows(), 1);
1068
1069        for f in 0..faces.nrows() {
1070            for d in 0..3 {
1071                let double_area = proj_doublearea(d, (d + 1) % 3, f);
1072                double_face_areas[(f, 0)] += double_area * double_area;
1073            }
1074        }
1075        //sqrt for every value
1076        double_face_areas = double_face_areas.map(f32::sqrt);
1077
1078        double_face_areas
1079    }
1080
1081    #[allow(clippy::similar_names)]
1082    /// Compute tangents given verts, faces, normals and uvs
1083    pub fn compute_tangents(
1084        verts: &na::DMatrix<f32>,
1085        faces: &na::DMatrix<u32>,
1086        normals: &na::DMatrix<f32>,
1087        uvs: &na::DMatrix<f32>,
1088    ) -> na::DMatrix<f32> {
1089        //if we have UV per vertex then we can calculate a tangent that is aligned with
1090        // the U direction code from http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/
1091        //more explanation in https://learnopengl.com/Advanced-Lighting/Normal-Mapping
1092        // https://gamedev.stackexchange.com/questions/68612/how-to-compute-tangent-and-bitangent-vectors
1093
1094        // let mut tangents = DMatrix::<f32>::zeros(verts.nrows(), 3);
1095        // let mut handness = DMatrix::<f32>::zeros(verts.nrows(), 1);
1096        // let mut bitangents = DMatrix::<f32>::zeros(verts.nrows(), 3);
1097        // let mut degree_vertices = vec![0; verts.nrows()];
1098
1099        //convert to rowmajor for more efficient traversal
1100        let faces_row_major = faces.transpose(); //for more efficient row iteration
1101        let verts_row_major = verts.transpose(); //for more efficient row iteration
1102        let normals_row_major = normals.transpose(); //for more efficient row iteration
1103        let uvs_row_major = uvs.transpose(); //for more efficient row iteration
1104        let mut tangents_rm = DMatrix::<f32>::zeros(3, verts.nrows());
1105        let mut handness_rm = DMatrix::<f32>::zeros(1, verts.nrows());
1106        let mut bitangents_rm = DMatrix::<f32>::zeros(3, verts.nrows());
1107
1108        //put verts and uv together so it's faster to sample
1109        let mut v_uv_rm = DMatrix::<f32>::zeros(3 + 2, verts.nrows());
1110        v_uv_rm.view_mut((0, 0), (3, verts.nrows())).copy_from(&verts_row_major);
1111        v_uv_rm.view_mut((3, 0), (2, verts.nrows())).copy_from(&uvs_row_major);
1112
1113        for face in faces_row_major.column_iter() {
1114            // //increase the degree for the vertices we touch
1115            // degree_vertices[face[0] as usize]+=1;
1116            // degree_vertices[face[1] as usize]+=1;
1117            // degree_vertices[face[2] as usize]+=1;
1118            let id0 = face[0] as usize;
1119            let id1 = face[1] as usize;
1120            let id2 = face[2] as usize;
1121
1122            // let v0 = verts_row_major.column(id0);
1123            // let v1 = verts_row_major.column(id1);
1124            // let v2 = verts_row_major.column(id2);
1125
1126            // let uv0 = uvs_row_major.column(id0);
1127            // let uv1 = uvs_row_major.column(id1);
1128            // let uv2 = uvs_row_major.column(id2);
1129
1130            //sampel vertex position and uvs
1131            // let v_uv0 = v_uv_rm.column(id0);
1132            // let v_uv1 = v_uv_rm.column(id1);
1133            // let v_uv2 = v_uv_rm.column(id2);
1134            let v_uv0 = v_uv_rm.fixed_view::<5, 1>(0, id0);
1135            let v_uv1 = v_uv_rm.fixed_view::<5, 1>(0, id1);
1136            let v_uv2 = v_uv_rm.fixed_view::<5, 1>(0, id2);
1137
1138            //get vert and uv
1139            let v0 = v_uv0.fixed_rows::<3>(0);
1140            let v1 = v_uv1.fixed_rows::<3>(0);
1141            let v2 = v_uv2.fixed_rows::<3>(0);
1142            //uv
1143            let uv0 = v_uv0.fixed_rows::<2>(3);
1144            let uv1 = v_uv1.fixed_rows::<2>(3);
1145            let uv2 = v_uv2.fixed_rows::<2>(3);
1146
1147            // Edges of the triangle : position delta
1148            let delta_pos1 = v1 - v0;
1149            let delta_pos2 = v2 - v0;
1150
1151            // UV delta
1152            let delta_uv1 = uv1 - uv0;
1153            let delta_uv2 = uv2 - uv0;
1154
1155            let r = 1.0 / (delta_uv1[0] * delta_uv2[1] - delta_uv1[1] * delta_uv2[0]);
1156            let tangent = (delta_pos1 * delta_uv2[1] - delta_pos2 * delta_uv1[1]) * r;
1157            let bitangent = (delta_pos2 * delta_uv1[0] - delta_pos1 * delta_uv2[0]) * r;
1158
1159            //splat to vertices
1160            //it can happen that delta_uv1 and delta_uv2 is zero (in the case where there
1161            // is a degenerate face that has overlapping vertices) and in that case the norm
1162            // of the tangent would nan, so we don't splat that one
1163            if r.is_finite() {
1164                tangents_rm.column_mut(id0).add_assign(&tangent);
1165                tangents_rm.column_mut(id1).add_assign(&tangent);
1166                tangents_rm.column_mut(id2).add_assign(&tangent);
1167                bitangents_rm.column_mut(id0).add_assign(&bitangent);
1168                bitangents_rm.column_mut(id1).add_assign(&bitangent);
1169                bitangents_rm.column_mut(id2).add_assign(&bitangent);
1170            }
1171        }
1172
1173        // for (idx, ((mut tangent,normal), bitangent)) in
1174        // tangents.row_iter_mut().zip(normals.row_iter()).zip(bitangents.row_iter()).
1175        // enumerate(){
1176        for (idx, ((mut tangent, normal), bitangent)) in tangents_rm
1177            .column_iter_mut()
1178            .zip(normals_row_major.column_iter())
1179            .zip(bitangents_rm.column_iter())
1180            .enumerate()
1181        {
1182            // https://foundationsofgameenginedev.com/FGED2-sample.pdf
1183            let dot = tangent.dot(&normal);
1184            // Gram-Schmidt orthogonalize
1185            let new_tangent = (&tangent - normal * dot).normalize();
1186            // Calculate handedness
1187            let cross_vec = tangent.cross(&bitangent);
1188            let dot: f32 = cross_vec.dot(&normal);
1189            handness_rm.column_mut(idx).fill(dot.signum()); //if >0 we write a 1.0, else we write a -1.0
1190
1191            tangent.copy_from_slice(new_tangent.data.as_slice());
1192            // tangent[0] = new_tangent[0];
1193            // tangent[1] = new_tangent[1];
1194            // tangent[2] = new_tangent[2];
1195        }
1196
1197        // let mut tangents_and_handness = DMatrix::<f32>::zeros(verts.nrows(), 4);
1198        // let mut tangents_and_handness_rm = DMatrix::<f32>::zeros(4, verts.nrows());
1199        // // for i in (0..verts.nrows()){
1200        // for ((t, h), mut out) in tangents_rm
1201        //     .column_iter()
1202        //     .zip(handness_rm.iter())
1203        //     .zip(tangents_and_handness_rm.column_iter_mut())
1204        // {
1205        //     out[0] = t[0];
1206        //     out[1] = t[1];
1207        //     out[2] = t[2];
1208        //     out[3] = *h;
1209        // }
1210
1211        let mut tangents_and_handness_rm = DMatrix::<f32>::zeros(4, verts.nrows());
1212        tangents_and_handness_rm.view_mut((0, 0), (3, verts.nrows())).copy_from(&tangents_rm);
1213        tangents_and_handness_rm.view_mut((3, 0), (1, verts.nrows())).copy_from(&handness_rm);
1214
1215        tangents_and_handness_rm.transpose()
1216    }
1217
1218    #[allow(clippy::similar_names)]
1219    #[allow(clippy::too_many_lines)]
1220    /// Compute tangents given verts, faces, normals and uvs for Burn Tensors
1221    pub fn compute_tangents_burn<B: Backend>(
1222        verts: &Tensor<B, 2, Float>,   // Vertex positions (NUM_VERTS, 3)
1223        faces: &Tensor<B, 2, Int>,     // Faces (NUM_FACES, 3)
1224        normals: &Tensor<B, 2, Float>, // Normals (NUM_VERTS, 3)
1225        uvs: &Tensor<B, 2, Float>,     // UV coordinates (NUM_VERTS, 2)
1226    ) -> Tensor<B, 2, Float> {
1227        // We might be able to optimise a lot of stuff here
1228        // A lot of these operations are quite slow for Cpu backends (NdArray and
1229        // Candle)
1230
1231        let num_faces = faces.shape().dims[0];
1232        let num_verts = verts.shape().dims[0];
1233        // let now = wasm_timer::Instant::now();
1234
1235        // Flatten faces to extract indices // similar to per vert normals
1236        let all_idxs: Tensor<B, 1, Int> = faces.clone().flatten(0, 1);
1237        // println!("7.0 -- {:?}", now.elapsed());
1238
1239        // Extract vertices and UVs corresponding to face indices
1240        let all_tris_as_verts = verts.clone().select(0, all_idxs.clone()).reshape([num_faces, 3, 3]);
1241        let all_tris_as_uvs = uvs.clone().select(0, all_idxs.clone()).reshape([num_faces, 3, 2]);
1242        // println!("7.1 -- {:?}", now.elapsed());
1243
1244        let v0: Tensor<B, 2> = all_tris_as_verts.clone().slice([0..num_faces, 0..1, 0..3]).squeeze(1);
1245        let v1: Tensor<B, 2> = all_tris_as_verts.clone().slice([0..num_faces, 1..2, 0..3]).squeeze(1);
1246        let v2: Tensor<B, 2> = all_tris_as_verts.clone().slice([0..num_faces, 2..3, 0..3]).squeeze(1);
1247        // println!("7.2 -- {:?}", now.elapsed());
1248
1249        let uv0 = all_tris_as_uvs.clone().slice([0..num_faces, 0..1, 0..2]).squeeze(1);
1250        let uv1 = all_tris_as_uvs.clone().slice([0..num_faces, 1..2, 0..2]).squeeze(1);
1251        let uv2 = all_tris_as_uvs.clone().slice([0..num_faces, 2..3, 0..2]).squeeze(1);
1252
1253        let delta_pos1 = v1.clone().sub(v0.clone());
1254        let delta_pos2 = v2.clone().sub(v0.clone());
1255
1256        let delta_uv1 = uv1.clone().sub(uv0.clone());
1257        let delta_uv2 = uv2.clone().sub(uv0.clone());
1258        // println!("7.3 -- {:?}", now.elapsed());
1259
1260        let delta_uv1_0 = delta_uv1.clone().select(1, Tensor::<B, 1, Int>::from_ints([0], &verts.device())); // delta_uv1[0]
1261        let delta_uv1_1 = delta_uv1.clone().select(1, Tensor::<B, 1, Int>::from_ints([1], &verts.device())); // delta_uv1[1]
1262
1263        let delta_uv2_0 = delta_uv2.clone().select(1, Tensor::<B, 1, Int>::from_ints([0], &verts.device())); // delta_uv2[0]
1264        let delta_uv2_1 = delta_uv2.clone().select(1, Tensor::<B, 1, Int>::from_ints([1], &verts.device())); // delta_uv2[1]
1265                                                                                                             // println!("7.4 -- {:?}", now.elapsed());
1266
1267        let denominator = delta_uv1_0
1268            .clone()
1269            .mul(delta_uv2_1.clone())
1270            .sub(delta_uv1_1.clone().mul(delta_uv2_0.clone()));
1271
1272        let r_mask = denominator.clone().abs().greater_elem(f32::EPSILON).float(); // Only consider denominators that are not too small
1273        let r = denominator.recip().mul(r_mask.clone()); // Apply the mask to the reciprocal
1274                                                         // println!("7.5 -- {:?}", now.elapsed());
1275
1276        let tangent = delta_pos1
1277            .clone()
1278            .mul(delta_uv2_1.clone().repeat(&[1, 3]))
1279            .sub(delta_pos2.clone().mul(delta_uv1_1.clone().repeat(&[1, 3])))
1280            .mul(r.clone());
1281        // println!("7.6 -- {:?}", now.elapsed());
1282
1283        let bitangent = delta_pos2
1284            .clone()
1285            .mul(delta_uv1_0.clone())
1286            .sub(delta_pos1.clone().mul(delta_uv2_0.clone()))
1287            .mul(r.clone()); // mask the inf values here itself
1288                             // println!("7.7 -- {:?}", now.elapsed());
1289
1290        let mut tangents_rm = Tensor::<B, 2, Float>::zeros([verts.dims()[0], 3], &verts.device());
1291        let mut handness_rm = Tensor::<B, 2, Float>::zeros([verts.dims()[0], 1], &verts.device());
1292        let mut bitangents_rm = Tensor::<B, 2, Float>::zeros([verts.dims()[0], 3], &verts.device());
1293        let face_indices_expanded: Tensor<B, 1, Int> = faces.clone().flatten(0, 1); // Shape [num_faces * 3]
1294                                                                                    // println!("7.8 -- {:?}", now.elapsed());
1295
1296        let mut face_tangents_repeated: Tensor<B, 3> = tangent.unsqueeze_dim(1);
1297        face_tangents_repeated = face_tangents_repeated.repeat(&[1, 3, 1]);
1298
1299        let face_tangents_to_scatter = face_tangents_repeated.reshape([num_faces * 3, 3]); // Repeat face normals for each vertex
1300                                                                                           // println!("7.9 -- {:?}", now.elapsed());
1301
1302        tangents_rm = tangents_rm.select_assign(0, face_indices_expanded.clone(), face_tangents_to_scatter.clone());
1303        // println!("7.9.1 -- {:?}", now.elapsed());
1304
1305        let mut face_bitangents_repeated: Tensor<B, 3> = bitangent.unsqueeze_dim(1);
1306        face_bitangents_repeated = face_bitangents_repeated.repeat(&[1, 3, 1]);
1307
1308        let face_bitangents_to_scatter = face_bitangents_repeated.reshape([num_faces * 3, 3]); // Repeat face normals for each vertex
1309                                                                                               // println!("7.10 -- {:?}", now.elapsed());
1310
1311        bitangents_rm = bitangents_rm.select_assign(0, face_indices_expanded, face_bitangents_to_scatter.clone());
1312        let dot_product = tangents_rm.clone().mul(normals.clone()).sum_dim(1); // Shape: [verts.dims()[0], 1]
1313                                                                               // println!("7.11 -- {:?}", now.elapsed());
1314
1315        // Perform Gram-Schmidt orthogonalization: new_tangent = tangent - normal * dot
1316        let new_tangents = tensor::normalize_tensor(tangents_rm.clone().sub(normals.clone().mul(dot_product))); // Shape: [verts.dims()[0], 3]
1317        let cross_vec = tensor::cross_product(&tangents_rm, &bitangents_rm);
1318        let handedness_sign = cross_vec.clone().mul(normals.clone()).sum_dim(1).sign(); //.unsqueeze_dim(1); // Shape: [verts.dims()[0], 1]
1319
1320        let handness_indices: Tensor<B, 1, Int> = Tensor::<B, 1, Int>::arange(
1321            0..(i64::try_from(handness_rm.shape().dims[0]).expect("Dimension size exceeds i64 range")),
1322            &verts.device(),
1323        );
1324        handness_rm = handness_rm.select_assign(0, handness_indices.clone(), handedness_sign);
1325        tangents_rm = tangents_rm.slice_assign([0..num_verts, 0..3], new_tangents);
1326        // println!("7.12 -- {:?}", now.elapsed());
1327
1328        // let mut tangents_and_handness_rm =
1329        //     Tensor::<B, 2, Float>::zeros([num_verts, 4], &verts.device());
1330
1331        let tangent_x = tangents_rm.clone().slice([0..num_verts, 0..1]);
1332        let tangent_y = tangents_rm.clone().slice([0..num_verts, 1..2]);
1333        let tangent_z = tangents_rm.clone().slice([0..num_verts, 2..3]);
1334
1335        let handness = handness_rm.clone().slice([0..num_verts, 0..1]);
1336        // println!("7.1 -- {:?}", now.elapsed());
1337
1338        let tangents_and_handness_rm: Tensor<B, 2, Float> = Tensor::<B, 1>::stack(
1339            vec![tangent_x.squeeze(1), tangent_y.squeeze(1), tangent_z.squeeze(1), handness.squeeze(1)],
1340            1,
1341        );
1342        tangents_and_handness_rm
1343    }
1344
1345    pub fn transform_verts(verts: &na::DMatrix<f32>, model_matrix: &na::SimilarityMatrix3<f32>) -> na::DMatrix<f32> {
1346        let mut verts_transformed = verts.clone();
1347        for (vert, mut vert_transformed) in verts.row_iter().zip(verts_transformed.row_iter_mut()) {
1348            //Need to transform it to points since vectors don't get affected by the
1349            // translation part
1350            let v_modif = model_matrix * na::Point3::from(vert.fixed_columns::<3>(0).transpose());
1351            vert_transformed.copy_from_slice(v_modif.coords.as_slice());
1352        }
1353        verts_transformed
1354    }
1355
1356    pub fn transform_vectors(verts: &na::DMatrix<f32>, model_matrix: &na::SimilarityMatrix3<f32>) -> na::DMatrix<f32> {
1357        let mut verts_transformed = verts.clone();
1358        for (vert, mut vert_transformed) in verts.row_iter().zip(verts_transformed.row_iter_mut()) {
1359            //vectors don't get affected by the translation part
1360            let v_modif = model_matrix * vert.fixed_columns::<3>(0).transpose();
1361            vert_transformed.copy_from_slice(v_modif.data.as_slice());
1362        }
1363        verts_transformed
1364    }
1365
1366    pub fn get_bounding_points(verts: &na::DMatrix<f32>, model_matrix: Option<na::SimilarityMatrix3<f32>>) -> (na::Point3<f32>, na::Point3<f32>) {
1367        let mut min_point_global = na::Point3::<f32>::new(f32::MAX, f32::MAX, f32::MAX);
1368        let mut max_point_global = na::Point3::<f32>::new(f32::MIN, f32::MIN, f32::MIN);
1369
1370        //get min and max vertex in obj coords
1371        let min_coord_vec: Vec<f32> = verts.column_iter().map(|c| c.min()).collect();
1372        let max_coord_vec: Vec<f32> = verts.column_iter().map(|c| c.max()).collect();
1373        let min_point = na::Point3::<f32>::from_slice(&min_coord_vec);
1374        let max_point = na::Point3::<f32>::from_slice(&max_coord_vec);
1375
1376        //get the points to world coords
1377        let (min_point_w, max_point_w) = if let Some(model_matrix) = model_matrix {
1378            (model_matrix * min_point, model_matrix * max_point)
1379        } else {
1380            (min_point, max_point)
1381        };
1382
1383        //get the min/max between these points of this mesh and the global one
1384        min_point_global = min_point_global.inf(&min_point_w);
1385        max_point_global = max_point_global.sup(&max_point_w);
1386
1387        (min_point_global, max_point_global)
1388    }
1389
1390    pub fn get_centroid(verts: &na::DMatrix<f32>, model_matrix: Option<na::SimilarityMatrix3<f32>>) -> na::Point3<f32> {
1391        let (min_point_global, max_point_global) = Self::get_bounding_points(verts, model_matrix);
1392
1393        //exactly the miggle between min and max
1394        min_point_global.lerp(&max_point_global, 0.5)
1395    }
1396
1397    #[allow(clippy::cast_possible_truncation)]
1398    #[allow(clippy::cast_precision_loss)]
1399    #[allow(clippy::cast_sign_loss)]
1400    pub fn sample_img_with_uvs(img: &DynImage, uvs: &na::DMatrix<f32>, is_srgb: bool) -> DMatrix<f32> {
1401        let mut sampled_pixels =
1402            unsafe { DMatrix::<core::mem::MaybeUninit<f32>>::uninit(na::Dyn(uvs.nrows()), na::Dyn(img.channels() as usize)).assume_init() };
1403
1404        for (i, uv) in uvs.row_iter().enumerate() {
1405            let x = (uv[0] * img.width() as f32) - 0.5;
1406            let y = ((1.0 - uv[1]) * img.height() as f32) - 0.5;
1407
1408            //TODO add also bilinear interpolation, for now we only have nearest
1409            let x = x.floor() as u32;
1410            let y = y.floor() as u32;
1411            let mut sample = img.get_pixel(x, y);
1412
1413            if is_srgb {
1414                sample.apply(|v| (v / 255.0).powf(2.2) * 255.0);
1415            }
1416
1417            sampled_pixels.row_mut(i).copy_from_slice(&sample.0[0..img.channels() as usize]);
1418        }
1419
1420        //if the image is srgb, we first convert to linear space before we store it or
1421        // before we do any blending sampled_pixels = sampled_pixels.map(|v|
1422        // v.powf(2.2));
1423
1424        sampled_pixels
1425    }
1426
1427    /// returns the rows of mat where mask==keep
1428    /// returns the filtered rows and also a matrix of orig2filtered and
1429    /// filtered2orig which maps from original row indices to filtered ones and
1430    /// viceversa (-1 denotes a invalid index)
1431    pub fn filter_rows<T: na::Scalar>(mat: &na::DMatrix<T>, mask: &[bool], keep: bool) -> (DMatrix<T>, Vec<i32>, Vec<i32>) {
1432        assert_eq!(
1433            mat.nrows(),
1434            mask.len(),
1435            "Mat and mask need to have the same nr of rows. Mat has nr rows{} while mask have length {}",
1436            mat.nrows(),
1437            mask.len()
1438        );
1439
1440        //figure out how many rows we need
1441        let nr_filtered_rows: u32 = mask.iter().map(|v| u32::from(*v == keep)).sum();
1442
1443        let mut selected =
1444            unsafe { DMatrix::<core::mem::MaybeUninit<T>>::uninit(na::Dyn(nr_filtered_rows as usize), na::Dyn(mat.ncols())).assume_init() };
1445
1446        //indirections
1447        let mut orig2filtered: Vec<i32> = vec![-1; mat.nrows()];
1448        let mut filtered2orig: Vec<i32> = vec![-1; nr_filtered_rows as usize];
1449
1450        let mut idx_filtered = 0;
1451        for (idx_orig, (val, mask_val)) in izip!(mat.row_iter(), mask.iter()).enumerate() {
1452            if *mask_val == keep {
1453                selected.row_mut(idx_filtered).copy_from(&val);
1454
1455                //indirections
1456                orig2filtered[idx_orig] = i32::try_from(idx_filtered).unwrap();
1457                filtered2orig[idx_filtered] = i32::try_from(idx_orig).unwrap();
1458
1459                idx_filtered += 1;
1460            }
1461        }
1462
1463        (selected, orig2filtered, filtered2orig)
1464    }
1465
1466    /// returns the cols of mat where mask==keep
1467    /// returns the filtered cols and also a matrix of orig2filtered and
1468    /// filtered2orig which maps from original row indices to filtered ones and
1469    /// viceversa (-1 denotes a invalid index)
1470    pub fn filter_cols<T: na::Scalar>(mat: &na::DMatrix<T>, mask: &[bool], keep: bool) -> (DMatrix<T>, Vec<i32>, Vec<i32>) {
1471        assert_eq!(
1472            mat.ncols(),
1473            mask.len(),
1474            "Mat and mask need to have the same nr of cols. Mat has nr cols{} while mask have length {}",
1475            mat.ncols(),
1476            mask.len()
1477        );
1478
1479        //figure out how many rows we need
1480        let nr_filtered_cols: u32 = mask.iter().map(|v| u32::from(*v == keep)).sum();
1481
1482        let mut selected =
1483            unsafe { DMatrix::<core::mem::MaybeUninit<T>>::uninit(na::Dyn(mat.nrows()), na::Dyn(nr_filtered_cols as usize)).assume_init() };
1484
1485        //indirections
1486        let mut orig2filtered: Vec<i32> = vec![-1; mat.ncols()];
1487        let mut filtered2orig: Vec<i32> = vec![-1; nr_filtered_cols as usize];
1488
1489        let mut idx_filtered = 0;
1490        for (idx_orig, (val, mask_val)) in izip!(mat.column_iter(), mask.iter()).enumerate() {
1491            if *mask_val == keep {
1492                selected.column_mut(idx_filtered).copy_from(&val);
1493
1494                //indirections
1495                orig2filtered[idx_orig] = i32::try_from(idx_filtered).unwrap();
1496                filtered2orig[idx_filtered] = i32::try_from(idx_orig).unwrap();
1497
1498                idx_filtered += 1;
1499            }
1500        }
1501
1502        (selected, orig2filtered, filtered2orig)
1503    }
1504
1505    /// Gets rows of mat and splats them according to `indices_orig2splatted`
1506    /// such that:
1507    /// ```ignore
1508    /// vec = mat[i];
1509    /// idx_destination = indices_orig2splatted[i];
1510    /// splatted.row(idx_destination) += vec
1511    /// ```
1512    pub fn splat_rows(mat: &na::DMatrix<f32>, indices_orig2splatted: &[u32], splat_type: &SplatType) -> DMatrix<f32> {
1513        assert_eq!(
1514            mat.nrows(),
1515            indices_orig2splatted.len(),
1516            "Mat and indices need to have the same nr of rows. Mat has nr rows{} while indices have length {}",
1517            mat.nrows(),
1518            indices_orig2splatted.len()
1519        );
1520
1521        // let mut indices_sorted = indices_orig2splatted.to_vec();
1522        // indices_sorted.sort_unstable();
1523        // indices_sorted.dedup();
1524        // let nr_splatted_rows = indices_sorted.len();
1525        let max_idx_splatted = *indices_orig2splatted.iter().max().unwrap() as usize;
1526
1527        let mut splatted = na::DMatrix::zeros(max_idx_splatted + 1, mat.ncols());
1528        let mut normalization: Vec<f32> = vec![0.0; max_idx_splatted + 1];
1529
1530        for (val, idx_destin) in izip!(mat.row_iter(), indices_orig2splatted.iter()) {
1531            let mut splatted_row = splatted.row_mut(*idx_destin as usize);
1532            splatted_row.add_assign(val);
1533
1534            #[allow(clippy::single_match)]
1535            match splat_type {
1536                SplatType::Avg => {
1537                    normalization[*idx_destin as usize] += 1.0;
1538                }
1539                SplatType::Sum => {}
1540            }
1541        }
1542
1543        //renormalize
1544        match splat_type {
1545            SplatType::Avg => {
1546                for (mut splatted_row, normalization_row) in izip!(splatted.row_iter_mut(), normalization.iter()) {
1547                    for e in splatted_row.iter_mut() {
1548                        *e /= normalization_row;
1549                    }
1550                }
1551            }
1552            SplatType::Sum => {}
1553        }
1554
1555        splatted
1556    }
1557
1558    pub fn apply_indirection(mat: &na::DMatrix<u32>, indices_orig2destin: &[i32], removal_policy: &IndirRemovalPolicy) -> (DMatrix<u32>, Vec<bool>) {
1559        //applies the indices_orig2destin to mat. The values that are invalid are
1560        // mapped to -1
1561        let reindexed: DMatrix<i32> = DMatrix::from_iterator(
1562            mat.nrows(),
1563            mat.ncols(),
1564            mat.iter().map(|v| indices_orig2destin.get(*v as usize).map_or(-1, |v| *v)),
1565        );
1566
1567        //remove rows or cols that have any -1
1568        let (reindexed_only_valid, mask) = match removal_policy {
1569            IndirRemovalPolicy::RemoveInvalidRows => {
1570                let mut valid_rows = vec![true; mat.nrows()];
1571                reindexed
1572                    .row_iter()
1573                    .enumerate()
1574                    .filter(|(_, r)| r.iter().any(|x| *x == -1))
1575                    .for_each(|(idx, _)| valid_rows[idx] = false);
1576                let (filtered, _, _) = Self::filter_rows(&reindexed, &valid_rows, true);
1577                (filtered, valid_rows)
1578            }
1579            IndirRemovalPolicy::RemoveInvalidCols => {
1580                let mut valid_cols = vec![true; mat.ncols()];
1581                reindexed
1582                    .column_iter()
1583                    .enumerate()
1584                    .filter(|(_, c)| c.iter().any(|x| *x == -1))
1585                    .for_each(|(idx, _)| valid_cols[idx] = false);
1586                let (filtered, _, _) = Self::filter_cols(&reindexed, &valid_cols, true);
1587                (filtered, valid_cols)
1588            }
1589        };
1590
1591        let reindexed_filtered = reindexed_only_valid.try_cast::<u32>().unwrap();
1592
1593        (reindexed_filtered, mask)
1594    }
1595
1596    // pub fn compute_dummy_uvs(nr_verts: usize) -> DMatrix<f32> {
1597    //     DMatrix::<f32>::zeros(nr_verts, 2)
1598    // }
1599    // pub fn compute_dummy_colors(nr_verts: usize) -> DMatrix<f32> {
1600    //     DMatrix::<f32>::zeros(nr_verts, 3)
1601    // }
1602    // pub fn compute_dummy_tangents(nr_verts: usize) -> DMatrix<f32> {
1603    //     DMatrix::<f32>::zeros(nr_verts, 4) //make it 4 because it's both the
1604    // tangent and the handness as the last element }
1605    pub fn compute_dummy_uvs<B: Backend>(nr_verts: usize, device: &B::Device) -> Tensor<B, 2, Float> {
1606        Tensor::<B, 2, Float>::zeros([nr_verts, 2], device)
1607    }
1608    pub fn compute_dummy_colors<B: Backend>(nr_verts: usize, device: &B::Device) -> Tensor<B, 2, Float> {
1609        Tensor::<B, 2, Float>::zeros([nr_verts, 3], device)
1610    }
1611    pub fn compute_dummy_tangents<B: Backend>(nr_verts: usize, device: &B::Device) -> Tensor<B, 2, Float> {
1612        Tensor::<B, 2, Float>::zeros([nr_verts, 4], device)
1613    }
1614
1615    pub fn create_frustum_verts_and_edges(
1616        extrinsics: &na::Matrix4<f32>,
1617        fovy: f32,
1618        aspect_ratio: f32,
1619        near: f32, // this is for  the camera frustum not viewing frustum
1620        far: f32,
1621    ) -> (DMatrix<f32>, DMatrix<u32>) {
1622        // Extract the rotation and translation from the extrinsics matrix
1623        let rot = extrinsics.fixed_view::<3, 3>(0, 0);
1624        let trans = extrinsics.fixed_view::<3, 1>(0, 3);
1625
1626        // Calculate the camera's position (lookfrom) in world space
1627        let lookfrom = -rot.transpose() * trans;
1628
1629        let forward = -rot.column(2);
1630        let right = rot.column(0);
1631        let up = rot.column(1);
1632
1633        // Frustum dimensions at near and far planes
1634        let near_height = 2.0 * (fovy / 2.0).tan() * near;
1635        let near_width = near_height * aspect_ratio;
1636
1637        let far_height = 2.0 * (fovy / 2.0).tan() * far;
1638        let far_width = far_height * aspect_ratio;
1639
1640        // Calculate the corners of the near and far planes
1641        let near_center = lookfrom + forward * near;
1642        let near_top_left = near_center + (up * (near_height / 2.0)) - (right * (near_width / 2.0));
1643        let near_top_right = near_center + (up * (near_height / 2.0)) + (right * (near_width / 2.0));
1644        let near_bottom_left = near_center - (up * (near_height / 2.0)) - (right * (near_width / 2.0));
1645        let near_bottom_right = near_center - (up * (near_height / 2.0)) + (right * (near_width / 2.0));
1646
1647        let far_center = lookfrom + forward * far;
1648        let far_top_left = far_center + (up * (far_height / 2.0)) - (right * (far_width / 2.0));
1649        let far_top_right = far_center + (up * (far_height / 2.0)) + (right * (far_width / 2.0));
1650        let far_bottom_left = far_center - (up * (far_height / 2.0)) - (right * (far_width / 2.0));
1651        let far_bottom_right = far_center - (up * (far_height / 2.0)) + (right * (far_width / 2.0));
1652
1653        // Create the vertices and edges matrices
1654        let verts = DMatrix::from_row_slice(
1655            8,
1656            3,
1657            &[
1658                near_top_left.x,
1659                near_top_left.y,
1660                near_top_left.z,
1661                near_top_right.x,
1662                near_top_right.y,
1663                near_top_right.z,
1664                near_bottom_left.x,
1665                near_bottom_left.y,
1666                near_bottom_left.z,
1667                near_bottom_right.x,
1668                near_bottom_right.y,
1669                near_bottom_right.z,
1670                far_top_left.x,
1671                far_top_left.y,
1672                far_top_left.z,
1673                far_top_right.x,
1674                far_top_right.y,
1675                far_top_right.z,
1676                far_bottom_left.x,
1677                far_bottom_left.y,
1678                far_bottom_left.z,
1679                far_bottom_right.x,
1680                far_bottom_right.y,
1681                far_bottom_right.z,
1682            ],
1683        );
1684
1685        let edges = DMatrix::from_row_slice(12, 2, &[0, 1, 1, 3, 3, 2, 2, 0, 4, 5, 5, 7, 7, 6, 6, 4, 0, 4, 1, 5, 2, 6, 3, 7]);
1686
1687        (verts, edges)
1688    }
1689}