system.rs
1    use std::f32::consts::PI;
2    use std::path::PathBuf;
3    use std::time::Instant;
4    
5    use cgmath::{Deg, Euler, Point3, Quaternion, Rad};
6    use imgui::*;
7    use imgui::{Condition, FontSource, Ui};
8    use legion::systems::CommandBuffer;
9    use legion::world::SubWorld;
10   use legion::IntoQuery;
11   use legion::*;
12   use nalgebra::Quaternion as naQuaternion;
13   use rapier3d::dynamics::{
14       IntegrationParameters, JointSet, MassProperties, RigidBodyBuilder, RigidBodySet,
15   };
16   use rapier3d::geometry::{BroadPhase, ColliderBuilder, ColliderSet, NarrowPhase};
17   use rapier3d::na::{Isometry3, Vector, Vector3};
18   use rapier3d::pipeline::{ChannelEventCollector, PhysicsPipeline};
19   
20   use crate::camera::{Camera, CameraController};
21   use crate::components::{Collider, ImguiWindow, LoopState, Mesh, Physics, Position};
22   use crate::geometry;
23   use crate::physics::state::PhysicsState;
24   use crate::render::state::RenderState;
25   use crate::render::system::{ImguiGenericOutputLine, ImguiPerformanceProfilerLine};
26   use crate::runtime::state::RuntimeState;
27   use crate::geometry::RawMesh;
28   
29   pub fn quad_color(color: [f32; 4]) -> [[f32; 4]; 4] {
30       [color, color, color, color]
31   }
32   
33   #[system]
34   #[write_component(Mesh)]
35   pub fn runtime_load(
36       cmd: &mut CommandBuffer,
37       world: &mut SubWorld,
38       #[resource] runtime_state: &mut RuntimeState,
39   ) {
40       runtime_state.preload_meshes(PathBuf::from("./resources"));
41   
42       let entity: Entity = cmd.push((ImguiWindow {
43           // a window that does everything for the performance profiler
44           window: || {
45               imgui::Window::new(im_str!("Performance Profiler"))
46                   .size([400.0, 200.0], Condition::FirstUseEver)
47                   .position([10.0, 10.0], Condition::FirstUseEver)
48           },
49           func: |ui: &Ui, a: Vec<&ImguiPerformanceProfilerLine>| {
50               // ui.text(im_str!("Performance Graph"));
51   
52               let draw_list = ui.get_window_draw_list();
53               let top_left = ui.cursor_screen_pos();
54               let region_size = ui.content_region_avail();
55   
56               let region_size = [region_size[0] * 0.80, region_size[1]];
57   
58               // Fill rect
59               let qcolor = quad_color([0.5, 0.5, 1.0, 0.1]);
60               draw_list.add_rect_filled_multicolor(
61                   top_left,
62                   [top_left[0] + (region_size[0]), top_left[1] + region_size[1]],
63                   qcolor[0],
64                   qcolor[1],
65                   qcolor[2],
66                   qcolor[3],
67               );
68   
69               for profiler_line in a {
70                   let x_scale = (region_size[0]) / 400.0;
71                   let y_scale = region_size[1] / profiler_line.scale_max;
72                   profiler_line
73                       .iter_data_from_head()
74                       .fold((0, 0.0f32), |accum, &fps_val| {
75                           let x1 = accum.0 as f32 * x_scale + top_left[0];
76                           let y1 = top_left[1] + region_size[1] - accum.1 * y_scale;
77                           let x2 = (accum.0 as f32 + 1.0) * x_scale + top_left[0];
78                           let y2 = top_left[1] + region_size[1] - fps_val * y_scale;
79                           let p1 = [x1, y1];
80                           let p2 = [x2, y2];
81                           draw_list
82                               .add_line(p1, p2, [1.0, 1.0, 0.0, 0.8])
83                               .thickness(1.0)
84                               .build();
85                           (accum.0 + 1, fps_val)
86                       });
87   
88                   let text_x = (region_size[0] + top_left[0]);
89                   let text_y = top_left[1] + region_size[1]
90                       - profiler_line.current_average_label().0 * y_scale;
91                   draw_list.add_text(
92                       [text_x, text_y],
93                       [1.0, 1.0, 0.0, 1.0],
94                       format!(
95                           "{} {:.0}",
96                           profiler_line.current_average_label().1,
97                           1.0 / profiler_line.current_average_label().0
98                       ),
99                   );
100              }
101          },
102      },));
103      let entity: Entity = cmd.push((ImguiPerformanceProfilerLine::new("RenderFPS".to_string()),));
104  
105      let entity: Entity = cmd.push((ImguiWindow {
106          // a window that does everything for the performance profiler
107          window: || {
108              imgui::Window::new(im_str!("Generic Output"))
109                  .size([400.0, 500.0], Condition::FirstUseEver)
110                  .position([50.0, 250.0], Condition::FirstUseEver)
111          },
112          func: |ui: &Ui, a: Vec<&ImguiGenericOutputLine>| {
113              for label in a {
114                  ui.text(im_str!("{}", label.label));
115              }
116          },
117      },));
118  }
119  
120  #[system]
121  #[write_component(Mesh)]
122  pub fn runtime_spawn(
123      cmd: &mut CommandBuffer,
124      world: &mut SubWorld,
125      #[resource] runtime_state: &mut RuntimeState,
126      #[resource] renderer: &mut RenderState,
127  ) {
128      for entity in &runtime_state.get_entities() {
129          match entity.type_name.as_ref() {
130              "Sprite" => {
131  
132  
133  
134                  let mesh_name = entity.mesh.clone().unwrap();
135                  let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) {
136                      None => {
137                          log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name);
138                          continue;
139                      }
140                      Some(mesh) => mesh,
141                  };
142  
143                  let position = Position::from(entity.position.clone());
144  
145                  let mut static_body = RigidBodyBuilder::new_static()
146                      .position(Isometry3::new(
147                          Vector3::new(position.x, position.y, position.z),
148                          Vector::y(),
149                      ))
150                      .build();
151  
152                  let mesh_collider = ColliderBuilder::trimesh(
153                      raw_mesh.vertices.iter().map(|v| v.position()).collect(),
154                      raw_mesh.indices.clone(),
155                  )
156                      .build();
157  
158                  let gpu_mesh_buffer = renderer
159                      .upload_mesh_to_buffer(
160                          raw_mesh,
161                          Some(wgpu::Color {
162                              r: 1.0,
163                              g: 0.7,
164                              b: 0.3,
165                              a: 1.0,
166                          }),
167                      )
168                      .unwrap();
169  
170                  let entity: Entity = cmd.push((
171                      position,
172                      gpu_mesh_buffer,
173                      Physics {
174                          rigid_body: static_body,
175                          rigid_body_handle: None,
176                      },
177                      Collider {
178                          collider: mesh_collider,
179                          collider_handle: None,
180                      },
181                  ));
182              }
183              "PhysicsEntity" => {
184                  let mesh_name = entity.mesh.as_ref().unwrap();
185                  let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) {
186                      None => {
187                          log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name);
188                          continue;
189                      }
190                      Some(mesh) => mesh,
191                  };
192  
193                  let collider = ColliderBuilder::trimesh(
194                      raw_mesh.vertices.iter().map(|v| v.position()).collect(),
195                      raw_mesh.indices.clone(),
196                  )
197                  .build();
198  
199                  let collider = ColliderBuilder::capsule_y(2.0, 1.0).build();
200  
201                  let gpu_mesh_buffer = renderer
202                      .upload_mesh_to_buffer(
203                          raw_mesh,
204                          Some(wgpu::Color {
205                              r: 1.0,
206                              g: 0.7,
207                              b: 0.3,
208                              a: 1.0,
209                          }),
210                      )
211                      .unwrap();
212  
213                  let position = Position::from(entity.position.clone());
214  
215                  let mut dynamic_body = RigidBodyBuilder::new_dynamic()
216                      .can_sleep(false)
217                      .mass(100.0)
218                      .translation(position.x, position.y, position.z)
219                      .build();
220  
221                  let entity: Entity = cmd.push((
222                      position,
223                      gpu_mesh_buffer,
224                      Physics {
225                          rigid_body: dynamic_body,
226                          rigid_body_handle: None,
227                      },
228                      Collider {
229                          collider: collider,
230                          collider_handle: None,
231                      },
232                      ImguiGenericOutputLine::new("wahoo! from a physics entity".to_string()),
233                  ));
234              }
235              "StaticMesh" => {
236                  let mesh_name = entity.mesh.clone().unwrap();
237                  let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) {
238                      None => {
239                          log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name);
240                          continue;
241                      }
242                      Some(mesh) => mesh,
243                  };
244  
245                  let position = Position::from(entity.position.clone());
246  
247                  let mut static_body = RigidBodyBuilder::new_static()
248                      .position(Isometry3::new(
249                          Vector3::new(position.x, position.y, position.z),
250                          Vector::y(),
251                      ))
252                      .build();
253  
254                  let mesh_collider = ColliderBuilder::trimesh(
255                      raw_mesh.vertices.iter().map(|v| v.position()).collect(),
256                      raw_mesh.indices.clone(),
257                  )
258                  .build();
259  
260                  let gpu_mesh_buffer = renderer
261                      .upload_mesh_to_buffer(
262                          raw_mesh,
263                          Some(wgpu::Color {
264                              r: 1.0,
265                              g: 0.7,
266                              b: 0.3,
267                              a: 1.0,
268                          }),
269                      )
270                      .unwrap();
271  
272                  let entity: Entity = cmd.push((
273                      position,
274                      gpu_mesh_buffer,
275                      Physics {
276                          rigid_body: static_body,
277                          rigid_body_handle: None,
278                      },
279                      Collider {
280                          collider: mesh_collider,
281                          collider_handle: None,
282                      },
283                  ));
284              }
285              "Camera" => {
286                  let position = Position::from(entity.position.clone());
287                  let entity: Entity = cmd.push((
288                      Camera {
289                          position: cgmath::Point3 {
290                              x: position.x,
291                              y: position.y,
292                              z: position.z,
293                          },
294                          yaw: Rad(PI / 2.0),
295                          pitch: Rad(PI / 2.0 + 25.0),
296                      },
297                      CameraController::new(5.0, 1.0),
298                      ImguiGenericOutputLine::new("wahoo! from a camera".to_string()),
299                  ));
300              }
301              "Light" => {
302                  let mesh_name = entity.mesh.clone().unwrap();
303                  let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) {
304                      None => {
305                          log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name);
306                          continue;
307                      }
308                      Some(mesh) => mesh,
309                  };
310  
311                  let position = Position::from(entity.position.clone());
312  
313                  let gpu_mesh_buffer = renderer
314                      .upload_mesh_to_buffer(
315                          raw_mesh,
316                          Some(wgpu::Color {
317                              r: 1.0,
318                              g: 0.7,
319                              b: 0.3,
320                              a: 1.0,
321                          }),
322                      )
323                      .unwrap();
324  
325                  let light_entity: Entity =
326                      cmd.push((position, gpu_mesh_buffer, renderer.create_light()));
327              }
328  
329              _ => {}
330          }
331      }
332  }
333