diff --git a/dust3d.pro b/dust3d.pro index 1a2cf6cc..a83c7a74 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -294,6 +294,9 @@ 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.h b/src/combinemode.h index 95ff2a52..9ec4a328 100644 --- a/src/combinemode.h +++ b/src/combinemode.h @@ -6,6 +6,7 @@ enum class CombineMode { Normal = 0, Inversion, + Inflation, Count }; CombineMode CombineModeFromString(const char *modeString); @@ -17,6 +18,8 @@ CombineMode CombineModeFromString(const char *modeString) \ return CombineMode::Normal; \ if (mode == "Inversion") \ return CombineMode::Inversion; \ + if (mode == "Inflation") \ + return CombineMode::Inflation; \ return CombineMode::Normal; \ } const char *CombineModeToString(CombineMode mode); @@ -28,6 +31,8 @@ const char *CombineModeToString(CombineMode mode) \ return "Normal"; \ case CombineMode::Inversion: \ return "Inversion"; \ + case CombineMode::Inflation: \ + return "Inflation"; \ default: \ return "Normal"; \ } \ @@ -41,6 +46,8 @@ QString CombineModeToDispName(CombineMode mode) \ return QObject::tr("Normal"); \ case CombineMode::Inversion: \ return QObject::tr("Inversion"); \ + case CombineMode::Inflation: \ + return QObject::tr("Inflation"); \ default: \ return QObject::tr("Normal"); \ } \ diff --git a/src/meshgenerator.cpp b/src/meshgenerator.cpp index d7727e65..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,16 +522,26 @@ void *MeshGenerator::combineComponentMesh(QString componentId, bool *inverse) component = &findComponent->second; } - CombineMode combineMode = CombineModeFromString(valueOfKeyInMapOrEmpty(*component, "combineMode").toUtf8().constData()); - if (combineMode == CombineMode::Inversion) - *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); + } } } @@ -553,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); } @@ -573,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); @@ -644,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(); @@ -713,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); @@ -733,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