diff options
| author | git perforce import user <a@b> | 2016-10-25 12:29:14 -0600 |
|---|---|---|
| committer | Sheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees> | 2016-10-25 18:56:37 -0500 |
| commit | 3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch) | |
| tree | fa6485c169e50d7415a651bf838f5bcd0fd3bfbd /APEX_1.4/module/clothing/src/ClothingAssetImpl.cpp | |
| download | physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip | |
Initial commit:
PhysX 3.4.0 Update @ 21294896
APEX 1.4.0 Update @ 21275617
[CL 21300167]
Diffstat (limited to 'APEX_1.4/module/clothing/src/ClothingAssetImpl.cpp')
| -rw-r--r-- | APEX_1.4/module/clothing/src/ClothingAssetImpl.cpp | 3849 |
1 files changed, 3849 insertions, 0 deletions
diff --git a/APEX_1.4/module/clothing/src/ClothingAssetImpl.cpp b/APEX_1.4/module/clothing/src/ClothingAssetImpl.cpp new file mode 100644 index 00000000..cba5ef51 --- /dev/null +++ b/APEX_1.4/module/clothing/src/ClothingAssetImpl.cpp @@ -0,0 +1,3849 @@ +/* + * 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 "ClothingAsset.h" + +#include "ApexAuthorableObject.h" +#include "ClothingActorProxy.h" +#include "ClothingAssetImpl.h" +#include "ClothingAssetData.h" +#include "ClothingGlobals.h" +#include "ClothingPhysicalMeshImpl.h" +#include "ClothingPreviewProxy.h" +#include "ClothingScene.h" +#include "CookingPhysX.h" +#include "ModulePerfScope.h" +#include "nvparameterized/NvParamUtils.h" + +#include "ApexMath.h" +#include "ClothingActorParam.h" +#include "ClothingAssetParameters.h" +#include "ClothingGraphicalLodParameters.h" +#include "ModuleClothingHelpers.h" + +#include "PxStrideIterator.h" +#include "PsSort.h" +#include "PsThread.h" +#include "ApexUsingNamespace.h" +#include "PxMat33.h" +#include "PsVecMath.h" + +#include "SimulationAbstract.h" +#include "ApexPermute.h" + +#include "ApexPvdClient.h" +#include "PxPvdDataStream.h" + +#define PX_SIMD_SKINNING 1 + +#pragma warning( disable: 4101 ) // PX_COMPILE_TIME_ASSERT causes these warnings since they are +// used within our apex namespace + +namespace nvidia +{ +namespace clothing +{ + +AuthObjTypeID ClothingAssetImpl::mAssetTypeID = 0xffffffff; + +ClothingAssetImpl::ClothingAssetImpl(ModuleClothingImpl* module, ResourceList& list, const char* name) : + mModule(module), + mParams(DYNAMIC_CAST(ClothingAssetParameters*)(GetInternalApexSDK()->getParameterizedTraits()->createNvParameterized(ClothingAssetParameters::staticClassName()))), + mPhysicalMeshes(mParams, "physicalMeshes", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->physicalMeshes)), + mGraphicalLods(mParams, "graphicalLods", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->graphicalLods)), + mBones(mParams, "bones", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->bones)), + mBoneSpheres(mParams, "boneSpheres", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->boneSpheres)), + mSpherePairs(mParams, "boneSphereConnections", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->boneSphereConnections)), + mBoneActors(mParams, "boneActors", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->boneActors)), + mBoneVertices(mParams, "boneVertices", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->boneVertices)), + mBonePlanes(mParams, "bonePlanes", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->bonePlanes)), + mCollisionConvexes(mParams, "collisionConvexes", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->collisionConvexes)), + mName(name), + mExt2IntMorphMappingMaxValue(0), + mDirty(false), + mMorphMappingWarning(false) +{ + // this constructor is only executed when initializing the authoring asset + list.add(*this); + +#ifndef WITHOUT_PVD + mActors.setupForPvd(static_cast<ApexResourceInterface*>(this), "ClothingActors", "ClothingActor"); +#endif + + // make sure these two methods are compiled! + AbstractMeshDescription pcm; + pcm.avgEdgeLength = 0.1f; +} + + +ClothingAssetImpl::ClothingAssetImpl(ModuleClothingImpl* module, ResourceList& list, NvParameterized::Interface* params, const char* name) : + mModule(module), + mParams(NULL), + mName(name), + mExt2IntMorphMappingMaxValue(0), + mDirty(false), + mMorphMappingWarning(false) +{ + +#ifndef WITHOUT_PVD + mActors.setupForPvd(static_cast<ApexResourceInterface*>(this), "ClothingActors", "ClothingActor"); +#endif + + // wrong name? + if (params != NULL && ::strcmp(params->className(), ClothingAssetParameters::staticClassName()) != 0) + { + APEX_INTERNAL_ERROR( + "The parameterized interface is of type <%s> instead of <%s>. " + "This object will be initialized by an empty one instead!", + params->className(), + ClothingAssetParameters::staticClassName()); + + params->destroy(); + params = NULL; + } + else if (params != NULL) + { + ClothingAssetParameters* checkParams = DYNAMIC_CAST(ClothingAssetParameters*)(params); + + uint32_t boneRefsMesh = 0, boneRefsRB = 0; + for (int i = 0; i < checkParams->bones.arraySizes[0]; i++) + { + boneRefsMesh += checkParams->bones.buf[i].numMeshReferenced; + boneRefsRB += checkParams->bones.buf[i].numRigidBodiesReferenced; + } + + if (checkParams->bones.arraySizes[0] > 0 && (boneRefsRB + boneRefsMesh == 0)) + { + APEX_INTERNAL_ERROR( + "This parameterized object has not been prepared before serialization. " + "It will not be able to work and has been replaced by an empty one instead. " + "See NvParameterized::Interface::callPreSerializeCallback()"); + + params->destroy(); + params = NULL; + } + } + + if (params == NULL) + { + params = DYNAMIC_CAST(ClothingAssetParameters*)(GetInternalApexSDK()->getParameterizedTraits()->createNvParameterized(ClothingAssetParameters::staticClassName())); + } + + + PX_ASSERT(nvidia::strcmp(params->className(), ClothingAssetParameters::staticClassName()) == 0); + if (::strcmp(params->className(), ClothingAssetParameters::staticClassName()) == 0) + { + mParams = static_cast<ClothingAssetParameters*>(params); + mParams->setSerializationCallback(this, NULL); + + bool ok = false; + PX_UNUSED(ok); + + ok = mPhysicalMeshes.init(mParams, "physicalMeshes", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->physicalMeshes)); + PX_ASSERT(ok); + ok = mGraphicalLods.init(mParams, "graphicalLods", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->graphicalLods)); + PX_ASSERT(ok); + ok = mBones.init(mParams, "bones", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->bones)); + PX_ASSERT(ok); + ok = mBoneSpheres.init(mParams, "boneSpheres", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->boneSpheres)); + PX_ASSERT(ok); + ok = mSpherePairs.init(mParams, "boneSphereConnections", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->boneSphereConnections)); + PX_ASSERT(ok); + ok = mBoneActors.init(mParams, "boneActors", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->boneActors)); + PX_ASSERT(ok); + ok = mBoneVertices.init(mParams, "boneVertices", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->boneVertices)); + PX_ASSERT(ok); + ok = mBonePlanes.init(mParams, "bonePlanes", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->bonePlanes)); + PX_ASSERT(ok); + ok = mCollisionConvexes.init(mParams, "collisionConvexes", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->collisionConvexes)); + PX_ASSERT(ok); + + for (uint32_t i = 0; i < mGraphicalLods.size(); i++) + { + if (mGraphicalLods[i]->renderMeshAsset == NULL) + continue; + + char buf[128]; + size_t len = PxMin(mName.len(), 120u); + buf[0] = 0; + nvidia::strlcat(buf, len + 1, mName.c_str()); + buf[len] = '_'; + nvidia::snprintf(buf + len + 1, 128 - len - 1, "%d", i); + Asset* asset = GetApexSDK()->createAsset(mGraphicalLods[i]->renderMeshAsset, buf); + PX_ASSERT(::strcmp(asset->getObjTypeName(), RENDER_MESH_AUTHORING_TYPE_NAME) == 0); + + RenderMeshAssetIntl* rma = static_cast<RenderMeshAssetIntl*>(asset); + if (rma->mergeBinormalsIntoTangents()) + { + mDirty = true; + APEX_DEBUG_INFO("Performance warning. This asset <%s> has to be re-saved to speed up loading", name); + } + + mGraphicalLods[i]->renderMeshAssetPointer = rma; + mDirty |= reorderGraphicsVertices(i, i == 0); // only warn the first time + } + + bool cookingInvalid = false; + + for (uint32_t i = 0; i < mPhysicalMeshes.size(); i++) + { + bool reorderVerts = false; + if (mPhysicalMeshes[i]->physicalMesh.numMaxDistance0Vertices == 0) + { + reorderVerts = true; + } + + if (reorderVerts) + { + ClothingPhysicalMeshImpl* mesh = mModule->createPhysicalMeshInternal(mPhysicalMeshes[i]); + + if (mesh != NULL) + { + const bool changed = reorderDeformableVertices(*mesh); + + mesh->release(); + + cookingInvalid |= changed; + mDirty |= changed; + } + } + } + + if (mParams->materialLibrary != NULL && cookingInvalid) + { + // So, if we turn on zerostretch also for the new solver, we should make sure the values are set soft enough not to introduce too much ghost forces + ClothingMaterialLibraryParameters* matLib = static_cast<ClothingMaterialLibraryParameters*>(mParams->materialLibrary); + + for (int32_t i = 0; i < matLib->materials.arraySizes[0]; i++) + { + float& limit = matLib->materials.buf[i].hardStretchLimitation; + + if (limit >= 1.0f) + { + limit = PxMax(limit, 1.1f); // must be either 0 (disabled) or > 1.1 for stability + } + } + } + + if (mParams->boundingBox.minimum.isZero() && mParams->boundingBox.maximum.isZero()) + { + updateBoundingBox(); + } + + uint32_t cookNow = 0; + const char* cookedDataClass = "Embedded"; + + ParamArray<ClothingAssetParametersNS::CookedEntry_Type> cookedEntries(mParams, "cookedData", reinterpret_cast<ParamDynamicArrayStruct*>(&mParams->cookedData)); + for (uint32_t i = 0; i < cookedEntries.size(); i++) + { + if (cookedEntries[i].cookedData != NULL) + { + BackendFactory* cookedDataBackend = mModule->getBackendFactory(cookedEntries[i].cookedData->className()); + PX_ASSERT(cookedDataBackend); + if (cookedDataBackend != NULL) + { + // compare data version with current version of the data backend + uint32_t cookedDataVersion = cookedDataBackend->getCookedDataVersion(cookedEntries[i].cookedData); + uint32_t cookingVersion = cookedDataBackend->getCookingVersion(); + + if (cookingVersion != cookedDataVersion || cookingInvalid +#if PX_PHYSICS_VERSION_MAJOR == 3 + // don't use native CookingPhysX, only use embedded solver + || cookedDataVersion == CookingPhysX::getCookingVersion() +#endif + ) + { + cookNow = cookedDataVersion; + cookedEntries[i].cookedData->destroy(); + cookedEntries[i].cookedData = NULL; + } + } + } + } + if (cookNow != 0) + { + APEX_DEBUG_WARNING("Asset (%s) cooked data version (%d/0x%08x) does not match the current sdk version. Recooking.", name, cookNow, cookNow); + } + + if (cookedEntries.isEmpty()) + { + ClothingAssetParametersNS::CookedEntry_Type entry; + entry.scale = 1.0f; + entry.cookedData = NULL; + cookedEntries.pushBack(entry); + APEX_DEBUG_INFO("Asset (%s) has no cooked data and will be re-cooked every time it's loaded, asset needs to be resaved!", name); + cookNow = 1; + } + + if (cookNow != 0) + { + BackendFactory* cookingBackend = mModule->getBackendFactory(cookedDataClass); + + PX_ASSERT(cookingBackend != NULL); + + for (uint32_t i = 0; i < cookedEntries.size(); i++) + { + if (cookedEntries[i].cookedData == NULL && cookingBackend != NULL) + { + ClothingAssetParametersNS::CookedEntry_Type& entry = cookedEntries[i]; + + CookingAbstract* cookingJob = cookingBackend->createCookingJob(); + prepareCookingJob(*cookingJob, entry.scale, NULL, NULL); + + if (cookingJob->isValid()) + { + entry.cookedData = cookingJob->execute(); + } + PX_DELETE_AND_RESET(cookingJob); + } + } + + mDirty = true; + } + } + + list.add(*this); +} + + + +uint32_t ClothingAssetImpl::forceLoadAssets() +{ + uint32_t assetLoadedCount = 0; + + for (uint32_t i = 0; i < mGraphicalLods.size(); i++) + { + if (mGraphicalLods[i]->renderMeshAssetPointer == NULL) + continue; + + RenderMeshAssetIntl* rma = reinterpret_cast<RenderMeshAssetIntl*>(mGraphicalLods[i]->renderMeshAssetPointer); + assetLoadedCount += rma->forceLoadAssets(); + } + + return assetLoadedCount; + +} + + + +NvParameterized::Interface* ClothingAssetImpl::getDefaultActorDesc() +{ + NvParameterized::Interface* ret = NULL; + + if (mModule != NULL) + { + ret = mModule->getApexClothingActorParams(); + + if (ret != NULL) + { + ret->initDefaults(); + + // need to customize it per asset + PX_ASSERT(nvidia::strcmp(ret->className(), ClothingActorParam::staticClassName()) == 0); + ClothingActorParam* actorDesc = static_cast<ClothingActorParam*>(ret); + actorDesc->clothingMaterialIndex = mParams->materialIndex; + } + } + + return ret; +} + + + +NvParameterized::Interface* ClothingAssetImpl::getDefaultAssetPreviewDesc() +{ + NvParameterized::Interface* ret = NULL; + + if (mModule != NULL) + { + ret = mModule->getApexClothingPreviewParams(); + + if (ret != NULL) + { + ret->initDefaults(); + } + } + + return ret; +} + + + +Actor* ClothingAssetImpl::createApexActor(const NvParameterized::Interface& params, Scene& apexScene) +{ + if (!isValidForActorCreation(params, apexScene)) + { + return NULL; + } + ClothingActorProxy* proxy = NULL; + ClothingScene* clothingScene = mModule->getClothingScene(apexScene); + proxy = PX_NEW(ClothingActorProxy)(params, this, *clothingScene, &mActors); + PX_ASSERT(proxy != NULL); + return proxy; +} + + +NvParameterized::Interface* ClothingAssetImpl::releaseAndReturnNvParameterizedInterface() +{ + NvParameterized::Interface* ret = mParams; + mParams->setSerializationCallback(NULL, NULL); + mParams = NULL; + release(); + return ret; +} + + + +bool ClothingAssetImpl::isValidForActorCreation(const NvParameterized::Interface& params, Scene& apexScene) const +{ + bool ret = false; + + if (ClothingActorImpl::isValidDesc(params)) + { + ClothingScene* clothingScene = mModule->getClothingScene(apexScene); + if (clothingScene) + { + if (!clothingScene->isSimulating()) + { + ret = true; + } + else + { + APEX_INTERNAL_ERROR("Cannot create ClothingActor while simulation is running"); + } + } + } + else + { + APEX_INVALID_PARAMETER("ClothingActorDesc is invalid"); + } + return ret; +} + + +bool ClothingAssetImpl::isDirty() const +{ + return mDirty; +} + + + +void ClothingAssetImpl::release() +{ + mModule->mSdk->releaseAsset(*this); +} + + + +ClothingActor* ClothingAssetImpl::getActor(uint32_t index) +{ + READ_ZONE(); + if (index < mActors.getSize()) + { + return DYNAMIC_CAST(ClothingActorProxy*)(mActors.getResource(index)); + } + return NULL; +} + + + +float ClothingAssetImpl::getMaximumSimulationBudget(uint32_t solverIterations) const +{ + uint32_t maxCost = 0; + + for (uint32_t i = 0; i < mPhysicalMeshes.size(); i++) + { + uint32_t iterations = (uint32_t)(mPhysicalMeshes[i]->physicalMesh.numSimulatedVertices * solverIterations); + maxCost = PxMax(maxCost, iterations); + } + + return static_cast<float>(maxCost); +} + + + +uint32_t ClothingAssetImpl::getNumGraphicalLodLevels() const +{ + READ_ZONE(); + return mGraphicalLods.size(); +} + + + +uint32_t ClothingAssetImpl::getGraphicalLodValue(uint32_t lodLevel) const +{ + READ_ZONE(); + if (lodLevel < mGraphicalLods.size()) + { + return mGraphicalLods[lodLevel]->lod; + } + + return uint32_t(-1); +} + + + +float ClothingAssetImpl::getBiggestMaxDistance() const +{ + READ_ZONE(); + float maxValue = 0.0f; + for (uint32_t i = 0; i < mPhysicalMeshes.size(); i++) + { + maxValue = PxMax(maxValue, mPhysicalMeshes[i]->physicalMesh.maximumMaxDistance); + } + + return maxValue; +} + + + +bool ClothingAssetImpl::remapBoneIndex(const char* name, uint32_t newIndex) +{ + WRITE_ZONE(); + uint32_t found = 0; + const uint32_t numBones = mBones.size(); + for (uint32_t i = 0; i < numBones; i++) + { + if (mBones[i].name != NULL && (::strcmp(mBones[i].name, name) == 0)) + { + mBones[i].externalIndex = (int32_t)newIndex; + found++; + } + } + + if (found > 1) + { + APEX_DEBUG_WARNING("The asset contains %i bones with name %s. All occurences were mapped.", found, name); + } + else if (found == 0) + { + APEX_DEBUG_INFO("The asset does not contain a bone with name %s", name); + } + + return (found == 1); // sanity +} + + + +const char* ClothingAssetImpl::getBoneName(uint32_t internalIndex) const +{ + READ_ZONE(); + if (internalIndex >= mBones.size()) + { + return ""; + } + + return mBones[internalIndex].name; +} + + + +bool ClothingAssetImpl::getBoneBasePose(uint32_t internalIndex, PxMat44& result) const +{ + READ_ZONE(); + if (internalIndex < mBones.size()) + { + result = mBones[internalIndex].bindPose; + return true; + } + return false; +} + + + +void ClothingAssetImpl::getBoneMapping(uint32_t* internal2externalMap) const +{ + READ_ZONE(); + for (uint32_t i = 0; i < mBones.size(); i++) + { + internal2externalMap[(uint32_t)mBones[i].internalIndex] = (uint32_t)mBones[i].externalIndex; + } +} + + + +uint32_t ClothingAssetImpl::prepareMorphTargetMapping(const PxVec3* originalPositions, uint32_t numPositions, float epsilon) +{ + WRITE_ZONE(); + uint32_t numInternalVertices = 0; + for (uint32_t i = 0; i < mGraphicalLods.size(); i++) + { + RenderMeshAssetIntl* rma = getGraphicalMesh(i); + if (rma == NULL) + continue; + + for (uint32_t s = 0; s < rma->getSubmeshCount(); s++) + { + numInternalVertices += rma->getSubmesh(s).getVertexCount(0); + } + } + numInternalVertices += mBoneVertices.size(); + + mExt2IntMorphMapping.resize(numInternalVertices); + uint32_t indexWritten = 0; + + uint32_t numLarger = 0; + float epsilon2 = epsilon * epsilon; + + for (uint32_t i = 0; i < mGraphicalLods.size(); i++) + { + RenderMeshAssetIntl* rma = getGraphicalMesh(i); + if (rma == NULL) + continue; + + for (uint32_t s = 0; s < rma->getSubmeshCount(); s++) + { + const uint32_t numVertices = rma->getSubmesh(s).getVertexCount(0); + const VertexFormat& format = rma->getSubmesh(s).getVertexBuffer().getFormat(); + const uint32_t positionIndex = (uint32_t)format.getBufferIndexFromID(format.getSemanticID(RenderVertexSemantic::POSITION)); + if (format.getBufferFormat(positionIndex) == RenderDataFormat::FLOAT3) + { + PxVec3* positions = (PxVec3*)rma->getSubmesh(s).getVertexBuffer().getBuffer(positionIndex); + for (uint32_t v = 0; v < numVertices; v++) + { + float closestDist2 = PX_MAX_F32; + uint32_t closestIndex = (uint32_t) - 1; + for (uint32_t iv = 0; iv < numPositions; iv++) + { + float dist2 = (originalPositions[iv] - positions[v]).magnitudeSquared(); + if (dist2 < closestDist2) + { + closestDist2 = dist2; + closestIndex = iv; + } + } + PX_ASSERT(closestIndex != (uint32_t) - 1); + mExt2IntMorphMapping[indexWritten++] = closestIndex; + numLarger += closestDist2 < epsilon2 ? 0 : 1; + } + } + else + { + PX_ALWAYS_ASSERT(); + } + } + } + + for (uint32_t i = 0; i < mBoneVertices.size(); i++) + { + float closestDist2 = PX_MAX_F32; + uint32_t closestIndex = (uint32_t) - 1; + for (uint32_t iv = 0; iv < numPositions; iv++) + { + float dist2 = (originalPositions[iv] - mBoneVertices[i]).magnitudeSquared(); + if (dist2 < closestDist2) + { + closestDist2 = dist2; + closestIndex = iv; + } + } + PX_ASSERT(closestIndex != (uint32_t) - 1); + mExt2IntMorphMapping[indexWritten++] = closestIndex; + numLarger += closestDist2 < epsilon2 ? 0 : 1; + } + + PX_ASSERT(indexWritten == numInternalVertices); + mExt2IntMorphMappingMaxValue = numPositions; + + return numLarger; +} + + + +void ClothingAssetImpl::preSerialize(void* /*userData*/) +{ + mDirty = false; +} + + + +RenderMeshAssetIntl* ClothingAssetImpl::getGraphicalMesh(uint32_t index) +{ + if (index < mGraphicalLods.size()) + { + return reinterpret_cast<RenderMeshAssetIntl*>(mGraphicalLods[index]->renderMeshAssetPointer); + } + + return NULL; +} + + + +const ClothingGraphicalLodParameters* ClothingAssetImpl::getGraphicalLod(uint32_t index) const +{ + if (index < mGraphicalLods.size()) + { + return mGraphicalLods[index]; + } + + return NULL; +} + + +void ClothingAssetImpl::releaseClothingActor(ClothingActor& actor) +{ + ClothingActorProxy* proxy = DYNAMIC_CAST(ClothingActorProxy*)(&actor); + proxy->destroy(); +} + + + +void ClothingAssetImpl::releaseClothingPreview(ClothingPreview& preview) +{ + ClothingPreviewProxy* proxy = DYNAMIC_CAST(ClothingPreviewProxy*)(&preview); + proxy->destroy(); +} + + + +bool ClothingAssetImpl::writeBoneMatrices(PxMat44 localPose, const PxMat44* newBoneMatrices, const uint32_t byteStride, + const uint32_t numBones, PxMat44* dest, bool isInternalOrder, bool multInvBindPose) +{ + PX_PROFILE_ZONE("ClothingAssetImpl::writeBoneMatrices", GetInternalApexSDK()->getContextId()); + + PX_ASSERT(byteStride >= sizeof(PxMat44)); + + bool changed = false; + + if (mBones.isEmpty()) + { + APEX_INTERNAL_ERROR("bone map is empty, this is a error condition"); + } + else + { + // PH: if bones are present, but not set, we just have to write them with global pose + const uint8_t* src = (const uint8_t*)newBoneMatrices; + const uint32_t numBonesReferenced = mParams->bonesReferenced; + for (uint32_t i = 0; i < mBones.size(); i++) + { + PX_ASSERT(mBones[i].internalIndex < (int32_t)mBones.size()); + PX_ASSERT(isInternalOrder || mBones[i].externalIndex >= 0); + const uint32_t internalIndex = i; + const uint32_t externalIndex = isInternalOrder ? i : mBones[i].externalIndex; + if (internalIndex < numBonesReferenced && mBones[i].internalIndex >= 0) + { + PxMat44& oldMat = dest[internalIndex]; + PX_ALIGN(16, PxMat44) newMat; + + if (src != NULL && externalIndex < numBones) + { + PxMat44 skinningTransform = *(const PxMat44*)(src + byteStride * externalIndex); + if (multInvBindPose) + { + skinningTransform = skinningTransform * mInvBindPoses[internalIndex]; + } + newMat = localPose * skinningTransform; + } + else + { + newMat = localPose; + } + + if (newMat != oldMat) // PH: let's hope this comparison is not too slow + { + changed |= (i < numBonesReferenced); + oldMat = newMat; + } + } + } + } + return changed; +} + + + +ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* ClothingAssetImpl::getPhysicalMeshFromLod(uint32_t graphicalLodId) const +{ + const uint32_t physicalMeshId = mGraphicalLods[graphicalLodId]->physicalMeshId; + + // -1 is also bigger than mPhysicalMeshes.size() for an unsigned + if (physicalMeshId >= mPhysicalMeshes.size()) + { + return NULL; + } + + return &mPhysicalMeshes[physicalMeshId]->physicalMesh; +} + + +ClothingPhysicalMeshParametersNS::SkinClothMapB_Type* ClothingAssetImpl::getTransitionMapB(uint32_t dstPhysicalMeshId, uint32_t srcPhysicalMeshId, float& thickness, float& offset) +{ + if (srcPhysicalMeshId + 1 == dstPhysicalMeshId) + { + thickness = mPhysicalMeshes[dstPhysicalMeshId]->transitionDownThickness; + offset = mPhysicalMeshes[dstPhysicalMeshId]->transitionDownOffset; + return mPhysicalMeshes[dstPhysicalMeshId]->transitionDownB.buf; + } + else if (srcPhysicalMeshId == dstPhysicalMeshId + 1) + { + thickness = mPhysicalMeshes[dstPhysicalMeshId]->transitionUpThickness; + offset = mPhysicalMeshes[dstPhysicalMeshId]->transitionUpOffset; + return mPhysicalMeshes[dstPhysicalMeshId]->transitionUpB.buf; + } + + thickness = 0.0f; + offset = 0.0f; + return NULL; +} + + + +ClothingPhysicalMeshParametersNS::SkinClothMapD_Type* ClothingAssetImpl::getTransitionMap(uint32_t dstPhysicalMeshId, uint32_t srcPhysicalMeshId, float& thickness, float& offset) +{ + if (srcPhysicalMeshId + 1 == dstPhysicalMeshId) + { + thickness = mPhysicalMeshes[dstPhysicalMeshId]->transitionDownThickness; + offset = mPhysicalMeshes[dstPhysicalMeshId]->transitionDownOffset; + return mPhysicalMeshes[dstPhysicalMeshId]->transitionDown.buf; + } + else if (srcPhysicalMeshId == dstPhysicalMeshId + 1) + { + thickness = mPhysicalMeshes[dstPhysicalMeshId]->transitionUpThickness; + offset = mPhysicalMeshes[dstPhysicalMeshId]->transitionUpOffset; + return mPhysicalMeshes[dstPhysicalMeshId]->transitionUp.buf; + } + + thickness = 0.0f; + offset = 0.0f; + return NULL; +} + + + +NvParameterized::Interface* ClothingAssetImpl::getCookedData(float actorScale) +{ + NvParameterized::Interface* closest = NULL; + float closestDiff = PX_MAX_F32; + + for (int32_t i = 0; i < mParams->cookedData.arraySizes[0]; i++) + { + NvParameterized::Interface* cookedData = mParams->cookedData.buf[i].cookedData; + const float cookedDataScale = mParams->cookedData.buf[i].scale; + + if (cookedData == NULL) + { + continue; + } + +#ifdef _DEBUG + if (::strcmp(cookedData->className(), ClothingCookedParam::staticClassName()) == 0) + { + // silly debug verification + PX_ASSERT(cookedDataScale == ((ClothingCookedParam*)cookedData)->actorScale); + } +#endif + + const float scaleDiff = PxAbs(actorScale - cookedDataScale); + if (scaleDiff < closestDiff) + { + closest = cookedData; + closestDiff = scaleDiff; + } + } + + if (closest != NULL && closestDiff < 0.01f) + { + return closest; + } + + return NULL; +} + + + +uint32_t ClothingAssetImpl::getCookedPhysXVersion() const +{ + uint32_t version = 0; + + for (int32_t i = 0; i < mParams->cookedData.arraySizes[0]; i++) + { + const NvParameterized::Interface* cookedData = mParams->cookedData.buf[i].cookedData; + if (cookedData != NULL) + { + const char* className = cookedData->className(); + BackendFactory* factory = mModule->getBackendFactory(className); + + uint32_t v = factory->getCookedDataVersion(cookedData); + PX_ASSERT(version == 0 || version == v); + version = v; + } + } + + // version == 0 usually means that there is no maxdistance > 0 at all! + //PX_ASSERT(version != 0); + return version; +} + + + +ClothSolverMode::Enum ClothingAssetImpl::getClothSolverMode() const +{ + return ClothSolverMode::v3; +} + + + +SimulationAbstract* ClothingAssetImpl::getSimulation(uint32_t physicalMeshId, NvParameterized::Interface* cookedParam, ClothingScene* clothingScene) +{ + SimulationAbstract* result = NULL; + + mUnusedSimulationMutex.lock(); + + for (uint32_t i = 0; i < mUnusedSimulation.size(); i++) + { + PX_ASSERT(mUnusedSimulation[i] != NULL); + if (mUnusedSimulation[i]->physicalMeshId != physicalMeshId) + { + continue; + } + + if (mUnusedSimulation[i]->getCookedData() != cookedParam) + { + continue; + } + + if (mUnusedSimulation[i]->getClothingScene() != clothingScene) + { + continue; + } + + // we found one + result = mUnusedSimulation[i]; + for (uint32_t j = i + 1; j < mUnusedSimulation.size(); j++) + { + mUnusedSimulation[j - 1] = mUnusedSimulation[j]; + } + + mUnusedSimulation.popBack(); + break; + } + + mUnusedSimulationMutex.unlock(); + + return result; +} + + + +void ClothingAssetImpl::returnSimulation(SimulationAbstract* simulation) +{ + PX_ASSERT(simulation != NULL); + + bool isAssetParam = false; + for (int32_t i = 0; i < mParams->cookedData.arraySizes[0]; i++) + { + isAssetParam |= mParams->cookedData.buf[i].cookedData == simulation->getCookedData(); + } + + nvidia::Mutex::ScopedLock scopeLock(mUnusedSimulationMutex); + + if (mModule->getMaxUnusedPhysXResources() == 0 || !isAssetParam || !simulation->needsExpensiveCreation()) + { + destroySimulation(simulation); + return; + } + + if (mUnusedSimulation.size() > mModule->getMaxUnusedPhysXResources()) + { + destroySimulation(mUnusedSimulation[0]); + + for (uint32_t i = 1; i < mUnusedSimulation.size(); i++) + { + mUnusedSimulation[i - 1] = mUnusedSimulation[i]; + } + + mUnusedSimulation[mUnusedSimulation.size() - 1] = simulation; + } + else + { + mUnusedSimulation.pushBack(simulation); + } + + simulation->disablePhysX(mModule->getDummyActor()); +} + + + +void ClothingAssetImpl::destroySimulation(SimulationAbstract* simulation) +{ + PX_ASSERT(simulation != NULL); + + simulation->unregisterPhysX(); + PX_DELETE_AND_RESET(simulation); +} + + + +void ClothingAssetImpl::initCollision(SimulationAbstract* simulation, const PxMat44* boneTansformations, + ResourceList& actorPlanes, + ResourceList& actorConvexes, + ResourceList& actorSpheres, + ResourceList& actorCapsules, + ResourceList& actorTriangleMeshes, + const ClothingActorParam* actorParam, + const PxMat44& globalPose, bool localSpaceSim) +{ + simulation->initCollision( mBoneActors.begin(), mBoneActors.size(), mBoneSpheres.begin(), mBoneSpheres.size(), mSpherePairs.begin(), mSpherePairs.size(), mBonePlanes.begin(), mBonePlanes.size(), mCollisionConvexes.begin(), mCollisionConvexes.size(), mBones.begin(), boneTansformations, + actorPlanes, actorConvexes, actorSpheres, actorCapsules, actorTriangleMeshes, + actorParam->actorDescTemplate, actorParam->shapeDescTemplate, actorParam->actorScale, + globalPose, localSpaceSim); +} + + + +void ClothingAssetImpl::updateCollision(SimulationAbstract* simulation, const PxMat44* boneTansformations, + ResourceList& actorPlanes, + ResourceList& actorConvexes, + ResourceList& actorSpheres, + ResourceList& actorCapsules, + ResourceList& actorTriangleMeshes, + bool teleport) +{ + simulation->updateCollision(mBoneActors.begin(), mBoneActors.size(), mBoneSpheres.begin(), mBoneSpheres.size(), mBonePlanes.begin(), mBonePlanes.size(), mBones.begin(), boneTansformations, + actorPlanes, actorConvexes, actorSpheres, actorCapsules, actorTriangleMeshes, teleport); +} + + + +uint32_t ClothingAssetImpl::getPhysicalMeshID(uint32_t graphicalLodId) const +{ + if (graphicalLodId >= mGraphicalLods.size()) + { + return (uint32_t) - 1; + } + + return mGraphicalLods[graphicalLodId]->physicalMeshId; +} + + + +void ClothingAssetImpl::visualizeSkinCloth(RenderDebugInterface& renderDebug, AbstractMeshDescription& srcPM, bool showTets, float actorScale) const +{ +#ifdef WITHOUT_DEBUG_VISUALIZE + PX_UNUSED(renderDebug); + PX_UNUSED(srcPM); + PX_UNUSED(showTets); + PX_UNUSED(actorScale); +#else + using RENDER_DEBUG::DebugColors; + using RENDER_DEBUG::DebugRenderState; + + if ((srcPM.pPosition != NULL) && (srcPM.pIndices != NULL) && (srcPM.pNormal != NULL)) + { + RENDER_DEBUG_IFACE(&renderDebug)->pushRenderState(); + + RENDER_DEBUG_IFACE(&renderDebug)->removeFromCurrentState(DebugRenderState::SolidShaded); + + const uint32_t colorWhite = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::White); + const uint32_t colorRed = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Red); + const uint32_t colorDarkRed = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::DarkRed); + const uint32_t colorGreen = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Green); + const uint32_t colorPurple = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Purple); + + const float meshThickness = srcPM.avgEdgeLength * actorScale; + // for each triangle in the Physical Mesh draw lines delimiting all the tetras + for (uint32_t i = 0; i < srcPM.numIndices; i += 3) + { + if (showTets) + { + PxVec3 vtx[3], nrm[3]; + getNormalsAndVerticesForFace(vtx, nrm, i, srcPM); + + // draw lines for all edges of each tetrahedron (sure, there are redundant lines, but this is for debugging purposes) + for (uint32_t tIdx = 0; tIdx < TETRA_LUT_SIZE; tIdx++) + { + // compute the tetra vertices based on the index + const TetraEncoding& tetEnc = tetraTable[tIdx]; + PxVec3 tv0 = vtx[0] + (tetEnc.sign[0] * nrm[0] * meshThickness); + PxVec3 tv1 = vtx[1] + (tetEnc.sign[1] * nrm[1] * meshThickness); + PxVec3 tv2 = vtx[2] + (tetEnc.sign[2] * nrm[2] * meshThickness); + PxVec3 tv3 = vtx[tetEnc.lastVtxIdx] + (tetEnc.sign[3] * nrm[tetEnc.lastVtxIdx] * meshThickness); + + uint32_t color = tIdx < 3 ? colorGreen : colorPurple; + + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(color); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(tv1, tv2); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(tv2, tv3); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(tv3, tv1); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(tv0, tv1); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(tv0, tv2); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(tv0, tv3); + } + } + else + { + uint32_t idx[3] = + { + srcPM.pIndices[i + 0], + srcPM.pIndices[i + 1], + srcPM.pIndices[i + 2], + }; + + PxVec3 vtx[3], nrm[3]; + for (uint32_t u = 0; u < 3; u++) + { + vtx[u] = srcPM.pPosition[idx[u]]; + nrm[u] = srcPM.pNormal[idx[u]] * meshThickness; + } + + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorWhite); + RENDER_DEBUG_IFACE(&renderDebug)->debugTri(vtx[0], vtx[1], vtx[2]); + + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorGreen); + RENDER_DEBUG_IFACE(&renderDebug)->debugTri(vtx[0] + nrm[0], vtx[1] + nrm[1], vtx[2] + nrm[2]); + for (uint32_t u = 0; u < 3; u++) + { + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(vtx[u], vtx[u] + nrm[u]); + } + + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorPurple); + RENDER_DEBUG_IFACE(&renderDebug)->debugTri(vtx[0] - nrm[0], vtx[1] - nrm[1], vtx[2] - nrm[2]); + for (uint32_t u = 0; u < 3; u++) + { + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(vtx[u], vtx[u] - nrm[u]); + } + } + } + +#if 1 + // display some features of the physical mesh as it's updated at runtime + + srcPM.UpdateDerivedInformation(&renderDebug); + + // draw the mesh's bounding box + PxBounds3 bounds(srcPM.pMin, srcPM.pMax); + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorRed); + RENDER_DEBUG_IFACE(&renderDebug)->debugBound(bounds); + + // draw line from the centroid to the top-most corner of the AABB + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorDarkRed); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(srcPM.centroid, srcPM.pMax); + + // draw white line along the same direction (but negated) to display the avgEdgeLength + PxVec3 dirToPMax = (srcPM.pMax - srcPM.centroid); + dirToPMax.normalize(); + PxVec3 pEdgeLengthAway = srcPM.centroid - (dirToPMax * srcPM.avgEdgeLength); + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorWhite); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(srcPM.centroid, pEdgeLengthAway); + + // draw green line along the same direction to display the physical mesh thickness + PxVec3 pPmThicknessAway = srcPM.centroid + (dirToPMax * meshThickness); + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorGreen); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(srcPM.centroid, pPmThicknessAway); +#endif + + RENDER_DEBUG_IFACE(&renderDebug)->popRenderState(); + } +#endif +} + + + +void ClothingAssetImpl::visualizeSkinClothMap(RenderDebugInterface& renderDebug, AbstractMeshDescription& srcPM, + SkinClothMapB* skinClothMapB, uint32_t skinClothMapBSize, + SkinClothMap* skinClothMap, uint32_t skinClothMapSize, + float actorScale, bool onlyBad, bool invalidBary) const +{ +#ifdef WITHOUT_DEBUG_VISUALIZE + PX_UNUSED(renderDebug); + PX_UNUSED(srcPM); + PX_UNUSED(skinClothMapB); + PX_UNUSED(skinClothMapBSize); + PX_UNUSED(skinClothMap); + PX_UNUSED(skinClothMapSize); + PX_UNUSED(actorScale); + PX_UNUSED(onlyBad); + PX_UNUSED(invalidBary); +#else + using RENDER_DEBUG::DebugColors; + using RENDER_DEBUG::DebugRenderState; + + RENDER_DEBUG_IFACE(&renderDebug)->pushRenderState(); + + RENDER_DEBUG_IFACE(&renderDebug)->removeFromCurrentState(DebugRenderState::SolidShaded); + const float meshThickness = srcPM.avgEdgeLength * actorScale; + + if ((skinClothMapB != NULL) && (srcPM.pPosition != NULL) && (srcPM.pIndices != NULL) && (srcPM.pNormal != NULL)) + { + const uint32_t colorRed = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Red); + const uint32_t colorDarkRed = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::DarkRed); + const uint32_t colorBlue = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Blue); + const uint32_t colorYellow = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Yellow); + const uint32_t colorGreen = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Green); + const uint32_t colorPurple = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Purple); + + for (uint32_t i = 0; i < skinClothMapBSize; i++) + { + const SkinClothMapB& mapping = skinClothMapB[i]; + + if (mapping.faceIndex0 >= srcPM.numIndices) + { + break; + } + + const PxVec3 vtb = mapping.vtxTetraBary; + const float vtb_w = 1.0f - vtb.x - vtb.y - vtb.z; + const bool badVtx = + vtb.x < 0.0f || vtb.x > 1.0f || + vtb.y < 0.0f || vtb.y > 1.0f || + vtb.z < 0.0f || vtb.z > 1.0f || + vtb_w < 0.0f || vtb_w > 1.0f; + + const PxVec3 ntb = mapping.nrmTetraBary; + const float ntb_w = 1.0f - ntb.x - ntb.y - ntb.z; + + const bool badNrm = + ntb.x < 0.0f || ntb.x > 1.0f || + ntb.y < 0.0f || ntb.y > 1.0f || + ntb.z < 0.0f || ntb.z > 1.0f || + ntb_w < 0.0f || ntb_w > 1.0f; + + if (!onlyBad || badVtx || badNrm) + { + PxVec3 vtx[3], nrm[3]; + getNormalsAndVerticesForFace(vtx, nrm, mapping.faceIndex0, srcPM); + + const TetraEncoding& tetEnc = tetraTable[mapping.tetraIndex]; + + const PxVec3 tv0 = vtx[0] + (tetEnc.sign[0] * nrm[0] * meshThickness); + const PxVec3 tv1 = vtx[1] + (tetEnc.sign[1] * nrm[1] * meshThickness); + const PxVec3 tv2 = vtx[2] + (tetEnc.sign[2] * nrm[2] * meshThickness); + const PxVec3 tv3 = vtx[tetEnc.lastVtxIdx] + (tetEnc.sign[3] * nrm[tetEnc.lastVtxIdx] * meshThickness); + + const PxVec3 centroid = (tv0 + tv1 + tv2 + tv3) * 0.25f; + const PxVec3 graphicsPos = (vtb.x * tv0) + (vtb.y * tv1) + (vtb.z * tv2) + (vtb_w * tv3); + const PxVec3 graphicsNrm = (ntb.x * tv0) + (ntb.y * tv1) + (ntb.z * tv2) + (ntb_w * tv3); + + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorYellow); + RENDER_DEBUG_IFACE(&renderDebug)->debugPoint(graphicsPos, meshThickness * 0.1f); + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorBlue); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(graphicsPos, graphicsNrm); + + if (badVtx && onlyBad) + { + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorRed); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(centroid, graphicsPos); + } + + if (badNrm && onlyBad) + { + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorDarkRed); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(centroid, graphicsPos); + } + + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(mapping.tetraIndex < 3 ? colorGreen : colorPurple); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(tv1, tv2); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(tv2, tv3); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(tv3, tv1); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(tv0, tv1); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(tv0, tv2); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(tv0, tv3); + } + } + } + else if ((skinClothMap != NULL) && (srcPM.pPosition != NULL) && (srcPM.pIndices != NULL) && (srcPM.pNormal != NULL)) + { + const uint32_t colorWhite = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::White); + const uint32_t colorRed = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Red); + const uint32_t colorDarkRed = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::DarkRed); + const uint32_t colorBlue = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Blue); + const uint32_t colorYellow = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Yellow); + const uint32_t colorGreen = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Green); + const uint32_t colorPurple = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Purple); + + for (uint32_t i = 0; i < skinClothMapSize; i++) + { + const SkinClothMap& mapping = skinClothMap[i]; + + if (mapping.vertexIndex0 >= srcPM.numVertices || mapping.vertexIndex1 >= srcPM.numVertices || mapping.vertexIndex2 >= srcPM.numVertices) + { + continue; + } + + PxVec3 baryVtx = mapping.vertexBary; + const float heightVtx = baryVtx.z; + baryVtx.z = 1.0f - baryVtx.x - baryVtx.y; + + PxVec3 baryNrm = mapping.normalBary; + const float heightNrm = baryNrm.z; + baryNrm.z = 1.0f - baryNrm.x - baryNrm.y; + + bool badVtx = + baryVtx.x < 0.0f || baryVtx.x > 1.0f || + baryVtx.y < 0.0f || baryVtx.y > 1.0f || + baryVtx.z < 0.0f || baryVtx.z > 1.0f || + heightVtx < -1.0f || heightVtx > 1.0f; + + bool badNrm = + baryNrm.x < 0.0f || baryNrm.x > 1.0f || + baryNrm.y < 0.0f || baryNrm.y > 1.0f || + baryNrm.z < 0.0f || baryNrm.z > 1.0f || + heightNrm < -1.0f || heightNrm > 1.0f; + + if (!onlyBad || badVtx || badNrm) + { + uint32_t idx[3] = + { + mapping.vertexIndex0, + mapping.vertexIndex1, + mapping.vertexIndex2, + }; + + PxVec3 vtx[3], nrm[3]; + PxVec3 centroid(0.0f); + for (uint32_t j = 0; j < 3; j++) + { + vtx[j] = srcPM.pPosition[idx[j]]; + nrm[j] = srcPM.pNormal[idx[j]] * meshThickness; + centroid += vtx[j]; + } + centroid /= 3.0f; + + + uint32_t b = (uint32_t)(255 * i / skinClothMapSize); + uint32_t color = (b << 16) + (b << 8) + b; + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(color); + + PxVec3 vertexBary = mapping.vertexBary; + float vertexHeight = vertexBary.z; + vertexBary.z = 1.0f - vertexBary.x - vertexBary.y; + const PxVec3 vertexPos = vertexBary.x * vtx[0] + vertexBary.y * vtx[1] + vertexBary.z * vtx[2]; + const PxVec3 vertexNrm = vertexBary.x * nrm[0] + vertexBary.y * nrm[1] + vertexBary.z * nrm[2]; // meshThickness is already in + const PxVec3 graphicsPos = vertexPos + vertexNrm * vertexHeight; + + if (invalidBary) + { + uint32_t invalidColor = 0; + invalidColor = vertexBary == PxVec3(PX_MAX_F32) ? colorRed : invalidColor; + invalidColor = mapping.normalBary == PxVec3(PX_MAX_F32) ? colorPurple : invalidColor; + invalidColor = mapping.tangentBary == PxVec3(PX_MAX_F32) ? colorBlue : invalidColor; + + if (invalidColor != 0) + { + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(invalidColor); + RENDER_DEBUG_IFACE(&renderDebug)->debugTri(vtx[0], vtx[1], vtx[2]); + RENDER_DEBUG_IFACE(&renderDebug)->debugPoint(graphicsPos, meshThickness * 0.1f); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(centroid, graphicsPos); + } + continue; + } + + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(centroid, graphicsPos); + + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorYellow); + RENDER_DEBUG_IFACE(&renderDebug)->debugPoint(graphicsPos, meshThickness * 0.1f); + + if (badVtx && onlyBad) + { + // draw the projected position as well + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorRed); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(vertexPos, graphicsPos); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(vertexPos, centroid); + } + + PxVec3 normalBary = mapping.normalBary; + float normalHeight = normalBary.z; + normalBary.z = 1.0f - normalBary.x - normalBary.y; + const PxVec3 normalPos = normalBary.x * vtx[0] + normalBary.y * vtx[1] + normalBary.z * vtx[2]; + const PxVec3 normalNrm = normalBary.x * nrm[0] + normalBary.y * nrm[1] + normalBary.z * nrm[2]; // meshThickness is already in + const PxVec3 graphicsNrm = normalPos + normalNrm * normalHeight; + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorBlue); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(graphicsNrm, graphicsPos); +#if 0 + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Black)); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(graphicsNrm, centroid); +#endif + + if (badNrm && onlyBad) + { + // draw the projected normal as well + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorDarkRed); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(normalPos, graphicsNrm); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(normalPos, centroid); + } + + // turn the rendering on for the rest + badVtx = badNrm = true; + + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorWhite); + RENDER_DEBUG_IFACE(&renderDebug)->debugTri(vtx[0], vtx[1], vtx[2]); + if ((badVtx && heightVtx > 0.0f) || (badNrm && heightNrm > 0.0f)) + { + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorGreen); + RENDER_DEBUG_IFACE(&renderDebug)->debugTri(vtx[0] + nrm[0], vtx[1] + nrm[1], vtx[2] + nrm[2]); + for (uint32_t u = 0; u < 3; u++) + { + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(vtx[u], vtx[u] + nrm[u]); + } + } + else if ((badVtx && heightVtx < 0.0f) || (badNrm && heightNrm < 0.0f)) + { + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorPurple); + RENDER_DEBUG_IFACE(&renderDebug)->debugTri(vtx[0] - nrm[0], vtx[1] - nrm[1], vtx[2] - nrm[2]); + for (uint32_t u = 0; u < 3; u++) + { + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(vtx[u], vtx[u] - nrm[u]); + } + } + } + } + } + + RENDER_DEBUG_IFACE(&renderDebug)->popRenderState(); +#endif +} + + + +void ClothingAssetImpl::visualizeBones(RenderDebugInterface& renderDebug, const PxMat44* matrices, bool skeleton, float boneFramesScale, float boneNamesScale) +{ +#ifdef WITHOUT_DEBUG_VISUALIZE + PX_UNUSED(renderDebug); + PX_UNUSED(matrices); + PX_UNUSED(skeleton); + PX_UNUSED(boneFramesScale); + PX_UNUSED(boneNamesScale); +#else + + using RENDER_DEBUG::DebugColors; + using RENDER_DEBUG::DebugRenderState; + + RENDER_DEBUG_IFACE(&renderDebug)->pushRenderState(); + + const uint32_t activeBoneColor = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Purple); + const uint32_t passiveBoneColor = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Blue); + + const uint32_t colorWhite = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::White); + const uint32_t colorRed = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Red); + const uint32_t colorGreen = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Green); + const uint32_t colorBlue = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Blue); + + RENDER_DEBUG_IFACE(&renderDebug)->addToCurrentState(RENDER_DEBUG::DebugRenderState::CenterText); + RENDER_DEBUG_IFACE(&renderDebug)->addToCurrentState(RENDER_DEBUG::DebugRenderState::CameraFacing); + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentTextScale(boneNamesScale); + + if ((skeleton || boneFramesScale > 0.0f || boneNamesScale > 0.0f) && mPhysicalMeshes.size() > 0) + { + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type& physicalMesh = mPhysicalMeshes[0]->physicalMesh; + float sphereSize = 0.3f * physicalMesh.averageEdgeLength; + uint32_t rootIdx = mParams->rootBoneIndex; + PxMat44 absPose = matrices[rootIdx] * mBones[rootIdx].bindPose; + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::DarkRed)); + RENDER_DEBUG_IFACE(&renderDebug)->debugSphere(absPose.getPosition(), sphereSize); + } + + for (uint32_t i = 0; i < getNumUsedBones(); i++) + { + const int32_t parent = mBones[i].parentIndex; + + PxMat44 absPose = matrices[i] * mBones[i].bindPose; + + if (skeleton && parent >= 0 && parent < (int32_t)getNumUsedBones()) + { + PX_ASSERT((uint32_t)parent != i); + PxMat44 absPoseParent = matrices[(uint32_t)parent] * mBones[(uint32_t)parent].bindPose; + if ((mBones[(uint32_t)parent].numMeshReferenced + mBones[(uint32_t)parent].numRigidBodiesReferenced) == 0) + { + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(passiveBoneColor); + } + else + { + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(activeBoneColor); + } + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(absPose.getPosition(), absPoseParent.getPosition()); + } + + if (boneFramesScale > 0.0f) + { + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorRed); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(absPose.getPosition(), absPose.getPosition() + absPose.column0.getXYZ() * boneFramesScale); + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorGreen); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(absPose.getPosition(), absPose.getPosition() + absPose.column1.getXYZ() * boneFramesScale); + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorBlue); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(absPose.getPosition(), absPose.getPosition() + absPose.column2.getXYZ() * boneFramesScale); + } + + if (boneNamesScale > 0.0f) + { + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorWhite); + RENDER_DEBUG_IFACE(&renderDebug)->debugText(absPose.getPosition(), mBones[i].name.buf); + } + } + + RENDER_DEBUG_IFACE(&renderDebug)->popRenderState(); +#endif +} + + + +uint32_t ClothingAssetImpl::initializeAssetData(ClothingAssetData& assetData, const uint32_t uvChannel) +{ + //OK. Stage 1 - need to calculate the sizes required... + + const uint32_t numLods = mGraphicalLods.size(); + uint32_t numSubMeshes = 0; + + for (uint32_t a = 0; a < numLods; ++a) + { + ClothingGraphicalMeshAssetWrapper meshAsset(reinterpret_cast<RenderMeshAssetIntl*>(mGraphicalLods[a]->renderMeshAssetPointer)); + numSubMeshes += meshAsset.getSubmeshCount(); + } + + const uint32_t numPhysicalMeshes = mPhysicalMeshes.size(); + + const uint32_t requiredSize = ((sizeof(ClothingMeshAssetData) * numLods + sizeof(ClothingAssetSubMesh) * numSubMeshes + + sizeof(ClothingPhysicalMeshData) * numPhysicalMeshes) + 15) & 0xfffffff0; + + void* data = PX_ALLOC(requiredSize, PX_DEBUG_EXP("ClothingAssetData")); + memset(data, 0, requiredSize); + + assetData.mData = (uint8_t*)data; + assetData.mAssetSize = requiredSize; + + //assetData.m_pLods = (ClothingMeshAssetData*)data; + + assetData.mGraphicalLodsCount = numLods; + + assetData.mRootBoneIndex = mParams->rootBoneIndex; + + ClothingAssetSubMesh* pMeshes = (ClothingAssetSubMesh*)(((uint8_t*)data) + sizeof(ClothingMeshAssetData) * numLods); + + uint32_t maxVertices = 0; + + for (uint32_t a = 0; a < numLods; ++a) + { + ClothingGraphicalMeshAssetWrapper meshAsset(reinterpret_cast<RenderMeshAssetIntl*>(mGraphicalLods[a]->renderMeshAssetPointer)); + + const ClothingGraphicalLodParameters* graphicalLod = mGraphicalLods[a]; + + const uint32_t subMeshCount = meshAsset.getSubmeshCount(); + ClothingMeshAssetData* pAsset = assetData.GetLod(a); + PX_PLACEMENT_NEW(pAsset, ClothingMeshAssetData()); + //pAsset->m_pMeshes = pMeshes; + pAsset->mSubMeshCount = subMeshCount; + + //Params to set + pAsset->mImmediateClothMap = graphicalLod->immediateClothMap.buf; + pAsset->mImmediateClothMapCount = (uint32_t)graphicalLod->immediateClothMap.arraySizes[0]; + + + pAsset->mSkinClothMap = graphicalLod->skinClothMap.buf; + pAsset->mSkinClothMapCount = (uint32_t)graphicalLod->skinClothMap.arraySizes[0]; + + pAsset->mSkinClothMapB = graphicalLod->skinClothMapB.buf; + pAsset->mSkinClothMapBCount = (uint32_t)graphicalLod->skinClothMapB.arraySizes[0]; + + pAsset->mTetraMap = graphicalLod->tetraMap.buf; + pAsset->mTetraMapCount = (uint32_t)graphicalLod->tetraMap.arraySizes[0]; + + pAsset->mPhysicalMeshId = graphicalLod->physicalMeshId; + + pAsset->mSkinClothMapThickness = graphicalLod->skinClothMapThickness; + pAsset->mSkinClothMapOffset = graphicalLod->skinClothMapOffset; + + pAsset->mBounds = mParams->boundingBox; + + //For now - we'll set this outside again + pAsset->bActive = true; + + pAsset->mSubmeshOffset = (uint32_t)((uint8_t*)pMeshes - assetData.mData); + + for (uint32_t b = 0; b < subMeshCount; ++b) + { + PX_PLACEMENT_NEW(&pMeshes[b], ClothingAssetSubMesh()); + + RenderDataFormat::Enum outFormat; + pMeshes[b].mPositions = (const PxVec3*)meshAsset.getVertexBuffer(b, RenderVertexSemantic::POSITION, outFormat); + pMeshes[b].mPositionOutFormat = outFormat; + pMeshes[b].mNormals = (const PxVec3*)meshAsset.getVertexBuffer(b, RenderVertexSemantic::NORMAL, outFormat); + pMeshes[b].mNormalOutFormat = outFormat; + + pMeshes[b].mTangents = (const PxVec4*)meshAsset.getVertexBuffer(b, RenderVertexSemantic::TANGENT, outFormat); + pMeshes[b].mTangentOutFormat = outFormat; + PX_ASSERT(((size_t)pMeshes[b].mTangents & 0xf) == 0); + + pMeshes[b].mBoneWeights = (const float*)meshAsset.getVertexBuffer(b, RenderVertexSemantic::BONE_WEIGHT, outFormat); + pMeshes[b].mBoneWeightOutFormat = outFormat; + pMeshes[b].mBoneIndices = (const uint16_t*)meshAsset.getVertexBuffer(b, RenderVertexSemantic::BONE_INDEX, outFormat); + pMeshes[b].mVertexCount = meshAsset.getNumVertices(b); + pMeshes[b].mNumBonesPerVertex = meshAsset.getNumBonesPerVertex(b); + + maxVertices = PxMax(maxVertices, meshAsset.getNumVertices(b)); + + pMeshes[b].mIndices = (const uint32_t*)meshAsset.getIndexBuffer(b); + pMeshes[b].mIndicesCount = meshAsset.getNumIndices(b); + + const VertexUV* PX_RESTRICT uvs = NULL; + RenderDataFormat::Enum uvFormat = RenderDataFormat::UNSPECIFIED; + switch (uvChannel) + { + case 0: + uvs = (const VertexUV*)meshAsset.getVertexBuffer(b, RenderVertexSemantic::TEXCOORD0, uvFormat); + break; + case 1: + uvs = (const VertexUV*)meshAsset.getVertexBuffer(b, RenderVertexSemantic::TEXCOORD1, uvFormat); + break; + case 2: + uvs = (const VertexUV*)meshAsset.getVertexBuffer(b, RenderVertexSemantic::TEXCOORD2, uvFormat); + break; + case 3: + uvs = (const VertexUV*)meshAsset.getVertexBuffer(b, RenderVertexSemantic::TEXCOORD3, uvFormat); + break; + } + + pMeshes[b].mUvs = (VertexUVLocal*)uvs; + pMeshes[b].mUvFormat = uvFormat; + } + pMeshes += subMeshCount; + } + + + ClothingPhysicalMeshData* pPhysicalMeshes = (ClothingPhysicalMeshData*)pMeshes; + + assetData.mPhysicalMeshOffset = (uint32_t)((uint8_t*)pMeshes - assetData.mData); + + for (uint32_t a = 0; a < numPhysicalMeshes; ++a) + { + ClothingPhysicalMeshData* pPhysicalMesh = &pPhysicalMeshes[a]; + PX_PLACEMENT_NEW(pPhysicalMesh, ClothingPhysicalMeshData); + + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* physicalMesh = getPhysicalMeshFromLod(a); + ClothingPhysicalMeshParameters* pPhysicalMeshParams = mPhysicalMeshes[a]; + PX_UNUSED(pPhysicalMeshParams); + + pPhysicalMesh->mVertices = physicalMesh->vertices.buf; + pPhysicalMesh->mVertexCount = physicalMesh->numVertices; + pPhysicalMesh->mSimulatedVertexCount = physicalMesh->numSimulatedVertices; + pPhysicalMesh->mMaxDistance0VerticesCount = physicalMesh->numMaxDistance0Vertices; + + pPhysicalMesh->mNormals = physicalMesh->normals.buf; + pPhysicalMesh->mSkinningNormals = physicalMesh->skinningNormals.buf; + pPhysicalMesh->mSkinningNormalsCount = (uint32_t)physicalMesh->skinningNormals.arraySizes[0]; + pPhysicalMesh->mNumBonesPerVertex = physicalMesh->numBonesPerVertex; + + pPhysicalMesh->mBoneIndices = physicalMesh->boneIndices.buf; + pPhysicalMesh->mBoneWeights = physicalMesh->boneWeights.buf; + PX_ASSERT(physicalMesh->boneIndices.arraySizes[0] == physicalMesh->boneWeights.arraySizes[0]); + pPhysicalMesh->mBoneWeightsCount = (uint32_t)physicalMesh->boneWeights.arraySizes[0]; + + pPhysicalMesh->mOptimizationData = physicalMesh->optimizationData.buf; + pPhysicalMesh->mOptimizationDataCount = (uint32_t)physicalMesh->optimizationData.arraySizes[0]; + + pPhysicalMesh->mIndices = physicalMesh->indices.buf; + pPhysicalMesh->mIndicesCount = (uint32_t)physicalMesh->indices.arraySizes[0]; + pPhysicalMesh->mSimulatedIndicesCount = physicalMesh->numSimulatedIndices; + } + + //Initialized compressed num bones per vertex... + mCompressedNumBonesPerVertexMutex.lock(); + if (mCompressedNumBonesPerVertex.empty()) + { + initializeCompressedNumBonesPerVertex(); + } + mCompressedNumBonesPerVertexMutex.unlock(); + + assetData.mCompressedNumBonesPerVertexCount = mCompressedNumBonesPerVertex.size(); + assetData.mCompressedNumBonesPerVertex = assetData.mCompressedNumBonesPerVertexCount > 0 ? &(mCompressedNumBonesPerVertex.front()) : NULL; + + assetData.mCompressedTangentWCount = mCompressedTangentW.size(); + assetData.mCompressedTangentW = assetData.mCompressedTangentWCount > 0 ? &(mCompressedTangentW.front()) : NULL; + + assetData.mPhysicalMeshesCount = mPhysicalMeshes.size(); + assetData.mExt2IntMorphMappingCount = mExt2IntMorphMapping.size(); + if (mExt2IntMorphMapping.size()) + { + assetData.mExt2IntMorphMapping = &(mExt2IntMorphMapping.front()); + } + else + { + assetData.mExt2IntMorphMapping = NULL; + } + + assetData.mBoneCount = mBones.size(); + + return maxVertices; + +} + + + +const RenderMeshAsset* ClothingAssetImpl::getRenderMeshAsset(uint32_t lodLevel) const +{ + READ_ZONE(); + + if (lodLevel < mGraphicalLods.size()) + { + return reinterpret_cast<RenderMeshAssetIntl*>(mGraphicalLods[lodLevel]->renderMeshAssetPointer); + } + + return NULL; +} + + + +uint32_t ClothingAssetImpl::getMeshSkinningMapSize(uint32_t lod) +{ + WRITE_ZONE(); + if (lod >= mGraphicalLods.size()) + { + APEX_INVALID_PARAMETER("lod %i not a valid lod level. There are only %i graphical lods.", lod, mGraphicalLods.size()); + return 0; + } + ClothingGraphicalLodParameters* graphicalLod = mGraphicalLods[lod]; + + // make sure everything can be skinned using the skinClothMap, + // so the user doesn't have to care about immediate skinning as well + mergeMapping(graphicalLod); + + return (uint32_t)mGraphicalLods[lod]->skinClothMap.arraySizes[0]; +} + + + +void ClothingAssetImpl::getMeshSkinningMap(uint32_t lod, ClothingMeshSkinningMap* map) +{ + WRITE_ZONE(); + + if (lod >= mGraphicalLods.size()) + { + APEX_INVALID_PARAMETER("lod %i not a valid lod level. There are only %i graphical lods.", lod, mGraphicalLods.size()); + return; + } + ClothingGraphicalLodParameters* graphicalLod = mGraphicalLods[lod]; + + // make sure everything can be skinned using the skinClothMap, + // so the user doesn't have to care about immediate skinning as well + mergeMapping(graphicalLod); + + PX_ASSERT(graphicalLod->skinClothMapOffset > 0.0f); // skinClothMapOffset would only be needed if it's negative, but it's not expected to be + + // copy the values + int32_t size = mGraphicalLods[lod]->skinClothMap.arraySizes[0]; + for (int32_t i = 0; i < size; ++i) + { + const SkinClothMap& skinMap = mGraphicalLods[lod]->skinClothMap.buf[i]; + map[i].positionBary = skinMap.vertexBary; + map[i].vertexIndex0 = skinMap.vertexIndex0; + map[i].normalBary = skinMap.normalBary; + map[i].vertexIndex1 = skinMap.vertexIndex1; + map[i].tangentBary = skinMap.tangentBary; + map[i].vertexIndex2 = skinMap.vertexIndex2; + } +} + + + +bool ClothingAssetImpl::releaseGraphicalData() +{ + WRITE_ZONE(); + bool ok = true; + for (uint32_t i = 0; i < mGraphicalLods.size(); ++i) + { + if (mGraphicalLods[i]->skinClothMapB.arraySizes[0] > 0 || mGraphicalLods[i]->tetraMap.arraySizes[0] > 0) + { + APEX_DEBUG_WARNING("Asset contains data that is not supported for external skinning, graphical data cannot be released. Reexport the asset with a newer APEX version."); + ok = false; + } + + if (mGraphicalLods[i]->immediateClothMap.arraySizes[0] > 0) + { + APEX_DEBUG_WARNING("Asset contains immediate map data, graphical data cannot be released. Call getMeshSkinningMap first."); + ok = false; + } + } + + if (mActors.getSize() > 0) + { + APEX_DEBUG_WARNING("Graphical data in asset cannot be released while there are actors of this asset."); + ok = false; + } + + if (ok) + { + mModule->notifyReleaseGraphicalData(this); + + for (uint32_t i = 0; i < mGraphicalLods.size(); ++i) + { + if (mGraphicalLods[i]->renderMeshAssetPointer != NULL) + { + RenderMeshAssetIntl* rma = reinterpret_cast<RenderMeshAssetIntl*>(mGraphicalLods[i]->renderMeshAssetPointer); + rma->release(); + mGraphicalLods[i]->renderMeshAssetPointer = NULL; + + mGraphicalLods[i]->renderMeshAsset->destroy(); + mGraphicalLods[i]->renderMeshAsset = NULL; + } + + ParamArray<SkinClothMap> skinClothMap(mGraphicalLods[i], "skinClothMap", + reinterpret_cast<ParamDynamicArrayStruct*>(&mGraphicalLods[i]->skinClothMap)); + skinClothMap.clear(); + } + } + + return ok; +} + + + +void ClothingAssetImpl::setupInvBindMatrices() +{ + if (mInvBindPoses.size() == mBones.size()) + { + return; + } + + mInvBindPoses.resize(mBones.size()); + for (uint32_t i = 0; i < mBones.size(); i++) + { + mInvBindPoses[i] = mBones[i].bindPose.inverseRT(); + } +} + + + +void ClothingAssetImpl::prepareCookingJob(CookingAbstract& job, float scale, PxVec3* gravityDirection, PxVec3* morphedPhysicalMesh) +{ + PxVec3* tempVerticesForScale = NULL; + uint32_t tempVerticesOffset = 0; + if (scale != 1.0f || morphedPhysicalMesh != NULL) + { + uint32_t numMaxVertices = 0; + for (uint32_t physicalMeshId = 0; physicalMeshId < mPhysicalMeshes.size(); physicalMeshId++) + { + numMaxVertices += mPhysicalMeshes[physicalMeshId]->physicalMesh.numVertices; + } + numMaxVertices += mBoneVertices.size(); + + tempVerticesForScale = (PxVec3*)PX_ALLOC(sizeof(PxVec3) * numMaxVertices, PX_DEBUG_EXP("tempVerticesForScale")); + PX_ASSERT(tempVerticesForScale != NULL); + + for (uint32_t physicalMeshId = 0; physicalMeshId < mPhysicalMeshes.size(); physicalMeshId++) + { + const uint32_t numVertices = mPhysicalMeshes[physicalMeshId]->physicalMesh.numVertices; + PxVec3* origVertices = morphedPhysicalMesh != NULL ? morphedPhysicalMesh + tempVerticesOffset : mPhysicalMeshes[physicalMeshId]->physicalMesh.vertices.buf; + PxVec3* tempVertices = tempVerticesForScale + tempVerticesOffset; + for (uint32_t i = 0; i < numVertices; i++) + { + tempVertices[i] = origVertices[i] * scale; + } + tempVerticesOffset += numVertices; + } + } + + tempVerticesOffset = 0; + + for (uint32_t physicalMeshId = 0; physicalMeshId < mPhysicalMeshes.size(); physicalMeshId++) + { + CookingAbstract::PhysicalMesh physicalMesh; + physicalMesh.meshID = physicalMeshId; + if (tempVerticesForScale != NULL) + { + physicalMesh.vertices = tempVerticesForScale + tempVerticesOffset; + } + else + { + physicalMesh.vertices = mPhysicalMeshes[physicalMeshId]->physicalMesh.vertices.buf; + } + physicalMesh.numVertices = mPhysicalMeshes[physicalMeshId]->physicalMesh.numVertices; + physicalMesh.numSimulatedVertices = mPhysicalMeshes[physicalMeshId]->physicalMesh.numSimulatedVertices; + physicalMesh.numMaxDistance0Vertices = mPhysicalMeshes[physicalMeshId]->physicalMesh.numMaxDistance0Vertices; + physicalMesh.indices = mPhysicalMeshes[physicalMeshId]->physicalMesh.indices.buf; + physicalMesh.numIndices = mPhysicalMeshes[physicalMeshId]->physicalMesh.numIndices; + physicalMesh.numSimulatedIndices = mPhysicalMeshes[physicalMeshId]->physicalMesh.numSimulatedIndices; + physicalMesh.isTetrahedral = mPhysicalMeshes[physicalMeshId]->physicalMesh.isTetrahedralMesh; + + job.addPhysicalMesh(physicalMesh); + + tempVerticesOffset += physicalMesh.numVertices; + } + + if (tempVerticesForScale == NULL) + { + // verify that there are no invalid matrices + for (uint32_t i = 0; i < mBoneActors.size(); i++) + { + if (mBoneActors[i].capsuleRadius > 0.0f || mBoneActors[i].convexVerticesCount == 0) + { + continue; + } + + uint32_t boneIndex = (uint32_t)mBoneActors[i].boneIndex; + + const PxMat44& bpm44 = mBones[boneIndex].bindPose; + const PxMat44& lpm44 = mBoneActors[i].localPose; + const PxMat33 bpm33(bpm44.column0.getXYZ(), + bpm44.column1.getXYZ(), + bpm44.column2.getXYZ()), + lpm33(lpm44.column0.getXYZ(), + lpm44.column1.getXYZ(), + lpm44.column2.getXYZ()); + + const float det = bpm33.getDeterminant() * lpm33.getDeterminant(); + if (det < 0.0f) + { + // invalid matrices found, need to use temporary buffer only for bone vertices now + tempVerticesForScale = (PxVec3*)GetInternalApexSDK()->getTempMemory(sizeof(PxVec3) * mBoneVertices.size()); + PX_ASSERT(tempVerticesOffset == 0); + break; + } + } + } + + if (tempVerticesForScale != NULL) + { + memset(tempVerticesForScale + tempVerticesOffset, 0xff, sizeof(PxVec3) * mBoneVertices.size()); + + PxVec3* boneVertices = morphedPhysicalMesh != NULL ? morphedPhysicalMesh + tempVerticesOffset : mBoneVertices.begin(); + + for (uint32_t i = 0; i < mBoneActors.size(); i++) + { + uint32_t boneIndex = (uint32_t)mBoneActors[i].boneIndex; + const PxMat44 bpm44(mBones[boneIndex].bindPose), + lpm44(mBoneActors[i].localPose); + const PxMat33 bpm33(bpm44.column0.getXYZ(), + bpm44.column1.getXYZ(), + bpm44.column2.getXYZ()), + lpm33(lpm44.column0.getXYZ(), + lpm44.column1.getXYZ(), + lpm44.column2.getXYZ()); + + const float det = bpm33.getDeterminant() * lpm33.getDeterminant(); + for (uint32_t j = 0; j < mBoneActors[i].convexVerticesCount; j++) + { + uint32_t boneVertexIndex = mBoneActors[i].convexVerticesStart + j; + PxVec3 val = boneVertices[boneVertexIndex] * scale; + if (det < 0.0f) + { + val.z = -val.z; + } + + tempVerticesForScale[boneVertexIndex + tempVerticesOffset] = val; + } + } + + for (uint32_t i = 0; i < mBoneVertices.size(); i++) + { + PX_ASSERT(tempVerticesForScale[i + tempVerticesOffset].isFinite()); + } + } + + PxVec3* boneVertices = tempVerticesForScale != NULL ? tempVerticesForScale + tempVerticesOffset : mBoneVertices.begin(); + job.setConvexBones(mBoneActors.begin(), mBoneActors.size(), mBones.begin(), mBones.size(), boneVertices, 256); + + job.setScale(scale); + job.setVirtualParticleDensity(mParams->simulation.virtualParticleDensity); + + if (mParams->materialLibrary != NULL) + { + ClothingMaterialLibraryParameters* matLib = static_cast<ClothingMaterialLibraryParameters*>(mParams->materialLibrary); + float selfcollisionThickness = matLib->materials.buf[mParams->materialIndex].selfcollisionThickness; + job.setSelfcollisionRadius(selfcollisionThickness); + } + + PxVec3 gravityDir = mParams->simulation.gravityDirection; + if (gravityDir.isZero() && gravityDirection != NULL) + { + gravityDir = *gravityDirection; + } + if (gravityDir.isZero()) + { + APEX_DEBUG_WARNING("(%s) Gravity direction is zero. Impossible to extract vertical- and zero-stretch fibers.", mName.c_str()); + } + job.setGravityDirection(gravityDir); + + job.freeTempMemoryWhenDone(tempVerticesForScale); +} + + + +uint32_t* ClothingAssetImpl::getMorphMapping(uint32_t graphicalLod, uint32_t submeshIndex) +{ + if (mExt2IntMorphMapping.empty()) + { + if (!mMorphMappingWarning) + { + APEX_INVALID_OPERATION("A clothing actor with morph displacements was specified, but the Asset <%s> was not prepared for morph displacements", mName.c_str()); + mMorphMappingWarning = true; + } + return NULL; + } + + if (graphicalLod == (uint32_t) - 1 || graphicalLod > mGraphicalLods.size()) + { + graphicalLod = mGraphicalLods.size(); + } + + uint32_t offset = 0; + for (uint32_t i = 0; i < graphicalLod; i++) + { + RenderMeshAssetIntl* rma = getGraphicalMesh(i); + if (rma == NULL) + continue; + + for (uint32_t s = 0; s < rma->getSubmeshCount(); s++) + { + offset += rma->getSubmesh(s).getVertexCount(0); + } + } + + + RenderMeshAssetIntl* rma = getGraphicalMesh(graphicalLod); + if (rma != NULL) + { + PX_ASSERT(submeshIndex < rma->getSubmeshCount()); + submeshIndex = PxMin(submeshIndex, rma->getSubmeshCount()); + + for (uint32_t i = 0; i < submeshIndex; ++i) + { + offset += rma->getSubmesh(i).getVertexCount(0); + } + } + + return mExt2IntMorphMapping.begin() + offset; +} + + + +uint32_t ClothingAssetImpl::getPhysicalMeshOffset(uint32_t physicalMeshId) +{ + physicalMeshId = PxMin(physicalMeshId, mPhysicalMeshes.size()); + + uint32_t result = 0; + + for (uint32_t i = 0; i < physicalMeshId; i++) + { + result += mPhysicalMeshes[i]->physicalMesh.numVertices; + } + + return result; +} + + + +class SkinClothMapFacePredicate +{ +public: + bool operator()(const SkinClothMapB& map1, const SkinClothMapB& map2) const + { + if (map1.submeshIndex < map2.submeshIndex) + { + return true; + } + else if (map1.submeshIndex > map2.submeshIndex) + { + return false; + } + + return map1.faceIndex0 < map2.faceIndex0; + } +}; + + + +class SkinClothMapBVertexPredicate +{ +public: + bool operator()(const SkinClothMapB& map1, const SkinClothMapB& map2) const + { + return map1.vertexIndexPlusOffset < map2.vertexIndexPlusOffset; + } +}; + + + +void ClothingAssetImpl::getDisplacedPhysicalMeshPositions(PxVec3* morphDisplacements, ParamArray<PxVec3> displacedMeshPositions) +{ + uint32_t numPhysicalVertices = getPhysicalMeshOffset((uint32_t) - 1); + numPhysicalVertices += mBoneVertices.size(); + + if (numPhysicalVertices == 0) + { + displacedMeshPositions.clear(); + return; + } + + displacedMeshPositions.resize(numPhysicalVertices); + memset(displacedMeshPositions.begin(), 0, sizeof(PxVec3) * numPhysicalVertices); + float* resultWeights = (float*)PX_ALLOC(sizeof(float) * numPhysicalVertices, PX_DEBUG_EXP("ClothingAssetImpl::getDisplacedPhysicalMeshPositions")); + memset(resultWeights, 0, sizeof(float) * numPhysicalVertices); + + uint32_t resultOffset = 0; + for (uint32_t pm = 0; pm < mPhysicalMeshes.size(); pm++) + { + uint32_t gm = 0; + for (; gm < mGraphicalLods.size(); gm++) + { + if (mGraphicalLods[gm]->physicalMeshId == pm) + { + break; + } + } + + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type& physicalMesh = mPhysicalMeshes[pm]->physicalMesh; + + if (gm < mGraphicalLods.size()) + { + const ClothingGraphicalLodParameters* graphicalLod = mGraphicalLods[gm]; + + const RenderMeshAsset* rma = getRenderMeshAsset(gm); + uint32_t* morphMapping = getMorphMapping(gm, 0); + uint32_t morphOffset = 0; + + AbstractMeshDescription pMesh; + pMesh.pIndices = physicalMesh.indices.buf; + pMesh.numIndices = physicalMesh.numIndices; + pMesh.pPosition = physicalMesh.vertices.buf; + + if (graphicalLod->immediateClothMap.buf == NULL && graphicalLod->skinClothMapB.buf != NULL) + { + // PH: Need to resort the skinMapB buffer to vertex order, will resort back to face order just below + sort<SkinClothMapB, SkinClothMapBVertexPredicate>(graphicalLod->skinClothMapB.buf, (uint32_t)graphicalLod->skinClothMapB.arraySizes[0], SkinClothMapBVertexPredicate()); + } + + for (uint32_t submeshIndex = 0; submeshIndex < rma->getSubmeshCount(); submeshIndex++) + { + const RenderSubmesh& submesh = rma->getSubmesh(submeshIndex); + + const uint32_t vertexCount = submesh.getVertexCount(0); + for (uint32_t vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++) + { + const PxVec3 displacement = morphMapping != NULL ? morphDisplacements[morphMapping[morphOffset + vertexIndex]] : PxVec3(0.0f); + + uint32_t indices[4]; + float weight[4]; + + uint32_t numIndices = getCorrespondingPhysicalVertices(*graphicalLod, submeshIndex, vertexIndex, pMesh, morphOffset, indices, weight); + + for (uint32_t i = 0; i < numIndices; i++) + { + weight[i] += 0.001f; // none of the weights is 0! + PX_ASSERT(weight[i] > 0.0f); + PX_ASSERT(indices[i] < pMesh.numIndices); + displacedMeshPositions[resultOffset + indices[i]] += (displacement + pMesh.pPosition[indices[i]]) * weight[i]; + resultWeights[resultOffset + indices[i]] += weight[i]; + } + } + + morphOffset += vertexCount; + } + + if (graphicalLod->immediateClothMap.buf == NULL && graphicalLod->skinClothMapB.buf != NULL) + { + nvidia::sort<SkinClothMapB, SkinClothMapFacePredicate>(graphicalLod->skinClothMapB.buf, (uint32_t)graphicalLod->skinClothMapB.arraySizes[0], SkinClothMapFacePredicate()); + } + + for (uint32_t i = 0; i < physicalMesh.numVertices; i++) + { + const uint32_t resultIndex = i + resultOffset; + if (resultWeights[resultIndex] > 0.0f) + { + displacedMeshPositions[resultIndex] /= resultWeights[resultIndex]; + } + else + { + morphOffset = 0; + const PxVec3 physicsPosition = pMesh.pPosition[i]; + float shortestDistance = PX_MAX_F32; + for (uint32_t submeshIndex = 0; submeshIndex < rma->getSubmeshCount() && shortestDistance > 0.0f; submeshIndex++) + { + const RenderSubmesh& submesh = rma->getSubmesh(submeshIndex); + + const VertexFormat& format = submesh.getVertexBuffer().getFormat(); + uint32_t positionIndex = (uint32_t)format.getBufferIndexFromID(format.getSemanticID(RenderVertexSemantic::POSITION)); + PX_ASSERT(format.getBufferFormat(positionIndex) == RenderDataFormat::FLOAT3); + PxVec3* positions = (PxVec3*)submesh.getVertexBuffer().getBuffer(positionIndex); + + + const uint32_t vertexCount = submesh.getVertexCount(0); + for (uint32_t vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++) + { + float dist2 = (physicsPosition - positions[vertexIndex]).magnitudeSquared(); + if (dist2 < shortestDistance) + { + shortestDistance = dist2; + displacedMeshPositions[resultIndex] = physicsPosition + morphDisplacements[morphMapping[morphOffset + vertexIndex]]; + if (dist2 == 0.0f) + { + break; + } + } + } + morphOffset += submesh.getVertexCount(0); + } + } + } + } + + resultOffset += physicalMesh.numVertices; + } + + PX_FREE(resultWeights); + resultWeights = NULL; + + for (uint32_t ba = 0; ba < mBoneActors.size(); ba++) + { + + for (uint32_t bi = 0; bi < mBoneActors[ba].convexVerticesCount; bi++) + { + uint32_t boneIndex = bi + mBoneActors[ba].convexVerticesStart; + PX_ASSERT(mBoneActors[ba].localPose == PxMat44(PxIdentity)); + const PxMat44 bindPose = mBones[(uint32_t)mBoneActors[ba].boneIndex].bindPose; + const PxVec3 physicsPosition = bindPose.transform(mBoneVertices[boneIndex]); + PxVec3 resultPosition(0.0f); + + float shortestDistance = PX_MAX_F32; + for (uint32_t gm = 0; gm < mGraphicalLods.size() && shortestDistance > 0.0f; gm++) + { + const RenderMeshAsset* rma = getRenderMeshAsset(gm); + uint32_t* morphMapping = getMorphMapping(gm, 0); + uint32_t morphOffset = 0; + + for (uint32_t submeshIndex = 0; submeshIndex < rma->getSubmeshCount() && shortestDistance > 0.0f; submeshIndex++) + { + const RenderSubmesh& submesh = rma->getSubmesh(submeshIndex); + + const VertexFormat& format = submesh.getVertexBuffer().getFormat(); + uint32_t positionIndex = (uint32_t)format.getBufferIndexFromID(format.getSemanticID(RenderVertexSemantic::POSITION)); + PX_ASSERT(format.getBufferFormat(positionIndex) == RenderDataFormat::FLOAT3); + PxVec3* positions = (PxVec3*)submesh.getVertexBuffer().getBuffer(positionIndex); + + + const uint32_t vertexCount = submesh.getVertexCount(0); + for (uint32_t vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++) + { + float dist2 = (physicsPosition - positions[vertexIndex]).magnitudeSquared(); + if (dist2 < shortestDistance) + { + shortestDistance = dist2; + resultPosition = physicsPosition + morphDisplacements[morphMapping[morphOffset + vertexIndex]]; + if (dist2 == 0.0f) + { + break; + } + } + } + morphOffset += submesh.getVertexCount(0); + } + } + + PxMat33 invBindPose(bindPose.column0.getXYZ(), bindPose.column1.getXYZ(), bindPose.column2.getXYZ()); + invBindPose = invBindPose.getInverse(); + displacedMeshPositions[resultOffset + boneIndex] = invBindPose.transform(resultPosition) + invBindPose.transform(-bindPose.getPosition()); + } + } + resultOffset += mBoneVertices.size(); + + + PX_ASSERT(resultOffset == numPhysicalVertices); +} + + + +void ClothingAssetImpl::initializeCompressedNumBonesPerVertex() +{ + // PH: Merged the tangent w into this code, can be done at the same time + uint32_t numBonesElementCount = 0; + uint32_t numTangentElementCount = 0; + for (uint32_t lodIndex = 0; lodIndex < mGraphicalLods.size(); lodIndex++) + { + ClothingGraphicalMeshAssetWrapper meshAsset(reinterpret_cast<RenderMeshAssetIntl*>(mGraphicalLods[lodIndex]->renderMeshAssetPointer)); + + for (uint32_t submeshIdx = 0; submeshIdx < meshAsset.getSubmeshCount(); submeshIdx++) + { + const uint32_t numVertices = meshAsset.getNumVertices(submeshIdx); + + // 3 bits per entry, 10 entries per U32 + uint32_t numBoneEntries = (numVertices + 15) / 16; + + RenderDataFormat::Enum outFormat = RenderDataFormat::UNSPECIFIED; + const PxVec4* PX_RESTRICT tangents = (const PxVec4*)meshAsset.getVertexBuffer(submeshIdx, RenderVertexSemantic::TANGENT, outFormat); + PX_ASSERT(tangents == NULL || outFormat == RenderDataFormat::FLOAT4); + + uint32_t numTangentEntries = tangents != NULL ? (numVertices + 31) / 32 : 0; + + // Round up such that map for all submeshes is 16 byte aligned + while ((numBoneEntries & 0x3) != 0) // this is a numEntries % 4 + { + numBoneEntries++; + } + + while ((numTangentEntries & 0x3) != 0) + { + numTangentEntries++; + } + + numBonesElementCount += numBoneEntries; + numTangentElementCount += numTangentEntries; + } + } + + if (numBonesElementCount > 0) + { + mCompressedNumBonesPerVertex.resize(numBonesElementCount, 0); + + uint32_t numNonNormalizedVertices = 0; + uint32_t numInefficientVertices = 0; + + uint32_t* bonesPerVertex = mCompressedNumBonesPerVertex.begin(); + for (uint32_t lodIndex = 0; lodIndex < mGraphicalLods.size(); lodIndex++) + { + ClothingGraphicalMeshAssetWrapper meshAsset(reinterpret_cast<RenderMeshAssetIntl*>(mGraphicalLods[lodIndex]->renderMeshAssetPointer)); + + for (uint32_t submeshIdx = 0; submeshIdx < meshAsset.getSubmeshCount(); submeshIdx++) + { + uint32_t numVerticesWritten = 0; + + RenderDataFormat::Enum outFormat = RenderDataFormat::UNSPECIFIED; + const float* PX_RESTRICT boneWeights = (const float*)meshAsset.getVertexBuffer(submeshIdx, RenderVertexSemantic::BONE_WEIGHT, outFormat); + + const uint32_t numVertices = meshAsset.getNumVertices(submeshIdx); + const uint32_t numBonesPerVertex = meshAsset.getNumBonesPerVertex(submeshIdx); + PX_ASSERT((numBonesPerVertex > 0) == (boneWeights != NULL)); + + PX_ASSERT(((size_t)bonesPerVertex & 0xf) == 0); // make sure we start 16 byte aligned + for (uint32_t vertexIndex = 0; vertexIndex < numVertices; vertexIndex++) + { + uint32_t firstZeroBoneAfter = 0; + if (boneWeights != NULL) + { + const float* PX_RESTRICT vertexBoneWeights = boneWeights + (vertexIndex * numBonesPerVertex); + uint32_t firstZeroBone = numBonesPerVertex; + float sumWeights = 0.0f; + for (uint32_t k = 0; k < numBonesPerVertex; k++) + { + sumWeights += vertexBoneWeights[k]; + if (vertexBoneWeights[k] == 0.0f) + { + firstZeroBone = PxMin(firstZeroBone, k); + } + else + { + firstZeroBoneAfter = k + 1; + } + } + PX_ASSERT(firstZeroBoneAfter <= numBonesPerVertex); + + numNonNormalizedVertices += PxAbs(sumWeights - 1.0f) > 0.001f ? 1 : 0; + numInefficientVertices += firstZeroBone < firstZeroBoneAfter ? 1 : 0; + } + else + { + firstZeroBoneAfter = 1; + } + + // write the value + if (numVerticesWritten == 16) + { + bonesPerVertex++; + numVerticesWritten = 0; + } + + PX_ASSERT(firstZeroBoneAfter > 0); + PX_ASSERT(firstZeroBoneAfter < 5); // or else it doesn't fit + (*bonesPerVertex) |= ((firstZeroBoneAfter - 1) & 0x3) << (numVerticesWritten * 2); + numVerticesWritten++; + } + + // if *bonesPerVertex contains data, advance + if (numVerticesWritten > 0) + { + bonesPerVertex++; + } + + // advance until 16 byte aligned + while (((size_t)bonesPerVertex & 0xf) != 0) + { + bonesPerVertex++; + } + } + } + + if (numNonNormalizedVertices > 0) + { + APEX_DEBUG_WARNING("The Clothing Asset <%s> has %d vertices with non-normalized bone weights. This may lead to wrongly displayed meshes.", mName.c_str(), numNonNormalizedVertices); + } + + if (numInefficientVertices > 0) + { + APEX_DEBUG_WARNING("The Clothing Asset <%s> has %d vertices with non-sorted bone weights. This can decrease performance of the skinning. Resave the asset!", mName.c_str(), numInefficientVertices); + } + } + + if (numTangentElementCount > 0) + { + mCompressedTangentW.resize(numTangentElementCount, 0); + + uint32_t* tangentW = mCompressedTangentW.begin(); + for (uint32_t lodIndex = 0; lodIndex < mGraphicalLods.size(); lodIndex++) + { + ClothingGraphicalMeshAssetWrapper meshAsset(reinterpret_cast<RenderMeshAssetIntl*>(mGraphicalLods[lodIndex]->renderMeshAssetPointer)); + + for (uint32_t submeshIdx = 0; submeshIdx < meshAsset.getSubmeshCount(); submeshIdx++) + { + uint32_t numVerticesWritten = 0; + + RenderDataFormat::Enum outFormat = RenderDataFormat::UNSPECIFIED; + const PxVec4* PX_RESTRICT tangents = (const PxVec4*)meshAsset.getVertexBuffer(submeshIdx, RenderVertexSemantic::TANGENT, outFormat); + PX_ASSERT(outFormat == RenderDataFormat::FLOAT4); + + PX_ASSERT(((size_t)tangentW & 0xf) == 0); // make sure we start 16 byte aligned + + const uint32_t numVertices = meshAsset.getNumVertices(submeshIdx); + for (uint32_t vertexIndex = 0; vertexIndex < numVertices; vertexIndex++) + { + PX_ASSERT(PxAbs(tangents[vertexIndex].w) == 1.0f); + const uint32_t tangentWpositive = tangents[vertexIndex].w > 0.0f ? 1u : 0u; + + // write the value + if (numVerticesWritten == 32) + { + tangentW++; + numVerticesWritten = 0; + } + + (*tangentW) |= tangentWpositive << numVerticesWritten; + numVerticesWritten++; + } + + // if *bonesPerVertex contains data, advance + if (numVerticesWritten > 0) + { + tangentW++; + } + + // advance until 16 byte aligned + while (((size_t)tangentW & 0xf) != 0) + { + tangentW++; + } + } + } + } +} + + + +uint32_t ClothingAssetImpl::getRootBoneIndex() +{ + PX_ASSERT(mParams->rootBoneIndex < getNumUsedBones()); + return mParams->rootBoneIndex; +} + + + +uint32_t ClothingAssetImpl::getInterCollisionChannels() +{ + return mParams->interCollisionChannels; +} + + +void ClothingAssetImpl::releaseCookedInstances() +{ + if (mParams != NULL && mActors.getSize() == 0) + { + for (int32_t i = 0; i < mParams->cookedData.arraySizes[0]; i++) + { + NvParameterized::Interface* cookedData = mParams->cookedData.buf[i].cookedData; + if (cookedData != NULL) + { + BackendFactory* factory = mModule->getBackendFactory(cookedData->className()); + PX_ASSERT(factory != NULL); + if (factory != NULL) + { + factory->releaseCookedInstances(mParams->cookedData.buf[i].cookedData); + } + } + } + } +} + +void ClothingAssetImpl::destroy() +{ + mActors.clear(); + + while (numCookingDependencies() > 0) + { + nvidia::Thread::sleep(0); + } + + mModule->unregisterAssetWithScenes(this); + + mUnusedSimulationMutex.lock(); + for (uint32_t i = 0; i < mUnusedSimulation.size(); i++) + { + if (mUnusedSimulation[i] == NULL) + { + continue; + } + + destroySimulation(mUnusedSimulation[i]); + mUnusedSimulation[i] = NULL; + } + mUnusedSimulation.clear(); + mUnusedSimulationMutex.unlock(); + + releaseCookedInstances(); + + for (uint32_t i = 0; i < mPhysicalMeshes.size(); i++) + { + if (mParams != NULL) + { + // PH: we should only decrement if we don't use releaseAndReturnNvParameterizedInterface + mPhysicalMeshes[i]->referenceCount--; + } + } + + for (uint32_t i = 0; i < mGraphicalLods.size(); i++) + { + if (mGraphicalLods[i]->renderMeshAssetPointer == NULL) + continue; + + reinterpret_cast<RenderMeshAssetIntl*>(mGraphicalLods[i]->renderMeshAssetPointer)->release(); + mGraphicalLods[i]->renderMeshAssetPointer = NULL; + } + +#ifndef WITHOUT_PVD + destroyPvdInstances(); +#endif + if (mParams != NULL) + { + // safety! + for (uint32_t i = 0; i < mPhysicalMeshes.size(); i++) + { + PX_ASSERT(mPhysicalMeshes[i]->referenceCount == 0); + } + + mParams->setSerializationCallback(NULL, NULL); + mParams->destroy(); + mParams = NULL; + } + + mCompressedNumBonesPerVertex.reset(); + + delete this; +} + + + +int32_t ClothingAssetImpl::getBoneInternalIndex(const char* boneName) const +{ + if (boneName == NULL) + { + return -1; + } + + int32_t internalBoneIndex = -1; + for (uint32_t i = 0; i < mBones.size(); i++) + { + if (mBones[i].name != NULL && (::strcmp(mBones[i].name, boneName) == 0)) + { + internalBoneIndex = (int32_t)i; + break; + } + } + + return internalBoneIndex ; +} + + + +int32_t ClothingAssetImpl::getBoneInternalIndex(uint32_t boneIndex) const +{ + if (boneIndex >= mBones.size()) + { + return -1; + } + + int32_t internalBoneIndex = -1; + for (uint32_t i = 0; i < mBones.size(); i++) + { + if (mBones[i].externalIndex == (int32_t)boneIndex) + { + internalBoneIndex = (int32_t)i; + break; + } + } + + return internalBoneIndex; +} + + + +class SortGraphicalVerts +{ +public: + SortGraphicalVerts(uint32_t numVerts, uint32_t submeshOffset, const uint32_t* indices, uint32_t numIndices, ClothingGraphicalLodParameters* lodParameters, + ClothingPhysicalMeshParameters* physicsMesh, RenderMeshAssetIntl* renderMeshAsset = NULL) : mNumNotFoundVertices(0), mIndices(indices), mNumIndices(numIndices) + { + PX_UNUSED(physicsMesh); + mNew2Old.resize(numVerts, 0); + mOld2New.resize(numVerts, 0); + mVertexInfo.resize(numVerts); + for (uint32_t i = 0; i < numVerts; i++) + { + mNew2Old[i] = i; + mOld2New[i] = i; + mVertexInfo[i].idealPosition = (float)i; + } + + // scale the max distance such that it gives a guess for the 'idealPosition' + // const float maxDistanceScale = (float)numVerts / physicsMesh->physicalMesh.maximumMaxDistance; + // ClothingPhysicalMeshParametersNS::ConstrainCoefficient_Type* coeffs = physicsMesh->physicalMesh.constrainCoefficients.buf; + + if (lodParameters->immediateClothMap.arraySizes[0] > 0) + { + for (uint32_t i = 0; i < numVerts; i++) + { + const uint32_t immediateMap = lodParameters->immediateClothMap.buf[i + submeshOffset]; + if (immediateMap != ClothingConstants::ImmediateClothingInvalidValue) + { + if ((immediateMap & ClothingConstants::ImmediateClothingInSkinFlag) == 0) + { + const uint32_t targetIndex = immediateMap & ClothingConstants::ImmediateClothingReadMask; + mVertexInfo[i].physicsMeshNumber = getMeshIndex(targetIndex, physicsMesh); + PX_UNUSED(targetIndex); + } + } + } + } + + const uint32_t numSkins = (uint32_t)lodParameters->skinClothMap.arraySizes[0]; + for (uint32_t i = 0; i < numSkins; i++) + { + const uint32_t vertexIndex = lodParameters->skinClothMap.buf[i].vertexIndexPlusOffset - submeshOffset; + if (vertexIndex < numVerts) // this also handles underflow + { + uint32_t physVertexIndex = PxMax(lodParameters->skinClothMap.buf[i].vertexIndex0, lodParameters->skinClothMap.buf[i].vertexIndex1); + physVertexIndex = PxMax(physVertexIndex, lodParameters->skinClothMap.buf[i].vertexIndex2); + const PxI32 submeshNumber = getMeshIndexMaxFromVert(physVertexIndex, physicsMesh); + + mVertexInfo[vertexIndex].physicsMeshNumber = PxMax(submeshNumber, mVertexInfo[vertexIndex].physicsMeshNumber); + } + } + + const uint32_t numSkinB = (uint32_t)lodParameters->skinClothMapB.arraySizes[0]; + for (uint32_t i = 0; i < numSkinB; i++) + { + const uint32_t faceIndex0 = lodParameters->skinClothMapB.buf[i].faceIndex0; + PX_UNUSED(faceIndex0); + const uint32_t vertexIndex = lodParameters->skinClothMapB.buf[i].vertexIndexPlusOffset - submeshOffset; + if (vertexIndex < numVerts) + { + mVertexInfo[vertexIndex].physicsMeshNumber = PxMax(getMeshIndexMaxFromFace(faceIndex0, physicsMesh), mVertexInfo[vertexIndex].physicsMeshNumber); + } + } + + const uint32_t numTetras = (uint32_t)lodParameters->tetraMap.arraySizes[0]; + for (uint32_t i = 0; i < numTetras; i++) + { + const uint32_t tetraIndex0 = lodParameters->tetraMap.buf[i].tetraIndex0; + mVertexInfo[i].physicsMeshNumber = PxMax(getMeshIndexMaxFromFace(tetraIndex0, physicsMesh), mVertexInfo[i].physicsMeshNumber); + } + + if (renderMeshAsset != NULL) + { + uint32_t numSkinClothMaps = (uint32_t)lodParameters->skinClothMap.arraySizes[0]; + SkinClothMap* skinClothMaps = lodParameters->skinClothMap.buf; + + uint32_t count = 0; + uint32_t targetOffset = 0; + for (uint32_t s = 0; s < renderMeshAsset->getSubmeshCount(); s++) + { + const VertexBuffer& vb = renderMeshAsset->getSubmesh(s).getVertexBuffer(); + const VertexFormat& vf = vb.getFormat(); + + const uint32_t graphicalMaxDistanceIndex = (uint32_t)vf.getBufferIndexFromID(vf.getID("MAX_DISTANCE")); + RenderDataFormat::Enum outFormat = vf.getBufferFormat(graphicalMaxDistanceIndex); + const float* graphicalMaxDistance = outFormat == RenderDataFormat::UNSPECIFIED ? NULL : + reinterpret_cast<const float*>(vb.getBuffer(graphicalMaxDistanceIndex)); + PX_ASSERT(graphicalMaxDistance == NULL || outFormat == RenderDataFormat::FLOAT1); + + const uint32_t numVertices = renderMeshAsset->getSubmesh(s).getVertexCount(0); + for (uint32_t vertexIndex = 0; vertexIndex < numVertices; vertexIndex++) + { + const uint32_t index = vertexIndex + targetOffset; + if (graphicalMaxDistance != NULL && graphicalMaxDistance[vertexIndex] == 0.0f) + { + for (uint32_t i = 0; i < numSkinClothMaps; i++) + { + uint32_t grIndex = skinClothMaps[i].vertexIndexPlusOffset; + if (grIndex == index) + { + count++; + + const uint32_t vertexIndex = grIndex - submeshOffset; + if (vertexIndex < numVerts) // this also handles underflow + { + mVertexInfo[vertexIndex].physicsMeshNumber = -1; + } + } + } + } + } + + targetOffset += numVertices; + } + } + + for (uint32_t i = 0; i < numVerts; i++) + { + if (mVertexInfo[i].physicsMeshNumber == -1) + { + // give it the largest number, such that it gets sorted to the very end instead of the very beginning. Then at least it's cpu skinned. + mVertexInfo[i].physicsMeshNumber = PX_MAX_I32; + mNumNotFoundVertices++; + } + PX_ASSERT(mVertexInfo[i].idealPosition != -1); + } + + // we only know the submesh number for each individual vertex, but we need to make sure that this is consistent for each + // triangle. So we define the submesh number for a triangle as the min of all the submesh numbers of its vertices. + // Then we set the vertex submesh number to the min of all its triangle's submesh numbers. + + Array<int32_t> triangleMeshIndex(mNumIndices / 3, 0x7fffffff); + for (uint32_t i = 0; i < triangleMeshIndex.size(); i++) + { + PxU32 index = i * 3; + PxI32 meshNumber = PxMin(mVertexInfo[mIndices[index]].physicsMeshNumber, mVertexInfo[mIndices[index + 1]].physicsMeshNumber); + triangleMeshIndex[i] = PxMin(meshNumber, mVertexInfo[mIndices[index + 2]].physicsMeshNumber); + } + + // now let's redistribute it + for (uint32_t i = 0; i < triangleMeshIndex.size(); i++) + { + for (uint32_t j = 0; j < 3; j++) + { + uint32_t index = mIndices[i * 3 + j]; + if (triangleMeshIndex[i] < mVertexInfo[index].physicsMeshNumber) + { + mVertexInfo[index].physicsMeshNumber = triangleMeshIndex[i]; + // we need to distinguish the ones that naturally belong to a submesh, or the border ones that got added this late + mVertexInfo[index].pulledInThroughTriangle = true; + } + } + } + } + + PxI32 getMeshIndex(PxU32 vertexIndex, const ClothingPhysicalMeshParameters* physicsMesh) + { + if (physicsMesh->physicalMesh.numSimulatedVertices > vertexIndex) + { + return 0; + } + + return 1; + } + + PxI32 getMeshIndexMaxFromFace(PxU32 faceIndex0, const ClothingPhysicalMeshParameters* physicsMesh) + { + if (physicsMesh->physicalMesh.numSimulatedIndices > faceIndex0) + { + return 0; + } + + return 1; + } + + PxI32 getMeshIndexMaxFromVert(PxU32 vertexIndex, const ClothingPhysicalMeshParameters* physicsMesh) + { + + if (physicsMesh->physicalMesh.numSimulatedVertices > vertexIndex) + { + return 0; + } + + return 1; + } + + bool operator()(const uint32_t a, const uint32_t b) const + { + if (mVertexInfo[a].pulledInThroughTriangle != mVertexInfo[b].pulledInThroughTriangle) + { + return mVertexInfo[a].pulledInThroughTriangle < mVertexInfo[b].pulledInThroughTriangle; + } + + return mVertexInfo[a].idealPosition < mVertexInfo[b].idealPosition; + } + + void sortVertices() + { + nvidia::sort(mNew2Old.begin(), mNew2Old.size(), *this); + + for (uint32_t i = 0; i < mNew2Old.size(); i++) + { + mOld2New[mNew2Old[i]] = i; + } + } + + uint32_t computeCost() + { + uint32_t totalDist = 0; + // const uint32_t numVertsInCacheLine = 4096 / 12; + for (uint32_t i = 0; i < mNumIndices; i += 3) + { + // create 3 edges + const uint32_t edges[3] = + { + (uint32_t)PxAbs((int32_t)mOld2New[mIndices[i + 0]] - (int32_t)mOld2New[mIndices[i + 1]]), + (uint32_t)PxAbs((int32_t)mOld2New[mIndices[i + 1]] - (int32_t)mOld2New[mIndices[i + 2]]), + (uint32_t)PxAbs((int32_t)mOld2New[mIndices[i + 2]] - (int32_t)mOld2New[mIndices[i + 0]]), + }; + + for (uint32_t j = 0; j < 3; j++) + { + totalDist += edges[j]; + } + + } + + return totalDist; + } + + PxI32 getMesh(PxU32 newVertexIndex) + { + return mVertexInfo[mNew2Old[newVertexIndex]].physicsMeshNumber; + } + + bool isAdditional(uint32_t newVertexIndex) + { + return mVertexInfo[mNew2Old[newVertexIndex]].pulledInThroughTriangle; + } + + nvidia::Array<uint32_t> mOld2New; + nvidia::Array<uint32_t> mNew2Old; + + uint32_t mNumNotFoundVertices; + +private: + SortGraphicalVerts& operator=(const SortGraphicalVerts&); + + struct InternalInfo + { + InternalInfo() : physicsMeshNumber(-1), idealPosition(-1), idealChange(0.0f), idealCount(0.0f), pulledInThroughTriangle(false) {} + int32_t physicsMeshNumber; + float idealPosition; + float idealChange; + float idealCount; + bool pulledInThroughTriangle; + }; + + nvidia::Array<InternalInfo> mVertexInfo; + + const uint32_t* mIndices; + const uint32_t mNumIndices; +}; + + + +template <typename T> +class SkinClothMapPredicate +{ +public: + bool operator()(const T& map1, const T& map2) const + { + return map1.vertexIndexPlusOffset < map2.vertexIndexPlusOffset; + } +}; + + + +class SortGraphicalIndices +{ +public: + SortGraphicalIndices(uint32_t numIndices, uint32_t* indices) : mIndices(indices) + { + mTriangleInfo.resize(numIndices / 3); + for (uint32_t i = 0; i < mTriangleInfo.size(); i++) + { + mTriangleInfo[i].mesh = -1; + mTriangleInfo[i].originalPosition = i; + } + + mNew2Old.resize(numIndices / 3); + mOld2New.resize(numIndices / 3); + for (uint32_t i = 0; i < mNew2Old.size(); i++) + { + mNew2Old[i] = i; + mOld2New[i] = i; + } + } + + void setTriangleMesh(PxU32 triangle, PxI32 mesh) + { + mTriangleInfo[triangle].mesh = mesh; + } + + PxI32 getTriangleMesh(PxU32 newTriangleIndex) const + { + return mTriangleInfo[mNew2Old[newTriangleIndex]].mesh; + } + + bool operator()(const uint32_t t1, const uint32_t t2) const + { + if (mTriangleInfo[t1].mesh != mTriangleInfo[t2].mesh) + { + return mTriangleInfo[t1].mesh < mTriangleInfo[t2].mesh; + } + + return mTriangleInfo[t1].originalPosition < mTriangleInfo[t2].originalPosition; + } + + void sort() + { + nvidia::sort(mNew2Old.begin(), mNew2Old.size(), *this); + + for (uint32_t i = 0; i < mNew2Old.size(); i++) + { + mOld2New[mNew2Old[i]] = i; + } + + ApexPermute(mIndices, mNew2Old.begin(), mNew2Old.size(), 3); + } + +private: + nvidia::Array<uint32_t> mNew2Old; + nvidia::Array<uint32_t> mOld2New; + uint32_t* mIndices; + + struct TriangleInfo + { + int32_t mesh; + uint32_t originalPosition; + }; + nvidia::Array<TriangleInfo> mTriangleInfo; +}; + + + +bool ClothingAssetImpl::reorderGraphicsVertices(uint32_t graphicalLodId, bool perfWarning) +{ + PX_ASSERT(mGraphicalLods[graphicalLodId] != NULL); + + const uint32_t curSortingVersion = 2; // bump this number when the sorting changes! + + if (mGraphicalLods[graphicalLodId]->renderMeshAssetSorting >= curSortingVersion) + { + // nothing needs to be done. + return false; + } + + mGraphicalLods[graphicalLodId]->renderMeshAssetSorting = curSortingVersion; + + if (perfWarning) + { + APEX_DEBUG_INFO("Performance warning. This asset <%s> has to be re-saved to speed up loading", mName.c_str()); + } + + RenderMeshAssetIntl* rma = static_cast<RenderMeshAssetIntl*>(mGraphicalLods[graphicalLodId]->renderMeshAssetPointer); + PX_ASSERT(rma != NULL); + if (rma == NULL) + return false; + + const uint32_t numSubMeshes = rma->getSubmeshCount(); + uint32_t submeshVertexOffset = 0; + + const uint32_t numSkinClothMap = (uint32_t)mGraphicalLods[graphicalLodId]->skinClothMap.arraySizes[0]; + ClothingGraphicalLodParametersNS::SkinClothMapD_Type* skinClothMap = mGraphicalLods[graphicalLodId]->skinClothMap.buf; + + const uint32_t numSkinClothMapB = (uint32_t)mGraphicalLods[graphicalLodId]->skinClothMapB.arraySizes[0]; + ClothingGraphicalLodParametersNS::SkinClothMapB_Type* skinClothMapB = mGraphicalLods[graphicalLodId]->skinClothMapB.buf; + + // allocate enough space + { + if (mGraphicalLods[graphicalLodId]->physicsMeshPartitioning.arraySizes[0] != (int32_t)numSubMeshes) + { + NvParameterized::Handle handle(*mGraphicalLods[graphicalLodId], "physicsMeshPartitioning"); + PX_ASSERT(handle.isValid()); + PX_ASSERT(handle.parameterDefinition()->type() == NvParameterized::TYPE_ARRAY); + handle.resizeArray((int32_t)numSubMeshes); + } + } + + uint32_t meshPartitioningIndex = 0; + for (uint32_t s = 0; s < numSubMeshes; s++) + { + const RenderSubmesh& submesh = rma->getSubmesh(s); + const uint32_t numVertices = submesh.getVertexCount(0); + + const uint32_t numIters = 2; + uint32_t costs[numIters] = { 0 }; + + ClothingPhysicalMeshParameters* physicsMesh = mPhysicalMeshes[mGraphicalLods[graphicalLodId]->physicalMeshId]; + + SortGraphicalVerts sortedVertices(numVertices, submeshVertexOffset, submesh.getIndexBuffer(0), submesh.getIndexCount(0), mGraphicalLods[graphicalLodId], physicsMesh, rma); + + costs[0] = sortedVertices.computeCost(); + sortedVertices.sortVertices(); // same ordering as before, but grouped by submeshes now + + costs[1] = costs[0]; // stupid warnings + costs[1] = sortedVertices.computeCost(); + + +#if 0 + // reorder based on triangle distances (into the vertex buffer) + // disable for now... + // PH: On the PS3 perf is 30% better if this is not performed! Needs much more further investigation! + uint32_t numIncreases = 0; + for (uint32_t i = 2; i < numIters; i++) + { + sortedVertices.refineIdealPositions(1.0f); + sortedVertices.sortVertices(); + costs[i] = sortedVertices.computeCost(); + if (costs[i] >= costs[i - 1]) + { + numIncreases++; + if (numIncreases > 40) + { + break; + } + } + else if (numIncreases > 0) + { + numIncreases--; + } + } +#endif + + rma->getInternalSubmesh(s).applyPermutation(sortedVertices.mOld2New, sortedVertices.mNew2Old); + + { + uint32_t vertexCount = 0; + uint32_t vertexAdditionalCount = 0; + + while (vertexAdditionalCount < sortedVertices.mNew2Old.size() && sortedVertices.getMesh(vertexAdditionalCount) <= 0) + { + if (!sortedVertices.isAdditional(vertexAdditionalCount)) + { + vertexCount = vertexAdditionalCount + 1; + } + vertexAdditionalCount++; + } + + mGraphicalLods[graphicalLodId]->physicsMeshPartitioning.buf[meshPartitioningIndex].graphicalSubmesh = s; + mGraphicalLods[graphicalLodId]->physicsMeshPartitioning.buf[meshPartitioningIndex].numSimulatedVertices = vertexCount; + mGraphicalLods[graphicalLodId]->physicsMeshPartitioning.buf[meshPartitioningIndex].numSimulatedVerticesAdditional = vertexAdditionalCount; + } + + // also sort the index buffer accordingly + { + uint32_t* indices = rma->getInternalSubmesh(s).getIndexBufferWritable(0); + + uint32_t indexCount = submesh.getIndexCount(0); + uint32_t triCount = indexCount / 3; + SortGraphicalIndices sortedIndices(indexCount, indices); + + for (uint32_t i = 0; i < triCount; i++) + { + int32_t meshIndex = 0; + for (uint32_t j = 0; j < 3; j++) + { + meshIndex = PxMax(meshIndex, sortedVertices.getMesh(indices[i * 3 + j])); + } + sortedIndices.setTriangleMesh(i, meshIndex); + } + + sortedIndices.sort(); + + uint32_t startTriangle = 0; + while (startTriangle < triCount) // && sortedIndices.getTriangleSubmesh(startTriangle) <= i) + { + startTriangle++; + } + mGraphicalLods[graphicalLodId]->physicsMeshPartitioning.buf[meshPartitioningIndex].numSimulatedIndices = startTriangle * 3; + } + + // also adapt all mesh-mesh skinning tables + + uint32_t* immediateMap = mGraphicalLods[graphicalLodId]->immediateClothMap.buf; + if (immediateMap != NULL) + { + ApexPermute(immediateMap + submeshVertexOffset, sortedVertices.mNew2Old.begin(), numVertices); + } + + for (uint32_t i = 0; i < numSkinClothMap; i++) + { + if (skinClothMap[i].vertexIndexPlusOffset < submeshVertexOffset) + { + continue; + } + else if (skinClothMap[i].vertexIndexPlusOffset >= submeshVertexOffset + numVertices) + { + break; + } + + uint32_t oldVertexIndex = skinClothMap[i].vertexIndexPlusOffset - submeshVertexOffset; + skinClothMap[i].vertexIndexPlusOffset = submeshVertexOffset + sortedVertices.mOld2New[oldVertexIndex]; + } + + for (uint32_t i = 0; i < numSkinClothMapB; i++) + { + const uint32_t vertexIndex = skinClothMapB[i].vertexIndexPlusOffset; + if (vertexIndex >= submeshVertexOffset && vertexIndex < submeshVertexOffset + numVertices) + { + skinClothMapB[i].vertexIndexPlusOffset = submeshVertexOffset + sortedVertices.mOld2New[vertexIndex - submeshVertexOffset]; + } + } + + submeshVertexOffset += numVertices; + meshPartitioningIndex++; + } + + // make sure all maps are sorted again, only mapC type! + nvidia::sort(skinClothMap, numSkinClothMap, SkinClothMapPredicate<ClothingGraphicalLodParametersNS::SkinClothMapD_Type>()); + + uint32_t* immediateMap = mGraphicalLods[graphicalLodId]->immediateClothMap.buf; + if (immediateMap != NULL) + { + for (uint32_t i = 0; i < numSkinClothMap; i++) + { + PX_ASSERT((immediateMap[skinClothMap[i].vertexIndexPlusOffset] & ClothingConstants::ImmediateClothingInSkinFlag) == ClothingConstants::ImmediateClothingInSkinFlag); + immediateMap[skinClothMap[i].vertexIndexPlusOffset] = i | ClothingConstants::ImmediateClothingInSkinFlag; + } + +#ifdef _DEBUG + // sanity check + for (uint32_t i = 0; i < submeshVertexOffset; i++) + { + uint32_t imm = immediateMap[i]; + if (imm != ClothingConstants::ImmediateClothingInvalidValue) + { + if ((imm & ClothingConstants::ImmediateClothingInSkinFlag) == ClothingConstants::ImmediateClothingInSkinFlag) + { + imm &= ClothingConstants::ImmediateClothingReadMask; + + if (numSkinClothMap > 0) + { + PX_ASSERT(imm < numSkinClothMap); + PX_ASSERT(skinClothMap[imm].vertexIndexPlusOffset == i); + } + else if (numSkinClothMapB > 0) + { + PX_ASSERT(imm < numSkinClothMapB); + PX_ASSERT(skinClothMapB[imm].vertexIndexPlusOffset == i); + } + } + } + } +#endif + } + + return true; +} + + + +class DeformableVerticesMaxDistancePredicate +{ +public: + DeformableVerticesMaxDistancePredicate(ClothingConstrainCoefficients* constrainCoefficients) : mConstrainCoefficients(constrainCoefficients) {} + bool operator()(uint32_t oldindex1, uint32_t oldIndex2) const + { + return mConstrainCoefficients[oldindex1].maxDistance > mConstrainCoefficients[oldIndex2].maxDistance; + } + +private: + ClothingConstrainCoefficients* mConstrainCoefficients; +}; + + + +bool ClothingAssetImpl::reorderDeformableVertices(ClothingPhysicalMeshImpl& physicalMesh) +{ + ClothingPhysicalMeshParameters* params = static_cast<ClothingPhysicalMeshParameters*>(physicalMesh.getNvParameterized()); + + const uint32_t curSortingVersion = 1; // bump this number when the sorting changes! + + if (params->physicalMesh.physicalMeshSorting >= curSortingVersion) + { + // nothing needs to be done. + return false; + } + + params->physicalMesh.physicalMeshSorting = curSortingVersion; + + uint32_t* indices = physicalMesh.getIndicesBuffer(); + + // create mapping arrays + Array<uint32_t> newIndices(physicalMesh.getNumVertices(), (uint32_t) - 1); + Array<uint32_t> oldIndices(physicalMesh.getNumVertices(), (uint32_t) - 1); + uint32_t nextIndex = 0; + for (uint32_t i = 0; i < physicalMesh.getNumIndices(); i++) + { + const uint32_t vertexIndex = indices[i]; + if (newIndices[vertexIndex] == (uint32_t) - 1) + { + newIndices[vertexIndex] = nextIndex; + oldIndices[nextIndex] = vertexIndex; + nextIndex++; + } + } + + uint32_t maxVertexIndex = 0; + for (uint32_t j = 0; j < params->physicalMesh.numSimulatedIndices; j++) + { + const uint32_t newVertexIndex = newIndices[indices[j]]; + maxVertexIndex = PxMax(maxVertexIndex, newVertexIndex); + } + + maxVertexIndex++; + params->physicalMesh.numSimulatedVertices = maxVertexIndex; + + DeformableVerticesMaxDistancePredicate predicate(physicalMesh.getConstrainCoefficientBuffer()); + + // Sort mesh. + nvidia::sort(oldIndices.begin(), maxVertexIndex, predicate); + + // fix newIndices, the sort has destroyed them + for (uint32_t i = 0; i < nextIndex; i++) + { + PX_ASSERT(newIndices[oldIndices[i]] != (uint32_t) - 1); + newIndices[oldIndices[i]] = i; + } + + // move unused vertices to the end + if (nextIndex < physicalMesh.getNumVertices()) + { + // TODO check if ApexPermute works without this + for (uint32_t i = 0; i < newIndices.size(); i++) + { + if (newIndices[i] == (uint32_t) - 1) + { + newIndices[i] = nextIndex; + oldIndices[nextIndex] = i; + nextIndex++; + } + } + } + + PX_ASSERT(physicalMesh.getNumVertices() == oldIndices.size()); + PX_ASSERT(nextIndex == physicalMesh.getNumVertices()); // at this point we assume that all vertices are referenced + + // do reordering + physicalMesh.applyPermutation(oldIndices); + + // set max distance 0 vertices + ClothingConstrainCoefficients* coeffs = physicalMesh.getConstrainCoefficientBuffer(); + + const uint32_t numSimulatedVertices = params->physicalMesh.numSimulatedVertices; + + uint32_t vertexIndex = 0; + float eps = 1e-8; + for (; vertexIndex < numSimulatedVertices; vertexIndex++) + { + if (coeffs[vertexIndex].maxDistance <= eps) + { + break; + } + } + + params->physicalMesh.numMaxDistance0Vertices = numSimulatedVertices - vertexIndex; + + // safety + for (; vertexIndex < numSimulatedVertices; vertexIndex++) + { + PX_ASSERT(coeffs[vertexIndex].maxDistance <= eps); + } + + + // clean up existing references + for (uint32_t i = 0; i < physicalMesh.getNumIndices(); i++) + { + indices[i] = newIndices[indices[i]]; + } + + // update mappings into deformable vertex buffer + for (uint32_t i = 0; i < mGraphicalLods.size(); i++) + { + if (mPhysicalMeshes[mGraphicalLods[i]->physicalMeshId] == physicalMesh.getNvParameterized()) + { + ClothingGraphicalMeshAssetWrapper meshAsset(reinterpret_cast<RenderMeshAssetIntl*>(mGraphicalLods[i]->renderMeshAssetPointer)); + ClothingGraphicalLodParameters& graphicalLod = *mGraphicalLods[i]; + + const uint32_t numGraphicalVertices = meshAsset.getNumTotalVertices(); + + if (graphicalLod.immediateClothMap.arraySizes[0] > 0) + { + for (uint32_t j = 0; j < numGraphicalVertices; j++) + { + if (graphicalLod.immediateClothMap.buf[j] != ClothingConstants::ImmediateClothingInvalidValue) + { + if ((graphicalLod.immediateClothMap.buf[j] & ClothingConstants::ImmediateClothingInSkinFlag) == 0) + { + const uint32_t flags = graphicalLod.immediateClothMap.buf[j] & ~ClothingConstants::ImmediateClothingReadMask; + graphicalLod.immediateClothMap.buf[j] = + newIndices[graphicalLod.immediateClothMap.buf[j] & ClothingConstants::ImmediateClothingReadMask] | flags; + } + } + } + } + + for(int32_t j = 0; j < graphicalLod.skinClothMap.arraySizes[0]; ++j) + { + graphicalLod.skinClothMap.buf[j].vertexIndex0 = newIndices[graphicalLod.skinClothMap.buf[j].vertexIndex0]; + graphicalLod.skinClothMap.buf[j].vertexIndex1 = newIndices[graphicalLod.skinClothMap.buf[j].vertexIndex1]; + graphicalLod.skinClothMap.buf[j].vertexIndex2 = newIndices[graphicalLod.skinClothMap.buf[j].vertexIndex2]; + } + } + } + + // update transition maps + + if (params->transitionDown.arraySizes[0] > 0) + { + for (int32_t i = 0; i < params->transitionDown.arraySizes[0]; i++) + { + uint32_t& vertexIndex = params->transitionDown.buf[i].vertexIndexPlusOffset; + PX_ASSERT(vertexIndex == (uint32_t)i); + vertexIndex = newIndices[vertexIndex]; + } + + nvidia::sort(params->transitionDown.buf, (uint32_t)params->transitionDown.arraySizes[0], SkinClothMapPredicate<ClothingPhysicalMeshParametersNS::SkinClothMapD_Type>()); + } + + if (params->transitionUp.arraySizes[0] > 0) + { + for (int32_t i = 0; i < params->transitionUp.arraySizes[0]; i++) + { + uint32_t& vertexIndex = params->transitionUp.buf[i].vertexIndexPlusOffset; + PX_ASSERT(vertexIndex == (uint32_t)i); + vertexIndex = newIndices[vertexIndex]; + } + nvidia::sort(params->transitionUp.buf, (uint32_t)params->transitionUp.arraySizes[0], SkinClothMapPredicate<ClothingPhysicalMeshParametersNS::SkinClothMapD_Type>()); + } + + return true; +} + + + +float ClothingAssetImpl::getMaxMaxDistance(ClothingPhysicalMeshParametersNS::PhysicalMesh_Type& physicalMesh, uint32_t index, uint32_t numIndices) const +{ + uint32_t* indices = physicalMesh.indices.buf; + ClothingPhysicalMeshParametersNS::ConstrainCoefficient_Type* coeffs = physicalMesh.constrainCoefficients.buf; + + float maxDist = coeffs[indices[index]].maxDistance; + for (uint32_t i = 1; i < numIndices; i++) + { + maxDist = PxMax(maxDist, coeffs[indices[index + i]].maxDistance); + } + + return maxDist; +} + + + +uint32_t ClothingAssetImpl::getCorrespondingPhysicalVertices(const ClothingGraphicalLodParameters& graphLod, uint32_t submeshIndex, + uint32_t graphicalVertexIndex, const AbstractMeshDescription& pMesh, + uint32_t submeshVertexOffset, uint32_t indices[4], float trust[4]) const +{ + PX_UNUSED(submeshIndex); // stupid release mode + + PX_ASSERT(pMesh.numIndices > 0); + PX_ASSERT(pMesh.pIndices != NULL); + + + uint32_t result = 0; + + if (graphLod.immediateClothMap.arraySizes[0] > 0) + { + indices[0] = graphLod.immediateClothMap.buf[graphicalVertexIndex + submeshVertexOffset]; + trust[0] = 1.0f; + + if (indices[0] != ClothingConstants::ImmediateClothingInvalidValue) + { + if ((indices[0] & ClothingConstants::ImmediateClothingInSkinFlag) == 0) + { + indices[0] &= ClothingConstants::ImmediateClothingReadMask; + result = 1; + } + else if (graphLod.skinClothMapB.arraySizes[0] > 0) + { + const int32_t temp = int32_t(indices[0] & ClothingConstants::ImmediateClothingReadMask); + if (temp < graphLod.skinClothMapB.arraySizes[0]) + { + for (uint32_t i = 0; i < 3; i++) + { + PX_ASSERT(graphLod.skinClothMapB.buf[temp].faceIndex0 + i < pMesh.numIndices); + indices[i] = pMesh.pIndices[graphLod.skinClothMapB.buf[temp].faceIndex0 + i]; + trust[i] = 1.0f; // PH: This should be lower for some vertices! + } + result = 3; + } + } + else if (graphLod.skinClothMap.arraySizes[0] > 0) + { + const int32_t temp = int32_t(indices[0] & ClothingConstants::ImmediateClothingReadMask); + if (temp < graphLod.skinClothMap.arraySizes[0]) + { + PxVec3 bary = graphLod.skinClothMap.buf[temp].vertexBary; + bary.x = PxClamp(bary.x, 0.0f, 1.0f); + bary.y = PxClamp(bary.y, 0.0f, 1.0f); + bary.z = PxClamp(1.0f - bary.x - bary.y, 0.0f, 1.0f); + uint32_t physVertIndex[3] = + { + graphLod.skinClothMap.buf[temp].vertexIndex0, + graphLod.skinClothMap.buf[temp].vertexIndex1, + graphLod.skinClothMap.buf[temp].vertexIndex2 + }; + for (uint32_t i = 0; i < 3; i++) + { + //PX_ASSERT(graphLod.skinClothMap.buf[temp].faceIndex0 + i < pMesh.numIndices); + indices[i] = physVertIndex[i]; + trust[i] = bary[i]; + } + result = 3; + } + } + } + } + else if (graphLod.skinClothMapB.arraySizes[0] > 0) + { + PX_ASSERT(graphLod.skinClothMapB.buf[graphicalVertexIndex + submeshVertexOffset].submeshIndex == submeshIndex); + PX_ASSERT(graphLod.skinClothMapB.buf[graphicalVertexIndex + submeshVertexOffset].vertexIndexPlusOffset == graphicalVertexIndex + submeshVertexOffset); + + for (uint32_t i = 0; i < 3; i++) + { + PX_ASSERT(graphLod.skinClothMapB.buf[graphicalVertexIndex + submeshVertexOffset].faceIndex0 + i < pMesh.numIndices); + indices[i] = pMesh.pIndices[graphLod.skinClothMapB.buf[graphicalVertexIndex + submeshVertexOffset].faceIndex0 + i]; + trust[i] = 1.0f; + } + result = 3; + } + else if (graphLod.skinClothMap.arraySizes[0] > 0) + { + // we need to do binary search here + uint32_t curMin = 0; + uint32_t curMax = (uint32_t)graphLod.skinClothMap.arraySizes[0]; + const uint32_t searchFor = graphicalVertexIndex + submeshVertexOffset; + while (curMax > curMin) + { + uint32_t middle = (curMin + curMax) >> 1; + PX_ASSERT(middle == graphLod.skinClothMap.buf[middle].vertexIndexPlusOffset); + const uint32_t probeResult = middle; + if (probeResult < searchFor) + { + curMin = middle + 1; + } + else + { + curMax = middle; + } + } + + PX_ASSERT(curMin == graphLod.skinClothMap.buf[curMin].vertexIndexPlusOffset); + if (curMin == searchFor) + { + PxVec3 bary = graphLod.skinClothMap.buf[curMin].vertexBary; + bary.x = PxClamp(bary.x, 0.0f, 1.0f); + bary.y = PxClamp(bary.y, 0.0f, 1.0f); + bary.z = PxClamp(1.0f - bary.x - bary.y, 0.0f, 1.0f); + + uint32_t physVertIndex[3] = + { + graphLod.skinClothMap.buf[curMin].vertexIndex0, + graphLod.skinClothMap.buf[curMin].vertexIndex1, + graphLod.skinClothMap.buf[curMin].vertexIndex2 + }; + for (uint32_t i = 0; i < 3; i++) + { + // PX_ASSERT(graphLod.skinClothMap.buf[curMin].faceIndex0 + i < pMesh.numIndices); + indices[i] = physVertIndex[i]; + trust[i] = bary[i]; + } + result = 3; + } + } + else if (graphLod.tetraMap.arraySizes[0] > 0) + { + for (uint32_t i = 0; i < 4; i++) + { + PX_ASSERT(graphLod.tetraMap.buf[graphicalVertexIndex + submeshVertexOffset].tetraIndex0 + i < pMesh.numIndices); + indices[i] = pMesh.pIndices[graphLod.tetraMap.buf[graphicalVertexIndex + submeshVertexOffset].tetraIndex0 + i]; + trust[i] = 1.0f; + } + result = 4; + } + + for (uint32_t i = 0; i < result; i++) + { + PX_ASSERT(trust[i] >= 0.0f); + PX_ASSERT(trust[i] <= 1.0f); + } + + return result; +} + + + +void ClothingAssetImpl::getNormalsAndVerticesForFace(PxVec3* vtx, PxVec3* nrm, uint32_t i, const AbstractMeshDescription& srcPM) const +{ + // copy indices for convenience + PX_ASSERT(i < srcPM.numIndices); + uint32_t di[3]; + for (uint32_t j = 0; j < 3; j++) + { + di[j] = srcPM.pIndices[i + j]; + } + + // To guarantee consistency in our implicit tetrahedral mesh definition we must always order vertices + // idx[0,1,2] = min, max and mid + uint32_t idx[3]; + idx[0] = PxMin(di[0], PxMin(di[1], di[2])); + idx[1] = PxMax(di[0], PxMax(di[1], di[2])); + idx[2] = idx[0]; + for (uint32_t j = 0; j < 3; j++) + { + if ((idx[0] != di[j]) && (idx[1] != di[j])) + { + idx[2] = di[j]; + } + } + + for (uint32_t j = 0; j < 3; j++) + { + vtx[j] = srcPM.pPosition[idx[j]]; + nrm[j] = srcPM.pNormal[idx[j]]; +#ifdef _DEBUG + // sanity + // PH: These normals 'should' always be normalized, maybe we can get rid of the normalize completely! + const float length = nrm[j].magnitudeSquared(); + if (!(length >= 0.99f && length <= 1.01f)) + { + static bool first = true; + if (first) + { + PX_ALWAYS_ASSERT(); + first = false; + } + } +#else + // PH: let's try and disable it in release mode... + //nrm[j].normalize(); +#endif + }; +} + + + +bool ClothingAssetImpl::setBoneName(uint32_t internalIndex, const char* name) +{ + NvParameterized::Handle bonesHandle(*mParams); + mParams->getParameterHandle("bones", bonesHandle); + + if (bonesHandle.isValid()) + { + NvParameterized::Handle boneHandle(*mParams); + bonesHandle.getChildHandle((int32_t)internalIndex, boneHandle); + + if (boneHandle.isValid()) + { + NvParameterized::Handle nameHandle(*mParams); + boneHandle.getChildHandle(mParams, "name", nameHandle); + + if (nameHandle.isValid()) + { + mParams->setParamString(nameHandle, name); + return true; + } + } + } + return false; +} + + + +void ClothingAssetImpl::clearMapping(uint32_t graphicalLodIndex) +{ + if (mGraphicalLods[graphicalLodIndex]->immediateClothMap.buf != NULL) + { + ParamArray<uint32_t> immediateClothMap(mGraphicalLods[graphicalLodIndex], "immediateClothMap", reinterpret_cast<ParamDynamicArrayStruct*>(&mGraphicalLods[graphicalLodIndex]->immediateClothMap)); + immediateClothMap.clear(); + } + if (mGraphicalLods[graphicalLodIndex]->skinClothMapB.buf != NULL) + { + ParamArray<ClothingGraphicalLodParametersNS::SkinClothMapB_Type> skinClothMapB(mGraphicalLods[graphicalLodIndex], "skinClothMapB", reinterpret_cast<ParamDynamicArrayStruct*>(&mGraphicalLods[graphicalLodIndex]->skinClothMapB)); + skinClothMapB.clear(); + } + if (mGraphicalLods[graphicalLodIndex]->tetraMap.buf != NULL) + { + ParamArray<ClothingGraphicalLodParametersNS::TetraLink_Type> tetraMap(mGraphicalLods[graphicalLodIndex], "tetraMap", reinterpret_cast<ParamDynamicArrayStruct*>(&mGraphicalLods[graphicalLodIndex]->tetraMap)); + tetraMap.clear(); + } +} + + + +bool ClothingAssetImpl::findTriangleForImmediateVertex(uint32_t& faceIndex, uint32_t& indexInTriangle, uint32_t physVertIndex, ClothingPhysicalMeshParametersNS::PhysicalMesh_Type& physicalMesh) const +{ + // find triangle with smallest faceIndex, indices have been sorted during authoring + // such that simulated triangles are first + for (uint32_t physIndex = 0; physIndex < (uint32_t)physicalMesh.indices.arraySizes[0]; physIndex++) + { + if (physicalMesh.indices.buf[physIndex] == physVertIndex) + { + // this is a triangle that contains the vertex from the immediate map) + uint32_t currentFaceIndex = physIndex - (physIndex%3); + + faceIndex = currentFaceIndex; + indexInTriangle = physIndex - faceIndex; + return true; + } + } + + return false; +} + + + +// we can't just regenerate the skinClothMap, because master/slave info is only available during authoring +bool ClothingAssetImpl::mergeMapping(ClothingGraphicalLodParameters* graphicalLod) +{ + if (graphicalLod->immediateClothMap.buf == NULL) + return false; + + ParamArray<uint32_t> immediateMap(graphicalLod, "immediateClothMap", reinterpret_cast<ParamDynamicArrayStruct*>(&graphicalLod->immediateClothMap)); + + // size of immediateMap (equals number of graphical vertices) + uint32_t immediateCount = (uint32_t)graphicalLod->immediateClothMap.arraySizes[0]; + + ParamArray<SkinClothMap> skinClothMap(graphicalLod, "skinClothMap", + reinterpret_cast<ParamDynamicArrayStruct*>(&graphicalLod->skinClothMap)); + + uint32_t oldSkinMapSize = skinClothMap.size(); + skinClothMap.resize(immediateCount); + + SkinClothMap* mapEntry = &skinClothMap[oldSkinMapSize]; + + // get RenderMeshAsset + ClothingGraphicalMeshAssetWrapper renderMesh(reinterpret_cast<RenderMeshAssetIntl*>(graphicalLod->renderMeshAssetPointer)); + uint32_t numSubmeshes = renderMesh.getSubmeshCount(); + + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* physicalMesh = &mPhysicalMeshes[graphicalLod->physicalMeshId]->physicalMesh; + PX_ASSERT(physicalMesh != NULL); + + // some updates for the case where we didn't have a skinClothMap yet + if (graphicalLod->skinClothMapThickness == 0.0f) + { + graphicalLod->skinClothMapThickness = 1.0f; + } + if (graphicalLod->skinClothMapOffset == 0.0f) + { + graphicalLod->skinClothMapOffset = DEFAULT_PM_OFFSET_ALONG_NORMAL_FACTOR * physicalMesh->averageEdgeLength; + } + + uint32_t invalidCount = 0; + + // iterate through immediate map, append verts to skinClothMap that are not yet in there + uint32_t mapVertIndex = 0; + for (uint32_t submeshIndex = 0; submeshIndex < numSubmeshes; ++submeshIndex) + { + RenderDataFormat::Enum outFormat; + const PxVec4* tangents = (const PxVec4*)renderMesh.getVertexBuffer(submeshIndex, RenderVertexSemantic::TANGENT, outFormat); + PX_ASSERT(tangents == NULL || outFormat == RenderDataFormat::FLOAT4); + + const PxVec3* positions = (const PxVec3*)renderMesh.getVertexBuffer(submeshIndex, RenderVertexSemantic::POSITION, outFormat); + PX_ASSERT(positions == NULL || outFormat == RenderDataFormat::FLOAT3); + + const PxVec3* normals = (const PxVec3*)renderMesh.getVertexBuffer(submeshIndex, RenderVertexSemantic::NORMAL, outFormat); + PX_ASSERT(normals == NULL || outFormat == RenderDataFormat::FLOAT3); + + for (uint32_t submeshVertIndex = 0; submeshVertIndex < renderMesh.getNumVertices(submeshIndex); ++submeshVertIndex, ++mapVertIndex) + { + PX_ASSERT(mapVertIndex < immediateCount); + + uint32_t physVertIndex = 0; + if (immediateMap[mapVertIndex] == ClothingConstants::ImmediateClothingInvalidValue) + { + ++invalidCount; + continue; + } + + physVertIndex = immediateMap[mapVertIndex] & ClothingConstants::ImmediateClothingReadMask; + + if ((immediateMap[mapVertIndex] & ClothingConstants::ImmediateClothingInSkinFlag) > 0) + { + // in that case physVertIndex is the index in the skinClothMap. + // set it to the current index, so we can sort it afterwards + skinClothMap[physVertIndex].vertexIndexPlusOffset = mapVertIndex; + continue; + } + + // find triangle mapping + uint32_t faceIndex = UINT32_MAX; + uint32_t indexInTriangle = UINT32_MAX; + findTriangleForImmediateVertex(faceIndex, indexInTriangle, physVertIndex, *physicalMesh); + + mapEntry->vertexIndex0 = physicalMesh->indices.buf[faceIndex + 0]; + mapEntry->vertexIndex1 = physicalMesh->indices.buf[faceIndex + 1]; + mapEntry->vertexIndex2 = physicalMesh->indices.buf[faceIndex + 2]; + + // for immediate skinned verts + // position, normal and tangent all have the same barycentric coord on the triangle + PxVec3 bary(0.0f); + if (indexInTriangle < 2) + { + bary[indexInTriangle] = 1.0f; + } + + mapEntry->vertexBary = bary; + // mapEntry->vertexBary.z = 0 because it's on the triangle + + // offset the normal + mapEntry->normalBary = bary; + mapEntry->normalBary.z = graphicalLod->skinClothMapOffset; + + // we need to compute tangent bary's because there are no tangents on physical mesh + bary.x = bary.y = bary.z = UINT32_MAX; + if (positions != NULL && normals != NULL && tangents != NULL) + { + PxVec3 dummy(0.0f); + PxVec3 position = positions[submeshVertIndex]; + PxVec3 tangent = tangents[submeshVertIndex].getXYZ(); + + // prepare triangle data + TriangleWithNormals triangle; + triangle.valid = 0; + triangle.faceIndex0 = faceIndex; + PxVec3* physNormals = physicalMesh->skinningNormals.buf; + if (physNormals == NULL) + { + physNormals = physicalMesh->normals.buf; + } + PX_ASSERT(physNormals != NULL); + for (uint32_t j = 0; j < 3; j++) + { + uint32_t triVertIndex = physicalMesh->indices.buf[triangle.faceIndex0 + j]; + triangle.vertices[j] = physicalMesh->vertices.buf[triVertIndex]; + triangle.normals[j] = physNormals[triVertIndex]; + } + triangle.init(); + + ModuleClothingHelpers::computeTriangleBarys(triangle, dummy, dummy, position + tangent, graphicalLod->skinClothMapOffset, 0, true); + mapEntry->tangentBary = triangle.tempBaryTangent; + } + + mapEntry->vertexIndexPlusOffset = mapVertIndex; + mapEntry++; + } + } + + skinClothMap.resize(skinClothMap.size() - invalidCount); + + // make sure skinClothMap is sorted by graphical vertex order + nvidia::sort(graphicalLod->skinClothMap.buf, (uint32_t)graphicalLod->skinClothMap.arraySizes[0], SkinClothMapPredicate<ClothingGraphicalLodParametersNS::SkinClothMapD_Type>()); + + immediateMap.clear(); + + // notify actors of the asset change + for (uint32_t i = 0; i < getNumActors(); ++i) + { + ClothingActorImpl& actor = DYNAMIC_CAST(ClothingActorProxy*)(mActors.getResource(i))->impl; + actor.reinitActorData(); + } + + return true; +} + + +void ClothingAssetImpl::updateBoundingBox() +{ + PxBounds3 tempBounds; + tempBounds.setEmpty(); + + for (uint32_t graphicalMeshId = 0; graphicalMeshId < mGraphicalLods.size(); graphicalMeshId++) + { + if (mGraphicalLods[graphicalMeshId]->renderMeshAssetPointer == NULL) + continue; + + RenderMeshAssetIntl* renderMeshAsset = reinterpret_cast<RenderMeshAssetIntl*>(mGraphicalLods[graphicalMeshId]->renderMeshAssetPointer); + for (uint32_t submeshIndex = 0; submeshIndex < renderMeshAsset->getSubmeshCount(); submeshIndex++) + { + const VertexBuffer& vb = renderMeshAsset->getInternalSubmesh(submeshIndex).getVertexBuffer(); + const VertexFormat& vf = vb.getFormat(); + uint32_t bufferIndex = (uint32_t)vf.getBufferIndexFromID(vf.getSemanticID(nvidia::apex::RenderVertexSemantic::POSITION)); + RenderDataFormat::Enum positionFormat; + const PxVec3* pos = (const PxVec3*)vb.getBufferAndFormat(positionFormat, bufferIndex); + PX_ASSERT(positionFormat == nvidia::RenderDataFormat::FLOAT3); + const uint32_t numVertices = renderMeshAsset->getInternalSubmesh(submeshIndex).getVertexCount(0); + for (uint32_t i = 0; i < numVertices; i++) + { + tempBounds.include(pos[i]); + } + } + } + for (uint32_t i = 0; i < mPhysicalMeshes.size(); i++) + { + AbstractMeshDescription other; + other.pPosition = mPhysicalMeshes[i]->physicalMesh.vertices.buf; + + for (uint32_t j = 0; j < other.numVertices; j++) + { + tempBounds.include(other.pPosition[j]); + } + } + + mParams->boundingBox = tempBounds; +} + + + +float ClothingAssetImpl::getMaxDistReduction(ClothingPhysicalMeshParameters& physicalMesh, float maxDistanceMultiplier) const +{ + return physicalMesh.physicalMesh.maximumMaxDistance * (1.0f - maxDistanceMultiplier); +} + + + +#ifndef WITHOUT_PVD +void ClothingAssetImpl::initPvdInstances(pvdsdk::PvdDataStream& pvdStream) +{ + ApexResourceInterface* pvdInstance = static_cast<ApexResourceInterface*>(this); + + // Asset Params + pvdStream.createInstance(pvdsdk::NamespacedName(APEX_PVD_NAMESPACE, "ClothingAssetParameters"), mParams); + pvdStream.setPropertyValue(pvdInstance, "AssetParams", pvdsdk::DataRef<const uint8_t>((const uint8_t*)&mParams, sizeof(ClothingAssetParameters*)), pvdsdk::getPvdNamespacedNameForType<pvdsdk::ObjectRef>()); + + // update asset param properties (should we do this per frame? if so, how?) + pvdsdk::ApexPvdClient* client = GetInternalApexSDK()->getApexPvdClient(); + PX_ASSERT(client != NULL); + client->updatePvd(mParams, *mParams); + + mActors.initPvdInstances(pvdStream); +} + + + +void ClothingAssetImpl::destroyPvdInstances() +{ + pvdsdk::ApexPvdClient* client = GetInternalApexSDK()->getApexPvdClient(); + if (client != NULL) + { + if (client->isConnected() && client->getPxPvd().getInstrumentationFlags() & PxPvdInstrumentationFlag::eDEBUG) + { + pvdsdk::PvdDataStream* pvdStream = client->getDataStream(); + { + if (pvdStream != NULL) + { + client->updatePvd(mParams, *mParams, pvdsdk::PvdAction::DESTROY); + pvdStream->destroyInstance(mParams); + } + } + } + } +} +#endif + +} +} // namespace nvidia + + |