diff --git a/ACKNOWLEDGEMENTS.html b/ACKNOWLEDGEMENTS.html index 1014bd6d..df33c78a 100644 --- a/ACKNOWLEDGEMENTS.html +++ b/ACKNOWLEDGEMENTS.html @@ -557,3 +557,9 @@ https://www.reddit.com/r/gamedev/comments/5iuf3h/i_am_writting_a_3d_monster_mode 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. + +

Ahmad Abdul Karim, Alexandre Meyer, Thibaut Gaudin, Axel Buendia and Saida Bouakaz1

+
+    Generic Spine Model with Simple Physics for Life-Like Quadrupeds and Reptiles
+    http://liris.cnrs.fr/Documents/Liris-5778.pdf
+
diff --git a/docs/interface/menubar.rst b/docs/interface/menubar.rst index 22093902..2471edec 100644 --- a/docs/interface/menubar.rst +++ b/docs/interface/menubar.rst @@ -93,6 +93,10 @@ Rotate 90D CCW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Rotate selected nodes counterclockwise by 90 degrees. +Switch XZ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Switch selected nodes' X and Z position. if you accidentally put some parts in front view which you planned put into side view, you can select these nodes and switch the XZ components. + Align To ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Align selected nodes with center anchor globally or selected nodes' center locally. Normally, the center anchor(a Triangle) is not show up, you can turn on the Part Mirror to make it visible, then turn Part Mirror off, the center anchor would not gone once showed. diff --git a/src/jointnodetree.cpp b/src/jointnodetree.cpp index 572cb03b..eb77ae77 100644 --- a/src/jointnodetree.cpp +++ b/src/jointnodetree.cpp @@ -79,12 +79,17 @@ void JointNodeTree::traceBoneFromJoint(MeshResultContext &resultContext, std::pa } } +const std::vector &JointNodeTree::joints() const +{ + return m_tracedJoints; +} + std::vector &JointNodeTree::joints() { return m_tracedJoints; } -int JointNodeTree::nodeToJointIndex(int partId, int nodeId) +int JointNodeTree::nodeToJointIndex(int partId, int nodeId) const { const auto &findIt = m_tracedNodeToJointIndexMap.find(std::make_pair(partId, nodeId)); if (findIt == m_tracedNodeToJointIndexMap.end()) { diff --git a/src/jointnodetree.h b/src/jointnodetree.h index 4ad7d163..52c54be7 100644 --- a/src/jointnodetree.h +++ b/src/jointnodetree.h @@ -28,8 +28,9 @@ class JointNodeTree { public: JointNodeTree(MeshResultContext &resultContext); + const std::vector &joints() const; std::vector &joints(); - int nodeToJointIndex(int partId, int nodeId); + int nodeToJointIndex(int partId, int nodeId) const; void recalculateMatricesAfterPositionUpdated(); void recalculateMatricesAfterTransformUpdated(); private: diff --git a/src/skeletondocument.cpp b/src/skeletondocument.cpp index dd976d24..69538c51 100644 --- a/src/skeletondocument.cpp +++ b/src/skeletondocument.cpp @@ -530,6 +530,20 @@ void SkeletonDocument::setNodeRadius(QUuid nodeId, float radius) emit skeletonChanged(); } +void SkeletonDocument::switchNodeXZ(QUuid nodeId) +{ + auto it = nodeMap.find(nodeId); + if (it == nodeMap.end()) { + qDebug() << "Find node failed:" << nodeId; + return; + } + if (isPartReadonly(it->second.partId)) + return; + std::swap(it->second.x, it->second.z); + emit nodeOriginChanged(nodeId); + emit skeletonChanged(); +} + void SkeletonDocument::setNodeBoneMark(QUuid nodeId, SkeletonBoneMark mark) { auto it = nodeMap.find(nodeId); diff --git a/src/skeletondocument.h b/src/skeletondocument.h index 4dbee2b3..bdd4c3d4 100644 --- a/src/skeletondocument.h +++ b/src/skeletondocument.h @@ -291,6 +291,7 @@ public slots: void setNodeOrigin(QUuid nodeId, float x, float y, float z); void setNodeRadius(QUuid nodeId, float radius); void setNodeBoneMark(QUuid nodeId, SkeletonBoneMark mark); + void switchNodeXZ(QUuid nodeId); void moveOriginBy(float x, float y, float z); void addEdge(QUuid fromNodeId, QUuid toNodeId); void setEditMode(SkeletonDocumentEditMode mode); diff --git a/src/skeletondocumentwindow.cpp b/src/skeletondocumentwindow.cpp index 8be24ccc..dec265d4 100644 --- a/src/skeletondocumentwindow.cpp +++ b/src/skeletondocumentwindow.cpp @@ -316,6 +316,12 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : }); 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_alignToGlobalCenterAction = new QAction(tr("Global Center"), this); @@ -390,6 +396,7 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : 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()); @@ -547,6 +554,7 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : connect(graphicsWidget, &SkeletonGraphicsWidget::moveOriginBy, m_document, &SkeletonDocument::moveOriginBy); connect(graphicsWidget, &SkeletonGraphicsWidget::partChecked, m_document, &SkeletonDocument::partChecked); connect(graphicsWidget, &SkeletonGraphicsWidget::partUnchecked, m_document, &SkeletonDocument::partUnchecked); + connect(graphicsWidget, &SkeletonGraphicsWidget::switchNodeXZ, m_document, &SkeletonDocument::switchNodeXZ); connect(graphicsWidget, &SkeletonGraphicsWidget::setPartLockState, m_document, &SkeletonDocument::setPartLockState); connect(graphicsWidget, &SkeletonGraphicsWidget::setPartVisibleState, m_document, &SkeletonDocument::setPartVisibleState); diff --git a/src/skeletondocumentwindow.h b/src/skeletondocumentwindow.h index 6deaf72b..b27407cf 100644 --- a/src/skeletondocumentwindow.h +++ b/src/skeletondocumentwindow.h @@ -96,6 +96,7 @@ private: QAction *m_flipVerticallyAction; QAction *m_rotateClockwiseAction; QAction *m_rotateCounterclockwiseAction; + QAction *m_switchXzAction; QMenu *m_alignToMenu; QAction *m_alignToGlobalCenterAction; diff --git a/src/skeletongraphicswidget.cpp b/src/skeletongraphicswidget.cpp index 8fd87a5e..3f27d105 100644 --- a/src/skeletongraphicswidget.cpp +++ b/src/skeletongraphicswidget.cpp @@ -187,6 +187,12 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos) contextMenu.addAction(&rotateCounterclockwiseAction); } + QAction switchXzAction(tr("Switch XZ"), this); + if (hasSelection()) { + connect(&switchXzAction, &QAction::triggered, this, &SkeletonGraphicsWidget::switchSelectedXZ); + contextMenu.addAction(&switchXzAction); + } + QAction alignToLocalCenterAction(tr("Local Center"), this); QAction alignToLocalVerticalCenterAction(tr("Local Vertical Center"), this); QAction alignToLocalHorizontalCenterAction(tr("Local Horizontal Center"), this); @@ -844,6 +850,24 @@ void SkeletonGraphicsWidget::moveSelected(float byX, float byY) } } +void SkeletonGraphicsWidget::switchSelectedXZ() +{ + if (m_rangeSelectionSet.empty()) + return; + + std::set nodeIdSet; + readSkeletonNodeAndEdgeIdSetFromRangeSelection(&nodeIdSet); + if (nodeIdSet.empty()) + return; + + emit batchChangeBegin(); + for (const auto nodeId: nodeIdSet) { + emit switchNodeXZ(nodeId); + } + emit batchChangeEnd(); + emit groupOperationAdded(); +} + void SkeletonGraphicsWidget::zoomSelected(float delta) { if (m_rangeSelectionSet.empty()) @@ -1796,7 +1820,8 @@ void SkeletonGraphicsWidget::readSkeletonNodeAndEdgeIdSetFromRangeSelection(std: if (item->data(0) == "node") { nodeIdSet->insert(((SkeletonGraphicsNodeItem *)item)->id()); } else if (item->data(0) == "edge") { - edgeIdSet->insert(((SkeletonGraphicsEdgeItem *)item)->id()); + if (nullptr != edgeIdSet) + edgeIdSet->insert(((SkeletonGraphicsEdgeItem *)item)->id()); } } } diff --git a/src/skeletongraphicswidget.h b/src/skeletongraphicswidget.h index 09c3aaf9..2cc2b85b 100644 --- a/src/skeletongraphicswidget.h +++ b/src/skeletongraphicswidget.h @@ -385,6 +385,7 @@ signals: void setNodeOrigin(QUuid nodeId, float x, float y, float z); void setNodeBoneMark(QUuid nodeId, SkeletonBoneMark mark); void zoomRenderedModelBy(float delta); + void switchNodeXZ(QUuid nodeId); public: SkeletonGraphicsWidget(const SkeletonDocument *document); std::map> nodeItemMap; @@ -399,7 +400,7 @@ public: QUuid querySkeletonItemPartId(QGraphicsItem *item); static SkeletonProfile readSkeletonItemProfile(QGraphicsItem *item); void readMergedSkeletonNodeSetFromRangeSelection(std::set *nodeItemSet); - void readSkeletonNodeAndEdgeIdSetFromRangeSelection(std::set *nodeIdSet, std::set *edgeIdSet); + void readSkeletonNodeAndEdgeIdSetFromRangeSelection(std::set *nodeIdSet, std::set *edgeIdSet=nullptr); bool readSkeletonNodeAndAnyEdgeOfNodeFromRangeSelection(SkeletonGraphicsNodeItem **nodeItem, SkeletonGraphicsEdgeItem **edgeItem); bool hasSelection(); bool hasItems(); @@ -463,6 +464,7 @@ public slots: void ikMoveReady(); void setSelectedNodesBoneMark(SkeletonBoneMark boneMark); void timeToRemoveDeferredNodesAndEdges(); + void switchSelectedXZ(); private slots: void turnaroundImageReady(); private: