aboutsummaryrefslogtreecommitdiff
path: root/APEX_1.4/shared/external/src/ClothingAuthoring.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'APEX_1.4/shared/external/src/ClothingAuthoring.cpp')
-rw-r--r--APEX_1.4/shared/external/src/ClothingAuthoring.cpp5634
1 files changed, 5634 insertions, 0 deletions
diff --git a/APEX_1.4/shared/external/src/ClothingAuthoring.cpp b/APEX_1.4/shared/external/src/ClothingAuthoring.cpp
new file mode 100644
index 00000000..fefb4727
--- /dev/null
+++ b/APEX_1.4/shared/external/src/ClothingAuthoring.cpp
@@ -0,0 +1,5634 @@
+/*
+ * Copyright (c) 2008-2015, NVIDIA CORPORATION. All rights reserved.
+ *
+ * NVIDIA CORPORATION and its licensors retain all intellectual property
+ * and proprietary rights in and to this software, related documentation
+ * and any modifications thereto. Any use, reproduction, disclosure or
+ * distribution of this software and related documentation without an express
+ * license agreement from NVIDIA CORPORATION is strictly prohibited.
+ */
+
+#include "ApexDefs.h"
+#if PX_WINDOWS_FAMILY && PX_PHYSICS_VERSION_MAJOR == 3
+
+#include "ClothingAuthoring.h"
+
+#include <cstdio>
+#include <stdarg.h>
+#define NOMINMAX
+#include <windows.h>
+
+// for std::sort
+#include <algorithm>
+
+// PhysX foundation
+#include <PsMathUtils.h>
+#include <PsTime.h>
+
+// PhysX
+#include "extensions/PxDefaultCpuDispatcher.h"
+#include "extensions/PxDefaultSimulationFilterShader.h"
+#include <PxPhysics.h>
+#include <PxScene.h>
+#include <PxRigidStatic.h>
+#include "geometry/PxPlaneGeometry.h"
+//#endif
+#include "ApexCudaContextManager.h"
+#include "PxCudaContextManager.h"
+
+// APEX Framework
+#include <Apex.h>
+#include <RenderDebugInterface.h>
+#include <nvparameterized/NvParamUtils.h>
+#include <CustomBufferIterator.h>
+#include <PsArray.h>
+
+// APEX Clothing
+#include <clothing/ClothingActor.h>
+#include <clothing/ClothingAsset.h>
+#include <clothing/ClothingAssetAuthoring.h>
+#include <clothing/ClothingPhysicalMesh.h>
+#include <clothing/ClothingPreview.h>
+#include <clothing/ModuleClothing.h>
+#include <clothing/ClothingCollision.h>
+
+// shared::external
+#include "BestFit.h"
+#include "MeshPainter.h"
+#include "SkeletalAnim.h"
+#include "TriangleMesh.h"
+#include "PxMath.h"
+
+// shared::general
+#include <AutoGeometry.h>
+
+// MeshImport
+#include <MeshImport.h>
+
+//PVD
+#include "PxPvd.h"
+#include "PxPvdTransport.h"
+
+#ifdef _DEBUG
+#define VERIFY_PARAM(_A) PX_ASSERT(_A == NvParameterized::ERROR_NONE)
+#else
+#define VERIFY_PARAM(_A) _A
+#endif
+
+#define ASSERT_TRUE(x) { if(!x) PX_ASSERT(0); }
+#define EXPECT_TRUE(x) { if(!x) PX_ASSERT(0); }
+
+#pragma warning(disable:4127) // Disable the nag warning 'conditional expression is constant'
+
+typedef uint32_t uint32_t;
+typedef physx::PxVec3 PxVec3;
+typedef physx::PxBounds3 PxBounds3;
+
+namespace SharedTools
+{
+const unsigned int SIMPLIFICATOR_MAX_STEPS = 100000;
+
+class ProgressCallback : public nvidia::apex::IProgressListener
+{
+public:
+ ProgressCallback(int totalWork, nvidia::apex::IProgressListener* parent) :
+ m_work(0), m_subtaskWork(0), m_totalWork(totalWork), m_taskName(NULL), m_parent(parent) {}
+
+ void setSubtaskWork(int subtaskWork, const char* taskName = NULL)
+ {
+ if (subtaskWork < 0)
+ {
+ subtaskWork = m_totalWork - m_work;
+ }
+
+ m_subtaskWork = subtaskWork;
+ PX_ASSERT(m_work + m_subtaskWork <= m_totalWork);
+ m_taskName = taskName;
+ setProgress(0, m_taskName);
+ }
+
+ void completeSubtask()
+ {
+ setProgress(100, m_taskName);
+ m_work += m_subtaskWork;
+ m_subtaskWork = 0;
+ }
+
+ void setProgress(int progress, const char* taskName = NULL)
+ {
+ PX_ASSERT(progress >= 0);
+ PX_ASSERT(progress <= 100);
+
+ if (taskName == NULL)
+ {
+ taskName = m_taskName;
+ }
+
+ if (m_parent != NULL)
+ {
+ m_parent->setProgress(m_totalWork > 0 ? (m_work * 100 + m_subtaskWork * progress) / m_totalWork : 100, taskName);
+ }
+ }
+
+protected:
+ int m_work;
+ int m_subtaskWork;
+ int m_totalWork;
+ const char* m_taskName;
+ nvidia::apex::IProgressListener* m_parent;
+};
+
+
+void ClothingAuthoring::Simulation::ClothingAsset::releaseRenderMeshAssets()
+{
+ for (size_t i = 0; i < renderMeshAssets.size(); ++i)
+ {
+ if (renderMeshAssets[i] != NULL)
+ {
+ renderMeshAssets[i]->release();
+ }
+ }
+}
+
+
+ClothingAuthoring::ClothingAuthoring(nvidia::apex::ApexSDK* apexSDK,
+ nvidia::apex::ModuleClothing* moduleClothing,
+ nvidia::apex::ResourceCallback* resourceCallback,
+ nvidia::apex::UserRenderResourceManager* renderResourceManager,
+ ClothingAuthoring::ErrorCallback* errorCallback,
+ nvidia::PxPvd* pvd,
+ nvidia::apex::RenderDebugInterface* renderDebug) :
+
+ _mApexSDK(apexSDK),
+ _mModuleClothing(moduleClothing),
+ _mApexRenderDebug(renderDebug),
+ _mResourceCallback(resourceCallback),
+ _mRenderResourceManager(renderResourceManager),
+ _mMaterialList(NULL),
+ _mErrorCallback(errorCallback),
+ mPvd(pvd),
+ mCurrentActorDesc(0)
+{
+ mSimulation.init();
+ mMeshes.init();
+ mAuthoringObjects.init();
+ resetTempConfiguration();
+
+ initConfiguration();
+ prepareConfiguration();
+
+ nvidia::apex::SceneDesc sceneDesc;
+
+#if APEX_CUDA_SUPPORT
+ // create cuda context manager
+ if (mSimulation.cudaContextManager == NULL)
+ {
+ physx::PxCudaContextManagerDesc cudaContextManagerDesc;
+ physx::PxErrorCallback* eclbk = _mApexSDK->getErrorCallback();
+ if (mSimulation.cudaContextManager == NULL &&
+ eclbk != NULL &&
+ -1 != nvidia::apex::GetSuggestedCudaDeviceOrdinal(*eclbk))
+ {
+ mSimulation.cudaContextManager = nvidia::apex::CreateCudaContextManager(cudaContextManagerDesc, *eclbk);
+ }
+ }
+#endif
+
+
+#if PX_PHYSICS_VERSION_MAJOR == 0
+ mSimulation.cpuDispatcher = _mApexSDK->createCpuDispatcher(2);
+ sceneDesc.cpuDispatcher = mSimulation.cpuDispatcher;
+ if (mSimulation.cudaContextManager != NULL)
+ {
+ sceneDesc.gpuDispatcher = mSimulation.cudaContextManager->getGpuDispatcher();
+ }
+#elif PX_PHYSICS_VERSION_MAJOR == 3
+ mSimulation.cpuDispatcher = physx::PxDefaultCpuDispatcherCreate(2);
+#endif
+
+ sceneDesc.debugInterface = renderDebug;
+ mSimulation.apexScene = _mApexSDK->createScene(sceneDesc);
+
+
+ if (mSimulation.apexScene == NULL)
+ {
+ if (_mErrorCallback != NULL)
+ {
+ _mErrorCallback->reportError("Scene Creation", "Error: Unable to initialize APEX Scene.");
+ }
+ }
+ else
+ {
+ mSimulation.apexScene->allocViewMatrix(nvidia::apex::ViewMatrixType::LOOK_AT_RH);
+ mSimulation.apexScene->allocProjMatrix(nvidia::apex::ProjMatrixType::USER_CUSTOMIZED);
+ }
+
+}
+
+
+
+ClothingAuthoring::~ClothingAuthoring()
+{
+ releasePhysX();
+
+#if APEX_CUDA_SUPPORT
+ if (mSimulation.cudaContextManager != NULL)
+ {
+ mSimulation.cudaContextManager->release();
+ mSimulation.cudaContextManager = NULL;
+ }
+#endif
+
+ resetTempConfiguration();
+
+ mMeshes.clear(_mRenderResourceManager, _mResourceCallback, true);
+}
+
+
+void ClothingAuthoring::addNotifyCallback(NotificationCallback* callback)
+{
+ _mNotifyCallbacks.push_back(callback);
+}
+
+
+
+void ClothingAuthoring::setMaterialList(Samples::MaterialList* list)
+{
+ _mMaterialList = list;
+}
+
+
+
+void ClothingAuthoring::releasePhysX()
+{
+ for (size_t i = 0; i < mSimulation.actors.size(); i++)
+ {
+ for (size_t j = 0; j < mSimulation.actors[i].actorGroundPlanes.size(); j++)
+ {
+ if (mSimulation.actors[i].actorGroundPlanes[j] != NULL)
+ {
+ mSimulation.actors[i].actorGroundPlanes[j]->release();
+ mSimulation.actors[i].actorGroundPlanes[j] = NULL;
+ }
+ }
+
+ for (size_t j = 0; j < mSimulation.actors[i].actors.size(); j++)
+ {
+ if (mSimulation.actors[i].actors[j] != NULL)
+ {
+ mSimulation.actors[i].actors[j]->release();
+ mSimulation.actors[i].actors[j] = NULL;
+ }
+ }
+
+ for (size_t j = 0; j < mSimulation.actors[i].previews.size(); j++)
+ {
+ if (mSimulation.actors[i].previews[j] != NULL)
+ {
+ mSimulation.actors[i].previews[j]->release();
+ mSimulation.actors[i].previews[j] = NULL;
+ }
+ }
+
+ if (mSimulation.actors[i].triangleMesh != NULL)
+ {
+ mSimulation.actors[i].triangleMesh->clear(_mRenderResourceManager, _mResourceCallback);
+ delete mSimulation.actors[i].triangleMesh;
+ mSimulation.actors[i].triangleMesh = NULL;
+ }
+ }
+ mSimulation.actors.clear();
+
+ for (size_t i = 0; i < mSimulation.assets.size(); i++)
+ {
+ mSimulation.assets[i].releaseRenderMeshAssets();
+ mSimulation.assets[i].apexAsset->release();
+ mSimulation.assets[i].apexAsset = NULL;
+ }
+ mSimulation.assets.clear();
+
+ if (mSimulation.apexScene != NULL)
+ {
+ mSimulation.apexScene->release();
+ mSimulation.apexScene = NULL;
+ }
+
+ if (mSimulation.cpuDispatcher != NULL)
+ {
+#if PX_PHYSICS_VERSION_MAJOR == 0
+ _mApexSDK->releaseCpuDispatcher(*mSimulation.cpuDispatcher);
+#elif PX_PHYSICS_VERSION_MAJOR == 3
+ delete mSimulation.cpuDispatcher;
+#endif
+ mSimulation.cpuDispatcher = NULL;
+ }
+
+ if (mSimulation.groundPlane != NULL)
+ {
+ PX_ASSERT(mSimulation.physxScene != NULL);
+ mSimulation.groundPlane->release();
+ mSimulation.groundPlane = NULL;
+ }
+
+ if (mSimulation.physxScene != NULL)
+ {
+ mSimulation.physxScene->release();
+ mSimulation.physxScene = NULL;
+ }
+
+ PX_ASSERT(_mApexSDK->getPhysXSDK()->getNbClothFabrics() == 0);
+
+ mAuthoringObjects.clear();
+ clearMaterialLibraries();
+ clearLoadedActorDescs();
+}
+
+
+void ClothingAuthoring::connectPVD(bool toggle)
+{
+ nvidia::PxPvdInstrumentationFlags flags;
+ if (mConfig.simulation.pvdDebug)
+ {
+ flags |= nvidia::PxPvdInstrumentationFlag::eDEBUG;
+ }
+ if (mConfig.simulation.pvdProfile)
+ {
+ flags |= nvidia::PxPvdInstrumentationFlag::ePROFILE;
+ }
+ if (mConfig.simulation.pvdMemory)
+ {
+ flags |= nvidia::PxPvdInstrumentationFlag::eMEMORY;
+ }
+
+ if (mPvd != NULL)
+ {
+ stopSimulation();
+ if (mPvd->isConnected())
+ {
+ if (mState.needsReconnect)
+ {
+ mPvd->disconnect();
+ }
+ else if (toggle)
+ {
+ mPvd->disconnect();
+ return;
+ }
+ }
+ else
+ {
+ physx::PxPvdTransport* transport = physx::PxDefaultPvdSocketTransportCreate("127.0.0.1", 5425, 10u);
+ if (transport == NULL)
+ {
+ return;
+ }
+ mPvd->connect(*transport, flags);
+ }
+ }
+ else
+ {
+ mPvd = physx::PxCreatePvd(PxGetFoundation());
+
+ if (!mPvd->isConnected())
+ {
+ connectPVD(false);
+ }
+ }
+}
+
+
+
+int ClothingAuthoring::getNumParameters() const
+{
+ return (int)(mFloatConfiguration.size() + mIntConfiguration.size() + mBoolConfiguration.size());
+}
+
+
+
+bool ClothingAuthoring::getParameter(unsigned int i, std::string& name, std::string& type, std::string& val) const
+{
+ if (i < mFloatConfiguration.size())
+ {
+ std::map<std::string, float*>::const_iterator it;
+ for (it = mFloatConfiguration.begin(); i > 0 && it != mFloatConfiguration.end(); ++it, --i)
+ {
+ ;
+ }
+
+ PX_ASSERT(it != mFloatConfiguration.end());
+ name = it->first;
+ type = "float";
+ char buf[16];
+ sprintf_s(buf, 16, "%.9g", *it->second);
+ val = buf;
+ return true;
+ }
+ i -= (int)mFloatConfiguration.size();
+
+ if (i < mIntConfiguration.size())
+ {
+ std::map<std::string, int*>::const_iterator it;
+ for (it = mIntConfiguration.begin(); i > 0 && it != mIntConfiguration.end(); ++it, --i)
+ {
+ ;
+ }
+
+ PX_ASSERT(it != mIntConfiguration.end());
+ name = it->first;
+ type = "int";
+ char buf[16];
+ sprintf_s(buf, 16, "%d", *it->second);
+ val = buf;
+ return true;
+ }
+ i -= (int)mIntConfiguration.size();
+
+ if (i < mBoolConfiguration.size())
+ {
+ std::map<std::string, bool*>::const_iterator it;
+ for (it = mBoolConfiguration.begin(); i > 0 && it != mBoolConfiguration.end(); ++it, --i)
+ {
+ ;
+ }
+
+ PX_ASSERT(it != mBoolConfiguration.end());
+ name = it->first;
+ type = "bool";
+ val = *it->second ? "true" : "false";
+ return true;
+ }
+
+ return false;
+}
+
+
+
+bool ClothingAuthoring::setParameter(std::string& name, std::string& type, std::string& val)
+{
+ if (type == "float")
+ {
+ std::map<std::string, float*>::iterator it = mFloatConfiguration.find(name);
+
+ if (it != mFloatConfiguration.end())
+ {
+ *(it->second) = (float)atof(val.c_str());
+ return true;
+ }
+ else
+ {
+ it = mFloatConfigurationOld.find(name);
+ if (it != mFloatConfigurationOld.end())
+ {
+ *(it->second) = (float)atof(val.c_str());
+ return true;
+ }
+ }
+ }
+
+ if (type == "int")
+ {
+ std::map<std::string, int*>::iterator it = mIntConfiguration.find(name);
+
+ if (it != mIntConfiguration.end())
+ {
+ *(it->second) = atoi(val.c_str());
+
+ if (it->first == "mConfig.mesh.textureUvOrigin")
+ {
+ setTextureUvOrigin((nvidia::apex::TextureUVOrigin::Enum) * (it->second));
+ }
+ return true;
+ }
+ else
+ {
+ it = mIntConfigurationOld.find(name);
+ if (it != mIntConfigurationOld.end())
+ {
+ *(it->second) = atoi(val.c_str());
+ return true;
+ }
+ }
+ }
+
+ if (type == "bool")
+ {
+ std::map<std::string, bool*>::iterator it = mBoolConfiguration.find(name);
+
+ if (it != mBoolConfiguration.end())
+ {
+ *(it->second) = (val == "true");
+ return true;
+ }
+ else
+ {
+ it = mBoolConfigurationOld.find(name);
+ if (it != mBoolConfigurationOld.end())
+ {
+ *(it->second) = (val == "true");
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+
+bool ClothingAuthoring::createDefaultMaterialLibrary()
+{
+ if (!mState.materialLibraries.empty())
+ {
+ CurrentState::tMaterialLibraries::iterator found;
+
+ // Assert that each library has one material!
+ for (found = mState.materialLibraries.begin(); found != mState.materialLibraries.end(); ++found)
+ {
+ NvParameterized::Interface* materialLibrary = found->second;
+ PX_ASSERT(materialLibrary != NULL);
+
+ NvParameterized::Handle arrayHandle(materialLibrary);
+
+ NvParameterized::ErrorType error = NvParameterized::ERROR_NONE;
+ error = arrayHandle.getParameter("materials");
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ int numMaterials = 0;
+ error = arrayHandle.getArraySize(numMaterials);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ if (numMaterials == 0)
+ {
+ numMaterials = 1;
+ addMaterialToLibrary(found->first.c_str(), "Default");
+ }
+ }
+
+ // select one material
+ found = mState.materialLibraries.find(mState.selectedMaterialLibrary);
+ if (found == mState.materialLibraries.end())
+ {
+ found = mState.materialLibraries.begin();
+ mState.selectedMaterialLibrary = found->first;
+ }
+
+ NvParameterized::Interface* materialLibrary = found->second;
+ PX_ASSERT(materialLibrary != NULL);
+
+ NvParameterized::Handle arrayHandle(materialLibrary);
+
+ NvParameterized::ErrorType error = NvParameterized::ERROR_NONE;
+ error = arrayHandle.getParameter("materials");
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ int numMaterials = 0;
+ error = arrayHandle.getArraySize(numMaterials);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ const char* firstMaterialName = NULL;
+ for (int i = 0; i < numMaterials; i++)
+ {
+ error = arrayHandle.set(i);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ NvParameterized::Handle nameHandle(materialLibrary);
+ error = arrayHandle.getChildHandle(materialLibrary, "materialName", nameHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ const char* materialName = NULL;
+ error = nameHandle.getParamString(materialName);
+
+ if (firstMaterialName == NULL)
+ {
+ firstMaterialName = materialName;
+ }
+
+ if (mState.selectedMaterial == materialName)
+ {
+ return false; // we found an existing material
+ }
+
+ arrayHandle.popIndex();
+ }
+
+ PX_ASSERT(firstMaterialName != NULL);
+ mState.selectedMaterial = firstMaterialName;
+
+ return false;
+ }
+
+ mState.selectedMaterialLibrary = "Default";
+ mState.selectedMaterial = "Default";
+
+ addMaterialLibrary("Default", NULL);
+ addMaterialToLibrary("Default", "Default");
+ addMaterialToLibrary("Default", "HS Limited");
+ addMaterialToLibrary("Default", "Low Gravity");
+
+ mDirty.workspace = true;
+
+ PX_ASSERT(mState.materialLibraries.begin()->first == "Default");
+ NvParameterized::Interface* materialLibrary = mState.materialLibraries.begin()->second;
+
+ NvParameterized::Handle arrayHandle(materialLibrary);
+
+ NvParameterized::ErrorType error = NvParameterized::ERROR_NONE;
+ error = arrayHandle.getParameter("materials");
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ int numMaterials = 0;
+ error = arrayHandle.getArraySize(numMaterials);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ PX_ASSERT(numMaterials == 3);
+
+ NvParameterized::Handle elementHandle(materialLibrary);
+
+ for (int i = 0; i < numMaterials; i++)
+ {
+ arrayHandle.set(i);
+
+ if (i == 2)
+ {
+ error = arrayHandle.getChildHandle(materialLibrary, "gravityScale", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(0.2f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ }
+
+ if (i == 1)
+ {
+ error = arrayHandle.getChildHandle(materialLibrary, "hardStretchLimitation", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(1.03f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ }
+
+ arrayHandle.popIndex();
+ }
+
+ return true;
+}
+
+
+
+bool ClothingAuthoring::addMaterialLibrary(const char* name, NvParameterized::Interface* newInterface)
+{
+ PX_ASSERT(newInterface == NULL || ::strcmp(newInterface->className(), "ClothingMaterialLibraryParameters") == 0);
+
+ CurrentState::tMaterialLibraries::iterator found = mState.materialLibraries.find(name);
+ if (found != mState.materialLibraries.end())
+ {
+ if (newInterface == NULL)
+ {
+ return true;
+ }
+
+ if (_mErrorCallback != NULL)
+ {
+ _mErrorCallback->reportErrorPrintf("Material Loading", "Material \'%s\' already exists, didn't overwrite", name);
+ }
+ return false;
+ }
+ else
+ {
+ if (newInterface == NULL)
+ {
+ mState.materialLibraries[name] = _mApexSDK->getParameterizedTraits()->createNvParameterized("ClothingMaterialLibraryParameters");
+ }
+ else
+ {
+ mState.materialLibraries[name] = newInterface;
+ }
+ }
+ return true;
+}
+
+
+
+void ClothingAuthoring::removeMaterialLibrary(const char* name)
+{
+ CurrentState::tMaterialLibraries::iterator found = mState.materialLibraries.find(name);
+ if (found != mState.materialLibraries.end())
+ {
+ found->second->destroy();
+ mState.materialLibraries.erase(found);
+ }
+
+ createDefaultMaterialLibrary();
+}
+
+
+
+void ClothingAuthoring::addMaterialToLibrary(const char* libName, const char* matName)
+{
+ // here starts the fun
+ CurrentState::tMaterialLibraries::iterator found = mState.materialLibraries.find(libName);
+ if (found == mState.materialLibraries.end())
+ {
+ return;
+ }
+
+ NvParameterized::ErrorType error = NvParameterized::ERROR_NONE;
+
+ NvParameterized::Interface* materialLibrary = found->second;
+ NvParameterized::Handle arrayHandle(materialLibrary);
+ error = arrayHandle.getParameter("materials");
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ int oldSize = 0;
+ error = arrayHandle.getArraySize(oldSize);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = arrayHandle.resizeArray(oldSize + 1);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.set(oldSize);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ NvParameterized::Handle elementHandle(materialLibrary);
+ error = arrayHandle.getChildHandle(materialLibrary, "materialName", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamString(matName);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "verticalStretchingStiffness", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(1.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "horizontalStretchingStiffness", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(1.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "bendingStiffness", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(0.5f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "shearingStiffness", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(0.5f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "tetherStiffness", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(1.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "tetherLimit", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(1.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "orthoBending", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamBool(false);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "verticalStiffnessScaling.compressionRange", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(1.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "verticalStiffnessScaling.stretchRange", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(1.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "verticalStiffnessScaling.scale", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(0.5f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "horizontalStiffnessScaling.compressionRange", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(1.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "horizontalStiffnessScaling.stretchRange", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(1.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "horizontalStiffnessScaling.scale", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(0.5f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "bendingStiffnessScaling.compressionRange", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(1.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "bendingStiffnessScaling.stretchRange", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(1.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "bendingStiffnessScaling.scale", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(0.5f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "shearingStiffnessScaling.compressionRange", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(1.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "shearingStiffnessScaling.stretchRange", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(1.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "shearingStiffnessScaling.scale", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(0.5f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "damping", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(0.1f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "stiffnessFrequency", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(100.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "comDamping", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamBool(false);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "friction", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(0.25f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "massScale", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(0.25f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "solverIterations", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamU32(5);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "solverFrequency", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(250.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "gravityScale", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(1.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "inertiaScale", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(1.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "hardStretchLimitation", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(0.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "maxDistanceBias", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(0.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "hierarchicalSolverIterations", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamU32(0);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "selfcollisionThickness", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(0.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ error = arrayHandle.getChildHandle(materialLibrary, "selfcollisionStiffness", elementHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ error = elementHandle.setParamF32(1.0f);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ arrayHandle.popIndex();
+}
+
+
+
+void ClothingAuthoring::removeMaterialFromLibrary(const char* libName, const char* matName)
+{
+ // here the real fun starts
+ CurrentState::tMaterialLibraries::iterator found = mState.materialLibraries.find(libName);
+ if (found == mState.materialLibraries.end())
+ {
+ return;
+ }
+
+ NvParameterized::ErrorType error = NvParameterized::ERROR_NONE;
+
+ NvParameterized::Interface* materialLibrary = found->second;
+ NvParameterized::Handle arrayHandle(materialLibrary);
+ error = arrayHandle.getParameter("materials");
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ int numMaterials = 0;
+ error = arrayHandle.getArraySize(numMaterials);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ int deleteIndex = -1;
+
+ for (int i = 0; i < numMaterials; i++)
+ {
+ error = arrayHandle.set(i);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ NvParameterized::Handle stringHandle(materialLibrary);
+ error = arrayHandle.getChildHandle(materialLibrary, "materialName", stringHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ const char* currentMaterialName = NULL;
+ error = stringHandle.getParamString(currentMaterialName);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ if (::strcmp(currentMaterialName, matName) == 0)
+ {
+ PX_ASSERT(deleteIndex == -1); // should only find 1!!
+ deleteIndex = i;
+ }
+
+ arrayHandle.popIndex();
+ }
+
+ if (deleteIndex == -1)
+ {
+ return;
+ }
+
+
+ // we should remove the deleteIndex item!
+
+#if 1 // replace with last
+ if (deleteIndex < numMaterials - 1)
+ {
+ error = arrayHandle.swapArrayElements((uint32_t)deleteIndex, (uint32_t)numMaterials - 1);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ }
+#else // erase
+ for (int i = deleteIndex + 1; i < numMaterials; i++)
+ {
+ error = arrayHandle.swapArrayElements((uint32_t)i - 1, (uint32_t)i);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ }
+#endif
+ error = arrayHandle.resizeArray(numMaterials - 1);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+}
+
+
+
+void ClothingAuthoring::selectMaterial(const char* libName, const char* matName)
+{
+ mDirty.workspace |= mState.selectedMaterialLibrary != libName;
+ mState.selectedMaterialLibrary = libName;
+ mDirty.workspace |= mState.selectedMaterial != matName;
+ mState.selectedMaterial = matName;
+}
+
+
+
+physx::PxBounds3 ClothingAuthoring::getCombinedSimulationBounds() const
+{
+ physx::PxBounds3 cummulatedBound;
+ cummulatedBound.setEmpty();
+
+ for (size_t i = 0; i < mSimulation.actors.size(); i++)
+ {
+ PxVec3 actorPosition = mSimulation.actors[i].currentPose.getPosition();
+ physx::PxBounds3 actorBound = mState.apexBounds;
+ actorBound.minimum += actorPosition;
+ actorBound.maximum += actorPosition;
+
+ cummulatedBound.include(actorBound);
+ }
+
+ return cummulatedBound;
+}
+
+
+
+void ClothingAuthoring::clearMaterialLibraries()
+{
+ for (CurrentState::tMaterialLibraries::iterator it = mState.materialLibraries.begin(); it != mState.materialLibraries.end(); ++it)
+ {
+ if (it->second != NULL)
+ {
+ it->second->destroy();
+ }
+ }
+ mState.materialLibraries.clear();
+}
+
+
+
+NvParameterized::Interface* ClothingAuthoring::getMaterialLibrary(unsigned int libraryIndex) const
+{
+ NvParameterized::Interface* result = NULL;
+ if (libraryIndex < mState.materialLibraries.size())
+ {
+ unsigned int i = 0;
+ for (CurrentState::tMaterialLibraries::const_iterator it = mState.materialLibraries.begin(); it != mState.materialLibraries.end(); ++it, ++i)
+ {
+ if (libraryIndex == i)
+ {
+ result = it->second;
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+
+
+bool ClothingAuthoring::setMaterialLibrary(unsigned int libraryIndex, NvParameterized::Interface* data)
+{
+ if (::strcmp(data->className(), "ClothingMaterialLibraryParameters") != 0)
+ {
+ return false;
+ }
+
+ if (libraryIndex < mState.materialLibraries.size())
+ {
+ unsigned int i = 0;
+ for (CurrentState::tMaterialLibraries::iterator it = mState.materialLibraries.begin(); it != mState.materialLibraries.end(); ++it, ++i)
+ {
+ if (libraryIndex == i)
+ {
+ NvParameterized::Interface* oldData = it->second;
+ it->second = data;
+
+ // hopefully this is not used by any asset?
+ oldData->destroy();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+
+const char* ClothingAuthoring::getMaterialLibraryName(unsigned int libraryIndex) const
+{
+ const char* result = NULL;
+ if (libraryIndex < mState.materialLibraries.size())
+ {
+ unsigned int i = 0;
+ for (CurrentState::tMaterialLibraries::const_iterator it = mState.materialLibraries.begin(); it != mState.materialLibraries.end(); ++it, ++i)
+ {
+ if (libraryIndex == i)
+ {
+ result = it->first.c_str();
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+
+
+unsigned int ClothingAuthoring::getNumMaterials(unsigned int libraryIndex) const
+{
+ NvParameterized::Interface* materialLibrary = getMaterialLibrary(libraryIndex);
+
+ if (materialLibrary != NULL)
+ {
+ NvParameterized::ErrorType error = NvParameterized::ERROR_NONE;
+ NvParameterized::Handle handle(materialLibrary);
+
+ error = handle.getParameter("materials");
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ int numMaterials = 0;
+ error = handle.getArraySize(numMaterials);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ PX_ASSERT(numMaterials >= 0);
+
+ return (uint32_t)numMaterials;
+ }
+
+ return 0;
+}
+
+
+
+const char* ClothingAuthoring::getMaterialName(unsigned int libraryIndex, unsigned int materialIndex) const
+{
+ NvParameterized::Interface* materialLibrary = getMaterialLibrary(libraryIndex);
+
+ if (materialLibrary != NULL)
+ {
+ NvParameterized::ErrorType error = NvParameterized::ERROR_NONE;
+ NvParameterized::Handle handle(materialLibrary);
+
+ error = handle.getParameter("materials");
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ int numMaterials = 0;
+ error = handle.getArraySize(numMaterials);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+ PX_ASSERT(numMaterials >= 0);
+
+ if (materialIndex < (unsigned int)numMaterials)
+ {
+ error = handle.set((int)materialIndex);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ NvParameterized::Handle nameHandle(materialLibrary);
+ error = handle.getChildHandle(materialLibrary, "materialName", nameHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ const char* materialName = NULL;
+ error = nameHandle.getParamString(materialName);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ return materialName;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+const physx::PxMat44& ClothingAuthoring::getTriangleMeshPose(size_t index) const
+{
+ PX_ASSERT(index < mSimulation.actors.size());
+ return mSimulation.actors[index].currentPose;
+}
+
+
+
+size_t ClothingAuthoring::getNumSimulationActors() const
+{
+ size_t count = 0;
+ for (size_t actorIndex = 0; actorIndex < mSimulation.actors.size(); ++actorIndex)
+ {
+ count += mSimulation.actors[actorIndex].actors.size();
+ }
+ return count;
+}
+
+
+
+nvidia::apex::ClothingActor* ClothingAuthoring::getSimulationActor(size_t index)
+{
+ size_t actorIndex = index / mSimulation.assets.size();
+ size_t assetIndex = index % mSimulation.assets.size();
+ if (actorIndex < mSimulation.actors.size())
+ {
+ if (assetIndex < mSimulation.actors[actorIndex].actors.size())
+ {
+ return mSimulation.actors[actorIndex].actors[assetIndex];
+ }
+ }
+
+ return NULL;
+}
+
+
+
+void ClothingAuthoring::initMeshSkinningData()
+{
+ for (size_t assetIdx = 0; assetIdx < mSimulation.assets.size(); ++assetIdx)
+ {
+ if (mSimulation.assets[assetIdx].meshSkinningMaps.size() > 0)
+ continue;
+
+ nvidia::apex::ClothingAsset* clothingAsset = mSimulation.assets[assetIdx].apexAsset;
+ if (clothingAsset != NULL)
+ {
+ uint32_t numLods = clothingAsset->getNumGraphicalLodLevels();
+ mSimulation.assets[assetIdx].meshSkinningMaps.resize(numLods);
+ mSimulation.assets[assetIdx].renderMeshAssets.resize(numLods);
+ for (uint32_t lod = 0; lod < numLods; ++lod)
+ {
+ uint32_t numMapEntries = clothingAsset->getMeshSkinningMapSize(lod);
+
+ mSimulation.assets[assetIdx].meshSkinningMaps[lod].resize(numMapEntries);
+ if (numMapEntries > 0)
+ {
+ clothingAsset->getMeshSkinningMap(lod, &mSimulation.assets[assetIdx].meshSkinningMaps[lod][0]);
+ }
+
+ const nvidia::apex::RenderMeshAsset* renderMeshAsset = clothingAsset->getRenderMeshAsset(lod);
+
+ if (renderMeshAsset != NULL)
+ {
+ const NvParameterized::Interface* rmaParams = renderMeshAsset->getAssetNvParameterized();
+ NvParameterized::Interface* rmaParamsCopy = NULL;
+ rmaParams->clone(rmaParamsCopy);
+ char name[20];
+ sprintf(name, "rma_%i_%i", (int)mSimulation.assets.size(), (int)lod);
+ mSimulation.assets[assetIdx].renderMeshAssets[lod] = (nvidia::apex::RenderMeshAsset*)_mApexSDK->createAsset(rmaParamsCopy, name);
+ }
+ }
+ }
+ }
+}
+
+
+
+nvidia::apex::ClothingMeshSkinningMap* ClothingAuthoring::getMeshSkinningMap(size_t index, uint32_t lod, uint32_t& mapSize, nvidia::apex::RenderMeshAsset*& renderMeshAsset)
+{
+ size_t assetIndex = index % mSimulation.assets.size();
+
+ nvidia::apex::ClothingMeshSkinningMap* map = NULL;
+
+ mapSize = (uint32_t)mSimulation.assets[assetIndex].meshSkinningMaps[lod].size();
+ if (mapSize > 0)
+ {
+ map = &mSimulation.assets[assetIndex].meshSkinningMaps[lod][0];
+ }
+ renderMeshAsset = mSimulation.assets[assetIndex].renderMeshAssets[lod];
+ return map;
+}
+
+
+
+nvidia::apex::Renderable* ClothingAuthoring::getSimulationRenderable(size_t index)
+{
+ size_t actorIndex = index / mSimulation.assets.size();
+ size_t assetIndex = index % mSimulation.assets.size();
+ if (actorIndex < mSimulation.actors.size())
+ {
+ if (assetIndex < mSimulation.actors[actorIndex].actors.size())
+ {
+ return mSimulation.actors[actorIndex].actors[assetIndex];
+ }
+ else if (assetIndex < mSimulation.actors[actorIndex].previews.size())
+ {
+ return mSimulation.actors[actorIndex].previews[assetIndex];
+ }
+ }
+
+ return NULL;
+}
+
+
+
+void ClothingAuthoring::setActorCount(int count, bool addToCommandQueue)
+{
+ if (addToCommandQueue)
+ {
+ char buf[64];
+ sprintf_s(buf, 64, "setActorCount %d", count);
+ addCommand(buf);
+ }
+ if (mSimulation.assets.empty())
+ {
+ mSimulation.actorCount = count;
+ }
+ else if (mSimulation.actorCount != count)
+ {
+ stopSimulation();
+ mSimulation.actorCount = count;
+
+ // create instances
+ PX_ASSERT(mSimulation.assets.size() > 0);
+
+ NvParameterized::Interface* actorDesc = mSimulation.assets[0].apexAsset->getDefaultActorDesc();
+ PX_ASSERT(actorDesc != NULL);
+
+ NvParameterized::Interface* previewDesc = mSimulation.assets[0].apexAsset->getDefaultAssetPreviewDesc();
+ PX_ASSERT(previewDesc != NULL);
+
+ ASSERT_TRUE(NvParameterized::setParamBool(*actorDesc, "useHardwareCloth", mConfig.simulation.gpuSimulation));
+ ASSERT_TRUE(NvParameterized::setParamBool(*actorDesc, "flags.ParallelCpuSkinning", mConfig.apex.parallelCpuSkinning));
+ ASSERT_TRUE(NvParameterized::setParamBool(*actorDesc, "flags.RecomputeNormals", mConfig.apex.recomputeNormals));
+ ASSERT_TRUE(NvParameterized::setParamBool(*actorDesc, "flags.RecomputeTangents", mConfig.apex.recomputeTangents));
+ ASSERT_TRUE(NvParameterized::setParamBool(*actorDesc, "flags.CorrectSimulationNormals", mConfig.apex.correctSimulationNormals));
+ ASSERT_TRUE(NvParameterized::setParamF32(*actorDesc, "maxDistanceBlendTime", mConfig.simulation.blendTime));
+ ASSERT_TRUE(NvParameterized::setParamF32(*actorDesc, "pressure", mConfig.simulation.pressure));
+ ASSERT_TRUE(NvParameterized::setParamBool(*actorDesc, "fallbackSkinning", mConfig.simulation.fallbackSkinning));
+ ASSERT_TRUE(NvParameterized::setParamBool(*actorDesc, "useInternalBoneOrder", true));
+ ASSERT_TRUE(NvParameterized::setParamBool(*actorDesc, "updateStateWithGlobalMatrices", mConfig.animation.useGlobalPoseMatrices));
+ ASSERT_TRUE(NvParameterized::setParamBool(*actorDesc, "allowAdaptiveTargetFrequency", true));
+ ASSERT_TRUE(NvParameterized::setParamBool(*actorDesc, "localSpaceSim", mConfig.simulation.localSpaceSim));
+ ASSERT_TRUE(NvParameterized::setParamBool(*actorDesc, "multiplyGlobalPoseIntoBones", !mConfig.animation.applyGlobalPoseInApp));
+
+ // max distance scale
+ ASSERT_TRUE(NvParameterized::setParamF32(*actorDesc, "maxDistanceScale.Scale", mConfig.painting.maxDistanceScale));
+ ASSERT_TRUE(NvParameterized::setParamBool(*actorDesc, "maxDistanceScale.Multipliable", mConfig.painting.maxDistanceScaleMultipliable));
+
+ if (mConfig.apex.useMorphTargetTest)
+ {
+ float scale = mState.simulationValuesScale / 10.0f;
+ PxVec3 positions[3];
+ positions[0] = PxVec3(scale, 0.0f, 0.0f);
+ positions[1] = PxVec3(0.0f, scale, 0.0f);
+ positions[2] = PxVec3(0.0f, 0.0f, scale);
+
+ NvParameterized::Handle morphDisp(*actorDesc);
+ morphDisp.getParameter("morphDisplacements");
+ morphDisp.resizeArray(3);
+
+ for (int i = 0; i < 3; i++)
+ {
+ morphDisp.set(i);
+ morphDisp.setParamVec3(positions[i]);
+ morphDisp.popIndex();
+ }
+
+ for (int i = 0; i < 3; i++)
+ {
+ positions[i] += mState.apexBounds.getCenter();
+ }
+
+ for (size_t i = 0; i < mSimulation.assets.size(); i++)
+ {
+ mSimulation.assets[i].apexAsset->prepareMorphTargetMapping(positions, 3, 10);
+ }
+ }
+
+ if (getMeshSkinningInApp())
+ {
+ for (size_t i = 0; i < mSimulation.assets.size(); i++)
+ {
+ if (mSimulation.actors.size() == 0)
+ {
+ initMeshSkinningData();
+ mSimulation.assets[i].apexAsset->releaseGraphicalData();
+ }
+ }
+ }
+
+ // preview
+ ASSERT_TRUE(NvParameterized::setParamBool(*previewDesc, "useInternalBoneOrder", true));
+ ASSERT_TRUE(NvParameterized::setParamBool(*previewDesc, "updateStateWithGlobalMatrices", mConfig.animation.useGlobalPoseMatrices));
+
+ const float width = std::max(mState.apexBounds.maximum.x - mState.apexBounds.minimum.x,
+ std::max(mState.apexBounds.maximum.y - mState.apexBounds.minimum.y, mState.apexBounds.maximum.z - mState.apexBounds.minimum.z));
+
+ const physx::PxMat44* skinningMatrices = NULL;
+ size_t numSkinningMatrices = 0;
+
+ if (mMeshes.skeleton != NULL)
+ {
+ if (mConfig.animation.useGlobalPoseMatrices)
+ {
+ numSkinningMatrices = mMeshes.skeleton->getSkinningMatricesWorld().size();
+ skinningMatrices = numSkinningMatrices > 0 ? &mMeshes.skeleton->getSkinningMatricesWorld()[0] : NULL;
+ }
+ else
+ {
+ numSkinningMatrices = mMeshes.skeleton->getSkinningMatrices().size();
+ skinningMatrices = numSkinningMatrices > 0 ? &mMeshes.skeleton->getSkinningMatrices()[0] : NULL;
+ }
+ }
+
+
+ std::vector<physx::PxMat44> scaledSkinningMatrices;
+ if (mSimulation.actorCount > (int)mSimulation.actors.size())
+ {
+ for (int i = (int)mSimulation.actors.size(); i < mSimulation.actorCount; i++)
+ {
+ Simulation::ClothingActor clothingActor;
+
+ clothingActor.scale = physx::PxPow(mConfig.simulation.scaleFactor, (float)(i + 1));
+ ASSERT_TRUE(NvParameterized::setParamF32(*actorDesc, "actorScale", clothingActor.scale));
+
+ PxVec3 pos(0.0f, 0.0f, 0.0f);
+
+ const float index = i + 1.0f;
+ const float squareRoot = physx::PxSqrt(index);
+ const float roundedSquareRoot = physx::PxFloor(squareRoot);
+ const float square = roundedSquareRoot * roundedSquareRoot;
+ if (physx::PxAbs(index - square) < 0.01f)
+ {
+ pos.x = width * 2.0f * (squareRoot - 1);
+ pos.z = -pos.x;
+ }
+ else if (squareRoot - roundedSquareRoot < 0.5f)
+ {
+ pos.x = width * 2.0f * roundedSquareRoot;
+ pos.z = -(i - square) * width * 2.0f;
+ }
+ else
+ {
+ pos.z = -width * 2.0f * roundedSquareRoot;
+ pos.x = (i - square - roundedSquareRoot) * width * 2.0f;
+ }
+
+ if (mConfig.ui.zAxisUp)
+ {
+ const float t = pos.y;
+ pos.y = pos.z;
+ pos.z = t;
+ }
+
+ clothingActor.initPose.setPosition(pos);
+
+ physx::PxMat44 pose(physx::PxIdentity);
+ pose.setPosition(pos);
+ clothingActor.currentPose = mSimulation.CCTPose * pose;
+
+ clothingActor.currentPose.scale(physx::PxVec4(clothingActor.scale, clothingActor.scale, clothingActor.scale, 1.0f));
+ ASSERT_TRUE(NvParameterized::setParamMat44(*actorDesc, "globalPose", clothingActor.currentPose));
+ ASSERT_TRUE(NvParameterized::setParamMat44(*previewDesc, "globalPose", clothingActor.currentPose));
+
+ for (size_t j = 0; j < mSimulation.assets.size(); j++)
+ {
+ std::vector<short>& remapTable = mSimulation.assets[j].remapToSkeleton;
+ if (scaledSkinningMatrices.size() < remapTable.size())
+ {
+ scaledSkinningMatrices.resize(remapTable.size());
+ }
+
+ for (size_t k = 0; k < remapTable.size(); k++)
+ {
+ PX_ASSERT((unsigned int)remapTable[k] < numSkinningMatrices);
+ scaledSkinningMatrices[k] = skinningMatrices[remapTable[k]];
+ }
+
+ const int numSkinningMatricesReal = (int)remapTable.size();
+ physx::PxMat44* skinningMatricesReal = numSkinningMatricesReal > 0 ? &scaledSkinningMatrices[0] : NULL;
+
+ if (mConfig.animation.applyGlobalPoseInApp)
+ {
+ if(numSkinningMatricesReal)
+ {
+ PX_ASSERT(skinningMatricesReal);
+ }
+ for (size_t k = 0; k < (size_t)numSkinningMatricesReal; k++)
+ {
+ skinningMatricesReal[k] = clothingActor.currentPose * skinningMatricesReal[k];
+ }
+ }
+
+ if (skinningMatricesReal != NULL)
+ {
+ NvParameterized::Handle actorHandle(actorDesc);
+ VERIFY_PARAM(actorHandle.getParameter("boneMatrices"));
+ VERIFY_PARAM(actorHandle.resizeArray(numSkinningMatricesReal));
+ VERIFY_PARAM(actorHandle.setParamMat44Array(skinningMatricesReal, numSkinningMatricesReal));
+
+ NvParameterized::Handle previewHandle(previewDesc);
+ VERIFY_PARAM(previewHandle.getParameter("boneMatrices"));
+ VERIFY_PARAM(previewHandle.resizeArray(numSkinningMatricesReal));
+ VERIFY_PARAM(previewHandle.setParamMat44Array(skinningMatricesReal, numSkinningMatricesReal));
+ }
+
+ if (mConfig.simulation.usePreview)
+ {
+ nvidia::apex::AssetPreview* apexPreview = mSimulation.assets[j].apexAsset->createApexAssetPreview(*previewDesc, NULL);
+ clothingActor.previews.push_back(static_cast<nvidia::apex::ClothingPreview*>(apexPreview));
+ }
+ else
+ {
+ NvParameterized::Interface* desc = (mLoadedActorDescs.size() > 0) ? mLoadedActorDescs[0] : actorDesc;
+ nvidia::apex::Actor* actor = mSimulation.assets[j].apexAsset->createApexActor(*desc, *mSimulation.apexScene);
+ nvidia::apex::ClothingActor* cActor = static_cast<nvidia::apex::ClothingActor*>(actor);
+ clothingActor.actors.push_back(cActor);
+
+ nvidia::apex::ClothingPlane* actorGroundPlane = NULL;
+ if (mConfig.simulation.groundplaneEnabled)
+ {
+ float d = -mConfig.simulation.groundplane * mState.simulationValuesScale * 0.1f;
+ PxVec3 normal = mConfig.ui.zAxisUp ? PxVec3(0.0f, 0.0f, 1.0f) : PxVec3(0.0f, 1.0f, 0.0f);
+ physx::PxPlane plane(normal, d);
+ actorGroundPlane = cActor->createCollisionPlane(plane);
+ }
+
+ clothingActor.actorGroundPlanes.push_back(actorGroundPlane);
+ }
+ }
+
+ mSimulation.actors.push_back(clothingActor);
+ }
+ }
+ else if (mSimulation.actorCount < (int)mSimulation.actors.size())
+ {
+ while ((int)mSimulation.actors.size() > mSimulation.actorCount)
+ {
+ Simulation::ClothingActor& clothingActor = mSimulation.actors.back();
+
+ for (size_t j = 0; j < clothingActor.actors.size(); j++)
+ {
+ if (clothingActor.actors[j] != NULL)
+ {
+ clothingActor.actors[j]->release();
+ clothingActor.actors[j] = NULL;
+ }
+ }
+ for (size_t j = 0; j < clothingActor.previews.size(); j++)
+ {
+ if (clothingActor.previews[j] != NULL)
+ {
+ clothingActor.previews[j]->release();
+ clothingActor.previews[j] = NULL;
+ }
+ }
+
+ if (clothingActor.triangleMesh != NULL)
+ {
+ clothingActor.triangleMesh->clear(_mRenderResourceManager, _mResourceCallback);
+ delete clothingActor.triangleMesh;
+ clothingActor.triangleMesh = NULL;
+ }
+
+ mSimulation.actors.pop_back();
+ }
+ }
+ }
+}
+
+
+
+float ClothingAuthoring::getActorScale(size_t index)
+{
+ size_t actorIndex = index / mSimulation.assets.size();
+ if (actorIndex < mSimulation.actors.size())
+ {
+ return mSimulation.actors[actorIndex].scale;
+ }
+
+ return 1.0f;
+}
+
+
+
+void ClothingAuthoring::setMatrices(const physx::PxMat44& viewMatrix, const physx::PxMat44& projectionMatrix)
+{
+ if (mSimulation.apexScene != NULL)
+ {
+ mSimulation.apexScene->setViewMatrix(viewMatrix, 0);
+ mSimulation.apexScene->setProjMatrix(projectionMatrix, 0);
+ mSimulation.apexScene->setUseViewProjMatrix(0, 0);
+ }
+}
+
+
+
+void ClothingAuthoring::startCreateApexScene()
+{
+ stopSimulation();
+
+ if (mSimulation.apexScene == NULL)
+ {
+ return;
+ }
+
+ mConfig.simulation.budgetPercent = 100;
+
+ for (size_t i = 0; i < mSimulation.assets.size(); i++)
+ {
+ mSimulation.assets[i].releaseRenderMeshAssets();
+ mSimulation.assets[i].apexAsset->release();
+ mSimulation.assets[i].apexAsset = NULL;
+ }
+ mSimulation.assets.clear();
+ for (size_t i = 0; i < mSimulation.actors.size(); i++)
+ {
+ if (mSimulation.actors[i].triangleMesh != NULL)
+ {
+ mSimulation.actors[i].triangleMesh->clear(_mRenderResourceManager, _mResourceCallback);
+ delete mSimulation.actors[i].triangleMesh;
+ mSimulation.actors[i].triangleMesh = NULL;
+ }
+ }
+ mSimulation.actors.clear(); // deleted when releasing all the assets :)
+
+ if (mSimulation.physxScene != NULL)
+ {
+ mSimulation.apexScene->setPhysXScene(NULL);
+
+
+ mSimulation.physxScene->release();
+
+ mSimulation.physxScene = NULL;
+ mSimulation.groundPlane = NULL;
+ }
+
+ {
+ NvParameterized::Interface* debugRendering = mSimulation.apexScene->getDebugRenderParams();
+
+ ASSERT_TRUE(NvParameterized::setParamBool(*debugRendering, "Enable", true));
+ ASSERT_TRUE(NvParameterized::setParamF32(*debugRendering, "Scale", 1.0f));
+ }
+
+ {
+ physx::PxSceneDesc sceneDesc(_mApexSDK->getPhysXSDK()->getTolerancesScale());
+#if APEX_CUDA_SUPPORT
+ if (mSimulation.cudaContextManager != NULL)
+ {
+ sceneDesc.gpuDispatcher = mSimulation.cudaContextManager->getGpuDispatcher();
+ }
+#endif
+ sceneDesc.flags|=physx::PxSceneFlag::eREQUIRE_RW_LOCK;
+ sceneDesc.cpuDispatcher = mSimulation.cpuDispatcher;
+ sceneDesc.filterShader = physx::PxDefaultSimulationFilterShader;
+
+ sceneDesc.gravity = PxVec3(0.0f);
+ sceneDesc.gravity[mConfig.ui.zAxisUp ? 2 : 1] = -0.1f; // will be set to the right value later, we just need the right direction for cooking
+
+ mSimulation.physxScene = _mApexSDK->getPhysXSDK()->createScene(sceneDesc); // disable this in UE4
+ }
+
+ // turn on debug rendering
+ if (mSimulation.physxScene)
+ {
+ mSimulation.physxScene->lockWrite(__FILE__, __LINE__);
+ mSimulation.physxScene->setVisualizationParameter(physx::PxVisualizationParameter::eSCALE, 1.0f);
+ mSimulation.physxScene->unlockWrite();
+ }
+
+ mSimulation.apexScene->setPhysXScene(mSimulation.physxScene);
+
+ // connect PVD
+ connectPVD(false);
+
+ if (mMeshes.inputMesh != NULL)
+ {
+ for (uint32_t i = 0; i < mMeshes.inputMesh->getNumSubmeshes(); i++)
+ {
+ mMeshes.inputMesh->setSubMeshHasPhysics(i, mMeshes.inputMesh->getSubMesh(i)->selected);
+ }
+ }
+}
+
+
+
+bool ClothingAuthoring::addAssetToScene(nvidia::apex::ClothingAsset* clothingAsset, nvidia::apex::IProgressListener* progress, unsigned int totalNumAssets)
+{
+ if (mSimulation.apexScene == NULL)
+ {
+ return false;
+ }
+
+ mSimulation.assets.reserve(totalNumAssets);
+
+ if (clothingAsset != NULL)
+ {
+ PX_ASSERT(!mSimulation.clearAssets || mSimulation.assets.empty());
+ mSimulation.clearAssets = false;
+ mSimulation.assets.push_back(clothingAsset);
+ }
+ else
+ {
+ PX_ASSERT(mSimulation.assets.empty());
+ mSimulation.clearAssets = true;
+ nvidia::apex::ClothingAssetAuthoring* authoringAsset = getClothingAsset(progress);
+ if (authoringAsset != NULL)
+ {
+#if 1
+ nvidia::apex::Asset* asset = _mApexSDK->createAsset(*authoringAsset, "TheSimulationAsset");
+#else
+ static bool flip = true;
+ if (flip)
+ {
+ physx::PxMat44 mat = physx::PxMat44::identity();
+ mat(1, 1) = -1;
+ authoringAsset->applyTransformation(mat, 1.0f, true, true);
+ }
+ // PH: test the releaseAndReturnNvParameterizedInterface() method
+ PX_ASSERT(mAuthoringObjects.clothingAssetAuthoring == authoringAsset);
+ NvParameterized::Interface* authoringInterface = authoringAsset->releaseAndReturnNvParameterizedInterface();
+
+ nvidia::apex::Asset* asset = NULL;
+ if (authoringInterface != NULL)
+ {
+ mAuthoringObjects.clothingAssetAuthoring = authoringAsset = NULL;
+ asset = _mApexSDK->createAsset(authoringInterface, "TheSimulationAsset");
+ }
+#endif
+ if (asset != NULL)
+ {
+ if (::strcmp(asset->getObjTypeName(), CLOTHING_AUTHORING_TYPE_NAME) == 0)
+ {
+ clothingAsset = static_cast<nvidia::apex::ClothingAsset*>(asset);
+ mSimulation.assets.push_back(clothingAsset);
+ }
+ else
+ {
+ _mApexSDK->releaseAsset(*asset);
+ }
+ }
+ }
+ }
+
+ if (mMeshes.skeleton != NULL && clothingAsset != NULL)
+ {
+ const unsigned int numBones = clothingAsset->getNumUsedBones();
+ std::vector<short> remap;
+ remap.resize(numBones, -1);
+
+ const std::vector<Samples::SkeletalBone>& bones = mMeshes.skeleton->getBones();
+
+ for (unsigned int i = 0; i < numBones; i++)
+ {
+ const char* boneName = clothingAsset->getBoneName(i);
+ for (unsigned int j = 0; j < bones.size(); j++)
+ {
+ if (bones[j].name == boneName)
+ {
+ remap[i] = (short)j;
+ break;
+ }
+ }
+ }
+
+ mSimulation.assets.back().remapToSkeleton.swap(remap);
+ }
+
+ if (clothingAsset != NULL)
+ {
+ uint32_t numLods = clothingAsset->getNumGraphicalLodLevels();
+ mSimulation.assets.back().meshSkinningMaps.resize(numLods);
+ mSimulation.assets.back().renderMeshAssets.resize(numLods);
+ for (uint32_t lod = 0; lod < numLods; ++lod)
+ {
+ uint32_t numMapEntries = clothingAsset->getMeshSkinningMapSize(lod);
+
+ mSimulation.assets.back().meshSkinningMaps[lod].resize(numMapEntries);
+ if (numMapEntries > 0)
+ {
+ clothingAsset->getMeshSkinningMap(lod, &mSimulation.assets.back().meshSkinningMaps[lod][0]);
+ }
+
+
+ const nvidia::apex::RenderMeshAsset* renderMeshAsset = clothingAsset->getRenderMeshAsset(lod);
+
+ if (renderMeshAsset != NULL)
+ {
+ const NvParameterized::Interface* rmaParams = renderMeshAsset->getAssetNvParameterized();
+ NvParameterized::Interface* rmaParamsCopy = NULL;
+ rmaParams->clone(rmaParamsCopy);
+ char name[20];
+ sprintf(name, "rma_%i_%i", (int)mSimulation.assets.size(), (int)lod);
+ mSimulation.assets.back().renderMeshAssets[lod] = static_cast<nvidia::RenderMeshAsset*>(_mApexSDK->createAsset(rmaParamsCopy, name));
+ }
+ }
+
+ mConfig.simulation.localSpaceSim = true;
+ }
+
+ return clothingAsset != NULL;
+}
+
+
+void ClothingAuthoring::handleGravity()
+{
+ if (getAndClearGravityDirty())
+ {
+ const float scale = mState.gravityValueScale != 0 ? mState.gravityValueScale : mState.simulationValuesScale;
+ PxVec3 gravity(0.0f);
+ gravity[mConfig.ui.zAxisUp ? 2 : 1] = -scale * mConfig.simulation.gravity;
+
+ mSimulation.apexScene->setGravity(gravity);
+ }
+}
+
+
+void ClothingAuthoring::finishCreateApexScene(bool recomputeScale)
+{
+ if (mSimulation.apexScene == NULL)
+ {
+ return;
+ }
+
+ int oldActorCount = std::max(1, mSimulation.actorCount);
+ mSimulation.actorCount = 0;
+
+ float time = 0.0f;
+ if (mMeshes.skeleton != NULL && !mMeshes.skeleton->getAnimations().empty())
+ {
+ Samples::SkeletalAnimation* anim = mMeshes.skeleton->getAnimations()[0];
+ clampAnimation(time, false, false, anim->minTime, anim->maxTime);
+ }
+ setAnimationTimes(time);
+
+ updateAnimation();
+
+ mState.apexBounds.setEmpty();
+
+ if (mMeshes.inputMesh != NULL)
+ {
+ mMeshes.inputMesh->getBounds(mState.apexBounds);
+ }
+
+ for (size_t i = 0; i < mSimulation.assets.size(); i++)
+ {
+ mState.apexBounds.include(mSimulation.assets[i].apexAsset->getBoundingBox());
+ }
+
+ if (mState.gravityValueScale != 0.0f)
+ {
+ // loaded from a .ctp
+ mState.simulationValuesScale = mState.gravityValueScale;
+ }
+ else if (recomputeScale)
+ {
+ // loaded without a .ctp, directly an .apx or .apb
+ float scale = 0.5f * (mState.apexBounds.minimum - mState.apexBounds.maximum).magnitude();
+ if (scale >= 0.5f && scale <= 2.0f)
+ {
+ scale = 1.0f;
+ }
+
+ mState.simulationValuesScale = scale;
+
+ if (mState.apexBounds.minimum.y < mState.apexBounds.minimum.z * 10)
+ {
+ setZAxisUp(true);
+ }
+ else if (mState.apexBounds.minimum.z < mState.apexBounds.minimum.y * 10)
+ {
+ setZAxisUp(false);
+ }
+
+ for (size_t i = 0; i < _mNotifyCallbacks.size(); i++)
+ {
+ _mNotifyCallbacks[i]->notifyInputMeshLoaded();
+ }
+ }
+
+ if (mLoadedActorDescs.size() > 0)
+ {
+ physx::PxMat44 globalPose;
+ if (NvParameterized::getParamMat44(*mLoadedActorDescs[0], "globalPose", globalPose))
+ {
+ for (size_t i = 0; i < _mNotifyCallbacks.size(); i++)
+ {
+ _mNotifyCallbacks[i]->notifyMeshPos(mState.apexBounds.getCenter() + globalPose.getPosition());
+ }
+ }
+ else
+ {
+ ASSERT_TRUE(0);
+ }
+ }
+
+ mSimulation.CCTPose = physx::PxMat44(physx::PxIdentity);
+ mSimulation.CCTDirection = PxVec3(0.0f);
+ mSimulation.CCTRotationDelta = PxVec3(0.0f);
+
+ mDirty.gravity = true;
+ mDirty.groundPlane = true;
+
+ mSimulation.paused = false;
+ mSimulation.frameNumber = 0;
+ PX_ASSERT(mSimulation.running == false);
+
+ // init gravity before actor creation, in case it's recooking the asset
+ handleGravity();
+
+ setActorCount(oldActorCount, false); // creates the actors and computes mApexBounds
+}
+
+
+
+#define TELEPORT_TEST 0
+
+void ClothingAuthoring::startSimulation()
+{
+ if (mSimulation.assets.empty() || mSimulation.running)
+ {
+ return;
+ }
+
+ for (size_t i = 0; i < mSimulation.actors.size(); i++)
+ {
+ for (size_t j = 0; j < mSimulation.actors[i].actors.size(); j++)
+ {
+ PX_ASSERT(mSimulation.actors[i].actors[j] != NULL);
+ }
+ }
+
+ if (getAndClearNeedsRestart())
+ {
+ restartSimulation(); // using setActorCount twice
+ }
+
+ if (mSimulation.stepsUntilPause > 0)
+ {
+ mSimulation.paused = false;
+ if (mConfig.animation.selectedAnimation >= 0)
+ {
+ mSimulation.stepsUntilPause--;
+
+ if (mSimulation.stepsUntilPause == 0)
+ {
+ setAnimation(-mConfig.animation.selectedAnimation);
+ mSimulation.paused = true;
+ }
+ }
+ else
+ {
+ mSimulation.stepsUntilPause = 0;
+ }
+ }
+
+ if (mSimulation.paused)
+ {
+ return;
+ }
+
+ float deltaT = 1.0f / mConfig.simulation.frequency;
+ if (mConfig.simulation.timingNoise != 0.0f)
+ {
+ const float randomValue = (::rand() / (float)RAND_MAX);
+ deltaT += randomValue * deltaT * mConfig.simulation.timingNoise;
+ }
+
+// const bool updatedCCT = updateCCT(deltaT);
+
+ // inter-collision settings
+ // removed from 1.3
+// _mModuleClothing->setInterCollisionDistance(mConfig.simulation.interCollisionDistance);
+// _mModuleClothing->setInterCollisionStiffness(mConfig.simulation.interCollisionStiffness);
+// _mModuleClothing->setInterCollisionIterations(mConfig.simulation.interCollisionIterations);
+
+ stepAnimationTimes(mConfig.animation.speed * deltaT / 100.f);
+
+ bool animationJumped = updateAnimation();
+
+ const physx::PxMat44* skinningMatrices = NULL;
+ size_t numSkinningMatrices = 0;
+
+ if (mMeshes.skeleton != NULL)
+ {
+ if (mConfig.animation.useGlobalPoseMatrices)
+ {
+ numSkinningMatrices = mMeshes.skeleton->getSkinningMatricesWorld().size();
+ skinningMatrices = numSkinningMatrices > 0 ? &mMeshes.skeleton->getSkinningMatricesWorld()[0] : NULL;
+ }
+ else
+ {
+ numSkinningMatrices = mMeshes.skeleton->getSkinningMatrices().size();
+ skinningMatrices = numSkinningMatrices > 0 ? &mMeshes.skeleton->getSkinningMatrices()[0] : NULL;
+ }
+ }
+
+ std::vector<physx::PxMat44> scaledSkinningMatrices;
+
+ for (size_t i = 0; i < mSimulation.actors.size(); i++)
+ {
+#if !TELEPORT_TEST
+ mSimulation.actors[i].currentPose = mSimulation.CCTPose * mSimulation.actors[i].initPose;
+ float actorScale = mSimulation.actors[i].scale;
+ mSimulation.actors[i].currentPose.scale(physx::PxVec4(actorScale, actorScale, actorScale, 1.0f));
+#endif
+
+ PX_ASSERT(mSimulation.actors[i].actors.size() == mSimulation.assets.size() || mSimulation.actors[i].previews.size() == mSimulation.assets.size());
+
+ for (size_t j = 0; j < mSimulation.assets.size(); j++)
+ {
+ std::vector<short>& remapTable = mSimulation.assets[j].remapToSkeleton;
+ if (scaledSkinningMatrices.size() < remapTable.size())
+ {
+ scaledSkinningMatrices.resize(remapTable.size());
+ }
+
+ for (size_t k = 0; k < remapTable.size(); k++)
+ {
+ PX_ASSERT((unsigned int)remapTable[k] < numSkinningMatrices);
+ scaledSkinningMatrices[k] = skinningMatrices[remapTable[k]]/* * mSimulation.actors[i].scale*/;
+ }
+
+ const unsigned int numSkinningMatricesReal = (unsigned int)remapTable.size();
+ physx::PxMat44* skinningMatricesReal = numSkinningMatricesReal > 0 ? &scaledSkinningMatrices[0] : NULL;
+
+ if (mConfig.animation.applyGlobalPoseInApp)
+ {
+ for (size_t k = 0; k < numSkinningMatricesReal; k++)
+ {
+ skinningMatricesReal[k] = mSimulation.actors[i].currentPose * skinningMatricesReal[k];
+ }
+ }
+
+ if (j < mSimulation.actors[i].previews.size())
+ {
+ mSimulation.actors[i].previews[j]->updateState(mSimulation.actors[i].currentPose, skinningMatricesReal, sizeof(physx::PxMat44), numSkinningMatricesReal);
+ }
+
+ if (j < mSimulation.actors[i].actors.size())
+ {
+ nvidia::apex::ClothingActor* actor = mSimulation.actors[i].actors[j];
+ PX_ASSERT(actor != NULL);
+
+ actor->setGraphicalLOD((uint32_t)mConfig.simulation.graphicalLod);
+
+ nvidia::apex::ClothingTeleportMode::Enum teleportMode = !animationJumped ?
+ nvidia::apex::ClothingTeleportMode::Continuous : nvidia::apex::ClothingTeleportMode::TeleportAndReset;
+
+#if TELEPORT_TEST
+ // Test teleport feature, make sure to also comment line 1633!! (above where the CCT position is set every frame, it ends in " + mSimulation.CCTPosition);")
+ static float timeout = 2.0f;
+ static int changeIndex = 0;
+ if (timeout < 0.0f)
+ {
+ timeout = 2.0f;
+
+ PxVec3 upAxis(0.0f);
+ upAxis[mConfig.ui.zAxisUp ? 2 : 1] = 1.0f;
+
+ PxVec3 pos1(1.0f * mState.simulationValuesScale, 0.0f, 0.0f);
+ physx::PxQuat rot1(nvidia::degToRad( 90.0f), upAxis);
+ PxVec3 pos2(5.0f * mState.simulationValuesScale, 0.0f, 0.0f);
+ physx::PxQuat rot2(nvidia::degToRad(-45.0f), upAxis);
+ physx::PxMat44 globalPoses[2] =
+ {
+ physx::PxMat44(physx::PxMat33(rot1) * mSimulation.actors[i].scale, pos1),
+ physx::PxMat44(physx::PxMat33(rot2) * mSimulation.actors[i].scale, pos2)
+ };
+
+ //mSimulation.stepsUntilPause = 2;
+ mSimulation.actors[i].currentPose = globalPoses[changeIndex];
+ changeIndex = 1 - changeIndex;
+
+ teleportMode = nvidia::apex::ClothingTeleportMode::Teleport;
+ }
+ timeout -= 1.0f / 50.0f;
+#endif
+ actor->updateState(mSimulation.actors[i].currentPose, skinningMatricesReal,
+ sizeof(physx::PxMat44), numSkinningMatricesReal, teleportMode);
+
+ NvParameterized::Interface* actorDesc = actor->getActorDesc();
+ NvParameterized::Handle handle(actorDesc);
+
+ // update wind
+ if (mConfig.simulation.windVelocity <= 0.0f)
+ {
+ mState.windOrigin = PxVec3(0.0f);
+ mState.windTarget = PxVec3(0.0f);
+ mState.drawWindTime = 0.0f;
+
+ ASSERT_TRUE(NvParameterized::setParamVec3(*actorDesc, "windParams.Velocity", PxVec3(0.0f)));
+ ASSERT_TRUE(NvParameterized::setParamF32(*actorDesc, "windParams.Adaption", 0.0f));
+ }
+ else
+ {
+ if (getZAxisUp())
+ {
+ mState.windOrigin = PxVec3(mConfig.simulation.windVelocity * mState.simulationValuesScale, 0.0f, 0.0f);
+ const physx::PxMat33 rotY(physx::PxQuat(physx::shdfnd::degToRad((float)-mConfig.simulation.windElevation), physx::PxVec3(0,1,0)));
+ const physx::PxMat33 rotZ(physx::PxQuat(physx::shdfnd::degToRad((float)+mConfig.simulation.windDirection), physx::PxVec3(0,0,1)));
+ const physx::PxMat33 rotation(rotZ * rotY);
+ mState.windOrigin = rotation * mState.windOrigin;
+ }
+ else
+ {
+ mState.windOrigin = PxVec3(mConfig.simulation.windVelocity * mState.simulationValuesScale, 0.0f, 0.0f);
+ const physx::PxMat33 rotY(physx::PxQuat(physx::shdfnd::degToRad((float)mConfig.simulation.windElevation), physx::PxVec3(0,0,1)));
+ const physx::PxMat33 rotZ(physx::PxQuat(physx::shdfnd::degToRad((float)mConfig.simulation.windDirection), physx::PxVec3(0,1,0)));
+ const physx::PxMat33 rotation(rotY * rotZ);
+ mState.windOrigin = rotation * mState.windOrigin;
+ }
+ mState.windTarget = mSimulation.actors[i].currentPose.getPosition();
+ mState.windOrigin += mState.windTarget;
+
+ ASSERT_TRUE(NvParameterized::setParamVec3(*actorDesc, "windParams.Velocity", mState.windTarget - mState.windOrigin));
+ ASSERT_TRUE(NvParameterized::setParamF32(*actorDesc, "windParams.Adaption", 1.0f));
+
+ // average the target
+ mState.windTarget = 0.8f * mState.windTarget + 0.2f * mState.windOrigin;
+
+ mState.drawWindTime = physx::PxMax(0.0f, mState.drawWindTime - deltaT);
+ }
+
+ actor->forceLod((float)mConfig.simulation.lodOverwrite);
+
+ if (mDirty.maxDistanceScale)
+ {
+ ASSERT_TRUE(NvParameterized::setParamF32(*actorDesc, "maxDistanceScale.Scale", mConfig.painting.maxDistanceScale));
+ ASSERT_TRUE(NvParameterized::setParamBool(*actorDesc, "maxDistanceScale.Multipliable", mConfig.painting.maxDistanceScaleMultipliable));
+ }
+
+ if (mDirty.clothingActorFlags)
+ {
+ ASSERT_TRUE(NvParameterized::setParamBool(*actorDesc, "flags.ParallelCpuSkinning", mConfig.apex.parallelCpuSkinning));
+ ASSERT_TRUE(NvParameterized::setParamBool(*actorDesc, "flags.RecomputeNormals", mConfig.apex.recomputeNormals));
+ ASSERT_TRUE(NvParameterized::setParamBool(*actorDesc, "flags.RecomputeTangents", mConfig.apex.recomputeTangents));
+ ASSERT_TRUE(NvParameterized::setParamBool(*actorDesc, "flags.CorrectSimulationNormals", mConfig.apex.correctSimulationNormals));
+ }
+
+ // disable skinning if done externally
+ ASSERT_TRUE(NvParameterized::setParamBool(*actorDesc, "flags.ComputeRenderData", !getMeshSkinningInApp()));
+
+ if (mDirty.blendTime)
+ {
+ ASSERT_TRUE(NvParameterized::setParamF32(*actorDesc, "maxDistanceBlendTime", mConfig.simulation.blendTime));
+ }
+
+ if (mDirty.pressure)
+ {
+ ASSERT_TRUE(NvParameterized::setParamF32(*actorDesc, "pressure", mConfig.simulation.pressure));
+ }
+
+ if (mCurrentActorDesc < mLoadedActorDescs.size())
+ {
+ actorDesc->copy(*mLoadedActorDescs[mCurrentActorDesc]);
+ if (animationJumped)
+ {
+ // overwrite loaded setting because of tool navigation
+ ASSERT_TRUE(NvParameterized::setParamI32(*actorDesc, "teleportMode", 2));
+ }
+ }
+ }
+ }
+ }
+
+ mDirty.maxDistanceScale = mDirty.clothingActorFlags = mDirty.blendTime = mDirty.pressure = false;
+
+ if (getAndClearGroundPlaneDirty())
+ {
+ if (mSimulation.groundPlane != NULL)
+ {
+ mSimulation.groundPlane->release();
+ mSimulation.groundPlane = NULL;
+ }
+
+ if (mConfig.simulation.groundplaneEnabled)
+ {
+ for (size_t i = 0; i < mSimulation.actors.size(); i++)
+ {
+ for (size_t j = 0; j < mSimulation.actors[i].actors.size(); j++)
+ {
+ nvidia::apex::ClothingActor* cActor = mSimulation.actors[i].actors[j];
+ if (mSimulation.actors[i].actorGroundPlanes[j] != NULL)
+ {
+ mSimulation.actors[i].actorGroundPlanes[j]->release();
+ mSimulation.actors[i].actorGroundPlanes[j] = NULL;
+ }
+ if (cActor)
+ {
+ float d = -mConfig.simulation.groundplane * mState.simulationValuesScale * 0.1f;
+ PxVec3 normal = mConfig.ui.zAxisUp ? PxVec3(0.0f, 0.0f, 1.0f) : PxVec3(0.0f, 1.0f, 0.0f);
+ physx::PxPlane plane(normal, d);
+ mSimulation.actors[i].actorGroundPlanes[j] = cActor->createCollisionPlane(plane);
+ }
+ }
+ }
+ }
+ else
+ {
+ for (size_t i = 0; i < mSimulation.actors.size(); i++)
+ {
+ for (size_t j = 0; j < mSimulation.actors[i].actorGroundPlanes.size(); j++)
+ {
+ if (mSimulation.actors[i].actorGroundPlanes[j] != NULL)
+ {
+ mSimulation.actors[i].actorGroundPlanes[j]->release();
+ mSimulation.actors[i].actorGroundPlanes[j] = NULL;
+ }
+ }
+ }
+ }
+ }
+
+ handleGravity();
+
+ if (mState.needsReconnect)
+ {
+ connectPVD(false);
+ mState.needsReconnect = false;
+ }
+
+
+ // advance scene
+ if (mSimulation.apexScene != NULL)
+ {
+#if 1
+
+ float dt = deltaT;
+ if (mCurrentActorDesc < mDts.size())
+ {
+ dt = mDts[mCurrentActorDesc];
+ }
+ if (mCurrentActorDesc < mGravities.size())
+ {
+ mSimulation.apexScene->setGravity(mGravities[mCurrentActorDesc]);
+ }
+ mSimulation.apexScene->simulate(dt);
+
+#else
+ // The UE3 way of stepping
+ int NumSubSteps = (int)::ceil(deltaT * mConfig.simulation.frequency);
+ NumSubSteps = physx::PxClamp(NumSubSteps, 1, 5);
+ float MaxSubstep = physx::PxClamp(deltaT / NumSubSteps, 0.0025f, 1.0f / mConfig.simulation.frequency);
+
+ for (int i = 0; i < NumSubSteps - 1; i++)
+ {
+ mSimulation.apexScene->simulate(MaxSubstep, false);
+ mSimulation.apexScene->fetchResults(true, NULL);
+ }
+ mSimulation.apexScene->simulate(MaxSubstep, true);
+
+#endif
+
+
+ mSimulation.running = true;
+ mState.currentFrameNumber++;
+ }
+}
+
+
+
+void ClothingAuthoring::stopSimulation()
+{
+ if (mSimulation.running)
+ {
+ unsigned int errorState = 0;
+ mSimulation.apexScene->fetchResults(true, &errorState);
+ PX_ASSERT(errorState == 0);
+ mSimulation.running = false;
+ mSimulation.frameNumber++;
+ }
+}
+
+
+
+void ClothingAuthoring::restartSimulation()
+{
+#if 1
+ int oldActorCount = mSimulation.actorCount;
+ setActorCount(0, false);
+ setActorCount(oldActorCount, false);
+#else
+ if (mSimulation.apexScene != NULL && mSimulation.physxScene != NULL)
+ {
+ mSimulation.apexScene->setPhysXScene(NULL);
+ mSimulation.apexScene->setPhysXScene(mSimulation.physxScene);
+ }
+#endif
+
+ for (size_t i = 0; i < _mNotifyCallbacks.size(); i++)
+ {
+ _mNotifyCallbacks[i]->notifyRestart();
+ }
+}
+
+
+
+bool ClothingAuthoring::updateCCT(float deltaT)
+{
+ bool updated = false;
+ if (mConfig.simulation.CCTSpeed > 0.0f)
+ {
+ const float cctSpeed = mConfig.simulation.CCTSpeed * mState.simulationValuesScale * 3.0f;
+ PxVec3 cctTargetSpeedVec(0.0f, 0.0f, 0.0f);
+ PxVec3 cctTargetRotation(0.0f, 0.0f, 0.0f);
+ if (!mSimulation.CCTDirection.isZero())
+ {
+ cctTargetSpeedVec = mSimulation.CCTDirection * (cctSpeed / mSimulation.CCTDirection.magnitude());
+ updated = true;
+ }
+
+ if (!mSimulation.CCTRotationDelta.isZero())
+ {
+ cctTargetRotation = mSimulation.CCTRotationDelta * physx::PxTwoPi * deltaT * mConfig.simulation.CCTSpeed;
+ updated = true;
+ }
+
+ mSimulation.CCTPose.setPosition(mSimulation.CCTPose.getPosition() + cctTargetSpeedVec * deltaT);
+
+ float rotationAngle = cctTargetRotation.normalize();
+ if (rotationAngle > 0)
+ {
+ // add rotation to CCTPose rotation
+ physx::PxMat44 rot(physx::PxQuat(rotationAngle, cctTargetRotation));
+ physx::PxMat44 poseRot = mSimulation.CCTPose;
+ PxVec3 posePos = mSimulation.CCTPose.getPosition();
+ poseRot.setPosition(PxVec3(0.0f));
+ mSimulation.CCTPose = rot * poseRot;
+ mSimulation.CCTPose.setPosition(posePos);
+ }
+ }
+ return updated;
+}
+
+
+
+void ClothingAuthoring::stepsUntilPause(int steps)
+{
+ mSimulation.stepsUntilPause = (uint32_t)steps + 1; // always need one more
+
+ if (mConfig.animation.selectedAnimation < 0)
+ {
+ setAnimation(-mConfig.animation.selectedAnimation);
+ }
+}
+
+
+
+int ClothingAuthoring::getMaxLodValue() const
+{
+ if (mSimulation.actors.size() > 0)
+ {
+ if (mSimulation.actors[0].actors.size() > 0)
+ {
+ if (mSimulation.actors[0].actors[0] != NULL)
+ {
+ float min, max;
+ bool intOnly;
+ mSimulation.actors[0].actors[0]->getLodRange(min, max, intOnly);
+
+ return (int)max;
+ }
+ }
+ }
+ return -1;
+}
+
+
+
+bool ClothingAuthoring::loadInputMesh(const char* filename, bool allowConversion, bool silentOnError, bool recurseIntoApx)
+{
+ mMeshes.clear(_mRenderResourceManager, _mResourceCallback, false);
+
+ stopSimulation();
+ resetTempConfiguration();
+
+ if (filename == NULL)
+ {
+ filename = mMeshes.inputMeshFilename.c_str();
+ }
+
+ bool OK = false;
+
+ const char* extension = filename;
+
+ const char* lastDir = std::max(::strrchr(filename, '/'), ::strrchr(filename, '\\'));
+ if (lastDir != NULL)
+ {
+ extension = lastDir + 1;
+ }
+
+ while (!OK)
+ {
+ extension = ::strchr(extension, '.'); // first dot in string
+ if (extension == NULL)
+ {
+ OK = false;
+ break;
+ }
+
+ extension++; // go beyond the '.'
+
+ if (mMeshes.inputMesh == NULL)
+ {
+ mMeshes.inputMesh = new Samples::TriangleMesh(0);
+ mMeshes.inputMesh->setTextureUVOrigin(getTextureUvOrigin());
+ }
+
+ mMeshes.inputMesh->clear(_mRenderResourceManager, _mResourceCallback);
+
+ if (::strcmp(extension, "mesh.xml") == 0)
+ {
+ OK = mMeshes.inputMesh->loadFromXML(filename, true);
+ if (OK)
+ {
+ if (mMeshes.skeleton == NULL)
+ {
+ mMeshes.skeleton = new Samples::SkeletalAnim();
+ }
+
+ // check whether there is a corresponding skeleton
+ std::string error;
+ std::string skeletonFile = filename;
+ if (!mMeshes.skeleton->loadFromXML(skeletonFile.substr(0, skeletonFile.size() - 8) + "skeleton.xml", error))
+ {
+ // error.empty() means the file couldn't be found, not an error!
+ if (!error.empty() && _mErrorCallback != NULL)
+ {
+ _mErrorCallback->reportErrorPrintf("Skeleton Import Failure", "%s", error.c_str());
+ }
+
+ delete mMeshes.skeleton;
+ mMeshes.skeleton = NULL;
+ }
+ }
+ }
+ else if (::strcmp(extension, "obj") == 0)
+ {
+ OK = mMeshes.inputMesh->loadFromObjFile(filename, true);
+ if (OK)
+ {
+ mMeshes.inputMesh->moveAboveGround(0.0f);
+ }
+ }
+ else if (::strcmp(extension, "apx") == 0 || ::strcmp(extension, "apb") == 0)
+ {
+ NvParameterized::Interface* renderMeshAssetInterface = NULL;
+
+ // try loading it as a render mesh asset
+ NvParameterized::Serializer::DeserializedData deserializedData;
+ loadParameterized(filename, NULL, deserializedData, silentOnError);
+
+ for (unsigned int i = 0; i < deserializedData.size(); i++)
+ {
+ if (::strcmp(deserializedData[i]->className(), "RenderMeshAssetParameters") == 0 && renderMeshAssetInterface == NULL)
+ {
+ renderMeshAssetInterface = deserializedData[i];
+ }
+ else if (renderMeshAssetInterface == NULL && recurseIntoApx)
+ {
+ NvParameterized::Handle handle(deserializedData[i]);
+ renderMeshAssetInterface = extractRMA(handle);
+
+ deserializedData[i]->destroy();
+ }
+ else
+ {
+ deserializedData[i]->destroy();
+ }
+ }
+
+ nvidia::apex::RenderMeshAssetAuthoring* renderMeshAssetAuthoring = NULL;
+ if (renderMeshAssetInterface != NULL)
+ {
+ renderMeshAssetAuthoring = static_cast<nvidia::apex::RenderMeshAssetAuthoring*>(_mApexSDK->createAssetAuthoring(renderMeshAssetInterface, "The Render Mesh"));
+ }
+
+ if (renderMeshAssetAuthoring != NULL)
+ {
+ mMeshes.inputMesh->initFrom(*renderMeshAssetAuthoring, true);
+
+ mMeshes.skeleton = new Samples::SkeletalAnim();
+ if (!mMeshes.skeleton->initFrom(*renderMeshAssetAuthoring))
+ {
+ delete mMeshes.skeleton;
+ mMeshes.skeleton = NULL;
+ }
+
+ for (size_t i = 0; i < mAuthoringObjects.renderMeshAssets.size(); i++)
+ {
+ mAuthoringObjects.renderMeshAssets[i]->release();
+ }
+ mAuthoringObjects.renderMeshAssets.clear();
+
+ mAuthoringObjects.renderMeshAssets.push_back(renderMeshAssetAuthoring);
+
+ OK = true;
+ }
+
+ }
+ else if (mimp::gMeshImport)
+ {
+ FILE* fph = fopen(filename, "rb");
+ if (fph)
+ {
+ fseek(fph, 0L, SEEK_END);
+ unsigned int len = (uint32_t)ftell(fph);
+ if (len > 0)
+ {
+ fseek(fph, 0L, SEEK_SET);
+ unsigned char* fileData = new unsigned char[len];
+ fread(fileData, len, 1, fph);
+ physx::Time timer;
+ mimp::MeshSystemContainer* msc = mimp::gMeshImport->createMeshSystemContainer(filename, fileData, len, 0);
+ if (msc)
+ {
+ if (::strcmp(extension, "gr2") == 0)
+ {
+ mimp::gMeshImport->scale(msc, 10);
+ }
+
+ physx::Time::Second elapsed = timer.getElapsedSeconds();
+ if (elapsed > 5.0f && allowConversion)
+ {
+ int seconds = (int)physx::PxFloor((float)elapsed);
+ int milliseconds = (int)physx::PxFloor((float)((elapsed - seconds) * 1000.0));
+
+ char temp[512];
+ sprintf_s(temp, 512, "Loading of %s took %d.%.3ds\nDo you want to save it to EazyMesh (ezm)?", filename, seconds, milliseconds);
+ elapsed = ::MessageBox(NULL, temp, "File Import Conversion", MB_YESNO) == IDYES ? 1.0 : 0.0;
+ }
+ else
+ {
+ elapsed = 0.0f;
+ }
+
+ if (elapsed == 1.0)
+ {
+ std::string newFileName = filename;
+ mimp::MeshSystem* ms = mimp::gMeshImport->getMeshSystem(msc);
+ mimp::MeshSerializeFormat format = mimp::MSF_EZMESH;
+ if (::strcmp(extension, "ezm") == 0)
+ {
+ format = mimp::MSF_PSK;
+ newFileName = newFileName.substr(0, newFileName.size() - 4) + "psk";
+ }
+ else
+ {
+ newFileName = newFileName.substr(0, newFileName.size() - 4) + "ezm";
+ }
+
+ mimp::MeshSerialize data(format);
+ bool ok = mimp::gMeshImport->serializeMeshSystem(ms, data);
+ if (ok && data.mBaseData)
+ {
+ FILE* fph = fopen(newFileName.c_str(), "wb");
+ if (fph)
+ {
+ fwrite(data.mBaseData, data.mBaseLen, 1, fph);
+ fclose(fph);
+ }
+ }
+ }
+
+ OK = mMeshes.inputMesh->loadFromMeshImport(msc, true);
+ if (OK)
+ {
+ if (mMeshes.skeleton == NULL)
+ {
+ mMeshes.skeleton = new Samples::SkeletalAnim();
+ }
+
+ std::string error;
+ if (!mMeshes.skeleton->loadFromMeshImport(msc, error, false))
+ {
+ PX_ASSERT(!error.empty());
+ if (!error.empty() && _mErrorCallback != NULL)
+ {
+ _mErrorCallback->reportErrorPrintf("SkeletalMesh Import Error", "%s", error.c_str());
+ }
+
+ delete mMeshes.skeleton;
+ mMeshes.skeleton = NULL;
+ }
+ }
+ mimp::gMeshImport->releaseMeshSystemContainer(msc);
+ }
+ delete [] fileData;
+ }
+ fclose(fph);
+ }
+ }
+ }
+
+ if (!OK)
+ {
+ if (_mErrorCallback != NULL && !silentOnError)
+ {
+ _mErrorCallback->reportErrorPrintf("Mesh Loading error", "%s unrecognized file type", filename);
+ }
+
+ delete mMeshes.inputMesh;
+ mMeshes.inputMesh = NULL;
+
+ setAuthoringState(AuthoringState::None, true);
+
+ return false;
+ }
+
+ mMeshes.inputMeshFilename = filename;
+ mMeshes.inputMesh->loadMaterials(_mResourceCallback, _mRenderResourceManager);
+
+ mMeshes.inputMesh->selectSubMesh((size_t)-1, true); // select all
+
+ if (mMeshes.skeleton != NULL)
+ {
+ mMeshes.skeletonBehind = new Samples::SkeletalAnim();
+ mMeshes.skeletonBehind->loadFromParent(mMeshes.skeleton);
+ PX_ASSERT(mMeshes.skeletonRemap.empty());
+ }
+
+ physx::PxBounds3 bounds;
+ mMeshes.inputMesh->getBounds(bounds);
+
+ // as if the character's height is about 2m
+ mState.simulationValuesScale = 0.5f * (bounds.minimum - bounds.maximum).magnitude();
+
+ // round to 1.0 if near
+ if (mState.simulationValuesScale >= 0.5f && mState.simulationValuesScale <= 2.0f)
+ {
+ mState.simulationValuesScale = 1.0f;
+ }
+
+
+ updatePaintingColors();
+ updatePainter();
+
+ mMeshes.subdivideHistory.clear();
+ mState.needsRedraw = true;
+ mDirty.maxDistancePainting = true;
+
+ setAuthoringState(AuthoringState::MeshLoaded, true);
+
+ for (size_t i = 0; i < _mNotifyCallbacks.size(); i++)
+ {
+ _mNotifyCallbacks[i]->notifyInputMeshLoaded();
+ }
+
+ return true;
+}
+
+
+
+bool ClothingAuthoring::loadAnimation(const char* filename, std::string& error)
+{
+ bool ret = false;
+
+ if (mimp::gMeshImport)
+ {
+ FILE* fph = fopen(filename, "rb");
+ if (fph)
+ {
+ fseek(fph, 0L, SEEK_END);
+ unsigned int len = (uint32_t)ftell(fph);
+ if (len > 0)
+ {
+ fseek(fph, 0L, SEEK_SET);
+ unsigned char* fileData = new unsigned char[len];
+ fread(fileData, len, 1, fph);
+ physx::Time timer;
+ mimp::MeshSystemContainer* msc = mimp::gMeshImport->createMeshSystemContainer(filename, fileData, len, 0);
+ if (msc)
+ {
+ if (mMeshes.skeleton != NULL)
+ {
+ ret = mMeshes.skeleton->loadFromMeshImport(msc, error, true);
+ }
+ mimp::gMeshImport->releaseMeshSystemContainer(msc);
+ }
+ delete [] fileData;
+ }
+ fclose(fph);
+ }
+ }
+
+ return ret;
+}
+
+
+
+NvParameterized::Interface* ClothingAuthoring::extractRMA(NvParameterized::Handle& param)
+{
+ NvParameterized::Interface* result = NULL;
+ switch (param.parameterDefinition()->type())
+ {
+ case NvParameterized::TYPE_REF:
+ {
+ NvParameterized::Interface* child = NULL;
+ param.getParamRef(child);
+
+ if (child != NULL && ::strcmp(child->className(), "RenderMeshAssetParameters") == 0)
+ {
+ result = child;
+ param.setParamRef(NULL, false);
+
+ EXPECT_TRUE(NvParameterized::setParamBool(*child, "isReferenced", false));
+ }
+ else
+ {
+ NvParameterized::Handle childHandle(child);
+ result = extractRMA(childHandle);
+ }
+ }
+ break;
+
+ case NvParameterized::TYPE_STRUCT:
+ case NvParameterized::TYPE_ARRAY:
+ {
+ int arraySize = 0;
+ if (param.parameterDefinition()->type() == NvParameterized::TYPE_ARRAY)
+ {
+ param.getArraySize(arraySize, 0);
+ }
+ else
+ {
+ arraySize = param.parameterDefinition()->numChildren();
+ }
+
+ for (int i = 0; i < arraySize && result == NULL; i++)
+ {
+ param.set(i);
+
+ result = extractRMA(param);
+
+ param.popIndex();
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return result;
+}
+
+
+
+bool ClothingAuthoring::saveInputMeshToXml(const char* filename)
+{
+ if (mMeshes.inputMesh == NULL || mMeshes.inputMesh->getNumVertices() == 0)
+ {
+ if (_mErrorCallback != NULL)
+ {
+ _mErrorCallback->reportErrorPrintf("saveInputMeshToXml Error", "Load a mesh first");
+ }
+
+ return false;
+ }
+
+// int l = (int)strlen(filename);
+ bool OK = mMeshes.inputMesh->saveToXML(filename);
+
+ if (OK && mMeshes.skeleton != NULL && !mMeshes.skeleton->getAnimations().empty())
+ {
+ std::string skeletonFile = filename;
+
+ if (strstr(filename, ".mesh.xml") != NULL)
+ {
+ skeletonFile = skeletonFile.substr(0, skeletonFile.size() - 8) + "skeleton.xml";
+ }
+ else
+ {
+ skeletonFile = skeletonFile.substr(0, skeletonFile.size() - 3) + "skeleton.xml";
+ }
+
+ OK = mMeshes.skeleton->saveToXML(skeletonFile.c_str());
+ }
+
+ if (!OK)
+ {
+ if (_mErrorCallback != NULL)
+ {
+ _mErrorCallback->reportErrorPrintf("SaveMeshToXML Error", "%s save error", filename);
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+
+
+bool ClothingAuthoring::saveInputMeshToEzm(const char* filename)
+{
+ if (mMeshes.inputMesh == NULL || mMeshes.inputMesh->getNumVertices() == 0)
+ {
+ return false;
+ }
+
+ const char* extension = strrchr(filename, '.');
+
+ mimp::MeshSerializeFormat format = mimp::MSF_LAST;
+ if (::strcmp(extension, ".ezm") == 0)
+ {
+ format = mimp::MSF_EZMESH;
+ }
+ else if (::strcmp(extension, ".ezb") == 0)
+ {
+ format = mimp::MSF_EZB;
+ }
+
+ bool ok = false;
+
+ if (format != mimp::MSF_LAST)
+ {
+ mimp::MeshSystemContainer* msc = mimp::gMeshImport->createMeshSystemContainer();
+
+ if (msc != NULL)
+ {
+ bool ok = mMeshes.inputMesh->saveToMeshImport(msc);
+
+ if (ok && mMeshes.skeleton != NULL)
+ {
+ ok &= mMeshes.skeleton->saveToMeshImport(msc);
+ }
+
+ if (ok)
+ {
+ mimp::MeshSystem* ms = mimp::gMeshImport->getMeshSystem(msc);
+
+ mimp::MeshSerialize data(format);
+ ok = mimp::gMeshImport->serializeMeshSystem(ms, data);
+ if (ok && data.mBaseData)
+ {
+ FILE* fph = fopen(filename, "wb");
+ if (fph)
+ {
+ fwrite(data.mBaseData, data.mBaseLen, 1, fph);
+ fclose(fph);
+ }
+ }
+
+ mimp::gMeshImport->releaseSerializeMemory(data);
+ }
+
+ mimp::MeshSystem* ms = mimp::gMeshImport->getMeshSystem(msc);
+ for (unsigned int m = 0; m < ms->mMeshCount; m++)
+ {
+ for (unsigned int sm = 0; sm < ms->mMeshes[m]->mSubMeshCount; sm++)
+ {
+ ::free(ms->mMeshes[m]->mSubMeshes[sm]->mIndices);
+ ::free((char*)ms->mMeshes[m]->mSubMeshes[sm]->mMaterialName);
+ delete ms->mMeshes[m]->mSubMeshes[sm];
+ }
+ ::free(ms->mMeshes[m]->mSubMeshes);
+
+ ::free(ms->mMeshes[m]->mVertices);
+
+ ::free((char*)ms->mMeshes[m]->mName);
+
+ delete ms->mMeshes[m];
+ }
+ ::free(ms->mMeshes);
+
+ for (unsigned int s = 0; s < ms->mSkeletonCount; s++)
+ {
+ for (int b = 0; b< ms->mSkeletons[s]->mBoneCount; b++)
+ {
+ ::free((char*)ms->mSkeletons[s]->mBones[b].mName);
+ }
+ ::free(ms->mSkeletons[s]->mBones);
+ delete ms->mSkeletons[s];
+ }
+ ::free(ms->mSkeletons);
+
+ for (unsigned int a = 0; a < ms->mAnimationCount; a++)
+ {
+ for (int t = 0; t < ms->mAnimations[a]->mTrackCount; t++)
+ {
+ mimp::MeshAnimTrack* track = ms->mAnimations[a]->mTracks[t];
+ track->mName = NULL; // not allocated
+
+ ::free(track->mPose);
+
+ delete ms->mAnimations[a]->mTracks[t];
+ }
+ ::free(ms->mAnimations[a]->mTracks);
+
+ ::free((char*)ms->mAnimations[a]->mName);
+ delete ms->mAnimations[a];
+ }
+ ::free(ms->mAnimations);
+
+ mimp::gMeshImport->releaseMeshSystemContainer(msc);
+ }
+ }
+
+ return ok;
+}
+
+
+
+void ClothingAuthoring::selectSubMesh(int subMeshNr, bool on)
+{
+ if (mMeshes.inputMesh == NULL)
+ {
+ return;
+ }
+
+ bool dirtyChanged = false;
+ if (subMeshNr == -1)
+ {
+ for (size_t i = 0; i < mMeshes.inputMesh->getNumSubmeshes(); i++)
+ {
+ const Samples::TriangleSubMesh* submesh = mMeshes.inputMesh->getSubMesh(i);
+ if (submesh != NULL)
+ {
+ dirtyChanged |= submesh->selected != on;
+ }
+ }
+ }
+ else
+ {
+ const Samples::TriangleSubMesh* submesh = mMeshes.inputMesh->getSubMesh((uint32_t)subMeshNr);
+ if (submesh != NULL)
+ {
+ dirtyChanged |= submesh->selected != on;
+ }
+ }
+
+ mDirty.workspace |= dirtyChanged;
+ mDirty.maxDistancePainting |= dirtyChanged;
+
+ mMeshes.inputMesh->selectSubMesh((uint32_t)subMeshNr, on);
+
+ setAuthoringState(AuthoringState::SubmeshSelectionChanged, false);
+
+ // restrict painting:
+ setPainterIndexBufferRange();
+}
+
+
+
+bool ClothingAuthoring::loadCustomPhysicsMesh(const char* filename)
+{
+ clearCustomPhysicsMesh();
+
+ if (filename == NULL)
+ {
+ return true;
+ }
+
+ if (mMeshes.customPhysicsMesh == NULL)
+ {
+ mMeshes.customPhysicsMesh = new Samples::TriangleMesh(0);
+ }
+
+ const char* extension = filename;
+
+ const char* lastDir = std::max(::strrchr(filename, '/'), ::strrchr(filename, '\\'));
+ if (lastDir != NULL)
+ {
+ extension = lastDir;
+ }
+
+ bool OK = false;
+
+ while (!OK)
+ {
+ extension = ::strchr(extension, '.'); // first dot in string
+ if (extension == NULL)
+ {
+ return false;
+ }
+
+ extension++; // go beyond the '.'
+
+
+ //mCustomPhysicsMeshFilename = filename;
+
+ if (::strcmp(extension, "obj") == 0)
+ {
+ OK = mMeshes.customPhysicsMesh->loadFromObjFile(filename, false);
+ }
+ else if (::strcmp(extension, "mesh.xml") == 0)
+ {
+ OK = mMeshes.customPhysicsMesh->loadFromXML(filename, false);
+ }
+ else if (mimp::gMeshImport)
+ {
+ FILE* fph = fopen(filename, "rb");
+ if (fph != NULL)
+ {
+ fseek(fph, 0L, SEEK_END);
+ size_t len = (uint32_t)ftell(fph);
+ if (len > 0)
+ {
+ fseek(fph, 0L, SEEK_SET);
+ unsigned char* data = new unsigned char[len];
+ fread(data, len, 1, fph);
+ mimp::MeshSystemContainer* msc = mimp::gMeshImport->createMeshSystemContainer(filename, data, (uint32_t)len, 0);
+ if (msc != NULL)
+ {
+ if (::strcmp(extension, "gr2") == 0)
+ {
+ mimp::gMeshImport->scale(msc, 10);
+ }
+
+ OK = mMeshes.customPhysicsMesh->loadFromMeshImport(msc, true);
+ mimp::gMeshImport->releaseMeshSystemContainer(msc);
+ }
+ }
+ fclose(fph);
+ }
+ }
+ }
+
+ if (!OK || mMeshes.customPhysicsMesh->getVertices().empty())
+ {
+ mMeshes.customPhysicsMesh->clear(_mRenderResourceManager, _mResourceCallback);
+ delete mMeshes.customPhysicsMesh;
+ mMeshes.customPhysicsMesh = NULL;
+
+ mMeshes.customPhysicsMeshFilename.clear();
+ return false;
+ }
+
+ mMeshes.customPhysicsMesh->loadMaterials(_mResourceCallback, _mRenderResourceManager, true);
+ mMeshes.customPhysicsMesh->setSubMeshColor((size_t)-1, 0xff00ff);
+
+ mMeshes.customPhysicsMeshFilename = filename;
+ return true;
+}
+
+
+
+void ClothingAuthoring::clearCustomPhysicsMesh()
+{
+ if (mMeshes.customPhysicsMesh != NULL)
+ {
+ mMeshes.customPhysicsMesh->clear(_mRenderResourceManager, _mResourceCallback);
+ delete mMeshes.customPhysicsMesh;
+ mMeshes.customPhysicsMesh = NULL;
+
+ mMeshes.customPhysicsMeshFilename.clear();
+ }
+}
+
+
+
+int ClothingAuthoring::subdivideSubmesh(int subMeshNumber)
+{
+ if (mMeshes.inputMesh == NULL)
+ {
+ return 0;
+ }
+
+ const size_t oldNumIndices = mMeshes.inputMesh->getNumIndices();
+ const Samples::TriangleSubMesh* submesh = mMeshes.inputMesh->getSubMesh((uint32_t)subMeshNumber);
+ if (submesh != NULL)
+ {
+ Meshes::SubdivideHistoryItem item;
+ item.submesh = subMeshNumber;
+ item.subdivision = mConfig.mesh.originalMeshSubdivision;
+ mMeshes.subdivideHistory.push_back(item);
+
+ mMeshes.inputMesh->subdivideSubMesh(item.submesh, item.subdivision, mConfig.mesh.evenOutVertexDegrees);
+ mMeshes.inputMesh->updatePaintingColors(Samples::PC_NUM_CHANNELS, mConfig.painting.valueMin, mConfig.painting.valueMax, (uint32_t)mConfig.painting.valueFlag, _mApexRenderDebug);
+
+ mDirty.workspace = true;
+ updatePainter();
+ setPainterIndexBufferRange();
+ }
+ else
+ {
+ // subdivide all selected
+ for (uint32_t i = 0; i < mMeshes.inputMesh->getNumSubmeshes(); i++)
+ {
+ submesh = mMeshes.inputMesh->getSubMesh(i);
+ if (submesh->selected)
+ {
+ subdivideSubmesh((int32_t)i);
+ }
+ }
+ }
+
+ return (int)(mMeshes.inputMesh->getNumIndices() - oldNumIndices) / 3;
+}
+
+
+
+void ClothingAuthoring::renameSubMeshMaterial(size_t submesh, const char* newName)
+{
+ if (mMeshes.inputMesh != NULL)
+ {
+ mMeshes.inputMesh->setSubMeshMaterialName(submesh, newName, _mResourceCallback);
+ mDirty.workspace = true;
+ }
+}
+
+
+
+void ClothingAuthoring::generateInputTangentSpace()
+{
+ if (mMeshes.inputMesh != NULL)
+ {
+ if (mMeshes.inputMesh->generateTangentSpace())
+ {
+ setAuthoringState(AuthoringState::PaintingChanged, false);
+ mMeshes.tangentSpaceGenerated = true;
+ }
+ }
+}
+
+
+
+void ClothingAuthoring::paint(const PxVec3& rayOrigin, const PxVec3& rayDirection, bool execute,
+ bool leftButton, bool rightButton)
+{
+ if (mMeshes.painter == NULL)
+ {
+ return;
+ }
+
+ mState.needsRedraw |= mMeshes.painter->raycastHit(); // hit last frame?
+
+ if (mMeshes.inputMesh != NULL && execute)
+ {
+ physx::PxBounds3 bounds;
+ mMeshes.inputMesh->getBounds(bounds);
+ const float boundDiagonal = (bounds.minimum - bounds.maximum).magnitude();
+
+ float scale = 1.0f;
+ if (mConfig.painting.channel == Samples::PC_MAX_DISTANCE)
+ {
+ scale *= getAbsolutePaintingScalingMaxDistance();
+ }
+ else if (mConfig.painting.channel == Samples::PC_COLLISION_DISTANCE)
+ {
+ scale *= getAbsolutePaintingScalingCollisionFactor();
+ }
+
+ const float paintingRadius = boundDiagonal * 0.002f * mConfig.painting.brushRadius;
+ const float paintingColor = mConfig.painting.value / mConfig.painting.valueMax;
+ mMeshes.painter->setRayAndRadius(rayOrigin, rayDirection, paintingRadius, mConfig.painting.brushMode,
+ mConfig.painting.falloffExponent,
+ mConfig.painting.value * scale, paintingColor);
+
+ if ((leftButton || rightButton) && mMeshes.painter->raycastHit())
+ {
+ if (mConfig.painting.channel == Samples::PC_MAX_DISTANCE ||
+ mConfig.painting.channel == Samples::PC_COLLISION_DISTANCE)
+ {
+ const float invalidValue = mConfig.painting.channel == Samples::PC_COLLISION_DISTANCE ? -1.1f : -0.01f;
+ const float minimum = mConfig.painting.channel == Samples::PC_COLLISION_DISTANCE ? -1.1f : -0.01f;
+ const float paintValue = rightButton ? invalidValue : mConfig.painting.value;
+
+ mMeshes.painter->paintFloat((uint32_t)mConfig.painting.channel, minimum, mConfig.painting.valueMax, paintValue);
+ }
+ else
+ {
+ unsigned int flag = (uint32_t)mConfig.painting.valueFlag;
+ if (rightButton)
+ {
+ flag = ~flag;
+ }
+
+ mMeshes.painter->paintFlag((uint32_t)mConfig.painting.channel, flag, rightButton);
+ }
+
+ ClothingAuthoring::updatePaintingColors();
+
+ setAuthoringState(AuthoringState::PaintingChanged, false);
+ mDirty.maxDistancePainting |= mConfig.painting.channel == Samples::PC_MAX_DISTANCE;
+ }
+
+ mState.needsRedraw |= mMeshes.painter->raycastHit(); // hit this frame?
+ }
+ else
+ {
+ // turn off brush
+ mMeshes.painter->setRayAndRadius(rayOrigin, rayDirection, 0.0f, mConfig.painting.brushMode,
+ mConfig.painting.falloffExponent, mState.simulationValuesScale,
+ mConfig.painting.value / mConfig.painting.valueMax);
+ }
+}
+
+
+
+void ClothingAuthoring::floodPainting(bool invalid)
+{
+ if (mMeshes.painter != NULL)
+ {
+ const float paintingRadius = -1; //flood
+ mMeshes.painter->setRayAndRadius(PxVec3(0.0f), PxVec3(0.0f), paintingRadius,
+ BrushMode::PaintVolumetric, mConfig.painting.falloffExponent, 0.0f,
+ mConfig.painting.value / mConfig.painting.valueMax);
+
+ if (mConfig.painting.channel == Samples::PC_MAX_DISTANCE ||
+ mConfig.painting.channel == Samples::PC_COLLISION_DISTANCE)
+ {
+ const float invalidValue = mConfig.painting.channel == Samples::PC_COLLISION_DISTANCE ? -1.1f : -0.01f;
+ const float minimum = mConfig.painting.channel == Samples::PC_COLLISION_DISTANCE ? -1.1f : -0.01f;
+ const float paintValue = invalid ? invalidValue : mConfig.painting.value;
+
+ mMeshes.painter->paintFloat((uint32_t)mConfig.painting.channel, minimum, mConfig.painting.valueMax, paintValue);
+ }
+ else
+ {
+ unsigned int flag = uint32_t(invalid ? ~mConfig.painting.valueFlag : mConfig.painting.valueFlag);
+ mMeshes.painter->paintFlag((uint32_t)mConfig.painting.channel, flag, invalid);
+ }
+
+ updatePaintingColors();
+
+ setAuthoringState(AuthoringState::PaintingChanged, false);
+ mDirty.maxDistancePainting |= mConfig.painting.channel == Samples::PC_MAX_DISTANCE;
+ mDirty.workspace = true;
+
+ mState.needsRedraw = true;
+ }
+}
+
+
+
+void ClothingAuthoring::smoothPainting(int numIterations)
+{
+ if (mMeshes.painter != NULL)
+ {
+ //mMeshes.painter->smoothFloat(mConfig.painting.channel, 0.5f, numIterations);
+ mMeshes.painter->smoothFloatFast((uint32_t)mConfig.painting.channel, (uint32_t)numIterations);
+
+ updatePaintingColors();
+
+ setAuthoringState(AuthoringState::PaintingChanged, false);
+ mDirty.maxDistancePainting |= mConfig.painting.channel == Samples::PC_MAX_DISTANCE;
+ mDirty.workspace = true;
+ }
+}
+
+
+
+void ClothingAuthoring::updatePainter()
+{
+ if (mMeshes.painter == NULL)
+ {
+ return;
+ }
+
+ mMeshes.painter->clear();
+
+ Samples::TriangleMesh* inputMesh = mMeshes.inputMesh;
+
+ if (inputMesh != NULL)
+ {
+ const std::vector<PxVec3> &verts = inputMesh->getVertices();
+ const std::vector<uint32_t> &indices = inputMesh->getIndices();
+
+ if (!verts.empty())
+ {
+ mMeshes.painter->initFrom(&verts[0], (int)verts.size(), sizeof(PxVec3), &indices[0], (int)indices.size(), sizeof(uint32_t));
+ mMeshes.painter->setFloatBuffer(Samples::PC_MAX_DISTANCE, &inputMesh->getPaintChannel(Samples::PC_MAX_DISTANCE)[0].paintValueF32, sizeof(Samples::PaintedVertex));
+ mMeshes.painter->setFloatBuffer(Samples::PC_COLLISION_DISTANCE, &inputMesh->getPaintChannel(Samples::PC_COLLISION_DISTANCE)[0].paintValueF32, sizeof(Samples::PaintedVertex));
+ mMeshes.painter->setFlagBuffer(Samples::PC_LATCH_TO_NEAREST_SLAVE, &inputMesh->getPaintChannel(Samples::PC_LATCH_TO_NEAREST_SLAVE)[0].paintValueU32, sizeof(Samples::PaintedVertex));
+ mMeshes.painter->setFlagBuffer(Samples::PC_LATCH_TO_NEAREST_MASTER, &inputMesh->getPaintChannel(Samples::PC_LATCH_TO_NEAREST_MASTER)[0].paintValueU32, sizeof(Samples::PaintedVertex));
+ }
+ }
+}
+
+
+
+void ClothingAuthoring::setPainterIndexBufferRange()
+{
+ if (mMeshes.painter == NULL)
+ {
+ return;
+ }
+
+ mMeshes.painter->clearIndexBufferRange();
+
+ Samples::TriangleMesh* inputMesh = mMeshes.inputMesh;
+
+ if (inputMesh != NULL && inputMesh->getNumIndices() > 0)
+ {
+ for (size_t i = 0; i < inputMesh->getNumSubmeshes(); i++)
+ {
+ const Samples::TriangleSubMesh* submesh = inputMesh->getSubMesh(i);
+ if (submesh->selected)
+ {
+ mMeshes.painter->addIndexBufferRange(submesh->firstIndex, submesh->firstIndex + submesh->numIndices);
+ }
+ }
+ }
+}
+
+
+
+void ClothingAuthoring::initGroundMesh(const char* resourceDir)
+{
+ if (mMeshes.groundMesh == NULL)
+ {
+ mMeshes.groundMesh = new Samples::TriangleMesh(0);
+
+ mMeshes.groundMesh->initPlane(50.0f, 5.0f, "ClothingToolGround");
+
+ mMeshes.groundMesh->loadMaterials(_mResourceCallback, _mRenderResourceManager, false, resourceDir);
+ mMeshes.groundMesh->setCullMode(nvidia::apex::RenderCullMode::CLOCKWISE, -1);
+ }
+}
+
+
+
+void ClothingAuthoring::updatePaintingColors()
+{
+ if (mMeshes.inputMesh != NULL)
+ {
+ if (mConfig.painting.channel == Samples::PC_MAX_DISTANCE)
+ {
+ mMeshes.inputMesh->updatePaintingColors(Samples::PC_MAX_DISTANCE, mConfig.painting.valueMin, mConfig.painting.valueMax, 0, _mApexRenderDebug);
+ }
+ else
+ {
+ const float maxDistMax = mMeshes.inputMesh->getMaximalMaxDistance();
+ mMeshes.inputMesh->updatePaintingColors(Samples::PC_MAX_DISTANCE, 0, maxDistMax, 0, _mApexRenderDebug);
+ }
+
+ mMeshes.inputMesh->updatePaintingColors(Samples::PC_COLLISION_DISTANCE, 0, 0, 0, _mApexRenderDebug);
+ mMeshes.inputMesh->updatePaintingColors(Samples::PC_LATCH_TO_NEAREST_SLAVE, 0, 0, (uint32_t)mConfig.painting.valueFlag, _mApexRenderDebug);
+ mMeshes.inputMesh->updatePaintingColors(Samples::PC_LATCH_TO_NEAREST_MASTER, 0, 0, (uint32_t)mConfig.painting.valueFlag, _mApexRenderDebug);
+
+ }
+}
+
+
+
+bool ClothingAuthoring::getMaxDistancePaintValues(const float*& values, int& numValues, int& byteStride)
+{
+ if (mMeshes.inputMesh == NULL || mMeshes.inputMesh->getPaintChannel(Samples::PC_MAX_DISTANCE).empty())
+ {
+ values = NULL;
+ numValues = 0;
+ }
+ else
+ {
+ values = &mMeshes.inputMesh->getPaintChannel(Samples::PC_MAX_DISTANCE)[0].paintValueF32;
+ numValues = (int)mMeshes.inputMesh->getVertices().size();
+ }
+ byteStride = sizeof(Samples::PaintedVertex);
+
+ return getAndClearMaxDistancePaintingDirty();
+}
+
+
+
+float ClothingAuthoring::getAbsolutePaintingScalingMaxDistance()
+{
+ if (mMeshes.inputMesh != NULL)
+ {
+ physx::PxBounds3 bounds;
+ mMeshes.inputMesh->getBounds(bounds);
+ return (bounds.minimum - bounds.maximum).magnitude() * mConfig.painting.scalingMaxdistance;
+ }
+
+ return 1.0f;
+}
+
+
+
+float ClothingAuthoring::getAbsolutePaintingScalingCollisionFactor()
+{
+ if (mMeshes.inputMesh != NULL)
+ {
+ physx::PxBounds3 bounds;
+ mMeshes.inputMesh->getBounds(bounds);
+ return (bounds.minimum - bounds.maximum).magnitude() * mConfig.painting.scalingCollisionFactor;
+ }
+
+ return 1.0f;
+}
+
+
+void ClothingAuthoring::setAnimationPose(int position)
+{
+ if (mMeshes.inputMesh == NULL || mMeshes.skeleton == NULL)
+ {
+ return;
+ }
+
+ setAnimationTime((float)position);
+ const int animation = mConfig.animation.selectedAnimation;
+ if (animation != 0)
+ {
+ mMeshes.skeleton->setAnimPose(physx::PxAbs(animation) - 1, mConfig.animation.time, mConfig.animation.lockRootbone);
+ skinMeshes(mMeshes.skeleton);
+ }
+ else
+ {
+ mMeshes.skeleton->setBindPose();
+ skinMeshes(NULL);
+ }
+ mConfig.animation.showSkinnedPose = animation != 0;
+}
+
+
+
+void ClothingAuthoring::setBindPose()
+{
+ if (mConfig.animation.showSkinnedPose)
+ {
+ if (mMeshes.skeleton != NULL && mMeshes.inputMesh != NULL)
+ {
+ mMeshes.skeleton->setBindPose();
+ skinMeshes(NULL);
+ }
+ mConfig.animation.showSkinnedPose = false;
+ }
+}
+
+
+
+void ClothingAuthoring::setAnimationTime(float time)
+{
+ if (mCurrentActorDesc < mLoadedActorDescs.size())
+ {
+ mCurrentActorDesc = (unsigned int)(((float)(mLoadedActorDescs.size()-1)) / 100.0f * time );
+ stepsUntilPause(1); // need to simulate once to update
+ mState.manualAnimation = true;
+ return;
+ }
+
+ const int animation = mConfig.animation.selectedAnimation;
+
+ if (animation == 0 || mMeshes.skeleton == NULL || physx::PxAbs(animation) > (int)mMeshes.skeleton->getAnimations().size())
+ {
+ setAnimationTimes(0.0f);
+ }
+ else
+ {
+ Samples::SkeletalAnimation* anim = mMeshes.skeleton->getAnimations()[(uint32_t)physx::PxAbs(animation) - 1];
+
+ const float minTime = physx::PxMax(anim->minTime, mConfig.animation.cropMin);
+ const float maxTime = physx::PxMin(anim->maxTime, mConfig.animation.cropMax);
+ if (maxTime > minTime)
+ {
+ const float newAnimationTime = minTime + (maxTime - minTime) * time / 100.f;
+ mState.manualAnimation = physx::PxAbs(mConfig.animation.time - newAnimationTime) > 0.05f;
+
+ setAnimationTimes(newAnimationTime);
+
+ mConfig.animation.showSkinnedPose = true;
+ }
+ }
+}
+
+
+
+float ClothingAuthoring::getAnimationTime() const
+{
+ if (mCurrentActorDesc < mLoadedActorDescs.size())
+ {
+ return 100.0f / (mLoadedActorDescs.size()-1) * mCurrentActorDesc;
+ }
+
+ const int animation = mConfig.animation.selectedAnimation;
+
+ if (animation == 0 || mMeshes.skeleton == NULL || physx::PxAbs(animation) > (int)mMeshes.skeleton->getAnimations().size())
+ {
+ return 0.0f;
+ }
+
+ Samples::SkeletalAnimation* anim = mMeshes.skeleton->getAnimations()[(uint32_t)physx::PxAbs(animation) - 1];
+
+ const float minTime = physx::PxMax(anim->minTime, mConfig.animation.cropMin);
+ const float maxTime = physx::PxMin(anim->maxTime, mConfig.animation.cropMax);
+
+ if (minTime >= maxTime)
+ {
+ return minTime;
+ }
+
+ return (mConfig.animation.time - minTime) * 100.f / (maxTime - minTime);
+}
+
+
+
+bool ClothingAuthoring::updateAnimation()
+{
+ if (mCurrentActorDesc < mLoadedActorDescs.size())
+ {
+ bool jumped = getAndClearManualAnimation();
+ if (mConfig.animation.selectedAnimation > 0)
+ {
+ ++mCurrentActorDesc;
+ if (mCurrentActorDesc >=mLoadedActorDescs.size())
+ {
+ mCurrentActorDesc = 0;
+ if (!mConfig.animation.continuous)
+ jumped = true;
+ }
+ }
+ else
+ {
+ stepsUntilPause(1);
+ }
+ return jumped;
+ }
+
+ if (mMeshes.skeleton == NULL || mMeshes.inputMesh == NULL)
+ {
+ return false;
+ }
+
+ PX_ASSERT(mMeshes.skeletonBehind != NULL);
+
+ bool jumped = false;
+ const std::vector<Samples::SkeletalAnimation*> &anims = mMeshes.skeleton->getAnimations();
+ const int animation = mConfig.animation.selectedAnimation;
+ if (animation == 0 || physx::PxAbs(animation) > (int)anims.size())
+ {
+ mMeshes.skeleton->setBindPose();
+ mMeshes.skeletonBehind->setBindPose();
+
+ skinMeshes(NULL);
+ }
+ else
+ {
+ int anim = physx::PxAbs(animation) - 1;
+
+ jumped |= clampAnimation(mConfig.animation.time, true, mConfig.animation.loop, anims[(uint32_t)anim]->minTime, anims[(uint32_t)anim]->maxTime) && !mConfig.animation.continuous;
+
+ bool lockRootBone = mConfig.animation.lockRootbone;
+ mMeshes.skeleton->setAnimPose(anim, mConfig.animation.time, lockRootBone);
+ mMeshes.skeletonBehind->setAnimPose(anim, mConfig.animation.time, lockRootBone);
+
+ skinMeshes(mMeshes.skeleton);
+
+ mConfig.animation.showSkinnedPose = true;
+ }
+
+ jumped |= getAndClearManualAnimation();
+ return jumped;
+}
+
+
+
+void ClothingAuthoring::skinMeshes(Samples::SkeletalAnim* anim)
+{
+ if (anim == NULL)
+ {
+ mMeshes.inputMesh->unskin();
+ }
+ else
+ {
+ mMeshes.inputMesh->skin(*anim);
+ }
+
+ for (size_t i = 0; i < mSimulation.actors.size(); i++)
+ {
+ if (mSimulation.actors[i].triangleMesh != NULL)
+ {
+ if (anim == NULL)
+ {
+ //mSimulation.actors[i].triangleMesh->unskin();
+ }
+ else
+ {
+ mSimulation.actors[i].triangleMesh->skin(*anim/*, mSimulation.actors[i].scale*/); // scale is on globalPose
+ }
+ }
+ }
+}
+
+
+
+void ClothingAuthoring::clearCollisionVolumes()
+{
+ mMeshes.collisionVolumes.clear();
+}
+
+unsigned int ClothingAuthoring::generateCollisionVolumes(bool useCapsule, bool commandMode, bool dirtyOnly)
+{
+ if (mMeshes.skeleton == NULL)
+ {
+ return 0;
+ }
+
+ const std::vector<Samples::SkeletalBone> &bones = mMeshes.skeleton->getBones();
+ int boneCount = (int)bones.size();
+
+
+ for (int i = (int)mMeshes.collisionVolumes.size() - 1; i >= 0; --i)
+ {
+ const int boneIndex = mMeshes.collisionVolumes[(uint32_t)i].boneIndex;
+ if ((bones[(uint32_t)boneIndex].dirtyParams && dirtyOnly) || (!bones[(uint32_t)boneIndex].manualShapes && !dirtyOnly))
+ {
+ mMeshes.collisionVolumes.erase(mMeshes.collisionVolumes.begin() + i);
+ mMeshes.skeleton->clearShapeCount(boneIndex);
+ }
+ }
+
+ unsigned int dirtyCount = 0;
+ for (size_t i = 0; i < bones.size(); i++)
+ {
+ if ((bones[i].dirtyParams && dirtyOnly) || (!bones[i].manualShapes && !dirtyOnly))
+ {
+ dirtyCount++;
+ }
+
+ }
+ if (dirtyCount == 0)
+ {
+ return 0;
+ }
+
+ if (!mMeshes.collisionVolumes.empty() && !commandMode)
+ {
+ mDirty.workspace = true;
+ }
+
+ HACD::AutoGeometry* autoGeometry = createAutoGeometry();
+
+ if (autoGeometry != NULL)
+ {
+ for (int i = 0; i < boneCount; i++)
+ {
+ const Samples::SkeletalBone& source = bones[(uint32_t)i];
+ HACD::SimpleBone dest;
+ dest.mOption = static_cast<HACD::BoneOption>(source.boneOption);
+ dest.mBoneName = source.name.c_str();
+ dest.mParentIndex = source.parent;
+ if ((dirtyOnly && !source.dirtyParams) || (!dirtyOnly && source.manualShapes))
+ {
+ // disable this bone
+ dest.mBoneMinimalWeight = 100.0f;
+ }
+ else
+ {
+ dest.mBoneMinimalWeight = source.minimalBoneWeight;
+ PX_ASSERT(mMeshes.skeleton);
+ mMeshes.skeleton->setBoneDirty((uint32_t)i, false);
+ }
+ memcpy(dest.mTransform, source.bindWorldPose.front(), sizeof(float) * 16);
+ memcpy(dest.mInverseTransform, source.invBindWorldPose.front(), sizeof(float) * 16);
+ autoGeometry->addSimpleBone(dest);
+ }
+
+
+ unsigned int geomCount;
+ float autoCollapsePercent = 5.0f;
+ HACD::SimpleHull** hulls = autoGeometry->createCollisionVolumes(autoCollapsePercent, geomCount);
+
+ if (hulls == NULL)
+ {
+ geomCount = 0;
+ }
+
+ for (unsigned int i = 0; i < geomCount; i++)
+ {
+ addCollisionVolumeInternal(hulls[i], useCapsule && bones[(uint32_t)hulls[i]->mBoneIndex].allowPrimitives);
+ }
+
+ HACD::releaseAutoGeometry(autoGeometry);
+ }
+
+ if (useCapsule && mMeshes.skeleton != NULL && false)
+ {
+ for (size_t i = 0; i < mMeshes.collisionVolumes.size(); i++)
+ {
+ CollisionVolume& volume = mMeshes.collisionVolumes[i];
+
+ if (mMeshes.skeleton->getBones()[(uint32_t)volume.boneIndex].allowPrimitives)
+ {
+ continue;
+ }
+
+ computeBestFitCapsule(volume.vertices.size(), (float*)&volume.vertices[0], sizeof(PxVec3),
+ volume.capsuleRadius, volume.capsuleHeight, &volume.shapeOffset.p.x, &volume.shapeOffset.q.x, true);
+
+ // apply scale
+ volume.capsuleRadius *= 100.0f / mState.simulationValuesScale;
+ volume.capsuleHeight *= 100.0f / mState.simulationValuesScale;
+
+ // PH: if the capsule radius is != 0, we use capsules, but we keep the convex just in case
+
+ const float capsuleVolume = (volume.capsuleRadius * volume.capsuleRadius * physx::PxPi * volume.capsuleHeight) +
+ (volume.capsuleRadius * volume.capsuleRadius * volume.capsuleRadius * 4.0f / 3.0f * physx::PxPi);
+
+ // PH: this does not seem like a good idea
+ if (capsuleVolume > volume.meshVolume * 1.5f && false)
+ {
+ // turn off capsule if bad approximation
+ volume.capsuleRadius = 0.0f;
+ }
+ }
+ }
+
+ return (unsigned int)mMeshes.collisionVolumes.size();
+}
+
+
+
+ClothingAuthoring::CollisionVolume* ClothingAuthoring::addCollisionVolume(bool useCapsule, unsigned int boneIndex, bool createFromMesh)
+{
+ if (mMeshes.skeleton == NULL)
+ {
+ return NULL;
+ }
+
+ const std::vector<Samples::SkeletalBone> &bones = mMeshes.skeleton->getBones();
+ const size_t boneCount = bones.size();
+
+ if (!createFromMesh)
+ {
+ CollisionVolume volume;
+ volume.boneIndex = (int32_t)boneIndex;
+ volume.boneName = mMeshes.skeleton->getBones()[(uint32_t)boneIndex].name;
+ mMeshes.collisionVolumes.push_back(volume);
+ return &mMeshes.collisionVolumes.back();
+ }
+ else
+ {
+ HACD::AutoGeometry* autoGeometry = createAutoGeometry();
+ if (autoGeometry != NULL)
+ {
+ for (size_t i = 0; i < boneCount; i++)
+ {
+ const Samples::SkeletalBone& source = bones[i];
+ HACD::SimpleBone dest;
+ dest.mOption = static_cast<HACD::BoneOption>(source.boneOption);
+ dest.mBoneName = source.name.c_str();
+ dest.mParentIndex = source.parent;
+ dest.mBoneMinimalWeight = (i != boneIndex) ? 100.0f : source.minimalBoneWeight;
+
+ memcpy(dest.mTransform, source.bindWorldPose.front(), sizeof(float) * 16);
+ memcpy(dest.mInverseTransform, source.invBindWorldPose.front(), sizeof(float) * 16);
+
+ autoGeometry->addSimpleBone(dest);
+ }
+
+ unsigned int geomCount;
+ float autoCollapsePercent = 5.0f;
+ HACD::SimpleHull** hulls = autoGeometry->createCollisionVolumes(autoCollapsePercent, geomCount);
+
+ PX_ASSERT(geomCount <= 1); // only one bone is turned on!
+ for (unsigned int i = 0; i < geomCount; i++)
+ {
+ addCollisionVolumeInternal(hulls[i], useCapsule && bones[(uint32_t)hulls[i]->mBoneIndex].allowPrimitives);
+ }
+
+ HACD::releaseAutoGeometry(autoGeometry);
+
+ return geomCount > 0 ? &mMeshes.collisionVolumes.back() : NULL;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+ClothingAuthoring::CollisionVolume* ClothingAuthoring::getCollisionVolume(int index)
+{
+ if (index >= 0 && (size_t)index < mMeshes.collisionVolumes.size())
+ {
+ return &mMeshes.collisionVolumes[(uint32_t)index];
+ }
+
+ return NULL;
+}
+
+
+
+bool ClothingAuthoring::deleteCollisionVolume(int index)
+{
+ if (index >= 0 && (size_t)index < mMeshes.collisionVolumes.size())
+ {
+ mMeshes.collisionVolumes.erase(mMeshes.collisionVolumes.begin() + index);
+ return true;
+ }
+
+ return false;
+}
+
+
+
+void ClothingAuthoring::drawCollisionVolumes(bool wireframe) const
+{
+ if (_mApexRenderDebug == NULL || mMeshes.collisionVolumes.empty())
+ {
+ return;
+ }
+
+ RENDER_DEBUG_IFACE(_mApexRenderDebug)->pushRenderState();
+ if (!wireframe)
+ {
+ RENDER_DEBUG_IFACE(_mApexRenderDebug)->addToCurrentState(RENDER_DEBUG::DebugRenderState::SolidShaded);
+ }
+ RENDER_DEBUG_IFACE(_mApexRenderDebug)->addToCurrentState(RENDER_DEBUG::DebugRenderState::CounterClockwise);
+
+
+ for (size_t i = 0; i < mMeshes.collisionVolumes.size(); i++)
+ {
+ const CollisionVolume& volume = mMeshes.collisionVolumes[i];
+
+ physx::PxMat44 xform = volume.transform * volume.shapeOffset;
+
+ unsigned int color = 0xdcd44eff;
+ float inflation = 0.0f;
+ bool selected = false;
+
+ if (mMeshes.skeleton != NULL)
+ {
+ const std::vector<Samples::SkeletalBone> &bones = mMeshes.skeleton->getBones();
+ xform = bones[(uint32_t)volume.boneIndex].currentWorldPose * volume.shapeOffset;
+
+ if (!bones[(uint32_t)volume.boneIndex].manualShapes)
+ {
+ inflation = bones[(uint32_t)volume.boneIndex].inflateConvex * mState.simulationValuesScale / 100.0f; // a bit magic, I know
+ }
+ else if (volume.capsuleRadius <= 0.0f)
+ {
+ inflation = volume.inflation * mState.simulationValuesScale / 100.0f;
+ }
+
+ selected = bones[(uint32_t)volume.boneIndex].selected;
+ }
+ // PH: render each bone with a different color
+ if (selected)
+ {
+ color = 0xefffffff;
+ }
+ else
+ {
+ // get a color from the bone name
+ const char* str = volume.boneName.c_str();
+#if 0
+ // djb2
+ int c = *str;
+ unsigned long hash = 5381 + volume.boneIndex;
+ while (c = *str++)
+ {
+ hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+ }
+#else
+ // sdbm
+ unsigned long hash = (uint32_t)volume.boneIndex;
+ while (*str)
+ {
+ int c = *str;
+ hash = c + (hash << 6) + (hash << 16) - hash;
+ str++;
+ }
+#endif
+ color = hash | 0xff000000;
+ }
+
+ RENDER_DEBUG_IFACE(_mApexRenderDebug)->setCurrentColor(color, color);
+
+ if (volume.capsuleRadius > 0.0f)
+ {
+ const float height = volume.capsuleHeight * mState.simulationValuesScale / 100.0f;
+
+ PxVec3 p0(0.0f, -height * 0.5f, 0.0f);
+ PxVec3 p1(0.0f, height * 0.5f, 0.0f);
+ p0 = xform.transform(p0);
+ p1 = xform.transform(p1);
+
+ const float radius = volume.capsuleRadius * mState.simulationValuesScale / 100.0f;
+ RENDER_DEBUG_IFACE(_mApexRenderDebug)->setPose(xform);
+ RENDER_DEBUG_IFACE(_mApexRenderDebug)->debugCapsule(radius + inflation, height, 2);
+ RENDER_DEBUG_IFACE(_mApexRenderDebug)->setPose(physx::PxIdentity);
+ }
+ else
+ {
+ size_t tcount = volume.indices.size() / 3;
+ if (tcount > 0)
+ {
+
+ PxVec3 center(0.0f, 0.0f, 0.0f);
+
+ if (inflation != 0.0f)
+ {
+ for (size_t i = 0; i < volume.vertices.size(); i++)
+ {
+ center += volume.vertices[i];
+ }
+ center /= (float)volume.vertices.size();
+ }
+
+ const unsigned int* indices = &volume.indices[0];
+ for (size_t i = 0; i < tcount; i++)
+ {
+ unsigned int i1 = indices[i * 3 + 0];
+ unsigned int i2 = indices[i * 3 + 1];
+ unsigned int i3 = indices[i * 3 + 2];
+ PxVec3 p1 = volume.vertices[i1];
+ PxVec3 p2 = volume.vertices[i2];
+ PxVec3 p3 = volume.vertices[i3];
+ if (inflation != 0.0f)
+ {
+ PxVec3 out = p1 - center;
+ float dist = out.normalize();
+ if (dist + inflation > 0.0f)
+ {
+ p1 += inflation * out;
+ }
+ else
+ {
+ p1 = center;
+ }
+
+ out = p2 - center;
+ dist = out.normalize();
+ if (dist + inflation > 0.0f)
+ {
+ p2 += inflation * out;
+ }
+ else
+ {
+ p2 = center;
+ }
+
+ out = p3 - center;
+ dist = out.normalize();
+ if (dist + inflation > 0.0f)
+ {
+ p3 += inflation * out;
+ }
+ else
+ {
+ p3 = center;
+ }
+ }
+ PxVec3 t1, t2, t3;
+ t1 = xform.transform(p1);
+ t2 = xform.transform(p2);
+ t3 = xform.transform(p3);
+
+ RENDER_DEBUG_IFACE(_mApexRenderDebug)->debugTri(t1, t2, t3);
+ }
+ }
+ }
+ }
+
+ RENDER_DEBUG_IFACE(_mApexRenderDebug)->popRenderState();
+}
+
+
+
+
+nvidia::apex::RenderMeshAssetAuthoring* ClothingAuthoring::getRenderMeshAsset(int index)
+{
+ createRenderMeshAssets();
+
+ if (index >= mConfig.cloth.numGraphicalLods)
+ {
+ return NULL;
+ }
+
+ return mAuthoringObjects.renderMeshAssets[(uint32_t)index];
+}
+
+
+
+nvidia::apex::ClothingPhysicalMesh* ClothingAuthoring::getClothMesh(int index, nvidia::apex::IProgressListener* progress)
+{
+ createClothMeshes(progress);
+
+ if (index >= (int)mAuthoringObjects.physicalMeshes.size())
+ {
+ return NULL;
+ }
+
+ return mAuthoringObjects.physicalMeshes[(uint32_t)index];
+}
+
+
+
+void ClothingAuthoring::simplifyClothMesh(float factor)
+{
+ for (size_t i = 0; i < mAuthoringObjects.physicalMeshes.size(); i++)
+ {
+ unsigned int maxSteps = (factor == 0.0f) ? SIMPLIFICATOR_MAX_STEPS : (unsigned int)(factor * mAuthoringObjects.physicalMeshes[i]->getNumVertices());
+ unsigned int subdivSize = (factor == 0.0f) ? mConfig.cloth.simplify : 1u;
+ mAuthoringObjects.physicalMeshes[i]->simplify(subdivSize, (int32_t)maxSteps, -1, NULL);
+ }
+}
+
+
+
+int ClothingAuthoring::getNumClothTriangles() const
+{
+ if (mAuthoringObjects.physicalMeshes.size() > 0 && !mAuthoringObjects.physicalMeshes[0]->isTetrahedralMesh())
+ {
+ return (int32_t)mAuthoringObjects.physicalMeshes[0]->getNumIndices() / 3;
+ }
+
+ return 0;
+}
+
+
+
+nvidia::apex::ClothingPhysicalMesh* ClothingAuthoring::getPhysicalMesh()
+{
+ return mAuthoringObjects.physicalMeshes.empty() ? NULL : mAuthoringObjects.physicalMeshes[0];
+}
+
+
+
+nvidia::apex::ClothingAssetAuthoring* ClothingAuthoring::getClothingAsset(nvidia::apex::IProgressListener* progress)
+{
+ createClothingAsset(progress);
+
+ if (mAuthoringObjects.clothingAssetAuthoring == NULL)
+ {
+ return NULL;
+ }
+
+ updateDeformableParameters();
+
+ CurrentState::tMaterialLibraries::iterator found = mState.materialLibraries.find(mState.selectedMaterialLibrary);
+ if (found != mState.materialLibraries.end())
+ {
+ // figure out material index
+ NvParameterized::Interface* materialLibrary = found->second;
+ NvParameterized::Handle arrayHandle(materialLibrary);
+
+ NvParameterized::ErrorType error = NvParameterized::ERROR_NONE;
+ error = arrayHandle.getParameter("materials");
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ int numMaterials = 0;
+ error = arrayHandle.getArraySize(numMaterials);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ int materialIndex = 0;
+ for (int i = 0; i < numMaterials; i++)
+ {
+ error = arrayHandle.set(i);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ NvParameterized::Handle nameHandle(materialLibrary);
+ error = arrayHandle.getChildHandle(materialLibrary, "materialName", nameHandle);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ const char* materialName = NULL;
+ error = nameHandle.getParamString(materialName);
+ PX_ASSERT(error == NvParameterized::ERROR_NONE);
+
+ if (mState.selectedMaterial == materialName)
+ {
+ materialIndex = i;
+ break;
+ }
+
+ arrayHandle.popIndex();
+ }
+
+ mAuthoringObjects.clothingAssetAuthoring->setMaterialLibrary(found->second, (uint32_t)materialIndex, false);
+ }
+
+
+ if (!mMeshes.collisionVolumes.empty())
+ {
+ PX_ASSERT(mMeshes.skeleton != NULL);
+
+ mAuthoringObjects.clothingAssetAuthoring->clearAllBoneActors();
+
+ uint32_t numCapsules = (uint32_t)mMeshes.collisionVolumes.size();
+ /*
+ std::vector<const char*> boneNames(2*numCapsules);
+ std::vector<float> radii(2*numCapsules);
+ std::vector<PxVec3> localPositions(2*numCapsules);
+ std::vector<uint16_t> pairs(2*numCapsules);
+ */
+ for (size_t i = 0; i < numCapsules; i++)
+ {
+ const CollisionVolume& volume = mMeshes.collisionVolumes[i];
+ const Samples::SkeletalBone& bone = mMeshes.skeleton->getBones()[(uint32_t)volume.boneIndex];
+
+ float inflation = (bone.manualShapes ? volume.inflation : bone.inflateConvex) * mState.simulationValuesScale / 100.0f;
+
+ /*
+ boneNames[2*i] = volume.boneName.c_str();
+ boneNames[2*i+1] = volume.boneName.c_str();
+
+ radii[2*i] = volume.capsuleRadius * mState.simulationValuesScale / 100.0f + inflation;
+ radii[2*i+1] = radii[2*i];
+
+ const float halfheight = 0.5f*volume.capsuleHeight * mState.simulationValuesScale / 100.0f;
+ PxVec3 dir = volume.shapeOffset.M.getColumn(1);
+ dir.normalize();
+ localPositions[2*i] = volume.shapeOffset.t + halfheight * dir;
+ localPositions[2*i+1] = volume.shapeOffset.t - halfheight * dir;
+
+ pairs[2*i] = (uint16_t)(2*i);
+ pairs[2*i+1] = (uint16_t)(2*i+1);
+ */
+
+ if (volume.capsuleRadius > 0.0f)
+ {
+ if (bone.manualShapes)
+ {
+ inflation = 0.0f;
+ }
+
+ const float radius = volume.capsuleRadius * mState.simulationValuesScale / 100.0f;
+ const float height = volume.capsuleHeight * mState.simulationValuesScale / 100.0f;
+ mAuthoringObjects.clothingAssetAuthoring->addBoneCapsule(volume.boneName.c_str(), radius + inflation, height, volume.shapeOffset);
+ }
+ else
+ {
+ if (inflation != 0.0f)
+ {
+
+ std::vector<PxVec3> vertexCopy(volume.vertices);
+ PxVec3 center(0.0f, 0.0f, 0.0f);
+ for (size_t i = 0; i < vertexCopy.size(); i++)
+ {
+ vertexCopy[i] = volume.shapeOffset.transform(vertexCopy[i]);
+ center += vertexCopy[i];
+ }
+ center /= (float)vertexCopy.size();
+ for (size_t i = 0; i < vertexCopy.size(); i++)
+ {
+ PxVec3 out = vertexCopy[i] - center;
+ const float dist = out.normalize();
+ if (dist + inflation > 0.0f)
+ {
+ vertexCopy[i] += inflation * out;
+ }
+ }
+ mAuthoringObjects.clothingAssetAuthoring->addBoneConvex(volume.boneName.c_str(), &vertexCopy[0], (unsigned int)volume.vertices.size());
+ }
+ else
+ {
+ mAuthoringObjects.clothingAssetAuthoring->addBoneConvex(volume.boneName.c_str(), &volume.vertices[0], (unsigned int)volume.vertices.size());
+ }
+ }
+ }
+
+ //mAuthoringObjects.clothingAssetAuthoring->setCollision(&boneNames[0], &radii[0], &localPositions[0], 2*numCapsules, &pairs[0], (uint32_t)pairs.size());
+
+ }
+
+ return mAuthoringObjects.clothingAssetAuthoring;
+}
+
+
+
+
+void ClothingAuthoring::setZAxisUp(bool z)
+{
+ mDirty.workspace |= mConfig.ui.zAxisUp != z;
+ mConfig.ui.zAxisUp = z;
+
+ setGroundplane(mConfig.simulation.groundplane);
+ setGravity(mConfig.simulation.gravity);
+}
+
+
+
+void ClothingAuthoring::setCullMode(nvidia::apex::RenderCullMode::Enum cullMode)
+{
+ mDirty.workspace |= mConfig.mesh.cullMode != cullMode;
+ mConfig.mesh.cullMode = cullMode;
+
+ if (mMeshes.inputMesh != NULL)
+ {
+ mMeshes.inputMesh->setCullMode(cullMode, -1);
+ }
+}
+
+
+
+void ClothingAuthoring::setTextureUvOrigin(nvidia::apex::TextureUVOrigin::Enum origin)
+{
+ mDirty.workspace |= mConfig.mesh.textureUvOrigin != origin;
+ mConfig.mesh.textureUvOrigin = origin;
+
+ if (mMeshes.inputMesh != NULL)
+ {
+ mMeshes.inputMesh->setTextureUVOrigin(origin);
+ }
+}
+
+
+
+void ClothingAuthoring::setPaintingChannel(int channel)
+{
+ if (mConfig.painting.channel != channel)
+ {
+ mState.needsRedraw = true;
+ }
+
+ mDirty.workspace |= mConfig.painting.channel != channel;
+ mConfig.painting.channel = channel;
+
+ switch (mConfig.painting.channel)
+ {
+ case Samples::PC_MAX_DISTANCE:
+ mConfig.painting.value = physx::PxMax(-0.1f, mConfig.painting.value);
+ break;
+ case Samples::PC_COLLISION_DISTANCE:
+ if (physx::PxAbs(mConfig.painting.value) > 1.0f)
+ {
+ mConfig.painting.value = physx::PxSign(mConfig.painting.value);
+ }
+ break;
+ case Samples::PC_LATCH_TO_NEAREST_SLAVE:
+ case Samples::PC_LATCH_TO_NEAREST_MASTER:
+ mConfig.painting.falloffExponent = 0.0f;
+ break;
+ }
+
+ if (mConfig.painting.channel != Samples::PC_NUM_CHANNELS && mMeshes.painter == NULL)
+ {
+ mMeshes.painter = new SharedTools::MeshPainter();
+ updatePainter();
+ setPainterIndexBufferRange();
+ }
+}
+
+
+
+void ClothingAuthoring::setPaintingValue(float val, float vmin, float vmax)
+{
+ PX_ASSERT(val >= 0.0f);
+ PX_ASSERT(val <= 1.0f);
+ const float vval = val * vmax + (1 - val) * vmin;
+ mDirty.workspace |= mConfig.painting.value != vval;
+ mConfig.painting.value = vval;
+
+ if (mConfig.painting.valueMin != vmin || mConfig.painting.valueMax != vmax)
+ {
+ mConfig.painting.valueMin = vmin;
+ mConfig.painting.valueMax = vmax;
+
+ mState.needsRedraw = true;
+ updatePaintingColors();
+ }
+}
+
+
+
+void ClothingAuthoring::setPaintingValueFlag(unsigned int flags)
+{
+ if ((unsigned int)mConfig.painting.valueFlag != flags)
+ {
+ mDirty.workspace = true;
+ mConfig.painting.valueFlag = (int32_t)flags;
+
+ updatePaintingColors();
+ mState.needsRedraw = true;
+ }
+}
+
+
+
+void ClothingAuthoring::setAnimation(int animation)
+{
+ if (mConfig.animation.selectedAnimation != animation)
+ {
+ mDirty.workspace = true;
+ mState.manualAnimation = mConfig.animation.selectedAnimation != -animation;
+ mConfig.animation.selectedAnimation = animation;
+
+ if (mConfig.animation.selectedAnimation > 0)
+ {
+ if (!mConfig.animation.loop && mMeshes.skeleton != NULL)
+ {
+ // set the animation to beginning if at end if looping doesn't take care of it ?!?
+
+ Samples::SkeletalAnimation* animation = mMeshes.skeleton->getAnimations()[(uint32_t)mConfig.animation.selectedAnimation - 1];
+ if (mConfig.animation.time >= animation->maxTime)
+ {
+ mConfig.animation.time = animation->minTime;
+ }
+ }
+ }
+ }
+ else if (mConfig.animation.selectedAnimation == 0 && animation == 0 && !mSimulation.paused)
+ {
+ if (_mErrorCallback != NULL)
+ {
+ _mErrorCallback->reportErrorPrintf("Animation Error", "Bind pose is selected, cannot play animation");
+ }
+ }
+ mSimulation.paused = false;
+}
+
+
+
+void ClothingAuthoring::stepAnimationTimes(float animStep)
+{
+ if (mConfig.animation.selectedAnimation > 0)
+ {
+ mConfig.animation.time += animStep;
+ }
+}
+
+
+
+bool ClothingAuthoring::clampAnimation(float& time, bool stoppable, bool loop, float minTime, float maxTime)
+{
+ bool jumped = false;
+
+// const float animLength = maxTime - minTime;
+
+ minTime = physx::PxMax(minTime, mConfig.animation.cropMin);
+ maxTime = physx::PxMin(maxTime, mConfig.animation.cropMax);
+
+ if (time < minTime)
+ {
+ if (loop)
+ {
+ jumped = true;
+ time = maxTime;
+ }
+ else
+ {
+ if (stoppable)
+ {
+ mConfig.animation.selectedAnimation = -mConfig.animation.selectedAnimation;
+ }
+
+ time = minTime;
+ }
+ }
+ else if (time > maxTime)
+ {
+ if (loop)
+ {
+ jumped = true;
+ time = minTime;
+ }
+ else
+ {
+ if (stoppable)
+ {
+ mConfig.animation.selectedAnimation = -mConfig.animation.selectedAnimation;
+ }
+
+ time = maxTime;
+ }
+ }
+
+ return jumped;
+}
+
+
+
+void ClothingAuthoring::setAnimationCrop(float min, float max)
+{
+ PX_ASSERT(min < max);
+ mConfig.animation.cropMin = min;
+ mConfig.animation.cropMax = max;
+}
+
+
+
+void ClothingAuthoring::ErrorCallback::reportErrorPrintf(const char* label, const char* fmt, ...)
+{
+ const size_t stringLength = 512;
+ char stringBuffer[stringLength];
+
+ va_list va;
+ va_start(va, fmt);
+ vsnprintf(stringBuffer, stringLength, fmt, va);
+ va_end(va);
+
+ reportError(label, stringBuffer);
+}
+
+
+
+void ClothingAuthoring::resetTempConfiguration()
+{
+ stopSimulation();
+
+ mSimulation.clear();
+
+ mMeshes.clear(_mRenderResourceManager, _mResourceCallback, false);
+ mAuthoringObjects.clear();
+ mState.init();
+
+ mConfig.apex.forceEmbedded = false;
+
+ selectMaterial("Default", "Default");
+ //releasePhysX();
+
+ mDirty.init();
+
+ mRecordCommands.clear();
+}
+
+
+
+void ClothingAuthoring::initConfiguration()
+{
+ mConfig.init();
+}
+
+
+void ClothingAuthoring::prepareConfiguration()
+{
+ mFloatConfiguration.clear();
+ mIntConfiguration.clear();
+ mBoolConfiguration.clear();
+
+#define ADD_PARAM_NEW(_CONFIG, _PARAM) _CONFIG[#_PARAM] = &_PARAM
+#define ADD_PARAM_OLD(_CONFIG, _PARAMNAME, _PARAM) _CONFIG##Old[_PARAMNAME] = &_PARAM
+
+ // configuration.UI
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.ui.zAxisUp);
+ ADD_PARAM_OLD(mBoolConfiguration, "mZAxisUp", mConfig.ui.zAxisUp);
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.ui.spotLight);
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.ui.spotLightShadow);
+
+ // configuration.mesh
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.mesh.originalMeshSubdivision);
+ ADD_PARAM_OLD(mIntConfiguration, "mOriginalMeshSubdivision", mConfig.mesh.originalMeshSubdivision);
+ ADD_PARAM_OLD(mIntConfiguration, "mSubmeshSubdiv", mConfig.mesh.originalMeshSubdivision);
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.mesh.evenOutVertexDegrees);
+ ADD_PARAM_OLD(mBoolConfiguration, "mEvenOutVertexDegrees", mConfig.mesh.evenOutVertexDegrees);
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.mesh.cullMode);
+ ADD_PARAM_OLD(mIntConfiguration, "mCullMode", mConfig.mesh.cullMode);
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.mesh.textureUvOrigin);
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.mesh.physicalMeshType);
+
+ // configuration.Apex
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.apex.parallelCpuSkinning);
+ ADD_PARAM_OLD(mBoolConfiguration, "mParallelCpuSkinning", mConfig.apex.parallelCpuSkinning);
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.apex.recomputeNormals);
+ ADD_PARAM_OLD(mBoolConfiguration, "mRecomputeNormals", mConfig.apex.recomputeNormals);
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.apex.recomputeTangents);
+ ADD_PARAM_OLD(mBoolConfiguration, "mRecomputeNormals", mConfig.apex.recomputeTangents);
+ //ADD_PARAM_NEW(mBoolConfiguration, mConfig.apex.correctSimulationNormals); // not added on purpose
+ //ADD_PARAM_NEW(mBoolConfiguration, mConfig.apex.useMorphTargetTest); // not added on purpose
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.apex.forceEmbedded);
+
+ // configuration.tempMeshes.Cloth
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.cloth.numGraphicalLods);
+ ADD_PARAM_OLD(mIntConfiguration, "mClothNumGraphicalLods", mConfig.cloth.numGraphicalLods);
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.cloth.simplify);
+ ADD_PARAM_OLD(mIntConfiguration, "mClothSimplifySL", mConfig.cloth.simplify);
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.cloth.close);
+ ADD_PARAM_OLD(mBoolConfiguration, "mCloseCloth", mConfig.cloth.close);
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.cloth.subdivide);
+ ADD_PARAM_OLD(mBoolConfiguration, "mSlSubdivideCloth", mConfig.cloth.subdivide);
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.cloth.subdivision);
+ ADD_PARAM_OLD(mIntConfiguration, "mClothMeshSubdiv", mConfig.cloth.subdivision);
+
+ // configuration.collisionVolumes
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.collisionVolumes.usePaintingChannel);
+ ADD_PARAM_OLD(mBoolConfiguration, "mCollisionVolumeUsePaintingChannel", mConfig.collisionVolumes.usePaintingChannel);
+
+ // configuration.painting
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.painting.brushMode);
+ ADD_PARAM_OLD(mIntConfiguration, "mBrushMode", mConfig.painting.brushMode);
+ ADD_PARAM_NEW(mFloatConfiguration, mConfig.painting.falloffExponent);
+ ADD_PARAM_OLD(mFloatConfiguration, "mFalloffExponent", mConfig.painting.falloffExponent);
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.painting.channel);
+ ADD_PARAM_OLD(mIntConfiguration, "mPaintingChannel", mConfig.painting.channel);
+ ADD_PARAM_NEW(mFloatConfiguration, mConfig.painting.value);
+ ADD_PARAM_OLD(mFloatConfiguration, "mPaintingValue", mConfig.painting.value);
+ //ADD_PARAM_NEW(mFloatConfiguration, mConfig.painting.valueMin); // not added on purpose
+ //ADD_PARAM_NEW(mFloatConfiguration, mConfig.painting.valueMax); // not added on purpose
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.painting.valueFlag); // not added on purpose
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.painting.brushRadius);
+ ADD_PARAM_OLD(mIntConfiguration, "mBrushRadius", mConfig.painting.brushRadius);
+ ADD_PARAM_NEW(mFloatConfiguration, mConfig.painting.scalingMaxdistance);
+ ADD_PARAM_OLD(mFloatConfiguration, "mPaintingScalingMaxdistanceNew", mConfig.painting.scalingMaxdistance);
+ ADD_PARAM_NEW(mFloatConfiguration, mConfig.painting.scalingCollisionFactor);
+ ADD_PARAM_OLD(mFloatConfiguration, "mPaintingScalingCollisionFactorNew", mConfig.painting.scalingCollisionFactor);
+ //ADD_PARAM_NEW(mFloatConfiguration, mConfig.painting.maxDistanceScale);
+ //ADD_PARAM_NEW(mBoolConfiguration, mConfig.painting.maxDistanceScaleMultipliable);
+
+ // configuration.setMeshes
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.setMeshes.deriveNormalsFromBones);
+ ADD_PARAM_OLD(mBoolConfiguration, "mDeriveNormalsFromBones", mConfig.setMeshes.deriveNormalsFromBones);
+
+ // configuration.simulation
+ ADD_PARAM_NEW(mFloatConfiguration, mConfig.simulation.frequency);
+ ADD_PARAM_OLD(mFloatConfiguration, "mSimulationFrequency", mConfig.simulation.frequency);
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.simulation.gravity);
+ ADD_PARAM_OLD(mIntConfiguration, "mGravity", mConfig.simulation.gravity);
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.simulation.groundplane);
+ ADD_PARAM_OLD(mIntConfiguration, "mGroundplane", mConfig.simulation.groundplane);
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.simulation.groundplaneEnabled);
+ ADD_PARAM_OLD(mBoolConfiguration, "mGroundplaneEnabled", mConfig.simulation.groundplaneEnabled);
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.simulation.budgetPercent);
+ ADD_PARAM_OLD(mIntConfiguration, "mBudgetPercent", mConfig.simulation.budgetPercent);
+ ADD_PARAM_NEW(mFloatConfiguration, mConfig.simulation.interCollisionDistance);
+ ADD_PARAM_OLD(mFloatConfiguration, "mInterCollisionDistance", mConfig.simulation.interCollisionDistance);
+ ADD_PARAM_NEW(mFloatConfiguration, mConfig.simulation.interCollisionStiffness);
+ ADD_PARAM_OLD(mFloatConfiguration, "mInterCollisionStiffness", mConfig.simulation.interCollisionStiffness);
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.simulation.interCollisionIterations);
+ ADD_PARAM_OLD(mIntConfiguration, "mInterCollisionIterations", mConfig.simulation.interCollisionIterations);
+ ADD_PARAM_NEW(mFloatConfiguration, mConfig.simulation.blendTime);
+ ADD_PARAM_OLD(mFloatConfiguration, "mBlendTime", mConfig.simulation.blendTime);
+ ADD_PARAM_NEW(mFloatConfiguration, mConfig.simulation.pressure);
+ //ADD_PARAM_NEW(mFloatConfiguration, mConfig.simulation.lodOverwrite); // not added on purpose
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.simulation.windDirection);
+ ADD_PARAM_OLD(mIntConfiguration, "mWindDirection", mConfig.simulation.windDirection);
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.simulation.windElevation);
+ ADD_PARAM_OLD(mIntConfiguration, "mWindElevation", mConfig.simulation.windElevation);
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.simulation.windVelocity);
+ ADD_PARAM_OLD(mIntConfiguration, "mWindVelocity", mConfig.simulation.windVelocity);
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.simulation.gpuSimulation);
+ ADD_PARAM_OLD(mBoolConfiguration, "mGpuSimulation", mConfig.simulation.gpuSimulation);
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.simulation.fallbackSkinning);
+ ADD_PARAM_OLD(mBoolConfiguration, "mFallbackSkinning", mConfig.simulation.fallbackSkinning);
+ //ADD_PARAM_NEW(mFloatConfiguration, mConfig.simulation.CCTSpeed); // Not added on purpose!
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.simulation.graphicalLod);
+ //ADD_PARAM_NEW(mBoolConfiguration, mConfig.simulation.usePreview); // Not added on purpose
+ //ADD_PARAM_NEW(mFloatConfiguration, mConfig.simulation.timingNoise); // Not added on purpose
+ //ADD_PARAM_NEW(mFloatConfiguration, mConfig.simulation.scaleFactor); // Not added on purpose
+ //ADD_PARAM_NEW(mBoolConfiguration, mConfig.simulation.localSpaceSim); // Not added on purpose
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.simulation.pvdDebug);
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.simulation.pvdProfile);
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.simulation.pvdMemory);
+
+ // configuration.animation
+ //ADD_PARAM_NEW(mBoolConfiguration, mConfig.animation.showSkinnedPose); // not done on purpose
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.animation.selectedAnimation);
+ ADD_PARAM_OLD(mIntConfiguration, "mAnimation", mConfig.animation.selectedAnimation);
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.animation.speed);
+ ADD_PARAM_OLD(mIntConfiguration, "mAnimationSpeed", mConfig.animation.speed);
+ //ADD_PARAM_NEW(mIntConfiguration, mConfig.animation.times[0]); // not done on purpose
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.animation.loop);
+ ADD_PARAM_OLD(mBoolConfiguration, "mLoopAnimation", mConfig.animation.loop);
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.animation.lockRootbone);
+ ADD_PARAM_OLD(mBoolConfiguration, "mLockRootbone", mConfig.animation.lockRootbone);
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.animation.continuous);
+ ADD_PARAM_OLD(mBoolConfiguration, "mAnimationContinuous", mConfig.animation.continuous);
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.animation.useGlobalPoseMatrices);
+ ADD_PARAM_OLD(mBoolConfiguration, "mUseGlobalPoseMatrices", mConfig.animation.useGlobalPoseMatrices);
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.animation.applyGlobalPoseInApp);
+ //ADD_PARAM_NEW(mFloatConfiguration,mConfig.animation.cropMin); // not added on purpose
+ //ADD_PARAM_NEW(mFloatConfiguration,mConfig.animation.cropMax); // not added on purpose
+
+ // configuration.deformable
+ ADD_PARAM_NEW(mFloatConfiguration, mConfig.deformable.thickness);
+ ADD_PARAM_OLD(mFloatConfiguration, "mDeformableThickness", mConfig.deformable.thickness);
+ //ADD_PARAM(mBoolConfiguration, mConfig.deformable.drawThickness); // not added on purpose
+ //ADD_PARAM_NEW(mFloatConfiguration, mConfig.deformable.selfcollisionThickness);
+ //ADD_PARAM_OLD(mFloatConfiguration, "mDeformableSelfcollisionThickness", mConfig.deformable.selfcollisionThickness);
+ //ADD_PARAM(mBoolConfiguration, mConfig.deformable.drawSelfcollisionThickness); // not added on purpose
+ ADD_PARAM_NEW(mFloatConfiguration, mConfig.deformable.virtualParticleDensity);
+ ADD_PARAM_NEW(mIntConfiguration, mConfig.deformable.hierarchicalLevels);
+ ADD_PARAM_OLD(mIntConfiguration, "mDeformableHierarchicalLevels", mConfig.deformable.hierarchicalLevels);
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.deformable.disableCCD);
+ ADD_PARAM_OLD(mBoolConfiguration, "mDeformableDisableCCD", mConfig.deformable.disableCCD);
+ //ADD_PARAM_NEW(mBoolConfiguration, mConfig.deformable.selfcollision);
+ //ADD_PARAM_OLD(mBoolConfiguration, "mDeformableSelfcollision", mConfig.deformable.selfcollision);
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.deformable.twowayInteraction);
+ ADD_PARAM_OLD(mBoolConfiguration, "mDeformableTwowayInteraction", mConfig.deformable.twowayInteraction);
+ ADD_PARAM_NEW(mBoolConfiguration, mConfig.deformable.untangling);
+ ADD_PARAM_OLD(mBoolConfiguration, "mDeformableUntangling", mConfig.deformable.untangling);
+ ADD_PARAM_NEW(mFloatConfiguration, mConfig.deformable.restLengthScale);
+
+#undef ADD_PARAM_NEW
+#undef ADD_PARAM_OLD
+}
+
+
+
+void ClothingAuthoring::addCommand(const char* commandString, int frameNumber)
+{
+ if (frameNumber == -2)
+ {
+ frameNumber = mState.currentFrameNumber;
+ }
+
+ PX_ASSERT(mRecordCommands.empty() || frameNumber >= mRecordCommands.back().frameNumber);
+
+ Command command;
+ command.frameNumber = frameNumber;
+ command.command = commandString;
+
+ mRecordCommands.push_back(command);
+}
+
+
+
+void ClothingAuthoring::clearCommands()
+{
+ mRecordCommands.clear();
+}
+
+
+
+bool ClothingAuthoring::loadParameterized(const char* filename, physx::PxFileBuf* filebuffer, NvParameterized::Serializer::DeserializedData& deserializedData, bool silent)
+{
+ bool error = false;
+ bool ownsFileBuffer = false;
+
+ if (filebuffer == NULL)
+ {
+ filebuffer = _mApexSDK->createStream(filename, physx::PxFileBuf::OPEN_READ_ONLY);
+ ownsFileBuffer = true;
+ }
+ else if (filename == NULL)
+ {
+ filename = "unnamed buffer";
+ }
+
+ NvParameterized::Serializer::SerializeType inTypeExt = extensionToType(filename);
+
+ if (filebuffer != NULL)
+ {
+ if (filebuffer->isOpen())
+ {
+ NvParameterized::Serializer::SerializeType inTypeFile = _mApexSDK->getSerializeType(*filebuffer);
+ PX_ASSERT(inTypeFile != NvParameterized::Serializer::NST_LAST);
+
+ if (inTypeFile == NvParameterized::Serializer::NST_LAST)
+ {
+ if (_mErrorCallback != NULL)
+ {
+ _mErrorCallback->reportErrorPrintf("loadParameterized error", "File \'%s\' contains neither xml nor binary data\n", filename);
+ }
+ error = true;
+ }
+ else
+ {
+ if (inTypeExt != NvParameterized::Serializer::NST_LAST && inTypeFile != inTypeExt && _mErrorCallback != NULL)
+ {
+ const char* realExtension = inTypeFile == NvParameterized::Serializer::NST_XML ? ".apx" : ".apb";
+ _mErrorCallback->reportErrorPrintf("loadParameterized error", "File \'%s\' has wrong extension should be %s\n", filename, realExtension);
+ }
+
+ NvParameterized::Serializer* serializer = _mApexSDK->createSerializer(inTypeFile);
+
+ NvParameterized::Serializer::ErrorType serError = serializer->deserialize(*filebuffer, deserializedData);
+
+ error = parameterizedError(serError, silent ? NULL : filename);
+
+ serializer->release();
+ }
+ PX_ASSERT(error || deserializedData.size() > 0);
+ }
+ if (ownsFileBuffer)
+ {
+ filebuffer->release();
+ filebuffer = NULL;
+ }
+ }
+
+ if (!error && deserializedData.size() > 0)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+
+
+bool ClothingAuthoring::saveParameterized(const char* filename, physx::PxFileBuf* filebuffer, const NvParameterized::Interface** pInterfaces, unsigned int numInterfaces)
+{
+ NvParameterized::Serializer::SerializeType serType = extensionToType(filename);
+
+
+ if (serType == NvParameterized::Serializer::NST_LAST)
+ {
+ if (_mErrorCallback != NULL)
+ {
+ _mErrorCallback->reportErrorPrintf("SaveParameterized Error", "Cannot find serialization for file \'%s\'", filename);
+ }
+ return false;
+ }
+
+ PX_ASSERT(pInterfaces != NULL);
+ if (pInterfaces == NULL)
+ {
+ return false;
+ }
+
+ for (unsigned int i = 0; i < numInterfaces; i++)
+ {
+ PX_ASSERT(pInterfaces[i] != NULL);
+ if (pInterfaces[i] == NULL)
+ {
+ return false;
+ }
+ }
+
+ bool error = false;
+ bool ownsFileBuffer = false;
+ if (filebuffer == NULL)
+ {
+ filebuffer = _mApexSDK->createStream(filename, physx::PxFileBuf::OPEN_WRITE_ONLY);
+ ownsFileBuffer = true;
+ }
+
+ if (filebuffer != NULL)
+ {
+ if (filebuffer->isOpen())
+ {
+ NvParameterized::Serializer* serializer = _mApexSDK->createSerializer(serType);
+ NvParameterized::Serializer::ErrorType serError = serializer->serialize(*filebuffer, pInterfaces, numInterfaces);
+ error = parameterizedError(serError, filename);
+
+ serializer->release();
+ }
+
+ if (ownsFileBuffer)
+ {
+ filebuffer->release();
+ filebuffer = NULL;
+ }
+ }
+
+
+ // that's stupid, on error we still create the empty or half finished file!
+ // maybe we should save to a memory stream
+ return error;
+}
+
+
+
+NvParameterized::Serializer::SerializeType ClothingAuthoring::extensionToType(const char* filename) const
+{
+ const char* lastSlash = std::max(strrchr(filename, '/'), strrchr(filename, '\\'));
+ if (lastSlash == NULL)
+ {
+ lastSlash = filename;
+ }
+
+ const char* extension = strchr(lastSlash, '.');
+
+ NvParameterized::Serializer::SerializeType serType = NvParameterized::Serializer::NST_LAST;
+
+ while (serType == NvParameterized::Serializer::NST_LAST && extension != NULL)
+ {
+ extension++; // move beyond the '.'
+ if (_stricmp(extension, "apx") == 0)
+ {
+ serType = NvParameterized::Serializer::NST_XML;
+ }
+ else if (_stricmp(extension, "apb") == 0)
+ {
+ serType = NvParameterized::Serializer::NST_BINARY;
+ }
+
+ extension = strchr(extension, '.');
+ }
+
+ return serType;
+}
+
+
+
+bool ClothingAuthoring::parameterizedError(NvParameterized::Serializer::ErrorType errorType, const char* filename)
+{
+ if (errorType != NvParameterized::Serializer::ERROR_NONE)
+ {
+ char* errorString = NULL;
+ switch (errorType)
+ {
+#define CASE(_A) case NvParameterized::Serializer::_A: errorString = #_A; break;
+ CASE(ERROR_UNKNOWN)
+ CASE(ERROR_NOT_IMPLEMENTED)
+ CASE(ERROR_INVALID_PLATFORM)
+ CASE(ERROR_INVALID_PLATFORM_NAME)
+ CASE(ERROR_INVALID_FILE_VERSION)
+ CASE(ERROR_INVALID_FILE_FORMAT)
+ CASE(ERROR_INVALID_MAGIC)
+ CASE(ERROR_STREAM_ERROR)
+ CASE(ERROR_MEMORY_ALLOCATION_FAILURE)
+ CASE(ERROR_UNALIGNED_MEMORY)
+ CASE(ERROR_PRESERIALIZE_FAILED)
+ CASE(ERROR_INTERNAL_BUFFER_OVERFLOW)
+ CASE(ERROR_OBJECT_CREATION_FAILED)
+ CASE(ERROR_CONVERSION_FAILED)
+ CASE(ERROR_VAL2STRING_FAILED)
+ CASE(ERROR_STRING2VAL_FAILED)
+ CASE(ERROR_INVALID_TYPE_ATTRIBUTE)
+ CASE(ERROR_UNKNOWN_XML_TAG)
+ CASE(ERROR_MISSING_DOCTYPE)
+ CASE(ERROR_MISSING_ROOT_ELEMENT)
+ CASE(ERROR_INVALID_NESTING)
+ CASE(ERROR_INVALID_ATTR)
+ CASE(ERROR_INVALID_ARRAY)
+ CASE(ERROR_ARRAY_INDEX_OUT_OF_RANGE)
+ CASE(ERROR_INVALID_VALUE)
+ CASE(ERROR_INVALID_INTERNAL_PTR)
+ CASE(ERROR_INVALID_PARAM_HANDLE)
+ CASE(ERROR_INVALID_RELOC_TYPE)
+ CASE(ERROR_INVALID_DATA_TYPE)
+
+#undef CASE
+ default:
+ errorString = "un-implemented error";
+ }
+
+ if (errorString != NULL && _mErrorCallback != NULL && filename != NULL)
+ {
+ _mErrorCallback->reportErrorPrintf("Serialization Error", "%s in %s", filename, errorString);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+
+
+void ClothingAuthoring::setAuthoringState(AuthoringState::Enum authoringState, bool allowAdvance)
+{
+ if (mState.authoringState > authoringState || allowAdvance)
+ {
+ mState.authoringState = authoringState;
+ }
+}
+
+
+
+HACD::AutoGeometry* ClothingAuthoring::createAutoGeometry()
+{
+ Samples::TriangleMesh* inputMesh = mMeshes.inputMesh;
+ Samples::SkeletalAnim* skeleton = mMeshes.skeleton;
+
+ if (inputMesh == NULL || skeleton == NULL)
+ {
+ return NULL;
+ }
+
+ const std::vector<Samples::SkeletalBone> &bones = skeleton->getBones();
+ unsigned int boneCount = (unsigned int)bones.size();
+
+ const std::vector<PxVec3>& meshVertices = inputMesh->getVertices();
+ const std::vector<unsigned short>& boneIndices = inputMesh->getBoneIndices();
+ const std::vector<physx::PxVec4>& boneWeights2 = inputMesh->getBoneWeights();
+
+ if (!meshVertices.empty() && (meshVertices.size() * 4) == boneIndices.size() && boneCount > 0)
+ {
+ HACD::AutoGeometry* autoGeometry = HACD::createAutoGeometry();
+
+ const std::vector<uint32_t> &meshIndices = inputMesh->getIndices();
+ const std::vector<Samples::PaintedVertex>& graphicalSlave = inputMesh->getPaintChannel(Samples::PC_LATCH_TO_NEAREST_SLAVE);
+
+ const size_t numSubmeshes = inputMesh->getNumSubmeshes();
+ for (size_t i = 0; i < numSubmeshes; i++)
+ {
+ const Samples::TriangleSubMesh& sub = *inputMesh->getSubMesh(i);
+
+ if (sub.usedForCollision)
+ {
+ const unsigned int tcount = sub.numIndices / 3;
+ for (unsigned int j = 0; j < tcount; j++)
+ {
+ const unsigned int triangleIndices[3] =
+ {
+ meshIndices[(j * 3 + 0) + sub.firstIndex ],
+ meshIndices[(j * 3 + 1) + sub.firstIndex ],
+ meshIndices[(j * 3 + 2) + sub.firstIndex ],
+ };
+
+ const bool used1 = graphicalSlave[triangleIndices[0]].paintValueU32 == 0;
+ const bool used2 = graphicalSlave[triangleIndices[1]].paintValueU32 == 0;
+ const bool used3 = graphicalSlave[triangleIndices[2]].paintValueU32 == 0;
+ if (!ClothingAuthoring::getCollisionVolumeUsePaintChannel() || (used1 && used2 && used3))
+ {
+ HACD::SimpleSkinnedVertex vertices[3];
+ for (unsigned int k = 0; k < 3; k++)
+ {
+ (PxVec3&)vertices[k].mPos[0] = meshVertices[triangleIndices[k]];
+ for (unsigned int l = 0; l < 4; l++)
+ {
+ const unsigned int boneIndex = triangleIndices[k] * 4 + l;
+ vertices[k].mBone[l] = boneIndices[boneIndex];
+ vertices[k].mWeight[l] = boneWeights2[triangleIndices[k]][l];
+ }
+ }
+
+ autoGeometry->addSimpleSkinnedTriangle(vertices[0], vertices[1], vertices[2]);
+ }
+ }
+ }
+ }
+
+ return autoGeometry;
+ }
+
+ return NULL;
+}
+
+
+
+void ClothingAuthoring::addCollisionVolumeInternal(HACD::SimpleHull* hull, bool useCapsule)
+{
+ CollisionVolume volume;
+ volume.boneIndex = hull->mBoneIndex;
+ volume.parentIndex = hull->mParentIndex;
+ volume.boneName = hull->mBoneName;
+ volume.meshVolume = hull->mMeshVolume;
+
+ physx::PxMat44 mat44;
+ memcpy(&mat44.column0.x, hull->mTransform, sizeof(float) * 16);
+ volume.transform = physx::PxTransform(mat44);
+
+
+ for (unsigned int j = 0; j < hull->mVertexCount; j++)
+ {
+ const float* p = &hull->mVertices[j * 3];
+ PxVec3 vp(p[0], p[1], p[2]);
+ volume.vertices.push_back(vp);
+ }
+
+ for (unsigned int j = 0; j < hull->mTriCount * 3; j++)
+ {
+ volume.indices.push_back(hull->mIndices[j]);
+ }
+
+
+ if (useCapsule)
+ {
+ computeBestFitCapsule(volume.vertices.size(), (float*)&volume.vertices[0], sizeof(PxVec3),
+ volume.capsuleRadius, volume.capsuleHeight, &volume.shapeOffset.p.x, &volume.shapeOffset.q.x, true);
+
+ // apply scale
+ volume.capsuleRadius *= 100.0f / mState.simulationValuesScale;
+ volume.capsuleHeight *= 100.0f / mState.simulationValuesScale;
+ }
+
+ mMeshes.collisionVolumes.push_back(volume);
+ mMeshes.skeleton->incShapeCount(volume.boneIndex);
+}
+
+
+
+struct PaintingValues
+{
+ float maxDistance;
+ float collisionSphereDistance;
+ float collisionSphereRadius;
+ unsigned int latchToNearestSlave;
+ unsigned int latchToNearestMaster;
+};
+
+
+
+void ClothingAuthoring::createRenderMeshAssets()
+{
+ if (mMeshes.inputMesh == NULL)
+ {
+ return;
+ }
+
+ if (mState.authoringState >= AuthoringState::RenderMeshAssetCreated && mAuthoringObjects.renderMeshAssets.size() == (unsigned int)mConfig.cloth.numGraphicalLods)
+ {
+ return;
+ }
+
+ for (size_t i = 0; i < mAuthoringObjects.renderMeshAssets.size(); i++)
+ {
+ mAuthoringObjects.renderMeshAssets[i]->release();
+ }
+ mAuthoringObjects.renderMeshAssets.clear();
+
+ // find maximum #bones per vertex (only the vertices that are submitted!)
+ unsigned int maxBonesPerVertex = 0;
+ if (mMeshes.inputMesh->getBoneWeights().size() == mMeshes.inputMesh->getNumVertices())
+ {
+ const unsigned int* indices = &mMeshes.inputMesh->getIndices()[0];
+ const physx::PxVec4* boneWeights2 = &mMeshes.inputMesh->getBoneWeights()[0];
+ for (size_t i = 0; i < mMeshes.inputMesh->getNumSubmeshes(); i++)
+ {
+ const Samples::TriangleSubMesh* submesh = mMeshes.inputMesh->getSubMesh(i);
+ if (submesh != NULL && submesh->selected)
+ {
+ const unsigned int start = submesh->firstIndex;
+ const unsigned int end = start + submesh->numIndices;
+
+ for (unsigned int i = start; i < end; i++)
+ {
+ const unsigned int index = indices[i];
+ for (unsigned int j = 0; j < 4; j++)
+ {
+ if (boneWeights2[index][j] != 0.0f)
+ {
+ maxBonesPerVertex = physx::PxMax(maxBonesPerVertex, j + 1);
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ std::vector<float> distances2;
+ if (mConfig.cloth.numGraphicalLods > 1)
+ {
+ // sort the edge distances for later subdivision
+ const size_t numIndices = mMeshes.inputMesh->getNumIndices();
+ const unsigned int* indices = &mMeshes.inputMesh->getIndices()[0];
+ const PxVec3* positions = &mMeshes.inputMesh->getVertices()[0];
+ distances2.reserve(numIndices);
+
+ for (size_t i = 0; i < numIndices; i += 3)
+ {
+ distances2.push_back((positions[indices[i + 0]] - positions[indices[i + 1]]).magnitudeSquared());
+ distances2.push_back((positions[indices[i + 1]] - positions[indices[i + 2]]).magnitudeSquared());
+ distances2.push_back((positions[indices[i + 2]] - positions[indices[i + 0]]).magnitudeSquared());
+ }
+
+ class Compare
+ {
+ public:
+ bool operator()(const float a, const float b) const
+ {
+ return a > b;
+ }
+
+ };
+ std::sort(distances2.begin(), distances2.end(), Compare());
+ }
+
+
+ Samples::TriangleMesh subdividedMesh(0);
+
+ for (int graphicalLod = 0; graphicalLod < mConfig.cloth.numGraphicalLods; graphicalLod++)
+ {
+ const Samples::TriangleMesh* useThisMesh = mMeshes.inputMesh;
+
+ if (graphicalLod > 0)
+ {
+ subdividedMesh.clear(NULL, NULL);
+ subdividedMesh.copyFrom(*mMeshes.inputMesh);
+ physx::PxBounds3 bounds;
+ subdividedMesh.getBounds(bounds);
+ const float dist2 = distances2[graphicalLod * distances2.size() / mConfig.cloth.numGraphicalLods];
+ int subdivision = (int)((bounds.minimum - bounds.maximum).magnitude() / physx::PxSqrt(dist2));
+ for (uint32_t j = 0; j < subdividedMesh.getNumSubmeshes(); j++)
+ {
+ const Samples::TriangleSubMesh* submesh = subdividedMesh.getSubMesh(j);
+ if (submesh != NULL && submesh->selected)
+ {
+ subdividedMesh.subdivideSubMesh((int32_t)j, subdivision, mConfig.mesh.evenOutVertexDegrees);
+ }
+ }
+ useThisMesh = &subdividedMesh;
+ }
+
+ physx::Array<nvidia::apex::RenderMeshAssetAuthoring::SubmeshDesc> submeshDescs;
+ physx::Array<nvidia::apex::RenderMeshAssetAuthoring::VertexBuffer> vertexBufferDescs;
+
+ physx::Array<PaintingValues> paintingValues((unsigned int)useThisMesh->getNumVertices());
+ {
+ const std::vector<Samples::PaintedVertex> maxDistances = useThisMesh->getPaintChannel(Samples::PC_MAX_DISTANCE);
+ const std::vector<Samples::PaintedVertex> collisionDistances = useThisMesh->getPaintChannel(Samples::PC_COLLISION_DISTANCE);
+ const std::vector<Samples::PaintedVertex> latchToNearestSlave = useThisMesh->getPaintChannel(Samples::PC_LATCH_TO_NEAREST_SLAVE);
+ const std::vector<Samples::PaintedVertex> latchToNearestMaster = useThisMesh->getPaintChannel(Samples::PC_LATCH_TO_NEAREST_MASTER);
+
+ const float paintScalingMaxDistance = getAbsolutePaintingScalingMaxDistance();
+ const float paintScalingCollisionFactor = getAbsolutePaintingScalingCollisionFactor();
+
+ for (unsigned int vertex = 0, numVertices = (unsigned int)useThisMesh->getNumVertices(); vertex < numVertices; ++vertex)
+ {
+ paintingValues[vertex].maxDistance = physx::PxMax(0.0f, maxDistances[vertex].paintValueF32 * paintScalingMaxDistance);
+
+ const float factor = collisionDistances[vertex].paintValueF32;
+ const float maxDistance = maxDistances[vertex].paintValueF32;
+
+ if (physx::PxAbs(factor) > 1.0f || maxDistance <= 0)
+ {
+ paintingValues[vertex].collisionSphereDistance = 0.0f;
+ paintingValues[vertex].collisionSphereRadius = 0.0f;
+ }
+ else
+ {
+ paintingValues[vertex].collisionSphereDistance = factor * paintScalingCollisionFactor;
+ paintingValues[vertex].collisionSphereRadius = 10.0f * maxDistance * paintScalingMaxDistance;
+ }
+
+ paintingValues[vertex].latchToNearestSlave = latchToNearestSlave[vertex].paintValueU32;
+ paintingValues[vertex].latchToNearestMaster = latchToNearestMaster[vertex].paintValueU32;
+ }
+ }
+
+ physx::Array<physx::PxVec4> tangentValues;
+ if (useThisMesh->getTangents().size() == useThisMesh->getVertices().size())
+ {
+ tangentValues.resize((uint32_t)useThisMesh->getVertices().size());
+
+ const PxVec3* normals = &useThisMesh->getNormals()[0];
+ const PxVec3* tangents = &useThisMesh->getTangents()[0];
+ const PxVec3* bitangents = &useThisMesh->getBitangents()[0];
+
+ for (uint32_t i = 0; i < tangentValues.size(); i++)
+ {
+ const float w = physx::PxSign(normals[i].cross(tangents[i]).dot(bitangents[i]));
+ PX_ASSERT(w != 0.0f);
+ tangentValues[i] = physx::PxVec4(tangents[i], w);
+ }
+ }
+
+
+ nvidia::apex::RenderMeshAssetAuthoring* renderMeshAsset = NULL;
+
+ unsigned int numSelected = 0;
+ for (size_t submeshIndex = 0, numSubmeshes = useThisMesh->getNumSubmeshes(); submeshIndex < numSubmeshes; submeshIndex++)
+ {
+ const Samples::TriangleSubMesh* submesh = useThisMesh->getSubMesh(submeshIndex);
+ if (submesh != NULL && submesh->selected)
+ {
+ numSelected++;
+ }
+ }
+ // PH: This array must be big enough from the start!
+ vertexBufferDescs.reserve(numSelected);
+ physx::Array<nvidia::apex::RenderSemanticData> customSemanticData;
+ customSemanticData.reserve(numSelected * 5);
+
+ for (size_t submeshIndex = 0, numSubmeshes = useThisMesh->getNumSubmeshes(); submeshIndex < numSubmeshes; submeshIndex++)
+ {
+ const Samples::TriangleSubMesh* submesh = useThisMesh->getSubMesh(submeshIndex);
+ if (submesh != NULL && submesh->selected)
+ {
+ nvidia::apex::RenderMeshAssetAuthoring::SubmeshDesc submeshDesc;
+
+ submeshDesc.m_numVertices = (uint32_t)useThisMesh->getVertices().size();
+
+ nvidia::apex::RenderMeshAssetAuthoring::VertexBuffer vertexBufferDesc;
+
+ vertexBufferDesc.setSemanticData(nvidia::apex::RenderVertexSemantic::POSITION,
+ &useThisMesh->getVertices()[0],
+ sizeof(PxVec3),
+ nvidia::apex::RenderDataFormat::FLOAT3);
+
+ if (useThisMesh->getNormals().size() == submeshDesc.m_numVertices)
+ {
+ vertexBufferDesc.setSemanticData(nvidia::apex::RenderVertexSemantic::NORMAL,
+ &useThisMesh->getNormals()[0],
+ sizeof(PxVec3),
+ nvidia::apex::RenderDataFormat::FLOAT3);
+ }
+
+ if (useThisMesh->getTangents().size() == submeshDesc.m_numVertices && useThisMesh->getBitangents().size() == submeshDesc.m_numVertices)
+ {
+#if 0
+ vertexBufferDesc.setSemanticData(nvidia::apex::RenderVertexSemantic::TANGENT,
+ &useThisMesh->getTangents()[0],
+ sizeof(PxVec3),
+ nvidia::apex::RenderDataFormat::FLOAT3);
+
+ vertexBufferDesc.setSemanticData(nvidia::apex::RenderVertexSemantic::BINORMAL,
+ &useThisMesh->getBitangents()[0],
+ sizeof(PxVec3),
+ nvidia::apex::RenderDataFormat::FLOAT3);
+#else
+ PX_ASSERT(tangentValues.size() == submeshDesc.m_numVertices);
+ vertexBufferDesc.setSemanticData(nvidia::apex::RenderVertexSemantic::TANGENT,
+ &tangentValues[0],
+ sizeof(physx::PxVec4),
+ nvidia::apex::RenderDataFormat::FLOAT4);
+#endif
+ }
+
+ for (uint32_t texCoord = 0; texCoord < Samples::TriangleMesh::NUM_TEXCOORDS ; texCoord++)
+ {
+ if (texCoord < 4 && useThisMesh->getTexCoords((int32_t)texCoord).size() == submeshDesc.m_numVertices)
+ {
+ nvidia::apex::RenderVertexSemantic::Enum semantic =
+ (nvidia::apex::RenderVertexSemantic::Enum)(nvidia::apex::RenderVertexSemantic::TEXCOORD0 + texCoord);
+
+ vertexBufferDesc.setSemanticData(semantic,
+ &useThisMesh->getTexCoords((int32_t)texCoord)[0],
+ sizeof(nvidia::apex::VertexUV),
+ nvidia::apex::RenderDataFormat::FLOAT2);
+ }
+ }
+
+ if (useThisMesh->getBoneWeights().size() == submeshDesc.m_numVertices && useThisMesh->getBoneIndices().size() == submeshDesc.m_numVertices * 4)
+ {
+ // check how many are actually used
+ uint32_t maxBonesPerSubmeshVertex = 0;
+ for (uint32_t index = submesh->firstIndex, end = submesh->firstIndex + submesh->numIndices; index < end; index++)
+ {
+ const uint32_t vertexIndex = useThisMesh->getIndices()[index];
+ for (uint32_t j = 0; j < 4; j++)
+ {
+ if (useThisMesh->getBoneWeights()[vertexIndex][j] != 0.0f)
+ {
+ maxBonesPerSubmeshVertex = physx::PxMax(maxBonesPerSubmeshVertex, j + 1);
+ }
+ }
+ }
+
+ nvidia::apex::RenderDataFormat::Enum format =
+ (nvidia::apex::RenderDataFormat::Enum)(nvidia::apex::RenderDataFormat::USHORT1 + maxBonesPerSubmeshVertex - 1);
+
+ vertexBufferDesc.setSemanticData(nvidia::apex::RenderVertexSemantic::BONE_INDEX,
+ &useThisMesh->getBoneIndices()[0],
+ sizeof(uint16_t) * 4,
+ format);
+
+ format = (nvidia::apex::RenderDataFormat::Enum)(nvidia::apex::RenderDataFormat::FLOAT1 + maxBonesPerSubmeshVertex - 1);
+
+ vertexBufferDesc.setSemanticData(nvidia::apex::RenderVertexSemantic::BONE_WEIGHT,
+ &useThisMesh->getBoneWeights()[0],
+ sizeof(physx::PxVec4),
+ format);
+ }
+
+ const uint32_t startCustom = customSemanticData.size();
+
+ if (useThisMesh->getPaintChannel(Samples::PC_MAX_DISTANCE).size() == useThisMesh->getNumVertices())
+ {
+ nvidia::apex::RenderSemanticData customData;
+ customData.data = &paintingValues[0].maxDistance;
+ customData.stride = sizeof(PaintingValues);
+ customData.format = nvidia::apex::RenderDataFormat::FLOAT1;
+ customData.ident = "MAX_DISTANCE";
+ customSemanticData.pushBack(customData);
+ }
+
+ if (useThisMesh->getPaintChannel(Samples::PC_COLLISION_DISTANCE).size() == useThisMesh->getNumVertices())
+ {
+ nvidia::apex::RenderSemanticData customData;
+ customData.data = &paintingValues[0].collisionSphereDistance;
+ customData.stride = sizeof(PaintingValues);
+ customData.format = nvidia::apex::RenderDataFormat::FLOAT1;
+ customData.ident = "COLLISION_SPHERE_DISTANCE";
+ customSemanticData.pushBack(customData);
+
+ customData.data = &paintingValues[0].collisionSphereRadius;
+ customData.stride = sizeof(PaintingValues);
+ customData.format = nvidia::apex::RenderDataFormat::FLOAT1;
+ customData.ident = "COLLISION_SPHERE_RADIUS";
+ customSemanticData.pushBack(customData);
+ }
+ if (useThisMesh->getPaintChannel(Samples::PC_LATCH_TO_NEAREST_SLAVE).size() == useThisMesh->getNumVertices())
+ {
+ nvidia::apex::RenderSemanticData customData;
+ customData.data = &paintingValues[0].latchToNearestSlave;
+ customData.stride = sizeof(PaintingValues);
+ customData.format = nvidia::apex::RenderDataFormat::UINT1;
+ customData.ident = "LATCH_TO_NEAREST_SLAVE";
+ customSemanticData.pushBack(customData);
+ }
+ if (useThisMesh->getPaintChannel(Samples::PC_LATCH_TO_NEAREST_MASTER).size() == useThisMesh->getNumVertices())
+ {
+ nvidia::apex::RenderSemanticData customData;
+ customData.data = &paintingValues[0].latchToNearestMaster;
+ customData.stride = sizeof(PaintingValues);
+ customData.format = nvidia::apex::RenderDataFormat::UINT1;
+ customData.ident = "LATCH_TO_NEAREST_MASTER";
+ customSemanticData.pushBack(customData);
+ }
+
+ if (startCustom < customSemanticData.size())
+ {
+ vertexBufferDesc.setCustomSemanticData(&customSemanticData[startCustom], customSemanticData.size() - startCustom);
+ }
+
+ vertexBufferDescs.pushBack(vertexBufferDesc);
+
+ submeshDesc.m_materialName = submesh->materialName.c_str();
+ submeshDesc.m_vertexBuffers = &vertexBufferDescs.back();
+ submeshDesc.m_numVertexBuffers = 1;
+
+ submeshDesc.m_primitive = nvidia::apex::RenderMeshAssetAuthoring::Primitive::TRIANGLE_LIST;
+ submeshDesc.m_indexType = nvidia::apex::RenderMeshAssetAuthoring::IndexType::UINT;
+
+ submeshDesc.m_vertexIndices = &useThisMesh->getIndices()[0] + submesh->firstIndex;
+ submeshDesc.m_numIndices = submesh->numIndices;
+ submeshDesc.m_firstVertex = 0;
+ submeshDesc.m_partIndices = NULL;
+ submeshDesc.m_numParts = 0;
+
+ submeshDesc.m_cullMode = ClothingAuthoring::getCullMode();
+
+ submeshDescs.pushBack(submeshDesc);
+ }
+ }
+
+ PX_ASSERT(customSemanticData.capacity() == numSelected * 5);
+
+ nvidia::apex::RenderMeshAssetAuthoring::MeshDesc meshDesc;
+ meshDesc.m_numSubmeshes = submeshDescs.size();
+ meshDesc.m_submeshes = submeshDescs.begin();
+ meshDesc.m_uvOrigin = mMeshes.inputMesh->getTextureUVOrigin();
+
+ char rmaName[30];
+ if (graphicalLod == 0)
+ {
+ sprintf(rmaName, "TheRMA");
+ }
+ else
+ {
+ sprintf(rmaName, "TheRMA_%i", graphicalLod);
+ }
+ renderMeshAsset = static_cast<nvidia::apex::RenderMeshAssetAuthoring*>(_mApexSDK->createAssetAuthoring(RENDER_MESH_AUTHORING_TYPE_NAME, rmaName));
+
+ renderMeshAsset->createRenderMesh(meshDesc, true);
+
+
+ if (renderMeshAsset != NULL)
+ {
+ mAuthoringObjects.renderMeshAssets.push_back(renderMeshAsset);
+ }
+ }
+
+ setAuthoringState(AuthoringState::RenderMeshAssetCreated, true);
+}
+
+
+
+void ClothingAuthoring::createCustomMesh()
+{
+ createRenderMeshAssets();
+
+ if (mMeshes.inputMesh != NULL && mMeshes.customPhysicsMesh != NULL)
+ {
+ if (mAuthoringObjects.physicalMeshes.size() == 0)
+ {
+ nvidia::apex::ClothingPhysicalMesh* physicalMesh = _mModuleClothing->createEmptyPhysicalMesh();
+ physicalMesh->setGeometry(false,
+ (uint32_t)mMeshes.customPhysicsMesh->getVertices().size(),
+ sizeof(PxVec3),
+ &mMeshes.customPhysicsMesh->getVertices()[0],
+ NULL,
+ (uint32_t)mMeshes.customPhysicsMesh->getIndices().size(),
+ sizeof(uint32_t),
+ &mMeshes.customPhysicsMesh->getIndices()[0]);
+
+ mAuthoringObjects.physicalMeshes.push_back(physicalMesh);
+
+ setAuthoringState(AuthoringState::PhysicalCustomMeshCreated, true);
+ }
+ PX_ASSERT(mAuthoringObjects.physicalMeshes.size() == 1);
+ }
+}
+
+
+
+void ClothingAuthoring::createClothMeshes(nvidia::apex::IProgressListener* progressParent)
+{
+ if (mState.authoringState >= AuthoringState::PhysicalClothMeshCreated)
+ {
+ return;
+ }
+
+ createRenderMeshAssets();
+
+ for (size_t i = 0; i < mAuthoringObjects.physicalMeshes.size(); i++)
+ {
+ if (mAuthoringObjects.physicalMeshes[i] != NULL)
+ {
+ mAuthoringObjects.physicalMeshes[i]->release();
+ }
+
+ mAuthoringObjects.physicalMeshes[i] = NULL;
+ }
+ mAuthoringObjects.physicalMeshes.clear();
+
+ ProgressCallback progress(mConfig.cloth.numGraphicalLods, progressParent);
+
+ //mApexPhysicalMeshes.resize(ClothingAuthoring::getNumRenderMeshAssets());
+ for (int32_t i = 0; i < mConfig.cloth.numGraphicalLods; i++)
+ {
+ progress.setSubtaskWork(1, "Create Physical Mesh");
+ nvidia::apex::ClothingPhysicalMesh* physicalMesh = _mModuleClothing->createSingleLayeredMesh(
+ mAuthoringObjects.renderMeshAssets[(uint32_t)i],
+ uint32_t(mConfig.cloth.subdivide ? mConfig.cloth.subdivision : 0),
+ true, // mergeVerticest
+ mConfig.cloth.close,
+ &progress);
+
+ mAuthoringObjects.physicalMeshes.push_back(physicalMesh);
+
+ progress.completeSubtask();
+ }
+
+ setAuthoringState(AuthoringState::PhysicalClothMeshCreated, true);
+ mConfig.mesh.physicalMeshType = 0;
+}
+
+
+
+void ClothingAuthoring::createClothingAsset(nvidia::apex::IProgressListener* progressParent)
+{
+ if (mState.authoringState <= AuthoringState::None || mState.authoringState >= AuthoringState::ClothingAssetCreated)
+ {
+ return;
+ }
+
+ if (mAuthoringObjects.clothingAssetAuthoring != NULL)
+ {
+ mAuthoringObjects.clothingAssetAuthoring->release();
+ mAuthoringObjects.clothingAssetAuthoring = NULL;
+ }
+
+ unsigned int totalWork = (uint32_t)mConfig.cloth.numGraphicalLods * 2;
+ ProgressCallback progress((int32_t)totalWork, progressParent);
+
+ if (mState.authoringState <= AuthoringState::RenderMeshAssetCreated)
+ {
+ createRenderMeshAssets();
+
+ progress.setSubtaskWork(mConfig.cloth.numGraphicalLods, "Create Physics Mesh");
+
+ if (mMeshes.customPhysicsMesh != NULL)
+ {
+ // only 1 lod
+ createCustomMesh();
+ }
+ else if (mConfig.mesh.physicalMeshType == 0) //cloth
+ {
+ // all lods
+ createClothMeshes(&progress);
+ }
+ else
+ {
+ PX_ASSERT(true);
+ }
+
+ progress.completeSubtask();
+ }
+
+ {
+ nvidia::apex::AssetAuthoring* assetAuthoring = _mApexSDK->createAssetAuthoring(CLOTHING_AUTHORING_TYPE_NAME, "The Authoring Asset");
+ assetAuthoring->setToolString("ClothingTool", NULL, 0);
+
+ PX_ASSERT(assetAuthoring != NULL);
+ if (assetAuthoring != NULL)
+ {
+ mAuthoringObjects.clothingAssetAuthoring = static_cast<nvidia::apex::ClothingAssetAuthoring*>(assetAuthoring);
+
+ updateDeformableParameters();
+
+ mAuthoringObjects.clothingAssetAuthoring->setDeriveNormalsFromBones(ClothingAuthoring::getDeriveNormalsFromBones());
+
+ if (mMeshes.skeleton != NULL)
+ {
+ const std::vector<Samples::SkeletalBone>& bones = mMeshes.skeleton->getBones();
+ for (unsigned int i = 0; i < (unsigned int)bones.size(); i++)
+ {
+ mAuthoringObjects.clothingAssetAuthoring->setBoneInfo(i, bones[i].name.c_str(), bones[i].bindWorldPose, bones[i].parent);
+ if (bones[i].isRoot)
+ {
+ mAuthoringObjects.clothingAssetAuthoring->setRootBone(bones[i].name.c_str());
+ }
+ }
+ }
+ }
+ }
+
+ PX_ASSERT(!mAuthoringObjects.physicalMeshes.empty());
+
+ for (uint32_t i = 0; i < mAuthoringObjects.physicalMeshes.size(); i++)
+ {
+ progress.setSubtaskWork(1, "combine graphical and physical mesh");
+
+ const uint32_t lod = mState.authoringState == AuthoringState::PhysicalClothMeshCreated ? (uint32_t)mConfig.cloth.numGraphicalLods - 1 - i : 0u;
+
+ mAuthoringObjects.clothingAssetAuthoring->setMeshes(lod,
+ mAuthoringObjects.renderMeshAssets[i],
+ mAuthoringObjects.physicalMeshes[i],
+ 25.0f, true, &progress);
+
+ progress.completeSubtask();
+ }
+
+ setAuthoringState(AuthoringState::ClothingAssetCreated, true);
+}
+
+
+
+void ClothingAuthoring::updateDeformableParameters()
+{
+ float thicknessScaling = 1.0f;
+ if (mMeshes.inputMesh != NULL)
+ {
+ physx::PxBounds3 bounds;
+ mMeshes.inputMesh->getBounds(bounds);
+ thicknessScaling = 0.1f * (bounds.minimum - bounds.maximum).magnitude();
+ }
+
+ if (mAuthoringObjects.clothingAssetAuthoring != NULL)
+ {
+ mAuthoringObjects.clothingAssetAuthoring->setSimulationThickness(mConfig.deformable.thickness * thicknessScaling);
+ mAuthoringObjects.clothingAssetAuthoring->setSimulationVirtualParticleDensity(mConfig.deformable.virtualParticleDensity);
+ mAuthoringObjects.clothingAssetAuthoring->setSimulationHierarchicalLevels((uint32_t)mConfig.deformable.hierarchicalLevels);
+ mAuthoringObjects.clothingAssetAuthoring->setSimulationGravityDirection(mConfig.ui.zAxisUp ? PxVec3(0.0f, 0.0f, -1.0f) : PxVec3(0.0f, -1.0f, 0.0f));
+
+ mAuthoringObjects.clothingAssetAuthoring->setSimulationDisableCCD(mConfig.deformable.disableCCD);
+ mAuthoringObjects.clothingAssetAuthoring->setSimulationTwowayInteraction(mConfig.deformable.twowayInteraction);
+ mAuthoringObjects.clothingAssetAuthoring->setSimulationUntangling(mConfig.deformable.untangling);
+
+ mAuthoringObjects.clothingAssetAuthoring->setSimulationRestLengthScale(mConfig.deformable.restLengthScale);
+ }
+}
+
+
+
+void ClothingAuthoring::Simulation::clear()
+{
+ for (size_t i = 0; i < actors.size(); i++)
+ {
+ for (size_t j = 0; j < actors[i].actors.size(); j++)
+ {
+ if (actors[i].actors[j] != NULL)
+ {
+ actors[i].actors[j]->release();
+ actors[i].actors[j] = NULL;
+ }
+ }
+ for (size_t j = 0; j < actors[i].previews.size(); j++)
+ {
+ if (actors[i].previews[j] != NULL)
+ {
+ actors[i].previews[j]->release();
+ actors[i].previews[j] = NULL;
+ }
+ }
+ }
+ actors.clear();
+
+ if (clearAssets)
+ {
+ for (size_t i = 0; i < assets.size(); i++)
+ {
+ for (size_t j = 0; j < assets[i].renderMeshAssets.size(); ++j)
+ {
+ if (assets[i].renderMeshAssets[j] != NULL)
+ {
+ assets[i].renderMeshAssets[j]->release();
+ }
+ }
+ assets[i].renderMeshAssets.clear();
+
+ assets[i].releaseRenderMeshAssets();
+ assets[i].apexAsset->release();
+ assets[i].apexAsset = NULL;
+ }
+ }
+ assets.clear();
+ clearAssets = true;
+
+ actorCount = 1;
+ stepsUntilPause = 0;
+}
+
+
+
+void ClothingAuthoring::Meshes::clear(nvidia::apex::UserRenderResourceManager* rrm, nvidia::apex::ResourceCallback* rcb, bool groundAsWell)
+{
+ if (inputMesh != NULL)
+ {
+ inputMesh->clear(rrm, rcb);
+ delete inputMesh;
+ inputMesh = NULL;
+ }
+ //inputMeshFilename.clear(); // PH: MUST not be reset
+
+ if (painter != NULL)
+ {
+ delete painter;
+ painter = NULL;
+ }
+
+ if (customPhysicsMesh != NULL)
+ {
+ customPhysicsMesh->clear(rrm, rcb);
+ delete customPhysicsMesh;
+ customPhysicsMesh = NULL;
+ }
+ customPhysicsMeshFilename.clear();
+
+ if (groundMesh != NULL && groundAsWell)
+ {
+ groundMesh->clear(rrm, rcb);
+ delete groundMesh;
+ groundMesh = NULL;
+ }
+
+ if (skeleton != NULL)
+ {
+ delete skeleton;
+ skeleton = NULL;
+ }
+
+ if (skeletonBehind != NULL)
+ {
+ delete skeletonBehind;
+ skeletonBehind = NULL;
+ }
+ skeletonRemap.clear();
+
+ collisionVolumes.clear();
+ subdivideHistory.clear();
+}
+
+
+
+void ClothingAuthoring::AuthoringObjects::clear()
+{
+ for (size_t i = 0; i < renderMeshAssets.size(); i++)
+ {
+ if (renderMeshAssets[i] != NULL)
+ {
+ renderMeshAssets[i]->release();
+ }
+
+ renderMeshAssets[i] = NULL;
+ }
+ renderMeshAssets.clear();
+
+ for (size_t i = 0; i < physicalMeshes.size(); i++)
+ {
+ if (physicalMeshes[i] != NULL)
+ {
+ physicalMeshes[i]->release();
+ }
+ physicalMeshes[i] = NULL;
+ }
+ physicalMeshes.clear();
+
+ if (clothingAssetAuthoring != NULL)
+ {
+ clothingAssetAuthoring->release();
+ clothingAssetAuthoring = NULL;
+ }
+}
+
+
+void ClothingAuthoring::clearLoadedActorDescs()
+{
+ for (uint32_t i = 0; i < mLoadedActorDescs.size(); ++i)
+ {
+ mLoadedActorDescs[i]->destroy();
+ }
+ mLoadedActorDescs.clear();
+ mMaxTimesteps.clear();
+ mMaxIterations.clear();
+ mTimestepMethods.clear();
+ mDts.clear();
+ mGravities.clear();
+}
+
+} // namespace SharedTools
+
+#endif // PX_WINDOWS_FAMILY