Add mesh smooth slider

This feature could be useful for smooth one component and soften the seam of combination,
This commit also include a crash fix about incorrect use of CGAL.
master
Jeremy Hu 2018-09-06 23:04:59 +08:00
parent 87b0f70460
commit 3c2d4f7480
22 changed files with 524 additions and 119 deletions

View File

@ -6,15 +6,14 @@ FloatNumberWidget::FloatNumberWidget(QWidget *parent) :
{ {
m_slider = new QSlider(Qt::Horizontal, this); m_slider = new QSlider(Qt::Horizontal, this);
m_slider->setRange(0, 100); m_slider->setRange(0, 100);
m_slider->setFixedWidth(120);
m_label = new QLabel(this); m_label = new QLabel(this);
m_label->setAlignment(Qt::AlignCenter); m_label->setAlignment(Qt::AlignLeft);
m_label->setNum(0);
m_label->setFixedWidth(30);
connect(m_slider, &QAbstractSlider::valueChanged, [=](int value) { connect(m_slider, &QAbstractSlider::valueChanged, [=](int value) {
float fvalue = value / 100.0; float fvalue = value / 100.0;
m_label->setText(QString().sprintf("%.2f", fvalue)); updateValueLabel(fvalue);
emit valueChanged(fvalue); emit valueChanged(fvalue);
}); });
@ -24,6 +23,21 @@ FloatNumberWidget::FloatNumberWidget(QWidget *parent) :
popupLayout->addWidget(m_label); popupLayout->addWidget(m_label);
} }
void FloatNumberWidget::updateValueLabel(float value)
{
QString valueString = QString().sprintf("%.2f", value);
if (m_itemName.isEmpty())
m_label->setText(valueString);
else
m_label->setText(m_itemName + ": " + valueString);
}
void FloatNumberWidget::setItemName(const QString &name)
{
m_itemName = name;
updateValueLabel(value());
}
void FloatNumberWidget::setRange(float min, float max) void FloatNumberWidget::setRange(float min, float max)
{ {
m_slider->setRange(min * 100, max * 100); m_slider->setRange(min * 100, max * 100);

View File

@ -12,6 +12,7 @@ public:
explicit FloatNumberWidget(QWidget *parent = nullptr); explicit FloatNumberWidget(QWidget *parent = nullptr);
void setRange(float min, float max); void setRange(float min, float max);
float value() const; float value() const;
void setItemName(const QString &name);
public slots: public slots:
void increaseValue(); void increaseValue();
@ -21,9 +22,13 @@ public slots:
signals: signals:
void valueChanged(float value); void valueChanged(float value);
private:
void updateValueLabel(float value);
private: private:
QLabel *m_label = nullptr; QLabel *m_label = nullptr;
QSlider *m_slider = nullptr; QSlider *m_slider = nullptr;
QString m_itemName;
}; };
#endif #endif

View File

