gloss_renderer/
scene.rs

1#![allow(clippy::doc_markdown)]
2
3use gloss_hecs::{CommandBuffer, Component, ComponentRef, DynamicBundle, Entity, EntityBuilder, EntityRef, World};
4use log::{error, trace};
5
6use crate::{
7    actor::Actor,
8    builders,
9    camera::Camera,
10    components::{
11        BoundingBox, CamController, ColorsGPU, DiffuseImg, DiffuseTex, EdgesGPU, EnvironmentMapGpu, FacesGPU, ImgConfig, LightEmit, MeshColorType,
12        MetalnessTex, ModelMatrix, Name, NormalTex, NormalsGPU, PosLookat, Projection, ProjectionWithFov, Renderable, RoughnessTex, ShadowCaster,
13        ShadowMap, TangentsGPU, UVsGPU, Verts, VertsGPU, VisLines, VisMesh, VisPoints,
14    },
15    config::{Config, FloorTexture, FloorType, LightConfig},
16    light::Light,
17};
18
19use gloss_geometry::geom;
20use gloss_utils::abi_stable_aliases::std_types::{RHashMap, RString};
21#[cfg(not(target_arch = "wasm32"))]
22use gloss_utils::abi_stable_aliases::StableAbi;
23use gloss_utils::tensor::{DynamicMatrixOps, DynamicTensorOps};
24use nalgebra as na;
25
26pub static GLOSS_FLOOR_NAME: &str = "floor";
27pub static GLOSS_CAM_NAME: &str = "gloss_camera";
28
29// TODO make parametric checkerboard
30static CHECKERBOARD_BYTES: &[u8; 2324] = include_bytes!("../data/checkerboard.png");
31
32/// Scene contains the ECS world and various functionality to interact with it.
33#[repr(C)]
34#[cfg_attr(not(target_arch = "wasm32"), derive(StableAbi))]
35#[allow(non_upper_case_globals, non_camel_case_types)]
36pub struct Scene {
37    pub world: World,
38    name2entity: RHashMap<RString, Entity>,
39    pub command_buffer: CommandBuffer, //defer insertions and deletion of scene entities for whenever we apply this command buffer
40    entity_resource: Entity,           //unique entity that contains resources, so unique componentes
41}
42impl Default for Scene {
43    fn default() -> Self {
44        Self::new()
45    }
46}
47
48impl Scene {
49    pub fn new() -> Self {
50        let mut world = World::default();
51        let name2entity = RHashMap::<RString, Entity>::new();
52        let command_buffer = CommandBuffer::new();
53        let entity_resource = world.spawn((Name("entity_resource".to_string()),));
54        Self {
55            world,
56            name2entity,
57            command_buffer,
58            entity_resource,
59        }
60    }
61
62    /// Gets the entity that contains all the resources
63    pub fn get_entity_resource(&self) -> Entity {
64        self.entity_resource
65    }
66
67    /// Creates a name that is guaranteed to be unused
68    pub fn get_unused_name(&self) -> String {
69        let mut cur_nr = self.get_renderables(false).len();
70        loop {
71            let name = String::from("ent_") + &cur_nr.to_string();
72            let r_name = RString::from(name.clone());
73            if !self.name2entity.contains_key(&r_name) {
74                return name;
75            }
76            cur_nr += 1;
77        }
78    }
79
80    /// Creates a entity with a name or gets the one that already exists with
81    /// that concrete name You can keep adding components to this entity
82    /// with .insert()
83    pub fn get_or_create_hidden_entity(&mut self, name: &str) -> EntityMut {
84        let r_name = RString::from(name.to_string());
85        let entity_ref = self
86            .name2entity
87            .entry(r_name)
88            .or_insert_with(|| self.world.spawn((Name(name.to_string()),))); //to insert a single component we use a tuple like (x,)
89        EntityMut::new(&mut self.world, *entity_ref)
90    }
91
92    /// Creates a entity with a name or gets the one that already exists with
93    /// that concrete name You can keep adding components to this entity
94    /// with .insert() Also inserts a renderable component
95    pub fn get_or_create_entity(&mut self, name: &str) -> EntityMut {
96        let r_name = RString::from(name.to_string());
97        let entity_ref = self
98            .name2entity
99            .entry(r_name)
100            .or_insert_with(|| self.world.spawn((Name(name.to_string()), Renderable))); //to insert a single component we use a tuple like (x,)
101        EntityMut::new(&mut self.world, *entity_ref)
102    }
103
104    /// Despawns entity with a certain name and all it's components
105    pub fn despawn_with_name(&mut self, name: &str) {
106        //get the entity from the world if there is one, despawn it from the world and
107        // remove it from our internal hashmap
108        if let Some(entity) = self.get_entity_with_name(name) {
109            let _ = self.world.despawn(entity);
110            let r_name = RString::from(name.to_string());
111            let _ = self.name2entity.remove(&r_name);
112        }
113    }
114
115    pub fn despawn(&mut self, entity: Entity) {
116        //if the entity has a name get it so we can remove it form our hashmap of
117        // name2entity
118        let name = self.get_comp::<&Name>(&entity).map(|x| RString::from(x.0.to_string()));
119        if let Ok(name) = name {
120            let _ = self.name2entity.remove(&name);
121        }
122        let _ = self.world.despawn(entity);
123    }
124
125    /// # Panics
126    /// Will panic if no entity has that name
127    pub fn get_entity_with_name(&self, name: &str) -> Option<Entity> {
128        self.name2entity.get(name).copied()
129    }
130
131    /// # Panics
132    /// Will panic if no entity has that name
133    pub fn get_entity_mut_with_name(&mut self, name: &str) -> Option<EntityMut> {
134        let entity_opt = self.name2entity.get(name);
135        entity_opt.map(|ent| EntityMut::new(&mut self.world, *ent))
136    }
137
138    /// # Panics
139    /// Will return None if no entity with id found
140    pub fn find_entity_with_id(&mut self, id: u8) -> Option<EntityRef> {
141        let entities = self.get_all_entities(false);
142
143        for entity in entities {
144            if u32::from(id) == entity.id() {
145                let e_ref = self.world.entity(entity).unwrap();
146                return Some(e_ref);
147            }
148        }
149        None
150    }
151
152    /// # Panics
153    /// Will panic if there is no camera added yet
154    pub fn get_current_cam(&self) -> Option<Camera> {
155        // TODO this has to be done better that with just a hard coded name. Maybe a
156        // marker component on the camera?
157        let entity_opt = self.name2entity.get(GLOSS_CAM_NAME);
158        entity_opt.map(|ent| Camera::from_entity(*ent))
159    }
160
161    /// Use to create a unique component, similar to resources in Bevy
162    /// # Panics
163    /// Will panic if the entity that contains all the resources has not been
164    /// created
165    pub fn add_resource<T: gloss_hecs::Component>(&mut self, component: T) {
166        self.world.insert_one(self.entity_resource, component).unwrap();
167    }
168
169    /// # Panics
170    /// This function will panic if the entity that contains all the resources
171    /// has not been created.
172    ///
173    /// # Errors
174    /// This function will return an error if the required component does not
175    /// exist.
176    pub fn remove_resource<T: gloss_hecs::Component>(&mut self) -> Result<T, gloss_hecs::ComponentError> {
177        self.world.remove_one::<T>(self.entity_resource) //DO NOT unwrap. this
178                                                         // functions throws
179                                                         // error if the
180                                                         // component was
181                                                         // already removed but
182                                                         // we don't care if we
183                                                         // do repeted
184                                                         // remove_resource
185    }
186
187    /// # Panics
188    /// Will panic if the entity that contains all the resources has not been
189    /// created
190    pub fn has_resource<T: gloss_hecs::Component>(&self) -> bool {
191        self.world.has::<T>(self.entity_resource).unwrap()
192    }
193
194    /// Gets a resource which is a component shared between all entities
195    /// Use with: scene.get_resource::<&mut Component>();
196    /// # Errors
197    /// Will error if the entity that contains all the resources has not been
198    /// created
199    pub fn get_resource<'a, T: gloss_hecs::ComponentRef<'a>>(&'a self) -> Result<<T as ComponentRef<'a>>::Ref, gloss_hecs::ComponentError> {
200        self.world.get::<T>(self.entity_resource)
201    }
202
203    /// # Panics
204    /// Will panic if the entity has not been created
205    pub fn insert_if_doesnt_exist<T: gloss_hecs::Component + Default>(&mut self, entity: Entity) {
206        if !self.world.has::<T>(entity).unwrap() {
207            let _ = self.world.insert_one(entity, T::default());
208        }
209    }
210
211    /// Generic function to get a component for a certain entity. Merely
212    /// syntactic sugar. Use with: scene.get_comp::<&mut
213    /// Component>(&entity);
214    ///
215    /// # Errors
216    /// Will error if the component or the entity does not exist
217    pub fn get_comp<'a, T: gloss_hecs::ComponentRef<'a>>(
218        &'a self,
219        entity: &Entity,
220    ) -> Result<<T as ComponentRef<'a>>::Ref, gloss_hecs::ComponentError> {
221        self.world.get::<T>(*entity)
222    }
223
224    /// # Panics
225    /// Will panic if the entity does not have a name assigned
226    pub fn get_lights(&self, sorted_by_name: bool) -> Vec<Entity> {
227        let mut entities_with_name = Vec::new();
228        for (entity_light, (name, _)) in self.world.query::<(&Name, &LightEmit)>().iter() {
229            entities_with_name.push((entity_light, name.0.clone()));
230        }
231        //sort by name
232        if sorted_by_name {
233            entities_with_name.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
234        }
235        let entities = entities_with_name.iter().map(|x| x.0).collect();
236        entities
237    }
238
239    #[allow(clippy::missing_panics_doc)]
240    pub fn get_all_entities(&self, sorted_by_name: bool) -> Vec<Entity> {
241        let mut entities_with_name = Vec::new();
242        for (entity, name) in self.world.query::<&Name>().iter() {
243            entities_with_name.push((entity, name.0.clone()));
244        }
245        //sort by name
246        if sorted_by_name {
247            entities_with_name.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
248        }
249        let entities = entities_with_name.iter().map(|x| x.0).collect();
250        entities
251    }
252
253    #[allow(clippy::missing_panics_doc)]
254    pub fn get_renderables(&self, sorted_by_name: bool) -> Vec<Entity> {
255        let mut entities_with_name = Vec::new();
256        for (entity, (name, _)) in self.world.query::<(&Name, &Renderable)>().iter() {
257            entities_with_name.push((entity, name.0.clone()));
258        }
259        //sort by name
260        if sorted_by_name {
261            entities_with_name.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
262        }
263        let entities = entities_with_name.iter().map(|x| x.0).collect();
264        entities
265    }
266
267    pub fn get_renderable_names(&self) -> Vec<String> {
268        self.world
269            .query::<(&Name, &Renderable)>()
270            .iter()
271            .map(|(_, (name, _))| name.0.clone())
272            .collect()
273    }
274
275    // # Errors
276    // Will return an error if the entity with the given name is not found.
277    // # Panics
278    // Will panic if no entity has that name
279    // pub fn remove_renderable(&mut self, name: &str) -> Result<(), String> {
280    //     let r_name = RString::from(name.to_string());
281    //     if let Some(&entity) = self.name2entity.get(&r_name) {
282    //         self.command_buffer.despawn(entity);
283    //         self.command_buffer.run_on(&mut self.world);
284    //         self.name2entity.remove(&r_name);
285    //         Ok(())
286    //     } else {
287    //         Err(format!("Entity with name '{name}' not found"))
288    //     }
289    // }
290
291    #[allow(clippy::cast_possible_truncation)]
292    pub fn nr_renderables(&self) -> u32 {
293        self.get_renderables(false).len() as u32
294    }
295
296    /// get two points that define the minimal point of the scene in all
297    /// dimensions and the maximum point of the scene in all directions. These
298    /// two points would form a rectangle containing the whole scene without the
299    /// floor
300    pub fn get_bounding_points(&self) -> (na::Point3<f32>, na::Point3<f32>) {
301        let mut min_point_global = na::Point3::<f32>::new(f32::MAX, f32::MAX, f32::MAX);
302        let mut max_point_global = na::Point3::<f32>::new(f32::MIN, f32::MIN, f32::MIN);
303
304        for (_entity, (verts, model_matrix_opt, name, _)) in self.world.query::<(&Verts, Option<&ModelMatrix>, &Name, &Renderable)>().iter() {
305            if name.0 == GLOSS_FLOOR_NAME {
306                continue;
307            }
308
309            //Some meshes may not have a model matrix yet because the prepass hasn't run
310            let model_matrix = if let Some(mm) = model_matrix_opt {
311                mm.clone()
312            } else {
313                ModelMatrix::default()
314            };
315
316            trace!("scale for mesh {}", name.0);
317
318            //get min and max vertex in obj coords
319            let min_coord_vec: Vec<f32> = verts.0.min_vec();
320            let max_coord_vec: Vec<f32> = verts.0.max_vec();
321            // let min_coord_vec: Vec<f32> = verts.0.column_iter().map(|c|
322            // c.min()).collect(); let max_coord_vec: Vec<f32> =
323            // verts.0.column_iter().map(|c| c.max()).collect();
324            let min_point = na::Point3::<f32>::from_slice(&min_coord_vec);
325            let max_point = na::Point3::<f32>::from_slice(&max_coord_vec);
326
327            //get the points to world coords
328            let min_point_w = model_matrix.0 * min_point;
329            let max_point_w = model_matrix.0 * max_point;
330
331            //get the min/max between these points of this mesh and the global one
332            min_point_global = min_point_global.inf(&min_point_w);
333            max_point_global = max_point_global.sup(&max_point_w);
334        }
335
336        //for entities that have no verts, but have a bounding box, we compute the bounding points based on that.
337        //this can happen for entities that are created directly on gpu, and they have only VertsGPU and no Verts components.
338        for (_entity, (bbox, name, _)) in self.world.query::<(&BoundingBox, &Name, &Renderable)>().without::<&Verts>().iter() {
339            if name.0 == GLOSS_FLOOR_NAME {
340                continue;
341            }
342            min_point_global = min_point_global.inf(&bbox.min);
343            max_point_global = max_point_global.sup(&bbox.max);
344        }
345
346        (min_point_global, max_point_global)
347    }
348
349    pub fn get_min_y(&self) -> f32 {
350        let mut min_y_global = f32::MAX;
351
352        for (_entity, (verts, model_matrix_opt, name, _)) in self.world.query::<(&Verts, Option<&ModelMatrix>, &Name, &Renderable)>().iter() {
353            if name.0 == GLOSS_FLOOR_NAME {
354                continue;
355            }
356
357            let model_matrix = if let Some(mm) = model_matrix_opt {
358                mm.clone()
359            } else {
360                ModelMatrix::default()
361            };
362
363            //get min and max vertex in obj coords
364            let v_world = geom::transform_verts(&verts.0.to_dmatrix(), &model_matrix.0);
365            let min_y_cur = v_world.column(1).min();
366            min_y_global = min_y_global.min(min_y_cur);
367        }
368
369        //for entities that have no verts, but have a bounding box, we compute the min_y based on that.
370        //this can happen for entities that are created directly on gpu, and they have only VertsGPU and no Verts components.
371        for (_entity, (bbox, name, _)) in self.world.query::<(&BoundingBox, &Name, &Renderable)>().without::<&Verts>().iter() {
372            if name.0 == GLOSS_FLOOR_NAME {
373                continue;
374            }
375            min_y_global = min_y_global.min(bbox.min.y);
376        }
377
378        min_y_global
379    }
380
381    /// get the scale of the scene. useful for adding camera or lights
382    /// automtically
383    pub fn get_scale(&self) -> f32 {
384        if self.get_renderables(false).is_empty() {
385            error!("scale: no renderables, returning 1.0");
386            return 1.0;
387        }
388
389        let (min_point_global, max_point_global) = self.get_bounding_points();
390
391        //get scale as the maximum distance between any of the coordinates
392        let scale = (max_point_global - min_point_global).abs().max();
393
394        if scale.is_infinite() {
395            1.0
396        } else {
397            scale
398        }
399    }
400
401    pub fn get_centroid(&self) -> na::Point3<f32> {
402        if self.get_renderables(false).is_empty() {
403            error!("centroid: no renderables, returning 1.0");
404            return na::Point3::<f32>::origin();
405        }
406
407        let (min_point_global, max_point_global) = self.get_bounding_points();
408
409        //exactly the miggle between min and max
410        min_point_global.lerp(&max_point_global, 0.5)
411    }
412
413    pub fn init_3_point_light(&self, light_config: &mut LightConfig, idx: usize, scale: f32, centroid: &na::Point3<f32>) {
414        let (mut dir_movement, intensity_at_point) = match idx {
415            0 => {
416                let dir_movement = na::Vector3::new(0.5, 0.6, 0.5);
417                let intensity_at_point = 2.9;
418                (dir_movement, intensity_at_point)
419            }
420            1 => {
421                let dir_movement = na::Vector3::new(-0.5, 0.6, 0.5);
422                let intensity_at_point = 1.0;
423                (dir_movement, intensity_at_point)
424            }
425            2 => {
426                let dir_movement = na::Vector3::new(-0.1, 0.6, -0.5);
427                let intensity_at_point = 3.5;
428                (dir_movement, intensity_at_point)
429            }
430            //rest of light that are not in the 3 point light
431            _ => {
432                let dir_movement = na::Vector3::new(0.0, 0.6, 0.5);
433                let intensity_at_point = 2.9;
434                (dir_movement, intensity_at_point)
435            }
436        };
437
438        dir_movement = dir_movement.normalize();
439        let lookat = centroid;
440        // let position = centroid + dir_movement * 3.5 * scale; //move the light
441        // starting from the center in the direction by a certain amout so that in
442        // engulfs the whole scene
443        let position = centroid + dir_movement * 8.0 * scale; //move the light starting from the center in the direction by a certain amout
444                                                              // so that in engulfs the whole scene
445        let intensity = Light::intensity_for_point(&position, lookat, intensity_at_point);
446
447        if light_config.position.is_none() {
448            light_config.position = Some(position);
449        }
450        if light_config.lookat.is_none() {
451            light_config.lookat = Some(*lookat);
452        }
453        if light_config.near.is_none() {
454            light_config.near = Some(scale * 0.5);
455        }
456        if light_config.far.is_none() {
457            light_config.far = Some(scale * 30.0);
458        }
459        if light_config.intensity.is_none() {
460            light_config.intensity = Some(intensity);
461        }
462        if light_config.range.is_none() {
463            light_config.range = Some(scale * 30.0);
464        }
465        if light_config.radius.is_none() {
466            light_config.radius = Some(scale * 1.5);
467        }
468        if light_config.shadow_res.is_none() {
469            light_config.shadow_res = Some(2048);
470        }
471        if light_config.shadow_bias_fixed.is_none() {
472            light_config.shadow_bias_fixed = Some(2e-6);
473        }
474        if light_config.shadow_bias.is_none() {
475            light_config.shadow_bias = Some(2e-6);
476        }
477        if light_config.shadow_bias_normal.is_none() {
478            light_config.shadow_bias_normal = Some(2e-6);
479        }
480    }
481
482    pub fn make_concrete_config(&self, config: &mut Config) {
483        let scale = self.get_scale();
484        let centroid = self.get_centroid();
485        let min_y = self.get_min_y();
486
487        //core
488        let floor_scale_multiplier = 300.0;
489        if config.core.floor_scale.is_none() {
490            config.core.floor_scale = Some(scale * floor_scale_multiplier); //just make a realy large plane. At some point we should change it to be actualyl infinite
491        }
492        if config.core.floor_origin.is_none() {
493            config.core.floor_origin = Some(na::Point3::<f32>::new(centroid.x, min_y, centroid.z));
494        }
495        if config.core.floor_uv_scale.is_none() {
496            config.core.floor_uv_scale = Some(scale * floor_scale_multiplier * 1.4);
497        }
498
499        //camera
500        let position = centroid + na::Vector3::z_axis().scale(2.0 * scale) + na::Vector3::y_axis().scale(0.5 * scale);
501        if config.scene.cam.position.is_none() {
502            config.scene.cam.position = Some(position);
503        }
504        if config.scene.cam.lookat.is_none() {
505            config.scene.cam.lookat = Some(centroid);
506        }
507        if config.scene.cam.near.is_none() {
508            let near = (centroid - position).norm() * 0.02;
509            config.scene.cam.near = Some(near);
510        }
511        if config.scene.cam.far.is_none() {
512            let far = (centroid - position).norm() * 50.0; //far plane can be quite big. The near plane shouldn't be too tiny because it
513                                                           // make the depth have very little precision
514            config.scene.cam.far = Some(far);
515        }
516
517        //render
518        if config.render.distance_fade_center.is_none() {
519            config.render.distance_fade_center = Some(centroid);
520        }
521        if config.render.distance_fade_start.is_none() {
522            config.render.distance_fade_start = Some(scale * 1.0);
523        }
524        if config.render.distance_fade_end.is_none() {
525            config.render.distance_fade_end = Some(scale * 8.5);
526        }
527
528        //lights
529        // let three_point_lights = self.create_3_point_light_configs(scale, centroid);
530        for (idx, light_config) in config.scene.lights.iter_mut().enumerate() {
531            self.init_3_point_light(light_config, idx, scale, &centroid);
532        }
533
534        //specify that it is now concrete so we don't rerun this function
535        config.set_concrete();
536    }
537
538    /// # Panics
539    /// Will panic if the camera entity has not yet been created
540    #[allow(clippy::cast_precision_loss)]
541    #[allow(clippy::needless_update)]
542    #[allow(clippy::too_many_lines)]
543    pub fn from_config(&mut self, config: &mut Config, width: u32, height: u32) {
544        //camera=============
545        let cam = self.get_current_cam().expect("Camera should be created");
546        //add a pos lookat if there is none
547        if !self.world.has::<PosLookat>(cam.entity).unwrap() {
548            self.world
549                .insert(
550                    cam.entity,
551                    (
552                        PosLookat::new(config.scene.cam.position.unwrap(), config.scene.cam.lookat.unwrap()),
553                        CamController::new(
554                            config.scene.cam.limit_max_dist,
555                            config.scene.cam.limit_max_vertical_angle,
556                            config.scene.cam.limit_min_vertical_angle,
557                        ),
558                    ),
559                )
560                .unwrap();
561        }
562        //add a projection if there is none
563        if !self.world.has::<Projection>(cam.entity).unwrap() {
564            let aspect_ratio = width as f32 / height as f32;
565            self.world
566                .insert(
567                    cam.entity,
568                    (Projection::WithFov(ProjectionWithFov {
569                        aspect_ratio,
570                        fovy: config.scene.cam.fovy,
571                        near: config.scene.cam.near.unwrap(),
572                        far: config.scene.cam.far.unwrap(),
573                        ..Default::default()
574                    }),),
575                )
576                .unwrap();
577        }
578
579        //create lights
580        for (idx, light_config) in config.scene.lights.iter_mut().enumerate() {
581            let entity = self
582                .get_or_create_hidden_entity(("light_".to_owned() + idx.to_string().as_str()).as_str())
583                .insert(PosLookat::new(light_config.position.unwrap(), light_config.lookat.unwrap()))
584                .insert(Projection::WithFov(ProjectionWithFov {
585                    aspect_ratio: 1.0,
586                    fovy: light_config.fovy, //radians
587                    near: light_config.near.unwrap(),
588                    far: light_config.far.unwrap(),
589                    ..Default::default()
590                }))
591                .insert(LightEmit {
592                    color: light_config.color,
593                    intensity: light_config.intensity.unwrap(),
594                    range: light_config.range.unwrap(),
595                    radius: light_config.radius.unwrap(),
596                    ..Default::default()
597                })
598                .entity;
599            //shadow
600            let shadow_res = light_config.shadow_res.unwrap_or(0);
601            if shadow_res != 0 {
602                self.world
603                    .insert_one(
604                        entity,
605                        ShadowCaster {
606                            shadow_res: light_config.shadow_res.unwrap(),
607                            shadow_bias_fixed: light_config.shadow_bias_fixed.unwrap(),
608                            shadow_bias: light_config.shadow_bias.unwrap(),
609                            shadow_bias_normal: light_config.shadow_bias_normal.unwrap(),
610                        },
611                    )
612                    .ok();
613            }
614            // .insert(ShadowCaster { shadow_res: 2048 });
615        }
616
617        //floor
618        if config.core.auto_add_floor {
619            self.create_floor(config);
620        }
621
622        //specify that it is now consumed so we don't rerun this function again
623        config.set_consumed();
624    }
625
626    pub fn create_floor(&mut self, config: &Config) {
627        #[allow(clippy::cast_possible_truncation)]
628        #[allow(clippy::cast_sign_loss)]
629        let floor_builder = match config.core.floor_type {
630            FloorType::Solid => builders::build_plane(
631                config.core.floor_origin.unwrap(),
632                na::Vector3::<f32>::new(0.0, 1.0, 0.0),
633                config.core.floor_scale.unwrap(),
634                config.core.floor_scale.unwrap(),
635                false,
636            ),
637            FloorType::Grid => builders::build_grid(
638                config.core.floor_origin.unwrap(),
639                na::Vector3::<f32>::new(0.0, 1.0, 0.0),
640                (config.core.floor_scale.unwrap() / 1.0) as u32,
641                (config.core.floor_scale.unwrap() / 1.0) as u32,
642                config.core.floor_scale.unwrap(),
643                config.core.floor_scale.unwrap(),
644                false,
645            ),
646        };
647
648        #[allow(clippy::cast_possible_truncation)]
649        let floor_ent = self
650            .get_or_create_entity(GLOSS_FLOOR_NAME)
651            .insert_builder(floor_builder)
652            .insert(VisMesh {
653                show_mesh: config.core.floor_type == FloorType::Solid,
654                solid_color: na::Vector4::<f32>::new(0.08, 0.08, 0.08, 1.0), //106
655                // perceptual_roughness: 0.75,
656                perceptual_roughness: 0.70,
657                uv_scale: config.core.floor_uv_scale.unwrap(),
658                // metalness: 1.0,
659                color_type: MeshColorType::Texture,
660                ..Default::default()
661            })
662            .insert(VisPoints::default())
663            .insert(VisLines {
664                show_lines: config.core.floor_type == FloorType::Grid,
665                line_color: na::Vector4::<f32>::new(0.2, 0.2, 0.2, 1.0),
666                line_width: config.core.floor_grid_line_width,
667                antialias_edges: true,
668                ..Default::default()
669            })
670            .entity();
671
672        if config.core.floor_texture == FloorTexture::Checkerboard {
673            let texture_checkerboard = DiffuseImg::new_from_buf(
674                CHECKERBOARD_BYTES,
675                &ImgConfig {
676                    mipmap_generation_cpu: true, /* we keep the generation of mipmaps on cpu because GPU one doesn't run on wasm due to webgl2 not
677                                                  * allowing reading and writing from the same texture even if it's different mipmaps :( */
678                    ..Default::default()
679                },
680            );
681            let _ = self.world.insert_one(floor_ent, texture_checkerboard);
682        }
683    }
684
685    #[allow(clippy::missing_panics_doc)]
686    pub fn get_floor(&self) -> Option<Actor> {
687        let ent_opt = self.name2entity.get(GLOSS_FLOOR_NAME);
688        ent_opt.map(|ent| Actor::from_entity(*ent))
689    }
690
691    pub fn has_floor(&self) -> bool {
692        self.name2entity.get(GLOSS_FLOOR_NAME).is_some()
693    }
694
695    pub fn create_camera(&mut self) {
696        Camera::new(GLOSS_CAM_NAME, self, false);
697    }
698
699    pub fn remove_all_gpu_components(&mut self) {
700        //TODO this is very brittle, need to somehow mark the gpu components somehow
701        let mut command_buffer = CommandBuffer::new();
702        for (entity, ()) in self.world.query::<()>().iter() {
703            command_buffer.remove_one::<VertsGPU>(entity);
704            command_buffer.remove_one::<UVsGPU>(entity);
705            command_buffer.remove_one::<NormalsGPU>(entity);
706            command_buffer.remove_one::<ColorsGPU>(entity);
707            command_buffer.remove_one::<EdgesGPU>(entity);
708            command_buffer.remove_one::<FacesGPU>(entity);
709            command_buffer.remove_one::<TangentsGPU>(entity);
710            command_buffer.remove_one::<DiffuseTex>(entity);
711            command_buffer.remove_one::<NormalTex>(entity);
712            command_buffer.remove_one::<MetalnessTex>(entity);
713            command_buffer.remove_one::<RoughnessTex>(entity);
714            command_buffer.remove_one::<EnvironmentMapGpu>(entity);
715            command_buffer.remove_one::<ShadowMap>(entity);
716        }
717
718        command_buffer.run_on(&mut self.world);
719    }
720}
721
722/// A mutable reference to a particular [`Entity`] and all of its components
723pub struct EntityMut<'w> {
724    world: &'w mut World,
725    entity: Entity,
726    // location: EntityLocation,
727}
728
729//similar to bevys entitymut https://docs.rs/bevy_ecs/latest/src/bevy_ecs/world/entity_ref.rs.html#183
730impl<'w> EntityMut<'w> {
731    pub(crate) fn new(world: &'w mut World, entity: Entity) -> Self {
732        EntityMut { world, entity }
733    }
734
735    /// Inserts a component to this entity.
736    /// This will overwrite any previous value(s) of the same component type.
737    pub fn insert<T: Component>(&mut self, component: T) -> &mut Self {
738        self.insert_bundle((component,))
739    }
740    /// Inserts a [`Bundle`] of components to the entity.
741    /// This will overwrite any previous value(s) of the same component type.
742    pub fn insert_bundle(&mut self, bundle: impl DynamicBundle) -> &mut Self {
743        let _ = self.world.insert(self.entity, bundle);
744        self
745    }
746    /// Inserts a [`EntityBuilder`] of components to the entity.
747    /// This will overwrite any previous value(s) of the same component type.
748    pub fn insert_builder(&mut self, mut builder: EntityBuilder) -> &mut Self {
749        let _ = self.world.insert(self.entity, builder.build());
750        self
751    }
752    /// Convenience function to get a component. Mostly useful for the python
753    /// bindings. # Panics
754    /// Will panic if the entity does not exist in the world
755    pub fn get_comp<'a, T: gloss_hecs::ComponentRef<'a>>(&'a self) -> <T as ComponentRef<'a>>::Ref {
756        let comp = self.world.get::<T>(self.entity).unwrap();
757        comp
758    }
759    pub fn entity(&self) -> Entity {
760        self.entity
761    }
762}