diff --git a/dust3d.pro b/dust3d.pro index da278bb8..caabab42 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -419,6 +419,12 @@ HEADERS += src/proceduralanimation.h SOURCES += src/boundingboxmesh.cpp HEADERS += src/boundingboxmesh.h +SOURCES += src/triangleislandsresolve.cpp +HEADERS += src/triangleislandsresolve.h + +SOURCES += src/triangleislandslink.cpp +HEADERS += src/triangleislandslink.h + SOURCES += src/main.cpp HEADERS += src/version.h diff --git a/src/animalrigger.cpp b/src/animalrigger.cpp index 81ab6804..90ada6ac 100644 --- a/src/animalrigger.cpp +++ b/src/animalrigger.cpp @@ -31,7 +31,7 @@ BoneMark AnimalRigger::translateBoneMark(BoneMark boneMark) return boneMark; } -bool AnimalRigger::collectJontsForChain(int markIndex, std::vector &jointMarkIndices) +bool AnimalRigger::collectJointsForChain(int markIndex, std::vector &jointMarkIndices) { const auto &mark = m_marks[markIndex]; @@ -484,7 +484,7 @@ bool AnimalRigger::rig() } std::vector jointMarkIndices; - if (!collectJontsForChain(chainMarkIndex, jointMarkIndices)) { + if (!collectJointsForChain(chainMarkIndex, jointMarkIndices)) { m_jointErrorItems.push_back(chainName); } diff --git a/src/animalrigger.h b/src/animalrigger.h index 3d2acc00..0ebf8e7a 100644 --- a/src/animalrigger.h +++ b/src/animalrigger.h @@ -18,7 +18,7 @@ protected: bool rig() override; BoneMark translateBoneMark(BoneMark boneMark) override; private: - bool collectJontsForChain(int markIndex, std::vector &jointMarkIndices); + bool collectJointsForChain(int markIndex, std::vector &jointMarkIndices); static QString namingSpine(int spineOrder, bool hasTail); static QString namingConnector(const QString &spineName, const QString &chainName); static QString namingChain(const QString &baseName, SkeletonSide side, int orderInSide, int totalInSide, int jointOrder); diff --git a/src/triangleislandslink.cpp b/src/triangleislandslink.cpp new file mode 100644 index 00000000..6ce74e08 --- /dev/null +++ b/src/triangleislandslink.cpp @@ -0,0 +1,234 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "triangleislandslink.h" +#include "triangleislandsresolve.h" + +template +bool precheckForConvexHull(InputIterator first, InputIterator beyond) +{ + typedef typename CGAL::internal::Convex_hull_3::Default_traits_for_Chull_3>::type Traits; + typedef typename Traits::Point_3 Point_3; + typedef std::list Point_3_list; + typedef typename Point_3_list::iterator P3_iterator; + Traits traits; + + Point_3_list points(first, beyond); + if (!(points.size() > 3)) + return false; + + // at least 4 points + typename Traits::Collinear_3 collinear = traits.collinear_3_object(); + typename Traits::Equal_3 equal = traits.equal_3_object(); + + P3_iterator point1_it = points.begin(); + P3_iterator point2_it = points.begin(); + point2_it++; + + // find three that are not collinear + while (point2_it != points.end() && equal(*point1_it,*point2_it)) + ++point2_it; + + if (!(point2_it != points.end())) + return false; + + P3_iterator point3_it = point2_it; + ++point3_it; + + if (!(point3_it != points.end())) + return false; + + while (point3_it != points.end() && collinear(*point1_it,*point2_it,*point3_it)) + ++point3_it; + + if (!(point3_it != points.end())) + return false; + + return true; +} + +template +typename CGAL::Surface_mesh *buildConvexCgalMesh(const std::vector &positions, const std::vector> &indices) +{ + typename CGAL::Surface_mesh *mesh = new typename CGAL::Surface_mesh; + std::vector points; + std::unordered_set addedIndices; + for (const auto &face: indices) { + for (const auto &index: face) { + if (addedIndices.find(index) != addedIndices.end()) + continue; + addedIndices.insert(index); + const auto &position = positions[index]; + points.push_back(typename Kernel::Point_3(position.x(), position.y(), position.z())); + } + } + try { + if (precheckForConvexHull::iterator, Kernel>(points.begin(), points.end())) { + CGAL::convex_hull_3(points.begin(), points.end(), *mesh); + } else { + delete mesh; + return nullptr; + } + } catch (...) { + delete mesh; + return nullptr; + } + return mesh; +} + +static CgalMesh *doIntersection(CgalMesh *firstCgalMesh, CgalMesh *secondCgalMesh) +{ + if (nullptr == firstCgalMesh || nullptr == secondCgalMesh) + return nullptr; + CgalMesh *resultCgalMesh = new CgalMesh; + try { + if (!CGAL::Polygon_mesh_processing::corefine_and_compute_intersection(*firstCgalMesh, *secondCgalMesh, *resultCgalMesh)) { + delete resultCgalMesh; + resultCgalMesh = nullptr; + } + } catch (...) { + delete resultCgalMesh; + resultCgalMesh = nullptr; + } + return resultCgalMesh; +} + +static CgalMesh *doUnion(CgalMesh *firstCgalMesh, CgalMesh *secondCgalMesh) +{ + if (nullptr == firstCgalMesh || nullptr == secondCgalMesh) + return nullptr; + CgalMesh *resultCgalMesh = new CgalMesh; + try { + if (!CGAL::Polygon_mesh_processing::corefine_and_compute_union(*firstCgalMesh, *secondCgalMesh, *resultCgalMesh)) { + delete resultCgalMesh; + resultCgalMesh = nullptr; + } + } catch (...) { + delete resultCgalMesh; + resultCgalMesh = nullptr; + } + return resultCgalMesh; +} + +static bool fetchCgalMeshCenter(CgalMesh *cgalMesh, QVector3D ¢er) +{ + if (nullptr == cgalMesh) + return false; + + std::vector vertices; + std::vector> faces; + fetchFromCgalMesh(cgalMesh, vertices, faces); + if (vertices.empty() || faces.empty()) + return false; + + QVector3D sumOfPositions; + for (const auto &face: faces) { + QVector3D sumOfTrianglePositions; + for (const auto &vertex: face) { + sumOfTrianglePositions += vertices[vertex]; + } + sumOfPositions += sumOfTrianglePositions / face.size(); + } + center = sumOfPositions / faces.size(); + + return true; +} + +static size_t findNearestTriangle(const Outcome &outcome, + const std::vector &group, + const QVector3D &point) +{ + float minLength2 = std::numeric_limits::max(); + size_t choosenIndex = 0; + for (const auto &triangleIndex: group) { + QVector3D sumOfPositions; + const auto &indices = outcome.triangles[triangleIndex]; + for (const auto &it: indices) { + sumOfPositions += outcome.vertices[it]; + } + QVector3D triangleCenter = sumOfPositions / indices.size(); + float length2 = (point - triangleCenter).lengthSquared(); + if (length2 < minLength2) { + minLength2 = length2; + choosenIndex = triangleIndex; + } + } + return choosenIndex; +} + +static bool mergeIntersectedConvexMeshesAndLinkTriangles(const Outcome &outcome, + std::map>> &convexMeshes, + std::vector> &links) +{ + if (convexMeshes.size() <= 1) + return false; + auto head = *convexMeshes.begin(); + for (const auto &it: convexMeshes) { + if (it.first == head.first) + continue; + CgalMesh *intersectionMesh = doIntersection(head.second.first, it.second.first); + QVector3D center; + bool fetchSuccess = fetchCgalMeshCenter(intersectionMesh, center); + delete intersectionMesh; + if (fetchSuccess) { + QString firstGroupName = head.first; + QString secondGroupName = it.first; + std::vector firstGroupTriangleIndices = head.second.second; + std::vector secondGroupTriangleIndices = it.second.second; + size_t firstGroupChoosenIndex = findNearestTriangle(outcome, firstGroupTriangleIndices, center); + size_t secondGroupChoosenIndex = findNearestTriangle(outcome, secondGroupTriangleIndices, center); + links.push_back(std::make_pair(firstGroupChoosenIndex, secondGroupChoosenIndex)); + std::vector triangleIndices(firstGroupTriangleIndices); + triangleIndices.insert(triangleIndices.end(), secondGroupTriangleIndices.begin(), secondGroupTriangleIndices.end()); + CgalMesh *unionMesh = doUnion(head.second.first, it.second.first); + delete head.second.first; + delete it.second.first; + convexMeshes.erase(firstGroupName); + convexMeshes.erase(secondGroupName); + convexMeshes[firstGroupName + "+" + secondGroupName] = std::make_pair( + unionMesh, + triangleIndices); + return true; + } + } + return false; +} + +void triangleIslandsLink(const Outcome &outcome, + std::vector> &links) +{ + std::vector group; + std::vector> islands; + size_t triangleCount = outcome.triangles.size(); + for (size_t i = 0; i < triangleCount; ++i) + group.push_back(i); + triangleIslandsResolve(outcome, group, islands); + if (islands.size() <= 2) + return; + const std::vector &positions = outcome.vertices; + std::map>> convexMeshes; + for (size_t islandIndex = 0; islandIndex < islands.size(); ++islandIndex) { + const auto &island = islands[islandIndex]; + std::vector> indices; + for (const auto &triangleIndex: island) { + indices.push_back(outcome.triangles[triangleIndex]); + } + convexMeshes[QString::number(islandIndex)] = std::make_pair( + buildConvexCgalMesh(positions, indices), + island); + } + while (convexMeshes.size() >= 2) { + if (!mergeIntersectedConvexMeshesAndLinkTriangles(outcome, + convexMeshes, links)) + break; + } + for (auto &it: convexMeshes) { + delete it.second.first; + } +} diff --git a/src/triangleislandslink.h b/src/triangleislandslink.h new file mode 100644 index 00000000..80ad2054 --- /dev/null +++ b/src/triangleislandslink.h @@ -0,0 +1,9 @@ +#ifndef DUST3D_TRIANGLE_ISLANDS_LINK_H +#define DUST3D_TRIANGLE_ISLANDS_LINK_H +#include +#include "outcome.h" + +void triangleIslandsLink(const Outcome &outcome, + std::vector> &links); + +#endif diff --git a/src/triangleislandsresolve.cpp b/src/triangleislandsresolve.cpp new file mode 100644 index 00000000..82583fe6 --- /dev/null +++ b/src/triangleislandsresolve.cpp @@ -0,0 +1,54 @@ +#include +#include +#include "triangleislandsresolve.h" + +static void buildEdgeToFaceMap(const Outcome &outcome, + std::map, size_t> &edgeToFaceMap) +{ + edgeToFaceMap.clear(); + for (size_t index = 0; index < outcome.triangles.size(); ++index) { + const auto &indices = outcome.triangles[index]; + for (size_t i = 0; i < 3; i++) { + size_t j = (i + 1) % 3; + edgeToFaceMap[{indices[i], indices[j]}] = index; + } + } +} + +void triangleIslandsResolve(const Outcome &outcome, + const std::vector &group, + std::vector> &islands) +{ + const std::vector> *sourceNodes = outcome.triangleSourceNodes(); + if (nullptr == sourceNodes) + return; + std::map, size_t> edgeToFaceMap; + buildEdgeToFaceMap(outcome, edgeToFaceMap); + std::unordered_set processedFaces; + std::queue waitFaces; + for (const auto &remainingIndex: group) { + if (processedFaces.find(remainingIndex) != processedFaces.end()) + continue; + waitFaces.push(remainingIndex); + std::vector island; + while (!waitFaces.empty()) { + size_t index = waitFaces.front(); + waitFaces.pop(); + if (processedFaces.find(index) != processedFaces.end()) + continue; + const auto &indices = outcome.triangles[index]; + for (size_t i = 0; i < 3; i++) { + size_t j = (i + 1) % 3; + auto findOppositeFaceResult = edgeToFaceMap.find({indices[j], indices[i]}); + if (findOppositeFaceResult == edgeToFaceMap.end()) + continue; + waitFaces.push(findOppositeFaceResult->second); + } + island.push_back(index); + processedFaces.insert(index); + } + if (island.empty()) + continue; + islands.push_back(island); + } +} diff --git a/src/triangleislandsresolve.h b/src/triangleislandsresolve.h new file mode 100644 index 00000000..6d9c63fe --- /dev/null +++ b/src/triangleislandsresolve.h @@ -0,0 +1,11 @@ +#ifndef DUST3D_TRIANGLE_ISLANDS_RESOLVE_H +#define DUST3D_TRIANGLE_ISLANDS_RESOLVE_H +#include +#include "outcome.h" + +void triangleIslandsResolve(const Outcome &outcome, + const std::vector &group, + std::vector> &islands); + +#endif +