onyx 0.2.0

A simple engine for simple minds
Documentation
#[macro_use]
extern crate glium;
extern crate rusttype;
#[macro_use]
extern crate serde_derive;
extern crate serde;

pub mod ui;
pub mod math;
pub mod text;
pub mod mesh;
pub mod context;
pub mod color;
mod transform;
mod frame;

use std::rc::Rc;
use std::collections::HashSet;
use std::time;
use glium::glutin::{ElementState, Event as GliumEvent};

pub use glium::glutin::VirtualKeyCode as Key;

pub use context::{Shader, Texture};
pub use transform::Transform;
pub use math::{Vec2, Vec3};

pub struct State<Action> {
    ui: ui::Ui<Action>,
    context: Rc<context::Context>,
    transform_system: transform::TransformSystem,
    render_system: RenderSystem,
    pressed: HashSet<Key>,
}

impl<Action> State<Action> {
    pub fn ui(&mut self) -> &mut ui::Ui<Action> {
        &mut self.ui
    }

    pub fn context(&self) -> Rc<context::Context> {
        self.context.clone()
    }

    pub fn graph(&mut self) -> &mut transform::TransformSystem {
        &mut self.transform_system
    }

    pub fn renderer(&mut self) -> &mut RenderSystem {
        &mut self.render_system
    }

    pub fn is_pressed(&self, key: Key) -> bool {
        self.pressed.contains(&key)
    }

    pub fn none(&self) -> Transition {
        Transition(TransitionKind::None)
    }

    pub fn quit(&self) -> Transition {
        Transition(TransitionKind::Quit)
    }

    pub fn pop(&self) -> Transition {
        Transition(TransitionKind::Pop)
    }

    pub fn push<S: Scene + 'static>(&self) -> Transition {
        Transition(TransitionKind::Push(Box::new(GenericScene::<S>::new(self.context.clone()))))
    }

    pub fn switch<S: Scene + 'static>(&self) -> Transition {
        Transition(TransitionKind::Switch(Box::new(GenericScene::<S>::new(self.context.clone()))))
    }
}

pub trait Scene {
    type Action;
    fn new(state: &mut State<Self::Action>) -> Self where Self: Sized;

    fn event(&mut self, _event: Event) -> Option<Self::Action> { None }

    fn action(&mut self, _action: Self::Action, _state: &mut State<Self::Action>) -> Transition {
        Transition(TransitionKind::None)
    }

    fn update(&mut self, _state: &mut State<Self::Action>) -> Transition {
        Transition(TransitionKind::None)
    }
}

struct GenericScene<S: Scene> {
    scene: S,
    state: State<S::Action>,
}

impl<S: Scene> GenericScene<S> {
    fn new(context: Rc<context::Context>) -> Self {
        let ui = ui::Ui::new(context.clone());
        let mut state = State { 
            ui,
            context: context.clone(),
            transform_system: transform::TransformSystem::new(),
            render_system: RenderSystem::new(context.clone()),
            pressed: HashSet::new(),
        };
        let scene = S::new(&mut state);
        Self {
            scene: scene,
            state: state,
        }
    }
}

trait ErasedScene {
    fn event(&mut self, event: Event) -> Transition;
    fn update(&mut self) -> Transition;
    fn draw(&self, frame: &mut frame::Frame);
}

impl<S: Scene> ErasedScene for GenericScene<S> {
    fn event(&mut self, event: Event) -> Transition {
        match self.state.ui.event(event) {
            Ok(action) => self.scene.action(action, &mut self.state),
            Err(event) => {
                match event {
                    Event::KeyPressed(key) => {
                        if self.state.pressed.contains(&key) {
                            return Transition(TransitionKind::None);
                        } else {
                            self.state.pressed.insert(key);
                        }
                    },
                    Event::KeyReleased(key) => {
                        self.state.pressed.remove(&key);
                    },
                    _ => {},
                }
                if let Some(action) = self.scene.event(event) {
                    self.scene.action(action, &mut self.state)
                } else {
                    Transition(TransitionKind::None)
                }
            }
        }
    }

    fn update(&mut self) -> Transition {
        self.state.transform_system.update();
        self.state.ui.update();
        self.scene.update(&mut self.state)
    }

    fn draw(&self, frame: &mut frame::Frame) {
        self.state.render_system.draw(&self.state.transform_system, frame);
        self.state.ui.draw(frame);
    }
}

#[derive(Copy, Clone, Debug)]
pub enum Event {
    None,
    KeyPressed(Key),
    KeyReleased(Key),
    MouseMoved(math::Vec2),
    Resized(math::Vec2),
    MousePressed,
    MouseReleased,
}

pub fn run<S: Scene + 'static>(title: &str, size: (u32, u32)) {
    let context = Rc::new(context::Context::new(title, size));
    let mut scenes: Vec<Box<ErasedScene>> = vec![Box::new(GenericScene::<S>::new(context.clone()))];
    let mut prev_time = time::Instant::now();
    'run: loop {
        for event in context.display.poll_events() {
            let event = match event {
                GliumEvent::KeyboardInput(ElementState::Pressed, _, Some(key)) => Event::KeyPressed(key),
                GliumEvent::KeyboardInput(ElementState::Released, _, Some(key)) => Event::KeyReleased(key),
                GliumEvent::MouseMoved(x, y) => {
                    let mouse = math::Vec2::new(x as f32, y as f32);
                    context.mouse.set(mouse);
                    Event::MouseMoved(mouse)
                },
                GliumEvent::Resized(w, h) => {
                    let size = math::Vec2::new(w as f32, h as f32);
                    context.size.set(size);
                    Event::Resized(size)
                }
                GliumEvent::MouseInput(ElementState::Pressed, ..) => Event::MousePressed,
                GliumEvent::MouseInput(ElementState::Released, ..) => Event::MouseReleased,
                GliumEvent::Closed => break 'run,
                _ => Event::None,
            };
            let transition = {
                let scene = scenes.last_mut().unwrap();
                scene.event(event).0
            };
            match transition {
                TransitionKind::None => {},
                TransitionKind::Quit => break 'run,
                TransitionKind::Pop => {
                    scenes.pop();
                },
                TransitionKind::Push(scene) => {
                    scenes.push(scene);
                }
                TransitionKind::Switch(scene) => {
                    scenes.pop();
                    scenes.push(scene);
                },
            }
        }
        let time = time::Instant::now();
        context.dt.set(time.duration_since(prev_time));
        prev_time = time;
        let scene = scenes.last_mut().unwrap();          
        scene.update();      
        let mut frame = frame::Frame::new(context.clone());
        scene.draw(&mut frame);
        frame.finish();
    }
}

struct Item {
    transform: Transform,
}

pub struct RenderSystem {
    context: Rc<context::Context>,
    items: Vec<Item>,
}

impl RenderSystem {
    fn new(context: Rc<context::Context>) -> Self {
        Self { context, items: Vec::new() }
    }

    fn draw(&self, transform_system: &transform::TransformSystem, frame: &mut frame::Frame) {
        for item in self.items.iter() {
            frame.draw_mesh(&self.context.quad, transform_system.global(item.transform), &self.context.default_shader, &self.context.white);
        }
    }

    pub fn add(&mut self, transform: Transform) {
        self.items.push(Item { transform });
    }
}

enum TransitionKind {
    None,
    Quit,
    Pop,
    Push(Box<ErasedScene>),
    Switch(Box<ErasedScene>),
}

pub struct Transition(TransitionKind);