2018-04-07 08:44:39 +00:00
|
|
|
#include <vector>
|
|
|
|
#include <QGuiApplication>
|
2018-08-27 08:50:40 +00:00
|
|
|
#include <QElapsedTimer>
|
2018-04-07 08:44:39 +00:00
|
|
|
#include "meshgenerator.h"
|
2018-05-07 16:08:19 +00:00
|
|
|
#include "dust3dutil.h"
|
2018-04-07 08:44:39 +00:00
|
|
|
#include "skeletondocument.h"
|
|
|
|
#include "meshlite.h"
|
|
|
|
#include "modelofflinerender.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-04-07 08:44:39 +00:00
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
bool MeshGenerator::m_enableDebug = false;
|
|
|
|
|
|
|
|
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-04-07 08:44:39 +00:00
|
|
|
MeshGenerator::MeshGenerator(SkeletonSnapshot *snapshot, QThread *thread) :
|
|
|
|
m_snapshot(snapshot),
|
|
|
|
m_mesh(nullptr),
|
|
|
|
m_preview(nullptr),
|
2018-04-26 02:23:22 +00:00
|
|
|
m_thread(thread),
|
2018-08-27 08:50:40 +00:00
|
|
|
m_meshResultContext(nullptr),
|
|
|
|
m_sharedContextWidget(nullptr),
|
|
|
|
m_cacheContext(nullptr)
|
2018-04-07 08:44:39 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
MeshGenerator::~MeshGenerator()
|
|
|
|
{
|
|
|
|
delete m_snapshot;
|
|
|
|
delete m_mesh;
|
|
|
|
delete m_preview;
|
|
|
|
for (const auto &partPreviewIt: m_partPreviewMap) {
|
|
|
|
delete partPreviewIt.second;
|
|
|
|
}
|
|
|
|
for (const auto &render: m_partPreviewRenderMap) {
|
|
|
|
delete render.second;
|
|
|
|
}
|
2018-04-26 02:23:22 +00:00
|
|
|
delete m_meshResultContext;
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
void MeshGenerator::addPartPreviewRequirement(const QString &partId)
|
|
|
|
{
|
2018-04-15 15:15:29 +00:00
|
|
|
//qDebug() << "addPartPreviewRequirement:" << partId;
|
2018-04-07 08:44:39 +00:00
|
|
|
m_requirePartPreviewMap.insert(partId);
|
|
|
|
if (m_partPreviewRenderMap.find(partId) == m_partPreviewRenderMap.end()) {
|
2018-08-27 08:50:40 +00:00
|
|
|
ModelOfflineRender *render = new ModelOfflineRender(m_sharedContextWidget);
|
2018-04-07 08:44:39 +00:00
|
|
|
render->setRenderThread(m_thread);
|
|
|
|
m_partPreviewRenderMap[partId] = render;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
void MeshGenerator::setSharedContextWidget(QOpenGLWidget *widget)
|
|
|
|
{
|
|
|
|
m_sharedContextWidget = widget;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
QImage *MeshGenerator::takePreview()
|
|
|
|
{
|
|
|
|
QImage *resultPreview = m_preview;
|
|
|
|
m_preview = nullptr;
|
|
|
|
return resultPreview;
|
|
|
|
}
|
|
|
|
|
|
|
|
QImage *MeshGenerator::takePartPreview(const QString &partId)
|
|
|
|
{
|
|
|
|
QImage *resultImage = m_partPreviewMap[partId];
|
|
|
|
m_partPreviewMap[partId] = nullptr;
|
|
|
|
return resultImage;
|
|
|
|
}
|
|
|
|
|
2018-04-26 02:23:22 +00:00
|
|
|
MeshResultContext *MeshGenerator::takeMeshResultContext()
|
|
|
|
{
|
|
|
|
MeshResultContext *meshResultContext = m_meshResultContext;
|
|
|
|
m_meshResultContext = nullptr;
|
|
|
|
return meshResultContext;
|
|
|
|
}
|
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
void MeshGenerator::loadVertexSources(void *meshliteContext, int meshId, QUuid partId, const std::map<int, QUuid> &bmeshToNodeIdMap, std::vector<BmeshVertex> &bmeshVertices)
|
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);
|
|
|
|
for (int i = 0, positionIndex = 0; i < positionCount; i++, positionIndex+=3) {
|
|
|
|
BmeshVertex vertex;
|
2018-08-27 08:50:40 +00:00
|
|
|
vertex.partId = partId;
|
|
|
|
auto findNodeId = bmeshToNodeIdMap.find(sourceBuffer[i]);
|
|
|
|
if (findNodeId != bmeshToNodeIdMap.end())
|
|
|
|
vertex.nodeId = findNodeId->second;
|
2018-04-26 02:23:22 +00:00
|
|
|
vertex.position = QVector3D(positionBuffer[positionIndex + 0], positionBuffer[positionIndex + 1], positionBuffer[positionIndex + 2]);
|
2018-08-27 08:50:40 +00:00
|
|
|
bmeshVertices.push_back(vertex);
|
2018-04-26 02:23:22 +00:00
|
|
|
}
|
|
|
|
delete[] positionBuffer;
|
|
|
|
delete[] sourceBuffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MeshGenerator::loadGeneratedPositionsToMeshResultContext(void *meshliteContext, int triangulatedMeshId)
|
|
|
|
{
|
|
|
|
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) {
|
|
|
|
ResultVertex vertex;
|
|
|
|
vertex.position = QVector3D(positionBuffer[positionIndex + 0], positionBuffer[positionIndex + 1], positionBuffer[positionIndex + 2]);
|
2018-05-07 16:08:19 +00:00
|
|
|
verticesMap[i] = m_meshResultContext->vertices.size();
|
|
|
|
m_meshResultContext->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);
|
|
|
|
for (int i = 0, triangleVertIndex = 0, normalIndex=0; i < triangleCount; i++, triangleVertIndex+=3, normalIndex += 3) {
|
|
|
|
ResultTriangle triangle;
|
|
|
|
triangle.indicies[0] = verticesMap[triangleIndexBuffer[triangleVertIndex + 0]];
|
|
|
|
triangle.indicies[1] = verticesMap[triangleIndexBuffer[triangleVertIndex + 1]];
|
|
|
|
triangle.indicies[2] = verticesMap[triangleIndexBuffer[triangleVertIndex + 2]];
|
|
|
|
triangle.normal = QVector3D(normalBuffer[normalIndex + 0], normalBuffer[normalIndex + 1], normalBuffer[normalIndex + 2]);
|
2018-05-07 16:08:19 +00:00
|
|
|
m_meshResultContext->triangles.push_back(triangle);
|
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"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void *MeshGenerator::combinePartMesh(QString partId)
|
|
|
|
{
|
|
|
|
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-08-27 08:50:40 +00:00
|
|
|
QString mirroredPartId;
|
|
|
|
QUuid mirroredPartIdNotAsString;
|
|
|
|
if (xMirrored) {
|
|
|
|
mirroredPartIdNotAsString = QUuid().createUuid();
|
|
|
|
mirroredPartId = mirroredPartIdNotAsString.toString();
|
2018-04-15 12:11:51 +00:00
|
|
|
}
|
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];
|
|
|
|
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());
|
|
|
|
int bmeshNodeId = meshlite_bmesh_add_node(m_meshliteContext, bmeshId, x, y, z, radius);
|
|
|
|
|
|
|
|
nodeToBmeshIdMap[nodeId] = bmeshNodeId;
|
|
|
|
bmeshToNodeIdMap[bmeshNodeId] = nodeId;
|
2018-04-26 02:23:22 +00:00
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
BmeshNode bmeshNode;
|
|
|
|
bmeshNode.partId = QUuid(partId);
|
|
|
|
bmeshNode.origin = QVector3D(x, y, z);
|
|
|
|
bmeshNode.radius = radius;
|
|
|
|
bmeshNode.nodeId = QUuid(nodeId);
|
|
|
|
bmeshNode.color = partColor;
|
|
|
|
cacheBmeshNodes.push_back(bmeshNode);
|
2018-04-30 11:31:09 +00:00
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
if (xMirrored) {
|
|
|
|
bmeshNode.partId = mirroredPartId;
|
|
|
|
bmeshNode.origin.setX(-x);
|
|
|
|
cacheBmeshNodes.push_back(bmeshNode);
|
2018-04-30 11:31:09 +00:00
|
|
|
}
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
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;
|
2018-04-09 11:13:34 +00:00
|
|
|
continue;
|
2018-08-27 08:50:40 +00:00
|
|
|
}
|
|
|
|
auto &edge = findEdge->second;
|
2018-04-26 02:23:22 +00:00
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
QString fromNodeId = valueOfKeyInMapOrEmpty(edge, "from");
|
|
|
|
QString toNodeId = valueOfKeyInMapOrEmpty(edge, "to");
|
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);
|
|
|
|
loadVertexSources(m_meshliteContext, meshId, partIdNotAsString, bmeshToNodeIdMap, cacheBmeshVertices);
|
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);
|
|
|
|
loadVertexSources(m_meshliteContext, xMirroredMeshId, mirroredPartIdNotAsString, bmeshToNodeIdMap, cacheBmeshVertices);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
if (m_requirePartPreviewMap.find(partId) != m_requirePartPreviewMap.end()) {
|
|
|
|
ModelOfflineRender *render = m_partPreviewRenderMap[partId];
|
|
|
|
int trimedMeshId = meshlite_trim(m_meshliteContext, meshId, 1);
|
|
|
|
render->updateMesh(new MeshLoader(m_meshliteContext, trimedMeshId, -1, partColor));
|
|
|
|
QImage *image = new QImage(render->toImage(QSize(Theme::previewImageRenderSize, Theme::previewImageRenderSize)));
|
|
|
|
if (Theme::previewImageSize != Theme::previewImageRenderSize) {
|
|
|
|
int cropOffset = (Theme::previewImageRenderSize - Theme::previewImageSize) / 2;
|
|
|
|
QImage *crop = new QImage(image->copy(cropOffset, cropOffset, Theme::previewImageSize, Theme::previewImageSize));
|
|
|
|
delete image;
|
|
|
|
image = crop;
|
|
|
|
}
|
|
|
|
m_partPreviewMap[partId] = image;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *MeshGenerator::combineComponentMesh(QString componentId, bool *inverse)
|
|
|
|
{
|
|
|
|
QUuid componentIdNotAsString;
|
|
|
|
|
|
|
|
*inverse = false;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isTrueValueString(valueOfKeyInMapOrEmpty(*component, "inverse")))
|
|
|
|
*inverse = true;
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QString linkDataType = valueOfKeyInMapOrEmpty(*component, "linkDataType");
|
|
|
|
if ("partId" == linkDataType) {
|
|
|
|
QString partId = valueOfKeyInMapOrEmpty(*component, "linkData");
|
|
|
|
return combinePartMesh(partId);
|
|
|
|
}
|
|
|
|
|
|
|
|
void *resultMesh = nullptr;
|
|
|
|
|
|
|
|
for (const auto &childId: valueOfKeyInMapOrEmpty(*component, "children").split(",")) {
|
|
|
|
if (childId.isEmpty())
|
|
|
|
continue;
|
|
|
|
bool childInverse = false;
|
|
|
|
void *childCombinedMesh = combineComponentMesh(childId, &childInverse);
|
|
|
|
if (nullptr == childCombinedMesh)
|
|
|
|
continue;
|
|
|
|
if (nullptr == resultMesh) {
|
|
|
|
if (childInverse) {
|
|
|
|
deleteCombinableMesh(childCombinedMesh);
|
|
|
|
} else {
|
|
|
|
resultMesh = childCombinedMesh;
|
|
|
|
}
|
2018-04-11 09:37:28 +00:00
|
|
|
} else {
|
2018-08-27 08:50:40 +00:00
|
|
|
void *newResultMesh = childInverse ? diffCombinableMeshs(resultMesh, childCombinedMesh) : unionCombinableMeshs(resultMesh, childCombinedMesh);
|
|
|
|
deleteCombinableMesh(childCombinedMesh);
|
|
|
|
if (nullptr != newResultMesh) {
|
|
|
|
deleteCombinableMesh(resultMesh);
|
|
|
|
resultMesh = newResultMesh;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!componentIdNotAsString.isNull()) {
|
|
|
|
m_cacheContext->updateComponentCombinableMesh(componentId, resultMesh);
|
|
|
|
}
|
|
|
|
|
|
|
|
return resultMesh;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MeshGenerator::process()
|
|
|
|
{
|
|
|
|
if (nullptr == m_snapshot)
|
|
|
|
return;
|
|
|
|
QElapsedTimer countTimeConsumed;
|
|
|
|
countTimeConsumed.start();
|
|
|
|
|
|
|
|
m_meshliteContext = meshlite_create_context();
|
|
|
|
|
|
|
|
initMeshUtils();
|
|
|
|
m_meshResultContext = new MeshResultContext;
|
|
|
|
|
|
|
|
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()) {
|
|
|
|
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-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-08-27 08:50:40 +00:00
|
|
|
collectParts();
|
|
|
|
checkDirtyFlags();
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
bool inverse = false;
|
|
|
|
void *combinedMesh = combineComponentMesh(QUuid().toString(), &inverse);
|
|
|
|
if (nullptr != combinedMesh) {
|
|
|
|
resultMeshId = convertFromCombinableMesh(m_meshliteContext, combinedMesh);
|
|
|
|
deleteCombinableMesh(combinedMesh);
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
for (const auto &bmeshVertices: m_cacheContext->partBmeshVertices) {
|
|
|
|
m_meshResultContext->bmeshVertices.insert(m_meshResultContext->bmeshVertices.end(),
|
|
|
|
bmeshVertices.second.begin(), bmeshVertices.second.end());
|
2018-04-07 08:44:39 +00:00
|
|
|
}
|
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
for (const auto &bmeshNodes: m_cacheContext->partBmeshNodes) {
|
|
|
|
m_meshResultContext->bmeshNodes.insert(m_meshResultContext->bmeshNodes.end(),
|
|
|
|
bmeshNodes.second.begin(), bmeshNodes.second.end());
|
|
|
|
}
|
2018-04-07 08:44:39 +00:00
|
|
|
|
2018-08-27 08:50:40 +00:00
|
|
|
if (resultMeshId > 0) {
|
|
|
|
resultMeshId = meshlite_combine_coplanar_faces(m_meshliteContext, resultMeshId);
|
|
|
|
if (resultMeshId > 0)
|
|
|
|
resultMeshId = meshlite_fix_hole(m_meshliteContext, resultMeshId);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (resultMeshId > 0) {
|
|
|
|
int triangulatedFinalMeshId = meshlite_triangulate(m_meshliteContext, resultMeshId);
|
|
|
|
loadGeneratedPositionsToMeshResultContext(m_meshliteContext, triangulatedFinalMeshId);
|
|
|
|
m_mesh = new MeshLoader(m_meshliteContext, resultMeshId, triangulatedFinalMeshId, Theme::white, &m_meshResultContext->triangleColors());
|
|
|
|
}
|
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";
|
|
|
|
|
|
|
|
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());
|
|
|
|
}
|