Add 3d mouse picker
parent
c99ce31755
commit
bde3c01da0
|
@ -410,6 +410,9 @@ HEADERS += src/intnumberwidget.h
|
|||
SOURCES += src/imagepreviewwidget.cpp
|
||||
HEADERS += src/imagepreviewwidget.h
|
||||
|
||||
SOURCES += src/mousepicker.cpp
|
||||
HEADERS += src/mousepicker.h
|
||||
|
||||
SOURCES += src/main.cpp
|
||||
|
||||
HEADERS += src/version.h
|
||||
|
|
|
@ -362,6 +362,10 @@ Tips:
|
|||
<source>Check for Updates...</source>
|
||||
<translation>检查新版本...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Paint brush</source>
|
||||
<translation>画刷</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ExportPreviewWidget</name>
|
||||
|
|
|
@ -75,6 +75,8 @@ uniform highp sampler2D metalnessRoughnessAmbientOcclusionMapId;
|
|||
uniform highp int metalnessMapEnabled;
|
||||
uniform highp int roughnessMapEnabled;
|
||||
uniform highp int ambientOcclusionMapEnabled;
|
||||
uniform highp int mousePickEnabled;
|
||||
uniform highp vec3 mousePickTargetPosition;
|
||||
|
||||
const int MAX_LIGHTS = 8;
|
||||
const int TYPE_POINT = 0;
|
||||
|
@ -320,6 +322,11 @@ void main()
|
|||
if (textureEnabled == 1) {
|
||||
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));
|
||||
|
||||
highp vec3 normal = vertNormal;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "motionsgenerator.h"
|
||||
#include "skeletonside.h"
|
||||
#include "scriptrunner.h"
|
||||
#include "mousepicker.h"
|
||||
|
||||
unsigned long Document::m_maxSnapshot = 1000;
|
||||
|
||||
|
@ -64,7 +65,9 @@ Document::Document() :
|
|||
m_meshGenerationId(0),
|
||||
m_nextMeshGenerationId(1),
|
||||
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::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
|
||||
{
|
||||
return *m_postProcessedOutcome;
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
class MaterialPreviewsGenerator;
|
||||
class MotionsGenerator;
|
||||
class ScriptRunner;
|
||||
class MousePicker;
|
||||
|
||||
class HistoryItem
|
||||
{
|
||||
|
@ -471,6 +472,7 @@ signals:
|
|||
void scriptRunning();
|
||||
void scriptErrorChanged();
|
||||
void scriptConsoleLogChanged();
|
||||
void mouseTargetChanged();
|
||||
public: // need initialize
|
||||
QImage *textureGuideImage;
|
||||
QImage *textureImage;
|
||||
|
@ -539,6 +541,7 @@ public:
|
|||
const std::map<QString, std::map<QString, QString>> &variables() const;
|
||||
const QString &scriptError() const;
|
||||
const QString &scriptConsoleLog() const;
|
||||
const QVector3D &mouseTargetPosition() const;
|
||||
public slots:
|
||||
void undo() override;
|
||||
void redo() override;
|
||||
|
@ -577,6 +580,8 @@ public slots:
|
|||
void materialPreviewsReady();
|
||||
void generateMotions();
|
||||
void motionsReady();
|
||||
void pickMouseTarget(const QVector3D &nearPosition, const QVector3D &farPosition);
|
||||
void mouseTargetReady();
|
||||
void setPartLockState(QUuid partId, bool locked);
|
||||
void setPartVisibleState(QUuid partId, bool visible);
|
||||
void setPartSubdivState(QUuid partId, bool subdived);
|
||||
|
@ -721,6 +726,11 @@ private: // need initialize
|
|||
std::map<QString, std::map<QString, QString>> m_mergedVariables;
|
||||
ScriptRunner *m_scriptRunner;
|
||||
bool m_isScriptResultObsolete;
|
||||
MousePicker *m_mousePicker;
|
||||
bool m_isMouseTargetResultObsolete;
|
||||
QVector3D m_mouseRayNear;
|
||||
QVector3D m_mouseRayFar;
|
||||
QVector3D m_mouseTargetPosition;
|
||||
QString m_scriptError;
|
||||
QString m_scriptConsoleLog;
|
||||
private:
|
||||
|
|
|
@ -157,6 +157,10 @@ DocumentWindow::DocumentWindow() :
|
|||
selectButton->setToolTip(tr("Select node on canvas"));
|
||||
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));
|
||||
dragButton->setToolTip(tr("Enter drag mode"));
|
||||
Theme::initAwesomeButton(dragButton);
|
||||
|
@ -228,6 +232,7 @@ DocumentWindow::DocumentWindow() :
|
|||
|
||||
toolButtonLayout->addWidget(addButton);
|
||||
toolButtonLayout->addWidget(selectButton);
|
||||
toolButtonLayout->addWidget(paintButton);
|
||||
toolButtonLayout->addWidget(dragButton);
|
||||
toolButtonLayout->addWidget(zoomInButton);
|
||||
toolButtonLayout->addWidget(zoomOutButton);
|
||||
|
@ -278,6 +283,11 @@ DocumentWindow::DocumentWindow() :
|
|||
m_modelRenderWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
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);
|
||||
containerWidget->setModelWidget(m_modelRenderWidget);
|
||||
|
||||
|
@ -768,6 +778,10 @@ DocumentWindow::DocumentWindow() :
|
|||
m_document->setEditMode(SkeletonDocumentEditMode::Select);
|
||||
});
|
||||
|
||||
connect(paintButton, &QPushButton::clicked, [=]() {
|
||||
m_document->setEditMode(SkeletonDocumentEditMode::Paint);
|
||||
});
|
||||
|
||||
connect(dragButton, &QPushButton::clicked, [=]() {
|
||||
m_document->setEditMode(SkeletonDocumentEditMode::Drag);
|
||||
});
|
||||
|
@ -793,6 +807,10 @@ DocumentWindow::DocumentWindow() :
|
|||
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, [=]() {
|
||||
if (m_graphicsWidget->hasItems()) {
|
||||
if (partTreeDocker->isHidden())
|
||||
|
@ -973,6 +991,7 @@ DocumentWindow::DocumentWindow() :
|
|||
|
||||
connect(graphicsWidget, &SkeletonGraphicsWidget::cursorChanged, [=]() {
|
||||
m_modelRenderWidget->setCursor(graphicsWidget->cursor());
|
||||
containerWidget->setCursor(graphicsWidget->cursor());
|
||||
//m_skeletonRenderWidget->setCursor(graphicsWidget->cursor());
|
||||
});
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ signals:
|
|||
void initialized();
|
||||
void uninialized();
|
||||
void waitingExportFinished(const QString &filename, bool succeed);
|
||||
void mouseTargetVertexPositionChanged(const QVector3D &position);
|
||||
public:
|
||||
DocumentWindow();
|
||||
~DocumentWindow();
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
GraphicsContainerWidget::GraphicsContainerWidget()
|
||||
{
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
void GraphicsContainerWidget::resizeEvent(QResizeEvent *event)
|
||||
|
|
|
@ -53,6 +53,8 @@ ModelShaderProgram::ModelShaderProgram(bool usePBR)
|
|||
m_roughnessMapEnabledLoc = this->uniformLocation("roughnessMapEnabled");
|
||||
m_ambientOcclusionMapEnabledLoc = this->uniformLocation("ambientOcclusionMapEnabled");
|
||||
m_metalnessRoughnessAmbientOcclusionMapIdLoc = this->uniformLocation("metalnessRoughnessAmbientOcclusionMapId");
|
||||
m_mousePickEnabledLoc = this->uniformLocation("mousePickEnabled");
|
||||
m_mousePickTargetPositionLoc = this->uniformLocation("mousePickTargetPosition");
|
||||
}
|
||||
|
||||
int ModelShaderProgram::projectionMatrixLoc()
|
||||
|
@ -120,3 +122,12 @@ int ModelShaderProgram::metalnessRoughnessAmbientOcclusionMapIdLoc()
|
|||
return m_metalnessRoughnessAmbientOcclusionMapIdLoc;
|
||||
}
|
||||
|
||||
int ModelShaderProgram::mousePickEnabledLoc()
|
||||
{
|
||||
return m_mousePickEnabledLoc;
|
||||
}
|
||||
|
||||
int ModelShaderProgram::mousePickTargetPositionLoc()
|
||||
{
|
||||
return m_mousePickTargetPositionLoc;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ public:
|
|||
int roughnessMapEnabledLoc();
|
||||
int ambientOcclusionMapEnabledLoc();
|
||||
int metalnessRoughnessAmbientOcclusionMapIdLoc();
|
||||
int mousePickEnabledLoc();
|
||||
int mousePickTargetPositionLoc();
|
||||
static const QString &loadShaderSource(const QString &name);
|
||||
private:
|
||||
int m_projectionMatrixLoc;
|
||||
|
@ -35,6 +37,8 @@ private:
|
|||
int m_roughnessMapEnabledLoc;
|
||||
int m_ambientOcclusionMapEnabledLoc;
|
||||
int m_metalnessRoughnessAmbientOcclusionMapIdLoc;
|
||||
int m_mousePickEnabledLoc;
|
||||
int m_mousePickTargetPositionLoc;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
// Modifed from http://doc.qt.io/qt-5/qtopengl-hellogl2-glwidget-cpp.html
|
||||
|
||||
bool ModelWidget::m_transparent = true;
|
||||
const QVector3D ModelWidget::m_cameraPosition = QVector3D(0, 0, -4.0);
|
||||
|
||||
ModelWidget::ModelWidget(QWidget *parent) :
|
||||
QOpenGLWidget(parent),
|
||||
|
@ -19,7 +20,8 @@ ModelWidget::ModelWidget(QWidget *parent) :
|
|||
m_program(nullptr),
|
||||
m_moveStarted(false),
|
||||
m_moveEnabled(true),
|
||||
m_zoomEnabled(true)
|
||||
m_zoomEnabled(true),
|
||||
m_mousePickingEnabled(false)
|
||||
{
|
||||
// --transparent causes the clear color to be transparent. Therefore, on systems that
|
||||
// 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.
|
||||
m_camera.setToIdentity();
|
||||
// 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.
|
||||
// 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->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_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);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if (m_meshBinder.isWireframesVisible())
|
||||
|
@ -217,11 +242,17 @@ bool ModelWidget::inputMouseReleaseEventFromOtherWidget(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) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QPoint pos = convertInputPosFromOtherWidget(event);
|
||||
int dx = pos.x() - m_lastPos.x();
|
||||
int dy = pos.y() - m_lastPos.y();
|
||||
|
||||
|
@ -275,6 +306,12 @@ void ModelWidget::zoom(float delta)
|
|||
setGeometry(geometry().marginsAdded(margins));
|
||||
}
|
||||
|
||||
void ModelWidget::setMousePickTargetPositionInModelSpace(QVector3D position)
|
||||
{
|
||||
m_mousePickTargetPositionInModelSpace = position;
|
||||
update();
|
||||
}
|
||||
|
||||
void ModelWidget::updateMesh(MeshLoader *mesh)
|
||||
{
|
||||
m_meshBinder.updateMesh(mesh);
|
||||
|
@ -291,6 +328,11 @@ void ModelWidget::enableZoom(bool enabled)
|
|||
m_zoomEnabled = enabled;
|
||||
}
|
||||
|
||||
void ModelWidget::enableMousePicking(bool enabled)
|
||||
{
|
||||
m_mousePickingEnabled = enabled;
|
||||
}
|
||||
|
||||
void ModelWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
inputMousePressEventFromOtherWidget(event);
|
||||
|
|
|
@ -17,6 +17,8 @@ class SkeletonGraphicsFunctions;
|
|||
class ModelWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
||||
{
|
||||
Q_OBJECT
|
||||
signals:
|
||||
void mouseRayChanged(const QVector3D &near, const QVector3D &far);
|
||||
public:
|
||||
ModelWidget(QWidget *parent = 0);
|
||||
~ModelWidget();
|
||||
|
@ -33,6 +35,7 @@ public:
|
|||
void toggleWireframe();
|
||||
void enableMove(bool enabled);
|
||||
void enableZoom(bool enabled);
|
||||
void enableMousePicking(bool enabled);
|
||||
bool inputMousePressEventFromOtherWidget(QMouseEvent *event);
|
||||
bool inputMouseMoveEventFromOtherWidget(QMouseEvent *event);
|
||||
bool inputWheelEventFromOtherWidget(QWheelEvent *event);
|
||||
|
@ -44,6 +47,7 @@ public slots:
|
|||
void setZRotation(int angle);
|
||||
void cleanup();
|
||||
void zoom(float delta);
|
||||
void setMousePickTargetPositionInModelSpace(QVector3D position);
|
||||
signals:
|
||||
void xRotationChanged(int angle);
|
||||
void yRotationChanged(int angle);
|
||||
|
@ -68,6 +72,8 @@ private:
|
|||
bool m_moveStarted;
|
||||
bool m_moveEnabled;
|
||||
bool m_zoomEnabled;
|
||||
bool m_mousePickingEnabled;
|
||||
QVector3D m_mousePickTargetPositionInModelSpace;
|
||||
private:
|
||||
QPoint m_lastPos;
|
||||
ModelMeshBinder m_meshBinder;
|
||||
|
@ -75,8 +81,10 @@ private:
|
|||
QMatrix4x4 m_camera;
|
||||
QMatrix4x4 m_world;
|
||||
static bool m_transparent;
|
||||
static const QVector3D m_cameraPosition;
|
||||
QPoint m_moveStartPos;
|
||||
QRect m_moveStartGeometry;
|
||||
std::pair<QVector3D, QVector3D> screenPositionToMouseRay(const QPoint &screenPosition);
|
||||
};
|
||||
|
||||
#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,
|
||||
Select,
|
||||
Paint,
|
||||
Drag,
|
||||
ZoomIn,
|
||||
ZoomOut
|
||||
|
|
|
@ -598,6 +598,9 @@ void SkeletonGraphicsWidget::updateCursor()
|
|||
case SkeletonDocumentEditMode::Select:
|
||||
setCursor(QCursor(Theme::awesome()->icon(fa::mousepointer).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize), Theme::toolIconFontSize / 5, 0));
|
||||
break;
|
||||
case SkeletonDocumentEditMode::Paint:
|
||||
setCursor(QCursor(Theme::awesome()->icon(fa::paintbrush).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize)));
|
||||
break;
|
||||
case SkeletonDocumentEditMode::Drag:
|
||||
setCursor(QCursor(Theme::awesome()->icon(m_dragStarted ? fa::handrocko : fa::handpapero).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize)));
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue