|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "../include/NvFlexExt.h" |
|
|
|
|
|
#include "../core/cloth.h" |
|
|
|
|
|
namespace |
|
|
{ |
|
|
struct Key |
|
|
{ |
|
|
Key(int i, float d) : index(i), depth(d) {} |
|
|
|
|
|
int index; |
|
|
float depth; |
|
|
|
|
|
bool operator < (const Key& rhs) const { return depth < rhs.depth; } |
|
|
}; |
|
|
} |
|
|
|
|
|
int NvFlexExtCreateWeldedMeshIndices(const float* vertices, int numVertices, int* uniqueIndices, int* originalToUniqueMap, float threshold) |
|
|
{ |
|
|
memset(originalToUniqueMap, -1, numVertices*sizeof(int)); |
|
|
|
|
|
const Vec3* positions = (const Vec3*)vertices; |
|
|
|
|
|
|
|
|
std::vector<Key> keys; |
|
|
for (int i=0; i < numVertices; i++) |
|
|
keys.push_back(Key(i, positions[i].z)); |
|
|
|
|
|
std::sort(keys.begin(), keys.end()); |
|
|
|
|
|
int uniqueCount = 0; |
|
|
|
|
|
|
|
|
for (int i=0; i < numVertices; ++i) |
|
|
{ |
|
|
|
|
|
if (originalToUniqueMap[keys[i].index] != -1) |
|
|
continue; |
|
|
|
|
|
|
|
|
for (int j=i+1; j < numVertices && (keys[j].depth-keys[i].depth) <= threshold; ++j) |
|
|
{ |
|
|
float distance = Length(Vector3(positions[keys[i].index])-Vector3(positions[keys[j].index])); |
|
|
|
|
|
if (distance <= threshold) |
|
|
originalToUniqueMap[keys[j].index] = uniqueCount; |
|
|
} |
|
|
|
|
|
originalToUniqueMap[keys[i].index] = uniqueCount; |
|
|
|
|
|
uniqueIndices[uniqueCount++] = keys[i].index; |
|
|
} |
|
|
|
|
|
return uniqueCount; |
|
|
} |
|
|
|
|
|
NvFlexExtAsset* NvFlexExtCreateClothFromMesh(const float* particles, int numVertices, const int* indices, int numTriangles, float stretchStiffness, float bendStiffness, float tetherStiffness, float tetherGive, float pressure) |
|
|
{ |
|
|
NvFlexExtAsset* asset = new NvFlexExtAsset(); |
|
|
memset(asset, 0, sizeof(*asset)); |
|
|
|
|
|
asset->particles = new float[numVertices*4]; |
|
|
memcpy(asset->particles, particles, numVertices*sizeof(float)*4); |
|
|
|
|
|
asset->triangleIndices = new int[numTriangles*3]; |
|
|
memcpy(asset->triangleIndices, indices, numTriangles*3*sizeof(int)); |
|
|
|
|
|
asset->numParticles = numVertices; |
|
|
asset->maxParticles = numVertices; |
|
|
|
|
|
asset->numTriangles = numTriangles; |
|
|
|
|
|
|
|
|
ClothMesh cloth((Vec4*)particles, numVertices, indices, numTriangles*3, stretchStiffness, bendStiffness, true); |
|
|
|
|
|
if (cloth.mValid) |
|
|
{ |
|
|
|
|
|
if (tetherStiffness > 0.0f) |
|
|
{ |
|
|
std::vector<int> anchors; |
|
|
anchors.reserve(numVertices); |
|
|
|
|
|
|
|
|
for (int i=0; i < numVertices; ++i) |
|
|
{ |
|
|
Vec4& particle = ((Vec4*)particles)[i]; |
|
|
|
|
|
if (particle.w == 0.0f) |
|
|
anchors.push_back(i); |
|
|
} |
|
|
|
|
|
if (anchors.size()) |
|
|
{ |
|
|
|
|
|
for (int i=0; i < numVertices; ++i) |
|
|
{ |
|
|
Vec4& particle = ((Vec4*)particles)[i]; |
|
|
if (particle.w == 0.0f) |
|
|
continue; |
|
|
|
|
|
float minSqrDist = FLT_MAX; |
|
|
int minIndex = -1; |
|
|
|
|
|
|
|
|
for (int a=0; a < int(anchors.size()); ++a) |
|
|
{ |
|
|
Vec4& attachment = ((Vec4*)particles)[anchors[a]]; |
|
|
|
|
|
float distSqr = LengthSq(Vec3(particle)-Vec3(attachment)); |
|
|
if (distSqr < minSqrDist) |
|
|
{ |
|
|
minSqrDist = distSqr; |
|
|
minIndex = anchors[a]; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (minIndex != -1) |
|
|
{ |
|
|
cloth.mConstraintIndices.push_back(i); |
|
|
cloth.mConstraintIndices.push_back(minIndex); |
|
|
cloth.mConstraintRestLengths.push_back(sqrtf(minSqrDist)*(1.0f + tetherGive)); |
|
|
|
|
|
|
|
|
cloth.mConstraintCoefficients.push_back(-tetherStiffness); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
const int numSprings = int(cloth.mConstraintCoefficients.size()); |
|
|
|
|
|
asset->springIndices = new int[numSprings*2]; |
|
|
asset->springCoefficients = new float[numSprings]; |
|
|
asset->springRestLengths = new float[numSprings]; |
|
|
asset->numSprings = numSprings; |
|
|
|
|
|
for (int i=0; i < numSprings; ++i) |
|
|
{ |
|
|
asset->springIndices[i*2+0] = cloth.mConstraintIndices[i*2+0]; |
|
|
asset->springIndices[i*2+1] = cloth.mConstraintIndices[i*2+1]; |
|
|
asset->springRestLengths[i] = cloth.mConstraintRestLengths[i]; |
|
|
asset->springCoefficients[i] = cloth.mConstraintCoefficients[i]; |
|
|
} |
|
|
|
|
|
if (pressure > 0.0f) |
|
|
{ |
|
|
asset->inflatable = true; |
|
|
asset->inflatableVolume = cloth.mRestVolume; |
|
|
asset->inflatableStiffness = cloth.mConstraintScale; |
|
|
asset->inflatablePressure = pressure; |
|
|
} |
|
|
} |
|
|
else |
|
|
{ |
|
|
NvFlexExtDestroyAsset(asset); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
return asset; |
|
|
} |
|
|
|
|
|
struct FlexExtTearingClothAsset : public NvFlexExtAsset |
|
|
{ |
|
|
ClothMesh* mMesh; |
|
|
}; |
|
|
|
|
|
NvFlexExtAsset* NvFlexExtCreateTearingClothFromMesh(const float* particles, int numParticles, int maxParticles, const int* indices, int numTriangles, float stretchStiffness, float bendStiffness, float pressure) |
|
|
{ |
|
|
FlexExtTearingClothAsset* asset = new FlexExtTearingClothAsset(); |
|
|
memset(asset, 0, sizeof(*asset)); |
|
|
|
|
|
asset->particles = new float[maxParticles*4]; |
|
|
memcpy(asset->particles, particles, numParticles*sizeof(float)*4); |
|
|
|
|
|
asset->triangleIndices = new int[numTriangles*3]; |
|
|
memcpy(asset->triangleIndices, indices, numTriangles*3*sizeof(int)); |
|
|
|
|
|
asset->numParticles = numParticles; |
|
|
asset->maxParticles = maxParticles; |
|
|
|
|
|
asset->numTriangles = numTriangles; |
|
|
|
|
|
|
|
|
asset->mMesh = new ClothMesh((Vec4*)particles, numParticles, indices, numTriangles*3, stretchStiffness, bendStiffness, true); |
|
|
|
|
|
ClothMesh& cloth = *asset->mMesh; |
|
|
|
|
|
if (cloth.mValid) |
|
|
{ |
|
|
const int numSprings = int(cloth.mConstraintCoefficients.size()); |
|
|
|
|
|
|
|
|
asset->springIndices = &cloth.mConstraintIndices[0]; |
|
|
asset->springCoefficients = &cloth.mConstraintCoefficients[0]; |
|
|
asset->springRestLengths = &cloth.mConstraintRestLengths[0]; |
|
|
asset->numSprings = numSprings; |
|
|
|
|
|
if (pressure > 0.0f) |
|
|
{ |
|
|
asset->inflatable = true; |
|
|
asset->inflatableVolume = cloth.mRestVolume; |
|
|
asset->inflatableStiffness = cloth.mConstraintScale; |
|
|
asset->inflatablePressure = pressure; |
|
|
} |
|
|
} |
|
|
else |
|
|
{ |
|
|
NvFlexExtDestroyAsset(asset); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
return asset; |
|
|
|
|
|
} |
|
|
|
|
|
void NvFlexExtDestroyTearingCloth(NvFlexExtAsset* asset) |
|
|
{ |
|
|
FlexExtTearingClothAsset* tearable = (FlexExtTearingClothAsset*)asset; |
|
|
|
|
|
delete[] asset->particles; |
|
|
delete[] asset->triangleIndices; |
|
|
|
|
|
delete tearable->mMesh; |
|
|
delete tearable; |
|
|
} |
|
|
|
|
|
void NvFlexExtTearClothMesh(NvFlexExtAsset* asset, float maxStrain, int maxSplits, NvFlexExtTearingParticleClone* particleCopies, int* numParticleCopies, int maxCopies, NvFlexExtTearingMeshEdit* triangleEdits, int* numTriangleEdits, int maxEdits) |
|
|
{ |
|
|
FlexExtTearingClothAsset* tearable = (FlexExtTearingClothAsset*)asset; |
|
|
|
|
|
std::vector<ClothMesh::TriangleUpdate> edits; |
|
|
std::vector<ClothMesh::VertexCopy> copies; |
|
|
|
|
|
int splits = 0; |
|
|
|
|
|
maxCopies = Min(maxCopies, tearable->maxParticles-tearable->numParticles); |
|
|
|
|
|
|
|
|
for (int i=0; i < tearable->numSprings && int(copies.size()) < maxCopies && splits < maxSplits; ++i) |
|
|
{ |
|
|
int a = tearable->springIndices[i*2+0]; |
|
|
int b = tearable->springIndices[i*2+1]; |
|
|
|
|
|
Vec3 p = Vec3(&tearable->particles[a*4]); |
|
|
Vec3 q = Vec3(&tearable->particles[b*4]); |
|
|
|
|
|
|
|
|
if (Length(p-q) > tearable->springRestLengths[i]*maxStrain) |
|
|
{ |
|
|
|
|
|
if (Vec4(&tearable->particles[a*4]).w == 0.0f) |
|
|
continue; |
|
|
|
|
|
if (Vec4(&tearable->particles[b*4]).w == 0.0f) |
|
|
continue; |
|
|
|
|
|
|
|
|
const int splitIndex = Randf() > 0.5f ? a : b; |
|
|
const Vec3 splitPlane = Normalize(p-q); |
|
|
|
|
|
std::vector<int> adjacentTriangles; |
|
|
std::vector<int> adjacentVertices; |
|
|
|
|
|
const int newIndex = tearable->mMesh->SplitVertex((Vec4*)tearable->particles, splitIndex, splitPlane, adjacentTriangles, adjacentVertices, edits, copies, maxCopies-int(copies.size())); |
|
|
|
|
|
if (newIndex != -1) |
|
|
{ |
|
|
++splits; |
|
|
|
|
|
|
|
|
for (int s=0; s < int(adjacentVertices.size()); ++s) |
|
|
{ |
|
|
const int adjacentVertex = adjacentVertices[s]; |
|
|
|
|
|
tearable->mMesh->SeparateVertex(adjacentVertex, edits, copies, maxCopies-int(copies.size())); |
|
|
} |
|
|
|
|
|
|
|
|
tearable->mMesh->SeparateVertex(newIndex, edits, copies, maxCopies-int(copies.size())); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
tearable->numParticles = tearable->mMesh->mNumVertices; |
|
|
|
|
|
|
|
|
for (int c=0; c < int(copies.size()); ++c) |
|
|
{ |
|
|
NvFlexExtTearingParticleClone clone; |
|
|
clone.srcIndex = copies[c].srcIndex; |
|
|
clone.destIndex = copies[c].destIndex; |
|
|
|
|
|
particleCopies[c] = clone; |
|
|
} |
|
|
|
|
|
|
|
|
const int numEdits = Min(int(edits.size()), maxEdits); |
|
|
|
|
|
for (int u=0; u < numEdits; ++u) |
|
|
{ |
|
|
NvFlexExtTearingMeshEdit update; |
|
|
update.triIndex = edits[u].triangle; |
|
|
update.newParticleIndex = edits[u].vertex; |
|
|
|
|
|
tearable->triangleIndices[update.triIndex] = update.newParticleIndex; |
|
|
|
|
|
triangleEdits[u] = update; |
|
|
} |
|
|
|
|
|
|
|
|
*numTriangleEdits = numEdits; |
|
|
*numParticleCopies = int(copies.size()); |
|
|
} |
|
|
|