/******************************************************************/
/* This file is part of the homework assignments for CSCI-427/527 */
/* at The College of William & Mary and authored by Pieter Peers. */
/* No part of this file, whether altered or in original form, can */
/* be distributed or used outside the context of CSCI-427/527 */
/* without consent of either the College of William & Mary or */
/* Pieter Peers. */
/******************************************************************/
#include <cmath>
#include "constants.h"
#include "camera.h"
/////////////////
// Constructor //
/////////////////
camera::camera(void)
{
_eye = vec3d();
_view = vec3d(0.0f, 0.0f, -1.0f);
_up = vec3d(0.0f, 1.0f, 0.0f);
_fov = 60.0f;
_width = _height = 256;
}
camera::camera(const vec3d& eye, const vec3d& viewDirection, const vec3d& up, float fov, size_t xres, size_t yres)
{
_eye = eye;
_view = normalize(viewDirection);
_up = normalize(up);
_fov = fov;
_width = xres;
_height = yres;
// fix up if needed
vec3d right = _view.cross(up).normalize();
_up = right.cross(_view).normalize();
}
camera::camera(const camera& cam)
{
_eye = cam._eye;
_view = cam._view;
_up = cam._up;
_fov = cam._fov;
_width = cam._width;
_height = cam._height;
}
///////////////
// Operators //
///////////////
camera& camera::operator=(const camera& cam)
{
_assign(cam);
return *this;
}
ray camera::operator()(float x, float y) const
{
vec3d right = _view.cross(_up).normalize();
// aspect ratio
float aspect = (float)(_height) / (float)(_width);
float tanFov = tan(_fov / 180.0f * M_PI);
// compute view plane center, and U and V unnormalized axes.
vec3d center = _eye + _view;
vec3d U = 2.0f * tanFov * right;
vec3d V = -2.0f * tanFov * aspect * _up; // y runs from top to bottom (opposite direction of up)
// get point on view plane
vec3d p = center + (x / (float)(_width) - 0.5f) * U + (y / (float)(_height) - 0.5f) * V;
// Done.
return ray(_eye, p - _eye);
}
//////////////
// Mutators //
//////////////
void camera::frameBoundingBox(const boundingBox& bb)
{
// determine the best eye location, given the other parameters and a bounding box such that the bounding box maximally occupies the view
vec3d right = _view.cross(_up).normalize();
_eye = 0.5f * (bb.corner(true,true,true) + bb.corner(false,false,false));
// => find max projection in up and right direction.
float maxRight=-LARGE, maxUp=-LARGE, maxDepth=-LARGE;
for(unsigned int i=0; i < 8; i++)
{
vec3d c = bb.corner( (i&1)==1, (i&2)==2, (i&4)==4 ) - _eye;
float r = fabs(c.dot(right));
float u = fabs(c.dot(_up));
float d = fabs(c.dot(_view));
maxRight = std::max(maxRight, r);
maxUp = std::max(maxUp, u);
maxDepth = std::max(maxDepth, d);
}
// => compute optimal distance for up and right
float aspect = (float)(_height) / (float)(_width);
float tanFov = tan(_fov / 180.0f * M_PI);
float optDistUp = fabs(maxUp / tanFov);
float optDistRight = fabs(maxRight / (tanFov * aspect));
// => move _eye back based on (max) optimal distance
_eye -= _view * (std::max(optDistUp, optDistRight) + maxDepth);
}
/////////////////////
// Private Methods //
/////////////////////
void camera::_swap(camera& cam)
{
// sanity check
if(&cam == this) return;
// swap
swap(_eye, cam._eye);
swap(_view, cam._view);
swap(_up, cam._up);
std::swap(_fov, cam._fov);
std::swap(_width, cam._width);
std::swap(_height, cam._height);
}
void camera::_assign(const camera& cam)
{
// sanity check
if(&cam == this) return;
// copy
_eye = cam._eye;
_view = cam._view;
_up = cam._up;
_fov = cam._fov;
_width = cam._width;
_height = cam._height;
}