Add triangle islands link algorithm

master
Jeremy Hu 2019-09-30 23:58:04 +09:30
parent 2366bc3f6f
commit 00c05e8cb0
7 changed files with 317 additions and 3 deletions

View File

@ -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

View File

@ -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);
}

View File

@ -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);

234
src/triangleislandslink.cpp Normal file
View File

@ -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 &center)
{
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;
}
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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