|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "../include/NvFlexExt.h" |
|
|
|
|
|
#include "../core/core.h" |
|
|
#include "../core/maths.h" |
|
|
#include "../core/voxelize.h" |
|
|
|
|
|
#include <vector> |
|
|
#include <algorithm> |
|
|
|
|
|
using namespace std; |
|
|
|
|
|
|
|
|
|
|
|
namespace |
|
|
{ |
|
|
|
|
|
Vec3 CalculateMean(const Vec3* particles, const int* indices, int numIndices) |
|
|
{ |
|
|
Vec3 sum; |
|
|
|
|
|
for (int i = 0; i < numIndices; ++i) |
|
|
sum += Vec3(particles[indices[i]]); |
|
|
|
|
|
if (numIndices) |
|
|
return sum / float(numIndices); |
|
|
else |
|
|
return sum; |
|
|
} |
|
|
|
|
|
float CalculateRadius(const Vec3* particles, Vec3 center, const int* indices, int numIndices) |
|
|
{ |
|
|
float radiusSq = 0.0f; |
|
|
|
|
|
for (int i = 0; i < numIndices; ++i) |
|
|
{ |
|
|
float dSq = LengthSq(Vec3(particles[indices[i]]) - center); |
|
|
if (dSq > radiusSq) |
|
|
radiusSq = dSq; |
|
|
} |
|
|
|
|
|
return sqrtf(radiusSq); |
|
|
} |
|
|
|
|
|
struct Cluster |
|
|
{ |
|
|
Vec3 mean; |
|
|
float radius; |
|
|
|
|
|
|
|
|
std::vector<int> indices; |
|
|
}; |
|
|
|
|
|
struct Seed |
|
|
{ |
|
|
int index; |
|
|
float priority; |
|
|
|
|
|
bool operator < (const Seed& rhs) const |
|
|
{ |
|
|
return priority < rhs.priority; |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
struct SweepAndPrune |
|
|
{ |
|
|
struct Entry |
|
|
{ |
|
|
Entry(Vec3 p, int i) : point(p), index(i) {} |
|
|
|
|
|
Vec3 point; |
|
|
int index; |
|
|
}; |
|
|
|
|
|
SweepAndPrune(const Vec3* points, int n) |
|
|
{ |
|
|
entries.reserve(n); |
|
|
for (int i=0; i < n; ++i) |
|
|
entries.push_back(Entry(points[i], i)); |
|
|
|
|
|
struct SortOnAxis |
|
|
{ |
|
|
int axis; |
|
|
|
|
|
SortOnAxis(int axis) : axis(axis) {} |
|
|
|
|
|
bool operator()(const Entry& lhs, const Entry& rhs) const |
|
|
{ |
|
|
return lhs.point[axis] < rhs.point[axis]; |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
Vec3 lower(FLT_MAX), upper(-FLT_MAX); |
|
|
for (int i=0; i < n; ++i) |
|
|
{ |
|
|
lower = Min(points[i], lower); |
|
|
upper = Max(points[i], upper); |
|
|
} |
|
|
|
|
|
Vec3 edges = upper-lower; |
|
|
|
|
|
if (edges.x > edges.y && edges.x > edges.z) |
|
|
longestAxis = 0; |
|
|
else if (edges.y > edges.z) |
|
|
longestAxis = 1; |
|
|
else |
|
|
longestAxis = 2; |
|
|
|
|
|
std::sort(entries.begin(), entries.end(), SortOnAxis(longestAxis)); |
|
|
} |
|
|
|
|
|
void QuerySphere(Vec3 center, float radius, std::vector<int>& indices) |
|
|
{ |
|
|
|
|
|
int low = 0; |
|
|
int high = int(entries.size()); |
|
|
|
|
|
|
|
|
float queryLower = center[longestAxis] - radius; |
|
|
float queryUpper = center[longestAxis] + radius; |
|
|
|
|
|
|
|
|
while (low < high) |
|
|
{ |
|
|
const int mid = (high+low)/2; |
|
|
|
|
|
if (queryLower > entries[mid].point[longestAxis]) |
|
|
low = mid+1; |
|
|
else |
|
|
high = mid; |
|
|
} |
|
|
|
|
|
|
|
|
float radiusSq = radius*radius; |
|
|
|
|
|
for (int i=low; i < int(entries.size()); ++i) |
|
|
{ |
|
|
Vec3 p = entries[i].point; |
|
|
|
|
|
if (LengthSq(p-center) < radiusSq) |
|
|
{ |
|
|
indices.push_back(entries[i].index); |
|
|
} |
|
|
else if (entries[i].point[longestAxis] > queryUpper) |
|
|
{ |
|
|
|
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
int longestAxis; |
|
|
|
|
|
std::vector<Entry> entries; |
|
|
}; |
|
|
|
|
|
int CreateClusters(Vec3* particles, const float* priority, int numParticles, std::vector<int>& outClusterOffsets, std::vector<int>& outClusterIndices, std::vector<Vec3>& outClusterPositions, float radius, float smoothing = 0.0f) |
|
|
{ |
|
|
std::vector<Seed> seeds; |
|
|
std::vector<Cluster> clusters; |
|
|
|
|
|
|
|
|
std::vector<bool> used(numParticles, false); |
|
|
|
|
|
|
|
|
for (int i = 0; i < numParticles; ++i) |
|
|
{ |
|
|
Seed s; |
|
|
s.index = i; |
|
|
s.priority = priority[i]; |
|
|
|
|
|
seeds.push_back(s); |
|
|
} |
|
|
|
|
|
|
|
|
std::stable_sort(seeds.begin(), seeds.end()); |
|
|
|
|
|
SweepAndPrune sap(particles, numParticles); |
|
|
|
|
|
while (seeds.size()) |
|
|
{ |
|
|
|
|
|
Seed seed = seeds.back(); |
|
|
seeds.pop_back(); |
|
|
|
|
|
if (!used[seed.index]) |
|
|
{ |
|
|
Cluster c; |
|
|
|
|
|
sap.QuerySphere(Vec3(particles[seed.index]), radius, c.indices); |
|
|
|
|
|
|
|
|
for (int i=0; i < int(c.indices.size()); ++i) |
|
|
used[c.indices[i]] = true; |
|
|
|
|
|
c.mean = CalculateMean(particles, &c.indices[0], int(c.indices.size())); |
|
|
|
|
|
clusters.push_back(c); |
|
|
} |
|
|
} |
|
|
|
|
|
if (smoothing > 0.0f) |
|
|
{ |
|
|
for (int i = 0; i < int(clusters.size()); ++i) |
|
|
{ |
|
|
Cluster& c = clusters[i]; |
|
|
|
|
|
|
|
|
c.indices.resize(0); |
|
|
|
|
|
|
|
|
sap.QuerySphere(c.mean, smoothing, c.indices); |
|
|
|
|
|
c.mean = CalculateMean(particles, &c.indices[0], int(c.indices.size())); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
int count = 0; |
|
|
|
|
|
for (int c = 0; c < int(clusters.size()); ++c) |
|
|
{ |
|
|
const Cluster& cluster = clusters[c]; |
|
|
|
|
|
const int clusterSize = int(cluster.indices.size()); |
|
|
|
|
|
|
|
|
if (clusterSize) |
|
|
{ |
|
|
|
|
|
for (int i = 0; i < clusterSize; ++i) |
|
|
outClusterIndices.push_back(cluster.indices[i]); |
|
|
|
|
|
|
|
|
outClusterOffsets.push_back(int(outClusterIndices.size())); |
|
|
|
|
|
|
|
|
outClusterPositions.push_back(cluster.mean); |
|
|
|
|
|
++count; |
|
|
} |
|
|
} |
|
|
|
|
|
return count; |
|
|
} |
|
|
|
|
|
|
|
|
int CreateLinks(const Vec3* particles, int numParticles, std::vector<int>& outSpringIndices, std::vector<float>& outSpringLengths, std::vector<float>& outSpringStiffness, float radius, float stiffness = 1.0f) |
|
|
{ |
|
|
int count = 0; |
|
|
|
|
|
std::vector<int> neighbors; |
|
|
SweepAndPrune sap(particles, numParticles); |
|
|
|
|
|
for (int i = 0; i < numParticles; ++i) |
|
|
{ |
|
|
neighbors.resize(0); |
|
|
|
|
|
sap.QuerySphere(Vec3(particles[i]), radius, neighbors); |
|
|
|
|
|
for (int j = 0; j < int(neighbors.size()); ++j) |
|
|
{ |
|
|
const int nj = neighbors[j]; |
|
|
|
|
|
if (nj != i) |
|
|
{ |
|
|
outSpringIndices.push_back(i); |
|
|
outSpringIndices.push_back(nj); |
|
|
outSpringLengths.push_back(Length(Vec3(particles[i]) - Vec3(particles[nj]))); |
|
|
outSpringStiffness.push_back(stiffness); |
|
|
|
|
|
++count; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return count; |
|
|
} |
|
|
|
|
|
void CreateSkinning(const Vec3* vertices, int numVertices, const Vec3* clusters, int numClusters, float* outWeights, int* outIndices, float falloff, float maxdist) |
|
|
{ |
|
|
const int maxBones = 4; |
|
|
|
|
|
SweepAndPrune sap(clusters, numClusters); |
|
|
|
|
|
std::vector<int> influences; |
|
|
|
|
|
|
|
|
for (int i = 0; i < numVertices; ++i) |
|
|
{ |
|
|
int indices[4] = { -1, -1, -1, -1 }; |
|
|
float distances[4] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }; |
|
|
float weights[maxBones]; |
|
|
|
|
|
influences.resize(0); |
|
|
sap.QuerySphere(vertices[i], maxdist, influences); |
|
|
|
|
|
for (int c = 0; c < int(influences.size()); ++c) |
|
|
{ |
|
|
float dSq = LengthSq(vertices[i] - clusters[influences[c]]); |
|
|
|
|
|
|
|
|
int w = 0; |
|
|
for (; w < maxBones; ++w) |
|
|
if (dSq < distances[w]) |
|
|
break; |
|
|
|
|
|
if (w < maxBones) |
|
|
{ |
|
|
|
|
|
for (int s = maxBones - 1; s > w; --s) |
|
|
{ |
|
|
indices[s] = indices[s - 1]; |
|
|
distances[s] = distances[s - 1]; |
|
|
} |
|
|
|
|
|
distances[w] = dSq; |
|
|
indices[w] = influences[c]; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
float wSum = 0.0f; |
|
|
|
|
|
for (int w = 0; w < maxBones; ++w) |
|
|
{ |
|
|
if (distances[w] > Sqr(maxdist)) |
|
|
{ |
|
|
|
|
|
weights[w] = 0.0f; |
|
|
} |
|
|
else |
|
|
{ |
|
|
|
|
|
weights[w] = 1.0f / (powf(distances[w], falloff) + 0.0001f); |
|
|
} |
|
|
|
|
|
wSum += weights[w]; |
|
|
} |
|
|
|
|
|
if (wSum == 0.0f) |
|
|
{ |
|
|
|
|
|
|
|
|
weights[0] = 1.0f; |
|
|
} |
|
|
else |
|
|
{ |
|
|
|
|
|
for (int w = 0; w < maxBones; ++w) |
|
|
{ |
|
|
weights[w] = weights[w] / wSum; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
for (int j = 0; j < maxBones; ++j) |
|
|
{ |
|
|
outWeights[i*maxBones + j] = weights[j]; |
|
|
outIndices[i*maxBones + j] = indices[j]; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void SampleMesh(const Vec3* vertices, int numVertices, const int* indices, int numIndices, float radius, float volumeSampling, float surfaceSampling, std::vector<Vec3>& outPositions) |
|
|
{ |
|
|
Vec3 meshLower(FLT_MAX); |
|
|
Vec3 meshUpper(-FLT_MAX); |
|
|
|
|
|
for (int i = 0; i < numVertices; ++i) |
|
|
{ |
|
|
meshLower = Min(meshLower, vertices[i]); |
|
|
meshUpper = Max(meshUpper, vertices[i]); |
|
|
} |
|
|
|
|
|
std::vector<Vec3> samples; |
|
|
|
|
|
if (volumeSampling > 0.0f) |
|
|
{ |
|
|
|
|
|
Vec3 edges = meshUpper - meshLower; |
|
|
|
|
|
|
|
|
float spacing = radius / volumeSampling; |
|
|
|
|
|
|
|
|
|
|
|
float spacingEps = spacing*(1.0f - 1e-4f); |
|
|
|
|
|
|
|
|
int dx, dy, dz; |
|
|
dx = spacing > edges.x ? 1 : int(edges.x / spacingEps); |
|
|
dy = spacing > edges.y ? 1 : int(edges.y / spacingEps); |
|
|
dz = spacing > edges.z ? 1 : int(edges.z / spacingEps); |
|
|
|
|
|
int maxDim = max(max(dx, dy), dz); |
|
|
|
|
|
|
|
|
meshLower -= 2.0f*Vec3(spacing); |
|
|
meshUpper += 2.0f*Vec3(spacing); |
|
|
maxDim += 4; |
|
|
|
|
|
vector<uint32_t> voxels(maxDim*maxDim*maxDim); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Vec3 meshOffset; |
|
|
meshOffset.x = 0.5f * (spacing - (edges.x - (dx - 1)*spacing)); |
|
|
meshOffset.y = 0.5f * (spacing - (edges.y - (dy - 1)*spacing)); |
|
|
meshOffset.z = 0.5f * (spacing - (edges.z - (dz - 1)*spacing)); |
|
|
meshLower -= meshOffset; |
|
|
|
|
|
Voxelize(vertices, numVertices, indices, numIndices, maxDim, maxDim, maxDim, &voxels[0], meshLower, meshLower + Vec3(maxDim*spacing)); |
|
|
|
|
|
|
|
|
for (int x = 0; x < maxDim; ++x) |
|
|
{ |
|
|
for (int y = 0; y < maxDim; ++y) |
|
|
{ |
|
|
for (int z = 0; z < maxDim; ++z) |
|
|
{ |
|
|
const int index = z*maxDim*maxDim + y*maxDim + x; |
|
|
|
|
|
|
|
|
if (voxels[index]) |
|
|
{ |
|
|
Vec3 position = meshLower + spacing*Vec3(float(x) + 0.5f, float(y) + 0.5f, float(z) + 0.5f); |
|
|
|
|
|
|
|
|
samples.push_back(position); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if (surfaceSampling > 0.0f) |
|
|
{ |
|
|
|
|
|
for (int i = 0; i < numVertices; ++i) |
|
|
samples.push_back(vertices[i]); |
|
|
|
|
|
|
|
|
const int numSamples = int(50000 * surfaceSampling); |
|
|
const int numTriangles = numIndices/3; |
|
|
|
|
|
RandInit(); |
|
|
|
|
|
for (int i = 0; i < numSamples; ++i) |
|
|
{ |
|
|
int t = Rand() % numTriangles; |
|
|
float u = Randf(); |
|
|
float v = Randf()*(1.0f - u); |
|
|
float w = 1.0f - u - v; |
|
|
|
|
|
int a = indices[t*3 + 0]; |
|
|
int b = indices[t*3 + 1]; |
|
|
int c = indices[t*3 + 2]; |
|
|
|
|
|
Vec3 p = vertices[a] * u + vertices[b] * v + vertices[c] * w; |
|
|
|
|
|
samples.push_back(p); |
|
|
} |
|
|
} |
|
|
|
|
|
std::vector<int> clusterIndices; |
|
|
std::vector<int> clusterOffsets; |
|
|
std::vector<Vec3> clusterPositions; |
|
|
std::vector<float> priority(samples.size()); |
|
|
|
|
|
|
|
|
CreateClusters(&samples[0], &priority[0], int(samples.size()), clusterOffsets, clusterIndices, outPositions, radius); |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
NvFlexExtAsset* NvFlexExtCreateSoftFromMesh(const float* vertices, int numVertices, const int* indices, int numIndices, float particleSpacing, float volumeSampling, float surfaceSampling, float clusterSpacing, float clusterRadius, float clusterStiffness, float linkRadius, float linkStiffness, float globalStiffness, float clusterPlasticThreshold, float clusterPlasticCreep) |
|
|
{ |
|
|
|
|
|
|
|
|
Vec3 meshOffset(0.0f); |
|
|
for (int i = 0; i < numVertices; i++) |
|
|
{ |
|
|
meshOffset += ((Vec3*)vertices)[i]; |
|
|
} |
|
|
meshOffset /= float(numVertices); |
|
|
|
|
|
Vec3* relativeVertices = new Vec3[numVertices]; |
|
|
for (int i = 0; i < numVertices; i++) |
|
|
{ |
|
|
relativeVertices[i] += ((Vec3*)vertices)[i] - meshOffset; |
|
|
} |
|
|
|
|
|
|
|
|
NvFlexExtAsset* asset = new NvFlexExtAsset(); |
|
|
|
|
|
|
|
|
std::vector<Vec3> samples; |
|
|
SampleMesh(relativeVertices, numVertices, indices, numIndices, particleSpacing, volumeSampling, surfaceSampling, samples); |
|
|
|
|
|
delete[] relativeVertices; |
|
|
|
|
|
const int numParticles = int(samples.size()); |
|
|
|
|
|
std::vector<int> clusterIndices; |
|
|
std::vector<int> clusterOffsets; |
|
|
std::vector<Vec3> clusterPositions; |
|
|
std::vector<float> clusterCoefficients; |
|
|
std::vector<float> clusterPlasticThresholds; |
|
|
std::vector<float> clusterPlasticCreeps; |
|
|
|
|
|
|
|
|
std::vector<float> priority(numParticles); |
|
|
for (int i = 0; i < int(priority.size()); ++i) |
|
|
priority[i] = 0.0f; |
|
|
|
|
|
|
|
|
int numClusters = CreateClusters(&samples[0], &priority[0], int(samples.size()), clusterOffsets, clusterIndices, clusterPositions, clusterSpacing, clusterRadius); |
|
|
|
|
|
|
|
|
clusterCoefficients.resize(numClusters, clusterStiffness); |
|
|
|
|
|
if (clusterPlasticCreep) |
|
|
{ |
|
|
|
|
|
clusterPlasticThresholds.resize(numClusters, clusterPlasticThreshold); |
|
|
|
|
|
|
|
|
clusterPlasticCreeps.resize(numClusters, clusterPlasticCreep); |
|
|
} |
|
|
|
|
|
|
|
|
if (linkRadius > 0.0f) |
|
|
{ |
|
|
std::vector<int> springIndices; |
|
|
std::vector<float> springLengths; |
|
|
std::vector<float> springStiffness; |
|
|
|
|
|
|
|
|
int numLinks = CreateLinks(&samples[0], int(samples.size()), springIndices, springLengths, springStiffness, linkRadius, linkStiffness); |
|
|
|
|
|
|
|
|
if (numLinks) |
|
|
{ |
|
|
asset->springIndices = new int[numLinks * 2]; |
|
|
memcpy(asset->springIndices, &springIndices[0], sizeof(int)*springIndices.size()); |
|
|
|
|
|
asset->springCoefficients = new float[numLinks]; |
|
|
memcpy(asset->springCoefficients, &springStiffness[0], sizeof(float)*numLinks); |
|
|
|
|
|
asset->springRestLengths = new float[numLinks]; |
|
|
memcpy(asset->springRestLengths, &springLengths[0], sizeof(float)*numLinks); |
|
|
|
|
|
asset->numSprings = numLinks; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (globalStiffness > 0.0f) |
|
|
{ |
|
|
numClusters += 1; |
|
|
clusterCoefficients.push_back(globalStiffness); |
|
|
if (clusterPlasticCreep) |
|
|
{ |
|
|
clusterPlasticThresholds.push_back(clusterPlasticThreshold); |
|
|
clusterPlasticCreeps.push_back(clusterPlasticCreep); |
|
|
} |
|
|
|
|
|
for (int i = 0; i < numParticles; ++i) |
|
|
{ |
|
|
clusterIndices.push_back(i); |
|
|
} |
|
|
clusterOffsets.push_back((int)clusterIndices.size()); |
|
|
|
|
|
|
|
|
Vec3 globalMeanPosition(0.0f); |
|
|
for (int i = 0; i < numParticles; ++i) |
|
|
{ |
|
|
globalMeanPosition += samples[i]; |
|
|
} |
|
|
globalMeanPosition /= float(numParticles); |
|
|
clusterPositions.push_back(globalMeanPosition); |
|
|
} |
|
|
|
|
|
|
|
|
for (int i = 0; i < numParticles; ++i) |
|
|
{ |
|
|
samples[i] += meshOffset; |
|
|
} |
|
|
for (int i = 0; i < numClusters; ++i) |
|
|
{ |
|
|
clusterPositions[i] += meshOffset; |
|
|
} |
|
|
|
|
|
|
|
|
asset->particles = new float[numParticles * 4]; |
|
|
asset->numParticles = numParticles; |
|
|
asset->maxParticles = numParticles; |
|
|
|
|
|
for (int i = 0; i < numParticles; ++i) |
|
|
{ |
|
|
asset->particles[i*4+0] = samples[i].x; |
|
|
asset->particles[i*4+1] = samples[i].y; |
|
|
asset->particles[i*4+2] = samples[i].z; |
|
|
asset->particles[i*4+3] = 1.0f; |
|
|
} |
|
|
|
|
|
|
|
|
asset->shapeIndices = new int[clusterIndices.size()]; |
|
|
memcpy(asset->shapeIndices, &clusterIndices[0], sizeof(int)*clusterIndices.size()); |
|
|
|
|
|
asset->shapeOffsets = new int[numClusters]; |
|
|
memcpy(asset->shapeOffsets, &clusterOffsets[0], sizeof(int)*numClusters); |
|
|
|
|
|
asset->shapeCenters = new float[numClusters*3]; |
|
|
memcpy(asset->shapeCenters, &clusterPositions[0], sizeof(float)*numClusters*3); |
|
|
|
|
|
asset->shapeCoefficients = new float[numClusters]; |
|
|
memcpy(asset->shapeCoefficients, &clusterCoefficients[0], sizeof(float)*numClusters); |
|
|
|
|
|
if (clusterPlasticCreep) |
|
|
{ |
|
|
asset->shapePlasticThresholds = new float[numClusters]; |
|
|
memcpy(asset->shapePlasticThresholds, &clusterPlasticThresholds[0], sizeof(float)*numClusters); |
|
|
|
|
|
asset->shapePlasticCreeps = new float[numClusters]; |
|
|
memcpy(asset->shapePlasticCreeps, &clusterPlasticCreeps[0], sizeof(float)*numClusters); |
|
|
} |
|
|
else |
|
|
{ |
|
|
asset->shapePlasticThresholds = NULL; |
|
|
asset->shapePlasticCreeps = NULL; |
|
|
} |
|
|
|
|
|
asset->numShapeIndices = int(clusterIndices.size()); |
|
|
asset->numShapes = numClusters; |
|
|
|
|
|
return asset; |
|
|
} |
|
|
|
|
|
void NvFlexExtCreateSoftMeshSkinning(const float* vertices, int numVertices, const float* bones, int numBones, float falloff, float maxDistance, float* skinningWeights, int* skinningIndices) |
|
|
{ |
|
|
CreateSkinning((Vec3*)vertices, numVertices, (Vec3*)bones, numBones, skinningWeights, skinningIndices, falloff, maxDistance); |
|
|
} |
|
|
|