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/ClothingActorImpl.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/ClothingActorImpl.cpp')
| -rw-r--r-- | APEX_1.4/module/clothing/src/ClothingActorImpl.cpp | 4852 |
1 files changed, 4852 insertions, 0 deletions
diff --git a/APEX_1.4/module/clothing/src/ClothingActorImpl.cpp b/APEX_1.4/module/clothing/src/ClothingActorImpl.cpp new file mode 100644 index 00000000..06956ddd --- /dev/null +++ b/APEX_1.4/module/clothing/src/ClothingActorImpl.cpp @@ -0,0 +1,4852 @@ +/* + * 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 "ApexSimdMath.h" +#include "ApexDefs.h" + +#include "PsIntrinsics.h" + +#include "ClothingActorImpl.h" +#include "ClothingActorProxy.h" +#include "ClothingCooking.h" +#include "ClothingPhysicalMesh.h" +#include "ClothingPreviewProxy.h" +#include "ClothingScene.h" +#include "CookingPhysX.h" +#include "ModuleClothing.h" +#include "ModulePerfScope.h" +#include "ClothingVelocityCallback.h" +#include "RenderMeshActorDesc.h" +#include "SimulationAbstract.h" + +#include "ClothingGraphicalLodParameters.h" +#include "DebugRenderParams.h" + +#include "SceneIntl.h" +#include "ApexSDKIntl.h" +#include "ApexUsingNamespace.h" +#include "PsAtomic.h" + +#include "ApexMath.h" + +#if PX_PHYSICS_VERSION_MAJOR == 3 +#include "PxScene.h" +//#include "ApexReadWriteLock.h" +#endif + +#include "PsVecMath.h" + +#include "PxStrideIterator.h" + +#include "Simulation.h" + +#include "ApexPvdClient.h" + +namespace nvidia +{ +namespace clothing +{ + +ClothingActorImpl::ClothingActorImpl(const NvParameterized::Interface& descriptor, ClothingActorProxy* actorProxy, + ClothingPreviewProxy* previewProxy, ClothingAssetImpl* asset, ClothingScene* scene) : + mActorProxy(actorProxy), + mPreviewProxy(previewProxy), + mAsset(asset), + mClothingScene(scene), +#if PX_PHYSICS_VERSION_MAJOR == 3 + mPhysXScene(NULL), +#endif + mActorDesc(NULL), + mBackendName(NULL), + mInternalGlobalPose(PxVec4(1.0f)), + mOldInternalGlobalPose(PxVec4(0.0f)), + mInternalInterpolatedGlobalPose(PxVec4(1.0f)), + mInternalInterpolatedBoneMatrices(NULL), + mCurrentSolverIterations(0), + mInternalScaledGravity(0.0f, 0.0f, 0.0f), + mInternalMaxDistanceBlendTime(0.0f), + mMaxDistReduction(0.0f), + mBufferedGraphicalLod(0), + mCurrentGraphicalLodId(0), + mRenderProxyReady(NULL), + mRenderProxyURR(NULL), + mClothingSimulation(NULL), + mCurrentMaxDistanceBias(0.0f), + bIsSimulationOn(false), + mForceSimulation(-1), + mLodCentroid(0.0f, 0.0f, 0.0f), + mLodRadiusSquared(0.0f), + mVelocityCallback(NULL), + mInterCollisionChannels(0), + mIsAllowedHalfPrecisionSolver(false), + mBeforeTickTask(this), + mDuringTickTask(this), + mFetchResultsTask(this), + mActiveCookingTask(NULL), + mFetchResultsRunning(false), + bGlobalPoseChanged(1), + bBoneMatricesChanged(1), + bBoneBufferDirty(0), + bMaxDistanceScaleChanged(0), + bBlendingAllowed(1), + bDirtyActorTemplate(0), + bDirtyShapeTemplate(0), + bDirtyClothingTemplate(0), + bBufferedVisible(1), + bInternalVisible(1), + bUpdateFrozenFlag(0), + bBufferedFrozen(0), + bInternalFrozen(0), + bPressureWarning(0), + bUnsucessfullCreation(0), + bInternalTeleportDue(ClothingTeleportMode::Continuous), + bInternalScaledGravityChanged(1), + bReinitActorData(0), + bInternalLocalSpaceSim(0), + bActorCollisionChanged(0) +{ + //mBufferedBoneMatrices = NULL; + + if ((((size_t)this) & 0xf) != 0) + { + APEX_INTERNAL_ERROR("ClothingActorImpl is not 16 byte aligned"); + } + // make sure the alignment is ok + if ((((size_t)&mInternalGlobalPose) & 0xf) != 0) + { + APEX_INTERNAL_ERROR("Matrix ClothingActorImpl::mInternalGlobalPose is not 16 byte aligned"); + } + if ((((size_t)&mOldInternalGlobalPose) & 0xf) != 0) + { + APEX_INTERNAL_ERROR("Matrix ClothingActorImpl::mOldInternalGlobalPose is not 16 byte aligned"); + } + if ((((size_t)&mInternalInterpolatedGlobalPose) & 0xf) != 0) + { + APEX_INTERNAL_ERROR("Matrix ClothingActorImpl::mInternalInterpolatedGlobalPose is not 16 byte aligned"); + } + + if (::strcmp(descriptor.className(), ClothingActorParam::staticClassName()) == 0) + { + PX_ASSERT(mActorProxy != NULL); + + mActorDesc = static_cast<ClothingActorParam*>(GetInternalApexSDK()->getParameterizedTraits()->createNvParameterized(ClothingActorParam::staticClassName())); + PX_ASSERT(mActorDesc != NULL); + mActorDesc->copy(descriptor); + PX_ASSERT(mActorDesc->equals(descriptor, NULL, 0)); + + const ClothingActorParamNS::ParametersStruct& actorDesc = static_cast<const ClothingActorParamNS::ParametersStruct&>(*mActorDesc); + + // initialize these too + mInternalWindParams = mActorDesc->windParams; + mInternalMaxDistanceScale = mActorDesc->maxDistanceScale; + mInternalFlags = mActorDesc->flags; + bInternalLocalSpaceSim = mActorDesc->localSpaceSim ? 1u : 0u; + + mInterCollisionChannels = mAsset->getInterCollisionChannels(); + + // Physics is turned off initially + mCurrentSolverIterations = 0; + + if (actorDesc.slowStart) + { + mMaxDistReduction = mAsset->getBiggestMaxDistance(); + } + + + mNewBounds.setEmpty(); + + // prepare some runtime data for each graphical mesh + mGraphicalMeshes.reserve(mAsset->getNumGraphicalMeshes()); + uint32_t vertexOffset = 0; + for (uint32_t i = 0; i < mAsset->getNumGraphicalMeshes(); i++) + { + ClothingGraphicalMeshActor actor; + + RenderMeshAssetIntl* renderMeshAsset = mAsset->getGraphicalMesh(i); + // it can be NULL if ClothingAsset::releaseGraphicalData has beend called to do skinning externally + if (renderMeshAsset != NULL) + { + const uint32_t numSubmeshes = renderMeshAsset->getSubmeshCount(); + + for (uint32_t si = 0; si < numSubmeshes; ++si) + { + actor.morphTargetVertexOffsets.pushBack(vertexOffset); + vertexOffset += renderMeshAsset->getSubmesh(si).getVertexCount(0); + } + } + else + { + actor.morphTargetVertexOffsets.pushBack(0); + } + + actor.active = i == 0; + mGraphicalMeshes.pushBack(actor); + } + + // When we add ourselves to the ApexScene, it will call us back with setPhysXScene + addSelfToContext(*mClothingScene->mApexScene->getApexContext()); + + // Add ourself to our ClothingScene + addSelfToContext(*static_cast<ApexContext*>(mClothingScene)); + + // make sure the clothing material gets initialized when + // applyClothingMaterial is called the first time + mClothingMaterial.solverIterations = uint32_t(-1); + + if (::strcmp(mActorDesc->simulationBackend, "Default") == 0) + { + mBackendName = "Embedded"; + } + else if (::strcmp(mActorDesc->simulationBackend, "ForceEmbedded") == 0) + { + mBackendName = "Embedded"; + } + else + { + mBackendName = "Native"; + } + + if (mActorDesc->morphDisplacements.arraySizes[0] > 0) + { + PX_PROFILE_ZONE("ClothingActorImpl::morphTarget", GetInternalApexSDK()->getContextId()); + + if (mActorDesc->morphPhysicalMeshNewPositions.buf == NULL) + { + ParamArray<PxVec3> morphPhysicalNewPos(mActorDesc, "morphPhysicalMeshNewPositions", reinterpret_cast<ParamDynamicArrayStruct*>(&mActorDesc->morphPhysicalMeshNewPositions)); + mAsset->getDisplacedPhysicalMeshPositions(mActorDesc->morphDisplacements.buf, morphPhysicalNewPos); + + CookingAbstract* cookingJob = mAsset->getModuleClothing()->getBackendFactory(mBackendName)->createCookingJob(); + PX_ASSERT(cookingJob != NULL); + + if (cookingJob != NULL) + { + PxVec3 gravity = scene->mApexScene->getGravity(); + gravity = mActorDesc->globalPose.inverseRT().rotate(gravity); + mAsset->prepareCookingJob(*cookingJob, mActorDesc->actorScale, &gravity, morphPhysicalNewPos.begin()); + + if (cookingJob->isValid()) + { + if (mAsset->getModuleClothing()->allowAsyncCooking()) + { + mActiveCookingTask = PX_NEW(ClothingCookingTask)(mClothingScene, *cookingJob); + mActiveCookingTask->lockObject(mAsset); + mClothingScene->submitCookingTask(mActiveCookingTask); + } + else + { + mActorDesc->runtimeCooked = cookingJob->execute(); + PX_DELETE_AND_RESET(cookingJob); + } + } + else + { + PX_DELETE_AND_RESET(cookingJob); + } + } + } + + if (mActorDesc->morphGraphicalMeshNewPositions.buf == NULL) + { + ParamArray<PxVec3> morphGraphicalNewPos(mActorDesc, "morphGraphicalMeshNewPositions", reinterpret_cast<ParamDynamicArrayStruct*>(&mActorDesc->morphGraphicalMeshNewPositions)); + + uint32_t graphicalVertexCount = 0; + for (uint32_t gi = 0; gi < mGraphicalMeshes.size(); ++gi) + { + RenderMeshAssetIntl* renderMeshAsset = mAsset->getGraphicalMesh(gi); + + const ClothingGraphicalMeshAssetWrapper meshAsset(renderMeshAsset); + graphicalVertexCount += meshAsset.getNumTotalVertices(); + } + + morphGraphicalNewPos.resize(graphicalVertexCount); + + uint32_t vertexOffset = 0; + for (uint32_t gi = 0; gi < mGraphicalMeshes.size(); ++gi) + { + RenderMeshAssetIntl* renderMeshAsset = mAsset->getGraphicalMesh(gi); + if (renderMeshAsset == NULL) + continue; + + const uint32_t numSubmeshes = renderMeshAsset->getSubmeshCount(); + for (uint32_t si = 0; si < numSubmeshes; ++si) + { + const RenderSubmesh& submesh = renderMeshAsset->getSubmesh(si); + const VertexFormat& format = submesh.getVertexBuffer().getFormat(); + + uint32_t* morphMapping = mAsset->getMorphMapping(gi, si); + + const int32_t positionIndex = format.getBufferIndexFromID(format.getSemanticID(RenderVertexSemantic::POSITION)); + if (positionIndex != -1) + { + RenderDataFormat::Enum bufferFormat = RenderDataFormat::UNSPECIFIED; + const PxVec3* positions = reinterpret_cast<const PxVec3*>(submesh.getVertexBuffer().getBufferAndFormat(bufferFormat, (uint32_t)positionIndex)); + PX_ASSERT(bufferFormat == RenderDataFormat::FLOAT3); + if (bufferFormat == RenderDataFormat::FLOAT3) + { + const uint32_t vertexCount = submesh.getVertexCount(0); + for (uint32_t i = 0; i < vertexCount; i++) + { + const PxVec3 disp = morphMapping != NULL ? mActorDesc->morphDisplacements.buf[morphMapping[i]] : PxVec3(0.0f); + morphGraphicalNewPos[i + vertexOffset] = positions[i] + disp; + } + } + } + + vertexOffset += submesh.getVertexCount(0); + } + } + } + } + + // default render proxy to handle pre simulate case + mRenderProxyReady = mClothingScene->getRenderProxy(mAsset->getGraphicalMesh(0), mActorDesc->fallbackSkinning, false, mOverrideMaterials, mActorDesc->morphPhysicalMeshNewPositions.buf, &mGraphicalMeshes[0].morphTargetVertexOffsets[0]); + + if (getRuntimeCookedDataPhysX() != NULL && mActorDesc->actorScale != getRuntimeCookedDataPhysX()->actorScale) + { + mActorDesc->runtimeCooked->destroy(); + mActorDesc->runtimeCooked = NULL; + } + + // PH: So if backend name is 'embedded', i won't get an asset cooked data ever, so it also won't complain about the cooked version, good + // if backend is native, it might, but that's only when you force native with the 2.8.x sdk on a 3.2 asset + // const char* cookingDataType = mAsset->getModuleClothing()->getBackendFactory(mBackendName)->getCookingJobType(); + NvParameterized::Interface* assetCookedData = mAsset->getCookedData(mActorDesc->actorScale); + NvParameterized::Interface* actorCookedData = mActorDesc->runtimeCooked; + + BackendFactory* factory = mAsset->getModuleClothing()->getBackendFactory(mBackendName); + + uint32_t assetCookedDataVersion = factory->getCookedDataVersion(assetCookedData); + uint32_t actorCookedDataVersion = factory->getCookedDataVersion(actorCookedData); + + if (assetCookedData != NULL && !factory->isMatch(assetCookedData->className())) + { + APEX_DEBUG_WARNING("Asset (%s) cooked data type (%s) does not match the current backend (%s). Recooking.", + mAsset->getName(), assetCookedData->className(), mBackendName); + assetCookedData = NULL; + } + // If the PhysX3 cooking format changes from 3.0 to 3.x, then APEX needs to store something other than the + // _SDK_VERSION_NUMBER. Perhaps _PHYSICS_SDK_VERSION (which is something like 0x03010000 or 0x02080400). + // Currently, _SDK_VERSION_NUMBER does not change for P3, it is fixed at 300. The PhysX2 path will continue to use + // _SDK_VERSION_NUMBER so existing assets cooked with PhysX2 data aren't recooked by default. + + else if (assetCookedData && assetCookedDataVersion != factory->getCookingVersion()) + { + APEX_DEBUG_WARNING("Asset (%s) cooked data version (%d/0x%08x) does not match the current sdk version. Recooking.", + mAsset->getName(), + assetCookedDataVersion, + assetCookedDataVersion); + assetCookedData = NULL; + } + + if (actorCookedData != NULL && !factory->isMatch(actorCookedData->className())) + { + APEX_DEBUG_WARNING("Asset (%s) cooked data type (%s) does not match the current backend (%s). Recooking.", + mAsset->getName(), assetCookedData->className(), mBackendName); + actorCookedData = NULL; + } + + if (actorCookedData && actorCookedDataVersion != factory->getCookingVersion()) + { + APEX_DEBUG_WARNING("Actor (%s) cooked data version (%d/0x%08x) does not match the current sdk version. Recooking.", + mAsset->getName(), + actorCookedDataVersion, + actorCookedDataVersion); + actorCookedData = NULL; + } + + if (assetCookedData == NULL && actorCookedData == NULL && mActiveCookingTask == NULL) + { + CookingAbstract* cookingJob = mAsset->getModuleClothing()->getBackendFactory(mBackendName)->createCookingJob(); + PX_ASSERT(cookingJob != NULL); + + if (cookingJob != NULL) + { + PxVec3 gravity = scene->mApexScene->getGravity(); + gravity = mActorDesc->globalPose.inverseRT().rotate(gravity); + mAsset->prepareCookingJob(*cookingJob, mActorDesc->actorScale, &gravity, NULL); + + if (cookingJob->isValid()) + { + if (mAsset->getModuleClothing()->allowAsyncCooking()) + { + mActiveCookingTask = PX_NEW(ClothingCookingTask)(mClothingScene, *cookingJob); + mActiveCookingTask->lockObject(mAsset); + mClothingScene->submitCookingTask(mActiveCookingTask); + } + else + { + mActorDesc->runtimeCooked = cookingJob->execute(); + PX_DELETE_AND_RESET(cookingJob); + } + } + else + { + PX_DELETE_AND_RESET(cookingJob); + } + } + + } + + mActorProxy->userData = reinterpret_cast<void*>(mActorDesc->userData); + } + else if (::strcmp(descriptor.className(), ClothingPreviewParam::staticClassName()) == 0) + { + PX_ASSERT(mPreviewProxy != NULL); + + const ClothingPreviewParam& previewDesc = static_cast<const ClothingPreviewParam&>(descriptor); + mActorDesc = static_cast<ClothingActorParam*>(GetInternalApexSDK()->getParameterizedTraits()->createNvParameterized(ClothingActorParam::staticClassName())); + PX_ASSERT(mActorDesc != NULL); + mActorDesc->globalPose = previewDesc.globalPose; + { + NvParameterized::Handle handle(mActorDesc); + handle.getParameter("boneMatrices"); + handle.resizeArray(previewDesc.boneMatrices.arraySizes[0]); + for (int32_t i = 0; i < previewDesc.boneMatrices.arraySizes[0]; i++) + { + mActorDesc->boneMatrices.buf[i] = previewDesc.boneMatrices.buf[i]; + } + } + mActorDesc->useInternalBoneOrder = previewDesc.useInternalBoneOrder; + mActorDesc->updateStateWithGlobalMatrices = previewDesc.updateStateWithGlobalMatrices; + mActorDesc->fallbackSkinning = previewDesc.fallbackSkinning; + + //mActorDesc->copy(descriptor); + //PX_ASSERT(mActorDesc->equals(descriptor, NULL, 0)); + + // prepare some runtime data for each graphical mesh + mGraphicalMeshes.reserve(mAsset->getNumGraphicalMeshes()); + for (uint32_t i = 0; i < mAsset->getNumGraphicalMeshes(); i++) + { + ClothingGraphicalMeshActor actor; + RenderMeshAssetIntl* renderMeshAsset = mAsset->getGraphicalMesh(i); + if (renderMeshAsset == NULL) + continue; + + actor.active = i == 0; + actor.morphTargetVertexOffsets.pushBack(0); + mGraphicalMeshes.pushBack(actor); + } + // default render proxy to handle pre simulate case + mRenderProxyReady = PX_NEW(ClothingRenderProxyImpl)(mAsset->getGraphicalMesh(0), mActorDesc->fallbackSkinning, false, mOverrideMaterials, mActorDesc->morphPhysicalMeshNewPositions.buf, &mGraphicalMeshes[0].morphTargetVertexOffsets[0], NULL); + + mPreviewProxy->userData = reinterpret_cast<void*>(mActorDesc->userData); + } + else + { + APEX_INVALID_PARAMETER("%s is not a valid descriptor class", descriptor.className()); + + PX_ASSERT(mActorProxy == NULL); + PX_ASSERT(mPreviewProxy == NULL); + } + + if (mActorDesc != NULL) + { + // initialize overrideMaterialMap with data from actor desc + for (uint32_t i = 0; i < (uint32_t)mActorDesc->overrideMaterialNames.arraySizes[0]; ++i) + { + mOverrideMaterials[i] = mActorDesc->overrideMaterialNames.buf[i]; + } + + if (mActorDesc->updateStateWithGlobalMatrices) + { + mAsset->setupInvBindMatrices(); + } + + uint32_t numMeshesWithTangents = 0; + uint32_t maxVertexCount = 0; + + for (uint32_t i = 0; i < mGraphicalMeshes.size(); i++) + { + RenderMeshAssetIntl* renderMeshAsset = mAsset->getGraphicalMesh(i); + + const ClothingGraphicalMeshAssetWrapper meshAsset(renderMeshAsset); + + if (meshAsset.hasChannel(NULL, RenderVertexSemantic::TANGENT) && meshAsset.hasChannel(NULL, RenderVertexSemantic::BINORMAL)) + { + PX_ALWAYS_ASSERT(); + // need to compress them into one semantic + //APEX_INVALID_PARAMETER("RenderMeshAsset must have either TANGENT and BINORMAL semantics, or none. But not only one!"); + } + + if (meshAsset.hasChannel(NULL, RenderVertexSemantic::TANGENT) && meshAsset.hasChannel(NULL, RenderVertexSemantic::TEXCOORD0)) + { + numMeshesWithTangents++; + mGraphicalMeshes[i].needsTangents = true; + } + maxVertexCount = PxMax(maxVertexCount, meshAsset.getNumTotalVertices()); + } + + const uint32_t numBones = mActorDesc->useInternalBoneOrder ? mAsset->getNumUsedBones() : mActorDesc->boneMatrices.arraySizes[0]; + updateState(mActorDesc->globalPose, mActorDesc->boneMatrices.buf, sizeof(PxMat44), numBones, ClothingTeleportMode::Continuous); + updateStateInternal_NoPhysX(false); + updateBoneBuffer(mRenderProxyReady); + } + + mRenderBounds = getRenderMeshAssetBoundsTransformed(); + bool bHasBones = mActorDesc->boneMatrices.arraySizes[0] > 0 || mAsset->getNumBones() > 0; + if (bHasBones && bInternalLocalSpaceSim == 1) + { + PX_ASSERT(!mRenderBounds.isEmpty()); + mRenderBounds = PxBounds3::transformFast(PxTransform(mInternalGlobalPose), mRenderBounds); + } + + PX_ASSERT(mRenderBounds.isFinite()); + PX_ASSERT(!mRenderBounds.isEmpty()); +} + + + +void ClothingActorImpl::release() +{ + if (mInRelease) + { + return; + } + + if (isSimulationRunning()) + { + APEX_INVALID_OPERATION("Cannot release ClothingActorImpl while simulation is still running"); + return; + } + + waitForFetchResults(); + + mInRelease = true; + + if (mActorProxy != NULL) + { + mAsset->releaseClothingActor(*mActorProxy); + } + else + { + PX_ASSERT(mPreviewProxy != NULL); + mAsset->releaseClothingPreview(*mPreviewProxy); + } +} + + + +Renderable* ClothingActorImpl::getRenderable() +{ + // make sure the result is ready + // this is mainly for legacy kind of rendering + // with the renderable iterator. note that the + // user does not acquire the render proxy here + waitForFetchResults(); + + return mRenderProxyReady; +} + + + +void ClothingActorImpl::dispatchRenderResources(UserRenderer& api) +{ + mRenderProxyMutex.lock(); + if (mRenderProxyURR != NULL) + { + mRenderProxyURR->dispatchRenderResources(api); + } + mRenderProxyMutex.unlock(); +} + + + +void ClothingActorImpl::updateRenderResources(bool rewriteBuffers, void* userRenderData) +{ + waitForFetchResults(); + + ClothingRenderProxyImpl* newRenderProxy = static_cast<ClothingRenderProxyImpl*>(acquireRenderProxy()); + if (newRenderProxy != NULL) + { + if (mRenderProxyURR != NULL) + { + mRenderProxyURR->release(); + mRenderProxyURR = NULL; + } + mRenderProxyURR = newRenderProxy; + } + if (mRenderProxyURR != NULL) + { + mRenderProxyURR->updateRenderResources(rewriteBuffers, userRenderData); + } +} + + + +NvParameterized::Interface* ClothingActorImpl::getActorDesc() +{ + if (mActorDesc != NULL && isValidDesc(*mActorDesc)) + { + return mActorDesc; + } + + return NULL; +} + + + +void ClothingActorImpl::updateState(const PxMat44& globalPose, const PxMat44* newBoneMatrices, uint32_t boneMatricesByteStride, uint32_t numBoneMatrices, ClothingTeleportMode::Enum teleportMode) +{ + PX_PROFILE_ZONE("ClothingActorImpl::updateState", GetInternalApexSDK()->getContextId()); + + PX_ASSERT(mActorDesc); + const bool useInternalBoneOrder = mActorDesc->useInternalBoneOrder; + + uint32_t numElements = useInternalBoneOrder ? mAsset->getNumUsedBones() : mAsset->getNumBones(); + if (useInternalBoneOrder && (numBoneMatrices > numElements)) + { + APEX_DEBUG_WARNING("numMatrices too big."); + return; + } + + mActorDesc->globalPose = globalPose; + if (!PxEquals(globalPose.column0.magnitude(), mActorDesc->actorScale, 1e-5)) + { + APEX_DEBUG_WARNING("Actor Scale wasn't set properly, it doesn't equal to the Global Pose scale: %f != %f", + mActorDesc->actorScale, + globalPose.column0.magnitude()); + } + + PX_ASSERT(newBoneMatrices == NULL || boneMatricesByteStride >= sizeof(PxMat44)); + if (boneMatricesByteStride >= sizeof(PxMat44) && newBoneMatrices != NULL) + { + if (mActorDesc->boneMatrices.arraySizes[0] != (int32_t)numBoneMatrices) + { + // PH: aligned alloc? + NvParameterized::Handle handle(mActorDesc); + handle.getParameter("boneMatrices"); + handle.resizeArray((int32_t)numBoneMatrices); + } + + for (uint32_t i = 0; i < numBoneMatrices; i++) + { + const PxMat44* source = (const PxMat44*)(((const uint8_t*)newBoneMatrices) + boneMatricesByteStride * i); + mActorDesc->boneMatrices.buf[i] = *source; + } + } + else + { + NvParameterized::Handle handle(mActorDesc); + handle.getParameter("boneMatrices"); + handle.resizeArray(0); + } + + mActorDesc->teleportMode = teleportMode; + + if (mClothingScene == NULL) + { + // In Preview mode! + updateStateInternal_NoPhysX(false); + updateBoneBuffer(mRenderProxyReady); + } +} + + + +void ClothingActorImpl::updateMaxDistanceScale(float scale, bool multipliable) +{ + PX_ASSERT(mActorDesc != NULL); + mActorDesc->maxDistanceScale.Scale = PxClamp(scale, 0.0f, 1.0f); + mActorDesc->maxDistanceScale.Multipliable = multipliable; +} + + + +const PxMat44& ClothingActorImpl::getGlobalPose() const +{ + PX_ASSERT(mActorDesc != NULL); + return mActorDesc->globalPose; +} + + + +void ClothingActorImpl::setWind(float windAdaption, const PxVec3& windVelocity) +{ + if (windAdaption < 0.0f) + { + APEX_INVALID_PARAMETER("windAdaption must be bigger or equal than 0.0 (is %f)", windAdaption); + windAdaption = 0.0f; + } + + PX_ASSERT(mActorDesc); + mActorDesc->windParams.Adaption = windAdaption; + mActorDesc->windParams.Velocity = windVelocity; +} + + + +void ClothingActorImpl::setMaxDistanceBlendTime(float blendTime) +{ + PX_ASSERT(mActorDesc); + mActorDesc->maxDistanceBlendTime = blendTime; +} + + + + +float ClothingActorImpl::getMaxDistanceBlendTime() const +{ + PX_ASSERT(mActorDesc); + return mActorDesc->maxDistanceBlendTime; +} + + + +void ClothingActorImpl::setVisible(bool enable) +{ + // buffer enable + bBufferedVisible = enable ? 1u : 0u; + + // disable immediately + if (!enable) + { + bInternalVisible = 0; + } +} + + + +bool ClothingActorImpl::isVisibleBuffered() const +{ + return bBufferedVisible == 1; +} + + + +bool ClothingActorImpl::isVisible() const +{ + return bInternalVisible == 1; +} + + + +bool ClothingActorImpl::shouldComputeRenderData() const +{ + return mInternalFlags.ComputeRenderData && bInternalVisible == 1 && mGraphicalMeshes[mCurrentGraphicalLodId].renderProxy != NULL; +} + + + +void ClothingActorImpl::setFrozen(bool enable) +{ + bUpdateFrozenFlag = 1; + bBufferedFrozen = enable ? 1u : 0u; +} + + + +bool ClothingActorImpl::isFrozenBuffered() const +{ + return bBufferedFrozen == 1; +} + + + +ClothSolverMode::Enum ClothingActorImpl::getClothSolverMode() const +{ + return ClothSolverMode::v3; +} + + + +void ClothingActorImpl::freeze_LocksPhysX(bool on) +{ + if (mClothingSimulation != NULL) + { + mClothingSimulation->setStatic(on); + } +} + + + +void ClothingActorImpl::setGraphicalLOD(uint32_t lod) +{ + mBufferedGraphicalLod = PxMin(lod, mAsset->getNumGraphicalLodLevels()-1); +} + + + +uint32_t ClothingActorImpl::getGraphicalLod() +{ + return mBufferedGraphicalLod; +} + + + +bool ClothingActorImpl::rayCast(const PxVec3& worldOrigin, const PxVec3& worldDirection, float& time, PxVec3& normal, uint32_t& vertexIndex) +{ + if (mClothingSimulation != NULL) + { + PxVec3 origin(worldOrigin); + PxVec3 dir(worldDirection); + if (bInternalLocalSpaceSim == 1) + { +#if _DEBUG + bool ok = true; + ok &= mInternalGlobalPose.column0.isNormalized(); + ok &= mInternalGlobalPose.column1.isNormalized(); + ok &= mInternalGlobalPose.column2.isNormalized(); + if (!ok) + { + APEX_DEBUG_WARNING("Internal Global Pose is not normalized (Scale: %f %f %f). Raycast could be wrong.", mInternalGlobalPose.column0.magnitude(), mInternalGlobalPose.column1.magnitude(), mInternalGlobalPose.column2.magnitude()); + } +#endif + PxMat44 invGlobalPose = mInternalGlobalPose.inverseRT(); + origin = invGlobalPose.transform(worldOrigin); + dir = invGlobalPose.rotate(worldDirection); + } + + bool hit = mClothingSimulation->raycast(origin, dir, time, normal, vertexIndex); + + if (hit && bInternalLocalSpaceSim == 1) + { + mInternalGlobalPose.rotate(normal); + } + return hit; + + } + + return false; +} + + + +void ClothingActorImpl::attachVertexToGlobalPosition(uint32_t vertexIndex, const PxVec3& worldPosition) +{ + if (mClothingSimulation != NULL) + { + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* physicalMesh = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId); + const ClothingPhysicalMeshParametersNS::ConstrainCoefficient_Type* const PX_RESTRICT coeffs = physicalMesh->constrainCoefficients.buf; + PX_ASSERT((int32_t)vertexIndex < physicalMesh->constrainCoefficients.arraySizes[0]); + + const float linearScale = (mInternalMaxDistanceScale.Multipliable ? mInternalMaxDistanceScale.Scale : 1.0f) * mActorDesc->actorScale; + const float absoluteScale = mInternalMaxDistanceScale.Multipliable ? 0.0f : (physicalMesh->maximumMaxDistance * (1.0f - mInternalMaxDistanceScale.Scale)); + const float reduceMaxDistance = mMaxDistReduction + absoluteScale; + + const float maxDistance = PxMax(0.0f, coeffs[vertexIndex].maxDistance - reduceMaxDistance) * linearScale; + const PxVec3 skinnedPosition = mClothingSimulation->skinnedPhysicsPositions[vertexIndex]; + + PxVec3 restrictedWorldPosition = worldPosition; + if (bInternalLocalSpaceSim == 1) + { +#if _DEBUG + bool ok = true; + ok &= mInternalGlobalPose.column0.isNormalized(); + ok &= mInternalGlobalPose.column1.isNormalized(); + ok &= mInternalGlobalPose.column2.isNormalized(); + if (!ok) + { + APEX_DEBUG_WARNING("Internal Global Pose is not normalized (Scale: %f %f %f). attachVertexToGlobalPosition could be wrong.", mInternalGlobalPose.column0.magnitude(), mInternalGlobalPose.column1.magnitude(), mInternalGlobalPose.column2.magnitude()); + } +#endif + restrictedWorldPosition = mInternalGlobalPose.inverseRT().transform(restrictedWorldPosition); + } + + PxVec3 dir = restrictedWorldPosition - skinnedPosition; + if (dir.magnitude() > maxDistance) + { + dir.normalize(); + restrictedWorldPosition = skinnedPosition + dir * maxDistance; + } + + mClothingSimulation->attachVertexToGlobalPosition(vertexIndex, restrictedWorldPosition); + } +} + + + +void ClothingActorImpl::freeVertex(uint32_t vertexIndex) +{ + if (mClothingSimulation != NULL) + { + mClothingSimulation->freeVertex(vertexIndex); + } +} + + + +uint32_t ClothingActorImpl::getClothingMaterial() const +{ + const ClothingAssetParameters* clothingAsset = static_cast<const ClothingAssetParameters*>(mAsset->getAssetNvParameterized()); + ClothingMaterialLibraryParameters* materialLib = static_cast<ClothingMaterialLibraryParameters*>(clothingAsset->materialLibrary); + + PX_ASSERT(materialLib != NULL); + if (materialLib == NULL) + { + return 0; + } + + PX_ASSERT(materialLib->materials.buf != NULL); + PX_ASSERT(materialLib->materials.arraySizes[0] > 0); + + PX_ASSERT(mActorDesc->clothingMaterialIndex < (uint32_t)materialLib->materials.arraySizes[0]); + + return mActorDesc->clothingMaterialIndex; +} + + + +void ClothingActorImpl::setClothingMaterial(uint32_t index) +{ + mActorDesc->clothingMaterialIndex = index; +} + + + +void ClothingActorImpl::setOverrideMaterial(uint32_t submeshIndex, const char* overrideMaterialName) +{ + mOverrideMaterials[submeshIndex] = ApexSimpleString(overrideMaterialName); + + for (uint32_t i = 0; i < mGraphicalMeshes.size(); ++i) + { + ClothingRenderProxyImpl* renderProxy = mGraphicalMeshes[i].renderProxy; + if (renderProxy != NULL) + { + renderProxy->setOverrideMaterial(i, overrideMaterialName); + } + } +} + + + +void ClothingActorImpl::getLodRange(float& min, float& max, bool& intOnly) const +{ + min = 0.f; + max = 1.f; + intOnly = true; +} + + + +float ClothingActorImpl::getActiveLod() const +{ + return bIsSimulationOn ? 0.f : 1.f; +} + + + +void ClothingActorImpl::forceLod(float lod) +{ + if (lod < 0.0f) + { + mForceSimulation = -1; + } + else if (lod >= 1.f) + { + mForceSimulation = 1; + } + else + { + mForceSimulation = (int32_t)(lod + 0.5f); + } + + if (mClothingSimulation == NULL && mForceSimulation > 0) + { + mMaxDistReduction = mAsset->getBiggestMaxDistance(); + } +} + + + +void ClothingActorImpl::getPhysicalMeshPositions(void* buffer, uint32_t byteStride) +{ + if (isSimulationRunning()) + { + APEX_INTERNAL_ERROR("Cannot be called while the scene is running"); + return; + } + + PX_ASSERT(buffer != NULL); + if (byteStride == 0) + { + byteStride = sizeof(PxVec3); + } + + if (byteStride < sizeof(PxVec3)) + { + APEX_INTERNAL_ERROR("Bytestride is too small (%d, but must be >= %d)", byteStride, sizeof(PxVec3)); + return; + } + + uint32_t numSimulatedVertices = (mClothingSimulation == NULL) ? 0 : mClothingSimulation->sdkNumDeformableVertices; + PxStrideIterator<PxVec3> it((PxVec3*)buffer, byteStride); + for (uint32_t i = 0; i < numSimulatedVertices; i++, ++it) + { + *it = mClothingSimulation->sdkWritebackPosition[i]; + } + + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* pmesh = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId); + const PxVec3* skinPosePosition = pmesh->vertices.buf; + const PxMat44* matrices = mData.mInternalBoneMatricesCur; + + const uint8_t* const PX_RESTRICT optimizationData = pmesh->optimizationData.buf; + PX_ASSERT(optimizationData != NULL); + + if (matrices != NULL) + { + const uint16_t* boneIndices = pmesh->boneIndices.buf; + const float* boneWeights = pmesh->boneWeights.buf; + const uint32_t numBonesPerVertex = pmesh->numBonesPerVertex; + const uint32_t numVertices = pmesh->numVertices; + for (uint32_t vertexIndex = numSimulatedVertices; vertexIndex < numVertices; ++vertexIndex, ++it) + { + const uint8_t shift = 4 * (vertexIndex % 2); + const uint8_t numBones = uint8_t((optimizationData[vertexIndex / 2] >> shift) & 0x7); + + Simd4f temp = gSimd4fZero; + for (uint32_t j = 0; j < numBones; j++) + { + const Simd4f boneWeightV = Simd4fScalarFactory(boneWeights[vertexIndex * numBonesPerVertex + j]); + const uint32_t boneIndex = boneIndices[vertexIndex * numBonesPerVertex + j]; + const PxMat44& mat = (PxMat44&)(matrices[boneIndex]); + + Simd4f transformedPosV = applyAffineTransform(mat, createSimd3f(skinPosePosition[vertexIndex])); + transformedPosV = transformedPosV * boneWeightV; + temp = temp + transformedPosV; + } + store3(&(*it).x, temp); + } + } + else + { + const uint32_t numVertices = pmesh->numVertices; + const PxMat44& mat = (PxMat44&)(mInternalGlobalPose); + for (uint32_t vertexIndex = numSimulatedVertices; vertexIndex < numVertices; ++vertexIndex, ++it) + { + Simd4f transformedPosV = applyAffineTransform(mat, createSimd3f(skinPosePosition[vertexIndex])); + store3(&(*it).x, transformedPosV); + } + } +} + + + +void ClothingActorImpl::getPhysicalMeshNormals(void* buffer, uint32_t byteStride) +{ + if (isSimulationRunning()) + { + APEX_INTERNAL_ERROR("Cannot be called while the scene is running"); + return; + } + + PX_ASSERT(buffer != NULL); + if (byteStride == 0) + { + byteStride = sizeof(PxVec3); + } + + if (byteStride < sizeof(PxVec3)) + { + APEX_INTERNAL_ERROR("Bytestride is too small (%d, but must be >= %d)", byteStride, sizeof(PxVec3)); + return; + } + + if (mClothingSimulation == NULL) + { + APEX_INTERNAL_ERROR("No simulation data available"); + return; + } + + if (mClothingSimulation->sdkWritebackNormal == NULL) + { + APEX_INTERNAL_ERROR("No simulation normals for softbodies"); + return; + } + + PxStrideIterator<PxVec3> it((PxVec3*)buffer, byteStride); + for (uint32_t i = 0; i < mClothingSimulation->sdkNumDeformableVertices; i++, ++it) + { + *it = mClothingSimulation->sdkWritebackNormal[i]; + } + + + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* pmesh = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId); + const PxVec3* skinPoseNormal = pmesh->normals.buf; + const PxMat44* matrices = mData.mInternalBoneMatricesCur; + const uint32_t numVertices = pmesh->numVertices; + + const uint8_t* const PX_RESTRICT optimizationData = pmesh->optimizationData.buf; + PX_ASSERT(optimizationData != NULL); + + if (matrices != NULL) + { + const uint16_t* boneIndices = pmesh->boneIndices.buf; + const float* boneWeights = pmesh->boneWeights.buf; + const uint32_t numBonesPerVertex = pmesh->numBonesPerVertex; + for (uint32_t vertexIndex = mClothingSimulation->sdkNumDeformableVertices; vertexIndex < numVertices; ++vertexIndex, ++it) + { + const uint8_t shift = 4 * (vertexIndex % 2); + const uint8_t numBones = uint8_t((optimizationData[vertexIndex / 2] >> shift) & 0x7); + + Simd4f temp = gSimd4fZero; + for (uint32_t j = 0; j < numBones; j++) + { + const Simd4f boneWeightV = Simd4fScalarFactory(boneWeights[vertexIndex * numBonesPerVertex + j]); + const uint32_t boneIndex = boneIndices[vertexIndex * numBonesPerVertex + j]; + const PxMat44& mat = (PxMat44&)(matrices[boneIndex]); + + Simd4f transformedNormalV = applyAffineTransform(mat, createSimd3f(skinPoseNormal[vertexIndex])); + transformedNormalV = transformedNormalV * boneWeightV; + temp = temp + transformedNormalV; + } + store3(&(*it).x, temp); + } + } + else + { + const PxMat44& mat = (PxMat44&)(mInternalGlobalPose); + for (uint32_t vertexIndex = mClothingSimulation->sdkNumDeformableVertices; vertexIndex < numVertices; ++vertexIndex, ++it) + { + Simd4f transformedNormalV = applyLinearTransform(mat, createSimd3f(skinPoseNormal[vertexIndex])); + store3(&(*it).x, transformedNormalV); + } + } +} + + + +float ClothingActorImpl::getMaximumSimulationBudget() const +{ + uint32_t solverIterations = 5; + + ClothingMaterialLibraryParametersNS::ClothingMaterial_Type* clothingMaterial = getCurrentClothingMaterial(); + if (clothingMaterial != NULL) + { + solverIterations = clothingMaterial->solverIterations; + } + + return mAsset->getMaximumSimulationBudget(solverIterations); +} + + + +uint32_t ClothingActorImpl::getNumSimulationVertices() const +{ + uint32_t numVerts = 0; + if (mClothingSimulation != NULL) + { + numVerts = mClothingSimulation->sdkNumDeformableVertices; + } + return numVerts; +} + + + +const PxVec3* ClothingActorImpl::getSimulationPositions() +{ + if (mClothingSimulation == NULL) + return NULL; + + waitForFetchResults(); + + return mClothingSimulation->sdkWritebackPosition; +} + + + +const PxVec3* ClothingActorImpl::getSimulationNormals() +{ + if (mClothingSimulation == NULL) + return NULL; + + waitForFetchResults(); + + return mClothingSimulation->sdkWritebackNormal; +} + + + +bool ClothingActorImpl::getSimulationVelocities(PxVec3* velocities) +{ + if (mClothingSimulation == NULL) + return false; + + waitForFetchResults(); + + mClothingSimulation->getVelocities(velocities); + return true; +} + + + +uint32_t ClothingActorImpl::getNumGraphicalVerticesActive(uint32_t submeshIndex) const +{ + if (mClothingSimulation == NULL) + return 0; + + uint32_t numVertices = 0; + + const ClothingGraphicalLodParameters* graphicalLod = mAsset->getGraphicalLod(mCurrentGraphicalLodId); + + const uint32_t numParts = (uint32_t)graphicalLod->physicsMeshPartitioning.arraySizes[0]; + ClothingGraphicalLodParametersNS::PhysicsMeshPartitioning_Type* parts = graphicalLod->physicsMeshPartitioning.buf; + +#if PX_DEBUG || PX_CHECKED + bool found = false; +#endif + for (uint32_t c = 0; c < numParts; c++) + { + if (parts[c].graphicalSubmesh == submeshIndex) + { + numVertices = parts[c].numSimulatedVertices; +#if defined _DEBUG || PX_CHECKED + found = true; +#endif + break; + } + } +#if PX_DEBUG || PX_CHECKED + PX_ASSERT(found); +#endif + + return numVertices; +} + + + +PxMat44 ClothingActorImpl::getRenderGlobalPose() const +{ + return (bInternalLocalSpaceSim == 1) ? mInternalGlobalPose : PxMat44(PxIdentity); +} + + + +const PxMat44* ClothingActorImpl::getCurrentBoneSkinningMatrices() const +{ + return mData.mInternalBoneMatricesCur; +} + + +#if PX_PHYSICS_VERSION_MAJOR == 3 +void ClothingActorImpl::setPhysXScene(PxScene* physXscene) +{ + if (isSimulationRunning()) + { + APEX_INTERNAL_ERROR("Cannot change the physics scene while the simulation is running"); + return; + } + + if (mPhysXScene != NULL && mPhysXScene != physXscene) + { + removePhysX_LocksPhysX(); + } + + mPhysXScene = physXscene; + + if (mPhysXScene != NULL) + { + if (isCookedDataReady()) + { + createPhysX_LocksPhysX(0.0f); + } + } +} + +PxScene* ClothingActorImpl::getPhysXScene() const +{ + return mPhysXScene; +} +#endif + + +// this is 2.8.x only +void ClothingActorImpl::updateScaledGravity(float substepSize) +{ + if (mClothingScene != NULL && mClothingScene->mApexScene != NULL) + { + PxVec3 oldInternalScaledGravity = mInternalScaledGravity; + mInternalScaledGravity = mClothingScene->mApexScene->getGravity(); + + if (mActorDesc->allowAdaptiveTargetFrequency) + { + // disable adaptive frequency if the simulation doesn't require it + if (mClothingSimulation && !mClothingSimulation->needsAdaptiveTargetFrequency()) + { + substepSize = 0.0f; + } + + const float targetFrequency = mClothingScene->getAverageSimulationFrequency(); // will return 0 if the module is not set to compute it! + if (targetFrequency > 0.0f && substepSize > 0.0f) + { + // PH: This will scale the gravity to result in fixed velocity deltas (in Cloth/SoftBody) + const float targetScale = 1.0f / (targetFrequency * substepSize); + mInternalScaledGravity *= targetScale * targetScale; // need to square this to achieve constant behavior. + } + } + + bInternalScaledGravityChanged = (oldInternalScaledGravity != mInternalScaledGravity) ? 1u : 0u; + } +} + + + +void ClothingActorImpl::tickSynchBeforeSimulate_LocksPhysX(float simulationDelta, float substepSize, uint32_t substepNumber, uint32_t numSubSteps) +{ + PX_PROFILE_ZONE("ClothingActorImpl::beforeSimulate", GetInternalApexSDK()->getContextId()); + + // PH: simulationDelta can be 0 for subsequent substeps (substepNumber > 0 and numSubSteps > 1 + + if (mClothingSimulation != NULL && simulationDelta > 0.0f) + { + mClothingSimulation->verifyTimeStep(substepSize); + } + + if (substepNumber == 0) + { + PX_PROFILE_ZONE("ClothingActorImpl::updateStateInternal", GetInternalApexSDK()->getContextId()); + + updateStateInternal_NoPhysX(numSubSteps > 1); + updateScaledGravity(substepSize); // moved after updateStateInternal, cause it reads the localSpace state + freeze_LocksPhysX(bInternalFrozen == 1); + } + + /// interpolate matrices + if (numSubSteps > 1) + { + PX_PROFILE_ZONE("ClothingActorImpl::interpolateMatrices", GetInternalApexSDK()->getContextId()); + + const uint32_t numBones = mActorDesc->useInternalBoneOrder ? mAsset->getNumUsedBones() : mAsset->getNumBones(); + PX_ASSERT((numBones != 0) == (mInternalInterpolatedBoneMatrices != NULL)); + PX_ASSERT((numBones != 0) == (mData.mInternalBoneMatricesPrev != NULL)); + if (substepNumber == (numSubSteps - 1)) + { + bool matrixChanged = false; + for (uint32_t i = 0; i < numBones; i++) + { + mInternalInterpolatedBoneMatrices[i] = mData.mInternalBoneMatricesCur[i]; + matrixChanged |= mData.mInternalBoneMatricesCur[i] != mData.mInternalBoneMatricesPrev[i]; + } + + mInternalInterpolatedGlobalPose = mInternalGlobalPose; + bGlobalPoseChanged |= mOldInternalGlobalPose != mInternalGlobalPose ? 1 : 0; + bBoneMatricesChanged |= matrixChanged ? 1 : 0; + } + else + { + const float ratio = bInternalTeleportDue == ClothingTeleportMode::TeleportAndReset ? 0.0f : (1.0f - float(substepNumber + 1) / float(numSubSteps)); + + bool matrixChanged = false; + for (uint32_t i = 0; i < numBones; i++) + { + mInternalInterpolatedBoneMatrices[i] = interpolateMatrix(ratio, mData.mInternalBoneMatricesPrev[i], mData.mInternalBoneMatricesCur[i]); + matrixChanged |= mData.mInternalBoneMatricesCur[i] != mData.mInternalBoneMatricesPrev[i]; + } + mInternalInterpolatedGlobalPose = interpolateMatrix(ratio, mOldInternalGlobalPose, mInternalGlobalPose); + bGlobalPoseChanged |= mOldInternalGlobalPose != mInternalGlobalPose ? 1 : 0; + bBoneMatricesChanged |= matrixChanged ? 1 : 0; + } + } + + if (mClothingSimulation != NULL) + { + mClothingSimulation->setGlobalPose(substepNumber == 0 ? mInternalGlobalPose : mInternalInterpolatedGlobalPose); + } + + // PH: this is done before createPhysX mesh is called to make sure it's not executed on the freshly generated skeleton + // see ClothignAsset::createCollisionBulk for more info + { + PX_PROFILE_ZONE("ClothingActorImpl::updateCollision", GetInternalApexSDK()->getContextId()); + updateCollision_LocksPhysX(numSubSteps > 1); + } + + // PH: Done before skinPhysicsMesh to use the same buffers + if (mClothingSimulation != NULL && substepNumber == 0 && (bInternalFrozen == 0)) + { + PX_PROFILE_ZONE("ClothingActorImpl::applyVelocityChanges", GetInternalApexSDK()->getContextId()); + applyVelocityChanges_LocksPhysX(simulationDelta); + } + + if (substepNumber == 0) + { + lodTick_LocksPhysX(simulationDelta); + } + + if (mCurrentSolverIterations > 0) + { + PX_ASSERT(mClothingSimulation != NULL); // after lodTick there should be a simulation mesh + + applyTeleport(false, substepNumber); + + if (bDirtyClothingTemplate == 1) + { + bDirtyClothingTemplate = 0; + mClothingSimulation->applyClothingDesc(mActorDesc->clothDescTemplate); + } + } + + initializeActorData(); + if (mClothingSimulation != NULL) + { + skinPhysicsMesh(numSubSteps > 1, (float)(substepNumber + 1) / (float)numSubSteps); + + applyLockingTasks(); + + mClothingSimulation->setInterCollisionChannels(mInterCollisionChannels); + + mClothingSimulation->setHalfPrecisionOption(mIsAllowedHalfPrecisionSolver); + } +} + + + +void ClothingActorImpl::applyLockingTasks() +{ + if (mClothingSimulation != NULL) + { + // depends on nothing + applyClothingMaterial_LocksPhysX(); + + // depends on skinning + applyTeleport(true, 0); + + // depends on applyTeleport + applyGlobalPose_LocksPhysX(); + + // depends on skinning and applyTeleport + updateConstrainPositions_LocksPhysX(); + + // depends on lod tick (and maybe applyTeleport eventually) + applyCollision_LocksPhysX(); + } +} + + + +bool ClothingActorImpl::isSkinningDirty() +{ + return bBoneMatricesChanged == 1 || ((!bInternalLocalSpaceSim) == 1 && bGlobalPoseChanged == 1); +} + + + +void ClothingActorImpl::skinPhysicsMesh(bool useInterpolatedMatrices, float substepFraction) +{ + if (mClothingSimulation == NULL || mClothingSimulation->skinnedPhysicsPositions == NULL) + { + return; + } + + const bool skinningDirty = isSkinningDirty(); + if (skinningDirty) + { + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* physicalMesh = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId); + + if (physicalMesh->hasNegativeBackstop) + { + skinPhysicsMeshInternal<true>(useInterpolatedMatrices, substepFraction); + } + else + { + skinPhysicsMeshInternal<false>(useInterpolatedMatrices, substepFraction); + } + } +} + + + +void ClothingActorImpl::updateConstrainPositions_LocksPhysX() +{ + PX_PROFILE_ZONE("ClothingActorImpl::updateConstrainPositions", GetInternalApexSDK()->getContextId()); + + if (mClothingSimulation == NULL || mClothingSimulation->skinnedPhysicsPositions == NULL) + { + return; + } + + const bool skinningDirty = isSkinningDirty(); + mClothingSimulation->updateConstrainPositions(skinningDirty); + + bBoneMatricesChanged = 0; + bGlobalPoseChanged = 0; +} + + + +void ClothingActorImpl::applyCollision_LocksPhysX() +{ + PX_PROFILE_ZONE("ClothingActorImpl::applyCollision", GetInternalApexSDK()->getContextId()); + + if (mClothingSimulation != NULL) + { + mClothingSimulation->applyCollision(); + } +} + + + +ClothingMaterialLibraryParametersNS::ClothingMaterial_Type* ClothingActorImpl::getCurrentClothingMaterial() const +{ + const ClothingAssetParameters* clothingAsset = static_cast<const ClothingAssetParameters*>(mAsset->getAssetNvParameterized()); + const ClothingMaterialLibraryParameters* materialLib = static_cast<const ClothingMaterialLibraryParameters*>(clothingAsset->materialLibrary); + if (materialLib == NULL) + { + APEX_DEBUG_WARNING("No Clothing Material Library present in asset"); + return NULL; + } + + PX_ASSERT(materialLib->materials.buf != NULL); + PX_ASSERT(materialLib->materials.arraySizes[0] > 0); + + uint32_t index = mActorDesc->clothingMaterialIndex; + if (index >= (uint32_t)materialLib->materials.arraySizes[0]) + { + APEX_INVALID_PARAMETER("Index must be smaller than materials array: %d < %d", index, materialLib->materials.arraySizes[0]); + + PX_ASSERT(nvidia::strcmp(clothingAsset->className(), ClothingAssetParameters::staticClassName()) == 0); + index = clothingAsset->materialIndex; + mActorDesc->clothingMaterialIndex = index; + } + + return &materialLib->materials.buf[index]; +} + + +bool ClothingActorImpl::clothingMaterialsEqual(ClothingMaterialLibraryParametersNS::ClothingMaterial_Type& a, ClothingMaterialLibraryParametersNS::ClothingMaterial_Type& b) +{ + // update this compare function in case the struct has changed + // PH: Let's hope that we bump the version number when modifying the materials in the .pl + PX_COMPILE_TIME_ASSERT(ClothingMaterialLibraryParameters::ClassVersion == 14); + + return + a.verticalStretchingStiffness == b.verticalStretchingStiffness && + a.horizontalStretchingStiffness == b.horizontalStretchingStiffness && + a.bendingStiffness == b.bendingStiffness && + a.shearingStiffness == b.shearingStiffness && + a.tetherStiffness == b.tetherStiffness && + a.tetherLimit == b.tetherLimit && + a.orthoBending == b.orthoBending && + a.verticalStiffnessScaling.compressionRange == b.verticalStiffnessScaling.compressionRange && + a.verticalStiffnessScaling.stretchRange == b.verticalStiffnessScaling.stretchRange && + a.verticalStiffnessScaling.scale == b.verticalStiffnessScaling.scale && + a.horizontalStiffnessScaling.compressionRange == b.horizontalStiffnessScaling.compressionRange && + a.horizontalStiffnessScaling.stretchRange == b.horizontalStiffnessScaling.stretchRange && + a.horizontalStiffnessScaling.scale == b.horizontalStiffnessScaling.scale && + a.bendingStiffnessScaling.compressionRange == b.bendingStiffnessScaling.compressionRange && + a.bendingStiffnessScaling.stretchRange == b.bendingStiffnessScaling.stretchRange && + a.bendingStiffnessScaling.scale == b.bendingStiffnessScaling.scale && + a.shearingStiffnessScaling.compressionRange == b.shearingStiffnessScaling.compressionRange && + a.shearingStiffnessScaling.stretchRange == b.shearingStiffnessScaling.stretchRange && + a.shearingStiffnessScaling.scale == b.shearingStiffnessScaling.scale && + a.damping == b.damping && + a.stiffnessFrequency == b.stiffnessFrequency && + a.drag == b.drag && + a.comDamping == b.comDamping && + a.friction == b.friction && + a.massScale == b.massScale && + a.solverIterations == b.solverIterations && + a.solverFrequency == b.solverFrequency && + a.gravityScale == b.gravityScale && + a.inertiaScale == b.inertiaScale && + a.hardStretchLimitation == b.hardStretchLimitation && + a.maxDistanceBias == b.maxDistanceBias && + a.hierarchicalSolverIterations == b.hierarchicalSolverIterations && + a.selfcollisionThickness == b.selfcollisionThickness && + a.selfcollisionSquashScale == b.selfcollisionSquashScale && + a.selfcollisionStiffness == b.selfcollisionStiffness; +} + + + +void ClothingActorImpl::applyClothingMaterial_LocksPhysX() +{ + PX_PROFILE_ZONE("ClothingActorImpl::applyClothingMaterial", GetInternalApexSDK()->getContextId()); + + ClothingMaterialLibraryParametersNS::ClothingMaterial_Type* currentMaterial = getCurrentClothingMaterial(); + bool clothingMaterialDirty = !clothingMaterialsEqual(*currentMaterial, mClothingMaterial) || (bInternalScaledGravityChanged == 1); + + if (mClothingSimulation != NULL && clothingMaterialDirty) + { + if (mClothingSimulation->applyClothingMaterial(currentMaterial, mInternalScaledGravity)) + { + mClothingMaterial = *currentMaterial; + bInternalScaledGravityChanged = 0; + } + } +} + + + +void ClothingActorImpl::tickAsynch_NoPhysX() +{ + if (mClothingSimulation != NULL) + { + if (shouldComputeRenderData() /*&& bInternalFrozen == 0*/) + { + // perform mesh-to-mesh skinning if using skin cloth + if (mInternalFlags.ParallelCpuSkinning) + { + mData.skinToAnimation_NoPhysX(false); + } + } + } +} + + + +bool ClothingActorImpl::needsManualSubstepping() +{ + return mClothingSimulation != NULL && mClothingSimulation->needsManualSubstepping(); +} + + +#ifndef WITHOUT_PVD +void ClothingActorImpl::initPvdInstances(pvdsdk::PvdDataStream& pvdStream) +{ + ApexResourceInterface* pvdInstance = static_cast<ApexResourceInterface*>(mActorProxy); + + // Actor Params + pvdStream.createInstance(pvdsdk::NamespacedName(APEX_PVD_NAMESPACE, "ClothingActorParam"), mActorDesc); + pvdStream.setPropertyValue(pvdInstance, "ActorParams", pvdsdk::DataRef<const uint8_t>((const uint8_t*)&mActorDesc, sizeof(ClothingActorParam*)), pvdsdk::getPvdNamespacedNameForType<pvdsdk::ObjectRef>()); + + pvdsdk::ApexPvdClient* client = GetInternalApexSDK()->getApexPvdClient(); + PX_ASSERT(client != NULL); + client->updatePvd(mActorDesc, *mActorDesc); +} + + +void ClothingActorImpl::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(mActorDesc, *mActorDesc, pvdsdk::PvdAction::DESTROY); + pvdStream->destroyInstance(mActorDesc); + // the actor instance is destroyed in ResourceList::remove + } + } + } + } +} + + +void ClothingActorImpl::updatePvd() +{ + // update pvd + pvdsdk::ApexPvdClient* client = GetInternalApexSDK()->getApexPvdClient(); + if (client != NULL) + { + if (client->isConnected() && client->getPxPvd().getInstrumentationFlags() & PxPvdInstrumentationFlag::eDEBUG) + { + pvdsdk::PvdDataStream* pvdStream = client->getDataStream(); + pvdsdk::PvdUserRenderer* pvdRenderer = client->getUserRender(); + + if (pvdStream != NULL && pvdRenderer != NULL) + { + ApexResourceInterface* pvdInstance = static_cast<ApexResourceInterface*>(mActorProxy); + + client->updatePvd(mActorDesc, *mActorDesc); + + if (mClothingSimulation) + { + mClothingSimulation->updatePvd(*pvdStream, *pvdRenderer, pvdInstance, bInternalLocalSpaceSim == 1); + } + } + } + } +} +#endif + + +void ClothingActorImpl::visualize() +{ +#ifdef WITHOUT_DEBUG_VISUALIZE +#else + + using RENDER_DEBUG::DebugColors; + using RENDER_DEBUG::DebugRenderState; + + if (mClothingScene == NULL || mClothingScene->mRenderDebug == NULL) + { + return; + } + if ( !mEnableDebugVisualization ) return; + + RenderDebugInterface& renderDebug = *mClothingScene->mRenderDebug; + const physx::PxMat44& savedPose = *RENDER_DEBUG_IFACE(&renderDebug)->getPoseTyped(); + RENDER_DEBUG_IFACE(&renderDebug)->setIdentityPose(); + + const float visualizationScale = mClothingScene->mDebugRenderParams->Scale; + + PX_ASSERT(mActorDesc != NULL); + + if (visualizationScale == 0.0f || !mActorDesc->flags.Visualize) + { + return; + } + + if (mClothingScene->mClothingDebugRenderParams->GlobalPose) + { +#if 1 + // PH: This uses only lines, not triangles, hence wider engine support + 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); + PxMat44 absPose(mInternalGlobalPose); + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorRed); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(absPose.getPosition(), absPose.getPosition() + absPose.column0.getXYZ() * visualizationScale); + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorGreen); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(absPose.getPosition(), absPose.getPosition() + absPose.column1.getXYZ() * visualizationScale); + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorBlue); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(absPose.getPosition(), absPose.getPosition() + absPose.column2.getXYZ() * visualizationScale); +#else + // PH: But this one looks a bit nicer + RENDER_DEBUG_IFACE(&renderDebug)->debugAxes(mInternalGlobalPose, visualizationScale); +#endif + } + + // transform debug rendering to global space + if (bInternalLocalSpaceSim == 1 && !mClothingScene->mClothingDebugRenderParams->ShowInLocalSpace) + { + RENDER_DEBUG_IFACE(&renderDebug)->setPose(mInternalGlobalPose); + } + + const uint32_t colorRed = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Red); + const uint32_t colorBlue = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Blue); + const uint32_t colorWhite = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::White); + + renderDataLock(); + +#if 0 + static bool turnOn = true; + if (turnOn) + { + RENDER_DEBUG_IFACE(&renderDebug)->pushRenderState(); + + RenderMeshAssetIntl* rma = mAsset->getGraphicalMesh(mCurrentGraphicalLodId); + if (false && mActorDesc->morphDisplacements.arraySizes[0] > 0 && rma != NULL) + { + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorBlue); + + uint32_t* morphMap = mAsset->getMorphMapping(mCurrentGraphicalLodId); + + 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 = 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++) + { + PxVec3 from = positions[v]; + PX_ASSERT((int)morphMap[v] < mActorDesc->morphDisplacements.arraySizes[0]); + PxVec3 to = from + mActorDesc->morphDisplacements.buf[morphMap[v]]; + + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(from, to); + } + } + } + + } + + if (mActorDesc->morphPhysicalMeshNewPositions.buf != NULL) + { + mClothingScene-> mRENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorRed); + + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* physicalMesh = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId); + uint32_t offset = mAsset->getPhysicalMeshOffset(mAsset->getPhysicalMeshID(mCurrentGraphicalLodId)); + PX_ASSERT((int32_t)(offset + physicalMesh->numVertices) <= mActorDesc->morphPhysicalMeshNewPositions.arraySizes[0]); + for (uint32_t p = 0; p < physicalMesh->numVertices; p++) + { + const PxVec3 renderDisp(0.0f, 0.0f, 0.001f); + PxVec3 from = physicalMesh->vertices.buf[p] + renderDisp; + PxVec3 to = mActorDesc->morphPhysicalMeshNewPositions.buf[offset + p] + renderDisp; + + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(from, to); + } + } + + RENDER_DEBUG_IFACE(&renderDebug)->popRenderState(); + } +#endif + + // save the rendering state. + RENDER_DEBUG_IFACE(&renderDebug)->pushRenderState(); + + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorRed); + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentState(DebugRenderState::SolidShaded); + + + // visualize velocities + const float velocityScale = mClothingScene->mClothingDebugRenderParams->Velocities * visualizationScale; + if (velocityScale > 0.0f && mClothingSimulation != NULL) + { + PxVec3* velocities = (PxVec3*)GetInternalApexSDK()->getTempMemory(sizeof(PxVec3) * mClothingSimulation->sdkNumDeformableVertices); + if (velocities != NULL) + { + mClothingSimulation->getVelocities(velocities); + + const bool useVelocityClamp = mActorDesc->useVelocityClamping; + PxBounds3 velocityClamp(mActorDesc->vertexVelocityClamp); + + for (uint32_t i = 0; i < mClothingSimulation->sdkNumDeformableVertices; i++) + { + const PxVec3 pos = mClothingSimulation->sdkWritebackPosition[i]; + const PxVec3 vel = velocities[i]; + bool clamped = false; + if (useVelocityClamp) + { + clamped = (vel.x < velocityClamp.minimum.x) || (vel.x > velocityClamp.maximum.x); + clamped |= (vel.y < velocityClamp.minimum.y) || (vel.y > velocityClamp.maximum.y); + clamped |= (vel.z < velocityClamp.minimum.z) || (vel.z > velocityClamp.maximum.z); + } + const PxVec3 dest = pos + vel * velocityScale; + RENDER_DEBUG_IFACE(&renderDebug)->debugGradientLine(pos, dest, clamped ? colorRed : colorBlue, colorWhite); + } + + GetInternalApexSDK()->releaseTempMemory(velocities); + } + } + + // visualize Skeleton + const bool skeleton = mClothingScene->mClothingDebugRenderParams->Skeleton; + const float boneFramesScale = mClothingScene->mClothingDebugRenderParams->BoneFrames; + const float boneNamesScale = mClothingScene->mClothingDebugRenderParams->BoneNames; + if (skeleton || boneFramesScale + boneNamesScale > 0.0f) + { + const PxMat44* matrices = mData.mInternalBoneMatricesCur; + + if (matrices != NULL) + { + mAsset->visualizeBones(renderDebug, matrices, skeleton, boneFramesScale, boneNamesScale); + } + } + + // visualization of the physical mesh + if (mClothingScene->mClothingDebugRenderParams->Backstop) + { + visualizeBackstop(renderDebug); + } + + const float backstopPrecise = mClothingScene->mClothingDebugRenderParams->BackstopPrecise; + if (backstopPrecise > 0.0f) + { + visualizeBackstopPrecise(renderDebug, backstopPrecise); + } + + const float skinnedPositionsScale = mClothingScene->mClothingDebugRenderParams->SkinnedPositions; + const bool drawMaxDistance = mClothingScene->mClothingDebugRenderParams->MaxDistance; + const bool drawMaxDistanceIn = mClothingScene->mClothingDebugRenderParams->MaxDistanceInwards; + if (skinnedPositionsScale > 0.0f || drawMaxDistance || drawMaxDistanceIn) + { + visualizeSkinnedPositions(renderDebug, skinnedPositionsScale, drawMaxDistance, drawMaxDistanceIn); + } + + // visualize vertex - bone connections for skinning + for (uint32_t g = 0; g < mGraphicalMeshes.size(); g++) + { + if (!mGraphicalMeshes[g].active) + { + continue; + } + + //const ClothingGraphicalLodParameters* graphicalLod = mAsset->getGraphicalLod(g); + ClothingGraphicalMeshAssetWrapper meshAsset(mAsset->getRenderMeshAsset(g)); + + const float graphicalVertexBonesScale = mClothingScene->mClothingDebugRenderParams->GraphicalVertexBones; + if (graphicalVertexBonesScale > 0.0f) + { + for (uint32_t submeshIndex = 0; submeshIndex < meshAsset.getSubmeshCount(); submeshIndex++) + { + RenderDataFormat::Enum outFormat; + const PxVec3* positions = (const PxVec3*)meshAsset.getVertexBuffer(submeshIndex, RenderVertexSemantic::POSITION, outFormat); + PX_ASSERT(outFormat == RenderDataFormat::FLOAT3); + const uint16_t* boneIndices = (const uint16_t*)meshAsset.getVertexBuffer(submeshIndex, RenderVertexSemantic::BONE_INDEX, outFormat); + const float* boneWeights = (const float*)meshAsset.getVertexBuffer(submeshIndex, RenderVertexSemantic::BONE_WEIGHT, outFormat); + visualizeBoneConnections(renderDebug, positions, boneIndices, boneWeights, + meshAsset.getNumBonesPerVertex(submeshIndex), meshAsset.getNumVertices(submeshIndex)); + } + } + + const float physicalVertexBonesScale = mClothingScene->mClothingDebugRenderParams->PhysicalVertexBones; + if (physicalVertexBonesScale > 0.0f) + { + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* physicalMesh = mAsset->getPhysicalMeshFromLod(g); + visualizeBoneConnections(renderDebug, physicalMesh->vertices.buf, physicalMesh->boneIndices.buf, + physicalMesh->boneWeights.buf, physicalMesh->numBonesPerVertex, physicalMesh->numVertices); + } + } + + if (mClothingSimulation != NULL) + { + // visualization of the physical mesh + const float physicsMeshWireScale = mClothingScene->mClothingDebugRenderParams->PhysicsMeshWire; + const float physicsMeshSolidScale = mClothingScene->mClothingDebugRenderParams->PhysicsMeshSolid; + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* physicalMesh = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId); + + const float physicsMeshNormalScale = mClothingScene->mClothingDebugRenderParams->PhysicsMeshNormals; + if (physicsMeshNormalScale > 0.0f) + { + const PxVec3* positions = mClothingSimulation->sdkWritebackPosition; + const PxVec3* normals = mClothingSimulation->sdkWritebackNormal; + + RENDER_DEBUG_IFACE(&renderDebug)->pushRenderState(); + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Blue)); + + const uint32_t numVertices = mClothingSimulation->sdkNumDeformableVertices; + for (uint32_t i = 0; i < numVertices; i++) + { + const PxVec3 dest = positions[i] + normals[i] * physicsMeshNormalScale; + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(positions[i], dest); + } + + RENDER_DEBUG_IFACE(&renderDebug)->popRenderState(); + } + + if (mClothingScene->mClothingDebugRenderParams->PhysicsMeshIndices) + { + RENDER_DEBUG_IFACE(&renderDebug)->pushRenderState(); + const PxVec3 upAxis = -mInternalScaledGravity.getNormalized(); + const float avgEdgeLength = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId)->averageEdgeLength; + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentTextScale(avgEdgeLength); + RENDER_DEBUG_IFACE(&renderDebug)->addToCurrentState(RENDER_DEBUG::DebugRenderState::CameraFacing); + + const uint32_t numVertices = mClothingSimulation->sdkNumDeformableVertices; + const PxVec3* const positions = mClothingSimulation->sdkWritebackPosition; + const PxVec3* const normals = mClothingSimulation->sdkWritebackNormal; + for (uint32_t i = 0; i < numVertices; ++i) + { + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(0xFFFFFFFF); + const PxVec3 pos = positions[i]; + const PxVec3 normal = normals[i].getNormalized(); + PxVec3 rightAxis = upAxis.cross(normal).getNormalized(); + PxVec3 realUpAxis = normal.cross(rightAxis).getNormalized(); + + RENDER_DEBUG_IFACE(&renderDebug)->debugText(pos + normal * (avgEdgeLength * 0.1f), "%d", i); + } + RENDER_DEBUG_IFACE(&renderDebug)->popRenderState(); + } + + if (physicsMeshWireScale > 0.0f || physicsMeshSolidScale > 0.0f) + { + RENDER_DEBUG_IFACE(&renderDebug)->pushRenderState(); + RENDER_DEBUG_IFACE(&renderDebug)->setIdentityPose(); + const float actorScale = mActorDesc->actorScale; + + const float linearScale = (mInternalMaxDistanceScale.Multipliable ? mInternalMaxDistanceScale.Scale : 1.0f) * actorScale; + const float absoluteScale = mInternalMaxDistanceScale.Multipliable ? 0.0f : (physicalMesh->maximumMaxDistance * (1.0f - mInternalMaxDistanceScale.Scale)); + + const float reduceMaxDistance = mMaxDistReduction + absoluteScale; + + const uint32_t numIndices = mClothingSimulation->sdkNumDeformableIndices; + const uint32_t* const indices = physicalMesh->indices.buf; + const PxVec3* const positions = mClothingSimulation->sdkWritebackPosition; + const PxVec3* const skinnedPositions = mClothingSimulation->skinnedPhysicsPositions; + + const ClothingPhysicalMeshParametersNS::ConstrainCoefficient_Type* const PX_RESTRICT coeffs = physicalMesh->constrainCoefficients.buf; + + RENDER_DEBUG_IFACE(&renderDebug)->removeFromCurrentState(DebugRenderState::SolidWireShaded); + + union BlendColors + { + unsigned char chars[4]; + uint32_t color; + } blendColors[3]; + + blendColors[0].color = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Yellow); + blendColors[1].color = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Gold); + blendColors[2].color = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Red); + const uint32_t lightBlue = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::LightBlue); + const uint32_t darkBlue = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Blue); + + if (bInternalFrozen) + { + blendColors[0].color = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Purple); + blendColors[1].color = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::DarkPurple); + } + else if (bInternalTeleportDue == ClothingTeleportMode::TeleportAndReset) + { + blendColors[0].color = darkBlue; + blendColors[1].color = darkBlue; + blendColors[2].color = darkBlue; + } + else if (bInternalTeleportDue == ClothingTeleportMode::Teleport) + { + blendColors[0].color = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Red); + blendColors[1].color = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Red); + blendColors[2].color = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Red); + } + + const uint8_t sides[4][3] = {{2, 1, 0}, {0, 1, 3}, {1, 2, 3}, {2, 0, 3}}; + + const uint32_t numIndicesPerPrim = physicalMesh->isTetrahedralMesh ? 4u : 3u; + const float numindicesPerPrimF = (float)numIndicesPerPrim; + + for (uint32_t i = 0; i < numIndices; i += numIndicesPerPrim) + { + PxVec3 vecs[8]; + PxVec3 center(0.0f, 0.0f, 0.0f); + uint32_t colors[8]; + for (uint32_t j = 0; j < numIndicesPerPrim; j++) + { + const uint32_t index = indices[i + j]; + vecs[j] = positions[index]; + center += vecs[j]; + const float maxDistance = (coeffs[index].maxDistance - reduceMaxDistance) * linearScale; + if (maxDistance <= 0.0f) + { + colors[j ] = lightBlue; + colors[j + numIndicesPerPrim] = darkBlue; + } + else + { + const float distance = (positions[index] - skinnedPositions[index]).magnitude() / maxDistance; + + union + { + unsigned char tempChars[8]; + uint32_t tempColor[2]; + }; + + if (distance > 1.0f) + { + colors[j ] = blendColors[2].color; + colors[j + numIndicesPerPrim] = blendColors[2].color; + } + else + { + for (uint32_t k = 0; k < 4; k++) + { + tempChars[k ] = (unsigned char)(blendColors[2].chars[k] * distance + blendColors[0].chars[k] * (1.0f - distance)); + tempChars[k + 4] = (unsigned char)(blendColors[2].chars[k] * distance + blendColors[1].chars[k] * (1.0f - distance)); + } + colors[j ] = tempColor[0]; + colors[j + numIndicesPerPrim] = tempColor[1]; + } + } + } + + center /= numindicesPerPrimF; + + for (uint32_t j = 0; j < numIndicesPerPrim; j++) + { + vecs[j + numIndicesPerPrim] = vecs[j] * physicsMeshSolidScale + center * (1.0f - physicsMeshSolidScale); + vecs[j] = vecs[j] * physicsMeshWireScale + center * (1.0f - physicsMeshWireScale); + } + + if (physicsMeshWireScale > 0.0f) + { + RENDER_DEBUG_IFACE(&renderDebug)->removeFromCurrentState(DebugRenderState::SolidShaded); + + if (numIndicesPerPrim == 3) + { + RENDER_DEBUG_IFACE(&renderDebug)->debugGradientLine(vecs[0], vecs[1], colors[0], colors[1]); + RENDER_DEBUG_IFACE(&renderDebug)->debugGradientLine(vecs[1], vecs[2], colors[1], colors[2]); + RENDER_DEBUG_IFACE(&renderDebug)->debugGradientLine(vecs[2], vecs[0], colors[2], colors[0]); + } + else + { + for (uint32_t j = 0; j < 4; j++) + { + uint32_t triIndices[3] = { sides[j][0], sides[j][1], sides[j][2] }; + RENDER_DEBUG_IFACE(&renderDebug)->debugGradientLine(vecs[triIndices[0]], vecs[triIndices[2]], colors[triIndices[0]], colors[triIndices[2]]); + RENDER_DEBUG_IFACE(&renderDebug)->debugGradientLine(vecs[triIndices[2]], vecs[triIndices[1]], colors[triIndices[2]], colors[triIndices[1]]); + RENDER_DEBUG_IFACE(&renderDebug)->debugGradientLine(vecs[triIndices[1]], vecs[triIndices[0]], colors[triIndices[1]], colors[triIndices[0]]); + } + } + } + if (physicsMeshSolidScale > 0.0f) + { + RENDER_DEBUG_IFACE(&renderDebug)->addToCurrentState(DebugRenderState::SolidShaded); + + if (numIndicesPerPrim == 3) + { + // culling is active for these, so we need both of them + RENDER_DEBUG_IFACE(&renderDebug)->debugGradientTri(vecs[3], vecs[4], vecs[5], colors[3], colors[4], colors[5]); + RENDER_DEBUG_IFACE(&renderDebug)->debugGradientTri(vecs[3], vecs[5], vecs[4], colors[3], colors[5], colors[4]); + } + else + { + for (uint32_t j = 0; j < 4; j++) + { + uint32_t triIndices[3] = { (uint32_t)sides[j][0] + 4, (uint32_t)sides[j][1] + 4, (uint32_t)sides[j][2] + 4 }; + RENDER_DEBUG_IFACE(&renderDebug)->debugGradientTri( + vecs[triIndices[0]], vecs[triIndices[2]], vecs[triIndices[1]], + colors[triIndices[0]], colors[triIndices[2]], colors[triIndices[1]]); + } + } + } + } + RENDER_DEBUG_IFACE(&renderDebug)->popRenderState(); + } + + // self collision visualization + if ( (mClothingScene->mClothingDebugRenderParams->SelfCollision || mClothingScene->mClothingDebugRenderParams->SelfCollisionWire) + && (!mAsset->getModuleClothing()->useSparseSelfCollision() || mClothingSimulation->getType() != SimulationType::CLOTH3x)) + { + const PxVec3* const positions = mClothingSimulation->sdkWritebackPosition; + ClothingMaterialLibraryParametersNS::ClothingMaterial_Type* material = getCurrentClothingMaterial(); + + if (material->selfcollisionThickness > 0.0f && material->selfcollisionStiffness > 0.0f) + { + visualizeSpheres(renderDebug, positions, mClothingSimulation->sdkNumDeformableVertices, 0.5f*material->selfcollisionThickness*mActorDesc->actorScale, colorRed, mClothingScene->mClothingDebugRenderParams->SelfCollisionWire); + } + } + /* + // inter collision visualization + if ( (mClothingScene->mClothingDebugRenderParams->InterCollision || mClothingScene->mClothingDebugRenderParams->InterCollisionWire) + && mClothingSimulation->getType() == SimulationType::CLOTH3x) + { + const PxVec3* const positions = mClothingSimulation->sdkWritebackPosition; + float distance = mAsset->getModuleClothing()->getInterCollisionDistance(); + float stiffness = mAsset->getModuleClothing()->getInterCollisionStiffness(); + if (distance > 0.0f && stiffness > 0.0f) + { + visualizeSpheres(renderDebug, positions, mClothingSimulation->sdkNumDeformableVertices, 0.5f*distance, colorBlue, mClothingScene->mClothingDebugRenderParams->InterCollisionWire); + } + } + */ + // visualization of the graphical mesh + for (uint32_t g = 0; g < mGraphicalMeshes.size(); g++) + { + if (!mGraphicalMeshes[g].active) + { + continue; + } + + const ClothingGraphicalLodParameters* graphicalLod = mAsset->getGraphicalLod(g); + + if (graphicalLod != NULL && (graphicalLod->skinClothMapB.buf != NULL || graphicalLod->skinClothMap.buf != NULL)) + { + AbstractMeshDescription pcm; + pcm.numVertices = mClothingSimulation->sdkNumDeformableVertices; + pcm.numIndices = mClothingSimulation->sdkNumDeformableIndices; + pcm.pPosition = mClothingSimulation->sdkWritebackPosition; + pcm.pNormal = mClothingSimulation->sdkWritebackNormal; + pcm.pIndices = physicalMesh->indices.buf; + pcm.avgEdgeLength = graphicalLod->skinClothMapThickness; + + if (mClothingScene->mClothingDebugRenderParams->SkinMapAll) + { + mAsset->visualizeSkinCloth(renderDebug, pcm, graphicalLod->skinClothMapB.buf != NULL, mActorDesc->actorScale); + } + if (mClothingScene->mClothingDebugRenderParams->SkinMapBad) + { + mAsset->visualizeSkinClothMap(renderDebug, pcm, + graphicalLod->skinClothMapB.buf, (uint32_t)graphicalLod->skinClothMapB.arraySizes[0], + graphicalLod->skinClothMap.buf, (uint32_t)graphicalLod->skinClothMap.arraySizes[0], mActorDesc->actorScale, true, false); + } + if (mClothingScene->mClothingDebugRenderParams->SkinMapActual) + { + mAsset->visualizeSkinClothMap(renderDebug, pcm, + graphicalLod->skinClothMapB.buf, (uint32_t)graphicalLod->skinClothMapB.arraySizes[0], + graphicalLod->skinClothMap.buf, (uint32_t)graphicalLod->skinClothMap.arraySizes[0], mActorDesc->actorScale, false, false); + } + if (mClothingScene->mClothingDebugRenderParams->SkinMapInvalidBary) + { + mAsset->visualizeSkinClothMap(renderDebug, pcm, + graphicalLod->skinClothMapB.buf, (uint32_t)graphicalLod->skinClothMapB.arraySizes[0], + graphicalLod->skinClothMap.buf, (uint32_t)graphicalLod->skinClothMap.arraySizes[0], mActorDesc->actorScale, false, true); + } + + } + + RENDER_DEBUG_IFACE(&renderDebug)->pushRenderState(); + + RENDER_DEBUG_IFACE(&renderDebug)->addToCurrentState(RENDER_DEBUG::DebugRenderState::CenterText); + RENDER_DEBUG_IFACE(&renderDebug)->addToCurrentState(RENDER_DEBUG::DebugRenderState::CameraFacing); + + // Probably should be removed + if (graphicalLod != NULL && mClothingScene->mClothingDebugRenderParams->RecomputeSubmeshes) + { + const RenderMeshAsset* rma = mAsset->getRenderMeshAsset(mCurrentGraphicalLodId); + + RENDER_DEBUG_IFACE(&renderDebug)->addToCurrentState(DebugRenderState::SolidShaded); + + uint32_t submeshVertexOffset = 0; + for (uint32_t s = 0; s < rma->getSubmeshCount(); s++) + { + const RenderSubmesh& submesh = rma->getSubmesh(s); + const uint32_t vertexCount = submesh.getVertexCount(0); + const uint32_t* indices = submesh.getIndexBuffer(0); + const uint32_t numIndices = submesh.getIndexCount(0); + + if(mGraphicalMeshes[mCurrentGraphicalLodId].renderProxy == NULL) + continue; + const PxVec3* positions = mGraphicalMeshes[mCurrentGraphicalLodId].renderProxy->renderingDataPosition + submeshVertexOffset; + + + uint32_t maxForColor = (uint32_t)graphicalLod->physicsMeshPartitioning.arraySizes[0] + 2; + { + const uint8_t colorPart = (uint8_t)((255 / maxForColor) & 0xff); + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(uint32_t(colorPart | colorPart << 8 | colorPart << 16)); + } + + for (uint32_t i = 0; i < numIndices; i += 3) + { + RENDER_DEBUG_IFACE(&renderDebug)->debugTri(positions[indices[i]], positions[indices[i + 1]], positions[indices[i + 2]]); + } + const uint8_t colorPart = (uint8_t)((2 * 255 / maxForColor) & 0xff); + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(uint32_t(colorPart | colorPart << 8 | colorPart << 16)); + + submeshVertexOffset += vertexCount; + } + } + if (graphicalLod != NULL && mClothingScene->mClothingDebugRenderParams->RecomputeVertices) + { + uint32_t color1 = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Orange); + uint32_t color2 = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Purple); + + const PxVec3 upAxis = -mInternalScaledGravity.getNormalized(); + PX_UNUSED(upAxis); + + const float avgEdgeLength = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId)->averageEdgeLength; + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentTextScale(0.2f * avgEdgeLength); + + const RenderMeshAsset* rma = mAsset->getRenderMeshAsset(mCurrentGraphicalLodId); + + uint32_t submeshVertexOffset = 0; + for (uint32_t s = 0; s < rma->getSubmeshCount(); s++) + { + const RenderSubmesh& submesh = rma->getSubmesh(s); + const uint32_t vertexCount = submesh.getVertexCount(0); + + ClothingRenderProxyImpl* renderProxy = mGraphicalMeshes[mCurrentGraphicalLodId].renderProxy; + + if (renderProxy == NULL) + { + renderProxy = mRenderProxyReady; + } + + if (renderProxy == NULL) + { + renderProxy = mRenderProxyURR; + } + + if(renderProxy == NULL) + continue; + + const PxVec3* positions = renderProxy->renderingDataPosition + submeshVertexOffset; + + uint32_t simulatedVertices = graphicalLod->physicsMeshPartitioning.buf[s].numSimulatedVertices; + uint32_t simulatedVerticesAdditional = graphicalLod->physicsMeshPartitioning.buf[s].numSimulatedVerticesAdditional; + + for (uint32_t i = 0; i < simulatedVerticesAdditional && i < vertexCount; i++) + { + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(i < simulatedVertices ? color1 : color2); + const PxVec3 pos = positions[i]; + + RENDER_DEBUG_IFACE(&renderDebug)->debugText(pos, "%d", submeshVertexOffset + i); + } + + submeshVertexOffset += vertexCount; + } + } + + RENDER_DEBUG_IFACE(&renderDebug)->popRenderState(); + } + } + + if (mClothingSimulation != NULL) + { + mClothingSimulation->visualize(renderDebug, *mClothingScene->mClothingDebugRenderParams); + } + + // restore the rendering state. + RENDER_DEBUG_IFACE(&renderDebug)->popRenderState(); + + const float windScale = mClothingScene->mClothingDebugRenderParams->Wind; + if (mClothingSimulation != NULL && windScale != 0.0f) + { + //PX_ASSERT(mWindDebugRendering.size() == mClothingSimulation->sdkNumDeformableVertices); + const uint32_t numVertices = PxMin(mClothingSimulation->sdkNumDeformableVertices, mWindDebugRendering.size()); + const PxVec3* positions = mClothingSimulation->sdkWritebackPosition; + const uint32_t red = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Red); + const uint32_t white = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::White); + for (uint32_t i = 0; i < numVertices; i++) + { + RENDER_DEBUG_IFACE(&renderDebug)->debugGradientLine(positions[i], positions[i] + mWindDebugRendering[i] * windScale, red, white); + } + } + + // fetchresults must be completed before Normal/Tangent debug rendering + waitForFetchResults(); + + // render mesh actor debug rendering + for (uint32_t i = 0; i < mGraphicalMeshes.size(); i++) + { + if (mGraphicalMeshes[i].active && mGraphicalMeshes[i].renderProxy != NULL) + { + mGraphicalMeshes[i].renderProxy->getRenderMeshActor()->visualize(renderDebug, mClothingScene->mDebugRenderParams); + } + } + if (mRenderProxyReady != NULL) + { + mRenderProxyReady->getRenderMeshActor()->visualize(renderDebug, mClothingScene->mDebugRenderParams); + } + else if (mRenderProxyURR != NULL) + { + mRenderProxyURR->getRenderMeshActor()->visualize(renderDebug, mClothingScene->mDebugRenderParams); + } + + // transform debug rendering to global space + if (bInternalLocalSpaceSim == 1) + { + RENDER_DEBUG_IFACE(&renderDebug)->setPose(PxMat44(PxIdentity)); + } + + + if (mClothingScene->mClothingDebugRenderParams->Wind != 0.0f && mActorDesc->windParams.Adaption > 0.0f) + { + const PxVec3 center = mRenderBounds.getCenter(); + const float radius = mRenderBounds.getExtents().magnitude() * 0.02f; + RENDER_DEBUG_IFACE(&renderDebug)->debugThickRay(center, center + mActorDesc->windParams.Velocity * mClothingScene->mClothingDebugRenderParams->Wind, radius); + } + + if (mClothingScene->mClothingDebugRenderParams->SolverMode) + { + RENDER_DEBUG_IFACE(&renderDebug)->pushRenderState(); + RENDER_DEBUG_IFACE(&renderDebug)->addToCurrentState(RENDER_DEBUG::DebugRenderState::CenterText); + RENDER_DEBUG_IFACE(&renderDebug)->addToCurrentState(RENDER_DEBUG::DebugRenderState::CameraFacing); + + ApexSimpleString solverString; + + if (mClothingSimulation != NULL) + { + solverString = (mClothingSimulation->getType() == SimulationType::CLOTH3x) ? "3.x" : "2.x"; +#if APEX_CUDA_SUPPORT + ApexSimpleString gpu(mClothingSimulation->isGpuSim() ? " GPU" : " CPU"); + + if (mClothingSimulation->getGpuSimMemType() == GpuSimMemType::GLOBAL) + { + gpu += ApexSimpleString(", Global"); + } + else if(mClothingSimulation->getGpuSimMemType() == GpuSimMemType::MIXED) + { + gpu += ApexSimpleString(", Mixed"); + } + else if (mClothingSimulation->getGpuSimMemType() == GpuSimMemType::SHARED) + { + gpu += ApexSimpleString(", Shared"); + } + + solverString += gpu; +#endif + solverString += ApexSimpleString(", "); + ApexSimpleString solverCount; + ApexSimpleString::itoa(mClothingSimulation->getNumSolverIterations(), solverCount); + solverString += solverCount; + } + else + { + solverString = "Disabled"; + } + + PxVec3 up(0.0f, 1.0f, 0.0f); + up = mData.mInternalGlobalPose.transform(up) - mClothingScene->getApexScene()->getGravity(); + up.normalize(); + + const uint32_t white = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::White); + const uint32_t gray = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::DarkGray); + up = mRenderBounds.getDimensions().multiply(up) * 1.1f; + const PxVec3 center = mRenderBounds.getCenter(); + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentTextScale(mRenderBounds.getDimensions().magnitude()); + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(gray); + RENDER_DEBUG_IFACE(&renderDebug)->debugText(center + up * 1.1f, solverString.c_str()); + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(white); + RENDER_DEBUG_IFACE(&renderDebug)->debugText(center + up * 1.12f, solverString.c_str()); + + RENDER_DEBUG_IFACE(&renderDebug)->popRenderState(); + } + + + RENDER_DEBUG_IFACE(&renderDebug)->setPose(savedPose); + renderDataUnLock(); +#endif +} + + + +void ClothingActorImpl::destroy() +{ + PX_ASSERT(!isSimulationRunning()); + if (mActiveCookingTask != NULL) + { + mActiveCookingTask->abort(); + PX_ASSERT(mActorDesc->runtimeCooked == NULL); + mActiveCookingTask = NULL; + } + + ApexActor::destroy(); // remove self from contexts, prevent re-release() + + removePhysX_LocksPhysX(); + + if (mData.mInternalBoneMatricesCur != NULL) + { + PX_FREE(mData.mInternalBoneMatricesCur); + mData.mInternalBoneMatricesCur = NULL; + } + if (mData.mInternalBoneMatricesPrev != NULL) + { + PX_FREE(mData.mInternalBoneMatricesPrev); + mData.mInternalBoneMatricesPrev = NULL; + } + + if (mInternalInterpolatedBoneMatrices != NULL) + { + PX_FREE(mInternalInterpolatedBoneMatrices); + mInternalInterpolatedBoneMatrices = NULL; + } + + PX_ASSERT(mActorDesc != NULL); // This is a requirement! + + +#ifndef WITHOUT_PVD + destroyPvdInstances(); +#endif + if (mActorDesc != NULL) + { + if (mActorDesc->runtimeCooked != NULL) + { + mAsset->getModuleClothing()->getBackendFactory(mActorDesc->runtimeCooked->className())->releaseCookedInstances(mActorDesc->runtimeCooked); + } + mActorDesc->destroy(); + mActorDesc = NULL; + } + + for (uint32_t i = 0; i < mGraphicalMeshes.size(); i++) + { + if (mGraphicalMeshes[i].renderProxy != NULL) + { + mGraphicalMeshes[i].renderProxy->release(); + mGraphicalMeshes[i].renderProxy = NULL; + } + } + mGraphicalMeshes.clear(); + + mRenderProxyMutex.lock(); + if (mRenderProxyURR != NULL) + { + mRenderProxyURR->release(); + mRenderProxyURR = NULL; + } + if (mRenderProxyReady != NULL) + { + mRenderProxyReady->release(); + mRenderProxyReady = NULL; + } + mRenderProxyMutex.unlock(); +} + + +#if APEX_UE4 +void ClothingActorImpl::initBeforeTickTasks(PxF32 deltaTime, PxF32 substepSize, PxU32 numSubSteps, PxTaskManager* taskManager, PxTaskID before, PxTaskID after) +#else +void ClothingActorImpl::initBeforeTickTasks(float deltaTime, float substepSize, uint32_t numSubSteps) +#endif +{ +#if APEX_UE4 + PX_UNUSED(before); + PX_UNUSED(after); + PX_UNUSED(taskManager); +#endif + mBeforeTickTask.setDeltaTime(deltaTime, substepSize, numSubSteps); +} + + + +void ClothingActorImpl::submitTasksDuring(PxTaskManager* taskManager) +{ + taskManager->submitUnnamedTask(mDuringTickTask); +} + + + +void ClothingActorImpl::setTaskDependenciesBefore(PxBaseTask* after) +{ + mBeforeTickTask.setContinuation(after); +} + + +void ClothingActorImpl::startBeforeTickTask() +{ + mBeforeTickTask.removeReference(); +} + + + +PxTaskID ClothingActorImpl::setTaskDependenciesDuring(PxTaskID before, PxTaskID after) +{ + mDuringTickTask.startAfter(before); + mDuringTickTask.finishBefore(after); + + return mDuringTickTask.getTaskID(); +} + + +#if !APEX_UE4 +void ClothingActorImpl::setFetchContinuation() +{ + PxTaskManager* taskManager = mClothingScene->getApexScene()->getTaskManager(); + taskManager->submitUnnamedTask(mWaitForFetchTask); + + mFetchResultsTask.setContinuation(&mWaitForFetchTask); + + // reduce refcount to 1 + mWaitForFetchTask.removeReference(); +} +#endif + + +void ClothingActorImpl::startFetchTasks() +{ + mFetchResultsRunningMutex.lock(); + mFetchResultsRunning = true; +#if APEX_UE4 + mFetchResultsSync.reset(); +#else + mWaitForFetchTask.mWaiting.reset(); +#endif + mFetchResultsRunningMutex.unlock(); + +#if APEX_UE4 + PxTaskManager* taskManager = mClothingScene->getApexScene()->getTaskManager(); + taskManager->submitUnnamedTask(mFetchResultsTask); +#else + setFetchContinuation(); +#endif + + mFetchResultsTask.removeReference(); +} + + + +void ClothingActorImpl::waitForFetchResults() +{ + mFetchResultsRunningMutex.lock(); + if (mFetchResultsRunning) + { + PX_PROFILE_ZONE("ClothingActorImpl::waitForFetchResults", GetInternalApexSDK()->getContextId()); +#if APEX_UE4 + mFetchResultsSync.wait(); +#else + mWaitForFetchTask.mWaiting.wait(); +#endif + syncActorData(); + mFetchResultsRunning = false; + +#ifndef WITHOUT_PVD + updatePvd(); +#endif + } + mFetchResultsRunningMutex.unlock(); +} + + + +#if !APEX_UE4 +void ClothingWaitForFetchTask::run() +{ +} + + + +void ClothingWaitForFetchTask::release() +{ + PxTask::release(); + + mWaiting.set(); +} + + + +const char* ClothingWaitForFetchTask::getName() const +{ + return "ClothingWaitForFetchTask"; +} +#endif + + + +void ClothingActorImpl::applyTeleport(bool skinningReady, uint32_t substepNumber) +{ + const float teleportWeight = (bInternalTeleportDue != ClothingTeleportMode::Continuous && substepNumber == 0) ? 1.0f : 0.0f; + const bool teleportReset = bInternalTeleportDue == ClothingTeleportMode::TeleportAndReset; + + // skinninReady is required when the teleportWeight is > 0.0 + // != is the same as XOR, it prevents calling setTeleportWeights twice when using a normal or (||) + if ((mClothingSimulation != NULL) && ((teleportWeight == 0.0f) != skinningReady)) + { + mClothingSimulation->setTeleportWeight(teleportWeight, teleportReset, bInternalLocalSpaceSim == 1); + } +} + + + +void ClothingActorImpl::applyGlobalPose_LocksPhysX() +{ + if (mClothingSimulation) + { + mClothingSimulation->applyGlobalPose(); + } +} + + + +bool ClothingActorImpl::isValidDesc(const NvParameterized::Interface& params) +{ + // make this verbose!!!! + if (::strcmp(params.className(), ClothingActorParam::staticClassName()) == 0) + { + const ClothingActorParam& actorDescGeneric = static_cast<const ClothingActorParam&>(params); + const ClothingActorParamNS::ParametersStruct& actorDesc = static_cast<const ClothingActorParamNS::ParametersStruct&>(actorDescGeneric); + + // commented variables don't need validation. + // actorDesc.actorDescTemplate + for (int32_t i = 0; i < actorDesc.boneMatrices.arraySizes[0]; i++) + { + if (!actorDesc.boneMatrices.buf[i].isFinite()) + { + APEX_INVALID_PARAMETER("boneMatrices[%d] is not finite!", i); + return false; + } + } + // actorDesc.clothDescTemplate + // actorDesc.fallbackSkinning + // actorDesc.flags.ParallelCpuSkinning + // actorDesc.flags.ParallelMeshMeshSkinning + // actorDesc.flags.ParallelPhysxMeshSkinning + // actorDesc.flags.RecomputeNormals + // actorDesc.flags.Visualize + if (!actorDesc.globalPose.isFinite()) + { + APEX_INVALID_PARAMETER("globalPose is not finite!"); + return false; + } + + if (actorDesc.maxDistanceBlendTime < 0.0f) + { + APEX_INVALID_PARAMETER("maxDistanceBlendTime must be positive"); + return false; + } + + if (actorDesc.maxDistanceScale.Scale < 0.0f || actorDesc.maxDistanceScale.Scale > 1.0f) + { + APEX_INVALID_PARAMETER("maxDistanceScale.Scale must be in the [0.0, 1.0] interval (is %f)", + actorDesc.maxDistanceScale.Scale); + return false; + } + + // actorDesc.shapeDescTemplate + // actorDesc.slowStart + // actorDesc.updateStateWithGlobalMatrices + // actorDesc.useHardwareCloth + // actorDesc.useInternalBoneOrder + // actorDesc.userData + // actorDesc.uvChannelForTangentUpdate + if (actorDesc.windParams.Adaption < 0.0f) + { + APEX_INVALID_PARAMETER("windParams.Adaption must be positive or zero"); + return false; + } + // actorDesc.windParams.Velocity + + if (actorDesc.actorScale <= 0.0f) + { + APEX_INVALID_PARAMETER("ClothingActorParam::actorScale must be bigger than 0 (is %f)", actorDesc.actorScale); + return false; + } + + return true; + } + else if (::strcmp(params.className(), ClothingPreviewParam::staticClassName()) == 0) + { + const ClothingPreviewParam& previewDescGeneric = static_cast<const ClothingPreviewParam&>(params); + const ClothingPreviewParamNS::ParametersStruct& previewDesc = static_cast<const ClothingPreviewParamNS::ParametersStruct&>(previewDescGeneric); + + + for (int32_t i = 0; i < previewDesc.boneMatrices.arraySizes[0]; i++) + { + if (!previewDesc.boneMatrices.buf[i].isFinite()) + { + APEX_INVALID_PARAMETER("boneMatrices[%d] is not finite!", i); + return false; + } + } + + if (!previewDesc.globalPose.isFinite()) + { + APEX_INVALID_PARAMETER("globalPose is not finite!"); + return false; + } + + return true; + } + return false; +} + + + +ClothingCookedParam* ClothingActorImpl::getRuntimeCookedDataPhysX() +{ + if (mActorDesc->runtimeCooked != NULL && ::strcmp(mActorDesc->runtimeCooked->className(), ClothingCookedParam::staticClassName()) == 0) + { + return static_cast<ClothingCookedParam*>(mActorDesc->runtimeCooked); + } + + return NULL; +} + + + +// ------ private methods ------- + + +void ClothingActorImpl::updateBoneBuffer(ClothingRenderProxyImpl* renderProxy) +{ + if (renderProxy == NULL) + return; + + renderProxy->setPose(getRenderGlobalPose()); + + RenderMeshActor* meshActor = renderProxy->getRenderMeshActor(); + if (meshActor == NULL) + return; + + if (mData.mInternalBoneMatricesCur == NULL) + { + // no bones + PxMat44 pose = PxMat44(PxIdentity); + if (mClothingSimulation == NULL) + { + // no sim + if (bInternalLocalSpaceSim == 1) + { + pose = PxMat44(PxIdentity) * mActorDesc->actorScale; + } + else + { + pose = mInternalGlobalPose; + } + } + + meshActor->setTM(pose, 0); + } + else /*if (bBoneBufferDirty)*/ // this dirty flag can only be used if we know that the + // render mesh asset stays with the clothing actor + // reactivate when APEX-43 is fixed. Note that currently + // the flag is set every frame in removePhysX_LocksPhysX + // when simulation is disabled, so the flag is + // currently not too useful + { + // bones or simulation have changed + PxMat44* buffer = mData.mInternalBoneMatricesCur; + PX_ASSERT(buffer != NULL); + + if (mAsset->getNumUsedBonesForMesh() == 1 && mClothingSimulation != NULL) + { + meshActor->setTM(PxMat44(PxIdentity), 0); + } + else + { + const uint32_t numBones = PxMin(mAsset->getNumUsedBonesForMesh(), meshActor->getBoneCount()); + for (uint32_t i = 0; i < numBones; i++) + { + meshActor->setTM(buffer[i], i); + } + } + } + + bBoneBufferDirty = 0; +} + + + +PxBounds3 ClothingActorImpl::getRenderMeshAssetBoundsTransformed() +{ + PxBounds3 newBounds = mAsset->getBoundingBox(); + + PxMat44 transformation; + if (mData.mInternalBoneMatricesCur != NULL) + { + transformation = mData.mInternalBoneMatricesCur[mAsset->getRootBoneIndex()]; + } + else + { + transformation = mActorDesc->globalPose; + } + + if (!newBounds.isEmpty()) + { + const PxVec3 center = transformation.transform(newBounds.getCenter()); + const PxVec3 extent = newBounds.getExtents(); + const PxMat33 basis(transformation.column0.getXYZ(), transformation.column1.getXYZ(), transformation.column2.getXYZ()); + + return PxBounds3::basisExtent(center, basis, extent); + } + else + { + return newBounds; + } +} + + + +bool ClothingActorImpl::allocateEnoughBoneBuffers_NoPhysX(bool prepareForSubstepping) +{ + PX_ASSERT(mActorDesc != NULL); + const uint32_t numBones = mActorDesc->useInternalBoneOrder ? mAsset->getNumUsedBones() : mAsset->getNumBones(); + + if (prepareForSubstepping && mInternalInterpolatedBoneMatrices == NULL) + { + mInternalInterpolatedBoneMatrices = (PxMat44*)PX_ALLOC(sizeof(PxMat44) * numBones, "mInternalInterpolatedBoneMatrices"); + } + + if (mData.mInternalBoneMatricesCur == NULL) + { + mData.mInternalBoneMatricesCur = (PxMat44*)PX_ALLOC(sizeof(PxMat44) * numBones, "mInternalBoneMatrices"); + mData.mInternalBoneMatricesPrev = (PxMat44*)PX_ALLOC(sizeof(PxMat44) * numBones, "mInternalBoneMatrices2"); + intrinsics::memSet(mData.mInternalBoneMatricesCur, 0, sizeof(PxMat44) * numBones); + intrinsics::memSet(mData.mInternalBoneMatricesPrev, 0, sizeof(PxMat44) * numBones); + return true; + } + + return false; +} + + + +bool ClothingActorImpl::isSimulationRunning() const +{ + if (mClothingScene != NULL) + { + return mClothingScene->isSimulating(); // virtual call + } + + return false; +} + + + +void ClothingActorImpl::updateStateInternal_NoPhysX(bool prepareForSubstepping) +{ + PX_ASSERT(mActorDesc); + mInternalFlags = mActorDesc->flags; + + mInternalMaxDistanceBlendTime = (mActorDesc->freezeByLOD) ? 0.0f : mActorDesc->maxDistanceBlendTime; + + mInternalWindParams = mActorDesc->windParams; + + bInternalVisible = bBufferedVisible; + if (bUpdateFrozenFlag == 1) + { + bInternalFrozen = bBufferedFrozen; + bUpdateFrozenFlag = 0; + } + + // update teleportation from double buffering + bInternalTeleportDue = (ClothingTeleportMode::Enum)mActorDesc->teleportMode; + mActorDesc->teleportMode = ClothingTeleportMode::Continuous; + + if (mActorDesc->localSpaceSim != (bInternalLocalSpaceSim == 1)) + { + bInternalTeleportDue = ClothingTeleportMode::TeleportAndReset; + } + bInternalLocalSpaceSim = mActorDesc->localSpaceSim ? 1u : 0u; + + bMaxDistanceScaleChanged = + (mInternalMaxDistanceScale.Scale != mActorDesc->maxDistanceScale.Scale || + mInternalMaxDistanceScale.Multipliable != mActorDesc->maxDistanceScale.Multipliable) + ? 1u : 0u; + + mInternalMaxDistanceScale = mActorDesc->maxDistanceScale; + + lockRenderResources(); + + PxMat44 globalPose = mActorDesc->globalPose; + + PxMat44 rootBoneTransform = PxMat44(PxIdentity); + const float invActorScale = 1.0f / mActorDesc->actorScale; + bool bHasBones = mActorDesc->boneMatrices.arraySizes[0] > 0 || mAsset->getNumBones() > 0; + bool bMultiplyGlobalPoseIntoBones = mActorDesc->multiplyGlobalPoseIntoBones || mActorDesc->boneMatrices.arraySizes[0] == 0; + if (bHasBones) + { + bool newBuffers = allocateEnoughBoneBuffers_NoPhysX(prepareForSubstepping); + + nvidia::swap(mData.mInternalBoneMatricesCur, mData.mInternalBoneMatricesPrev); + + const uint32_t numBones = (mActorDesc->useInternalBoneOrder) ? mAsset->getNumUsedBones() : mAsset-> getNumBones(); + + uint32_t rootNodeExternalIndex = mActorDesc->useInternalBoneOrder ? mAsset->getRootBoneIndex() : mAsset->getBoneExternalIndex(mAsset->getRootBoneIndex()); + if (rootNodeExternalIndex < (uint32_t)mActorDesc->boneMatrices.arraySizes[0]) + { + // new pose of root bone available + rootBoneTransform = mActorDesc->boneMatrices.buf[rootNodeExternalIndex]; + } + else if (mActorDesc->updateStateWithGlobalMatrices) + { + // no pose for root bone available, use bind pose + mAsset->getBoneBasePose(mAsset->getRootBoneIndex(), rootBoneTransform); + } + + PxMat44 pose = PxMat44(PxIdentity); + if (bInternalLocalSpaceSim == 1) + { + // consider the root bone as local space reference + // PH: Note that inverseRT does not invert the scale, but preserve it + + // normalize (dividing by actorscale is not precise enough) + if (!bMultiplyGlobalPoseIntoBones) + { + rootBoneTransform.column0.normalize(); + rootBoneTransform.column1.normalize(); + rootBoneTransform.column2.normalize(); + } + + // this transforms the skeleton into origin, and keeps the scale + PxMat44 invRootBoneTransformTimesScale = rootBoneTransform.inverseRT(); + + if (bMultiplyGlobalPoseIntoBones) + { + invRootBoneTransformTimesScale *= mActorDesc->actorScale; + } + + // the result will be transformed back to global space in rendering + pose = invRootBoneTransformTimesScale; + } + else if (bMultiplyGlobalPoseIntoBones) + { + pose = globalPose; + } + + if (mActorDesc->boneMatrices.arraySizes[0] >= (int32_t)numBones) + { + // TODO when no globalPose is set and the bones are given internal, there could be a memcpy + if (mAsset->writeBoneMatrices(pose, mActorDesc->boneMatrices.buf, sizeof(PxMat44), (uint32_t)mActorDesc->boneMatrices.arraySizes[0], + mData.mInternalBoneMatricesCur, mActorDesc->useInternalBoneOrder, mActorDesc->updateStateWithGlobalMatrices)) + { + bBoneMatricesChanged = 1; + } + } + else + { + // no matrices provided. mInternalBoneMatrices (skinningMatrices) should just reflect the + // the global pose transform + + for (uint32_t i = 0; i < numBones; i++) + { + mData.mInternalBoneMatricesCur[i] = pose; + } + } + + if (newBuffers) + { + memcpy(mData.mInternalBoneMatricesPrev, mData.mInternalBoneMatricesCur, sizeof(PxMat44) * numBones); + } + } + else if (mData.mInternalBoneMatricesCur != NULL) + { + PX_FREE(mData.mInternalBoneMatricesCur); + PX_FREE(mData.mInternalBoneMatricesPrev); + mData.mInternalBoneMatricesCur = mData.mInternalBoneMatricesPrev = NULL; + } + + unlockRenderResources(); + + PxMat44 newInternalGlobalPose; + if (bInternalLocalSpaceSim == 1) + { + // transform back into global space: + if (bMultiplyGlobalPoseIntoBones || !bHasBones) + { + // we need to remove the scale when transforming back, as we keep the scale in local space + // hmm, not sure why the adjustments on the translation parts are necessary, but they are.. + globalPose *= invActorScale; + rootBoneTransform.scale(PxVec4(1.0f, 1.0f, 1.0f, mActorDesc->actorScale)); + newInternalGlobalPose = globalPose * rootBoneTransform; + } + else + { + newInternalGlobalPose = rootBoneTransform; + } + } + else + { + newInternalGlobalPose = globalPose; + } + + mOldInternalGlobalPose = mOldInternalGlobalPose.column0.isZero() ? newInternalGlobalPose : mInternalGlobalPose; + mInternalGlobalPose = newInternalGlobalPose; + + bGlobalPoseChanged = (mInternalGlobalPose != mOldInternalGlobalPose) ? 1u : 0u; + + // set bBoneBufferDirty to 1 if any matrices have changed + bBoneBufferDirty = (bGlobalPoseChanged == 1) || (bBoneMatricesChanged == 1) || (bBoneBufferDirty == 1) ? 1u : 0u; +} + + + +#define RENDER_DEBUG_INTERMEDIATE_STEPS 0 + +template<bool withBackstop> +void ClothingActorImpl::skinPhysicsMeshInternal(bool useInterpolatedMatrices, float substepFraction) +{ +#if RENDER_DEBUG_INTERMEDIATE_STEPS + const float RenderDebugIntermediateRadius = 0.8f; + mClothingScene->mRenderDebug->setCurrentDisplayTime(0.1); + const uint8_t yellowColor = 255 - (uint8_t)(255.0f * substepFraction); + const uint32_t color = 0xff0000 | yellowColor << 8; + mClothingScene->mRenderDebug->setCurrentColor(color); +#else + PX_UNUSED(substepFraction); +#endif + + uint32_t morphOffset = mAsset->getPhysicalMeshOffset(mAsset->getPhysicalMeshID(mCurrentGraphicalLodId)); + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* physicalMesh = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId); + PxVec3* morphedPositions = mActorDesc->morphPhysicalMeshNewPositions.buf; + const PxVec3* const PX_RESTRICT positions = morphedPositions != NULL ? morphedPositions + morphOffset : physicalMesh->vertices.buf; + const PxVec3* const PX_RESTRICT normals = physicalMesh->normals.buf; + const ClothingPhysicalMeshParametersNS::ConstrainCoefficient_Type* const PX_RESTRICT coeffs = physicalMesh->constrainCoefficients.buf; + const float actorScale = mActorDesc->actorScale; + + const uint32_t numVertices = physicalMesh->numSimulatedVertices; + const uint32_t numBoneIndicesPerVertex = physicalMesh->numBonesPerVertex; + + PxVec3* const PX_RESTRICT targetPositions = mClothingSimulation->skinnedPhysicsPositions; + PxVec3* const PX_RESTRICT targetNormals = mClothingSimulation->skinnedPhysicsNormals; + + const uint32_t numPrefetches = ((numVertices + NUM_VERTICES_PER_CACHE_BLOCK - 1) / NUM_VERTICES_PER_CACHE_BLOCK); + + if (mData.mInternalBoneMatricesCur == NULL || numBoneIndicesPerVertex == 0) + { + PxMat44 matrix = PxMat44(PxIdentity); + if (bInternalLocalSpaceSim == 1) + { + matrix *= actorScale; + } + else + { + // PH: maybe we can skip the matrix multiplication altogether when using local space sim? + matrix = useInterpolatedMatrices ? mInternalInterpolatedGlobalPose : mInternalGlobalPose; + } + for (uint32_t i = 0; i < numVertices; i++) + { + //const PxVec3 untransformedPosition = positions[index] + morphReordering == NULL ? PxVec3(0.0f) : morphDisplacements[morphReordering[index]]; + targetPositions[i] = matrix.transform(positions[i]); + targetNormals[i] = matrix.rotate(normals[i]); + + if (withBackstop) + { + if (coeffs[i].collisionSphereDistance < 0.0f) + { + targetPositions[i] -= (coeffs[i].collisionSphereDistance * actorScale) * targetNormals[i]; + } + } +#if RENDER_DEBUG_INTERMEDIATE_STEPS + if (RenderDebugIntermediateRadius > 0.0f) + { + mClothingScene->mRenderDebug->debugPoint(targetPositions[i], RenderDebugIntermediateRadius); + } +#endif + } + } + else + { + const uint16_t* const PX_RESTRICT simBoneIndices = physicalMesh->boneIndices.buf; + const float* const PX_RESTRICT simBoneWeights = physicalMesh->boneWeights.buf; + const PxMat44* matrices = useInterpolatedMatrices ? mInternalInterpolatedBoneMatrices : mData.mInternalBoneMatricesCur; + + const uint8_t* const PX_RESTRICT optimizationData = physicalMesh->optimizationData.buf; + PX_ASSERT(optimizationData != NULL); + PX_ASSERT((int32_t)(numPrefetches * NUM_VERTICES_PER_CACHE_BLOCK) / 2 <= physicalMesh->optimizationData.arraySizes[0]); + + uint32_t vertexIndex = 0; + for (uint32_t i = 0; i < numPrefetches; ++i) + { + // HL: i tried to put positions and normals into a 16 byte aligned struct + // but there was no significant perf benefit, and it caused a lot of adaptations + // in the code because of the introduction of strides. Had to use + // a stride iterators in AbstractMeshDescription, which made + // its usage slower on xbox + + uint8_t* cache = (uint8_t*)((((size_t)(positions + vertexIndex + NUM_VERTICES_PER_CACHE_BLOCK)) >> 7) << 7); + prefetchLine(cache); + //prefetchLine(cache + 128); + + cache = (uint8_t*)((((size_t)(normals + vertexIndex + NUM_VERTICES_PER_CACHE_BLOCK)) >> 7) << 7); + prefetchLine(cache); + //prefetchLine(cache + 128); + + cache = (uint8_t*)((((size_t)(&simBoneWeights[(vertexIndex + NUM_VERTICES_PER_CACHE_BLOCK) * numBoneIndicesPerVertex])) >> 7) << 7); + prefetchLine(cache); + prefetchLine(cache + 128); + + cache = (uint8_t*)((((size_t)(&simBoneIndices[(vertexIndex + NUM_VERTICES_PER_CACHE_BLOCK) * numBoneIndicesPerVertex])) >> 7) << 7); + prefetchLine(cache); + //prefetchLine(cache + 128); + + + if (withBackstop) + { + prefetchLine(&coeffs[vertexIndex + NUM_VERTICES_PER_CACHE_BLOCK]); + } + + for (uint32_t j = 0; j < NUM_VERTICES_PER_CACHE_BLOCK; ++j) + { + //float sumWeights = 0.0f; // this is just for sanity + + Simd4f positionV = gSimd4fZero; + Simd4f normalV = gSimd4fZero; + + const uint8_t shift = 4 * (vertexIndex % 2); + const uint8_t numBones = uint8_t((optimizationData[vertexIndex / 2] >> shift) & 0x7); + for (uint32_t k = 0; k < numBones; k++) + { + const float weight = simBoneWeights[vertexIndex * numBoneIndicesPerVertex + k]; + + PX_ASSERT(weight <= 1.0f); + + //sumWeights += weight; + Simd4f weightV = Simd4fScalarFactory(weight); + + const uint32_t index = simBoneIndices[vertexIndex * numBoneIndicesPerVertex + k]; + PX_ASSERT(index < mAsset->getNumUsedBones()); + + /// PH: This might be faster without the reference, but on PC I can't tell + /// HL: Now with SIMD it's significantly faster as reference + const PxMat44& bone = (PxMat44&)matrices[index]; + + Simd4f pV = applyAffineTransform(bone, createSimd3f(positions[vertexIndex])); + pV = pV * weightV; + positionV = positionV + pV; + + ///todo There are probably cases where we don't need the normal on the physics mesh + Simd4f nV = applyLinearTransform(bone, createSimd3f(normals[vertexIndex])); + nV = nV * weightV; + normalV = normalV + nV; + } + + // PH: Sanity test. if this is not fulfilled, skinning went awfully wrong anyways + // TODO do this check only once somewhere at initialization + //PX_ASSERT(sumWeights == 0.0f || (sumWeights > 0.9999f && sumWeights < 1.0001f)); + + normalV = normalV * rsqrt(dot3(normalV, normalV)); + store3(&targetNormals[vertexIndex].x, normalV); + PX_ASSERT(numBones == 0 || targetNormals[vertexIndex].isFinite()); + + // We disabled this in skinToBones as well, cause it's not really a valid case + //if (sumWeights == 0) + // positionV = V3LoadU(positions[vertexIndex]); + + // in case of a negative collision sphere distance we move the animated position upwards + // along the normal and set the collision sphere distance to zero. + if (withBackstop) + { + if ((optimizationData[vertexIndex / 2] >> shift) & 0x8) + { + const float collisionSphereDistance = coeffs[vertexIndex].collisionSphereDistance; + Simd4f dV = normalV * Simd4fScalarFactory(collisionSphereDistance * actorScale); + positionV = positionV - dV; + } + } + + store3(&targetPositions[vertexIndex].x, positionV); + PX_ASSERT(targetPositions[vertexIndex].isFinite()); + +#if RENDER_DEBUG_INTERMEDIATE_STEPS + if (RenderDebugIntermediateRadius > 0.0f) + { + mClothingScene->mRenderDebug->debugPoint(targetPositions[vertexIndex], RenderDebugIntermediateRadius); + } +#endif + + ++vertexIndex; + } + } + } +#if RENDER_DEBUG_INTERMEDIATE_STEPS + mClothingScene->mRenderDebug->setCurrentDisplayTime(); +#endif +} + + + +void ClothingActorImpl::fetchResults() +{ + PX_PROFILE_ZONE("ClothingActorImpl::fetchResults", GetInternalApexSDK()->getContextId()); + if (isVisible() && mClothingSimulation != NULL && bInternalFrozen == 0) + { + mClothingSimulation->fetchResults(mInternalFlags.ComputePhysicsMeshNormals); + } +} + + + +ClothingActorData& ClothingActorImpl::getActorData() +{ + return mData; +} + + + +void ClothingActorImpl::initializeActorData() +{ + PX_PROFILE_ZONE("ClothingActorImpl::initializeActorData", GetInternalApexSDK()->getContextId()); + + mData.bIsClothingSimulationNull = mClothingSimulation == NULL; + const bool bUninit = mData.bIsInitialized && mClothingSimulation == NULL; + if (bReinitActorData == 1 || bUninit) + { + //We need to uninitialize ourselves + PX_FREE(mData.mAsset.mData); + mData.mAsset.mData = NULL; + mData.bIsInitialized = false; + bReinitActorData = 0; + + if (bUninit) + { + return; + } + } + + if(mGraphicalMeshes[mCurrentGraphicalLodId].renderProxy != NULL) + { + mData.mRenderingDataPosition = mGraphicalMeshes[mCurrentGraphicalLodId].renderProxy->renderingDataPosition; + mData.mRenderingDataNormal = mGraphicalMeshes[mCurrentGraphicalLodId].renderProxy->renderingDataNormal; + mData.mRenderingDataTangent = mGraphicalMeshes[mCurrentGraphicalLodId].renderProxy->renderingDataTangent; + } + + if (!mData.bIsInitialized && mClothingSimulation != NULL) + { + mData.bIsInitialized = true; + //Initialize + mData.mRenderLock = mRenderDataLock; + + mData.mSdkDeformableVerticesCount = mClothingSimulation->sdkNumDeformableVertices; + mData.mSdkDeformableIndicesCount = mClothingSimulation->sdkNumDeformableIndices; + mData.mSdkWritebackPositions = mClothingSimulation->sdkWritebackPosition; + mData.mSdkWritebackNormal = mClothingSimulation->sdkWritebackNormal; + + mData.mSkinnedPhysicsPositions = mClothingSimulation->skinnedPhysicsPositions; + mData.mSkinnedPhysicsNormals = mClothingSimulation->skinnedPhysicsNormals; + + //Allocate the clothing asset now... + + mAsset->initializeAssetData(mData.mAsset, mActorDesc->uvChannelForTangentUpdate); + + mData.mMorphDisplacementBuffer = mActorDesc->morphDisplacements.buf; + mData.mMorphDisplacementBufferCount = (uint32_t)mActorDesc->morphDisplacements.arraySizes[0]; + } + + uint32_t largestSubmesh = 0; + if (mData.bIsInitialized && mClothingSimulation != NULL) + { + mData.bRecomputeNormals = mInternalFlags.RecomputeNormals; + mData.bRecomputeTangents = mInternalFlags.RecomputeTangents; + mData.bIsSimulationMeshDirty = mClothingSimulation->isSimulationMeshDirty(); + + // this updates per-frame so I need to sync it every frame + mData.mInternalGlobalPose = mInternalGlobalPose; + mData.mCurrentGraphicalLodId = mCurrentGraphicalLodId; + + for (uint32_t a = 0; a < mData.mAsset.mGraphicalLodsCount; ++a) + { + ClothingMeshAssetData* pLod = mData.mAsset.GetLod(a); + pLod->bActive = mGraphicalMeshes[a].active; + pLod->bNeedsTangents = mGraphicalMeshes[a].needsTangents; + + // check if map contains tangent values, otherwise print out warning + if (!mData.bRecomputeTangents && mGraphicalMeshes[a].needsTangents && pLod->mImmediateClothMap != NULL) + { + // tangentBary has been marked invalid during asset update, or asset has immediate map (without tangent info) + mData.bRecomputeTangents = true; + mInternalFlags.RecomputeTangents = true; + mActorDesc->flags.RecomputeTangents = true; + + // hm, let's not spam the user, as RecomputeTangents is off by default + //APEX_DEBUG_INFO("Asset (%s) does not support tangent skinning. Resetting RecomputeTangents to true.", mAsset->getName()); + } + + //Copy to... + + for (uint32_t b = 0; b < pLod->mSubMeshCount; b++) + { + ClothingAssetSubMesh* submesh = mData.mAsset.GetSubmesh(a, b); + + const uint32_t numParts = (uint32_t)mAsset->getGraphicalLod(a)->physicsMeshPartitioning.arraySizes[0]; + ClothingGraphicalLodParametersNS::PhysicsMeshPartitioning_Type* parts = mAsset->getGraphicalLod(a)->physicsMeshPartitioning.buf; + +#if defined _DEBUG || PX_CHECKED + bool found = false; +#endif + for (uint32_t c = 0; c < numParts; c++) + { + if (parts[c].graphicalSubmesh == b) + { + submesh->mCurrentMaxVertexSimulation = parts[c].numSimulatedVertices; + submesh->mCurrentMaxVertexAdditionalSimulation = parts[c].numSimulatedVerticesAdditional; + submesh->mCurrentMaxIndexSimulation = parts[c].numSimulatedIndices; + + largestSubmesh = PxMax(largestSubmesh, parts[c].numSimulatedVerticesAdditional); +#if defined _DEBUG || PX_CHECKED + found = true; +#endif + break; + } + } +#if defined _DEBUG || PX_CHECKED + PX_ASSERT(found); +#endif + } + } + mData.mActorScale = mActorDesc->actorScale; + + mData.bShouldComputeRenderData = shouldComputeRenderData(); + mData.bInternalFrozen = bInternalFrozen; + mData.bCorrectSimulationNormals = mInternalFlags.CorrectSimulationNormals; + mData.bParallelCpuSkinning = mInternalFlags.ParallelCpuSkinning; + mData.mGlobalPose = mActorDesc->globalPose; + + mData.mInternalMatricesCount = mActorDesc->useInternalBoneOrder ? mAsset->getNumUsedBones() : mAsset->getNumBones(); + } + +} + +void ClothingActorImpl::syncActorData() +{ + PX_PROFILE_ZONE("ClothingActorImpl::syncActorData", GetInternalApexSDK()->getContextId()); + + if (mData.bIsInitialized && mData.bShouldComputeRenderData && mClothingSimulation != NULL /*&& bInternalFrozen == 0*/) + { + PX_ASSERT(!mData.mNewBounds.isEmpty()); + PX_ASSERT(mData.mNewBounds.isFinite()); + + //Write back all the modified variables so that the simulation is consistent + mNewBounds = mData.mNewBounds; + } + else + { + mNewBounds = getRenderMeshAssetBoundsTransformed(); + } + + if (bInternalLocalSpaceSim == 1) + { +#if _DEBUG + bool ok = true; + ok &= mInternalGlobalPose.column0.isNormalized(); + ok &= mInternalGlobalPose.column1.isNormalized(); + ok &= mInternalGlobalPose.column2.isNormalized(); + if (!ok) + { + APEX_DEBUG_WARNING("Internal Global Pose is not normalized (Scale: %f %f %f). Bounds could be wrong.", mInternalGlobalPose.column0.magnitude(), mInternalGlobalPose.column1.magnitude(), mInternalGlobalPose.column2.magnitude()); + } +#endif + PX_ASSERT(!mNewBounds.isEmpty()); + mRenderBounds = PxBounds3::transformFast(PxTransform(mInternalGlobalPose), mNewBounds); + } + else + { + mRenderBounds = mNewBounds; + } + + markRenderProxyReady(); +} + + + +void ClothingActorImpl::markRenderProxyReady() +{ + PX_PROFILE_ZONE("ClothingActorImpl::markRenderProxyReady", GetInternalApexSDK()->getContextId()); + mRenderProxyMutex.lock(); + if (mRenderProxyReady != NULL) + { + // user didn't request the renderable after fetchResults, + // let's release it, so it can be reused + mRenderProxyReady->release(); + } + + ClothingRenderProxyImpl* renderProxy = mGraphicalMeshes[mCurrentGraphicalLodId].renderProxy; + if (renderProxy != NULL) + { + updateBoneBuffer(renderProxy); + mGraphicalMeshes[mCurrentGraphicalLodId].renderProxy = NULL; + renderProxy->setBounds(mRenderBounds); + } + + mRenderProxyReady = renderProxy; + mRenderProxyMutex.unlock(); +} + + + +void ClothingActorImpl::fillWritebackData_LocksPhysX(const WriteBackInfo& writeBackInfo) +{ + PX_ASSERT(mClothingSimulation != NULL); + PX_ASSERT(mClothingSimulation->physicalMeshId != 0xffffffff); + PX_ASSERT(writeBackInfo.simulationDelta >= 0.0f); + + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* destPhysicalMesh = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId); + + // copy the data from the old mesh + // use bigger position buffer as temp buffer + uint32_t numCopyVertices = 0; + PxVec3* velocities = NULL; + + float transitionMapThickness = 0.0f; + float transitionMapOffset = 0.0f; + const ClothingPhysicalMeshParametersNS::SkinClothMapB_Type* pTCMB = NULL; + const ClothingPhysicalMeshParametersNS::SkinClothMapD_Type* pTCM = NULL; + if (writeBackInfo.oldSimulation) + { + PX_ASSERT(writeBackInfo.oldSimulation->physicalMeshId != 0xffffffff); + pTCMB = mAsset->getTransitionMapB(mClothingSimulation->physicalMeshId, writeBackInfo.oldSimulation->physicalMeshId, transitionMapThickness, transitionMapOffset); + pTCM = mAsset->getTransitionMap(mClothingSimulation->physicalMeshId, writeBackInfo.oldSimulation->physicalMeshId, transitionMapThickness, transitionMapOffset); + } + + if (pTCMB != NULL || pTCM != NULL) + { + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* srcPhysicalMesh = mAsset->getPhysicalMeshFromLod(writeBackInfo.oldGraphicalLodId); + PX_ASSERT(srcPhysicalMesh != NULL); + + AbstractMeshDescription srcPM; + srcPM.numIndices = writeBackInfo.oldSimulation->sdkNumDeformableIndices; + srcPM.numVertices = writeBackInfo.oldSimulation->sdkNumDeformableVertices; + srcPM.pIndices = srcPhysicalMesh->indices.buf; + srcPM.pNormal = writeBackInfo.oldSimulation->sdkWritebackNormal; + srcPM.pPosition = writeBackInfo.oldSimulation->sdkWritebackPosition; + srcPM.avgEdgeLength = transitionMapThickness; + + // new position and normal buffer will be initialized afterwards, buffer can be used here + if (mClothingSimulation->sdkNumDeformableVertices > writeBackInfo.oldSimulation->sdkNumDeformableVertices) + { + transferVelocities_LocksPhysX(*writeBackInfo.oldSimulation, pTCMB, pTCM, destPhysicalMesh->numVertices, srcPM.pIndices, srcPM.numIndices, srcPM.numVertices, + mClothingSimulation->sdkWritebackPosition, mClothingSimulation->sdkWritebackNormal, writeBackInfo.simulationDelta); + } + + // PH: sdkWritebackPosition will contain qnans for the values that have not been written. + intrinsics::memSet(mClothingSimulation->sdkWritebackPosition, 0xff, sizeof(PxVec3) * mClothingSimulation->sdkNumDeformableVertices); + + if (pTCMB != NULL) + { + numCopyVertices = mData.mAsset.skinClothMapB(mClothingSimulation->sdkWritebackPosition, mClothingSimulation->sdkWritebackNormal, + mClothingSimulation->sdkNumDeformableVertices, srcPM, (ClothingGraphicalLodParametersNS::SkinClothMapB_Type*)pTCMB, destPhysicalMesh->numVertices, true); + } + else + { + numCopyVertices = mData.mAsset.skinClothMap<true>(mClothingSimulation->sdkWritebackPosition, mClothingSimulation->sdkWritebackNormal, NULL, mClothingSimulation->sdkNumDeformableVertices, + srcPM, (ClothingGraphicalLodParametersNS::SkinClothMapD_Type*)pTCM, destPhysicalMesh->numVertices, transitionMapOffset, mActorDesc->actorScale); + } + + // don't need old positions and normals anymore + if (writeBackInfo.oldSimulation->sdkNumDeformableVertices >= mClothingSimulation->sdkNumDeformableVertices) + { + transferVelocities_LocksPhysX(*writeBackInfo.oldSimulation, pTCMB, pTCM, destPhysicalMesh->numVertices, srcPM.pIndices, srcPM.numIndices, srcPM.numVertices, + writeBackInfo.oldSimulation->sdkWritebackPosition, writeBackInfo.oldSimulation->sdkWritebackNormal, writeBackInfo.simulationDelta); + } + } + else if (writeBackInfo.oldSimulation != NULL && writeBackInfo.oldSimulation->physicalMeshId == mClothingSimulation->physicalMeshId) + { + if (writeBackInfo.oldSimulation->sdkNumDeformableVertices < mClothingSimulation->sdkNumDeformableVertices) + { + // old is smaller + numCopyVertices = writeBackInfo.oldSimulation->sdkNumDeformableVertices; + velocities = mClothingSimulation->sdkWritebackPosition; + copyAndComputeVelocities_LocksPhysX(numCopyVertices, writeBackInfo.oldSimulation, velocities, writeBackInfo.simulationDelta); + + // PH: sdkWritebackPosition will contain qnans for the values that have not been written. + intrinsics::memSet(mClothingSimulation->sdkWritebackPosition, 0xff, sizeof(PxVec3) * mClothingSimulation->sdkNumDeformableVertices); + + copyPositionAndNormal_NoPhysX(numCopyVertices, writeBackInfo.oldSimulation); + } + else + { + // new is smaller + numCopyVertices = mClothingSimulation->sdkNumDeformableVertices; + velocities = writeBackInfo.oldSimulation->sdkWritebackPosition; + + // PH: sdkWritebackPosition will contain qnans for the values that have not been written. + intrinsics::memSet(mClothingSimulation->sdkWritebackPosition, 0xff, sizeof(PxVec3) * mClothingSimulation->sdkNumDeformableVertices); + + copyPositionAndNormal_NoPhysX(numCopyVertices, writeBackInfo.oldSimulation); + copyAndComputeVelocities_LocksPhysX(numCopyVertices, writeBackInfo.oldSimulation, velocities, writeBackInfo.simulationDelta); + } + } + else + { + velocities = mClothingSimulation->sdkWritebackPosition; + copyAndComputeVelocities_LocksPhysX(0, writeBackInfo.oldSimulation, velocities, writeBackInfo.simulationDelta); + + // PH: sdkWritebackPosition will contain qnans for the values that have not been written. + intrinsics::memSet(mClothingSimulation->sdkWritebackPosition, 0xff, sizeof(PxVec3) * mClothingSimulation->sdkNumDeformableVertices); + } + + + const PxVec3* positions = destPhysicalMesh->vertices.buf; + const PxVec3* normals = destPhysicalMesh->normals.buf; + const uint32_t numBoneIndicesPerVertex = destPhysicalMesh->numBonesPerVertex; + const uint16_t* simBoneIndices = destPhysicalMesh->boneIndices.buf; + const float* simBoneWeights = destPhysicalMesh->boneWeights.buf; + + // apply an initial skinning on the physical mesh + // ASSUMPTION: when allocated, mSdkWriteback* buffers will always contain meaningful data, so initialize correctly! + // All data that is not skinned from the old to the new mesh will have non-finite values (qnan) + + const uint8_t* const PX_RESTRICT optimizationData = destPhysicalMesh->optimizationData.buf; + PX_ASSERT(optimizationData != NULL); + + const PxMat44* matrices = mData.mInternalBoneMatricesCur; + if (matrices != NULL) + { + // one pass of cpu skinning on the physical mesh + const bool useNormals = mClothingSimulation->sdkWritebackNormal != NULL; + + const uint32_t numVertices = destPhysicalMesh->numVertices; + for (uint32_t i = numCopyVertices; i < numVertices; i++) + { + const uint8_t shift = 4 * (i % 2); + const uint8_t numBones = uint8_t((optimizationData[i / 2] >> shift) & 0x7); + + const uint32_t vertexIndex = (pTCMB == NULL) ? i : pTCMB[i].vertexIndexPlusOffset; + + if (vertexIndex >= mClothingSimulation->sdkNumDeformableVertices) + { + continue; + } + + if (PxIsFinite(mClothingSimulation->sdkWritebackPosition[vertexIndex].x)) + { + continue; + } + + Simd4f positionV = gSimd4fZero; + Simd4f normalV = gSimd4fZero; + + const uint32_t numUsedBones = mAsset->getNumUsedBones(); + PX_UNUSED(numUsedBones); + + for (uint32_t j = 0; j < numBones; j++) + { + + Simd4f weightV = Simd4fScalarFactory(simBoneWeights[vertexIndex * numBoneIndicesPerVertex + j]); + uint16_t index = simBoneIndices[vertexIndex * numBoneIndicesPerVertex + j]; + PX_ASSERT(index < numUsedBones); + + const PxMat44& bone = (PxMat44&)matrices[index]; + + Simd4f pV = applyAffineTransform(bone, createSimd3f(positions[vertexIndex])); + pV = pV * weightV; + positionV = positionV + pV; + + if (useNormals) + { + Simd4f nV = applyLinearTransform(bone, createSimd3f(normals[vertexIndex])); + nV = nV * weightV; + normalV = normalV + nV; + } + + } + + if (useNormals) + { + normalV = normalV * rsqrt(dot3(normalV, normalV)); + store3(&mClothingSimulation->sdkWritebackNormal[vertexIndex].x, normalV); + } + store3(&mClothingSimulation->sdkWritebackPosition[vertexIndex].x, positionV); + } + } + else + { + // no bone matrices, just move into world space + const uint32_t numVertices = destPhysicalMesh->numVertices; + PxMat44 TM = bInternalLocalSpaceSim == 1 ? PxMat44(PxIdentity) * mActorDesc->actorScale : mInternalGlobalPose; + for (uint32_t i = numCopyVertices; i < numVertices; i++) + { + const uint32_t vertexIndex = (pTCMB == NULL) ? i : pTCMB[i].vertexIndexPlusOffset; + if (vertexIndex >= mClothingSimulation->sdkNumDeformableVertices) + { + continue; + } + + if (PxIsFinite(mClothingSimulation->sdkWritebackPosition[vertexIndex].x)) + { + continue; + } + + mClothingSimulation->sdkWritebackPosition[vertexIndex] = TM.transform(positions[vertexIndex]); + if (mClothingSimulation->sdkWritebackNormal != NULL) + { + mClothingSimulation->sdkWritebackNormal[vertexIndex] = TM.rotate(normals[vertexIndex]); + } + } + } +} + + + +/// todo simdify? +void ClothingActorImpl::applyVelocityChanges_LocksPhysX(float simulationDelta) +{ + if (mClothingSimulation == NULL) + { + return; + } + + float pressure = mActorDesc->pressure; + if (pressure >= 0.0f) + { + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* mesh = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId); + if (!mesh->isClosed) + { + pressure = -1.0f; + + if (bPressureWarning == 0) + { + bPressureWarning = 1; + if (!mesh->isClosed) + { + APEX_INTERNAL_ERROR("Pressure requires a closed mesh!\n"); + } + else + { + APEX_INTERNAL_ERROR("Pressure only works on Physics LODs where all vertices are active!\n"); + } + } + } + } + + // did the simulation handle pressure already? + const bool needsPressure = !mClothingSimulation->applyPressure(pressure) && (pressure > 0.0f); + + if (mInternalWindParams.Adaption > 0.0f || mVelocityCallback != NULL || mActorDesc->useVelocityClamping || needsPressure) + { + PX_ASSERT(mClothingScene); + PX_PROFILE_ZONE("ClothingActorImpl::applyVelocityChanges", GetInternalApexSDK()->getContextId()); + + const uint32_t numVertices = mClothingSimulation->sdkNumDeformableVertices; + + // copy velocities to temp array + PxVec3* velocities = mClothingSimulation->skinnedPhysicsNormals; + + // use the skinnedPhysics* buffers when possible + const bool doNotUseWritebackMemory = (bBoneMatricesChanged == 0 && bGlobalPoseChanged == 0); + + if (doNotUseWritebackMemory) + { + velocities = (PxVec3*)GetInternalApexSDK()->getTempMemory(sizeof(PxVec3) * numVertices); + } + + if (velocities == NULL) + { + return; + } + + mClothingSimulation->getVelocities(velocities); + // positions never need to be read! + + bool writeVelocities = false; + + // get pointers + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* physicalMesh = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId); + const PxVec3* assetNormals = physicalMesh->normals.buf; + const ClothingPhysicalMeshParametersNS::ConstrainCoefficient_Type* coeffs = physicalMesh->constrainCoefficients.buf; + const PxVec3* normals = (mClothingSimulation->sdkWritebackNormal != NULL) ? mClothingSimulation->sdkWritebackNormal : assetNormals; + + PxVec3 windVelocity = mInternalWindParams.Velocity; + if (windVelocity.magnitudeSquared() > 0.0f && bInternalLocalSpaceSim == 1) + { +#if _DEBUG + bool ok = true; + ok &= mInternalGlobalPose.column0.isNormalized(); + ok &= mInternalGlobalPose.column1.isNormalized(); + ok &= mInternalGlobalPose.column2.isNormalized(); + if (!ok) + { + APEX_DEBUG_WARNING("Internal Global Pose is not normalized (Scale: %f %f %f). Velocities could be wrong.", mInternalGlobalPose.column0.magnitude(), mInternalGlobalPose.column1.magnitude(), mInternalGlobalPose.column2.magnitude()); + } +#endif + PxMat44 invGlobalPose = mInternalGlobalPose.inverseRT(); + windVelocity = invGlobalPose.rotate(windVelocity); + } + + // modify velocities (2.8.x) or set acceleration (3.x) based on wind + writeVelocities |= mClothingSimulation->applyWind(velocities, normals, coeffs, windVelocity, mInternalWindParams.Adaption, simulationDelta); + + // clamp velocities + if (mActorDesc->useVelocityClamping) + { + PxBounds3 velocityClamp(mActorDesc->vertexVelocityClamp); + for (uint32_t i = 0; i < numVertices; i++) + { + PxVec3 velocity = velocities[i]; + velocity.x = PxClamp(velocity.x, velocityClamp.minimum.x, velocityClamp.maximum.x); + velocity.y = PxClamp(velocity.y, velocityClamp.minimum.y, velocityClamp.maximum.y); + velocity.z = PxClamp(velocity.z, velocityClamp.minimum.z, velocityClamp.maximum.z); + velocities[i] = velocity; + } + writeVelocities = true; + } + + if (needsPressure) + { + PX_ALWAYS_ASSERT(); + //writeVelocities = true; + } + + if (mVelocityCallback != NULL) + { + PX_PROFILE_ZONE("ClothingActorImpl::velocityShader", GetInternalApexSDK()->getContextId()); + writeVelocities |= mVelocityCallback->velocityShader(velocities, mClothingSimulation->sdkWritebackPosition, mClothingSimulation->sdkNumDeformableVertices); + } + + if (writeVelocities) + { + if (mClothingScene->mClothingDebugRenderParams->Wind != 0.0f) + { + mWindDebugRendering.clear(); // no memory operation! + + PxVec3* oldVelocities = (PxVec3*)GetInternalApexSDK()->getTempMemory(sizeof(PxVec3) * numVertices); + mClothingSimulation->getVelocities(oldVelocities); + + for (uint32_t i = 0; i < numVertices; i++) + { + mWindDebugRendering.pushBack(velocities[i] - oldVelocities[i]); + } + + GetInternalApexSDK()->releaseTempMemory(oldVelocities); + } + else if (mWindDebugRendering.capacity() > 0) + { + mWindDebugRendering.reset(); + } + mClothingSimulation->setVelocities(velocities); + } + + if (doNotUseWritebackMemory) + { + GetInternalApexSDK()->releaseTempMemory(velocities); + } + } +} + + + +// update the renderproxy to which the data of this frame is written +void ClothingActorImpl::updateRenderProxy() +{ + PX_PROFILE_ZONE("ClothingActorImpl::updateRenderProxy", GetInternalApexSDK()->getContextId()); + PX_ASSERT(mGraphicalMeshes[mCurrentGraphicalLodId].renderProxy == NULL); + + // get a new render proxy from the pool + RenderMeshAssetIntl* renderMeshAsset = mAsset->getGraphicalMesh(mCurrentGraphicalLodId); + ClothingRenderProxyImpl* renderProxy = mClothingScene->getRenderProxy(renderMeshAsset, mActorDesc->fallbackSkinning, mClothingSimulation != NULL, + mOverrideMaterials, mActorDesc->morphGraphicalMeshNewPositions.buf, + &mGraphicalMeshes[mCurrentGraphicalLodId].morphTargetVertexOffsets[0]); + + mGraphicalMeshes[mCurrentGraphicalLodId].renderProxy = renderProxy; +} + + + +ClothingRenderProxy* ClothingActorImpl::acquireRenderProxy() +{ + PX_PROFILE_ZONE("ClothingActorImpl::acquireRenderProxy", GetInternalApexSDK()->getContextId()); + if (!mClothingScene->isSimulating()) // after fetchResults + { + // For consistency, only return the new result after fetchResults. + // During simulation always return the old result + // even if the new result might be ready + waitForFetchResults(); + } + + mRenderProxyMutex.lock(); + ClothingRenderProxyImpl* renderProxy = mRenderProxyReady; + mRenderProxyReady = NULL; + mRenderProxyMutex.unlock(); + + return renderProxy; +} + + + +void ClothingActorImpl::getSimulation(const WriteBackInfo& writeBackInfo) +{ + const uint32_t physicalMeshId = mAsset->getGraphicalLod(mCurrentGraphicalLodId)->physicalMeshId; + +#if defined _DEBUG || PX_CHECKED + BackendFactory* factory = mAsset->getModuleClothing()->getBackendFactory(mBackendName); + PX_UNUSED(factory); // Strange warning fix +#endif + + NvParameterized::Interface* cookingInterface = mActorDesc->runtimeCooked; + + if (cookingInterface != NULL) + { +#if defined _DEBUG || PX_CHECKED + PX_ASSERT(factory->isMatch(cookingInterface->className())); +#endif + } + else + { + cookingInterface = mAsset->getCookedData(mActorDesc->actorScale); +#if defined _DEBUG || PX_CHECKED + PX_ASSERT(factory->isMatch(cookingInterface->className())); +#endif + } + + mClothingSimulation = mAsset->getSimulation(physicalMeshId, cookingInterface, mClothingScene); + if (mClothingSimulation != NULL) + { + mClothingSimulation->reenablePhysX(mActorProxy, mInternalGlobalPose); + + mAsset->updateCollision(mClothingSimulation, mData.mInternalBoneMatricesCur, mCollisionPlanes, mCollisionConvexes, mCollisionSpheres, mCollisionCapsules, mCollisionTriangleMeshes, true); + mClothingSimulation->updateCollisionDescs(mActorDesc->actorDescTemplate, mActorDesc->shapeDescTemplate); + + fillWritebackData_LocksPhysX(writeBackInfo); + + mClothingSimulation->setPositions(mClothingSimulation->sdkWritebackPosition); + + PX_ASSERT(mClothingSimulation->physicalMeshId != 0xffffffff); + PX_ASSERT(mClothingSimulation->submeshId != 0xffffffff); + } + else if (cookingInterface != NULL) + { + // will call fillWritebackData_LocksPhysX + createSimulation(physicalMeshId, cookingInterface, writeBackInfo); + } + + bDirtyClothingTemplate = 1; // updates the clothing desc + + if (mClothingSimulation != NULL) + { + // make sure skinPhysicalMesh does something + bBoneMatricesChanged = 1; + } +} + + + +bool ClothingActorImpl::isCookedDataReady() +{ + // Move getResult of the Cooking Task outside because it would block the other cooking tasks when the actor stopped the simulation. + if (mActiveCookingTask == NULL) + { + return true; + } + + PX_ASSERT(mActorDesc->runtimeCooked == NULL); + mActorDesc->runtimeCooked = mActiveCookingTask->getResult(); + if (mActorDesc->runtimeCooked != NULL) + { + mActiveCookingTask = NULL; // will be deleted by the scene + return true; + } + + if(mAsset->getModuleClothing()->allowAsyncCooking()) + { + mClothingScene->submitCookingTask(NULL); //trigger tasks to be submitted + } + + return false; +} + + + +void ClothingActorImpl::createPhysX_LocksPhysX(float simulationDelta) +{ +#if PX_PHYSICS_VERSION_MAJOR == 3 + if (mPhysXScene == NULL) + { + return; + } +#endif + if (mCurrentSolverIterations == 0) + { + return; + } + + if (mClothingSimulation != NULL) + { + APEX_INTERNAL_ERROR("Physics mesh already created!"); + return; + } + + PX_PROFILE_ZONE("ClothingActorImpl::createPhysX", GetInternalApexSDK()->getContextId()); + + WriteBackInfo writeBackInfo; + writeBackInfo.simulationDelta = simulationDelta; + + getSimulation(writeBackInfo); + + if (mClothingSimulation == NULL) + { + mCurrentSolverIterations = 0; + } + + updateConstraintCoefficients_LocksPhysX(); + + bBoneBufferDirty = 1; +} + + + +void ClothingActorImpl::removePhysX_LocksPhysX() +{ + if (mClothingScene != NULL) + { + if (mClothingSimulation != NULL) + { + mAsset->returnSimulation(mClothingSimulation); + mClothingSimulation = NULL; + } + } + else + { + PX_ASSERT(mClothingSimulation == NULL); + } + + bBoneBufferDirty = 1; +} + + + +void ClothingActorImpl::changePhysicsMesh_LocksPhysX(uint32_t oldGraphicalLodId, float simulationDelta) +{ + PX_ASSERT(mClothingSimulation != NULL); + PX_ASSERT(mClothingScene != NULL); + + WriteBackInfo writeBackInfo; + writeBackInfo.oldSimulation = mClothingSimulation; + writeBackInfo.oldGraphicalLodId = oldGraphicalLodId; + writeBackInfo.simulationDelta = simulationDelta; + + getSimulation(writeBackInfo); // sets mClothingSimulation & will register the sim buffers + writeBackInfo.oldSimulation->swapCollision(mClothingSimulation); + + mAsset->returnSimulation(writeBackInfo.oldSimulation); + writeBackInfo.oldSimulation = NULL; + + updateConstraintCoefficients_LocksPhysX(); + + // make sure skinPhysicalMesh does something + bBoneMatricesChanged = 1; + + // make sure actorData gets updated + reinitActorData(); +} + + + +void ClothingActorImpl::updateCollision_LocksPhysX(bool useInterpolatedMatrices) +{ + if (mClothingSimulation == NULL || (bBoneMatricesChanged == 0 && bGlobalPoseChanged == 0 && bActorCollisionChanged == 0)) + { + return; + } + + PX_ASSERT(mClothingScene != NULL); + + const PxMat44* matrices = useInterpolatedMatrices ? mInternalInterpolatedBoneMatrices : mData.mInternalBoneMatricesCur; + mAsset->updateCollision(mClothingSimulation, matrices, mCollisionPlanes, mCollisionConvexes, mCollisionSpheres, mCollisionCapsules, mCollisionTriangleMeshes, bInternalTeleportDue != ClothingTeleportMode::Continuous); + bActorCollisionChanged = 0; + + if (bDirtyActorTemplate == 1 || bDirtyShapeTemplate == 1) + { + mClothingSimulation->updateCollisionDescs(mActorDesc->actorDescTemplate, mActorDesc->shapeDescTemplate); + + bDirtyActorTemplate = 0; + bDirtyShapeTemplate = 0; + } +} + + + +void ClothingActorImpl::updateConstraintCoefficients_LocksPhysX() +{ + if (mClothingSimulation == NULL) + { + return; + } + + const ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* physicalMesh = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId); + const ClothingPhysicalMeshParametersNS::ConstrainCoefficient_Type* assetCoeffs = physicalMesh->constrainCoefficients.buf; + + const float actorScale = mActorDesc->actorScale; + + const float linearScale = (mInternalMaxDistanceScale.Multipliable ? mInternalMaxDistanceScale.Scale : 1.0f) * actorScale; + const float absoluteScale = mInternalMaxDistanceScale.Multipliable ? 0.0f : (physicalMesh->maximumMaxDistance * (1.0f - mInternalMaxDistanceScale.Scale)); + + const float reduceMaxDistance = mMaxDistReduction + absoluteScale; + + mCurrentMaxDistanceBias = 0.0f; + ClothingMaterialLibraryParametersNS::ClothingMaterial_Type* clothingMaterial = getCurrentClothingMaterial(); + if (clothingMaterial != NULL) + { + mCurrentMaxDistanceBias = clothingMaterial->maxDistanceBias; + } + + mClothingSimulation->setConstrainCoefficients(assetCoeffs, reduceMaxDistance, linearScale, mCurrentMaxDistanceBias, actorScale); + + // change is applied now + bMaxDistanceScaleChanged = 0; +} + + + +void ClothingActorImpl::copyPositionAndNormal_NoPhysX(uint32_t numCopyVertices, SimulationAbstract* oldClothingSimulation) +{ + if (oldClothingSimulation == NULL) + { + return; + } + + PX_ASSERT(numCopyVertices <= mClothingSimulation->sdkNumDeformableVertices && numCopyVertices <= oldClothingSimulation->sdkNumDeformableVertices); + + memcpy(mClothingSimulation->sdkWritebackPosition, oldClothingSimulation->sdkWritebackPosition, sizeof(PxVec3) * numCopyVertices); + + if (mClothingSimulation->sdkWritebackNormal != NULL) + { + memcpy(mClothingSimulation->sdkWritebackNormal, oldClothingSimulation->sdkWritebackNormal, sizeof(PxVec3) * numCopyVertices); + } +} + + + +void ClothingActorImpl::copyAndComputeVelocities_LocksPhysX(uint32_t numCopyVertices, SimulationAbstract* oldClothingSimulation, PxVec3* velocities, float simulationDelta) const +{ + PX_ASSERT(mClothingScene != NULL); + + // copy + if (oldClothingSimulation != NULL && numCopyVertices > 0) + { + oldClothingSimulation->getVelocities(velocities); + } + + // compute velocity from old and current skinned pos + // TODO only skin when bone matrices have changed -> how to use bBoneMatricesChanged in a safe way? + const ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* physicalMesh = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId); + PX_ASSERT(physicalMesh != NULL); + + const uint32_t numVertices = physicalMesh->numSimulatedVertices; + PX_ASSERT(numVertices == mClothingSimulation->sdkNumDeformableVertices); + if (mData.mInternalBoneMatricesCur != NULL && mData.mInternalBoneMatricesPrev != NULL && simulationDelta > 0 && bBoneMatricesChanged == 1) + { + // cpu skinning on the physical mesh + for (uint32_t i = numCopyVertices; i < numVertices; i++) + { + velocities[i] = computeVertexVelFromAnim(i, physicalMesh, simulationDelta); + } + } + else + { + // no bone matrices, just set 0 velocities + memset(velocities + numCopyVertices, 0, sizeof(PxVec3) * (numVertices - numCopyVertices)); + } + + // set the velocities + mClothingSimulation->setVelocities(velocities); +} + + + +void ClothingActorImpl::transferVelocities_LocksPhysX(const SimulationAbstract& oldClothingSimulation, + const ClothingPhysicalMeshParametersNS::SkinClothMapB_Type* pTCMB, + const ClothingPhysicalMeshParametersNS::SkinClothMapD_Type* pTCM, + uint32_t numVerticesInMap, const uint32_t* srcIndices, uint32_t numSrcIndices, uint32_t numSrcVertices, + PxVec3* oldVelocities, PxVec3* newVelocities, float simulationDelta) +{ + oldClothingSimulation.getVelocities(oldVelocities); + + // data for skinning + const ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* physicalMesh = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId); + + // copy velocities + uint32_t vertexIndex = (uint32_t) - 1; + uint32_t idx[3] = + { + (uint32_t) - 1, + (uint32_t) - 1, + (uint32_t) - 1, + }; + for (uint32_t i = 0; i < numVerticesInMap; ++i) + { + if (pTCMB) + { + uint32_t faceIndex = pTCMB[i].faceIndex0; + idx[0] = (faceIndex >= numSrcIndices) ? srcIndices[faceIndex + 0] : (uint32_t) - 1; + idx[1] = (faceIndex >= numSrcIndices) ? srcIndices[faceIndex + 1] : (uint32_t) - 1; + idx[2] = (faceIndex >= numSrcIndices) ? srcIndices[faceIndex + 2] : (uint32_t) - 1; + + vertexIndex = pTCMB[i].vertexIndexPlusOffset; + } + else if (pTCM) + { + idx[0] = pTCM[i].vertexIndex0; + idx[1] = pTCM[i].vertexIndex1; + idx[2] = pTCM[i].vertexIndex2; + vertexIndex = pTCM[i].vertexIndexPlusOffset; + //PX_ASSERT(i == pTCM[i].vertexIndexPlusOffset); + } + else + { + PX_ALWAYS_ASSERT(); + } + + if (vertexIndex >= mClothingSimulation->sdkNumDeformableVertices) + { + continue; + } + + if (idx[0] >= numSrcVertices || idx[1] >= numSrcVertices || idx[2] >= numSrcVertices) + { + // compute from anim + if (mData.mInternalBoneMatricesPrev == NULL || simulationDelta == 0.0f) + { + newVelocities[vertexIndex] = PxVec3(0.0f); + } + else + { + newVelocities[vertexIndex] = computeVertexVelFromAnim(vertexIndex, physicalMesh, simulationDelta); + } + } + else + { + // transfer from old mesh + newVelocities[vertexIndex] = (oldVelocities[idx[0]] + oldVelocities[idx[1]] + oldVelocities[idx[2]]) / 3.0f; + } + } + + mClothingSimulation->setVelocities(newVelocities); +} + + + +PxVec3 ClothingActorImpl::computeVertexVelFromAnim(uint32_t vertexIndex, const ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* physicalMesh, float simulationDelta) const +{ + PX_ASSERT(simulationDelta > 0); + PX_ASSERT(mData.mInternalBoneMatricesCur != NULL); + PX_ASSERT(mData.mInternalBoneMatricesPrev != NULL); + + const PxVec3* positions = physicalMesh->vertices.buf; + Simd4f simulationDeltaV = Simd4fScalarFactory(simulationDelta); + + uint32_t numBoneIndicesPerVertex = physicalMesh->numBonesPerVertex; + const uint16_t* simBoneIndices = physicalMesh->boneIndices.buf; + const float* simBoneWeights = physicalMesh->boneWeights.buf; + + const uint8_t* const optimizationData = physicalMesh->optimizationData.buf; + PX_ASSERT(optimizationData != NULL); + + const uint8_t shift = 4 * (vertexIndex % 2); + const uint8_t numBones = uint8_t((optimizationData[vertexIndex / 2] >> shift) & 0x7); + + Simd4f oldPosV = gSimd4fZero; + Simd4f newPosV = gSimd4fZero; + for (uint32_t j = 0; j < numBones; j++) + { + const Simd4f weightV = Simd4fScalarFactory(simBoneWeights[vertexIndex * numBoneIndicesPerVertex + j]); + + const uint16_t index = simBoneIndices[vertexIndex * numBoneIndicesPerVertex + j]; + PX_ASSERT(index < mAsset->getNumUsedBones()); + const PxMat44& oldBoneV = (PxMat44&)mData.mInternalBoneMatricesPrev[index]; + const PxMat44& boneV = (PxMat44&)mData.mInternalBoneMatricesCur[index]; + + + //oldPos += oldBone * positions[vertexIndex] * weight; + Simd4f pV = applyAffineTransform(oldBoneV, createSimd3f(positions[vertexIndex])); + pV = pV * weightV; + oldPosV = oldPosV + pV; + + //newPos += bone * positions[vertexIndex] * weight; + pV = applyAffineTransform(boneV, createSimd3f(positions[vertexIndex])); + pV = pV * weightV; + newPosV = newPosV + pV; + + } + + Simd4f velV = newPosV - oldPosV; + velV = velV / simulationDeltaV; + + PxVec3 vel; + store3(&vel.x, velV); + return vel; +} + + + +void ClothingActorImpl::createSimulation(uint32_t physicalMeshId, NvParameterized::Interface* cookedData, const WriteBackInfo& writeBackInfo) +{ + PX_ASSERT(mClothingSimulation == NULL); +#if PX_PHYSICS_VERSION_MAJOR == 3 + if (mPhysXScene == NULL) + { + return; + } +#endif + if (bUnsucessfullCreation == 1) + { + return; + } + + PX_PROFILE_ZONE("ClothingActorImpl::SDKCreateClothSoftbody", GetInternalApexSDK()->getContextId()); + + mClothingSimulation = mAsset->getModuleClothing()->getBackendFactory(mBackendName)->createSimulation(mClothingScene, mActorDesc->useHardwareCloth); + + bool success = false; + + if (mClothingSimulation != NULL) + { + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* physicalMesh = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId); + const uint32_t numVertices = physicalMesh->numSimulatedVertices; + const uint32_t numIndices = physicalMesh->numSimulatedIndices; + + mClothingSimulation->init(numVertices, numIndices, true); + + PX_ASSERT(nvidia::strcmp(mAsset->getAssetNvParameterized()->className(), ClothingAssetParameters::staticClassName()) == 0); + const ClothingAssetParameters* assetParams = static_cast<const ClothingAssetParameters*>(mAsset->getAssetNvParameterized()); + mClothingSimulation->initSimulation(assetParams->simulation); + + mClothingSimulation->physicalMeshId = physicalMeshId; + fillWritebackData_LocksPhysX(writeBackInfo); + + uint32_t* indices = NULL; + PxVec3* vertices = NULL; + if (physicalMesh != NULL) + { + indices = physicalMesh->indices.buf; + vertices = physicalMesh->vertices.buf; + } + + success = mClothingSimulation->setCookedData(cookedData, mActorDesc->actorScale); + + mAsset->initCollision(mClothingSimulation, mData.mInternalBoneMatricesCur, mCollisionPlanes, mCollisionConvexes, mCollisionSpheres, mCollisionCapsules, mCollisionTriangleMeshes, mActorDesc, mInternalGlobalPose, bInternalLocalSpaceSim == 1); + + // sets positions to sdkWritebackPosition + ClothingMaterialLibraryParametersNS::ClothingMaterial_Type* clothingMaterial = getCurrentClothingMaterial(); + success &= mClothingSimulation->initPhysics(physicalMeshId, indices, vertices, clothingMaterial, mInternalGlobalPose, mInternalScaledGravity, bInternalLocalSpaceSim == 1); + + if (success) + { + mClothingSimulation->registerPhysX(mActorProxy); + } + + mClothingScene->registerAsset(mAsset); + } + + if (!success) + { + bUnsucessfullCreation = 1; + + if (mClothingSimulation != NULL) + { + PX_DELETE_AND_RESET(mClothingSimulation); + } + } +} + + + +float ClothingActorImpl::getCost() const +{ + ClothingMaterialLibraryParametersNS::ClothingMaterial_Type* clothingMaterial = getCurrentClothingMaterial(); + const uint32_t solverIterations = clothingMaterial != NULL ? clothingMaterial->solverIterations : 5; + + float cost = 0.0f; + if (mClothingSimulation != NULL) + { + cost = static_cast<float>(solverIterations * mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId)->numSimulatedVertices); + } + return cost; +} + + + +uint32_t ClothingActorImpl::getGraphicalMeshIndex(uint32_t lod) const +{ + for (uint32_t i = 1; i < mAsset->getNumGraphicalMeshes(); i++) + { + if (mAsset->getGraphicalLod(i)->lod > lod) + { + return i - 1; + } + } + + // not found, return last index + return mAsset->getNumGraphicalMeshes() - 1; +} + + + +void ClothingActorImpl::lodTick_LocksPhysX(float simulationDelta) +{ + PX_PROFILE_ZONE("ClothingActorImpl::lodTick", GetInternalApexSDK()->getContextId()); + + bool actorCooked = isCookedDataReady(); + + // update graphics lod + // hlanker: active needs a lock if parallel active checks are allowed (like parallel updateRenderResource) + for (uint32_t i = 0; i < mGraphicalMeshes.size(); i++) + { + mGraphicalMeshes[i].active = false; + } + + const uint32_t newGraphicalLodId = getGraphicalMeshIndex(mBufferedGraphicalLod); + + if (newGraphicalLodId >= mGraphicalMeshes.size()) + { + return; + } + + mGraphicalMeshes[newGraphicalLodId].active = true; + + uint32_t oldPhysicalMeshId = mAsset->getGraphicalLod(mCurrentGraphicalLodId)->physicalMeshId; + const bool physicalMeshChanged = oldPhysicalMeshId != mAsset->getGraphicalLod(newGraphicalLodId)->physicalMeshId; + if (physicalMeshChanged) + { + bInternalScaledGravityChanged = 1; + } + + const bool graphicalLodChanged = newGraphicalLodId != mCurrentGraphicalLodId; + const uint32_t oldGraphicalLodId = mCurrentGraphicalLodId; + mCurrentGraphicalLodId = newGraphicalLodId; + + if (mForceSimulation < 0) + { + mForceSimulation = 1; + } + bIsSimulationOn = mForceSimulation > 0? false : true; + + float maxDistReductionTarget = 0.f; + if (graphicalLodChanged) + { + // interrupt blending when switching graphical lod + mMaxDistReduction = maxDistReductionTarget; + } + + // must not enter here if graphical lod changed. otherwise we'll assert in updateConstraintCoefficients because of a pointer mismatch + if (mAsset->getGraphicalLod(mCurrentGraphicalLodId)->physicalMeshId != uint32_t(-1) && !graphicalLodChanged) + { + const ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* physicalMesh = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId); + + // update maxDistReductionTarget + // if there is no simulation bulk, wait with reducing the maxDistReductionTarget + if (mMaxDistReduction != maxDistReductionTarget && mClothingSimulation != NULL) + { + const float maxBlendDistance = physicalMesh->maximumMaxDistance * (mInternalMaxDistanceBlendTime > 0 ? (simulationDelta / mInternalMaxDistanceBlendTime) : 1.0f); + if (mMaxDistReduction == -1.0f) + { + // initialization + mMaxDistReduction = maxDistReductionTarget; + } + else if (PxAbs(maxDistReductionTarget - mMaxDistReduction) < maxBlendDistance) + { + // distance multiplier target reached + mMaxDistReduction = maxDistReductionTarget; + } + else if (bBlendingAllowed == 0) + { + // No blending + mMaxDistReduction = maxDistReductionTarget; + } + else + { + // move towards distance multiplier target + mMaxDistReduction += PxSign(maxDistReductionTarget - mMaxDistReduction) * maxBlendDistance; + } + updateConstraintCoefficients_LocksPhysX(); + } + else if (mCurrentMaxDistanceBias != mClothingMaterial.maxDistanceBias) + { + // update them if the max distance bias changes + updateConstraintCoefficients_LocksPhysX(); + } + else if (bMaxDistanceScaleChanged == 1) + { + updateConstraintCoefficients_LocksPhysX(); + } + } + + + // switch immediately when simulation was switched off or when graphical lod has changed, otherwise wait until finished blending + if (mMaxDistReduction >= maxDistReductionTarget) + { + ClothingMaterialLibraryParametersNS::ClothingMaterial_Type* clothingMaterial = getCurrentClothingMaterial(); + const uint32_t solverIterations = clothingMaterial != NULL ? clothingMaterial->solverIterations : 5; + + uint32_t solverIterationsTarget = solverIterations; + + bool solverIterChanged = (mCurrentSolverIterations != solverIterationsTarget); + mCurrentSolverIterations = solverIterationsTarget; + if (actorCooked && mCurrentSolverIterations > 0) + { + if (mClothingSimulation == NULL) + { + createPhysX_LocksPhysX(simulationDelta); + } + else if (graphicalLodChanged) + { + PX_ASSERT(!physicalMeshChanged || mCurrentGraphicalLodId != oldGraphicalLodId); + changePhysicsMesh_LocksPhysX(oldGraphicalLodId, simulationDelta); + } + + if (solverIterChanged && mClothingSimulation != NULL) + { + mClothingSimulation->setSolverIterations(mCurrentSolverIterations); + } + + bInternalFrozen = bBufferedFrozen; + freeze_LocksPhysX(bInternalFrozen == 1); + bUpdateFrozenFlag = 0; + } + else + { + mCurrentSolverIterations = 0; + + if (!mActorDesc->freezeByLOD) + { + removePhysX_LocksPhysX(); + } + else + { + freeze_LocksPhysX(true); + bInternalFrozen = 1; + bUpdateFrozenFlag = 0; + } + } + } + + // get render proxy for this simulate call + updateRenderProxy(); +} + + + +void ClothingActorImpl::visualizeSkinnedPositions(RenderDebugInterface& renderDebug, float positionRadius, bool maxDistanceOut, bool maxDistanceIn) const +{ +#ifdef WITHOUT_DEBUG_VISUALIZE + PX_UNUSED(renderDebug); + PX_UNUSED(positionRadius); + PX_UNUSED(maxDistanceOut); + PX_UNUSED(maxDistanceIn); +#else + using RENDER_DEBUG::DebugColors; + using RENDER_DEBUG::DebugRenderState; + + if (mClothingSimulation != NULL) + { + const float pointRadius = positionRadius * 0.1f; + PX_ASSERT(mClothingSimulation->skinnedPhysicsPositions != NULL); + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* physicalMesh = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId); + ClothingPhysicalMeshParametersNS::ConstrainCoefficient_Type* coeffs = physicalMesh->constrainCoefficients.buf; + + + const float actorScale = mActorDesc->actorScale; + + const float linearScale = (mInternalMaxDistanceScale.Multipliable ? mInternalMaxDistanceScale.Scale : 1.0f) * actorScale; + const float absoluteScale = mInternalMaxDistanceScale.Multipliable ? 0.0f : (physicalMesh->maximumMaxDistance * (1.0f - mInternalMaxDistanceScale.Scale)); + + const float reduceMaxDistance = mMaxDistReduction + absoluteScale; + + const float maxMotionRadius = (physicalMesh->maximumMaxDistance - reduceMaxDistance) * linearScale; + + const uint32_t colorGreen = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Green); + const uint32_t colorBlue = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Blue); + + const uint32_t numVertices = physicalMesh->numSimulatedVertices; + for (uint32_t i = 0; i < numVertices; i++) + { + const float maxDistance = PxMax(0.0f, coeffs[i].maxDistance - reduceMaxDistance) * linearScale; + uint32_t color; + if (maxDistance < 0.0f) + { + color = colorGreen; + } + else if (maxDistance == 0.0f) + { + color = colorBlue; + } + else + { + uint32_t b = (uint32_t)(255 * maxDistance / maxMotionRadius); + color = (b << 16) + (b << 8) + b; + } + + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(color); + //RENDER_DEBUG_IFACE(&renderDebug)->setCurrentDisplayTime(0.1f); + RENDER_DEBUG_IFACE(&renderDebug)->debugPoint(mClothingSimulation->skinnedPhysicsPositions[i], pointRadius); + //RENDER_DEBUG_IFACE(&renderDebug)->setCurrentDisplayTime(); + + if (maxDistanceOut) + { + RENDER_DEBUG_IFACE(&renderDebug)->debugLine( + mClothingSimulation->skinnedPhysicsPositions[i], + mClothingSimulation->skinnedPhysicsPositions[i] + mClothingSimulation->skinnedPhysicsNormals[i] * maxDistance + ); + } + if (maxDistanceIn) + { + float collDist = PxMax(0.0f, coeffs[i].collisionSphereDistance * actorScale); + //float scaledMaxDist = PxMax(0.0f, maxDistance - reduceMaxDistance); + if (coeffs[i].collisionSphereRadius > 0.0f && collDist < maxDistance) + { + RENDER_DEBUG_IFACE(&renderDebug)->debugLine( + mClothingSimulation->skinnedPhysicsPositions[i] - mClothingSimulation->skinnedPhysicsNormals[i] * collDist, + mClothingSimulation->skinnedPhysicsPositions[i] + ); + } + else + { + RENDER_DEBUG_IFACE(&renderDebug)->debugLine( + mClothingSimulation->skinnedPhysicsPositions[i] - mClothingSimulation->skinnedPhysicsNormals[i] * maxDistance, + mClothingSimulation->skinnedPhysicsPositions[i] + ); + } + } + } + } +#endif +} + + + +void ClothingActorImpl::visualizeSpheres(RenderDebugInterface& renderDebug, const PxVec3* positions, uint32_t numPositions, float radius, uint32_t color, bool wire) const +{ +#ifdef WITHOUT_DEBUG_VISUALIZE + PX_UNUSED(renderDebug); + PX_UNUSED(positions); + PX_UNUSED(numPositions); + PX_UNUSED(radius); + PX_UNUSED(color); + PX_UNUSED(wire); +#else + RENDER_DEBUG_IFACE(&renderDebug)->pushRenderState(); + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(color); + + if (wire) + { + PxMat44 cameraPose = mClothingScene->mApexScene->getViewMatrix(0).inverseRT(); + cameraPose = mInternalGlobalPose.inverseRT() * cameraPose; + PxVec3 cameraPos = cameraPose.getPosition(); + for (uint32_t i = 0; i < numPositions; ++i) + { + // face camera + PxVec3 y = positions[i] - cameraPos; + y.normalize(); + PxPlane p(y, 0.0f); + PxVec3 x = p.project(cameraPose.column0.getXYZ()); + x.normalize(); + PxMat44 pose(x, y, x.cross(y), positions[i]); + + RENDER_DEBUG_IFACE(&renderDebug)->setPose(pose); + RENDER_DEBUG_IFACE(&renderDebug)->debugCircle(PxVec3(0.0f), radius, 2); + } + } + else + { + PxMat44 pose(PxIdentity); + for (uint32_t i = 0; i < numPositions; ++i) + { + pose.setPosition(positions[i]); + RENDER_DEBUG_IFACE(&renderDebug)->debugSphere(pose.getPosition(), radius, 0); + } + } + RENDER_DEBUG_IFACE(&renderDebug)->popRenderState(); +#endif +} + + + +void ClothingActorImpl::visualizeBackstop(RenderDebugInterface& renderDebug) const +{ +#ifdef WITHOUT_DEBUG_VISUALIZE + PX_UNUSED(renderDebug); +#else + using RENDER_DEBUG::DebugColors; + using RENDER_DEBUG::DebugRenderState; + + if (mClothingSimulation != NULL) + { + PX_ASSERT(mClothingSimulation->skinnedPhysicsPositions != NULL); + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* physicalMesh = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId); + ClothingPhysicalMeshParametersNS::ConstrainCoefficient_Type* coeffs = physicalMesh->constrainCoefficients.buf; + uint32_t* indices = physicalMesh->indices.buf; + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Red)); + + const float actorScale = mActorDesc->actorScale; + + if (!physicalMesh->isTetrahedralMesh) + { + // render collision surface as triangle-mesh + const uint32_t colorDarkRed = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::DarkRed); + const uint32_t colorDarkBlue = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::DarkBlue); + + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentState(DebugRenderState::SolidShaded); + for (uint32_t i = 0; i < physicalMesh->numSimulatedIndices; i += 3) + { + PxVec3 p[3]; + + bool show = true; + + for (uint32_t j = 0; j < 3; j++) + { + const uint32_t index = indices[i + j]; + if (coeffs[index].collisionSphereRadius <= 0.0f) + { + show = false; + break; + } + + const float collisionSphereDistance = coeffs[index].collisionSphereDistance * actorScale; + if (collisionSphereDistance < 0.0f) + { + p[j] = mClothingSimulation->skinnedPhysicsPositions[index]; + } + else + { + p[j] = mClothingSimulation->skinnedPhysicsPositions[index] + - (mClothingSimulation->skinnedPhysicsNormals[index] * collisionSphereDistance); + } + } + + if (show) + { + // frontface + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorDarkRed); + RENDER_DEBUG_IFACE(&renderDebug)->debugTri(p[0], p[2], p[1]); + + // backface + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorDarkBlue); + RENDER_DEBUG_IFACE(&renderDebug)->debugTri(p[0], p[1], p[2]); + } + } + } + } +#endif +} + + + +void ClothingActorImpl::visualizeBackstopPrecise(RenderDebugInterface& renderDebug, float scale) const +{ +#ifdef WITHOUT_DEBUG_VISUALIZE + PX_UNUSED(renderDebug); + PX_UNUSED(scale); +#else + using RENDER_DEBUG::DebugColors; + using RENDER_DEBUG::DebugRenderState; + + if (mClothingSimulation != NULL) + { + PX_ASSERT(mClothingSimulation->skinnedPhysicsPositions != NULL); + ClothingPhysicalMeshParametersNS::PhysicalMesh_Type* physicalMesh = mAsset->getPhysicalMeshFromLod(mCurrentGraphicalLodId); + ClothingPhysicalMeshParametersNS::ConstrainCoefficient_Type* coeffs = physicalMesh->constrainCoefficients.buf; + + const float shortestEdgeLength = physicalMesh->averageEdgeLength * 0.5f * scale; + + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentState(DebugRenderState::SolidShaded); + + const uint32_t colorRed = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Red); + const uint32_t colorBlue = RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::Blue); + + const float actorScale = mActorDesc->actorScale; + + for (uint32_t i = 0; i < mClothingSimulation->sdkNumDeformableVertices; i++) + { + if (coeffs[i].collisionSphereRadius <= 0.0f) + { + continue; + } + + PxVec3 skinnedPosition = mClothingSimulation->skinnedPhysicsPositions[i]; + if (coeffs[i].collisionSphereDistance > 0.0f) + { + skinnedPosition -= mClothingSimulation->skinnedPhysicsNormals[i] * (coeffs[i].collisionSphereDistance * actorScale); + } + + const float collisionSphereRadius = coeffs[i].collisionSphereRadius * actorScale; + + const PxVec3 sphereCenter = skinnedPosition - mClothingSimulation->skinnedPhysicsNormals[i] * collisionSphereRadius; + + PxVec3 centerToSim = mClothingSimulation->sdkWritebackPosition[i] - sphereCenter; + centerToSim.normalize(); + PxVec3 right = centerToSim.cross(PxVec3(0.0f, 1.0f, 0.0f)); + PxVec3 up = right.cross(centerToSim); + PxVec3 target = sphereCenter + centerToSim * collisionSphereRadius; + + right *= shortestEdgeLength; + up *= shortestEdgeLength; + + const float r = collisionSphereRadius; + const float back = r - sqrtf(r * r - shortestEdgeLength * shortestEdgeLength); + + // move the verts a bit back such that they are on the sphere + centerToSim *= back; + + PxVec3 l1 = target + right - centerToSim; + PxVec3 l2 = target + up - centerToSim; + PxVec3 l3 = target - right - centerToSim; + PxVec3 l4 = target - up - centerToSim; + + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorRed); + RENDER_DEBUG_IFACE(&renderDebug)->debugTri(target, l1, l2); + RENDER_DEBUG_IFACE(&renderDebug)->debugTri(target, l2, l3); + RENDER_DEBUG_IFACE(&renderDebug)->debugTri(target, l3, l4); + RENDER_DEBUG_IFACE(&renderDebug)->debugTri(target, l4, l1); +#if 1 + // PH: also render backfaces, in blue + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(colorBlue); + RENDER_DEBUG_IFACE(&renderDebug)->debugTri(target, l1, l4); + RENDER_DEBUG_IFACE(&renderDebug)->debugTri(target, l4, l3); + RENDER_DEBUG_IFACE(&renderDebug)->debugTri(target, l3, l2); + RENDER_DEBUG_IFACE(&renderDebug)->debugTri(target, l2, l1); +#endif + } + } +#endif +} + + + +void ClothingActorImpl::visualizeBoneConnections(RenderDebugInterface& renderDebug, const PxVec3* positions, const uint16_t* boneIndices, + const float* boneWeights, uint32_t numBonesPerVertex, uint32_t numVertices) const +{ +#ifdef WITHOUT_DEBUG_VISUALIZE + PX_UNUSED(renderDebug); + PX_UNUSED(positions); + PX_UNUSED(boneIndices); + PX_UNUSED(boneWeights); + PX_UNUSED(numBonesPerVertex); + PX_UNUSED(numVertices); +#else + const PxMat44* matrices = mData.mInternalBoneMatricesCur; + if (matrices == NULL) + { + return; + } + + for (uint32_t i = 0; i < numVertices; i++) + { + // skin the vertex + PxVec3 pos(0.0f); + for (uint32_t j = 0; j < numBonesPerVertex; j++) + { + float boneWeight = (boneWeights == NULL) ? 1.0f : boneWeights[i * numBonesPerVertex + j]; + if (boneWeight > 0.0f) + { + uint32_t boneIndex = boneIndices[i * numBonesPerVertex + j]; + pos += matrices[boneIndex].transform(positions[i]) * boneWeight; + } + } + + // draw the lines to the bones + for (uint32_t j = 0; j < numBonesPerVertex; j++) + { + float boneWeight = (boneWeights == NULL) ? 1.0f : boneWeights[i * numBonesPerVertex + j]; + if (boneWeight > 0.0f) + { + uint32_t boneIndex = boneIndices[i * numBonesPerVertex + j]; + uint32_t b = (uint32_t)(255 * boneWeight); + uint32_t color = (b << 16) + (b << 8) + b; + RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(color); + RENDER_DEBUG_IFACE(&renderDebug)->debugLine(pos, matrices[boneIndex].transform(mAsset->getBoneBindPose(boneIndex).getPosition())); + } + } + } +#endif +} + + + +// collision functions +ClothingPlane* ClothingActorImpl::createCollisionPlane(const PxPlane& plane) +{ + ClothingPlane* actorPlane = NULL; + actorPlane = PX_NEW(ClothingPlaneImpl)(mCollisionPlanes, *this, plane); + PX_ASSERT(actorPlane != NULL); + bActorCollisionChanged = true; + return actorPlane; +} + +ClothingConvex* ClothingActorImpl::createCollisionConvex(ClothingPlane** planes, uint32_t numPlanes) +{ + if (numPlanes < 3) + return NULL; + + ClothingConvex* convex = NULL; + convex = PX_NEW(ClothingConvexImpl)(mCollisionConvexes, *this, planes, numPlanes); + PX_ASSERT(convex != NULL); + bActorCollisionChanged = true; + + return convex; +} + +ClothingSphere* ClothingActorImpl::createCollisionSphere(const PxVec3& position, float radius) +{ + + ClothingSphere* actorSphere = NULL; + actorSphere = PX_NEW(ClothingSphereImpl)(mCollisionSpheres, *this, position, radius); + PX_ASSERT(actorSphere != NULL); + bActorCollisionChanged = true; + return actorSphere; +} + +ClothingCapsule* ClothingActorImpl::createCollisionCapsule(ClothingSphere& sphere1, ClothingSphere& sphere2) +{ + ClothingCapsule* actorCapsule = NULL; + actorCapsule = PX_NEW(ClothingCapsuleImpl)(mCollisionCapsules, *this, sphere1, sphere2); + PX_ASSERT(actorCapsule != NULL); + bActorCollisionChanged = true; + return actorCapsule; +} + +ClothingTriangleMesh* ClothingActorImpl::createCollisionTriangleMesh() +{ + ClothingTriangleMesh* triMesh = NULL; + triMesh = PX_NEW(ClothingTriangleMeshImpl)(mCollisionTriangleMeshes, *this); + PX_ASSERT(triMesh != NULL); + bActorCollisionChanged = true; + return triMesh; +} + + +void ClothingActorImpl::releaseCollision(ClothingCollisionImpl& collision) +{ + bActorCollisionChanged = 1; + if (mClothingSimulation != NULL) + { + mClothingSimulation->releaseCollision(collision); + } + collision.destroy(); +} + +#if APEX_UE4 +void ClothingActorImpl::simulate(PxF32 dt) +{ + // before tick task + tickSynchBeforeSimulate_LocksPhysX(dt, dt, 0, 1); + + if (mClothingSimulation != NULL) + mClothingSimulation->simulate(dt); + + // during tick task + tickAsynch_NoPhysX(); // this is a no-op + + // start fetch result task + mFetchResultsRunningMutex.lock(); + mFetchResultsRunning = true; + mFetchResultsSync.reset(); + mFetchResultsRunningMutex.unlock(); + + // fetch result task + fetchResults(); + getActorData().tickSynchAfterFetchResults_LocksPhysX(); // this is a no-op + setFetchResultsSync(); +} +#endif + +} +} // namespace nvidia + |