// This code contains NVIDIA Confidential Information and is disclosed to you // under a form of NVIDIA software license agreement provided separately to you. // // Notice // NVIDIA Corporation and its licensors retain all intellectual property and // proprietary rights in and to this software and related documentation and // any modifications thereto. Any use, reproduction, disclosure, or // distribution of this software and related documentation without an express // license agreement from NVIDIA Corporation is strictly prohibited. // // ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES // NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO // THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, // MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. // // Information and code furnished is believed to be accurate and reliable. // However, NVIDIA Corporation assumes no responsibility for the consequences of use of such // information or for any infringement of patents or other rights of third parties that may // result from its use. No license is granted by implication or otherwise under any patent // or patent rights of NVIDIA Corporation. Details are subject to change without notice. // This code supersedes and replaces all information previously supplied. // NVIDIA Corporation products are not authorized for use as critical // components in life support devices or systems without express written approval of // NVIDIA Corporation. // // Copyright (c) 2013-2017 NVIDIA Corporation. All rights reserved. #include #include #include #include "../core/core.h" #include "../core/maths.h" #include "../include/NvFlex.h" #include "../include/NvFlexExt.h" class Bitmap { public: typedef unsigned int Word; static const int kWordSize = sizeof(Word)*8; Bitmap(int numBits) : mBits((numBits+kWordSize-1)/kWordSize) { } inline void Set(int bit) { const int wordIndex = bit/kWordSize; const int bitIndex = bit&(kWordSize-1); const Word word = mBits[wordIndex]; mBits[wordIndex] = word|(1< mBits; }; struct NvFlexExtContainer { int mMaxParticles; NvFlexSolver* mSolver; NvFlexLibrary* mFlexLib; // first n indices NvFlexVector mActiveList; std::vector mFreeList; std::vector mInstances; std::vector mSoftJoints; // particles NvFlexVector mParticles; NvFlexVector mParticlesRest; NvFlexVector mVelocities; NvFlexVector mPhases; NvFlexVector mNormals; // shapes NvFlexVector mShapeOffsets; NvFlexVector mShapeIndices; NvFlexVector mShapeCoefficients; NvFlexVector mShapePlasticThresholds; NvFlexVector mShapePlasticCreeps; NvFlexVector mShapeRotations; NvFlexVector mShapeTranslations; NvFlexVector mShapeRestPositions; // springs NvFlexVector mSpringIndices; NvFlexVector mSpringLengths; NvFlexVector mSpringCoefficients; // cloth NvFlexVector mTriangleIndices; NvFlexVector mTriangleNormals; NvFlexVector mInflatableStarts; NvFlexVector mInflatableCounts; NvFlexVector mInflatableRestVolumes; NvFlexVector mInflatableCoefficients; NvFlexVector mInflatableOverPressures; // bounds (CPU), stored in a vector to ensure transfers can happen asynchronously NvFlexVector mBoundsLower; NvFlexVector mBoundsUpper; // needs compact bool mNeedsCompact; // needs to update active list bool mNeedsActiveListRebuild; NvFlexExtContainer(NvFlexLibrary* l) : mMaxParticles(0), mSolver(NULL), mFlexLib(l), mActiveList(l),mParticles(l),mParticlesRest(l),mVelocities(l), mPhases(l),mNormals(l),mShapeOffsets(l),mShapeIndices(l), mShapeCoefficients(l),mShapePlasticThresholds(l), mShapePlasticCreeps(l),mShapeRotations(l),mShapeTranslations(l), mShapeRestPositions(l),mSpringIndices(l),mSpringLengths(l), mSpringCoefficients(l),mTriangleIndices(l),mTriangleNormals(l), mInflatableStarts(l),mInflatableCounts(l),mInflatableRestVolumes(l), mInflatableCoefficients(l),mInflatableOverPressures(l), mBoundsLower(l), mBoundsUpper(l), mNeedsCompact(false), mNeedsActiveListRebuild(false) {} }; namespace { // compacts all constraints into linear arrays void CompactObjects(NvFlexExtContainer* c) { int totalNumSprings = 0; int totalNumTris = 0; int totalNumShapes = 0; int totalNumShapeIndices = 0; bool plasticDeformation = false; // pre-calculate array sizes for (size_t i = 0; i < c->mInstances.size(); ++i) { NvFlexExtInstance* inst = c->mInstances[i]; const NvFlexExtAsset* asset = inst->asset; // index into the triangle array for this instance inst->triangleIndex = totalNumTris; totalNumSprings += asset->numSprings; totalNumTris += asset->numTriangles; totalNumShapeIndices += asset->numShapeIndices; totalNumShapes += asset->numShapes; if (asset->shapePlasticThresholds && asset->shapePlasticCreeps) { plasticDeformation = true; } } // each joint corresponds to one shape matching constraint for (size_t i = 0; i < c->mSoftJoints.size(); ++i) { const NvFlexExtSoftJoint* joint = c->mSoftJoints[i]; totalNumShapeIndices += joint->numParticles; ++totalNumShapes; } //---------------------- // map buffers // springs c->mSpringIndices.map(); c->mSpringLengths.map(); c->mSpringCoefficients.map(); // cloth c->mTriangleIndices.map(); c->mTriangleNormals.map(); // inflatables c->mInflatableStarts.map(); c->mInflatableCounts.map(); c->mInflatableRestVolumes.map(); c->mInflatableCoefficients.map(); c->mInflatableOverPressures.map(); // shapes c->mShapeIndices.map(); c->mShapeRestPositions.map(); c->mShapeOffsets.map(); c->mShapeCoefficients.map(); c->mShapePlasticThresholds.map(); c->mShapePlasticCreeps.map(); c->mShapeTranslations.map(); c->mShapeRotations.map(); //---------------------- // resize buffers // springs c->mSpringIndices.resize(totalNumSprings * 2); c->mSpringLengths.resize(totalNumSprings); c->mSpringCoefficients.resize(totalNumSprings); // cloth c->mTriangleIndices.resize(totalNumTris * 3); c->mTriangleNormals.resize(totalNumTris); // inflatables c->mInflatableStarts.resize(0); c->mInflatableCounts.resize(0); c->mInflatableRestVolumes.resize(0); c->mInflatableCoefficients.resize(0); c->mInflatableOverPressures.resize(0); // shapes c->mShapeIndices.resize(totalNumShapeIndices); c->mShapeRestPositions.resize(totalNumShapeIndices); c->mShapeOffsets.resize(1 + totalNumShapes); c->mShapeCoefficients.resize(totalNumShapes); if (plasticDeformation) { c->mShapePlasticThresholds.resize(totalNumShapes); c->mShapePlasticCreeps.resize(totalNumShapes); } else { c->mShapePlasticThresholds.resize(0); c->mShapePlasticCreeps.resize(0); } c->mShapeTranslations.resize(totalNumShapes); c->mShapeRotations.resize(totalNumShapes); int* __restrict dstSpringIndices = (totalNumSprings) ? &c->mSpringIndices[0] : NULL; float* __restrict dstSpringLengths = (totalNumSprings) ? &c->mSpringLengths[0] : NULL; float* __restrict dstSpringCoefficients = (totalNumSprings) ? &c->mSpringCoefficients[0] : NULL; int* __restrict dstTriangleIndices = (totalNumTris) ? &c->mTriangleIndices[0] : NULL; int* __restrict dstShapeIndices = (totalNumShapeIndices) ? &c->mShapeIndices[0] : NULL; Vec3* __restrict dstShapeRestPositions = (totalNumShapeIndices) ? &c->mShapeRestPositions[0] : NULL; int* __restrict dstShapeOffsets = (totalNumShapes) ? &c->mShapeOffsets[0] : NULL; float* __restrict dstShapeCoefficients = (totalNumShapes) ? &c->mShapeCoefficients[0] : NULL; float* __restrict dstShapePlasticThresholds = NULL; float* __restrict dstShapePlasticCreeps = NULL; if (plasticDeformation) { dstShapePlasticThresholds = (totalNumShapes) ? &c->mShapePlasticThresholds[0] : NULL; dstShapePlasticCreeps = (totalNumShapes) ? &c->mShapePlasticCreeps[0] : NULL; } Vec3* __restrict dstShapeTranslations = (totalNumShapes) ? &c->mShapeTranslations[0] : NULL; Quat* __restrict dstShapeRotations = (totalNumShapes) ? &c->mShapeRotations[0] : NULL; // push leading zero if necessary if (totalNumShapes != 0) { *dstShapeOffsets = 0; ++dstShapeOffsets; } int shapeIndexOffset = 0; int shapeIndex = 0; // go through each instance and update springs, shapes, etc for (size_t i = 0; i < c->mInstances.size(); ++i) { NvFlexExtInstance* inst = c->mInstances[i]; const NvFlexExtAsset* asset = inst->asset; // map indices from the asset to the instance const int* __restrict remap = &inst->particleIndices[0]; // flatten spring data int numSprings = asset->numSprings; const int numSpringIndices = asset->numSprings * 2; const int* __restrict srcSpringIndices = asset->springIndices; for (int i = 0; i < numSpringIndices; ++i) { *dstSpringIndices = remap[*srcSpringIndices]; ++dstSpringIndices; ++srcSpringIndices; } memcpy(dstSpringLengths, asset->springRestLengths, numSprings*sizeof(float)); memcpy(dstSpringCoefficients, asset->springCoefficients, numSprings*sizeof(float)); dstSpringLengths += numSprings; dstSpringCoefficients += numSprings; // shapes if (asset->numShapes) { const int indexOffset = shapeIndexOffset; // store start index into shape array inst->shapeIndex = shapeIndex; int shapeStart = 0; for (int s=0; s < asset->numShapes; ++s) { dstShapeOffsets[shapeIndex] = asset->shapeOffsets[s] + indexOffset; dstShapeCoefficients[shapeIndex] = asset->shapeCoefficients[s]; if (plasticDeformation) { if (asset->shapePlasticThresholds) dstShapePlasticThresholds[shapeIndex] = asset->shapePlasticThresholds[s]; else dstShapePlasticThresholds[shapeIndex] = 0.0f; if (asset->shapePlasticCreeps) dstShapePlasticCreeps[shapeIndex] = asset->shapePlasticCreeps[s]; else dstShapePlasticCreeps[shapeIndex] = 0.0f; } dstShapeTranslations[shapeIndex] = Vec3(&inst->shapeTranslations[s*3]); dstShapeRotations[shapeIndex] = Quat(&inst->shapeRotations[s*4]); ++shapeIndex; const int shapeEnd = asset->shapeOffsets[s]; for (int i=shapeStart; i < shapeEnd; ++i) { const int currentParticle = asset->shapeIndices[i]; // remap indices and create local space positions for each shape dstShapeRestPositions[shapeIndexOffset] = Vec3(&asset->particles[currentParticle*4]) - Vec3(&asset->shapeCenters[s*3]); dstShapeIndices[shapeIndexOffset] = remap[asset->shapeIndices[i]]; ++shapeIndexOffset; } shapeStart = shapeEnd; } } if (asset->numTriangles) { // triangles const int numTriIndices = asset->numTriangles * 3; const int* __restrict srcTriIndices = asset->triangleIndices; for (int i = 0; i < numTriIndices; ++i) { *dstTriangleIndices = remap[*srcTriIndices]; ++dstTriangleIndices; ++srcTriIndices; } if (asset->inflatable) { c->mInflatableStarts.push_back(inst->triangleIndex); c->mInflatableCounts.push_back(asset->numTriangles); c->mInflatableRestVolumes.push_back(asset->inflatableVolume); c->mInflatableCoefficients.push_back(asset->inflatableStiffness); c->mInflatableOverPressures.push_back(asset->inflatablePressure); } } } // go through each joint and add shape matching constraint to the solver for (size_t i = 0; i < c->mSoftJoints.size(); ++i) { NvFlexExtSoftJoint* joint = c->mSoftJoints[i]; const int numJointParticles = joint->numParticles; // store start index into shape array joint->shapeIndex = shapeIndex; const int offset = dstShapeOffsets[shapeIndex - 1]; dstShapeOffsets[shapeIndex] = offset + numJointParticles; for (int i = 0; i < numJointParticles; ++i) { dstShapeIndices[shapeIndexOffset] = joint->particleIndices[i]; dstShapeRestPositions[shapeIndexOffset] = Vec3(joint->particleLocalPositions[3 * i + 0], joint->particleLocalPositions[3 * i + 1], joint->particleLocalPositions[3 * i + 2]); ++shapeIndexOffset; } dstShapeTranslations[shapeIndex] = Vec3(joint->shapeTranslations); dstShapeRotations[shapeIndex] = Quat(joint->shapeRotations); dstShapeCoefficients[shapeIndex] = joint->stiffness; ++shapeIndex; } //---------------------- // unmap buffers // springs c->mSpringIndices.unmap(); c->mSpringLengths.unmap(); c->mSpringCoefficients.unmap(); // cloth c->mTriangleIndices.unmap(); c->mTriangleNormals.unmap(); // inflatables c->mInflatableStarts.unmap(); c->mInflatableCounts.unmap(); c->mInflatableRestVolumes.unmap(); c->mInflatableCoefficients.unmap(); c->mInflatableOverPressures.unmap(); // shapes c->mShapeIndices.unmap(); c->mShapeRestPositions.unmap(); c->mShapeOffsets.unmap(); c->mShapeCoefficients.unmap(); c->mShapePlasticThresholds.unmap(); c->mShapePlasticCreeps.unmap(); c->mShapeTranslations.unmap(); c->mShapeRotations.unmap(); // ---------------------- // Flex update // springs if (c->mSpringLengths.size()) NvFlexSetSprings(c->mSolver, c->mSpringIndices.buffer, c->mSpringLengths.buffer, c->mSpringCoefficients.buffer, int(c->mSpringLengths.size())); else NvFlexSetSprings(c->mSolver, NULL, NULL, NULL, 0); // shapes if (c->mShapeCoefficients.size()) { NvFlexSetRigids(c->mSolver, c->mShapeOffsets.buffer, c->mShapeIndices.buffer, c->mShapeRestPositions.buffer, NULL, c->mShapeCoefficients.buffer, c->mShapePlasticThresholds.buffer, c->mShapePlasticCreeps.buffer, c->mShapeRotations.buffer, c->mShapeTranslations.buffer, int(c->mShapeCoefficients.size()), c->mShapeIndices.size()); } else { c->mShapeRotations.resize(0); c->mShapeTranslations.resize(0); NvFlexSetRigids(c->mSolver, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0); } // triangles if (c->mTriangleIndices.size()) NvFlexSetDynamicTriangles(c->mSolver, c->mTriangleIndices.buffer, NULL, int(c->mTriangleIndices.size()/3)); else NvFlexSetDynamicTriangles(c->mSolver, NULL, NULL, 0); // inflatables if (c->mInflatableCounts.size()) NvFlexSetInflatables(c->mSolver, c->mInflatableStarts.buffer, c->mInflatableCounts.buffer, c->mInflatableRestVolumes.buffer, c->mInflatableOverPressures.buffer, c->mInflatableCoefficients.buffer, int(c->mInflatableCounts.size())); else NvFlexSetInflatables(c->mSolver, NULL, NULL, NULL, NULL, NULL, 0); c->mNeedsCompact = false; } } // anonymous namespace NvFlexExtContainer* NvFlexExtCreateContainer(NvFlexLibrary* flexLib, NvFlexSolver* solver, int maxParticles) { NvFlexExtContainer* c = new NvFlexExtContainer(flexLib); c->mSolver = solver; c->mFlexLib = flexLib; c->mMaxParticles = maxParticles; // initialize free list c->mFreeList.resize(maxParticles); for (int i=0; i < maxParticles; ++i) c->mFreeList[i] = i; c->mActiveList.init(maxParticles); c->mParticles.init(maxParticles); c->mParticlesRest.init(maxParticles); c->mVelocities.init(maxParticles); c->mPhases.init(maxParticles); c->mNormals.init(maxParticles); // ensure we have the corerct CUDA context set NvFlexAcquireContext(flexLib); c->mBoundsLower.init(1); c->mBoundsUpper.init(1); NvFlexRestoreContext(flexLib); c->mNeedsCompact = false; return c; } void NvFlexExtDestroyContainer(NvFlexExtContainer* c) { // ensure we have the corerct CUDA context set NvFlexLibrary* lib = c->mFlexLib; NvFlexAcquireContext(lib); delete c; NvFlexRestoreContext(lib); } int NvFlexExtAllocParticles(NvFlexExtContainer* c, int n, int* indices) { const int numToAlloc = Min(int(c->mFreeList.size()), n); const int start = int(c->mFreeList.size())-numToAlloc; if (numToAlloc) { memcpy(indices, &c->mFreeList[start], numToAlloc*sizeof(int)); c->mFreeList.resize(start); } c->mNeedsActiveListRebuild = true; return numToAlloc; } void NvFlexExtFreeParticles(NvFlexExtContainer* c, int n, const int* indices) { #if _DEBUG for (int i=0; i < n; ++i) { // check valid values assert(indices[i] >= 0 && indices[i] < int(c->mFreeList.capacity())); // check for double delete assert(std::find(c->mFreeList.begin(), c->mFreeList.end(), indices[i]) == c->mFreeList.end()); } #endif c->mFreeList.insert(c->mFreeList.end(), indices, indices+n); c->mNeedsActiveListRebuild = true; } int NvFlexExtGetActiveList(NvFlexExtContainer* c, int* indices) { int count = 0; Bitmap inactive(c->mMaxParticles); // create bitmap for (size_t i=0; i < c->mFreeList.size(); ++i) { // if this fires then somehow a duplicate has ended up in the free list (double delete) assert(!inactive.IsSet(c->mFreeList[i])); inactive.Set(c->mFreeList[i]); } // iterate bitmap to find active elements for (int i=0; i < c->mMaxParticles; ++i) if (inactive.IsSet(i) == false) indices[count++] = i; return count; } NvFlexExtParticleData NvFlexExtMapParticleData(NvFlexExtContainer* c) { NvFlexExtParticleData data; c->mParticles.map(); c->mParticlesRest.map(); c->mVelocities.map(); c->mPhases.map(); c->mNormals.map(); c->mBoundsLower.map(); c->mBoundsUpper.map(); if (c->mParticles.size()) data.particles = (float*)&c->mParticles[0]; if (c->mParticlesRest.size()) data.restParticles = (float*)&c->mParticlesRest[0]; if (c->mVelocities.size()) data.velocities = (float*)&c->mVelocities[0]; if (c->mPhases.size()) data.phases = (int*)&c->mPhases[0]; if (c->mNormals.size()) data.normals = (float*)&c->mNormals[0]; data.lower = c->mBoundsLower[0]; data.upper = c->mBoundsUpper[0]; return data; } void NvFlexExtUnmapParticleData(NvFlexExtContainer*c) { c->mParticles.unmap(); c->mParticlesRest.unmap(); c->mVelocities.unmap(); c->mPhases.unmap(); c->mNormals.unmap(); c->mBoundsLower.unmap(); c->mBoundsUpper.unmap(); } NvFlexExtTriangleData NvFlexExtMapTriangleData(NvFlexExtContainer* c) { NvFlexExtTriangleData data; c->mTriangleIndices.map(); c->mTriangleNormals.map(); if (c->mTriangleIndices.size()) data.indices = &c->mTriangleIndices[0]; if (c->mTriangleNormals.size()) data.normals = (float*)&c->mTriangleNormals[0]; return data; } void NvFlexExtUnmapTriangleData(NvFlexExtContainer* c) { c->mTriangleIndices.unmap(); c->mTriangleNormals.unmap(); } NvFlexExtShapeData NvFlexExtMapShapeData(NvFlexExtContainer* c) { NvFlexExtShapeData data; c->mShapeRotations.map(); c->mShapeTranslations.map(); if (c->mShapeRotations.size()) data.rotations = (float*)&c->mShapeRotations[0]; if (c->mShapeTranslations.size()) data.positions = (float*)&c->mShapeTranslations[0]; return data; } void NvFlexExtUnmapShapeData(NvFlexExtContainer* c) { c->mShapeRotations.unmap(); c->mShapeTranslations.unmap(); } NvFlexExtInstance* NvFlexExtCreateInstance(NvFlexExtContainer* c, NvFlexExtParticleData* particleData, const NvFlexExtAsset* asset, const float* transform, float vx, float vy, float vz, int phase, float invMassScale) { const int numParticles = asset->numParticles; // check if asset will fit if (int(c->mFreeList.size()) < numParticles) return NULL; NvFlexExtInstance* inst = new NvFlexExtInstance(); inst->asset = asset; inst->triangleIndex = -1; inst->shapeIndex = -1; inst->inflatableIndex = -1; inst->userData = NULL; inst->numParticles = numParticles; assert(inst->numParticles <= asset->maxParticles); // allocate particles for instance inst->particleIndices = new int[asset->maxParticles]; int n = NvFlexExtAllocParticles(c, numParticles, &inst->particleIndices[0]); assert(n == numParticles); (void)n; c->mInstances.push_back(inst); const Matrix44 xform(transform); for (int i=0; i < numParticles; ++i) { const int index = inst->particleIndices[i]; // add transformed particles to the locked particle data ((Vec4*)(particleData->particles))[index] = xform*Vec4(Vec3(&asset->particles[i*4]), 1.0f); ((Vec4*)(particleData->particles))[index].w = asset->particles[i*4+3]*invMassScale; ((Vec4*)(particleData->restParticles))[index] = Vec4(&asset->particles[i*4]); ((Vec3*)(particleData->velocities))[index] = Vec3(vx, vy, vz); ((int*)(particleData->phases))[index] = phase; ((Vec4*)(particleData->normals))[index] = Vec4(0.0f); } const int numShapes = asset->numShapes; // allocate memory for shape transforms Vec3* shapeTranslations = new Vec3[numShapes]; Quat* shapeRotations = new Quat[numShapes]; Quat rotation = Quat(Matrix33(xform.GetAxis(0), xform.GetAxis(1), xform.GetAxis(2))); for (int i=0; i < numShapes; ++i) { shapeTranslations[i] = Vec3(xform*Vec4(asset->shapeCenters[i*3+0], asset->shapeCenters[i*3+1], asset->shapeCenters[i*3+2], 1.0)); shapeRotations[i] = rotation; } inst->shapeTranslations = (float*)shapeTranslations; inst->shapeRotations = (float*)shapeRotations; // mark container as dirty c->mNeedsCompact = true; c->mNeedsActiveListRebuild = true; return inst; } void NvFlexExtDestroyInstance(NvFlexExtContainer* c, const NvFlexExtInstance* inst) { NvFlexExtFreeParticles(c, inst->numParticles, &inst->particleIndices[0]); delete[] inst->particleIndices; delete[] inst->shapeRotations; delete[] inst->shapeTranslations; // TODO: O(N) remove std::vector::iterator iter = std::find(c->mInstances.begin(), c->mInstances.end(), inst); assert(iter != c->mInstances.end()); c->mInstances.erase(iter); c->mNeedsCompact = true; c->mNeedsActiveListRebuild = true; delete inst; } void NvFlexExtTickContainer(NvFlexExtContainer* c, float dt, int substeps, bool enableTiming) { // update the device NvFlexExtPushToDevice(c); // update solver NvFlexUpdateSolver(c->mSolver, dt, substeps, enableTiming); // update host NvFlexExtPullFromDevice(c); } void NvFlexExtNotifyAssetChanged(NvFlexExtContainer* c, const NvFlexExtAsset* asset) { c->mNeedsCompact = true; } void NvFlexExtPushToDevice(NvFlexExtContainer* c) { if (c->mNeedsActiveListRebuild) { // update active list c->mActiveList.map(); int n = NvFlexExtGetActiveList(c, &c->mActiveList[0]); c->mActiveList.unmap(); NvFlexSetActive(c->mSolver, c->mActiveList.buffer, NULL); NvFlexSetActiveCount(c->mSolver, n); c->mNeedsActiveListRebuild = false; } // push any changes to solver NvFlexSetParticles(c->mSolver, c->mParticles.buffer, NULL); NvFlexSetRestParticles(c->mSolver, c->mParticlesRest.buffer, NULL); NvFlexSetVelocities(c->mSolver, c->mVelocities.buffer, NULL); NvFlexSetPhases(c->mSolver, c->mPhases.buffer, NULL); NvFlexSetNormals(c->mSolver, c->mNormals.buffer, NULL); if (c->mNeedsCompact) CompactObjects(c); } void NvFlexExtPullFromDevice(NvFlexExtContainer* c) { // read back particle data NvFlexGetParticles(c->mSolver, c->mParticles.buffer, NULL); NvFlexGetVelocities(c->mSolver, c->mVelocities.buffer, NULL); NvFlexGetPhases(c->mSolver, c->mPhases.buffer, NULL); NvFlexGetNormals(c->mSolver, c->mNormals.buffer, NULL); NvFlexGetBounds(c->mSolver, c->mBoundsLower.buffer, c->mBoundsUpper.buffer); // read back shape transforms if (c->mShapeCoefficients.size()) NvFlexGetRigids(c->mSolver, NULL, NULL, NULL, NULL, NULL, NULL, NULL, c->mShapeRotations.buffer, c->mShapeTranslations.buffer); } void NvFlexExtUpdateInstances(NvFlexExtContainer* c) { c->mShapeTranslations.map(); c->mShapeRotations.map(); for (int i=0; i < int(c->mInstances.size()); ++i) { NvFlexExtInstance* inst = c->mInstances[i]; // copy data back to per-instance memory from the container's memory const int numShapes = inst->asset->numShapes; const int shapeStart = inst->shapeIndex; if (shapeStart == -1) continue; for (int s=0; s < numShapes; ++s) { ((Vec3*)inst->shapeTranslations)[s] = c->mShapeTranslations[shapeStart + s]; ((Quat*)inst->shapeRotations)[s] = c->mShapeRotations[shapeStart + s]; } } for (int i = 0; i < int(c->mSoftJoints.size()); ++i) { NvFlexExtSoftJoint* joint = c->mSoftJoints[i]; const int shapeStart = joint->shapeIndex; // Here we compute the COM only once instead of in NvFlexExtCreateSoftJoint() to avoid buffer mapping issue if (!joint->initialized) { // Calculate the center of mass of the new shape matching constraint given a set of joint particles and its indices // To improve the accuracy of the result, first transform the particlePosition to relative coordinates (by finding the mean and subtracting that from all positions) // Note: If this is not done, one might see ghost forces if the mean of the particlePosition is far from the origin. Vec3 shapeOffset(0.0f); for (int i = 0; i < joint->numParticles; ++i) { const Vec4 particlePosition = c->mParticles[joint->particleIndices[i]]; shapeOffset += Vec3(particlePosition); } shapeOffset /= float(joint->numParticles); Vec3 com; for (int i = 0; i < joint->numParticles; ++i) { const Vec4 particlePosition = c->mParticles[joint->particleIndices[i]]; // By subtracting shapeOffset the calculation is done in relative coordinates com += Vec3(particlePosition) - shapeOffset; } com /= float(joint->numParticles); // Add the shapeOffset to switch back to absolute coordinates com += shapeOffset; // update per-joint shapeTranslations and copy to the container's memory joint->shapeTranslations[0] = com.x; joint->shapeTranslations[1] = com.y; joint->shapeTranslations[2] = com.z; joint->initialized = true; // Complete joint initilization process } else { joint->shapeTranslations[0] = c->mShapeTranslations[shapeStart].x; joint->shapeTranslations[1] = c->mShapeTranslations[shapeStart].y; joint->shapeTranslations[2] = c->mShapeTranslations[shapeStart].z; } // copy data back to per-joint memory from the container's memory joint->shapeRotations[0] = c->mShapeRotations[shapeStart].x; joint->shapeRotations[1] = c->mShapeRotations[shapeStart].y; joint->shapeRotations[2] = c->mShapeRotations[shapeStart].z; joint->shapeRotations[3] = c->mShapeRotations[shapeStart].w; } c->mShapeTranslations.unmap(); c->mShapeRotations.unmap(); } void NvFlexExtDestroyAsset(NvFlexExtAsset* asset) { delete[] asset->particles; delete[] asset->springIndices; delete[] asset->springCoefficients; delete[] asset->springRestLengths; delete[] asset->triangleIndices; delete[] asset->shapeIndices; delete[] asset->shapeOffsets; delete[] asset->shapeCenters; delete[] asset->shapeCoefficients; delete[] asset->shapePlasticThresholds; delete[] asset->shapePlasticCreeps; delete asset; } NvFlexExtSoftJoint* NvFlexExtCreateSoftJoint(NvFlexExtContainer* c, const int* particleIndices, const float* particleLocalPositions, const int numJointParticles, const float stiffness) { NvFlexExtSoftJoint* joint = new NvFlexExtSoftJoint(); joint->particleIndices = new int[numJointParticles]; memcpy(joint->particleIndices, particleIndices, sizeof(int) * numJointParticles); joint->particleLocalPositions = new float[3 * numJointParticles]; memcpy(joint->particleLocalPositions, particleLocalPositions, 3 * sizeof(float)*numJointParticles); // initialize with Quat() joint->shapeRotations[0] = Quat().x; joint->shapeRotations[1] = Quat().y; joint->shapeRotations[2] = Quat().z; joint->shapeRotations[3] = Quat().w; joint->numParticles = numJointParticles; joint->stiffness = stiffness; joint->initialized = false; // Initialization will be fully completed in NvFlexExtUpdateInstances() c->mSoftJoints.push_back(joint); // mark container as dirty c->mNeedsCompact = true; return joint; } void NvFlexExtDestroySoftJoint(NvFlexExtContainer* c, NvFlexExtSoftJoint* joint) { delete[] joint->particleIndices; delete[] joint->particleLocalPositions; // TODO: O(N) remove std::vector::iterator iter = std::find(c->mSoftJoints.begin(), c->mSoftJoints.end(), joint); assert(iter != c->mSoftJoints.end()); c->mSoftJoints.erase(iter); c->mNeedsCompact = true; delete joint; } void NvFlexExtSoftJointSetTransform(NvFlexExtContainer* c, NvFlexExtSoftJoint* joint, const float* newPosition, const float* newRotation) { // calculate transform from old position to new position Matrix44 LocalFromOld = AffineInverse(TranslationMatrix(Point3(joint->shapeTranslations))*RotationMatrix(joint->shapeRotations)); Matrix44 NewFromLocal = TranslationMatrix(Point3(newPosition))*RotationMatrix(newRotation); Matrix44 transform = NewFromLocal*LocalFromOld; // transform soft joint particles to new location //---------------------- // map buffers c->mParticles.map(); for (int i = 0; i < joint->numParticles; ++i) { const Vec3 particlePosition = Vec3(c->mParticles[joint->particleIndices[i]]); Vec4 particleNewPostion = transform * Vec4(particlePosition, 1.0f); // update soft joint particles c->mParticles[joint->particleIndices[i]].x = particleNewPostion.x; c->mParticles[joint->particleIndices[i]].y = particleNewPostion.y; c->mParticles[joint->particleIndices[i]].z = particleNewPostion.z; } joint->shapeTranslations[0] = newPosition[0]; joint->shapeTranslations[1] = newPosition[1]; joint->shapeTranslations[2] = newPosition[2]; joint->shapeRotations[0] = newRotation[0]; joint->shapeRotations[1] = newRotation[1]; joint->shapeRotations[2] = newRotation[2]; joint->shapeRotations[3] = newRotation[3]; //---------------------- // unmap buffers c->mParticles.unmap(); }