Add wrap button to enable use controlled convex hull generation for part.

This feature could be useful to generate complex mesh as part.
master
Jeremy Hu 2018-08-31 12:54:32 +08:00
parent 6bab154cf5
commit df9c8e8e3c
17 changed files with 133 additions and 15 deletions

View File

@ -66,6 +66,8 @@ Keyboard
+----------------------+--------------------------------------------------------------------------+
| U | Toggle Part End Roundable |
+----------------------+--------------------------------------------------------------------------+
| W | Toggle Part Wrap Status: (W)rap using Convex hull/Normal |
+----------------------+--------------------------------------------------------------------------+
| TAB | Swith the Selected Nodes to Different Profile (Main / Side) |
+----------------------+--------------------------------------------------------------------------+

View File

@ -196,6 +196,7 @@ void *MeshGenerator::combinePartMesh(QString partId)
bool isDisabled = isTrueValueString(valueOfKeyInMapOrEmpty(part, "disabled"));
bool xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(part, "xMirrored"));
bool subdived = isTrueValueString(valueOfKeyInMapOrEmpty(part, "subdived"));
bool wrapped = isTrueValueString(valueOfKeyInMapOrEmpty(part, "wrapped"));
int bmeshId = meshlite_bmesh_create(m_meshliteContext);
if (subdived)
meshlite_bmesh_set_cut_subdiv_count(m_meshliteContext, bmeshId, 1);
@ -288,14 +289,21 @@ void *MeshGenerator::combinePartMesh(QString partId)
if (!bmeshToNodeIdMap.empty()) {
meshId = meshlite_bmesh_generate_mesh(m_meshliteContext, bmeshId);
loadVertexSources(m_meshliteContext, meshId, partIdNotAsString, bmeshToNodeIdMap, cacheBmeshVertices);
resultMesh = convertToCombinableMesh(m_meshliteContext, meshlite_triangulate(m_meshliteContext, meshId));
if (wrapped)
resultMesh = convertToCombinableConvexHullMesh(m_meshliteContext, meshId);
else
resultMesh = convertToCombinableMesh(m_meshliteContext, meshlite_triangulate(m_meshliteContext, meshId));
}
if (nullptr != resultMesh) {
if (xMirrored) {
int xMirroredMeshId = meshlite_mirror_in_x(m_meshliteContext, meshId, 0);
loadVertexSources(m_meshliteContext, xMirroredMeshId, mirroredPartIdNotAsString, bmeshToNodeIdMap, cacheBmeshVertices);
void *mirroredMesh = convertToCombinableMesh(m_meshliteContext, meshlite_triangulate(m_meshliteContext, xMirroredMeshId));
void *mirroredMesh = nullptr;
if (wrapped)
mirroredMesh = convertToCombinableConvexHullMesh(m_meshliteContext, xMirroredMeshId);
else
mirroredMesh = convertToCombinableMesh(m_meshliteContext, meshlite_triangulate(m_meshliteContext, xMirroredMeshId));
if (nullptr != mirroredMesh) {
void *newResultMesh = unionCombinableMeshs(resultMesh, mirroredMesh);
deleteCombinableMesh(mirroredMesh);

View File

@ -23,6 +23,7 @@
#include <CGAL/Polygon_mesh_processing/remesh.h>
#include <CGAL/subdivision_method_3.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/convex_hull_3.h>
typedef CGAL::Exact_predicates_exact_constructions_kernel ExactKernel;
typedef CGAL::Simple_cartesian<double> SimpleKernel;
@ -132,6 +133,34 @@ int makeMeshliteMeshFromCgal(void *meshlite, typename CGAL::Surface_mesh<typenam
return meshId;
}
template <class Kernel>
ExactMesh *makeCgalConvexHullMeshFromMeshlite(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<typename Kernel::Point_3> points;
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;
points.push_back(typename Kernel::Point_3(x, y, z));
offset += 3;
}
delete[] vertexPositions;
CGAL::convex_hull_3(points.begin(), points.end(), *mesh);
return mesh;
}
ExactMesh *unionCgalMeshs(ExactMesh *first, ExactMesh *second)
{
ExactMesh *mesh = new ExactMesh;
@ -315,3 +344,8 @@ void *cloneCombinableMesh(void *mesh)
return (void *)new ExactMesh(*(ExactMesh *)mesh);
}
void *convertToCombinableConvexHullMesh(void *meshliteContext, int meshId)
{
ExactMesh *mesh = makeCgalConvexHullMeshFromMeshlite<ExactKernel>(meshliteContext, meshId);
return (void *)mesh;
}

View File

@ -15,5 +15,6 @@ void *diffCombinableMeshs(void *first, void *second);
int convertFromCombinableMesh(void *meshliteContext, void *mesh);
void deleteCombinableMesh(void *mesh);
void *cloneCombinableMesh(void *mesh);
void *convertToCombinableConvexHullMesh(void *meshliteContext, int meshId);
#endif

View File

@ -661,6 +661,7 @@ void SkeletonDocument::toSnapshot(SkeletonSnapshot *snapshot, const std::set<QUu
part["xMirrored"] = partIt.second.xMirrored ? "true" : "false";
part["zMirrored"] = partIt.second.zMirrored ? "true" : "false";
part["rounded"] = partIt.second.rounded ? "true" : "false";
part["wrapped"] = partIt.second.wrapped ? "true" : "false";
part["dirty"] = partIt.second.dirty ? "true" : "false";
if (partIt.second.hasColor)
part["color"] = partIt.second.color.name();
@ -777,6 +778,7 @@ void SkeletonDocument::addFromSnapshot(const SkeletonSnapshot &snapshot)
part.xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "xMirrored"));
part.zMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "zMirrored"));
part.rounded = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "rounded"));
part.wrapped = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "wrapped"));
if (isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "inverse")))
inversePartIds.insert(part.id);
const auto &colorIt = partKv.second.find("color");
@ -1733,6 +1735,21 @@ void SkeletonDocument::setPartRoundState(QUuid partId, bool rounded)
emit skeletonChanged();
}
void SkeletonDocument::setPartWrapState(QUuid partId, bool wrapped)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (part->second.wrapped == wrapped)
return;
part->second.wrapped = wrapped;
part->second.dirty = true;
emit partWrapStateChanged(partId);
emit skeletonChanged();
}
void SkeletonDocument::setPartColorState(QUuid partId, bool hasColor, QColor color)
{
auto part = partMap.find(partId);

View File

@ -87,6 +87,7 @@ public:
QUuid componentId;
std::vector<QUuid> nodeIds;
bool dirty;
bool wrapped;
SkeletonPart(const QUuid &withId=QUuid()) :
visible(true),
locked(false),
@ -99,7 +100,8 @@ public:
rounded(false),
color(Theme::white),
hasColor(false),
dirty(true)
dirty(true),
wrapped(false)
{
id = withId.isNull() ? QUuid::createUuid() : withId;
}
@ -148,6 +150,7 @@ public:
rounded = other.rounded;
color = other.color;
hasColor = other.hasColor;
wrapped = other.wrapped;
componentId = other.componentId;
}
};
@ -328,6 +331,7 @@ signals:
void partDeformWidthChanged(QUuid partId);
void partRoundStateChanged(QUuid partId);
void partColorStateChanged(QUuid partId);
void partWrapStateChanged(QUuid partId);
void componentInverseStateChanged(QUuid partId);
void cleanup();
void originChanged();
@ -425,6 +429,7 @@ public slots:
void setPartDeformWidth(QUuid partId, float width);
void setPartRoundState(QUuid partId, bool rounded);
void setPartColorState(QUuid partId, bool hasColor, QColor color);
void setPartWrapState(QUuid partId, bool wrapped);
void setComponentInverseState(QUuid componentId, bool inverse);
void moveComponentUp(QUuid componentId);
void moveComponentDown(QUuid componentId);

