1#![allow(clippy::missing_panics_doc)] use crate::components::{Colors, Edges};
5use gloss_hecs::EntityBuilder;
6use gloss_utils::tensor::{DynamicMatrixOps, DynamicTensorFloat2D, DynamicTensorInt2D};
7use log::debug;
8use nalgebra_glm::{Vec2, Vec3};
9
10extern crate nalgebra as na;
11use crate::components::{Faces, ModelMatrix, Normals, UVs, Verts};
12use core::f32;
13use gloss_utils::io::FileType;
14#[allow(unused_imports)]
15use log::{error, info, warn};
16use na::DMatrix;
17use std::path::Path;
18use tobj;
19
20use ply_rs::{parser, ply};
21
22#[must_use]
24pub fn build_cube(center: na::Point3<f32>) -> EntityBuilder {
25 let verts = DMatrix::<f32>::from_row_slice(
27 8,
28 3,
29 &[
30 -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, ],
42 );
43
44 let faces = DMatrix::<u32>::from_row_slice(
46 12,
47 3,
48 &[
49 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, ],
62 );
63
64 let mut verts_dup = Vec::new();
67 let mut faces_dup = Vec::new();
68 for f in faces.row_iter() {
69 let p1 = verts.row(f[0] as usize);
70 let p2 = verts.row(f[2] as usize);
71 let p3 = verts.row(f[1] as usize);
72 verts_dup.push(p1);
73 verts_dup.push(p2);
74 verts_dup.push(p3);
75
76 #[allow(clippy::cast_possible_truncation)]
77 let v_size = verts_dup.len() as u32;
78 let new_face = na::RowDVector::<u32>::from_row_slice(&[v_size - 1, v_size - 2, v_size - 3]); faces_dup.push(new_face);
82 }
83
84 let verts = na::DMatrix::<f32>::from_rows(verts_dup.as_slice());
85 let faces = na::DMatrix::<u32>::from_rows(faces_dup.as_slice());
86
87 let mut model_matrix = na::SimilarityMatrix3::<f32>::identity();
88 model_matrix.append_translation_mut(¢er.coords.into());
90 let verts_tensor = DynamicTensorFloat2D::from_dmatrix(&verts);
91 let faces_tensor = DynamicTensorInt2D::from_dmatrix(&faces);
92
93 let mut builder = EntityBuilder::new();
94 builder.add(Verts(verts_tensor)).add(Faces(faces_tensor)).add(ModelMatrix(model_matrix));
95
96 builder
97}
98
99#[must_use]
106pub fn build_plane(center: na::Point3<f32>, normal: na::Vector3<f32>, size_x: f32, size_y: f32, transform_cpu_data: bool) -> EntityBuilder {
107 let mut verts = DMatrix::<f32>::from_row_slice(
109 4,
110 3,
111 &[
112 -1.0 * size_x,
113 0.0,
114 -1.0 * size_y, 1.0 * size_x,
116 0.0,
117 -1.0 * size_y, 1.0 * size_x,
119 0.0,
120 1.0 * size_y, -1.0 * size_x,
122 0.0,
123 1.0 * size_y, ],
125 );
126 let faces = DMatrix::<u32>::from_row_slice(
128 2,
129 3,
130 &[
131 2, 1, 0, 3, 2, 0,
133 ],
134 );
135
136 let uvs = DMatrix::<f32>::from_row_slice(
138 4,
139 2,
140 &[
141 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, ],
146 );
147
148 let up = na::Vector3::<f32>::new(0.0, 1.0, 0.0);
150 let lookat = center + normal * 1.0;
151 let mut model_matrix = if up.angle(&normal.normalize()) < 1e-6 {
154 let mut mm = na::SimilarityMatrix3::<f32>::identity();
155 mm.append_translation_mut(&na::Translation3::from(center));
156 mm
157 } else {
158 let mut m = na::SimilarityMatrix3::<f32>::face_towards(¢er, &lookat, &up, 1.0);
159 m = m
160 * na::Rotation3::<f32>::from_axis_angle(&na::Vector3::z_axis(), std::f32::consts::FRAC_PI_2)
161 * na::Rotation3::<f32>::from_axis_angle(&na::Vector3::x_axis(), std::f32::consts::FRAC_PI_2); m
163 };
164
165 if transform_cpu_data {
166 for mut vert in verts.row_iter_mut() {
168 let v_modif = model_matrix * na::Point3::from(vert.fixed_columns::<3>(0).transpose());
169 vert.copy_from_slice(v_modif.coords.as_slice());
170 }
171 model_matrix = na::SimilarityMatrix3::<f32>::identity();
173 }
174 let verts_tensor = DynamicTensorFloat2D::from_dmatrix(&verts);
175 let faces_tensor = DynamicTensorInt2D::from_dmatrix(&faces);
176 let uvs_tensor = DynamicTensorFloat2D::from_dmatrix(&uvs);
177
178 let mut builder = EntityBuilder::new();
179 builder
180 .add(Verts(verts_tensor))
181 .add(Faces(faces_tensor))
182 .add(UVs(uvs_tensor))
183 .add(ModelMatrix(model_matrix));
184
185 builder
186}
187
188#[must_use]
189pub fn build_floor() -> EntityBuilder {
190 let verts = DMatrix::<f32>::from_row_slice(
192 4,
193 3,
194 &[
195 -1.0, 0.0, -1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, 1.0, ],
200 );
201 let faces = DMatrix::<u32>::from_row_slice(
203 2,
204 3,
205 &[
206 2, 1, 0, 3, 2, 0,
208 ],
209 );
210
211 let uvs = DMatrix::<f32>::from_row_slice(
213 4,
214 2,
215 &[
216 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, ],
221 );
222 let verts_tensor = DynamicTensorFloat2D::from_dmatrix(&verts);
223 let faces_tensor = DynamicTensorInt2D::from_dmatrix(&faces);
224 let uvs_tensor = DynamicTensorFloat2D::from_dmatrix(&uvs);
225 let mut builder = EntityBuilder::new();
226 builder.add(Verts(verts_tensor)).add(Faces(faces_tensor)).add(UVs(uvs_tensor));
227
228 builder
229}
230
231#[must_use]
232#[allow(clippy::cast_precision_loss)]
233pub fn build_grid(
234 center: na::Point3<f32>,
235 normal: na::Vector3<f32>,
236 nr_lines_x: u32,
237 nr_lines_y: u32,
238 size_x: f32,
239 size_y: f32,
240 transform_cpu_data: bool,
241) -> EntityBuilder {
242 let nr_lines_x = nr_lines_x.max(2);
245 let nr_lines_y = nr_lines_y.max(2);
246
247 let size_cell_x = size_x / nr_lines_x as f32;
253 let size_cell_y = size_y / nr_lines_y as f32;
254 let grid_half_size_x = size_x / 2.0;
255 let grid_half_size_y = size_y / 2.0;
256
257 let mut verts = Vec::new();
263 for idx_y in 0..nr_lines_y {
264 for idx_x in 0..nr_lines_x {
265 verts.push(idx_x as f32 * size_cell_x - grid_half_size_x);
266 verts.push(0.0);
267 verts.push(idx_y as f32 * size_cell_y - grid_half_size_y);
268 }
269 }
270
271 let mut edges_h = Vec::new();
273 for idx_y in 0..nr_lines_y {
274 for idx_x in 0..nr_lines_x - 1 {
275 let idx_cur = idx_y * nr_lines_x + idx_x;
276 let idx_next = idx_y * nr_lines_x + idx_x + 1;
277 edges_h.push(idx_cur);
278 edges_h.push(idx_next);
279 }
280 }
281
282 let mut edges_v = Vec::new();
284 for idx_y in 0..nr_lines_y - 1 {
285 for idx_x in 0..nr_lines_x {
286 let idx_cur = idx_y * nr_lines_x + idx_x;
287 let idx_next = (idx_y + 1) * nr_lines_x + idx_x;
288 edges_v.push(idx_cur);
289 edges_v.push(idx_next);
290 }
291 }
292
293 let mut edges = edges_h;
294 edges.extend(edges_v);
295
296 let mut verts = DMatrix::<f32>::from_row_slice(verts.len() / 3, 3, &verts);
298 let edges = DMatrix::<u32>::from_row_slice(edges.len() / 2, 2, &edges);
299
300 let up = na::Vector3::<f32>::new(0.0, 1.0, 0.0);
302 let lookat = center + normal * 1.0;
303 let mut model_matrix = if up.angle(&normal.normalize()) < 1e-6 {
306 let mut mm = na::SimilarityMatrix3::<f32>::identity();
307 mm.append_translation_mut(&na::Translation3::from(center));
308 mm
309 } else {
310 let mut m = na::SimilarityMatrix3::<f32>::face_towards(¢er, &lookat, &up, 1.0);
311 m = m
312 * na::Rotation3::<f32>::from_axis_angle(&na::Vector3::z_axis(), std::f32::consts::FRAC_PI_2)
313 * na::Rotation3::<f32>::from_axis_angle(&na::Vector3::x_axis(), std::f32::consts::FRAC_PI_2); m
315 };
316
317 if transform_cpu_data {
318 for mut vert in verts.row_iter_mut() {
320 let v_modif = model_matrix * na::Point3::from(vert.fixed_columns::<3>(0).transpose());
321 vert.copy_from_slice(v_modif.coords.as_slice());
322 }
323 model_matrix = na::SimilarityMatrix3::<f32>::identity();
325 }
326 let verts_tensor = DynamicTensorFloat2D::from_dmatrix(&verts);
327 let edges_tensor = DynamicTensorInt2D::from_dmatrix(&edges);
328
329 let mut builder = EntityBuilder::new();
330 builder.add(Verts(verts_tensor)).add(Edges(edges_tensor)).add(ModelMatrix(model_matrix));
331
332 builder
333}
334
335pub fn build_from_file(path: &str) -> EntityBuilder {
336 let filetype = match Path::new(path).extension() {
338 Some(extension) => FileType::find_match(extension.to_str().unwrap_or("")),
339 None => FileType::Unknown,
340 };
341
342 #[allow(clippy::single_match_else)]
343 match filetype {
344 FileType::Obj => build_from_obj(Path::new(path)),
345 FileType::Ply => build_from_ply(Path::new(path)),
346 FileType::Unknown => {
347 error!("Could not read file {:?}", path);
348 EntityBuilder::new() }
350 }
351}
352
353#[cfg(target_arch = "wasm32")]
356pub async fn build_from_file_async(path: &str) -> EntityBuilder {
357 let filetype = match Path::new(path).extension() {
359 Some(extension) => FileType::find_match(extension.to_str().unwrap_or("")),
360 _ => FileType::Unknown,
361 };
362
363 match filetype {
364 FileType::Obj => build_from_obj_async(Path::new(path)).await,
365 _ => {
367 error!("Could not read file {:?}", path);
368 EntityBuilder::new() }
370 }
371}
372
373#[allow(clippy::unused_async)] #[allow(clippy::identity_op)] fn build_from_obj(path: &Path) -> EntityBuilder {
378 info!("reading obj from {path:?}");
379
380 let (models, _) = tobj::load_obj(path, &tobj::GPU_LOAD_OPTIONS).expect("Failed to OBJ load file");
382
383 model_obj_to_entity_builder(&models)
384}
385
386#[allow(clippy::unused_async)] #[cfg(target_arch = "wasm32")]
390#[allow(deprecated)]
391async fn build_from_obj_async(path: &Path) -> EntityBuilder {
392 let mut file_wasm = gloss_utils::io::FileLoader::open(path.to_str().unwrap()).await;
394 let (models, _) = tobj::load_obj_buf_async(&mut file_wasm, &tobj::GPU_LOAD_OPTIONS, move |p| async move {
395 match p.as_str() {
396 _ => unreachable!(),
397 }
398 })
399 .await
400 .expect("Failed to OBJ load file");
401
402 model_obj_to_entity_builder(&models)
403}
404
405#[allow(clippy::unused_async)] #[allow(clippy::identity_op)] pub fn build_from_obj_buf(buf: &[u8]) -> EntityBuilder {
410 let mut reader = std::io::BufReader::new(buf);
411
412 let (models, _) = tobj::load_obj_buf(&mut reader, &tobj::GPU_LOAD_OPTIONS, move |_p| Err(tobj::LoadError::MaterialParseError))
414 .expect("Failed to OBJ load file");
415
416 model_obj_to_entity_builder(&models)
417}
418
419#[allow(clippy::identity_op)] fn model_obj_to_entity_builder(models: &[tobj::Model]) -> EntityBuilder {
421 let mesh = &models[0].mesh;
424 debug!("obj: nr indices {}", mesh.indices.len() / 3);
425 debug!("obj: nr positions {}", mesh.positions.len() / 3);
426 debug!("obj: nr normals {}", mesh.normals.len() / 3);
427 debug!("obj: nr texcoords {}", mesh.texcoords.len() / 2);
428
429 let nr_verts = mesh.positions.len() / 3;
430 let nr_faces = mesh.indices.len() / 3;
431 let nr_normals = mesh.normals.len() / 3;
432 let nr_texcoords = mesh.texcoords.len() / 2;
433
434 let mut builder = EntityBuilder::new();
435
436 if nr_verts > 0 {
437 debug!("read_obj: file has verts");
438 let verts = DMatrix::<f32>::from_row_slice(nr_verts, 3, mesh.positions.as_slice());
439 let verts_tensor = DynamicTensorFloat2D::from_dmatrix(&verts);
440 builder.add(Verts(verts_tensor));
441 }
442
443 if nr_faces > 0 {
444 debug!("read_obj: file has faces");
445 let faces = DMatrix::<u32>::from_row_slice(nr_faces, 3, mesh.indices.as_slice());
446 let faces_tensor = DynamicTensorInt2D::from_dmatrix(&faces);
447 builder.add(Faces(faces_tensor));
448 }
449
450 if nr_normals > 0 {
451 debug!("read_obj: file has normals");
452 let normals = DMatrix::<f32>::from_row_slice(nr_normals, 3, mesh.normals.as_slice());
453 let normals_tensor = DynamicTensorFloat2D::from_dmatrix(&normals);
454 builder.add(Normals(normals_tensor));
455 }
456
457 if nr_texcoords > 0 {
458 debug!("read_obj: file has texcoords");
459 let uv = DMatrix::<f32>::from_row_slice(nr_texcoords, 2, mesh.texcoords.as_slice());
460 let uvs_tensor = DynamicTensorFloat2D::from_dmatrix(&uv);
461 builder.add(UVs(uvs_tensor));
462 }
463
464 builder
469}
470
471#[allow(clippy::unused_async)] #[allow(clippy::identity_op)] #[allow(clippy::too_many_lines)] fn build_from_ply(path: &Path) -> EntityBuilder {
477 #[derive(Debug, Default)]
478 pub struct Vertex {
479 pos: Vec3,
480 color: Vec3,
481 normal: Vec3,
482 uv: Vec2,
483 }
484
485 #[derive(Debug)]
486 pub struct Face {
487 vertex_index: Vec<u32>,
488 }
489
490 impl ply::PropertyAccess for Vertex {
494 fn new() -> Self {
495 Self::default()
496 }
497 fn set_property(&mut self, key: String, property: ply::Property) {
498 match (key.as_ref(), property) {
499 ("x", ply::Property::Float(v)) => self.pos.x = v,
500 ("y", ply::Property::Float(v)) => self.pos.y = v,
501 ("z", ply::Property::Float(v)) => self.pos.z = v,
502 ("red", ply::Property::UChar(v)) => {
503 self.color.x = f32::from(v) / 255.0;
504 }
505 ("green", ply::Property::UChar(v)) => self.color.y = f32::from(v) / 255.0,
506 ("blue", ply::Property::UChar(v)) => self.color.z = f32::from(v) / 255.0,
507 ("nx", ply::Property::Float(v)) => self.normal.x = v,
509 ("ny", ply::Property::Float(v)) => self.normal.y = v,
510 ("nz", ply::Property::Float(v)) => self.normal.z = v,
511 ("u" | "s", ply::Property::Float(v)) => self.uv.x = v,
513 ("v" | "t", ply::Property::Float(v)) => self.uv.y = v,
514 (k, prop) => {
517 warn!("unknown key {} of type {:?}", k, prop);
518 }
519 }
520 }
521 }
522
523 impl ply::PropertyAccess for Face {
525 fn new() -> Self {
526 Face { vertex_index: Vec::new() }
527 }
528 #[allow(clippy::cast_sign_loss)]
529 fn set_property(&mut self, key: String, property: ply::Property) {
530 match (key.as_ref(), property.clone()) {
531 ("vertex_indices" | "vertex_index", ply::Property::ListInt(vec)) => {
532 self.vertex_index = vec.iter().map(|x| *x as u32).collect();
533 }
534 ("vertex_indices" | "vertex_index", ply::Property::ListUInt(vec)) => {
535 self.vertex_index = vec;
536 }
537 (k, _) => {
538 panic!("Face: Unexpected key/value combination: key, val: {k} {property:?}")
539 }
540 }
541 }
542 }
543
544 info!("reading ply from {path:?}");
545 let f = std::fs::File::open(path).unwrap();
547 let mut f = std::io::BufReader::new(f);
550
551 let vertex_parser = parser::Parser::<Vertex>::new();
553 let face_parser = parser::Parser::<Face>::new();
554
555 let header = vertex_parser.read_header(&mut f).unwrap();
559
560 let mut vertex_list = Vec::new();
562 let mut face_list = Vec::new();
563 for (_ignore_key, element) in &header.elements {
564 match element.name.as_ref() {
566 "vertex" | "point" => {
567 vertex_list = vertex_parser.read_payload_for_element(&mut f, element, &header).unwrap();
568 }
569 "face" => {
570 face_list = face_parser.read_payload_for_element(&mut f, element, &header).unwrap();
571 }
572 unknown_name => panic!("Unexpected element! {unknown_name}"),
573 }
574 }
575
576 let mut builder = EntityBuilder::new();
577
578 let mut verts = DMatrix::<f32>::zeros(vertex_list.len(), 3);
580 for (idx, v) in vertex_list.iter().enumerate() {
581 verts.row_mut(idx)[0] = v.pos.x;
582 verts.row_mut(idx)[1] = v.pos.y;
583 verts.row_mut(idx)[2] = v.pos.z;
584 }
585 let verts_tensor = DynamicTensorFloat2D::from_dmatrix(&verts);
586 builder.add(Verts(verts_tensor));
587 let mut colors = DMatrix::<f32>::zeros(vertex_list.len(), 3);
589 for (idx, v) in vertex_list.iter().enumerate() {
590 colors.row_mut(idx)[0] = v.color.x;
591 colors.row_mut(idx)[1] = v.color.y;
592 colors.row_mut(idx)[2] = v.color.z;
593 }
594 if colors.min() != 0.0 || colors.max() != 0.0 {
596 debug!("read_ply: file has colors");
597 let colors_tensor = DynamicTensorFloat2D::from_dmatrix(&colors);
598 builder.add(Colors(colors_tensor));
599 }
600 let mut normals = DMatrix::<f32>::zeros(vertex_list.len(), 3);
602 for (idx, v) in vertex_list.iter().enumerate() {
603 normals.row_mut(idx)[0] = v.normal.x;
604 normals.row_mut(idx)[1] = v.normal.y;
605 normals.row_mut(idx)[2] = v.normal.z;
606 }
607 if normals.min() != 0.0 || normals.max() != 0.0 {
609 debug!("read_ply: file has normals");
610 let normals_tensor = DynamicTensorFloat2D::from_dmatrix(&normals);
611 builder.add(Normals(normals_tensor));
612 }
613 let mut uvs = DMatrix::<f32>::zeros(vertex_list.len(), 2);
615 for (idx, v) in vertex_list.iter().enumerate() {
616 uvs.row_mut(idx)[0] = v.uv.x;
617 uvs.row_mut(idx)[1] = v.uv.y;
618 }
619 if uvs.min() != 0.0 || uvs.max() != 0.0 {
621 debug!("read_ply: file has uvs");
622 let uvs_tensor = DynamicTensorFloat2D::from_dmatrix(&uvs);
623 builder.add(UVs(uvs_tensor));
624 }
625
626 if !face_list.is_empty() {
627 debug!("read_ply: file has verts");
628 let mut faces = DMatrix::<u32>::zeros(face_list.len(), 3);
629 #[allow(clippy::cast_sign_loss)]
630 for (idx, f) in face_list.iter().enumerate() {
631 faces.row_mut(idx)[0] = f.vertex_index[0];
632 faces.row_mut(idx)[1] = f.vertex_index[1];
633 faces.row_mut(idx)[2] = f.vertex_index[2];
634 }
635 let faces_tensor = DynamicTensorInt2D::from_dmatrix(&faces);
636
637 builder.add(Faces(faces_tensor));
638 }
639
640 builder
641}