aboutsummaryrefslogtreecommitdiff
path: root/APEX_1.4/shared/external/src
diff options
context:
space:
mode:
authorgit perforce import user <a@b>2016-10-25 12:29:14 -0600
committerSheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees>2016-10-25 18:56:37 -0500
commit3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch)
treefa6485c169e50d7415a651bf838f5bcd0fd3bfbd /APEX_1.4/shared/external/src
downloadphysx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz
physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip
Initial commit:
PhysX 3.4.0 Update @ 21294896 APEX 1.4.0 Update @ 21275617 [CL 21300167]
Diffstat (limited to 'APEX_1.4/shared/external/src')
-rw-r--r--APEX_1.4/shared/external/src/ApexMaterial.cpp642
-rw-r--r--APEX_1.4/shared/external/src/BestFit.cpp1063
-rw-r--r--APEX_1.4/shared/external/src/ClothingAuthoring.cpp5634
-rw-r--r--APEX_1.4/shared/external/src/FilterBits.cpp692
-rw-r--r--APEX_1.4/shared/external/src/Find.cpp85
-rw-r--r--APEX_1.4/shared/external/src/MaterialList.cpp436
-rw-r--r--APEX_1.4/shared/external/src/MemTracker.cpp1174
-rw-r--r--APEX_1.4/shared/external/src/MeshPainter.cpp1500
-rw-r--r--APEX_1.4/shared/external/src/MultiClientRenderResourceManager.cpp663
-rw-r--r--APEX_1.4/shared/external/src/RecordingRenderResourceManager.cpp952
-rw-r--r--APEX_1.4/shared/external/src/SampleApexRenderResources.cpp1865
-rw-r--r--APEX_1.4/shared/external/src/SampleApexRenderer.cpp329
-rw-r--r--APEX_1.4/shared/external/src/SampleApexResourceCallback.cpp757
-rw-r--r--APEX_1.4/shared/external/src/SkeletalAnim.cpp1335
-rw-r--r--APEX_1.4/shared/external/src/TextRenderResourceManager.cpp1132
-rw-r--r--APEX_1.4/shared/external/src/TriangleMesh.cpp5048
-rw-r--r--APEX_1.4/shared/external/src/UserAllocator.cpp239
-rw-r--r--APEX_1.4/shared/external/src/UserErrorCallback.cpp367
-rw-r--r--APEX_1.4/shared/external/src/htmltable.cpp3663
19 files changed, 27576 insertions, 0 deletions
diff --git a/APEX_1.4/shared/external/src/ApexMaterial.cpp b/APEX_1.4/shared/external/src/ApexMaterial.cpp
new file mode 100644
index 00000000..e05116fb
--- /dev/null
+++ b/APEX_1.4/shared/external/src/ApexMaterial.cpp
@@ -0,0 +1,642 @@
+/*
+ * 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.
+ */
+
+#define NOMINMAX
+#include <stdio.h>
+
+#include "RenderMesh.h"
+#include "PxVec3.h"
+#include "PxFileBuf.h"
+#include "PsAllocator.h"
+#include "ApexUsingNamespace.h"
+#include "ApexMaterial.h"
+
+// Local utilities
+
+PX_INLINE bool pathsMatch(const char* pathA, const char* pathB, int length)
+{
+ while (length-- > 0)
+ {
+ char a = *pathA++;
+ char b = *pathB++;
+ if (a == '\\')
+ {
+ a = '/';
+ }
+ if (b == '\\')
+ {
+ b = '/';
+ }
+ if (a != b)
+ {
+ return false;
+ }
+ if (a == '\0')
+ {
+ return true;
+ }
+ }
+
+ return true;
+}
+
+struct MaterialNameComponents
+{
+ const char* path;
+ int pathLen;
+ const char* filename;
+ int filenameLen;
+ const char* ext;
+ int extLen;
+ const char* name;
+ int nameLen;
+
+ bool operator <= (const MaterialNameComponents& c) const
+ {
+ if (pathLen > 0 && !pathsMatch(path, c.path, pathLen))
+ {
+ return false;
+ }
+ if (filenameLen > 0 && strncmp(filename, c.filename, (uint32_t)filenameLen))
+ {
+ return false;
+ }
+ if (extLen > 0 && strncmp(ext, c.ext, (uint32_t)extLen))
+ {
+ return false;
+ }
+ return 0 == strncmp(name, c.name, (uint32_t)nameLen);
+ }
+};
+
+PX_INLINE void decomposeMaterialName(MaterialNameComponents& components, const char* materialName)
+{
+ components.path = materialName;
+ components.pathLen = 0;
+ components.filename = materialName;
+ components.filenameLen = 0;
+ components.ext = materialName;
+ components.extLen = 0;
+ components.name = materialName;
+ components.nameLen = 0;
+
+ if (materialName == NULL)
+ {
+ return;
+ }
+
+ const int len = (int)strlen(materialName);
+
+ // Get name - will exclude any '#' deliniator
+ components.name += len;
+ while (components.name > materialName)
+ {
+ if (*(components.name - 1) == '#')
+ {
+ break;
+ }
+ --components.name;
+ }
+ components.nameLen = len - (int)(components.name - materialName);
+ if (components.name == materialName)
+ {
+ return;
+ }
+
+ // Get extension - will include '.'
+ components.ext = components.name;
+ while (components.ext > materialName)
+ {
+ if (*(--components.ext) == '.')
+ {
+ break;
+ }
+ }
+ if (components.ext != materialName)
+ {
+ components.extLen = (int)(components.name - components.ext) - 1;
+ }
+
+ // Get filename
+ components.filename = components.ext;
+ while (components.filename > materialName)
+ {
+ if (*(components.filename - 1) == '/' || *(components.filename - 1) == '\\')
+ {
+ break;
+ }
+ --components.filename;
+ }
+ if (components.filename != materialName)
+ {
+ components.filenameLen = (int)(components.ext - components.filename);
+ }
+
+ // Get path
+ components.path = materialName;
+ components.pathLen = (int)(components.filename - materialName);
+}
+
+PX_INLINE void copyToStringBufferSafe(char*& buffer, int& bufferSize, const char* src, int copySize)
+{
+ if (copySize >= bufferSize)
+ {
+ copySize = bufferSize - 1;
+ }
+ memcpy(buffer, src, (uint32_t)copySize);
+ buffer += copySize;
+ bufferSize -= copySize;
+}
+
+struct ApexDefaultMaterialLibraryVersion
+{
+ enum
+ {
+ Initial = 0,
+ AddedBumpMapType,
+ AddedMaterialNamingConvention,
+ RemovedMaterialNamingConvention,
+
+ Count,
+ Current = Count - 1
+ };
+};
+
+
+PX_INLINE void serialize_string(physx::PxFileBuf& stream, const std::string& string)
+{
+ const uint32_t length = (uint32_t)string.length();
+ stream.storeDword(length);
+ stream.write(string.c_str(), length);
+}
+
+PX_INLINE void deserialize_string(physx::PxFileBuf& stream, std::string& string)
+{
+ const uint32_t length = stream.readDword();
+ char* cstr = (char*)PxAlloca(length + 1);
+ stream.read(cstr, length);
+ cstr[length] = '\0';
+ string = cstr;
+}
+
+
+// ApexDefaultTextureMap functions
+
+ApexDefaultTextureMap::ApexDefaultTextureMap() :
+ mPixelFormat(PIXEL_FORMAT_UNKNOWN),
+ mWidth(0),
+ mHeight(0),
+ mComponentCount(0),
+ mPixelBufferSize(0),
+ mPixelBuffer(NULL)
+{
+}
+
+ApexDefaultTextureMap& ApexDefaultTextureMap::operator = (const ApexDefaultTextureMap& textureMap)
+{
+ mPixelFormat = textureMap.mPixelFormat;
+ mWidth = textureMap.mWidth;
+ mHeight = textureMap.mHeight;
+ mComponentCount = textureMap.mComponentCount;
+ mPixelBufferSize = textureMap.mPixelBufferSize;
+ mPixelBuffer = new uint8_t[mPixelBufferSize];
+ memcpy(mPixelBuffer, textureMap.mPixelBuffer, mPixelBufferSize);
+ return *this;
+}
+
+ApexDefaultTextureMap::~ApexDefaultTextureMap()
+{
+ unload();
+}
+
+void
+ApexDefaultTextureMap::build(PixelFormat format, uint32_t width, uint32_t height, uint32_t* fillColor)
+{
+ uint8_t fillBuffer[4];
+ int componentCount;
+
+ switch (format)
+ {
+ case PIXEL_FORMAT_RGB:
+ componentCount = 3;
+ if (fillColor != NULL)
+ {
+ fillBuffer[0] = (*fillColor >> 16) & 0xFF;
+ fillBuffer[1] = (*fillColor >> 8) & 0xFF;
+ fillBuffer[2] = (*fillColor) & 0xFF;
+ }
+ break;
+ case PIXEL_FORMAT_BGR_EXT:
+ componentCount = 3;
+ if (fillColor != NULL)
+ {
+ fillBuffer[0] = (*fillColor) & 0xFF;
+ fillBuffer[1] = (*fillColor >> 8) & 0xFF;
+ fillBuffer[2] = (*fillColor >> 16) & 0xFF;
+ }
+ break;
+ case PIXEL_FORMAT_BGRA_EXT:
+ componentCount = 4;
+ if (fillColor != NULL)
+ {
+ fillBuffer[0] = (*fillColor) & 0xFF;
+ fillBuffer[1] = (*fillColor >> 8) & 0xFF;
+ fillBuffer[2] = (*fillColor >> 16) & 0xFF;
+ fillBuffer[3] = (*fillColor >> 24) & 0xFF;
+ }
+ break;
+ default:
+ return; // Not supported
+ }
+
+ unload();
+
+ mPixelBufferSize = componentCount * width * height;
+ mPixelBuffer = new uint8_t[mPixelBufferSize];
+ mPixelFormat = format;
+ mComponentCount = (uint32_t)componentCount;
+ mWidth = width;
+ mHeight = height;
+
+ if (fillColor != NULL)
+ {
+ uint8_t* write = mPixelBuffer;
+ uint8_t* writeStop = mPixelBuffer + mPixelBufferSize;
+ uint8_t* read = fillBuffer;
+ uint8_t* readStop = fillBuffer + componentCount;
+ while (write < writeStop)
+ {
+ *write++ = *read++;
+ if (read == readStop)
+ {
+ read = fillBuffer;
+ }
+ }
+ }
+}
+
+void ApexDefaultTextureMap::unload()
+{
+ mPixelFormat = PIXEL_FORMAT_UNKNOWN;
+ mWidth = 0;
+ mHeight = 0;
+ mComponentCount = 0;
+ mPixelBufferSize = 0;
+ delete [] mPixelBuffer;
+ mPixelBuffer = NULL;
+}
+
+void ApexDefaultTextureMap::serialize(physx::PxFileBuf& stream) const
+{
+ stream.storeDword((uint32_t)mPixelFormat);
+ stream.storeDword(mWidth);
+ stream.storeDword(mHeight);
+ stream.storeDword(mComponentCount);
+ stream.storeDword(mPixelBufferSize);
+ if (mPixelBufferSize != 0)
+ {
+ stream.write(mPixelBuffer, mPixelBufferSize);
+ }
+}
+
+void ApexDefaultTextureMap::deserialize(physx::PxFileBuf& stream, uint32_t /*version*/)
+{
+ unload();
+ mPixelFormat = (PixelFormat)stream.readDword();
+ mWidth = stream.readDword();
+ mHeight = stream.readDword();
+ mComponentCount = stream.readDword();
+ mPixelBufferSize = stream.readDword();
+ if (mPixelBufferSize != 0)
+ {
+ mPixelBuffer = new uint8_t[mPixelBufferSize];
+ stream.read(mPixelBuffer, mPixelBufferSize);
+ }
+}
+
+
+// ApexDefaultMaterial functions
+ApexDefaultMaterial::ApexDefaultMaterial() :
+ mAmbient(0.0f),
+ mDiffuse(0.0f),
+ mSpecular(0.0f),
+ mAlpha(0.0f),
+ mShininess(0.0f)
+{
+ for (uint32_t i = 0; i < TEXTURE_MAP_TYPE_COUNT; ++i)
+ {
+ mTextureMaps[i] = NULL;
+ }
+}
+
+ApexDefaultMaterial& ApexDefaultMaterial::operator = (const ApexDefaultMaterial& material)
+{
+ mName = material.mName;
+
+ for (uint32_t i = 0; i < TEXTURE_MAP_TYPE_COUNT; ++i)
+ {
+ if (material.mTextureMaps[i])
+ {
+ mTextureMaps[i] = new ApexDefaultTextureMap(*material.mTextureMaps[i]);
+ }
+ else
+ {
+ mTextureMaps[i] = NULL;
+ }
+ }
+
+ mAmbient = material.mAmbient;
+ mDiffuse = material.mDiffuse;
+ mSpecular = material.mSpecular;
+ mAlpha = material.mAlpha;
+ mShininess = material.mShininess;
+
+ return *this;
+}
+
+ApexDefaultMaterial::~ApexDefaultMaterial()
+{
+ unload();
+}
+
+void ApexDefaultMaterial::setName(const char* name)
+{
+ mName = name;
+}
+
+bool ApexDefaultMaterial::setTextureMap(TextureMapType type, ApexDefaultTextureMap* textureMap)
+{
+ if (type < 0 || type >= TEXTURE_MAP_TYPE_COUNT)
+ {
+ return false;
+ }
+
+ delete mTextureMaps[type];
+
+ mTextureMaps[type] = textureMap;
+
+ return true;
+}
+
+void ApexDefaultMaterial::unload()
+{
+ mName.clear();
+
+ for (uint32_t i = 0; i < TEXTURE_MAP_TYPE_COUNT; ++i)
+ {
+ delete mTextureMaps[i];
+ mTextureMaps[i] = NULL;
+ }
+
+ mAmbient = physx::PxVec3(0.0f);
+ mDiffuse = physx::PxVec3(0.0f);
+ mSpecular = physx::PxVec3(0.0f);
+ mAlpha = 0.0f;
+ mShininess = 0.0f;
+}
+
+
+void ApexDefaultMaterial::serialize(physx::PxFileBuf& stream) const
+{
+ serialize_string(stream, mName);
+
+ for (uint32_t i = 0; i < TEXTURE_MAP_TYPE_COUNT; ++i)
+ {
+ if (mTextureMaps[i] == NULL)
+ {
+ stream.storeDword((uint32_t)0);
+ }
+ else
+ {
+ stream.storeDword((uint32_t)1);
+ mTextureMaps[i]->serialize(stream);
+ }
+ }
+
+ stream.storeFloat(mAmbient.x);
+ stream.storeFloat(mAmbient.y);
+ stream.storeFloat(mAmbient.z);
+ stream.storeFloat(mDiffuse.x);
+ stream.storeFloat(mDiffuse.y);
+ stream.storeFloat(mDiffuse.z);
+ stream.storeFloat(mSpecular.x);
+ stream.storeFloat(mSpecular.y);
+ stream.storeFloat(mSpecular.z);
+ stream.storeFloat(mAlpha);
+ stream.storeFloat(mShininess);
+}
+
+void ApexDefaultMaterial::deserialize(physx::PxFileBuf& stream, uint32_t version)
+{
+ unload();
+
+ deserialize_string(stream, mName);
+
+ if (version < ApexDefaultMaterialLibraryVersion::AddedBumpMapType)
+ {
+ for (uint32_t i = 0; i < 2; ++i)
+ {
+ const uint32_t pointerIsValid = stream.readDword();
+ if (pointerIsValid)
+ {
+ mTextureMaps[i] = new ApexDefaultTextureMap();
+ mTextureMaps[i]->deserialize(stream, version);
+ }
+ }
+ mTextureMaps[2] = mTextureMaps[1];
+ mTextureMaps[1] = NULL;
+ }
+ else
+ {
+ for (uint32_t i = 0; i < TEXTURE_MAP_TYPE_COUNT; ++i)
+ {
+ const uint32_t pointerIsValid = stream.readDword();
+ if (pointerIsValid)
+ {
+ mTextureMaps[i] = new ApexDefaultTextureMap();
+ mTextureMaps[i]->deserialize(stream, version);
+ }
+ }
+ }
+
+ mAmbient.x = stream.readFloat();
+ mAmbient.y = stream.readFloat();
+ mAmbient.z = stream.readFloat();
+ mDiffuse.x = stream.readFloat();
+ mDiffuse.y = stream.readFloat();
+ mDiffuse.z = stream.readFloat();
+ mSpecular.x = stream.readFloat();
+ mSpecular.y = stream.readFloat();
+ mSpecular.z = stream.readFloat();
+ mAlpha = stream.readFloat();
+ mShininess = stream.readFloat();
+}
+
+TextureMap* ApexDefaultMaterial::getTextureMap(TextureMapType type) const
+{
+ if (type < 0 || type >= TEXTURE_MAP_TYPE_COUNT)
+ {
+ return NULL;
+ }
+
+ return mTextureMaps[type];
+}
+
+
+// ApexDefaultMaterialLibrary functions
+ApexDefaultMaterialLibrary::ApexDefaultMaterialLibrary()
+{
+}
+
+ApexDefaultMaterialLibrary& ApexDefaultMaterialLibrary::operator = (const ApexDefaultMaterialLibrary& materialLibrary)
+{
+ mMaterials.resize(materialLibrary.getMaterialCount());
+ for (uint32_t i = 0; i < materialLibrary.getMaterialCount(); ++i)
+ {
+ ApexDefaultMaterial* material = materialLibrary.getMaterial(i);
+ PX_ASSERT(material != NULL);
+ mMaterials[i] = new ApexDefaultMaterial(*material);
+ }
+
+ return *this;
+}
+
+ApexDefaultMaterialLibrary::~ApexDefaultMaterialLibrary()
+{
+ unload();
+}
+
+void ApexDefaultMaterialLibrary::unload()
+{
+ const uint32_t size = (uint32_t)mMaterials.size();
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ delete mMaterials[i];
+ mMaterials[i] = NULL;
+ }
+ mMaterials.resize(0);
+}
+
+ApexDefaultMaterial* ApexDefaultMaterialLibrary::getMaterial(uint32_t materialIndex) const
+{
+ if (materialIndex >= mMaterials.size())
+ {
+ return NULL;
+ }
+
+ return mMaterials[materialIndex];
+}
+
+void ApexDefaultMaterialLibrary::merge(const ApexDefaultMaterialLibrary& materialLibrary)
+{
+ for (uint32_t i = 0; i < materialLibrary.getMaterialCount(); ++i)
+ {
+ ApexDefaultMaterial* material = materialLibrary.getMaterial(i);
+ PX_ASSERT(material != NULL);
+ const uint32_t size = (uint32_t)mMaterials.size();
+ uint32_t j = 0;
+ for (; j < size; ++j)
+ {
+ if (!strcmp(mMaterials[j]->getName(), material->getName()))
+ {
+ break;
+ }
+ }
+ if (j == size)
+ {
+ ApexDefaultMaterial* newMaterial = new ApexDefaultMaterial(*material);
+ mMaterials.push_back(newMaterial);
+ }
+ }
+}
+
+void ApexDefaultMaterialLibrary::serialize(physx::PxFileBuf& stream) const
+{
+ stream.storeDword((uint32_t)ApexDefaultMaterialLibraryVersion::Current);
+
+ const uint32_t size = (uint32_t)mMaterials.size();
+ stream.storeDword(size);
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ mMaterials[i]->serialize(stream);
+ }
+}
+
+void ApexDefaultMaterialLibrary::deserialize(physx::PxFileBuf& stream)
+{
+ unload();
+
+ uint32_t version = stream.readDword();
+
+ uint32_t size = stream.readDword();
+ mMaterials.resize(size);
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ mMaterials[i] = new ApexDefaultMaterial();
+ mMaterials[i]->deserialize(stream, version);
+ }
+
+ if (version >= ApexDefaultMaterialLibraryVersion::AddedMaterialNamingConvention && version < ApexDefaultMaterialLibraryVersion::RemovedMaterialNamingConvention)
+ {
+ stream.readDword(); // Eat naming convention
+ }
+}
+
+Material* ApexDefaultMaterialLibrary::getMaterial(const char* materialName, bool& created)
+{
+ int32_t index = findMaterialIndex(materialName);
+ if (index >= 0)
+ {
+ created = false;
+ return mMaterials[(uint32_t)index];
+ }
+
+ ApexDefaultMaterial* newMaterial = new ApexDefaultMaterial();
+ newMaterial->setName(materialName);
+ mMaterials.push_back(newMaterial);
+ created = true;
+ return newMaterial;
+}
+
+bool ApexDefaultMaterialLibrary::deleteMaterial(const char* materialName)
+{
+ int32_t index = findMaterialIndex(materialName);
+ if (index < 0)
+ {
+ return false;
+ }
+
+ ApexDefaultMaterial* material = mMaterials[(uint32_t)index];
+ delete material;
+
+ mMaterials[(uint32_t)index] = mMaterials[mMaterials.size() - 1];
+ mMaterials.resize(mMaterials.size() - 1);
+
+ return true;
+}
+
+int32_t ApexDefaultMaterialLibrary::findMaterialIndex(const char* materialName)
+{
+ const char blank[] = "";
+ materialName = materialName ? materialName : blank;
+
+ const uint32_t size = (uint32_t)mMaterials.size();
+ int32_t index = 0;
+ for (; index < (int32_t)size; ++index)
+ {
+ const char* existingMaterialName = mMaterials[(uint32_t)index]->getName() ? mMaterials[(uint32_t)index]->getName() : blank;
+ if (!strcmp(existingMaterialName, materialName))
+ {
+ return index;
+ }
+ }
+
+ return -1;
+}
diff --git a/APEX_1.4/shared/external/src/BestFit.cpp b/APEX_1.4/shared/external/src/BestFit.cpp
new file mode 100644
index 00000000..5aefcb41
--- /dev/null
+++ b/APEX_1.4/shared/external/src/BestFit.cpp
@@ -0,0 +1,1063 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <math.h>
+#include <float.h>
+
+#include "BestFit.h"
+
+#define REAL float
+
+namespace SharedTools
+{
+
+const float FM_PI = 3.1415926535897932384626433832795028841971693993751f;
+const float FM_DEG_TO_RAD = ((2.0f * FM_PI) / 360.0f);
+//const float FM_RAD_TO_DEG = (360.0f / (2.0f * FM_PI));
+
+void fm_identity(REAL matrix[16]) // set 4x4 matrix to identity.
+{
+ matrix[0 * 4 + 0] = 1;
+ matrix[1 * 4 + 1] = 1;
+ matrix[2 * 4 + 2] = 1;
+ matrix[3 * 4 + 3] = 1;
+
+ matrix[1 * 4 + 0] = 0;
+ matrix[2 * 4 + 0] = 0;
+ matrix[3 * 4 + 0] = 0;
+
+ matrix[0 * 4 + 1] = 0;
+ matrix[2 * 4 + 1] = 0;
+ matrix[3 * 4 + 1] = 0;
+
+ matrix[0 * 4 + 2] = 0;
+ matrix[1 * 4 + 2] = 0;
+ matrix[3 * 4 + 2] = 0;
+
+ matrix[0 * 4 + 3] = 0;
+ matrix[1 * 4 + 3] = 0;
+ matrix[2 * 4 + 3] = 0;
+
+}
+
+
+
+void fm_matrixMultiply(const REAL* pA, const REAL* pB, REAL* pM)
+{
+ REAL a = pA[0 * 4 + 0] * pB[0 * 4 + 0] + pA[0 * 4 + 1] * pB[1 * 4 + 0] + pA[0 * 4 + 2] * pB[2 * 4 + 0] + pA[0 * 4 + 3] * pB[3 * 4 + 0];
+ REAL b = pA[0 * 4 + 0] * pB[0 * 4 + 1] + pA[0 * 4 + 1] * pB[1 * 4 + 1] + pA[0 * 4 + 2] * pB[2 * 4 + 1] + pA[0 * 4 + 3] * pB[3 * 4 + 1];
+ REAL c = pA[0 * 4 + 0] * pB[0 * 4 + 2] + pA[0 * 4 + 1] * pB[1 * 4 + 2] + pA[0 * 4 + 2] * pB[2 * 4 + 2] + pA[0 * 4 + 3] * pB[3 * 4 + 2];
+ REAL d = pA[0 * 4 + 0] * pB[0 * 4 + 3] + pA[0 * 4 + 1] * pB[1 * 4 + 3] + pA[0 * 4 + 2] * pB[2 * 4 + 3] + pA[0 * 4 + 3] * pB[3 * 4 + 3];
+
+ REAL e = pA[1 * 4 + 0] * pB[0 * 4 + 0] + pA[1 * 4 + 1] * pB[1 * 4 + 0] + pA[1 * 4 + 2] * pB[2 * 4 + 0] + pA[1 * 4 + 3] * pB[3 * 4 + 0];
+ REAL f = pA[1 * 4 + 0] * pB[0 * 4 + 1] + pA[1 * 4 + 1] * pB[1 * 4 + 1] + pA[1 * 4 + 2] * pB[2 * 4 + 1] + pA[1 * 4 + 3] * pB[3 * 4 + 1];
+ REAL g = pA[1 * 4 + 0] * pB[0 * 4 + 2] + pA[1 * 4 + 1] * pB[1 * 4 + 2] + pA[1 * 4 + 2] * pB[2 * 4 + 2] + pA[1 * 4 + 3] * pB[3 * 4 + 2];
+ REAL h = pA[1 * 4 + 0] * pB[0 * 4 + 3] + pA[1 * 4 + 1] * pB[1 * 4 + 3] + pA[1 * 4 + 2] * pB[2 * 4 + 3] + pA[1 * 4 + 3] * pB[3 * 4 + 3];
+
+ REAL i = pA[2 * 4 + 0] * pB[0 * 4 + 0] + pA[2 * 4 + 1] * pB[1 * 4 + 0] + pA[2 * 4 + 2] * pB[2 * 4 + 0] + pA[2 * 4 + 3] * pB[3 * 4 + 0];
+ REAL j = pA[2 * 4 + 0] * pB[0 * 4 + 1] + pA[2 * 4 + 1] * pB[1 * 4 + 1] + pA[2 * 4 + 2] * pB[2 * 4 + 1] + pA[2 * 4 + 3] * pB[3 * 4 + 1];
+ REAL k = pA[2 * 4 + 0] * pB[0 * 4 + 2] + pA[2 * 4 + 1] * pB[1 * 4 + 2] + pA[2 * 4 + 2] * pB[2 * 4 + 2] + pA[2 * 4 + 3] * pB[3 * 4 + 2];
+ REAL l = pA[2 * 4 + 0] * pB[0 * 4 + 3] + pA[2 * 4 + 1] * pB[1 * 4 + 3] + pA[2 * 4 + 2] * pB[2 * 4 + 3] + pA[2 * 4 + 3] * pB[3 * 4 + 3];
+
+ REAL m = pA[3 * 4 + 0] * pB[0 * 4 + 0] + pA[3 * 4 + 1] * pB[1 * 4 + 0] + pA[3 * 4 + 2] * pB[2 * 4 + 0] + pA[3 * 4 + 3] * pB[3 * 4 + 0];
+ REAL n = pA[3 * 4 + 0] * pB[0 * 4 + 1] + pA[3 * 4 + 1] * pB[1 * 4 + 1] + pA[3 * 4 + 2] * pB[2 * 4 + 1] + pA[3 * 4 + 3] * pB[3 * 4 + 1];
+ REAL o = pA[3 * 4 + 0] * pB[0 * 4 + 2] + pA[3 * 4 + 1] * pB[1 * 4 + 2] + pA[3 * 4 + 2] * pB[2 * 4 + 2] + pA[3 * 4 + 3] * pB[3 * 4 + 2];
+ REAL p = pA[3 * 4 + 0] * pB[0 * 4 + 3] + pA[3 * 4 + 1] * pB[1 * 4 + 3] + pA[3 * 4 + 2] * pB[2 * 4 + 3] + pA[3 * 4 + 3] * pB[3 * 4 + 3];
+
+ pM[0] = a;
+ pM[1] = b;
+ pM[2] = c;
+ pM[3] = d;
+
+ pM[4] = e;
+ pM[5] = f;
+ pM[6] = g;
+ pM[7] = h;
+
+ pM[8] = i;
+ pM[9] = j;
+ pM[10] = k;
+ pM[11] = l;
+
+ pM[12] = m;
+ pM[13] = n;
+ pM[14] = o;
+ pM[15] = p;
+
+}
+
+void fm_matrixToQuat(const REAL* matrix, REAL* quat) // convert the 3x3 portion of a 4x4 matrix into a quaterion as x,y,z,w
+{
+ REAL tr = matrix[0 * 4 + 0] + matrix[1 * 4 + 1] + matrix[2 * 4 + 2];
+
+ // check the diagonal
+
+ if (tr > 0.0f)
+ {
+ REAL s = (REAL) sqrt((double)(tr + 1.0f));
+ quat[3] = s * 0.5f;
+ s = 0.5f / s;
+ quat[0] = (matrix[1 * 4 + 2] - matrix[2 * 4 + 1]) * s;
+ quat[1] = (matrix[2 * 4 + 0] - matrix[0 * 4 + 2]) * s;
+ quat[2] = (matrix[0 * 4 + 1] - matrix[1 * 4 + 0]) * s;
+
+ }
+ else
+ {
+ // diagonal is negative
+ int nxt[3] = {1, 2, 0};
+ REAL qa[4];
+
+ int i = 0;
+
+ if (matrix[1 * 4 + 1] > matrix[0 * 4 + 0])
+ {
+ i = 1;
+ }
+ if (matrix[2 * 4 + 2] > matrix[i * 4 + i])
+ {
+ i = 2;
+ }
+
+ int j = nxt[i];
+ int k = nxt[j];
+
+ REAL s = static_cast<REAL>(sqrt(((matrix[i * 4 + i] - (matrix[j * 4 + j] + matrix[k * 4 + k])) + 1.0f)));
+
+ qa[i] = s * 0.5f;
+
+ if (s != 0.0f)
+ {
+ s = 0.5f / s;
+ }
+
+ qa[3] = (matrix[j * 4 + k] - matrix[k * 4 + j]) * s;
+ qa[j] = (matrix[i * 4 + j] + matrix[j * 4 + i]) * s;
+ qa[k] = (matrix[i * 4 + k] + matrix[k * 4 + i]) * s;
+
+ quat[0] = qa[0];
+ quat[1] = qa[1];
+ quat[2] = qa[2];
+ quat[3] = qa[3];
+ }
+}
+
+
+void fm_getTranslation(const REAL* matrix, REAL* t)
+{
+ t[0] = matrix[3 * 4 + 0];
+ t[1] = matrix[3 * 4 + 1];
+ t[2] = matrix[3 * 4 + 2];
+}
+
+
+void fm_rotate(const REAL matrix[16], const REAL v[3], REAL t[3]) // rotate and translate this point
+{
+ if (matrix)
+ {
+ REAL tx = (matrix[0 * 4 + 0] * v[0]) + (matrix[1 * 4 + 0] * v[1]) + (matrix[2 * 4 + 0] * v[2]);
+ REAL ty = (matrix[0 * 4 + 1] * v[0]) + (matrix[1 * 4 + 1] * v[1]) + (matrix[2 * 4 + 1] * v[2]);
+ REAL tz = (matrix[0 * 4 + 2] * v[0]) + (matrix[1 * 4 + 2] * v[1]) + (matrix[2 * 4 + 2] * v[2]);
+ t[0] = tx;
+ t[1] = ty;
+ t[2] = tz;
+ }
+ else
+ {
+ t[0] = v[0];
+ t[1] = v[1];
+ t[2] = v[2];
+ }
+}
+
+void fm_inverseRT(const REAL matrix[16], const REAL pos[3], REAL t[3]) // inverse rotate translate the point.
+{
+
+ REAL _x = pos[0] - matrix[3 * 4 + 0];
+ REAL _y = pos[1] - matrix[3 * 4 + 1];
+ REAL _z = pos[2] - matrix[3 * 4 + 2];
+
+ // Multiply inverse-translated source vector by inverted rotation transform
+
+ t[0] = (matrix[0 * 4 + 0] * _x) + (matrix[0 * 4 + 1] * _y) + (matrix[0 * 4 + 2] * _z);
+ t[1] = (matrix[1 * 4 + 0] * _x) + (matrix[1 * 4 + 1] * _y) + (matrix[1 * 4 + 2] * _z);
+ t[2] = (matrix[2 * 4 + 0] * _x) + (matrix[2 * 4 + 1] * _y) + (matrix[2 * 4 + 2] * _z);
+
+}
+
+void fm_setTranslation(const REAL* translation, REAL* matrix)
+{
+ matrix[12] = translation[0];
+ matrix[13] = translation[1];
+ matrix[14] = translation[2];
+}
+
+void fm_transform(const REAL matrix[16], const REAL v[3], REAL t[3]) // rotate and translate this point
+{
+ if (matrix)
+ {
+ REAL tx = (matrix[0 * 4 + 0] * v[0]) + (matrix[1 * 4 + 0] * v[1]) + (matrix[2 * 4 + 0] * v[2]) + matrix[3 * 4 + 0];
+ REAL ty = (matrix[0 * 4 + 1] * v[0]) + (matrix[1 * 4 + 1] * v[1]) + (matrix[2 * 4 + 1] * v[2]) + matrix[3 * 4 + 1];
+ REAL tz = (matrix[0 * 4 + 2] * v[0]) + (matrix[1 * 4 + 2] * v[1]) + (matrix[2 * 4 + 2] * v[2]) + matrix[3 * 4 + 2];
+ t[0] = tx;
+ t[1] = ty;
+ t[2] = tz;
+ }
+ else
+ {
+ t[0] = v[0];
+ t[1] = v[1];
+ t[2] = v[2];
+ }
+}
+
+void fm_quatToMatrix(const REAL* quat, REAL* matrix) // convert quaterinion rotation to matrix, zeros out the translation component.
+{
+
+ REAL xx = quat[0] * quat[0];
+ REAL yy = quat[1] * quat[1];
+ REAL zz = quat[2] * quat[2];
+ REAL xy = quat[0] * quat[1];
+ REAL xz = quat[0] * quat[2];
+ REAL yz = quat[1] * quat[2];
+ REAL wx = quat[3] * quat[0];
+ REAL wy = quat[3] * quat[1];
+ REAL wz = quat[3] * quat[2];
+
+ matrix[0 * 4 + 0] = 1 - 2 * (yy + zz);
+ matrix[1 * 4 + 0] = 2 * (xy - wz);
+ matrix[2 * 4 + 0] = 2 * (xz + wy);
+
+ matrix[0 * 4 + 1] = 2 * (xy + wz);
+ matrix[1 * 4 + 1] = 1 - 2 * (xx + zz);
+ matrix[2 * 4 + 1] = 2 * (yz - wx);
+
+ matrix[0 * 4 + 2] = 2 * (xz - wy);
+ matrix[1 * 4 + 2] = 2 * (yz + wx);
+ matrix[2 * 4 + 2] = 1 - 2 * (xx + yy);
+
+ matrix[3 * 4 + 0] = matrix[3 * 4 + 1] = matrix[3 * 4 + 2] = (REAL) 0.0f;
+ matrix[0 * 4 + 3] = matrix[1 * 4 + 3] = matrix[2 * 4 + 3] = (REAL) 0.0f;
+ matrix[3 * 4 + 3] = (REAL) 1.0f;
+
+}
+
+void fm_cross(REAL* cross, const REAL* a, const REAL* b)
+{
+ cross[0] = a[1] * b[2] - a[2] * b[1];
+ cross[1] = a[2] * b[0] - a[0] * b[2];
+ cross[2] = a[0] * b[1] - a[1] * b[0];
+}
+
+
+REAL fm_dot(const REAL* p1, const REAL* p2)
+{
+ return p1[0] * p2[0] + p1[1] * p2[1] + p1[2] * p2[2];
+}
+
+// Reference, from Stan Melax in Game Gems I
+// Quaternion q;
+// vector3 c = CrossProduct(v0,v1);
+// REAL d = DotProduct(v0,v1);
+// REAL s = (REAL)sqrt((1+d)*2);
+// q.x = c.x / s;
+// q.y = c.y / s;
+// q.z = c.z / s;
+// q.w = s /2.0f;
+// return q;
+void fm_rotationArc(const REAL* v0, const REAL* v1, REAL* quat)
+{
+ REAL cross[3];
+
+ fm_cross(cross, v0, v1);
+ REAL d = fm_dot(v0, v1);
+ REAL s = static_cast<REAL>(sqrt((1 + d) * 2));
+ REAL recip = 1.0f / s;
+
+ quat[0] = cross[0] * recip;
+ quat[1] = cross[1] * recip;
+ quat[2] = cross[2] * recip;
+ quat[3] = s * 0.5f;
+
+}
+
+void fm_planeToMatrix(const REAL* plane, REAL* matrix) // convert a plane equation to a 4x4 rotation matrix
+{
+ REAL ref[3] = { 0, 1, 0 };
+ REAL quat[4];
+ fm_rotationArc(ref, plane, quat);
+ fm_quatToMatrix(quat, matrix);
+ REAL origin[3] = { 0, -plane[3], 0 };
+ REAL center[3];
+ fm_transform(matrix, origin, center);
+ fm_setTranslation(center, matrix);
+}
+
+void fm_planeToQuat(const REAL* plane, REAL* quat, REAL* pos) // convert a plane equation to a quaternion and translation
+{
+ REAL ref[3] = { 0, 1, 0 };
+ REAL matrix[16];
+ fm_rotationArc(ref, plane, quat);
+ fm_quatToMatrix(quat, matrix);
+ REAL origin[3] = { 0, plane[3], 0 };
+ fm_transform(matrix, origin, pos);
+}
+
+void fm_eulerToQuat(REAL roll, REAL pitch, REAL yaw, REAL* quat) // convert euler angles to quaternion.
+{
+ roll *= 0.5f;
+ pitch *= 0.5f;
+ yaw *= 0.5f;
+
+ REAL cr = static_cast<REAL>(cos(roll));
+ REAL cp = static_cast<REAL>(cos(pitch));
+ REAL cy = static_cast<REAL>(cos(yaw));
+
+ REAL sr = static_cast<REAL>(sin(roll));
+ REAL sp = static_cast<REAL>(sin(pitch));
+ REAL sy = static_cast<REAL>(sin(yaw));
+
+ REAL cpcy = cp * cy;
+ REAL spsy = sp * sy;
+ REAL spcy = sp * cy;
+ REAL cpsy = cp * sy;
+
+ quat[0] = (sr * cpcy - cr * spsy);
+ quat[1] = (cr * spcy + sr * cpsy);
+ quat[2] = (cr * cpsy - sr * spcy);
+ quat[3] = cr * cpcy + sr * spsy;
+}
+
+void fm_eulerToQuat(const REAL* euler, REAL* quat) // convert euler angles to quaternion.
+{
+ fm_eulerToQuat(euler[0], euler[1], euler[2], quat);
+}
+
+
+void fm_eulerMatrix(REAL ax, REAL ay, REAL az, REAL* matrix) // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
+{
+ REAL quat[4];
+ fm_eulerToQuat(ax, ay, az, quat);
+ fm_quatToMatrix(quat, matrix);
+}
+
+template <class Type> class Eigen
+{
+public:
+
+
+ void DecrSortEigenStuff(void)
+ {
+ Tridiagonal(); //diagonalize the matrix.
+ QLAlgorithm(); //
+ DecreasingSort();
+ GuaranteeRotation();
+ }
+
+ void Tridiagonal(void)
+ {
+ Type fM00 = mElement[0][0];
+ Type fM01 = mElement[0][1];
+ Type fM02 = mElement[0][2];
+ Type fM11 = mElement[1][1];
+ Type fM12 = mElement[1][2];
+ Type fM22 = mElement[2][2];
+
+ m_afDiag[0] = fM00;
+ m_afSubd[2] = 0;
+ if (fM02 != (Type)0.0)
+ {
+ Type fLength = static_cast<Type>(sqrt(fM01 * fM01 + fM02 * fM02));
+ Type fInvLength = ((Type)1.0) / fLength;
+ fM01 *= fInvLength;
+ fM02 *= fInvLength;
+ Type fQ = ((Type)2.0) * fM01 * fM12 + fM02 * (fM22 - fM11);
+ m_afDiag[1] = fM11 + fM02 * fQ;
+ m_afDiag[2] = fM22 - fM02 * fQ;
+ m_afSubd[0] = fLength;
+ m_afSubd[1] = fM12 - fM01 * fQ;
+ mElement[0][0] = (Type)1.0;
+ mElement[0][1] = (Type)0.0;
+ mElement[0][2] = (Type)0.0;
+ mElement[1][0] = (Type)0.0;
+ mElement[1][1] = fM01;
+ mElement[1][2] = fM02;
+ mElement[2][0] = (Type)0.0;
+ mElement[2][1] = fM02;
+ mElement[2][2] = -fM01;
+ m_bIsRotation = false;
+ }
+ else
+ {
+ m_afDiag[1] = fM11;
+ m_afDiag[2] = fM22;
+ m_afSubd[0] = fM01;
+ m_afSubd[1] = fM12;
+ mElement[0][0] = (Type)1.0;
+ mElement[0][1] = (Type)0.0;
+ mElement[0][2] = (Type)0.0;
+ mElement[1][0] = (Type)0.0;
+ mElement[1][1] = (Type)1.0;
+ mElement[1][2] = (Type)0.0;
+ mElement[2][0] = (Type)0.0;
+ mElement[2][1] = (Type)0.0;
+ mElement[2][2] = (Type)1.0;
+ m_bIsRotation = true;
+ }
+ }
+
+ bool QLAlgorithm(void)
+ {
+ const int iMaxIter = 32;
+
+ for (int i0 = 0; i0 < 3; i0++)
+ {
+ int i1;
+ for (i1 = 0; i1 < iMaxIter; i1++)
+ {
+ int i2;
+ for (i2 = i0; i2 <= (3 - 2); i2++)
+ {
+ Type fTmp = static_cast<Type>(fabs(m_afDiag[i2]) + fabs(m_afDiag[i2 + 1]));
+ if (fabs(m_afSubd[i2]) + fTmp == fTmp)
+ {
+ break;
+ }
+ }
+ if (i2 == i0)
+ {
+ break;
+ }
+
+ Type fG = (m_afDiag[i0 + 1] - m_afDiag[i0]) / (((Type)2.0) * m_afSubd[i0]);
+ Type fR = static_cast<Type>(sqrt(fG * fG + (Type)1.0));
+ if (fG < (Type)0.0)
+ {
+ fG = m_afDiag[i2] - m_afDiag[i0] + m_afSubd[i0] / (fG - fR);
+ }
+ else
+ {
+ fG = m_afDiag[i2] - m_afDiag[i0] + m_afSubd[i0] / (fG + fR);
+ }
+ Type fSin = (Type)1.0, fCos = (Type)1.0, fP = (Type)0.0;
+ for (int i3 = i2 - 1; i3 >= i0; i3--)
+ {
+ Type fF = fSin * m_afSubd[i3];
+ Type fB = fCos * m_afSubd[i3];
+ if (fabs(fF) >= fabs(fG))
+ {
+ fCos = fG / fF;
+ fR = static_cast<Type>(sqrt(fCos * fCos + (Type)1.0));
+ m_afSubd[i3 + 1] = fF * fR;
+ fSin = ((Type)1.0) / fR;
+ fCos *= fSin;
+ }
+ else
+ {
+ fSin = fF / fG;
+ fR = static_cast<Type>(sqrt(fSin * fSin + (Type)1.0));
+ m_afSubd[i3 + 1] = fG * fR;
+ fCos = ((Type)1.0) / fR;
+ fSin *= fCos;
+ }
+ fG = m_afDiag[i3 + 1] - fP;
+ fR = (m_afDiag[i3] - fG) * fSin + ((Type)2.0) * fB * fCos;
+ fP = fSin * fR;
+ m_afDiag[i3 + 1] = fG + fP;
+ fG = fCos * fR - fB;
+ for (int i4 = 0; i4 < 3; i4++)
+ {
+ fF = mElement[i4][i3 + 1];
+ mElement[i4][i3 + 1] = fSin * mElement[i4][i3] + fCos * fF;
+ mElement[i4][i3] = fCos * mElement[i4][i3] - fSin * fF;
+ }
+ }
+ m_afDiag[i0] -= fP;
+ m_afSubd[i0] = fG;
+ m_afSubd[i2] = (Type)0.0;
+ }
+ if (i1 == iMaxIter)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void DecreasingSort(void)
+ {
+ //sort eigenvalues in decreasing order, e[0] >= ... >= e[iSize-1]
+ for (int i0 = 0, i1; i0 <= 3 - 2; i0++)
+ {
+ // locate maximum eigenvalue
+ i1 = i0;
+ Type fMax = m_afDiag[i1];
+ int i2;
+ for (i2 = i0 + 1; i2 < 3; i2++)
+ {
+ if (m_afDiag[i2] > fMax)
+ {
+ i1 = i2;
+ fMax = m_afDiag[i1];
+ }
+ }
+
+ if (i1 != i0)
+ {
+ // swap eigenvalues
+ m_afDiag[i1] = m_afDiag[i0];
+ m_afDiag[i0] = fMax;
+ // swap eigenvectors
+ for (i2 = 0; i2 < 3; i2++)
+ {
+ Type fTmp = mElement[i2][i0];
+ mElement[i2][i0] = mElement[i2][i1];
+ mElement[i2][i1] = fTmp;
+ m_bIsRotation = !m_bIsRotation;
+ }
+ }
+ }
+ }
+
+
+ void GuaranteeRotation(void)
+ {
+ if (!m_bIsRotation)
+ {
+ // change sign on the first column
+ for (int iRow = 0; iRow < 3; iRow++)
+ {
+ mElement[iRow][0] = -mElement[iRow][0];
+ }
+ }
+ }
+
+ Type mElement[3][3];
+ Type m_afDiag[3];
+ Type m_afSubd[3];
+ bool m_bIsRotation;
+};
+
+bool computeBestFitPlane(size_t vcount,
+ const REAL* points,
+ size_t vstride,
+ const REAL* weights,
+ size_t wstride,
+ REAL* plane)
+{
+ bool ret = false;
+
+ REAL kOrigin[3] = { 0, 0, 0 };
+
+ REAL wtotal = 0;
+
+ {
+ const char* source = (const char*) points;
+ const char* wsource = (const char*) weights;
+
+ for (size_t i = 0; i < vcount; i++)
+ {
+
+ const REAL* p = (const REAL*) source;
+
+ REAL w = 1;
+
+ if (wsource)
+ {
+ const REAL* ws = (const REAL*) wsource;
+ w = *ws; //
+ wsource += wstride;
+ }
+
+ kOrigin[0] += p[0] * w;
+ kOrigin[1] += p[1] * w;
+ kOrigin[2] += p[2] * w;
+
+ wtotal += w;
+
+ source += vstride;
+ }
+ }
+
+ REAL recip = 1.0f / wtotal; // reciprocol of total weighting
+
+ kOrigin[0] *= recip;
+ kOrigin[1] *= recip;
+ kOrigin[2] *= recip;
+
+
+ REAL fSumXX = 0;
+ REAL fSumXY = 0;
+ REAL fSumXZ = 0;
+
+ REAL fSumYY = 0;
+ REAL fSumYZ = 0;
+ REAL fSumZZ = 0;
+
+
+ {
+ const char* source = (const char*) points;
+ const char* wsource = (const char*) weights;
+
+ for (size_t i = 0; i < vcount; i++)
+ {
+
+ const REAL* p = (const REAL*) source;
+
+ REAL w = 1;
+
+ if (wsource)
+ {
+ const REAL* ws = (const REAL*) wsource;
+ w = *ws; //
+ wsource += wstride;
+ }
+
+ REAL kDiff[3];
+
+ kDiff[0] = w * (p[0] - kOrigin[0]); // apply vertex weighting!
+ kDiff[1] = w * (p[1] - kOrigin[1]);
+ kDiff[2] = w * (p[2] - kOrigin[2]);
+
+ fSumXX += kDiff[0] * kDiff[0]; // sume of the squares of the differences.
+ fSumXY += kDiff[0] * kDiff[1]; // sume of the squares of the differences.
+ fSumXZ += kDiff[0] * kDiff[2]; // sume of the squares of the differences.
+
+ fSumYY += kDiff[1] * kDiff[1];
+ fSumYZ += kDiff[1] * kDiff[2];
+ fSumZZ += kDiff[2] * kDiff[2];
+
+
+ source += vstride;
+ }
+ }
+
+ fSumXX *= recip;
+ fSumXY *= recip;
+ fSumXZ *= recip;
+ fSumYY *= recip;
+ fSumYZ *= recip;
+ fSumZZ *= recip;
+
+ // setup the eigensolver
+ Eigen<REAL> kES;
+
+ kES.mElement[0][0] = fSumXX;
+ kES.mElement[0][1] = fSumXY;
+ kES.mElement[0][2] = fSumXZ;
+
+ kES.mElement[1][0] = fSumXY;
+ kES.mElement[1][1] = fSumYY;
+ kES.mElement[1][2] = fSumYZ;
+
+ kES.mElement[2][0] = fSumXZ;
+ kES.mElement[2][1] = fSumYZ;
+ kES.mElement[2][2] = fSumZZ;
+
+ // compute eigenstuff, smallest eigenvalue is in last position
+ kES.DecrSortEigenStuff();
+
+ REAL kNormal[3];
+
+ kNormal[0] = kES.mElement[0][2];
+ kNormal[1] = kES.mElement[1][2];
+ kNormal[2] = kES.mElement[2][2];
+
+ // the minimum energy
+ plane[0] = kNormal[0];
+ plane[1] = kNormal[1];
+ plane[2] = kNormal[2];
+
+ plane[3] = 0 - fm_dot(kNormal, kOrigin);
+
+ ret = true;
+
+ return ret;
+}
+
+// computes the OBB for this set of points relative to this transform matrix.
+void computeOBB(size_t vcount, const REAL* points, size_t pstride, REAL* sides, REAL* matrix)
+{
+ const char* src = (const char*) points;
+
+ REAL bmin[3] = { 1e9, 1e9, 1e9 };
+ REAL bmax[3] = { -1e9, -1e9, -1e9 };
+
+ for (size_t i = 0; i < vcount; i++)
+ {
+ const REAL* p = (const REAL*) src;
+ REAL t[3];
+
+ fm_inverseRT(matrix, p, t); // inverse rotate translate
+
+ if (t[0] < bmin[0])
+ {
+ bmin[0] = t[0];
+ }
+ if (t[1] < bmin[1])
+ {
+ bmin[1] = t[1];
+ }
+ if (t[2] < bmin[2])
+ {
+ bmin[2] = t[2];
+ }
+
+ if (t[0] > bmax[0])
+ {
+ bmax[0] = t[0];
+ }
+ if (t[1] > bmax[1])
+ {
+ bmax[1] = t[1];
+ }
+ if (t[2] > bmax[2])
+ {
+ bmax[2] = t[2];
+ }
+
+ src += pstride;
+ }
+
+ REAL center[3];
+
+ sides[0] = bmax[0] - bmin[0];
+ sides[1] = bmax[1] - bmin[1];
+ sides[2] = bmax[2] - bmin[2];
+
+ center[0] = sides[0] * 0.5f + bmin[0];
+ center[1] = sides[1] * 0.5f + bmin[1];
+ center[2] = sides[2] * 0.5f + bmin[2];
+
+ REAL ocenter[3];
+
+ fm_rotate(matrix, center, ocenter);
+
+ matrix[12] += ocenter[0];
+ matrix[13] += ocenter[1];
+ matrix[14] += ocenter[2];
+
+}
+
+
+
+void computeBestFitOBB(size_t vcount, const REAL* points, size_t pstride, REAL* sides, REAL* matrix, bool bruteForce)
+{
+ fm_identity(matrix);
+ REAL bmin[3];
+ REAL bmax[3];
+ computeBestFitAABB(vcount, points, pstride, bmin, bmax);
+
+ REAL avolume = (bmax[0] - bmin[0]) * (bmax[1] - bmin[1]) * (bmax[2] - bmin[2]);
+
+ REAL plane[4];
+ computeBestFitPlane(vcount, points, pstride, 0, 0, plane);
+ fm_planeToMatrix(plane, matrix);
+ computeOBB(vcount, points, pstride, sides, matrix);
+
+ REAL refmatrix[16];
+ memcpy(refmatrix, matrix, 16 * sizeof(REAL));
+
+ REAL volume = sides[0] * sides[1] * sides[2];
+ if (bruteForce)
+ {
+ for (REAL a = 10; a < 180; a += 10)
+ {
+ REAL quat[4];
+ fm_eulerToQuat(0, a * FM_DEG_TO_RAD, 0, quat);
+ REAL temp[16];
+ REAL pmatrix[16];
+ fm_quatToMatrix(quat, temp);
+ fm_matrixMultiply(temp, refmatrix, pmatrix);
+ REAL psides[3];
+ computeOBB(vcount, points, pstride, psides, pmatrix);
+ REAL v = psides[0] * psides[1] * psides[2];
+ if (v < volume)
+ {
+ volume = v;
+ memcpy(matrix, pmatrix, sizeof(REAL) * 16);
+ sides[0] = psides[0];
+ sides[1] = psides[1];
+ sides[2] = psides[2];
+ }
+ }
+ }
+ if (avolume < volume)
+ {
+ fm_identity(matrix);
+ matrix[12] = (bmin[0] + bmax[0]) * 0.5f;
+ matrix[13] = (bmin[1] + bmax[1]) * 0.5f;
+ matrix[14] = (bmin[2] + bmax[2]) * 0.5f;
+ sides[0] = bmax[0] - bmin[0];
+ sides[1] = bmax[1] - bmin[1];
+ sides[2] = bmax[2] - bmin[2];
+ }
+}
+
+void computeBestFitOBB(size_t vcount, const REAL* points, size_t pstride, REAL* sides, REAL* pos, REAL* quat, bool bruteForce)
+{
+ REAL matrix[16];
+ computeBestFitOBB(vcount, points, pstride, sides, matrix, bruteForce);
+ fm_getTranslation(matrix, pos);
+ fm_matrixToQuat(matrix, quat);
+}
+
+void computeBestFitABB(size_t vcount, const REAL* points, size_t pstride, REAL* sides, REAL* pos)
+{
+ REAL bmin[3];
+ REAL bmax[3];
+
+ bmin[0] = points[0];
+ bmin[1] = points[1];
+ bmin[2] = points[2];
+
+ bmax[0] = points[0];
+ bmax[1] = points[1];
+ bmax[2] = points[2];
+
+ const char* cp = (const char*) points;
+ for (size_t i = 0; i < vcount; i++)
+ {
+ const REAL* p = (const REAL*) cp;
+
+ if (p[0] < bmin[0])
+ {
+ bmin[0] = p[0];
+ }
+ if (p[1] < bmin[1])
+ {
+ bmin[1] = p[1];
+ }
+ if (p[2] < bmin[2])
+ {
+ bmin[2] = p[2];
+ }
+
+ if (p[0] > bmax[0])
+ {
+ bmax[0] = p[0];
+ }
+ if (p[1] > bmax[1])
+ {
+ bmax[1] = p[1];
+ }
+ if (p[2] > bmax[2])
+ {
+ bmax[2] = p[2];
+ }
+
+ cp += pstride;
+ }
+
+
+ sides[0] = bmax[0] - bmin[0];
+ sides[1] = bmax[1] - bmin[1];
+ sides[2] = bmax[2] - bmin[2];
+
+ pos[0] = bmin[0] + sides[0] * 0.5f;
+ pos[1] = bmin[1] + sides[1] * 0.5f;
+ pos[2] = bmin[2] + sides[2] * 0.5f;
+
+}
+
+
+REAL computeBestFitSphere(size_t vcount, const REAL* points, size_t pstride, REAL* center)
+{
+ REAL sides[3];
+ REAL omatrix[16];
+ computeBestFitOBB(vcount, points, pstride, sides, omatrix, true);
+ center[0] = omatrix[12];
+ center[1] = omatrix[13];
+ center[2] = omatrix[14];
+ REAL radius = static_cast<REAL>(sqrt(sides[0] * sides[0] + sides[1] * sides[1] + sides[2] * sides[2]));
+ return radius * 0.5f;
+}
+
+void computeBestFitCapsule(size_t vcount, const REAL* points, size_t pstride, REAL& radius, REAL& height, REAL matrix[16], bool bruteForce)
+{
+ REAL sides[3];
+ REAL omatrix[16];
+ computeBestFitOBB(vcount, points, pstride, sides, omatrix, bruteForce);
+
+ int axis = 0;
+ if (sides[0] > sides[1] && sides[0] > sides[2])
+ {
+ axis = 0;
+ }
+ else if (sides[1] > sides[0] && sides[1] > sides[2])
+ {
+ axis = 1;
+ }
+ else
+ {
+ axis = 2;
+ }
+
+ REAL localTransform[16];
+
+ REAL maxDist = 0;
+ REAL maxLen = 0;
+
+ switch (axis)
+ {
+ case 0:
+ {
+ fm_eulerMatrix(0, 0, FM_PI / 2, localTransform);
+ fm_matrixMultiply(localTransform, omatrix, matrix);
+
+ const unsigned char* scan = (const unsigned char*)points;
+ for (size_t i = 0; i < vcount; i++)
+ {
+ const REAL* p = (const REAL*)scan;
+ REAL t[3];
+ fm_inverseRT(omatrix, p, t);
+ REAL dist = t[1] * t[1] + t[2] * t[2];
+ if (dist > maxDist)
+ {
+ maxDist = dist;
+ }
+ REAL l = (REAL) fabs(t[0]);
+ if (l > maxLen)
+ {
+ maxLen = l;
+ }
+ scan += pstride;
+ }
+ }
+ height = sides[0];
+ break;
+ case 1:
+ {
+ fm_eulerMatrix(0, FM_PI / 2, 0, localTransform);
+ fm_matrixMultiply(localTransform, omatrix, matrix);
+
+ const unsigned char* scan = (const unsigned char*)points;
+ for (size_t i = 0; i < vcount; i++)
+ {
+ const REAL* p = (const REAL*)scan;
+ REAL t[3];
+ fm_inverseRT(omatrix, p, t);
+ REAL dist = t[0] * t[0] + t[2] * t[2];
+ if (dist > maxDist)
+ {
+ maxDist = dist;
+ }
+ REAL l = (REAL) fabs(t[1]);
+ if (l > maxLen)
+ {
+ maxLen = l;
+ }
+ scan += pstride;
+ }
+ }
+ height = sides[1];
+ break;
+ case 2:
+ {
+ fm_eulerMatrix(FM_PI / 2, 0, 0, localTransform);
+ fm_matrixMultiply(localTransform, omatrix, matrix);
+
+ const unsigned char* scan = (const unsigned char*)points;
+ for (size_t i = 0; i < vcount; i++)
+ {
+ const REAL* p = (const REAL*)scan;
+ REAL t[3];
+ fm_inverseRT(omatrix, p, t);
+ REAL dist = t[0] * t[0] + t[1] * t[1];
+ if (dist > maxDist)
+ {
+ maxDist = dist;
+ }
+ REAL l = (REAL) fabs(t[2]);
+ if (l > maxLen)
+ {
+ maxLen = l;
+ }
+ scan += pstride;
+ }
+ }
+ height = sides[2];
+ break;
+ }
+ radius = (REAL)sqrt(maxDist);
+ height = (maxLen * 2) - (radius * 2);
+}
+
+
+void computeBestFitCapsule(size_t vcount, const float* points, size_t pstride, float& radius, float& height, float pos[3], float quat[4], bool bruteForce)
+{
+ REAL matrix[16];
+ computeBestFitCapsule(vcount, points, pstride, radius, height, matrix, bruteForce);
+ fm_getTranslation(matrix, pos);
+ fm_matrixToQuat(matrix, quat);
+}
+
+REAL computeBestFitAABB(size_t vcount, const REAL* points, size_t pstride, REAL* bmin, REAL* bmax) // returns the diagonal distance
+{
+
+ const unsigned char* source = (const unsigned char*) points;
+
+ bmin[0] = points[0];
+ bmin[1] = points[1];
+ bmin[2] = points[2];
+
+ bmax[0] = points[0];
+ bmax[1] = points[1];
+ bmax[2] = points[2];
+
+
+ for (size_t i = 1; i < vcount; i++)
+ {
+ source += pstride;
+ const REAL* p = (const REAL*) source;
+
+ if (p[0] < bmin[0])
+ {
+ bmin[0] = p[0];
+ }
+ if (p[1] < bmin[1])
+ {
+ bmin[1] = p[1];
+ }
+ if (p[2] < bmin[2])
+ {
+ bmin[2] = p[2];
+ }
+
+ if (p[0] > bmax[0])
+ {
+ bmax[0] = p[0];
+ }
+ if (p[1] > bmax[1])
+ {
+ bmax[1] = p[1];
+ }
+ if (p[2] > bmax[2])
+ {
+ bmax[2] = p[2];
+ }
+
+ }
+
+ REAL dx = bmax[0] - bmin[0];
+ REAL dy = bmax[1] - bmin[1];
+ REAL dz = bmax[2] - bmin[2];
+
+ return (REAL) sqrt(dx * dx + dy * dy + dz * dz);
+
+}
+
+
+}; // namespace SharedTools
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
diff --git a/APEX_1.4/shared/external/src/FilterBits.cpp b/APEX_1.4/shared/external/src/FilterBits.cpp
new file mode 100644
index 00000000..9c10c73d
--- /dev/null
+++ b/APEX_1.4/shared/external/src/FilterBits.cpp
@@ -0,0 +1,692 @@
+/*
+ * 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 "FilterBits.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+
+#pragma warning(disable:4100 4996)
+
+#define MAX_FILTER_DATA_SPECS 512 // maximum number of unique filter data specs ever expected, 512 is a pretty huge number; bump it if necessary.
+#define MAX_TYPES 64 // maximum number of unique bit 'types'
+#define MAX_TEMP_STRING 1024 // maximum size of the temporary string to return intermediate ASCII data
+
+FilterBits *gFilterBits=NULL;
+
+namespace FILTER_BITS
+{
+
+#define MAGIC_ID1 0x57454947
+#define MAGIC_ID2 0x48544544
+
+class FilterDataSpec : public FilterData, public EncodedFilterData
+{
+public:
+ FilterDataSpec(void)
+ {
+ mSpec = NULL;
+ }
+ ~FilterDataSpec(void)
+ {
+ free(mSpec);
+ }
+
+ void buildEncode(void)
+ {
+ ew32.word0 = MAGIC_ID1;
+ ew32.word1 = MAGIC_ID2;
+#if PX_X64
+ ew64.matchBits64 = (uint64_t)this;
+#else
+ ew32.word2 = (uint32_t)this;
+ ew32.word3 = 0;
+#endif
+ }
+
+ float computeWeight(uint64_t mask,float weightIn) const
+ {
+ float w = weightIn;
+ uint32_t index=0;
+ while ( mask )
+ {
+ if ( mask &1 )
+ {
+ w*=weights[index];
+ }
+ mask = mask>>1;
+ index++;
+ }
+ return w;
+ }
+
+ bool allWeightOne(void) const // return true if the weights of all mask bits are one..
+ {
+ bool ret = true;
+
+ uint32_t index=0;
+ uint64_t mask = ew64.matchBits64;
+ while ( mask )
+ {
+ if ( mask &1 )
+ {
+ if ( weights[index] != 1 )
+ {
+ ret = false;
+ break;
+ }
+ }
+ mask = mask>>1;
+ index++;
+ }
+ return ret;
+ }
+
+ char *mSpec;
+};
+
+class FilterBitsImpl : public FilterBits
+{
+public:
+
+ FilterBitsImpl(void)
+ {
+ gFilterBits = this;
+ mFilterDataSpecCount = 0;
+ mTypesCount = 0;
+ }
+
+ virtual ~FilterBitsImpl(void)
+ {
+ gFilterBits = NULL;
+ for (uint32_t i=0; i<mFilterDataSpecCount; i++)
+ {
+ FilterDataSpec *spec = mFilterDataTypes[i];
+ delete spec;
+ }
+ for (uint32_t i=0; i<mTypesCount; i++)
+ {
+ char *str = mTypes[i];
+ free(str);
+ }
+ }
+
+ void addTemp(const char *str) const
+ {
+ size_t slen = strlen(mTemp) + strlen(str)+1;
+ if ( slen < MAX_TEMP_STRING )
+ {
+ strcat(mTemp,str);
+ }
+ else
+ {
+ assert(0); // exceeded maximum length of the temporary string; probably need to make it bigger.
+ }
+
+ }
+
+ // Returns the ASCII string equivalent of this set of FilterData flags
+ virtual const char *getFilterDataString(const FilterData &fd) const
+ {
+ mTemp[0] = 0;
+
+ if ( fd.w64.typeBits64 )
+ {
+ uint64_t bits = 1;
+ bool previous = false;
+ for (uint32_t i=0; i<mTypesCount; i++)
+ {
+ if ( bits & fd.w64.typeBits64 )
+ {
+ if ( previous )
+ {
+ addTemp(",");
+ }
+ addTemp(mTypes[i]);
+ previous = true;
+ }
+ bits = bits<<1;
+ }
+ }
+
+ const FilterDataSpec *fds = static_cast< const FilterDataSpec *>(&fd);
+
+ if ( fd.w64.matchBits64 )
+ {
+ addTemp("=");
+ if ( fd.w64.matchBits64 == 0xffffffffffffffff && fds->allWeightOne() )
+ {
+ addTemp("all");
+ }
+ else
+ {
+ uint64_t bits = 1;
+ bool previous = false;
+
+ for (uint32_t i=0; i<mTypesCount; i++)
+ {
+ if ( bits & fd.w64.matchBits64 )
+ {
+ if ( previous )
+ {
+ addTemp(",");
+ }
+ addTemp(mTypes[i]);
+
+ if ( fds->weights[i] != 1 )
+ {
+ addTemp("(");
+ char number[512];
+ sprintf(number,"%f", fds->weights[i] );
+ addTemp(number);
+ addTemp(")");
+ }
+
+ previous = true;
+ }
+ bits = bits<<1;
+ }
+ }
+ }
+ else
+ {
+ addTemp("=none");
+ }
+
+ return mTemp;
+ }
+
+
+ // Returns the combination of string of types based on this bit sequence.
+ virtual const char *getFilterString(uint64_t bits) const
+ {
+ mTemp[0] = 0;
+
+ if ( bits == 0 )
+ {
+ addTemp("none");
+ }
+ else if ( bits == 0xFFFFFFFFFFFFFFFF )
+ {
+ addTemp("all");
+ }
+ else
+ {
+ uint64_t bits = 1;
+ bool previous = false;
+ for (uint32_t i=0; i<mTypesCount; i++)
+ {
+ if ( bits & bits )
+ {
+ if ( previous )
+ {
+ addTemp(",");
+ }
+ addTemp(mTypes[i]);
+ previous = true;
+ }
+ bits = bits<<1;
+ }
+ }
+ return mTemp;
+ }
+
+ // Returns the string for a single bit type 0-63
+ virtual const char *getTypeString(uint8_t type) const
+ {
+ const char *ret = NULL;
+
+ if ( type < mTypesCount )
+ {
+ ret = mTypes[type];
+ }
+
+ return ret;
+ }
+
+ // Returns how many types were defined.
+ virtual uint8_t getTypeCount(void) const
+ {
+ return (uint8_t) mTypesCount;
+ }
+
+ char *getAllocString(const char *str) const
+ {
+ if ( str == NULL ) str = "";
+ size_t slen = strlen(str);
+ char *ret = (char *)malloc(slen+1);
+ assert(ret);
+ if ( ret )
+ {
+ memcpy(ret,str,slen+1);
+ }
+ return ret;
+ }
+
+ // Return the bit flag for a single group/type
+ virtual uint64_t getTypeBit(const char *str,uint32_t &index,bool &applyDefault)
+ {
+ uint64_t ret = 0;
+
+ if ( strcmp(str,"all") == 0 )
+ {
+ ret = 0xFFFFFFFFFFFFFFFF;
+ index = 0xFFFFFFFF;
+ }
+ else if ( strcmp(str,"none") == 0 )
+ {
+ index = 0;
+ }
+ else if ( strcmp(str,"default") == 0 )
+ {
+ index = 0;
+ applyDefault = true;
+ }
+ else
+ {
+ uint64_t bit = 1;
+ for (size_t i=0; i<mTypesCount; i++)
+ {
+ const char *t = mTypes[i];
+ if ( strcmp(t,str) == 0 )
+ {
+ ret = bit;
+ index = (uint32_t)i;
+ break;
+ }
+ bit = bit<<1; // advance the bit field.
+ }
+
+ if ( ret == 0 ) // if the type was not already found
+ {
+ if ( mTypesCount < 64 )
+ {
+ index = mTypesCount;
+ mTypes[mTypesCount] = getAllocString(str);
+ mTypesCount++;
+ ret = bit;
+ }
+ else
+ {
+ assert(0); // encountered more than 64 uinque bit field 'types' which exceeded the maximum size available.
+ }
+ }
+
+ }
+ return ret;
+ }
+
+ // Return true if this makes the end of the numeric (either zero byte, a percent sign, or a right parenthesis
+ inline bool eon(char c)
+ {
+ return ( c == 0 || c == '%' || c == ')' || c == ',' );
+ }
+ inline bool eon1(char c)
+ {
+ return ( c == 0 || c == ')' || c == ',' );
+ }
+
+ // Return true if this marks the end of the keyword/string which is either a zero byte, a comma, or an open parenthesis
+ inline bool eos(char c)
+ {
+ return ( c == 0 || c == ',' || c == '(' );
+ }
+
+ inline const char* skipLeadingSpaces(const char* str)
+ {
+ while ( ::isspace(*str) )
+ {
+ ++str;
+ }
+ return str;
+ }
+ inline char* skipTrailingSpaces(char* str, char* str0)
+ {
+ while ( str > str0 && ::isspace(*(str-1)) )
+ {
+ --str;
+ }
+ return str;
+ }
+
+ // Converts this ASCII string to it's corresponding binary bit representation.
+ virtual const FilterData *getFilterData(const char *str)
+ {
+ FilterData fd;
+
+ const char *sourceString = str;
+
+ while ( *str && *str != '=' )
+ {
+ char temp[256];
+ char *dest = temp;
+
+ str = skipLeadingSpaces(str);
+ while ( *str && *str != ',' && *str != '=' )
+ {
+ *dest++ = *str++;
+ if ( dest > &temp[254] )
+ {
+ break;
+ }
+ }
+ dest = skipTrailingSpaces(dest, temp);
+ *dest = 0;
+ if ( *temp )
+ {
+ uint32_t index;
+ bool adefault = false;
+ fd.w64.typeBits64 |= getTypeBit(temp,index,adefault);
+ }
+ if ( *str == ',' )
+ {
+ str++;
+ }
+ }
+ if ( *str == '=' )
+ {
+ bool applyDefault = false;
+ float defaultWeight = 1.0f;
+ str++;
+ while ( *str )
+ {
+ char temp[256];
+ char *dest = temp;
+
+ str = skipLeadingSpaces(str);
+ while ( !eos(*str) )
+ {
+ *dest++ = *str++;
+ if ( dest > &temp[254] )
+ {
+ break;
+ }
+ }
+ dest = skipTrailingSpaces(dest, temp);
+ *dest = 0;
+ uint64_t bits = 0;
+ uint32_t index = 0;
+ bool adefault = false;
+ if (*temp)
+ {
+ fd.w64.matchBits64 |= (bits = getTypeBit(temp,index,adefault));
+ }
+ if ( *str == '(' )
+ {
+ str++;
+ //don't need to skip leading spaces here - atof does it!
+ float v = (float)atof(str);
+ // scan past the weight value and up to the closing parenthesis
+ while ( !eon(*str) ) str++;
+ if ( *str == '%' )
+ {
+ v*=1.0f/100.0f; // convert from a percentage notation to a straight up multiplier value
+ str++;
+ // scan up to the closing parenthesis
+ while ( !eon1(*str) ) str++;
+ }
+ if ( adefault )
+ {
+ applyDefault = true;
+ defaultWeight*=v;
+ }
+ if ( bits )
+ {
+ if ( index == 0xFFFFFFFF )
+ {
+ for (uint32_t i=0; i<64; i++)
+ {
+ fd.weights[i] = v;
+ }
+ }
+ else
+ {
+ fd.weights[index] = v;
+ }
+ }
+ // If we ended at the close paren, then advance past it and pick up at the next comma separated value
+ if ( *str == ')' ) str++;
+ }
+ else
+ {
+ if ( adefault )
+ {
+ applyDefault = true;
+ }
+ }
+ if ( *str == ',' )
+ {
+ str++;
+ }
+ }
+ if ( applyDefault )
+ {
+ // ok any bit not currently set to true, we set to true and assign the default weight...
+ uint64_t bit = 1;
+ for (uint32_t i=0; i<64; i++)
+ {
+ if ( fd.w64.matchBits64 & bit ) // if it's already set, leave it alone..
+ {
+
+ }
+ else
+ {
+ fd.w64.matchBits64|=bit;
+ fd.weights[i] = defaultWeight;
+ }
+ bit = bit << 1;
+ }
+ }
+ }
+
+
+
+ FilterData *ret = NULL;
+
+ for (uint32_t i=0; i<mFilterDataSpecCount; i++)
+ {
+ FilterDataSpec *spec = mFilterDataTypes[i];
+ if ( spec )
+ {
+ if ( spec->w64.typeBits64 == fd.w64.typeBits64 && spec->w64.matchBits64 == fd.w64.matchBits64 )
+ {
+ bool areWeightsEqual = true;
+ for (uint32_t i = 0; i < 64; ++i)
+ {
+ if (spec->weights[i] != fd.weights[i])
+ {
+ areWeightsEqual = false;
+ break;
+ }
+ }
+ if (areWeightsEqual)
+ {
+ ret = static_cast< FilterData *>(spec);
+ break;
+ }
+ }
+ }
+ }
+ if ( ret == NULL )
+ {
+ FilterDataSpec *spec = new FilterDataSpec;
+ FilterData *root = static_cast< FilterData *>(spec);
+ *root = fd;
+ spec->mSpec = getAllocString(sourceString);
+ spec->typeString = spec->mSpec;
+ bool added = false;
+ for (uint32_t i=0; i<mFilterDataSpecCount; i++)
+ {
+ if ( mFilterDataTypes[i] == NULL )
+ {
+ mFilterDataTypes[i] = spec;
+ added = true;
+ break;
+ }
+ }
+ if ( !added )
+ {
+ if ( mFilterDataSpecCount < MAX_FILTER_DATA_SPECS )
+ {
+ mFilterDataTypes[mFilterDataSpecCount] = spec;
+ mFilterDataSpecCount++;
+ }
+ else
+ {
+ assert(0); // if you hit this, it means that we encountered more than MAX_FILTER_DATA_SPECS unique filter strings; bump the maximum up higher to match the needs of your application.
+ }
+ }
+ ret = static_cast< FilterData *>(spec);
+ }
+
+ if ( ret )
+ {
+ FilterDataSpec *fds = static_cast< FilterDataSpec *>(ret);
+ fds->buildEncode();
+ }
+
+ return ret;
+ }
+
+ // Encode it into 128 bits
+ virtual const EncodedFilterData &getEncodedFilterData(const FilterData &fd)
+ {
+ const FilterDataSpec *fds = static_cast< const FilterDataSpec *>(&fd);
+ const EncodedFilterData *efd = static_cast< const EncodedFilterData *>(fds);
+ return *efd;
+ }
+
+ virtual bool isEncodedFilterData(const EncodedFilterData &d) const
+ {
+ return getFilterData(d) ? true : false;
+ }
+
+ // If this is properly encoded filter data, then return the original filter-data pointer
+ virtual FilterData * getFilterData(const EncodedFilterData &d) const
+ {
+ FilterData *ret = NULL;
+
+ if ( d.ew32.word0 == MAGIC_ID1 && d.ew32.word1 == MAGIC_ID2 )
+ {
+#if PX_X64
+ ret = (FilterData *)d.ew64.matchBits64;
+#else
+ ret = (FilterData *)d.ew32.word2;
+#endif
+ }
+ return ret;
+ }
+
+ // see if these two objects interact and, if so, return the weighting value to apply
+ virtual bool getWeightedFilter(const EncodedFilterData &o1,const EncodedFilterData &o2,float &weight)
+ {
+ bool ret = false;
+
+ weight = 0;
+
+ const FilterData *fd1 = getFilterData(o1);
+ const FilterData *fd2 = getFilterData(o2);
+
+ EncodedFilterData d1 = o1;
+ EncodedFilterData d2 = o2;
+ if ( fd1 )
+ {
+ d1.ew64.typeBits64 = fd1->w64.typeBits64;
+ d1.ew64.matchBits64 = fd1->w64.matchBits64;
+ }
+ if ( fd2 )
+ {
+ d2.ew64.typeBits64 = fd2->w64.typeBits64;
+ d2.ew64.matchBits64 = fd2->w64.matchBits64;
+ }
+ uint64_t mask1 = d1.ew64.typeBits64 & d2.ew64.matchBits64;
+ uint64_t mask2 = d2.ew64.typeBits64 & d1.ew64.matchBits64;
+ // See if the second object match bits matches the first object's type bits.
+ if ( mask1 || mask2 )
+ {
+ ret = true;
+ // Default weighting multiplier value
+ weight = 1;
+ const FilterDataSpec *fds1 = static_cast< const FilterDataSpec *>(fd1);
+ const FilterDataSpec *fds2 = static_cast< const FilterDataSpec *>(fd2);
+ // If we have a fully weighted spec, then return the weighted value of matching bits.
+ if ( mask1 )
+ {
+ if ( fds2 )
+ {
+ weight = fds2->computeWeight(mask1,weight);
+ mask2&=~mask1; // remove bits already processed...
+ }
+ }
+ if ( mask2 && fds1 )
+ {
+ weight = fds1->computeWeight(mask2,weight);
+ }
+ }
+ return ret;
+ }
+
+ virtual const EncodedFilterData *getEncodedFilterData(const char *str)
+ {
+ const EncodedFilterData *ret = NULL;
+ const FilterData *fd = getFilterData(str);
+ if ( fd )
+ {
+ const FilterDataSpec *fds = static_cast< const FilterDataSpec *>(fd);
+ ret = static_cast< const EncodedFilterData *>(fds);
+ }
+ return ret;
+ }
+
+
+ virtual void release(void)
+ {
+ delete this;
+ }
+
+ virtual void release(FilterData &fb)
+ {
+ FilterDataSpec *fds = static_cast< FilterDataSpec *>(&fb);
+ bool found = false;
+ for (uint32_t i=0; i<mFilterDataSpecCount; i++)
+ {
+ if ( mFilterDataTypes[i] == fds )
+ {
+ found = true;
+ mFilterDataTypes[i] = NULL;
+ if ( (i+1) == mFilterDataSpecCount ) // if released the last one in the array, then decrement the array count size.
+ {
+ mFilterDataSpecCount--;
+ }
+ break;
+ }
+ }
+ assert(found); // it should always find the address of the item being released!
+ }
+
+ mutable char mTemp[MAX_TEMP_STRING]; // temporary string used to return requests; it's considered 'mutable' i.e. const methods are ok to modify it.
+ uint32_t mTypesCount;
+ char *mTypes[MAX_TYPES];
+ uint32_t mFilterDataSpecCount;
+ FilterDataSpec *mFilterDataTypes[MAX_FILTER_DATA_SPECS];
+};
+
+}; // end of namespace FILTER_BITS
+
+
+FilterBits *createFilterBits(void)
+{
+ FILTER_BITS::FilterBitsImpl *f = new FILTER_BITS::FilterBitsImpl;
+ return static_cast< FilterBits *>(f);
+}
diff --git a/APEX_1.4/shared/external/src/Find.cpp b/APEX_1.4/shared/external/src/Find.cpp
new file mode 100644
index 00000000..fe2a6a0d
--- /dev/null
+++ b/APEX_1.4/shared/external/src/Find.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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 <PsString.h>
+#include "PxAssert.h"
+
+#include <DirEntry.h>
+
+#include "Find.h"
+
+namespace nvidia {
+namespace apex {
+
+void Find(const char* root, FileHandler& f, const char** ignoredFiles)
+{
+ if (!root)
+ return;
+
+ // Fix slashes
+ char goodRoot[128];
+ strcpy(goodRoot, root);
+#if PX_WINDOWS_FAMILY
+ for (char* p = goodRoot; *p; ++p)
+ {
+ if ('/' == *p)
+ *p = '\\';
+ }
+#endif
+
+ physx::DirEntry dentry;
+ if (!physx::DirEntry::GetFirstEntry(goodRoot, dentry))
+ {
+ PX_ALWAYS_ASSERT();
+ return;
+ }
+
+ for (; !dentry.isDone(); dentry.next())
+ {
+ const char* filename = dentry.getName();
+
+ if (!filename || 0 == strcmp(".", filename) || 0 == strcmp("..", filename))
+ continue;
+
+ bool doSkip = false;
+ for (size_t i = 0; ignoredFiles && ignoredFiles[i]; ++i)
+ {
+ if (0 == strcmp(filename, ignoredFiles[i]))
+ {
+ doSkip = true;
+ break;
+ }
+ }
+
+ if (doSkip)
+ continue;
+
+ char tmp[128];
+ physx::shdfnd::snprintf(tmp, sizeof(tmp), "%s/%s", goodRoot, filename);
+
+#if PX_WINDOWS_FAMILY
+ for (char* p = tmp; *p; ++p)
+ {
+ if ('/' == *p)
+ *p = '\\';
+ }
+#endif
+
+ if (dentry.isDirectory())
+ {
+ Find(tmp, f, ignoredFiles);
+ continue;
+ }
+
+ f.handle(tmp);
+ }
+}
+
+}}
diff --git a/APEX_1.4/shared/external/src/MaterialList.cpp b/APEX_1.4/shared/external/src/MaterialList.cpp
new file mode 100644
index 00000000..1679dd99
--- /dev/null
+++ b/APEX_1.4/shared/external/src/MaterialList.cpp
@@ -0,0 +1,436 @@
+/*
+ * 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 "MaterialList.h"
+
+#include "PxAssert.h"
+
+#if PX_WINDOWS_FAMILY
+#define NOMINMAX
+#include <windows.h>
+#endif
+
+#include "PsFastXml.h"
+#include "PsFileBuffer.h"
+#include "PsString.h"
+
+#include "PxInputDataFromPxFileBuf.h"
+
+namespace Samples
+{
+
+MaterialList::MaterialList()
+{
+}
+
+
+MaterialList::~MaterialList()
+{
+}
+
+
+
+void MaterialList::clear()
+{
+ mMaterialNames.clear();
+ mTextureNames.clear();
+
+ mPaths.clear();
+}
+
+
+
+void MaterialList::addPath(const char* path)
+{
+ PX_UNUSED(path);
+
+#if PX_WINDOWS_FAMILY
+ const char* matPrefixes[2] = { "", "materials/" };
+
+ PX_ASSERT(strlen(path) < 240);
+ char fileMask[256];
+
+ unsigned int materialsAdded = 0;
+
+ for (unsigned int pass = 0; pass < 2; pass++)
+ {
+ physx::shdfnd::snprintf(fileMask, 255, "%s/%s*.xml", path, matPrefixes[(unsigned int)pass]);
+
+ WIN32_FIND_DATA ffd;
+ HANDLE hFind = ::FindFirstFile(fileMask, &ffd);
+
+ if (hFind != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ materialsAdded += addMaterial(path, matPrefixes[pass], ffd.cFileName);
+ }
+ while (FindNextFile(hFind, &ffd) != 0);
+
+ FindClose(hFind);
+ }
+ }
+
+
+ const char* texPrefixes[2] = { "", "textures/" };
+ const char* texSuffixes[2] = { "dds", "tga" };
+
+ unsigned int texturesAdded = 0;
+
+ for (unsigned int prefixes = 0; prefixes < 2; prefixes++)
+ {
+ for (unsigned int suffixes = 0; suffixes < 2; suffixes++)
+ {
+ physx::shdfnd::snprintf(fileMask, 255, "%s/%s*.%s", path, texPrefixes[prefixes], texSuffixes[suffixes]);
+
+ WIN32_FIND_DATA ffd;
+ HANDLE hFind = ::FindFirstFile(fileMask, &ffd);
+
+ if (hFind != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ texturesAdded += addTexture(path, texPrefixes[prefixes], ffd.cFileName);
+ }
+ while (FindNextFile(hFind, &ffd) != 0);
+
+ FindClose(hFind);
+ }
+ }
+ }
+
+ if (materialsAdded > 0 || texturesAdded > 0)
+ {
+ mPaths.push_back(path);
+ }
+
+#if 0
+ // verification step
+ for (tMaterialNames::const_iterator it = mMaterialNames.begin(); it != mMaterialNames.end(); ++it)
+ {
+ if (!it->second.diffuseTexture.empty())
+ {
+ tTextureNames::const_iterator tex = mTextureNames.find(it->second.diffuseTexture);
+ if (tex == mTextureNames.end())
+ {
+ PX_ASSERT(!"Texture not found");
+ }
+ else
+ {
+ PX_ASSERT(tex->second.fromPath <= it->second.fromPath);
+ }
+ }
+
+ if (!it->second.normalTexture.empty())
+ {
+ tTextureNames::const_iterator tex = mTextureNames.find(it->second.normalTexture);
+ if (tex == mTextureNames.end())
+ {
+ PX_ASSERT(!"Texture not found");
+ }
+ else
+ {
+ PX_ASSERT(tex->second.fromPath <= it->second.fromPath);
+ }
+ }
+ }
+#endif
+
+#endif
+}
+
+
+
+const MaterialList::MaterialInfo* MaterialList::containsMaterial(const char* materialName) const
+{
+ tMaterialNames::const_iterator it = mMaterialNames.find(materialName);
+
+ if (it != mMaterialNames.end())
+ {
+ return &it->second;
+ }
+
+ return NULL;
+}
+
+
+
+const char* MaterialList::findClosest(const char* materialName) const
+{
+ for (std::map<std::string, MaterialInfo>::const_iterator it = mMaterialNames.begin(); it != mMaterialNames.end(); ++it)
+ {
+ if (it->first.find(materialName) != std::string::npos)
+ {
+ return it->first.c_str();
+ }
+ }
+
+ //return "materials/debug_texture.xml";
+ return "materials/simple_lit.xml";
+}
+
+
+
+const MaterialList::TextureInfo* MaterialList::containsTexture(const char* textureName) const
+{
+ tTextureNames::const_iterator it = mTextureNames.find(textureName);
+
+ if (it != mTextureNames.end())
+ {
+ return &it->second;
+ }
+
+ return NULL;
+}
+
+
+
+
+MaterialList::MaterialInfo::MaterialInfo() :
+ isLit(false),
+ vshaderStatic(false),
+ vshader1bone(false),
+ vshader4bones(false),
+ fromPath(0xffffffff)
+{
+}
+
+
+
+MaterialList::TextureInfo::TextureInfo() :
+ fromPath(0xffffffff)
+{
+}
+
+
+void MaterialList::getFirstMaterial(std::string& name, MaterialInfo& info)
+{
+ mMaterialIterator = mMaterialNames.begin();
+
+ name = mMaterialIterator->first;
+ info = mMaterialIterator->second;
+}
+
+
+
+bool MaterialList::getNextMaterial(std::string& name, MaterialInfo& info)
+{
+ ++mMaterialIterator;
+ if (mMaterialIterator != mMaterialNames.end())
+ {
+ name = mMaterialIterator->first;
+ info = mMaterialIterator->second;
+
+ return true;
+ }
+
+ return false;
+}
+
+
+
+class XmlParser : public physx::shdfnd::FastXml::Callback
+{
+public:
+ XmlParser(unsigned int fromPath) : mIsMaterial(false)
+ {
+ mMaterialInfo.fromPath = fromPath;
+ }
+
+ virtual ~XmlParser() {}
+
+ const MaterialList::MaterialInfo& getMaterialInfo() { return mMaterialInfo; }
+
+ bool isMaterial() { return mIsMaterial; }
+
+protected:
+
+ // encountered a comment in the XML
+ virtual bool processComment(const char * /*comment*/)
+ {
+ return true;
+ }
+
+ // 'element' is the name of the element that is being closed.
+ // depth is the recursion depth of this element.
+ // Return true to continue processing the XML file.
+ // Return false to stop processing the XML file; leaves the read pointer of the stream right after this close tag.
+ // The bool 'isError' indicates whether processing was stopped due to an error, or intentionally canceled early.
+ virtual bool processClose(const char * /*element*/, unsigned int /*depth*/, bool & /*isError*/)
+ {
+ return true;
+ }
+
+ // return true to continue processing the XML document, false to skip.
+ virtual bool processElement(
+ const char *elementName, // name of the element
+ const char *elementData, // element data, null if none
+ const physx::shdfnd::FastXml::AttributePairs& attr,
+ int /*lineno*/) // line number in the source XML file
+ {
+ if (!mIsMaterial && ::strcmp(elementName, "material") != 0)
+ {
+ // not a material file
+ return false;
+ }
+
+ if (::strcmp(elementName, "material") == 0)
+ {
+ for (int i = 0; i < attr.getNbAttr(); i++)
+ {
+ if (::strcmp(attr.getKey(i), "type") == 0)
+ {
+ mMaterialInfo.isLit = ::strcmp(attr.getValue(i), "lit") == 0;
+ }
+ }
+ mIsMaterial = true;
+ }
+ else if (::strcmp(elementName, "shader") == 0)
+ {
+ int nameIndex = -1;
+ for (int i = 0; i < attr.getNbAttr(); i++)
+ {
+ if (::strcmp(attr.getKey(i), "name") == 0)
+ {
+ nameIndex = i;
+ break;
+ }
+ }
+
+ if (::strcmp(attr.getValue(nameIndex), "vertex") == 0)
+ {
+ // identify the three
+ mMaterialInfo.vshaderStatic |= strstr(elementData, "staticmesh") != NULL;
+ mMaterialInfo.vshader1bone |= strstr(elementData, "skeletalmesh_1bone") != NULL;
+ mMaterialInfo.vshader4bones |= strstr(elementData, "skeletalmesh_4bone") != NULL;
+ }
+ }
+ else if (::strcmp(elementName, "sampler2D") == 0)
+ {
+ int nameIndex = -1;
+ for (int i = 0; i < attr.getNbAttr(); i += 2)
+ {
+ if (::strcmp(attr.getKey(i), "name") == 0)
+ {
+ nameIndex = i;
+ break;
+ }
+ }
+
+ if (::strcmp(attr.getValue(nameIndex), "diffuseTexture") == 0)
+ {
+ mMaterialInfo.diffuseTexture = elementData;
+ }
+ else if (::strcmp(attr.getValue(nameIndex), "normalTexture") == 0)
+ {
+ mMaterialInfo.normalTexture = elementData;
+ }
+ }
+
+ return true;
+ }
+
+ virtual void* fastxml_malloc(unsigned int size)
+ {
+ return ::malloc(size);
+ }
+
+ virtual void fastxml_free(void *mem)
+ {
+ ::free(mem);
+ }
+
+private:
+ bool mIsMaterial;
+ MaterialList::MaterialInfo mMaterialInfo;
+};
+
+
+
+unsigned int MaterialList::addMaterial(const char* directory, const char* prefix, const char* materialName)
+{
+ char filename[256];
+ physx::shdfnd::snprintf(filename, 256, "%s/%s%s", directory, prefix, materialName);
+
+ XmlParser parser((unsigned int)mPaths.size());
+ physx::shdfnd::FastXml* fastXml = physx::shdfnd::createFastXml(&parser);
+ if (fastXml != NULL)
+ {
+ physx::PsFileBuffer fileBuffer(filename, physx::PxFileBuf::OPEN_READ_ONLY);
+ physx::PxInputDataFromPxFileBuf id(fileBuffer);
+ fastXml->processXml(id);
+
+ int errorLineNumber = 0;
+ const char* xmlError = fastXml->getError(errorLineNumber);
+
+ if (xmlError != NULL)
+ {
+ if (strncmp(xmlError, "User aborted", 12) == 0)
+ {
+
+ }
+ else
+ {
+#if PX_WINDOWS_FAMILY
+ char error[512];
+ sprintf_s(error, 512, "%s\n%s", filename, xmlError);
+ ::MessageBox(NULL, error, "MaterialList XML Parse error", MB_OK);
+#endif
+ }
+ }
+
+ fastXml->release();
+ }
+
+ if (parser.isMaterial())
+ {
+ char saneFileName[128];
+ physx::shdfnd::snprintf(saneFileName, 128, "%s%s", prefix, materialName);
+
+ if (mMaterialNames.find(saneFileName) != mMaterialNames.end())
+ {
+ PX_ASSERT(!"Duplicated material name found");
+ }
+ else
+ {
+ mMaterialNames[saneFileName] = parser.getMaterialInfo();
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+
+unsigned int MaterialList::addTexture(const char* /*directory*/, const char* prefix, const char* textureName)
+{
+ char saneFileName[128];
+ physx::shdfnd::snprintf(saneFileName, 128, "%s%s", prefix, textureName);
+
+ if (mTextureNames.find(saneFileName) != mTextureNames.end())
+ {
+ PX_ASSERT(!"duplicated texture found!");
+ }
+ else
+ {
+ TextureInfo info;
+ info.fromPath = (unsigned int)mPaths.size();
+ mTextureNames[saneFileName] = info;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
+} // namespace Samples
diff --git a/APEX_1.4/shared/external/src/MemTracker.cpp b/APEX_1.4/shared/external/src/MemTracker.cpp
new file mode 100644
index 00000000..d3883b86
--- /dev/null
+++ b/APEX_1.4/shared/external/src/MemTracker.cpp
@@ -0,0 +1,1174 @@
+/*
+ * 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 "MemTracker.h"
+
+#if PX_WINDOWS_FAMILY // only compile this source code for windows!
+
+#define USE_HASH_MAP 1
+
+// VC10 hash maps are abominably slow with iterator debug level == 0
+// Note: All STL containers are used internally, so their should not be any resultant conflict
+#if (PX_VC == 10) && USE_HASH_MAP
+#define OVERRIDE_ITERATOR_DEBUG_LEVEL 1
+#else
+#define OVERRIDE_ITERATOR_DEBUG_LEVEL 0
+#endif
+
+#if OVERRIDE_ITERATOR_DEBUG_LEVEL
+#undef _ITERATOR_DEBUG_LEVEL
+#define _ITERATOR_DEBUG_LEVEL 0
+#define _ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH 1
+#endif
+
+#include "htmltable.h"
+#include "PxSimpleTypes.h"
+#include "PxAssert.h"
+#include <stdio.h>
+#include <string>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/timeb.h>
+#include <map>
+#include <vector>
+#include <windows.h>
+
+#define _SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS
+#include <hash_map>
+
+#pragma warning(disable:4100 4996 4267 4239)
+
+namespace MEM_TRACKER
+{
+
+class ContextType
+{
+public:
+ ContextType(void)
+ {
+ mContext = NULL;
+ mType = NULL;
+ mAllocCount = 0;
+ mAllocSize = 0;
+ }
+ ContextType(const char *context,const char *type,size_t size)
+ {
+ mContext = context;
+ mType = type;
+ char scratch[512];
+ sprintf_s(scratch,512,"PlatformAnalyzer/ContextType/%s/%s/Count(AllocCount)", mContext, mType );
+ mAllocCountSpec = scratch;
+ sprintf_s(scratch,512,"PlatformAnalyzer/ContextType/%s/%s/Size(AllocSize)", mContext, mType );
+ mAllocSizeSpec = scratch;
+ mAllocCount = 1;
+ mAllocSize = size;
+ }
+
+ const char *mContext;
+ const char *mType;
+ uint32_t mAllocCount;
+ size_t mAllocSize;
+ std::string mAllocCountSpec;
+ std::string mAllocSizeSpec;
+};
+
+class ByContextIndex
+{
+public:
+ ByContextIndex(const char *context,const char *type)
+ {
+ mContext = context;
+ mType = type;
+ }
+
+ const char *mContext;
+ const char *mType;
+};
+
+class ByContextHasher
+{
+public:
+ static const size_t bucket_size = 4;
+ static const size_t min_buckets = 8;
+
+ size_t operator()(const ByContextIndex &index) const
+ {
+ size_t hash1 = (size_t)index.mContext;
+ size_t hash2 = (size_t)index.mType;
+ size_t hash = hash1 ^ hash2;
+ return hash;
+ }
+
+ bool operator()(const ByContextIndex &s1,const ByContextIndex &s2) const
+ {
+ if ( s1.mContext < s2.mContext ) return true;
+ if ( s1.mContext > s2.mContext ) return false;
+ return s1.mType < s2.mType;
+ }
+};
+
+#if USE_HASH_MAP
+typedef stdext::hash_map< ByContextIndex, ContextType, ByContextHasher > ByContextTypeHash;
+#else
+typedef std::map< ByContextIndex, ContextType, ByContextHasher > ByContextTypeHash;
+#endif
+
+static void getDateTime(char *date_time)
+{
+ time_t ltime;
+ struct tm *today;
+ _tzset();
+ time( &ltime );
+ today = localtime( &ltime );
+ strftime( date_time, 128,"%A-%B-%d-%Y-%I-%M-%S-%p", today );
+}
+
+size_t getPow2(size_t s,size_t &p)
+{
+ size_t ret = 0;
+ while ( s > p )
+ {
+ p = p<<1;
+ ret++;
+ }
+ return ret;
+}
+
+class MemTrack
+{
+public:
+ MemoryType mType;
+ size_t mSize;
+ size_t mThreadId;
+ const char *mContext;
+ const char *mClassName;
+ const char *mFileName;
+ uint32_t mLineNo;
+ size_t mAllocCount;
+};
+
+class ByType
+{
+public:
+ ByType(size_t size)
+ {
+ mSize = size;
+ mCount = 1;
+ }
+ ByType(void) { };
+ size_t mSize;
+ size_t mCount;
+};
+
+class BySource
+{
+public:
+ BySource(void) { };
+
+ BySource(const MemTrack &t)
+ {
+ mClassName = t.mClassName;
+ mFileName = t.mFileName;
+ mLineNo = t.mLineNo;
+ mSize = t.mSize;
+ mLineNo = t.mLineNo;
+ mCount = 0;
+ }
+
+ const char *mClassName;
+ const char *mFileName;
+ size_t mLineNo;
+ size_t mSize;
+ size_t mCount;
+};
+
+#if USE_HASH_MAP
+typedef stdext::hash_map< size_t, ByType > ByTypeHash;
+#else
+typedef std::map< size_t, ByType > ByTypeHash;
+#endif
+
+class BySourceIndex
+{
+public:
+ BySourceIndex(const char *fileName,uint32_t lineno)
+ {
+ mFileName = fileName;
+ mLineNo = lineno;
+ }
+
+ const char *mFileName;
+ uint32_t mLineNo;
+};
+
+class BySourceHasher
+{
+public:
+ static const size_t bucket_size = 4;
+ static const size_t min_buckets = 8;
+
+ size_t operator()(const BySourceIndex &index) const
+ {
+ size_t hash = (size_t)index.mFileName;
+ hash = hash^index.mLineNo;
+ return hash;
+ }
+
+ bool operator()(const BySourceIndex &s1,const BySourceIndex &s2) const
+ {
+ if ( s1.mFileName < s2.mFileName ) return true;
+ if ( s1.mFileName > s2.mFileName ) return false;
+ return s1.mLineNo < s2.mLineNo;
+ }
+};
+
+#if USE_HASH_MAP
+typedef stdext::hash_map< BySourceIndex, BySource, BySourceHasher > BySourceHash;
+typedef stdext::hash_map< size_t , MemTrack > MemTrackHash;
+#else
+typedef std::map< BySourceIndex, BySource, BySourceHasher > BySourceHash;
+typedef std::map< size_t , MemTrack > MemTrackHash;
+#endif
+
+typedef std::vector< MemTrack > MemTrackVector;
+
+class ReportContext
+{
+public:
+ ReportContext(const char *context)
+ {
+ mContext = context;
+ }
+ void add(const MemTrack &t)
+ {
+ assert(t.mAllocCount > 0 );
+ mMemTracks.push_back(t);
+ }
+
+ const char * getContext(void) const { return mContext; };
+
+ // first we generate a report based on type.
+ void generateReport(nvidia::HtmlDocument *document,nvidia::HtmlTable *contextTable)
+ {
+ unsigned int totalMemory = 0;
+ unsigned int totalAllocations = 0;
+ unsigned int typeCount = 0;
+ unsigned int sourceCount = 0;
+
+ {
+
+
+
+ ByTypeHash bt;
+ MemTrackVector::iterator i;
+ for (i=mMemTracks.begin(); i!=mMemTracks.end(); i++)
+ {
+ const MemTrack &t = (*i);
+ size_t hash = (size_t)t.mClassName;
+ ByTypeHash::iterator found = bt.find(hash);
+ if ( found == bt.end() )
+ {
+ ByType b(t.mSize);
+ bt[hash] = b;
+ }
+ else
+ {
+ ByType &b = (ByType &)(*found).second;
+ b.mSize+=t.mSize;
+ b.mCount++;
+ }
+ }
+
+ {
+ typeCount = bt.size();
+
+ char scratch[512];
+ char date_time[512];
+ getDateTime(date_time);
+ sprintf(scratch,"Memory Usage by Class Name or Memory Type for Context: %s : %s", mContext, date_time );
+ nvidia::HtmlTable *table = document->createHtmlTable(scratch);
+ // 1 2 3
+ table->addHeader("Memory/Type,Memory/Size,Allocation/Count");
+ table->addSort("Sorted By Memory Size",2,false,3,false);
+ for (ByTypeHash::iterator iter = bt.begin(); iter !=bt.end(); ++iter)
+ {
+ ByType b = (*iter).second;
+ table->addColumn( (const char *)(*iter).first );
+ table->addColumn( (uint32_t)b.mSize );
+ table->addColumn( (uint32_t)b.mCount );
+ table->nextRow();
+ }
+ table->computeTotals();
+ }
+ }
+
+ {
+
+ BySourceHash bt;
+ MemTrackVector::iterator i;
+ for (i=mMemTracks.begin(); i!=mMemTracks.end(); i++)
+ {
+ const MemTrack &t = (*i);
+ BySource b(t);
+ BySourceIndex index(b.mFileName,b.mLineNo);
+ BySourceHash::iterator found = bt.find(index);
+ if ( found == bt.end() )
+ {
+ b.mCount = 1;
+ bt[index] = b;
+ }
+ else
+ {
+ BySource &bs = (BySource &)(*found).second;
+ bs.mSize+=t.mSize;
+ bs.mCount++;
+ }
+ }
+
+
+ {
+ sourceCount = bt.size();
+
+ char scratch[512];
+ char date_time[512];
+ getDateTime(date_time);
+ sprintf(scratch,"Memory Usage by Source File and Line Number for Context: %s : %s", mContext, date_time );
+ nvidia::HtmlTable *table = document->createHtmlTable(scratch);
+ // 1 2 3 4 5
+ table->addHeader("Source/File,Line/Number,Memory/Type,Memory/Size,Allocation/Count");
+ table->addSort("Sorted By Memory Size",4,false,5,false);
+ table->excludeTotals(2);
+ for (BySourceHash::iterator i=bt.begin(); i!=bt.end(); ++i)
+ {
+ BySource b = (*i).second;
+ table->addColumn( b.mFileName );
+ table->addColumn( (uint32_t)b.mLineNo );
+ table->addColumn( b.mClassName );
+ table->addColumn( (uint32_t)b.mSize );
+ assert( b.mCount > 0 );
+ table->addColumn( (uint32_t)b.mCount );
+ table->nextRow();
+ totalMemory+=b.mSize;
+ totalAllocations+=b.mCount;
+ }
+ table->computeTotals();
+ }
+ }
+
+
+ // Power of two, sizes 1-256
+ {
+ char scratch[512];
+ char date_time[512];
+ getDateTime(date_time);
+ sprintf(scratch,"Power of Two Memory 1 to 256 bytes for Context: %s : %s", mContext, date_time );
+ nvidia::HtmlTable *table = document->createHtmlTable(scratch);
+ // 1 2 3 4
+ table->addHeader("Mem/Size,Total/Alloc,Alloc/Count,Fragment/Amount");
+ table->addSort("Sorted By Memory Size",2,false,3,false);
+ table->excludeTotals(1);
+
+ // 0 1 2 3 4 5
+ // 8, 16, 32, 64, 128, 256
+ struct Power2
+ {
+ Power2(void)
+ {
+ memSize = 0;
+ memTotal = 0;
+ memCount = 0;
+ fragmentTotal = 0;
+ }
+ unsigned int memSize;
+ unsigned int memTotal;
+ unsigned int memCount;
+ unsigned int fragmentTotal;
+ };
+
+ Power2 p[6];
+
+ MemTrackVector::iterator i;
+ for (i=mMemTracks.begin(); i!=mMemTracks.end(); i++)
+ {
+ const MemTrack &t = (*i);
+ if ( t.mSize <= 256 )
+ {
+ size_t p2=8;
+ size_t index = getPow2(t.mSize,p2);
+ p[index].memSize = p2;
+ p[index].memTotal+=t.mSize;
+ p[index].memCount++;
+ p[index].fragmentTotal+=(p2-t.mSize);
+ }
+ }
+ for (size_t i=0; i<6; i++)
+ {
+ Power2 &t = p[i];
+ if ( t.memCount > 0 )
+ {
+ table->addColumn(t.memSize);
+ table->addColumn(t.memTotal);
+ table->addColumn(t.memCount);
+ table->addColumn(t.fragmentTotal);
+ table->nextRow();
+ }
+ }
+ table->computeTotals();
+ }
+
+ // power of two allocations > 256 bytes
+ {
+ char scratch[512];
+ char date_time[512];
+ getDateTime(date_time);
+ sprintf(scratch,"Power of Two Memory Greater than 256 bytes for Context: %s : %s", mContext, date_time );
+ nvidia::HtmlTable *table = document->createHtmlTable(scratch);
+ // 1 2 3 4
+ table->addHeader("Mem/Size,Total/Alloc,Alloc/Count,Fragment/Amount");
+ table->addSort("Sorted By Memory Size",2,false,3,false);
+ table->excludeTotals(1);
+
+ struct Power2
+ {
+ Power2(void)
+ {
+ memSize = 0;
+ memTotal = 0;
+ memCount = 0;
+ fragmentTotal = 0;
+ }
+ unsigned int memSize;
+ unsigned int memTotal;
+ unsigned int memCount;
+ unsigned int fragmentTotal;
+ };
+
+ Power2 p[32];
+
+ MemTrackVector::iterator i;
+ for (i=mMemTracks.begin(); i!=mMemTracks.end(); i++)
+ {
+ const MemTrack &t = (*i);
+ if ( t.mSize > 256 )
+ {
+ size_t p2=512;
+ size_t index = getPow2(t.mSize,p2);
+ p[index].memSize = p2;
+ p[index].memTotal+=t.mSize;
+ p[index].memCount++;
+ p[index].fragmentTotal+=(p2-t.mSize);
+ }
+ }
+ for (size_t i=0; i<32; i++)
+ {
+ Power2 &t = p[i];
+ if ( t.memCount > 0 )
+ {
+ table->addColumn(t.memSize);
+ table->addColumn(t.memTotal);
+ table->addColumn(t.memCount);
+ table->addColumn(t.fragmentTotal);
+ table->nextRow();
+ }
+ }
+ table->computeTotals();
+ }
+
+
+ // context summary..
+ if ( contextTable )
+ {
+ contextTable->addColumn( mContext );
+ contextTable->addColumn( totalMemory );
+ contextTable->addColumn( totalAllocations );
+ contextTable->addColumn( typeCount );
+ contextTable->addColumn(sourceCount );
+ contextTable->nextRow();
+ }
+
+ }
+
+private:
+ const char *mContext;
+ MemTrackVector mMemTracks;
+};
+
+#if USE_HASH_MAP
+typedef stdext::hash_map< size_t , ReportContext * > ReportContextHash;
+#else
+typedef std::map< size_t , ReportContext * > ReportContextHash;
+#endif
+
+class MyMemTracker : public MemTracker
+{
+public:
+
+ MyMemTracker(void)
+ {
+ mSingleThreaded = false;
+ mFrameNo = 1;
+ mAllocCount = 0;
+ mAllocSize = 0;
+ mAllocFrameCount = 0;
+ mFreeFrameCount = 0;
+ mAllocFrameSize = 0;
+ mFreeFrameSize = 0;
+ mDocument = 0;
+ mFrameSummary = 0;
+ mDetailed = 0;
+ // nvidia::HtmlTableInterface *h = nvidia::getHtmlTableInterface();
+ // if ( h )
+ // {
+ //mDocument = h->createHtmlDocument("MemTracker");
+ // }
+
+ }
+
+ virtual ~MyMemTracker(void)
+ {
+ if ( mDocument )
+ {
+ nvidia::HtmlTableInterface *h = nvidia::getHtmlTableInterface();
+ h->releaseHtmlDocument(mDocument);
+ }
+ }
+
+ virtual void trackAlloc(size_t threadId,void *mem,size_t size,MemoryType type,const char *context,const char *className,const char *fileName,uint32_t lineno)
+ {
+ if ( mem )
+ {
+ addContextType(context,className,size);
+
+ mAllocCount++;
+ mAllocSize+=size;
+
+ mAllocFrameCount++;
+ mAllocFrameSize+=size;
+
+ size_t hash = (size_t) mem;
+ MemTrack t;
+ t.mType = type;
+ t.mSize = size;
+ t.mThreadId = threadId;
+ t.mContext = context;
+ t.mClassName = className;
+ t.mFileName = fileName;
+ t.mLineNo = lineno;
+ t.mAllocCount = 1;
+ MemTrackHash::iterator found = mMemory.find(hash);
+ if ( found != mMemory.end() )
+ {
+ PX_ALWAYS_ASSERT();
+ const MemTrack &previous = (*found).second;
+ printf("Prev: %s\r\n", previous.mClassName );
+ PX_UNUSED(previous);
+ }
+ // track which allocation number this one was.
+ {
+ BySource b(t);
+ b.mCount = 1;
+ BySourceIndex index(b.mFileName,b.mLineNo);
+ BySourceHash::iterator found = mSourceHash.find(index);
+ if ( found == mSourceHash.end() )
+ {
+ mSourceHash[index] = b;
+ }
+ else
+ {
+ BySource bs = (*found).second;
+ bs.mCount++;
+ t.mAllocCount = bs.mCount;
+ mSourceHash[index] = bs;
+ }
+ }
+ if ( mDetailed )
+ {
+ mDetailed->addColumnHex( (size_t)mem );
+ switch ( type )
+ {
+ case MT_NEW:
+ mDetailed->addColumn("NEW");
+ break;
+ case MT_NEW_ARRAY:
+ mDetailed->addColumn("NEW_ARRAY");
+ break;
+ case MT_MALLOC:
+ mDetailed->addColumn("MALLOC");
+ break;
+ case MT_GLOBAL_NEW:
+ mDetailed->addColumn("GLOBAL_NEW");
+ break;
+ case MT_GLOBAL_NEW_ARRAY:
+ mDetailed->addColumn("GLOBAL_NEW_ARRAY");
+ break;
+ default:
+ PX_ALWAYS_ASSERT();
+ mDetailed->addColumn("ERROR");
+ break;
+ }
+ mDetailed->addColumn((uint32_t)size);
+ mDetailed->addColumn((uint32_t)t.mAllocCount);
+ mDetailed->addColumnHex(threadId);
+ mDetailed->addColumn(context);
+ mDetailed->addColumn(className);
+ mDetailed->addColumn(fileName);
+ mDetailed->addColumn((uint32_t)lineno);
+ mDetailed->nextRow();
+ }
+
+ mMemory[hash] = t;
+
+ PX_ASSERT( mAllocCount == mMemory.size() );
+ }
+ }
+
+ virtual void trackRealloc(size_t threadId,
+ void *oldMem,
+ void *newMem,
+ size_t newSize,
+ const char *context,
+ const char *className,
+ const char *fileName,
+ uint32_t lineno)
+ {
+ TrackInfo info;
+ bool found = trackInfo(oldMem,info);
+ PX_ASSERT(found);
+ if ( found )
+ {
+ PX_ASSERT( info.mType == MT_MALLOC );
+ trackFree(threadId,oldMem,MT_FREE,context,fileName,lineno);
+ trackAlloc(threadId,newMem,newSize,info.mType,context,className,fileName,lineno);
+ }
+ }
+
+ virtual const char * typeString(MemoryType type)
+ {
+ const char *ret = "unknown";
+ switch ( type )
+ {
+ case MT_NEW:
+ ret = "new operator";
+ break;
+ case MT_NEW_ARRAY:
+ ret = "new[] array operator";
+ break;
+ case MT_MALLOC:
+ ret = "malloc";
+ break;
+ case MT_FREE:
+ ret = "free";
+ break;
+ case MT_DELETE:
+ ret = "delete operator";
+ break;
+ case MT_DELETE_ARRAY:
+ ret = "delete[] array operator";
+ break;
+ case MT_GLOBAL_NEW:
+ ret = "global new";
+ break;
+ case MT_GLOBAL_NEW_ARRAY:
+ ret = "global new[] array";
+ break;
+ case MT_GLOBAL_DELETE:
+ ret = "global delete";
+ break;
+ case MT_GLOBAL_DELETE_ARRAY:
+ ret = "global delete array";
+ break;
+ }
+ return ret;
+ }
+
+
+ virtual const char * trackValidateFree(size_t threadId,void *mem,MemoryType type,const char *context,const char *fileName,uint32_t lineno)
+ {
+ const char *ret = NULL;
+ if ( mem )
+ {
+ char scratch[1024];
+ scratch[0] = 0;
+
+ size_t hash = (size_t) mem;
+ MemTrackHash::iterator found = mMemory.find(hash);
+ if ( found == mMemory.end() )
+ {
+ sprintf_s(scratch,1024,"Error! Tried to free memory never tracked. Source: %s : Line: %d\r\n", fileName, lineno);
+ PX_ALWAYS_ASSERT();
+ }
+ else
+ {
+ MemTrack &t = (MemTrack &)(*found).second;
+
+ switch ( type )
+ {
+ case MT_DELETE:
+ if ( t.mType != MT_NEW )
+ {
+ sprintf_s(scratch,1024,"Error: Allocated with %s but deallocated with the delete operator\r\n", typeString(t.mType));
+ }
+ break;
+ case MT_DELETE_ARRAY:
+ if ( t.mType != MT_NEW_ARRAY )
+ {
+ sprintf_s(scratch,1024,"Error: Allocated with %s but deallocated with the delete array operator.\r\n", typeString(t.mType));
+ }
+ break;
+ case MT_FREE:
+ if ( t.mType != MT_MALLOC )
+ {
+ sprintf_s(scratch,1024,"Error: Allocated with %s but deallocated with malloc.\r\n", typeString(t.mType));
+ }
+ break;
+ case MT_GLOBAL_DELETE:
+ if ( t.mType != MT_GLOBAL_NEW )
+ {
+ sprintf_s(scratch,1024,"Error: Allocated with %s but deallocated with global delete.\r\n", typeString(t.mType));
+ }
+ break;
+ case MT_GLOBAL_DELETE_ARRAY:
+ if ( t.mType != MT_GLOBAL_NEW_ARRAY )
+ {
+ sprintf_s(scratch,1024,"Error: Allocated with %s but deallocated with global delete array.\r\n", typeString(t.mType));
+ }
+ break;
+ default:
+ sprintf_s(scratch,1024,"Invalid memory type encountered. Data corrupted!?\r\n");
+ break;
+ }
+ if ( t.mThreadId != threadId && mSingleThreaded )
+ {
+ sprintf_s(scratch,1024,"Memory de-allocated from a different thread than it was allocated from!\r\n");
+ }
+ if ( !context )
+ {
+ sprintf_s(scratch,1024,"Null context!\r\n");
+ }
+ if ( !t.mContext )
+ {
+ sprintf_s(scratch,1024,"Original memory block allocated with a null context, or the data is corrupted!\r\n");
+ }
+ if ( context != t.mContext )
+ {
+ sprintf_s(scratch,1024,"Memory is de-allocated from a different context. Allocated from (%s) deallocated from (%s)\r\n", context, t.mContext );
+ }
+ }
+ if ( scratch[0] )
+ {
+ mErrorMessage = scratch;
+ ret = mErrorMessage.c_str();
+ }
+ }
+ return ret;
+ }
+
+ void addContextType(const char *context,const char *type,size_t size)
+ {
+ ByContextIndex index(context,type);
+ ByContextTypeHash::iterator found = mContextType.find(index);
+ if ( found != mContextType.end() )
+ {
+ ContextType &c = (ContextType &)(*found).second;
+ PX_ASSERT( c.mContext == context );
+ PX_ASSERT( c.mType == type );
+ c.mAllocCount++;
+ c.mAllocSize+=size;
+ }
+ else
+ {
+ ContextType c(context,type,size);
+ mContextType[index] = c;
+ }
+#if 0
+ int foundCount=0;
+ {
+ for (ByContextTypeHash::iterator i=mContextType.begin(); i!=mContextType.end(); ++i)
+ {
+ const ByContextIndex &index = (*i).first;
+ if ( index.mContext == context && index.mType == type )
+ {
+ foundCount++;
+ }
+ }
+ }
+ PX_ASSERT( foundCount == 1 );
+#endif
+ }
+
+ void removeContextType(const char *context,const char *type,size_t size)
+ {
+ ByContextIndex index(context,type);
+ ByContextTypeHash::iterator found = mContextType.find(index);
+ if ( found != mContextType.end() )
+ {
+ ContextType &c = (ContextType &)(*found).second;
+ c.mAllocCount--;
+ c.mAllocSize-=size;
+ }
+ else
+ {
+ PX_ALWAYS_ASSERT();
+ }
+
+
+ }
+
+
+ virtual void trackFree(size_t threadId,void *mem,MemoryType type,const char *context,const char *fileName,uint32_t lineno)
+ {
+ if ( mem )
+ {
+ size_t hash = (size_t) mem;
+ MemTrackHash::iterator found = mMemory.find(hash);
+ if ( found == mMemory.end() )
+ {
+ PX_ALWAYS_ASSERT();
+ }
+ else
+ {
+ const MemTrack &t = (*found).second;
+ removeContextType(t.mContext,t.mClassName,t.mSize);
+ if ( mDetailed )
+ {
+ mDetailed->addColumnHex( (size_t) mem );
+ switch ( type )
+ {
+ case MT_FREE:
+ mDetailed->addColumn("FREE");
+ break;
+ case MT_DELETE:
+ mDetailed->addColumn("DELETE");
+ break;
+ case MT_DELETE_ARRAY:
+ mDetailed->addColumn("DELETE_ARRAY");
+ break;
+ case MT_GLOBAL_DELETE:
+ mDetailed->addColumn("GLOBAL_DELETE");
+ break;
+ case MT_GLOBAL_DELETE_ARRAY:
+ mDetailed->addColumn("GLOBAL_DELETE_ARRAY");
+ break;
+ default:
+ printf("Invalid memory allocation type! %s : %d\r\n", fileName, lineno );
+ mDetailed->addColumn("INVALID ALLOC TYPE");
+ break;
+ }
+ mDetailed->addColumn((uint32_t)t.mSize);
+ mDetailed->addColumn((uint32_t)t.mAllocCount);
+ mDetailed->addColumnHex(threadId);
+ mDetailed->addColumn(context);
+ mDetailed->addColumn(t.mClassName);
+ mDetailed->addColumn(fileName);
+ mDetailed->addColumn((uint32_t)lineno);
+ mDetailed->nextRow();
+ }
+
+ mAllocCount--;
+ mAllocSize-=t.mSize;
+
+ mFreeFrameCount++;
+ mFreeFrameSize+=t.mSize;
+
+ mMemory.erase(hash);
+
+ PX_ASSERT( mAllocCount == mMemory.size() );
+ }
+ }
+ }
+
+ void trackFrame(void)
+ {
+ mFrameNo++;
+ if ( mAllocFrameCount || mFreeFrameCount )
+ {
+ if ( mFrameSummary )
+ {
+ mFrameSummary->addColumn((uint32_t)mFrameNo);
+ mFrameSummary->addColumn((uint32_t)mAllocCount);
+ mFrameSummary->addColumn((uint32_t)mAllocSize);
+ mFrameSummary->addColumn((uint32_t)mAllocFrameCount);
+ mFrameSummary->addColumn((uint32_t)mAllocFrameSize);
+ mFrameSummary->addColumn((uint32_t)mFreeFrameCount);
+ mFrameSummary->addColumn((uint32_t)mFreeFrameSize);
+ mFrameSummary->addColumn((uint32_t)(mAllocFrameCount-mFreeFrameCount));
+ mFrameSummary->addColumn((uint32_t)(mAllocFrameSize-mFreeFrameSize));
+ mFrameSummary->nextRow();
+ }
+
+ mAllocFrameCount = 0;
+ mAllocFrameSize = 0;
+ mFreeFrameCount = 0;
+ mFreeFrameSize = 0;
+
+ if ( mDetailed )
+ {
+ char scratch[512];
+ sprintf(scratch,"New Frame %d", (int)mFrameNo );
+ mDetailed->addColumn(scratch);
+ mDetailed->addColumn((uint32_t)mAllocSize);
+ mDetailed->nextRow();
+ }
+
+
+ }
+ }
+
+ virtual void releaseReportMemory(void *mem)
+ {
+ ::free(mem);
+ }
+
+ void *generateReport(MemoryReportFormat format,const char *fname,uint32_t &saveLen,bool reportAllLeaks)
+ {
+ void *ret = NULL;
+ saveLen = 0;
+
+ //
+ nvidia::HtmlTable *contextTable = NULL;
+ {
+ char scratch[512];
+ char date_time[512];
+ getDateTime(date_time);
+ sprintf(scratch,"Summary Report for All Contexts : %s", date_time);
+ contextTable = mDocument->createHtmlTable(scratch);
+ // 1 2 3
+ contextTable->addHeader("Memory/Context,Total/Memory,Alloc/Count,Type/Count,Source/Count");
+ contextTable->addSort("Sorted By Total Memory",2,true,3,true);
+
+ }
+ ReportContextHash rchash;
+ {
+ ReportContext *current = 0;
+
+ for (MemTrackHash::iterator i=mMemory.begin(); i!=mMemory.end(); ++i)
+ {
+ MemTrack t = (*i).second;
+ if ( (current == 0) || current->getContext() != t.mContext )
+ {
+ size_t hash = (size_t)t.mContext;
+ ReportContextHash::iterator found = rchash.find(hash);
+ if ( found == rchash.end() )
+ {
+ current = new ReportContext(t.mContext);
+ rchash[hash] = current;
+ }
+ else
+ {
+ current = (*found).second;
+ }
+ }
+ current->add(t);
+ }
+ //
+ if ( reportAllLeaks )
+ {
+ char scratch[512];
+ char date_time[512];
+ getDateTime(date_time);
+ sprintf(scratch,"Memory Leaks in order of allocation : %s", date_time);
+
+ nvidia::HtmlTable *table = mDocument->createHtmlTable(scratch);
+ // 1 2 3 4 5 6 7 8
+ table->addHeader("Memory/Address,Alloc/Count,Alloc/Size,ThreadId,Context,Class/Type,Source/File,Lineno");
+ table->addSort("Sorted By Source File and Line Number",7,true,8,true);
+
+ table->excludeTotals(2);
+ table->excludeTotals(8);
+
+ for (MemTrackHash::iterator i=mMemory.begin(); i!=mMemory.end(); ++i)
+ {
+ MemTrack t = (*i).second;
+ table->addColumnHex( (size_t)(*i).first ); // memory address
+ table->addColumn((uint32_t) t.mAllocCount ); // allocation count.
+ table->addColumn((uint32_t) t.mSize ); // size of allocation.
+ table->addColumnHex( t.mThreadId ); // thread id
+ table->addColumn( t.mContext ); // context
+ table->addColumn( t.mClassName ); //
+ table->addColumn( t.mFileName );
+ table->addColumn((uint32_t) t.mLineNo );
+ table->nextRow();
+ }
+ table->computeTotals();
+ }
+ }
+ //
+ {
+
+ for (ReportContextHash::iterator i=rchash.begin(); i!=rchash.end(); ++i)
+ {
+ ReportContext *rc = (*i).second;
+ rc->generateReport(mDocument,contextTable);
+ delete rc;
+ }
+ }
+ //
+ if ( mFrameSummary )
+ {
+ mFrameSummary->excludeTotals(1);
+ mFrameSummary->excludeTotals(2);
+ mFrameSummary->excludeTotals(3);
+ mFrameSummary->excludeTotals(8);
+ mFrameSummary->excludeTotals(9);
+ mFrameSummary->computeTotals();
+ }
+
+ contextTable->computeTotals();
+
+ nvidia::HtmlSaveType saveType = nvidia::HST_SIMPLE_HTML;
+ switch ( format )
+ {
+ case MRF_SIMPLE_HTML: // just a very simple HTML document containing the tables.
+ saveType = nvidia::HST_SIMPLE_HTML;
+ break;
+ case MRF_CSV: // Saves the Tables out as comma seperated value text
+ saveType = nvidia::HST_CSV;
+ break;
+ case MRF_TEXT: // Saves the tables out in human readable text format.
+ saveType = nvidia::HST_TEXT;
+ break;
+ case MRF_TEXT_EXTENDED: // Saves the tables out in human readable text format, but uses the MS-DOS style extended ASCII character set for the borders.
+ saveType = nvidia::HST_TEXT_EXTENDED;
+ break;
+ }
+
+ size_t len;
+ const char *data = mDocument->saveDocument(len,saveType);
+ if ( data )
+ {
+ ret = ::malloc(len);
+ memcpy(ret,data,len);
+ saveLen = len;
+ mDocument->releaseDocumentMemory(data);
+ }
+ return ret;
+ }
+
+
+ virtual void usage(void)
+ {
+ printf("On Frame Number: %d has performed %d memory allocations for a total %d bytes of memory.\r\n", (int)mFrameNo, (int)mAllocCount, (int)mAllocSize );
+ }
+
+
+ virtual size_t detectLeaks(size_t &acount)
+ {
+ acount = mAllocCount;
+ return mAllocSize;
+ }
+
+
+ virtual void setLogLevel(bool logEveryAllocation,bool logEveryFrame,bool verifySingleThreaded)
+ {
+ mSingleThreaded = verifySingleThreaded;
+ mLogEveryAllocation = logEveryAllocation;
+ mLogEveryFrame = logEveryFrame;
+ if ( mDocument )
+ {
+ if ( mLogEveryFrame && mFrameSummary == 0 )
+ {
+ char date_time[512];
+ getDateTime(date_time);
+ char scratch[1024];
+ sprintf(scratch,"Per Frame Memory Usage Summary : %s ", date_time);
+
+ mFrameSummary = mDocument->createHtmlTable(scratch);
+ // 1 2 3 4 5 6 7 8 9
+ mFrameSummary->addHeader("Frame/Number,Total/Alloc Count,Total/Alloc Mem,Frame/Alloc Count,Frame/Alloc Mem,Frame/Free Count,Frame/Free Mem,Frame/Delta Count,Frame/Deleta Mem");
+ }
+ if ( mLogEveryAllocation && mDetailed == 0 )
+ {
+ char date_time[512];
+ getDateTime(date_time);
+ char scratch[2048];
+ sprintf(scratch,"Detailed Memory Usage Report : %s ",date_time);
+ mDetailed = mDocument->createHtmlTable(scratch);
+ // 1 2 3 4 5 6 7 8
+ mDetailed->addHeader("Memory,Event,Size,Alloc Count,ThreadId,Context,Class or Type,Source File,Line Number");
+ mDetailed->setOrder(1000); // make sure it displays this last!
+ }
+ }
+ }
+
+
+ virtual bool trackInfo(const void *mem,TrackInfo &info)
+ {
+ bool ret = false;
+
+ if ( mem )
+ {
+ size_t hash = (size_t) mem;
+ MemTrackHash::iterator found = mMemory.find(hash);
+ if ( found == mMemory.end() )
+ {
+ printf("Error! Tried to get information for memory never tracked.\r\n");
+ }
+ else
+ {
+ MemTrack &t = (MemTrack &)(*found).second;
+ info.mMemory = mem;
+ info.mType = t.mType;
+ info.mSize = t.mSize;
+ info.mContext = t.mContext;
+ info.mClassName = t.mClassName;
+ info.mFileName = t.mFileName;
+ info.mLineNo = t.mLineNo;
+ info.mAllocCount = t.mAllocCount;
+ ret = true;
+ }
+ }
+ return ret;
+ }
+
+
+private:
+
+ bool mLogEveryAllocation;
+ bool mLogEveryFrame;
+
+ size_t mFrameNo;
+ MemTrackHash mMemory;
+
+ size_t mAllocFrameCount;
+ size_t mFreeFrameCount;
+
+ size_t mAllocFrameSize;
+ size_t mFreeFrameSize;
+
+ size_t mAllocCount;
+ size_t mAllocSize;
+ bool mSingleThreaded;
+
+ nvidia::HtmlTable *mFrameSummary;
+ nvidia::HtmlTable *mDetailed;
+ nvidia::HtmlDocument *mDocument;
+ BySourceHash mSourceHash;
+ ByContextTypeHash mContextType;
+
+ std::string mErrorMessage;
+};
+
+MemTracker *createMemTracker(void)
+{
+ MyMemTracker *m = new MyMemTracker;
+ return static_cast< MemTracker *>(m);
+}
+
+void releaseMemTracker(MemTracker *mt)
+{
+ MyMemTracker *m = static_cast< MyMemTracker *>(mt);
+ delete m;
+}
+};
+
+#endif \ No newline at end of file
diff --git a/APEX_1.4/shared/external/src/MeshPainter.cpp b/APEX_1.4/shared/external/src/MeshPainter.cpp
new file mode 100644
index 00000000..c4e189f7
--- /dev/null
+++ b/APEX_1.4/shared/external/src/MeshPainter.cpp
@@ -0,0 +1,1500 @@
+/*
+ * 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 "MeshPainter.h"
+
+#if PX_WINDOWS_FAMILY
+#include <PxMat44.h>
+#include <PsMathUtils.h>
+#include <clothing/ClothingPhysicalMesh.h>
+#include <RenderDebugInterface.h>
+
+#include <algorithm>
+#include <assert.h>
+
+namespace SharedTools
+{
+struct TetraFace
+{
+ void set(int _v0, int _v1, int _v2)
+ {
+ v0 = _v0;
+ v1 = _v1;
+ v2 = _v2;
+ flipped = false;
+ if (v0 > v1)
+ {
+ int v = v0;
+ v0 = v1;
+ v1 = v;
+ flipped = !flipped;
+ }
+ if (v1 > v2)
+ {
+ int v = v1;
+ v1 = v2;
+ v2 = v;
+ flipped = !flipped;
+ }
+ if (v0 > v1)
+ {
+ int v = v0;
+ v0 = v1;
+ v1 = v;
+ flipped = !flipped;
+ }
+ }
+ bool operator==(const TetraFace& f) const
+ {
+ return v0 == f.v0 && v1 == f.v1 && v2 == f.v2;
+ }
+ bool operator < (const TetraFace& f) const
+ {
+ if (v0 < f.v0)
+ {
+ return true;
+ }
+ if (v0 > f.v0)
+ {
+ return false;
+ }
+ if (v1 < f.v1)
+ {
+ return true;
+ }
+ if (v1 > f.v1)
+ {
+ return false;
+ }
+ return v2 < f.v2;
+ }
+ int v0, v1, v2;
+ bool flipped;
+};
+
+struct Edge
+{
+ void set(int v0, int v1, int triNr, int faceNr)
+ {
+ if (v0 < v1)
+ {
+ this->v0 = v0;
+ this->v1 = v1;
+ }
+ else
+ {
+ this->v0 = v1;
+ this->v1 = v0;
+ }
+ this->triNr = triNr;
+ this->faceNr = faceNr;
+ }
+ bool operator==(const Edge& f) const
+ {
+ return v0 == f.v0 && v1 == f.v1;
+ }
+ bool operator < (const Edge& f) const
+ {
+ if (v0 < f.v0)
+ {
+ return true;
+ }
+ if (v0 > f.v0)
+ {
+ return false;
+ }
+ return v1 < f.v1;
+ }
+ int v0, v1;
+ int triNr;
+ int faceNr;
+};
+
+
+MeshPainter::MeshPainter()
+{
+ clear();
+}
+
+
+
+MeshPainter::~MeshPainter()
+{
+ clear();
+}
+
+
+
+void MeshPainter::clear()
+{
+ mCurrentMark = 0;
+ mTriMarks.clear();
+ mVertices.clear();
+ mIndices.clear();
+ mIndexRanges.clear();
+ mNeighbors.clear();
+ mNormals.clear();
+ mTetraNormals.clear();
+ mCollectedTriangles.clear();
+
+ for (unsigned i = 0; i < mFloatBuffers.size(); i++)
+ {
+ if (mFloatBuffers[i].buffer != NULL && mFloatBuffers[i].allocated)
+ {
+ delete mFloatBuffers[i].buffer;
+ }
+ }
+ mFloatBuffers.clear();
+
+ mFlagBuffers.clear();
+
+ mPaintRadius = 0.0f;
+ mBrushMode = 0;
+ mFalloffExponent = 1.0f;
+ mTargetValue = 0.8f;
+ mScaledTargetValue = 0.8f;
+
+ mLastTriangle = -1;
+ mRayOrig = physx::PxVec3(0.0f);
+ mRayDir = physx::PxVec3(0.0f);
+ mLastRaycastNormal = physx::PxVec3(0.0f);
+}
+
+
+
+PaintFloatBuffer& MeshPainter::getInternalFloatBuffer(unsigned int id)
+{
+ for (unsigned i = 0; i < mFloatBuffers.size(); i++)
+ if (mFloatBuffers[i].id == id)
+ {
+ return mFloatBuffers[i];
+ }
+
+ mFloatBuffers.resize(mFloatBuffers.size() + 1);
+ return mFloatBuffers[mFloatBuffers.size() - 1];
+}
+
+
+
+PaintFlagBuffer& MeshPainter::getInternalFlagBuffer(unsigned int id)
+{
+ for (unsigned i = 0; i < mFlagBuffers.size(); i++)
+ if (mFlagBuffers[i].id == id)
+ {
+ return mFlagBuffers[i];
+ }
+
+ mFlagBuffers.resize(mFlagBuffers.size() + 1);
+ return mFlagBuffers[mFlagBuffers.size() - 1];
+}
+
+
+
+/*void MeshPainter::allocateFloatBuffer(uint32_t id)
+{
+ PaintFloatBuffer& fb = getInternalFloatBuffer(id);
+ fb.id = id;
+ if (fb.buffer) delete fb.buffer;
+ fb.buffer = (float*)malloc(mVertices.size() * sizeof(float));
+ fb.stride = sizeof(float);
+ fb.allocated = true;
+}*/
+
+
+
+void MeshPainter::clearIndexBufferRange()
+{
+ mIndexRanges.clear();
+}
+
+
+
+void MeshPainter::addIndexBufferRange(uint32_t start, uint32_t end)
+{
+ PX_ASSERT(start < mIndices.size());
+ PX_ASSERT(end <= mIndices.size());
+ PX_ASSERT((end - start) % 3 == 0);
+
+ IndexBufferRange range;
+ range.start = start;
+ range.end = end;
+
+ for (uint32_t i = 0; i < mIndexRanges.size(); i++)
+ {
+ PX_ASSERT(!mIndexRanges[i].isOverlapping(range));
+ if (mIndexRanges[i].isOverlapping(range))
+ {
+ return; // do not add overlapping ranges!
+ }
+ }
+
+ mIndexRanges.push_back(range);
+
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ mVerticesDisabled[i] = true;
+ }
+
+ for (uint32_t r = 0; r < mIndexRanges.size(); r++)
+ {
+ IndexBufferRange range = mIndexRanges[r];
+ for (uint32_t i = range.start; i < range.end; i++)
+ {
+ mVerticesDisabled[mIndices[i]] = false;
+ }
+ }
+}
+
+
+
+void MeshPainter::setFloatBuffer(unsigned int id, float* buffer, int stride)
+{
+ PaintFloatBuffer& fb = getInternalFloatBuffer(id);
+ fb.id = id;
+ fb.buffer = buffer;
+ fb.stride = stride;
+ fb.allocated = false;
+}
+
+
+
+void MeshPainter::setFlagBuffer(unsigned int id, unsigned int* buffer, int stride)
+{
+ PaintFlagBuffer& fb = getInternalFlagBuffer(id);
+ fb.id = id;
+ fb.buffer = buffer;
+ fb.stride = stride;
+}
+
+
+
+void* MeshPainter::getFloatBuffer(uint32_t id)
+{
+ for (unsigned i = 0; i < mFloatBuffers.size(); i++)
+ if (mFloatBuffers[i].id == id)
+ {
+ return mFloatBuffers[i].buffer;
+ }
+ return NULL;
+}
+
+
+
+void MeshPainter::initFrom(const nvidia::apex::ClothingPhysicalMesh* mesh)
+{
+ clear();
+
+ if (mesh->isTetrahedralMesh())
+ {
+ static const int faceIndices[4][3] = {{1, 2, 3}, {2, 0, 3}, {0, 1, 3}, {2, 1, 0}};
+
+ unsigned numVerts = mesh->getNumVertices();
+ mVertices.resize(numVerts);
+ mVerticesDisabled.resize(numVerts, false);
+ mesh->getVertices(&mVertices[0], sizeof(physx::PxVec3));
+ mTetraNormals.resize(numVerts);
+ mesh->getNormals(&mTetraNormals[0], sizeof(physx::PxVec3));
+
+ // extract surface triangle mesh
+ std::vector<uint32_t> tetIndices;
+ unsigned numIndices = mesh->getNumIndices();
+ tetIndices.resize(numIndices);
+ mesh->getIndices(&tetIndices[0], sizeof(uint32_t));
+
+ std::vector<TetraFace> faces;
+ TetraFace face;
+
+ for (unsigned i = 0; i < numIndices; i += 4)
+ {
+ for (unsigned j = 0; j < 4; j++)
+ {
+ face.set(
+ (int)tetIndices[i + (unsigned)faceIndices[j][0]],
+ (int)tetIndices[i + (unsigned)faceIndices[j][1]],
+ (int)tetIndices[i + (unsigned)faceIndices[j][2]]
+ );
+ faces.push_back(face);
+ }
+ }
+
+ std::sort(faces.begin(), faces.end());
+
+ uint32_t numFaces = (uint32_t)faces.size();
+ uint32_t i = 0;
+
+ while (i < numFaces)
+ {
+ TetraFace& f = faces[i];
+ i++;
+ if (i < numFaces && !(faces[i] == f))
+ {
+ if (f.flipped)
+ {
+ mIndices.push_back((uint32_t)f.v0);
+ mIndices.push_back((uint32_t)f.v2);
+ mIndices.push_back((uint32_t)f.v1);
+ }
+ else
+ {
+ mIndices.push_back((uint32_t)f.v0);
+ mIndices.push_back((uint32_t)f.v1);
+ mIndices.push_back((uint32_t)f.v2);
+ }
+ }
+ else
+ {
+ while (i < numFaces && faces[i] == f)
+ {
+ i++;
+ }
+ }
+ }
+ }
+ else
+ {
+ unsigned numVerts = mesh->getNumVertices();
+ mVertices.resize(numVerts);
+ mVerticesDisabled.resize(numVerts, false);
+ mesh->getVertices(&mVertices[0], sizeof(physx::PxVec3));
+
+ unsigned numIndices = mesh->getNumIndices();
+ mIndices.resize(numIndices);
+ mesh->getIndices(&mIndices[0], sizeof(uint32_t));
+ }
+
+ clearIndexBufferRange();
+ addIndexBufferRange(0, (uint32_t)mIndices.size());
+ complete();
+}
+
+
+
+void MeshPainter::initFrom(const physx::PxVec3* vertices, int numVertices, int vertexStride, const uint32_t* indices, int numIndices, int indexStride)
+{
+ clear();
+
+ mVertices.resize((unsigned)numVertices);
+ mVerticesDisabled.resize((unsigned)numVertices, false);
+ const uint8_t* p = (uint8_t*)vertices;
+ for (int i = 0; i < numVertices; i++, p += vertexStride)
+ {
+ mVertices[(unsigned)i] = *((physx::PxVec3*)p);
+ }
+
+ mIndices.resize((unsigned)numIndices);
+ p = (uint8_t*)indices;
+ for (int i = 0; i < numIndices; i++, p += indexStride)
+ {
+ mIndices[(unsigned)i] = *((uint32_t*)p);
+ }
+
+ clearIndexBufferRange();
+ addIndexBufferRange(0, (uint32_t)mIndices.size());
+ complete();
+}
+
+
+
+void MeshPainter::complete()
+{
+ computeNormals();
+ createNeighborInfo();
+ mTriMarks.resize(mIndices.size() / 3, 0);
+ computeSiblingInfo(0.0f);
+}
+
+
+
+void MeshPainter::computeNormals()
+{
+ const uint32_t numVerts = (uint32_t)mVertices.size();
+ mNormals.resize(numVerts);
+ memset(&mNormals[0], 0, sizeof(physx::PxVec3) * numVerts);
+
+ for (uint32_t i = 0; i < mIndices.size(); i += 3)
+ {
+ const uint32_t i0 = mIndices[i + 0];
+ const uint32_t i1 = mIndices[i + 1];
+ const uint32_t i2 = mIndices[i + 2];
+ physx::PxVec3 n = (mVertices[i1] - mVertices[i0]).cross(mVertices[i2] - mVertices[i0]);
+ mNormals[i0] += n;
+ mNormals[i1] += n;
+ mNormals[i2] += n;
+ }
+
+ for (uint32_t i = 0; i < numVerts; i++)
+ {
+ mNormals[i].normalize();
+ }
+}
+
+
+
+void MeshPainter::createNeighborInfo()
+{
+ mNeighbors.clear();
+ mNeighbors.resize(mIndices.size(), -1);
+
+ std::vector<Edge> edges;
+ Edge edge;
+ for (uint32_t i = 0; i < mIndices.size(); i += 3)
+ {
+ for (uint32_t j = 0; j < 3; j++)
+ {
+ edge.set((int32_t)mIndices[i + j], (int32_t)mIndices[i + (j + 1) % 3], (int32_t)i / 3, (int32_t)j);
+ edges.push_back(edge);
+ }
+ }
+
+ std::sort(edges.begin(), edges.end());
+
+ uint32_t numEdges = (uint32_t)edges.size();
+ uint32_t i = 0;
+ while (i < numEdges)
+ {
+ Edge& e0 = edges[i];
+ i++;
+ if (i < numEdges && edges[i] == e0)
+ {
+ Edge& e1 = edges[i];
+ mNeighbors[uint32_t(3 * e0.triNr + e0.faceNr)] = e1.triNr;
+ mNeighbors[uint32_t(3 * e1.triNr + e1.faceNr)] = e0.triNr;
+ }
+ while (i < numEdges && edges[i] == e0)
+ {
+ i++;
+ }
+ }
+}
+
+
+
+bool MeshPainter::rayCast(int& triNr, float& t) const
+{
+ t = PX_MAX_F32;
+ triNr = -1;
+ mLastRaycastNormal = physx::PxVec3(0.0f);
+ mLastRaycastPos = physx::PxVec3(0.0f);
+
+ if (mRayDir.isZero())
+ {
+ return false;
+ }
+
+ for (uint32_t r = 0; r < mIndexRanges.size(); r++)
+ {
+ const IndexBufferRange range = mIndexRanges[r];
+ for (uint32_t i = range.start; i < range.end; i += 3)
+ {
+ const physx::PxVec3& p0 = mVertices[mIndices[i]];
+ const physx::PxVec3& p1 = mVertices[mIndices[i + 1]];
+ const physx::PxVec3& p2 = mVertices[mIndices[i + 2]];
+ float triT, u, v;
+
+ bool hit = rayTriangleIntersection(mRayOrig, mRayDir, p0, p1, p2, triT, u, v);
+ if (!hit)
+ {
+ continue;
+ }
+ if (triT < t)
+ {
+ t = triT;
+ triNr = (int32_t)i / 3;
+ mLastRaycastNormal = (p1 - p0).cross(p2 - p0);
+ mLastRaycastPos = mRayOrig + mRayDir * t;
+ }
+ }
+ }
+ // smooth normals would be nicer...
+ mLastRaycastNormal.normalize();
+ return triNr >= 0;
+}
+
+
+
+bool MeshPainter::rayTriangleIntersection(const physx::PxVec3& orig, const physx::PxVec3& dir,
+ const physx::PxVec3& a, const physx::PxVec3& b, const physx::PxVec3& c,
+ float& t, float& u, float& v) const
+{
+ physx::PxVec3 edge1, edge2, tvec, pvec, qvec;
+ float det, iPX_det;
+
+ edge1 = b - a;
+ edge2 = c - a;
+ pvec = dir.cross(edge2);
+
+ /* if determinant is near zero, ray lies in plane of triangle */
+ det = edge1.dot(pvec);
+
+ if (det == 0.0f)
+ {
+ return false;
+ }
+ iPX_det = 1.0f / det;
+
+ /* calculate distance from vert0 to ray origin */
+ tvec = orig - a;
+
+ /* calculate U parameter and test bounds */
+ u = tvec.dot(pvec) * iPX_det;
+ if (u < 0.0f || u > 1.0f)
+ {
+ return false;
+ }
+
+ /* prepare to test V parameter */
+ qvec = tvec.cross(edge1);
+
+ /* calculate V parameter and test bounds */
+ v = dir.dot(qvec) * iPX_det;
+ if (v < 0.0f || u + v > 1.0f)
+ {
+ return false;
+ }
+
+ /* calculate t, ray intersects triangle */
+ t = edge2.dot(qvec) * iPX_det;
+
+ return true;
+}
+
+
+
+physx::PxVec3 MeshPainter::getTriangleCenter(int triNr) const
+{
+ const physx::PxVec3& p0 = mVertices[mIndices[3 * (uint32_t)triNr + 0]];
+ const physx::PxVec3& p1 = mVertices[mIndices[3 * (uint32_t)triNr + 1]];
+ const physx::PxVec3& p2 = mVertices[mIndices[3 * (uint32_t)triNr + 2]];
+ return (p0 + p1 + p2) / 3.0f;
+}
+
+
+
+physx::PxVec3 MeshPainter::getTriangleNormal(int triNr) const
+{
+ const physx::PxVec3& p0 = mVertices[mIndices[3 * (uint32_t)triNr + 0]];
+ const physx::PxVec3& p1 = mVertices[mIndices[3 * (uint32_t)triNr + 1]];
+ const physx::PxVec3& p2 = mVertices[mIndices[3 * (uint32_t)triNr + 2]];
+ physx::PxVec3 normal = (p1 - p0).cross(p2 - p0);
+ normal.normalize();
+ return normal;
+}
+
+
+
+void MeshPainter::collectTriangles() const
+{
+ // Dijkstra along the mesh
+ mCollectedTriangles.clear();
+ //int triNr;
+ //float t;
+ if (mLastTriangle == -1)
+ {
+ return;
+ }
+
+ //if (!rayCast(triNr, t))
+ // return;
+
+ mCurrentMark++;
+ std::vector<DistTriPair> heap;
+
+ DistTriPair start;
+ start.set(mLastTriangle, 0.0f);
+ heap.push_back(start);
+
+ while (!heap.empty())
+ {
+ std::pop_heap(heap.begin(), heap.end());
+
+ DistTriPair dt = heap[heap.size() - 1];
+ heap.pop_back();
+
+ if (mTriMarks[(uint32_t)dt.triNr] == mCurrentMark)
+ {
+ continue;
+ }
+
+ mTriMarks[(uint32_t)dt.triNr] = mCurrentMark;
+ mCollectedTriangles.push_back(dt);
+ physx::PxVec3 center = getTriangleCenter(dt.triNr);
+
+ for (uint32_t i = 0; i < 3; i++)
+ {
+ int adjNr = mNeighbors[3 * (uint32_t)dt.triNr + i];
+ if (!isValidRange(adjNr * 3))
+ {
+ continue;
+ }
+
+ if (mTriMarks[(uint32_t)adjNr] == mCurrentMark)
+ {
+ continue;
+ }
+
+ physx::PxVec3 adjCenter = getTriangleCenter(adjNr);
+ DistTriPair adj;
+ adj.set(adjNr, dt.dist - (center - adjCenter).magnitude()); // heap sorts large -> small, we need the opposite
+ if (-adj.dist > mPaintRadius)
+ {
+ continue;
+ }
+
+ physx::PxVec3 adjNormal = getTriangleNormal(adjNr);
+
+ // stop at 90 degrees
+ const float angle = physx::PxCos(physx::shdfnd::degToRad(70.0f));
+ if (adjNormal.dot(mLastRaycastNormal) < angle)
+ {
+ continue;
+ }
+
+ heap.push_back(adj);
+
+ std::push_heap(heap.begin(), heap.end());
+
+ }
+ }
+}
+
+
+
+bool MeshPainter::isValidRange(int vertexNumber) const
+{
+ for (uint32_t r = 0; r < mIndexRanges.size(); r++)
+ {
+ const IndexBufferRange& range = mIndexRanges[r];
+ if ((vertexNumber >= (int32_t)range.start) && (vertexNumber < (int32_t)range.end))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+bool MeshPainter::IndexBufferRange::isOverlapping(const IndexBufferRange& other) const
+{
+ if (other.start >= end)
+ {
+ return false;
+ }
+
+ if (start >= other.end)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+
+void MeshPainter::changeRadius(float paintRadius)
+{
+ mPaintRadius = paintRadius;
+}
+
+
+
+void MeshPainter::setRayAndRadius(const physx::PxVec3& rayOrig, const physx::PxVec3& rayDir, float paintRadius, int brushMode, float falloffExponent, float scaledTargetValue, float targetColor)
+{
+ float t;
+ mRayOrig = rayOrig;
+ mRayDir = rayDir;
+ rayCast(mLastTriangle, t);
+ mPaintRadius = paintRadius;
+ mBrushMode = brushMode;
+ mFalloffExponent = falloffExponent;
+ mScaledTargetValue = scaledTargetValue;
+ mBrushColor = targetColor;
+}
+
+
+
+void MeshPainter::paintFloat(unsigned int id, float min, float max, float target) const
+{
+ mTargetValue = target;
+
+ if (mLastRaycastNormal.isZero() && mPaintRadius >= 0)
+ {
+ return;
+ }
+
+ if (!mLastRaycastNormal.isZero() && mPaintRadius <= 0)
+ {
+ return;
+ }
+
+ int bufferNr = -1;
+ for (size_t i = 0; i < mFloatBuffers.size(); i++)
+ {
+ if (mFloatBuffers[i].id == id)
+ {
+ bufferNr = (int)i;
+ }
+ }
+
+ if (bufferNr < 0)
+ {
+ return;
+ }
+
+ if (mBrushMode == 1)
+ {
+ // sphere painting
+ // also used for flooding, with a negative radius
+ const float paintRadius2 = mPaintRadius * mPaintRadius;
+ physx::PxVec3 origProj = mRayOrig - mRayDir * mRayDir.dot(mRayOrig);
+
+ const unsigned int numVertices = (unsigned int)mVertices.size();
+ for (unsigned int i = 0; i < numVertices; i++)
+ {
+ if (mVerticesDisabled[i])
+ {
+ continue;
+ }
+
+ // negative radius means flood all vertices
+ float dist2 = 0;
+ if (mPaintRadius >= 0.0f)
+ {
+ // this is the sphere
+ dist2 = (mLastRaycastPos - mVertices[i]).magnitudeSquared();
+
+ if (dist2 > paintRadius2)
+ {
+ continue;
+ }
+ }
+
+ float relative = 1.0f - (physx::PxSqrt(dist2) / mPaintRadius);
+ PX_ASSERT(relative >= 0.0f);
+ if (relative < 0)
+ {
+ continue;
+ }
+ PX_ASSERT(relative <= 1.0f);
+
+ relative = physx::PxPow(relative, mFalloffExponent);
+
+ float& f = mFloatBuffers[(uint32_t)bufferNr][i];
+ f = physx::PxClamp(relative * target + (1.0f - relative) * f, min, max);
+ }
+ }
+ else if (mBrushMode == 0)
+ {
+ // flat painting
+ collectTriangles();
+ mCollectedVertices.clear();
+ for (uint32_t i = 0; i < mCollectedTriangles.size(); i++)
+ {
+ DistTriPair& dt = mCollectedTriangles[i];
+ for (uint32_t j = 0; j < 3; j++)
+ {
+ const uint32_t index = mIndices[3 * (int32_t)dt.triNr + j];
+ bool found = false;
+ for (uint32_t k = 0; k < mCollectedVertices.size(); k++)
+ {
+ if (mCollectedVertices[k] == index)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ mCollectedVertices.push_back(index);
+ }
+ }
+ }
+
+ for (uint32_t i = 0; i < mCollectedVertices.size(); i++)
+ {
+ float dist2 = (mLastRaycastPos - mVertices[mCollectedVertices[i]]).magnitudeSquared();
+ float relative = 1.0f - (physx::PxSqrt(dist2) / mPaintRadius);
+ if (relative < 0)
+ {
+ continue;
+ }
+
+ relative = physx::PxPow(relative, mFalloffExponent);
+
+ float& f = mFloatBuffers[(uint32_t)bufferNr][(int32_t)mCollectedVertices[i]];
+ f = physx::PxClamp(relative * target + (1.0f - relative) * f, min, max);
+ }
+ }
+ else
+ {
+ // smooth painting
+ PX_ASSERT(mBrushMode == 2);
+ collectTriangles();
+
+ mSmoothingCollectedIndices.clear();
+
+ mCollectedVertices.clear();
+ for (uint32_t i = 0; i < mCollectedTriangles.size(); i++)
+ {
+ DistTriPair& dt = mCollectedTriangles[i];
+ for (uint32_t j = 0; j < 3; j++)
+ {
+ const uint32_t index = mIndices[3 * (uint32_t)dt.triNr + j];
+ bool found = false;
+ for (uint32_t k = 0; k < mCollectedVertices.size(); k++)
+ {
+ if (mCollectedVertices[k] == index)
+ {
+ mSmoothingCollectedIndices.push_back(k);
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ mSmoothingCollectedIndices.push_back((uint32_t)mCollectedVertices.size());
+ mCollectedVertices.push_back(index);
+ }
+ }
+ }
+
+ if (mCollectedVerticesFloats.size() < mCollectedVertices.size() * 2)
+ {
+ mCollectedVerticesFloats.resize(mCollectedVertices.size() * 2, 0.0f);
+ }
+ else
+ {
+ memset(&mCollectedVerticesFloats[0], 0, sizeof(float) * mCollectedVertices.size() * 2);
+ }
+
+ for (uint32_t i = 0; i < mCollectedTriangles.size(); i++)
+ {
+ DistTriPair& dt = mCollectedTriangles[i];
+
+ // generate the average
+ float average = 0.0f;
+ for (uint32_t j = 0; j < 3; j++)
+ {
+ const uint32_t index = mIndices[3 * (uint32_t)dt.triNr + j];
+ average += mFloatBuffers[(uint32_t)bufferNr][index];
+ }
+ average /= 3.0f;
+
+ float relative = 1.0f - (dt.dist / mPaintRadius);
+
+ if (relative < 0)
+ {
+ continue;
+ }
+
+ relative = physx::PxPow(relative, mFalloffExponent);
+
+ for (uint32_t j = 0; j < 3; j++)
+ {
+ const uint32_t index = mIndices[3 * (uint32_t)dt.triNr + j];
+ const float source = mFloatBuffers[(uint32_t)bufferNr][index];
+
+ const uint32_t targetIndex = mSmoothingCollectedIndices[i * 3 + j];
+ mCollectedVerticesFloats[targetIndex * 2] += relative * average + (1.0f - relative) * source;
+ mCollectedVerticesFloats[targetIndex * 2 + 1] += 1.0f;
+ }
+ }
+
+ for (uint32_t i = 0; i < mCollectedVertices.size(); i++)
+ {
+ if (mFloatBuffers[(uint32_t)bufferNr][mCollectedVertices[i]] >= 0.0f)
+ {
+ mFloatBuffers[(uint32_t)bufferNr][mCollectedVertices[i]] = mCollectedVerticesFloats[i * 2] / mCollectedVerticesFloats[i * 2 + 1];
+ }
+ }
+ }
+}
+
+
+
+
+void MeshPainter::paintFlag(unsigned int id, unsigned int flag, bool useAND) const
+{
+ mTargetValue = 1.0f;
+ PX_ASSERT(mFalloffExponent == 0.0f);
+
+ if (mLastRaycastNormal.isZero() && mPaintRadius >= 0)
+ {
+ return;
+ }
+
+ if (!mLastRaycastNormal.isZero() && mPaintRadius <= 0)
+ {
+ return;
+ }
+
+ int bufferNr = -1;
+ for (size_t i = 0; i < mFlagBuffers.size(); i++)
+ {
+ if (mFlagBuffers[i].id == id)
+ {
+ bufferNr = (int)i;
+ }
+ }
+
+ if (bufferNr < 0)
+ {
+ return;
+ }
+
+ if (mBrushMode == 1)
+ {
+ // sphere painting
+ const float paintRadius2 = mPaintRadius * mPaintRadius;
+ physx::PxVec3 origProj = mRayOrig - mRayDir * mRayDir.dot(mRayOrig);
+
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ if (mVerticesDisabled[i])
+ {
+ continue;
+ }
+
+ // negative radius means flood all vertices
+ float dist2 = 0;
+ if (mPaintRadius >= 0.0f)
+ {
+ // this is the sphere
+ dist2 = (mLastRaycastPos - mVertices[i]).magnitudeSquared();
+
+ if (dist2 > paintRadius2)
+ {
+ continue;
+ }
+ }
+
+ unsigned int& u = mFlagBuffers[(uint32_t)bufferNr][i];
+ if (useAND)
+ {
+ u &= flag;
+ }
+ else
+ {
+ u |= flag;
+ }
+ }
+ }
+ else if (mBrushMode == 0)
+ {
+ // flat painting
+ collectTriangles();
+ mCollectedVertices.clear();
+ for (uint32_t i = 0; i < mCollectedTriangles.size(); i++)
+ {
+ DistTriPair& dt = mCollectedTriangles[i];
+ for (int j = 0; j < 3; j++)
+ {
+ const uint32_t index = mIndices[3 * (uint32_t)dt.triNr + j];
+ bool found = false;
+ for (size_t k = 0; k < mCollectedVertices.size(); k++)
+ {
+ if (mCollectedVertices[k] == index)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ mCollectedVertices.push_back(index);
+ }
+ }
+ }
+
+ const float paintRadius2 = mPaintRadius * mPaintRadius;
+
+ for (uint32_t i = 0; i < mCollectedVertices.size(); i++)
+ {
+ float dist2 = (mLastRaycastPos - mVertices[mCollectedVertices[i]]).magnitudeSquared();
+ if (dist2 > paintRadius2)
+ {
+ continue;
+ }
+
+ unsigned int& u = mFlagBuffers[(uint32_t)bufferNr][mCollectedVertices[i]];
+ if (useAND)
+ {
+ u &= flag;
+ }
+ else
+ {
+ u |= flag;
+ }
+ }
+ }
+}
+
+
+
+void MeshPainter::smoothFloat(uint32_t id, float smoothingFactor, uint32_t numIterations) const
+{
+ int bufferNr = -1;
+ for (uint32_t i = 0; i < mFloatBuffers.size(); i++)
+ if (mFloatBuffers[i].id == id)
+ {
+ bufferNr = (int32_t)i;
+ }
+
+ if (bufferNr < 0)
+ {
+ return;
+ }
+
+
+ struct Edge
+ {
+ Edge(uint32_t _v1, uint32_t _v2, float _l)
+ {
+ v1 = physx::PxMin(_v1, _v2);
+ v2 = physx::PxMax(_v1, _v2);
+ invLength = 1.0f / _l;
+ }
+ uint32_t v1;
+ uint32_t v2;
+ float invLength;
+ bool operator<(const Edge& other) const
+ {
+ if (v1 == other.v1)
+ {
+ return v2 < other.v2;
+ }
+ return v1 < other.v1;
+ }
+ bool operator==(const Edge& other) const
+ {
+ return v1 == other.v1 && v2 == other.v2;
+ }
+ };
+
+ int totalSize = 0;
+ for (uint32_t i = 0; i < mIndexRanges.size(); i++)
+ {
+ totalSize += mIndexRanges[i].end - mIndexRanges[i].start;
+ }
+
+ std::vector<Edge> edges;
+ edges.reserve((uint32_t)totalSize);
+ for (uint32_t r = 0; r < mIndexRanges.size(); r++)
+ {
+ for (uint32_t i = mIndexRanges[r].start; i < mIndexRanges[r].end; i += 3)
+ {
+ Edge e1(mIndices[i + 0], mIndices[i + 1], (mVertices[mIndices[i + 0]] - mVertices[mIndices[i + 1]]).magnitude());
+ Edge e2(mIndices[i + 1], mIndices[i + 2], (mVertices[mIndices[i + 1]] - mVertices[mIndices[i + 2]]).magnitude());
+ Edge e3(mIndices[i + 2], mIndices[i + 0], (mVertices[mIndices[i + 2]] - mVertices[mIndices[i + 0]]).magnitude());
+ edges.push_back(e1);
+ edges.push_back(e2);
+ edges.push_back(e3);
+ }
+ }
+
+ std::sort(edges.begin(), edges.end());
+
+ const PaintFloatBuffer& buffer = mFloatBuffers[(uint32_t)bufferNr];
+
+ std::vector<float> newValues(mVertices.size(), 0.0f);
+ std::vector<float> newWeights(mVertices.size(), 0.0f);
+
+ for (uint32_t iteration = 0; iteration < numIterations; iteration++)
+ {
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ newValues[i] = buffer[i];
+ newWeights[i] = 1.0f;
+ }
+
+ for (uint32_t i = 0; i < edges.size(); i++)
+ {
+ uint32_t j = i;
+ while (j < edges.size() && edges[j] == edges[i])
+ {
+ i = j;
+ j++;
+ }
+
+ // maxDistance < 0 is a drain, collisionFactor < 0 is not!
+ if (id != 1 || buffer[edges[i].v1] > 0.0f && buffer[edges[i].v2] > 0.0f)
+ {
+ const float factor = edges[i].invLength * smoothingFactor;
+ if (buffer[edges[i].v1] >= 0.0f)
+ {
+ newValues[edges[i].v1] += buffer[edges[i].v2] * factor;
+ newWeights[edges[i].v1] += factor;
+ }
+
+ if (buffer[edges[i].v2] >= 0.0f)
+ {
+ newValues[edges[i].v2] += buffer[edges[i].v1] * factor;
+ newWeights[edges[i].v2] += factor;
+ }
+ }
+ }
+
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ if (newWeights[i] > 0.0f)
+ {
+ buffer[i] = newValues[i] / newWeights[i];
+ }
+ }
+
+ uint32_t i = 0;
+ while (i < mSiblings.size())
+ {
+ float avg = 0.0f;
+ uint32_t num = 0;
+ uint32_t j = i;
+ while (mSiblings[j] >= 0)
+ {
+ avg += buffer[mSiblings[j]];
+ num++;
+ j++;
+ }
+ PX_ASSERT(num > 0);
+ avg /= num;
+ j = i;
+ while (mSiblings[j] >= 0)
+ {
+ buffer[mSiblings[j]] = avg;
+ j++;
+ }
+ i = j + 1;
+ }
+ }
+}
+
+
+
+void MeshPainter::smoothFloatFast(uint32_t id, uint32_t numIterations) const
+{
+ int bufferNr = -1;
+ for (uint32_t i = 0; i < mFloatBuffers.size(); i++)
+ if (mFloatBuffers[i].id == id)
+ {
+ bufferNr = (int32_t)i;
+ }
+
+ if (bufferNr < 0)
+ {
+ return;
+ }
+
+ const PaintFloatBuffer& buffer = mFloatBuffers[(uint32_t)bufferNr];
+
+ float min0 = PX_MAX_F32;
+ float max0 = -PX_MAX_F32;
+ for (uint32_t r = 0; r < mIndexRanges.size(); r++)
+ {
+ for (uint32_t i = mIndexRanges[r].start; i < mIndexRanges[r].end; i += 3)
+ {
+ for (uint32_t j = 0; j < 3; j++)
+ {
+ const float b = buffer[mIndices[i + j]];
+ if (id != 0 || b >= 0.0f)
+ {
+ min0 = physx::PxMin(min0, b);
+ max0 = physx::PxMax(max0, b);
+ }
+ }
+ }
+ }
+ if (min0 == PX_MAX_F32)
+ {
+ return;
+ }
+
+ for (uint32_t iteration = 0; iteration < numIterations; iteration++)
+ {
+ for (uint32_t r = 0; r < mIndexRanges.size(); r++)
+ {
+ for (uint32_t i = mIndexRanges[r].start; i < mIndexRanges[r].end; i += 3)
+ {
+ float avg = 0.0f;
+ // uint32_t num = 0;
+ for (uint32_t j = 0; j < 3; j++)
+ {
+ if (id == 0)
+ {
+ avg += physx::PxMax(buffer[mIndices[i + j]], 0.01f);
+ }
+ else
+ {
+ avg += buffer[mIndices[i + j]];
+ }
+ }
+ avg /= 3.0f;
+
+ for (uint32_t j = 0; j < 3; j++)
+ {
+ float& b = buffer[mIndices[i + j]];
+
+ if (id != 0 || (b >= 0.0f && b < 0.95f * max0))
+ {
+ b = avg;
+ }
+ }
+ }
+ }
+
+ for (uint32_t i = 0; i < mSiblings.size();)
+ {
+ float avg = 0.0f;
+ uint32_t num = 0;
+ uint32_t j = i;
+ while (mSiblings[j] >= 0)
+ {
+ avg += buffer[mSiblings[j]];
+ num++;
+ j++;
+ }
+ PX_ASSERT(num > 0);
+ avg /= num;
+ j = i;
+ while (mSiblings[j] >= 0)
+ {
+ buffer[mSiblings[j]] = avg;
+ j++;
+ }
+ i = j + 1;
+ }
+ }
+
+ float min1 = PX_MAX_F32;
+ float max1 = -PX_MAX_F32;
+ for (uint32_t r = 0; r < mIndexRanges.size(); r++)
+ {
+ for (uint32_t i = mIndexRanges[r].start; i < mIndexRanges[r].end; i += 3)
+ {
+ for (uint32_t j = 0; j < 3; j++)
+ {
+ float b = buffer[mIndices[i + j]];
+ if (id != 0 || b >= 0.0f)
+ {
+ min1 = physx::PxMin(min1, b);
+ max1 = physx::PxMax(max1, b);
+ }
+ }
+ }
+ }
+
+ std::vector<bool> marked;
+ marked.resize(mVertices.size(), false);
+
+ //float s = (max1 - min1);
+ //if (s > 0.0f)
+ //{
+ // s = (max0 - min0) / s;
+ // for (uint32_t i = mIndicesStart; i < mIndicesEnd; i+=3)
+ // {
+ // for (uint32_t j = 0; j < 3; j++)
+ // {
+ // uint32_t index = mIndices[i+j];
+ // if (!marked[index])
+ // {
+ // marked[index] = true;
+ // if (id != 0 || buffer[index] >= 0.0f)
+ // buffer[index] = min0 + (buffer[index] - min1) * s;
+ // }
+ // }
+ // }
+ //}
+}
+
+
+
+void MeshPainter::drawBrush(nvidia::apex::RenderDebugInterface* batcher) const
+{
+ PX_ASSERT(batcher != NULL);
+ if (mPaintRadius == 0 || mLastRaycastNormal.isZero() || batcher == NULL)
+ {
+ return;
+ }
+
+ uint32_t brushColor;
+ using RENDER_DEBUG::DebugColors;
+ if (mBrushColor < 0.0f || mBrushColor > 1.0f || mTargetValue < 0.0f)
+ {
+ brushColor = RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::Red);
+ }
+ else
+ {
+ brushColor = RENDER_DEBUG_IFACE(batcher)->getDebugColor(mBrushColor, mBrushColor, mBrushColor);
+ }
+
+ RENDER_DEBUG_IFACE(batcher)->setCurrentColor(brushColor);
+
+
+ const physx::PxVec3 up(0.0f, 1.0f, 0.0f);
+ physx::PxVec3 axis = mLastRaycastNormal.cross(up);
+ if (axis.isZero())
+ {
+ axis = physx::PxVec3(0.0f, 1.0f, 0.0f);
+ }
+ else
+ {
+ axis.normalize();
+ }
+ float angle = physx::PxAcos(mLastRaycastNormal.dot(up));
+ physx::PxMat44 transform(physx::PxQuat(-angle, axis));
+ transform = transform * physx::PxMat44(physx::PxVec4(mPaintRadius, mPaintRadius, mPaintRadius, 1.f));
+ transform.setPosition(mLastRaycastPos);
+ RENDER_DEBUG_IFACE(batcher)->debugLine(mLastRaycastPos, mLastRaycastPos + mLastRaycastNormal * mScaledTargetValue);
+
+ const uint32_t arcSize = 20;
+ physx::PxVec3 lastPos(1.0f, 0.0f, 0.0f);
+ lastPos = transform.transform(lastPos);
+ for (uint32_t i = 1; i <= arcSize; i++)
+ {
+ const float angle = i * physx::PxTwoPi / (float)arcSize;
+ physx::PxVec3 pos(physx::PxCos(angle), 0.0f, physx::PxSin(angle));
+ pos = transform.transform(pos);
+
+ RENDER_DEBUG_IFACE(batcher)->debugLine(lastPos, pos);
+
+ lastPos = pos;
+ }
+
+ const uint32_t numSteps = 10;
+ float lastHeight = mScaledTargetValue / mPaintRadius;
+ float lastT = 0.0f;
+ for (uint32_t i = 1; i <= numSteps; i++)
+ {
+ const float t = (float)i / (float)numSteps;
+
+ const float height = physx::PxPow(1.0f - t, mFalloffExponent) * mScaledTargetValue / mPaintRadius;
+
+ RENDER_DEBUG_IFACE(batcher)->debugLine(transform.transform(physx::PxVec3(t, height, 0)), transform.transform(physx::PxVec3(lastT, lastHeight, 0)));
+ RENDER_DEBUG_IFACE(batcher)->debugLine(transform.transform(physx::PxVec3(-t, height, 0)), transform.transform(physx::PxVec3(-lastT, lastHeight, 0)));
+ RENDER_DEBUG_IFACE(batcher)->debugLine(transform.transform(physx::PxVec3(0, height, t)), transform.transform(physx::PxVec3(0, lastHeight, lastT)));
+ RENDER_DEBUG_IFACE(batcher)->debugLine(transform.transform(physx::PxVec3(0, height, -t)), transform.transform(physx::PxVec3(0, lastHeight, -lastT)));
+
+ lastT = t;
+ lastHeight = height;
+ }
+
+ if (mFalloffExponent == 0)
+ {
+ const float height = mScaledTargetValue / mPaintRadius;
+ lastPos = transform.transform(physx::PxVec3(1.0f, height, 0.0f));
+ for (uint32_t i = 1; i <= arcSize; i++)
+ {
+ const float angle = i * physx::PxTwoPi / (float)arcSize;
+ physx::PxVec3 pos(physx::PxCos(angle), height, physx::PxSin(angle));
+ pos = transform.transform(pos);
+
+ RENDER_DEBUG_IFACE(batcher)->debugLine(lastPos, pos);
+
+ lastPos = pos;
+ }
+
+ RENDER_DEBUG_IFACE(batcher)->debugLine(transform.transform(physx::PxVec3(1, 0, 0)), transform.transform(physx::PxVec3(1, height, 0)));
+ RENDER_DEBUG_IFACE(batcher)->debugLine(transform.transform(physx::PxVec3(-1, 0, 0)), transform.transform(physx::PxVec3(-1, height, 0)));
+ RENDER_DEBUG_IFACE(batcher)->debugLine(transform.transform(physx::PxVec3(0, 0, 1)), transform.transform(physx::PxVec3(0, height, 1)));
+ RENDER_DEBUG_IFACE(batcher)->debugLine(transform.transform(physx::PxVec3(0, 0, -1)), transform.transform(physx::PxVec3(0, height, -1)));
+ }
+}
+
+
+
+void MeshPainter::computeSiblingInfo(float distanceThreshold)
+{
+ uint32_t numVerts = (uint32_t)mVertices.size();
+
+ // select primary axis
+ physx::PxBounds3 bounds;
+ bounds.setEmpty();
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ bounds.include(mVertices[i]);
+ }
+
+
+ physx::PxVec3 extents = bounds.getExtents();
+ int axis;
+ if (extents.x > extents.y && extents.x > extents.z)
+ {
+ axis = 0;
+ }
+ else if (extents.y > extents.z)
+ {
+ axis = 1;
+ }
+ else
+ {
+ axis = 2;
+ }
+
+ // create sorted vertex list
+ struct VertRef
+ {
+ uint32_t vertNr;
+ float val;
+ bool operator < (const VertRef& v) const
+ {
+ return val < v.val;
+ }
+ };
+
+ std::vector<VertRef> refs;
+ refs.resize(numVerts);
+ for (uint32_t i = 0; i < numVerts; i++)
+ {
+ refs[i].vertNr = i;
+ refs[i].val = mVertices[i][axis];
+ }
+ std::sort(refs.begin(), refs.end());
+
+ // collect siblings
+ mFirstSibling.resize(numVerts, -1);
+ mSiblings.clear();
+
+ float d2 = distanceThreshold * distanceThreshold;
+
+ for (uint32_t i = 0; i < numVerts; i++)
+ {
+ uint32_t vi = refs[i].vertNr;
+ if (mFirstSibling[vi] >= 0)
+ {
+ continue;
+ }
+
+ int32_t firstSibling = (int32_t)mSiblings.size();
+ uint32_t numSiblings = 0;
+
+ for (uint32_t j = i + 1; j < numVerts; j++)
+ {
+ if (refs[j].val - refs[i].val > distanceThreshold)
+ {
+ break;
+ }
+ uint32_t vj = refs[j].vertNr;
+ if ((mVertices[vi] - mVertices[vj]).magnitudeSquared() <= d2)
+ {
+ mFirstSibling[vj] = firstSibling;
+ mSiblings.push_back((int32_t)vj);
+ numSiblings++;
+ }
+ }
+ if (numSiblings > 0)
+ {
+ mFirstSibling[vi] = firstSibling;
+ mSiblings.push_back((int32_t)vi);
+ mSiblings.push_back(-1); // end marker
+ }
+ }
+}
+
+} //namespace SharedTools
+
+#endif // PX_WINDOWS_FAMILY
diff --git a/APEX_1.4/shared/external/src/MultiClientRenderResourceManager.cpp b/APEX_1.4/shared/external/src/MultiClientRenderResourceManager.cpp
new file mode 100644
index 00000000..86e9c82b
--- /dev/null
+++ b/APEX_1.4/shared/external/src/MultiClientRenderResourceManager.cpp
@@ -0,0 +1,663 @@
+/*
+ * 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 "MultiClientRenderResourceManager.h"
+
+#include "UserRenderVertexBuffer.h"
+#include "UserRenderIndexBuffer.h"
+#include "UserRenderBoneBuffer.h"
+#include "UserRenderInstanceBuffer.h"
+#include "UserRenderSpriteBuffer.h"
+#include "UserRenderSurfaceBuffer.h"
+
+#include "UserRenderResource.h"
+#include "UserRenderResourceDesc.h"
+
+#include "RenderContext.h"
+
+
+#include <assert.h>
+#include <algorithm> // for std::min
+
+
+MultiClientRenderResourceManager::MultiClientRenderResourceManager()
+{
+
+}
+
+
+
+MultiClientRenderResourceManager::~MultiClientRenderResourceManager()
+{
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ if (mChildren[i].destroyRrm)
+ {
+ delete mChildren[i].rrm;
+ }
+ mChildren[i].rrm = NULL;
+ }
+}
+
+
+
+void MultiClientRenderResourceManager::addChild(nvidia::apex::UserRenderResourceManager* rrm, bool destroyAutomatic)
+{
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ if (mChildren[i].rrm == rrm)
+ {
+ return;
+ }
+ }
+
+ mChildren.push_back(Child(rrm, destroyAutomatic));
+}
+
+bool MultiClientRenderResourceManager::getSpriteLayoutData(uint32_t spriteCount, uint32_t spriteSemanticsBitmap, nvidia::apex::UserRenderSpriteBufferDesc* bufferDesc)
+{
+ PX_UNUSED(spriteCount);
+ PX_UNUSED(spriteSemanticsBitmap);
+ PX_UNUSED(bufferDesc);
+ return false;
+}
+
+bool MultiClientRenderResourceManager::getInstanceLayoutData(uint32_t particleCount, uint32_t particleSemanticsBitmap, nvidia::apex::UserRenderInstanceBufferDesc* bufferDesc)
+{
+ PX_UNUSED(particleCount);
+ PX_UNUSED(particleSemanticsBitmap);
+ PX_UNUSED(bufferDesc);
+ return false;
+}
+
+template<typename T>
+class MultiClientBuffer
+{
+public:
+ MultiClientBuffer() {}
+ ~MultiClientBuffer() {}
+
+
+ void addChild(T* vb)
+ {
+ mChildren.push_back(vb);
+ }
+
+ T* getChild(size_t index)
+ {
+ assert(index < mChildren.size());
+ return mChildren[index];
+ }
+
+protected:
+ std::vector<T*> mChildren;
+};
+
+
+
+
+class MultiClientVertexBuffer : public nvidia::apex::UserRenderVertexBuffer, public MultiClientBuffer<nvidia::apex::UserRenderVertexBuffer>
+{
+public:
+ MultiClientVertexBuffer() {}
+ ~MultiClientVertexBuffer() {}
+
+ virtual void writeBuffer(const nvidia::apex::RenderVertexBufferData& data, unsigned int firstVertex, unsigned int numVertices)
+ {
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ mChildren[i]->writeBuffer(data, firstVertex, numVertices);
+ }
+ }
+
+};
+
+
+nvidia::apex::UserRenderVertexBuffer* MultiClientRenderResourceManager::createVertexBuffer(const nvidia::apex::UserRenderVertexBufferDesc& desc)
+{
+ MultiClientVertexBuffer* vb = new MultiClientVertexBuffer();
+
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ vb->addChild(mChildren[i].rrm->createVertexBuffer(desc));
+ }
+
+ return vb;
+}
+
+void MultiClientRenderResourceManager::releaseVertexBuffer(nvidia::apex::UserRenderVertexBuffer& buffer)
+{
+ MultiClientVertexBuffer* vb = static_cast<MultiClientVertexBuffer*>(&buffer);
+
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ nvidia::apex::UserRenderVertexBuffer* childVb = vb->getChild(i);
+ mChildren[i].rrm->releaseVertexBuffer(*childVb);
+ }
+
+ delete vb;
+}
+
+
+
+
+
+
+class MultiClientIndexBuffer : public nvidia::apex::UserRenderIndexBuffer, public MultiClientBuffer<nvidia::apex::UserRenderIndexBuffer>
+{
+public:
+ MultiClientIndexBuffer() {}
+ ~MultiClientIndexBuffer() {}
+
+ virtual void writeBuffer(const void* srcData, unsigned int srcStride, unsigned int firstDestElement, unsigned int numElements)
+ {
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ mChildren[i]->writeBuffer(srcData, srcStride, firstDestElement, numElements);
+ }
+ }
+};
+
+
+nvidia::apex::UserRenderIndexBuffer* MultiClientRenderResourceManager::createIndexBuffer(const nvidia::apex::UserRenderIndexBufferDesc& desc)
+{
+ MultiClientIndexBuffer* ib = new MultiClientIndexBuffer();
+
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ ib->addChild(mChildren[i].rrm->createIndexBuffer(desc));
+ }
+
+ return ib;
+}
+
+void MultiClientRenderResourceManager::releaseIndexBuffer(nvidia::apex::UserRenderIndexBuffer& buffer)
+{
+ MultiClientIndexBuffer* ib = static_cast<MultiClientIndexBuffer*>(&buffer);
+
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ nvidia::apex::UserRenderIndexBuffer* childIb = ib->getChild(i);
+ mChildren[i].rrm->releaseIndexBuffer(*childIb);
+ }
+
+ delete ib;
+}
+
+
+
+
+
+
+
+
+class MultiClientBoneBuffer : public nvidia::apex::UserRenderBoneBuffer, public MultiClientBuffer<nvidia::apex::UserRenderBoneBuffer>
+{
+public:
+ MultiClientBoneBuffer() {}
+ ~MultiClientBoneBuffer() {}
+
+ virtual void writeBuffer(const nvidia::apex::RenderBoneBufferData& data, unsigned int firstBone, unsigned int numBones)
+ {
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ mChildren[i]->writeBuffer(data, firstBone, numBones);
+ }
+ }
+};
+
+
+
+nvidia::apex::UserRenderBoneBuffer* MultiClientRenderResourceManager::createBoneBuffer(const nvidia::apex::UserRenderBoneBufferDesc& desc)
+{
+ MultiClientBoneBuffer* bb = new MultiClientBoneBuffer();
+
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ bb->addChild(mChildren[i].rrm->createBoneBuffer(desc));
+ }
+
+ return bb;
+}
+
+
+
+void MultiClientRenderResourceManager::releaseBoneBuffer(nvidia::apex::UserRenderBoneBuffer& buffer)
+{
+ MultiClientBoneBuffer* bb = static_cast<MultiClientBoneBuffer*>(&buffer);
+
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ nvidia::apex::UserRenderBoneBuffer* childBb = bb->getChild(i);
+ mChildren[i].rrm->releaseBoneBuffer(*childBb);
+ }
+
+ delete bb;
+}
+
+
+
+
+
+class MultiClientInstanceBuffer : public nvidia::apex::UserRenderInstanceBuffer, public MultiClientBuffer<nvidia::apex::UserRenderInstanceBuffer>
+{
+public:
+ MultiClientInstanceBuffer() {}
+ ~MultiClientInstanceBuffer() {}
+
+ virtual void writeBuffer(const void* data, unsigned int firstInstance, unsigned int numInstances)
+ {
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ mChildren[i]->writeBuffer(data, firstInstance, numInstances);
+ }
+ }
+};
+
+
+
+
+nvidia::apex::UserRenderInstanceBuffer* MultiClientRenderResourceManager::createInstanceBuffer(const nvidia::apex::UserRenderInstanceBufferDesc& desc)
+{
+ MultiClientInstanceBuffer* ib = new MultiClientInstanceBuffer();
+
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ ib->addChild(mChildren[i].rrm->createInstanceBuffer(desc));
+ }
+
+ return ib;
+}
+
+
+
+void MultiClientRenderResourceManager::releaseInstanceBuffer(nvidia::apex::UserRenderInstanceBuffer& buffer)
+{
+ MultiClientInstanceBuffer* ib = static_cast<MultiClientInstanceBuffer*>(&buffer);
+
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ nvidia::apex::UserRenderInstanceBuffer* childIb = ib->getChild(i);
+ mChildren[i].rrm->releaseInstanceBuffer(*childIb);
+ }
+
+ delete ib;
+}
+
+
+
+
+
+class MultiClientSpriteBuffer : public nvidia::apex::UserRenderSpriteBuffer, public MultiClientBuffer<nvidia::apex::UserRenderSpriteBuffer>
+{
+public:
+ MultiClientSpriteBuffer() {}
+ ~MultiClientSpriteBuffer() {}
+
+ virtual void writeBuffer(const void* data, unsigned int firstSprite, unsigned int numSprites)
+ {
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ mChildren[i]->writeBuffer(data, firstSprite, numSprites);
+ }
+ }
+};
+
+
+
+nvidia::apex::UserRenderSpriteBuffer* MultiClientRenderResourceManager::createSpriteBuffer(const nvidia::apex::UserRenderSpriteBufferDesc& desc)
+{
+ MultiClientSpriteBuffer* sb = new MultiClientSpriteBuffer();
+
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ sb->addChild(mChildren[i].rrm->createSpriteBuffer(desc));
+ }
+
+ return sb;
+}
+
+
+
+void MultiClientRenderResourceManager::releaseSpriteBuffer(nvidia::apex::UserRenderSpriteBuffer& buffer)
+{
+ MultiClientSpriteBuffer* sb = static_cast<MultiClientSpriteBuffer*>(&buffer);
+
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ nvidia::apex::UserRenderSpriteBuffer* childSb = sb->getChild(i);
+ mChildren[i].rrm->releaseSpriteBuffer(*childSb);
+ }
+
+ delete sb;
+}
+
+
+class MultiClientSurfaceBuffer : public nvidia::apex::UserRenderSurfaceBuffer, public MultiClientBuffer<nvidia::apex::UserRenderSurfaceBuffer>
+{
+public:
+ MultiClientSurfaceBuffer() {}
+ ~MultiClientSurfaceBuffer() {}
+
+ virtual void writeBuffer(const void* /*srcData*/,
+ uint32_t /*srcPitch*/,
+ uint32_t /*srcHeight*/,
+ uint32_t /*dstX*/,
+ uint32_t /*dstY*/,
+ uint32_t /*dstZ*/,
+ uint32_t /*width*/,
+ uint32_t /*height*/,
+ uint32_t /*depth*/)
+ {
+ //for (size_t i = 0; i < mChildren.size(); i++)
+ //{
+ // mChildren[i]->writeBuffer(data, firstSprite, numSprites);
+ //}
+ }
+};
+
+nvidia::apex::UserRenderSurfaceBuffer* MultiClientRenderResourceManager::createSurfaceBuffer( const nvidia::apex::UserRenderSurfaceBufferDesc &desc )
+{
+ MultiClientSurfaceBuffer* sb = new MultiClientSurfaceBuffer();
+
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ sb->addChild(mChildren[i].rrm->createSurfaceBuffer(desc));
+ }
+
+ return sb;
+}
+
+
+
+void MultiClientRenderResourceManager::releaseSurfaceBuffer( nvidia::apex::UserRenderSurfaceBuffer &buffer )
+{
+ MultiClientSurfaceBuffer* sb = static_cast<MultiClientSurfaceBuffer*>(&buffer);
+
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ nvidia::apex::UserRenderSurfaceBuffer* childSb = sb->getChild(i);
+ mChildren[i].rrm->releaseSurfaceBuffer(*childSb);
+ }
+
+ delete sb;
+}
+
+
+
+
+
+
+
+class MultiClientRenderResource : public nvidia::apex::UserRenderResource
+{
+public:
+ MultiClientRenderResource(const nvidia::apex::UserRenderResourceDesc& desc) : mDescriptor(desc)
+ {
+ assert(desc.numVertexBuffers > 0);
+
+ mVertexBufferOriginal.resize(desc.numVertexBuffers);
+ for (size_t i = 0; i < mVertexBufferOriginal.size(); i++)
+ {
+ mVertexBufferOriginal[i] = desc.vertexBuffers[i];
+ }
+
+ mDescriptor.vertexBuffers = &mVertexBufferOriginal[0];
+
+ }
+
+ ~MultiClientRenderResource()
+ {
+
+ }
+
+
+
+ void addChild(nvidia::apex::UserRenderResourceManager* rrm)
+ {
+ nvidia::apex::UserRenderResourceDesc newDesc(mDescriptor);
+
+ std::vector<nvidia::apex::UserRenderVertexBuffer*> childVertexBuffers(mVertexBufferOriginal.size());
+
+ size_t nextChild = mChildren.size();
+
+ for (size_t i = 0; i < mVertexBufferOriginal.size(); i++)
+ {
+ MultiClientVertexBuffer* vb = static_cast<MultiClientVertexBuffer*>(mVertexBufferOriginal[i]);
+ childVertexBuffers[i] = vb->getChild(nextChild);
+ }
+
+ newDesc.vertexBuffers = &childVertexBuffers[0];
+
+ if (mDescriptor.indexBuffer != NULL)
+ {
+ newDesc.indexBuffer = static_cast<MultiClientIndexBuffer*>(mDescriptor.indexBuffer)->getChild(nextChild);
+ }
+
+ if (mDescriptor.boneBuffer != NULL)
+ {
+ newDesc.boneBuffer = static_cast<MultiClientBoneBuffer*>(mDescriptor.boneBuffer)->getChild(nextChild);
+ }
+
+ if (mDescriptor.spriteBuffer != NULL)
+ {
+ newDesc.spriteBuffer = static_cast<MultiClientSpriteBuffer*>(mDescriptor.spriteBuffer)->getChild(nextChild);
+ }
+
+ if (rrm != NULL)
+ {
+ mChildren.push_back(rrm->createResource(newDesc));
+ }
+ }
+
+
+ nvidia::apex::UserRenderResource* getChild(size_t index)
+ {
+ assert(index < mChildren.size());
+ return mChildren[index];
+ }
+
+
+
+ void setVertexBufferRange(unsigned int firstVertex, unsigned int numVerts)
+ {
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ mChildren[i]->setVertexBufferRange(firstVertex, numVerts);
+ }
+
+ mDescriptor.firstVertex = firstVertex;
+ mDescriptor.numVerts = numVerts;
+ }
+
+
+
+ void setIndexBufferRange(unsigned int firstIndex, unsigned int numIndices)
+ {
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ mChildren[i]->setIndexBufferRange(firstIndex, numIndices);
+ }
+
+ mDescriptor.firstIndex = firstIndex;
+ mDescriptor.numIndices = numIndices;
+ }
+
+
+
+ void setBoneBufferRange(unsigned int firstBone, unsigned int numBones)
+ {
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ mChildren[i]->setBoneBufferRange(firstBone, numBones);
+ }
+
+ mDescriptor.firstBone = firstBone;
+ mDescriptor.numBones = numBones;
+ }
+
+
+
+ void setInstanceBufferRange(unsigned int firstInstance, unsigned int numInstances)
+ {
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ mChildren[i]->setInstanceBufferRange(firstInstance, numInstances);
+ }
+
+ mDescriptor.firstInstance = firstInstance;
+ mDescriptor.numInstances = numInstances;
+ }
+
+
+
+ void setSpriteBufferRange(unsigned int firstSprite, unsigned int numSprites)
+ {
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ mChildren[i]->setSpriteBufferRange(firstSprite, numSprites);
+ }
+
+ mDescriptor.firstSprite = firstSprite;
+ mDescriptor.numSprites = numSprites;
+ }
+
+
+
+
+ void setMaterial(void* material)
+ {
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ mChildren[i]->setMaterial(material);
+ }
+
+ mDescriptor.material = material;
+ }
+
+
+
+
+ unsigned int getNbVertexBuffers() const
+ {
+ return mDescriptor.numVertexBuffers;
+ }
+
+
+
+ nvidia::apex::UserRenderVertexBuffer* getVertexBuffer(unsigned int index) const
+ {
+ return mDescriptor.vertexBuffers[index];
+ }
+
+
+
+ nvidia::apex::UserRenderIndexBuffer* getIndexBuffer() const
+ {
+ return mDescriptor.indexBuffer;
+ }
+
+
+
+
+ nvidia::apex::UserRenderBoneBuffer* getBoneBuffer() const
+ {
+ return mDescriptor.boneBuffer;
+ }
+
+
+
+ nvidia::apex::UserRenderInstanceBuffer* getInstanceBuffer() const
+ {
+ return mDescriptor.instanceBuffer;
+ }
+
+
+
+ nvidia::apex::UserRenderSpriteBuffer* getSpriteBuffer() const
+ {
+ return mDescriptor.spriteBuffer;
+ }
+
+protected:
+ std::vector<nvidia::apex::UserRenderVertexBuffer*> mVertexBufferOriginal;
+ std::vector<nvidia::apex::UserRenderResource*> mChildren;
+
+ nvidia::apex::UserRenderResourceDesc mDescriptor;
+};
+
+
+
+
+nvidia::apex::UserRenderResource* MultiClientRenderResourceManager::createResource(const nvidia::apex::UserRenderResourceDesc& desc)
+{
+ MultiClientRenderResource* rr = new MultiClientRenderResource(desc);
+
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ rr->addChild(mChildren[i].rrm);
+ }
+
+ return rr;
+}
+
+
+
+void MultiClientRenderResourceManager::releaseResource(nvidia::apex::UserRenderResource& resource)
+{
+ MultiClientRenderResource* rr = static_cast<MultiClientRenderResource*>(&resource);
+
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ mChildren[i].rrm->releaseResource(*rr->getChild(i));
+ }
+
+ delete rr;
+}
+
+
+
+unsigned int MultiClientRenderResourceManager::getMaxBonesForMaterial(void* material)
+{
+ unsigned int smallestMax = 10000;
+
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ unsigned int childMax = mChildren[i].rrm->getMaxBonesForMaterial(material);
+ if (childMax > 0)
+ {
+ smallestMax = std::min(smallestMax, childMax);
+ }
+ }
+
+ return smallestMax;
+}
+
+
+
+void MultiClientUserRenderer::addChild(nvidia::apex::UserRenderer* child)
+{
+ mChildren.push_back(child);
+}
+
+
+
+void MultiClientUserRenderer::renderResource(const nvidia::apex::RenderContext& context)
+{
+ MultiClientRenderResource* rr = static_cast<MultiClientRenderResource*>(context.renderResource);
+
+ for (size_t i = 0; i < mChildren.size(); i++)
+ {
+ nvidia::apex::RenderContext newContext(context);
+ newContext.renderResource = rr->getChild(i);
+ mChildren[i]->renderResource(newContext);
+ }
+}
diff --git a/APEX_1.4/shared/external/src/RecordingRenderResourceManager.cpp b/APEX_1.4/shared/external/src/RecordingRenderResourceManager.cpp
new file mode 100644
index 00000000..06e24dfa
--- /dev/null
+++ b/APEX_1.4/shared/external/src/RecordingRenderResourceManager.cpp
@@ -0,0 +1,952 @@
+/*
+ * 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 "RecordingRenderResourceManager.h"
+
+#include <assert.h>
+#include <time.h>
+
+#include <vector>
+
+#include "UserRenderVertexBuffer.h"
+#include "UserRenderResource.h"
+#include "UserRenderResourceDesc.h"
+#include "UserRenderIndexBuffer.h"
+#include "UserRenderIndexBufferDesc.h"
+#include "UserRenderBoneBuffer.h"
+#include "UserRenderBoneBufferDesc.h"
+
+#include "RenderContext.h"
+
+#define BREAK_ON_UNIMPLEMENTED 1
+
+#if BREAK_ON_UNIMPLEMENTED
+#define UNIMPLEMENTED assert(0)
+#else
+#define UNIMPLEMENTED /*void&*/
+#endif
+
+#if PX_WINDOWS_FAMILY
+#define NOMINMAX
+#include "windows.h"
+#endif
+
+RecordingRenderResourceManager::RecordingRenderResourceManager(nvidia::apex::UserRenderResourceManager* child, bool /*ownsChild*/, RecorderInterface* recorder) : mChild(child), mRecorder(recorder)
+{
+}
+
+
+
+RecordingRenderResourceManager::~RecordingRenderResourceManager()
+{
+ if (mOwnsChild)
+ {
+ delete mChild;
+ }
+ mChild = NULL;
+
+ if (mRecorder != NULL)
+ {
+ delete mRecorder;
+ mRecorder = NULL;
+ }
+}
+
+
+class RecordingVertexBuffer : public nvidia::apex::UserRenderVertexBuffer
+{
+public:
+ RecordingVertexBuffer(const nvidia::apex::UserRenderVertexBufferDesc& desc, nvidia::apex::UserRenderVertexBuffer* child, RecordingRenderResourceManager::RecorderInterface* recorder) :
+ mDescriptor(desc), mChild(child), mBufferId(0), mRecorder(recorder)
+ {
+ mBufferId = vertexBufferId++;
+
+ if (mRecorder != NULL)
+ {
+ mRecorder->createVertexBuffer(mBufferId, desc);
+ }
+ }
+
+ ~RecordingVertexBuffer()
+ {
+ if (mRecorder != NULL)
+ {
+ mRecorder->releaseVertexBuffer(mBufferId);
+ }
+ }
+
+ virtual void writeBuffer(const nvidia::apex::RenderVertexBufferData& data, unsigned int firstVertex, unsigned int numVertices)
+ {
+ if (mChild != NULL)
+ {
+ mChild->writeBuffer(data, firstVertex, numVertices);
+ }
+
+ if (mRecorder != NULL)
+ {
+ mRecorder->writeVertexBuffer(mBufferId, data, firstVertex, numVertices);
+ }
+ }
+
+ nvidia::apex::UserRenderVertexBuffer* getChild()
+ {
+ return mChild;
+ }
+
+protected:
+ nvidia::apex::UserRenderVertexBufferDesc mDescriptor;
+ nvidia::apex::UserRenderVertexBuffer* mChild;
+
+ static unsigned int vertexBufferId;
+ unsigned int mBufferId;
+
+ RecordingRenderResourceManager::RecorderInterface* mRecorder;
+};
+
+unsigned int RecordingVertexBuffer::vertexBufferId = 0;
+
+
+nvidia::apex::UserRenderVertexBuffer* RecordingRenderResourceManager::createVertexBuffer(const nvidia::apex::UserRenderVertexBufferDesc& desc)
+{
+ nvidia::apex::UserRenderVertexBuffer* child = NULL;
+ if (mChild != NULL)
+ {
+ child = mChild->createVertexBuffer(desc);
+ }
+
+ return new RecordingVertexBuffer(desc, child, mRecorder);
+}
+
+
+
+void RecordingRenderResourceManager::releaseVertexBuffer(nvidia::apex::UserRenderVertexBuffer& buffer)
+{
+ if (mChild != NULL)
+ {
+ mChild->releaseVertexBuffer(*reinterpret_cast<RecordingVertexBuffer&>(buffer).getChild());
+ }
+
+ delete &buffer;
+}
+
+
+
+class RecordingIndexBuffer : public nvidia::apex::UserRenderIndexBuffer
+{
+public:
+ RecordingIndexBuffer(const nvidia::apex::UserRenderIndexBufferDesc& desc, nvidia::apex::UserRenderIndexBuffer* child, RecordingRenderResourceManager::RecorderInterface* recorder) :
+ mDescriptor(desc), mChild(child), mBufferId(0), mRecorder(recorder)
+ {
+ mBufferId = indexBufferId++;
+
+ if (mRecorder != NULL)
+ {
+ mRecorder->createIndexBuffer(mBufferId, desc);
+ }
+ }
+
+ ~RecordingIndexBuffer()
+ {
+ if (mRecorder != NULL)
+ {
+ mRecorder->releaseIndexBuffer(mBufferId);
+ }
+ }
+
+ virtual void writeBuffer(const void* srcData, uint32_t srcStride, unsigned int firstDestElement, unsigned int numElements)
+ {
+ if (mChild != NULL)
+ {
+ mChild->writeBuffer(srcData, srcStride, firstDestElement, numElements);
+ }
+
+ if (mRecorder != NULL)
+ {
+ mRecorder->writeIndexBuffer(mBufferId, srcData, srcStride, firstDestElement, numElements, mDescriptor.format);
+ }
+ }
+
+ nvidia::apex::UserRenderIndexBuffer* getChild()
+ {
+ return mChild;
+ }
+
+protected:
+ nvidia::apex::UserRenderIndexBufferDesc mDescriptor;
+ nvidia::apex::UserRenderIndexBuffer* mChild;
+
+ static unsigned int indexBufferId;
+ unsigned int mBufferId;
+
+ RecordingRenderResourceManager::RecorderInterface* mRecorder;
+};
+
+unsigned int RecordingIndexBuffer::indexBufferId = 0;
+
+
+nvidia::apex::UserRenderIndexBuffer* RecordingRenderResourceManager::createIndexBuffer(const nvidia::apex::UserRenderIndexBufferDesc& desc)
+{
+ nvidia::apex::UserRenderIndexBuffer* child = NULL;
+ if (mChild != NULL)
+ {
+ child = mChild->createIndexBuffer(desc);
+ }
+
+ return new RecordingIndexBuffer(desc, child, mRecorder);
+}
+
+
+
+void RecordingRenderResourceManager::releaseIndexBuffer(nvidia::apex::UserRenderIndexBuffer& buffer)
+{
+ if (mChild != NULL)
+ {
+ mChild->releaseIndexBuffer(*reinterpret_cast<RecordingIndexBuffer&>(buffer).getChild());
+ }
+
+ delete &buffer;
+}
+
+
+
+class RecordingBoneBuffer : public nvidia::apex::UserRenderBoneBuffer
+{
+public:
+ RecordingBoneBuffer(const nvidia::apex::UserRenderBoneBufferDesc& desc, nvidia::apex::UserRenderBoneBuffer* child, RecordingRenderResourceManager::RecorderInterface* recorder) :
+ mDescriptor(desc), mChild(child), mBufferId(0), mRecorder(recorder)
+ {
+ mBufferId = boneBufferId++;
+
+ if (mRecorder != NULL)
+ {
+ mRecorder->createBoneBuffer(mBufferId, desc);
+ }
+ }
+
+ ~RecordingBoneBuffer()
+ {
+ if (mRecorder != NULL)
+ {
+ mRecorder->releaseBoneBuffer(mBufferId);
+ }
+ }
+
+ virtual void writeBuffer(const nvidia::apex::RenderBoneBufferData& data, unsigned int firstBone, unsigned int numBones)
+ {
+ if (mChild != NULL)
+ {
+ mChild->writeBuffer(data, firstBone, numBones);
+ }
+
+ if (mRecorder != NULL)
+ {
+ mRecorder->writeBoneBuffer(mBufferId, data, firstBone, numBones);
+ }
+ }
+
+ nvidia::apex::UserRenderBoneBuffer* getChild()
+ {
+ return mChild;
+ }
+
+protected:
+ nvidia::apex::UserRenderBoneBufferDesc mDescriptor;
+ nvidia::apex::UserRenderBoneBuffer* mChild;
+
+ static unsigned int boneBufferId;
+ unsigned int mBufferId;
+
+ RecordingRenderResourceManager::RecorderInterface* mRecorder;
+};
+
+unsigned int RecordingBoneBuffer::boneBufferId = 0;
+
+
+nvidia::apex::UserRenderBoneBuffer* RecordingRenderResourceManager::createBoneBuffer(const nvidia::apex::UserRenderBoneBufferDesc& desc)
+{
+ nvidia::apex::UserRenderBoneBuffer* child = NULL;
+ if (mChild != NULL)
+ {
+ child = mChild->createBoneBuffer(desc);
+ }
+
+ return new RecordingBoneBuffer(desc, child, mRecorder);
+}
+
+
+
+void RecordingRenderResourceManager::releaseBoneBuffer(nvidia::apex::UserRenderBoneBuffer& buffer)
+{
+ if (mChild != NULL)
+ {
+ mChild->releaseBoneBuffer(*reinterpret_cast<RecordingBoneBuffer&>(buffer).getChild());
+ }
+}
+
+
+
+nvidia::apex::UserRenderInstanceBuffer* RecordingRenderResourceManager::createInstanceBuffer(const nvidia::apex::UserRenderInstanceBufferDesc& desc)
+{
+ UNIMPLEMENTED;
+ if (mChild != NULL)
+ {
+ return mChild->createInstanceBuffer(desc);
+ }
+
+ return NULL;
+}
+
+
+
+void RecordingRenderResourceManager::releaseInstanceBuffer(nvidia::apex::UserRenderInstanceBuffer& buffer)
+{
+ if (mChild != NULL)
+ {
+ mChild->releaseInstanceBuffer(buffer);
+ }
+}
+
+
+
+nvidia::apex::UserRenderSpriteBuffer* RecordingRenderResourceManager::createSpriteBuffer(const nvidia::apex::UserRenderSpriteBufferDesc& desc)
+{
+ UNIMPLEMENTED;
+ if (mChild != NULL)
+ {
+ return mChild->createSpriteBuffer(desc);
+ }
+
+ return NULL;
+}
+
+
+
+void RecordingRenderResourceManager::releaseSpriteBuffer(nvidia::apex::UserRenderSpriteBuffer& buffer)
+{
+ if (mChild != NULL)
+ {
+ mChild->releaseSpriteBuffer(buffer);
+ }
+}
+
+
+nvidia::apex::UserRenderSurfaceBuffer* RecordingRenderResourceManager::createSurfaceBuffer(const nvidia::apex::UserRenderSurfaceBufferDesc& desc)
+{
+ UNIMPLEMENTED;
+ if (mChild != NULL)
+ {
+ return mChild->createSurfaceBuffer(desc);
+ }
+
+ return NULL;
+}
+
+
+
+void RecordingRenderResourceManager::releaseSurfaceBuffer(nvidia::apex::UserRenderSurfaceBuffer& buffer)
+{
+ if (mChild != NULL)
+ {
+ mChild->releaseSurfaceBuffer(buffer);
+ }
+}
+
+
+class RecordingRenderResource : public nvidia::apex::UserRenderResource
+{
+public:
+ RecordingRenderResource(const nvidia::apex::UserRenderResourceDesc& desc, nvidia::apex::UserRenderResourceManager* childRrm, RecordingRenderResourceManager::RecorderInterface* recorder) : mChild(NULL), mDescriptor(desc), mRecorder(recorder)
+ {
+ assert(desc.numVertexBuffers > 0);
+
+ mResourceId = resourceIds++;
+
+ vertexBufferOriginal.resize(desc.numVertexBuffers);
+ vertexBufferChild.resize(desc.numVertexBuffers);
+ for (size_t i = 0; i < vertexBufferOriginal.size(); i++)
+ {
+ vertexBufferOriginal[i] = desc.vertexBuffers[i];
+ vertexBufferChild[i] = reinterpret_cast<RecordingVertexBuffer*>(desc.vertexBuffers[i])->getChild();
+ }
+
+ mDescriptor.vertexBuffers = &vertexBufferOriginal[0];
+
+ nvidia::apex::UserRenderResourceDesc newDesc(desc);
+ newDesc.vertexBuffers = &vertexBufferChild[0];
+
+ if (desc.indexBuffer != NULL)
+ {
+ newDesc.indexBuffer = reinterpret_cast<RecordingIndexBuffer*>(desc.indexBuffer)->getChild();
+ }
+
+ if (desc.boneBuffer != NULL)
+ {
+ newDesc.boneBuffer = reinterpret_cast<RecordingBoneBuffer*>(desc.boneBuffer)->getChild();
+ }
+
+ if (childRrm != NULL)
+ {
+ mChild = childRrm->createResource(newDesc);
+ }
+
+ if (mRecorder != NULL)
+ {
+ mRecorder->createResource(mResourceId, desc);
+ }
+ }
+
+ ~RecordingRenderResource()
+ {
+
+ }
+
+
+ nvidia::apex::UserRenderResource* getChild()
+ {
+ return mChild;
+ }
+
+
+
+ void setVertexBufferRange(unsigned int firstVertex, unsigned int numVerts)
+ {
+ if (mChild != NULL)
+ {
+ mChild->setVertexBufferRange(firstVertex, numVerts);
+ }
+
+ mDescriptor.firstVertex = firstVertex;
+ mDescriptor.numVerts = numVerts;
+ }
+
+
+
+ void setIndexBufferRange(unsigned int firstIndex, unsigned int numIndices)
+ {
+ if (mChild != NULL)
+ {
+ mChild->setIndexBufferRange(firstIndex, numIndices);
+ }
+
+ mDescriptor.firstIndex = firstIndex;
+ mDescriptor.numIndices = numIndices;
+ }
+
+
+
+ void setBoneBufferRange(unsigned int firstBone, unsigned int numBones)
+ {
+ if (mChild != NULL)
+ {
+ mChild->setBoneBufferRange(firstBone, numBones);
+ }
+
+ mDescriptor.firstBone = firstBone;
+ mDescriptor.numBones = numBones;
+ }
+
+
+
+ void setInstanceBufferRange(unsigned int firstInstance, unsigned int numInstances)
+ {
+ if (mChild != NULL)
+ {
+ mChild->setInstanceBufferRange(firstInstance, numInstances);
+ }
+
+ mDescriptor.firstInstance = firstInstance;
+ mDescriptor.numInstances = numInstances;
+ }
+
+
+
+ void setSpriteBufferRange(unsigned int firstSprite, unsigned int numSprites)
+ {
+ if (mChild != NULL)
+ {
+ mChild->setSpriteBufferRange(firstSprite, numSprites);
+ }
+
+ mDescriptor.firstSprite = firstSprite;
+ mDescriptor.numSprites = numSprites;
+ }
+
+
+
+
+ void setMaterial(void* material)
+ {
+ if (mChild != NULL)
+ {
+ mChild->setMaterial(material);
+ }
+
+ mDescriptor.material = material;
+ }
+
+
+
+
+ unsigned int getNbVertexBuffers() const
+ {
+ return mDescriptor.numVertexBuffers;
+ }
+
+
+
+ nvidia::apex::UserRenderVertexBuffer* getVertexBuffer(unsigned int index) const
+ {
+ return mDescriptor.vertexBuffers[index];
+ }
+
+
+
+ nvidia::apex::UserRenderIndexBuffer* getIndexBuffer() const
+ {
+ return mDescriptor.indexBuffer;
+ }
+
+
+
+
+ nvidia::apex::UserRenderBoneBuffer* getBoneBuffer() const
+ {
+ return mDescriptor.boneBuffer;
+ }
+
+
+
+ nvidia::apex::UserRenderInstanceBuffer* getInstanceBuffer() const
+ {
+ return mDescriptor.instanceBuffer;
+ }
+
+
+
+ nvidia::apex::UserRenderSpriteBuffer* getSpriteBuffer() const
+ {
+ return mDescriptor.spriteBuffer;
+ }
+
+ void render()
+ {
+ if (mRecorder != NULL)
+ {
+ mRecorder->renderResource(mResourceId, mDescriptor);
+ }
+ }
+
+
+protected:
+ std::vector<nvidia::apex::UserRenderVertexBuffer*> vertexBufferOriginal;
+ std::vector<nvidia::apex::UserRenderVertexBuffer*> vertexBufferChild;
+ nvidia::apex::UserRenderResource* mChild;
+
+ nvidia::apex::UserRenderResourceDesc mDescriptor;
+
+ static unsigned int resourceIds;
+ unsigned int mResourceId;
+
+ RecordingRenderResourceManager::RecorderInterface* mRecorder;
+};
+
+unsigned int RecordingRenderResource::resourceIds = 0;
+
+
+
+nvidia::apex::UserRenderResource* RecordingRenderResourceManager::createResource(const nvidia::apex::UserRenderResourceDesc& desc)
+{
+ return new RecordingRenderResource(desc, mChild, mRecorder);
+}
+
+
+
+void RecordingRenderResourceManager::releaseResource(nvidia::apex::UserRenderResource& resource)
+{
+ if (mChild != NULL)
+ {
+ mChild->releaseResource(*reinterpret_cast<RecordingRenderResource&>(resource).getChild());
+ }
+
+ delete &resource;
+}
+
+
+
+unsigned int RecordingRenderResourceManager::getMaxBonesForMaterial(void* material)
+{
+ unsigned int maxBones = 60; // whatever
+ if (mChild != NULL)
+ {
+ maxBones = mChild->getMaxBonesForMaterial(material);
+ }
+
+ if (mRecorder != NULL)
+ {
+ mRecorder->setMaxBonesForMaterial(material, maxBones);
+ }
+
+ return maxBones;
+}
+
+
+
+
+RecordingRenderer::RecordingRenderer(nvidia::apex::UserRenderer* child, RecordingRenderResourceManager::RecorderInterface* recorder) : mChild(child), mRecorder(recorder)
+{
+
+}
+
+
+
+RecordingRenderer::~RecordingRenderer()
+{
+
+}
+
+
+
+void RecordingRenderer::renderResource(const nvidia::apex::RenderContext& context)
+{
+ RecordingRenderResource* resource = reinterpret_cast<RecordingRenderResource*>(context.renderResource);
+
+ resource->render();
+
+ nvidia::apex::UserRenderResource* child = resource->getChild();
+ if (mChild != NULL && child != NULL)
+ {
+ nvidia::apex::RenderContext newContext(context);
+ newContext.renderResource = child;
+
+ mChild->renderResource(newContext);
+ }
+}
+
+
+
+
+FileRecorder::FileRecorder(const char* filename)
+{
+
+ if (filename != NULL)
+ {
+ mOutputFile = fopen(filename, "w");
+ assert(mOutputFile);
+
+ if (mOutputFile != NULL)
+ {
+ time_t curtime;
+ ::time(&curtime);
+
+ fprintf(mOutputFile, "# Logfile created on %s\n\n", ctime(&curtime));
+ }
+ }
+ else
+ {
+ // console
+ mOutputFile = stdout;
+
+#if PX_WINDOWS_FAMILY
+ //open a console for printf:
+ if (AllocConsole())
+ {
+ FILE* stream;
+ freopen_s(&stream, "CONOUT$", "wb", stdout);
+ freopen_s(&stream, "CONOUT$", "wb", stderr);
+
+ SetConsoleTitle("Recording Resource Manager Output");
+ //SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED);
+
+ CONSOLE_SCREEN_BUFFER_INFO coninfo;
+ GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
+ coninfo.dwSize.Y = 1000;
+ SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
+ }
+#endif
+ }
+}
+
+
+FileRecorder::~FileRecorder()
+{
+ if (mOutputFile != stdout && mOutputFile != NULL)
+ {
+ fclose(mOutputFile);
+ }
+}
+
+
+#define WRITE_ITEM(_A) writeElem(#_A, _A)
+#define WRITE_DESC_ELEM(_A) writeElem(#_A, (uint32_t)desc._A)
+#define WRITE_REQUEST(_A) if (desc.buffersRequest[nvidia::apex::RenderVertexSemantic::_A] != nvidia::apex::RenderDataFormat::UNSPECIFIED) writeElem(#_A, desc.buffersRequest[nvidia::apex::RenderVertexSemantic::_A])
+
+
+
+void FileRecorder::createVertexBuffer(unsigned int id, const nvidia::apex::UserRenderVertexBufferDesc& desc)
+{
+ fprintf(mOutputFile, "VertexBuffer[%d]::create: ", id);
+
+ WRITE_DESC_ELEM(maxVerts);
+ WRITE_DESC_ELEM(hint);
+
+ WRITE_REQUEST(POSITION);
+ WRITE_REQUEST(NORMAL);
+ WRITE_REQUEST(TANGENT);
+ WRITE_REQUEST(BINORMAL);
+ WRITE_REQUEST(COLOR);
+ WRITE_REQUEST(TEXCOORD0);
+ WRITE_REQUEST(TEXCOORD1);
+ WRITE_REQUEST(TEXCOORD2);
+ WRITE_REQUEST(TEXCOORD3);
+ WRITE_REQUEST(BONE_INDEX);
+ WRITE_REQUEST(BONE_WEIGHT);
+ WRITE_REQUEST(DISPLACEMENT_TEXCOORD);
+ WRITE_REQUEST(DISPLACEMENT_FLAGS);
+
+ WRITE_DESC_ELEM(numCustomBuffers);
+ // PH: not done on purpose (yet)
+ //void** customBuffersIdents;
+ //RenderDataFormat::Enum* customBuffersRequest;
+ WRITE_DESC_ELEM(moduleIdentifier);
+ WRITE_DESC_ELEM(uvOrigin);
+ WRITE_DESC_ELEM(canBeShared);
+
+ fprintf(mOutputFile, "\n");
+
+#if PX_X86
+ // PH: Make sure that if the size of the descriptor changes, we get a compile error here and adapt the WRITE_REQUESTs from above accordingly
+ PX_COMPILE_TIME_ASSERT(sizeof(desc) == 4 + 4 + (13 * 4) + 4 + sizeof(void*) + sizeof(void*) + 4 + 4 + 1 + 1 + 2/*padding*/ + sizeof(void*) );
+#endif
+}
+
+
+
+#define WRITE_DATA_ITEM(_A) writeElem(#_A, semanticData._A)
+void FileRecorder::writeVertexBuffer(unsigned int id, const nvidia::apex::RenderVertexBufferData& data, unsigned int firstVertex, unsigned int numVertices)
+{
+ fprintf(mOutputFile, "VertexBuffer[%d]::write: ", id);
+ WRITE_ITEM(firstVertex);
+ WRITE_ITEM(numVertices);
+
+ WRITE_ITEM(data.moduleId);
+ WRITE_ITEM(data.numModuleSpecificSemantics);
+
+ fprintf(mOutputFile, "\n");
+
+#if PX_X86
+ PX_COMPILE_TIME_ASSERT(sizeof(nvidia::apex::RenderSemanticData) == (sizeof(void*) + 4 + sizeof(void*) + 4 + 4 + 1 + 3/*padding*/));
+#endif
+
+ for (unsigned int i = 0; i < nvidia::apex::RenderVertexSemantic::NUM_SEMANTICS; i++)
+ {
+ const nvidia::apex::RenderSemanticData& semanticData = data.getSemanticData(nvidia::apex::RenderVertexSemantic::Enum(i));
+ if (semanticData.format != nvidia::apex::RenderDataFormat::UNSPECIFIED)
+ {
+ fprintf(mOutputFile, " [%d]: ", i);
+ WRITE_DATA_ITEM(stride);
+ WRITE_DATA_ITEM(format);
+ WRITE_DATA_ITEM(srcFormat);
+ fprintf(mOutputFile, "\n");
+
+ //writeBufferData(semanticData.data, semanticData.stride, numVertices, semanticData.format);
+ }
+ }
+
+ PX_COMPILE_TIME_ASSERT(nvidia::apex::RenderVertexSemantic::NUM_SEMANTICS == 13);
+
+#if PX_X86
+ PX_COMPILE_TIME_ASSERT(sizeof(data) == sizeof(nvidia::apex::RenderSemanticData) * nvidia::apex::RenderVertexSemantic::NUM_SEMANTICS + 4 + sizeof(void*) + 4 + sizeof(void*) + 4);
+#endif
+}
+#undef WRITE_DATA_ITEM
+
+
+
+void FileRecorder::releaseVertexBuffer(unsigned int id)
+{
+ fprintf(mOutputFile, "VertexBuffer[%d]::release\n", id);
+}
+
+
+
+
+void FileRecorder::createIndexBuffer(unsigned int id, const nvidia::apex::UserRenderIndexBufferDesc& desc)
+{
+ fprintf(mOutputFile, "IndexBuffer[%d]::create: ", id);
+
+ WRITE_DESC_ELEM(maxIndices);
+ WRITE_DESC_ELEM(hint);
+ WRITE_DESC_ELEM(format);
+ WRITE_DESC_ELEM(primitives);
+ WRITE_DESC_ELEM(registerInCUDA);
+ //WRITE_DESC_ELEM(interopContext);
+
+ fprintf(mOutputFile, "\n");
+
+#if PX_X86
+ PX_COMPILE_TIME_ASSERT(sizeof(desc) == 4 + 4 + 4 + 4 + 1 + 3/*padding*/ + sizeof(void*));
+#endif
+}
+
+
+
+void FileRecorder::writeIndexBuffer(unsigned int id, const void* /*srcData*/, uint32_t /*srcStride*/, unsigned int firstDestElement, unsigned int numElements, nvidia::apex::RenderDataFormat::Enum /*format*/)
+{
+ fprintf(mOutputFile, "IndexBuffer[%d]::write ", id);
+ WRITE_ITEM(firstDestElement);
+ WRITE_ITEM(numElements);
+
+ //writeBufferData(srcData, srcStride, numElements, format);
+
+ fprintf(mOutputFile, "\n");
+}
+
+
+
+void FileRecorder::releaseIndexBuffer(unsigned int id)
+{
+ fprintf(mOutputFile, "IndexBuffer[%d]::release\n", id);
+}
+
+
+
+void FileRecorder::createBoneBuffer(unsigned int id, const nvidia::apex::UserRenderBoneBufferDesc& /*desc*/)
+{
+ fprintf(mOutputFile, "BoneBuffer[%d]::create\n", id);
+}
+
+
+
+void FileRecorder::writeBoneBuffer(unsigned int id, const nvidia::apex::RenderBoneBufferData& /*data*/, unsigned int /*firstBone*/, unsigned int /*numBones*/)
+{
+ fprintf(mOutputFile, "BoneBuffer[%d]::write\n", id);
+}
+
+
+
+void FileRecorder::releaseBoneBuffer(unsigned int id)
+{
+ fprintf(mOutputFile, "BoneBuffer[%d]::release\n", id);
+}
+
+
+
+void FileRecorder::createResource(unsigned int id, const nvidia::apex::UserRenderResourceDesc& /*desc*/)
+{
+ fprintf(mOutputFile, "Resource[%d]::create\n", id);
+}
+
+
+
+void FileRecorder::renderResource(unsigned int id, const nvidia::apex::UserRenderResourceDesc& /*desc*/)
+{
+ fprintf(mOutputFile, "Resource[%d]::render\n", id);
+}
+
+
+
+void FileRecorder::releaseResource(unsigned int id)
+{
+ fprintf(mOutputFile, "Resource[%d]::release\n", id);
+}
+
+
+
+void FileRecorder::setMaxBonesForMaterial(void* material, unsigned int maxBones)
+{
+ fprintf(mOutputFile, "MaterialMaxBones[%p]=%d\n", material, maxBones);
+}
+
+
+
+void FileRecorder::writeElem(const char* name, unsigned int value)
+{
+ fprintf(mOutputFile, "%s=%d ", name, value);
+}
+
+
+void FileRecorder::writeBufferData(const void* data, unsigned int stride, unsigned int numElements, nvidia::apex::RenderDataFormat::Enum format)
+{
+ switch (format)
+ {
+ case nvidia::apex::RenderDataFormat::FLOAT2:
+ writeBufferDataFloat(data, stride, numElements, 2);
+ break;
+ case nvidia::apex::RenderDataFormat::FLOAT3:
+ writeBufferDataFloat(data, stride, numElements, 3);
+ break;
+ case nvidia::apex::RenderDataFormat::FLOAT4:
+ writeBufferDataFloat(data, stride, numElements, 4);
+ break;
+ case nvidia::apex::RenderDataFormat::USHORT4:
+ writeBufferDataShort(data, stride, numElements, 4);
+ break;
+ case nvidia::apex::RenderDataFormat::UINT1:
+ writeBufferDataLong(data, stride, numElements, 1);
+ break;
+ default:
+ UNIMPLEMENTED;
+ break;
+ }
+}
+
+
+
+
+void FileRecorder::writeBufferDataFloat(const void* data, unsigned int stride, unsigned int numElements, unsigned int numFloatsPerDataSet)
+{
+ const char* startData = (const char*)data;
+ for (unsigned int i = 0; i < numElements; i++)
+ {
+ const float* elemData = (const float*)(startData + stride * i);
+
+ fprintf(mOutputFile, "(");
+ for (unsigned int j = 0; j < numFloatsPerDataSet; j++)
+ {
+ fprintf(mOutputFile, "%f ", elemData[j]);
+ }
+ fprintf(mOutputFile, "),");
+ }
+}
+
+
+void FileRecorder::writeBufferDataShort(const void* data, unsigned int stride, unsigned int numElements, unsigned int numFloatsPerDataSet)
+{
+ const char* startData = (const char*)data;
+ for (unsigned int i = 0; i < numElements; i++)
+ {
+ const short* elemData = (const short*)(startData + stride * i);
+
+ fprintf(mOutputFile, "(");
+ for (unsigned int j = 0; j < numFloatsPerDataSet; j++)
+ {
+ fprintf(mOutputFile, "%d ", elemData[j]);
+ }
+ fprintf(mOutputFile, "),");
+ }
+}
+
+
+void FileRecorder::writeBufferDataLong(const void* data, unsigned int stride, unsigned int numElements, unsigned int numFloatsPerDataSet)
+{
+ const char* startData = (const char*)data;
+ for (unsigned int i = 0; i < numElements; i++)
+ {
+ const long* elemData = (const long*)(startData + stride * i);
+
+ fprintf(mOutputFile, "(");
+ for (unsigned int j = 0; j < numFloatsPerDataSet; j++)
+ {
+ fprintf(mOutputFile, "%d ", (int)elemData[j]);
+ }
+ fprintf(mOutputFile, "),");
+ }
+}
diff --git a/APEX_1.4/shared/external/src/SampleApexRenderResources.cpp b/APEX_1.4/shared/external/src/SampleApexRenderResources.cpp
new file mode 100644
index 00000000..fdcede8f
--- /dev/null
+++ b/APEX_1.4/shared/external/src/SampleApexRenderResources.cpp
@@ -0,0 +1,1865 @@
+/*
+ * 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 <SampleApexRenderResources.h>
+
+#include <Renderer.h>
+
+#include <RendererVertexBuffer.h>
+#include <RendererVertexBufferDesc.h>
+
+#include <RendererIndexBuffer.h>
+#include <RendererIndexBufferDesc.h>
+
+#include <RendererInstanceBuffer.h>
+#include <RendererInstanceBufferDesc.h>
+
+#include <RendererTexture.h>
+#include <RendererTextureDesc.h>
+
+#include <RendererMesh.h>
+#include <RendererMeshDesc.h>
+
+#include <RendererMaterial.h>
+#include <RendererMaterialDesc.h>
+#include <RendererMaterialInstance.h>
+
+#if !USE_RENDERER_MATERIAL
+#include <SampleMaterialAsset.h>
+#endif
+
+#include <RenderContext.h>
+#include <UserRenderIndexBufferDesc.h>
+#include <UserRenderInstanceBuffer.h>
+#include <UserRenderResourceDesc.h>
+#include <UserRenderSpriteBufferDesc.h>
+#include <UserRenderSurfaceBufferDesc.h>
+#include <UserRenderVertexBufferDesc.h>
+
+
+static RendererVertexBuffer::Hint convertFromApexVB(nvidia::apex::RenderBufferHint::Enum apexHint)
+{
+ RendererVertexBuffer::Hint vbhint = RendererVertexBuffer::HINT_STATIC;
+ if (apexHint == nvidia::apex::RenderBufferHint::DYNAMIC || apexHint == nvidia::apex::RenderBufferHint::STREAMING)
+ {
+ vbhint = RendererVertexBuffer::HINT_DYNAMIC;
+ }
+ return vbhint;
+}
+
+static RendererVertexBuffer::Semantic convertFromApexVB(nvidia::apex::RenderVertexSemantic::Enum apexSemantic)
+{
+ RendererVertexBuffer::Semantic semantic = RendererVertexBuffer::NUM_SEMANTICS;
+ switch (apexSemantic)
+ {
+ case nvidia::apex::RenderVertexSemantic::POSITION:
+ semantic = RendererVertexBuffer::SEMANTIC_POSITION;
+ break;
+ case nvidia::apex::RenderVertexSemantic::NORMAL:
+ semantic = RendererVertexBuffer::SEMANTIC_NORMAL;
+ break;
+ case nvidia::apex::RenderVertexSemantic::TANGENT:
+ semantic = RendererVertexBuffer::SEMANTIC_TANGENT;
+ break;
+ case nvidia::apex::RenderVertexSemantic::COLOR:
+ semantic = RendererVertexBuffer::SEMANTIC_COLOR;
+ break;
+ case nvidia::apex::RenderVertexSemantic::TEXCOORD0:
+ semantic = RendererVertexBuffer::SEMANTIC_TEXCOORD0;
+ break;
+ case nvidia::apex::RenderVertexSemantic::TEXCOORD1:
+ semantic = RendererVertexBuffer::SEMANTIC_TEXCOORD1;
+ break;
+ case nvidia::apex::RenderVertexSemantic::TEXCOORD2:
+ semantic = RendererVertexBuffer::SEMANTIC_TEXCOORD2;
+ break;
+ case nvidia::apex::RenderVertexSemantic::TEXCOORD3:
+ semantic = RendererVertexBuffer::SEMANTIC_TEXCOORD3;
+ break;
+ case nvidia::apex::RenderVertexSemantic::BONE_INDEX:
+ semantic = RendererVertexBuffer::SEMANTIC_BONEINDEX;
+ break;
+ case nvidia::apex::RenderVertexSemantic::BONE_WEIGHT:
+ semantic = RendererVertexBuffer::SEMANTIC_BONEWEIGHT;
+ break;
+ case nvidia::apex::RenderVertexSemantic::DISPLACEMENT_TEXCOORD:
+ semantic = RendererVertexBuffer::SEMANTIC_DISPLACEMENT_TEXCOORD;
+ break;
+ case nvidia::apex::RenderVertexSemantic::DISPLACEMENT_FLAGS:
+ semantic = RendererVertexBuffer::SEMANTIC_DISPLACEMENT_FLAGS;
+ break;
+ default:
+ //PX_ASSERT(semantic < RendererVertexBuffer::NUM_SEMANTICS);
+ break;
+ }
+ return semantic;
+}
+
+static RendererVertexBuffer::Format convertFromApexVB(nvidia::apex::RenderDataFormat::Enum apexFormat)
+{
+ RendererVertexBuffer::Format format = RendererVertexBuffer::NUM_FORMATS;
+ switch (apexFormat)
+ {
+ case nvidia::apex::RenderDataFormat::FLOAT1:
+ format = RendererVertexBuffer::FORMAT_FLOAT1;
+ break;
+ case nvidia::apex::RenderDataFormat::FLOAT2:
+ format = RendererVertexBuffer::FORMAT_FLOAT2;
+ break;
+ case nvidia::apex::RenderDataFormat::FLOAT3:
+ format = RendererVertexBuffer::FORMAT_FLOAT3;
+ break;
+ case nvidia::apex::RenderDataFormat::FLOAT4:
+ format = RendererVertexBuffer::FORMAT_FLOAT4;
+ break;
+ case nvidia::apex::RenderDataFormat::B8G8R8A8:
+ format = RendererVertexBuffer::FORMAT_COLOR_BGRA;
+ break;
+ case nvidia::apex::RenderDataFormat::UINT1:
+ case nvidia::apex::RenderDataFormat::UBYTE4:
+ case nvidia::apex::RenderDataFormat::R8G8B8A8:
+ format = RendererVertexBuffer::FORMAT_COLOR_RGBA;
+ break;
+ case nvidia::apex::RenderDataFormat::USHORT1:
+ case nvidia::apex::RenderDataFormat::USHORT2:
+ case nvidia::apex::RenderDataFormat::USHORT3:
+ case nvidia::apex::RenderDataFormat::USHORT4:
+ format = RendererVertexBuffer::FORMAT_USHORT4;
+ break;
+ default:
+ //PX_ASSERT(format < RendererVertexBuffer::NUM_FORMATS || apexFormat==RenderDataFormat::UNSPECIFIED);
+ break;
+ }
+ return format;
+}
+
+#if 0 // Unused
+static RendererVertexBuffer::Semantic convertFromApexSB(nvidia::apex::RenderSpriteSemantic::Enum apexSemantic)
+{
+ RendererVertexBuffer::Semantic semantic = RendererVertexBuffer::NUM_SEMANTICS;
+ switch (apexSemantic)
+ {
+ case nvidia::apex::RenderSpriteSemantic::POSITION:
+ semantic = RendererVertexBuffer::SEMANTIC_POSITION;
+ break;
+ case nvidia::apex::RenderSpriteSemantic::COLOR:
+ semantic = RendererVertexBuffer::SEMANTIC_COLOR;
+ break;
+ case nvidia::apex::RenderSpriteSemantic::VELOCITY:
+ semantic = RendererVertexBuffer::SEMANTIC_NORMAL;
+ break;
+ case nvidia::apex::RenderSpriteSemantic::SCALE:
+ semantic = RendererVertexBuffer::SEMANTIC_TANGENT;
+ break;
+ case nvidia::apex::RenderSpriteSemantic::LIFE_REMAIN:
+ semantic = RendererVertexBuffer::SEMANTIC_TEXCOORD0;
+ break;
+ case nvidia::apex::RenderSpriteSemantic::DENSITY:
+ semantic = RendererVertexBuffer::SEMANTIC_TEXCOORD1;
+ break;
+ case nvidia::apex::RenderSpriteSemantic::SUBTEXTURE:
+ semantic = RendererVertexBuffer::SEMANTIC_TEXCOORD2;
+ break;
+ case nvidia::apex::RenderSpriteSemantic::ORIENTATION:
+ semantic = RendererVertexBuffer::SEMANTIC_TEXCOORD3;
+ break;
+ default:
+ //PX_ASSERT(semantic < RendererVertexBuffer::NUM_SEMANTICS);
+ break;
+ }
+ return semantic;
+}
+#endif
+
+static RendererVertexBuffer::Semantic convertFromApexLayoutSB(const nvidia::apex::RenderSpriteLayoutElement::Enum layout)
+{
+ RendererVertexBuffer::Semantic semantic = RendererVertexBuffer::NUM_SEMANTICS;
+ switch (layout)
+ {
+ case nvidia::apex::RenderSpriteLayoutElement::POSITION_FLOAT3:
+ semantic = RendererVertexBuffer::SEMANTIC_POSITION;
+ break;
+ case nvidia::apex::RenderSpriteLayoutElement::COLOR_BGRA8:
+ case nvidia::apex::RenderSpriteLayoutElement::COLOR_RGBA8:
+ case nvidia::apex::RenderSpriteLayoutElement::COLOR_FLOAT4:
+ semantic = RendererVertexBuffer::SEMANTIC_COLOR;
+ break;
+ case nvidia::apex::RenderSpriteLayoutElement::VELOCITY_FLOAT3:
+ semantic = RendererVertexBuffer::SEMANTIC_NORMAL;
+ break;
+ case nvidia::apex::RenderSpriteLayoutElement::SCALE_FLOAT2:
+ semantic = RendererVertexBuffer::SEMANTIC_TANGENT;
+ break;
+ case nvidia::apex::RenderSpriteLayoutElement::LIFE_REMAIN_FLOAT1:
+ semantic = RendererVertexBuffer::SEMANTIC_TEXCOORD0;
+ break;
+ case nvidia::apex::RenderSpriteLayoutElement::DENSITY_FLOAT1:
+ semantic = RendererVertexBuffer::SEMANTIC_TEXCOORD1;
+ break;
+ case nvidia::apex::RenderSpriteLayoutElement::SUBTEXTURE_FLOAT1:
+ semantic = RendererVertexBuffer::SEMANTIC_TEXCOORD2;
+ break;
+ case nvidia::apex::RenderSpriteLayoutElement::ORIENTATION_FLOAT1:
+ semantic = RendererVertexBuffer::SEMANTIC_TEXCOORD3;
+ break;
+ default:
+ //PX_ASSERT(semantic < RendererVertexBuffer::NUM_SEMANTICS);
+ break;
+ }
+ return semantic;
+}
+
+static RendererVertexBuffer::Format convertFromApexLayoutVB(const nvidia::apex::RenderSpriteLayoutElement::Enum layout)
+{
+ RendererVertexBuffer::Format format = RendererVertexBuffer::NUM_FORMATS;
+ switch (layout)
+ {
+ case nvidia::apex::RenderSpriteLayoutElement::POSITION_FLOAT3:
+ format = RendererVertexBuffer::FORMAT_FLOAT3;
+ break;
+ case nvidia::apex::RenderSpriteLayoutElement::COLOR_BGRA8:
+ format = RendererVertexBuffer::FORMAT_COLOR_BGRA;
+ break;
+ case nvidia::apex::RenderSpriteLayoutElement::COLOR_RGBA8:
+ format = RendererVertexBuffer::FORMAT_COLOR_RGBA;
+ break;
+ case nvidia::apex::RenderSpriteLayoutElement::COLOR_FLOAT4:
+ format = RendererVertexBuffer::FORMAT_FLOAT4;
+ break;
+ case nvidia::apex::RenderSpriteLayoutElement::VELOCITY_FLOAT3:
+ format = RendererVertexBuffer::FORMAT_FLOAT3;
+ break;
+ case nvidia::apex::RenderSpriteLayoutElement::SCALE_FLOAT2:
+ format = RendererVertexBuffer::FORMAT_FLOAT2;
+ break;
+ case nvidia::apex::RenderSpriteLayoutElement::LIFE_REMAIN_FLOAT1:
+ format = RendererVertexBuffer::FORMAT_FLOAT1;
+ break;
+ case nvidia::apex::RenderSpriteLayoutElement::DENSITY_FLOAT1:
+ format = RendererVertexBuffer::FORMAT_FLOAT1;
+ break;
+ case nvidia::apex::RenderSpriteLayoutElement::SUBTEXTURE_FLOAT1:
+ format = RendererVertexBuffer::FORMAT_FLOAT1;
+ break;
+ case nvidia::apex::RenderSpriteLayoutElement::ORIENTATION_FLOAT1:
+ format = RendererVertexBuffer::FORMAT_FLOAT1;
+ break;
+ default:
+ //PX_ASSERT(semantic < RendererVertexBuffer::NUM_SEMANTICS);
+ break;
+ }
+ return format;
+}
+
+static RendererIndexBuffer::Hint convertFromApexIB(nvidia::apex::RenderBufferHint::Enum apexHint)
+{
+ RendererIndexBuffer::Hint ibhint = RendererIndexBuffer::HINT_STATIC;
+ if (apexHint == nvidia::apex::RenderBufferHint::DYNAMIC || apexHint == nvidia::apex::RenderBufferHint::STREAMING)
+ {
+ ibhint = RendererIndexBuffer::HINT_DYNAMIC;
+ }
+ return ibhint;
+}
+
+static RendererIndexBuffer::Format convertFromApexIB(nvidia::apex::RenderDataFormat::Enum apexFormat)
+{
+ RendererIndexBuffer::Format format = RendererIndexBuffer::NUM_FORMATS;
+ switch (apexFormat)
+ {
+ case nvidia::apex::RenderDataFormat::UBYTE1:
+ PX_ASSERT(0); /* UINT8 Indices not in HW. */
+ break;
+ case nvidia::apex::RenderDataFormat::USHORT1:
+ format = RendererIndexBuffer::FORMAT_UINT16;
+ break;
+ case nvidia::apex::RenderDataFormat::UINT1:
+ format = RendererIndexBuffer::FORMAT_UINT32;
+ break;
+ default:
+ PX_ASSERT(format < RendererIndexBuffer::NUM_FORMATS);
+ }
+ return format;
+}
+
+static RendererTexture::Format convertFromApexSB(nvidia::apex::RenderDataFormat::Enum apexFormat)
+{
+ RendererTexture::Format format = RendererTexture::NUM_FORMATS;
+ switch (apexFormat)
+ {
+ case nvidia::apex::RenderDataFormat::FLOAT1:
+ format = RendererTexture::FORMAT_R32F;
+ break;
+ case nvidia::apex::RenderDataFormat::R32G32B32A32_FLOAT:
+ case nvidia::apex::RenderDataFormat::B32G32R32A32_FLOAT:
+ case nvidia::apex::RenderDataFormat::FLOAT4:
+ format = RendererTexture::FORMAT_R32F_G32F_B32G_A32F;
+ break;
+ case nvidia::apex::RenderDataFormat::HALF4:
+ format = RendererTexture::FORMAT_R16F_G16F_B16G_A16F;
+ break;
+ default:
+ PX_ASSERT(format < RendererTexture::NUM_FORMATS);
+ break;
+ }
+ return format;
+}
+
+static RendererMesh::Primitive convertFromApex(nvidia::apex::RenderPrimitiveType::Enum apexPrimitive)
+{
+ RendererMesh::Primitive primitive = RendererMesh::NUM_PRIMITIVES;
+ switch (apexPrimitive)
+ {
+ case nvidia::apex::RenderPrimitiveType::TRIANGLES:
+ primitive = RendererMesh::PRIMITIVE_TRIANGLES;
+ break;
+ case nvidia::apex::RenderPrimitiveType::TRIANGLE_STRIP:
+ primitive = RendererMesh::PRIMITIVE_TRIANGLE_STRIP;
+ break;
+
+ case nvidia::apex::RenderPrimitiveType::LINES:
+ primitive = RendererMesh::PRIMITIVE_LINES;
+ break;
+ case nvidia::apex::RenderPrimitiveType::LINE_STRIP:
+ primitive = RendererMesh::PRIMITIVE_LINE_STRIP;
+ break;
+
+ case nvidia::apex::RenderPrimitiveType::POINTS:
+ primitive = RendererMesh::PRIMITIVE_POINTS;
+ break;
+ case nvidia::apex::RenderPrimitiveType::POINT_SPRITES:
+ primitive = RendererMesh::PRIMITIVE_POINT_SPRITES;
+ break;
+
+ case nvidia::apex::RenderPrimitiveType::UNKNOWN: // Make compiler happy
+ break;
+ }
+ PX_ASSERT(primitive < RendererMesh::NUM_PRIMITIVES);
+ return primitive;
+}
+
+/*********************************
+* SampleApexRendererVertexBuffer *
+*********************************/
+
+SampleApexRendererVertexBuffer::SampleApexRendererVertexBuffer(SampleRenderer::Renderer& renderer, const nvidia::apex::UserRenderVertexBufferDesc& desc) :
+ m_renderer(renderer)
+{
+ m_vertexbuffer = 0;
+ SampleRenderer::RendererVertexBufferDesc vbdesc;
+ vbdesc.hint = convertFromApexVB(desc.hint);
+ for (uint32_t i = 0; i < nvidia::apex::RenderVertexSemantic::NUM_SEMANTICS; i++)
+ {
+ nvidia::apex::RenderVertexSemantic::Enum apexSemantic = nvidia::apex::RenderVertexSemantic::Enum(i);
+ nvidia::apex::RenderDataFormat::Enum apexFormat = desc.buffersRequest[i];
+ if ((apexSemantic == nvidia::apex::RenderVertexSemantic::NORMAL || apexSemantic == nvidia::apex::RenderVertexSemantic::BINORMAL) && apexFormat != nvidia::apex::RenderDataFormat::UNSPECIFIED)
+ {
+ PX_ASSERT(apexFormat == nvidia::apex::RenderDataFormat::FLOAT3 || apexFormat == nvidia::apex::RenderDataFormat::BYTE_SNORM3);
+ // always use FLOAT3 for normals and binormals
+ apexFormat = nvidia::apex::RenderDataFormat::FLOAT3;
+ }
+ else if (apexSemantic == nvidia::apex::RenderVertexSemantic::TANGENT && apexFormat != nvidia::apex::RenderDataFormat::UNSPECIFIED)
+ {
+ PX_ASSERT(apexFormat == nvidia::apex::RenderDataFormat::FLOAT3 || apexFormat == nvidia::apex::RenderDataFormat::BYTE_SNORM3 ||
+ apexFormat == nvidia::apex::RenderDataFormat::FLOAT4 || apexFormat == nvidia::apex::RenderDataFormat::BYTE_SNORM4);
+ // always use FLOAT4 for tangents!!!
+ apexFormat = nvidia::apex::RenderDataFormat::FLOAT4;
+ }
+ else if (apexSemantic == nvidia::apex::RenderVertexSemantic::BONE_INDEX && apexFormat != nvidia::apex::RenderDataFormat::UNSPECIFIED)
+ {
+ PX_ASSERT(apexFormat == nvidia::apex::RenderDataFormat::USHORT1
+ || apexFormat == nvidia::apex::RenderDataFormat::USHORT2
+ || apexFormat == nvidia::apex::RenderDataFormat::USHORT3
+ || apexFormat == nvidia::apex::RenderDataFormat::USHORT4);
+
+ // use either USHORT1 for destruction or USHORT4 for everything else. fill with 0 in writeBuffer
+ if (apexFormat == nvidia::apex::RenderDataFormat::USHORT2 || apexFormat == nvidia::apex::RenderDataFormat::USHORT3)
+ {
+ apexFormat = nvidia::apex::RenderDataFormat::USHORT4;
+ }
+ }
+ else if (apexSemantic == nvidia::apex::RenderVertexSemantic::BONE_WEIGHT && apexFormat != nvidia::apex::RenderDataFormat::UNSPECIFIED)
+ {
+ PX_ASSERT(apexFormat == nvidia::apex::RenderDataFormat::FLOAT1
+ || apexFormat == nvidia::apex::RenderDataFormat::FLOAT2
+ || apexFormat == nvidia::apex::RenderDataFormat::FLOAT3
+ || apexFormat == nvidia::apex::RenderDataFormat::FLOAT4);
+
+ // use either FLOAT1 for destruction or FLOAT4 for everything else. fill with 0.0 in writeBuffer
+ if (apexFormat == nvidia::apex::RenderDataFormat::FLOAT2 || apexFormat == nvidia::apex::RenderDataFormat::FLOAT3)
+ {
+ apexFormat = nvidia::apex::RenderDataFormat::FLOAT4;
+ }
+ }
+ RendererVertexBuffer::Semantic semantic = convertFromApexVB(apexSemantic);
+ RendererVertexBuffer::Format format = convertFromApexVB(apexFormat);
+ if (semantic < RendererVertexBuffer::NUM_SEMANTICS && format < RendererVertexBuffer::NUM_FORMATS)
+ {
+ vbdesc.semanticFormats[semantic] = format;
+ }
+ }
+#if APEX_CUDA_SUPPORT
+ vbdesc.registerInCUDA = desc.registerInCUDA;
+ vbdesc.interopContext = desc.interopContext;
+#endif
+ vbdesc.maxVertices = desc.maxVerts;
+ m_vertexbuffer = m_renderer.createVertexBuffer(vbdesc);
+ PX_ASSERT(m_vertexbuffer);
+ m_uvOrigin = desc.uvOrigin;
+}
+
+SampleApexRendererVertexBuffer::~SampleApexRendererVertexBuffer(void)
+{
+ if (m_vertexbuffer)
+ {
+ m_vertexbuffer->release();
+ }
+}
+
+bool SampleApexRendererVertexBuffer::getInteropResourceHandle(CUgraphicsResource& handle)
+{
+#if APEX_CUDA_SUPPORT
+ if (m_vertexbuffer)
+ {
+ return m_vertexbuffer->getInteropResourceHandle(handle);
+ }
+ else
+ {
+ return false;
+ }
+#else
+ CUgraphicsResource* tmp = &handle;
+ PX_UNUSED(tmp);
+
+ return false;
+#endif
+}
+
+void SampleApexRendererVertexBuffer::fixUVOrigin(void* uvdata, uint32_t stride, uint32_t num)
+{
+#define ITERATE_UVS(_uop,_vop) \
+ for(uint32_t i=0; i<num; i++) \
+ { \
+ float &u = *(((float*)uvdata) + 0); \
+ float &v = *(((float*)uvdata) + 1); \
+ u=_uop; \
+ v=_vop; \
+ uvdata = ((uint8_t*)uvdata)+stride; \
+}
+ switch (m_uvOrigin)
+ {
+ case nvidia::apex::TextureUVOrigin::ORIGIN_BOTTOM_LEFT:
+ // nothing to do...
+ break;
+ case nvidia::apex::TextureUVOrigin::ORIGIN_BOTTOM_RIGHT:
+ ITERATE_UVS(1 - u, v);
+ break;
+ case nvidia::apex::TextureUVOrigin::ORIGIN_TOP_LEFT:
+ ITERATE_UVS(u, 1 - v);
+ break;
+ case nvidia::apex::TextureUVOrigin::ORIGIN_TOP_RIGHT:
+ ITERATE_UVS(1 - u, 1 - v);
+ break;
+ default:
+ PX_ASSERT(0); // UNKNOWN ORIGIN / TODO!
+ }
+#undef ITERATE_UVS
+}
+
+void SampleApexRendererVertexBuffer::flipColors(void* uvData, uint32_t stride, uint32_t num)
+{
+ for (uint32_t i = 0; i < num; i++)
+ {
+ uint8_t* color = ((uint8_t*)uvData) + (stride * i);
+ std::swap(color[0], color[3]);
+ std::swap(color[1], color[2]);
+ }
+}
+
+// special fast path for interleaved vec3 position and normal and optional vec4 tangent (avoids painfully slow strided writes to locked vertex buffer)
+bool SampleApexRendererVertexBuffer::writeBufferFastPath(const nvidia::apex::RenderVertexBufferData& data, uint32_t firstVertex, uint32_t numVerts)
+{
+ for (uint32_t i = 0; i < nvidia::apex::RenderVertexSemantic::NUM_SEMANTICS; i++)
+ {
+ nvidia::apex::RenderVertexSemantic::Enum apexSemantic = (nvidia::apex::RenderVertexSemantic::Enum)i;
+ switch(apexSemantic)
+ {
+ case nvidia::apex::RenderVertexSemantic::POSITION:
+ case nvidia::apex::RenderVertexSemantic::NORMAL:
+ case nvidia::apex::RenderVertexSemantic::TANGENT:
+ break;
+ default:
+ if(data.getSemanticData(apexSemantic).data)
+ return false;
+ }
+ }
+
+ const nvidia::apex::RenderSemanticData& positionSemantic = data.getSemanticData(nvidia::apex::RenderVertexSemantic::POSITION);
+ const nvidia::apex::RenderSemanticData& normalSemantic = data.getSemanticData(nvidia::apex::RenderVertexSemantic::NORMAL);
+ const nvidia::apex::RenderSemanticData& tangentSemantic = data.getSemanticData(nvidia::apex::RenderVertexSemantic::TANGENT);
+
+ const physx::PxVec3* PX_RESTRICT positionSrc = (const physx::PxVec3*)positionSemantic.data;
+ const physx::PxVec3* PX_RESTRICT normalSrc = (const physx::PxVec3*)normalSemantic.data;
+ const physx::PxVec4* PX_RESTRICT tangentSrc = (const physx::PxVec4*)tangentSemantic.data;
+
+ RendererVertexBuffer::Format positionFormat = m_vertexbuffer->getFormatForSemantic(RendererVertexBuffer::SEMANTIC_POSITION);
+ RendererVertexBuffer::Format normalFormat = m_vertexbuffer->getFormatForSemantic(RendererVertexBuffer::SEMANTIC_NORMAL);
+ RendererVertexBuffer::Format tangentFormat = m_vertexbuffer->getFormatForSemantic(RendererVertexBuffer::SEMANTIC_TANGENT);
+
+ if(positionSrc == 0 || positionSemantic.stride != 12 || RendererVertexBuffer::getFormatByteSize(positionFormat) != 12)
+ return false;
+
+ if(normalSrc == 0 || normalSemantic.stride != 12 || RendererVertexBuffer::getFormatByteSize(normalFormat) != 12)
+ return false;
+
+ if(tangentSrc != 0 && (tangentSemantic.stride != 16 || RendererVertexBuffer::getFormatByteSize(tangentFormat) != 16))
+ return false;
+
+ uint32_t stride = 0;
+ void* positionDst = m_vertexbuffer->lockSemantic(RendererVertexBuffer::SEMANTIC_POSITION, stride);
+ void* normalDst = m_vertexbuffer->lockSemantic(RendererVertexBuffer::SEMANTIC_NORMAL, stride);
+ void* tangentDst = tangentSrc ? m_vertexbuffer->lockSemantic(RendererVertexBuffer::SEMANTIC_TANGENT, stride) : 0;
+
+ bool useFastPath = stride == (uint32_t)(tangentSrc ? 40 : 24);
+ useFastPath &= normalDst == (uint8_t*)positionDst + 12;
+ useFastPath &= !tangentSrc || tangentDst == (uint8_t*)positionDst + 24;
+
+ if(useFastPath)
+ {
+ uint8_t* dstIt = (uint8_t*)positionDst + stride * firstVertex;
+ uint8_t* dstEnd = dstIt + stride * numVerts;
+
+ for(; dstIt < dstEnd; dstIt += stride)
+ {
+ *(physx::PxVec3* PX_RESTRICT)(dstIt ) = *positionSrc++;
+ *(physx::PxVec3* PX_RESTRICT)(dstIt+12) = *normalSrc++;
+ if(tangentSrc)
+ *(physx::PxVec4* PX_RESTRICT)(dstIt+24) = -*tangentSrc++;
+ }
+ }
+
+ m_vertexbuffer->unlockSemantic(RendererVertexBuffer::SEMANTIC_POSITION);
+ m_vertexbuffer->unlockSemantic(RendererVertexBuffer::SEMANTIC_NORMAL);
+ if(tangentSrc)
+ m_vertexbuffer->unlockSemantic(RendererVertexBuffer::SEMANTIC_TANGENT);
+
+ return useFastPath;
+}
+
+void SampleApexRendererVertexBuffer::writeBuffer(const nvidia::apex::RenderVertexBufferData& data, uint32_t firstVertex, uint32_t numVerts)
+{
+ if (!m_vertexbuffer || !numVerts)
+ {
+ return;
+ }
+
+ if(writeBufferFastPath(data, firstVertex, numVerts))
+ return;
+
+ for (uint32_t i = 0; i < nvidia::apex::RenderVertexSemantic::NUM_SEMANTICS; i++)
+ {
+ nvidia::apex::RenderVertexSemantic::Enum apexSemantic = (nvidia::apex::RenderVertexSemantic::Enum)i;
+ const nvidia::apex::RenderSemanticData& semanticData = data.getSemanticData(apexSemantic);
+ if (semanticData.data)
+ {
+ const void* srcData = semanticData.data;
+ const uint32_t srcStride = semanticData.stride;
+
+ RendererVertexBuffer::Semantic semantic = convertFromApexVB(apexSemantic);
+ if (semantic < RendererVertexBuffer::NUM_SEMANTICS)
+ {
+ RendererVertexBuffer::Format format = m_vertexbuffer->getFormatForSemantic(semantic);
+ uint32_t semanticStride = 0;
+ void* dstData = m_vertexbuffer->lockSemantic(semantic, semanticStride);
+ void* dstDataCopy = dstData;
+ PX_ASSERT(dstData && semanticStride);
+ if (dstData && semanticStride)
+ {
+#if defined(RENDERER_DEBUG) && 0 // enable to confirm that destruction bone indices are within valid range.
+ // verify input data...
+ if (apexSemantic == RenderVertexSemantic::BONE_INDEX)
+ {
+ uint32_t maxIndex = 0;
+ for (uint32_t i = 0; i < numVerts; i++)
+ {
+ uint16_t index = *(uint16_t*)(((uint8_t*)srcData) + (srcStride * i));
+ if (index > maxIndex)
+ {
+ maxIndex = index;
+ }
+ }
+ PX_ASSERT(maxIndex < RENDERER_MAX_BONES);
+ }
+#endif
+ dstData = ((uint8_t*)dstData) + firstVertex * semanticStride;
+ uint32_t formatSize = RendererVertexBuffer::getFormatByteSize(format);
+
+ if ((apexSemantic == nvidia::apex::RenderVertexSemantic::NORMAL || apexSemantic == nvidia::apex::RenderVertexSemantic::BINORMAL) && semanticData.format == nvidia::apex::RenderDataFormat::BYTE_SNORM3)
+ {
+ for (uint32_t j = 0; j < numVerts; j++)
+ {
+ uint8_t* vector = (uint8_t*)srcData;
+ *(physx::PxVec3*)dstData = physx::PxVec3(vector[0], vector[1], vector[2])/127.0f;
+ dstData = ((uint8_t*)dstData) + semanticStride;
+ srcData = ((uint8_t*)srcData) + srcStride;
+ }
+ }
+ else if (apexSemantic == nvidia::apex::RenderVertexSemantic::TANGENT && semanticData.format == nvidia::apex::RenderDataFormat::FLOAT4)
+ {
+ // invert entire tangent
+ for (uint32_t j = 0; j < numVerts; j++)
+ {
+ physx::PxVec4 tangent = *(physx::PxVec4*)srcData;
+ *(physx::PxVec4*)dstData = -tangent;
+ dstData = ((uint8_t*)dstData) + semanticStride;
+ srcData = ((uint8_t*)srcData) + srcStride;
+ }
+ }
+ else if (apexSemantic == nvidia::apex::RenderVertexSemantic::TANGENT && semanticData.format == nvidia::apex::RenderDataFormat::BYTE_SNORM4)
+ {
+ // invert entire tangent
+ for (uint32_t j = 0; j < numVerts; j++)
+ {
+ uint8_t* tangent = (uint8_t*)srcData;
+ *(physx::PxVec4*)dstData = physx::PxVec4(tangent[0], tangent[1], tangent[2], tangent[3])/-127.0f;
+ dstData = ((uint8_t*)dstData) + semanticStride;
+ srcData = ((uint8_t*)srcData) + srcStride;
+ }
+ }
+ else if (apexSemantic == nvidia::apex::RenderVertexSemantic::TANGENT && (semanticData.format == nvidia::apex::RenderDataFormat::FLOAT3 || semanticData.format == nvidia::apex::RenderDataFormat::BYTE_SNORM3))
+ {
+ // we need to increase the data from 3 components to 4
+ const nvidia::apex::RenderSemanticData& bitangentData = data.getSemanticData(nvidia::apex::RenderVertexSemantic::BINORMAL);
+ const nvidia::apex::RenderSemanticData& normalData = data.getSemanticData(nvidia::apex::RenderVertexSemantic::NORMAL);
+ if (bitangentData.format != nvidia::apex::RenderDataFormat::UNSPECIFIED && normalData.format != nvidia::apex::RenderDataFormat::UNSPECIFIED)
+ {
+ PX_ASSERT(bitangentData.format == nvidia::apex::RenderDataFormat::FLOAT3);
+ const void* srcDataBitangent = bitangentData.data;
+ const uint32_t srcStrideBitangent = bitangentData.stride;
+
+ PX_ASSERT(normalData.format == nvidia::apex::RenderDataFormat::FLOAT3);
+ const void* srcDataNormal = normalData.data;
+ const uint32_t srcStrideNormal = normalData.stride;
+
+ for (uint32_t j = 0; j < numVerts; j++)
+ {
+ physx::PxVec3 normal = normalData.format == nvidia::RenderDataFormat::FLOAT3 ? *(physx::PxVec3*)srcDataNormal :
+ physx::PxVec3(((uint8_t*)srcDataNormal)[0], ((uint8_t*)srcDataNormal)[1], ((uint8_t*)srcDataNormal)[2])/127.0f;
+ physx::PxVec3 bitangent = bitangentData.format == nvidia::RenderDataFormat::FLOAT3 ? *(physx::PxVec3*)srcDataBitangent :
+ physx::PxVec3(((uint8_t*)srcDataBitangent)[0], ((uint8_t*)srcDataBitangent)[1], ((uint8_t*)srcDataBitangent)[2])/127.0f;
+ physx::PxVec3 tangent = semanticData.format == nvidia::RenderDataFormat::FLOAT3 ? *(physx::PxVec3*)srcData :
+ physx::PxVec3(((uint8_t*)srcData)[0], ((uint8_t*)srcData)[1], ((uint8_t*)srcData)[2])/127.0f;
+ float tangentw = physx::PxSign(normal.cross(tangent).dot(bitangent));
+ *(physx::PxVec4*)dstData = physx::PxVec4(tangent, -tangentw);
+
+ dstData = ((uint8_t*)dstData) + semanticStride;
+ srcData = ((uint8_t*)srcData) + srcStride;
+ srcDataBitangent = ((uint8_t*)srcDataBitangent) + srcStrideBitangent;
+ srcDataNormal = ((uint8_t*)srcDataNormal) + srcStrideNormal;
+ }
+ }
+ else
+ {
+ // just assume 1.0 as tangent.w if there is no bitangent to calculate this from
+ if (semanticData.format == nvidia::apex::RenderDataFormat::FLOAT3)
+ {
+ for (uint32_t j = 0; j < numVerts; j++)
+ {
+ physx::PxVec3 tangent = *(physx::PxVec3*)srcData;
+ *(physx::PxVec4*)dstData = physx::PxVec4(tangent, 1.0f);
+ dstData = ((uint8_t*)dstData) + semanticStride;
+ srcData = ((uint8_t*)srcData) + srcStride;
+ }
+ }
+ else
+ {
+ for (uint32_t j = 0; j < numVerts; j++)
+ {
+ uint8_t* tangent = (uint8_t*)srcData;
+ *(physx::PxVec4*)dstData = physx::PxVec4(tangent[0]/127.0f, tangent[1]/127.0f, tangent[2]/127.0f, 1.0f);
+ dstData = ((uint8_t*)dstData) + semanticStride;
+ srcData = ((uint8_t*)srcData) + srcStride;
+ }
+ }
+ }
+ }
+ else if (apexSemantic == nvidia::apex::RenderVertexSemantic::BONE_INDEX && (semanticData.format == nvidia::apex::RenderDataFormat::USHORT2 || semanticData.format == nvidia::apex::RenderDataFormat::USHORT3))
+ {
+ unsigned int numIndices = 0;
+ switch (semanticData.format)
+ {
+ case nvidia::apex::RenderDataFormat::USHORT1: numIndices = 1; break;
+ case nvidia::apex::RenderDataFormat::USHORT2: numIndices = 2; break;
+ case nvidia::apex::RenderDataFormat::USHORT3: numIndices = 3; break;
+ default:
+ PX_ALWAYS_ASSERT();
+ break;
+ }
+
+ for (uint32_t j = 0; j < numVerts; j++)
+ {
+ unsigned short* boneIndices = (unsigned short*)srcData;
+ unsigned short* dstBoneIndices = (unsigned short*)dstData;
+
+ for (unsigned int i = 0; i < numIndices; i++)
+ {
+ dstBoneIndices[i] = boneIndices[i];
+ }
+ for (unsigned int i = numIndices; i < 4; i++)
+ {
+ dstBoneIndices[i] = 0;
+ }
+
+ dstData = ((uint8_t*)dstData) + semanticStride;
+ srcData = ((uint8_t*)srcData) + srcStride;
+ }
+ }
+ else if (apexSemantic == nvidia::apex::RenderVertexSemantic::BONE_WEIGHT && (semanticData.format == nvidia::apex::RenderDataFormat::FLOAT2 || semanticData.format == nvidia::apex::RenderDataFormat::FLOAT3))
+ {
+ unsigned int numWeights = 0;
+ switch (semanticData.format)
+ {
+ case nvidia::apex::RenderDataFormat::FLOAT1: numWeights = 1; break;
+ case nvidia::apex::RenderDataFormat::FLOAT2: numWeights = 2; break;
+ case nvidia::apex::RenderDataFormat::FLOAT3: numWeights = 3; break;
+ default:
+ PX_ALWAYS_ASSERT();
+ break;
+ }
+
+ for (uint32_t j = 0; j < numVerts; j++)
+ {
+ float* boneIndices = (float*)srcData;
+ float* dstBoneIndices = (float*)dstData;
+
+ for (unsigned int i = 0; i < numWeights; i++)
+ {
+ dstBoneIndices[i] = boneIndices[i];
+ }
+ for (unsigned int i = numWeights; i < 4; i++)
+ {
+ dstBoneIndices[i] = 0.0f;
+ }
+
+ dstData = ((uint8_t*)dstData) + semanticStride;
+ srcData = ((uint8_t*)srcData) + srcStride;
+ }
+ }
+ else if (formatSize == 4)
+ {
+ for (uint32_t j = 0; j < numVerts; j++)
+ {
+ *(uint32_t*)dstData = *(uint32_t*)srcData;
+ dstData = ((uint8_t*)dstData) + semanticStride;
+ srcData = ((uint8_t*)srcData) + srcStride;
+ }
+ }
+ else if (formatSize == 12)
+ {
+ for (uint32_t j = 0; j < numVerts; j++)
+ {
+ *(physx::PxVec3*)dstData = *(physx::PxVec3*)srcData;
+ dstData = ((uint8_t*)dstData) + semanticStride;
+ srcData = ((uint8_t*)srcData) + srcStride;
+ }
+ }
+ else
+ {
+ for (uint32_t j = 0; j < numVerts; j++)
+ {
+ memcpy(dstData, srcData, formatSize);
+ dstData = ((uint8_t*)dstData) + semanticStride;
+ srcData = ((uint8_t*)srcData) + srcStride;
+ }
+ }
+
+ // fix-up the UVs...
+ if ((semantic >= RendererVertexBuffer::SEMANTIC_TEXCOORD0 &&
+ semantic <= RendererVertexBuffer::SEMANTIC_TEXCOORDMAX) ||
+ semantic == RendererVertexBuffer::SEMANTIC_DISPLACEMENT_TEXCOORD)
+ {
+ fixUVOrigin(dstDataCopy, semanticStride, numVerts);
+ }
+ }
+ m_vertexbuffer->unlockSemantic(semantic);
+ }
+ }
+ }
+}
+
+
+/********************************
+* SampleApexRendererIndexBuffer *
+********************************/
+
+SampleApexRendererIndexBuffer::SampleApexRendererIndexBuffer(SampleRenderer::Renderer& renderer, const nvidia::apex::UserRenderIndexBufferDesc& desc) :
+ m_renderer(renderer)
+{
+ m_indexbuffer = 0;
+ SampleRenderer::RendererIndexBufferDesc ibdesc;
+ ibdesc.hint = convertFromApexIB(desc.hint);
+ ibdesc.format = convertFromApexIB(desc.format);
+ ibdesc.maxIndices = desc.maxIndices;
+#if APEX_CUDA_SUPPORT
+ ibdesc.registerInCUDA = desc.registerInCUDA;
+ ibdesc.interopContext = desc.interopContext;
+#endif
+ m_indexbuffer = m_renderer.createIndexBuffer(ibdesc);
+ PX_ASSERT(m_indexbuffer);
+
+ m_primitives = desc.primitives;
+}
+
+bool SampleApexRendererIndexBuffer::getInteropResourceHandle(CUgraphicsResource& handle)
+{
+#if APEX_CUDA_SUPPORT
+ if (m_indexbuffer)
+ {
+ return m_indexbuffer->getInteropResourceHandle(handle);
+ }
+ else
+ {
+ return false;
+ }
+#else
+ CUgraphicsResource* tmp = &handle;
+ PX_UNUSED(tmp);
+
+ return false;
+#endif
+}
+
+SampleApexRendererIndexBuffer::~SampleApexRendererIndexBuffer(void)
+{
+ if (m_indexbuffer)
+ {
+ m_indexbuffer->release();
+ }
+}
+
+void SampleApexRendererIndexBuffer::writeBuffer(const void* srcData, uint32_t srcStride, uint32_t firstDestElement, uint32_t numElements)
+{
+ if (m_indexbuffer && numElements)
+ {
+ void* dstData = m_indexbuffer->lock();
+ PX_ASSERT(dstData);
+ if (dstData)
+ {
+ RendererIndexBuffer::Format format = m_indexbuffer->getFormat();
+
+ if (m_primitives == nvidia::apex::RenderPrimitiveType::TRIANGLES)
+ {
+ uint32_t numTriangles = numElements / 3;
+ if (format == RendererIndexBuffer::FORMAT_UINT16)
+ {
+ uint16_t* dst = ((uint16_t*)dstData) + firstDestElement;
+ const uint16_t* src = (const uint16_t*)srcData;
+ for (uint32_t i = 0; i < numTriangles; i++)
+ for (uint32_t j = 0; j < 3; j++)
+ {
+ dst[i * 3 + j] = src[i * 3 + (2 - j)];
+ }
+ }
+ else if (format == RendererIndexBuffer::FORMAT_UINT32)
+ {
+ uint32_t* dst = ((uint32_t*)dstData) + firstDestElement;
+ const uint32_t* src = (const uint32_t*)srcData;
+ for (uint32_t i = 0; i < numTriangles; i++)
+ for (uint32_t j = 0; j < 3; j++)
+ {
+ dst[i * 3 + j] = src[i * 3 + (2 - j)];
+ }
+ }
+ else
+ {
+ PX_ASSERT(0);
+ }
+ }
+ else
+ {
+ if (format == RendererIndexBuffer::FORMAT_UINT16)
+ {
+ uint16_t* dst = ((uint16_t*)dstData) + firstDestElement;
+ const uint8_t* src = (const uint8_t*)srcData;
+ for (uint32_t i = 0; i < numElements; i++, dst++, src += srcStride)
+ {
+ *dst = *((uint16_t*)src);
+ }
+ }
+ else if (format == RendererIndexBuffer::FORMAT_UINT32)
+ {
+ uint32_t* dst = ((uint32_t*)dstData) + firstDestElement;
+ const uint8_t* src = (const uint8_t*)srcData;
+ for (uint32_t i = 0; i < numElements; i++, dst++, src += srcStride)
+ {
+ *dst = *((uint32_t*)src);
+ }
+ }
+ else
+ {
+ PX_ASSERT(0);
+ }
+ }
+ }
+ m_indexbuffer->unlock();
+ }
+}
+
+
+/********************************
+* SampleApexRendererSurfaceBuffer *
+********************************/
+
+SampleApexRendererSurfaceBuffer::SampleApexRendererSurfaceBuffer(SampleRenderer::Renderer& renderer, const nvidia::apex::UserRenderSurfaceBufferDesc& desc) :
+ m_renderer(renderer)
+{
+ m_texture = 0;
+ SampleRenderer::RendererTextureDesc tdesc;
+ //tdesc.hint = convertFromApexSB(desc.hint);
+ tdesc.format = convertFromApexSB(desc.format);
+ tdesc.width = desc.width;
+ tdesc.height = desc.height;
+ tdesc.depth = desc.depth;
+ tdesc.filter = SampleRenderer::RendererTexture::FILTER_NEAREST; // we don't need interpolation at all
+ tdesc.addressingU = SampleRenderer::RendererTexture::ADDRESSING_CLAMP;
+ tdesc.addressingV = SampleRenderer::RendererTexture::ADDRESSING_CLAMP;
+ tdesc.addressingW = SampleRenderer::RendererTexture::ADDRESSING_CLAMP;
+ tdesc.numLevels = 1;
+
+#if APEX_CUDA_SUPPORT
+ tdesc.registerInCUDA = desc.registerInCUDA;
+ tdesc.interopContext = desc.interopContext;
+#endif
+
+ m_texture = m_renderer.createTexture(tdesc);
+ PX_ASSERT(m_texture);
+}
+
+SampleApexRendererSurfaceBuffer::~SampleApexRendererSurfaceBuffer(void)
+{
+ if (m_texture)
+ {
+ m_texture->release();
+ }
+}
+
+void SampleApexRendererSurfaceBuffer::writeBuffer(const void* srcData, uint32_t srcPitch, uint32_t srcHeight, uint32_t dstX, uint32_t dstY, uint32_t dstZ, uint32_t width, uint32_t height, uint32_t depth)
+{
+ if (m_texture && width && height && depth)
+ {
+ const RendererTexture::Format format = m_texture->getFormat();
+ PX_ASSERT(format < RendererTexture::NUM_FORMATS);
+ PX_ASSERT(RendererTexture::isCompressedFormat(format) == false);
+ PX_ASSERT(RendererTexture::isDepthStencilFormat(format) == false);
+
+ const uint32_t texelSize = RendererTexture::getFormatBlockSize(format);
+ const uint32_t dstXInBytes = dstX * texelSize;
+ const uint32_t widthInBytes = width * texelSize;
+
+ const uint32_t dstHeight = m_texture->getHeight();
+ uint32_t dstPitch;
+ void* dstData = m_texture->lockLevel(0, dstPitch);
+ PX_ASSERT(dstData);
+ if (dstData)
+ {
+ uint8_t* dst = ((uint8_t*)dstData) + dstXInBytes + dstPitch * (dstY + dstHeight * dstZ);
+ const uint8_t* src = (const uint8_t*)srcData;
+ for (uint32_t z = 0; z < depth; z++)
+ {
+ uint8_t* dstLine = dst;
+ const uint8_t* srcLine = src;
+ for (uint32_t y = 0; y < height; y++)
+ {
+ memcpy(dstLine, srcLine, widthInBytes);
+ dstLine += dstPitch;
+ srcLine += srcPitch;
+ }
+ dst += dstPitch * dstHeight;
+ src += srcPitch * srcHeight;
+ }
+ }
+ m_texture->unlockLevel(0);
+ }
+}
+
+bool SampleApexRendererSurfaceBuffer::getInteropResourceHandle(CUgraphicsResource& handle)
+{
+#if APEX_CUDA_SUPPORT
+ return (m_texture != 0) && m_texture->getInteropResourceHandle(handle);
+#else
+ CUgraphicsResource* tmp = &handle;
+ PX_UNUSED(tmp);
+
+ return false;
+#endif
+}
+
+
+/*******************************
+* SampleApexRendererBoneBuffer *
+*******************************/
+
+SampleApexRendererBoneBuffer::SampleApexRendererBoneBuffer(SampleRenderer::Renderer& renderer, const nvidia::apex::UserRenderBoneBufferDesc& desc) :
+ m_renderer(renderer)
+{
+ m_boneTexture = 0;
+ m_maxBones = desc.maxBones;
+ m_bones = 0;
+
+ if(m_renderer.isVTFEnabled()) // Create vertex texture to hold bone matrices
+ {
+ SampleRenderer::RendererTextureDesc textureDesc;
+ textureDesc.format = RendererTexture::FORMAT_R32F_G32F_B32G_A32F;
+ textureDesc.filter = RendererTexture::FILTER_NEAREST;
+ textureDesc.addressingU = RendererTexture::ADDRESSING_CLAMP;
+ textureDesc.addressingV = RendererTexture::ADDRESSING_CLAMP;
+ textureDesc.addressingU = RendererTexture::ADDRESSING_CLAMP;
+ textureDesc.width = 4; // 4x4 matrix per row
+ textureDesc.height = m_maxBones;
+ textureDesc.depth = 1;
+ textureDesc.numLevels = 1;
+
+ m_boneTexture = renderer.createTexture(textureDesc);
+ }
+ else
+ {
+ m_bones = new physx::PxMat44[m_maxBones];
+ }
+}
+
+SampleApexRendererBoneBuffer::~SampleApexRendererBoneBuffer(void)
+{
+ if(m_boneTexture)
+ m_boneTexture->release();
+
+ if (m_bones)
+ {
+ delete [] m_bones;
+ }
+}
+
+void SampleApexRendererBoneBuffer::writeBuffer(const nvidia::apex::RenderBoneBufferData& data, uint32_t firstBone, uint32_t numBones)
+{
+ const nvidia::apex::RenderSemanticData& semanticData = data.getSemanticData(nvidia::apex::RenderBoneSemantic::POSE);
+ if(!semanticData.data)
+ return;
+
+ if(m_boneTexture) // Write bone matrices into vertex texture
+ {
+ const void* srcData = semanticData.data;
+ const uint32_t srcStride = semanticData.stride;
+
+ unsigned pitch = 0;
+ unsigned char* textureData = static_cast<unsigned char*>(m_boneTexture->lockLevel(0, pitch));
+ if(textureData)
+ {
+ for(uint32_t row = firstBone; row < firstBone + numBones; ++row)
+ {
+ physx::PxMat44* mat = (physx::PxMat44*)(textureData + row * pitch);
+
+ *mat = static_cast<const physx::PxMat44*>(srcData)->getTranspose();
+
+ srcData = ((uint8_t*)srcData) + srcStride;
+ }
+
+ m_boneTexture->unlockLevel(0);
+ }
+ }
+ else if(m_bones)
+ {
+ const void* srcData = semanticData.data;
+ const uint32_t srcStride = semanticData.stride;
+ for (uint32_t i = 0; i < numBones; i++)
+ {
+ // the bones are stored in physx::PxMat44
+ m_bones[firstBone + i] = *(const physx::PxMat44*)srcData;
+ srcData = ((uint8_t*)srcData) + srcStride;
+ }
+ }
+}
+
+
+/***********************************
+* SampleApexRendererInstanceBuffer *
+***********************************/
+#if 0
+float lifeRemain[0x10000]; //64k
+#endif
+SampleApexRendererInstanceBuffer::SampleApexRendererInstanceBuffer(SampleRenderer::Renderer& renderer, const nvidia::apex::UserRenderInstanceBufferDesc& desc)
+{
+ m_maxInstances = desc.maxInstances;
+ m_instanceBuffer = 0;
+
+ SampleRenderer::RendererInstanceBufferDesc ibdesc;
+
+ ibdesc.hint = RendererInstanceBuffer::HINT_DYNAMIC;
+ ibdesc.maxInstances = desc.maxInstances;
+
+ for (uint32_t i = 0; i < RendererInstanceBuffer::NUM_SEMANTICS; i++)
+ {
+ ibdesc.semanticFormats[i] = RendererInstanceBuffer::NUM_FORMATS;
+ }
+
+ for (uint32_t i = 0; i < nvidia::apex::RenderInstanceLayoutElement::NUM_SEMANTICS; i++)
+ {
+ // Skip unspecified, but if it IS specified, IGNORE the specification and make your own up!! yay!
+ if (desc.semanticOffsets[i] == uint32_t(-1))
+ {
+ continue;
+ }
+
+ switch (i)
+ {
+ case nvidia::apex::RenderInstanceLayoutElement::POSITION_FLOAT3:
+ ibdesc.semanticFormats[RendererInstanceBuffer::SEMANTIC_POSITION] = RendererInstanceBuffer::FORMAT_FLOAT3;
+ break;
+ case nvidia::apex::RenderInstanceLayoutElement::ROTATION_SCALE_FLOAT3x3:
+ ibdesc.semanticFormats[RendererInstanceBuffer::SEMANTIC_NORMALX] = RendererInstanceBuffer::FORMAT_FLOAT3;
+ ibdesc.semanticFormats[RendererInstanceBuffer::SEMANTIC_NORMALY] = RendererInstanceBuffer::FORMAT_FLOAT3;
+ ibdesc.semanticFormats[RendererInstanceBuffer::SEMANTIC_NORMALZ] = RendererInstanceBuffer::FORMAT_FLOAT3;
+ break;
+ case nvidia::apex::RenderInstanceLayoutElement::VELOCITY_LIFE_FLOAT4:
+ ibdesc.semanticFormats[RendererInstanceBuffer::SEMANTIC_VELOCITY_LIFE] = RendererInstanceBuffer::FORMAT_FLOAT4;
+ break;
+ case nvidia::apex::RenderInstanceLayoutElement::DENSITY_FLOAT1:
+ ibdesc.semanticFormats[RendererInstanceBuffer::SEMANTIC_DENSITY] = RendererInstanceBuffer::FORMAT_FLOAT1;
+ break;
+ case nvidia::apex::RenderInstanceLayoutElement::UV_OFFSET_FLOAT2:
+ ibdesc.semanticFormats[RendererInstanceBuffer::SEMANTIC_UV_OFFSET] = RendererInstanceBuffer::FORMAT_FLOAT2;
+ break;
+ case nvidia::apex::RenderInstanceLayoutElement::LOCAL_OFFSET_FLOAT3:
+ ibdesc.semanticFormats[RendererInstanceBuffer::SEMANTIC_LOCAL_OFFSET] = RendererInstanceBuffer::FORMAT_FLOAT3;
+ break;
+ }
+ }
+
+#if APEX_CUDA_SUPPORT
+ ibdesc.registerInCUDA = desc.registerInCUDA;
+ ibdesc.interopContext = desc.interopContext;
+#endif
+ m_instanceBuffer = renderer.createInstanceBuffer(ibdesc);
+}
+
+SampleApexRendererInstanceBuffer::~SampleApexRendererInstanceBuffer(void)
+{
+ if (m_instanceBuffer)
+ {
+ m_instanceBuffer->release();
+ }
+}
+
+void SampleApexRendererInstanceBuffer::writeBuffer(const void* srcData, uint32_t firstInstance, uint32_t numInstances)
+{
+ /* Find beginning of the destination data buffer */
+ RendererInstanceBuffer::Semantic semantic = (RendererInstanceBuffer::Semantic)0;
+ for(uint32_t i = 0; i <= RendererInstanceBuffer::NUM_SEMANTICS; i++)
+ {
+ if(i == RendererVertexBuffer::NUM_SEMANTICS)
+ {
+ PX_ASSERT(0 && "Couldn't find any semantic in the VBO having zero offset");
+ }
+ semantic = static_cast<RendererInstanceBuffer::Semantic>(i);;
+ RendererInstanceBuffer::Format format = m_instanceBuffer->getFormatForSemantic(semantic);
+ if(format != RendererInstanceBuffer::NUM_FORMATS && m_instanceBuffer->getOffsetForSemantic(semantic) == 0)
+ {
+ break;
+ }
+ }
+ uint32_t dstStride = 0;
+ void* dstData = m_instanceBuffer->lockSemantic(semantic, dstStride);
+ ::memcpy(static_cast<char*>(dstData) + firstInstance * dstStride, srcData, dstStride * numInstances);
+ m_instanceBuffer->unlockSemantic(semantic);
+}
+
+bool SampleApexRendererInstanceBuffer::writeBufferFastPath(const nvidia::apex::RenderInstanceBufferData& data, uint32_t firstInstance, uint32_t numInstances)
+{
+ const void* srcData = reinterpret_cast<void*>(~0);
+ uint32_t srcStride = 0;
+ /* Find beginning of the source data buffer */
+ for (uint32_t i = 0; i < nvidia::apex::RenderInstanceSemantic::NUM_SEMANTICS; i++)
+ {
+ nvidia::apex::RenderInstanceSemantic::Enum semantic = static_cast<nvidia::apex::RenderInstanceSemantic::Enum>(i);
+ const nvidia::apex::RenderSemanticData& semanticData = data.getSemanticData(semantic);
+ if(semanticData.data && semanticData.data < srcData)
+ {
+ srcData = semanticData.data;
+ srcStride = semanticData.stride;
+ }
+ }
+ /* Find beginning of the destination data buffer */
+ RendererInstanceBuffer::Semantic semantic = (RendererInstanceBuffer::Semantic)0;
+ for(uint32_t i = 0; i <= RendererInstanceBuffer::NUM_SEMANTICS; i++)
+ {
+ if(i == RendererVertexBuffer::NUM_SEMANTICS)
+ {
+ PX_ASSERT(0 && "Couldn't find any semantic in the VBO having zero offset");
+ return false;
+ }
+ semantic = static_cast<RendererInstanceBuffer::Semantic>(i);;
+ RendererInstanceBuffer::Format format = m_instanceBuffer->getFormatForSemantic(semantic);
+ if(format != RendererInstanceBuffer::NUM_FORMATS && m_instanceBuffer->getOffsetForSemantic(semantic) == 0)
+ {
+ break;
+ }
+ }
+ uint32_t dstStride = 0;
+ void* dstData = m_instanceBuffer->lockSemantic(semantic, dstStride);
+ bool stridesEqual = false;
+ if(dstStride == srcStride)
+ {
+ stridesEqual = true;
+ ::memcpy(static_cast<char*>(dstData) + firstInstance * dstStride, srcData, dstStride * numInstances);
+ }
+ m_instanceBuffer->unlockSemantic(semantic);
+ if(stridesEqual)
+ return true;
+ else
+ return false;
+}
+
+void SampleApexRendererInstanceBuffer::internalWriteBuffer(nvidia::apex::RenderInstanceSemantic::Enum semantic, const void* srcData, uint32_t srcStride,
+ uint32_t firstDestElement, uint32_t numElements)
+{
+ if (semantic == nvidia::apex::RenderInstanceSemantic::POSITION)
+ {
+ internalWriteSemantic<physx::PxVec3>(RendererInstanceBuffer::SEMANTIC_POSITION, srcData, srcStride, firstDestElement, numElements);
+ }
+ else if (semantic == nvidia::apex::RenderInstanceSemantic::ROTATION_SCALE)
+ {
+ PX_ASSERT(m_instanceBuffer);
+
+ uint32_t xnormalsStride = 0;
+ uint8_t* xnormals = (uint8_t*)m_instanceBuffer->lockSemantic(RendererInstanceBuffer::SEMANTIC_NORMALX, xnormalsStride);
+ uint32_t ynormalsStride = 0;
+ uint8_t* ynormals = (uint8_t*)m_instanceBuffer->lockSemantic(RendererInstanceBuffer::SEMANTIC_NORMALY, ynormalsStride);
+ uint32_t znormalsStride = 0;
+ uint8_t* znormals = (uint8_t*)m_instanceBuffer->lockSemantic(RendererInstanceBuffer::SEMANTIC_NORMALZ, znormalsStride);
+
+ PX_ASSERT(xnormals && ynormals && znormals);
+
+ if (xnormals && ynormals && znormals)
+ {
+ xnormals += firstDestElement * xnormalsStride;
+ ynormals += firstDestElement * ynormalsStride;
+ znormals += firstDestElement * znormalsStride;
+
+ for (uint32_t i = 0; i < numElements; i++)
+ {
+ physx::PxVec3* p = (physx::PxVec3*)(((uint8_t*)srcData) + srcStride * i);
+
+ *((physx::PxVec3*)xnormals) = p[0];
+ *((physx::PxVec3*)ynormals) = p[1];
+ *((physx::PxVec3*)znormals) = p[2];
+
+ xnormals += xnormalsStride;
+ ynormals += ynormalsStride;
+ znormals += znormalsStride;
+ }
+ }
+ m_instanceBuffer->unlockSemantic(RendererInstanceBuffer::SEMANTIC_NORMALX);
+ m_instanceBuffer->unlockSemantic(RendererInstanceBuffer::SEMANTIC_NORMALY);
+ m_instanceBuffer->unlockSemantic(RendererInstanceBuffer::SEMANTIC_NORMALZ);
+ }
+ else if (semantic == nvidia::apex::RenderInstanceSemantic::VELOCITY_LIFE)
+ {
+ internalWriteSemantic<physx::PxVec4>(RendererInstanceBuffer::SEMANTIC_VELOCITY_LIFE, srcData, srcStride, firstDestElement, numElements);
+ }
+ else if (semantic == nvidia::apex::RenderInstanceSemantic::DENSITY)
+ {
+ internalWriteSemantic<float>(RendererInstanceBuffer::SEMANTIC_DENSITY, srcData, srcStride, firstDestElement, numElements);
+ }
+ else if (semantic == nvidia::apex::RenderInstanceSemantic::UV_OFFSET)
+ {
+ internalWriteSemantic<physx::PxVec2>(RendererInstanceBuffer::SEMANTIC_UV_OFFSET, srcData, srcStride, firstDestElement, numElements);
+ }
+ else if (semantic == nvidia::apex::RenderInstanceSemantic::LOCAL_OFFSET)
+ {
+ internalWriteSemantic<physx::PxVec3>(RendererInstanceBuffer::SEMANTIC_LOCAL_OFFSET, srcData, srcStride, firstDestElement, numElements);
+ }
+}
+
+bool SampleApexRendererInstanceBuffer::getInteropResourceHandle(CUgraphicsResource& handle)
+{
+#if APEX_CUDA_SUPPORT
+ if (m_instanceBuffer)
+ {
+ return m_instanceBuffer->getInteropResourceHandle(handle);
+ }
+ else
+ {
+ return false;
+ }
+#else
+ CUgraphicsResource* tmp = &handle;
+ PX_UNUSED(tmp);
+
+ return false;
+#endif
+}
+
+#if USE_RENDER_SPRITE_BUFFER
+
+/*********************************
+* SampleApexRendererSpriteBuffer *
+*********************************/
+
+SampleApexRendererSpriteBuffer::SampleApexRendererSpriteBuffer(SampleRenderer::Renderer& renderer, const nvidia::apex::UserRenderSpriteBufferDesc& desc) :
+ m_renderer(renderer)
+{
+ memset(m_textures, 0, sizeof(m_textures));
+ memset(m_textureIndexFromLayoutType, -1, sizeof(m_textureIndexFromLayoutType));
+ m_vertexbuffer = 0;
+ m_texturesCount = desc.textureCount;
+ SampleRenderer::RendererVertexBufferDesc vbdesc;
+ vbdesc.hint = convertFromApexVB(desc.hint);
+#if APEX_CUDA_SUPPORT
+ vbdesc.registerInCUDA = desc.registerInCUDA;
+ vbdesc.interopContext = desc.interopContext;
+#endif
+ /* TODO: there is no need to create VBO if desc.textureCount > 0. Document this.
+ Maybe it's better to change APEX so that desc will not require creating VBOs
+ in case desc.textureCount > 0 */
+ if(m_texturesCount == 0)
+ {
+ for (uint32_t i = 0; i < nvidia::apex::RenderSpriteLayoutElement::NUM_SEMANTICS; i++)
+ {
+ if(desc.semanticOffsets[i] == static_cast<uint32_t>(-1)) continue;
+ RendererVertexBuffer::Semantic semantic = convertFromApexLayoutSB((nvidia::apex::RenderSpriteLayoutElement::Enum)i);
+ RendererVertexBuffer::Format format = convertFromApexLayoutVB((nvidia::apex::RenderSpriteLayoutElement::Enum)i);
+ if (semantic < RendererVertexBuffer::NUM_SEMANTICS && format < RendererVertexBuffer::NUM_FORMATS)
+ {
+ vbdesc.semanticFormats[semantic] = format;
+ }
+ }
+ }
+ else
+ {
+ vbdesc.semanticFormats[RendererVertexBuffer::SEMANTIC_POSITION] = RendererVertexBuffer::FORMAT_FLOAT1;
+ vbdesc.hint = RendererVertexBuffer::HINT_STATIC;
+ vbdesc.registerInCUDA = false;
+ vbdesc.interopContext = NULL;
+ }
+ vbdesc.maxVertices = desc.maxSprites;
+ m_vertexbuffer = m_renderer.createVertexBuffer(vbdesc);
+ PX_ASSERT(m_vertexbuffer);
+
+ if(desc.textureCount != 0)
+ {
+ uint32_t semanticStride = 0;
+ void* dstData = m_vertexbuffer->lockSemantic(SampleRenderer::RendererVertexBuffer::SEMANTIC_POSITION, semanticStride);
+ PX_ASSERT(dstData && semanticStride);
+ if (dstData && semanticStride)
+ {
+ uint32_t formatSize = SampleRenderer::RendererVertexBuffer::getFormatByteSize(SampleRenderer::RendererVertexBuffer::FORMAT_FLOAT1);
+ for (uint32_t j = 0; j < desc.maxSprites; j++)
+ {
+ float index = j * 1.0f;
+ memcpy(dstData, &index, formatSize);
+ dstData = ((uint8_t*)dstData) + semanticStride;
+ }
+ }
+ m_vertexbuffer->unlockSemantic(SampleRenderer::RendererVertexBuffer::SEMANTIC_POSITION);
+ }
+ /* Create textures for the user-defined layout */
+ for (uint32_t i = 0; i < m_texturesCount; i++)
+ {
+ SampleRenderer::RendererTextureDesc texdesc;
+ switch(desc.textureDescs[i].layout) {
+ case nvidia::apex::RenderSpriteTextureLayout::POSITION_FLOAT4:
+ case nvidia::apex::RenderSpriteTextureLayout::COLOR_FLOAT4:
+ case nvidia::apex::RenderSpriteTextureLayout::SCALE_ORIENT_SUBTEX_FLOAT4:
+ texdesc.format = SampleRenderer::RendererTexture::FORMAT_R32F_G32F_B32G_A32F;
+ break;
+ case nvidia::apex::RenderSpriteTextureLayout::COLOR_BGRA8:
+ texdesc.format = SampleRenderer::RendererTexture::FORMAT_B8G8R8A8;
+ break;
+ default: PX_ASSERT("Unknown sprite texture layout");
+ }
+ texdesc.width = desc.textureDescs[i].width;
+ texdesc.height = desc.textureDescs[i].height;
+ texdesc.filter = SampleRenderer::RendererTexture::FILTER_NEAREST; // we don't need interpolation at all
+ texdesc.addressingU = SampleRenderer::RendererTexture::ADDRESSING_CLAMP;
+ texdesc.addressingV = SampleRenderer::RendererTexture::ADDRESSING_CLAMP;
+ texdesc.addressingW = SampleRenderer::RendererTexture::ADDRESSING_CLAMP;
+ texdesc.numLevels = 1;
+ texdesc.registerInCUDA = desc.registerInCUDA;
+ texdesc.interopContext = desc.interopContext;
+ m_textureIndexFromLayoutType[desc.textureDescs[i].layout] = i;
+ m_textures[i] = m_renderer.createTexture(texdesc);
+ PX_ASSERT(m_textures[i]);
+ }
+}
+
+SampleRenderer::RendererTexture* SampleApexRendererSpriteBuffer::getTexture(const nvidia::apex::RenderSpriteTextureLayout::Enum e) const
+{
+ if(e > nvidia::apex::RenderSpriteTextureLayout::NONE &&
+ e < nvidia::apex::RenderSpriteTextureLayout::NUM_LAYOUTS)
+ {
+ return m_textures[m_textureIndexFromLayoutType[e]];
+ }
+ return NULL;
+}
+
+uint32_t SampleApexRendererSpriteBuffer::getTexturesCount() const
+{
+ return m_texturesCount;
+}
+
+bool SampleApexRendererSpriteBuffer::getInteropResourceHandle(CUgraphicsResource& handle)
+{
+#if APEX_CUDA_SUPPORT
+ return (m_vertexbuffer != 0) && m_vertexbuffer->getInteropResourceHandle(handle);
+#else
+ CUgraphicsResource* tmp = &handle;
+ PX_UNUSED(tmp);
+
+ return false;
+#endif
+}
+
+bool SampleApexRendererSpriteBuffer::getInteropTextureHandleList(CUgraphicsResource* handleList)
+{
+#if APEX_CUDA_SUPPORT
+ for(uint32_t i = 0; i < m_texturesCount; ++i)
+ {
+ bool result = (m_textures[i] != 0) && m_textures[i]->getInteropResourceHandle(handleList[i]);
+ if (!result)
+ {
+ return false;
+ }
+ }
+ return true;
+#else
+ PX_UNUSED(handleList);
+
+ return false;
+#endif
+}
+
+SampleApexRendererSpriteBuffer::~SampleApexRendererSpriteBuffer(void)
+{
+ if (m_vertexbuffer)
+ {
+ m_vertexbuffer->release();
+ }
+ for(uint32_t i = 0; i < m_texturesCount; ++i)
+ {
+ if(m_textures[i])
+ {
+ m_textures[i]->release();
+ }
+ }
+}
+
+void SampleApexRendererSpriteBuffer::flipColors(void* uvData, uint32_t stride, uint32_t num)
+{
+ for (uint32_t i = 0; i < num; i++)
+ {
+ uint8_t* color = ((uint8_t*)uvData) + (stride * i);
+ std::swap(color[0], color[3]);
+ std::swap(color[1], color[2]);
+ }
+}
+
+void SampleApexRendererSpriteBuffer::writeBuffer(const void* srcData, uint32_t firstSprite, uint32_t numSprites)
+{
+ /* Find beginning of the destination data buffer */
+ RendererVertexBuffer::Semantic semantic = (RendererVertexBuffer::Semantic)0;
+ for(uint32_t i = 0; i <= RendererVertexBuffer::NUM_SEMANTICS; i++)
+ {
+ if(i == RendererVertexBuffer::NUM_SEMANTICS)
+ {
+ PX_ASSERT(0 && "Couldn't find any semantic in the VBO having zero offset");
+ }
+ semantic = static_cast<RendererVertexBuffer::Semantic>(i);;
+ RendererVertexBuffer::Format format = m_vertexbuffer->getFormatForSemantic(semantic);
+ if(format != RendererVertexBuffer::NUM_FORMATS && m_vertexbuffer->getOffsetForSemantic(semantic) == 0)
+ {
+ break;
+ }
+ }
+ uint32_t dstStride = 0;
+ void* dstData = m_vertexbuffer->lockSemantic(semantic, dstStride);
+ ::memcpy(static_cast<char*>(dstData) + firstSprite * dstStride, srcData, dstStride * numSprites);
+ m_vertexbuffer->unlockSemantic(semantic);
+}
+
+void SampleApexRendererSpriteBuffer::writeTexture(uint32_t textureId, uint32_t numSprites, const void* srcData, size_t srcSize)
+{
+ PX_ASSERT((textureId < m_texturesCount) && "Invalid sprite texture id!");
+ if(textureId < m_texturesCount)
+ {
+ PX_ASSERT(m_textures[textureId] && "Sprite texture is not initialized");
+ if(m_textures[textureId])
+ {
+ uint32_t pitch;
+ void* dstData = m_textures[textureId]->lockLevel(0, pitch);
+ PX_ASSERT(dstData);
+ uint32_t size = numSprites * (pitch / m_textures[textureId]->getWidth());
+ memcpy(dstData, srcData, size);
+ m_textures[textureId]->unlockLevel(0);
+ }
+ }
+}
+
+#endif /* USE_RENDER_SPRITE_BUFFER */
+
+
+/*************************
+* SampleApexRendererMesh *
+*************************/
+SampleApexRendererMesh::SampleApexRendererMesh(SampleRenderer::Renderer& renderer, const nvidia::apex::UserRenderResourceDesc& desc) :
+ m_renderer(renderer)
+{
+ m_vertexBuffers = 0;
+ m_numVertexBuffers = 0;
+ m_indexBuffer = 0;
+
+ m_boneBuffer = 0;
+ m_firstBone = 0;
+ m_numBones = 0;
+
+ m_instanceBuffer = 0;
+
+ m_mesh = 0;
+
+ m_meshTransform = physx::PxMat44(physx::PxIdentity);
+
+ m_numVertexBuffers = desc.numVertexBuffers;
+ if (m_numVertexBuffers > 0)
+ {
+ m_vertexBuffers = new SampleApexRendererVertexBuffer*[m_numVertexBuffers];
+ for (uint32_t i = 0; i < m_numVertexBuffers; i++)
+ {
+ m_vertexBuffers[i] = static_cast<SampleApexRendererVertexBuffer*>(desc.vertexBuffers[i]);
+ }
+ }
+
+ uint32_t numVertexBuffers = m_numVertexBuffers;
+#if USE_RENDER_SPRITE_BUFFER
+ m_spriteBuffer = static_cast<SampleApexRendererSpriteBuffer*>(desc.spriteBuffer);
+ if (m_spriteBuffer)
+ {
+ numVertexBuffers = 1;
+ }
+#endif
+
+ RendererVertexBuffer** internalsvbs = 0;
+ if (numVertexBuffers > 0)
+ {
+ internalsvbs = new RendererVertexBuffer*[numVertexBuffers];
+
+#if USE_RENDER_SPRITE_BUFFER
+ if (m_spriteBuffer)
+ {
+ internalsvbs[0] = m_spriteBuffer->m_vertexbuffer;
+ }
+ else
+#endif
+ {
+ for (uint32_t i = 0; i < m_numVertexBuffers; i++)
+ {
+ internalsvbs[i] = m_vertexBuffers[i]->m_vertexbuffer;
+ }
+ }
+ }
+
+ m_indexBuffer = static_cast<SampleApexRendererIndexBuffer*>(desc.indexBuffer);
+ m_boneBuffer = static_cast<SampleApexRendererBoneBuffer*>(desc.boneBuffer);
+ m_instanceBuffer = static_cast<SampleApexRendererInstanceBuffer*>(desc.instanceBuffer);
+
+ m_cullMode = desc.cullMode;
+
+ SampleRenderer::RendererMeshDesc meshdesc;
+ meshdesc.primitives = convertFromApex(desc.primitives);
+
+ meshdesc.vertexBuffers = internalsvbs;
+ meshdesc.numVertexBuffers = numVertexBuffers;
+
+#if USE_RENDER_SPRITE_BUFFER
+ // the sprite buffer currently uses a vb
+ if (m_spriteBuffer)
+ {
+ meshdesc.firstVertex = desc.firstSprite;
+ meshdesc.numVertices = desc.numSprites;
+ }
+ else
+#endif
+ {
+ meshdesc.firstVertex = desc.firstVertex;
+ meshdesc.numVertices = desc.numVerts;
+ }
+
+ {
+ if (m_indexBuffer != 0)
+ {
+ meshdesc.indexBuffer = m_indexBuffer->m_indexbuffer;
+ meshdesc.firstIndex = desc.firstIndex;
+ meshdesc.numIndices = desc.numIndices;
+ }
+ }
+
+ meshdesc.instanceBuffer = m_instanceBuffer ? m_instanceBuffer->m_instanceBuffer : 0;
+ meshdesc.firstInstance = desc.firstInstance;
+ meshdesc.numInstances = desc.numInstances;
+ m_mesh = m_renderer.createMesh(meshdesc);
+ PX_ASSERT(m_mesh);
+ if (m_mesh)
+ {
+ m_meshContext.mesh = m_mesh;
+ m_meshContext.transform = &m_meshTransform;
+ }
+
+#if USE_RENDER_SPRITE_BUFFER
+ // the sprite buffer currently uses a vb
+ if (m_spriteBuffer)
+ {
+ setVertexBufferRange(desc.firstSprite, desc.numSprites);
+ }
+ else
+#endif
+ {
+ setVertexBufferRange(desc.firstVertex, desc.numVerts);
+ }
+ setIndexBufferRange(desc.firstIndex, desc.numIndices);
+ setBoneBufferRange(desc.firstBone, desc.numBones);
+ setInstanceBufferRange(desc.firstInstance, desc.numInstances);
+
+ setMaterial(desc.material);
+
+ if (internalsvbs)
+ {
+ delete [] internalsvbs;
+ }
+}
+
+SampleApexRendererMesh::~SampleApexRendererMesh(void)
+{
+ if (m_mesh)
+ {
+ m_mesh->release();
+ }
+ if (m_vertexBuffers)
+ {
+ delete [] m_vertexBuffers;
+ }
+}
+
+void SampleApexRendererMesh::setVertexBufferRange(uint32_t firstVertex, uint32_t numVerts)
+{
+ if (m_mesh)
+ {
+ m_mesh->setVertexBufferRange(firstVertex, numVerts);
+ }
+}
+
+void SampleApexRendererMesh::setIndexBufferRange(uint32_t firstIndex, uint32_t numIndices)
+{
+ if (m_mesh)
+ {
+ m_mesh->setIndexBufferRange(firstIndex, numIndices);
+ }
+}
+
+void SampleApexRendererMesh::setBoneBufferRange(uint32_t firstBone, uint32_t numBones)
+{
+ m_firstBone = firstBone;
+ m_numBones = numBones;
+}
+
+void SampleApexRendererMesh::setInstanceBufferRange(uint32_t firstInstance, uint32_t numInstances)
+{
+ if (m_mesh)
+ {
+ m_mesh->setInstanceBufferRange(firstInstance, numInstances);
+ }
+}
+
+#if !USE_RENDERER_MATERIAL
+void SampleApexRendererMesh::pickMaterial(SampleRenderer::RendererMeshContext& context, bool hasBones, SampleFramework::SampleMaterialAsset& material, BlendType hasBlending)
+{
+ // use this if it can't find another
+ context.material = material.getMaterial(0);
+ context.materialInstance = material.getMaterialInstance(0);
+
+ // try to find a better one
+ for (size_t i = 0; i < material.getNumVertexShaders(); i++)
+ {
+ if ((material.getMaxBones(i) > 0) == hasBones &&
+ (BLENDING_ANY == hasBlending || material.getMaterial(i)->getBlending() == (BLENDING_ENABLED == hasBlending)))
+ {
+ context.material = material.getMaterial(i);
+ context.materialInstance = material.getMaterialInstance(i);
+ break;
+ }
+ }
+ RENDERER_ASSERT(context.material, "Material has wrong vertex buffers!")
+}
+#endif
+
+void SampleApexRendererMesh::setMaterial(void* material, BlendType hasBlending)
+{
+ if (material)
+ {
+#if USE_RENDERER_MATERIAL
+ m_meshContext.materialInstance = static_cast<SampleRenderer::RendererMaterialInstance*>(material);
+ m_meshContext.material = &m_meshContext.materialInstance->getMaterial();
+#else
+ SampleFramework::SampleMaterialAsset& materialAsset = *static_cast<SampleFramework::SampleMaterialAsset*>(material);
+
+ pickMaterial(m_meshContext, m_boneBuffer != NULL, materialAsset, hasBlending);
+#endif
+
+#if USE_RENDER_SPRITE_BUFFER
+ // get sprite shader variables
+ if (m_spriteBuffer)
+ {
+ m_spriteShaderVariables[0] = m_meshContext.materialInstance->findVariable("windowWidth", SampleRenderer::RendererMaterial::VARIABLE_FLOAT);
+ m_spriteShaderVariables[1] = m_meshContext.materialInstance->findVariable("particleSize", SampleRenderer::RendererMaterial::VARIABLE_FLOAT);
+ m_spriteShaderVariables[2] = m_meshContext.materialInstance->findVariable("positionTexture", SampleRenderer::RendererMaterial::VARIABLE_SAMPLER2D);
+ m_spriteShaderVariables[3] = m_meshContext.materialInstance->findVariable("colorTexture", SampleRenderer::RendererMaterial::VARIABLE_SAMPLER2D);
+ m_spriteShaderVariables[4] = m_meshContext.materialInstance->findVariable("transformTexture", SampleRenderer::RendererMaterial::VARIABLE_SAMPLER2D);
+ m_spriteShaderVariables[5] = m_meshContext.materialInstance->findVariable("vertexTextureWidth", SampleRenderer::RendererMaterial::VARIABLE_FLOAT);
+ m_spriteShaderVariables[6] = m_meshContext.materialInstance->findVariable("vertexTextureHeight", SampleRenderer::RendererMaterial::VARIABLE_FLOAT);
+ }
+#endif
+ }
+ else
+ {
+ m_meshContext.material = 0;
+ m_meshContext.materialInstance = 0;
+ }
+}
+
+void SampleApexRendererMesh::setScreenSpace(bool ss)
+{
+ m_meshContext.screenSpace = ss;
+}
+
+void SampleApexRendererMesh::render(const nvidia::apex::RenderContext& context, bool forceWireframe, SampleFramework::SampleMaterialAsset* overrideMaterial)
+{
+ if (m_mesh && m_meshContext.mesh && m_mesh->getNumVertices() > 0)
+ {
+#if USE_RENDER_SPRITE_BUFFER
+ // set default sprite shader variables
+ if (m_spriteBuffer)
+ {
+ // windowWidth
+ if (m_spriteShaderVariables[0] && m_meshContext.materialInstance)
+ {
+ uint32_t width, height;
+ m_renderer.getWindowSize(width, height);
+ float fwidth = (float)width;
+ m_meshContext.materialInstance->writeData(*m_spriteShaderVariables[0], &fwidth);
+ }
+
+ // position texture
+ if (m_spriteShaderVariables[2] && m_meshContext.materialInstance)
+ {
+ SampleRenderer::RendererTexture* tex = m_spriteBuffer->getTexture(nvidia::apex::RenderSpriteTextureLayout::POSITION_FLOAT4);
+ if(tex) m_meshContext.materialInstance->writeData(*m_spriteShaderVariables[2], &tex);
+ }
+ // color texture
+ if (m_spriteShaderVariables[3] && m_meshContext.materialInstance)
+ {
+ SampleRenderer::RendererTexture* tex = m_spriteBuffer->getTexture(nvidia::apex::RenderSpriteTextureLayout::COLOR_FLOAT4);
+ if(tex)
+ {
+ m_meshContext.materialInstance->writeData(*m_spriteShaderVariables[3], &tex);
+ }
+ else
+ {
+ tex = m_spriteBuffer->getTexture(nvidia::apex::RenderSpriteTextureLayout::COLOR_BGRA8);
+ if(tex)
+ {
+ m_meshContext.materialInstance->writeData(*m_spriteShaderVariables[3], &tex);
+ }
+ else
+ {
+ /* Couldn't find a texture for color */
+ PX_ALWAYS_ASSERT();
+ }
+ }
+ }
+ // transform texture
+ if (m_spriteShaderVariables[4] && m_meshContext.materialInstance)
+ {
+ SampleRenderer::RendererTexture* tex = m_spriteBuffer->getTexture(nvidia::apex::RenderSpriteTextureLayout::SCALE_ORIENT_SUBTEX_FLOAT4);
+ if(tex) m_meshContext.materialInstance->writeData(*m_spriteShaderVariables[4], &tex);
+ }
+ // vertexTextureWidth
+ if (m_spriteShaderVariables[5] && m_meshContext.materialInstance)
+ {
+ SampleRenderer::RendererTexture* tex = m_spriteBuffer->getTexture(nvidia::apex::RenderSpriteTextureLayout::POSITION_FLOAT4);
+ if(!tex) tex = m_spriteBuffer->getTexture(nvidia::apex::RenderSpriteTextureLayout::COLOR_FLOAT4);
+ if(!tex) tex = m_spriteBuffer->getTexture(nvidia::apex::RenderSpriteTextureLayout::COLOR_BGRA8);
+ if(!tex) tex = m_spriteBuffer->getTexture(nvidia::apex::RenderSpriteTextureLayout::SCALE_ORIENT_SUBTEX_FLOAT4);
+ if(tex)
+ {
+ const float width = tex->getWidth() * 1.0f;
+ m_meshContext.materialInstance->writeData(*m_spriteShaderVariables[5], &width);
+ }
+ }
+ // vertexTextureHeight
+ if (m_spriteShaderVariables[6] && m_meshContext.materialInstance)
+ {
+ SampleRenderer::RendererTexture* tex = m_spriteBuffer->getTexture(nvidia::apex::RenderSpriteTextureLayout::POSITION_FLOAT4);
+ if(!tex) tex = m_spriteBuffer->getTexture(nvidia::apex::RenderSpriteTextureLayout::COLOR_FLOAT4);
+ if(!tex) tex = m_spriteBuffer->getTexture(nvidia::apex::RenderSpriteTextureLayout::COLOR_BGRA8);
+ if(!tex) tex = m_spriteBuffer->getTexture(nvidia::apex::RenderSpriteTextureLayout::SCALE_ORIENT_SUBTEX_FLOAT4);
+ if(tex)
+ {
+ const float height = tex->getHeight() * 1.0f;
+ m_meshContext.materialInstance->writeData(*m_spriteShaderVariables[6], &height);
+ }
+ }
+ }
+#endif /* #if USE_RENDER_SPRITE_BUFFER */
+
+ m_meshTransform = context.local2world;
+ if (m_boneBuffer)
+ {
+ // Pass bone texture information
+ m_meshContext.boneTexture = m_boneBuffer->m_boneTexture;
+ m_meshContext.boneTextureHeight = m_boneBuffer->m_maxBones;
+
+ m_meshContext.boneMatrices = m_boneBuffer->m_bones + m_firstBone;
+ m_meshContext.numBones = m_numBones;
+ }
+ switch (m_cullMode)
+ {
+ case nvidia::apex::RenderCullMode::CLOCKWISE:
+ m_meshContext.cullMode = SampleRenderer::RendererMeshContext::CLOCKWISE;
+ break;
+ case nvidia::apex::RenderCullMode::COUNTER_CLOCKWISE:
+ m_meshContext.cullMode = SampleRenderer::RendererMeshContext::COUNTER_CLOCKWISE;
+ break;
+ case nvidia::apex::RenderCullMode::NONE:
+ m_meshContext.cullMode = SampleRenderer::RendererMeshContext::NONE;
+ break;
+ default:
+ PX_ASSERT(0 && "Invalid Cull Mode");
+ }
+ m_meshContext.screenSpace = context.isScreenSpace;
+
+ SampleRenderer::RendererMeshContext tmpContext = m_meshContext;
+ if (forceWireframe)
+ {
+ tmpContext.fillMode = SampleRenderer::RendererMeshContext::LINE;
+ }
+#if !USE_RENDERER_MATERIAL
+ if (overrideMaterial != NULL)
+ {
+ pickMaterial(tmpContext, m_boneBuffer != NULL, *overrideMaterial);
+ }
+#endif
+ m_renderer.queueMeshForRender(tmpContext);
+ }
+}
diff --git a/APEX_1.4/shared/external/src/SampleApexRenderer.cpp b/APEX_1.4/shared/external/src/SampleApexRenderer.cpp
new file mode 100644
index 00000000..895622e4
--- /dev/null
+++ b/APEX_1.4/shared/external/src/SampleApexRenderer.cpp
@@ -0,0 +1,329 @@
+/*
+ * 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 <SampleApexRenderer.h>
+
+#include <SampleApexRenderResources.h>
+
+#if !USE_RENDERER_MATERIAL
+#include <SampleMaterialAsset.h>
+#endif
+
+#include <RenderContext.h>
+#include <UserRenderIndexBufferDesc.h>
+#include <UserRenderInstanceBuffer.h>
+#include <UserRenderResourceDesc.h>
+#include <UserRenderBoneBufferDesc.h>
+#include <UserRenderSpriteBufferDesc.h>
+#include <UserRenderSurfaceBufferDesc.h>
+#include <UserRenderVertexBufferDesc.h>
+#include <UserRenderSpriteBufferDesc.h>
+#include <algorithm> // for std::min
+
+/**********************************
+* SampleApexRenderResourceManager *
+**********************************/
+
+SampleApexRenderResourceManager::SampleApexRenderResourceManager(SampleRenderer::Renderer& renderer) :
+ m_renderer(renderer), m_particleRenderingMechanism(VERTEX_BUFFER_OBJECT)
+{
+ m_numVertexBuffers = 0;
+ m_numIndexBuffers = 0;
+ m_numSurfaceBuffers = 0;
+ m_numBoneBuffers = 0;
+ m_numInstanceBuffers = 0;
+ m_numResources = 0;
+}
+
+SampleApexRenderResourceManager::~SampleApexRenderResourceManager(void)
+{
+ RENDERER_ASSERT(m_numVertexBuffers == 0, "Not all Vertex Buffers were released prior to Render Resource Manager destruction!");
+ RENDERER_ASSERT(m_numIndexBuffers == 0, "Not all Index Buffers were released prior to Render Resource Manager destruction!");
+ RENDERER_ASSERT(m_numSurfaceBuffers == 0, "Not all Surface Buffers were released prior to Render Resource Manager destruction!");
+ RENDERER_ASSERT(m_numBoneBuffers == 0, "Not all Bone Buffers were released prior to Render Resource Manager destruction!");
+ RENDERER_ASSERT(m_numInstanceBuffers == 0, "Not all Instance Buffers were released prior to Render Resource Manager destruction!");
+ RENDERER_ASSERT(m_numResources == 0, "Not all Resources were released prior to Render Resource Manager destruction!");
+}
+
+nvidia::apex::UserRenderVertexBuffer* SampleApexRenderResourceManager::createVertexBuffer(const nvidia::apex::UserRenderVertexBufferDesc& desc)
+{
+ SampleApexRendererVertexBuffer* vb = 0;
+
+ unsigned int numSemantics = 0;
+ for (unsigned int i = 0; i < nvidia::apex::RenderVertexSemantic::NUM_SEMANTICS; i++)
+ {
+ numSemantics += desc.buffersRequest[i] != nvidia::apex::RenderDataFormat::UNSPECIFIED ? 1 : 0;
+ }
+ PX_ASSERT(desc.isValid());
+ if (desc.isValid() && numSemantics > 0)
+ {
+ vb = new SampleApexRendererVertexBuffer(m_renderer, desc);
+ m_numVertexBuffers++;
+ }
+ return vb;
+}
+
+void SampleApexRenderResourceManager::releaseVertexBuffer(nvidia::apex::UserRenderVertexBuffer& buffer)
+{
+ PX_ASSERT(m_numVertexBuffers > 0);
+ m_numVertexBuffers--;
+ delete &buffer;
+}
+
+nvidia::apex::UserRenderIndexBuffer* SampleApexRenderResourceManager::createIndexBuffer(const nvidia::apex::UserRenderIndexBufferDesc& desc)
+{
+ SampleApexRendererIndexBuffer* ib = 0;
+ PX_ASSERT(desc.isValid());
+ if (desc.isValid())
+ {
+ ib = new SampleApexRendererIndexBuffer(m_renderer, desc);
+ m_numIndexBuffers++;
+ }
+ return ib;
+}
+
+void SampleApexRenderResourceManager::releaseIndexBuffer(nvidia::apex::UserRenderIndexBuffer& buffer)
+{
+ PX_ASSERT(m_numIndexBuffers > 0);
+ m_numIndexBuffers--;
+ delete &buffer;
+}
+
+nvidia::apex::UserRenderSurfaceBuffer* SampleApexRenderResourceManager::createSurfaceBuffer(const nvidia::apex::UserRenderSurfaceBufferDesc& desc)
+{
+ SampleApexRendererSurfaceBuffer* sb = 0;
+ PX_ASSERT(desc.isValid());
+ if (desc.isValid())
+ {
+ sb = new SampleApexRendererSurfaceBuffer(m_renderer, desc);
+ m_numSurfaceBuffers++;
+ }
+ return sb;
+}
+
+void SampleApexRenderResourceManager::releaseSurfaceBuffer(nvidia::apex::UserRenderSurfaceBuffer& buffer)
+{
+ PX_ASSERT(m_numSurfaceBuffers > 0);
+ m_numSurfaceBuffers--;
+ delete &buffer;
+}
+
+nvidia::apex::UserRenderBoneBuffer* SampleApexRenderResourceManager::createBoneBuffer(const nvidia::apex::UserRenderBoneBufferDesc& desc)
+{
+ SampleApexRendererBoneBuffer* bb = 0;
+ PX_ASSERT(desc.isValid());
+ if (desc.isValid())
+ {
+ bb = new SampleApexRendererBoneBuffer(m_renderer, desc);
+ m_numBoneBuffers++;
+ }
+ return bb;
+}
+
+void SampleApexRenderResourceManager::releaseBoneBuffer(nvidia::apex::UserRenderBoneBuffer& buffer)
+{
+ PX_ASSERT(m_numBoneBuffers > 0);
+ m_numBoneBuffers--;
+ delete &buffer;
+}
+
+nvidia::apex::UserRenderInstanceBuffer* SampleApexRenderResourceManager::createInstanceBuffer(const nvidia::apex::UserRenderInstanceBufferDesc& desc)
+{
+ SampleApexRendererInstanceBuffer* ib = 0;
+ PX_ASSERT(desc.isValid());
+ if (desc.isValid())
+ {
+ ib = new SampleApexRendererInstanceBuffer(m_renderer, desc);
+ m_numInstanceBuffers++;
+ }
+ return ib;
+}
+
+void SampleApexRenderResourceManager::releaseInstanceBuffer(nvidia::apex::UserRenderInstanceBuffer& buffer)
+{
+ PX_ASSERT(m_numInstanceBuffers > 0);
+ m_numInstanceBuffers--;
+ delete &buffer;
+}
+
+nvidia::apex::UserRenderSpriteBuffer* SampleApexRenderResourceManager::createSpriteBuffer(const nvidia::apex::UserRenderSpriteBufferDesc& desc)
+{
+#if USE_RENDER_SPRITE_BUFFER
+ SampleApexRendererSpriteBuffer* sb = 0;
+ PX_ASSERT(desc.isValid());
+ if (desc.isValid())
+ {
+ // convert SB to VB
+ sb = new SampleApexRendererSpriteBuffer(m_renderer, desc);
+ m_numVertexBuffers++;
+ }
+ return sb;
+#else
+ return NULL;
+#endif
+}
+
+void SampleApexRenderResourceManager::releaseSpriteBuffer(nvidia::apex::UserRenderSpriteBuffer& buffer)
+{
+#if USE_RENDER_SPRITE_BUFFER
+ // LRR: for now, just use a VB
+ PX_ASSERT(m_numVertexBuffers > 0);
+ m_numVertexBuffers--;
+ delete &buffer;
+#endif
+}
+
+nvidia::apex::UserRenderResource* SampleApexRenderResourceManager::createResource(const nvidia::apex::UserRenderResourceDesc& desc)
+{
+ SampleApexRendererMesh* mesh = 0;
+ PX_ASSERT(desc.isValid());
+ if (desc.isValid())
+ {
+ mesh = new SampleApexRendererMesh(m_renderer, desc);
+ m_numResources++;
+ }
+ return mesh;
+}
+
+void SampleApexRenderResourceManager::releaseResource(nvidia::apex::UserRenderResource& resource)
+{
+ PX_ASSERT(m_numResources > 0);
+ m_numResources--;
+ delete &resource;
+}
+
+uint32_t SampleApexRenderResourceManager::getMaxBonesForMaterial(void* material)
+{
+ if (material != NULL)
+ {
+ unsigned int maxBones = 0xffffffff;
+#if USE_RENDERER_MATERIAL
+ // don't yet know if this material even supports bones, but this would be the max...
+ maxBones = RENDERER_MAX_BONES;
+#else
+ SampleFramework::SampleMaterialAsset* materialAsset = static_cast<SampleFramework::SampleMaterialAsset*>(material);
+ for (size_t i = 0; i < materialAsset->getNumVertexShaders(); i++)
+ {
+ unsigned int maxBonesMat = materialAsset->getMaxBones(i);
+ if (maxBonesMat > 0)
+ {
+ maxBones = std::min(maxBones, maxBonesMat);
+ }
+ }
+#endif
+
+ return maxBones != 0xffffffff ? maxBones : 0;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+bool SampleApexRenderResourceManager::getInstanceLayoutData(uint32_t particleCount,
+ uint32_t particleSemanticsBitmap,
+ nvidia::apex::UserRenderInstanceBufferDesc* bufferDesc)
+{
+ using namespace nvidia::apex;
+ RenderDataFormat::Enum positionFormat = RenderInstanceLayoutElement::getSemanticFormat(RenderInstanceLayoutElement::POSITION_FLOAT3);
+ RenderDataFormat::Enum rotationFormat = RenderInstanceLayoutElement::getSemanticFormat(RenderInstanceLayoutElement::ROTATION_SCALE_FLOAT3x3);
+ RenderDataFormat::Enum velocityFormat = RenderInstanceLayoutElement::getSemanticFormat(RenderInstanceLayoutElement::VELOCITY_LIFE_FLOAT4);
+ const uint32_t positionElementSize = RenderDataFormat::getFormatDataSize(positionFormat);
+ const uint32_t rotationElementSize = RenderDataFormat::getFormatDataSize(rotationFormat);
+ const uint32_t velocityElementSize = RenderDataFormat::getFormatDataSize(velocityFormat);
+ bufferDesc->semanticOffsets[RenderInstanceLayoutElement::POSITION_FLOAT3] = 0;
+ bufferDesc->semanticOffsets[RenderInstanceLayoutElement::ROTATION_SCALE_FLOAT3x3] = positionElementSize;
+ bufferDesc->semanticOffsets[RenderInstanceLayoutElement::VELOCITY_LIFE_FLOAT4] = positionElementSize + rotationElementSize;
+ uint32_t strideInBytes = positionElementSize + rotationElementSize + velocityElementSize;
+ bufferDesc->stride = strideInBytes;
+ bufferDesc->maxInstances = particleCount;
+ return true;
+}
+
+bool SampleApexRenderResourceManager::getSpriteLayoutData(uint32_t spriteCount,
+ uint32_t spriteSemanticsBitmap,
+ nvidia::apex::UserRenderSpriteBufferDesc* bufferDesc)
+{
+ using namespace nvidia::apex;
+ if(m_particleRenderingMechanism == VERTEX_TEXTURE_FETCH)
+ {
+ const uint32_t TextureCount = 3;
+
+ uint32_t width = (uint32_t)physx::PxCeil(physx::PxSqrt((float)spriteCount));
+ //make sizeX >= 32 [32 is WARP_SIZE in CUDA]
+ width = physx::PxMax(width, 32U);
+ //compute the next highest power of 2
+ width--;
+ width |= width >> 1;
+ width |= width >> 2;
+ width |= width >> 4;
+ width |= width >> 8;
+ width |= width >> 16;
+ width++;
+
+ uint32_t height = (spriteCount + width - 1) / width;
+ bufferDesc->textureCount = TextureCount;
+ bufferDesc->textureDescs[0].layout = RenderSpriteTextureLayout::POSITION_FLOAT4;
+ bufferDesc->textureDescs[1].layout = RenderSpriteTextureLayout::SCALE_ORIENT_SUBTEX_FLOAT4;
+ bufferDesc->textureDescs[2].layout = RenderSpriteTextureLayout::COLOR_FLOAT4;
+
+ for (uint32_t i = 0; i < TextureCount; ++i)
+ {
+ bufferDesc->textureDescs[i].width = width;
+ bufferDesc->textureDescs[i].height = height;
+
+ const uint32_t ElemSize = RenderDataFormat::getFormatDataSize( RenderSpriteTextureLayout::getLayoutFormat(bufferDesc->textureDescs[i].layout) );
+ bufferDesc->textureDescs[i].pitchBytes = ElemSize * bufferDesc->textureDescs[i].width;
+
+ bufferDesc->textureDescs[i].arrayIndex = 0;
+ bufferDesc->textureDescs[i].mipLevel = 0;
+ }
+
+ bufferDesc->maxSprites = spriteCount;
+ return true;
+ }
+ else if(m_particleRenderingMechanism == VERTEX_BUFFER_OBJECT)
+ {
+ RenderDataFormat::Enum positionFormat = RenderSpriteLayoutElement::getSemanticFormat(RenderSpriteLayoutElement::POSITION_FLOAT3);
+ RenderDataFormat::Enum colorFormat = RenderSpriteLayoutElement::getSemanticFormat(RenderSpriteLayoutElement::COLOR_BGRA8);
+ const uint32_t positionElementSize = RenderDataFormat::getFormatDataSize(positionFormat);
+ const uint32_t colorElementSize = RenderDataFormat::getFormatDataSize(colorFormat);
+ bufferDesc->semanticOffsets[RenderSpriteLayoutElement::POSITION_FLOAT3] = 0;
+ bufferDesc->semanticOffsets[RenderSpriteLayoutElement::COLOR_BGRA8] = positionElementSize;
+ uint32_t strideInBytes = positionElementSize + colorElementSize;
+ bufferDesc->stride = strideInBytes;
+ bufferDesc->maxSprites = spriteCount;
+ bufferDesc->textureCount = 0;
+ return true;
+ }
+ else
+ {
+ PX_ASSERT(0 && "Select a method to update particle render buffer.");
+ }
+ return true;
+}
+
+void SampleApexRenderResourceManager::setMaterial(nvidia::apex::UserRenderResource& resource, void* material)
+{
+ static_cast<SampleApexRendererMesh&>(resource).setMaterial(material);
+}
+
+
+/*********************
+* SampleApexRenderer *
+*********************/
+
+void SampleApexRenderer::renderResource(const nvidia::apex::RenderContext& context)
+{
+ if (context.renderResource)
+ {
+ static_cast<SampleApexRendererMesh*>(context.renderResource)->render(context, mForceWireframe, mOverrideMaterial);
+ }
+}
diff --git a/APEX_1.4/shared/external/src/SampleApexResourceCallback.cpp b/APEX_1.4/shared/external/src/SampleApexResourceCallback.cpp
new file mode 100644
index 00000000..85bc2c33
--- /dev/null
+++ b/APEX_1.4/shared/external/src/SampleApexResourceCallback.cpp
@@ -0,0 +1,757 @@
+/*
+ * Copyright (c) 2008-2015, NVIDIA CORPORATION. All rights reserved.
+ *
+ * NVIDIA CORPORATION and its licensors retain all intellectual property
+ * and proprietary rights in and to this software, related documentation
+ * and any modifications thereto. Any use, reproduction, disclosure or
+ * distribution of this software and related documentation without an express
+ * license agreement from NVIDIA CORPORATION is strictly prohibited.
+ */
+
+
+#include "ApexDefs.h"
+#include "SampleApexResourceCallback.h"
+
+#include <ApexNameSpace.h>
+#include <ApexSDK.h>
+#include <ResourceProvider.h>
+
+#include <DestructibleAsset.h>
+#include <ClothingAsset.h>
+
+#if APEX_USE_PARTICLES
+#include <EmitterAsset.h>
+#include <BasicIosAsset.h>
+#include <BasicFSAsset.h>
+#include <GroundEmitterAsset.h>
+#include <IofxAsset.h>
+#include <ImpactEmitterAsset.h>
+#include <TurbulenceFSAsset.h>
+#include <VelocitySourceAsset.h>
+#include <HeatSourceAsset.h>
+#include <SubstanceSourceAsset.h>
+#include <ModuleParticles.h>
+
+#if PX_PHYSICS_VERSION_MAJOR == 3
+#include <ParticleIosAsset.h>
+#include <ForceFieldAsset.h>
+#endif
+#endif // APEX_USE_PARTICLES
+
+#include "FilterBits.h"
+
+#include <SampleAssetManager.h>
+#include <SampleMaterialAsset.h>
+#include <Renderer.h>
+
+#include <PsString.h>
+#include <PsUtilities.h>
+
+#define INPLACE_BINARY 1
+
+#define DEBUG_RESOURCE_REQUESTS 0
+#if DEBUG_RESOURCE_REQUESTS
+# include <stdio.h>
+# define DEBUG_OUTPUT_FILENAME "debugResourceRequests.txt"
+FILE* gDebugOutput = 0 /* stdout */;
+#endif
+
+#include <sys/stat.h>
+
+/*****************************
+* SampleApexResourceCallback *
+*****************************/
+
+SampleApexResourceCallback::SampleApexResourceCallback(SampleRenderer::Renderer& renderer, SampleFramework::SampleAssetManager& assetManager) :
+ m_renderer(renderer),
+ m_assetManager(assetManager),
+ m_assetPreference(ANY_ASSET)
+{
+#if APEX_USE_PARTICLES
+ mModuleParticles = NULL;
+#endif
+ m_apexSDK = 0;
+ m_numGets = 0;
+
+ m_FilterDatas.reserve(128);
+ m_FilterBits = createFilterBits(); // create the weighted collision filtering helper class
+ m_nxGroupsMask64s.reserve(128);
+
+#if DEBUG_RESOURCE_REQUESTS
+ if (!gDebugOutput)
+ {
+ gDebugOutput = fopen(DEBUG_OUTPUT_FILENAME, "w");
+ }
+#endif
+}
+
+SampleApexResourceCallback::~SampleApexResourceCallback(void)
+{
+ PX_ASSERT(m_numGets == 0);
+ clearResourceSearchPaths();
+ if ( m_FilterBits )
+ {
+ m_FilterBits->release();
+ }
+#if DEBUG_RESOURCE_REQUESTS
+ if (gDebugOutput && gDebugOutput != stdout && gDebugOutput != stderr)
+ {
+ fclose(gDebugOutput);
+ }
+#endif
+}
+
+void SampleApexResourceCallback::addResourceSearchPath(const char* path)
+{
+ uint32_t len = path && *path ? (uint32_t)strlen(path) : 0;
+ if (len)
+ {
+ len++;
+ char* searchPath = new char[len];
+ physx::shdfnd::strlcpy(searchPath, len, path);
+ m_searchPaths.push_back(searchPath);
+ }
+}
+
+void SampleApexResourceCallback::removeResourceSearchPath(const char* path)
+{
+ if (path)
+ {
+ for (uint32_t i = 0; i < m_searchPaths.size(); i++)
+ {
+ if (strcmp(m_searchPaths[i], path) == 0)
+ {
+ m_searchPaths.erase(m_searchPaths.begin() + i);
+ break;
+ }
+ }
+ }
+}
+
+void SampleApexResourceCallback::clearResourceSearchPaths()
+{
+ const size_t numSearchPaths = m_searchPaths.size();
+ for (size_t i = 0; i < numSearchPaths; i++)
+ {
+ delete [] m_searchPaths[i];
+ }
+ m_searchPaths.clear();
+}
+
+
+void SampleApexResourceCallback::registerPhysicalMaterial(const char* name, physx::PxMaterialTableIndex physicalMaterial)
+{
+ PX_ASSERT(m_apexSDK);
+ if (m_apexSDK)
+ {
+ m_apexSDK->getNamedResourceProvider()->setResource(APEX_PHYSICS_MATERIAL_NAME_SPACE, name, (void*)(static_cast<size_t>(physicalMaterial)));
+ }
+}
+void SampleApexResourceCallback::registerSimulationFilterData(const char* name, const physx::PxFilterData& simulationFilterData)
+{
+ PX_ASSERT(m_apexSDK);
+ PX_ASSERT(m_FilterDatas.size() < 128);
+ if (m_apexSDK)
+ {
+ m_FilterDatas.push_back(simulationFilterData);
+ m_apexSDK->getNamedResourceProvider()->setResource(APEX_COLLISION_GROUP_128_NAME_SPACE, name, (void*)&m_FilterDatas.back());
+ }
+}
+
+
+void SampleApexResourceCallback::registerGroupsMask64(const char* name, nvidia::apex::GroupsMask64& groupsMask)
+{
+ PX_ASSERT(m_apexSDK);
+ PX_ASSERT(m_nxGroupsMask64s.size() < 128);
+ if (m_apexSDK)
+ {
+ m_nxGroupsMask64s.push_back(groupsMask);
+ m_apexSDK->getNamedResourceProvider()->setResource(APEX_COLLISION_GROUP_64_NAME_SPACE, name, (void*)(&(m_nxGroupsMask64s.back())));
+ }
+}
+
+
+//GroupsMask64
+void SampleApexResourceCallback::setApexSupport(nvidia::apex::ApexSDK& apexSDK)
+{
+ PX_ASSERT(!m_apexSDK);
+ m_apexSDK = &apexSDK;
+#if APEX_USE_PARTICLES
+ uint32_t count = m_apexSDK->getNbModules();
+ nvidia::apex::Module **modules = m_apexSDK->getModules();
+ for (uint32_t i=0; i<count; i++)
+ {
+ nvidia::apex::Module *m = modules[i];
+ const char *name = m->getName();
+ if ( strcmp(name,"Particles") == 0 )
+ {
+ mModuleParticles = static_cast< nvidia::apex::ModuleParticles *>(m);
+ break;
+ }
+ }
+#endif
+}
+
+physx::PxFileBuf* SampleApexResourceCallback::findApexAsset(const char* assetName)
+{
+ physx::PxFileBuf* outStream = 0;
+ const size_t numSearchPaths = m_searchPaths.size();
+ for (size_t i = 0; i < numSearchPaths; i++)
+ {
+ const char* searchPath = m_searchPaths[i];
+ const uint32_t pathMaxLen = 512;
+ char fullPath[pathMaxLen] = {0};
+ physx::shdfnd::strlcpy(fullPath, pathMaxLen, searchPath);
+ physx::shdfnd::strlcat(fullPath, pathMaxLen, assetName);
+
+ outStream = m_apexSDK->createStream(fullPath, physx::PxFileBuf::OPEN_READ_ONLY);
+ if (outStream && outStream->getOpenMode() == physx::PxFileBuf::OPEN_READ_ONLY)
+ {
+ break;
+ }
+ else if (outStream)
+ {
+ outStream->release();
+ outStream = 0;
+ }
+ }
+ PX_ASSERT(outStream);
+ return outStream;
+}
+
+void SampleApexResourceCallback::findFiles(const char* dir, nvidia::apex::FileHandler& handler)
+{
+ const unsigned int fullMaskLength = 256;
+ char fullMask[fullMaskLength];
+
+ for (size_t i = 0; i < m_searchPaths.size(); i++)
+ {
+ physx::shdfnd::strlcpy(fullMask, fullMaskLength, m_searchPaths[i]);
+ physx::shdfnd::strlcat(fullMask, fullMaskLength, dir);
+
+ Find(fullMask, handler);
+ }
+}
+
+bool SampleApexResourceCallback::doesFileExist(const char* filename, const char* ext)
+{
+ char fullname[512] = {0};
+ physx::shdfnd::strlcat(fullname, sizeof(fullname), filename);
+ physx::shdfnd::strlcat(fullname, sizeof(fullname), ext);
+
+ return doesFileExist(fullname);
+}
+
+bool SampleApexResourceCallback::doesFileExist(const char* filename)
+{
+ const size_t numSearchPaths = m_searchPaths.size();
+ for (size_t i = 0; i < numSearchPaths; i++)
+ {
+ const char* searchPath = m_searchPaths[i];
+ const uint32_t pathMaxLen = 512;
+ char fullPath[pathMaxLen] = {0};
+ physx::shdfnd::strlcpy(fullPath, pathMaxLen, searchPath);
+ physx::shdfnd::strlcat(fullPath, pathMaxLen, filename);
+
+#if PX_X360
+ // exchange '/' with '\'
+ for (unsigned int i = 0, len = strlen(fullPath); i < len; i++)
+ {
+ if (fullPath[i] == '/')
+ {
+ fullPath[i] = '\\';
+ }
+ }
+#endif
+ struct stat info;
+ if ( (stat(fullPath, &info) != -1) && (info.st_mode & (S_IFREG)) != 0)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool SampleApexResourceCallback::isFileReadable(const char* fullPath)
+{
+ physx::PxFileBuf* outStream = m_apexSDK->createStream(fullPath, physx::PxFileBuf::OPEN_READ_ONLY);
+ bool isSuccess = false;
+ if (outStream != NULL)
+ {
+ isSuccess = outStream->getOpenMode() == physx::PxFileBuf::OPEN_READ_ONLY;
+ outStream->release();
+ }
+ return isSuccess;
+}
+
+SampleFramework::SampleAsset* SampleApexResourceCallback::findSampleAsset(const char* assetName, SampleFramework::SampleAsset::Type type)
+{
+ SampleFramework::SampleAsset* asset = 0;
+#if WORK_AROUND_BROKEN_ASSET_PATHS
+ assetName = mapHackyPath(assetName);
+#endif
+ asset = m_assetManager.getAsset(assetName, type);
+ PX_ASSERT(asset);
+ return asset;
+}
+
+#if WORK_AROUND_BROKEN_ASSET_PATHS
+
+struct HackyPath
+{
+ const char* original;
+ const char* mapped;
+};
+
+const char* SampleApexResourceCallback::mapHackyPath(const char* path)
+{
+ const char* mappedPath = path;
+ static const HackyPath map[] =
+ {
+ // John, fix the apex debug renderer to allow the user to specify the material path...
+ { "ApexSolidShaded", "materials/simple_lit_color.xml" },
+ { "ApexWireframe", "materials/simple_unlit.xml" },
+ };
+ const uint32_t mapSize = PX_ARRAY_SIZE(map);
+ for (uint32_t i = 0; i < mapSize; i++)
+ {
+ const HackyPath& hp = map[i];
+ if (!strcmp(hp.original, mappedPath))
+ {
+ mappedPath = hp.mapped;
+ break;
+ }
+ }
+ PX_ASSERT(mappedPath == path && "IF YOU HIT THIS ASSET IT MEANS A HACKY PATH WAS MAPPED, FIX YOUR F-ING ASSET FILES!!!");
+ return mappedPath;
+}
+
+bool SampleApexResourceCallback::xmlFileExtension(const char* name)
+{
+ const char* ext = getFileExtension(name);
+
+ if (ext && !strcmp(".xml", ext))
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// This returns all extensions (.xml.flz, .xml, .ini.old)
+const char* SampleApexResourceCallback::getFileExtension(const char* name)
+{
+ const char* ext = 0;
+ for (const char* c = name; *c; c++)
+ {
+ if (*c == '/' || *c == '\\')
+ {
+ ext = 0;
+ }
+ else if (*c == '.')
+ {
+ ext = c;
+ }
+ }
+
+ return ext;
+}
+#endif //WORK_AROUND_BROKEN_ASSET_PATHS
+
+
+void* SampleApexResourceCallback::requestResource(const char* nameSpace, const char* pname)
+{
+ void* resource = 0;
+
+ bool incrementNumGets = true;
+
+ PX_ASSERT(nameSpace && *nameSpace);
+ PX_ASSERT(pname && *pname);
+
+#if DEBUG_RESOURCE_REQUESTS
+ fprintf(gDebugOutput, "new - %s\n", pname);
+#endif
+
+#if WORK_AROUND_BROKEN_ASSET_PATHS
+ // look for goofy "::" characters...
+ const char* p = pname;
+ while (*p && *p != ':')
+ {
+ p++;
+ }
+ if (*p == ':')
+ {
+ PX_ASSERT(!"Obsolete asset name format, fix your assets!");
+ return NULL;
+ }
+#endif
+
+ if (!strcmp(nameSpace, APEX_MATERIALS_NAME_SPACE))
+ {
+ SampleFramework::SampleAsset* asset = findSampleAsset(pname, SampleFramework::SampleAsset::ASSET_MATERIAL);
+ if (asset)
+ {
+ resource = asset;
+ }
+ }
+ else if (!strcmp(nameSpace, APEX_CUSTOM_VB_NAME_SPACE))
+ {
+ // We currently don't support any custom vertex semantics in the samples,
+ // so the resource will simply be a copy the name. A real game engine
+ // could return a pointer to whatever they want, or a member of an enum of
+ // custom semantics. Whatever resources are returned here will be provided
+ // in the Render Resources API, in the array:
+ // void ** UserRenderVertexBufferDesc::customBuffersIdents
+
+ size_t len = strlen(pname);
+ char* n = new char[len + 1];
+ physx::shdfnd::strlcpy(n, len + 1, pname);
+ resource = (void*)(n);
+ }
+ else if (!strcmp(nameSpace, APEX_COLLISION_GROUP_NAME_SPACE))
+ {
+ PX_ASSERT(0 && "NRP seed failure for" APEX_COLLISION_GROUP_NAME_SPACE);
+ }
+ else if (!strcmp(nameSpace, APEX_COLLISION_GROUP_MASK_NAME_SPACE))
+ {
+ PX_ASSERT(0 && "NRP seed failure for " APEX_COLLISION_GROUP_MASK_NAME_SPACE);
+ }
+ else if (!strcmp(nameSpace, APEX_COLLISION_GROUP_128_NAME_SPACE))
+ {
+ // Note: When using effects that were authored in the ParticleEffectTool the
+ // collision group names are defined by an artist/designer and it is up to the application
+ // to translate that name into a viable set of collision group flags. The algorithm for
+ // how to translate these named fields to a set of bit fields is application specific.
+ // Within the context of the sample framework, we must simply return 'some' valid result.
+ // The previous behavior of this code is that it required that all named collision groups
+ // be predefined ahead of time. However, within the context of using assets authored by
+ // the ParticleEffectTool this is on longer the case. These values are considered 'globals'
+ // within the context of the APEX SDK and do not get specific release calls performed on them.
+ // For this reason the 'number of gets' counter should not be incredmented.
+
+ incrementNumGets = false;
+ const char *equal = strchr(pname,'='); // if it uses the ASCII encoded filter bits format...
+ if ( equal )
+ {
+ physx::PxFilterData *fdata = (physx::PxFilterData *)m_FilterBits->getEncodedFilterData(pname);
+ resource = fdata;
+ }
+ else
+ {
+ static physx::PxFilterData filterData;
+ resource = &filterData;
+ memset(resource,0xFF,sizeof(filterData));
+ }
+
+ }
+ else if (!strcmp(nameSpace, APEX_COLLISION_GROUP_64_NAME_SPACE))
+ {
+ incrementNumGets = false;
+ static uint64_t collisionGroup;
+ resource = &collisionGroup;
+ memset(resource,0xFF,sizeof(collisionGroup));
+// PX_ASSERT(0 && "NRP seed failure for " APEX_COLLISION_GROUP_64_NAME_SPACE);
+ }
+ else if (!strcmp(nameSpace, APEX_PHYSICS_MATERIAL_NAME_SPACE))
+ {
+ PX_ASSERT(0 && "NRP seed failure for " APEX_PHYSICS_MATERIAL_NAME_SPACE);
+ }
+ else
+ {
+#if APEX_USE_PARTICLES
+ if ( mModuleParticles ) // If we are using the Particles module
+ {
+ // See if this data for this resource was preloaded into the particles module.
+ NvParameterized::Interface *iface = mModuleParticles->locateResource(pname,nameSpace);
+ if ( iface )
+ {
+ NvParameterized::Interface *copyInterface=NULL;
+ iface->clone(copyInterface); // Create a copy of the parameterize data.
+ PX_ASSERT(copyInterface);
+ if ( copyInterface )
+ {
+ nvidia::apex::Asset *asset = m_apexSDK->createAsset(copyInterface,pname); // Create the asset using this NvParameterized::Inteface data
+ PX_ASSERT(asset);
+ resource = asset; // return this resource that we just created
+ if ( asset == NULL )
+ {
+ // If it failed to create the asset; destroy the interface data.
+ copyInterface->destroy();
+ }
+ }
+ }
+ }
+#endif
+
+ if ( resource == NULL )
+ {
+ nvidia::apex::Asset* asset = 0;
+
+ if (
+#if PX_PHYSICS_VERSION_MAJOR == 3
+ #if APEX_USE_PARTICLES
+ !strcmp(nameSpace, EMITTER_AUTHORING_TYPE_NAME) ||
+ !strcmp(nameSpace, GROUND_EMITTER_AUTHORING_TYPE_NAME) ||
+ !strcmp(nameSpace, IMPACT_EMITTER_AUTHORING_TYPE_NAME) ||
+ !strcmp(nameSpace, PARTICLE_IOS_AUTHORING_TYPE_NAME) ||
+ !strcmp(nameSpace, FORCEFIELD_AUTHORING_TYPE_NAME) ||
+ !strcmp(nameSpace, IOFX_AUTHORING_TYPE_NAME) ||
+ !strcmp(nameSpace, BASIC_IOS_AUTHORING_TYPE_NAME) ||
+ !strcmp(nameSpace, JET_FS_AUTHORING_TYPE_NAME) ||
+ !strcmp(nameSpace, ATTRACTOR_FS_AUTHORING_TYPE_NAME) ||
+ !strcmp(nameSpace, NOISE_FS_AUTHORING_TYPE_NAME) ||
+ !strcmp(nameSpace, VORTEX_FS_AUTHORING_TYPE_NAME) ||
+ !strcmp(nameSpace, WIND_FS_AUTHORING_TYPE_NAME) ||
+ !strcmp(nameSpace, TURBULENCE_FS_AUTHORING_TYPE_NAME) ||
+ !strcmp(nameSpace, VELOCITY_SOURCE_AUTHORING_TYPE_NAME) ||
+ !strcmp(nameSpace, HEAT_SOURCE_AUTHORING_TYPE_NAME) ||
+ !strcmp(nameSpace, SUBSTANCE_SOURCE_AUTHORING_TYPE_NAME) ||
+ #endif // APEX_USE_PARTICLES
+
+ !strcmp(nameSpace, DESTRUCTIBLE_AUTHORING_TYPE_NAME) ||
+ !strcmp(nameSpace, RENDER_MESH_AUTHORING_TYPE_NAME) ||
+#endif
+ !strcmp(nameSpace, CLOTHING_AUTHORING_TYPE_NAME)
+ )
+ {
+ // Assets that are using NvParameterized (and serialized outside of APEX)
+ // currently have an XML extension.
+ PX_ASSERT(pname);
+
+ physx::PxFileBuf* stream = 0;
+
+ const char* ext = getFileExtension(pname);
+ if (ext)
+ {
+ stream = findApexAsset(pname);
+ }
+ else
+ {
+ if (XML_ASSET == m_assetPreference)
+ {
+ if (doesFileExist(pname, ".apx"))
+ {
+ ext = ".apx";
+ }
+
+ if (!ext && doesFileExist(pname, ".apb"))
+ {
+ ext = ".apb";
+ }
+ }
+ else if (BIN_ASSET == m_assetPreference)
+ {
+ if (!ext && doesFileExist(pname, ".apb"))
+ {
+ ext = ".apb";
+ }
+
+ if (!ext && doesFileExist(pname, ".apx"))
+ {
+ ext = ".apx";
+ }
+ }
+ else
+ {
+ // We prefer binary files in shipping builds
+
+ if (!ext && doesFileExist(pname, ".apx"))
+ {
+ ext = ".apx";
+ }
+
+ if (!ext && doesFileExist(pname, ".apb"))
+ {
+ ext = ".apb";
+ }
+ }
+
+ PX_ASSERT(ext);
+ if (ext)
+ {
+ char fullname[512] = {0};
+ physx::shdfnd::strlcpy(fullname, sizeof(fullname), pname);
+ physx::shdfnd::strlcat(fullname, sizeof(fullname), ext);
+
+ stream = findApexAsset(fullname);
+ }
+ }
+
+ if (stream)
+ {
+ // we really shouldn't have extensions in our asset names, and apps should
+ // determine the serialization type using this ApesSDK::getSerializeType() method
+ NvParameterized::Serializer::SerializeType serType = m_apexSDK->getSerializeType(*stream);
+
+ if (ext)
+ {
+ NvParameterized::Serializer::SerializeType iSerType;
+ if (0 == strcmp(".apx", ext))
+ {
+ iSerType = NvParameterized::Serializer::NST_XML;
+ }
+ else if (0 == strcmp(".apb", ext))
+ {
+ iSerType = NvParameterized::Serializer::NST_BINARY;
+ }
+ else
+ {
+ iSerType = NvParameterized::Serializer::NST_LAST;
+ PX_ASSERT(0 && "Invalid asset file extension");
+ }
+
+ // PH: If you end up here, you have a binary file with an xml extension (.apx or .xml) or vice versa
+ PX_ASSERT(iSerType == serType && "Wrong file extension??");
+ PX_UNUSED(iSerType);
+ }
+
+ NvParameterized::Serializer::ErrorType serError;
+
+ NvParameterized::SerializePlatform platform;
+ serError = m_apexSDK->getSerializePlatform(*stream, platform);
+ PX_ASSERT(serError == NvParameterized::Serializer::ERROR_NONE);
+
+ NvParameterized::Serializer* ser = m_apexSDK->createSerializer(serType);
+ PX_ASSERT(ser);
+ if (NULL==ser)
+ {
+ if (m_renderer.getErrorCallback())
+ {
+ // emit a "missing asset" warning using output error stream
+ char msg[1024];
+
+ physx::shdfnd::snprintf(msg, sizeof(msg), "Error creating the serializer for asset <%s> in namespace <%s>", pname, nameSpace);
+ m_renderer.getErrorCallback()->reportError(physx::PxErrorCode::eDEBUG_WARNING, msg, __FILE__, __LINE__);
+ }
+
+ if (stream)
+ {
+ stream->release();
+ stream = NULL;
+ }
+ return NULL;
+ }
+
+ NvParameterized::Serializer::DeserializedData data;
+ NvParameterized::SerializePlatform currentPlatform;
+ m_apexSDK->getCurrentPlatform(currentPlatform);
+ NvParameterized::Traits* t = m_apexSDK->getParameterizedTraits();
+ uint32_t len = stream->getFileLength();
+
+ if (NvParameterized::Serializer::NST_BINARY == serType && INPLACE_BINARY && platform == currentPlatform)
+ {
+ void* p = t->alloc(len);
+ stream->read(p, len);
+ serError = ser->deserializeInplace(p, len, data);
+ }
+ else if (NvParameterized::Serializer::NST_BINARY == serType)
+ {
+ // If the asset is binary but not inplace, read it into a memory buffer (MUCH faster with PS3 dev env).
+ // We could do this with XML files as well, but if there's a huge XML asset the consoles may fail on
+ // the allocation.
+ void* p = t->alloc(len);
+ stream->read(p, len);
+ physx::PxFileBuf* memStream = m_apexSDK->createMemoryReadStream(p, len);
+
+ serError = ser->deserialize(*memStream, data);
+
+ m_apexSDK->releaseMemoryReadStream(*memStream);
+ t->free(p);
+ }
+ else
+ {
+ serError = ser->deserialize(*stream, data);
+ }
+
+ if (serError == NvParameterized::Serializer::ERROR_NONE && data.size() == 1)
+ {
+ NvParameterized::Interface* params = data[0];
+ asset = m_apexSDK->createAsset(params, pname);
+ PX_ASSERT(asset && "ERROR Creating NvParameterized Asset");
+ }
+ else
+ {
+ PX_ASSERT(0 && "ERROR Deserializing NvParameterized Asset");
+ }
+
+ stream->release();
+ ser->release();
+ }
+ }
+
+
+ PX_ASSERT(asset);
+ if (asset)
+ {
+ bool rightType = strcmp(nameSpace, asset->getObjTypeName()) == 0;
+ PX_ASSERT(rightType);
+ if (rightType)
+ {
+ resource = asset;
+ }
+ else
+ {
+ m_apexSDK->releaseAsset(*asset);
+ asset = 0;
+ }
+ }
+ }
+ }
+
+ if (resource )
+ {
+ if ( incrementNumGets )
+ {
+ m_numGets++;
+ }
+ }
+ else if (m_renderer.getErrorCallback())
+ {
+ // emit a "missing asset" warning using output error stream
+ char msg[1024];
+
+ physx::shdfnd::snprintf(msg, sizeof(msg), "Could not find asset <%s> in namespace <%s>", pname, nameSpace);
+ m_renderer.getErrorCallback()->reportError(physx::PxErrorCode::eDEBUG_WARNING, msg, __FILE__, __LINE__);
+ }
+
+ return resource;
+}
+
+void SampleApexResourceCallback::releaseResource(const char* nameSpace, const char* name, void* resource)
+{
+ PX_ASSERT(resource);
+ PX_UNUSED(name);
+ if (resource)
+ {
+
+#if DEBUG_RESOURCE_REQUESTS
+ fprintf(gDebugOutput, "free - %s\n", name);
+#endif
+ if (!strcmp(nameSpace, APEX_MATERIALS_NAME_SPACE))
+ {
+ SampleFramework::SampleMaterialAsset* asset = static_cast<SampleFramework::SampleMaterialAsset*>(resource);
+ m_assetManager.returnAsset(*asset);
+ }
+ else if (!strcmp(nameSpace, APEX_CUSTOM_VB_NAME_SPACE))
+ {
+ delete(char*) resource; // char* allocated above with new
+ }
+ else if (!strcmp(nameSpace, APEX_COLLISION_GROUP_128_NAME_SPACE))
+ {
+ PX_ALWAYS_ASSERT();
+ }
+ else if (!strcmp(nameSpace, APEX_COLLISION_GROUP_64_NAME_SPACE))
+ {
+ PX_ALWAYS_ASSERT();
+ }
+ else
+ {
+ nvidia::apex::Asset* asset = (nvidia::apex::Asset*)resource;
+ m_apexSDK->releaseAsset(*asset);
+ }
+ m_numGets--;
+ }
+}
diff --git a/APEX_1.4/shared/external/src/SkeletalAnim.cpp b/APEX_1.4/shared/external/src/SkeletalAnim.cpp
new file mode 100644
index 00000000..2e969776
--- /dev/null
+++ b/APEX_1.4/shared/external/src/SkeletalAnim.cpp
@@ -0,0 +1,1335 @@
+/*
+ * 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 <ApexUsingNamespace.h>
+#include <PsFileBuffer.h>
+#include <MeshImport.h>
+#include <AutoGeometry.h>
+#include <PsString.h>
+
+#include "SkeletalAnim.h"
+#include "TriangleMesh.h"
+#include <RenderDebugInterface.h>
+#include "PsMathUtils.h"
+
+#include "PxInputDataFromPxFileBuf.h"
+
+#include <algorithm>
+
+namespace Samples
+{
+
+// -------------------------------------------------------------------
+void SkeletalBone::clear()
+{
+ name = "";
+ id = -1;
+ pose = physx::PxTransform(physx::PxIdentity);
+ bindWorldPose = physx::PxMat44(physx::PxIdentity);
+ invBindWorldPose = physx::PxMat44(physx::PxIdentity);
+ currentWorldPose = physx::PxMat44(physx::PxIdentity);
+
+ scale = physx::PxVec3(1.0f, 1.0f, 1.0f);
+ parent = -1;
+ firstChild = -1;
+ numChildren = 0;
+ firstVertex = -1;
+ boneOption = 0;
+ inflateConvex = 0.0f;
+ minimalBoneWeight = 0.4f;
+ numShapes = 0;
+ selected = false;
+ allowPrimitives = true;
+ dirtyParams = false;
+ manualShapes = false;
+ isRoot = false;
+ isRootLock = false;
+}
+
+// -------------------------------------------------------------------
+void BoneKeyFrame::clear()
+{
+ relPose = physx::PxTransform(physx::PxIdentity);
+ time = 0.0f;
+ scale = physx::PxVec3(1.0f, 1.0f, 1.0f);
+}
+
+// -------------------------------------------------------------------
+void BoneTrack::clear()
+{
+ firstFrame = -1;
+ numFrames = 0;
+}
+
+// -------------------------------------------------------------------
+void SkeletalAnimation::clear()
+{
+ name = "";
+ mBoneTracks.clear();
+ minTime = 0.0f;
+ maxTime = 0.0f;
+};
+
+// -------------------------------------------------------------------
+SkeletalAnim::SkeletalAnim()
+{
+ clear();
+}
+
+// -------------------------------------------------------------------
+SkeletalAnim::~SkeletalAnim()
+{
+ clear();
+}
+
+// -------------------------------------------------------------------
+void SkeletalAnim::clear()
+{
+ ragdollMode = false;
+
+ mBones.clear();
+ mBones.resize(0);
+
+ for (uint32_t i = 0; i < mAnimations.size(); i++)
+ {
+ delete mAnimations[i];
+ }
+
+ mAnimations.clear();
+ mAnimations.resize(0);
+
+ mKeyFrames.clear();
+ mKeyFrames.resize(0);
+
+ mSkinningMatrices.clear();
+ mSkinningMatrices.resize(0);
+ mSkinningMatricesWorld.clear();
+ mSkinningMatricesWorld.resize(0);
+
+ mParent = NULL;
+}
+
+// -------------------------------------------------------------------
+int SkeletalAnim::findBone(const std::string& name)
+{
+ for (uint32_t i = 0; i < mBones.size(); i++)
+ {
+ if (mBones[i].name == name)
+ {
+ return (int)i;
+ }
+ }
+ return -1;
+}
+
+// -------------------------------------------------------------------
+void SkeletalAnim::interpolateBonePose(int animNr, int boneNr, float time, physx::PxTransform& pose, physx::PxVec3& scale)
+{
+ // the default
+ pose = physx::PxTransform(physx::PxIdentity);
+ scale = physx::PxVec3(1.0f, 1.0f, 1.0f);
+
+ const std::vector<SkeletalAnimation*>& animations = mParent == NULL ? mAnimations : mParent->mAnimations;
+ const std::vector<BoneKeyFrame>& keyFrames = mParent == NULL ? mKeyFrames : mParent->mKeyFrames;
+
+ if (animNr < 0 || animNr >= (int)animations.size())
+ {
+ return;
+ }
+ if (boneNr < 0 || boneNr >= (int)animations[(uint32_t)animNr]->mBoneTracks.size())
+ {
+ return;
+ }
+
+ BoneTrack& t = animations[(uint32_t)animNr]->mBoneTracks[(uint32_t)boneNr];
+ if (t.numFrames == 0)
+ {
+ return;
+ }
+
+ // special cases
+ int frameNr = -1;
+ if (t.numFrames == 1)
+ {
+ frameNr = t.firstFrame;
+ }
+ else if (time <= keyFrames[(uint32_t)t.firstFrame].time)
+ {
+ frameNr = t.firstFrame;
+ }
+ else if (time >= keyFrames[uint32_t(t.firstFrame + t.numFrames - 1)].time)
+ {
+ frameNr = t.firstFrame + t.numFrames - 1;
+ }
+
+ if (frameNr >= 0)
+ {
+ pose = keyFrames[(uint32_t)frameNr].relPose;
+ scale = keyFrames[(uint32_t)frameNr].scale;
+ return;
+ }
+ // binary search
+ uint32_t l = (uint32_t)t.firstFrame;
+ uint32_t r = uint32_t(t.firstFrame + t.numFrames - 1);
+ while (r > l + 1)
+ {
+ uint32_t m = (l + r) / 2;
+ if (keyFrames[m].time == time)
+ {
+ pose = keyFrames[m].relPose;
+ scale = keyFrames[m].scale;
+ return;
+ }
+ else if (keyFrames[m].time > time)
+ {
+ r = m;
+ }
+ else
+ {
+ l = m;
+ }
+ }
+ float dt = keyFrames[r].time - keyFrames[l].time;
+ // avoid singular case
+ if (dt == 0.0f)
+ {
+ pose = keyFrames[l].relPose;
+ scale = keyFrames[l].scale;
+ }
+
+ // interpolation
+ float sr = (time - keyFrames[l].time) / dt;
+ float sl = 1.0f - sr;
+
+ scale = keyFrames[l].scale * sl + keyFrames[r].scale * sr;
+ pose.p = keyFrames[l].relPose.p * sl + keyFrames[r].relPose.p * sr;
+ pose.q = physx::shdfnd::slerp(sr, keyFrames[l].relPose.q, keyFrames[r].relPose.q);
+}
+
+// -------------------------------------------------------------------
+void SkeletalAnim::setBindPose()
+{
+ PX_ASSERT(mBones.size() == mSkinningMatrices.size());
+ PX_ASSERT(mBones.size() == mSkinningMatricesWorld.size());
+ for (uint32_t i = 0; i < mBones.size(); i++)
+ {
+ mSkinningMatrices[i] = physx::PxMat44(physx::PxIdentity);
+ mBones[i].currentWorldPose = mBones[i].bindWorldPose;
+ mSkinningMatricesWorld[i] = mBones[i].currentWorldPose;
+ }
+}
+
+// -------------------------------------------------------------------
+void SkeletalAnim::setAnimPose(int animNr, float time, bool lockRootbone /* = false */)
+{
+ if (animNr >= 0)
+ {
+ for (uint32_t i = 0; i < mBones.size(); i++)
+ {
+ if (mBones[i].parent < 0)
+ {
+ setAnimPoseRec(animNr, (int)i, time, lockRootbone);
+ }
+ }
+
+ PX_ASSERT(mBones.size() == mSkinningMatrices.size());
+ PX_ASSERT(mBones.size() == mSkinningMatricesWorld.size());
+ for (uint32_t i = 0; i < mBones.size(); i++)
+ {
+ SkeletalBone& b = mBones[i];
+ mSkinningMatrices[i] = b.currentWorldPose * b.invBindWorldPose;
+ mSkinningMatricesWorld[i] = b.currentWorldPose;
+ }
+ }
+ else
+ {
+ for (uint32_t i = 0; i < mBones.size(); i++)
+ {
+ mSkinningMatrices[i] = physx::PxMat44(physx::PxIdentity);
+ mSkinningMatricesWorld[i] = mBones[i].bindWorldPose;
+ }
+ }
+}
+
+// -------------------------------------------------------------------
+void SkeletalAnim::setBoneCollision(uint32_t boneNr, int option)
+{
+ if (mBones[boneNr].boneOption != option)
+ {
+ if (mBones[boneNr].boneOption == HACD::BO_COLLAPSE || option == HACD::BO_COLLAPSE)
+ {
+ // PH: Follow up the hierarchy until something is not set to collapse and mark all dirty
+ int current = mBones[boneNr].parent;
+ while (current != -1)
+ {
+ mBones[(uint32_t)current].dirtyParams = true;
+ if (mBones[(uint32_t)current].boneOption != HACD::BO_COLLAPSE)
+ {
+ break;
+ }
+
+ if (mBones[(uint32_t)current].parent == current)
+ {
+ break;
+ }
+
+ current = mBones[(uint32_t)current].parent;
+ }
+ }
+ mBones[boneNr].dirtyParams = true;
+
+ // Find all children that collapse into this bone and mark them dirty
+ for (uint32_t i = 0; i < mBones.size(); i++)
+ {
+ // See whether boneNr is one of its parents
+ bool found = false;
+ uint32_t current = i;
+ while (current != (uint32_t)-1 && !found)
+ {
+ if (current == boneNr)
+ {
+ found = true;
+ }
+
+ if (mBones[current].boneOption != HACD::BO_COLLAPSE)
+ {
+ break;
+ }
+
+ if ((int)current == mBones[current].parent)
+ {
+ break;
+ }
+
+ current = (uint32_t)mBones[current].parent;
+ }
+ if (found)
+ {
+ mBones[i].dirtyParams = true;
+ }
+ }
+ }
+
+ mBones[boneNr].boneOption = option;
+}
+
+// -------------------------------------------------------------------
+void SkeletalAnim::setAnimPoseRec(int animNr, int boneNr, float time, bool lockBoneTranslation)
+{
+ SkeletalBone& b = mBones[(uint32_t)boneNr];
+
+ {
+ physx::PxTransform keyPose;
+ physx::PxVec3 keyScale;
+
+ // query the first frame instead of the current one if you want to lock this bone
+ float myTime = (lockBoneTranslation && b.isRootLock) ? 0.0f : time;
+ interpolateBonePose(animNr, boneNr, myTime, keyPose, keyScale);
+
+ // todo: consider scale
+ physx::PxTransform combinedPose;
+ combinedPose.p = b.pose.p + keyPose.p;
+ combinedPose.q = b.pose.q * keyPose.q;
+
+ if (b.parent < 0)
+ {
+ b.currentWorldPose = combinedPose;
+ }
+ else
+ {
+ b.currentWorldPose = mBones[(uint32_t)b.parent].currentWorldPose * combinedPose;
+ }
+ }
+
+ const int* children = mParent == NULL ? &mChildren[0] : &mParent->mChildren[0];
+ for (int i = b.firstChild; i < b.firstChild + b.numChildren; i++)
+ {
+ setAnimPoseRec(animNr, children[i], time, lockBoneTranslation);
+ }
+}
+
+// -------------------------------------------------------------------
+bool SkeletalAnim::loadFromMeshImport(mimp::MeshSystemContainer* msc, std::string& error, bool onlyAddAnimation)
+{
+ bool ret = false;
+
+ if (!onlyAddAnimation)
+ {
+ mBones.clear();
+ mSkinningMatrices.clear();
+ mSkinningMatricesWorld.clear();
+
+ for (unsigned int i = 0; i < mAnimations.size(); i++)
+ {
+ SkeletalAnimation* a = mAnimations[i];
+ delete a;
+ }
+ mAnimations.clear();
+ }
+
+ bool addAnimation = true;
+
+ if (msc)
+ {
+ mimp::MeshSystem* ms = mimp::gMeshImport->getMeshSystem(msc);
+ if (onlyAddAnimation && ms->mSkeletonCount > 0)
+ {
+ std::vector<int> overwriteBindPose(ms->mSkeletons[0]->mBoneCount, -1);
+ // figure out how those bones map to each other.
+ int numNotEqual = 0;
+ int numNonZeroMatches = 0;
+ for (int i = 0; i < ms->mSkeletons[0]->mBoneCount; i++)
+ {
+ mimp::MeshBone& bone = ms->mSkeletons[0]->mBones[i];
+ for (size_t j = 0; j < mBones.size(); j++)
+ {
+ if (mBones[j].name.compare(ms->mSkeletons[0]->mBones[i].mName) == 0)
+ {
+ // found one, but let's see if the bind pose also matches more or less
+ physx::PxTransform inputPose;
+ inputPose.p = *(physx::PxVec3*)bone.mPosition;
+ inputPose.q = *(physx::PxQuat*)bone.mOrientation;
+
+
+ physx::PxTransform pose(inputPose);
+
+ physx::PxTransform poseOld(mBones[j].pose);
+
+ bool equal = (pose.p - poseOld.p).magnitude() <= ((0.5f * pose.p + 0.5f * poseOld.p).magnitude() * 0.01f);
+
+ if (equal && !pose.p.isZero())
+ {
+ numNonZeroMatches++;
+ }
+
+ if (!equal && (inputPose.p.isZero() || numNonZeroMatches > 0))
+ {
+ // identity, skip the new bone's bind pose
+ continue;
+ }
+
+ if (!equal)
+ {
+ char buf[128];
+ physx::shdfnd::snprintf(buf, 128, "Bone %d (%s) does not match bind pose\n", (int)i, ms->mSkeletons[0]->mBones[i].mName);
+ error.append(buf);
+
+ numNotEqual++;
+ }
+
+ overwriteBindPose[(uint32_t)i] = (int)j;
+ break;
+ }
+ }
+ }
+
+ if (numNotEqual > 0)
+ {
+ error = std::string("Failed to load animation:\n") + error;
+ addAnimation = false;
+ }
+ else
+ {
+ // reset all bind poses exactly, now that we know they match pretty well
+ for (uint32_t i = 0; i < (uint32_t)ms->mSkeletons[0]->mBoneCount; i++)
+ {
+ mimp::MeshBone& bone = ms->mSkeletons[0]->mBones[i];
+
+ if (overwriteBindPose[i] != -1)
+ {
+ physx::PxTransform inputPose;
+ inputPose.p = *(physx::PxVec3*)bone.mPosition;
+ inputPose.q = *(physx::PxQuat*)bone.mOrientation;
+ physx::PxTransform pose(inputPose);
+
+ mBones[(uint32_t)overwriteBindPose[i]].pose = pose;
+ }
+ }
+ }
+ }
+ else if (ms->mSkeletonCount)
+ {
+ mimp::MeshSkeleton* sk = ms->mSkeletons[0];
+
+ for (int i = 0; i < sk->mBoneCount; i++)
+ {
+ mimp::MeshBone& b = sk->mBones[i];
+ SkeletalBone sb;
+ sb.clear();
+ sb.name = b.mName;
+ sb.id = i;
+ sb.parent = b.mParentIndex;
+ sb.firstChild = 0;
+ sb.numChildren = 0;
+ sb.isRootLock = b.mParentIndex == 0; // lock the second bone in the hierarchy, first one is the scene root, not the anim root (for fbx files)
+
+ PxQuatFromArray(sb.pose.q, b.mOrientation);
+ PxVec3FromArray(sb.pose.p, b.mPosition);
+ PxVec3FromArray(sb.scale, b.mScale);
+
+ for (uint32_t bi = 0; bi < mBones.size(); bi++)
+ {
+ if (mBones[bi].name == b.mName)
+ {
+ if (error.empty())
+ {
+ error = "Duplicated Bone Names, rename one:\n";
+ }
+
+ error.append(b.mName);
+ error.append("\n");
+ }
+ }
+
+ mBones.push_back(sb);
+ }
+
+ ret = true; // allow loading a skeleton without animation
+ }
+
+ if (ms->mAnimationCount && addAnimation)
+ {
+ for (unsigned int i = 0; i < ms->mAnimationCount; i++)
+ {
+ mimp::MeshAnimation* animation = ms->mAnimations[i];
+ SkeletalAnimation* anim = new SkeletalAnimation;
+ anim->clear();
+ if (ms->mAnimationCount == 1 && ms->mAssetName != NULL)
+ {
+ const char* lastDir = std::max(strrchr(ms->mAssetName, '/'), strrchr(ms->mAssetName, '\\'));
+ anim->name = lastDir != NULL ? lastDir + 1 : ms->mAssetName;
+ }
+ else
+ {
+ anim->name = animation->mName;
+ }
+
+ size_t numBones = mBones.size();
+ anim->mBoneTracks.resize(numBones);
+ for (uint32_t j = 0; j < numBones; j++)
+ {
+ anim->mBoneTracks[j].clear();
+ }
+
+ for (uint32_t j = 0; j < (uint32_t)animation->mTrackCount; j++)
+ {
+ mimp::MeshAnimTrack* track = animation->mTracks[j];
+ std::string boneName = track->mName;
+ int boneNr = findBone(boneName);
+ if (boneNr >= 0 && boneNr < (int32_t)numBones)
+ {
+ anim->mBoneTracks[(uint32_t)boneNr].firstFrame = (int)mKeyFrames.size();
+ anim->mBoneTracks[(uint32_t)boneNr].numFrames = track->mFrameCount;
+
+ physx::PxTransform parent = mBones[(uint32_t)boneNr].pose;
+
+ float ftime = 0;
+
+ for (uint32_t k = 0; k < (uint32_t)track->mFrameCount; k++)
+ {
+ mimp::MeshAnimPose& pose = track->mPose[k];
+ BoneKeyFrame frame;
+ frame.clear();
+
+ physx::PxTransform mat;
+ PxQuatFromArray(mat.q, pose.mQuat);
+ PxVec3FromArray(mat.p, pose.mPos);
+
+ frame.time = ftime;
+ PxVec3FromArray(frame.scale, pose.mScale);
+
+ frame.relPose.p = mat.p - parent.p;
+ frame.relPose.q = parent.q.getConjugate() * mat.q;
+
+ mKeyFrames.push_back(frame);
+
+ // eazymesh samples at 60 Hz, not 1s
+ ftime += track->mDtime / 200.f;
+ }
+ }
+ else if (!onlyAddAnimation)
+ {
+ // if onlyAddAnimation is set, the bone count does not have to match up, additional bones are just ignored
+ PX_ASSERT(0);
+ }
+
+ }
+
+ mAnimations.push_back(anim);
+ }
+
+ ret = true;
+ }
+
+ physx::PxTransform matId(physx::PxIdentity);
+ mSkinningMatrices.resize((uint32_t)mBones.size(), matId);
+ mSkinningMatricesWorld.resize((uint32_t)mBones.size(), matId);
+ init(!onlyAddAnimation);
+ }
+
+ return ret;
+}
+
+// -------------------------------------------------------------------
+bool SkeletalAnim::saveToMeshImport(mimp::MeshSystemContainer* msc)
+{
+#if PX_WINDOWS_FAMILY == 0
+ PX_UNUSED(msc);
+ return false;
+#else
+
+ if (msc == NULL)
+ {
+ return false;
+ }
+
+ mimp::MeshSystem* ms = mimp::gMeshImport->getMeshSystem(msc);
+ if (ms == NULL)
+ {
+ return false;
+ }
+
+ ms->mSkeletonCount = 1;
+ ms->mSkeletons = (mimp::MeshSkeleton**)::malloc(sizeof(mimp::MeshSkeleton*));
+ ms->mSkeletons[0] = new mimp::MeshSkeleton;
+
+ ms->mSkeletons[0]->mBoneCount = (int)mBones.size();
+ ms->mSkeletons[0]->mBones = (mimp::MeshBone*)::malloc(sizeof(mimp::MeshBone) * mBones.size());
+ for (size_t i = 0; i < mBones.size(); i++)
+ {
+ mimp::MeshBone& bone = ms->mSkeletons[0]->mBones[i];
+
+ size_t nameLen = mBones[i].name.length() + 1;
+ bone.mName = (char*)::malloc(sizeof(char) * nameLen);
+ strcpy_s((char*)bone.mName, nameLen, mBones[i].name.c_str());
+
+ (physx::PxQuat&)bone.mOrientation = mBones[i].pose.q;
+
+ bone.mParentIndex = mBones[i].parent;
+
+ (physx::PxVec3&)bone.mPosition = mBones[i].pose.p;
+
+ (physx::PxVec3&)bone.mScale = mBones[i].scale;
+ }
+
+ ms->mAnimationCount = (unsigned int)mAnimations.size();
+ ms->mAnimations = (mimp::MeshAnimation**)::malloc(sizeof(mimp::MeshAnimation*) * mAnimations.size());
+ for (unsigned int a = 0; a < ms->mAnimationCount; a++)
+ {
+ ms->mAnimations[a] = new mimp::MeshAnimation;
+
+ PX_ASSERT(mAnimations[a] != NULL);
+ size_t nameLen = mAnimations[a]->name.length() + 1;
+ ms->mAnimations[a]->mName = (char*)::malloc(sizeof(char) * nameLen);
+ strcpy_s((char*)ms->mAnimations[a]->mName, nameLen, mAnimations[a]->name.c_str());
+
+ unsigned int trackCount = 0;
+ for (size_t i = 0; i < mBones.size(); i++)
+ {
+ trackCount += mAnimations[a]->mBoneTracks[i].numFrames > 0 ? 1 : 0;
+ }
+
+ ms->mAnimations[a]->mTrackCount = (int32_t)trackCount;
+ ms->mAnimations[a]->mTracks = (mimp::MeshAnimTrack**)::malloc(sizeof(mimp::MeshAnimTrack*) * trackCount);
+ ms->mAnimations[a]->mDuration = 0.0f;
+ ms->mAnimations[a]->mDtime = 0.0f;
+
+ unsigned int curTrack = 0;
+ for (size_t t = 0; t < mBones.size(); t++)
+ {
+ if (mAnimations[a]->mBoneTracks[t].numFrames <= 0)
+ {
+ continue;
+ }
+
+ mimp::MeshAnimTrack* track = ms->mAnimations[a]->mTracks[curTrack++] = new mimp::MeshAnimTrack;
+
+ track->mName = ms->mSkeletons[0]->mBones[t].mName; // just use the same name as the bone array already does
+ const unsigned int firstFrame = (uint32_t)mAnimations[a]->mBoneTracks[t].firstFrame;
+ track->mFrameCount = mAnimations[a]->mBoneTracks[t].numFrames;
+ ms->mAnimations[a]->mFrameCount = std::max(ms->mAnimations[a]->mFrameCount, track->mFrameCount);
+
+ track->mPose = (mimp::MeshAnimPose*)::malloc(sizeof(mimp::MeshAnimPose) * track->mFrameCount);
+
+ track->mDuration = 0.0f;
+
+ for (int f = 0; f < track->mFrameCount; f++)
+ {
+ mimp::MeshAnimPose& pose = track->mPose[f];
+ BoneKeyFrame& frame = mKeyFrames[firstFrame + f];
+
+ physx::PxTransform mat;
+
+ mat.q = frame.relPose.q * mBones[t].pose.q;
+ mat.p = frame.relPose.p + mBones[t].pose.p;
+
+ (physx::PxVec3&)pose.mScale = frame.scale;
+ (physx::PxVec3&)pose.mPos = mat.p;
+
+ (physx::PxQuat&)pose.mQuat = mat.q;
+
+ track->mDuration = std::max(track->mDuration, frame.time);
+ }
+
+ track->mDtime = track->mDuration / (float)track->mFrameCount * 200.0f;
+
+ ms->mAnimations[a]->mDuration = std::max(ms->mAnimations[a]->mDuration, track->mDuration);
+ ms->mAnimations[a]->mDtime = std::max(ms->mAnimations[a]->mDtime, track->mDtime);
+ }
+ }
+
+ return true;
+#endif
+}
+
+// -------------------------------------------------------------------
+bool SkeletalAnim::initFrom(nvidia::apex::RenderMeshAssetAuthoring& mesh)
+{
+ PX_ASSERT(mesh.getPartCount() == 1);
+
+ uint32_t numBones = 0;
+ for (uint32_t submeshIndex = 0; submeshIndex < mesh.getSubmeshCount(); submeshIndex++)
+ {
+ const nvidia::VertexBuffer& vb = mesh.getSubmesh(submeshIndex).getVertexBuffer();
+ const nvidia::VertexFormat& vf = vb.getFormat();
+ uint32_t bufferIndex = (uint32_t)vf.getBufferIndexFromID(vf.getSemanticID(nvidia::apex::RenderVertexSemantic::BONE_INDEX));
+
+ nvidia::apex::RenderDataFormat::Enum format;
+ const uint16_t* boneIndices = (const uint16_t*)vb.getBufferAndFormat(format, bufferIndex);
+
+ unsigned int numBonesPerVertex = 0;
+ switch (format)
+ {
+ case nvidia::apex::RenderDataFormat::USHORT1:
+ numBonesPerVertex = 1;
+ break;
+ case nvidia::apex::RenderDataFormat::USHORT2:
+ numBonesPerVertex = 2;
+ break;
+ case nvidia::apex::RenderDataFormat::USHORT3:
+ numBonesPerVertex = 3;
+ break;
+ case nvidia::apex::RenderDataFormat::USHORT4:
+ numBonesPerVertex = 4;
+ break;
+ default:
+ break;
+ }
+
+ if (boneIndices == NULL || numBonesPerVertex == 0)
+ {
+ return false;
+ }
+
+ const unsigned int numElements = numBonesPerVertex * vb.getVertexCount();
+
+ for (unsigned int i = 0; i < numElements; i++)
+ {
+ numBones = std::max(numBones, boneIndices[i] + 1u);
+ }
+ }
+
+ SkeletalBone initBone;
+ initBone.clear();
+ mBones.resize(numBones, initBone);
+
+ for (unsigned int i = 0; i < numBones; i++)
+ {
+ mBones[i].id = (int32_t)i;
+ }
+
+ mSkinningMatrices.resize(numBones);
+ mSkinningMatricesWorld.resize(numBones);
+
+ init(true);
+
+ return numBones > 0;
+}
+
+// -------------------------------------------------------------------
+bool SkeletalAnim::loadFromXML(const std::string& xmlFile, std::string& error)
+{
+ clear();
+
+ physx::PsFileBuffer fb(xmlFile.c_str(), physx::PxFileBuf::OPEN_READ_ONLY);
+ physx::PxInputDataFromPxFileBuf id(fb);
+
+ if (!fb.isOpen())
+ {
+ return false;
+ }
+
+ physx::shdfnd::FastXml* fastXml = physx::shdfnd::createFastXml(this);
+ fastXml->processXml(id);
+
+ int errorLineNumber = -1;
+ const char* xmlError = fastXml->getError(errorLineNumber);
+ if (xmlError != NULL)
+ {
+ char temp[1024];
+ physx::shdfnd::snprintf(temp, 1024, "Xml parse error in %s on line %d:\n\n%s", xmlFile.c_str(), errorLineNumber, xmlError);
+ error = temp;
+ return false;
+ }
+
+ fastXml->release();
+
+ physx::PxTransform matId(physx::PxIdentity);
+ mSkinningMatrices.resize((uint32_t)mBones.size(), matId);
+ mSkinningMatricesWorld.resize((uint32_t)mBones.size(), matId);
+
+ init(true);
+ return true;
+}
+
+// -------------------------------------------------------------------
+bool SkeletalAnim::loadFromParent(const SkeletalAnim* parent)
+{
+ if (parent == NULL)
+ {
+ return false;
+ }
+
+ mParent = parent;
+
+ mBones.resize(mParent->mBones.size());
+ physx::PxTransform matId(physx::PxIdentity);
+ mSkinningMatrices.resize((uint32_t)mBones.size(), matId);
+ mSkinningMatricesWorld.resize((uint32_t)mBones.size(), matId);
+ for (uint32_t i = 0; i < mBones.size(); i++)
+ {
+ mBones[i] = mParent->mBones[i];
+ }
+
+ return true;
+}
+
+// -------------------------------------------------------------------
+bool SkeletalAnim::saveToXML(const std::string& xmlFile) const
+{
+#if PX_WINDOWS_FAMILY == 0
+ PX_UNUSED(xmlFile);
+ return false;
+#else
+ FILE* f = 0;
+ if (::fopen_s(&f, xmlFile.c_str(), "w") != 0)
+ {
+ return false;
+ }
+
+ fprintf(f, "<skeleton>\n\n");
+
+ fprintf(f, " <bones>\n");
+ for (uint32_t i = 0; i < mBones.size(); i++)
+ {
+ const SkeletalBone& bone = mBones[i];
+
+ float angle;
+ physx::PxVec3 axis;
+ bone.pose.q.toRadiansAndUnitAxis(angle, axis);
+ angle = bone.pose.q.getAngle();
+
+ fprintf(f, " <bone id = \"%i\" name = \"%s\">\n", bone.id, bone.name.c_str());
+ fprintf(f, " <position x=\"%f\" y=\"%f\" z=\"%f\" />\n", bone.pose.p.x, bone.pose.p.y, bone.pose.p.z);
+ fprintf(f, " <rotation angle=\"%f\">\n", angle);
+ fprintf(f, " <axis x=\"%f\" y=\"%f\" z=\"%f\" />\n", axis.x, axis.y, axis.z);
+ fprintf(f, " </rotation>\n");
+ fprintf(f, " <scale x=\"%f\" y=\"%f\" z=\"%f\" />\n", 1.0f, 1.0f, 1.0f);
+// dont' use bone.scale.x, bone.scale.y, bone.scale.z because the length is baked into the bones
+ fprintf(f, " </bone>\n");
+ }
+ fprintf(f, " </bones>\n\n");
+
+ fprintf(f, " <bonehierarchy>\n");
+ for (uint32_t i = 0; i < mBones.size(); i++)
+ {
+ const SkeletalBone& bone = mBones[i];
+ if (bone.parent < 0)
+ {
+ continue;
+ }
+ fprintf(f, " <boneparent bone=\"%s\" parent=\"%s\" />\n", bone.name.c_str(), mBones[(uint32_t)bone.parent].name.c_str());
+ }
+ fprintf(f, " </bonehierarchy>\n\n");
+
+ fprintf(f, " <animations>\n");
+ for (uint32_t i = 0; i < mAnimations.size(); i++)
+ {
+ const SkeletalAnimation* anim = mAnimations[i];
+
+ fprintf(f, " <animation name = \"%s\" length=\"%f\">\n", anim->name.c_str(), anim->maxTime);
+ fprintf(f, " <tracks>\n");
+
+ for (uint32_t j = 0; j < anim->mBoneTracks.size(); j++)
+ {
+ const BoneTrack& track = anim->mBoneTracks[j];
+ if (track.numFrames == 0)
+ {
+ continue;
+ }
+
+ fprintf(f, " <track bone = \"%s\">\n", mBones[j].name.c_str());
+ fprintf(f, " <keyframes>\n");
+
+ for (int k = track.firstFrame; k < track.firstFrame + track.numFrames; k++)
+ {
+ const BoneKeyFrame& frame = mKeyFrames[(uint32_t)k];
+ float angle;
+ physx::PxVec3 axis;
+ frame.relPose.q.toRadiansAndUnitAxis(angle, axis);
+ angle = frame.relPose.q.getAngle();
+
+ fprintf(f, " <keyframe time = \"%f\">\n", frame.time);
+ fprintf(f, " <translate x=\"%f\" y=\"%f\" z=\"%f\" />\n", frame.relPose.p.x, frame.relPose.p.y, frame.relPose.p.z);
+ fprintf(f, " <rotate angle=\"%f\">\n", angle);
+ fprintf(f, " <axis x=\"%f\" y=\"%f\" z=\"%f\" />\n", axis.x, axis.y, axis.z);
+ fprintf(f, " </rotate>\n");
+ fprintf(f, " <scale x=\"%f\" y=\"%f\" z=\"%f\" />\n", frame.scale.x, frame.scale.y, frame.scale.z);
+ fprintf(f, " </keyframe>\n");
+ }
+ fprintf(f, " </keyframes>\n");
+ fprintf(f, " </track>\n");
+ }
+ fprintf(f, " </tracks>\n");
+ fprintf(f, " </animation>\n");
+ }
+ fprintf(f, " </animations>\n");
+ fprintf(f, "</skeleton>\n\n");
+
+ fclose(f);
+
+
+ return true;
+#endif
+}
+
+// -------------------------------------------------------------------
+void SkeletalAnim::init(bool firstTime)
+{
+ if (firstTime)
+ {
+ setupConnectivity();
+
+ // init bind poses
+ physx::PxVec3 oneOneOne(1.0f, 1.0f, 1.0f);
+ for (uint32_t i = 0; i < mBones.size(); i++)
+ {
+ if (mBones[i].parent < 0)
+ {
+ initBindPoses((int32_t)i, oneOneOne);
+ }
+
+ // collapse finger and toes
+ if (
+ mBones[i].name.find("finger") != std::string::npos ||
+ mBones[i].name.find("Finger") != std::string::npos ||
+ mBones[i].name.find("FINGER") != std::string::npos ||
+ mBones[i].name.find("toe") != std::string::npos ||
+ mBones[i].name.find("Toe") != std::string::npos ||
+ mBones[i].name.find("TOE") != std::string::npos)
+ {
+ mBones[i].boneOption = 2; // this is collapse
+ }
+ }
+ }
+
+ PX_ASSERT(mBones.size() == mSkinningMatrices.size());
+ PX_ASSERT(mBones.size() == mSkinningMatricesWorld.size());
+ for (uint32_t i = 0; i < mBones.size(); i++)
+ {
+ SkeletalBone& b = mBones[i];
+ b.invBindWorldPose = b.bindWorldPose.inverseRT();
+ b.currentWorldPose = mBones[i].bindWorldPose;
+ mSkinningMatrices[i] = physx::PxMat44(physx::PxIdentity);
+ mSkinningMatricesWorld[i] = b.currentWorldPose;
+ }
+
+ // init time interval of animations
+ for (uint32_t i = 0; i < mAnimations.size(); i++)
+ {
+ SkeletalAnimation* a = mAnimations[i];
+ bool first = true;
+ for (uint32_t j = 0; j < a->mBoneTracks.size(); j++)
+ {
+ BoneTrack& b = a->mBoneTracks[j];
+ for (int k = b.firstFrame; k < b.firstFrame + b.numFrames; k++)
+ {
+ float time = mKeyFrames[(uint32_t)k].time;
+ if (first)
+ {
+ a->minTime = time;
+ a->maxTime = time;
+ first = false;
+ }
+ else
+ {
+ if (time < a->minTime)
+ {
+ a->minTime = time;
+ }
+ if (time > a->maxTime)
+ {
+ a->maxTime = time;
+ }
+ }
+ }
+ }
+ }
+}
+
+// -------------------------------------------------------------------
+void SkeletalAnim::initBindPoses(int boneNr, const physx::PxVec3& scale)
+{
+ SkeletalBone& b = mBones[(uint32_t)boneNr];
+ b.pose.p = b.pose.p.multiply(scale);
+
+ physx::PxVec3 newScale = scale.multiply(b.scale);
+
+ if (b.parent < 0)
+ {
+ b.bindWorldPose = b.pose;
+ }
+ else
+ {
+ b.bindWorldPose = mBones[(uint32_t)b.parent].bindWorldPose * b.pose;
+ }
+
+ for (int i = b.firstChild; i < b.firstChild + b.numChildren; i++)
+ {
+ initBindPoses(mChildren[(uint32_t)i], newScale);
+ }
+}
+
+// -------------------------------------------------------------------
+void SkeletalAnim::setupConnectivity()
+{
+ size_t i;
+ size_t numBones = mBones.size();
+ for (i = 0; i < numBones; i++)
+ {
+ SkeletalBone& b = mBones[i];
+ if (b.parent >= 0)
+ {
+ mBones[(uint32_t)b.parent].numChildren++;
+ }
+ }
+ int first = 0;
+ for (i = 0; i < numBones; i++)
+ {
+ mBones[i].firstChild = first;
+ first += mBones[i].numChildren;
+ }
+ mChildren.resize((uint32_t)first);
+ for (i = 0; i < numBones; i++)
+ {
+ if (mBones[i].parent < 0)
+ {
+ continue;
+ }
+ SkeletalBone& p = mBones[(uint32_t)mBones[i].parent];
+ mChildren[(uint32_t)p.firstChild++] = (int)i;
+ }
+ for (i = 0; i < numBones; i++)
+ {
+ mBones[i].firstChild -= mBones[i].numChildren;
+ }
+}
+
+// -------------------------------------------------------------------
+void SkeletalAnim::draw(nvidia::RenderDebugInterface* batcher)
+{
+ PX_ASSERT(batcher != NULL);
+ if (batcher == NULL)
+ {
+ return;
+ }
+
+ using RENDER_DEBUG::DebugColors;
+ const uint32_t colorWhite = RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::White);
+ const uint32_t colorBlack = RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::Black);
+ for (uint32_t i = 0; i < mBones.size(); i++)
+ {
+ SkeletalBone& bone = mBones[i];
+
+ uint32_t color = bone.selected ? colorWhite : colorBlack;
+ RENDER_DEBUG_IFACE(batcher)->setCurrentColor(color);
+
+ if (bone.parent >= 0 /*&& mBones[bone.parent].parent >= 0*/)
+ {
+ SkeletalBone& parent = mBones[(uint32_t)bone.parent];
+
+ RENDER_DEBUG_IFACE(batcher)->debugLine(bone.currentWorldPose.getPosition(), parent.currentWorldPose.getPosition());
+ }
+ }
+}
+
+// -------------------------------------------------------------------
+void SkeletalAnim::copyFrom(const SkeletalAnim& anim)
+{
+ clear();
+
+ mBones.resize(anim.mBones.size());
+ for (uint32_t i = 0; i < anim.mBones.size(); i++)
+ {
+ mBones[i] = anim.mBones[i];
+ }
+
+ mSkinningMatrices.resize(anim.mSkinningMatrices.size());
+ for (uint32_t i = 0; i < anim.mSkinningMatrices.size(); i++)
+ {
+ mSkinningMatrices[i] = anim.mSkinningMatrices[i];
+ }
+
+ mSkinningMatricesWorld.resize(anim.mSkinningMatricesWorld.size());
+ for (uint32_t i = 0; i < anim.mSkinningMatricesWorld.size(); i++)
+ {
+ mSkinningMatricesWorld[i] = anim.mSkinningMatricesWorld[i];
+ }
+
+ mChildren.resize(anim.mChildren.size());
+ for (uint32_t i = 0; i < anim.mChildren.size(); i++)
+ {
+ mChildren[i] = anim.mChildren[i];
+ }
+
+ for (uint32_t i = 0; i < anim.mAnimations.size(); i++)
+ {
+ SkeletalAnimation* a = anim.mAnimations[i];
+ SkeletalAnimation* na = new SkeletalAnimation();
+ na->minTime = a->minTime;
+ na->maxTime = a->maxTime;
+ na->name = a->name;
+ na->mBoneTracks.resize(a->mBoneTracks.size());
+ for (uint32_t j = 0; j < a->mBoneTracks.size(); j++)
+ {
+ na->mBoneTracks[j] = a->mBoneTracks[j];
+ }
+ mAnimations.push_back(na);
+ }
+
+ mKeyFrames.resize(anim.mKeyFrames.size());
+ for (uint32_t i = 0; i < anim.mKeyFrames.size(); i++)
+ {
+ mKeyFrames[i] = anim.mKeyFrames[i];
+ }
+}
+
+
+// -------------------------------------------------------------------
+void SkeletalAnim::clearShapeCount(int boneIndex)
+{
+ if (boneIndex < 0)
+ {
+ for (uint32_t i = 0; i < mBones.size(); i++)
+ {
+ mBones[i].numShapes = 0;
+ }
+ }
+ else
+ {
+ PX_ASSERT((uint32_t)boneIndex < mBones.size());
+ mBones[(uint32_t)boneIndex].numShapes = 0;
+ }
+}
+
+// -------------------------------------------------------------------
+void SkeletalAnim::incShapeCount(int boneIndex)
+{
+ if (boneIndex >= 0 && (uint32_t)boneIndex < mBones.size())
+ {
+ mBones[(uint32_t)boneIndex].numShapes++;
+ }
+}
+
+// -------------------------------------------------------------------
+void SkeletalAnim::decShapeCount(int boneIndex)
+{
+ if (boneIndex >= 0 && (uint32_t)boneIndex < mBones.size())
+ {
+ PX_ASSERT(mBones[(uint32_t)boneIndex].numShapes > 0);
+ mBones[(uint32_t)boneIndex].numShapes--;
+ }
+}
+// -------------------------------------------------------------------
+bool SkeletalAnim::processElement(const char* elementName, const char* /*elementData*/, const physx::shdfnd::FastXml::AttributePairs& attr, int /*lineno*/)
+
+{
+ static int activeBoneTrack = -1;
+ static BoneKeyFrame* activeKeyFrame;
+ static bool isAnimation = false;
+
+ if (::strcmp(elementName, "skeleton") == 0)
+ {
+ // ok, a start
+ }
+ else if (::strcmp(elementName, "bones") == 0)
+ {
+ // the list of bones
+ }
+ else if (::strcmp(elementName, "bone") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 2);
+ PX_ASSERT(::strcmp(attr.getKey(0), "id") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(1), "name") == 0);
+ SkeletalBone bone;
+ bone.clear();
+ bone.id = atoi(attr.getValue(0));
+ bone.name = attr.getValue(1);
+ mBones.push_back(bone);
+ }
+ else if (::strcmp(elementName, "position") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 3);
+ PX_ASSERT(::strcmp(attr.getKey(0), "x") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(1), "y") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(2), "z") == 0);
+ physx::PxVec3 pos;
+ pos.x = (float)atof(attr.getValue(0));
+ pos.y = (float)atof(attr.getValue(1));
+ pos.z = (float)atof(attr.getValue(2));
+ mBones.back().pose.p = pos;
+ }
+ else if (::strcmp(elementName, "rotation") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 1);
+ PX_ASSERT(::strcmp(attr.getKey(0), "angle") == 0);
+ mBones.back().pose.q = physx::PxQuat((float)atof(attr.getValue(0)));
+ isAnimation = false;
+ }
+ else if (::strcmp(elementName, "axis") == 0 && !isAnimation)
+ {
+ PX_ASSERT(attr.getNbAttr() == 3);
+ PX_ASSERT(::strcmp(attr.getKey(0), "x") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(1), "y") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(2), "z") == 0);
+ physx::PxVec3 axis;
+ axis.x = (float)atof(attr.getValue(0));
+ axis.y = (float)atof(attr.getValue(1));
+ axis.z = (float)atof(attr.getValue(2));
+ float angle = mBones.back().pose.q.getAngle();
+ mBones.back().pose.q = physx::PxQuat(angle, axis);
+ }
+ else if (::strcmp(elementName, "scale") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 3);
+ PX_ASSERT(::strcmp(attr.getKey(0), "x") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(1), "y") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(2), "z") == 0);
+ physx::PxVec3 scale;
+ scale.x = (float)atof(attr.getValue(0));
+ scale.y = (float)atof(attr.getValue(1));
+ scale.z = (float)atof(attr.getValue(2));
+ mBones.back().scale = scale;
+ }
+ else if (::strcmp(elementName, "bonehierarchy") == 0)
+ {
+ // ok
+ }
+ else if (::strcmp(elementName, "boneparent") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 2);
+ PX_ASSERT(::strcmp(attr.getKey(0), "bone") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(1), "parent") == 0);
+ int child = findBone(attr.getValue(0));
+ int parent = findBone(attr.getValue(1));
+ if (child >= 0 && child < (int)mBones.size() && parent >= 0 && parent < (int)mBones.size())
+ {
+ mBones[(uint32_t)child].parent = parent;
+ }
+ }
+ else if (::strcmp(elementName, "animations") == 0)
+ {
+ // ok
+ }
+ else if (::strcmp(elementName, "animation") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 2);
+ PX_ASSERT(::strcmp(attr.getKey(0), "name") == 0);
+
+ SkeletalAnimation* anim = new SkeletalAnimation;
+ anim->clear();
+ anim->name = attr.getValue(0);
+ anim->mBoneTracks.resize((uint32_t)mBones.size());
+
+ mAnimations.push_back(anim);
+ }
+ else if (::strcmp(elementName, "tracks") == 0)
+ {
+ // ok
+ }
+ else if (::strcmp(elementName, "track") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 1);
+ PX_ASSERT(::strcmp(attr.getKey(0), "bone") == 0);
+ activeBoneTrack = findBone(attr.getValue(0));
+ if (activeBoneTrack >= 0 && activeBoneTrack < (int)mBones.size())
+ {
+ mAnimations.back()->mBoneTracks[(uint32_t)activeBoneTrack].firstFrame = (int)(mKeyFrames.size());
+ mAnimations.back()->mBoneTracks[(uint32_t)activeBoneTrack].numFrames = 0;
+ }
+ }
+ else if (::strcmp(elementName, "keyframes") == 0)
+ {
+ // ok
+ }
+ else if (::strcmp(elementName, "keyframe") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 1);
+ PX_ASSERT(::strcmp(attr.getKey(0), "time") == 0);
+
+ mAnimations.back()->mBoneTracks[(uint32_t)activeBoneTrack].numFrames++;
+
+ mKeyFrames.push_back(BoneKeyFrame());
+ activeKeyFrame = &mKeyFrames.back();
+ activeKeyFrame->clear();
+ activeKeyFrame->time = (float)atof(attr.getValue(0));
+ }
+ else if (::strcmp(elementName, "translate") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 3);
+ PX_ASSERT(::strcmp(attr.getKey(0), "x") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(1), "y") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(2), "z") == 0);
+ activeKeyFrame->relPose.p.x = (float)atof(attr.getValue(0));
+ activeKeyFrame->relPose.p.y = (float)atof(attr.getValue(1));
+ activeKeyFrame->relPose.p.z = (float)atof(attr.getValue(2));
+ }
+ else if (::strcmp(elementName, "rotate") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 1);
+ PX_ASSERT(::strcmp(attr.getKey(0), "angle") == 0);
+ activeKeyFrame->relPose.q = physx::PxQuat((float)atof(attr.getValue(0)));
+ isAnimation = true;
+ }
+ else if (::strcmp(elementName, "axis") == 0 && isAnimation)
+ {
+ PX_ASSERT(attr.getNbAttr() == 3);
+ PX_ASSERT(::strcmp(attr.getKey(0), "x") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(1), "y") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(2), "z") == 0);
+ physx::PxVec3 axis;
+ axis.x = (float)atof(attr.getValue(0));
+ axis.y = (float)atof(attr.getValue(1));
+ axis.z = (float)atof(attr.getValue(2));
+ axis.normalize();
+ float angle = activeKeyFrame->relPose.q.getAngle();
+ physx::PxQuat quat(angle, axis);
+ activeKeyFrame->relPose.q = quat;
+ }
+ else
+ {
+ // always break here, at least in debug mode
+ PX_ALWAYS_ASSERT();
+ }
+
+ return true;
+}
+
+} // namespace Samples
diff --git a/APEX_1.4/shared/external/src/TextRenderResourceManager.cpp b/APEX_1.4/shared/external/src/TextRenderResourceManager.cpp
new file mode 100644
index 00000000..f8f48b6a
--- /dev/null
+++ b/APEX_1.4/shared/external/src/TextRenderResourceManager.cpp
@@ -0,0 +1,1132 @@
+/*
+ * 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 "TextRenderResourceManager.h"
+
+#include "UserRenderResource.h"
+#include "UserRenderResourceDesc.h"
+
+#include "RenderContext.h"
+
+
+TextRenderResourceManager::TextRenderResourceManager() :
+mVerbosity(0),
+ mOutputFile(NULL),
+ mIO(NULL),
+ mVertexBufferCount(0),
+ mIndexBufferCount(0),
+ mBoneBufferCount(0),
+ mInstanceBufferCount(0),
+ mSpriteBufferCount(0),
+ mRenderResourceCount(0),
+ mSurfaceBufferCount(0)
+{
+}
+
+
+TextRenderResourceManager::TextRenderResourceManager(int verbosity, const char* outputFilename) :
+ mVerbosity(verbosity),
+ mOutputFile(NULL),
+ mVertexBufferCount(0),
+ mIndexBufferCount(0),
+ mBoneBufferCount(0),
+ mInstanceBufferCount(0),
+ mSpriteBufferCount(0),
+ mRenderResourceCount(0),
+ mSurfaceBufferCount(0)
+{
+ if (outputFilename != NULL)
+ {
+ mOutputFile = fopen(outputFilename, "w");
+ }
+ else
+ {
+ mOutputFile = stdout;
+ }
+ PX_ASSERT(mOutputFile != NULL);
+
+ mIO = new Writer(mOutputFile);
+}
+
+
+TextRenderResourceManager::~TextRenderResourceManager()
+{
+ if (mOutputFile != NULL && mOutputFile != stdout)
+ {
+ fclose(mOutputFile);
+ }
+
+ if (mIO != NULL)
+ {
+ delete mIO;
+ mIO = NULL;
+ }
+}
+
+unsigned int TextRenderResourceManager::material2Id(void* material)
+{
+ std::map<void*, unsigned int>::iterator it = mMaterial2Id.find(material);
+ if (it != mMaterial2Id.end())
+ {
+ return it->second;
+ }
+
+ unsigned int result = (unsigned int)mMaterial2Id.size();
+ mMaterial2Id[material] = result;
+
+ return result;
+}
+
+bool TextRenderResourceManager::getSpriteLayoutData(uint32_t spriteCount, uint32_t spriteSemanticsBitmap, nvidia::apex::UserRenderSpriteBufferDesc* bufferDesc)
+{
+ PX_UNUSED(spriteCount);
+ PX_UNUSED(spriteSemanticsBitmap);
+ PX_UNUSED(bufferDesc);
+ return false;
+}
+
+bool TextRenderResourceManager::getInstanceLayoutData(uint32_t particleCount, uint32_t particleSemanticsBitmap, nvidia::apex::UserRenderInstanceBufferDesc* bufferDesc)
+{
+ PX_UNUSED(particleCount);
+ PX_UNUSED(particleSemanticsBitmap);
+ PX_UNUSED(bufferDesc);
+ return false;
+}
+
+
+
+Writer::Writer(FILE* outputFile) : mOutputFile(outputFile)
+{
+ mIsStdout = mOutputFile == stdout;
+}
+
+Writer::~Writer() {}
+
+void Writer::printAndScan(const char* format)
+{
+ fprintf(mOutputFile,"%s", format);
+}
+
+void Writer::printAndScan(const char* format, const char* arg)
+{
+ fprintf(mOutputFile, format, arg);
+}
+
+void Writer::printAndScan(const char* format, int arg)
+{
+ fprintf(mOutputFile, format, arg);
+}
+
+void Writer::printAndScan(float /*tol*/, nvidia::apex::RenderVertexSemantic::Enum /*s*/, const char* format, float arg)
+{
+ fprintf(mOutputFile, format, arg);
+}
+
+const char* Writer::semanticToString(nvidia::apex::RenderVertexSemantic::Enum semantic)
+{
+ const char* result = NULL;
+ switch (semantic)
+ {
+#define CASE(_SEMANTIC) case nvidia::apex::RenderVertexSemantic::_SEMANTIC: result = #_SEMANTIC; break
+ CASE(POSITION);
+ CASE(NORMAL);
+ CASE(TANGENT);
+ CASE(BINORMAL);
+ CASE(COLOR);
+ CASE(TEXCOORD0);
+ CASE(TEXCOORD1);
+ CASE(TEXCOORD2);
+ CASE(TEXCOORD3);
+ CASE(BONE_INDEX);
+ CASE(BONE_WEIGHT);
+#undef CASE
+
+ default:
+ PX_ALWAYS_ASSERT();
+ }
+
+ return result;
+}
+
+
+
+const char* Writer::semanticToString(nvidia::apex::RenderBoneSemantic::Enum semantic)
+{
+ const char* result = NULL;
+ switch (semantic)
+ {
+#define CASE(_SEMANTIC) case nvidia::apex::RenderBoneSemantic::_SEMANTIC: result = #_SEMANTIC; break
+ CASE(POSE);
+ CASE(PREVIOUS_POSE);
+#undef CASE
+
+ // if this assert is hit add/remove semantics above to match RenderBoneSemantic
+ PX_COMPILE_TIME_ASSERT(nvidia::apex::RenderBoneSemantic::NUM_SEMANTICS == 2);
+
+ default:
+ PX_ALWAYS_ASSERT();
+ }
+
+ return result;
+}
+
+
+
+void Writer::writeElem(const char* name, unsigned int val)
+{
+ const bool isStdout = mIsStdout;
+
+ if (isStdout)
+ {
+ printAndScan("\n ");
+ }
+ printAndScan("%s=", name);
+ printAndScan("%d", (int)val);
+ if (!isStdout)
+ {
+ printAndScan(" ");
+ }
+}
+
+
+
+void Writer::writeArray(nvidia::apex::RenderDataFormat::Enum format, unsigned int stride, unsigned int numElements, const void* data, float tolerance, nvidia::apex::RenderVertexSemantic::Enum s)
+{
+ if (mIsStdout)
+ {
+ unsigned int maxNumElements = 1000000;
+
+ switch (format)
+ {
+ case nvidia::apex::RenderDataFormat::USHORT1:
+ case nvidia::apex::RenderDataFormat::UINT1:
+ maxNumElements = 10;
+ break;
+ case nvidia::apex::RenderDataFormat::FLOAT2:
+ maxNumElements = 3;
+ break;
+ case nvidia::apex::RenderDataFormat::FLOAT3:
+ maxNumElements = 2;
+ break;
+ case nvidia::apex::RenderDataFormat::FLOAT3x4:
+ case nvidia::apex::RenderDataFormat::FLOAT4x4:
+ maxNumElements = 1;
+ break;
+ default:
+ PX_ALWAYS_ASSERT();
+ }
+
+ if (maxNumElements < numElements)
+ {
+ numElements = maxNumElements;
+ }
+ }
+
+ for (unsigned int i = 0; i < numElements; i++)
+ {
+ const char* dataSample = ((const char*)data) + stride * i;
+
+ switch (format)
+ {
+ case nvidia::apex::RenderDataFormat::USHORT1:
+ {
+ const short* sh = (const short*)dataSample;
+ printAndScan("%d ", sh[0]);
+ }
+ break;
+ case nvidia::apex::RenderDataFormat::USHORT2:
+ {
+ const short* sh = (const short*)dataSample;
+ printAndScan("(%d ", sh[0]);
+ printAndScan("%d )", sh[1]);
+ }
+ break;
+ case nvidia::apex::RenderDataFormat::USHORT3:
+ {
+ const short* sh = (const short*)dataSample;
+ printAndScan("(%d ", sh[0]);
+ printAndScan("%d ", sh[2]);
+ printAndScan("%d )", sh[1]);
+ }
+ break;
+ case nvidia::apex::RenderDataFormat::USHORT4:
+ {
+ const short* sh = (const short*)dataSample;
+ printAndScan("(%d ", sh[0]);
+ printAndScan("%d ", sh[1]);
+ printAndScan("%d ", sh[2]);
+ printAndScan("%d )", sh[3]);
+ }
+ break;
+ case nvidia::apex::RenderDataFormat::UINT1:
+ {
+ const unsigned int* ui = (const unsigned int*)dataSample;
+ printAndScan("%d ", (int)ui[0]);
+ }
+ break;
+ case nvidia::apex::RenderDataFormat::FLOAT2:
+ {
+ const float* fl = (const float*)dataSample;
+ printAndScan(tolerance, s, "(%f ", fl[0]);
+ printAndScan(tolerance, s, "%f ) ", fl[1]);
+ }
+ break;
+ case nvidia::apex::RenderDataFormat::FLOAT3:
+ {
+ const physx::PxVec3* vec3 = (const physx::PxVec3*)dataSample;
+ printAndScan(tolerance, s, "(%f ", vec3->x);
+ printAndScan(tolerance, s, "%f ", vec3->y);
+ printAndScan(tolerance, s, "%f ) ", vec3->z);
+ }
+ break;
+ case nvidia::apex::RenderDataFormat::FLOAT4:
+ {
+ const physx::PxVec4* vec4 = (const physx::PxVec4*)dataSample;
+ printAndScan(tolerance, s, "(%f ", vec4->x);
+ printAndScan(tolerance, s, "%f ", vec4->y);
+ printAndScan(tolerance, s, "%f ", vec4->z);
+ printAndScan(tolerance, s, "%f ) ", vec4->w);
+ }
+ break;
+ case nvidia::apex::RenderDataFormat::FLOAT3x4:
+ {
+ const physx::PxVec3* vec3 = (const physx::PxVec3*)dataSample;
+ printAndScan("(");
+ for (unsigned int j = 0; j < 4; j++)
+ {
+ printAndScan(tolerance, s, "%f ", vec3[j].x);
+ printAndScan(tolerance, s, "%f ", vec3[j].y);
+ printAndScan(tolerance, s, "%f ", vec3[j].z);
+ }
+ printAndScan(") ");
+ }
+ break;
+ case nvidia::apex::RenderDataFormat::FLOAT4x4:
+ {
+ const physx::PxVec4* vec4 = (const physx::PxVec4*)dataSample;
+ printAndScan("(");
+ for (unsigned int j = 0; j < 4; j++)
+ {
+ printAndScan(tolerance, s, "%f ", vec4[j].x);
+ printAndScan(tolerance, s, "%f ", vec4[j].y);
+ printAndScan(tolerance, s, "%f ", vec4[j].z);
+ printAndScan(tolerance, s, "%f ", vec4[j].w);
+ }
+ printAndScan(") ");
+ }
+ break;
+ case nvidia::apex::RenderDataFormat::R8G8B8A8:
+ case nvidia::apex::RenderDataFormat::B8G8R8A8:
+ {
+ const unsigned int* ui = (const unsigned int*)dataSample;
+ printAndScan("0x%x ", (int)ui[0]);
+ }
+ break;
+ default:
+ PX_ALWAYS_ASSERT();
+ }
+ }
+}
+
+
+#define WRITE_ITEM(_A) mIO->writeElem(#_A, _A)
+#define WRITE_DESC_ELEM(_A) mIO->writeElem(#_A, (uint32_t)desc._A)
+#define WRITE_VREQUEST(_A) if (desc.buffersRequest[nvidia::apex::RenderVertexSemantic::_A] != nvidia::apex::RenderDataFormat::UNSPECIFIED) mIO->writeElem(#_A, desc.buffersRequest[nvidia::apex::RenderVertexSemantic::_A])
+#define WRITE_BREQUEST(_A) if (desc.buffersRequest[nvidia::apex::RenderBoneSemantic::_A] != nvidia::apex::RenderDataFormat::UNSPECIFIED) mIO->writeElem(#_A, desc.buffersRequest[nvidia::apex::RenderBoneSemantic::_A])
+
+
+
+
+
+
+
+
+
+
+
+class TextUserRenderVertexBuffer : public nvidia::apex::UserRenderVertexBuffer
+{
+public:
+ TextUserRenderVertexBuffer(const nvidia::apex::UserRenderVertexBufferDesc& desc, TextRenderResourceManager* manager, Writer* readerWriter, int bufferId) : mManager(manager), mIO(readerWriter), mWriteCalls(0), mDescriptor(desc)
+ {
+ mBufferId = bufferId;
+
+ if (mManager->getVerbosity() >= 1)
+ {
+ mIO->printAndScan("VertexBuffer[%d]::create(", mBufferId);
+
+ WRITE_DESC_ELEM(maxVerts);
+ WRITE_DESC_ELEM(hint);
+ WRITE_VREQUEST(POSITION);
+ WRITE_VREQUEST(NORMAL);
+ WRITE_VREQUEST(TANGENT);
+ WRITE_VREQUEST(BINORMAL);
+ WRITE_VREQUEST(COLOR);
+ WRITE_VREQUEST(TEXCOORD0);
+ WRITE_VREQUEST(TEXCOORD1);
+ WRITE_VREQUEST(TEXCOORD2);
+ WRITE_VREQUEST(TEXCOORD3);
+ WRITE_VREQUEST(BONE_INDEX);
+ WRITE_VREQUEST(BONE_WEIGHT);
+ // PH: not done on purpose (yet)
+ //WRITE_DESC_ELEM(numCustomBuffers);
+ //void** customBuffersIdents;
+ //RenderDataFormat::Enum* customBuffersRequest;
+ WRITE_DESC_ELEM(moduleIdentifier);
+ WRITE_DESC_ELEM(uvOrigin);
+ WRITE_DESC_ELEM(canBeShared);
+
+ mIO->printAndScan(")\n");
+ }
+ }
+
+ ~TextUserRenderVertexBuffer()
+ {
+ if (mManager->getVerbosity() >= 1)
+ {
+ mIO->printAndScan("VertexBuffer[%d]::destroy\n", mBufferId);
+ }
+ }
+
+ virtual void writeBuffer(const nvidia::RenderVertexBufferData& data, uint32_t firstVertex, uint32_t numVertices)
+ {
+ if (mManager->getVerbosity() >= 2)
+ {
+ mIO->printAndScan("VertexBuffer[%d]", mBufferId);
+ mIO->printAndScan("::writeBuffer#%d", (int)mWriteCalls);
+ mIO->printAndScan("(firstVertex=%d ", (int)firstVertex);
+ mIO->printAndScan("numVertices=%d)\n", (int)numVertices);
+
+ if (mManager->getVerbosity() >= 3)
+ {
+ for (unsigned int i = 0; i < nvidia::apex::RenderVertexSemantic::NUM_SEMANTICS; i++)
+ {
+ nvidia::apex::RenderVertexSemantic::Enum s = nvidia::apex::RenderVertexSemantic::Enum(i);
+ const nvidia::apex::RenderSemanticData& semanticData = data.getSemanticData(s);
+
+ if (semanticData.data != NULL)
+ {
+ mIO->printAndScan(" %s: ", mIO->semanticToString(s));
+ mIO->writeArray(semanticData.format, semanticData.stride, numVertices, semanticData.data, mManager->getVBTolerance(s), s);
+ mIO->printAndScan("\n");
+ }
+ }
+ }
+ }
+
+ mWriteCalls++;
+ }
+
+
+ unsigned int getId()
+ {
+ return (uint32_t)mBufferId;
+ }
+
+
+protected:
+ TextRenderResourceManager* mManager;
+ Writer* mIO;
+ int mBufferId;
+ unsigned int mWriteCalls;
+ nvidia::apex::UserRenderVertexBufferDesc mDescriptor;
+};
+
+
+
+
+nvidia::apex::UserRenderVertexBuffer* TextRenderResourceManager::createVertexBuffer(const nvidia::apex::UserRenderVertexBufferDesc& desc)
+{
+ nvidia::apex::UserRenderVertexBuffer* vb = new TextUserRenderVertexBuffer(desc, this, mIO, mVertexBufferCount++);
+ return vb;
+};
+
+
+
+void TextRenderResourceManager::releaseVertexBuffer(nvidia::apex::UserRenderVertexBuffer& buffer)
+{
+ delete &buffer;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+class TextUserRenderIndexBuffer : public nvidia::apex::UserRenderIndexBuffer
+{
+public:
+ TextUserRenderIndexBuffer(const nvidia::apex::UserRenderIndexBufferDesc& desc, TextRenderResourceManager* manager, Writer* readerWriter, int bufferId) : mManager(manager), mIO(readerWriter), mWriteCalls(0), mDescriptor(desc)
+ {
+ mBufferId = bufferId;
+
+ if (mManager->getVerbosity() >= 1)
+ {
+ mIO->printAndScan("IndexBuffer[%d]::create(", mBufferId);
+
+ WRITE_DESC_ELEM(maxIndices);
+ WRITE_DESC_ELEM(hint);
+ WRITE_DESC_ELEM(format);
+ WRITE_DESC_ELEM(primitives);
+ WRITE_DESC_ELEM(registerInCUDA);
+
+ mIO->printAndScan(")\n");
+ }
+ }
+
+ ~TextUserRenderIndexBuffer()
+ {
+ if (mManager->getVerbosity() >= 1)
+ {
+ mIO->printAndScan("IndexBuffer[%d]::destroy\n", mBufferId);
+ }
+ }
+
+ virtual void writeBuffer(const void* srcData, uint32_t srcStride, uint32_t firstDestElement, uint32_t numElements)
+ {
+ if (mManager->getVerbosity() >= 2)
+ {
+ mIO->printAndScan("IndexBuffer[%d]", mBufferId);
+ mIO->printAndScan("::writeBuffer#%d", (int)mWriteCalls);
+ mIO->printAndScan("(srcStride=%d ", (int)srcStride);
+ mIO->printAndScan("firstDestElement=%d ", (int)firstDestElement);
+ mIO->printAndScan("numElements=%d)\n ", (int)numElements);
+
+ mIO->writeArray(mDescriptor.format, srcStride, numElements, srcData, 0.0f, nvidia::apex::RenderVertexSemantic::CUSTOM);
+
+ mIO->printAndScan("\n");
+ }
+
+ mWriteCalls++;
+ }
+
+
+ unsigned int getId()
+ {
+ return (uint32_t)mBufferId;
+ }
+
+protected:
+ TextRenderResourceManager* mManager;
+ Writer* mIO;
+ int mBufferId;
+ unsigned int mWriteCalls;
+ nvidia::apex::UserRenderIndexBufferDesc mDescriptor;
+};
+
+
+
+nvidia::apex::UserRenderIndexBuffer* TextRenderResourceManager::createIndexBuffer(const nvidia::apex::UserRenderIndexBufferDesc& desc)
+{
+ nvidia::apex::UserRenderIndexBuffer* ib = new TextUserRenderIndexBuffer(desc, this, mIO, mIndexBufferCount++);
+ return ib;
+}
+
+
+
+void TextRenderResourceManager::releaseIndexBuffer(nvidia::apex::UserRenderIndexBuffer& buffer)
+{
+ delete &buffer;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+class TextUserRenderBoneBuffer : public nvidia::apex::UserRenderBoneBuffer
+{
+public:
+ TextUserRenderBoneBuffer(const nvidia::apex::UserRenderBoneBufferDesc& desc, TextRenderResourceManager* manager, Writer* readerWriter, int bufferId) : mManager(manager), mIO(readerWriter), mWriteCalls(0), mDescriptor(desc)
+ {
+ mBufferId = bufferId;
+
+ if (mManager->getVerbosity() >= 1)
+ {
+ mIO->printAndScan("BoneBuffer[%d]::create(", mBufferId);
+ WRITE_DESC_ELEM(maxBones);
+ WRITE_DESC_ELEM(hint);
+ WRITE_BREQUEST(POSE);
+ mIO->printAndScan(")\n");
+ }
+ }
+
+ ~TextUserRenderBoneBuffer()
+ {
+ if (mManager->getVerbosity() >= 1)
+ {
+ mIO->printAndScan("BoneBuffer[%d]::destroy\n", mBufferId);
+ }
+ }
+
+ virtual void writeBuffer(const nvidia::RenderBoneBufferData& data, uint32_t firstBone, uint32_t numBones)
+ {
+ if (mManager->getVerbosity() >= 2)
+ {
+ mIO->printAndScan("BoneBuffer[%d]", mBufferId);
+ mIO->printAndScan("::writeBuffer#%d", (int)mWriteCalls);
+ mIO->printAndScan("(firstBone=%d ", (int)firstBone);
+ mIO->printAndScan("numBones=%d)\n", (int)numBones);
+
+ for (unsigned int i = 0; i < nvidia::apex::RenderBoneSemantic::NUM_SEMANTICS; i++)
+ {
+ nvidia::apex::RenderBoneSemantic::Enum s = nvidia::apex::RenderBoneSemantic::Enum(i);
+ const nvidia::apex::RenderSemanticData& semanticData = data.getSemanticData(s);
+
+ if (semanticData.data != NULL)
+ {
+ mIO->printAndScan(" %s: ", mIO->semanticToString(s));
+ mIO->writeArray(semanticData.format, semanticData.stride, numBones, semanticData.data, mManager->getBonePoseTolerance(), nvidia::apex::RenderVertexSemantic::CUSTOM);
+ mIO->printAndScan("\n");
+ }
+ }
+ }
+
+ mWriteCalls++;
+ }
+
+
+ unsigned int getId()
+ {
+ return (uint32_t)mBufferId;
+ }
+
+protected:
+ TextRenderResourceManager* mManager;
+ Writer* mIO;
+ int mBufferId;
+ unsigned int mWriteCalls;
+ nvidia::apex::UserRenderBoneBufferDesc mDescriptor;
+};
+
+
+
+nvidia::apex::UserRenderBoneBuffer* TextRenderResourceManager::createBoneBuffer(const nvidia::apex::UserRenderBoneBufferDesc& desc)
+{
+ nvidia::apex::UserRenderBoneBuffer* bb = new TextUserRenderBoneBuffer(desc, this, mIO, mBoneBufferCount++);
+ return bb;
+}
+
+
+
+void TextRenderResourceManager::releaseBoneBuffer(nvidia::apex::UserRenderBoneBuffer& buffer)
+{
+ delete &buffer;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+class TextUserRenderInstanceBuffer : public nvidia::apex::UserRenderInstanceBuffer
+{
+public:
+ TextUserRenderInstanceBuffer(const nvidia::apex::UserRenderInstanceBufferDesc& desc, TextRenderResourceManager* manager, Writer* readerWriter, int bufferId) : mManager(manager), mIO(readerWriter), mDescriptor(desc)
+ {
+ mBufferId = bufferId;
+
+ if (mManager->getVerbosity() >= 1)
+ {
+ mIO->printAndScan("InstanceBuffer[%d]::create\n", mBufferId);
+ }
+ }
+
+ ~TextUserRenderInstanceBuffer()
+ {
+ if (mManager->getVerbosity() >= 1)
+ {
+ mIO->printAndScan("InstanceBuffer[%d]::destroy\n", mBufferId);
+ }
+ }
+
+ virtual void writeBuffer(const void* /*data*/, uint32_t /*firstInstance*/, uint32_t /*numInstances*/)
+ {
+ if (mManager->getVerbosity() >= 2)
+ {
+ mIO->printAndScan("InstanceBuffer[%d]::writeBuffer\n", mBufferId);
+
+ }
+ }
+
+ unsigned int getId()
+ {
+ return (uint32_t)mBufferId;
+ }
+
+protected:
+ TextRenderResourceManager* mManager;
+ Writer* mIO;
+ FILE* mOutputFile;
+ int mBufferId;
+ nvidia::apex::UserRenderInstanceBufferDesc mDescriptor;
+};
+
+
+
+nvidia::apex::UserRenderInstanceBuffer* TextRenderResourceManager::createInstanceBuffer(const nvidia::apex::UserRenderInstanceBufferDesc& desc)
+{
+ nvidia::apex::UserRenderInstanceBuffer* ib = new TextUserRenderInstanceBuffer(desc, this, mIO, mInstanceBufferCount++);
+ return ib;
+}
+
+
+
+void TextRenderResourceManager::releaseInstanceBuffer(nvidia::apex::UserRenderInstanceBuffer& buffer)
+{
+ delete &buffer;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+class TextUserRenderSpriteBuffer : public nvidia::apex::UserRenderSpriteBuffer
+{
+public:
+ TextUserRenderSpriteBuffer(const nvidia::apex::UserRenderSpriteBufferDesc& desc, TextRenderResourceManager* manager, Writer* readerWriter, int bufferId) : mManager(manager), mIO(readerWriter), mDescriptor(desc)
+ {
+ mBufferId = bufferId;
+
+ if (mManager->getVerbosity() >= 1)
+ {
+ mIO->printAndScan("SpriteBuffer[%d]::create\n", mBufferId);
+ }
+ }
+
+ ~TextUserRenderSpriteBuffer()
+ {
+ if (mManager->getVerbosity() >= 1)
+ {
+ mIO->printAndScan("SpriteBuffer[%d]::destroy\n", mBufferId);
+ }
+ }
+
+ virtual void writeBuffer(const void* /*data*/, uint32_t /*firstSprite*/, uint32_t /*numSprites*/)
+ {
+ if (mManager->getVerbosity() >= 2)
+ {
+ mIO->printAndScan("SpriteBuffer[%d]::writeBuffer\n", mBufferId);
+ }
+ }
+
+
+ unsigned int getId()
+ {
+ return (uint32_t)mBufferId;
+ }
+
+protected:
+ TextRenderResourceManager* mManager;
+ Writer* mIO;
+ FILE* mOutputFile;
+ int mBufferId;
+ nvidia::apex::UserRenderSpriteBufferDesc mDescriptor;
+};
+
+
+
+nvidia::apex::UserRenderSpriteBuffer* TextRenderResourceManager::createSpriteBuffer(const nvidia::apex::UserRenderSpriteBufferDesc& desc)
+{
+ nvidia::apex::UserRenderSpriteBuffer* sb = new TextUserRenderSpriteBuffer(desc, this, mIO, mSpriteBufferCount++);
+ return sb;
+}
+
+
+
+void TextRenderResourceManager::releaseSpriteBuffer(nvidia::apex::UserRenderSpriteBuffer& buffer)
+{
+ delete &buffer;
+}
+
+
+
+
+
+class TextUserRenderSurfaceBuffer : public nvidia::apex::UserRenderSurfaceBuffer
+{
+public:
+ TextUserRenderSurfaceBuffer(const nvidia::apex::UserRenderSurfaceBufferDesc& desc, TextRenderResourceManager* manager, Writer* readerWriter, int bufferId) : mManager(manager), mIO(readerWriter), mDescriptor(desc)
+ {
+ mBufferId = bufferId;
+
+ if (mManager->getVerbosity() >= 1)
+ {
+ mIO->printAndScan("SurfaceBuffer[%d]::create\n", mBufferId);
+ }
+ }
+
+ ~TextUserRenderSurfaceBuffer()
+ {
+ if (mManager->getVerbosity() >= 1)
+ {
+ mIO->printAndScan("SurfaceBuffer[%d]::destroy\n", mBufferId);
+ }
+ }
+
+ virtual void writeBuffer(const void* /*srcData*/, uint32_t /*srcPitch*/, uint32_t /*srcHeight*/, uint32_t /*dstX*/, uint32_t /*dstY*/, uint32_t /*dstZ*/, uint32_t /*width*/, uint32_t /*height*/, uint32_t /*depth*/)
+ {
+ if (mManager->getVerbosity() >= 2)
+ {
+ mIO->printAndScan("SurfaceBuffer[%d]::writeBuffer\n", mBufferId);
+ }
+ }
+
+
+ unsigned int getId()
+ {
+ return (uint32_t)mBufferId;
+ }
+
+protected:
+ TextRenderResourceManager* mManager;
+ Writer* mIO;
+ FILE* mOutputFile;
+ int mBufferId;
+ nvidia::apex::UserRenderSurfaceBufferDesc mDescriptor;
+};
+
+
+nvidia::apex::UserRenderSurfaceBuffer* TextRenderResourceManager::createSurfaceBuffer( const nvidia::apex::UserRenderSurfaceBufferDesc &desc )
+{
+ nvidia::apex::UserRenderSurfaceBuffer* sb = new TextUserRenderSurfaceBuffer(desc, this, mIO, mSurfaceBufferCount++);
+ return sb;
+}
+
+
+
+void TextRenderResourceManager::releaseSurfaceBuffer( nvidia::apex::UserRenderSurfaceBuffer &buffer )
+{
+ delete &buffer;
+}
+
+
+
+
+
+
+
+
+
+
+
+class TextUserRenderResource : public nvidia::apex::UserRenderResource
+{
+public:
+ TextUserRenderResource(const nvidia::apex::UserRenderResourceDesc& desc, TextRenderResourceManager* manager, Writer* readerWriter, int bufferId) : mManager(manager), mIO(readerWriter), mRenderCount(0), mDescriptor(desc)
+ {
+ mBufferId = bufferId;
+
+ if (mManager->getVerbosity() >= 1)
+ {
+ mIO->printAndScan("RenderResource[%d]::create(", mBufferId);
+
+ for (unsigned int i = 0; i < mDescriptor.numVertexBuffers; i++)
+ {
+ TextUserRenderVertexBuffer* vb = static_cast<TextUserRenderVertexBuffer*>(mDescriptor.vertexBuffers[i]);
+ mIO->writeElem("VertexBuffer", vb->getId());
+ }
+
+ if (mDescriptor.indexBuffer != NULL)
+ {
+ TextUserRenderIndexBuffer* ib = static_cast<TextUserRenderIndexBuffer*>(mDescriptor.indexBuffer);
+ mIO->writeElem("IndexBuffer", ib->getId());
+ }
+
+ if (mDescriptor.boneBuffer != NULL)
+ {
+ TextUserRenderBoneBuffer* bb = static_cast<TextUserRenderBoneBuffer*>(mDescriptor.boneBuffer);
+ mIO->writeElem("BoneBuffer", bb->getId());
+ }
+
+ if (mDescriptor.instanceBuffer != NULL)
+ {
+ TextUserRenderInstanceBuffer* ib = static_cast<TextUserRenderInstanceBuffer*>(mDescriptor.instanceBuffer);
+ mIO->writeElem("InstanceBuffer", ib->getId());
+ }
+
+ if (mDescriptor.spriteBuffer != NULL)
+ {
+ TextUserRenderSpriteBuffer* sp = static_cast<TextUserRenderSpriteBuffer*>(mDescriptor.spriteBuffer);
+ mIO->writeElem("SpriteBuffer", sp->getId());
+ }
+
+ {
+ unsigned int materialId = manager->material2Id(mDescriptor.material);
+ mIO->writeElem("Material", materialId);
+ }
+
+ WRITE_DESC_ELEM(numVertexBuffers);
+ WRITE_DESC_ELEM(firstVertex);
+ WRITE_DESC_ELEM(numVerts);
+ WRITE_DESC_ELEM(firstIndex);
+ WRITE_DESC_ELEM(numIndices);
+ WRITE_DESC_ELEM(firstBone);
+ WRITE_DESC_ELEM(numBones);
+ WRITE_DESC_ELEM(firstInstance);
+ WRITE_DESC_ELEM(numInstances);
+ WRITE_DESC_ELEM(firstSprite);
+ WRITE_DESC_ELEM(numSprites);
+ WRITE_DESC_ELEM(submeshIndex);
+ WRITE_DESC_ELEM(cullMode);
+ WRITE_DESC_ELEM(primitives);
+
+ mIO->printAndScan(")\n");
+ }
+ }
+
+ ~TextUserRenderResource()
+ {
+ if (mManager->getVerbosity() >= 1)
+ {
+ mIO->printAndScan("RenderResource[%d]::destroy\n", mBufferId);
+ }
+ }
+
+
+
+ void setVertexBufferRange(unsigned int firstVertex, unsigned int numVerts)
+ {
+ mDescriptor.firstVertex = firstVertex;
+ mDescriptor.numVerts = numVerts;
+
+ if (mManager->getVerbosity() >= 2)
+ {
+ mIO->printAndScan("RenderResource[%d]", mBufferId);
+ mIO->printAndScan("::setVertexBufferRange(firstVertex=%d", (int)firstVertex);
+ mIO->printAndScan(" numVerts=%d)\n", (int)numVerts);
+ }
+ }
+
+
+
+ void setIndexBufferRange(unsigned int firstIndex, unsigned int numIndices)
+ {
+ mDescriptor.firstIndex = firstIndex;
+ mDescriptor.numIndices = numIndices;
+
+ if (mManager->getVerbosity() >= 2)
+ {
+ mIO->printAndScan("RenderResource[%d]", mBufferId);
+ mIO->printAndScan("::setIndexBufferRange(firstIndex=%d ", (int)firstIndex);
+ mIO->printAndScan("numIndices=%d)\n", (int)numIndices);
+ }
+ }
+
+
+
+ void setBoneBufferRange(unsigned int firstBone, unsigned int numBones)
+ {
+ mDescriptor.firstBone = firstBone;
+ mDescriptor.numBones = numBones;
+
+ if (mManager->getVerbosity() >= 2)
+ {
+ mIO->printAndScan("RenderResource[%d]", mBufferId);
+ mIO->printAndScan("::setBoneBufferRange(firstBone=%d ", (int)firstBone);
+ mIO->printAndScan("numBones=%d)\n", (int)numBones);
+ }
+ }
+
+
+
+ void setInstanceBufferRange(unsigned int firstInstance, unsigned int numInstances)
+ {
+ mDescriptor.firstInstance = firstInstance;
+ mDescriptor.numInstances = numInstances;
+
+ if (mManager->getVerbosity() >= 2)
+ {
+ mIO->printAndScan("RenderResource[%d]", mBufferId);
+ mIO->printAndScan("::setInstanceBufferRange(firstInstance=%d ", (int)firstInstance);
+ mIO->printAndScan("numInstances=%d)\n", (int)numInstances);
+ }
+ }
+
+
+
+ void setSpriteBufferRange(unsigned int firstSprite, unsigned int numSprites)
+ {
+ mDescriptor.firstSprite = firstSprite;
+ mDescriptor.numSprites = numSprites;
+
+ if (mManager->getVerbosity() >= 2)
+ {
+ mIO->printAndScan("RenderResource[%d]", mBufferId);
+ mIO->printAndScan("(firstSprite=%d ", (int)firstSprite);
+ mIO->printAndScan("numSprites=%d)\n", (int)numSprites);
+ }
+ }
+
+
+
+ void setMaterial(void* material)
+ {
+ mDescriptor.material = material;
+
+ if (mManager->getVerbosity() >= 2 && material != mDescriptor.material)
+ {
+ mIO->printAndScan("RenderResource[%d]", mBufferId);
+ mIO->printAndScan("::setMaterial(material=%d)\n", (int)mManager->material2Id(material));
+ }
+ }
+
+
+
+ unsigned int getNbVertexBuffers() const
+ {
+ return mDescriptor.numVertexBuffers;
+ }
+
+
+
+ nvidia::apex::UserRenderVertexBuffer* getVertexBuffer(unsigned int index) const
+ {
+ return mDescriptor.vertexBuffers[index];
+ }
+
+
+
+ nvidia::apex::UserRenderIndexBuffer* getIndexBuffer() const
+ {
+ return mDescriptor.indexBuffer;
+ }
+
+
+
+
+ nvidia::apex::UserRenderBoneBuffer* getBoneBuffer() const
+ {
+ return mDescriptor.boneBuffer;
+ }
+
+
+
+ nvidia::apex::UserRenderInstanceBuffer* getInstanceBuffer() const
+ {
+ return mDescriptor.instanceBuffer;
+ }
+
+
+
+ nvidia::apex::UserRenderSpriteBuffer* getSpriteBuffer() const
+ {
+ return mDescriptor.spriteBuffer;
+ }
+
+
+
+ void render(const nvidia::apex::RenderContext& context)
+ {
+ if (mManager->getVerbosity() >= 2)
+ {
+ mIO->printAndScan("RenderResource[%d]", mBufferId);
+ mIO->printAndScan("::render#%d(", (int)mRenderCount);
+
+ for (int i = 0; i < 4; i++)
+ {
+ for (int j = 0; j < 4; j++)
+ {
+ mIO->printAndScan(mManager->getRenderPoseTolerance(), nvidia::apex::RenderVertexSemantic::CUSTOM, "%f ", context.local2world[i][j]);
+ }
+ }
+
+ mIO->printAndScan(")\n");
+ }
+
+ mRenderCount++;
+ }
+
+protected:
+ TextRenderResourceManager* mManager;
+ Writer* mIO;
+ int mBufferId;
+ unsigned int mRenderCount;
+ nvidia::apex::UserRenderResourceDesc mDescriptor;
+};
+
+
+
+nvidia::apex::UserRenderResource* TextRenderResourceManager::createResource(const nvidia::apex::UserRenderResourceDesc& desc)
+{
+ nvidia::apex::UserRenderResource* rr = new TextUserRenderResource(desc, this, mIO, mRenderResourceCount++);
+ return rr;
+}
+
+
+
+void TextRenderResourceManager::releaseResource(nvidia::apex::UserRenderResource& resource)
+{
+ delete &resource;
+}
+
+
+
+unsigned int TextRenderResourceManager::getMaxBonesForMaterial(void* /*material*/)
+{
+ return 0;
+}
+
+
+
+void TextUserRenderer::renderResource(const nvidia::apex::RenderContext& context)
+{
+ TextUserRenderResource* rr = static_cast<TextUserRenderResource*>(context.renderResource);
+ rr->render(context);
+}
diff --git a/APEX_1.4/shared/external/src/TriangleMesh.cpp b/APEX_1.4/shared/external/src/TriangleMesh.cpp
new file mode 100644
index 00000000..de45c439
--- /dev/null
+++ b/APEX_1.4/shared/external/src/TriangleMesh.cpp
@@ -0,0 +1,5048 @@
+/*
+ * 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 <MeshImport.h>
+
+#include "PsMathUtils.h"
+#include "TriangleMesh.h"
+#include "SkeletalAnim.h"
+#include "RenderMeshAsset.h"
+#include "RenderDataFormat.h"
+#include "PsFileBuffer.h"
+
+#include "PxInputDataFromPxFileBuf.h"
+
+#include "PxStrideIterator.h"
+
+#include "ApexUsingNamespace.h"
+#include "PsString.h"
+#include "PxIntrinsics.h"
+
+#include <algorithm>
+#include <vector>
+
+#include <RenderDebugInterface.h>
+#include <ResourceCallback.h>
+#include <ApexNameSpace.h>
+#include <clothing/ClothingPhysicalMesh.h>
+#include <UserRenderer.h>
+#include <UserRenderBoneBuffer.h>
+#include <UserRenderBoneBufferDesc.h>
+#include <UserRenderVertexBuffer.h>
+#include <UserRenderVertexBufferDesc.h>
+#include <UserRenderIndexBuffer.h>
+#include <UserRenderIndexBufferDesc.h>
+#include <UserRenderResourceDesc.h>
+#include <RenderContext.h>
+
+#ifdef USE_SAMPLE_RENDERER
+#include <Renderer.h>
+#include <RendererIndexBufferDesc.h>
+#include <RendererMaterialDesc.h>
+#include <RendererMaterialInstance.h>
+#include <RendererMeshDesc.h>
+#include <RendererMeshContext.h>
+#include <RendererVertexBufferDesc.h>
+
+#include <SampleMaterialAsset.h>
+#include <SampleAssetManager.h>
+#endif
+
+const uint32_t OBJ_STR_LEN = 256;
+
+#if PX_WINDOWS_FAMILY
+#define NOMINMAX
+#include <windows.h>
+#endif
+
+#if PX_APPLE_FAMILY
+#include <stdio.h>
+#endif
+
+mimp::MeshImport* gMeshImport = NULL; // has to be declared somewhere in the code
+
+
+namespace Samples
+{
+
+//-----------------------------------------------------------------------------
+struct TriangleMeshEdge
+{
+ void init(int v0, int v1, int edgeNr, int triNr)
+ {
+ if (v0 < v1)
+ {
+ this->v0 = v0;
+ this->v1 = v1;
+ }
+ else
+ {
+ this->v0 = v1;
+ this->v1 = v0;
+ }
+ this->edgeNr = edgeNr;
+ this->triNr = triNr;
+ }
+ bool operator < (const TriangleMeshEdge& e) const
+ {
+ if (v0 < e.v0)
+ {
+ return true;
+ }
+ if (v0 > e.v0)
+ {
+ return false;
+ }
+ return v1 < e.v1;
+ }
+ bool operator == (const TriangleMeshEdge& e) const
+ {
+ if (v0 == e.v0 && v1 == e.v1)
+ {
+ return true;
+ }
+ if (v0 == e.v1 && v1 == e.v0)
+ {
+ return true;
+ }
+ return false;
+ }
+ int v0, v1;
+ int edgeNr;
+ int triNr;
+};
+
+// ------------------------------------------------------------------------------------
+struct TexCoord
+{
+ TexCoord() {}
+ TexCoord(float u, float v)
+ {
+ this->u = u;
+ this->v = v;
+ }
+ void zero()
+ {
+ u = 0.0f;
+ v = 0.0f;
+ }
+ TexCoord operator + (const TexCoord& tc) const
+ {
+ TexCoord r;
+ r.u = u + tc.u;
+ r.v = v + tc.v;
+ return tc;
+ }
+ void operator += (const TexCoord& tc)
+ {
+ u += tc.u;
+ v += tc.v;
+ }
+ void operator *= (float r)
+ {
+ u *= r;
+ v *= r;
+ }
+ void operator /= (float r)
+ {
+ u /= r;
+ v /= r;
+ }
+ float u, v;
+};
+
+// -------------------------------------------------------------------
+struct SimpleVertexRef
+{
+ int vert, normal, texCoord;
+ int indexNr;
+ float handedNess;
+
+ bool operator < (const SimpleVertexRef& r) const
+ {
+ if (vert < r.vert)
+ {
+ return true;
+ }
+ if (vert > r.vert)
+ {
+ return false;
+ }
+ if (normal < r.normal)
+ {
+ return true;
+ }
+ if (normal > r.normal)
+ {
+ return false;
+ }
+ if (texCoord < r.texCoord)
+ {
+ return true;
+ }
+ else if (texCoord > r.texCoord)
+ {
+ return false;
+ }
+ return handedNess > r.handedNess;
+ }
+ bool operator == (const SimpleVertexRef& r) const
+ {
+ return vert == r.vert && normal == r.normal && texCoord == r.texCoord && handedNess == r.handedNess;
+ }
+ void parse(char* s, int indexNr)
+ {
+ int nr[3] = {0, 0, 0};
+ char* p = s;
+ int i = 0;
+ while (*p != 0 && i < 3 && sscanf(p, "%d", &nr[i]) == 1)
+ {
+ while (*p != '/' && *p != 0)
+ {
+ p++;
+ }
+
+ if (*p == 0)
+ {
+ break;
+ }
+
+ p++;
+ i++;
+ if (*p == '/')
+ {
+ p++;
+ i++;
+ }
+ }
+ PX_ASSERT(nr[0] > 0);
+ vert = nr[0] - 1;
+ texCoord = nr[1] - 1;
+ normal = nr[2] - 1;
+ this->indexNr = indexNr;
+ }
+};
+
+// ----------------------------------------------------------------------
+void TriangleSubMesh::setMaterialReference(SampleRenderer::RendererMaterial* material, SampleRenderer::RendererMaterialInstance* materialInstance)
+{
+ PX_UNUSED(material);
+ PX_UNUSED(materialInstance);
+
+#ifdef USE_SAMPLE_RENDERER
+ if (mRendererMaterialReference != material && mRendererMaterialInstance != NULL)
+ {
+ delete mRendererMaterialInstance;
+ mRendererMaterialInstance = NULL;
+ }
+ mRendererMaterialReference = material;
+
+ if (mRendererMaterialInstance == NULL && materialInstance != NULL && mRendererMaterialReference != NULL)
+ {
+ mRendererMaterialInstance = new SampleRenderer::RendererMaterialInstance(*mRendererMaterialReference);
+ }
+
+ if (mRendererMaterialInstance != NULL && materialInstance != NULL)
+ {
+ // copy the values of all variables
+ *mRendererMaterialInstance = *materialInstance;
+ }
+#endif // USE_SAMPLE_RENDERER
+}
+
+// ----------------------------------------------------------------------
+TriangleMesh::TriangleMesh(uint32_t /*moduleIdentifier*/, SampleRenderer::Renderer* renderer /*= NULL*/) :
+ mDynamicVertexBuffer(NULL),
+ mStaticVertexBuffer(NULL),
+ mIndexBuffer(NULL),
+ mBoneBuffer(NULL),
+ mTextureUVOrigin(nvidia::TextureUVOrigin::ORIGIN_TOP_LEFT),
+ mRenderer(renderer),
+ mRendererVertexBufferDynamic(NULL),
+ mRendererVertexBufferShared(NULL),
+ mRendererIndexBuffer(NULL),
+ mOverrideMaterial(NULL),
+ mRendererTransform(physx::PxIdentity),
+ mUseGpuSkinning(false)
+{
+ clear(NULL, NULL);
+}
+
+// ----------------------------------------------------------------------
+TriangleMesh::~TriangleMesh()
+{
+ clear(NULL, NULL);
+}
+
+
+// ----------------------------------------------------------------------
+void TriangleMesh::setRenderer(SampleRenderer::Renderer* renderer)
+{
+ PX_ASSERT(mRenderer == NULL || mRenderer == renderer);
+ mRenderer = renderer;
+}
+
+
+// ----------------------------------------------------------------------
+void TriangleMesh::clear(nvidia::apex::UserRenderResourceManager* rrm, nvidia::apex::ResourceCallback* rcb)
+{
+ mMaxBoneIndexInternal = -1;
+ mMaxBoneIndexExternal = -1;
+
+ mName = "";
+ mSkeletonFile = "";
+
+ mBounds.setEmpty();
+
+ oneCullModeChanged = false;
+ textureUvOriginChanged = false;
+
+ mVertices.clear();
+ mNormals.clear();
+ mTangents.clear();
+ mBitangents.clear();
+ mSkinnedVertices.clear();
+ mSkinnedNormals.clear();
+
+ mSkinningMatrices.clear();
+ skinningMatricesChanged = true;
+ vertexValuesChangedDynamic = false;
+ vertexValuesChangedStatic = false;
+ vertexCountChanged = false;
+ indicesChanged = false;
+
+ for (int i = 0; i < PC_NUM_CHANNELS; i++)
+ {
+ mPaintChannels[i].clear();
+ }
+
+ for (int i = 0; i < NUM_TEXCOORDS; i++)
+ {
+ mTexCoords[i].clear();
+ }
+
+ mIndices.clear();
+
+ mBoneIndicesExternal.clear();
+ mBoneWeights.clear();
+
+ mNextMark = -1;
+ mTriangleMarks.clear();
+ mVertexMarks.clear();
+ mActiveSubmeshVertices.clear();
+
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ if (mSubMeshes[i].mRenderResource != NULL)
+ {
+ PX_ASSERT(rrm != NULL);
+ rrm->releaseResource(*mSubMeshes[i].mRenderResource);
+ mSubMeshes[i].mRenderResource = NULL;
+ }
+
+ if (mParent != NULL)
+ {
+ mSubMeshes[i].materialResource = NULL;
+ }
+
+ if (mSubMeshes[i].materialResource != NULL)
+ {
+#ifndef USE_SAMPLE_RENDERER
+ PX_ASSERT(rcb != NULL);
+ std::string fullMaterialName = mMaterialPrefix + mSubMeshes[i].materialName + mMaterialSuffix;
+ rcb->releaseResource(APEX_MATERIALS_NAME_SPACE, fullMaterialName.c_str(), mSubMeshes[i].materialResource);
+#endif
+ mSubMeshes[i].materialResource = NULL;
+ }
+
+#ifdef USE_SAMPLE_RENDERER
+ if (mSubMeshes[i].mRendererMeshContext != NULL)
+ {
+ delete mSubMeshes[i].mRendererMeshContext;
+ mSubMeshes[i].mRendererMeshContext = NULL;
+ }
+
+ if (mSubMeshes[i].mRendererMaterialInstance != NULL)
+ {
+ delete mSubMeshes[i].mRendererMaterialInstance;
+ mSubMeshes[i].mRendererMaterialInstance = NULL;
+ }
+
+ if (mSubMeshes[i].mSampleMaterial != NULL && mParent == NULL)
+ {
+ PX_ASSERT(rcb != NULL);
+ rcb->releaseResource(APEX_MATERIALS_NAME_SPACE, NULL, mSubMeshes[i].mSampleMaterial);
+
+ mSubMeshes[i].mRendererMaterialReference = NULL;
+ }
+
+ if (mSubMeshes[i].mRendererMesh != NULL && mParent == NULL)
+ {
+ mSubMeshes[i].mRendererMesh->release();
+ mSubMeshes[i].mRendererMesh = NULL;
+ }
+#endif // USE_SAMPLE_RENDERER
+ }
+ mSubMeshes.clear();
+
+ if (mDynamicVertexBuffer != NULL)
+ {
+ PX_ASSERT(rrm != NULL);
+ rrm->releaseVertexBuffer(*mDynamicVertexBuffer);
+ mDynamicVertexBuffer = NULL;
+ }
+
+ if (mStaticVertexBuffer != NULL)
+ {
+ PX_ASSERT(rrm != NULL);
+ rrm->releaseVertexBuffer(*mStaticVertexBuffer);
+ mStaticVertexBuffer = NULL;
+ }
+
+ if (mIndexBuffer != NULL)
+ {
+ PX_ASSERT(rrm != NULL);
+ rrm->releaseIndexBuffer(*mIndexBuffer);
+ mIndexBuffer = NULL;
+ }
+
+ if (mBoneBuffer != NULL)
+ {
+ PX_ASSERT(rrm != NULL);
+ rrm->releaseBoneBuffer(*mBoneBuffer);
+ mBoneBuffer = NULL;
+ }
+
+
+#ifdef USE_SAMPLE_RENDERER
+ if (mRendererIndexBuffer != NULL)
+ {
+ mRendererIndexBuffer->release();
+ mRendererIndexBuffer = NULL;
+ }
+
+ if (mRendererVertexBufferDynamic != NULL)
+ {
+ mRendererVertexBufferDynamic->release();
+ mRendererVertexBufferDynamic = NULL;
+ }
+
+ if (mRendererVertexBufferShared != NULL)
+ {
+ mRendererVertexBufferShared->release();
+ mRendererVertexBufferShared = NULL;
+ }
+
+ if (mOverrideMaterial != NULL && rcb != NULL)
+ {
+ rcb->releaseResource(APEX_MATERIALS_NAME_SPACE, NULL, mOverrideMaterial);
+ mOverrideMaterial = NULL;
+ }
+#endif // USE_SAMPLE_RENDERER
+
+ mParent = NULL;
+ mUseGpuSkinning = false;
+ mRendererTransform = physx::PxMat44(physx::PxIdentity);
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::loadMaterials(nvidia::apex::ResourceCallback* resourceCallback, nvidia::apex::UserRenderResourceManager* rrm,
+ bool dummyMaterial, const char* materialPrefix, const char* materialSuffix, bool onlyVisibleMaterials)
+{
+ std::string path;
+ PX_ASSERT(mParent == NULL);
+
+ if (dummyMaterial)
+ {
+ static int dummyCount = 0;
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ char buf[64];
+ physx::shdfnd::snprintf(buf, 64, "DummMaterial%d", dummyCount++);
+ mSubMeshes[i].materialName = buf;
+ }
+ }
+
+ unsigned int maxBonesShader = 0;
+
+ for (uint32_t submeshIndex = 0; submeshIndex < mSubMeshes.size(); submeshIndex++)
+ {
+ TriangleSubMesh& sm = mSubMeshes[submeshIndex];
+
+ if (sm.materialResource != NULL)
+ {
+ continue;
+ }
+
+ if (sm.materialName.find("invisible") != std::string::npos)
+ {
+ sm.show = false;
+ }
+
+ if (onlyVisibleMaterials && !sm.show)
+ {
+ continue;
+ }
+
+ if (sm.materialName.empty())
+ {
+ sm.materialName = sm.name;
+ }
+
+ std::string materialName;
+ if (materialPrefix != NULL)
+ {
+ mMaterialPrefix = materialPrefix;
+ materialName.append(materialPrefix);
+ }
+ materialName.append(sm.materialName);
+ if (materialSuffix != NULL)
+ {
+ mMaterialSuffix = materialSuffix;
+ materialName.append(materialSuffix);
+ }
+
+#ifdef USE_SAMPLE_RENDERER
+ PX_ASSERT(sm.mSampleMaterial == NULL);
+ sm.mSampleMaterial = reinterpret_cast<SampleFramework::SampleMaterialAsset*>(
+ resourceCallback->requestResource(APEX_MATERIALS_NAME_SPACE, materialName.c_str()));
+#else
+ sm.materialResource = (MaterialResource*)resourceCallback->requestResource(APEX_MATERIALS_NAME_SPACE, materialName.c_str());
+#endif
+
+ if (materialName.find("__cloth") != std::string::npos)
+ {
+ sm.usedForCollision = false;
+ }
+
+ if (sm.materialResource != NULL || sm.mSampleMaterial != NULL)
+ {
+#ifdef USE_SAMPLE_RENDERER
+ sm.maxBonesShader = 0;//rrm->getMaxBonesForMaterial(sm.mSampleMaterial);
+#else
+ sm.maxBonesShader = rrm->getMaxBonesForMaterial(sm.materialResource);
+#endif
+ if (maxBonesShader == 0 || sm.maxBonesShader < maxBonesShader)
+ {
+ maxBonesShader = sm.maxBonesShader;
+ }
+ }
+ }
+
+#ifdef USE_SAMPLE_RENDERER
+ if (mOverrideMaterial == NULL)
+ {
+ mOverrideMaterial = reinterpret_cast<SampleFramework::SampleMaterialAsset*>(
+ resourceCallback->requestResource(APEX_MATERIALS_NAME_SPACE, "materials/simple_lit_uniform_color.xml"));
+ }
+ PX_ASSERT(mOverrideMaterial != NULL);
+#endif
+
+ optimizeForRendering();
+
+ const unsigned int maxBoneIndexMesh = uint32_t(mParent != NULL ? mParent->mMaxBoneIndexInternal : mMaxBoneIndexInternal);
+
+ mUseGpuSkinning = maxBoneIndexMesh < maxBonesShader;
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::moveAboveGround(float level)
+{
+ float lowest = FLT_MAX;
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ lowest = std::min(lowest, mVertices[i].y);
+ }
+
+ const float change = std::max(0.0f, level - lowest);
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ mVertices[i].y += change;
+ }
+ mBounds.minimum.y += change;
+ mBounds.maximum.y += change;
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::initSingleMesh()
+{
+ clear(NULL, NULL);
+ mSubMeshes.resize(1);
+ mSubMeshes[0].init();
+ mSubMeshes[0].firstIndex = 0;
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::copyFrom(const TriangleMesh& mesh)
+{
+ clear(NULL, NULL);
+
+ mVertices.resize(mesh.mVertices.size());
+ for (size_t i = 0; i < mesh.mVertices.size(); i++)
+ {
+ mVertices[i] = mesh.mVertices[i];
+ }
+
+ mNormals.resize(mesh.mNormals.size());
+ for (size_t i = 0; i < mesh.mNormals.size(); i++)
+ {
+ mNormals[i] = mesh.mNormals[i];
+ }
+
+ mTangents.resize(mesh.mTangents.size());
+ for (size_t i = 0; i < mesh.mTangents.size(); i++)
+ {
+ mTangents[i] = mesh.mTangents[i];
+ }
+
+ mBitangents.resize(mesh.mBitangents.size());
+ for (size_t i = 0; i < mesh.mBitangents.size(); i++)
+ {
+ mBitangents[i] = mesh.mBitangents[i];
+ }
+
+ for (unsigned int t = 0; t < NUM_TEXCOORDS; t++)
+ {
+ mTexCoords[t].resize(mesh.mTexCoords[t].size());
+ for (size_t i = 0; i < mesh.mTexCoords[t].size(); i++)
+ {
+ mTexCoords[t][i] = mesh.mTexCoords[t][i];
+ }
+ }
+
+ for (unsigned int i = 0; i < PC_NUM_CHANNELS; i++)
+ {
+ mPaintChannels[i].resize(mesh.mPaintChannels[i].size());
+ for (size_t j = 0; j < mesh.mPaintChannels[i].size(); j++)
+ {
+ mPaintChannels[i][j] = mesh.mPaintChannels[i][j];
+ }
+ }
+
+ mSubMeshes.resize(mesh.mSubMeshes.size());
+ for (size_t i = 0; i < mesh.mSubMeshes.size(); i++)
+ {
+ mSubMeshes[i] = mesh.mSubMeshes[i];
+ mSubMeshes[i].materialResource = NULL;
+ // don't copy these
+ mSubMeshes[i].mRenderResource = NULL;
+ mSubMeshes[i].mRendererMesh = NULL;
+ mSubMeshes[i].mRendererMeshContext = NULL;
+ mSubMeshes[i].mSampleMaterial = NULL;
+ mSubMeshes[i].mRendererMaterialReference = NULL;
+ mSubMeshes[i].mRendererMaterialInstance = NULL;
+ }
+
+ mIndices.resize(mesh.mIndices.size());
+ for (size_t i = 0; i < mesh.mIndices.size(); i++)
+ {
+ mIndices[i] = mesh.mIndices[i];
+ }
+
+ // skeleton binding
+ mSkeletonFile = mesh.mSkeletonFile;
+
+ mBoneIndicesExternal.resize(mesh.mBoneIndicesExternal.size());
+ for (size_t i = 0; i < mesh.mBoneIndicesExternal.size(); i++)
+ {
+ mBoneIndicesExternal[i] = mesh.mBoneIndicesExternal[i];
+ }
+
+ mBoneWeights.resize(mesh.mBoneWeights.size());
+ for (size_t i = 0; i < mesh.mBoneWeights.size(); i++)
+ {
+ mBoneWeights[i] = mesh.mBoneWeights[i];
+ }
+
+ mName = mesh.mName;
+ mRendererTransform = mesh.mRendererTransform;
+ mMaxBoneIndexExternal = mesh.mMaxBoneIndexExternal;
+ updateBounds();
+ updateBoneWeights();
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::copyFromSubMesh(const TriangleMesh& mesh, int subMeshNr, bool filterVertices)
+{
+ if (!filterVertices && subMeshNr < 0)
+ {
+ copyFrom(mesh);
+ return;
+ }
+
+ clear(NULL, NULL);
+
+ if (subMeshNr >= (int)mesh.mSubMeshes.size())
+ {
+ return;
+ }
+
+ int firstIndex = 0;
+ int numIndices = (int)mesh.getNumIndices();
+ if (subMeshNr >= 0)
+ {
+ const TriangleSubMesh& sm = mesh.mSubMeshes[(uint32_t)subMeshNr];
+ firstIndex = (int32_t)sm.firstIndex;
+ numIndices = (int32_t)sm.numIndices;
+ }
+
+ size_t numVerts = mesh.mVertices.size();
+
+ std::vector<int> oldToNew;
+ oldToNew.resize(numVerts);
+ for (size_t i = 0; i < numVerts; i++)
+ {
+ oldToNew[i] = -1;
+ }
+
+ mSubMeshes.resize(1);
+ mSubMeshes[0].init();
+
+ const std::vector<PaintedVertex> &physChannel = mesh.getPaintChannel(PC_LATCH_TO_NEAREST_SLAVE);
+ const bool filter = filterVertices && physChannel.size() == mesh.getNumVertices();
+ mIndices.clear();
+ int nextIndex = 0;
+ for (int i = firstIndex; i < firstIndex + numIndices; i += 3)
+ {
+
+ bool skipTri = false;
+ if (filter)
+ {
+ for (int j = 0; j < 3; j++)
+ {
+ skipTri |= (physChannel[mesh.mIndices[uint32_t(i + j)]].paintValueU32 != 0);
+ }
+ }
+ if (skipTri)
+ {
+ continue;
+ }
+
+ for (int j = 0; j < 3; j++)
+ {
+ uint32_t idx = mesh.mIndices[uint32_t(i + j)];
+ if (oldToNew[idx] >= 0)
+ {
+ mIndices.push_back((uint32_t)oldToNew[idx]);
+ }
+ else
+ {
+ mIndices.push_back((uint32_t)nextIndex);
+ oldToNew[idx] = nextIndex;
+ nextIndex++;
+ }
+ }
+ }
+ int numNewVerts = nextIndex;
+
+ mVertices.resize((uint32_t)numNewVerts);
+ for (size_t i = 0; i < numVerts; i++)
+ {
+ if (oldToNew[i] >= 0)
+ {
+ mVertices[(uint32_t)oldToNew[i]] = mesh.mVertices[i];
+ }
+ }
+
+ if (mesh.mNormals.size() == numVerts)
+ {
+ mNormals.resize((uint32_t)numNewVerts);
+ for (size_t i = 0; i < numVerts; i++)
+ {
+ if (oldToNew[i] >= 0)
+ {
+ mNormals[(uint32_t)oldToNew[i]] = mesh.mNormals[i];
+ }
+ }
+ }
+
+ if (mesh.mTangents.size() == numVerts)
+ {
+ mTangents.resize((uint32_t)numNewVerts);
+ for (size_t i = 0; i < numVerts; i++)
+ {
+ if (oldToNew[i] >= 0)
+ {
+ mTangents[(uint32_t)oldToNew[i]] = mesh.mTangents[i];
+ }
+ }
+ }
+
+ if (mesh.mBitangents.size() == numVerts)
+ {
+ mBitangents.resize((uint32_t)numNewVerts);
+ for (size_t i = 0; i < numVerts; i++)
+ {
+ if (oldToNew[i] >= 0)
+ {
+ mBitangents[(uint32_t)oldToNew[i]] = mesh.mBitangents[i];
+ }
+ }
+ }
+
+ for (unsigned int t = 0; t < NUM_TEXCOORDS; t++)
+ {
+ // PH: I suppose we could delete these 3 lines?
+ mTexCoords[t].resize(mesh.mTexCoords[t].size());
+ for (uint32_t i = 0; i < mesh.mTexCoords[t].size(); i++)
+ {
+ mTexCoords[t][i] = mesh.mTexCoords[t][i];
+ }
+
+ if (mesh.mTexCoords[t].size() == numVerts)
+ {
+ mTexCoords[t].resize((uint32_t)numNewVerts);
+
+ for (size_t j = 0; j < numVerts; j++)
+ {
+ if (oldToNew[j] >= 0)
+ {
+ mTexCoords[t][(uint32_t)oldToNew[j]] = mesh.mTexCoords[t][j];
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < PC_NUM_CHANNELS; i++)
+ {
+ if (mesh.mPaintChannels[i].size() == numVerts)
+ {
+ mPaintChannels[i].resize((uint32_t)numNewVerts);
+ for (size_t j = 0; j < numVerts; j++)
+ {
+ if (oldToNew[j] >= 0)
+ {
+ mPaintChannels[i][(uint32_t)oldToNew[j]] = mesh.mPaintChannels[i][j];
+ }
+ }
+ }
+ }
+
+ // skeleton binding
+
+ mSkeletonFile = mesh.mSkeletonFile;
+
+ if (mesh.mBoneIndicesExternal.size() == numVerts * 4)
+ {
+ mBoneIndicesExternal.resize((uint32_t)numNewVerts * 4);
+ mBoneWeights.resize((uint32_t)numNewVerts);
+
+ for (size_t i = 0; i < numVerts; i++)
+ {
+ if (oldToNew[i] >= 0)
+ {
+ const int newIndex = oldToNew[i];
+ for (int j = 0; j < 4; j++)
+ {
+ mBoneIndicesExternal[(uint32_t)newIndex * 4 + j] = mesh.mBoneIndicesExternal[i * 4 + j];
+ }
+ mBoneWeights[(uint32_t)newIndex] = mesh.mBoneWeights[i];
+ }
+ }
+ }
+ mSubMeshes[0].firstIndex = 0;
+ mSubMeshes[0].numIndices = (unsigned int)mIndices.size();
+ mName = mesh.mName;
+ mRendererTransform = mesh.mRendererTransform;
+ updateBounds();
+ updateBoneWeights();
+}
+
+// -------------------------------------------------------------------
+bool TriangleMesh::loadFromObjFile(const std::string& filename, bool useCustomChannels)
+{
+#if PX_WINDOWS_FAMILY == 0
+ PX_UNUSED(filename);
+ PX_UNUSED(useCustomChannels);
+ return false;
+#else
+ initSingleMesh();
+ mName = filename;
+
+ // extract path
+ size_t slashPos = filename.rfind('\\', std::string::npos);
+ size_t columnPos = filename.rfind(':', std::string::npos);
+
+ size_t pos = slashPos > columnPos ? slashPos : columnPos;
+ std::string path = pos == std::string::npos ? "" : filename.substr(0, pos);
+
+ char s[OBJ_STR_LEN], ps[OBJ_STR_LEN];
+ physx::PxVec3 v;
+ nvidia::VertexUV tc;
+ std::vector<SimpleVertexRef> refs;
+ SimpleVertexRef ref[3];
+
+ int numIndices = 0;
+ std::vector<physx::PxVec3> vertices;
+ std::vector<physx::PxVec3> normals;
+ std::vector<nvidia::VertexUV> texCoords;
+
+
+ FILE* f = 0;
+
+ if (::fopen_s(&f, filename.c_str(), "r") != 0)
+ {
+ return false;
+ }
+
+ std::string groupName;
+ std::string useMtl;
+
+ // first a vertex ref is generated for each v/n/t combination
+ while (!feof(f))
+ {
+ if (fgets(s, OBJ_STR_LEN, f) == NULL)
+ {
+ break;
+ }
+
+ //wxMessageBox(s);
+ if (strncmp(s, "usemtl", 6) == 0 || strncmp(s, "g ", 2) == 0) // new group
+ {
+ if (strncmp(s, "usemtl", 6) == 0)
+ {
+ char sub[OBJ_STR_LEN];
+ sscanf(&s[7], "%s", sub);
+ useMtl = sub;
+ }
+ else
+ {
+ char sub[OBJ_STR_LEN];
+ sscanf(&s[2], "%s", sub);
+ groupName = sub;
+ }
+
+ size_t numSubs = mSubMeshes.size();
+ if (mSubMeshes[numSubs - 1].numIndices > 0)
+ {
+ mSubMeshes.resize(numSubs + 1);
+ mSubMeshes[numSubs].init();
+ mSubMeshes[numSubs].firstIndex = (uint32_t)(mIndices.size());
+ mSubMeshes[numSubs].materialName = useMtl;
+ mSubMeshes[numSubs].originalMaterialName = useMtl;
+ }
+ else
+ {
+ mSubMeshes[numSubs - 1].materialName = useMtl;
+ mSubMeshes[numSubs - 1].originalMaterialName = useMtl;
+ }
+ size_t subNr = mSubMeshes.size() - 1;
+ mSubMeshes[subNr].name = groupName;
+ }
+ else if (strncmp(s, "v ", 2) == 0) // vertex
+ {
+ sscanf(s, "v %f %f %f", &v.x, &v.y, &v.z);
+ vertices.push_back(v);
+ }
+ else if (strncmp(s, "vn ", 3) == 0) // normal
+ {
+ sscanf(s, "vn %f %f %f", &v.x, &v.y, &v.z);
+ normals.push_back(v);
+ }
+ else if (strncmp(s, "vt ", 3) == 0) // texture coords
+ {
+ sscanf(s, "vt %f %f", &tc.u, &tc.v);
+ texCoords.push_back(tc);
+ }
+ else if (strncmp(s, "f ", 2) == 0) // face, tri or quad
+ {
+ size_t offset = 2;
+ size_t index = 0;
+ while (sscanf(s + offset, "%s", ps) > 0)
+ {
+ offset += strlen(ps);
+ while (s[offset] == ' ' || s[offset] == '\t')
+ {
+ offset++;
+ }
+
+ if (index >= 2)
+ {
+ // submit triangle
+ ref[2].parse(ps, 0);
+ ref[0].indexNr = numIndices++;
+ refs.push_back(ref[0]);
+ mIndices.push_back(0);
+ ref[1].indexNr = numIndices++;
+ refs.push_back(ref[1]);
+ mIndices.push_back(0);
+ ref[2].indexNr = numIndices++;
+ refs.push_back(ref[2]);
+ mIndices.push_back(0);
+ mSubMeshes[mSubMeshes.size() - 1].numIndices += 3;
+
+ ref[1] = ref[2];
+ index++;
+ }
+ else
+ {
+ ref[index].parse(ps, 0);
+ index++;
+ }
+ }
+ }
+ }
+ fclose(f);
+
+ // make sure that vertices with left/right handed tangent space don't get merged
+ for (size_t i = 0; i < refs.size(); i += 3)
+ {
+ const physx::PxVec3 p0 = vertices[(uint32_t)refs[i + 0].vert];
+ const physx::PxVec3 p1 = vertices[(uint32_t)refs[i + 1].vert];
+ const physx::PxVec3 p2 = vertices[(uint32_t)refs[i + 2].vert];
+
+ float handedNess = 1.0f;
+
+ if (refs[i + 0].texCoord >= 0)
+ {
+ const physx::PxVec3 faceNormal = (p1 - p0).cross(p2 - p0);
+
+ const nvidia::VertexUV w0 = texCoords[(uint32_t)refs[i + 0].texCoord];
+ const nvidia::VertexUV w1 = texCoords[(uint32_t)refs[i + 1].texCoord];
+ const nvidia::VertexUV w2 = texCoords[(uint32_t)refs[i + 2].texCoord];
+
+ const float x1 = p1.x - p0.x;
+ const float x2 = p2.x - p0.x;
+ const float y1 = p1.y - p0.y;
+ const float y2 = p2.y - p0.y;
+ const float z1 = p1.z - p0.z;
+ const float z2 = p2.z - p0.z;
+
+ const float s1 = w1.u - w0.u;
+ const float s2 = w2.u - w0.u;
+ const float t1 = w1.v - w0.v;
+ const float t2 = w2.v - w0.v;
+
+ const float r = 1.0F / (s1 * t2 - s2 * t1);
+ const physx::PxVec3 tangentDir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
+ const physx::PxVec3 bitangentDir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
+
+ handedNess = faceNormal.cross(tangentDir).dot(bitangentDir) > 0.0f ? 1.0f : -1.0f;
+ }
+
+ refs[i + 0].handedNess = handedNess;
+ refs[i + 1].handedNess = handedNess;
+ refs[i + 2].handedNess = handedNess;
+ }
+
+ // now we merge multiple v/n/t triplets
+ //std::sort(refs.begin(), refs.end());
+
+ size_t readRefs = 0;
+ physx::PxVec3 defNormal(1.0f, 0.0f, 0.0f);
+ bool normalsOK = true;
+ int numTexCoords = (int)(texCoords.size());
+
+ while (readRefs < refs.size())
+ {
+ int vertNr = (int)(mVertices.size());
+ SimpleVertexRef& r = refs[readRefs];
+ mVertices.push_back(vertices[(uint32_t)r.vert]);
+
+ if (r.normal >= 0)
+ {
+ mNormals.push_back(normals[(uint32_t)r.normal]);
+ }
+ else
+ {
+ mNormals.push_back(defNormal);
+ normalsOK = false;
+ }
+
+ if (r.texCoord >= 0 && r.texCoord < numTexCoords)
+ {
+ mTexCoords[0].push_back(texCoords[(uint32_t)r.texCoord]);
+ }
+ else
+ {
+ mTexCoords[0].push_back(nvidia::VertexUV(0.0f, 0.0f));
+ }
+
+ mIndices[(uint32_t)r.indexNr] = (uint32_t)vertNr;
+ readRefs++;
+ /*while (readRefs < refs.size() && r == refs[readRefs])
+ {
+ mIndices[refs[readRefs].indexNr] = vertNr;
+ readRefs++;
+ }*/
+ }
+
+ complete(useCustomChannels);
+
+ if (!normalsOK)
+ {
+ updateNormals(-1);
+ }
+
+ updateTangents();
+
+ mMaterialPrefix.clear();
+ mMaterialSuffix.clear();
+
+ return true;
+#endif
+}
+
+// ----------------------------------------------------------------------
+bool TriangleMesh::saveToObjFile(const std::string& filename) const
+{
+#if PX_WINDOWS_FAMILY == 0
+ PX_UNUSED(filename);
+ return false;
+#else
+ FILE* f = 0;
+ if (::fopen_s(&f, filename.c_str(), "w") != 0)
+ {
+ return false;
+ }
+
+ fprintf(f, "# Wavefront OBJ\n");
+ fprintf(f, "\n");
+
+ fprintf(f, "\n");
+ fprintf(f, "# %i vertices:\n", (int)(mVertices.size()));
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ const physx::PxVec3& v = mVertices[i];
+ fprintf(f, "v %f %f %f\n", v.x, v.y, v.z);
+ }
+
+ fprintf(f, "\n");
+ int numTex = (int)(mTexCoords[0].size());
+ fprintf(f, "# %i texture coordinates:\n", numTex);
+ for (uint32_t i = 0; i < (uint32_t)numTex; i++)
+ {
+ fprintf(f, "vt %f %f\n", mTexCoords[0][i].u, mTexCoords[0][i].v);
+ }
+
+ fprintf(f, "\n");
+ fprintf(f, "# %i normals:\n", (int)(mNormals.size()));
+ for (uint32_t i = 0; i < mNormals.size(); i++)
+ {
+ const physx::PxVec3& v = mNormals[i];
+ fprintf(f, "vn %f %f %f\n", v.x, v.y, v.z);
+ }
+
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ const TriangleSubMesh& sm = mSubMeshes[i];
+ fprintf(f, "\n");
+ fprintf(f, "g %s\n", sm.name.c_str());
+
+ for (uint32_t j = sm.firstIndex; j < sm.firstIndex + sm.numIndices; j += 3)
+ {
+ uint32_t i0 = mIndices[j];
+ uint32_t i1 = mIndices[j + 1];
+ uint32_t i2 = mIndices[j + 2];
+
+ fprintf(f, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", i0, i0, i0, i1, i1, i1, i2, i2, i2);
+ }
+ fprintf(f, "\n");
+ }
+ fclose(f);
+
+ return true;
+#endif
+}
+
+// -------------------------------------------------------------------
+bool TriangleMesh::loadFromXML(const std::string& filename, bool loadCustomChannels)
+{
+ clear(NULL, NULL);
+
+ physx::shdfnd::FastXml* fastXml = physx::shdfnd::createFastXml(this);
+ mParserState = PS_Uninitialized;
+ physx::PsFileBuffer fb(filename.c_str(), physx::PxFileBuf::OPEN_READ_ONLY);
+ physx::PxInputDataFromPxFileBuf id(fb);
+ fastXml->processXml(id);
+
+ int errorLineNumber = -1;
+ const char* xmlError = fastXml->getError(errorLineNumber);
+ if (xmlError != NULL)
+ {
+#ifdef WIN32
+ char temp[1024];
+ sprintf_s(temp, 1024, "Xml parse error in %s on line %d:\n\n%s", filename.c_str(), errorLineNumber, xmlError);
+ MessageBox(NULL, temp, "FastXML", MB_OK);
+#else
+ printf("Xml parse error in %s on line %d:\n\n%s", filename.c_str(), errorLineNumber, xmlError);
+#endif
+ return false;
+ }
+
+ if (fastXml)
+ {
+ fastXml->release();
+ }
+
+ // normalize bone weights
+ int invalidStart = -1;
+ if (mBoneWeights.size() > 0)
+ {
+ for (size_t i = 0; i < mVertices.size(); i++)
+ {
+ float sum = 0;
+ for (unsigned int j = 0; j < 4; j++)
+ {
+ sum += mBoneWeights[i][j];
+ }
+ if (sum > 0)
+ {
+ float scale = 1.0f / sum;
+ mBoneWeights[i] *= scale;
+ if (invalidStart != -1)
+ {
+ PX_ASSERT(i > 0);
+ printf("\n\nWARNING: Invalid Vertices from %d to %d\n\n\n", invalidStart, (int)(i - 1));
+ invalidStart = -1;
+ }
+ }
+ else
+ {
+ if (invalidStart == -1)
+ {
+ invalidStart = (int)i;
+ }
+ }
+ }
+ }
+ if (invalidStart != -1)
+ {
+ printf("\n\nWARNING: Invalid Vertices from %d to %d\n\n\n", invalidStart, (int)(mVertices.size() - 1));
+ invalidStart = -1;
+ }
+
+ complete(loadCustomChannels);
+
+ //mShaderHasEnoughBones = getMaxBoneIndex() < shaderMaxBones;
+
+ mMaterialPrefix.clear();
+ mMaterialSuffix.clear();
+
+ return true;
+}
+
+// -------------------------------------------------------------------
+bool TriangleMesh::saveToXML(const std::string& filename)
+{
+ FILE* f = fopen(filename.c_str(), "w");
+ if (!f)
+ {
+ return false;
+ }
+
+ fprintf(f, "<mesh>\n");
+
+ fprintf(f, " <sharedgeometry vertexcount=\"%i\">\n", (int)(mVertices.size()));
+
+ fprintf(f, " <vertexbuffer positions=\"true\" normals=\"true\">\n");
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ fprintf(f, " <vertex>\n");
+ fprintf(f, " <position x=\"%f\" y=\"%f\" z=\"%f\" />\n", mVertices[i].x, mVertices[i].y, mVertices[i].z);
+ if (i < mNormals.size())
+ {
+ fprintf(f, " <normal x=\"%f\" y=\"%f\" z=\"%f\" />\n", mNormals[i].x, mNormals[i].y, mNormals[i].z);
+ }
+ fprintf(f, " </vertex>\n");
+ }
+ fprintf(f, " </vertexbuffer>\n");
+
+ fprintf(f, " <vertexbuffer texture_coord_dimensions_0=\"2\">\n");
+ for (uint32_t i = 0; i < mTexCoords[0].size(); i++)
+ {
+ fprintf(f, " <vertex>\n");
+ fprintf(f, " <texcoord u=\"%f\" v=\"%f\" />\n", mTexCoords[0][i].u, mTexCoords[0][i].v);
+ fprintf(f, " </vertex>\n");
+ }
+ fprintf(f, " </vertexbuffer>\n");
+
+ bool coeffsOK = true;
+// coeffsOK = false; // no more coeffs
+ for (int i = 0; i < PC_NUM_CHANNELS; i++)
+ {
+ if (mPaintChannels[i].size() != mVertices.size())
+ {
+ coeffsOK = false;
+ }
+ }
+
+ if (coeffsOK)
+ {
+ fprintf(f, " <vertexbuffer physics_coeffs=\"%i\">\n", PC_NUM_CHANNELS);
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ fprintf(f, " <vertex>\n");
+ fprintf(f, " <physics_coeffs ");
+ for (uint32_t j = 0; j < PC_NUM_CHANNELS; j++)
+ {
+ fprintf(f, "v%i=\"%f\" ", j, mPaintChannels[j][i].paintValueF32);
+ }
+ fprintf(f, " />\n");
+ fprintf(f, " </vertex>\n");
+ }
+ fprintf(f, " </vertexbuffer>\n");
+ }
+
+ fprintf(f, " </sharedgeometry>\n");
+
+ fprintf(f, " <submeshes>\n");
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ TriangleSubMesh& sm = mSubMeshes[i];
+ fprintf(f, " <submesh material=\"%s\" usesharedvertices=\"true\" use32bitindexes=\"true\" operationtype=\"triangle_list\">\n",
+ sm.materialName.c_str());
+ unsigned int numTris = sm.numIndices / 3;
+ fprintf(f, " <faces count =\"%i\">\n", numTris);
+
+ for (uint32_t j = sm.firstIndex; j < sm.firstIndex + sm.numIndices; j += 3)
+ {
+ fprintf(f, " <face v1=\"%i\" v2=\"%i\" v3=\"%i\" />\n", mIndices[j], mIndices[j + 1], mIndices[j + 2]);
+ }
+ fprintf(f, " </faces>\n");
+ fprintf(f, " </submesh>\n");
+ }
+ fprintf(f, " </submeshes>\n");
+
+ fprintf(f, " <skeletonlink name=\"%s\" />\n", mSkeletonFile.c_str());
+
+ if (mBoneWeights.size() > 0)
+ {
+ fprintf(f, " <boneassignments>\n");
+ for (uint32_t i = 0; i < (uint32_t)mBoneWeights.size(); i++)
+ {
+ for (uint32_t j = 0; j < 4; j++)
+ {
+ fprintf(f, " <vertexboneassignment vertexindex=\"%i\" boneindex=\"%i\" weight=\"%f\" />\n",
+ i, mBoneIndicesExternal[i * 4 + j], mBoneWeights[i][j]);
+ }
+ }
+ fprintf(f, " </boneassignments>\n");
+ }
+
+ fprintf(f, "</mesh>\n");
+
+
+ fclose(f);
+
+ return true;
+}
+
+// ----------------------------------------------------------------------
+bool TriangleMesh::loadFromParent(TriangleMesh* parent)
+{
+ if (parent == NULL)
+ {
+ return false;
+ }
+
+ mParent = parent;
+ mMaxBoneIndexExternal = mParent->mMaxBoneIndexExternal;
+ mMaxBoneIndexInternal = mParent->mMaxBoneIndexInternal;
+
+ mSubMeshes.resize(mParent->mSubMeshes.size());
+ for (uint32_t i = 0; i < mParent->mSubMeshes.size(); i++)
+ {
+ mSubMeshes[i] = mParent->mSubMeshes[i];
+ mSubMeshes[i].mRenderResource = NULL; // don't copy that one!!!
+#ifdef USE_SAMPLE_RENDERER
+ mSubMeshes[i].mRendererMeshContext = new SampleRenderer::RendererMeshContext;
+ if (mSubMeshes[i].mRendererMaterialInstance != NULL)
+ {
+ mSubMeshes[i].mRendererMaterialInstance = new SampleRenderer::RendererMaterialInstance(*mSubMeshes[i].mRendererMaterialInstance);
+ }
+ mSubMeshes[i].mRendererMesh = NULL;
+#endif
+ }
+
+ mMaterialPrefix.clear();
+ mMaterialSuffix.clear();
+
+ mUseGpuSkinning = parent->mUseGpuSkinning;
+ mRenderer = mParent->mRenderer;
+
+ return true;
+}
+
+// ----------------------------------------------------------------------
+bool TriangleMesh::loadFromMeshImport(mimp::MeshSystemContainer* msc, bool useCustomChannels)
+{
+ if (msc == NULL)
+ {
+ return false;
+ }
+
+ mimp::MeshSystem* ms = mimp::gMeshImport->getMeshSystem(msc);
+
+ if (ms->mMeshCount == 0)
+ {
+ return false;
+ }
+
+ mimp::Mesh* m = ms->mMeshes[0];
+ if (m->mVertexCount == 0)
+ {
+ return false;
+ }
+
+ // only now that everything is OK do we delete the existing mesh
+ initSingleMesh();
+
+ // and since we generate all submeshes ourselves
+ mSubMeshes.clear();
+
+ const char* bake = strstr(ms->mAssetInfo, "applyRootTransformToMesh");
+ bool bakeRootBoneTransformation = bake != NULL;
+ physx::PxTransform rootBoneTransformation;
+ if (ms->mSkeletonCount > 0 && bakeRootBoneTransformation)
+ {
+ mimp::MeshBone& b = ms->mSkeletons[0]->mBones[0];
+ PxQuatFromArray(rootBoneTransformation.q, b.mOrientation);
+ PxVec3FromArray(rootBoneTransformation.p, b.mPosition);
+ }
+
+ for (unsigned int k = 0; k < ms->mMeshCount; k++)
+ {
+ mimp::Mesh* m = ms->mMeshes[k];
+
+ unsigned int base_index = (uint32_t)mVertices.size();
+
+ // PH: ClothingTool does not cope well with really small meshes
+ if (m->mVertexCount < 3)
+ {
+ continue;
+ }
+
+ for (unsigned int i = 0; i < m->mVertexCount; i++)
+ {
+ const mimp::MeshVertex& v = m->mVertices[i];
+
+ physx::PxVec3 p;
+ PxVec3FromArray(p, v.mPos);
+ if (bakeRootBoneTransformation)
+ {
+ p = rootBoneTransformation.transform(p);
+ }
+
+ if (m->mVertexFlags & mimp::MIVF_NORMAL)
+ {
+ physx::PxVec3 n;
+ PxVec3FromArray(n, v.mNormal);
+ if (bakeRootBoneTransformation)
+ {
+ n = rootBoneTransformation.rotate(n);
+ }
+ mVertices.push_back(p);
+ mNormals.push_back(n);
+ }
+
+ if (m->mVertexFlags & mimp::MIVF_TANGENT)
+ {
+ physx::PxVec3 t;
+ PxVec3FromArray(t, v.mTangent);
+ if (bakeRootBoneTransformation)
+ {
+ t = rootBoneTransformation.rotate(t);
+ }
+ mTangents.push_back(t);
+ }
+
+ if (m->mVertexFlags & mimp::MIVF_BINORMAL)
+ {
+ physx::PxVec3 b;
+ PxVec3FromArray(b, v.mBiNormal);
+ if (bakeRootBoneTransformation)
+ {
+ b = rootBoneTransformation.rotate(b);
+ }
+ mBitangents.push_back(b);
+ }
+
+ if (m->mVertexFlags & mimp::MIVF_TEXEL1)
+ {
+ mTexCoords[0].push_back(nvidia::VertexUV(v.mTexel1[0], v.mTexel1[1]));
+ }
+
+ if (m->mVertexFlags & mimp::MIVF_TEXEL2)
+ {
+ mTexCoords[1].push_back(nvidia::VertexUV(v.mTexel2[0], v.mTexel2[1]));
+ }
+
+ if (m->mVertexFlags & mimp::MIVF_TEXEL3)
+ {
+ mTexCoords[2].push_back(nvidia::VertexUV(v.mTexel3[0], v.mTexel3[1]));
+ }
+
+ if (m->mVertexFlags & mimp::MIVF_TEXEL4)
+ {
+ mTexCoords[3].push_back(nvidia::VertexUV(v.mTexel4[0], v.mTexel4[1]));
+ }
+
+ if (m->mVertexFlags & mimp::MIVF_BONE_WEIGHTING)
+ {
+ physx::PxVec4 boneWeight(0.0f, 0.0f, 0.0f, 0.0f);
+ unsigned int boneWeightWritten = 0;
+ for (unsigned int j = 0; j < 4; j++)
+ {
+ if (v.mWeight[j] > 0.0f)
+ {
+ mBoneIndicesExternal.push_back(v.mBone[j]);
+ mMaxBoneIndexExternal = physx::PxMax(mMaxBoneIndexExternal, (int32_t)v.mBone[j]);
+ boneWeight[boneWeightWritten++] = v.mWeight[j];
+ }
+ else
+ {
+ mBoneIndicesExternal.push_back(0);
+ }
+ }
+ PX_ASSERT(boneWeightWritten <= 4);
+ mBoneWeights.push_back(boneWeight);
+ }
+
+ // Fixup
+ for (unsigned int t = 0; t < NUM_TEXCOORDS; t++)
+ {
+ nvidia::VertexUV uv(0.0f, 0.0f);
+ if (mTexCoords[t].size() > 0)
+ {
+ while (mTexCoords[t].size() < mVertices.size())
+ {
+ mTexCoords[t].push_back(uv);
+ }
+ }
+ }
+ }
+
+ for (unsigned int i = 0; i < m->mSubMeshCount; i++)
+ {
+ bool showMesh = true;
+
+ mimp::SubMesh* sm = m->mSubMeshes[i];
+ if (sm->mMaterial && sm->mMaterial->mMetaData)
+ {
+ mimp::KeyValue kv;
+ unsigned int count;
+ const char** keyValues = kv.getKeyValues(sm->mMaterial->mMetaData, count);
+ for (unsigned int k = 0; k < count; ++k)
+ {
+ const char* key = keyValues[k * 2];
+ const char* kvalue = keyValues[k * 2 + 1];
+ if (0 == ::strcmp(key, "show") && 0 == ::strcmp(kvalue, "invisible"))
+ {
+ showMesh = false;
+ }
+ }
+ }
+
+ TriangleSubMesh t;
+ t.init();
+ t.firstIndex = (uint32_t)mIndices.size();
+ t.numIndices = sm->mTriCount * 3;
+ t.name = m->mName;
+ t.materialName = sm->mMaterialName;
+ t.originalMaterialName = sm->mMaterialName;
+ t.show = showMesh;
+
+ for (unsigned int j = 0; j < t.numIndices; j++)
+ {
+ mIndices.push_back(sm->mIndices[j] + base_index);
+ }
+ mSubMeshes.push_back(t);
+ }
+ }
+
+ complete(useCustomChannels);
+
+ //mShaderHasEnoughBones = getMaxBoneIndex() < shaderMaxBones;
+
+ mMaterialPrefix.clear();
+ mMaterialSuffix.clear();
+
+ return true;
+}
+
+// ----------------------------------------------------------------------
+bool TriangleMesh::saveToMeshImport(mimp::MeshSystemContainer* msc)
+{
+#if PX_WINDOWS_FAMILY == 0
+ PX_UNUSED(msc);
+ return false;
+#else
+ if (msc == NULL)
+ {
+ return false;
+ }
+
+ mimp::MeshSystem* ms = mimp::gMeshImport->getMeshSystem(msc);
+
+ ms->mMeshCount = 1;
+ ms->mMeshes = (mimp::Mesh**)::malloc(sizeof(mimp::Mesh*) * 1);
+ ms->mMeshes[0] = new mimp::Mesh;
+
+ unsigned int vertexFlags = mimp::MIVF_POSITION;
+
+ // fill out vertices
+ {
+ ms->mMeshes[0]->mVertexCount = (unsigned int)mVertices.size();
+ mimp::MeshVertex* verts = ms->mMeshes[0]->mVertices = (mimp::MeshVertex*)::malloc(sizeof(mimp::MeshVertex) * mVertices.size());
+ ::memset(verts, 0, sizeof(mimp::MeshVertex) * mVertices.size());
+
+ for (size_t i = 0; i < mVertices.size(); i++)
+ {
+ (physx::PxVec3&)verts[i].mPos = mVertices[i];
+ }
+
+ if (mNormals.size() == mVertices.size())
+ {
+ vertexFlags |= mimp::MIVF_NORMAL;
+ for (size_t i = 0; i < mNormals.size(); i++)
+ {
+ (physx::PxVec3&)verts[i].mNormal = mNormals[i];
+ }
+ }
+
+ if (mTangents.size() == mVertices.size())
+ {
+ vertexFlags |= mimp::MIVF_TANGENT;
+ for (size_t i = 0; i < mTangents.size(); i++)
+ {
+ (physx::PxVec3&)verts[i].mTangent = mTangents[i];
+ }
+ }
+
+ if (mBitangents.size() == mVertices.size())
+ {
+ vertexFlags |= mimp::MIVF_BINORMAL;
+ for (size_t i = 0; i < mBitangents.size(); i++)
+ {
+ (physx::PxVec3&)verts[i].mBiNormal = mBitangents[i];
+ }
+ }
+
+ if (mTexCoords[0].size() == mVertices.size())
+ {
+ vertexFlags |= mimp::MIVF_TEXEL1;
+ for (size_t i = 0; i < mTexCoords[0].size(); i++)
+ {
+ (nvidia::VertexUV&)verts[i].mTexel1 = mTexCoords[0][i];
+ }
+ }
+
+ if (mBoneWeights.size() == mVertices.size())
+ {
+ PX_ASSERT(mBoneIndicesExternal.size() == mVertices.size() * 4);
+ vertexFlags |= mimp::MIVF_BONE_WEIGHTING;
+
+ for (size_t i = 0; i < mBoneWeights.size(); i++)
+ {
+ for (int k = 0; k < 4; k++)
+ {
+ verts[i].mBone[k] = mBoneIndicesExternal[i * 4 + k];
+ verts[i].mWeight[k] = mBoneWeights[i][k];
+ }
+ }
+ }
+ }
+
+ ms->mMeshes[0]->mVertexFlags = vertexFlags;
+
+ {
+ size_t nameLen = mSubMeshes[0].name.length() + 1;
+ ms->mMeshes[0]->mName = (char*)::malloc(sizeof(char) * nameLen);
+ strcpy_s((char*)ms->mMeshes[0]->mName, nameLen, mSubMeshes[0].name.c_str());
+ }
+
+ ms->mMeshes[0]->mSubMeshCount = (unsigned int)mSubMeshes.size();
+ ms->mMeshes[0]->mSubMeshes = (mimp::SubMesh**)::malloc(sizeof(mimp::SubMesh*) * mSubMeshes.size());
+
+ for (size_t submeshIndex = 0; submeshIndex < mSubMeshes.size(); submeshIndex++)
+ {
+ ms->mMeshes[0]->mSubMeshes[submeshIndex] = new mimp::SubMesh;
+ mimp::SubMesh* sm = ms->mMeshes[0]->mSubMeshes[submeshIndex];
+
+ sm->mIndices = (unsigned int*)::malloc(sizeof(unsigned int) * mSubMeshes[submeshIndex].numIndices);
+
+ for (size_t i = 0; i < mSubMeshes[submeshIndex].numIndices; i++)
+ {
+ sm->mIndices[i] = mIndices[mSubMeshes[submeshIndex].firstIndex + i];
+ }
+ PX_ASSERT(mSubMeshes[submeshIndex].numIndices % 3 == 0);
+ sm->mTriCount = mSubMeshes[submeshIndex].numIndices / 3;
+
+ size_t matNameLength = mSubMeshes[submeshIndex].materialName.length() + 1;
+ sm->mMaterialName = (char*)::malloc(sizeof(char) * matNameLength);
+ strcpy_s((char*)sm->mMaterialName, matNameLength, mSubMeshes[submeshIndex].materialName.c_str());
+
+ sm->mVertexFlags = vertexFlags;
+ }
+
+ return true;
+#endif
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::initPlane(float length, float uvDist, const char* materialName)
+{
+ initSingleMesh();
+
+ mVertices.resize(9);
+ mNormals.resize(9);
+ mTexCoords[0].resize(9);
+
+ for (uint32_t i = 0; i < 9; i++)
+ {
+ mNormals[i] = physx::PxVec3(0.0f, 1.0f, 0.0f);
+ }
+
+ mVertices[0] = physx::PxVec3(-length, 0.0f, length);
+ mTexCoords[0][0].set(-uvDist, uvDist);
+
+ mVertices[1] = physx::PxVec3(0.0f, 0.0f, length);
+ mTexCoords[0][1].set(0.0f, uvDist);
+
+ mVertices[2] = physx::PxVec3(length, 0.0f, length);
+ mTexCoords[0][2].set(uvDist, uvDist);
+
+ mVertices[3] = physx::PxVec3(-length, 0.0f, 0.0f);
+ mTexCoords[0][3].set(-uvDist, 0.0f);
+
+ mVertices[4] = physx::PxVec3(0.0f, 0.0f, 0.0f);
+ mTexCoords[0][4].set(0.0f, 0.0f);
+
+ mVertices[5] = physx::PxVec3(length, 0.0f, 0.0f);
+ mTexCoords[0][5].set(uvDist, 0.0f);
+
+ mVertices[6] = physx::PxVec3(-length, 0.0f, -length);
+ mTexCoords[0][6].set(-uvDist, -uvDist);
+
+ mVertices[7] = physx::PxVec3(0.0f, 0.0f, -length);
+ mTexCoords[0][7].set(0.0f, -uvDist);
+
+ mVertices[8] = physx::PxVec3(length, 0.0f, -length);
+ mTexCoords[0][8].set(uvDist, -uvDist);
+
+
+ mIndices.resize(8 * 3);
+
+ mIndices[ 0] = 0;
+ mIndices[ 1] = 1;
+ mIndices[ 2] = 3;
+ mIndices[ 3] = 3;
+ mIndices[ 4] = 1;
+ mIndices[ 5] = 4;
+ mIndices[ 6] = 1;
+ mIndices[ 7] = 2;
+ mIndices[ 8] = 4;
+ mIndices[ 9] = 4;
+ mIndices[10] = 2;
+ mIndices[11] = 5;
+ mIndices[12] = 3;
+ mIndices[13] = 4;
+ mIndices[14] = 6;
+ mIndices[15] = 6;
+ mIndices[16] = 4;
+ mIndices[17] = 7;
+ mIndices[18] = 4;
+ mIndices[19] = 5;
+ mIndices[20] = 7;
+ mIndices[21] = 7;
+ mIndices[22] = 5;
+ mIndices[23] = 8;
+
+ mSubMeshes[0].materialName = mSubMeshes[0].originalMaterialName = materialName;
+ mSubMeshes[0].numIndices = 24;
+
+ updateBounds();
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::initFrom(nvidia::apex::ClothingPhysicalMesh& mesh, bool initCustomChannels)
+{
+ initSingleMesh();
+
+ mVertices.resize(mesh.getNumVertices());
+ mesh.getVertices(&mVertices[0], sizeof(physx::PxVec3));
+
+ if (mesh.isTetrahedralMesh())
+ {
+ // reset normals to prevent updateNormals from doing something stupid on a tet mesh!
+ mNormals.clear();
+ mNormals.resize(mVertices.size(), physx::PxVec3(0.0f, 0.0f, 0.0f));
+ // normals are not yet generated here (only available in ClothingAssetAuthoring)
+ //mesh.getVertexValue(RenderVertexSemantic::NORMAL, RenderDataFormat::FLOAT3, &mNormals[0], sizeof(physx::PxVec3));
+
+ /*unsigned int numIndices = mesh.getNumIndices();
+ assert(numIndices % 4 == 0);
+ mIndices.reserve(numIndices / 4 * 3); // same as / 4 * 12
+
+ std::vector<uint32_t> tempIndices;
+ tempIndices.resize(numIndices);
+ mesh.getIndices(&tempIndices[0], 0);
+
+ for (unsigned int i = 0; i < numIndices; i += 4)
+ {
+ unsigned int indices[4] =
+ {
+ tempIndices[i + 0],
+ tempIndices[i + 1],
+ tempIndices[i + 2],
+ tempIndices[i + 3],
+ };
+
+ mIndices.push_back(indices[0]);
+ mIndices.push_back(indices[1]);
+ mIndices.push_back(indices[2]);
+
+ mIndices.push_back(indices[0]);
+ mIndices.push_back(indices[3]);
+ mIndices.push_back(indices[1]);
+
+ mIndices.push_back(indices[0]);
+ mIndices.push_back(indices[2]);
+ mIndices.push_back(indices[3]);
+
+ mIndices.push_back(indices[1]);
+ mIndices.push_back(indices[3]);
+ mIndices.push_back(indices[2]);
+ }*/
+ }
+ //else
+ {
+ mIndices.resize(mesh.getNumIndices());
+ mesh.getIndices(&mIndices[0], 0);
+ }
+
+ complete(initCustomChannels);
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::initFrom(nvidia::apex::RenderMeshAssetAuthoring& mesh, bool initCustomChannels)
+{
+ PX_ASSERT(mesh.getPartCount() == 1);
+
+ initSingleMesh();
+
+ uint32_t numVertices = 0;
+ uint32_t numIndices = 0;
+ uint32_t numBonesPerVertex = 0;
+ for (uint32_t i = 0; i < mesh.getSubmeshCount(); i++)
+ {
+ numVertices += mesh.getSubmesh(i).getVertexCount(0);
+ numIndices += mesh.getSubmesh(i).getIndexCount(0);
+
+ const nvidia::apex::VertexFormat& format = mesh.getSubmesh(i).getVertexBuffer().getFormat();
+ const int boneIndexIndex = format.getBufferIndexFromID(format.getSemanticID(nvidia::apex::RenderVertexSemantic::BONE_INDEX));
+ uint32_t numBonesPerVertexSubmesh = 0;
+ switch(format.getBufferFormat((uint32_t)boneIndexIndex))
+ {
+ case nvidia::apex::RenderDataFormat::USHORT1:
+ numBonesPerVertexSubmesh = 1;
+ break;
+ case nvidia::apex::RenderDataFormat::USHORT2:
+ numBonesPerVertexSubmesh = 2;
+ break;
+ case nvidia::apex::RenderDataFormat::USHORT3:
+ numBonesPerVertexSubmesh = 3;
+ break;
+ case nvidia::apex::RenderDataFormat::USHORT4:
+ numBonesPerVertexSubmesh = 4;
+ break;
+ default:
+ numBonesPerVertexSubmesh = 0;
+ break;
+ }
+
+ // they are either identical or one of them is 0
+ PX_ASSERT(numBonesPerVertexSubmesh == numBonesPerVertex || (numBonesPerVertex * numBonesPerVertexSubmesh) == 0);
+ numBonesPerVertex = numBonesPerVertexSubmesh;
+ }
+
+ mVertices.clear();
+ mVertices.resize(numVertices);
+ mNormals.clear();
+ mNormals.resize(numVertices);
+ mIndices.clear();
+ mIndices.resize(numIndices);
+ mSubMeshes.resize(mesh.getSubmeshCount());
+
+ if (numBonesPerVertex > 0)
+ {
+ mBoneIndicesExternal.clear();
+ mBoneIndicesExternal.resize(numVertices * 4, 0);
+ mBoneWeights.clear();
+ mBoneWeights.resize(numVertices, physx::PxVec4(0.0f));
+ }
+ mMaxBoneIndexExternal = -1;
+
+ bool hasTexCoords = false;
+ for (uint32_t i = 0; i < mesh.getSubmeshCount(); i++)
+ {
+ const nvidia::apex::VertexFormat& format = mesh.getSubmesh(i).getVertexBuffer().getFormat();
+ const int texCoordIndex = format.getBufferIndexFromID(format.getSemanticID(nvidia::apex::RenderVertexSemantic::TEXCOORD0));
+ if (texCoordIndex != -1)
+ {
+ hasTexCoords = true;
+ }
+ }
+ if (hasTexCoords)
+ {
+ mTexCoords[0].clear();
+ mTexCoords[0].resize(numVertices);
+ }
+
+ bool hasTangents = false;
+ for (uint32_t i = 0; i < mesh.getSubmeshCount(); i++)
+ {
+ const nvidia::apex::VertexFormat& format = mesh.getSubmesh(i).getVertexBuffer().getFormat();
+ const int bitangentIndex = format.getBufferIndexFromID(format.getSemanticID(nvidia::apex::RenderVertexSemantic::TANGENT));
+ const int tangentIndex = format.getBufferIndexFromID(format.getSemanticID(nvidia::apex::RenderVertexSemantic::BINORMAL));
+ if (tangentIndex != -1 && bitangentIndex != -1)
+ {
+ hasTangents = true;
+ }
+ }
+ if (hasTangents)
+ {
+ mTangents.clear();
+ mTangents.resize(numVertices);
+ mBitangents.clear();
+ mBitangents.resize(numVertices);
+ }
+
+ mBounds = physx::PxBounds3::empty();
+ uint32_t vertexOffset = 0;
+ uint32_t indexOffset = 0;
+ for (uint32_t submeshIndex = 0; submeshIndex < mesh.getSubmeshCount(); submeshIndex++)
+ {
+ const nvidia::VertexBuffer& vb = mesh.getSubmesh(submeshIndex).getVertexBuffer();
+ const nvidia::VertexFormat& vf = vb.getFormat();
+ int bufferIndex = 0;
+ nvidia::RenderDataFormat::Enum format;
+
+ const uint32_t vertexCount = mesh.getSubmesh(submeshIndex).getVertexCount(0);
+
+ {
+ bufferIndex = vf.getBufferIndexFromID(vf.getSemanticID(nvidia::apex::RenderVertexSemantic::POSITION));
+ const physx::PxVec3* positions = (const physx::PxVec3*)vb.getBufferAndFormat(format, (uint32_t)bufferIndex);
+ if (!positions || format != nvidia::RenderDataFormat::FLOAT3)
+ {
+ PX_ALWAYS_ASSERT();
+ return;
+ }
+
+ for (uint32_t i = 0; i < vertexCount; i++)
+ {
+ mVertices[i + vertexOffset] = positions[i];
+ mBounds.include(positions[i]);
+ }
+ }
+
+ {
+ bufferIndex = vf.getBufferIndexFromID(vf.getSemanticID(nvidia::apex::RenderVertexSemantic::NORMAL));
+ const physx::PxVec3* normals = (const physx::PxVec3*)vb.getBufferAndFormat(format, (uint32_t)bufferIndex);
+ if (!normals || format != nvidia::RenderDataFormat::FLOAT3)
+ {
+ PX_ALWAYS_ASSERT();
+ }
+ else
+ {
+ for (uint32_t i = 0; i < vertexCount; i++)
+ {
+ mNormals[i + vertexOffset] = normals[i];
+ }
+ }
+ }
+
+ if (numBonesPerVertex > 0)
+ {
+ bufferIndex = vf.getBufferIndexFromID(vf.getSemanticID(nvidia::apex::RenderVertexSemantic::BONE_WEIGHT));
+ const float* boneWeights = (const float*)vb.getBufferAndFormat(format, (uint32_t)bufferIndex);
+ bufferIndex = vf.getBufferIndexFromID(vf.getSemanticID(nvidia::apex::RenderVertexSemantic::BONE_INDEX));
+ const short* boneIndices = (const short*)vb.getBufferAndFormat(format, (uint32_t)bufferIndex);
+ if (!boneWeights || !boneIndices)
+ {
+ PX_ALWAYS_ASSERT();
+ }
+ else
+ {
+ for (uint32_t i = 0; i < vertexCount; i++)
+ {
+ for (uint32_t j = 0; j < numBonesPerVertex; j++)
+ {
+ mBoneWeights[i + vertexOffset][j] = boneWeights[i * numBonesPerVertex + j];
+ mBoneIndicesExternal[(i + vertexOffset) * 4 + j] = (unsigned short)boneIndices[i * numBonesPerVertex + j];
+
+ mMaxBoneIndexExternal = std::max<int>(mMaxBoneIndexExternal, boneIndices[i * numBonesPerVertex + j]);
+ }
+ }
+ }
+ }
+
+ if (hasTexCoords)
+ {
+ bufferIndex = vf.getBufferIndexFromID(vf.getSemanticID(nvidia::apex::RenderVertexSemantic::TEXCOORD0));
+ const nvidia::VertexUV* texCoords = (const nvidia::VertexUV*)vb.getBufferAndFormat(format, (uint32_t)bufferIndex);
+ if (texCoords == NULL || format != nvidia::apex::RenderDataFormat::FLOAT2)
+ {
+ PX_ALWAYS_ASSERT();
+ }
+ else
+ {
+ for (uint32_t i = 0; i < vertexCount; i++)
+ {
+ mTexCoords[0][i + vertexOffset] = texCoords[i];
+ }
+ }
+ }
+
+ if (hasTangents)
+ {
+ bufferIndex = vf.getBufferIndexFromID(vf.getSemanticID(nvidia::apex::RenderVertexSemantic::TANGENT));
+ const physx::PxVec3* tangents = (const physx::PxVec3*)vb.getBufferAndFormat(format, (uint32_t)bufferIndex);
+ if (tangents == NULL || format != nvidia::apex::RenderDataFormat::FLOAT3)
+ {
+ PX_ALWAYS_ASSERT();
+ }
+ else
+ {
+ for (uint32_t i = 0; i < vertexCount; i++)
+ {
+ mTangents[i + vertexOffset] = tangents[i];
+ }
+ }
+
+ bufferIndex = vf.getBufferIndexFromID(vf.getSemanticID(nvidia::apex::RenderVertexSemantic::BINORMAL));
+ const physx::PxVec3* bitangents = (const physx::PxVec3*)vb.getBufferAndFormat(format, (uint32_t)bufferIndex);
+ if (bitangents == NULL || format != nvidia::apex::RenderDataFormat::FLOAT3)
+ {
+ PX_ALWAYS_ASSERT();
+ }
+ else
+ {
+ for (uint32_t i = 0; i < vertexCount; i++)
+ {
+ mBitangents[i + vertexOffset] = bitangents[i];
+ }
+ }
+ }
+
+ bufferIndex = vf.getBufferIndexFromID(vf.getID("MAX_DISTANCE"));
+ if (bufferIndex != -1)
+ {
+ const float* maxDistances = (const float*)vb.getBufferAndFormat(format, (uint32_t)bufferIndex);
+
+ const float scale = 1.0f / (mBounds.maximum - mBounds.minimum).magnitude();
+
+ if (maxDistances != NULL && format == nvidia::RenderDataFormat::FLOAT1)
+ {
+ if (mPaintChannels[PC_MAX_DISTANCE].size() != mVertices.size())
+ {
+ mPaintChannels[PC_MAX_DISTANCE].resize(mVertices.size());
+ }
+
+ for (size_t i = 0; i < vertexCount; i++)
+ {
+ mPaintChannels[PC_MAX_DISTANCE][i + vertexOffset].paintValueF32 = maxDistances[i] * scale;
+ }
+ }
+ }
+
+ bufferIndex = vf.getBufferIndexFromID(vf.getID("USED_FOR_PHYSICS"));
+ if (bufferIndex != -1)
+ {
+ const unsigned char* usedForPhysics = (const unsigned char*)vb.getBufferAndFormat(format, (uint32_t)bufferIndex);
+
+ if (usedForPhysics != NULL && format == nvidia::apex::RenderDataFormat::UBYTE1)
+ {
+ if (mPaintChannels[PC_LATCH_TO_NEAREST_SLAVE].size() != mVertices.size())
+ {
+ mPaintChannels[PC_LATCH_TO_NEAREST_SLAVE].resize(mVertices.size());
+ }
+
+ for (size_t i = 0; i < vertexCount; i++)
+ {
+ mPaintChannels[PC_LATCH_TO_NEAREST_SLAVE][i + vertexOffset].paintValueU32 = usedForPhysics[i] > 0 ? 0 : 0xffffffff;
+ }
+ }
+ }
+
+ const uint32_t indexCount = mesh.getSubmesh(submeshIndex).getIndexCount(0);
+ const uint32_t* indices = mesh.getSubmesh(submeshIndex).getIndexBuffer(0);
+ for (uint32_t i = 0; i < indexCount; i++)
+ {
+ mIndices[i + indexOffset] = indices[i] + vertexOffset;
+ }
+
+ {
+ mSubMeshes[submeshIndex].init();
+ char buf[64];
+ physx::shdfnd::snprintf(buf, 64, "Submesh %d", submeshIndex);
+ mSubMeshes[submeshIndex].name = buf;
+ mSubMeshes[submeshIndex].materialName = mesh.getMaterialName(submeshIndex);
+
+ mSubMeshes[submeshIndex].firstIndex = indexOffset;
+ mSubMeshes[submeshIndex].numIndices = indexCount;
+ }
+
+ vertexOffset += mesh.getSubmesh(submeshIndex).getVertexCount(0);
+ indexOffset += indexCount;
+ }
+
+ complete(initCustomChannels);
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::applyMorphDisplacements(const std::vector<physx::PxVec3>& displacements)
+{
+ if (mVertices.empty() && mParent != NULL)
+ {
+ if (mParent->mVertices.size() == displacements.size())
+ {
+ mVertices.resize(displacements.size());
+
+ for (size_t i = 0; i < mVertices.size(); i++)
+ {
+ mVertices[i] = mParent->mVertices[i] + displacements[i];
+ }
+
+ mUseGpuSkinning = false;
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::drawPainting(PaintChannelType channelType, bool skinned, nvidia::apex::RenderDebugInterface* batcher)
+{
+ if (batcher == NULL || channelType == PC_NUM_CHANNELS)
+ {
+ return;
+ }
+
+ std::vector<physx::PxVec3>& vertices = skinned ? mSkinnedVertices : (mParent == NULL ? mVertices : mParent->mVertices);
+ std::vector<uint32_t>& indices = mParent == NULL ? mIndices : mParent->mIndices;
+
+ physx::PxBounds3 bounds;
+ getBounds(bounds);
+ updateSubmeshInfo();
+
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ if (mSubMeshes[i].selected)
+ {
+ const uint32_t lastIndex = mSubMeshes[i].firstIndex + mSubMeshes[i].numIndices;
+ for (uint32_t j = mSubMeshes[i].firstIndex; j < lastIndex; j += 3)
+ {
+ uint32_t colors[3] =
+ {
+ mPaintChannels[channelType][indices[j + 0]].color,
+ mPaintChannels[channelType][indices[j + 1]].color,
+ mPaintChannels[channelType][indices[j + 2]].color,
+ };
+ RENDER_DEBUG_IFACE(batcher)->debugGradientTri(vertices[indices[j + 0]], vertices[indices[j + 1]], vertices[indices[j + 2]], colors[0], colors[1], colors[2]);
+ }
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::drawVertices(PaintChannelType channelType, float maxDistanceScaling, float collisionDistanceScaling, float pointScaling,
+ float vmin, float vmax, nvidia::apex::RenderDebugInterface* batcher)
+{
+ if (batcher == NULL || channelType == PC_NUM_CHANNELS)
+ {
+ return;
+ }
+
+ std::vector<physx::PxVec3>& vertices = mParent == NULL ? mVertices : mParent->mVertices;
+ std::vector<physx::PxVec3>& normals = mParent == NULL ? mNormals : mParent->mNormals;
+
+ physx::PxBounds3 bounds;
+ getBounds(bounds);
+
+ updateSubmeshInfo();
+
+ const PaintedVertex* channel = channelType != PC_NUM_CHANNELS ? &mPaintChannels[channelType][0] : NULL;
+ PX_ASSERT(channelType >= 0);
+ PX_ASSERT(channelType <= PC_NUM_CHANNELS);
+
+ const bool needsNoLines = channelType == PC_LATCH_TO_NEAREST_SLAVE || channelType == PC_LATCH_TO_NEAREST_MASTER;
+
+ for (size_t i = 0; i < vertices.size(); i++)
+ {
+ if (!mActiveSubmeshVertices[i])
+ {
+ continue;
+ }
+
+ unsigned int color;
+ color = channel != NULL ? channel[i].color : 0;
+
+ // swap red and blue
+ RENDER_DEBUG_IFACE(batcher)->setCurrentColor(color);
+ RENDER_DEBUG_IFACE(batcher)->debugPoint(vertices[i], pointScaling);
+
+ if (needsNoLines)
+ {
+ continue;
+ }
+
+ if (channel[i].paintValueF32 < vmin || channel[i].paintValueF32 > vmax)
+ {
+ continue;
+ }
+
+ const float channelPaintValue = channel[i].paintValueF32;
+ float len = (channel != NULL ? channelPaintValue : 1.0f) * maxDistanceScaling;
+ if (channelType == PC_MAX_DISTANCE && len < 0.0f)
+ {
+ len = 0.0f;
+ }
+ else if (channelType == PC_COLLISION_DISTANCE)
+ {
+ float scale = collisionDistanceScaling;
+ if (scale == 0.0f)
+ {
+ scale = mPaintChannels[PC_MAX_DISTANCE][i].paintValueF32 * maxDistanceScaling;
+ }
+ PX_ASSERT(channelPaintValue >= vmin && channelPaintValue <= vmax);
+ len = channelPaintValue * scale;
+
+ // positive collisionDistance means moving inwards against the mesh normal
+ len *= -1;
+ }
+ if (len != 0.0f)
+ {
+ physx::PxVec3 target = vertices[i] + normals[i] * len;
+ RENDER_DEBUG_IFACE(batcher)->debugLine(vertices[i], target);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::drawVertices(size_t boneNr, float minWeight, float pointScaling, nvidia::apex::RenderDebugInterface* batcher) const
+{
+ if ((mBoneIndicesExternal.size() != mVertices.size() * 4) || batcher == NULL)
+ {
+ return;
+ }
+
+ const size_t numVertices = mVertices.size();
+ for (size_t i = 0; i < numVertices; i++)
+ {
+ bool found = false;
+ float weight = 0;
+ for (int j = 0; j < 4; j++)
+ {
+ weight = mBoneWeights[i][j];
+ if (mBoneIndicesExternal[i * 4 + j] == boneNr && weight > minWeight)
+ {
+ found = true;
+ }
+ }
+ if (found)
+ {
+ PX_ASSERT(weight >= 0.0f);
+ PX_ASSERT(weight <= 1.0f);
+
+ uint8_t gray = (uint8_t)(255 * weight);
+ RENDER_DEBUG_IFACE(batcher)->setCurrentColor(uint32_t(gray << 16 | gray << 8 | gray));
+ RENDER_DEBUG_IFACE(batcher)->debugPoint(mVertices[i], pointScaling);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::drawNormals(float normalScale, bool activeVerticesOnly, nvidia::apex::RenderDebugInterface* batcher) const
+{
+ if ((normalScale <= 0.0f) || batcher == NULL)
+ {
+ return;
+ }
+
+
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ if (activeVerticesOnly && !mActiveSubmeshVertices[i])
+ {
+ continue;
+ }
+
+ RENDER_DEBUG_IFACE(batcher)->setCurrentColor(0x00ff0000); // red
+ physx::PxVec3 otherEnd = mVertices[i];
+ otherEnd += mNormals[i] * normalScale;
+ RENDER_DEBUG_IFACE(batcher)->debugLine(mVertices[i], otherEnd);
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::drawTangents(float tangentScale, nvidia::apex::RenderDebugInterface* batcher) const
+{
+ if (tangentScale <= 0.0f || batcher == NULL || mTangents.size() != mVertices.size() || mBitangents.size() != mVertices.size())
+ {
+ return;
+ }
+
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ RENDER_DEBUG_IFACE(batcher)->setCurrentColor(0x0000ff00); // green
+ physx::PxVec3 otherEnd = mVertices[i] + mTangents[i] * tangentScale;
+ RENDER_DEBUG_IFACE(batcher)->debugLine(mVertices[i], otherEnd);
+
+ RENDER_DEBUG_IFACE(batcher)->setCurrentColor(0x000000ff); // blue
+ otherEnd = mVertices[i] + mBitangents[i] * tangentScale;
+ RENDER_DEBUG_IFACE(batcher)->debugLine(mVertices[i], otherEnd);
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::drawMaxDistancePartitions(float paintingScale, const float* partitions, size_t numPartitions, nvidia::apex::RenderDebugInterface* batcher)
+{
+ if (batcher == NULL)
+ {
+ return;
+ }
+
+ hasRandomColors(numPartitions);
+
+ RENDER_DEBUG_IFACE(batcher)->pushRenderState();
+ RENDER_DEBUG_IFACE(batcher)->addToCurrentState(RENDER_DEBUG::DebugRenderState::SolidShaded);
+
+ for (uint32_t s = 0; s < mSubMeshes.size(); s++)
+ {
+ if (!mSubMeshes[s].selected)
+ {
+ continue;
+ }
+
+ const uint32_t start = mSubMeshes[s].firstIndex;
+ const uint32_t end = start + mSubMeshes[s].numIndices;
+
+ using RENDER_DEBUG::DebugColors;
+ const uint32_t colorDarkGray = RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::DarkGray);
+ const uint32_t colorWhite = RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::White);
+
+ for (uint32_t i = start; i < end; i += 3)
+ {
+ float maxMaxDistance = mPaintChannels[PC_MAX_DISTANCE][mIndices[i]].paintValueF32;
+ maxMaxDistance = physx::PxMax(maxMaxDistance, mPaintChannels[PC_MAX_DISTANCE][mIndices[i + 1]].paintValueF32);
+ maxMaxDistance = physx::PxMax(maxMaxDistance, mPaintChannels[PC_MAX_DISTANCE][mIndices[i + 2]].paintValueF32);
+ maxMaxDistance *= paintingScale;
+
+ uint32_t color = colorDarkGray;
+
+ if (maxMaxDistance >= 0)
+ {
+ color = colorWhite;
+ for (uint32_t j = 0; j < numPartitions; j++)
+ {
+ if (maxMaxDistance < partitions[j])
+ {
+ color = mRandomColors[j];
+ break;
+ }
+ }
+ }
+
+ RENDER_DEBUG_IFACE(batcher)->setCurrentColor(color);
+
+ physx::PxVec3 normal = (mVertices[mIndices[i + 2]] - mVertices[mIndices[i]]).cross(mVertices[mIndices[i + 1]] - mVertices[mIndices[i]]);
+ normal.normalize();
+ RENDER_DEBUG_IFACE(batcher)->debugTriNormals(
+ mVertices[mIndices[i + 0]], mVertices[mIndices[i + 2]], mVertices[mIndices[i + 1]],
+ normal, normal, normal);
+
+ normal = -normal;
+
+ RENDER_DEBUG_IFACE(batcher)->debugTriNormals(
+ mVertices[mIndices[i + 0]], mVertices[mIndices[i + 1]], mVertices[mIndices[i + 2]],
+ normal, normal, normal);
+ }
+ }
+
+ RENDER_DEBUG_IFACE(batcher)->popRenderState();
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::drawTetrahedrons(bool wireframe, float scale, nvidia::apex::RenderDebugInterface* batcher)
+{
+ if (RENDER_DEBUG_IFACE(batcher) == NULL)
+ {
+ return;
+ }
+
+ const uint32_t numIndices = (uint32_t)(mIndices.size());
+ if (numIndices == 0)
+ {
+ return;
+ }
+
+ PX_ASSERT(numIndices % 4 == 0);
+
+ PX_ASSERT(scale < 1.0f);
+ PX_ASSERT(scale >= 0.0f);
+
+ if (scale > 1.0f)
+ {
+ scale = 1.0f;
+ }
+
+ const physx::PxVec3* positions = &mVertices[0];
+
+ RENDER_DEBUG_IFACE(batcher)->pushRenderState();
+ RENDER_DEBUG_IFACE(batcher)->setCurrentColor(RENDER_DEBUG_IFACE(batcher)->getDebugColor(RENDER_DEBUG::DebugColors::DarkGreen));
+ RENDER_DEBUG_IFACE(batcher)->addToCurrentState(RENDER_DEBUG::DebugRenderState::SolidShaded);
+
+ const uint32_t edgeIndices[6][2] = {{0, 1}, {0, 2}, {0, 3}, {1, 2}, {1, 3}, {2, 3}};
+ const uint32_t sides[4][3] = {{2, 1, 0}, {0, 1, 3}, {1, 2, 3}, {2, 0, 3}};
+
+ for (uint32_t i = 0; i < numIndices; i += 4)
+ {
+ physx::PxVec3 vecs[4];
+ physx::PxVec3 center(0.0f);
+ for (uint32_t j = 0; j < 4; j++)
+ {
+ vecs[j] = positions[mIndices[i + j]];
+ center += vecs[j];
+ }
+ center *= 0.25f;
+
+ for (uint32_t j = 0; j < 4; j++)
+ {
+ vecs[j] = vecs[j] * scale + center * (1.0f - scale);
+ }
+
+ if (wireframe)
+ {
+ for (uint32_t j = 0; j < 6; j++)
+ {
+ RENDER_DEBUG_IFACE(batcher)->debugLine(vecs[edgeIndices[j][0]], vecs[edgeIndices[j][1]]);
+ }
+ }
+ else
+ {
+ for (uint32_t j = 0; j < 4; j++)
+ {
+ RENDER_DEBUG_IFACE(batcher)->debugTri(vecs[sides[j][0]], vecs[sides[j][1]], vecs[sides[j][2]]);
+ }
+ }
+ }
+
+ RENDER_DEBUG_IFACE(batcher)->popRenderState();
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::updateRenderResources(bool rewriteBuffers, nvidia::apex::UserRenderResourceManager& rrm, void* userRenderData)
+{
+ updateRenderResourcesInternal(rewriteBuffers, rrm, userRenderData, true);
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::updateRenderResourcesInternal(bool rewriteBuffers, nvidia::apex::UserRenderResourceManager& rrm, void* userRenderData, bool createResource)
+{
+ if (mParent != NULL && mUseGpuSkinning)
+ {
+ mParent->updateRenderResourcesInternal(rewriteBuffers, rrm, userRenderData, false);
+ }
+
+ const uint32_t numIndices = mParent != NULL ? (uint32_t)(mParent->mIndices.size()) : (uint32_t)(mIndices.size());
+ const uint32_t numVertices = mParent != NULL ? (uint32_t)(mParent->mVertices.size()) : (uint32_t)(mVertices.size());
+
+ bool resourceDirty = false;
+
+ bool boneBufferSwitch = false;
+ if (skinningMatricesChanged && mUseGpuSkinning)
+ {
+ if (mSkinningMatrices.empty() && mBoneBuffer != NULL)
+ {
+ rrm.releaseBoneBuffer(*mBoneBuffer);
+ mBoneBuffer = NULL;
+ boneBufferSwitch = true;
+ }
+ if (!mSkinningMatrices.empty() && mBoneBuffer == NULL && mUseGpuSkinning)
+ {
+ nvidia::apex::UserRenderBoneBufferDesc bufferDesc;
+ bufferDesc.buffersRequest[nvidia::RenderBoneSemantic::POSE] = nvidia::RenderDataFormat::FLOAT4x4;
+ bufferDesc.maxBones = (uint32_t)(mSkinningMatrices.size());
+ mBoneBuffer = rrm.createBoneBuffer(bufferDesc);
+ boneBufferSwitch = true;
+ }
+
+ if (!mSkinningMatrices.empty() && mBoneBuffer != NULL)
+ {
+ nvidia::apex::RenderBoneBufferData boneWriteData;
+ boneWriteData.setSemanticData(nvidia::RenderBoneSemantic::POSE, &mSkinningMatrices[0], sizeof(physx::PxMat44), nvidia::RenderDataFormat::FLOAT4x4);
+ mBoneBuffer->writeBuffer(boneWriteData, 0, (uint32_t)mSkinningMatrices.size());
+ }
+
+ skinningMatricesChanged = false;
+ }
+
+ if (numVertices == 0 || vertexCountChanged || textureUvOriginChanged)
+ {
+ if (mDynamicVertexBuffer != NULL)
+ {
+ rrm.releaseVertexBuffer(*mDynamicVertexBuffer);
+ vertexValuesChangedDynamic = true;
+ }
+ if (mStaticVertexBuffer != NULL)
+ {
+ rrm.releaseVertexBuffer(*mStaticVertexBuffer);
+ vertexValuesChangedStatic = true;
+ }
+ mDynamicVertexBuffer = mStaticVertexBuffer = NULL;
+ textureUvOriginChanged = false;
+ resourceDirty = true;
+ }
+
+ if (mDynamicVertexBuffer == NULL && numVertices > 0)
+ {
+ nvidia::apex::UserRenderVertexBufferDesc bufferDesc;
+ bufferDesc.uvOrigin = mTextureUVOrigin;
+ bufferDesc.hint = nvidia::apex::RenderBufferHint::DYNAMIC;
+
+ bufferDesc.maxVerts = mParent != NULL ? (uint32_t)(mParent->mVertices.size()) : (uint32_t)(mVertices.size());
+
+ bufferDesc.buffersRequest[nvidia::apex::RenderVertexSemantic::POSITION] = nvidia::apex::RenderDataFormat::FLOAT3;
+ bufferDesc.buffersRequest[nvidia::apex::RenderVertexSemantic::NORMAL] = nvidia::apex::RenderDataFormat::FLOAT3;
+
+ if (mParent == NULL || !mUseGpuSkinning)
+ {
+ mDynamicVertexBuffer = rrm.createVertexBuffer(bufferDesc);
+ vertexValuesChangedDynamic = true;
+ }
+ }
+
+ if (mStaticVertexBuffer == NULL && numVertices > 0)
+ {
+ nvidia::apex::UserRenderVertexBufferDesc bufferDesc;
+ bufferDesc.uvOrigin = mTextureUVOrigin;
+ bufferDesc.hint = nvidia::apex::RenderBufferHint::STATIC;
+
+ bufferDesc.maxVerts = mParent != NULL ? (uint32_t)(mParent->mVertices.size()) : (uint32_t)(mVertices.size());
+
+ unsigned int numSemantics = 0;
+
+ if (mParent == NULL || !mUseGpuSkinning)
+ {
+ for (uint32_t i = 0; i < NUM_TEXCOORDS; i++)
+ {
+ const uint32_t numTexCoords = mParent != NULL ? (uint32_t)(mParent->mTexCoords[i].size()) : (uint32_t)mTexCoords[i].size();
+ if (numTexCoords == numVertices)
+ {
+ bufferDesc.buffersRequest[nvidia::apex::RenderVertexSemantic::TEXCOORD0 + i] = nvidia::apex::RenderDataFormat::FLOAT2;
+ numSemantics++;
+ }
+ }
+
+ const uint32_t numBoneIndices = mParent != NULL ? (uint32_t)(mParent->mBoneIndicesInternal.size()) : (uint32_t)(mBoneIndicesInternal.size());
+ if (numBoneIndices == numVertices * 4 && mUseGpuSkinning)
+ {
+ PX_ASSERT(mParent == NULL);
+ bufferDesc.buffersRequest[nvidia::apex::RenderVertexSemantic::BONE_INDEX] = nvidia::apex::RenderDataFormat::USHORT4;
+ bufferDesc.buffersRequest[nvidia::apex::RenderVertexSemantic::BONE_WEIGHT] = nvidia::apex::RenderDataFormat::FLOAT4;
+ numSemantics += 2;
+ }
+ }
+ else
+ {
+ PX_ASSERT(mParent->mStaticVertexBuffer != NULL);
+ }
+
+ if ((mParent == NULL || !mUseGpuSkinning) && numSemantics > 0)
+ {
+ mStaticVertexBuffer = rrm.createVertexBuffer(bufferDesc);
+ vertexValuesChangedStatic = true;
+ }
+ }
+
+
+ if (mDynamicVertexBuffer && (vertexValuesChangedDynamic || rewriteBuffers))
+ {
+ nvidia::apex::RenderVertexBufferData writeData;
+
+ const uint32_t numVertices = mParent != NULL ? (uint32_t)(mParent->mVertices.size()) : (uint32_t)(mVertices.size());
+ if (mSkinnedVertices.size() == numVertices && !mUseGpuSkinning)
+ {
+ PX_ASSERT(mSkinnedNormals.size() == mSkinnedVertices.size());
+ writeData.setSemanticData(nvidia::apex::RenderVertexSemantic::POSITION, &mSkinnedVertices[0], sizeof(physx::PxVec3), nvidia::apex::RenderDataFormat::FLOAT3);
+ writeData.setSemanticData(nvidia::apex::RenderVertexSemantic::NORMAL, &mSkinnedNormals[0], sizeof(physx::PxVec3), nvidia::apex::RenderDataFormat::FLOAT3);
+ }
+ else
+ {
+ std::vector<physx::PxVec3>& vertices = mParent != NULL ? mParent->mVertices : mVertices;
+ std::vector<physx::PxVec3>& normals = mParent != NULL ? mParent->mNormals : mNormals;
+ PX_ASSERT(vertices.size() == numVertices);
+ PX_ASSERT(normals.size() == numVertices);
+ writeData.setSemanticData(nvidia::apex::RenderVertexSemantic::POSITION, &vertices[0], sizeof(physx::PxVec3), nvidia::apex::RenderDataFormat::FLOAT3);
+ writeData.setSemanticData(nvidia::apex::RenderVertexSemantic::NORMAL, &normals[0], sizeof(physx::PxVec3), nvidia::apex::RenderDataFormat::FLOAT3);
+ }
+ mDynamicVertexBuffer->writeBuffer(writeData, 0, numVertices);
+ vertexValuesChangedDynamic = false;
+ }
+
+ if (mStaticVertexBuffer != NULL && vertexValuesChangedStatic)
+ {
+ nvidia::apex::RenderVertexBufferData writeData;
+
+ if (mParent == NULL || !mUseGpuSkinning)
+ {
+ nvidia::apex::RenderVertexSemantic::Enum semantics[4] =
+ {
+ nvidia::apex::RenderVertexSemantic::TEXCOORD0,
+ nvidia::apex::RenderVertexSemantic::TEXCOORD1,
+ nvidia::apex::RenderVertexSemantic::TEXCOORD2,
+ nvidia::apex::RenderVertexSemantic::TEXCOORD3,
+ };
+ for (uint32_t i = 0; i < NUM_TEXCOORDS; i++)
+ {
+ const uint32_t numTexCoords = mParent != NULL ? (uint32_t)(mParent->mTexCoords[i].size()) : (uint32_t)(mTexCoords[i].size());
+ if (numTexCoords == numVertices)
+ {
+ writeData.setSemanticData(semantics[i], mParent != NULL ? &mParent->mTexCoords[i][0] : &mTexCoords[i][0], sizeof(nvidia::apex::VertexUV), nvidia::apex::RenderDataFormat::FLOAT2);
+ }
+ }
+
+ const uint32_t numBoneIndices = mParent != NULL ? (uint32_t)(mParent->mBoneIndicesInternal.size()) : (uint32_t)(mBoneIndicesInternal.size());
+ if (numBoneIndices == numVertices * 4 && mUseGpuSkinning)
+ {
+ PX_ASSERT(mParent == NULL);
+ writeData.setSemanticData(nvidia::apex::RenderVertexSemantic::BONE_INDEX, &mBoneIndicesInternal[0], sizeof(uint16_t) * 4, nvidia::apex::RenderDataFormat::USHORT4);
+ writeData.setSemanticData(nvidia::apex::RenderVertexSemantic::BONE_WEIGHT, &mBoneWeights[0], sizeof(physx::PxVec4), nvidia::apex::RenderDataFormat::FLOAT4);
+ }
+ }
+ mStaticVertexBuffer->writeBuffer(writeData, 0, numVertices);
+ vertexValuesChangedStatic = false;
+ }
+
+ if ( (mIndexBuffer != NULL && numIndices == 0) || vertexCountChanged)
+ {
+ rrm.releaseIndexBuffer(*mIndexBuffer);
+ mIndexBuffer = NULL;
+ indicesChanged |= vertexCountChanged;
+ }
+
+ if (mIndexBuffer == NULL && (mParent == NULL || !mUseGpuSkinning) && numIndices > 0)
+ {
+ nvidia::apex::UserRenderIndexBufferDesc bufferDesc;
+ bufferDesc.format = nvidia::apex::RenderDataFormat::UINT1;
+ bufferDesc.maxIndices = numIndices;
+
+ mIndexBuffer = rrm.createIndexBuffer(bufferDesc);
+ indicesChanged = mIndexBuffer != NULL;
+ }
+
+ if (mIndexBuffer != NULL && indicesChanged)
+ {
+ mIndexBuffer->writeBuffer(mParent != NULL ? &mParent->mIndices[0] : &mIndices[0], sizeof(uint32_t), 0, mParent != NULL ? (uint32_t)(mParent->mIndices.size()) : (uint32_t)(mIndices.size()));
+ indicesChanged = false;
+ }
+
+ if (boneBufferSwitch || numIndices == 0 || numVertices == 0 || vertexCountChanged || oneCullModeChanged)
+ {
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ if (mSubMeshes[i].mRenderResource != NULL)
+ {
+ rrm.releaseResource(*mSubMeshes[i].mRenderResource);
+ mSubMeshes[i].mRenderResource = NULL;
+ }
+ }
+ vertexCountChanged = false;
+ oneCullModeChanged = false;
+ }
+
+
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ bool show = mSubMeshes[i].show;
+ if (mParent != NULL)
+ {
+ PX_ASSERT(mParent->mSubMeshes.size() == mSubMeshes.size());
+ show &= mParent->mSubMeshes[i].show;
+ }
+
+ if (mSubMeshes[i].mRenderResource != NULL && (!show || mSubMeshes[i].resourceNeedsUpdate || !createResource || resourceDirty))
+ {
+ rrm.releaseResource(*mSubMeshes[i].mRenderResource);
+ mSubMeshes[i].mRenderResource = NULL;
+ mSubMeshes[i].resourceNeedsUpdate = false;
+ }
+
+ if (mSubMeshes[i].mRenderResource == NULL && show && numIndices > 0 && numVertices > 0 && createResource)
+ {
+ nvidia::apex::UserRenderVertexBuffer* vertexBuffers[2] = { NULL , NULL };
+ uint32_t numVertexBuffers = 0;
+
+ nvidia::apex::UserRenderResourceDesc resourceDesc;
+ if (mParent != NULL && mParent->mStaticVertexBuffer != NULL)
+ {
+ vertexBuffers[numVertexBuffers++] = mParent->mStaticVertexBuffer;
+ }
+ else if (mStaticVertexBuffer)
+ {
+ vertexBuffers[numVertexBuffers++] = mStaticVertexBuffer;
+ }
+
+ if (mUseGpuSkinning && mParent != NULL && mParent->mDynamicVertexBuffer != NULL)
+ {
+ vertexBuffers[numVertexBuffers++] = mParent->mDynamicVertexBuffer;
+ }
+ else
+ {
+ PX_ASSERT(mDynamicVertexBuffer != NULL);
+ vertexBuffers[numVertexBuffers++] = mDynamicVertexBuffer;
+ }
+
+
+ PX_ASSERT(numVertexBuffers > 0);
+
+ resourceDesc.vertexBuffers = vertexBuffers;
+ resourceDesc.numVertexBuffers = numVertexBuffers;
+
+ resourceDesc.indexBuffer = (mParent != NULL && mParent->mIndexBuffer != NULL) ? mParent->mIndexBuffer : mIndexBuffer;
+ PX_ASSERT(resourceDesc.indexBuffer != NULL);
+ resourceDesc.boneBuffer = mBoneBuffer;
+
+ resourceDesc.firstVertex = 0;
+ resourceDesc.numVerts = mParent != NULL ? (uint32_t)(mParent->mVertices.size()) : (uint32_t)(mVertices.size());
+
+ resourceDesc.firstIndex = mSubMeshes[i].firstIndex;
+ resourceDesc.numIndices = mSubMeshes[i].numIndices;
+
+ resourceDesc.firstBone = 0;
+ resourceDesc.numBones = mBoneBuffer != NULL ? (uint32_t)(mSkinningMatrices.size()) : 0;
+
+ resourceDesc.primitives = nvidia::apex::RenderPrimitiveType::TRIANGLES;
+
+ resourceDesc.cullMode = mSubMeshes[i].cullMode;
+
+ resourceDesc.material = mSubMeshes[i].materialResource;
+
+ resourceDesc.userRenderData = userRenderData;
+
+ mSubMeshes[i].mRenderResource = rrm.createResource(resourceDesc);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::dispatchRenderResources(nvidia::UserRenderer& r, const physx::PxMat44& currentPose)
+{
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ if (mSubMeshes[i].mRenderResource != NULL)
+ {
+ nvidia::RenderContext context;
+ context.local2world = currentPose;
+ context.renderResource = mSubMeshes[i].mRenderResource;
+
+ r.renderResource(context);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::updateRenderer(bool rewriteBuffers, bool overrideMaterial, bool sharedOnly /* = false */)
+{
+ PX_UNUSED(rewriteBuffers);
+ PX_UNUSED(overrideMaterial);
+ PX_UNUSED(sharedOnly);
+
+#ifdef USE_SAMPLE_RENDERER
+ if (mParent != NULL)
+ {
+ if (mParent->mRenderer == NULL)
+ {
+ mParent->mRenderer = mRenderer;
+ }
+
+ mParent->updateRenderer(rewriteBuffers, true);
+ }
+
+ if (mRenderer == NULL)
+ {
+ return;
+ }
+
+ const size_t numVertices = mParent != NULL ? mParent->mVertices.size() : mVertices.size();
+ const size_t numIndices = mParent != NULL ? mParent->mIndices.size() : mIndices.size();
+
+ if (numVertices == 0 || numIndices == 0)
+ {
+ return;
+ }
+
+ bool vertexBufferDynamicDirty = vertexValuesChangedDynamic || rewriteBuffers;
+ vertexValuesChangedDynamic = false;
+ if (mRendererVertexBufferDynamic == NULL && (mVertices.size() > 0 || mSkinnedVertices.size() > 0))
+ {
+ //PH: Static or dynamic, depending on whether you're in the parent or not.
+ // The child might have mVertices.size() == 0 when it can do gpu skinning, hence no buffer needed
+ using SampleRenderer::RendererVertexBuffer;
+
+ SampleRenderer::RendererVertexBufferDesc desc;
+ desc.hint = mParent == NULL ? RendererVertexBuffer::HINT_STATIC : RendererVertexBuffer::HINT_DYNAMIC;
+ desc.maxVertices = (uint32_t)numVertices;
+ desc.semanticFormats[RendererVertexBuffer::SEMANTIC_POSITION] = RendererVertexBuffer::FORMAT_FLOAT3;
+ desc.semanticFormats[RendererVertexBuffer::SEMANTIC_NORMAL] = RendererVertexBuffer::FORMAT_FLOAT3;
+
+ PX_ASSERT(desc.isValid());
+
+ mRendererVertexBufferDynamic = mRenderer->createVertexBuffer(desc);
+ vertexBufferDynamicDirty = true;
+ }
+
+ if (mRendererVertexBufferDynamic && vertexBufferDynamicDirty)
+ {
+ using SampleRenderer::RendererVertexBuffer;
+
+ physx::PxStrideIterator<physx::PxVec3> positions;
+ {
+ unsigned int stride = 0;
+ void* data = mRendererVertexBufferDynamic->lockSemantic(RendererVertexBuffer::SEMANTIC_POSITION, stride);
+ positions = PxMakeIterator((physx::PxVec3*)data, stride);
+ }
+ physx::PxStrideIterator<physx::PxVec3> normals;
+ {
+ unsigned int stride = 0;
+ void* data = mRendererVertexBufferDynamic->lockSemantic(RendererVertexBuffer::SEMANTIC_NORMAL, stride);
+ normals = PxMakeIterator((physx::PxVec3*)data, stride);
+ }
+
+ std::vector<physx::PxVec3>& realVertices = mParent != NULL ? mParent->mVertices : mVertices;
+ std::vector<physx::PxVec3>& realNormals = mParent != NULL ? mParent->mNormals : mNormals;
+
+ physx::PxVec3* inPositions = mSkinnedVertices.size() == realVertices.size() ? &mSkinnedVertices[0] : &realVertices[0];
+ physx::PxVec3* inNormals = mSkinnedNormals.size() == realNormals.size() ? &mSkinnedNormals[0] : &realNormals[0];
+ for (unsigned int i = 0; i < numVertices; i++)
+ {
+ positions[i] = inPositions[i];
+ normals[i] = inNormals[i];
+ }
+
+ mRendererVertexBufferDynamic->unlockSemantic(RendererVertexBuffer::SEMANTIC_POSITION);
+ mRendererVertexBufferDynamic->unlockSemantic(RendererVertexBuffer::SEMANTIC_NORMAL);
+ }
+
+
+ bool vertexBufferSharedDirty = rewriteBuffers || textureUvOriginChanged;
+ bool hasBoneBuffer = mParent == NULL && mBoneWeights.size() == mVertices.size() && getMaxBoneIndex() < RENDERER_MAX_BONES;
+ const bool hasTexCoords = mParent == NULL && mTexCoords[0].size() == mVertices.size();
+
+ if (mRendererVertexBufferShared == NULL && mParent == NULL && (hasBoneBuffer || hasTexCoords))
+ {
+ // PH: Only done in the parent, hence shared
+ using SampleRenderer::RendererVertexBuffer;
+
+ SampleRenderer::RendererVertexBufferDesc desc;
+ desc.hint = SampleRenderer::RendererVertexBuffer::HINT_STATIC;
+ desc.maxVertices = (uint32_t)numVertices;
+ if (hasBoneBuffer)
+ {
+ desc.semanticFormats[RendererVertexBuffer::SEMANTIC_BONEINDEX] = RendererVertexBuffer::FORMAT_USHORT4;
+ desc.semanticFormats[RendererVertexBuffer::SEMANTIC_BONEWEIGHT] = RendererVertexBuffer::FORMAT_FLOAT4;
+ }
+ if (hasTexCoords)
+ {
+ desc.semanticFormats[RendererVertexBuffer::SEMANTIC_TEXCOORD0] = RendererVertexBuffer::FORMAT_FLOAT2;
+ }
+
+ PX_ASSERT(desc.isValid());
+
+ mRendererVertexBufferShared = mRenderer->createVertexBuffer(desc);
+ vertexBufferSharedDirty = true;
+ }
+
+ if (mRendererVertexBufferShared != NULL && vertexBufferSharedDirty)
+ {
+ using SampleRenderer::RendererVertexBuffer;
+
+ physx::PxStrideIterator<ushort4> boneIndices;
+ if (hasBoneBuffer)
+ {
+ unsigned int stride = 0;
+ void* data = mRendererVertexBufferShared->lockSemantic(RendererVertexBuffer::SEMANTIC_BONEINDEX, stride);
+ boneIndices = physx::PxMakeIterator((ushort4*)data, stride);
+ }
+ physx::PxStrideIterator<physx::PxVec4> boneWeights;
+ if (hasBoneBuffer)
+ {
+ unsigned int stride = 0;
+ void* data = mRendererVertexBufferShared->lockSemantic(RendererVertexBuffer::SEMANTIC_BONEWEIGHT, stride);
+ boneWeights = physx::PxMakeIterator((physx::PxVec4*)data, stride);
+ }
+ physx::PxStrideIterator<physx::PxVec2> texCoords;
+ if (hasTexCoords)
+ {
+ unsigned int stride = 0;
+ void* data = mRendererVertexBufferShared->lockSemantic(RendererVertexBuffer::SEMANTIC_TEXCOORD0, stride);
+ texCoords = physx::PxMakeIterator((physx::PxVec2*)data, stride);
+ }
+
+ ushort4* inBoneIndices = hasBoneBuffer ? (ushort4*)&mBoneIndicesInternal[0] : NULL;
+ physx::PxVec4* inBoneWeights = hasBoneBuffer ? &mBoneWeights[0] : NULL;
+ nvidia::VertexUV* inTexCoords = hasTexCoords ? &mTexCoords[0][0] : NULL;
+ for (unsigned int i = 0; i < numVertices; i++)
+ {
+ if (hasBoneBuffer)
+ {
+ boneIndices[i] = inBoneIndices[i];
+ boneWeights[i] = inBoneWeights[i];
+ }
+ if (hasTexCoords)
+ {
+ // ugh, we switch to bottom left in SampleApexRenderer.cpp
+ switch (mTextureUVOrigin)
+ {
+ case nvidia::apex::TextureUVOrigin::ORIGIN_TOP_LEFT:
+ texCoords[i].x = inTexCoords[i].u;
+ texCoords[i].y = 1.0f - inTexCoords[i].v;
+ break;
+ case nvidia::apex::TextureUVOrigin::ORIGIN_TOP_RIGHT:
+ texCoords[i].x = 1.0f - inTexCoords[i].u;
+ texCoords[i].y = 1.0f - inTexCoords[i].v;
+ break;
+ case nvidia::apex::TextureUVOrigin::ORIGIN_BOTTOM_LEFT:
+ texCoords[i].x = inTexCoords[i].u;
+ texCoords[i].y = inTexCoords[i].v;
+ break;
+ case nvidia::apex::TextureUVOrigin::ORIGIN_BOTTOM_RIGHT:
+ texCoords[i].x = 1.0f - inTexCoords[i].u;
+ texCoords[i].y = inTexCoords[i].v;
+ break;
+ }
+ }
+ }
+
+ if (hasBoneBuffer)
+ {
+ mRendererVertexBufferShared->unlockSemantic(RendererVertexBuffer::SEMANTIC_BONEINDEX);
+ mRendererVertexBufferShared->unlockSemantic(RendererVertexBuffer::SEMANTIC_BONEWEIGHT);
+ }
+ if (hasTexCoords)
+ {
+ mRendererVertexBufferShared->unlockSemantic(RendererVertexBuffer::SEMANTIC_TEXCOORD0);
+ }
+ }
+
+ bool indexBufferDirty = rewriteBuffers;
+ if (mRendererIndexBuffer == NULL && mParent == NULL)
+ {
+ // PH: Only done in the parent
+ using SampleRenderer::RendererIndexBuffer;
+
+ SampleRenderer::RendererIndexBufferDesc desc;
+ desc.hint = RendererIndexBuffer::HINT_STATIC;
+ desc.maxIndices = (uint32_t)numIndices;
+ desc.format = RendererIndexBuffer::FORMAT_UINT32;
+
+ PX_ASSERT(desc.isValid());
+
+ mRendererIndexBuffer = mRenderer->createIndexBuffer(desc);
+ indexBufferDirty = true;
+ }
+
+ if (mRendererIndexBuffer != NULL && indexBufferDirty && numIndices > 0)
+ {
+ unsigned int* indices = (unsigned int*)mRendererIndexBuffer->lock();
+
+ unsigned int* inIndices = mParent != NULL ? &mParent->mIndices[0] : &mIndices[0];
+
+ // PH: Ugh, I need to reverse winding to make the SampleRenderer happy (mainly because of double sided rendering in D3D)
+ for (unsigned int i = 0; i < numIndices; i += 3)
+ {
+ indices[i + 0] = inIndices[i + 2];
+ indices[i + 1] = inIndices[i + 1];
+ indices[i + 2] = inIndices[i + 0];
+ }
+
+ mRendererIndexBuffer->unlock();
+ }
+
+ // only create render meshes and materials for a mesh we actually want to render
+ if (!sharedOnly)
+ {
+// bool hasTextures = false;
+// bool hasBones = mParent != NULL && mParent->mBoneWeights.size() == mParent->mVertices.size() && mParent->getMaxBoneIndex() < RENDERER_MAX_BONES;
+
+ for (size_t submeshIndex = 0; submeshIndex < mSubMeshes.size(); submeshIndex++)
+ {
+ TriangleSubMesh& sm = mSubMeshes[submeshIndex];
+ bool show = sm.show;
+ if (mParent != NULL)
+ {
+ PX_ASSERT(mParent->mSubMeshes.size() == mSubMeshes.size());
+ show &= mParent->mSubMeshes[submeshIndex].show;
+ }
+
+ if (sm.mRendererMesh == NULL && show)
+ {
+ using SampleRenderer::RendererMesh;
+ using SampleRenderer::RendererVertexBuffer;
+
+ RendererVertexBuffer* vertexBuffers[2];
+ vertexBuffers[0] = mRendererVertexBufferDynamic;
+ if (vertexBuffers[0] == NULL && mParent != NULL)
+ {
+ vertexBuffers[0] = mParent->mRendererVertexBufferDynamic;
+ }
+
+ PX_ASSERT(vertexBuffers[0] != NULL);
+
+ vertexBuffers[1] = mParent != NULL ? mParent->mRendererVertexBufferShared : mRendererVertexBufferShared;
+
+ SampleRenderer::RendererMeshDesc desc;
+ desc.vertexBuffers = vertexBuffers;
+ desc.numVertexBuffers = vertexBuffers[1] != NULL ? 2 : 1;
+ desc.numVertices = (uint32_t)numVertices;
+
+ desc.indexBuffer = mRendererIndexBuffer;
+ if (desc.indexBuffer == NULL && mParent != NULL)
+ {
+ desc.indexBuffer = mParent->mRendererIndexBuffer;
+ }
+ PX_ASSERT(desc.indexBuffer != NULL);
+
+ desc.firstIndex = sm.firstIndex;
+ desc.numIndices = sm.numIndices;
+
+ desc.primitives = RendererMesh::PRIMITIVE_TRIANGLES;
+
+ PX_ASSERT(desc.isValid());
+ sm.mRendererMesh = mRenderer->createMesh(desc);
+ }
+ else if (sm.mRendererMesh != NULL && !show)
+ {
+ sm.mRendererMesh->release();
+ sm.mRendererMesh = NULL;
+ }
+
+ if (sm.mRendererMeshContext == NULL && show)
+ {
+ sm.mRendererMeshContext = new SampleRenderer::RendererMeshContext;
+ }
+
+ SampleFramework::SampleMaterialAsset* materialAsset = mOverrideMaterial;
+ if (sm.mSampleMaterial != NULL && !overrideMaterial)
+ {
+ materialAsset = sm.mSampleMaterial;
+ }
+
+ PX_ASSERT(materialAsset != NULL);
+
+ for (unsigned int i = 0; i < materialAsset->getNumVertexShaders(); i++)
+ {
+ if (mUseGpuSkinning == (materialAsset->getMaxBones(i) > 0))
+ {
+ sm.setMaterialReference(materialAsset->getMaterial(i), materialAsset->getMaterialInstance(i));
+ break;
+ }
+ }
+ PX_ASSERT(sm.mRendererMaterialReference != NULL);
+
+ if (sm.mRendererMaterialInstance != NULL)
+ {
+ using SampleRenderer::RendererMaterial;
+
+ const RendererMaterial::Variable* v = sm.mRendererMaterialInstance->findVariable("diffuseColor", RendererMaterial::VARIABLE_FLOAT4);
+ if (v != NULL)
+ {
+ if (sm.color != 0x00808080) // check for lightgray, the default color. in this case leave the diffuseColor alone
+ {
+ const unsigned int color = sm.color;
+
+ const float red = ((color >> 16) & 0xff) / 255.f;
+ const float green = ((color >> 8) & 0xff) / 255.f;
+ const float blue = ((color >> 0) & 0xff) / 255.f;
+
+ float diffuseColor[4] = { red, green, blue, 1.0f };
+ sm.mRendererMaterialInstance->writeData(*v, diffuseColor);
+ }
+ }
+ }
+ }
+ }
+#endif // USE_SAMPLE_RENDERER
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::queueForRendering(const physx::PxMat44& currentPose, bool wireframe)
+{
+ const physx::PxMat44* tmp = &currentPose;
+ PX_UNUSED(tmp);
+
+ PX_UNUSED(wireframe);
+
+#ifdef USE_SAMPLE_RENDERER
+ if (mRenderer == NULL)
+ {
+ return;
+ }
+
+ mRendererTransform = currentPose;
+
+ for (size_t submeshIndex = 0; submeshIndex < mSubMeshes.size(); submeshIndex++)
+ {
+ TriangleSubMesh& sm = mSubMeshes[submeshIndex];
+ if (sm.mRendererMesh != NULL && sm.mRendererMeshContext != NULL && sm.mRendererMaterialReference != NULL)
+ {
+ using SampleRenderer::RendererMeshContext;
+
+ RendererMeshContext::CullMode cullMode = RendererMeshContext::NONE;
+
+ switch (sm.cullMode)
+ {
+ case nvidia::apex::RenderCullMode::CLOCKWISE:
+ cullMode = RendererMeshContext::CLOCKWISE;
+ break;
+ case nvidia::apex::RenderCullMode::COUNTER_CLOCKWISE:
+ cullMode = RendererMeshContext::COUNTER_CLOCKWISE;
+ break;
+ }
+
+ sm.mRendererMeshContext->cullMode = cullMode;
+ sm.mRendererMeshContext->fillMode = wireframe ? RendererMeshContext::LINE : RendererMeshContext::SOLID;
+ sm.mRendererMeshContext->material = sm.mRendererMaterialReference;
+ sm.mRendererMeshContext->materialInstance = sm.mRendererMaterialInstance;
+ sm.mRendererMeshContext->mesh = sm.mRendererMesh;
+ sm.mRendererMeshContext->transform = &mRendererTransform;
+
+ if (mUseGpuSkinning)
+ {
+ PX_ALWAYS_ASSERT();
+ }
+
+ mRenderer->queueMeshForRender(*sm.mRendererMeshContext);
+ }
+ }
+#endif // USE_SAMPLE_RENDERER
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::skin(const SkeletalAnim& anim, float scale /* = 1.0f */)
+{
+ const size_t numVerts = mParent != NULL ? mParent->mVertices.size() : mVertices.size();
+ const size_t numBoneIndices = mParent != NULL ? mParent->mBoneIndicesExternal.size() : mBoneIndicesExternal.size();
+
+ if (numBoneIndices != numVerts * 4 || mMaxBoneIndexExternal < 0)
+ {
+ return;
+ }
+
+ const std::vector<physx::PxMat44>& skinningMatrices = anim.getSkinningMatrices();
+
+ PX_ASSERT((int32_t)skinningMatrices.size() > mMaxBoneIndexInternal);
+
+ std::vector<int>& boneMappingInt2Ext = mParent != NULL ? mParent->mBoneMappingInt2Ext : mBoneMappingInt2Ext;
+
+ PX_ASSERT(mMaxBoneIndexInternal + 1 == (int)boneMappingInt2Ext.size());
+ const uint32_t numSkinningMatricesNeeded = physx::PxMin<uint32_t>((uint32_t)mMaxBoneIndexInternal + 1, (uint32_t)skinningMatrices.size());
+ if (mSkinningMatrices.size() != numSkinningMatricesNeeded)
+ {
+ mSkinningMatrices.resize(numSkinningMatricesNeeded);
+ }
+
+ for (uint32_t i = 0; i < numSkinningMatricesNeeded; i++)
+ {
+ mSkinningMatrices[i] = skinningMatrices[(uint32_t)boneMappingInt2Ext[i]];
+ mSkinningMatrices[i].scale(physx::PxVec4(scale, scale, scale, 1.f));
+ }
+
+ skinningMatricesChanged = true;
+
+ if (mUseGpuSkinning)
+ {
+ mSkinnedVertices.clear();
+ mSkinnedNormals.clear();
+ return;
+ }
+
+ vertexValuesChangedDynamic = true;
+
+ if (mSkinnedVertices.size() != numVerts)
+ {
+ mSkinnedVertices.resize(numVerts);
+ mSkinnedNormals.resize(numVerts);
+ }
+
+ const size_t numBones = skinningMatrices.size();
+ PX_ASSERT((int)numBones > mMaxBoneIndexInternal);
+ PX_UNUSED(numBones);
+
+ const physx::PxVec3* __restrict originalVertices = mParent == NULL || (mVertices.size() == mParent->mVertices.size()) ? &mVertices[0] : &mParent->mVertices[0];
+ const physx::PxVec3* __restrict originalNormals = mParent == NULL ? &mNormals[0] : &mParent->mNormals[0];
+ const uint16_t* __restrict skinningIndices = mParent == NULL ? &mBoneIndicesInternal[0] : &mParent->mBoneIndicesInternal[0];
+ const physx::PxVec4* __restrict skinningWeights = mParent == NULL ? &mBoneWeights[0] : &mParent->mBoneWeights[0];
+ physx::PxVec3* __restrict destVertices = &mSkinnedVertices[0];
+ physx::PxVec3* __restrict destNormals = &mSkinnedNormals[0];
+ uint32_t* __restrict numBoneWeights = mParent == NULL ? &mNumBoneWeights[0] : &mParent->mNumBoneWeights[0];
+
+
+ nvidia::prefetchLine(skinningWeights, 0);
+
+ const size_t count16 = (numVerts + 15) / 16;
+ for (size_t i = 0; i < count16; i++)
+ {
+ uint32_t numBoneWeight = *numBoneWeights;
+
+ const size_t maxVerts = (i + 1) * 16 > numVerts ? numVerts - (i * 16) : 16;
+
+ for (size_t j = 0; j < maxVerts; j++)
+ {
+ nvidia::prefetchLine(skinningWeights + 1, 0);
+
+ size_t twoBit = numBoneWeight & 0x3;
+ numBoneWeight >>= 2;
+
+ const physx::PxVec3 v = *originalVertices;
+ const physx::PxVec3 n = *originalNormals;
+
+ physx::PxVec3 vs(0.0f, 0.0f, 0.0f);
+ physx::PxVec3 ns(0.0f, 0.0f, 0.0f);
+
+ physx::PxVec4 skinningWeight = *skinningWeights;
+
+ for (unsigned int k = 0; k <= twoBit; k++)
+ {
+ //const uint32_t weightIdx = i*4 + k;
+ PX_ASSERT(physx::PxIsFinite(skinningWeight[k]));
+ const float weight = skinningWeight[k];
+
+ PX_ASSERT(weight > 0.0f);
+ const uint16_t boneNr = skinningIndices[k];
+
+ vs += mSkinningMatrices[boneNr].transform(v) * weight;
+ ns += mSkinningMatrices[boneNr].rotate(n) * weight;
+ }
+ ns *= nvidia::recipSqrtFast(ns.magnitudeSquared());
+ //ns.normalize();
+
+ *destVertices = vs;
+ *destNormals = ns;
+
+ originalVertices++;
+ originalNormals++;
+ skinningWeights++;
+ skinningIndices += 4;
+ destVertices++;
+ destNormals++;
+ }
+
+ numBoneWeights++;
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::unskin()
+{
+ mSkinningMatrices.clear();
+ skinningMatricesChanged = true;
+
+ mSkinnedVertices.clear();
+ mSkinnedNormals.clear();
+ vertexValuesChangedDynamic = true;
+
+ PX_ASSERT(mSkinnedVertices.empty());
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::showSubmesh(size_t index, bool on)
+{
+ if (index >= mSubMeshes.size())
+ {
+ for (size_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ mSubMeshes[i].show = on;
+ }
+ }
+ else
+ {
+ mSubMeshes[index].show = on;
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::selectSubMesh(size_t index, bool selected)
+{
+ if (index >= mSubMeshes.size())
+ {
+ for (size_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ mSubMeshes[i].selected = selected;
+ }
+ }
+ else
+ {
+ mSubMeshes[index].selected = selected;
+ }
+ updateSubmeshInfo();
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::hideSubMesh(const char* submeshName, const char* materialName)
+{
+ // Selected submeshes are hidden later on
+ int found = 0;
+ if (materialName != NULL)
+ {
+ for (size_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ if (mSubMeshes[i].name == submeshName && mSubMeshes[i].originalMaterialName == materialName)
+ {
+ mSubMeshes[i].selected = true;
+ found++;
+ }
+ }
+ }
+
+ if (found == 0)
+ {
+ for (size_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ if (mSubMeshes[i].name == submeshName)
+ {
+ mSubMeshes[i].selected = true;
+ found++;
+ }
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+size_t TriangleMesh::getNumTriangles(size_t subMeshNr) const
+{
+ if (mParent != NULL)
+ {
+ return mParent->getNumTriangles(subMeshNr);
+ }
+
+ if (subMeshNr >= mSubMeshes.size())
+ {
+ return mIndices.size() / 3;
+ }
+ else
+ {
+ return mSubMeshes[subMeshNr].numIndices / 3;
+ }
+}
+
+// ----------------------------------------------------------------------
+std::vector<PaintedVertex> &TriangleMesh::getPaintChannel(PaintChannelType channelType)
+{
+ PX_ASSERT(0 <= channelType && channelType < PC_NUM_CHANNELS);
+ return mPaintChannels[channelType];
+}
+
+// ----------------------------------------------------------------------
+const std::vector<PaintedVertex> &TriangleMesh::getPaintChannel(PaintChannelType channelType) const
+{
+ PX_ASSERT(0 <= channelType && channelType < PC_NUM_CHANNELS);
+ return mPaintChannels[channelType];
+}
+// ----------------------------------------------------------------------
+float TriangleMesh::getMaximalMaxDistance() const
+{
+ if (mPaintChannels[PC_MAX_DISTANCE].size() != mVertices.size())
+ {
+ return 0.0f;
+ }
+
+ float maxValue = 0.0f;
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ maxValue = physx::PxMax(maxValue, mPaintChannels[PC_MAX_DISTANCE][i].paintValueF32);
+ }
+ return maxValue;
+}
+
+// ----------------------------------------------------------------------
+int TriangleMesh::getBoneAssignments(uint32_t vertNr, const uint16_t* &bones, const float* &weights) const
+{
+ if (mParent != NULL)
+ {
+ return mParent->getBoneAssignments(vertNr, bones, weights);
+ }
+
+ if (mBoneIndicesExternal.empty())
+ {
+ return 0;
+ }
+
+ if (vertNr >= mVertices.size())
+ {
+ return 0;
+ }
+
+ bones = &mBoneIndicesExternal[vertNr * 4];
+ weights = &mBoneWeights[vertNr].x;
+
+ for (unsigned int i = 0; i < 4; i++)
+ {
+ if (weights[i] == 0.0f)
+ {
+ return (int)i;
+ }
+ }
+ return 4;
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::displaceAlongNormal(float displacement)
+{
+ if (mNormals.size() != mVertices.size())
+ {
+ return;
+ }
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ mVertices[i] += mNormals[i] * displacement;
+ }
+ updateBounds();
+}
+
+// ----------------------------------------------------------------------
+bool TriangleMesh::generateTangentSpace()
+{
+ if (mTexCoords[0].size() == mVertices.size())
+ {
+ // just make the size not 0
+ mTangents.push_back(physx::PxVec3(0.0f));
+ updateTangents();
+
+ return true;
+ }
+ return false;
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::updateBounds()
+{
+ mBounds.setEmpty();
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ mBounds.include(mVertices[i]);
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::setSubMeshColor(size_t subMeshNr, uint32_t color)
+{
+ if (subMeshNr >= mSubMeshes.size())
+ {
+ for (size_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ setSubMeshColor(i, color);
+ }
+ }
+ else
+ {
+ mSubMeshes[subMeshNr].color = color;
+
+ if (mSubMeshes[subMeshNr].materialResource != NULL)
+ {
+ float red = ((color >> 16) & 0xff) / 255.f;
+ float green = ((color >> 8) & 0xff) / 255.f;
+ float blue = ((color >> 0) & 0xff) / 255.f;
+
+ mSubMeshes[subMeshNr].materialResource->color = physx::PxVec3(red, green, blue);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::setSubMeshMaterialName(size_t subMeshNr, const char* materialName, nvidia::ResourceCallback* resourceCallback)
+{
+ if (subMeshNr >= mSubMeshes.size())
+ {
+ for (size_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ setSubMeshMaterialName(i, materialName, resourceCallback);
+ }
+ }
+ else
+ {
+ mSubMeshes[subMeshNr].materialName = materialName;
+ std::string name;
+ name.append(mMaterialPrefix);
+ name.append(materialName);
+ name.append(mMaterialSuffix);
+#ifdef USE_SAMPLE_RENDERER
+ if (mSubMeshes[subMeshNr].mSampleMaterial != NULL)
+ {
+ resourceCallback->releaseResource(APEX_MATERIALS_NAME_SPACE, NULL, mSubMeshes[subMeshNr].mSampleMaterial);
+ }
+ mSubMeshes[subMeshNr].mSampleMaterial = reinterpret_cast<SampleFramework::SampleMaterialAsset*>(resourceCallback->requestResource(APEX_MATERIALS_NAME_SPACE, name.c_str()));
+#else
+ mSubMeshes[subMeshNr].materialResource = (MaterialResource*)resourceCallback->requestResource(APEX_MATERIALS_NAME_SPACE, name.c_str());
+#endif
+ mSubMeshes[subMeshNr].resourceNeedsUpdate = true;
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::setSubMeshUsedForCollision(size_t subMeshNr, bool enable)
+{
+ if (subMeshNr < mSubMeshes.size())
+ {
+ mSubMeshes[subMeshNr].usedForCollision = enable;
+ }
+ else
+ {
+ for (size_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ setSubMeshUsedForCollision(i, enable);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::setSubMeshHasPhysics(size_t subMeshNr, bool enable)
+{
+ if (subMeshNr < mSubMeshes.size())
+ {
+ mSubMeshes[subMeshNr].hasApexAsset = enable;
+ }
+ else
+ {
+ for (size_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ setSubMeshHasPhysics(i, enable);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::setAllColors(unsigned int color)
+{
+ for (size_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ setSubMeshColor(i, color);
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::subdivideSubMesh(int subMeshNr, int subdivision, bool evenOutVertexDegrees)
+{
+ const int newTris[8][13] =
+ {
+ { 0, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+ { 0, 3, 2, 3, 1, 2, -1, -1, -1, -1, -1, -1, -1},
+ { 0, 1, 4, 0, 4, 2, -1, -1, -1, -1, -1, -1, -1},
+ { 0, 3, 2, 3, 1, 4, 3, 4, 2, -1, -1, -1, -1},
+ { 0, 1, 5, 1, 2, 5, -1, -1, -1, -1, -1, -1, -1},
+ { 0, 3, 5, 3, 1, 5, 1, 2, 5, -1, -1, -1, -1},
+ { 0, 1, 5, 1, 4, 5, 4, 2, 5, -1, -1, -1, -1},
+ { 0, 3, 5, 3, 1, 4, 3, 4, 5, 4, 2, 5, -1}
+ };
+
+ if (subMeshNr < 0 || subMeshNr >= (int)mSubMeshes.size())
+ {
+ return;
+ }
+
+ if (subdivision == 0)
+ {
+ return;
+ }
+
+ float maxEdgeLength = (mBounds.minimum - mBounds.maximum).magnitude() / subdivision;
+ TriangleSubMesh& sm = mSubMeshes[(uint32_t)subMeshNr];
+
+ // move submesh to the end so it can be expanded
+ std::vector<uint32_t> tempIndices;
+ tempIndices.resize(sm.numIndices);
+ for (uint32_t i = 0; i < sm.numIndices; i++)
+ {
+ tempIndices[i] = mIndices[sm.firstIndex + i];
+ }
+
+ uint32_t numShift = (uint32_t)mIndices.size() - (sm.firstIndex + sm.numIndices);
+ for (uint32_t i = 0; i < numShift; i++)
+ {
+ mIndices[sm.firstIndex + i] = mIndices[sm.firstIndex + sm.numIndices + i];
+ }
+
+ uint32_t last = (uint32_t)mIndices.size() - sm.numIndices;
+ for (uint32_t i = 0; i < sm.numIndices; i++)
+ {
+ mIndices[last + i] = tempIndices[i];
+ }
+
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ TriangleSubMesh& si = mSubMeshes[i];
+ if (si.firstIndex > sm.firstIndex)
+ {
+ si.firstIndex -= sm.numIndices;
+ }
+ }
+ sm.firstIndex = (uint32_t)mIndices.size() - sm.numIndices;
+
+ // subdivide
+ float h2 = maxEdgeLength * maxEdgeLength;
+ mVertexFirstSplit.resize(mVertices.size());
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ mVertexFirstSplit[i] = -1;
+ }
+ mVertexSplits.clear();
+
+ uint32_t numTris = sm.numIndices / 3;
+ int id[6];
+
+ vertexCountChanged |= sm.numIndices > 0;
+
+ for (uint32_t i = 0; i < numTris; i++)
+ {
+ id[0] = (int32_t)mIndices[sm.firstIndex + 3 * i + 0];
+ id[1] = (int32_t)mIndices[sm.firstIndex + 3 * i + 1];
+ id[2] = (int32_t)mIndices[sm.firstIndex + 3 * i + 2];
+ physx::PxVec3 p0 = mVertices[(uint32_t)id[0]];
+ physx::PxVec3 p1 = mVertices[(uint32_t)id[1]];
+ physx::PxVec3 p2 = mVertices[(uint32_t)id[2]];
+
+ uint32_t code = 0;
+ if ((p0 - p1).magnitudeSquared() > h2)
+ {
+ id[3] = addSplitVert(id[0], id[1]);
+ code |= 1;
+ }
+ if ((p1 - p2).magnitudeSquared() > h2)
+ {
+ id[4] = addSplitVert(id[1], id[2]);
+ code |= 2;
+ }
+ if ((p2 - p0).magnitudeSquared() > h2)
+ {
+ id[5] = addSplitVert(id[2], id[0]);
+ code |= 4;
+ }
+
+ const int* newId = newTris[code];
+
+ // the first sub triangle replaces the old one
+ mIndices[sm.firstIndex + 3 * i + 0] = (uint32_t)id[(uint32_t)*newId++];
+ mIndices[sm.firstIndex + 3 * i + 1] = (uint32_t)id[(uint32_t)*newId++];
+ mIndices[sm.firstIndex + 3 * i + 2] = (uint32_t)id[(uint32_t)*newId++];
+
+ // the others are appended at the end
+ while (true)
+ {
+ int j = *newId++;
+ if (j >= 0)
+ {
+ mIndices.push_back((uint32_t)id[(uint32_t)j]);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ sm.numIndices = (uint32_t)mIndices.size() - sm.firstIndex;
+
+ if (evenOutVertexDegrees)
+ {
+ evenOutVertexDegree(subMeshNr, 3);
+ }
+
+ updateNormals(subMeshNr);
+ updateTangents();
+ updateBoneWeights();
+ updateSubmeshInfo();
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::evenOutVertexDegree(int subMeshNr, int numIters)
+{
+ std::vector<TriangleMeshEdge> edges;
+ TriangleMeshEdge edge;
+
+ // compute degrees and neighbor information
+ std::vector<int> vertDegs;
+ vertDegs.resize(mVertices.size(), 0);
+
+ PX_ASSERT(subMeshNr < (int)mSubMeshes.size());
+ uint32_t firstIndex = 0;
+ uint32_t lastIndex = (uint32_t)mIndices.size();
+ if (subMeshNr >= 0)
+ {
+ firstIndex = mSubMeshes[(uint32_t)subMeshNr].firstIndex;
+ lastIndex = firstIndex + mSubMeshes[(uint32_t)subMeshNr].numIndices;
+ }
+ uint32_t* indices = &mIndices[firstIndex];
+ uint32_t numTris = (lastIndex - firstIndex) / 3;
+
+ for (uint32_t i = 0; i < numTris; i++)
+ {
+ uint32_t i0 = indices[3 * i];
+ uint32_t i1 = indices[3 * i + 1];
+ uint32_t i2 = indices[3 * i + 2];
+ vertDegs[i0]++;
+ vertDegs[i1]++;
+ vertDegs[i2]++;
+ edge.init((int32_t)i0, (int32_t)i1, 0, (int32_t)i);
+ edges.push_back(edge);
+ edge.init((int32_t)i1, (int32_t)i2, 1, (int32_t)i);
+ edges.push_back(edge);
+ edge.init((int32_t)i2, (int32_t)i0, 2, (int32_t)i);
+ edges.push_back(edge);
+ }
+ std::sort(edges.begin(), edges.end());
+
+ std::vector<int> neighbors;
+ neighbors.resize(3 * numTris, -1);
+
+ size_t edgeNr = 0;
+ size_t numEdges = edges.size();
+ while (edgeNr < numEdges)
+ {
+ TriangleMeshEdge& e0 = edges[edgeNr];
+ size_t j = edgeNr + 1;
+ while (j < numEdges && edges[j] == e0)
+ {
+ j++;
+ }
+ if (j < numEdges && j == edgeNr + 2) // manifold edge
+ {
+ TriangleMeshEdge& e1 = edges[edgeNr + 1];
+ neighbors[uint32_t(e0.triNr * 3 + e0.edgeNr)] = e1.triNr;
+ neighbors[uint32_t(e1.triNr * 3 + e1.edgeNr)] = e0.triNr;
+ }
+ edgeNr = j;
+ }
+
+ // artifically increase the degree of border vertices
+ for (uint32_t i = 0; i < numTris; i++)
+ {
+ for (uint32_t j = 0; j < 3; j++)
+ {
+ if (neighbors[3 * i + j] < 0)
+ {
+ vertDegs[indices[3 * i + j]]++;
+ vertDegs[indices[3 * i + (j + 1) % 3]]++;
+ }
+ }
+ }
+
+ // create random triangle permutation
+ std::vector<int> permutation;
+ permutation.resize(numTris);
+ for (uint32_t i = 0; i < numTris; i++)
+ {
+ permutation[i] = (int32_t)i;
+ }
+
+ uint32_t random = 0;
+ for (uint32_t i = 0; i < numTris - 1; i++)
+ {
+ random = random * 1664525 + 1013904223;
+ uint32_t j = i + random % (numTris - i);
+ int p = permutation[i];
+ permutation[i] = permutation[j];
+ permutation[j] = p;
+ }
+
+ // flip edges
+ for (uint32_t iters = 0; iters < (uint32_t)numIters; iters++)
+ {
+ for (uint32_t i = 0; i < numTris; i++)
+ {
+ int t = permutation[i];
+ for (uint32_t j = 0; j < 3; j++)
+ {
+ int n = neighbors[3 * t + j];
+ if (n < t)
+ {
+ continue;
+ }
+ // determine the indices of the two triangles involved
+ uint32_t& i0 = indices[3 * (uint32_t)t + j];
+ uint32_t& i1 = indices[3 * (uint32_t)t + (j + 1) % 3];
+ uint32_t& i2 = indices[3 * (uint32_t)t + (j + 2) % 3];
+
+ int backNr = -1;
+ if (neighbors[3 * (uint32_t)n] == t)
+ {
+ backNr = 0;
+ }
+ else if (neighbors[3 * (uint32_t)n + 1] == t)
+ {
+ backNr = 1;
+ }
+ else if (neighbors[3 * (uint32_t)n + 2] == t)
+ {
+ backNr = 2;
+ }
+ PX_ASSERT(backNr >= 0);
+ //uint32_t& j0 = indices[3 * n + backNr];
+ uint32_t& j1 = indices[3 * (uint32_t)n + ((uint32_t)backNr + 1) % 3];
+ uint32_t& j2 = indices[3 * (uint32_t)n + ((uint32_t)backNr + 2) % 3];
+
+ // do we want to flip?
+
+ // geometrical tests
+ physx::PxVec3& p0 = mVertices[i0];
+ physx::PxVec3& p1 = mVertices[i1];
+ physx::PxVec3& q0 = mVertices[i2];
+ physx::PxVec3& q1 = mVertices[j2];
+
+ // does the triangle pair form a convex shape?
+ float r = (p0 - p1).magnitudeSquared();
+ if (r == 0.0f)
+ {
+ continue;
+ }
+
+ float s = (q0 - p0).dot(p1 - p0) / r;
+ if (s < 0.2f || s > 0.8f)
+ {
+ continue;
+ }
+ s = (q1 - p0).dot(p1 - p0) / r;
+ if (s < 0.2f || s > 0.8f)
+ {
+ continue;
+ }
+
+ // the new edge shoulndnot be significantly longer than the old one
+ float d0 = (mVertices[i0] - mVertices[i1]).magnitude();
+ float d1 = (mVertices[i2] - mVertices[j2]).magnitude();
+ if (d1 > 2.0f * d0)
+ {
+ continue;
+ }
+
+ // will the flip even out the degrees?
+ int deg0 = vertDegs[i0];
+ int deg1 = vertDegs[i1];
+ int deg2 = vertDegs[i2];
+ int deg3 = vertDegs[j2];
+
+ int min = physx::PxMin(deg0, physx::PxMin(deg1, physx::PxMin(deg2, deg3)));
+ int max = physx::PxMax(deg0, physx::PxMax(deg1, physx::PxMax(deg2, deg3)));
+ int span = max - min;
+
+ deg0--;
+ deg1--;
+ deg2++;
+ deg3++;
+
+ min = physx::PxMin(deg0, physx::PxMin(deg1, physx::PxMin(deg2, deg3)));
+ max = physx::PxMax(deg0, physx::PxMax(deg1, physx::PxMax(deg2, deg3)));
+
+ if (max - min > span)
+ {
+ continue;
+ }
+
+
+ // update degrees
+ vertDegs[i0]--;
+ vertDegs[i1]--;
+ vertDegs[i2]++;
+ vertDegs[j2]++;
+
+ // flip
+ i1 = j2;
+ j1 = i2;
+
+ // update neighbors
+ int& ni0 = neighbors[3 * (uint32_t)t + j];
+ int& ni1 = neighbors[3 * (uint32_t)t + (j + 1) % 3];
+
+ int& nj0 = neighbors[3 * (uint32_t)n + (uint32_t)backNr];
+ int& nj1 = neighbors[3 * (uint32_t)n + ((uint32_t)backNr + 1) % 3];
+
+ ni0 = nj1;
+ nj0 = ni1;
+ ni1 = n;
+ nj1 = t;
+
+ // fix backwards links
+ if (ni0 >= 0)
+ {
+ if (neighbors[3 * (uint32_t)ni0] == n)
+ {
+ neighbors[3 * (uint32_t)ni0] = t;
+ }
+ else if (neighbors[3 * (uint32_t)ni0 + 1] == n)
+ {
+ neighbors[3 * (uint32_t)ni0 + 1] = t;
+ }
+ else if (neighbors[3 * (uint32_t)ni0 + 2] == n)
+ {
+ neighbors[3 * (uint32_t)ni0 + 2] = t;
+ }
+ else
+ {
+ PX_ALWAYS_ASSERT();
+ }
+ }
+ if (nj0 >= 0)
+ {
+ if (neighbors[3 * (uint32_t)nj0] == t)
+ {
+ neighbors[3 * (uint32_t)nj0] = n;
+ }
+ else if (neighbors[3 * (uint32_t)nj0 + 1] == t)
+ {
+ neighbors[3 * (uint32_t)nj0 + 1] = n;
+ }
+ else if (neighbors[3 * (uint32_t)nj0 + 2] == t)
+ {
+ neighbors[3 * (uint32_t)nj0 + 2] = n;
+ }
+ else
+ {
+ PX_ALWAYS_ASSERT();
+ }
+ }
+ }
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::setCullMode(nvidia::RenderCullMode::Enum cullMode, int32_t submeshIndex)
+{
+ if (submeshIndex < 0)
+ {
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ setCullMode(cullMode, (int32_t)i);
+ }
+ }
+ else if ((uint32_t)submeshIndex < mSubMeshes.size())
+ {
+ if (cullMode != mSubMeshes[(uint32_t)submeshIndex].cullMode)
+ {
+ mSubMeshes[(uint32_t)submeshIndex].cullMode = cullMode;
+ oneCullModeChanged = true;
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::updatePaintingColors(PaintChannelType channelType, float maxDistMin, float maxDistMax, unsigned int flag, nvidia::apex::RenderDebugInterface* batcher)
+{
+ using RENDER_DEBUG::DebugColors;
+ const uint32_t colorBlack = batcher != NULL ? RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::Black) : 0xff000000;
+ const uint32_t colorWhite = batcher != NULL ? RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::White) : 0xffffffff;
+ const uint32_t colorLightBlue = batcher != NULL ? RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::LightBlue) : 0xff7777ff;
+ const uint32_t colorRed = batcher != NULL ? RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::Red) : 0xffff0000;
+
+ const uint32_t colorDisabled = batcher != NULL ? RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::Purple) : 0xffff00ff;
+ const uint32_t colorSmall = batcher != NULL ? RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::Blue) : 0xff0000ff;;
+ const uint32_t colorBig = colorRed;
+
+
+ if (channelType == PC_MAX_DISTANCE || channelType == PC_NUM_CHANNELS)
+ {
+ for (uint32_t i = 0; i < mPaintChannels[PC_MAX_DISTANCE].size(); i++)
+ {
+ const float val = mPaintChannels[PC_MAX_DISTANCE][i].paintValueF32;
+ if (val < 0.0f)
+ {
+ mPaintChannels[PC_MAX_DISTANCE][i].color = colorDisabled;
+ }
+ else if (val < maxDistMin)
+ {
+ mPaintChannels[PC_MAX_DISTANCE][i].color = colorSmall;
+ }
+ else if (val > maxDistMax)
+ {
+ mPaintChannels[PC_MAX_DISTANCE][i].color = colorBig;
+ }
+ else
+ {
+ const float setVal = (val - maxDistMin) / (maxDistMax - maxDistMin);
+ mPaintChannels[PC_MAX_DISTANCE][i].setColor(setVal, setVal, setVal);
+ }
+ }
+ }
+ if (channelType == PC_COLLISION_DISTANCE || channelType == PC_NUM_CHANNELS)
+ {
+ for (uint32_t i = 0; i < mPaintChannels[PC_COLLISION_DISTANCE].size(); i++)
+ {
+ const float v = mPaintChannels[PC_COLLISION_DISTANCE][i].paintValueF32;
+ physx::PxVec3 color;
+ if (physx::PxAbs(v) > 1.0f)
+ {
+ color = physx::PxVec3(0.0f, 0.0f, 0.0f);
+ }
+ else if (v == 0.0f)
+ {
+ color = physx::PxVec3(1.0f, 1.0f, 1.0f);
+ }
+ else if (v < 0.0f)
+ {
+ color = physx::PxVec3(1.0f, -v, 0.0f);
+ }
+ else // v > 0.0f
+ {
+ color = physx::PxVec3(0.0f, v, 1.0f);
+ }
+ mPaintChannels[PC_COLLISION_DISTANCE][i].setColor(color.x, color.y, color.z);
+ }
+ }
+
+ if (channelType == PC_LATCH_TO_NEAREST_SLAVE || channelType == PC_NUM_CHANNELS)
+ {
+ for (size_t i = 0; i < mPaintChannels[PC_LATCH_TO_NEAREST_SLAVE].size(); i++)
+ {
+ const unsigned int slave = mPaintChannels[PC_LATCH_TO_NEAREST_SLAVE][i].paintValueU32;
+ const unsigned int master = mPaintChannels[PC_LATCH_TO_NEAREST_MASTER][i].paintValueU32;
+
+ unsigned int color;
+ if (slave == 0)
+ {
+ if (master == 0)
+ {
+ color = colorRed;
+ }
+ else
+ {
+ color = colorBlack;
+ }
+ }
+ else
+ {
+ if (flag != 0 && (slave & flag) == flag)
+ {
+ color = colorWhite;
+ }
+ else
+ {
+ color = colorLightBlue;
+ }
+ }
+
+ mPaintChannels[PC_LATCH_TO_NEAREST_SLAVE][i].color = color;
+ }
+ }
+ if (channelType == PC_LATCH_TO_NEAREST_MASTER || channelType == PC_NUM_CHANNELS)
+ {
+ for (size_t i = 0; i < mPaintChannels[PC_LATCH_TO_NEAREST_MASTER].size(); i++)
+ {
+ const unsigned int slave = mPaintChannels[PC_LATCH_TO_NEAREST_SLAVE][i].paintValueU32;
+ const unsigned int master = mPaintChannels[PC_LATCH_TO_NEAREST_MASTER][i].paintValueU32;
+
+ unsigned int color;
+ if (slave != 0)
+ {
+ color = colorDisabled;
+ }
+ else
+ {
+ if (flag != 0 && (master & flag) == flag)
+ {
+ color = colorWhite;
+ }
+ else if (master != 0)
+ {
+ color = colorLightBlue;
+ }
+ else
+ {
+ color = colorRed;
+ }
+ }
+
+ mPaintChannels[PC_LATCH_TO_NEAREST_MASTER][i].color = color;
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+bool TriangleMesh::processElement(const char* elementName, const char* /*elementData*/, const physx::shdfnd::FastXml::AttributePairs& attr, int /*lineno*/)
+{
+ if (::strcmp(elementName, "mesh") == 0)
+ {
+ PX_ASSERT(mParserState == PS_Uninitialized);
+ mParserState = PS_Mesh;
+ }
+ else if (::strcmp(elementName, "sharedgeometry") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 1);
+ PX_ASSERT(::strcmp(attr.getKey(0), "vertexcount") == 0);
+ const int vertexCount = atoi(attr.getValue(0));
+ mVertices.resize(0);
+ mVertices.reserve((uint32_t)vertexCount);
+ }
+ else if (::strcmp(elementName, "vertexbuffer") == 0)
+ {
+ for (int i = 0; i < attr.getNbAttr(); i++)
+ {
+ if (::strcmp(attr.getKey(i), "normals") == 0)
+ {
+ mNormals.reserve(mVertices.capacity());
+ }
+
+ else if (::strcmp(attr.getKey(i), "texture_coord_dimensions_0") == 0)
+ {
+ mTexCoords[0].reserve(mVertices.capacity());
+ }
+ }
+ }
+ else if (::strcmp(elementName, "vertex") == 0)
+ {
+ int a = 0;
+ PX_UNUSED(a);
+
+ // do nothing?
+ }
+ else if (::strcmp(elementName, "position") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 3);
+ PX_ASSERT(::strcmp(attr.getKey(0), "x") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(1), "y") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(2), "z") == 0);
+ physx::PxVec3 pos;
+ pos.x = (float)atof(attr.getValue(0));
+ pos.y = (float)atof(attr.getValue(1));
+ pos.z = (float)atof(attr.getValue(2));
+ mVertices.push_back(pos);
+ }
+ else if (::strcmp(elementName, "normal") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 3);
+ PX_ASSERT(::strcmp(attr.getKey(0), "x") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(1), "y") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(2), "z") == 0);
+ physx::PxVec3 normal;
+ normal.x = (float)atof(attr.getValue(0));
+ normal.y = (float)atof(attr.getValue(1));
+ normal.z = (float)atof(attr.getValue(2));
+ mNormals.push_back(normal);
+ }
+ else if (::strcmp(elementName, "texcoord") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 2);
+ PX_ASSERT(::strcmp(attr.getKey(0), "u") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(1), "v") == 0);
+ nvidia::VertexUV tc;
+ tc.u = (float)atof(attr.getValue(0));
+ tc.v = (float)atof(attr.getValue(1));
+ mTexCoords[0].push_back(tc);
+ }
+ else if (::strcmp(elementName, "colour_diffuse") == 0)
+ {
+ int a = 0;
+ PX_UNUSED(a);
+ }
+ else if (::strcmp(elementName, "physics_coeffs") == 0)
+ {
+ for (int i = 0; i < PC_NUM_CHANNELS; i++)
+ {
+ if (mPaintChannels[i].capacity() < mVertices.size())
+ {
+ mPaintChannels[i].reserve(mVertices.size());
+ }
+
+ if (i < attr.getNbAttr())
+ {
+ float value = (float)atof(attr.getValue(i));
+ mPaintChannels[i].push_back(PaintedVertex(value));
+ }
+ }
+ }
+ else if (::strcmp(elementName, "submeshes") == 0)
+ {
+ PX_ASSERT(mParserState == PS_Mesh);
+ mParserState = PS_Submeshes;
+ }
+ else if (::strcmp(elementName, "submesh") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() >= 1);
+ PX_ASSERT(::strcmp(attr.getKey(0), "material") == 0);
+ TriangleSubMesh sm;
+ sm.init();
+ sm.name = attr.getValue(0); // ogre xml doesn't have submesh names...
+ sm.materialName = attr.getValue(0);
+ sm.firstIndex = (uint32_t)mIndices.size();
+ if (sm.materialName.empty())
+ {
+ char buf[64];
+ physx::shdfnd::snprintf(buf, 64, "Material_%2d", mSubMeshes.size());
+ sm.materialName = buf;
+ }
+ sm.originalMaterialName = sm.materialName;
+ mSubMeshes.push_back(sm);
+ }
+ else if (::strcmp(elementName, "faces") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 1);
+ PX_ASSERT(::strcmp(attr.getKey(0), "count") == 0);
+ const int faceCount = atoi(attr.getValue(0));
+ mIndices.reserve(mIndices.size() + faceCount * 3);
+ PX_ASSERT(mSubMeshes.size() > 0);
+ PX_ASSERT(mSubMeshes.back().numIndices == 0);
+ mSubMeshes.back().numIndices = (uint32_t)faceCount * 3;
+ }
+ else if (::strcmp(elementName, "face") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 3);
+ PX_ASSERT(::strcmp(attr.getKey(0), "v1") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(1), "v2") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(2), "v3") == 0);
+ mIndices.push_back((uint32_t)atoi(attr.getValue(0)));
+ mIndices.push_back((uint32_t)atoi(attr.getValue(1)));
+ mIndices.push_back((uint32_t)atoi(attr.getValue(2)));
+ }
+ else if (::strcmp(elementName, "skeletonlink") == 0)
+ {
+ PX_ASSERT(mParserState == PS_Submeshes);
+ mParserState = PS_Skeleton;
+ }
+ else if (::strcmp(elementName, "boneassignments") == 0)
+ {
+ // do nothing?
+ mBoneIndicesExternal.resize(mVertices.capacity() * 4, 0);
+ mBoneWeights.resize(mVertices.capacity(), physx::PxVec4(0.0f));
+ }
+ else if (::strcmp(elementName, "vertexboneassignment") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 3);
+ PX_ASSERT(::strcmp(attr.getKey(0), "vertexindex") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(1), "boneindex") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(2), "weight") == 0);
+
+ const int vertexNr = atoi(attr.getValue(0));
+ const int boneNr = atoi(attr.getValue(1));
+ const float weight = (float)atof(attr.getValue(2));
+
+ mMaxBoneIndexExternal = physx::PxMax(mMaxBoneIndexExternal, boneNr);
+ PX_ASSERT(vertexNr < (int)mVertices.size());
+ float* weights = &mBoneWeights[(uint32_t)vertexNr].x;
+ uint16_t* indices = &mBoneIndicesExternal[(uint32_t)vertexNr * 4];
+ for (uint32_t i = 0; i < 4; i++)
+ {
+ if (weights[i] == 0)
+ {
+ PX_ASSERT(boneNr < 0xffff);
+ weights[i] = weight;
+ indices[i] = (uint16_t)boneNr;
+ break;
+ }
+ if (weights[i] < weight)
+ {
+ // move all one back
+ for (uint32_t j = 3; j > i; j--)
+ {
+ weights[j] = weights[j - 1];
+ indices[j] = indices[j - 1];
+ }
+ weights[i] = weight;
+ indices[i] = (uint16_t)boneNr;
+ break;
+ }
+#if 1
+ // safety
+ for (uint32_t j = 0; j < 4; j++)
+ {
+ PX_ASSERT(weights[j] >= 0.0f);
+ PX_ASSERT(weights[j] <= 1.0f);
+ }
+ for (uint32_t j = 0; j < 3; j++)
+ {
+ PX_ASSERT(weights[j] >= weights[j + 1]);
+ }
+#endif
+ }
+ }
+ else
+ {
+ if (mParserState == PS_Uninitialized)
+ {
+ clear(NULL, NULL);
+ return false;
+ }
+ PX_ALWAYS_ASSERT();
+ }
+
+ return true;
+}
+
+
+
+bool TriangleMesh::hasSkinningVertices()
+{
+ return mSkinnedVertices.size() == mVertices.size();
+}
+
+
+
+// -----------------[ private methods ]----------------------------------
+
+// ----------------------------------------------------------------------
+void TriangleMesh::updateNormals(int subMeshNr)
+{
+ if (mParent != NULL)
+ {
+ mParent->updateNormals(subMeshNr);
+ }
+ else
+ {
+ std::vector<physx::PxVec3> newNormals(mVertices.size(), physx::PxVec3(0.0f, 0.0f, 0.0f));
+
+ const uint32_t start = subMeshNr < 0 ? 0 : mSubMeshes[(uint32_t)subMeshNr].firstIndex;
+ const uint32_t end = subMeshNr < 0 ? (uint32_t)mIndices.size() : mSubMeshes[(uint32_t)subMeshNr].firstIndex + mSubMeshes[(uint32_t)subMeshNr].numIndices;
+ for (uint32_t i = start; i < end; i += 3)
+ {
+ const uint32_t i0 = mIndices[i + 0];
+ const uint32_t i1 = mIndices[i + 1];
+ const uint32_t i2 = mIndices[i + 2];
+
+ physx::PxVec3 n = (mVertices[i1] - mVertices[i0]).cross(mVertices[i2] - mVertices[i0]);
+ newNormals[i0] += n;
+ newNormals[i1] += n;
+ newNormals[i2] += n;
+ }
+
+ for (uint32_t i = 0; i < newNormals.size(); i++)
+ {
+ if (newNormals[i].isZero())
+ {
+ if (i < mNormals.size())
+ {
+ newNormals[i] = mNormals[i];
+ }
+ else
+ {
+ newNormals[i] = physx::PxVec3(0.0f, 1.0f, 0.0f);
+ }
+ }
+ else
+ {
+ newNormals[i].normalize();
+ }
+ }
+
+ mNormals.resize(mVertices.size());
+
+ for (uint32_t i = 0; i < mNormals.size(); i++)
+ {
+ mNormals[i] = newNormals[i];
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::updateTangents()
+{
+ const int useTexCoords = 0;
+ if (mParent != NULL)
+ {
+ mParent->updateTangents();
+ }
+ else if ((mTangents.empty() && mBitangents.empty()) || mTexCoords[useTexCoords].empty())
+ {
+ mTangents.clear();
+ mBitangents.clear();
+ // do nothing, no tangents!
+ }
+ else if (mTangents.size() != mVertices.size() || (mTangents.size() != mBitangents.size() && mVertices.size() > 0 && mTexCoords[useTexCoords].size() == mVertices.size()) )
+ {
+ mTangents.clear();
+ mTangents.resize(mVertices.size(), physx::PxVec3(0.0f, 0.0f, 0.0f));
+ mBitangents.clear();
+ mBitangents.resize(mVertices.size(), physx::PxVec3(0.0f, 0.0f, 0.0f));
+
+ PX_ASSERT(mTangents[0].isZero());
+
+
+ for (uint32_t i = 0; i < mIndices.size(); i += 3)
+ {
+ const physx::PxVec3& p0 = mVertices[mIndices[i + 0]];
+ const physx::PxVec3& p1 = mVertices[mIndices[i + 1]];
+ const physx::PxVec3& p2 = mVertices[mIndices[i + 2]];
+
+ const nvidia::VertexUV& w0 = mTexCoords[useTexCoords][mIndices[i + 0]];
+ const nvidia::VertexUV& w1 = mTexCoords[useTexCoords][mIndices[i + 1]];
+ const nvidia::VertexUV& w2 = mTexCoords[useTexCoords][mIndices[i + 2]];
+
+ const float x1 = p1.x - p0.x;
+ const float x2 = p2.x - p0.x;
+ const float y1 = p1.y - p0.y;
+ const float y2 = p2.y - p0.y;
+ const float z1 = p1.z - p0.z;
+ const float z2 = p2.z - p0.z;
+
+ const float s1 = w1.u - w0.u;
+ const float s2 = w2.u - w0.u;
+ const float t1 = w1.v - w0.v;
+ const float t2 = w2.v - w0.v;
+
+ const float div = (s1 * t2 - s2 * t1);
+ if (div > 0.0f)
+ {
+ const float r = 1.0F / div;
+ physx::PxVec3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
+ physx::PxVec3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
+
+ mTangents[mIndices[i + 0]] += sdir;
+ mTangents[mIndices[i + 1]] += sdir;
+ mTangents[mIndices[i + 2]] += sdir;
+
+ mBitangents[mIndices[i + 0]] += tdir;
+ mBitangents[mIndices[i + 1]] += tdir;
+ mBitangents[mIndices[i + 2]] += tdir;
+ }
+ }
+
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ physx::PxVec3& t = mTangents[i];
+ physx::PxVec3& bt = mBitangents[i];
+
+ // ortho-normalize tangents
+ const physx::PxVec3& n = mNormals[i];
+ t -= n * n.dot(t);
+ t.normalize();
+
+ const physx::PxVec3 nxt = n.cross(t);
+ const float sign = bt.dot(nxt) < 0.0f ? -1.0f : 1.0f;
+ bt = nxt * sign;
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::updateBoneWeights()
+{
+ if (mParent != NULL)
+ {
+ mParent->updateBoneWeights();
+ }
+ else
+ {
+ if (mBoneWeights.size() == mVertices.size())
+ {
+ size_t count = (mVertices.size() + 15) / 16;
+ mNumBoneWeights.resize(count, 0xffffffff);
+
+ for (unsigned int i = 0; i < mVertices.size(); i++)
+ {
+ physx::PxVec4 boneWeight = mBoneWeights[i];
+ int maxWeight = -1;
+ for (int j = 0; j < 4; j++)
+ {
+ maxWeight += boneWeight[j] > 0 ? 1 : 0;
+ PX_ASSERT((maxWeight == j) != (boneWeight[j] == 0.0f));
+ }
+
+ const unsigned int index = i / 16;
+ const unsigned int offset = (i - (index * 16)) * 2;
+ unsigned int newValue = mNumBoneWeights[index];
+ newValue = uint32_t((newValue & ~(0x3 << offset)) | maxWeight << offset);
+ mNumBoneWeights[index] = newValue;
+ }
+ }
+ else
+ {
+ PX_ASSERT(mBoneWeights.empty());
+ mNumBoneWeights.clear();
+ }
+
+ optimizeForRendering();
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::optimizeForRendering()
+{
+ PX_ASSERT(mParent == NULL);
+
+ if (mBoneWeights.size() == mVertices.size() && mVertices.size() > 0)
+ {
+ PX_ASSERT(mMaxBoneIndexExternal >= 0);
+ // basically compress the bone indices
+ std::vector<int> ext2int(mMaxBoneIndexExternal + 1, -1);
+ unsigned int maxBones = 0;
+
+ for (size_t v = 0; v < mVertices.size(); v++)
+ {
+ for (unsigned int k = 0; k < 4; k++)
+ {
+ if (mBoneWeights[v][k] > 0.0f)
+ {
+ unsigned int boneIndex = mBoneIndicesExternal[v * 4 + k];
+
+ if (ext2int[boneIndex] == -1)
+ {
+ ext2int[boneIndex] = (int32_t)maxBones++;
+ }
+ }
+ }
+ }
+
+ mBoneIndicesInternal.resize(mBoneIndicesExternal.size(), 0);
+
+ for (size_t v = 0; v < mVertices.size(); v++)
+ {
+ for (unsigned int k = 0; k < 4; k++)
+ {
+ unsigned int index = (unsigned int)v * 4 + k;
+ if (mBoneWeights[v][k] > 0.0f)
+ {
+ mBoneIndicesInternal[index] = (unsigned short)ext2int[mBoneIndicesExternal[index]];
+ }
+ else
+ {
+ mBoneIndicesInternal[index] = 0;
+ }
+ }
+ }
+ mMaxBoneIndexInternal = (int)maxBones - 1;
+
+ mBoneMappingInt2Ext.resize(maxBones);
+ for (size_t i = 0; i < ext2int.size(); i++)
+ {
+ if (ext2int[i] >= 0)
+ {
+ PX_ASSERT(ext2int[i] < (int)mBoneMappingInt2Ext.size());
+ mBoneMappingInt2Ext[(uint32_t)ext2int[i]] = (int)i;
+ }
+ }
+ }
+
+
+#if 0 // PH: too advanced yet, and only a first draft
+
+ for (size_t submeshIndex = 0; submeshIndex < mSubMeshes.size(); submeshIndex++)
+ {
+ TriangleSubMesh& submesh = mSubMeshes[submeshIndex];
+
+ if (submesh.maxBonesShader > 0)
+ {
+ unsigned int indicesMapped = 0;
+ //while (indicesMapped < submesh.numIndices)
+ {
+ //OptimizedRenderData renderData;
+
+ std::vector<int> currentBoneMapping(mMaxBoneIndex + 1, -1);
+ unsigned int currentBoneMappingSize = 0;
+
+ for (unsigned int i = 0; i < submesh.numIndices; i += 3)
+ {
+ // collect all the bone indices
+ unsigned int boneIndices[12];
+ unsigned int numBoneIndices = 0;
+
+ for (int j = 0; j < 3; j++)
+ {
+ const unsigned int vertexIndex = mIndices[submesh.firstIndex + i + j];
+ for (int k = 0; k < 4; k++)
+ {
+ if (mBoneWeights[vertexIndex][k] > 0.0f)
+ {
+ unsigned int boneIndex = mBoneIndices[vertexIndex * 4 + k];
+
+ bool found = false;
+
+ for (unsigned int l = 0; l < numBoneIndices; l++)
+ {
+ if (boneIndices[l] == boneIndex)
+ {
+ found = true;
+ }
+ }
+
+ if (!found)
+ {
+ boneIndices[numBoneIndices++] = boneIndex;
+ }
+ }
+ }
+ }
+
+ // now let's see if they fit into the current mapping
+ unsigned int notInMapping = 0;
+ for (unsigned int j = 0; j < numBoneIndices; j++)
+ {
+ if (currentBoneMapping[boneIndices[j]] < 0)
+ {
+ notInMapping++;
+ }
+ }
+
+ if (notInMapping == 0)
+ {
+ //indicesMapped += 3;
+ }
+ else if (notInMapping < submesh.maxBonesShader - currentBoneMappingSize)
+ {
+ // add them to the mapping
+ for (unsigned int j = 0; j < numBoneIndices; j++)
+ {
+ if (currentBoneMapping[boneIndices[j]] < 0)
+ {
+ currentBoneMapping[boneIndices[j]] = currentBoneMappingSize++;
+ }
+ }
+
+ //indicesMapped += 3;
+ }
+ else
+ {
+ // we're full I guess
+ int a = 0;
+ a = a;
+ }
+ }
+ }
+
+ int a = 0;
+ a = a;
+ }
+ }
+#endif
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::complete(bool useCustomChannels)
+{
+ for (uint32_t i = 0; i < PC_NUM_CHANNELS; i++)
+ {
+ std::vector<PaintedVertex> &channel = mPaintChannels[i];
+ if (useCustomChannels && (channel.size() == 0 || channel.size() != mVertices.size()))
+ {
+ channel.clear();
+ switch (i)
+ {
+ case PC_MAX_DISTANCE:
+ channel.resize(mVertices.size(), PaintedVertex(-0.1f));
+ break;
+ case PC_COLLISION_DISTANCE:
+ channel.resize(mVertices.size(), PaintedVertex(-1.1f));
+ break;
+ case PC_LATCH_TO_NEAREST_SLAVE:
+ channel.resize(mVertices.size(), PaintedVertex(0, 0.0f));
+ break;
+ case PC_LATCH_TO_NEAREST_MASTER:
+ channel.resize(mVertices.size(), PaintedVertex(0xffffffff, 0.0f));
+ break;
+ default:
+#if PX_WINDOWS_FAMILY
+ DebugBreak();
+#endif
+ break;
+ }
+ }
+ else if (!useCustomChannels)
+ {
+ channel.clear();
+ channel.resize(0);
+ }
+ }
+
+ if (mSubMeshes.size() < 1)
+ {
+ mSubMeshes.resize(1);
+ mSubMeshes[0].init();
+ mSubMeshes[0].firstIndex = 0;
+ mSubMeshes[0].numIndices = (uint32_t)mIndices.size();
+ mSubMeshes[0].name = "One Single Mesh";
+ }
+ else if (mSubMeshes.size() == 1)
+ {
+ mSubMeshes[0].firstIndex = 0;
+ mSubMeshes[0].numIndices = (uint32_t)mIndices.size();
+ }
+ else
+ {
+ // Make sure all submeshes have distinct areas of the index buffers
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ if (mSubMeshes[i].name.empty())
+ {
+ char buf[32];
+ physx::shdfnd::snprintf(buf, 32, "Submesh %d", i);
+ mSubMeshes[i].name = buf;
+ }
+ else
+ {
+ if (mSubMeshes[i].name.find("_invisible") != std::string::npos)
+ {
+ mSubMeshes[i].invisible = true;
+ }
+ }
+ for (uint32_t j = i + 1; j < mSubMeshes.size(); j++)
+ {
+ PX_ASSERT(mSubMeshes[i].firstIndex + mSubMeshes[i].numIndices <= mSubMeshes[j].firstIndex ||
+ mSubMeshes[j].firstIndex + mSubMeshes[j].numIndices <= mSubMeshes[i].firstIndex);
+ }
+ }
+ // Merge submeshes
+ std::sort(mSubMeshes.begin(), mSubMeshes.end());
+ std::vector<uint32_t> newIndices;
+ newIndices.resize(mIndices.size());
+ uint32_t offset = 0;
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ uint32_t indexStart = mSubMeshes[i].firstIndex;
+ for (uint32_t j = 0; j < mSubMeshes[i].numIndices; j++)
+ {
+ newIndices[offset + j] = mIndices[indexStart + j];
+ }
+ mSubMeshes[i].firstIndex = offset;
+ offset += mSubMeshes[i].numIndices;
+ }
+ mIndices.resize(newIndices.size());
+ memcpy(&mIndices[0], &newIndices[0], sizeof(uint32_t) * newIndices.size());
+
+ for (size_t i = mSubMeshes.size() - 1; i > 0; i--)
+ {
+ if (mSubMeshes[i].name == mSubMeshes[i - 1].name && mSubMeshes[i].materialName == mSubMeshes[i - 1].materialName)
+ {
+ mSubMeshes[i - 1].numIndices += mSubMeshes[i].numIndices;
+ mSubMeshes[i].numIndices = 0;
+ }
+ }
+ for (uint32_t i = (uint32_t)mSubMeshes.size() - 1; i > 0; i--)
+ {
+ if (mSubMeshes[i].numIndices == 0)
+ {
+ mSubMeshes.erase(mSubMeshes.begin() + i);
+ }
+ }
+ }
+
+ if (mNormals.size() != mVertices.size())
+ {
+ updateNormals(-1);
+ }
+
+ if (mTangents.size() != mVertices.size())
+ {
+ updateTangents();
+ }
+
+ updateBoneWeights();
+ updateBounds();
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::hasRandomColors(size_t howmany)
+{
+ for (size_t i = mRandomColors.size(); i < howmany; i++)
+ {
+ const float h = physx::shdfnd::rand(0.0f, 359.9f);
+ const float s = physx::shdfnd::rand(0.5f, 1.0f);
+ const float v = physx::shdfnd::rand(0.5f, 1.0f);
+
+ const uint32_t hi = (uint32_t)physx::PxFloor(h / 60.0f) % 6;
+ const float f = (h / 60.0f - physx::PxFloor(h / 60.0f));
+ const float p = v * (1.0f - s);
+ const float q = v * (1.0f - f * s);
+ const float t = v * (1.0f - (1.0f - f) * s);
+
+ float r, g, b;
+ switch (hi)
+ {
+ case 0:
+ r = v;
+ g = t;
+ b = p;
+ break;
+ case 1:
+ r = q;
+ g = v;
+ b = p;
+ break;
+ case 2:
+ r = p;
+ g = v;
+ b = t;
+ break;
+ case 3:
+ r = p;
+ g = q;
+ b = v;
+ break;
+ case 4:
+ r = t;
+ g = p;
+ b = v;
+ break;
+ case 5:
+ r = v;
+ g = p;
+ b = q;
+ break;
+ default:
+ r = g = b = 0.0f;
+ break;
+ }
+ union
+ {
+ uint32_t color;
+ uint8_t components[4];
+ };
+ color = 0;
+ components[0] = (uint8_t)(r * 255);
+ components[1] = (uint8_t)(g * 255);
+ components[2] = (uint8_t)(b * 255);
+ mRandomColors.push_back(color);
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::updateSubmeshInfo()
+{
+ uint32_t selectionChanged = 0;
+ uint32_t numNotSelected = 0;
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ selectionChanged += (mSubMeshes[i].selected != mSubMeshes[i].selectionActivated) ? 1 : 0;
+ numNotSelected += mSubMeshes[i].selected ? 0 : 1;
+ }
+ if (mActiveSubmeshVertices.size() == mVertices.size() && selectionChanged == 0)
+ {
+ return;
+ }
+
+ mActiveSubmeshVertices.clear();
+ if (numNotSelected == 0)
+ {
+ mActiveSubmeshVertices.resize(mVertices.size(), true);
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ mSubMeshes[i].selectionActivated = mSubMeshes[i].selected;
+ }
+
+ return;
+ }
+
+ mActiveSubmeshVertices.resize(mVertices.size(), false);
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ if (mSubMeshes[i].selected)
+ {
+ TriangleSubMesh& sm = mSubMeshes[i];
+ for (uint32_t j = sm.firstIndex; j < sm.firstIndex + sm.numIndices; j++)
+ {
+ mActiveSubmeshVertices[mIndices[j]] = true;
+ }
+ }
+ mSubMeshes[i].selectionActivated = mSubMeshes[i].selected;
+ }
+}
+
+//------------------------------------------------------------------------------------
+int TriangleMesh::addSplitVert(int vertNr0, int vertNr1)
+{
+ int v0, v1;
+ if (vertNr0 < vertNr1)
+ {
+ v0 = vertNr0;
+ v1 = vertNr1;
+ }
+ else
+ {
+ v0 = vertNr1;
+ v1 = vertNr0;
+ }
+
+ // do we already have the split vert?
+ int i = mVertexFirstSplit[(uint32_t)v0];
+ while (i >= 0)
+ {
+ TriangleEdgeSplit& s = mVertexSplits[(uint32_t)i];
+ if (s.adjVertNr == v1)
+ {
+ return s.newVertNr;
+ }
+ i = s.next;
+ }
+
+ TriangleEdgeSplit sNew;
+ sNew.adjVertNr = v1;
+ sNew.newVertNr = (int32_t)mVertices.size();
+
+ // create interpolated vertex
+ physx::PxVec3 v = (mVertices[(uint32_t)v0] + mVertices[(uint32_t)v1]) * 0.5f;
+ mVertices.push_back(v);
+ if (mSkinnedVertices.size() == mVertices.size() - 1)
+ {
+ physx::PxVec3 sv = (mSkinnedVertices[(uint32_t)v0] + mSkinnedVertices[(uint32_t)v1]) * 0.5f;
+ mSkinnedVertices.push_back(sv);
+ }
+
+ for (uint32_t j = 0; j < PC_NUM_CHANNELS; j++)
+ {
+ // ok, special case, if it's collision distance, and one of them is < -1 then the new is as well
+ float v = 0.0f;
+ unsigned int u = 0;
+ if (j == PC_LATCH_TO_NEAREST_SLAVE || j == PC_LATCH_TO_NEAREST_MASTER)
+ {
+ u = mPaintChannels[j][(uint32_t)v0].paintValueU32 | mPaintChannels[j][(uint32_t)v1].paintValueU32; // bitwise OR
+ }
+ else if (j == PC_COLLISION_DISTANCE && (mPaintChannels[PC_COLLISION_DISTANCE][(uint32_t)v0].paintValueF32 == -1.1f || mPaintChannels[PC_COLLISION_DISTANCE][(uint32_t)v1].paintValueF32 == -1.1f))
+ {
+ v = -1.1f;
+ }
+ else
+ {
+ v = (mPaintChannels[j][(uint32_t)v0].paintValueF32 + mPaintChannels[j][(uint32_t)v1].paintValueF32) * 0.5f;
+ }
+ mPaintChannels[j].push_back(PaintedVertex(u, v));
+ }
+
+ for (uint32_t j = 0; j < NUM_TEXCOORDS; j++)
+ {
+ if (mTexCoords[j].empty())
+ {
+ continue;
+ }
+
+ nvidia::VertexUV newTc;
+ newTc.u = (mTexCoords[j][(uint32_t)v0].u + mTexCoords[j][(uint32_t)v1].u) * 0.5f;
+ newTc.v = (mTexCoords[j][(uint32_t)v0].v + mTexCoords[j][(uint32_t)v1].v) * 0.5f;
+ mTexCoords[j].push_back(newTc);
+ }
+
+ int newVertNr = (int)mVertices.size() - 1;
+ PX_UNUSED(newVertNr);
+
+ // mix bone weights - kind of tricky
+#if 1
+ if (!mBoneIndicesExternal.empty())
+ {
+ uint16_t newIndices[4];
+ physx::PxVec4 newWeights;
+ for (int j = 0; j < 4; j++)
+ {
+ newIndices[j] = 0;
+ newWeights[j] = 0.0f;
+ }
+
+ PX_ASSERT(!mBoneWeights.empty());
+
+ for (uint32_t k = 0; k < 2; k++)
+ {
+ uint32_t v = uint32_t((k == 0) ? v0 : v1);
+ for (uint32_t j = 0; j < 4; j++)
+ {
+ uint16_t bi = mBoneIndicesExternal[4 * v + j];
+ float bw = mBoneWeights[v][j];
+
+ // do we have the index already? If so just average the weights
+ int k = 0;
+ while (k < 4 && newIndices[(uint32_t)k] != bi)
+ {
+ k++;
+ }
+ if (k < 4)
+ {
+ newWeights[k] = (newWeights[(uint32_t)k] + bw) * 0.5f;
+ continue;
+ }
+ // else insert the pair at the right place
+ k = 3;
+ while (k >= 0 && newWeights[(uint32_t)k] < bw)
+ {
+ if (k < 3)
+ {
+ newWeights[(uint32_t)k + 1] = newWeights[(uint32_t)k];
+ newIndices[(uint32_t)k + 1] = newIndices[(uint32_t)k];
+ }
+ k--;
+ }
+ k++;
+ if (k < 4)
+ {
+ newWeights[(uint32_t)k] = bw;
+ newIndices[(uint32_t)k] = bi;
+ }
+ }
+ }
+
+ // copy indices to the new vertex
+ float sum = 0.0f;
+ for (int j = 0; j < 4; j++)
+ {
+ sum += newWeights[j];
+ }
+
+ if (sum > 0.0f)
+ {
+ sum = 1.0f / sum;
+ }
+
+ for (int j = 0; j < 4; j++)
+ {
+ mBoneIndicesExternal.push_back(newIndices[j]);
+ }
+ mBoneWeights.push_back(newWeights * sum);
+ }
+
+#else
+ mFirstAssignment.resize(newVertNr + 2);
+ mFirstAssignment[newVertNr] = mBoneAssignments.size();
+
+ std::vector<BoneAssignment> assigns;
+ BoneAssignment a;
+
+ for (int i = 0; i < 2; i++)
+ {
+ int v = (i == 0) ? v0 : v1;
+ int first = mFirstAssignment[v];
+ int num = mFirstAssignment[v + 1] - first;
+ for (int j = 0; j < num; j++)
+ {
+ a.boneNr = mBoneAssignments[first + j];
+ a.weight = mBoneWeights[first + j];
+ int k = 0;
+ while (k < (int)assigns.size() && a.boneNr != assigns[k].boneNr)
+ {
+ k++;
+ }
+ if (k < (int)assigns.size())
+ {
+ assigns[k].weight = (assigns[k].weight + a.weight) * 0.5f;
+ }
+ else
+ {
+ assigns.pushBack(a);
+ }
+ }
+ }
+ // select the ones with the biggest weights
+ std::sort(assigns.begin(), assigns.end());
+ const int maxAssignments = 4;
+ int first = assigns.size() - maxAssignments;
+ if (first < 0)
+ {
+ first = 0;
+ }
+ float sum = 0.0f;
+ for (int i = first; i < (int)assigns.size(); i++)
+ {
+ sum += assigns[i].weight;
+ }
+ PX_ASSERT(sum != 0.0f);
+ for (int i = first; i < (int)assigns.size(); i++)
+ {
+ mBoneAssignments.pushBack(assigns[i].boneNr);
+ mBoneWeights.pushBack(assigns[i].weight / sum);
+ }
+ mFirstAssignment[newVertNr + 1] = mBoneAssignments.size();
+#endif
+
+ // add split vertex info
+ sNew.next = mVertexFirstSplit[(uint32_t)v0];
+ mVertexFirstSplit[(uint32_t)v0] = (int32_t)mVertexSplits.size();
+ mVertexSplits.push_back(sNew);
+ return sNew.newVertNr;
+}
+
+} // namespace Samples
diff --git a/APEX_1.4/shared/external/src/UserAllocator.cpp b/APEX_1.4/shared/external/src/UserAllocator.cpp
new file mode 100644
index 00000000..e9343c4a
--- /dev/null
+++ b/APEX_1.4/shared/external/src/UserAllocator.cpp
@@ -0,0 +1,239 @@
+/*
+ * 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 <stdio.h>
+#include "UserAllocator.h"
+
+#include "MemTracker.h"
+
+#include "PxAssert.h"
+
+#pragma warning(disable:4100 4152)
+
+#ifdef USE_MEM_TRACKER
+
+static const char* USERALLOCATOR = "UserAllocator";
+#endif
+
+/*============ UserPxAllocator ============*/
+
+#ifdef USE_MEM_TRACKER
+
+#include <windows.h>
+
+class MemMutex
+{
+public:
+ MemMutex(void);
+ ~MemMutex(void);
+
+public:
+ // Blocking Lock.
+ void Lock(void);
+
+ // Unlock.
+ void Unlock(void);
+
+private:
+ CRITICAL_SECTION m_Mutex;
+};
+
+//==================================================================================
+MemMutex::MemMutex(void)
+{
+ InitializeCriticalSection(&m_Mutex);
+}
+
+//==================================================================================
+MemMutex::~MemMutex(void)
+{
+ DeleteCriticalSection(&m_Mutex);
+}
+
+//==================================================================================
+// Blocking Lock.
+//==================================================================================
+void MemMutex::Lock(void)
+{
+ EnterCriticalSection(&m_Mutex);
+}
+
+//==================================================================================
+// Unlock.
+//==================================================================================
+void MemMutex::Unlock(void)
+{
+ LeaveCriticalSection(&m_Mutex);
+}
+
+static inline MemMutex& gMemMutex()
+{
+ static MemMutex sMemMutex;
+ return sMemMutex;
+}
+
+static size_t getThreadId(void)
+{
+ return GetCurrentThreadId();
+}
+
+static bool memoryReport(MEM_TRACKER::MemTracker *memTracker,MEM_TRACKER::MemoryReportFormat format,const char *fname,bool reportAllLeaks) // detect memory leaks and, if any, write out a report to the filename specified.
+{
+ bool ret = false;
+
+ size_t leakCount;
+ size_t leaked = memTracker->detectLeaks(leakCount);
+ if ( leaked )
+ {
+ uint32_t dataLen;
+ void *mem = memTracker->generateReport(format,fname,dataLen,reportAllLeaks);
+ if ( mem )
+ {
+ FILE *fph = fopen(fname,"wb");
+ fwrite(mem,dataLen,1,fph);
+ fclose(fph);
+ memTracker->releaseReportMemory(mem);
+ }
+ ret = true; // it leaked memory!
+ }
+ return ret;
+}
+
+class ScopedLock
+{
+public:
+ ScopedLock(MemMutex &mutex, bool enabled) : mMutex(mutex), mEnabled(enabled)
+ {
+ if (mEnabled) mMutex.Lock();
+ }
+ ~ScopedLock()
+ {
+ if (mEnabled) mMutex.Unlock();
+ }
+
+private:
+ ScopedLock();
+ ScopedLock(const ScopedLock&);
+ ScopedLock& operator=(const ScopedLock&);
+
+ MemMutex& mMutex;
+ bool mEnabled;
+};
+
+#define SCOPED_TRACKER_LOCK_IF(USE_TRACKER) ScopedLock lock(gMemMutex(), (USE_TRACKER))
+#define TRACKER_CALL_IF(USE_TRACKER, CALL) if ((USE_TRACKER)) { (CALL); }
+
+void releaseAndReset(MEM_TRACKER::MemTracker*& memTracker)
+{
+ MEM_TRACKER::releaseMemTracker(memTracker);
+ memTracker = NULL;
+}
+
+#else
+
+#define SCOPED_TRACKER_LOCK_IF(USE_TRACKER)
+#define TRACKER_CALL_IF(USE_TRACKER, CALL)
+
+#endif
+
+int UserPxAllocator::gMemoryTrackerClients = 0;
+MEM_TRACKER::MemTracker *UserPxAllocator::mMemoryTracker=NULL;
+
+unsigned int UserPxAllocator::mNumAllocations = 0;
+unsigned int UserPxAllocator::mNumFrees = 0;
+
+UserPxAllocator::UserPxAllocator(const char* context,
+ const char* /*dllName*/,
+ bool useTrackerIfSupported /* = true */)
+ : mContext(context)
+ , mMemoryAllocated(0)
+ , mUseTracker(useTrackerIfSupported)
+{
+ SCOPED_TRACKER_LOCK_IF(mUseTracker);
+ TRACKER_CALL_IF(mUseTracker && NULL == mMemoryTracker, mMemoryTracker = MEM_TRACKER::createMemTracker());
+ TRACKER_CALL_IF(trackerEnabled(), ++gMemoryTrackerClients);
+}
+
+void* UserPxAllocator::allocate(size_t size, const char* typeName, const char* filename, int line)
+{
+ void* ret = 0;
+
+ PX_UNUSED(typeName);
+ PX_UNUSED(filename);
+ PX_UNUSED(line);
+ SCOPED_TRACKER_LOCK_IF(trackerEnabled());
+
+#if PX_WINDOWS_FAMILY
+ ret = ::_aligned_malloc(size, 16);
+#elif PX_ANDROID || PX_LINUX_FAMILY
+ /* Allocate size + (15 + sizeof(void*)) bytes and shift pointer further, write original address in the beginning of block.*/
+ /* Weirdly, memalign sometimes returns unaligned address */
+ // ret = ::memalign(size, 16);
+ size_t alignment = 16;
+ void* originalRet = ::malloc(size + 2*alignment + sizeof(void*));
+ // find aligned location
+ ret = ((char*)originalRet) + 2 * alignment - (((size_t)originalRet) & 0xF);
+ // write block address prior to aligned position, so it could be possible to free it later
+ ::memcpy((char*)ret - sizeof(originalRet), &originalRet, sizeof(originalRet));
+#else
+ ret = ::malloc(size);
+#endif
+
+ TRACKER_CALL_IF(trackerEnabled(), mMemoryAllocated += size);
+ TRACKER_CALL_IF(trackerEnabled(), mMemoryTracker->trackAlloc(getThreadId(),ret, size, MEM_TRACKER::MT_MALLOC, USERALLOCATOR, typeName, filename, (uint32_t)line));
+
+ // this should probably be a atomic increment
+ mNumAllocations++;
+
+ return ret;
+}
+
+void UserPxAllocator::deallocate(void* memory)
+{
+ SCOPED_TRACKER_LOCK_IF(trackerEnabled());
+
+ if (memory)
+ {
+#if PX_WINDOWS_FAMILY
+ ::_aligned_free(memory);
+#elif PX_ANDROID || PX_LINUX_FAMILY
+ // Looks scary, but all it does is getting original unaligned block address back from 4/8 bytes (depends on pointer size) prior to <memory> pointer and frees this memory
+ void* originalPtr = (void*)(*(size_t*)((char*)memory - sizeof(void*)));
+ ::free(originalPtr);
+#else
+ ::free(memory);
+#endif
+
+ // this should probably be a atomic decrement
+ mNumFrees++;
+ }
+
+ TRACKER_CALL_IF(trackerEnabled(), mMemoryTracker->trackFree(getThreadId(), memory, MEM_TRACKER::MT_FREE, USERALLOCATOR, __FILE__, __LINE__));
+}
+
+
+UserPxAllocator::~UserPxAllocator()
+{
+ SCOPED_TRACKER_LOCK_IF(trackerEnabled());
+ TRACKER_CALL_IF(trackerEnabled() && (0 == --gMemoryTrackerClients), releaseAndReset(mMemoryTracker));
+}
+
+bool UserPxAllocator::dumpMemoryLeaks(const char* filename)
+{
+ bool leaked = false;
+
+ PX_UNUSED(filename);
+ TRACKER_CALL_IF(mMemoryTracker, leaked = memoryReport(mMemoryTracker, MEM_TRACKER::MRF_SIMPLE_HTML, filename, true));
+
+ return leaked;
+}
+
+#undef SCOPED_TRACKER_LOCK_IF
+#undef TRACKER_CALL_IF \ No newline at end of file
diff --git a/APEX_1.4/shared/external/src/UserErrorCallback.cpp b/APEX_1.4/shared/external/src/UserErrorCallback.cpp
new file mode 100644
index 00000000..9cccd073
--- /dev/null
+++ b/APEX_1.4/shared/external/src/UserErrorCallback.cpp
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2008-2015, NVIDIA CORPORATION. All rights reserved.
+ *
+ * NVIDIA CORPORATION and its licensors retain all intellectual property
+ * and proprietary rights in and to this software, related documentation
+ * and any modifications thereto. Any use, reproduction, disclosure or
+ * distribution of this software and related documentation without an express
+ * license agreement from NVIDIA CORPORATION is strictly prohibited.
+ */
+
+
+#include "ApexDefs.h"
+#include "UserErrorCallback.h"
+#include "PxAssert.h"
+#include <algorithm>
+
+#if PX_WINDOWS_FAMILY
+#define NOMINMAX
+#include <windows.h>
+#include <cstdio>
+#include <ctime>
+#endif
+
+#if PX_X360
+#include <stdarg.h>
+#include <cstdio>
+#endif
+
+UserErrorCallback* UserErrorCallback::s_instance /* = NULL */;
+
+UserErrorCallback::UserErrorCallback(const char* filename, const char* mode, bool header, bool reportErrors)
+ : mNumErrors(0)
+ , mOutFile(NULL)
+ , mOutFileName(filename == NULL ? "" : filename)
+ , mOutFileMode(mode)
+ , mOutFileHeader(header)
+ , mReportErrors(reportErrors)
+{
+ mFirstErrorBuffer[0] = '\0';
+ mFirstErrorBufferUpdated = false;
+ if (!s_instance)
+ {
+ s_instance = this;
+ }
+ else if (s_instance->mOutFileName.empty())
+ {
+ // replace stub error handler
+ delete s_instance;
+ s_instance = this;
+ }
+
+ // initialize the filtered messages.
+ //mFilteredMessages.insert("");
+ addFilteredMessage("CUDA not available", true);
+ addFilteredMessage("CUDA is not available", true);
+
+ // filter particle debug visualization warnings from PhysX when APEX is using GPU particles in device exclusive mode
+ // the message was changed in 3.3, so adding both
+ addFilteredMessage("Operation not allowed in device exclusive mode", true);
+ addFilteredMessage("Receiving particles through host interface not supported device exclusive mode", true);
+
+
+ // Filter out a harmless particle warning from PhysX 3.2.4
+ addFilteredMessage("Adding particles before the first simulation step is not supported.", true);
+}
+
+const char* sanitizeFileName(const char* fullPath)
+{
+ return std::max(::strrchr(fullPath, '\\'), ::strrchr(fullPath, '/')) + 1;
+}
+
+bool UserErrorCallback::messageFiltered(const char * code, const char * msg)
+{
+ if (0 == strcmp(code, "info"))
+ {
+ return true;
+ }
+
+ std::map<std::string, bool*>::iterator found = mFilteredMessages.find(msg);
+ if (found != mFilteredMessages.end())
+ {
+ if (found->second != NULL)
+ {
+ // set the trigger
+ *(found->second) = true;
+ }
+ return true;
+ }
+
+ for (size_t i = 0; i < mFilteredParts.size(); i++)
+ {
+ const char* fmsg = mFilteredParts[i].first.c_str();
+ if (strstr(msg, fmsg) != NULL)
+ {
+ if (mFilteredParts[i].second != NULL)
+ {
+ // set the trigger
+ *(mFilteredParts[i].second) = true;
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#if PX_WINDOWS_FAMILY
+
+void logf(FILE* file, const char* format, ...)
+{
+ // size = 2047 from SampleApexApplication::printMessageUser()
+ // '\n' appended by UserErrorCallback::printError()
+ // '\0' appended by vsnprintf()
+ enum { BUFFER_SIZE = 2049 };
+ char buffer[BUFFER_SIZE];
+
+ va_list args;
+ va_start(args, format);
+ vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+
+ // Output to file
+ fputs(buffer, file);
+
+ // Output to debug stream
+ ::OutputDebugString(buffer);
+}
+
+#else
+
+#define logf(file, format, ...) fprintf(file, format, __VA_ARGS__)
+
+#endif /* PX_WINDOWS_FAMILY */
+
+void UserErrorCallback::printError(const char* message, const char* errorCode, const char* file, int line)
+{
+ if (mOutFile == NULL)
+ {
+ openFile();
+ }
+
+ // do not count eDEBUG_INFO "info" messages as errors.
+ // errors trigger benchmarks to be exited.
+ if (!messageFiltered(errorCode, message))
+ {
+ mNumErrors++;
+ }
+
+ // if this is the first error while running a benchmark
+ if (mNumErrors == 1 && !mFirstErrorBufferUpdated)
+ {
+ if (errorCode)
+ {
+ strcpy(mFirstErrorBuffer, errorCode);
+ strcat(mFirstErrorBuffer, ": ");
+ }
+ if (file)
+ {
+ char lineNumBuf[20];
+ strcat(mFirstErrorBuffer, sanitizeFileName(file));
+ strcat(mFirstErrorBuffer, ":");
+ sprintf(lineNumBuf, "%d: ", line);
+ strcat(mFirstErrorBuffer, lineNumBuf);
+ }
+ if (message)
+ {
+ strcat(mFirstErrorBuffer, message);
+ strcat(mFirstErrorBuffer, "\n");
+ }
+ mFirstErrorBufferUpdated = true;
+ }
+
+ if (mOutFile == NULL)
+ {
+ return;
+ }
+
+ if (errorCode != NULL)
+ {
+ logf(mOutFile, "\n%s: ", errorCode);
+ }
+
+ if (file != NULL)
+ {
+ logf(mOutFile, "%s:%d:\n", sanitizeFileName(file), line);
+ }
+
+ if (message != NULL)
+ {
+ logf(mOutFile, "%s\n", message);
+ }
+
+ fflush(mOutFile);
+}
+
+int UserErrorCallback::getNumErrors(void)
+{
+ return((int)mNumErrors);
+}
+
+void UserErrorCallback::clearErrorCounter(void)
+{
+ mNumErrors = 0;
+ mFirstErrorBuffer[0] = '\0';
+}
+
+const char* UserErrorCallback::getFirstEror(void)
+{
+ return(mFirstErrorBuffer);
+}
+
+void UserErrorCallback::addFilteredMessage(const char* msg, bool fullMatch, bool* trigger)
+{
+ if (fullMatch)
+ {
+ mFilteredMessages.insert(std::pair<std::string, bool*>(msg, trigger));
+ }
+ else
+ {
+ mFilteredParts.push_back(std::pair<std::string, bool*>(msg, trigger));
+ }
+}
+
+
+
+void UserErrorCallback::reportErrors(bool enabled)
+{
+ PX_UNUSED(enabled);
+ mReportErrors = false;
+}
+
+
+
+void UserErrorCallback::reportError(physx::PxErrorCode::Enum e, const char* message, const char* file, int line)
+{
+ const char* errorCode = NULL;
+
+ switch (e)
+ {
+ case physx::PxErrorCode::eINVALID_PARAMETER:
+ errorCode = "invalid parameter";
+ break;
+ case physx::PxErrorCode::eINVALID_OPERATION:
+ errorCode = "invalid operation";
+ break;
+ case physx::PxErrorCode::eOUT_OF_MEMORY:
+ errorCode = "out of memory";
+ break;
+ case physx::PxErrorCode::eDEBUG_INFO:
+ errorCode = "info";
+ break;
+ case physx::PxErrorCode::eDEBUG_WARNING:
+ errorCode = "warning";
+ break;
+ default:
+ errorCode = "unknown error";
+ break;
+ }
+
+ PX_ASSERT(errorCode != NULL);
+ if (errorCode != NULL)
+ {
+ printError(message, errorCode, file, line);
+ }
+}
+
+void UserErrorCallback::printError(physx::PxErrorCode::Enum code, const char* file, int line, const char* fmt, ...)
+{
+ char buff[2048];
+ va_list arg;
+ va_start(arg, fmt);
+ physx::shdfnd::vsnprintf(buff, sizeof(buff), fmt, arg);
+ va_end(arg);
+ reportError(code, buff, file, line);
+}
+
+
+void UserErrorCallback::openFile()
+{
+ if (mOutFile != NULL)
+ {
+ return;
+ }
+
+ if (mOutFileName.empty())
+ {
+ return;
+ }
+
+ PX_ASSERT(mNumErrors == 0);
+
+ if (mOutFileMode == NULL)
+ {
+ mOutFileMode = "w";
+ }
+
+ mOutFile = fopen(mOutFileName.c_str(), mOutFileMode);
+
+ // if that failed, try the temp location on windows
+#if PX_WINDOWS_FAMILY
+ if (!mOutFile)
+ {
+ DWORD pathLen = ::GetTempPathA(0, NULL);
+ if (pathLen)
+ {
+ char *pathStr = (char*)malloc(pathLen);
+ GetTempPathA(pathLen, pathStr);
+ std::string tmpPath(pathStr);
+ tmpPath.append(mOutFileName);
+ mOutFileName = tmpPath;
+ free(pathStr);
+
+ ::fopen_s(&mOutFile, mOutFileName.c_str(), mOutFileMode);
+ }
+ }
+#endif
+
+ if (mOutFile && mOutFileHeader)
+ {
+ fprintf(mOutFile,
+ "\n\n"
+ "-------------------------------------\n"
+ "-- new error stream\n");
+#if PX_WINDOWS_FAMILY
+ char timeBuf[30];
+ time_t rawTime;
+ time(&rawTime);
+ ctime_s(timeBuf, sizeof(timeBuf), &rawTime);
+ fprintf(mOutFile,
+ "--\n"
+ "-- %s", timeBuf);
+#endif
+ fprintf(mOutFile,
+ "-------------------------------------\n\n");
+
+ fflush(mOutFile);
+ }
+}
+
+UserErrorCallback::~UserErrorCallback()
+{
+#if PX_WINDOWS_FAMILY
+ if (mNumErrors > 0 && mReportErrors)
+ {
+ std::string errorString;
+ char buf[64];
+ physx::shdfnd::snprintf(buf, sizeof(buf), "The error callback captured %d errors", mNumErrors);
+
+ errorString.append(buf);
+ if (mOutFile != stdout && mOutFile != 0)
+ {
+ errorString.append(" in ");
+ errorString.append(mOutFileName);
+ }
+
+ ::MessageBoxA(NULL, errorString.c_str(), "UserErrorCallback", MB_OK);
+ }
+#else
+ PX_ASSERT(mNumErrors == 0);
+#endif
+
+ if (mOutFile != stdout && mOutFile != 0)
+ {
+ fclose(mOutFile);
+ mOutFile = 0;
+ }
+}
diff --git a/APEX_1.4/shared/external/src/htmltable.cpp b/APEX_1.4/shared/external/src/htmltable.cpp
new file mode 100644
index 00000000..7bd26a1e
--- /dev/null
+++ b/APEX_1.4/shared/external/src/htmltable.cpp
@@ -0,0 +1,3663 @@
+/*
+ * 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 "htmltable.h"
+
+#if PX_WINDOWS_FAMILY // only compile this source code for windows!
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+#include <stdarg.h>
+#include <string>
+#include <vector>
+
+#include <direct.h>
+
+#pragma warning(disable:4267 4127)
+
+#define USE_CPP 0
+#define USE_EXCEL 0
+
+#pragma warning(disable:4996 4702) // Disable Microsof'ts freaking idiotic 'warnings' not to use standard ANSI C stdlib and string functions!
+
+#include "htmltable.h"
+
+#if PX_LINUX
+#define stricmp(a,b) strcasecmp(a,b)
+#define _vsnprintf vsnprintf
+#define _mkdir(a) mkdir(a, 0)
+#endif
+
+namespace HTMLTABLE_NVSHARE
+{
+
+using namespace nvidia;
+
+#pragma warning(disable:4100)
+
+class MemHeader
+{
+public:
+ MemHeader *mNext;
+ MemHeader *mPrevious;
+ size_t mLength;
+ const char *mTag;
+ const char *mFile;
+ int mLineno;
+};
+
+class MemTracker
+{
+public:
+ MemTracker(void)
+ {
+ mCount = 0;
+ mTotal = 0;
+ mRoot = 0;
+ }
+
+ ~MemTracker(void)
+ {
+ }
+
+ void * memAlloc(size_t len,const char *tag,const char *file,int lineno)
+ {
+ MemHeader *mh = (MemHeader *)::malloc(len+sizeof(MemHeader));
+ mh->mNext = mRoot;
+ mh->mPrevious = 0;
+ if ( mRoot ) mRoot->mPrevious = mh;
+ mRoot = mh;
+ mh->mLength = len;
+ mh->mTag = tag;
+ mh->mFile = file;
+ mh->mLineno = lineno;
+ mCount++;
+ mTotal+=len;
+ mh++;
+ return mh;
+ }
+
+ void memFree(void *mem)
+ {
+ MemHeader *mh = (MemHeader *)mem;
+ mh--;
+
+ MemHeader *prev = mh->mPrevious;
+ MemHeader *next = mh->mNext;
+
+ if ( prev )
+ {
+ prev->mNext = next;
+ }
+ else
+ {
+ assert( mRoot == mh );
+ mRoot = next;
+ }
+ if ( next )
+ {
+ next->mPrevious = prev;
+ }
+ mCount--;
+ mTotal-=mh->mLength;
+
+ assert(mCount>=0);
+ assert(mTotal>=0);
+
+ ::free(mh);
+ }
+
+ int getMemoryUsage(void)
+ {
+ int c = 0;
+ int t = 0;
+
+ MemHeader *mh = mRoot;
+ MemHeader *prev = 0;
+ while ( mh )
+ {
+ c++;
+ t+=mh->mLength;
+ assert( mh->mPrevious == prev );
+ prev = mh;
+ mh = mh->mNext;
+ }
+
+ assert( c == mCount );
+ assert( t == mTotal );
+
+ return mTotal;
+ }
+
+private:
+ int mCount;
+ int mTotal;
+ MemHeader *mRoot;
+};
+
+static MemTracker gMemTracker;
+
+#define HTML_NEW(x) new ( gMemTracker.memAlloc(sizeof(x),#x,__FILE__,__LINE__) )x
+#define HTML_DELETE(y,x) if ( x ) { x->~y(); gMemTracker.memFree(x); }
+
+#define HTML_MALLOC(x) gMemTracker.memAlloc(x,__FILE__,__FILE__,__LINE__)
+#define HTML_FREE(x) gMemTracker.memFree(x)
+
+
+static char * lastDot(char *src)
+{
+ char *ret = 0;
+
+ char *dot = strchr(src,'.');
+ while ( dot )
+ {
+ ret = dot;
+ dot = strchr(dot+1,'.');
+ }
+ return ret;
+}
+
+
+static char * lastSlash(char *src) // last forward or backward slash character, null if none found.
+{
+ char *ret = 0;
+
+ char *dot = strchr(src,'\\');
+ if ( dot == 0 )
+ dot = strchr(src,'/');
+ while ( dot )
+ {
+ ret = dot;
+ dot = strchr(ret+1,'\\');
+ if ( dot == 0 )
+ dot = strchr(ret+1,'/');
+ }
+ return ret;
+}
+
+static inline const char * tf(bool v)
+{
+ const char *ret = "false";
+ if ( v ) ret = "true";
+ return ret;
+}
+
+class QuickSortPointers
+{
+public:
+ void qsort(void **base,int num); // perform the qsort.
+protected:
+ // -1 less, 0 equal, +1 greater.
+ virtual int compare(void **p1,void **p2) = 0;
+private:
+ void inline swap(char **a,char **b);
+};
+
+
+void QuickSortPointers::swap(char **a,char **b)
+{
+ char *tmp;
+
+ if ( a != b )
+ {
+ tmp = *a;
+ *a++ = *b;
+ *b++ = tmp;
+ }
+}
+
+
+void QuickSortPointers::qsort(void **b,int num)
+{
+ char *lo,*hi;
+ char *mid;
+ char *bottom, *top;
+ int size;
+ char *lostk[30], *histk[30];
+ int stkptr;
+ char **base = (char **)b;
+
+ if (num < 2 ) return;
+
+ stkptr = 0;
+
+ lo = (char *)base;
+ hi = (char *)base + sizeof(char **) * (num-1);
+
+nextone:
+
+ size = (int)(hi - lo) / (int)sizeof(char**) + 1;
+
+ mid = lo + (size / 2) * sizeof(char **);
+ swap((char **)mid,(char **)lo);
+ bottom = lo;
+ top = hi + sizeof(char **);
+
+ for (;;)
+ {
+ do
+ {
+ bottom += sizeof(char **);
+ } while (bottom <= hi && compare((void **)bottom,(void **)lo) <= 0);
+
+ do
+ {
+ top -= sizeof(char **);
+ } while (top > lo && compare((void **)top,(void **)lo) >= 0);
+
+ if (top < bottom) break;
+
+ swap((char **)bottom,(char **)top);
+
+ }
+
+ swap((char **)lo,(char **)top);
+
+ if ( top - 1 - lo >= hi - bottom )
+ {
+ if (lo + sizeof(char **) < top)
+ {
+ lostk[stkptr] = lo;
+ histk[stkptr] = top - sizeof(char **);
+ stkptr++;
+ }
+ if (bottom < hi)
+ {
+ lo = bottom;
+ goto nextone;
+ }
+ }
+ else
+ {
+ if ( bottom < hi )
+ {
+ lostk[stkptr] = bottom;
+ histk[stkptr] = hi;
+ stkptr++;
+ }
+ if (lo + sizeof(char **) < top)
+ {
+ hi = top - sizeof(char **);
+ goto nextone; /* do small recursion */
+ }
+ }
+
+ stkptr--;
+
+ if (stkptr >= 0)
+ {
+ lo = lostk[stkptr];
+ hi = histk[stkptr];
+ goto nextone;
+ }
+ return;
+}
+
+
+//*** Including my 'FILE_INTERFACE' wrapper that supposed an STDIO style interface to read and write buffers.
+class FILE_INTERFACE;
+
+
+FILE_INTERFACE * fi_fopen(const char *fname,const char *spec,void *mem=0,size_t len=0);
+void fi_fclose(FILE_INTERFACE *file);
+size_t fi_fread(void *buffer,size_t size,size_t count,FILE_INTERFACE *fph);
+size_t fi_fwrite(const void *buffer,size_t size,size_t count,FILE_INTERFACE *fph);
+size_t fi_fprintf(FILE_INTERFACE *fph,const char *fmt,...);
+size_t fi_fflush(FILE_INTERFACE *fph);
+size_t fi_fseek(FILE_INTERFACE *fph,size_t loc,size_t mode);
+size_t fi_ftell(FILE_INTERFACE *fph);
+size_t fi_fputc(char c,FILE_INTERFACE *fph);
+size_t fi_fputs(const char *str,FILE_INTERFACE *fph);
+size_t fi_feof(FILE_INTERFACE *fph);
+size_t fi_ferror(FILE_INTERFACE *fph);
+void * fi_getMemBuffer(FILE_INTERFACE *fph,size_t &outputLength); // return the buffer and length of the file.
+
+
+#define DEFAULT_BUFFER_SIZE 8192
+
+#if defined(LINUX)
+# define _stricmp(a,b) strcasecmp((a),(b))
+#endif
+
+class FILE_INTERFACE
+{
+public:
+ FILE_INTERFACE(const char *fname,const char *spec,void *mem,size_t len)
+ {
+ mMyAlloc = false;
+ mRead = true; // default is read access.
+ mFph = 0;
+ mData = (char *) mem;
+ mLen = len;
+ mLoc = 0;
+
+ if ( spec && _stricmp(spec,"wmem") == 0 )
+ {
+ mRead = false;
+ if ( mem == 0 || len == 0 )
+ {
+ mData = (char *)HTML_MALLOC(DEFAULT_BUFFER_SIZE);
+ mLen = DEFAULT_BUFFER_SIZE;
+ mMyAlloc = true;
+ }
+ }
+
+ if ( mData == 0 )
+ {
+ mFph = fopen(fname,spec);
+ }
+
+ strncpy(mName,fname,512);
+ }
+
+ ~FILE_INTERFACE(void)
+ {
+ if ( mMyAlloc )
+ {
+ HTML_FREE(mData);
+ }
+ if ( mFph )
+ {
+ fclose(mFph);
+ }
+ }
+
+ size_t read(char *data,size_t size)
+ {
+ size_t ret = 0;
+ if ( (mLoc+size) <= mLen )
+ {
+ memcpy(data, &mData[mLoc], size );
+ mLoc+=size;
+ ret = 1;
+ }
+ return ret;
+ }
+
+ size_t write(const char *data,size_t size)
+ {
+ size_t ret = 0;
+
+ if ( (mLoc+size) >= mLen && mMyAlloc ) // grow it
+ {
+ size_t newLen = mLen*2;
+ if ( size > newLen ) newLen = size+mLen;
+
+ char *data = (char *)HTML_MALLOC(newLen);
+ memcpy(data,mData,mLoc);
+ HTML_FREE(mData);
+ mData = data;
+ mLen = newLen;
+ }
+
+ if ( (mLoc+size) <= mLen )
+ {
+ memcpy(&mData[mLoc],data,size);
+ mLoc+=size;
+ ret = 1;
+ }
+ return ret;
+ }
+
+ size_t read(void *buffer,size_t size,size_t count)
+ {
+ size_t ret = 0;
+ if ( mFph )
+ {
+ ret = fread(buffer,size,count,mFph);
+ }
+ else
+ {
+ char *data = (char *)buffer;
+ for (size_t i=0; i<count; i++)
+ {
+ if ( (mLoc+size) <= mLen )
+ {
+ read(data,size);
+ data+=size;
+ ret++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ return ret;
+ }
+
+ size_t write(const void *buffer,size_t size,size_t count)
+ {
+ size_t ret = 0;
+
+ if ( mFph )
+ {
+ ret = fwrite(buffer,size,count,mFph);
+ }
+ else
+ {
+ const char *data = (const char *)buffer;
+
+ for (size_t i=0; i<count; i++)
+ {
+ if ( write(data,size) )
+ {
+ data+=size;
+ ret++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ return ret;
+ }
+
+ size_t writeString(const char *str)
+ {
+ size_t ret = 0;
+ if ( str )
+ {
+ size_t len = strlen(str);
+ ret = write(str,len, 1 );
+ }
+ return ret;
+ }
+
+
+ size_t flush(void)
+ {
+ size_t ret = 0;
+ if ( mFph )
+ {
+ ret = (size_t)fflush(mFph);
+ }
+ return ret;
+ }
+
+
+ size_t seek(size_t loc,size_t mode)
+ {
+ size_t ret = 0;
+ if ( mFph )
+ {
+ ret = (size_t)fseek(mFph,(int)loc,(int)mode);
+ }
+ else
+ {
+ if ( mode == SEEK_SET )
+ {
+ if ( loc <= mLen )
+ {
+ mLoc = loc;
+ ret = 1;
+ }
+ }
+ else if ( mode == SEEK_END )
+ {
+ mLoc = mLen;
+ }
+ else
+ {
+ assert(0);
+ }
+ }
+ return ret;
+ }
+
+ size_t tell(void)
+ {
+ size_t ret = 0;
+ if ( mFph )
+ {
+ ret = (size_t)ftell(mFph);
+ }
+ else
+ {
+ ret = mLoc;
+ }
+ return ret;
+ }
+
+ size_t myputc(char c)
+ {
+ size_t ret = 0;
+ if ( mFph )
+ {
+ ret = (size_t)fputc(c,mFph);
+ }
+ else
+ {
+ ret = write(&c,1);
+ }
+ return ret;
+ }
+
+ size_t eof(void)
+ {
+ size_t ret = 0;
+ if ( mFph )
+ {
+ ret = (size_t)feof(mFph);
+ }
+ else
+ {
+ if ( mLoc >= mLen )
+ ret = 1;
+ }
+ return ret;
+ }
+
+ size_t error(void)
+ {
+ size_t ret = 0;
+ if ( mFph )
+ {
+ ret = (size_t)ferror(mFph);
+ }
+ return ret;
+ }
+
+
+ FILE *mFph;
+ char *mData;
+ size_t mLen;
+ size_t mLoc;
+ bool mRead;
+ char mName[512];
+ bool mMyAlloc;
+
+};
+
+FILE_INTERFACE * fi_fopen(const char *fname,const char *spec,void *mem,size_t len)
+{
+ FILE_INTERFACE *ret = 0;
+
+ ret = HTML_NEW(FILE_INTERFACE)(fname,spec,mem,len);
+
+ if ( mem == 0 && ret->mData == 0)
+ {
+ if ( ret->mFph == 0 )
+ {
+ HTML_DELETE(FILE_INTERFACE,ret);
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+void fi_fclose(FILE_INTERFACE *file)
+{
+ HTML_DELETE(FILE_INTERFACE,file);
+}
+
+size_t fi_fread(void *buffer,size_t size,size_t count,FILE_INTERFACE *fph)
+{
+ size_t ret = 0;
+ if ( fph )
+ {
+ ret = fph->read(buffer,size,count);
+ }
+ return ret;
+}
+
+size_t fi_fwrite(const void *buffer,size_t size,size_t count,FILE_INTERFACE *fph)
+{
+ size_t ret = 0;
+ if ( fph )
+ {
+ ret = fph->write(buffer,size,count);
+ }
+ return ret;
+}
+
+size_t fi_fprintf(FILE_INTERFACE *fph,const char *fmt,...)
+{
+ size_t ret = 0;
+
+ char buffer[2048];
+ buffer[2047] = 0;
+ _vsnprintf(buffer,2047, fmt, (char *)(&fmt+1));
+
+ if ( fph )
+ {
+ ret = fph->writeString(buffer);
+ }
+
+ return ret;
+}
+
+
+size_t fi_fflush(FILE_INTERFACE *fph)
+{
+ size_t ret = 0;
+ if ( fph )
+ {
+ ret = fph->flush();
+ }
+ return ret;
+}
+
+
+size_t fi_fseek(FILE_INTERFACE *fph,size_t loc,size_t mode)
+{
+ size_t ret = 0;
+ if ( fph )
+ {
+ ret = fph->seek(loc,mode);
+ }
+ return ret;
+}
+
+size_t fi_ftell(FILE_INTERFACE *fph)
+{
+ size_t ret = 0;
+ if ( fph )
+ {
+ ret = fph->tell();
+ }
+ return ret;
+}
+
+size_t fi_fputc(char c,FILE_INTERFACE *fph)
+{
+ size_t ret = 0;
+ if ( fph )
+ {
+ ret = fph->myputc(c);
+ }
+ return ret;
+}
+
+size_t fi_fputs(const char *str,FILE_INTERFACE *fph)
+{
+ size_t ret = 0;
+ if ( fph )
+ {
+ ret = fph->writeString(str);
+ }
+ return ret;
+}
+
+size_t fi_feof(FILE_INTERFACE *fph)
+{
+ size_t ret = 0;
+ if ( fph )
+ {
+ ret = fph->eof();
+ }
+ return ret;
+}
+
+size_t fi_ferror(FILE_INTERFACE *fph)
+{
+ size_t ret = 0;
+ if ( fph )
+ {
+ ret = fph->error();
+ }
+ return ret;
+}
+
+void * fi_getMemBuffer(FILE_INTERFACE *fph,size_t &outputLength)
+{
+ outputLength = 0;
+ void * ret = 0;
+ if ( fph )
+ {
+ ret = fph->mData;
+ outputLength = fph->mLoc;
+ }
+ return ret;
+}
+
+
+//**** Probably a little bit overkill, but I have just copy pasted my 'InPlaceParser' to handle the comma seperated value lines, since it handles quotated strings and whitspace automatically.
+
+class InPlaceParserInterface
+{
+public:
+ virtual int ParseLine(int lineno,int argc,const char **argv) =0; // return TRUE to continue parsing, return FALSE to abort parsing process
+ virtual bool preParseLine(int /*lineno*/,const char * /*line */) { return false; }; // optional chance to pre-parse the line as raw data. If you return 'true' the line will be skipped assuming you snarfed it.
+};
+
+enum SeparatorType
+{
+ ST_DATA, // is data
+ ST_HARD, // is a hard separator
+ ST_SOFT, // is a soft separator
+ ST_EOS, // is a comment symbol, and everything past this character should be ignored
+ ST_LINE_FEED
+};
+
+class InPlaceParser
+{
+public:
+ InPlaceParser(void)
+ {
+ Init();
+ }
+
+ InPlaceParser(char *data,int len)
+ {
+ Init();
+ SetSourceData(data,len);
+ }
+
+ InPlaceParser(const char *fname)
+ {
+ Init();
+ SetFile(fname);
+ }
+
+ ~InPlaceParser(void);
+
+ void Init(void)
+ {
+ mQuoteChar = 34;
+ mData = 0;
+ mLen = 0;
+ mMyAlloc = false;
+ for (int i=0; i<256; i++)
+ {
+ mHard[i] = ST_DATA;
+ mHardString[i*2] = (char)i;
+ mHardString[i*2+1] = 0;
+ }
+ mHard[0] = ST_EOS;
+ mHard[32] = ST_SOFT;
+ mHard[9] = ST_SOFT;
+ mHard[13] = ST_LINE_FEED;
+ mHard[10] = ST_LINE_FEED;
+ }
+
+ void SetFile(const char *fname);
+
+ void SetSourceData(char *data,int len)
+ {
+ mData = data;
+ mLen = len;
+ mMyAlloc = false;
+ };
+
+ int Parse(const char *str,InPlaceParserInterface *callback); // returns true if entire file was parsed, false if it aborted for some reason
+ int Parse(InPlaceParserInterface *callback); // returns true if entire file was parsed, false if it aborted for some reason
+
+ int ProcessLine(int lineno,char *line,InPlaceParserInterface *callback);
+
+ const char ** GetArglist(char *source,int &count); // convert source string into an arg list, this is a destructive parse.
+
+ void SetHardSeparator(char c) // add a hard separator
+ {
+ mHard[c] = ST_HARD;
+ }
+
+ void SetHard(char c) // add a hard separator
+ {
+ mHard[c] = ST_HARD;
+ }
+
+ void SetSoft(char c) // add a hard separator
+ {
+ mHard[c] = ST_SOFT;
+ }
+
+
+ void SetCommentSymbol(char c) // comment character, treated as 'end of string'
+ {
+ mHard[c] = ST_EOS;
+ }
+
+ void ClearHardSeparator(char c)
+ {
+ mHard[c] = ST_DATA;
+ }
+
+
+ void DefaultSymbols(void); // set up default symbols for hard seperator and comment symbol of the '#' character.
+
+ bool EOS(char c)
+ {
+ if ( mHard[c] == ST_EOS )
+ {
+ return true;
+ }
+ return false;
+ }
+
+ void SetQuoteChar(char c)
+ {
+ mQuoteChar = c;
+ }
+
+ bool HasData( void ) const
+ {
+ return ( mData != 0 );
+ }
+
+ void setLineFeed(char c)
+ {
+ mHard[c] = ST_LINE_FEED;
+ }
+
+ bool isLineFeed(char c)
+ {
+ if ( mHard[c] == ST_LINE_FEED ) return true;
+ return false;
+ }
+
+private:
+
+ inline char * AddHard(int &argc,const char **argv,char *foo);
+ inline bool IsHard(char c);
+ inline char * SkipSpaces(char *foo);
+ inline bool IsWhiteSpace(char c);
+ inline bool IsNonSeparator(char c); // non seperator,neither hard nor soft
+
+ bool mMyAlloc; // whether or not *I* allocated the buffer and am responsible for deleting it.
+ char *mData; // ascii data to parse.
+ int mLen; // length of data
+ SeparatorType mHard[256];
+ char mHardString[256*2];
+ char mQuoteChar;
+};
+
+//==================================================================================
+void InPlaceParser::SetFile(const char *fname)
+{
+ if ( mMyAlloc )
+ {
+ HTML_FREE(mData);
+ }
+ mData = 0;
+ mLen = 0;
+ mMyAlloc = false;
+
+ FILE *fph = fopen(fname,"rb");
+ if ( fph )
+ {
+ fseek(fph,0L,SEEK_END);
+ mLen = ftell(fph);
+ fseek(fph,0L,SEEK_SET);
+
+ if ( mLen )
+ {
+ mData = (char *) HTML_MALLOC(sizeof(char)*(mLen+1));
+ int read = (int)fread(mData,(size_t)mLen,1,fph);
+ if ( !read )
+ {
+ HTML_FREE(mData);
+ mData = 0;
+ }
+ else
+ {
+ mData[mLen] = 0; // zero byte terminate end of file marker.
+ mMyAlloc = true;
+ }
+ }
+ fclose(fph);
+ }
+}
+
+//==================================================================================
+InPlaceParser::~InPlaceParser(void)
+{
+ if ( mMyAlloc )
+ {
+ HTML_FREE(mData);
+ }
+}
+
+#define MAXARGS 512
+
+//==================================================================================
+bool InPlaceParser::IsHard(char c)
+{
+ return mHard[c] == ST_HARD;
+}
+
+//==================================================================================
+char * InPlaceParser::AddHard(int &argc,const char **argv,char *foo)
+{
+ while ( IsHard(*foo) )
+ {
+ const char *hard = &mHardString[*foo*2];
+ if ( argc < MAXARGS )
+ {
+ argv[argc++] = hard;
+ }
+ ++foo;
+ }
+ return foo;
+}
+
+//==================================================================================
+bool InPlaceParser::IsWhiteSpace(char c)
+{
+ return mHard[c] == ST_SOFT;
+}
+
+//==================================================================================
+char * InPlaceParser::SkipSpaces(char *foo)
+{
+ while ( !EOS(*foo) && IsWhiteSpace(*foo) )
+ ++foo;
+ return foo;
+}
+
+//==================================================================================
+bool InPlaceParser::IsNonSeparator(char c)
+{
+ return ( !IsHard(c) && !IsWhiteSpace(c) && c != 0 );
+}
+
+//==================================================================================
+int InPlaceParser::ProcessLine(int lineno,char *line,InPlaceParserInterface *callback)
+{
+ int ret = 0;
+
+ const char *argv[MAXARGS];
+ int argc = 0;
+
+ char *foo = line;
+
+ while ( !EOS(*foo) && argc < MAXARGS )
+ {
+ foo = SkipSpaces(foo); // skip any leading spaces
+
+ if ( EOS(*foo) )
+ break;
+
+ if ( *foo == mQuoteChar ) // if it is an open quote
+ {
+ ++foo;
+ if ( argc < MAXARGS )
+ {
+ argv[argc++] = foo;
+ }
+ while ( !EOS(*foo) && *foo != mQuoteChar )
+ ++foo;
+ if ( !EOS(*foo) )
+ {
+ *foo = 0; // replace close quote with zero byte EOS
+ ++foo;
+ }
+ }
+ else
+ {
+ foo = AddHard(argc,argv,foo); // add any hard separators, skip any spaces
+
+ if ( IsNonSeparator(*foo) ) // add non-hard argument.
+ {
+ bool quote = false;
+ if ( *foo == mQuoteChar )
+ {
+ ++foo;
+ quote = true;
+ }
+
+ if ( argc < MAXARGS )
+ {
+ argv[argc++] = foo;
+ }
+
+ if ( quote )
+ {
+ while (*foo && *foo != mQuoteChar )
+ ++foo;
+ if ( *foo )
+ *foo = 32;
+ }
+
+ // continue..until we hit an eos ..
+ while ( !EOS(*foo) ) // until we hit EOS
+ {
+ if ( IsWhiteSpace(*foo) ) // if we hit a space, stomp a zero byte, and exit
+ {
+ *foo = 0;
+ ++foo;
+ break;
+ }
+ else if ( IsHard(*foo) ) // if we hit a hard separator, stomp a zero byte and store the hard separator argument
+ {
+ const char *hard = &mHardString[*foo*2];
+ *foo = 0;
+ if ( argc < MAXARGS )
+ {
+ argv[argc++] = hard;
+ }
+ ++foo;
+ break;
+ }
+ ++foo;
+ } // end of while loop...
+ }
+ }
+ }
+
+ if ( argc )
+ {
+ ret = callback->ParseLine(lineno, argc, argv );
+ }
+
+ return ret;
+}
+
+
+int InPlaceParser::Parse(const char *str,InPlaceParserInterface *callback) // returns true if entire file was parsed, false if it aborted for some reason
+{
+ int ret = 0;
+
+ mLen = (int)strlen(str);
+ if ( mLen )
+ {
+ mData = (char *)HTML_MALLOC((size_t)mLen+1);
+ strcpy(mData,str);
+ mMyAlloc = true;
+ ret = Parse(callback);
+ }
+ return ret;
+}
+
+//==================================================================================
+// returns true if entire file was parsed, false if it aborted for some reason
+//==================================================================================
+int InPlaceParser::Parse(InPlaceParserInterface *callback)
+{
+ int ret = 0;
+ assert( callback );
+ if ( mData )
+ {
+ int lineno = 0;
+
+ char *foo = mData;
+ char *begin = foo;
+
+ while ( *foo )
+ {
+ if ( isLineFeed(*foo) )
+ {
+ ++lineno;
+ *foo = 0;
+ if ( *begin ) // if there is any data to parse at all...
+ {
+ bool snarfed = callback->preParseLine(lineno,begin);
+ if ( !snarfed )
+ {
+ int v = ProcessLine(lineno,begin,callback);
+ if ( v )
+ ret = v;
+ }
+ }
+
+ ++foo;
+ if ( *foo == 10 )
+ ++foo; // skip line feed, if it is in the carraige-return line-feed format...
+ begin = foo;
+ }
+ else
+ {
+ ++foo;
+ }
+ }
+
+ lineno++; // lasst line.
+
+ int v = ProcessLine(lineno,begin,callback);
+ if ( v )
+ ret = v;
+ }
+ return ret;
+}
+
+//==================================================================================
+void InPlaceParser::DefaultSymbols(void)
+{
+ SetHardSeparator(',');
+ SetHardSeparator('(');
+ SetHardSeparator(')');
+ SetHardSeparator('=');
+ SetHardSeparator('[');
+ SetHardSeparator(']');
+ SetHardSeparator('{');
+ SetHardSeparator('}');
+ SetCommentSymbol('#');
+}
+
+//==================================================================================
+// convert source string into an arg list, this is a destructive parse.
+//==================================================================================
+const char ** InPlaceParser::GetArglist(char *line,int &count)
+{
+ const char **ret = 0;
+
+ static const char *argv[MAXARGS];
+ int argc = 0;
+
+ char *foo = line;
+
+ while ( !EOS(*foo) && argc < MAXARGS )
+ {
+ foo = SkipSpaces(foo); // skip any leading spaces
+
+ if ( EOS(*foo) )
+ break;
+
+ if ( *foo == mQuoteChar ) // if it is an open quote
+ {
+ ++foo;
+ if ( argc < MAXARGS )
+ {
+ argv[argc++] = foo;
+ }
+ while ( !EOS(*foo) && *foo != mQuoteChar )
+ ++foo;
+ if ( !EOS(*foo) )
+ {
+ *foo = 0; // replace close quote with zero byte EOS
+ ++foo;
+ }
+ }
+ else
+ {
+ foo = AddHard(argc,argv,foo); // add any hard separators, skip any spaces
+
+ if ( IsNonSeparator(*foo) ) // add non-hard argument.
+ {
+ bool quote = false;
+ if ( *foo == mQuoteChar )
+ {
+ ++foo;
+ quote = true;
+ }
+
+ if ( argc < MAXARGS )
+ {
+ argv[argc++] = foo;
+ }
+
+ if ( quote )
+ {
+ while (*foo && *foo != mQuoteChar )
+ ++foo;
+ if ( *foo )
+ *foo = 32;
+ }
+
+ // continue..until we hit an eos ..
+ while ( !EOS(*foo) ) // until we hit EOS
+ {
+ if ( IsWhiteSpace(*foo) ) // if we hit a space, stomp a zero byte, and exit
+ {
+ *foo = 0;
+ ++foo;
+ break;
+ }
+ else if ( IsHard(*foo) ) // if we hit a hard separator, stomp a zero byte and store the hard separator argument
+ {
+ const char *hard = &mHardString[*foo*2];
+ *foo = 0;
+ if ( argc < MAXARGS )
+ {
+ argv[argc++] = hard;
+ }
+ ++foo;
+ break;
+ }
+ ++foo;
+ } // end of while loop...
+ }
+ }
+ }
+
+ count = argc;
+ if ( argc )
+ {
+ ret = argv;
+ }
+
+ return ret;
+}
+
+static bool numeric(char c)
+{
+ bool ret = false;
+
+ if ( (c >= '0' && c <= '9' ) || c == ',' || c == '.' || c == 32)
+ {
+ ret = true;
+ }
+ return ret;
+}
+
+static bool isNumeric(const std::string &str)
+{
+ bool ret = true;
+
+ if ( str.size() == 0 )
+ {
+ ret = false;
+ }
+ else
+ {
+ const char *scan = str.c_str();
+ if ( *scan == '-' ) scan++;
+ while ( *scan )
+ {
+ if ( !numeric(*scan) )
+ {
+ ret = false;
+ break;
+ }
+ scan++;
+ }
+ }
+ return ret;
+}
+
+#define MAXNUMERIC 32 // JWR support up to 16 32 character long numeric formated strings
+#define MAXFNUM 16
+
+static char gFormat[MAXNUMERIC*MAXFNUM];
+static int gIndex=0;
+
+const char * formatNumber(int number) // JWR format this integer into a fancy comma delimited string
+{
+ char * dest = &gFormat[gIndex*MAXNUMERIC];
+ gIndex++;
+ if ( gIndex == MAXFNUM ) gIndex = 0;
+
+ char scratch[512];
+
+#if defined (LINUX_GENERIC) || defined(LINUX) || defined (__CELLOS_LV2__)
+ snprintf(scratch, 10, "%d", number);
+#else
+ itoa(number,scratch,10);
+#endif
+
+ char *source = scratch;
+ char *str = dest;
+ unsigned int len = (unsigned int)strlen(scratch);
+ if ( scratch[0] == '-' )
+ {
+ *str++ = '-';
+ source++;
+ len--;
+ }
+ for (unsigned int i=0; i<len; i++)
+ {
+ int place = ((int)len-1)-(int)i;
+ *str++ = source[i];
+ if ( place && (place%3) == 0 ) *str++ = ',';
+ }
+ *str = 0;
+
+ return dest;
+}
+
+void stripFraction(char *fraction)
+{
+ size_t len = strlen(fraction);
+ if ( len > 0 )
+ {
+ len--;
+ while ( len )
+ {
+ if ( fraction[len] == '0' )
+ {
+ fraction[len] = 0;
+ len--;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+}
+
+float getFloatValue(const char *data)
+{
+ char temp[512];
+ char *dest = temp;
+
+ while ( *data )
+ {
+ char c = *data++;
+ if ( c != ',' )
+ {
+ *dest++ = c;
+ }
+ }
+ *dest = 0;
+ float v = (float)atof(temp);
+ return v;
+}
+
+void getFloat(float v,std::string &ret)
+{
+ int ivalue = (int)v;
+
+ if ( v == 0 )
+ {
+ ret = "0";
+ }
+ else if ( v == 1 )
+ {
+ ret = "1";
+ }
+ else if ( v == -1 )
+ {
+ ret = "-1";
+ }
+ else if ( ivalue == 0 )
+ {
+ char fraction[512];
+ sprintf(fraction,"%0.9f", v );
+ stripFraction(fraction);
+ ret = fraction;
+ }
+ else
+ {
+ v-=(float)ivalue;
+ v = fabsf(v);
+ if (v < 0.00001f )
+ v = 0;
+
+
+ const char *temp = formatNumber(ivalue);
+ if ( v != 0 )
+ {
+ char fraction[512];
+ sprintf(fraction,"%0.9f", v );
+ assert( fraction[0] == '0' );
+ assert( fraction[1] == '.' );
+ stripFraction(fraction);
+ char scratch[512];
+ sprintf(scratch,"%s%s", temp, &fraction[1] );
+ ret = scratch;
+ }
+ else
+ {
+ ret = temp;
+ }
+ }
+}
+
+class SortRequest
+{
+public:
+ SortRequest(void)
+ {
+
+ }
+ SortRequest(const char *sort_name,unsigned int primary_key,bool primary_ascending,unsigned int secondary_key,bool secondary_ascending)
+ {
+ if ( sort_name )
+ mSortName = sort_name;
+
+ mPrimaryKey = primary_key;
+ mPrimaryAscending = primary_ascending;
+ mSecondaryKey = secondary_key;
+ mSecondaryAscending = secondary_ascending;
+
+ }
+
+
+ std::string mSortName;
+ unsigned int mPrimaryKey;
+ unsigned int mSecondaryKey;
+ bool mPrimaryAscending:1;
+ bool mSecondaryAscending:1;
+};
+
+typedef std::vector< SortRequest > SortRequestVector;
+
+typedef std::vector< std::string > StringVector;
+typedef std::vector< size_t > SizetVector;
+
+class HtmlRow
+{
+public:
+ HtmlRow(void)
+ {
+ mHeader = false;
+ mFooter = false;
+ }
+ ~HtmlRow(void)
+ {
+ }
+
+ void setFooter(bool state)
+ {
+ mFooter = state;
+ }
+
+ bool isFooter(void) const { return mFooter; };
+
+ void setHeader(bool state)
+ {
+ mHeader = state;
+ }
+
+ bool isHeader(void) const { return mHeader; };
+
+ void clear(void)
+ {
+ mRow.clear();
+ }
+
+ void addCSV(const char *data,InPlaceParser &parser)
+ {
+ if ( data )
+ {
+ size_t len = strlen(data);
+ if ( len )
+ {
+ char *temp = (char *)HTML_MALLOC(sizeof(char)*(len+1));
+ memcpy(temp,data,len+1);
+ int count;
+ const char **args = parser.GetArglist(temp,count);
+ if ( args )
+ {
+ for (int i=0; i<count; i++)
+ {
+ const char *arg = args[i];
+ if ( arg[0] != ',' )
+ {
+ addColumn(arg);
+ }
+ }
+ }
+ HTML_FREE(temp);
+ }
+ }
+ }
+
+ void addColumn(const char *data)
+ {
+ if ( data )
+ {
+ std::string str = data;
+ if ( isNumeric(data) )
+ {
+ float v = getFloatValue(data);
+ getFloat(v,str);
+ }
+ mRow.push_back(str);
+ }
+ }
+
+ void columnSizes(SizetVector &csizes)
+ {
+ size_t ccount = csizes.size();
+ size_t count = mRow.size();
+ for (size_t i=ccount; i<count; i++)
+ {
+ csizes.push_back(0);
+ }
+ for (size_t i=0; i<count; i++)
+ {
+ if ( mRow[i].size() > csizes[i] )
+ {
+ csizes[i] = mRow[i].size();
+ }
+ }
+ }
+
+ void getString(size_t index,std::string &str) const
+ {
+ if ( index < mRow.size() )
+ {
+ str = mRow[index];
+ }
+ else
+ {
+ str.clear();
+ }
+ }
+
+ void htmlRow(FILE_INTERFACE *fph,HtmlTable *table)
+ {
+ {
+ fi_fprintf(fph,"<TR>");
+
+ unsigned int column = 1;
+
+ StringVector::iterator i;
+ for (i=mRow.begin(); i!=mRow.end(); ++i)
+ {
+
+ unsigned int color = table->getColor(column,mHeader,mFooter);
+
+ const char *str = (*i).c_str();
+
+ if ( mHeader )
+ {
+ fi_fprintf(fph,"<TH bgcolor=\"#%06X\"> %s </TH>", color, str );
+ }
+ else if ( mFooter )
+ {
+ if ( isNumeric(str))
+ {
+ fi_fprintf(fph,"<TH bgcolor=\"#%06X\" align=\"right\"> %s</TH>", color, str );
+ }
+ else
+ {
+ fi_fprintf(fph,"<TH bgcolor=\"#%06X\" align=\"left\">%s </TH>", color, str );
+ }
+ }
+ else
+ {
+ if ( isNumeric(str))
+ {
+ fi_fprintf(fph,"<TD bgcolor=\"#%06X\" align=\"right\"> %s</TD>", color, str );
+ }
+ else
+ {
+ fi_fprintf(fph,"<TD bgcolor=\"#%06X\" align=\"left\">%s </TD>", color, str );
+ }
+ }
+
+ column++;
+ }
+
+ fi_fprintf(fph,"</TR>\r\n");
+ }
+ }
+
+ void saveExcel(FILE *fph,HtmlTable *table)
+ {
+ {
+ fprintf(fph,"<TR>");
+
+ unsigned int column = 1;
+
+ StringVector::iterator i;
+ for (i=mRow.begin(); i!=mRow.end(); ++i)
+ {
+
+ unsigned int color = table->getColor(column,mHeader,mFooter);
+
+ const char *str = (*i).c_str();
+
+ if ( mHeader )
+ {
+ fprintf(fph,"<TH bgcolor=\"#%06X\"> %s </TH>", color, str );
+ }
+ else if ( mFooter )
+ {
+ if ( isNumeric(str))
+ {
+ fprintf(fph,"<TH bgcolor=\"#%06X\" align=\"right\"> %s</TH>", color, str );
+ }
+ else
+ {
+ fprintf(fph,"<TH bgcolor=\"#%06X\" align=\"left\">%s </TH>", color, str );
+ }
+ }
+ else
+ {
+ if ( isNumeric(str))
+ {
+ fprintf(fph,"<TD bgcolor=\"#%06X\" align=\"right\"> %s</TD>", color, str );
+ }
+ else
+ {
+ fprintf(fph,"<TD bgcolor=\"#%06X\" align=\"left\">%s </TD>", color, str );
+ }
+ }
+
+ column++;
+ }
+
+ fprintf(fph,"</TR>\r\n");
+ }
+ }
+
+ void saveCSV(FILE_INTERFACE *fph)
+ {
+ size_t count = mRow.size();
+ for (size_t i=0; i<count; i++)
+ {
+ const char *data = mRow[i].c_str();
+ fi_fprintf(fph,"\"%s\"", data );
+ if ( (i+1) < count )
+ {
+ fi_fprintf(fph,",");
+ }
+ }
+ fi_fprintf(fph,"\r\n");
+ }
+
+ void saveCPP(FILE_INTERFACE *fph)
+ {
+ if ( mHeader )
+ {
+ fi_fprintf(fph," table->addHeader(%c%c%c%c,%c", 34, '%', 's', 34, 34, 34);
+ }
+ else
+ {
+ fi_fprintf(fph," table->addCSV(%c%c%c%c,%c", 34, '%', 's', 34, 34, 34 );
+ }
+
+ size_t count = mRow.size();
+ for (size_t i=0; i<count; i++)
+ {
+ const char *data = mRow[i].c_str();
+
+ bool needQuote = false;
+ bool isNumeric = true;
+
+ std::string str;
+ while ( *data )
+ {
+ char c = *data++;
+
+ if ( !numeric(c) )
+ {
+ isNumeric = false;
+ if ( c == ',' )
+ needQuote = true;
+ }
+
+
+ if ( c == 34 )
+ {
+ str.push_back('\\');
+ str.push_back(34);
+ }
+ else if ( c == '\\' )
+ {
+ str.push_back('\\');
+ str.push_back('\\');
+ }
+ else
+ {
+ str.push_back(c);
+ }
+ }
+
+ if ( isNumeric )
+ {
+ const char *data = mRow[i].c_str();
+ str.clear();
+ while ( *data )
+ {
+ char c = *data++;
+ if ( c != ',' )
+ {
+ str.push_back(c);
+ }
+ }
+ }
+ if ( needQuote )
+ {
+ fi_fprintf(fph,"%c%c%s%c%c", '\\', 34, str.c_str(), '\\', 34 );
+ }
+ else
+ {
+ fi_fprintf(fph,"%s", str.c_str() );
+ }
+ if ( (i+1) < count )
+ {
+ fi_fprintf(fph,",");
+ }
+ }
+ fi_fprintf(fph,"%c);\r\n",34);
+ }
+
+ int compare(const HtmlRow &r,const SortRequest &s)
+ {
+ int ret = 0;
+
+ std::string p1; // primary 1
+ std::string p2; // primary 2
+
+
+ getString(s.mPrimaryKey-1,p1);
+
+ r.getString(s.mPrimaryKey-1,p2);
+
+ if (isNumeric(p1) && isNumeric(p2) )
+ {
+ float v1 = getFloatValue(p1.c_str());
+ float v2 = getFloatValue(p2.c_str());
+ if ( v1 < v2 )
+ ret = -1;
+ else if ( v1 > v2 )
+ ret = 1;
+ }
+ else
+ {
+ ret = stricmp(p1.c_str(),p2.c_str());
+
+ if ( ret < 0 )
+ ret = -1;
+ else if ( ret > 0 )
+ ret = 1;
+
+ }
+
+ if ( !s.mPrimaryAscending )
+ {
+ ret*=-1;
+ }
+
+ if ( ret == 0 )
+ {
+ std::string p1; // secondary 1
+ std::string p2; // secondary 2
+ getString(s.mSecondaryKey-1,p1);
+ r.getString(s.mSecondaryKey-1,p2);
+ if (isNumeric(p1) && isNumeric(p2) )
+ {
+ float v1 = getFloatValue(p1.c_str());
+ float v2 = getFloatValue(p2.c_str());
+ if ( v1 < v2 )
+ ret = -1;
+ else if ( v1 > v2 )
+ ret = 1;
+ }
+ else
+ {
+ ret = stricmp(p1.c_str(),p2.c_str());
+
+ if ( ret < 0 )
+ ret = -1;
+ else if ( ret > 0 )
+ ret = 1;
+
+ }
+
+ if ( !s.mSecondaryAscending )
+ {
+ ret*=-1;
+ }
+
+ }
+
+ return ret;
+ }
+
+private:
+ bool mHeader:1;
+ bool mFooter:1;
+ StringVector mRow;
+};
+
+typedef std::vector< HtmlRow * > HtmlRowVector;
+
+
+static int gTableCount=0;
+
+class _HtmlTable : public HtmlTable, public QuickSortPointers
+{
+public:
+ _HtmlTable(const char *heading,HtmlDocument *parent);
+
+
+ virtual ~_HtmlTable(void)
+ {
+// gTableCount--;
+// printf("Destructed _HtmlTable(%08X) Count:%d\r\n", this, gTableCount );
+ reset();
+ }
+
+ unsigned int getDisplayOrder(void) const { return mDisplayOrder; };
+
+
+ int compare(void **p1,void **p2)
+ {
+ HtmlRow **r1 = (HtmlRow **)p1;
+ HtmlRow **r2 = (HtmlRow **)p2;
+
+ HtmlRow *row1 = r1[0];
+ HtmlRow *row2 = r2[0];
+
+ assert( !row1->isHeader() );
+ assert( !row1->isFooter() );
+
+ assert( !row2->isHeader() );
+ assert( !row2->isFooter() );
+
+ return row1->compare(*row2,mSortRequest);
+ }
+
+ void BorderASCII(void)
+ {
+ UPPER_LEFT_BORDER = '/';
+ UPPER_RIGHT_BORDER = '\\';
+
+ LOWER_LEFT_BORDER = '\\';
+ LOWER_RIGHT_BORDER = '/';
+
+ TOP_SEPARATOR = '-';
+ BOTTOM_SEPARATOR = '-';
+ TOP_BORDER = '-';
+ BOTTOM_BORDER = '-';
+ LEFT_BORDER = '|';
+ RIGHT_BORDER = '|';
+ VERTICAL_SEPARATOR = '|';
+ LEFT_SIDE_BORDER = '|';
+ RIGHT_SIDE_BORDER = '|';
+ CROSS_BORDER = '-';
+ }
+
+ void BorderDOS(void)
+ {
+ UPPER_LEFT_BORDER = 201;
+ UPPER_RIGHT_BORDER = 187;
+ LOWER_LEFT_BORDER = 200;
+ LOWER_RIGHT_BORDER = 188;
+ TOP_SEPARATOR = 203;
+ BOTTOM_SEPARATOR = 202;
+ TOP_BORDER = 205;
+ BOTTOM_BORDER = 205;
+ LEFT_BORDER = 186;
+ RIGHT_BORDER = 186;
+ VERTICAL_SEPARATOR = 186;
+ LEFT_SIDE_BORDER = 204;
+ RIGHT_SIDE_BORDER = 185;
+ CROSS_BORDER = 206;
+ }
+
+ void reset(void)
+ {
+ HtmlRowVector::iterator i;
+ for (i=mBody.begin(); i!=mBody.end(); i++)
+ {
+ HtmlRow *row = (*i);
+ HTML_DELETE(HtmlRow,row);
+ }
+ mBody.clear();
+ mExcludeTotals.clear();
+ mCurrent = 0;
+ }
+
+ void addString(std::string &str,const char *data)
+ {
+ str.push_back(34);
+ while ( *data )
+ {
+ str.push_back(*data);
+ data++;
+ }
+ str.push_back(34);
+ }
+
+ void addHeader(const char *fmt,...)
+ {
+ char data[8192];
+ data[8191] = 0;
+ _vsnprintf(data,8191, fmt, (char *)(&fmt+1));
+
+ mParser.ClearHardSeparator(32);
+ mParser.ClearHardSeparator(9);
+
+ if ( strstr(data,"/") )
+ {
+ std::string sdata = data;
+
+ while ( true )
+ {
+ size_t len = sdata.size();
+
+
+ std::string header1;
+ std::string header2;
+
+ char *temp = (char *)HTML_MALLOC(sizeof(char)*(len+1));
+ memcpy(temp,sdata.c_str(),len+1);
+ int count;
+ const char **args = mParser.GetArglist(temp,count);
+
+ if ( args )
+ {
+ for (int i=0; i<count; i++)
+ {
+ const char *arg = args[i];
+ if ( arg[0] == ',' )
+ {
+ header1.push_back(',');
+ header2.push_back(',');
+ }
+ else
+ {
+ if ( strstr(arg,"/") )
+ {
+ header1.push_back(34);
+ while ( *arg && *arg != '/' )
+ {
+ header1.push_back(*arg);
+ arg++;
+ }
+ header1.push_back(34);
+
+ header2.push_back(34);
+ if ( *arg == '/' )
+ {
+ arg++;
+ while ( *arg )
+ {
+ header2.push_back(*arg);
+ arg++;
+ }
+ }
+ header2.push_back(34);
+ }
+ else
+ {
+ addString(header1,arg);
+ }
+ }
+ }
+ }
+ HTML_FREE(temp);
+
+ getCurrent();
+ mCurrent->setHeader(true);
+ mCurrent->addCSV(header1.c_str(),mParser);
+ nextRow();
+
+ if ( strstr(header2.c_str(),"/") )
+ {
+ sdata = header2; // now process header2...
+ }
+ else
+ {
+ getCurrent();
+ mCurrent->setHeader(true);
+ mCurrent->addCSV(header2.c_str(),mParser);
+ nextRow();
+ break;
+ }
+ }
+
+ }
+ else
+ {
+ getCurrent();
+ mCurrent->setHeader(true);
+ mCurrent->addCSV(data,mParser);
+ nextRow();
+ }
+ }
+
+ void addColumn(const char *data)
+ {
+ getCurrent();
+ mCurrent->addColumn(data);
+ }
+
+ void addColumn(float v)
+ {
+ std::string str;
+ getFloat(v,str);
+ addColumn(str.c_str());
+ }
+
+ void addColumnHex(unsigned int v)
+ {
+ char scratch[512];
+ sprintf(scratch,"$%08X",v);
+ addColumn(scratch);
+ }
+
+
+ void addColumn(int v)
+ {
+ const char *temp = formatNumber(v);
+ addColumn(temp);
+ }
+
+ void addColumn(unsigned int v)
+ {
+ const char *temp = formatNumber((int)v);
+ addColumn(temp);
+ }
+
+
+ void addCSV(bool newRow,const char *fmt,...)
+ {
+ char data[8192];
+ data[8191] = 0;
+ _vsnprintf(data,8191, fmt, (char *)(&fmt+1));
+
+ getCurrent();
+ mCurrent->addCSV(data,mParser);
+ if ( newRow )
+ {
+ mCurrent = 0;
+ }
+ }
+
+ void nextRow(void)
+ {
+ mCurrent = 0;
+ }
+
+ void getCurrent(void)
+ {
+ if ( mCurrent == 0 )
+ {
+ mCurrent = HTML_NEW(HtmlRow);
+ mBody.push_back(mCurrent);
+ }
+ }
+
+
+ void printLeft(FILE_INTERFACE *fph,const std::string &str,size_t width)
+ {
+ size_t swid = str.size();
+ assert( swid <= width );
+
+ size_t justify = (width-swid)-1;
+ fi_fprintf(fph,"%c", 32 );
+ fi_fprintf(fph,"%s", str.c_str() );
+ for (size_t i=0; i<justify; i++)
+ {
+ fi_fprintf(fph,"%c", 32 );
+ }
+ }
+
+ void printRight(FILE_INTERFACE *fph,const std::string &str,size_t width)
+ {
+ size_t swid = str.size();
+ assert( swid <= width );
+ size_t justify = (width-swid)-1;
+
+ for (size_t i=0; i<justify; i++)
+ {
+ fi_fprintf(fph,"%c", 32 );
+ }
+
+ fi_fprintf(fph,"%s", str.c_str() );
+ fi_fprintf(fph,"%c", 32 );
+
+ }
+
+ void printCenter(FILE_INTERFACE *fph,const std::string &str,size_t width)
+ {
+ size_t swid = str.size();
+ if ( swid > width )
+ {
+ width = swid;
+ }
+
+ size_t count = 0;
+
+ size_t center = (width-swid)/2;
+ for (size_t i=0; i<center; i++)
+ {
+ fi_fprintf(fph,"%c", 32 );
+ count++;
+ }
+ count+=str.size();
+ fi_fprintf(fph,"%s", str.c_str() );
+ for (size_t i=0; i<center; i++)
+ {
+ fi_fprintf(fph,"%c", 32 );
+ count++;
+ }
+ if ( count < width )
+ {
+ assert( (count+1) == width );
+ fi_fprintf(fph,"%c", 32 );
+ }
+ }
+
+ void saveExcel(FILE *fph)
+ {
+ fprintf(fph,"<TABLE BORDER=\"1\">\r\n");
+ fprintf(fph," <caption><EM>%s</EM></caption>\r\n", mHeading.c_str() );
+
+
+ HtmlRowVector::iterator i;
+ for (i=mBody.begin(); i!=mBody.end(); i++)
+ {
+ HtmlRow *row = (*i);
+ row->saveExcel(fph,this);
+ }
+
+ fprintf(fph,"</TABLE>\r\n");
+ fprintf(fph,"<p></p>\r\n");
+ fprintf(fph,"<p></p>\r\n");
+ fprintf(fph,"<p></p>\r\n");
+ }
+
+
+ void saveSimpleHTML(FILE_INTERFACE *fph)
+ {
+ fi_fprintf(fph,"<TABLE BORDER=\"1\">\r\n");
+ fi_fprintf(fph," <caption><EM>%s</EM></caption>\r\n", mHeading.c_str() );
+
+
+ HtmlRowVector::iterator i;
+ for (i=mBody.begin(); i!=mBody.end(); i++)
+ {
+ HtmlRow *row = (*i);
+ row->htmlRow(fph,this);
+ }
+
+ fi_fprintf(fph,"</TABLE>\r\n");
+ fi_fprintf(fph,"<p></p>\r\n");
+ fi_fprintf(fph,"<p></p>\r\n");
+ fi_fprintf(fph,"<p></p>\r\n");
+ }
+
+ void saveCSV(FILE_INTERFACE *fph)
+ {
+ fi_fprintf(fph,"%s\r\n", mHeading.c_str() );
+ HtmlRowVector::iterator i;
+ for (i=mBody.begin(); i!=mBody.end(); i++)
+ {
+ HtmlRow *row = (*i);
+ row->saveCSV(fph);
+ }
+ fi_fprintf(fph,"\r\n");
+ }
+
+ void saveCPP(FILE_INTERFACE *fph)
+ {
+ fi_fprintf(fph," if ( 1 )\r\n");
+ fi_fprintf(fph," {\r\n");
+ fi_fprintf(fph," nvidia::HtmlTable *table = document->createHtmlTable(\"%s\");\r\n", mHeading.c_str() );
+ HtmlRowVector::iterator i;
+ for (i=mBody.begin(); i!=mBody.end(); i++)
+ {
+ HtmlRow *row = (*i);
+ row->saveCPP(fph);
+ }
+
+ if ( mComputeTotals )
+ {
+ for (size_t i=0; i<mExcludeTotals.size(); i++)
+ {
+ fi_fprintf(fph," table->excludeTotals(%d);\r\n", mExcludeTotals[i] );
+ }
+ fi_fprintf(fph," table->computeTotals();\r\n");
+ }
+
+
+ if ( !mSortRequests.empty() )
+ {
+ SortRequestVector::iterator i;
+ for (i=mSortRequests.begin(); i!=mSortRequests.end(); ++i)
+ {
+ SortRequest &sr = (*i);
+ fi_fprintf(fph," table->addSort(%c%s%c,%d,%s,%d,%s);\r\n", 34, sr.mSortName.c_str(), 34, sr.mPrimaryKey, tf(sr.mPrimaryAscending), sr.mSecondaryKey, tf(sr.mSecondaryAscending) );
+ }
+ }
+
+ fi_fprintf(fph," }\r\n");
+ fi_fprintf(fph,"\r\n");
+ }
+
+ void sortBody(const SortRequest &sr)
+ {
+ mSortRequest = sr;
+
+ size_t rcount = mBody.size();
+ int index = 0;
+
+ HtmlRow **rows = (HtmlRow **) HTML_MALLOC(sizeof(HtmlRow *)*rcount);
+ size_t *indices = (size_t *) HTML_MALLOC(sizeof(size_t)*rcount);
+
+
+ for (size_t i=0; i<rcount; i++)
+ {
+ HtmlRow *row = mBody[i];
+ if ( !row->isHeader() )
+ {
+ rows[index] = row;
+ indices[index] = i;
+ index++;
+ }
+ }
+
+ qsort( (void **)rows,index);
+
+ for (int i=0; i<index; i++)
+ {
+ HtmlRow *row = rows[i];
+ size_t dest = indices[i];
+ mBody[dest] = row;
+ }
+
+ HTML_FREE(rows);
+ HTML_FREE(indices);
+
+ }
+
+ void save(FILE_INTERFACE *fph,HtmlSaveType type)
+ {
+
+ if ( mBody.size() >= 2 ) // must have at least one header row and one data row
+ {
+
+ if ( mSortRequests.size() && type != HST_CPP )
+ {
+ SortRequestVector::iterator i;
+ for (i=mSortRequests.begin(); i!=mSortRequests.end(); i++)
+ {
+ sortBody( (*i) );
+ saveInternal(fph,type,(*i).mSortName.c_str());
+ }
+ }
+ else
+ {
+ saveInternal(fph,type,"");
+ }
+ }
+ }
+
+ void saveInternal(FILE_INTERFACE *fph,HtmlSaveType type,const char *secondary_caption)
+ {
+ bool totals = false;
+
+ if ( mComputeTotals )
+ {
+ totals = addTotalsRow();
+ }
+
+ switch ( type )
+ {
+ case HST_SIMPLE_HTML:
+ saveSimpleHTML(fph);
+ break;
+ case HST_CSV:
+ saveCSV(fph);
+ break;
+ case HST_TEXT:
+ BorderASCII();
+ saveText(fph,secondary_caption);
+ break;
+ case HST_TEXT_EXTENDED:
+ BorderDOS();
+ saveText(fph,secondary_caption);
+ break;
+ case HST_CPP:
+ saveCPP(fph);
+ break;
+ case HST_XML:
+ break;
+ }
+
+ if ( totals )
+ {
+ removeTotalsRow();
+ }
+ }
+
+ bool excluded(size_t c)
+ {
+ bool ret = false;
+
+ for (size_t i=0; i<mExcludeTotals.size(); i++)
+ {
+ if ( c == mExcludeTotals[i] )
+ {
+ ret = true;
+ break;
+ }
+ }
+
+ return ret;
+ }
+
+ float computeTotal(size_t column)
+ {
+ float ret = 0;
+ HtmlRowVector::iterator i;
+ for (i=mBody.begin(); i!=mBody.end(); i++)
+ {
+ HtmlRow *row = (*i);
+ if ( !row->isHeader() )
+ {
+ std::string str;
+ row->getString(column,str);
+ if ( isNumeric(str) )
+ {
+ float v = getFloatValue(str.c_str());
+ ret+=v;
+ }
+ }
+ }
+ return ret;
+ }
+
+ bool addTotalsRow(void)
+ {
+ bool ret = false;
+
+ if ( mBody.size() >= 2 )
+ {
+ HtmlRow *first_row = 0;
+
+ SizetVector csize;
+ HtmlRowVector::iterator i;
+ for (i=mBody.begin(); i!=mBody.end(); i++)
+ {
+ HtmlRow *row = (*i);
+ if ( !row->isHeader() && first_row == 0 )
+ {
+ first_row = row;
+ }
+ row->columnSizes(csize);
+ }
+ if ( first_row )
+ {
+ HtmlRow *totals = HTML_NEW(HtmlRow);
+ totals->setFooter(true);
+ size_t count = csize.size();
+ for (size_t i=0; i<count; i++)
+ {
+ if ( !excluded(i+1) )
+ {
+ std::string str;
+ first_row->getString(i,str);
+ if ( isNumeric(str) )
+ {
+ float v = computeTotal(i);
+ std::string str;
+ getFloat(v,str);
+ totals->addColumn(str.c_str());
+ }
+ else
+ {
+ if ( i == 0 )
+ {
+ totals->addColumn("Totals");
+ }
+ else
+ {
+ totals->addColumn("");
+ }
+ }
+ ret = true;
+ }
+ else
+ {
+ totals->addColumn("");
+ }
+ }
+ if ( ret )
+ {
+ mBody.push_back(totals);
+ }
+ else
+ {
+ HTML_DELETE(HtmlRow,totals);
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ void removeTotalsRow(void)
+ {
+ if ( mBody.size() )
+ {
+ size_t index = mBody.size()-1;
+ HtmlRow *row = mBody[index];
+ HTML_DELETE(HtmlRow,row);
+ HtmlRowVector::iterator i = mBody.end();
+ i--;
+ mBody.erase(i);
+ }
+ }
+
+ void saveText(FILE_INTERFACE *fph,const char *secondary_caption)
+ {
+ SizetVector csize;
+ HtmlRowVector::iterator i;
+ for (i=mBody.begin(); i!=mBody.end(); i++)
+ {
+ HtmlRow *row = (*i);
+ row->columnSizes(csize);
+ }
+
+ size_t column_count = csize.size();
+ size_t column_size = 0;
+
+ for (size_t i=0; i<column_count; i++)
+ {
+ csize[i]+=2;
+ column_size+=csize[i];
+ }
+
+
+ fi_fprintf(fph," \r\n" );
+ printCenter(fph,mHeading,column_size);
+ fi_fprintf(fph," \r\n" );
+
+ if ( secondary_caption && strlen(secondary_caption) > 0 )
+ {
+ printCenter(fph,secondary_caption,column_size);
+ fi_fprintf(fph," \r\n" );
+ }
+
+
+
+ //*****************************************
+ // Print the top border
+ //*****************************************
+
+ fi_fprintf(fph,"%c",UPPER_LEFT_BORDER );
+
+ for (size_t i=0; i<column_count; i++)
+ {
+ size_t c = csize[i];
+ for (size_t j=0; j<c; j++)
+ {
+ fi_fprintf(fph,"%c", TOP_BORDER );
+ }
+ if ( (i+1) < column_count )
+ {
+ fi_fprintf(fph,"%c", TOP_SEPARATOR );
+ }
+ }
+
+ fi_fprintf(fph,"%c",UPPER_RIGHT_BORDER );
+ fi_fprintf(fph," \r\n");
+
+
+ bool lastHeader = true; // assume heading by default..
+
+
+ //*****************************************
+ // Print the body data
+ //*****************************************
+ HtmlRowVector::iterator j;
+ for (j=mBody.begin(); j!=mBody.end(); j++)
+ {
+ HtmlRow &row = *(*j);
+
+ if ( (!row.isHeader() && lastHeader) || row.isFooter() )
+ {
+ //*****************************************
+ // Print the separator border
+ //*****************************************
+
+
+ fi_fprintf(fph,"%c",LEFT_SIDE_BORDER );
+
+ for (size_t i=0; i<column_count; i++)
+ {
+ size_t c = csize[i];
+ for (size_t j=0; j<c; j++)
+ {
+ fi_fprintf(fph,"%c", TOP_BORDER );
+ }
+ if ( (i+1) < column_count )
+ {
+ fi_fprintf(fph,"%c", CROSS_BORDER );
+ }
+ }
+
+ fi_fprintf(fph,"%c",RIGHT_SIDE_BORDER );
+ fi_fprintf(fph," \r\n");
+ }
+
+ lastHeader = row.isHeader();
+
+ fi_fprintf(fph,"%c", LEFT_BORDER );
+
+ for (size_t i=0; i<column_count; i++)
+ {
+ size_t c = csize[i];
+
+
+ std::string str;
+ row.getString(i,str);
+
+ assert( str.size() < csize[i] );
+
+ if ( lastHeader )
+ {
+ printCenter(fph,str,c);
+ }
+ else if ( isNumeric(str) )
+ {
+ printRight(fph,str,c);
+ }
+ else
+ {
+ printLeft(fph,str,c);
+ }
+
+ if ( (i+1) < column_count )
+ {
+ fi_fprintf(fph,"%c", VERTICAL_SEPARATOR );
+ }
+ }
+
+ fi_fprintf(fph,"%c",RIGHT_BORDER );
+ fi_fprintf(fph," \r\n");
+
+
+ }
+
+ //*****************************************
+ // Print the separator border
+ //*****************************************
+
+
+ fi_fprintf(fph,"%c",LOWER_LEFT_BORDER );
+
+ for (size_t i=0; i<column_count; i++)
+ {
+ size_t c = csize[i];
+ for (size_t j=0; j<c; j++)
+ {
+ fi_fprintf(fph,"%c", TOP_BORDER );
+ }
+ if ( (i+1) < column_count )
+ {
+ fi_fprintf(fph,"%c", CROSS_BORDER );
+ }
+ }
+
+ fi_fprintf(fph,"%c",LOWER_RIGHT_BORDER );
+ fi_fprintf(fph," \r\n");
+ fi_fprintf(fph," \r\n");
+ fi_fprintf(fph," \r\n");
+
+
+
+
+ }
+
+ HtmlDocument * getDocument(void) { return mParent; };
+
+ HtmlTableInterface * getHtmlTableInterface(void)
+ {
+ HtmlTableInterface *ret = 0;
+
+ ret = mParent->getHtmlTableInterface();
+
+ return ret;
+ }
+
+ void computeTotals(void) // compute and display totals of numeric columns when displaying this table.
+ {
+ mComputeTotals = true;
+ }
+
+ void excludeTotals(unsigned int column)
+ {
+ mExcludeTotals.push_back(column);
+ }
+
+ void addSort(const char *sort_name,unsigned int primary_key,bool primary_ascending,unsigned int secondary_key,bool secondary_ascending) // adds a sorted result. You can set up mulitple sort requests for a single table.
+ {
+ SortRequest sr(sort_name,primary_key,primary_ascending,secondary_key,secondary_ascending);
+ mSortRequests.push_back(sr);
+ }
+
+ void setColumnColor(unsigned int column,unsigned int color) // set a color for a specific column.
+ {
+ mColumnColors.push_back(column);
+ mColumnColors.push_back(color);
+ }
+
+ void setHeaderColor(unsigned int color) // color for header lines
+ {
+ mHeaderColor = color;
+ }
+
+ void setFooterColor(unsigned int color) // color for footer lines
+ {
+ mFooterColor = color;
+ }
+
+ void setBodyColor(unsigned int color)
+ {
+ mBodyColor = color;
+ }
+
+ unsigned int getColor(unsigned int column,bool isHeader,bool isFooter)
+ {
+
+ unsigned int ret = mBodyColor;
+
+ if ( isHeader )
+ {
+ ret = mHeaderColor;
+ }
+ else if ( isFooter )
+ {
+ ret = mFooterColor;
+ }
+ else
+ {
+ unsigned int count = unsigned int(mColumnColors.size())/2;
+ for (unsigned int i=0; i<count; i++)
+ {
+ unsigned int c = unsigned int(mColumnColors[i*2+0]);
+ unsigned int color = unsigned int(mColumnColors[i*2+1]);
+ if ( column == c )
+ {
+ ret = color;
+ break;
+ }
+ }
+ }
+ return ret;
+ }
+
+ virtual void setOrder(unsigned int order)
+ {
+ mDisplayOrder = order;
+ }
+
+
+ const std::string & getHeading(void) const { return mHeading; };
+
+private:
+ unsigned int mDisplayOrder;
+ unsigned int mHeaderColor;
+ unsigned int mFooterColor;
+ unsigned int mBodyColor;
+ std::vector< unsigned int > mColumnColors;
+ HtmlDocument *mParent;
+ std::string mHeading;
+ HtmlRow *mCurrent;
+ HtmlRowVector mBody;
+ InPlaceParser mParser;
+ SortRequest mSortRequest; // the current sort request...
+ SortRequestVector mSortRequests;
+
+ bool mComputeTotals;
+ std::vector< unsigned int > mExcludeTotals;
+
+ unsigned char UPPER_LEFT_BORDER;
+ unsigned char UPPER_RIGHT_BORDER;
+ unsigned char LOWER_LEFT_BORDER;
+ unsigned char LOWER_RIGHT_BORDER;
+ unsigned char TOP_SEPARATOR;
+ unsigned char BOTTOM_SEPARATOR;
+ unsigned char TOP_BORDER;
+ unsigned char BOTTOM_BORDER;
+ unsigned char LEFT_BORDER;
+ unsigned char RIGHT_BORDER;
+ unsigned char VERTICAL_SEPARATOR;
+ unsigned char LEFT_SIDE_BORDER;
+ unsigned char RIGHT_SIDE_BORDER;
+ unsigned char CROSS_BORDER;
+
+
+};
+
+typedef std::vector< _HtmlTable * > HtmlTableVector;
+
+
+class SortTables : public QuickSortPointers
+{
+public:
+ SortTables(unsigned int tcount,_HtmlTable **tables)
+ {
+ qsort((void **)tables,(int)tcount);
+ }
+
+ virtual int compare(void **p1,void **p2)
+ {
+ _HtmlTable **tp1 = (_HtmlTable **)p1;
+ _HtmlTable **tp2 = (_HtmlTable **)p2;
+ _HtmlTable *t1 = *tp1;
+ _HtmlTable *t2 = *tp2;
+ return (int)t1->getDisplayOrder() - (int)t2->getDisplayOrder();
+ }
+
+};
+
+class _HtmlDocument : public HtmlDocument
+{
+public:
+ _HtmlDocument(const char *document_name,HtmlTableInterface *iface)
+ {
+ mDisplayOrder = 0;
+ mInterface = iface;
+ if ( document_name )
+ {
+ mDocumentName = document_name;
+ }
+ }
+
+ virtual ~_HtmlDocument(void)
+ {
+ reset();
+ }
+
+ unsigned int getDisplayOrder(void)
+ {
+ mDisplayOrder++;
+ return mDisplayOrder;
+ }
+
+ void reset(void)
+ {
+ HtmlTableVector::iterator i;
+ for (i=mTables.begin(); i!=mTables.end(); i++)
+ {
+ HtmlTable *t = (*i);
+ _HtmlTable *tt = static_cast< _HtmlTable *>(t);
+ HTML_DELETE(_HtmlTable,tt);
+ }
+ mTables.clear();
+ }
+
+
+ const char * saveDocument(size_t &len,HtmlSaveType type)
+ {
+ const char *ret = 0;
+
+ FILE_INTERFACE *fph = fi_fopen("temp", "wmem");
+ if ( fph )
+ {
+
+ switch ( type )
+ {
+ case HST_SIMPLE_HTML:
+ fi_fprintf(fph,"<HTML>\r\n");
+ fi_fprintf(fph,"<HEAD>\r\n");
+ fi_fprintf(fph,"<TITLE>%s</TITLE>\r\n", mDocumentName.c_str() );
+ fi_fprintf(fph,"<BODY>\r\n");
+ break;
+ case HST_CSV:
+ break;
+ case HST_CPP:
+#if USE_CPP
+ fi_fprintf(fph,"#include <stdlib.h>\r\n");
+ fi_fprintf(fph,"#include <stdio.h>\r\n");
+ fi_fprintf(fph,"#include <string.h>\r\n");
+ fi_fprintf(fph,"#include <assert.h>\r\n");
+ fi_fprintf(fph,"\r\n");
+ fi_fprintf(fph,"#pragma warning(disable:4996)\r\n");
+ fi_fprintf(fph,"\r\n");
+ fi_fprintf(fph,"#include \"HtmlTable.h\"\r\n");
+ fi_fprintf(fph,"\r\n");
+
+
+ fi_fprintf(fph,"%s\r\n","void testSave(nvidia::HtmlDocument *document,const char *fname,nvidia::HtmlSaveType type)");
+ fi_fprintf(fph,"%s\r\n","{");
+ fi_fprintf(fph,"%s\r\n"," size_t len;");
+ fi_fprintf(fph,"%s\r\n"," const char *data = document->saveDocument(len,type);");
+ fi_fprintf(fph,"%s\r\n"," if ( data )");
+ fi_fprintf(fph,"%s\r\n"," {");
+ fi_fprintf(fph,"%s\r\n"," printf(\"Saving document '%s' which is %d bytes long.\\r\\n\", fname, len );");
+ fi_fprintf(fph,"%s\r\n"," FILE *fph = fopen(fname,\"wb\");");
+ fi_fprintf(fph,"%s\r\n"," if ( fph )");
+ fi_fprintf(fph,"%s\r\n"," {");
+ fi_fprintf(fph,"%s\r\n"," fwrite(data,len,1,fph);");
+ fi_fprintf(fph,"%s\r\n"," fclose(fph);");
+ fi_fprintf(fph,"%s\r\n"," }");
+ fi_fprintf(fph,"%s\r\n"," else");
+ fi_fprintf(fph,"%s\r\n"," {");
+ fi_fprintf(fph,"%s\r\n"," printf(\"Failed to open file for write access.\\r\\n\");");
+ fi_fprintf(fph,"%s\r\n"," }");
+ fi_fprintf(fph,"%s\r\n"," document->releaseDocumentMemory(data);");
+ fi_fprintf(fph,"%s\r\n"," }");
+ fi_fprintf(fph,"%s\r\n"," else");
+ fi_fprintf(fph,"%s\r\n"," {");
+ fi_fprintf(fph,"%s\r\n"," printf(\"Failed to save document %s.\\r\\n\", fname );");
+ fi_fprintf(fph,"%s\r\n"," }");
+ fi_fprintf(fph,"%s\r\n","}");
+
+ fi_fprintf(fph,"void html_test(void)\r\n");
+ fi_fprintf(fph,"{\r\n");
+ fi_fprintf(fph," nvidia::HtmlTableInterface *iface = nvidia::getHtmlTableInterface();\r\n");
+ fi_fprintf(fph," nvidia::HtmlDocument *document = iface->createHtmlDocument(\"%s\");\r\n", mDocumentName.c_str() );
+#endif
+ break;
+ case HST_TEXT:
+ case HST_TEXT_EXTENDED:
+ fi_fprintf(fph,"[%s]\r\n", mDocumentName.c_str() );
+ fi_fprintf(fph,"\r\n" );
+ break;
+ default:
+ assert(0);
+ }
+
+ if ( !mTables.empty() )
+ {
+ unsigned int tcount = mTables.size();
+ _HtmlTable **tables = &mTables[0];
+ SortTables st( tcount, tables );
+ HtmlTableVector::iterator i;
+ for (i=mTables.begin(); i!=mTables.end(); ++i)
+ {
+ (*i)->save(fph,type);
+ }
+ }
+
+ switch ( type )
+ {
+ case HST_SIMPLE_HTML:
+ fi_fprintf(fph,"</BODY>\r\n");
+ fi_fprintf(fph,"</HEAD>\r\n");
+ fi_fprintf(fph,"</HTML>\r\n");
+ break;
+ case HST_CPP:
+#if USE_CPP
+ fi_fprintf(fph,"%s\r\n"," testSave(document,\"table.txt\", nvidia::HST_TEXT );");
+ fi_fprintf(fph,"%s\r\n"," testSave(document,\"table.html\", nvidia::HST_SIMPLE_HTML );");
+ fi_fprintf(fph,"%s\r\n"," testSave(document,\"table.cpp\", nvidia::HST_CPP );");
+ fi_fprintf(fph,"%s\r\n","");
+ fi_fprintf(fph,"%s\r\n","");
+ fi_fprintf(fph,"%s\r\n","");
+ fi_fprintf(fph,"%s\r\n"," iface->releaseHtmlDocument(document);");
+ fi_fprintf(fph,"%s\r\n","");
+ fi_fprintf(fph,"%s\r\n","}");
+#endif
+ break;
+ case HST_CSV:
+ break;
+ case HST_TEXT:
+ case HST_TEXT_EXTENDED:
+ fi_fprintf(fph,"\r\n" );
+ break;
+ default:
+ assert(0);
+ }
+ void *data = fi_getMemBuffer(fph,len);
+ if ( data )
+ {
+ char *temp = (char *)HTML_MALLOC(sizeof(char)*(len+1));
+ temp[len] = 0;
+ memcpy(temp,data,len);
+ ret = temp;
+ }
+ fi_fclose(fph);
+ }
+ return ret;
+ }
+
+ HtmlTable * createHtmlTable(const char *heading)
+ {
+ _HtmlTable *ret = HTML_NEW(_HtmlTable)(heading,this);
+ mTables.push_back(ret);
+ return ret;
+ }
+
+ void releaseDocumentMemory(const char *mem) // release memory previously allocated for a document save.
+ {
+ HTML_FREE((void *)mem);
+ }
+
+
+ HtmlTableInterface *getHtmlTableInterface(void)
+ {
+ return mInterface;
+ }
+
+ bool saveExcel(const char *fname) // excel format can only be saved directly to files on disk, as it needs to create a sub-directory for the intermediate files.
+ {
+ bool ret = false;
+
+ std::string dest_name;
+ std::string base_name;
+ std::string root_dir;
+
+
+ char scratch[512];
+ strcpy(scratch,fname);
+ char *dot = lastDot(scratch);
+ if ( dot )
+ {
+ strcpy(scratch,fname);
+ char *slash = lastSlash(scratch);
+ if ( slash )
+ {
+ slash++;
+ char bname[512];
+ strcpy(bname,slash);
+ dest_name = bname;
+ *slash = 0;
+ dot = lastDot(bname);
+ assert(dot);
+ if ( dot )
+ {
+ char temp[512];
+ *dot = 0;
+ base_name = bname;
+ root_dir = scratch;
+ sprintf(temp,"%s%s_files",scratch,bname);
+ _mkdir(temp);
+ }
+ }
+ else
+ {
+ dest_name = fname;
+ *dot = 0;
+ base_name = scratch;
+ char temp[512];
+ sprintf(temp,"%s_files", scratch );
+ _mkdir(temp);
+ }
+ }
+
+ saveExcel(dest_name,base_name,root_dir);
+
+ return ret;
+ }
+
+ void saveExcel(const std::string &dest_name,const std::string &base_name,std::string &root_dir)
+ {
+#if USE_EXCEL
+
+ char scratch[512];
+ sprintf(scratch,"%s%s", root_dir.c_str(), dest_name.c_str() );
+
+ unsigned int tableCount = unsigned int(mTables.size());
+
+ FILE *fph = fopen(scratch,"wb");
+
+ if ( fph )
+ {
+ fprintf(fph,"%s\r\n","<html xmlns:o=\"urn:schemas-microsoft-com:office:office\"");
+ fprintf(fph,"%s\r\n","xmlns:x=\"urn:schemas-microsoft-com:office:excel\"");
+ fprintf(fph,"%s\r\n","xmlns=\"http://www.w3.org/TR/REC-html40\">");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","<head>");
+ fprintf(fph,"%s\r\n","<meta name=\"Excel Workbook Frameset\">");
+ fprintf(fph,"%s\r\n","<meta http-equiv=Content-Type content=\"text/html; charset=windows-1252\">");
+ fprintf(fph,"%s\r\n","<meta name=ProgId content=Excel.Sheet>");
+ fprintf(fph,"%s\r\n","<meta name=Generator content=\"Microsoft Excel 11\">");
+ fprintf(fph,"<link rel=File-List href=\"%s_files/filelist.xml\">\r\n", base_name.c_str());
+ fprintf(fph,"<link rel=Edit-Time-Data href=\"%s_files/editdata.mso\">\r\n", base_name.c_str());
+ fprintf(fph,"<link rel=OLE-Object-Data href=\"%s_files/oledata.mso\">\r\n", base_name.c_str());
+ fprintf(fph,"%s\r\n","<!--[if gte mso 9]><xml>");
+ fprintf(fph,"%s\r\n"," <o:DocumentProperties>");
+ fprintf(fph,"%s\r\n"," <o:Author>John Ratcliff</o:Author>");
+ fprintf(fph,"%s\r\n"," <o:LastAuthor>John Ratcliff</o:LastAuthor>");
+ fprintf(fph,"%s\r\n"," <o:Created>2008-04-17T20:09:59Z</o:Created>");
+ fprintf(fph,"%s\r\n"," <o:LastSaved>2008-04-17T20:10:32Z</o:LastSaved>");
+ fprintf(fph,"%s\r\n"," <o:Company>Simutronics Corporation</o:Company>");
+ fprintf(fph,"%s\r\n"," <o:Version>11.8132</o:Version>");
+ fprintf(fph,"%s\r\n"," </o:DocumentProperties>");
+ fprintf(fph,"%s\r\n"," <o:OfficeDocumentSettings>");
+ fprintf(fph,"%s\r\n"," <o:DownloadComponents/>");
+ fprintf(fph,"%s\r\n"," <o:LocationOfComponents HRef=\"file:///D:\\ENGLISH\\OFFICE_SYSTEM\\OFFICE2003\\PROFESSIONAL\\\"/>");
+ fprintf(fph,"%s\r\n"," </o:OfficeDocumentSettings>");
+ fprintf(fph,"%s\r\n","</xml><![endif]--><![if !supportTabStrip]>");
+
+ {
+ unsigned int index = 0;
+ HtmlTableVector::iterator i;
+ for (i=mTables.begin(); i!=mTables.end(); ++i)
+ {
+ fprintf(fph,"<link id=\"shLink\" href=\"%s_files/sheet%03d.htm\">\r\n", base_name.c_str(), index+1 );
+ index++;
+ }
+ }
+
+
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","<link id=\"shLink\">");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","<script language=\"JavaScript\">");
+ fprintf(fph,"%s\r\n","<!--");
+
+ {
+ fprintf(fph," var c_lTabs=%d;\r\n", mTables.size());
+ }
+
+
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n"," var c_rgszSh=new Array(c_lTabs);");
+
+ {
+ unsigned int index = 0;
+ HtmlTableVector::iterator i;
+ for (i=mTables.begin(); i!=mTables.end(); ++i)
+ {
+ fprintf(fph," c_rgszSh[%d] = \"Sheet%d\";\r\n",index,index+1);
+ index++;
+ }
+ }
+
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n"," var c_rgszClr=new Array(8);");
+ fprintf(fph,"%s\r\n"," c_rgszClr[0]=\"window\";");
+ fprintf(fph,"%s\r\n"," c_rgszClr[1]=\"buttonface\";");
+ fprintf(fph,"%s\r\n"," c_rgszClr[2]=\"windowframe\";");
+ fprintf(fph,"%s\r\n"," c_rgszClr[3]=\"windowtext\";");
+ fprintf(fph,"%s\r\n"," c_rgszClr[4]=\"threedlightshadow\";");
+ fprintf(fph,"%s\r\n"," c_rgszClr[5]=\"threedhighlight\";");
+ fprintf(fph,"%s\r\n"," c_rgszClr[6]=\"threeddarkshadow\";");
+ fprintf(fph,"%s\r\n"," c_rgszClr[7]=\"threedshadow\";");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n"," var g_iShCur;");
+ fprintf(fph,"%s\r\n"," var g_rglTabX=new Array(c_lTabs);");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","function fnGetIEVer()");
+ fprintf(fph,"%s\r\n","{");
+ fprintf(fph,"%s\r\n"," var ua=window.navigator.userAgent");
+ fprintf(fph,"%s\r\n"," var msie=ua.indexOf(\"MSIE\")");
+ fprintf(fph,"%s\r\n"," if (msie>0 && window.navigator.platform==\"Win32\")");
+ fprintf(fph,"%s\r\n"," return parseInt(ua.substring(msie+5,ua.indexOf(\".\", msie)));");
+ fprintf(fph,"%s\r\n"," else");
+ fprintf(fph,"%s\r\n"," return 0;");
+ fprintf(fph,"%s\r\n","}");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","function fnBuildFrameset()");
+ fprintf(fph,"%s\r\n","{");
+ fprintf(fph,"%s\r\n"," var szHTML=\"<frameset rows=\\\"*,18\\\" border=0 width=0 frameborder=no framespacing=0>\"+");
+ fprintf(fph,"%s\r\n"," \"<frame src=\\\"\"+document.all.item(\"shLink\")[1].href+\"\\\" name=\\\"frSheet\\\" noresize>\"+");
+ fprintf(fph,"%s\r\n"," \"<frameset cols=\\\"54,*\\\" border=0 width=0 frameborder=no framespacing=0>\"+");
+ fprintf(fph,"%s\r\n"," \"<frame src=\\\"\\\" name=\\\"frScroll\\\" marginwidth=0 marginheight=0 scrolling=no>\"+");
+ fprintf(fph,"%s\r\n"," \"<frame src=\\\"\\\" name=\\\"frTabs\\\" marginwidth=0 marginheight=0 scrolling=no>\"+");
+ fprintf(fph,"%s\r\n"," \"</frameset></frameset><plaintext>\";");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n"," with (document) {");
+ fprintf(fph,"%s\r\n"," open(\"text/html\",\"replace\");");
+ fprintf(fph,"%s\r\n"," write(szHTML);");
+ fprintf(fph,"%s\r\n"," close();");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n"," fnBuildTabStrip();");
+ fprintf(fph,"%s\r\n","}");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","function fnBuildTabStrip()");
+ fprintf(fph,"%s\r\n","{");
+ fprintf(fph,"%s\r\n"," var szHTML=");
+ fprintf(fph,"%s\r\n"," \"<html><head><style>.clScroll {font:8pt Courier New;color:\"+c_rgszClr[6]+\";cursor:default;line-height:10pt;}\"+");
+ fprintf(fph,"%s\r\n"," \".clScroll2 {font:10pt Arial;color:\"+c_rgszClr[6]+\";cursor:default;line-height:11pt;}</style></head>\"+");
+ fprintf(fph,"%s\r\n"," \"<body onclick=\\\"event.returnValue=false;\\\" ondragstart=\\\"event.returnValue=false;\\\" onselectstart=\\\"event.returnValue=false;\\\" bgcolor=\"+c_rgszClr[4]+\" topmargin=0 leftmargin=0><table cellpadding=0 cellspacing=0 width=100%>\"+");
+ fprintf(fph,"%s\r\n"," \"<tr><td colspan=6 height=1 bgcolor=\"+c_rgszClr[2]+\"></td></tr>\"+");
+ fprintf(fph,"%s\r\n"," \"<tr><td style=\\\"font:1pt\\\">&nbsp;<td>\"+");
+ fprintf(fph,"%s\r\n"," \"<td valign=top id=tdScroll class=\\\"clScroll\\\" onclick=\\\"parent.fnFastScrollTabs(0);\\\" onmouseover=\\\"parent.fnMouseOverScroll(0);\\\" onmouseout=\\\"parent.fnMouseOutScroll(0);\\\"><a>&#171;</a></td>\"+");
+ fprintf(fph,"%s\r\n"," \"<td valign=top id=tdScroll class=\\\"clScroll2\\\" onclick=\\\"parent.fnScrollTabs(0);\\\" ondblclick=\\\"parent.fnScrollTabs(0);\\\" onmouseover=\\\"parent.fnMouseOverScroll(1);\\\" onmouseout=\\\"parent.fnMouseOutScroll(1);\\\"><a>&lt</a></td>\"+");
+ fprintf(fph,"%s\r\n"," \"<td valign=top id=tdScroll class=\\\"clScroll2\\\" onclick=\\\"parent.fnScrollTabs(1);\\\" ondblclick=\\\"parent.fnScrollTabs(1);\\\" onmouseover=\\\"parent.fnMouseOverScroll(2);\\\" onmouseout=\\\"parent.fnMouseOutScroll(2);\\\"><a>&gt</a></td>\"+");
+ fprintf(fph,"%s\r\n"," \"<td valign=top id=tdScroll class=\\\"clScroll\\\" onclick=\\\"parent.fnFastScrollTabs(1);\\\" onmouseover=\\\"parent.fnMouseOverScroll(3);\\\" onmouseout=\\\"parent.fnMouseOutScroll(3);\\\"><a>&#187;</a></td>\"+");
+ fprintf(fph,"%s\r\n"," \"<td style=\\\"font:1pt\\\">&nbsp;<td></tr></table></body></html>\";");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n"," with (frames['frScroll'].document) {");
+ fprintf(fph,"%s\r\n"," open(\"text/html\",\"replace\");");
+ fprintf(fph,"%s\r\n"," write(szHTML);");
+ fprintf(fph,"%s\r\n"," close();");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n"," szHTML =");
+ fprintf(fph,"%s\r\n"," \"<html><head>\"+");
+ fprintf(fph,"%s\r\n"," \"<style>A:link,A:visited,A:active {text-decoration:none;\"+\"color:\"+c_rgszClr[3]+\";}\"+");
+ fprintf(fph,"%s\r\n"," \".clTab {cursor:hand;background:\"+c_rgszClr[1]+\";font:9pt Arial;padding-left:3px;padding-right:3px;text-align:center;}\"+");
+ fprintf(fph,"%s\r\n"," \".clBorder {background:\"+c_rgszClr[2]+\";font:1pt;}\"+");
+ fprintf(fph,"%s\r\n"," \"</style></head><body onload=\\\"parent.fnInit();\\\" onselectstart=\\\"event.returnValue=false;\\\" ondragstart=\\\"event.returnValue=false;\\\" bgcolor=\"+c_rgszClr[4]+");
+ fprintf(fph,"%s\r\n"," \" topmargin=0 leftmargin=0><table id=tbTabs cellpadding=0 cellspacing=0>\";");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n"," var iCellCount=(c_lTabs+1)*2;");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n"," var i;");
+ fprintf(fph,"%s\r\n"," for (i=0;i<iCellCount;i+=2)");
+ fprintf(fph,"%s\r\n"," szHTML+=\"<col width=1><col>\";");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n"," var iRow;");
+ fprintf(fph,"%s\r\n"," for (iRow=0;iRow<6;iRow++) {");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n"," szHTML+=\"<tr>\";");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n"," if (iRow==5)");
+ fprintf(fph,"%s\r\n"," szHTML+=\"<td colspan=\"+iCellCount+\"></td>\";");
+ fprintf(fph,"%s\r\n"," else {");
+ fprintf(fph,"%s\r\n"," if (iRow==0) {");
+ fprintf(fph,"%s\r\n"," for(i=0;i<iCellCount;i++)");
+ fprintf(fph,"%s\r\n"," szHTML+=\"<td height=1 class=\\\"clBorder\\\"></td>\";");
+ fprintf(fph,"%s\r\n"," } else if (iRow==1) {");
+ fprintf(fph,"%s\r\n"," for(i=0;i<c_lTabs;i++) {");
+ fprintf(fph,"%s\r\n"," szHTML+=\"<td height=1 nowrap class=\\\"clBorder\\\">&nbsp;</td>\";");
+ fprintf(fph,"%s\r\n"," szHTML+=");
+ fprintf(fph,"%s\r\n"," \"<td id=tdTab height=1 nowrap class=\\\"clTab\\\" onmouseover=\\\"parent.fnMouseOverTab(\"+i+\");\\\" onmouseout=\\\"parent.fnMouseOutTab(\"+i+\");\\\">\"+");
+ fprintf(fph,"%s\r\n"," \"<a href=\\\"\"+document.all.item(\"shLink\")[i].href+\"\\\" target=\\\"frSheet\\\" id=aTab>&nbsp;\"+c_rgszSh[i]+\"&nbsp;</a></td>\";");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n"," szHTML+=\"<td id=tdTab height=1 nowrap class=\\\"clBorder\\\"><a id=aTab>&nbsp;</a></td><td width=100%></td>\";");
+ fprintf(fph,"%s\r\n"," } else if (iRow==2) {");
+ fprintf(fph,"%s\r\n"," for (i=0;i<c_lTabs;i++)");
+ fprintf(fph,"%s\r\n"," szHTML+=\"<td height=1></td><td height=1 class=\\\"clBorder\\\"></td>\";");
+ fprintf(fph,"%s\r\n"," szHTML+=\"<td height=1></td><td height=1></td>\";");
+ fprintf(fph,"%s\r\n"," } else if (iRow==3) {");
+ fprintf(fph,"%s\r\n"," for (i=0;i<iCellCount;i++)");
+ fprintf(fph,"%s\r\n"," szHTML+=\"<td height=1></td>\";");
+ fprintf(fph,"%s\r\n"," } else if (iRow==4) {");
+ fprintf(fph,"%s\r\n"," for (i=0;i<c_lTabs;i++)");
+ fprintf(fph,"%s\r\n"," szHTML+=\"<td height=1 width=1></td><td height=1></td>\";");
+ fprintf(fph,"%s\r\n"," szHTML+=\"<td height=1 width=1></td><td></td>\";");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n"," szHTML+=\"</tr>\";");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n"," szHTML+=\"</table></body></html>\";");
+ fprintf(fph,"%s\r\n"," with (frames['frTabs'].document) {");
+ fprintf(fph,"%s\r\n"," open(\"text/html\",\"replace\");");
+ fprintf(fph,"%s\r\n"," charset=document.charset;");
+ fprintf(fph,"%s\r\n"," write(szHTML);");
+ fprintf(fph,"%s\r\n"," close();");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n","}");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","function fnInit()");
+ fprintf(fph,"%s\r\n","{");
+ fprintf(fph,"%s\r\n"," g_rglTabX[0]=0;");
+ fprintf(fph,"%s\r\n"," var i;");
+ fprintf(fph,"%s\r\n"," for (i=1;i<=c_lTabs;i++)");
+ fprintf(fph,"%s\r\n"," with (frames['frTabs'].document.all.tbTabs.rows[1].cells[fnTabToCol(i-1)])");
+ fprintf(fph,"%s\r\n"," g_rglTabX[i]=offsetLeft+offsetWidth-6;");
+ fprintf(fph,"%s\r\n","}");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","function fnTabToCol(iTab)");
+ fprintf(fph,"%s\r\n","{");
+ fprintf(fph,"%s\r\n"," return 2*iTab+1;");
+ fprintf(fph,"%s\r\n","}");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","function fnNextTab(fDir)");
+ fprintf(fph,"%s\r\n","{");
+ fprintf(fph,"%s\r\n"," var iNextTab=-1;");
+ fprintf(fph,"%s\r\n"," var i;");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n"," with (frames['frTabs'].document.body) {");
+ fprintf(fph,"%s\r\n"," if (fDir==0) {");
+ fprintf(fph,"%s\r\n"," if (scrollLeft>0) {");
+ fprintf(fph,"%s\r\n"," for (i=0;i<c_lTabs&&g_rglTabX[i]<scrollLeft;i++);");
+ fprintf(fph,"%s\r\n"," if (i<c_lTabs)");
+ fprintf(fph,"%s\r\n"," iNextTab=i-1;");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n"," } else {");
+ fprintf(fph,"%s\r\n"," if (g_rglTabX[c_lTabs]+6>offsetWidth+scrollLeft) {");
+ fprintf(fph,"%s\r\n"," for (i=0;i<c_lTabs&&g_rglTabX[i]<=scrollLeft;i++);");
+ fprintf(fph,"%s\r\n"," if (i<c_lTabs)");
+ fprintf(fph,"%s\r\n"," iNextTab=i;");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n"," return iNextTab;");
+ fprintf(fph,"%s\r\n","}");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","function fnScrollTabs(fDir)");
+ fprintf(fph,"%s\r\n","{");
+ fprintf(fph,"%s\r\n"," var iNextTab=fnNextTab(fDir);");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n"," if (iNextTab>=0) {");
+ fprintf(fph,"%s\r\n"," frames['frTabs'].scroll(g_rglTabX[iNextTab],0);");
+ fprintf(fph,"%s\r\n"," return true;");
+ fprintf(fph,"%s\r\n"," } else");
+ fprintf(fph,"%s\r\n"," return false;");
+ fprintf(fph,"%s\r\n","}");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","function fnFastScrollTabs(fDir)");
+ fprintf(fph,"%s\r\n","{");
+ fprintf(fph,"%s\r\n"," if (c_lTabs>16)");
+ fprintf(fph,"%s\r\n"," frames['frTabs'].scroll(g_rglTabX[fDir?c_lTabs-1:0],0);");
+ fprintf(fph,"%s\r\n"," else");
+ fprintf(fph,"%s\r\n"," if (fnScrollTabs(fDir)>0) window.setTimeout(\"fnFastScrollTabs(\"+fDir+\");\",5);");
+ fprintf(fph,"%s\r\n","}");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","function fnSetTabProps(iTab,fActive)");
+ fprintf(fph,"%s\r\n","{");
+ fprintf(fph,"%s\r\n"," var iCol=fnTabToCol(iTab);");
+ fprintf(fph,"%s\r\n"," var i;");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n"," if (iTab>=0) {");
+ fprintf(fph,"%s\r\n"," with (frames['frTabs'].document.all) {");
+ fprintf(fph,"%s\r\n"," with (tbTabs) {");
+ fprintf(fph,"%s\r\n"," for (i=0;i<=4;i++) {");
+ fprintf(fph,"%s\r\n"," with (rows[i]) {");
+ fprintf(fph,"%s\r\n"," if (i==0)");
+ fprintf(fph,"%s\r\n"," cells[iCol].style.background=c_rgszClr[fActive?0:2];");
+ fprintf(fph,"%s\r\n"," else if (i>0 && i<4) {");
+ fprintf(fph,"%s\r\n"," if (fActive) {");
+ fprintf(fph,"%s\r\n"," cells[iCol-1].style.background=c_rgszClr[2];");
+ fprintf(fph,"%s\r\n"," cells[iCol].style.background=c_rgszClr[0];");
+ fprintf(fph,"%s\r\n"," cells[iCol+1].style.background=c_rgszClr[2];");
+ fprintf(fph,"%s\r\n"," } else {");
+ fprintf(fph,"%s\r\n"," if (i==1) {");
+ fprintf(fph,"%s\r\n"," cells[iCol-1].style.background=c_rgszClr[2];");
+ fprintf(fph,"%s\r\n"," cells[iCol].style.background=c_rgszClr[1];");
+ fprintf(fph,"%s\r\n"," cells[iCol+1].style.background=c_rgszClr[2];");
+ fprintf(fph,"%s\r\n"," } else {");
+ fprintf(fph,"%s\r\n"," cells[iCol-1].style.background=c_rgszClr[4];");
+ fprintf(fph,"%s\r\n"," cells[iCol].style.background=c_rgszClr[(i==2)?2:4];");
+ fprintf(fph,"%s\r\n"," cells[iCol+1].style.background=c_rgszClr[4];");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n"," } else");
+ fprintf(fph,"%s\r\n"," cells[iCol].style.background=c_rgszClr[fActive?2:4];");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n"," with (aTab[iTab].style) {");
+ fprintf(fph,"%s\r\n"," cursor=(fActive?\"default\":\"hand\");");
+ fprintf(fph,"%s\r\n"," color=c_rgszClr[3];");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n","}");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","function fnMouseOverScroll(iCtl)");
+ fprintf(fph,"%s\r\n","{");
+ fprintf(fph,"%s\r\n"," frames['frScroll'].document.all.tdScroll[iCtl].style.color=c_rgszClr[7];");
+ fprintf(fph,"%s\r\n","}");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","function fnMouseOutScroll(iCtl)");
+ fprintf(fph,"%s\r\n","{");
+ fprintf(fph,"%s\r\n"," frames['frScroll'].document.all.tdScroll[iCtl].style.color=c_rgszClr[6];");
+ fprintf(fph,"%s\r\n","}");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","function fnMouseOverTab(iTab)");
+ fprintf(fph,"%s\r\n","{");
+ fprintf(fph,"%s\r\n"," if (iTab!=g_iShCur) {");
+ fprintf(fph,"%s\r\n"," var iCol=fnTabToCol(iTab);");
+ fprintf(fph,"%s\r\n"," with (frames['frTabs'].document.all) {");
+ fprintf(fph,"%s\r\n"," tdTab[iTab].style.background=c_rgszClr[5];");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n","}");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","function fnMouseOutTab(iTab)");
+ fprintf(fph,"%s\r\n","{");
+ fprintf(fph,"%s\r\n"," if (iTab>=0) {");
+ fprintf(fph,"%s\r\n"," var elFrom=frames['frTabs'].event.srcElement;");
+ fprintf(fph,"%s\r\n"," var elTo=frames['frTabs'].event.toElement;");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n"," if ((!elTo) ||");
+ fprintf(fph,"%s\r\n"," (elFrom.tagName==elTo.tagName) ||");
+ fprintf(fph,"%s\r\n"," (elTo.tagName==\"A\" && elTo.parentElement!=elFrom) ||");
+ fprintf(fph,"%s\r\n"," (elFrom.tagName==\"A\" && elFrom.parentElement!=elTo)) {");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n"," if (iTab!=g_iShCur) {");
+ fprintf(fph,"%s\r\n"," with (frames['frTabs'].document.all) {");
+ fprintf(fph,"%s\r\n"," tdTab[iTab].style.background=c_rgszClr[1];");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n","}");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","function fnSetActiveSheet(iSh)");
+ fprintf(fph,"%s\r\n","{");
+ fprintf(fph,"%s\r\n"," if (iSh!=g_iShCur) {");
+ fprintf(fph,"%s\r\n"," fnSetTabProps(g_iShCur,false);");
+ fprintf(fph,"%s\r\n"," fnSetTabProps(iSh,true);");
+ fprintf(fph,"%s\r\n"," g_iShCur=iSh;");
+ fprintf(fph,"%s\r\n"," }");
+ fprintf(fph,"%s\r\n","}");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n"," window.g_iIEVer=fnGetIEVer();");
+ fprintf(fph,"%s\r\n"," if (window.g_iIEVer>=4)");
+ fprintf(fph,"%s\r\n"," fnBuildFrameset();");
+ fprintf(fph,"%s\r\n","//-->");
+ fprintf(fph,"%s\r\n","</script>");
+ fprintf(fph,"%s\r\n","<![endif]><!--[if gte mso 9]><xml>");
+ fprintf(fph,"%s\r\n"," <x:ExcelWorkbook>");
+ fprintf(fph,"%s\r\n"," <x:ExcelWorksheets>");
+
+ {
+ unsigned int index = 0;
+ HtmlTableVector::iterator i;
+ for (i=mTables.begin(); i!=mTables.end(); ++i)
+ {
+ fprintf(fph,"%s\r\n"," <x:ExcelWorksheet>");
+ fprintf(fph," <x:Name>Sheet%d</x:Name>\r\n", index+1 );
+ fprintf(fph," <x:WorksheetSource HRef=\"%s_files/sheet%03d.htm\"/>\r\n", base_name.c_str(), index+1 );
+ fprintf(fph,"%s\r\n"," </x:ExcelWorksheet>");
+ index++;
+ }
+ }
+
+
+ fprintf(fph,"%s\r\n"," </x:ExcelWorksheets>");
+ fprintf(fph," <x:Stylesheet HRef=\"%s_files/stylesheet.css\"/>\r\n", base_name.c_str());
+ fprintf(fph,"%s\r\n"," <x:WindowHeight>15585</x:WindowHeight>");
+ fprintf(fph,"%s\r\n"," <x:WindowWidth>24795</x:WindowWidth>");
+ fprintf(fph,"%s\r\n"," <x:WindowTopX>480</x:WindowTopX>");
+ fprintf(fph,"%s\r\n"," <x:WindowTopY>105</x:WindowTopY>");
+ fprintf(fph,"%s\r\n"," <x:ActiveSheet>1</x:ActiveSheet>");
+ fprintf(fph,"%s\r\n"," <x:ProtectStructure>False</x:ProtectStructure>");
+ fprintf(fph,"%s\r\n"," <x:ProtectWindows>False</x:ProtectWindows>");
+ fprintf(fph,"%s\r\n"," </x:ExcelWorkbook>");
+ fprintf(fph,"%s\r\n","</xml><![endif]-->");
+ fprintf(fph,"%s\r\n","</head>");
+ fprintf(fph,"%s\r\n","");
+ fprintf(fph,"%s\r\n","<frameset rows=\"*,39\" border=0 width=0 frameborder=no framespacing=0>");
+ fprintf(fph," <frame src=\"%s_files/sheet001.htm\" name=\"frSheet\">\r\n", base_name.c_str());
+ fprintf(fph," <frame src=\"%s_files/tabstrip.htm\" name=\"frTabs\" marginwidth=0 marginheight=0>\r\n", base_name.c_str());
+ fprintf(fph,"%s\r\n"," <noframes>");
+ fprintf(fph,"%s\r\n"," <body>");
+ fprintf(fph,"%s\r\n"," <p>This page uses frames, but your browser doesn't support them.</p>");
+ fprintf(fph,"%s\r\n"," </body>");
+ fprintf(fph,"%s\r\n"," </noframes>");
+ fprintf(fph,"%s\r\n","</frameset>");
+ fprintf(fph,"%s\r\n","</html>");
+ fclose(fph);
+ }
+
+ sprintf(scratch,"%s%s_files\\filelist.xml", root_dir.c_str(), base_name.c_str() );
+ fph = fopen(scratch,"wb");
+ if ( fph )
+ {
+
+ fprintf(fph,"<xml xmlns:o=\"urn:schemas-microsoft-com:office:office\">\r\n");
+ fprintf(fph," <o:MainFile HRef=\"../%s\"/>\r\n", dest_name.c_str());
+ fprintf(fph," <o:File HRef=\"stylesheet.css\"/>\r\n");
+ fprintf(fph," <o:File HRef=\"tabstrip.htm\"/>\r\n");
+
+ for (unsigned int i=0; i<tableCount; i++)
+ {
+ fprintf(fph," <o:File HRef=\"sheet%03d.htm\"/>\r\n", i+1);
+ }
+
+ fprintf(fph," <o:File HRef=\"filelist.xml\"/>\r\n");
+ fprintf(fph,"</xml>\r\n");
+ fclose(fph);
+ }
+
+
+ sprintf(scratch,"%s%s_files\\stylesheet.css", root_dir.c_str(), base_name.c_str() );
+ fph = fopen(scratch,"wb");
+ if ( fph )
+ {
+ fprintf(fph,"%s\r\n", "tr");
+ fprintf(fph,"%s\r\n", " {mso-height-source:auto;}");
+ fprintf(fph,"%s\r\n", "col");
+ fprintf(fph,"%s\r\n", " {mso-width-source:auto;}");
+ fprintf(fph,"%s\r\n", "br");
+ fprintf(fph,"%s\r\n", " {mso-data-placement:same-cell;}");
+ fprintf(fph,"%s\r\n", ".style0");
+ fprintf(fph,"%s\r\n", " {mso-number-format:General;");
+ fprintf(fph,"%s\r\n", " text-align:general;");
+ fprintf(fph,"%s\r\n", " vertical-align:bottom;");
+ fprintf(fph,"%s\r\n", " white-space:nowrap;");
+ fprintf(fph,"%s\r\n", " mso-rotate:0;");
+ fprintf(fph,"%s\r\n", " mso-background-source:auto;");
+ fprintf(fph,"%s\r\n", " mso-pattern:auto;");
+ fprintf(fph,"%s\r\n", " color:windowtext;");
+ fprintf(fph,"%s\r\n", " font-size:10.0pt;");
+ fprintf(fph,"%s\r\n", " font-weight:400;");
+ fprintf(fph,"%s\r\n", " font-style:normal;");
+ fprintf(fph,"%s\r\n", " text-decoration:none;");
+ fprintf(fph,"%s\r\n", " font-family:Arial;");
+ fprintf(fph,"%s\r\n", " mso-generic-font-family:auto;");
+ fprintf(fph,"%s\r\n", " mso-font-charset:0;");
+ fprintf(fph,"%s\r\n", " border:none;");
+ fprintf(fph,"%s\r\n", " mso-protection:locked visible;");
+ fprintf(fph,"%s\r\n", " mso-style-name:Normal;");
+ fprintf(fph,"%s\r\n", " mso-style-id:0;}");
+ fprintf(fph,"%s\r\n", "td");
+ fprintf(fph,"%s\r\n", " {mso-style-parent:style0;");
+ fprintf(fph,"%s\r\n", " padding-top:1px;");
+ fprintf(fph,"%s\r\n", " padding-right:1px;");
+ fprintf(fph,"%s\r\n", " padding-left:1px;");
+ fprintf(fph,"%s\r\n", " mso-ignore:padding;");
+ fprintf(fph,"%s\r\n", " color:windowtext;");
+ fprintf(fph,"%s\r\n", " font-size:10.0pt;");
+ fprintf(fph,"%s\r\n", " font-weight:400;");
+ fprintf(fph,"%s\r\n", " font-style:normal;");
+ fprintf(fph,"%s\r\n", " text-decoration:none;");
+ fprintf(fph,"%s\r\n", " font-family:Arial;");
+ fprintf(fph,"%s\r\n", " mso-generic-font-family:auto;");
+ fprintf(fph,"%s\r\n", " mso-font-charset:0;");
+ fprintf(fph,"%s\r\n", " mso-number-format:General;");
+ fprintf(fph,"%s\r\n", " text-align:general;");
+ fprintf(fph,"%s\r\n", " vertical-align:bottom;");
+ fprintf(fph,"%s\r\n", " border:none;");
+ fprintf(fph,"%s\r\n", " mso-background-source:auto;");
+ fprintf(fph,"%s\r\n", " mso-pattern:auto;");
+ fprintf(fph,"%s\r\n", " mso-protection:locked visible;");
+ fprintf(fph,"%s\r\n", " white-space:nowrap;");
+ fprintf(fph,"%s\r\n", " mso-rotate:0;}");
+ fclose(fph);
+ }
+
+ sprintf(scratch,"%s%s_files\\tabstrip.htm", root_dir.c_str(), base_name.c_str() );
+ fph = fopen(scratch,"wb");
+ if ( fph )
+ {
+ fprintf(fph,"<html>\r\n");
+ fprintf(fph,"<head>\r\n");
+ fprintf(fph,"<meta http-equiv=Content-Type content=\"text/html; charset=windows-1252\">\r\n");
+ fprintf(fph,"<meta name=ProgId content=Excel.Sheet>\r\n");
+ fprintf(fph,"<meta name=Generator content=\"Microsoft Excel 11\">\r\n");
+ fprintf(fph,"<link id=Main-File rel=Main-File href=\"../%s\">\r\n", dest_name.c_str());
+ fprintf(fph,"\r\n");
+ fprintf(fph,"<script language=\"JavaScript\">\r\n");
+ fprintf(fph,"<!--\r\n");
+ fprintf(fph,"if (window.name!=\"frTabs\")\r\n");
+ fprintf(fph," window.location.replace(document.all.item(\"Main-File\").href);\r\n");
+ fprintf(fph,"//-->\r\n");
+ fprintf(fph,"</script>\r\n");
+ fprintf(fph,"<style>\r\n");
+ fprintf(fph,"<!--\r\n");
+ fprintf(fph,"A {\r\n");
+ fprintf(fph," text-decoration:none;\r\n");
+ fprintf(fph," color:#000000;\r\n");
+ fprintf(fph," font-size:9pt;\r\n");
+ fprintf(fph,"}\r\n");
+ fprintf(fph,"-->\r\n");
+ fprintf(fph,"</style>\r\n");
+ fprintf(fph,"</head>\r\n");
+ fprintf(fph,"<body topmargin=0 leftmargin=0 bgcolor=\"#808080\">\r\n");
+ fprintf(fph,"<table border=0 cellspacing=1>\r\n");
+ fprintf(fph," <tr>\r\n");
+ for (unsigned int i=0; i<tableCount; i++)
+ {
+ fprintf(fph," <td bgcolor=\"#FFFFFF\" nowrap><b><small><small>&nbsp;<a href=\"sheet%03d.htm\" target=\"frSheet\"><font face=\"Arial\" color=\"#000000\">Sheet%d</font></a>&nbsp;</small></small></b></td>\r\n", i+1, i+1);
+ }
+ fprintf(fph,"\r\n");
+ fprintf(fph," </tr>\r\n");
+ fprintf(fph,"</table>\r\n");
+ fprintf(fph,"</body>\r\n");
+ fprintf(fph,"</html>\r\n");
+ fclose(fph);
+ }
+
+
+ {
+ unsigned int index = 0;
+ HtmlTableVector::iterator i;
+ for (i=mTables.begin(); i!=mTables.end(); ++i)
+ {
+ _HtmlTable *table = (*i);
+ sprintf(scratch,"%s%s_files\\sheet%03d.htm", root_dir.c_str(), base_name.c_str(), index+1 );
+ fph = fopen(scratch,"wb");
+ if ( fph )
+ {
+
+ fprintf(fph,"<html xmlns:o=\"urn:schemas-microsoft-com:office:office\"\r\n");
+ fprintf(fph,"xmlns:x=\"urn:schemas-microsoft-com:office:excel\"\r\n");
+ fprintf(fph,"xmlns=\"http://www.w3.org/TR/REC-html40\">\r\n");
+ fprintf(fph,"\r\n");
+ fprintf(fph,"<head>\r\n");
+ fprintf(fph,"<meta http-equiv=Content-Type content=\"text/html; charset=windows-1252\">\r\n");
+ fprintf(fph,"<meta name=ProgId content=Excel.Sheet>\r\n");
+ fprintf(fph,"<meta name=Generator content=\"Microsoft Excel 11\">\r\n");
+ fprintf(fph,"<link id=Main-File rel=Main-File href=\"../%s\">\r\n", dest_name.c_str());
+ fprintf(fph,"<link rel=File-List href=filelist.xml>\r\n");
+ fprintf(fph,"<link rel=Edit-Time-Data href=editdata.mso>\r\n");
+ fprintf(fph,"<link rel=Stylesheet href=stylesheet.css>\r\n");
+ fprintf(fph,"<style>\r\n");
+ fprintf(fph,"<!--table\r\n");
+ fprintf(fph," {mso-displayed-decimal-separator:\"\\.\";\r\n");
+ fprintf(fph," mso-displayed-thousand-separator:\"\\,\";}\r\n");
+ fprintf(fph,"@page\r\n");
+ fprintf(fph," {margin:1.0in .75in 1.0in .75in;\r\n");
+ fprintf(fph," mso-header-margin:.5in;\r\n");
+ fprintf(fph," mso-footer-margin:.5in;}\r\n");
+ fprintf(fph,"-->\r\n");
+ fprintf(fph,"</style>\r\n");
+ fprintf(fph,"<![if !supportTabStrip]><script language=\"JavaScript\">\r\n");
+ fprintf(fph,"<!--\r\n");
+ fprintf(fph,"function fnUpdateTabs()\r\n");
+ fprintf(fph," {\r\n");
+ fprintf(fph," if (parent.window.g_iIEVer>=4) {\r\n");
+ fprintf(fph," if (parent.document.readyState==\"complete\"\r\n");
+ fprintf(fph," && parent.frames['frTabs'].document.readyState==\"complete\")\r\n");
+ fprintf(fph," parent.fnSetActiveSheet(0);\r\n");
+ fprintf(fph," else\r\n");
+ fprintf(fph," window.setTimeout(\"fnUpdateTabs();\",150);\r\n");
+ fprintf(fph," }\r\n");
+ fprintf(fph,"}\r\n");
+ fprintf(fph,"\r\n");
+ fprintf(fph,"if (window.name!=\"frSheet\")\r\n");
+ fprintf(fph," window.location.replace(\"../%s\");\r\n", dest_name.c_str());
+ fprintf(fph,"else\r\n");
+ fprintf(fph," fnUpdateTabs();\r\n");
+ fprintf(fph,"//-->\r\n");
+ fprintf(fph,"</script>\r\n");
+ fprintf(fph,"<![endif]><!--[if gte mso 9]><xml>\r\n");
+ fprintf(fph," <x:WorksheetOptions>\r\n");
+ fprintf(fph," <x:Panes>\r\n");
+ fprintf(fph," <x:Pane>\r\n");
+ fprintf(fph," <x:Number>3</x:Number>\r\n");
+ fprintf(fph," <x:ActiveRow>3</x:ActiveRow>\r\n");
+ fprintf(fph," <x:ActiveCol>1</x:ActiveCol>\r\n");
+ fprintf(fph," </x:Pane>\r\n");
+ fprintf(fph," </x:Panes>\r\n");
+ fprintf(fph," <x:ProtectContents>False</x:ProtectContents>\r\n");
+ fprintf(fph," <x:ProtectObjects>False</x:ProtectObjects>\r\n");
+ fprintf(fph," <x:ProtectScenarios>False</x:ProtectScenarios>\r\n");
+ fprintf(fph," </x:WorksheetOptions>\r\n");
+ fprintf(fph,"</xml><![endif]-->\r\n");
+ fprintf(fph,"</head>\r\n");
+ fprintf(fph,"\r\n");
+ fprintf(fph,"<body link=blue vlink=purple>\r\n");
+ fprintf(fph,"\r\n");
+ table->saveExcel(fph);
+/******
+ fprintf(fph,"<table x:str border=0 cellpadding=0 cellspacing=0 width=128 style='border-collapse:\r\n");
+ fprintf(fph," collapse;table-layout:fixed;width:96pt'>\r\n");
+ fprintf(fph," <col width=64 span=2 style='width:48pt'>\r\n");
+ fprintf(fph," <tr height=17 style='height:12.75pt'>\r\n");
+ fprintf(fph," <td height=17 width=64 style='height:12.75pt;width:48pt'>a</td>\r\n");
+ fprintf(fph," <td width=64 style='width:48pt'>b</td>\r\n");
+ fprintf(fph," </tr>\r\n");
+ fprintf(fph," <tr height=17 style='height:12.75pt'>\r\n");
+ fprintf(fph," <td height=17 align=right style='height:12.75pt' x:num>1</td>\r\n");
+ fprintf(fph," <td align=right x:num>2</td>\r\n");
+ fprintf(fph," </tr>\r\n");
+ fprintf(fph," <tr height=17 style='height:12.75pt'>\r\n");
+ fprintf(fph," <td height=17 align=right style='height:12.75pt' x:num>3</td>\r\n");
+ fprintf(fph," <td align=right x:num>4</td>\r\n");
+ fprintf(fph," </tr>\r\n");
+ fprintf(fph," <![if supportMisalignedColumns]>\r\n");
+ fprintf(fph," <tr height=0 style='display:none'>\r\n");
+ fprintf(fph," <td width=64 style='width:48pt'></td>\r\n");
+ fprintf(fph," <td width=64 style='width:48pt'></td>\r\n");
+ fprintf(fph," </tr>\r\n");
+ fprintf(fph," <![endif]>\r\n");
+ fprintf(fph,"</table>\r\n");
+***/
+ fprintf(fph,"\r\n");
+ fprintf(fph,"</body>\r\n");
+ fprintf(fph,"\r\n");
+ fprintf(fph,"</html>\r\n");
+ fclose(fph);
+ }
+ index++;
+ }
+ }
+
+
+
+#endif
+ }
+
+
+private:
+ unsigned int mDisplayOrder;
+ std::string mDocumentName;
+ HtmlTableVector mTables;
+ HtmlTableInterface *mInterface;
+};
+
+ _HtmlTable::_HtmlTable(const char *heading,HtmlDocument *parent)
+ {
+// gTableCount++;
+// printf("Constructed _HtmlTable(%08X) Count:%d\r\n", this,gTableCount );
+
+ _HtmlDocument *hd = static_cast< _HtmlDocument *>(parent);
+ mDisplayOrder = hd->getDisplayOrder();
+
+ mHeaderColor = 0x00FFFF;
+ mFooterColor = 0xCCFFFF;
+ mBodyColor = 0xCCFFCC;
+
+ mParent = parent;
+ if ( heading )
+ {
+ mHeading = heading;
+ }
+ mComputeTotals = false;
+
+ mCurrent = 0;
+ mParser.ClearHardSeparator(32);
+ mParser.ClearHardSeparator(9);
+ mParser.SetHard(',');
+ }
+
+
+class MyHtmlTableInterface : public HtmlTableInterface
+{
+public:
+
+ HtmlDocument * createHtmlDocument(const char *document_name)
+ {
+ HtmlDocument *ret = 0;
+
+ ret = HTML_NEW(_HtmlDocument)(document_name,this);
+
+ return ret;
+ }
+
+ void releaseHtmlDocument(HtmlDocument *document) // release a previously created HTML document
+ {
+ assert(document);
+ _HtmlDocument *doc = static_cast< _HtmlDocument *>(document);
+ HTML_DELETE(_HtmlDocument,doc);
+ }
+
+
+}; //
+
+}; // end of namespace
+
+namespace nvidia
+{
+using namespace HTMLTABLE_NVSHARE;
+
+static MyHtmlTableInterface *gInterface=NULL;
+
+HtmlTableInterface *getHtmlTableInterface(void)
+{
+ if ( gInterface == NULL )
+ {
+ gInterface = new MyHtmlTableInterface;
+ }
+ return gInterface;
+}
+
+int getHtmlMemoryUsage(void)
+{
+ return gMemTracker.getMemoryUsage();
+}
+
+};
+
+#endif \ No newline at end of file