Add color solubility setting

If one part configured color solubility, the generated color texture seam between the neighbor parts will be gradient filled using this part's color with the specified solubility.

Demo: https://twitter.com/jeremyhu2016/status/1132159910815227904
master
Jeremy Hu 2019-05-26 08:54:24 +09:30
parent bb19489ee1
commit 938b6d5d6f
14 changed files with 201 additions and 9 deletions

View File

@ -915,6 +915,8 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set<QUuid> &limitNodeId
part["dirty"] = partIt.second.dirty ? "true" : "false"; part["dirty"] = partIt.second.dirty ? "true" : "false";
if (partIt.second.hasColor) if (partIt.second.hasColor)
part["color"] = partIt.second.color.name(); part["color"] = partIt.second.color.name();
if (partIt.second.colorSolubilityAdjusted())
part["colorSolubility"] = QString::number(partIt.second.colorSolubility);
if (partIt.second.deformThicknessAdjusted()) if (partIt.second.deformThicknessAdjusted())
part["deformThickness"] = QString::number(partIt.second.deformThickness); part["deformThickness"] = QString::number(partIt.second.deformThickness);
if (partIt.second.deformWidthAdjusted()) if (partIt.second.deformWidthAdjusted())
@ -1190,6 +1192,9 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste)
part.color = QColor(colorIt->second); part.color = QColor(colorIt->second);
part.hasColor = true; part.hasColor = true;
} }
const auto &colorSolubilityIt = partKv.second.find("colorSolubility");
if (colorSolubilityIt != partKv.second.end())
part.colorSolubility = colorSolubilityIt->second.toFloat();
const auto &deformThicknessIt = partKv.second.find("deformThickness"); const auto &deformThicknessIt = partKv.second.find("deformThickness");
if (deformThicknessIt != partKv.second.end()) if (deformThicknessIt != partKv.second.end())
part.setDeformThickness(deformThicknessIt->second.toFloat()); part.setDeformThickness(deformThicknessIt->second.toFloat());
@ -2311,6 +2316,21 @@ void Document::setPartTarget(QUuid partId, PartTarget target)
emit skeletonChanged(); emit skeletonChanged();
} }
void Document::setPartColorSolubility(QUuid partId, float solubility)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (qFuzzyCompare(part->second.colorSolubility, solubility))
return;
part->second.colorSolubility = solubility;
part->second.dirty = true;
emit partColorSolubilityChanged(partId);
emit skeletonChanged();
}
void Document::setPartCutRotation(QUuid partId, float cutRotation) void Document::setPartCutRotation(QUuid partId, float cutRotation)
{ {
auto part = partMap.find(partId); auto part = partMap.find(partId);

View File

@ -406,6 +406,7 @@ signals:
void partMaterialIdChanged(QUuid partId); void partMaterialIdChanged(QUuid partId);
void partChamferStateChanged(QUuid partId); void partChamferStateChanged(QUuid partId);
void partTargetChanged(QUuid partId); void partTargetChanged(QUuid partId);
void partColorSolubilityChanged(QUuid partId);
void componentCombineModeChanged(QUuid componentId); void componentCombineModeChanged(QUuid componentId);
void cleanup(); void cleanup();
void originChanged(); void originChanged();
@ -565,6 +566,7 @@ public slots:
void setPartMaterialId(QUuid partId, QUuid materialId); void setPartMaterialId(QUuid partId, QUuid materialId);
void setPartChamferState(QUuid partId, bool chamfered); void setPartChamferState(QUuid partId, bool chamfered);
void setPartTarget(QUuid partId, PartTarget target); void setPartTarget(QUuid partId, PartTarget target);
void setPartColorSolubility(QUuid partId, float solubility);
void setComponentCombineMode(QUuid componentId, CombineMode combineMode); void setComponentCombineMode(QUuid componentId, CombineMode combineMode);
void moveComponentUp(QUuid componentId); void moveComponentUp(QUuid componentId);
void moveComponentDown(QUuid componentId); void moveComponentDown(QUuid componentId);

View File

@ -860,6 +860,7 @@ DocumentWindow::DocumentWindow() :
connect(m_document, &Document::partCutRotationChanged, partTreeWidget, &PartTreeWidget::partCutRotationChanged); connect(m_document, &Document::partCutRotationChanged, partTreeWidget, &PartTreeWidget::partCutRotationChanged);
connect(m_document, &Document::partCutFaceChanged, partTreeWidget, &PartTreeWidget::partCutFaceChanged); connect(m_document, &Document::partCutFaceChanged, partTreeWidget, &PartTreeWidget::partCutFaceChanged);
connect(m_document, &Document::partMaterialIdChanged, partTreeWidget, &PartTreeWidget::partMaterialIdChanged); connect(m_document, &Document::partMaterialIdChanged, partTreeWidget, &PartTreeWidget::partMaterialIdChanged);
connect(m_document, &Document::partColorSolubilityChanged, partTreeWidget, &PartTreeWidget::partColorSolubilityChanged);
connect(m_document, &Document::partRemoved, partTreeWidget, &PartTreeWidget::partRemoved); connect(m_document, &Document::partRemoved, partTreeWidget, &PartTreeWidget::partRemoved);
connect(m_document, &Document::cleanup, partTreeWidget, &PartTreeWidget::removeAllContent); connect(m_document, &Document::cleanup, partTreeWidget, &PartTreeWidget::removeAllContent);
connect(m_document, &Document::partChecked, partTreeWidget, &PartTreeWidget::partChecked); connect(m_document, &Document::partChecked, partTreeWidget, &PartTreeWidget::partChecked);

View File

@ -308,6 +308,11 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
if (!materialIdString.isEmpty()) if (!materialIdString.isEmpty())
materialId = QUuid(materialIdString); materialId = QUuid(materialIdString);
float colorSolubility = 0;
QString colorSolubilityString = valueOfKeyInMapOrEmpty(part, "colorSolubility");
if (!colorSolubilityString.isEmpty())
colorSolubility = colorSolubilityString.toFloat();
auto &partCache = m_cacheContext->parts[partIdString]; auto &partCache = m_cacheContext->parts[partIdString];
partCache.outcomeNodes.clear(); partCache.outcomeNodes.clear();
partCache.outcomeNodeVertices.clear(); partCache.outcomeNodeVertices.clear();
@ -400,6 +405,7 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
outcomeNode.radius = nodeInfo.radius; outcomeNode.radius = nodeInfo.radius;
outcomeNode.color = partColor; outcomeNode.color = partColor;
outcomeNode.materialId = materialId; outcomeNode.materialId = materialId;
outcomeNode.colorSolubility = colorSolubility;
outcomeNode.boneMark = nodeInfo.boneMark; outcomeNode.boneMark = nodeInfo.boneMark;
outcomeNode.mirroredByPartId = mirroredPartIdString; outcomeNode.mirroredByPartId = mirroredPartIdString;
partCache.outcomeNodes.push_back(outcomeNode); partCache.outcomeNodes.push_back(outcomeNode);
@ -622,6 +628,10 @@ QString MeshGenerator::componentColorName(const std::map<QString, QString> *comp
return QString(); return QString();
} }
auto &part = findPart->second; auto &part = findPart->second;
QString colorSolubility = valueOfKeyInMapOrEmpty(part, "colorSolubility");
if (!colorSolubility.isEmpty()) {
return QString("+");
}
QString colorName = valueOfKeyInMapOrEmpty(part, "color"); QString colorName = valueOfKeyInMapOrEmpty(part, "color");
if (colorName.isEmpty()) if (colorName.isEmpty())
return QString("-"); return QString("-");
@ -682,11 +692,15 @@ nodemesh::Combiner::Mesh *MeshGenerator::combineComponentMesh(const QString &com
// Firstly, group by combine mode // Firstly, group by combine mode
int currentGroupIndex = -1; int currentGroupIndex = -1;
auto lastCombineMode = CombineMode::Count; auto lastCombineMode = CombineMode::Count;
bool foundColorSolubilitySetting = false;
for (const auto &childIdString: valueOfKeyInMapOrEmpty(*component, "children").split(",")) { for (const auto &childIdString: valueOfKeyInMapOrEmpty(*component, "children").split(",")) {
if (childIdString.isEmpty()) if (childIdString.isEmpty())
continue; continue;
const auto &child = findComponent(childIdString); const auto &child = findComponent(childIdString);
QString colorName = componentColorName(child); QString colorName = componentColorName(child);
if (colorName == "+") {
foundColorSolubilitySetting = true;
}
auto combineMode = componentCombineMode(child); auto combineMode = componentCombineMode(child);
if (lastCombineMode != combineMode || lastCombineMode == CombineMode::Inversion) { if (lastCombineMode != combineMode || lastCombineMode == CombineMode::Inversion) {
qDebug() << "New group[" << currentGroupIndex << "] for combine mode[" << CombineModeToString(combineMode) << "]"; qDebug() << "New group[" << currentGroupIndex << "] for combine mode[" << CombineModeToString(combineMode) << "]";
@ -748,7 +762,7 @@ nodemesh::Combiner::Mesh *MeshGenerator::combineComponentMesh(const QString &com
} }
multipleMeshes.push_back({childMesh, CombineMode::Normal}); multipleMeshes.push_back({childMesh, CombineMode::Normal});
} }
nodemesh::Combiner::Mesh *subGroupMesh = combineMultipleMeshes(multipleMeshes, false); nodemesh::Combiner::Mesh *subGroupMesh = combineMultipleMeshes(multipleMeshes, foundColorSolubilitySetting);
if (nullptr == subGroupMesh) if (nullptr == subGroupMesh)
continue; continue;
groupMeshes.push_back({subGroupMesh, group.first}); groupMeshes.push_back({subGroupMesh, group.first});
@ -806,6 +820,10 @@ nodemesh::Combiner::Mesh *MeshGenerator::combineMultipleMeshes(const std::vector
} }
} }
} }
if (nullptr != mesh && mesh->isNull()) {
delete mesh;
mesh = nullptr;
}
return mesh; return mesh;
} }
@ -831,6 +849,11 @@ nodemesh::Combiner::Mesh *MeshGenerator::combineComponentChildGroupMesh(const st
for (const auto &it: childComponentCache.outcomeNodeVertices) for (const auto &it: childComponentCache.outcomeNodeVertices)
componentCache.outcomeNodeVertices.push_back(it); componentCache.outcomeNodeVertices.push_back(it);
if (nullptr == subMesh || subMesh->isNull()) {
delete subMesh;
continue;
}
multipleMeshes.push_back({subMesh, childCombineMode}); multipleMeshes.push_back({subMesh, childCombineMode});
} }
return combineMultipleMeshes(multipleMeshes); return combineMultipleMeshes(multipleMeshes);

View File

@ -18,6 +18,7 @@ struct OutcomeNode
QVector3D origin; QVector3D origin;
float radius = 0; float radius = 0;
QColor color; QColor color;
float colorSolubility = 0;
QUuid materialId; QUuid materialId;
QUuid mirrorFromPartId; QUuid mirrorFromPartId;
QUuid mirroredByPartId; QUuid mirroredByPartId;

View File

@ -1003,6 +1003,17 @@ void PartTreeWidget::partMaterialIdChanged(QUuid partId)
widget->updateColorButton(); widget->updateColorButton();
} }
void PartTreeWidget::partColorSolubilityChanged(QUuid partId)
{
auto item = m_partItemMap.find(partId);
if (item == m_partItemMap.end()) {
qDebug() << "Part item not found:" << partId;
return;
}
PartWidget *widget = (PartWidget *)itemWidget(item->second, 0);
widget->updateColorButton();
}
void PartTreeWidget::partChecked(QUuid partId) void PartTreeWidget::partChecked(QUuid partId)
{ {
auto item = m_partItemMap.find(partId); auto item = m_partItemMap.find(partId);

View File

@ -67,6 +67,7 @@ public slots:
void partCutRotationChanged(QUuid partId); void partCutRotationChanged(QUuid partId);
void partCutFaceChanged(QUuid partId); void partCutFaceChanged(QUuid partId);
void partMaterialIdChanged(QUuid partId); void partMaterialIdChanged(QUuid partId);
void partColorSolubilityChanged(QUuid partId);
void partChecked(QUuid partId); void partChecked(QUuid partId);
void partUnchecked(QUuid partId); void partUnchecked(QUuid partId);
void groupChanged(QTreeWidgetItem *item, int column); void groupChanged(QTreeWidgetItem *item, int column);

View File

@ -166,6 +166,7 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
connect(this, &PartWidget::setPartCutFaceLinkedId, m_document, &Document::setPartCutFaceLinkedId); connect(this, &PartWidget::setPartCutFaceLinkedId, m_document, &Document::setPartCutFaceLinkedId);
connect(this, &PartWidget::setPartColorState, m_document, &Document::setPartColorState); connect(this, &PartWidget::setPartColorState, m_document, &Document::setPartColorState);
connect(this, &PartWidget::setPartMaterialId, m_document, &Document::setPartMaterialId); connect(this, &PartWidget::setPartMaterialId, m_document, &Document::setPartMaterialId);
connect(this, &PartWidget::setPartColorSolubility, m_document, &Document::setPartColorSolubility);
connect(this, &PartWidget::checkPart, m_document, &Document::checkPart); connect(this, &PartWidget::checkPart, m_document, &Document::checkPart);
connect(this, &PartWidget::enableBackgroundBlur, m_document, &Document::enableBackgroundBlur); connect(this, &PartWidget::enableBackgroundBlur, m_document, &Document::enableBackgroundBlur);
connect(this, &PartWidget::disableBackgroundBlur, m_document, &Document::disableBackgroundBlur); connect(this, &PartWidget::disableBackgroundBlur, m_document, &Document::disableBackgroundBlur);
@ -375,8 +376,31 @@ void PartWidget::showColorSettingPopup(const QPoint &pos)
} }
}); });
FloatNumberWidget *colorSolubilityWidget = new FloatNumberWidget;
colorSolubilityWidget->setItemName(tr("Solubility"));
colorSolubilityWidget->setRange(0.0, 1.0);
colorSolubilityWidget->setValue(part->colorSolubility);
connect(colorSolubilityWidget, &FloatNumberWidget::valueChanged, [=](float value) {
emit setPartColorSolubility(m_partId, value);
emit groupOperationAdded();
});
QPushButton *colorSolubilityEraser = new QPushButton(QChar(fa::eraser));
initToolButton(colorSolubilityEraser);
connect(colorSolubilityEraser, &QPushButton::clicked, [=]() {
colorSolubilityWidget->setValue(0.0);
emit groupOperationAdded();
});
QHBoxLayout *colorSolubilityLayout = new QHBoxLayout;
colorSolubilityLayout->addWidget(colorSolubilityEraser);
colorSolubilityLayout->addWidget(colorSolubilityWidget);
QVBoxLayout *mainLayout = new QVBoxLayout; QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(colorLayout); mainLayout->addLayout(colorLayout);
mainLayout->addLayout(colorSolubilityLayout);
if (m_document->materialIdList.empty()) { if (m_document->materialIdList.empty()) {
InfoLabel *infoLabel = new InfoLabel; InfoLabel *infoLabel = new InfoLabel;
@ -712,7 +736,7 @@ void PartWidget::updateColorButton()
qDebug() << "Part not found:" << m_partId; qDebug() << "Part not found:" << m_partId;
return; return;
} }
if (part->hasColor || part->materialAdjusted()) if (part->hasColor || part->materialAdjusted() || part->colorSolubilityAdjusted())
updateButton(m_colorButton, QChar(fa::eyedropper), true); updateButton(m_colorButton, QChar(fa::eyedropper), true);
else else
updateButton(m_colorButton, QChar(fa::eyedropper), false); updateButton(m_colorButton, QChar(fa::eyedropper), false);