View File

@ -547,6 +547,7 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
connect(graphicsWidget, &SkeletonGraphicsWidget::setPartDisableState, m_document, &SkeletonDocument::setPartDisableState);
connect(graphicsWidget, &SkeletonGraphicsWidget::setPartXmirrorState, m_document, &SkeletonDocument::setPartXmirrorState);
connect(graphicsWidget, &SkeletonGraphicsWidget::setPartRoundState, m_document, &SkeletonDocument::setPartRoundState);
connect(graphicsWidget, &SkeletonGraphicsWidget::setPartWrapState, m_document, &SkeletonDocument::setPartWrapState);
connect(graphicsWidget, &SkeletonGraphicsWidget::setXlockState, m_document, &SkeletonDocument::setXlockState);
connect(graphicsWidget, &SkeletonGraphicsWidget::setYlockState, m_document, &SkeletonDocument::setYlockState);
@ -614,6 +615,7 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
connect(m_document, &SkeletonDocument::partDeformThicknessChanged, partTreeWidget, &SkeletonPartTreeWidget::partDeformChanged);
connect(m_document, &SkeletonDocument::partDeformWidthChanged, partTreeWidget, &SkeletonPartTreeWidget::partDeformChanged);
connect(m_document, &SkeletonDocument::partRoundStateChanged, partTreeWidget, &SkeletonPartTreeWidget::partRoundStateChanged);
connect(m_document, &SkeletonDocument::partWrapStateChanged, partTreeWidget, &SkeletonPartTreeWidget::partWrapStateChanged);
connect(m_document, &SkeletonDocument::partColorStateChanged, partTreeWidget, &SkeletonPartTreeWidget::partColorStateChanged);
connect(m_document, &SkeletonDocument::partRemoved, partTreeWidget, &SkeletonPartTreeWidget::partRemoved);
connect(m_document, &SkeletonDocument::cleanup, partTreeWidget, &SkeletonPartTreeWidget::removeAllContent);

