dust3d/src/meshresultcontext.cpp

542 lines
22 KiB
C++

#include <map>
#include <vector>
#include <QDebug>
#include <set>
#include <cmath>
#include "theme.h"
#include "meshresultcontext.h"
#include "thekla_atlas.h"
#include "positionmap.h"
#include "nvcore/Debug.h"
struct HalfColorEdge
{
int cornVertexIndex;
std::pair<QUuid, QUuid> source;
};
struct CandidateEdge
{
std::pair<QUuid, QUuid> source;
int fromVertexIndex;
int toVertexIndex;
float dot;
float length;
};
MeshResultContext::MeshResultContext() :
m_triangleSourceResolved(false),
m_triangleColorResolved(false),
m_triangleEdgeSourceMapResolved(false),
m_bmeshNodeMapResolved(false),
m_resultPartsResolved(false),
m_resultTriangleUvsResolved(false),
m_resultRearrangedVerticesResolved(false)
{
}
const std::vector<std::pair<QUuid, QUuid>> &MeshResultContext::triangleSourceNodes()
{
if (!m_triangleSourceResolved) {
m_triangleSourceResolved = true;
calculateTriangleSourceNodes(m_triangleSourceNodes, m_vertexSourceMap);
calculateRemainingVertexSourceNodesAfterTriangleSourceNodesSolved(m_vertexSourceMap);
}
return m_triangleSourceNodes;
}
const std::map<int, std::pair<QUuid, QUuid>> &MeshResultContext::vertexSourceMap()
{
if (!m_triangleSourceResolved) {
m_triangleSourceResolved = true;
calculateTriangleSourceNodes(m_triangleSourceNodes, m_vertexSourceMap);
calculateRemainingVertexSourceNodesAfterTriangleSourceNodesSolved(m_vertexSourceMap);
}
return m_vertexSourceMap;
}
const std::vector<QColor> &MeshResultContext::triangleColors()
{
if (!m_triangleColorResolved) {
calculateTriangleColors(m_triangleColors);
m_triangleColorResolved = true;
}
return m_triangleColors;
}
const std::map<std::pair<int, int>, std::pair<QUuid, QUuid>> &MeshResultContext::triangleEdgeSourceMap()
{
if (!m_triangleEdgeSourceMapResolved) {
calculateTriangleEdgeSourceMap(m_triangleEdgeSourceMap);
m_triangleEdgeSourceMapResolved = true;
}
return m_triangleEdgeSourceMap;
}
const std::map<std::pair<QUuid, QUuid>, BmeshNode *> &MeshResultContext::bmeshNodeMap()
{
if (!m_bmeshNodeMapResolved) {
calculateBmeshNodeMap(m_bmeshNodeMap);
m_bmeshNodeMapResolved = true;
}
return m_bmeshNodeMap;
}
void MeshResultContext::calculateTriangleSourceNodes(std::vector<std::pair<QUuid, QUuid>> &triangleSourceNodes, std::map<int, std::pair<QUuid, QUuid>> &vertexSourceMap)
{
PositionMap<std::pair<QUuid, QUuid>> positionMap;
std::map<std::pair<int, int>, HalfColorEdge> halfColorEdgeMap;
std::set<int> brokenTriangleSet;
for (const auto &it: bmeshVertices) {
positionMap.addPosition(it.position.x(), it.position.y(), it.position.z(),
std::make_pair(it.partId, it.nodeId));
}
for (auto x = 0u; x < vertices.size(); x++) {
ResultVertex *resultVertex = &vertices[x];
std::pair<QUuid, QUuid> source;
if (positionMap.findPosition(resultVertex->position.x(), resultVertex->position.y(), resultVertex->position.z(), &source)) {
vertexSourceMap[x] = source;
}
}
for (auto x = 0u; x < triangles.size(); x++) {
const auto triangle = &triangles[x];
std::vector<std::pair<std::pair<QUuid, QUuid>, int>> colorTypes;
for (int i = 0; i < 3; i++) {
int index = triangle->indicies[i];
const auto &findResult = vertexSourceMap.find(index);
if (findResult != vertexSourceMap.end()) {
std::pair<QUuid, QUuid> source = findResult->second;
bool colorExisted = false;
for (auto j = 0u; j < colorTypes.size(); j++) {
if (colorTypes[j].first == source) {
colorTypes[j].second++;
colorExisted = true;
break;
}
}
if (!colorExisted) {
colorTypes.push_back(std::make_pair(source, 1));
}
}
}
if (colorTypes.empty()) {
//qDebug() << "All vertices of a triangle can't find a color";
triangleSourceNodes.push_back(std::make_pair(QUuid(), QUuid()));
brokenTriangleSet.insert(x);
continue;
}
if (colorTypes.size() != 1 || 3 == colorTypes[0].second) {
std::sort(colorTypes.begin(), colorTypes.end(), [](const std::pair<std::pair<QUuid, QUuid>, int> &a, const std::pair<std::pair<QUuid, QUuid>, int> &b) -> bool {
return a.second > b.second;
});
}
std::pair<QUuid, QUuid> choosenColor = colorTypes[0].first;
triangleSourceNodes.push_back(choosenColor);
for (int i = 0; i < 3; i++) {
int oppositeStartIndex = triangle->indicies[(i + 1) % 3];
int oppositeStopIndex = triangle->indicies[i];
auto oppositePair = std::make_pair(oppositeStartIndex, oppositeStopIndex);
if (halfColorEdgeMap.find(oppositePair) != halfColorEdgeMap.end()) {
halfColorEdgeMap.erase(oppositePair);
continue;
}
auto selfPair = std::make_pair(oppositeStopIndex, oppositeStartIndex);
HalfColorEdge edge;
edge.cornVertexIndex = triangle->indicies[(i + 2) % 3];
edge.source = choosenColor;
halfColorEdgeMap[selfPair] = edge;
}
}
std::map<std::pair<int, int>, int> brokenTriangleMapByEdge;
std::vector<CandidateEdge> candidateEdges;
for (const auto &x: brokenTriangleSet) {
const auto triangle = &triangles[x];
for (int i = 0; i < 3; i++) {
int oppositeStartIndex = triangle->indicies[(i + 1) % 3];
int oppositeStopIndex = triangle->indicies[i];
auto selfPair = std::make_pair(oppositeStopIndex, oppositeStartIndex);
brokenTriangleMapByEdge[selfPair] = x;
auto oppositePair = std::make_pair(oppositeStartIndex, oppositeStopIndex);
const auto &findOpposite = halfColorEdgeMap.find(oppositePair);
if (findOpposite == halfColorEdgeMap.end())
continue;
QVector3D selfPositions[3] = {
vertices[triangle->indicies[i]].position, // A
vertices[triangle->indicies[(i + 1) % 3]].position, // B
vertices[triangle->indicies[(i + 2) % 3]].position // C
};
QVector3D oppositeCornPosition = vertices[findOpposite->second.cornVertexIndex].position; // D
QVector3D AB = selfPositions[1] - selfPositions[0];
float length = AB.length();
QVector3D AC = selfPositions[2] - selfPositions[0];
QVector3D AD = oppositeCornPosition - selfPositions[0];
AB.normalize();
AC.normalize();
AD.normalize();
QVector3D ABxAC = QVector3D::crossProduct(AB, AC);
QVector3D ADxAB = QVector3D::crossProduct(AD, AB);
ABxAC.normalize();
ADxAB.normalize();
float dot = QVector3D::dotProduct(ABxAC, ADxAB);
//qDebug() << "dot:" << dot;
CandidateEdge candidate;
candidate.dot = dot;
candidate.length = length;
candidate.fromVertexIndex = triangle->indicies[i];
candidate.toVertexIndex = triangle->indicies[(i + 1) % 3];
candidate.source = findOpposite->second.source;
candidateEdges.push_back(candidate);
}
}
if (candidateEdges.empty())
return;
std::sort(candidateEdges.begin(), candidateEdges.end(), [](const CandidateEdge &a, const CandidateEdge &b) -> bool {
if (a.dot > b.dot)
return true;
else if (a.dot < b.dot)
return false;
return a.length > b.length;
});
for (auto cand = 0u; cand < candidateEdges.size(); cand++) {
const auto &candidate = candidateEdges[cand];
if (brokenTriangleSet.empty())
break;
//qDebug() << "candidate dot[" << cand << "]:" << candidate.dot;
std::vector<std::pair<int, int>> toResolvePairs;
toResolvePairs.push_back(std::make_pair(candidate.fromVertexIndex, candidate.toVertexIndex));
for (auto order = 0u; order < toResolvePairs.size(); order++) {
const auto &findTriangle = brokenTriangleMapByEdge.find(toResolvePairs[order]);
if (findTriangle == brokenTriangleMapByEdge.end())
continue;
int x = findTriangle->second;
if (brokenTriangleSet.find(x) == brokenTriangleSet.end())
continue;
brokenTriangleSet.erase(x);
triangleSourceNodes[x] = candidate.source;
//qDebug() << "resolved triangle:" << x;
const auto triangle = &triangles[x];
for (int i = 0; i < 3; i++) {
int oppositeStartIndex = triangle->indicies[(i + 1) % 3];
int oppositeStopIndex = triangle->indicies[i];
auto oppositePair = std::make_pair(oppositeStartIndex, oppositeStopIndex);
toResolvePairs.push_back(oppositePair);
}
}
}
}
void MeshResultContext::calculateRemainingVertexSourceNodesAfterTriangleSourceNodesSolved(std::map<int, std::pair<QUuid, QUuid>> &vertexSourceMap)
{
std::map<int, std::set<std::pair<QUuid, QUuid>>> remainings;
for (auto x = 0u; x < triangles.size(); x++) {
const auto triangle = &triangles[x];
for (int i = 0; i < 3; i++) {
int index = triangle->indicies[i];
const auto &findResult = vertexSourceMap.find(index);
if (findResult == vertexSourceMap.end()) {
remainings[index].insert(triangleSourceNodes()[x]);
}
}
}
for (const auto &it: remainings) {
float minDist2 = 100000000;
std::pair<QUuid, QUuid> minSource = std::make_pair(QUuid(), QUuid());
for (const auto &source: it.second) {
const auto &vertex = vertices[it.first];
const auto &findNode = bmeshNodeMap().find(source);
if (findNode != bmeshNodeMap().end()) {
float dist2 = (vertex.position - findNode->second->origin).lengthSquared();
if (dist2 < minDist2) {
minSource = source;
minDist2 = dist2;
}
}
}
vertexSourceMap[it.first] = minSource;
}
}
void MeshResultContext::calculateTriangleColors(std::vector<QColor> &triangleColors)
{
std::map<std::pair<QUuid, QUuid>, QColor> nodeColorMap;
for (const auto &it: bmeshNodes) {
nodeColorMap[std::make_pair(it.partId, it.nodeId)] = it.color;
}
const auto sourceNodes = triangleSourceNodes();
for (const auto &it: sourceNodes) {
triangleColors.push_back(nodeColorMap[it]);
}
}
void MeshResultContext::calculateTriangleEdgeSourceMap(std::map<std::pair<int, int>, std::pair<QUuid, QUuid>> &triangleEdgeSourceMap)
{
const std::vector<std::pair<QUuid, QUuid>> sourceNodes = triangleSourceNodes();
for (auto x = 0u; x < triangles.size(); x++) {
const auto triangle = &triangles[x];
for (int i = 0; i < 3; i++) {
int startIndex = triangle->indicies[i];
int stopIndex = triangle->indicies[(i + 1) % 3];
triangleEdgeSourceMap[std::make_pair(startIndex, stopIndex)] = sourceNodes[x];
}
}
}
void MeshResultContext::calculateBmeshNodeMap(std::map<std::pair<QUuid, QUuid>, BmeshNode *> &bmeshNodeMap) {
for (auto i = 0u; i < bmeshNodes.size(); i++) {
BmeshNode *bmeshNode = &bmeshNodes[i];
bmeshNodeMap[std::make_pair(bmeshNode->partId, bmeshNode->nodeId)] = bmeshNode;
}
}
struct BmeshNodeDistWithWorldCenter
{
BmeshNode *bmeshNode;
float dist2;
};
const std::map<QUuid, ResultPart> &MeshResultContext::parts()
{
if (!m_resultPartsResolved) {
calculateResultParts(m_resultParts);
m_resultPartsResolved = true;
}
return m_resultParts;
}
const std::vector<ResultTriangleUv> &MeshResultContext::triangleUvs()
{
if (!m_resultTriangleUvsResolved) {
calculateResultTriangleUvs(m_resultTriangleUvs, m_seamVertices);
m_resultTriangleUvsResolved = true;
}
return m_resultTriangleUvs;
}
void MeshResultContext::calculateResultParts(std::map<QUuid, ResultPart> &parts)
{
std::map<std::pair<QUuid, int>, int> oldVertexToNewMap;
for (auto x = 0u; x < triangles.size(); x++) {
const auto &triangle = triangles[x];
const auto &sourceNode = triangleSourceNodes()[x];
auto it = parts.find(sourceNode.first);
if (it == parts.end()) {
ResultPart newPart;
newPart.color = triangleColors()[x];
parts.insert(std::make_pair(sourceNode.first, newPart));
}
auto &resultPart = parts[sourceNode.first];
ResultTriangle newTriangle;
newTriangle.normal = triangle.normal;
for (auto i = 0u; i < 3; i++) {
auto key = std::make_pair(sourceNode.first, triangle.indicies[i]);
const auto &it = oldVertexToNewMap.find(key);
bool isNewVertex = it == oldVertexToNewMap.end();
bool isSeamVertex = m_seamVertices.end() != m_seamVertices.find(triangle.indicies[i]);
if (isNewVertex || isSeamVertex) {
int newIndex = resultPart.vertices.size();
resultPart.interpolatedVertexNormals.push_back(newTriangle.normal);
resultPart.verticesOldIndicies.push_back(triangle.indicies[i]);
resultPart.vertices.push_back(vertices[triangle.indicies[i]]);
ResultVertexUv vertexUv;
vertexUv.uv[0] = triangleUvs()[x].uv[i][0];
vertexUv.uv[1] = triangleUvs()[x].uv[i][1];
resultPart.vertexUvs.push_back(vertexUv);
if (isNewVertex && !isSeamVertex)
oldVertexToNewMap.insert(std::make_pair(key, newIndex));
newTriangle.indicies[i] = newIndex;
} else {
newTriangle.indicies[i] = it->second;
resultPart.interpolatedVertexNormals[it->second] += newTriangle.normal;
}
}
resultPart.triangles.push_back(newTriangle);
resultPart.uvs.push_back(triangleUvs()[x]);
}
for (auto &partIt: parts) {
for (auto &normalIt: partIt.second.interpolatedVertexNormals) {
normalIt.normalize();
}
}
}
void MeshResultContext::calculateResultTriangleUvs(std::vector<ResultTriangleUv> &uvs, std::set<int> &seamVertices)
{
using namespace Thekla;
const std::vector<ResultRearrangedVertex> &choosenVertices = rearrangedVertices();
const std::vector<ResultRearrangedTriangle> &choosenTriangles = rearrangedTriangles();
Atlas_Input_Mesh inputMesh;
using namespace nv;
class NvAssertHandler : public nv::AssertHandler {
virtual int assertion(const char *exp, const char *file, int line, const char *func, const char *msg, va_list arg)
{
qDebug() << "Something bad happended inside nvMesh:" << exp << "file:" << file << "line:" << line << "msg:" << msg;
return NV_ABORT_IGNORE;
};
};
NvAssertHandler assertHandler;
nv::debug::setAssertHandler(&assertHandler);
inputMesh.vertex_count = choosenVertices.size();
inputMesh.vertex_array = new Atlas_Input_Vertex[inputMesh.vertex_count];
inputMesh.face_count = choosenTriangles.size();
inputMesh.face_array = new Atlas_Input_Face[inputMesh.face_count];
for (auto i = 0; i < inputMesh.vertex_count; i++) {
const ResultRearrangedVertex *src = &choosenVertices[i];
Atlas_Input_Vertex *dest = &inputMesh.vertex_array[i];
dest->position[0] = src->position.x();
dest->position[1] = src->position.y();
dest->position[2] = src->position.z();
dest->normal[0] = 0;
dest->normal[1] = 0;
dest->normal[2] = 0;
dest->uv[0] = 0;
dest->uv[1] = 0;
dest->first_colocal = i;
}
std::map<std::pair<int, int>, int> edgeToFaceIndexMap;
std::map<QUuid, int> materialIndexMap;
for (auto i = 0; i < inputMesh.face_count; i++) {
const ResultRearrangedTriangle *src = &choosenTriangles[i];
Atlas_Input_Face *dest = &inputMesh.face_array[i];
auto &materialKey = triangleSourceNodes()[src->originalIndex].first;
auto findMaterialResult = materialIndexMap.find(materialKey);
int materialIndex = 0;
if (findMaterialResult == materialIndexMap.end()) {
materialIndex = materialIndexMap.size() + 1;
materialIndexMap[materialKey] = materialIndex;
} else {
materialIndex = findMaterialResult->second;
}
dest->material_index = materialIndex;
dest->vertex_index[0] = src->indicies[0];
dest->vertex_index[1] = src->indicies[1];
dest->vertex_index[2] = src->indicies[2];
edgeToFaceIndexMap[std::make_pair(src->indicies[0], src->indicies[1])] = src->originalIndex;
edgeToFaceIndexMap[std::make_pair(src->indicies[1], src->indicies[2])] = src->originalIndex;
edgeToFaceIndexMap[std::make_pair(src->indicies[2], src->indicies[0])] = src->originalIndex;
for (auto j = 0; j < 3; j++) {
Atlas_Input_Vertex *vertex = &inputMesh.vertex_array[src->indicies[j]];
vertex->normal[0] += src->normal.x();
vertex->normal[1] += src->normal.y();
vertex->normal[2] += src->normal.z();
}
}
for (auto i = 0; i < inputMesh.vertex_count; i++) {
Atlas_Input_Vertex *dest = &inputMesh.vertex_array[i];
QVector3D normal(dest->normal[0], dest->normal[1], dest->normal[2]);
normal.normalize();
dest->normal[0] = normal.x();
dest->normal[1] = normal.y();
dest->normal[2] = normal.z();
}
Atlas_Options atlasOptions;
atlas_set_default_options(&atlasOptions);
atlasOptions.packer_options.witness.packing_quality = 4;
Atlas_Error error = Atlas_Error_Success;
Atlas_Output_Mesh *outputMesh = atlas_generate(&inputMesh, &atlasOptions, &error);
PositionMap<int> uvPositionAndIndexMap;
uvs.resize(triangles.size());
std::set<int> refs;
for (auto i = 0; i < outputMesh->index_count; i += 3) {
Atlas_Output_Vertex *outputVertices[] = {
&outputMesh->vertex_array[outputMesh->index_array[i + 0]],
&outputMesh->vertex_array[outputMesh->index_array[i + 1]],
&outputMesh->vertex_array[outputMesh->index_array[i + 2]]
};
int faceIndex = edgeToFaceIndexMap[std::make_pair(outputVertices[0]->xref, outputVertices[1]->xref)];
//Q_ASSERT(faceIndex == edgeToFaceIndexMap[std::make_pair(outputVertices[1]->xref, outputVertices[2]->xref)]);
//Q_ASSERT(faceIndex == edgeToFaceIndexMap[std::make_pair(outputVertices[2]->xref, outputVertices[0]->xref)]);
int firstIndex = 0;
for (auto j = 0; j < 3; j++) {
if (choosenVertices[outputVertices[0]->xref].originalIndex == triangles[faceIndex].indicies[j]) {
firstIndex = j;
break;
}
}
for (auto j = 0; j < 3; j++) {
Atlas_Output_Vertex *from = outputVertices[j];
ResultTriangleUv *to = &uvs[faceIndex];
to->resolved = true;
int toIndex = (firstIndex + j) % 3;
to->uv[toIndex][0] = (float)from->uv[0] / outputMesh->atlas_width;
to->uv[toIndex][1] = (float)from->uv[1] / outputMesh->atlas_height;
int originalRef = choosenVertices[from->xref].originalIndex;
if (refs.find(originalRef) == refs.end()) {
refs.insert(originalRef);
uvPositionAndIndexMap.addPosition(from->uv[0], from->uv[1], (float)originalRef, 0);
} else {
if (!uvPositionAndIndexMap.findPosition(from->uv[0], from->uv[1], (float)originalRef, nullptr)) {
seamVertices.insert(originalRef);
}
}
}
}
int unresolvedUvFaceCount = 0;
for (auto i = 0u; i < uvs.size(); i++) {
ResultTriangleUv *uv = &uvs[i];
if (!uv->resolved) {
unresolvedUvFaceCount++;
}
}
qDebug() << "unresolvedUvFaceCount:" << unresolvedUvFaceCount;
atlas_free(outputMesh);
}
const std::vector<ResultRearrangedVertex> &MeshResultContext::rearrangedVertices()
{
if (!m_resultRearrangedVerticesResolved) {
calculateResultRearrangedVertices(m_rearrangedVertices, m_rearrangedTriangles);
m_resultRearrangedVerticesResolved = true;
}
return m_rearrangedVertices;
}
const std::vector<ResultRearrangedTriangle> &MeshResultContext::rearrangedTriangles()
{
if (!m_resultRearrangedVerticesResolved) {
calculateResultRearrangedVertices(m_rearrangedVertices, m_rearrangedTriangles);
m_resultRearrangedVerticesResolved = true;
}
return m_rearrangedTriangles;
}
void MeshResultContext::calculateResultRearrangedVertices(std::vector<ResultRearrangedVertex> &rearrangedVertices, std::vector<ResultRearrangedTriangle> &rearrangedTriangles)
{
std::map<std::pair<QUuid, int>, int> oldVertexToNewMap;
rearrangedVertices.clear();
rearrangedTriangles.clear();
for (auto x = 0u; x < triangles.size(); x++) {
const auto &triangle = triangles[x];
const auto &sourceNode = triangleSourceNodes()[x];
ResultRearrangedTriangle newTriangle;
newTriangle.normal = triangle.normal;
newTriangle.originalIndex = x;
for (auto i = 0u; i < 3; i++) {
auto key = std::make_pair(sourceNode.first, triangle.indicies[i]);
const auto &it = oldVertexToNewMap.find(key);
if (it == oldVertexToNewMap.end()) {
ResultRearrangedVertex rearrangedVertex;
rearrangedVertex.originalIndex = triangle.indicies[i];
rearrangedVertex.position = vertices[triangle.indicies[i]].position;
int newIndex = rearrangedVertices.size();
rearrangedVertices.push_back(rearrangedVertex);
oldVertexToNewMap.insert(std::make_pair(key, newIndex));
newTriangle.indicies[i] = newIndex;
} else {
newTriangle.indicies[i] = it->second;
}
}
rearrangedTriangles.push_back(newTriangle);
}
}