375 lines
12 KiB
C++
375 lines
12 KiB
C++
#include "View3D.h"
|
||
#include <QHBoxLayout>
|
||
#include <QShowEvent>
|
||
#include <QMouseEvent>
|
||
#include <QWheelEvent>
|
||
#include <Qt3DExtras/QForwardRenderer>
|
||
#include <QtMath>
|
||
#include <Qt3DRender/QAttribute>
|
||
#include <QGeometryRenderer>
|
||
|
||
View3DBase::View3DBase(const QString& baseModelPath,
|
||
const QString& armModelPath,
|
||
QWidget* parent)
|
||
: QWidget(parent),
|
||
m_container(nullptr),
|
||
m_baseModelPath(baseModelPath),
|
||
m_armModelPath(armModelPath)
|
||
{
|
||
m_view = new Qt3DExtras::Qt3DWindow();
|
||
// 部分 Qt5.9 构建可能需要强转 frame graph,但 defaultFrameGraph() 通常可用
|
||
QColor c1("#0D1233");
|
||
m_view->defaultFrameGraph()->setClearColor(c1);
|
||
|
||
m_rootEntity = new Qt3DCore::QEntity();
|
||
|
||
initScene();
|
||
initCamera();
|
||
|
||
// 自动旋转臂(如果不需要可注释掉 timer/connect)
|
||
//connect(&m_timer, &QTimer::timeout, this, [=]() {
|
||
// m_angle += 1.0f;
|
||
// m_t += 1.0f;
|
||
// if (m_angle >= 360.0f) m_angle = 0.0f;
|
||
// if (m_armTransform)
|
||
// {
|
||
// //m_armTransform->setRotationX(m_angle);
|
||
// //m_armTransform->setTranslation(QVector3D(m_t, 0, 0));
|
||
//
|
||
// Qt3DCore::QTransform transform;
|
||
// transform.setTranslation(QVector3D(2, 0, 0));
|
||
|
||
// QMatrix4x4 M = m_armTransform->matrix();
|
||
// M = transform.matrix() * M; // 左乘:在世界坐标系叠加
|
||
// m_armTransform->setMatrix(M);
|
||
|
||
// qDebug() << m_armTransform->matrix();
|
||
// }
|
||
// });
|
||
}
|
||
|
||
void View3DBase::setViewCenter(float x, float y, float z)
|
||
{
|
||
m_viewCenter.setX(x);
|
||
m_viewCenter.setY(y);
|
||
m_viewCenter.setZ(z);
|
||
}
|
||
|
||
void View3DBase::setDistance(float distance)
|
||
{
|
||
m_distance = distance;
|
||
}
|
||
|
||
void View3DBase::initScene()
|
||
{
|
||
/*auto* lightEntity = new Qt3DCore::QEntity(m_rootEntity);
|
||
|
||
auto* light = new Qt3DRender::QPointLight(lightEntity);
|
||
light->setColor(Qt::white);
|
||
light->setIntensity(1.2f);
|
||
|
||
auto* lightTransform = new Qt3DCore::QTransform(lightEntity);
|
||
lightTransform->setTranslation(QVector3D(500, 500, 500));
|
||
|
||
lightEntity->addComponent(light);
|
||
lightEntity->addComponent(lightTransform);*/
|
||
|
||
// ===== 创建 base 根节点 =====
|
||
auto* baseModel = new Qt3DCore::QEntity(m_rootEntity);
|
||
auto* baseLoader = new Qt3DRender::QSceneLoader(baseModel);
|
||
baseLoader->setSource(QUrl::fromLocalFile(m_baseModelPath));
|
||
|
||
//connect(baseLoader, &Qt3DRender::QSceneLoader::statusChanged,
|
||
// this, &View3DBase::onSceneLoaderStatusChanged);
|
||
|
||
|
||
m_baseTransform = new Qt3DCore::QTransform();
|
||
m_baseTransform->setTranslation(QVector3D(0, 0, 0));
|
||
baseModel->addComponent(baseLoader);
|
||
baseModel->addComponent(m_baseTransform);
|
||
|
||
connect(baseLoader, &Qt3DRender::QSceneLoader::statusChanged,
|
||
this, [=](Qt3DRender::QSceneLoader::Status status) {
|
||
|
||
if (status == Qt3DRender::QSceneLoader::Ready) {
|
||
applyWhiteMaterialRecursive(baseModel);
|
||
}
|
||
});
|
||
|
||
|
||
// ===== 创建 arm 根节点 =====
|
||
auto* armModel = new Qt3DCore::QEntity(m_rootEntity);
|
||
auto* armLoader = new Qt3DRender::QSceneLoader(armModel);
|
||
armLoader->setSource(QUrl::fromLocalFile(m_armModelPath));
|
||
|
||
m_armTransform = new Qt3DCore::QTransform();
|
||
m_armTransform->setTranslation(QVector3D(0, 0, 0));
|
||
armModel->addComponent(armLoader);
|
||
armModel->addComponent(m_armTransform);
|
||
|
||
connect(armLoader, &Qt3DRender::QSceneLoader::statusChanged,
|
||
this, [=](Qt3DRender::QSceneLoader::Status status) {
|
||
|
||
if (status == Qt3DRender::QSceneLoader::Ready) {
|
||
applyWhiteMaterialRecursive(armModel);
|
||
}
|
||
});
|
||
|
||
// 坐标轴依然挂在 root,不会被移动
|
||
//createAxes();
|
||
|
||
m_view->setRootEntity(m_rootEntity);
|
||
|
||
qDebug() << m_baseTransform->matrix();
|
||
qDebug() << m_armTransform->matrix();
|
||
|
||
//m_armTransform->setTranslation(QVector3D(2000, 0, 0));
|
||
//m_baseTransform->setTranslation(QVector3D(-1000, -1000, -1000));
|
||
|
||
qDebug() << m_baseTransform->matrix();
|
||
qDebug() << m_armTransform->matrix();
|
||
}
|
||
|
||
void View3DBase::applyWhiteMaterialRecursive(Qt3DCore::QEntity* entity)
|
||
{
|
||
// 如果这个 entity 有 mesh,就给它加白色材质
|
||
auto meshes = entity->componentsOfType<Qt3DRender::QGeometryRenderer>();
|
||
if (!meshes.isEmpty()) {
|
||
auto* mat = new Qt3DExtras::QPhongMaterial(entity);
|
||
QColor c1("#cccccc");
|
||
mat->setDiffuse(c1);
|
||
mat->setAmbient(c1);
|
||
mat->setSpecular(c1);
|
||
mat->setShininess(50.0f);
|
||
entity->addComponent(mat);
|
||
}
|
||
|
||
// 递归处理子节点
|
||
const auto children = entity->children();
|
||
for (QObject* obj : children) {
|
||
auto* childEntity = qobject_cast<Qt3DCore::QEntity*>(obj);
|
||
if (childEntity)
|
||
applyWhiteMaterialRecursive(childEntity);
|
||
}
|
||
}
|
||
|
||
void View3DBase::createAxes()
|
||
{
|
||
// 参数
|
||
float axisLength = 500.0f;
|
||
float axisRadius = 50.0f;
|
||
|
||
// ----- X axis (red) -----
|
||
Qt3DCore::QEntity* xAxis = new Qt3DCore::QEntity(m_rootEntity);
|
||
auto* xMesh = new Qt3DExtras::QCylinderMesh();
|
||
xMesh->setRadius(axisRadius);
|
||
xMesh->setLength(axisLength);
|
||
xMesh->setRings(16);
|
||
xMesh->setSlices(16);
|
||
|
||
auto* xTrans = new Qt3DCore::QTransform();
|
||
// cylinder 默认沿 Y 轴,绕 Z 轴 90 度让其沿 X
|
||
xTrans->setRotation(QQuaternion::fromAxisAndAngle(QVector3D(0, 0, 1), 90.0f));
|
||
xTrans->setTranslation(QVector3D(axisLength / 2.0f, 0.0f, 0.0f));
|
||
|
||
auto* xMat = new Qt3DExtras::QPhongMaterial();
|
||
xMat->setDiffuse(QColor(Qt::red));
|
||
|
||
xAxis->addComponent(xMesh);
|
||
xAxis->addComponent(xTrans);
|
||
xAxis->addComponent(xMat);
|
||
|
||
// ----- Y axis (green) -----
|
||
Qt3DCore::QEntity* yAxis = new Qt3DCore::QEntity(m_rootEntity);
|
||
auto* yMesh = new Qt3DExtras::QCylinderMesh();
|
||
yMesh->setRadius(axisRadius);
|
||
yMesh->setLength(axisLength*2);
|
||
yMesh->setRings(16);
|
||
yMesh->setSlices(16);
|
||
|
||
auto* yTrans = new Qt3DCore::QTransform();
|
||
// Y 轴无需旋转(cylinder 默认沿 Y)
|
||
yTrans->setTranslation(QVector3D(0.0f, axisLength / 2.0f, 0.0f));
|
||
|
||
auto* yMat = new Qt3DExtras::QPhongMaterial();
|
||
yMat->setDiffuse(QColor(Qt::green));
|
||
|
||
yAxis->addComponent(yMesh);
|
||
yAxis->addComponent(yTrans);
|
||
yAxis->addComponent(yMat);
|
||
|
||
// ----- Z axis (blue) -----
|
||
Qt3DCore::QEntity* zAxis = new Qt3DCore::QEntity(m_rootEntity);
|
||
auto* zMesh = new Qt3DExtras::QCylinderMesh();
|
||
zMesh->setRadius(axisRadius);
|
||
zMesh->setLength(axisLength*3);
|
||
zMesh->setRings(16);
|
||
zMesh->setSlices(16);
|
||
|
||
auto* zTrans = new Qt3DCore::QTransform();
|
||
// 让 cylinder 沿 Z:绕 X 轴 90 度
|
||
zTrans->setRotation(QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), 90.0f));
|
||
zTrans->setTranslation(QVector3D(0.0f, 0.0f, axisLength / 2.0f));
|
||
|
||
auto* zMat = new Qt3DExtras::QPhongMaterial();
|
||
zMat->setDiffuse(QColor(Qt::blue));
|
||
|
||
zAxis->addComponent(zMesh);
|
||
zAxis->addComponent(zTrans);
|
||
zAxis->addComponent(zMat);
|
||
}
|
||
|
||
void View3DBase::initCamera()
|
||
{
|
||
m_camera = m_view->camera();
|
||
// 16:10 假设窗口比例,后续 resize 时相机透视会保持
|
||
m_camera->lens()->setPerspectiveProjection(50.0f, 16.0f / 10.0f, 0.1f, 10000.0f);
|
||
updateCameraPosition();
|
||
}
|
||
|
||
void View3DBase::updateCameraPosition()
|
||
{
|
||
float yaw = qDegreesToRadians(m_yawDeg);
|
||
float pitch = qDegreesToRadians(m_pitchDeg);
|
||
|
||
float x = m_distance * qCos(pitch) * qSin(yaw);
|
||
float y = m_distance * qSin(pitch);
|
||
float z = m_distance * qCos(pitch) * qCos(yaw);
|
||
|
||
QVector3D camPos = m_viewCenter + QVector3D(x, y, z);
|
||
m_camera->setPosition(camPos);
|
||
m_camera->setViewCenter(m_viewCenter);
|
||
}
|
||
|
||
void View3DBase::showEvent(QShowEvent* event)
|
||
{
|
||
QWidget::showEvent(event);
|
||
|
||
if (!m_container) {
|
||
m_container = QWidget::createWindowContainer(m_view, this);
|
||
m_view->installEventFilter(this);
|
||
|
||
auto* layout = new QHBoxLayout(this);
|
||
layout->addWidget(m_container);
|
||
layout->setMargin(0);
|
||
setLayout(layout);
|
||
|
||
// 启动自动旋转 timer(如果你不需要可以注释)
|
||
m_timer.start(100);
|
||
}
|
||
}
|
||
|
||
bool View3DBase::eventFilter(QObject* obj, QEvent* event)
|
||
{
|
||
if (obj == m_view)
|
||
{
|
||
// 鼠标按下
|
||
if (event->type() == QEvent::MouseButtonPress) {
|
||
auto* e = static_cast<QMouseEvent*>(event);
|
||
if (e->button() == Qt::LeftButton) {
|
||
m_mouseDragging = true;
|
||
m_lastMousePos = e->pos();
|
||
}
|
||
else if (e->button() == Qt::MiddleButton) {
|
||
m_middleDragging = true;
|
||
m_lastMousePos = e->pos();
|
||
}
|
||
}
|
||
// 鼠标释放
|
||
else if (event->type() == QEvent::MouseButtonRelease) {
|
||
auto* e = static_cast<QMouseEvent*>(event);
|
||
if (e->button() == Qt::LeftButton) {
|
||
m_mouseDragging = false;
|
||
}
|
||
else if (e->button() == Qt::MiddleButton) {
|
||
m_middleDragging = false;
|
||
}
|
||
}
|
||
// 鼠标移动
|
||
else if (event->type() == QEvent::MouseMove) {
|
||
auto* e = static_cast<QMouseEvent*>(event);
|
||
QPoint pos = e->pos();
|
||
QPoint delta = pos - m_lastMousePos;
|
||
|
||
// 左键:orbit(旋转)
|
||
if (m_mouseDragging) {
|
||
float sensitivity = 0.3f;
|
||
m_yawDeg -= delta.x() * sensitivity;
|
||
m_pitchDeg += delta.y() * sensitivity;
|
||
|
||
if (m_pitchDeg > 89.0f) m_pitchDeg = 89.0f;
|
||
if (m_pitchDeg < -89.0f) m_pitchDeg = -89.0f;
|
||
|
||
updateCameraPosition();
|
||
}
|
||
|
||
// 中键:pan(平移 viewCenter)
|
||
if (m_middleDragging) {
|
||
float speed = 2.0f;
|
||
|
||
// 根据摄像机方向计算平移方向
|
||
QVector3D camPos = m_camera->position();
|
||
QVector3D forward = (m_viewCenter - camPos).normalized();
|
||
QVector3D up(0, 1, 0);
|
||
QVector3D right = QVector3D::crossProduct(forward, up).normalized();
|
||
|
||
// 世界坐标变化量
|
||
QVector3D deltaMove =
|
||
-right * (delta.x() * speed) +
|
||
up * (delta.y() * speed);
|
||
|
||
// 将平移应用到两个模型根 Transform
|
||
if (m_baseRootTransform)
|
||
m_baseRootTransform->setTranslation(
|
||
m_baseRootTransform->translation() + deltaMove*-1);
|
||
|
||
if (m_armRootTransform)
|
||
m_armRootTransform->setTranslation(
|
||
m_armRootTransform->translation() + deltaMove*-1);
|
||
}
|
||
|
||
m_lastMousePos = pos;
|
||
}
|
||
// 滚轮缩放
|
||
else if (event->type() == QEvent::Wheel) {
|
||
auto* e = static_cast<QWheelEvent*>(event);
|
||
// Qt5: angleDelta 返回像素值(通常为 120 per notch)
|
||
int delta = e->angleDelta().y();
|
||
if (delta == 0) delta = e->delta(); // 备选
|
||
m_distance -= delta * 2.0f; // 缩放速度
|
||
if (m_distance < 2.0f) m_distance = 2.0f;
|
||
if (m_distance > 10000.0f) m_distance = 10000.0f;
|
||
updateCameraPosition();
|
||
}
|
||
}
|
||
|
||
// 让 Qt 继续处理(保持原行为)
|
||
return QWidget::eventFilter(obj, event);
|
||
}
|
||
|
||
View3DPlantPhenotype::View3DPlantPhenotype(const QString& baseModelPath, const QString& armModelPath, QWidget* parent)
|
||
:View3DBase(baseModelPath, armModelPath, parent)
|
||
{
|
||
|
||
}
|
||
|
||
void View3DPlantPhenotype::setLoc(std::vector<double> loc)
|
||
{
|
||
double x = round(loc[0] * 100) / 100;
|
||
double y = round(loc[1] * 100) / 100;
|
||
|
||
m_armTransform->setTranslation(QVector3D(x, y, 0));
|
||
}
|
||
|
||
View3DLinearStage::View3DLinearStage(const QString& baseModelPath, const QString& armModelPath, QWidget* parent)
|
||
:View3DBase(baseModelPath, armModelPath, parent)
|
||
{
|
||
|
||
}
|
||
|
||
void View3DLinearStage::setLoc(std::vector<double> loc)
|
||
{
|
||
double x = round(loc[0] * 100) / 100;
|
||
|
||
m_armTransform->setTranslation(QVector3D(x, 0, 0));
|
||
} |