diff --git a/dust3d.pro b/dust3d.pro index 74e48df8..41492228 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -81,6 +81,9 @@ HEADERS += src/logbrowser.h SOURCES += src/logbrowserdialog.cpp HEADERS += src/logbrowserdialog.h +SOURCES += src/floatnumberwidget.cpp +HEADERS += src/floatnumberwidget.h + SOURCES += src/main.cpp HEADERS += src/version.h diff --git a/src/floatnumberwidget.cpp b/src/floatnumberwidget.cpp new file mode 100644 index 00000000..af6f2919 --- /dev/null +++ b/src/floatnumberwidget.cpp @@ -0,0 +1,50 @@ +#include +#include "floatnumberwidget.h" + +FloatNumberWidget::FloatNumberWidget(QWidget *parent) : + QWidget(parent) +{ + m_slider = new QSlider(Qt::Horizontal, this); + m_slider->setRange(0, 100); + + m_label = new QLabel(this); + m_label->setAlignment(Qt::AlignCenter); + m_label->setNum(0); + m_label->setFixedWidth(30); + + connect(m_slider, &QAbstractSlider::valueChanged, [=](int value) { + float fvalue = value / 100.0; + m_label->setText(QString().sprintf("%.2f", fvalue)); + emit valueChanged(fvalue); + }); + + QBoxLayout *popupLayout = new QHBoxLayout(this); + popupLayout->setMargin(2); + popupLayout->addWidget(m_slider); + popupLayout->addWidget(m_label); +} + +void FloatNumberWidget::setRange(float min, float max) +{ + m_slider->setRange(min * 100, max * 100); +} + +void FloatNumberWidget::increaseValue() +{ + m_slider->triggerAction(QSlider::SliderPageStepAdd); +} + +void FloatNumberWidget::descreaseValue() +{ + m_slider->triggerAction(QSlider::SliderPageStepSub); +} + +float FloatNumberWidget::value() const +{ + return m_slider->value() / 100.0; +} + +void FloatNumberWidget::setValue(float value) +{ + m_slider->setValue(value * 100); +} diff --git a/src/floatnumberwidget.h b/src/floatnumberwidget.h new file mode 100644 index 00000000..4df1d5a2 --- /dev/null +++ b/src/floatnumberwidget.h @@ -0,0 +1,29 @@ +#ifndef FLOAT_NUMBER_WIDGET_H +#define FLOAT_NUMBER_WIDGET_H +#include + +QT_FORWARD_DECLARE_CLASS(QLabel) +QT_FORWARD_DECLARE_CLASS(QSlider) + +class FloatNumberWidget : public QWidget +{ + Q_OBJECT +public: + explicit FloatNumberWidget(QWidget *parent = nullptr); + void setRange(float min, float max); + float value() const; + +public slots: + void increaseValue(); + void descreaseValue(); + void setValue(float value); + +signals: + void valueChanged(float value); + +private: + QLabel *m_label = nullptr; + QSlider *m_slider = nullptr; +}; + +#endif diff --git a/src/meshgenerator.cpp b/src/meshgenerator.cpp index 693ce4ac..138799dc 100644 --- a/src/meshgenerator.cpp +++ b/src/meshgenerator.cpp @@ -125,6 +125,9 @@ void MeshGenerator::process() int bmeshId = meshlite_bmesh_create(meshliteContext); if (subdived) meshlite_bmesh_set_cut_subdiv_count(meshliteContext, bmeshId, 1); + QString thicknessString = valueOfKeyInMapOrEmpty(part->second, "thickness"); + if (!thicknessString.isEmpty()) + meshlite_bmesh_set_thickness(meshliteContext, bmeshId, thicknessString.toFloat()); if (MeshGenerator::enableDebug) meshlite_bmesh_enable_debug(meshliteContext, bmeshId, 1); partBmeshMap[partIdIt] = bmeshId; diff --git a/src/skeletondocument.cpp b/src/skeletondocument.cpp index f973250e..8011479c 100644 --- a/src/skeletondocument.cpp +++ b/src/skeletondocument.cpp @@ -585,6 +585,8 @@ void SkeletonDocument::toSnapshot(SkeletonSnapshot *snapshot, const std::setparts[part["id"]] = part; @@ -655,6 +657,9 @@ void SkeletonDocument::addFromSnapshot(const SkeletonSnapshot &snapshot) part.disabled = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "disabled")); part.xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "xMirrored")); part.zMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "zMirrored")); + const auto &thicknessIt = partKv.second.find("thickness"); + if (thicknessIt != partKv.second.end()) + part.setThickness(thicknessIt->second.toFloat()); partMap[part.id] = part; } for (const auto &nodeKv : snapshot.nodes) { @@ -924,6 +929,18 @@ void SkeletonDocument::setPartZmirrorState(QUuid partId, bool mirrored) emit skeletonChanged(); } +void SkeletonDocument::setPartThickness(QUuid partId, float thickness) +{ + auto part = partMap.find(partId); + if (part == partMap.end()) { + qDebug() << "Part not found:" << partId; + return; + } + part->second.setThickness(thickness); + emit partThicknessChanged(partId); + emit skeletonChanged(); +} + void SkeletonDocument::saveSnapshot() { if (m_undoItems.size() + 1 > m_maxSnapshot) diff --git a/src/skeletondocument.h b/src/skeletondocument.h index 40d9014f..1f3d15b8 100644 --- a/src/skeletondocument.h +++ b/src/skeletondocument.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "skeletonsnapshot.h" #include "mesh.h" #include "meshgenerator.h" @@ -70,6 +71,7 @@ public: bool disabled; bool xMirrored; bool zMirrored; + float thickness; QImage preview; std::vector nodeIds; SkeletonPart(const QUuid &withId=QUuid()) : @@ -78,10 +80,23 @@ public: subdived(false), disabled(false), xMirrored(false), - zMirrored(false) + zMirrored(false), + thickness(1.0) { id = withId.isNull() ? QUuid::createUuid() : withId; } + void setThickness(float toThickness) + { + if (toThickness < 0) + toThickness = 0; + else if (toThickness > 2) + toThickness = 2; + thickness = toThickness; + } + bool thicknessAdjusted() const + { + return fabs(thickness - 1.0) >= 0.01; + } bool isEditVisible() const { return visible && !disabled; @@ -94,6 +109,7 @@ public: disabled = other.disabled; xMirrored = other.xMirrored; zMirrored = other.zMirrored; + thickness = other.thickness; } }; @@ -145,6 +161,7 @@ signals: void partDisableStateChanged(QUuid partId); void partXmirrorStateChanged(QUuid partId); void partZmirrorStateChanged(QUuid partId); + void partThicknessChanged(QUuid partId); void cleanup(); void originChanged(); void xlockStateChanged(); @@ -203,6 +220,7 @@ public slots: void setPartDisableState(QUuid partId, bool disabled); void setPartXmirrorState(QUuid partId, bool mirrored); void setPartZmirrorState(QUuid partId, bool mirrored); + void setPartThickness(QUuid partId, float thickness); void saveSnapshot(); void undo(); void redo(); diff --git a/src/skeletondocumentwindow.cpp b/src/skeletondocumentwindow.cpp index d189b6fa..64f1d8c6 100644 --- a/src/skeletondocumentwindow.cpp +++ b/src/skeletondocumentwindow.cpp @@ -182,7 +182,7 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : m_fileMenu = menuBar()->addMenu(tr("File")); m_newWindowAction = new QAction(tr("New Window"), this); - connect(m_newWindowAction, &QAction::triggered, this, &SkeletonDocumentWindow::newWindow); + connect(m_newWindowAction, &QAction::triggered, this, &SkeletonDocumentWindow::newWindow, Qt::QueuedConnection); m_fileMenu->addAction(m_newWindowAction); m_newDocumentAction = new QAction(tr("New"), this); @@ -190,29 +190,29 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : m_fileMenu->addAction(m_newDocumentAction); m_openAction = new QAction(tr("Open..."), this); - connect(m_openAction, &QAction::triggered, this, &SkeletonDocumentWindow::open); + connect(m_openAction, &QAction::triggered, this, &SkeletonDocumentWindow::open, Qt::QueuedConnection); m_fileMenu->addAction(m_openAction); m_saveAction = new QAction(tr("Save"), this); - connect(m_saveAction, &QAction::triggered, this, &SkeletonDocumentWindow::save); + connect(m_saveAction, &QAction::triggered, this, &SkeletonDocumentWindow::save, Qt::QueuedConnection); m_fileMenu->addAction(m_saveAction); m_saveAsAction = new QAction(tr("Save As..."), this); - connect(m_saveAsAction, &QAction::triggered, this, &SkeletonDocumentWindow::saveAs); + connect(m_saveAsAction, &QAction::triggered, this, &SkeletonDocumentWindow::saveAs, Qt::QueuedConnection); m_fileMenu->addAction(m_saveAsAction); m_saveAllAction = new QAction(tr("Save All"), this); - connect(m_saveAllAction, &QAction::triggered, this, &SkeletonDocumentWindow::saveAll); + connect(m_saveAllAction, &QAction::triggered, this, &SkeletonDocumentWindow::saveAll, Qt::QueuedConnection); m_fileMenu->addAction(m_saveAllAction); m_fileMenu->addSeparator(); m_exportAction = new QAction(tr("Export..."), this); - connect(m_exportAction, &QAction::triggered, this, &SkeletonDocumentWindow::exportResult); + connect(m_exportAction, &QAction::triggered, this, &SkeletonDocumentWindow::exportResult, Qt::QueuedConnection); m_fileMenu->addAction(m_exportAction); m_changeTurnaroundAction = new QAction(tr("Change Turnaround..."), this); - connect(m_changeTurnaroundAction, &QAction::triggered, this, &SkeletonDocumentWindow::changeTurnaround); + connect(m_changeTurnaroundAction, &QAction::triggered, this, &SkeletonDocumentWindow::changeTurnaround, Qt::QueuedConnection); m_fileMenu->addAction(m_changeTurnaroundAction); connect(m_fileMenu, &QMenu::aboutToShow, [=]() { @@ -422,7 +422,7 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() : connect(m_document, &SkeletonDocument::partSubdivStateChanged, partListWidget, &SkeletonPartListWidget::partSubdivStateChanged); connect(m_document, &SkeletonDocument::partDisableStateChanged, partListWidget, &SkeletonPartListWidget::partDisableStateChanged); connect(m_document, &SkeletonDocument::partXmirrorStateChanged, partListWidget, &SkeletonPartListWidget::partXmirrorStateChanged); - connect(m_document, &SkeletonDocument::partZmirrorStateChanged, partListWidget, &SkeletonPartListWidget::partZmirrorStateChanged); + connect(m_document, &SkeletonDocument::partThicknessChanged, partListWidget, &SkeletonPartListWidget::partThicknessChanged); connect(m_document, &SkeletonDocument::cleanup, partListWidget, &SkeletonPartListWidget::partListChanged); connect(m_document, &SkeletonDocument::skeletonChanged, m_document, &SkeletonDocument::generateMesh); diff --git a/src/skeletonpartlistwidget.cpp b/src/skeletonpartlistwidget.cpp index 72f7f934..16e7c0d9 100644 --- a/src/skeletonpartlistwidget.cpp +++ b/src/skeletonpartlistwidget.cpp @@ -1,8 +1,11 @@ #include #include #include +#include +#include #include "skeletonpartlistwidget.h" #include "theme.h" +#include "floatnumberwidget.h" SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid partId) : m_document(document), @@ -28,9 +31,9 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p initButton(m_xMirrorButton); updateXmirrorButton(); - m_zMirrorButton = new QPushButton(); - initButton(m_zMirrorButton); - updateZmirrorButton(); + m_thicknessButton = new QPushButton(); + initButton(m_thicknessButton); + updateThicknessButton(); m_previewLabel = new QLabel; @@ -39,15 +42,15 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p miniTopToolLayout->setContentsMargins(0, 0, 0, 0); miniTopToolLayout->addWidget(m_visibleButton); miniTopToolLayout->addWidget(m_lockButton); - miniTopToolLayout->addWidget(m_subdivButton); + miniTopToolLayout->addWidget(m_disableButton); miniTopToolLayout->addStretch(); QHBoxLayout *miniBottomToolLayout = new QHBoxLayout; miniBottomToolLayout->setSpacing(0); miniBottomToolLayout->setContentsMargins(0, 0, 0, 0); - miniBottomToolLayout->addWidget(m_disableButton); + miniBottomToolLayout->addWidget(m_subdivButton); miniBottomToolLayout->addWidget(m_xMirrorButton); - miniBottomToolLayout->addWidget(m_zMirrorButton); + miniBottomToolLayout->addWidget(m_thicknessButton); miniBottomToolLayout->addStretch(); QWidget *hrWidget = new QWidget; @@ -71,7 +74,7 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p connect(this, &SkeletonPartWidget::setPartSubdivState, m_document, &SkeletonDocument::setPartSubdivState); connect(this, &SkeletonPartWidget::setPartDisableState, m_document, &SkeletonDocument::setPartDisableState); connect(this, &SkeletonPartWidget::setPartXmirrorState, m_document, &SkeletonDocument::setPartXmirrorState); - connect(this, &SkeletonPartWidget::setPartZmirrorState, m_document, &SkeletonDocument::setPartZmirrorState); + connect(this, &SkeletonPartWidget::setPartThickness, m_document, &SkeletonDocument::setPartThickness); connect(m_lockButton, &QPushButton::clicked, [=]() { const SkeletonPart *part = m_document->findPart(m_partId); @@ -118,16 +121,42 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p emit setPartXmirrorState(m_partId, !part->xMirrored); }); - connect(m_zMirrorButton, &QPushButton::clicked, [=]() { + connect(m_thicknessButton, &QPushButton::clicked, [=]() { const SkeletonPart *part = m_document->findPart(m_partId); if (!part) { qDebug() << "Part not found:" << m_partId; return; } - emit setPartZmirrorState(m_partId, !part->zMirrored); + showThicknessSettingPopup(mapFromGlobal(QCursor::pos())); }); } +void SkeletonPartWidget::showThicknessSettingPopup(const QPoint &pos) +{ + QMenu popupMenu; + + const SkeletonPart *part = m_document->findPart(m_partId); + if (!part) { + qDebug() << "Find part failed:" << m_partId; + return; + } + + FloatNumberWidget *popup = new FloatNumberWidget; + popup->setRange(0, 2); + popup->setValue(part->thickness); + + connect(popup, &FloatNumberWidget::valueChanged, [=](float value) { + emit setPartThickness(m_partId, value); + }); + + QWidgetAction *action = new QWidgetAction(this); + action->setDefaultWidget(popup); + + popupMenu.addAction(action); + + popupMenu.exec(mapToGlobal(pos)); +} + void SkeletonPartWidget::initButton(QPushButton *button) { button->setFont(Theme::awesome()->font(Theme::miniIconFontSize)); @@ -219,17 +248,17 @@ void SkeletonPartWidget::updateXmirrorButton() updateButton(m_xMirrorButton, QChar(fa::balancescale), false); } -void SkeletonPartWidget::updateZmirrorButton() +void SkeletonPartWidget::updateThicknessButton() { const SkeletonPart *part = m_document->findPart(m_partId); if (!part) { qDebug() << "Part not found:" << m_partId; return; } - if (part->zMirrored) - updateButton(m_zMirrorButton, QChar(fa::balancescale), true); + if (part->thicknessAdjusted()) + updateButton(m_thicknessButton, QChar(fa::handlizardo), true); else - updateButton(m_zMirrorButton, QChar(fa::balancescale), false); + updateButton(m_thicknessButton, QChar(fa::handlizardo), false); } void SkeletonPartWidget::reload() @@ -240,7 +269,7 @@ void SkeletonPartWidget::reload() updateSubdivButton(); updateDisableButton(); updateXmirrorButton(); - updateZmirrorButton(); + updateThicknessButton(); } SkeletonPartListWidget::SkeletonPartListWidget(const SkeletonDocument *document) : @@ -347,7 +376,7 @@ void SkeletonPartListWidget::partXmirrorStateChanged(QUuid partId) widget->updateXmirrorButton(); } -void SkeletonPartListWidget::partZmirrorStateChanged(QUuid partId) +void SkeletonPartListWidget::partThicknessChanged(QUuid partId) { auto item = m_itemMap.find(partId); if (item == m_itemMap.end()) { @@ -355,5 +384,5 @@ void SkeletonPartListWidget::partZmirrorStateChanged(QUuid partId) return; } SkeletonPartWidget *widget = (SkeletonPartWidget *)itemWidget(item->second); - widget->updateZmirrorButton(); + widget->updateThicknessButton(); } diff --git a/src/skeletonpartlistwidget.h b/src/skeletonpartlistwidget.h index fbe3545c..0dc5dc74 100644 --- a/src/skeletonpartlistwidget.h +++ b/src/skeletonpartlistwidget.h @@ -16,6 +16,7 @@ signals: void setPartDisableState(QUuid partId, bool disabled); void setPartXmirrorState(QUuid partId, bool mirrored); void setPartZmirrorState(QUuid partId, bool mirrored); + void setPartThickness(QUuid partId, float thickness); public: SkeletonPartWidget(const SkeletonDocument *document, QUuid partId); void reload(); @@ -26,6 +27,9 @@ public: void updateDisableButton(); void updateXmirrorButton(); void updateZmirrorButton(); + void updateThicknessButton(); +public slots: + void showThicknessSettingPopup(const QPoint &pos); private: const SkeletonDocument *m_document; QUuid m_partId; @@ -36,6 +40,7 @@ private: QPushButton *m_disableButton; QPushButton *m_xMirrorButton; QPushButton *m_zMirrorButton; + QPushButton *m_thicknessButton; QLabel *m_nameLabel; private: void initButton(QPushButton *button); @@ -56,7 +61,7 @@ public slots: void partSubdivStateChanged(QUuid partId); void partDisableStateChanged(QUuid partId); void partXmirrorStateChanged(QUuid partId); - void partZmirrorStateChanged(QUuid partId); + void partThicknessChanged(QUuid partId); private: const SkeletonDocument *m_document; std::map m_itemMap; diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.dll b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.dll index 3624b1b3..8be2b867 100644 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.dll and b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.dll differ diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.dll.lib b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.dll.lib index 9fbd2f81..77d7b201 100644 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.dll.lib and b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.dll.lib differ diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.h b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.h index 3dae9505..8267039f 100644 --- a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.h +++ b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite.h @@ -33,6 +33,7 @@ int meshlite_get_halfedge_normal_array(void *context, int mesh_id, float *buffer int meshlite_build(void *context, float *vertex_position_buffer, int vertex_count, int *face_index_buffer, int face_index_buffer_len); int meshlite_bmesh_create(void *context); int meshlite_bmesh_set_cut_subdiv_count(void *context, int bmesh_id, int subdiv_count); +int meshlite_bmesh_set_thickness(void *context, int bmesh_id, float thickness); int meshlite_bmesh_enable_debug(void *context, int bmesh_id, int enable); int meshlite_bmesh_add_node(void *context, int bmesh_id, float x, float y, float z, float radius); int meshlite_bmesh_add_edge(void *context, int bmesh_id, int first_node_id, int second_node_id); diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.dll b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.dll index 170fdf3e..0681b7e1 100644 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.dll and b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.dll differ diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.dll.lib b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.dll.lib index c6a70f0a..8d28a127 100644 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.dll.lib and b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.dll.lib differ diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.h b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.h index 3dae9505..8267039f 100644 --- a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.h +++ b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite.h @@ -33,6 +33,7 @@ int meshlite_get_halfedge_normal_array(void *context, int mesh_id, float *buffer int meshlite_build(void *context, float *vertex_position_buffer, int vertex_count, int *face_index_buffer, int face_index_buffer_len); int meshlite_bmesh_create(void *context); int meshlite_bmesh_set_cut_subdiv_count(void *context, int bmesh_id, int subdiv_count); +int meshlite_bmesh_set_thickness(void *context, int bmesh_id, float thickness); int meshlite_bmesh_enable_debug(void *context, int bmesh_id, int enable); int meshlite_bmesh_add_node(void *context, int bmesh_id, float x, float y, float z, float radius); int meshlite_bmesh_add_edge(void *context, int bmesh_id, int first_node_id, int second_node_id);