Optimize cloth mesh simulation by enable tbb.
parent
cff5cff6bc
commit
5a59abdca4
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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