From 4c9767835fac7ffc8ee0cdd74835361c6d44fb72 Mon Sep 17 00:00:00 2001 From: Jeremy Hu Date: Tue, 20 Mar 2018 15:56:49 +0800 Subject: [PATCH] Export working model as obj --- dust3d.pro | 4 +- src/mainwindow.cpp | 18 ++++++- src/mainwindow.h | 1 + src/mesh.cpp | 36 ++++++++++++++ src/mesh.h | 6 +++ src/{modelingwidget.cpp => modelwidget.cpp} | 52 +++++++++++++++------ src/{modelingwidget.h => modelwidget.h} | 11 +++-- src/skeletonwidget.cpp | 25 ++++++---- src/skeletonwidget.h | 5 +- 9 files changed, 122 insertions(+), 36 deletions(-) rename src/{modelingwidget.cpp => modelwidget.cpp} (86%) rename src/{modelingwidget.h => modelwidget.h} (87%) diff --git a/dust3d.pro b/dust3d.pro index ee051c37..f5164288 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -7,8 +7,8 @@ INCLUDEPATH += src SOURCES += src/mainwindow.cpp HEADERS += src/mainwindow.h -SOURCES += src/modelingwidget.cpp -HEADERS += src/modelingwidget.h +SOURCES += src/modelwidget.cpp +HEADERS += src/modelwidget.h SOURCES += src/skeletoneditgraphicsview.cpp HEADERS += src/skeletoneditgraphicsview.h diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3776f3e1..1aaca8a9 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -69,8 +69,8 @@ MainWindow::MainWindow() QPushButton *changeTurnaroundButton = new QPushButton(" Change Turnaround "); modelRightLayout->addWidget(changeTurnaroundButton); - QPushButton *exportPartsModelButton = new QPushButton(" Export "); - modelRightLayout->addWidget(exportPartsModelButton); + QPushButton *exportModelButton = new QPushButton(" Export "); + modelRightLayout->addWidget(exportModelButton); QPushButton *newModelButton = new QPushButton(" New "); modelRightLayout->addWidget(newModelButton); @@ -130,6 +130,9 @@ MainWindow::MainWindow() connectResult = connect(m_sharePageButton, SIGNAL(clicked()), this, SLOT(updatePageButtons())); assert(connectResult); + connectResult = connect(exportModelButton, SIGNAL(clicked()), this, SLOT(exportModel())); + assert(connectResult); + connectResult = connect(saveModelButton, SIGNAL(clicked()), this, SLOT(saveModel())); assert(connectResult); @@ -139,6 +142,17 @@ MainWindow::MainWindow() updatePageButtons(); } +void MainWindow::exportModel() +{ + QString exportTo = QFileDialog::getSaveFileName(this, + tr("Export Model"), ".", + tr("Wavefront OBJ File (*.obj)")); + if (exportTo.isEmpty()) { + return; + } + m_skeletonWidget->modelWidget()->exportMeshAsObj(exportTo); +} + void MainWindow::loadModel() { QString filename = QFileDialog::getOpenFileName(this, diff --git a/src/mainwindow.h b/src/mainwindow.h index 9022b198..d3b473c6 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -16,6 +16,7 @@ public slots: void updatePageButtons(); void saveModel(); void loadModel(); + void exportModel(); private: QPushButton *m_modelPageButton; QPushButton *m_sharePageButton; diff --git a/src/mesh.cpp b/src/mesh.cpp index ce000229..68d9ce7b 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -2,6 +2,8 @@ #include "meshlite.h" #include +#define MAX_VERTICES_PER_FACE 100 + Mesh::Mesh(void *meshlite, int meshId) : m_triangleVertices(NULL), m_triangleVertexCount(0), @@ -12,6 +14,30 @@ Mesh::Mesh(void *meshlite, int meshId) : GLfloat *edgeVertexPositions = new GLfloat[edgeVertexPositionCount * 3]; int loadedEdgeVertexPositionItemCount = meshlite_get_vertex_position_array(meshlite, meshId, edgeVertexPositions, edgeVertexPositionCount * 3); + int offset = 0; + while (offset < loadedEdgeVertexPositionItemCount) { + QVector3D position = QVector3D(edgeVertexPositions[offset], edgeVertexPositions[offset + 1], edgeVertexPositions[offset + 2]); + m_vertices.push_back(position); + offset += 3; + } + int faceCount = meshlite_get_face_count(meshlite, meshId); + int *faceVertexNumAndIndices = new int[faceCount * (1 + MAX_VERTICES_PER_FACE)]; + int loadedFaceVertexNumAndIndicesItemCount = meshlite_get_face_index_array(meshlite, meshId, faceVertexNumAndIndices, faceCount * (1 + MAX_VERTICES_PER_FACE)); + offset = 0; + while (offset < loadedFaceVertexNumAndIndicesItemCount) { + int indicesNum = faceVertexNumAndIndices[offset++]; + assert(indicesNum >= 0 && indicesNum <= MAX_VERTICES_PER_FACE); + std::vector face; + for (int i = 0; i < indicesNum && offset < loadedFaceVertexNumAndIndicesItemCount; i++) { + int index = faceVertexNumAndIndices[offset++]; + assert(index >= 0 && index < loadedEdgeVertexPositionItemCount); + face.push_back(index); + } + m_faces.push_back(face); + } + delete[] faceVertexNumAndIndices; + faceVertexNumAndIndices = NULL; + int edgeCount = meshlite_get_halfedge_count(meshlite, meshId); int *edgeIndices = new int[edgeCount * 2]; int loadedEdgeVertexIndexItemCount = meshlite_get_halfedge_index_array(meshlite, meshId, edgeIndices, edgeCount * 2); @@ -89,6 +115,16 @@ Mesh::~Mesh() m_triangleVertexCount = 0; } +const std::vector &Mesh::vertices() +{ + return m_vertices; +} + +const std::vector> &Mesh::faces() +{ + return m_faces; +} + Vertex *Mesh::triangleVertices() { return m_triangleVertices; diff --git a/src/mesh.h b/src/mesh.h index f726368f..fb3a64a6 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -2,6 +2,8 @@ #define MESH_H #include #include +#include +#include #pragma pack(push) #pragma pack(1) @@ -28,11 +30,15 @@ public: int triangleVertexCount(); Vertex *edgeVertices(); int edgeVertexCount(); + const std::vector &vertices(); + const std::vector> &faces(); private: Vertex *m_triangleVertices; int m_triangleVertexCount; Vertex *m_edgeVertices; int m_edgeVertexCount; + std::vector m_vertices; + std::vector> m_faces; }; #endif diff --git a/src/modelingwidget.cpp b/src/modelwidget.cpp similarity index 86% rename from src/modelingwidget.cpp rename to src/modelwidget.cpp index a426ef2c..6635b664 100644 --- a/src/modelingwidget.cpp +++ b/src/modelwidget.cpp @@ -1,4 +1,5 @@ -#include "modelingwidget.h" +#include "modelwidget.h" +#include "ds3file.h" #include #include #include @@ -6,9 +7,9 @@ // Modifed from http://doc.qt.io/qt-5/qtopengl-hellogl2-glwidget-cpp.html -bool ModelingWidget::m_transparent = false; +bool ModelWidget::m_transparent = false; -ModelingWidget::ModelingWidget(QWidget *parent) +ModelWidget::ModelWidget(QWidget *parent) : QOpenGLWidget(parent), m_xRot(0), m_yRot(0), @@ -34,7 +35,7 @@ ModelingWidget::ModelingWidget(QWidget *parent) } } -ModelingWidget::~ModelingWidget() +ModelWidget::~ModelWidget() { cleanup(); delete m_mesh; @@ -48,7 +49,7 @@ static void qNormalizeAngle(int &angle) angle -= 360 * 16; } -void ModelingWidget::setXRotation(int angle) +void ModelWidget::setXRotation(int angle) { qNormalizeAngle(angle); if (angle != m_xRot) { @@ -58,7 +59,7 @@ void ModelingWidget::setXRotation(int angle) } } -void ModelingWidget::setYRotation(int angle) +void ModelWidget::setYRotation(int angle) { qNormalizeAngle(angle); if (angle != m_yRot) { @@ -68,7 +69,7 @@ void ModelingWidget::setYRotation(int angle) } } -void ModelingWidget::setZRotation(int angle) +void ModelWidget::setZRotation(int angle) { qNormalizeAngle(angle); if (angle != m_zRot) { @@ -78,7 +79,7 @@ void ModelingWidget::setZRotation(int angle) } } -void ModelingWidget::cleanup() +void ModelWidget::cleanup() { if (m_program == nullptr) return; @@ -153,7 +154,7 @@ static const char *fragmentShaderSource = " gl_FragColor = vec4(col, 1.0);\n" "}\n"; -void ModelingWidget::initializeGL() +void ModelWidget::initializeGL() { // In this example the widget's corresponding top-level window can change // several times during the widget's lifetime. Whenever this happens, the @@ -162,7 +163,7 @@ void ModelingWidget::initializeGL() // aboutToBeDestroyed() signal, instead of the destructor. The emission of // the signal will be followed by an invocation of initializeGL() where we // can recreate all resources. - connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ModelingWidget::cleanup); + connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ModelWidget::cleanup); initializeOpenGLFunctions(); QColor bgcolor = QWidget::palette().color(QWidget::backgroundRole()); @@ -199,7 +200,7 @@ void ModelingWidget::initializeGL() m_program->release(); } -void ModelingWidget::paintGL() +void ModelWidget::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); @@ -275,18 +276,18 @@ void ModelingWidget::paintGL() m_program->release(); } -void ModelingWidget::resizeGL(int w, int h) +void ModelWidget::resizeGL(int w, int h) { m_proj.setToIdentity(); m_proj.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f); } -void ModelingWidget::mousePressEvent(QMouseEvent *event) +void ModelWidget::mousePressEvent(QMouseEvent *event) { m_lastPos = event->pos(); } -void ModelingWidget::mouseMoveEvent(QMouseEvent *event) +void ModelWidget::mouseMoveEvent(QMouseEvent *event) { int dx = event->x() - m_lastPos.x(); int dy = event->y() - m_lastPos.y(); @@ -301,7 +302,7 @@ void ModelingWidget::mouseMoveEvent(QMouseEvent *event) m_lastPos = event->pos(); } -void ModelingWidget::updateMesh(Mesh *mesh) +void ModelWidget::updateMesh(Mesh *mesh) { QMutexLocker lock(&m_meshMutex); if (mesh != m_mesh) { @@ -312,3 +313,24 @@ void ModelingWidget::updateMesh(Mesh *mesh) } } +void ModelWidget::exportMeshAsObj(const QString &filename) +{ + QMutexLocker lock(&m_meshMutex); + if (m_mesh) { + QFile file(filename); + if (file.open(QIODevice::WriteOnly)) { + QTextStream stream(&file); + stream << "# " << Ds3FileReader::m_applicationName << endl; + for (std::vector::iterator it = m_mesh->vertices().begin() ; it != m_mesh->vertices().end(); ++it) { + stream << "v " << (*it).x() << " " << (*it).y() << " " << (*it).z() << endl; + } + for (std::vector>::iterator it = m_mesh->faces().begin() ; it != m_mesh->faces().end(); ++it) { + stream << "f"; + for (std::vector::iterator subIt = (*it).begin() ; subIt != (*it).end(); ++subIt) { + stream << " " << (1 + *subIt); + } + stream << endl; + } + } + } +} diff --git a/src/modelingwidget.h b/src/modelwidget.h similarity index 87% rename from src/modelingwidget.h rename to src/modelwidget.h index fb06925c..195eb3b2 100644 --- a/src/modelingwidget.h +++ b/src/modelwidget.h @@ -1,5 +1,5 @@ -#ifndef MODELING_WIDGET_H -#define MODELING_WIDGET_H +#ifndef MODEL_WIDGET_H +#define MODEL_WIDGET_H #include #include @@ -11,18 +11,19 @@ QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram) -class ModelingWidget : public QOpenGLWidget, protected QOpenGLFunctions +class ModelWidget : public QOpenGLWidget, protected QOpenGLFunctions { Q_OBJECT public: - ModelingWidget(QWidget *parent = 0); - ~ModelingWidget(); + ModelWidget(QWidget *parent = 0); + ~ModelWidget(); static bool isTransparent() { return m_transparent; } static void setTransparent(bool t) { m_transparent = t; } void updateMesh(Mesh *mesh); + void exportMeshAsObj(const QString &filename); public slots: void setXRotation(int angle); diff --git a/src/skeletonwidget.cpp b/src/skeletonwidget.cpp index d683c8c4..501cdd26 100644 --- a/src/skeletonwidget.cpp +++ b/src/skeletonwidget.cpp @@ -30,11 +30,11 @@ SkeletonWidget::SkeletonWidget(QWidget *parent) : m_graphicsView->setBackgroundBrush(QBrush(QWidget::palette().color(QWidget::backgroundRole()), Qt::SolidPattern)); - m_modelingWidget = new ModelingWidget(this); - m_modelingWidget->setMinimumSize(128, 128); - m_modelingWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - m_modelingWidget->setWindowFlags(Qt::Tool | Qt::Window); - m_modelingWidget->setWindowTitle("3D Model"); + m_modelWidget = new ModelWidget(this); + m_modelWidget->setMinimumSize(128, 128); + m_modelWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + m_modelWidget->setWindowFlags(Qt::Tool | Qt::Window); + m_modelWidget->setWindowTitle("3D Model"); QVBoxLayout *rightLayout = new QVBoxLayout; rightLayout->addSpacing(10); @@ -101,14 +101,19 @@ SkeletonEditGraphicsView *SkeletonWidget::graphicsView() return m_graphicsView; } +ModelWidget *SkeletonWidget::modelWidget() +{ + return m_modelWidget; +} + void SkeletonWidget::showModelingWidgetAtCorner() { - if (!m_modelingWidget->isVisible()) { + if (!m_modelWidget->isVisible()) { QPoint pos = QPoint(QApplication::desktop()->width(), QApplication::desktop()->height()); - m_modelingWidget->move(pos.x() - m_modelingWidget->width(), - pos.y() - m_modelingWidget->height()); - m_modelingWidget->show(); + m_modelWidget->move(pos.x() - m_modelWidget->width(), + pos.y() - m_modelWidget->height()); + m_modelWidget->show(); } } @@ -116,7 +121,7 @@ void SkeletonWidget::meshReady() { Mesh *resultMesh = m_skeletonToMesh->takeResultMesh(); showModelingWidgetAtCorner(); - m_modelingWidget->updateMesh(resultMesh); + m_modelWidget->updateMesh(resultMesh); delete m_skeletonToMesh; m_skeletonToMesh = NULL; if (m_skeletonDirty) { diff --git a/src/skeletonwidget.h b/src/skeletonwidget.h index ed56eec1..73796e8f 100644 --- a/src/skeletonwidget.h +++ b/src/skeletonwidget.h @@ -4,7 +4,7 @@ #include #include #include -#include "modelingwidget.h" +#include "modelwidget.h" #include "skeletontomesh.h" #include "turnaroundloader.h" #include "skeletoneditgraphicsview.h" @@ -15,6 +15,7 @@ class SkeletonWidget : public QWidget public: SkeletonWidget(QWidget *parent=0); SkeletonEditGraphicsView *graphicsView(); + ModelWidget *modelWidget(); public slots: void skeletonChanged(); void meshReady(); @@ -23,7 +24,7 @@ public slots: void changeTurnaround(); void showModelingWidgetAtCorner(); private: - ModelingWidget *m_modelingWidget; + ModelWidget *m_modelWidget; SkeletonEditGraphicsView *m_graphicsView; SkeletonToMesh *m_skeletonToMesh; bool m_skeletonDirty;