arcball 1.1.0

An implementation of the Shoemake Arcball camera
Documentation
#[macro_use]
extern crate glium;
extern crate arcball;
extern crate cgmath;

use arcball::ArcballCamera;
use cgmath::{Vector2, Vector3};
use glium::index::PrimitiveType;
use glium::{glutin, Surface};

#[derive(Copy, Clone)]
struct Vertex {
    pos: [f32; 3],
    color: [f32; 3],
}
implement_vertex!(Vertex, pos, color);

// NOTE: This is still provided as an example of how to hook up mouse events
// to the camera, however it should eventually be replaced by one using
// device events (I think) since window events don't seem to fire often

fn main() {
    let window = glutin::window::WindowBuilder::new().with_title("Arcball Camera Cube Example");
    let context = glutin::ContextBuilder::new()
        .with_vsync(true);
    let event_loop = glutin::event_loop::EventLoop::new();

    let display =
        glium::Display::new(window, context, &event_loop).expect("failed to create display");

    // Hard-coded cube triangle strip
    let vertex_buffer = glium::VertexBuffer::new(
        &display,
        &[
            Vertex {
                pos: [1.0, 1.0, -1.0],
                color: [1.0, 0.0, 0.0],
            },
            Vertex {
                pos: [-1.0, 1.0, -1.0],
                color: [1.0, 0.0, 0.0],
            },
            Vertex {
                pos: [1.0, 1.0, 1.0],
                color: [1.0, 0.0, 0.0],
            },
            Vertex {
                pos: [-1.0, 1.0, 1.0],
                color: [0.0, 1.0, 0.0],
            },
            Vertex {
                pos: [-1.0, -1.0, 1.0],
                color: [0.0, 1.0, 0.0],
            },
            Vertex {
                pos: [-1.0, 1.0, -1.0],
                color: [0.0, 1.0, 0.0],
            },
            Vertex {
                pos: [-1.0, -1.0, -1.0],
                color: [0.0, 0.0, 1.0],
            },
            Vertex {
                pos: [1.0, 1.0, -1.0],
                color: [0.0, 0.0, 1.0],
            },
            Vertex {
                pos: [1.0, -1.0, -1.0],
                color: [0.0, 0.0, 1.0],
            },
            Vertex {
                pos: [1.0, 1.0, 1.0],
                color: [1.0, 1.0, 0.0],
            },
            Vertex {
                pos: [1.0, -1.0, 1.0],
                color: [1.0, 1.0, 0.0],
            },
            Vertex {
                pos: [-1.0, -1.0, 1.0],
                color: [1.0, 1.0, 0.0],
            },
            Vertex {
                pos: [1.0, -1.0, -1.0],
                color: [1.0, 0.0, 1.0],
            },
            Vertex {
                pos: [-1.0, -1.0, -1.0],
                color: [1.0, 0.0, 1.0],
            },
        ],
    )
    .unwrap();
    let index_buffer = glium::index::NoIndices(PrimitiveType::TriangleStrip);

    let program = program!(&display,
        140 => {
            vertex: "
                #version 140

                uniform mat4 proj_view;

                in vec3 pos;
                in vec3 color;

                out vec3 vcolor;

                void main(void) {
                    gl_Position = proj_view * vec4(pos, 1.0);
                    vcolor = color;
                }
            ",
            fragment: "
                #version 140

                in vec3 vcolor;
                out vec4 color;

                void main(void) {
                    color = vec4(vcolor, 1.0);
                }
            "
        },
    )
    .unwrap();

    let display_dims = display.get_framebuffer_dimensions();
    let persp_proj = cgmath::perspective(
        cgmath::Deg(65.0),
        display_dims.0 as f32 / display_dims.1 as f32,
        1.0,
        200.0,
    );
    let mut arcball_camera = ArcballCamera::new(
        Vector3::new(0.0, 0.0, 0.0),
        1.0,
        [display_dims.0 as f32, display_dims.1 as f32],
    );

    // Track if left/right mouse is down
    let mut mouse_pressed = [false, false];
    let mut prev_mouse = None;

    event_loop.run(move |event, _, control_flow| {
        let mut should_quit = false;
        println!("running");
        match event {
            glutin::event::Event::WindowEvent { event, .. } => match event {
                glutin::event::WindowEvent::CloseRequested => should_quit = true,
                glutin::event::WindowEvent::KeyboardInput { input, .. } => match input.virtual_keycode {
                    Some(glutin::event::VirtualKeyCode::Escape) => should_quit = true,
                    _ => {}
                },
                glutin::event::WindowEvent::CursorMoved { position, .. } if prev_mouse.is_none() => {
                    prev_mouse = Some(position);
                    println!("init mouse = {:?}", prev_mouse);
                }
                glutin::event::WindowEvent::CursorMoved { position, .. } => {
                    let prev = prev_mouse.unwrap();
                    if mouse_pressed[0] {
                        arcball_camera.rotate(
                            Vector2::new(prev.x as f32, prev.y as f32),
                            Vector2::new(position.x as f32, position.y as f32),
                        );
                    } else if mouse_pressed[1] {
                        let mouse_delta = Vector2::new(
                            (position.x - prev.x) as f32,
                            (position.y - prev.y) as f32,
                        );
                        arcball_camera.pan(mouse_delta);
                    }
                    prev_mouse = Some(position);
                    println!("prev = {:?}", prev_mouse);
                }
                glutin::event::WindowEvent::MouseInput { state, button, .. } => {
                    if button == glutin::event::MouseButton::Left {
                        mouse_pressed[0] = state == glutin::event::ElementState::Pressed;
                    } else if button == glutin::event::MouseButton::Right {
                        mouse_pressed[1] = state == glutin::event::ElementState::Pressed;
                    }
                }
                glutin::event::WindowEvent::MouseWheel { delta, .. } => {
                    let y = match delta {
                        glutin::event::MouseScrollDelta::LineDelta(_, y) => y,
                        glutin::event::MouseScrollDelta::PixelDelta(p) => p.y as f32,
                    };
                    arcball_camera.zoom(y, 0.16);
                }
                _ => {}
            },
            _ => {}
        }
        *control_flow = if should_quit {
            glutin::event_loop::ControlFlow::Exit
        } else {
            glutin::event_loop::ControlFlow::Poll
        };

        let proj_view: [[f32; 4]; 4] = (persp_proj * arcball_camera.get_mat4()).into();
        let uniforms = uniform! {
            proj_view: proj_view,
        };
        let draw_params = glium::DrawParameters {
            depth: glium::Depth {
                test: glium::draw_parameters::DepthTest::IfLess,
                write: true,
                ..Default::default()
            },
            ..Default::default()
        };

        let mut target = display.draw();
        target.clear_color(0.1, 0.1, 0.1, 0.0);
        target.clear_depth(1.0);
        target
            .draw(
                &vertex_buffer,
                &index_buffer,
                &program,
                &uniforms,
                &draw_params,
            )
            .unwrap();
        target.finish().unwrap();
    });
}