main.rs
1    extern crate env_logger;
2    extern crate imgui;
3    extern crate imgui_wgpu;
4    #[macro_use]
5    extern crate lazy_static;
6    #[macro_use]
7    extern crate serde_derive;
8    extern crate tobj;
9    extern crate toml;
10   extern crate winit_24;
11   
12   use std::collections::HashMap;
13   use std::f32::consts::PI;
14   use std::fs;
15   use std::sync::{Arc, Mutex};
16   #[cfg(not(target_arch = "wasm32"))]
17   use std::time::{Duration, Instant};
18   
19   use cgmath::{
20       Decomposed, Deg, Euler, InnerSpace, Point3, Quaternion, Rad, Rotation3, SquareMatrix,
21   };
22   use futures::executor::block_on;
23   use futures::task::LocalSpawn;
24   use futures::FutureExt;
25   use gilrs::Event as GilEvent;
26   use gilrs::{Gamepad, Gilrs};
27   use imgui::FontSource;
28   use imgui::__core::convert::TryInto;
29   use imgui::*;
30   use imgui_wgpu::{Renderer as ImguiRenderer, RendererConfig as ImguiRendererConfig};
31   use legion::systems::{SyncResources, UnsafeResources};
32   use legion::*;
33   use log::LevelFilter;
34   use rapier3d::counters::Timer;
35   use rapier3d::dynamics::{
36       IntegrationParameters, JointSet, RigidBody, RigidBodyBuilder, RigidBodyHandle, RigidBodySet,
37   };
38   use rapier3d::geometry::{BroadPhase, ColliderBuilder, ColliderHandle, ColliderSet, NarrowPhase};
39   use rapier3d::math;
40   use rapier3d::math::Point;
41   use rapier3d::na::{Isometry, Isometry3, Vector, Vector3};
42   use rapier3d::pipeline::PhysicsPipeline;
43   use wgpu::{BindGroup, Buffer, TextureView};
44   use wgpu_subscriber;
45   use winit_24::event::DeviceEvent::MouseMotion;
46   use winit_24::event::{ElementState, VirtualKeyCode};
47   use winit_24::event_loop::EventLoopProxy;
48   use winit_24::platform::unix::x11::ffi::Time;
49   use winit_24::window::Window;
50   use winit_24::{
51       event::{self, WindowEvent},
52       event_loop::{ControlFlow, EventLoop},
53   };
54   
55   use crate::camera::{Camera, CameraController};
56   use crate::components::{Collider, ImguiWindow, LoopState, Physics, Position};
57   use crate::geometry::{load_obj, RawMesh};
58   use crate::imgui_supp::extended_winit_imgui_support;
59   use crate::imgui_supp::imgui_support::{ImguiContext, ImguiPlatform};
60   use crate::owned_event::{OwnedEvent, OwnedEventExtension};
61   use crate::physics::state::PhysicsState;
62   use crate::render::system::ImguiPerformanceProfilerLine;
63   use crate::runtime::state::RuntimeState;
64   use winit_24::dpi::PhysicalSize;
65   
66   mod camera;
67   mod components;
68   mod geometry;
69   mod imgui_supp;
70   mod light;
71   mod owned_event;
72   mod physics;
73   mod render;
74   mod runtime;
75   
76   /* 
77    
78   Collision detection 
79   https://nphysics.org/rigid_body_simulations_with_contacts/ 
80    
81   Obj file format 
82   http://paulbourke.net/dataformats/obj/ 
83    
84   tobj obj loader 
85   https://docs.rs/tobj/2.0.3/tobj/index.html 
86    
87   mesh generator lib, might be useful 
88   https://docs.rs/genmesh/0.6.2/genmesh/ 
89    
90   legion ECS 
91   https://github.com/amethyst/legion 
92    
93    
94   ECS 
95       animation 
96       config / save loading (sorta!) 
97       render 3d (good!) 
98       input/io (yep!) 
99       collision / physics (yep!) 
100      entities & behaviours (got the entities!) 
101      scripting! 
102   
103   
104  Todo: 
105      Load scene imgui interface w/ toml files 
106      FPS graph port from voxel raycaster 
107      better imgui interface with components & systems 
108      Figure out eventing, GameInput, passing all events, etc. 
109      + texturing 
110   
111  I need to figure out the way that I want to do 2d graphics in a 3d engine... 
112  I suppose I will need sprites. And those are just 2 polygons which are textured 
113   
114   
115   
116   */
117  
118  //log::info!("");
119  
120  // ImGUI works on more or less an unsafe global state. which is MegaLame
121  static mut CURRENT_UI: Option<imgui::Ui<'static>> = None;
122  pub unsafe fn current_ui<'a>() -> Option<&'a imgui::Ui<'a>> {
123      CURRENT_UI.as_ref()
124  }
125  
126  
127  
128  fn main() {
129      let logger = env_logger::builder()
130          .filter(Some("minimal_viable_game_engine"), LevelFilter::Info)
131          .init();
132  
133      let mut world = World::default();
134  
135      let mut imgui_prepare_schedule = Schedule::builder()
136          .add_system(render::system::imgui_prepare_system())
137          .build();
138  
139      let mut load_schedule = Schedule::builder()
140          .add_system(runtime::system::runtime_load_system())
141          .add_system(runtime::system::runtime_spawn_system())
142          .flush()
143          .build();
144  
145      let mut render_schedule = Schedule::builder()
146          .add_system(render::system::render_imgui_system())
147          .add_system(render::system::render_test_system())
148          .add_system(render::system::render_performance_flag_system())
149          .build();
150  
151      let mut update_schedule = Schedule::builder()
152          .add_system(physics::system::update_camera_system())
153          .add_system(physics::system::run_physics_system())
154          .add_system(physics::system::update_models_system())
155          // next system here, gamelogic update system?
156          .build();
157  
158      let mut event_schedule = Schedule::builder()
159          .add_system(owned_event::event_dispatch_system())
160          .build();
161  
162      let event_loop = EventLoop::<OwnedEventExtension>::with_user_event();
163      let mut builder = winit_24::window::WindowBuilder::new();
164      builder = builder.with_title("MVGE");
165      builder = builder.with_inner_size(PhysicalSize::new(1200,900));
166  
167      let window = builder.build(&event_loop).unwrap();
168  
169      let mut resources = Resources::default();
170  
171  
172  
173      // Load up all the resources
174      {
175          let mut imgui_context = imgui::Context::create();
176          let mut platform = extended_winit_imgui_support::WinitPlatform::init(&mut imgui_context);
177          platform.attach_window(
178              imgui_context.io_mut(),
179              &window,
180              extended_winit_imgui_support::HiDpiMode::Default,
181          );
182  
183          // imgui_supp rendering context
184          let mut imgui_context = ImguiContext {
185              context: imgui_context,
186          };
187          let mut imgui_platform = ImguiPlatform { platform: platform };
188          let font_size = 20.0 as f32;
189          imgui_context.context.io_mut().font_global_scale = 1.0 as f32;
190          imgui_context
191              .context
192              .fonts()
193              .add_font(&[FontSource::DefaultFontData {
194                  config: Some(imgui::FontConfig {
195                      oversample_h: 1,
196                      pixel_snap_h: true,
197                      size_pixels: font_size,
198                      ..Default::default()
199                  }),
200              }]);
201          imgui_context.context.set_ini_filename(None);
202  
203          // The renderer
204          let mut renderer = render::state::RenderState::init(&window, &mut imgui_context);
205  
206          resources.insert(renderer);
207          resources.insert(Arc::new(Mutex::new(imgui_context)));
208          resources.insert(Arc::new(Mutex::new(imgui_platform)));
209          resources.insert(window);
210  
211          // Physics
212          let (physics_state, physics_pipeline) =
213              PhysicsState::build(rapier3d::math::Vector::new(0.0, -9.81, 0.0));
214          resources.insert(physics_state);
215          resources.insert(physics_pipeline);
216  
217          // Loop data
218          resources.insert(LoopState {
219              delta_time: Default::default(),
220              start_time: Instant::now(),
221              step_size: 0.01666, // 60hz
222          });
223  
224          // And our event stack
225          resources.insert(Vec::<OwnedEvent<OwnedEventExtension>>::new());
226  
227          // Our init and runtime data
228          resources.insert(RuntimeState::new())
229      };
230  
231      setup_gamepad(&event_loop);
232  
233      let mut elapsed_time: f32 = {
234          // deltatime since last frame
235          let loop_state = resources.get::<LoopState>().unwrap();
236          loop_state.start_time.elapsed()
237      }
238      .as_secs_f32();
239  
240      let mut delta_time: f32 = 0.0;
241      let mut accumulator_time: f32 = 0.0;
242      let mut current_time: f32 = elapsed_time;
243  
244      event_loop.run(move |event, _, control_flow| {
245          *control_flow = ControlFlow::Poll;
246          match event {
247              event::Event::NewEvents(cause) => {
248                  if cause == winit_24::event::StartCause::Init {
249                      load_schedule.execute(&mut world, &mut resources);
250                  }
251              }
252  
253              // This is the big boy section of the event loop
254              // We : dispatch events and clear the queue, query the loops
255              //      time data and prep the dt data. Loop the dt locked
256              //      conditionally, and run the fps locked renderer
257              event::Event::MainEventsCleared => {
258                  event_schedule.execute(&mut world, &mut resources);
259                  resources
260                      .get_mut::<Vec<OwnedEvent<OwnedEventExtension>>>()
261                      .unwrap()
262                      .clear();
263  
264                  imgui_prepare_schedule.execute(&mut world, &mut resources);
265  
266                  let (step_size, elapsed_time) = {
267                      let mut loop_state = resources.get_mut::<LoopState>().unwrap();
268                      (
269                          loop_state.step_size,
270                          loop_state.start_time.elapsed().as_secs_f32(),
271                      )
272                  };
273                  delta_time = elapsed_time - current_time;
274  
275                  {
276                      let mut loop_state = resources.get_mut::<LoopState>().unwrap();
277                      loop_state.delta_time = Duration::from_secs_f32(delta_time);
278                  }
279                  current_time = elapsed_time;
280                  if delta_time > 0.02 {
281                      delta_time = 0.02;
282                  }
283                  accumulator_time += delta_time;
284  
285                  while accumulator_time - step_size >= step_size {
286                      accumulator_time -= step_size;
287  
288                      // ==== DELTA TIME LOCKED ====
289                      update_schedule.execute(&mut world, &mut resources);
290                  }
291                  // ==== FPS LOCKED ====
292                  render_schedule.execute(&mut world, &mut resources);
293              }
294              // Resizing will queue a request_redraw
295              event::Event::WindowEvent {
296                  event: WindowEvent::Resized(size),
297                  ..
298              } => {
299                  let width = size.width;
300                  let height = size.height;
301  
302                  resources
303                      .get_mut::<render::state::RenderState>()
304                      .unwrap()
305                      .resize(width, height);
306              }
307              event::Event::DeviceEvent {
308                  event: winit_24::event::DeviceEvent::Key(keyboard_input),
309                  ..
310              } => {
311                  if keyboard_input.virtual_keycode.is_some() {
312                      match keyboard_input.virtual_keycode.unwrap() {
313                          VirtualKeyCode::Escape => {
314                              if keyboard_input.state == ElementState::Pressed {
315                                  *control_flow = ControlFlow::Exit;
316                              } else {
317                                  //d
318                              }
319                          }
320                          _ => (),
321                      }
322                  }
323              }
324              event::Event::WindowEvent {
325                  event: WindowEvent::CloseRequested,
326                  ..
327              } => *control_flow = ControlFlow::Exit,
328              event::Event::RedrawRequested(_) => {
329                  // Call the render system
330                  //                imgui_prepare_schedule.execute(&mut world, &mut resources);
331                  //                render_schedule.execute(&mut world, &mut resources);
332              }
333              _ => {}
334          }
335  
336          resources
337              .get_mut::<Vec<OwnedEvent<OwnedEventExtension>>>()
338              .unwrap()
339              .push(event.into());
340      });
341  }
342  
343  pub fn setup_gamepad(event_loop: &EventLoop<OwnedEventExtension>) {
344      let event_loop_proxy = event_loop.create_proxy();
345  
346      std::thread::spawn(move || {
347          let mut gilrs = Gilrs::new().unwrap();
348          // Iterate over all connected gamepads
349          let mut gamepad: Option<Gamepad> = None;
350          for (_id, gamepad_) in gilrs.gamepads() {
351              if gamepad_.name() == "PS4" {
352                  gamepad = Some(gamepad_);
353              }
354              // println!(
355              //     "{} is {:?} {:?}",
356              //     gamepad_.name(),
357              //     gamepad_.power_info(),
358              //     gamepad_.id()
359              // );
360          }
361          let mut active_gamepad = None;
362  
363          loop {
364              while let Some(GilEvent { id, event, time }) = gilrs.next_event() {
365                  //println!("{:?} New event from {}: {:?}", time, id, event);
366                  active_gamepad = Some(id);
367                  event_loop_proxy
368                      .send_event(OwnedEventExtension::GamepadEvent {
369                          gil_event: GilEvent { id, event, time },
370                      })
371                      .ok();
372              }
373  
374              // // You can also use cached gamepad state
375              // if let Some(gamepad) = active_gamepad.map(|id| gilrs.gamepad(id)) {
376              //     if gamepad.is_pressed(Button::South) {
377              //         println!("Button South is pressed (XBox - A, PS - X)");
378              //     }
379              // }
380  
381              std::thread::sleep(std::time::Duration::from_millis(50));
382          }
383      });
384  }
385