#[cfg(feature = "default")]
#[cfg(not(feature = "3d-audio"))]
use audio::Audio;
#[cfg(feature = "3d-audio")]
use audio::{Ambisonic, AmbisonicBuilder};
use imgui::Ui;
use input::Input;
use renderer::{Draw, Renderer};
#[cfg(feature = "nphysics")]
use types::PhysicsType;
use types::{Camera, RenderItem, TextItem};
#[cfg(feature = "nphysics")]
use nalgebra::Translation3;
#[cfg(feature = "nphysics")]
use nalgebra::Vector3 as nVector3;
#[cfg(feature = "nphysics")]
use ncollide::shape::{Cuboid, ShapeHandle};
#[cfg(feature = "nphysics")]
use nphysics3d::object::{BodyHandle, BodyStatus, ColliderDesc, RigidBodyDesc};
#[cfg(feature = "nphysics")]
use nphysics3d::world::World;
use glium::glutin::event::{Event, StartCause};
use glium::glutin::event_loop::{ControlFlow, EventLoop};
use std::time::{Duration, Instant};
use rayon::prelude::*;
use rayon::slice::IterMut;
#[cfg(feature = "nphysics")]
const PHYSICS_DIVISOR: f32 = 2f32;
pub enum UpdateStatus {
Continue,
Finish,
}
#[cfg(feature = "nphysics")]
struct PhysicsHandle {
render_item: (usize, usize),
body_handle: BodyHandle,
}
pub struct Game<T: Default> {
pub input: Input,
pub renderer: Renderer,
#[cfg(feature = "nphysics")]
pub physics: World<f32>,
#[cfg(feature = "default")]
#[cfg(not(feature = "3d-audio"))]
pub audio: Audio,
#[cfg(feature = "3d-audio")]
pub audio: Ambisonic,
pub cams: Vec<Camera>,
render_items: Vec<RenderItem<T>>,
text_items: Vec<TextItem>,
#[cfg(feature = "nphysics")]
physics_items: Vec<PhysicsHandle>,
pub delta: f32,
}
impl<T: Default> Game<T> {
pub fn new() -> (Game<T>, EventLoop<()>) {
#[cfg(feature = "nphysics")]
let mut physics = World::new();
#[cfg(feature = "nphysics")]
physics.set_gravity(nVector3::new(0.0, -9.81, 0.0));
let cam = Camera {
pos: (0.0f32, 0.0, 0.0),
euler_rot: (0.0f32, 0.0, 0.0),
};
let event_loop = EventLoop::new();
let renderer = Renderer::new("caper window".to_string(), &event_loop);
#[cfg(feature = "default")]
#[cfg(not(feature = "3d-audio"))]
let audio = Audio::new();
#[cfg(feature = "3d-audio")]
let audio = AmbisonicBuilder::default().build();
(
Game {
input: Input::new(),
renderer,
#[cfg(feature = "nphysics")]
physics,
#[cfg(feature = "default")]
#[cfg(not(feature = "3d-audio"))]
audio: audio,
#[cfg(feature = "3d-audio")]
audio: audio,
cams: vec![cam],
render_items: Vec::new(),
text_items: Vec::new(),
#[cfg(feature = "nphysics")]
physics_items: Vec::new(),
delta: 0.016_666_667f32,
},
event_loop,
)
}
}
impl<T: Default> Default for Game<T> {
fn default() -> Self {
Self::new().0
}
}
pub trait RenderItems {
type T: Default;
fn render_items_len(&self) -> usize;
fn render_items_iter_mut(&mut self) -> IterMut<RenderItem<Self::T>>;
fn get_render_item(&mut self, index: usize) -> &mut RenderItem<Self::T>;
fn get_render_item_by_name(&mut self, name: &str) -> Option<&mut RenderItem<Self::T>>;
fn add_render_item(&mut self, render_item: RenderItem<Self::T>);
}
impl<T: Default> RenderItems for Game<T> {
type T = T;
fn render_items_len(&self) -> usize {
self.render_items.len()
}
fn render_items_iter_mut(&mut self) -> IterMut<RenderItem<T>> {
self.render_items.par_iter_mut()
}
fn get_render_item(&mut self, index: usize) -> &mut RenderItem<T> {
&mut self.render_items[index]
}
fn get_render_item_by_name(&mut self, name: &str) -> Option<&mut RenderItem<T>> {
self.render_items.iter_mut().find(|item| item.name == name)
}
fn add_render_item(&mut self, render_item: RenderItem<T>) {
self.render_items.push(render_item);
let i = self.render_items.len() - 1;
#[cfg(feature = "nphysics")]
self.add_physics(i);
}
}
#[cfg(feature = "nphysics")]
pub trait Physics {
fn add_physics(&mut self, i: usize);
fn update_physics(&mut self);
}
#[cfg(feature = "nphysics")]
impl<T: Default> Physics for Game<T> {
fn add_physics(&mut self, i: usize) {
match self.render_items[i].physics_type {
PhysicsType::Static => {
for j in 0..self.render_items[i].instance_transforms.len() {
let ri_trans = self.render_items[i].instance_transforms[j];
let geom = ShapeHandle::new(Cuboid::new(nVector3::new(
ri_trans.scale.0,
ri_trans.scale.1,
ri_trans.scale.2,
)));
let collider_desc = ColliderDesc::new(geom).density(1.0);
let mut rb_desc = RigidBodyDesc::new().collider(&collider_desc);
let pos = nVector3::new(
ri_trans.pos.0 * PHYSICS_DIVISOR,
ri_trans.pos.1 * PHYSICS_DIVISOR,
ri_trans.pos.2 * PHYSICS_DIVISOR,
);
let rb = rb_desc
.set_translation(pos)
.set_status(BodyStatus::Static)
.build(&mut self.physics);
let physics_handle = PhysicsHandle {
render_item: (i, j),
body_handle: rb.handle(),
};
self.physics_items.push(physics_handle);
}
}
PhysicsType::Dynamic => {
for j in 0..self.render_items[i].instance_transforms.len() {
let ri_trans = self.render_items[i].instance_transforms[j];
let geom = ShapeHandle::new(Cuboid::new(nVector3::new(
ri_trans.scale.0,
ri_trans.scale.1,
ri_trans.scale.2,
)));
let collider_desc = ColliderDesc::new(geom).density(1.0);
let mut rb_desc = RigidBodyDesc::new().collider(&collider_desc);
let pos = nVector3::new(
ri_trans.pos.0 * PHYSICS_DIVISOR,
ri_trans.pos.1 * PHYSICS_DIVISOR,
ri_trans.pos.2 * PHYSICS_DIVISOR,
);
let rb = rb_desc.set_translation(pos).build(&mut self.physics);
let physics_handle = PhysicsHandle {
render_item: (i, j),
body_handle: rb.handle(),
};
self.physics_items.push(physics_handle);
}
}
PhysicsType::None => {}
}
}
fn update_physics(&mut self) {
{
for ph in self.physics_items.iter() {
let (ri_i, ri_it_i) = ph.render_item;
if self.render_items.len() > ri_i
&& self.render_items[ri_i].instance_transforms.len() > ri_it_i
{
let rb = self.physics.rigid_body_mut(ph.body_handle).unwrap();
let ri_pos = self.render_items[ri_i].instance_transforms[ri_it_i].pos;
let mut rb_pos = rb.position().clone();
rb_pos.translation = Translation3::new(
ri_pos.0 * PHYSICS_DIVISOR,
ri_pos.1 * PHYSICS_DIVISOR,
ri_pos.2 * PHYSICS_DIVISOR,
);
rb.set_position(rb_pos);
}
}
}
{
self.physics.step();
for ph in self.physics_items.iter() {
let rb = self.physics.rigid_body_mut(ph.body_handle).unwrap();
let trans = rb.position().translation.vector;
let prot = rb.position().rotation;
let rot = prot.coords.data.as_slice();
let (ri_i, ri_it_i) = ph.render_item;
if self.render_items.len() > ri_i
&& self.render_items[ri_i].instance_transforms.len() > ri_it_i
{
self.render_items[ri_i].instance_transforms[ri_it_i].pos = (
trans.x / PHYSICS_DIVISOR,
trans.y / PHYSICS_DIVISOR,
trans.z / PHYSICS_DIVISOR,
);
self.render_items[ri_i].instance_transforms[ri_it_i].rot =
(rot[0], rot[1], rot[2], rot[3]);
}
}
}
}
}
pub trait TextItems {
fn text_items_len(&self) -> usize;
fn text_items_iter_mut(&mut self) -> IterMut<TextItem>;
fn get_text_item(&mut self, index: usize) -> &mut TextItem;
fn get_text_item_by_name(&mut self, name: String) -> Option<&mut TextItem>;
fn add_text_item(&mut self, text_item: TextItem);
}
impl<T: Default> TextItems for Game<T> {
fn text_items_len(&self) -> usize {
self.text_items.len()
}
fn text_items_iter_mut(&mut self) -> IterMut<TextItem> {
self.text_items.par_iter_mut()
}
fn get_text_item(&mut self, index: usize) -> &mut TextItem {
&mut self.text_items[index]
}
fn get_text_item_by_name(&mut self, name: String) -> Option<&mut TextItem> {
for i in 0..self.text_items.len() {
if self.text_items[i].name == name {
return Some(&mut self.text_items[i]);
}
}
None
}
fn add_text_item(&mut self, text_item: TextItem) {
self.text_items.push(text_item);
}
}
pub trait Update {
type T;
fn update<F: FnMut(&Ui), U: FnMut(&mut Game<Self::T>) -> UpdateStatus>(
&mut self,
render_imgui: F,
update: U,
events: &Vec<Event<()>>,
) -> UpdateStatus;
fn update_inputs(&mut self, events: &Vec<Event<()>>);
}
impl<T: Default> Update for Game<T> {
type T = T;
fn update<F: FnMut(&Ui), U: FnMut(&mut Game<T>) -> UpdateStatus>(
&mut self,
mut render_imgui: F,
mut update: U,
events: &Vec<Event<()>>,
) -> UpdateStatus {
let frame_start = Instant::now();
self.update_inputs(events);
#[cfg(feature = "nphysics")]
self.update_physics();
let status = update(self);
{
self.renderer.draw(
&mut self.cams,
&mut self.render_items,
&mut self.text_items,
&mut render_imgui,
);
}
self.delta = 0.000_000_001f32 * frame_start.elapsed().subsec_nanos() as f32;
status
}
fn update_inputs(&mut self, events: &Vec<Event<()>>) {
{
let gl_window = self.renderer.display.gl_window();
let window = gl_window.window();
self.input.update_inputs(window, events);
}
{
self.renderer.update_imgui_input(&self.input);
}
}
}
pub fn start_loop<F>(event_loop: EventLoop<()>, mut callback: F) -> !
where
F: 'static + FnMut(&Vec<Event<()>>) -> UpdateStatus,
{
let mut events_buffer = Vec::new();
let mut next_frame_time = Instant::now();
event_loop.run(move |event, _, control_flow| {
let run_callback = match event.to_static() {
Some(Event::NewEvents(cause)) => match cause {
StartCause::ResumeTimeReached { .. } | StartCause::Init => true,
_ => false,
},
Some(event) => {
events_buffer.push(event);
false
}
None => {
false
}
};
let action = if run_callback {
let action = callback(&events_buffer);
next_frame_time = Instant::now() + Duration::from_nanos(16666667);
events_buffer.clear();
action
} else {
UpdateStatus::Continue
};
match action {
UpdateStatus::Continue => {
*control_flow = ControlFlow::WaitUntil(next_frame_time);
}
UpdateStatus::Finish => *control_flow = ControlFlow::Exit,
}
})
}