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
|
||||
qtPrepareTool(LUPDATE, lupdate)
|
||||
LUPDATE += src/*.cpp src/*.h -locations none
|
||||
TSFILES = $$files($$PWD/languages/dust3d_??.ts)
|
||||
for(file, TSFILES) {
|
||||
lang = $$replace(file, .*_([^/]*).ts, 1)
|
||||
v = ts-$${lang}.commands
|
||||
$$v = cd $$wd && $$LUPDATE $$SOURCES $$APP_FILES -ts $$file
|
||||
QMAKE_EXTRA_TARGETS += ts-$$lang
|
||||
for(lang, LANGUAGES) {
|
||||
command = $$LUPDATE -ts languages/dust3d_$${lang}.ts
|
||||
system($$command)|error("Failed to run: $$command")
|
||||
}
|
||||
|
||||
##########################################################
|
||||
|
|
|
@ -342,6 +342,18 @@ Tips:
|
|||
<source>glTF Binary Format (.glb)</source>
|
||||
<translation>glb文档(.glb)</translation>
|
||||
</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>
|
||||
<name>ExportPreviewWidget</name>
|
||||
|
@ -1075,5 +1087,13 @@ Tips:
|
|||
<source>Unselect All</source>
|
||||
<translation>取消全选</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cut Face...</source>
|
||||
<translation>切面...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Clear Cut Face</source>
|
||||
<translation>清除切面</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
114
src/document.cpp
114
src/document.cpp
|
@ -614,6 +614,19 @@ void Document::updateLinkedPart(QUuid oldPartId, QUuid 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
|
||||
|
@ -799,6 +812,75 @@ void Document::setNodeBoneMark(QUuid nodeId, BoneMark mark)
|
|||
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)
|
||||
{
|
||||
turnaround = image;
|
||||
|
@ -976,6 +1058,16 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set<QUuid> &limitNodeId
|
|||
node["partId"] = nodeIt.second.partId.toString();
|
||||
if (nodeIt.second.boneMark != BoneMark::None)
|
||||
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())
|
||||
node["name"] = nodeIt.second.name;
|
||||
snapshot->nodes[node["id"]] = node;
|
||||
|
@ -1248,7 +1340,7 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste)
|
|||
for (const auto &it: cutFaceLinkedIdModifyMap) {
|
||||
SkeletonPart &part = partMap[it.first];
|
||||
auto findNewLinkedId = oldNewIdMap.find(it.second);
|
||||
if (oldNewIdMap.find(it.second) == oldNewIdMap.end()) {
|
||||
if (findNewLinkedId == oldNewIdMap.end()) {
|
||||
if (partMap.find(it.second) == partMap.end()) {
|
||||
part.setCutFaceLinkedId(QUuid());
|
||||
}
|
||||
|
@ -1273,6 +1365,26 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste)
|
|||
node.z = valueOfKeyInMapOrEmpty(nodeKv.second, "z").toFloat();
|
||||
node.partId = oldNewIdMap[QUuid(valueOfKeyInMapOrEmpty(nodeKv.second, "partId"))];
|
||||
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;
|
||||
newAddedNodeIds.insert(node.id);
|
||||
}
|
||||
|
|
|
@ -383,6 +383,9 @@ signals:
|
|||
void edgeRemoved(QUuid edgeId);
|
||||
void nodeRadiusChanged(QUuid nodeId);
|
||||
void nodeBoneMarkChanged(QUuid nodeId);
|
||||
void nodeColorStateChanged(QUuid nodeId);
|
||||
void nodeCutRotationChanged(QUuid nodeId);
|
||||
void nodeCutFaceChanged(QUuid nodeId);
|
||||
void nodeOriginChanged(QUuid nodeId);
|
||||
void edgeChanged(QUuid edgeId);
|
||||
void partPreviewChanged(QUuid partId);
|
||||
|
@ -536,6 +539,10 @@ public slots:
|
|||
void setNodeOrigin(QUuid nodeId, float x, float y, float z);
|
||||
void setNodeRadius(QUuid nodeId, float radius);
|
||||
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 moveOriginBy(float x, float y, float z);
|
||||
void addEdge(QUuid fromNodeId, QUuid toNodeId);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <map>
|
||||
#include <QDesktopServices>
|
||||
#include <QDockWidget>
|
||||
#include <QWidgetAction>
|
||||
#include "documentwindow.h"
|
||||
#include "skeletongraphicswidget.h"
|
||||
#include "theme.h"
|
||||
|
@ -36,6 +37,8 @@
|
|||
#include "spinnableawesomebutton.h"
|
||||
#include "fbxfile.h"
|
||||
#include "shortcuts.h"
|
||||
#include "floatnumberwidget.h"
|
||||
#include "cutfacelistwidget.h"
|
||||
|
||||
int DocumentWindow::m_modelRenderWidgetInitialX = 16;
|
||||
int DocumentWindow::m_modelRenderWidgetInitialY = 16;
|
||||
|
@ -493,6 +496,18 @@ DocumentWindow::DocumentWindow() :
|
|||
});
|
||||
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_alignToLocalCenterAction = new QAction(tr("Local Center"), this);
|
||||
|
@ -568,6 +583,8 @@ DocumentWindow::DocumentWindow() :
|
|||
m_rotateClockwiseAction->setEnabled(m_graphicsWidget->hasMultipleSelection());
|
||||
m_rotateCounterclockwiseAction->setEnabled(m_graphicsWidget->hasMultipleSelection());
|
||||
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_alignToGlobalVerticalCenterAction->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::setNodeOrigin, m_document, &Document::setNodeOrigin);
|
||||
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::setEditMode, m_document, &Document::setEditMode);
|
||||
connect(graphicsWidget, &SkeletonGraphicsWidget::removeEdge, m_document, &Document::removeEdge);
|
||||
|
@ -802,6 +820,7 @@ DocumentWindow::DocumentWindow() :
|
|||
connect(graphicsWidget, &SkeletonGraphicsWidget::changeTurnaround, this, &DocumentWindow::changeTurnaround);
|
||||
connect(graphicsWidget, &SkeletonGraphicsWidget::save, this, &DocumentWindow::save);
|
||||
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::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());
|
||||
}
|
||||
|
||||
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 unregisterDialog(QWidget *widget);
|
||||
void showPreferences();
|
||||
void showCutFaceSettingPopup(const QPoint &globalPos, std::set<QUuid> nodeIds);
|
||||
private:
|
||||
void initLockButton(QPushButton *button);
|
||||
void setCurrentFilename(const QString &filename);
|
||||
|
@ -119,6 +120,8 @@ private:
|
|||
QAction *m_rotateClockwiseAction;
|
||||
QAction *m_rotateCounterclockwiseAction;
|
||||
QAction *m_switchXzAction;
|
||||
QAction *m_setCutFaceAction;
|
||||
QAction *m_clearCutFaceAction;
|
||||
|
||||
QMenu *m_alignToMenu;
|
||||
QAction *m_alignToGlobalCenterAction;
|
||||
|
|
|
@ -111,9 +111,24 @@ bool MeshGenerator::checkIsPartDependencyDirty(const QString &partIdString)
|
|||
}
|
||||
QString cutFaceString = valueOfKeyInMapOrEmpty(findPart->second, "cutFace");
|
||||
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 checkIsPartDirty(cutFaceString);
|
||||
}
|
||||
|
||||
bool MeshGenerator::checkIsComponentDirty(const QString &componentIdString)
|
||||
|
@ -167,32 +182,9 @@ void MeshGenerator::checkDirtyFlags()
|
|||
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);
|
||||
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");
|
||||
//std::map<QString, QVector2D> cutTemplateMapByName;
|
||||
QUuid cutFaceLinkedPartId = QUuid(cutFaceString);
|
||||
if (!cutFaceLinkedPartId.isNull()) {
|
||||
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
|
||||
std::vector<QString> cutTemplateNames;
|
||||
cutFacePointsFromNodes(cutTemplate, cutFaceNodes, isRing, &cutTemplateNames);
|
||||
for (size_t i = 0; i < cutTemplateNames.size(); ++i) {
|
||||
cutTemplateMapByName.insert({cutTemplateNames[i], cutTemplate[i]});
|
||||
}
|
||||
//for (size_t i = 0; i < cutTemplateNames.size(); ++i) {
|
||||
// cutTemplateMapByName.insert({cutTemplateNames[i], cutTemplate[i]});
|
||||
//}
|
||||
}
|
||||
}
|
||||
if (cutTemplate.size() < 3) {
|
||||
CutFace cutFace = CutFaceFromString(cutFaceString.toUtf8().constData());
|
||||
cutTemplate = CutFaceToPoints(cutFace);
|
||||
cutTemplateMapByName.clear();
|
||||
for (size_t i = 0; i < cutTemplate.size(); ++i) {
|
||||
cutTemplateMapByName.insert({cutFaceString + "/" + QString::number(i + 1), cutTemplate[i]});
|
||||
//cutTemplateMapByName.clear();
|
||||
//for (size_t i = 0; i < cutTemplate.size(); ++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)
|
||||
nodemesh::chamferFace2D(&cutTemplate);
|
||||
|
||||
|
@ -362,6 +382,9 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
|
|||
float radius = 0;
|
||||
QVector3D position;
|
||||
BoneMark boneMark = BoneMark::None;
|
||||
bool hasCutFaceSettings = false;
|
||||
float cutRotation = 0.0;
|
||||
QString cutFace;
|
||||
};
|
||||
std::map<QString, NodeInfo> nodeInfos;
|
||||
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());
|
||||
|
||||
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];
|
||||
nodeInfo.position = QVector3D(x, y, z);
|
||||
nodeInfo.radius = radius;
|
||||
nodeInfo.boneMark = boneMark;
|
||||
nodeInfo.hasCutFaceSettings = hasCutFaceSettings;
|
||||
nodeInfo.cutRotation = cutRotation;
|
||||
nodeInfo.cutFace = cutFace;
|
||||
}
|
||||
|
||||
std::set<std::pair<QString, QString>> edges;
|
||||
|
@ -428,7 +468,16 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
|
|||
for (const auto &nodeIt: nodeInfos) {
|
||||
const auto &nodeIdString = nodeIt.first;
|
||||
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;
|
||||
nodeIndexToIdStringMap[nodeIndex] = nodeIdString;
|
||||
|
||||
|
@ -482,7 +531,6 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
|
|||
nodemesh::Builder *builder = new nodemesh::Builder;
|
||||
builder->setDeformThickness(deformThickness);
|
||||
builder->setDeformWidth(deformWidth);
|
||||
builder->setCutRotation(cutRotation);
|
||||
if (PartBase::YZ == base) {
|
||||
builder->enableBaseNormalOnX(false);
|
||||
} else if (PartBase::Average == base) {
|
||||
|
@ -495,7 +543,7 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
|
|||
|
||||
std::vector<size_t> builderNodeIndices;
|
||||
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);
|
||||
builderNodeIndices.push_back(nodeIndex);
|
||||
}
|
||||
|
@ -503,19 +551,19 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
|
|||
builder->addEdge(edge.firstNodeIndex, edge.secondNodeIndex);
|
||||
bool buildSucceed = builder->build();
|
||||
|
||||
for (size_t i = 0; i < modifier->nodes().size(); ++i) {
|
||||
const auto &node = modifier->nodes()[i];
|
||||
if (!node.isOriginal)
|
||||
continue;
|
||||
const QString &nodeIdString = nodeIndexToIdStringMap[node.originNodeIndex];
|
||||
const nodemesh::Builder::CutFaceTransform *cutFaceTransform = builder->nodeAdjustableCutFaceTransform(builderNodeIndices[i]);
|
||||
if (nullptr != cutFaceTransform &&
|
||||
PartTarget::Model == target) {
|
||||
QUuid nodeId = QUuid(nodeIdString);
|
||||
m_cutFaceTransforms->insert({nodeId, *cutFaceTransform});
|
||||
m_nodesCutFaces->insert({nodeId, cutTemplateMapByName});
|
||||
}
|
||||
}
|
||||
//for (size_t i = 0; i < modifier->nodes().size(); ++i) {
|
||||
// const auto &node = modifier->nodes()[i];
|
||||
// if (!node.isOriginal)
|
||||
// continue;
|
||||
// const QString &nodeIdString = nodeIndexToIdStringMap[node.originNodeIndex];
|
||||
// const nodemesh::Builder::CutFaceTransform *cutFaceTransform = builder->nodeAdjustableCutFaceTransform(builderNodeIndices[i]);
|
||||
// if (nullptr != cutFaceTransform &&
|
||||
// PartTarget::Model == target) {
|
||||
// QUuid nodeId = QUuid(nodeIdString);
|
||||
// m_cutFaceTransforms->insert({nodeId, *cutFaceTransform});
|
||||
// m_nodesCutFaces->insert({nodeId, cutTemplateMapByName});
|
||||
// }
|
||||
//}
|
||||
|
||||
partCache.vertices = builder->generatedVertices();
|
||||
partCache.faces = builder->generatedFaces();
|
||||
|
@ -531,7 +579,7 @@ nodemesh::Combiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdSt
|
|||
nodemesh::Combiner::Mesh *mesh = nullptr;
|
||||
|
||||
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 (xMirrored) {
|
||||
std::vector<QVector3D> xMirroredVertices;
|
||||
|
@ -1008,8 +1056,8 @@ void MeshGenerator::generate()
|
|||
countTimeConsumed.start();
|
||||
|
||||
m_outcome = new Outcome;
|
||||
m_cutFaceTransforms = new std::map<QUuid, nodemesh::Builder::CutFaceTransform>;
|
||||
m_nodesCutFaces = new std::map<QUuid, std::map<QString, QVector2D>>;
|
||||
//m_cutFaceTransforms = new std::map<QUuid, nodemesh::Builder::CutFaceTransform>;
|
||||
//m_nodesCutFaces = new std::map<QUuid, std::map<QString, QVector2D>>;
|
||||
|
||||
bool needDeleteCacheContext = false;
|
||||
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);
|
||||
QString componentColorName(const std::map<QString, QString> *component);
|
||||
void collectUncombinedComponent(const QString &componentIdString);
|
||||
void cutFaceStringToCutTemplate(const QString &cutFaceString, std::vector<QVector2D> &cutTemplate);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -22,7 +22,10 @@ public:
|
|||
y(0),
|
||||
z(0),
|
||||
radius(0),
|
||||
boneMark(BoneMark::None)
|
||||
boneMark(BoneMark::None),
|
||||
cutRotation(0.0),
|
||||
cutFace(CutFace::Quad),
|
||||
hasCutFaceSettings(false)
|
||||
{
|
||||
id = withId.isNull() ? QUuid::createUuid() : withId;
|
||||
}
|
||||
|
@ -34,6 +37,38 @@ public:
|
|||
toRadius = 1;
|
||||
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 partId;
|
||||
QString name;
|
||||
|
@ -42,6 +77,10 @@ public:
|
|||
float z;
|
||||
float radius;
|
||||
BoneMark boneMark;
|
||||
float cutRotation;
|
||||
CutFace cutFace;
|
||||
QUuid cutFaceLinkedId;
|
||||
bool hasCutFaceSettings;
|
||||
std::vector<QUuid> edgeIds;
|
||||
};
|
||||
|
||||
|
|
|
@ -219,6 +219,20 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos)
|
|||
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 alignToLocalVerticalCenterAction(tr("Local Vertical Center"), this);
|
||||
QAction alignToLocalHorizontalCenterAction(tr("Local Horizontal Center"), this);
|
||||
|
@ -353,6 +367,23 @@ bool SkeletonGraphicsWidget::hasTwoDisconnectedNodesSelection()
|
|||
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()
|
||||
{
|
||||
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)
|
||||
{
|
||||
m_nodePositionModifyOnly = nodePositionModifyOnly;
|
||||
|
|
|
@ -406,6 +406,8 @@ signals:
|
|||
void setZlockState(bool locked);
|
||||
void setNodeOrigin(QUuid nodeId, float x, float y, float z);
|
||||
void setNodeBoneMark(QUuid nodeId, BoneMark mark);
|
||||
void clearNodeCutFaceSettings(QUuid nodeId);
|
||||
void showCutFaceSettingPopup(const QPoint &globalPos, std::set<QUuid> nodeIds);
|
||||
void zoomRenderedModelBy(float delta);
|
||||
void switchNodeXZ(QUuid nodeId);
|
||||
void switchChainSide(std::set<QUuid> nodeIds);
|
||||
|
@ -437,6 +439,7 @@ public:
|
|||
bool hasEdgeSelection();
|
||||
bool hasNodeSelection();
|
||||
bool hasTwoDisconnectedNodesSelection();
|
||||
bool hasCutFaceAdjustedNodesSelection();
|
||||
void setModelWidget(ModelWidget *modelWidget);
|
||||
void setNodePositionModifyOnly(bool nodePositionModifyOnly);
|
||||
void setMainProfileOnly(bool mainProfileOnly);
|
||||
|
@ -505,6 +508,8 @@ public slots:
|
|||
void timeToRemoveDeferredNodesAndEdges();
|
||||
void switchSelectedXZ();
|
||||
void switchSelectedChainSide();
|
||||
void showSelectedCutFaceSettingPopup(const QPoint &pos);
|
||||
void clearSelectedCutFace();
|
||||
void shortcutDelete();
|
||||
void shortcutAddMode();
|
||||
void shortcutUndo();
|
||||
|
|
|
@ -16,13 +16,14 @@
|
|||
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();
|
||||
Node node;
|
||||
node.position = position;
|
||||
node.radius = radius;
|
||||
node.cutTemplate = cutTemplate;
|
||||
node.cutRotation = cutRotation;
|
||||
m_nodes.push_back(node);
|
||||
m_sortedNodeIndices.push_back(nodeIndex);
|
||||
//qDebug() << "addNode" << position << radius;
|
||||
|
@ -452,7 +453,7 @@ bool Builder::generateCutsForNode(size_t nodeIndex)
|
|||
if (1 == neighborsCount) {
|
||||
QVector3D cutNormal = node.cutNormal;
|
||||
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;
|
||||
std::vector<size_t> vertices;
|
||||
insertCutVertices(cut, vertices, nodeIndex, cutNormal);
|
||||
|
@ -477,7 +478,7 @@ bool Builder::generateCutsForNode(size_t nodeIndex)
|
|||
}
|
||||
}
|
||||
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;
|
||||
std::vector<size_t> vertices;
|
||||
insertCutVertices(cut, vertices, nodeIndex, cutNormal);
|
||||
|
@ -549,7 +550,7 @@ bool Builder::tryWrapMultipleBranchesForNode(size_t nodeIndex, std::vector<float
|
|||
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;
|
||||
insertCutVertices(cut, vertices, nodeIndex, cutNormal);
|
||||
cutsForEdges.push_back({vertices, -cutNormal});
|
||||
|
@ -715,6 +716,7 @@ QVector3D Builder::revisedBaseNormalAcordingToCutNormal(const QVector3D &baseNor
|
|||
void Builder::makeCut(const QVector3D &position,
|
||||
float radius,
|
||||
const std::vector<QVector2D> &cutTemplate,
|
||||
float cutRotation,
|
||||
QVector3D &baseNormal,
|
||||
QVector3D &cutNormal,
|
||||
const QVector3D &traverseDirection,
|
||||
|
@ -723,8 +725,8 @@ void Builder::makeCut(const QVector3D &position,
|
|||
{
|
||||
auto finalCutTemplate = cutTemplate;
|
||||
float degree = 0;
|
||||
if (!qFuzzyIsNull(m_cutRotation)) {
|
||||
degree = m_cutRotation * 180;
|
||||
if (!qFuzzyIsNull(cutRotation)) {
|
||||
degree = cutRotation * 180;
|
||||
}
|
||||
if (QVector3D::dotProduct(cutNormal, traverseDirection) <= 0) {
|
||||
cutNormal = -cutNormal;
|
||||
|
@ -834,11 +836,6 @@ void Builder::setDeformWidth(float 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 revisedNormal = QVector3D::dotProduct(ray, deformNormal) < 0.0 ? -deformNormal : deformNormal;
|
||||
|
|
|
@ -23,12 +23,11 @@ public:
|
|||
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);
|
||||
void setNodeOriginInfo(size_t nodeIndex, int nearOriginNodeIndex, int farOriginNodeIndex);
|
||||
void setDeformThickness(float thickness);
|
||||
void setDeformWidth(float width);
|
||||
void setCutRotation(float cutRotation);
|
||||
void enableBaseNormalOnX(bool enabled);
|
||||
void enableBaseNormalOnY(bool enabled);
|
||||
void enableBaseNormalOnZ(bool enabled);
|
||||
|
@ -50,6 +49,7 @@ private:
|
|||
QVector3D position;
|
||||
std::vector<size_t> edges;
|
||||
std::vector<QVector2D> cutTemplate;
|
||||
float cutRotation;
|
||||
std::vector<QVector3D> raysToNeibors;
|
||||
QVector3D cutNormal;
|
||||
CutFaceTransform cutFaceTransform;
|
||||
|
@ -137,6 +137,7 @@ private:
|
|||
void makeCut(const QVector3D &position,
|
||||
float radius,
|
||||
const std::vector<QVector2D> &cutTemplate,
|
||||
float cutRotation,
|
||||
QVector3D &baseNormal,
|
||||
QVector3D &cutNormal,
|
||||
const QVector3D &traverseDirection,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
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();
|
||||
|
||||
|
@ -15,6 +15,7 @@ size_t Modifier::addNode(const QVector3D &position, float radius, const std::vec
|
|||
node.position = position;
|
||||
node.radius = radius;
|
||||
node.cutTemplate = cutTemplate;
|
||||
node.cutRotation = cutRotation;
|
||||
node.originNodeIndex = nodeIndex;
|
||||
m_nodes.push_back(node);
|
||||
|
||||
|
@ -38,11 +39,18 @@ void Modifier::createIntermediateNode(const Node &firstNode, const Node &secondN
|
|||
float firstFactor = 1.0 - factor;
|
||||
resultNode->position = firstNode.position * firstFactor + secondNode.position * 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;
|
||||
for (size_t i = 0; i < secondNode.cutTemplate.size(); ++i) {
|
||||
if (i >= resultNode->cutTemplate.size())
|
||||
break;
|
||||
resultNode->cutTemplate[i] = resultNode->cutTemplate[i] * firstFactor + secondNode.cutTemplate[i] * factor;
|
||||
} else {
|
||||
resultNode->originNodeIndex = secondNode.originNodeIndex;
|
||||
resultNode->nearOriginNodeIndex = secondNode.originNodeIndex;
|
||||
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.position = currentNode.position + (currentNode.position - neighborNode.position).normalized() * endNode.radius;
|
||||
endNode.cutTemplate = currentNode.cutTemplate;
|
||||
endNode.cutRotation = currentNode.cutRotation;
|
||||
endNode.originNodeIndex = currentNode.originNodeIndex;
|
||||
size_t endNodeIndex = m_nodes.size();
|
||||
m_nodes.push_back(endNode);
|
||||
|
@ -117,15 +126,6 @@ void Modifier::finalize()
|
|||
const Node &firstNode = m_nodes[edge.firstNodeIndex];
|
||||
const Node &secondNode = m_nodes[edge.secondNodeIndex];
|
||||
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();
|
||||
nodeIndices.push_back(intermedidateNodeIndex);
|
||||
m_nodes.push_back(intermediateNode);
|
||||
|
|
|
@ -15,6 +15,7 @@ public:
|
|||
QVector3D position;
|
||||
float radius = 0.0;
|
||||
std::vector<QVector2D> cutTemplate;
|
||||
float cutRotation = 0.0;
|
||||
int nearOriginNodeIndex = -1;
|
||||
int farOriginNodeIndex = -1;
|
||||
int originNodeIndex = 0;
|
||||
|
@ -26,7 +27,7 @@ public:
|
|||
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);
|
||||
void subdivide();
|
||||
void roundEnd();
|
||||
|
|
Loading…
Reference in New Issue