Support user defined cut face per each node

Before this commit, the user defined cut face can only be set for whole part. Now each node could been configured a separate cut face and rotation.
master
Jeremy Hu 2019-07-09 08:06:07 +09:30
parent 7c66e6b5d0
commit 31d627bca9
15 changed files with 536 additions and 91 deletions

View File

@ -37,12 +37,9 @@ wd = $$replace(PWD, /, $$QMAKE_DIR_SEP)
# Update the .ts file from source # Update the .ts file from source
qtPrepareTool(LUPDATE, lupdate) qtPrepareTool(LUPDATE, lupdate)
LUPDATE += src/*.cpp src/*.h -locations none LUPDATE += src/*.cpp src/*.h -locations none
TSFILES = $$files($$PWD/languages/dust3d_??.ts) for(lang, LANGUAGES) {
for(file, TSFILES) { command = $$LUPDATE -ts languages/dust3d_$${lang}.ts
lang = $$replace(file, .*_([^/]*).ts, 1) system($$command)|error("Failed to run: $$command")
v = ts-$${lang}.commands
$$v = cd $$wd && $$LUPDATE $$SOURCES $$APP_FILES -ts $$file
QMAKE_EXTRA_TARGETS += ts-$$lang
} }
########################################################## ##########################################################

View File

@ -342,6 +342,18 @@ Tips:
<source>glTF Binary Format (.glb)</source> <source>glTF Binary Format (.glb)</source>
<translation>glb(.glb)</translation> <translation>glb(.glb)</translation>
</message> </message>
<message>
<source>Rotation</source>
<translation></translation>
</message>
<message>
<source>Cut Face...</source>
<translation>...</translation>
</message>
<message>
<source>Clear Cut Face</source>
<translation></translation>
</message>
</context> </context>
<context> <context>
<name>ExportPreviewWidget</name> <name>ExportPreviewWidget</name>
@ -1075,5 +1087,13 @@ Tips:
<source>Unselect All</source> <source>Unselect All</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Cut Face...</source>
<translation>...</translation>
</message>
<message>
<source>Clear Cut Face</source>
<translation></translation>
</message>
</context> </context>
</TS> </TS>

View File

@ -614,6 +614,19 @@ void Document::updateLinkedPart(QUuid oldPartId, QUuid newPartId)
partIt.second.setCutFaceLinkedId(newPartId); partIt.second.setCutFaceLinkedId(newPartId);
} }
} }
std::set<QUuid> dirtyPartIds;
for (auto &nodeIt: nodeMap) {
if (nodeIt.second.cutFaceLinkedId == oldPartId) {
dirtyPartIds.insert(nodeIt.second.partId);
nodeIt.second.setCutFaceLinkedId(newPartId);
}
}
for (const auto &partId: dirtyPartIds) {
SkeletonPart *part = (SkeletonPart *)findPart(partId);
if (nullptr == part)
continue;
part->dirty = true;
}
} }
const Component *Document::findComponent(QUuid componentId) const const Component *Document::findComponent(QUuid componentId) const
@ -799,6 +812,75 @@ void Document::setNodeBoneMark(QUuid nodeId, BoneMark mark)
emit skeletonChanged(); emit skeletonChanged();
} }
void Document::setNodeCutRotation(QUuid nodeId, float cutRotation)
{
auto node = nodeMap.find(nodeId);
if (node == nodeMap.end()) {
qDebug() << "Node not found:" << nodeId;
return;
}
if (qFuzzyCompare(cutRotation, node->second.cutRotation))
return;
node->second.setCutRotation(cutRotation);
auto part = partMap.find(node->second.partId);
if (part != partMap.end())
part->second.dirty = true;
emit nodeCutRotationChanged(nodeId);
emit skeletonChanged();
}
void Document::setNodeCutFace(QUuid nodeId, CutFace cutFace)
{
auto node = nodeMap.find(nodeId);
if (node == nodeMap.end()) {
qDebug() << "Node not found:" << nodeId;
return;
}
if (node->second.cutFace == cutFace)
return;
node->second.setCutFace(cutFace);
auto part = partMap.find(node->second.partId);
if (part != partMap.end())
part->second.dirty = true;
emit nodeCutFaceChanged(nodeId);
emit skeletonChanged();
}
void Document::setNodeCutFaceLinkedId(QUuid nodeId, QUuid linkedId)
{
auto node = nodeMap.find(nodeId);
if (node == nodeMap.end()) {
qDebug() << "Node not found:" << nodeId;
return;
}
if (node->second.cutFace == CutFace::UserDefined &&
node->second.cutFaceLinkedId == linkedId)
return;
node->second.setCutFaceLinkedId(linkedId);
auto part = partMap.find(node->second.partId);
if (part != partMap.end())
part->second.dirty = true;
emit nodeCutFaceChanged(nodeId);
emit skeletonChanged();
}
void Document::clearNodeCutFaceSettings(QUuid nodeId)
{
auto node = nodeMap.find(nodeId);
if (node == nodeMap.end()) {
qDebug() << "Node not found:" << nodeId;
return;
}
if (!node->second.hasCutFaceSettings)
return;
node->second.clearCutFaceSettings();
auto part = partMap.find(node->second.partId);
if (part != partMap.end())
part->second.dirty = true;
emit nodeCutFaceChanged(nodeId);
emit skeletonChanged();
}
void Document::updateTurnaround(const QImage &image) void Document::updateTurnaround(const QImage &image)
{ {
turnaround = image; turnaround = image;
@ -976,6 +1058,16 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set<QUuid> &limitNodeId
node["partId"] = nodeIt.second.partId.toString(); node["partId"] = nodeIt.second.partId.toString();
if (nodeIt.second.boneMark != BoneMark::None) if (nodeIt.second.boneMark != BoneMark::None)
node["boneMark"] = BoneMarkToString(nodeIt.second.boneMark); node["boneMark"] = BoneMarkToString(nodeIt.second.boneMark);
if (nodeIt.second.hasCutFaceSettings) {
node["cutRotation"] = QString::number(nodeIt.second.cutRotation);
if (CutFace::UserDefined == nodeIt.second.cutFace) {
if (!nodeIt.second.cutFaceLinkedId.isNull()) {
node["cutFace"] = nodeIt.second.cutFaceLinkedId.toString();
}
} else {
node["cutFace"] = CutFaceToString(nodeIt.second.cutFace);
}
}
if (!nodeIt.second.name.isEmpty()) if (!nodeIt.second.name.isEmpty())
node["name"] = nodeIt.second.name; node["name"] = nodeIt.second.name;
snapshot->nodes[node["id"]] = node; snapshot->nodes[node["id"]] = node;
@ -1248,7 +1340,7 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste)
for (const auto &it: cutFaceLinkedIdModifyMap) { for (const auto &it: cutFaceLinkedIdModifyMap) {
SkeletonPart &part = partMap[it.first]; SkeletonPart &part = partMap[it.first];
auto findNewLinkedId = oldNewIdMap.find(it.second); auto findNewLinkedId = oldNewIdMap.find(it.second);
if (oldNewIdMap.find(it.second) == oldNewIdMap.end()) { if (findNewLinkedId == oldNewIdMap.end()) {
if (partMap.find(it.second) == partMap.end()) { if (partMap.find(it.second) == partMap.end()) {
part.setCutFaceLinkedId(QUuid()); part.setCutFaceLinkedId(QUuid());
} }
@ -1273,6 +1365,26 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste)
node.z = valueOfKeyInMapOrEmpty(nodeKv.second, "z").toFloat(); node.z = valueOfKeyInMapOrEmpty(nodeKv.second, "z").toFloat();
node.partId = oldNewIdMap[QUuid(valueOfKeyInMapOrEmpty(nodeKv.second, "partId"))]; node.partId = oldNewIdMap[QUuid(valueOfKeyInMapOrEmpty(nodeKv.second, "partId"))];
node.boneMark = BoneMarkFromString(valueOfKeyInMapOrEmpty(nodeKv.second, "boneMark").toUtf8().constData()); node.boneMark = BoneMarkFromString(valueOfKeyInMapOrEmpty(nodeKv.second, "boneMark").toUtf8().constData());
const auto &cutRotationIt = nodeKv.second.find("cutRotation");
if (cutRotationIt != nodeKv.second.end())
node.setCutRotation(cutRotationIt->second.toFloat());
const auto &cutFaceIt = nodeKv.second.find("cutFace");
if (cutFaceIt != nodeKv.second.end()) {
QUuid cutFaceLinkedId = QUuid(cutFaceIt->second);
if (cutFaceLinkedId.isNull()) {
node.setCutFace(CutFaceFromString(cutFaceIt->second.toUtf8().constData()));
} else {
node.setCutFaceLinkedId(cutFaceLinkedId);
auto findNewLinkedId = oldNewIdMap.find(cutFaceLinkedId);
if (findNewLinkedId == oldNewIdMap.end()) {
if (partMap.find(cutFaceLinkedId) == partMap.end()) {
node.setCutFaceLinkedId(QUuid());
}
} else {
node.setCutFaceLinkedId(findNewLinkedId->second);
}
}
}
nodeMap[node.id] = node; nodeMap[node.id] = node;
newAddedNodeIds.insert(node.id); newAddedNodeIds.insert(node.id);
} }

View File

@ -383,6 +383,9 @@ signals:
void edgeRemoved(QUuid edgeId); void edgeRemoved(QUuid edgeId);
void nodeRadiusChanged(QUuid nodeId); void nodeRadiusChanged(QUuid nodeId);
void nodeBoneMarkChanged(QUuid nodeId); void nodeBoneMarkChanged(QUuid nodeId);
void nodeColorStateChanged(QUuid nodeId);
void nodeCutRotationChanged(QUuid nodeId);
void nodeCutFaceChanged(QUuid nodeId);
void nodeOriginChanged(QUuid nodeId); void nodeOriginChanged(QUuid nodeId);
void edgeChanged(QUuid edgeId); void edgeChanged(QUuid edgeId);
void partPreviewChanged(QUuid partId); void partPreviewChanged(QUuid partId);
@ -536,6 +539,10 @@ public slots:
void setNodeOrigin(QUuid nodeId, float x, float y, float z); void setNodeOrigin(QUuid nodeId, float x, float y, float z);
void setNodeRadius(QUuid nodeId, float radius); void setNodeRadius(QUuid nodeId, float radius);
void setNodeBoneMark(QUuid nodeId, BoneMark mark); void setNodeBoneMark(QUuid nodeId, BoneMark mark);
void setNodeCutRotation(QUuid nodeId, float cutRotation);
void setNodeCutFace(QUuid nodeId, CutFace cutFace);
void setNodeCutFaceLinkedId(QUuid nodeId, QUuid linkedId);
void clearNodeCutFaceSettings(QUuid nodeId);
void switchNodeXZ(QUuid nodeId); void switchNodeXZ(QUuid nodeId);
void moveOriginBy(float x, float y, float z); void moveOriginBy(float x, float y, float z);
void addEdge(QUuid fromNodeId, QUuid toNodeId); void addEdge(QUuid fromNodeId, QUuid toNodeId);

View File

@ -15,6 +15,7 @@
#include <map> #include <map>
#include <QDesktopServices> #include <QDesktopServices>
#include <QDockWidget> #include <QDockWidget>
#include <QWidgetAction>
#include "documentwindow.h" #include "documentwindow.h"
#include "skeletongraphicswidget.h" #include "skeletongraphicswidget.h"
#include "theme.h" #include "theme.h"
@ -36,6 +37,8 @@
#include "spinnableawesomebutton.h" #include "spinnableawesomebutton.h"
#include "fbxfile.h" #include "fbxfile.h"
#include "shortcuts.h" #include "shortcuts.h"
#include "floatnumberwidget.h"
#include "cutfacelistwidget.h"
int DocumentWindow::m_modelRenderWidgetInitialX = 16; int DocumentWindow::m_modelRenderWidgetInitialX = 16;
int DocumentWindow::m_modelRenderWidgetInitialY = 16; int DocumentWindow::m_modelRenderWidgetInitialY = 16;
@ -493,6 +496,18 @@ DocumentWindow::DocumentWindow() :
}); });
m_editMenu->addAction(m_switchXzAction); m_editMenu->addAction(m_switchXzAction);
m_setCutFaceAction = new QAction(tr("Cut Face..."), this);
connect(m_setCutFaceAction, &QAction::triggered, [=] {
m_graphicsWidget->showSelectedCutFaceSettingPopup(m_graphicsWidget->mapFromGlobal(QCursor::pos()));
});
m_editMenu->addAction(m_setCutFaceAction);
m_clearCutFaceAction = new QAction(tr("Clear Cut Face"), this);
connect(m_clearCutFaceAction, &QAction::triggered, [=] {
m_graphicsWidget->clearSelectedCutFace();
});
m_editMenu->addAction(m_clearCutFaceAction);
m_alignToMenu = new QMenu(tr("Align To")); m_alignToMenu = new QMenu(tr("Align To"));
m_alignToLocalCenterAction = new QAction(tr("Local Center"), this); m_alignToLocalCenterAction = new QAction(tr("Local Center"), this);
@ -568,6 +583,8 @@ DocumentWindow::DocumentWindow() :
m_rotateClockwiseAction->setEnabled(m_graphicsWidget->hasMultipleSelection()); m_rotateClockwiseAction->setEnabled(m_graphicsWidget->hasMultipleSelection());
m_rotateCounterclockwiseAction->setEnabled(m_graphicsWidget->hasMultipleSelection()); m_rotateCounterclockwiseAction->setEnabled(m_graphicsWidget->hasMultipleSelection());
m_switchXzAction->setEnabled(m_graphicsWidget->hasSelection()); m_switchXzAction->setEnabled(m_graphicsWidget->hasSelection());
m_setCutFaceAction->setEnabled(m_graphicsWidget->hasSelection());
m_clearCutFaceAction->setEnabled(m_graphicsWidget->hasCutFaceAdjustedNodesSelection());
m_alignToGlobalCenterAction->setEnabled(m_graphicsWidget->hasSelection() && m_document->originSettled()); m_alignToGlobalCenterAction->setEnabled(m_graphicsWidget->hasSelection() && m_document->originSettled());
m_alignToGlobalVerticalCenterAction->setEnabled(m_graphicsWidget->hasSelection() && m_document->originSettled()); m_alignToGlobalVerticalCenterAction->setEnabled(m_graphicsWidget->hasSelection() && m_document->originSettled());
m_alignToGlobalHorizontalCenterAction->setEnabled(m_graphicsWidget->hasSelection() && m_document->originSettled()); m_alignToGlobalHorizontalCenterAction->setEnabled(m_graphicsWidget->hasSelection() && m_document->originSettled());
@ -768,6 +785,7 @@ DocumentWindow::DocumentWindow() :
connect(graphicsWidget, &SkeletonGraphicsWidget::moveNodeBy, m_document, &Document::moveNodeBy); connect(graphicsWidget, &SkeletonGraphicsWidget::moveNodeBy, m_document, &Document::moveNodeBy);
connect(graphicsWidget, &SkeletonGraphicsWidget::setNodeOrigin, m_document, &Document::setNodeOrigin); connect(graphicsWidget, &SkeletonGraphicsWidget::setNodeOrigin, m_document, &Document::setNodeOrigin);
connect(graphicsWidget, &SkeletonGraphicsWidget::setNodeBoneMark, m_document, &Document::setNodeBoneMark); connect(graphicsWidget, &SkeletonGraphicsWidget::setNodeBoneMark, m_document, &Document::setNodeBoneMark);
connect(graphicsWidget, &SkeletonGraphicsWidget::clearNodeCutFaceSettings, m_document, &Document::clearNodeCutFaceSettings);
connect(graphicsWidget, &SkeletonGraphicsWidget::removeNode, m_document, &Document::removeNode); connect(graphicsWidget, &SkeletonGraphicsWidget::removeNode, m_document, &Document::removeNode);
connect(graphicsWidget, &SkeletonGraphicsWidget::setEditMode, m_document, &Document::setEditMode); connect(graphicsWidget, &SkeletonGraphicsWidget::setEditMode, m_document, &Document::setEditMode);
connect(graphicsWidget, &SkeletonGraphicsWidget::removeEdge, m_document, &Document::removeEdge); connect(graphicsWidget, &SkeletonGraphicsWidget::removeEdge, m_document, &Document::removeEdge);
@ -802,6 +820,7 @@ DocumentWindow::DocumentWindow() :
connect(graphicsWidget, &SkeletonGraphicsWidget::changeTurnaround, this, &DocumentWindow::changeTurnaround); connect(graphicsWidget, &SkeletonGraphicsWidget::changeTurnaround, this, &DocumentWindow::changeTurnaround);
connect(graphicsWidget, &SkeletonGraphicsWidget::save, this, &DocumentWindow::save); connect(graphicsWidget, &SkeletonGraphicsWidget::save, this, &DocumentWindow::save);
connect(graphicsWidget, &SkeletonGraphicsWidget::open, this, &DocumentWindow::open); connect(graphicsWidget, &SkeletonGraphicsWidget::open, this, &DocumentWindow::open);
connect(graphicsWidget, &SkeletonGraphicsWidget::showCutFaceSettingPopup, this, &DocumentWindow::showCutFaceSettingPopup);
connect(m_document, &Document::nodeAdded, graphicsWidget, &SkeletonGraphicsWidget::nodeAdded); connect(m_document, &Document::nodeAdded, graphicsWidget, &SkeletonGraphicsWidget::nodeAdded);
connect(m_document, &Document::nodeRemoved, graphicsWidget, &SkeletonGraphicsWidget::nodeRemoved); connect(m_document, &Document::nodeRemoved, graphicsWidget, &SkeletonGraphicsWidget::nodeRemoved);
@ -1494,3 +1513,133 @@ void DocumentWindow::unregisterDialog(QWidget *widget)
{ {
m_dialogs.erase(std::remove(m_dialogs.begin(), m_dialogs.end(), widget), m_dialogs.end()); m_dialogs.erase(std::remove(m_dialogs.begin(), m_dialogs.end(), widget), m_dialogs.end());
} }
void DocumentWindow::showCutFaceSettingPopup(const QPoint &globalPos, std::set<QUuid> nodeIds)
{
QMenu popupMenu;
const SkeletonNode *node = nullptr;
if (1 == nodeIds.size()) {
node = m_document->findNode(*nodeIds.begin());
}
QWidget *popup = new QWidget;
FloatNumberWidget *rotationWidget = new FloatNumberWidget;
rotationWidget->setItemName(tr("Rotation"));
rotationWidget->setRange(-1, 1);
rotationWidget->setValue(0);
if (nullptr != node) {
rotationWidget->setValue(node->cutRotation);
}
connect(rotationWidget, &FloatNumberWidget::valueChanged, [=](float value) {
m_document->batchChangeBegin();
for (const auto &id: nodeIds) {
m_document->setNodeCutRotation(id, value);
}
m_document->batchChangeEnd();
m_document->saveSnapshot();
});
QPushButton *rotationEraser = new QPushButton(QChar(fa::eraser));
Theme::initAwesomeToolButton(rotationEraser);
connect(rotationEraser, &QPushButton::clicked, [=]() {
rotationWidget->setValue(0.0);
m_document->saveSnapshot();
});
QHBoxLayout *rotationLayout = new QHBoxLayout;
rotationLayout->addWidget(rotationEraser);
rotationLayout->addWidget(rotationWidget);
QHBoxLayout *standardFacesLayout = new QHBoxLayout;
QPushButton *buttons[(int)CutFace::Count] = {0};
CutFaceListWidget *cutFaceListWidget = new CutFaceListWidget(m_document);
size_t cutFaceTypeCount = (size_t)CutFace::Count;
if (cutFaceListWidget->isEmpty())
cutFaceTypeCount = (size_t)CutFace::UserDefined;
auto updateCutFaceButtonState = [&](size_t index) {
for (size_t i = 0; i < (size_t)cutFaceTypeCount; ++i) {
auto button = buttons[i];
if (i == index) {
button->setFlat(true);
button->setEnabled(false);
} else {
button->setFlat(false);
button->setEnabled(true);
}
}
if (index != (int)CutFace::UserDefined)
cutFaceListWidget->selectCutFace(QUuid());
};
cutFaceListWidget->enableMultipleSelection(false);
if (nullptr != node) {
cutFaceListWidget->selectCutFace(node->cutFaceLinkedId);
}
connect(cutFaceListWidget, &CutFaceListWidget::currentSelectedCutFaceChanged, this, [=](QUuid partId) {
if (partId.isNull()) {
CutFace cutFace = CutFace::Quad;
updateCutFaceButtonState((int)cutFace);
m_document->batchChangeBegin();
for (const auto &id: nodeIds) {
m_document->setNodeCutFace(id, cutFace);
}
m_document->batchChangeEnd();
m_document->saveSnapshot();
} else {
updateCutFaceButtonState((int)CutFace::UserDefined);
m_document->batchChangeBegin();
for (const auto &id: nodeIds) {
m_document->setNodeCutFaceLinkedId(id, partId);
}
m_document->batchChangeEnd();
m_document->saveSnapshot();
}
});
if (cutFaceListWidget->isEmpty())
cutFaceListWidget->hide();
for (size_t i = 0; i < (size_t)cutFaceTypeCount; ++i) {
CutFace cutFace = (CutFace)i;
QString iconFilename = ":/resources/" + CutFaceToString(cutFace).toLower() + ".png";
QPixmap pixmap(iconFilename);
QIcon buttonIcon(pixmap);
QPushButton *button = new QPushButton;
button->setIconSize(QSize(Theme::toolIconSize / 2, Theme::toolIconSize / 2));
button->setIcon(buttonIcon);
connect(button, &QPushButton::clicked, [=]() {
updateCutFaceButtonState(i);
m_document->batchChangeBegin();
for (const auto &id: nodeIds) {
m_document->setNodeCutFace(id, cutFace);
}
m_document->batchChangeEnd();
m_document->saveSnapshot();
});
standardFacesLayout->addWidget(button);
buttons[i] = button;
}
if (nullptr != node) {
updateCutFaceButtonState((size_t)node->cutFace);
}
QVBoxLayout *popupLayout = new QVBoxLayout;
popupLayout->addLayout(rotationLayout);
popupLayout->addSpacing(10);
popupLayout->addLayout(standardFacesLayout);
popupLayout->addWidget(cutFaceListWidget);
popup->setLayout(popupLayout);
QWidgetAction action(this);
action.setDefaultWidget(popup);
popupMenu.addAction(&action);
popupMenu.exec(globalPos);
}

View File

@ -67,6 +67,7 @@ public slots:
void registerDialog(QWidget *widget); void registerDialog(QWidget *widget);
void unregisterDialog(QWidget *widget); void unregisterDialog(QWidget *widget);
void showPreferences(); void showPreferences();
void showCutFaceSettingPopup(const QPoint &globalPos, std::set<QUuid> nodeIds);
private: private:
void initLockButton(QPushButton *button); void initLockButton(QPushButton *button);
void setCurrentFilename(const QString &filename); void setCurrentFilename(const QString &filename);
@ -119,6 +120,8 @@ private:
QAction *m_rotateClockwiseAction; QAction *m_rotateClockwiseAction;
QAction *m_rotateCounterclockwiseAction; QAction *m_rotateCounterclockwiseAction;
QAction *m_switchXzAction; QAction *m_switchXzAction;
QAction *m_setCutFaceAction;
QAction *m_clearCutFaceAction;
QMenu *m_alignToMenu; QMenu *m_alignToMenu;
QAction *m_alignToGlobalCenterAction; QAction *m_alignToGlobalCenterAction;

View File

@ -111,9 +111,24 @@ bool MeshGenerator::checkIsPartDependencyDirty(const QString &partIdString)
} }
QString cutFaceString = valueOfKeyInMapOrEmpty(findPart->second, "cutFace"); QString cutFaceString = valueOfKeyInMapOrEmpty(findPart->second, "cutFace");
QUuid cutFaceLinkedPartId = QUuid(cutFaceString); QUuid cutFaceLinkedPartId = QUuid(cutFaceString);
if (cutFaceLinkedPartId.isNull()) if (!cutFaceLinkedPartId.isNull()) {
if (checkIsPartDirty(cutFaceString))
return true;
}
for (const auto &nodeIdString: m_partNodeIds[partIdString]) {
auto findNode = m_snapshot->nodes.find(nodeIdString);
if (findNode == m_snapshot->nodes.end()) {
qDebug() << "Find node failed:" << nodeIdString;
continue;
}
QString cutFaceString = valueOfKeyInMapOrEmpty(findNode->second, "cutFace");
QUuid cutFaceLinkedPartId = QUuid(cutFaceString);
if (!cutFaceLinkedPartId.isNull()) {
if (checkIsPartDirty(cutFaceString))
return true;
}
}
return false; return false;
return checkIsPartDirty(cutFaceString);
} }
bool MeshGenerator::checkIsComponentDirty(const QString &componentIdString) bool MeshGenerator::checkIsComponentDirty(const QString &componentIdString)
@ -167,32 +182,9 @@ void MeshGenerator::checkDirtyFlags()
checkIsComponentDirty(QUuid().toString()); checkIsComponentDirty(QUuid().toString());
} }
nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString) void MeshGenerator::cutFaceStringToCutTemplate(const QString &cutFaceString, std::vector<QVector2D> &cutTemplate)
{ {
auto findPart = m_snapshot->parts.find(partIdString); //std::map<QString, QVector2D> cutTemplateMapByName;
if (findPart == m_snapshot->parts.end()) {
qDebug() << "Find part failed:" << partIdString;
return nullptr;
}
QUuid partId = QUuid(partIdString);
auto &part = findPart->second;
bool isDisabled = isTrueValueString(valueOfKeyInMapOrEmpty(part, "disabled"));
bool xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(part, "xMirrored"));
bool subdived = isTrueValueString(valueOfKeyInMapOrEmpty(part, "subdived"));
bool rounded = isTrueValueString(valueOfKeyInMapOrEmpty(part, "rounded"));
bool chamfered = isTrueValueString(valueOfKeyInMapOrEmpty(part, "chamfered"));
QString colorString = valueOfKeyInMapOrEmpty(part, "color");
QColor partColor = colorString.isEmpty() ? m_defaultPartColor : QColor(colorString);
float deformThickness = 1.0;
float deformWidth = 1.0;
float cutRotation = 0.0;
auto target = PartTargetFromString(valueOfKeyInMapOrEmpty(part, "target").toUtf8().constData());
auto base = PartBaseFromString(valueOfKeyInMapOrEmpty(part, "base").toUtf8().constData());
std::map<QString, QVector2D> cutTemplateMapByName;
std::vector<QVector2D> cutTemplate;
QString cutFaceString = valueOfKeyInMapOrEmpty(part, "cutFace");
QUuid cutFaceLinkedPartId = QUuid(cutFaceString); QUuid cutFaceLinkedPartId = QUuid(cutFaceString);
if (!cutFaceLinkedPartId.isNull()) { if (!cutFaceLinkedPartId.isNull()) {
std::map<QString, std::tuple<float, float, float>> cutFaceNodeMap; std::map<QString, std::tuple<float, float, float>> cutFaceNodeMap;
@ -306,19 +298,47 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
// Fetch points from linked nodes // Fetch points from linked nodes
std::vector<QString> cutTemplateNames; std::vector<QString> cutTemplateNames;
cutFacePointsFromNodes(cutTemplate, cutFaceNodes, isRing, &cutTemplateNames); cutFacePointsFromNodes(cutTemplate, cutFaceNodes, isRing, &cutTemplateNames);
for (size_t i = 0; i < cutTemplateNames.size(); ++i) { //for (size_t i = 0; i < cutTemplateNames.size(); ++i) {
cutTemplateMapByName.insert({cutTemplateNames[i], cutTemplate[i]}); // cutTemplateMapByName.insert({cutTemplateNames[i], cutTemplate[i]});
} //}
} }
} }
if (cutTemplate.size() < 3) { if (cutTemplate.size() < 3) {
CutFace cutFace = CutFaceFromString(cutFaceString.toUtf8().constData()); CutFace cutFace = CutFaceFromString(cutFaceString.toUtf8().constData());
cutTemplate = CutFaceToPoints(cutFace); cutTemplate = CutFaceToPoints(cutFace);
cutTemplateMapByName.clear(); //cutTemplateMapByName.clear();
for (size_t i = 0; i < cutTemplate.size(); ++i) { //for (size_t i = 0; i < cutTemplate.size(); ++i) {
cutTemplateMapByName.insert({cutFaceString + "/" + QString::number(i + 1), cutTemplate[i]}); // cutTemplateMapByName.insert({cutFaceString + "/" + QString::number(i + 1), cutTemplate[i]});
//}
} }
} }
nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString)
{
auto findPart = m_snapshot->parts.find(partIdString);
if (findPart == m_snapshot->parts.end()) {
qDebug() << "Find part failed:" << partIdString;
return nullptr;
}
QUuid partId = QUuid(partIdString);
auto &part = findPart->second;
bool isDisabled = isTrueValueString(valueOfKeyInMapOrEmpty(part, "disabled"));
bool xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(part, "xMirrored"));
bool subdived = isTrueValueString(valueOfKeyInMapOrEmpty(part, "subdived"));
bool rounded = isTrueValueString(valueOfKeyInMapOrEmpty(part, "rounded"));
bool chamfered = isTrueValueString(valueOfKeyInMapOrEmpty(part, "chamfered"));
QString colorString = valueOfKeyInMapOrEmpty(part, "color");
QColor partColor = colorString.isEmpty() ? m_defaultPartColor : QColor(colorString);
float deformThickness = 1.0;
float deformWidth = 1.0;
float cutRotation = 0.0;
auto target = PartTargetFromString(valueOfKeyInMapOrEmpty(part, "target").toUtf8().constData());
auto base = PartBaseFromString(valueOfKeyInMapOrEmpty(part, "base").toUtf8().constData());
QString cutFaceString = valueOfKeyInMapOrEmpty(part, "cutFace");
std::vector<QVector2D> cutTemplate;
cutFaceStringToCutTemplate(cutFaceString, cutTemplate);
if (chamfered) if (chamfered)
nodemesh::chamferFace2D(&cutTemplate); nodemesh::chamferFace2D(&cutTemplate);
@ -362,6 +382,9 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
float radius = 0; float radius = 0;
QVector3D position; QVector3D position;
BoneMark boneMark = BoneMark::None; BoneMark boneMark = BoneMark::None;
bool hasCutFaceSettings = false;
float cutRotation = 0.0;
QString cutFace;
}; };
std::map<QString, NodeInfo> nodeInfos; std::map<QString, NodeInfo> nodeInfos;
for (const auto &nodeIdString: m_partNodeIds[partIdString]) { for (const auto &nodeIdString: m_partNodeIds[partIdString]) {
@ -379,10 +402,27 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
BoneMark boneMark = BoneMarkFromString(valueOfKeyInMapOrEmpty(node, "boneMark").toUtf8().constData()); BoneMark boneMark = BoneMarkFromString(valueOfKeyInMapOrEmpty(node, "boneMark").toUtf8().constData());
bool hasCutFaceSettings = false;
float cutRotation = 0.0;
QString cutFace;
const auto &cutFaceIt = node.find("cutFace");
if (cutFaceIt != node.end()) {
cutFace = cutFaceIt->second;
hasCutFaceSettings = true;
const auto &cutRotationIt = node.find("cutRotation");
if (cutRotationIt != node.end()) {
cutRotation = cutRotationIt->second.toFloat();
}
}
auto &nodeInfo = nodeInfos[nodeIdString]; auto &nodeInfo = nodeInfos[nodeIdString];
nodeInfo.position = QVector3D(x, y, z); nodeInfo.position = QVector3D(x, y, z);
nodeInfo.radius = radius; nodeInfo.radius = radius;
nodeInfo.boneMark = boneMark; nodeInfo.boneMark = boneMark;
nodeInfo.hasCutFaceSettings = hasCutFaceSettings;
nodeInfo.cutRotation = cutRotation;
nodeInfo.cutFace = cutFace;
} }
std::set<std::pair<QString, QString>> edges; std::set<std::pair<QString, QString>> edges;
@ -428,7 +468,16 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
for (const auto &nodeIt: nodeInfos) { for (const auto &nodeIt: nodeInfos) {
const auto &nodeIdString = nodeIt.first; const auto &nodeIdString = nodeIt.first;
const auto &nodeInfo = nodeIt.second; const auto &nodeInfo = nodeIt.second;
size_t nodeIndex = modifier->addNode(nodeInfo.position, nodeInfo.radius, cutTemplate); size_t nodeIndex = 0;
if (nodeInfo.hasCutFaceSettings) {
std::vector<QVector2D> nodeCutTemplate;
cutFaceStringToCutTemplate(nodeInfo.cutFace, nodeCutTemplate);
if (chamfered)
nodemesh::chamferFace2D(&nodeCutTemplate);
nodeIndex = modifier->addNode(nodeInfo.position, nodeInfo.radius, nodeCutTemplate, nodeInfo.cutRotation);
} else {
nodeIndex = modifier->addNode(nodeInfo.position, nodeInfo.radius, cutTemplate, cutRotation);
}
nodeIdStringToIndexMap[nodeIdString] = nodeIndex; nodeIdStringToIndexMap[nodeIdString] = nodeIndex;
nodeIndexToIdStringMap[nodeIndex] = nodeIdString; nodeIndexToIdStringMap[nodeIndex] = nodeIdString;
@ -482,7 +531,6 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
nodemesh::Builder *builder = new nodemesh::Builder; nodemesh::Builder *builder = new nodemesh::Builder;
builder->setDeformThickness(deformThickness); builder->setDeformThickness(deformThickness);
builder->setDeformWidth(deformWidth); builder->setDeformWidth(deformWidth);
builder->setCutRotation(cutRotation);
if (PartBase::YZ == base) { if (PartBase::YZ == base) {
builder->enableBaseNormalOnX(false); builder->enableBaseNormalOnX(false);
} else if (PartBase::Average == base) { } else if (PartBase::Average == base) {
@ -495,7 +543,7 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
std::vector<size_t> builderNodeIndices; std::vector<size_t> builderNodeIndices;
for (const auto &node: modifier->nodes()) { for (const auto &node: modifier->nodes()) {
auto nodeIndex = builder->addNode(node.position, node.radius, node.cutTemplate); auto nodeIndex = builder->addNode(node.position, node.radius, node.cutTemplate, node.cutRotation);
builder->setNodeOriginInfo(nodeIndex, node.nearOriginNodeIndex, node.farOriginNodeIndex); builder->setNodeOriginInfo(nodeIndex, node.nearOriginNodeIndex, node.farOriginNodeIndex);
builderNodeIndices.push_back(nodeIndex); builderNodeIndices.push_back(nodeIndex);
} }
@ -503,19 +551,19 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
builder->addEdge(edge.firstNodeIndex, edge.secondNodeIndex); builder->addEdge(edge.firstNodeIndex, edge.secondNodeIndex);
bool buildSucceed = builder->build(); bool buildSucceed = builder->build();
for (size_t i = 0; i < modifier->nodes().size(); ++i) { //for (size_t i = 0; i < modifier->nodes().size(); ++i) {
const auto &node = modifier->nodes()[i]; // const auto &node = modifier->nodes()[i];
if (!node.isOriginal) // if (!node.isOriginal)
continue; // continue;
const QString &nodeIdString = nodeIndexToIdStringMap[node.originNodeIndex]; // const QString &nodeIdString = nodeIndexToIdStringMap[node.originNodeIndex];
const nodemesh::Builder::CutFaceTransform *cutFaceTransform = builder->nodeAdjustableCutFaceTransform(builderNodeIndices[i]); // const nodemesh::Builder::CutFaceTransform *cutFaceTransform = builder->nodeAdjustableCutFaceTransform(builderNodeIndices[i]);
if (nullptr != cutFaceTransform && // if (nullptr != cutFaceTransform &&
PartTarget::Model == target) { // PartTarget::Model == target) {
QUuid nodeId = QUuid(nodeIdString); // QUuid nodeId = QUuid(nodeIdString);
m_cutFaceTransforms->insert({nodeId, *cutFaceTransform}); // m_cutFaceTransforms->insert({nodeId, *cutFaceTransform});
m_nodesCutFaces->insert({nodeId, cutTemplateMapByName}); // m_nodesCutFaces->insert({nodeId, cutTemplateMapByName});
} // }
} //}
partCache.vertices = builder->generatedVertices(); partCache.vertices = builder->generatedVertices();
partCache.faces = builder->generatedFaces(); partCache.faces = builder->generatedFaces();
@ -531,7 +579,7 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
nodemesh::Combiner::Mesh *mesh = nullptr; nodemesh::Combiner::Mesh *mesh = nullptr;
if (buildSucceed) { if (buildSucceed) {
mesh = new nodemesh::Combiner::Mesh(partCache.vertices, partCache.faces); mesh = new nodemesh::Combiner::Mesh(partCache.vertices, partCache.faces, false);
if (!mesh->isNull()) { if (!mesh->isNull()) {
if (xMirrored) { if (xMirrored) {
std::vector<QVector3D> xMirroredVertices; std::vector<QVector3D> xMirroredVertices;
@ -1008,8 +1056,8 @@ void MeshGenerator::generate()
countTimeConsumed.start(); countTimeConsumed.start();
m_outcome = new Outcome; m_outcome = new Outcome;
m_cutFaceTransforms = new std::map<QUuid, nodemesh::Builder::CutFaceTransform>; //m_cutFaceTransforms = new std::map<QUuid, nodemesh::Builder::CutFaceTransform>;
m_nodesCutFaces = new std::map<QUuid, std::map<QString, QVector2D>>; //m_nodesCutFaces = new std::map<QUuid, std::map<QString, QVector2D>>;
bool needDeleteCacheContext = false; bool needDeleteCacheContext = false;
if (nullptr == m_cacheContext) { if (nullptr == m_cacheContext) {

View File

@ -116,6 +116,7 @@ private:
nodemesh::Combiner::Mesh *combineMultipleMeshes(const std::vector<std::pair<nodemesh::Combiner::Mesh *, CombineMode>> &multipleMeshes, bool recombine=true); nodemesh::Combiner::Mesh *combineMultipleMeshes(const std::vector<std::pair<nodemesh::Combiner::Mesh *, CombineMode>> &multipleMeshes, bool recombine=true);
QString componentColorName(const std::map<QString, QString> *component); QString componentColorName(const std::map<QString, QString> *component);
void collectUncombinedComponent(const QString &componentIdString); void collectUncombinedComponent(const QString &componentIdString);
void cutFaceStringToCutTemplate(const QString &cutFaceString, std::vector<QVector2D> &cutTemplate);
}; };
#endif #endif

View File

@ -22,7 +22,10 @@ public:
y(0), y(0),
z(0), z(0),
radius(0), radius(0),
boneMark(BoneMark::None) boneMark(BoneMark::None),
cutRotation(0.0),
cutFace(CutFace::Quad),
hasCutFaceSettings(false)
{ {
id = withId.isNull() ? QUuid::createUuid() : withId; id = withId.isNull() ? QUuid::createUuid() : withId;
} }
@ -34,6 +37,38 @@ public:
toRadius = 1; toRadius = 1;
radius = toRadius; radius = toRadius;
} }
void setCutRotation(float toRotation)
{
if (toRotation < -1)
toRotation = -1;
else if (toRotation > 1)
toRotation = 1;
cutRotation = toRotation;
hasCutFaceSettings = true;
}
void setCutFace(CutFace face)
{
cutFace = face;
cutFaceLinkedId = QUuid();
hasCutFaceSettings = true;
}
void setCutFaceLinkedId(const QUuid &linkedId)
{
if (linkedId.isNull()) {
clearCutFaceSettings();
return;
}
cutFace = CutFace::UserDefined;
cutFaceLinkedId = linkedId;
hasCutFaceSettings = true;
}
void clearCutFaceSettings()
{
cutFace = CutFace::Quad;
cutFaceLinkedId = QUuid();
cutRotation = 0;
hasCutFaceSettings = false;
}
QUuid id; QUuid id;
QUuid partId; QUuid partId;
QString name; QString name;
@ -42,6 +77,10 @@ public:
float z; float z;
float radius; float radius;
BoneMark boneMark; BoneMark boneMark;
float cutRotation;
CutFace cutFace;
QUuid cutFaceLinkedId;
bool hasCutFaceSettings;
std::vector<QUuid> edgeIds; std::vector<QUuid> edgeIds;
}; };

View File

@ -219,6 +219,20 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos)
contextMenu.addAction(&switchChainSideAction); contextMenu.addAction(&switchChainSideAction);
} }
QAction setCutFaceAction(tr("Cut Face..."), this);
if (!m_nodePositionModifyOnly && hasSelection()) {
connect(&setCutFaceAction, &QAction::triggered, this, [&]() {
showSelectedCutFaceSettingPopup(mapFromGlobal(QCursor::pos()));
});
contextMenu.addAction(&setCutFaceAction);
}
QAction clearCutFaceAction(tr("Clear Cut Face"), this);
if (!m_nodePositionModifyOnly && hasCutFaceAdjustedNodesSelection()) {
connect(&clearCutFaceAction, &QAction::triggered, this, &SkeletonGraphicsWidget::clearSelectedCutFace);
contextMenu.addAction(&clearCutFaceAction);
}
QAction alignToLocalCenterAction(tr("Local Center"), this); QAction alignToLocalCenterAction(tr("Local Center"), this);
QAction alignToLocalVerticalCenterAction(tr("Local Vertical Center"), this); QAction alignToLocalVerticalCenterAction(tr("Local Vertical Center"), this);
QAction alignToLocalHorizontalCenterAction(tr("Local Horizontal Center"), this); QAction alignToLocalHorizontalCenterAction(tr("Local Horizontal Center"), this);
@ -353,6 +367,23 @@ bool SkeletonGraphicsWidget::hasTwoDisconnectedNodesSelection()
return true; return true;
} }
bool SkeletonGraphicsWidget::hasCutFaceAdjustedNodesSelection()
{
for (const auto &it: m_rangeSelectionSet) {
if (it->data(0) == "node") {
const auto &nodeId = ((SkeletonGraphicsNodeItem *)it)->id();
const SkeletonNode *node = m_document->findNode(nodeId);
if (nullptr == node) {
qDebug() << "Find node failed:" << nodeId;
continue;
}
if (node->hasCutFaceSettings)
return true;
}
}
return false;
}
void SkeletonGraphicsWidget::breakSelected() void SkeletonGraphicsWidget::breakSelected()
{ {
std::set<QUuid> edgeIds; std::set<QUuid> edgeIds;
@ -2613,6 +2644,40 @@ void SkeletonGraphicsWidget::setSelectedNodesBoneMark(BoneMark boneMark)
} }
} }
void SkeletonGraphicsWidget::showSelectedCutFaceSettingPopup(const QPoint &pos)
{
std::set<QUuid> nodeIdSet;
std::set<QUuid> edgeIdSet;
readSkeletonNodeAndEdgeIdSetFromRangeSelection(&nodeIdSet, &edgeIdSet);
emit showCutFaceSettingPopup(mapToGlobal(pos), nodeIdSet);
}
void SkeletonGraphicsWidget::clearSelectedCutFace()
{
std::set<QUuid> nodeIdSet;
for (const auto &it: m_rangeSelectionSet) {
if (it->data(0) == "node") {
const auto &nodeId = ((SkeletonGraphicsNodeItem *)it)->id();
const SkeletonNode *node = m_document->findNode(nodeId);
if (nullptr == node) {
qDebug() << "Find node failed:" << nodeId;
continue;
}
if (node->hasCutFaceSettings) {
nodeIdSet.insert(nodeId);
}
}
}
if (nodeIdSet.empty())
return;
emit batchChangeBegin();
for (const auto &id: nodeIdSet) {
emit clearNodeCutFaceSettings(id);
}
emit batchChangeEnd();
emit groupOperationAdded();
}
void SkeletonGraphicsWidget::setNodePositionModifyOnly(bool nodePositionModifyOnly) void SkeletonGraphicsWidget::setNodePositionModifyOnly(bool nodePositionModifyOnly)
{ {
m_nodePositionModifyOnly = nodePositionModifyOnly; m_nodePositionModifyOnly = nodePositionModifyOnly;

View File

@ -406,6 +406,8 @@ signals:
void setZlockState(bool locked); void setZlockState(bool locked);
void setNodeOrigin(QUuid nodeId, float x, float y, float z); void setNodeOrigin(QUuid nodeId, float x, float y, float z);
void setNodeBoneMark(QUuid nodeId, BoneMark mark); void setNodeBoneMark(QUuid nodeId, BoneMark mark);
void clearNodeCutFaceSettings(QUuid nodeId);
void showCutFaceSettingPopup(const QPoint &globalPos, std::set<QUuid> nodeIds);
void zoomRenderedModelBy(float delta); void zoomRenderedModelBy(float delta);
void switchNodeXZ(QUuid nodeId); void switchNodeXZ(QUuid nodeId);
void switchChainSide(std::set<QUuid> nodeIds); void switchChainSide(std::set<QUuid> nodeIds);
@ -437,6 +439,7 @@ public:
bool hasEdgeSelection(); bool hasEdgeSelection();
bool hasNodeSelection(); bool hasNodeSelection();
bool hasTwoDisconnectedNodesSelection(); bool hasTwoDisconnectedNodesSelection();
bool hasCutFaceAdjustedNodesSelection();
void setModelWidget(ModelWidget *modelWidget); void setModelWidget(ModelWidget *modelWidget);
void setNodePositionModifyOnly(bool nodePositionModifyOnly); void setNodePositionModifyOnly(bool nodePositionModifyOnly);
void setMainProfileOnly(bool mainProfileOnly); void setMainProfileOnly(bool mainProfileOnly);
@ -505,6 +508,8 @@ public slots:
void timeToRemoveDeferredNodesAndEdges(); void timeToRemoveDeferredNodesAndEdges();
void switchSelectedXZ(); void switchSelectedXZ();
void switchSelectedChainSide(); void switchSelectedChainSide();
void showSelectedCutFaceSettingPopup(const QPoint &pos);
void clearSelectedCutFace();
void shortcutDelete(); void shortcutDelete();
void shortcutAddMode(); void shortcutAddMode();
void shortcutUndo(); void shortcutUndo();

View File

@ -16,13 +16,14 @@
namespace nodemesh namespace nodemesh
{ {
size_t Builder::addNode(const QVector3D &position, float radius, const std::vector<QVector2D> &cutTemplate) size_t Builder::addNode(const QVector3D &position, float radius, const std::vector<QVector2D> &cutTemplate, float cutRotation)
{ {
size_t nodeIndex = m_nodes.size(); size_t nodeIndex = m_nodes.size();
Node node; Node node;
node.position = position; node.position = position;
node.radius = radius; node.radius = radius;
node.cutTemplate = cutTemplate; node.cutTemplate = cutTemplate;
node.cutRotation = cutRotation;
m_nodes.push_back(node); m_nodes.push_back(node);
m_sortedNodeIndices.push_back(nodeIndex); m_sortedNodeIndices.push_back(nodeIndex);
//qDebug() << "addNode" << position << radius; //qDebug() << "addNode" << position << radius;
@ -452,7 +453,7 @@ bool Builder::generateCutsForNode(size_t nodeIndex)
if (1 == neighborsCount) { if (1 == neighborsCount) {
QVector3D cutNormal = node.cutNormal; QVector3D cutNormal = node.cutNormal;
std::vector<QVector3D> cut; std::vector<QVector3D> cut;
makeCut(node.position, node.radius, node.cutTemplate, node.baseNormal, cutNormal, node.traverseDirection, cut, &node.cutFaceTransform); makeCut(node.position, node.radius, node.cutTemplate, node.cutRotation, node.baseNormal, cutNormal, node.traverseDirection, cut, &node.cutFaceTransform);
node.hasAdjustableCutFace = true; node.hasAdjustableCutFace = true;
std::vector<size_t> vertices; std::vector<size_t> vertices;
insertCutVertices(cut, vertices, nodeIndex, cutNormal); insertCutVertices(cut, vertices, nodeIndex, cutNormal);
@ -477,7 +478,7 @@ bool Builder::generateCutsForNode(size_t nodeIndex)
} }
} }
std::vector<QVector3D> cut; std::vector<QVector3D> cut;
makeCut(node.position, node.radius, node.cutTemplate, node.baseNormal, cutNormal, node.traverseDirection, cut, &node.cutFaceTransform); makeCut(node.position, node.radius, node.cutTemplate, node.cutRotation, node.baseNormal, cutNormal, node.traverseDirection, cut, &node.cutFaceTransform);
node.hasAdjustableCutFace = true; node.hasAdjustableCutFace = true;
std::vector<size_t> vertices; std::vector<size_t> vertices;
insertCutVertices(cut, vertices, nodeIndex, cutNormal); insertCutVertices(cut, vertices, nodeIndex, cutNormal);
@ -549,7 +550,7 @@ bool Builder::tryWrapMultipleBranchesForNode(size_t nodeIndex, std::vector<float
continue; continue;
} }
} }
makeCut(node.position + cutNormal * finalDistance, radius, node.cutTemplate, node.baseNormal, cutNormal, neighbor.traverseDirection, cut); makeCut(node.position + cutNormal * finalDistance, radius, node.cutTemplate, node.cutRotation, node.baseNormal, cutNormal, neighbor.traverseDirection, cut);
std::vector<size_t> vertices; std::vector<size_t> vertices;
insertCutVertices(cut, vertices, nodeIndex, cutNormal); insertCutVertices(cut, vertices, nodeIndex, cutNormal);
cutsForEdges.push_back({vertices, -cutNormal}); cutsForEdges.push_back({vertices, -cutNormal});
@ -715,6 +716,7 @@ QVector3D Builder::revisedBaseNormalAcordingToCutNormal(const QVector3D &baseNor
void Builder::makeCut(const QVector3D &position, void Builder::makeCut(const QVector3D &position,
float radius, float radius,
const std::vector<QVector2D> &cutTemplate, const std::vector<QVector2D> &cutTemplate,
float cutRotation,
QVector3D &baseNormal, QVector3D &baseNormal,
QVector3D &cutNormal, QVector3D &cutNormal,
const QVector3D &traverseDirection, const QVector3D &traverseDirection,
@ -723,8 +725,8 @@ void Builder::makeCut(const QVector3D &position,
{ {
auto finalCutTemplate = cutTemplate; auto finalCutTemplate = cutTemplate;
float degree = 0; float degree = 0;
if (!qFuzzyIsNull(m_cutRotation)) { if (!qFuzzyIsNull(cutRotation)) {
degree = m_cutRotation * 180; degree = cutRotation * 180;
} }
if (QVector3D::dotProduct(cutNormal, traverseDirection) <= 0) { if (QVector3D::dotProduct(cutNormal, traverseDirection) <= 0) {
cutNormal = -cutNormal; cutNormal = -cutNormal;
@ -834,11 +836,6 @@ void Builder::setDeformWidth(float width)
m_deformWidth = width; m_deformWidth = width;
} }
void Builder::setCutRotation(float cutRotation)
{
m_cutRotation = cutRotation;
}
QVector3D Builder::calculateDeformPosition(const QVector3D &vertexPosition, const QVector3D &ray, const QVector3D &deformNormal, float deformFactor) QVector3D Builder::calculateDeformPosition(const QVector3D &vertexPosition, const QVector3D &ray, const QVector3D &deformNormal, float deformFactor)
{ {
QVector3D revisedNormal = QVector3D::dotProduct(ray, deformNormal) < 0.0 ? -deformNormal : deformNormal; QVector3D revisedNormal = QVector3D::dotProduct(ray, deformNormal) < 0.0 ? -deformNormal : deformNormal;

View File

@ -23,12 +23,11 @@ public:
bool reverse = false; bool reverse = false;
}; };
size_t addNode(const QVector3D &position, float radius, const std::vector<QVector2D> &cutTemplate); size_t addNode(const QVector3D &position, float radius, const std::vector<QVector2D> &cutTemplate, float cutRotation);
size_t addEdge(size_t firstNodeIndex, size_t secondNodeIndex); size_t addEdge(size_t firstNodeIndex, size_t secondNodeIndex);
void setNodeOriginInfo(size_t nodeIndex, int nearOriginNodeIndex, int farOriginNodeIndex); void setNodeOriginInfo(size_t nodeIndex, int nearOriginNodeIndex, int farOriginNodeIndex);
void setDeformThickness(float thickness); void setDeformThickness(float thickness);
void setDeformWidth(float width); void setDeformWidth(float width);
void setCutRotation(float cutRotation);
void enableBaseNormalOnX(bool enabled); void enableBaseNormalOnX(bool enabled);
void enableBaseNormalOnY(bool enabled); void enableBaseNormalOnY(bool enabled);
void enableBaseNormalOnZ(bool enabled); void enableBaseNormalOnZ(bool enabled);
@ -50,6 +49,7 @@ private:
QVector3D position; QVector3D position;
std::vector<size_t> edges; std::vector<size_t> edges;
std::vector<QVector2D> cutTemplate; std::vector<QVector2D> cutTemplate;
float cutRotation;
std::vector<QVector3D> raysToNeibors; std::vector<QVector3D> raysToNeibors;
QVector3D cutNormal; QVector3D cutNormal;
CutFaceTransform cutFaceTransform; CutFaceTransform cutFaceTransform;
@ -137,6 +137,7 @@ private:
void makeCut(const QVector3D &position, void makeCut(const QVector3D &position,
float radius, float radius,
const std::vector<QVector2D> &cutTemplate, const std::vector<QVector2D> &cutTemplate,
float cutRotation,
QVector3D &baseNormal, QVector3D &baseNormal,
QVector3D &cutNormal, QVector3D &cutNormal,
const QVector3D &traverseDirection, const QVector3D &traverseDirection,

View File

@ -6,7 +6,7 @@
namespace nodemesh namespace nodemesh
{ {
size_t Modifier::addNode(const QVector3D &position, float radius, const std::vector<QVector2D> &cutTemplate) size_t Modifier::addNode(const QVector3D &position, float radius, const std::vector<QVector2D> &cutTemplate, float cutRotation)
{ {
size_t nodeIndex = m_nodes.size(); size_t nodeIndex = m_nodes.size();
@ -15,6 +15,7 @@ size_t Modifier::addNode(const QVector3D &position, float radius, const std::vec
node.position = position; node.position = position;
node.radius = radius; node.radius = radius;
node.cutTemplate = cutTemplate; node.cutTemplate = cutTemplate;
node.cutRotation = cutRotation;
node.originNodeIndex = nodeIndex; node.originNodeIndex = nodeIndex;
m_nodes.push_back(node); m_nodes.push_back(node);
@ -38,11 +39,18 @@ void Modifier::createIntermediateNode(const Node &firstNode, const Node &secondN
float firstFactor = 1.0 - factor; float firstFactor = 1.0 - factor;
resultNode->position = firstNode.position * firstFactor + secondNode.position * factor; resultNode->position = firstNode.position * firstFactor + secondNode.position * factor;
resultNode->radius = firstNode.radius * firstFactor + secondNode.radius * factor; resultNode->radius = firstNode.radius * firstFactor + secondNode.radius * factor;
if (factor <= 0.5) {
resultNode->originNodeIndex = firstNode.originNodeIndex;
resultNode->nearOriginNodeIndex = firstNode.originNodeIndex;
resultNode->farOriginNodeIndex = secondNode.originNodeIndex;
resultNode->cutRotation = firstNode.cutRotation;
resultNode->cutTemplate = firstNode.cutTemplate; resultNode->cutTemplate = firstNode.cutTemplate;
for (size_t i = 0; i < secondNode.cutTemplate.size(); ++i) { } else {
if (i >= resultNode->cutTemplate.size()) resultNode->originNodeIndex = secondNode.originNodeIndex;
break; resultNode->nearOriginNodeIndex = secondNode.originNodeIndex;
resultNode->cutTemplate[i] = resultNode->cutTemplate[i] * firstFactor + secondNode.cutTemplate[i] * factor; resultNode->farOriginNodeIndex = firstNode.originNodeIndex;
resultNode->cutRotation = secondNode.cutRotation;
resultNode->cutTemplate = secondNode.cutTemplate;
} }
} }
@ -81,6 +89,7 @@ void Modifier::roundEnd()
endNode.radius = currentNode.radius * 0.5; endNode.radius = currentNode.radius * 0.5;
endNode.position = currentNode.position + (currentNode.position - neighborNode.position).normalized() * endNode.radius; endNode.position = currentNode.position + (currentNode.position - neighborNode.position).normalized() * endNode.radius;
endNode.cutTemplate = currentNode.cutTemplate; endNode.cutTemplate = currentNode.cutTemplate;
endNode.cutRotation = currentNode.cutRotation;
endNode.originNodeIndex = currentNode.originNodeIndex; endNode.originNodeIndex = currentNode.originNodeIndex;
size_t endNodeIndex = m_nodes.size(); size_t endNodeIndex = m_nodes.size();
m_nodes.push_back(endNode); m_nodes.push_back(endNode);
@ -117,15 +126,6 @@ void Modifier::finalize()
const Node &firstNode = m_nodes[edge.firstNodeIndex]; const Node &firstNode = m_nodes[edge.firstNodeIndex];
const Node &secondNode = m_nodes[edge.secondNodeIndex]; const Node &secondNode = m_nodes[edge.secondNodeIndex];
createIntermediateNode(firstNode, secondNode, factor, &intermediateNode); createIntermediateNode(firstNode, secondNode, factor, &intermediateNode);
if (factor <= 0.5) {
intermediateNode.originNodeIndex = firstNode.originNodeIndex;
intermediateNode.nearOriginNodeIndex = firstNode.originNodeIndex;
intermediateNode.farOriginNodeIndex = secondNode.originNodeIndex;
} else {
intermediateNode.originNodeIndex = secondNode.originNodeIndex;
intermediateNode.nearOriginNodeIndex = secondNode.originNodeIndex;
intermediateNode.farOriginNodeIndex = firstNode.originNodeIndex;
}
size_t intermedidateNodeIndex = m_nodes.size(); size_t intermedidateNodeIndex = m_nodes.size();
nodeIndices.push_back(intermedidateNodeIndex); nodeIndices.push_back(intermedidateNodeIndex);
m_nodes.push_back(intermediateNode); m_nodes.push_back(intermediateNode);

View File

@ -15,6 +15,7 @@ public:
QVector3D position; QVector3D position;
float radius = 0.0; float radius = 0.0;
std::vector<QVector2D> cutTemplate; std::vector<QVector2D> cutTemplate;
float cutRotation = 0.0;
int nearOriginNodeIndex = -1; int nearOriginNodeIndex = -1;
int farOriginNodeIndex = -1; int farOriginNodeIndex = -1;
int originNodeIndex = 0; int originNodeIndex = 0;
@ -26,7 +27,7 @@ public:
size_t secondNodeIndex; size_t secondNodeIndex;
}; };
size_t addNode(const QVector3D &position, float radius, const std::vector<QVector2D> &cutTemplate); size_t addNode(const QVector3D &position, float radius, const std::vector<QVector2D> &cutTemplate, float cutRotation);
size_t addEdge(size_t firstNodeIndex, size_t secondNodeIndex); size_t addEdge(size_t firstNodeIndex, size_t secondNodeIndex);
void subdivide(); void subdivide();
void roundEnd(); void roundEnd();