Add 3d mouse picker
parent
c99ce31755
commit
bde3c01da0
|
@ -410,6 +410,9 @@ HEADERS += src/intnumberwidget.h
|
||||||
SOURCES += src/imagepreviewwidget.cpp
|
SOURCES += src/imagepreviewwidget.cpp
|
||||||
HEADERS += src/imagepreviewwidget.h
|
HEADERS += src/imagepreviewwidget.h
|
||||||
|
|
||||||
|
SOURCES += src/mousepicker.cpp
|
||||||
|
HEADERS += src/mousepicker.h
|
||||||
|
|
||||||
SOURCES += src/main.cpp
|
SOURCES += src/main.cpp
|
||||||
|
|
||||||
HEADERS += src/version.h
|
HEADERS += src/version.h
|
||||||
|
|
|
@ -362,6 +362,10 @@ Tips:
|
||||||
<source>Check for Updates...</source>
|
<source>Check for Updates...</source>
|
||||||
<translation>检查新版本...</translation>
|
<translation>检查新版本...</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Paint brush</source>
|
||||||
|
<translation>画刷</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ExportPreviewWidget</name>
|
<name>ExportPreviewWidget</name>
|
||||||
|
|
|
@ -75,6 +75,8 @@ uniform highp sampler2D metalnessRoughnessAmbientOcclusionMapId;
|
||||||
uniform highp int metalnessMapEnabled;
|
uniform highp int metalnessMapEnabled;
|
||||||
uniform highp int roughnessMapEnabled;
|
uniform highp int roughnessMapEnabled;
|
||||||
uniform highp int ambientOcclusionMapEnabled;
|
uniform highp int ambientOcclusionMapEnabled;
|
||||||
|
uniform highp int mousePickEnabled;
|
||||||
|
uniform highp vec3 mousePickTargetPosition;
|
||||||
|
|
||||||
const int MAX_LIGHTS = 8;
|
const int MAX_LIGHTS = 8;
|
||||||
const int TYPE_POINT = 0;
|
const int TYPE_POINT = 0;
|
||||||
|
@ -320,6 +322,11 @@ void main()
|
||||||
if (textureEnabled == 1) {
|
if (textureEnabled == 1) {
|
||||||
color = texture2D(textureId, vertTexCoord).rgb;
|
color = texture2D(textureId, vertTexCoord).rgb;
|
||||||
}
|
}
|
||||||
|
if (mousePickEnabled == 1) {
|
||||||
|
if (distance(mousePickTargetPosition, vert) <= 0.1) {
|
||||||
|
color = color + vec3(0.99, 0.4, 0.13);
|
||||||
|
}
|
||||||
|
}
|
||||||
color = pow(color, vec3(gamma));
|
color = pow(color, vec3(gamma));
|
||||||
|
|
||||||
highp vec3 normal = vertNormal;
|
highp vec3 normal = vertNormal;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "motionsgenerator.h"
|
#include "motionsgenerator.h"
|
||||||
#include "skeletonside.h"
|
#include "skeletonside.h"
|
||||||
#include "scriptrunner.h"
|
#include "scriptrunner.h"
|
||||||
|
#include "mousepicker.h"
|
||||||
|
|
||||||
unsigned long Document::m_maxSnapshot = 1000;
|
unsigned long Document::m_maxSnapshot = 1000;
|
||||||
|
|
||||||
|
@ -64,7 +65,9 @@ Document::Document() :
|
||||||
m_meshGenerationId(0),
|
m_meshGenerationId(0),
|
||||||
m_nextMeshGenerationId(1),
|
m_nextMeshGenerationId(1),
|
||||||
m_scriptRunner(nullptr),
|
m_scriptRunner(nullptr),
|
||||||
m_isScriptResultObsolete(false)
|
m_isScriptResultObsolete(false),
|
||||||
|
m_mousePicker(nullptr),
|
||||||
|
m_isMouseTargetResultObsolete(false)
|
||||||
{
|
{
|
||||||
connect(&Preferences::instance(), &Preferences::partColorChanged, this, &Document::applyPreferencePartColorChange);
|
connect(&Preferences::instance(), &Preferences::partColorChanged, this, &Document::applyPreferencePartColorChange);
|
||||||
connect(&Preferences::instance(), &Preferences::flatShadingChanged, this, &Document::applyPreferenceFlatShadingChange);
|
connect(&Preferences::instance(), &Preferences::flatShadingChanged, this, &Document::applyPreferenceFlatShadingChange);
|
||||||
|
@ -1922,6 +1925,56 @@ void Document::postProcessedMeshResultReady()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Document::pickMouseTarget(const QVector3D &nearPosition, const QVector3D &farPosition)
|
||||||
|
{
|
||||||
|
m_mouseRayNear = nearPosition;
|
||||||
|
m_mouseRayFar = farPosition;
|
||||||
|
|
||||||
|
if (nullptr != m_mousePicker) {
|
||||||
|
m_isMouseTargetResultObsolete = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_isMouseTargetResultObsolete = false;
|
||||||
|
|
||||||
|
if (!m_currentOutcome) {
|
||||||
|
qDebug() << "MeshLoader is null";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Mouse picking..";
|
||||||
|
|
||||||
|
QThread *thread = new QThread;
|
||||||
|
m_mousePicker = new MousePicker(*m_currentOutcome, m_mouseRayNear, m_mouseRayFar);
|
||||||
|
m_mousePicker->moveToThread(thread);
|
||||||
|
connect(thread, &QThread::started, m_mousePicker, &MousePicker::process);
|
||||||
|
connect(m_mousePicker, &MousePicker::finished, this, &Document::mouseTargetReady);
|
||||||
|
connect(m_mousePicker, &MousePicker::finished, thread, &QThread::quit);
|
||||||
|
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
|
||||||
|
thread->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::mouseTargetReady()
|
||||||
|
{
|
||||||
|
m_mouseTargetPosition = m_mousePicker->targetPosition();
|
||||||
|
|
||||||
|
delete m_mousePicker;
|
||||||
|
m_mousePicker = nullptr;
|
||||||
|
|
||||||
|
emit mouseTargetChanged();
|
||||||
|
|
||||||
|
qDebug() << "Mouse pick done";
|
||||||
|
|
||||||
|
if (m_isMouseTargetResultObsolete) {
|
||||||
|
pickMouseTarget(m_mouseRayNear, m_mouseRayFar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QVector3D &Document::mouseTargetPosition() const
|
||||||
|
{
|
||||||
|
return m_mouseTargetPosition;
|
||||||
|
}
|
||||||
|
|
||||||
const Outcome &Document::currentPostProcessedOutcome() const
|
const Outcome &Document::currentPostProcessedOutcome() const
|
||||||
{
|
{
|
||||||
return *m_postProcessedOutcome;
|
return *m_postProcessedOutcome;
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
class MaterialPreviewsGenerator;
|
class MaterialPreviewsGenerator;
|
||||||
class MotionsGenerator;
|
class MotionsGenerator;
|
||||||
class ScriptRunner;
|
class ScriptRunner;
|
||||||
|
class MousePicker;
|
||||||
|
|
||||||
class HistoryItem
|
class HistoryItem
|
||||||
{
|
{
|
||||||
|
@ -471,6 +472,7 @@ signals:
|
||||||
void scriptRunning();
|
void scriptRunning();
|
||||||
void scriptErrorChanged();
|
void scriptErrorChanged();
|
||||||
void scriptConsoleLogChanged();
|
void scriptConsoleLogChanged();
|
||||||
|
void mouseTargetChanged();
|
||||||
public: // need initialize
|
public: // need initialize
|
||||||
QImage *textureGuideImage;
|
QImage *textureGuideImage;
|
||||||
QImage *textureImage;
|
QImage *textureImage;
|
||||||
|
@ -539,6 +541,7 @@ public:
|
||||||
const std::map<QString, std::map<QString, QString>> &variables() const;
|
const std::map<QString, std::map<QString, QString>> &variables() const;
|
||||||
const QString &scriptError() const;
|
const QString &scriptError() const;
|
||||||
const QString &scriptConsoleLog() const;
|
const QString &scriptConsoleLog() const;
|
||||||
|
const QVector3D &mouseTargetPosition() const;
|
||||||
public slots:
|
public slots:
|
||||||
void undo() override;
|
void undo() override;
|
||||||
void redo() override;
|
void redo() override;
|
||||||
|
@ -577,6 +580,8 @@ public slots:
|
||||||
void materialPreviewsReady();
|
void materialPreviewsReady();
|
||||||
void generateMotions();
|
void generateMotions();
|
||||||
void motionsReady();
|
void motionsReady();
|
||||||
|
void pickMouseTarget(const QVector3D &nearPosition, const QVector3D &farPosition);
|
||||||
|
void mouseTargetReady();
|
||||||
void setPartLockState(QUuid partId, bool locked);
|
void setPartLockState(QUuid partId, bool locked);
|
||||||
void setPartVisibleState(QUuid partId, bool visible);
|
void setPartVisibleState(QUuid partId, bool visible);
|
||||||
void setPartSubdivState(QUuid partId, bool subdived);
|
void setPartSubdivState(QUuid partId, bool subdived);
|
||||||
|
@ -721,6 +726,11 @@ private: // need initialize
|
||||||
std::map<QString, std::map<QString, QString>> m_mergedVariables;
|
std::map<QString, std::map<QString, QString>> m_mergedVariables;
|
||||||
ScriptRunner *m_scriptRunner;
|
ScriptRunner *m_scriptRunner;
|
||||||
bool m_isScriptResultObsolete;
|
bool m_isScriptResultObsolete;
|
||||||
|
MousePicker *m_mousePicker;
|
||||||
|
bool m_isMouseTargetResultObsolete;
|
||||||
|
QVector3D m_mouseRayNear;
|
||||||
|
QVector3D m_mouseRayFar;
|
||||||
|
QVector3D m_mouseTargetPosition;
|
||||||
QString m_scriptError;
|
QString m_scriptError;
|
||||||
QString m_scriptConsoleLog;
|
QString m_scriptConsoleLog;
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -157,6 +157,10 @@ DocumentWindow::DocumentWindow() :
|
||||||
selectButton->setToolTip(tr("Select node on canvas"));
|
selectButton->setToolTip(tr("Select node on canvas"));
|
||||||
Theme::initAwesomeButton(selectButton);
|
Theme::initAwesomeButton(selectButton);
|
||||||
|
|
||||||
|
QPushButton *paintButton = new QPushButton(QChar(fa::paintbrush));
|
||||||
|
paintButton->setToolTip(tr("Paint brush"));
|
||||||
|
Theme::initAwesomeButton(paintButton);
|
||||||
|
|
||||||
QPushButton *dragButton = new QPushButton(QChar(fa::handrocko));
|
QPushButton *dragButton = new QPushButton(QChar(fa::handrocko));
|
||||||
dragButton->setToolTip(tr("Enter drag mode"));
|
dragButton->setToolTip(tr("Enter drag mode"));
|
||||||
Theme::initAwesomeButton(dragButton);
|
Theme::initAwesomeButton(dragButton);
|
||||||
|
@ -228,6 +232,7 @@ DocumentWindow::DocumentWindow() :
|
||||||
|
|
||||||
toolButtonLayout->addWidget(addButton);
|
toolButtonLayout->addWidget(addButton);
|
||||||
toolButtonLayout->addWidget(selectButton);
|
toolButtonLayout->addWidget(selectButton);
|
||||||
|
toolButtonLayout->addWidget(paintButton);
|
||||||
toolButtonLayout->addWidget(dragButton);
|
toolButtonLayout->addWidget(dragButton);
|
||||||
toolButtonLayout->addWidget(zoomInButton);
|
toolButtonLayout->addWidget(zoomInButton);
|
||||||
toolButtonLayout->addWidget(zoomOutButton);
|
toolButtonLayout->addWidget(zoomOutButton);
|
||||||
|
@ -278,6 +283,11 @@ DocumentWindow::DocumentWindow() :
|
||||||
m_modelRenderWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
m_modelRenderWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||||
m_modelRenderWidget->move(DocumentWindow::m_modelRenderWidgetInitialX, DocumentWindow::m_modelRenderWidgetInitialY);
|
m_modelRenderWidget->move(DocumentWindow::m_modelRenderWidgetInitialX, DocumentWindow::m_modelRenderWidgetInitialY);
|
||||||
|
|
||||||
|
connect(m_modelRenderWidget, &ModelWidget::mouseRayChanged, m_document, &Document::pickMouseTarget);
|
||||||
|
connect(m_document, &Document::mouseTargetChanged, this, [=]() {
|
||||||
|
m_modelRenderWidget->setMousePickTargetPositionInModelSpace(m_document->mouseTargetPosition());
|
||||||
|
});
|
||||||
|
|
||||||
m_graphicsWidget->setModelWidget(m_modelRenderWidget);
|
m_graphicsWidget->setModelWidget(m_modelRenderWidget);
|
||||||
containerWidget->setModelWidget(m_modelRenderWidget);
|
containerWidget->setModelWidget(m_modelRenderWidget);
|
||||||
|
|
||||||
|
@ -768,6 +778,10 @@ DocumentWindow::DocumentWindow() :
|
||||||
m_document->setEditMode(SkeletonDocumentEditMode::Select);
|
m_document->setEditMode(SkeletonDocumentEditMode::Select);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(paintButton, &QPushButton::clicked, [=]() {
|
||||||
|
m_document->setEditMode(SkeletonDocumentEditMode::Paint);
|
||||||
|
});
|
||||||
|
|
||||||
connect(dragButton, &QPushButton::clicked, [=]() {
|
connect(dragButton, &QPushButton::clicked, [=]() {
|
||||||
m_document->setEditMode(SkeletonDocumentEditMode::Drag);
|
m_document->setEditMode(SkeletonDocumentEditMode::Drag);
|
||||||
});
|
});
|
||||||
|
@ -793,6 +807,10 @@ DocumentWindow::DocumentWindow() :
|
||||||
m_document->setRadiusLockState(!m_document->radiusLocked);
|
m_document->setRadiusLockState(!m_document->radiusLocked);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(m_document, &Document::editModeChanged, this, [=]() {
|
||||||
|
m_modelRenderWidget->enableMousePicking(SkeletonDocumentEditMode::Paint == m_document->editMode);
|
||||||
|
});
|
||||||
|
|
||||||
m_partListDockerVisibleSwitchConnection = connect(m_document, &Document::skeletonChanged, [=]() {
|
m_partListDockerVisibleSwitchConnection = connect(m_document, &Document::skeletonChanged, [=]() {
|
||||||
if (m_graphicsWidget->hasItems()) {
|
if (m_graphicsWidget->hasItems()) {
|
||||||
if (partTreeDocker->isHidden())
|
if (partTreeDocker->isHidden())
|
||||||
|
@ -973,6 +991,7 @@ DocumentWindow::DocumentWindow() :
|
||||||
|
|
||||||
connect(graphicsWidget, &SkeletonGraphicsWidget::cursorChanged, [=]() {
|
connect(graphicsWidget, &SkeletonGraphicsWidget::cursorChanged, [=]() {
|
||||||
m_modelRenderWidget->setCursor(graphicsWidget->cursor());
|
m_modelRenderWidget->setCursor(graphicsWidget->cursor());
|
||||||
|
containerWidget->setCursor(graphicsWidget->cursor());
|
||||||
//m_skeletonRenderWidget->setCursor(graphicsWidget->cursor());
|
//m_skeletonRenderWidget->setCursor(graphicsWidget->cursor());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ signals:
|
||||||
void initialized();
|
void initialized();
|
||||||
void uninialized();
|
void uninialized();
|
||||||
void waitingExportFinished(const QString &filename, bool succeed);
|
void waitingExportFinished(const QString &filename, bool succeed);
|
||||||
|
void mouseTargetVertexPositionChanged(const QVector3D &position);
|
||||||
public:
|
public:
|
||||||
DocumentWindow();
|
DocumentWindow();
|
||||||
~DocumentWindow();
|
~DocumentWindow();
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
GraphicsContainerWidget::GraphicsContainerWidget()
|
GraphicsContainerWidget::GraphicsContainerWidget()
|
||||||
{
|
{
|
||||||
|
setMouseTracking(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsContainerWidget::resizeEvent(QResizeEvent *event)
|
void GraphicsContainerWidget::resizeEvent(QResizeEvent *event)
|
||||||
|
|
|
@ -53,6 +53,8 @@ ModelShaderProgram::ModelShaderProgram(bool usePBR)
|
||||||
m_roughnessMapEnabledLoc = this->uniformLocation("roughnessMapEnabled");
|
m_roughnessMapEnabledLoc = this->uniformLocation("roughnessMapEnabled");
|
||||||
m_ambientOcclusionMapEnabledLoc = this->uniformLocation("ambientOcclusionMapEnabled");
|
m_ambientOcclusionMapEnabledLoc = this->uniformLocation("ambientOcclusionMapEnabled");
|
||||||
m_metalnessRoughnessAmbientOcclusionMapIdLoc = this->uniformLocation("metalnessRoughnessAmbientOcclusionMapId");
|
m_metalnessRoughnessAmbientOcclusionMapIdLoc = this->uniformLocation("metalnessRoughnessAmbientOcclusionMapId");
|
||||||
|
m_mousePickEnabledLoc = this->uniformLocation("mousePickEnabled");
|
||||||
|
m_mousePickTargetPositionLoc = this->uniformLocation("mousePickTargetPosition");
|
||||||
}
|
}
|
||||||
|
|
||||||
int ModelShaderProgram::projectionMatrixLoc()
|
int ModelShaderProgram::projectionMatrixLoc()
|
||||||
|
@ -120,3 +122,12 @@ int ModelShaderProgram::metalnessRoughnessAmbientOcclusionMapIdLoc()
|
||||||
return m_metalnessRoughnessAmbientOcclusionMapIdLoc;
|
return m_metalnessRoughnessAmbientOcclusionMapIdLoc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ModelShaderProgram::mousePickEnabledLoc()
|
||||||
|
{
|
||||||
|
return m_mousePickEnabledLoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ModelShaderProgram::mousePickTargetPositionLoc()
|
||||||
|
{
|
||||||
|
return m_mousePickTargetPositionLoc;
|
||||||
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ public:
|
||||||
int roughnessMapEnabledLoc();
|
int roughnessMapEnabledLoc();
|
||||||
int ambientOcclusionMapEnabledLoc();
|
int ambientOcclusionMapEnabledLoc();
|
||||||
int metalnessRoughnessAmbientOcclusionMapIdLoc();
|
int metalnessRoughnessAmbientOcclusionMapIdLoc();
|
||||||
|
int mousePickEnabledLoc();
|
||||||
|
int mousePickTargetPositionLoc();
|
||||||
static const QString &loadShaderSource(const QString &name);
|
static const QString &loadShaderSource(const QString &name);
|
||||||
private:
|
private:
|
||||||
int m_projectionMatrixLoc;
|
int m_projectionMatrixLoc;
|
||||||
|
@ -35,6 +37,8 @@ private:
|
||||||
int m_roughnessMapEnabledLoc;
|
int m_roughnessMapEnabledLoc;
|
||||||
int m_ambientOcclusionMapEnabledLoc;
|
int m_ambientOcclusionMapEnabledLoc;
|
||||||
int m_metalnessRoughnessAmbientOcclusionMapIdLoc;
|
int m_metalnessRoughnessAmbientOcclusionMapIdLoc;
|
||||||
|
int m_mousePickEnabledLoc;
|
||||||
|
int m_mousePickTargetPositionLoc;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
// Modifed from http://doc.qt.io/qt-5/qtopengl-hellogl2-glwidget-cpp.html
|
// Modifed from http://doc.qt.io/qt-5/qtopengl-hellogl2-glwidget-cpp.html
|
||||||
|
|
||||||
bool ModelWidget::m_transparent = true;
|
bool ModelWidget::m_transparent = true;
|
||||||
|
const QVector3D ModelWidget::m_cameraPosition = QVector3D(0, 0, -4.0);
|
||||||
|
|
||||||
ModelWidget::ModelWidget(QWidget *parent) :
|
ModelWidget::ModelWidget(QWidget *parent) :
|
||||||
QOpenGLWidget(parent),
|
QOpenGLWidget(parent),
|
||||||
|
@ -19,7 +20,8 @@ ModelWidget::ModelWidget(QWidget *parent) :
|
||||||
m_program(nullptr),
|
m_program(nullptr),
|
||||||
m_moveStarted(false),
|
m_moveStarted(false),
|
||||||
m_moveEnabled(true),
|
m_moveEnabled(true),
|
||||||
m_zoomEnabled(true)
|
m_zoomEnabled(true),
|
||||||
|
m_mousePickingEnabled(false)
|
||||||
{
|
{
|
||||||
// --transparent causes the clear color to be transparent. Therefore, on systems that
|
// --transparent causes the clear color to be transparent. Therefore, on systems that
|
||||||
// support it, the widget will become transparent apart from the logo.
|
// support it, the widget will become transparent apart from the logo.
|
||||||
|
@ -130,7 +132,7 @@ void ModelWidget::initializeGL()
|
||||||
// Our camera never changes in this example.
|
// Our camera never changes in this example.
|
||||||
m_camera.setToIdentity();
|
m_camera.setToIdentity();
|
||||||
// FIXME: if change here, please also change the camera pos in PBR shader
|
// FIXME: if change here, please also change the camera pos in PBR shader
|
||||||
m_camera.translate(0, 0, -4.0);
|
m_camera.translate(m_cameraPosition.x(), m_cameraPosition.y(), m_cameraPosition.z());
|
||||||
|
|
||||||
// Light position is fixed.
|
// Light position is fixed.
|
||||||
// FIXME: PBR render no longer use this parameter
|
// FIXME: PBR render no longer use this parameter
|
||||||
|
@ -162,6 +164,15 @@ void ModelWidget::paintGL()
|
||||||
m_program->setUniformValue(m_program->textureEnabledLoc(), 0);
|
m_program->setUniformValue(m_program->textureEnabledLoc(), 0);
|
||||||
m_program->setUniformValue(m_program->normalMapEnabledLoc(), 0);
|
m_program->setUniformValue(m_program->normalMapEnabledLoc(), 0);
|
||||||
|
|
||||||
|
if (m_mousePickingEnabled && !m_mousePickTargetPositionInModelSpace.isNull()) {
|
||||||
|
m_program->setUniformValue(m_program->mousePickEnabledLoc(), 1);
|
||||||
|
m_program->setUniformValue(m_program->mousePickTargetPositionLoc(),
|
||||||
|
m_world * m_mousePickTargetPositionInModelSpace);
|
||||||
|
} else {
|
||||||
|
m_program->setUniformValue(m_program->mousePickEnabledLoc(), 0);
|
||||||
|
m_program->setUniformValue(m_program->mousePickTargetPositionLoc(), QVector3D());
|
||||||
|
}
|
||||||
|
|
||||||
m_meshBinder.paint(m_program);
|
m_meshBinder.paint(m_program);
|
||||||
|
|
||||||
m_program->release();
|
m_program->release();
|
||||||
|
@ -173,6 +184,20 @@ void ModelWidget::resizeGL(int w, int h)
|
||||||
m_projection.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f);
|
m_projection.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<QVector3D, QVector3D> ModelWidget::screenPositionToMouseRay(const QPoint &screenPosition)
|
||||||
|
{
|
||||||
|
auto modelView = m_camera * m_world;
|
||||||
|
float x = qMax(qMin(screenPosition.x(), width() - 1), 0);
|
||||||
|
float y = qMax(qMin(screenPosition.y(), height() - 1), 0);
|
||||||
|
QVector3D nearScreen = QVector3D(x, height() - y, 0.0);
|
||||||
|
QVector3D farScreen = QVector3D(x, height() - y, 1.0);
|
||||||
|
auto viewPort = QRect(0, 0, width(), height());
|
||||||
|
auto nearPosition = nearScreen.unproject(modelView, m_projection, viewPort);
|
||||||
|
auto farPosition = farScreen.unproject(modelView, m_projection, viewPort);
|
||||||
|
qDebug() << "near:" << nearPosition << "far:" << farPosition << "x:" << x << "y:" << y;
|
||||||
|
return std::make_pair(nearPosition, farPosition);
|
||||||
|
}
|
||||||
|
|
||||||
void ModelWidget::toggleWireframe()
|
void ModelWidget::toggleWireframe()
|
||||||
{
|
{
|
||||||
if (m_meshBinder.isWireframesVisible())
|
if (m_meshBinder.isWireframesVisible())
|
||||||
|
@ -217,11 +242,17 @@ bool ModelWidget::inputMouseReleaseEventFromOtherWidget(QMouseEvent *event)
|
||||||
|
|
||||||
bool ModelWidget::inputMouseMoveEventFromOtherWidget(QMouseEvent *event)
|
bool ModelWidget::inputMouseMoveEventFromOtherWidget(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
|
QPoint pos = convertInputPosFromOtherWidget(event);
|
||||||
|
|
||||||
|
if (m_mousePickingEnabled) {
|
||||||
|
auto segment = screenPositionToMouseRay(pos);
|
||||||
|
emit mouseRayChanged(segment.first, segment.second);
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_moveStarted) {
|
if (!m_moveStarted) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPoint pos = convertInputPosFromOtherWidget(event);
|
|
||||||
int dx = pos.x() - m_lastPos.x();
|
int dx = pos.x() - m_lastPos.x();
|
||||||
int dy = pos.y() - m_lastPos.y();
|
int dy = pos.y() - m_lastPos.y();
|
||||||
|
|
||||||
|
@ -275,6 +306,12 @@ void ModelWidget::zoom(float delta)
|
||||||
setGeometry(geometry().marginsAdded(margins));
|
setGeometry(geometry().marginsAdded(margins));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModelWidget::setMousePickTargetPositionInModelSpace(QVector3D position)
|
||||||
|
{
|
||||||
|
m_mousePickTargetPositionInModelSpace = position;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
void ModelWidget::updateMesh(MeshLoader *mesh)
|
void ModelWidget::updateMesh(MeshLoader *mesh)
|
||||||
{
|
{
|
||||||
m_meshBinder.updateMesh(mesh);
|
m_meshBinder.updateMesh(mesh);
|
||||||
|
@ -291,6 +328,11 @@ void ModelWidget::enableZoom(bool enabled)
|
||||||
m_zoomEnabled = enabled;
|
m_zoomEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModelWidget::enableMousePicking(bool enabled)
|
||||||
|
{
|
||||||
|
m_mousePickingEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
void ModelWidget::mousePressEvent(QMouseEvent *event)
|
void ModelWidget::mousePressEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
inputMousePressEventFromOtherWidget(event);
|
inputMousePressEventFromOtherWidget(event);
|
||||||
|
|
|
@ -17,6 +17,8 @@ class SkeletonGraphicsFunctions;
|
||||||
class ModelWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
class ModelWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
signals:
|
||||||
|
void mouseRayChanged(const QVector3D &near, const QVector3D &far);
|
||||||
public:
|
public:
|
||||||
ModelWidget(QWidget *parent = 0);
|
ModelWidget(QWidget *parent = 0);
|
||||||
~ModelWidget();
|
~ModelWidget();
|
||||||
|
@ -33,6 +35,7 @@ public:
|
||||||
void toggleWireframe();
|
void toggleWireframe();
|
||||||
void enableMove(bool enabled);
|
void enableMove(bool enabled);
|
||||||
void enableZoom(bool enabled);
|
void enableZoom(bool enabled);
|
||||||
|
void enableMousePicking(bool enabled);
|
||||||
bool inputMousePressEventFromOtherWidget(QMouseEvent *event);
|
bool inputMousePressEventFromOtherWidget(QMouseEvent *event);
|
||||||
bool inputMouseMoveEventFromOtherWidget(QMouseEvent *event);
|
bool inputMouseMoveEventFromOtherWidget(QMouseEvent *event);
|
||||||
bool inputWheelEventFromOtherWidget(QWheelEvent *event);
|
bool inputWheelEventFromOtherWidget(QWheelEvent *event);
|
||||||
|
@ -44,6 +47,7 @@ public slots:
|
||||||
void setZRotation(int angle);
|
void setZRotation(int angle);
|
||||||
void cleanup();
|
void cleanup();
|
||||||
void zoom(float delta);
|
void zoom(float delta);
|
||||||
|
void setMousePickTargetPositionInModelSpace(QVector3D position);
|
||||||
signals:
|
signals:
|
||||||
void xRotationChanged(int angle);
|
void xRotationChanged(int angle);
|
||||||
void yRotationChanged(int angle);
|
void yRotationChanged(int angle);
|
||||||
|
@ -68,6 +72,8 @@ private:
|
||||||
bool m_moveStarted;
|
bool m_moveStarted;
|
||||||
bool m_moveEnabled;
|
bool m_moveEnabled;
|
||||||
bool m_zoomEnabled;
|
bool m_zoomEnabled;
|
||||||
|
bool m_mousePickingEnabled;
|
||||||
|
QVector3D m_mousePickTargetPositionInModelSpace;
|
||||||
private:
|
private:
|
||||||
QPoint m_lastPos;
|
QPoint m_lastPos;
|
||||||
ModelMeshBinder m_meshBinder;
|
ModelMeshBinder m_meshBinder;
|
||||||
|
@ -75,8 +81,10 @@ private:
|
||||||
QMatrix4x4 m_camera;
|
QMatrix4x4 m_camera;
|
||||||
QMatrix4x4 m_world;
|
QMatrix4x4 m_world;
|
||||||
static bool m_transparent;
|
static bool m_transparent;
|
||||||
|
static const QVector3D m_cameraPosition;
|
||||||
QPoint m_moveStartPos;
|
QPoint m_moveStartPos;
|
||||||
QRect m_moveStartGeometry;
|
QRect m_moveStartGeometry;
|
||||||
|
std::pair<QVector3D, QVector3D> screenPositionToMouseRay(const QPoint &screenPosition);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
#include <QDebug>
|
||||||
|
#include "mousepicker.h"
|
||||||
|
|
||||||
|
MousePicker::MousePicker(const Outcome &outcome, const QVector3D &mouseRayNear, const QVector3D &mouseRayFar) :
|
||||||
|
m_outcome(outcome),
|
||||||
|
m_mouseRayNear(mouseRayNear),
|
||||||
|
m_mouseRayFar(mouseRayFar)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MousePicker::~MousePicker()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void MousePicker::process()
|
||||||
|
{
|
||||||
|
float minDistance2 = std::numeric_limits<float>::max();
|
||||||
|
for (size_t i = 0; i < m_outcome.triangles.size(); ++i) {
|
||||||
|
const auto &triangleIndices = m_outcome.triangles[i];
|
||||||
|
std::vector<QVector3D> triangle = {
|
||||||
|
m_outcome.vertices[triangleIndices[0]],
|
||||||
|
m_outcome.vertices[triangleIndices[1]],
|
||||||
|
m_outcome.vertices[triangleIndices[2]],
|
||||||
|
};
|
||||||
|
const auto &triangleNormal = m_outcome.triangleNormals[i];
|
||||||
|
QVector3D intersection;
|
||||||
|
if (intersectSegmentAndTriangle(m_mouseRayNear, m_mouseRayFar,
|
||||||
|
triangle,
|
||||||
|
triangleNormal,
|
||||||
|
&intersection)) {
|
||||||
|
float distance2 = (intersection - m_mouseRayNear).lengthSquared();
|
||||||
|
if (distance2 < minDistance2) {
|
||||||
|
m_targetPosition = intersection;
|
||||||
|
minDistance2 = distance2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit finished();
|
||||||
|
}
|
||||||
|
|
||||||
|
const QVector3D &MousePicker::targetPosition()
|
||||||
|
{
|
||||||
|
return m_targetPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MousePicker::intersectSegmentAndPlane(const QVector3D &segmentPoint0, const QVector3D &segmentPoint1,
|
||||||
|
const QVector3D &pointOnPlane, const QVector3D &planeNormal,
|
||||||
|
QVector3D *intersection)
|
||||||
|
{
|
||||||
|
auto u = segmentPoint1 - segmentPoint0;
|
||||||
|
auto w = segmentPoint0 - pointOnPlane;
|
||||||
|
auto d = QVector3D::dotProduct(planeNormal, u);
|
||||||
|
auto n = QVector3D::dotProduct(-planeNormal, w);
|
||||||
|
if (qAbs(d) < 0.00000001)
|
||||||
|
return false;
|
||||||
|
auto s = n / d;
|
||||||
|
if (s < 0 || s > 1 || qIsNaN(s) || qIsInf(s))
|
||||||
|
return false;
|
||||||
|
if (nullptr != intersection)
|
||||||
|
*intersection = segmentPoint0 + s * u;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MousePicker::intersectSegmentAndTriangle(const QVector3D &segmentPoint0, const QVector3D &segmentPoint1,
|
||||||
|
const std::vector<QVector3D> &triangle,
|
||||||
|
const QVector3D &triangleNormal,
|
||||||
|
QVector3D *intersection)
|
||||||
|
{
|
||||||
|
QVector3D possibleIntersection;
|
||||||
|
if (!intersectSegmentAndPlane(segmentPoint0, segmentPoint1,
|
||||||
|
triangle[0], triangleNormal, &possibleIntersection)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto ray = (segmentPoint0 - segmentPoint1).normalized();
|
||||||
|
std::vector<QVector3D> normals;
|
||||||
|
for (size_t i = 0; i < 3; ++i) {
|
||||||
|
size_t j = (i + 1) % 3;
|
||||||
|
normals.push_back(QVector3D::normal(possibleIntersection, triangle[i], triangle[j]));
|
||||||
|
}
|
||||||
|
if (QVector3D::dotProduct(normals[0], ray) <= 0)
|
||||||
|
return false;
|
||||||
|
if (QVector3D::dotProduct(normals[0], normals[1]) <= 0)
|
||||||
|
return false;
|
||||||
|
if (QVector3D::dotProduct(normals[0], normals[2]) <= 0)
|
||||||
|
return false;
|
||||||
|
if (nullptr != intersection)
|
||||||
|
*intersection = possibleIntersection;
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef DUST3D_MOUSE_PICKER_H
|
||||||
|
#define DUST3D_MOUSE_PICKER_H
|
||||||
|
#include <QObject>
|
||||||
|
#include <QVector3D>
|
||||||
|
#include <vector>
|
||||||
|
#include "outcome.h"
|
||||||
|
|
||||||
|
class MousePicker : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
MousePicker(const Outcome &outcome, const QVector3D &mouseRayNear, const QVector3D &mouseRayFar);
|
||||||
|
~MousePicker();
|
||||||
|
const QVector3D &targetPosition();
|
||||||
|
signals:
|
||||||
|
void finished();
|
||||||
|
public slots:
|
||||||
|
void process();
|
||||||
|
private:
|
||||||
|
Outcome m_outcome;
|
||||||
|
QVector3D m_mouseRayNear;
|
||||||
|
QVector3D m_mouseRayFar;
|
||||||
|
QVector3D m_targetPosition;
|
||||||
|
static bool intersectSegmentAndPlane(const QVector3D &segmentPoint0, const QVector3D &segmentPoint1,
|
||||||
|
const QVector3D &pointOnPlane, const QVector3D &planeNormal,
|
||||||
|
QVector3D *intersection=nullptr);
|
||||||
|
static bool intersectSegmentAndTriangle(const QVector3D &segmentPoint0, const QVector3D &segmentPoint1,
|
||||||
|
const std::vector<QVector3D> &triangle,
|
||||||
|
const QVector3D &triangleNormal,
|
||||||
|
QVector3D *intersection=nullptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -285,6 +285,7 @@ enum class SkeletonDocumentEditMode
|
||||||
{
|
{
|
||||||
Add = 0,
|
Add = 0,
|
||||||
Select,
|
Select,
|
||||||
|
Paint,
|
||||||
Drag,
|
Drag,
|
||||||
ZoomIn,
|
ZoomIn,
|
||||||
ZoomOut
|
ZoomOut
|
||||||
|
|
|
@ -598,6 +598,9 @@ void SkeletonGraphicsWidget::updateCursor()
|
||||||
case SkeletonDocumentEditMode::Select:
|
case SkeletonDocumentEditMode::Select:
|
||||||
setCursor(QCursor(Theme::awesome()->icon(fa::mousepointer).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize), Theme::toolIconFontSize / 5, 0));
|
setCursor(QCursor(Theme::awesome()->icon(fa::mousepointer).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize), Theme::toolIconFontSize / 5, 0));
|
||||||
break;
|
break;
|
||||||
|
case SkeletonDocumentEditMode::Paint:
|
||||||
|
setCursor(QCursor(Theme::awesome()->icon(fa::paintbrush).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize)));
|
||||||
|
break;
|
||||||
case SkeletonDocumentEditMode::Drag:
|
case SkeletonDocumentEditMode::Drag:
|
||||||
setCursor(QCursor(Theme::awesome()->icon(m_dragStarted ? fa::handrocko : fa::handpapero).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize)));
|
setCursor(QCursor(Theme::awesome()->icon(m_dragStarted ? fa::handrocko : fa::handpapero).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize)));
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue