From 14bce78a4a4963707d75e3e51c92636a736095db Mon Sep 17 00:00:00 2001 From: Jeremy Hu Date: Mon, 12 Mar 2018 13:27:22 +0800 Subject: [PATCH] Add skeleton to mesh generation(finished one side) --- .gitignore | 1 + src/mainwindow.cpp | 30 ++++++--- src/mainwindow.h | 3 + src/skeletoneditedgeitem.cpp | 11 ++++ src/skeletoneditedgeitem.h | 2 + src/skeletoneditgraphicsview.cpp | 5 +- src/skeletoneditnodeitem.cpp | 6 ++ src/skeletoneditnodeitem.h | 1 + src/skeletontomesh.cpp | 109 ++++++++++++++++++++++++++++--- src/skeletontomesh.h | 20 ++++++ 10 files changed, 165 insertions(+), 23 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..27a652ce --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +moc_* diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 281d5906..48fd887f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -10,7 +10,9 @@ #include "meshlite.h" #include "skeletontomesh.h" -MainWindow::MainWindow() +MainWindow::MainWindow() : + m_skeletonToMesh(NULL), + m_skeletonDirty(false) { QPushButton *skeletonButton = new QPushButton("Skeleton"); QPushButton *motionButton = new QPushButton("Motion"); @@ -94,21 +96,29 @@ MainWindow::MainWindow() void MainWindow::meshReady() { - SkeletonToMesh *worker = dynamic_cast(sender()); - if (worker) { - m_modelingWidget->updateMesh(worker->takeResultMesh()); + m_modelingWidget->updateMesh(m_skeletonToMesh->takeResultMesh()); + delete m_skeletonToMesh; + m_skeletonToMesh = NULL; + if (m_skeletonDirty) { + skeletonChanged(); } } void MainWindow::skeletonChanged() { + if (m_skeletonToMesh) { + m_skeletonDirty = true; + return; + } + + m_skeletonDirty = false; + 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())); + m_skeletonToMesh = new SkeletonToMesh(m_skeletonEditWidget->graphicsView()); + m_skeletonToMesh->moveToThread(thread); + connect(thread, SIGNAL(started()), m_skeletonToMesh, SLOT(process())); + connect(m_skeletonToMesh, SIGNAL(finished()), this, SLOT(meshReady())); + connect(m_skeletonToMesh, SIGNAL(finished()), thread, SLOT(quit())); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); thread->start(); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 66a0baa1..543d8773 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -3,6 +3,7 @@ #include #include "modelingwidget.h" #include "skeletoneditwidget.h" +#include "skeletontomesh.h" class MainWindow : public QMainWindow { @@ -15,6 +16,8 @@ public slots: private: ModelingWidget *m_modelingWidget; SkeletonEditWidget *m_skeletonEditWidget; + SkeletonToMesh *m_skeletonToMesh; + bool m_skeletonDirty; }; #endif diff --git a/src/skeletoneditedgeitem.cpp b/src/skeletoneditedgeitem.cpp index 993f41dd..6a6423d1 100644 --- a/src/skeletoneditedgeitem.cpp +++ b/src/skeletoneditedgeitem.cpp @@ -6,6 +6,7 @@ SkeletonEditEdgeItem::SkeletonEditEdgeItem(QGraphicsItem *parent) : m_firstNode(NULL), m_secondNode(NULL) { + setData(0, "edge"); QPen pen(Qt::darkGray); pen.setWidth(15); setPen(pen); @@ -25,3 +26,13 @@ void SkeletonEditEdgeItem::updatePosition() setLine(line); } } + +SkeletonEditNodeItem *SkeletonEditEdgeItem::firstNode() +{ + return m_firstNode; +} + +SkeletonEditNodeItem *SkeletonEditEdgeItem::secondNode() +{ + return m_secondNode; +} diff --git a/src/skeletoneditedgeitem.h b/src/skeletoneditedgeitem.h index d3cb203d..d797022f 100644 --- a/src/skeletoneditedgeitem.h +++ b/src/skeletoneditedgeitem.h @@ -9,6 +9,8 @@ public: SkeletonEditEdgeItem(QGraphicsItem *parent = 0); void setNodes(SkeletonEditNodeItem *first, SkeletonEditNodeItem *second); void updatePosition(); + SkeletonEditNodeItem *firstNode(); + SkeletonEditNodeItem *secondNode(); private: SkeletonEditNodeItem *m_firstNode; SkeletonEditNodeItem *m_secondNode; diff --git a/src/skeletoneditgraphicsview.cpp b/src/skeletoneditgraphicsview.cpp index 8f7ea98d..864cdab5 100644 --- a/src/skeletoneditgraphicsview.cpp +++ b/src/skeletoneditgraphicsview.cpp @@ -61,9 +61,8 @@ SkeletonEditNodeItem *SkeletonEditGraphicsView::findNodeItemByPos(QPointF pos) QList::iterator it; QList list = scene()->items(); for (it = list.begin(); it != list.end(); ++it) { - SkeletonEditNodeItem *nodeItem = dynamic_cast(*it); - if (nodeItem) { - SkeletonEditNodeItem *nodeItem = (SkeletonEditNodeItem *)(*it); + if ((*it)->data(0).toString() == "node") { + SkeletonEditNodeItem *nodeItem = static_cast(*it); if (nodeItem->rect().contains(pos)) { return nodeItem; } diff --git a/src/skeletoneditnodeitem.cpp b/src/skeletoneditnodeitem.cpp index 084c35a4..5ab15323 100644 --- a/src/skeletoneditnodeitem.cpp +++ b/src/skeletoneditnodeitem.cpp @@ -6,6 +6,7 @@ SkeletonEditNodeItem::SkeletonEditNodeItem(const QRectF &rect, QGraphicsItem *pa m_highlighted(false), m_isNextStartNode(false) { + setData(0, "node"); updateBorder(); } @@ -15,6 +16,11 @@ QPointF SkeletonEditNodeItem::origin() rect().top() + rect().height() / 2); } +float SkeletonEditNodeItem::radius() +{ + return rect().width() / 2; +} + void SkeletonEditNodeItem::setHighlighted(bool highlighted) { m_highlighted = highlighted; diff --git a/src/skeletoneditnodeitem.h b/src/skeletoneditnodeitem.h index dc231d35..24ca91f5 100644 --- a/src/skeletoneditnodeitem.h +++ b/src/skeletoneditnodeitem.h @@ -7,6 +7,7 @@ class SkeletonEditNodeItem : public QGraphicsEllipseItem public: SkeletonEditNodeItem(const QRectF &rect, QGraphicsItem *parent = 0); QPointF origin(); + float radius(); void setHighlighted(bool highlited); void setIsNextStartNode(bool isNextStartNode); private: diff --git a/src/skeletontomesh.cpp b/src/skeletontomesh.cpp index 2ac8b5e0..b1df87f7 100644 --- a/src/skeletontomesh.cpp +++ b/src/skeletontomesh.cpp @@ -1,11 +1,63 @@ #include "skeletontomesh.h" #include "meshlite.h" +#include "skeletoneditnodeitem.h" +#include "skeletoneditedgeitem.h" // Modified from https://wiki.qt.io/QThreads_general_usage -SkeletonToMesh::SkeletonToMesh(SkeletonEditGraphicsView *graphicsView) : - m_mesh(NULL) +struct NodeItemInfo { + int index; + int neighborCount; +}; + +SkeletonToMesh::SkeletonToMesh(SkeletonEditGraphicsView *graphicsView) : + m_mesh(NULL), m_rootNodeId(0) +{ + QList::iterator it; + QList list = graphicsView->scene()->items(); + std::map nodeItemsMap; + int maxNeighborCount = 0; + for (it = list.begin(); it != list.end(); ++it) { + if ((*it)->data(0).toString() == "edge") { + SkeletonEditEdgeItem *edgeItem = static_cast(*it); + SkeletonEditNodeItem *nodeItems[] = {edgeItem->firstNode(), edgeItem->secondNode()}; + int nodeIndices[] = {0, 0}; + for (int i = 0; i < 2; i++) { + SkeletonEditNodeItem *nodeItem = nodeItems[i]; + std::map::iterator findResult = nodeItemsMap.find(nodeItem); + if (findResult == nodeItemsMap.end()) { + SkeletonNode node; + NodeItemInfo info; + QPointF origin = nodeItem->origin(); + + node.originX = origin.x(); + node.originY = origin.y(); + node.originZ = 0; + node.bmeshNodeId = -1; + node.radius = nodeItem->radius(); + + info.index = m_nodes.size(); + info.neighborCount = 1; + + nodeIndices[i] = info.index; + nodeItemsMap[nodeItem] = info; + m_nodes.push_back(node); + } else { + nodeIndices[i] = findResult->second.index; + findResult->second.neighborCount++; + if (findResult->second.neighborCount > maxNeighborCount) { + m_rootNodeId = findResult->second.index; + maxNeighborCount = findResult->second.neighborCount; + } + } + } + SkeletonEdge edge; + edge.firstNode = nodeIndices[0]; + edge.secondNode = nodeIndices[1]; + m_edges.push_back(edge); + } + } } SkeletonToMesh::~SkeletonToMesh() @@ -22,13 +74,50 @@ Mesh *SkeletonToMesh::takeResultMesh() 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); + if (m_nodes.size() <= 1) { + emit finished(); + return; + } + float left = -1; + float top = -1; + float bottom = -1; + for (size_t i = 0; i < m_nodes.size(); i++) { + SkeletonNode *node = &m_nodes[i]; + if (left < 0 || node->originX < left) { + left = node->originX; + } + if (top < 0 || node->originY < top) { + top = node->originY; + } + if (node->originY > bottom) { + bottom = node->originY; + } + } + float height = bottom - top; + if (height <= 0) { + emit finished(); + return; + } + void *context = meshlite_create_context(); + int bmesh = meshlite_bmesh_create(context); + for (size_t i = 0; i < m_nodes.size(); i++) { + SkeletonNode *node = &m_nodes[i]; + float x = (node->originX - left) / height; + float y = (node->originY - top) / height; + float z = node->originZ / height; + float r = node->radius / height; + node->bmeshNodeId = meshlite_bmesh_add_node(context, bmesh, x, y, z, r); + } + for (size_t i = 0; i < m_edges.size(); i++) { + SkeletonNode *firstNode = &m_nodes[m_edges[i].firstNode]; + SkeletonNode *secondNode = &m_nodes[m_edges[i].secondNode]; + meshlite_bmesh_add_edge(context, bmesh, firstNode->bmeshNodeId, secondNode->bmeshNodeId); + } + int mesh = meshlite_bmesh_generate_mesh(context, bmesh, m_nodes[m_rootNodeId].bmeshNodeId); + meshlite_bmesh_destroy(context, bmesh); + int triangulate = meshlite_triangulate(context, mesh); + meshlite_export(context, triangulate, "/Users/jeremy/testlib.obj"); + m_mesh = new Mesh(context, triangulate); + meshlite_destroy_context(context); emit finished(); } diff --git a/src/skeletontomesh.h b/src/skeletontomesh.h index cb2ac951..51397bb7 100644 --- a/src/skeletontomesh.h +++ b/src/skeletontomesh.h @@ -2,9 +2,26 @@ #define SKELETON_TO_MESH_H #include #include +#include +#include #include "skeletoneditgraphicsview.h" #include "mesh.h" +struct SkeletonNode +{ + float originX; + float originY; + float originZ; + float radius; + int bmeshNodeId; +}; + +struct SkeletonEdge +{ + int firstNode; + int secondNode; +}; + class SkeletonToMesh : public QObject { Q_OBJECT @@ -18,6 +35,9 @@ public slots: void process(); private: Mesh *m_mesh; + std::vector m_nodes; + std::vector m_edges; + int m_rootNodeId; }; #endif