Add stiffness setting for cloth simulation
parent
c4882dec1f
commit
44cd33d799
|
@ -752,6 +752,10 @@ Tips:
|
|||
<source>Layer</source>
|
||||
<translation>层</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Stiffness</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PartWidget</name>
|
||||
|
|
|
@ -55,16 +55,13 @@ private:
|
|||
};
|
||||
|
||||
// System parameters
|
||||
namespace SystemParam {
|
||||
static const int n = 61; // must be odd, n * n = n_vertices | 61
|
||||
static const float w = 2.0f; // width | 2.0f
|
||||
static const float h = 0.008f; // time step, smaller for better results | 0.008f = 0.016f/2
|
||||
static const float r = w / (n - 1) * 1.05f; // spring rest legnth
|
||||
static const float k = 1.0f; // spring stiffness | 1.0f;
|
||||
static const float m = 0.25f / (n * n); // point mass | 0.25f
|
||||
static const float a = 0.993f; // damping, close to 1.0 | 0.993f
|
||||
static const float g = 9.8f * m; // gravitational force | 9.8f
|
||||
}
|
||||
//namespace SystemParam {
|
||||
// static const int n = 61; // must be odd, n * n = n_vertices | 61
|
||||
// static const float h = 0.001f; // time step, smaller for better results | 0.008f = 0.016f/2
|
||||
// static const float m = 0.25f / (n * n); // point mass | 0.25f
|
||||
// static const float a = 0.993f; // damping, close to 1.0 | 0.993f
|
||||
// static const float g = 9.8f * m; // gravitational force | 9.8f
|
||||
//}
|
||||
|
||||
// Point - mesh collision node
|
||||
class CgMeshCollisionNode : public CgPointNode {
|
||||
|
@ -136,6 +133,11 @@ ClothSimulator::~ClothSimulator()
|
|||
delete m_meshCollisionNode;
|
||||
}
|
||||
|
||||
void ClothSimulator::setStiffness(float stiffness)
|
||||
{
|
||||
m_stiffness = 1.0f + 5.0f * stiffness;
|
||||
}
|
||||
|
||||
void ClothSimulator::convertMeshToCloth()
|
||||
{
|
||||
m_clothPointSources.reserve(m_vertices.size());
|
||||
|
@ -176,7 +178,7 @@ void ClothSimulator::getCurrentVertices(std::vector<QVector3D> *currentVertices)
|
|||
size_t oldIndex = m_clothPointSources[newIndex];
|
||||
auto offset = newIndex * 3;
|
||||
(*currentVertices)[oldIndex] = QVector3D(m_clothPointBuffer[offset + 0],
|
||||
m_clothPointBuffer[offset + 1],
|
||||
m_clothPointBuffer[offset + 1] + 0.01,
|
||||
m_clothPointBuffer[offset + 2]);
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +202,12 @@ void ClothSimulator::create()
|
|||
if (m_clothPointSources.empty())
|
||||
return;
|
||||
|
||||
mass_spring_system::VectorXf masses(SystemParam::m * mass_spring_system::VectorXf::Ones((unsigned int)m_clothSprings.size()));
|
||||
float mass = 0.25f / m_clothPointSources.size();
|
||||
float gravitationalForce = 9.8f * mass;
|
||||
float damping = 0.993f; // damping, close to 1.0 | 0.993f;
|
||||
float timeStep = 0.001f; //smaller for better results | 0.008f = 0.016f/2;
|
||||
|
||||
mass_spring_system::VectorXf masses(mass * mass_spring_system::VectorXf::Ones((unsigned int)m_clothSprings.size()));
|
||||
|
||||
mass_spring_system::EdgeList springList(m_clothSprings.size());
|
||||
mass_spring_system::VectorXf restLengths(m_clothSprings.size());
|
||||
|
@ -210,13 +217,13 @@ void ClothSimulator::create()
|
|||
const auto &source = m_clothSprings[i];
|
||||
springList[i] = mass_spring_system::Edge(source.first, source.second);
|
||||
restLengths[i] = (m_vertices[m_clothPointSources[source.first]] - m_vertices[m_clothPointSources[source.second]]).length();
|
||||
stiffnesses[i] = SystemParam::k;
|
||||
stiffnesses[i] = m_stiffness;
|
||||
}
|
||||
|
||||
mass_spring_system::VectorXf fext = Eigen::Vector3f(0, -SystemParam::g, 0).replicate(m_clothPointSources.size(), 1);
|
||||
mass_spring_system::VectorXf fext = Eigen::Vector3f(0, -gravitationalForce, 0).replicate(m_clothPointSources.size(), 1);
|
||||
|
||||
m_massSpringSystem = new mass_spring_system(m_clothPointSources.size(), m_clothSprings.size(), SystemParam::h, springList, restLengths,
|
||||
stiffnesses, masses, fext, SystemParam::a);
|
||||
m_massSpringSystem = new mass_spring_system(m_clothPointSources.size(), m_clothSprings.size(), timeStep, springList, restLengths,
|
||||
stiffnesses, masses, fext, damping);
|
||||
|
||||
m_massSpringSolver = new MassSpringSolver(m_massSpringSystem, m_clothPointBuffer.data());
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ public:
|
|||
const std::vector<QVector3D> &collisionVertices,
|
||||
const std::vector<std::vector<size_t>> &collisionTriangles);
|
||||
~ClothSimulator();
|
||||
void setStiffness(float stiffness);
|
||||
void create();
|
||||
void step();
|
||||
void getCurrentVertices(std::vector<QVector3D> *currentVertices);
|
||||
|
@ -30,6 +31,7 @@ private:
|
|||
std::vector<float> m_clothPointBuffer;
|
||||
std::vector<size_t> m_clothPointSources;
|
||||
std::vector<std::pair<size_t, size_t>> m_clothSprings;
|
||||
float m_stiffness = 1.0f;
|
||||
mass_spring_system *m_massSpringSystem = nullptr;
|
||||
MassSpringSolver *m_massSpringSolver = nullptr;
|
||||
CgRootNode *m_rootNode = nullptr;
|
||||
|
|
|
@ -1203,6 +1203,8 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set<QUuid> &limitNodeId
|
|||
component["polyCount"] = PolyCountToString(componentIt.second.polyCount);
|
||||
if (componentIt.second.layer != ComponentLayer::Body)
|
||||
component["layer"] = ComponentLayerToString(componentIt.second.layer);
|
||||
if (componentIt.second.clothStiffnessAdjusted())
|
||||
component["clothStiffness"] = QString::number(componentIt.second.clothStiffness);
|
||||
QStringList childIdList;
|
||||
for (const auto &childId: componentIt.second.childrenIds) {
|
||||
childIdList.append(childId.toString());
|
||||
|
@ -1711,6 +1713,9 @@ void Document::addFromSnapshot(const Snapshot &snapshot, bool fromPaste)
|
|||
component.setSmoothSeam(smoothSeamIt->second.toFloat());
|
||||
component.polyCount = PolyCountFromString(valueOfKeyInMapOrEmpty(componentKv.second, "polyCount").toUtf8().constData());
|
||||
component.layer = ComponentLayerFromString(valueOfKeyInMapOrEmpty(componentKv.second, "layer").toUtf8().constData());
|
||||
auto findClothStiffness = componentKv.second.find("clothStiffness");
|
||||
if (findClothStiffness != componentKv.second.end())
|
||||
component.clothStiffness = findClothStiffness->second.toFloat();
|
||||
//qDebug() << "Add component:" << component.id << " old:" << componentKv.first << "name:" << component.name;
|
||||
if ("partId" == linkDataType) {
|
||||
QUuid partId = oldNewIdMap[QUuid(linkData)];
|
||||
|
@ -2514,6 +2519,20 @@ void Document::setComponentLayer(QUuid componentId, ComponentLayer layer)
|
|||
emit skeletonChanged();
|
||||
}
|
||||
|
||||
void Document::setComponentClothStiffness(QUuid componentId, float stiffness)
|
||||
{
|
||||
Component *component = (Component *)findComponent(componentId);
|
||||
if (nullptr == component)
|
||||
return;
|
||||
if (qFuzzyCompare(component->clothStiffness, stiffness))
|
||||
return;
|
||||
|
||||
component->clothStiffness = stiffness;
|
||||
component->dirty = true;
|
||||
emit componentClothStiffnessChanged(componentId);
|
||||
emit skeletonChanged();
|
||||
}
|
||||
|
||||
void Document::createNewComponentAndMoveThisIn(QUuid componentId)
|
||||
{
|
||||
auto component = componentMap.find(componentId);
|
||||
|
|
|
@ -69,6 +69,7 @@ public:
|
|||
float smoothSeam = 0.0;
|
||||
PolyCount polyCount = PolyCount::Original;
|
||||
ComponentLayer layer = ComponentLayer::Body;
|
||||
float clothStiffness = 1.0f;
|
||||
std::vector<QUuid> childrenIds;
|
||||
QString linkData() const
|
||||
{
|
||||
|
@ -188,6 +189,10 @@ public:
|
|||
{
|
||||
return smoothAllAdjusted() || smoothSeamAdjusted();
|
||||
}
|
||||
bool clothStiffnessAdjusted() const
|
||||
{
|
||||
return fabs(clothStiffness - 1.0) >= 0.01;
|
||||
}
|
||||
private:
|
||||
std::set<QUuid> m_childrenIdSet;
|
||||
};
|
||||
|
@ -399,6 +404,7 @@ signals:
|
|||
void componentSmoothSeamChanged(QUuid componentId);
|
||||
void componentPolyCountChanged(QUuid componentId);
|
||||
void componentLayerChanged(QUuid componentId);
|
||||
void componentClothStiffnessChanged(QUuid componentId);
|
||||
void nodeRemoved(QUuid nodeId);
|
||||
void edgeRemoved(QUuid edgeId);
|
||||
void nodeRadiusChanged(QUuid nodeId);
|
||||
|
@ -646,6 +652,7 @@ public slots:
|
|||
void setComponentSmoothSeam(QUuid componentId, float toSmoothSeam);
|
||||
void setComponentPolyCount(QUuid componentId, PolyCount count);
|
||||
void setComponentLayer(QUuid componentId, ComponentLayer layer);
|
||||
void setComponentClothStiffness(QUuid componentId, float stiffness);
|
||||
void hideOtherComponents(QUuid componentId);
|
||||
void lockOtherComponents(QUuid componentId);
|
||||
void hideAllComponents();
|
||||
|
|
|
@ -1029,6 +1029,7 @@ DocumentWindow::DocumentWindow() :
|
|||
connect(partTreeWidget, &PartTreeWidget::setPartVisibleState, m_document, &Document::setPartVisibleState);
|
||||
connect(partTreeWidget, &PartTreeWidget::setPartColorState, m_document, &Document::setPartColorState);
|
||||
connect(partTreeWidget, &PartTreeWidget::setComponentCombineMode, m_document, &Document::setComponentCombineMode);
|
||||
connect(partTreeWidget, &PartTreeWidget::setComponentClothStiffness, m_document, &Document::setComponentClothStiffness);
|
||||
connect(partTreeWidget, &PartTreeWidget::setPartTarget, m_document, &Document::setPartTarget);
|
||||
connect(partTreeWidget, &PartTreeWidget::setPartBase, m_document, &Document::setPartBase);
|
||||
connect(partTreeWidget, &PartTreeWidget::hideDescendantComponents, m_document, &Document::hideDescendantComponents);
|
||||
|
|
|
@ -893,6 +893,16 @@ ComponentLayer MeshGenerator::componentLayer(const std::map<QString, QString> *c
|
|||
return ComponentLayerFromString(valueOfKeyInMapOrEmpty(*component, "layer").toUtf8().constData());
|
||||
}
|
||||
|
||||
float MeshGenerator::componentClothStiffness(const std::map<QString, QString> *component)
|
||||
{
|
||||
if (nullptr == component)
|
||||
return 1.0f;
|
||||
auto findClothStiffness = component->find("clothStiffness");
|
||||
if (findClothStiffness == component->end())
|
||||
return 1.0f;
|
||||
return findClothStiffness->second.toFloat();
|
||||
}
|
||||
|
||||
MeshCombiner::Mesh *MeshGenerator::combineComponentMesh(const QString &componentIdString, CombineMode *combineMode)
|
||||
{
|
||||
MeshCombiner::Mesh *mesh = nullptr;
|
||||
|
@ -1613,10 +1623,13 @@ void MeshGenerator::collectClothComponent(const QString &componentIdString)
|
|||
isotropicRemesh(uncombinedVertices, uncombinedFaces, uncombinedVertices, uncombinedFaces, 0.02f, 3);
|
||||
|
||||
std::map<PositionKey, std::pair<QUuid, QUuid>> positionMap;
|
||||
std::pair<QUuid, QUuid> defaultSource;
|
||||
for (const auto &it: componentCache.outcomeNodeVertices) {
|
||||
if (!it.second.first.isNull())
|
||||
defaultSource.first = it.second.first;
|
||||
positionMap.insert({PositionKey(it.first), it.second});
|
||||
}
|
||||
std::vector<std::pair<QUuid, QUuid>> uncombinedVertexSources(uncombinedVertices.size());
|
||||
std::vector<std::pair<QUuid, QUuid>> uncombinedVertexSources(uncombinedVertices.size(), defaultSource);
|
||||
for (size_t i = 0; i < uncombinedVertices.size(); ++i) {
|
||||
auto findSource = positionMap.find(PositionKey(uncombinedVertices[i]));
|
||||
if (findSource == positionMap.end())
|
||||
|
@ -1628,8 +1641,9 @@ void MeshGenerator::collectClothComponent(const QString &componentIdString)
|
|||
uncombinedFaces,
|
||||
m_clothCollisionVertices,
|
||||
m_clothCollisionTriangles);
|
||||
clothSimulator.setStiffness(componentClothStiffness(component));
|
||||
clothSimulator.create();
|
||||
for (size_t i = 0; i < 30; ++i)
|
||||
for (size_t i = 0; i < 400; ++i)
|
||||
clothSimulator.step();
|
||||
clothSimulator.getCurrentVertices(&uncombinedVertices);
|
||||
|
||||
|
|
|
@ -129,6 +129,7 @@ private:
|
|||
MeshCombiner::Mesh *combineMultipleMeshes(const std::vector<std::tuple<MeshCombiner::Mesh *, CombineMode, QString>> &multipleMeshes, bool recombine=true);
|
||||
QString componentColorName(const std::map<QString, QString> *component);
|
||||
ComponentLayer componentLayer(const std::map<QString, QString> *component);
|
||||
float componentClothStiffness(const std::map<QString, QString> *component);
|
||||
void collectUncombinedComponent(const QString &componentIdString);
|
||||
void collectClothComponent(const QString &componentIdString);
|
||||
void cutFaceStringToCutTemplate(const QString &cutFaceString, std::vector<QVector2D> &cutTemplate);
|
||||
|
|
|
@ -214,6 +214,51 @@ void PartTreeWidget::mousePressEvent(QMouseEvent *event)
|
|||
}
|
||||
}
|
||||
|
||||
void PartTreeWidget::showClothSettingMenu(const QPoint &pos, const QUuid &componentId)
|
||||
{
|
||||
const Component *component = nullptr;
|
||||
|
||||
if (componentId.isNull())
|
||||
return;
|
||||
|
||||
component = m_document->findComponent(componentId);
|
||||
if (nullptr == component)
|
||||
return;
|
||||
|
||||
QMenu popupMenu;
|
||||
|
||||
QWidget *popup = new QWidget;
|
||||
|
||||
FloatNumberWidget *clothStiffnessWidget = new FloatNumberWidget;
|
||||
clothStiffnessWidget->setItemName(tr("Stiffness"));
|
||||
clothStiffnessWidget->setRange(0.0f, 1.0f);
|
||||
clothStiffnessWidget->setValue(component->clothStiffness);
|
||||
|
||||
connect(clothStiffnessWidget, &FloatNumberWidget::valueChanged, [=](float value) {
|
||||
emit setComponentClothStiffness(componentId, value);
|
||||
emit groupOperationAdded();
|
||||
});
|
||||
|
||||
QPushButton *clothStiffnessEraser = new QPushButton(QChar(fa::eraser));
|
||||
Theme::initAwesomeToolButton(clothStiffnessEraser);
|
||||
|
||||
QHBoxLayout *clothStiffnessLayout = new QHBoxLayout;
|
||||
clothStiffnessLayout->addWidget(clothStiffnessEraser);
|
||||
clothStiffnessLayout->addWidget(clothStiffnessWidget);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(clothStiffnessLayout);
|
||||
|
||||
popup->setLayout(mainLayout);
|
||||
|
||||
QWidgetAction action(this);
|
||||
action.setDefaultWidget(popup);
|
||||
|
||||
popupMenu.addAction(&action);
|
||||
|
||||
popupMenu.exec(mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void PartTreeWidget::showContextMenu(const QPoint &pos)
|
||||
{
|
||||
const Component *component = nullptr;
|
||||
|
@ -315,6 +360,13 @@ void PartTreeWidget::showContextMenu(const QPoint &pos)
|
|||
}
|
||||
QWidget *widget = new QWidget;
|
||||
if (nullptr != component) {
|
||||
QPushButton *clothSettingButton = new QPushButton();
|
||||
connect(clothSettingButton, &QPushButton::clicked, this, [=]() {
|
||||
showClothSettingMenu(mapFromGlobal(QCursor::pos()), component->id);
|
||||
});
|
||||
clothSettingButton->setIcon(Theme::awesome()->icon(fa::gear));
|
||||
if (ComponentLayer::Cloth != component->layer)
|
||||
clothSettingButton->hide();
|
||||
QComboBox *componentLayerSelectBox = new QComboBox;
|
||||
for (size_t i = 0; i < (size_t)ComponentLayer::Count; ++i) {
|
||||
ComponentLayer layer = (ComponentLayer)i;
|
||||
|
@ -322,9 +374,14 @@ void PartTreeWidget::showContextMenu(const QPoint &pos)
|
|||
}
|
||||
componentLayerSelectBox->setCurrentIndex((int)component->layer);
|
||||
connect(componentLayerSelectBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [=](int index) {
|
||||
clothSettingButton->setVisible(ComponentLayer::Cloth == (ComponentLayer)index);
|
||||
emit setComponentLayer(component->id, (ComponentLayer)index);
|
||||
emit groupOperationAdded();
|
||||
});
|
||||
QHBoxLayout *componentLayerLayout = new QHBoxLayout;
|
||||
componentLayerLayout->addWidget(componentLayerSelectBox);
|
||||
componentLayerLayout->addWidget(clothSettingButton);
|
||||
componentLayerLayout->setStretch(0, 1);
|
||||
|
||||
QComboBox *combineModeSelectBox = new QComboBox;
|
||||
for (size_t i = 0; i < (size_t)CombineMode::Count; ++i) {
|
||||
|
@ -379,7 +436,7 @@ void PartTreeWidget::showContextMenu(const QPoint &pos)
|
|||
if (nullptr != partTargetSelectBox)
|
||||
componentSettingsLayout->addRow(tr("Target"), partTargetSelectBox);
|
||||
componentSettingsLayout->addRow(tr("Mode"), combineModeSelectBox);
|
||||
componentSettingsLayout->addRow(tr("Layer"), componentLayerSelectBox);
|
||||
componentSettingsLayout->addRow(tr("Layer"), componentLayerLayout);
|
||||
|
||||
QVBoxLayout *newLayout = new QVBoxLayout;
|
||||
newLayout->addLayout(layout);
|
||||
|
|
|
@ -41,6 +41,7 @@ signals:
|
|||
void setPartVisibleState(QUuid partId, bool visible);
|
||||
void setPartColorState(QUuid partId, bool hasColor, QColor color);
|
||||
void setComponentCombineMode(QUuid componentId, CombineMode combineMode);
|
||||
void setComponentClothStiffness(QUuid componentId, float clothStiffness);
|
||||
void hideDescendantComponents(QUuid componentId);
|
||||
void showDescendantComponents(QUuid componentId);
|
||||
void lockDescendantComponents(QUuid componentId);
|
||||
|
@ -83,6 +84,7 @@ public slots:
|
|||
void groupCollapsed(QTreeWidgetItem *item);
|
||||
void removeAllContent();
|
||||
void showContextMenu(const QPoint &pos);
|
||||
void showClothSettingMenu(const QPoint &pos, const QUuid &componentId);
|
||||
protected:
|
||||
QSize sizeHint() const override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
|
|
Loading…
Reference in New Issue