aboutsummaryrefslogtreecommitdiff
path: root/APEX_1.4/module/clothing/src/ClothingPhysicalMeshImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'APEX_1.4/module/clothing/src/ClothingPhysicalMeshImpl.cpp')
-rw-r--r--APEX_1.4/module/clothing/src/ClothingPhysicalMeshImpl.cpp1576
1 files changed, 1576 insertions, 0 deletions
diff --git a/APEX_1.4/module/clothing/src/ClothingPhysicalMeshImpl.cpp b/APEX_1.4/module/clothing/src/ClothingPhysicalMeshImpl.cpp
new file mode 100644
index 00000000..538a7e1a
--- /dev/null
+++ b/APEX_1.4/module/clothing/src/ClothingPhysicalMeshImpl.cpp
@@ -0,0 +1,1576 @@
+/*
+ * Copyright (c) 2008-2015, NVIDIA CORPORATION. All rights reserved.
+ *
+ * NVIDIA CORPORATION and its licensors retain all intellectual property
+ * and proprietary rights in and to this software, 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.
+ */
+
+
+#include "ApexDefs.h"
+
+#include "ClothingPhysicalMeshImpl.h"
+#include "ApexMeshHash.h"
+#include "ApexQuadricSimplifier.h"
+#include "ClothingAssetAuthoringImpl.h"
+#include "ModuleClothingImpl.h"
+
+#include "ApexPermute.h"
+#include "ApexSharedUtils.h"
+
+#include "PxStrideIterator.h"
+#include "PsMathUtils.h"
+#include "PsSort.h"
+
+namespace nvidia
+{
+namespace clothing
+{
+
+struct SortedEdge
+{
+ SortedEdge(uint32_t _i0, uint32_t _i1) : i0(PxMin(_i0, _i1)), i1(PxMax(_i0, _i1)) {}
+
+ bool operator==(const SortedEdge& other) const
+ {
+ return i0 == other.i0 && i1 == other.i1;
+ }
+ bool operator()(const SortedEdge& s1, const SortedEdge& s2) const
+ {
+ if (s1.i0 != s2.i0)
+ {
+ return s1.i0 < s2.i0;
+ }
+
+ return s1.i1 < s2.i1;
+ }
+
+ uint32_t i0, i1;
+};
+
+ClothingPhysicalMeshImpl::ClothingPhysicalMeshImpl(ModuleClothingImpl* module, ClothingPhysicalMeshParameters* params, ResourceList* list) :
+ mModule(module),
+ mParams(NULL),
+ ownsParams(false),
+ mSimplifier(NULL),
+ isDirty(false)
+{
+ if (params != NULL && nvidia::strcmp(params->className(), ClothingPhysicalMeshParameters::staticClassName()) != 0)
+ {
+ APEX_INTERNAL_ERROR(
+ "The parameterized interface is of type <%s> instead of <%s>. "
+ "An empty ClothingPhhsicalMesh has been created instead.",
+ params->className(),
+ ClothingPhysicalMeshParameters::staticClassName());
+
+ params = NULL;
+ }
+
+ if (params == NULL)
+ {
+ params = DYNAMIC_CAST(ClothingPhysicalMeshParameters*)(GetInternalApexSDK()->getParameterizedTraits()->createNvParameterized(ClothingPhysicalMeshParameters::staticClassName()));
+ ownsParams = true;
+ }
+ PX_ASSERT(params != NULL);
+
+ mParams = params;
+ mVertices.init(mParams, "physicalMesh.vertices", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->physicalMesh.vertices));
+ mNormals.init(mParams, "physicalMesh.normals", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->physicalMesh.normals));
+ mSkinningNormals.init(mParams, "physicalMesh.skinningNormals", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->physicalMesh.skinningNormals));
+ mConstrainCoefficients.init(mParams, "physicalMesh.constrainCoefficients", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->physicalMesh.constrainCoefficients));
+ mBoneIndices.init(mParams, "physicalMesh.boneIndices", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->physicalMesh.boneIndices));
+ mBoneWeights.init(mParams, "physicalMesh.boneWeights", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->physicalMesh.boneWeights));
+ mIndices.init(mParams, "physicalMesh.indices", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->physicalMesh.indices));
+
+ mNumSimulatedVertices = mParams->physicalMesh.numSimulatedVertices;
+ mNumMaxDistanc0Vertices = mParams->physicalMesh.numMaxDistance0Vertices;
+ mNumSimulatedIndices = mParams->physicalMesh.numSimulatedIndices;
+
+ mParams->referenceCount++;
+
+ mParams->setSerializationCallback(this);
+#if 0
+ // debugging only
+ char buf[32];
+ sprintf_s(buf, 32, "++ %p -> %d\n", mParams, mParams->referenceCount);
+ OutputDebugString(buf);
+#endif
+
+ if (mParams->physicalMesh.shortestEdgeLength == 0.0f)
+ {
+ computeEdgeLengths();
+ }
+
+ list->add(*this);
+}
+
+
+
+void ClothingPhysicalMeshImpl::release()
+{
+ PX_ASSERT(mParams != NULL);
+ if (mParams != NULL)
+ {
+ // make sure everything is set up correctly before we let the param object live on its own
+ preSerialize(NULL);
+
+ mParams->setSerializationCallback(NULL);
+ }
+ mModule->releasePhysicalMesh(this);
+}
+
+
+
+void ClothingPhysicalMeshImpl::destroy()
+{
+ if (mSimplifier != NULL)
+ {
+ delete mSimplifier;
+ mSimplifier = NULL;
+ }
+
+ if (mParams != NULL)
+ {
+ mParams->referenceCount--;
+#if 0
+ // debugging only
+ char buf[32];
+ sprintf_s(buf, 32, "-- %p -> %d\n", mParams, mParams->referenceCount);
+ OutputDebugString(buf);
+#endif
+ }
+
+ if (ownsParams && mParams)
+ {
+ PX_ASSERT(mParams->referenceCount == 0);
+ mParams->destroy();
+ }
+
+ delete this;
+}
+
+
+
+void ClothingPhysicalMeshImpl::makeCopy(ClothingPhysicalMeshParameters* params)
+{
+ PX_ASSERT(mParams != NULL);
+ params->copy(*mParams);
+}
+
+
+
+void ClothingPhysicalMeshImpl::allocateNormalBuffer()
+{
+ mNormals.resize(mParams->physicalMesh.numVertices);
+}
+
+
+
+void ClothingPhysicalMeshImpl::allocateSkinningNormalsBuffer()
+{
+ mSkinningNormals.resize(mParams->physicalMesh.numVertices);
+}
+
+
+void ClothingPhysicalMeshImpl::allocateMasterFlagsBuffer()
+{
+ uint32_t numVertices = mVertices.size();
+ mMasterFlags.resize(numVertices);
+
+ for (uint32_t i = 0; i < numVertices; i++)
+ {
+ mMasterFlags[i] = 0xffffffff;
+ }
+}
+
+
+void ClothingPhysicalMeshImpl::allocateConstrainCoefficientBuffer()
+{
+ WRITE_ZONE();
+ const uint32_t numVertices = mParams->physicalMesh.numVertices;
+ mConstrainCoefficients.resize(numVertices);
+
+ for (uint32_t i = 0; i < numVertices; i++)
+ {
+ mConstrainCoefficients[i].maxDistance = PX_MAX_F32;
+ mConstrainCoefficients[i].collisionSphereRadius = PX_MAX_F32;
+ mConstrainCoefficients[i].collisionSphereDistance = PX_MAX_F32;
+ }
+}
+
+
+
+void ClothingPhysicalMeshImpl::allocateBoneIndexAndWeightBuffers()
+{
+ const uint32_t numBonesPerVertex = mParams->physicalMesh.numBonesPerVertex;
+ if (numBonesPerVertex == 0)
+ {
+ APEX_DEBUG_WARNING("Number of bones per vertex is set to 0. Not allocating memory.");
+ return;
+ }
+ const uint32_t numVertices = mParams->physicalMesh.numVertices;
+
+ mBoneIndices.resize(numBonesPerVertex * numVertices);
+
+ // PH: At one point we can start trying to safe this buffer
+ //if (numBonesPerVertex > 1)
+ {
+ mBoneWeights.resize(numBonesPerVertex * numVertices);
+ }
+}
+
+
+
+void ClothingPhysicalMeshImpl::freeAdditionalBuffers()
+{
+
+ mNormals.resize(0);
+ mSkinningNormals.resize(0);
+ mConstrainCoefficients.resize(0);
+ mBoneIndices.resize(0);
+ mBoneWeights.resize(0);
+ mParams->physicalMesh.numBonesPerVertex = 0;
+}
+
+
+
+uint32_t ClothingPhysicalMeshImpl::getNumVertices() const
+{
+ READ_ZONE();
+ if (mSimplifier != NULL)
+ {
+ return mSimplifier->getNumVertices() - mSimplifier->getNumDeletedVertices();
+ }
+
+ return mParams->physicalMesh.numVertices;
+}
+
+
+
+uint32_t ClothingPhysicalMeshImpl::getNumSimulatedVertices() const
+{
+ READ_ZONE();
+ return mParams->physicalMesh.numSimulatedVertices;
+}
+
+
+
+uint32_t ClothingPhysicalMeshImpl::getNumMaxDistance0Vertices() const
+{
+ READ_ZONE();
+ return mParams->physicalMesh.numMaxDistance0Vertices;
+}
+
+
+
+uint32_t ClothingPhysicalMeshImpl::getNumIndices() const
+{
+ READ_ZONE();
+ if (mSimplifier != NULL)
+ {
+ return mSimplifier->getNumTriangles() * 3;
+ }
+
+ return mParams->physicalMesh.numIndices;
+}
+
+
+
+uint32_t ClothingPhysicalMeshImpl::getNumSimulatedIndices() const
+{
+ READ_ZONE();
+ if (mSimplifier != NULL)
+ {
+ return mSimplifier->getNumTriangles() * 3;
+ }
+
+ return mParams->physicalMesh.numSimulatedIndices;
+}
+
+
+
+void ClothingPhysicalMeshImpl::getIndices(void* indexDestination, uint32_t byteStride, uint32_t numIndices) const
+{
+ READ_ZONE();
+ numIndices = PxMin(numIndices, mParams->physicalMesh.numIndices);
+
+ if (byteStride == 0)
+ {
+ byteStride = sizeof(uint32_t);
+ }
+
+ if (byteStride < sizeof(uint32_t))
+ {
+ APEX_INTERNAL_ERROR("byte stride is too small (%d)", byteStride);
+ return;
+ }
+
+ const_cast<ClothingPhysicalMeshImpl*>(this)->writeBackData();
+
+ uint8_t* destPtr = (uint8_t*)indexDestination;
+ for (uint32_t i = 0; i < numIndices; i++)
+ {
+ (uint32_t&)(*(destPtr + byteStride * i)) = mIndices[i];
+ }
+}
+
+
+
+void ClothingPhysicalMeshImpl::simplify(uint32_t subdivisions, int32_t maxSteps, float maxError, IProgressListener* progressListener)
+{
+ WRITE_ZONE();
+ if (mParams->physicalMesh.isTetrahedralMesh)
+ {
+ APEX_INVALID_OPERATION("Cannot simplify a tetrahedral mesh");
+ return;
+ }
+
+ if (mParams->physicalMesh.boneIndices.buf != NULL || mParams->physicalMesh.boneWeights.buf != NULL)
+ {
+ APEX_INVALID_OPERATION("Cannot simplif a triangle mesh with additional bone data");
+ return;
+ }
+
+
+ const uint32_t numVertices = mParams->physicalMesh.numVertices;
+ const uint32_t numIndices = mParams->physicalMesh.numIndices;
+
+ if (numVertices == 0 || numIndices == 0)
+ {
+ return;
+ }
+
+ HierarchicalProgressListener progress(100, progressListener);
+
+ if (mSimplifier == NULL)
+ {
+ progress.setSubtaskWork(80, "Init simplificator");
+ mSimplifier = PX_NEW(ApexQuadricSimplifier);
+
+ for (uint32_t i = 0; i < numVertices; i++)
+ {
+ mSimplifier->registerVertex(mVertices[i]);
+ }
+
+ for (uint32_t i = 0; i < numIndices; i += 3)
+ {
+ mSimplifier->registerTriangle(mIndices[i + 0], mIndices[i + 1], mIndices[i + 2]);
+ }
+
+ mSimplifier->endRegistration(false, &progress);
+ progress.completeSubtask();
+ }
+
+ progress.setSubtaskWork(-1, "Simplification steps");
+
+ uint32_t steps = mSimplifier->simplify(subdivisions, maxSteps, maxError, &progress);
+
+ if (!isDirty)
+ {
+ isDirty = steps > 0;
+ }
+
+ progress.completeSubtask();
+}
+
+
+
+void ClothingPhysicalMeshImpl::setGeometry(bool tetraMesh, uint32_t numVertices, uint32_t vertexByteStride, const void* vertices,
+ const uint32_t* masterFlags, uint32_t numIndices, uint32_t indexByteStride, const void* indices)
+{
+ WRITE_ZONE();
+ if (vertexByteStride < sizeof(PxVec3))
+ {
+ APEX_INTERNAL_ERROR("vertexByteStride is too small (%d)", vertexByteStride);
+ return;
+ }
+
+ if (indexByteStride < sizeof(uint32_t))
+ {
+ APEX_INTERNAL_ERROR("indexByteStride is too small (%d)", indexByteStride);
+ return;
+ }
+
+ if (numVertices > 0 && vertices == NULL)
+ {
+ APEX_INTERNAL_ERROR("vertex pointer is NULL");
+ return;
+ }
+
+ if (numIndices > 0 && indices == NULL)
+ {
+ APEX_INTERNAL_ERROR("index pointer is NULL");
+ return;
+ }
+
+ if (tetraMesh && (numIndices % 4 != 0))
+ {
+ APEX_INTERNAL_ERROR("Indices must be a multiple of 4 for physical tetrahedral meshes");
+ return;
+ }
+
+ if (!tetraMesh && (numIndices % 3 != 0))
+ {
+ APEX_INTERNAL_ERROR("Indices must be a multiple of 3 for physical meshes");
+ return;
+ }
+
+ mParams->physicalMesh.isTetrahedralMesh = tetraMesh;
+
+ mParams->physicalMesh.numVertices = numVertices;
+ mParams->physicalMesh.numSimulatedVertices = numVertices;
+ mParams->physicalMesh.numMaxDistance0Vertices = 0;
+
+ mVertices.resize(numVertices);
+ mMasterFlags.resize(numVertices);
+
+ mNormals.resize(0);
+ mSkinningNormals.resize(0);
+
+ const uint8_t* srcVertices = (const uint8_t*)vertices;
+ for (uint32_t i = 0; i < numVertices; i++)
+ {
+ const PxVec3& currVec = *(const PxVec3*)(srcVertices + vertexByteStride * i);
+ mVertices[i] = currVec;
+
+ mMasterFlags[i] = masterFlags != NULL ? masterFlags[i] : 0xffffffff;
+ }
+
+
+ if (tetraMesh || !removeDuplicatedTriangles(numIndices, indexByteStride, indices))
+ {
+ mParams->physicalMesh.numIndices = numIndices;
+ mParams->physicalMesh.numSimulatedIndices = numIndices;
+ mIndices.resize(numIndices);
+
+ const uint8_t* srcIndices = (const uint8_t*)indices;
+ for (uint32_t i = 0; i < numIndices; i++)
+ {
+ const uint32_t currIndex = *(const uint32_t*)(srcIndices + indexByteStride * i);
+ mIndices[i] = currIndex;
+ }
+ }
+
+ if (mSimplifier != NULL)
+ {
+ delete mSimplifier;
+ mSimplifier = NULL;
+ }
+
+ clearMiscBuffers();
+ computeEdgeLengths();
+
+ if (!mParams->physicalMesh.isTetrahedralMesh)
+ {
+ mSkinningNormals.resize(numVertices);
+ updateSkinningNormals();
+ }
+}
+
+
+
+bool ClothingPhysicalMeshImpl::getIndices(uint32_t* indices, uint32_t byteStride) const
+{
+ READ_ZONE();
+ if (mIndices.size() == 0)
+ {
+ return false;
+ }
+
+ if (byteStride == 0)
+ {
+ byteStride = sizeof(uint32_t);
+ }
+
+ if (byteStride < sizeof(uint32_t))
+ {
+ APEX_INTERNAL_ERROR("bytestride too small (%d)", byteStride);
+ return false;
+ }
+
+ const_cast<ClothingPhysicalMeshImpl*>(this)->writeBackData();
+
+ const uint32_t numIndices = mParams->physicalMesh.numIndices;
+
+ PxStrideIterator<uint32_t> iterator(indices, byteStride);
+ for (uint32_t i = 0; i < numIndices; i++, ++iterator)
+ {
+ *iterator = mIndices[i];
+ }
+
+ return true;
+}
+
+
+
+bool ClothingPhysicalMeshImpl::getVertices(PxVec3* vertices, uint32_t byteStride) const
+{
+ READ_ZONE();
+ if (mVertices.size() == 0)
+ {
+ return false;
+ }
+
+ if (byteStride == 0)
+ {
+ byteStride = sizeof(PxVec3);
+ }
+
+ if (byteStride < sizeof(PxVec3))
+ {
+ APEX_INTERNAL_ERROR("bytestride too small (%d)", byteStride);
+ return false;
+ }
+
+ const_cast<ClothingPhysicalMeshImpl*>(this)->writeBackData();
+
+ const uint32_t numVertices = mParams->physicalMesh.numVertices;
+
+ PxStrideIterator<PxVec3> iterator(vertices, byteStride);
+ for (uint32_t i = 0; i < numVertices; i++, ++iterator)
+ {
+ *iterator = mVertices[i];
+ }
+
+ return true;
+}
+
+
+
+bool ClothingPhysicalMeshImpl::getNormals(PxVec3* normals, uint32_t byteStride) const
+{
+ READ_ZONE();
+ if (mNormals.size() == 0)
+ {
+ return false;
+ }
+
+ if (byteStride == 0)
+ {
+ byteStride = sizeof(PxVec3);
+ }
+
+ if (byteStride < sizeof(PxVec3))
+ {
+ APEX_INTERNAL_ERROR("bytestride too small (%d)", byteStride);
+ return false;
+ }
+
+ const_cast<ClothingPhysicalMeshImpl*>(this)->writeBackData();
+
+ const uint32_t numVertices = mParams->physicalMesh.numVertices;
+
+ PxStrideIterator<PxVec3> iterator(normals, byteStride);
+ for (uint32_t i = 0; i < numVertices; i++, ++iterator)
+ {
+ *iterator = mNormals[i];
+ }
+
+ return true;
+}
+
+
+
+bool ClothingPhysicalMeshImpl::getBoneIndices(uint16_t* boneIndices, uint32_t byteStride) const
+{
+ READ_ZONE();
+ if (mBoneIndices.size() == 0)
+ {
+ return false;
+ }
+
+ const uint32_t numBonesPerVertex = mParams->physicalMesh.numBonesPerVertex;
+ if (byteStride == 0)
+ {
+ byteStride = numBonesPerVertex * sizeof(uint16_t);
+ }
+
+ if (byteStride < numBonesPerVertex * sizeof(uint16_t))
+ {
+ APEX_INTERNAL_ERROR("bytestride too small (%d)", byteStride);
+ return false;
+ }
+
+ const uint32_t numElements = mParams->physicalMesh.numVertices * numBonesPerVertex;
+
+ PxStrideIterator<uint16_t> iterator(boneIndices, byteStride);
+ for (uint32_t i = 0; i < numElements; i += numBonesPerVertex, ++iterator)
+ {
+ for (uint32_t j = 0; j < numBonesPerVertex; j++)
+ {
+ uint16_t* current = iterator.ptr();
+ current[j] = mBoneIndices[i + j];
+ }
+ }
+
+ return true;
+}
+
+
+
+bool ClothingPhysicalMeshImpl::getBoneWeights(float* boneWeights, uint32_t byteStride) const
+{
+ READ_ZONE();
+ if (mBoneWeights.size() == 0)
+ {
+ return false;
+ }
+
+ const uint32_t numBonesPerVertex = mParams->physicalMesh.numBonesPerVertex;
+ if (byteStride == 0)
+ {
+ byteStride = numBonesPerVertex * sizeof(float);
+ }
+
+ if (byteStride < numBonesPerVertex * sizeof(float))
+ {
+ APEX_INTERNAL_ERROR("bytestride too small (%d)", byteStride);
+ return false;
+ }
+
+ const_cast<ClothingPhysicalMeshImpl*>(this)->writeBackData();
+
+ const uint32_t numElements = mParams->physicalMesh.numVertices * numBonesPerVertex;
+
+ PxStrideIterator<float> iterator(boneWeights, byteStride);
+ for (uint32_t i = 0; i < numElements; i += numBonesPerVertex, ++iterator)
+ {
+ for (uint32_t j = 0; j < numBonesPerVertex; j++)
+ {
+ float* current = iterator.ptr();
+ current[j] = mBoneWeights[i + j];
+ }
+ }
+
+
+ return true;
+}
+
+
+
+bool ClothingPhysicalMeshImpl::getConstrainCoefficients(ClothingConstrainCoefficients* coeffs, uint32_t byteStride) const
+{
+ READ_ZONE();
+ if (mConstrainCoefficients.size() == 0)
+ {
+ return false;
+ }
+
+ if (byteStride == 0)
+ {
+ byteStride = sizeof(ClothingConstrainCoefficients);
+ }
+
+ if (byteStride < sizeof(ClothingConstrainCoefficients))
+ {
+ APEX_INTERNAL_ERROR("bytestride too small (%d)", byteStride);
+ return false;
+ }
+
+ const_cast<ClothingPhysicalMeshImpl*>(this)->writeBackData();
+
+ const uint32_t numVertices = mParams->physicalMesh.numVertices;
+
+ PxStrideIterator<ClothingConstrainCoefficients> iterator(coeffs, byteStride);
+ for (uint32_t i = 0; i < numVertices; i++, ++iterator)
+ {
+ iterator->maxDistance = mConstrainCoefficients[i].maxDistance;
+ iterator->collisionSphereDistance = mConstrainCoefficients[i].collisionSphereDistance;
+ iterator->collisionSphereRadius = mConstrainCoefficients[i].collisionSphereRadius;
+ }
+
+ return true;
+}
+
+
+
+void ClothingPhysicalMeshImpl::getStats(ClothingPhysicalMeshStats& stats) const
+{
+ READ_ZONE();
+ memset(&stats, 0, sizeof(ClothingPhysicalMeshStats));
+
+ stats.totalBytes = sizeof(ClothingPhysicalMeshImpl);
+
+ /*
+
+ stats.numVertices = mNumVertices;
+ stats.numIndices = mNumIndices;
+
+ stats.totalBytes += (mVertices != NULL ? mNumVertices : 0) * sizeof(PxVec3);
+ stats.totalBytes += (mNormals != NULL ? mNumVertices : 0) * sizeof(PxVec3);
+ stats.totalBytes += (mSkinningNormals != NULL ? mNumVertices : 0) * sizeof(PxVec3);
+ stats.totalBytes += (mVertexFlags != NULL ? mNumVertices : 0) * sizeof(uint32_t);
+ stats.totalBytes += (mConstrainCoefficients != NULL ? mNumVertices : 0) * sizeof(ClothConstrainCoefficients);
+
+ stats.totalBytes += (mBoneIndices != NULL ? mNumVertices * mNumBonesPerVertex : 0) * sizeof(uint16_t);
+ stats.totalBytes += (mBoneWeights != NULL ? mNumVertices * mNumBonesPerVertex : 0) * sizeof(float);
+
+ stats.totalBytes += (mIndices != NULL ? mNumIndices : 0) * sizeof(uint32_t);
+ */
+}
+
+
+
+void ClothingPhysicalMeshImpl::writeBackData()
+{
+ if (!isDirty || mSimplifier == NULL)
+ {
+ return;
+ }
+
+ isDirty = false;
+
+ Array<int32_t> old2new(mSimplifier->getNumVertices());
+
+ PX_ASSERT(mSimplifier->getNumVertices() - mSimplifier->getNumDeletedVertices() < mParams->physicalMesh.numVertices);
+
+ uint32_t verticesWritten = 0;
+ for (uint32_t i = 0; i < mSimplifier->getNumVertices(); i++)
+ {
+ PxVec3 pos;
+ if (mSimplifier->getVertexPosition(i, pos))
+ {
+ old2new[i] = (int32_t)verticesWritten;
+ mVertices[verticesWritten++] = pos;
+ }
+ else
+ {
+ old2new[i] = -1;
+ }
+ }
+ PX_ASSERT(verticesWritten == (mSimplifier->getNumVertices() - mSimplifier->getNumDeletedVertices()));
+ mParams->physicalMesh.numVertices = verticesWritten;
+
+ PX_ASSERT(mSimplifier->getNumTriangles() * 3 < mParams->physicalMesh.numIndices);
+
+ uint32_t indicesWritten = 0;
+ uint32_t trianglesRead = 0;
+ while (indicesWritten < mSimplifier->getNumTriangles() * 3)
+ {
+ uint32_t v0, v1, v2;
+ if (mSimplifier->getTriangle(trianglesRead++, v0, v1, v2))
+ {
+ PX_ASSERT(old2new[v0] != -1);
+ PX_ASSERT(old2new[v1] != -1);
+ PX_ASSERT(old2new[v2] != -1);
+ mIndices[indicesWritten++] = (uint32_t)old2new[v0];
+ mIndices[indicesWritten++] = (uint32_t)old2new[v1];
+ mIndices[indicesWritten++] = (uint32_t)old2new[v2];
+ }
+ }
+ mParams->physicalMesh.numIndices = indicesWritten;
+
+ updateSkinningNormals();
+}
+
+
+void ClothingPhysicalMeshImpl::clearMiscBuffers()
+{
+ mConstrainCoefficients.resize(0);
+ mBoneIndices.resize(0);
+ mBoneWeights.resize(0);
+}
+
+
+
+void ClothingPhysicalMeshImpl::computeEdgeLengths() const
+{
+ const uint32_t numIndices = mParams->physicalMesh.numIndices;
+
+ float average = 0;
+ float shortest = PX_MAX_F32;
+
+ if (mParams->physicalMesh.isTetrahedralMesh)
+ {
+ for (uint32_t i = 0; i < numIndices; i += 4)
+ {
+ const float edge0 = (mVertices[mIndices[i + 0]] - mVertices[mIndices[i + 1]]).magnitudeSquared();
+ const float edge1 = (mVertices[mIndices[i + 0]] - mVertices[mIndices[i + 2]]).magnitudeSquared();
+ const float edge2 = (mVertices[mIndices[i + 0]] - mVertices[mIndices[i + 3]]).magnitudeSquared();
+ const float edge3 = (mVertices[mIndices[i + 1]] - mVertices[mIndices[i + 2]]).magnitudeSquared();
+ const float edge4 = (mVertices[mIndices[i + 1]] - mVertices[mIndices[i + 3]]).magnitudeSquared();
+ const float edge5 = (mVertices[mIndices[i + 2]] - mVertices[mIndices[i + 3]]).magnitudeSquared();
+ shortest = PxMin(shortest, edge0);
+ shortest = PxMin(shortest, edge1);
+ shortest = PxMin(shortest, edge2);
+ shortest = PxMin(shortest, edge3);
+ shortest = PxMin(shortest, edge4);
+ shortest = PxMin(shortest, edge5);
+
+ average += PxSqrt(edge0) + PxSqrt(edge1) + PxSqrt(edge2) + PxSqrt(edge3) + PxSqrt(edge4) + PxSqrt(edge5);
+ }
+ mParams->physicalMesh.isClosed = false;
+ }
+ else
+ {
+ // also check if the mesh is closed
+ nvidia::Array<SortedEdge> edges;
+
+ for (uint32_t i = 0; i < numIndices; i += 3)
+ {
+ const float edge0 = (mVertices[mIndices[i + 0]] - mVertices[mIndices[i + 1]]).magnitudeSquared();
+ const float edge1 = (mVertices[mIndices[i + 0]] - mVertices[mIndices[i + 2]]).magnitudeSquared();
+ const float edge2 = (mVertices[mIndices[i + 1]] - mVertices[mIndices[i + 2]]).magnitudeSquared();
+ shortest = PxMin(shortest, edge0);
+ shortest = PxMin(shortest, edge1);
+ shortest = PxMin(shortest, edge2);
+
+ average += PxSqrt(edge0) + PxSqrt(edge1) + PxSqrt(edge2);
+
+ edges.pushBack(SortedEdge(mIndices[i + 0], mIndices[i + 1]));
+ edges.pushBack(SortedEdge(mIndices[i + 1], mIndices[i + 2]));
+ edges.pushBack(SortedEdge(mIndices[i + 2], mIndices[i + 0]));
+ }
+
+ nvidia::sort(edges.begin(), edges.size(), SortedEdge(0, 0));
+
+ bool meshClosed = false;
+ if ((edges.size() & 0x1) == 0) // only works for even number of indices
+ {
+ meshClosed = true;
+ for (uint32_t i = 0; i < edges.size(); i += 2)
+ {
+ meshClosed &= edges[i] == edges[i + 1];
+
+ if (i > 0)
+ {
+ meshClosed &= !(edges[i - 1] == edges[i]);
+ }
+ }
+ }
+ mParams->physicalMesh.isClosed = meshClosed;
+ }
+
+ mParams->physicalMesh.shortestEdgeLength = PxSqrt(shortest);
+ if (numIndices > 0)
+ {
+ mParams->physicalMesh.averageEdgeLength = average / (float)numIndices;
+ }
+ else
+ {
+ mParams->physicalMesh.averageEdgeLength = 0.0f;
+ }
+}
+
+
+
+void ClothingPhysicalMeshImpl::addBoneToVertex(uint32_t vertexNumber, uint16_t boneIndex, float boneWeight)
+{
+ if (mBoneIndices.size() == 0 || mBoneWeights.size() == 0)
+ {
+ return;
+ }
+
+ const uint32_t numBonesPerVertex = mParams->physicalMesh.numBonesPerVertex;
+ for (uint32_t i = 0; i < numBonesPerVertex; i++)
+ {
+ if (mBoneIndices[vertexNumber * numBonesPerVertex + i] == boneIndex ||
+ mBoneWeights[vertexNumber * numBonesPerVertex + i] == 0.0f)
+ {
+ mBoneIndices[vertexNumber * numBonesPerVertex + i] = boneIndex;
+ mBoneWeights[vertexNumber * numBonesPerVertex + i] =
+ PxMax(mBoneWeights[vertexNumber * numBonesPerVertex + i], boneWeight);
+ sortBonesOfVertex(vertexNumber);
+ return;
+ }
+ }
+}
+
+
+
+void ClothingPhysicalMeshImpl::sortBonesOfVertex(uint32_t vertexNumber)
+{
+ const uint32_t numBonesPerVertex = mParams->physicalMesh.numBonesPerVertex;
+ if (mBoneIndices.size() == 0 || mBoneWeights.size() == 0 || numBonesPerVertex <= 1)
+ {
+ return;
+ }
+
+ // bubble sort
+ bool changed = true;
+ while (changed)
+ {
+ changed = false;
+ for (uint32_t i = 0; i < numBonesPerVertex - 1; i++)
+ {
+ const uint32_t index = vertexNumber * numBonesPerVertex + i;
+ if (mBoneWeights[index] < mBoneWeights[index + 1])
+ {
+ // swap
+ float tempF = mBoneWeights[index];
+ mBoneWeights[index] = mBoneWeights[index + 1];
+ mBoneWeights[index + 1] = tempF;
+
+ uint16_t tempI = mBoneIndices[index];
+ mBoneIndices[index] = mBoneIndices[index + 1];
+ mBoneIndices[index + 1] = tempI;
+
+ changed = true;
+ }
+ }
+ }
+}
+
+
+
+void ClothingPhysicalMeshImpl::normalizeBonesOfVertex(uint32_t vertexNumber)
+{
+ if (mBoneIndices.size() == 0 || mBoneWeights.size() == 0)
+ {
+ return;
+ }
+
+ const uint32_t numBonesPerVertex = mParams->physicalMesh.numBonesPerVertex;
+
+ float sum = 0;
+ float last = FLT_MAX;
+ for (uint32_t i = 0; i < numBonesPerVertex; i++)
+ {
+ sum += mBoneWeights[vertexNumber * numBonesPerVertex + i];
+
+ // make sure it is sorted!
+ PX_ASSERT(mBoneWeights[vertexNumber * numBonesPerVertex + i] <= last);
+ last = mBoneWeights[vertexNumber * numBonesPerVertex + i];
+ }
+
+ PX_UNUSED(last);
+
+ if (sum > 0)
+ {
+ float invSum = 1.0f / sum;
+ for (uint32_t i = 0; i < numBonesPerVertex; i++)
+ {
+ float& weight = mBoneWeights[vertexNumber * numBonesPerVertex + i];
+ if (weight > 0)
+ {
+ weight *= invSum;
+ }
+ else
+ {
+ mBoneIndices[vertexNumber * numBonesPerVertex + i] = 0;
+ }
+ }
+ }
+ else
+ {
+ for (uint32_t i = 0; i < numBonesPerVertex; i++)
+ {
+ mBoneIndices[vertexNumber * numBonesPerVertex + i] = 0;
+ mBoneWeights[vertexNumber * numBonesPerVertex + i] = 0.0f;
+ }
+ }
+}
+
+
+
+void ClothingPhysicalMeshImpl::updateSkinningNormals()
+{
+ // only for non-softbodies
+ if (isTetrahedralMesh())
+ {
+ return;
+ }
+
+ const uint32_t numVertices = mParams->physicalMesh.numVertices;
+ const uint32_t numIndices = mParams->physicalMesh.numIndices;
+
+ PX_ASSERT(mSkinningNormals.size() == mVertices.size());
+ memset(mSkinningNormals.begin(), 0, sizeof(PxVec3) * numVertices);
+
+ for (uint32_t i = 0; i < numIndices; i += 3)
+ {
+ PxVec3 normal;
+ normal = mVertices[mIndices[i + 1]] - mVertices[mIndices[i]];
+ normal = normal.cross(mVertices[mIndices[i + 2]] - mVertices[mIndices[i]]);
+ mSkinningNormals[mIndices[i]] += normal;
+ mSkinningNormals[mIndices[i + 1]] += normal;
+ mSkinningNormals[mIndices[i + 2]] += normal;
+ }
+
+ for (uint32_t i = 0; i < numVertices; i++)
+ {
+ mSkinningNormals[i].normalize();
+ }
+}
+
+
+
+void ClothingPhysicalMeshImpl::smoothNormals(uint32_t numIterations)
+{
+ const uint32_t increment = mParams->physicalMesh.isTetrahedralMesh ? 4u : 3u;
+ const uint32_t numIndices = mParams->physicalMesh.numIndices;
+
+ for (uint32_t iters = 0; iters < numIterations; iters++)
+ {
+ for (uint32_t i = 0; i < numIndices; i += increment)
+ {
+ PxVec3& n0 = mNormals[mIndices[i + 0]];
+ PxVec3& n1 = mNormals[mIndices[i + 1]];
+ PxVec3& n2 = mNormals[mIndices[i + 2]];
+ PxVec3 n = n0 + n1 + n2;
+ n.normalize();
+ n0 = n;
+ n1 = n;
+ n2 = n;
+ }
+ }
+}
+
+
+
+void ClothingPhysicalMeshImpl::updateOptimizationData()
+{
+ PX_ASSERT(mParams != NULL);
+
+ const int32_t numVertices = mParams->physicalMesh.vertices.arraySizes[0];
+ const uint32_t numBonesPerVertex = mParams->physicalMesh.numBonesPerVertex;
+
+ const float* boneWeights = mParams->physicalMesh.boneWeights.buf;
+ PX_ASSERT(boneWeights == NULL || mParams->physicalMesh.boneWeights.arraySizes[0] == numVertices * (int32_t)numBonesPerVertex);
+
+ const ClothingPhysicalMeshParametersNS::ConstrainCoefficient_Type* constrainCoeffs = mParams->physicalMesh.constrainCoefficients.buf;
+ PX_ASSERT(constrainCoeffs == NULL || mParams->physicalMesh.constrainCoefficients.arraySizes[0] == numVertices);
+
+ if (boneWeights == NULL && constrainCoeffs == NULL)
+ {
+ return;
+ }
+
+ uint32_t allocNumVertices = ((uint32_t)physx::shdfnd::ceil((float)numVertices / NUM_VERTICES_PER_CACHE_BLOCK)) * NUM_VERTICES_PER_CACHE_BLOCK; // allocate more to have a multiple of numVerticesPerCachBlock
+
+ NvParameterized::Handle optimizationDataHandle(*mParams, "physicalMesh.optimizationData");
+ PX_ASSERT(optimizationDataHandle.isValid());
+ optimizationDataHandle.resizeArray((int32_t)(allocNumVertices + 1) / 2);
+ uint8_t* optimizationData = mParams->physicalMesh.optimizationData.buf;
+ memset(optimizationData, 0, sizeof(uint8_t) * mParams->physicalMesh.optimizationData.arraySizes[0]);
+
+ for (int32_t i = 0; i < numVertices; ++i)
+ {
+ uint8_t numBones = 0;
+ if (boneWeights != NULL)
+ {
+ for (; numBones < numBonesPerVertex; numBones++)
+ {
+ if (boneWeights[i * numBonesPerVertex + numBones] == 0.0f)
+ {
+ break;
+ }
+ }
+ }
+
+ uint8_t& data = optimizationData[i / 2];
+ PX_ASSERT(numBones < 8); // we use 3 bits
+
+ if (constrainCoeffs != NULL)
+ {
+ uint8_t bitShift = 0;
+ if (i % 2 == 0)
+ {
+ data = 0;
+ }
+ else
+ {
+ bitShift = 4;
+ }
+ data |= numBones << bitShift;
+
+ // store for each vertex if collisionSphereDistance is < 0
+ if (constrainCoeffs[i].collisionSphereDistance < 0.0f)
+ {
+ data |= 8 << bitShift;
+ mParams->physicalMesh.hasNegativeBackstop = true;
+ }
+ else
+ {
+ data &= ~(8 << bitShift);
+ }
+ }
+ }
+}
+
+
+
+void ClothingPhysicalMeshImpl::updateMaxMaxDistance()
+{
+ const uint32_t numVertices = mParams->physicalMesh.numVertices;
+
+ float maxMaxDistance = 0.0f;
+ for (uint32_t i = 0; i < numVertices; i++)
+ {
+ maxMaxDistance = PxMax(maxMaxDistance, mConstrainCoefficients[i].maxDistance);
+ }
+
+ mParams->physicalMesh.maximumMaxDistance = maxMaxDistance;
+}
+
+
+
+void ClothingPhysicalMeshImpl::preSerialize(void* userData_)
+{
+ PX_UNUSED(userData_);
+
+ writeBackData();
+
+ // shrink the buffers
+
+ if (!mVertices.isEmpty() && mVertices.size() != mParams->physicalMesh.numVertices)
+ {
+ mVertices.resize(mParams->physicalMesh.numVertices);
+ }
+
+ if (!mNormals.isEmpty() && mNormals.size() != mParams->physicalMesh.numVertices)
+ {
+ mNormals.resize(mParams->physicalMesh.numVertices);
+ }
+
+ if (!mSkinningNormals.isEmpty() && mSkinningNormals.size() != mParams->physicalMesh.numVertices)
+ {
+ mSkinningNormals.resize(mParams->physicalMesh.numVertices);
+ }
+
+ if (!mConstrainCoefficients.isEmpty() && mConstrainCoefficients.size() != mParams->physicalMesh.numVertices)
+ {
+ mConstrainCoefficients.resize(mParams->physicalMesh.numVertices);
+ }
+
+ if (!mBoneIndices.isEmpty() && mBoneIndices.size() != mParams->physicalMesh.numVertices * mParams->physicalMesh.numBonesPerVertex)
+ {
+ mBoneIndices.resize(mParams->physicalMesh.numVertices * mParams->physicalMesh.numBonesPerVertex);
+ }
+
+ if (!mBoneWeights.isEmpty() && mBoneWeights.size() != mParams->physicalMesh.numVertices * mParams->physicalMesh.numBonesPerVertex)
+ {
+ mBoneWeights.resize(mParams->physicalMesh.numVertices * mParams->physicalMesh.numBonesPerVertex);
+ }
+
+ if (!mIndices.isEmpty() && mIndices.size() != mParams->physicalMesh.numIndices)
+ {
+ mIndices.resize(mParams->physicalMesh.numIndices);
+ }
+
+ updateOptimizationData();
+}
+
+
+
+void ClothingPhysicalMeshImpl::permuteBoneIndices(Array<int32_t>& old2newBoneIndices)
+{
+ if (mBoneIndices.size() == 0)
+ {
+ return;
+ }
+
+ const uint32_t numVertices = mParams->physicalMesh.numVertices;
+ const uint32_t numBonesPerVertex = mParams->physicalMesh.numBonesPerVertex;
+
+ for (uint32_t j = 0; j < numVertices; j++)
+ {
+ for (uint32_t k = 0; k < numBonesPerVertex; k++)
+ {
+ uint16_t& index = mBoneIndices[j * numBonesPerVertex + k];
+ PX_ASSERT(old2newBoneIndices[index] >= 0);
+ PX_ASSERT(old2newBoneIndices[index] <= 0xffff);
+ index = (uint16_t)old2newBoneIndices[index];
+ }
+ }
+}
+
+
+
+void ClothingPhysicalMeshImpl::applyTransformation(const PxMat44& transformation, float scale)
+{
+ const uint32_t numVertices = mParams->physicalMesh.numVertices;
+
+ PX_ASSERT(scale > 0.0f); // PH: negative scale won't work well here
+
+ for (uint32_t i = 0; i < numVertices; i++)
+ {
+ if (!mVertices.isEmpty())
+ {
+ mVertices[i] = (transformation.transform(mVertices[i])) * scale;
+ }
+ if (!mNormals.isEmpty())
+ {
+ mNormals[i] = transformation.transform(mNormals[i]);
+ }
+ if (!mSkinningNormals.isEmpty())
+ {
+ mSkinningNormals[i] = transformation.transform(mSkinningNormals[i]);
+ }
+ if (!mConstrainCoefficients.isEmpty())
+ {
+ mConstrainCoefficients[i].maxDistance *= scale;
+ mConstrainCoefficients[i].collisionSphereDistance *= scale;
+ mConstrainCoefficients[i].collisionSphereRadius *= scale;
+ }
+ }
+
+ PxMat33 t33(PxVec3(transformation.column0.x, transformation.column0.y, transformation.column0.z),
+ PxVec3(transformation.column1.x, transformation.column1.y, transformation.column1.z),
+ PxVec3(transformation.column2.x, transformation.column2.y, transformation.column2.z));
+
+ if (t33.getDeterminant() * scale < 0.0f)
+ {
+ const uint32_t numIndices = mParams->physicalMesh.numIndices;
+
+ if (mParams->physicalMesh.isTetrahedralMesh)
+ {
+ PX_ASSERT(numIndices % 4 == 0);
+ for (uint32_t i = 0; i < numIndices; i += 4)
+ {
+ nvidia::swap(mIndices[i + 2], mIndices[i + 3]);
+ }
+ }
+ else
+ {
+ // Flip the triangle indices to change winding (and thus normal generation in the PhysX SDK
+ PX_ASSERT(numIndices % 3 == 0);
+ for (uint32_t i = 0; i < numIndices; i += 3)
+ {
+ nvidia::swap(mIndices[i + 1], mIndices[i + 2]);
+ }
+ }
+
+ mParams->physicalMesh.flipNormals ^= true;
+
+ if (mParams->transitionDownB.buf != NULL || mParams->transitionUpB.buf != NULL)
+ {
+ APEX_DEBUG_WARNING("applyTransformation will not work with old assets, re-export from DCC tools");
+ }
+
+ const uint32_t numTransDown = (uint32_t)mParams->transitionDown.arraySizes[0];
+ for (uint32_t i = 0; i < numTransDown; i++)
+ {
+ mParams->transitionDown.buf[i].vertexBary.z *= scale;
+ mParams->transitionDown.buf[i].normalBary.z *= scale;
+ }
+
+ const uint32_t numTransUp = (uint32_t)mParams->transitionUp.arraySizes[0];
+ for (uint32_t i = 0; i < numTransUp; i++)
+ {
+ mParams->transitionUp.buf[i].vertexBary.z *= scale;
+ mParams->transitionUp.buf[i].normalBary.z *= scale;
+ }
+ }
+
+ mParams->physicalMesh.maximumMaxDistance *= scale;
+ mParams->physicalMesh.shortestEdgeLength *= scale;
+ mParams->physicalMesh.averageEdgeLength *= scale;
+}
+
+
+
+void ClothingPhysicalMeshImpl::applyPermutation(const Array<uint32_t>& permutation)
+{
+ const uint32_t numVertices = mParams->physicalMesh.numVertices;
+ const uint32_t numBonesPerVertex = mParams->physicalMesh.numBonesPerVertex;
+
+ if (!mVertices.isEmpty())
+ {
+ ApexPermute<PxVec3>(mVertices.begin(), &permutation[0], numVertices);
+ }
+
+ if (!mNormals.isEmpty())
+ {
+ ApexPermute<PxVec3>(mNormals.begin(), &permutation[0], numVertices);
+ }
+
+ if (!mSkinningNormals.isEmpty())
+ {
+ ApexPermute<PxVec3>(mSkinningNormals.begin(), &permutation[0], numVertices);
+ }
+
+ if (!mConstrainCoefficients.isEmpty())
+ {
+ ApexPermute<ClothingPhysicalMeshParametersNS::ConstrainCoefficient_Type>(mConstrainCoefficients.begin(), &permutation[0], numVertices);
+ }
+
+ if (!mBoneIndices.isEmpty())
+ {
+ ApexPermute<uint16_t>(mBoneIndices.begin(), &permutation[0], numVertices, numBonesPerVertex);
+ }
+
+ if (!mBoneWeights.isEmpty())
+ {
+ ApexPermute<float>(mBoneWeights.begin(), &permutation[0], numVertices, numBonesPerVertex);
+ }
+}
+
+
+
+struct OrderedTriangle
+{
+ void init(uint32_t _triNr, uint32_t _v0, uint32_t _v1, uint32_t _v2)
+ {
+ triNr = _triNr;
+ v0 = _v0;
+ v1 = _v1;
+ v2 = _v2;
+ // bubble sort
+ if (v0 > v1)
+ {
+ uint32_t v = v0;
+ v0 = v1;
+ v1 = v;
+ }
+ if (v1 > v2)
+ {
+ uint32_t v = v1;
+ v1 = v2;
+ v2 = v;
+ }
+ if (v0 > v1)
+ {
+ uint32_t v = v0;
+ v0 = v1;
+ v1 = v;
+ }
+ }
+ bool operator()(const OrderedTriangle& a, const OrderedTriangle& b) const
+ {
+ if (a.v0 < b.v0)
+ {
+ return true;
+ }
+ if (a.v0 > b.v0)
+ {
+ return false;
+ }
+ if (a.v1 < b.v1)
+ {
+ return true;
+ }
+ if (a.v1 > b.v1)
+ {
+ return false;
+ }
+ return (a.v2 < b.v2);
+ }
+ bool operator == (const OrderedTriangle& t) const
+ {
+ return v0 == t.v0 && v1 == t.v1 && v2 == t.v2;
+ }
+ uint32_t v0, v1, v2;
+ uint32_t triNr;
+};
+
+bool ClothingPhysicalMeshImpl::removeDuplicatedTriangles(uint32_t numIndices, uint32_t indexByteStride, const void* indices)
+{
+ uint32_t numTriangles = numIndices / 3;
+ Array<OrderedTriangle> triangles;
+ triangles.resize(numTriangles);
+
+ {
+ const uint8_t* srcIndices = (const uint8_t*)indices;
+ for (uint32_t i = 0; i < numTriangles; i++)
+ {
+ uint32_t i0 = *(const uint32_t*)(srcIndices);
+ srcIndices += indexByteStride;
+ uint32_t i1 = *(const uint32_t*)(srcIndices);
+ srcIndices += indexByteStride;
+ uint32_t i2 = *(const uint32_t*)(srcIndices);
+ srcIndices += indexByteStride;
+
+ triangles[i].init(i, i0, i1, i2);
+ }
+ }
+
+ nvidia::sort(triangles.begin(), triangles.size(), OrderedTriangle());
+
+ uint32_t fromPos = 0;
+ uint32_t toPos = 0;
+ while (fromPos < numTriangles)
+ {
+ OrderedTriangle& t = triangles[fromPos];
+ triangles[toPos] = t;
+ fromPos++;
+ toPos++;
+ while (fromPos < numTriangles && triangles[fromPos] == t)
+ {
+ fromPos++;
+ }
+ }
+ if (fromPos == toPos)
+ {
+ return false;
+ }
+
+ mParams->physicalMesh.numIndices = 3 * toPos;
+
+ mIndices.resize(mParams->physicalMesh.numIndices);
+ if (mParams->physicalMesh.numIndices > 0)
+ {
+ for (uint32_t i = 0; i < toPos; i++)
+ {
+ OrderedTriangle& t = triangles[i];
+ const uint8_t* srcIndices = (const uint8_t*)indices + 3 * t.triNr * indexByteStride;
+
+ mIndices[3 * i] = *(uint32_t*)srcIndices;
+ srcIndices += indexByteStride;
+ mIndices[3 * i + 1] = *(uint32_t*)srcIndices;
+ srcIndices += indexByteStride;
+ mIndices[3 * i + 2] = *(uint32_t*)srcIndices;
+ srcIndices += indexByteStride;
+ }
+ }
+
+ fixTriangleOrientations();
+ return true;
+}
+
+
+struct OrderedTriangleEdge
+{
+ void init(uint32_t _v0, uint32_t _v1, uint32_t _triNr, uint32_t _edgeNr)
+ {
+ if (_v0 < _v1)
+ {
+ v0 = _v0;
+ v1 = _v1;
+ }
+ else
+ {
+ v0 = _v1;
+ v1 = _v0;
+ }
+ triNr = _triNr;
+ edgeNr = _edgeNr;
+ }
+ bool operator()(const OrderedTriangleEdge& a, const OrderedTriangleEdge& b) const
+ {
+ if (a.v0 < b.v0)
+ {
+ return true;
+ }
+ if (a.v0 > b.v0)
+ {
+ return false;
+ }
+ return (a.v1 < b.v1);
+ }
+ bool operator == (const OrderedTriangleEdge& e) const
+ {
+ return v0 == e.v0 && v1 == e.v1;
+ }
+ uint32_t v0, v1;
+ uint32_t triNr, edgeNr;
+};
+
+void ClothingPhysicalMeshImpl::computeNeighborInformation(Array<int32_t> &neighbors)
+{
+ // compute neighbor information
+ const uint32_t numTriangles = mParams->physicalMesh.numIndices / 3;
+
+ Array<OrderedTriangleEdge> edges;
+ edges.resize(3 * numTriangles);
+
+ for (uint32_t i = 0; i < numTriangles; i++)
+ {
+ uint32_t i0 = mIndices[3 * i];
+ uint32_t i1 = mIndices[3 * i + 1];
+ uint32_t i2 = mIndices[3 * i + 2];
+ edges[3 * i ].init(i0, i1, i, 0);
+ edges[3 * i + 1].init(i1, i2, i, 1);
+ edges[3 * i + 2].init(i2, i0, i, 2);
+ }
+
+ nvidia::sort(edges.begin(), edges.size(), OrderedTriangleEdge());
+
+ neighbors.resize(3 * numTriangles, -1);
+
+ uint32_t i = 0;
+ while (i < edges.size())
+ {
+ OrderedTriangleEdge& e0 = edges[i];
+ i++;
+ while (i < edges.size() && edges[i] == e0)
+ {
+ OrderedTriangleEdge& e1 = edges[i];
+ neighbors[3 * e0.triNr + e0.edgeNr] = (int32_t)e1.triNr;
+ neighbors[3 * e1.triNr + e1.edgeNr] = (int32_t)e0.triNr;
+ i++;
+ }
+ }
+}
+
+
+void ClothingPhysicalMeshImpl::fixTriangleOrientations()
+{
+ PX_ASSERT(!mParams->physicalMesh.isTetrahedralMesh);
+ Array<int32_t> neighbors;
+ computeNeighborInformation(neighbors);
+
+ const uint32_t numTriangles = mParams->physicalMesh.numIndices / 3;
+
+ // 0 = non visited, 1 = visited, 2 = visited, to be flipped
+ Array<uint8_t> marks;
+ marks.resize(numTriangles, 0);
+
+ Array<uint32_t> queue;
+
+ for (uint32_t i = 0; i < numTriangles; i++)
+ {
+ if (marks[i] != 0)
+ {
+ continue;
+ }
+ queue.clear();
+ marks[i] = 1;
+ queue.pushBack(i);
+ while (!queue.empty())
+ {
+ uint32_t triNr = queue[queue.size() - 1];
+ queue.popBack();
+ for (uint32_t j = 0; j < 3; j++)
+ {
+ int adjNr = neighbors[3 * triNr + j];
+ if (adjNr < 0 || marks[(uint32_t)adjNr] != 0)
+ {
+ continue;
+ }
+ queue.pushBack((uint32_t)adjNr);
+ uint32_t i0, i1;
+ if (marks[triNr] == 1)
+ {
+ i0 = mIndices[3 * triNr + j];
+ i1 = mIndices[3 * triNr + ((j + 1) % 3)];
+ }
+ else
+ {
+ i1 = mIndices[3 * triNr + j];
+ i0 = mIndices[3 * triNr + ((j + 1) % 3)];
+ }
+ // don't swap here because this would corrupt the neighbor information
+ marks[(uint32_t)adjNr] = 1;
+ if (mIndices[3 * (uint32_t)adjNr + 0] == i0 && mIndices[3 * (uint32_t)adjNr + 1] == i1)
+ {
+ marks[(uint32_t)adjNr] = 2;
+ }
+ if (mIndices[3 * (uint32_t)adjNr + 1] == i0 && mIndices[3 * (uint32_t)adjNr + 2] == i1)
+ {
+ marks[(uint32_t)adjNr] = 2;
+ }
+ if (mIndices[3 * (uint32_t)adjNr + 2] == i0 && mIndices[3 * (uint32_t)adjNr + 0] == i1)
+ {
+ marks[(uint32_t)adjNr] = 2;
+ }
+ }
+ }
+ }
+ for (uint32_t i = 0; i < numTriangles; i++)
+ {
+ if (marks[i] == 2)
+ {
+ uint32_t i0 = mIndices[3 * i];
+ mIndices[3 * i] = mIndices[3 * i + 1];
+ mIndices[3 * i + 1] = i0;
+ }
+ }
+}
+
+}
+} // namespace nvidia
+
+