Skip to content

Commit

Permalink
Implement normal smooth cutoff configure
Browse files Browse the repository at this point in the history
  • Loading branch information
huxingyi committed Dec 19, 2022
1 parent adf1918 commit 9e5620b
Show file tree
Hide file tree
Showing 11 changed files with 89 additions and 21 deletions.
4 changes: 2 additions & 2 deletions application/sources/bone_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ void BoneGenerator::process()
dust3d::smoothNormal(it.second.vertices,
it.second.triangles,
previewTriangleNormals,
0,
nullptr,
&previewTriangleVertexNormals);
std::vector<std::tuple<dust3d::Color, float /*metalness*/, float /*roughness*/>> vertexProperties(it.second.vertexColors.size());
for (size_t i = 0; i < vertexProperties.size(); ++i) {
Expand Down Expand Up @@ -108,7 +108,7 @@ void BoneGenerator::process()
dust3d::smoothNormal(preview.vertices,
preview.triangles,
previewTriangleNormals,
0,
nullptr,
&previewTriangleVertexNormals);
std::vector<std::tuple<dust3d::Color, float /*metalness*/, float /*roughness*/>> vertexProperties(preview.vertexColors.size());
for (size_t i = 0; i < vertexProperties.size(); ++i) {
Expand Down
54 changes: 54 additions & 0 deletions application/sources/component_property_widget.cc
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,38 @@ ComponentPropertyWidget::ComponentPropertyWidget(Document* document,
cutFaceGroupBox->setLayout(cutFaceLayout);
}

QGroupBox* smoothGroupBox = nullptr;
if (!m_componentIds.empty()) {
FloatNumberWidget* smoothCutoffDegreesWidget = new FloatNumberWidget;
smoothCutoffDegreesWidget->setItemName(tr("Cutoff"));
smoothCutoffDegreesWidget->setRange(0.0, 180.0);
smoothCutoffDegreesWidget->setValue(lastSmoothCutoffDegrees());

connect(smoothCutoffDegreesWidget, &FloatNumberWidget::valueChanged, [=](float value) {
for (const auto& partId : m_partIds)
emit setPartSmoothCutoffDegrees(partId, value);
emit groupOperationAdded();
});

QPushButton* smoothCutoffDegreesEraser = new QPushButton(Theme::awesome()->icon(fa::eraser), "");
Theme::initIconButton(smoothCutoffDegreesEraser);

connect(smoothCutoffDegreesEraser, &QPushButton::clicked, [=]() {
smoothCutoffDegreesWidget->setValue(0.0);
emit groupOperationAdded();
});

QHBoxLayout* smoothCutoffDegreesLayout = new QHBoxLayout;
smoothCutoffDegreesLayout->addWidget(smoothCutoffDegreesEraser);
smoothCutoffDegreesLayout->addWidget(smoothCutoffDegreesWidget);

QVBoxLayout* smoothGroupLayout = new QVBoxLayout;
smoothGroupLayout->addLayout(smoothCutoffDegreesLayout);

smoothGroupBox = new QGroupBox(tr("Normal Smooth"));
smoothGroupBox->setLayout(smoothGroupLayout);
}

QGroupBox* colorImageGroupBox = nullptr;
if (!m_componentIds.empty()) {
ImagePreviewWidget* colorImagePreviewWidget = new ImagePreviewWidget;
Expand Down Expand Up @@ -280,6 +312,8 @@ ComponentPropertyWidget::ComponentPropertyWidget(Document* document,
mainLayout->addWidget(deformGroupBox);
if (nullptr != cutFaceGroupBox)
mainLayout->addWidget(cutFaceGroupBox);
if (nullptr != smoothGroupBox)
mainLayout->addWidget(smoothGroupBox);
mainLayout->addLayout(skinLayout);
mainLayout->setSizeConstraint(QLayout::SetFixedSize);

Expand All @@ -294,6 +328,7 @@ ComponentPropertyWidget::ComponentPropertyWidget(Document* document,
connect(this, &ComponentPropertyWidget::setPartChamferState, m_document, &Document::setPartChamferState);
connect(this, &ComponentPropertyWidget::setPartRoundState, m_document, &Document::setPartRoundState);
connect(this, &ComponentPropertyWidget::setPartColorImage, m_document, &Document::setPartColorImage);
connect(this, &ComponentPropertyWidget::setPartSmoothCutoffDegrees, m_document, &Document::setPartSmoothCutoffDegrees);
connect(this, &ComponentPropertyWidget::setComponentCombineMode, m_document, &Document::setComponentCombineMode);
connect(this, &ComponentPropertyWidget::groupOperationAdded, m_document, &Document::saveSnapshot);

Expand Down Expand Up @@ -373,6 +408,25 @@ dust3d::Uuid ComponentPropertyWidget::lastColorImageId()
return colorImageId;
}

float ComponentPropertyWidget::lastSmoothCutoffDegrees()
{
float smoothCutoffDegrees = 0.0;
std::map<std::string, int> degreesMap;
for (const auto& partId : m_partIds) {
const Document::Part* part = m_document->findPart(partId);
if (nullptr == part)
continue;
degreesMap[std::to_string(part->smoothCutoffDegrees)]++;
}
if (!degreesMap.empty()) {
smoothCutoffDegrees = dust3d::String::toFloat(std::max_element(degreesMap.begin(), degreesMap.end(),
[](const std::map<std::string, int>::value_type& a, const std::map<std::string, int>::value_type& b) {
return a.second < b.second;
})->first);
}
return smoothCutoffDegrees;
}

void ComponentPropertyWidget::showColorDialog()
{
emit beginColorPicking();
Expand Down
2 changes: 2 additions & 0 deletions application/sources/component_property_widget.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class ComponentPropertyWidget : public QWidget {
void setPartRoundState(const dust3d::Uuid& partId, bool rounded);
void setPartCutRotation(const dust3d::Uuid& partId, float cutRotation);
void setPartColorImage(const dust3d::Uuid& partId, const dust3d::Uuid& imageId);
void setPartSmoothCutoffDegrees(const dust3d::Uuid& partId, float degrees);
void setComponentCombineMode(dust3d::Uuid componentId, dust3d::CombineMode combineMode);
void groupOperationAdded();

Expand All @@ -38,6 +39,7 @@ public slots:
QColor m_color;
QColor lastColor();
dust3d::Uuid lastColorImageId();
float lastSmoothCutoffDegrees();
void preparePartIds();
QImage* pickImage();
};
Expand Down
16 changes: 9 additions & 7 deletions application/sources/document.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1640,8 +1640,8 @@ void Document::toSnapshot(dust3d::Snapshot* snapshot, const std::set<dust3d::Uui
part["name"] = partIt.second.name.toUtf8().constData();
if (partIt.second.countershaded)
part["countershaded"] = "true";
if (partIt.second.smooth)
part["smooth"] = "true";
if (partIt.second.smoothCutoffDegrees > 0)
part["smoothCutoffDegrees"] = std::to_string(partIt.second.smoothCutoffDegrees);
snapshot->parts[part["id"]] = part;
}
for (const auto& nodeIt : nodeMap) {
Expand Down Expand Up @@ -1849,7 +1849,9 @@ void Document::addFromSnapshot(const dust3d::Snapshot& snapshot, enum SnapshotSo
if (hollowThicknessIt != partKv.second.end())
part.hollowThickness = dust3d::String::toFloat(hollowThicknessIt->second);
part.countershaded = dust3d::String::isTrue(dust3d::String::valueOrEmpty(partKv.second, "countershaded"));
part.smooth = dust3d::String::isTrue(dust3d::String::valueOrEmpty(partKv.second, "smooth"));
const auto& smoothCutoffDegreesIt = partKv.second.find("smoothCutoffDegrees");
if (smoothCutoffDegreesIt != partKv.second.end())
part.smoothCutoffDegrees = dust3d::String::toFloat(smoothCutoffDegreesIt->second);
newAddedPartIds.insert(part.id);
}
for (const auto& it : cutFaceLinkedIdModifyMap) {
Expand Down Expand Up @@ -2586,18 +2588,18 @@ void Document::setPartCountershaded(dust3d::Uuid partId, bool countershaded)
emit textureChanged();
}

void Document::setPartSmoothState(dust3d::Uuid partId, bool smooth)
void Document::setPartSmoothCutoffDegrees(dust3d::Uuid partId, float degrees)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (part->second.smooth == smooth)
if (dust3d::Math::isEqual(part->second.smoothCutoffDegrees, degrees))
return;
part->second.smooth = smooth;
part->second.smoothCutoffDegrees = degrees;
part->second.dirty = true;
emit partSmoothStateChanged(partId);
emit partSmoothCutoffDegreesChanged(partId);
emit skeletonChanged();
}

Expand Down
6 changes: 3 additions & 3 deletions application/sources/document.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ class Document : public QObject {
float roughness;
float hollowThickness;
bool countershaded;
bool smooth;
float smoothCutoffDegrees;
dust3d::Uuid colorImageId;
Part(const dust3d::Uuid& withId = dust3d::Uuid());
bool hasPolyFunction() const;
Expand Down Expand Up @@ -245,7 +245,7 @@ class Document : public QObject {
void partRoughnessChanged(dust3d::Uuid partId);
void partHollowThicknessChanged(dust3d::Uuid partId);
void partCountershadeStateChanged(dust3d::Uuid partId);
void partSmoothStateChanged(dust3d::Uuid partId);
void partSmoothCutoffDegreesChanged(dust3d::Uuid partId);
void componentCombineModeChanged(dust3d::Uuid componentId);
void cleanup();
void checkPart(dust3d::Uuid partId);
Expand Down Expand Up @@ -466,7 +466,7 @@ public slots:
void setPartRoughness(dust3d::Uuid partId, float roughness);
void setPartHollowThickness(dust3d::Uuid partId, float hollowThickness);
void setPartCountershaded(dust3d::Uuid partId, bool countershaded);
void setPartSmoothState(dust3d::Uuid partId, bool smooth);
void setPartSmoothCutoffDegrees(dust3d::Uuid partId, float degrees);
void setComponentCombineMode(dust3d::Uuid componentId, dust3d::CombineMode combineMode);
void saveSnapshot();
void batchChangeBegin();
Expand Down
4 changes: 2 additions & 2 deletions application/sources/document_part.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Document::Part::Part(const dust3d::Uuid& withId)
, roughness(1.0)
, hollowThickness(0.0)
, countershaded(false)
, smooth(false)
, smoothCutoffDegrees(0.0)
{
id = withId.isNull() ? dust3d::Uuid::createUuid() : withId;
}
Expand Down Expand Up @@ -224,6 +224,6 @@ void Document::Part::copyAttributes(const Part& other)
metalness = other.metalness;
roughness = other.roughness;
deformUnified = other.deformUnified;
smooth = other.smooth;
smoothCutoffDegrees = other.smoothCutoffDegrees;
hollowThickness = other.hollowThickness;
}
2 changes: 1 addition & 1 deletion application/sources/mesh_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ void MeshGenerator::process()
dust3d::smoothNormal(it->second.vertices,
it->second.triangles,
previewTriangleNormals,
0,
nullptr,
&previewTriangleVertexNormals);
(*m_componentPreviewMeshes)[componentId] = std::make_unique<ModelMesh>(it->second.vertices,
it->second.triangles,
Expand Down
2 changes: 2 additions & 0 deletions dust3d/base/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ struct ObjectNode {
Vector3 origin;
//float radius = 0.0;
Color color;
float smoothCutoffDegrees = 0.0;
//float colorSolubility = 0.0;
//float metalness = 0.0;
//float roughness = 1.0;
Expand All @@ -65,6 +66,7 @@ class Object {
std::vector<std::map<std::array<PositionKey, 3>, std::array<Vector2, 3>>> seamTriangleUvs;
std::vector<Vector3> triangleNormals;
std::vector<Color> vertexColors;
std::vector<float> vertexSmoothCutoffDegrees;
bool alphaEnabled = false;
uint64_t meshId = 0;

Expand Down
13 changes: 10 additions & 3 deletions dust3d/mesh/mesh_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -563,13 +563,13 @@ std::unique_ptr<MeshState> MeshGenerator::combinePartMesh(const std::string& par
bool rounded = String::isTrue(String::valueOrEmpty(part, "rounded"));
bool chamfered = String::isTrue(String::valueOrEmpty(part, "chamfered"));
bool countershaded = String::isTrue(String::valueOrEmpty(part, "countershaded"));
bool smooth = String::isTrue(String::valueOrEmpty(part, "smooth"));
std::string colorString = String::valueOrEmpty(part, "color");
Color partColor = colorString.empty() ? m_defaultPartColor : Color(colorString);
float deformThickness = 1.0;
float deformWidth = 1.0;
float cutRotation = 0.0;
float hollowThickness = 0.0;
float smoothCutoffDegrees = 0.0;
auto target = PartTargetFromString(String::valueOrEmpty(part, "target").c_str());

std::string searchPartIdString = __mirrorFromPartId.empty() ? partIdString : __mirrorFromPartId;
Expand All @@ -582,6 +582,11 @@ std::unique_ptr<MeshState> MeshGenerator::combinePartMesh(const std::string& par
if (subdived)
subdivideFace(&cutTemplate);

std::string smoothCutoffDegreesString = String::valueOrEmpty(part, "smoothCutoffDegrees");
if (!smoothCutoffDegreesString.empty()) {
smoothCutoffDegrees = String::toFloat(smoothCutoffDegreesString);
}

std::string cutRotationString = String::valueOrEmpty(part, "cutRotation");
if (!cutRotationString.empty()) {
cutRotation = String::toFloat(cutRotationString);
Expand Down Expand Up @@ -641,7 +646,7 @@ std::unique_ptr<MeshState> MeshGenerator::combinePartMesh(const std::string& par
partCache.nodeMap.clear();
for (const auto& meshNode : meshNodes) {
partCache.nodeMap.emplace(std::make_pair(meshNode.sourceId,
ObjectNode { meshNode.origin, partColor }));
ObjectNode { meshNode.origin, partColor, smoothCutoffDegrees }));
}

if (PartTarget::Model == target) {
Expand Down Expand Up @@ -1015,6 +1020,7 @@ void MeshGenerator::postprocessObject(Object* object)
object->triangleNormals = combinedFacesNormals;

object->vertexColors.resize(object->vertices.size(), Color::createWhite());
object->vertexSmoothCutoffDegrees.resize(object->vertices.size(), 0.0f);
for (size_t i = 0; i < object->vertices.size(); ++i) {
auto findSourceNode = object->positionToNodeIdMap.find(object->vertices[i]);
if (findSourceNode == object->positionToNodeIdMap.end())
Expand All @@ -1023,13 +1029,14 @@ void MeshGenerator::postprocessObject(Object* object)
if (findObjectNode == object->nodeMap.end())
continue;
object->vertexColors[i] = findObjectNode->second.color;
object->vertexSmoothCutoffDegrees[i] = findObjectNode->second.smoothCutoffDegrees;
}

std::vector<std::vector<Vector3>> triangleVertexNormals;
smoothNormal(object->vertices,
object->triangles,
object->triangleNormals,
m_smoothShadingThresholdAngleDegrees,
&object->vertexSmoothCutoffDegrees,
&triangleVertexNormals);
object->setTriangleVertexNormals(triangleVertexNormals);
}
Expand Down
5 changes: 3 additions & 2 deletions dust3d/mesh/smooth_normal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace dust3d {
void smoothNormal(const std::vector<Vector3>& vertices,
const std::vector<std::vector<size_t>>& triangles,
const std::vector<Vector3>& triangleNormals,
float thresholdAngleDegrees,
const std::vector<float>* thresholdAngleDegrees,
std::vector<std::vector<Vector3>>* triangleVertexNormals)
{
std::vector<std::vector<std::pair<size_t, size_t>>> triangleVertexNormalsMapByIndices(vertices.size());
Expand Down Expand Up @@ -56,6 +56,7 @@ void smoothNormal(const std::vector<Vector3>& vertices,
std::vector<Vector3> finalNormals = angleAreaWeightedNormals;
std::map<std::pair<size_t, size_t>, float> degreesBetweenFacesMap;
for (size_t vertexIndex = 0; vertexIndex < vertices.size(); ++vertexIndex) {
float threshold = nullptr == thresholdAngleDegrees ? 0.0f : thresholdAngleDegrees->at(vertexIndex);
const auto& triangleVertices = triangleVertexNormalsMapByIndices[vertexIndex];
for (const auto& triangleVertex : triangleVertices) {
for (const auto& otherTriangleVertex : triangleVertices) {
Expand All @@ -70,7 +71,7 @@ void smoothNormal(const std::vector<Vector3>& vertices,
} else {
degrees = findDegreesResult->second;
}
if (degrees > thresholdAngleDegrees) {
if (degrees > threshold) {
continue;
}
finalNormals[triangleVertex.second] += angleAreaWeightedNormals[otherTriangleVertex.second];
Expand Down
2 changes: 1 addition & 1 deletion dust3d/mesh/smooth_normal.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ namespace dust3d {
void smoothNormal(const std::vector<Vector3>& vertices,
const std::vector<std::vector<size_t>>& triangles,
const std::vector<Vector3>& triangleNormals,
float thresholdAngleDegrees,
const std::vector<float>* thresholdAngleDegrees,
std::vector<std::vector<Vector3>>* triangleVertexNormals);

}
Expand Down

0 comments on commit 9e5620b

Please sign in to comment.