@ -28,7 +28,7 @@ int main(int argc, char ** argv)
darkPalette.setColor(QPalette::ButtonText, Theme::white); darkPalette.setColor(QPalette::ButtonText, Theme::white);
darkPalette.setColor(QPalette::BrightText, Theme::red); darkPalette.setColor(QPalette::BrightText, Theme::red);
darkPalette.setColor(QPalette::Link, QColor(42, 130, 218)); darkPalette.setColor(QPalette::Link, QColor(42, 130, 218));
darkPalette.setColor(QPalette::Highlight, Theme::white); darkPalette.setColor(QPalette::Highlight, Theme::red);
darkPalette.setColor(QPalette::HighlightedText, Theme::black); darkPalette.setColor(QPalette::HighlightedText, Theme::black);
qApp->setPalette(darkPalette); qApp->setPalette(darkPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #fc6621; border: 1px solid white; }"); qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #fc6621; border: 1px solid white; }");

View File

@ -215,12 +215,12 @@ void *MeshGenerator::combinePartMesh(QString partId)
if (MeshGenerator::m_enableDebug) if (MeshGenerator::m_enableDebug)
meshlite_bmesh_enable_debug(m_meshliteContext, bmeshId, 1); meshlite_bmesh_enable_debug(m_meshliteContext, bmeshId, 1);
QString mirroredPartId; //QString mirroredPartId;
QUuid mirroredPartIdNotAsString; //QUuid mirroredPartIdNotAsString;
if (xMirrored) { //if (xMirrored) {
mirroredPartIdNotAsString = QUuid().createUuid(); // mirroredPartIdNotAsString = QUuid().createUuid();
mirroredPartId = mirroredPartIdNotAsString.toString(); // mirroredPartId = mirroredPartIdNotAsString.toString();
} //}
std::map<QString, int> nodeToBmeshIdMap; std::map<QString, int> nodeToBmeshIdMap;
std::map<int, QUuid> bmeshToNodeIdMap; std::map<int, QUuid> bmeshToNodeIdMap;
@ -252,12 +252,11 @@ void *MeshGenerator::combinePartMesh(QString partId)
bmeshNode.nodeId = QUuid(nodeId); bmeshNode.nodeId = QUuid(nodeId);
bmeshNode.color = partColor; bmeshNode.color = partColor;
cacheBmeshNodes.push_back(bmeshNode); cacheBmeshNodes.push_back(bmeshNode);
//if (xMirrored) {
if (xMirrored) { // bmeshNode.partId = mirroredPartId;
bmeshNode.partId = mirroredPartId; // bmeshNode.origin.setX(-x);
bmeshNode.origin.setX(-x); // cacheBmeshNodes.push_back(bmeshNode);
cacheBmeshNodes.push_back(bmeshNode); //}
}
} }
for (const auto &edgeId: m_partEdgeIds[partId]) { for (const auto &edgeId: m_partEdgeIds[partId]) {
@ -300,7 +299,7 @@ void *MeshGenerator::combinePartMesh(QString partId)
if (nullptr != resultMesh) { if (nullptr != resultMesh) {
if (xMirrored) { if (xMirrored) {
int xMirroredMeshId = meshlite_mirror_in_x(m_meshliteContext, meshId, 0); int xMirroredMeshId = meshlite_mirror_in_x(m_meshliteContext, meshId, 0);
loadVertexSources(m_meshliteContext, xMirroredMeshId, mirroredPartIdNotAsString, bmeshToNodeIdMap, cacheBmeshVertices); loadVertexSources(m_meshliteContext, xMirroredMeshId, partIdNotAsString, bmeshToNodeIdMap, cacheBmeshVertices);
void *mirroredMesh = nullptr; void *mirroredMesh = nullptr;
if (wrapped) if (wrapped)
mirroredMesh = convertToCombinableConvexHullMesh(m_meshliteContext, xMirroredMeshId); mirroredMesh = convertToCombinableConvexHullMesh(m_meshliteContext, xMirroredMeshId);
@ -411,41 +410,122 @@ void *MeshGenerator::combineComponentMesh(QString componentId, bool *inverse)
} }
} }
QString linkDataType = valueOfKeyInMapOrEmpty(*component, "linkDataType"); bool smoothSeam = false;
if ("partId" == linkDataType) { float smoothSeamFactor = 0.0;
QString partId = valueOfKeyInMapOrEmpty(*component, "linkData"); QString smoothSeamString = valueOfKeyInMapOrEmpty(*component, "smoothSeam");
return combinePartMesh(partId); if (!smoothSeamString.isEmpty()) {
smoothSeam = true;
smoothSeamFactor = smoothSeamString.toFloat();
}
bool smoothAll = false;
float smoothAllFactor = 0.0;
QString smoothAllString = valueOfKeyInMapOrEmpty(*component, "smoothAll");
if (!smoothAllString.isEmpty()) {
smoothAll = true;
smoothAllFactor = smoothAllString.toFloat();
} }
void *resultMesh = nullptr; void *resultMesh = nullptr;
for (const auto &childId: valueOfKeyInMapOrEmpty(*component, "children").split(",")) { PositionMap<bool> positionsBeforeCombination;
if (childId.isEmpty()) auto &verticesSources = m_cacheContext->componentVerticesSources[componentId];
continue; verticesSources.map().clear();
bool childInverse = false; QString linkDataType = valueOfKeyInMapOrEmpty(*component, "linkDataType");
void *childCombinedMesh = combineComponentMesh(childId, &childInverse); if ("partId" == linkDataType) {
if (nullptr == childCombinedMesh) QString partId = valueOfKeyInMapOrEmpty(*component, "linkData");
continue; resultMesh = combinePartMesh(partId);
if (nullptr == resultMesh) { for (const auto &bmeshVertex: m_cacheContext->partBmeshVertices[partId]) {
if (childInverse) { verticesSources.addPosition(bmeshVertex.position.x(), bmeshVertex.position.y(), bmeshVertex.position.z(),
deleteCombinableMesh(childCombinedMesh); bmeshVertex);
} else { }
resultMesh = childCombinedMesh; } else {
for (const auto &childId: valueOfKeyInMapOrEmpty(*component, "children").split(",")) {
if (childId.isEmpty())
continue;
bool childInverse = false;
void *childCombinedMesh = combineComponentMesh(childId, &childInverse);
if (smoothSeam) {
for (const auto &positionIt: m_cacheContext->componentPositions[childId]) {
positionsBeforeCombination.addPosition(positionIt.x(), positionIt.y(), positionIt.z(), true);
}
} }
} else { for (const auto &verticesSourceIt: m_cacheContext->componentVerticesSources[childId].map()) {
void *newResultMesh = childInverse ? diffCombinableMeshs(resultMesh, childCombinedMesh) : unionCombinableMeshs(resultMesh, childCombinedMesh); verticesSources.map()[verticesSourceIt.first] = verticesSourceIt.second;
deleteCombinableMesh(childCombinedMesh); }
if (nullptr != newResultMesh) { if (nullptr == childCombinedMesh)
deleteCombinableMesh(resultMesh); continue;
resultMesh = newResultMesh; if (nullptr == resultMesh) {
if (childInverse) {
deleteCombinableMesh(childCombinedMesh);
} else {
resultMesh = childCombinedMesh;
}
} else {
void *newResultMesh = childInverse ? diffCombinableMeshs(resultMesh, childCombinedMesh) : unionCombinableMeshs(resultMesh, childCombinedMesh);
deleteCombinableMesh(childCombinedMesh);
if (nullptr != newResultMesh) {
deleteCombinableMesh(resultMesh);
resultMesh = newResultMesh;
}
} }
} }
} }
if (!componentIdNotAsString.isNull()) { if (nullptr != resultMesh) {
m_cacheContext->updateComponentCombinableMesh(componentId, resultMesh); if (smoothSeam || smoothAll) {
int meshIdForSmooth = convertFromCombinableMesh(m_meshliteContext, resultMesh);
std::vector<QVector3D> positionsBeforeSmooth;
loadMeshVerticesPositions(m_meshliteContext, meshIdForSmooth, positionsBeforeSmooth);
if (!positionsBeforeSmooth.empty()) {
if (smoothSeam) {
int *seamVerticesIndicies = new int[positionsBeforeSmooth.size()];
int seamVerticesNum = 0;
for (size_t vertexIndex = 0; vertexIndex < positionsBeforeSmooth.size(); vertexIndex++) {
const auto &oldPosition = positionsBeforeSmooth[vertexIndex];
if (!positionsBeforeCombination.findPosition(oldPosition.x(), oldPosition.y(), oldPosition.z())) {
seamVerticesIndicies[seamVerticesNum++] = vertexIndex + 1;
}
}
if (seamVerticesNum > 0) {
qDebug() << "smoothSeamFactor:" << smoothSeamFactor << "seamVerticesIndicies.size():" << seamVerticesNum;
meshlite_smooth_vertices(m_meshliteContext, meshIdForSmooth, smoothSeamFactor, seamVerticesIndicies, seamVerticesNum);
}
delete[] seamVerticesIndicies;
}
if (smoothAll) {
meshlite_smooth(m_meshliteContext, meshIdForSmooth, smoothAllFactor);
}
std::vector<QVector3D> positionsAfterSmooth;
loadMeshVerticesPositions(m_meshliteContext, meshIdForSmooth, positionsAfterSmooth);
assert(positionsBeforeSmooth.size() == positionsAfterSmooth.size());
for (size_t vertexIndex = 0; vertexIndex < positionsBeforeSmooth.size(); vertexIndex++) {
const auto &oldPosition = positionsBeforeSmooth[vertexIndex];
const auto &smoothedPosition = positionsAfterSmooth[vertexIndex];
BmeshVertex source;
if (verticesSources.findPosition(oldPosition.x(), oldPosition.y(), oldPosition.z(), &source)) {
verticesSources.removePosition(oldPosition.x(), oldPosition.y(), oldPosition.z());
source.position = smoothedPosition;
verticesSources.addPosition(smoothedPosition.x(), smoothedPosition.y(), smoothedPosition.z(), source);
}
}
deleteCombinableMesh(resultMesh);
resultMesh = convertToCombinableMesh(m_meshliteContext, meshIdForSmooth);
}
}
} }
m_cacheContext->updateComponentCombinableMesh(componentId, resultMesh);
auto &cachedComponentPositions = m_cacheContext->componentPositions[componentId];
cachedComponentPositions.clear();
loadCombinableMeshVerticesPositions(resultMesh, cachedComponentPositions);
return resultMesh; return resultMesh;
} }
@ -488,11 +568,27 @@ void MeshGenerator::process()
} }
it++; it++;
} }
for (auto it = m_cacheContext->componentPositions.begin(); it != m_cacheContext->componentPositions.end(); ) {
if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) {
it = m_cacheContext->componentPositions.erase(it);
continue;
}
it++;
}
for (auto it = m_cacheContext->componentVerticesSources.begin(); it != m_cacheContext->componentVerticesSources.end(); ) {
if (m_snapshot->components.find(it->first) == m_snapshot->components.end()) {
it = m_cacheContext->componentVerticesSources.erase(it);
continue;
}
it++;
}
} }
collectParts(); collectParts();
checkDirtyFlags(); checkDirtyFlags();
m_dirtyComponentIds.insert(QUuid().toString());
m_mainProfileMiddleX = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originX").toFloat(); m_mainProfileMiddleX = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originX").toFloat();
m_mainProfileMiddleY = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originY").toFloat(); m_mainProfileMiddleY = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originY").toFloat();
m_sideProfileMiddleX = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originZ").toFloat(); m_sideProfileMiddleX = valueOfKeyInMapOrEmpty(m_snapshot->canvas, "originZ").toFloat();
@ -506,9 +602,8 @@ void MeshGenerator::process()
deleteCombinableMesh(combinedMesh); deleteCombinableMesh(combinedMesh);
} }
for (const auto &bmeshVertices: m_cacheContext->partBmeshVertices) { for (const auto &verticesSourcesIt: m_cacheContext->componentVerticesSources[QUuid().toString()].map()) {
m_meshResultContext->bmeshVertices.insert(m_meshResultContext->bmeshVertices.end(), m_meshResultContext->bmeshVertices.push_back(verticesSourcesIt.second);
bmeshVertices.second.begin(), bmeshVertices.second.end());
} }
for (const auto &bmeshNodes: m_cacheContext->partBmeshNodes) { for (const auto &bmeshNodes: m_cacheContext->partBmeshNodes) {

View File

@ -11,6 +11,7 @@
#include "meshloader.h" #include "meshloader.h"
#include "modelofflinerender.h" #include "modelofflinerender.h"
#include "meshresultcontext.h" #include "meshresultcontext.h"
#include "positionmap.h"
class GeneratedCacheContext class GeneratedCacheContext
{ {
@ -19,6 +20,8 @@ public:
std::map<QString, std::vector<BmeshVertex>> partBmeshVertices; std::map<QString, std::vector<BmeshVertex>> partBmeshVertices;
std::map<QString, std::vector<BmeshNode>> partBmeshNodes; std::map<QString, std::vector<BmeshNode>> partBmeshNodes;
std::map<QString, void *> componentCombinableMeshs; std::map<QString, void *> componentCombinableMeshs;
std::map<QString, std::vector<QVector3D>> componentPositions;
std::map<QString, PositionMap<BmeshVertex>> componentVerticesSources;
void updateComponentCombinableMesh(QString componentId, void *mesh); void updateComponentCombinableMesh(QString componentId, void *mesh);
}; };
@ -29,7 +32,6 @@ public:
MeshGenerator(SkeletonSnapshot *snapshot, QThread *thread); MeshGenerator(SkeletonSnapshot *snapshot, QThread *thread);
~MeshGenerator(); ~MeshGenerator();
void setSharedContextWidget(QOpenGLWidget *widget); void setSharedContextWidget(QOpenGLWidget *widget);
//void addPreviewRequirement();
void addPartPreviewRequirement(const QString &partId); void addPartPreviewRequirement(const QString &partId);
void setGeneratedCacheContext(GeneratedCacheContext *cacheContext); void setGeneratedCacheContext(GeneratedCacheContext *cacheContext);
MeshLoader *takeResultMesh(); MeshLoader *takeResultMesh();
@ -59,6 +61,7 @@ private:
std::map<QString, std::set<QString>> m_partEdgeIds; std::map<QString, std::set<QString>> m_partEdgeIds;
std::set<QString> m_dirtyComponentIds; std::set<QString> m_dirtyComponentIds;
std::set<QString> m_dirtyPartIds; std::set<QString> m_dirtyPartIds;
PositionMap<std::pair<QString, int>> m_bmeshPartVerticesIndiciesMap;
private: private:
static bool m_enableDebug; static bool m_enableDebug;
private: private:

View File

@ -31,16 +31,13 @@ typedef CGAL::Surface_mesh<ExactKernel::Point_3> ExactMesh;
typedef CGAL::Surface_mesh<SimpleKernel::Point_3> SimpleMesh; typedef CGAL::Surface_mesh<SimpleKernel::Point_3> SimpleMesh;
namespace PMP = CGAL::Polygon_mesh_processing; namespace PMP = CGAL::Polygon_mesh_processing;
template <class Kernel> void loadMeshVerticesPositions(void *meshliteContext, int meshId, std::vector<QVector3D> &positions)
typename CGAL::Surface_mesh<typename Kernel::Point_3> *makeCgalMeshFromMeshlite(void *meshlite, int meshId)
{ {
typename CGAL::Surface_mesh<typename Kernel::Point_3> *mesh = new typename CGAL::Surface_mesh<typename Kernel::Point_3>; int vertexCount = meshlite_get_vertex_count(meshliteContext, meshId);
int vertexCount = meshlite_get_vertex_count(meshlite, meshId);
float *vertexPositions = new float[vertexCount * 3]; float *vertexPositions = new float[vertexCount * 3];
int vertexArrayLen = meshlite_get_vertex_position_array(meshlite, meshId, vertexPositions, vertexCount * 3); int vertexArrayLen = meshlite_get_vertex_position_array(meshliteContext, meshId, vertexPositions, vertexCount * 3);
int offset = 0; int offset = 0;
assert(vertexArrayLen == vertexCount * 3); assert(vertexArrayLen == vertexCount * 3);
std::vector<typename CGAL::Surface_mesh<typename Kernel::Point_3>::Vertex_index> vertices;
for (int i = 0; i < vertexCount; i++) { for (int i = 0; i < vertexCount; i++) {
float x = vertexPositions[offset + 0]; float x = vertexPositions[offset + 0];
float y = vertexPositions[offset + 1]; float y = vertexPositions[offset + 1];
@ -51,7 +48,34 @@ typename CGAL::Surface_mesh<typename Kernel::Point_3> *makeCgalMeshFromMeshlite(
y = 0; y = 0;
if (std::isnan(z) || std::isinf(z)) if (std::isnan(z) || std::isinf(z))
z = 0; z = 0;
vertices.push_back(mesh->add_vertex(typename Kernel::Point_3(x, y, z))); positions.push_back(QVector3D(x, y, z));
offset += 3;
}
delete[] vertexPositions;
}
template <class Kernel>
typename CGAL::Surface_mesh<typename Kernel::Point_3> *makeCgalMeshFromMeshlite(void *meshlite, int meshId)
{
typename CGAL::Surface_mesh<typename Kernel::Point_3> *mesh = new typename CGAL::Surface_mesh<typename Kernel::Point_3>;
int vertexCount = meshlite_get_vertex_count(meshlite, meshId);
float *vertexPositions = new float[vertexCount * 3];
int vertexArrayLen = meshlite_get_vertex_position_array(meshlite, meshId, vertexPositions, vertexCount * 3);
int offset = 0;
assert(vertexArrayLen == vertexCount * 3);
std::vector<QVector3D> oldPositions;
std::map<int, typename CGAL::Surface_mesh<typename Kernel::Point_3>::Vertex_index> oldToNewMap;
for (int i = 0; i < vertexCount; i++) {
float x = vertexPositions[offset + 0];
float y = vertexPositions[offset + 1];
float z = vertexPositions[offset + 2];
if (std::isnan(x) || std::isinf(x))
x = 0;
if (std::isnan(y) || std::isinf(y))
y = 0;
if (std::isnan(z) || std::isinf(z))
z = 0;
oldPositions.push_back(QVector3D(x, y, z));
offset += 3; offset += 3;
} }
int faceCount = meshlite_get_face_count(meshlite, meshId); int faceCount = meshlite_get_face_count(meshlite, meshId);
@ -62,26 +86,51 @@ typename CGAL::Surface_mesh<typename Kernel::Point_3> *makeCgalMeshFromMeshlite(
while (i < filledLength) { while (i < filledLength) {
int num = faceVertexNumAndIndices[i++]; int num = faceVertexNumAndIndices[i++];
assert(num > 0 && num <= MAX_VERTICES_PER_FACE); assert(num > 0 && num <= MAX_VERTICES_PER_FACE);
if (num < 3)
continue;
std::vector<typename CGAL::Surface_mesh<typename Kernel::Point_3>::Vertex_index> faceVertexIndices; std::vector<typename CGAL::Surface_mesh<typename Kernel::Point_3>::Vertex_index> faceVertexIndices;
for (int j = 0; j < num; j++) { for (int j = 0; j < num; j++) {
int index = faceVertexNumAndIndices[i++]; int index = faceVertexNumAndIndices[i++];
assert(index >= 0 && index < vertexCount); assert(index >= 0 && index < vertexCount);
faceVertexIndices.push_back(vertices[index]); if (oldToNewMap.find(index) == oldToNewMap.end()) {
} const auto &pos = oldPositions[index];
if (faceVertexIndices.size() >= 3) { oldToNewMap[index] = mesh->add_vertex(typename Kernel::Point_3(pos.x(), pos.y(), pos.z()));
mesh->add_face(faceVertexIndices); }
addedFaceCount++; faceVertexIndices.push_back(oldToNewMap[index]);
} }
mesh->add_face(faceVertexIndices);
addedFaceCount++;
} }
delete[] faceVertexNumAndIndices; delete[] faceVertexNumAndIndices;
delete[] vertexPositions; delete[] vertexPositions;
if (0 == addedFaceCount) { if (addedFaceCount < 3) {
delete mesh; delete mesh;
mesh = nullptr; mesh = nullptr;
} }
return mesh; return mesh;
} }
void loadCombinableMeshVerticesPositions(void *mesh, std::vector<QVector3D> &positions)
{
ExactMesh *exactMesh = (ExactMesh *)mesh;
if (nullptr == exactMesh)
return;
for (auto vertexIt = exactMesh->vertices_begin(); vertexIt != exactMesh->vertices_end(); vertexIt++) {
auto point = exactMesh->point(*vertexIt);
float x = (float)CGAL::to_double(point.x());
float y = (float)CGAL::to_double(point.y());
float z = (float)CGAL::to_double(point.z());
if (std::isnan(x) || std::isinf(x))
x = 0;
if (std::isnan(y) || std::isinf(y))
y = 0;
if (std::isnan(z) || std::isinf(z))
z = 0;
positions.push_back(QVector3D(x, y, z));
}
}
// https://doc.cgal.org/latest/Surface_mesh/index.html#circulators_example // https://doc.cgal.org/latest/Surface_mesh/index.html#circulators_example
// https://www.cgal.org/FAQ.html // https://www.cgal.org/FAQ.html
template <class Kernel> template <class Kernel>
@ -256,7 +305,6 @@ ExactMesh *diffCgalMeshs(ExactMesh *first, ExactMesh *second)
int unionMeshs(void *meshliteContext, const std::vector<int> &meshIds, const std::set<int> &inverseIds, int *errorCount) int unionMeshs(void *meshliteContext, const std::vector<int> &meshIds, const std::set<int> &inverseIds, int *errorCount)
{ {
#if USE_CGAL == 1 #if USE_CGAL == 1
CGAL::set_error_behaviour(CGAL::CONTINUE);
std::vector<ExactMesh *> externalMeshs; std::vector<ExactMesh *> externalMeshs;
for (size_t i = 0; i < meshIds.size(); i++) { for (size_t i = 0; i < meshIds.size(); i++) {
int triangledMeshId = meshlite_triangulate(meshliteContext, meshIds[i]); int triangledMeshId = meshlite_triangulate(meshliteContext, meshIds[i]);
@ -331,7 +379,6 @@ int subdivMesh(void *meshliteContext, int meshId, int *errorCount)
int triangulatedMeshId = meshlite_triangulate(meshliteContext, meshId); int triangulatedMeshId = meshlite_triangulate(meshliteContext, meshId);
if (0 == meshlite_is_triangulated_manifold(meshliteContext, triangulatedMeshId)) { if (0 == meshlite_is_triangulated_manifold(meshliteContext, triangulatedMeshId)) {
#if USE_CGAL == 1 #if USE_CGAL == 1
CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
int subdiviedMeshId = 0; int subdiviedMeshId = 0;
SimpleMesh *simpleMesh = nullptr; SimpleMesh *simpleMesh = nullptr;
try { try {
@ -359,7 +406,7 @@ int fixMeshHoles(void *meshliteContext, int meshId)
void initMeshUtils() void initMeshUtils()
{ {
CGAL::set_error_behaviour(CGAL::CONTINUE); CGAL::set_error_behaviour(CGAL::THROW_EXCEPTION);
} }
void *convertToCombinableMesh(void *meshliteContext, int meshId) void *convertToCombinableMesh(void *meshliteContext, int meshId)

View File

@ -2,6 +2,7 @@
#define MESH_UTIL_H #define MESH_UTIL_H
#include <vector> #include <vector>
#include <set> #include <set>
#include <QVector3D>
int mergeMeshs(void *meshliteContext, const std::vector<int> &meshIds); int mergeMeshs(void *meshliteContext, const std::vector<int> &meshIds);
int unionMeshs(void *meshliteContext, const std::vector<int> &meshIds, const std::set<int> &inverseIds, int *errorCount=0); int unionMeshs(void *meshliteContext, const std::vector<int> &meshIds, const std::set<int> &inverseIds, int *errorCount=0);
@ -16,5 +17,7 @@ int convertFromCombinableMesh(void *meshliteContext, void *mesh);
void deleteCombinableMesh(void *mesh); void deleteCombinableMesh(void *mesh);
void *cloneCombinableMesh(void *mesh); void *cloneCombinableMesh(void *mesh);
void *convertToCombinableConvexHullMesh(void *meshliteContext, int meshId); void *convertToCombinableConvexHullMesh(void *meshliteContext, int meshId);
void loadMeshVerticesPositions(void *meshliteContext, int meshId, std::vector<QVector3D> &positions);
void loadCombinableMeshVerticesPositions(void *mesh, std::vector<QVector3D> &positions);
#endif #endif

View File

@ -50,7 +50,7 @@ public:
m_map[makeKey(x, y, z)] = data; m_map[makeKey(x, y, z)] = data;
} }
bool findPosition(float x, float y, float z, T *data) bool findPosition(float x, float y, float z, T *data = nullptr)
{ {
const auto &result = m_map.find(makeKey(x, y, z)); const auto &result = m_map.find(makeKey(x, y, z));
if (result == m_map.end()) if (result == m_map.end())
@ -60,6 +60,16 @@ public:
return true; return true;
} }
void removePosition(float x, float y, float z)
{
m_map.erase(makeKey(x, y, z));
}
std::map<PositionMapKey, T> &map()
{
return m_map;
}
private: private:
std::map<PositionMapKey, T> m_map; std::map<PositionMapKey, T> m_map;
int m_intGridSize; int m_intGridSize;

View File

@ -725,6 +725,10 @@ void SkeletonDocument::toSnapshot(SkeletonSnapshot *snapshot, const std::set<QUu
component["expanded"] = componentIt.second.expanded ? "true" : "false"; component["expanded"] = componentIt.second.expanded ? "true" : "false";
component["inverse"] = componentIt.second.inverse ? "true" : "false"; component["inverse"] = componentIt.second.inverse ? "true" : "false";
component["dirty"] = componentIt.second.dirty ? "true" : "false"; component["dirty"] = componentIt.second.dirty ? "true" : "false";
if (componentIt.second.smoothAllAdjusted())
component["smoothAll"] = QString::number(componentIt.second.smoothAll);
if (componentIt.second.smoothSeamAdjusted())
component["smoothSeam"] = QString::number(componentIt.second.smoothSeam);
QStringList childIdList; QStringList childIdList;
for (const auto &childId: componentIt.second.childrenIds) { for (const auto &childId: componentIt.second.childrenIds) {
childIdList.append(childId.toString()); childIdList.append(childId.toString());
@ -862,6 +866,12 @@ void SkeletonDocument::addFromSnapshot(const SkeletonSnapshot &snapshot)
component.name = valueOfKeyInMapOrEmpty(componentKv.second, "name"); component.name = valueOfKeyInMapOrEmpty(componentKv.second, "name");
component.expanded = isTrueValueString(valueOfKeyInMapOrEmpty(componentKv.second, "expanded")); component.expanded = isTrueValueString(valueOfKeyInMapOrEmpty(componentKv.second, "expanded"));
component.inverse = isTrueValueString(valueOfKeyInMapOrEmpty(componentKv.second, "inverse")); component.inverse = isTrueValueString(valueOfKeyInMapOrEmpty(componentKv.second, "inverse"));
const auto &smoothAllIt = componentKv.second.find("smoothAll");
if (smoothAllIt != componentKv.second.end())
component.setSmoothAll(smoothAllIt->second.toFloat());
const auto &smoothSeamIt = componentKv.second.find("smoothSeam");
if (smoothSeamIt != componentKv.second.end())
component.setSmoothSeam(smoothSeamIt->second.toFloat());
qDebug() << "Add component:" << component.id << " old:" << componentKv.first << "name:" << component.name; qDebug() << "Add component:" << component.id << " old:" << componentKv.first << "name:" << component.name;
if ("partId" == linkDataType) { if ("partId" == linkDataType) {
QUuid partId = oldNewIdMap[QUuid(linkData)]; QUuid partId = oldNewIdMap[QUuid(linkData)];
@ -1284,6 +1294,32 @@ void SkeletonDocument::setComponentInverseState(QUuid componentId, bool inverse)
emit skeletonChanged(); emit skeletonChanged();
} }
void SkeletonDocument::setComponentSmoothAll(QUuid componentId, float toSmoothAll)
{
auto component = componentMap.find(componentId);
if (component == componentMap.end()) {
qDebug() << "Component not found:" << componentId;
return;
}
component->second.setSmoothAll(toSmoothAll);
component->second.dirty = true;
emit componentSmoothAllChanged(componentId);
emit skeletonChanged();
}
void SkeletonDocument::setComponentSmoothSeam(QUuid componentId, float toSmoothSeam)
{
auto component = componentMap.find(componentId);
if (component == componentMap.end()) {
qDebug() << "Component not found:" << componentId;
return;
}
component->second.setSmoothSeam(toSmoothSeam);
component->second.dirty = true;
emit componentSmoothSeamChanged(componentId);
emit skeletonChanged();
}
void SkeletonDocument::setPartSubdivState(QUuid partId, bool subdived) void SkeletonDocument::setPartSubdivState(QUuid partId, bool subdived)
{ {
auto part = partMap.find(partId); auto part = partMap.find(partId);

View File

@ -199,6 +199,8 @@ public:
bool expanded = true; bool expanded = true;
bool inverse = false; bool inverse = false;
bool dirty = true; bool dirty = true;
float smoothAll = 0.0;
float smoothSeam = 0.0;
std::vector<QUuid> childrenIds; std::vector<QUuid> childrenIds;
QString linkData() const QString linkData() const
{ {
@ -290,6 +292,34 @@ public:
for (int i = index; i <= (int)childrenIds.size() - 2; i++) for (int i = index; i <= (int)childrenIds.size() - 2; i++)
std::swap(childrenIds[i], childrenIds[i + 1]); std::swap(childrenIds[i], childrenIds[i + 1]);
} }
void setSmoothAll(float toSmoothAll)
{
if (toSmoothAll < 0)
toSmoothAll = 0;
else if (toSmoothAll > 1)
toSmoothAll = 1;
smoothAll = toSmoothAll;
}
void setSmoothSeam(float toSmoothSeam)
{
if (toSmoothSeam < 0)
toSmoothSeam = 0;
else if (toSmoothSeam > 1)
toSmoothSeam = 1;
smoothSeam = toSmoothSeam;
}
bool smoothAllAdjusted() const
{
return fabs(smoothAll - 0.0) >= 0.01;
}
bool smoothSeamAdjusted() const
{
return fabs(smoothSeam - 0.0) >= 0.01;
}
bool smoothAdjusted() const
{
return smoothAllAdjusted() || smoothSeamAdjusted();
}
private: private:
std::set<QUuid> m_childrenIdSet; std::set<QUuid> m_childrenIdSet;
}; };
@ -307,6 +337,8 @@ signals:
void componentRemoved(QUuid componentId); void componentRemoved(QUuid componentId);
void componentAdded(QUuid componentId); void componentAdded(QUuid componentId);
void componentExpandStateChanged(QUuid componentId); void componentExpandStateChanged(QUuid componentId);
void componentSmoothAllChanged(QUuid componentId);
void componentSmoothSeamChanged(QUuid componentId);
void nodeRemoved(QUuid nodeId); void nodeRemoved(QUuid nodeId);
void edgeRemoved(QUuid edgeId); void edgeRemoved(QUuid edgeId);
void nodeRadiusChanged(QUuid nodeId); void nodeRadiusChanged(QUuid nodeId);
@ -442,6 +474,8 @@ public slots:
void setCurrentCanvasComponentId(QUuid componentId); void setCurrentCanvasComponentId(QUuid componentId);
void createNewComponentAndMoveThisIn(QUuid componentId); void createNewComponentAndMoveThisIn(QUuid componentId);
void setComponentExpandState(QUuid componentId, bool expanded); void setComponentExpandState(QUuid componentId, bool expanded);
void setComponentSmoothAll(QUuid componentId, float toSmoothAll);
void setComponentSmoothSeam(QUuid componentId, float toSmoothSeam);
void hideOtherComponents(QUuid componentId); void hideOtherComponents(QUuid componentId);
void lockOtherComponents(QUuid componentId); void lockOtherComponents(QUuid componentId);
void hideAllComponents(); void hideAllComponents();

View File

@ -596,6 +596,8 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
connect(partTreeWidget, &SkeletonPartTreeWidget::createNewComponentAndMoveThisIn, m_document, &SkeletonDocument::createNewComponentAndMoveThisIn); connect(partTreeWidget, &SkeletonPartTreeWidget::createNewComponentAndMoveThisIn, m_document, &SkeletonDocument::createNewComponentAndMoveThisIn);
connect(partTreeWidget, &SkeletonPartTreeWidget::renameComponent, m_document, &SkeletonDocument::renameComponent); connect(partTreeWidget, &SkeletonPartTreeWidget::renameComponent, m_document, &SkeletonDocument::renameComponent);
connect(partTreeWidget, &SkeletonPartTreeWidget::setComponentExpandState, m_document, &SkeletonDocument::setComponentExpandState); connect(partTreeWidget, &SkeletonPartTreeWidget::setComponentExpandState, m_document, &SkeletonDocument::setComponentExpandState);
connect(partTreeWidget, &SkeletonPartTreeWidget::setComponentSmoothAll, m_document, &SkeletonDocument::setComponentSmoothAll);
connect(partTreeWidget, &SkeletonPartTreeWidget::setComponentSmoothSeam, m_document, &SkeletonDocument::setComponentSmoothSeam);
connect(partTreeWidget, &SkeletonPartTreeWidget::moveComponent, m_document, &SkeletonDocument::moveComponent); connect(partTreeWidget, &SkeletonPartTreeWidget::moveComponent, m_document, &SkeletonDocument::moveComponent);
connect(partTreeWidget, &SkeletonPartTreeWidget::removeComponent, m_document, &SkeletonDocument::removeComponent); connect(partTreeWidget, &SkeletonPartTreeWidget::removeComponent, m_document, &SkeletonDocument::removeComponent);
connect(partTreeWidget, &SkeletonPartTreeWidget::hideOtherComponents, m_document, &SkeletonDocument::hideOtherComponents); connect(partTreeWidget, &SkeletonPartTreeWidget::hideOtherComponents, m_document, &SkeletonDocument::hideOtherComponents);

View File

@ -1,8 +1,12 @@
#include <QMenu> #include <QMenu>
#include <vector> #include <vector>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QWidgetAction>
#include "skeletonparttreewidget.h" #include "skeletonparttreewidget.h"
#include "skeletonpartwidget.h" #include "skeletonpartwidget.h"
#include "skeletongraphicswidget.h" #include "skeletongraphicswidget.h"
#include "floatnumberwidget.h"
SkeletonPartTreeWidget::SkeletonPartTreeWidget(const SkeletonDocument *document, QWidget *parent) : SkeletonPartTreeWidget::SkeletonPartTreeWidget(const SkeletonDocument *document, QWidget *parent) :
QTreeWidget(parent), QTreeWidget(parent),
@ -22,6 +26,7 @@ SkeletonPartTreeWidget::SkeletonPartTreeWidget(const SkeletonDocument *document,
m_componentItemMap[QUuid()] = invisibleRootItem(); m_componentItemMap[QUuid()] = invisibleRootItem();
setContextMenuPolicy(Qt::CustomContextMenu); setContextMenuPolicy(Qt::CustomContextMenu);
//setIconSize(QSize(Theme::miniIconFontSize, Theme::miniIconFontSize));
setStyleSheet("QTreeView {qproperty-indentation: 10; margin-left: 5px; margin-top: 5px;}" setStyleSheet("QTreeView {qproperty-indentation: 10; margin-left: 5px; margin-top: 5px;}"
"QTreeView::branch:has-siblings:!adjoins-item {border-image: url(:/resources/tree-vline.png);}" "QTreeView::branch:has-siblings:!adjoins-item {border-image: url(:/resources/tree-vline.png);}"
@ -30,42 +35,63 @@ SkeletonPartTreeWidget::SkeletonPartTreeWidget(const SkeletonDocument *document,
"QTreeView::branch:has-children:!has-siblings:closed, QTreeView::branch:closed:has-children:has-siblings {border-image: none; image: url(:/resources/tree-branch-closed.png);}" "QTreeView::branch:has-children:!has-siblings:closed, QTreeView::branch:closed:has-children:has-siblings {border-image: none; image: url(:/resources/tree-branch-closed.png);}"
"QTreeView::branch:open:has-children:!has-siblings, QTreeView::branch:open:has-children:has-siblings {border-image: none; image: url(:/resources/tree-branch-open.png);}"); "QTreeView::branch:open:has-children:!has-siblings, QTreeView::branch:open:has-children:has-siblings {border-image: none; image: url(:/resources/tree-branch-open.png);}");
QFont font; m_normalFont.setWeight(QFont::Light);
font.setWeight(QFont::Light); m_normalFont.setPixelSize(9);
font.setPixelSize(9); m_normalFont.setBold(false);
font.setBold(false);
setFont(font); m_selectedFont.setWeight(QFont::Light);
m_selectedFont.setPixelSize(9);
m_selectedFont.setBold(true);
setFont(m_normalFont);
connect(this, &QTreeWidget::customContextMenuRequested, this, &SkeletonPartTreeWidget::showContextMenu); connect(this, &QTreeWidget::customContextMenuRequested, this, &SkeletonPartTreeWidget::showContextMenu);
connect(this, &QTreeWidget::itemChanged, this, &SkeletonPartTreeWidget::groupChanged); connect(this, &QTreeWidget::itemChanged, this, &SkeletonPartTreeWidget::groupChanged);
connect(this, &QTreeWidget::itemExpanded, this, &SkeletonPartTreeWidget::groupExpanded); connect(this, &QTreeWidget::itemExpanded, this, &SkeletonPartTreeWidget::groupExpanded);
connect(this, &QTreeWidget::itemCollapsed, this, &SkeletonPartTreeWidget::groupCollapsed); connect(this, &QTreeWidget::itemCollapsed, this, &SkeletonPartTreeWidget::groupCollapsed);
connect(this, &QTreeWidget::currentItemChanged, this, &SkeletonPartTreeWidget::currentGroupChanged);
} }
void SkeletonPartTreeWidget::currentGroupChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous) void SkeletonPartTreeWidget::selectComponent(QUuid componentId)
{ {
if (nullptr != current) { if (m_currentSelectedComponent != componentId) {
auto componentId = QUuid(current->data(0, Qt::UserRole).toString()); if (!m_currentSelectedComponent.isNull()) {
const SkeletonComponent *component = m_document->findComponent(componentId); auto item = m_componentItemMap.find(m_currentSelectedComponent);
if (nullptr != component) { if (item != m_componentItemMap.end()) {
emit currentComponentChanged(componentId); item->second->setFont(0, m_normalFont);
return; }
} }
m_currentSelectedComponent = componentId;
if (!m_currentSelectedComponent.isNull()) {
auto item = m_componentItemMap.find(m_currentSelectedComponent);
if (item != m_componentItemMap.end()) {
item->second->setFont(0, m_selectedFont);
}
}
emit currentComponentChanged(m_currentSelectedComponent);
} }
emit currentComponentChanged(QUuid());
} }
void SkeletonPartTreeWidget::mousePressEvent(QMouseEvent *event) void SkeletonPartTreeWidget::mousePressEvent(QMouseEvent *event)
{ {
QModelIndex item = indexAt(event->pos()); QModelIndex itemIndex = indexAt(event->pos());
if (item.isValid()) { QTreeView::mousePressEvent(event);
QTreeView::mousePressEvent(event); if (itemIndex.isValid()) {
} else { QTreeWidgetItem *item = itemFromIndex(itemIndex);
clearSelection(); auto componentId = QUuid(item->data(0, Qt::UserRole).toString());
QTreeView::mousePressEvent(event); if (m_componentItemMap.find(componentId) != m_componentItemMap.end()) {
emit currentComponentChanged(QUuid()); selectComponent(componentId);
return;
}
item = item->parent();
if (nullptr != item) {
componentId = QUuid(item->data(0, Qt::UserRole).toString());
if (m_componentItemMap.find(componentId) != m_componentItemMap.end()) {
selectComponent(componentId);
return;
}
}
} }
selectComponent(QUuid());
} }
void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos) void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
@ -92,9 +118,13 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
contextMenu.addSection(component->name); contextMenu.addSection(component->name);
QAction showAction(tr("Show"), this); QAction showAction(tr("Show"), this);
showAction.setIcon(Theme::awesome()->icon(fa::eye));
QAction hideAction(tr("Hide"), this); QAction hideAction(tr("Hide"), this);
hideAction.setIcon(Theme::awesome()->icon(fa::eyeslash));
QAction lockAction(tr("Lock"), this); QAction lockAction(tr("Lock"), this);
lockAction.setIcon(Theme::awesome()->icon(fa::lock));
QAction unlockAction(tr("Unlock"), this); QAction unlockAction(tr("Unlock"), this);
unlockAction.setIcon(Theme::awesome()->icon(fa::unlock));
QAction invertAction(tr("Invert"), this); QAction invertAction(tr("Invert"), this);
QAction cancelInverseAction(tr("Cancel Inverse"), this); QAction cancelInverseAction(tr("Cancel Inverse"), this);
QAction selectAction(tr("Select"), this); QAction selectAction(tr("Select"), this);
@ -176,13 +206,22 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
contextMenu.addSeparator(); contextMenu.addSeparator();
QMenu *smoothMenu = contextMenu.addMenu(tr("Smooth"));
QWidgetAction smoothAction(this);
smoothAction.setDefaultWidget(createSmoothMenuWidget(componentId));
smoothMenu->addAction(&smoothAction);
contextMenu.addSeparator();
QAction hideOthersAction(tr("Hide Others"), this); QAction hideOthersAction(tr("Hide Others"), this);
hideOthersAction.setIcon(Theme::awesome()->icon(fa::eyeslash));
connect(&hideOthersAction, &QAction::triggered, [=]() { connect(&hideOthersAction, &QAction::triggered, [=]() {
emit hideOtherComponents(componentId); emit hideOtherComponents(componentId);
}); });
contextMenu.addAction(&hideOthersAction); contextMenu.addAction(&hideOthersAction);
QAction lockOthersAction(tr("Lock Others"), this); QAction lockOthersAction(tr("Lock Others"), this);
lockOthersAction.setIcon(Theme::awesome()->icon(fa::lock));
connect(&lockOthersAction, &QAction::triggered, [=]() { connect(&lockOthersAction, &QAction::triggered, [=]() {
emit lockOtherComponents(componentId); emit lockOtherComponents(componentId);
}); });
@ -205,12 +244,14 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
contextMenu.addSeparator(); contextMenu.addSeparator();
QAction hideAllAction(tr("Hide All"), this); QAction hideAllAction(tr("Hide All"), this);
hideAllAction.setIcon(Theme::awesome()->icon(fa::eyeslash));
connect(&hideAllAction, &QAction::triggered, [=]() { connect(&hideAllAction, &QAction::triggered, [=]() {
emit hideAllComponents(); emit hideAllComponents();
}); });
contextMenu.addAction(&hideAllAction); contextMenu.addAction(&hideAllAction);
QAction showAllAction(tr("Show All"), this); QAction showAllAction(tr("Show All"), this);
showAllAction.setIcon(Theme::awesome()->icon(fa::eye));
connect(&showAllAction, &QAction::triggered, [=]() { connect(&showAllAction, &QAction::triggered, [=]() {
emit showAllComponents(); emit showAllComponents();
}); });
@ -219,12 +260,14 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
contextMenu.addSeparator(); contextMenu.addSeparator();
QAction lockAllAction(tr("Lock All"), this); QAction lockAllAction(tr("Lock All"), this);
lockAllAction.setIcon(Theme::awesome()->icon(fa::lock));
connect(&lockAllAction, &QAction::triggered, [=]() { connect(&lockAllAction, &QAction::triggered, [=]() {
emit lockAllComponents(); emit lockAllComponents();
}); });
contextMenu.addAction(&lockAllAction); contextMenu.addAction(&lockAllAction);
QAction unlockAllAction(tr("Unlock All"), this); QAction unlockAllAction(tr("Unlock All"), this);
unlockAllAction.setIcon(Theme::awesome()->icon(fa::unlock));
connect(&unlockAllAction, &QAction::triggered, [=]() { connect(&unlockAllAction, &QAction::triggered, [=]() {
emit unlockAllComponents(); emit unlockAllComponents();
}); });
@ -234,25 +277,29 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
QMenu *moveToMenu = contextMenu.addMenu(tr("Move To")); QMenu *moveToMenu = contextMenu.addMenu(tr("Move To"));
QAction moveToTopAction(tr("Top"), this);
moveToTopAction.setIcon(Theme::awesome()->icon(fa::angledoubleup));
connect(&moveToTopAction, &QAction::triggered, [=]() {
emit moveComponentToTop(componentId);
});
moveToMenu->addAction(&moveToTopAction);
QAction moveUpAction(tr("Up"), this); QAction moveUpAction(tr("Up"), this);
moveUpAction.setIcon(Theme::awesome()->icon(fa::angleup));
connect(&moveUpAction, &QAction::triggered, [=]() { connect(&moveUpAction, &QAction::triggered, [=]() {
emit moveComponentUp(componentId); emit moveComponentUp(componentId);
}); });
moveToMenu->addAction(&moveUpAction); moveToMenu->addAction(&moveUpAction);
QAction moveDownAction(tr("Down"), this); QAction moveDownAction(tr("Down"), this);
moveDownAction.setIcon(Theme::awesome()->icon(fa::angledown));
connect(&moveDownAction, &QAction::triggered, [=]() { connect(&moveDownAction, &QAction::triggered, [=]() {
emit moveComponentDown(componentId); emit moveComponentDown(componentId);
}); });
moveToMenu->addAction(&moveDownAction); moveToMenu->addAction(&moveDownAction);
QAction moveToTopAction(tr("Top"), this);
connect(&moveToTopAction, &QAction::triggered, [=]() {
emit moveComponentToTop(componentId);
});
moveToMenu->addAction(&moveToTopAction);
QAction moveToBottomAction(tr("Bottom"), this); QAction moveToBottomAction(tr("Bottom"), this);
moveToBottomAction.setIcon(Theme::awesome()->icon(fa::angledoubledown));
connect(&moveToBottomAction, &QAction::triggered, [=]() { connect(&moveToBottomAction, &QAction::triggered, [=]() {
emit moveComponentToBottom(componentId); emit moveComponentToBottom(componentId);
}); });
@ -261,6 +308,7 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
moveToMenu->addSeparator(); moveToMenu->addSeparator();
QAction moveToNewGroupAction(tr("New Group"), this); QAction moveToNewGroupAction(tr("New Group"), this);
moveToNewGroupAction.setIcon(Theme::awesome()->icon(fa::file));
moveToMenu->addAction(&moveToNewGroupAction); moveToMenu->addAction(&moveToNewGroupAction);
connect(&moveToNewGroupAction, &QAction::triggered, [=]() { connect(&moveToNewGroupAction, &QAction::triggered, [=]() {
emit createNewComponentAndMoveThisIn(componentId); emit createNewComponentAndMoveThisIn(componentId);
@ -297,6 +345,7 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
contextMenu.addSeparator(); contextMenu.addSeparator();
QAction deleteAction(tr("Delete"), this); QAction deleteAction(tr("Delete"), this);
deleteAction.setIcon(Theme::awesome()->icon(fa::trash));
connect(&deleteAction, &QAction::triggered, [=]() { connect(&deleteAction, &QAction::triggered, [=]() {
emit removeComponent(componentId); emit removeComponent(componentId);
}); });
@ -309,6 +358,78 @@ void SkeletonPartTreeWidget::showContextMenu(const QPoint &pos)
} }
} }
QWidget *SkeletonPartTreeWidget::createSmoothMenuWidget(QUuid componentId)
{
QWidget *popup = new QWidget;
const SkeletonComponent *component = m_document->findComponent(componentId);
if (!component) {
qDebug() << "Find component failed:" << componentId;
return popup;
}
bool showSeamControl = component->linkToPartId.isNull();
FloatNumberWidget *smoothAllWidget = new FloatNumberWidget;
smoothAllWidget->setItemName(tr("All"));
smoothAllWidget->setRange(0, 1);
smoothAllWidget->setValue(component->smoothAll);
connect(smoothAllWidget, &FloatNumberWidget::valueChanged, [=](float value) {
emit setComponentSmoothAll(componentId, value);
emit groupOperationAdded();
});
QPushButton *smoothAllEraser = new QPushButton(QChar(fa::eraser));
Theme::initAwesomeToolButton(smoothAllEraser);
connect(smoothAllEraser, &QPushButton::clicked, [=]() {
smoothAllWidget->setValue(0.0);
});
FloatNumberWidget *smoothSeamWidget = nullptr;
QPushButton *smoothSeamEraser = nullptr;
if (showSeamControl) {
smoothSeamWidget = new FloatNumberWidget;
smoothSeamWidget->setItemName(tr("Seam"));
smoothSeamWidget->setRange(0, 1);
smoothSeamWidget->setValue(component->smoothSeam);
connect(smoothSeamWidget, &FloatNumberWidget::valueChanged, [=](float value) {
emit setComponentSmoothSeam(componentId, value);
emit groupOperationAdded();
});
smoothSeamEraser = new QPushButton(QChar(fa::eraser));
Theme::initAwesomeToolButton(smoothSeamEraser);
connect(smoothSeamEraser, &QPushButton::clicked, [=]() {
smoothSeamWidget->setValue(0.0);
});
}
QHBoxLayout *smoothSeamLayout = nullptr;
QVBoxLayout *layout = new QVBoxLayout;
QHBoxLayout *smoothAllLayout = new QHBoxLayout;
if (showSeamControl)
smoothSeamLayout = new QHBoxLayout;
smoothAllLayout->addWidget(smoothAllEraser);
smoothAllLayout->addWidget(smoothAllWidget);
if (showSeamControl) {
smoothSeamLayout->addWidget(smoothSeamEraser);
smoothSeamLayout->addWidget(smoothSeamWidget);
}
layout->addLayout(smoothAllLayout);
if (showSeamControl)
layout->addLayout(smoothSeamLayout);
popup->setLayout(layout);
return popup;
}
QTreeWidgetItem *SkeletonPartTreeWidget::findComponentItem(QUuid componentId) QTreeWidgetItem *SkeletonPartTreeWidget::findComponentItem(QUuid componentId)
{ {
auto findResult = m_componentItemMap.find(componentId); auto findResult = m_componentItemMap.find(componentId);
@ -379,7 +500,7 @@ void SkeletonPartTreeWidget::addComponentChildrenToItem(QUuid componentId, QTree
item->setData(0, Qt::UserRole, QVariant(component->id.toString())); item->setData(0, Qt::UserRole, QVariant(component->id.toString()));
item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
item->setExpanded(component->expanded); item->setExpanded(component->expanded);
item->setFlags(item->flags() | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); item->setFlags((item->flags() | Qt::ItemIsEditable | Qt::ItemIsEnabled) & ~(Qt::ItemIsSelectable));
m_componentItemMap[childId] = item; m_componentItemMap[childId] = item;
addComponentChildrenToItem(childId, item); addComponentChildrenToItem(childId, item);
} }

View File

@ -20,6 +20,8 @@ signals:
void createNewComponentAndMoveThisIn(QUuid componentId); void createNewComponentAndMoveThisIn(QUuid componentId);
void renameComponent(QUuid componentId, QString name); void renameComponent(QUuid componentId, QString name);
void setComponentExpandState(QUuid componentId, bool expanded); void setComponentExpandState(QUuid componentId, bool expanded);
void setComponentSmoothAll(QUuid componentId, float toSmoothAll);
void setComponentSmoothSeam(QUuid componentId, float toSmoothSeam);
void moveComponent(QUuid componentId, QUuid toParentId); void moveComponent(QUuid componentId, QUuid toParentId);
void removeComponent(QUuid componentId); void removeComponent(QUuid componentId);
void hideOtherComponents(QUuid componentId); void hideOtherComponents(QUuid componentId);
@ -38,6 +40,7 @@ signals:
void lockDescendantComponents(QUuid componentId); void lockDescendantComponents(QUuid componentId);
void unlockDescendantComponents(QUuid componentId); void unlockDescendantComponents(QUuid componentId);
void addPartToSelection(QUuid partId); void addPartToSelection(QUuid partId);
void groupOperationAdded();
public: public:
SkeletonPartTreeWidget(const SkeletonDocument *document, QWidget *parent); SkeletonPartTreeWidget(const SkeletonDocument *document, QWidget *parent);
QTreeWidgetItem *findComponentItem(QUuid componentId); QTreeWidgetItem *findComponentItem(QUuid componentId);
@ -64,7 +67,6 @@ public slots:
void groupChanged(QTreeWidgetItem *item, int column); void groupChanged(QTreeWidgetItem *item, int column);
void groupExpanded(QTreeWidgetItem *item); void groupExpanded(QTreeWidgetItem *item);
void groupCollapsed(QTreeWidgetItem *item); void groupCollapsed(QTreeWidgetItem *item);
void currentGroupChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void removeAllContent(); void removeAllContent();
void showContextMenu(const QPoint &pos); void showContextMenu(const QPoint &pos);
protected: protected:
@ -74,11 +76,16 @@ protected:
private: private:
void addComponentChildrenToItem(QUuid componentId, QTreeWidgetItem *parentItem); void addComponentChildrenToItem(QUuid componentId, QTreeWidgetItem *parentItem);
void deleteItemChildren(QTreeWidgetItem *item); void deleteItemChildren(QTreeWidgetItem *item);
void selectComponent(QUuid componentId);
QWidget *createSmoothMenuWidget(QUuid componentId);
private: private:
const SkeletonDocument *m_document = nullptr; const SkeletonDocument *m_document = nullptr;
std::map<QUuid, QTreeWidgetItem *> m_partItemMap; std::map<QUuid, QTreeWidgetItem *> m_partItemMap;
std::map<QUuid, QTreeWidgetItem *> m_componentItemMap; std::map<QUuid, QTreeWidgetItem *> m_componentItemMap;
SkeletonGraphicsFunctions *m_graphicsFunctions = nullptr; SkeletonGraphicsFunctions *m_graphicsFunctions = nullptr;
QFont m_normalFont;
QFont m_selectedFont;
QUuid m_currentSelectedComponent;
}; };
#endif #endif

View File

@ -263,15 +263,12 @@ void SkeletonPartWidget::mouseDoubleClickEvent(QMouseEvent *event)
void SkeletonPartWidget::initToolButtonWithoutFont(QPushButton *button) void SkeletonPartWidget::initToolButtonWithoutFont(QPushButton *button)
{ {
button->setFixedSize(Theme::toolIconSize / 2, Theme::toolIconSize / 2); Theme::initAwesomeToolButtonWithoutFont(button);
button->setStyleSheet("QPushButton {color: #f7d9c8}");
button->setFocusPolicy(Qt::NoFocus);
} }
void SkeletonPartWidget::initToolButton(QPushButton *button) void SkeletonPartWidget::initToolButton(QPushButton *button)
{ {
button->setFont(Theme::awesome()->font(Theme::toolIconFontSize / 2)); Theme::initAwesomeToolButton(button);
initToolButtonWithoutFont(button);
} }
void SkeletonPartWidget::showColorSettingPopup(const QPoint &pos) void SkeletonPartWidget::showColorSettingPopup(const QPoint &pos)
@ -289,10 +286,6 @@ void SkeletonPartWidget::showColorSettingPopup(const QPoint &pos)
QPushButton *colorEraser = new QPushButton(QChar(fa::eraser)); QPushButton *colorEraser = new QPushButton(QChar(fa::eraser));
initToolButton(colorEraser); initToolButton(colorEraser);
//QLabel *colorPreviewLabel = new QLabel;
//colorPreviewLabel->setFixedSize(Theme::miniIconSize, Theme::miniIconSize);
//colorPreviewLabel->setAutoFillBackground(true);
QPushButton *pickButton = new QPushButton(); QPushButton *pickButton = new QPushButton();
initToolButtonWithoutFont(pickButton); initToolButtonWithoutFont(pickButton);
QPalette palette = pickButton->palette(); QPalette palette = pickButton->palette();
@ -343,14 +336,17 @@ void SkeletonPartWidget::showDeformSettingPopup(const QPoint &pos)
QWidget *popup = new QWidget; QWidget *popup = new QWidget;
FloatNumberWidget *thicknessWidget = new FloatNumberWidget; FloatNumberWidget *thicknessWidget = new FloatNumberWidget;
thicknessWidget->setItemName(tr("Thickness"));
thicknessWidget->setRange(0, 2); thicknessWidget->setRange(0, 2);
thicknessWidget->setValue(part->deformThickness); thicknessWidget->setValue(part->deformThickness);
connect(thicknessWidget, &FloatNumberWidget::valueChanged, [=](float value) { connect(thicknessWidget, &FloatNumberWidget::valueChanged, [=](float value) {
emit setPartDeformThickness(m_partId, value); emit setPartDeformThickness(m_partId, value);
emit groupOperationAdded();
}); });
FloatNumberWidget *widthWidget = new FloatNumberWidget; FloatNumberWidget *widthWidget = new FloatNumberWidget;
widthWidget->setItemName(tr("Width"));
widthWidget->setRange(0, 2); widthWidget->setRange(0, 2);
widthWidget->setValue(part->deformWidth); widthWidget->setValue(part->deformWidth);
@ -364,6 +360,7 @@ void SkeletonPartWidget::showDeformSettingPopup(const QPoint &pos)
connect(thicknessEraser, &QPushButton::clicked, [=]() { connect(thicknessEraser, &QPushButton::clicked, [=]() {
thicknessWidget->setValue(1.0); thicknessWidget->setValue(1.0);
emit groupOperationAdded();
}); });
QPushButton *widthEraser = new QPushButton(QChar(fa::eraser)); QPushButton *widthEraser = new QPushButton(QChar(fa::eraser));
@ -371,6 +368,7 @@ void SkeletonPartWidget::showDeformSettingPopup(const QPoint &pos)
connect(widthEraser, &QPushButton::clicked, [=]() { connect(widthEraser, &QPushButton::clicked, [=]() {
widthWidget->setValue(1.0); widthWidget->setValue(1.0);
emit groupOperationAdded();
}); });
QVBoxLayout *layout = new QVBoxLayout; QVBoxLayout *layout = new QVBoxLayout;
@ -385,35 +383,22 @@ void SkeletonPartWidget::showDeformSettingPopup(const QPoint &pos)
popup->setLayout(layout); popup->setLayout(layout);
QWidgetAction *action = new QWidgetAction(this); QWidgetAction action(this);
action->setDefaultWidget(popup); action.setDefaultWidget(popup);
popupMenu.addAction(action); popupMenu.addAction(&action);
popupMenu.exec(mapToGlobal(pos)); popupMenu.exec(mapToGlobal(pos));
} }
void SkeletonPartWidget::initButton(QPushButton *button) void SkeletonPartWidget::initButton(QPushButton *button)
{ {
button->setFont(Theme::awesome()->font(Theme::miniIconFontSize)); Theme::initAwesomeMiniButton(button);
button->setFixedSize(Theme::miniIconSize, Theme::miniIconSize);
button->setFocusPolicy(Qt::NoFocus);
} }
void SkeletonPartWidget::updateButton(QPushButton *button, QChar icon, bool highlighted) void SkeletonPartWidget::updateButton(QPushButton *button, QChar icon, bool highlighted)
{ {
button->setText(icon); Theme::updateAwesomeMiniButton(button, icon, highlighted);
QColor color;
if (highlighted)
color = QColor("#fc6621");
else
color = QColor("#525252");
color = color.toHsv();
color.setHsv(color.hue(), color.saturation() / 5, color.value() * 2 / 3);
color = color.toRgb();
button->setStyleSheet("QPushButton {border: none; background: none; color: " + color.name() + ";}");
} }
void SkeletonPartWidget::updatePreview() void SkeletonPartWidget::updatePreview()

View File

@ -92,4 +92,39 @@ void Theme::initAwesomeLabel(QLabel *label)
label->setStyleSheet("QLabel {color: #f7d9c8}"); label->setStyleSheet("QLabel {color: #f7d9c8}");
} }
void Theme::initAwesomeMiniButton(QPushButton *button)
{
button->setFont(Theme::awesome()->font(Theme::miniIconFontSize));
button->setFixedSize(Theme::miniIconSize, Theme::miniIconSize);
button->setFocusPolicy(Qt::NoFocus);
}
void Theme::updateAwesomeMiniButton(QPushButton *button, QChar icon, bool highlighted)
{
button->setText(icon);
QColor color;
if (highlighted)
color = QColor("#fc6621");
else
color = QColor("#525252");
color = color.toHsv();
color.setHsv(color.hue(), color.saturation() / 5, color.value() * 2 / 3);
color = color.toRgb();
button->setStyleSheet("QPushButton {border: none; background: none; color: " + color.name() + ";}");
}
void Theme::initAwesomeToolButtonWithoutFont(QPushButton *button)
{
button->setFixedSize(Theme::toolIconSize / 2, Theme::toolIconSize / 2);
button->setStyleSheet("QPushButton {color: #f7d9c8}");
button->setFocusPolicy(Qt::NoFocus);
}
void Theme::initAwesomeToolButton(QPushButton *button)
{
button->setFont(Theme::awesome()->font(Theme::toolIconFontSize / 2));
Theme::initAwesomeToolButtonWithoutFont(button);
}

View File

@ -40,6 +40,10 @@ public:
static void initAwesomeButton(QPushButton *button); static void initAwesomeButton(QPushButton *button);
static void initAwesomeLabel(QLabel *label); static void initAwesomeLabel(QLabel *label);
static void initAwesomeSmallButton(QPushButton *button); static void initAwesomeSmallButton(QPushButton *button);
static void initAwesomeMiniButton(QPushButton *button);
static void updateAwesomeMiniButton(QPushButton *button, QChar icon, bool highlighted);
static void initAwesomeToolButton(QPushButton *button);
static void initAwesomeToolButtonWithoutFont(QPushButton *button);
}; };
#endif #endif

View File

@ -59,6 +59,8 @@ int meshlite_skeletonmesh_create(void *context);
int meshlite_skeletonmesh_set_end_radius(void *context, float radius); int meshlite_skeletonmesh_set_end_radius(void *context, float radius);
int meshlite_skeletonmesh_add_bone(void *context, int sklt_id, float from_x, float from_y, float from_z, float to_x, float to_y, float to_z); int meshlite_skeletonmesh_add_bone(void *context, int sklt_id, float from_x, float from_y, float from_z, float to_x, float to_y, float to_z);
int meshlite_skeletonmesh_generate_mesh(void *context, int sklt_id); int meshlite_skeletonmesh_generate_mesh(void *context, int sklt_id);
int meshlite_smooth_vertices(void *context, int mesh_id, float factor, int *buffer, int max_buffer_len);
int meshlite_smooth(void *context, int mesh_id, float factor);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -59,6 +59,8 @@ int meshlite_skeletonmesh_create(void *context);
int meshlite_skeletonmesh_set_end_radius(void *context, float radius); int meshlite_skeletonmesh_set_end_radius(void *context, float radius);
int meshlite_skeletonmesh_add_bone(void *context, int sklt_id, float from_x, float from_y, float from_z, float to_x, float to_y, float to_z); int meshlite_skeletonmesh_add_bone(void *context, int sklt_id, float from_x, float from_y, float from_z, float to_x, float to_y, float to_z);
int meshlite_skeletonmesh_generate_mesh(void *context, int sklt_id); int meshlite_skeletonmesh_generate_mesh(void *context, int sklt_id);
int meshlite_smooth_vertices(void *context, int mesh_id, float factor, int *buffer, int max_buffer_len);
int meshlite_smooth(void *context, int mesh_id, float factor);
#ifdef __cplusplus #ifdef __cplusplus
} }