View File

@ -1502,6 +1502,14 @@ bool SkeletonGraphicsWidget::keyPress(QKeyEvent *event)
emit groupOperationAdded();
return true;
}
} else if (event->key() == Qt::Key_W) {
if (SkeletonDocumentEditMode::Select == m_document->editMode && !m_lastCheckedPart.isNull()) {
const SkeletonPart *part = m_document->findPart(m_lastCheckedPart);
bool partWrapped = part && part->wrapped;
emit setPartWrapState(m_lastCheckedPart, !partWrapped);
emit groupOperationAdded();
return true;
}
}
return false;
}

View File

@ -379,6 +379,7 @@ signals:
void setPartDisableState(QUuid partId, bool disabled);
void setPartXmirrorState(QUuid partId, bool mirrored);
void setPartRoundState(QUuid partId, bool rounded);
void setPartWrapState(QUuid partId, bool wrapped);
void setXlockState(bool locked);
void setYlockState(bool locked);
void setZlockState(bool locked);

View File

@ -561,6 +561,17 @@ void SkeletonPartTreeWidget::partRoundStateChanged(QUuid partId)
widget->updateRoundButton();
}
void SkeletonPartTreeWidget::partWrapStateChanged(QUuid partId)
{
auto item = m_partItemMap.find(partId);
if (item == m_partItemMap.end()) {
qDebug() << "Part item not found:" << partId;
return;
}
SkeletonPartWidget *widget = (SkeletonPartWidget *)itemWidget(item->second, 0);
widget->updateWrapButton();
}
void SkeletonPartTreeWidget::partColorStateChanged(QUuid partId)
{
auto item = m_partItemMap.find(partId);

View File

@ -53,6 +53,7 @@ public slots:
void partXmirrorStateChanged(QUuid partId);
void partDeformChanged(QUuid partId);
void partRoundStateChanged(QUuid partId);
void partWrapStateChanged(QUuid partId);
void partColorStateChanged(QUuid partId);
void partChecked(QUuid partId);
void partUnchecked(QUuid partId);

View File

@ -48,6 +48,10 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
m_colorButton->setSizePolicy(retainSizePolicy);
initButton(m_colorButton);
m_wrapButton = new QPushButton;
m_wrapButton->setSizePolicy(retainSizePolicy);
initButton(m_wrapButton);
m_previewLabel = new QLabel;
m_previewLabel->setFixedSize(Theme::previewImageSize, Theme::previewImageSize);
@ -68,9 +72,9 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
toolsLayout->setContentsMargins(0, 0, 5, 0);
int row = 0;
int col = 0;
toolsLayout->addWidget(m_visibleButton, row, col++, Qt::AlignBottom);
toolsLayout->addWidget(m_lockButton, row, col++, Qt::AlignBottom);
toolsLayout->addWidget(m_disableButton, row, col++, Qt::AlignBottom);
toolsLayout->addWidget(m_wrapButton, row, col++, Qt::AlignBottom);
toolsLayout->addWidget(m_colorButton, row, col++, Qt::AlignBottom);
row++;
col = 0;
@ -79,17 +83,21 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
toolsLayout->addWidget(m_xMirrorButton, row, col++, Qt::AlignTop);
toolsLayout->addWidget(m_roundButton, row, col++, Qt::AlignTop);
m_visibleButton->setContentsMargins(0, 0, 0, 0);
QHBoxLayout *previewAndToolsLayout = new QHBoxLayout;
previewAndToolsLayout->setSpacing(0);
previewAndToolsLayout->setContentsMargins(0, 0, 0, 0);
previewAndToolsLayout->addWidget(m_visibleButton);
previewAndToolsLayout->addWidget(m_previewLabel);
previewAndToolsLayout->addLayout(toolsLayout);
previewAndToolsLayout->setStretch(0, 0);
previewAndToolsLayout->setStretch(1, 0);
previewAndToolsLayout->setStretch(2, 1);
QWidget *backgroundWidget = new QWidget;
backgroundWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
backgroundWidget->setFixedSize(Theme::previewImageSize + Theme::miniIconSize * 4 + 5, Theme::previewImageSize);
backgroundWidget->setFixedSize(preferredSize().width(), Theme::previewImageSize);
backgroundWidget->setObjectName("background");
m_backgroundWidget = backgroundWidget;
backgroundWidget->setLayout(previewAndToolsLayout);
@ -119,6 +127,7 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
connect(this, &SkeletonPartWidget::setPartDeformThickness, m_document, &SkeletonDocument::setPartDeformThickness);
connect(this, &SkeletonPartWidget::setPartDeformWidth, m_document, &SkeletonDocument::setPartDeformWidth);
connect(this, &SkeletonPartWidget::setPartRoundState, m_document, &SkeletonDocument::setPartRoundState);
connect(this, &SkeletonPartWidget::setPartWrapState, m_document, &SkeletonDocument::setPartWrapState);
connect(this, &SkeletonPartWidget::setPartColorState, m_document, &SkeletonDocument::setPartColorState);
connect(this, &SkeletonPartWidget::checkPart, m_document, &SkeletonDocument::checkPart);
connect(this, &SkeletonPartWidget::enableBackgroundBlur, m_document, &SkeletonDocument::enableBackgroundBlur);
@ -202,6 +211,16 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
showColorSettingPopup(mapFromGlobal(QCursor::pos()));
});
connect(m_wrapButton, &QPushButton::clicked, [=]() {
const SkeletonPart *part = m_document->findPart(m_partId);
if (!part) {
qDebug() << "Part not found:" << m_partId;
return;
}
emit setPartWrapState(m_partId, !part->wrapped);
emit groupOperationAdded();
});
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
setFixedSize(preferredSize());
@ -210,7 +229,7 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
QSize SkeletonPartWidget::preferredSize()
{
return QSize(Theme::previewImageSize + Theme::miniIconSize * 4 + 5 + 2, Theme::previewImageSize + 6);
return QSize(Theme::miniIconSize + Theme::previewImageSize + Theme::miniIconSize * 4 + 5 + 2, Theme::previewImageSize + 6);
}
void SkeletonPartWidget::updateAllButtons()
@ -223,6 +242,7 @@ void SkeletonPartWidget::updateAllButtons()
updateDeformButton();
updateRoundButton();
updateColorButton();
updateWrapButton();
}
void SkeletonPartWidget::updateCheckedState(bool checked)
@ -505,15 +525,21 @@ void SkeletonPartWidget::updateColorButton()
updateButton(m_colorButton, QChar(fa::eyedropper), false);
}
void SkeletonPartWidget::updateWrapButton()
{
const SkeletonPart *part = m_document->findPart(m_partId);
if (!part) {
qDebug() << "Part not found:" << m_partId;
return;
}
if (part->wrapped)
updateButton(m_wrapButton, QChar(fa::cube), true);
else
updateButton(m_wrapButton, QChar(fa::cube), false);
}
void SkeletonPartWidget::reload()
{
updatePreview();
updateLockButton();
updateVisibleButton();
updateSubdivButton();
updateDisableButton();
updateXmirrorButton();
updateDeformButton();
updateRoundButton();
updateColorButton();
updateAllButtons();
}

View File

@ -19,7 +19,7 @@ signals:
void setPartDeformWidth(QUuid partId, float width);
void setPartRoundState(QUuid partId, bool rounded);
void setPartColorState(QUuid partId, bool hasColor, QColor color);
void setPartInverseState(QUuid partId, bool inverse);
void setPartWrapState(QUuid partId, bool wrapped);
void movePartUp(QUuid partId);
void movePartDown(QUuid partId);
void movePartToTop(QUuid partId);
@ -41,6 +41,7 @@ public:
void updateDeformButton();
void updateRoundButton();
void updateColorButton();
void updateWrapButton();
void updateCheckedState(bool checked);
static QSize preferredSize();
protected:
@ -62,6 +63,7 @@ private:
QPushButton *m_deformButton;
QPushButton *m_roundButton;
QPushButton *m_colorButton;
QPushButton *m_wrapButton;
QLabel *m_nameLabel;
QWidget *m_backgroundWidget;
private: