Add triangle islands link algorithm
parent
2366bc3f6f
commit
00c05e8cb0
|
@ -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
|
||||
|
|
|
@ -31,7 +31,7 @@ BoneMark AnimalRigger::translateBoneMark(BoneMark boneMark)
|
|||
return boneMark;
|
||||
}
|
||||
|
||||
bool AnimalRigger::collectJontsForChain(int markIndex, std::vector<int> &jointMarkIndices)
|
||||
bool AnimalRigger::collectJointsForChain(int markIndex, std::vector<int> &jointMarkIndices)
|
||||
{
|
||||
const auto &mark = m_marks[markIndex];
|
||||
|
||||
|
@ -484,7 +484,7 @@ bool AnimalRigger::rig()
|
|||
}
|
||||
|
||||
std::vector<int> jointMarkIndices;
|
||||
if (!collectJontsForChain(chainMarkIndex, jointMarkIndices)) {
|
||||
if (!collectJointsForChain(chainMarkIndex, jointMarkIndices)) {
|
||||
m_jointErrorItems.push_back(chainName);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ protected:
|
|||
bool rig() override;
|
||||
BoneMark translateBoneMark(BoneMark boneMark) override;
|
||||
private:
|
||||
bool collectJontsForChain(int markIndex, std::vector<int> &jointMarkIndices);
|
||||
bool collectJointsForChain(int markIndex, std::vector<int> &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);
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
#include <nodemesh/cgalmesh.h>
|
||||
#include <CGAL/convex_hull_3.h>
|
||||
#include <CGAL/Polygon_mesh_processing/corefinement.h>
|
||||
#include <QVector3D>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <cmath>
|
||||
#include <unordered_set>
|
||||
#include <QString>
|
||||
#include "triangleislandslink.h"
|
||||
#include "triangleislandsresolve.h"
|
||||
|
||||
template <class InputIterator, class Kernel>
|
||||
bool precheckForConvexHull(InputIterator first, InputIterator beyond)
|
||||
{
|
||||
typedef typename CGAL::internal::Convex_hull_3::Default_traits_for_Chull_3<typename Kernel::Point_3, typename CGAL::Surface_mesh<typename Kernel::Point_3>>::type Traits;
|
||||
typedef typename Traits::Point_3 Point_3;
|
||||
typedef std::list<Point_3> 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 <class Kernel>
|
||||
typename CGAL::Surface_mesh<typename Kernel::Point_3> *buildConvexCgalMesh(const std::vector<QVector3D> &positions, const std::vector<std::vector<size_t>> &indices)
|
||||
{
|
||||
typename CGAL::Surface_mesh<typename Kernel::Point_3> *mesh = new typename CGAL::Surface_mesh<typename Kernel::Point_3>;
|
||||
std::vector<typename Kernel::Point_3> points;
|
||||
std::unordered_set<size_t> 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<typename std::vector<typename Kernel::Point_3>::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<QVector3D> vertices;
|
||||
std::vector<std::vector<size_t>> faces;
|
||||
fetchFromCgalMesh<CgalKernel>(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<size_t> &group,
|
||||
const QVector3D &point)
|
||||
{
|
||||
float minLength2 = std::numeric_limits<float>::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<QString, std::pair<CgalMesh *, std::vector<size_t>>> &convexMeshes,
|
||||
std::vector<std::pair<size_t, size_t>> &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<size_t> firstGroupTriangleIndices = head.second.second;
|
||||
std::vector<size_t> 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<size_t> 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<std::pair<size_t, size_t>> &links)
|
||||
{
|
||||
std::vector<size_t> group;
|
||||
std::vector<std::vector<size_t>> 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<QVector3D> &positions = outcome.vertices;
|
||||
std::map<QString, std::pair<CgalMesh *, std::vector<size_t>>> convexMeshes;
|
||||
for (size_t islandIndex = 0; islandIndex < islands.size(); ++islandIndex) {
|
||||
const auto &island = islands[islandIndex];
|
||||
std::vector<std::vector<size_t>> indices;
|
||||
for (const auto &triangleIndex: island) {
|
||||
indices.push_back(outcome.triangles[triangleIndex]);
|
||||
}
|
||||
convexMeshes[QString::number(islandIndex)] = std::make_pair(
|
||||
buildConvexCgalMesh<CgalKernel>(positions, indices),
|
||||
island);
|
||||
}
|
||||
while (convexMeshes.size() >= 2) {
|
||||
if (!mergeIntersectedConvexMeshesAndLinkTriangles(outcome,
|
||||
convexMeshes, links))
|
||||
break;
|
||||
}
|
||||
for (auto &it: convexMeshes) {
|
||||
delete it.second.first;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef DUST3D_TRIANGLE_ISLANDS_LINK_H
|
||||
#define DUST3D_TRIANGLE_ISLANDS_LINK_H
|
||||
#include <vector>
|
||||
#include "outcome.h"
|
||||
|
||||
void triangleIslandsLink(const Outcome &outcome,
|
||||
std::vector<std::pair<size_t, size_t>> &links);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,54 @@
|
|||
#include <unordered_set>
|
||||
#include <queue>
|
||||
#include "triangleislandsresolve.h"
|
||||
|
||||
static void buildEdgeToFaceMap(const Outcome &outcome,
|
||||
std::map<std::pair<size_t, size_t>, 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<size_t> &group,
|
||||
std::vector<std::vector<size_t>> &islands)
|
||||
{
|
||||
const std::vector<std::pair<QUuid, QUuid>> *sourceNodes = outcome.triangleSourceNodes();
|
||||
if (nullptr == sourceNodes)
|
||||
return;
|
||||
std::map<std::pair<size_t, size_t>, size_t> edgeToFaceMap;
|
||||
buildEdgeToFaceMap(outcome, edgeToFaceMap);
|
||||
std::unordered_set<size_t> processedFaces;
|
||||
std::queue<size_t> waitFaces;
|
||||
for (const auto &remainingIndex: group) {
|
||||
if (processedFaces.find(remainingIndex) != processedFaces.end())
|
||||
continue;
|
||||
waitFaces.push(remainingIndex);
|
||||
std::vector<size_t> 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef DUST3D_TRIANGLE_ISLANDS_RESOLVE_H
|
||||
#define DUST3D_TRIANGLE_ISLANDS_RESOLVE_H
|
||||
#include <vector>
|
||||
#include "outcome.h"
|
||||
|
||||
void triangleIslandsResolve(const Outcome &outcome,
|
||||
const std::vector<size_t> &group,
|
||||
std::vector<std::vector<size_t>> &islands);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue