gloss_renderer/components/
mesh_cpu_comps.rs

1//! These represent CPU components which you usually add to entities when you
2//! spawn them.
3
4extern crate nalgebra as na;
5// use burn::backend::ndarray::NdArrayDevice;
6// use burn::backend::candle::CandleDevice;
7// use burn::backend::Candle;
8use gloss_img::DynImage;
9use gloss_utils::{
10    io::FileLoader,
11    tensor::{DynamicTensorFloat2D, DynamicTensorInt2D},
12};
13use image::ImageReader;
14use na::DMatrix;
15use std::io::{BufReader, Cursor, Read, Seek};
16// use burn::backend::{Candle, NdArray, Wgpu};
17
18/// Component that modifications to the config
19#[derive(Clone)]
20pub struct ConfigChanges {
21    pub new_distance_fade_center: na::Point3<f32>,
22}
23
24/// Defines the color type an entity which is displayed as a point cloud
25#[derive(Debug, Clone, Copy, PartialEq)]
26pub enum PointColorType {
27    Solid = 0,
28    PerVert,
29}
30
31/// Defines the color type an entity which is displayed as a point cloud
32#[derive(Debug, Clone, Copy, PartialEq)]
33pub enum LineColorType {
34    Solid = 0,
35    PerVert,
36}
37
38/// Defines the color type an entity which is displayed as a mesh
39#[derive(Debug, Clone, Copy, PartialEq)]
40pub enum MeshColorType {
41    Solid = 0,
42    PerVert,
43    Texture,
44    UV,
45    Normal,
46    NormalViewCoords,
47}
48
49/// Component for visualization options of lines
50#[derive(Clone)]
51#[allow(clippy::struct_excessive_bools)]
52pub struct VisLines {
53    pub show_lines: bool,
54    pub line_color: na::Vector4<f32>,
55    pub line_width: f32,
56    pub color_type: LineColorType,
57    pub zbuffer: bool,
58    pub antialias_edges: bool,
59    //if this components was added automatically by the renderer this will be set to true. This is useful to know since when we upload textures to
60    // gpu the vis_mesh.color_type will be set to Texture but this should happen ONLY if the VisMesh was added automatically. If the used adds this
61    // component manually then we shouldn't override his VisMesh.colortype.
62    pub added_automatically: bool,
63}
64/// Component for visualization options of wireframe
65#[derive(Clone)]
66pub struct VisWireframe {
67    pub show_wireframe: bool,
68    pub wire_color: na::Vector4<f32>,
69    pub wire_width: f32,
70    //if this components was added automatically by the renderer this will be set to true. This is useful to know since when we upload textures to
71    // gpu the vis_mesh.color_type will be set to Texture but this should happen ONLY if the VisMesh was added automatically. If the used adds this
72    // component manually then we shouldn't override his VisMesh.colortype.
73    pub added_automatically: bool,
74}
75/// Component for visualization options of normals
76#[derive(Clone)]
77pub struct VisNormals {
78    pub show_normals: bool,
79    pub normals_color: na::Vector4<f32>,
80    pub normals_width: f32,
81    pub normals_scale: f32, //the scale of the arrows for the normal.
82    //if this components was added automatically by the renderer this will be set to true. This is useful to know since when we upload textures to
83    // gpu the vis_mesh.color_type will be set to Texture but this should happen ONLY if the VisMesh was added automatically. If the used adds this
84    // component manually then we shouldn't override his VisMesh.colortype.
85    pub added_automatically: bool,
86}
87/// Component for visualization options of point clouds
88#[derive(Clone)]
89#[allow(clippy::struct_excessive_bools)]
90pub struct VisPoints {
91    pub show_points: bool,
92    pub show_points_indices: bool,
93    pub point_color: na::Vector4<f32>,
94    pub point_size: f32,
95    pub is_point_size_in_world_space: bool,
96    pub color_type: PointColorType,
97    pub zbuffer: bool,
98    //if this components was added automatically by the renderer this will be set to true. This is useful to know since when we upload textures to
99    // gpu the vis_mesh.color_type will be set to Texture but this should happen ONLY if the VisMesh was added automatically. If the used adds this
100    // component manually then we shouldn't override his VisMesh.colortype.
101    pub added_automatically: bool,
102}
103/// Component for visualization options of meshes
104#[derive(Clone)]
105pub struct VisMesh {
106    pub show_mesh: bool,
107    pub solid_color: na::Vector4<f32>,
108    pub metalness: f32,
109    pub perceptual_roughness: f32,
110    pub roughness_black_lvl: f32,
111    pub uv_scale: f32,
112    pub opacity: f32,
113    pub needs_sss: bool,
114    pub color_type: MeshColorType,
115    //if this components was added automatically by the renderer this will be set to true. This is useful to know since when we upload textures to
116    // gpu the vis_mesh.color_type will be set to Texture but this should happen ONLY if the VisMesh was added automatically. If the used adds this
117    // component manually then we shouldn't override his VisMesh.colortype.
118    pub added_automatically: bool,
119}
120//implementations of default vis options
121impl Default for VisLines {
122    fn default() -> VisLines {
123        VisLines {
124            show_lines: false,
125            line_color: na::Vector4::<f32>::new(1.0, 0.1, 0.1, 1.0),
126            line_width: 1.0,
127            color_type: LineColorType::Solid,
128            zbuffer: true,
129            antialias_edges: false,
130            added_automatically: false,
131        }
132    }
133}
134impl Default for VisWireframe {
135    fn default() -> VisWireframe {
136        VisWireframe {
137            show_wireframe: false,
138            wire_color: na::Vector4::<f32>::new(1.0, 0.0, 0.0, 1.0),
139            wire_width: 1.0,
140            added_automatically: false,
141        }
142    }
143}
144impl Default for VisNormals {
145    fn default() -> VisNormals {
146        VisNormals {
147            show_normals: false,
148            normals_color: na::Vector4::<f32>::new(1.0, 0.0, 0.0, 1.0),
149            normals_width: 1.0,
150            normals_scale: 1.0,
151            added_automatically: false,
152        }
153    }
154}
155impl Default for VisPoints {
156    fn default() -> VisPoints {
157        VisPoints {
158            show_points: false,
159            show_points_indices: false,
160            point_color: na::Vector4::<f32>::new(245.0 / 255.0, 175.0 / 255.0, 110.0 / 255.0, 1.0),
161            point_size: 1.0,
162            is_point_size_in_world_space: false,
163            color_type: PointColorType::Solid,
164            zbuffer: true,
165            added_automatically: false,
166        }
167    }
168}
169impl Default for VisMesh {
170    fn default() -> VisMesh {
171        VisMesh {
172            show_mesh: true,
173            solid_color: na::Vector4::<f32>::new(1.0, 206.0 / 255.0, 143.0 / 255.0, 1.0),
174            metalness: 0.0,
175            perceptual_roughness: 0.5,
176            roughness_black_lvl: 0.0,
177            uv_scale: 1.0,
178            opacity: 1.0,
179            needs_sss: false,
180            color_type: MeshColorType::Solid,
181            added_automatically: false,
182        }
183    }
184}
185
186/// Component that transforms from object coordinates to world. Usually added
187/// automatically but you can also add it yourself.
188#[derive(Clone)]
189pub struct ModelMatrix(pub na::SimilarityMatrix3<f32>); //transform from object coordinates to world corresponds to TfWorldObj
190                                                        // pub struct ModelMatrix(pub na::Affine3<f32>); //transform from object
191                                                        // coordinates to world corresponds to TfWorldObj
192impl Default for ModelMatrix {
193    fn default() -> ModelMatrix {
194        ModelMatrix(na::SimilarityMatrix3::<f32>::identity())
195    }
196}
197impl ModelMatrix {
198    #[must_use]
199    pub fn with_translation(self, t: &na::Vector3<f32>) -> Self {
200        let mut mat = self;
201        mat.0.append_translation_mut(&na::Translation3::new(t[0], t[1], t[2]));
202        mat
203    }
204    #[must_use]
205    pub fn with_rotation_rot3(self, r: &na::Rotation3<f32>) -> Self {
206        let mut mat = self;
207        mat.0.append_rotation_mut(r);
208        mat
209    }
210    #[must_use]
211    pub fn with_rotation_axis_angle(self, v: &na::Vector3<f32>) -> Self {
212        let mut mat = self;
213        mat.0
214            .append_rotation_mut(&na::Rotation3::from_axis_angle(&na::UnitVector3::<f32>::new_normalize(*v), v.norm()));
215        mat
216    }
217    #[must_use]
218    pub fn with_rotation_euler(self, e: &na::Vector3<f32>) -> Self {
219        let mut mat = self;
220        mat.0.append_rotation_mut(&na::Rotation3::from_euler_angles(e.x, e.y, e.z));
221        mat
222    }
223}
224
225/// Component that represents a track of camera extrinsics - num frames x 16
226#[derive(Clone, Debug)]
227pub struct CamTrack(pub DMatrix<f32>);
228
229#[derive(Clone, Debug)]
230pub struct Verts(pub DynamicTensorFloat2D);
231
232/// Component that represents a matrix of vertex positions for the first vertex
233/// of an edge
234#[derive(Clone, Debug)]
235pub struct EdgesV1(pub DynamicTensorFloat2D);
236/// Component that represents a matrix of vertex positions for the second vertex
237/// of an edge
238#[derive(Clone, Debug)]
239pub struct EdgesV2(pub DynamicTensorFloat2D);
240
241/// Component that represents a matrix of face indices for rendering a triangle
242/// mesh
243// #[derive(Clone)]
244// pub struct Faces(pub DMatrix<u32>);
245#[derive(Clone)]
246pub struct Faces(pub DynamicTensorInt2D);
247
248/// Component that represents a matrix of edges as Nx2 where each row is the
249/// start idx and end idx of an edge which indexes into [``Verts``]
250#[derive(Clone, Debug)]
251pub struct Edges(pub DynamicTensorInt2D);
252
253/// Component that represents UV coordinates
254#[derive(Clone)]
255pub struct UVs(pub DynamicTensorFloat2D);
256// / Component that represents the scale of the uv coordinates. Increasing it
257// will tile the texture on the mesh. #[derive(Clone)]
258// pub struct UvScale(pub f32);
259
260/// Component that represents normal vectors
261#[derive(Clone)]
262pub struct Normals(pub DynamicTensorFloat2D);
263
264/// Component that represents tangents
265#[derive(Clone)]
266pub struct Tangents(pub DynamicTensorFloat2D);
267
268/// Component that represents per vertex colors
269#[derive(Clone)]
270pub struct Colors(pub DynamicTensorFloat2D);
271
272#[derive(Clone)]
273#[allow(clippy::struct_excessive_bools)]
274pub struct ImgConfig {
275    pub keep_on_cpu: bool,
276    pub fast_upload: bool, //fast upload uses the staging memory of wgpu but potentially uses more memory
277    pub generate_mipmaps: bool,
278    pub mipmap_generation_cpu: bool,
279}
280impl Default for ImgConfig {
281    fn default() -> Self {
282        Self {
283            keep_on_cpu: true,
284            fast_upload: true,
285            generate_mipmaps: true,
286            mipmap_generation_cpu: false,
287        }
288    }
289}
290
291/// A generic Img that has a path and img
292/// Concrete images like [``DiffuseImg``] will contain the generic img
293#[derive(Clone)]
294pub struct GenericImg {
295    pub path: Option<String>,
296    pub cpu_img: Option<DynImage>, //keep it as an option so we can drop it and release the memory
297    pub config: ImgConfig,
298}
299impl GenericImg {
300    /// # Panics
301    /// Will panic if the path cannot be opened.
302    pub fn new_from_path(path: &str, config: &ImgConfig) -> Self {
303        let cpu_img = Some(ImageReader::open(path).unwrap().decode().unwrap());
304
305        Self {
306            path: Some(path.to_string()),
307            cpu_img: cpu_img.map(|v| v.try_into().unwrap()),
308            config: config.clone(),
309        }
310    }
311
312    /// # Panics
313    /// Will panic if the path cannot be opened.
314    pub async fn new_from_path_async(path: &str, config: &ImgConfig) -> Self {
315        let reader = ImageReader::new(BufReader::new(FileLoader::open(path).await))
316            .with_guessed_format()
317            .expect("Cursor io never fails");
318
319        let cpu_img = Some(reader.decode().unwrap());
320
321        Self {
322            path: Some(path.to_string()),
323            cpu_img: cpu_img.map(|v| v.try_into().unwrap()),
324            config: config.clone(),
325        }
326    }
327
328    /// # Panics
329    /// Will panic if the img cannot be processed and decoded.
330    pub fn new_from_buf(buf: &[u8], config: &ImgConfig) -> Self {
331        Self::new_from_reader(Cursor::new(buf), config)
332    }
333
334    /// # Panics
335    /// Will panic if the img cannot be processed and decoded.
336    pub fn new_from_reader<R: Read + Seek>(reader: R, config: &ImgConfig) -> Self {
337        let reader_img = ImageReader::new(BufReader::new(reader))
338            .with_guessed_format()
339            .expect("Format for image should be something known and valid");
340
341        let cpu_img = Some(reader_img.decode().unwrap());
342
343        Self {
344            path: None,
345            cpu_img: cpu_img.map(|v| v.try_into().unwrap()),
346            config: config.clone(),
347        }
348    }
349
350    pub fn img_ref(&self) -> &DynImage {
351        self.cpu_img.as_ref().unwrap()
352    }
353
354    pub fn img_ref_mut(&mut self) -> &mut DynImage {
355        self.cpu_img.as_mut().unwrap()
356    }
357}
358
359/// Component which represents a diffuse img.
360#[derive(Clone)]
361pub struct DiffuseImg {
362    pub generic_img: GenericImg,
363}
364
365impl DiffuseImg {
366    pub fn new_from_path(path: &str, config: &ImgConfig) -> Self {
367        let generic_img = GenericImg::new_from_path(path, config);
368        Self { generic_img }
369    }
370
371    pub async fn new_from_path_async(path: &str, config: &ImgConfig) -> Self {
372        let generic_img = GenericImg::new_from_path_async(path, config).await;
373        Self { generic_img }
374    }
375
376    pub fn new_from_buf(buf: &[u8], config: &ImgConfig) -> Self {
377        let generic_img = GenericImg::new_from_buf(buf, config);
378        Self { generic_img }
379    }
380
381    pub fn new_from_reader<R: Read + Seek>(reader: R, config: &ImgConfig) -> Self {
382        let generic_img = GenericImg::new_from_reader(reader, config);
383        Self { generic_img }
384    }
385}
386
387/// Component which represents a normal img.
388#[derive(Clone)]
389pub struct NormalImg {
390    pub generic_img: GenericImg,
391}
392
393impl NormalImg {
394    pub fn new_from_path(path: &str, config: &ImgConfig) -> Self {
395        let generic_img = GenericImg::new_from_path(path, config);
396        Self { generic_img }
397    }
398
399    pub async fn new_from_path_async(path: &str, config: &ImgConfig) -> Self {
400        let generic_img = GenericImg::new_from_path_async(path, config).await;
401        Self { generic_img }
402    }
403
404    pub fn new_from_buf(buf: &[u8], config: &ImgConfig) -> Self {
405        let generic_img = GenericImg::new_from_buf(buf, config);
406        Self { generic_img }
407    }
408
409    pub fn new_from_reader<R: Read + Seek>(reader: R, config: &ImgConfig) -> Self {
410        let generic_img = GenericImg::new_from_reader(reader, config);
411        Self { generic_img }
412    }
413}
414
415/// Component which represents a metalness img.
416pub struct MetalnessImg {
417    pub generic_img: GenericImg,
418}
419impl MetalnessImg {
420    pub fn new_from_path(path: &str, config: &ImgConfig) -> Self {
421        let generic_img = GenericImg::new_from_path(path, config);
422        Self { generic_img }
423    }
424
425    pub async fn new_from_path_async(path: &str, config: &ImgConfig) -> Self {
426        let generic_img = GenericImg::new_from_path_async(path, config).await;
427        Self { generic_img }
428    }
429
430    pub fn new_from_buf(buf: &[u8], config: &ImgConfig) -> Self {
431        let generic_img = GenericImg::new_from_buf(buf, config);
432        Self { generic_img }
433    }
434
435    pub fn new_from_reader<R: Read + Seek>(reader: R, config: &ImgConfig) -> Self {
436        let generic_img = GenericImg::new_from_reader(reader, config);
437        Self { generic_img }
438    }
439}
440
441/// Component which represents a roughness img. Assumes it is stored as
442/// perceptual roughness/
443pub struct RoughnessImg {
444    pub generic_img: GenericImg,
445}
446impl RoughnessImg {
447    pub fn new_from_path(path: &str, config: &ImgConfig) -> Self {
448        let generic_img = GenericImg::new_from_path(path, config);
449        Self { generic_img }
450    }
451
452    pub async fn new_from_path_async(path: &str, config: &ImgConfig) -> Self {
453        let generic_img = GenericImg::new_from_path_async(path, config).await;
454        Self { generic_img }
455    }
456
457    pub fn new_from_buf(buf: &[u8], config: &ImgConfig) -> Self {
458        let generic_img = GenericImg::new_from_buf(buf, config);
459        Self { generic_img }
460    }
461
462    pub fn new_from_reader<R: Read + Seek>(reader: R, config: &ImgConfig) -> Self {
463        let generic_img = GenericImg::new_from_reader(reader, config);
464        Self { generic_img }
465    }
466}
467
468//implement some atributes for the vertex atributes so we can use them in a
469// generic function also implement common things like the atom size of an
470// element which is f32 for most or u32 for faces and edges https://stackoverflow.com/a/53085395
471//https://stackoverflow.com/a/66794115
472// pub trait CpuAtrib<T> {
473//     // type TypeElement;
474//     fn byte_size_element(&self) -> usize;
475//     // pub fn get_data(&self) -> DMatrix<Self::TypeElement>;
476//     fn data_ref(&self) -> &DMatrix<T>;
477// }
478
479// impl CpuAtrib<f32> for Colors {
480//     fn byte_size_element(&self) -> usize {
481//         std::mem::size_of::<f32>()
482//     }
483//     fn data_ref(&self) -> &DMatrix<f32> {
484//         &self.0
485//     }
486// }
487
488/// Environment map based ambient lighting representing light from distant
489/// scenery.
490///
491/// When added as a resource to the scene, this component adds indirect light
492/// to every point of the scene (including inside, enclosed areas) based on
493/// an environment cubemap texture. This is similar to [`crate::AmbientLight`],
494/// but higher quality, and is intended for outdoor scenes.
495///
496/// The environment map must be prefiltered into a diffuse and specular cubemap
497/// based on the [split-sum approximation](https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf).
498///
499/// To prefilter your environment map, you can use `KhronosGroup`'s [glTF-IBL-Sampler](https://github.com/KhronosGroup/glTF-IBL-Sampler).
500/// The diffuse map uses the Lambertian distribution, and the specular map uses
501/// the GGX distribution.
502///
503/// `KhronosGroup` also has several prefiltered environment maps that can be found [here](https://github.com/KhronosGroup/glTF-Sample-Environments)
504pub struct EnvironmentMap {
505    pub diffuse_path: String,
506    pub specular_path: String,
507}
508impl EnvironmentMap {
509    pub fn new_from_path(diffuse_path: &str, specular_path: &str) -> Self {
510        Self {
511            diffuse_path: String::from(diffuse_path),
512            specular_path: String::from(specular_path),
513        }
514    }
515}
516
517// /so we can use the Components inside the Mutex<Hashmap> in the scene and wasm
518// https://stackoverflow.com/a/73773940/22166964
519// shenanigans
520//ConfigDeltas
521#[cfg(target_arch = "wasm32")]
522unsafe impl Send for ConfigChanges {}
523#[cfg(target_arch = "wasm32")]
524unsafe impl Sync for ConfigChanges {}
525//verts
526#[cfg(target_arch = "wasm32")]
527unsafe impl Send for Verts {}
528#[cfg(target_arch = "wasm32")]
529unsafe impl Sync for Verts {}
530//vertse1
531#[cfg(target_arch = "wasm32")]
532unsafe impl Send for EdgesV1 {}
533#[cfg(target_arch = "wasm32")]
534unsafe impl Sync for EdgesV1 {}
535//vertse2
536#[cfg(target_arch = "wasm32")]
537unsafe impl Send for EdgesV2 {}
538#[cfg(target_arch = "wasm32")]
539unsafe impl Sync for EdgesV2 {}
540//edges
541#[cfg(target_arch = "wasm32")]
542unsafe impl Send for Edges {}
543#[cfg(target_arch = "wasm32")]
544unsafe impl Sync for Edges {}
545//faces
546#[cfg(target_arch = "wasm32")]
547unsafe impl Send for Faces {}
548#[cfg(target_arch = "wasm32")]
549unsafe impl Sync for Faces {}
550//uvs
551#[cfg(target_arch = "wasm32")]
552unsafe impl Send for UVs {}
553#[cfg(target_arch = "wasm32")]
554unsafe impl Sync for UVs {}
555//normalss
556#[cfg(target_arch = "wasm32")]
557unsafe impl Send for Normals {}
558#[cfg(target_arch = "wasm32")]
559unsafe impl Sync for Normals {}
560//tangents
561#[cfg(target_arch = "wasm32")]
562unsafe impl Send for Tangents {}
563#[cfg(target_arch = "wasm32")]
564unsafe impl Sync for Tangents {}
565//colors
566#[cfg(target_arch = "wasm32")]
567unsafe impl Send for Colors {}
568#[cfg(target_arch = "wasm32")]
569unsafe impl Sync for Colors {}
570//Diffuseimg
571#[cfg(target_arch = "wasm32")]
572unsafe impl Send for DiffuseImg {}
573#[cfg(target_arch = "wasm32")]
574unsafe impl Sync for DiffuseImg {}
575//Normalimg
576#[cfg(target_arch = "wasm32")]
577unsafe impl Send for NormalImg {}
578#[cfg(target_arch = "wasm32")]
579unsafe impl Sync for NormalImg {}
580//Metalnessimg
581#[cfg(target_arch = "wasm32")]
582unsafe impl Send for MetalnessImg {}
583#[cfg(target_arch = "wasm32")]
584unsafe impl Sync for MetalnessImg {}
585//Roughnessimg
586#[cfg(target_arch = "wasm32")]
587unsafe impl Send for RoughnessImg {}
588#[cfg(target_arch = "wasm32")]
589unsafe impl Sync for RoughnessImg {}
590//EnvironmentMap
591#[cfg(target_arch = "wasm32")]
592unsafe impl Send for EnvironmentMap {}
593#[cfg(target_arch = "wasm32")]
594unsafe impl Sync for EnvironmentMap {}