ResInsight/Fwk/AppFwk/cafViewer/cafViewer.cpp
Jacob Støren 0eace5579a #168 Fixed shader based light in parallel projection
Added the light position as a uniform in the standard light model shader snippet in the effect generator.
Set the uniform default value on the shader program where ever it is used.
Added an override global uniform set on the viewer to control the light position, and to set it far behind camera when in parallel projection.
2016-08-16 10:58:07 +02:00

1172 lines
40 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 "cafViewer.h"
#include "cafCadNavigation.h"
#include "cafFrameAnimationControl.h"
#include "cafNavigationPolicy.h"
#include "cvfCamera.h"
#include "cvfDebugTimer.h"
#include "cvfDrawable.h"
#include "cvfDrawableGeo.h"
#include "cvfDynamicUniformSet.h"
#include "cvfHitItemCollection.h"
#include "cvfManipulatorTrackball.h"
#include "cvfModel.h"
#include "cvfOpenGLResourceManager.h"
#include "cvfOverlayImage.h"
#include "cvfPart.h"
#include "cvfRay.h"
#include "cvfRayIntersectSpec.h"
#include "cvfRenderQueueSorter.h"
#include "cvfRenderSequence.h"
#include "cvfRendering.h"
#include "cvfScene.h"
#include "cvfTextureImage.h"
#include "cvfTransform.h"
#include "cvfUniform.h"
#include "cvfUniformSet.h"
#include "cvfqtOpenGLContext.h"
#include "cvfqtPerformanceInfoHud.h"
#include "cvfqtUtils.h"
#include <QDebug>
#include <QHBoxLayout>
#include <QInputEvent>
namespace caf
{
class GlobalViewerDynUniformSet: public cvf::DynamicUniformSet
{
public:
GlobalViewerDynUniformSet()
{
m_headlightPosition = new cvf::UniformFloat("u_ecLightPosition", cvf::Vec3f(0.5, 5.0, 7.0));
m_uniformSet = new cvf::UniformSet();
m_uniformSet->setUniform(m_headlightPosition.p());
}
virtual ~GlobalViewerDynUniformSet() {}
void setHeadLightPosition(const cvf::Vec3f posRelativeToCamera) { m_headlightPosition->set(posRelativeToCamera);}
virtual cvf::UniformSet* uniformSet() { return m_uniformSet.p(); }
virtual void update(cvf::Rendering* rendering){};
private:
cvf::ref<cvf::UniformSet> m_uniformSet;
cvf::ref<cvf::UniformFloat> m_headlightPosition;
};
}
std::list<caf::Viewer*> caf::Viewer::sm_viewers;
cvf::ref<cvf::OpenGLContextGroup> caf::Viewer::sm_openGLContextGroup;
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf::Viewer::Viewer(const QGLFormat& format, QWidget* parent)
: caf::OpenGLWidget(contextGroup(), format, new QWidget(parent), sharedWidget()),
m_navigationPolicy(NULL),
m_minNearPlaneDistance(0.05),
m_maxFarPlaneDistance(cvf::UNDEFINED_DOUBLE),
m_cameraFieldOfViewYDeg(40.0),
m_releaseOGLResourcesEachFrame(false),
m_paintCounter(0),
m_navigationPolicyEnabled(true),
m_isOverlayPaintingEnabled(true)
{
m_layoutWidget = parentWidget();
QHBoxLayout* layout = new QHBoxLayout(m_layoutWidget);
layout->addWidget(this);
layout->setContentsMargins(0, 0, 0, 0);
setAutoFillBackground(false);
setMouseTracking(true);
// Needed to get keystrokes
setFocusPolicy(Qt::ClickFocus);
m_globalUniformSet = new GlobalViewerDynUniformSet();
m_mainCamera = new cvf::Camera;
m_mainCamera->setFromLookAt(cvf::Vec3d(0,0,-1), cvf::Vec3d(0,0,0), cvf::Vec3d(0,1,0));
m_renderingSequence = new cvf::RenderSequence();
m_renderingSequence->setDefaultFFLightPositional(cvf::Vec3f(0.5, 5.0, 7.0));
m_mainRendering = new cvf::Rendering();
m_animationControl = new caf::FrameAnimationControl(this);
connect(m_animationControl, SIGNAL(changeFrame(int)), SLOT(slotSetCurrentFrame(int)));
connect(m_animationControl, SIGNAL(endAnimation()), SLOT(slotEndAnimation()));
this->setNavigationPolicy(new caf::CadNavigation);
m_overlayTextureImage = new cvf::TextureImage;
m_overlayImage = new cvf::OverlayImage(m_overlayTextureImage.p());
m_overlayImage->setBlending(cvf::OverlayImage::TEXTURE_ALPHA);
m_overlayImage->setLayoutFixedPosition(cvf::Vec2i(0,0));
setupMainRendering();
setupRenderingSequence();
m_showPerfInfoHud = false;
sm_viewers.push_back(this);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf::Viewer::~Viewer()
{
this->cvfShutdownOpenGLContext();
sm_viewers.remove(this);
// To delete the layout widget
if (m_layoutWidget) m_layoutWidget->deleteLater();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::setupMainRendering()
{
m_mainRendering->setCamera(m_mainCamera.p());
m_mainRendering->setRenderQueueSorter(new cvf::RenderQueueSorterBasic(cvf::RenderQueueSorterBasic::EFFECT_ONLY));
m_mainRendering->addGlobalDynamicUniformSet(m_globalUniformSet.p());
// Set fixed function rendering if QGLFormat does not support directRendering
if (!this->format().directRendering())
{
m_mainRendering->renderEngine()->enableForcedImmediateMode(true);
}
updateOverlayImagePresence();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::setupRenderingSequence()
{
m_renderingSequence->addRendering(m_mainRendering.p());
updateCamera(width(), height());
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf::Viewer* caf::Viewer::sharedWidget()
{
if (sm_viewers.size() > 0)
{
return *(sm_viewers.begin());
}
return NULL;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::OpenGLContextGroup* caf::Viewer::contextGroup()
{
if (sm_openGLContextGroup.isNull())
{
sm_openGLContextGroup = new cvf::OpenGLContextGroup();
}
return sm_openGLContextGroup.p();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::Camera* caf::Viewer::mainCamera()
{
return m_mainCamera.p();
}
//--------------------------------------------------------------------------------------------------
/// Set the scene to be rendered when the animation is inactive (Stopped)
//--------------------------------------------------------------------------------------------------
void caf::Viewer::setMainScene(cvf::Scene* scene)
{
appendAllStaticModelsToFrame(scene);
m_mainScene = scene;
m_mainRendering->setScene(scene);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::Scene* caf::Viewer::mainScene()
{
return m_mainScene.p();
}
//--------------------------------------------------------------------------------------------------
/// Return the currently rendered scene
//--------------------------------------------------------------------------------------------------
cvf::Scene* caf::Viewer::currentScene()
{
return m_renderingSequence->firstRendering()->scene();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::updateCamera(int width, int height)
{
if (width < 1 || height < 1) return;
m_mainCamera->viewport()->set(0, 0, width, height);
if (m_mainCamera->projection() == cvf::Camera::PERSPECTIVE)
{
m_mainCamera->setProjectionAsPerspective(m_cameraFieldOfViewYDeg, m_mainCamera->nearPlane(), m_mainCamera->farPlane());
}
else
{
m_mainCamera->setProjectionAsOrtho(m_mainCamera->frontPlaneFrustumHeight(), m_mainCamera->nearPlane(), m_mainCamera->farPlane());
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool caf::Viewer::canRender() const
{
if (m_renderingSequence->renderingCount() < 1) return false;
if (m_mainCamera.isNull()) return false;
if (m_mainCamera->viewport()->width() < 1) return false;
if (m_mainCamera->viewport()->height() < 1) return false;
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::optimizeClippingPlanes()
{
cvf::BoundingBox bb = m_renderingSequence->boundingBox();
if (!bb.isValid()) return;
cvf::Vec3d eye = m_mainCamera->position();
cvf::Vec3d viewdir = m_mainCamera->direction();
cvf::Vec3d bboxCorners[8];
bb.cornerVertices(bboxCorners);
// Find the distance to the bbox corners most behind and most in front of camera
double maxDistEyeToCornerAlongViewDir = -HUGE_VAL;
double minDistEyeToCornerAlongViewDir = HUGE_VAL;
for (int bcIdx = 0; bcIdx < 8; ++bcIdx )
{
double distEyeBoxCornerAlongViewDir = (bboxCorners[bcIdx] - eye)*viewdir;
if (distEyeBoxCornerAlongViewDir > maxDistEyeToCornerAlongViewDir)
{
maxDistEyeToCornerAlongViewDir = distEyeBoxCornerAlongViewDir;
}
if (distEyeBoxCornerAlongViewDir < minDistEyeToCornerAlongViewDir)
{
minDistEyeToCornerAlongViewDir = distEyeBoxCornerAlongViewDir; // Sometimes negative-> behind camera
}
}
double farPlaneDist = CVF_MIN(maxDistEyeToCornerAlongViewDir * 1.2, m_maxFarPlaneDistance);
// Near-plane:
bool isOrthoNearPlaneFollowingCamera = false;
double nearPlaneDist = HUGE_VAL;
// If we have perspective projection, set the near plane just in front of camera, and not behind
if (m_mainCamera->projection() == cvf::Camera::PERSPECTIVE || isOrthoNearPlaneFollowingCamera)
{
nearPlaneDist = CVF_MAX( m_minNearPlaneDistance, minDistEyeToCornerAlongViewDir);
if (m_navigationPolicy.notNull() && m_navigationPolicyEnabled)
{
double pointOfInterestDist = (eye - m_navigationPolicy->pointOfInterest()).length();
nearPlaneDist = CVF_MIN(nearPlaneDist, pointOfInterestDist*0.2);
}
}
else // Orthographic projection. Set to encapsulate the complete boundingbox, possibly setting a negative nearplane
{
if(minDistEyeToCornerAlongViewDir >= 0)
{
nearPlaneDist = CVF_MIN(0.8 * minDistEyeToCornerAlongViewDir, m_maxFarPlaneDistance);
}
else
{
nearPlaneDist = CVF_MAX(1.2 * minDistEyeToCornerAlongViewDir, -m_maxFarPlaneDistance);
}
}
#if 0
double distEyeBoxCenterAlongViewDir = (bb.center() - eye)*viewdir;
double farPlaneDist = distEyeBoxCenterAlongViewDir + bb.radius() * 1.2;
farPlaneDist = CVF_MIN(farPlaneDist, m_maxFarPlaneDistance);
double nearPlaneDist = distEyeBoxCenterAlongViewDir - bb.radius();
if (nearPlaneDist < m_minNearPlaneDistance) nearPlaneDist = m_minNearPlaneDistance;
if (m_navigationPolicy.notNull() && m_navigationPolicyEnabled)
{
double pointOfInterestDist = (eye - m_navigationPolicy->pointOfInterest()).length();
nearPlaneDist = CVF_MIN(nearPlaneDist, pointOfInterestDist*0.2);
}
#endif
if (farPlaneDist <= nearPlaneDist) farPlaneDist = nearPlaneDist + 1.0;
if (m_mainCamera->projection() == cvf::Camera::PERSPECTIVE)
{
m_mainCamera->setProjectionAsPerspective(m_cameraFieldOfViewYDeg, nearPlaneDist, farPlaneDist);
}
else
{
m_mainCamera->setProjectionAsOrtho(m_mainCamera->frontPlaneFrustumHeight(), nearPlaneDist, farPlaneDist);
}
}
//--------------------------------------------------------------------------------------------------
/// Forward all events classified as QInputEvent to the navigation policy
//--------------------------------------------------------------------------------------------------
bool caf::Viewer::event(QEvent* e)
{
if (e && m_navigationPolicy.notNull() && m_navigationPolicyEnabled)
{
switch (e->type())
{
case QEvent::ContextMenu:
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::ShortcutOverride:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
case QEvent::TabletMove:
case QEvent::TabletPress:
case QEvent::TabletRelease:
case QEvent::TabletEnterProximity:
case QEvent::TabletLeaveProximity:
case QEvent::Wheel:
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
if (m_navigationPolicy->handleInputEvent(static_cast<QInputEvent*>(e)))
return true;
else return QGLWidget::event(e);
break;
default:
return QGLWidget::event(e);
break;
}
}
else return QGLWidget::event(e);
}
//--------------------------------------------------------------------------------------------------
/// Set the pointer to the navigation policy to be used. Stored as a cvf::ref internally
//--------------------------------------------------------------------------------------------------
void caf::Viewer::setNavigationPolicy(caf::NavigationPolicy* navigationPolicy)
{
m_navigationPolicy = navigationPolicy;
if (m_navigationPolicy.notNull()) m_navigationPolicy->setViewer(this);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool caf::Viewer::rayPick(int winPosX, int winPosY, cvf::HitItemCollection* pickedPoints)
{
CVF_ASSERT(m_mainRendering.notNull());
int translatedMousePosX = winPosX;
int translatedMousePosY = height() - winPosY;
cvf::ref<cvf::RayIntersectSpec> ris = m_mainRendering->rayIntersectSpecFromWindowCoordinates(translatedMousePosX, translatedMousePosY);
if (ris.notNull())
{
bool retVal = m_mainRendering->rayIntersect(*ris, pickedPoints);
return retVal;
}
else
{
return false;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::resizeGL(int width, int height)
{
if (width < 1 || height < 1) return;
updateCamera(width, height);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::enablePerfInfoHud(bool enable)
{
m_showPerfInfoHud = enable;
updateOverlayImagePresence();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool caf::Viewer::isPerfInfoHudEnabled()
{
return m_showPerfInfoHud;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::paintEvent(QPaintEvent* event)
{
makeCurrent();
cvf::ref<cvf::OpenGLContext> myOglContext = cvfOpenGLContext();
CVF_CHECK_OGL(myOglContext.p());
CVF_ASSERT(myOglContext->isContextValid());
QPainter painter(this);
if (m_renderingSequence.isNull() || !canRender())
{
QColor bgClr(128, 128, 128);
painter.fillRect(rect(), bgClr);
return;
}
// If Qt overlay painting is enabled, paint to an QImage, and set it to the cvf::OverlayImage
if (m_isOverlayPaintingEnabled || m_showPerfInfoHud)
{
// Set up image to draw to, and painter
if (m_overlayPaintingQImage.size() != this->size())
{
m_overlayPaintingQImage = QImage(this->size(), QImage::Format_ARGB32);
}
m_overlayPaintingQImage.fill(Qt::transparent);
QPainter overlyPainter(&m_overlayPaintingQImage);
// Call virtual method to allow subclasses to paint on the OpenGlCanvas
if (m_isOverlayPaintingEnabled)
{
this->paintOverlayItems(&overlyPainter);
}
// Draw performance overlay
if (m_showPerfInfoHud )
{
cvfqt::PerformanceInfoHud hud;
hud.addStrings(m_renderingSequence->performanceInfo());
hud.addStrings(*m_mainCamera);
hud.addString(QString("PaintCount: %1").arg(m_paintCounter++));
hud.draw(&overlyPainter, width(), height());
}
// Convert the QImage into the cvf::TextureImage,
// handling vertical mirroring and (possible) byteswapping
if (((int)m_overlayTextureImage->height()) != this->height() || ((int)m_overlayTextureImage->width() != this->width()))
{
m_overlayTextureImage->allocate(this->width(), this->height());
}
cvfqt::Utils::toTextureImage(m_overlayPaintingQImage, m_overlayTextureImage.p());
m_overlayImage->setImage(m_overlayTextureImage.p());
m_overlayImage->setPixelSize(cvf::Vec2ui(this->width(), this->height()));
}
#if QT_VERSION >= 0x040600
// Qt 4.6
painter.beginNativePainting();
#endif
if (isShadersSupported())
{
cvfqt::OpenGLContext::saveOpenGLState(myOglContext.p());
}
optimizeClippingPlanes();
// Do normal drawing
m_renderingSequence->render(myOglContext.p());
CVF_CHECK_OGL(cvfOpenGLContext());
if (isShadersSupported())
{
cvfqt::OpenGLContext::restoreOpenGLState(myOglContext.p());
}
#if QT_VERSION >= 0x040600
// Qt 4.6
painter.endNativePainting();
#endif
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::setMinNearPlaneDistance(double dist)
{
m_minNearPlaneDistance = dist;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::setMaxFarPlaneDistance(double dist)
{
m_maxFarPlaneDistance = dist;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::setView(const cvf::Vec3d& alongDirection, const cvf::Vec3d& upDirection)
{
if (m_navigationPolicy.notNull() && m_navigationPolicyEnabled)
{
m_navigationPolicy->setView(alongDirection, upDirection);
navigationPolicyUpdate();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::zoomAll()
{
cvf::BoundingBox bb = m_renderingSequence->boundingBox();
if (!bb.isValid())
{
return;
}
cvf::Vec3d eye, vrp, up;
m_mainCamera->toLookAt(&eye, &vrp, &up);
cvf::Vec3d newEye = m_mainCamera->computeFitViewEyePosition(bb, vrp-eye, up, 0.9, m_cameraFieldOfViewYDeg, m_mainCamera->viewport()->aspectRatio());
m_mainCamera->setFromLookAt(newEye, bb.center(), up);
updateParallelProjectionHeightFromMoveZoom(bb.center());
navigationPolicyUpdate();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::addFrame(cvf::Scene* scene)
{
appendAllStaticModelsToFrame(scene);
m_frameScenes.push_back(scene);
m_animationControl->setNumFrames(static_cast<int>(m_frameScenes.size()));
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::removeAllFrames()
{
m_frameScenes.clear();
m_animationControl->setNumFrames(0);
m_renderingSequence->firstRendering()->setScene(m_mainScene.p());
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool caf::Viewer::isAnimationActive()
{
cvf::Scene* currentScene = m_renderingSequence->firstRendering()->scene();
if (!currentScene)
{
return false;
}
if (m_mainScene.notNull() && m_mainScene.p() == currentScene)
{
return false;
}
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::slotSetCurrentFrame(int frameIndex)
{
if (m_frameScenes.size() == 0) return;
int clampedFrameIndex = clampFrameIndex(frameIndex);
if (m_frameScenes.at(clampedFrameIndex) == NULL) return;
if (m_releaseOGLResourcesEachFrame)
{
releaseOGlResourcesForCurrentFrame();
}
m_renderingSequence->firstRendering()->setScene(m_frameScenes.at(clampedFrameIndex));
update();
}
void caf::Viewer::releaseOGlResourcesForCurrentFrame()
{
if (isAnimationActive())
{
cvf::Scene* currentScene = m_renderingSequence->firstRendering()->scene();
makeCurrent();
cvf::uint modelCount = currentScene->modelCount();
for (cvf::uint i = 0; i < modelCount; ++i)
{
cvf::Collection<cvf::Part> partCollection;
currentScene->model(i)->allParts(&partCollection);
for (size_t pIdx = 0; pIdx < partCollection.size(); ++pIdx)
{
if (partCollection[pIdx].notNull() && partCollection[pIdx]->drawable())
{
partCollection[pIdx]->drawable()->releaseBufferObjectsGPU();
}
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::slotEndAnimation()
{
if(m_releaseOGLResourcesEachFrame)
{
releaseOGlResourcesForCurrentFrame();
}
m_renderingSequence->firstRendering()->setScene(m_mainScene.p());
update();
}
//--------------------------------------------------------------------------------------------------
/// This only updates the boundingboxes yet. Might want to do other things as well
//--------------------------------------------------------------------------------------------------
void caf::Viewer::updateCachedValuesInScene()
{
if (m_mainScene.notNull())
{
cvf::uint midx;
for (midx = 0; midx < m_mainScene->modelCount() ; ++midx)
{
m_mainScene->model(midx)->updateBoundingBoxesRecursive();
}
}
size_t sIdx;
for (sIdx = 0; sIdx < m_frameScenes.size(); ++sIdx)
{
cvf::uint midx;
for (midx = 0; midx < m_frameScenes[sIdx]->modelCount() ; ++midx)
{
m_frameScenes[sIdx]->model(midx)->updateBoundingBoxesRecursive();
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool caf::Viewer::isShadersSupported()
{
QGLFormat::OpenGLVersionFlags flags = QGLFormat::openGLVersionFlags();
bool hasOpenGL_2_0 = QGLFormat::OpenGL_Version_2_0 & flags;
if (hasOpenGL_2_0)
{
return true;
}
return false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::Scene* caf::Viewer::frame(size_t frameIndex)
{
if (frameIndex < m_frameScenes.size())
return m_frameScenes[frameIndex].p();
else
return NULL;
}
//--------------------------------------------------------------------------------------------------
/// Helper function used to write out the name of all parts in a rendering sequence
//--------------------------------------------------------------------------------------------------
void caf::Viewer::debugShowRenderingSequencePartNames()
{
qDebug() << "\n\n";
size_t globalPartCount = 0;
cvf::uint rIdx = m_renderingSequence->renderingCount();
for (rIdx = 0; rIdx < m_renderingSequence->renderingCount(); rIdx++)
{
cvf::Rendering* rendering = m_renderingSequence->rendering(rIdx);
if (rendering && rendering->scene())
{
cvf::uint mIdx;
for (mIdx = 0; mIdx < rendering->scene()->modelCount(); mIdx++)
{
cvf::Model* model = rendering->scene()->model(mIdx);
if (model)
{
cvf::Collection<cvf::Part> parts;
model->allParts(&parts);
size_t pIdx;
for (pIdx = 0; pIdx < parts.size(); pIdx++)
{
cvf::Part* part = parts.at(pIdx);
qDebug() << QString("%1").arg(globalPartCount++) << cvfqt::Utils::toQString(part->name());
}
}
}
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::enableNavigationPolicy(bool enable)
{
m_navigationPolicyEnabled = enable;
if (enable && m_navigationPolicy.notNull()) m_navigationPolicy->init();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QSize caf::Viewer::sizeHint() const
{
return QSize(500, 400);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::enableForcedImmediateMode(bool enable)
{
cvf::uint rIdx = m_renderingSequence->renderingCount();
for (rIdx = 0; rIdx < m_renderingSequence->renderingCount(); rIdx++)
{
cvf::Rendering* rendering = m_renderingSequence->rendering(rIdx);
if (rendering && rendering->scene())
{
rendering->renderEngine()->enableForcedImmediateMode(enable);
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int caf::Viewer::currentFrameIndex() const
{
if (m_animationControl)
{
int clampedFrameIndex = clampFrameIndex(m_animationControl->currentFrame());
return clampedFrameIndex;
}
else return 0;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool caf::Viewer::isOverlyPaintingEnabled() const
{
return m_isOverlayPaintingEnabled;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::enableOverlyPainting(bool val)
{
m_isOverlayPaintingEnabled = val;
updateOverlayImagePresence();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::updateOverlayImagePresence()
{
if (m_isOverlayPaintingEnabled || m_showPerfInfoHud)
{
m_mainRendering->addOverlayItem(m_overlayImage.p());
}
else
{
m_mainRendering->removeOverlayItem(m_overlayImage.p());
}
}
//--------------------------------------------------------------------------------------------------
/// Create a virtual method so it is possible to override this function in derived classes
//--------------------------------------------------------------------------------------------------
void caf::Viewer::navigationPolicyUpdate()
{
update();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::addStaticModelOnce(cvf::Model* model)
{
if (m_staticModels.contains(model)) return;
m_staticModels.push_back(model);
appendModelToAllFrames(model);
updateCachedValuesInScene();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::removeStaticModel(cvf::Model* model)
{
removeModelFromAllFrames(model);
m_staticModels.erase(model);
updateCachedValuesInScene();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::removeAllStaticModels()
{
for (size_t i = 0; i < m_staticModels.size(); i++)
{
removeModelFromAllFrames(m_staticModels.at(i));
}
m_staticModels.clear();
updateCachedValuesInScene();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::removeModelFromAllFrames(cvf::Model* model)
{
for (size_t i = 0; i < m_frameScenes.size(); i++)
{
cvf::Scene* scene = m_frameScenes.at(i);
scene->removeModel(model);
}
if (m_mainScene.notNull())
{
m_mainScene->removeModel(model);
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::appendModelToAllFrames(cvf::Model* model)
{
for (size_t i = 0; i < m_frameScenes.size(); i++)
{
cvf::Scene* scene = m_frameScenes.at(i);
scene->addModel(model);
}
if (m_mainScene.notNull())
{
m_mainScene->addModel(model);
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::appendAllStaticModelsToFrame(cvf::Scene* scene)
{
for (size_t i = 0; i < m_staticModels.size(); i++)
{
scene->addModel(m_staticModels.at(i));
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::OverlayItem* caf::Viewer::overlayItem(int winPosX, int winPosY)
{
if (m_mainRendering.isNull()) return NULL;
int translatedMousePosX = winPosX;
int translatedMousePosY = height() - winPosY;
return m_mainRendering->overlayItemFromWindowCoordinates(translatedMousePosX, translatedMousePosY);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void caf::Viewer::enableParallelProjection(bool enableOrtho)
{
if (enableOrtho && m_mainCamera->projection() == cvf::Camera::PERSPECTIVE)
{
cvf::Vec3d pointOfInterest;
if (m_navigationPolicy.isNull() || !m_navigationPolicyEnabled)
{
using namespace cvf;
Vec3d eye, vrp, up;
m_mainCamera->toLookAt(&eye, &vrp, &up);
Vec3d eyeToFocus = pointOfInterest - eye;
Vec3d camDir = vrp - eye;
camDir.normalize();
double distToFocusPlane = 0.5*(m_mainCamera->farPlane() - m_mainCamera->nearPlane());
pointOfInterest = camDir *distToFocusPlane;
}
else
{
pointOfInterest = m_navigationPolicy->pointOfInterest();
}
m_mainCamera->setProjectionAsOrtho(1.0, m_mainCamera->nearPlane(), m_mainCamera->farPlane());
this->updateParallelProjectionHeightFromMoveZoom(pointOfInterest);
// Set the light position behind us, far away from the scene
float sceneDepth = m_mainCamera->farPlane() - m_mainCamera->nearPlane();
this->m_renderingSequence->setDefaultFFLightPositional(cvf::Vec3f(0,0, 2 * sceneDepth));
m_globalUniformSet->setHeadLightPosition(cvf::Vec3f(0,0, 2 * sceneDepth));
this->update();
}
else if (!enableOrtho && m_mainCamera->projection() == cvf::Camera::ORTHO)
{
// We currently expect all the navigation policies to do walk-based navigation and not fiddle with the field of view
// so we do not need to update the camera position based on orthoHeight and fieldOfView.
// We assume the camera is in a sensible position.
// Set a dummy near plane to be > 0 and < farPlane. These wll be updated by the optimize clipping planes
double dummyNearPlane = m_mainCamera->farPlane() *0.1;
m_mainCamera->setProjectionAsPerspective(m_cameraFieldOfViewYDeg, dummyNearPlane, m_mainCamera->farPlane());
this->m_renderingSequence->setDefaultFFLightPositional(cvf::Vec3f(0.5, 5.0, 7.0));
m_globalUniformSet->setHeadLightPosition(cvf::Vec3f(0.5, 5.0, 7.0));
this->update();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double calculateOrthoHeight(double perspectiveViewAngleYDeg, double focusPlaneDist)
{
return 2 * (cvf::Math::tan( cvf::Math::toRadians(0.5 * perspectiveViewAngleYDeg) ) * focusPlaneDist);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double calculateDistToPlaneOfInterest(double perspectiveViewAngleYDeg, double orthoHeight)
{
return orthoHeight / (2 * (cvf::Math::tan( cvf::Math::toRadians(0.5 * perspectiveViewAngleYDeg) )));
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double distToPlaneOfInterest(const cvf::Camera* camera, const cvf::Vec3d& pointOfInterest)
{
using namespace cvf;
CVF_ASSERT(camera);
Vec3d eye, vrp, up;
camera->toLookAt(&eye, &vrp, &up);
Vec3d camDir = vrp - eye;
camDir.normalize();
Vec3d eyeToFocus = pointOfInterest - eye;
double distToFocusPlane = eyeToFocus*camDir;
return cvf::Math::abs(distToFocusPlane);
}
//--------------------------------------------------------------------------------------------------
/// Update the ortho projection view height from a walk based camera manipulation.
/// Using pointOfInterest, the perspective Y-field Of View along with the camera position
//--------------------------------------------------------------------------------------------------
void caf::Viewer::updateParallelProjectionHeightFromMoveZoom(const cvf::Vec3d& pointOfInterest)
{
using namespace cvf;
cvf::Camera* camera = m_mainCamera.p();
if (!camera || camera->projection() != Camera::ORTHO) return;
double distToFocusPlane = distToPlaneOfInterest(camera, pointOfInterest);
double orthoHeight = calculateOrthoHeight(m_cameraFieldOfViewYDeg, distToFocusPlane);
camera->setProjectionAsOrtho(orthoHeight, camera->nearPlane(), camera->farPlane());
}
//--------------------------------------------------------------------------------------------------
/// Update the camera eye position from point of interest, keeping the ortho height fixed and in sync
/// with distToPlaneOfInterest from a walk based camera manipulation in ortho projection.
//--------------------------------------------------------------------------------------------------
void caf::Viewer::updateParallelProjectionCameraPosFromPointOfInterestMove(const cvf::Vec3d& pointOfInterest)
{
using namespace cvf;
cvf::Camera* camera = m_mainCamera.p();
if (!camera || camera->projection() != Camera::ORTHO) return;
double orthoHeight = camera->frontPlaneFrustumHeight();
//Trace::show(String::number(orthoHeight));
double neededDistToFocusPlane = calculateDistToPlaneOfInterest(m_cameraFieldOfViewYDeg, orthoHeight);
Vec3d eye, vrp, up;
camera->toLookAt(&eye, &vrp, &up);
Vec3d camDir = vrp - eye;
camDir.normalize();
double existingDistToFocusPlane = distToPlaneOfInterest(camera, pointOfInterest);
Vec3d newEye = eye + (existingDistToFocusPlane - neededDistToFocusPlane) * camDir;
//Trace::show(String::number(newEye.x()) + ", " + String::number(newEye.y()) + ", " +String::number(newEye.z()));
camera->setFromLookAt(newEye, newEye + 10.0*camDir, up);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int caf::Viewer::clampFrameIndex(int frameIndex) const
{
size_t clampedFrameIndex = static_cast<size_t>(frameIndex);
if (clampedFrameIndex >= frameCount())
{
clampedFrameIndex = frameCount() - 1;
}
else if (clampedFrameIndex < 0)
{
clampedFrameIndex = 0;
}
return static_cast<int>(clampedFrameIndex);
}