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
parent
7c66e6b5d0
commit
31d627bca9
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
##########################################################
|
##########################################################
|
||||||
|
|
|
@ -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>
|
||||||
|
|
114
src/document.cpp
114
src/document.cpp
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in New Issue