Add wrap button to enable use controlled convex hull generation for part.
This feature could be useful to generate complex mesh as part.master
parent
6bab154cf5
commit
df9c8e8e3c
|
@ -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) |
|
||||
+----------------------+--------------------------------------------------------------------------+
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue