aboutsummaryrefslogtreecommitdiff
path: root/NvCloth/src/ClothImpl.h
diff options
context:
space:
mode:
authormtamis <[email protected]>2017-02-15 16:06:25 +0100
committermtamis <[email protected]>2017-02-15 16:06:25 +0100
commit85305930aeeb1d513e23522bd91f29ba81aa6d14 (patch)
tree45f1bb20a45a300d1fef107e436cac95602a0e57 /NvCloth/src/ClothImpl.h
downloadnvcloth-85305930aeeb1d513e23522bd91f29ba81aa6d14.tar.xz
nvcloth-85305930aeeb1d513e23522bd91f29ba81aa6d14.zip
NvCloth library v1.0.0
Diffstat (limited to 'NvCloth/src/ClothImpl.h')
-rw-r--r--NvCloth/src/ClothImpl.h1393
1 files changed, 1393 insertions, 0 deletions
diff --git a/NvCloth/src/ClothImpl.h b/NvCloth/src/ClothImpl.h
new file mode 100644
index 0000000..85d201a
--- /dev/null
+++ b/NvCloth/src/ClothImpl.h
@@ -0,0 +1,1393 @@
+// 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) 2008-2017 NVIDIA Corporation. All rights reserved.
+// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
+// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
+
+#pragma once
+
+#include "NvCloth/Cloth.h"
+#include "NvCloth/Fabric.h"
+#include <foundation/PxVec4.h>
+#include <foundation/PxVec3.h>
+#include <PsMathUtils.h>
+#include <cmath>
+
+namespace nv
+{
+namespace cloth
+{
+
+// SwCloth or CuCloth aggregate implementing the Cloth interface
+// Member specializations are implemented in Sw/CuCloth.cpp
+template <typename T>
+class ClothImpl : public Cloth
+{
+ ClothImpl(const ClothImpl&);
+
+ public:
+ ClothImpl& operator = (const ClothImpl&);
+
+ typedef T ClothType;
+ typedef typename ClothType::FactoryType FactoryType;
+ typedef typename ClothType::FabricType FabricType;
+ typedef typename ClothType::ContextLockType ContextLockType;
+
+ ClothImpl(Factory&, Fabric&, Range<const physx::PxVec4>);
+ ClothImpl(Factory&, const ClothImpl&);
+
+ virtual Cloth* clone(Factory& factory) const;
+
+ virtual Fabric& getFabric() const;
+ virtual Factory& getFactory() const;
+
+ virtual uint32_t getNumParticles() const;
+ virtual void lockParticles() const;
+ virtual void unlockParticles() const;
+ virtual MappedRange<physx::PxVec4> getCurrentParticles();
+ virtual MappedRange<const physx::PxVec4> getCurrentParticles() const;
+ virtual MappedRange<physx::PxVec4> getPreviousParticles();
+ virtual MappedRange<const physx::PxVec4> getPreviousParticles() const;
+ virtual GpuParticles getGpuParticles();
+
+ virtual void setTranslation(const physx::PxVec3& trans);
+ virtual void setRotation(const physx::PxQuat& rot);
+
+ virtual const physx::PxVec3& getTranslation() const;
+ virtual const physx::PxQuat& getRotation() const;
+
+ virtual void clearInertia();
+
+ virtual void teleport(const physx::PxVec3& delta);
+
+ virtual float getPreviousIterationDt() const;
+ virtual void setGravity(const physx::PxVec3& gravity);
+ virtual physx::PxVec3 getGravity() const;
+ virtual void setDamping(const physx::PxVec3& damping);
+ virtual physx::PxVec3 getDamping() const;
+ virtual void setLinearDrag(const physx::PxVec3& drag);
+ virtual physx::PxVec3 getLinearDrag() const;
+ virtual void setAngularDrag(const physx::PxVec3& drag);
+ virtual physx::PxVec3 getAngularDrag() const;
+ virtual void setLinearInertia(const physx::PxVec3& inertia);
+ virtual physx::PxVec3 getLinearInertia() const;
+ virtual void setAngularInertia(const physx::PxVec3& inertia);
+ virtual physx::PxVec3 getAngularInertia() const;
+ virtual void setCentrifugalInertia(const physx::PxVec3& inertia);
+ virtual physx::PxVec3 getCentrifugalInertia() const;
+
+ virtual void setSolverFrequency(float frequency);
+ virtual float getSolverFrequency() const;
+
+ virtual void setStiffnessFrequency(float frequency);
+ virtual float getStiffnessFrequency() const;
+
+ virtual void setAcceleationFilterWidth(uint32_t);
+ virtual uint32_t getAccelerationFilterWidth() const;
+
+ virtual void setPhaseConfig(Range<const PhaseConfig> configs);
+
+ virtual void setSpheres(Range<const physx::PxVec4>, uint32_t first, uint32_t last);
+ virtual uint32_t getNumSpheres() const;
+
+ virtual void setCapsules(Range<const uint32_t>, uint32_t first, uint32_t last);
+ virtual uint32_t getNumCapsules() const;
+
+ virtual void setPlanes(Range<const physx::PxVec4>, uint32_t first, uint32_t last);
+ virtual uint32_t getNumPlanes() const;
+
+ virtual void setConvexes(Range<const uint32_t>, uint32_t first, uint32_t last);
+ virtual uint32_t getNumConvexes() const;
+
+ virtual void setTriangles(Range<const physx::PxVec3>, uint32_t first, uint32_t last);
+ virtual void setTriangles(Range<const physx::PxVec3>, Range<const physx::PxVec3>, uint32_t first);
+ virtual uint32_t getNumTriangles() const;
+
+ virtual bool isContinuousCollisionEnabled() const;
+ virtual void enableContinuousCollision(bool);
+
+ virtual float getCollisionMassScale() const;
+ virtual void setCollisionMassScale(float);
+ virtual void setFriction(float friction);
+ virtual float getFriction() const;
+
+ virtual void setVirtualParticles(Range<const uint32_t[4]>, Range<const physx::PxVec3>);
+ virtual uint32_t getNumVirtualParticles() const;
+ virtual uint32_t getNumVirtualParticleWeights() const;
+
+ virtual void setTetherConstraintScale(float scale);
+ virtual float getTetherConstraintScale() const;
+ virtual void setTetherConstraintStiffness(float stiffness);
+ virtual float getTetherConstraintStiffness() const;
+
+ virtual Range<physx::PxVec4> getMotionConstraints();
+ virtual void clearMotionConstraints();
+ virtual uint32_t getNumMotionConstraints() const;
+ virtual void setMotionConstraintScaleBias(float scale, float bias);
+ virtual float getMotionConstraintScale() const;
+ virtual float getMotionConstraintBias() const;
+ virtual void setMotionConstraintStiffness(float stiffness);
+ virtual float getMotionConstraintStiffness() const;
+
+ virtual Range<physx::PxVec4> getSeparationConstraints();
+ virtual void clearSeparationConstraints();
+ virtual uint32_t getNumSeparationConstraints() const;
+
+ virtual void clearInterpolation();
+
+ virtual Range<physx::PxVec4> getParticleAccelerations();
+ virtual void clearParticleAccelerations();
+ virtual uint32_t getNumParticleAccelerations() const;
+
+ virtual void setWindVelocity(physx::PxVec3);
+ virtual physx::PxVec3 getWindVelocity() const;
+ virtual void setDragCoefficient(float);
+ virtual float getDragCoefficient() const;
+ virtual void setLiftCoefficient(float);
+ virtual float getLiftCoefficient() const;
+
+ virtual void setSelfCollisionDistance(float);
+ virtual float getSelfCollisionDistance() const;
+ virtual void setSelfCollisionStiffness(float);
+ virtual float getSelfCollisionStiffness() const;
+
+ virtual void setSelfCollisionIndices(Range<const uint32_t>);
+ virtual uint32_t getNumSelfCollisionIndices() const;
+
+ virtual void setRestPositions(Range<const physx::PxVec4>);
+ virtual uint32_t getNumRestPositions() const;
+
+ virtual const physx::PxVec3& getBoundingBoxCenter() const;
+ virtual const physx::PxVec3& getBoundingBoxScale() const;
+
+ virtual void setSleepThreshold(float);
+ virtual float getSleepThreshold() const;
+ virtual void setSleepTestInterval(uint32_t);
+ virtual uint32_t getSleepTestInterval() const;
+ virtual void setSleepAfterCount(uint32_t);
+ virtual uint32_t getSleepAfterCount() const;
+ virtual uint32_t getSleepPassCount() const;
+ virtual bool isAsleep() const;
+ virtual void putToSleep();
+ virtual void wakeUp();
+
+ virtual void setUserData(void*);
+ virtual void* getUserData() const;
+
+ // helper function
+ template <typename U>
+ MappedRange<U> getMappedParticles(U* data) const;
+
+ ClothType mCloth;
+};
+
+class SwCloth;
+typedef ClothImpl<SwCloth> SwClothImpl;
+
+class CuCloth;
+typedef ClothImpl<CuCloth> CuClothImpl;
+
+class DxCloth;
+typedef ClothImpl<DxCloth> DxClothImpl;
+
+template <typename T>
+ClothImpl<T>::ClothImpl(Factory& factory, Fabric& fabric, Range<const physx::PxVec4> particles)
+: mCloth(static_cast<FactoryType&>(factory), static_cast<FabricType&>(fabric), particles)
+{
+ // fabric and cloth need to be created by the same factory
+ NV_CLOTH_ASSERT(&fabric.getFactory() == &factory);
+}
+
+template <typename T>
+ClothImpl<T>::ClothImpl(Factory& factory, const ClothImpl& impl)
+: mCloth(static_cast<FactoryType&>(factory), impl.mCloth)
+{
+}
+
+template <typename T>
+inline Fabric& ClothImpl<T>::getFabric() const
+{
+ return mCloth.mFabric;
+}
+
+template <typename T>
+inline Factory& ClothImpl<T>::getFactory() const
+{
+ return mCloth.mFactory;
+}
+
+template <typename T>
+inline void ClothImpl<T>::setTranslation(const physx::PxVec3& trans)
+{
+ physx::PxVec3 t = reinterpret_cast<const physx::PxVec3&>(trans);
+ if (t == mCloth.mTargetMotion.p)
+ return;
+
+ mCloth.mTargetMotion.p = t;
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline void ClothImpl<T>::setRotation(const physx::PxQuat& q)
+{
+ if ((q - mCloth.mTargetMotion.q).magnitudeSquared() == 0.0f)
+ return;
+
+ mCloth.mTargetMotion.q = q;
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline const physx::PxVec3& ClothImpl<T>::getTranslation() const
+{
+ return mCloth.mTargetMotion.p;
+}
+
+template <typename T>
+inline const physx::PxQuat& ClothImpl<T>::getRotation() const
+{
+ return mCloth.mTargetMotion.q;
+}
+
+template <typename T>
+inline void ClothImpl<T>::clearInertia()
+{
+ mCloth.mCurrentMotion = mCloth.mTargetMotion;
+ mCloth.mLinearVelocity = physx::PxVec3(0.0);
+ mCloth.mAngularVelocity = physx::PxVec3(0.0);
+
+ mCloth.wakeUp();
+}
+
+// Fixed 4505:local function has been removed
+template <typename T>
+inline void ClothImpl<T>::teleport(const physx::PxVec3& delta)
+{
+ mCloth.mCurrentMotion.p += delta;
+ mCloth.mTargetMotion.p += delta;
+}
+
+template <typename T>
+inline float ClothImpl<T>::getPreviousIterationDt() const
+{
+ return mCloth.mPrevIterDt;
+}
+
+template <typename T>
+inline void ClothImpl<T>::setGravity(const physx::PxVec3& gravity)
+{
+ physx::PxVec3 value = gravity;
+ if (value == mCloth.mGravity)
+ return;
+
+ mCloth.mGravity = value;
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline physx::PxVec3 ClothImpl<T>::getGravity() const
+{
+ return mCloth.mGravity;
+}
+
+inline float safeLog2(float x)
+{
+ return x ? physx::shdfnd::log2(x) : -FLT_MAX_EXP;
+}
+
+inline physx::PxVec3 safeLog2(const physx::PxVec3& v)
+{
+ return physx::PxVec3( safeLog2(v.x), safeLog2(v.y), safeLog2(v.z) );
+}
+
+inline float safeExp2(float x)
+{
+ if (x <= -FLT_MAX_EXP)
+ return 0.0f;
+ else
+ return physx::shdfnd::exp2(x);
+}
+
+inline physx::PxVec3 safeExp2(const physx::PxVec3& v)
+{
+ return physx::PxVec3( safeExp2(v.x), safeExp2(v.y), safeExp2(v.z) );
+}
+
+template <typename T>
+inline void ClothImpl<T>::setDamping(const physx::PxVec3& damping)
+{
+ physx::PxVec3 value = safeLog2(physx::PxVec3(1.f) - damping);
+ if (value == mCloth.mLogDamping)
+ return;
+
+ mCloth.mLogDamping = value;
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline physx::PxVec3 ClothImpl<T>::getDamping() const
+{
+ return physx::PxVec3(1.f) - safeExp2(mCloth.mLogDamping);
+}
+
+template <typename T>
+inline void ClothImpl<T>::setLinearDrag(const physx::PxVec3& drag)
+{
+ physx::PxVec3 value = safeLog2(physx::PxVec3(1.f) - drag);
+ if (value == mCloth.mLinearLogDrag)
+ return;
+
+ mCloth.mLinearLogDrag = value;
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline physx::PxVec3 ClothImpl<T>::getLinearDrag() const
+{
+ return physx::PxVec3(1.f) - safeExp2(mCloth.mLinearLogDrag);
+}
+
+template <typename T>
+inline void ClothImpl<T>::setAngularDrag(const physx::PxVec3& drag)
+{
+ physx::PxVec3 value = safeLog2(physx::PxVec3(1.f) - drag);
+ if (value == mCloth.mAngularLogDrag)
+ return;
+
+ mCloth.mAngularLogDrag = value;
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline physx::PxVec3 ClothImpl<T>::getAngularDrag() const
+{
+ return physx::PxVec3(1.f) - safeExp2(mCloth.mAngularLogDrag);
+}
+
+template <typename T>
+inline void ClothImpl<T>::setLinearInertia(const physx::PxVec3& inertia)
+{
+ physx::PxVec3 value = inertia;
+ if (value == mCloth.mLinearInertia)
+ return;
+
+ mCloth.mLinearInertia = value;
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline physx::PxVec3 ClothImpl<T>::getLinearInertia() const
+{
+ return mCloth.mLinearInertia;
+}
+
+template <typename T>
+inline void ClothImpl<T>::setAngularInertia(const physx::PxVec3& inertia)
+{
+ physx::PxVec3 value = inertia;
+ if (value == mCloth.mAngularInertia)
+ return;
+
+ mCloth.mAngularInertia = value;
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline physx::PxVec3 ClothImpl<T>::getAngularInertia() const
+{
+ return mCloth.mAngularInertia;
+}
+
+template <typename T>
+inline void ClothImpl<T>::setCentrifugalInertia(const physx::PxVec3& inertia)
+{
+ physx::PxVec3 value = inertia;
+ if (value == mCloth.mCentrifugalInertia)
+ return;
+
+ mCloth.mCentrifugalInertia = value;
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline physx::PxVec3 ClothImpl<T>::getCentrifugalInertia() const
+{
+ return mCloth.mCentrifugalInertia;
+}
+
+template <typename T>
+inline void ClothImpl<T>::setSolverFrequency(float frequency)
+{
+ if (frequency == mCloth.mSolverFrequency)
+ return;
+
+ mCloth.mSolverFrequency = frequency;
+ mCloth.mClothCostDirty = true;
+ mCloth.mIterDtAvg.reset();
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline float ClothImpl<T>::getSolverFrequency() const
+{
+ return mCloth.mSolverFrequency;
+}
+
+template <typename T>
+inline void ClothImpl<T>::setStiffnessFrequency(float frequency)
+{
+ if (frequency == mCloth.mStiffnessFrequency)
+ return;
+
+ mCloth.mStiffnessFrequency = frequency;
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline float ClothImpl<T>::getStiffnessFrequency() const
+{
+ return mCloth.mStiffnessFrequency;
+}
+
+template <typename T>
+inline void ClothImpl<T>::setAcceleationFilterWidth(uint32_t n)
+{
+ mCloth.mIterDtAvg.resize(n);
+}
+
+template <typename T>
+inline uint32_t ClothImpl<T>::getAccelerationFilterWidth() const
+{
+ return mCloth.mIterDtAvg.size();
+}
+
+// move a subarray
+template <typename Iter>
+void move(Iter it, uint32_t first, uint32_t last, uint32_t result)
+{
+ if (result > first)
+ {
+ result += last - first;
+ while (first < last)
+ it[--result] = it[--last];
+ }
+ else
+ {
+ while (first < last)
+ it[result++] = it[first++];
+ }
+}
+
+// update capsule index
+inline bool updateIndex(uint32_t& index, uint32_t first, int32_t delta)
+{
+ return index >= first && int32_t(index += delta) < int32_t(first);
+}
+
+template <typename T>
+inline void ClothImpl<T>::setSpheres(Range<const physx::PxVec4> spheres, uint32_t first, uint32_t last)
+{
+ uint32_t oldSize = uint32_t(mCloth.mStartCollisionSpheres.size());
+ uint32_t newSize = uint32_t(spheres.size()) + oldSize - last + first;
+
+ NV_CLOTH_ASSERT(newSize <= 32);
+ NV_CLOTH_ASSERT(first <= oldSize);
+ NV_CLOTH_ASSERT(last <= oldSize);
+
+#if PX_DEBUG
+ for (const physx::PxVec4* it = spheres.begin(); it < spheres.end(); ++it)
+ NV_CLOTH_ASSERT(it->w >= 0.0f);
+#endif
+
+ if (!oldSize && !newSize)
+ return;
+
+ if (!oldSize)
+ {
+ ContextLockType contextLock(mCloth.mFactory);
+ mCloth.mStartCollisionSpheres.assign(spheres.begin(), spheres.end());
+ mCloth.notifyChanged();
+ }
+ else
+ {
+ if (std::max(oldSize, newSize) >
+ std::min(mCloth.mStartCollisionSpheres.capacity(), mCloth.mTargetCollisionSpheres.capacity()))
+ {
+ ContextLockType contextLock(mCloth.mFactory);
+ mCloth.mStartCollisionSpheres.reserve(newSize);
+ mCloth.mTargetCollisionSpheres.reserve(std::max(oldSize, newSize));
+ }
+
+ typename T::MappedVec4fVectorType start = mCloth.mStartCollisionSpheres;
+ typename T::MappedVec4fVectorType target = mCloth.mTargetCollisionSpheres;
+
+ // fill target from start
+ for (uint32_t i = uint32_t(target.size()); i < oldSize; ++i)
+ target.pushBack(start[i]);
+
+ // resize to larger of oldSize and newSize
+ start.resize(std::max(oldSize, newSize), physx::PxVec4(0.0f));
+ target.resize(std::max(oldSize, newSize), physx::PxVec4(0.0f));
+
+ if (int32_t delta = int32_t(newSize - oldSize))
+ {
+ // move past-range elements to new place
+ move(start.begin(), last, oldSize, last + delta);
+ move(target.begin(), last, oldSize, last + delta);
+
+ // fill new elements from spheres
+ for (uint32_t i = last; i < last + delta; ++i)
+ start[i] = spheres[i - first];
+
+ // adjust capsule indices
+ typename T::MappedIndexVectorType indices = mCloth.mCapsuleIndices;
+ Vector<IndexPair>::Type::Iterator cIt, cEnd = indices.end();
+ for (cIt = indices.begin(); cIt != cEnd;)
+ {
+ bool removed = false;
+ removed |= updateIndex(cIt->first, last + std::min(0, delta), int32_t(delta));
+ removed |= updateIndex(cIt->second, last + std::min(0, delta), int32_t(delta));
+ if (!removed)
+ ++cIt;
+ else
+ {
+ indices.replaceWithLast(cIt);
+ cEnd = indices.end();
+ }
+ }
+
+ start.resize(newSize);
+ target.resize(newSize);
+
+ mCloth.notifyChanged();
+ }
+
+ // fill target elements with spheres
+ for (uint32_t i = 0; i < spheres.size(); ++i)
+ target[first + i] = spheres[i];
+ }
+
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline uint32_t ClothImpl<T>::getNumSpheres() const
+{
+ return uint32_t(mCloth.mStartCollisionSpheres.size());
+}
+
+// Fixed 4505:local function has been removed
+template <typename T>
+inline void ClothImpl<T>::setCapsules(Range<const uint32_t> capsules, uint32_t first, uint32_t last)
+{
+ uint32_t oldSize = uint32_t(mCloth.mCapsuleIndices.size());
+ uint32_t newSize = uint32_t(capsules.size() / 2) + oldSize - last + first;
+
+ NV_CLOTH_ASSERT(newSize <= 32);
+ NV_CLOTH_ASSERT(first <= oldSize);
+ NV_CLOTH_ASSERT(last <= oldSize);
+
+ const IndexPair* srcIndices = reinterpret_cast<const IndexPair*>(capsules.begin());
+
+ if (mCloth.mCapsuleIndices.capacity() < newSize)
+ {
+ ContextLockType contextLock(mCloth.mFactory);
+ mCloth.mCapsuleIndices.reserve(newSize);
+ }
+
+ // resize to larger of oldSize and newSize
+ mCloth.mCapsuleIndices.resize(std::max(oldSize, newSize));
+
+ typename T::MappedIndexVectorType dstIndices = mCloth.mCapsuleIndices;
+
+ if (uint32_t delta = newSize - oldSize)
+ {
+ // move past-range elements to new place
+ move(dstIndices.begin(), last, oldSize, last + delta);
+
+ // fill new elements from capsules
+ for (uint32_t i = last; i < last + delta; ++i)
+ dstIndices[i] = srcIndices[i - first];
+
+ dstIndices.resize(newSize);
+ mCloth.notifyChanged();
+ }
+
+ // fill existing elements from capsules
+ for (uint32_t i = first; i < last; ++i)
+ dstIndices[i] = srcIndices[i - first];
+
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline uint32_t ClothImpl<T>::getNumCapsules() const
+{
+ return uint32_t(mCloth.mCapsuleIndices.size());
+}
+
+template <typename T>
+inline void ClothImpl<T>::setPlanes(Range<const physx::PxVec4> planes, uint32_t first, uint32_t last)
+{
+ uint32_t oldSize = uint32_t(mCloth.mStartCollisionPlanes.size());
+ uint32_t newSize = uint32_t(planes.size()) + oldSize - last + first;
+
+ NV_CLOTH_ASSERT(newSize <= 32);
+ NV_CLOTH_ASSERT(first <= oldSize);
+ NV_CLOTH_ASSERT(last <= oldSize);
+#if PX_DEBUG || PX_CHECKED
+ int logCount = 0;
+ for (int i = 0; i<static_cast<int>(planes.size()); i++)
+ {
+ if (fabsf(planes[i].getXYZ().magnitudeSquared() - 1.0f) > 0.01f)
+ {
+ if (logCount == 0)
+ NV_CLOTH_LOG_INVALID_PARAMETER("The plane normals passed to Cloth::setPlanes are not normalized. First error encounterd at plane %d (%f, %f, %f, %f)",
+ i, static_cast<double>(planes[i].x), static_cast<double>(planes[i].y), static_cast<double>(planes[i].z), static_cast<double>(planes[i].w));
+ logCount++;
+ }
+ }
+ if (logCount>1)
+ {
+ NV_CLOTH_LOG_INVALID_PARAMETER("This error was encountered %d more times.", logCount-1);
+ }
+#endif
+
+ if (!oldSize && !newSize)
+ return;
+
+ if (!oldSize)
+ {
+ ContextLockType contextLock(mCloth.mFactory);
+ mCloth.mStartCollisionPlanes.assign(planes.begin(), planes.end());
+ mCloth.notifyChanged();
+ }
+ else
+ {
+ if (std::max(oldSize, newSize) >
+ std::min(mCloth.mStartCollisionPlanes.capacity(), mCloth.mTargetCollisionPlanes.capacity()))
+ {
+ ContextLockType contextLock(mCloth.mFactory);
+ mCloth.mStartCollisionPlanes.reserve(newSize);
+ mCloth.mTargetCollisionPlanes.reserve(std::max(oldSize, newSize));
+ }
+
+ typename T::MappedVec4fVectorType start = mCloth.mStartCollisionPlanes;
+ typename T::MappedVec4fVectorType target = mCloth.mTargetCollisionPlanes;
+
+ // fill target from start
+ for (uint32_t i = target.size(); i < oldSize; ++i)
+ target.pushBack(start[i]);
+
+ // resize to larger of oldSize and newSize
+ start.resize(std::max(oldSize, newSize), physx::PxVec4(0.0f));
+ target.resize(std::max(oldSize, newSize), physx::PxVec4(0.0f));
+
+ if (int32_t delta = int32_t(newSize - oldSize))
+ {
+ // move past-range elements to new place
+ move(start.begin(), last, oldSize, last + delta);
+ move(target.begin(), last, oldSize, last + delta);
+
+ // fill new elements from planes
+ for (uint32_t i = last; i < last + delta; ++i)
+ start[i] = planes[i - first];
+
+ // adjust convex indices
+ uint32_t mask = (uint32_t(1) << (last + std::min(delta, 0))) - 1;
+ typename T::MappedMaskVectorType masks = mCloth.mConvexMasks;
+ Vector<uint32_t>::Type::Iterator cIt, cEnd = masks.end();
+ for (cIt = masks.begin(); cIt != cEnd;)
+ {
+ uint32_t convex = (*cIt & mask);
+ if (delta < 0)
+ convex |= *cIt >> -delta & ~mask;
+ else
+ convex |= (*cIt & ~mask) << delta;
+ if (convex)
+ *cIt++ = convex;
+ else
+ {
+ masks.replaceWithLast(cIt);
+ cEnd = masks.end();
+ }
+ }
+
+ start.resize(newSize);
+ target.resize(newSize);
+
+ mCloth.notifyChanged();
+ }
+
+ // fill target elements with planes
+ for (uint32_t i = 0; i < planes.size(); ++i)
+ target[first + i] = planes[i];
+ }
+
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline uint32_t ClothImpl<T>::getNumPlanes() const
+{
+ return uint32_t(mCloth.mStartCollisionPlanes.size());
+}
+
+template <typename T>
+inline void ClothImpl<T>::setConvexes(Range<const uint32_t> convexMasks, uint32_t first, uint32_t last)
+{
+ uint32_t oldSize = uint32_t(mCloth.mConvexMasks.size());
+ uint32_t newSize = uint32_t(convexMasks.size()) + oldSize - last + first;
+
+ NV_CLOTH_ASSERT(newSize <= 32);
+ NV_CLOTH_ASSERT(first <= oldSize);
+ NV_CLOTH_ASSERT(last <= oldSize);
+#if PX_DEBUG || PX_CHECKED
+ for (int i = 0; i<static_cast<int>(convexMasks.size()); i++)
+ {
+ if (convexMasks[i] == 0)
+ {
+ NV_CLOTH_LOG_INVALID_PARAMETER("Cloth::setConvexes expects bit masks of the form (1<<planeIndex1)|(1<<planeIndex2). 0 is not a valid mask/plane index. Error found in location %d", i);
+ continue;
+ }
+ }
+#endif
+
+ if (mCloth.mConvexMasks.capacity() < newSize)
+ {
+ ContextLockType contextLock(mCloth.mFactory);
+ mCloth.mConvexMasks.reserve(newSize);
+ }
+
+ // resize to larger of oldSize and newSize
+ mCloth.mConvexMasks.resize(std::max(oldSize, newSize));
+
+ if (uint32_t delta = newSize - oldSize)
+ {
+ typename T::MappedMaskVectorType masks = mCloth.mConvexMasks;
+
+ // move past-range elements to new place
+ move(masks.begin(), last, oldSize, last + delta);
+
+ // fill new elements from capsules
+ for (uint32_t i = last; i < last + delta; ++i)
+ masks[i] = convexMasks[i - first];
+
+ masks.resize(newSize);
+ mCloth.notifyChanged();
+ }
+
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline uint32_t ClothImpl<T>::getNumConvexes() const
+{
+ return uint32_t(mCloth.mConvexMasks.size());
+}
+
+template <typename T>
+inline void ClothImpl<T>::setTriangles(Range<const physx::PxVec3> triangles, uint32_t first, uint32_t last)
+{
+ // convert from triangle to vertex count
+ first *= 3;
+ last *= 3;
+
+ triangles = mCloth.clampTriangleCount(triangles, last - first);
+ NV_CLOTH_ASSERT(0 == triangles.size() % 3);
+
+ uint32_t oldSize = uint32_t(mCloth.mStartCollisionTriangles.size());
+ uint32_t newSize = uint32_t(triangles.size()) + oldSize - last + first;
+
+ NV_CLOTH_ASSERT(first <= oldSize);
+ NV_CLOTH_ASSERT(last <= oldSize);
+
+ if (!oldSize && !newSize)
+ return;
+
+ if (!oldSize)
+ {
+ ContextLockType contextLock(mCloth.mFactory);
+ mCloth.mStartCollisionTriangles.assign(triangles.begin(), triangles.end());
+ mCloth.notifyChanged();
+ }
+ else
+ {
+ if (std::max(oldSize, newSize) >
+ std::min(mCloth.mStartCollisionTriangles.capacity(), mCloth.mTargetCollisionTriangles.capacity()))
+ {
+ ContextLockType contextLock(mCloth.mFactory);
+ mCloth.mStartCollisionTriangles.reserve(newSize);
+ mCloth.mTargetCollisionTriangles.reserve(std::max(oldSize, newSize));
+ }
+
+ typename T::MappedVec3fVectorType start = mCloth.mStartCollisionTriangles;
+ typename T::MappedVec3fVectorType target = mCloth.mTargetCollisionTriangles;
+
+ // fill target from start
+ for (uint32_t i = target.size(); i < oldSize; ++i)
+ target.pushBack(start[i]);
+
+ // resize to larger of oldSize and newSize
+ start.resize(std::max(oldSize, newSize), physx::PxVec3(0.0f));
+ target.resize(std::max(oldSize, newSize), physx::PxVec3(0.0f));
+
+ if (int32_t delta = int32_t(newSize - oldSize))
+ {
+ // move past-range elements to new place
+ move(start.begin(), last, oldSize, last + delta);
+ move(target.begin(), last, oldSize, last + delta);
+
+ // fill new elements from planes
+ for (uint32_t i = last; i < last + delta; ++i)
+ start[i] = triangles[i - first];
+
+ start.resize(newSize);
+ target.resize(newSize);
+
+ mCloth.notifyChanged();
+ }
+ //////////////////////
+
+ // if (std::max(oldSize, newSize) >
+ // std::min(mCloth.mStartCollisionTriangles.capacity(), mCloth.mTargetCollisionTriangles.capacity()))
+ // {
+ // ContextLockType contextLock(mCloth.mFactory);
+ // mCloth.mStartCollisionTriangles.reserve(newSize);
+ // mCloth.mTargetCollisionTriangles.reserve(std::max(oldSize, newSize));
+ // }
+ //
+ // // fill target from start
+ // for (uint32_t i = mCloth.mTargetCollisionTriangles.size(); i < oldSize; ++i)
+ // mCloth.mTargetCollisionTriangles.pushBack(mCloth.mStartCollisionTriangles[i]);
+ //
+ // // resize to larger of oldSize and newSize
+ // mCloth.mStartCollisionTriangles.resize(std::max(oldSize, newSize));
+ // mCloth.mTargetCollisionTriangles.resize(std::max(oldSize, newSize));
+ //
+ // if (uint32_t delta = newSize - oldSize)
+ // {
+ // // move past-range elements to new place
+ // move(mCloth.mStartCollisionTriangles.begin(), last, oldSize, last + delta);
+ // move(mCloth.mTargetCollisionTriangles.begin(), last, oldSize, last + delta);
+ //
+ // // fill new elements from triangles
+ // for (uint32_t i = last; i < last + delta; ++i)
+ // mCloth.mStartCollisionTriangles[i] = triangles[i - first];
+ //
+ // mCloth.mStartCollisionTriangles.resize(newSize);
+ // mCloth.mTargetCollisionTriangles.resize(newSize);
+ //
+ // mCloth.notifyChanged();
+ // }
+
+ // fill target elements with triangles
+ // for (uint32_t i = 0; i < triangles.size(); ++i)
+ // mCloth.mTargetCollisionTriangles[first + i] = triangles[i];
+
+ // fill target elements with planes
+ for (uint32_t i = 0; i < triangles.size(); ++i)
+ target[first + i] = triangles[i];
+ }
+
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline void ClothImpl<T>::setTriangles(Range<const physx::PxVec3> startTriangles, Range<const physx::PxVec3> targetTriangles,
+ uint32_t first)
+{
+ NV_CLOTH_ASSERT(startTriangles.size() == targetTriangles.size());
+
+ // convert from triangle to vertex count
+ first *= 3;
+
+ uint32_t last = uint32_t(mCloth.mStartCollisionTriangles.size());
+
+ startTriangles = mCloth.clampTriangleCount(startTriangles, last - first);
+ targetTriangles = mCloth.clampTriangleCount(targetTriangles, last - first);
+
+ uint32_t oldSize = uint32_t(mCloth.mStartCollisionTriangles.size());
+ uint32_t newSize = uint32_t(startTriangles.size()) + oldSize - last + first;
+
+ NV_CLOTH_ASSERT(first <= oldSize);
+ NV_CLOTH_ASSERT(last == oldSize); // this path only supports replacing the tail
+
+ if (!oldSize && !newSize)
+ return;
+
+ if (newSize > std::min(mCloth.mStartCollisionTriangles.capacity(), mCloth.mTargetCollisionTriangles.capacity()))
+ {
+ ContextLockType contextLock(mCloth.mFactory);
+ // mCloth.mStartCollisionTriangles.reserve(newSize);
+ // mCloth.mTargetCollisionTriangles.reserve(newSize);
+
+
+ mCloth.mStartCollisionTriangles.assign(startTriangles.begin(), startTriangles.end());
+ mCloth.mTargetCollisionTriangles.assign(targetTriangles.begin(), targetTriangles.end());
+ mCloth.notifyChanged();
+ }
+ else
+ {
+ uint32_t retainSize = oldSize - last + first;
+ mCloth.mStartCollisionTriangles.resize(retainSize);
+ mCloth.mTargetCollisionTriangles.resize(retainSize);
+
+ mCloth.mStartCollisionTriangles.assign(startTriangles.begin(), startTriangles.end());
+ mCloth.mTargetCollisionTriangles.assign(targetTriangles.begin(), targetTriangles.end());
+
+ // for (uint32_t i = 0, n = startTriangles.size(); i < n; ++i)
+ // {
+ // mCloth.mStartCollisionTriangles.pushBack(startTriangles[i]);
+ // mCloth.mTargetCollisionTriangles.pushBack(targetTriangles[i]);
+ // }
+
+ if (newSize - oldSize)
+ mCloth.notifyChanged();
+ }
+
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline uint32_t ClothImpl<T>::getNumTriangles() const
+{
+ return uint32_t(mCloth.mStartCollisionTriangles.size()) / 3;
+}
+
+template <typename T>
+inline bool ClothImpl<T>::isContinuousCollisionEnabled() const
+{
+ return mCloth.mEnableContinuousCollision;
+}
+
+template <typename T>
+inline void ClothImpl<T>::enableContinuousCollision(bool enable)
+{
+ if (enable == mCloth.mEnableContinuousCollision)
+ return;
+
+ mCloth.mEnableContinuousCollision = enable;
+ mCloth.notifyChanged();
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline float ClothImpl<T>::getCollisionMassScale() const
+{
+ return mCloth.mCollisionMassScale;
+}
+
+template <typename T>
+inline void ClothImpl<T>::setCollisionMassScale(float scale)
+{
+ if (scale == mCloth.mCollisionMassScale)
+ return;
+
+ mCloth.mCollisionMassScale = scale;
+ mCloth.notifyChanged();
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline void ClothImpl<T>::setFriction(float friction)
+{
+ mCloth.mFriction = friction;
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline float ClothImpl<T>::getFriction() const
+{
+ return mCloth.mFriction;
+}
+
+template <typename T>
+inline uint32_t ClothImpl<T>::getNumVirtualParticleWeights() const
+{
+ return uint32_t(mCloth.mVirtualParticleWeights.size());
+}
+
+template <typename T>
+inline void ClothImpl<T>::setTetherConstraintScale(float scale)
+{
+ if (scale == mCloth.mTetherConstraintScale)
+ return;
+
+ mCloth.mTetherConstraintScale = scale;
+ mCloth.notifyChanged();
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline float ClothImpl<T>::getTetherConstraintScale() const
+{
+ return mCloth.mTetherConstraintScale;
+}
+
+template <typename T>
+inline void ClothImpl<T>::setTetherConstraintStiffness(float stiffness)
+{
+ float value = safeLog2(1 - stiffness);
+ if (value == mCloth.mTetherConstraintLogStiffness)
+ return;
+
+ mCloth.mTetherConstraintLogStiffness = value;
+ mCloth.notifyChanged();
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline float ClothImpl<T>::getTetherConstraintStiffness() const
+{
+ return 1.f - safeExp2(mCloth.mTetherConstraintLogStiffness);
+}
+
+template <typename T>
+inline Range<physx::PxVec4> ClothImpl<T>::getMotionConstraints()
+{
+ mCloth.wakeUp();
+ return mCloth.push(mCloth.mMotionConstraints);
+}
+
+template <typename T>
+inline void ClothImpl<T>::clearMotionConstraints()
+{
+ mCloth.clear(mCloth.mMotionConstraints);
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline uint32_t ClothImpl<T>::getNumMotionConstraints() const
+{
+ return uint32_t(mCloth.mMotionConstraints.mStart.size());
+}
+
+template <typename T>
+inline void ClothImpl<T>::setMotionConstraintScaleBias(float scale, float bias)
+{
+ if (scale == mCloth.mMotionConstraintScale && bias == mCloth.mMotionConstraintBias)
+ return;
+
+ mCloth.mMotionConstraintScale = scale;
+ mCloth.mMotionConstraintBias = bias;
+ mCloth.notifyChanged();
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline float ClothImpl<T>::getMotionConstraintScale() const
+{
+ return mCloth.mMotionConstraintScale;
+}
+
+template <typename T>
+inline float ClothImpl<T>::getMotionConstraintBias() const
+{
+ return mCloth.mMotionConstraintBias;
+}
+
+template <typename T>
+inline void ClothImpl<T>::setMotionConstraintStiffness(float stiffness)
+{
+ float value = safeLog2(1 - stiffness);
+ if (value == mCloth.mMotionConstraintLogStiffness)
+ return;
+
+ mCloth.mMotionConstraintLogStiffness = value;
+ mCloth.notifyChanged();
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline float ClothImpl<T>::getMotionConstraintStiffness() const
+{
+ return 1.f - safeExp2(mCloth.mMotionConstraintLogStiffness);
+}
+
+template <typename T>
+inline Range<physx::PxVec4> ClothImpl<T>::getSeparationConstraints()
+{
+ mCloth.wakeUp();
+ return mCloth.push(mCloth.mSeparationConstraints);
+}
+
+template <typename T>
+inline void ClothImpl<T>::clearSeparationConstraints()
+{
+ mCloth.clear(mCloth.mSeparationConstraints);
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline void ClothImpl<T>::clearInterpolation()
+{
+ if (!mCloth.mTargetCollisionSpheres.empty())
+ {
+ ContextLockType contextLock(mCloth.mFactory);
+ physx::shdfnd::swap(mCloth.mStartCollisionSpheres, mCloth.mTargetCollisionSpheres);
+ mCloth.mTargetCollisionSpheres.resize(0);
+ }
+ mCloth.mMotionConstraints.pop();
+ mCloth.mSeparationConstraints.pop();
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline uint32_t ClothImpl<T>::getNumSeparationConstraints() const
+{
+ return uint32_t(mCloth.mSeparationConstraints.mStart.size());
+}
+
+template <typename T>
+inline uint32_t ClothImpl<T>::getNumParticleAccelerations() const
+{
+ return uint32_t(mCloth.mParticleAccelerations.size());
+}
+
+template <typename T>
+inline void ClothImpl<T>::setWindVelocity(physx::PxVec3 wind)
+{
+ if (wind == mCloth.mWind)
+ return;
+
+ mCloth.mWind = wind;
+ mCloth.notifyChanged();
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline physx::PxVec3 ClothImpl<T>::getWindVelocity() const
+{
+ return mCloth.mWind;
+}
+
+template <typename T>
+inline void ClothImpl<T>::setDragCoefficient(float coefficient)
+{
+ NV_CLOTH_ASSERT(coefficient < 1.f);
+
+ float value = safeLog2(1.f - coefficient);
+ if (value == mCloth.mDragLogCoefficient)
+ return;
+
+ mCloth.mDragLogCoefficient = value;
+ mCloth.notifyChanged();
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline float ClothImpl<T>::getDragCoefficient() const
+{
+ return 1.f - safeExp2(mCloth.mDragLogCoefficient);
+}
+
+template <typename T>
+inline void ClothImpl<T>::setLiftCoefficient(float coefficient)
+{
+ NV_CLOTH_ASSERT(coefficient < 1.f);
+
+ float value = safeLog2(1.f - coefficient);
+ if (value == mCloth.mLiftLogCoefficient)
+ return;
+
+ mCloth.mLiftLogCoefficient = value;
+ mCloth.notifyChanged();
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline float ClothImpl<T>::getLiftCoefficient() const
+{
+ return 1.f - safeExp2(mCloth.mLiftLogCoefficient);
+}
+
+template <typename T>
+inline uint32_t ClothImpl<T>::getNumSelfCollisionIndices() const
+{
+ return uint32_t(mCloth.mSelfCollisionIndices.size());
+}
+
+// Fixed 4505:local function has been removed
+template <typename T>
+inline void ClothImpl<T>::setRestPositions(Range<const physx::PxVec4> restPositions)
+{
+ NV_CLOTH_ASSERT(restPositions.empty() || restPositions.size() == getNumParticles());
+ ContextLockType contextLock(mCloth.mFactory);
+ mCloth.mRestPositions.assign(restPositions.begin(), restPositions.end());
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline uint32_t ClothImpl<T>::getNumRestPositions() const
+{
+ return uint32_t(mCloth.mRestPositions.size());
+}
+
+template <typename T>
+inline void ClothImpl<T>::setSelfCollisionDistance(float distance)
+{
+ if (distance == mCloth.mSelfCollisionDistance)
+ return;
+
+ mCloth.mSelfCollisionDistance = distance;
+ mCloth.notifyChanged();
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline float ClothImpl<T>::getSelfCollisionDistance() const
+{
+ return mCloth.mSelfCollisionDistance;
+}
+
+template <typename T>
+inline void ClothImpl<T>::setSelfCollisionStiffness(float stiffness)
+{
+ float value = safeLog2(1 - stiffness);
+ if (value == mCloth.mSelfCollisionLogStiffness)
+ return;
+
+ mCloth.mSelfCollisionLogStiffness = value;
+ mCloth.notifyChanged();
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline float ClothImpl<T>::getSelfCollisionStiffness() const
+{
+ return 1.f - safeExp2(mCloth.mSelfCollisionLogStiffness);
+}
+
+template <typename T>
+inline const physx::PxVec3& ClothImpl<T>::getBoundingBoxCenter() const
+{
+ return mCloth.mParticleBoundsCenter;
+}
+
+template <typename T>
+inline const physx::PxVec3& ClothImpl<T>::getBoundingBoxScale() const
+{
+ return mCloth.mParticleBoundsHalfExtent;
+}
+
+template <typename T>
+inline void ClothImpl<T>::setSleepThreshold(float threshold)
+{
+ if (threshold == mCloth.mSleepThreshold)
+ return;
+
+ mCloth.mSleepThreshold = threshold;
+ mCloth.notifyChanged();
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline float ClothImpl<T>::getSleepThreshold() const
+{
+ return mCloth.mSleepThreshold;
+}
+
+template <typename T>
+inline void ClothImpl<T>::setSleepTestInterval(uint32_t interval)
+{
+ if (interval == mCloth.mSleepTestInterval)
+ return;
+
+ mCloth.mSleepTestInterval = interval;
+ mCloth.notifyChanged();
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline uint32_t ClothImpl<T>::getSleepTestInterval() const
+{
+ return mCloth.mSleepTestInterval;
+}
+
+template <typename T>
+inline void ClothImpl<T>::setSleepAfterCount(uint32_t afterCount)
+{
+ if (afterCount == mCloth.mSleepAfterCount)
+ return;
+
+ mCloth.mSleepAfterCount = afterCount;
+ mCloth.notifyChanged();
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline uint32_t ClothImpl<T>::getSleepAfterCount() const
+{
+ return mCloth.mSleepAfterCount;
+}
+
+template <typename T>
+inline uint32_t ClothImpl<T>::getSleepPassCount() const
+{
+ return mCloth.mSleepPassCounter;
+}
+
+template <typename T>
+inline bool ClothImpl<T>::isAsleep() const
+{
+ return mCloth.isSleeping();
+}
+
+template <typename T>
+inline void ClothImpl<T>::putToSleep()
+{
+ mCloth.mSleepPassCounter = mCloth.mSleepAfterCount;
+}
+
+template <typename T>
+inline void ClothImpl<T>::wakeUp()
+{
+ mCloth.wakeUp();
+}
+
+template <typename T>
+inline void ClothImpl<T>::setUserData(void* data)
+{
+ mCloth.mUserData = data;
+}
+
+template <typename T>
+inline void* ClothImpl<T>::getUserData() const
+{
+ return mCloth.mUserData;
+}
+
+template <typename T>
+template <typename U>
+inline MappedRange<U> ClothImpl<T>::getMappedParticles(U* data) const
+{
+ return MappedRange<U>(data, data + getNumParticles(), *this, &Cloth::lockParticles, &Cloth::unlockParticles);
+}
+
+
+} // namespace cloth
+} // namespace nv