aboutsummaryrefslogtreecommitdiff
path: root/APEX_1.4/shared/external/src/TriangleMesh.cpp
diff options
context:
space:
mode:
authorgit perforce import user <a@b>2016-10-25 12:29:14 -0600
committerSheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees>2016-10-25 18:56:37 -0500
commit3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch)
treefa6485c169e50d7415a651bf838f5bcd0fd3bfbd /APEX_1.4/shared/external/src/TriangleMesh.cpp
downloadphysx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz
physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip
Initial commit:
PhysX 3.4.0 Update @ 21294896 APEX 1.4.0 Update @ 21275617 [CL 21300167]
Diffstat (limited to 'APEX_1.4/shared/external/src/TriangleMesh.cpp')
-rw-r--r--APEX_1.4/shared/external/src/TriangleMesh.cpp5048
1 files changed, 5048 insertions, 0 deletions
diff --git a/APEX_1.4/shared/external/src/TriangleMesh.cpp b/APEX_1.4/shared/external/src/TriangleMesh.cpp
new file mode 100644
index 00000000..de45c439
--- /dev/null
+++ b/APEX_1.4/shared/external/src/TriangleMesh.cpp
@@ -0,0 +1,5048 @@
+/*
+ * Copyright (c) 2008-2015, NVIDIA CORPORATION. All rights reserved.
+ *
+ * NVIDIA CORPORATION and its licensors retain all intellectual property
+ * and proprietary rights in and to this software, related documentation
+ * and any modifications thereto. Any use, reproduction, disclosure or
+ * distribution of this software and related documentation without an express
+ * license agreement from NVIDIA CORPORATION is strictly prohibited.
+ */
+
+#include <MeshImport.h>
+
+#include "PsMathUtils.h"
+#include "TriangleMesh.h"
+#include "SkeletalAnim.h"
+#include "RenderMeshAsset.h"
+#include "RenderDataFormat.h"
+#include "PsFileBuffer.h"
+
+#include "PxInputDataFromPxFileBuf.h"
+
+#include "PxStrideIterator.h"
+
+#include "ApexUsingNamespace.h"
+#include "PsString.h"
+#include "PxIntrinsics.h"
+
+#include <algorithm>
+#include <vector>
+
+#include <RenderDebugInterface.h>
+#include <ResourceCallback.h>
+#include <ApexNameSpace.h>
+#include <clothing/ClothingPhysicalMesh.h>
+#include <UserRenderer.h>
+#include <UserRenderBoneBuffer.h>
+#include <UserRenderBoneBufferDesc.h>
+#include <UserRenderVertexBuffer.h>
+#include <UserRenderVertexBufferDesc.h>
+#include <UserRenderIndexBuffer.h>
+#include <UserRenderIndexBufferDesc.h>
+#include <UserRenderResourceDesc.h>
+#include <RenderContext.h>
+
+#ifdef USE_SAMPLE_RENDERER
+#include <Renderer.h>
+#include <RendererIndexBufferDesc.h>
+#include <RendererMaterialDesc.h>
+#include <RendererMaterialInstance.h>
+#include <RendererMeshDesc.h>
+#include <RendererMeshContext.h>
+#include <RendererVertexBufferDesc.h>
+
+#include <SampleMaterialAsset.h>
+#include <SampleAssetManager.h>
+#endif
+
+const uint32_t OBJ_STR_LEN = 256;
+
+#if PX_WINDOWS_FAMILY
+#define NOMINMAX
+#include <windows.h>
+#endif
+
+#if PX_APPLE_FAMILY
+#include <stdio.h>
+#endif
+
+mimp::MeshImport* gMeshImport = NULL; // has to be declared somewhere in the code
+
+
+namespace Samples
+{
+
+//-----------------------------------------------------------------------------
+struct TriangleMeshEdge
+{
+ void init(int v0, int v1, int edgeNr, int triNr)
+ {
+ if (v0 < v1)
+ {
+ this->v0 = v0;
+ this->v1 = v1;
+ }
+ else
+ {
+ this->v0 = v1;
+ this->v1 = v0;
+ }
+ this->edgeNr = edgeNr;
+ this->triNr = triNr;
+ }
+ bool operator < (const TriangleMeshEdge& e) const
+ {
+ if (v0 < e.v0)
+ {
+ return true;
+ }
+ if (v0 > e.v0)
+ {
+ return false;
+ }
+ return v1 < e.v1;
+ }
+ bool operator == (const TriangleMeshEdge& e) const
+ {
+ if (v0 == e.v0 && v1 == e.v1)
+ {
+ return true;
+ }
+ if (v0 == e.v1 && v1 == e.v0)
+ {
+ return true;
+ }
+ return false;
+ }
+ int v0, v1;
+ int edgeNr;
+ int triNr;
+};
+
+// ------------------------------------------------------------------------------------
+struct TexCoord
+{
+ TexCoord() {}
+ TexCoord(float u, float v)
+ {
+ this->u = u;
+ this->v = v;
+ }
+ void zero()
+ {
+ u = 0.0f;
+ v = 0.0f;
+ }
+ TexCoord operator + (const TexCoord& tc) const
+ {
+ TexCoord r;
+ r.u = u + tc.u;
+ r.v = v + tc.v;
+ return tc;
+ }
+ void operator += (const TexCoord& tc)
+ {
+ u += tc.u;
+ v += tc.v;
+ }
+ void operator *= (float r)
+ {
+ u *= r;
+ v *= r;
+ }
+ void operator /= (float r)
+ {
+ u /= r;
+ v /= r;
+ }
+ float u, v;
+};
+
+// -------------------------------------------------------------------
+struct SimpleVertexRef
+{
+ int vert, normal, texCoord;
+ int indexNr;
+ float handedNess;
+
+ bool operator < (const SimpleVertexRef& r) const
+ {
+ if (vert < r.vert)
+ {
+ return true;
+ }
+ if (vert > r.vert)
+ {
+ return false;
+ }
+ if (normal < r.normal)
+ {
+ return true;
+ }
+ if (normal > r.normal)
+ {
+ return false;
+ }
+ if (texCoord < r.texCoord)
+ {
+ return true;
+ }
+ else if (texCoord > r.texCoord)
+ {
+ return false;
+ }
+ return handedNess > r.handedNess;
+ }
+ bool operator == (const SimpleVertexRef& r) const
+ {
+ return vert == r.vert && normal == r.normal && texCoord == r.texCoord && handedNess == r.handedNess;
+ }
+ void parse(char* s, int indexNr)
+ {
+ int nr[3] = {0, 0, 0};
+ char* p = s;
+ int i = 0;
+ while (*p != 0 && i < 3 && sscanf(p, "%d", &nr[i]) == 1)
+ {
+ while (*p != '/' && *p != 0)
+ {
+ p++;
+ }
+
+ if (*p == 0)
+ {
+ break;
+ }
+
+ p++;
+ i++;
+ if (*p == '/')
+ {
+ p++;
+ i++;
+ }
+ }
+ PX_ASSERT(nr[0] > 0);
+ vert = nr[0] - 1;
+ texCoord = nr[1] - 1;
+ normal = nr[2] - 1;
+ this->indexNr = indexNr;
+ }
+};
+
+// ----------------------------------------------------------------------
+void TriangleSubMesh::setMaterialReference(SampleRenderer::RendererMaterial* material, SampleRenderer::RendererMaterialInstance* materialInstance)
+{
+ PX_UNUSED(material);
+ PX_UNUSED(materialInstance);
+
+#ifdef USE_SAMPLE_RENDERER
+ if (mRendererMaterialReference != material && mRendererMaterialInstance != NULL)
+ {
+ delete mRendererMaterialInstance;
+ mRendererMaterialInstance = NULL;
+ }
+ mRendererMaterialReference = material;
+
+ if (mRendererMaterialInstance == NULL && materialInstance != NULL && mRendererMaterialReference != NULL)
+ {
+ mRendererMaterialInstance = new SampleRenderer::RendererMaterialInstance(*mRendererMaterialReference);
+ }
+
+ if (mRendererMaterialInstance != NULL && materialInstance != NULL)
+ {
+ // copy the values of all variables
+ *mRendererMaterialInstance = *materialInstance;
+ }
+#endif // USE_SAMPLE_RENDERER
+}
+
+// ----------------------------------------------------------------------
+TriangleMesh::TriangleMesh(uint32_t /*moduleIdentifier*/, SampleRenderer::Renderer* renderer /*= NULL*/) :
+ mDynamicVertexBuffer(NULL),
+ mStaticVertexBuffer(NULL),
+ mIndexBuffer(NULL),
+ mBoneBuffer(NULL),
+ mTextureUVOrigin(nvidia::TextureUVOrigin::ORIGIN_TOP_LEFT),
+ mRenderer(renderer),
+ mRendererVertexBufferDynamic(NULL),
+ mRendererVertexBufferShared(NULL),
+ mRendererIndexBuffer(NULL),
+ mOverrideMaterial(NULL),
+ mRendererTransform(physx::PxIdentity),
+ mUseGpuSkinning(false)
+{
+ clear(NULL, NULL);
+}
+
+// ----------------------------------------------------------------------
+TriangleMesh::~TriangleMesh()
+{
+ clear(NULL, NULL);
+}
+
+
+// ----------------------------------------------------------------------
+void TriangleMesh::setRenderer(SampleRenderer::Renderer* renderer)
+{
+ PX_ASSERT(mRenderer == NULL || mRenderer == renderer);
+ mRenderer = renderer;
+}
+
+
+// ----------------------------------------------------------------------
+void TriangleMesh::clear(nvidia::apex::UserRenderResourceManager* rrm, nvidia::apex::ResourceCallback* rcb)
+{
+ mMaxBoneIndexInternal = -1;
+ mMaxBoneIndexExternal = -1;
+
+ mName = "";
+ mSkeletonFile = "";
+
+ mBounds.setEmpty();
+
+ oneCullModeChanged = false;
+ textureUvOriginChanged = false;
+
+ mVertices.clear();
+ mNormals.clear();
+ mTangents.clear();
+ mBitangents.clear();
+ mSkinnedVertices.clear();
+ mSkinnedNormals.clear();
+
+ mSkinningMatrices.clear();
+ skinningMatricesChanged = true;
+ vertexValuesChangedDynamic = false;
+ vertexValuesChangedStatic = false;
+ vertexCountChanged = false;
+ indicesChanged = false;
+
+ for (int i = 0; i < PC_NUM_CHANNELS; i++)
+ {
+ mPaintChannels[i].clear();
+ }
+
+ for (int i = 0; i < NUM_TEXCOORDS; i++)
+ {
+ mTexCoords[i].clear();
+ }
+
+ mIndices.clear();
+
+ mBoneIndicesExternal.clear();
+ mBoneWeights.clear();
+
+ mNextMark = -1;
+ mTriangleMarks.clear();
+ mVertexMarks.clear();
+ mActiveSubmeshVertices.clear();
+
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ if (mSubMeshes[i].mRenderResource != NULL)
+ {
+ PX_ASSERT(rrm != NULL);
+ rrm->releaseResource(*mSubMeshes[i].mRenderResource);
+ mSubMeshes[i].mRenderResource = NULL;
+ }
+
+ if (mParent != NULL)
+ {
+ mSubMeshes[i].materialResource = NULL;
+ }
+
+ if (mSubMeshes[i].materialResource != NULL)
+ {
+#ifndef USE_SAMPLE_RENDERER
+ PX_ASSERT(rcb != NULL);
+ std::string fullMaterialName = mMaterialPrefix + mSubMeshes[i].materialName + mMaterialSuffix;
+ rcb->releaseResource(APEX_MATERIALS_NAME_SPACE, fullMaterialName.c_str(), mSubMeshes[i].materialResource);
+#endif
+ mSubMeshes[i].materialResource = NULL;
+ }
+
+#ifdef USE_SAMPLE_RENDERER
+ if (mSubMeshes[i].mRendererMeshContext != NULL)
+ {
+ delete mSubMeshes[i].mRendererMeshContext;
+ mSubMeshes[i].mRendererMeshContext = NULL;
+ }
+
+ if (mSubMeshes[i].mRendererMaterialInstance != NULL)
+ {
+ delete mSubMeshes[i].mRendererMaterialInstance;
+ mSubMeshes[i].mRendererMaterialInstance = NULL;
+ }
+
+ if (mSubMeshes[i].mSampleMaterial != NULL && mParent == NULL)
+ {
+ PX_ASSERT(rcb != NULL);
+ rcb->releaseResource(APEX_MATERIALS_NAME_SPACE, NULL, mSubMeshes[i].mSampleMaterial);
+
+ mSubMeshes[i].mRendererMaterialReference = NULL;
+ }
+
+ if (mSubMeshes[i].mRendererMesh != NULL && mParent == NULL)
+ {
+ mSubMeshes[i].mRendererMesh->release();
+ mSubMeshes[i].mRendererMesh = NULL;
+ }
+#endif // USE_SAMPLE_RENDERER
+ }
+ mSubMeshes.clear();
+
+ if (mDynamicVertexBuffer != NULL)
+ {
+ PX_ASSERT(rrm != NULL);
+ rrm->releaseVertexBuffer(*mDynamicVertexBuffer);
+ mDynamicVertexBuffer = NULL;
+ }
+
+ if (mStaticVertexBuffer != NULL)
+ {
+ PX_ASSERT(rrm != NULL);
+ rrm->releaseVertexBuffer(*mStaticVertexBuffer);
+ mStaticVertexBuffer = NULL;
+ }
+
+ if (mIndexBuffer != NULL)
+ {
+ PX_ASSERT(rrm != NULL);
+ rrm->releaseIndexBuffer(*mIndexBuffer);
+ mIndexBuffer = NULL;
+ }
+
+ if (mBoneBuffer != NULL)
+ {
+ PX_ASSERT(rrm != NULL);
+ rrm->releaseBoneBuffer(*mBoneBuffer);
+ mBoneBuffer = NULL;
+ }
+
+
+#ifdef USE_SAMPLE_RENDERER
+ if (mRendererIndexBuffer != NULL)
+ {
+ mRendererIndexBuffer->release();
+ mRendererIndexBuffer = NULL;
+ }
+
+ if (mRendererVertexBufferDynamic != NULL)
+ {
+ mRendererVertexBufferDynamic->release();
+ mRendererVertexBufferDynamic = NULL;
+ }
+
+ if (mRendererVertexBufferShared != NULL)
+ {
+ mRendererVertexBufferShared->release();
+ mRendererVertexBufferShared = NULL;
+ }
+
+ if (mOverrideMaterial != NULL && rcb != NULL)
+ {
+ rcb->releaseResource(APEX_MATERIALS_NAME_SPACE, NULL, mOverrideMaterial);
+ mOverrideMaterial = NULL;
+ }
+#endif // USE_SAMPLE_RENDERER
+
+ mParent = NULL;
+ mUseGpuSkinning = false;
+ mRendererTransform = physx::PxMat44(physx::PxIdentity);
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::loadMaterials(nvidia::apex::ResourceCallback* resourceCallback, nvidia::apex::UserRenderResourceManager* rrm,
+ bool dummyMaterial, const char* materialPrefix, const char* materialSuffix, bool onlyVisibleMaterials)
+{
+ std::string path;
+ PX_ASSERT(mParent == NULL);
+
+ if (dummyMaterial)
+ {
+ static int dummyCount = 0;
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ char buf[64];
+ physx::shdfnd::snprintf(buf, 64, "DummMaterial%d", dummyCount++);
+ mSubMeshes[i].materialName = buf;
+ }
+ }
+
+ unsigned int maxBonesShader = 0;
+
+ for (uint32_t submeshIndex = 0; submeshIndex < mSubMeshes.size(); submeshIndex++)
+ {
+ TriangleSubMesh& sm = mSubMeshes[submeshIndex];
+
+ if (sm.materialResource != NULL)
+ {
+ continue;
+ }
+
+ if (sm.materialName.find("invisible") != std::string::npos)
+ {
+ sm.show = false;
+ }
+
+ if (onlyVisibleMaterials && !sm.show)
+ {
+ continue;
+ }
+
+ if (sm.materialName.empty())
+ {
+ sm.materialName = sm.name;
+ }
+
+ std::string materialName;
+ if (materialPrefix != NULL)
+ {
+ mMaterialPrefix = materialPrefix;
+ materialName.append(materialPrefix);
+ }
+ materialName.append(sm.materialName);
+ if (materialSuffix != NULL)
+ {
+ mMaterialSuffix = materialSuffix;
+ materialName.append(materialSuffix);
+ }
+
+#ifdef USE_SAMPLE_RENDERER
+ PX_ASSERT(sm.mSampleMaterial == NULL);
+ sm.mSampleMaterial = reinterpret_cast<SampleFramework::SampleMaterialAsset*>(
+ resourceCallback->requestResource(APEX_MATERIALS_NAME_SPACE, materialName.c_str()));
+#else
+ sm.materialResource = (MaterialResource*)resourceCallback->requestResource(APEX_MATERIALS_NAME_SPACE, materialName.c_str());
+#endif
+
+ if (materialName.find("__cloth") != std::string::npos)
+ {
+ sm.usedForCollision = false;
+ }
+
+ if (sm.materialResource != NULL || sm.mSampleMaterial != NULL)
+ {
+#ifdef USE_SAMPLE_RENDERER
+ sm.maxBonesShader = 0;//rrm->getMaxBonesForMaterial(sm.mSampleMaterial);
+#else
+ sm.maxBonesShader = rrm->getMaxBonesForMaterial(sm.materialResource);
+#endif
+ if (maxBonesShader == 0 || sm.maxBonesShader < maxBonesShader)
+ {
+ maxBonesShader = sm.maxBonesShader;
+ }
+ }
+ }
+
+#ifdef USE_SAMPLE_RENDERER
+ if (mOverrideMaterial == NULL)
+ {
+ mOverrideMaterial = reinterpret_cast<SampleFramework::SampleMaterialAsset*>(
+ resourceCallback->requestResource(APEX_MATERIALS_NAME_SPACE, "materials/simple_lit_uniform_color.xml"));
+ }
+ PX_ASSERT(mOverrideMaterial != NULL);
+#endif
+
+ optimizeForRendering();
+
+ const unsigned int maxBoneIndexMesh = uint32_t(mParent != NULL ? mParent->mMaxBoneIndexInternal : mMaxBoneIndexInternal);
+
+ mUseGpuSkinning = maxBoneIndexMesh < maxBonesShader;
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::moveAboveGround(float level)
+{
+ float lowest = FLT_MAX;
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ lowest = std::min(lowest, mVertices[i].y);
+ }
+
+ const float change = std::max(0.0f, level - lowest);
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ mVertices[i].y += change;
+ }
+ mBounds.minimum.y += change;
+ mBounds.maximum.y += change;
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::initSingleMesh()
+{
+ clear(NULL, NULL);
+ mSubMeshes.resize(1);
+ mSubMeshes[0].init();
+ mSubMeshes[0].firstIndex = 0;
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::copyFrom(const TriangleMesh& mesh)
+{
+ clear(NULL, NULL);
+
+ mVertices.resize(mesh.mVertices.size());
+ for (size_t i = 0; i < mesh.mVertices.size(); i++)
+ {
+ mVertices[i] = mesh.mVertices[i];
+ }
+
+ mNormals.resize(mesh.mNormals.size());
+ for (size_t i = 0; i < mesh.mNormals.size(); i++)
+ {
+ mNormals[i] = mesh.mNormals[i];
+ }
+
+ mTangents.resize(mesh.mTangents.size());
+ for (size_t i = 0; i < mesh.mTangents.size(); i++)
+ {
+ mTangents[i] = mesh.mTangents[i];
+ }
+
+ mBitangents.resize(mesh.mBitangents.size());
+ for (size_t i = 0; i < mesh.mBitangents.size(); i++)
+ {
+ mBitangents[i] = mesh.mBitangents[i];
+ }
+
+ for (unsigned int t = 0; t < NUM_TEXCOORDS; t++)
+ {
+ mTexCoords[t].resize(mesh.mTexCoords[t].size());
+ for (size_t i = 0; i < mesh.mTexCoords[t].size(); i++)
+ {
+ mTexCoords[t][i] = mesh.mTexCoords[t][i];
+ }
+ }
+
+ for (unsigned int i = 0; i < PC_NUM_CHANNELS; i++)
+ {
+ mPaintChannels[i].resize(mesh.mPaintChannels[i].size());
+ for (size_t j = 0; j < mesh.mPaintChannels[i].size(); j++)
+ {
+ mPaintChannels[i][j] = mesh.mPaintChannels[i][j];
+ }
+ }
+
+ mSubMeshes.resize(mesh.mSubMeshes.size());
+ for (size_t i = 0; i < mesh.mSubMeshes.size(); i++)
+ {
+ mSubMeshes[i] = mesh.mSubMeshes[i];
+ mSubMeshes[i].materialResource = NULL;
+ // don't copy these
+ mSubMeshes[i].mRenderResource = NULL;
+ mSubMeshes[i].mRendererMesh = NULL;
+ mSubMeshes[i].mRendererMeshContext = NULL;
+ mSubMeshes[i].mSampleMaterial = NULL;
+ mSubMeshes[i].mRendererMaterialReference = NULL;
+ mSubMeshes[i].mRendererMaterialInstance = NULL;
+ }
+
+ mIndices.resize(mesh.mIndices.size());
+ for (size_t i = 0; i < mesh.mIndices.size(); i++)
+ {
+ mIndices[i] = mesh.mIndices[i];
+ }
+
+ // skeleton binding
+ mSkeletonFile = mesh.mSkeletonFile;
+
+ mBoneIndicesExternal.resize(mesh.mBoneIndicesExternal.size());
+ for (size_t i = 0; i < mesh.mBoneIndicesExternal.size(); i++)
+ {
+ mBoneIndicesExternal[i] = mesh.mBoneIndicesExternal[i];
+ }
+
+ mBoneWeights.resize(mesh.mBoneWeights.size());
+ for (size_t i = 0; i < mesh.mBoneWeights.size(); i++)
+ {
+ mBoneWeights[i] = mesh.mBoneWeights[i];
+ }
+
+ mName = mesh.mName;
+ mRendererTransform = mesh.mRendererTransform;
+ mMaxBoneIndexExternal = mesh.mMaxBoneIndexExternal;
+ updateBounds();
+ updateBoneWeights();
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::copyFromSubMesh(const TriangleMesh& mesh, int subMeshNr, bool filterVertices)
+{
+ if (!filterVertices && subMeshNr < 0)
+ {
+ copyFrom(mesh);
+ return;
+ }
+
+ clear(NULL, NULL);
+
+ if (subMeshNr >= (int)mesh.mSubMeshes.size())
+ {
+ return;
+ }
+
+ int firstIndex = 0;
+ int numIndices = (int)mesh.getNumIndices();
+ if (subMeshNr >= 0)
+ {
+ const TriangleSubMesh& sm = mesh.mSubMeshes[(uint32_t)subMeshNr];
+ firstIndex = (int32_t)sm.firstIndex;
+ numIndices = (int32_t)sm.numIndices;
+ }
+
+ size_t numVerts = mesh.mVertices.size();
+
+ std::vector<int> oldToNew;
+ oldToNew.resize(numVerts);
+ for (size_t i = 0; i < numVerts; i++)
+ {
+ oldToNew[i] = -1;
+ }
+
+ mSubMeshes.resize(1);
+ mSubMeshes[0].init();
+
+ const std::vector<PaintedVertex> &physChannel = mesh.getPaintChannel(PC_LATCH_TO_NEAREST_SLAVE);
+ const bool filter = filterVertices && physChannel.size() == mesh.getNumVertices();
+ mIndices.clear();
+ int nextIndex = 0;
+ for (int i = firstIndex; i < firstIndex + numIndices; i += 3)
+ {
+
+ bool skipTri = false;
+ if (filter)
+ {
+ for (int j = 0; j < 3; j++)
+ {
+ skipTri |= (physChannel[mesh.mIndices[uint32_t(i + j)]].paintValueU32 != 0);
+ }
+ }
+ if (skipTri)
+ {
+ continue;
+ }
+
+ for (int j = 0; j < 3; j++)
+ {
+ uint32_t idx = mesh.mIndices[uint32_t(i + j)];
+ if (oldToNew[idx] >= 0)
+ {
+ mIndices.push_back((uint32_t)oldToNew[idx]);
+ }
+ else
+ {
+ mIndices.push_back((uint32_t)nextIndex);
+ oldToNew[idx] = nextIndex;
+ nextIndex++;
+ }
+ }
+ }
+ int numNewVerts = nextIndex;
+
+ mVertices.resize((uint32_t)numNewVerts);
+ for (size_t i = 0; i < numVerts; i++)
+ {
+ if (oldToNew[i] >= 0)
+ {
+ mVertices[(uint32_t)oldToNew[i]] = mesh.mVertices[i];
+ }
+ }
+
+ if (mesh.mNormals.size() == numVerts)
+ {
+ mNormals.resize((uint32_t)numNewVerts);
+ for (size_t i = 0; i < numVerts; i++)
+ {
+ if (oldToNew[i] >= 0)
+ {
+ mNormals[(uint32_t)oldToNew[i]] = mesh.mNormals[i];
+ }
+ }
+ }
+
+ if (mesh.mTangents.size() == numVerts)
+ {
+ mTangents.resize((uint32_t)numNewVerts);
+ for (size_t i = 0; i < numVerts; i++)
+ {
+ if (oldToNew[i] >= 0)
+ {
+ mTangents[(uint32_t)oldToNew[i]] = mesh.mTangents[i];
+ }
+ }
+ }
+
+ if (mesh.mBitangents.size() == numVerts)
+ {
+ mBitangents.resize((uint32_t)numNewVerts);
+ for (size_t i = 0; i < numVerts; i++)
+ {
+ if (oldToNew[i] >= 0)
+ {
+ mBitangents[(uint32_t)oldToNew[i]] = mesh.mBitangents[i];
+ }
+ }
+ }
+
+ for (unsigned int t = 0; t < NUM_TEXCOORDS; t++)
+ {
+ // PH: I suppose we could delete these 3 lines?
+ mTexCoords[t].resize(mesh.mTexCoords[t].size());
+ for (uint32_t i = 0; i < mesh.mTexCoords[t].size(); i++)
+ {
+ mTexCoords[t][i] = mesh.mTexCoords[t][i];
+ }
+
+ if (mesh.mTexCoords[t].size() == numVerts)
+ {
+ mTexCoords[t].resize((uint32_t)numNewVerts);
+
+ for (size_t j = 0; j < numVerts; j++)
+ {
+ if (oldToNew[j] >= 0)
+ {
+ mTexCoords[t][(uint32_t)oldToNew[j]] = mesh.mTexCoords[t][j];
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < PC_NUM_CHANNELS; i++)
+ {
+ if (mesh.mPaintChannels[i].size() == numVerts)
+ {
+ mPaintChannels[i].resize((uint32_t)numNewVerts);
+ for (size_t j = 0; j < numVerts; j++)
+ {
+ if (oldToNew[j] >= 0)
+ {
+ mPaintChannels[i][(uint32_t)oldToNew[j]] = mesh.mPaintChannels[i][j];
+ }
+ }
+ }
+ }
+
+ // skeleton binding
+
+ mSkeletonFile = mesh.mSkeletonFile;
+
+ if (mesh.mBoneIndicesExternal.size() == numVerts * 4)
+ {
+ mBoneIndicesExternal.resize((uint32_t)numNewVerts * 4);
+ mBoneWeights.resize((uint32_t)numNewVerts);
+
+ for (size_t i = 0; i < numVerts; i++)
+ {
+ if (oldToNew[i] >= 0)
+ {
+ const int newIndex = oldToNew[i];
+ for (int j = 0; j < 4; j++)
+ {
+ mBoneIndicesExternal[(uint32_t)newIndex * 4 + j] = mesh.mBoneIndicesExternal[i * 4 + j];
+ }
+ mBoneWeights[(uint32_t)newIndex] = mesh.mBoneWeights[i];
+ }
+ }
+ }
+ mSubMeshes[0].firstIndex = 0;
+ mSubMeshes[0].numIndices = (unsigned int)mIndices.size();
+ mName = mesh.mName;
+ mRendererTransform = mesh.mRendererTransform;
+ updateBounds();
+ updateBoneWeights();
+}
+
+// -------------------------------------------------------------------
+bool TriangleMesh::loadFromObjFile(const std::string& filename, bool useCustomChannels)
+{
+#if PX_WINDOWS_FAMILY == 0
+ PX_UNUSED(filename);
+ PX_UNUSED(useCustomChannels);
+ return false;
+#else
+ initSingleMesh();
+ mName = filename;
+
+ // extract path
+ size_t slashPos = filename.rfind('\\', std::string::npos);
+ size_t columnPos = filename.rfind(':', std::string::npos);
+
+ size_t pos = slashPos > columnPos ? slashPos : columnPos;
+ std::string path = pos == std::string::npos ? "" : filename.substr(0, pos);
+
+ char s[OBJ_STR_LEN], ps[OBJ_STR_LEN];
+ physx::PxVec3 v;
+ nvidia::VertexUV tc;
+ std::vector<SimpleVertexRef> refs;
+ SimpleVertexRef ref[3];
+
+ int numIndices = 0;
+ std::vector<physx::PxVec3> vertices;
+ std::vector<physx::PxVec3> normals;
+ std::vector<nvidia::VertexUV> texCoords;
+
+
+ FILE* f = 0;
+
+ if (::fopen_s(&f, filename.c_str(), "r") != 0)
+ {
+ return false;
+ }
+
+ std::string groupName;
+ std::string useMtl;
+
+ // first a vertex ref is generated for each v/n/t combination
+ while (!feof(f))
+ {
+ if (fgets(s, OBJ_STR_LEN, f) == NULL)
+ {
+ break;
+ }
+
+ //wxMessageBox(s);
+ if (strncmp(s, "usemtl", 6) == 0 || strncmp(s, "g ", 2) == 0) // new group
+ {
+ if (strncmp(s, "usemtl", 6) == 0)
+ {
+ char sub[OBJ_STR_LEN];
+ sscanf(&s[7], "%s", sub);
+ useMtl = sub;
+ }
+ else
+ {
+ char sub[OBJ_STR_LEN];
+ sscanf(&s[2], "%s", sub);
+ groupName = sub;
+ }
+
+ size_t numSubs = mSubMeshes.size();
+ if (mSubMeshes[numSubs - 1].numIndices > 0)
+ {
+ mSubMeshes.resize(numSubs + 1);
+ mSubMeshes[numSubs].init();
+ mSubMeshes[numSubs].firstIndex = (uint32_t)(mIndices.size());
+ mSubMeshes[numSubs].materialName = useMtl;
+ mSubMeshes[numSubs].originalMaterialName = useMtl;
+ }
+ else
+ {
+ mSubMeshes[numSubs - 1].materialName = useMtl;
+ mSubMeshes[numSubs - 1].originalMaterialName = useMtl;
+ }
+ size_t subNr = mSubMeshes.size() - 1;
+ mSubMeshes[subNr].name = groupName;
+ }
+ else if (strncmp(s, "v ", 2) == 0) // vertex
+ {
+ sscanf(s, "v %f %f %f", &v.x, &v.y, &v.z);
+ vertices.push_back(v);
+ }
+ else if (strncmp(s, "vn ", 3) == 0) // normal
+ {
+ sscanf(s, "vn %f %f %f", &v.x, &v.y, &v.z);
+ normals.push_back(v);
+ }
+ else if (strncmp(s, "vt ", 3) == 0) // texture coords
+ {
+ sscanf(s, "vt %f %f", &tc.u, &tc.v);
+ texCoords.push_back(tc);
+ }
+ else if (strncmp(s, "f ", 2) == 0) // face, tri or quad
+ {
+ size_t offset = 2;
+ size_t index = 0;
+ while (sscanf(s + offset, "%s", ps) > 0)
+ {
+ offset += strlen(ps);
+ while (s[offset] == ' ' || s[offset] == '\t')
+ {
+ offset++;
+ }
+
+ if (index >= 2)
+ {
+ // submit triangle
+ ref[2].parse(ps, 0);
+ ref[0].indexNr = numIndices++;
+ refs.push_back(ref[0]);
+ mIndices.push_back(0);
+ ref[1].indexNr = numIndices++;
+ refs.push_back(ref[1]);
+ mIndices.push_back(0);
+ ref[2].indexNr = numIndices++;
+ refs.push_back(ref[2]);
+ mIndices.push_back(0);
+ mSubMeshes[mSubMeshes.size() - 1].numIndices += 3;
+
+ ref[1] = ref[2];
+ index++;
+ }
+ else
+ {
+ ref[index].parse(ps, 0);
+ index++;
+ }
+ }
+ }
+ }
+ fclose(f);
+
+ // make sure that vertices with left/right handed tangent space don't get merged
+ for (size_t i = 0; i < refs.size(); i += 3)
+ {
+ const physx::PxVec3 p0 = vertices[(uint32_t)refs[i + 0].vert];
+ const physx::PxVec3 p1 = vertices[(uint32_t)refs[i + 1].vert];
+ const physx::PxVec3 p2 = vertices[(uint32_t)refs[i + 2].vert];
+
+ float handedNess = 1.0f;
+
+ if (refs[i + 0].texCoord >= 0)
+ {
+ const physx::PxVec3 faceNormal = (p1 - p0).cross(p2 - p0);
+
+ const nvidia::VertexUV w0 = texCoords[(uint32_t)refs[i + 0].texCoord];
+ const nvidia::VertexUV w1 = texCoords[(uint32_t)refs[i + 1].texCoord];
+ const nvidia::VertexUV w2 = texCoords[(uint32_t)refs[i + 2].texCoord];
+
+ const float x1 = p1.x - p0.x;
+ const float x2 = p2.x - p0.x;
+ const float y1 = p1.y - p0.y;
+ const float y2 = p2.y - p0.y;
+ const float z1 = p1.z - p0.z;
+ const float z2 = p2.z - p0.z;
+
+ const float s1 = w1.u - w0.u;
+ const float s2 = w2.u - w0.u;
+ const float t1 = w1.v - w0.v;
+ const float t2 = w2.v - w0.v;
+
+ const float r = 1.0F / (s1 * t2 - s2 * t1);
+ const physx::PxVec3 tangentDir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
+ const physx::PxVec3 bitangentDir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
+
+ handedNess = faceNormal.cross(tangentDir).dot(bitangentDir) > 0.0f ? 1.0f : -1.0f;
+ }
+
+ refs[i + 0].handedNess = handedNess;
+ refs[i + 1].handedNess = handedNess;
+ refs[i + 2].handedNess = handedNess;
+ }
+
+ // now we merge multiple v/n/t triplets
+ //std::sort(refs.begin(), refs.end());
+
+ size_t readRefs = 0;
+ physx::PxVec3 defNormal(1.0f, 0.0f, 0.0f);
+ bool normalsOK = true;
+ int numTexCoords = (int)(texCoords.size());
+
+ while (readRefs < refs.size())
+ {
+ int vertNr = (int)(mVertices.size());
+ SimpleVertexRef& r = refs[readRefs];
+ mVertices.push_back(vertices[(uint32_t)r.vert]);
+
+ if (r.normal >= 0)
+ {
+ mNormals.push_back(normals[(uint32_t)r.normal]);
+ }
+ else
+ {
+ mNormals.push_back(defNormal);
+ normalsOK = false;
+ }
+
+ if (r.texCoord >= 0 && r.texCoord < numTexCoords)
+ {
+ mTexCoords[0].push_back(texCoords[(uint32_t)r.texCoord]);
+ }
+ else
+ {
+ mTexCoords[0].push_back(nvidia::VertexUV(0.0f, 0.0f));
+ }
+
+ mIndices[(uint32_t)r.indexNr] = (uint32_t)vertNr;
+ readRefs++;
+ /*while (readRefs < refs.size() && r == refs[readRefs])
+ {
+ mIndices[refs[readRefs].indexNr] = vertNr;
+ readRefs++;
+ }*/
+ }
+
+ complete(useCustomChannels);
+
+ if (!normalsOK)
+ {
+ updateNormals(-1);
+ }
+
+ updateTangents();
+
+ mMaterialPrefix.clear();
+ mMaterialSuffix.clear();
+
+ return true;
+#endif
+}
+
+// ----------------------------------------------------------------------
+bool TriangleMesh::saveToObjFile(const std::string& filename) const
+{
+#if PX_WINDOWS_FAMILY == 0
+ PX_UNUSED(filename);
+ return false;
+#else
+ FILE* f = 0;
+ if (::fopen_s(&f, filename.c_str(), "w") != 0)
+ {
+ return false;
+ }
+
+ fprintf(f, "# Wavefront OBJ\n");
+ fprintf(f, "\n");
+
+ fprintf(f, "\n");
+ fprintf(f, "# %i vertices:\n", (int)(mVertices.size()));
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ const physx::PxVec3& v = mVertices[i];
+ fprintf(f, "v %f %f %f\n", v.x, v.y, v.z);
+ }
+
+ fprintf(f, "\n");
+ int numTex = (int)(mTexCoords[0].size());
+ fprintf(f, "# %i texture coordinates:\n", numTex);
+ for (uint32_t i = 0; i < (uint32_t)numTex; i++)
+ {
+ fprintf(f, "vt %f %f\n", mTexCoords[0][i].u, mTexCoords[0][i].v);
+ }
+
+ fprintf(f, "\n");
+ fprintf(f, "# %i normals:\n", (int)(mNormals.size()));
+ for (uint32_t i = 0; i < mNormals.size(); i++)
+ {
+ const physx::PxVec3& v = mNormals[i];
+ fprintf(f, "vn %f %f %f\n", v.x, v.y, v.z);
+ }
+
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ const TriangleSubMesh& sm = mSubMeshes[i];
+ fprintf(f, "\n");
+ fprintf(f, "g %s\n", sm.name.c_str());
+
+ for (uint32_t j = sm.firstIndex; j < sm.firstIndex + sm.numIndices; j += 3)
+ {
+ uint32_t i0 = mIndices[j];
+ uint32_t i1 = mIndices[j + 1];
+ uint32_t i2 = mIndices[j + 2];
+
+ fprintf(f, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", i0, i0, i0, i1, i1, i1, i2, i2, i2);
+ }
+ fprintf(f, "\n");
+ }
+ fclose(f);
+
+ return true;
+#endif
+}
+
+// -------------------------------------------------------------------
+bool TriangleMesh::loadFromXML(const std::string& filename, bool loadCustomChannels)
+{
+ clear(NULL, NULL);
+
+ physx::shdfnd::FastXml* fastXml = physx::shdfnd::createFastXml(this);
+ mParserState = PS_Uninitialized;
+ physx::PsFileBuffer fb(filename.c_str(), physx::PxFileBuf::OPEN_READ_ONLY);
+ physx::PxInputDataFromPxFileBuf id(fb);
+ fastXml->processXml(id);
+
+ int errorLineNumber = -1;
+ const char* xmlError = fastXml->getError(errorLineNumber);
+ if (xmlError != NULL)
+ {
+#ifdef WIN32
+ char temp[1024];
+ sprintf_s(temp, 1024, "Xml parse error in %s on line %d:\n\n%s", filename.c_str(), errorLineNumber, xmlError);
+ MessageBox(NULL, temp, "FastXML", MB_OK);
+#else
+ printf("Xml parse error in %s on line %d:\n\n%s", filename.c_str(), errorLineNumber, xmlError);
+#endif
+ return false;
+ }
+
+ if (fastXml)
+ {
+ fastXml->release();
+ }
+
+ // normalize bone weights
+ int invalidStart = -1;
+ if (mBoneWeights.size() > 0)
+ {
+ for (size_t i = 0; i < mVertices.size(); i++)
+ {
+ float sum = 0;
+ for (unsigned int j = 0; j < 4; j++)
+ {
+ sum += mBoneWeights[i][j];
+ }
+ if (sum > 0)
+ {
+ float scale = 1.0f / sum;
+ mBoneWeights[i] *= scale;
+ if (invalidStart != -1)
+ {
+ PX_ASSERT(i > 0);
+ printf("\n\nWARNING: Invalid Vertices from %d to %d\n\n\n", invalidStart, (int)(i - 1));
+ invalidStart = -1;
+ }
+ }
+ else
+ {
+ if (invalidStart == -1)
+ {
+ invalidStart = (int)i;
+ }
+ }
+ }
+ }
+ if (invalidStart != -1)
+ {
+ printf("\n\nWARNING: Invalid Vertices from %d to %d\n\n\n", invalidStart, (int)(mVertices.size() - 1));
+ invalidStart = -1;
+ }
+
+ complete(loadCustomChannels);
+
+ //mShaderHasEnoughBones = getMaxBoneIndex() < shaderMaxBones;
+
+ mMaterialPrefix.clear();
+ mMaterialSuffix.clear();
+
+ return true;
+}
+
+// -------------------------------------------------------------------
+bool TriangleMesh::saveToXML(const std::string& filename)
+{
+ FILE* f = fopen(filename.c_str(), "w");
+ if (!f)
+ {
+ return false;
+ }
+
+ fprintf(f, "<mesh>\n");
+
+ fprintf(f, " <sharedgeometry vertexcount=\"%i\">\n", (int)(mVertices.size()));
+
+ fprintf(f, " <vertexbuffer positions=\"true\" normals=\"true\">\n");
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ fprintf(f, " <vertex>\n");
+ fprintf(f, " <position x=\"%f\" y=\"%f\" z=\"%f\" />\n", mVertices[i].x, mVertices[i].y, mVertices[i].z);
+ if (i < mNormals.size())
+ {
+ fprintf(f, " <normal x=\"%f\" y=\"%f\" z=\"%f\" />\n", mNormals[i].x, mNormals[i].y, mNormals[i].z);
+ }
+ fprintf(f, " </vertex>\n");
+ }
+ fprintf(f, " </vertexbuffer>\n");
+
+ fprintf(f, " <vertexbuffer texture_coord_dimensions_0=\"2\">\n");
+ for (uint32_t i = 0; i < mTexCoords[0].size(); i++)
+ {
+ fprintf(f, " <vertex>\n");
+ fprintf(f, " <texcoord u=\"%f\" v=\"%f\" />\n", mTexCoords[0][i].u, mTexCoords[0][i].v);
+ fprintf(f, " </vertex>\n");
+ }
+ fprintf(f, " </vertexbuffer>\n");
+
+ bool coeffsOK = true;
+// coeffsOK = false; // no more coeffs
+ for (int i = 0; i < PC_NUM_CHANNELS; i++)
+ {
+ if (mPaintChannels[i].size() != mVertices.size())
+ {
+ coeffsOK = false;
+ }
+ }
+
+ if (coeffsOK)
+ {
+ fprintf(f, " <vertexbuffer physics_coeffs=\"%i\">\n", PC_NUM_CHANNELS);
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ fprintf(f, " <vertex>\n");
+ fprintf(f, " <physics_coeffs ");
+ for (uint32_t j = 0; j < PC_NUM_CHANNELS; j++)
+ {
+ fprintf(f, "v%i=\"%f\" ", j, mPaintChannels[j][i].paintValueF32);
+ }
+ fprintf(f, " />\n");
+ fprintf(f, " </vertex>\n");
+ }
+ fprintf(f, " </vertexbuffer>\n");
+ }
+
+ fprintf(f, " </sharedgeometry>\n");
+
+ fprintf(f, " <submeshes>\n");
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ TriangleSubMesh& sm = mSubMeshes[i];
+ fprintf(f, " <submesh material=\"%s\" usesharedvertices=\"true\" use32bitindexes=\"true\" operationtype=\"triangle_list\">\n",
+ sm.materialName.c_str());
+ unsigned int numTris = sm.numIndices / 3;
+ fprintf(f, " <faces count =\"%i\">\n", numTris);
+
+ for (uint32_t j = sm.firstIndex; j < sm.firstIndex + sm.numIndices; j += 3)
+ {
+ fprintf(f, " <face v1=\"%i\" v2=\"%i\" v3=\"%i\" />\n", mIndices[j], mIndices[j + 1], mIndices[j + 2]);
+ }
+ fprintf(f, " </faces>\n");
+ fprintf(f, " </submesh>\n");
+ }
+ fprintf(f, " </submeshes>\n");
+
+ fprintf(f, " <skeletonlink name=\"%s\" />\n", mSkeletonFile.c_str());
+
+ if (mBoneWeights.size() > 0)
+ {
+ fprintf(f, " <boneassignments>\n");
+ for (uint32_t i = 0; i < (uint32_t)mBoneWeights.size(); i++)
+ {
+ for (uint32_t j = 0; j < 4; j++)
+ {
+ fprintf(f, " <vertexboneassignment vertexindex=\"%i\" boneindex=\"%i\" weight=\"%f\" />\n",
+ i, mBoneIndicesExternal[i * 4 + j], mBoneWeights[i][j]);
+ }
+ }
+ fprintf(f, " </boneassignments>\n");
+ }
+
+ fprintf(f, "</mesh>\n");
+
+
+ fclose(f);
+
+ return true;
+}
+
+// ----------------------------------------------------------------------
+bool TriangleMesh::loadFromParent(TriangleMesh* parent)
+{
+ if (parent == NULL)
+ {
+ return false;
+ }
+
+ mParent = parent;
+ mMaxBoneIndexExternal = mParent->mMaxBoneIndexExternal;
+ mMaxBoneIndexInternal = mParent->mMaxBoneIndexInternal;
+
+ mSubMeshes.resize(mParent->mSubMeshes.size());
+ for (uint32_t i = 0; i < mParent->mSubMeshes.size(); i++)
+ {
+ mSubMeshes[i] = mParent->mSubMeshes[i];
+ mSubMeshes[i].mRenderResource = NULL; // don't copy that one!!!
+#ifdef USE_SAMPLE_RENDERER
+ mSubMeshes[i].mRendererMeshContext = new SampleRenderer::RendererMeshContext;
+ if (mSubMeshes[i].mRendererMaterialInstance != NULL)
+ {
+ mSubMeshes[i].mRendererMaterialInstance = new SampleRenderer::RendererMaterialInstance(*mSubMeshes[i].mRendererMaterialInstance);
+ }
+ mSubMeshes[i].mRendererMesh = NULL;
+#endif
+ }
+
+ mMaterialPrefix.clear();
+ mMaterialSuffix.clear();
+
+ mUseGpuSkinning = parent->mUseGpuSkinning;
+ mRenderer = mParent->mRenderer;
+
+ return true;
+}
+
+// ----------------------------------------------------------------------
+bool TriangleMesh::loadFromMeshImport(mimp::MeshSystemContainer* msc, bool useCustomChannels)
+{
+ if (msc == NULL)
+ {
+ return false;
+ }
+
+ mimp::MeshSystem* ms = mimp::gMeshImport->getMeshSystem(msc);
+
+ if (ms->mMeshCount == 0)
+ {
+ return false;
+ }
+
+ mimp::Mesh* m = ms->mMeshes[0];
+ if (m->mVertexCount == 0)
+ {
+ return false;
+ }
+
+ // only now that everything is OK do we delete the existing mesh
+ initSingleMesh();
+
+ // and since we generate all submeshes ourselves
+ mSubMeshes.clear();
+
+ const char* bake = strstr(ms->mAssetInfo, "applyRootTransformToMesh");
+ bool bakeRootBoneTransformation = bake != NULL;
+ physx::PxTransform rootBoneTransformation;
+ if (ms->mSkeletonCount > 0 && bakeRootBoneTransformation)
+ {
+ mimp::MeshBone& b = ms->mSkeletons[0]->mBones[0];
+ PxQuatFromArray(rootBoneTransformation.q, b.mOrientation);
+ PxVec3FromArray(rootBoneTransformation.p, b.mPosition);
+ }
+
+ for (unsigned int k = 0; k < ms->mMeshCount; k++)
+ {
+ mimp::Mesh* m = ms->mMeshes[k];
+
+ unsigned int base_index = (uint32_t)mVertices.size();
+
+ // PH: ClothingTool does not cope well with really small meshes
+ if (m->mVertexCount < 3)
+ {
+ continue;
+ }
+
+ for (unsigned int i = 0; i < m->mVertexCount; i++)
+ {
+ const mimp::MeshVertex& v = m->mVertices[i];
+
+ physx::PxVec3 p;
+ PxVec3FromArray(p, v.mPos);
+ if (bakeRootBoneTransformation)
+ {
+ p = rootBoneTransformation.transform(p);
+ }
+
+ if (m->mVertexFlags & mimp::MIVF_NORMAL)
+ {
+ physx::PxVec3 n;
+ PxVec3FromArray(n, v.mNormal);
+ if (bakeRootBoneTransformation)
+ {
+ n = rootBoneTransformation.rotate(n);
+ }
+ mVertices.push_back(p);
+ mNormals.push_back(n);
+ }
+
+ if (m->mVertexFlags & mimp::MIVF_TANGENT)
+ {
+ physx::PxVec3 t;
+ PxVec3FromArray(t, v.mTangent);
+ if (bakeRootBoneTransformation)
+ {
+ t = rootBoneTransformation.rotate(t);
+ }
+ mTangents.push_back(t);
+ }
+
+ if (m->mVertexFlags & mimp::MIVF_BINORMAL)
+ {
+ physx::PxVec3 b;
+ PxVec3FromArray(b, v.mBiNormal);
+ if (bakeRootBoneTransformation)
+ {
+ b = rootBoneTransformation.rotate(b);
+ }
+ mBitangents.push_back(b);
+ }
+
+ if (m->mVertexFlags & mimp::MIVF_TEXEL1)
+ {
+ mTexCoords[0].push_back(nvidia::VertexUV(v.mTexel1[0], v.mTexel1[1]));
+ }
+
+ if (m->mVertexFlags & mimp::MIVF_TEXEL2)
+ {
+ mTexCoords[1].push_back(nvidia::VertexUV(v.mTexel2[0], v.mTexel2[1]));
+ }
+
+ if (m->mVertexFlags & mimp::MIVF_TEXEL3)
+ {
+ mTexCoords[2].push_back(nvidia::VertexUV(v.mTexel3[0], v.mTexel3[1]));
+ }
+
+ if (m->mVertexFlags & mimp::MIVF_TEXEL4)
+ {
+ mTexCoords[3].push_back(nvidia::VertexUV(v.mTexel4[0], v.mTexel4[1]));
+ }
+
+ if (m->mVertexFlags & mimp::MIVF_BONE_WEIGHTING)
+ {
+ physx::PxVec4 boneWeight(0.0f, 0.0f, 0.0f, 0.0f);
+ unsigned int boneWeightWritten = 0;
+ for (unsigned int j = 0; j < 4; j++)
+ {
+ if (v.mWeight[j] > 0.0f)
+ {
+ mBoneIndicesExternal.push_back(v.mBone[j]);
+ mMaxBoneIndexExternal = physx::PxMax(mMaxBoneIndexExternal, (int32_t)v.mBone[j]);
+ boneWeight[boneWeightWritten++] = v.mWeight[j];
+ }
+ else
+ {
+ mBoneIndicesExternal.push_back(0);
+ }
+ }
+ PX_ASSERT(boneWeightWritten <= 4);
+ mBoneWeights.push_back(boneWeight);
+ }
+
+ // Fixup
+ for (unsigned int t = 0; t < NUM_TEXCOORDS; t++)
+ {
+ nvidia::VertexUV uv(0.0f, 0.0f);
+ if (mTexCoords[t].size() > 0)
+ {
+ while (mTexCoords[t].size() < mVertices.size())
+ {
+ mTexCoords[t].push_back(uv);
+ }
+ }
+ }
+ }
+
+ for (unsigned int i = 0; i < m->mSubMeshCount; i++)
+ {
+ bool showMesh = true;
+
+ mimp::SubMesh* sm = m->mSubMeshes[i];
+ if (sm->mMaterial && sm->mMaterial->mMetaData)
+ {
+ mimp::KeyValue kv;
+ unsigned int count;
+ const char** keyValues = kv.getKeyValues(sm->mMaterial->mMetaData, count);
+ for (unsigned int k = 0; k < count; ++k)
+ {
+ const char* key = keyValues[k * 2];
+ const char* kvalue = keyValues[k * 2 + 1];
+ if (0 == ::strcmp(key, "show") && 0 == ::strcmp(kvalue, "invisible"))
+ {
+ showMesh = false;
+ }
+ }
+ }
+
+ TriangleSubMesh t;
+ t.init();
+ t.firstIndex = (uint32_t)mIndices.size();
+ t.numIndices = sm->mTriCount * 3;
+ t.name = m->mName;
+ t.materialName = sm->mMaterialName;
+ t.originalMaterialName = sm->mMaterialName;
+ t.show = showMesh;
+
+ for (unsigned int j = 0; j < t.numIndices; j++)
+ {
+ mIndices.push_back(sm->mIndices[j] + base_index);
+ }
+ mSubMeshes.push_back(t);
+ }
+ }
+
+ complete(useCustomChannels);
+
+ //mShaderHasEnoughBones = getMaxBoneIndex() < shaderMaxBones;
+
+ mMaterialPrefix.clear();
+ mMaterialSuffix.clear();
+
+ return true;
+}
+
+// ----------------------------------------------------------------------
+bool TriangleMesh::saveToMeshImport(mimp::MeshSystemContainer* msc)
+{
+#if PX_WINDOWS_FAMILY == 0
+ PX_UNUSED(msc);
+ return false;
+#else
+ if (msc == NULL)
+ {
+ return false;
+ }
+
+ mimp::MeshSystem* ms = mimp::gMeshImport->getMeshSystem(msc);
+
+ ms->mMeshCount = 1;
+ ms->mMeshes = (mimp::Mesh**)::malloc(sizeof(mimp::Mesh*) * 1);
+ ms->mMeshes[0] = new mimp::Mesh;
+
+ unsigned int vertexFlags = mimp::MIVF_POSITION;
+
+ // fill out vertices
+ {
+ ms->mMeshes[0]->mVertexCount = (unsigned int)mVertices.size();
+ mimp::MeshVertex* verts = ms->mMeshes[0]->mVertices = (mimp::MeshVertex*)::malloc(sizeof(mimp::MeshVertex) * mVertices.size());
+ ::memset(verts, 0, sizeof(mimp::MeshVertex) * mVertices.size());
+
+ for (size_t i = 0; i < mVertices.size(); i++)
+ {
+ (physx::PxVec3&)verts[i].mPos = mVertices[i];
+ }
+
+ if (mNormals.size() == mVertices.size())
+ {
+ vertexFlags |= mimp::MIVF_NORMAL;
+ for (size_t i = 0; i < mNormals.size(); i++)
+ {
+ (physx::PxVec3&)verts[i].mNormal = mNormals[i];
+ }
+ }
+
+ if (mTangents.size() == mVertices.size())
+ {
+ vertexFlags |= mimp::MIVF_TANGENT;
+ for (size_t i = 0; i < mTangents.size(); i++)
+ {
+ (physx::PxVec3&)verts[i].mTangent = mTangents[i];
+ }
+ }
+
+ if (mBitangents.size() == mVertices.size())
+ {
+ vertexFlags |= mimp::MIVF_BINORMAL;
+ for (size_t i = 0; i < mBitangents.size(); i++)
+ {
+ (physx::PxVec3&)verts[i].mBiNormal = mBitangents[i];
+ }
+ }
+
+ if (mTexCoords[0].size() == mVertices.size())
+ {
+ vertexFlags |= mimp::MIVF_TEXEL1;
+ for (size_t i = 0; i < mTexCoords[0].size(); i++)
+ {
+ (nvidia::VertexUV&)verts[i].mTexel1 = mTexCoords[0][i];
+ }
+ }
+
+ if (mBoneWeights.size() == mVertices.size())
+ {
+ PX_ASSERT(mBoneIndicesExternal.size() == mVertices.size() * 4);
+ vertexFlags |= mimp::MIVF_BONE_WEIGHTING;
+
+ for (size_t i = 0; i < mBoneWeights.size(); i++)
+ {
+ for (int k = 0; k < 4; k++)
+ {
+ verts[i].mBone[k] = mBoneIndicesExternal[i * 4 + k];
+ verts[i].mWeight[k] = mBoneWeights[i][k];
+ }
+ }
+ }
+ }
+
+ ms->mMeshes[0]->mVertexFlags = vertexFlags;
+
+ {
+ size_t nameLen = mSubMeshes[0].name.length() + 1;
+ ms->mMeshes[0]->mName = (char*)::malloc(sizeof(char) * nameLen);
+ strcpy_s((char*)ms->mMeshes[0]->mName, nameLen, mSubMeshes[0].name.c_str());
+ }
+
+ ms->mMeshes[0]->mSubMeshCount = (unsigned int)mSubMeshes.size();
+ ms->mMeshes[0]->mSubMeshes = (mimp::SubMesh**)::malloc(sizeof(mimp::SubMesh*) * mSubMeshes.size());
+
+ for (size_t submeshIndex = 0; submeshIndex < mSubMeshes.size(); submeshIndex++)
+ {
+ ms->mMeshes[0]->mSubMeshes[submeshIndex] = new mimp::SubMesh;
+ mimp::SubMesh* sm = ms->mMeshes[0]->mSubMeshes[submeshIndex];
+
+ sm->mIndices = (unsigned int*)::malloc(sizeof(unsigned int) * mSubMeshes[submeshIndex].numIndices);
+
+ for (size_t i = 0; i < mSubMeshes[submeshIndex].numIndices; i++)
+ {
+ sm->mIndices[i] = mIndices[mSubMeshes[submeshIndex].firstIndex + i];
+ }
+ PX_ASSERT(mSubMeshes[submeshIndex].numIndices % 3 == 0);
+ sm->mTriCount = mSubMeshes[submeshIndex].numIndices / 3;
+
+ size_t matNameLength = mSubMeshes[submeshIndex].materialName.length() + 1;
+ sm->mMaterialName = (char*)::malloc(sizeof(char) * matNameLength);
+ strcpy_s((char*)sm->mMaterialName, matNameLength, mSubMeshes[submeshIndex].materialName.c_str());
+
+ sm->mVertexFlags = vertexFlags;
+ }
+
+ return true;
+#endif
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::initPlane(float length, float uvDist, const char* materialName)
+{
+ initSingleMesh();
+
+ mVertices.resize(9);
+ mNormals.resize(9);
+ mTexCoords[0].resize(9);
+
+ for (uint32_t i = 0; i < 9; i++)
+ {
+ mNormals[i] = physx::PxVec3(0.0f, 1.0f, 0.0f);
+ }
+
+ mVertices[0] = physx::PxVec3(-length, 0.0f, length);
+ mTexCoords[0][0].set(-uvDist, uvDist);
+
+ mVertices[1] = physx::PxVec3(0.0f, 0.0f, length);
+ mTexCoords[0][1].set(0.0f, uvDist);
+
+ mVertices[2] = physx::PxVec3(length, 0.0f, length);
+ mTexCoords[0][2].set(uvDist, uvDist);
+
+ mVertices[3] = physx::PxVec3(-length, 0.0f, 0.0f);
+ mTexCoords[0][3].set(-uvDist, 0.0f);
+
+ mVertices[4] = physx::PxVec3(0.0f, 0.0f, 0.0f);
+ mTexCoords[0][4].set(0.0f, 0.0f);
+
+ mVertices[5] = physx::PxVec3(length, 0.0f, 0.0f);
+ mTexCoords[0][5].set(uvDist, 0.0f);
+
+ mVertices[6] = physx::PxVec3(-length, 0.0f, -length);
+ mTexCoords[0][6].set(-uvDist, -uvDist);
+
+ mVertices[7] = physx::PxVec3(0.0f, 0.0f, -length);
+ mTexCoords[0][7].set(0.0f, -uvDist);
+
+ mVertices[8] = physx::PxVec3(length, 0.0f, -length);
+ mTexCoords[0][8].set(uvDist, -uvDist);
+
+
+ mIndices.resize(8 * 3);
+
+ mIndices[ 0] = 0;
+ mIndices[ 1] = 1;
+ mIndices[ 2] = 3;
+ mIndices[ 3] = 3;
+ mIndices[ 4] = 1;
+ mIndices[ 5] = 4;
+ mIndices[ 6] = 1;
+ mIndices[ 7] = 2;
+ mIndices[ 8] = 4;
+ mIndices[ 9] = 4;
+ mIndices[10] = 2;
+ mIndices[11] = 5;
+ mIndices[12] = 3;
+ mIndices[13] = 4;
+ mIndices[14] = 6;
+ mIndices[15] = 6;
+ mIndices[16] = 4;
+ mIndices[17] = 7;
+ mIndices[18] = 4;
+ mIndices[19] = 5;
+ mIndices[20] = 7;
+ mIndices[21] = 7;
+ mIndices[22] = 5;
+ mIndices[23] = 8;
+
+ mSubMeshes[0].materialName = mSubMeshes[0].originalMaterialName = materialName;
+ mSubMeshes[0].numIndices = 24;
+
+ updateBounds();
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::initFrom(nvidia::apex::ClothingPhysicalMesh& mesh, bool initCustomChannels)
+{
+ initSingleMesh();
+
+ mVertices.resize(mesh.getNumVertices());
+ mesh.getVertices(&mVertices[0], sizeof(physx::PxVec3));
+
+ if (mesh.isTetrahedralMesh())
+ {
+ // reset normals to prevent updateNormals from doing something stupid on a tet mesh!
+ mNormals.clear();
+ mNormals.resize(mVertices.size(), physx::PxVec3(0.0f, 0.0f, 0.0f));
+ // normals are not yet generated here (only available in ClothingAssetAuthoring)
+ //mesh.getVertexValue(RenderVertexSemantic::NORMAL, RenderDataFormat::FLOAT3, &mNormals[0], sizeof(physx::PxVec3));
+
+ /*unsigned int numIndices = mesh.getNumIndices();
+ assert(numIndices % 4 == 0);
+ mIndices.reserve(numIndices / 4 * 3); // same as / 4 * 12
+
+ std::vector<uint32_t> tempIndices;
+ tempIndices.resize(numIndices);
+ mesh.getIndices(&tempIndices[0], 0);
+
+ for (unsigned int i = 0; i < numIndices; i += 4)
+ {
+ unsigned int indices[4] =
+ {
+ tempIndices[i + 0],
+ tempIndices[i + 1],
+ tempIndices[i + 2],
+ tempIndices[i + 3],
+ };
+
+ mIndices.push_back(indices[0]);
+ mIndices.push_back(indices[1]);
+ mIndices.push_back(indices[2]);
+
+ mIndices.push_back(indices[0]);
+ mIndices.push_back(indices[3]);
+ mIndices.push_back(indices[1]);
+
+ mIndices.push_back(indices[0]);
+ mIndices.push_back(indices[2]);
+ mIndices.push_back(indices[3]);
+
+ mIndices.push_back(indices[1]);
+ mIndices.push_back(indices[3]);
+ mIndices.push_back(indices[2]);
+ }*/
+ }
+ //else
+ {
+ mIndices.resize(mesh.getNumIndices());
+ mesh.getIndices(&mIndices[0], 0);
+ }
+
+ complete(initCustomChannels);
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::initFrom(nvidia::apex::RenderMeshAssetAuthoring& mesh, bool initCustomChannels)
+{
+ PX_ASSERT(mesh.getPartCount() == 1);
+
+ initSingleMesh();
+
+ uint32_t numVertices = 0;
+ uint32_t numIndices = 0;
+ uint32_t numBonesPerVertex = 0;
+ for (uint32_t i = 0; i < mesh.getSubmeshCount(); i++)
+ {
+ numVertices += mesh.getSubmesh(i).getVertexCount(0);
+ numIndices += mesh.getSubmesh(i).getIndexCount(0);
+
+ const nvidia::apex::VertexFormat& format = mesh.getSubmesh(i).getVertexBuffer().getFormat();
+ const int boneIndexIndex = format.getBufferIndexFromID(format.getSemanticID(nvidia::apex::RenderVertexSemantic::BONE_INDEX));
+ uint32_t numBonesPerVertexSubmesh = 0;
+ switch(format.getBufferFormat((uint32_t)boneIndexIndex))
+ {
+ case nvidia::apex::RenderDataFormat::USHORT1:
+ numBonesPerVertexSubmesh = 1;
+ break;
+ case nvidia::apex::RenderDataFormat::USHORT2:
+ numBonesPerVertexSubmesh = 2;
+ break;
+ case nvidia::apex::RenderDataFormat::USHORT3:
+ numBonesPerVertexSubmesh = 3;
+ break;
+ case nvidia::apex::RenderDataFormat::USHORT4:
+ numBonesPerVertexSubmesh = 4;
+ break;
+ default:
+ numBonesPerVertexSubmesh = 0;
+ break;
+ }
+
+ // they are either identical or one of them is 0
+ PX_ASSERT(numBonesPerVertexSubmesh == numBonesPerVertex || (numBonesPerVertex * numBonesPerVertexSubmesh) == 0);
+ numBonesPerVertex = numBonesPerVertexSubmesh;
+ }
+
+ mVertices.clear();
+ mVertices.resize(numVertices);
+ mNormals.clear();
+ mNormals.resize(numVertices);
+ mIndices.clear();
+ mIndices.resize(numIndices);
+ mSubMeshes.resize(mesh.getSubmeshCount());
+
+ if (numBonesPerVertex > 0)
+ {
+ mBoneIndicesExternal.clear();
+ mBoneIndicesExternal.resize(numVertices * 4, 0);
+ mBoneWeights.clear();
+ mBoneWeights.resize(numVertices, physx::PxVec4(0.0f));
+ }
+ mMaxBoneIndexExternal = -1;
+
+ bool hasTexCoords = false;
+ for (uint32_t i = 0; i < mesh.getSubmeshCount(); i++)
+ {
+ const nvidia::apex::VertexFormat& format = mesh.getSubmesh(i).getVertexBuffer().getFormat();
+ const int texCoordIndex = format.getBufferIndexFromID(format.getSemanticID(nvidia::apex::RenderVertexSemantic::TEXCOORD0));
+ if (texCoordIndex != -1)
+ {
+ hasTexCoords = true;
+ }
+ }
+ if (hasTexCoords)
+ {
+ mTexCoords[0].clear();
+ mTexCoords[0].resize(numVertices);
+ }
+
+ bool hasTangents = false;
+ for (uint32_t i = 0; i < mesh.getSubmeshCount(); i++)
+ {
+ const nvidia::apex::VertexFormat& format = mesh.getSubmesh(i).getVertexBuffer().getFormat();
+ const int bitangentIndex = format.getBufferIndexFromID(format.getSemanticID(nvidia::apex::RenderVertexSemantic::TANGENT));
+ const int tangentIndex = format.getBufferIndexFromID(format.getSemanticID(nvidia::apex::RenderVertexSemantic::BINORMAL));
+ if (tangentIndex != -1 && bitangentIndex != -1)
+ {
+ hasTangents = true;
+ }
+ }
+ if (hasTangents)
+ {
+ mTangents.clear();
+ mTangents.resize(numVertices);
+ mBitangents.clear();
+ mBitangents.resize(numVertices);
+ }
+
+ mBounds = physx::PxBounds3::empty();
+ uint32_t vertexOffset = 0;
+ uint32_t indexOffset = 0;
+ for (uint32_t submeshIndex = 0; submeshIndex < mesh.getSubmeshCount(); submeshIndex++)
+ {
+ const nvidia::VertexBuffer& vb = mesh.getSubmesh(submeshIndex).getVertexBuffer();
+ const nvidia::VertexFormat& vf = vb.getFormat();
+ int bufferIndex = 0;
+ nvidia::RenderDataFormat::Enum format;
+
+ const uint32_t vertexCount = mesh.getSubmesh(submeshIndex).getVertexCount(0);
+
+ {
+ bufferIndex = vf.getBufferIndexFromID(vf.getSemanticID(nvidia::apex::RenderVertexSemantic::POSITION));
+ const physx::PxVec3* positions = (const physx::PxVec3*)vb.getBufferAndFormat(format, (uint32_t)bufferIndex);
+ if (!positions || format != nvidia::RenderDataFormat::FLOAT3)
+ {
+ PX_ALWAYS_ASSERT();
+ return;
+ }
+
+ for (uint32_t i = 0; i < vertexCount; i++)
+ {
+ mVertices[i + vertexOffset] = positions[i];
+ mBounds.include(positions[i]);
+ }
+ }
+
+ {
+ bufferIndex = vf.getBufferIndexFromID(vf.getSemanticID(nvidia::apex::RenderVertexSemantic::NORMAL));
+ const physx::PxVec3* normals = (const physx::PxVec3*)vb.getBufferAndFormat(format, (uint32_t)bufferIndex);
+ if (!normals || format != nvidia::RenderDataFormat::FLOAT3)
+ {
+ PX_ALWAYS_ASSERT();
+ }
+ else
+ {
+ for (uint32_t i = 0; i < vertexCount; i++)
+ {
+ mNormals[i + vertexOffset] = normals[i];
+ }
+ }
+ }
+
+ if (numBonesPerVertex > 0)
+ {
+ bufferIndex = vf.getBufferIndexFromID(vf.getSemanticID(nvidia::apex::RenderVertexSemantic::BONE_WEIGHT));
+ const float* boneWeights = (const float*)vb.getBufferAndFormat(format, (uint32_t)bufferIndex);
+ bufferIndex = vf.getBufferIndexFromID(vf.getSemanticID(nvidia::apex::RenderVertexSemantic::BONE_INDEX));
+ const short* boneIndices = (const short*)vb.getBufferAndFormat(format, (uint32_t)bufferIndex);
+ if (!boneWeights || !boneIndices)
+ {
+ PX_ALWAYS_ASSERT();
+ }
+ else
+ {
+ for (uint32_t i = 0; i < vertexCount; i++)
+ {
+ for (uint32_t j = 0; j < numBonesPerVertex; j++)
+ {
+ mBoneWeights[i + vertexOffset][j] = boneWeights[i * numBonesPerVertex + j];
+ mBoneIndicesExternal[(i + vertexOffset) * 4 + j] = (unsigned short)boneIndices[i * numBonesPerVertex + j];
+
+ mMaxBoneIndexExternal = std::max<int>(mMaxBoneIndexExternal, boneIndices[i * numBonesPerVertex + j]);
+ }
+ }
+ }
+ }
+
+ if (hasTexCoords)
+ {
+ bufferIndex = vf.getBufferIndexFromID(vf.getSemanticID(nvidia::apex::RenderVertexSemantic::TEXCOORD0));
+ const nvidia::VertexUV* texCoords = (const nvidia::VertexUV*)vb.getBufferAndFormat(format, (uint32_t)bufferIndex);
+ if (texCoords == NULL || format != nvidia::apex::RenderDataFormat::FLOAT2)
+ {
+ PX_ALWAYS_ASSERT();
+ }
+ else
+ {
+ for (uint32_t i = 0; i < vertexCount; i++)
+ {
+ mTexCoords[0][i + vertexOffset] = texCoords[i];
+ }
+ }
+ }
+
+ if (hasTangents)
+ {
+ bufferIndex = vf.getBufferIndexFromID(vf.getSemanticID(nvidia::apex::RenderVertexSemantic::TANGENT));
+ const physx::PxVec3* tangents = (const physx::PxVec3*)vb.getBufferAndFormat(format, (uint32_t)bufferIndex);
+ if (tangents == NULL || format != nvidia::apex::RenderDataFormat::FLOAT3)
+ {
+ PX_ALWAYS_ASSERT();
+ }
+ else
+ {
+ for (uint32_t i = 0; i < vertexCount; i++)
+ {
+ mTangents[i + vertexOffset] = tangents[i];
+ }
+ }
+
+ bufferIndex = vf.getBufferIndexFromID(vf.getSemanticID(nvidia::apex::RenderVertexSemantic::BINORMAL));
+ const physx::PxVec3* bitangents = (const physx::PxVec3*)vb.getBufferAndFormat(format, (uint32_t)bufferIndex);
+ if (bitangents == NULL || format != nvidia::apex::RenderDataFormat::FLOAT3)
+ {
+ PX_ALWAYS_ASSERT();
+ }
+ else
+ {
+ for (uint32_t i = 0; i < vertexCount; i++)
+ {
+ mBitangents[i + vertexOffset] = bitangents[i];
+ }
+ }
+ }
+
+ bufferIndex = vf.getBufferIndexFromID(vf.getID("MAX_DISTANCE"));
+ if (bufferIndex != -1)
+ {
+ const float* maxDistances = (const float*)vb.getBufferAndFormat(format, (uint32_t)bufferIndex);
+
+ const float scale = 1.0f / (mBounds.maximum - mBounds.minimum).magnitude();
+
+ if (maxDistances != NULL && format == nvidia::RenderDataFormat::FLOAT1)
+ {
+ if (mPaintChannels[PC_MAX_DISTANCE].size() != mVertices.size())
+ {
+ mPaintChannels[PC_MAX_DISTANCE].resize(mVertices.size());
+ }
+
+ for (size_t i = 0; i < vertexCount; i++)
+ {
+ mPaintChannels[PC_MAX_DISTANCE][i + vertexOffset].paintValueF32 = maxDistances[i] * scale;
+ }
+ }
+ }
+
+ bufferIndex = vf.getBufferIndexFromID(vf.getID("USED_FOR_PHYSICS"));
+ if (bufferIndex != -1)
+ {
+ const unsigned char* usedForPhysics = (const unsigned char*)vb.getBufferAndFormat(format, (uint32_t)bufferIndex);
+
+ if (usedForPhysics != NULL && format == nvidia::apex::RenderDataFormat::UBYTE1)
+ {
+ if (mPaintChannels[PC_LATCH_TO_NEAREST_SLAVE].size() != mVertices.size())
+ {
+ mPaintChannels[PC_LATCH_TO_NEAREST_SLAVE].resize(mVertices.size());
+ }
+
+ for (size_t i = 0; i < vertexCount; i++)
+ {
+ mPaintChannels[PC_LATCH_TO_NEAREST_SLAVE][i + vertexOffset].paintValueU32 = usedForPhysics[i] > 0 ? 0 : 0xffffffff;
+ }
+ }
+ }
+
+ const uint32_t indexCount = mesh.getSubmesh(submeshIndex).getIndexCount(0);
+ const uint32_t* indices = mesh.getSubmesh(submeshIndex).getIndexBuffer(0);
+ for (uint32_t i = 0; i < indexCount; i++)
+ {
+ mIndices[i + indexOffset] = indices[i] + vertexOffset;
+ }
+
+ {
+ mSubMeshes[submeshIndex].init();
+ char buf[64];
+ physx::shdfnd::snprintf(buf, 64, "Submesh %d", submeshIndex);
+ mSubMeshes[submeshIndex].name = buf;
+ mSubMeshes[submeshIndex].materialName = mesh.getMaterialName(submeshIndex);
+
+ mSubMeshes[submeshIndex].firstIndex = indexOffset;
+ mSubMeshes[submeshIndex].numIndices = indexCount;
+ }
+
+ vertexOffset += mesh.getSubmesh(submeshIndex).getVertexCount(0);
+ indexOffset += indexCount;
+ }
+
+ complete(initCustomChannels);
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::applyMorphDisplacements(const std::vector<physx::PxVec3>& displacements)
+{
+ if (mVertices.empty() && mParent != NULL)
+ {
+ if (mParent->mVertices.size() == displacements.size())
+ {
+ mVertices.resize(displacements.size());
+
+ for (size_t i = 0; i < mVertices.size(); i++)
+ {
+ mVertices[i] = mParent->mVertices[i] + displacements[i];
+ }
+
+ mUseGpuSkinning = false;
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::drawPainting(PaintChannelType channelType, bool skinned, nvidia::apex::RenderDebugInterface* batcher)
+{
+ if (batcher == NULL || channelType == PC_NUM_CHANNELS)
+ {
+ return;
+ }
+
+ std::vector<physx::PxVec3>& vertices = skinned ? mSkinnedVertices : (mParent == NULL ? mVertices : mParent->mVertices);
+ std::vector<uint32_t>& indices = mParent == NULL ? mIndices : mParent->mIndices;
+
+ physx::PxBounds3 bounds;
+ getBounds(bounds);
+ updateSubmeshInfo();
+
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ if (mSubMeshes[i].selected)
+ {
+ const uint32_t lastIndex = mSubMeshes[i].firstIndex + mSubMeshes[i].numIndices;
+ for (uint32_t j = mSubMeshes[i].firstIndex; j < lastIndex; j += 3)
+ {
+ uint32_t colors[3] =
+ {
+ mPaintChannels[channelType][indices[j + 0]].color,
+ mPaintChannels[channelType][indices[j + 1]].color,
+ mPaintChannels[channelType][indices[j + 2]].color,
+ };
+ RENDER_DEBUG_IFACE(batcher)->debugGradientTri(vertices[indices[j + 0]], vertices[indices[j + 1]], vertices[indices[j + 2]], colors[0], colors[1], colors[2]);
+ }
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::drawVertices(PaintChannelType channelType, float maxDistanceScaling, float collisionDistanceScaling, float pointScaling,
+ float vmin, float vmax, nvidia::apex::RenderDebugInterface* batcher)
+{
+ if (batcher == NULL || channelType == PC_NUM_CHANNELS)
+ {
+ return;
+ }
+
+ std::vector<physx::PxVec3>& vertices = mParent == NULL ? mVertices : mParent->mVertices;
+ std::vector<physx::PxVec3>& normals = mParent == NULL ? mNormals : mParent->mNormals;
+
+ physx::PxBounds3 bounds;
+ getBounds(bounds);
+
+ updateSubmeshInfo();
+
+ const PaintedVertex* channel = channelType != PC_NUM_CHANNELS ? &mPaintChannels[channelType][0] : NULL;
+ PX_ASSERT(channelType >= 0);
+ PX_ASSERT(channelType <= PC_NUM_CHANNELS);
+
+ const bool needsNoLines = channelType == PC_LATCH_TO_NEAREST_SLAVE || channelType == PC_LATCH_TO_NEAREST_MASTER;
+
+ for (size_t i = 0; i < vertices.size(); i++)
+ {
+ if (!mActiveSubmeshVertices[i])
+ {
+ continue;
+ }
+
+ unsigned int color;
+ color = channel != NULL ? channel[i].color : 0;
+
+ // swap red and blue
+ RENDER_DEBUG_IFACE(batcher)->setCurrentColor(color);
+ RENDER_DEBUG_IFACE(batcher)->debugPoint(vertices[i], pointScaling);
+
+ if (needsNoLines)
+ {
+ continue;
+ }
+
+ if (channel[i].paintValueF32 < vmin || channel[i].paintValueF32 > vmax)
+ {
+ continue;
+ }
+
+ const float channelPaintValue = channel[i].paintValueF32;
+ float len = (channel != NULL ? channelPaintValue : 1.0f) * maxDistanceScaling;
+ if (channelType == PC_MAX_DISTANCE && len < 0.0f)
+ {
+ len = 0.0f;
+ }
+ else if (channelType == PC_COLLISION_DISTANCE)
+ {
+ float scale = collisionDistanceScaling;
+ if (scale == 0.0f)
+ {
+ scale = mPaintChannels[PC_MAX_DISTANCE][i].paintValueF32 * maxDistanceScaling;
+ }
+ PX_ASSERT(channelPaintValue >= vmin && channelPaintValue <= vmax);
+ len = channelPaintValue * scale;
+
+ // positive collisionDistance means moving inwards against the mesh normal
+ len *= -1;
+ }
+ if (len != 0.0f)
+ {
+ physx::PxVec3 target = vertices[i] + normals[i] * len;
+ RENDER_DEBUG_IFACE(batcher)->debugLine(vertices[i], target);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::drawVertices(size_t boneNr, float minWeight, float pointScaling, nvidia::apex::RenderDebugInterface* batcher) const
+{
+ if ((mBoneIndicesExternal.size() != mVertices.size() * 4) || batcher == NULL)
+ {
+ return;
+ }
+
+ const size_t numVertices = mVertices.size();
+ for (size_t i = 0; i < numVertices; i++)
+ {
+ bool found = false;
+ float weight = 0;
+ for (int j = 0; j < 4; j++)
+ {
+ weight = mBoneWeights[i][j];
+ if (mBoneIndicesExternal[i * 4 + j] == boneNr && weight > minWeight)
+ {
+ found = true;
+ }
+ }
+ if (found)
+ {
+ PX_ASSERT(weight >= 0.0f);
+ PX_ASSERT(weight <= 1.0f);
+
+ uint8_t gray = (uint8_t)(255 * weight);
+ RENDER_DEBUG_IFACE(batcher)->setCurrentColor(uint32_t(gray << 16 | gray << 8 | gray));
+ RENDER_DEBUG_IFACE(batcher)->debugPoint(mVertices[i], pointScaling);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::drawNormals(float normalScale, bool activeVerticesOnly, nvidia::apex::RenderDebugInterface* batcher) const
+{
+ if ((normalScale <= 0.0f) || batcher == NULL)
+ {
+ return;
+ }
+
+
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ if (activeVerticesOnly && !mActiveSubmeshVertices[i])
+ {
+ continue;
+ }
+
+ RENDER_DEBUG_IFACE(batcher)->setCurrentColor(0x00ff0000); // red
+ physx::PxVec3 otherEnd = mVertices[i];
+ otherEnd += mNormals[i] * normalScale;
+ RENDER_DEBUG_IFACE(batcher)->debugLine(mVertices[i], otherEnd);
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::drawTangents(float tangentScale, nvidia::apex::RenderDebugInterface* batcher) const
+{
+ if (tangentScale <= 0.0f || batcher == NULL || mTangents.size() != mVertices.size() || mBitangents.size() != mVertices.size())
+ {
+ return;
+ }
+
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ RENDER_DEBUG_IFACE(batcher)->setCurrentColor(0x0000ff00); // green
+ physx::PxVec3 otherEnd = mVertices[i] + mTangents[i] * tangentScale;
+ RENDER_DEBUG_IFACE(batcher)->debugLine(mVertices[i], otherEnd);
+
+ RENDER_DEBUG_IFACE(batcher)->setCurrentColor(0x000000ff); // blue
+ otherEnd = mVertices[i] + mBitangents[i] * tangentScale;
+ RENDER_DEBUG_IFACE(batcher)->debugLine(mVertices[i], otherEnd);
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::drawMaxDistancePartitions(float paintingScale, const float* partitions, size_t numPartitions, nvidia::apex::RenderDebugInterface* batcher)
+{
+ if (batcher == NULL)
+ {
+ return;
+ }
+
+ hasRandomColors(numPartitions);
+
+ RENDER_DEBUG_IFACE(batcher)->pushRenderState();
+ RENDER_DEBUG_IFACE(batcher)->addToCurrentState(RENDER_DEBUG::DebugRenderState::SolidShaded);
+
+ for (uint32_t s = 0; s < mSubMeshes.size(); s++)
+ {
+ if (!mSubMeshes[s].selected)
+ {
+ continue;
+ }
+
+ const uint32_t start = mSubMeshes[s].firstIndex;
+ const uint32_t end = start + mSubMeshes[s].numIndices;
+
+ using RENDER_DEBUG::DebugColors;
+ const uint32_t colorDarkGray = RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::DarkGray);
+ const uint32_t colorWhite = RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::White);
+
+ for (uint32_t i = start; i < end; i += 3)
+ {
+ float maxMaxDistance = mPaintChannels[PC_MAX_DISTANCE][mIndices[i]].paintValueF32;
+ maxMaxDistance = physx::PxMax(maxMaxDistance, mPaintChannels[PC_MAX_DISTANCE][mIndices[i + 1]].paintValueF32);
+ maxMaxDistance = physx::PxMax(maxMaxDistance, mPaintChannels[PC_MAX_DISTANCE][mIndices[i + 2]].paintValueF32);
+ maxMaxDistance *= paintingScale;
+
+ uint32_t color = colorDarkGray;
+
+ if (maxMaxDistance >= 0)
+ {
+ color = colorWhite;
+ for (uint32_t j = 0; j < numPartitions; j++)
+ {
+ if (maxMaxDistance < partitions[j])
+ {
+ color = mRandomColors[j];
+ break;
+ }
+ }
+ }
+
+ RENDER_DEBUG_IFACE(batcher)->setCurrentColor(color);
+
+ physx::PxVec3 normal = (mVertices[mIndices[i + 2]] - mVertices[mIndices[i]]).cross(mVertices[mIndices[i + 1]] - mVertices[mIndices[i]]);
+ normal.normalize();
+ RENDER_DEBUG_IFACE(batcher)->debugTriNormals(
+ mVertices[mIndices[i + 0]], mVertices[mIndices[i + 2]], mVertices[mIndices[i + 1]],
+ normal, normal, normal);
+
+ normal = -normal;
+
+ RENDER_DEBUG_IFACE(batcher)->debugTriNormals(
+ mVertices[mIndices[i + 0]], mVertices[mIndices[i + 1]], mVertices[mIndices[i + 2]],
+ normal, normal, normal);
+ }
+ }
+
+ RENDER_DEBUG_IFACE(batcher)->popRenderState();
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::drawTetrahedrons(bool wireframe, float scale, nvidia::apex::RenderDebugInterface* batcher)
+{
+ if (RENDER_DEBUG_IFACE(batcher) == NULL)
+ {
+ return;
+ }
+
+ const uint32_t numIndices = (uint32_t)(mIndices.size());
+ if (numIndices == 0)
+ {
+ return;
+ }
+
+ PX_ASSERT(numIndices % 4 == 0);
+
+ PX_ASSERT(scale < 1.0f);
+ PX_ASSERT(scale >= 0.0f);
+
+ if (scale > 1.0f)
+ {
+ scale = 1.0f;
+ }
+
+ const physx::PxVec3* positions = &mVertices[0];
+
+ RENDER_DEBUG_IFACE(batcher)->pushRenderState();
+ RENDER_DEBUG_IFACE(batcher)->setCurrentColor(RENDER_DEBUG_IFACE(batcher)->getDebugColor(RENDER_DEBUG::DebugColors::DarkGreen));
+ RENDER_DEBUG_IFACE(batcher)->addToCurrentState(RENDER_DEBUG::DebugRenderState::SolidShaded);
+
+ const uint32_t edgeIndices[6][2] = {{0, 1}, {0, 2}, {0, 3}, {1, 2}, {1, 3}, {2, 3}};
+ const uint32_t sides[4][3] = {{2, 1, 0}, {0, 1, 3}, {1, 2, 3}, {2, 0, 3}};
+
+ for (uint32_t i = 0; i < numIndices; i += 4)
+ {
+ physx::PxVec3 vecs[4];
+ physx::PxVec3 center(0.0f);
+ for (uint32_t j = 0; j < 4; j++)
+ {
+ vecs[j] = positions[mIndices[i + j]];
+ center += vecs[j];
+ }
+ center *= 0.25f;
+
+ for (uint32_t j = 0; j < 4; j++)
+ {
+ vecs[j] = vecs[j] * scale + center * (1.0f - scale);
+ }
+
+ if (wireframe)
+ {
+ for (uint32_t j = 0; j < 6; j++)
+ {
+ RENDER_DEBUG_IFACE(batcher)->debugLine(vecs[edgeIndices[j][0]], vecs[edgeIndices[j][1]]);
+ }
+ }
+ else
+ {
+ for (uint32_t j = 0; j < 4; j++)
+ {
+ RENDER_DEBUG_IFACE(batcher)->debugTri(vecs[sides[j][0]], vecs[sides[j][1]], vecs[sides[j][2]]);
+ }
+ }
+ }
+
+ RENDER_DEBUG_IFACE(batcher)->popRenderState();
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::updateRenderResources(bool rewriteBuffers, nvidia::apex::UserRenderResourceManager& rrm, void* userRenderData)
+{
+ updateRenderResourcesInternal(rewriteBuffers, rrm, userRenderData, true);
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::updateRenderResourcesInternal(bool rewriteBuffers, nvidia::apex::UserRenderResourceManager& rrm, void* userRenderData, bool createResource)
+{
+ if (mParent != NULL && mUseGpuSkinning)
+ {
+ mParent->updateRenderResourcesInternal(rewriteBuffers, rrm, userRenderData, false);
+ }
+
+ const uint32_t numIndices = mParent != NULL ? (uint32_t)(mParent->mIndices.size()) : (uint32_t)(mIndices.size());
+ const uint32_t numVertices = mParent != NULL ? (uint32_t)(mParent->mVertices.size()) : (uint32_t)(mVertices.size());
+
+ bool resourceDirty = false;
+
+ bool boneBufferSwitch = false;
+ if (skinningMatricesChanged && mUseGpuSkinning)
+ {
+ if (mSkinningMatrices.empty() && mBoneBuffer != NULL)
+ {
+ rrm.releaseBoneBuffer(*mBoneBuffer);
+ mBoneBuffer = NULL;
+ boneBufferSwitch = true;
+ }
+ if (!mSkinningMatrices.empty() && mBoneBuffer == NULL && mUseGpuSkinning)
+ {
+ nvidia::apex::UserRenderBoneBufferDesc bufferDesc;
+ bufferDesc.buffersRequest[nvidia::RenderBoneSemantic::POSE] = nvidia::RenderDataFormat::FLOAT4x4;
+ bufferDesc.maxBones = (uint32_t)(mSkinningMatrices.size());
+ mBoneBuffer = rrm.createBoneBuffer(bufferDesc);
+ boneBufferSwitch = true;
+ }
+
+ if (!mSkinningMatrices.empty() && mBoneBuffer != NULL)
+ {
+ nvidia::apex::RenderBoneBufferData boneWriteData;
+ boneWriteData.setSemanticData(nvidia::RenderBoneSemantic::POSE, &mSkinningMatrices[0], sizeof(physx::PxMat44), nvidia::RenderDataFormat::FLOAT4x4);
+ mBoneBuffer->writeBuffer(boneWriteData, 0, (uint32_t)mSkinningMatrices.size());
+ }
+
+ skinningMatricesChanged = false;
+ }
+
+ if (numVertices == 0 || vertexCountChanged || textureUvOriginChanged)
+ {
+ if (mDynamicVertexBuffer != NULL)
+ {
+ rrm.releaseVertexBuffer(*mDynamicVertexBuffer);
+ vertexValuesChangedDynamic = true;
+ }
+ if (mStaticVertexBuffer != NULL)
+ {
+ rrm.releaseVertexBuffer(*mStaticVertexBuffer);
+ vertexValuesChangedStatic = true;
+ }
+ mDynamicVertexBuffer = mStaticVertexBuffer = NULL;
+ textureUvOriginChanged = false;
+ resourceDirty = true;
+ }
+
+ if (mDynamicVertexBuffer == NULL && numVertices > 0)
+ {
+ nvidia::apex::UserRenderVertexBufferDesc bufferDesc;
+ bufferDesc.uvOrigin = mTextureUVOrigin;
+ bufferDesc.hint = nvidia::apex::RenderBufferHint::DYNAMIC;
+
+ bufferDesc.maxVerts = mParent != NULL ? (uint32_t)(mParent->mVertices.size()) : (uint32_t)(mVertices.size());
+
+ bufferDesc.buffersRequest[nvidia::apex::RenderVertexSemantic::POSITION] = nvidia::apex::RenderDataFormat::FLOAT3;
+ bufferDesc.buffersRequest[nvidia::apex::RenderVertexSemantic::NORMAL] = nvidia::apex::RenderDataFormat::FLOAT3;
+
+ if (mParent == NULL || !mUseGpuSkinning)
+ {
+ mDynamicVertexBuffer = rrm.createVertexBuffer(bufferDesc);
+ vertexValuesChangedDynamic = true;
+ }
+ }
+
+ if (mStaticVertexBuffer == NULL && numVertices > 0)
+ {
+ nvidia::apex::UserRenderVertexBufferDesc bufferDesc;
+ bufferDesc.uvOrigin = mTextureUVOrigin;
+ bufferDesc.hint = nvidia::apex::RenderBufferHint::STATIC;
+
+ bufferDesc.maxVerts = mParent != NULL ? (uint32_t)(mParent->mVertices.size()) : (uint32_t)(mVertices.size());
+
+ unsigned int numSemantics = 0;
+
+ if (mParent == NULL || !mUseGpuSkinning)
+ {
+ for (uint32_t i = 0; i < NUM_TEXCOORDS; i++)
+ {
+ const uint32_t numTexCoords = mParent != NULL ? (uint32_t)(mParent->mTexCoords[i].size()) : (uint32_t)mTexCoords[i].size();
+ if (numTexCoords == numVertices)
+ {
+ bufferDesc.buffersRequest[nvidia::apex::RenderVertexSemantic::TEXCOORD0 + i] = nvidia::apex::RenderDataFormat::FLOAT2;
+ numSemantics++;
+ }
+ }
+
+ const uint32_t numBoneIndices = mParent != NULL ? (uint32_t)(mParent->mBoneIndicesInternal.size()) : (uint32_t)(mBoneIndicesInternal.size());
+ if (numBoneIndices == numVertices * 4 && mUseGpuSkinning)
+ {
+ PX_ASSERT(mParent == NULL);
+ bufferDesc.buffersRequest[nvidia::apex::RenderVertexSemantic::BONE_INDEX] = nvidia::apex::RenderDataFormat::USHORT4;
+ bufferDesc.buffersRequest[nvidia::apex::RenderVertexSemantic::BONE_WEIGHT] = nvidia::apex::RenderDataFormat::FLOAT4;
+ numSemantics += 2;
+ }
+ }
+ else
+ {
+ PX_ASSERT(mParent->mStaticVertexBuffer != NULL);
+ }
+
+ if ((mParent == NULL || !mUseGpuSkinning) && numSemantics > 0)
+ {
+ mStaticVertexBuffer = rrm.createVertexBuffer(bufferDesc);
+ vertexValuesChangedStatic = true;
+ }
+ }
+
+
+ if (mDynamicVertexBuffer && (vertexValuesChangedDynamic || rewriteBuffers))
+ {
+ nvidia::apex::RenderVertexBufferData writeData;
+
+ const uint32_t numVertices = mParent != NULL ? (uint32_t)(mParent->mVertices.size()) : (uint32_t)(mVertices.size());
+ if (mSkinnedVertices.size() == numVertices && !mUseGpuSkinning)
+ {
+ PX_ASSERT(mSkinnedNormals.size() == mSkinnedVertices.size());
+ writeData.setSemanticData(nvidia::apex::RenderVertexSemantic::POSITION, &mSkinnedVertices[0], sizeof(physx::PxVec3), nvidia::apex::RenderDataFormat::FLOAT3);
+ writeData.setSemanticData(nvidia::apex::RenderVertexSemantic::NORMAL, &mSkinnedNormals[0], sizeof(physx::PxVec3), nvidia::apex::RenderDataFormat::FLOAT3);
+ }
+ else
+ {
+ std::vector<physx::PxVec3>& vertices = mParent != NULL ? mParent->mVertices : mVertices;
+ std::vector<physx::PxVec3>& normals = mParent != NULL ? mParent->mNormals : mNormals;
+ PX_ASSERT(vertices.size() == numVertices);
+ PX_ASSERT(normals.size() == numVertices);
+ writeData.setSemanticData(nvidia::apex::RenderVertexSemantic::POSITION, &vertices[0], sizeof(physx::PxVec3), nvidia::apex::RenderDataFormat::FLOAT3);
+ writeData.setSemanticData(nvidia::apex::RenderVertexSemantic::NORMAL, &normals[0], sizeof(physx::PxVec3), nvidia::apex::RenderDataFormat::FLOAT3);
+ }
+ mDynamicVertexBuffer->writeBuffer(writeData, 0, numVertices);
+ vertexValuesChangedDynamic = false;
+ }
+
+ if (mStaticVertexBuffer != NULL && vertexValuesChangedStatic)
+ {
+ nvidia::apex::RenderVertexBufferData writeData;
+
+ if (mParent == NULL || !mUseGpuSkinning)
+ {
+ nvidia::apex::RenderVertexSemantic::Enum semantics[4] =
+ {
+ nvidia::apex::RenderVertexSemantic::TEXCOORD0,
+ nvidia::apex::RenderVertexSemantic::TEXCOORD1,
+ nvidia::apex::RenderVertexSemantic::TEXCOORD2,
+ nvidia::apex::RenderVertexSemantic::TEXCOORD3,
+ };
+ for (uint32_t i = 0; i < NUM_TEXCOORDS; i++)
+ {
+ const uint32_t numTexCoords = mParent != NULL ? (uint32_t)(mParent->mTexCoords[i].size()) : (uint32_t)(mTexCoords[i].size());
+ if (numTexCoords == numVertices)
+ {
+ writeData.setSemanticData(semantics[i], mParent != NULL ? &mParent->mTexCoords[i][0] : &mTexCoords[i][0], sizeof(nvidia::apex::VertexUV), nvidia::apex::RenderDataFormat::FLOAT2);
+ }
+ }
+
+ const uint32_t numBoneIndices = mParent != NULL ? (uint32_t)(mParent->mBoneIndicesInternal.size()) : (uint32_t)(mBoneIndicesInternal.size());
+ if (numBoneIndices == numVertices * 4 && mUseGpuSkinning)
+ {
+ PX_ASSERT(mParent == NULL);
+ writeData.setSemanticData(nvidia::apex::RenderVertexSemantic::BONE_INDEX, &mBoneIndicesInternal[0], sizeof(uint16_t) * 4, nvidia::apex::RenderDataFormat::USHORT4);
+ writeData.setSemanticData(nvidia::apex::RenderVertexSemantic::BONE_WEIGHT, &mBoneWeights[0], sizeof(physx::PxVec4), nvidia::apex::RenderDataFormat::FLOAT4);
+ }
+ }
+ mStaticVertexBuffer->writeBuffer(writeData, 0, numVertices);
+ vertexValuesChangedStatic = false;
+ }
+
+ if ( (mIndexBuffer != NULL && numIndices == 0) || vertexCountChanged)
+ {
+ rrm.releaseIndexBuffer(*mIndexBuffer);
+ mIndexBuffer = NULL;
+ indicesChanged |= vertexCountChanged;
+ }
+
+ if (mIndexBuffer == NULL && (mParent == NULL || !mUseGpuSkinning) && numIndices > 0)
+ {
+ nvidia::apex::UserRenderIndexBufferDesc bufferDesc;
+ bufferDesc.format = nvidia::apex::RenderDataFormat::UINT1;
+ bufferDesc.maxIndices = numIndices;
+
+ mIndexBuffer = rrm.createIndexBuffer(bufferDesc);
+ indicesChanged = mIndexBuffer != NULL;
+ }
+
+ if (mIndexBuffer != NULL && indicesChanged)
+ {
+ mIndexBuffer->writeBuffer(mParent != NULL ? &mParent->mIndices[0] : &mIndices[0], sizeof(uint32_t), 0, mParent != NULL ? (uint32_t)(mParent->mIndices.size()) : (uint32_t)(mIndices.size()));
+ indicesChanged = false;
+ }
+
+ if (boneBufferSwitch || numIndices == 0 || numVertices == 0 || vertexCountChanged || oneCullModeChanged)
+ {
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ if (mSubMeshes[i].mRenderResource != NULL)
+ {
+ rrm.releaseResource(*mSubMeshes[i].mRenderResource);
+ mSubMeshes[i].mRenderResource = NULL;
+ }
+ }
+ vertexCountChanged = false;
+ oneCullModeChanged = false;
+ }
+
+
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ bool show = mSubMeshes[i].show;
+ if (mParent != NULL)
+ {
+ PX_ASSERT(mParent->mSubMeshes.size() == mSubMeshes.size());
+ show &= mParent->mSubMeshes[i].show;
+ }
+
+ if (mSubMeshes[i].mRenderResource != NULL && (!show || mSubMeshes[i].resourceNeedsUpdate || !createResource || resourceDirty))
+ {
+ rrm.releaseResource(*mSubMeshes[i].mRenderResource);
+ mSubMeshes[i].mRenderResource = NULL;
+ mSubMeshes[i].resourceNeedsUpdate = false;
+ }
+
+ if (mSubMeshes[i].mRenderResource == NULL && show && numIndices > 0 && numVertices > 0 && createResource)
+ {
+ nvidia::apex::UserRenderVertexBuffer* vertexBuffers[2] = { NULL , NULL };
+ uint32_t numVertexBuffers = 0;
+
+ nvidia::apex::UserRenderResourceDesc resourceDesc;
+ if (mParent != NULL && mParent->mStaticVertexBuffer != NULL)
+ {
+ vertexBuffers[numVertexBuffers++] = mParent->mStaticVertexBuffer;
+ }
+ else if (mStaticVertexBuffer)
+ {
+ vertexBuffers[numVertexBuffers++] = mStaticVertexBuffer;
+ }
+
+ if (mUseGpuSkinning && mParent != NULL && mParent->mDynamicVertexBuffer != NULL)
+ {
+ vertexBuffers[numVertexBuffers++] = mParent->mDynamicVertexBuffer;
+ }
+ else
+ {
+ PX_ASSERT(mDynamicVertexBuffer != NULL);
+ vertexBuffers[numVertexBuffers++] = mDynamicVertexBuffer;
+ }
+
+
+ PX_ASSERT(numVertexBuffers > 0);
+
+ resourceDesc.vertexBuffers = vertexBuffers;
+ resourceDesc.numVertexBuffers = numVertexBuffers;
+
+ resourceDesc.indexBuffer = (mParent != NULL && mParent->mIndexBuffer != NULL) ? mParent->mIndexBuffer : mIndexBuffer;
+ PX_ASSERT(resourceDesc.indexBuffer != NULL);
+ resourceDesc.boneBuffer = mBoneBuffer;
+
+ resourceDesc.firstVertex = 0;
+ resourceDesc.numVerts = mParent != NULL ? (uint32_t)(mParent->mVertices.size()) : (uint32_t)(mVertices.size());
+
+ resourceDesc.firstIndex = mSubMeshes[i].firstIndex;
+ resourceDesc.numIndices = mSubMeshes[i].numIndices;
+
+ resourceDesc.firstBone = 0;
+ resourceDesc.numBones = mBoneBuffer != NULL ? (uint32_t)(mSkinningMatrices.size()) : 0;
+
+ resourceDesc.primitives = nvidia::apex::RenderPrimitiveType::TRIANGLES;
+
+ resourceDesc.cullMode = mSubMeshes[i].cullMode;
+
+ resourceDesc.material = mSubMeshes[i].materialResource;
+
+ resourceDesc.userRenderData = userRenderData;
+
+ mSubMeshes[i].mRenderResource = rrm.createResource(resourceDesc);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::dispatchRenderResources(nvidia::UserRenderer& r, const physx::PxMat44& currentPose)
+{
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ if (mSubMeshes[i].mRenderResource != NULL)
+ {
+ nvidia::RenderContext context;
+ context.local2world = currentPose;
+ context.renderResource = mSubMeshes[i].mRenderResource;
+
+ r.renderResource(context);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::updateRenderer(bool rewriteBuffers, bool overrideMaterial, bool sharedOnly /* = false */)
+{
+ PX_UNUSED(rewriteBuffers);
+ PX_UNUSED(overrideMaterial);
+ PX_UNUSED(sharedOnly);
+
+#ifdef USE_SAMPLE_RENDERER
+ if (mParent != NULL)
+ {
+ if (mParent->mRenderer == NULL)
+ {
+ mParent->mRenderer = mRenderer;
+ }
+
+ mParent->updateRenderer(rewriteBuffers, true);
+ }
+
+ if (mRenderer == NULL)
+ {
+ return;
+ }
+
+ const size_t numVertices = mParent != NULL ? mParent->mVertices.size() : mVertices.size();
+ const size_t numIndices = mParent != NULL ? mParent->mIndices.size() : mIndices.size();
+
+ if (numVertices == 0 || numIndices == 0)
+ {
+ return;
+ }
+
+ bool vertexBufferDynamicDirty = vertexValuesChangedDynamic || rewriteBuffers;
+ vertexValuesChangedDynamic = false;
+ if (mRendererVertexBufferDynamic == NULL && (mVertices.size() > 0 || mSkinnedVertices.size() > 0))
+ {
+ //PH: Static or dynamic, depending on whether you're in the parent or not.
+ // The child might have mVertices.size() == 0 when it can do gpu skinning, hence no buffer needed
+ using SampleRenderer::RendererVertexBuffer;
+
+ SampleRenderer::RendererVertexBufferDesc desc;
+ desc.hint = mParent == NULL ? RendererVertexBuffer::HINT_STATIC : RendererVertexBuffer::HINT_DYNAMIC;
+ desc.maxVertices = (uint32_t)numVertices;
+ desc.semanticFormats[RendererVertexBuffer::SEMANTIC_POSITION] = RendererVertexBuffer::FORMAT_FLOAT3;
+ desc.semanticFormats[RendererVertexBuffer::SEMANTIC_NORMAL] = RendererVertexBuffer::FORMAT_FLOAT3;
+
+ PX_ASSERT(desc.isValid());
+
+ mRendererVertexBufferDynamic = mRenderer->createVertexBuffer(desc);
+ vertexBufferDynamicDirty = true;
+ }
+
+ if (mRendererVertexBufferDynamic && vertexBufferDynamicDirty)
+ {
+ using SampleRenderer::RendererVertexBuffer;
+
+ physx::PxStrideIterator<physx::PxVec3> positions;
+ {
+ unsigned int stride = 0;
+ void* data = mRendererVertexBufferDynamic->lockSemantic(RendererVertexBuffer::SEMANTIC_POSITION, stride);
+ positions = PxMakeIterator((physx::PxVec3*)data, stride);
+ }
+ physx::PxStrideIterator<physx::PxVec3> normals;
+ {
+ unsigned int stride = 0;
+ void* data = mRendererVertexBufferDynamic->lockSemantic(RendererVertexBuffer::SEMANTIC_NORMAL, stride);
+ normals = PxMakeIterator((physx::PxVec3*)data, stride);
+ }
+
+ std::vector<physx::PxVec3>& realVertices = mParent != NULL ? mParent->mVertices : mVertices;
+ std::vector<physx::PxVec3>& realNormals = mParent != NULL ? mParent->mNormals : mNormals;
+
+ physx::PxVec3* inPositions = mSkinnedVertices.size() == realVertices.size() ? &mSkinnedVertices[0] : &realVertices[0];
+ physx::PxVec3* inNormals = mSkinnedNormals.size() == realNormals.size() ? &mSkinnedNormals[0] : &realNormals[0];
+ for (unsigned int i = 0; i < numVertices; i++)
+ {
+ positions[i] = inPositions[i];
+ normals[i] = inNormals[i];
+ }
+
+ mRendererVertexBufferDynamic->unlockSemantic(RendererVertexBuffer::SEMANTIC_POSITION);
+ mRendererVertexBufferDynamic->unlockSemantic(RendererVertexBuffer::SEMANTIC_NORMAL);
+ }
+
+
+ bool vertexBufferSharedDirty = rewriteBuffers || textureUvOriginChanged;
+ bool hasBoneBuffer = mParent == NULL && mBoneWeights.size() == mVertices.size() && getMaxBoneIndex() < RENDERER_MAX_BONES;
+ const bool hasTexCoords = mParent == NULL && mTexCoords[0].size() == mVertices.size();
+
+ if (mRendererVertexBufferShared == NULL && mParent == NULL && (hasBoneBuffer || hasTexCoords))
+ {
+ // PH: Only done in the parent, hence shared
+ using SampleRenderer::RendererVertexBuffer;
+
+ SampleRenderer::RendererVertexBufferDesc desc;
+ desc.hint = SampleRenderer::RendererVertexBuffer::HINT_STATIC;
+ desc.maxVertices = (uint32_t)numVertices;
+ if (hasBoneBuffer)
+ {
+ desc.semanticFormats[RendererVertexBuffer::SEMANTIC_BONEINDEX] = RendererVertexBuffer::FORMAT_USHORT4;
+ desc.semanticFormats[RendererVertexBuffer::SEMANTIC_BONEWEIGHT] = RendererVertexBuffer::FORMAT_FLOAT4;
+ }
+ if (hasTexCoords)
+ {
+ desc.semanticFormats[RendererVertexBuffer::SEMANTIC_TEXCOORD0] = RendererVertexBuffer::FORMAT_FLOAT2;
+ }
+
+ PX_ASSERT(desc.isValid());
+
+ mRendererVertexBufferShared = mRenderer->createVertexBuffer(desc);
+ vertexBufferSharedDirty = true;
+ }
+
+ if (mRendererVertexBufferShared != NULL && vertexBufferSharedDirty)
+ {
+ using SampleRenderer::RendererVertexBuffer;
+
+ physx::PxStrideIterator<ushort4> boneIndices;
+ if (hasBoneBuffer)
+ {
+ unsigned int stride = 0;
+ void* data = mRendererVertexBufferShared->lockSemantic(RendererVertexBuffer::SEMANTIC_BONEINDEX, stride);
+ boneIndices = physx::PxMakeIterator((ushort4*)data, stride);
+ }
+ physx::PxStrideIterator<physx::PxVec4> boneWeights;
+ if (hasBoneBuffer)
+ {
+ unsigned int stride = 0;
+ void* data = mRendererVertexBufferShared->lockSemantic(RendererVertexBuffer::SEMANTIC_BONEWEIGHT, stride);
+ boneWeights = physx::PxMakeIterator((physx::PxVec4*)data, stride);
+ }
+ physx::PxStrideIterator<physx::PxVec2> texCoords;
+ if (hasTexCoords)
+ {
+ unsigned int stride = 0;
+ void* data = mRendererVertexBufferShared->lockSemantic(RendererVertexBuffer::SEMANTIC_TEXCOORD0, stride);
+ texCoords = physx::PxMakeIterator((physx::PxVec2*)data, stride);
+ }
+
+ ushort4* inBoneIndices = hasBoneBuffer ? (ushort4*)&mBoneIndicesInternal[0] : NULL;
+ physx::PxVec4* inBoneWeights = hasBoneBuffer ? &mBoneWeights[0] : NULL;
+ nvidia::VertexUV* inTexCoords = hasTexCoords ? &mTexCoords[0][0] : NULL;
+ for (unsigned int i = 0; i < numVertices; i++)
+ {
+ if (hasBoneBuffer)
+ {
+ boneIndices[i] = inBoneIndices[i];
+ boneWeights[i] = inBoneWeights[i];
+ }
+ if (hasTexCoords)
+ {
+ // ugh, we switch to bottom left in SampleApexRenderer.cpp
+ switch (mTextureUVOrigin)
+ {
+ case nvidia::apex::TextureUVOrigin::ORIGIN_TOP_LEFT:
+ texCoords[i].x = inTexCoords[i].u;
+ texCoords[i].y = 1.0f - inTexCoords[i].v;
+ break;
+ case nvidia::apex::TextureUVOrigin::ORIGIN_TOP_RIGHT:
+ texCoords[i].x = 1.0f - inTexCoords[i].u;
+ texCoords[i].y = 1.0f - inTexCoords[i].v;
+ break;
+ case nvidia::apex::TextureUVOrigin::ORIGIN_BOTTOM_LEFT:
+ texCoords[i].x = inTexCoords[i].u;
+ texCoords[i].y = inTexCoords[i].v;
+ break;
+ case nvidia::apex::TextureUVOrigin::ORIGIN_BOTTOM_RIGHT:
+ texCoords[i].x = 1.0f - inTexCoords[i].u;
+ texCoords[i].y = inTexCoords[i].v;
+ break;
+ }
+ }
+ }
+
+ if (hasBoneBuffer)
+ {
+ mRendererVertexBufferShared->unlockSemantic(RendererVertexBuffer::SEMANTIC_BONEINDEX);
+ mRendererVertexBufferShared->unlockSemantic(RendererVertexBuffer::SEMANTIC_BONEWEIGHT);
+ }
+ if (hasTexCoords)
+ {
+ mRendererVertexBufferShared->unlockSemantic(RendererVertexBuffer::SEMANTIC_TEXCOORD0);
+ }
+ }
+
+ bool indexBufferDirty = rewriteBuffers;
+ if (mRendererIndexBuffer == NULL && mParent == NULL)
+ {
+ // PH: Only done in the parent
+ using SampleRenderer::RendererIndexBuffer;
+
+ SampleRenderer::RendererIndexBufferDesc desc;
+ desc.hint = RendererIndexBuffer::HINT_STATIC;
+ desc.maxIndices = (uint32_t)numIndices;
+ desc.format = RendererIndexBuffer::FORMAT_UINT32;
+
+ PX_ASSERT(desc.isValid());
+
+ mRendererIndexBuffer = mRenderer->createIndexBuffer(desc);
+ indexBufferDirty = true;
+ }
+
+ if (mRendererIndexBuffer != NULL && indexBufferDirty && numIndices > 0)
+ {
+ unsigned int* indices = (unsigned int*)mRendererIndexBuffer->lock();
+
+ unsigned int* inIndices = mParent != NULL ? &mParent->mIndices[0] : &mIndices[0];
+
+ // PH: Ugh, I need to reverse winding to make the SampleRenderer happy (mainly because of double sided rendering in D3D)
+ for (unsigned int i = 0; i < numIndices; i += 3)
+ {
+ indices[i + 0] = inIndices[i + 2];
+ indices[i + 1] = inIndices[i + 1];
+ indices[i + 2] = inIndices[i + 0];
+ }
+
+ mRendererIndexBuffer->unlock();
+ }
+
+ // only create render meshes and materials for a mesh we actually want to render
+ if (!sharedOnly)
+ {
+// bool hasTextures = false;
+// bool hasBones = mParent != NULL && mParent->mBoneWeights.size() == mParent->mVertices.size() && mParent->getMaxBoneIndex() < RENDERER_MAX_BONES;
+
+ for (size_t submeshIndex = 0; submeshIndex < mSubMeshes.size(); submeshIndex++)
+ {
+ TriangleSubMesh& sm = mSubMeshes[submeshIndex];
+ bool show = sm.show;
+ if (mParent != NULL)
+ {
+ PX_ASSERT(mParent->mSubMeshes.size() == mSubMeshes.size());
+ show &= mParent->mSubMeshes[submeshIndex].show;
+ }
+
+ if (sm.mRendererMesh == NULL && show)
+ {
+ using SampleRenderer::RendererMesh;
+ using SampleRenderer::RendererVertexBuffer;
+
+ RendererVertexBuffer* vertexBuffers[2];
+ vertexBuffers[0] = mRendererVertexBufferDynamic;
+ if (vertexBuffers[0] == NULL && mParent != NULL)
+ {
+ vertexBuffers[0] = mParent->mRendererVertexBufferDynamic;
+ }
+
+ PX_ASSERT(vertexBuffers[0] != NULL);
+
+ vertexBuffers[1] = mParent != NULL ? mParent->mRendererVertexBufferShared : mRendererVertexBufferShared;
+
+ SampleRenderer::RendererMeshDesc desc;
+ desc.vertexBuffers = vertexBuffers;
+ desc.numVertexBuffers = vertexBuffers[1] != NULL ? 2 : 1;
+ desc.numVertices = (uint32_t)numVertices;
+
+ desc.indexBuffer = mRendererIndexBuffer;
+ if (desc.indexBuffer == NULL && mParent != NULL)
+ {
+ desc.indexBuffer = mParent->mRendererIndexBuffer;
+ }
+ PX_ASSERT(desc.indexBuffer != NULL);
+
+ desc.firstIndex = sm.firstIndex;
+ desc.numIndices = sm.numIndices;
+
+ desc.primitives = RendererMesh::PRIMITIVE_TRIANGLES;
+
+ PX_ASSERT(desc.isValid());
+ sm.mRendererMesh = mRenderer->createMesh(desc);
+ }
+ else if (sm.mRendererMesh != NULL && !show)
+ {
+ sm.mRendererMesh->release();
+ sm.mRendererMesh = NULL;
+ }
+
+ if (sm.mRendererMeshContext == NULL && show)
+ {
+ sm.mRendererMeshContext = new SampleRenderer::RendererMeshContext;
+ }
+
+ SampleFramework::SampleMaterialAsset* materialAsset = mOverrideMaterial;
+ if (sm.mSampleMaterial != NULL && !overrideMaterial)
+ {
+ materialAsset = sm.mSampleMaterial;
+ }
+
+ PX_ASSERT(materialAsset != NULL);
+
+ for (unsigned int i = 0; i < materialAsset->getNumVertexShaders(); i++)
+ {
+ if (mUseGpuSkinning == (materialAsset->getMaxBones(i) > 0))
+ {
+ sm.setMaterialReference(materialAsset->getMaterial(i), materialAsset->getMaterialInstance(i));
+ break;
+ }
+ }
+ PX_ASSERT(sm.mRendererMaterialReference != NULL);
+
+ if (sm.mRendererMaterialInstance != NULL)
+ {
+ using SampleRenderer::RendererMaterial;
+
+ const RendererMaterial::Variable* v = sm.mRendererMaterialInstance->findVariable("diffuseColor", RendererMaterial::VARIABLE_FLOAT4);
+ if (v != NULL)
+ {
+ if (sm.color != 0x00808080) // check for lightgray, the default color. in this case leave the diffuseColor alone
+ {
+ const unsigned int color = sm.color;
+
+ const float red = ((color >> 16) & 0xff) / 255.f;
+ const float green = ((color >> 8) & 0xff) / 255.f;
+ const float blue = ((color >> 0) & 0xff) / 255.f;
+
+ float diffuseColor[4] = { red, green, blue, 1.0f };
+ sm.mRendererMaterialInstance->writeData(*v, diffuseColor);
+ }
+ }
+ }
+ }
+ }
+#endif // USE_SAMPLE_RENDERER
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::queueForRendering(const physx::PxMat44& currentPose, bool wireframe)
+{
+ const physx::PxMat44* tmp = &currentPose;
+ PX_UNUSED(tmp);
+
+ PX_UNUSED(wireframe);
+
+#ifdef USE_SAMPLE_RENDERER
+ if (mRenderer == NULL)
+ {
+ return;
+ }
+
+ mRendererTransform = currentPose;
+
+ for (size_t submeshIndex = 0; submeshIndex < mSubMeshes.size(); submeshIndex++)
+ {
+ TriangleSubMesh& sm = mSubMeshes[submeshIndex];
+ if (sm.mRendererMesh != NULL && sm.mRendererMeshContext != NULL && sm.mRendererMaterialReference != NULL)
+ {
+ using SampleRenderer::RendererMeshContext;
+
+ RendererMeshContext::CullMode cullMode = RendererMeshContext::NONE;
+
+ switch (sm.cullMode)
+ {
+ case nvidia::apex::RenderCullMode::CLOCKWISE:
+ cullMode = RendererMeshContext::CLOCKWISE;
+ break;
+ case nvidia::apex::RenderCullMode::COUNTER_CLOCKWISE:
+ cullMode = RendererMeshContext::COUNTER_CLOCKWISE;
+ break;
+ }
+
+ sm.mRendererMeshContext->cullMode = cullMode;
+ sm.mRendererMeshContext->fillMode = wireframe ? RendererMeshContext::LINE : RendererMeshContext::SOLID;
+ sm.mRendererMeshContext->material = sm.mRendererMaterialReference;
+ sm.mRendererMeshContext->materialInstance = sm.mRendererMaterialInstance;
+ sm.mRendererMeshContext->mesh = sm.mRendererMesh;
+ sm.mRendererMeshContext->transform = &mRendererTransform;
+
+ if (mUseGpuSkinning)
+ {
+ PX_ALWAYS_ASSERT();
+ }
+
+ mRenderer->queueMeshForRender(*sm.mRendererMeshContext);
+ }
+ }
+#endif // USE_SAMPLE_RENDERER
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::skin(const SkeletalAnim& anim, float scale /* = 1.0f */)
+{
+ const size_t numVerts = mParent != NULL ? mParent->mVertices.size() : mVertices.size();
+ const size_t numBoneIndices = mParent != NULL ? mParent->mBoneIndicesExternal.size() : mBoneIndicesExternal.size();
+
+ if (numBoneIndices != numVerts * 4 || mMaxBoneIndexExternal < 0)
+ {
+ return;
+ }
+
+ const std::vector<physx::PxMat44>& skinningMatrices = anim.getSkinningMatrices();
+
+ PX_ASSERT((int32_t)skinningMatrices.size() > mMaxBoneIndexInternal);
+
+ std::vector<int>& boneMappingInt2Ext = mParent != NULL ? mParent->mBoneMappingInt2Ext : mBoneMappingInt2Ext;
+
+ PX_ASSERT(mMaxBoneIndexInternal + 1 == (int)boneMappingInt2Ext.size());
+ const uint32_t numSkinningMatricesNeeded = physx::PxMin<uint32_t>((uint32_t)mMaxBoneIndexInternal + 1, (uint32_t)skinningMatrices.size());
+ if (mSkinningMatrices.size() != numSkinningMatricesNeeded)
+ {
+ mSkinningMatrices.resize(numSkinningMatricesNeeded);
+ }
+
+ for (uint32_t i = 0; i < numSkinningMatricesNeeded; i++)
+ {
+ mSkinningMatrices[i] = skinningMatrices[(uint32_t)boneMappingInt2Ext[i]];
+ mSkinningMatrices[i].scale(physx::PxVec4(scale, scale, scale, 1.f));
+ }
+
+ skinningMatricesChanged = true;
+
+ if (mUseGpuSkinning)
+ {
+ mSkinnedVertices.clear();
+ mSkinnedNormals.clear();
+ return;
+ }
+
+ vertexValuesChangedDynamic = true;
+
+ if (mSkinnedVertices.size() != numVerts)
+ {
+ mSkinnedVertices.resize(numVerts);
+ mSkinnedNormals.resize(numVerts);
+ }
+
+ const size_t numBones = skinningMatrices.size();
+ PX_ASSERT((int)numBones > mMaxBoneIndexInternal);
+ PX_UNUSED(numBones);
+
+ const physx::PxVec3* __restrict originalVertices = mParent == NULL || (mVertices.size() == mParent->mVertices.size()) ? &mVertices[0] : &mParent->mVertices[0];
+ const physx::PxVec3* __restrict originalNormals = mParent == NULL ? &mNormals[0] : &mParent->mNormals[0];
+ const uint16_t* __restrict skinningIndices = mParent == NULL ? &mBoneIndicesInternal[0] : &mParent->mBoneIndicesInternal[0];
+ const physx::PxVec4* __restrict skinningWeights = mParent == NULL ? &mBoneWeights[0] : &mParent->mBoneWeights[0];
+ physx::PxVec3* __restrict destVertices = &mSkinnedVertices[0];
+ physx::PxVec3* __restrict destNormals = &mSkinnedNormals[0];
+ uint32_t* __restrict numBoneWeights = mParent == NULL ? &mNumBoneWeights[0] : &mParent->mNumBoneWeights[0];
+
+
+ nvidia::prefetchLine(skinningWeights, 0);
+
+ const size_t count16 = (numVerts + 15) / 16;
+ for (size_t i = 0; i < count16; i++)
+ {
+ uint32_t numBoneWeight = *numBoneWeights;
+
+ const size_t maxVerts = (i + 1) * 16 > numVerts ? numVerts - (i * 16) : 16;
+
+ for (size_t j = 0; j < maxVerts; j++)
+ {
+ nvidia::prefetchLine(skinningWeights + 1, 0);
+
+ size_t twoBit = numBoneWeight & 0x3;
+ numBoneWeight >>= 2;
+
+ const physx::PxVec3 v = *originalVertices;
+ const physx::PxVec3 n = *originalNormals;
+
+ physx::PxVec3 vs(0.0f, 0.0f, 0.0f);
+ physx::PxVec3 ns(0.0f, 0.0f, 0.0f);
+
+ physx::PxVec4 skinningWeight = *skinningWeights;
+
+ for (unsigned int k = 0; k <= twoBit; k++)
+ {
+ //const uint32_t weightIdx = i*4 + k;
+ PX_ASSERT(physx::PxIsFinite(skinningWeight[k]));
+ const float weight = skinningWeight[k];
+
+ PX_ASSERT(weight > 0.0f);
+ const uint16_t boneNr = skinningIndices[k];
+
+ vs += mSkinningMatrices[boneNr].transform(v) * weight;
+ ns += mSkinningMatrices[boneNr].rotate(n) * weight;
+ }
+ ns *= nvidia::recipSqrtFast(ns.magnitudeSquared());
+ //ns.normalize();
+
+ *destVertices = vs;
+ *destNormals = ns;
+
+ originalVertices++;
+ originalNormals++;
+ skinningWeights++;
+ skinningIndices += 4;
+ destVertices++;
+ destNormals++;
+ }
+
+ numBoneWeights++;
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::unskin()
+{
+ mSkinningMatrices.clear();
+ skinningMatricesChanged = true;
+
+ mSkinnedVertices.clear();
+ mSkinnedNormals.clear();
+ vertexValuesChangedDynamic = true;
+
+ PX_ASSERT(mSkinnedVertices.empty());
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::showSubmesh(size_t index, bool on)
+{
+ if (index >= mSubMeshes.size())
+ {
+ for (size_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ mSubMeshes[i].show = on;
+ }
+ }
+ else
+ {
+ mSubMeshes[index].show = on;
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::selectSubMesh(size_t index, bool selected)
+{
+ if (index >= mSubMeshes.size())
+ {
+ for (size_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ mSubMeshes[i].selected = selected;
+ }
+ }
+ else
+ {
+ mSubMeshes[index].selected = selected;
+ }
+ updateSubmeshInfo();
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::hideSubMesh(const char* submeshName, const char* materialName)
+{
+ // Selected submeshes are hidden later on
+ int found = 0;
+ if (materialName != NULL)
+ {
+ for (size_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ if (mSubMeshes[i].name == submeshName && mSubMeshes[i].originalMaterialName == materialName)
+ {
+ mSubMeshes[i].selected = true;
+ found++;
+ }
+ }
+ }
+
+ if (found == 0)
+ {
+ for (size_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ if (mSubMeshes[i].name == submeshName)
+ {
+ mSubMeshes[i].selected = true;
+ found++;
+ }
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+size_t TriangleMesh::getNumTriangles(size_t subMeshNr) const
+{
+ if (mParent != NULL)
+ {
+ return mParent->getNumTriangles(subMeshNr);
+ }
+
+ if (subMeshNr >= mSubMeshes.size())
+ {
+ return mIndices.size() / 3;
+ }
+ else
+ {
+ return mSubMeshes[subMeshNr].numIndices / 3;
+ }
+}
+
+// ----------------------------------------------------------------------
+std::vector<PaintedVertex> &TriangleMesh::getPaintChannel(PaintChannelType channelType)
+{
+ PX_ASSERT(0 <= channelType && channelType < PC_NUM_CHANNELS);
+ return mPaintChannels[channelType];
+}
+
+// ----------------------------------------------------------------------
+const std::vector<PaintedVertex> &TriangleMesh::getPaintChannel(PaintChannelType channelType) const
+{
+ PX_ASSERT(0 <= channelType && channelType < PC_NUM_CHANNELS);
+ return mPaintChannels[channelType];
+}
+// ----------------------------------------------------------------------
+float TriangleMesh::getMaximalMaxDistance() const
+{
+ if (mPaintChannels[PC_MAX_DISTANCE].size() != mVertices.size())
+ {
+ return 0.0f;
+ }
+
+ float maxValue = 0.0f;
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ maxValue = physx::PxMax(maxValue, mPaintChannels[PC_MAX_DISTANCE][i].paintValueF32);
+ }
+ return maxValue;
+}
+
+// ----------------------------------------------------------------------
+int TriangleMesh::getBoneAssignments(uint32_t vertNr, const uint16_t* &bones, const float* &weights) const
+{
+ if (mParent != NULL)
+ {
+ return mParent->getBoneAssignments(vertNr, bones, weights);
+ }
+
+ if (mBoneIndicesExternal.empty())
+ {
+ return 0;
+ }
+
+ if (vertNr >= mVertices.size())
+ {
+ return 0;
+ }
+
+ bones = &mBoneIndicesExternal[vertNr * 4];
+ weights = &mBoneWeights[vertNr].x;
+
+ for (unsigned int i = 0; i < 4; i++)
+ {
+ if (weights[i] == 0.0f)
+ {
+ return (int)i;
+ }
+ }
+ return 4;
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::displaceAlongNormal(float displacement)
+{
+ if (mNormals.size() != mVertices.size())
+ {
+ return;
+ }
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ mVertices[i] += mNormals[i] * displacement;
+ }
+ updateBounds();
+}
+
+// ----------------------------------------------------------------------
+bool TriangleMesh::generateTangentSpace()
+{
+ if (mTexCoords[0].size() == mVertices.size())
+ {
+ // just make the size not 0
+ mTangents.push_back(physx::PxVec3(0.0f));
+ updateTangents();
+
+ return true;
+ }
+ return false;
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::updateBounds()
+{
+ mBounds.setEmpty();
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ mBounds.include(mVertices[i]);
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::setSubMeshColor(size_t subMeshNr, uint32_t color)
+{
+ if (subMeshNr >= mSubMeshes.size())
+ {
+ for (size_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ setSubMeshColor(i, color);
+ }
+ }
+ else
+ {
+ mSubMeshes[subMeshNr].color = color;
+
+ if (mSubMeshes[subMeshNr].materialResource != NULL)
+ {
+ float red = ((color >> 16) & 0xff) / 255.f;
+ float green = ((color >> 8) & 0xff) / 255.f;
+ float blue = ((color >> 0) & 0xff) / 255.f;
+
+ mSubMeshes[subMeshNr].materialResource->color = physx::PxVec3(red, green, blue);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::setSubMeshMaterialName(size_t subMeshNr, const char* materialName, nvidia::ResourceCallback* resourceCallback)
+{
+ if (subMeshNr >= mSubMeshes.size())
+ {
+ for (size_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ setSubMeshMaterialName(i, materialName, resourceCallback);
+ }
+ }
+ else
+ {
+ mSubMeshes[subMeshNr].materialName = materialName;
+ std::string name;
+ name.append(mMaterialPrefix);
+ name.append(materialName);
+ name.append(mMaterialSuffix);
+#ifdef USE_SAMPLE_RENDERER
+ if (mSubMeshes[subMeshNr].mSampleMaterial != NULL)
+ {
+ resourceCallback->releaseResource(APEX_MATERIALS_NAME_SPACE, NULL, mSubMeshes[subMeshNr].mSampleMaterial);
+ }
+ mSubMeshes[subMeshNr].mSampleMaterial = reinterpret_cast<SampleFramework::SampleMaterialAsset*>(resourceCallback->requestResource(APEX_MATERIALS_NAME_SPACE, name.c_str()));
+#else
+ mSubMeshes[subMeshNr].materialResource = (MaterialResource*)resourceCallback->requestResource(APEX_MATERIALS_NAME_SPACE, name.c_str());
+#endif
+ mSubMeshes[subMeshNr].resourceNeedsUpdate = true;
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::setSubMeshUsedForCollision(size_t subMeshNr, bool enable)
+{
+ if (subMeshNr < mSubMeshes.size())
+ {
+ mSubMeshes[subMeshNr].usedForCollision = enable;
+ }
+ else
+ {
+ for (size_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ setSubMeshUsedForCollision(i, enable);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::setSubMeshHasPhysics(size_t subMeshNr, bool enable)
+{
+ if (subMeshNr < mSubMeshes.size())
+ {
+ mSubMeshes[subMeshNr].hasApexAsset = enable;
+ }
+ else
+ {
+ for (size_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ setSubMeshHasPhysics(i, enable);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::setAllColors(unsigned int color)
+{
+ for (size_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ setSubMeshColor(i, color);
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::subdivideSubMesh(int subMeshNr, int subdivision, bool evenOutVertexDegrees)
+{
+ const int newTris[8][13] =
+ {
+ { 0, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
+ { 0, 3, 2, 3, 1, 2, -1, -1, -1, -1, -1, -1, -1},
+ { 0, 1, 4, 0, 4, 2, -1, -1, -1, -1, -1, -1, -1},
+ { 0, 3, 2, 3, 1, 4, 3, 4, 2, -1, -1, -1, -1},
+ { 0, 1, 5, 1, 2, 5, -1, -1, -1, -1, -1, -1, -1},
+ { 0, 3, 5, 3, 1, 5, 1, 2, 5, -1, -1, -1, -1},
+ { 0, 1, 5, 1, 4, 5, 4, 2, 5, -1, -1, -1, -1},
+ { 0, 3, 5, 3, 1, 4, 3, 4, 5, 4, 2, 5, -1}
+ };
+
+ if (subMeshNr < 0 || subMeshNr >= (int)mSubMeshes.size())
+ {
+ return;
+ }
+
+ if (subdivision == 0)
+ {
+ return;
+ }
+
+ float maxEdgeLength = (mBounds.minimum - mBounds.maximum).magnitude() / subdivision;
+ TriangleSubMesh& sm = mSubMeshes[(uint32_t)subMeshNr];
+
+ // move submesh to the end so it can be expanded
+ std::vector<uint32_t> tempIndices;
+ tempIndices.resize(sm.numIndices);
+ for (uint32_t i = 0; i < sm.numIndices; i++)
+ {
+ tempIndices[i] = mIndices[sm.firstIndex + i];
+ }
+
+ uint32_t numShift = (uint32_t)mIndices.size() - (sm.firstIndex + sm.numIndices);
+ for (uint32_t i = 0; i < numShift; i++)
+ {
+ mIndices[sm.firstIndex + i] = mIndices[sm.firstIndex + sm.numIndices + i];
+ }
+
+ uint32_t last = (uint32_t)mIndices.size() - sm.numIndices;
+ for (uint32_t i = 0; i < sm.numIndices; i++)
+ {
+ mIndices[last + i] = tempIndices[i];
+ }
+
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ TriangleSubMesh& si = mSubMeshes[i];
+ if (si.firstIndex > sm.firstIndex)
+ {
+ si.firstIndex -= sm.numIndices;
+ }
+ }
+ sm.firstIndex = (uint32_t)mIndices.size() - sm.numIndices;
+
+ // subdivide
+ float h2 = maxEdgeLength * maxEdgeLength;
+ mVertexFirstSplit.resize(mVertices.size());
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ mVertexFirstSplit[i] = -1;
+ }
+ mVertexSplits.clear();
+
+ uint32_t numTris = sm.numIndices / 3;
+ int id[6];
+
+ vertexCountChanged |= sm.numIndices > 0;
+
+ for (uint32_t i = 0; i < numTris; i++)
+ {
+ id[0] = (int32_t)mIndices[sm.firstIndex + 3 * i + 0];
+ id[1] = (int32_t)mIndices[sm.firstIndex + 3 * i + 1];
+ id[2] = (int32_t)mIndices[sm.firstIndex + 3 * i + 2];
+ physx::PxVec3 p0 = mVertices[(uint32_t)id[0]];
+ physx::PxVec3 p1 = mVertices[(uint32_t)id[1]];
+ physx::PxVec3 p2 = mVertices[(uint32_t)id[2]];
+
+ uint32_t code = 0;
+ if ((p0 - p1).magnitudeSquared() > h2)
+ {
+ id[3] = addSplitVert(id[0], id[1]);
+ code |= 1;
+ }
+ if ((p1 - p2).magnitudeSquared() > h2)
+ {
+ id[4] = addSplitVert(id[1], id[2]);
+ code |= 2;
+ }
+ if ((p2 - p0).magnitudeSquared() > h2)
+ {
+ id[5] = addSplitVert(id[2], id[0]);
+ code |= 4;
+ }
+
+ const int* newId = newTris[code];
+
+ // the first sub triangle replaces the old one
+ mIndices[sm.firstIndex + 3 * i + 0] = (uint32_t)id[(uint32_t)*newId++];
+ mIndices[sm.firstIndex + 3 * i + 1] = (uint32_t)id[(uint32_t)*newId++];
+ mIndices[sm.firstIndex + 3 * i + 2] = (uint32_t)id[(uint32_t)*newId++];
+
+ // the others are appended at the end
+ while (true)
+ {
+ int j = *newId++;
+ if (j >= 0)
+ {
+ mIndices.push_back((uint32_t)id[(uint32_t)j]);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ sm.numIndices = (uint32_t)mIndices.size() - sm.firstIndex;
+
+ if (evenOutVertexDegrees)
+ {
+ evenOutVertexDegree(subMeshNr, 3);
+ }
+
+ updateNormals(subMeshNr);
+ updateTangents();
+ updateBoneWeights();
+ updateSubmeshInfo();
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::evenOutVertexDegree(int subMeshNr, int numIters)
+{
+ std::vector<TriangleMeshEdge> edges;
+ TriangleMeshEdge edge;
+
+ // compute degrees and neighbor information
+ std::vector<int> vertDegs;
+ vertDegs.resize(mVertices.size(), 0);
+
+ PX_ASSERT(subMeshNr < (int)mSubMeshes.size());
+ uint32_t firstIndex = 0;
+ uint32_t lastIndex = (uint32_t)mIndices.size();
+ if (subMeshNr >= 0)
+ {
+ firstIndex = mSubMeshes[(uint32_t)subMeshNr].firstIndex;
+ lastIndex = firstIndex + mSubMeshes[(uint32_t)subMeshNr].numIndices;
+ }
+ uint32_t* indices = &mIndices[firstIndex];
+ uint32_t numTris = (lastIndex - firstIndex) / 3;
+
+ for (uint32_t i = 0; i < numTris; i++)
+ {
+ uint32_t i0 = indices[3 * i];
+ uint32_t i1 = indices[3 * i + 1];
+ uint32_t i2 = indices[3 * i + 2];
+ vertDegs[i0]++;
+ vertDegs[i1]++;
+ vertDegs[i2]++;
+ edge.init((int32_t)i0, (int32_t)i1, 0, (int32_t)i);
+ edges.push_back(edge);
+ edge.init((int32_t)i1, (int32_t)i2, 1, (int32_t)i);
+ edges.push_back(edge);
+ edge.init((int32_t)i2, (int32_t)i0, 2, (int32_t)i);
+ edges.push_back(edge);
+ }
+ std::sort(edges.begin(), edges.end());
+
+ std::vector<int> neighbors;
+ neighbors.resize(3 * numTris, -1);
+
+ size_t edgeNr = 0;
+ size_t numEdges = edges.size();
+ while (edgeNr < numEdges)
+ {
+ TriangleMeshEdge& e0 = edges[edgeNr];
+ size_t j = edgeNr + 1;
+ while (j < numEdges && edges[j] == e0)
+ {
+ j++;
+ }
+ if (j < numEdges && j == edgeNr + 2) // manifold edge
+ {
+ TriangleMeshEdge& e1 = edges[edgeNr + 1];
+ neighbors[uint32_t(e0.triNr * 3 + e0.edgeNr)] = e1.triNr;
+ neighbors[uint32_t(e1.triNr * 3 + e1.edgeNr)] = e0.triNr;
+ }
+ edgeNr = j;
+ }
+
+ // artifically increase the degree of border vertices
+ for (uint32_t i = 0; i < numTris; i++)
+ {
+ for (uint32_t j = 0; j < 3; j++)
+ {
+ if (neighbors[3 * i + j] < 0)
+ {
+ vertDegs[indices[3 * i + j]]++;
+ vertDegs[indices[3 * i + (j + 1) % 3]]++;
+ }
+ }
+ }
+
+ // create random triangle permutation
+ std::vector<int> permutation;
+ permutation.resize(numTris);
+ for (uint32_t i = 0; i < numTris; i++)
+ {
+ permutation[i] = (int32_t)i;
+ }
+
+ uint32_t random = 0;
+ for (uint32_t i = 0; i < numTris - 1; i++)
+ {
+ random = random * 1664525 + 1013904223;
+ uint32_t j = i + random % (numTris - i);
+ int p = permutation[i];
+ permutation[i] = permutation[j];
+ permutation[j] = p;
+ }
+
+ // flip edges
+ for (uint32_t iters = 0; iters < (uint32_t)numIters; iters++)
+ {
+ for (uint32_t i = 0; i < numTris; i++)
+ {
+ int t = permutation[i];
+ for (uint32_t j = 0; j < 3; j++)
+ {
+ int n = neighbors[3 * t + j];
+ if (n < t)
+ {
+ continue;
+ }
+ // determine the indices of the two triangles involved
+ uint32_t& i0 = indices[3 * (uint32_t)t + j];
+ uint32_t& i1 = indices[3 * (uint32_t)t + (j + 1) % 3];
+ uint32_t& i2 = indices[3 * (uint32_t)t + (j + 2) % 3];
+
+ int backNr = -1;
+ if (neighbors[3 * (uint32_t)n] == t)
+ {
+ backNr = 0;
+ }
+ else if (neighbors[3 * (uint32_t)n + 1] == t)
+ {
+ backNr = 1;
+ }
+ else if (neighbors[3 * (uint32_t)n + 2] == t)
+ {
+ backNr = 2;
+ }
+ PX_ASSERT(backNr >= 0);
+ //uint32_t& j0 = indices[3 * n + backNr];
+ uint32_t& j1 = indices[3 * (uint32_t)n + ((uint32_t)backNr + 1) % 3];
+ uint32_t& j2 = indices[3 * (uint32_t)n + ((uint32_t)backNr + 2) % 3];
+
+ // do we want to flip?
+
+ // geometrical tests
+ physx::PxVec3& p0 = mVertices[i0];
+ physx::PxVec3& p1 = mVertices[i1];
+ physx::PxVec3& q0 = mVertices[i2];
+ physx::PxVec3& q1 = mVertices[j2];
+
+ // does the triangle pair form a convex shape?
+ float r = (p0 - p1).magnitudeSquared();
+ if (r == 0.0f)
+ {
+ continue;
+ }
+
+ float s = (q0 - p0).dot(p1 - p0) / r;
+ if (s < 0.2f || s > 0.8f)
+ {
+ continue;
+ }
+ s = (q1 - p0).dot(p1 - p0) / r;
+ if (s < 0.2f || s > 0.8f)
+ {
+ continue;
+ }
+
+ // the new edge shoulndnot be significantly longer than the old one
+ float d0 = (mVertices[i0] - mVertices[i1]).magnitude();
+ float d1 = (mVertices[i2] - mVertices[j2]).magnitude();
+ if (d1 > 2.0f * d0)
+ {
+ continue;
+ }
+
+ // will the flip even out the degrees?
+ int deg0 = vertDegs[i0];
+ int deg1 = vertDegs[i1];
+ int deg2 = vertDegs[i2];
+ int deg3 = vertDegs[j2];
+
+ int min = physx::PxMin(deg0, physx::PxMin(deg1, physx::PxMin(deg2, deg3)));
+ int max = physx::PxMax(deg0, physx::PxMax(deg1, physx::PxMax(deg2, deg3)));
+ int span = max - min;
+
+ deg0--;
+ deg1--;
+ deg2++;
+ deg3++;
+
+ min = physx::PxMin(deg0, physx::PxMin(deg1, physx::PxMin(deg2, deg3)));
+ max = physx::PxMax(deg0, physx::PxMax(deg1, physx::PxMax(deg2, deg3)));
+
+ if (max - min > span)
+ {
+ continue;
+ }
+
+
+ // update degrees
+ vertDegs[i0]--;
+ vertDegs[i1]--;
+ vertDegs[i2]++;
+ vertDegs[j2]++;
+
+ // flip
+ i1 = j2;
+ j1 = i2;
+
+ // update neighbors
+ int& ni0 = neighbors[3 * (uint32_t)t + j];
+ int& ni1 = neighbors[3 * (uint32_t)t + (j + 1) % 3];
+
+ int& nj0 = neighbors[3 * (uint32_t)n + (uint32_t)backNr];
+ int& nj1 = neighbors[3 * (uint32_t)n + ((uint32_t)backNr + 1) % 3];
+
+ ni0 = nj1;
+ nj0 = ni1;
+ ni1 = n;
+ nj1 = t;
+
+ // fix backwards links
+ if (ni0 >= 0)
+ {
+ if (neighbors[3 * (uint32_t)ni0] == n)
+ {
+ neighbors[3 * (uint32_t)ni0] = t;
+ }
+ else if (neighbors[3 * (uint32_t)ni0 + 1] == n)
+ {
+ neighbors[3 * (uint32_t)ni0 + 1] = t;
+ }
+ else if (neighbors[3 * (uint32_t)ni0 + 2] == n)
+ {
+ neighbors[3 * (uint32_t)ni0 + 2] = t;
+ }
+ else
+ {
+ PX_ALWAYS_ASSERT();
+ }
+ }
+ if (nj0 >= 0)
+ {
+ if (neighbors[3 * (uint32_t)nj0] == t)
+ {
+ neighbors[3 * (uint32_t)nj0] = n;
+ }
+ else if (neighbors[3 * (uint32_t)nj0 + 1] == t)
+ {
+ neighbors[3 * (uint32_t)nj0 + 1] = n;
+ }
+ else if (neighbors[3 * (uint32_t)nj0 + 2] == t)
+ {
+ neighbors[3 * (uint32_t)nj0 + 2] = n;
+ }
+ else
+ {
+ PX_ALWAYS_ASSERT();
+ }
+ }
+ }
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::setCullMode(nvidia::RenderCullMode::Enum cullMode, int32_t submeshIndex)
+{
+ if (submeshIndex < 0)
+ {
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ setCullMode(cullMode, (int32_t)i);
+ }
+ }
+ else if ((uint32_t)submeshIndex < mSubMeshes.size())
+ {
+ if (cullMode != mSubMeshes[(uint32_t)submeshIndex].cullMode)
+ {
+ mSubMeshes[(uint32_t)submeshIndex].cullMode = cullMode;
+ oneCullModeChanged = true;
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::updatePaintingColors(PaintChannelType channelType, float maxDistMin, float maxDistMax, unsigned int flag, nvidia::apex::RenderDebugInterface* batcher)
+{
+ using RENDER_DEBUG::DebugColors;
+ const uint32_t colorBlack = batcher != NULL ? RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::Black) : 0xff000000;
+ const uint32_t colorWhite = batcher != NULL ? RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::White) : 0xffffffff;
+ const uint32_t colorLightBlue = batcher != NULL ? RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::LightBlue) : 0xff7777ff;
+ const uint32_t colorRed = batcher != NULL ? RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::Red) : 0xffff0000;
+
+ const uint32_t colorDisabled = batcher != NULL ? RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::Purple) : 0xffff00ff;
+ const uint32_t colorSmall = batcher != NULL ? RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::Blue) : 0xff0000ff;;
+ const uint32_t colorBig = colorRed;
+
+
+ if (channelType == PC_MAX_DISTANCE || channelType == PC_NUM_CHANNELS)
+ {
+ for (uint32_t i = 0; i < mPaintChannels[PC_MAX_DISTANCE].size(); i++)
+ {
+ const float val = mPaintChannels[PC_MAX_DISTANCE][i].paintValueF32;
+ if (val < 0.0f)
+ {
+ mPaintChannels[PC_MAX_DISTANCE][i].color = colorDisabled;
+ }
+ else if (val < maxDistMin)
+ {
+ mPaintChannels[PC_MAX_DISTANCE][i].color = colorSmall;
+ }
+ else if (val > maxDistMax)
+ {
+ mPaintChannels[PC_MAX_DISTANCE][i].color = colorBig;
+ }
+ else
+ {
+ const float setVal = (val - maxDistMin) / (maxDistMax - maxDistMin);
+ mPaintChannels[PC_MAX_DISTANCE][i].setColor(setVal, setVal, setVal);
+ }
+ }
+ }
+ if (channelType == PC_COLLISION_DISTANCE || channelType == PC_NUM_CHANNELS)
+ {
+ for (uint32_t i = 0; i < mPaintChannels[PC_COLLISION_DISTANCE].size(); i++)
+ {
+ const float v = mPaintChannels[PC_COLLISION_DISTANCE][i].paintValueF32;
+ physx::PxVec3 color;
+ if (physx::PxAbs(v) > 1.0f)
+ {
+ color = physx::PxVec3(0.0f, 0.0f, 0.0f);
+ }
+ else if (v == 0.0f)
+ {
+ color = physx::PxVec3(1.0f, 1.0f, 1.0f);
+ }
+ else if (v < 0.0f)
+ {
+ color = physx::PxVec3(1.0f, -v, 0.0f);
+ }
+ else // v > 0.0f
+ {
+ color = physx::PxVec3(0.0f, v, 1.0f);
+ }
+ mPaintChannels[PC_COLLISION_DISTANCE][i].setColor(color.x, color.y, color.z);
+ }
+ }
+
+ if (channelType == PC_LATCH_TO_NEAREST_SLAVE || channelType == PC_NUM_CHANNELS)
+ {
+ for (size_t i = 0; i < mPaintChannels[PC_LATCH_TO_NEAREST_SLAVE].size(); i++)
+ {
+ const unsigned int slave = mPaintChannels[PC_LATCH_TO_NEAREST_SLAVE][i].paintValueU32;
+ const unsigned int master = mPaintChannels[PC_LATCH_TO_NEAREST_MASTER][i].paintValueU32;
+
+ unsigned int color;
+ if (slave == 0)
+ {
+ if (master == 0)
+ {
+ color = colorRed;
+ }
+ else
+ {
+ color = colorBlack;
+ }
+ }
+ else
+ {
+ if (flag != 0 && (slave & flag) == flag)
+ {
+ color = colorWhite;
+ }
+ else
+ {
+ color = colorLightBlue;
+ }
+ }
+
+ mPaintChannels[PC_LATCH_TO_NEAREST_SLAVE][i].color = color;
+ }
+ }
+ if (channelType == PC_LATCH_TO_NEAREST_MASTER || channelType == PC_NUM_CHANNELS)
+ {
+ for (size_t i = 0; i < mPaintChannels[PC_LATCH_TO_NEAREST_MASTER].size(); i++)
+ {
+ const unsigned int slave = mPaintChannels[PC_LATCH_TO_NEAREST_SLAVE][i].paintValueU32;
+ const unsigned int master = mPaintChannels[PC_LATCH_TO_NEAREST_MASTER][i].paintValueU32;
+
+ unsigned int color;
+ if (slave != 0)
+ {
+ color = colorDisabled;
+ }
+ else
+ {
+ if (flag != 0 && (master & flag) == flag)
+ {
+ color = colorWhite;
+ }
+ else if (master != 0)
+ {
+ color = colorLightBlue;
+ }
+ else
+ {
+ color = colorRed;
+ }
+ }
+
+ mPaintChannels[PC_LATCH_TO_NEAREST_MASTER][i].color = color;
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+bool TriangleMesh::processElement(const char* elementName, const char* /*elementData*/, const physx::shdfnd::FastXml::AttributePairs& attr, int /*lineno*/)
+{
+ if (::strcmp(elementName, "mesh") == 0)
+ {
+ PX_ASSERT(mParserState == PS_Uninitialized);
+ mParserState = PS_Mesh;
+ }
+ else if (::strcmp(elementName, "sharedgeometry") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 1);
+ PX_ASSERT(::strcmp(attr.getKey(0), "vertexcount") == 0);
+ const int vertexCount = atoi(attr.getValue(0));
+ mVertices.resize(0);
+ mVertices.reserve((uint32_t)vertexCount);
+ }
+ else if (::strcmp(elementName, "vertexbuffer") == 0)
+ {
+ for (int i = 0; i < attr.getNbAttr(); i++)
+ {
+ if (::strcmp(attr.getKey(i), "normals") == 0)
+ {
+ mNormals.reserve(mVertices.capacity());
+ }
+
+ else if (::strcmp(attr.getKey(i), "texture_coord_dimensions_0") == 0)
+ {
+ mTexCoords[0].reserve(mVertices.capacity());
+ }
+ }
+ }
+ else if (::strcmp(elementName, "vertex") == 0)
+ {
+ int a = 0;
+ PX_UNUSED(a);
+
+ // do nothing?
+ }
+ else if (::strcmp(elementName, "position") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 3);
+ PX_ASSERT(::strcmp(attr.getKey(0), "x") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(1), "y") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(2), "z") == 0);
+ physx::PxVec3 pos;
+ pos.x = (float)atof(attr.getValue(0));
+ pos.y = (float)atof(attr.getValue(1));
+ pos.z = (float)atof(attr.getValue(2));
+ mVertices.push_back(pos);
+ }
+ else if (::strcmp(elementName, "normal") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 3);
+ PX_ASSERT(::strcmp(attr.getKey(0), "x") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(1), "y") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(2), "z") == 0);
+ physx::PxVec3 normal;
+ normal.x = (float)atof(attr.getValue(0));
+ normal.y = (float)atof(attr.getValue(1));
+ normal.z = (float)atof(attr.getValue(2));
+ mNormals.push_back(normal);
+ }
+ else if (::strcmp(elementName, "texcoord") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 2);
+ PX_ASSERT(::strcmp(attr.getKey(0), "u") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(1), "v") == 0);
+ nvidia::VertexUV tc;
+ tc.u = (float)atof(attr.getValue(0));
+ tc.v = (float)atof(attr.getValue(1));
+ mTexCoords[0].push_back(tc);
+ }
+ else if (::strcmp(elementName, "colour_diffuse") == 0)
+ {
+ int a = 0;
+ PX_UNUSED(a);
+ }
+ else if (::strcmp(elementName, "physics_coeffs") == 0)
+ {
+ for (int i = 0; i < PC_NUM_CHANNELS; i++)
+ {
+ if (mPaintChannels[i].capacity() < mVertices.size())
+ {
+ mPaintChannels[i].reserve(mVertices.size());
+ }
+
+ if (i < attr.getNbAttr())
+ {
+ float value = (float)atof(attr.getValue(i));
+ mPaintChannels[i].push_back(PaintedVertex(value));
+ }
+ }
+ }
+ else if (::strcmp(elementName, "submeshes") == 0)
+ {
+ PX_ASSERT(mParserState == PS_Mesh);
+ mParserState = PS_Submeshes;
+ }
+ else if (::strcmp(elementName, "submesh") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() >= 1);
+ PX_ASSERT(::strcmp(attr.getKey(0), "material") == 0);
+ TriangleSubMesh sm;
+ sm.init();
+ sm.name = attr.getValue(0); // ogre xml doesn't have submesh names...
+ sm.materialName = attr.getValue(0);
+ sm.firstIndex = (uint32_t)mIndices.size();
+ if (sm.materialName.empty())
+ {
+ char buf[64];
+ physx::shdfnd::snprintf(buf, 64, "Material_%2d", mSubMeshes.size());
+ sm.materialName = buf;
+ }
+ sm.originalMaterialName = sm.materialName;
+ mSubMeshes.push_back(sm);
+ }
+ else if (::strcmp(elementName, "faces") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 1);
+ PX_ASSERT(::strcmp(attr.getKey(0), "count") == 0);
+ const int faceCount = atoi(attr.getValue(0));
+ mIndices.reserve(mIndices.size() + faceCount * 3);
+ PX_ASSERT(mSubMeshes.size() > 0);
+ PX_ASSERT(mSubMeshes.back().numIndices == 0);
+ mSubMeshes.back().numIndices = (uint32_t)faceCount * 3;
+ }
+ else if (::strcmp(elementName, "face") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 3);
+ PX_ASSERT(::strcmp(attr.getKey(0), "v1") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(1), "v2") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(2), "v3") == 0);
+ mIndices.push_back((uint32_t)atoi(attr.getValue(0)));
+ mIndices.push_back((uint32_t)atoi(attr.getValue(1)));
+ mIndices.push_back((uint32_t)atoi(attr.getValue(2)));
+ }
+ else if (::strcmp(elementName, "skeletonlink") == 0)
+ {
+ PX_ASSERT(mParserState == PS_Submeshes);
+ mParserState = PS_Skeleton;
+ }
+ else if (::strcmp(elementName, "boneassignments") == 0)
+ {
+ // do nothing?
+ mBoneIndicesExternal.resize(mVertices.capacity() * 4, 0);
+ mBoneWeights.resize(mVertices.capacity(), physx::PxVec4(0.0f));
+ }
+ else if (::strcmp(elementName, "vertexboneassignment") == 0)
+ {
+ PX_ASSERT(attr.getNbAttr() == 3);
+ PX_ASSERT(::strcmp(attr.getKey(0), "vertexindex") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(1), "boneindex") == 0);
+ PX_ASSERT(::strcmp(attr.getKey(2), "weight") == 0);
+
+ const int vertexNr = atoi(attr.getValue(0));
+ const int boneNr = atoi(attr.getValue(1));
+ const float weight = (float)atof(attr.getValue(2));
+
+ mMaxBoneIndexExternal = physx::PxMax(mMaxBoneIndexExternal, boneNr);
+ PX_ASSERT(vertexNr < (int)mVertices.size());
+ float* weights = &mBoneWeights[(uint32_t)vertexNr].x;
+ uint16_t* indices = &mBoneIndicesExternal[(uint32_t)vertexNr * 4];
+ for (uint32_t i = 0; i < 4; i++)
+ {
+ if (weights[i] == 0)
+ {
+ PX_ASSERT(boneNr < 0xffff);
+ weights[i] = weight;
+ indices[i] = (uint16_t)boneNr;
+ break;
+ }
+ if (weights[i] < weight)
+ {
+ // move all one back
+ for (uint32_t j = 3; j > i; j--)
+ {
+ weights[j] = weights[j - 1];
+ indices[j] = indices[j - 1];
+ }
+ weights[i] = weight;
+ indices[i] = (uint16_t)boneNr;
+ break;
+ }
+#if 1
+ // safety
+ for (uint32_t j = 0; j < 4; j++)
+ {
+ PX_ASSERT(weights[j] >= 0.0f);
+ PX_ASSERT(weights[j] <= 1.0f);
+ }
+ for (uint32_t j = 0; j < 3; j++)
+ {
+ PX_ASSERT(weights[j] >= weights[j + 1]);
+ }
+#endif
+ }
+ }
+ else
+ {
+ if (mParserState == PS_Uninitialized)
+ {
+ clear(NULL, NULL);
+ return false;
+ }
+ PX_ALWAYS_ASSERT();
+ }
+
+ return true;
+}
+
+
+
+bool TriangleMesh::hasSkinningVertices()
+{
+ return mSkinnedVertices.size() == mVertices.size();
+}
+
+
+
+// -----------------[ private methods ]----------------------------------
+
+// ----------------------------------------------------------------------
+void TriangleMesh::updateNormals(int subMeshNr)
+{
+ if (mParent != NULL)
+ {
+ mParent->updateNormals(subMeshNr);
+ }
+ else
+ {
+ std::vector<physx::PxVec3> newNormals(mVertices.size(), physx::PxVec3(0.0f, 0.0f, 0.0f));
+
+ const uint32_t start = subMeshNr < 0 ? 0 : mSubMeshes[(uint32_t)subMeshNr].firstIndex;
+ const uint32_t end = subMeshNr < 0 ? (uint32_t)mIndices.size() : mSubMeshes[(uint32_t)subMeshNr].firstIndex + mSubMeshes[(uint32_t)subMeshNr].numIndices;
+ for (uint32_t i = start; i < end; i += 3)
+ {
+ const uint32_t i0 = mIndices[i + 0];
+ const uint32_t i1 = mIndices[i + 1];
+ const uint32_t i2 = mIndices[i + 2];
+
+ physx::PxVec3 n = (mVertices[i1] - mVertices[i0]).cross(mVertices[i2] - mVertices[i0]);
+ newNormals[i0] += n;
+ newNormals[i1] += n;
+ newNormals[i2] += n;
+ }
+
+ for (uint32_t i = 0; i < newNormals.size(); i++)
+ {
+ if (newNormals[i].isZero())
+ {
+ if (i < mNormals.size())
+ {
+ newNormals[i] = mNormals[i];
+ }
+ else
+ {
+ newNormals[i] = physx::PxVec3(0.0f, 1.0f, 0.0f);
+ }
+ }
+ else
+ {
+ newNormals[i].normalize();
+ }
+ }
+
+ mNormals.resize(mVertices.size());
+
+ for (uint32_t i = 0; i < mNormals.size(); i++)
+ {
+ mNormals[i] = newNormals[i];
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::updateTangents()
+{
+ const int useTexCoords = 0;
+ if (mParent != NULL)
+ {
+ mParent->updateTangents();
+ }
+ else if ((mTangents.empty() && mBitangents.empty()) || mTexCoords[useTexCoords].empty())
+ {
+ mTangents.clear();
+ mBitangents.clear();
+ // do nothing, no tangents!
+ }
+ else if (mTangents.size() != mVertices.size() || (mTangents.size() != mBitangents.size() && mVertices.size() > 0 && mTexCoords[useTexCoords].size() == mVertices.size()) )
+ {
+ mTangents.clear();
+ mTangents.resize(mVertices.size(), physx::PxVec3(0.0f, 0.0f, 0.0f));
+ mBitangents.clear();
+ mBitangents.resize(mVertices.size(), physx::PxVec3(0.0f, 0.0f, 0.0f));
+
+ PX_ASSERT(mTangents[0].isZero());
+
+
+ for (uint32_t i = 0; i < mIndices.size(); i += 3)
+ {
+ const physx::PxVec3& p0 = mVertices[mIndices[i + 0]];
+ const physx::PxVec3& p1 = mVertices[mIndices[i + 1]];
+ const physx::PxVec3& p2 = mVertices[mIndices[i + 2]];
+
+ const nvidia::VertexUV& w0 = mTexCoords[useTexCoords][mIndices[i + 0]];
+ const nvidia::VertexUV& w1 = mTexCoords[useTexCoords][mIndices[i + 1]];
+ const nvidia::VertexUV& w2 = mTexCoords[useTexCoords][mIndices[i + 2]];
+
+ const float x1 = p1.x - p0.x;
+ const float x2 = p2.x - p0.x;
+ const float y1 = p1.y - p0.y;
+ const float y2 = p2.y - p0.y;
+ const float z1 = p1.z - p0.z;
+ const float z2 = p2.z - p0.z;
+
+ const float s1 = w1.u - w0.u;
+ const float s2 = w2.u - w0.u;
+ const float t1 = w1.v - w0.v;
+ const float t2 = w2.v - w0.v;
+
+ const float div = (s1 * t2 - s2 * t1);
+ if (div > 0.0f)
+ {
+ const float r = 1.0F / div;
+ physx::PxVec3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
+ physx::PxVec3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
+
+ mTangents[mIndices[i + 0]] += sdir;
+ mTangents[mIndices[i + 1]] += sdir;
+ mTangents[mIndices[i + 2]] += sdir;
+
+ mBitangents[mIndices[i + 0]] += tdir;
+ mBitangents[mIndices[i + 1]] += tdir;
+ mBitangents[mIndices[i + 2]] += tdir;
+ }
+ }
+
+ for (uint32_t i = 0; i < mVertices.size(); i++)
+ {
+ physx::PxVec3& t = mTangents[i];
+ physx::PxVec3& bt = mBitangents[i];
+
+ // ortho-normalize tangents
+ const physx::PxVec3& n = mNormals[i];
+ t -= n * n.dot(t);
+ t.normalize();
+
+ const physx::PxVec3 nxt = n.cross(t);
+ const float sign = bt.dot(nxt) < 0.0f ? -1.0f : 1.0f;
+ bt = nxt * sign;
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::updateBoneWeights()
+{
+ if (mParent != NULL)
+ {
+ mParent->updateBoneWeights();
+ }
+ else
+ {
+ if (mBoneWeights.size() == mVertices.size())
+ {
+ size_t count = (mVertices.size() + 15) / 16;
+ mNumBoneWeights.resize(count, 0xffffffff);
+
+ for (unsigned int i = 0; i < mVertices.size(); i++)
+ {
+ physx::PxVec4 boneWeight = mBoneWeights[i];
+ int maxWeight = -1;
+ for (int j = 0; j < 4; j++)
+ {
+ maxWeight += boneWeight[j] > 0 ? 1 : 0;
+ PX_ASSERT((maxWeight == j) != (boneWeight[j] == 0.0f));
+ }
+
+ const unsigned int index = i / 16;
+ const unsigned int offset = (i - (index * 16)) * 2;
+ unsigned int newValue = mNumBoneWeights[index];
+ newValue = uint32_t((newValue & ~(0x3 << offset)) | maxWeight << offset);
+ mNumBoneWeights[index] = newValue;
+ }
+ }
+ else
+ {
+ PX_ASSERT(mBoneWeights.empty());
+ mNumBoneWeights.clear();
+ }
+
+ optimizeForRendering();
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::optimizeForRendering()
+{
+ PX_ASSERT(mParent == NULL);
+
+ if (mBoneWeights.size() == mVertices.size() && mVertices.size() > 0)
+ {
+ PX_ASSERT(mMaxBoneIndexExternal >= 0);
+ // basically compress the bone indices
+ std::vector<int> ext2int(mMaxBoneIndexExternal + 1, -1);
+ unsigned int maxBones = 0;
+
+ for (size_t v = 0; v < mVertices.size(); v++)
+ {
+ for (unsigned int k = 0; k < 4; k++)
+ {
+ if (mBoneWeights[v][k] > 0.0f)
+ {
+ unsigned int boneIndex = mBoneIndicesExternal[v * 4 + k];
+
+ if (ext2int[boneIndex] == -1)
+ {
+ ext2int[boneIndex] = (int32_t)maxBones++;
+ }
+ }
+ }
+ }
+
+ mBoneIndicesInternal.resize(mBoneIndicesExternal.size(), 0);
+
+ for (size_t v = 0; v < mVertices.size(); v++)
+ {
+ for (unsigned int k = 0; k < 4; k++)
+ {
+ unsigned int index = (unsigned int)v * 4 + k;
+ if (mBoneWeights[v][k] > 0.0f)
+ {
+ mBoneIndicesInternal[index] = (unsigned short)ext2int[mBoneIndicesExternal[index]];
+ }
+ else
+ {
+ mBoneIndicesInternal[index] = 0;
+ }
+ }
+ }
+ mMaxBoneIndexInternal = (int)maxBones - 1;
+
+ mBoneMappingInt2Ext.resize(maxBones);
+ for (size_t i = 0; i < ext2int.size(); i++)
+ {
+ if (ext2int[i] >= 0)
+ {
+ PX_ASSERT(ext2int[i] < (int)mBoneMappingInt2Ext.size());
+ mBoneMappingInt2Ext[(uint32_t)ext2int[i]] = (int)i;
+ }
+ }
+ }
+
+
+#if 0 // PH: too advanced yet, and only a first draft
+
+ for (size_t submeshIndex = 0; submeshIndex < mSubMeshes.size(); submeshIndex++)
+ {
+ TriangleSubMesh& submesh = mSubMeshes[submeshIndex];
+
+ if (submesh.maxBonesShader > 0)
+ {
+ unsigned int indicesMapped = 0;
+ //while (indicesMapped < submesh.numIndices)
+ {
+ //OptimizedRenderData renderData;
+
+ std::vector<int> currentBoneMapping(mMaxBoneIndex + 1, -1);
+ unsigned int currentBoneMappingSize = 0;
+
+ for (unsigned int i = 0; i < submesh.numIndices; i += 3)
+ {
+ // collect all the bone indices
+ unsigned int boneIndices[12];
+ unsigned int numBoneIndices = 0;
+
+ for (int j = 0; j < 3; j++)
+ {
+ const unsigned int vertexIndex = mIndices[submesh.firstIndex + i + j];
+ for (int k = 0; k < 4; k++)
+ {
+ if (mBoneWeights[vertexIndex][k] > 0.0f)
+ {
+ unsigned int boneIndex = mBoneIndices[vertexIndex * 4 + k];
+
+ bool found = false;
+
+ for (unsigned int l = 0; l < numBoneIndices; l++)
+ {
+ if (boneIndices[l] == boneIndex)
+ {
+ found = true;
+ }
+ }
+
+ if (!found)
+ {
+ boneIndices[numBoneIndices++] = boneIndex;
+ }
+ }
+ }
+ }
+
+ // now let's see if they fit into the current mapping
+ unsigned int notInMapping = 0;
+ for (unsigned int j = 0; j < numBoneIndices; j++)
+ {
+ if (currentBoneMapping[boneIndices[j]] < 0)
+ {
+ notInMapping++;
+ }
+ }
+
+ if (notInMapping == 0)
+ {
+ //indicesMapped += 3;
+ }
+ else if (notInMapping < submesh.maxBonesShader - currentBoneMappingSize)
+ {
+ // add them to the mapping
+ for (unsigned int j = 0; j < numBoneIndices; j++)
+ {
+ if (currentBoneMapping[boneIndices[j]] < 0)
+ {
+ currentBoneMapping[boneIndices[j]] = currentBoneMappingSize++;
+ }
+ }
+
+ //indicesMapped += 3;
+ }
+ else
+ {
+ // we're full I guess
+ int a = 0;
+ a = a;
+ }
+ }
+ }
+
+ int a = 0;
+ a = a;
+ }
+ }
+#endif
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::complete(bool useCustomChannels)
+{
+ for (uint32_t i = 0; i < PC_NUM_CHANNELS; i++)
+ {
+ std::vector<PaintedVertex> &channel = mPaintChannels[i];
+ if (useCustomChannels && (channel.size() == 0 || channel.size() != mVertices.size()))
+ {
+ channel.clear();
+ switch (i)
+ {
+ case PC_MAX_DISTANCE:
+ channel.resize(mVertices.size(), PaintedVertex(-0.1f));
+ break;
+ case PC_COLLISION_DISTANCE:
+ channel.resize(mVertices.size(), PaintedVertex(-1.1f));
+ break;
+ case PC_LATCH_TO_NEAREST_SLAVE:
+ channel.resize(mVertices.size(), PaintedVertex(0, 0.0f));
+ break;
+ case PC_LATCH_TO_NEAREST_MASTER:
+ channel.resize(mVertices.size(), PaintedVertex(0xffffffff, 0.0f));
+ break;
+ default:
+#if PX_WINDOWS_FAMILY
+ DebugBreak();
+#endif
+ break;
+ }
+ }
+ else if (!useCustomChannels)
+ {
+ channel.clear();
+ channel.resize(0);
+ }
+ }
+
+ if (mSubMeshes.size() < 1)
+ {
+ mSubMeshes.resize(1);
+ mSubMeshes[0].init();
+ mSubMeshes[0].firstIndex = 0;
+ mSubMeshes[0].numIndices = (uint32_t)mIndices.size();
+ mSubMeshes[0].name = "One Single Mesh";
+ }
+ else if (mSubMeshes.size() == 1)
+ {
+ mSubMeshes[0].firstIndex = 0;
+ mSubMeshes[0].numIndices = (uint32_t)mIndices.size();
+ }
+ else
+ {
+ // Make sure all submeshes have distinct areas of the index buffers
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ if (mSubMeshes[i].name.empty())
+ {
+ char buf[32];
+ physx::shdfnd::snprintf(buf, 32, "Submesh %d", i);
+ mSubMeshes[i].name = buf;
+ }
+ else
+ {
+ if (mSubMeshes[i].name.find("_invisible") != std::string::npos)
+ {
+ mSubMeshes[i].invisible = true;
+ }
+ }
+ for (uint32_t j = i + 1; j < mSubMeshes.size(); j++)
+ {
+ PX_ASSERT(mSubMeshes[i].firstIndex + mSubMeshes[i].numIndices <= mSubMeshes[j].firstIndex ||
+ mSubMeshes[j].firstIndex + mSubMeshes[j].numIndices <= mSubMeshes[i].firstIndex);
+ }
+ }
+ // Merge submeshes
+ std::sort(mSubMeshes.begin(), mSubMeshes.end());
+ std::vector<uint32_t> newIndices;
+ newIndices.resize(mIndices.size());
+ uint32_t offset = 0;
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ uint32_t indexStart = mSubMeshes[i].firstIndex;
+ for (uint32_t j = 0; j < mSubMeshes[i].numIndices; j++)
+ {
+ newIndices[offset + j] = mIndices[indexStart + j];
+ }
+ mSubMeshes[i].firstIndex = offset;
+ offset += mSubMeshes[i].numIndices;
+ }
+ mIndices.resize(newIndices.size());
+ memcpy(&mIndices[0], &newIndices[0], sizeof(uint32_t) * newIndices.size());
+
+ for (size_t i = mSubMeshes.size() - 1; i > 0; i--)
+ {
+ if (mSubMeshes[i].name == mSubMeshes[i - 1].name && mSubMeshes[i].materialName == mSubMeshes[i - 1].materialName)
+ {
+ mSubMeshes[i - 1].numIndices += mSubMeshes[i].numIndices;
+ mSubMeshes[i].numIndices = 0;
+ }
+ }
+ for (uint32_t i = (uint32_t)mSubMeshes.size() - 1; i > 0; i--)
+ {
+ if (mSubMeshes[i].numIndices == 0)
+ {
+ mSubMeshes.erase(mSubMeshes.begin() + i);
+ }
+ }
+ }
+
+ if (mNormals.size() != mVertices.size())
+ {
+ updateNormals(-1);
+ }
+
+ if (mTangents.size() != mVertices.size())
+ {
+ updateTangents();
+ }
+
+ updateBoneWeights();
+ updateBounds();
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::hasRandomColors(size_t howmany)
+{
+ for (size_t i = mRandomColors.size(); i < howmany; i++)
+ {
+ const float h = physx::shdfnd::rand(0.0f, 359.9f);
+ const float s = physx::shdfnd::rand(0.5f, 1.0f);
+ const float v = physx::shdfnd::rand(0.5f, 1.0f);
+
+ const uint32_t hi = (uint32_t)physx::PxFloor(h / 60.0f) % 6;
+ const float f = (h / 60.0f - physx::PxFloor(h / 60.0f));
+ const float p = v * (1.0f - s);
+ const float q = v * (1.0f - f * s);
+ const float t = v * (1.0f - (1.0f - f) * s);
+
+ float r, g, b;
+ switch (hi)
+ {
+ case 0:
+ r = v;
+ g = t;
+ b = p;
+ break;
+ case 1:
+ r = q;
+ g = v;
+ b = p;
+ break;
+ case 2:
+ r = p;
+ g = v;
+ b = t;
+ break;
+ case 3:
+ r = p;
+ g = q;
+ b = v;
+ break;
+ case 4:
+ r = t;
+ g = p;
+ b = v;
+ break;
+ case 5:
+ r = v;
+ g = p;
+ b = q;
+ break;
+ default:
+ r = g = b = 0.0f;
+ break;
+ }
+ union
+ {
+ uint32_t color;
+ uint8_t components[4];
+ };
+ color = 0;
+ components[0] = (uint8_t)(r * 255);
+ components[1] = (uint8_t)(g * 255);
+ components[2] = (uint8_t)(b * 255);
+ mRandomColors.push_back(color);
+ }
+}
+
+// ----------------------------------------------------------------------
+void TriangleMesh::updateSubmeshInfo()
+{
+ uint32_t selectionChanged = 0;
+ uint32_t numNotSelected = 0;
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ selectionChanged += (mSubMeshes[i].selected != mSubMeshes[i].selectionActivated) ? 1 : 0;
+ numNotSelected += mSubMeshes[i].selected ? 0 : 1;
+ }
+ if (mActiveSubmeshVertices.size() == mVertices.size() && selectionChanged == 0)
+ {
+ return;
+ }
+
+ mActiveSubmeshVertices.clear();
+ if (numNotSelected == 0)
+ {
+ mActiveSubmeshVertices.resize(mVertices.size(), true);
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ mSubMeshes[i].selectionActivated = mSubMeshes[i].selected;
+ }
+
+ return;
+ }
+
+ mActiveSubmeshVertices.resize(mVertices.size(), false);
+ for (uint32_t i = 0; i < mSubMeshes.size(); i++)
+ {
+ if (mSubMeshes[i].selected)
+ {
+ TriangleSubMesh& sm = mSubMeshes[i];
+ for (uint32_t j = sm.firstIndex; j < sm.firstIndex + sm.numIndices; j++)
+ {
+ mActiveSubmeshVertices[mIndices[j]] = true;
+ }
+ }
+ mSubMeshes[i].selectionActivated = mSubMeshes[i].selected;
+ }
+}
+
+//------------------------------------------------------------------------------------
+int TriangleMesh::addSplitVert(int vertNr0, int vertNr1)
+{
+ int v0, v1;
+ if (vertNr0 < vertNr1)
+ {
+ v0 = vertNr0;
+ v1 = vertNr1;
+ }
+ else
+ {
+ v0 = vertNr1;
+ v1 = vertNr0;
+ }
+
+ // do we already have the split vert?
+ int i = mVertexFirstSplit[(uint32_t)v0];
+ while (i >= 0)
+ {
+ TriangleEdgeSplit& s = mVertexSplits[(uint32_t)i];
+ if (s.adjVertNr == v1)
+ {
+ return s.newVertNr;
+ }
+ i = s.next;
+ }
+
+ TriangleEdgeSplit sNew;
+ sNew.adjVertNr = v1;
+ sNew.newVertNr = (int32_t)mVertices.size();
+
+ // create interpolated vertex
+ physx::PxVec3 v = (mVertices[(uint32_t)v0] + mVertices[(uint32_t)v1]) * 0.5f;
+ mVertices.push_back(v);
+ if (mSkinnedVertices.size() == mVertices.size() - 1)
+ {
+ physx::PxVec3 sv = (mSkinnedVertices[(uint32_t)v0] + mSkinnedVertices[(uint32_t)v1]) * 0.5f;
+ mSkinnedVertices.push_back(sv);
+ }
+
+ for (uint32_t j = 0; j < PC_NUM_CHANNELS; j++)
+ {
+ // ok, special case, if it's collision distance, and one of them is < -1 then the new is as well
+ float v = 0.0f;
+ unsigned int u = 0;
+ if (j == PC_LATCH_TO_NEAREST_SLAVE || j == PC_LATCH_TO_NEAREST_MASTER)
+ {
+ u = mPaintChannels[j][(uint32_t)v0].paintValueU32 | mPaintChannels[j][(uint32_t)v1].paintValueU32; // bitwise OR
+ }
+ else if (j == PC_COLLISION_DISTANCE && (mPaintChannels[PC_COLLISION_DISTANCE][(uint32_t)v0].paintValueF32 == -1.1f || mPaintChannels[PC_COLLISION_DISTANCE][(uint32_t)v1].paintValueF32 == -1.1f))
+ {
+ v = -1.1f;
+ }
+ else
+ {
+ v = (mPaintChannels[j][(uint32_t)v0].paintValueF32 + mPaintChannels[j][(uint32_t)v1].paintValueF32) * 0.5f;
+ }
+ mPaintChannels[j].push_back(PaintedVertex(u, v));
+ }
+
+ for (uint32_t j = 0; j < NUM_TEXCOORDS; j++)
+ {
+ if (mTexCoords[j].empty())
+ {
+ continue;
+ }
+
+ nvidia::VertexUV newTc;
+ newTc.u = (mTexCoords[j][(uint32_t)v0].u + mTexCoords[j][(uint32_t)v1].u) * 0.5f;
+ newTc.v = (mTexCoords[j][(uint32_t)v0].v + mTexCoords[j][(uint32_t)v1].v) * 0.5f;
+ mTexCoords[j].push_back(newTc);
+ }
+
+ int newVertNr = (int)mVertices.size() - 1;
+ PX_UNUSED(newVertNr);
+
+ // mix bone weights - kind of tricky
+#if 1
+ if (!mBoneIndicesExternal.empty())
+ {
+ uint16_t newIndices[4];
+ physx::PxVec4 newWeights;
+ for (int j = 0; j < 4; j++)
+ {
+ newIndices[j] = 0;
+ newWeights[j] = 0.0f;
+ }
+
+ PX_ASSERT(!mBoneWeights.empty());
+
+ for (uint32_t k = 0; k < 2; k++)
+ {
+ uint32_t v = uint32_t((k == 0) ? v0 : v1);
+ for (uint32_t j = 0; j < 4; j++)
+ {
+ uint16_t bi = mBoneIndicesExternal[4 * v + j];
+ float bw = mBoneWeights[v][j];
+
+ // do we have the index already? If so just average the weights
+ int k = 0;
+ while (k < 4 && newIndices[(uint32_t)k] != bi)
+ {
+ k++;
+ }
+ if (k < 4)
+ {
+ newWeights[k] = (newWeights[(uint32_t)k] + bw) * 0.5f;
+ continue;
+ }
+ // else insert the pair at the right place
+ k = 3;
+ while (k >= 0 && newWeights[(uint32_t)k] < bw)
+ {
+ if (k < 3)
+ {
+ newWeights[(uint32_t)k + 1] = newWeights[(uint32_t)k];
+ newIndices[(uint32_t)k + 1] = newIndices[(uint32_t)k];
+ }
+ k--;
+ }
+ k++;
+ if (k < 4)
+ {
+ newWeights[(uint32_t)k] = bw;
+ newIndices[(uint32_t)k] = bi;
+ }
+ }
+ }
+
+ // copy indices to the new vertex
+ float sum = 0.0f;
+ for (int j = 0; j < 4; j++)
+ {
+ sum += newWeights[j];
+ }
+
+ if (sum > 0.0f)
+ {
+ sum = 1.0f / sum;
+ }
+
+ for (int j = 0; j < 4; j++)
+ {
+ mBoneIndicesExternal.push_back(newIndices[j]);
+ }
+ mBoneWeights.push_back(newWeights * sum);
+ }
+
+#else
+ mFirstAssignment.resize(newVertNr + 2);
+ mFirstAssignment[newVertNr] = mBoneAssignments.size();
+
+ std::vector<BoneAssignment> assigns;
+ BoneAssignment a;
+
+ for (int i = 0; i < 2; i++)
+ {
+ int v = (i == 0) ? v0 : v1;
+ int first = mFirstAssignment[v];
+ int num = mFirstAssignment[v + 1] - first;
+ for (int j = 0; j < num; j++)
+ {
+ a.boneNr = mBoneAssignments[first + j];
+ a.weight = mBoneWeights[first + j];
+ int k = 0;
+ while (k < (int)assigns.size() && a.boneNr != assigns[k].boneNr)
+ {
+ k++;
+ }
+ if (k < (int)assigns.size())
+ {
+ assigns[k].weight = (assigns[k].weight + a.weight) * 0.5f;
+ }
+ else
+ {
+ assigns.pushBack(a);
+ }
+ }
+ }
+ // select the ones with the biggest weights
+ std::sort(assigns.begin(), assigns.end());
+ const int maxAssignments = 4;
+ int first = assigns.size() - maxAssignments;
+ if (first < 0)
+ {
+ first = 0;
+ }
+ float sum = 0.0f;
+ for (int i = first; i < (int)assigns.size(); i++)
+ {
+ sum += assigns[i].weight;
+ }
+ PX_ASSERT(sum != 0.0f);
+ for (int i = first; i < (int)assigns.size(); i++)
+ {
+ mBoneAssignments.pushBack(assigns[i].boneNr);
+ mBoneWeights.pushBack(assigns[i].weight / sum);
+ }
+ mFirstAssignment[newVertNr + 1] = mBoneAssignments.size();
+#endif
+
+ // add split vertex info
+ sNew.next = mVertexFirstSplit[(uint32_t)v0];
+ mVertexFirstSplit[(uint32_t)v0] = (int32_t)mVertexSplits.size();
+ mVertexSplits.push_back(sNew);
+ return sNew.newVertNr;
+}
+
+} // namespace Samples