| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #pragma once |
|
|
| #include <stdarg.h> |
|
|
| |
| #if _WIN32 |
| #pragma warning(disable: 4267) |
| #endif |
|
|
| float SampleSDF(const float* sdf, int dim, int x, int y, int z) |
| { |
| assert(x < dim && x >= 0); |
| assert(y < dim && y >= 0); |
| assert(z < dim && z >= 0); |
|
|
| return sdf[z*dim*dim + y*dim + x]; |
| } |
|
|
| |
| Vec3 SampleSDFGrad(const float* sdf, int dim, int x, int y, int z) |
| { |
| int x0 = max(x-1, 0); |
| int x1 = min(x+1, dim-1); |
|
|
| int y0 = max(y-1, 0); |
| int y1 = min(y+1, dim-1); |
|
|
| int z0 = max(z-1, 0); |
| int z1 = min(z+1, dim-1); |
|
|
| float dx = (SampleSDF(sdf, dim, x1, y, z) - SampleSDF(sdf, dim, x0, y, z))*(dim*0.5f); |
| float dy = (SampleSDF(sdf, dim, x, y1, z) - SampleSDF(sdf, dim, x, y0, z))*(dim*0.5f); |
| float dz = (SampleSDF(sdf, dim, x, y, z1) - SampleSDF(sdf, dim, x, y, z0))*(dim*0.5f); |
|
|
| return Vec3(dx, dy, dz); |
| } |
|
|
| void GetParticleBounds(Vec3& lower, Vec3& upper) |
| { |
| lower = Vec3(FLT_MAX); |
| upper = Vec3(-FLT_MAX); |
|
|
| for (int i=0; i < g_buffers->positions.size(); ++i) |
| { |
| lower = Min(Vec3(g_buffers->positions[i]), lower); |
| upper = Max(Vec3(g_buffers->positions[i]), upper); |
| } |
| } |
|
|
|
|
| void CreateParticleGrid(Vec3 lower, int dimx, int dimy, int dimz, float radius, Vec3 velocity, float invMass, bool rigid, float rigidStiffness, int phase, float jitter=0.005f) |
| { |
| if (rigid && g_buffers->rigidIndices.empty()) |
| g_buffers->rigidOffsets.push_back(0); |
|
|
| for (int x = 0; x < dimx; ++x) |
| { |
| for (int y = 0; y < dimy; ++y) |
| { |
| for (int z=0; z < dimz; ++z) |
| { |
| if (rigid) |
| g_buffers->rigidIndices.push_back(int(g_buffers->positions.size())); |
|
|
| Vec3 position = lower + Vec3(float(x), float(y), float(z))*radius + RandomUnitVector()*jitter; |
|
|
| g_buffers->positions.push_back(Vec4(position.x, position.y, position.z, invMass)); |
| g_buffers->velocities.push_back(velocity); |
| g_buffers->phases.push_back(phase); |
| } |
| } |
| } |
|
|
| if (rigid) |
| { |
| g_buffers->rigidCoefficients.push_back(rigidStiffness); |
| g_buffers->rigidOffsets.push_back(int(g_buffers->rigidIndices.size())); |
| } |
| } |
|
|
| void CreateParticleSphere(Vec3 center, int dim, float radius, Vec3 velocity, float invMass, bool rigid, float rigidStiffness, int phase, float jitter=0.005f) |
| { |
| if (rigid && g_buffers->rigidIndices.empty()) |
| g_buffers->rigidOffsets.push_back(0); |
|
|
| for (int x=0; x <= dim; ++x) |
| { |
| for (int y=0; y <= dim; ++y) |
| { |
| for (int z=0; z <= dim; ++z) |
| { |
| float sx = x - dim*0.5f; |
| float sy = y - dim*0.5f; |
| float sz = z - dim*0.5f; |
|
|
| if (sx*sx + sy*sy + sz*sz <= float(dim*dim)*0.25f) |
| { |
| if (rigid) |
| g_buffers->rigidIndices.push_back(int(g_buffers->positions.size())); |
|
|
| Vec3 position = center + radius*Vec3(sx, sy, sz) + RandomUnitVector()*jitter; |
|
|
| g_buffers->positions.push_back(Vec4(position.x, position.y, position.z, invMass)); |
| g_buffers->velocities.push_back(velocity); |
| g_buffers->phases.push_back(phase); |
| } |
| } |
| } |
| } |
|
|
| if (rigid) |
| { |
| g_buffers->rigidCoefficients.push_back(rigidStiffness); |
| g_buffers->rigidOffsets.push_back(int(g_buffers->rigidIndices.size())); |
| } |
| } |
|
|
| void CreateSpring(int i, int j, float stiffness, float give=0.0f) |
| { |
| g_buffers->springIndices.push_back(i); |
| g_buffers->springIndices.push_back(j); |
| g_buffers->springLengths.push_back((1.0f+give)*Length(Vec3(g_buffers->positions[i])-Vec3(g_buffers->positions[j]))); |
| g_buffers->springStiffness.push_back(stiffness); |
| } |
|
|
|
|
| void CreateParticleShape(const Mesh* srcMesh, Vec3 lower, Vec3 scale, float rotation, float spacing, Vec3 velocity, float invMass, bool rigid, float rigidStiffness, int phase, bool skin, float jitter=0.005f, Vec3 skinOffset=0.0f, float skinExpand=0.0f, Vec4 color=Vec4(0.0f), float springStiffness=0.0f) |
| { |
| if (rigid && g_buffers->rigidIndices.empty()) |
| g_buffers->rigidOffsets.push_back(0); |
|
|
| if (!srcMesh) |
| return; |
|
|
| |
| Mesh mesh; |
| mesh.AddMesh(*srcMesh); |
|
|
| int startIndex = int(g_buffers->positions.size()); |
|
|
| { |
| mesh.Transform(RotationMatrix(rotation, Vec3(0.0f, 1.0f, 0.0f))); |
|
|
| Vec3 meshLower, meshUpper; |
| mesh.GetBounds(meshLower, meshUpper); |
|
|
| Vec3 edges = meshUpper-meshLower; |
| float maxEdge = max(max(edges.x, edges.y), edges.z); |
|
|
| |
| Matrix44 xform = ScaleMatrix(scale/maxEdge)*TranslationMatrix(Point3(-meshLower)); |
|
|
| mesh.Transform(xform); |
| mesh.GetBounds(meshLower, meshUpper); |
|
|
| |
| edges = meshUpper-meshLower; |
| maxEdge = max(max(edges.x, edges.y), edges.z); |
|
|
| |
| |
| 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((const Vec3*)&mesh.m_positions[0], mesh.m_positions.size(), (const int*)&mesh.m_indices[0], mesh.m_indices.size(), maxDim, maxDim, maxDim, &voxels[0], meshLower, meshLower + Vec3(maxDim*spacing)); |
|
|
| vector<int> indices(maxDim*maxDim*maxDim); |
| vector<float> sdf(maxDim*maxDim*maxDim); |
| MakeSDF(&voxels[0], maxDim, maxDim, maxDim, &sdf[0]); |
|
|
| 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]) |
| { |
| if (rigid) |
| g_buffers->rigidIndices.push_back(int(g_buffers->positions.size())); |
|
|
| Vec3 position = lower + meshLower + spacing*Vec3(float(x) + 0.5f, float(y) + 0.5f, float(z) + 0.5f) + RandomUnitVector()*jitter; |
|
|
| |
| Vec3 n = SafeNormalize(SampleSDFGrad(&sdf[0], maxDim, x, y, z)); |
| float d = sdf[index]*maxEdge; |
|
|
| if (rigid) |
| g_buffers->rigidLocalNormals.push_back(Vec4(n, d)); |
|
|
| |
| indices[index] = g_buffers->positions.size(); |
|
|
| g_buffers->positions.push_back(Vec4(position.x, position.y, position.z, invMass)); |
| g_buffers->velocities.push_back(velocity); |
| g_buffers->phases.push_back(phase); |
| } |
| } |
| } |
| } |
| mesh.Transform(ScaleMatrix(1.0f + skinExpand)*TranslationMatrix(Point3(-0.5f*(meshUpper+meshLower)))); |
| mesh.Transform(TranslationMatrix(Point3(lower + 0.5f*(meshUpper+meshLower)))); |
| |
| |
| if (springStiffness > 0.0f) |
| { |
| |
| for (int x=0; x < maxDim; ++x) |
| { |
| for (int y=0; y < maxDim; ++y) |
| { |
| for (int z=0; z < maxDim; ++z) |
| { |
| const int centerCell = z*maxDim*maxDim + y*maxDim + x; |
|
|
| |
| if (voxels[centerCell]) |
| { |
| const int width = 1; |
|
|
| |
| for (int i=x-width; i <= x+width; ++i) |
| { |
| for (int j=y-width; j <= y+width; ++j) |
| { |
| for (int k=z-width; k <= z+width; ++k) |
| { |
| const int neighborCell = k*maxDim*maxDim + j*maxDim + i; |
|
|
| if (neighborCell > 0 && neighborCell < int(voxels.size()) && voxels[neighborCell] && neighborCell != centerCell) |
| { |
| CreateSpring(indices[neighborCell], indices[centerCell], springStiffness); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
|
|
| } |
| |
|
|
| if (skin) |
| { |
| g_buffers->rigidMeshSize.push_back(mesh.GetNumVertices()); |
|
|
| int startVertex = 0; |
|
|
| if (!g_mesh) |
| g_mesh = new Mesh(); |
|
|
| |
| startVertex = g_mesh->GetNumVertices(); |
|
|
| g_mesh->Transform(TranslationMatrix(Point3(skinOffset))); |
| g_mesh->AddMesh(mesh); |
|
|
| const Colour colors[7] = |
| { |
| Colour(0.0f, 0.5f, 1.0f), |
| Colour(0.797f, 0.354f, 0.000f), |
| Colour(0.000f, 0.349f, 0.173f), |
| Colour(0.875f, 0.782f, 0.051f), |
| Colour(0.01f, 0.170f, 0.453f), |
| Colour(0.673f, 0.111f, 0.000f), |
| Colour(0.612f, 0.194f, 0.394f) |
| }; |
|
|
| for (uint32_t i=startVertex; i < g_mesh->GetNumVertices(); ++i) |
| { |
| int indices[g_numSkinWeights] = { -1, -1, -1, -1 }; |
| float distances[g_numSkinWeights] = {FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }; |
| |
| if (LengthSq(color) == 0.0f) |
| g_mesh->m_colours[i] = 1.25f*colors[((unsigned int)(phase))%7]; |
| else |
| g_mesh->m_colours[i] = Colour(color); |
|
|
| |
| for (int j=startIndex; j < g_buffers->positions.size(); ++j) |
| { |
| float dSq = LengthSq(Vec3(g_mesh->m_positions[i])-Vec3(g_buffers->positions[j])); |
|
|
| |
| int w=0; |
| for (; w < 4; ++w) |
| if (dSq < distances[w]) |
| break; |
| |
| if (w < 4) |
| { |
| |
| for (int s=3; s > w; --s) |
| { |
| indices[s] = indices[s-1]; |
| distances[s] = distances[s-1]; |
| } |
|
|
| distances[w] = dSq; |
| indices[w] = int(j); |
| } |
| } |
|
|
| |
| float wSum = 0.0f; |
|
|
| for (int w=0; w < 4; ++w) |
| { |
| |
| distances[w] = 1.0f/(0.1f + powf(distances[w], .125f)); |
|
|
| wSum += distances[w]; |
|
|
| } |
|
|
| float weights[4]; |
| for (int w=0; w < 4; ++w) |
| weights[w] = distances[w]/wSum; |
|
|
| for (int j=0; j < 4; ++j) |
| { |
| g_meshSkinIndices.push_back(indices[j]); |
| g_meshSkinWeights.push_back(weights[j]); |
| } |
| } |
| } |
|
|
| if (rigid) |
| { |
| g_buffers->rigidCoefficients.push_back(rigidStiffness); |
| g_buffers->rigidOffsets.push_back(int(g_buffers->rigidIndices.size())); |
| } |
| } |
|
|
| |
| void CreateParticleShape(const char* filename, Vec3 lower, Vec3 scale, float rotation, float spacing, Vec3 velocity, float invMass, bool rigid, float rigidStiffness, int phase, bool skin, float jitter=0.005f, Vec3 skinOffset=0.0f, float skinExpand=0.0f, Vec4 color=Vec4(0.0f), float springStiffness=0.0f) |
| { |
| Mesh* mesh = ImportMesh(filename); |
| if (mesh) |
| CreateParticleShape(mesh, lower, scale, rotation, spacing, velocity, invMass, rigid, rigidStiffness, phase, skin, jitter, skinOffset, skinExpand, color, springStiffness); |
| |
| delete mesh; |
| } |
|
|
| void SkinMesh() |
| { |
| if (g_mesh) |
| { |
| int startVertex = 0; |
|
|
| for (int r=0; r < g_buffers->rigidRotations.size(); ++r) |
| { |
| const Matrix33 rotation = g_buffers->rigidRotations[r]; |
| const int numVertices = g_buffers->rigidMeshSize[r]; |
|
|
| for (int i=startVertex; i < numVertices+startVertex; ++i) |
| { |
| Vec3 skinPos; |
|
|
| for (int w=0; w < 4; ++w) |
| { |
| |
| if (g_meshSkinIndices[i*4+w] > -1) |
| { |
| assert(g_meshSkinWeights[i*4+w] < FLT_MAX); |
|
|
| int index = g_meshSkinIndices[i*4+w]; |
| float weight = g_meshSkinWeights[i*4+w]; |
|
|
| skinPos += (rotation*(g_meshRestPositions[i]-Point3(g_buffers->restPositions[index])) + Vec3(g_buffers->positions[index]))*weight; |
| } |
| } |
|
|
| g_mesh->m_positions[i] = Point3(skinPos); |
| } |
|
|
| startVertex += numVertices; |
| } |
|
|
| g_mesh->CalculateNormals(); |
| } |
| } |
|
|
| void AddBox(Vec3 halfEdge = Vec3(2.0f), Vec3 center=Vec3(0.0f), Quat quat=Quat(), bool dynamic=false, int channels=eNvFlexPhaseShapeChannelMask) |
| { |
| |
| g_buffers->shapePositions.push_back(Vec4(center.x, center.y, center.z, 0.0f)); |
| g_buffers->shapeRotations.push_back(quat); |
|
|
| g_buffers->shapePrevPositions.push_back(g_buffers->shapePositions.back()); |
| g_buffers->shapePrevRotations.push_back(g_buffers->shapeRotations.back()); |
|
|
| NvFlexCollisionGeometry geo; |
| geo.box.halfExtents[0] = halfEdge.x; |
| geo.box.halfExtents[1] = halfEdge.y; |
| geo.box.halfExtents[2] = halfEdge.z; |
|
|
| g_buffers->shapeGeometry.push_back(geo); |
| g_buffers->shapeFlags.push_back(NvFlexMakeShapeFlagsWithChannels(eNvFlexShapeBox, dynamic, channels)); |
| } |
|
|
| |
| void AddPlinth() |
| { |
| Vec3 lower, upper; |
| GetParticleBounds(lower, upper); |
|
|
| Vec3 center = (lower+upper)*0.5f; |
| center.y = 0.5f; |
|
|
| AddBox(Vec3(2.0f, 0.5f, 2.0f), center); |
| } |
|
|
| void AddSphere(float radius, Vec3 position, Quat rotation) |
| { |
| NvFlexCollisionGeometry geo; |
| geo.sphere.radius = radius; |
| g_buffers->shapeGeometry.push_back(geo); |
|
|
| g_buffers->shapePositions.push_back(Vec4(position, 0.0f)); |
| g_buffers->shapeRotations.push_back(rotation); |
|
|
| g_buffers->shapePrevPositions.push_back(g_buffers->shapePositions.back()); |
| g_buffers->shapePrevRotations.push_back(g_buffers->shapeRotations.back()); |
|
|
| int flags = NvFlexMakeShapeFlags(eNvFlexShapeSphere, false); |
| g_buffers->shapeFlags.push_back(flags); |
| } |
|
|
| |
| void AddCapsule(float radius, float halfHeight, Vec3 position, Quat rotation) |
| { |
| NvFlexCollisionGeometry geo; |
| geo.capsule.radius = radius; |
| geo.capsule.halfHeight = halfHeight; |
|
|
| g_buffers->shapeGeometry.push_back(geo); |
|
|
| g_buffers->shapePositions.push_back(Vec4(position, 0.0f)); |
| g_buffers->shapeRotations.push_back(rotation); |
|
|
| g_buffers->shapePrevPositions.push_back(g_buffers->shapePositions.back()); |
| g_buffers->shapePrevRotations.push_back(g_buffers->shapeRotations.back()); |
|
|
| int flags = NvFlexMakeShapeFlags(eNvFlexShapeCapsule, false); |
| g_buffers->shapeFlags.push_back(flags); |
| } |
|
|
| void CreateSDF(const Mesh* mesh, uint32_t dim, Vec3 lower, Vec3 upper, float* sdf) |
| { |
| if (mesh) |
| { |
| printf("Begin mesh voxelization\n"); |
|
|
| double startVoxelize = GetSeconds(); |
|
|
| uint32_t* volume = new uint32_t[dim*dim*dim]; |
| Voxelize((const Vec3*)&mesh->m_positions[0], mesh->m_positions.size(), (const int*)&mesh->m_indices[0], mesh->m_indices.size(), dim, dim, dim, volume, lower, upper); |
|
|
| printf("End mesh voxelization (%.2fs)\n", (GetSeconds()-startVoxelize)); |
| |
| printf("Begin SDF gen (fast marching method)\n"); |
|
|
| double startSDF = GetSeconds(); |
|
|
| MakeSDF(volume, dim, dim, dim, sdf); |
|
|
| printf("End SDF gen (%.2fs)\n", (GetSeconds()-startSDF)); |
| |
| delete[] volume; |
| } |
| } |
|
|
|
|
| void AddRandomConvex(int numPlanes, Vec3 position, float minDist, float maxDist, Vec3 axis, float angle) |
| { |
| const int maxPlanes = 12; |
|
|
| |
| const Vec3 directions[maxPlanes] = { |
| Vec3(1.0f, 0.0f, 0.0f), |
| Vec3(0.0f, 1.0f, 0.0f), |
| Vec3(0.0f, 0.0f, 1.0f), |
| Vec3(-1.0f, 0.0f, 0.0f), |
| Vec3(0.0f, -1.0f, 0.0f), |
| Vec3(0.0f, 0.0f, -1.0f), |
| Vec3(1.0f, 1.0f, 0.0f), |
| Vec3(-1.0f, -1.0f, 0.0f), |
| Vec3(1.0f, 0.0f, 1.0f), |
| Vec3(-1.0f, 0.0f, -1.0f), |
| Vec3(0.0f, 1.0f, 1.0f), |
| Vec3(0.0f, -1.0f, -1.0f), |
| }; |
|
|
| numPlanes = Clamp(6, numPlanes, maxPlanes); |
|
|
| int mesh = NvFlexCreateConvexMesh(g_flexLib); |
|
|
| NvFlexVector<Vec4> planes(g_flexLib); |
| planes.map(); |
|
|
| |
| for (int i=0; i < numPlanes; ++i) |
| { |
| Vec4 plane = Vec4(Normalize(directions[i]), -Randf(minDist, maxDist)); |
| planes.push_back(plane); |
| } |
|
|
| g_buffers->shapePositions.push_back(Vec4(position.x, position.y, position.z, 0.0f)); |
| g_buffers->shapeRotations.push_back(QuatFromAxisAngle(axis, angle)); |
|
|
| g_buffers->shapePrevPositions.push_back(g_buffers->shapePositions.back()); |
| g_buffers->shapePrevRotations.push_back(g_buffers->shapeRotations.back()); |
|
|
| |
| ConvexMeshBuilder builder(&planes[0]); |
| builder(numPlanes); |
|
|
| Vec3 lower(FLT_MAX), upper(-FLT_MAX); |
| for (size_t v=0; v < builder.mVertices.size(); ++v) |
| { |
| const Vec3 p = builder.mVertices[v]; |
|
|
| lower = Min(lower, p); |
| upper = Max(upper, p); |
| } |
|
|
| planes.unmap(); |
|
|
| |
| NvFlexUpdateConvexMesh(g_flexLib, mesh, planes.buffer, planes.size(), lower, upper); |
|
|
| NvFlexCollisionGeometry geo; |
| geo.convexMesh.mesh = mesh; |
| geo.convexMesh.scale[0] = 1.0f; |
| geo.convexMesh.scale[1] = 1.0f; |
| geo.convexMesh.scale[2] = 1.0f; |
|
|
| g_buffers->shapeGeometry.push_back(geo); |
|
|
| int flags = NvFlexMakeShapeFlags(eNvFlexShapeConvexMesh, false); |
| g_buffers->shapeFlags.push_back(flags); |
|
|
|
|
| |
| Mesh renderMesh; |
|
|
| for (uint32_t j = 0; j < builder.mIndices.size(); j += 3) |
| { |
| uint32_t a = builder.mIndices[j + 0]; |
| uint32_t b = builder.mIndices[j + 1]; |
| uint32_t c = builder.mIndices[j + 2]; |
|
|
| Vec3 n = Normalize(Cross(builder.mVertices[b] - builder.mVertices[a], builder.mVertices[c] - builder.mVertices[a])); |
| |
| int startIndex = renderMesh.m_positions.size(); |
|
|
| renderMesh.m_positions.push_back(Point3(builder.mVertices[a])); |
| renderMesh.m_normals.push_back(n); |
|
|
| renderMesh.m_positions.push_back(Point3(builder.mVertices[b])); |
| renderMesh.m_normals.push_back(n); |
|
|
| renderMesh.m_positions.push_back(Point3(builder.mVertices[c])); |
| renderMesh.m_normals.push_back(n); |
|
|
| renderMesh.m_indices.push_back(startIndex+0); |
| renderMesh.m_indices.push_back(startIndex+1); |
| renderMesh.m_indices.push_back(startIndex+2); |
| } |
|
|
| |
| GpuMesh* gpuMesh = CreateGpuMesh(&renderMesh); |
| g_convexes[mesh] = gpuMesh; |
| } |
|
|
| void CreateRandomBody(int numPlanes, Vec3 position, float minDist, float maxDist, Vec3 axis, float angle, float invMass, int phase, float stiffness) |
| { |
| |
| const Vec3 directions[] = { |
| Vec3(1.0f, 0.0f, 0.0f), |
| Vec3(0.0f, 1.0f, 0.0f), |
| Vec3(0.0f, 0.0f, 1.0f), |
| Vec3(-1.0f, 0.0f, 0.0f), |
| Vec3(0.0f, -1.0f, 0.0f), |
| Vec3(0.0f, 0.0f, -1.0f), |
| Vec3(1.0f, 1.0f, 0.0f), |
| Vec3(-1.0f, -1.0f, 0.0f), |
| Vec3(1.0f, 0.0f, 1.0f), |
| Vec3(-1.0f, 0.0f, -1.0f), |
| Vec3(0.0f, 1.0f, 1.0f), |
| Vec3(0.0f, -1.0f, -1.0f), |
| }; |
|
|
| numPlanes = max(4, numPlanes); |
|
|
| vector<Vec4> planes; |
|
|
| |
| for (int i=0; i < numPlanes; ++i) |
| { |
| |
| Vec3 dir = Normalize(directions[i]); |
| float dist = Randf(minDist, maxDist); |
|
|
| planes.push_back(Vec4(dir.x, dir.y, dir.z, -dist)); |
| } |
|
|
| |
| ConvexMeshBuilder builder(&planes[0]); |
| builder(numPlanes); |
| |
| int startIndex = int(g_buffers->positions.size()); |
|
|
| for (size_t v=0; v < builder.mVertices.size(); ++v) |
| { |
| Quat q = QuatFromAxisAngle(axis, angle); |
| Vec3 p = rotate(Vec3(q), q.w, builder.mVertices[v]) + position; |
|
|
| g_buffers->positions.push_back(Vec4(p.x, p.y, p.z, invMass)); |
| g_buffers->velocities.push_back(0.0f); |
| g_buffers->phases.push_back(phase); |
|
|
| |
| for (size_t i=v+1; i < builder.mVertices.size(); ++i) |
| { |
| int a = startIndex + int(v); |
| int b = startIndex + int(i); |
|
|
| g_buffers->springIndices.push_back(a); |
| g_buffers->springIndices.push_back(b); |
| g_buffers->springLengths.push_back(Length(builder.mVertices[v]-builder.mVertices[i])); |
| g_buffers->springStiffness.push_back(stiffness); |
|
|
| } |
| } |
|
|
| for (size_t t=0; t < builder.mIndices.size(); ++t) |
| g_buffers->triangles.push_back(startIndex + builder.mIndices[t]); |
|
|
| |
| g_buffers->triangleNormals.resize(g_buffers->triangleNormals.size() + builder.mIndices.size()/3, Vec3(0.0f)); |
| } |
|
|
| NvFlexTriangleMeshId CreateTriangleMesh(Mesh* m) |
| { |
| if (!m) |
| return 0; |
|
|
| Vec3 lower, upper; |
| m->GetBounds(lower, upper); |
|
|
| NvFlexVector<Vec4> positions(g_flexLib, m->m_positions.size()); |
| positions.map(); |
| NvFlexVector<int> indices(g_flexLib); |
|
|
| for (int i = 0; i < int(m->m_positions.size()); ++i) |
| { |
| Vec3 vertex = Vec3(m->m_positions[i]); |
| positions[i] = Vec4(vertex, 0.0f); |
| } |
| indices.assign((int*)&m->m_indices[0], m->m_indices.size()); |
|
|
| positions.unmap(); |
| indices.unmap(); |
|
|
| NvFlexTriangleMeshId flexMesh = NvFlexCreateTriangleMesh(g_flexLib); |
| NvFlexUpdateTriangleMesh(g_flexLib, flexMesh, positions.buffer, indices.buffer, m->GetNumVertices(), m->GetNumFaces(), (float*)&lower, (float*)&upper); |
|
|
| |
| g_meshes[flexMesh] = CreateGpuMesh(m); |
| |
| return flexMesh; |
| } |
|
|
| void AddTriangleMesh(NvFlexTriangleMeshId mesh, Vec3 translation, Quat rotation, Vec3 scale) |
| { |
| Vec3 lower, upper; |
| NvFlexGetTriangleMeshBounds(g_flexLib, mesh, lower, upper); |
|
|
| NvFlexCollisionGeometry geo; |
| geo.triMesh.mesh = mesh; |
| geo.triMesh.scale[0] = scale.x; |
| geo.triMesh.scale[1] = scale.y; |
| geo.triMesh.scale[2] = scale.z; |
|
|
| g_buffers->shapePositions.push_back(Vec4(translation, 0.0f)); |
| g_buffers->shapeRotations.push_back(Quat(rotation)); |
| g_buffers->shapePrevPositions.push_back(Vec4(translation, 0.0f)); |
| g_buffers->shapePrevRotations.push_back(Quat(rotation)); |
| g_buffers->shapeGeometry.push_back((NvFlexCollisionGeometry&)geo); |
| g_buffers->shapeFlags.push_back(NvFlexMakeShapeFlags(eNvFlexShapeTriangleMesh, false)); |
| } |
|
|
| NvFlexDistanceFieldId CreateSDF(const char* meshFile, int dim, float margin = 0.1f, float expand = 0.0f) |
| { |
| Mesh* mesh = ImportMesh(meshFile); |
|
|
| |
| mesh->Normalize(1.0f - margin); |
| mesh->Transform(TranslationMatrix(Point3(margin, margin, margin)*0.5f)); |
|
|
| Vec3 lower(0.0f); |
| Vec3 upper(1.0f); |
|
|
| |
| |
| #ifdef ANDROID |
| string sdfFile = string(meshFile, strlen(meshFile) - strlen(strrchr(meshFile, '.'))) + ".pfm"; |
| #else |
| string sdfFile = string(meshFile, strrchr(meshFile, '.')) + ".pfm"; |
| #endif |
| |
|
|
| PfmImage pfm; |
| if (!PfmLoad(sdfFile.c_str(), pfm)) |
| { |
| pfm.m_width = dim; |
| pfm.m_height = dim; |
| pfm.m_depth = dim; |
| pfm.m_data = new float[dim*dim*dim]; |
|
|
| printf("Cooking SDF: %s - dim: %d^3\n", sdfFile.c_str(), dim); |
|
|
| CreateSDF(mesh, dim, lower, upper, pfm.m_data); |
|
|
| PfmSave(sdfFile.c_str(), pfm); |
| } |
|
|
| |
|
|
| assert(pfm.m_width == pfm.m_height && pfm.m_width == pfm.m_depth); |
|
|
| |
| int numVoxels = int(pfm.m_width*pfm.m_height*pfm.m_depth); |
| for (int i = 0; i < numVoxels; ++i) |
| pfm.m_data[i] += expand; |
|
|
| NvFlexVector<float> field(g_flexLib); |
| field.assign(pfm.m_data, pfm.m_width*pfm.m_height*pfm.m_depth); |
| field.unmap(); |
|
|
| |
| NvFlexDistanceFieldId sdf = NvFlexCreateDistanceField(g_flexLib); |
| NvFlexUpdateDistanceField(g_flexLib, sdf, dim, dim, dim, field.buffer); |
|
|
| |
| g_fields[sdf] = CreateGpuMesh(mesh); |
|
|
| delete mesh; |
| delete[] pfm.m_data; |
|
|
| return sdf; |
| } |
|
|
| void AddSDF(NvFlexDistanceFieldId sdf, Vec3 translation, Quat rotation, float width) |
| { |
| NvFlexCollisionGeometry geo; |
| geo.sdf.field = sdf; |
| geo.sdf.scale = width; |
|
|
| g_buffers->shapePositions.push_back(Vec4(translation, 0.0f)); |
| g_buffers->shapeRotations.push_back(Quat(rotation)); |
| g_buffers->shapePrevPositions.push_back(Vec4(translation, 0.0f)); |
| g_buffers->shapePrevRotations.push_back(Quat(rotation)); |
| g_buffers->shapeGeometry.push_back((NvFlexCollisionGeometry&)geo); |
| g_buffers->shapeFlags.push_back(NvFlexMakeShapeFlags(eNvFlexShapeSDF, false)); |
| } |
|
|
| inline int GridIndex(int x, int y, int dx) { return y*dx + x; } |
|
|
| void CreateSpringGrid(Vec3 lower, int dx, int dy, int dz, float radius, int phase, float stretchStiffness, float bendStiffness, float shearStiffness, Vec3 velocity, float invMass) |
| { |
| int baseIndex = int(g_buffers->positions.size()); |
|
|
| for (int z=0; z < dz; ++z) |
| { |
| for (int y=0; y < dy; ++y) |
| { |
| for (int x=0; x < dx; ++x) |
| { |
| Vec3 position = lower + radius*Vec3(float(x), float(z), float(y)); |
|
|
| g_buffers->positions.push_back(Vec4(position.x, position.y, position.z, invMass)); |
| g_buffers->velocities.push_back(velocity); |
| g_buffers->phases.push_back(phase); |
|
|
| if (x > 0 && y > 0) |
| { |
| g_buffers->triangles.push_back(baseIndex + GridIndex(x-1, y-1, dx)); |
| g_buffers->triangles.push_back(baseIndex + GridIndex(x, y-1, dx)); |
| g_buffers->triangles.push_back(baseIndex + GridIndex(x, y, dx)); |
| |
| g_buffers->triangles.push_back(baseIndex + GridIndex(x-1, y-1, dx)); |
| g_buffers->triangles.push_back(baseIndex + GridIndex(x, y, dx)); |
| g_buffers->triangles.push_back(baseIndex + GridIndex(x-1, y, dx)); |
|
|
| g_buffers->triangleNormals.push_back(Vec3(0.0f, 1.0f, 0.0f)); |
| g_buffers->triangleNormals.push_back(Vec3(0.0f, 1.0f, 0.0f)); |
| } |
| } |
| } |
| } |
|
|
| |
| for (int y=0; y < dy; ++y) |
| { |
| for (int x=0; x < dx; ++x) |
| { |
| int index0 = y*dx + x; |
|
|
| if (x > 0) |
| { |
| int index1 = y*dx + x - 1; |
| CreateSpring(baseIndex + index0, baseIndex + index1, stretchStiffness); |
| } |
|
|
| if (x > 1) |
| { |
| int index2 = y*dx + x - 2; |
| CreateSpring(baseIndex + index0, baseIndex + index2, bendStiffness); |
| } |
|
|
| if (y > 0 && x < dx-1) |
| { |
| int indexDiag = (y-1)*dx + x + 1; |
| CreateSpring(baseIndex + index0, baseIndex + indexDiag, shearStiffness); |
| } |
|
|
| if (y > 0 && x > 0) |
| { |
| int indexDiag = (y-1)*dx + x - 1; |
| CreateSpring(baseIndex + index0, baseIndex + indexDiag, shearStiffness); |
| } |
| } |
| } |
|
|
| |
| for (int x=0; x < dx; ++x) |
| { |
| for (int y=0; y < dy; ++y) |
| { |
| int index0 = y*dx + x; |
|
|
| if (y > 0) |
| { |
| int index1 = (y-1)*dx + x; |
| CreateSpring(baseIndex + index0, baseIndex + index1, stretchStiffness); |
| } |
|
|
| if (y > 1) |
| { |
| int index2 = (y-2)*dx + x; |
| CreateSpring(baseIndex + index0, baseIndex + index2, bendStiffness); |
| } |
| } |
| } |
| } |
|
|
| void CreateRope(Rope& rope, Vec3 start, Vec3 dir, float stiffness, int segments, float length, int phase, float spiralAngle=0.0f, float invmass=1.0f, float give=0.075f) |
| { |
| rope.mIndices.push_back(int(g_buffers->positions.size())); |
|
|
| g_buffers->positions.push_back(Vec4(start.x, start.y, start.z, invmass)); |
| g_buffers->velocities.push_back(0.0f); |
| g_buffers->phases.push_back(phase); |
| |
| Vec3 left, right; |
| BasisFromVector(dir, &left, &right); |
|
|
| float segmentLength = length/segments; |
| Vec3 spiralAxis = dir; |
| float spiralHeight = spiralAngle/(2.0f*kPi)*(length/segments); |
|
|
| if (spiralAngle > 0.0f) |
| dir = left; |
|
|
| Vec3 p = start; |
|
|
| for (int i=0; i < segments; ++i) |
| { |
| int prev = int(g_buffers->positions.size())-1; |
|
|
| p += dir*segmentLength; |
|
|
| |
| if (spiralAngle > 0.0f) |
| { |
| p += spiralAxis*spiralHeight; |
|
|
| dir = RotationMatrix(spiralAngle, spiralAxis)*dir; |
| } |
|
|
| rope.mIndices.push_back(int(g_buffers->positions.size())); |
|
|
| g_buffers->positions.push_back(Vec4(p.x, p.y, p.z, 1.0f)); |
| g_buffers->velocities.push_back(0.0f); |
| g_buffers->phases.push_back(phase); |
|
|
| |
| CreateSpring(prev, prev+1, stiffness, give); |
|
|
| |
| |
| |
| |
| |
| if (i > 0) |
| CreateSpring(prev-1, prev+1, stiffness*0.5f, give); |
| } |
| } |
|
|
| namespace |
| { |
| struct Tri |
| { |
| int a; |
| int b; |
| int c; |
|
|
| Tri(int a, int b, int c) : a(a), b(b), c(c) {} |
|
|
| bool operator < (const Tri& rhs) |
| { |
| if (a != rhs.a) |
| return a < rhs.a; |
| else if (b != rhs.b) |
| return b < rhs.b; |
| else |
| return c < rhs.c; |
| } |
| }; |
| } |
|
|
|
|
| namespace |
| { |
| struct TriKey |
| { |
| int orig[3]; |
| int indices[3]; |
|
|
| TriKey(int a, int b, int c) |
| { |
| orig[0] = a; |
| orig[1] = b; |
| orig[2] = c; |
|
|
| indices[0] = a; |
| indices[1] = b; |
| indices[2] = c; |
|
|
| std::sort(indices, indices+3); |
| } |
|
|
| bool operator < (const TriKey& rhs) const |
| { |
| if (indices[0] != rhs.indices[0]) |
| return indices[0] < rhs.indices[0]; |
| else if (indices[1] != rhs.indices[1]) |
| return indices[1] < rhs.indices[1]; |
| else |
| return indices[2] < rhs.indices[2]; |
| } |
| }; |
| } |
|
|
| void CreateTetMesh(const char* filename, Vec3 lower, float scale, float stiffness, int phase) |
| { |
| FILE* f = fopen(filename, "r"); |
|
|
| char line[2048]; |
|
|
| if (f) |
| { |
| typedef std::map<TriKey, int> TriMap; |
| TriMap triCount; |
|
|
| const int vertOffset = g_buffers->positions.size(); |
|
|
| Vec3 meshLower(FLT_MAX); |
| Vec3 meshUpper(-FLT_MAX); |
|
|
| bool firstTet = true; |
|
|
| while (!feof(f)) |
| { |
| if (fgets(line, 2048, f)) |
| { |
| switch(line[0]) |
| { |
| case '#': |
| break; |
| case 'v': |
| { |
| Vec3 pos; |
| sscanf(line, "v %f %f %f", &pos.x, &pos.y, &pos.z); |
|
|
| g_buffers->positions.push_back(Vec4(pos.x, pos.y, pos.z, 1.0f)); |
| g_buffers->velocities.push_back(0.0f); |
| g_buffers->phases.push_back(phase); |
|
|
| meshLower = Min(pos, meshLower); |
| meshUpper = Max(pos, meshUpper); |
| break; |
| } |
| case 't': |
| { |
| if (firstTet) |
| { |
| Vec3 edges = meshUpper-meshLower; |
| float maxEdge = max(edges.x, max(edges.y, edges.z)); |
|
|
| |
| for (int i=vertOffset; i < int(g_buffers->positions.size()); ++i) |
| { |
| Vec3 p = lower + (Vec3(g_buffers->positions[i])-meshLower)*scale/maxEdge; |
| g_buffers->positions[i] = Vec4(p, g_buffers->positions[i].w); |
| } |
|
|
| firstTet = false; |
| } |
|
|
| int indices[4]; |
| sscanf(line, "t %d %d %d %d", &indices[0], &indices[1], &indices[2], &indices[3]); |
|
|
| indices[0] += vertOffset; |
| indices[1] += vertOffset; |
| indices[2] += vertOffset; |
| indices[3] += vertOffset; |
|
|
| CreateSpring(indices[0], indices[1], stiffness); |
| CreateSpring(indices[0], indices[2], stiffness); |
| CreateSpring(indices[0], indices[3], stiffness); |
| |
| CreateSpring(indices[1], indices[2], stiffness); |
| CreateSpring(indices[1], indices[3], stiffness); |
| CreateSpring(indices[2], indices[3], stiffness); |
|
|
| TriKey k1(indices[0], indices[2], indices[1]); |
| triCount[k1] += 1; |
|
|
| TriKey k2(indices[1], indices[2], indices[3]); |
| triCount[k2] += 1; |
|
|
| TriKey k3(indices[0], indices[1], indices[3]); |
| triCount[k3] += 1; |
|
|
| TriKey k4(indices[0], indices[3], indices[2]); |
| triCount[k4] += 1; |
|
|
| break; |
| } |
| } |
| } |
| } |
|
|
| for (TriMap::iterator iter=triCount.begin(); iter != triCount.end(); ++iter) |
| { |
| TriKey key = iter->first; |
|
|
| |
| if (iter->second == 1) |
| { |
| g_buffers->triangles.push_back(key.orig[0]); |
| g_buffers->triangles.push_back(key.orig[1]); |
| g_buffers->triangles.push_back(key.orig[2]); |
| g_buffers->triangleNormals.push_back(0.0f); |
| } |
| } |
|
|
|
|
| fclose(f); |
| } |
| } |
|
|
|
|
| |
| int PickParticle(Vec3 origin, Vec3 dir, Vec4* particles, int* phases, int n, float radius, float &outT) |
| { |
| float maxDistSq = radius*radius; |
| float minT = FLT_MAX; |
| int minIndex = -1; |
|
|
| for (int i=0; i < n; ++i) |
| { |
| if (phases[i] & eNvFlexPhaseFluid) |
| continue; |
|
|
| Vec3 delta = Vec3(particles[i])-origin; |
| float t = Dot(delta, dir); |
|
|
| if (t > 0.0f) |
| { |
| Vec3 perp = delta - t*dir; |
|
|
| float dSq = LengthSq(perp); |
|
|
| if (dSq < maxDistSq && t < minT) |
| { |
| minT = t; |
| minIndex = i; |
| } |
| } |
| } |
|
|
| outT = minT; |
|
|
| return minIndex; |
| } |
|
|
| |
| void CalculateRigidCentersOfMass(const Vec4* restPositions, int numRestPositions, const int* offsets, Vec3* translations, const int* indices, int numRigids) |
| { |
| |
| |
| Vec3 shapeOffset(0.0f); |
|
|
| for (int i = 0; i < numRestPositions; i++) |
| { |
| shapeOffset += Vec3(restPositions[i]); |
| } |
|
|
| shapeOffset /= float(numRestPositions); |
|
|
| for (int i=0; i < numRigids; ++i) |
| { |
| const int startIndex = offsets[i]; |
| const int endIndex = offsets[i+1]; |
|
|
| const int n = endIndex-startIndex; |
|
|
| assert(n); |
|
|
| Vec3 com; |
| |
| for (int j=startIndex; j < endIndex; ++j) |
| { |
| const int r = indices[j]; |
|
|
| |
| com += Vec3(restPositions[r]) - shapeOffset; |
| } |
|
|
| com /= float(n); |
|
|
| |
| com += shapeOffset; |
|
|
| translations[i] = com; |
|
|
| } |
| } |
|
|
| |
| void CalculateRigidLocalPositions(const Vec4* restPositions, const int* offsets, const Vec3* translations, const int* indices, int numRigids, Vec3* localPositions) |
| { |
| int count = 0; |
|
|
| for (int i=0; i < numRigids; ++i) |
| { |
| const int startIndex = offsets[i]; |
| const int endIndex = offsets[i+1]; |
|
|
| assert(endIndex-startIndex); |
|
|
| for (int j=startIndex; j < endIndex; ++j) |
| { |
| const int r = indices[j]; |
|
|
| localPositions[count++] = Vec3(restPositions[r]) - translations[i]; |
| } |
| } |
| } |
|
|
| void DrawImguiString(int x, int y, Vec3 color, int align, const char* s, ...) |
| { |
| char buf[2048]; |
|
|
| va_list args; |
|
|
| va_start(args, s); |
| vsnprintf(buf, 2048, s, args); |
| va_end(args); |
|
|
| imguiDrawText(x, y, align, buf, imguiRGBA((unsigned char)(color.x*255), (unsigned char)(color.y*255), (unsigned char)(color.z*255))); |
| } |
|
|
| enum |
| { |
| HELPERS_SHADOW_OFFSET = 1, |
| }; |
|
|
| void DrawShadowedText(int x, int y, Vec3 color, int align, const char* s, ...) |
| { |
| char buf[2048]; |
|
|
| va_list args; |
|
|
| va_start(args, s); |
| vsnprintf(buf, 2048, s, args); |
| va_end(args); |
|
|
|
|
| imguiDrawText(x + HELPERS_SHADOW_OFFSET, y - HELPERS_SHADOW_OFFSET, align, buf, imguiRGBA(0, 0, 0)); |
| imguiDrawText(x, y, align, buf, imguiRGBA((unsigned char)(color.x * 255), (unsigned char)(color.y * 255), (unsigned char)(color.z * 255))); |
| } |
|
|
| void DrawRect(float x, float y, float w, float h, Vec3 color) |
| { |
| imguiDrawRect(x, y, w, h, imguiRGBA((unsigned char)(color.x * 255), (unsigned char)(color.y * 255), (unsigned char)(color.z * 255))); |
| } |
|
|
| void DrawShadowedRect(float x, float y, float w, float h, Vec3 color) |
| { |
| imguiDrawRect(x + HELPERS_SHADOW_OFFSET, y - HELPERS_SHADOW_OFFSET, w, h, imguiRGBA(0, 0, 0)); |
| imguiDrawRect(x, y, w, h, imguiRGBA((unsigned char)(color.x * 255), (unsigned char)(color.y * 255), (unsigned char)(color.z * 255))); |
| } |
|
|
| void DrawLine(float x0, float y0, float x1, float y1, float r, Vec3 color) |
| { |
| imguiDrawLine(x0, y0, x1, y1, r, imguiRGBA((unsigned char)(color.x * 255), (unsigned char)(color.y * 255), (unsigned char)(color.z * 255))); |
| } |
|
|
| void DrawShadowedLine(float x0, float y0, float x1, float y1, float r, Vec3 color) |
| { |
| imguiDrawLine(x0 + HELPERS_SHADOW_OFFSET, y0 - HELPERS_SHADOW_OFFSET, x1 + HELPERS_SHADOW_OFFSET, y1 - HELPERS_SHADOW_OFFSET, r, imguiRGBA(0, 0, 0)); |
| imguiDrawLine(x0, y0, x1, y1, r, imguiRGBA((unsigned char)(color.x * 255), (unsigned char)(color.y * 255), (unsigned char)(color.z * 255))); |
| } |
|
|
| |
|
|
| 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; |
| } |
| }; |
|
|
| 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()); |
|
|
| while (seeds.size()) |
| { |
| |
| Seed seed = seeds.back(); |
| seeds.pop_back(); |
|
|
| if (!used[seed.index]) |
| { |
| Cluster c; |
|
|
| const float radiusSq = sqr(radius); |
|
|
| |
| for (int p = 0; p < numParticles; ++p) |
| { |
| float dSq = LengthSq(Vec3(particles[seed.index]) - Vec3(particles[p])); |
| if (dSq <= radiusSq) |
| { |
| c.indices.push_back(p); |
|
|
| used[p] = true; |
| } |
| } |
|
|
| c.mean = CalculateMean(particles, &c.indices[0], c.indices.size()); |
|
|
| clusters.push_back(c); |
| } |
| } |
|
|
| if (smoothing > 0.0f) |
| { |
| |
| float radiusSmoothSq = sqr(smoothing); |
|
|
| for (int i = 0; i < int(clusters.size()); ++i) |
| { |
| Cluster& c = clusters[i]; |
|
|
| |
| c.indices.resize(0); |
|
|
| |
| for (int p = 0; p < numParticles; ++p) |
| { |
| float dSq = LengthSq(c.mean - Vec3(particles[p])); |
| if (dSq <= radiusSmoothSq) |
| c.indices.push_back(p); |
| } |
|
|
| c.mean = CalculateMean(particles, &c.indices[0], 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(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) |
| { |
| const float radiusSq = sqr(radius); |
|
|
| int count = 0; |
|
|
| for (int i = 0; i < numParticles; ++i) |
| { |
| for (int j = i + 1; j < numParticles; ++j) |
| { |
| float dSq = LengthSq(Vec3(particles[i]) - Vec3(particles[j])); |
|
|
| if (dSq < radiusSq) |
| { |
| outSpringIndices.push_back(i); |
| outSpringIndices.push_back(j); |
| outSpringLengths.push_back(sqrtf(dSq)); |
| 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; |
|
|
| |
| 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]; |
|
|
| for (int c = 0; c < numClusters; ++c) |
| { |
| float dSq = LengthSq(vertices[i] - clusters[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] = 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(Mesh* mesh, Vec3 lower, Vec3 scale, float rotation, float radius, float volumeSampling, float surfaceSampling, std::vector<Vec3>& outPositions) |
| { |
| if (!mesh) |
| return; |
|
|
| mesh->Transform(RotationMatrix(rotation, Vec3(0.0f, 1.0f, 0.0f))); |
|
|
| Vec3 meshLower, meshUpper; |
| mesh->GetBounds(meshLower, meshUpper); |
|
|
| Vec3 edges = meshUpper - meshLower; |
| float maxEdge = max(max(edges.x, edges.y), edges.z); |
|
|
| |
| Matrix44 xform = ScaleMatrix(scale / maxEdge)*TranslationMatrix(Point3(-meshLower)); |
|
|
| mesh->Transform(xform); |
| mesh->GetBounds(meshLower, meshUpper); |
|
|
| std::vector<Vec3> samples; |
|
|
| if (volumeSampling > 0.0f) |
| { |
| |
| edges = meshUpper - meshLower; |
| maxEdge = max(max(edges.x, edges.y), edges.z); |
|
|
| |
| 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((const Vec3*)&mesh->m_positions[0], mesh->m_positions.size(), (const int*)&mesh->m_indices[0], mesh->m_indices.size(), 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 = lower + meshLower + spacing*Vec3(float(x) + 0.5f, float(y) + 0.5f, float(z) + 0.5f); |
|
|
| |
| samples.push_back(position); |
| } |
| } |
| } |
| } |
| } |
|
|
| |
| mesh->Transform(ScaleMatrix(1.0f)*TranslationMatrix(Point3(-0.5f*(meshUpper + meshLower)))); |
| mesh->Transform(TranslationMatrix(Point3(lower + 0.5f*(meshUpper + meshLower)))); |
|
|
| if (surfaceSampling > 0.0f) |
| { |
| |
| for (int i = 0; i < int(mesh->m_positions.size()); ++i) |
| samples.push_back(Vec3(mesh->m_positions[i])); |
|
|
| |
| if (1) |
| { |
| for (int i = 0; i < 50000; ++i) |
| { |
| int t = Rand() % mesh->GetNumFaces(); |
| float u = Randf(); |
| float v = Randf()*(1.0f - u); |
| float w = 1.0f - u - v; |
|
|
| int a = mesh->m_indices[t * 3 + 0]; |
| int b = mesh->m_indices[t * 3 + 1]; |
| int c = mesh->m_indices[t * 3 + 2]; |
| |
| Point3 pt = mesh->m_positions[a] * u + mesh->m_positions[b] * v + mesh->m_positions[c] * w; |
| Vec3 p(pt.x,pt.y,pt.z); |
|
|
| 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], samples.size(), clusterOffsets, clusterIndices, outPositions, radius); |
|
|
| } |
|
|
| void ClearShapes() |
| { |
| g_buffers->shapeGeometry.resize(0); |
| g_buffers->shapePositions.resize(0); |
| g_buffers->shapeRotations.resize(0); |
| g_buffers->shapePrevPositions.resize(0); |
| g_buffers->shapePrevRotations.resize(0); |
| g_buffers->shapeFlags.resize(0); |
| } |
|
|
| void UpdateShapes() |
| { |
| |
| g_shapesChanged = true; |
| } |
|
|
| |
| void GetShapeBounds(Vec3& totalLower, Vec3& totalUpper) |
| { |
| Bounds totalBounds; |
|
|
| for (int i=0; i < g_buffers->shapeFlags.size(); ++i) |
| { |
| NvFlexCollisionGeometry geo = g_buffers->shapeGeometry[i]; |
|
|
| int type = g_buffers->shapeFlags[i]&eNvFlexShapeFlagTypeMask; |
|
|
| Vec3 localLower; |
| Vec3 localUpper; |
|
|
| switch(type) |
| { |
| case eNvFlexShapeBox: |
| { |
| localLower = -Vec3(geo.box.halfExtents); |
| localUpper = Vec3(geo.box.halfExtents); |
| break; |
| } |
| case eNvFlexShapeSphere: |
| { |
| localLower = -geo.sphere.radius; |
| localUpper = geo.sphere.radius; |
| break; |
| } |
| case eNvFlexShapeCapsule: |
| { |
| localLower = -Vec3(geo.capsule.halfHeight, 0.0f, 0.0f) - Vec3(geo.capsule.radius); |
| localUpper = Vec3(geo.capsule.halfHeight, 0.0f, 0.0f) + Vec3(geo.capsule.radius); |
| break; |
| } |
| case eNvFlexShapeConvexMesh: |
| { |
| NvFlexGetConvexMeshBounds(g_flexLib, geo.convexMesh.mesh, localLower, localUpper); |
|
|
| |
| localLower *= geo.convexMesh.scale; |
| localUpper *= geo.convexMesh.scale; |
| break; |
| } |
| case eNvFlexShapeTriangleMesh: |
| { |
| NvFlexGetTriangleMeshBounds(g_flexLib, geo.triMesh.mesh, localLower, localUpper); |
| |
| |
| localLower *= Vec3(geo.triMesh.scale); |
| localUpper *= Vec3(geo.triMesh.scale); |
| break; |
| } |
| case eNvFlexShapeSDF: |
| { |
| localLower = 0.0f; |
| localUpper = geo.sdf.scale; |
| break; |
| } |
| }; |
|
|
| |
| Vec3 worldLower, worldUpper; |
| TransformBounds(localLower, localUpper, Vec3(g_buffers->shapePositions[i]), g_buffers->shapeRotations[i], 1.0f, worldLower, worldUpper); |
|
|
| totalBounds = Union(totalBounds, Bounds(worldLower, worldUpper)); |
| } |
|
|
| totalLower = totalBounds.lower; |
| totalUpper = totalBounds.upper; |
| } |