Add skeleton edit UI
parent
283e76b1b6
commit
d3e82ea306
12
dust3d.pro
12
dust3d.pro
|
@ -12,6 +12,18 @@ HEADERS += src/modelingwidget.h
|
||||||
SOURCES += src/skeletoneditwidget.cpp
|
SOURCES += src/skeletoneditwidget.cpp
|
||||||
HEADERS += src/skeletoneditwidget.h
|
HEADERS += src/skeletoneditwidget.h
|
||||||
|
|
||||||
|
SOURCES += src/skeletoneditgraphicsview.cpp
|
||||||
|
HEADERS += src/skeletoneditgraphicsview.h
|
||||||
|
|
||||||
|
SOURCES += src/skeletoneditnodeitem.cpp
|
||||||
|
HEADERS += src/skeletoneditnodeitem.h
|
||||||
|
|
||||||
|
SOURCES += src/skeletoneditedgeitem.cpp
|
||||||
|
HEADERS += src/skeletoneditedgeitem.h
|
||||||
|
|
||||||
|
SOURCES += src/skeletontomesh.cpp
|
||||||
|
HEADERS += src/skeletontomesh.h
|
||||||
|
|
||||||
SOURCES += src/mesh.cpp
|
SOURCES += src/mesh.cpp
|
||||||
HEADERS += src/mesh.h
|
HEADERS += src/mesh.h
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
#include "mainwindow.h"
|
|
||||||
#include "skeletoneditwidget.h"
|
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QButtonGroup>
|
#include <QButtonGroup>
|
||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
#include <QToolBar>
|
#include <QToolBar>
|
||||||
|
#include <QThread>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "skeletoneditwidget.h"
|
||||||
|
#include "meshlite.h"
|
||||||
|
#include "skeletontomesh.h"
|
||||||
|
|
||||||
MainWindow::MainWindow()
|
MainWindow::MainWindow()
|
||||||
{
|
{
|
||||||
|
@ -36,14 +40,15 @@ MainWindow::MainWindow()
|
||||||
motionButton->adjustSize();
|
motionButton->adjustSize();
|
||||||
modelButton->adjustSize();
|
modelButton->adjustSize();
|
||||||
|
|
||||||
SkeletonEditWidget *skeletonEditWidget = new SkeletonEditWidget;
|
m_skeletonEditWidget = new SkeletonEditWidget;
|
||||||
ModelingWidget *modelViewWidget = new ModelingWidget;
|
|
||||||
modelViewWidget->setFixedSize(128, 128);
|
m_modelingWidget = new ModelingWidget;
|
||||||
|
m_modelingWidget->setFixedSize(128, 128);
|
||||||
|
|
||||||
QPushButton *changeTurnaroundButton = new QPushButton("Change turnaround..");
|
QPushButton *changeTurnaroundButton = new QPushButton("Change turnaround..");
|
||||||
|
|
||||||
QVBoxLayout *rightLayout = new QVBoxLayout;
|
QVBoxLayout *rightLayout = new QVBoxLayout;
|
||||||
rightLayout->addWidget(modelViewWidget);
|
rightLayout->addWidget(m_modelingWidget);
|
||||||
rightLayout->addSpacing(10);
|
rightLayout->addSpacing(10);
|
||||||
rightLayout->addWidget(changeTurnaroundButton);
|
rightLayout->addWidget(changeTurnaroundButton);
|
||||||
rightLayout->addStretch();
|
rightLayout->addStretch();
|
||||||
|
@ -61,7 +66,7 @@ MainWindow::MainWindow()
|
||||||
|
|
||||||
QHBoxLayout *middleLayout = new QHBoxLayout;
|
QHBoxLayout *middleLayout = new QHBoxLayout;
|
||||||
middleLayout->addLayout(leftLayout);
|
middleLayout->addLayout(leftLayout);
|
||||||
middleLayout->addWidget(skeletonEditWidget);
|
middleLayout->addWidget(m_skeletonEditWidget);
|
||||||
middleLayout->addLayout(rightLayout);
|
middleLayout->addLayout(rightLayout);
|
||||||
|
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||||
|
@ -74,5 +79,36 @@ MainWindow::MainWindow()
|
||||||
|
|
||||||
setCentralWidget(centralWidget);
|
setCentralWidget(centralWidget);
|
||||||
setWindowTitle(tr("Dust 3D"));
|
setWindowTitle(tr("Dust 3D"));
|
||||||
|
|
||||||
|
bool connectResult;
|
||||||
|
|
||||||
|
connectResult = connect(addAction, SIGNAL(triggered(bool)), m_skeletonEditWidget->graphicsView(), SLOT(turnOnAddNodeMode()));
|
||||||
|
assert(connectResult);
|
||||||
|
|
||||||
|
connectResult = connectResult = connect(selectAction, SIGNAL(triggered(bool)), m_skeletonEditWidget->graphicsView(), SLOT(turnOffAddNodeMode()));
|
||||||
|
assert(connectResult);
|
||||||
|
|
||||||
|
connectResult = connect(m_skeletonEditWidget->graphicsView(), SIGNAL(nodesChanged()), this, SLOT(skeletonChanged()));
|
||||||
|
assert(connectResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::meshReady()
|
||||||
|
{
|
||||||
|
SkeletonToMesh *worker = dynamic_cast<SkeletonToMesh *>(sender());
|
||||||
|
if (worker) {
|
||||||
|
m_modelingWidget->updateMesh(worker->takeResultMesh());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::skeletonChanged()
|
||||||
|
{
|
||||||
|
QThread *thread = new QThread;
|
||||||
|
SkeletonToMesh *worker = new SkeletonToMesh(m_skeletonEditWidget->graphicsView());
|
||||||
|
worker->moveToThread(thread);
|
||||||
|
connect(thread, SIGNAL(started()), worker, SLOT(process()));
|
||||||
|
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
|
||||||
|
connect(worker, SIGNAL(finished()), this, SLOT(meshReady()));
|
||||||
|
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
|
||||||
|
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
|
||||||
|
thread->start();
|
||||||
|
}
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
#ifndef MAIN_WINDOW_H
|
#ifndef MAIN_WINDOW_H
|
||||||
#define MAIN_WINDOW_H
|
#define MAIN_WINDOW_H
|
||||||
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include "modelingwidget.h"
|
#include "modelingwidget.h"
|
||||||
|
#include "skeletoneditwidget.h"
|
||||||
|
|
||||||
class MainWindow : public QMainWindow
|
class MainWindow : public QMainWindow
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
MainWindow();
|
MainWindow();
|
||||||
|
public slots:
|
||||||
|
void skeletonChanged();
|
||||||
|
void meshReady();
|
||||||
private:
|
private:
|
||||||
ModelingWidget *modelingWidget;
|
ModelingWidget *m_modelingWidget;
|
||||||
|
SkeletonEditWidget *m_skeletonEditWidget;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
#include <QOpenGLShaderProgram>
|
#include <QOpenGLShaderProgram>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include "meshlite.h"
|
|
||||||
|
|
||||||
// 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
|
||||||
|
|
||||||
|
@ -15,7 +14,9 @@ ModelingWidget::ModelingWidget(QWidget *parent)
|
||||||
m_yRot(0),
|
m_yRot(0),
|
||||||
m_zRot(0),
|
m_zRot(0),
|
||||||
m_program(0),
|
m_program(0),
|
||||||
m_mesh(NULL)
|
m_renderVertexCount(0),
|
||||||
|
m_mesh(NULL),
|
||||||
|
m_meshUpdated(false)
|
||||||
{
|
{
|
||||||
m_core = QSurfaceFormat::defaultFormat().profile() == QSurfaceFormat::CoreProfile;
|
m_core = QSurfaceFormat::defaultFormat().profile() == QSurfaceFormat::CoreProfile;
|
||||||
// --transparent causes the clear color to be transparent. Therefore, on systems that
|
// --transparent causes the clear color to be transparent. Therefore, on systems that
|
||||||
|
@ -25,16 +26,6 @@ ModelingWidget::ModelingWidget(QWidget *parent)
|
||||||
fmt.setAlphaBufferSize(8);
|
fmt.setAlphaBufferSize(8);
|
||||||
setFormat(fmt);
|
setFormat(fmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *lite = meshlite_create_context();
|
|
||||||
int first = meshlite_import(lite, "../assets/cube.obj");
|
|
||||||
int second = meshlite_import(lite, "../assets/ball.obj");
|
|
||||||
meshlite_scale(lite, first, 0.65);
|
|
||||||
int merged = meshlite_union(lite, first, second);
|
|
||||||
int triangulate = meshlite_triangulate(lite, merged);
|
|
||||||
//meshlite_export(lite, triangulate, "/Users/jeremy/testlib.obj");
|
|
||||||
Mesh *mesh = new Mesh(lite, triangulate);
|
|
||||||
updateMesh(mesh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ModelingWidget::~ModelingWidget()
|
ModelingWidget::~ModelingWidget()
|
||||||
|
@ -96,7 +87,8 @@ void ModelingWidget::cleanup()
|
||||||
if (m_program == nullptr)
|
if (m_program == nullptr)
|
||||||
return;
|
return;
|
||||||
makeCurrent();
|
makeCurrent();
|
||||||
m_modelVbo.destroy();
|
if (m_modelVbo.isCreated())
|
||||||
|
m_modelVbo.destroy();
|
||||||
delete m_program;
|
delete m_program;
|
||||||
m_program = 0;
|
m_program = 0;
|
||||||
doneCurrent();
|
doneCurrent();
|
||||||
|
@ -169,7 +161,8 @@ void ModelingWidget::initializeGL()
|
||||||
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ModelingWidget::cleanup);
|
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ModelingWidget::cleanup);
|
||||||
|
|
||||||
initializeOpenGLFunctions();
|
initializeOpenGLFunctions();
|
||||||
glClearColor(0.2078, 0.2078, 0.2078, m_transparent ? 0 : 1);
|
QColor bgcolor = QWidget::palette().color(QWidget::backgroundRole());
|
||||||
|
glClearColor(bgcolor.redF(), bgcolor.greenF(), bgcolor.blueF(), m_transparent ? 0 : 1);
|
||||||
|
|
||||||
m_program = new QOpenGLShaderProgram;
|
m_program = new QOpenGLShaderProgram;
|
||||||
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, m_core ? vertexShaderSourceCore : vertexShaderSource);
|
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, m_core ? vertexShaderSourceCore : vertexShaderSource);
|
||||||
|
@ -189,15 +182,10 @@ void ModelingWidget::initializeGL()
|
||||||
// at all. Nonetheless the below code works in all cases and makes
|
// at all. Nonetheless the below code works in all cases and makes
|
||||||
// sure there is a VAO when one is needed.
|
// sure there is a VAO when one is needed.
|
||||||
m_vao.create();
|
m_vao.create();
|
||||||
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
|
//QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
|
||||||
|
|
||||||
// Setup our vertex buffer object.
|
|
||||||
m_modelVbo.create();
|
|
||||||
m_modelVbo.bind();
|
|
||||||
m_modelVbo.allocate(m_mesh->vertices(), m_mesh->vertexCount() * sizeof(Vertex));
|
|
||||||
|
|
||||||
// Store the vertex attribute bindings for the program.
|
// Store the vertex attribute bindings for the program.
|
||||||
setupVertexAttribs();
|
|
||||||
|
|
||||||
// Our camera never changes in this example.
|
// Our camera never changes in this example.
|
||||||
m_camera.setToIdentity();
|
m_camera.setToIdentity();
|
||||||
|
@ -232,13 +220,34 @@ void ModelingWidget::paintGL()
|
||||||
m_world.rotate(m_zRot / 16.0f, 0, 0, 1);
|
m_world.rotate(m_zRot / 16.0f, 0, 0, 1);
|
||||||
|
|
||||||
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
|
QOpenGLVertexArrayObject::Binder vaoBinder(&m_vao);
|
||||||
|
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&m_meshMutex);
|
||||||
|
if (m_meshUpdated) {
|
||||||
|
if (m_mesh) {
|
||||||
|
// Setup our vertex buffer object.
|
||||||
|
if (m_modelVbo.isCreated())
|
||||||
|
m_modelVbo.destroy();
|
||||||
|
m_modelVbo.create();
|
||||||
|
m_modelVbo.bind();
|
||||||
|
m_modelVbo.allocate(m_mesh->vertices(), m_mesh->vertexCount() * sizeof(Vertex));
|
||||||
|
m_renderVertexCount = m_mesh->vertexCount();
|
||||||
|
setupVertexAttribs();
|
||||||
|
} else {
|
||||||
|
m_renderVertexCount = 0;
|
||||||
|
}
|
||||||
|
m_meshUpdated = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_program->bind();
|
m_program->bind();
|
||||||
m_program->setUniformValue(m_projMatrixLoc, m_proj);
|
m_program->setUniformValue(m_projMatrixLoc, m_proj);
|
||||||
m_program->setUniformValue(m_mvMatrixLoc, m_camera * m_world);
|
m_program->setUniformValue(m_mvMatrixLoc, m_camera * m_world);
|
||||||
QMatrix3x3 normalMatrix = m_world.normalMatrix();
|
QMatrix3x3 normalMatrix = m_world.normalMatrix();
|
||||||
m_program->setUniformValue(m_normalMatrixLoc, normalMatrix);
|
m_program->setUniformValue(m_normalMatrixLoc, normalMatrix);
|
||||||
|
|
||||||
glDrawArrays(GL_TRIANGLES, 0, m_mesh->vertexCount());
|
if (m_renderVertexCount > 0)
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, m_renderVertexCount);
|
||||||
|
|
||||||
m_program->release();
|
m_program->release();
|
||||||
}
|
}
|
||||||
|
@ -275,6 +284,8 @@ void ModelingWidget::updateMesh(Mesh *mesh)
|
||||||
if (mesh != m_mesh) {
|
if (mesh != m_mesh) {
|
||||||
delete m_mesh;
|
delete m_mesh;
|
||||||
m_mesh = mesh;
|
m_mesh = mesh;
|
||||||
|
m_meshUpdated = true;
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@ private:
|
||||||
QOpenGLVertexArrayObject m_vao;
|
QOpenGLVertexArrayObject m_vao;
|
||||||
QOpenGLBuffer m_modelVbo;
|
QOpenGLBuffer m_modelVbo;
|
||||||
QOpenGLShaderProgram *m_program;
|
QOpenGLShaderProgram *m_program;
|
||||||
|
int m_renderVertexCount;
|
||||||
int m_projMatrixLoc;
|
int m_projMatrixLoc;
|
||||||
int m_mvMatrixLoc;
|
int m_mvMatrixLoc;
|
||||||
int m_normalMatrixLoc;
|
int m_normalMatrixLoc;
|
||||||
|
@ -66,6 +67,7 @@ private:
|
||||||
|
|
||||||
Mesh *m_mesh;
|
Mesh *m_mesh;
|
||||||
QMutex m_meshMutex;
|
QMutex m_meshMutex;
|
||||||
|
bool m_meshUpdated;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
#include <QPen>
|
||||||
|
#include "skeletoneditedgeitem.h"
|
||||||
|
|
||||||
|
SkeletonEditEdgeItem::SkeletonEditEdgeItem(QGraphicsItem *parent) :
|
||||||
|
QGraphicsLineItem(parent),
|
||||||
|
m_firstNode(NULL),
|
||||||
|
m_secondNode(NULL)
|
||||||
|
{
|
||||||
|
QPen pen(Qt::darkGray);
|
||||||
|
pen.setWidth(15);
|
||||||
|
setPen(pen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonEditEdgeItem::setNodes(SkeletonEditNodeItem *first, SkeletonEditNodeItem *second)
|
||||||
|
{
|
||||||
|
m_firstNode = first;
|
||||||
|
m_secondNode = second;
|
||||||
|
updatePosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonEditEdgeItem::updatePosition()
|
||||||
|
{
|
||||||
|
if (m_firstNode && m_secondNode) {
|
||||||
|
QLineF line(m_firstNode->origin(), m_secondNode->origin());
|
||||||
|
setLine(line);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef SKELETON_EDIT_EDGE_ITEM_H
|
||||||
|
#define SKELETON_EDIT_EDGE_ITEM_H
|
||||||
|
#include <QGraphicsEllipseItem>
|
||||||
|
#include "skeletoneditnodeitem.h"
|
||||||
|
|
||||||
|
class SkeletonEditEdgeItem : public QGraphicsLineItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SkeletonEditEdgeItem(QGraphicsItem *parent = 0);
|
||||||
|
void setNodes(SkeletonEditNodeItem *first, SkeletonEditNodeItem *second);
|
||||||
|
void updatePosition();
|
||||||
|
private:
|
||||||
|
SkeletonEditNodeItem *m_firstNode;
|
||||||
|
SkeletonEditNodeItem *m_secondNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,181 @@
|
||||||
|
#include <QGraphicsPixmapItem>
|
||||||
|
#include "skeletoneditgraphicsview.h"
|
||||||
|
#include "skeletoneditnodeitem.h"
|
||||||
|
#include "skeletoneditedgeitem.h"
|
||||||
|
|
||||||
|
qreal SkeletonEditGraphicsView::m_initialNodeSize = 128;
|
||||||
|
qreal SkeletonEditGraphicsView::m_minimalNodeSize = 32;
|
||||||
|
|
||||||
|
SkeletonEditGraphicsView::SkeletonEditGraphicsView(QWidget *parent) :
|
||||||
|
QGraphicsView(parent),
|
||||||
|
m_pendingNodeItem(NULL),
|
||||||
|
m_pendingEdgeItem(NULL),
|
||||||
|
m_inAddNodeMode(true),
|
||||||
|
m_nextStartNodeItem(NULL),
|
||||||
|
m_lastHoverNodeItem(NULL)
|
||||||
|
{
|
||||||
|
setScene(new QGraphicsScene());
|
||||||
|
|
||||||
|
setMouseTracking(true);
|
||||||
|
|
||||||
|
QImage image("../assets/male_werewolf_turnaround_lineart_by_jennette_brown.png");
|
||||||
|
m_backgroundItem = new QGraphicsPixmapItem(QPixmap::fromImage(image));
|
||||||
|
scene()->addItem(m_backgroundItem);
|
||||||
|
|
||||||
|
m_pendingNodeItem = new QGraphicsEllipseItem(0, 0, m_initialNodeSize, m_initialNodeSize);
|
||||||
|
scene()->addItem(m_pendingNodeItem);
|
||||||
|
|
||||||
|
m_pendingEdgeItem = new QGraphicsLineItem(0, 0, 0, 0);
|
||||||
|
scene()->addItem(m_pendingEdgeItem);
|
||||||
|
|
||||||
|
applyAddNodeMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonEditGraphicsView::toggleAddNodeMode()
|
||||||
|
{
|
||||||
|
m_inAddNodeMode = !m_inAddNodeMode;
|
||||||
|
applyAddNodeMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonEditGraphicsView::applyAddNodeMode()
|
||||||
|
{
|
||||||
|
m_pendingNodeItem->setVisible(m_inAddNodeMode);
|
||||||
|
m_pendingEdgeItem->setVisible(m_inAddNodeMode);
|
||||||
|
setMouseTracking(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonEditGraphicsView::turnOffAddNodeMode()
|
||||||
|
{
|
||||||
|
m_inAddNodeMode = false;
|
||||||
|
applyAddNodeMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonEditGraphicsView::turnOnAddNodeMode()
|
||||||
|
{
|
||||||
|
m_inAddNodeMode = true;
|
||||||
|
applyAddNodeMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
SkeletonEditNodeItem *SkeletonEditGraphicsView::findNodeItemByPos(QPointF pos)
|
||||||
|
{
|
||||||
|
QList<QGraphicsItem *>::iterator it;
|
||||||
|
QList<QGraphicsItem *> list = scene()->items();
|
||||||
|
for (it = list.begin(); it != list.end(); ++it) {
|
||||||
|
SkeletonEditNodeItem *nodeItem = dynamic_cast<SkeletonEditNodeItem *>(*it);
|
||||||
|
if (nodeItem) {
|
||||||
|
SkeletonEditNodeItem *nodeItem = (SkeletonEditNodeItem *)(*it);
|
||||||
|
if (nodeItem->rect().contains(pos)) {
|
||||||
|
return nodeItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonEditGraphicsView::mousePressEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if (event->button() == Qt::LeftButton) {
|
||||||
|
if (!m_inAddNodeMode) {
|
||||||
|
if (m_lastHoverNodeItem) {
|
||||||
|
setNextStartNodeItem(m_lastHoverNodeItem);
|
||||||
|
m_lastHoverNodeItem = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonEditGraphicsView::mouseReleaseEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if (event->button() == Qt::LeftButton) {
|
||||||
|
if (m_inAddNodeMode) {
|
||||||
|
SkeletonEditNodeItem *newNode = new SkeletonEditNodeItem(m_pendingNodeItem->rect());
|
||||||
|
scene()->addItem(newNode);
|
||||||
|
if (m_nextStartNodeItem) {
|
||||||
|
SkeletonEditEdgeItem *newEdge = new SkeletonEditEdgeItem();
|
||||||
|
newEdge->setNodes(newNode, m_nextStartNodeItem);
|
||||||
|
scene()->addItem(newEdge);
|
||||||
|
}
|
||||||
|
setNextStartNodeItem(newNode);
|
||||||
|
emit nodesChanged();
|
||||||
|
}
|
||||||
|
} else if (event->button() == Qt::RightButton) {
|
||||||
|
if (m_inAddNodeMode) {
|
||||||
|
toggleAddNodeMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonEditGraphicsView::mouseMoveEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
QWidget::mouseMoveEvent(event);
|
||||||
|
QPointF pos = mapToScene(event->pos());
|
||||||
|
QPointF moveTo = QPointF(pos.x() - m_pendingNodeItem->rect().width() / 2, pos.y() - m_pendingNodeItem->rect().height() / 2);
|
||||||
|
if (moveTo.x() < 0)
|
||||||
|
moveTo.setX(0);
|
||||||
|
if (moveTo.y() < 0)
|
||||||
|
moveTo.setY(0);
|
||||||
|
if (moveTo.x() + m_pendingNodeItem->rect().width() >= m_backgroundItem->boundingRect().width())
|
||||||
|
moveTo.setX(m_backgroundItem->boundingRect().width() - m_pendingNodeItem->rect().width());
|
||||||
|
if (moveTo.y() + m_pendingNodeItem->rect().height() >= m_backgroundItem->boundingRect().height())
|
||||||
|
moveTo.setY(m_backgroundItem->boundingRect().height() - m_pendingNodeItem->rect().height());
|
||||||
|
QSizeF oldSize = m_pendingNodeItem->rect().size();
|
||||||
|
m_pendingNodeItem->setRect(moveTo.x(), moveTo.y(),
|
||||||
|
oldSize.width(),
|
||||||
|
oldSize.height());
|
||||||
|
if (m_nextStartNodeItem) {
|
||||||
|
m_pendingEdgeItem->setLine(QLineF(m_nextStartNodeItem->origin(), QPointF(moveTo.x() + m_pendingNodeItem->rect().width() / 2,
|
||||||
|
moveTo.y() + m_pendingNodeItem->rect().height() / 2)));
|
||||||
|
}
|
||||||
|
if (!m_inAddNodeMode) {
|
||||||
|
SkeletonEditNodeItem *hoverNodeItem = findNodeItemByPos(pos);
|
||||||
|
if (hoverNodeItem) {
|
||||||
|
hoverNodeItem->setHighlighted(true);
|
||||||
|
}
|
||||||
|
if (hoverNodeItem != m_lastHoverNodeItem) {
|
||||||
|
if (m_lastHoverNodeItem)
|
||||||
|
m_lastHoverNodeItem->setHighlighted(false);
|
||||||
|
m_lastHoverNodeItem = hoverNodeItem;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (m_lastHoverNodeItem) {
|
||||||
|
m_lastHoverNodeItem->setHighlighted(false);
|
||||||
|
m_lastHoverNodeItem = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonEditGraphicsView::wheelEvent(QWheelEvent *event)
|
||||||
|
{
|
||||||
|
QWidget::wheelEvent(event);
|
||||||
|
qreal delta = event->delta();
|
||||||
|
QSizeF oldSize = m_pendingNodeItem->rect().size();
|
||||||
|
QPointF originPt = QPointF(m_pendingNodeItem->rect().left() + oldSize.width() / 2,
|
||||||
|
m_pendingNodeItem->rect().top() + oldSize.height() / 2);
|
||||||
|
QSizeF newSize = QSizeF(oldSize.width() + delta, oldSize.height() + delta);
|
||||||
|
if (newSize.width() < m_minimalNodeSize || newSize.height() < m_minimalNodeSize) {
|
||||||
|
newSize.setWidth(m_minimalNodeSize);
|
||||||
|
newSize.setHeight(m_minimalNodeSize);
|
||||||
|
}
|
||||||
|
QPointF newLeftTop = QPointF(originPt.x() - newSize.width() / 2,
|
||||||
|
originPt.y() - newSize.height() / 2);
|
||||||
|
if (newLeftTop.x() < 0 || newLeftTop.x() + newSize.width() >= m_backgroundItem->boundingRect().width())
|
||||||
|
return;
|
||||||
|
if (newLeftTop.y() < 0 || newLeftTop.y() + newSize.height() >= m_backgroundItem->boundingRect().height())
|
||||||
|
return;
|
||||||
|
m_pendingNodeItem->setRect(newLeftTop.x(),
|
||||||
|
newLeftTop.y(),
|
||||||
|
newSize.width(),
|
||||||
|
newSize.height());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonEditGraphicsView::setNextStartNodeItem(SkeletonEditNodeItem *item)
|
||||||
|
{
|
||||||
|
if (m_nextStartNodeItem != item) {
|
||||||
|
if (m_nextStartNodeItem)
|
||||||
|
m_nextStartNodeItem->setIsNextStartNode(false);
|
||||||
|
}
|
||||||
|
m_nextStartNodeItem = item;
|
||||||
|
if (m_nextStartNodeItem)
|
||||||
|
m_nextStartNodeItem->setIsNextStartNode(true);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
#ifndef SKELETON_EDIT_GRAPHICS_VIEW_H
|
||||||
|
#define SKELETON_EDIT_GRAPHICS_VIEW_H
|
||||||
|
#include <QGraphicsView>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include "skeletoneditnodeitem.h"
|
||||||
|
#include "skeletoneditedgeitem.h"
|
||||||
|
|
||||||
|
class SkeletonEditGraphicsView : public QGraphicsView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
signals:
|
||||||
|
void nodesChanged();
|
||||||
|
public slots:
|
||||||
|
void turnOffAddNodeMode();
|
||||||
|
void turnOnAddNodeMode();
|
||||||
|
public:
|
||||||
|
SkeletonEditGraphicsView(QWidget *parent = 0);
|
||||||
|
protected:
|
||||||
|
void mouseMoveEvent(QMouseEvent *event);
|
||||||
|
void wheelEvent(QWheelEvent *event);
|
||||||
|
void mouseReleaseEvent(QMouseEvent *event);
|
||||||
|
void mousePressEvent(QMouseEvent *event);
|
||||||
|
private:
|
||||||
|
QGraphicsPixmapItem *m_backgroundItem;
|
||||||
|
QGraphicsEllipseItem *m_pendingNodeItem;
|
||||||
|
QGraphicsLineItem *m_pendingEdgeItem;
|
||||||
|
static qreal m_initialNodeSize;
|
||||||
|
static qreal m_minimalNodeSize;
|
||||||
|
bool m_inAddNodeMode;
|
||||||
|
SkeletonEditNodeItem *m_nextStartNodeItem;
|
||||||
|
SkeletonEditNodeItem *m_lastHoverNodeItem;
|
||||||
|
void toggleAddNodeMode();
|
||||||
|
void applyAddNodeMode();
|
||||||
|
SkeletonEditNodeItem *findNodeItemByPos(QPointF pos);
|
||||||
|
void setNextStartNodeItem(SkeletonEditNodeItem *item);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,39 @@
|
||||||
|
#include <QPen>
|
||||||
|
#include "skeletoneditnodeitem.h"
|
||||||
|
|
||||||
|
SkeletonEditNodeItem::SkeletonEditNodeItem(const QRectF &rect, QGraphicsItem *parent) :
|
||||||
|
QGraphicsEllipseItem(rect, parent),
|
||||||
|
m_highlighted(false),
|
||||||
|
m_isNextStartNode(false)
|
||||||
|
{
|
||||||
|
updateBorder();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF SkeletonEditNodeItem::origin()
|
||||||
|
{
|
||||||
|
return QPointF(rect().left() + rect().width() / 2,
|
||||||
|
rect().top() + rect().height() / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonEditNodeItem::setHighlighted(bool highlighted)
|
||||||
|
{
|
||||||
|
m_highlighted = highlighted;
|
||||||
|
if (m_highlighted) {
|
||||||
|
setBrush(QBrush(Qt::gray));
|
||||||
|
} else {
|
||||||
|
setBrush(QBrush(Qt::transparent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonEditNodeItem::setIsNextStartNode(bool isNextStartNode)
|
||||||
|
{
|
||||||
|
m_isNextStartNode = isNextStartNode;
|
||||||
|
updateBorder();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonEditNodeItem::updateBorder()
|
||||||
|
{
|
||||||
|
QPen pen(m_isNextStartNode ? Qt::black : Qt::darkGray);
|
||||||
|
pen.setWidth(15);
|
||||||
|
setPen(pen);
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef SKELETON_EDIT_NODE_ITEM_H
|
||||||
|
#define SKELETON_EDIT_NODE_ITEM_H
|
||||||
|
#include <QGraphicsEllipseItem>
|
||||||
|
|
||||||
|
class SkeletonEditNodeItem : public QGraphicsEllipseItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SkeletonEditNodeItem(const QRectF &rect, QGraphicsItem *parent = 0);
|
||||||
|
QPointF origin();
|
||||||
|
void setHighlighted(bool highlited);
|
||||||
|
void setIsNextStartNode(bool isNextStartNode);
|
||||||
|
private:
|
||||||
|
bool m_highlighted;
|
||||||
|
bool m_isNextStartNode;
|
||||||
|
void updateBorder();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -6,23 +6,34 @@
|
||||||
|
|
||||||
// Modifed from http://doc.qt.io/qt-5/qtwidgets-graphicsview-chip-view-cpp.html
|
// Modifed from http://doc.qt.io/qt-5/qtwidgets-graphicsview-chip-view-cpp.html
|
||||||
|
|
||||||
SkeletonEditWidget::SkeletonEditWidget(QFrame *parent)
|
SkeletonEditWidget::SkeletonEditWidget(QFrame *parent) :
|
||||||
: QFrame(parent)
|
QFrame(parent)
|
||||||
{
|
{
|
||||||
//setFrameStyle(Sunken | StyledPanel);
|
m_graphicsView = new SkeletonEditGraphicsView(this);
|
||||||
|
m_graphicsView->setRenderHint(QPainter::Antialiasing, false);
|
||||||
|
m_graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
m_graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
|
||||||
graphicsView = new QGraphicsView(this);
|
m_graphicsView->setBackgroundBrush(QBrush(QWidget::palette().color(QWidget::backgroundRole()), Qt::SolidPattern));
|
||||||
graphicsView->setRenderHint(QPainter::Antialiasing, false);
|
|
||||||
|
|
||||||
scene = new QGraphicsScene();
|
|
||||||
graphicsView->setScene(scene);
|
|
||||||
|
|
||||||
QImage image("../assets/male_werewolf_turnaround_lineart_by_jennette_brown.png");
|
|
||||||
QGraphicsPixmapItem *backgroundItem = new QGraphicsPixmapItem(QPixmap::fromImage(image));
|
|
||||||
scene->addItem(backgroundItem);
|
|
||||||
|
|
||||||
QGridLayout *mainLayout = new QGridLayout;
|
QGridLayout *mainLayout = new QGridLayout;
|
||||||
mainLayout->addWidget(graphicsView, 0, 0, 1, 1);
|
mainLayout->addWidget(m_graphicsView, 0, 0, 1, 1);
|
||||||
|
|
||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SkeletonEditGraphicsView *SkeletonEditWidget::graphicsView()
|
||||||
|
{
|
||||||
|
return m_graphicsView;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonEditWidget::resizeEvent(QResizeEvent *event)
|
||||||
|
{
|
||||||
|
QFrame::resizeEvent(event);
|
||||||
|
m_graphicsView->fitInView(QRectF(0, 0, m_graphicsView->scene()->width(), m_graphicsView->scene()->height()), Qt::KeepAspectRatio);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonEditWidget::mouseMoveEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
QFrame::mouseMoveEvent(event);
|
||||||
|
}
|
||||||
|
|
|
@ -3,15 +3,22 @@
|
||||||
#include <QFrame>
|
#include <QFrame>
|
||||||
#include <QGraphicsView>
|
#include <QGraphicsView>
|
||||||
#include <QGraphicsScene>
|
#include <QGraphicsScene>
|
||||||
|
#include <QGraphicsPixmapItem>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QGraphicsEllipseItem>
|
||||||
|
#include "skeletoneditgraphicsview.h"
|
||||||
|
|
||||||
class SkeletonEditWidget : public QFrame
|
class SkeletonEditWidget : public QFrame
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
SkeletonEditWidget(QFrame *parent = 0);
|
SkeletonEditWidget(QFrame *parent = 0);
|
||||||
|
SkeletonEditGraphicsView *graphicsView();
|
||||||
|
protected:
|
||||||
|
void resizeEvent(QResizeEvent *event);
|
||||||
|
void mouseMoveEvent(QMouseEvent *event);
|
||||||
private:
|
private:
|
||||||
QGraphicsView *graphicsView;
|
SkeletonEditGraphicsView *m_graphicsView;
|
||||||
QGraphicsScene *scene;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
#include "skeletontomesh.h"
|
||||||
|
#include "meshlite.h"
|
||||||
|
|
||||||
|
// Modified from https://wiki.qt.io/QThreads_general_usage
|
||||||
|
|
||||||
|
SkeletonToMesh::SkeletonToMesh(SkeletonEditGraphicsView *graphicsView) :
|
||||||
|
m_mesh(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SkeletonToMesh::~SkeletonToMesh()
|
||||||
|
{
|
||||||
|
delete m_mesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesh *SkeletonToMesh::takeResultMesh()
|
||||||
|
{
|
||||||
|
Mesh *mesh = m_mesh;
|
||||||
|
m_mesh = NULL;
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonToMesh::process()
|
||||||
|
{
|
||||||
|
void *lite = meshlite_create_context();
|
||||||
|
int first = meshlite_import(lite, "../assets/cube.obj");
|
||||||
|
int second = meshlite_import(lite, "../assets/ball.obj");
|
||||||
|
meshlite_scale(lite, first, 0.65);
|
||||||
|
int merged = meshlite_union(lite, first, second);
|
||||||
|
int triangulate = meshlite_triangulate(lite, merged);
|
||||||
|
//meshlite_export(lite, triangulate, "/Users/jeremy/testlib.obj");
|
||||||
|
m_mesh = new Mesh(lite, triangulate);
|
||||||
|
emit finished();
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef SKELETON_TO_MESH_H
|
||||||
|
#define SKELETON_TO_MESH_H
|
||||||
|
#include <QObject>
|
||||||
|
#include <QList>
|
||||||
|
#include "skeletoneditgraphicsview.h"
|
||||||
|
#include "mesh.h"
|
||||||
|
|
||||||
|
class SkeletonToMesh : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
SkeletonToMesh(SkeletonEditGraphicsView *graphicsView);
|
||||||
|
~SkeletonToMesh();
|
||||||
|
Mesh *takeResultMesh();
|
||||||
|
signals:
|
||||||
|
void finished();
|
||||||
|
public slots:
|
||||||
|
void process();
|
||||||
|
private:
|
||||||
|
Mesh *m_mesh;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue