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/1132159910815227904master
parent
bb19489ee1
commit
938b6d5d6f
|
@ -915,6 +915,8 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set<QUuid> &limitNodeId
|
|||
part["dirty"] = partIt.second.dirty ? "true" : "false";
|
||||
if (partIt.second.hasColor)
|
||||
part["color"] = partIt.second.color.name();
|
||||
if (partIt.second.colorSolubilityAdjusted())
|
||||
part["colorSolubility"] = QString::number(partIt.second.colorSolubility);
|
||||
if (partIt.second.deformThicknessAdjusted())
|
||||
part["deformThickness"] = QString::number(partIt.second.deformThickness);
|
||||
if (partIt.second.deformWidthAdjusted())
|
||||
|
@ -1190,6 +1192,9 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste)
|
|||
part.color = QColor(colorIt->second);
|
||||
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");
|
||||
if (deformThicknessIt != partKv.second.end())
|
||||
part.setDeformThickness(deformThicknessIt->second.toFloat());
|
||||
|
@ -2311,6 +2316,21 @@ void Document::setPartTarget(QUuid partId, PartTarget target)
|
|||
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)
|
||||
{
|
||||
auto part = partMap.find(partId);
|
||||
|
|
|
@ -406,6 +406,7 @@ signals:
|
|||
void partMaterialIdChanged(QUuid partId);
|
||||
void partChamferStateChanged(QUuid partId);
|
||||
void partTargetChanged(QUuid partId);
|
||||
void partColorSolubilityChanged(QUuid partId);
|
||||
void componentCombineModeChanged(QUuid componentId);
|
||||
void cleanup();
|
||||
void originChanged();
|
||||
|
@ -565,6 +566,7 @@ public slots:
|
|||
void setPartMaterialId(QUuid partId, QUuid materialId);
|
||||
void setPartChamferState(QUuid partId, bool chamfered);
|
||||
void setPartTarget(QUuid partId, PartTarget target);
|
||||
void setPartColorSolubility(QUuid partId, float solubility);
|
||||
void setComponentCombineMode(QUuid componentId, CombineMode combineMode);
|
||||
void moveComponentUp(QUuid componentId);
|
||||
void moveComponentDown(QUuid componentId);
|
||||
|
|
|
@ -860,6 +860,7 @@ DocumentWindow::DocumentWindow() :
|
|||
connect(m_document, &Document::partCutRotationChanged, partTreeWidget, &PartTreeWidget::partCutRotationChanged);
|
||||
connect(m_document, &Document::partCutFaceChanged, partTreeWidget, &PartTreeWidget::partCutFaceChanged);
|
||||
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::cleanup, partTreeWidget, &PartTreeWidget::removeAllContent);
|
||||
connect(m_document, &Document::partChecked, partTreeWidget, &PartTreeWidget::partChecked);
|
||||
|
|
|
@ -308,6 +308,11 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
|
|||
if (!materialIdString.isEmpty())
|
||||
materialId = QUuid(materialIdString);
|
||||
|
||||
float colorSolubility = 0;
|
||||
QString colorSolubilityString = valueOfKeyInMapOrEmpty(part, "colorSolubility");
|
||||
if (!colorSolubilityString.isEmpty())
|
||||
colorSolubility = colorSolubilityString.toFloat();
|
||||
|
||||
auto &partCache = m_cacheContext->parts[partIdString];
|
||||
partCache.outcomeNodes.clear();
|
||||
partCache.outcomeNodeVertices.clear();
|
||||
|
@ -400,6 +405,7 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
|
|||
outcomeNode.radius = nodeInfo.radius;
|
||||
outcomeNode.color = partColor;
|
||||
outcomeNode.materialId = materialId;
|
||||
outcomeNode.colorSolubility = colorSolubility;
|
||||
outcomeNode.boneMark = nodeInfo.boneMark;
|
||||
outcomeNode.mirroredByPartId = mirroredPartIdString;
|
||||
partCache.outcomeNodes.push_back(outcomeNode);
|
||||
|
@ -622,6 +628,10 @@ QString MeshGenerator::componentColorName(const std::map<QString, QString> *comp
|
|||
return QString();
|
||||
}
|
||||
auto &part = findPart->second;
|
||||
QString colorSolubility = valueOfKeyInMapOrEmpty(part, "colorSolubility");
|
||||
if (!colorSolubility.isEmpty()) {
|
||||
return QString("+");
|
||||
}
|
||||
QString colorName = valueOfKeyInMapOrEmpty(part, "color");
|
||||
if (colorName.isEmpty())
|
||||
return QString("-");
|
||||
|
@ -682,11 +692,15 @@ nodemesh::Combiner::Mesh *MeshGenerator::combineComponentMesh(const QString &com
|
|||
// Firstly, group by combine mode
|
||||
int currentGroupIndex = -1;
|
||||
auto lastCombineMode = CombineMode::Count;
|
||||
bool foundColorSolubilitySetting = false;
|
||||
for (const auto &childIdString: valueOfKeyInMapOrEmpty(*component, "children").split(",")) {
|
||||
if (childIdString.isEmpty())
|
||||
continue;
|
||||
const auto &child = findComponent(childIdString);
|
||||
QString colorName = componentColorName(child);
|
||||
if (colorName == "+") {
|
||||
foundColorSolubilitySetting = true;
|
||||
}
|
||||
auto combineMode = componentCombineMode(child);
|
||||
if (lastCombineMode != combineMode || lastCombineMode == CombineMode::Inversion) {
|
||||
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});
|
||||
}
|
||||
nodemesh::Combiner::Mesh *subGroupMesh = combineMultipleMeshes(multipleMeshes, false);
|
||||
nodemesh::Combiner::Mesh *subGroupMesh = combineMultipleMeshes(multipleMeshes, foundColorSolubilitySetting);
|
||||
if (nullptr == subGroupMesh)
|
||||
continue;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -830,6 +848,11 @@ nodemesh::Combiner::Mesh *MeshGenerator::combineComponentChildGroupMesh(const st
|
|||
componentCache.outcomeNodes.push_back(it);
|
||||
for (const auto &it: childComponentCache.outcomeNodeVertices)
|
||||
componentCache.outcomeNodeVertices.push_back(it);
|
||||
|
||||
if (nullptr == subMesh || subMesh->isNull()) {
|
||||
delete subMesh;
|
||||
continue;
|
||||
}
|
||||
|
||||
multipleMeshes.push_back({subMesh, childCombineMode});
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ struct OutcomeNode
|
|||
QVector3D origin;
|
||||
float radius = 0;
|
||||
QColor color;
|
||||
float colorSolubility = 0;
|
||||
QUuid materialId;
|
||||
QUuid mirrorFromPartId;
|
||||
QUuid mirroredByPartId;
|
||||
|
|
|
@ -1003,6 +1003,17 @@ void PartTreeWidget::partMaterialIdChanged(QUuid partId)
|
|||
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)
|
||||
{
|
||||
auto item = m_partItemMap.find(partId);
|
||||
|
|
|
@ -67,6 +67,7 @@ public slots:
|
|||
void partCutRotationChanged(QUuid partId);
|
||||
void partCutFaceChanged(QUuid partId);
|
||||
void partMaterialIdChanged(QUuid partId);
|
||||
void partColorSolubilityChanged(QUuid partId);
|
||||
void partChecked(QUuid partId);
|
||||
void partUnchecked(QUuid partId);
|
||||
void groupChanged(QTreeWidgetItem *item, int column);
|
||||
|
|
|
@ -166,6 +166,7 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
|
|||
connect(this, &PartWidget::setPartCutFaceLinkedId, m_document, &Document::setPartCutFaceLinkedId);
|
||||
connect(this, &PartWidget::setPartColorState, m_document, &Document::setPartColorState);
|
||||
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::enableBackgroundBlur, m_document, &Document::enableBackgroundBlur);
|
||||
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;
|
||||
mainLayout->addLayout(colorLayout);
|
||||
mainLayout->addLayout(colorSolubilityLayout);
|
||||
|
||||
if (m_document->materialIdList.empty()) {
|
||||
InfoLabel *infoLabel = new InfoLabel;
|
||||
|
@ -712,7 +736,7 @@ void PartWidget::updateColorButton()
|
|||
qDebug() << "Part not found:" << m_partId;
|
||||
return;
|
||||
}
|
||||
if (part->hasColor || part->materialAdjusted())
|
||||
if (part->hasColor || part->materialAdjusted() || part->colorSolubilityAdjusted())
|
||||
updateButton(m_colorButton, QChar(fa::eyedropper), true);
|
||||
else
|
||||
updateButton(m_colorButton, QChar(fa::eyedropper), false);
|
||||
|
|
|
@ -25,6 +25,7 @@ signals:
|
|||
void setPartCutFace(QUuid partId, CutFace cutFace);
|
||||
void setPartCutFaceLinkedId(QUuid partId, QUuid linkedId);
|
||||
void setPartMaterialId(QUuid partId, QUuid materialId);
|
||||
void setPartColorSolubility(QUuid partId, float colorSolubility);
|
||||
void movePartUp(QUuid partId);
|
||||
void movePartDown(QUuid partId);
|
||||
void movePartToTop(QUuid partId);
|
||||
|
|
|
@ -93,6 +93,7 @@ public:
|
|||
QUuid cutFaceLinkedId;
|
||||
QUuid materialId;
|
||||
PartTarget target;
|
||||
float colorSolubility;
|
||||
SkeletonPart(const QUuid &withId=QUuid()) :
|
||||
visible(true),
|
||||
locked(false),
|
||||
|
@ -110,7 +111,8 @@ public:
|
|||
dirty(true),
|
||||
cutRotation(0.0),
|
||||
cutFace(CutFace::Quad),
|
||||
target(PartTarget::Model)
|
||||
target(PartTarget::Model),
|
||||
colorSolubility(0.0)
|
||||
{
|
||||
id = withId.isNull() ? QUuid::createUuid() : withId;
|
||||
}
|
||||
|
@ -164,6 +166,10 @@ public:
|
|||
{
|
||||
return deformThicknessAdjusted() || deformWidthAdjusted();
|
||||
}
|
||||
bool colorSolubilityAdjusted() const
|
||||
{
|
||||
return fabs(colorSolubility - 0.0) >= 0.01;
|
||||
}
|
||||
bool cutRotationAdjusted() const
|
||||
{
|
||||
return fabs(cutRotation - 0.0) >= 0.01;
|
||||
|
@ -206,6 +212,7 @@ public:
|
|||
dirty = other.dirty;
|
||||
materialId = other.materialId;
|
||||
target = other.target;
|
||||
colorSolubility = other.colorSolubility;
|
||||
}
|
||||
void updatePreviewMesh(MeshLoader *previewMesh)
|
||||
{
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <QRegion>
|
||||
#include <QPolygon>
|
||||
#include <QElapsedTimer>
|
||||
#include <QRadialGradient>
|
||||
#include "texturegenerator.h"
|
||||
#include "theme.h"
|
||||
#include "util.h"
|
||||
|
@ -231,9 +232,11 @@ void TextureGenerator::generate()
|
|||
|
||||
std::map<QUuid, QColor> partColorMap;
|
||||
std::map<std::pair<QUuid, QUuid>, const OutcomeNode *> nodeMap;
|
||||
std::map<QUuid, float> partColorSolubilityMap;
|
||||
for (const auto &item: m_outcome->nodes) {
|
||||
nodeMap.insert({{item.partId, item.nodeId}, &item});
|
||||
partColorMap.insert({item.partId, item.color});
|
||||
partColorSolubilityMap.insert({item.partId, item.colorSolubility});
|
||||
}
|
||||
|
||||
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++) {
|
||||
QPainterPath path;
|
||||
const std::vector<QVector2D> &uv = triangleVertexUvs[i];
|
||||
|
@ -337,12 +415,12 @@ void TextureGenerator::generate()
|
|||
texturePainter.drawImage(0, 0, findColorTextureResult->second);
|
||||
texturePainter.setClipping(false);
|
||||
} else {
|
||||
auto findSourceNodeResult = nodeMap.find(source);
|
||||
if (findSourceNodeResult != nodeMap.end() && nullptr != findSourceNodeResult->second) {
|
||||
texturePainter.fillPath(path, QBrush(findSourceNodeResult->second->color));
|
||||
} else {
|
||||
texturePainter.fillPath(path, QBrush(m_defaultTextureColor));
|
||||
}
|
||||
//auto findSourceNodeResult = nodeMap.find(source);
|
||||
//if (findSourceNodeResult != nodeMap.end() && nullptr != findSourceNodeResult->second) {
|
||||
// texturePainter.fillPath(path, QBrush(findSourceNodeResult->second->color));
|
||||
//} else {
|
||||
// texturePainter.fillPath(path, QBrush(m_defaultTextureColor));
|
||||
//}
|
||||
}
|
||||
// Copy normal texture if there is one
|
||||
auto findNormalTextureResult = m_partNormalTextureMap.find(source.first);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -47,12 +47,14 @@ Combiner::Mesh::Mesh(const std::vector<QVector3D> &vertices, const std::vector<s
|
|||
}
|
||||
}
|
||||
m_privateData = cgalMesh;
|
||||
validate();
|
||||
}
|
||||
|
||||
Combiner::Mesh::Mesh(const Mesh &other)
|
||||
{
|
||||
if (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;
|
||||
}
|
||||
|
||||
void Combiner::Mesh::validate()
|
||||
{
|
||||
if (nullptr == m_privateData)
|
||||
return;
|
||||
|
||||
CgalMesh *exactMesh = (CgalMesh *)m_privateData;
|
||||
if (isNullCgalMesh<CgalKernel>(exactMesh)) {
|
||||
delete exactMesh;
|
||||
m_privateData = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,6 +38,8 @@ public:
|
|||
private:
|
||||
void *m_privateData = nullptr;
|
||||
bool m_isSelfIntersected = false;
|
||||
|
||||
void validate();
|
||||
};
|
||||
|
||||
static Mesh *combine(const Mesh &firstMesh, const Mesh &secondMesh, Method method,
|
||||
|
|
Loading…
Reference in New Issue