Skip to content

Commit

Permalink
Merge pull request #22 from bwasty/cameras
Browse files Browse the repository at this point in the history
Add orthographic and infinite perspective cameras
  • Loading branch information
bwasty authored Feb 22, 2018
2 parents 1d35137 + 7cba4e7 commit 7eade6d
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 44 deletions.
14 changes: 7 additions & 7 deletions src/controls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ impl OrbitControls {
self.pan_up(distance);
} else {
// TODO!: orthographic camera pan
unimplemented!("orthographic camera pan")
warn!("unimplemented: orthographic camera pan")
}
}

Expand All @@ -225,14 +225,14 @@ impl OrbitControls {
// Processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
pub fn process_mouse_scroll(&mut self, mut yoffset: f32) {
yoffset *= ZOOM_SENSITIVITY;
if self.camera.fovy >= MIN_ZOOM && self.camera.fovy <= MAZ_ZOOM {
self.camera.fovy -= yoffset;
if self.camera.fovy.0 >= MIN_ZOOM && self.camera.fovy.0 <= MAZ_ZOOM {
self.camera.fovy.0 -= yoffset;
}
if self.camera.fovy <= MIN_ZOOM {
self.camera.fovy = MIN_ZOOM;
if self.camera.fovy.0 <= MIN_ZOOM {
self.camera.fovy.0 = MIN_ZOOM;
}
if self.camera.fovy >= MAZ_ZOOM {
self.camera.fovy = MAZ_ZOOM;
if self.camera.fovy.0 >= MAZ_ZOOM {
self.camera.fovy.0 = MAZ_ZOOM;
}
self.camera.update_projection_matrix();
}
Expand Down
4 changes: 2 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// #![feature(test)]
#[macro_use] extern crate clap;
extern crate cgmath;
// use cgmath::prelude::*;
use cgmath::Deg;

extern crate collision;

Expand Down Expand Up @@ -121,7 +121,7 @@ pub fn main() {
index: args.value_of("CAM-INDEX").map(|n| n.parse().unwrap()).unwrap(),
position: args.value_of("CAM-POS").map(|v| parse_vec3(v).unwrap()),
target: args.value_of("CAM-TARGET").map(|v| parse_vec3(v).unwrap()),
fovy: args.value_of("CAM-FOVY").map(|n| n.parse().unwrap()).unwrap(),
fovy: args.value_of("CAM-FOVY").map(|n| Deg(n.parse().unwrap())).unwrap(),
};

let log_level = match args.occurrences_of("verbose") {
Expand Down
57 changes: 48 additions & 9 deletions src/render/camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ use ::controls::{ZOOM};

#[derive(Clone)]
pub struct Camera {
pub index: usize, // gltf index
pub name: Option<String>,

pub projection_matrix: Matrix4,

pub znear: f32,
pub zfar: Option<f32>,

// perspective camera
// TODO!: setters that update...
pub fovy: f32, // degrees
pub fovy: Deg<f32>,
aspect_ratio: f32,

// orthographic camera
Expand All @@ -26,10 +29,13 @@ pub struct Camera {
impl Default for Camera {
fn default() -> Self {
Camera {
index: 0,
name: None,

znear: 0.01,
zfar: Some(1000.0),

fovy: ZOOM,
fovy: Deg(ZOOM),
aspect_ratio: 1.0,

xmag: None,
Expand All @@ -43,10 +49,12 @@ impl Default for Camera {
impl Camera {
pub fn from_gltf(g_camera: &gltf::Camera) -> Self {
let mut camera = Camera {
index: g_camera.index(),
name: g_camera.name().map(|n| n.to_owned()),
projection_matrix: Matrix4::zero(),
znear: 0.0,
zfar: None,
fovy: 0.0,
fovy: Deg(0.0),
aspect_ratio: 1.0,
xmag: None,
ymag: None,
Expand All @@ -55,7 +63,7 @@ impl Camera {
Projection::Perspective(persp) => {
// TODO!!: ignoring aspect ratio for now as it would require window resizing...
let _aspect = persp.aspect_ratio();
camera.fovy = Deg::from(Rad(persp.yfov())).0;
camera.fovy = Deg::from(Rad(persp.yfov()));
camera.znear = persp.znear();
camera.zfar = persp.zfar();
},
Expand All @@ -80,20 +88,51 @@ impl Camera {
}

pub fn update_projection_matrix(&mut self) {
if let Some(_xmag) = self.xmag {
unimplemented!("orthographic camera") // TODO!!!: ortho camera
if let Some(xmag) = self.xmag {
// from https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#orthographic-projection
let r = xmag;
let t = self.ymag.unwrap();
let f = self.zfar.unwrap();
let n = self.znear;
self.projection_matrix = Matrix4::new(
1.0/r, 0.0, 0.0, 0.0, // NOTE: first column!
0.0, 1.0/t, 0.0, 0.0, // 2nd
0.0, 0.0, 2.0/(n-f), 0.0, // 3rd
0.0, 0.0, (f+n)/(n-f), 1.0 // 4th
);
} else if let Some(zfar) = self.zfar {
self.projection_matrix = perspective(
Deg(self.fovy),
self.fovy,
self.aspect_ratio,
self.znear, zfar)
} else {
// TODO!!: inifinite perspective (missing sample models)
unimplemented!("infinite perspective")
// from https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#infinite-perspective-projection
let a = self.aspect_ratio;
let y = Rad::from(self.fovy).0;
let n = self.znear;

self.projection_matrix = Matrix4::new(
1.0/(a*(0.5*y).tan()), 0.0, 0.0, 0.0, // NOTE: first column!
0.0, 1.0/(0.5*y).tan(), 0.0, 0.0,
0.0, 0.0, -1.0, -1.0,
0.0, 0.0, -2.0*n, 0.0
);
}
}

pub fn is_perspective(&self) -> bool {
self.xmag.is_none()
}

pub fn description(&self) -> String {
let type_ = if !self.is_perspective() {
"ortho"
} else if self.zfar.is_none() {
"infinite perspective"
} else {
"perspective"
};

format!("{} ({:?}, {})", self.index, self.name, type_)
}
}
1 change: 0 additions & 1 deletion src/render/scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ impl Scene {
let node = root.unsafe_get_node_mut(*node_id);
node.update_transform(root, &root_transform);
node.update_bounds(root);
// TODO!: visualize final bounds
scene.bounds = scene.bounds.union(&node.bounds);
}

Expand Down
1 change: 0 additions & 1 deletion src/shader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,6 @@ bitflags! {
const HAS_NORMALS = 1;
const HAS_TANGENTS = 1 << 1;
const HAS_UV = 1 << 2;
// TODO!: the shader doesn't have colors yet
const HAS_COLORS = 1 << 3;

// fragment shader only
Expand Down
53 changes: 29 additions & 24 deletions src/viewer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::path::Path;
use std::process;
use std::time::Instant;

use cgmath::{ Point3 };
use cgmath::{ Deg, Point3 };
use collision::Aabb;
use gl;
use glutin;
Expand Down Expand Up @@ -44,7 +44,7 @@ pub struct CameraOptions {
pub index: i32,
pub position: Option<Vector3>,
pub target: Option<Vector3>,
pub fovy: f32,
pub fovy: Deg<f32>,
}

pub struct GltfViewer {
Expand Down Expand Up @@ -169,22 +169,24 @@ impl GltfViewer {
};
unsafe { gl_check_error!(); };

if !viewer.root.camera_nodes.is_empty() && !camera_options.index == -1 {
if camera_options.index >= viewer.root.camera_nodes.len() as i32 {
error!("No camera with index {} found in glTF file (max: {})",
camera_options.index, viewer.root.camera_nodes.len() - 1);
process::exit(2)
}
if camera_options.index != 0 && camera_options.index >= viewer.root.camera_nodes.len() as i32 {
error!("No camera with index {} found in glTF file (max: {})",
camera_options.index, viewer.root.camera_nodes.len() as i32 - 1);
process::exit(2)
}
if !viewer.root.camera_nodes.is_empty() && camera_options.index != -1 {
let cam_node = &viewer.root.get_camera_node(camera_options.index as usize);
viewer.orbit_controls.set_camera(
cam_node.camera.as_ref().unwrap(),
&cam_node.final_transform);
let cam_node_info = format!("{} ({:?})", cam_node.index, cam_node.name);
let cam = cam_node.camera.as_ref().unwrap();
info!("Using camera {} on node {}", cam.description(), cam_node_info);
viewer.orbit_controls.set_camera(cam, &cam_node.final_transform);

if camera_options.position.is_some() || camera_options.target.is_some() {
warn!("Ignoring --cam-pos / --cam-target since --cam-index is given.")
}
} else {
viewer.set_camera_from_bounds();
info!("Determining camera view from bounding box");
viewer.set_camera_from_bounds(false);

if let Some(p) = camera_options.position {
viewer.orbit_controls.position = Point3::from_vec(p)
Expand Down Expand Up @@ -239,25 +241,28 @@ impl GltfViewer {
}

/// determine "nice" camera perspective from bounding box. Inspired by donmccurdy/three-gltf-viewer
fn set_camera_from_bounds(&mut self) {
fn set_camera_from_bounds(&mut self, straight: bool) {
let bounds = &self.scene.bounds;
let size = (bounds.max - bounds.min).magnitude();
let center = bounds.center();

let _max_distance = size * 10.0;
// TODO: x,y addition optional, z optionally minus instead
let cam_pos = Point3::new(
center.x + size / 2.0,
center.y + size / 5.0,
center.z + size / 2.0,
);
let _near = size / 100.0;
let _far = size * 100.0;
// TODO: x,y addition optional
let cam_pos = if straight {
Point3::new(
center.x,
center.y,
center.z + size * 0.75,
)
} else {
Point3::new(
center.x + size / 2.0,
center.y + size / 5.0,
center.z + size / 2.0,
)
};

self.orbit_controls.position = cam_pos;
self.orbit_controls.target = center;

// TODO!: set near, far, max_distance, obj_pos_modifier...
}

pub fn start_render_loop(&mut self) {
Expand Down

0 comments on commit 7eade6d

Please sign in to comment.