diff --git a/dust3d.pro b/dust3d.pro index 9d82921f..a83c7a74 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -291,6 +291,12 @@ HEADERS += src/skeletondocument.h SOURCES += src/posedocument.cpp HEADERS += src/posedocument.h +SOURCES += src/combinemode.cpp +HEADERS += src/combinemode.h + +SOURCES += src/meshinflate.cpp +HEADERS += src/meshinflate.h + SOURCES += src/main.cpp HEADERS += src/version.h diff --git a/src/combinemode.cpp b/src/combinemode.cpp new file mode 100644 index 00000000..4087cdd5 --- /dev/null +++ b/src/combinemode.cpp @@ -0,0 +1,6 @@ +#include +#include "combinemode.h" + +IMPL_CombineModeToString +IMPL_CombineModeFromString +IMPL_CombineModeToDispName diff --git a/src/combinemode.h b/src/combinemode.h new file mode 100644 index 00000000..9ec4a328 --- /dev/null +++ b/src/combinemode.h @@ -0,0 +1,56 @@ +#ifndef DUST3D_COMBINE_MODE_H +#define DUST3D_COMBINE_MODE_H +#include + +enum class CombineMode +{ + Normal = 0, + Inversion, + Inflation, + Count +}; +CombineMode CombineModeFromString(const char *modeString); +#define IMPL_CombineModeFromString \ +CombineMode CombineModeFromString(const char *modeString) \ +{ \ + QString mode = modeString; \ + if (mode == "Normal") \ + return CombineMode::Normal; \ + if (mode == "Inversion") \ + return CombineMode::Inversion; \ + if (mode == "Inflation") \ + return CombineMode::Inflation; \ + return CombineMode::Normal; \ +} +const char *CombineModeToString(CombineMode mode); +#define IMPL_CombineModeToString \ +const char *CombineModeToString(CombineMode mode) \ +{ \ + switch (mode) { \ + case CombineMode::Normal: \ + return "Normal"; \ + case CombineMode::Inversion: \ + return "Inversion"; \ + case CombineMode::Inflation: \ + return "Inflation"; \ + default: \ + return "Normal"; \ + } \ +} +QString CombineModeToDispName(CombineMode mode); +#define IMPL_CombineModeToDispName \ +QString CombineModeToDispName(CombineMode mode) \ +{ \ + switch (mode) { \ + case CombineMode::Normal: \ + return QObject::tr("Normal"); \ + case CombineMode::Inversion: \ + return QObject::tr("Inversion"); \ + case CombineMode::Inflation: \ + return QObject::tr("Inflation"); \ + default: \ + return QObject::tr("Normal"); \ + } \ +} + +#endif diff --git a/src/document.cpp b/src/document.cpp index c96c343b..4997b8bd 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -906,7 +906,7 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set &limitNodeId if (!componentIt.second.name.isEmpty()) component["name"] = componentIt.second.name; component["expanded"] = componentIt.second.expanded ? "true" : "false"; - component["inverse"] = componentIt.second.inverse ? "true" : "false"; + component["combineMode"] = CombineModeToString(componentIt.second.combineMode); component["dirty"] = componentIt.second.dirty ? "true" : "false"; if (componentIt.second.smoothAllAdjusted()) component["smoothAll"] = QString::number(componentIt.second.smoothAll); @@ -1181,7 +1181,11 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste) oldNewIdMap[QUuid(componentKv.first)] = component.id; component.name = valueOfKeyInMapOrEmpty(componentKv.second, "name"); component.expanded = isTrueValueString(valueOfKeyInMapOrEmpty(componentKv.second, "expanded")); - component.inverse = isTrueValueString(valueOfKeyInMapOrEmpty(componentKv.second, "inverse")); + component.combineMode = CombineModeFromString(valueOfKeyInMapOrEmpty(componentKv.second, "combineMode").toUtf8().constData()); + if (component.combineMode == CombineMode::Normal) { + if (isTrueValueString(valueOfKeyInMapOrEmpty(componentKv.second, "inverse"))) + component.combineMode = CombineMode::Inversion; + } const auto &smoothAllIt = componentKv.second.find("smoothAll"); if (smoothAllIt != componentKv.second.end()) component.setSmoothAll(smoothAllIt->second.toFloat()); @@ -1195,7 +1199,7 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste) //qDebug() << "Add part:" << partId << " from component:" << component.id; partMap[partId].componentId = component.id; if (inversePartIds.find(partId) != inversePartIds.end()) - component.inverse = true; + component.combineMode = CombineMode::Inversion; } componentMap[component.id] = component; newAddedComponentIds.insert(component.id); @@ -1615,18 +1619,18 @@ void Document::setPartVisibleState(QUuid partId, bool visible) emit optionsChanged(); } -void Document::setComponentInverseState(QUuid componentId, bool inverse) +void Document::setComponentCombineMode(QUuid componentId, CombineMode combineMode) { auto component = componentMap.find(componentId); if (component == componentMap.end()) { qDebug() << "Component not found:" << componentId; return; } - if (component->second.inverse == inverse) + if (component->second.combineMode == combineMode) return; - component->second.inverse = inverse; + component->second.combineMode = combineMode; component->second.dirty = true; - emit componentInverseStateChanged(componentId); + emit componentCombineModeChanged(componentId); emit skeletonChanged(); } diff --git a/src/document.h b/src/document.h index 5bcfdbbd..97cb815d 100644 --- a/src/document.h +++ b/src/document.h @@ -24,6 +24,7 @@ #include "interpolationtype.h" #include "jointnodetree.h" #include "skeletondocument.h" +#include "combinemode.h" class MaterialPreviewsGenerator; class MotionsGenerator; @@ -55,7 +56,7 @@ public: QUuid linkToPartId; QUuid parentId; bool expanded = true; - bool inverse = false; + CombineMode combineMode = CombineMode::Normal; bool dirty = true; float smoothAll = 0.0; float smoothSeam = 0.0; @@ -401,7 +402,7 @@ signals: void partColorStateChanged(QUuid partId); void partWrapStateChanged(QUuid partId); void partMaterialIdChanged(QUuid partId); - void componentInverseStateChanged(QUuid partId); + void componentCombineModeChanged(QUuid componentId); void cleanup(); void originChanged(); void xlockStateChanged(); @@ -550,7 +551,7 @@ public slots: void setPartColorState(QUuid partId, bool hasColor, QColor color); void setPartWrapState(QUuid partId, bool wrapped); void setPartMaterialId(QUuid partId, QUuid materialId); - void setComponentInverseState(QUuid componentId, bool inverse); + void setComponentCombineMode(QUuid componentId, CombineMode combineMode); void moveComponentUp(QUuid componentId); void moveComponentDown(QUuid componentId); void moveComponentToTop(QUuid componentId); diff --git a/src/documentwindow.cpp b/src/documentwindow.cpp index 8ad85a65..21074683 100644 --- a/src/documentwindow.cpp +++ b/src/documentwindow.cpp @@ -783,7 +783,7 @@ DocumentWindow::DocumentWindow() : 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::setComponentCombineMode, m_document, &Document::setComponentCombineMode); connect(partTreeWidget, &PartTreeWidget::hideDescendantComponents, m_document, &Document::hideDescendantComponents); connect(partTreeWidget, &PartTreeWidget::showDescendantComponents, m_document, &Document::showDescendantComponents); connect(partTreeWidget, &PartTreeWidget::lockDescendantComponents, m_document, &Document::lockDescendantComponents); @@ -796,6 +796,7 @@ DocumentWindow::DocumentWindow() : 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::componentCombineModeChanged, partTreeWidget, &PartTreeWidget::componentCombineModeChanged); connect(m_document, &Document::partPreviewChanged, partTreeWidget, &PartTreeWidget::partPreviewChanged); connect(m_document, &Document::partLockStateChanged, partTreeWidget, &PartTreeWidget::partLockStateChanged); connect(m_document, &Document::partVisibleStateChanged, partTreeWidget, &PartTreeWidget::partVisibleStateChanged); diff --git a/src/graphicscontainerwidget.cpp b/src/graphicscontainerwidget.cpp index c9b9846e..b0aed1fd 100644 --- a/src/graphicscontainerwidget.cpp +++ b/src/graphicscontainerwidget.cpp @@ -1,7 +1,6 @@ #include "graphicscontainerwidget.h" -GraphicsContainerWidget::GraphicsContainerWidget() : - m_graphicsWidget(nullptr) +GraphicsContainerWidget::GraphicsContainerWidget() { } @@ -31,11 +30,16 @@ void GraphicsContainerWidget::mouseReleaseEvent(QMouseEvent *event) void GraphicsContainerWidget::wheelEvent(QWheelEvent *event) { + if (m_graphicsWidget) { + m_graphicsWidget->inputWheelEventFromOtherWidget(event); + return; + } + if (m_modelWidget) m_modelWidget->inputWheelEventFromOtherWidget(event); } -void GraphicsContainerWidget::setGraphicsWidget(QWidget *graphicsWidget) +void GraphicsContainerWidget::setGraphicsWidget(SkeletonGraphicsWidget *graphicsWidget) { m_graphicsWidget = graphicsWidget; } diff --git a/src/graphicscontainerwidget.h b/src/graphicscontainerwidget.h index cd0cbff5..987d158c 100644 --- a/src/graphicscontainerwidget.h +++ b/src/graphicscontainerwidget.h @@ -5,6 +5,7 @@ #include #include #include "modelwidget.h" +#include "skeletongraphicswidget.h" class GraphicsContainerWidget : public QWidget { @@ -13,7 +14,7 @@ signals: void containerSizeChanged(QSize size); public: GraphicsContainerWidget(); - void setGraphicsWidget(QWidget *graphicsWidget); + void setGraphicsWidget(SkeletonGraphicsWidget *graphicsWidget); void setModelWidget(ModelWidget *modelWidget); protected: void resizeEvent(QResizeEvent *event) override; @@ -22,8 +23,8 @@ protected: void mouseReleaseEvent(QMouseEvent *event) override; void wheelEvent(QWheelEvent *event) override; private: - QWidget *m_graphicsWidget = nullptr; ModelWidget *m_modelWidget = nullptr; + SkeletonGraphicsWidget *m_graphicsWidget = nullptr; }; #endif diff --git a/src/meshgenerator.cpp b/src/meshgenerator.cpp index f7824b4d..e610cd16 100644 --- a/src/meshgenerator.cpp +++ b/src/meshgenerator.cpp @@ -14,6 +14,7 @@ #include "imageforever.h" #include "material.h" #include "trianglesourcenoderesolve.h" +#include "meshinflate.h" bool MeshGenerator::m_enableDebug = false; PositionMap *MeshGenerator::m_forMakePositionKey = new PositionMap; @@ -225,7 +226,7 @@ bool MeshGenerator::checkIsPartDirty(QString partId) return isTrueValueString(valueOfKeyInMapOrEmpty(findPart->second, "dirty")); } -void *MeshGenerator::combinePartMesh(QString partId) +void *MeshGenerator::combinePartMesh(QString partId, std::vector> *balls) { auto findPart = m_snapshot->parts.find(partId); if (findPart == m_snapshot->parts.end()) { @@ -306,6 +307,13 @@ void *MeshGenerator::combinePartMesh(QString partId) nodeInfo.position = QVector3D(x, y, z); nodeInfo.radius = radius; nodeInfo.boneMark = boneMark; + + if (nullptr != balls) { + balls->push_back({QVector3D(x, y, z), radius}); + if (xMirrored) { + balls->push_back({QVector3D(-x, y, z), radius}); + } + } } std::set> edges; for (const auto &edgeId: m_partEdgeIds[partId]) { @@ -499,12 +507,10 @@ bool MeshGenerator::checkIsComponentDirty(QString componentId) return isDirty; } -void *MeshGenerator::combineComponentMesh(QString componentId, bool *inverse) +void *MeshGenerator::combineComponentMesh(QString componentId, CombineMode *combineMode, std::vector> *inflateBalls) { QUuid componentIdNotAsString; - *inverse = false; - const std::map *component = &m_snapshot->rootComponent; if (componentId != QUuid().toString()) { componentIdNotAsString = QUuid(componentId); @@ -516,15 +522,26 @@ void *MeshGenerator::combineComponentMesh(QString componentId, bool *inverse) component = &findComponent->second; } - if (isTrueValueString(valueOfKeyInMapOrEmpty(*component, "inverse"))) - *inverse = true; + *combineMode = CombineModeFromString(valueOfKeyInMapOrEmpty(*component, "combineMode").toUtf8().constData()); - if (m_dirtyComponentIds.find(componentId) == m_dirtyComponentIds.end()) { - auto findCachedMesh = m_cacheContext->componentCombinableMeshs.find(componentId); - if (findCachedMesh != m_cacheContext->componentCombinableMeshs.end() && - nullptr != findCachedMesh->second) { - //qDebug() << "Component mesh cache used:" << componentId; - return cloneCombinableMesh(findCachedMesh->second); + if (*combineMode == CombineMode::Inflation) { + if (m_dirtyComponentIds.find(componentId) == m_dirtyComponentIds.end()) { + auto findCacheInflateBalls = m_cacheContext->componentInflateBalls.find(componentId); + if (findCacheInflateBalls != m_cacheContext->componentInflateBalls.end()) { + *inflateBalls = findCacheInflateBalls->second; + return nullptr; + } + } + } + + if (*combineMode != CombineMode::Inflation) { + if (m_dirtyComponentIds.find(componentId) == m_dirtyComponentIds.end()) { + auto findCachedMesh = m_cacheContext->componentCombinableMeshs.find(componentId); + if (findCachedMesh != m_cacheContext->componentCombinableMeshs.end() && + nullptr != findCachedMesh->second) { + //qDebug() << "Component mesh cache used:" << componentId; + return cloneCombinableMesh(findCachedMesh->second); + } } } @@ -552,17 +569,47 @@ void *MeshGenerator::combineComponentMesh(QString componentId, bool *inverse) QString linkDataType = valueOfKeyInMapOrEmpty(*component, "linkDataType"); if ("partId" == linkDataType) { QString partId = valueOfKeyInMapOrEmpty(*component, "linkData"); - resultMesh = combinePartMesh(partId); - for (const auto &bmeshVertex: m_cacheContext->partBmeshVertices[partId]) { - verticesSources.addPosition(bmeshVertex.first.x(), bmeshVertex.first.y(), bmeshVertex.first.z(), - bmeshVertex); + std::vector> partBalls; + resultMesh = combinePartMesh(partId, &partBalls); + if (*combineMode == CombineMode::Inflation) { + deleteCombinableMesh(resultMesh); + resultMesh = nullptr; + inflateBalls->insert(inflateBalls->end(), partBalls.begin(), partBalls.end()); + } else { + for (const auto &bmeshVertex: m_cacheContext->partBmeshVertices[partId]) { + verticesSources.addPosition(bmeshVertex.first.x(), bmeshVertex.first.y(), bmeshVertex.first.z(), + bmeshVertex); + } } } else { for (const auto &childId: valueOfKeyInMapOrEmpty(*component, "children").split(",")) { if (childId.isEmpty()) continue; - bool childInverse = false; - void *childCombinedMesh = combineComponentMesh(childId, &childInverse); + CombineMode childCombineMode = CombineMode::Normal; + std::vector> childInflateBalls; + void *childCombinedMesh = combineComponentMesh(childId, &childCombineMode, &childInflateBalls); + inflateBalls->insert(inflateBalls->end(), childInflateBalls.begin(), childInflateBalls.end()); + if (childCombineMode == CombineMode::Inflation) { + deleteCombinableMesh(childCombinedMesh); + childCombinedMesh = nullptr; + if (nullptr == resultMesh) + continue; + std::vector> inflatedVertices; + void *inflatedMesh = meshInflate(resultMesh, childInflateBalls, inflatedVertices); + deleteCombinableMesh(resultMesh); + resultMesh = inflatedMesh; + for (const auto &item: inflatedVertices) { + const auto &oldPosition = item.first; + const auto &newPosition = item.second; + std::pair> source; + if (verticesSources.findPosition(oldPosition.x(), oldPosition.y(), oldPosition.z(), &source)) { + verticesSources.removePosition(oldPosition.x(), oldPosition.y(), oldPosition.z()); + source.first = newPosition; + verticesSources.addPosition(newPosition.x(), newPosition.y(), newPosition.z(), source); + } + } + continue; + } for (const auto &positionIt: m_cacheContext->componentPositions[childId]) { positionsBeforeCombination.addPosition(positionIt.x(), positionIt.y(), positionIt.z(), true); } @@ -572,13 +619,13 @@ void *MeshGenerator::combineComponentMesh(QString componentId, bool *inverse) if (nullptr == childCombinedMesh) continue; if (nullptr == resultMesh) { - if (childInverse) { + if (childCombineMode == CombineMode::Inversion) { deleteCombinableMesh(childCombinedMesh); } else { resultMesh = childCombinedMesh; } } else { - void *newResultMesh = childInverse ? diffCombinableMeshs(resultMesh, childCombinedMesh) : unionCombinableMeshs(resultMesh, childCombinedMesh); + void *newResultMesh = childCombineMode == CombineMode::Inversion ? diffCombinableMeshs(resultMesh, childCombinedMesh) : unionCombinableMeshs(resultMesh, childCombinedMesh); deleteCombinableMesh(childCombinedMesh); if (nullptr != newResultMesh) { deleteCombinableMesh(resultMesh); @@ -643,6 +690,8 @@ void *MeshGenerator::combineComponentMesh(QString componentId, bool *inverse) } } + m_cacheContext->componentInflateBalls[componentId] = *inflateBalls; + m_cacheContext->updateComponentCombinableMesh(componentId, resultMesh); auto &cachedComponentPositions = m_cacheContext->componentPositions[componentId]; cachedComponentPositions.clear(); @@ -712,6 +761,13 @@ void MeshGenerator::generate() } it++; } + for (auto it = m_cacheContext->componentInflateBalls.begin(); it != m_cacheContext->componentInflateBalls.end(); ) { + if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) { + it = m_cacheContext->componentInflateBalls.erase(it); + continue; + } + it++; + } for (auto it = m_cacheContext->componentVerticesSources.begin(); it != m_cacheContext->componentVerticesSources.end(); ) { if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) { it = m_cacheContext->componentVerticesSources.erase(it); @@ -732,8 +788,9 @@ void MeshGenerator::generate() int resultMeshId = 0; - bool inverse = false; - void *combinedMesh = combineComponentMesh(QUuid().toString(), &inverse); + CombineMode combineMode; + std::vector> inflateBalls; + void *combinedMesh = combineComponentMesh(QUuid().toString(), &combineMode, &inflateBalls); if (nullptr != combinedMesh) { resultMeshId = convertFromCombinableMesh(m_meshliteContext, combinedMesh); deleteCombinableMesh(combinedMesh); diff --git a/src/meshgenerator.h b/src/meshgenerator.h index 1b11b95e..b87e758b 100644 --- a/src/meshgenerator.h +++ b/src/meshgenerator.h @@ -11,6 +11,7 @@ #include "meshloader.h" #include "outcome.h" #include "positionmap.h" +#include "combinemode.h" class GeneratedCacheContext { @@ -23,6 +24,7 @@ public: std::map> componentPositions; std::map>>> componentVerticesSources; std::map partMirrorIdMap; + std::map>> componentInflateBalls; void updateComponentCombinableMesh(QString componentId, void *mesh); }; @@ -78,8 +80,8 @@ private: void checkDirtyFlags(); bool checkIsComponentDirty(QString componentId); bool checkIsPartDirty(QString partId); - void *combineComponentMesh(QString componentId, bool *inverse); - void *combinePartMesh(QString partId); + void *combineComponentMesh(QString componentId, CombineMode *combineMode, std::vector> *inflateBalls); + void *combinePartMesh(QString partId, std::vector> *balls=nullptr); }; #endif diff --git a/src/meshinflate.cpp b/src/meshinflate.cpp new file mode 100644 index 00000000..1f540dc1 --- /dev/null +++ b/src/meshinflate.cpp @@ -0,0 +1,50 @@ +#include +#include +#include "meshinflate.h" +#include "meshutil.h" + +void *meshInflate(void *combinableMesh, const std::vector> &inflateBalls, std::vector> &inflatedVertices) +{ + std::vector oldPositions; + std::vector newPositions; + std::vector> faces; + std::set changedVertices; + + loadCombinableMeshVerticesPositionsAndFacesIndices(combinableMesh, oldPositions, faces); + newPositions = oldPositions; + + std::vector inflateBallRadius2s(inflateBalls.size()); + for (size_t i = 0; i < inflateBalls.size(); ++i) { + const auto &radius = inflateBalls[i].second; + inflateBallRadius2s[i] = radius * radius; + } + + for (size_t i = 0; i < oldPositions.size(); ++i) { + const auto &oldPos = oldPositions[i]; + QVector3D sumOfNewPos; + size_t pushedByBallNum = 0; + for (size_t j = 0; j < inflateBalls.size(); ++j) { + const auto &ball = inflateBalls[j]; + const auto &ballPos = ball.first; + const auto &ballRadius = std::max(ball.second, (float)0.00001); + const auto ballToPosVec = oldPos - ballPos; + if (ballToPosVec.lengthSquared() > inflateBallRadius2s[j]) + continue; + const auto distance = 1.0 - ballToPosVec.length() / ballRadius; + const auto smoothedDist = 3.0 * std::pow(distance, 2) - 2.0 * std::pow(distance, 3); + const auto newPosPushByBall = oldPos + ballToPosVec * smoothedDist; + sumOfNewPos += newPosPushByBall; + ++pushedByBallNum; + } + if (0 == pushedByBallNum) + continue; + newPositions[i] = sumOfNewPos / pushedByBallNum; + changedVertices.insert(i); + } + + for (const auto &index: changedVertices) { + inflatedVertices.push_back({oldPositions[index], newPositions[index]}); + } + + return buildCombinableMeshFromVerticesPositionsAndFacesIndices(newPositions, faces); +} \ No newline at end of file diff --git a/src/meshinflate.h b/src/meshinflate.h new file mode 100644 index 00000000..9e901ed5 --- /dev/null +++ b/src/meshinflate.h @@ -0,0 +1,8 @@ +#ifndef DUST3D_MESH_INFLATE_H +#define DUST3D_MESH_INFLATE_H +#include +#include + +void *meshInflate(void *combinableMesh, const std::vector> &inflateBalls, std::vector> &inflatedVertices); + +#endif diff --git a/src/meshutil.cpp b/src/meshutil.cpp index 83194d28..6599fe40 100644 --- a/src/meshutil.cpp +++ b/src/meshutil.cpp @@ -52,6 +52,25 @@ void loadMeshVerticesPositions(void *meshliteContext, int meshId, std::vector +typename CGAL::Surface_mesh *buildCgalMesh(const std::vector &positions, const std::vector> &indices) +{ + typename CGAL::Surface_mesh *mesh = new typename CGAL::Surface_mesh; + std::map::Vertex_index> oldToNewMap; + for (int i = 0; i < (int)positions.size(); ++i) { + const auto &pos = positions[i]; + oldToNewMap[i] = mesh->add_vertex(typename Kernel::Point_3(pos.x(), pos.y(), pos.z())); + } + for (const auto &face: indices) { + std::vector::Vertex_index> faceVertexIndices; + for (const auto &index: face) { + faceVertexIndices.push_back(oldToNewMap[index]); + } + mesh->add_face(faceVertexIndices); + } + return mesh; +} + template typename CGAL::Surface_mesh *makeCgalMeshFromMeshlite(void *meshlite, int meshId) { @@ -458,3 +477,54 @@ void *convertToCombinableConvexHullMesh(void *meshliteContext, int meshId) ExactMesh *mesh = makeCgalConvexHullMeshFromMeshlite(meshliteContext, meshId); return (void *)mesh; } + +void loadCombinableMeshVerticesPositionsAndFacesIndices(void *mesh, std::vector &positions, std::vector> &indices) +{ + ExactMesh *exactMesh = (ExactMesh *)mesh; + if (nullptr == exactMesh) + return; + + for (auto vertexIt = exactMesh->vertices_begin(); vertexIt != exactMesh->vertices_end(); vertexIt++) { + auto point = exactMesh->point(*vertexIt); + float x = (float)CGAL::to_double(point.x()); + float y = (float)CGAL::to_double(point.y()); + float z = (float)CGAL::to_double(point.z()); + if (std::isnan(x) || std::isinf(x)) + x = 0; + if (std::isnan(y) || std::isinf(y)) + y = 0; + if (std::isnan(z) || std::isinf(z)) + z = 0; + positions.push_back(QVector3D(x, y, z)); + } + + typename CGAL::Surface_mesh::Face_range faceRage = exactMesh->faces(); + typename CGAL::Surface_mesh::Face_range::iterator faceIt; + for (faceIt = faceRage.begin(); faceIt != faceRage.end(); faceIt++) { + CGAL::Vertex_around_face_iterator> vbegin, vend; + std::vector faceIndices; + for (boost::tie(vbegin, vend) = CGAL::vertices_around_face(exactMesh->halfedge(*faceIt), *exactMesh); + vbegin != vend; + ++vbegin){ + faceIndices.push_back(*vbegin); + } + indices.push_back(faceIndices); + } +} + +void *buildCombinableMeshFromVerticesPositionsAndFacesIndices(const std::vector &positions, const std::vector> &indices) +{ + ExactMesh *mesh = nullptr; + if (indices.empty()) + return nullptr; + mesh = buildCgalMesh(positions, indices); + if (nullptr == mesh) { + return mesh; + } + if (CGAL::Polygon_mesh_processing::does_self_intersect(*mesh)) { + qDebug() << "CGAL::Polygon_mesh_processing::does_self_intersect"; + delete mesh; + return nullptr; + } + return (void *)mesh; +} diff --git a/src/meshutil.h b/src/meshutil.h index 82252f3b..7a144a2d 100644 --- a/src/meshutil.h +++ b/src/meshutil.h @@ -21,5 +21,7 @@ void *cloneCombinableMesh(void *mesh); void *convertToCombinableConvexHullMesh(void *meshliteContext, int meshId); void loadMeshVerticesPositions(void *meshliteContext, int meshId, std::vector &positions); void loadCombinableMeshVerticesPositions(void *mesh, std::vector &positions); +void loadCombinableMeshVerticesPositionsAndFacesIndices(void *mesh, std::vector &positions, std::vector> &indices); +void *buildCombinableMeshFromVerticesPositionsAndFacesIndices(const std::vector &positions, const std::vector> &indices); #endif diff --git a/src/parttreewidget.cpp b/src/parttreewidget.cpp index 6253f257..daa1afc9 100644 --- a/src/parttreewidget.cpp +++ b/src/parttreewidget.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "parttreewidget.h" #include "partwidget.h" #include "skeletongraphicswidget.h" @@ -103,6 +104,11 @@ void PartTreeWidget::selectComponent(QUuid componentId, bool multiple) } } +void PartTreeWidget::updateComponentAppearance(QUuid componentId) +{ + updateComponentSelectState(componentId, isComponentSelected(componentId)); +} + void PartTreeWidget::updateComponentSelectState(QUuid componentId, bool selected) { const Component *component = m_document->findComponent(componentId); @@ -114,19 +120,19 @@ void PartTreeWidget::updateComponentSelectState(QUuid componentId, bool selected auto item = m_partItemMap.find(component->linkToPartId); if (item != m_componentItemMap.end()) { PartWidget *widget = (PartWidget *)itemWidget(item->second, 0); + // Unnormal state updating call should be called before check state updating call + widget->updateUnnormalState(component->combineMode != CombineMode::Normal); widget->updateCheckedState(selected); } return; } auto item = m_componentItemMap.find(componentId); if (item != m_componentItemMap.end()) { - if (selected) { - item->second->setFont(0, m_selectedFont); - item->second->setForeground(0, QBrush(Theme::red)); - } else { - item->second->setFont(0, m_normalFont); - item->second->setForeground(0, QBrush(Theme::white)); - } + item->second->setFont(0, selected ? m_selectedFont : m_normalFont); + if (component->combineMode != CombineMode::Normal) + item->second->setForeground(0, selected ? QBrush(Theme::blue) : QBrush(Theme::blue)); + else + item->second->setForeground(0, selected ? QBrush(Theme::red) : QBrush(Theme::white)); } } @@ -254,7 +260,7 @@ void PartTreeWidget::showContextMenu(const QPoint &pos) } else { QLabel *previewLabel = new QLabel; previewLabel->setFixedHeight(Theme::partPreviewImageSize); - previewLabel->setStyleSheet("QLabel {color: " + Theme::red.name() + "}"); + previewLabel->setStyleSheet("QLabel {color: " + (component && (component->combineMode != CombineMode::Normal) ? Theme::blue.name() : Theme::red.name()) + "}"); if (nullptr != component) { previewLabel->setText(component->name); } else if (!componentIds.empty()) { @@ -263,7 +269,31 @@ void PartTreeWidget::showContextMenu(const QPoint &pos) layout->addWidget(previewLabel); } QWidget *widget = new QWidget; - widget->setLayout(layout); + if (nullptr != component) { + QComboBox *combineModeSelectBox = new QComboBox; + for (size_t i = 0; i < (size_t)CombineMode::Count; ++i) { + CombineMode mode = (CombineMode)i; + combineModeSelectBox->addItem(CombineModeToDispName(mode)); + } + combineModeSelectBox->setCurrentIndex((int)component->combineMode); + + connect(combineModeSelectBox, static_cast(&QComboBox::currentIndexChanged), this, [=](int index) { + emit setComponentCombineMode(component->id, (CombineMode)index); + }); + + QHBoxLayout *combineModeLayout = new QHBoxLayout; + combineModeLayout->setAlignment(Qt::AlignCenter); + combineModeLayout->setContentsMargins(0, 0, 0, 0); + combineModeLayout->setSpacing(0); + combineModeLayout->addWidget(combineModeSelectBox); + + QVBoxLayout *newLayout = new QVBoxLayout; + newLayout->addLayout(layout); + newLayout->addLayout(combineModeLayout); + widget->setLayout(newLayout); + } else { + widget->setLayout(layout); + } forDisplayPartImage.setDefaultWidget(widget); if (!componentIds.empty()) { contextMenu.addAction(&forDisplayPartImage); @@ -278,8 +308,6 @@ void PartTreeWidget::showContextMenu(const QPoint &pos) lockAction.setIcon(Theme::awesome()->icon(fa::lock)); QAction unlockAction(tr("Unlock"), this); unlockAction.setIcon(Theme::awesome()->icon(fa::unlock)); - QAction invertAction(tr("Invert"), this); - QAction cancelInverseAction(tr("Cancel Inverse"), this); QAction selectAction(tr("Select"), this); if (nullptr != component && nullptr != part) { @@ -348,6 +376,7 @@ void PartTreeWidget::showContextMenu(const QPoint &pos) contextMenu.addAction(&unlockAction); } + /* if (component && !component->inverse) { connect(&invertAction, &QAction::triggered, [=]() { emit setComponentInverseState(component->id, true); @@ -361,6 +390,7 @@ void PartTreeWidget::showContextMenu(const QPoint &pos) }); contextMenu.addAction(&cancelInverseAction); } + */ contextMenu.addSeparator(); @@ -650,6 +680,11 @@ void PartTreeWidget::componentExpandStateChanged(QUuid componentId) componentItem->second->setExpanded(component->expanded); } +void PartTreeWidget::componentCombineModeChanged(QUuid componentId) +{ + updateComponentAppearance(componentId); +} + void PartTreeWidget::addComponentChildrenToItem(QUuid componentId, QTreeWidgetItem *parentItem) { const Component *parentComponent = m_document->findComponent(componentId); diff --git a/src/parttreewidget.h b/src/parttreewidget.h index 54fef091..7471b97a 100644 --- a/src/parttreewidget.h +++ b/src/parttreewidget.h @@ -33,7 +33,7 @@ signals: void unlockAllComponents(); void setPartLockState(QUuid partId, bool locked); void setPartVisibleState(QUuid partId, bool visible); - void setComponentInverseState(QUuid componentId, bool inverse); + void setComponentCombineMode(QUuid componentId, CombineMode combineMode); void hideDescendantComponents(QUuid componentId); void showDescendantComponents(QUuid componentId); void lockDescendantComponents(QUuid componentId); @@ -49,6 +49,7 @@ public slots: void componentRemoved(QUuid componentId); void componentAdded(QUuid componentId); void componentExpandStateChanged(QUuid componentId); + void componentCombineModeChanged(QUuid componentId); void partRemoved(QUuid partId); void partPreviewChanged(QUuid partid); void partLockStateChanged(QUuid partId); @@ -77,6 +78,7 @@ private: void selectComponent(QUuid componentId, bool multiple=false); QWidget *createSmoothMenuWidget(QUuid componentId); void updateComponentSelectState(QUuid componentId, bool selected); + void updateComponentAppearance(QUuid componentId); bool isComponentSelected(QUuid componentId); private: const Document *m_document = nullptr; diff --git a/src/partwidget.cpp b/src/partwidget.cpp index 724239e2..b6a3e9f7 100644 --- a/src/partwidget.cpp +++ b/src/partwidget.cpp @@ -14,7 +14,8 @@ PartWidget::PartWidget(const Document *document, QUuid partId) : m_document(document), - m_partId(partId) + m_partId(partId), + m_unnormal(false) { QSizePolicy retainSizePolicy = sizePolicy(); retainSizePolicy.setRetainSizeWhenHidden(true); @@ -262,11 +263,20 @@ void PartWidget::updateAllButtons() void PartWidget::updateCheckedState(bool checked) { if (checked) - m_backgroundWidget->setStyleSheet("QWidget#background {border: 1px solid " + Theme::red.name() + ";}"); + m_backgroundWidget->setStyleSheet("QWidget#background {border: 1px solid " + (m_unnormal ? Theme::blue.name() : Theme::red.name()) + ";}"); else m_backgroundWidget->setStyleSheet("QWidget#background {border: 1px solid transparent;}"); } +void PartWidget::updateUnnormalState(bool unnormal) +{ + if (m_unnormal == unnormal) + return; + + m_unnormal = unnormal; + updateAllButtons(); +} + void PartWidget::mouseDoubleClickEvent(QMouseEvent *event) { QWidget::mouseDoubleClickEvent(event); @@ -429,7 +439,7 @@ void PartWidget::initButton(QPushButton *button) void PartWidget::updateButton(QPushButton *button, QChar icon, bool highlighted) { - Theme::updateAwesomeMiniButton(button, icon, highlighted); + Theme::updateAwesomeMiniButton(button, icon, highlighted, m_unnormal); } void PartWidget::updatePreview() diff --git a/src/partwidget.h b/src/partwidget.h index 3641aa1c..f6b63609 100644 --- a/src/partwidget.h +++ b/src/partwidget.h @@ -45,6 +45,7 @@ public: void updateColorButton(); void updateWrapButton(); void updateCheckedState(bool checked); + void updateUnnormalState(bool unnormal); static QSize preferredSize(); ModelWidget *previewWidget(); protected: @@ -55,6 +56,7 @@ public slots: private: // need initialize const Document *m_document; QUuid m_partId; + bool m_unnormal; private: ModelWidget *m_previewWidget; QPushButton *m_visibleButton; diff --git a/src/skeletongraphicswidget.cpp b/src/skeletongraphicswidget.cpp index 78201d31..59ea648c 100644 --- a/src/skeletongraphicswidget.cpp +++ b/src/skeletongraphicswidget.cpp @@ -588,17 +588,21 @@ void SkeletonGraphicsWidget::mouseMoveEvent(QMouseEvent *event) mouseMove(event); } +bool SkeletonGraphicsWidget::inputWheelEventFromOtherWidget(QWheelEvent *event) +{ + if (!wheel(event)) { + if (m_modelWidget && m_modelWidget->inputWheelEventFromOtherWidget(event)) + return true; + } + return true; +} + void SkeletonGraphicsWidget::wheelEvent(QWheelEvent *event) { if (!wheel(event)) { if (m_modelWidget && m_modelWidget->inputWheelEventFromOtherWidget(event)) return; } - - //if (SkeletonDocumentEditMode::ZoomIn == m_document->editMode || - // SkeletonDocumentEditMode::ZoomOut == m_document->editMode || - // SkeletonDocumentEditMode::Drag == m_document->editMode) - // QGraphicsView::wheelEvent(event); } void SkeletonGraphicsWidget::mouseReleaseEvent(QMouseEvent *event) diff --git a/src/skeletongraphicswidget.h b/src/skeletongraphicswidget.h index 38653eba..09fd2b92 100644 --- a/src/skeletongraphicswidget.h +++ b/src/skeletongraphicswidget.h @@ -410,6 +410,7 @@ public: bool hasTwoDisconnectedNodesSelection(); void setModelWidget(ModelWidget *modelWidget); void setNodePositionModifyOnly(bool nodePositionModifyOnly); + bool inputWheelEventFromOtherWidget(QWheelEvent *event); protected: void mouseMoveEvent(QMouseEvent *event) override; void wheelEvent(QWheelEvent *event) override; diff --git a/src/theme.cpp b/src/theme.cpp index 7c21085b..9cd6d7b2 100644 --- a/src/theme.cpp +++ b/src/theme.cpp @@ -103,18 +103,28 @@ void Theme::initAwesomeMiniButton(QPushButton *button) button->setFocusPolicy(Qt::NoFocus); } -void Theme::updateAwesomeMiniButton(QPushButton *button, QChar icon, bool highlighted) +void Theme::updateAwesomeMiniButton(QPushButton *button, QChar icon, bool highlighted, bool unnormal) { button->setText(icon); QColor color; - if (highlighted) - color = QColor("#fc6621"); - else + bool needDesaturation = true; + + if (highlighted) { + if (unnormal) { + color = Theme::blue; + needDesaturation = false; + } else { + color = Theme::red; + } + } else { color = QColor("#525252"); - - color = color.toHsv(); - color.setHsv(color.hue(), color.saturation() / 5, color.value() * 2 / 3); - color = color.toRgb(); + } + + if (needDesaturation) { + color = color.toHsv(); + color.setHsv(color.hue(), color.saturation() / 5, color.value() * 2 / 3); + color = color.toRgb(); + } button->setStyleSheet("QPushButton {border: none; background: none; color: " + color.name() + ";}"); } diff --git a/src/theme.h b/src/theme.h index 30351182..e0867f6c 100644 --- a/src/theme.h +++ b/src/theme.h @@ -47,7 +47,7 @@ public: static void initAwesomeLabel(QLabel *label); static void initAwesomeSmallButton(QPushButton *button); static void initAwesomeMiniButton(QPushButton *button); - static void updateAwesomeMiniButton(QPushButton *button, QChar icon, bool highlighted); + static void updateAwesomeMiniButton(QPushButton *button, QChar icon, bool highlighted, bool unnormal=false); static void initAwesomeToolButton(QPushButton *button); static void initAwesomeToolButtonWithoutFont(QPushButton *button); }; diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll index 1c3971bf..0159aa38 100644 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll and b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll differ diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll.lib b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll.lib index f5f923a5..96d506d2 100644 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll.lib and b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll.lib differ diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll index 7bc81cc9..b21be9e9 100644 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll and b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll differ diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll.lib b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll.lib index af531d81..7b6484f9 100644 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll.lib and b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll.lib differ