View File

@ -25,6 +25,7 @@ signals:
void setPartCutFace(QUuid partId, CutFace cutFace); void setPartCutFace(QUuid partId, CutFace cutFace);
void setPartCutFaceLinkedId(QUuid partId, QUuid linkedId); void setPartCutFaceLinkedId(QUuid partId, QUuid linkedId);
void setPartMaterialId(QUuid partId, QUuid materialId); void setPartMaterialId(QUuid partId, QUuid materialId);
void setPartColorSolubility(QUuid partId, float colorSolubility);
void movePartUp(QUuid partId); void movePartUp(QUuid partId);
void movePartDown(QUuid partId); void movePartDown(QUuid partId);
void movePartToTop(QUuid partId); void movePartToTop(QUuid partId);

View File

@ -93,6 +93,7 @@ public:
QUuid cutFaceLinkedId; QUuid cutFaceLinkedId;
QUuid materialId; QUuid materialId;
PartTarget target; PartTarget target;
float colorSolubility;
SkeletonPart(const QUuid &withId=QUuid()) : SkeletonPart(const QUuid &withId=QUuid()) :
visible(true), visible(true),
locked(false), locked(false),
@ -110,7 +111,8 @@ public:
dirty(true), dirty(true),
cutRotation(0.0), cutRotation(0.0),
cutFace(CutFace::Quad), cutFace(CutFace::Quad),
target(PartTarget::Model) target(PartTarget::Model),
colorSolubility(0.0)
{ {
id = withId.isNull() ? QUuid::createUuid() : withId; id = withId.isNull() ? QUuid::createUuid() : withId;
} }
@ -164,6 +166,10 @@ public:
{ {
return deformThicknessAdjusted() || deformWidthAdjusted(); return deformThicknessAdjusted() || deformWidthAdjusted();
} }
bool colorSolubilityAdjusted() const
{
return fabs(colorSolubility - 0.0) >= 0.01;
}
bool cutRotationAdjusted() const bool cutRotationAdjusted() const
{ {
return fabs(cutRotation - 0.0) >= 0.01; return fabs(cutRotation - 0.0) >= 0.01;
@ -206,6 +212,7 @@ public:
dirty = other.dirty; dirty = other.dirty;
materialId = other.materialId; materialId = other.materialId;
target = other.target; target = other.target;
colorSolubility = other.colorSolubility;
} }
void updatePreviewMesh(MeshLoader *previewMesh) void updatePreviewMesh(MeshLoader *previewMesh)
{ {

View File

@ -3,6 +3,7 @@
#include <QRegion> #include <QRegion>
#include <QPolygon> #include <QPolygon>
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QRadialGradient>
#include "texturegenerator.h" #include "texturegenerator.h"
#include "theme.h" #include "theme.h"
#include "util.h" #include "util.h"
@ -231,9 +232,11 @@ void TextureGenerator::generate()
std::map<QUuid, QColor> partColorMap; std::map<QUuid, QColor> partColorMap;
std::map<std::pair<QUuid, QUuid>, const OutcomeNode *> nodeMap; std::map<std::pair<QUuid, QUuid>, const OutcomeNode *> nodeMap;
std::map<QUuid, float> partColorSolubilityMap;
for (const auto &item: m_outcome->nodes) { for (const auto &item: m_outcome->nodes) {
nodeMap.insert({{item.partId, item.nodeId}, &item}); nodeMap.insert({{item.partId, item.nodeId}, &item});
partColorMap.insert({item.partId, item.color}); partColorMap.insert({item.partId, item.color});
partColorSolubilityMap.insert({item.partId, item.colorSolubility});
} }
auto createImageBeginTime = countTimeConsumed.elapsed(); auto createImageBeginTime = countTimeConsumed.elapsed();
@ -316,6 +319,81 @@ void TextureGenerator::generate()
} }
} }
auto drawGradient = [&](const QUuid &partId, size_t triangleIndex, size_t firstVertexIndex, size_t secondVertexIndex,
const QUuid &neighborPartId) {
const std::vector<QVector2D> &uv = triangleVertexUvs[triangleIndex];
const auto &allRects = partUvRects.find(partId);
if (allRects == partUvRects.end()) {
qDebug() << "Found part uv rects failed";
return;
}
const auto &firstPoint = uv[firstVertexIndex];
const auto &secondPoint = uv[secondVertexIndex];
auto edgeLength = firstPoint.distanceToPoint(secondPoint);
auto middlePoint = (firstPoint + secondPoint) / 2.0;
const auto &findColor = partColorMap.find(partId);
if (findColor == partColorMap.end())
return;
const auto &findNeighborColorSolubility = partColorSolubilityMap.find(neighborPartId);
if (findNeighborColorSolubility == partColorSolubilityMap.end())
return;
if (qFuzzyIsNull(findNeighborColorSolubility->second))
return;
const auto &findNeighborColor = partColorMap.find(neighborPartId);
if (findNeighborColor == partColorMap.end())
return;
for (const auto &it: allRects->second) {
if (it.contains(firstPoint.x(), firstPoint.y()) ||
it.contains(secondPoint.x(), secondPoint.y())) {
float finalRadius = (it.width() + it.height()) * 0.5 * findNeighborColorSolubility->second;
if (finalRadius < edgeLength)
finalRadius = edgeLength;
QRadialGradient gradient(QPointF(middlePoint.x() * TextureGenerator::m_textureSize,
middlePoint.y() * TextureGenerator::m_textureSize),
finalRadius * TextureGenerator::m_textureSize);
gradient.setColorAt(0.0, findNeighborColor->second);
gradient.setColorAt(1.0, Qt::transparent);
QRectF fillTarget((middlePoint.x() - finalRadius),
(middlePoint.y() - finalRadius),
(finalRadius + finalRadius),
(finalRadius + finalRadius));
auto clippedRect = it.intersected(fillTarget);
texturePainter.fillRect(clippedRect.left() * TextureGenerator::m_textureSize,
clippedRect.top() * TextureGenerator::m_textureSize,
clippedRect.width() * TextureGenerator::m_textureSize,
clippedRect.height() * TextureGenerator::m_textureSize,
gradient);
break;
}
}
};
std::map<std::pair<size_t, size_t>, std::tuple<size_t, size_t, size_t>> halfEdgeToTriangleMap;
for (size_t i = 0; i < m_outcome->triangles.size(); ++i) {
const auto &triangleIndices = m_outcome->triangles[i];
if (triangleIndices.size() != 3) {
qDebug() << "Found invalid triangle indices";
continue;
}
for (size_t j = 0; j < triangleIndices.size(); ++j) {
size_t k = (j + 1) % triangleIndices.size();
halfEdgeToTriangleMap.insert(std::make_pair(std::make_pair(triangleIndices[j], triangleIndices[k]),
std::make_tuple(i, j, k)));
}
}
for (const auto &it: halfEdgeToTriangleMap) {
auto oppositeHalfEdge = std::make_pair(it.first.second, it.first.first);
const auto &opposite = halfEdgeToTriangleMap.find(oppositeHalfEdge);
if (opposite == halfEdgeToTriangleMap.end())
continue;
const std::pair<QUuid, QUuid> &source = triangleSourceNodes[std::get<0>(it.second)];
const std::pair<QUuid, QUuid> &oppositeSource = triangleSourceNodes[std::get<0>(opposite->second)];
if (source.first == oppositeSource.first)
continue;
drawGradient(source.first, std::get<0>(it.second), std::get<1>(it.second), std::get<2>(it.second), oppositeSource.first);
drawGradient(oppositeSource.first, std::get<0>(opposite->second), std::get<1>(opposite->second), std::get<2>(opposite->second), source.first);
}
for (auto i = 0u; i < triangleVertexUvs.size(); i++) { for (auto i = 0u; i < triangleVertexUvs.size(); i++) {
QPainterPath path; QPainterPath path;
const std::vector<QVector2D> &uv = triangleVertexUvs[i]; const std::vector<QVector2D> &uv = triangleVertexUvs[i];
@ -337,12 +415,12 @@ void TextureGenerator::generate()
texturePainter.drawImage(0, 0, findColorTextureResult->second); texturePainter.drawImage(0, 0, findColorTextureResult->second);
texturePainter.setClipping(false); texturePainter.setClipping(false);
} else { } else {
auto findSourceNodeResult = nodeMap.find(source); //auto findSourceNodeResult = nodeMap.find(source);
if (findSourceNodeResult != nodeMap.end() && nullptr != findSourceNodeResult->second) { //if (findSourceNodeResult != nodeMap.end() && nullptr != findSourceNodeResult->second) {
texturePainter.fillPath(path, QBrush(findSourceNodeResult->second->color)); // texturePainter.fillPath(path, QBrush(findSourceNodeResult->second->color));
} else { //} else {
texturePainter.fillPath(path, QBrush(m_defaultTextureColor)); // texturePainter.fillPath(path, QBrush(m_defaultTextureColor));
} //}
} }
// Copy normal texture if there is one // Copy normal texture if there is one
auto findNormalTextureResult = m_partNormalTextureMap.find(source.first); auto findNormalTextureResult = m_partNormalTextureMap.find(source.first);

