/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Copyright (c) 2004-2013 HUBzero Foundation, LLC * * Authors: * Wei Qiao */ #include #include #include #include #include #include #include #include #include #include #include #include "nanovis.h" #include "Camera.h" #include "Trace.h" using namespace nv; using namespace vrmath; static inline double deg2rad(double deg) { return ((deg * M_PI) / 180.); } static inline double rad2deg(double rad) { return ((rad * 180.) / M_PI); } Camera::Camera(int x, int y, int width, int height) : _updir(Y_POS), _position(0, 0, 2.5), _fov(30.0), _near(0.1), _far(50.0) { _viewport[0] = x; _viewport[1] = y; _viewport[2] = width; _viewport[3] = height; } const Matrix4x4d& Camera::getUpDirMatrix() const { return _updirMatrix; } void Camera::setUpDirMatrix(AxisDirection dir) { _updir = dir; switch (_updir) { case X_POS: { _updirMatrix.makeRotation(0, 0, 1, deg2rad(90)); Matrix4x4d tmp; tmp.makeRotation(1, 0, 0, deg2rad(90)); _updirMatrix.multiply(tmp); } break; case Y_POS: _updirMatrix.makeIdentity(); break; case Z_POS: { _updirMatrix.makeRotation(1, 0, 0, deg2rad(-90)); Matrix4x4d tmp; tmp.makeRotation(0, 0, 1, deg2rad(-90)); _updirMatrix.multiply(tmp); } break; case X_NEG: _updirMatrix.makeRotation(0, 0, 1, deg2rad(-90)); break; case Y_NEG: { _updirMatrix.makeRotation(0, 0, 1, deg2rad(180)); Matrix4x4d tmp; tmp.makeRotation(0, 1, 0, deg2rad(-90)); _updirMatrix.multiply(tmp); } break; case Z_NEG: _updirMatrix.makeRotation(1, 0, 0, deg2rad(90)); break; } } /** * \brief Reset zoom to include extents */ void Camera::reset(const Vector3f& bboxMin, const Vector3f& bboxMax, bool resetOrientation) { TRACE("Enter"); if (resetOrientation) { _rotationMatrix.makeIdentity(); } Vector3f center(bboxMin + bboxMax); center.scale(0.5); Matrix4x4d mat, upMat; upMat = getUpDirMatrix(); mat.makeTranslation(-center); mat.multiply(_rotationMatrix, mat); mat.multiply(upMat); Vector3f emin(FLT_MAX, FLT_MAX, FLT_MAX), emax(-FLT_MAX, -FLT_MAX, -FLT_MAX); // Transform bounds by camera matrix Vector4f bboxEye[8]; bboxEye[0] = Vector4f(bboxMin.x, bboxMin.y, bboxMin.z, 1); bboxEye[1] = Vector4f(bboxMax.x, bboxMin.y, bboxMin.z, 1); bboxEye[2] = Vector4f(bboxMin.x, bboxMax.y, bboxMin.z, 1); bboxEye[3] = Vector4f(bboxMin.x, bboxMin.y, bboxMax.z, 1); bboxEye[4] = Vector4f(bboxMax.x, bboxMax.y, bboxMin.z, 1); bboxEye[5] = Vector4f(bboxMax.x, bboxMin.y, bboxMax.z, 1); bboxEye[6] = Vector4f(bboxMin.x, bboxMax.y, bboxMax.z, 1); bboxEye[7] = Vector4f(bboxMax.x, bboxMax.y, bboxMax.z, 1); for (int i = 0; i < 8; i++) { Vector4f eyeVert = mat.transform(bboxEye[i]); if (eyeVert.x < emin.x) emin.x = eyeVert.x; if (eyeVert.x > emax.x) emax.x = eyeVert.x; if (eyeVert.y < emin.y) emin.y = eyeVert.y; if (eyeVert.y > emax.y) emax.y = eyeVert.y; if (eyeVert.z < emin.z) emin.z = eyeVert.z; if (eyeVert.z > emax.z) emax.z = eyeVert.z; } TRACE("Eye bounds: (%g,%g,%g) - (%g,%g,%g)", emin.x, emin.y, emin.z, emax.x, emax.y, emax.z); double bwidth = emax.x - emin.x; double bheight = emax.y - emin.y; double bdepth = emax.z - emin.z; TRACE("bwidth: %g, bheight: %g, bdepth: %g", bwidth, bheight, bdepth); double angle = deg2rad(_fov); double distance; // Deal with vertical aspect window double winAspect = (double)_viewport[2]/(double)_viewport[3]; double sceneAspect = 1.0;; if (bheight > 0.0) sceneAspect = bwidth / bheight; if (sceneAspect >= winAspect) { angle = 2.0 * atan(tan(angle*0.5)*winAspect); _near = bwidth / (2.0 * tan(angle*0.5)); } else { _near = bheight / (2.0 * tan(angle*0.5)); } distance = _near + bdepth * 0.5; _far = _near + bdepth; _position.set(center.x, center.y, center.z + distance); TRACE("win aspect: %g scene aspect: %g", winAspect, sceneAspect); TRACE("c: %g,%g,%g, d: %g", center.x, center.y, center.z, distance); TRACE("pos: %g, %g, %g", _position.x, _position.y, _position.z); TRACE("near: %g, far: %g", _near, _far); initialize(); resetClippingRange(bboxMin, bboxMax); } /** * \brief Reset near and far planes based on given world space * bounding box */ void Camera::resetClippingRange(const Vector3f& bboxMin, const Vector3f& bboxMax) { Vector3f emin(bboxMin.x, bboxMin.y, bboxMin.z), emax(bboxMax.x, bboxMax.y, bboxMax.z); Vector3f center(emin + emax); center.scale(0.5); // Compute the radius of the enclosing sphere, // which is half the bounding box diagonal Vector3f diagonal(emax - emin); double radius = diagonal.length() * 0.5; // If we have just a single point, pick a radius of 1.0 radius = (radius == 0) ? 1.0 : radius; TRACE("c: %g,%g,%g, r: %g cam z: %g", center.x, center.y, center.z, radius, _position.z); _near = _position.z - radius; _far = _position.z + radius; if (_near < 0.0) { _near = 0.001; } if (_far < 0.0) { _far = 1.0; } TRACE("Resetting camera clipping range to: near: %g, far: %g", _near, _far); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(_fov, (GLdouble)_viewport[2]/(GLdouble)_viewport[3], _near, _far); glMatrixMode(GL_MODELVIEW); print(); } void Camera::initialize() { TRACE("Enter"); print(); glViewport(_viewport[0], _viewport[1], _viewport[2], _viewport[3]); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(_fov, (GLdouble)_viewport[2]/(GLdouble)_viewport[3], _near, _far); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(-_position.x, -_position.y, -_position.z); glMultMatrixd((const GLdouble *)_rotationMatrix.get()); } void Camera::getProjectionMatrix(Matrix4x4d& mat) { glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluPerspective(_fov, (GLdouble)_viewport[2]/(GLdouble)_viewport[3], _near, _far); glGetDoublev(GL_PROJECTION_MATRIX, (GLdouble *)mat.get()); glPopMatrix(); glMatrixMode(GL_MODELVIEW); } void Camera::orient(double *quat) { Quaternion q(quat[0], quat[1], quat[2], quat[3]); Rotation rot; rot.set(q); _rotationMatrix.makeRotation(rot); _rotationMatrix.transpose(); TRACE("Set rotation to quat: %g %g %g %g", quat[0], quat[1], quat[2], quat[3]); } void Camera::orient(float angleX, float angleY, float angleZ) { angleX = -angleX; angleY = angleY - 180.; _rotationMatrix.makeRotation(1, 0, 0, deg2rad(angleX)); Matrix4x4d mat; mat.makeRotation(0, 1, 0, deg2rad(angleY)); _rotationMatrix.multiply(mat); mat.makeRotation(0, 0, 1, deg2rad(angleZ)); _rotationMatrix.multiply(mat); TRACE("Set rotation to angles: %g %g %g", angleX, angleY, angleZ); } void Camera::print() const { TRACE("x: %d y: %d w: %d h: %d", _viewport[0], _viewport[1], _viewport[2], _viewport[3]); TRACE("fov: %g near: %g far: %g", _fov, _near, _far); TRACE("pos: %g %g %g", _position.x, _position.y, _position.z); TRACE("Rotation matrix: "); _rotationMatrix.print(); }