diff --git a/ACKNOWLEDGEMENTS.html b/ACKNOWLEDGEMENTS.html index 947272f6..0ed40bfb 100644 --- a/ACKNOWLEDGEMENTS.html +++ b/ACKNOWLEDGEMENTS.html @@ -1132,4 +1132,9 @@ https://www.reddit.com/r/gamedev/comments/5iuf3h/i_am_writting_a_3d_monster_mode

Birdy

     http://bediyap.com/programming/convert-quaternion-to-euler-rotations/
-
\ No newline at end of file + + +

Eadweard Muybridge

+
+    https://en.wikipedia.org/wiki/Eadweard_Muybridge
+
diff --git a/dust3d.pro b/dust3d.pro index 9642b5c2..54577290 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -294,6 +294,9 @@ HEADERS += src/poserconstruct.h SOURCES += src/skeletondocument.cpp HEADERS += src/skeletondocument.h +SOURCES += src/posedocument.cpp +HEADERS += src/posedocument.h + SOURCES += src/main.cpp HEADERS += src/version.h diff --git a/src/document.cpp b/src/document.cpp index 10371f57..ca4f2292 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -416,6 +416,20 @@ void Document::setPoseParameters(QUuid poseId, std::map attributes) +{ + auto findPoseResult = poseMap.find(poseId); + if (findPoseResult == poseMap.end()) { + qDebug() << "Find pose failed:" << poseId; + return; + } + findPoseResult->second.attributes = attributes; + findPoseResult->second.dirty = true; + emit posesChanged(); + emit poseAttributesChanged(poseId); + emit optionsChanged(); +} + void Document::renamePose(QUuid poseId, QString name) { auto findPoseResult = poseMap.find(poseId); @@ -431,26 +445,6 @@ void Document::renamePose(QUuid poseId, QString name) emit optionsChanged(); } -const SkeletonEdge *Document::findEdgeByNodes(QUuid firstNodeId, QUuid secondNodeId) const -{ - const SkeletonNode *firstNode = nullptr; - firstNode = findNode(firstNodeId); - if (nullptr == firstNode) { - qDebug() << "Find node failed:" << firstNodeId; - return nullptr; - } - for (auto edgeIdIt = firstNode->edgeIds.begin(); edgeIdIt != firstNode->edgeIds.end(); edgeIdIt++) { - auto edgeIt = edgeMap.find(*edgeIdIt); - if (edgeIt == edgeMap.end()) { - qDebug() << "Find edge failed:" << *edgeIdIt; - continue; - } - if (std::find(edgeIt->second.nodeIds.begin(), edgeIt->second.nodeIds.end(), secondNodeId) != edgeIt->second.nodeIds.end()) - return &edgeIt->second; - } - return nullptr; -} - bool Document::originSettled() const { return !qFuzzyIsNull(originX) && !qFuzzyIsNull(originY) && !qFuzzyIsNull(originZ); @@ -537,30 +531,6 @@ void Document::addEdge(QUuid fromNodeId, QUuid toNodeId) emit skeletonChanged(); } -const SkeletonNode *Document::findNode(QUuid nodeId) const -{ - auto it = nodeMap.find(nodeId); - if (it == nodeMap.end()) - return nullptr; - return &it->second; -} - -const SkeletonEdge *Document::findEdge(QUuid edgeId) const -{ - auto it = edgeMap.find(edgeId); - if (it == edgeMap.end()) - return nullptr; - return &it->second; -} - -const SkeletonPart *Document::findPart(QUuid partId) const -{ - auto it = partMap.find(partId); - if (it == partMap.end()) - return nullptr; - return &it->second; -} - const Component *Document::findComponent(QUuid componentId) const { if (componentId.isNull()) @@ -1001,7 +971,7 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set &limitNodeId continue; } auto &poseIt = *findPoseResult; - std::map pose; + std::map pose = poseIt.second.attributes; pose["id"] = poseIt.second.id.toString(); if (!poseIt.second.name.isEmpty()) pose["name"] = poseIt.second.name; @@ -1251,6 +1221,13 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste) newPose.id = newPoseId; const auto &poseAttributes = poseIt.first; newPose.name = valueOfKeyInMapOrEmpty(poseAttributes, "name"); + for (const auto &attribute: poseAttributes) { + if (attribute.first == "name" || + attribute.first == "id") { + continue; + } + newPose.attributes.insert({attribute.first, attribute.second}); + } newPose.parameters = poseIt.second; oldNewIdMap[QUuid(valueOfKeyInMapOrEmpty(poseAttributes, "id"))] = newPoseId; poseIdList.push_back(newPoseId); @@ -2394,32 +2371,6 @@ void Document::checkExportReadyState() emit exportReady(); } -void Document::findAllNeighbors(QUuid nodeId, std::set &neighbors) const -{ - const auto &node = findNode(nodeId); - if (nullptr == node) { - qDebug() << "findNode:" << nodeId << "failed"; - return; - } - for (const auto &edgeId: node->edgeIds) { - const auto &edge = findEdge(edgeId); - if (nullptr == edge) { - qDebug() << "findEdge:" << edgeId << "failed"; - continue; - } - const auto &neighborNodeId = edge->neighborOf(nodeId); - if (neighborNodeId.isNull()) { - qDebug() << "neighborOf:" << nodeId << "is null from edge:" << edgeId; - continue; - } - if (neighbors.find(neighborNodeId) != neighbors.end()) { - continue; - } - neighbors.insert(neighborNodeId); - findAllNeighbors(neighborNodeId, neighbors); - } -} - void Document::setSharedContextWidget(QOpenGLWidget *sharedContextWidget) { m_sharedContextWidget = sharedContextWidget; @@ -2945,4 +2896,4 @@ void Document::copyNodes(std::set nodeIdSet) const saveSkeletonToXmlStream(&snapshot, &xmlStreamWriter); QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(snapshotXml); -} \ No newline at end of file +} diff --git a/src/document.h b/src/document.h index f0614746..52f3f33f 100644 --- a/src/document.h +++ b/src/document.h @@ -195,6 +195,7 @@ public: QUuid id; QString name; bool dirty = true; + std::map attributes; std::map> parameters; void updatePreviewMesh(MeshLoader *previewMesh) { @@ -424,6 +425,7 @@ signals: void poseListChanged(); void poseNameChanged(QUuid poseId); void poseParametersChanged(QUuid poseId); + void poseAttributesChanged(QUuid poseId); void posePreviewChanged(QUuid poseId); void motionAdded(QUuid motionId); void motionRemoved(QUuid motionId); @@ -453,9 +455,6 @@ public: // need initialize public: Document(); ~Document(); - std::map partMap; - std::map nodeMap; - std::map edgeMap; std::map componentMap; std::map materialMap; std::vector materialIdList; @@ -465,17 +464,12 @@ public: std::vector motionIdList; Component rootComponent; QImage preview; - const SkeletonNode *findNode(QUuid nodeId) const override; - const SkeletonEdge *findEdge(QUuid edgeId) const override; - const SkeletonPart *findPart(QUuid partId) const override; - const SkeletonEdge *findEdgeByNodes(QUuid firstNodeId, QUuid secondNodeId) const override; bool undoable() const override; bool redoable() const override; bool hasPastableNodesInClipboard() const override; bool originSettled() const override; bool isNodeEditable(QUuid nodeId) const override; bool isEdgeEditable(QUuid edgeId) const override; - void findAllNeighbors(QUuid nodeId, std::set &neighbors) const override; void copyNodes(std::set nodeIdSet) const override; void toSnapshot(Snapshot *snapshot, const std::set &limitNodeIds=std::set(), DocumentToSnapshotFor forWhat=DocumentToSnapshotFor::Document, @@ -601,6 +595,7 @@ public slots: void addPose(QString name, std::map> parameters); void removePose(QUuid poseId); void setPoseParameters(QUuid poseId, std::map> parameters); + void setPoseAttributes(QUuid poseId, std::map attributes); void renamePose(QUuid poseId, QString name); void addMotion(QString name, std::vector clips); void removeMotion(QUuid motionId); diff --git a/src/documentwindow.cp b/src/documentwindow.cp deleted file mode 100644 index b408adeb..00000000 --- a/src/documentwindow.cp +++ /dev/null @@ -1,1341 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "documentwindow.h" -#include "skeletongraphicswidget.h" -#include "theme.h" -#include "ds3file.h" -#include "snapshot.h" -#include "snapshotxml.h" -#include "logbrowser.h" -#include "util.h" -#include "aboutwidget.h" -#include "version.h" -#include "gltffile.h" -#include "graphicscontainerwidget.h" -#include "parttreewidget.h" -#include "rigwidget.h" -#include "markiconcreator.h" -#include "motionmanagewidget.h" -#include "materialmanagewidget.h" -#include "imageforever.h" -#include "spinnableawesomebutton.h" -#include "fbxfile.h" -#include "shortcuts.h" - -int SkeletonDocumentWindow::m_modelRenderWidgetInitialX = 16; -int SkeletonDocumentWindow::m_modelRenderWidgetInitialY = 16; -int SkeletonDocumentWindow::m_modelRenderWidgetInitialSize = 128; -int SkeletonDocumentWindow::m_skeletonRenderWidgetInitialX = SkeletonDocumentWindow::m_modelRenderWidgetInitialX + SkeletonDocumentWindow::m_modelRenderWidgetInitialSize + 16; -int SkeletonDocumentWindow::m_skeletonRenderWidgetInitialY = SkeletonDocumentWindow::m_modelRenderWidgetInitialY; -int SkeletonDocumentWindow::m_skeletonRenderWidgetInitialSize = SkeletonDocumentWindow::m_modelRenderWidgetInitialSize; - -LogBrowser *g_logBrowser = nullptr; -std::set g_documentWindows; -QTextBrowser *g_acknowlegementsWidget = nullptr; -AboutWidget *g_aboutWidget = nullptr; -QTextBrowser *g_contributorsWidget = nullptr; - -void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg) -{ - if (g_logBrowser) - g_logBrowser->outputMessage(type, msg, context.file, context.line); -} - -void SkeletonDocumentWindow::showAcknowlegements() -{ - if (!g_acknowlegementsWidget) { - g_acknowlegementsWidget = new QTextBrowser; - g_acknowlegementsWidget->setWindowTitle(unifiedWindowTitle(tr("Acknowlegements"))); - g_acknowlegementsWidget->setMinimumSize(QSize(400, 300)); - QFile file(":/ACKNOWLEDGEMENTS.html"); - file.open(QFile::ReadOnly | QFile::Text); - QTextStream stream(&file); - g_acknowlegementsWidget->setHtml(stream.readAll()); - } - g_acknowlegementsWidget->show(); - g_acknowlegementsWidget->activateWindow(); - g_acknowlegementsWidget->raise(); -} - -void SkeletonDocumentWindow::showContributors() -{ - if (!g_contributorsWidget) { - g_contributorsWidget = new QTextBrowser; - g_contributorsWidget->setWindowTitle(unifiedWindowTitle(tr("Contributors"))); - g_contributorsWidget->setMinimumSize(QSize(400, 300)); - QFile authors(":/AUTHORS"); - authors.open(QFile::ReadOnly | QFile::Text); - QFile contributors(":/CONTRIBUTORS"); - contributors.open(QFile::ReadOnly | QFile::Text); - g_contributorsWidget->setHtml("

AUTHORS

" + authors.readAll() + "

CONTRIBUTORS

" + contributors.readAll() + "
"); - } - g_contributorsWidget->show(); - g_contributorsWidget->activateWindow(); - g_contributorsWidget->raise(); -} - -void SkeletonDocumentWindow::showAbout() -{ - if (!g_aboutWidget) { - g_aboutWidget = new AboutWidget; - } - g_aboutWidget->show(); - g_aboutWidget->activateWindow(); - g_aboutWidget->raise(); -} - -SkeletonDocumentWindow::SkeletonDocumentWindow() : - m_document(nullptr), - m_firstShow(true), - m_documentSaved(true), - m_exportPreviewWidget(nullptr), - m_advanceSettingWidget(nullptr) -{ - if (!g_logBrowser) { - g_logBrowser = new LogBrowser; - qInstallMessageHandler(&outputMessage); - } - - g_documentWindows.insert(this); - - m_document = new Document; - - QVBoxLayout *toolButtonLayout = new QVBoxLayout; - toolButtonLayout->setSpacing(0); - toolButtonLayout->setContentsMargins(5, 10, 4, 0); - - QPushButton *addButton = new QPushButton(QChar(fa::plus)); - Theme::initAwesomeButton(addButton); - - QPushButton *selectButton = new QPushButton(QChar(fa::mousepointer)); - Theme::initAwesomeButton(selectButton); - - QPushButton *dragButton = new QPushButton(QChar(fa::handrocko)); - Theme::initAwesomeButton(dragButton); - - QPushButton *zoomInButton = new QPushButton(QChar(fa::searchplus)); - Theme::initAwesomeButton(zoomInButton); - - QPushButton *zoomOutButton = new QPushButton(QChar(fa::searchminus)); - Theme::initAwesomeButton(zoomOutButton); - - m_xlockButton = new QPushButton(QChar('X')); - initLockButton(m_xlockButton); - updateXlockButtonState(); - - m_ylockButton = new QPushButton(QChar('Y')); - initLockButton(m_ylockButton); - updateYlockButtonState(); - - m_zlockButton = new QPushButton(QChar('Z')); - initLockButton(m_zlockButton); - updateZlockButtonState(); - - m_radiusLockButton = new QPushButton(QChar(fa::bullseye)); - Theme::initAwesomeButton(m_radiusLockButton); - updateRadiusLockButtonState(); - - QPushButton *rotateCounterclockwiseButton = new QPushButton(QChar(fa::rotateleft)); - Theme::initAwesomeButton(rotateCounterclockwiseButton); - - QPushButton *rotateClockwiseButton = new QPushButton(QChar(fa::rotateright)); - Theme::initAwesomeButton(rotateClockwiseButton); - - SpinnableAwesomeButton *regenerateButton = new SpinnableAwesomeButton(); - regenerateButton->setAwesomeIcon(QChar(fa::recycle)); - connect(m_document, &Document::meshGenerating, this, [=]() { - regenerateButton->showSpinner(true); - }); - connect(m_document, &Document::postProcessing, this, [=]() { - regenerateButton->showSpinner(true); - }); - connect(m_document, &Document::textureGenerating, this, [=]() { - regenerateButton->showSpinner(true); - }); - connect(m_document, &Document::resultTextureChanged, this, [=]() { - regenerateButton->showSpinner(false); - }); - connect(regenerateButton->button(), &QPushButton::clicked, m_document, &Document::regenerateMesh); - - toolButtonLayout->addWidget(addButton); - toolButtonLayout->addWidget(selectButton); - toolButtonLayout->addWidget(dragButton); - toolButtonLayout->addWidget(zoomInButton); - toolButtonLayout->addWidget(zoomOutButton); - toolButtonLayout->addSpacing(10); - toolButtonLayout->addWidget(m_xlockButton); - toolButtonLayout->addWidget(m_ylockButton); - toolButtonLayout->addWidget(m_zlockButton); - toolButtonLayout->addWidget(m_radiusLockButton); - toolButtonLayout->addSpacing(10); - toolButtonLayout->addWidget(rotateCounterclockwiseButton); - toolButtonLayout->addWidget(rotateClockwiseButton); - toolButtonLayout->addSpacing(10); - toolButtonLayout->addWidget(regenerateButton); - - - QLabel *verticalLogoLabel = new QLabel; - QImage verticalLogoImage; - verticalLogoImage.load(":/resources/dust3d-vertical.png"); - verticalLogoLabel->setPixmap(QPixmap::fromImage(verticalLogoImage)); - - QHBoxLayout *logoLayout = new QHBoxLayout; - logoLayout->addWidget(verticalLogoLabel); - logoLayout->setContentsMargins(0, 0, 0, 0); - - QVBoxLayout *mainLeftLayout = new QVBoxLayout; - mainLeftLayout->setSpacing(0); - mainLeftLayout->setContentsMargins(0, 0, 0, 0); - mainLeftLayout->addLayout(toolButtonLayout); - mainLeftLayout->addStretch(); - mainLeftLayout->addLayout(logoLayout); - mainLeftLayout->addSpacing(10); - - SkeletonGraphicsWidget *graphicsWidget = new SkeletonGraphicsWidget(m_document); - m_graphicsWidget = graphicsWidget; - - GraphicsContainerWidget *containerWidget = new GraphicsContainerWidget; - containerWidget->setGraphicsWidget(graphicsWidget); - QGridLayout *containerLayout = new QGridLayout; - containerLayout->setSpacing(0); - containerLayout->setContentsMargins(1, 0, 0, 0); - containerLayout->addWidget(graphicsWidget); - containerWidget->setLayout(containerLayout); - containerWidget->setMinimumSize(400, 400); - - m_modelRenderWidget = new ModelWidget(containerWidget); - m_modelRenderWidget->setMinimumSize(SkeletonDocumentWindow::m_modelRenderWidgetInitialSize, SkeletonDocumentWindow::m_modelRenderWidgetInitialSize); - m_modelRenderWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - m_modelRenderWidget->move(SkeletonDocumentWindow::m_modelRenderWidgetInitialX, SkeletonDocumentWindow::m_modelRenderWidgetInitialY); - - m_graphicsWidget->setModelWidget(m_modelRenderWidget); - - m_document->setSharedContextWidget(m_modelRenderWidget); - - setTabPosition(Qt::RightDockWidgetArea, QTabWidget::East); - - QDockWidget *partTreeDocker = new QDockWidget(tr("Parts"), this); - partTreeDocker->setAllowedAreas(Qt::RightDockWidgetArea); - PartTreeWidget *partTreeWidget = new PartTreeWidget(m_document, partTreeDocker); - partTreeDocker->setWidget(partTreeWidget); - addDockWidget(Qt::RightDockWidgetArea, partTreeDocker); - connect(partTreeDocker, &QDockWidget::topLevelChanged, [=](bool topLevel) { - Q_UNUSED(topLevel); - for (const auto &part: m_document->partMap) - partTreeWidget->partPreviewChanged(part.first); - }); - - QDockWidget *materialDocker = new QDockWidget(tr("Materials"), this); - materialDocker->setAllowedAreas(Qt::RightDockWidgetArea); - MaterialManageWidget *materialManageWidget = new MaterialManageWidget(m_document, materialDocker); - materialDocker->setWidget(materialManageWidget); - connect(materialManageWidget, &MaterialManageWidget::registerDialog, this, &SkeletonDocumentWindow::registerDialog); - connect(materialManageWidget, &MaterialManageWidget::unregisterDialog, this, &SkeletonDocumentWindow::unregisterDialog); - addDockWidget(Qt::RightDockWidgetArea, materialDocker); - connect(materialDocker, &QDockWidget::topLevelChanged, [=](bool topLevel) { - Q_UNUSED(topLevel); - for (const auto &material: m_document->materialMap) - emit m_document->materialPreviewChanged(material.first); - }); - - QDockWidget *rigDocker = new QDockWidget(tr("Rig"), this); - rigDocker->setAllowedAreas(Qt::RightDockWidgetArea); - m_rigWidget = new RigWidget(m_document, rigDocker); - rigDocker->setWidget(m_rigWidget); - addDockWidget(Qt::RightDockWidgetArea, rigDocker); - connect(rigDocker, &QDockWidget::topLevelChanged, [=](bool topLevel) { - Q_UNUSED(topLevel); - updateRigWeightRenderWidget(); - }); - - QDockWidget *poseDocker = new QDockWidget(tr("Poses"), this); - poseDocker->setAllowedAreas(Qt::RightDockWidgetArea); - PoseManageWidget *poseManageWidget = new PoseManageWidget(m_document, poseDocker); - poseDocker->setWidget(poseManageWidget); - connect(poseManageWidget, &PoseManageWidget::registerDialog, this, &SkeletonDocumentWindow::registerDialog); - connect(poseManageWidget, &PoseManageWidget::unregisterDialog, this, &SkeletonDocumentWindow::unregisterDialog); - addDockWidget(Qt::RightDockWidgetArea, poseDocker); - connect(poseDocker, &QDockWidget::topLevelChanged, [=](bool topLevel) { - Q_UNUSED(topLevel); - for (const auto &pose: m_document->poseMap) - emit m_document->posePreviewChanged(pose.first); - }); - - QDockWidget *motionDocker = new QDockWidget(tr("Motions"), this); - motionDocker->setAllowedAreas(Qt::RightDockWidgetArea); - MotionManageWidget *motionManageWidget = new MotionManageWidget(m_document, motionDocker); - motionDocker->setWidget(motionManageWidget); - connect(motionManageWidget, &MotionManageWidget::registerDialog, this, &SkeletonDocumentWindow::registerDialog); - connect(motionManageWidget, &MotionManageWidget::unregisterDialog, this, &SkeletonDocumentWindow::unregisterDialog); - addDockWidget(Qt::RightDockWidgetArea, motionDocker); - - tabifyDockWidget(partTreeDocker, materialDocker); - tabifyDockWidget(materialDocker, rigDocker); - tabifyDockWidget(rigDocker, poseDocker); - tabifyDockWidget(poseDocker, motionDocker); - - partTreeDocker->raise(); - - QHBoxLayout *mainLayout = new QHBoxLayout; - mainLayout->setSpacing(0); - mainLayout->setContentsMargins(0, 0, 0, 0); - mainLayout->addLayout(mainLeftLayout); - mainLayout->addWidget(containerWidget); - mainLayout->addSpacing(3); - - QWidget *centralWidget = new QWidget; - centralWidget->setLayout(mainLayout); - - setCentralWidget(centralWidget); - setWindowTitle(APP_NAME); - - m_fileMenu = menuBar()->addMenu(tr("File")); - - m_newWindowAction = new QAction(tr("New Window"), this); - connect(m_newWindowAction, &QAction::triggered, this, &SkeletonDocumentWindow::newWindow, Qt::QueuedConnection); - m_fileMenu->addAction(m_newWindowAction); - - m_newDocumentAction = new QAction(tr("New"), this); - connect(m_newDocumentAction, &QAction::triggered, this, &SkeletonDocumentWindow::newDocument); - m_fileMenu->addAction(m_newDocumentAction); - - m_openAction = new QAction(tr("Open..."), this); - connect(m_openAction, &QAction::triggered, this, &SkeletonDocumentWindow::open, Qt::QueuedConnection); - m_fileMenu->addAction(m_openAction); - - m_saveAction = new QAction(tr("Save"), this); - connect(m_saveAction, &QAction::triggered, this, &SkeletonDocumentWindow::save, Qt::QueuedConnection); - m_fileMenu->addAction(m_saveAction); - - m_saveAsAction = new QAction(tr("Save As..."), this); - connect(m_saveAsAction, &QAction::triggered, this, &SkeletonDocumentWindow::saveAs, Qt::QueuedConnection); - m_fileMenu->addAction(m_saveAsAction); - - m_saveAllAction = new QAction(tr("Save All"), this); - connect(m_saveAllAction, &QAction::triggered, this, &SkeletonDocumentWindow::saveAll, Qt::QueuedConnection); - m_fileMenu->addAction(m_saveAllAction); - - m_fileMenu->addSeparator(); - - //m_exportMenu = m_fileMenu->addMenu(tr("Export")); - - m_exportAction = new QAction(tr("Export..."), this); - connect(m_exportAction, &QAction::triggered, this, &SkeletonDocumentWindow::showExportPreview, Qt::QueuedConnection); - m_fileMenu->addAction(m_exportAction); - - m_exportAsObjAction = new QAction(tr("Export as OBJ..."), this); - connect(m_exportAsObjAction, &QAction::triggered, this, &SkeletonDocumentWindow::exportObjResult, Qt::QueuedConnection); - m_fileMenu->addAction(m_exportAsObjAction); - - //m_exportRenderedAsImageAction = new QAction(tr("Export as PNG..."), this); - //connect(m_exportRenderedAsImageAction, &QAction::triggered, this, &SkeletonDocumentWindow::exportRenderedResult, Qt::QueuedConnection); - //m_fileMenu->addAction(m_exportRenderedAsImageAction); - - //m_exportAsObjPlusMaterialsAction = new QAction(tr("Wavefront (.obj + .mtl)..."), this); - //connect(m_exportAsObjPlusMaterialsAction, &QAction::triggered, this, &SkeletonDocumentWindow::exportObjPlusMaterialsResult, Qt::QueuedConnection); - //m_exportMenu->addAction(m_exportAsObjPlusMaterialsAction); - - m_fileMenu->addSeparator(); - - m_changeTurnaroundAction = new QAction(tr("Change Reference Sheet..."), this); - connect(m_changeTurnaroundAction, &QAction::triggered, this, &SkeletonDocumentWindow::changeTurnaround, Qt::QueuedConnection); - m_fileMenu->addAction(m_changeTurnaroundAction); - - m_fileMenu->addSeparator(); - - connect(m_fileMenu, &QMenu::aboutToShow, [=]() { - m_exportAsObjAction->setEnabled(m_graphicsWidget->hasItems()); - //m_exportAsObjPlusMaterialsAction->setEnabled(m_graphicsWidget->hasItems()); - m_exportAction->setEnabled(m_graphicsWidget->hasItems()); - //m_exportRenderedAsImageAction->setEnabled(m_graphicsWidget->hasItems()); - }); - - m_editMenu = menuBar()->addMenu(tr("Edit")); - - m_addAction = new QAction(tr("Add..."), this); - connect(m_addAction, &QAction::triggered, [=]() { - m_document->setEditMode(DocumentEditMode::Add); - }); - m_editMenu->addAction(m_addAction); - - m_undoAction = new QAction(tr("Undo"), this); - connect(m_undoAction, &QAction::triggered, m_document, &Document::undo); - m_editMenu->addAction(m_undoAction); - - m_redoAction = new QAction(tr("Redo"), this); - connect(m_redoAction, &QAction::triggered, m_document, &Document::redo); - m_editMenu->addAction(m_redoAction); - - m_deleteAction = new QAction(tr("Delete"), this); - connect(m_deleteAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::deleteSelected); - m_editMenu->addAction(m_deleteAction); - - m_breakAction = new QAction(tr("Break"), this); - connect(m_breakAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::breakSelected); - m_editMenu->addAction(m_breakAction); - - m_connectAction = new QAction(tr("Connect"), this); - connect(m_connectAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::connectSelected); - m_editMenu->addAction(m_connectAction); - - m_cutAction = new QAction(tr("Cut"), this); - connect(m_cutAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::cut); - m_editMenu->addAction(m_cutAction); - - m_copyAction = new QAction(tr("Copy"), this); - connect(m_copyAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::copy); - m_editMenu->addAction(m_copyAction); - - m_pasteAction = new QAction(tr("Paste"), this); - connect(m_pasteAction, &QAction::triggered, m_document, &Document::paste); - m_editMenu->addAction(m_pasteAction); - - m_flipHorizontallyAction = new QAction(tr("H Flip"), this); - connect(m_flipHorizontallyAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::flipHorizontally); - m_editMenu->addAction(m_flipHorizontallyAction); - - m_flipVerticallyAction = new QAction(tr("V Flip"), this); - connect(m_flipVerticallyAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::flipVertically); - m_editMenu->addAction(m_flipVerticallyAction); - - m_rotateClockwiseAction = new QAction(tr("Rotate 90D CW"), this); - connect(m_rotateClockwiseAction, &QAction::triggered, [=] { - m_graphicsWidget->rotateClockwise90Degree(); - }); - m_editMenu->addAction(m_rotateClockwiseAction); - - m_rotateCounterclockwiseAction = new QAction(tr("Rotate 90D CCW"), this); - connect(m_rotateCounterclockwiseAction, &QAction::triggered, [=] { - m_graphicsWidget->rotateCounterclockwise90Degree(); - }); - m_editMenu->addAction(m_rotateCounterclockwiseAction); - - m_switchXzAction = new QAction(tr("Switch XZ"), this); - connect(m_switchXzAction, &QAction::triggered, [=] { - m_graphicsWidget->switchSelectedXZ(); - }); - m_editMenu->addAction(m_switchXzAction); - - m_alignToMenu = new QMenu(tr("Align To")); - - m_alignToLocalCenterAction = new QAction(tr("Local Center"), this); - connect(m_alignToLocalCenterAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::alignSelectedToLocalCenter); - m_alignToMenu->addAction(m_alignToLocalCenterAction); - - m_alignToLocalVerticalCenterAction = new QAction(tr("Local Vertical Center"), this); - connect(m_alignToLocalVerticalCenterAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::alignSelectedToLocalVerticalCenter); - m_alignToMenu->addAction(m_alignToLocalVerticalCenterAction); - - m_alignToLocalHorizontalCenterAction = new QAction(tr("Local Horizontal Center"), this); - connect(m_alignToLocalHorizontalCenterAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::alignSelectedToLocalHorizontalCenter); - m_alignToMenu->addAction(m_alignToLocalHorizontalCenterAction); - - m_alignToGlobalCenterAction = new QAction(tr("Global Center"), this); - connect(m_alignToGlobalCenterAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::alignSelectedToGlobalCenter); - m_alignToMenu->addAction(m_alignToGlobalCenterAction); - - m_alignToGlobalVerticalCenterAction = new QAction(tr("Global Vertical Center"), this); - connect(m_alignToGlobalVerticalCenterAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::alignSelectedToGlobalVerticalCenter); - m_alignToMenu->addAction(m_alignToGlobalVerticalCenterAction); - - m_alignToGlobalHorizontalCenterAction = new QAction(tr("Global Horizontal Center"), this); - connect(m_alignToGlobalHorizontalCenterAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::alignSelectedToGlobalHorizontalCenter); - m_alignToMenu->addAction(m_alignToGlobalHorizontalCenterAction); - - m_editMenu->addMenu(m_alignToMenu); - - m_markAsMenu = new QMenu(tr("Mark As")); - - m_markAsNoneAction = new QAction(tr("None"), this); - connect(m_markAsNoneAction, &QAction::triggered, [=]() { - m_graphicsWidget->setSelectedNodesBoneMark(BoneMark::None); - }); - m_markAsMenu->addAction(m_markAsNoneAction); - - m_markAsMenu->addSeparator(); - - for (int i = 0; i < (int)BoneMark::Count - 1; i++) { - BoneMark boneMark = (BoneMark)(i + 1); - m_markAsActions[i] = new QAction(MarkIconCreator::createIcon(boneMark), BoneMarkToDispName(boneMark), this); - connect(m_markAsActions[i], &QAction::triggered, [=]() { - m_graphicsWidget->setSelectedNodesBoneMark(boneMark); - }); - m_markAsMenu->addAction(m_markAsActions[i]); - } - - m_editMenu->addMenu(m_markAsMenu); - - m_selectAllAction = new QAction(tr("Select All"), this); - connect(m_selectAllAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::selectAll); - m_editMenu->addAction(m_selectAllAction); - - m_selectPartAllAction = new QAction(tr("Select Part"), this); - connect(m_selectPartAllAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::selectPartAll); - m_editMenu->addAction(m_selectPartAllAction); - - m_unselectAllAction = new QAction(tr("Unselect All"), this); - connect(m_unselectAllAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::unselectAll); - m_editMenu->addAction(m_unselectAllAction); - - connect(m_editMenu, &QMenu::aboutToShow, [=]() { - m_undoAction->setEnabled(m_document->undoable()); - m_redoAction->setEnabled(m_document->redoable()); - m_deleteAction->setEnabled(m_graphicsWidget->hasSelection()); - m_breakAction->setEnabled(m_graphicsWidget->hasEdgeSelection()); - m_connectAction->setEnabled(m_graphicsWidget->hasTwoDisconnectedNodesSelection()); - m_cutAction->setEnabled(m_graphicsWidget->hasSelection()); - m_copyAction->setEnabled(m_graphicsWidget->hasSelection()); - m_pasteAction->setEnabled(m_document->hasPastableNodesInClipboard()); - m_flipHorizontallyAction->setEnabled(m_graphicsWidget->hasMultipleSelection()); - m_flipVerticallyAction->setEnabled(m_graphicsWidget->hasMultipleSelection()); - m_rotateClockwiseAction->setEnabled(m_graphicsWidget->hasMultipleSelection()); - m_rotateCounterclockwiseAction->setEnabled(m_graphicsWidget->hasMultipleSelection()); - m_switchXzAction->setEnabled(m_graphicsWidget->hasSelection()); - m_alignToGlobalCenterAction->setEnabled(m_graphicsWidget->hasSelection() && m_document->originSettled()); - m_alignToGlobalVerticalCenterAction->setEnabled(m_graphicsWidget->hasSelection() && m_document->originSettled()); - m_alignToGlobalHorizontalCenterAction->setEnabled(m_graphicsWidget->hasSelection() && m_document->originSettled()); - m_alignToLocalCenterAction->setEnabled(m_graphicsWidget->hasMultipleSelection()); - m_alignToLocalVerticalCenterAction->setEnabled(m_graphicsWidget->hasMultipleSelection()); - m_alignToLocalHorizontalCenterAction->setEnabled(m_graphicsWidget->hasMultipleSelection()); - m_alignToMenu->setEnabled((m_graphicsWidget->hasSelection() && m_document->originSettled()) || m_graphicsWidget->hasMultipleSelection()); - m_selectAllAction->setEnabled(m_graphicsWidget->hasItems()); - m_selectPartAllAction->setEnabled(m_graphicsWidget->hasItems()); - m_unselectAllAction->setEnabled(m_graphicsWidget->hasSelection()); - }); - - m_viewMenu = menuBar()->addMenu(tr("View")); - - auto isModelSitInVisibleArea = [](ModelWidget *modelWidget) { - QRect parentRect = QRect(QPoint(0, 0), modelWidget->parentWidget()->size()); - return parentRect.contains(modelWidget->geometry().center()); - }; - - m_resetModelWidgetPosAction = new QAction(tr("Show Model"), this); - connect(m_resetModelWidgetPosAction, &QAction::triggered, [=]() { - if (!isModelSitInVisibleArea(m_modelRenderWidget)) { - m_modelRenderWidget->move(SkeletonDocumentWindow::m_modelRenderWidgetInitialX, SkeletonDocumentWindow::m_modelRenderWidgetInitialY); - } - }); - m_viewMenu->addAction(m_resetModelWidgetPosAction); - - //m_toggleWireframeAction = new QAction(tr("Toggle Wireframe"), this); - //connect(m_toggleWireframeAction, &QAction::triggered, [=]() { - // m_modelRenderWidget->toggleWireframe(); - //}); - //m_viewMenu->addAction(m_toggleWireframeAction); - - //m_toggleSmoothNormalAction = new QAction(tr("Toggle Smooth Normal"), this); - //connect(m_toggleSmoothNormalAction, &QAction::triggered, [=]() { - // m_document->toggleSmoothNormal(); - //}); - //m_viewMenu->addAction(m_toggleSmoothNormalAction); - - connect(m_viewMenu, &QMenu::aboutToShow, [=]() { - m_resetModelWidgetPosAction->setEnabled(!isModelSitInVisibleArea(m_modelRenderWidget)); - }); - - m_windowMenu = menuBar()->addMenu(tr("Window")); - - m_showPartsListAction = new QAction(tr("Parts"), this); - connect(m_showPartsListAction, &QAction::triggered, [=]() { - partTreeDocker->show(); - partTreeDocker->raise(); - }); - m_windowMenu->addAction(m_showPartsListAction); - - m_showMaterialsAction = new QAction(tr("Materials"), this); - connect(m_showMaterialsAction, &QAction::triggered, [=]() { - materialDocker->show(); - materialDocker->raise(); - }); - m_windowMenu->addAction(m_showMaterialsAction); - - m_showRigAction = new QAction(tr("Rig"), this); - connect(m_showRigAction, &QAction::triggered, [=]() { - rigDocker->show(); - rigDocker->raise(); - }); - m_windowMenu->addAction(m_showRigAction); - - m_showPosesAction = new QAction(tr("Poses"), this); - connect(m_showPosesAction, &QAction::triggered, [=]() { - poseDocker->show(); - poseDocker->raise(); - }); - m_windowMenu->addAction(m_showPosesAction); - - m_showMotionsAction = new QAction(tr("Motions"), this); - connect(m_showMotionsAction, &QAction::triggered, [=]() { - motionDocker->show(); - motionDocker->raise(); - }); - m_windowMenu->addAction(m_showMotionsAction); - - QMenu *dialogsMenu = m_windowMenu->addMenu(tr("Dialogs")); - connect(dialogsMenu, &QMenu::aboutToShow, [=]() { - dialogsMenu->clear(); - if (this->m_dialogs.empty()) { - QAction *action = dialogsMenu->addAction(tr("None")); - action->setEnabled(false); - return; - } - for (const auto &dialog: this->m_dialogs) { - QAction *action = dialogsMenu->addAction(dialog->windowTitle()); - connect(action, &QAction::triggered, [=]() { - dialog->show(); - dialog->raise(); - }); - } - }); - - 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")); - - m_viewSourceAction = new QAction(tr("Fork me on GitHub"), this); - connect(m_viewSourceAction, &QAction::triggered, this, &SkeletonDocumentWindow::viewSource); - m_helpMenu->addAction(m_viewSourceAction); - - m_helpMenu->addSeparator(); - - m_seeReferenceGuideAction = new QAction(tr("Reference Guide"), this); - connect(m_seeReferenceGuideAction, &QAction::triggered, this, &SkeletonDocumentWindow::seeReferenceGuide); - m_helpMenu->addAction(m_seeReferenceGuideAction); - - m_helpMenu->addSeparator(); - - m_aboutAction = new QAction(tr("About"), this); - connect(m_aboutAction, &QAction::triggered, this, &SkeletonDocumentWindow::about); - m_helpMenu->addAction(m_aboutAction); - - m_reportIssuesAction = new QAction(tr("Report Issues"), this); - connect(m_reportIssuesAction, &QAction::triggered, this, &SkeletonDocumentWindow::reportIssues); - m_helpMenu->addAction(m_reportIssuesAction); - - m_helpMenu->addSeparator(); - - m_seeContributorsAction = new QAction(tr("Contributors"), this); - connect(m_seeContributorsAction, &QAction::triggered, this, &SkeletonDocumentWindow::seeContributors); - m_helpMenu->addAction(m_seeContributorsAction); - - m_seeAcknowlegementsAction = new QAction(tr("Acknowlegements"), this); - connect(m_seeAcknowlegementsAction, &QAction::triggered, this, &SkeletonDocumentWindow::seeAcknowlegements); - m_helpMenu->addAction(m_seeAcknowlegementsAction); - - connect(containerWidget, &GraphicsContainerWidget::containerSizeChanged, - graphicsWidget, &SkeletonGraphicsWidget::canvasResized); - - connect(m_document, &Document::turnaroundChanged, - graphicsWidget, &SkeletonGraphicsWidget::turnaroundChanged); - - connect(rotateCounterclockwiseButton, &QPushButton::clicked, graphicsWidget, &SkeletonGraphicsWidget::rotateAllMainProfileCounterclockwise90DegreeAlongOrigin); - connect(rotateClockwiseButton, &QPushButton::clicked, graphicsWidget, &SkeletonGraphicsWidget::rotateAllMainProfileClockwise90DegreeAlongOrigin); - - connect(addButton, &QPushButton::clicked, [=]() { - m_document->setEditMode(DocumentEditMode::Add); - }); - - connect(selectButton, &QPushButton::clicked, [=]() { - m_document->setEditMode(DocumentEditMode::Select); - }); - - connect(dragButton, &QPushButton::clicked, [=]() { - m_document->setEditMode(DocumentEditMode::Drag); - }); - - connect(zoomInButton, &QPushButton::clicked, [=]() { - m_document->setEditMode(DocumentEditMode::ZoomIn); - }); - - connect(zoomOutButton, &QPushButton::clicked, [=]() { - m_document->setEditMode(DocumentEditMode::ZoomOut); - }); - - connect(m_xlockButton, &QPushButton::clicked, [=]() { - m_document->setXlockState(!m_document->xlocked); - }); - connect(m_ylockButton, &QPushButton::clicked, [=]() { - m_document->setYlockState(!m_document->ylocked); - }); - connect(m_zlockButton, &QPushButton::clicked, [=]() { - m_document->setZlockState(!m_document->zlocked); - }); - connect(m_radiusLockButton, &QPushButton::clicked, [=]() { - m_document->setRadiusLockState(!m_document->radiusLocked); - }); - - m_partListDockerVisibleSwitchConnection = connect(m_document, &Document::skeletonChanged, [=]() { - if (m_graphicsWidget->hasItems()) { - if (partTreeDocker->isHidden()) - partTreeDocker->show(); - disconnect(m_partListDockerVisibleSwitchConnection); - } - }); - - connect(m_document, &Document::editModeChanged, graphicsWidget, &SkeletonGraphicsWidget::editModeChanged); - - connect(graphicsWidget, &SkeletonGraphicsWidget::zoomRenderedModelBy, m_modelRenderWidget, &ModelWidget::zoom); - - connect(graphicsWidget, &SkeletonGraphicsWidget::addNode, m_document, &Document::addNode); - connect(graphicsWidget, &SkeletonGraphicsWidget::scaleNodeByAddRadius, m_document, &Document::scaleNodeByAddRadius); - connect(graphicsWidget, &SkeletonGraphicsWidget::moveNodeBy, m_document, &Document::moveNodeBy); - connect(graphicsWidget, &SkeletonGraphicsWidget::setNodeOrigin, m_document, &Document::setNodeOrigin); - connect(graphicsWidget, &SkeletonGraphicsWidget::setNodeBoneMark, m_document, &Document::setNodeBoneMark); - connect(graphicsWidget, &SkeletonGraphicsWidget::removeNode, m_document, &Document::removeNode); - connect(graphicsWidget, &SkeletonGraphicsWidget::setEditMode, m_document, &Document::setEditMode); - connect(graphicsWidget, &SkeletonGraphicsWidget::removeEdge, m_document, &Document::removeEdge); - connect(graphicsWidget, &SkeletonGraphicsWidget::addEdge, m_document, &Document::addEdge); - connect(graphicsWidget, &SkeletonGraphicsWidget::groupOperationAdded, m_document, &Document::saveSnapshot); - connect(graphicsWidget, &SkeletonGraphicsWidget::undo, m_document, &Document::undo); - connect(graphicsWidget, &SkeletonGraphicsWidget::redo, m_document, &Document::redo); - connect(graphicsWidget, &SkeletonGraphicsWidget::paste, m_document, &Document::paste); - connect(graphicsWidget, &SkeletonGraphicsWidget::batchChangeBegin, m_document, &Document::batchChangeBegin); - connect(graphicsWidget, &SkeletonGraphicsWidget::batchChangeEnd, m_document, &Document::batchChangeEnd); - connect(graphicsWidget, &SkeletonGraphicsWidget::breakEdge, m_document, &Document::breakEdge); - connect(graphicsWidget, &SkeletonGraphicsWidget::moveOriginBy, m_document, &Document::moveOriginBy); - connect(graphicsWidget, &SkeletonGraphicsWidget::partChecked, m_document, &Document::partChecked); - connect(graphicsWidget, &SkeletonGraphicsWidget::partUnchecked, m_document, &Document::partUnchecked); - connect(graphicsWidget, &SkeletonGraphicsWidget::switchNodeXZ, m_document, &Document::switchNodeXZ); - - connect(graphicsWidget, &SkeletonGraphicsWidget::setPartLockState, m_document, &Document::setPartLockState); - connect(graphicsWidget, &SkeletonGraphicsWidget::setPartVisibleState, m_document, &Document::setPartVisibleState); - connect(graphicsWidget, &SkeletonGraphicsWidget::setPartSubdivState, m_document, &Document::setPartSubdivState); - connect(graphicsWidget, &SkeletonGraphicsWidget::setPartDisableState, m_document, &Document::setPartDisableState); - connect(graphicsWidget, &SkeletonGraphicsWidget::setPartXmirrorState, m_document, &Document::setPartXmirrorState); - connect(graphicsWidget, &SkeletonGraphicsWidget::setPartRoundState, m_document, &Document::setPartRoundState); - connect(graphicsWidget, &SkeletonGraphicsWidget::setPartWrapState, m_document, &Document::setPartWrapState); - - connect(graphicsWidget, &SkeletonGraphicsWidget::setXlockState, m_document, &Document::setXlockState); - connect(graphicsWidget, &SkeletonGraphicsWidget::setYlockState, m_document, &Document::setYlockState); - connect(graphicsWidget, &SkeletonGraphicsWidget::setZlockState, m_document, &Document::setZlockState); - - connect(graphicsWidget, &SkeletonGraphicsWidget::enableAllPositionRelatedLocks, m_document, &Document::enableAllPositionRelatedLocks); - connect(graphicsWidget, &SkeletonGraphicsWidget::disableAllPositionRelatedLocks, m_document, &Document::disableAllPositionRelatedLocks); - - connect(graphicsWidget, &SkeletonGraphicsWidget::changeTurnaround, this, &SkeletonDocumentWindow::changeTurnaround); - connect(graphicsWidget, &SkeletonGraphicsWidget::save, this, &SkeletonDocumentWindow::save); - connect(graphicsWidget, &SkeletonGraphicsWidget::open, this, &SkeletonDocumentWindow::open); - - connect(m_document, &Document::optionsChanged, m_document, &Document::saveSnapshot); - - connect(m_document, &Document::nodeAdded, graphicsWidget, &SkeletonGraphicsWidget::nodeAdded); - connect(m_document, &Document::nodeRemoved, graphicsWidget, &SkeletonGraphicsWidget::nodeRemoved); - connect(m_document, &Document::edgeAdded, graphicsWidget, &SkeletonGraphicsWidget::edgeAdded); - connect(m_document, &Document::edgeRemoved, graphicsWidget, &SkeletonGraphicsWidget::edgeRemoved); - connect(m_document, &Document::nodeRadiusChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeRadiusChanged); - connect(m_document, &Document::nodeBoneMarkChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeBoneMarkChanged); - connect(m_document, &Document::nodeOriginChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeOriginChanged); - connect(m_document, &Document::partVisibleStateChanged, graphicsWidget, &SkeletonGraphicsWidget::partVisibleStateChanged); - connect(m_document, &Document::partDisableStateChanged, graphicsWidget, &SkeletonGraphicsWidget::partVisibleStateChanged); - connect(m_document, &Document::cleanup, graphicsWidget, &SkeletonGraphicsWidget::removeAllContent); - connect(m_document, &Document::originChanged, graphicsWidget, &SkeletonGraphicsWidget::originChanged); - connect(m_document, &Document::checkPart, graphicsWidget, &SkeletonGraphicsWidget::selectPartAllById); - connect(m_document, &Document::enableBackgroundBlur, graphicsWidget, &SkeletonGraphicsWidget::enableBackgroundBlur); - connect(m_document, &Document::disableBackgroundBlur, graphicsWidget, &SkeletonGraphicsWidget::disableBackgroundBlur); - connect(m_document, &Document::uncheckAll, graphicsWidget, &SkeletonGraphicsWidget::unselectAll); - connect(m_document, &Document::checkNode, graphicsWidget, &SkeletonGraphicsWidget::addSelectNode); - connect(m_document, &Document::checkEdge, graphicsWidget, &SkeletonGraphicsWidget::addSelectEdge); - - connect(partTreeWidget, &PartTreeWidget::currentComponentChanged, m_document, &Document::setCurrentCanvasComponentId); - connect(partTreeWidget, &PartTreeWidget::moveComponentUp, m_document, &Document::moveComponentUp); - connect(partTreeWidget, &PartTreeWidget::moveComponentDown, m_document, &Document::moveComponentDown); - connect(partTreeWidget, &PartTreeWidget::moveComponentToTop, m_document, &Document::moveComponentToTop); - connect(partTreeWidget, &PartTreeWidget::moveComponentToBottom, m_document, &Document::moveComponentToBottom); - connect(partTreeWidget, &PartTreeWidget::checkPart, m_document, &Document::checkPart); - connect(partTreeWidget, &PartTreeWidget::createNewComponentAndMoveThisIn, m_document, &Document::createNewComponentAndMoveThisIn); - connect(partTreeWidget, &PartTreeWidget::createNewChildComponent, m_document, &Document::createNewChildComponent); - connect(partTreeWidget, &PartTreeWidget::renameComponent, m_document, &Document::renameComponent); - connect(partTreeWidget, &PartTreeWidget::setComponentExpandState, m_document, &Document::setComponentExpandState); - connect(partTreeWidget, &PartTreeWidget::setComponentSmoothAll, m_document, &Document::setComponentSmoothAll); - connect(partTreeWidget, &PartTreeWidget::setComponentSmoothSeam, m_document, &Document::setComponentSmoothSeam); - connect(partTreeWidget, &PartTreeWidget::moveComponent, m_document, &Document::moveComponent); - connect(partTreeWidget, &PartTreeWidget::removeComponent, m_document, &Document::removeComponent); - connect(partTreeWidget, &PartTreeWidget::hideOtherComponents, m_document, &Document::hideOtherComponents); - connect(partTreeWidget, &PartTreeWidget::lockOtherComponents, m_document, &Document::lockOtherComponents); - connect(partTreeWidget, &PartTreeWidget::hideAllComponents, m_document, &Document::hideAllComponents); - connect(partTreeWidget, &PartTreeWidget::showAllComponents, m_document, &Document::showAllComponents); - connect(partTreeWidget, &PartTreeWidget::collapseAllComponents, m_document, &Document::collapseAllComponents); - connect(partTreeWidget, &PartTreeWidget::expandAllComponents, m_document, &Document::expandAllComponents); - connect(partTreeWidget, &PartTreeWidget::lockAllComponents, m_document, &Document::lockAllComponents); - connect(partTreeWidget, &PartTreeWidget::unlockAllComponents, m_document, &Document::unlockAllComponents); - connect(partTreeWidget, &PartTreeWidget::setPartLockState, m_document, &Document::setPartLockState); - connect(partTreeWidget, &PartTreeWidget::setPartVisibleState, m_document, &Document::setPartVisibleState); - connect(partTreeWidget, &PartTreeWidget::setComponentInverseState, m_document, &Document::setComponentInverseState); - connect(partTreeWidget, &PartTreeWidget::hideDescendantComponents, m_document, &Document::hideDescendantComponents); - connect(partTreeWidget, &PartTreeWidget::showDescendantComponents, m_document, &Document::showDescendantComponents); - connect(partTreeWidget, &PartTreeWidget::lockDescendantComponents, m_document, &Document::lockDescendantComponents); - connect(partTreeWidget, &PartTreeWidget::unlockDescendantComponents, m_document, &Document::unlockDescendantComponents); - - connect(partTreeWidget, &PartTreeWidget::addPartToSelection, graphicsWidget, &SkeletonGraphicsWidget::addPartToSelection); - - connect(m_document, &Document::componentNameChanged, partTreeWidget, &PartTreeWidget::componentNameChanged); - connect(m_document, &Document::componentChildrenChanged, partTreeWidget, &PartTreeWidget::componentChildrenChanged); - connect(m_document, &Document::componentRemoved, partTreeWidget, &PartTreeWidget::componentRemoved); - connect(m_document, &Document::componentAdded, partTreeWidget, &PartTreeWidget::componentAdded); - connect(m_document, &Document::componentExpandStateChanged, partTreeWidget, &PartTreeWidget::componentExpandStateChanged); - connect(m_document, &Document::partPreviewChanged, partTreeWidget, &PartTreeWidget::partPreviewChanged); - connect(m_document, &Document::partLockStateChanged, partTreeWidget, &PartTreeWidget::partLockStateChanged); - connect(m_document, &Document::partVisibleStateChanged, partTreeWidget, &PartTreeWidget::partVisibleStateChanged); - connect(m_document, &Document::partSubdivStateChanged, partTreeWidget, &PartTreeWidget::partSubdivStateChanged); - connect(m_document, &Document::partDisableStateChanged, partTreeWidget, &PartTreeWidget::partDisableStateChanged); - connect(m_document, &Document::partXmirrorStateChanged, partTreeWidget, &PartTreeWidget::partXmirrorStateChanged); - connect(m_document, &Document::partDeformThicknessChanged, partTreeWidget, &PartTreeWidget::partDeformChanged); - connect(m_document, &Document::partDeformWidthChanged, partTreeWidget, &PartTreeWidget::partDeformChanged); - connect(m_document, &Document::partRoundStateChanged, partTreeWidget, &PartTreeWidget::partRoundStateChanged); - connect(m_document, &Document::partWrapStateChanged, partTreeWidget, &PartTreeWidget::partWrapStateChanged); - connect(m_document, &Document::partColorStateChanged, partTreeWidget, &PartTreeWidget::partColorStateChanged); - connect(m_document, &Document::partMaterialIdChanged, partTreeWidget, &PartTreeWidget::partMaterialIdChanged); - connect(m_document, &Document::partRemoved, partTreeWidget, &PartTreeWidget::partRemoved); - connect(m_document, &Document::cleanup, partTreeWidget, &PartTreeWidget::removeAllContent); - connect(m_document, &Document::partChecked, partTreeWidget, &PartTreeWidget::partChecked); - connect(m_document, &Document::partUnchecked, partTreeWidget, &PartTreeWidget::partUnchecked); - - connect(m_document, &Document::skeletonChanged, m_document, &Document::generateMesh); - //connect(m_document, &SkeletonDocument::resultMeshChanged, [=]() { - // if ((m_exportPreviewWidget && m_exportPreviewWidget->isVisible())) { - // m_document->postProcess(); - // } - //}); - //connect(m_document, &SkeletonDocument::textureChanged, [=]() { - // if ((m_exportPreviewWidget && m_exportPreviewWidget->isVisible())) { - // m_document->generateTexture(); - // } - //}); - connect(m_document, &Document::textureChanged, m_document, &Document::generateTexture); - connect(m_document, &Document::resultMeshChanged, m_document, &Document::postProcess); - connect(m_document, &Document::resultMeshChanged, m_document, &Document::generateRig); - connect(m_document, &Document::rigChanged, m_document, &Document::generateRig); - connect(m_document, &Document::postProcessedResultChanged, m_document, &Document::generateTexture); - //connect(m_document, &SkeletonDocument::resultTextureChanged, m_document, &SkeletonDocument::bakeAmbientOcclusionTexture); - connect(m_document, &Document::resultTextureChanged, [=]() { - if (m_document->isMeshGenerating()) - return; - m_modelRenderWidget->updateMesh(m_document->takeResultTextureMesh()); - }); - - connect(m_document, &Document::resultMeshChanged, [=]() { - m_modelRenderWidget->updateMesh(m_document->takeResultMesh()); - }); - - connect(m_document, &Document::posesChanged, m_document, &Document::generateMotions); - connect(m_document, &Document::motionsChanged, m_document, &Document::generateMotions); - - connect(graphicsWidget, &SkeletonGraphicsWidget::cursorChanged, [=]() { - m_modelRenderWidget->setCursor(graphicsWidget->cursor()); - //m_skeletonRenderWidget->setCursor(graphicsWidget->cursor()); - }); - - connect(m_document, &Document::skeletonChanged, this, &SkeletonDocumentWindow::documentChanged); - connect(m_document, &Document::turnaroundChanged, this, &SkeletonDocumentWindow::documentChanged); - connect(m_document, &Document::optionsChanged, this, &SkeletonDocumentWindow::documentChanged); - connect(m_document, &Document::rigChanged, this, &SkeletonDocumentWindow::documentChanged); - - connect(m_modelRenderWidget, &ModelWidget::customContextMenuRequested, [=](const QPoint &pos) { - graphicsWidget->showContextMenu(graphicsWidget->mapFromGlobal(m_modelRenderWidget->mapToGlobal(pos))); - }); - - connect(m_document, &Document::xlockStateChanged, this, &SkeletonDocumentWindow::updateXlockButtonState); - connect(m_document, &Document::ylockStateChanged, this, &SkeletonDocumentWindow::updateYlockButtonState); - connect(m_document, &Document::zlockStateChanged, this, &SkeletonDocumentWindow::updateZlockButtonState); - connect(m_document, &Document::radiusLockStateChanged, this, &SkeletonDocumentWindow::updateRadiusLockButtonState); - - connect(m_rigWidget, &RigWidget::setRigType, m_document, &Document::setRigType); - - connect(m_document, &Document::rigTypeChanged, m_rigWidget, &RigWidget::rigTypeChanged); - connect(m_document, &Document::resultRigChanged, m_rigWidget, &RigWidget::updateResultInfo); - connect(m_document, &Document::resultRigChanged, this, &SkeletonDocumentWindow::updateRigWeightRenderWidget); - - //connect(m_document, &SkeletonDocument::resultRigChanged, tetrapodPoseEditWidget, &TetrapodPoseEditWidget::updatePreview); - - connect(m_document, &Document::poseAdded, this, [=](QUuid poseId) { - Q_UNUSED(poseId); - m_document->generatePosePreviews(); - }); - connect(m_document, &Document::poseParametersChanged, this, [=](QUuid poseId) { - Q_UNUSED(poseId); - m_document->generatePosePreviews(); - }); - connect(m_document, &Document::resultRigChanged, m_document, &Document::generatePosePreviews); - - connect(m_document, &Document::resultRigChanged, m_document, &Document::generateMotions); - - connect(m_document, &Document::materialAdded, this, [=](QUuid materialId) { - Q_UNUSED(materialId); - m_document->generateMaterialPreviews(); - }); - connect(m_document, &Document::materialLayersChanged, this, [=](QUuid materialId) { - Q_UNUSED(materialId); - m_document->generateMaterialPreviews(); - }); - - initShortCuts(this, m_graphicsWidget); - - connect(this, &SkeletonDocumentWindow::initialized, m_document, &Document::uiReady); - - QTimer *timer = new QTimer(this); - timer->setInterval(250); - connect(timer, &QTimer::timeout, [=] { - QWidget *focusedWidget = QApplication::focusWidget(); - //qDebug() << (focusedWidget ? ("Focused on:" + QString(focusedWidget->metaObject()->className()) + " title:" + focusedWidget->windowTitle()) : "No Focus") << " isActive:" << isActiveWindow(); - if (nullptr == focusedWidget && isActiveWindow()) - graphicsWidget->setFocus(); - }); - timer->start(); -} - -SkeletonDocumentWindow *SkeletonDocumentWindow::createDocumentWindow() -{ - SkeletonDocumentWindow *documentWindow = new SkeletonDocumentWindow(); - documentWindow->setAttribute(Qt::WA_DeleteOnClose); - documentWindow->showMaximized(); - return documentWindow; -} - -void SkeletonDocumentWindow::closeEvent(QCloseEvent *event) -{ - if (m_documentSaved) { - event->accept(); - return; - } - - QMessageBox::StandardButton answer = QMessageBox::question(this, - APP_NAME, - tr("Do you really want to close while there are unsaved changes?"), - QMessageBox::Yes | QMessageBox::No); - if (answer == QMessageBox::Yes) - event->accept(); - else - event->ignore(); -} - -void SkeletonDocumentWindow::setCurrentFilename(const QString &filename) -{ - m_currentFilename = filename; - m_documentSaved = true; - updateTitle(); -} - -void SkeletonDocumentWindow::updateTitle() -{ - QString appName = APP_NAME; - QString appVer = APP_HUMAN_VER; - setWindowTitle(QString("%1 %2 %3%4").arg(appName).arg(appVer).arg(m_currentFilename).arg(m_documentSaved ? "" : "*")); -} - -void SkeletonDocumentWindow::documentChanged() -{ - if (m_documentSaved) { - m_documentSaved = false; - updateTitle(); - } -} - -void SkeletonDocumentWindow::newWindow() -{ - SkeletonDocumentWindow::createDocumentWindow(); -} - -void SkeletonDocumentWindow::newDocument() -{ - if (!m_documentSaved) { - QMessageBox::StandardButton answer = QMessageBox::question(this, - APP_NAME, - tr("Do you really want to create new document and lose the unsaved changes?"), - QMessageBox::Yes | QMessageBox::No); - if (answer != QMessageBox::Yes) - return; - } - m_document->reset(); -} - -void SkeletonDocumentWindow::saveAs() -{ - QString filename = QFileDialog::getSaveFileName(this, QString(), QString(), - tr("Dust3D Document (*.ds3)")); - if (filename.isEmpty()) { - return; - } - saveTo(filename); -} - -void SkeletonDocumentWindow::saveAll() -{ - for (auto &window: g_documentWindows) { - window->save(); - } -} - -void SkeletonDocumentWindow::viewSource() -{ - QString url = APP_REPOSITORY_URL; - qDebug() << "viewSource:" << url; - QDesktopServices::openUrl(QUrl(url)); -} - -void SkeletonDocumentWindow::about() -{ - SkeletonDocumentWindow::showAbout(); -} - -void SkeletonDocumentWindow::reportIssues() -{ - QString url = APP_ISSUES_URL; - qDebug() << "reportIssues:" << url; - QDesktopServices::openUrl(QUrl(url)); -} - -void SkeletonDocumentWindow::seeReferenceGuide() -{ - QString url = APP_REFERENCE_GUIDE_URL; - qDebug() << "referenceGuide:" << url; - QDesktopServices::openUrl(QUrl(url)); -} - -void SkeletonDocumentWindow::seeAcknowlegements() -{ - SkeletonDocumentWindow::showAcknowlegements(); -} - -void SkeletonDocumentWindow::seeContributors() -{ - SkeletonDocumentWindow::showContributors(); -} - -void SkeletonDocumentWindow::initLockButton(QPushButton *button) -{ - QFont font; - font.setWeight(QFont::Light); - font.setPixelSize(Theme::toolIconFontSize); - font.setBold(false); - - button->setFont(font); - button->setFixedSize(Theme::toolIconSize, Theme::toolIconSize); - button->setStyleSheet("QPushButton {color: #f7d9c8}"); - button->setFocusPolicy(Qt::NoFocus); -} - -SkeletonDocumentWindow::~SkeletonDocumentWindow() -{ - g_documentWindows.erase(this); -} - -void SkeletonDocumentWindow::showEvent(QShowEvent *event) -{ - QMainWindow::showEvent(event); - if (m_firstShow) { - m_firstShow = false; - updateTitle(); - m_document->silentReset(); - m_graphicsWidget->setFocus(); - emit initialized(); - } -} - -void SkeletonDocumentWindow::mousePressEvent(QMouseEvent *event) -{ - QMainWindow::mousePressEvent(event); -} - -void SkeletonDocumentWindow::changeTurnaround() -{ - QString fileName = QFileDialog::getOpenFileName(this, QString(), QString(), - tr("Image Files (*.png *.jpg *.bmp)")).trimmed(); - if (fileName.isEmpty()) - return; - QImage image; - if (!image.load(fileName)) - return; - m_document->updateTurnaround(image); -} - -void SkeletonDocumentWindow::save() -{ - saveTo(m_currentFilename); -} - -void SkeletonDocumentWindow::saveTo(const QString &saveAsFilename) -{ - QString filename = saveAsFilename; - - if (filename.isEmpty()) { - filename = QFileDialog::getSaveFileName(this, QString(), QString(), - tr("Dust3D Document (*.ds3)")); - if (filename.isEmpty()) { - return; - } - } - - QApplication::setOverrideCursor(Qt::WaitCursor); - - Ds3FileWriter ds3Writer; - - QByteArray modelXml; - QXmlStreamWriter stream(&modelXml); - Snapshot snapshot; - m_document->toSnapshot(&snapshot); - saveSkeletonToXmlStream(&snapshot, &stream); - if (modelXml.size() > 0) - ds3Writer.add("model.xml", "model", &modelXml); - - QByteArray imageByteArray; - QBuffer pngBuffer(&imageByteArray); - if (!m_document->turnaround.isNull()) { - pngBuffer.open(QIODevice::WriteOnly); - m_document->turnaround.save(&pngBuffer, "PNG"); - if (imageByteArray.size() > 0) - ds3Writer.add("canvas.png", "asset", &imageByteArray); - } - - for (auto &material: snapshot.materials) { - for (auto &layer: material.second) { - for (auto &mapItem: layer.second) { - auto findImageIdString = mapItem.find("linkData"); - if (findImageIdString == mapItem.end()) - continue; - QUuid imageId = QUuid(findImageIdString->second); - const QImage *image = ImageForever::get(imageId); - if (nullptr == image) - continue; - QByteArray imageByteArray; - QBuffer pngBuffer(&imageByteArray); - pngBuffer.open(QIODevice::WriteOnly); - image->save(&pngBuffer, "PNG"); - if (imageByteArray.size() > 0) - ds3Writer.add("images/" + imageId.toString() + ".png", "asset", &imageByteArray); - } - } - } - - if (ds3Writer.save(filename)) { - setCurrentFilename(filename); - } - - QApplication::restoreOverrideCursor(); -} - -void SkeletonDocumentWindow::open() -{ - if (!m_documentSaved) { - QMessageBox::StandardButton answer = QMessageBox::question(this, - APP_NAME, - tr("Do you really want to open another file and lose the unsaved changes?"), - QMessageBox::Yes | QMessageBox::No); - if (answer != QMessageBox::Yes) - return; - } - - QString filename = QFileDialog::getOpenFileName(this, QString(), QString(), - tr("Dust3D Document (*.ds3)")); - if (filename.isEmpty()) - return; - - QApplication::setOverrideCursor(Qt::WaitCursor); - Ds3FileReader ds3Reader(filename); - - for (int i = 0; i < ds3Reader.items().size(); ++i) { - Ds3ReaderItem item = ds3Reader.items().at(i); - if (item.type == "asset") { - if (item.name.startsWith("images/")) { - QString filename = item.name.split("/")[1]; - QString imageIdString = filename.split(".")[0]; - QUuid imageId = QUuid(imageIdString); - if (!imageId.isNull()) { - QByteArray data; - ds3Reader.loadItem(item.name, &data); - QImage image = QImage::fromData(data, "PNG"); - (void)ImageForever::add(&image, imageId); - } - } - } - } - - for (int i = 0; i < ds3Reader.items().size(); ++i) { - Ds3ReaderItem item = ds3Reader.items().at(i); - if (item.type == "model") { - QByteArray data; - ds3Reader.loadItem(item.name, &data); - QXmlStreamReader stream(data); - Snapshot snapshot; - loadSkeletonFromXmlStream(&snapshot, stream); - m_document->fromSnapshot(snapshot); - m_document->saveSnapshot(); - } else if (item.type == "asset") { - if (item.name == "canvas.png") { - QByteArray data; - ds3Reader.loadItem(item.name, &data); - QImage image = QImage::fromData(data, "PNG"); - m_document->updateTurnaround(image); - } - } - } - QApplication::restoreOverrideCursor(); - - 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(), - tr("Wavefront (*.obj)")); - if (filename.isEmpty()) { - return; - } - QApplication::setOverrideCursor(Qt::WaitCursor); - MeshLoader *resultMesh = m_document->takeResultMesh(); - if (nullptr != resultMesh) { - resultMesh->exportAsObj(filename); - delete resultMesh; - } - QApplication::restoreOverrideCursor(); -} - -void SkeletonDocumentWindow::showExportPreview() -{ - if (nullptr == m_exportPreviewWidget) { - m_exportPreviewWidget = new ExportPreviewWidget(m_document, this); - connect(m_exportPreviewWidget, &ExportPreviewWidget::regenerate, m_document, &Document::regenerateMesh); - connect(m_exportPreviewWidget, &ExportPreviewWidget::saveAsGltf, this, &SkeletonDocumentWindow::exportGltfResult); - connect(m_exportPreviewWidget, &ExportPreviewWidget::saveAsFbx, this, &SkeletonDocumentWindow::exportFbxResult); - connect(m_document, &Document::resultMeshChanged, m_exportPreviewWidget, &ExportPreviewWidget::checkSpinner); - connect(m_document, &Document::exportReady, m_exportPreviewWidget, &ExportPreviewWidget::checkSpinner); - connect(m_document, &Document::resultTextureChanged, m_exportPreviewWidget, &ExportPreviewWidget::updateTexturePreview); - connect(m_document, &Document::resultBakedTextureChanged, m_exportPreviewWidget, &ExportPreviewWidget::updateTexturePreview); - registerDialog(m_exportPreviewWidget); - } - m_exportPreviewWidget->show(); - m_exportPreviewWidget->raise(); -} - -void SkeletonDocumentWindow::exportFbxResult() -{ - QString filename = QFileDialog::getSaveFileName(this, QString(), QString(), - tr("Autodesk FBX (.fbx)")); - if (filename.isEmpty()) { - return; - } - if (!m_document->isExportReady()) { - qDebug() << "Export but document is not export ready"; - return; - } - QApplication::setOverrideCursor(Qt::WaitCursor); - Outcome skeletonResult = m_document->currentPostProcessedResultContext(); - FbxFileWriter fbxFileWriter(skeletonResult, m_document->resultRigBones(), m_document->resultRigWeights(), filename); - fbxFileWriter.save(); - QApplication::restoreOverrideCursor(); -} - -void SkeletonDocumentWindow::exportGltfResult() -{ - QString filename = QFileDialog::getSaveFileName(this, QString(), QString(), - tr("GL Transmission Format (.gltf)")); - if (filename.isEmpty()) { - return; - } - if (!m_document->isExportReady()) { - qDebug() << "Export but document is not export ready"; - return; - } - QApplication::setOverrideCursor(Qt::WaitCursor); - Outcome skeletonResult = m_document->currentPostProcessedResultContext(); - GltfFileWriter gltfFileWriter(skeletonResult, m_document->resultRigBones(), m_document->resultRigWeights(), filename); - gltfFileWriter.save(); - if (m_document->textureImage) - m_document->textureImage->save(gltfFileWriter.textureFilenameInGltf()); - QFileInfo nameInfo(filename); - QString borderFilename = nameInfo.path() + QDir::separator() + nameInfo.completeBaseName() + "-BORDER.png"; - if (m_document->textureBorderImage) - m_document->textureBorderImage->save(borderFilename); - QString ambientOcclusionFilename = nameInfo.path() + QDir::separator() + nameInfo.completeBaseName() + "-AMBIENT-OCCLUSION.png"; - if (m_document->textureAmbientOcclusionImage) - m_document->textureAmbientOcclusionImage->save(ambientOcclusionFilename); - QString colorFilename = nameInfo.path() + QDir::separator() + nameInfo.completeBaseName() + "-COLOR.png"; - if (m_document->textureColorImage) - m_document->textureColorImage->save(colorFilename); - QApplication::restoreOverrideCursor(); -} - -void SkeletonDocumentWindow::updateXlockButtonState() -{ - if (m_document->xlocked) - m_xlockButton->setStyleSheet("QPushButton {color: #252525}"); - else - m_xlockButton->setStyleSheet("QPushButton {color: #fc6621}"); -} - -void SkeletonDocumentWindow::updateYlockButtonState() -{ - if (m_document->ylocked) - m_ylockButton->setStyleSheet("QPushButton {color: #252525}"); - else - m_ylockButton->setStyleSheet("QPushButton {color: #2a5aac}"); -} - -void SkeletonDocumentWindow::updateZlockButtonState() -{ - if (m_document->zlocked) - m_zlockButton->setStyleSheet("QPushButton {color: #252525}"); - else - m_zlockButton->setStyleSheet("QPushButton {color: #aaebc4}"); -} - -void SkeletonDocumentWindow::updateRadiusLockButtonState() -{ - if (m_document->radiusLocked) - m_radiusLockButton->setStyleSheet("QPushButton {color: #252525}"); - else - m_radiusLockButton->setStyleSheet("QPushButton {color: " + Theme::white.name() + "}"); -} - -void SkeletonDocumentWindow::updateRigWeightRenderWidget() -{ - MeshLoader *resultRigWeightMesh = m_document->takeResultRigWeightMesh(); - if (nullptr == resultRigWeightMesh) { - m_rigWidget->rigWeightRenderWidget()->hide(); - } else { - m_rigWidget->rigWeightRenderWidget()->updateMesh(resultRigWeightMesh); - m_rigWidget->rigWeightRenderWidget()->show(); - m_rigWidget->rigWeightRenderWidget()->update(); - } -} - -void SkeletonDocumentWindow::registerDialog(QWidget *widget) -{ - m_dialogs.push_back(widget); -} - -void SkeletonDocumentWindow::unregisterDialog(QWidget *widget) -{ - m_dialogs.erase(std::remove(m_dialogs.begin(), m_dialogs.end(), widget), m_dialogs.end()); -} diff --git a/src/documentwindow.cpp b/src/documentwindow.cpp index f246e703..22e62ced 100644 --- a/src/documentwindow.cpp +++ b/src/documentwindow.cpp @@ -37,15 +37,15 @@ #include "fbxfile.h" #include "shortcuts.h" -int SkeletonDocumentWindow::m_modelRenderWidgetInitialX = 16; -int SkeletonDocumentWindow::m_modelRenderWidgetInitialY = 16; -int SkeletonDocumentWindow::m_modelRenderWidgetInitialSize = 128; -int SkeletonDocumentWindow::m_skeletonRenderWidgetInitialX = SkeletonDocumentWindow::m_modelRenderWidgetInitialX + SkeletonDocumentWindow::m_modelRenderWidgetInitialSize + 16; -int SkeletonDocumentWindow::m_skeletonRenderWidgetInitialY = SkeletonDocumentWindow::m_modelRenderWidgetInitialY; -int SkeletonDocumentWindow::m_skeletonRenderWidgetInitialSize = SkeletonDocumentWindow::m_modelRenderWidgetInitialSize; +int DocumentWindow::m_modelRenderWidgetInitialX = 16; +int DocumentWindow::m_modelRenderWidgetInitialY = 16; +int DocumentWindow::m_modelRenderWidgetInitialSize = 128; +int DocumentWindow::m_skeletonRenderWidgetInitialX = DocumentWindow::m_modelRenderWidgetInitialX + DocumentWindow::m_modelRenderWidgetInitialSize + 16; +int DocumentWindow::m_skeletonRenderWidgetInitialY = DocumentWindow::m_modelRenderWidgetInitialY; +int DocumentWindow::m_skeletonRenderWidgetInitialSize = DocumentWindow::m_modelRenderWidgetInitialSize; LogBrowser *g_logBrowser = nullptr; -std::set g_documentWindows; +std::set g_documentWindows; QTextBrowser *g_acknowlegementsWidget = nullptr; AboutWidget *g_aboutWidget = nullptr; QTextBrowser *g_contributorsWidget = nullptr; @@ -56,7 +56,7 @@ void outputMessage(QtMsgType type, const QMessageLogContext &context, const QStr g_logBrowser->outputMessage(type, msg, context.file, context.line); } -void SkeletonDocumentWindow::showAcknowlegements() +void DocumentWindow::showAcknowlegements() { if (!g_acknowlegementsWidget) { g_acknowlegementsWidget = new QTextBrowser; @@ -72,7 +72,7 @@ void SkeletonDocumentWindow::showAcknowlegements() g_acknowlegementsWidget->raise(); } -void SkeletonDocumentWindow::showContributors() +void DocumentWindow::showContributors() { if (!g_contributorsWidget) { g_contributorsWidget = new QTextBrowser; @@ -89,7 +89,7 @@ void SkeletonDocumentWindow::showContributors() g_contributorsWidget->raise(); } -void SkeletonDocumentWindow::showAbout() +void DocumentWindow::showAbout() { if (!g_aboutWidget) { g_aboutWidget = new AboutWidget; @@ -99,7 +99,7 @@ void SkeletonDocumentWindow::showAbout() g_aboutWidget->raise(); } -SkeletonDocumentWindow::SkeletonDocumentWindow() : +DocumentWindow::DocumentWindow() : m_document(nullptr), m_firstShow(true), m_documentSaved(true), @@ -220,9 +220,9 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : m_modelRenderWidget = new ModelWidget(containerWidget); m_modelRenderWidget->setAttribute(Qt::WA_TransparentForMouseEvents); - m_modelRenderWidget->setMinimumSize(SkeletonDocumentWindow::m_modelRenderWidgetInitialSize, SkeletonDocumentWindow::m_modelRenderWidgetInitialSize); + m_modelRenderWidget->setMinimumSize(DocumentWindow::m_modelRenderWidgetInitialSize, DocumentWindow::m_modelRenderWidgetInitialSize); m_modelRenderWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - m_modelRenderWidget->move(SkeletonDocumentWindow::m_modelRenderWidgetInitialX, SkeletonDocumentWindow::m_modelRenderWidgetInitialY); + m_modelRenderWidget->move(DocumentWindow::m_modelRenderWidgetInitialX, DocumentWindow::m_modelRenderWidgetInitialY); m_graphicsWidget->setModelWidget(m_modelRenderWidget); containerWidget->setModelWidget(m_modelRenderWidget); @@ -246,8 +246,8 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : materialDocker->setAllowedAreas(Qt::RightDockWidgetArea); MaterialManageWidget *materialManageWidget = new MaterialManageWidget(m_document, materialDocker); materialDocker->setWidget(materialManageWidget); - connect(materialManageWidget, &MaterialManageWidget::registerDialog, this, &SkeletonDocumentWindow::registerDialog); - connect(materialManageWidget, &MaterialManageWidget::unregisterDialog, this, &SkeletonDocumentWindow::unregisterDialog); + connect(materialManageWidget, &MaterialManageWidget::registerDialog, this, &DocumentWindow::registerDialog); + connect(materialManageWidget, &MaterialManageWidget::unregisterDialog, this, &DocumentWindow::unregisterDialog); addDockWidget(Qt::RightDockWidgetArea, materialDocker); connect(materialDocker, &QDockWidget::topLevelChanged, [=](bool topLevel) { Q_UNUSED(topLevel); @@ -269,8 +269,8 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : poseDocker->setAllowedAreas(Qt::RightDockWidgetArea); PoseManageWidget *poseManageWidget = new PoseManageWidget(m_document, poseDocker); poseDocker->setWidget(poseManageWidget); - connect(poseManageWidget, &PoseManageWidget::registerDialog, this, &SkeletonDocumentWindow::registerDialog); - connect(poseManageWidget, &PoseManageWidget::unregisterDialog, this, &SkeletonDocumentWindow::unregisterDialog); + connect(poseManageWidget, &PoseManageWidget::registerDialog, this, &DocumentWindow::registerDialog); + connect(poseManageWidget, &PoseManageWidget::unregisterDialog, this, &DocumentWindow::unregisterDialog); addDockWidget(Qt::RightDockWidgetArea, poseDocker); connect(poseDocker, &QDockWidget::topLevelChanged, [=](bool topLevel) { Q_UNUSED(topLevel); @@ -282,8 +282,8 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : motionDocker->setAllowedAreas(Qt::RightDockWidgetArea); MotionManageWidget *motionManageWidget = new MotionManageWidget(m_document, motionDocker); motionDocker->setWidget(motionManageWidget); - connect(motionManageWidget, &MotionManageWidget::registerDialog, this, &SkeletonDocumentWindow::registerDialog); - connect(motionManageWidget, &MotionManageWidget::unregisterDialog, this, &SkeletonDocumentWindow::unregisterDialog); + connect(motionManageWidget, &MotionManageWidget::registerDialog, this, &DocumentWindow::registerDialog); + connect(motionManageWidget, &MotionManageWidget::unregisterDialog, this, &DocumentWindow::unregisterDialog); addDockWidget(Qt::RightDockWidgetArea, motionDocker); tabifyDockWidget(partTreeDocker, materialDocker); @@ -309,27 +309,27 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : m_fileMenu = menuBar()->addMenu(tr("File")); m_newWindowAction = new QAction(tr("New Window"), this); - connect(m_newWindowAction, &QAction::triggered, this, &SkeletonDocumentWindow::newWindow, Qt::QueuedConnection); + connect(m_newWindowAction, &QAction::triggered, this, &DocumentWindow::newWindow, Qt::QueuedConnection); m_fileMenu->addAction(m_newWindowAction); m_newDocumentAction = new QAction(tr("New"), this); - connect(m_newDocumentAction, &QAction::triggered, this, &SkeletonDocumentWindow::newDocument); + connect(m_newDocumentAction, &QAction::triggered, this, &DocumentWindow::newDocument); m_fileMenu->addAction(m_newDocumentAction); m_openAction = new QAction(tr("Open..."), this); - connect(m_openAction, &QAction::triggered, this, &SkeletonDocumentWindow::open, Qt::QueuedConnection); + connect(m_openAction, &QAction::triggered, this, &DocumentWindow::open, Qt::QueuedConnection); m_fileMenu->addAction(m_openAction); m_saveAction = new QAction(tr("Save"), this); - connect(m_saveAction, &QAction::triggered, this, &SkeletonDocumentWindow::save, Qt::QueuedConnection); + connect(m_saveAction, &QAction::triggered, this, &DocumentWindow::save, Qt::QueuedConnection); m_fileMenu->addAction(m_saveAction); m_saveAsAction = new QAction(tr("Save As..."), this); - connect(m_saveAsAction, &QAction::triggered, this, &SkeletonDocumentWindow::saveAs, Qt::QueuedConnection); + connect(m_saveAsAction, &QAction::triggered, this, &DocumentWindow::saveAs, Qt::QueuedConnection); m_fileMenu->addAction(m_saveAsAction); m_saveAllAction = new QAction(tr("Save All"), this); - connect(m_saveAllAction, &QAction::triggered, this, &SkeletonDocumentWindow::saveAll, Qt::QueuedConnection); + connect(m_saveAllAction, &QAction::triggered, this, &DocumentWindow::saveAll, Qt::QueuedConnection); m_fileMenu->addAction(m_saveAllAction); m_fileMenu->addSeparator(); @@ -337,11 +337,11 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : //m_exportMenu = m_fileMenu->addMenu(tr("Export")); m_exportAction = new QAction(tr("Export..."), this); - connect(m_exportAction, &QAction::triggered, this, &SkeletonDocumentWindow::showExportPreview, Qt::QueuedConnection); + connect(m_exportAction, &QAction::triggered, this, &DocumentWindow::showExportPreview, Qt::QueuedConnection); m_fileMenu->addAction(m_exportAction); m_exportAsObjAction = new QAction(tr("Export as OBJ..."), this); - connect(m_exportAsObjAction, &QAction::triggered, this, &SkeletonDocumentWindow::exportObjResult, Qt::QueuedConnection); + connect(m_exportAsObjAction, &QAction::triggered, this, &DocumentWindow::exportObjResult, Qt::QueuedConnection); m_fileMenu->addAction(m_exportAsObjAction); //m_exportRenderedAsImageAction = new QAction(tr("Export as PNG..."), this); @@ -355,7 +355,7 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : m_fileMenu->addSeparator(); m_changeTurnaroundAction = new QAction(tr("Change Reference Sheet..."), this); - connect(m_changeTurnaroundAction, &QAction::triggered, this, &SkeletonDocumentWindow::changeTurnaround, Qt::QueuedConnection); + connect(m_changeTurnaroundAction, &QAction::triggered, this, &DocumentWindow::changeTurnaround, Qt::QueuedConnection); m_fileMenu->addAction(m_changeTurnaroundAction); m_fileMenu->addSeparator(); @@ -530,7 +530,7 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : m_resetModelWidgetPosAction = new QAction(tr("Show Model"), this); connect(m_resetModelWidgetPosAction, &QAction::triggered, [=]() { if (!isModelSitInVisibleArea(m_modelRenderWidget)) { - m_modelRenderWidget->move(SkeletonDocumentWindow::m_modelRenderWidgetInitialX, SkeletonDocumentWindow::m_modelRenderWidgetInitialY); + m_modelRenderWidget->move(DocumentWindow::m_modelRenderWidgetInitialX, DocumentWindow::m_modelRenderWidgetInitialY); } }); m_viewMenu->addAction(m_resetModelWidgetPosAction); @@ -610,7 +610,7 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : m_windowMenu->addAction(m_showDebugDialogAction); m_showAdvanceSettingAction = new QAction(tr("Advance"), this); - connect(m_showAdvanceSettingAction, &QAction::triggered, this, &SkeletonDocumentWindow::showAdvanceSetting); + connect(m_showAdvanceSettingAction, &QAction::triggered, this, &DocumentWindow::showAdvanceSetting); #ifndef NDEBUG m_windowMenu->addAction(m_showAdvanceSettingAction); #endif @@ -618,33 +618,33 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : m_helpMenu = menuBar()->addMenu(tr("Help")); m_viewSourceAction = new QAction(tr("Fork me on GitHub"), this); - connect(m_viewSourceAction, &QAction::triggered, this, &SkeletonDocumentWindow::viewSource); + connect(m_viewSourceAction, &QAction::triggered, this, &DocumentWindow::viewSource); m_helpMenu->addAction(m_viewSourceAction); m_helpMenu->addSeparator(); m_seeReferenceGuideAction = new QAction(tr("Reference Guide"), this); - connect(m_seeReferenceGuideAction, &QAction::triggered, this, &SkeletonDocumentWindow::seeReferenceGuide); + connect(m_seeReferenceGuideAction, &QAction::triggered, this, &DocumentWindow::seeReferenceGuide); m_helpMenu->addAction(m_seeReferenceGuideAction); m_helpMenu->addSeparator(); m_aboutAction = new QAction(tr("About"), this); - connect(m_aboutAction, &QAction::triggered, this, &SkeletonDocumentWindow::about); + connect(m_aboutAction, &QAction::triggered, this, &DocumentWindow::about); m_helpMenu->addAction(m_aboutAction); m_reportIssuesAction = new QAction(tr("Report Issues"), this); - connect(m_reportIssuesAction, &QAction::triggered, this, &SkeletonDocumentWindow::reportIssues); + connect(m_reportIssuesAction, &QAction::triggered, this, &DocumentWindow::reportIssues); m_helpMenu->addAction(m_reportIssuesAction); m_helpMenu->addSeparator(); m_seeContributorsAction = new QAction(tr("Contributors"), this); - connect(m_seeContributorsAction, &QAction::triggered, this, &SkeletonDocumentWindow::seeContributors); + connect(m_seeContributorsAction, &QAction::triggered, this, &DocumentWindow::seeContributors); m_helpMenu->addAction(m_seeContributorsAction); m_seeAcknowlegementsAction = new QAction(tr("Acknowlegements"), this); - connect(m_seeAcknowlegementsAction, &QAction::triggered, this, &SkeletonDocumentWindow::seeAcknowlegements); + connect(m_seeAcknowlegementsAction, &QAction::triggered, this, &DocumentWindow::seeAcknowlegements); m_helpMenu->addAction(m_seeAcknowlegementsAction); connect(containerWidget, &GraphicsContainerWidget::containerSizeChanged, @@ -737,9 +737,9 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : connect(graphicsWidget, &SkeletonGraphicsWidget::enableAllPositionRelatedLocks, m_document, &Document::enableAllPositionRelatedLocks); connect(graphicsWidget, &SkeletonGraphicsWidget::disableAllPositionRelatedLocks, m_document, &Document::disableAllPositionRelatedLocks); - connect(graphicsWidget, &SkeletonGraphicsWidget::changeTurnaround, this, &SkeletonDocumentWindow::changeTurnaround); - connect(graphicsWidget, &SkeletonGraphicsWidget::save, this, &SkeletonDocumentWindow::save); - connect(graphicsWidget, &SkeletonGraphicsWidget::open, this, &SkeletonDocumentWindow::open); + connect(graphicsWidget, &SkeletonGraphicsWidget::changeTurnaround, this, &DocumentWindow::changeTurnaround); + connect(graphicsWidget, &SkeletonGraphicsWidget::save, this, &DocumentWindow::save); + connect(graphicsWidget, &SkeletonGraphicsWidget::open, this, &DocumentWindow::open); connect(m_document, &Document::nodeAdded, graphicsWidget, &SkeletonGraphicsWidget::nodeAdded); connect(m_document, &Document::nodeRemoved, graphicsWidget, &SkeletonGraphicsWidget::nodeRemoved); @@ -848,25 +848,25 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : //m_skeletonRenderWidget->setCursor(graphicsWidget->cursor()); }); - connect(m_document, &Document::skeletonChanged, this, &SkeletonDocumentWindow::documentChanged); - connect(m_document, &Document::turnaroundChanged, this, &SkeletonDocumentWindow::documentChanged); - connect(m_document, &Document::optionsChanged, this, &SkeletonDocumentWindow::documentChanged); - connect(m_document, &Document::rigChanged, this, &SkeletonDocumentWindow::documentChanged); + connect(m_document, &Document::skeletonChanged, this, &DocumentWindow::documentChanged); + connect(m_document, &Document::turnaroundChanged, this, &DocumentWindow::documentChanged); + connect(m_document, &Document::optionsChanged, this, &DocumentWindow::documentChanged); + connect(m_document, &Document::rigChanged, this, &DocumentWindow::documentChanged); connect(m_modelRenderWidget, &ModelWidget::customContextMenuRequested, [=](const QPoint &pos) { graphicsWidget->showContextMenu(graphicsWidget->mapFromGlobal(m_modelRenderWidget->mapToGlobal(pos))); }); - connect(m_document, &Document::xlockStateChanged, this, &SkeletonDocumentWindow::updateXlockButtonState); - connect(m_document, &Document::ylockStateChanged, this, &SkeletonDocumentWindow::updateYlockButtonState); - connect(m_document, &Document::zlockStateChanged, this, &SkeletonDocumentWindow::updateZlockButtonState); - connect(m_document, &Document::radiusLockStateChanged, this, &SkeletonDocumentWindow::updateRadiusLockButtonState); + connect(m_document, &Document::xlockStateChanged, this, &DocumentWindow::updateXlockButtonState); + connect(m_document, &Document::ylockStateChanged, this, &DocumentWindow::updateYlockButtonState); + connect(m_document, &Document::zlockStateChanged, this, &DocumentWindow::updateZlockButtonState); + connect(m_document, &Document::radiusLockStateChanged, this, &DocumentWindow::updateRadiusLockButtonState); connect(m_rigWidget, &RigWidget::setRigType, m_document, &Document::setRigType); connect(m_document, &Document::rigTypeChanged, m_rigWidget, &RigWidget::rigTypeChanged); connect(m_document, &Document::resultRigChanged, m_rigWidget, &RigWidget::updateResultInfo); - connect(m_document, &Document::resultRigChanged, this, &SkeletonDocumentWindow::updateRigWeightRenderWidget); + connect(m_document, &Document::resultRigChanged, this, &DocumentWindow::updateRigWeightRenderWidget); //connect(m_document, &SkeletonDocument::resultRigChanged, tetrapodPoseEditWidget, &TetrapodPoseEditWidget::updatePreview); @@ -893,7 +893,7 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : initShortCuts(this, m_graphicsWidget); - connect(this, &SkeletonDocumentWindow::initialized, m_document, &Document::uiReady); + connect(this, &DocumentWindow::initialized, m_document, &Document::uiReady); QTimer *timer = new QTimer(this); timer->setInterval(250); @@ -906,15 +906,15 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : timer->start(); } -SkeletonDocumentWindow *SkeletonDocumentWindow::createDocumentWindow() +DocumentWindow *DocumentWindow::createDocumentWindow() { - SkeletonDocumentWindow *documentWindow = new SkeletonDocumentWindow(); + DocumentWindow *documentWindow = new DocumentWindow(); documentWindow->setAttribute(Qt::WA_DeleteOnClose); documentWindow->showMaximized(); return documentWindow; } -void SkeletonDocumentWindow::closeEvent(QCloseEvent *event) +void DocumentWindow::closeEvent(QCloseEvent *event) { if (m_documentSaved) { event->accept(); @@ -931,21 +931,21 @@ void SkeletonDocumentWindow::closeEvent(QCloseEvent *event) event->ignore(); } -void SkeletonDocumentWindow::setCurrentFilename(const QString &filename) +void DocumentWindow::setCurrentFilename(const QString &filename) { m_currentFilename = filename; m_documentSaved = true; updateTitle(); } -void SkeletonDocumentWindow::updateTitle() +void DocumentWindow::updateTitle() { QString appName = APP_NAME; QString appVer = APP_HUMAN_VER; setWindowTitle(QString("%1 %2 %3%4").arg(appName).arg(appVer).arg(m_currentFilename).arg(m_documentSaved ? "" : "*")); } -void SkeletonDocumentWindow::documentChanged() +void DocumentWindow::documentChanged() { if (m_documentSaved) { m_documentSaved = false; @@ -953,12 +953,12 @@ void SkeletonDocumentWindow::documentChanged() } } -void SkeletonDocumentWindow::newWindow() +void DocumentWindow::newWindow() { - SkeletonDocumentWindow::createDocumentWindow(); + DocumentWindow::createDocumentWindow(); } -void SkeletonDocumentWindow::newDocument() +void DocumentWindow::newDocument() { if (!m_documentSaved) { QMessageBox::StandardButton answer = QMessageBox::question(this, @@ -973,7 +973,7 @@ void SkeletonDocumentWindow::newDocument() m_document->saveSnapshot(); } -void SkeletonDocumentWindow::saveAs() +void DocumentWindow::saveAs() { QString filename = QFileDialog::getSaveFileName(this, QString(), QString(), tr("Dust3D Document (*.ds3)")); @@ -983,50 +983,50 @@ void SkeletonDocumentWindow::saveAs() saveTo(filename); } -void SkeletonDocumentWindow::saveAll() +void DocumentWindow::saveAll() { for (auto &window: g_documentWindows) { window->save(); } } -void SkeletonDocumentWindow::viewSource() +void DocumentWindow::viewSource() { QString url = APP_REPOSITORY_URL; qDebug() << "viewSource:" << url; QDesktopServices::openUrl(QUrl(url)); } -void SkeletonDocumentWindow::about() +void DocumentWindow::about() { - SkeletonDocumentWindow::showAbout(); + DocumentWindow::showAbout(); } -void SkeletonDocumentWindow::reportIssues() +void DocumentWindow::reportIssues() { QString url = APP_ISSUES_URL; qDebug() << "reportIssues:" << url; QDesktopServices::openUrl(QUrl(url)); } -void SkeletonDocumentWindow::seeReferenceGuide() +void DocumentWindow::seeReferenceGuide() { QString url = APP_REFERENCE_GUIDE_URL; qDebug() << "referenceGuide:" << url; QDesktopServices::openUrl(QUrl(url)); } -void SkeletonDocumentWindow::seeAcknowlegements() +void DocumentWindow::seeAcknowlegements() { - SkeletonDocumentWindow::showAcknowlegements(); + DocumentWindow::showAcknowlegements(); } -void SkeletonDocumentWindow::seeContributors() +void DocumentWindow::seeContributors() { - SkeletonDocumentWindow::showContributors(); + DocumentWindow::showContributors(); } -void SkeletonDocumentWindow::initLockButton(QPushButton *button) +void DocumentWindow::initLockButton(QPushButton *button) { QFont font; font.setWeight(QFont::Light); @@ -1039,12 +1039,12 @@ void SkeletonDocumentWindow::initLockButton(QPushButton *button) button->setFocusPolicy(Qt::NoFocus); } -SkeletonDocumentWindow::~SkeletonDocumentWindow() +DocumentWindow::~DocumentWindow() { g_documentWindows.erase(this); } -void SkeletonDocumentWindow::showEvent(QShowEvent *event) +void DocumentWindow::showEvent(QShowEvent *event) { QMainWindow::showEvent(event); if (m_firstShow) { @@ -1056,12 +1056,12 @@ void SkeletonDocumentWindow::showEvent(QShowEvent *event) } } -void SkeletonDocumentWindow::mousePressEvent(QMouseEvent *event) +void DocumentWindow::mousePressEvent(QMouseEvent *event) { QMainWindow::mousePressEvent(event); } -void SkeletonDocumentWindow::changeTurnaround() +void DocumentWindow::changeTurnaround() { QString fileName = QFileDialog::getOpenFileName(this, QString(), QString(), tr("Image Files (*.png *.jpg *.bmp)")).trimmed(); @@ -1073,12 +1073,12 @@ void SkeletonDocumentWindow::changeTurnaround() m_document->updateTurnaround(image); } -void SkeletonDocumentWindow::save() +void DocumentWindow::save() { saveTo(m_currentFilename); } -void SkeletonDocumentWindow::saveTo(const QString &saveAsFilename) +void DocumentWindow::saveTo(const QString &saveAsFilename) { QString filename = saveAsFilename; @@ -1111,6 +1111,7 @@ void SkeletonDocumentWindow::saveTo(const QString &saveAsFilename) ds3Writer.add("canvas.png", "asset", &imageByteArray); } + std::set imageIds; for (auto &material: snapshot.materials) { for (auto &layer: material.second) { for (auto &mapItem: layer.second) { @@ -1118,18 +1119,30 @@ void SkeletonDocumentWindow::saveTo(const QString &saveAsFilename) if (findImageIdString == mapItem.end()) continue; QUuid imageId = QUuid(findImageIdString->second); - const QImage *image = ImageForever::get(imageId); - if (nullptr == image) - continue; - QByteArray imageByteArray; - QBuffer pngBuffer(&imageByteArray); - pngBuffer.open(QIODevice::WriteOnly); - image->save(&pngBuffer, "PNG"); - if (imageByteArray.size() > 0) - ds3Writer.add("images/" + imageId.toString() + ".png", "asset", &imageByteArray); + imageIds.insert(imageId); } } } + + for (auto &pose: snapshot.poses) { + auto findCanvasImageId = pose.first.find("canvasImageId"); + if (findCanvasImageId != pose.first.end()) { + QUuid imageId = QUuid(findCanvasImageId->second); + imageIds.insert(imageId); + } + } + + for (const auto &imageId: imageIds) { + const QImage *image = ImageForever::get(imageId); + if (nullptr == image) + continue; + QByteArray imageByteArray; + QBuffer pngBuffer(&imageByteArray); + pngBuffer.open(QIODevice::WriteOnly); + image->save(&pngBuffer, "PNG"); + if (imageByteArray.size() > 0) + ds3Writer.add("images/" + imageId.toString() + ".png", "asset", &imageByteArray); + } if (ds3Writer.save(filename)) { setCurrentFilename(filename); @@ -1138,7 +1151,7 @@ void SkeletonDocumentWindow::saveTo(const QString &saveAsFilename) QApplication::restoreOverrideCursor(); } -void SkeletonDocumentWindow::open() +void DocumentWindow::open() { if (!m_documentSaved) { QMessageBox::StandardButton answer = QMessageBox::question(this, @@ -1202,7 +1215,7 @@ void SkeletonDocumentWindow::open() setCurrentFilename(filename); } -void SkeletonDocumentWindow::showAdvanceSetting() +void DocumentWindow::showAdvanceSetting() { if (nullptr == m_advanceSettingWidget) { m_advanceSettingWidget = new AdvanceSettingWidget(m_document, this); @@ -1211,7 +1224,7 @@ void SkeletonDocumentWindow::showAdvanceSetting() m_advanceSettingWidget->raise(); } -void SkeletonDocumentWindow::exportObjResult() +void DocumentWindow::exportObjResult() { QString filename = QFileDialog::getSaveFileName(this, QString(), QString(), tr("Wavefront (*.obj)")); @@ -1227,13 +1240,13 @@ void SkeletonDocumentWindow::exportObjResult() QApplication::restoreOverrideCursor(); } -void SkeletonDocumentWindow::showExportPreview() +void DocumentWindow::showExportPreview() { if (nullptr == m_exportPreviewWidget) { m_exportPreviewWidget = new ExportPreviewWidget(m_document, this); connect(m_exportPreviewWidget, &ExportPreviewWidget::regenerate, m_document, &Document::regenerateMesh); - connect(m_exportPreviewWidget, &ExportPreviewWidget::saveAsGlb, this, &SkeletonDocumentWindow::exportGlbResult); - connect(m_exportPreviewWidget, &ExportPreviewWidget::saveAsFbx, this, &SkeletonDocumentWindow::exportFbxResult); + connect(m_exportPreviewWidget, &ExportPreviewWidget::saveAsGlb, this, &DocumentWindow::exportGlbResult); + connect(m_exportPreviewWidget, &ExportPreviewWidget::saveAsFbx, this, &DocumentWindow::exportFbxResult); connect(m_document, &Document::resultMeshChanged, m_exportPreviewWidget, &ExportPreviewWidget::checkSpinner); connect(m_document, &Document::exportReady, m_exportPreviewWidget, &ExportPreviewWidget::checkSpinner); connect(m_document, &Document::resultTextureChanged, m_exportPreviewWidget, &ExportPreviewWidget::updateTexturePreview); @@ -1244,7 +1257,7 @@ void SkeletonDocumentWindow::showExportPreview() m_exportPreviewWidget->raise(); } -void SkeletonDocumentWindow::exportFbxResult() +void DocumentWindow::exportFbxResult() { QString filename = QFileDialog::getSaveFileName(this, QString(), QString(), tr("Autodesk FBX (.fbx)")); @@ -1270,7 +1283,7 @@ void SkeletonDocumentWindow::exportFbxResult() QApplication::restoreOverrideCursor(); } -void SkeletonDocumentWindow::exportGlbResult() +void DocumentWindow::exportGlbResult() { QString filename = QFileDialog::getSaveFileName(this, QString(), QString(), tr("glTF Binary Format (.glb)")); @@ -1296,7 +1309,7 @@ void SkeletonDocumentWindow::exportGlbResult() QApplication::restoreOverrideCursor(); } -void SkeletonDocumentWindow::updateXlockButtonState() +void DocumentWindow::updateXlockButtonState() { if (m_document->xlocked) m_xlockButton->setStyleSheet("QPushButton {color: #252525}"); @@ -1304,7 +1317,7 @@ void SkeletonDocumentWindow::updateXlockButtonState() m_xlockButton->setStyleSheet("QPushButton {color: #fc6621}"); } -void SkeletonDocumentWindow::updateYlockButtonState() +void DocumentWindow::updateYlockButtonState() { if (m_document->ylocked) m_ylockButton->setStyleSheet("QPushButton {color: #252525}"); @@ -1312,7 +1325,7 @@ void SkeletonDocumentWindow::updateYlockButtonState() m_ylockButton->setStyleSheet("QPushButton {color: #2a5aac}"); } -void SkeletonDocumentWindow::updateZlockButtonState() +void DocumentWindow::updateZlockButtonState() { if (m_document->zlocked) m_zlockButton->setStyleSheet("QPushButton {color: #252525}"); @@ -1320,7 +1333,7 @@ void SkeletonDocumentWindow::updateZlockButtonState() m_zlockButton->setStyleSheet("QPushButton {color: #aaebc4}"); } -void SkeletonDocumentWindow::updateRadiusLockButtonState() +void DocumentWindow::updateRadiusLockButtonState() { if (m_document->radiusLocked) m_radiusLockButton->setStyleSheet("QPushButton {color: #252525}"); @@ -1328,7 +1341,7 @@ void SkeletonDocumentWindow::updateRadiusLockButtonState() m_radiusLockButton->setStyleSheet("QPushButton {color: " + Theme::white.name() + "}"); } -void SkeletonDocumentWindow::updateRigWeightRenderWidget() +void DocumentWindow::updateRigWeightRenderWidget() { MeshLoader *resultRigWeightMesh = m_document->takeResultRigWeightMesh(); if (nullptr == resultRigWeightMesh) { @@ -1340,12 +1353,12 @@ void SkeletonDocumentWindow::updateRigWeightRenderWidget() } } -void SkeletonDocumentWindow::registerDialog(QWidget *widget) +void DocumentWindow::registerDialog(QWidget *widget) { m_dialogs.push_back(widget); } -void SkeletonDocumentWindow::unregisterDialog(QWidget *widget) +void DocumentWindow::unregisterDialog(QWidget *widget) { m_dialogs.erase(std::remove(m_dialogs.begin(), m_dialogs.end(), widget), m_dialogs.end()); } diff --git a/src/documentwindow.h b/src/documentwindow.h index b9310aae..8b0ddc42 100644 --- a/src/documentwindow.h +++ b/src/documentwindow.h @@ -17,15 +17,15 @@ class SkeletonGraphicsWidget; -class SkeletonDocumentWindow : public QMainWindow +class DocumentWindow : public QMainWindow { Q_OBJECT signals: void initialized(); public: - SkeletonDocumentWindow(); - ~SkeletonDocumentWindow(); - static SkeletonDocumentWindow *createDocumentWindow(); + DocumentWindow(); + ~DocumentWindow(); + static DocumentWindow *createDocumentWindow(); static void showAcknowlegements(); static void showContributors(); static void showAbout(); diff --git a/src/genericposer.cpp b/src/genericposer.cpp index 2942136f..7ed178c0 100644 --- a/src/genericposer.cpp +++ b/src/genericposer.cpp @@ -8,50 +8,7 @@ GenericPoser::GenericPoser(const std::vector &bones) : void GenericPoser::commit() { - for (const auto &item: parameters()) { - int boneIndex = findBoneIndex(item.first); - if (-1 == boneIndex) { - continue; - } - auto findPitchResult = item.second.find("pitch"); - auto findYawResult = item.second.find("yaw"); - auto findRollResult = item.second.find("roll"); - if (findPitchResult != item.second.end() || - findYawResult != item.second.end() || - findRollResult != item.second.end()) { - float yawAngle = valueOfKeyInMapOrEmpty(item.second, "yaw").toFloat(); - if (item.first.startsWith("Left")) { - yawAngle = -yawAngle; - } - QQuaternion rotation = eulerAnglesToQuaternion(valueOfKeyInMapOrEmpty(item.second, "pitch").toFloat(), - yawAngle, - valueOfKeyInMapOrEmpty(item.second, "roll").toFloat()); - m_jointNodeTree.updateRotation(boneIndex, rotation); - continue; - } - auto findIntersectionResult = item.second.find("intersection"); - if (findIntersectionResult != item.second.end()) { - float intersectionAngle = valueOfKeyInMapOrEmpty(item.second, "intersection").toFloat(); - const RiggerBone &bone = bones()[boneIndex]; - QVector3D axis = bone.baseNormal; - QQuaternion rotation = QQuaternion::fromAxisAndAngle(axis, intersectionAngle); - m_jointNodeTree.updateRotation(boneIndex, rotation); - continue; - } - auto findXResult = item.second.find("x"); - auto findYResult = item.second.find("y"); - auto findZResult = item.second.find("z"); - if (findXResult != item.second.end() || - findYResult != item.second.end() || - findZResult != item.second.end()) { - float x = valueOfKeyInMapOrEmpty(item.second, "x").toFloat(); - float y = valueOfKeyInMapOrEmpty(item.second, "y").toFloat(); - float z = valueOfKeyInMapOrEmpty(item.second, "z").toFloat(); - QVector3D translation = {x, y, z}; - m_jointNodeTree.addTranslation(boneIndex, translation); - continue; - } - } + // TODO: Poser::commit(); } diff --git a/src/genericrigger.cpp b/src/genericrigger.cpp index 4bff8e80..e1a54d27 100644 --- a/src/genericrigger.cpp +++ b/src/genericrigger.cpp @@ -302,7 +302,7 @@ bool GenericRigger::rig() m_resultBones.push_back(RiggerBone()); RiggerBone &bodyBone = m_resultBones.back(); bodyBone.index = m_resultBones.size() - 1; - bodyBone.name = "Body"; + bodyBone.name = Rigger::rootBoneName; bodyBone.headPosition = QVector3D(0, 0, 0); bodyBone.hasButton = true; bodyBone.button = {spineNodes.size(), 0}; @@ -378,8 +378,8 @@ bool GenericRigger::rig() //qDebug() << spineBone.name << "head:" << spineBone.headPosition << "tail:" << spineBone.tailPosition; if (1 == spineGenerateOrder) { - m_resultBones[boneIndexMap["Body"]].tailPosition = spineBone.headPosition; - m_resultBones[boneIndexMap["Body"]].children.push_back(spineBone.index); + m_resultBones[boneIndexMap[Rigger::rootBoneName]].tailPosition = spineBone.headPosition; + m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(spineBone.index); } else { m_resultBones[boneIndexMap["Spine" + QString::number(spineGenerateOrder - 1)]].tailPosition = spineBone.headPosition; m_resultBones[boneIndexMap["Spine" + QString::number(spineGenerateOrder - 1)]].children.push_back(spineBone.index); @@ -396,7 +396,7 @@ bool GenericRigger::rig() ribBone.headPosition = spineBoneHeadPosition; boneIndexMap[ribBone.name] = ribBone.index; if (1 == spineGenerateOrder) { - m_resultBones[boneIndexMap["Body"]].children.push_back(ribBone.index); + m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(ribBone.index); } else { m_resultBones[boneIndexMap["Spine" + QString::number(spineGenerateOrder)]].children.push_back(ribBone.index); } diff --git a/src/main.cpp b/src/main.cpp index a3172576..4dc275c7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -46,7 +46,7 @@ int main(int argc, char ** argv) font.setBold(false); QApplication::setFont(font); - SkeletonDocumentWindow::createDocumentWindow(); + DocumentWindow::createDocumentWindow(); return app.exec(); } diff --git a/src/motionmanagewidget.cpp b/src/motionmanagewidget.cpp index cff342ec..3627f411 100644 --- a/src/motionmanagewidget.cpp +++ b/src/motionmanagewidget.cpp @@ -22,14 +22,21 @@ MotionManageWidget::MotionManageWidget(const Document *document, QWidget *parent connect(m_motionListWidget, &MotionListWidget::modifyMotion, this, &MotionManageWidget::showMotionDialog); InfoLabel *infoLabel = new InfoLabel; - infoLabel->setText(tr("Missing Rig")); infoLabel->show(); auto refreshInfoLabel = [=]() { if (m_document->currentRigSucceed()) { - infoLabel->hide(); - addMotionButton->show(); + if (m_document->rigType == RigType::Tetrapod) { + infoLabel->setText(""); + infoLabel->hide(); + addMotionButton->show(); + } else { + infoLabel->setText(tr("Motion editor doesn't support this rig type yet: ") + RigTypeToDispName(m_document->rigType)); + infoLabel->show(); + addMotionButton->hide(); + } } else { + infoLabel->setText(tr("Missing Rig")); infoLabel->show(); addMotionButton->hide(); } diff --git a/src/posedocument.cpp b/src/posedocument.cpp new file mode 100644 index 00000000..f3bc2ee5 --- /dev/null +++ b/src/posedocument.cpp @@ -0,0 +1,401 @@ +#include +#include "posedocument.h" +#include "rigger.h" +#include "util.h" + +constexpr float PoseDocument::m_nodeRadius = 0.01; +constexpr float PoseDocument::m_groundPlaneHalfThickness = m_nodeRadius / 4; + +bool PoseDocument::hasPastableNodesInClipboard() const +{ + return false; +} + +bool PoseDocument::originSettled() const +{ + return false; +} + +bool PoseDocument::isNodeEditable(QUuid nodeId) const +{ + return true; +} + +bool PoseDocument::isEdgeEditable(QUuid edgeId) const +{ + return true; +} + +void PoseDocument::copyNodes(std::set nodeIdSet) const +{ + // TODO: +} + +void PoseDocument::saveHistoryItem() +{ + PoseHistoryItem item; + toParameters(item.parameters); + m_undoItems.push_back(item); +} + +bool PoseDocument::undoable() const +{ + return m_undoItems.size() >= 2; +} + +bool PoseDocument::redoable() const +{ + return !m_redoItems.empty(); +} + +void PoseDocument::undo() +{ + if (!undoable()) + return; + m_redoItems.push_back(m_undoItems.back()); + m_undoItems.pop_back(); + const auto &item = m_undoItems.back(); + fromParameters(&m_riggerBones, item.parameters); +} + +void PoseDocument::redo() +{ + if (m_redoItems.empty()) + return; + m_undoItems.push_back(m_redoItems.back()); + const auto &item = m_redoItems.back(); + fromParameters(&m_riggerBones, item.parameters); + m_redoItems.pop_back(); +} + +void PoseDocument::paste() +{ +} + +void PoseDocument::updateTurnaround(const QImage &image) +{ + turnaround = image; + emit turnaroundChanged(); +} + +void PoseDocument::reset() +{ + nodeMap.clear(); + edgeMap.clear(); + partMap.clear(); + m_boneNameToIdsMap.clear(); + m_bonesPartId = QUuid(); + m_groundPartId = QUuid(); + m_groundEdgeId = QUuid(); + emit cleanup(); + emit parametersChanged(); +} + +void PoseDocument::fromParameters(const std::vector *rigBones, + const std::map> ¶meters) +{ + if (nullptr == rigBones || rigBones->empty()) { + m_riggerBones.clear(); + return; + } + + if (&m_riggerBones != rigBones) + m_riggerBones = *rigBones; + + QVector3D rootTranslation; + std::vector bones = *rigBones; + for (auto &bone: bones) { + const auto findParameterResult = parameters.find(bone.name); + if (findParameterResult == parameters.end()) + continue; + const auto &map = findParameterResult->second; + { + auto findXResult = map.find("fromX"); + auto findYResult = map.find("fromY"); + auto findZResult = map.find("fromZ"); + if (findXResult != map.end() || + findYResult != map.end() || + findZResult != map.end()) { + bone.headPosition = { + valueOfKeyInMapOrEmpty(map, "fromX").toFloat(), + valueOfKeyInMapOrEmpty(map, "fromY").toFloat(), + valueOfKeyInMapOrEmpty(map, "fromZ").toFloat() + }; + } + } + { + auto findXResult = map.find("toX"); + auto findYResult = map.find("toY"); + auto findZResult = map.find("toZ"); + if (findXResult != map.end() || + findYResult != map.end() || + findZResult != map.end()) { + bone.tailPosition = { + valueOfKeyInMapOrEmpty(map, "toX").toFloat(), + valueOfKeyInMapOrEmpty(map, "toY").toFloat(), + valueOfKeyInMapOrEmpty(map, "toZ").toFloat() + }; + } + } + } + + const auto findRoot = parameters.find(Rigger::rootBoneName); + if (findRoot != parameters.end()) { + const auto &map = findRoot->second; + { + auto findXResult = map.find("translateX"); + auto findYResult = map.find("translateY"); + auto findZResult = map.find("translateZ"); + if (findXResult != map.end() || + findYResult != map.end() || + findZResult != map.end()) { + rootTranslation = { + valueOfKeyInMapOrEmpty(map, "translateX").toFloat(), + valueOfKeyInMapOrEmpty(map, "translateY").toFloat(), + valueOfKeyInMapOrEmpty(map, "translateZ").toFloat() + }; + } + } + } + + updateRigBones(&bones, rootTranslation); +} + +void PoseDocument::updateRigBones(const std::vector *rigBones, const QVector3D &rootTranslation) +{ + reset(); + + if (nullptr == rigBones || rigBones->empty()) { + return; + } + + std::set newAddedNodeIds; + std::set newAddedEdgeIds; + + m_bonesPartId = QUuid::createUuid(); + auto &bonesPart = partMap[m_bonesPartId]; + bonesPart.id = m_bonesPartId; + + std::vector> edgePairs; + for (size_t i = 1; i < rigBones->size(); ++i) { + const auto &bone = (*rigBones)[i]; + for (const auto &child: bone.children) { + edgePairs.push_back({i, child}); + } + } + std::map boneIndexToHeadNodeIdMap; + for (const auto &edgePair: edgePairs) { + QUuid firstNodeId, secondNodeId; + auto findFirst = boneIndexToHeadNodeIdMap.find(edgePair.first); + if (findFirst == boneIndexToHeadNodeIdMap.end()) { + const auto &bone = (*rigBones)[edgePair.first]; + SkeletonNode node; + node.partId = m_bonesPartId; + node.id = QUuid::createUuid(); + node.setRadius(m_nodeRadius); + node.x = bone.headPosition.x() + 0.5; + node.y = -bone.headPosition.y() + 0.5; + node.z = -bone.headPosition.z() + 1; + nodeMap[node.id] = node; + newAddedNodeIds.insert(node.id); + boneIndexToHeadNodeIdMap[edgePair.first] = node.id; + firstNodeId = node.id; + } else { + firstNodeId = findFirst->second; + } + auto findSecond = boneIndexToHeadNodeIdMap.find(edgePair.second); + if (findSecond == boneIndexToHeadNodeIdMap.end()) { + const auto &bone = (*rigBones)[edgePair.second]; + SkeletonNode node; + node.partId = m_bonesPartId; + node.id = QUuid::createUuid(); + node.setRadius(m_nodeRadius); + node.x = bone.headPosition.x() + 0.5; + node.y = -bone.headPosition.y() + 0.5; + node.z = -bone.headPosition.z() + 1; + nodeMap[node.id] = node; + newAddedNodeIds.insert(node.id); + boneIndexToHeadNodeIdMap[edgePair.second] = node.id; + secondNodeId = node.id; + } else { + secondNodeId = findSecond->second; + } + + SkeletonEdge edge; + edge.partId = m_bonesPartId; + edge.id = QUuid::createUuid(); + edge.nodeIds.push_back(firstNodeId); + edge.nodeIds.push_back(secondNodeId); + edgeMap[edge.id] = edge; + newAddedEdgeIds.insert(edge.id); + nodeMap[firstNodeId].edgeIds.push_back(edge.id); + nodeMap[secondNodeId].edgeIds.push_back(edge.id); + } + for (size_t i = 0; i < rigBones->size(); ++i) { + const auto &bone = (*rigBones)[i]; + if (bone.children.empty()) { + const QUuid &firstNodeId = boneIndexToHeadNodeIdMap[i]; + + SkeletonNode node; + node.partId = m_bonesPartId; + node.id = QUuid::createUuid(); + node.setRadius(m_nodeRadius / 2); + node.x = bone.tailPosition.x() + 0.5; + node.y = -bone.tailPosition.y() + 0.5; + node.z = -bone.tailPosition.z() + 1; + nodeMap[node.id] = node; + newAddedNodeIds.insert(node.id); + m_boneNameToIdsMap[bone.name] = {firstNodeId, node.id}; + + SkeletonEdge edge; + edge.partId = m_bonesPartId; + edge.id = QUuid::createUuid(); + edge.nodeIds.push_back(firstNodeId); + edge.nodeIds.push_back(node.id); + edgeMap[edge.id] = edge; + newAddedEdgeIds.insert(edge.id); + nodeMap[firstNodeId].edgeIds.push_back(edge.id); + nodeMap[node.id].edgeIds.push_back(edge.id); + continue; + } + for (const auto &child: bone.children) { + m_boneNameToIdsMap[bone.name] = {boneIndexToHeadNodeIdMap[i], boneIndexToHeadNodeIdMap[child]}; + } + } + + m_groundPartId = QUuid::createUuid(); + auto &groundPart = partMap[m_groundPartId]; + groundPart.id = m_groundPartId; + + float groundY = findGroundY() + rootTranslation.y(); + + std::pair groundNodesPair; + { + SkeletonNode node; + node.partId = m_groundPartId; + node.id = QUuid::createUuid(); + node.setRadius(m_groundPlaneHalfThickness); + node.x = -100; + node.y = groundY + m_groundPlaneHalfThickness; + node.z = -100; + nodeMap[node.id] = node; + newAddedNodeIds.insert(node.id); + groundNodesPair.first = node.id; + } + + { + SkeletonNode node; + node.partId = m_groundPartId; + node.id = QUuid::createUuid(); + node.setRadius(m_groundPlaneHalfThickness); + node.x = 100; + node.y = groundY + m_groundPlaneHalfThickness; + node.z = 100; + nodeMap[node.id] = node; + newAddedNodeIds.insert(node.id); + groundNodesPair.second = node.id; + } + + { + SkeletonEdge edge; + edge.partId = m_groundPartId; + edge.id = QUuid::createUuid(); + edge.nodeIds.push_back(groundNodesPair.first); + edge.nodeIds.push_back(groundNodesPair.second); + edgeMap[edge.id] = edge; + m_groundEdgeId = edge.id; + newAddedEdgeIds.insert(edge.id); + nodeMap[groundNodesPair.first].edgeIds.push_back(edge.id); + nodeMap[groundNodesPair.second].edgeIds.push_back(edge.id); + } + + for (const auto &nodeIt: newAddedNodeIds) { + emit nodeAdded(nodeIt); + } + for (const auto &edgeIt: newAddedEdgeIds) { + emit edgeAdded(edgeIt); + } + + emit parametersChanged(); +} + +void PoseDocument::moveNodeBy(QUuid nodeId, float x, float y, float z) +{ + auto it = nodeMap.find(nodeId); + if (it == nodeMap.end()) { + qDebug() << "Find node failed:" << nodeId; + return; + } + it->second.x += x; + it->second.y += y; + it->second.z += z; + emit nodeOriginChanged(it->first); + emit parametersChanged(); +} + +void PoseDocument::setNodeOrigin(QUuid nodeId, float x, float y, float z) +{ + auto it = nodeMap.find(nodeId); + if (it == nodeMap.end()) { + qDebug() << "Find node failed:" << nodeId; + return; + } + it->second.x = x; + it->second.y = y; + it->second.z = z; + auto part = partMap.find(it->second.partId); + if (part != partMap.end()) + part->second.dirty = true; + emit nodeOriginChanged(nodeId); + emit parametersChanged(); +} + +float PoseDocument::findGroundY() const +{ + auto maxY = std::numeric_limits::lowest(); + for (const auto &nodeIt: nodeMap) { + if (nodeIt.second.partId != m_bonesPartId) + continue; + auto y = nodeIt.second.y + nodeIt.second.radius; + if (y > maxY) + maxY = y; + } + return maxY; +} + +void PoseDocument::toParameters(std::map> ¶meters) const +{ + float translateY = 0; + auto findGroundEdge = edgeMap.find(m_groundEdgeId); + if (findGroundEdge != edgeMap.end()) { + const auto &nodeIds = findGroundEdge->second.nodeIds; + if (nodeIds.size() == 2) { + auto findFirstNode = nodeMap.find(nodeIds[0]); + auto findSecondNode = nodeMap.find(nodeIds[1]); + if (findFirstNode != nodeMap.end() && findSecondNode != nodeMap.end()) { + translateY = (findFirstNode->second.y + findSecondNode->second.y) / 2 - + (findGroundY() + m_groundPlaneHalfThickness); + } + } + } + if (!qFuzzyIsNull(translateY)) { + auto &boneParameter = parameters[Rigger::rootBoneName]; + boneParameter["translateY"] = QString::number(translateY); + } + for (const auto &item: m_boneNameToIdsMap) { + auto &boneParameter = parameters[item.first]; + const auto &boneNodeIdPair = item.second; + auto findFirstNode = nodeMap.find(boneNodeIdPair.first); + if (findFirstNode == nodeMap.end()) + continue; + auto findSecondNode = nodeMap.find(boneNodeIdPair.second); + if (findSecondNode == nodeMap.end()) + continue; + boneParameter["fromX"] = QString::number(findFirstNode->second.x - 0.5); + boneParameter["fromY"] = QString::number(0.5 - findFirstNode->second.y); + boneParameter["fromZ"] = QString::number(1.0 - findFirstNode->second.z); + boneParameter["toX"] = QString::number(findSecondNode->second.x - 0.5); + boneParameter["toY"] = QString::number(0.5 - findSecondNode->second.y); + boneParameter["toZ"] = QString::number(1.0 - findSecondNode->second.z); + } +} diff --git a/src/posedocument.h b/src/posedocument.h new file mode 100644 index 00000000..0910a96c --- /dev/null +++ b/src/posedocument.h @@ -0,0 +1,66 @@ +#ifndef DUST3D_POSE_DOCUMENT_H +#define DUST3D_POSE_DOCUMENT_H +#include +#include +#include +#include "skeletondocument.h" +#include "rigger.h" + +struct PoseHistoryItem +{ + std::map> parameters; +}; + +class PoseDocument : public SkeletonDocument +{ + Q_OBJECT +signals: + void turnaroundChanged(); + void cleanup(); + void nodeAdded(QUuid nodeId); + void edgeAdded(QUuid edgeId); + void nodeOriginChanged(QUuid nodeId); + void parametersChanged(); + +public: + bool undoable() const override; + bool redoable() const override; + bool hasPastableNodesInClipboard() const override; + bool originSettled() const override; + bool isNodeEditable(QUuid nodeId) const override; + bool isEdgeEditable(QUuid edgeId) const override; + void copyNodes(std::set nodeIdSet) const override; + + void updateTurnaround(const QImage &image); + void updateRigBones(const std::vector *rigBones, const QVector3D &rootTranslation=QVector3D(0, 0, 0)); + void reset(); + + void toParameters(std::map> ¶meters) const; + void fromParameters(const std::vector *rigBones, + const std::map> ¶meters); + +public slots: + void saveHistoryItem(); + void undo() override; + void redo() override; + void paste() override; + + void moveNodeBy(QUuid nodeId, float x, float y, float z); + void setNodeOrigin(QUuid nodeId, float x, float y, float z); + float findGroundY() const; + +public: + static const float m_nodeRadius; + static const float m_groundPlaneHalfThickness; + +private: + std::map> m_boneNameToIdsMap; + QUuid m_groundPartId; + QUuid m_bonesPartId; + QUuid m_groundEdgeId; + std::deque m_undoItems; + std::deque m_redoItems; + std::vector m_riggerBones; +}; + +#endif diff --git a/src/poseeditwidget.cpp b/src/poseeditwidget.cpp index 1b8ac0be..029389af 100644 --- a/src/poseeditwidget.cpp +++ b/src/poseeditwidget.cpp @@ -6,15 +6,21 @@ #include #include #include +#include #include "theme.h" #include "poseeditwidget.h" #include "floatnumberwidget.h" #include "version.h" #include "poserconstruct.h" +#include "graphicscontainerwidget.h" +#include "documentwindow.h" +#include "shortcuts.h" +#include "imageforever.h" PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) : QDialog(parent), - m_document(document) + m_document(document), + m_poseDocument(new PoseDocument) { m_posePreviewManager = new PosePreviewManager(); connect(m_posePreviewManager, &PosePreviewManager::renderDone, [=]() { @@ -29,15 +35,53 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) : m_previewWidget->updateMesh(m_posePreviewManager->takeResultPreviewMesh()); }); - m_previewWidget = new ModelWidget(this); - m_previewWidget->setMinimumSize(128, 128); - m_previewWidget->resize(384, 384); - m_previewWidget->move(-64, -64+22); + SkeletonGraphicsWidget *graphicsWidget = new SkeletonGraphicsWidget(m_poseDocument); + graphicsWidget->setNodePositionModifyOnly(true); + m_poseGraphicsWidget = graphicsWidget; + + initShortCuts(this, graphicsWidget); + + GraphicsContainerWidget *containerWidget = new GraphicsContainerWidget; + containerWidget->setGraphicsWidget(graphicsWidget); + QGridLayout *containerLayout = new QGridLayout; + containerLayout->setSpacing(0); + containerLayout->setContentsMargins(1, 0, 0, 0); + containerLayout->addWidget(graphicsWidget); + containerWidget->setLayout(containerLayout); + containerWidget->setMinimumSize(400, 400); + + m_previewWidget = new ModelWidget(containerWidget); + m_previewWidget->setAttribute(Qt::WA_TransparentForMouseEvents); + m_previewWidget->setMinimumSize(DocumentWindow::m_modelRenderWidgetInitialSize, DocumentWindow::m_modelRenderWidgetInitialSize); + m_previewWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + m_previewWidget->move(DocumentWindow::m_modelRenderWidgetInitialX, DocumentWindow::m_modelRenderWidgetInitialY); + + m_poseGraphicsWidget->setModelWidget(m_previewWidget); + containerWidget->setModelWidget(m_previewWidget); + + connect(containerWidget, &GraphicsContainerWidget::containerSizeChanged, + graphicsWidget, &SkeletonGraphicsWidget::canvasResized); + + connect(graphicsWidget, &SkeletonGraphicsWidget::moveNodeBy, m_poseDocument, &PoseDocument::moveNodeBy); + connect(graphicsWidget, &SkeletonGraphicsWidget::setNodeOrigin, m_poseDocument, &PoseDocument::setNodeOrigin); + connect(graphicsWidget, &SkeletonGraphicsWidget::groupOperationAdded, m_poseDocument, &PoseDocument::saveHistoryItem); + connect(graphicsWidget, &SkeletonGraphicsWidget::undo, m_poseDocument, &PoseDocument::undo); + connect(graphicsWidget, &SkeletonGraphicsWidget::redo, m_poseDocument, &PoseDocument::redo); + + connect(m_poseDocument, &PoseDocument::cleanup, graphicsWidget, &SkeletonGraphicsWidget::removeAllContent); + + connect(m_poseDocument, &PoseDocument::nodeAdded, graphicsWidget, &SkeletonGraphicsWidget::nodeAdded); + connect(m_poseDocument, &PoseDocument::edgeAdded, graphicsWidget, &SkeletonGraphicsWidget::edgeAdded); + connect(m_poseDocument, &PoseDocument::nodeOriginChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeOriginChanged); + + connect(m_poseDocument, &PoseDocument::parametersChanged, this, [&]() { + m_parameters.clear(); + m_poseDocument->toParameters(m_parameters); + emit parametersAdjusted(); + }); QHBoxLayout *paramtersLayout = new QHBoxLayout; - paramtersLayout->setContentsMargins(0, 480, 0, 0); - paramtersLayout->addStretch(); - paramtersLayout->addSpacing(20); + paramtersLayout->addWidget(containerWidget); m_nameEdit = new QLineEdit; m_nameEdit->setFixedWidth(200); @@ -49,9 +93,17 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) : connect(saveButton, &QPushButton::clicked, this, &PoseEditWidget::save); saveButton->setDefault(true); + QPushButton *changeReferenceSheet = new QPushButton(tr("Change Reference Sheet...")); + connect(changeReferenceSheet, &QPushButton::clicked, this, &PoseEditWidget::changeTurnaround); + connect(m_poseDocument, &PoseDocument::turnaroundChanged, + graphicsWidget, &SkeletonGraphicsWidget::turnaroundChanged); + + connect(m_document, &Document::resultRigChanged, this, &PoseEditWidget::updatePoseDocument); + QHBoxLayout *baseInfoLayout = new QHBoxLayout; baseInfoLayout->addWidget(new QLabel(tr("Name"))); baseInfoLayout->addWidget(m_nameEdit); + baseInfoLayout->addWidget(changeReferenceSheet); baseInfoLayout->addStretch(); baseInfoLayout->addWidget(saveButton); @@ -62,7 +114,7 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) : setLayout(mainLayout); - connect(m_document, &Document::resultRigChanged, this, &PoseEditWidget::updatePreview); + connect(m_document, &Document::resultRigChanged, this, &PoseEditWidget::updatePoseDocument); connect(this, &PoseEditWidget::parametersAdjusted, this, &PoseEditWidget::updatePreview); connect(this, &PoseEditWidget::parametersAdjusted, [=]() { m_unsaved = true; @@ -71,55 +123,43 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) : connect(this, &PoseEditWidget::addPose, m_document, &Document::addPose); connect(this, &PoseEditWidget::renamePose, m_document, &Document::renamePose); connect(this, &PoseEditWidget::setPoseParameters, m_document, &Document::setPoseParameters); + connect(this, &PoseEditWidget::setPoseAttributes, m_document, &Document::setPoseAttributes); - updateButtons(); - updatePreview(); + updatePoseDocument(); updateTitle(); + m_poseDocument->saveHistoryItem(); } -void PoseEditWidget::updateButtons() +void PoseEditWidget::changeTurnaround() { - delete m_buttonsContainer; - m_buttonsContainer = new QWidget(this); - m_buttonsContainer->resize(600, 500); - m_buttonsContainer->move(256, 0); - m_buttonsContainer->show(); - - QGridLayout *marksContainerLayout = new QGridLayout; - marksContainerLayout->setContentsMargins(0, 0, 0, 0); - marksContainerLayout->setSpacing(2); - - QFont buttonFont; - buttonFont.setWeight(QFont::Light); - buttonFont.setPixelSize(7); - buttonFont.setBold(false); - - std::map> buttons; - const std::vector *rigBones = m_document->resultRigBones(); - if (nullptr != rigBones && !rigBones->empty()) { - for (const auto &bone: *rigBones) { - if (!bone.hasButton) - continue; - QPushButton *buttonWidget = new QPushButton(bone.name); - PopupWidgetType widgetType = bone.buttonParameterType; - buttonWidget->setFont(buttonFont); - buttonWidget->setMaximumWidth(100); - QString boneName = bone.name; - connect(buttonWidget, &QPushButton::clicked, [this, boneName, widgetType]() { - emit showPopupAngleDialog(boneName, widgetType, mapFromGlobal(QCursor::pos())); - }); - marksContainerLayout->addWidget(buttonWidget, bone.button.first, bone.button.second); - } - } - - marksContainerLayout->setSizeConstraint(QLayout::SizeConstraint::SetMinimumSize); - - QVBoxLayout *mainLayouer = new QVBoxLayout; - mainLayouer->addStretch(); - mainLayouer->addLayout(marksContainerLayout); - mainLayouer->addStretch(); - - m_buttonsContainer->setLayout(mainLayouer); + QString fileName = QFileDialog::getOpenFileName(this, QString(), QString(), + tr("Image Files (*.png *.jpg *.bmp)")).trimmed(); + if (fileName.isEmpty()) + return; + QImage image; + if (!image.load(fileName)) + return; + m_imageId = ImageForever::add(&image); + m_attributes["canvasImageId"] = m_imageId.toString(); + m_poseDocument->updateTurnaround(image); +} + +QUuid PoseEditWidget::findImageIdFromAttributes(const std::map &attributes) +{ + auto findImageIdResult = attributes.find("canvasImageId"); + if (findImageIdResult == attributes.end()) + return QUuid(); + return QUuid(findImageIdResult->second); +} + +void PoseEditWidget::updatePoseDocument() +{ + m_poseDocument->fromParameters(m_document->resultRigBones(), m_parameters); + QUuid imageId = findImageIdFromAttributes(m_attributes); + auto image = ImageForever::get(imageId); + if (nullptr != image) + m_poseDocument->updateTurnaround(*image); + updatePreview(); } void PoseEditWidget::reject() @@ -141,10 +181,6 @@ void PoseEditWidget::closeEvent(QCloseEvent *event) } m_closed = true; hide(); - if (m_openedMenuCount > 0) { - event->ignore(); - return; - } if (m_posePreviewManager->isRendering()) { event->ignore(); return; @@ -160,6 +196,7 @@ QSize PoseEditWidget::sizeHint() const PoseEditWidget::~PoseEditWidget() { delete m_posePreviewManager; + delete m_poseDocument; } void PoseEditWidget::updatePreview() @@ -214,157 +251,6 @@ void PoseEditWidget::updateTitle() setWindowTitle(unifiedWindowTitle(pose->name + (m_unsaved ? "*" : ""))); } -void PoseEditWidget::showPopupAngleDialog(QString boneName, PopupWidgetType popupWidgetType, QPoint pos) -{ - QMenu popupMenu; - - QWidget *popup = new QWidget; - - QVBoxLayout *layout = new QVBoxLayout; - - if (PopupWidgetType::PitchYawRoll == popupWidgetType) { - FloatNumberWidget *pitchWidget = new FloatNumberWidget; - pitchWidget->setItemName(tr("Pitch")); - pitchWidget->setRange(-180, 180); - pitchWidget->setValue(valueOfKeyInMapOrEmpty(m_parameters[boneName], "pitch").toFloat()); - connect(pitchWidget, &FloatNumberWidget::valueChanged, this, [=](float value) { - m_parameters[boneName]["pitch"] = QString::number(value); - emit parametersAdjusted(); - }); - QPushButton *pitchEraser = new QPushButton(QChar(fa::eraser)); - Theme::initAwesomeMiniButton(pitchEraser); - connect(pitchEraser, &QPushButton::clicked, this, [=]() { - pitchWidget->setValue(0.0); - }); - QHBoxLayout *pitchLayout = new QHBoxLayout; - pitchLayout->addWidget(pitchEraser); - pitchLayout->addWidget(pitchWidget); - layout->addLayout(pitchLayout); - - FloatNumberWidget *yawWidget = new FloatNumberWidget; - yawWidget->setItemName(tr("Yaw")); - yawWidget->setRange(-180, 180); - yawWidget->setValue(valueOfKeyInMapOrEmpty(m_parameters[boneName], "yaw").toFloat()); - connect(yawWidget, &FloatNumberWidget::valueChanged, this, [=](float value) { - m_parameters[boneName]["yaw"] = QString::number(value); - emit parametersAdjusted(); - }); - QPushButton *yawEraser = new QPushButton(QChar(fa::eraser)); - Theme::initAwesomeMiniButton(yawEraser); - connect(yawEraser, &QPushButton::clicked, this, [=]() { - yawWidget->setValue(0.0); - }); - QHBoxLayout *yawLayout = new QHBoxLayout; - yawLayout->addWidget(yawEraser); - yawLayout->addWidget(yawWidget); - layout->addLayout(yawLayout); - - FloatNumberWidget *rollWidget = new FloatNumberWidget; - rollWidget->setItemName(tr("Roll")); - rollWidget->setRange(-180, 180); - rollWidget->setValue(valueOfKeyInMapOrEmpty(m_parameters[boneName], "roll").toFloat()); - connect(rollWidget, &FloatNumberWidget::valueChanged, this, [=](float value) { - m_parameters[boneName]["roll"] = QString::number(value); - emit parametersAdjusted(); - }); - QPushButton *rollEraser = new QPushButton(QChar(fa::eraser)); - Theme::initAwesomeMiniButton(rollEraser); - connect(rollEraser, &QPushButton::clicked, this, [=]() { - rollWidget->setValue(0.0); - }); - QHBoxLayout *rollLayout = new QHBoxLayout; - rollLayout->addWidget(rollEraser); - rollLayout->addWidget(rollWidget); - layout->addLayout(rollLayout); - } else if (PopupWidgetType::Intersection == popupWidgetType) { - FloatNumberWidget *intersectionWidget = new FloatNumberWidget; - intersectionWidget->setItemName(tr("Intersection")); - intersectionWidget->setRange(-180, 180); - intersectionWidget->setValue(valueOfKeyInMapOrEmpty(m_parameters[boneName], "intersection").toFloat()); - connect(intersectionWidget, &FloatNumberWidget::valueChanged, this, [=](float value) { - m_parameters[boneName]["intersection"] = QString::number(value); - emit parametersAdjusted(); - }); - QPushButton *intersectionEraser = new QPushButton(QChar(fa::eraser)); - Theme::initAwesomeMiniButton(intersectionEraser); - connect(intersectionEraser, &QPushButton::clicked, this, [=]() { - intersectionWidget->setValue(0.0); - }); - QHBoxLayout *intersectionLayout = new QHBoxLayout; - intersectionLayout->addWidget(intersectionEraser); - intersectionLayout->addWidget(intersectionWidget); - layout->addLayout(intersectionLayout); - } else if (PopupWidgetType::Translation == popupWidgetType) { - FloatNumberWidget *xWidget = new FloatNumberWidget; - xWidget->setItemName(tr("X")); - xWidget->setRange(-1, 1); - xWidget->setValue(valueOfKeyInMapOrEmpty(m_parameters[boneName], "x").toFloat()); - connect(xWidget, &FloatNumberWidget::valueChanged, this, [=](float value) { - m_parameters[boneName]["x"] = QString::number(value); - emit parametersAdjusted(); - }); - QPushButton *xEraser = new QPushButton(QChar(fa::eraser)); - Theme::initAwesomeMiniButton(xEraser); - connect(xEraser, &QPushButton::clicked, this, [=]() { - xWidget->setValue(0.0); - }); - QHBoxLayout *xLayout = new QHBoxLayout; - xLayout->addWidget(xEraser); - xLayout->addWidget(xWidget); - layout->addLayout(xLayout); - - FloatNumberWidget *yWidget = new FloatNumberWidget; - yWidget->setItemName(tr("Y")); - yWidget->setRange(-1, 1); - yWidget->setValue(valueOfKeyInMapOrEmpty(m_parameters[boneName], "y").toFloat()); - connect(yWidget, &FloatNumberWidget::valueChanged, this, [=](float value) { - m_parameters[boneName]["y"] = QString::number(value); - emit parametersAdjusted(); - }); - QPushButton *yEraser = new QPushButton(QChar(fa::eraser)); - Theme::initAwesomeMiniButton(yEraser); - connect(yEraser, &QPushButton::clicked, this, [=]() { - yWidget->setValue(0.0); - }); - QHBoxLayout *yLayout = new QHBoxLayout; - yLayout->addWidget(yEraser); - yLayout->addWidget(yWidget); - layout->addLayout(yLayout); - - FloatNumberWidget *zWidget = new FloatNumberWidget; - zWidget->setItemName(tr("Z")); - zWidget->setRange(-1, 1); - zWidget->setValue(valueOfKeyInMapOrEmpty(m_parameters[boneName], "z").toFloat()); - connect(zWidget, &FloatNumberWidget::valueChanged, this, [=](float value) { - m_parameters[boneName]["z"] = QString::number(value); - emit parametersAdjusted(); - }); - QPushButton *zEraser = new QPushButton(QChar(fa::eraser)); - Theme::initAwesomeMiniButton(zEraser); - connect(zEraser, &QPushButton::clicked, this, [=]() { - zWidget->setValue(0.0); - }); - QHBoxLayout *zLayout = new QHBoxLayout; - zLayout->addWidget(zEraser); - zLayout->addWidget(zWidget); - layout->addLayout(zLayout); - } - - popup->setLayout(layout); - - QWidgetAction action(this); - action.setDefaultWidget(popup); - - popupMenu.addAction(&action); - - m_openedMenuCount++; - popupMenu.exec(mapToGlobal(pos)); - m_openedMenuCount--; - - if (m_closed) - close(); -} - void PoseEditWidget::setEditPoseName(QString name) { m_nameEdit->setText(name); @@ -374,6 +260,15 @@ void PoseEditWidget::setEditPoseName(QString name) void PoseEditWidget::setEditParameters(std::map> parameters) { m_parameters = parameters; + updatePoseDocument(); + updatePreview(); + m_poseDocument->saveHistoryItem(); +} + +void PoseEditWidget::setEditAttributes(std::map attributes) +{ + m_attributes = attributes; + updatePoseDocument(); updatePreview(); } @@ -390,6 +285,7 @@ void PoseEditWidget::save() } else if (m_unsaved) { emit renamePose(m_poseId, m_nameEdit->text()); emit setPoseParameters(m_poseId, m_parameters); + emit setPoseAttributes(m_poseId, m_attributes); } m_unsaved = false; close(); diff --git a/src/poseeditwidget.h b/src/poseeditwidget.h index 6e022f51..9b8af96c 100644 --- a/src/poseeditwidget.h +++ b/src/poseeditwidget.h @@ -8,6 +8,8 @@ #include "document.h" #include "modelwidget.h" #include "rigger.h" +#include "skeletongraphicswidget.h" +#include "posedocument.h" typedef RiggerButtonParameterType PopupWidgetType; @@ -18,21 +20,23 @@ signals: void addPose(QString name, std::map> parameters); void removePose(QUuid poseId); void setPoseParameters(QUuid poseId, std::map> parameters); + void setPoseAttributes(QUuid poseId, std::map attributes); void renamePose(QUuid poseId, QString name); void parametersAdjusted(); public: PoseEditWidget(const Document *document, QWidget *parent=nullptr); ~PoseEditWidget(); public slots: - void updateButtons(); + void updatePoseDocument(); void updatePreview(); - void showPopupAngleDialog(QString boneName, PopupWidgetType popupWidgetType, QPoint pos); void setEditPoseId(QUuid poseId); void setEditPoseName(QString name); void setEditParameters(std::map> parameters); + void setEditAttributes(std::map attributes); void updateTitle(); void save(); void clearUnsaveState(); + void changeTurnaround(); protected: QSize sizeHint() const override; void closeEvent(QCloseEvent *event) override; @@ -44,11 +48,14 @@ private: bool m_isPreviewDirty = false; bool m_closed = false; std::map> m_parameters; - size_t m_openedMenuCount = 0; + std::map m_attributes; QUuid m_poseId; bool m_unsaved = false; + QUuid m_imageId; QLineEdit *m_nameEdit = nullptr; - QWidget *m_buttonsContainer = nullptr; + PoseDocument *m_poseDocument = nullptr; + SkeletonGraphicsWidget *m_poseGraphicsWidget = nullptr; + QUuid findImageIdFromAttributes(const std::map &attributes); }; #endif diff --git a/src/posemanagewidget.cpp b/src/posemanagewidget.cpp index e03f0627..2e5d2f65 100644 --- a/src/posemanagewidget.cpp +++ b/src/posemanagewidget.cpp @@ -22,14 +22,21 @@ PoseManageWidget::PoseManageWidget(const Document *document, QWidget *parent) : connect(m_poseListWidget, &PoseListWidget::modifyPose, this, &PoseManageWidget::showPoseDialog); InfoLabel *infoLabel = new InfoLabel; - infoLabel->setText(tr("Missing Rig")); infoLabel->show(); auto refreshInfoLabel = [=]() { if (m_document->currentRigSucceed()) { - infoLabel->hide(); - addPoseButton->show(); + if (m_document->rigType == RigType::Tetrapod) { + infoLabel->setText(""); + infoLabel->hide(); + addPoseButton->show(); + } else { + infoLabel->setText(tr("Pose editor doesn't support this rig type yet: ") + RigTypeToDispName(m_document->rigType)); + infoLabel->show(); + addPoseButton->hide(); + } } else { + infoLabel->setText(tr("Missing Rig")); infoLabel->show(); addPoseButton->hide(); } @@ -71,6 +78,7 @@ void PoseManageWidget::showPoseDialog(QUuid poseId) poseEditWidget->setEditPoseId(poseId); poseEditWidget->setEditPoseName(pose->name); poseEditWidget->setEditParameters(pose->parameters); + poseEditWidget->setEditAttributes(pose->attributes); poseEditWidget->clearUnsaveState(); } } diff --git a/src/rigger.cpp b/src/rigger.cpp index 00b8bdb3..e25d7ccb 100644 --- a/src/rigger.cpp +++ b/src/rigger.cpp @@ -6,6 +6,7 @@ #include "rigger.h" size_t Rigger::m_maxCutOffSplitterExpandRound = 3; +QString Rigger::rootBoneName = "Body"; Rigger::Rigger(const std::vector &verticesPositions, const std::set &inputTriangles) : diff --git a/src/rigger.h b/src/rigger.h index 183b9258..771edaaa 100644 --- a/src/rigger.h +++ b/src/rigger.h @@ -125,6 +125,7 @@ public: const std::vector &missingMarkNames(); const std::vector &errorMarkNames(); virtual bool rig() = 0; + static QString rootBoneName; protected: virtual bool validate() = 0; virtual bool isCutOffSplitter(BoneMark boneMark) = 0; diff --git a/src/shortcuts.cpp b/src/shortcuts.cpp index 08407a2c..edc36ebe 100644 --- a/src/shortcuts.cpp +++ b/src/shortcuts.cpp @@ -3,13 +3,13 @@ #include "shortcuts.h" #define defineKey(keyVal, funcName) do { \ - auto key = new QShortcut(mainWindow); \ + auto key = new QShortcut(widget); \ key->setKey(keyVal); \ QObject::connect(key, &QShortcut::activated, \ graphicsWidget, funcName); \ } while (false) -void initShortCuts(QMainWindow *mainWindow, SkeletonGraphicsWidget *graphicsWidget) +void initShortCuts(QWidget *widget, SkeletonGraphicsWidget *graphicsWidget) { defineKey(Qt::Key_Delete, &SkeletonGraphicsWidget::shortcutDelete); defineKey(Qt::Key_Backspace, &SkeletonGraphicsWidget::shortcutDelete); diff --git a/src/shortcuts.h b/src/shortcuts.h index 9e48ce1c..426ae9c5 100644 --- a/src/shortcuts.h +++ b/src/shortcuts.h @@ -3,6 +3,6 @@ #include #include "skeletongraphicswidget.h" -void initShortCuts(QMainWindow *mainWindow, SkeletonGraphicsWidget *graphicsWidget); +void initShortCuts(QWidget *widget, SkeletonGraphicsWidget *graphicsWidget); #endif diff --git a/src/skeletondocument.cpp b/src/skeletondocument.cpp index 858d0c3e..e806aaf1 100644 --- a/src/skeletondocument.cpp +++ b/src/skeletondocument.cpp @@ -1,2 +1,73 @@ +#include #include "skeletondocument.h" +const SkeletonNode *SkeletonDocument::findNode(QUuid nodeId) const +{ + auto it = nodeMap.find(nodeId); + if (it == nodeMap.end()) + return nullptr; + return &it->second; +} + +const SkeletonEdge *SkeletonDocument::findEdge(QUuid edgeId) const +{ + auto it = edgeMap.find(edgeId); + if (it == edgeMap.end()) + return nullptr; + return &it->second; +} + +const SkeletonPart *SkeletonDocument::findPart(QUuid partId) const +{ + auto it = partMap.find(partId); + if (it == partMap.end()) + return nullptr; + return &it->second; +} + +const SkeletonEdge *SkeletonDocument::findEdgeByNodes(QUuid firstNodeId, QUuid secondNodeId) const +{ + const SkeletonNode *firstNode = nullptr; + firstNode = findNode(firstNodeId); + if (nullptr == firstNode) { + qDebug() << "Find node failed:" << firstNodeId; + return nullptr; + } + for (auto edgeIdIt = firstNode->edgeIds.begin(); edgeIdIt != firstNode->edgeIds.end(); edgeIdIt++) { + auto edgeIt = edgeMap.find(*edgeIdIt); + if (edgeIt == edgeMap.end()) { + qDebug() << "Find edge failed:" << *edgeIdIt; + continue; + } + if (std::find(edgeIt->second.nodeIds.begin(), edgeIt->second.nodeIds.end(), secondNodeId) != edgeIt->second.nodeIds.end()) + return &edgeIt->second; + } + return nullptr; +} + +void SkeletonDocument::findAllNeighbors(QUuid nodeId, std::set &neighbors) const +{ + const auto &node = findNode(nodeId); + if (nullptr == node) { + qDebug() << "findNode:" << nodeId << "failed"; + return; + } + for (const auto &edgeId: node->edgeIds) { + const auto &edge = findEdge(edgeId); + if (nullptr == edge) { + qDebug() << "findEdge:" << edgeId << "failed"; + continue; + } + const auto &neighborNodeId = edge->neighborOf(nodeId); + if (neighborNodeId.isNull()) { + qDebug() << "neighborOf:" << nodeId << "is null from edge:" << edgeId; + continue; + } + if (neighbors.find(neighborNodeId) != neighbors.end()) { + continue; + } + neighbors.insert(neighborNodeId); + findAllNeighbors(neighborNodeId, neighbors); + } +} + diff --git a/src/skeletondocument.h b/src/skeletondocument.h index 24c5e090..6050a81d 100644 --- a/src/skeletondocument.h +++ b/src/skeletondocument.h @@ -200,18 +200,22 @@ public: bool zlocked = false; bool radiusLocked = false; QImage turnaround; + std::map partMap; + std::map nodeMap; + std::map edgeMap; - virtual const SkeletonNode *findNode(QUuid nodeId) const = 0; - virtual const SkeletonEdge *findEdge(QUuid edgeId) const = 0; - virtual const SkeletonPart *findPart(QUuid partId) const = 0; - virtual const SkeletonEdge *findEdgeByNodes(QUuid firstNodeId, QUuid secondNodeId) const = 0; + const SkeletonNode *findNode(QUuid nodeId) const; + const SkeletonEdge *findEdge(QUuid edgeId) const; + const SkeletonPart *findPart(QUuid partId) const; + const SkeletonEdge *findEdgeByNodes(QUuid firstNodeId, QUuid secondNodeId) const; + void findAllNeighbors(QUuid nodeId, std::set &neighbors) const; + virtual bool undoable() const = 0; virtual bool redoable() const = 0; virtual bool hasPastableNodesInClipboard() const = 0; virtual bool originSettled() const = 0; virtual bool isNodeEditable(QUuid nodeId) const = 0; virtual bool isEdgeEditable(QUuid edgeId) const = 0; - virtual void findAllNeighbors(QUuid nodeId, std::set &neighbors) const = 0; virtual void copyNodes(std::set nodeIdSet) const = 0; public slots: diff --git a/src/skeletongraphicswidget.cpp b/src/skeletongraphicswidget.cpp index d49ae1ff..5eb9ec27 100644 --- a/src/skeletongraphicswidget.cpp +++ b/src/skeletongraphicswidget.cpp @@ -44,7 +44,8 @@ SkeletonGraphicsWidget::SkeletonGraphicsWidget(const SkeletonDocument *document) m_eventForwardingToModelWidget(false), m_modelWidget(nullptr), m_inTempDragMode(false), - m_modeBeforeEnterTempDragMode(SkeletonDocumentEditMode::Select) + m_modeBeforeEnterTempDragMode(SkeletonDocumentEditMode::Select), + m_nodePositionModifyOnly(false) { setRenderHint(QPainter::Antialiasing, false); setBackgroundBrush(QBrush(QWidget::palette().color(QWidget::backgroundRole()), Qt::SolidPattern)); @@ -118,10 +119,12 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos) QMenu contextMenu(this); QAction addAction(tr("Add..."), this); - connect(&addAction, &QAction::triggered, [=]() { - emit setEditMode(SkeletonDocumentEditMode::Add); - }); - contextMenu.addAction(&addAction); + if (!m_nodePositionModifyOnly) { + connect(&addAction, &QAction::triggered, [=]() { + emit setEditMode(SkeletonDocumentEditMode::Add); + }); + contextMenu.addAction(&addAction); + } QAction undoAction(tr("Undo"), this); if (m_document->undoable()) { @@ -136,67 +139,67 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos) } QAction deleteAction(tr("Delete"), this); - if (hasSelection()) { + if (!m_nodePositionModifyOnly && hasSelection()) { connect(&deleteAction, &QAction::triggered, this, &SkeletonGraphicsWidget::deleteSelected); contextMenu.addAction(&deleteAction); } QAction breakAction(tr("Break"), this); - if (hasEdgeSelection()) { + if (!m_nodePositionModifyOnly && hasEdgeSelection()) { connect(&breakAction, &QAction::triggered, this, &SkeletonGraphicsWidget::breakSelected); contextMenu.addAction(&breakAction); } QAction connectAction(tr("Connect"), this); - if (hasTwoDisconnectedNodesSelection()) { + if (!m_nodePositionModifyOnly && hasTwoDisconnectedNodesSelection()) { connect(&connectAction, &QAction::triggered, this, &SkeletonGraphicsWidget::connectSelected); contextMenu.addAction(&connectAction); } QAction cutAction(tr("Cut"), this); - if (hasSelection()) { + if (!m_nodePositionModifyOnly && hasSelection()) { connect(&cutAction, &QAction::triggered, this, &SkeletonGraphicsWidget::cut); contextMenu.addAction(&cutAction); } QAction copyAction(tr("Copy"), this); - if (hasSelection()) { + if (!m_nodePositionModifyOnly && hasSelection()) { connect(©Action, &QAction::triggered, this, &SkeletonGraphicsWidget::copy); contextMenu.addAction(©Action); } QAction pasteAction(tr("Paste"), this); - if (m_document->hasPastableNodesInClipboard()) { + if (!m_nodePositionModifyOnly && m_document->hasPastableNodesInClipboard()) { connect(&pasteAction, &QAction::triggered, m_document, &SkeletonDocument::paste); contextMenu.addAction(&pasteAction); } QAction flipHorizontallyAction(tr("H Flip"), this); - if (hasMultipleSelection()) { + if (!m_nodePositionModifyOnly && hasMultipleSelection()) { connect(&flipHorizontallyAction, &QAction::triggered, this, &SkeletonGraphicsWidget::flipHorizontally); contextMenu.addAction(&flipHorizontallyAction); } QAction flipVerticallyAction(tr("V Flip"), this); - if (hasMultipleSelection()) { + if (!m_nodePositionModifyOnly && hasMultipleSelection()) { connect(&flipVerticallyAction, &QAction::triggered, this, &SkeletonGraphicsWidget::flipVertically); contextMenu.addAction(&flipVerticallyAction); } QAction rotateClockwiseAction(tr("Rotate 90D CW"), this); - if (hasMultipleSelection()) { + if (!m_nodePositionModifyOnly && hasMultipleSelection()) { connect(&rotateClockwiseAction, &QAction::triggered, this, &SkeletonGraphicsWidget::rotateClockwise90Degree); contextMenu.addAction(&rotateClockwiseAction); } QAction rotateCounterclockwiseAction(tr("Rotate 90D CCW"), this); - if (hasMultipleSelection()) { + if (!m_nodePositionModifyOnly && hasMultipleSelection()) { connect(&rotateCounterclockwiseAction, &QAction::triggered, this, &SkeletonGraphicsWidget::rotateCounterclockwise90Degree); contextMenu.addAction(&rotateCounterclockwiseAction); } QAction switchXzAction(tr("Switch XZ"), this); - if (hasSelection()) { + if (!m_nodePositionModifyOnly && hasSelection()) { connect(&switchXzAction, &QAction::triggered, this, &SkeletonGraphicsWidget::switchSelectedXZ); contextMenu.addAction(&switchXzAction); } @@ -207,7 +210,7 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos) QAction alignToGlobalCenterAction(tr("Global Center"), this); QAction alignToGlobalVerticalCenterAction(tr("Global Vertical Center"), this); QAction alignToGlobalHorizontalCenterAction(tr("Global Horizontal Center"), this); - if ((hasSelection() && m_document->originSettled()) || hasMultipleSelection()) { + if (!m_nodePositionModifyOnly && ((hasSelection() && m_document->originSettled()) || hasMultipleSelection())) { QMenu *subMenu = contextMenu.addMenu(tr("Align To")); if (hasMultipleSelection()) { @@ -238,7 +241,7 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos) for (int i = 0; i < (int)BoneMark::Count - 1; i++) { markAsActions[i] = nullptr; } - if (hasNodeSelection()) { + if (!m_nodePositionModifyOnly && hasNodeSelection()) { QMenu *subMenu = contextMenu.addMenu(tr("Mark As")); connect(&markAsNoneAction, &QAction::triggered, [=]() { @@ -265,7 +268,7 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos) } QAction selectPartAllAction(tr("Select Part"), this); - if (hasItems()) { + if (!m_nodePositionModifyOnly && hasItems()) { connect(&selectPartAllAction, &QAction::triggered, this, &SkeletonGraphicsWidget::selectPartAll); contextMenu.addAction(&selectPartAllAction); } @@ -2456,3 +2459,8 @@ void SkeletonGraphicsWidget::setSelectedNodesBoneMark(BoneMark boneMark) emit groupOperationAdded(); } } + +void SkeletonGraphicsWidget::setNodePositionModifyOnly(bool nodePositionModifyOnly) +{ + m_nodePositionModifyOnly = nodePositionModifyOnly; +} diff --git a/src/skeletongraphicswidget.h b/src/skeletongraphicswidget.h index f4db94f6..1d2c593d 100644 --- a/src/skeletongraphicswidget.h +++ b/src/skeletongraphicswidget.h @@ -408,6 +408,7 @@ public: bool hasNodeSelection(); bool hasTwoDisconnectedNodesSelection(); void setModelWidget(ModelWidget *modelWidget); + void setNodePositionModifyOnly(bool nodePositionModifyOnly); protected: void mouseMoveEvent(QMouseEvent *event) override; void wheelEvent(QWheelEvent *event) override; @@ -557,6 +558,7 @@ private: //need initalize ModelWidget *m_modelWidget; bool m_inTempDragMode; SkeletonDocumentEditMode m_modeBeforeEnterTempDragMode; + bool m_nodePositionModifyOnly; private: QVector3D m_ikMoveTarget; QUuid m_ikMoveEndEffectorId; diff --git a/src/tetrapodposer.cpp b/src/tetrapodposer.cpp index d9203515..f8c09204 100644 --- a/src/tetrapodposer.cpp +++ b/src/tetrapodposer.cpp @@ -1,3 +1,5 @@ +#include +#include #include "tetrapodposer.h" #include "util.h" @@ -6,79 +8,211 @@ TetrapodPoser::TetrapodPoser(const std::vector &bones) : { } -void TetrapodPoser::commit() +void TetrapodPoser::resolveTranslation() { for (const auto &item: parameters()) { int boneIndex = findBoneIndex(item.first); if (-1 == boneIndex) { continue; } - auto findPitchResult = item.second.find("pitch"); - auto findYawResult = item.second.find("yaw"); - auto findRollResult = item.second.find("roll"); - if (findPitchResult != item.second.end() || - findYawResult != item.second.end() || - findRollResult != item.second.end()) { - float yawAngle = valueOfKeyInMapOrEmpty(item.second, "yaw").toFloat(); - if (item.first.startsWith("Left")) { - yawAngle = -yawAngle; - } - QQuaternion rotation = eulerAnglesToQuaternion(valueOfKeyInMapOrEmpty(item.second, "pitch").toFloat(), - yawAngle, - valueOfKeyInMapOrEmpty(item.second, "roll").toFloat()); - m_jointNodeTree.updateRotation(boneIndex, rotation); - continue; - } - auto findIntersectionResult = item.second.find("intersection"); - if (findIntersectionResult != item.second.end()) { - float intersectionAngle = valueOfKeyInMapOrEmpty(item.second, "intersection").toFloat(); - const RiggerBone &bone = bones()[boneIndex]; - if (bone.name == "LeftHand" || bone.name == "RightHand") { - QVector3D handDirection = bone.tailPosition - bone.headPosition; - QVector3D referenceDirection = bone.name == "RightHand" ? QVector3D(1, 0, 0) : QVector3D(-1, 0, 0); - auto angleWithX = (int)angleBetweenVectors(handDirection, -referenceDirection); - auto angleWithZ = (int)angleBetweenVectors(handDirection, QVector3D(0, 0, -1)); - QVector3D rotateAxis = angleWithX < angleWithZ ? - QVector3D::crossProduct(handDirection, referenceDirection).normalized() : - QVector3D::crossProduct(handDirection, QVector3D(0, 0, -1)).normalized(); - QQuaternion rotation = QQuaternion::fromAxisAndAngle(rotateAxis, intersectionAngle); - m_jointNodeTree.updateRotation(boneIndex, rotation); - continue; - } else if (bone.name == "LeftLowerArm" || bone.name == "RightLowerArm") { - QVector3D lowerArmDirection = bone.tailPosition - bone.headPosition; - QVector3D rotateAxis = QVector3D::crossProduct(lowerArmDirection, QVector3D(0, 0, 1)).normalized(); - QQuaternion rotation = QQuaternion::fromAxisAndAngle(rotateAxis, intersectionAngle); - m_jointNodeTree.updateRotation(boneIndex, rotation); - continue; - } else if (bone.name == "LeftFoot" || bone.name == "RightFoot") { - QVector3D footDirection = bone.tailPosition - bone.headPosition; - QVector3D rotateAxis = QVector3D::crossProduct(footDirection, QVector3D(0, 1, 0)).normalized(); - QQuaternion rotation = QQuaternion::fromAxisAndAngle(rotateAxis, intersectionAngle); - m_jointNodeTree.updateRotation(boneIndex, rotation); - continue; - } else if (bone.name == "LeftLowerLeg" || bone.name == "RightLowerLeg") { - QVector3D lowerLegDirection = bone.tailPosition - bone.headPosition; - QVector3D rotateAxis = QVector3D::crossProduct(lowerLegDirection, QVector3D(0, 0, -1)).normalized(); - QQuaternion rotation = QQuaternion::fromAxisAndAngle(rotateAxis, intersectionAngle); - m_jointNodeTree.updateRotation(boneIndex, rotation); - continue; - } - continue; - } - auto findXResult = item.second.find("x"); - auto findYResult = item.second.find("y"); - auto findZResult = item.second.find("z"); - if (findXResult != item.second.end() || - findYResult != item.second.end() || - findZResult != item.second.end()) { - float x = valueOfKeyInMapOrEmpty(item.second, "x").toFloat(); - float y = valueOfKeyInMapOrEmpty(item.second, "y").toFloat(); - float z = valueOfKeyInMapOrEmpty(item.second, "z").toFloat(); + auto findTranslateXResult = item.second.find("translateX"); + auto findTranslateYResult = item.second.find("translateY"); + auto findTranslateZResult = item.second.find("translateZ"); + if (findTranslateXResult != item.second.end() || + findTranslateYResult != item.second.end() || + findTranslateZResult != item.second.end()) { + float x = valueOfKeyInMapOrEmpty(item.second, "translateX").toFloat(); + float y = valueOfKeyInMapOrEmpty(item.second, "translateY").toFloat(); + float z = valueOfKeyInMapOrEmpty(item.second, "translateZ").toFloat(); QVector3D translation = {x, y, z}; m_jointNodeTree.addTranslation(boneIndex, translation); continue; } } + resolveLimbRotation({QString("LeftUpperArm"), QString("LeftLowerArm"), QString("LeftHand")}); + resolveLimbRotation({QString("RightUpperArm"), QString("RightLowerArm"), QString("RightHand")}); + resolveLimbRotation({QString("LeftUpperLeg"), QString("LeftLowerLeg"), QString("LeftFoot")}); + resolveLimbRotation({QString("RightUpperLeg"), QString("RightLowerLeg"), QString("RightFoot")}); + resolveLimbRotation({QString("Spine"), QString("Chest"), QString("Neck"), QString("Head")}); +} + +std::pair TetrapodPoser::findQVector3DFromMap(const std::map &map, const QString &xName, const QString &yName, const QString &zName) +{ + auto findXResult = map.find(xName); + auto findYResult = map.find(yName); + auto findZResult = map.find(zName); + if (findXResult == map.end() && + findYResult == map.end() && + findZResult == map.end()) { + return {false, QVector3D()}; + } + return {true, { + valueOfKeyInMapOrEmpty(map, xName).toFloat(), + valueOfKeyInMapOrEmpty(map, yName).toFloat(), + valueOfKeyInMapOrEmpty(map, zName).toFloat() + }}; +} + +std::pair> TetrapodPoser::findBonePositionsFromParameters(const std::map &map) +{ + auto findBoneStartResult = findQVector3DFromMap(map, "fromX", "fromY", "fromZ"); + auto findBoneStopResult = findQVector3DFromMap(map, "toX", "toY", "toZ"); + + if (!findBoneStartResult.first || !findBoneStopResult.first) + return {false, {QVector3D(), QVector3D()}}; + + return {true, {findBoneStartResult.second, findBoneStopResult.second}}; +} + +void TetrapodPoser::resolveLimbRotation(const std::vector &limbBoneNames) +{ + // We match the poses by the distance and rotation plane + + if (limbBoneNames.size() < 3) { + qDebug() << "Cann't resolve limb bones with invalid joints:" << limbBoneNames.size(); + return; + } + const auto &beginBoneName = limbBoneNames[0]; + const auto &middleBoneName = limbBoneNames[1]; + const auto &endBoneName = limbBoneNames[2]; + + const auto &beginBoneParameters = parameters().find(beginBoneName); + if (beginBoneParameters == parameters().end()) { + qDebug() << beginBoneName << "'s parameters not found"; + return; + } + + auto matchBeginBonePositions = findBonePositionsFromParameters(beginBoneParameters->second); + if (!matchBeginBonePositions.first) { + qDebug() << beginBoneName << "'s positions not found"; + return; + } + + const auto &endBoneParameters = parameters().find(endBoneName); + if (endBoneParameters == parameters().end()) { + qDebug() << endBoneName << "'s parameters not found"; + return; + } + + auto matchEndBonePositions = findBonePositionsFromParameters(endBoneParameters->second); + if (!matchEndBonePositions.first) { + qDebug() << endBoneName << "'s positions not found"; + return; + } + + float matchLimbLength = (matchBeginBonePositions.second.first - matchBeginBonePositions.second.second).length() + + (matchBeginBonePositions.second.second - matchEndBonePositions.second.first).length(); + + auto matchDistanceBetweenBeginAndEndBones = (matchBeginBonePositions.second.first - matchEndBonePositions.second.first).length(); + auto matchRotatePlaneNormal = QVector3D::crossProduct((matchBeginBonePositions.second.second - matchBeginBonePositions.second.first).normalized(), (matchEndBonePositions.second.first - matchBeginBonePositions.second.second).normalized()); + + auto matchDirectionBetweenBeginAndEndPones = (matchEndBonePositions.second.first - matchBeginBonePositions.second.first).normalized(); + + auto matchEndBoneDirection = (matchEndBonePositions.second.second - matchEndBonePositions.second.first).normalized(); + + int beginBoneIndex = findBoneIndex(beginBoneName); + if (-1 == beginBoneIndex) { + qDebug() << beginBoneName << "not found in rigged bones"; + return; + } + const auto &beginBone = bones()[beginBoneIndex]; + + int middleBoneIndex = findBoneIndex(middleBoneName); + if (-1 == middleBoneIndex) { + qDebug() << middleBoneName << "not found in rigged bones"; + return; + } + + int endBoneIndex = findBoneIndex(endBoneName); + if (-1 == endBoneIndex) { + qDebug() << endBoneName << "not found in rigged bones"; + return; + } + const auto &endBone = bones()[endBoneIndex]; + + float targetBeginBoneLength = (beginBone.headPosition - beginBone.tailPosition).length(); + float targetMiddleBoneLength = (beginBone.tailPosition - endBone.headPosition).length(); + float targetLimbLength = targetBeginBoneLength + targetMiddleBoneLength; + + float targetDistanceBetweenBeginAndEndBones = matchDistanceBetweenBeginAndEndBones * (targetLimbLength / matchLimbLength); + QVector3D targetEndBoneStartPosition = beginBone.headPosition + matchDirectionBetweenBeginAndEndPones * targetDistanceBetweenBeginAndEndBones; + + float angleBetweenDistanceAndMiddleBones = 0; + { + const float &a = targetMiddleBoneLength; + const float &b = targetDistanceBetweenBeginAndEndBones; + const float &c = targetBeginBoneLength; + double cosC = (a*a + b*b - c*c) / (2.0*a*b); + angleBetweenDistanceAndMiddleBones = qRadiansToDegrees(acos(cosC)); + } + + QVector3D targetMiddleBoneStartPosition; + { + qDebug() << beginBoneName << "Angle:" << angleBetweenDistanceAndMiddleBones; + auto rotation = QQuaternion::fromAxisAndAngle(matchRotatePlaneNormal, angleBetweenDistanceAndMiddleBones); + targetMiddleBoneStartPosition = targetEndBoneStartPosition + rotation.rotatedVector(-matchDirectionBetweenBeginAndEndPones).normalized() * targetMiddleBoneLength; + } + + // Now the bones' positions have been resolved, we calculate the rotation + + auto oldBeginBoneDirection = (beginBone.tailPosition - beginBone.headPosition).normalized(); + auto newBeginBoneDirection = (targetMiddleBoneStartPosition - beginBone.headPosition).normalized(); + auto beginBoneRotation = QQuaternion::rotationTo(oldBeginBoneDirection, newBeginBoneDirection); + m_jointNodeTree.updateRotation(beginBoneIndex, beginBoneRotation); + + auto oldMiddleBoneDirection = (endBone.headPosition - beginBone.tailPosition).normalized(); + auto newMiddleBoneDirection = (targetEndBoneStartPosition - targetMiddleBoneStartPosition).normalized(); + oldMiddleBoneDirection = beginBoneRotation.rotatedVector(oldMiddleBoneDirection); + auto middleBoneRotation = QQuaternion::rotationTo(oldMiddleBoneDirection, newMiddleBoneDirection); + m_jointNodeTree.updateRotation(middleBoneIndex, middleBoneRotation); + + // Calculate the end effectors' rotation + auto oldEndBoneDirection = (endBone.tailPosition - endBone.headPosition).normalized(); + auto newEndBoneDirection = matchEndBoneDirection; + oldEndBoneDirection = beginBoneRotation.rotatedVector(oldEndBoneDirection); + oldEndBoneDirection = middleBoneRotation.rotatedVector(oldEndBoneDirection); + auto endBoneRotation = QQuaternion::rotationTo(oldEndBoneDirection, newEndBoneDirection); + m_jointNodeTree.updateRotation(endBoneIndex, endBoneRotation); + + if (limbBoneNames.size() > 3) { + std::vector rotations; + rotations.push_back(beginBoneRotation); + rotations.push_back(middleBoneRotation); + rotations.push_back(endBoneRotation); + for (size_t i = 3; i < limbBoneNames.size(); ++i) { + const auto &boneName = limbBoneNames[i]; + int boneIndex = findBoneIndex(boneName); + if (-1 == boneIndex) { + qDebug() << "Find bone failed:" << boneName; + continue; + } + const auto &bone = bones()[boneIndex]; + const auto &boneParameters = parameters().find(boneName); + if (boneParameters == parameters().end()) { + qDebug() << "Find bone parameters:" << boneName; + continue; + } + auto matchBonePositions = findBonePositionsFromParameters(boneParameters->second); + if (!matchBonePositions.first) { + qDebug() << "Find bone positions failed:" << boneName; + continue; + } + auto matchBoneDirection = (matchBonePositions.second.second - matchBonePositions.second.first).normalized(); + auto oldBoneDirection = (bone.tailPosition - bone.headPosition).normalized(); + auto newBoneDirection = matchBoneDirection; + for (const auto &rotation: rotations) { + oldBoneDirection = rotation.rotatedVector(oldBoneDirection); + } + auto boneRotation = QQuaternion::rotationTo(oldBoneDirection, newBoneDirection); + m_jointNodeTree.updateRotation(boneIndex, boneRotation); + rotations.push_back(boneRotation); + } + } +} + +void TetrapodPoser::commit() +{ + resolveTranslation(); Poser::commit(); } diff --git a/src/tetrapodposer.h b/src/tetrapodposer.h index 18268ada..73d4eba1 100644 --- a/src/tetrapodposer.h +++ b/src/tetrapodposer.h @@ -1,5 +1,6 @@ #ifndef DUST3D_TETRAPOD_POSER_H #define DUST3D_TETRAPOD_POSER_H +#include #include "poser.h" class TetrapodPoser : public Poser @@ -7,8 +8,13 @@ class TetrapodPoser : public Poser Q_OBJECT public: TetrapodPoser(const std::vector &bones); -public: void commit() override; + +private: + void resolveTranslation(); + void resolveLimbRotation(const std::vector &limbBoneNames); + std::pair findQVector3DFromMap(const std::map &map, const QString &xName, const QString &yName, const QString &zName); + std::pair> findBonePositionsFromParameters(const std::map &map); }; #endif diff --git a/src/tetrapodrigger.cpp b/src/tetrapodrigger.cpp index 27e61bab..bfbd0de9 100644 --- a/src/tetrapodrigger.cpp +++ b/src/tetrapodrigger.cpp @@ -371,7 +371,7 @@ bool TetrapodRigger::rig() m_resultBones.push_back(RiggerBone()); RiggerBone &bodyBone = m_resultBones.back(); bodyBone.index = m_resultBones.size() - 1; - bodyBone.name = "Body"; + bodyBone.name = Rigger::rootBoneName; bodyBone.headPosition = QVector3D(0, 0, 0); bodyBone.tailPosition = bonesOrigin; bodyBone.hasButton = true; @@ -383,10 +383,10 @@ bool TetrapodRigger::rig() RiggerBone &leftHipBone = m_resultBones.back(); leftHipBone.index = m_resultBones.size() - 1; leftHipBone.name = "LeftHip"; - leftHipBone.headPosition = m_resultBones[boneIndexMap["Body"]].tailPosition; + leftHipBone.headPosition = m_resultBones[boneIndexMap[Rigger::rootBoneName]].tailPosition; leftHipBone.tailPosition = leftUpperLegBoneStartPosition; boneIndexMap[leftHipBone.name] = leftHipBone.index; - m_resultBones[boneIndexMap["Body"]].children.push_back(leftHipBone.index); + m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(leftHipBone.index); m_resultBones.push_back(RiggerBone()); RiggerBone &leftUpperLegBone = m_resultBones.back(); @@ -431,10 +431,10 @@ bool TetrapodRigger::rig() RiggerBone &rightHipBone = m_resultBones.back(); rightHipBone.index = m_resultBones.size() - 1; rightHipBone.name = "RightHip"; - rightHipBone.headPosition = m_resultBones[boneIndexMap["Body"]].tailPosition; + rightHipBone.headPosition = m_resultBones[boneIndexMap[Rigger::rootBoneName]].tailPosition; rightHipBone.tailPosition = rightUpperLegBoneStartPosition; boneIndexMap[rightHipBone.name] = rightHipBone.index; - m_resultBones[boneIndexMap["Body"]].children.push_back(rightHipBone.index); + m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(rightHipBone.index); m_resultBones.push_back(RiggerBone()); RiggerBone &rightUpperLegBone = m_resultBones.back(); @@ -479,14 +479,14 @@ bool TetrapodRigger::rig() RiggerBone &spineBone = m_resultBones.back(); spineBone.index = m_resultBones.size() - 1; spineBone.name = "Spine"; - spineBone.headPosition = m_resultBones[boneIndexMap["Body"]].tailPosition; + spineBone.headPosition = m_resultBones[boneIndexMap[Rigger::rootBoneName]].tailPosition; spineBone.tailPosition = chestBoneStartPosition; spineBone.color = Qt::white; spineBone.hasButton = true; spineBone.button = {3, 1}; spineBone.buttonParameterType = RiggerButtonParameterType::PitchYawRoll; boneIndexMap[spineBone.name] = spineBone.index; - m_resultBones[boneIndexMap["Body"]].children.push_back(spineBone.index); + m_resultBones[boneIndexMap[Rigger::rootBoneName]].children.push_back(spineBone.index); m_resultBones.push_back(RiggerBone()); RiggerBone &chestBone = m_resultBones.back();