Optimize cloth mesh simulation by enable tbb.
parent
cff5cff6bc
commit
5a59abdca4
|
@ -497,6 +497,9 @@ HEADERS += src/clothforce.h
|
||||||
SOURCES += src/projectfacestonodes.cpp
|
SOURCES += src/projectfacestonodes.cpp
|
||||||
HEADERS += src/projectfacestonodes.h
|
HEADERS += src/projectfacestonodes.h
|
||||||
|
|
||||||
|
SOURCES += src/simulateclothmeshes.cpp
|
||||||
|
HEADERS += src/simulateclothmeshes.h
|
||||||
|
|
||||||
SOURCES += src/main.cpp
|
SOURCES += src/main.cpp
|
||||||
|
|
||||||
HEADERS += src/version.h
|
HEADERS += src/version.h
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "isotropicremesh.h"
|
#include "isotropicremesh.h"
|
||||||
#include "projectfacestonodes.h"
|
#include "projectfacestonodes.h"
|
||||||
#include "document.h"
|
#include "document.h"
|
||||||
|
#include "simulateclothmeshes.h"
|
||||||
|
|
||||||
MeshGenerator::MeshGenerator(Snapshot *snapshot) :
|
MeshGenerator::MeshGenerator(Snapshot *snapshot) :
|
||||||
m_snapshot(snapshot)
|
m_snapshot(snapshot)
|
||||||
|
@ -1677,7 +1678,8 @@ void MeshGenerator::collectUncombinedComponent(const QString &componentIdString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshGenerator::collectClothComponent(const QString &componentIdString)
|
void MeshGenerator::collectClothComponentIdStrings(const QString &componentIdString,
|
||||||
|
std::vector<QString> *componentIdStrings)
|
||||||
{
|
{
|
||||||
const auto &component = findComponent(componentIdString);
|
const auto &component = findComponent(componentIdString);
|
||||||
if (ComponentLayer::Cloth == componentLayer(component)) {
|
if (ComponentLayer::Cloth == componentLayer(component)) {
|
||||||
|
@ -1685,73 +1687,45 @@ void MeshGenerator::collectClothComponent(const QString &componentIdString)
|
||||||
if (nullptr == componentCache.mesh) {
|
if (nullptr == componentCache.mesh) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (m_clothCollisionTriangles.empty() || m_clothTargetNodes.empty())
|
componentIdStrings->push_back(componentIdString);
|
||||||
return;
|
return;
|
||||||
std::vector<QVector3D> uncombinedVertices;
|
}
|
||||||
std::vector<std::vector<size_t>> uncombinedOriginalFaces;
|
for (const auto &childIdString: valueOfKeyInMapOrEmpty(*component, "children").split(",")) {
|
||||||
std::vector<std::vector<size_t>> uncombinedFaces;
|
if (childIdString.isEmpty())
|
||||||
componentCache.mesh->fetch(uncombinedVertices, uncombinedOriginalFaces);
|
continue;
|
||||||
uncombinedFaces.reserve(uncombinedOriginalFaces.size());
|
collectClothComponentIdStrings(childIdString, componentIdStrings);
|
||||||
for (const auto &it: uncombinedOriginalFaces) {
|
}
|
||||||
if (4 == it.size()) {
|
}
|
||||||
uncombinedFaces.push_back(std::vector<size_t> {
|
|
||||||
it[0], it[1], it[2]
|
void MeshGenerator::collectClothComponent(const QString &componentIdString)
|
||||||
});
|
{
|
||||||
uncombinedFaces.push_back(std::vector<size_t> {
|
if (m_clothCollisionTriangles.empty() || m_clothTargetNodes.empty())
|
||||||
it[2], it[3], it[0]
|
return;
|
||||||
});
|
|
||||||
} else if (3 == it.size()) {
|
|
||||||
uncombinedFaces.push_back(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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(), defaultSource);
|
|
||||||
for (size_t i = 0; i < uncombinedVertices.size(); ++i) {
|
|
||||||
auto findSource = positionMap.find(PositionKey(uncombinedVertices[i]));
|
|
||||||
if (findSource == positionMap.end())
|
|
||||||
continue;
|
|
||||||
uncombinedVertexSources[i] = findSource->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<QVector3D> &filteredClothVertices = uncombinedVertices;
|
|
||||||
std::vector<std::vector<size_t>> &filteredClothFaces = uncombinedFaces;
|
|
||||||
std::vector<QVector3D> externalForces;
|
|
||||||
ClothForce clothForce = componentClothForce(component);
|
|
||||||
float clothOffset = 0.015f + (componentClothOffset(component) * 0.05f);
|
|
||||||
if (ClothForce::Centripetal == clothForce) {
|
|
||||||
externalForces.resize(filteredClothVertices.size());
|
|
||||||
for (size_t i = 0; i < filteredClothFaces.size(); ++i) {
|
|
||||||
const auto &face = filteredClothFaces[i];
|
|
||||||
auto faceForceDirection = -polygonNormal(uncombinedVertices, face);
|
|
||||||
for (const auto &vertex: face)
|
|
||||||
externalForces[vertex] += faceForceDirection;
|
|
||||||
}
|
|
||||||
for (auto &it: externalForces)
|
|
||||||
it = (it.normalized() + QVector3D(0.0f, -1.0f, 0.0f)).normalized();
|
|
||||||
} else {
|
|
||||||
externalForces.resize(filteredClothVertices.size(), QVector3D(0.0f, -1.0f, 0.0f));
|
|
||||||
}
|
|
||||||
ClothSimulator clothSimulator(filteredClothVertices,
|
|
||||||
filteredClothFaces,
|
|
||||||
m_clothCollisionVertices,
|
|
||||||
m_clothCollisionTriangles,
|
|
||||||
externalForces);
|
|
||||||
clothSimulator.setStiffness(componentClothStiffness(component));
|
|
||||||
clothSimulator.create();
|
|
||||||
for (size_t i = 0; i < 350; ++i)
|
|
||||||
clothSimulator.step();
|
|
||||||
clothSimulator.getCurrentVertices(&filteredClothVertices);
|
|
||||||
for (size_t i = 0; i < filteredClothVertices.size(); ++i) {
|
|
||||||
filteredClothVertices[i] -= externalForces[i] * clothOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
std::vector<QString> componentIdStrings;
|
||||||
|
collectClothComponentIdStrings(componentIdString, &componentIdStrings);
|
||||||
|
|
||||||
|
std::vector<ClothMesh> clothMeshes(componentIdStrings.size());
|
||||||
|
for (size_t i = 0; i < componentIdStrings.size(); ++i) {
|
||||||
|
const auto &componentIdString = componentIdStrings[i];
|
||||||
|
const auto &componentCache = m_cacheContext->components[componentIdString];
|
||||||
|
if (nullptr == componentCache.mesh) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto &component = findComponent(componentIdString);
|
||||||
|
auto &clothMesh = clothMeshes[i];
|
||||||
|
componentCache.mesh->fetch(clothMesh.vertices, clothMesh.faces);
|
||||||
|
clothMesh.clothForce = componentClothForce(component);
|
||||||
|
clothMesh.clothOffset = componentClothOffset(component);
|
||||||
|
clothMesh.clothStiffness = componentClothStiffness(component);
|
||||||
|
clothMesh.outcomeNodeVertices = &componentCache.outcomeNodeVertices;
|
||||||
|
m_outcome->nodes.insert(m_outcome->nodes.end(), componentCache.outcomeNodes.begin(), componentCache.outcomeNodes.end());
|
||||||
|
}
|
||||||
|
simulateClothMeshes(&clothMeshes,
|
||||||
|
&m_clothCollisionVertices,
|
||||||
|
&m_clothCollisionTriangles,
|
||||||
|
&m_clothTargetNodes);
|
||||||
|
for (auto &clothMesh: clothMeshes) {
|
||||||
auto vertexStartIndex = m_outcome->vertices.size();
|
auto vertexStartIndex = m_outcome->vertices.size();
|
||||||
auto updateVertexIndices = [=](std::vector<std::vector<size_t>> &faces) {
|
auto updateVertexIndices = [=](std::vector<std::vector<size_t>> &faces) {
|
||||||
for (auto &it: faces) {
|
for (auto &it: faces) {
|
||||||
|
@ -1759,10 +1733,9 @@ void MeshGenerator::collectClothComponent(const QString &componentIdString)
|
||||||
subIt += vertexStartIndex;
|
subIt += vertexStartIndex;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
updateVertexIndices(filteredClothFaces);
|
updateVertexIndices(clothMesh.faces);
|
||||||
|
m_outcome->vertices.insert(m_outcome->vertices.end(), clothMesh.vertices.begin(), clothMesh.vertices.end());
|
||||||
m_outcome->vertices.insert(m_outcome->vertices.end(), filteredClothVertices.begin(), filteredClothVertices.end());
|
for (const auto &it: clothMesh.faces) {
|
||||||
for (const auto &it: filteredClothFaces) {
|
|
||||||
if (4 == it.size()) {
|
if (4 == it.size()) {
|
||||||
m_outcome->triangles.push_back(std::vector<size_t> {
|
m_outcome->triangles.push_back(std::vector<size_t> {
|
||||||
it[0], it[1], it[2]
|
it[0], it[1], it[2]
|
||||||
|
@ -1774,20 +1747,11 @@ void MeshGenerator::collectClothComponent(const QString &componentIdString)
|
||||||
m_outcome->triangles.push_back(it);
|
m_outcome->triangles.push_back(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_outcome->triangleAndQuads.insert(m_outcome->triangleAndQuads.end(), filteredClothFaces.begin(), filteredClothFaces.end());
|
m_outcome->triangleAndQuads.insert(m_outcome->triangleAndQuads.end(), clothMesh.faces.begin(), clothMesh.faces.end());
|
||||||
|
for (size_t i = 0; i < clothMesh.vertices.size(); ++i) {
|
||||||
m_outcome->nodes.insert(m_outcome->nodes.end(), componentCache.outcomeNodes.begin(), componentCache.outcomeNodes.end());
|
const auto &source = clothMesh.vertexSources[i];
|
||||||
Q_ASSERT(uncombinedVertices.size() == filteredClothVertices.size());
|
m_outcome->nodeVertices.push_back(std::make_pair(clothMesh.vertices[i], source));
|
||||||
for (size_t i = 0; i < uncombinedVertices.size(); ++i) {
|
|
||||||
const auto &source = uncombinedVertexSources[i];
|
|
||||||
m_outcome->nodeVertices.push_back(std::make_pair(filteredClothVertices[i], source));
|
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (const auto &childIdString: valueOfKeyInMapOrEmpty(*component, "children").split(",")) {
|
|
||||||
if (childIdString.isEmpty())
|
|
||||||
continue;
|
|
||||||
collectClothComponent(childIdString);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -138,6 +138,8 @@ private:
|
||||||
float componentClothOffset(const std::map<QString, QString> *component);
|
float componentClothOffset(const std::map<QString, QString> *component);
|
||||||
void collectUncombinedComponent(const QString &componentIdString);
|
void collectUncombinedComponent(const QString &componentIdString);
|
||||||
void collectClothComponent(const QString &componentIdString);
|
void collectClothComponent(const QString &componentIdString);
|
||||||
|
void collectClothComponentIdStrings(const QString &componentIdString,
|
||||||
|
std::vector<QString> *componentIdStrings);
|
||||||
void cutFaceStringToCutTemplate(const QString &cutFaceString, std::vector<QVector2D> &cutTemplate);
|
void cutFaceStringToCutTemplate(const QString &cutFaceString, std::vector<QVector2D> &cutTemplate);
|
||||||
void remesh(const std::vector<OutcomeNode> &inputNodes,
|
void remesh(const std::vector<OutcomeNode> &inputNodes,
|
||||||
const std::vector<QVector3D> &inputVertices,
|
const std::vector<QVector3D> &inputVertices,
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
#include <tbb/parallel_for.h>
|
||||||
|
#include <tbb/blocked_range.h>
|
||||||
|
#include "simulateclothmeshes.h"
|
||||||
|
#include "positionkey.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "clothsimulator.h"
|
||||||
|
|
||||||
|
class ClothMeshesSimulator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ClothMeshesSimulator(std::vector<ClothMesh> *clothMeshes,
|
||||||
|
const std::vector<QVector3D> *clothCollisionVertices,
|
||||||
|
const std::vector<std::vector<size_t>> *clothCollisionTriangles,
|
||||||
|
const std::vector<std::pair<QVector3D, float>> *clothTargetNodes) :
|
||||||
|
m_clothMeshes(clothMeshes),
|
||||||
|
m_clothCollisionVertices(clothCollisionVertices),
|
||||||
|
m_clothCollisionTriangles(clothCollisionTriangles),
|
||||||
|
m_clothTargetNodes(clothTargetNodes)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void simulate(ClothMesh *clothMesh) const
|
||||||
|
{
|
||||||
|
std::vector<std::vector<size_t>> filteredClothFaces;
|
||||||
|
for (const auto &it: clothMesh->faces) {
|
||||||
|
if (4 == it.size()) {
|
||||||
|
filteredClothFaces.push_back(std::vector<size_t> {
|
||||||
|
it[0], it[1], it[2]
|
||||||
|
});
|
||||||
|
filteredClothFaces.push_back(std::vector<size_t> {
|
||||||
|
it[2], it[3], it[0]
|
||||||
|
});
|
||||||
|
} else if (3 == it.size()) {
|
||||||
|
filteredClothFaces.push_back(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::map<PositionKey, std::pair<QUuid, QUuid>> positionMap;
|
||||||
|
std::pair<QUuid, QUuid> defaultSource;
|
||||||
|
for (const auto &it: *clothMesh->outcomeNodeVertices) {
|
||||||
|
if (!it.second.first.isNull())
|
||||||
|
defaultSource.first = it.second.first;
|
||||||
|
positionMap.insert({PositionKey(it.first), it.second});
|
||||||
|
}
|
||||||
|
clothMesh->vertexSources.resize(clothMesh->vertices.size(), defaultSource);
|
||||||
|
for (size_t i = 0; i < clothMesh->vertices.size(); ++i) {
|
||||||
|
auto findSource = positionMap.find(PositionKey(clothMesh->vertices[i]));
|
||||||
|
if (findSource == positionMap.end())
|
||||||
|
continue;
|
||||||
|
clothMesh->vertexSources[i] = findSource->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<QVector3D> &filteredClothVertices = clothMesh->vertices;
|
||||||
|
std::vector<QVector3D> externalForces;
|
||||||
|
const auto &clothForce = clothMesh->clothForce;
|
||||||
|
float clothOffset = 0.015f + (clothMesh->clothOffset * 0.05f);
|
||||||
|
if (ClothForce::Centripetal == clothForce) {
|
||||||
|
externalForces.resize(filteredClothVertices.size());
|
||||||
|
for (size_t i = 0; i < filteredClothFaces.size(); ++i) {
|
||||||
|
const auto &face = filteredClothFaces[i];
|
||||||
|
auto faceForceDirection = -polygonNormal(filteredClothVertices, face);
|
||||||
|
for (const auto &vertex: face)
|
||||||
|
externalForces[vertex] += faceForceDirection;
|
||||||
|
}
|
||||||
|
for (auto &it: externalForces)
|
||||||
|
it = (it.normalized() + QVector3D(0.0f, -1.0f, 0.0f)).normalized();
|
||||||
|
} else {
|
||||||
|
externalForces.resize(filteredClothVertices.size(), QVector3D(0.0f, -1.0f, 0.0f));
|
||||||
|
}
|
||||||
|
ClothSimulator clothSimulator(filteredClothVertices,
|
||||||
|
filteredClothFaces,
|
||||||
|
*m_clothCollisionVertices,
|
||||||
|
*m_clothCollisionTriangles,
|
||||||
|
externalForces);
|
||||||
|
clothSimulator.setStiffness(clothMesh->clothStiffness);
|
||||||
|
clothSimulator.create();
|
||||||
|
for (size_t i = 0; i < 350; ++i)
|
||||||
|
clothSimulator.step();
|
||||||
|
clothSimulator.getCurrentVertices(&filteredClothVertices);
|
||||||
|
for (size_t i = 0; i < filteredClothVertices.size(); ++i) {
|
||||||
|
filteredClothVertices[i] -= externalForces[i] * clothOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void operator()(const tbb::blocked_range<size_t> &range) const
|
||||||
|
{
|
||||||
|
for (size_t i = range.begin(); i != range.end(); ++i) {
|
||||||
|
simulate(&(*m_clothMeshes)[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::vector<ClothMesh> *m_clothMeshes = nullptr;
|
||||||
|
const std::vector<QVector3D> *m_clothCollisionVertices = nullptr;
|
||||||
|
const std::vector<std::vector<size_t>> *m_clothCollisionTriangles = nullptr;
|
||||||
|
const std::vector<std::pair<QVector3D, float>> *m_clothTargetNodes = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
void simulateClothMeshes(std::vector<ClothMesh> *clothMeshes,
|
||||||
|
const std::vector<QVector3D> *clothCollisionVertices,
|
||||||
|
const std::vector<std::vector<size_t>> *clothCollisionTriangles,
|
||||||
|
const std::vector<std::pair<QVector3D, float>> *clothTargetNodes)
|
||||||
|
{
|
||||||
|
tbb::parallel_for(tbb::blocked_range<size_t>(0, clothMeshes->size()),
|
||||||
|
ClothMeshesSimulator(clothMeshes,
|
||||||
|
clothCollisionVertices,
|
||||||
|
clothCollisionTriangles,
|
||||||
|
clothTargetNodes));
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef DUST3D_SIMULATE_CLOTH_MESHES_H
|
||||||
|
#define DUST3D_SIMULATE_CLOTH_MESHES_H
|
||||||
|
#include <QVector3D>
|
||||||
|
#include <vector>
|
||||||
|
#include <QUuid>
|
||||||
|
#include "clothforce.h"
|
||||||
|
|
||||||
|
struct ClothMesh
|
||||||
|
{
|
||||||
|
std::vector<QVector3D> vertices;
|
||||||
|
std::vector<std::vector<size_t>> faces;
|
||||||
|
std::vector<std::pair<QUuid, QUuid>> vertexSources;
|
||||||
|
const std::vector<std::pair<QVector3D, std::pair<QUuid, QUuid>>> *outcomeNodeVertices;
|
||||||
|
ClothForce clothForce;
|
||||||
|
float clothOffset;
|
||||||
|
float clothStiffness;
|
||||||
|
};
|
||||||
|
|
||||||
|
void simulateClothMeshes(std::vector<ClothMesh> *clothMeshes,
|
||||||
|
const std::vector<QVector3D> *clothCollisionVertices,
|
||||||
|
const std::vector<std::vector<size_t>> *clothCollisionTriangles,
|
||||||
|
const std::vector<std::pair<QVector3D, float>> *clothTargetNodes);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue