You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/Src/nmNum/nmRender/nmVTKCameraController.cpp

265 lines
7.4 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "nmVTKCameraController.h"
#include <QCoreApplication>
#include <QThread>
#include <vtkCamera.h>
#include <vtkMath.h>
#include <vtkRenderer.h>
namespace {
// 相机和 Renderer 都是界面对象Release 中也用返回值保护线程边界。
bool isCameraGuiThread()
{
QCoreApplication* pApplication = QCoreApplication::instance();
return pApplication == NULL ||
pApplication->thread() == QThread::currentThread();
}
}
nmVTKCameraController::nmVTKCameraController(vtkRenderer* pRenderer)
: m_pRenderer(pRenderer)
{
Q_ASSERT(isCameraGuiThread());
}
nmVTKCameraController::~nmVTKCameraController()
{
Q_ASSERT(isCameraGuiThread());
// Renderer 为非所有权引用,析构只解除关系,不修改相机或场景。
m_pRenderer = NULL;
}
void nmVTKCameraController::setRenderer(vtkRenderer* pRenderer)
{
Q_ASSERT(isCameraGuiThread());
if(isCameraGuiThread()) {
m_pRenderer = pRenderer;
}
}
vtkRenderer* nmVTKCameraController::getRenderer() const
{
return m_pRenderer;
}
bool nmVTKCameraController::resetCamera()
{
Q_ASSERT(isCameraGuiThread());
double dBounds[6];
if(!isCameraGuiThread() || !getVisibleBounds(dBounds)) {
return false;
}
vtkCamera* pCamera = m_pRenderer->GetActiveCamera();
if(pCamera == NULL) {
return false;
}
// 重置入口恢复默认三维透视投影,再按当前可见边界适配。
pCamera->ParallelProjectionOff();
m_pRenderer->ResetCamera(dBounds);
m_pRenderer->ResetCameraClippingRange(dBounds);
return true;
}
bool nmVTKCameraController::zoomIn()
{
return zoom(1.2);
}
bool nmVTKCameraController::zoomOut()
{
return zoom(1.0 / 1.2);
}
bool nmVTKCameraController::fitView()
{
Q_ASSERT(isCameraGuiThread());
double dBounds[6];
if(!isCameraGuiThread() || !getVisibleBounds(dBounds)) {
return false;
}
vtkCamera* pCamera = m_pRenderer->GetActiveCamera();
if(pCamera == NULL) {
return false;
}
// ResetCamera 只重新适配边界,不改写调用方选定的观察方向和投影方式。
m_pRenderer->ResetCamera(dBounds);
m_pRenderer->ResetCameraClippingRange(dBounds);
return true;
}
bool nmVTKCameraController::setTopView()
{
return applyStandardView(0.0, 0.0, 1.0,
0.0, 1.0, 0.0, true);
}
bool nmVTKCameraController::setFrontView()
{
return applyStandardView(0.0, -1.0, 0.0,
0.0, 0.0, 1.0, true);
}
bool nmVTKCameraController::setRightView()
{
return applyStandardView(1.0, 0.0, 0.0,
0.0, 0.0, 1.0, true);
}
bool nmVTKCameraController::setIsometricView()
{
return applyStandardView(1.0, -1.0, 1.0,
0.0, 0.0, 1.0, false);
}
bool nmVTKCameraController::setRotationCenter(
double dX,
double dY,
double dZ)
{
Q_ASSERT(isCameraGuiThread());
if(!isCameraGuiThread() || m_pRenderer == NULL ||
!vtkMath::IsFinite(dX) || !vtkMath::IsFinite(dY) ||
!vtkMath::IsFinite(dZ)) {
return false;
}
vtkCamera* pCamera = m_pRenderer->GetActiveCamera();
if(pCamera == NULL) {
return false;
}
double dOldFocalPoint[3];
double dOldPosition[3];
pCamera->GetFocalPoint(dOldFocalPoint);
pCamera->GetPosition(dOldPosition);
pCamera->SetFocalPoint(dX, dY, dZ);
// 同步平移相机位置,保持原观察方向和相机到焦点的距离。
pCamera->SetPosition(
dOldPosition[0] + dX - dOldFocalPoint[0],
dOldPosition[1] + dY - dOldFocalPoint[1],
dOldPosition[2] + dZ - dOldFocalPoint[2]);
m_pRenderer->ResetCameraClippingRange();
return true;
}
bool nmVTKCameraController::resetRotationCenter()
{
Q_ASSERT(isCameraGuiThread());
double dBounds[6];
if(!isCameraGuiThread() || !getVisibleBounds(dBounds)) {
return false;
}
// 复用平移逻辑,保持当前观察方向、投影方式和观察距离不变。
return setRotationCenter((dBounds[0] + dBounds[1]) * 0.5,
(dBounds[2] + dBounds[3]) * 0.5,
(dBounds[4] + dBounds[5]) * 0.5);
}
bool nmVTKCameraController::getVisibleBounds(double dBounds[6]) const
{
if(m_pRenderer == NULL || dBounds == NULL) {
return false;
}
// 只使用可见 Prop隐藏图层不会影响标准视角和旋转中心。
m_pRenderer->ComputeVisiblePropBounds(dBounds);
if(!vtkMath::AreBoundsInitialized(dBounds)) {
return false;
}
for(int nIndex = 0; nIndex < 6; ++nIndex) {
if(!vtkMath::IsFinite(dBounds[nIndex])) {
return false;
}
}
return dBounds[0] <= dBounds[1] &&
dBounds[2] <= dBounds[3] &&
dBounds[4] <= dBounds[5];
}
bool nmVTKCameraController::applyStandardView(
double dDirectionX,
double dDirectionY,
double dDirectionZ,
double dViewUpX,
double dViewUpY,
double dViewUpZ,
bool bParallelProjection)
{
Q_ASSERT(isCameraGuiThread());
double dBounds[6];
if(!isCameraGuiThread() || !getVisibleBounds(dBounds)) {
return false;
}
vtkCamera* pCamera = m_pRenderer->GetActiveCamera();
if(pCamera == NULL) {
return false;
}
double dDirection[3] = {
dDirectionX, dDirectionY, dDirectionZ
};
if(vtkMath::Normalize(dDirection) == 0.0) {
return false;
}
const double dCenter[3] = {
(dBounds[0] + dBounds[1]) * 0.5,
(dBounds[2] + dBounds[3]) * 0.5,
(dBounds[4] + dBounds[5]) * 0.5
};
// 先给相机一个与最大跨度成比例的稳定距离,再由 ResetCamera 精确适配。
const double dExtent = qMax(
qMax(dBounds[1] - dBounds[0],
dBounds[3] - dBounds[2]),
dBounds[5] - dBounds[4]);
const double dDistance = dExtent > 0.0 ? dExtent * 2.0 : 1.0;
// 顶、前、右视图分别位于 +Z、-Y、+X 一侧观察包围盒中心。
// 等轴视图使用透视投影,三个标准正视图使用正交投影。
pCamera->SetFocalPoint(dCenter);
pCamera->SetPosition(dCenter[0] + dDirection[0] * dDistance,
dCenter[1] + dDirection[1] * dDistance,
dCenter[2] + dDirection[2] * dDistance);
pCamera->SetViewUp(dViewUpX, dViewUpY, dViewUpZ);
pCamera->SetParallelProjection(bParallelProjection ? 1 : 0);
pCamera->OrthogonalizeViewUp();
m_pRenderer->ResetCamera(dBounds);
m_pRenderer->ResetCameraClippingRange(dBounds);
return true;
}
bool nmVTKCameraController::zoom(double dFactor)
{
Q_ASSERT(isCameraGuiThread());
double dBounds[6];
if(!isCameraGuiThread() || !vtkMath::IsFinite(dFactor) ||
dFactor <= 0.0 || !getVisibleBounds(dBounds)) {
return false;
}
vtkCamera* pCamera = m_pRenderer->GetActiveCamera();
if(pCamera == NULL) {
return false;
}
// 正交投影必须调整 ParallelScaleDolly 只对透视投影产生可见缩放。
if(pCamera->GetParallelProjection() != 0) {
const double dParallelScale = pCamera->GetParallelScale();
if(!vtkMath::IsFinite(dParallelScale) || dParallelScale <= 0.0) {
return false;
}
pCamera->SetParallelScale(dParallelScale / dFactor);
} else {
pCamera->Dolly(dFactor);
}
m_pRenderer->ResetCameraClippingRange(dBounds);
return true;
}