2019-02-18 12:57:18 +00:00
|
|
|
#include <QDebug>
|
2018-08-27 08:50:40 +00:00
|
|
|
#include <QElapsedTimer>
|
2019-02-18 12:57:18 +00:00
|
|
|
#include <QVector2D>
|
|
|
|
#include <QGuiApplication>
|
|
|
|
#include <nodemesh/builder.h>
|
|
|
|
#include <nodemesh/modifier.h>
|
2019-02-19 11:21:12 +00:00
|
|
|
#include <nodemesh/misc.h>
|
2019-02-18 12:57:18 +00:00
|
|
|
#include <nodemesh/recombiner.h>
|
2018-04-07 08:44:39 +00:00
|
|
|
#include "meshgenerator.h"
|
2018-10-25 00:19:38 +00:00
|
|
|
#include "util.h"
|
2018-10-26 23:04:45 +00:00
|
|
|
#include "trianglesourcenoderesolve.h"
|
2018-08-27 08:50:40 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
const std::vector<QVector2D> g_defaultCutTemplate = {
|
|
|
|
{-1.0, -1.0},
|
|
|
|
{ 1.0, -1.0},
|
|
|
|
{ 1.0, 1.0},
|
|
|
|
{-1.0, 1.0},
|
|
|
|
};
|
2018-04-11 06:15:11 +00:00
|
|
|
|
2018-10-25 00:19:38 +00:00
|
|
|
MeshGenerator::MeshGenerator(Snapshot *snapshot) :
|
2019-02-18 12:57:18 +00:00
|
|
|
m_snapshot(snapshot)
|
2018-04-07 08:44:39 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
MeshGenerator::~MeshGenerator()
|
|
|
|
{
|
2019-02-18 12:57:18 +00:00
|
|
|
for (auto &it: m_partPreviewMeshes)
|
|
|
|
delete it.second;
|
|
|
|
delete m_resultMesh;
|
2018-04-07 08:44:39 +00:00
|
|
|
delete m_snapshot;
|
2018-10-25 00:19:38 +00:00
|
|
|
delete m_outcome;
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
|
|
|
|
2019-01-07 13:03:42 +00:00
|
|
|
bool MeshGenerator::isSucceed()
|
|
|
|
{
|
|
|
|
return m_isSucceed;
|
|
|
|
}
|
|
|
|
|
2018-05-07 17:16:58 +00:00
|
|
|
MeshLoader *MeshGenerator::takeResultMesh()
|
2018-04-07 08:44:39 +00:00
|
|
|
{
|
2019-02-18 12:57:18 +00:00
|
|
|
MeshLoader *resultMesh = m_resultMesh;
|
|
|
|
m_resultMesh = nullptr;
|
2018-04-07 08:44:39 +00:00
|
|
|
return resultMesh;
|
|
|
|
}
|
|
|
|
|
2018-09-18 06:22:29 +00:00
|
|
|
MeshLoader *MeshGenerator::takePartPreviewMesh(const QUuid &partId)
|
2018-04-07 08:44:39 +00:00
|
|
|
{
|
2019-02-18 12:57:18 +00:00
|
|
|
MeshLoader *resultMesh = m_partPreviewMeshes[partId];
|
|
|
|
m_partPreviewMeshes[partId] = nullptr;
|
2018-09-18 06:22:29 +00:00
|
|
|
return resultMesh;
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
|
|
|
|
2018-09-18 06:22:29 +00:00
|
|
|
const std::set<QUuid> &MeshGenerator::generatedPreviewPartIds()
|
|
|
|
{
|
|
|
|
return m_generatedPreviewPartIds;
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
|
|
|
|
2018-10-26 23:04:45 +00:00
|
|
|
Outcome *MeshGenerator::takeOutcome()
|
2018-04-26 02:23:22 +00:00
|
|
|
{
|
2018-10-25 00:19:38 +00:00
|
|
|
Outcome *outcome = m_outcome;
|
|
|
|
m_outcome = nullptr;
|
|
|
|
return outcome;
|
2018-04-26 02:23:22 +00:00
|
|
|
}
|
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
void MeshGenerator::collectParts()
|
2018-04-07 08:44:39 +00:00
|
|
|
{
|
2018-08-27 08:50:40 +00:00
|
|
|
for (const auto &node: m_snapshot->nodes) {
|
|
|
|
QString partId = valueOfKeyInMapOrEmpty(node.second, "partId");
|
|
|
|
if (partId.isEmpty())
|
|
|
|
continue;
|
|
|
|
m_partNodeIds[partId].insert(node.first);
|
|
|
|
}
|
|
|
|
for (const auto &edge: m_snapshot->edges) {
|
|
|
|
QString partId = valueOfKeyInMapOrEmpty(edge.second, "partId");
|
|
|
|
if (partId.isEmpty())
|
|
|
|
continue;
|
|
|
|
m_partEdgeIds[partId].insert(edge.first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
bool MeshGenerator::checkIsPartDirty(const QString &partIdString)
|
2018-08-27 08:50:40 +00:00
|
|
|
{
|
2019-02-18 12:57:18 +00:00
|
|
|
auto findPart = m_snapshot->parts.find(partIdString);
|
2018-08-27 08:50:40 +00:00
|
|
|
if (findPart == m_snapshot->parts.end()) {
|
2019-02-18 12:57:18 +00:00
|
|
|
qDebug() << "Find part failed:" << partIdString;
|
2018-08-27 08:50:40 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return isTrueValueString(valueOfKeyInMapOrEmpty(findPart->second, "dirty"));
|
|
|
|
}
|
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
bool MeshGenerator::checkIsComponentDirty(const QString &componentIdString)
|
|
|
|
{
|
|
|
|
bool isDirty = false;
|
|
|
|
|
|
|
|
const std::map<QString, QString> *component = &m_snapshot->rootComponent;
|
|
|
|
if (componentIdString != QUuid().toString()) {
|
|
|
|
auto findComponent = m_snapshot->components.find(componentIdString);
|
|
|
|
if (findComponent == m_snapshot->components.end()) {
|
|
|
|
qDebug() << "Component not found:" << componentIdString;
|
|
|
|
return isDirty;
|
|
|
|
}
|
|
|
|
component = &findComponent->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isTrueValueString(valueOfKeyInMapOrEmpty(*component, "dirty"))) {
|
|
|
|
isDirty = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString linkDataType = valueOfKeyInMapOrEmpty(*component, "linkDataType");
|
|
|
|
if ("partId" == linkDataType) {
|
|
|
|
QString partId = valueOfKeyInMapOrEmpty(*component, "linkData");
|
|
|
|
if (checkIsPartDirty(partId)) {
|
|
|
|
m_dirtyPartIds.insert(partId);
|
|
|
|
isDirty = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto &childId: valueOfKeyInMapOrEmpty(*component, "children").split(",")) {
|
|
|
|
if (childId.isEmpty())
|
|
|
|
continue;
|
|
|
|
if (checkIsComponentDirty(childId)) {
|
|
|
|
isDirty = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isDirty)
|
|
|
|
m_dirtyComponentIds.insert(componentIdString);
|
|
|
|
|
|
|
|
return isDirty;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MeshGenerator::checkDirtyFlags()
|
|
|
|
{
|
|
|
|
checkIsComponentDirty(QUuid().toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString)
|
2018-08-27 08:50:40 +00:00
|
|
|
{
|
2019-02-18 12:57:18 +00:00
|
|
|
auto findPart = m_snapshot->parts.find(partIdString);
|
2018-08-27 08:50:40 +00:00
|
|
|
if (findPart == m_snapshot->parts.end()) {
|
2019-02-18 12:57:18 +00:00
|
|
|
qDebug() << "Find part failed:" << partIdString;
|
2018-08-27 08:50:40 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
2018-04-07 08:44:39 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
QUuid partId = QUuid(partIdString);
|
2018-08-27 08:50:40 +00:00
|
|
|
auto &part = findPart->second;
|
|
|
|
bool isDisabled = isTrueValueString(valueOfKeyInMapOrEmpty(part, "disabled"));
|
|
|
|
bool xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(part, "xMirrored"));
|
|
|
|
bool subdived = isTrueValueString(valueOfKeyInMapOrEmpty(part, "subdived"));
|
2019-02-18 12:57:18 +00:00
|
|
|
bool rounded = isTrueValueString(valueOfKeyInMapOrEmpty(part, "rounded"));
|
2018-08-27 08:50:40 +00:00
|
|
|
QString colorString = valueOfKeyInMapOrEmpty(part, "color");
|
2019-02-18 12:57:18 +00:00
|
|
|
QColor partColor = colorString.isEmpty() ? m_defaultPartColor : QColor(colorString);
|
|
|
|
float deformThickness = 1.0;
|
|
|
|
float deformWidth = 1.0;
|
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
QString thicknessString = valueOfKeyInMapOrEmpty(part, "deformThickness");
|
2019-02-18 12:57:18 +00:00
|
|
|
if (!thicknessString.isEmpty()) {
|
|
|
|
deformThickness = thicknessString.toFloat();
|
|
|
|
}
|
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
QString widthString = valueOfKeyInMapOrEmpty(part, "deformWidth");
|
2019-02-18 12:57:18 +00:00
|
|
|
if (!widthString.isEmpty()) {
|
|
|
|
deformWidth = widthString.toFloat();
|
|
|
|
}
|
2018-04-26 02:23:22 +00:00
|
|
|
|
2018-10-09 02:19:12 +00:00
|
|
|
QUuid materialId;
|
|
|
|
QString materialIdString = valueOfKeyInMapOrEmpty(part, "materialId");
|
|
|
|
if (!materialIdString.isEmpty())
|
|
|
|
materialId = QUuid(materialIdString);
|
2018-10-04 12:51:01 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
auto &partCache = m_cacheContext->parts[partIdString];
|
|
|
|
partCache.outcomeNodes.clear();
|
|
|
|
partCache.outcomeNodeVertices.clear();
|
|
|
|
partCache.vertices.clear();
|
|
|
|
partCache.faces.clear();
|
|
|
|
delete partCache.mesh;
|
|
|
|
partCache.mesh = nullptr;
|
2018-11-11 23:21:34 +00:00
|
|
|
|
|
|
|
struct NodeInfo
|
|
|
|
{
|
|
|
|
float radius = 0;
|
|
|
|
QVector3D position;
|
|
|
|
BoneMark boneMark = BoneMark::None;
|
|
|
|
};
|
|
|
|
std::map<QString, NodeInfo> nodeInfos;
|
2019-02-18 12:57:18 +00:00
|
|
|
for (const auto &nodeIdString: m_partNodeIds[partIdString]) {
|
|
|
|
auto findNode = m_snapshot->nodes.find(nodeIdString);
|
2018-08-27 08:50:40 +00:00
|
|
|
if (findNode == m_snapshot->nodes.end()) {
|
2019-02-18 12:57:18 +00:00
|
|
|
qDebug() << "Find node failed:" << nodeIdString;
|
2018-04-11 09:19:27 +00:00
|
|
|
continue;
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
auto &node = findNode->second;
|
2018-04-07 08:44:39 +00:00
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
float radius = valueOfKeyInMapOrEmpty(node, "radius").toFloat();
|
|
|
|
float x = (valueOfKeyInMapOrEmpty(node, "x").toFloat() - m_mainProfileMiddleX);
|
|
|
|
float y = (m_mainProfileMiddleY - valueOfKeyInMapOrEmpty(node, "y").toFloat());
|
|
|
|
float z = (m_sideProfileMiddleX - valueOfKeyInMapOrEmpty(node, "z").toFloat());
|
2018-11-11 23:21:34 +00:00
|
|
|
|
2018-10-25 00:19:38 +00:00
|
|
|
BoneMark boneMark = BoneMarkFromString(valueOfKeyInMapOrEmpty(node, "boneMark").toUtf8().constData());
|
2018-09-14 09:45:05 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
auto &nodeInfo = nodeInfos[nodeIdString];
|
2018-11-11 23:21:34 +00:00
|
|
|
nodeInfo.position = QVector3D(x, y, z);
|
|
|
|
nodeInfo.radius = radius;
|
|
|
|
nodeInfo.boneMark = boneMark;
|
|
|
|
}
|
2019-02-18 12:57:18 +00:00
|
|
|
|
2018-11-11 23:21:34 +00:00
|
|
|
std::set<std::pair<QString, QString>> edges;
|
2019-02-18 12:57:18 +00:00
|
|
|
for (const auto &edgeIdString: m_partEdgeIds[partIdString]) {
|
|
|
|
auto findEdge = m_snapshot->edges.find(edgeIdString);
|
2018-11-11 23:21:34 +00:00
|
|
|
if (findEdge == m_snapshot->edges.end()) {
|
2019-02-18 12:57:18 +00:00
|
|
|
qDebug() << "Find edge failed:" << edgeIdString;
|
2018-11-11 23:21:34 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
auto &edge = findEdge->second;
|
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
QString fromNodeIdString = valueOfKeyInMapOrEmpty(edge, "from");
|
|
|
|
QString toNodeIdString = valueOfKeyInMapOrEmpty(edge, "to");
|
2018-11-11 23:21:34 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
const auto &findFromNodeInfo = nodeInfos.find(fromNodeIdString);
|
|
|
|
if (findFromNodeInfo == nodeInfos.end()) {
|
|
|
|
qDebug() << "Find from-node info failed:" << fromNodeIdString;
|
|
|
|
continue;
|
|
|
|
}
|
2018-11-11 23:21:34 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
const auto &findToNodeInfo = nodeInfos.find(toNodeIdString);
|
|
|
|
if (findToNodeInfo == nodeInfos.end()) {
|
|
|
|
qDebug() << "Find to-node info failed:" << toNodeIdString;
|
|
|
|
continue;
|
|
|
|
}
|
2018-11-11 23:21:34 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
edges.insert({fromNodeIdString, toNodeIdString});
|
2018-11-11 23:21:34 +00:00
|
|
|
}
|
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
std::map<QString, int> nodeIdStringToIndexMap;
|
|
|
|
std::map<int, QString> nodeIndexToIdStringMap;
|
|
|
|
|
|
|
|
nodemesh::Modifier *modifier = new nodemesh::Modifier;
|
|
|
|
|
|
|
|
QString mirroredPartIdString;
|
|
|
|
QUuid mirroredPartId;
|
|
|
|
if (xMirrored) {
|
|
|
|
mirroredPartId = QUuid().createUuid();
|
|
|
|
mirroredPartIdString = mirroredPartId.toString();
|
|
|
|
m_cacheContext->partMirrorIdMap[mirroredPartIdString] = partIdString;
|
|
|
|
}
|
2018-11-11 23:21:34 +00:00
|
|
|
|
|
|
|
for (const auto &nodeIt: nodeInfos) {
|
2019-02-18 12:57:18 +00:00
|
|
|
const auto &nodeIdString = nodeIt.first;
|
2018-11-11 23:21:34 +00:00
|
|
|
const auto &nodeInfo = nodeIt.second;
|
2019-02-18 12:57:18 +00:00
|
|
|
size_t nodeIndex = modifier->addNode(nodeInfo.position, nodeInfo.radius, g_defaultCutTemplate);
|
|
|
|
nodeIdStringToIndexMap[nodeIdString] = nodeIndex;
|
|
|
|
nodeIndexToIdStringMap[nodeIndex] = nodeIdString;
|
2018-11-11 23:21:34 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
OutcomeNode outcomeNode;
|
|
|
|
outcomeNode.partId = QUuid(partIdString);
|
|
|
|
outcomeNode.nodeId = QUuid(nodeIdString);
|
|
|
|
outcomeNode.origin = nodeInfo.position;
|
|
|
|
outcomeNode.radius = nodeInfo.radius;
|
|
|
|
outcomeNode.color = partColor;
|
|
|
|
outcomeNode.materialId = materialId;
|
|
|
|
outcomeNode.boneMark = nodeInfo.boneMark;
|
|
|
|
outcomeNode.mirroredByPartId = mirroredPartIdString;
|
|
|
|
partCache.outcomeNodes.push_back(outcomeNode);
|
2018-09-14 09:45:05 +00:00
|
|
|
if (xMirrored) {
|
2019-02-18 12:57:18 +00:00
|
|
|
outcomeNode.partId = mirroredPartId;
|
|
|
|
outcomeNode.mirrorFromPartId = QUuid(partId);
|
|
|
|
outcomeNode.mirroredByPartId = QUuid();
|
|
|
|
outcomeNode.origin.setX(-nodeInfo.position.x());
|
|
|
|
partCache.outcomeNodes.push_back(outcomeNode);
|
2018-09-14 09:45:05 +00:00
|
|
|
}
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
|
|
|
|
2018-11-11 23:21:34 +00:00
|
|
|
for (const auto &edgeIt: edges) {
|
2019-02-18 12:57:18 +00:00
|
|
|
const QString &fromNodeIdString = edgeIt.first;
|
|
|
|
const QString &toNodeIdString = edgeIt.second;
|
2018-06-11 14:24:25 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
auto findFromNodeIndex = nodeIdStringToIndexMap.find(fromNodeIdString);
|
|
|
|
if (findFromNodeIndex == nodeIdStringToIndexMap.end()) {
|
|
|
|
qDebug() << "Find from-node failed:" << fromNodeIdString;
|
2018-08-27 08:50:40 +00:00
|
|
|
continue;
|
|
|
|
}
|
2018-04-30 11:31:09 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
auto findToNodeIndex = nodeIdStringToIndexMap.find(toNodeIdString);
|
|
|
|
if (findToNodeIndex == nodeIdStringToIndexMap.end()) {
|
|
|
|
qDebug() << "Find to-node failed:" << toNodeIdString;
|
2018-08-27 08:50:40 +00:00
|
|
|
continue;
|
2018-04-30 11:31:09 +00:00
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
modifier->addEdge(findFromNodeIndex->second, findToNodeIndex->second);
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
2018-04-11 09:37:28 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
if (subdived)
|
|
|
|
modifier->subdivide();
|
2018-08-27 08:50:40 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
if (rounded)
|
|
|
|
modifier->roundEnd();
|
2018-08-27 08:50:40 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
modifier->finalize();
|
|
|
|
|
|
|
|
nodemesh::Builder *builder = new nodemesh::Builder;
|
|
|
|
builder->setDeformThickness(deformThickness);
|
|
|
|
builder->setDeformWidth(deformWidth);
|
|
|
|
|
|
|
|
for (const auto &node: modifier->nodes())
|
|
|
|
builder->addNode(node.position, node.radius, node.cutTemplate);
|
|
|
|
for (const auto &edge: modifier->edges())
|
|
|
|
builder->addEdge(edge.firstNodeIndex, edge.secondNodeIndex);
|
|
|
|
bool buildSucceed = builder->build();
|
|
|
|
|
|
|
|
partCache.vertices = builder->generatedVertices();
|
|
|
|
partCache.faces = builder->generatedFaces();
|
|
|
|
for (size_t i = 0; i < partCache.vertices.size(); ++i) {
|
|
|
|
const auto &position = partCache.vertices[i];
|
|
|
|
const auto &source = builder->generatedVerticesSourceNodeIndices()[i];
|
|
|
|
size_t nodeIndex = modifier->nodes()[source].originNodeIndex;
|
|
|
|
const auto &nodeIdString = nodeIndexToIdStringMap[nodeIndex];
|
|
|
|
partCache.outcomeNodeVertices.push_back({position, {partIdString, nodeIdString}});
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
nodemesh::Combiner::Mesh *mesh = nullptr;
|
2018-08-27 08:50:40 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
if (buildSucceed) {
|
|
|
|
mesh = new nodemesh::Combiner::Mesh(partCache.vertices, partCache.faces);
|
|
|
|
if (!mesh->isNull()) {
|
|
|
|
if (xMirrored) {
|
|
|
|
std::vector<QVector3D> xMirroredVertices;
|
|
|
|
std::vector<std::vector<size_t>> xMirroredFaces;
|
|
|
|
makeXmirror(partCache.vertices, partCache.faces, &xMirroredVertices, &xMirroredFaces);
|
|
|
|
for (size_t i = 0; i < xMirroredVertices.size(); ++i) {
|
|
|
|
const auto &position = xMirroredVertices[i];
|
|
|
|
const auto &source = builder->generatedVerticesSourceNodeIndices()[i];
|
|
|
|
size_t nodeIndex = modifier->nodes()[source].originNodeIndex;
|
|
|
|
const auto &nodeIdString = nodeIndexToIdStringMap[nodeIndex];
|
|
|
|
partCache.outcomeNodeVertices.push_back({position, {mirroredPartIdString, nodeIdString}});
|
|
|
|
}
|
|
|
|
size_t xMirrorStart = partCache.vertices.size();
|
|
|
|
for (const auto &vertex: xMirroredVertices)
|
|
|
|
partCache.vertices.push_back(vertex);
|
|
|
|
for (const auto &face: xMirroredFaces) {
|
|
|
|
std::vector<size_t> newFace = face;
|
|
|
|
for (auto &it: newFace)
|
|
|
|
it += xMirrorStart;
|
|
|
|
partCache.faces.push_back(newFace);
|
|
|
|
}
|
|
|
|
nodemesh::Combiner::Mesh *xMirroredMesh = new nodemesh::Combiner::Mesh(xMirroredVertices, xMirroredFaces);
|
|
|
|
nodemesh::Combiner::Mesh *newMesh = combineTwoMeshes(*mesh,
|
|
|
|
*xMirroredMesh, nodemesh::Combiner::Method::Union);
|
|
|
|
delete xMirroredMesh;
|
|
|
|
if (newMesh && !newMesh->isNull()) {
|
|
|
|
delete mesh;
|
|
|
|
mesh = newMesh;
|
|
|
|
} else {
|
|
|
|
m_isSucceed = false;
|
|
|
|
qDebug() << "Xmirrored mesh generate failed";
|
|
|
|
delete newMesh;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
m_isSucceed = false;
|
|
|
|
qDebug() << "Mesh built is uncombinable";
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
2019-02-18 12:57:18 +00:00
|
|
|
} else {
|
|
|
|
m_isSucceed = false;
|
|
|
|
qDebug() << "Mesh build failed";
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
m_partPreviewMeshes[partId] = nullptr;
|
|
|
|
m_generatedPreviewPartIds.insert(partId);
|
2018-08-27 08:50:40 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
if (nullptr != mesh) {
|
|
|
|
partCache.mesh = new nodemesh::Combiner::Mesh(*mesh);
|
|
|
|
|
|
|
|
std::vector<QVector3D> partPreviewVertices;
|
|
|
|
std::vector<std::vector<size_t>> partPreviewTriangles;
|
|
|
|
mesh->fetch(partPreviewVertices, partPreviewTriangles);
|
|
|
|
nodemesh::trim(&partPreviewVertices, true);
|
|
|
|
std::vector<QVector3D> partPreviewTriangleNormals;
|
|
|
|
for (const auto &face: partPreviewTriangles) {
|
|
|
|
partPreviewTriangleNormals.push_back(QVector3D::normal(
|
|
|
|
partPreviewVertices[face[0]],
|
|
|
|
partPreviewVertices[face[1]],
|
|
|
|
partPreviewVertices[face[2]]
|
|
|
|
));
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
2019-02-18 12:57:18 +00:00
|
|
|
std::vector<std::vector<QVector3D>> partPreviewTriangleVertexNormals;
|
|
|
|
generateSmoothTriangleVertexNormals(partPreviewVertices,
|
|
|
|
partPreviewTriangles,
|
|
|
|
partPreviewTriangleNormals,
|
|
|
|
&partPreviewTriangleVertexNormals);
|
|
|
|
m_partPreviewMeshes[partId] = new MeshLoader(partPreviewVertices,
|
|
|
|
partPreviewTriangles,
|
|
|
|
partPreviewTriangleVertexNormals,
|
|
|
|
partColor);
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
delete builder;
|
|
|
|
delete modifier;
|
|
|
|
|
|
|
|
if (mesh && mesh->isNull()) {
|
|
|
|
delete mesh;
|
|
|
|
mesh = nullptr;
|
2018-04-11 06:15:11 +00:00
|
|
|
}
|
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
if (isDisabled) {
|
|
|
|
delete mesh;
|
|
|
|
mesh = nullptr;
|
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
return mesh;
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
nodemesh::Combiner::Mesh *MeshGenerator::combineComponentMesh(const QString &componentIdString, CombineMode *combineMode)
|
2018-08-27 08:50:40 +00:00
|
|
|
{
|
2019-02-18 12:57:18 +00:00
|
|
|
nodemesh::Combiner::Mesh *mesh = nullptr;
|
2018-08-27 08:50:40 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
QUuid componentId;
|
2018-08-27 08:50:40 +00:00
|
|
|
const std::map<QString, QString> *component = &m_snapshot->rootComponent;
|
2019-02-18 12:57:18 +00:00
|
|
|
if (componentIdString != QUuid().toString()) {
|
|
|
|
componentId = QUuid(componentIdString);
|
|
|
|
auto findComponent = m_snapshot->components.find(componentIdString);
|
2018-08-27 08:50:40 +00:00
|
|
|
if (findComponent == m_snapshot->components.end()) {
|
2019-02-18 12:57:18 +00:00
|
|
|
qDebug() << "Component not found:" << componentIdString;
|
2018-08-27 08:50:40 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
component = &findComponent->second;
|
|
|
|
}
|
2019-02-18 12:57:18 +00:00
|
|
|
|
2018-11-15 14:01:20 +00:00
|
|
|
*combineMode = CombineModeFromString(valueOfKeyInMapOrEmpty(*component, "combineMode").toUtf8().constData());
|
2019-02-18 12:57:18 +00:00
|
|
|
if (*combineMode == CombineMode::Normal) {
|
|
|
|
if (isTrueValueString(valueOfKeyInMapOrEmpty(*component, "inverse")))
|
|
|
|
*combineMode = CombineMode::Inversion;
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
auto &componentCache = m_cacheContext->components[componentIdString];
|
2018-09-06 15:04:59 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
if (m_cacheEnabled) {
|
|
|
|
if (m_dirtyComponentIds.find(componentIdString) == m_dirtyComponentIds.end()) {
|
|
|
|
if (nullptr == componentCache.mesh)
|
|
|
|
return nullptr;
|
|
|
|
return new nodemesh::Combiner::Mesh(*componentCache.mesh);
|
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
componentCache.sharedQuadEdges.clear();
|
|
|
|
componentCache.noneSeamVertices.clear();
|
|
|
|
componentCache.outcomeNodes.clear();
|
|
|
|
componentCache.outcomeNodeVertices.clear();
|
|
|
|
delete componentCache.mesh;
|
|
|
|
componentCache.mesh = nullptr;
|
2018-08-27 08:50:40 +00:00
|
|
|
|
2018-09-06 15:04:59 +00:00
|
|
|
QString linkDataType = valueOfKeyInMapOrEmpty(*component, "linkDataType");
|
|
|
|
if ("partId" == linkDataType) {
|
2019-02-18 12:57:18 +00:00
|
|
|
QString partIdString = valueOfKeyInMapOrEmpty(*component, "linkData");
|
|
|
|
mesh = combinePartMesh(partIdString);
|
|
|
|
|
|
|
|
const auto &partCache = m_cacheContext->parts[partIdString];
|
|
|
|
for (const auto &vertex: partCache.vertices)
|
|
|
|
componentCache.noneSeamVertices.insert(vertex);
|
|
|
|
collectSharedQuadEdges(partCache.vertices, partCache.faces, &componentCache.sharedQuadEdges);
|
|
|
|
for (const auto &it: partCache.outcomeNodes)
|
|
|
|
componentCache.outcomeNodes.push_back(it);
|
|
|
|
for (const auto &it: partCache.outcomeNodeVertices)
|
|
|
|
componentCache.outcomeNodeVertices.push_back(it);
|
2018-09-06 15:04:59 +00:00
|
|
|
} else {
|
2019-02-18 12:57:18 +00:00
|
|
|
for (const auto &childIdString: valueOfKeyInMapOrEmpty(*component, "children").split(",")) {
|
|
|
|
if (childIdString.isEmpty())
|
2018-09-06 15:04:59 +00:00
|
|
|
continue;
|
2018-11-15 14:01:20 +00:00
|
|
|
CombineMode childCombineMode = CombineMode::Normal;
|
2019-02-18 12:57:18 +00:00
|
|
|
nodemesh::Combiner::Mesh *subMesh = combineComponentMesh(childIdString, &childCombineMode);
|
|
|
|
|
|
|
|
const auto &childComponentCache = m_cacheContext->components[childIdString];
|
|
|
|
for (const auto &vertex: childComponentCache.noneSeamVertices)
|
|
|
|
componentCache.noneSeamVertices.insert(vertex);
|
|
|
|
for (const auto &it: childComponentCache.sharedQuadEdges)
|
|
|
|
componentCache.sharedQuadEdges.insert(it);
|
|
|
|
for (const auto &it: childComponentCache.outcomeNodes)
|
|
|
|
componentCache.outcomeNodes.push_back(it);
|
|
|
|
for (const auto &it: childComponentCache.outcomeNodeVertices)
|
|
|
|
componentCache.outcomeNodeVertices.push_back(it);
|
|
|
|
|
|
|
|
qDebug() << "Combine mode:" << CombineModeToString(childCombineMode);
|
|
|
|
if (nullptr == subMesh) {
|
|
|
|
m_isSucceed = false;
|
|
|
|
qDebug() << "Child mesh is null";
|
2018-11-15 14:01:20 +00:00
|
|
|
continue;
|
|
|
|
}
|
2019-02-18 12:57:18 +00:00
|
|
|
if (subMesh->isNull()) {
|
|
|
|
m_isSucceed = false;
|
|
|
|
qDebug() << "Child mesh is uncombinable";
|
|
|
|
delete subMesh;
|
2018-09-06 15:04:59 +00:00
|
|
|
continue;
|
2019-02-18 12:57:18 +00:00
|
|
|
}
|
|
|
|
if (nullptr == mesh) {
|
2018-11-15 14:01:20 +00:00
|
|
|
if (childCombineMode == CombineMode::Inversion) {
|
2019-02-18 12:57:18 +00:00
|
|
|
delete subMesh;
|
2018-09-06 15:04:59 +00:00
|
|
|
} else {
|
2019-02-18 12:57:18 +00:00
|
|
|
mesh = subMesh;
|
2018-09-06 15:04:59 +00:00
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
} else {
|
2019-02-18 12:57:18 +00:00
|
|
|
nodemesh::Combiner::Mesh *newMesh = combineTwoMeshes(*mesh,
|
|
|
|
*subMesh,
|
|
|
|
childCombineMode == CombineMode::Inversion ?
|
|
|
|
nodemesh::Combiner::Method::Diff : nodemesh::Combiner::Method::Union);
|
|
|
|
delete subMesh;
|
|
|
|
if (newMesh && !newMesh->isNull()) {
|
|
|
|
delete mesh;
|
|
|
|
mesh = newMesh;
|
|
|
|
} else {
|
|
|
|
m_isSucceed = false;
|
|
|
|
qDebug() << "Mesh combine failed";
|
|
|
|
delete newMesh;
|
2018-09-06 15:04:59 +00:00
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
2018-09-06 15:04:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
if (nullptr != mesh)
|
|
|
|
componentCache.mesh = new nodemesh::Combiner::Mesh(*mesh);
|
|
|
|
|
|
|
|
if (nullptr != mesh && mesh->isNull()) {
|
|
|
|
delete mesh;
|
|
|
|
mesh = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mesh;
|
|
|
|
}
|
|
|
|
|
|
|
|
nodemesh::Combiner::Mesh *MeshGenerator::combineTwoMeshes(const nodemesh::Combiner::Mesh &first, const nodemesh::Combiner::Mesh &second,
|
|
|
|
nodemesh::Combiner::Method method)
|
|
|
|
{
|
|
|
|
if (first.isNull() || second.isNull())
|
|
|
|
return nullptr;
|
|
|
|
std::vector<std::pair<nodemesh::Combiner::Source, size_t>> combinedVerticesSources;
|
|
|
|
nodemesh::Combiner::Mesh *newMesh = nodemesh::Combiner::combine(first,
|
|
|
|
second,
|
|
|
|
method,
|
|
|
|
&combinedVerticesSources);
|
|
|
|
if (nullptr == newMesh)
|
|
|
|
return nullptr;
|
|
|
|
if (!newMesh->isNull()) {
|
|
|
|
nodemesh::Recombiner recombiner;
|
|
|
|
std::vector<QVector3D> combinedVertices;
|
|
|
|
std::vector<std::vector<size_t>> combinedFaces;
|
|
|
|
newMesh->fetch(combinedVertices, combinedFaces);
|
|
|
|
recombiner.setVertices(&combinedVertices, &combinedVerticesSources);
|
|
|
|
recombiner.setFaces(&combinedFaces);
|
|
|
|
if (recombiner.recombine()) {
|
|
|
|
if (nodemesh::isManifold(recombiner.regeneratedFaces())) {
|
|
|
|
nodemesh::Combiner::Mesh *reMesh = new nodemesh::Combiner::Mesh(recombiner.regeneratedVertices(), recombiner.regeneratedFaces(), false);
|
|
|
|
if (!reMesh->isNull() && !reMesh->isSelfIntersected()) {
|
|
|
|
delete newMesh;
|
|
|
|
newMesh = reMesh;
|
|
|
|
} else {
|
|
|
|
delete reMesh;
|
2018-09-06 15:04:59 +00:00
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-02-18 12:57:18 +00:00
|
|
|
return newMesh;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MeshGenerator::makeXmirror(const std::vector<QVector3D> &sourceVertices, const std::vector<std::vector<size_t>> &sourceFaces,
|
|
|
|
std::vector<QVector3D> *destVertices, std::vector<std::vector<size_t>> *destFaces)
|
|
|
|
{
|
|
|
|
for (const auto &mirrorFrom: sourceVertices) {
|
|
|
|
destVertices->push_back(QVector3D(-mirrorFrom.x(), mirrorFrom.y(), mirrorFrom.z()));
|
|
|
|
}
|
|
|
|
std::vector<std::vector<size_t>> newFaces;
|
|
|
|
for (const auto &mirrorFrom: sourceFaces) {
|
|
|
|
auto newFace = mirrorFrom;
|
|
|
|
std::reverse(newFace.begin(), newFace.end());
|
|
|
|
destFaces->push_back(newFace);
|
2019-01-07 13:03:42 +00:00
|
|
|
}
|
2019-02-18 12:57:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MeshGenerator::collectSharedQuadEdges(const std::vector<QVector3D> &vertices, const std::vector<std::vector<size_t>> &faces,
|
|
|
|
std::set<std::pair<nodemesh::PositionKey, nodemesh::PositionKey>> *sharedQuadEdges)
|
|
|
|
{
|
|
|
|
for (const auto &face: faces) {
|
|
|
|
if (face.size() != 4)
|
|
|
|
continue;
|
|
|
|
sharedQuadEdges->insert({
|
|
|
|
nodemesh::PositionKey(vertices[face[0]]),
|
|
|
|
nodemesh::PositionKey(vertices[face[2]])
|
|
|
|
});
|
|
|
|
sharedQuadEdges->insert({
|
|
|
|
nodemesh::PositionKey(vertices[face[1]]),
|
|
|
|
nodemesh::PositionKey(vertices[face[3]])
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MeshGenerator::setGeneratedCacheContext(GeneratedCacheContext *cacheContext)
|
|
|
|
{
|
|
|
|
m_cacheContext = cacheContext;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MeshGenerator::process()
|
|
|
|
{
|
|
|
|
generate();
|
2019-01-07 13:03:42 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
this->moveToThread(QGuiApplication::instance()->thread());
|
|
|
|
emit finished();
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
|
|
|
|
2018-10-09 02:19:12 +00:00
|
|
|
void MeshGenerator::generate()
|
2018-08-27 08:50:40 +00:00
|
|
|
{
|
|
|
|
if (nullptr == m_snapshot)
|
|
|
|
return;
|
2019-01-07 13:03:42 +00:00
|
|
|
|
|
|
|
m_isSucceed = true;
|
2018-08-27 08:50:40 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
QElapsedTimer countTimeConsumed;
|
|
|
|
countTimeConsumed.start();
|
|
|
|
|
2018-10-25 00:19:38 +00:00
|
|
|
m_outcome = new Outcome;
|
2018-08-27 08:50:40 +00:00
|
|
|
|
|
|
|
bool needDeleteCacheContext = false;
|
|
|
|
if (nullptr == m_cacheContext) {
|
|
|
|
m_cacheContext = new GeneratedCacheContext;
|
|
|
|
needDeleteCacheContext = true;
|
|
|
|
} else {
|
2019-02-18 12:57:18 +00:00
|
|
|
m_cacheEnabled = true;
|
|
|
|
for (auto it = m_cacheContext->parts.begin(); it != m_cacheContext->parts.end(); ) {
|
2018-08-27 08:50:40 +00:00
|
|
|
if (m_snapshot->parts.find(it->first) == m_snapshot->parts.end()) {
|
2018-09-14 09:45:05 +00:00
|
|
|
auto mirrorFrom = m_cacheContext->partMirrorIdMap.find(it->first);
|
|
|
|
if (mirrorFrom != m_cacheContext->partMirrorIdMap.end()) {
|
|
|
|
if (m_snapshot->parts.find(mirrorFrom->second) != m_snapshot->parts.end()) {
|
|
|
|
it++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
m_cacheContext->partMirrorIdMap.erase(mirrorFrom);
|
|
|
|
}
|
2019-02-18 12:57:18 +00:00
|
|
|
it = m_cacheContext->parts.erase(it);
|
2018-11-15 14:01:20 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
it++;
|
|
|
|
}
|
2019-02-18 12:57:18 +00:00
|
|
|
for (auto it = m_cacheContext->components.begin(); it != m_cacheContext->components.end(); ) {
|
2018-09-06 15:04:59 +00:00
|
|
|
if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) {
|
2019-02-18 12:57:18 +00:00
|
|
|
it = m_cacheContext->components.erase(it);
|
2018-09-06 15:04:59 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
it++;
|
|
|
|
}
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
collectParts();
|
|
|
|
checkDirtyFlags();
|
|
|
|
|
2018-09-06 15:04:59 +00:00
|
|
|
m_dirtyComponentIds.insert(QUuid().toString());
|
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
m_mainProfileMiddleX = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originX").toFloat();
|
|
|
|
m_mainProfileMiddleY = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originY").toFloat();
|
|
|
|
m_sideProfileMiddleX = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originZ").toFloat();
|
|
|
|
|
2018-11-15 14:01:20 +00:00
|
|
|
CombineMode combineMode;
|
2019-02-18 12:57:18 +00:00
|
|
|
auto combinedMesh = combineComponentMesh(QUuid().toString(), &combineMode);
|
2018-04-07 08:44:39 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
const auto &componentCache = m_cacheContext->components[QUuid().toString()];
|
2018-04-07 08:44:39 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
std::vector<QVector3D> combinedVertices;
|
|
|
|
std::vector<std::vector<size_t>> combinedFaces;
|
|
|
|
if (nullptr != combinedMesh) {
|
|
|
|
combinedMesh->fetch(combinedVertices, combinedFaces);
|
|
|
|
|
|
|
|
size_t totalAffectedNum = 0;
|
|
|
|
size_t affectedNum = 0;
|
|
|
|
do {
|
|
|
|
std::vector<QVector3D> weldedVertices;
|
|
|
|
std::vector<std::vector<size_t>> weldedFaces;
|
|
|
|
affectedNum = nodemesh::weldSeam(combinedVertices, combinedFaces,
|
|
|
|
0.025, componentCache.noneSeamVertices,
|
|
|
|
weldedVertices, weldedFaces);
|
|
|
|
combinedVertices = weldedVertices;
|
|
|
|
combinedFaces = weldedFaces;
|
|
|
|
totalAffectedNum += affectedNum;
|
|
|
|
} while (affectedNum > 0);
|
|
|
|
qDebug() << "Total weld affected triangles:" << totalAffectedNum;
|
|
|
|
|
|
|
|
std::vector<QVector3D> combinedFacesNormals;
|
|
|
|
for (const auto &face: combinedFaces) {
|
|
|
|
combinedFacesNormals.push_back(QVector3D::normal(
|
|
|
|
combinedVertices[face[0]],
|
|
|
|
combinedVertices[face[1]],
|
|
|
|
combinedVertices[face[2]]
|
|
|
|
));
|
2018-09-14 09:45:05 +00:00
|
|
|
}
|
2018-10-26 23:04:45 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
recoverQuads(combinedVertices, combinedFaces, componentCache.sharedQuadEdges, m_outcome->triangleAndQuads);
|
|
|
|
|
|
|
|
m_outcome->nodes = componentCache.outcomeNodes;
|
|
|
|
m_outcome->nodeVertices = componentCache.outcomeNodeVertices;
|
|
|
|
m_outcome->vertices = combinedVertices;
|
|
|
|
m_outcome->triangles = combinedFaces;
|
|
|
|
m_outcome->triangleNormals = combinedFacesNormals;
|
|
|
|
|
2018-10-26 23:04:45 +00:00
|
|
|
std::vector<std::pair<QUuid, QUuid>> sourceNodes;
|
|
|
|
triangleSourceNodeResolve(*m_outcome, sourceNodes);
|
|
|
|
m_outcome->setTriangleSourceNodes(sourceNodes);
|
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
std::vector<std::vector<QVector3D>> triangleVertexNormals;
|
|
|
|
generateSmoothTriangleVertexNormals(combinedVertices,
|
|
|
|
combinedFaces,
|
|
|
|
combinedFacesNormals,
|
|
|
|
&triangleVertexNormals);
|
|
|
|
m_outcome->setTriangleVertexNormals(triangleVertexNormals);
|
2018-10-26 23:04:45 +00:00
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
m_resultMesh = new MeshLoader(*m_outcome);
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
2019-02-18 12:57:18 +00:00
|
|
|
delete combinedMesh;
|
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
if (needDeleteCacheContext) {
|
|
|
|
delete m_cacheContext;
|
|
|
|
m_cacheContext = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
qDebug() << "The mesh generation took" << countTimeConsumed.elapsed() << "milliseconds";
|
2018-10-09 02:19:12 +00:00
|
|
|
}
|
|
|
|
|
2019-02-18 12:57:18 +00:00
|
|
|
void MeshGenerator::generateSmoothTriangleVertexNormals(const std::vector<QVector3D> &vertices, const std::vector<std::vector<size_t>> &triangles,
|
|
|
|
const std::vector<QVector3D> &triangleNormals,
|
|
|
|
std::vector<std::vector<QVector3D>> *triangleVertexNormals)
|
2018-10-09 02:19:12 +00:00
|
|
|
{
|
2019-02-18 12:57:18 +00:00
|
|
|
std::vector<QVector3D> smoothNormals;
|
|
|
|
nodemesh::angleSmooth(vertices,
|
|
|
|
triangles,
|
|
|
|
triangleNormals,
|
|
|
|
60,
|
|
|
|
smoothNormals);
|
|
|
|
triangleVertexNormals->resize(triangles.size(), {
|
|
|
|
QVector3D(), QVector3D(), QVector3D()
|
|
|
|
});
|
|
|
|
size_t index = 0;
|
|
|
|
for (size_t i = 0; i < triangles.size(); ++i) {
|
|
|
|
auto &normals = (*triangleVertexNormals)[i];
|
|
|
|
for (size_t j = 0; j < 3; ++j) {
|
|
|
|
if (index < smoothNormals.size())
|
|
|
|
normals[j] = smoothNormals[index];
|
|
|
|
++index;
|
|
|
|
}
|
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|