bismuth 0.0.4

A 3D game world represented as cubes in an oct-tree that can be manipulated in real time.
Documentation
extern crate bismuth;
extern crate glutin;
extern crate nalgebra;
extern crate plexus;

use bismuth::cube::{Cursor, Geometry, LogWidth, Spatial, Tree};
use bismuth::event::{ElementState, Event, MouseButton, React};
use bismuth::framework::{self, Activity, Context, Harness, RenderContextView, RenderResult,
                         Transition, UpdateContextView, UpdateResult, WindowView};
use bismuth::input::{InputState, InputTransition, Mouse, MousePosition, Snapshot};
use bismuth::math::{FMatrix4, FPoint3, FScalar, IntoSpace, UPoint3, UVector3};
use bismuth::render::{Camera, Index, MetaRenderer, Projection, ToMeshBuffer, Transform, Vertex};
use glutin::WindowBuilder;
use plexus::buffer::MeshBuffer;
use std::marker::PhantomData;

struct State {
    pub mouse: Mouse,
}

impl State {
    pub fn new() -> Self {
        State {
            mouse: Mouse::new(),
        }
    }
}

impl framework::State for State {}

impl React for State {
    fn react(&mut self, event: &Event) {
        self.mouse.react(event);
    }
}

struct MainActivity<R>
where
    R: MetaRenderer,
{
    tree: Tree,
    mesh: MeshBuffer<Index, Vertex>,
    camera: Camera,
    phantom: PhantomData<R>,
}

impl<R> MainActivity<R>
where
    R: MetaRenderer,
{
    pub fn new(context: &mut Context<State, R>) -> Self {
        let tree = new_tree(LogWidth::new(8));
        let mesh = tree.as_cube().to_mesh_buffer();
        let camera = new_camera(&context.renderer.window, &tree);
        MainActivity {
            tree: tree,
            mesh: mesh,
            camera: camera,
            phantom: PhantomData,
        }
    }
}

impl<R> Activity<State, R> for MainActivity<R>
where
    R: MetaRenderer,
{
    fn update(&mut self, context: &mut UpdateContextView<State = State>) -> UpdateResult<State, R> {
        let mut dirty = false;
        if let Some(ElementState::Pressed) = context.state().mouse.transition(MouseButton::Left) {
            let ray = self.camera.cast_ray(
                context.window(),
                &context.state().mouse.state(MousePosition),
            );
            let mut cube = self.tree.as_cube_mut();
            if let Some((_, mut cube)) = cube.at_ray_mut(&ray, LogWidth::min_value()) {
                if let Some(leaf) = cube.as_leaf_mut() {
                    leaf.geometry = Geometry::empty();
                    dirty = true;
                }
            }
        }
        if dirty {
            self.mesh = self.tree.as_cube().to_mesh_buffer();
        }
        context.state_mut().mouse.snapshot();
        Ok(Transition::None)
    }

    fn render(&mut self, context: &mut RenderContextView<R, State = State>) -> RenderResult {
        let renderer = context.renderer_mut();
        renderer.clear();
        renderer
            .set_transform(&Transform::new(
                &self.camera.transform(),
                &FMatrix4::identity(),
            ))
            .unwrap();
        renderer.draw_mesh_buffer(&self.mesh);
        renderer.flush().unwrap();
        Ok(())
    }
}

impl<R> React for MainActivity<R>
where
    R: MetaRenderer,
{
    fn react(&mut self, event: &Event) {
        self.camera.react(event);
    }
}

fn new_tree(width: LogWidth) -> Tree {
    let cursor = Cursor::at_point_with_span(&UPoint3::origin(), width - 3, &UVector3::new(7, 1, 7));
    let mut tree = Tree::new(width);
    tree.as_cube_mut().subdivide_to_cursor(&cursor);
    tree
}

fn new_camera<C>(window: &WindowView, cube: &C) -> Camera
where
    C: Spatial,
{
    let midpoint: FPoint3 = cube.partition().midpoint().into_space();
    let projection = {
        let mut projection = Projection::default();
        projection.far = cube.partition().width().exp() as FScalar * 2.0;
        projection
    };
    let mut camera = Camera::new(window, &projection);
    camera.look_at(
        &FPoint3::new(midpoint.x * 0.25, -midpoint.y, midpoint.z * 3.0),
        &midpoint,
    );
    camera
}

fn main() {
    let mut harness = Harness::from_glutin_window(
        State::new(),
        WindowBuilder::new()
            .with_title("Bismuth")
            .with_dimensions(1024, 576)
            .with_vsync()
            .build()
            .unwrap(),
    );
    harness.start(|context| Box::new(MainActivity::new(context)));
}