Optimize cloth mesh simulation by enable tbb.

master
Jeremy Hu 2020-01-13 22:54:09 +09:30
parent cff5cff6bc
commit 5a59abdca4
5 changed files with 182 additions and 84 deletions

View File

@ -497,6 +497,9 @@ HEADERS += src/clothforce.h
SOURCES += src/projectfacestonodes.cpp
HEADERS += src/projectfacestonodes.h
SOURCES += src/simulateclothmeshes.cpp
HEADERS += src/simulateclothmeshes.h
SOURCES += src/main.cpp
HEADERS += src/version.h

View File

@ -22,6 +22,7 @@
#include "isotropicremesh.h"
#include "projectfacestonodes.h"
#include "document.h"
#include "simulateclothmeshes.h"
MeshGenerator::MeshGenerator(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);
if (ComponentLayer::Cloth == componentLayer(component)) {
@ -1685,73 +1687,45 @@ void MeshGenerator::collectClothComponent(const QString &componentIdString)
if (nullptr == componentCache.mesh) {
return;
}
componentIdStrings->push_back(componentIdString);
return;
}
for (const auto &childIdString: valueOfKeyInMapOrEmpty(*component, "children").split(",")) {
if (childIdString.isEmpty())
continue;
collectClothComponentIdStrings(childIdString, componentIdStrings);
}
}
void MeshGenerator::collectClothComponent(const QString &componentIdString)
{
if (m_clothCollisionTriangles.empty() || m_clothTargetNodes.empty())
return;
std::vector<QVector3D> uncombinedVertices;
std::vector<std::vector<size_t>> uncombinedOriginalFaces;
std::vector<std::vector<size_t>> uncombinedFaces;
componentCache.mesh->fetch(uncombinedVertices, uncombinedOriginalFaces);
uncombinedFaces.reserve(uncombinedOriginalFaces.size());
for (const auto &it: uncombinedOriginalFaces) {
if (4 == it.size()) {
uncombinedFaces.push_back(std::vector<size_t> {
it[0], it[1], it[2]
});
uncombinedFaces.push_back(std::vector<size_t> {
it[2], it[3], it[0]
});
} 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<QString> componentIdStrings;
collectClothComponentIdStrings(componentIdString, &componentIdStrings);
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;
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;
}
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));
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());
}
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;
}
simulateClothMeshes(&clothMeshes,
&m_clothCollisionVertices,
&m_clothCollisionTriangles,
&m_clothTargetNodes);
for (auto &clothMesh: clothMeshes) {
auto vertexStartIndex = m_outcome->vertices.size();
auto updateVertexIndices = [=](std::vector<std::vector<size_t>> &faces) {
for (auto &it: faces) {
@ -1759,10 +1733,9 @@ void MeshGenerator::collectClothComponent(const QString &componentIdString)
subIt += vertexStartIndex;
}
};
updateVertexIndices(filteredClothFaces);
m_outcome->vertices.insert(m_outcome->vertices.end(), filteredClothVertices.begin(), filteredClothVertices.end());
for (const auto &it: filteredClothFaces) {
updateVertexIndices(clothMesh.faces);
m_outcome->vertices.insert(m_outcome->vertices.end(), clothMesh.vertices.begin(), clothMesh.vertices.end());
for (const auto &it: clothMesh.faces) {
if (4 == it.size()) {
m_outcome->triangles.push_back(std::vector<size_t> {
it[0], it[1], it[2]
@ -1774,20 +1747,11 @@ void MeshGenerator::collectClothComponent(const QString &componentIdString)
m_outcome->triangles.push_back(it);
}
}
m_outcome->triangleAndQuads.insert(m_outcome->triangleAndQuads.end(), filteredClothFaces.begin(), filteredClothFaces.end());
m_outcome->nodes.insert(m_outcome->nodes.end(), componentCache.outcomeNodes.begin(), componentCache.outcomeNodes.end());
Q_ASSERT(uncombinedVertices.size() == filteredClothVertices.size());
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));
m_outcome->triangleAndQuads.insert(m_outcome->triangleAndQuads.end(), clothMesh.faces.begin(), clothMesh.faces.end());
for (size_t i = 0; i < clothMesh.vertices.size(); ++i) {
const auto &source = clothMesh.vertexSources[i];
m_outcome->nodeVertices.push_back(std::make_pair(clothMesh.vertices[i], source));
}
return;
}
for (const auto &childIdString: valueOfKeyInMapOrEmpty(*component, "children").split(",")) {
if (childIdString.isEmpty())
continue;
collectClothComponent(childIdString);
}
}

View File

@ -138,6 +138,8 @@ private:
float componentClothOffset(const std::map<QString, QString> *component);
void collectUncombinedComponent(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 remesh(const std::vector<OutcomeNode> &inputNodes,
const std::vector<QVector3D> &inputVertices,

105
src/simulateclothmeshes.cpp Normal file
View File

@ -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));
}

24
src/simulateclothmeshes.h Normal file
View File

@ -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