diff options
| author | git perforce import user <a@b> | 2016-10-25 12:29:14 -0600 |
|---|---|---|
| committer | Sheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees> | 2016-10-25 18:56:37 -0500 |
| commit | 3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch) | |
| tree | fa6485c169e50d7415a651bf838f5bcd0fd3bfbd /APEX_1.4/shared/external/src | |
| download | physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip | |
Initial commit:
PhysX 3.4.0 Update @ 21294896
APEX 1.4.0 Update @ 21275617
[CL 21300167]
Diffstat (limited to 'APEX_1.4/shared/external/src')
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( <ime ); + today = localtime( <ime ); + 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 = ¤tPose; + 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\\\"> <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>«</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><</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>></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>»</a></td>\"+"); + fprintf(fph,"%s\r\n"," \"<td style=\\\"font:1pt\\\"> <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\\\"> </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> \"+c_rgszSh[i]+\" </a></td>\";"); + fprintf(fph,"%s\r\n"," }"); + fprintf(fph,"%s\r\n"," szHTML+=\"<td id=tdTab height=1 nowrap class=\\\"clBorder\\\"><a id=aTab> </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> <a href=\"sheet%03d.htm\" target=\"frSheet\"><font face=\"Arial\" color=\"#000000\">Sheet%d</font></a> </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 |