diff --git a/dust3d.pro b/dust3d.pro index f7121bc1..f0c20d94 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -194,6 +194,12 @@ HEADERS += src/posepreviewsgenerator.h SOURCES += src/posewidget.cpp HEADERS += src/posewidget.h +SOURCES += src/meshweldseam.cpp +HEADERS += src/meshweldseam.h + +SOURCES += src/advancesettingwidget.cpp +HEADERS += src/advancesettingwidget.h + SOURCES += src/main.cpp HEADERS += src/version.h diff --git a/src/advancesettingwidget.cpp b/src/advancesettingwidget.cpp new file mode 100644 index 00000000..90dca994 --- /dev/null +++ b/src/advancesettingwidget.cpp @@ -0,0 +1,22 @@ +#include +#include +#include "advancesettingwidget.h" +#include "dust3dutil.h" + +AdvanceSettingWidget::AdvanceSettingWidget(const SkeletonDocument *document, QWidget *parent) : + QDialog(parent), + m_document(document) +{ + QCheckBox *enableWeldBox = new QCheckBox(); + enableWeldBox->setChecked(document->weldEnabled); + connect(enableWeldBox, &QCheckBox::stateChanged, this, [=]() { + emit enableWeld(enableWeldBox->isChecked()); + }); + + QFormLayout *formLayout = new QFormLayout; + formLayout->addRow(tr("Weld"), enableWeldBox); + + setLayout(formLayout); + + connect(this, &AdvanceSettingWidget::enableWeld, document, &SkeletonDocument::enableWeld); +} diff --git a/src/advancesettingwidget.h b/src/advancesettingwidget.h new file mode 100644 index 00000000..e9d3261a --- /dev/null +++ b/src/advancesettingwidget.h @@ -0,0 +1,17 @@ +#ifndef ADVANCE_SETTING_WIDGET_H +#define ADVANCE_SETTING_WIDGET_H +#include +#include "skeletondocument.h" + +class AdvanceSettingWidget : public QDialog +{ + Q_OBJECT +signals: + void enableWeld(bool enabled); +public: + AdvanceSettingWidget(const SkeletonDocument *document, QWidget *parent=nullptr); +private: + const SkeletonDocument *m_document = nullptr; +}; + +#endif diff --git a/src/exportpreviewwidget.cpp b/src/exportpreviewwidget.cpp index 5ad41448..7368d7e5 100644 --- a/src/exportpreviewwidget.cpp +++ b/src/exportpreviewwidget.cpp @@ -120,6 +120,9 @@ void ExportPreviewWidget::showEvent(QShowEvent *event) { QWidget::showEvent(event); checkSpinner(); + if (m_document->isPostProcessResultObsolete()) { + m_document->postProcess(); + } } void ExportPreviewWidget::checkSpinner() diff --git a/src/meshgenerator.cpp b/src/meshgenerator.cpp index 8b876f4b..e055fa0f 100644 --- a/src/meshgenerator.cpp +++ b/src/meshgenerator.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "meshgenerator.h" #include "dust3dutil.h" #include "skeletondocument.h" @@ -9,6 +10,7 @@ #include "theme.h" #include "positionmap.h" #include "meshquadify.h" +#include "meshweldseam.h" bool MeshGenerator::m_enableDebug = false; PositionMap *MeshGenerator::m_forMakePositionKey = new PositionMap; @@ -31,12 +33,12 @@ void GeneratedCacheContext::updateComponentCombinableMesh(QString componentId, v MeshGenerator::MeshGenerator(SkeletonSnapshot *snapshot, QThread *thread) : m_snapshot(snapshot), m_mesh(nullptr), - //m_preview(nullptr), m_thread(thread), m_meshResultContext(nullptr), m_sharedContextWidget(nullptr), m_cacheContext(nullptr), - m_smoothNormal(true) + m_smoothNormal(true), + m_weldEnabled(true) { } @@ -55,6 +57,11 @@ void MeshGenerator::setSmoothNormal(bool smoothNormal) m_smoothNormal = smoothNormal; } +void MeshGenerator::setWeldEnabled(bool weldEnabled) +{ + m_weldEnabled = weldEnabled; +} + void MeshGenerator::setGeneratedCacheContext(GeneratedCacheContext *cacheContext) { m_cacheContext = cacheContext; @@ -474,10 +481,8 @@ void *MeshGenerator::combineComponentMesh(QString componentId, bool *inverse) continue; bool childInverse = false; void *childCombinedMesh = combineComponentMesh(childId, &childInverse); - if (smoothSeam) { - for (const auto &positionIt: m_cacheContext->componentPositions[childId]) { - positionsBeforeCombination.addPosition(positionIt.x(), positionIt.y(), positionIt.z(), true); - } + for (const auto &positionIt: m_cacheContext->componentPositions[childId]) { + positionsBeforeCombination.addPosition(positionIt.x(), positionIt.y(), positionIt.z(), true); } for (const auto &verticesSourceIt: m_cacheContext->componentVerticesSources[childId].map()) { verticesSources.map()[verticesSourceIt.first] = verticesSourceIt.second; @@ -502,33 +507,55 @@ void *MeshGenerator::combineComponentMesh(QString componentId, bool *inverse) } if (nullptr != resultMesh) { - if (smoothSeam || smoothAll) { - int meshIdForSmooth = convertFromCombinableMesh(m_meshliteContext, resultMesh); - std::vector positionsBeforeSmooth; - loadMeshVerticesPositions(m_meshliteContext, meshIdForSmooth, positionsBeforeSmooth); + int meshIdForSmooth = convertFromCombinableMesh(m_meshliteContext, resultMesh); + std::vector positionsBeforeSmooth; + loadMeshVerticesPositions(m_meshliteContext, meshIdForSmooth, positionsBeforeSmooth); + + if (!positionsBeforeSmooth.empty()) { + std::vector seamVerticesIds; + std::unordered_set seamVerticesIndicies; - if (!positionsBeforeSmooth.empty()) { - - if (smoothSeam) { - int *seamVerticesIndicies = new int[positionsBeforeSmooth.size()]; - int seamVerticesNum = 0; - for (size_t vertexIndex = 0; vertexIndex < positionsBeforeSmooth.size(); vertexIndex++) { - const auto &oldPosition = positionsBeforeSmooth[vertexIndex]; - if (!positionsBeforeCombination.findPosition(oldPosition.x(), oldPosition.y(), oldPosition.z())) { - seamVerticesIndicies[seamVerticesNum++] = vertexIndex + 1; + if (!positionsBeforeCombination.map().empty()) { + for (size_t vertexIndex = 0; vertexIndex < positionsBeforeSmooth.size(); vertexIndex++) { + const auto &oldPosition = positionsBeforeSmooth[vertexIndex]; + if (!positionsBeforeCombination.findPosition(oldPosition.x(), oldPosition.y(), oldPosition.z())) { + seamVerticesIds.push_back(vertexIndex + 1); + seamVerticesIndicies.insert(vertexIndex); + } + } + } + + bool meshChanged = false; + if (m_weldEnabled) { + if (!seamVerticesIndicies.empty()) { + int weldedMeshId = meshWeldSeam(m_meshliteContext, meshIdForSmooth, 0.025, seamVerticesIndicies); + { + void *testCombinableMesh = convertToCombinableMesh(m_meshliteContext, weldedMeshId); + if (nullptr != testCombinableMesh) { + deleteCombinableMesh(testCombinableMesh); + meshIdForSmooth = weldedMeshId; + meshChanged = true; + } else { + qDebug() << "Weld seam failed, fall back"; } } - if (seamVerticesNum > 0) { - //qDebug() << "smoothSeamFactor:" << smoothSeamFactor << "seamVerticesIndicies.size():" << seamVerticesNum; - meshlite_smooth_vertices(m_meshliteContext, meshIdForSmooth, smoothSeamFactor, seamVerticesIndicies, seamVerticesNum); - } - delete[] seamVerticesIndicies; } - - if (smoothAll) { - meshlite_smooth(m_meshliteContext, meshIdForSmooth, smoothAllFactor); + } + + if (smoothSeam) { + if (!seamVerticesIds.empty()) { + //qDebug() << "smoothSeamFactor:" << smoothSeamFactor << "seamVerticesIndicies.size():" << seamVerticesNum; + meshlite_smooth_vertices(m_meshliteContext, meshIdForSmooth, smoothSeamFactor, seamVerticesIds.data(), seamVerticesIds.size()); + meshChanged = true; } - + } + + if (smoothAll) { + meshlite_smooth(m_meshliteContext, meshIdForSmooth, smoothAllFactor); + meshChanged = true; + } + + if (meshChanged) { std::vector positionsAfterSmooth; loadMeshVerticesPositions(m_meshliteContext, meshIdForSmooth, positionsAfterSmooth); Q_ASSERT(positionsBeforeSmooth.size() == positionsAfterSmooth.size()); @@ -543,7 +570,6 @@ void *MeshGenerator::combineComponentMesh(QString componentId, bool *inverse) verticesSources.addPosition(smoothedPosition.x(), smoothedPosition.y(), smoothedPosition.z(), source); } } - deleteCombinableMesh(resultMesh); resultMesh = convertToCombinableMesh(m_meshliteContext, meshIdForSmooth); } @@ -655,12 +681,6 @@ void MeshGenerator::process() bmeshNodes.second.begin(), bmeshNodes.second.end()); } - //if (resultMeshId > 0) { - // resultMeshId = meshlite_combine_coplanar_faces(m_meshliteContext, resultMeshId); - // if (resultMeshId > 0) - // resultMeshId = meshlite_fix_hole(m_meshliteContext, resultMeshId); - //} - int triangulatedFinalMeshId = resultMeshId; if (triangulatedFinalMeshId > 0) { std::set> sharedQuadEdges; @@ -676,8 +696,6 @@ void MeshGenerator::process() } if (resultMeshId > 0) { - //int triangulatedFinalMeshId = meshlite_triangulate(m_meshliteContext, resultMeshId); - //triangulatedFinalMeshId = resultMeshId; loadGeneratedPositionsToMeshResultContext(m_meshliteContext, triangulatedFinalMeshId); m_mesh = new MeshLoader(m_meshliteContext, resultMeshId, triangulatedFinalMeshId, Theme::white, &m_meshResultContext->triangleColors(), m_smoothNormal); } diff --git a/src/meshgenerator.h b/src/meshgenerator.h index 77207129..8b9e3d7a 100644 --- a/src/meshgenerator.h +++ b/src/meshgenerator.h @@ -36,6 +36,7 @@ public: void addPartPreviewRequirement(const QUuid &partId); void setGeneratedCacheContext(GeneratedCacheContext *cacheContext); void setSmoothNormal(bool smoothNormal); + void setWeldEnabled(bool weldEnabled); MeshLoader *takeResultMesh(); MeshLoader *takePartPreviewMesh(const QUuid &partId); const std::set &requirePreviewPartIds(); @@ -57,6 +58,7 @@ private: void *m_meshliteContext; GeneratedCacheContext *m_cacheContext; bool m_smoothNormal; + bool m_weldEnabled; float m_mainProfileMiddleX; float m_sideProfileMiddleX; float m_mainProfileMiddleY; diff --git a/src/meshquadify.cpp b/src/meshquadify.cpp index cc7f17f6..143d7c98 100644 --- a/src/meshquadify.cpp +++ b/src/meshquadify.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "meshquadify.h" #include "meshlite.h" @@ -11,12 +12,12 @@ int meshQuadify(void *meshlite, int meshId, const std::set positionKeyMap; + std::vector positionKeyMap; for (int i = 0; i < vertexCount; i++) { float x = vertexPositions[offset + 0]; float y = vertexPositions[offset + 1]; float z = vertexPositions[offset + 2]; - positionKeyMap[i] = positionMapForMakeKey->makeKey(x, y, z); + positionKeyMap.push_back(positionMapForMakeKey->makeKey(x, y, z)); offset += 3; } int faceCount = meshlite_get_face_count(meshlite, meshId); diff --git a/src/meshweldseam.cpp b/src/meshweldseam.cpp new file mode 100644 index 00000000..d65c47d9 --- /dev/null +++ b/src/meshweldseam.cpp @@ -0,0 +1,159 @@ +#include +#include +#include +#include +#include +#include +#include "meshweldseam.h" +#include "meshutil.h" + +int meshWeldSeam(void *meshlite, int meshId, float allowedSmallestDistance, const std::unordered_set &seamVerticesIndicies) +{ + int vertexCount = meshlite_get_vertex_count(meshlite, meshId); + float *vertexPositions = new float[vertexCount * 3]; + int vertexArrayLen = meshlite_get_vertex_position_array(meshlite, meshId, vertexPositions, vertexCount * 3); + int offset = 0; + Q_ASSERT(vertexArrayLen == vertexCount * 3); + std::vector positions; + for (int i = 0; i < vertexCount; i++) { + float x = vertexPositions[offset + 0]; + float y = vertexPositions[offset + 1]; + float z = vertexPositions[offset + 2]; + positions.push_back(QVector3D(x, y, z)); + offset += 3; + } + int faceCount = meshlite_get_face_count(meshlite, meshId); + int *faceVertexNumAndIndices = new int[faceCount * MAX_VERTICES_PER_FACE]; + int filledLength = meshlite_get_face_index_array(meshlite, meshId, faceVertexNumAndIndices, faceCount * MAX_VERTICES_PER_FACE); + int i = 0; + std::vector> newFaceIndicies; + while (i < filledLength) { + int num = faceVertexNumAndIndices[i++]; + Q_ASSERT(num > 0 && num <= MAX_VERTICES_PER_FACE); + if (num < 3) { + i += num; + continue; + } + std::vector indices; + for (int j = 0; j < num; j++) { + int index = faceVertexNumAndIndices[i++]; + Q_ASSERT(index >= 0 && index < vertexCount); + indices.push_back(index); + } + newFaceIndicies.push_back(indices); + } + float squareOfAllowedSmallestDistance = allowedSmallestDistance * allowedSmallestDistance; + int weldedMesh = 0; + std::map weldVertexToMap; + std::unordered_set weldTargetVertices; + std::unordered_set processedFaces; + std::map, std::pair> triangleEdgeMap; + std::unordered_map vertexAdjFaceCountMap; + for (int i = 0; i < (int)newFaceIndicies.size(); i++) { + const auto &faceIndicies = newFaceIndicies[i]; + if (faceIndicies.size() == 3) { + vertexAdjFaceCountMap[faceIndicies[0]]++; + vertexAdjFaceCountMap[faceIndicies[1]]++; + vertexAdjFaceCountMap[faceIndicies[2]]++; + triangleEdgeMap[std::make_pair(faceIndicies[0], faceIndicies[1])] = std::make_pair(i, faceIndicies[2]); + triangleEdgeMap[std::make_pair(faceIndicies[1], faceIndicies[2])] = std::make_pair(i, faceIndicies[0]); + triangleEdgeMap[std::make_pair(faceIndicies[2], faceIndicies[0])] = std::make_pair(i, faceIndicies[1]); + } + } + for (int i = 0; i < (int)newFaceIndicies.size(); i++) { + if (processedFaces.find(i) != processedFaces.end()) + continue; + const auto &faceIndicies = newFaceIndicies[i]; + if (faceIndicies.size() == 3) { + bool indiciesSeamCheck[3] = { + seamVerticesIndicies.empty() || seamVerticesIndicies.find(faceIndicies[0]) != seamVerticesIndicies.end(), + seamVerticesIndicies.empty() || seamVerticesIndicies.find(faceIndicies[1]) != seamVerticesIndicies.end(), + seamVerticesIndicies.empty() || seamVerticesIndicies.find(faceIndicies[2]) != seamVerticesIndicies.end() + }; + for (int j = 0; j < 3; j++) { + int next = (j + 1) % 3; + int nextNext = (j + 2) % 3; + if (indiciesSeamCheck[j] && indiciesSeamCheck[next]) { + std::pair edge = std::make_pair(faceIndicies[j], faceIndicies[next]); + int thirdVertexIndex = faceIndicies[nextNext]; + if ((positions[edge.first] - positions[edge.second]).lengthSquared() < squareOfAllowedSmallestDistance) { + auto oppositeEdge = std::make_pair(edge.second, edge.first); + auto findOppositeFace = triangleEdgeMap.find(oppositeEdge); + if (findOppositeFace == triangleEdgeMap.end()) { + qDebug() << "Find opposite edge failed"; + continue; + } + int oppositeFaceIndex = findOppositeFace->second.first; + // Weld on the longer edge vertex + if (((positions[edge.first] - positions[thirdVertexIndex]).lengthSquared() < + (positions[edge.second] - positions[thirdVertexIndex]).lengthSquared()) && + vertexAdjFaceCountMap[edge.second] <= 4 && + weldVertexToMap.find(edge.second) == weldVertexToMap.end()) { + weldVertexToMap[edge.second] = edge.first; + weldTargetVertices.insert(edge.first); + processedFaces.insert(i); + processedFaces.insert(oppositeFaceIndex); + break; + } else if (vertexAdjFaceCountMap[edge.first] <= 4 && + weldVertexToMap.find(edge.first) == weldVertexToMap.end()) { + weldVertexToMap[edge.first] = edge.second; + weldTargetVertices.insert(edge.second); + processedFaces.insert(i); + processedFaces.insert(oppositeFaceIndex); + break; + } + } + } + } + } + } + std::vector newFaceVertexNumAndIndices; + int weldedCount = 0; + int faceCountAfterWeld = 0; + for (int i = 0; i < (int)newFaceIndicies.size(); i++) { + const auto &faceIndicies = newFaceIndicies[i]; + std::vector mappedFaceIndicies; + bool errored = false; + for (const auto &index: faceIndicies) { + int finalIndex = index; + int mapTimes = 0; + while (mapTimes < 500) { + auto findMapResult = weldVertexToMap.find(finalIndex); + if (findMapResult == weldVertexToMap.end()) + break; + finalIndex = findMapResult->second; + mapTimes++; + } + if (mapTimes >= 500) { + qDebug() << "Map too much times"; + errored = true; + break; + } + mappedFaceIndicies.push_back(finalIndex); + } + if (errored || mappedFaceIndicies.size() < 3) + continue; + bool welded = false; + for (decltype(mappedFaceIndicies.size()) j = 0; j < mappedFaceIndicies.size(); j++) { + int next = (j + 1) % 3; + if (mappedFaceIndicies[j] == mappedFaceIndicies[next]) { + welded = true; + break; + } + } + if (welded) { + weldedCount++; + continue; + } + faceCountAfterWeld++; + newFaceVertexNumAndIndices.push_back(mappedFaceIndicies.size()); + for (const auto &index: mappedFaceIndicies) { + newFaceVertexNumAndIndices.push_back(index); + } + } + qDebug() << "Welded" << weldedCount << "triangles(" << newFaceIndicies.size() << " - " << weldedCount << " = " << faceCountAfterWeld << ")"; + weldedMesh = meshlite_build(meshlite, vertexPositions, vertexCount, newFaceVertexNumAndIndices.data(), newFaceVertexNumAndIndices.size()); + delete[] faceVertexNumAndIndices; + delete[] vertexPositions; + return weldedMesh; +} diff --git a/src/meshweldseam.h b/src/meshweldseam.h new file mode 100644 index 00000000..edae4e8e --- /dev/null +++ b/src/meshweldseam.h @@ -0,0 +1,9 @@ +#ifndef MESH_WELD_SEAM_H +#define MESH_WELD_SEAM_H +#include "meshlite.h" +#include + +int meshWeldSeam(void *meshlite, int meshId, float allowedSmallestDistance, + const std::unordered_set &seamVerticesIndicies=std::unordered_set()); + +#endif diff --git a/src/skeletondocument.cpp b/src/skeletondocument.cpp index 8a85aa94..8de92dd0 100644 --- a/src/skeletondocument.cpp +++ b/src/skeletondocument.cpp @@ -28,6 +28,7 @@ SkeletonDocument::SkeletonDocument() : textureAmbientOcclusionImage(nullptr), textureColorImage(nullptr), rigType(RigType::None), + weldEnabled(false), // private m_isResultMeshObsolete(false), m_meshGenerator(nullptr), @@ -1213,6 +1214,12 @@ void SkeletonDocument::toggleSmoothNormal() regenerateMesh(); } +void SkeletonDocument::enableWeld(bool enabled) +{ + weldEnabled = enabled; + regenerateMesh(); +} + void SkeletonDocument::generateMesh() { if (nullptr != m_meshGenerator || m_batchChangeRefCount > 0) { @@ -1233,6 +1240,7 @@ void SkeletonDocument::generateMesh() resetDirtyFlags(); m_meshGenerator = new MeshGenerator(snapshot, thread); m_meshGenerator->setSmoothNormal(m_smoothNormal); + m_meshGenerator->setWeldEnabled(weldEnabled); m_meshGenerator->setGeneratedCacheContext(&m_generatedCacheContext); if (nullptr != m_sharedContextWidget) m_meshGenerator->setSharedContextWidget(m_sharedContextWidget); diff --git a/src/skeletondocument.h b/src/skeletondocument.h index ac7eea0d..e04c13e2 100644 --- a/src/skeletondocument.h +++ b/src/skeletondocument.h @@ -465,6 +465,7 @@ public: // need initialize QImage *textureAmbientOcclusionImage; QImage *textureColorImage; RigType rigType; + bool weldEnabled; public: SkeletonDocument(); ~SkeletonDocument(); @@ -594,6 +595,7 @@ public slots: void enableAllPositionRelatedLocks(); void disableAllPositionRelatedLocks(); void toggleSmoothNormal(); + void enableWeld(bool enabled); void setRigType(RigType toRigType); void addPose(QString name, std::map> parameters); void removePose(QUuid poseId); diff --git a/src/skeletondocumentwindow.cpp b/src/skeletondocumentwindow.cpp index 7a56ccc2..37dd010c 100644 --- a/src/skeletondocumentwindow.cpp +++ b/src/skeletondocumentwindow.cpp @@ -97,7 +97,8 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : m_document(nullptr), m_firstShow(true), m_documentSaved(true), - m_exportPreviewWidget(nullptr) + m_exportPreviewWidget(nullptr), + m_advanceSettingWidget(nullptr) { if (!g_logBrowser) { g_logBrowser = new LogBrowser; @@ -543,6 +544,12 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : m_showDebugDialogAction = new QAction(tr("Debug"), this); connect(m_showDebugDialogAction, &QAction::triggered, g_logBrowser, &LogBrowser::showDialog); m_windowMenu->addAction(m_showDebugDialogAction); + + m_showAdvanceSettingAction = new QAction(tr("Advance"), this); + connect(m_showAdvanceSettingAction, &QAction::triggered, this, &SkeletonDocumentWindow::showAdvanceSetting); +#ifndef NDEBUG + m_windowMenu->addAction(m_showAdvanceSettingAction); +#endif m_helpMenu = menuBar()->addMenu(tr("Help")); @@ -1061,6 +1068,15 @@ void SkeletonDocumentWindow::open() setCurrentFilename(filename); } +void SkeletonDocumentWindow::showAdvanceSetting() +{ + if (nullptr == m_advanceSettingWidget) { + m_advanceSettingWidget = new AdvanceSettingWidget(m_document, this); + } + m_advanceSettingWidget->show(); + m_advanceSettingWidget->raise(); +} + void SkeletonDocumentWindow::exportObjResult() { QString filename = QFileDialog::getSaveFileName(this, QString(), QString(), @@ -1097,10 +1113,8 @@ void SkeletonDocumentWindow::showExportPreview() connect(m_document, &SkeletonDocument::resultBakedTextureChanged, m_exportPreviewWidget, &ExportPreviewWidget::updateTexturePreview); registerDialog(m_exportPreviewWidget); } - if (m_document->isPostProcessResultObsolete()) { - m_document->postProcess(); - } m_exportPreviewWidget->show(); + m_exportPreviewWidget->raise(); } void SkeletonDocumentWindow::exportGltfResult() diff --git a/src/skeletondocumentwindow.h b/src/skeletondocumentwindow.h index c89a977c..47538e29 100644 --- a/src/skeletondocumentwindow.h +++ b/src/skeletondocumentwindow.h @@ -13,6 +13,7 @@ #include "rigwidget.h" #include "skeletonbonemark.h" #include "posemanagewidget.h" +#include "advancesettingwidget.h" class SkeletonGraphicsWidget; @@ -59,6 +60,7 @@ public slots: void updateRigWeightRenderWidget(); void registerDialog(QWidget *widget); void unregisterDialog(QWidget *widget); + void showAdvanceSetting(); private: void initLockButton(QPushButton *button); void setCurrentFilename(const QString &filename); @@ -68,6 +70,7 @@ private: bool m_firstShow; bool m_documentSaved; ExportPreviewWidget *m_exportPreviewWidget; + AdvanceSettingWidget *m_advanceSettingWidget; std::vector m_dialogs; private: QString m_currentFilename; @@ -75,7 +78,6 @@ private: ModelWidget *m_modelRenderWidget; SkeletonGraphicsWidget *m_graphicsWidget; RigWidget *m_rigWidget; - PoseManageWidget *m_poseManageWidget; QMenu *m_fileMenu; QAction *m_newWindowAction; @@ -135,6 +137,7 @@ private: QAction *m_showDebugDialogAction; QAction *m_showRigAction; QAction *m_showPosesAction; + QAction *m_showAdvanceSettingAction; QMenu *m_helpMenu; QAction *m_viewSourceAction;