mirror of
https://github.com/OPM/ResInsight.git
synced 2025-01-05 21:53:27 -06:00
ce9a65ee41
Housekeeping in VizFwk in preparation for introducing support for QOpenGLWidget and Qt6 * Adjusted unit tests to changes in source code * Use Qt5 as default and removed copying of Qt DLLs * Removed support for Qt4 * Removed the CVF_OPENGL_ES define. If we ever want to re-introduce support fro OpenGLES/Angle it should be handled differently. *Added include of <locale.h> * Added target for running Glsl2Include in order to build cvfShaderSourceStrings.h * Removed all usage of CVF_USING_CMAKE * Removed visual studio project files
1049 lines
37 KiB
C++
1049 lines
37 KiB
C++
//##################################################################################################
|
|
//
|
|
// Custom Visualization Core library
|
|
// Copyright (C) 2011-2013 Ceetron AS
|
|
//
|
|
// This library may be used under the terms of either the GNU General Public License or
|
|
// the GNU Lesser General Public License as follows:
|
|
//
|
|
// GNU General Public License Usage
|
|
// This library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
// FITNESS FOR A PARTICULAR PURPOSE.
|
|
//
|
|
// See the GNU General Public License at <<http://www.gnu.org/licenses/gpl.html>>
|
|
// for more details.
|
|
//
|
|
// GNU Lesser General Public License Usage
|
|
// This library is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation; either version 2.1 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
// FITNESS FOR A PARTICULAR PURPOSE.
|
|
//
|
|
// See the GNU Lesser General Public License at <<http://www.gnu.org/licenses/lgpl-2.1.html>>
|
|
// for more details.
|
|
//
|
|
//##################################################################################################
|
|
|
|
|
|
#include "cvfBase.h"
|
|
#include "cvfCamera.h"
|
|
#include "cvfOpenGL.h"
|
|
#include "cvfRay.h"
|
|
#include "cvfViewport.h"
|
|
#include "cvfBoundingBox.h"
|
|
#include "cvfGeometryUtils.h"
|
|
|
|
namespace cvf {
|
|
|
|
|
|
|
|
//==================================================================================================
|
|
///
|
|
/// \class cvf::Camera
|
|
/// \ingroup Render
|
|
///
|
|
/// A Camera defines a view matrix (viewpoint) and a projection matrix
|
|
///
|
|
/// The camera stores and applies the view matrix and the projection matrix to OpenGL. A camera
|
|
/// has a reference to a Viewport that is associated with the camera. The camera needs the viewport
|
|
/// to be able to setup the projection matrix.
|
|
///
|
|
//==================================================================================================
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
Camera::Camera()
|
|
: m_projectionType(PERSPECTIVE),
|
|
m_fieldOfViewYDeg(40.0),
|
|
m_frontPlaneFrustumHeight(0),
|
|
m_nearPlane(0.05),
|
|
m_farPlane(10000.0),
|
|
m_cachedFrontPlanePixelHeight(0)
|
|
{
|
|
m_viewport = new Viewport;
|
|
|
|
// Default perspective projection. This will update the cached values
|
|
setProjectionAsPerspective(m_fieldOfViewYDeg, m_nearPlane, m_farPlane);
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
Camera::~Camera()
|
|
{
|
|
// Empty destructor to avoid errors with undefined types when cvf::ref's destructor gets called
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// The Camera's view matrix.
|
|
///
|
|
/// This is what you would pass to OpenGL with:
|
|
/// \code
|
|
/// glMatrixMode(GL_MODELVIEW);
|
|
/// glLoadMatrix(camera.viewMatrix().ptr());
|
|
/// \endcode
|
|
//--------------------------------------------------------------------------------------------------
|
|
void Camera::setViewMatrix(const Mat4d& mat)
|
|
{
|
|
m_viewMatrix = mat;
|
|
|
|
updateCachedValues();
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Set the view matrix from the standard OpenGL lookat specification.
|
|
///
|
|
/// View direction will be (center - eye). Center is not stored in this class.
|
|
//--------------------------------------------------------------------------------------------------
|
|
void Camera::setFromLookAt(const Vec3d& eye, const Vec3d& center, const Vec3d& up)
|
|
{
|
|
Mat4d invViewMatrix = createLookAtMatrix(eye, center, up);
|
|
m_viewMatrix = invViewMatrix.getInverted();
|
|
|
|
updateCachedValues();
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Get the eye point, vrp and up vector. Params can be NULL if not needed.
|
|
//--------------------------------------------------------------------------------------------------
|
|
void Camera::toLookAt(Vec3d* eye, Vec3d* vrp, Vec3d* up) const
|
|
{
|
|
Vec3d vEye(0, 0, 0);
|
|
Vec3d vVrp(0, 0, -1);
|
|
Vec3d vUp(0, 1, 0);
|
|
|
|
vEye.transformPoint(m_cachedInvertedViewMatrix);
|
|
vVrp.transformPoint(m_cachedInvertedViewMatrix);
|
|
vUp.transformVector(m_cachedInvertedViewMatrix);
|
|
|
|
if (eye) *eye = vEye;
|
|
if (vrp) *vrp = vVrp;
|
|
if (up) *up = vUp;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Retrieves the camera's position (eye point)
|
|
//--------------------------------------------------------------------------------------------------
|
|
Vec3d Camera::position() const
|
|
{
|
|
Vec3d pos(0, 0, 0);
|
|
pos.transformPoint(m_cachedInvertedViewMatrix);
|
|
|
|
return pos;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Get the camera's forward direction vector. Vector is normalized
|
|
//--------------------------------------------------------------------------------------------------
|
|
Vec3d Camera::direction() const
|
|
{
|
|
Vec3d dir(0, 0, -1.0);
|
|
dir.transformVector(m_cachedInvertedViewMatrix);
|
|
|
|
return dir;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Get the camera's up vector. Vector is normalized
|
|
//--------------------------------------------------------------------------------------------------
|
|
Vec3d Camera::up() const
|
|
{
|
|
Vec3d up(0, 1.0, 0);
|
|
up.transformVector(m_cachedInvertedViewMatrix);
|
|
|
|
return up;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Get the camera's right vector. Vector is normalized
|
|
//--------------------------------------------------------------------------------------------------
|
|
Vec3d Camera::right() const
|
|
{
|
|
Vec3d right(1.0, 0, 0);
|
|
right.transformVector(m_cachedInvertedViewMatrix);
|
|
|
|
return right;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
Camera::ProjectionType Camera::projection() const
|
|
{
|
|
return m_projectionType;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Setup a perspective projection.
|
|
///
|
|
/// The fieldOfViewYDeg parameter is the total field of view angle (in degrees) in the Y direction.
|
|
/// Works similar to gluPerspective().
|
|
//--------------------------------------------------------------------------------------------------
|
|
void Camera::setProjectionAsPerspective(double fieldOfViewYDeg, double nearPlane, double farPlane)
|
|
{
|
|
CVF_ASSERT(fieldOfViewYDeg > 0 && fieldOfViewYDeg < 180);
|
|
CVF_ASSERT(nearPlane > 0);
|
|
CVF_ASSERT(farPlane > nearPlane);
|
|
|
|
m_projectionType = PERSPECTIVE;
|
|
m_fieldOfViewYDeg = fieldOfViewYDeg;
|
|
m_nearPlane = nearPlane;
|
|
m_farPlane = farPlane;
|
|
m_frontPlaneFrustumHeight = 2*m_nearPlane*Math::tan(Math::toRadians(m_fieldOfViewYDeg/2.0));
|
|
|
|
m_projectionMatrix = createPerspectiveMatrix(Math::toRadians(m_fieldOfViewYDeg), aspectRatio(), m_nearPlane, m_farPlane);
|
|
|
|
updateCachedValues();
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Setup an orthographic projection
|
|
///
|
|
/// This works similar to glOrtho().
|
|
//--------------------------------------------------------------------------------------------------
|
|
void Camera::setProjectionAsOrtho(double height, double nearPlane, double farPlane)
|
|
{
|
|
CVF_ASSERT(height > 0);
|
|
double width = height * aspectRatio();
|
|
|
|
m_projectionType = ORTHO;
|
|
m_fieldOfViewYDeg = UNDEFINED_DOUBLE;
|
|
m_frontPlaneFrustumHeight = height;
|
|
m_nearPlane = nearPlane;
|
|
m_farPlane = farPlane;
|
|
|
|
m_projectionMatrix = createOrthoMatrix(-width/2.0, width/2.0, -height/2.0, height/2.0, m_nearPlane, m_farPlane);
|
|
|
|
updateCachedValues();
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Setup an orthographic projection
|
|
///
|
|
/// This works similar to glOrtho().
|
|
//--------------------------------------------------------------------------------------------------
|
|
void Camera::setProjectionAsUnitOrtho()
|
|
{
|
|
m_projectionType = ORTHO;
|
|
m_fieldOfViewYDeg = UNDEFINED_DOUBLE;
|
|
m_frontPlaneFrustumHeight = 1;
|
|
m_nearPlane = -1.0;
|
|
m_farPlane = 1.0;
|
|
|
|
m_projectionMatrix = createOrthoMatrix(0.0, 1.0, 0.0, 1.0, m_nearPlane, m_farPlane);
|
|
|
|
updateCachedValues();
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Setup a pixel exact two-dimensional orthographic viewing region
|
|
///
|
|
/// Sets a 2D projection where each unit corresponds to a pixel.
|
|
//--------------------------------------------------------------------------------------------------
|
|
void Camera::setProjectionAsPixelExact2D()
|
|
{
|
|
m_projectionType = ORTHO;
|
|
m_fieldOfViewYDeg = UNDEFINED_DOUBLE;
|
|
m_frontPlaneFrustumHeight = m_viewport->height();
|
|
m_nearPlane = -1.0;
|
|
m_farPlane = 1.0;
|
|
|
|
m_projectionMatrix = createOrthoMatrix(0, m_viewport->width(), 0, m_viewport->height(), m_nearPlane, m_farPlane);
|
|
|
|
updateCachedValues();
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Setup the view to contain the passed bounding box, with the camera looking from the given
|
|
/// direction (dir) and with the given up vector (up).
|
|
///
|
|
/// The passed boundingBox should be the bounding box of the object/model you would like to fit
|
|
/// the view to. The relativeDistance parameter specifies the distance from the camera to the
|
|
/// center of the bounding box
|
|
//--------------------------------------------------------------------------------------------------
|
|
void Camera::fitView(const BoundingBox& boundingBox, const Vec3d& dir, const Vec3d& up, double coverageFactor)
|
|
{
|
|
// Use old view direction, but look towards model center
|
|
Vec3d eye = computeFitViewEyePosition(boundingBox, dir, up, coverageFactor, m_fieldOfViewYDeg, viewport()->aspectRatio());
|
|
|
|
// Will update cached values
|
|
setFromLookAt(eye, boundingBox.center(), up);
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
Vec3d Camera::computeFitViewEyePosition(const BoundingBox& boundingBox, const Vec3d& dir, const Vec3d& up, double coverageFactor, double fieldOfViewYDeg, double aspectRatio)
|
|
{
|
|
cvf::Vec3d corners[8];
|
|
boundingBox.cornerVertices(corners);
|
|
|
|
cvf::Vec3d upNorm = up.getNormalized();
|
|
cvf::Vec3d right = dir^up;
|
|
right.normalize();
|
|
cvf::Vec3d boxEyeNorm = (-dir).getNormalized();
|
|
|
|
cvf::Plane planeTop;
|
|
planeTop.setFromPointAndNormal(boundingBox.center(), up);
|
|
|
|
cvf::Plane planeSide;
|
|
planeSide.setFromPointAndNormal(boundingBox.center(), right);
|
|
|
|
// fieldOfViewYDeg is the complete angle in degrees, get half in radians
|
|
double fovY = Math::toRadians(fieldOfViewYDeg/2.0);
|
|
double fovX = Math::atan(Math::tan(fovY)*aspectRatio);
|
|
|
|
double dist = 0;
|
|
|
|
for (size_t i = 0; i < 8; ++i)
|
|
{
|
|
cvf::Vec3d centerToCorner = corners[i] - boundingBox.center();
|
|
|
|
// local horizontal plane
|
|
cvf::Vec3d centerToCornerTop = planeTop.projectPoint(centerToCorner);
|
|
double rightCoord = centerToCornerTop*right;
|
|
double distRight = Math::abs(rightCoord/Math::tan(fovX));
|
|
distRight += centerToCornerTop*boxEyeNorm;
|
|
|
|
// local vertical plane
|
|
cvf::Vec3d centerToCornerSide = planeSide.projectPoint(centerToCorner);
|
|
double upCoord = centerToCornerSide*upNorm;
|
|
double distUp = Math::abs(upCoord/Math::tan(fovY));
|
|
distUp += (centerToCornerSide*boxEyeNorm);
|
|
|
|
// Adjust for the distance scale factor
|
|
distRight /= coverageFactor;
|
|
distUp /= coverageFactor;
|
|
|
|
dist = CVF_MAX(dist, distRight);
|
|
dist = CVF_MAX(dist, distUp);
|
|
}
|
|
|
|
// Avoid distances of 0 when model has no extent
|
|
if (!(dist > 0))
|
|
{
|
|
dist = 1.0;
|
|
}
|
|
|
|
// Use old view direction, but look towards model center
|
|
Vec3d eye = boundingBox.center()- dir.getNormalized()*dist;
|
|
|
|
return eye;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void Camera::fitViewOrtho(const BoundingBox& boundingBox, double eyeDist, const Vec3d& dir, const Vec3d& up, double adjustmentFactor)
|
|
{
|
|
// Algorithm:
|
|
// Project all points into the viewing plan. Find the distance along the right and up vector.
|
|
// Set the height of the frustum to this distance.
|
|
cvf::Vec3d corners[8];
|
|
boundingBox.cornerVertices(corners);
|
|
|
|
cvf::BoundingBox projBox;
|
|
cvf::Plane viewPlane;
|
|
viewPlane.setFromPointAndNormal(boundingBox.center(), -dir);
|
|
|
|
cvf::Vec3d upNorm = up.getNormalized();
|
|
cvf::Vec3d right = up^dir;
|
|
right.normalize();
|
|
|
|
double rightMin = cvf::UNDEFINED_DOUBLE_THRESHOLD;
|
|
double rightMax = -cvf::UNDEFINED_DOUBLE_THRESHOLD;
|
|
double upMin = cvf::UNDEFINED_DOUBLE_THRESHOLD;
|
|
double upMax = -cvf::UNDEFINED_DOUBLE_THRESHOLD;
|
|
|
|
for (size_t i = 0; i < 8; ++i)
|
|
{
|
|
cvf::Vec3d cornerInPlane = viewPlane.projectPoint(corners[i]);
|
|
cvf::Vec3d cornerVec = cornerInPlane-boundingBox.center();
|
|
|
|
double rightCoord = cornerVec*right;
|
|
rightMin = CVF_MIN(rightMin, rightCoord);
|
|
rightMax = CVF_MAX(rightMax, rightCoord);
|
|
|
|
double upCood = cornerVec*upNorm;
|
|
upMin = CVF_MIN(upMin, upCood);
|
|
upMax = CVF_MAX(upMax, upCood);
|
|
}
|
|
|
|
double deltaRight = rightMax - rightMin;
|
|
double deltaUp = upMax - upMin;
|
|
double newHeight = CVF_MAX(deltaUp, deltaRight/aspectRatio())/adjustmentFactor;
|
|
|
|
setProjectionAsOrtho(newHeight, m_nearPlane, m_farPlane);
|
|
|
|
Vec3d eye = boundingBox.center()- eyeDist*dir.getNormalized();
|
|
setFromLookAt(eye, boundingBox.center(), up);
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Set the front and back clipping planes close to the given bounding box
|
|
//--------------------------------------------------------------------------------------------------
|
|
void Camera::setClipPlanesFromBoundingBox(const BoundingBox& boundingBox, double minNearPlaneDistance)
|
|
{
|
|
CVF_ASSERT(minNearPlaneDistance > 0);
|
|
|
|
Vec3d eye, vrp, up;
|
|
toLookAt(&eye, &vrp, &up);
|
|
|
|
Vec3d viewdir = (vrp - eye).getNormalized();
|
|
double distEyeBoxCenterAlongViewDir = (boundingBox.center() - eye)*viewdir;
|
|
|
|
double nearPlane = distEyeBoxCenterAlongViewDir - boundingBox.radius();
|
|
double farPlane = distEyeBoxCenterAlongViewDir + boundingBox.radius();
|
|
|
|
if (nearPlane < minNearPlaneDistance) nearPlane = minNearPlaneDistance;
|
|
if (farPlane <= nearPlane) farPlane = nearPlane + 1.0;
|
|
|
|
if (projection() == PERSPECTIVE)
|
|
{
|
|
setProjectionAsPerspective(m_fieldOfViewYDeg, nearPlane, farPlane);
|
|
}
|
|
else
|
|
{
|
|
setProjectionAsOrtho(m_frontPlaneFrustumHeight, nearPlane, farPlane);
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Returns the current view matrix
|
|
//--------------------------------------------------------------------------------------------------
|
|
const Mat4d& Camera::viewMatrix() const
|
|
{
|
|
return m_viewMatrix;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Returns the inverted current view matrix
|
|
///
|
|
/// The inverted view matrix is cached for performance reasons.
|
|
//--------------------------------------------------------------------------------------------------
|
|
const Mat4d& Camera::invertedViewMatrix() const
|
|
{
|
|
return m_cachedInvertedViewMatrix;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Returns the current projection matrix
|
|
//--------------------------------------------------------------------------------------------------
|
|
const Mat4d& Camera::projectionMatrix() const
|
|
{
|
|
return m_projectionMatrix;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Returns the total field of view in Y direction in degrees.
|
|
///
|
|
/// If projection is orthographic, this function will return UNDEFINED_DOUBLE
|
|
//--------------------------------------------------------------------------------------------------
|
|
double Camera::fieldOfViewYDeg() const
|
|
{
|
|
return m_fieldOfViewYDeg;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Returns the near clipping plane
|
|
//--------------------------------------------------------------------------------------------------
|
|
double Camera::nearPlane() const
|
|
{
|
|
return m_nearPlane;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Returns the far clipping plane
|
|
//--------------------------------------------------------------------------------------------------
|
|
double Camera::farPlane()const
|
|
{
|
|
return m_farPlane;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Returns the aspect ratio (width / height) of the corresponding viewport.
|
|
//--------------------------------------------------------------------------------------------------
|
|
double Camera::aspectRatio() const
|
|
{
|
|
CVF_ASSERT(m_viewport.notNull());
|
|
|
|
return viewport()->aspectRatio();
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Specify the position and dimensions of the viewport, and update the projection matrix.
|
|
///
|
|
/// This method calls Viewport::set() and then updates the projection matrix, as this is depending
|
|
/// on the viewport dimensions.
|
|
/// This method is usually used as a response to a Resize message from the window system
|
|
///
|
|
/// The window coordinates (x,y) are in OpenGL style coordinates, which means a right handed
|
|
/// coordinate system with the origin in the lower left corner of the window.
|
|
//--------------------------------------------------------------------------------------------------
|
|
void Camera::setViewport(int x, int y, uint width, uint height)
|
|
{
|
|
CVF_ASSERT(m_viewport.notNull());
|
|
m_viewport->set(x, y, width, height);
|
|
|
|
// Update projection:
|
|
if (m_projectionType == PERSPECTIVE)
|
|
{
|
|
setProjectionAsPerspective(m_fieldOfViewYDeg, m_nearPlane, m_farPlane);
|
|
}
|
|
else if (m_projectionType == ORTHO)
|
|
{
|
|
setProjectionAsOrtho(m_frontPlaneFrustumHeight, m_nearPlane, m_farPlane);
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Get the viewport used by this camera.
|
|
//--------------------------------------------------------------------------------------------------
|
|
Viewport* Camera::viewport()
|
|
{
|
|
return m_viewport.p();
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Get the viewport used by this camera.
|
|
//--------------------------------------------------------------------------------------------------
|
|
const Viewport* Camera::viewport() const
|
|
{
|
|
return m_viewport.p();
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Create a Ray from window coordinates
|
|
///
|
|
/// The window coordinates are in OpenGL style coordinates, which means a right handed
|
|
/// coordinate system with the origin in the lower left corner of the window.
|
|
//--------------------------------------------------------------------------------------------------
|
|
ref<Ray> Camera::rayFromWindowCoordinates(int x, int y) const
|
|
{
|
|
Vec3d coord0(0, 0, 0);
|
|
if (!unproject(Vec3d(static_cast<double>(x), static_cast<double>(y), 0), &coord0))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
Vec3d coord1(0, 0, 0);
|
|
if (!unproject(Vec3d(static_cast<double>(x), static_cast<double>(y), 1), &coord1))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
Ray* ray = new Ray;
|
|
ray->setOrigin(coord0);
|
|
|
|
Vec3d dir = (coord1 - coord0).getNormalized();
|
|
ray->setDirection(dir);
|
|
|
|
return ray;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Construct a plane from a line specified in window coordinates
|
|
///
|
|
/// The plane will be constructed so that the specified line lies in the plane, and the plane
|
|
/// normal will be pointing left when moving along the line from start to end.
|
|
///
|
|
/// The window coordinates are in OpenGL style coordinates, which means a right handed
|
|
/// coordinate system with the origin in the lower left corner of the window.
|
|
//--------------------------------------------------------------------------------------------------
|
|
ref<Plane> Camera::planeFromLineWindowCoordinates(Vec2i coordStart, Vec2i coordEnd) const
|
|
{
|
|
Vec3d s0(0, 0, 0);
|
|
Vec3d e0(0, 0, 0);
|
|
Vec3d e1(0, 0, 0);
|
|
bool unprojOk = true;
|
|
unprojOk &= unproject(Vec3d(coordStart.x(), coordStart.y(), 0), &s0);
|
|
unprojOk &= unproject(Vec3d(coordEnd.x(), coordEnd.y(), 0), &e0);
|
|
unprojOk &= unproject(Vec3d(coordEnd.x(), coordEnd.y(), 1), &e1);
|
|
if (!unprojOk)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ref<Plane> plane = new Plane;
|
|
if (!plane->setFromPoints(s0, e0, e1))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return plane;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Computes the maximum pixel area that the projected bounding box will occupy with the current camera settings.
|
|
///
|
|
/// The current implementation gives the area of the 2D bounding box of the projected corners of the
|
|
/// 3D bounding box, this not exact but at least >=
|
|
//--------------------------------------------------------------------------------------------------
|
|
double Camera::computeProjectedBoundingBoxPixelArea(const BoundingBox& box) const
|
|
{
|
|
Vec3d corner[8];
|
|
box.cornerVertices(corner);
|
|
|
|
BoundingBox projBB;
|
|
|
|
// project the 8 corners in the viewport
|
|
int i;
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
Vec3d v;
|
|
if (project(corner[i], &v))
|
|
{
|
|
projBB.add(v);
|
|
}
|
|
}
|
|
|
|
Vec3d extent = projBB.extent();
|
|
|
|
double pixels = extent.x() * extent.y();
|
|
|
|
return pixels;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
double Camera::computeProjectedBoundingSpherePixelArea(const Vec3d& center, double radius) const
|
|
{
|
|
if (m_cachedFrontPlanePixelHeight > 0)
|
|
{
|
|
Vec3d currentEye = position();
|
|
|
|
double eyeDist = (currentEye - center).length();
|
|
if (eyeDist > 0)
|
|
{
|
|
double radiusNearWorld = ((m_nearPlane*radius)/eyeDist);
|
|
double pixels = radiusNearWorld/m_cachedFrontPlanePixelHeight;
|
|
|
|
return PI_D*pixels*pixels;
|
|
}
|
|
else
|
|
{
|
|
// We should be returning a large value here, right?
|
|
return std::numeric_limits<double>::max();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool Camera::isProjectedBoundingBoxLessThanThreshold(const BoundingBox& box, double pixelThreshold) const
|
|
{
|
|
Vec3d currentEye = position();
|
|
|
|
double l2Dist = (currentEye - box.center()).lengthSquared();
|
|
|
|
double thresholdDist = distanceWhereObjectProjectsToPixelExtent(2*box.radius(), pixelThreshold) + box.radius();
|
|
|
|
if (l2Dist > thresholdDist*thresholdDist)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Get height of the view frustum in the front plane in world coordinates
|
|
//--------------------------------------------------------------------------------------------------
|
|
double Camera::frontPlaneFrustumHeight() const
|
|
{
|
|
return m_frontPlaneFrustumHeight;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Get the height of a pixel in the front plane in world coordinates
|
|
///
|
|
/// This value is cached for performance reasons.
|
|
//--------------------------------------------------------------------------------------------------
|
|
double Camera::frontPlanePixelHeight() const
|
|
{
|
|
return m_cachedFrontPlanePixelHeight;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Get the distance to where the object with the given extent projected onto the front clipping plane
|
|
/// would have the specified pixel extent.
|
|
///
|
|
/// \param objectExtentWorld The largest object extent in world coordinates
|
|
/// \param objectExtentPixels The requested pixel extent in the front clipping plane
|
|
//--------------------------------------------------------------------------------------------------
|
|
double Camera::distanceWhereObjectProjectsToPixelExtent(double objectExtentWorld, double objectExtentPixels) const
|
|
{
|
|
CVF_ASSERT(objectExtentPixels > 0);
|
|
|
|
if (m_cachedFrontPlanePixelHeight > 0)
|
|
{
|
|
double distance = (objectExtentWorld*m_nearPlane)/(m_cachedFrontPlanePixelHeight*objectExtentPixels);
|
|
return distance;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// OpenGL like unproject.
|
|
///
|
|
/// The input (window) coordinates \a coord must be specified in OpenGL style coordinates, which means
|
|
/// a right handed coordinate system with the origin in the lower left corner of the window.
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool Camera::unproject(const Vec3d& coord, Vec3d* out) const
|
|
{
|
|
CVF_ASSERT(out);
|
|
|
|
Vec4d v(coord, 1.0);
|
|
|
|
// map from viewport to 0-1
|
|
v.x() = (v.x() - m_viewport->x()) / m_viewport->width();
|
|
v.y() = (v.y() - m_viewport->y()) / m_viewport->height();
|
|
|
|
// map to range -1 to 1
|
|
v.x() = v.x() * 2.0f - 1.0f;
|
|
v.y() = v.y() * 2.0f - 1.0f;
|
|
v.z() = v.z() * 2.0f - 1.0f;
|
|
|
|
bool invertible=true;
|
|
Mat4d inverse = m_cachedProjectionMultViewMatrix.getInverted(&invertible);
|
|
if (!invertible)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
v = inverse * v;
|
|
|
|
if (v.w() == 0.0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
out->x() = v.x() / v.w();
|
|
out->y() = v.y() / v.w();
|
|
out->z() = v.z() / v.w();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Maps object coordinates to window coordinates
|
|
///
|
|
/// The returned window coordinates \a out are in OpenGL style coordinates, which means a right handed
|
|
/// coordinate system with the origin in the lower left corner of the window.
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool Camera::project(const Vec3d& point, Vec3d* out) const
|
|
{
|
|
return GeometryUtils::project(m_cachedProjectionMultViewMatrix, Vec2i(m_viewport->x(), m_viewport->y()), Vec2ui(m_viewport->width(), m_viewport->height()), point, out);
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Get camera's Frustum. Planes have an inward pointing normal
|
|
///
|
|
/// \sa
|
|
/// computeViewFrustum()
|
|
//--------------------------------------------------------------------------------------------------
|
|
Frustum Camera::frustum() const
|
|
{
|
|
return m_cachedViewFrustum;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Update the various cached variables stored in the view.
|
|
///
|
|
/// This needs to be called whenever one of the following items are updated:
|
|
/// - View matrix
|
|
/// - Projection matrix
|
|
/// - Clip planes
|
|
/// - Viewport dimensions (NB!), as this could be done directly in cvf::Viewport
|
|
///
|
|
/// The following values are cached:
|
|
/// - m_cachedInvertedViewMatrix
|
|
/// - m_cachedProjectionMultViewMatrix
|
|
/// - m_cachedViewFrustum
|
|
/// - m_cachedFrontPlanePixelSize
|
|
//--------------------------------------------------------------------------------------------------
|
|
void Camera::updateCachedValues()
|
|
{
|
|
// Update cached matrices
|
|
m_cachedProjectionMultViewMatrix = m_projectionMatrix * m_viewMatrix;
|
|
m_cachedInvertedViewMatrix = m_viewMatrix.getInverted();
|
|
|
|
// Update the cached frustum
|
|
m_cachedViewFrustum = computeViewFrustum();
|
|
|
|
// Update the front plane pixel size (height)
|
|
CVF_ASSERT(m_viewport.notNull());
|
|
uint vpWidth = m_viewport->width();
|
|
uint vpHeight = m_viewport->height();
|
|
if (vpWidth > 0 && vpHeight > 0)
|
|
{
|
|
// The height/size of a pixel in the front clipping plane
|
|
m_cachedFrontPlanePixelHeight = m_frontPlaneFrustumHeight/vpHeight;
|
|
}
|
|
else
|
|
{
|
|
// Technically we could compute a pixel height as long as the viewport height is non-zero,
|
|
// but considering normal usage of the pixel height it makes sense to only compute it
|
|
// when the viewport area is non-zero
|
|
m_cachedFrontPlanePixelHeight = 0;
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Get camera's Frustum. Planes have an inward pointing normal
|
|
//--------------------------------------------------------------------------------------------------
|
|
Frustum Camera::computeViewFrustum() const
|
|
{
|
|
// See:
|
|
// http://www2.ravensoft.com/users/ggribb/plane%20extraction.pdf
|
|
// http://zach.in.tu-clausthal.de/teaching/cg_literatur/lighthouse3d_view_frustum_culling/index.html
|
|
|
|
Frustum f;
|
|
const Mat4d& m = m_cachedProjectionMultViewMatrix;
|
|
|
|
Plane left = Plane(m.rowCol(3, 0) + m.rowCol(0, 0),
|
|
m.rowCol(3, 1) + m.rowCol(0, 1),
|
|
m.rowCol(3, 2) + m.rowCol(0, 2),
|
|
m.rowCol(3, 3) + m.rowCol(0, 3));
|
|
|
|
Plane right = Plane(m.rowCol(3, 0) - m.rowCol(0, 0),
|
|
m.rowCol(3, 1) - m.rowCol(0, 1),
|
|
m.rowCol(3, 2) - m.rowCol(0, 2),
|
|
m.rowCol(3, 3) - m.rowCol(0, 3));
|
|
|
|
Plane bot = Plane(m.rowCol(3, 0) + m.rowCol(1, 0),
|
|
m.rowCol(3, 1) + m.rowCol(1, 1),
|
|
m.rowCol(3, 2) + m.rowCol(1, 2),
|
|
m.rowCol(3, 3) + m.rowCol(1, 3));
|
|
|
|
Plane top = Plane(m.rowCol(3, 0) - m.rowCol(1, 0),
|
|
m.rowCol(3, 1) - m.rowCol(1, 1),
|
|
m.rowCol(3, 2) - m.rowCol(1, 2),
|
|
m.rowCol(3, 3) - m.rowCol(1, 3));
|
|
|
|
Plane front = Plane(m.rowCol(3, 0) + m.rowCol(2, 0),
|
|
m.rowCol(3, 1) + m.rowCol(2, 1),
|
|
m.rowCol(3, 2) + m.rowCol(2, 2),
|
|
m.rowCol(3, 3) + m.rowCol(2, 3));
|
|
|
|
Plane back = Plane(m.rowCol(3, 0) - m.rowCol(2, 0),
|
|
m.rowCol(3, 1) - m.rowCol(2, 1),
|
|
m.rowCol(3, 2) - m.rowCol(2, 2),
|
|
m.rowCol(3, 3) - m.rowCol(2, 3));
|
|
|
|
|
|
// Assign planes if all are valid
|
|
if (left.isValid() && right.isValid() && top.isValid() && bot.isValid() && front.isValid() && back.isValid())
|
|
{
|
|
// Left clipping plane
|
|
f.setPlane(Frustum::LEFT, left);
|
|
|
|
// Right clipping plane
|
|
f.setPlane(Frustum::RIGHT, right);
|
|
|
|
// Top clipping plane
|
|
f.setPlane(Frustum::TOP, top);
|
|
|
|
// Bottom clipping plane
|
|
f.setPlane(Frustum::BOTTOM, bot);
|
|
|
|
// Near clipping plane
|
|
f.setPlane(Frustum::FRONT, front);
|
|
|
|
// Far clipping plane
|
|
f.setPlane(Frustum::BACK, back);
|
|
}
|
|
|
|
|
|
return f;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Helper class to compute the projection matrix based on the given view frustum
|
|
//--------------------------------------------------------------------------------------------------
|
|
Mat4d Camera::createFrustumMatrix(double left, double right, double bottom, double top, double zNear, double zFar)
|
|
{
|
|
Matrix4<double> m;
|
|
|
|
if (zNear <= 0 || zFar <= 0 || zNear == zFar || left == right || top == bottom)
|
|
{
|
|
return Mat4d::ZERO;
|
|
}
|
|
|
|
double x = (2.0 * zNear) / (right - left);
|
|
double y = (2.0 * zNear) / (top - bottom);
|
|
double a = (right + left) / (right - left);
|
|
double b = (top + bottom) / (top - bottom);
|
|
double c = -(zFar + zNear) / (zFar - zNear);
|
|
double d = -(2.0 * zFar * zNear) / (zFar - zNear);
|
|
|
|
m.setRowCol(0, 0, x); m.setRowCol(0, 1, 0); m.setRowCol(0, 2, a); m.setRowCol(0, 3, 0);
|
|
m.setRowCol(1, 0, 0); m.setRowCol(1, 1, y); m.setRowCol(1, 2, b); m.setRowCol(1, 3, 0);
|
|
m.setRowCol(2, 0, 0); m.setRowCol(2, 1, 0); m.setRowCol(2, 2, c); m.setRowCol(2, 3, d);
|
|
m.setRowCol(3, 0, 0); m.setRowCol(3, 1, 0); m.setRowCol(3, 2, -1.0); m.setRowCol(3, 3, 0);
|
|
|
|
return m;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Helper class to create a perspective projection matrix
|
|
//--------------------------------------------------------------------------------------------------
|
|
Mat4d Camera::createPerspectiveMatrix(double fovy, double aspect_ratio, double znear, double zfar)
|
|
{
|
|
Mat4d m;
|
|
|
|
double rads = fovy / 2.0;
|
|
double dz = zfar - znear;
|
|
double sa = Math::sin(rads);
|
|
|
|
if ((dz == 0) || (sa == 0) || (aspect_ratio == 0))
|
|
{
|
|
return Mat4d::ZERO;
|
|
}
|
|
|
|
double ctan = Math::cos(rads) / sa;
|
|
|
|
m.setRowCol(0,0, ctan / aspect_ratio);
|
|
m.setRowCol(1,1, ctan);
|
|
m.setRowCol(2,2, -(zfar + znear)/dz);
|
|
m.setRowCol(2,3, -(2.0*znear* zfar)/dz);
|
|
m.setRowCol(3,2, -1.0);
|
|
m.setRowCol(3,3, 0);
|
|
|
|
// A (0,2) and B (1,2) are zero due to the frustum being symmetrical around zero
|
|
|
|
return m;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Helper class to create a orthographic projection matrix
|
|
//--------------------------------------------------------------------------------------------------
|
|
Mat4d Camera::createOrthoMatrix(double left, double right, double bottom, double top, double nearPlane, double farPlane)
|
|
{
|
|
Mat4d m;
|
|
|
|
m.setRowCol(0,0, 2.0 / (right - left));
|
|
m.setRowCol(0,1, 0);
|
|
m.setRowCol(0,2, 0);
|
|
m.setRowCol(0,3, -(right + left) / (right - left));
|
|
|
|
m.setRowCol(1,0, 0);
|
|
m.setRowCol(1,1, 2.0 / (top-bottom));
|
|
m.setRowCol(1,2, 0);
|
|
m.setRowCol(1,3, -(top + bottom) / (top - bottom));
|
|
|
|
m.setRowCol(2,0, 0);
|
|
m.setRowCol(2,1, 0);
|
|
m.setRowCol(2,2, -2.0 / (farPlane - nearPlane));
|
|
m.setRowCol(2,3, -(farPlane + nearPlane) / (farPlane-nearPlane));
|
|
|
|
m.setRowCol(3,0, 0);
|
|
m.setRowCol(3,1, 0);
|
|
m.setRowCol(3,2, 0);
|
|
m.setRowCol(3,3, 1.0);
|
|
|
|
return m;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Helper class to create a view matrix from an OpenGL "lookat" specification
|
|
//--------------------------------------------------------------------------------------------------
|
|
Mat4d Camera::createLookAtMatrix(Vec3d eye, Vec3d vrp, Vec3d up)
|
|
{
|
|
Vec3d y = up.getNormalized();
|
|
Vec3d z = (eye - vrp).getNormalized(); // == -(vrp-eye)
|
|
Vec3d x = (y^z).getNormalized();
|
|
y = (z^x).getNormalized();
|
|
|
|
Mat4d m;
|
|
|
|
m.setRowCol(0, 0, x.x()); m.setRowCol(0, 1, y.x()); m.setRowCol(0, 2, z.x()); m.setRowCol(0, 3, eye.x());
|
|
m.setRowCol(1, 0, x.y()); m.setRowCol(1, 1, y.y()); m.setRowCol(1, 2, z.y()); m.setRowCol(1, 3, eye.y());
|
|
m.setRowCol(2, 0, x.z()); m.setRowCol(2, 1, y.z()); m.setRowCol(2, 2, z.z()); m.setRowCol(2, 3, eye.z());
|
|
m.setRowCol(3, 0, 0); m.setRowCol(3, 1, 0); m.setRowCol(3, 2, 0); m.setRowCol(3, 3, 1);
|
|
|
|
return m;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Apply the camera settings to OpenGL. Note that the Viewport settings are not applied
|
|
//--------------------------------------------------------------------------------------------------
|
|
void Camera::applyOpenGL() const
|
|
{
|
|
// apply the projection matrix
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadMatrixd(m_projectionMatrix.ptr());
|
|
|
|
// apply the view matrix
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadMatrixd(viewMatrix().ptr());
|
|
}
|
|
|
|
} // namespace cvf
|
|
|