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