2018-04-07 08:44:39 +00:00
|
|
|
#include <vector>
|
|
|
|
#include <QGuiApplication>
|
2018-08-27 08:50:40 +00:00
|
|
|
#include <QElapsedTimer>
|
2018-09-22 10:31:02 +00:00
|
|
|
#include <unordered_set>
|
2018-04-07 08:44:39 +00:00
|
|
|
#include "meshgenerator.h"
|
2018-10-25 00:19:38 +00:00
|
|
|
#include "util.h"
|
|
|
|
#include "document.h"
|
2018-04-07 08:44:39 +00:00
|
|
|
#include "meshlite.h"
|
2018-04-08 08:05:12 +00:00
|
|
|
#include "meshutil.h"
|
2018-04-07 08:44:39 +00:00
|
|
|
#include "theme.h"
|
2018-04-26 02:23:22 +00:00
|
|
|
#include "positionmap.h"
|
2018-09-14 09:45:05 +00:00
|
|
|
#include "meshquadify.h"
|
2018-09-22 10:31:02 +00:00
|
|
|
#include "meshweldseam.h"
|
2018-10-09 02:19:12 +00:00
|
|
|
#include "imageforever.h"
|
2018-10-09 09:17:44 +00:00
|
|
|
#include "material.h"
|
2018-10-26 23:04:45 +00:00
|
|
|
#include "trianglesourcenoderesolve.h"
|
2018-11-15 14:01:20 +00:00
|
|
|
#include "meshinflate.h"
|
2018-04-07 08:44:39 +00:00
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
bool MeshGenerator::m_enableDebug = false;
|
2018-09-14 09:45:05 +00:00
|
|
|
PositionMap<int> *MeshGenerator::m_forMakePositionKey = new PositionMap<int>;
|
2018-08-27 08:50:40 +00:00
|
|
|
|
|
|
|
GeneratedCacheContext::~GeneratedCacheContext()
|
|
|
|
{
|
|
|
|
for (auto &cache: componentCombinableMeshs) {
|
|
|
|
deleteCombinableMesh(cache.second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GeneratedCacheContext::updateComponentCombinableMesh(QString componentId, void *mesh)
|
|
|
|
{
|
|
|
|
auto &cache = componentCombinableMeshs[componentId];
|
|
|
|
if (nullptr != cache)
|
|
|
|
deleteCombinableMesh(cache);
|
|
|
|
cache = cloneCombinableMesh(mesh);
|
|
|
|
}
|
2018-04-11 06:15:11 +00:00
|
|
|
|
2018-10-25 00:19:38 +00:00
|
|
|
MeshGenerator::MeshGenerator(Snapshot *snapshot) :
|
2018-04-07 08:44:39 +00:00
|
|
|
m_snapshot(snapshot),
|
2019-01-07 13:03:42 +00:00
|
|
|
m_isSucceed(false),
|
2018-04-07 08:44:39 +00:00
|
|
|
m_mesh(nullptr),
|
2018-10-25 00:19:38 +00:00
|
|
|
m_outcome(nullptr),
|
2018-08-27 08:50:40 +00:00
|
|
|
m_sharedContextWidget(nullptr),
|
2018-09-07 12:51:33 +00:00
|
|
|
m_cacheContext(nullptr),
|
2018-09-22 10:31:02 +00:00
|
|
|
m_smoothNormal(true),
|
|
|
|
m_weldEnabled(true)
|
2018-04-07 08:44:39 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
MeshGenerator::~MeshGenerator()
|
|
|
|
{
|
|
|
|
delete m_snapshot;
|
|
|
|
delete m_mesh;
|
2018-09-18 06:22:29 +00:00
|
|
|
for (const auto &partPreviewMeshIt: m_partPreviewMeshMap) {
|
|
|
|
delete partPreviewMeshIt.second;
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
2018-10-25 00:19:38 +00:00
|
|
|
delete m_outcome;
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
|
|
|
|
2018-09-07 12:51:33 +00:00
|
|
|
void MeshGenerator::setSmoothNormal(bool smoothNormal)
|
|
|
|
{
|
|
|
|
m_smoothNormal = smoothNormal;
|
|
|
|
}
|
|
|
|
|
2018-09-22 10:31:02 +00:00
|
|
|
void MeshGenerator::setWeldEnabled(bool weldEnabled)
|
|
|
|
{
|
|
|
|
m_weldEnabled = weldEnabled;
|
|
|
|
}
|
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
void MeshGenerator::setGeneratedCacheContext(GeneratedCacheContext *cacheContext)
|
2018-04-07 08:44:39 +00:00
|
|
|
{
|
2018-08-27 08:50:40 +00:00
|
|
|
m_cacheContext = cacheContext;
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
|
|
|
|
2018-09-18 06:22:29 +00:00
|
|
|
void MeshGenerator::addPartPreviewRequirement(const QUuid &partId)
|
2018-04-07 08:44:39 +00:00
|
|
|
{
|
2018-04-15 15:15:29 +00:00
|
|
|
//qDebug() << "addPartPreviewRequirement:" << partId;
|
2018-09-18 06:22:29 +00:00
|
|
|
m_requirePreviewPartIds.insert(partId);
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
void MeshGenerator::setSharedContextWidget(QOpenGLWidget *widget)
|
|
|
|
{
|
|
|
|
m_sharedContextWidget = widget;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
2018-05-07 17:16:58 +00:00
|
|
|
MeshLoader *resultMesh = m_mesh;
|
2018-04-07 08:44:39 +00:00
|
|
|
m_mesh = nullptr;
|
|
|
|
return resultMesh;
|
|
|
|
}
|
|
|
|
|
2018-09-18 06:22:29 +00:00
|
|
|
MeshLoader *MeshGenerator::takePartPreviewMesh(const QUuid &partId)
|
2018-04-07 08:44:39 +00:00
|
|
|
{
|
2018-09-18 06:22:29 +00:00
|
|
|
MeshLoader *resultMesh = m_partPreviewMeshMap[partId];
|
|
|
|
m_partPreviewMeshMap[partId] = nullptr;
|
|
|
|
return resultMesh;
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
|
|
|
|
2018-09-18 06:22:29 +00:00
|
|
|
const std::set<QUuid> &MeshGenerator::requirePreviewPartIds()
|
2018-04-07 08:44:39 +00:00
|
|
|
{
|
2018-09-18 06:22:29 +00:00
|
|
|
return m_requirePreviewPartIds;
|
|
|
|
}
|
|
|
|
|
|
|
|
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-10-26 23:04:45 +00:00
|
|
|
void MeshGenerator::loadVertexSources(void *meshliteContext, int meshId, QUuid partId, const std::map<int, QUuid> &bmeshToNodeIdMap, std::vector<std::pair<QVector3D, std::pair<QUuid, QUuid>>> &bmeshVertices,
|
2018-09-14 09:45:05 +00:00
|
|
|
std::vector<std::tuple<PositionMapKey, PositionMapKey, PositionMapKey, PositionMapKey>> &bmeshQuads)
|
2018-04-26 02:23:22 +00:00
|
|
|
{
|
|
|
|
int vertexCount = meshlite_get_vertex_count(meshliteContext, meshId);
|
|
|
|
int positionBufferLen = vertexCount * 3;
|
|
|
|
float *positionBuffer = new float[positionBufferLen];
|
|
|
|
int positionCount = meshlite_get_vertex_position_array(meshliteContext, meshId, positionBuffer, positionBufferLen) / 3;
|
|
|
|
int *sourceBuffer = new int[positionBufferLen];
|
|
|
|
int sourceCount = meshlite_get_vertex_source_array(meshliteContext, meshId, sourceBuffer, positionBufferLen);
|
|
|
|
Q_ASSERT(positionCount == sourceCount);
|
2018-09-14 09:45:05 +00:00
|
|
|
std::vector<QVector3D> verticesPositions;
|
2018-04-26 02:23:22 +00:00
|
|
|
for (int i = 0, positionIndex = 0; i < positionCount; i++, positionIndex+=3) {
|
2018-10-26 23:04:45 +00:00
|
|
|
std::pair<QVector3D, std::pair<QUuid, QUuid>> vertex;
|
|
|
|
vertex.second.first = partId;
|
2018-08-27 08:50:40 +00:00
|
|
|
auto findNodeId = bmeshToNodeIdMap.find(sourceBuffer[i]);
|
|
|
|
if (findNodeId != bmeshToNodeIdMap.end())
|
2018-10-26 23:04:45 +00:00
|
|
|
vertex.second.second = findNodeId->second;
|
|
|
|
vertex.first = QVector3D(positionBuffer[positionIndex + 0], positionBuffer[positionIndex + 1], positionBuffer[positionIndex + 2]);
|
|
|
|
verticesPositions.push_back(vertex.first);
|
2018-08-27 08:50:40 +00:00
|
|
|
bmeshVertices.push_back(vertex);
|
2018-04-26 02:23:22 +00:00
|
|
|
}
|
2018-09-14 09:45:05 +00:00
|
|
|
int faceCount = meshlite_get_face_count(meshliteContext, meshId);
|
|
|
|
int *faceVertexNumAndIndices = new int[faceCount * MAX_VERTICES_PER_FACE];
|
|
|
|
int filledLength = meshlite_get_face_index_array(meshliteContext, meshId, faceVertexNumAndIndices, faceCount * MAX_VERTICES_PER_FACE);
|
|
|
|
int i = 0;
|
|
|
|
while (i < filledLength) {
|
|
|
|
int num = faceVertexNumAndIndices[i++];
|
2018-09-14 10:28:03 +00:00
|
|
|
Q_ASSERT(num > 0 && num <= MAX_VERTICES_PER_FACE);
|
2018-09-14 09:45:05 +00:00
|
|
|
if (4 != num) {
|
|
|
|
i += num;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
int i0 = faceVertexNumAndIndices[i++];
|
|
|
|
int i1 = faceVertexNumAndIndices[i++];
|
|
|
|
int i2 = faceVertexNumAndIndices[i++];
|
|
|
|
int i3 = faceVertexNumAndIndices[i++];
|
|
|
|
const auto &v0 = verticesPositions[i0];
|
|
|
|
const auto &v1 = verticesPositions[i1];
|
|
|
|
const auto &v2 = verticesPositions[i2];
|
|
|
|
const auto &v3 = verticesPositions[i3];
|
|
|
|
bmeshQuads.push_back(std::make_tuple(m_forMakePositionKey->makeKey(v0.x(), v0.y(), v0.z()),
|
|
|
|
m_forMakePositionKey->makeKey(v1.x(), v1.y(), v1.z()),
|
|
|
|
m_forMakePositionKey->makeKey(v2.x(), v2.y(), v2.z()),
|
|
|
|
m_forMakePositionKey->makeKey(v3.x(), v3.y(), v3.z())));
|
|
|
|
}
|
|
|
|
delete[] faceVertexNumAndIndices;
|
2018-04-26 02:23:22 +00:00
|
|
|
delete[] positionBuffer;
|
|
|
|
delete[] sourceBuffer;
|
|
|
|
}
|
|
|
|
|
2018-10-26 23:04:45 +00:00
|
|
|
void MeshGenerator::loadGeneratedPositionsToOutcome(void *meshliteContext, int triangulatedMeshId)
|
2018-04-26 02:23:22 +00:00
|
|
|
{
|
|
|
|
int vertexCount = meshlite_get_vertex_count(meshliteContext, triangulatedMeshId);
|
|
|
|
int positionBufferLen = vertexCount * 3;
|
|
|
|
float *positionBuffer = new float[positionBufferLen];
|
|
|
|
int positionCount = meshlite_get_vertex_position_array(meshliteContext, triangulatedMeshId, positionBuffer, positionBufferLen) / 3;
|
|
|
|
std::map<int, int> verticesMap;
|
|
|
|
for (int i = 0, positionIndex = 0; i < positionCount; i++, positionIndex+=3) {
|
2018-10-26 23:04:45 +00:00
|
|
|
QVector3D vertex;
|
|
|
|
vertex = QVector3D(positionBuffer[positionIndex + 0], positionBuffer[positionIndex + 1], positionBuffer[positionIndex + 2]);
|
2018-10-25 00:19:38 +00:00
|
|
|
verticesMap[i] = m_outcome->vertices.size();
|
|
|
|
m_outcome->vertices.push_back(vertex);
|
2018-04-26 02:23:22 +00:00
|
|
|
}
|
|
|
|
int faceCount = meshlite_get_face_count(meshliteContext, triangulatedMeshId);
|
|
|
|
int triangleIndexBufferLen = faceCount * 3;
|
|
|
|
int *triangleIndexBuffer = new int[triangleIndexBufferLen];
|
|
|
|
int triangleCount = meshlite_get_triangle_index_array(meshliteContext, triangulatedMeshId, triangleIndexBuffer, triangleIndexBufferLen) / 3;
|
|
|
|
int triangleNormalBufferLen = faceCount * 3;
|
|
|
|
float *normalBuffer = new float[triangleNormalBufferLen];
|
|
|
|
int normalCount = meshlite_get_triangle_normal_array(meshliteContext, triangulatedMeshId, normalBuffer, triangleNormalBufferLen) / 3;
|
|
|
|
Q_ASSERT(triangleCount == normalCount);
|
2018-10-26 23:04:45 +00:00
|
|
|
for (int i = 0, triangleVertIndex = 0, normalIndex=0;
|
|
|
|
i < triangleCount;
|
|
|
|
i++, triangleVertIndex+=3, normalIndex += 3) {
|
2018-11-17 23:02:12 +00:00
|
|
|
std::vector<size_t> triangleIndices(3);
|
2018-10-26 23:04:45 +00:00
|
|
|
QVector3D triangleNormal;
|
2018-11-17 23:02:12 +00:00
|
|
|
triangleIndices[0] = verticesMap[triangleIndexBuffer[triangleVertIndex + 0]];
|
|
|
|
triangleIndices[1] = verticesMap[triangleIndexBuffer[triangleVertIndex + 1]];
|
|
|
|
triangleIndices[2] = verticesMap[triangleIndexBuffer[triangleVertIndex + 2]];
|
2018-10-26 23:04:45 +00:00
|
|
|
triangleNormal = QVector3D(normalBuffer[normalIndex + 0], normalBuffer[normalIndex + 1], normalBuffer[normalIndex + 2]);
|
2018-11-17 23:02:12 +00:00
|
|
|
m_outcome->triangles.push_back(triangleIndices);
|
2018-10-26 23:04:45 +00:00
|
|
|
m_outcome->triangleNormals.push_back(triangleNormal);
|
2018-04-26 02:23:22 +00:00
|
|
|
}
|
|
|
|
delete[] positionBuffer;
|
|
|
|
delete[] triangleIndexBuffer;
|
|
|
|
delete[] normalBuffer;
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MeshGenerator::checkIsPartDirty(QString partId)
|
|
|
|
{
|
|
|
|
auto findPart = m_snapshot->parts.find(partId);
|
|
|
|
if (findPart == m_snapshot->parts.end()) {
|
|
|
|
qDebug() << "Find part failed:" << partId;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return isTrueValueString(valueOfKeyInMapOrEmpty(findPart->second, "dirty"));
|
|
|
|
}
|
|
|
|
|
2018-11-15 14:01:20 +00:00
|
|
|
void *MeshGenerator::combinePartMesh(QString partId, std::vector<std::pair<QVector3D, float>> *balls)
|
2018-08-27 08:50:40 +00:00
|
|
|
{
|
|
|
|
auto findPart = m_snapshot->parts.find(partId);
|
|
|
|
if (findPart == m_snapshot->parts.end()) {
|
|
|
|
qDebug() << "Find part failed:" << partId;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
QUuid partIdNotAsString = QUuid(partId);
|
2018-04-07 08:44:39 +00:00
|
|
|
|
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"));
|
2018-08-31 04:54:32 +00:00
|
|
|
bool wrapped = isTrueValueString(valueOfKeyInMapOrEmpty(part, "wrapped"));
|
2018-08-27 08:50:40 +00:00
|
|
|
int bmeshId = meshlite_bmesh_create(m_meshliteContext);
|
|
|
|
if (subdived)
|
|
|
|
meshlite_bmesh_set_cut_subdiv_count(m_meshliteContext, bmeshId, 1);
|
|
|
|
if (isTrueValueString(valueOfKeyInMapOrEmpty(part, "rounded")))
|
|
|
|
meshlite_bmesh_set_round_way(m_meshliteContext, bmeshId, 1);
|
|
|
|
|
|
|
|
QString colorString = valueOfKeyInMapOrEmpty(part, "color");
|
|
|
|
QColor partColor = colorString.isEmpty() ? Theme::white : QColor(colorString);
|
|
|
|
|
|
|
|
QString thicknessString = valueOfKeyInMapOrEmpty(part, "deformThickness");
|
|
|
|
if (!thicknessString.isEmpty())
|
|
|
|
meshlite_bmesh_set_deform_thickness(m_meshliteContext, bmeshId, thicknessString.toFloat());
|
|
|
|
QString widthString = valueOfKeyInMapOrEmpty(part, "deformWidth");
|
|
|
|
if (!widthString.isEmpty())
|
|
|
|
meshlite_bmesh_set_deform_width(m_meshliteContext, bmeshId, widthString.toFloat());
|
|
|
|
if (MeshGenerator::m_enableDebug)
|
|
|
|
meshlite_bmesh_enable_debug(m_meshliteContext, bmeshId, 1);
|
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
|
|
|
|
2018-09-14 09:45:05 +00:00
|
|
|
QString mirroredPartId;
|
|
|
|
QUuid mirroredPartIdNotAsString;
|
|
|
|
if (xMirrored) {
|
|
|
|
mirroredPartIdNotAsString = QUuid().createUuid();
|
|
|
|
mirroredPartId = mirroredPartIdNotAsString.toString();
|
|
|
|
m_cacheContext->partMirrorIdMap[mirroredPartId] = partId;
|
|
|
|
}
|
2018-04-07 08:44:39 +00:00
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
std::map<QString, int> nodeToBmeshIdMap;
|
|
|
|
std::map<int, QUuid> bmeshToNodeIdMap;
|
|
|
|
auto &cacheBmeshNodes = m_cacheContext->partBmeshNodes[partId];
|
|
|
|
auto &cacheBmeshVertices = m_cacheContext->partBmeshVertices[partId];
|
2018-09-14 09:45:05 +00:00
|
|
|
auto &cacheBmeshQuads = m_cacheContext->partBmeshQuads[partId];
|
2018-09-01 10:18:28 +00:00
|
|
|
cacheBmeshNodes.clear();
|
|
|
|
cacheBmeshVertices.clear();
|
2018-09-14 09:45:05 +00:00
|
|
|
cacheBmeshQuads.clear();
|
2018-11-01 15:24:06 +00:00
|
|
|
std::map<int, std::vector<int>> bmeshNodeIdToDataMap;
|
2018-11-11 23:21:34 +00:00
|
|
|
|
|
|
|
struct NodeInfo
|
|
|
|
{
|
|
|
|
float radius = 0;
|
|
|
|
QVector3D position;
|
|
|
|
BoneMark boneMark = BoneMark::None;
|
|
|
|
};
|
|
|
|
std::map<QString, NodeInfo> nodeInfos;
|
2018-08-27 08:50:40 +00:00
|
|
|
for (const auto &nodeId: m_partNodeIds[partId]) {
|
|
|
|
auto findNode = m_snapshot->nodes.find(nodeId);
|
|
|
|
if (findNode == m_snapshot->nodes.end()) {
|
|
|
|
qDebug() << "Find node failed:" << nodeId;
|
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
|
|
|
|
2018-11-11 23:21:34 +00:00
|
|
|
auto &nodeInfo = nodeInfos[nodeId];
|
|
|
|
nodeInfo.position = QVector3D(x, y, z);
|
|
|
|
nodeInfo.radius = radius;
|
|
|
|
nodeInfo.boneMark = boneMark;
|
2018-11-15 14:01:20 +00:00
|
|
|
|
|
|
|
if (nullptr != balls) {
|
|
|
|
balls->push_back({QVector3D(x, y, z), radius});
|
|
|
|
if (xMirrored) {
|
|
|
|
balls->push_back({QVector3D(-x, y, z), radius});
|
|
|
|
}
|
|
|
|
}
|
2018-11-11 23:21:34 +00:00
|
|
|
}
|
|
|
|
std::set<std::pair<QString, QString>> edges;
|
|
|
|
for (const auto &edgeId: m_partEdgeIds[partId]) {
|
|
|
|
auto findEdge = m_snapshot->edges.find(edgeId);
|
|
|
|
if (findEdge == m_snapshot->edges.end()) {
|
|
|
|
qDebug() << "Find edge failed:" << edgeId;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
auto &edge = findEdge->second;
|
|
|
|
|
|
|
|
QString fromNodeId = valueOfKeyInMapOrEmpty(edge, "from");
|
|
|
|
QString toNodeId = valueOfKeyInMapOrEmpty(edge, "to");
|
|
|
|
|
|
|
|
std::function<void(const QString &, const QString &)> connectNodes;
|
|
|
|
connectNodes = [&connectNodes, &edges, &nodeInfos](const QString &fromNodeId, const QString &toNodeId) {
|
|
|
|
|
|
|
|
const auto &findFromNodeInfo = nodeInfos.find(fromNodeId);
|
|
|
|
if (findFromNodeInfo == nodeInfos.end()) {
|
|
|
|
qDebug() << "Find from-node info failed:" << fromNodeId;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto &findToNodeInfo = nodeInfos.find(toNodeId);
|
|
|
|
if (findToNodeInfo == nodeInfos.end()) {
|
|
|
|
qDebug() << "Find to-node info failed:" << toNodeId;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto distanceBetweenNodes = findFromNodeInfo->second.position.distanceToPoint(findToNodeInfo->second.position);
|
|
|
|
float centerEmptyLength = distanceBetweenNodes - (findFromNodeInfo->second.radius + findToNodeInfo->second.radius);
|
|
|
|
if (centerEmptyLength < distanceBetweenNodes * 0.5) {
|
|
|
|
edges.insert({fromNodeId, toNodeId});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cut off by add intermediate nodes
|
|
|
|
QString newNodeId = QUuid::createUuid().toString();
|
|
|
|
auto &nodeInfo = nodeInfos[newNodeId];
|
|
|
|
nodeInfo.position = (findFromNodeInfo->second.position + findToNodeInfo->second.position) / 2;
|
|
|
|
nodeInfo.radius = (findFromNodeInfo->second.radius + findToNodeInfo->second.radius) / 2;
|
|
|
|
|
|
|
|
connectNodes(fromNodeId, newNodeId);
|
|
|
|
connectNodes(newNodeId, toNodeId);
|
|
|
|
};
|
|
|
|
|
|
|
|
connectNodes(fromNodeId, toNodeId);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto &nodeIt: nodeInfos) {
|
|
|
|
const auto &nodeId = nodeIt.first;
|
|
|
|
const auto &nodeInfo = nodeIt.second;
|
|
|
|
|
|
|
|
int bmeshNodeId = meshlite_bmesh_add_node(m_meshliteContext, bmeshId,
|
|
|
|
nodeInfo.position.x(), nodeInfo.position.y(), nodeInfo.position.z(), nodeInfo.radius);
|
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
nodeToBmeshIdMap[nodeId] = bmeshNodeId;
|
|
|
|
bmeshToNodeIdMap[bmeshNodeId] = nodeId;
|
2018-04-26 02:23:22 +00:00
|
|
|
|
2018-10-25 00:19:38 +00:00
|
|
|
OutcomeNode bmeshNode;
|
2018-08-27 08:50:40 +00:00
|
|
|
bmeshNode.partId = QUuid(partId);
|
2018-11-11 23:21:34 +00:00
|
|
|
bmeshNode.origin = nodeInfo.position;
|
|
|
|
bmeshNode.radius = nodeInfo.radius;
|
2018-08-27 08:50:40 +00:00
|
|
|
bmeshNode.nodeId = QUuid(nodeId);
|
2018-10-26 23:04:45 +00:00
|
|
|
bmeshNode.color = partColor;
|
|
|
|
bmeshNode.materialId = materialId;
|
2018-11-11 23:21:34 +00:00
|
|
|
bmeshNode.boneMark = nodeInfo.boneMark;
|
2018-11-01 15:24:06 +00:00
|
|
|
bmeshNode.mirroredByPartId = mirroredPartId;
|
|
|
|
bmeshNodeIdToDataMap[bmeshNodeId].push_back(cacheBmeshNodes.size());
|
2018-08-27 08:50:40 +00:00
|
|
|
cacheBmeshNodes.push_back(bmeshNode);
|
2018-09-14 09:45:05 +00:00
|
|
|
if (xMirrored) {
|
|
|
|
bmeshNode.partId = mirroredPartId;
|
2018-10-09 13:01:04 +00:00
|
|
|
bmeshNode.mirrorFromPartId = QUuid(partId);
|
2018-11-01 15:24:06 +00:00
|
|
|
bmeshNode.mirroredByPartId = QUuid();
|
2018-11-11 23:21:34 +00:00
|
|
|
bmeshNode.origin.setX(-nodeInfo.position.x());
|
2018-11-01 15:24:06 +00:00
|
|
|
bmeshNodeIdToDataMap[bmeshNodeId].push_back(cacheBmeshNodes.size());
|
2018-09-14 09:45:05 +00:00
|
|
|
cacheBmeshNodes.push_back(bmeshNode);
|
|
|
|
}
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
|
|
|
|
2018-11-11 23:21:34 +00:00
|
|
|
for (const auto &edgeIt: edges) {
|
|
|
|
const QString &fromNodeId = edgeIt.first;
|
|
|
|
const QString &toNodeId = edgeIt.second;
|
2018-06-11 14:24:25 +00:00
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
auto findFromBmeshId = nodeToBmeshIdMap.find(fromNodeId);
|
|
|
|
if (findFromBmeshId == nodeToBmeshIdMap.end()) {
|
|
|
|
qDebug() << "Find from-node bmesh failed:" << fromNodeId;
|
|
|
|
continue;
|
|
|
|
}
|
2018-04-30 11:31:09 +00:00
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
auto findToBmeshId = nodeToBmeshIdMap.find(toNodeId);
|
|
|
|
if (findToBmeshId == nodeToBmeshIdMap.end()) {
|
|
|
|
qDebug() << "Find to-node bmesh failed:" << toNodeId;
|
|
|
|
continue;
|
2018-04-30 11:31:09 +00:00
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
|
|
|
|
meshlite_bmesh_add_edge(m_meshliteContext, bmeshId, findFromBmeshId->second, findToBmeshId->second);
|
2018-04-09 11:13:34 +00:00
|
|
|
}
|
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
int meshId = 0;
|
|
|
|
void *resultMesh = nullptr;
|
|
|
|
if (!bmeshToNodeIdMap.empty()) {
|
|
|
|
meshId = meshlite_bmesh_generate_mesh(m_meshliteContext, bmeshId);
|
2018-11-01 15:24:06 +00:00
|
|
|
for (const auto &item: bmeshNodeIdToDataMap) {
|
|
|
|
float baseNormal[3] = {0, 0, 0};
|
|
|
|
meshlite_bmesh_get_node_base_norm(m_meshliteContext, bmeshId, item.first, baseNormal);
|
|
|
|
for (auto &subItem: item.second)
|
|
|
|
cacheBmeshNodes[subItem].baseNormal = QVector3D(baseNormal[0], baseNormal[1], baseNormal[2]);
|
|
|
|
}
|
2018-09-14 09:45:05 +00:00
|
|
|
loadVertexSources(m_meshliteContext, meshId, partIdNotAsString, bmeshToNodeIdMap, cacheBmeshVertices, cacheBmeshQuads);
|
2018-08-31 04:54:32 +00:00
|
|
|
if (wrapped)
|
|
|
|
resultMesh = convertToCombinableConvexHullMesh(m_meshliteContext, meshId);
|
|
|
|
else
|
|
|
|
resultMesh = convertToCombinableMesh(m_meshliteContext, meshlite_triangulate(m_meshliteContext, meshId));
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
2018-04-11 09:37:28 +00:00
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
if (nullptr != resultMesh) {
|
2018-04-30 11:31:09 +00:00
|
|
|
if (xMirrored) {
|
2018-08-27 08:50:40 +00:00
|
|
|
int xMirroredMeshId = meshlite_mirror_in_x(m_meshliteContext, meshId, 0);
|
2018-09-14 09:45:05 +00:00
|
|
|
loadVertexSources(m_meshliteContext, xMirroredMeshId, mirroredPartIdNotAsString, bmeshToNodeIdMap, cacheBmeshVertices, cacheBmeshQuads);
|
2018-08-31 04:54:32 +00:00
|
|
|
void *mirroredMesh = nullptr;
|
|
|
|
if (wrapped)
|
|
|
|
mirroredMesh = convertToCombinableConvexHullMesh(m_meshliteContext, xMirroredMeshId);
|
|
|
|
else
|
|
|
|
mirroredMesh = convertToCombinableMesh(m_meshliteContext, meshlite_triangulate(m_meshliteContext, xMirroredMeshId));
|
2018-08-27 08:50:40 +00:00
|
|
|
if (nullptr != mirroredMesh) {
|
|
|
|
void *newResultMesh = unionCombinableMeshs(resultMesh, mirroredMesh);
|
|
|
|
deleteCombinableMesh(mirroredMesh);
|
|
|
|
if (nullptr != newResultMesh) {
|
|
|
|
deleteCombinableMesh(resultMesh);
|
|
|
|
resultMesh = newResultMesh;
|
|
|
|
}
|
2018-04-15 12:11:51 +00:00
|
|
|
}
|
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
|
|
|
|
2018-09-18 06:22:29 +00:00
|
|
|
if (m_requirePreviewPartIds.find(partIdNotAsString) != m_requirePreviewPartIds.end()) {
|
2018-08-27 08:50:40 +00:00
|
|
|
int trimedMeshId = meshlite_trim(m_meshliteContext, meshId, 1);
|
2018-10-09 02:19:12 +00:00
|
|
|
m_partPreviewMeshMap[partIdNotAsString] = new MeshLoader(m_meshliteContext, trimedMeshId, -1, partColor, nullptr, m_smoothNormal);
|
2018-09-18 06:22:29 +00:00
|
|
|
m_generatedPreviewPartIds.insert(partIdNotAsString);
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (isDisabled) {
|
|
|
|
if (nullptr != resultMesh) {
|
|
|
|
deleteCombinableMesh(resultMesh);
|
|
|
|
resultMesh = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return resultMesh;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MeshGenerator::checkIsComponentDirty(QString componentId)
|
|
|
|
{
|
|
|
|
bool isDirty = false;
|
|
|
|
|
|
|
|
const std::map<QString, QString> *component = &m_snapshot->rootComponent;
|
|
|
|
if (componentId != QUuid().toString()) {
|
|
|
|
auto findComponent = m_snapshot->components.find(componentId);
|
|
|
|
if (findComponent == m_snapshot->components.end()) {
|
|
|
|
qDebug() << "Component not found:" << componentId;
|
2018-09-16 19:23:54 +00:00
|
|
|
return isDirty;
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
|
|
|
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;
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto &childId: valueOfKeyInMapOrEmpty(*component, "children").split(",")) {
|
|
|
|
if (childId.isEmpty())
|
|
|
|
continue;
|
|
|
|
if (checkIsComponentDirty(childId)) {
|
|
|
|
isDirty = true;
|
2018-06-16 06:41:52 +00:00
|
|
|
}
|
2018-04-11 06:15:11 +00:00
|
|
|
}
|
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
if (isDirty)
|
|
|
|
m_dirtyComponentIds.insert(componentId);
|
|
|
|
|
|
|
|
return isDirty;
|
|
|
|
}
|
|
|
|
|
2018-11-15 14:01:20 +00:00
|
|
|
void *MeshGenerator::combineComponentMesh(QString componentId, CombineMode *combineMode, std::vector<std::pair<QVector3D, float>> *inflateBalls)
|
2018-08-27 08:50:40 +00:00
|
|
|
{
|
|
|
|
QUuid componentIdNotAsString;
|
|
|
|
|
|
|
|
const std::map<QString, QString> *component = &m_snapshot->rootComponent;
|
|
|
|
if (componentId != QUuid().toString()) {
|
|
|
|
componentIdNotAsString = QUuid(componentId);
|
|
|
|
auto findComponent = m_snapshot->components.find(componentId);
|
|
|
|
if (findComponent == m_snapshot->components.end()) {
|
|
|
|
qDebug() << "Component not found:" << componentId;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
component = &findComponent->second;
|
|
|
|
}
|
|
|
|
|
2018-11-15 14:01:20 +00:00
|
|
|
*combineMode = CombineModeFromString(valueOfKeyInMapOrEmpty(*component, "combineMode").toUtf8().constData());
|
|
|
|
|
|
|
|
if (*combineMode == CombineMode::Inflation) {
|
|
|
|
if (m_dirtyComponentIds.find(componentId) == m_dirtyComponentIds.end()) {
|
|
|
|
auto findCacheInflateBalls = m_cacheContext->componentInflateBalls.find(componentId);
|
|
|
|
if (findCacheInflateBalls != m_cacheContext->componentInflateBalls.end()) {
|
|
|
|
*inflateBalls = findCacheInflateBalls->second;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
|
2018-11-15 14:01:20 +00:00
|
|
|
if (*combineMode != CombineMode::Inflation) {
|
|
|
|
if (m_dirtyComponentIds.find(componentId) == m_dirtyComponentIds.end()) {
|
|
|
|
auto findCachedMesh = m_cacheContext->componentCombinableMeshs.find(componentId);
|
|
|
|
if (findCachedMesh != m_cacheContext->componentCombinableMeshs.end() &&
|
|
|
|
nullptr != findCachedMesh->second) {
|
|
|
|
//qDebug() << "Component mesh cache used:" << componentId;
|
|
|
|
return cloneCombinableMesh(findCachedMesh->second);
|
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-06 15:04:59 +00:00
|
|
|
bool smoothSeam = false;
|
|
|
|
float smoothSeamFactor = 0.0;
|
|
|
|
QString smoothSeamString = valueOfKeyInMapOrEmpty(*component, "smoothSeam");
|
|
|
|
if (!smoothSeamString.isEmpty()) {
|
|
|
|
smoothSeam = true;
|
|
|
|
smoothSeamFactor = smoothSeamString.toFloat();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool smoothAll = false;
|
|
|
|
float smoothAllFactor = 0.0;
|
|
|
|
QString smoothAllString = valueOfKeyInMapOrEmpty(*component, "smoothAll");
|
|
|
|
if (!smoothAllString.isEmpty()) {
|
|
|
|
smoothAll = true;
|
|
|
|
smoothAllFactor = smoothAllString.toFloat();
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void *resultMesh = nullptr;
|
|
|
|
|
2018-09-06 15:04:59 +00:00
|
|
|
PositionMap<bool> positionsBeforeCombination;
|
|
|
|
auto &verticesSources = m_cacheContext->componentVerticesSources[componentId];
|
|
|
|
verticesSources.map().clear();
|
|
|
|
QString linkDataType = valueOfKeyInMapOrEmpty(*component, "linkDataType");
|
|
|
|
if ("partId" == linkDataType) {
|
|
|
|
QString partId = valueOfKeyInMapOrEmpty(*component, "linkData");
|
2018-11-15 14:01:20 +00:00
|
|
|
std::vector<std::pair<QVector3D, float>> partBalls;
|
|
|
|
resultMesh = combinePartMesh(partId, &partBalls);
|
|
|
|
if (*combineMode == CombineMode::Inflation) {
|
|
|
|
deleteCombinableMesh(resultMesh);
|
|
|
|
resultMesh = nullptr;
|
|
|
|
inflateBalls->insert(inflateBalls->end(), partBalls.begin(), partBalls.end());
|
|
|
|
} else {
|
|
|
|
for (const auto &bmeshVertex: m_cacheContext->partBmeshVertices[partId]) {
|
|
|
|
verticesSources.addPosition(bmeshVertex.first.x(), bmeshVertex.first.y(), bmeshVertex.first.z(),
|
|
|
|
bmeshVertex);
|
|
|
|
}
|
2018-09-06 15:04:59 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (const auto &childId: valueOfKeyInMapOrEmpty(*component, "children").split(",")) {
|
|
|
|
if (childId.isEmpty())
|
|
|
|
continue;
|
2018-11-15 14:01:20 +00:00
|
|
|
CombineMode childCombineMode = CombineMode::Normal;
|
|
|
|
std::vector<std::pair<QVector3D, float>> childInflateBalls;
|
|
|
|
void *childCombinedMesh = combineComponentMesh(childId, &childCombineMode, &childInflateBalls);
|
|
|
|
inflateBalls->insert(inflateBalls->end(), childInflateBalls.begin(), childInflateBalls.end());
|
|
|
|
if (childCombineMode == CombineMode::Inflation) {
|
|
|
|
deleteCombinableMesh(childCombinedMesh);
|
|
|
|
childCombinedMesh = nullptr;
|
|
|
|
if (nullptr == resultMesh)
|
|
|
|
continue;
|
|
|
|
std::vector<std::pair<QVector3D, QVector3D>> inflatedVertices;
|
|
|
|
void *inflatedMesh = meshInflate(resultMesh, childInflateBalls, inflatedVertices);
|
|
|
|
deleteCombinableMesh(resultMesh);
|
|
|
|
resultMesh = inflatedMesh;
|
|
|
|
for (const auto &item: inflatedVertices) {
|
|
|
|
const auto &oldPosition = item.first;
|
|
|
|
const auto &newPosition = item.second;
|
|
|
|
std::pair<QVector3D, std::pair<QUuid, QUuid>> source;
|
|
|
|
if (verticesSources.findPosition(oldPosition.x(), oldPosition.y(), oldPosition.z(), &source)) {
|
|
|
|
verticesSources.removePosition(oldPosition.x(), oldPosition.y(), oldPosition.z());
|
|
|
|
source.first = newPosition;
|
|
|
|
verticesSources.addPosition(newPosition.x(), newPosition.y(), newPosition.z(), source);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2018-09-22 10:31:02 +00:00
|
|
|
for (const auto &positionIt: m_cacheContext->componentPositions[childId]) {
|
|
|
|
positionsBeforeCombination.addPosition(positionIt.x(), positionIt.y(), positionIt.z(), true);
|
2018-09-06 15:04:59 +00:00
|
|
|
}
|
|
|
|
for (const auto &verticesSourceIt: m_cacheContext->componentVerticesSources[childId].map()) {
|
|
|
|
verticesSources.map()[verticesSourceIt.first] = verticesSourceIt.second;
|
|
|
|
}
|
|
|
|
if (nullptr == childCombinedMesh)
|
|
|
|
continue;
|
|
|
|
if (nullptr == resultMesh) {
|
2018-11-15 14:01:20 +00:00
|
|
|
if (childCombineMode == CombineMode::Inversion) {
|
2018-09-06 15:04:59 +00:00
|
|
|
deleteCombinableMesh(childCombinedMesh);
|
|
|
|
} else {
|
|
|
|
resultMesh = childCombinedMesh;
|
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
} else {
|
2018-11-15 14:01:20 +00:00
|
|
|
void *newResultMesh = childCombineMode == CombineMode::Inversion ? diffCombinableMeshs(resultMesh, childCombinedMesh) : unionCombinableMeshs(resultMesh, childCombinedMesh);
|
2018-09-06 15:04:59 +00:00
|
|
|
deleteCombinableMesh(childCombinedMesh);
|
|
|
|
if (nullptr != newResultMesh) {
|
|
|
|
deleteCombinableMesh(resultMesh);
|
|
|
|
resultMesh = newResultMesh;
|
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
2018-09-06 15:04:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nullptr != resultMesh) {
|
2018-09-22 10:31:02 +00:00
|
|
|
int meshIdForSmooth = convertFromCombinableMesh(m_meshliteContext, resultMesh);
|
|
|
|
std::vector<QVector3D> positionsBeforeSmooth;
|
|
|
|
loadMeshVerticesPositions(m_meshliteContext, meshIdForSmooth, positionsBeforeSmooth);
|
|
|
|
|
|
|
|
if (!positionsBeforeSmooth.empty()) {
|
|
|
|
std::vector<int> seamVerticesIds;
|
2018-11-17 23:02:12 +00:00
|
|
|
std::unordered_set<int> seamVerticesIndices;
|
2018-09-06 15:04:59 +00:00
|
|
|
|
2018-09-22 10:31:02 +00:00
|
|
|
if (!positionsBeforeCombination.map().empty()) {
|
|
|
|
for (size_t vertexIndex = 0; vertexIndex < positionsBeforeSmooth.size(); vertexIndex++) {
|
|
|
|
const auto &oldPosition = positionsBeforeSmooth[vertexIndex];
|
|
|
|
if (!positionsBeforeCombination.findPosition(oldPosition.x(), oldPosition.y(), oldPosition.z())) {
|
|
|
|
seamVerticesIds.push_back(vertexIndex + 1);
|
2018-11-17 23:02:12 +00:00
|
|
|
seamVerticesIndices.insert(vertexIndex);
|
2018-09-06 15:04:59 +00:00
|
|
|
}
|
2018-09-22 10:31:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool meshChanged = false;
|
|
|
|
|
|
|
|
if (smoothSeam) {
|
|
|
|
if (!seamVerticesIds.empty()) {
|
2018-11-17 23:02:12 +00:00
|
|
|
//qDebug() << "smoothSeamFactor:" << smoothSeamFactor << "seamVerticesIndices.size():" << seamVerticesNum;
|
2018-09-22 10:31:02 +00:00
|
|
|
meshlite_smooth_vertices(m_meshliteContext, meshIdForSmooth, smoothSeamFactor, seamVerticesIds.data(), seamVerticesIds.size());
|
|
|
|
meshChanged = true;
|
2018-09-06 15:04:59 +00:00
|
|
|
}
|
2018-09-22 10:31:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (smoothAll) {
|
|
|
|
meshlite_smooth(m_meshliteContext, meshIdForSmooth, smoothAllFactor);
|
|
|
|
meshChanged = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (meshChanged) {
|
2018-09-06 15:04:59 +00:00
|
|
|
std::vector<QVector3D> positionsAfterSmooth;
|
|
|
|
loadMeshVerticesPositions(m_meshliteContext, meshIdForSmooth, positionsAfterSmooth);
|
2018-09-06 15:29:26 +00:00
|
|
|
Q_ASSERT(positionsBeforeSmooth.size() == positionsAfterSmooth.size());
|
2018-09-06 15:04:59 +00:00
|
|
|
|
|
|
|
for (size_t vertexIndex = 0; vertexIndex < positionsBeforeSmooth.size(); vertexIndex++) {
|
|
|
|
const auto &oldPosition = positionsBeforeSmooth[vertexIndex];
|
|
|
|
const auto &smoothedPosition = positionsAfterSmooth[vertexIndex];
|
2018-10-26 23:04:45 +00:00
|
|
|
std::pair<QVector3D, std::pair<QUuid, QUuid>> source;
|
2018-09-06 15:04:59 +00:00
|
|
|
if (verticesSources.findPosition(oldPosition.x(), oldPosition.y(), oldPosition.z(), &source)) {
|
|
|
|
verticesSources.removePosition(oldPosition.x(), oldPosition.y(), oldPosition.z());
|
2018-10-26 23:04:45 +00:00
|
|
|
source.first = smoothedPosition;
|
2018-09-06 15:04:59 +00:00
|
|
|
verticesSources.addPosition(smoothedPosition.x(), smoothedPosition.y(), smoothedPosition.z(), source);
|
|
|
|
}
|
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
deleteCombinableMesh(resultMesh);
|
2018-09-06 15:04:59 +00:00
|
|
|
resultMesh = convertToCombinableMesh(m_meshliteContext, meshIdForSmooth);
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-15 14:01:20 +00:00
|
|
|
m_cacheContext->componentInflateBalls[componentId] = *inflateBalls;
|
|
|
|
|
2018-09-06 15:04:59 +00:00
|
|
|
m_cacheContext->updateComponentCombinableMesh(componentId, resultMesh);
|
|
|
|
auto &cachedComponentPositions = m_cacheContext->componentPositions[componentId];
|
|
|
|
cachedComponentPositions.clear();
|
|
|
|
loadCombinableMeshVerticesPositions(resultMesh, cachedComponentPositions);
|
2018-08-27 08:50:40 +00:00
|
|
|
|
2019-01-07 13:03:42 +00:00
|
|
|
if (nullptr == resultMesh) {
|
|
|
|
m_isSucceed = false;
|
|
|
|
}
|
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
return resultMesh;
|
|
|
|
}
|
|
|
|
|
2018-10-09 02:19:12 +00:00
|
|
|
void MeshGenerator::generate()
|
2018-08-27 08:50:40 +00:00
|
|
|
{
|
|
|
|
if (nullptr == m_snapshot)
|
|
|
|
return;
|
|
|
|
QElapsedTimer countTimeConsumed;
|
|
|
|
countTimeConsumed.start();
|
2019-01-07 13:03:42 +00:00
|
|
|
|
|
|
|
m_isSucceed = true;
|
2018-08-27 08:50:40 +00:00
|
|
|
|
|
|
|
m_meshliteContext = meshlite_create_context();
|
|
|
|
|
|
|
|
initMeshUtils();
|
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 {
|
|
|
|
for (auto it = m_cacheContext->partBmeshNodes.begin(); it != m_cacheContext->partBmeshNodes.end(); ) {
|
|
|
|
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);
|
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
it = m_cacheContext->partBmeshNodes.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
it++;
|
2018-04-11 09:37:28 +00:00
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
for (auto it = m_cacheContext->partBmeshVertices.begin(); it != m_cacheContext->partBmeshVertices.end(); ) {
|
|
|
|
if (m_snapshot->parts.find(it->first) == m_snapshot->parts.end()) {
|
|
|
|
it = m_cacheContext->partBmeshVertices.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
it++;
|
2018-04-11 06:15:11 +00:00
|
|
|
}
|
2018-09-14 09:45:05 +00:00
|
|
|
for (auto it = m_cacheContext->partBmeshQuads.begin(); it != m_cacheContext->partBmeshQuads.end(); ) {
|
|
|
|
if (m_snapshot->parts.find(it->first) == m_snapshot->parts.end()) {
|
|
|
|
it = m_cacheContext->partBmeshQuads.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
it++;
|
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
for (auto it = m_cacheContext->componentCombinableMeshs.begin(); it != m_cacheContext->componentCombinableMeshs.end(); ) {
|
|
|
|
if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) {
|
|
|
|
deleteCombinableMesh(it->second);
|
|
|
|
it = m_cacheContext->componentCombinableMeshs.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
it++;
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
2018-09-06 15:04:59 +00:00
|
|
|
for (auto it = m_cacheContext->componentPositions.begin(); it != m_cacheContext->componentPositions.end(); ) {
|
|
|
|
if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) {
|
|
|
|
it = m_cacheContext->componentPositions.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
it++;
|
|
|
|
}
|
2018-11-15 14:01:20 +00:00
|
|
|
for (auto it = m_cacheContext->componentInflateBalls.begin(); it != m_cacheContext->componentInflateBalls.end(); ) {
|
|
|
|
if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) {
|
|
|
|
it = m_cacheContext->componentInflateBalls.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
it++;
|
|
|
|
}
|
2018-09-06 15:04:59 +00:00
|
|
|
for (auto it = m_cacheContext->componentVerticesSources.begin(); it != m_cacheContext->componentVerticesSources.end(); ) {
|
|
|
|
if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) {
|
|
|
|
it = m_cacheContext->componentVerticesSources.erase(it);
|
|
|
|
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();
|
|
|
|
|
|
|
|
int resultMeshId = 0;
|
|
|
|
|
2018-11-15 14:01:20 +00:00
|
|
|
CombineMode combineMode;
|
|
|
|
std::vector<std::pair<QVector3D, float>> inflateBalls;
|
|
|
|
void *combinedMesh = combineComponentMesh(QUuid().toString(), &combineMode, &inflateBalls);
|
2018-08-27 08:50:40 +00:00
|
|
|
if (nullptr != combinedMesh) {
|
|
|
|
resultMeshId = convertFromCombinableMesh(m_meshliteContext, combinedMesh);
|
|
|
|
deleteCombinableMesh(combinedMesh);
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
|
|
|
|
2018-09-06 15:04:59 +00:00
|
|
|
for (const auto &verticesSourcesIt: m_cacheContext->componentVerticesSources[QUuid().toString()].map()) {
|
2018-10-25 15:28:10 +00:00
|
|
|
m_outcome->nodeVertices.push_back(verticesSourcesIt.second);
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
for (const auto &bmeshNodes: m_cacheContext->partBmeshNodes) {
|
2018-10-25 15:28:10 +00:00
|
|
|
m_outcome->nodes.insert(m_outcome->nodes.end(),
|
2018-08-27 08:50:40 +00:00
|
|
|
bmeshNodes.second.begin(), bmeshNodes.second.end());
|
|
|
|
}
|
2018-04-07 08:44:39 +00:00
|
|
|
|
2018-09-14 09:45:05 +00:00
|
|
|
int triangulatedFinalMeshId = resultMeshId;
|
|
|
|
if (triangulatedFinalMeshId > 0) {
|
2018-10-05 15:54:12 +00:00
|
|
|
if (m_weldEnabled) {
|
|
|
|
PositionMap<bool> excludePositions;
|
|
|
|
for (auto it = m_cacheContext->partBmeshVertices.begin(); it != m_cacheContext->partBmeshVertices.end(); ++it) {
|
|
|
|
for (const auto &bmeshVertex: it->second) {
|
2018-10-26 23:04:45 +00:00
|
|
|
excludePositions.addPosition(bmeshVertex.first.x(), bmeshVertex.first.y(), bmeshVertex.first.z(), true);
|
2018-10-05 15:54:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
int totalAffectedNum = 0;
|
|
|
|
int affectedNum = 0;
|
|
|
|
int weldedMeshId = triangulatedFinalMeshId;
|
|
|
|
do {
|
|
|
|
affectedNum = 0;
|
|
|
|
weldedMeshId = meshWeldSeam(m_meshliteContext, weldedMeshId, 0.025, excludePositions, &affectedNum);
|
|
|
|
if (weldedMeshId <= 0)
|
|
|
|
break;
|
|
|
|
triangulatedFinalMeshId = weldedMeshId;
|
|
|
|
totalAffectedNum += affectedNum;
|
|
|
|
} while (affectedNum > 0);
|
|
|
|
qDebug() << "Total weld affected triangles:" << totalAffectedNum;
|
|
|
|
}
|
2018-09-14 09:45:05 +00:00
|
|
|
std::set<std::pair<PositionMapKey, PositionMapKey>> sharedQuadEdges;
|
|
|
|
for (const auto &bmeshQuads: m_cacheContext->partBmeshQuads) {
|
|
|
|
for (const auto &quad: bmeshQuads.second) {
|
|
|
|
sharedQuadEdges.insert(std::make_pair(std::get<0>(quad), std::get<2>(quad)));
|
|
|
|
sharedQuadEdges.insert(std::make_pair(std::get<1>(quad), std::get<3>(quad)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!sharedQuadEdges.empty()) {
|
|
|
|
resultMeshId = meshQuadify(m_meshliteContext, triangulatedFinalMeshId, sharedQuadEdges, m_forMakePositionKey);
|
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (resultMeshId > 0) {
|
2018-10-26 23:04:45 +00:00
|
|
|
loadGeneratedPositionsToOutcome(m_meshliteContext, triangulatedFinalMeshId);
|
|
|
|
|
|
|
|
std::vector<std::pair<QUuid, QUuid>> sourceNodes;
|
|
|
|
triangleSourceNodeResolve(*m_outcome, sourceNodes);
|
|
|
|
m_outcome->setTriangleSourceNodes(sourceNodes);
|
|
|
|
|
|
|
|
std::map<std::pair<QUuid, QUuid>, QColor> sourceNodeToColorMap;
|
|
|
|
for (const auto &node: m_outcome->nodes)
|
|
|
|
sourceNodeToColorMap.insert({{node.partId, node.nodeId}, node.color});
|
|
|
|
|
|
|
|
std::vector<QColor> triangleColors;
|
|
|
|
triangleColors.resize(m_outcome->triangles.size(), Theme::white);
|
|
|
|
const std::vector<std::pair<QUuid, QUuid>> *triangleSourceNodes = m_outcome->triangleSourceNodes();
|
|
|
|
if (nullptr != triangleSourceNodes) {
|
|
|
|
for (size_t triangleIndex = 0; triangleIndex < m_outcome->triangles.size(); triangleIndex++) {
|
|
|
|
const auto &source = (*triangleSourceNodes)[triangleIndex];
|
|
|
|
triangleColors[triangleIndex] = sourceNodeToColorMap[source];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_mesh = new MeshLoader(m_meshliteContext, resultMeshId, triangulatedFinalMeshId, Theme::white, &triangleColors, m_smoothNormal);
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
2018-04-07 08:44:39 +00:00
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
if (needDeleteCacheContext) {
|
|
|
|
delete m_cacheContext;
|
|
|
|
m_cacheContext = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
meshlite_destroy_context(m_meshliteContext);
|
|
|
|
|
|
|
|
qDebug() << "The mesh generation took" << countTimeConsumed.elapsed() << "milliseconds";
|
2018-10-09 02:19:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MeshGenerator::process()
|
|
|
|
{
|
|
|
|
generate();
|
2018-08-27 08:50:40 +00:00
|
|
|
|
|
|
|
this->moveToThread(QGuiApplication::instance()->thread());
|
2018-04-07 08:44:39 +00:00
|
|
|
emit finished();
|
|
|
|
}
|
2018-08-27 08:50:40 +00:00
|
|
|
|
|
|
|
void MeshGenerator::checkDirtyFlags()
|
|
|
|
{
|
|
|
|
checkIsComponentDirty(QUuid().toString());
|
|
|
|
}
|