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";
|
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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -830,6 +848,11 @@ nodemesh::Combiner::Mesh *MeshGenerator::combineComponentChildGroupMesh(const st
|
||||||
componentCache.outcomeNodes.push_back(it);
|
componentCache.outcomeNodes.push_back(it);
|
||||||
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});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue