1#![allow(clippy::missing_panics_doc)] use 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 Uniform,
36 Area,
38}
39
40#[derive(PartialEq)]
41pub enum SplatType {
42 Avg,
44 Sum,
46}
47
48#[derive(PartialEq)]
49pub enum IndirRemovalPolicy {
50 RemoveInvalidRows,
51 RemoveInvalidCols,
52}
53
54pub struct Geom {
59 }
61
62impl Geom {
63 #[must_use]
75 pub fn build_cube(center: na::Point3<f32>) -> EntityBuilder {
76 let verts = DMatrix::<f32>::from_row_slice(
78 8,
79 3,
80 &[
81 -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, ],
93 );
94
95 let faces = DMatrix::<u32>::from_row_slice(
97 12,
98 3,
99 &[
100 2, 1, 0, 2, 0, 3, 4, 5, 6, 7, 4, 6, 5, 0, 1, 4, 0, 5, 7, 6, 3, 3, 6, 2, 3, 0, 4, 3, 4, 7, 6, 5, 1, 6, 1, 2, ],
113 );
114
115 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>::from_row_slice(&[v_size - 1, v_size - 2, v_size - 3]); 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_translation_mut(¢er.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 #[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 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, 1.0 * size_x,
167 0.0,
168 -1.0 * size_y, 1.0 * size_x,
170 0.0,
171 1.0 * size_y, -1.0 * size_x,
173 0.0,
174 1.0 * size_y, ],
176 );
177 let faces = DMatrix::<u32>::from_row_slice(
179 2,
180 3,
181 &[
182 2, 1, 0, 3, 2, 0,
184 ],
185 );
186
187 let uvs = DMatrix::<f32>::from_row_slice(
189 4,
190 2,
191 &[
192 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, ],
197 );
198
199 let up = na::Vector3::<f32>::new(0.0, 1.0, 0.0);
201 let lookat = center + normal * 1.0;
202 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(¢er, &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); m
214 };
215
216 if transform_cpu_data {
217 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 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 let verts = DMatrix::<f32>::from_row_slice(
243 4,
244 3,
245 &[
246 -1.0, 0.0, -1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, 1.0, ],
251 );
252 let faces = DMatrix::<u32>::from_row_slice(
254 2,
255 3,
256 &[
257 2, 1, 0, 3, 2, 0,
259 ],
260 );
261
262 let uvs = DMatrix::<f32>::from_row_slice(
264 4,
265 2,
266 &[
267 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, ],
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 let nr_lines_x = nr_lines_x.max(2);
296 let nr_lines_y = nr_lines_y.max(2);
297
298 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 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 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 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 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 let up = na::Vector3::<f32>::new(0.0, 1.0, 0.0);
353 let lookat = center + normal * 1.0;
354 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(¢er, &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); m
366 };
367
368 if transform_cpu_data {
369 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 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 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() }
401 }
402 }
403
404 #[cfg(target_arch = "wasm32")]
407 pub async fn build_from_file_async(path: &str) -> EntityBuilder {
408 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 _ => {
418 error!("Could not read file {:?}", path);
419 EntityBuilder::new() }
421 }
422 }
423
424 #[allow(clippy::unused_async)] #[allow(clippy::identity_op)] fn build_from_obj(path: &Path) -> EntityBuilder {
429 info!("reading obj from {path:?}");
430
431 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 #[allow(clippy::unused_async)] #[cfg(target_arch = "wasm32")]
441 #[allow(deprecated)]
442 async fn build_from_obj_async(path: &Path) -> EntityBuilder {
443 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 #[allow(clippy::unused_async)] #[allow(clippy::identity_op)] pub fn build_from_obj_buf(buf: &[u8]) -> EntityBuilder {
461 let mut reader = std::io::BufReader::new(buf);
462
463 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)] fn model_obj_to_entity_builder(models: &[tobj::Model]) -> EntityBuilder {
472 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 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, 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::Ascii; ply.header.comments.push("Gloss Ply file".to_string());
610
611 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 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 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 let mut face_element = ply::ElementDef::new("face".to_string());
653 let f = ply::PropertyDef::new(
655 "vertex_indices".to_string(),
656 ply::PropertyType::List(ply::ScalarType::UChar, ply::ScalarType::UInt), );
658 face_element.properties.add(f);
659 ply.header.elements.add(face_element);
660
661 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 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 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 #[allow(clippy::unused_async)] #[allow(clippy::identity_op)] #[allow(clippy::too_many_lines)] 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 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 ("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 ("u" | "s", ply::Property::Float(v)) => self.uv.x = v,
763 ("v" | "t", ply::Property::Float(v)) => self.uv.y = v,
764 (k, prop) => {
767 warn!("unknown key {} of type {:?}", k, prop);
768 }
769 }
770 }
771 }
772
773 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 let f = std::fs::File::open(path).unwrap();
797 let mut f = std::io::BufReader::new(f);
800
801 let vertex_parser = parser::Parser::<Vertex>::new();
803 let face_parser = parser::Parser::<Face>::new();
804
805 let header = vertex_parser.read_header(&mut f).unwrap();
809
810 let mut vertex_list = Vec::new();
812 let mut face_list = Vec::new();
813 for (_ignore_key, element) in &header.elements {
814 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 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 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 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 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 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 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 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 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(); let verts_row_major = verts.transpose(); 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.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 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 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 let face_normals = Geom::compute_per_face_normals(verts, faces);
929 let w = Geom::compute_double_face_areas(verts, faces);
931 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 for mut row in per_vertex_normal.row_iter_mut() {
940 row /= row.norm();
941 }
942 per_vertex_normal
943 }
944 }
945 }
946
947 pub fn compute_per_vertex_normals_burn<B: Backend>(
949 verts: &Tensor<B, 2, Float>, faces: &Tensor<B, 2, Int>, 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 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); let v0 = all_tris_as_verts_chunks[0].clone().squeeze(1); let v1 = all_tris_as_verts_chunks[1].clone().squeeze(1); let v2 = all_tris_as_verts_chunks[2].clone().squeeze(1); let d1 = v1.sub(v0.clone()); let d2 = v2.sub(v0.clone()); let cross_product = tensor::cross_product(&d1, &d2); let face_normals = tensor::normalize_tensor(cross_product);
978
979 let face_indices_expanded: Tensor<B, 1, Int> = faces.clone().flatten(0, 1); 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]); per_vertex_normals = per_vertex_normals.select_assign(0, face_indices_expanded, face_normals_to_scatter);
989 per_vertex_normals = tensor::normalize_tensor(per_vertex_normals);
991 per_vertex_normals
992 }
993 PerVertexNormalsWeightingType::Area => {
994 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); let v0 = all_tris_as_verts_chunks[0].clone().squeeze(1); let v1 = all_tris_as_verts_chunks[1].clone().squeeze(1); let v2 = all_tris_as_verts_chunks[2].clone().squeeze(1); let d1 = v1.sub(v0.clone()); let d2 = v2.sub(v0.clone()); let face_normals = tensor::cross_product(&d1, &d2); let face_areas = face_normals.clone().powf_scalar(2.0).sum_dim(1).sqrt(); let weighted_face_normals = face_normals.div(face_areas); let face_indices_expanded: Tensor<B, 1, Int> = faces.clone().flatten(0, 1); 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 pub fn compute_per_face_normals(verts: &na::DMatrix<f32>, faces: &na::DMatrix<u32>) -> na::DMatrix<f32> {
1029 let verts_row_major = verts.transpose(); 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_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 face_normal.copy_from(&normal.transpose());
1046 }
1047
1048 face_normals
1049 }
1050
1051 pub fn compute_double_face_areas(verts: &na::DMatrix<f32>, faces: &na::DMatrix<u32>) -> na::DMatrix<f32> {
1053 let proj_doublearea =
1055 |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 double_face_areas = double_face_areas.map(f32::sqrt);
1077
1078 double_face_areas
1079 }
1080
1081 #[allow(clippy::similar_names)]
1082 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 let faces_row_major = faces.transpose(); let verts_row_major = verts.transpose(); let normals_row_major = normals.transpose(); let uvs_row_major = uvs.transpose(); 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 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 let id0 = face[0] as usize;
1119 let id1 = face[1] as usize;
1120 let id2 = face[2] as usize;
1121
1122 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 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 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 let delta_pos1 = v1 - v0;
1149 let delta_pos2 = v2 - v0;
1150
1151 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 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 tangents_rm
1177 .column_iter_mut()
1178 .zip(normals_row_major.column_iter())
1179 .zip(bitangents_rm.column_iter())
1180 .enumerate()
1181 {
1182 let dot = tangent.dot(&normal);
1184 let new_tangent = (&tangent - normal * dot).normalize();
1186 let cross_vec = tangent.cross(&bitangent);
1188 let dot: f32 = cross_vec.dot(&normal);
1189 handness_rm.column_mut(idx).fill(dot.signum()); tangent.copy_from_slice(new_tangent.data.as_slice());
1192 }
1196
1197 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 pub fn compute_tangents_burn<B: Backend>(
1222 verts: &Tensor<B, 2, Float>, faces: &Tensor<B, 2, Int>, normals: &Tensor<B, 2, Float>, uvs: &Tensor<B, 2, Float>, ) -> Tensor<B, 2, Float> {
1227 let num_faces = faces.shape().dims[0];
1232 let num_verts = verts.shape().dims[0];
1233 let all_idxs: Tensor<B, 1, Int> = faces.clone().flatten(0, 1);
1237 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 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 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 let delta_uv1_0 = delta_uv1.clone().select(1, Tensor::<B, 1, Int>::from_ints([0], &verts.device())); let delta_uv1_1 = delta_uv1.clone().select(1, Tensor::<B, 1, Int>::from_ints([1], &verts.device())); let delta_uv2_0 = delta_uv2.clone().select(1, Tensor::<B, 1, Int>::from_ints([0], &verts.device())); let delta_uv2_1 = delta_uv2.clone().select(1, Tensor::<B, 1, Int>::from_ints([1], &verts.device())); 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(); let r = denominator.recip().mul(r_mask.clone()); 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 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()); 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); 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]); tangents_rm = tangents_rm.select_assign(0, face_indices_expanded.clone(), face_tangents_to_scatter.clone());
1303 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]); 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); let new_tangents = tensor::normalize_tensor(tangents_rm.clone().sub(normals.clone().mul(dot_product))); let cross_vec = tensor::cross_product(&tangents_rm, &bitangents_rm);
1318 let handedness_sign = cross_vec.clone().mul(normals.clone()).sum_dim(1).sign(); 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 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 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 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 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 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 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 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 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 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 sampled_pixels
1425 }
1426
1427 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 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 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 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 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 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 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 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 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 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 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 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 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<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, far: f32,
1621 ) -> (DMatrix<f32>, DMatrix<u32>) {
1622 let rot = extrinsics.fixed_view::<3, 3>(0, 0);
1624 let trans = extrinsics.fixed_view::<3, 1>(0, 3);
1625
1626 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 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 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 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}