View File

@ -84,5 +84,12 @@ void fetchFromCgalMesh(typename CGAL::Surface_mesh<typename Kernel::Point_3> *me
} }
} }
template <class Kernel>
bool isNullCgalMesh(typename CGAL::Surface_mesh<typename Kernel::Point_3> *mesh)
{
typename CGAL::Surface_mesh<typename Kernel::Point_3>::Face_range faceRage = mesh->faces();
return faceRage.begin() == faceRage.end();
}
#endif #endif

View File

@ -47,12 +47,14 @@ Combiner::Mesh::Mesh(const std::vector<QVector3D> &vertices, const std::vector<s
} }
} }
m_privateData = cgalMesh; m_privateData = cgalMesh;
validate();
} }
Combiner::Mesh::Mesh(const Mesh &other) Combiner::Mesh::Mesh(const Mesh &other)
{ {
if (other.m_privateData) { if (other.m_privateData) {
m_privateData = new CgalMesh(*(CgalMesh *)other.m_privateData); m_privateData = new CgalMesh(*(CgalMesh *)other.m_privateData);
validate();
} }
} }
@ -158,4 +160,16 @@ Combiner::Mesh *Combiner::combine(const Mesh &firstMesh, const Mesh &secondMesh,
return mesh; return mesh;
} }
void Combiner::Mesh::validate()
{
if (nullptr == m_privateData)
return;
CgalMesh *exactMesh = (CgalMesh *)m_privateData;
if (isNullCgalMesh<CgalKernel>(exactMesh)) {
delete exactMesh;
m_privateData = nullptr;
}
}
} }

View File

@ -38,6 +38,8 @@ public:
private: private:
void *m_privateData = nullptr; void *m_privateData = nullptr;
bool m_isSelfIntersected = false; bool m_isSelfIntersected = false;
void validate();
}; };
static Mesh *combine(const Mesh &firstMesh, const Mesh &secondMesh, Method method, static Mesh *combine(const Mesh &firstMesh, const Mesh &secondMesh, Method method,