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/nmVTKRotationCenterMarkerLa...

531 lines
17 KiB
C++

#include "nmVTKRotationCenterMarkerLayer.h"
#include <QCoreApplication>
#include <QThread>
#include <vtkActor.h>
#include <vtkCallbackCommand.h>
#include <vtkCamera.h>
#include <vtkCellArray.h>
#include <vtkCellData.h>
#include <vtkCommand.h>
#include <vtkMath.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderer.h>
#include <vtkUnsignedCharArray.h>
#include <vtkWeakPointer.h>
#include <cmath>
#include <limits>
namespace {
const double ROTATION_CENTER_HALF_LENGTH_PIXELS = 18.0;
const double ROTATION_CENTER_DEPTH_OFFSET_PIXELS = 1.5;
const double ROTATION_CENTER_HOMOGENEOUS_EPSILON =
std::numeric_limits<double>::epsilon();
bool isRotationCenterMarkerGuiThread()
{
QCoreApplication* pApplication = QCoreApplication::instance();
return pApplication == nullptr ||
pApplication->thread() == QThread::currentThread();
}
bool isFinitePoint3(const double dPoint[3])
{
return dPoint != nullptr &&
vtkMath::IsFinite(dPoint[0]) &&
vtkMath::IsFinite(dPoint[1]) &&
vtkMath::IsFinite(dPoint[2]);
}
bool worldToDisplay(vtkRenderer* pRenderer,
const double dWorld[3],
double dDisplay[3])
{
if(pRenderer == nullptr || dWorld == nullptr || dDisplay == nullptr) {
return false;
}
pRenderer->SetWorldPoint(dWorld[0], dWorld[1], dWorld[2], 1.0);
pRenderer->WorldToDisplay();
const double* pDisplay = pRenderer->GetDisplayPoint();
if(!isFinitePoint3(pDisplay)) {
return false;
}
dDisplay[0] = pDisplay[0];
dDisplay[1] = pDisplay[1];
dDisplay[2] = pDisplay[2];
return true;
}
bool displayToWorld(vtkRenderer* pRenderer,
const double dDisplay[3],
double dWorld[3])
{
if(pRenderer == nullptr || dDisplay == nullptr || dWorld == nullptr ||
!isFinitePoint3(dDisplay)) {
return false;
}
pRenderer->SetDisplayPoint(
dDisplay[0], dDisplay[1], dDisplay[2]);
pRenderer->DisplayToWorld();
const double* pHomogeneousWorld = pRenderer->GetWorldPoint();
if(pHomogeneousWorld == nullptr ||
!vtkMath::IsFinite(pHomogeneousWorld[0]) ||
!vtkMath::IsFinite(pHomogeneousWorld[1]) ||
!vtkMath::IsFinite(pHomogeneousWorld[2]) ||
!vtkMath::IsFinite(pHomogeneousWorld[3]) ||
std::fabs(pHomogeneousWorld[3]) <=
ROTATION_CENTER_HOMOGENEOUS_EPSILON) {
return false;
}
const double dInverseW = 1.0 / pHomogeneousWorld[3];
dWorld[0] = pHomogeneousWorld[0] * dInverseW;
dWorld[1] = pHomogeneousWorld[1] * dInverseW;
dWorld[2] = pHomogeneousWorld[2] * dInverseW;
return isFinitePoint3(dWorld);
}
}
class nmVTKRotationCenterMarkerLayerPrivate
{
public:
nmVTKRotationCenterMarkerLayerPrivate()
: m_pPoints(vtkSmartPointer<vtkPoints>::New()),
m_pLines(vtkSmartPointer<vtkCellArray>::New()),
m_pColors(vtkSmartPointer<vtkUnsignedCharArray>::New()),
m_pPolyData(vtkSmartPointer<vtkPolyData>::New()),
m_pMapper(vtkSmartPointer<vtkPolyDataMapper>::New()),
m_pActor(vtkSmartPointer<vtkActor>::New()),
m_pRendererCallback(vtkSmartPointer<vtkCallbackCommand>::New()),
m_pCameraCallback(vtkSmartPointer<vtkCallbackCommand>::New()),
m_pRenderer(nullptr),
m_pCamera(nullptr),
m_nRendererObserverTag(0),
m_nCameraObserverTag(0),
m_bHasWorldCenter(false)
{
// 必须通过 vtkActor::New() 的对象工厂路径获得实际 OpenGL Actor。
initializeGeometry();
m_dWorldCenter[0] = 0.0;
m_dWorldCenter[1] = 0.0;
m_dWorldCenter[2] = 0.0;
m_pRendererCallback->SetClientData(this);
m_pRendererCallback->SetCallback(
nmVTKRotationCenterMarkerLayerPrivate::rendererStarted);
m_pCameraCallback->SetClientData(this);
m_pCameraCallback->SetCallback(
nmVTKRotationCenterMarkerLayerPrivate::cameraModified);
}
~nmVTKRotationCenterMarkerLayerPrivate()
{
// 先移除 Renderer 和 Camera Observer再阻断裸回调目标。
unbindRenderer();
m_pRendererCallback->SetClientData(nullptr);
m_pRendererCallback->SetCallback(nullptr);
m_pCameraCallback->SetClientData(nullptr);
m_pCameraCallback->SetCallback(nullptr);
}
vtkActor* getActor() const
{
return m_pActor;
}
bool bindRenderer(vtkRenderer* pRenderer)
{
if(pRenderer == nullptr) {
return false;
}
if(m_pRenderer.GetPointer() != pRenderer) {
unbindRenderer();
m_pRenderer = pRenderer;
}
if(!ensureRendererObserver(pRenderer) ||
!ensureCameraObserver(pRenderer->GetActiveCamera())) {
unbindRenderer();
return false;
}
return true;
}
void unbindRenderer()
{
removeCameraObserver();
removeRendererObserver();
m_pRenderer = nullptr;
}
bool synchronize()
{
return synchronizeForRenderer(m_pRenderer.GetPointer());
}
bool synchronizeForRenderer(vtkRenderer* pRenderer)
{
if(pRenderer == nullptr || m_pRenderer.GetPointer() != pRenderer ||
!m_bHasWorldCenter) {
return false;
}
vtkCamera* pCamera = pRenderer->GetActiveCamera();
if(pCamera == nullptr || !ensureCameraObserver(pCamera)) {
return false;
}
int* pViewportSize = pRenderer->GetSize();
if(pViewportSize == nullptr || pViewportSize[0] <= 0 ||
pViewportSize[1] <= 0) {
return false;
}
const double dLogicalCenter[3] = {
m_dWorldCenter[0], m_dWorldCenter[1], m_dWorldCenter[2]
};
double dCameraPosition[3];
pCamera->GetPosition(dCameraPosition);
if(!isFinitePoint3(dLogicalCenter) ||
!isFinitePoint3(dCameraPosition)) {
return false;
}
double dCenterDisplay[3];
if(!worldToDisplay(pRenderer, dLogicalCenter, dCenterDisplay)) {
return false;
}
double dOffsetDisplay[3] = {
dCenterDisplay[0],
dCenterDisplay[1] + ROTATION_CENTER_HALF_LENGTH_PIXELS,
dCenterDisplay[2]
};
double dCenterAtDepth[3];
double dOffsetAtDepth[3];
if(!displayToWorld(pRenderer, dCenterDisplay, dCenterAtDepth) ||
!displayToWorld(pRenderer, dOffsetDisplay, dOffsetAtDepth)) {
return false;
}
const double dScaleX = dOffsetAtDepth[0] - dCenterAtDepth[0];
const double dScaleY = dOffsetAtDepth[1] - dCenterAtDepth[1];
const double dScaleZ = dOffsetAtDepth[2] - dCenterAtDepth[2];
const double dHalfLength = std::sqrt(
dScaleX * dScaleX +
dScaleY * dScaleY +
dScaleZ * dScaleZ);
if(!vtkMath::IsFinite(dHalfLength) || dHalfLength <= 0.0) {
return false;
}
double dTowardCamera[3];
if(pCamera->GetParallelProjection() != 0) {
const double* pDirectionOfProjection =
pCamera->GetDirectionOfProjection();
if(!isFinitePoint3(pDirectionOfProjection)) {
return false;
}
dTowardCamera[0] = -pDirectionOfProjection[0];
dTowardCamera[1] = -pDirectionOfProjection[1];
dTowardCamera[2] = -pDirectionOfProjection[2];
} else {
dTowardCamera[0] = dCameraPosition[0] - dLogicalCenter[0];
dTowardCamera[1] = dCameraPosition[1] - dLogicalCenter[1];
dTowardCamera[2] = dCameraPosition[2] - dLogicalCenter[2];
}
if(vtkMath::Normalize(dTowardCamera) <= 0.0) {
return false;
}
const double dWorldPerPixel =
dHalfLength / ROTATION_CENTER_HALF_LENGTH_PIXELS;
const double dDepthOffset =
dWorldPerPixel * ROTATION_CENTER_DEPTH_OFFSET_PIXELS;
const double dDisplayCenter[3] = {
dLogicalCenter[0] + dTowardCamera[0] * dDepthOffset,
dLogicalCenter[1] + dTowardCamera[1] * dDepthOffset,
dLogicalCenter[2] + dTowardCamera[2] * dDepthOffset
};
const double dAxisPoints[6][3] = {
{ dDisplayCenter[0] - dHalfLength,
dDisplayCenter[1], dDisplayCenter[2] },
{ dDisplayCenter[0] + dHalfLength,
dDisplayCenter[1], dDisplayCenter[2] },
{ dDisplayCenter[0],
dDisplayCenter[1] - dHalfLength, dDisplayCenter[2] },
{ dDisplayCenter[0],
dDisplayCenter[1] + dHalfLength, dDisplayCenter[2] },
{ dDisplayCenter[0], dDisplayCenter[1],
dDisplayCenter[2] - dHalfLength },
{ dDisplayCenter[0], dDisplayCenter[1],
dDisplayCenter[2] + dHalfLength }
};
for(vtkIdType nPoint = 0; nPoint < 6; ++nPoint) {
if(!isFinitePoint3(dAxisPoints[nPoint])) {
return false;
}
}
for(vtkIdType nPoint = 0; nPoint < 6; ++nPoint) {
m_pPoints->SetPoint(nPoint, dAxisPoints[nPoint]);
}
m_pPoints->Modified();
m_pPolyData->Modified();
return true;
}
bool setWorldCenter(double dX, double dY, double dZ)
{
const double dCenter[3] = { dX, dY, dZ };
if(!isFinitePoint3(dCenter)) {
return false;
}
m_dWorldCenter[0] = dX;
m_dWorldCenter[1] = dY;
m_dWorldCenter[2] = dZ;
m_bHasWorldCenter = true;
synchronize();
return true;
}
bool hasWorldCenter() const
{
return m_bHasWorldCenter;
}
private:
static void rendererStarted(vtkObject* pCaller,
unsigned long,
void* pClientData,
void*)
{
nmVTKRotationCenterMarkerLayerPrivate* pThis =
static_cast<nmVTKRotationCenterMarkerLayerPrivate*>(
pClientData);
if(pThis != nullptr) {
// StartEvent 位于 Prop 遍历之前,可使用最终视口尺寸覆盖 Resize 路径。
pThis->synchronizeForRenderer(
vtkRenderer::SafeDownCast(pCaller));
}
}
static void cameraModified(vtkObject*,
unsigned long,
void* pClientData,
void*)
{
nmVTKRotationCenterMarkerLayerPrivate* pThis =
static_cast<nmVTKRotationCenterMarkerLayerPrivate*>(
pClientData);
if(pThis != nullptr) {
// 相机回调只同步几何,最终 Render 仍由交互器或容器统一提交。
pThis->synchronize();
}
}
void initializeGeometry()
{
m_pPoints->SetDataTypeToDouble();
m_pPoints->SetNumberOfPoints(6);
for(vtkIdType nPoint = 0; nPoint < 6; ++nPoint) {
m_pPoints->SetPoint(nPoint, 0.0, 0.0, 0.0);
}
for(vtkIdType nAxis = 0; nAxis < 3; ++nAxis) {
vtkIdType nPointIds[2] = {
nAxis * 2, nAxis * 2 + 1
};
m_pLines->InsertNextCell(2, nPointIds);
}
m_pColors->SetName("RotationCenterAxisColors");
m_pColors->SetNumberOfComponents(3);
m_pColors->InsertNextTuple3(255.0, 0.0, 0.0);
m_pColors->InsertNextTuple3(254.0, 255.0, 0.0);
m_pColors->InsertNextTuple3(0.0, 255.0, 0.0);
m_pPolyData->SetPoints(m_pPoints);
m_pPolyData->SetLines(m_pLines);
m_pPolyData->GetCellData()->SetScalars(m_pColors);
m_pMapper->SetInputData(m_pPolyData);
m_pMapper->ScalarVisibilityOn();
m_pMapper->SetScalarModeToUseCellData();
m_pMapper->SetColorModeToDirectScalars();
m_pActor->SetMapper(m_pMapper);
m_pActor->GetProperty()->SetLineWidth(1.0f);
m_pActor->GetProperty()->SetOpacity(1.0);
m_pActor->GetProperty()->LightingOff();
m_pActor->PickableOff();
m_pActor->UseBoundsOff();
}
bool ensureRendererObserver(vtkRenderer* pRenderer)
{
if(pRenderer == nullptr || m_pRendererCallback == nullptr) {
return false;
}
if(m_pRenderer.GetPointer() == pRenderer &&
m_nRendererObserverTag != 0) {
return true;
}
removeRendererObserver();
m_nRendererObserverTag = pRenderer->AddObserver(
vtkCommand::StartEvent, m_pRendererCallback);
return m_nRendererObserverTag != 0;
}
void removeRendererObserver()
{
vtkRenderer* pRenderer = m_pRenderer.GetPointer();
if(pRenderer != nullptr && m_nRendererObserverTag != 0) {
pRenderer->RemoveObserver(m_nRendererObserverTag);
}
m_nRendererObserverTag = 0;
}
bool ensureCameraObserver(vtkCamera* pCamera)
{
if(pCamera == nullptr || m_pCameraCallback == nullptr) {
return false;
}
if(m_pCamera.GetPointer() == pCamera &&
m_nCameraObserverTag != 0) {
return true;
}
removeCameraObserver();
m_pCamera = pCamera;
m_nCameraObserverTag = pCamera->AddObserver(
vtkCommand::ModifiedEvent, m_pCameraCallback);
if(m_nCameraObserverTag == 0) {
m_pCamera = nullptr;
return false;
}
return true;
}
void removeCameraObserver()
{
vtkCamera* pCamera = m_pCamera.GetPointer();
if(pCamera != nullptr && m_nCameraObserverTag != 0) {
pCamera->RemoveObserver(m_nCameraObserverTag);
}
m_nCameraObserverTag = 0;
m_pCamera = nullptr;
}
vtkSmartPointer<vtkPoints> m_pPoints;
vtkSmartPointer<vtkCellArray> m_pLines;
vtkSmartPointer<vtkUnsignedCharArray> m_pColors;
vtkSmartPointer<vtkPolyData> m_pPolyData;
vtkSmartPointer<vtkPolyDataMapper> m_pMapper;
vtkSmartPointer<vtkActor> m_pActor;
vtkSmartPointer<vtkCallbackCommand> m_pRendererCallback;
vtkSmartPointer<vtkCallbackCommand> m_pCameraCallback;
vtkWeakPointer<vtkRenderer> m_pRenderer; ///< 非所有权引用。
vtkWeakPointer<vtkCamera> m_pCamera; ///< 非所有权引用。
unsigned long m_nRendererObserverTag; ///< 为零表示尚未注册观察者。
unsigned long m_nCameraObserverTag; ///< 为零表示尚未注册观察者。
double m_dWorldCenter[3]; ///< 独立于相机焦点的逻辑中心。
bool m_bHasWorldCenter;
};
nmVTKRotationCenterMarkerLayer::nmVTKRotationCenterMarkerLayer(
const QString& sLayerId)
: nmVTKRenderLayer(sLayerId),
m_pPrivate(new nmVTKRotationCenterMarkerLayerPrivate)
{
Q_ASSERT(isRotationCenterMarkerGuiThread());
setRenderProp(m_pPrivate->getActor());
setVisible(false);
}
nmVTKRotationCenterMarkerLayer::~nmVTKRotationCenterMarkerLayer()
{
Q_ASSERT(isRotationCenterMarkerGuiThread());
if(m_pPrivate != nullptr) {
// Renderer/Camera Observer 必须在 Renderer 和回调目标释放前解除。
m_pPrivate->unbindRenderer();
detach();
delete m_pPrivate;
m_pPrivate = nullptr;
}
}
bool nmVTKRotationCenterMarkerLayer::bindRenderer(vtkRenderer* pRenderer)
{
Q_ASSERT(isRotationCenterMarkerGuiThread());
if(!isRotationCenterMarkerGuiThread() || m_pPrivate == nullptr ||
pRenderer == nullptr || getAttachedRenderer() != pRenderer) {
return false;
}
return m_pPrivate->bindRenderer(pRenderer);
}
void nmVTKRotationCenterMarkerLayer::unbindRenderer()
{
Q_ASSERT(isRotationCenterMarkerGuiThread());
if(isRotationCenterMarkerGuiThread() && m_pPrivate != nullptr) {
m_pPrivate->unbindRenderer();
}
}
bool nmVTKRotationCenterMarkerLayer::synchronize()
{
Q_ASSERT(isRotationCenterMarkerGuiThread());
return isRotationCenterMarkerGuiThread() && m_pPrivate != nullptr &&
m_pPrivate->synchronize();
}
bool nmVTKRotationCenterMarkerLayer::setWorldCenter(
double dX,
double dY,
double dZ)
{
Q_ASSERT(isRotationCenterMarkerGuiThread());
return isRotationCenterMarkerGuiThread() && m_pPrivate != nullptr &&
m_pPrivate->setWorldCenter(dX, dY, dZ);
}
bool nmVTKRotationCenterMarkerLayer::hasWorldCenter() const
{
return m_pPrivate != nullptr && m_pPrivate->hasWorldCenter();
}
void nmVTKRotationCenterMarkerLayer::setMarkerVisible(bool bVisible)
{
Q_ASSERT(isRotationCenterMarkerGuiThread());
if(!isRotationCenterMarkerGuiThread()) {
return;
}
const bool bCanShow = bVisible && m_pPrivate != nullptr &&
m_pPrivate->hasWorldCenter();
setVisible(bCanShow);
if(bCanShow) {
// 首次显示先准备几何;视口尚未建立时由绘制前同步再次补齐。
m_pPrivate->synchronize();
}
}
bool nmVTKRotationCenterMarkerLayer::isMarkerVisible() const
{
return isVisible();
}