aboutsummaryrefslogtreecommitdiff
path: root/APEX_1.4/framework/src/ApexRenderMeshActor.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/framework/src/ApexRenderMeshActor.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/framework/src/ApexRenderMeshActor.cpp')
-rw-r--r--APEX_1.4/framework/src/ApexRenderMeshActor.cpp2784
1 files changed, 2784 insertions, 0 deletions
diff --git a/APEX_1.4/framework/src/ApexRenderMeshActor.cpp b/APEX_1.4/framework/src/ApexRenderMeshActor.cpp
new file mode 100644
index 00000000..a7bdc298
--- /dev/null
+++ b/APEX_1.4/framework/src/ApexRenderMeshActor.cpp
@@ -0,0 +1,2784 @@
+/*
+ * 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 "ApexRenderMeshActor.h"
+#include "FrameworkPerfScope.h"
+#include "PsAllocator.h"
+#include "RenderDebugInterface.h"
+#include "DebugRenderParams.h"
+
+#include "RenderContext.h"
+#include "RenderMeshActorDesc.h"
+#include "UserRenderBoneBuffer.h"
+#include "UserRenderBoneBufferDesc.h"
+#include "UserRenderIndexBuffer.h"
+#include "UserRenderIndexBufferDesc.h"
+#include "UserRenderResource.h"
+#include "UserRenderResourceDesc.h"
+#include "UserRenderVertexBuffer.h"
+#include "UserRenderer.h"
+
+#include "PsIntrinsics.h"
+#include "PxProfiler.h"
+
+#define VERBOSE 0
+
+namespace nvidia
+{
+namespace apex
+{
+// ApexRenderMeshActor methods
+
+ApexRenderMeshActor::ApexRenderMeshActor(const RenderMeshActorDesc& desc, ApexRenderMeshAsset& asset, ResourceList& list) :
+ mRenderMeshAsset(&asset),
+ mIndexBufferHint(RenderBufferHint::STATIC),
+ mMaxInstanceCount(0),
+ mInstanceCount(0),
+ mInstanceOffset(0),
+ mInstanceBuffer(NULL),
+ mRenderResource(NULL),
+ mRenderWithoutSkinning(false),
+ mForceBoneIndexChannel(false),
+ mApiVisibilityChanged(false),
+ mPartVisibilityChanged(false),
+ mInstanceCountChanged(false),
+ mKeepVisibleBonesPacked(false),
+ mForceFallbackSkinning(false),
+ mBonePosesDirty(false),
+ mOneUserVertexBufferChanged(false),
+ mBoneBufferInUse(false),
+ mReleaseResourcesIfNothingToRender(true),
+ mCreateRenderResourcesAfterInit(false),
+ mBufferVisibility(false),
+ mKeepPreviousFrameBoneBuffer(false),
+ mPreviousFrameBoneBufferValid(false),
+ mSkinningMode(RenderMeshActorSkinningMode::Default)
+{
+#if ENABLE_INSTANCED_MESH_CLEANUP_HACK
+ mOrderedInstanceTemp = 0;
+ mOrderedInstanceTempSize = 0;
+#endif
+ list.add(*this);
+ init(desc, (uint16_t) asset.getPartCount(), (uint16_t) asset.getBoneCount());
+}
+
+ApexRenderMeshActor::~ApexRenderMeshActor()
+{
+#if ENABLE_INSTANCED_MESH_CLEANUP_HACK
+ if (mOrderedInstanceTemp)
+ {
+ PX_FREE(mOrderedInstanceTemp);
+ mOrderedInstanceTemp = 0;
+ }
+#endif
+}
+
+void ApexRenderMeshActor::release()
+{
+ if (mInRelease)
+ {
+ return;
+ }
+ mInRelease = true;
+ mRenderMeshAsset->releaseActor(*this);
+}
+
+void ApexRenderMeshActor::destroy()
+{
+ ApexActor::destroy();
+
+ releaseRenderResources();
+
+ // Release named resources
+ ResourceProviderIntl* resourceProvider = GetInternalApexSDK()->getInternalResourceProvider();
+ if (resourceProvider != NULL)
+ {
+ for (uint32_t i = 0; i < mSubmeshData.size(); i++)
+ {
+ if (mSubmeshData[i].materialID != mRenderMeshAsset->mMaterialIDs[i])
+ {
+ resourceProvider->releaseResource(mSubmeshData[i].materialID);
+ }
+ mSubmeshData[i].materialID = INVALID_RESOURCE_ID;
+ mSubmeshData[i].material = NULL;
+ mSubmeshData[i].isMaterialPointerValid = false;
+ }
+ }
+
+ delete this;
+}
+
+
+void ApexRenderMeshActor::loadMaterial(SubmeshData& submeshData)
+{
+ ResourceProviderIntl* resourceProvider = GetInternalApexSDK()->getInternalResourceProvider();
+ submeshData.material = resourceProvider->getResource(submeshData.materialID);
+ submeshData.isMaterialPointerValid = true;
+
+ if (submeshData.material != NULL)
+ {
+ UserRenderResourceManager* rrm = GetInternalApexSDK()->getUserRenderResourceManager();
+ submeshData.maxBonesPerMaterial = rrm->getMaxBonesForMaterial(submeshData.material);
+
+ if (submeshData.maxBonesPerMaterial == 0)
+ {
+ submeshData.maxBonesPerMaterial = mRenderMeshAsset->getBoneCount();
+ }
+ }
+}
+
+
+void ApexRenderMeshActor::init(const RenderMeshActorDesc& desc, uint16_t partCount, uint16_t boneCount)
+{
+ // TODO - LRR - This happened once, it shouldn't happen any more, let me know if it does
+ //PX_ASSERT(boneCount != 0);
+ //if (boneCount == 0)
+ // boneCount = partCount;
+
+ mRenderWithoutSkinning = desc.renderWithoutSkinning;
+ mForceBoneIndexChannel = desc.forceBoneIndexChannel;
+
+ if (mRenderWithoutSkinning)
+ {
+ boneCount = 1;
+ }
+
+ const uint32_t oldBoneCount = mTransforms.size();
+ mTransforms.resize(boneCount);
+ for (uint32_t i = oldBoneCount; i < boneCount; ++i)
+ {
+ mTransforms[i] = PxMat44(PxIdentity);
+ }
+
+ mVisiblePartsForAPI.reserve(partCount);
+ mVisiblePartsForAPI.lockCapacity(true);
+ mVisiblePartsForRendering.reserve(partCount);
+ mVisiblePartsForRendering.reset(); // size to 0, without reallocating
+
+ mBufferVisibility = desc.bufferVisibility;
+
+ mKeepPreviousFrameBoneBuffer = desc.keepPreviousFrameBoneBuffer;
+ mPreviousFrameBoneBufferValid = false;
+
+ mApiVisibilityChanged = true;
+ mPartVisibilityChanged = !mBufferVisibility;
+
+ for (uint32_t i = 0; i < partCount; ++i)
+ {
+ setVisibility(desc.visible, (uint16_t) i);
+ }
+
+ mIndexBufferHint = desc.indexBufferHint;
+ mMaxInstanceCount = desc.maxInstanceCount;
+ mInstanceCount = 0;
+ mInstanceOffset = 0;
+ mInstanceBuffer = NULL;
+
+ if (desc.keepVisibleBonesPacked && !mRenderWithoutSkinning)
+ {
+ if (mRenderMeshAsset->getBoneCount() == mRenderMeshAsset->getPartCount())
+ {
+ mKeepVisibleBonesPacked = true;
+ }
+ else
+ {
+ APEX_INVALID_PARAMETER("RenderMeshActorDesc::keepVisibleBonesPacked is only allowed when the number of bones (%d) equals the number of parts (%d)\n",
+ mRenderMeshAsset->getBoneCount(), mRenderMeshAsset->getPartCount());
+ }
+ }
+
+ if (desc.forceFallbackSkinning)
+ {
+ if (mKeepVisibleBonesPacked)
+ {
+ APEX_INVALID_PARAMETER("RenderMeshActorDesc::forceFallbackSkinning can not be used when RenderMeshActorDesc::keepVisibleBonesPacked is also used!\n");
+ }
+ else
+ {
+ mForceFallbackSkinning = true;
+ }
+ }
+
+ ResourceProviderIntl* resourceProvider = GetInternalApexSDK()->getInternalResourceProvider();
+ ResID materialNS = GetInternalApexSDK()->getMaterialNameSpace();
+
+ bool loadMaterialsOnActorCreation = !GetInternalApexSDK()->getRMALoadMaterialsLazily();
+
+ if (mRenderWithoutSkinning)
+ {
+ // make sure that createRenderResources() is called in this special case!
+ mCreateRenderResourcesAfterInit = true;
+ }
+
+ mSubmeshData.reserve(mRenderMeshAsset->getSubmeshCount());
+ for (uint32_t submeshIndex = 0; submeshIndex < mRenderMeshAsset->getSubmeshCount(); submeshIndex++)
+ {
+ SubmeshData submeshData;
+
+ // Resolve override material names using the NRP...
+ if (submeshIndex < desc.overrideMaterialCount && resourceProvider != NULL)
+ {
+ submeshData.materialID = resourceProvider->createResource(materialNS, desc.overrideMaterials[submeshIndex]);;
+ }
+ else
+ {
+ submeshData.materialID = mRenderMeshAsset->mMaterialIDs[submeshIndex];
+ }
+
+ submeshData.maxBonesPerMaterial = 0;
+
+ if (loadMaterialsOnActorCreation)
+ {
+ loadMaterial(submeshData);
+ }
+
+ mSubmeshData.pushBack(submeshData);
+ }
+}
+
+bool ApexRenderMeshActor::setVisibility(bool visible, uint16_t partIndex)
+{
+ WRITE_ZONE();
+ uint32_t oldRank = UINT32_MAX, newRank = UINT32_MAX;
+
+ bool changed;
+
+ if (visible)
+ {
+ changed = mVisiblePartsForAPI.useAndReturnRanks(partIndex, newRank, oldRank);
+ if (changed)
+ {
+ mApiVisibilityChanged = true;
+ if (!mBufferVisibility)
+ {
+ mPartVisibilityChanged = true;
+ }
+ if (mKeepVisibleBonesPacked && newRank != oldRank)
+ {
+ mTMSwapBuffer.pushBack(newRank << 16 | oldRank);
+ }
+ }
+ }
+ else
+ {
+ changed = mVisiblePartsForAPI.freeAndReturnRanks(partIndex, newRank, oldRank);
+ if (changed)
+ {
+ mApiVisibilityChanged = true;
+ if (!mBufferVisibility)
+ {
+ mPartVisibilityChanged = true;
+ }
+ if (mKeepVisibleBonesPacked && newRank != oldRank)
+ {
+ mTMSwapBuffer.pushBack(newRank << 16 | oldRank);
+ }
+ }
+ }
+
+ return changed;
+}
+
+bool ApexRenderMeshActor::getVisibilities(uint8_t* visibilityArray, uint32_t visibilityArraySize) const
+{
+ READ_ZONE();
+ uint8_t changed = 0;
+ const uint32_t numParts = PxMin(mRenderMeshAsset->getPartCount(), visibilityArraySize);
+ for (uint32_t index = 0; index < numParts; ++index)
+ {
+ const uint8_t newVisibility = (uint8_t)isVisible((uint16_t) index);
+ changed |= newVisibility ^(*visibilityArray);
+ *visibilityArray++ = newVisibility;
+ }
+ return changed != 0;
+}
+
+void ApexRenderMeshActor::updateRenderResources(bool rewriteBuffers, void* userRenderData)
+{
+ updateRenderResources(!mRenderWithoutSkinning, rewriteBuffers, userRenderData);
+}
+
+void ApexRenderMeshActor::updateRenderResources(bool useBones, bool rewriteBuffers, void* userRenderData)
+{
+ URR_SCOPE;
+
+#if VERBOSE > 1
+ printf("updateRenderResources(useBones=%s, rewriteBuffers=%s, userRenderData=0x%p)\n", useBones ? "true" : "false", rewriteBuffers ? "true" : "false", userRenderData);
+#endif
+
+ UserRenderResourceManager* rrm = GetInternalApexSDK()->getUserRenderResourceManager();
+
+ // fill out maxBonesPerMaterial (if it hasn't already been filled out). Also create fallback skinning if necessary.
+ for (uint32_t submeshIndex = 0; submeshIndex < mSubmeshData.size(); submeshIndex++)
+ {
+ SubmeshData& submeshData = mSubmeshData[submeshIndex];
+
+ if (submeshData.maxBonesPerMaterial == 0 && rrm != NULL)
+ {
+ if (!submeshData.isMaterialPointerValid)
+ {
+ // this should only be reached, when renderMeshActorLoadMaterialsLazily is true.
+ // URR may not be called asynchronously in that case (for example in a render thread)
+ ResourceProviderIntl* nrp = GetInternalApexSDK()->getInternalResourceProvider();
+ submeshData.material = nrp->getResource(submeshData.materialID);
+ submeshData.isMaterialPointerValid = true;
+ }
+
+ submeshData.maxBonesPerMaterial = rrm->getMaxBonesForMaterial(submeshData.material);
+
+ if (submeshData.maxBonesPerMaterial == 0)
+ {
+ submeshData.maxBonesPerMaterial = mRenderMeshAsset->getBoneCount();
+ }
+ }
+
+ bool needsFallbackSkinning = mForceFallbackSkinning || submeshData.maxBonesPerMaterial < mTransforms.size();
+ if (needsFallbackSkinning && !mKeepVisibleBonesPacked && submeshData.fallbackSkinningMemory == NULL)
+ {
+ createFallbackSkinning(submeshIndex);
+ }
+ }
+
+ PX_PROFILE_ZONE("ApexRenderMesh::updateRenderResources", GetInternalApexSDK()->getContextId());
+
+ const bool invisible = visiblePartCount() == 0;
+ const bool instanceless = mMaxInstanceCount > 0 && mInstanceCount == 0;
+ if ((mReleaseResourcesIfNothingToRender && ((mPartVisibilityChanged && invisible) || (mInstanceCountChanged && instanceless))) || rewriteBuffers)
+ {
+ // First send out signals that the resource is no longer needed.
+ for (uint32_t submeshIndex = 0; submeshIndex < mSubmeshData.size(); ++submeshIndex)
+ {
+ SubmeshData& submeshData = mSubmeshData[submeshIndex];
+ for (uint32_t i = 0; i < submeshData.renderResources.size(); ++i)
+ {
+ UserRenderResource* renderResource = submeshData.renderResources[i].resource;
+ if (renderResource != NULL)
+ {
+ if (renderResource->getBoneBuffer() != NULL)
+ {
+ renderResource->setBoneBufferRange(0, 0);
+ }
+ if (renderResource->getInstanceBuffer() != NULL)
+ {
+ renderResource->setInstanceBufferRange(0, 0);
+ }
+ }
+ }
+ }
+
+ // Now release the resources
+ releaseRenderResources();
+ mPartVisibilityChanged = false;
+ mBonePosesDirty = false;
+ mInstanceCountChanged = false;
+
+ // Rewrite buffers condition
+ if (rewriteBuffers)
+ {
+ mCreateRenderResourcesAfterInit = true; // createRenderResources
+ mPartVisibilityChanged = true; // writeBuffer for submesh data
+ }
+
+ return;
+ }
+
+ if (mCreateRenderResourcesAfterInit || mOneUserVertexBufferChanged || mPartVisibilityChanged || mBoneBufferInUse != useBones)
+ {
+ createRenderResources(useBones, userRenderData);
+ mCreateRenderResourcesAfterInit = false;
+ }
+ if (mRenderResource)
+ {
+ mRenderResource->setInstanceBufferRange(mInstanceOffset, mInstanceCount);
+ }
+
+ PX_ASSERT(mSubmeshData.size() == mRenderMeshAsset->getSubmeshCount());
+
+ for (uint32_t submeshIndex = 0; submeshIndex < mRenderMeshAsset->getSubmeshCount(); ++submeshIndex)
+ {
+ SubmeshData& submeshData = mSubmeshData[submeshIndex];
+
+ if (submeshData.indexBuffer == NULL)
+ {
+ continue;
+ }
+
+ if (mPartVisibilityChanged || submeshData.staticColorReplacementDirty)
+ {
+ updatePartVisibility(submeshIndex, useBones, userRenderData);
+ submeshData.staticColorReplacementDirty = false;
+ }
+ if (mBonePosesDirty)
+ {
+ if (submeshData.userDynamicVertexBuffer && !submeshData.userSpecifiedData)
+ {
+ updateFallbackSkinning(submeshIndex);
+ }
+
+ // Set up the previous bone buffer, if requested and available. If we're packing bones, we need to do a remapping
+ PX_ASSERT(!mPreviousFrameBoneBufferValid || mTransformsLastFrame.size() == mTransforms.size());
+ const uint32_t tmBufferSize = mKeepVisibleBonesPacked ? getRenderVisiblePartCount() : mTransforms.size();
+ if (mPreviousFrameBoneBufferValid && mTransformsLastFrame.size() == mTransforms.size() && mKeepVisibleBonesPacked)
+ {
+ mRemappedPreviousBoneTMs.resize(tmBufferSize);
+ for (uint32_t tmNum = 0; tmNum < tmBufferSize; ++tmNum)
+ {
+ mRemappedPreviousBoneTMs[tmNum] = mTransformsLastFrame[mVisiblePartsForAPILastFrame.getRank(mVisiblePartsForAPI.usedIndices()[tmNum])];
+ }
+ }
+ else
+ {
+ mRemappedPreviousBoneTMs.resize(0);
+ }
+
+ updateBonePoses(submeshIndex);
+
+ // move this under the render lock because the fracture buffer processing accesses these arrays
+ // this used to be at the end of dispatchRenderResources
+ if (mKeepPreviousFrameBoneBuffer)
+ {
+ PX_ASSERT(mTransforms.size() != 0);
+ mTransformsLastFrame = mTransforms;
+ mVisiblePartsForAPILastFrame = mVisiblePartsForAPI;
+ mPreviousFrameBoneBufferValid = true;
+ }
+ }
+
+ if (submeshData.userDynamicVertexBuffer && (submeshData.fallbackSkinningDirty || submeshData.userVertexBufferAlwaysDirty))
+ {
+ writeUserBuffers(submeshIndex);
+ submeshData.fallbackSkinningDirty = false;
+ }
+
+ if (mMaxInstanceCount)
+ {
+ updateInstances(submeshIndex);
+ }
+
+ if (!submeshData.isMaterialPointerValid)
+ {
+ // this should only be reached, when renderMeshActorLoadMaterialsLazily is true.
+ // URR may not be called asynchronously in that case (for example in a render thread)
+ ResourceProviderIntl* nrp = GetInternalApexSDK()->getInternalResourceProvider();
+ submeshData.material = nrp->getResource(submeshData.materialID);
+ submeshData.isMaterialPointerValid = true;
+ }
+
+ for (uint32_t i = 0; i < submeshData.renderResources.size(); ++i)
+ {
+ UserRenderResource* res = submeshData.renderResources[i].resource;
+ if (res != NULL)
+ {
+ // LRR - poor workaround for http://nvbugs/534501, you'll crash here if you have more than 60 bones/material
+ // and keepVisibleBonesPacked == false
+ res->setMaterial(submeshData.material);
+ }
+ }
+ }
+ mBonePosesDirty = false;
+ mPartVisibilityChanged = false;
+}
+
+
+
+void ApexRenderMeshActor::dispatchRenderResources(UserRenderer& renderer)
+{
+ dispatchRenderResources(renderer, PxMat44(PxIdentity));
+}
+
+
+
+void ApexRenderMeshActor::dispatchRenderResources(UserRenderer& renderer, const PxMat44& globalPose)
+{
+ PX_PROFILE_ZONE("ApexRenderMesh::dispatchRenderResources", GetInternalApexSDK()->getContextId());
+
+ RenderContext context;
+
+ // Assign the transform to the context when there is 1 part and no instancing
+
+ // if there are no parts to render, return early
+ // else if using instancing and there are not instances, return early
+ // else if not using instancing and there is just 1 part (no bone buffer), save the transform to the context
+ // else (using instancing and/or multiple parts), just assign identity to context
+ if (mRenderMeshAsset->getPartCount() == 0 && !mRenderMeshAsset->getOpaqueMesh())
+ {
+ return;
+ }
+ else if (mInstanceCount == 0 && mMaxInstanceCount > 0)
+ {
+ return;
+ }
+ else if (mMaxInstanceCount == 0 && mTransforms.size() == 1)
+ {
+ context.local2world = globalPose * mTransforms[0]; // provide context for non-instanced ARMs with a single bone
+ context.world2local = context.local2world.inverseRT();
+ }
+ else
+ {
+ context.local2world = globalPose;
+ context.world2local = globalPose.inverseRT();
+ }
+ if (mRenderMeshAsset->getOpaqueMesh())
+ {
+ if (mRenderResource)
+ {
+ context.renderResource = mRenderResource;
+ renderer.renderResource(context);
+ }
+ }
+ else
+ {
+ for (uint32_t submeshIndex = 0; submeshIndex < mSubmeshData.size(); ++submeshIndex)
+ {
+ for (uint32_t i = 0; i < mSubmeshData[submeshIndex].renderResources.size(); ++i)
+ {
+ context.renderResource = mSubmeshData[submeshIndex].renderResources[i].resource;
+
+ // no reason to render if we don't have any indices
+ if ((mSubmeshData[submeshIndex].indexBuffer && (mSubmeshData[submeshIndex].visibleTriangleCount == 0)) || (mSubmeshData[submeshIndex].renderResources[i].vertexCount == 0))
+ {
+ continue;
+ }
+
+ if (context.renderResource)
+ {
+ context.renderMeshName = mRenderMeshAsset->getName();
+ renderer.renderResource(context);
+ }
+ }
+ }
+ }
+}
+
+
+
+void ApexRenderMeshActor::addVertexBuffer(uint32_t submeshIndex, bool alwaysDirty, PxVec3* position, PxVec3* normal, PxVec4* tangents)
+{
+#if VERBOSE
+ GetInternalApexSDK()->getErrorCallback().reportError(PxErrorCode::eNO_ERROR, "addVertexBuffer\n", __FILE__, __LINE__);
+ printf("addVertexBuffer(submeshIndex=%d)\n", submeshIndex);
+#endif
+ if (submeshIndex < mSubmeshData.size())
+ {
+ SubmeshData& submeshData = mSubmeshData[submeshIndex];
+
+ if (submeshData.userSpecifiedData)
+ {
+ APEX_INVALID_PARAMETER("Cannot add user buffer to submesh %d, it's already assigned!", submeshIndex);
+ }
+ else
+ {
+ submeshData.userSpecifiedData = true;
+ submeshData.userPositions = position;
+ submeshData.userNormals = normal;
+ submeshData.userTangents4 = tangents;
+
+ submeshData.userVertexBufferAlwaysDirty = alwaysDirty;
+ mOneUserVertexBufferChanged = true;
+ }
+ }
+}
+
+
+
+void ApexRenderMeshActor::removeVertexBuffer(uint32_t submeshIndex)
+{
+#if VERBOSE
+ GetInternalApexSDK()->getErrorCallback().reportError(PxErrorCode::eNO_ERROR, "removeVertexBuffer\n", __FILE__, __LINE__);
+ printf("removeVertexBuffer(submeshIndex=%d)\n", submeshIndex);
+#endif
+ if (submeshIndex < mSubmeshData.size())
+ {
+ SubmeshData& submeshData = mSubmeshData[submeshIndex];
+
+ if (!submeshData.userSpecifiedData)
+ {
+ APEX_INVALID_PARAMETER("Cannot remove user buffer to submesh %d, it's not assigned!", submeshIndex);
+ }
+ else
+ {
+ submeshData.userSpecifiedData = false;
+ submeshData.userPositions = NULL;
+ submeshData.userNormals = NULL;
+ submeshData.userTangents4 = NULL;
+
+ submeshData.userVertexBufferAlwaysDirty = false;
+ mOneUserVertexBufferChanged = true;
+
+ if (submeshData.fallbackSkinningMemory != NULL)
+ {
+ distributeFallbackData(submeshIndex);
+ }
+ }
+ }
+}
+
+
+
+void ApexRenderMeshActor::setStaticPositionReplacement(uint32_t submeshIndex, const PxVec3* staticPositions)
+{
+ PX_ASSERT(staticPositions != NULL);
+
+ PX_ASSERT(submeshIndex < mSubmeshData.size());
+ if (submeshIndex < mSubmeshData.size())
+ {
+ SubmeshData& submeshData = mSubmeshData[submeshIndex];
+
+ PX_ASSERT(submeshData.staticPositionReplacement == NULL);
+ submeshData.staticPositionReplacement = staticPositions;
+
+ PX_ASSERT(submeshData.staticBufferReplacement == NULL);
+ PX_ASSERT(submeshData.dynamicBufferReplacement == NULL);
+ }
+}
+
+void ApexRenderMeshActor::setStaticColorReplacement(uint32_t submeshIndex, const ColorRGBA* staticColors)
+{
+ PX_ASSERT(staticColors != NULL);
+
+ PX_ASSERT(submeshIndex < mSubmeshData.size());
+ if (submeshIndex < mSubmeshData.size())
+ {
+ SubmeshData& submeshData = mSubmeshData[submeshIndex];
+
+ submeshData.staticColorReplacement = staticColors;
+ submeshData.staticColorReplacementDirty = true;
+ }
+}
+
+
+
+void ApexRenderMeshActor::setInstanceBuffer(UserRenderInstanceBuffer* instBuf)
+{
+ WRITE_ZONE();
+ mInstanceBuffer = instBuf;
+
+ for (nvidia::Array<SubmeshData>::Iterator it = mSubmeshData.begin(), end = mSubmeshData.end(); it != end; ++it)
+ {
+ it->instanceBuffer = mInstanceBuffer;
+ it->userIndexBufferChanged = true;
+ }
+
+ mOneUserVertexBufferChanged = true;
+}
+
+void ApexRenderMeshActor::setMaxInstanceCount(uint32_t count)
+{
+ WRITE_ZONE();
+ mMaxInstanceCount = count;
+}
+
+void ApexRenderMeshActor::setInstanceBufferRange(uint32_t from, uint32_t count)
+{
+ WRITE_ZONE();
+ mInstanceOffset = from;
+ mInstanceCountChanged = count != mInstanceCount;
+ mInstanceCount = count < mMaxInstanceCount ? count : mMaxInstanceCount;
+}
+
+
+
+void ApexRenderMeshActor::getLodRange(float& min, float& max, bool& intOnly) const
+{
+ READ_ZONE();
+ PX_UNUSED(min);
+ PX_UNUSED(max);
+ PX_UNUSED(intOnly);
+ APEX_INVALID_OPERATION("RenderMeshActor does not support this operation");
+}
+
+
+
+float ApexRenderMeshActor::getActiveLod() const
+{
+ READ_ZONE();
+ APEX_INVALID_OPERATION("RenderMeshActor does not support this operation");
+ return -1.0f;
+}
+
+void ApexRenderMeshActor::forceLod(float lod)
+{
+ WRITE_ZONE();
+ PX_UNUSED(lod);
+ APEX_INVALID_OPERATION("RenderMeshActor does not support this operation");
+}
+
+
+
+void ApexRenderMeshActor::createRenderResources(bool useBones, void* userRenderData)
+{
+#if VERBOSE
+ printf("createRenderResources(useBones=%s, userRenderData=0x%p)\n", useBones ? "true" : "false", userRenderData);
+#endif
+
+ PX_PROFILE_ZONE("ApexRenderMesh::createRenderResources", GetInternalApexSDK()->getContextId());
+
+ if (mRenderMeshAsset->getOpaqueMesh())
+ {
+ if (mRenderResource == NULL || mRenderResource->getInstanceBuffer() != mInstanceBuffer)
+ {
+ UserRenderResourceManager* rrm = GetInternalApexSDK()->getUserRenderResourceManager();
+ if (mRenderResource != NULL)
+ {
+ rrm->releaseResource(*mRenderResource);
+ mRenderResource = NULL;
+ }
+
+ UserRenderResourceDesc desc;
+ desc.instanceBuffer = mInstanceBuffer;
+ desc.opaqueMesh = mRenderMeshAsset->getOpaqueMesh();
+ desc.userRenderData = userRenderData;
+ mRenderResource = rrm->createResource(desc);
+ }
+ }
+
+ PX_ASSERT(mSubmeshData.size() == mRenderMeshAsset->getSubmeshCount());
+
+ UserRenderResourceManager* rrm = GetInternalApexSDK()->getUserRenderResourceManager();
+
+ bool createAndFillSharedVertexBuffersAll = mOneUserVertexBufferChanged;
+ if (mRenderMeshAsset->mRuntimeSubmeshData.empty())
+ {
+ mRenderMeshAsset->mRuntimeSubmeshData.resize(mRenderMeshAsset->getSubmeshCount());
+ memset(mRenderMeshAsset->mRuntimeSubmeshData.begin(), 0, sizeof(ApexRenderMeshAsset::SubmeshData) * mRenderMeshAsset->mRuntimeSubmeshData.size());
+ createAndFillSharedVertexBuffersAll = true;
+ }
+
+ bool fill2ndVertexBuffersAll = false;
+ if (!mPerActorVertexBuffers.size())
+ {
+ // Create a separate (instanced) buffer for bone indices and/or colors
+ fill2ndVertexBuffersAll = mKeepVisibleBonesPacked;
+ for (uint32_t submeshIndex = 0; !fill2ndVertexBuffersAll && submeshIndex < mRenderMeshAsset->getSubmeshCount(); ++submeshIndex)
+ {
+ fill2ndVertexBuffersAll = (mSubmeshData[submeshIndex].staticColorReplacement != NULL);
+ }
+ if (fill2ndVertexBuffersAll)
+ {
+ mPerActorVertexBuffers.resize(mRenderMeshAsset->getSubmeshCount());
+ }
+ }
+
+ PX_ASSERT(mRenderMeshAsset->mRuntimeSubmeshData.size() == mSubmeshData.size());
+ PX_ASSERT(mRenderMeshAsset->getSubmeshCount() == mSubmeshData.size());
+ for (uint32_t submeshIndex = 0; submeshIndex < mRenderMeshAsset->getSubmeshCount(); ++submeshIndex)
+ {
+ ApexRenderSubmesh& submesh = *mRenderMeshAsset->mSubmeshes[submeshIndex];
+ SubmeshData& submeshData = mSubmeshData[submeshIndex];
+
+ if (submesh.getVertexBuffer().getVertexCount() == 0 || !submeshHasVisibleTriangles(submeshIndex))
+ {
+ for (uint32_t i = 0; i < submeshData.renderResources.size(); ++i)
+ {
+ if (submeshData.renderResources[i].resource != NULL)
+ {
+ UserRenderBoneBuffer* boneBuffer = submeshData.renderResources[i].resource->getBoneBuffer();
+ rrm->releaseResource(*submeshData.renderResources[i].resource);
+ if (boneBuffer)
+ {
+ rrm->releaseBoneBuffer(*boneBuffer);
+ }
+ submeshData.renderResources[i].resource = NULL;
+ }
+ }
+ continue;
+ }
+
+ bool fill2ndVertexBuffers = fill2ndVertexBuffersAll;
+ // Handling color replacement through "2nd vertex buffer"
+ if ((mKeepVisibleBonesPacked || submeshData.staticColorReplacement != NULL) && mPerActorVertexBuffers[submeshIndex] == NULL)
+ {
+ fill2ndVertexBuffers = true;
+ }
+
+ bool createAndFillSharedVertexBuffers = createAndFillSharedVertexBuffersAll;
+
+ // create vertex buffers if some buffer replacements are present in the actor
+ if (submeshData.staticPositionReplacement != NULL && submeshData.staticBufferReplacement == NULL && submeshData.dynamicBufferReplacement == NULL)
+ {
+ createAndFillSharedVertexBuffers = true;
+ }
+
+ {
+ ApexRenderMeshAsset::SubmeshData& runtimeSubmeshData = mRenderMeshAsset->mRuntimeSubmeshData[submeshIndex];
+ if (runtimeSubmeshData.staticVertexBuffer == NULL && runtimeSubmeshData.dynamicVertexBuffer == NULL && runtimeSubmeshData.skinningVertexBuffer == NULL)
+ {
+ createAndFillSharedVertexBuffers = true;
+ }
+
+ // create vertex buffers if not all static vertex buffers have been created by the previous actors that were doing this
+ if (runtimeSubmeshData.needsStaticData && runtimeSubmeshData.staticVertexBuffer == NULL)
+ {
+ createAndFillSharedVertexBuffers = true;
+ }
+
+ if (runtimeSubmeshData.needsDynamicData && runtimeSubmeshData.dynamicVertexBuffer == NULL)
+ {
+ createAndFillSharedVertexBuffers = true;
+ }
+ }
+
+
+ VertexBufferIntl& srcVB = submesh.getVertexBufferWritable();
+ const VertexFormat& vf = srcVB.getFormat();
+
+ bool fillStaticSharedVertexBuffer = false;
+ bool fillDynamicSharedVertexBuffer = false;
+ bool fillSkinningSharedVertexBuffer = false;
+
+ if (createAndFillSharedVertexBuffers)
+ {
+ UserRenderVertexBufferDesc staticBufDesc, dynamicBufDesc, boneBufDesc;
+ staticBufDesc.moduleIdentifier = mRenderMeshAsset->mOwnerModuleID;
+ staticBufDesc.maxVerts = srcVB.getVertexCount();
+ staticBufDesc.hint = RenderBufferHint::STATIC;
+ staticBufDesc.uvOrigin = mRenderMeshAsset->getTextureUVOrigin();
+ staticBufDesc.numCustomBuffers = 0;
+ staticBufDesc.canBeShared = true;
+
+ dynamicBufDesc = staticBufDesc;
+ boneBufDesc = dynamicBufDesc;
+ bool useDynamicBuffer = false;
+ bool replaceStaticBuffer = false;
+ bool replaceDynamicBuffer = false;
+
+ // extract all the buffers into one of the three descs
+ for (uint32_t i = 0; i < vf.getBufferCount(); ++i)
+ {
+ RenderVertexSemantic::Enum semantic = vf.getBufferSemantic(i);
+ if (semantic >= RenderVertexSemantic::POSITION && semantic <= RenderVertexSemantic::COLOR)
+ {
+ if (vf.getBufferAccess(i) == RenderDataAccess::STATIC)
+ {
+ staticBufDesc.buffersRequest[semantic] = vf.getBufferFormat(i);
+
+ if (semantic == RenderVertexSemantic::POSITION && submeshData.staticPositionReplacement != NULL)
+ {
+ replaceStaticBuffer = true;
+ }
+ }
+ else
+ {
+ dynamicBufDesc.buffersRequest[semantic] = vf.getBufferFormat(i);
+ useDynamicBuffer = true;
+
+ if (semantic == RenderVertexSemantic::POSITION && submeshData.staticPositionReplacement != NULL)
+ {
+ replaceDynamicBuffer = true;
+ }
+ }
+ }
+ else if (semantic == RenderVertexSemantic::CUSTOM)
+ {
+ ++staticBufDesc.numCustomBuffers;
+ }
+ }
+
+ if (staticBufDesc.numCustomBuffers)
+ {
+ staticBufDesc.customBuffersIdents = &mRenderMeshAsset->mRuntimeCustomSubmeshData[submeshIndex].customBufferVoidPtrs[0];
+ staticBufDesc.customBuffersRequest = &mRenderMeshAsset->mRuntimeCustomSubmeshData[submeshIndex].customBufferFormats[0];
+ }
+
+ // PH: only create bone indices/weights if more than one bone is present. one bone just needs local2world
+ if (mTransforms.size() > 1)
+ {
+ UserRenderVertexBufferDesc* boneDesc = vf.hasSeparateBoneBuffer() ? &boneBufDesc : &staticBufDesc;
+
+ if (!fill2ndVertexBuffers)
+ {
+ uint32_t bufferIndex = (uint32_t)vf.getBufferIndexFromID(vf.getSemanticID(RenderVertexSemantic::BONE_INDEX));
+ boneDesc->buffersRequest[RenderVertexSemantic::BONE_INDEX] = vf.getBufferFormat(bufferIndex);
+ bufferIndex = (uint32_t)vf.getBufferIndexFromID(vf.getSemanticID(RenderVertexSemantic::BONE_WEIGHT));
+ boneDesc->buffersRequest[RenderVertexSemantic::BONE_WEIGHT] = vf.getBufferFormat(bufferIndex);
+ }
+ }
+ else
+ if (mForceBoneIndexChannel)
+ {
+ // Note, it is assumed here that this means there's an actor which will handle dynamic parts, and will require a shared bone index buffer
+ UserRenderVertexBufferDesc* boneDesc = vf.hasSeparateBoneBuffer() ? &boneBufDesc : &staticBufDesc;
+
+ if (!fill2ndVertexBuffers)
+ {
+ uint32_t bufferIndex = (uint32_t)vf.getBufferIndexFromID(vf.getSemanticID(RenderVertexSemantic::BONE_INDEX));
+ boneDesc->buffersRequest[RenderVertexSemantic::BONE_INDEX] = vf.getBufferFormat(bufferIndex);
+ }
+ }
+
+ for (uint32_t semantic = RenderVertexSemantic::TEXCOORD0; semantic <= RenderVertexSemantic::TEXCOORD3; ++semantic)
+ {
+ uint32_t bufferIndex = (uint32_t)vf.getBufferIndexFromID(vf.getSemanticID((RenderVertexSemantic::Enum)semantic));
+ staticBufDesc.buffersRequest[ semantic ] = vf.getBufferFormat(bufferIndex);
+ }
+
+ {
+ uint32_t bufferIndex = (uint32_t)vf.getBufferIndexFromID(vf.getSemanticID(RenderVertexSemantic::DISPLACEMENT_TEXCOORD));
+ staticBufDesc.buffersRequest[ RenderVertexSemantic::DISPLACEMENT_TEXCOORD ] = vf.getBufferFormat(bufferIndex);
+ bufferIndex = (uint32_t)vf.getBufferIndexFromID(vf.getSemanticID(RenderVertexSemantic::DISPLACEMENT_FLAGS));
+ staticBufDesc.buffersRequest[ RenderVertexSemantic::DISPLACEMENT_FLAGS ] = vf.getBufferFormat(bufferIndex);
+ }
+
+ // empty static buffer?
+ uint32_t numEntries = staticBufDesc.numCustomBuffers;
+ for (uint32_t i = 0; i < RenderVertexSemantic::NUM_SEMANTICS; i++)
+ {
+ numEntries += (staticBufDesc.buffersRequest[i] == RenderDataFormat::UNSPECIFIED) ? 0 : 1;
+
+ PX_ASSERT(staticBufDesc.buffersRequest[i] == RenderDataFormat::UNSPECIFIED || vertexSemanticFormatValid((RenderVertexSemantic::Enum)i, staticBufDesc.buffersRequest[i]));
+ PX_ASSERT(dynamicBufDesc.buffersRequest[i] == RenderDataFormat::UNSPECIFIED || vertexSemanticFormatValid((RenderVertexSemantic::Enum)i, dynamicBufDesc.buffersRequest[i]));
+ PX_ASSERT(boneBufDesc.buffersRequest[i] == RenderDataFormat::UNSPECIFIED || vertexSemanticFormatValid((RenderVertexSemantic::Enum)i, boneBufDesc.buffersRequest[i]));
+ }
+
+ if (numEntries > 0)
+ {
+ if (replaceStaticBuffer)
+ {
+ submeshData.staticBufferReplacement = rrm->createVertexBuffer(staticBufDesc);
+ fillStaticSharedVertexBuffer = true;
+ }
+ else if (mRenderMeshAsset->mRuntimeSubmeshData[submeshIndex].staticVertexBuffer == NULL)
+ {
+ mRenderMeshAsset->mRuntimeSubmeshData[submeshIndex].staticVertexBuffer = rrm->createVertexBuffer(staticBufDesc);
+ fillStaticSharedVertexBuffer = true;
+ }
+ mRenderMeshAsset->mRuntimeSubmeshData[submeshIndex].needsStaticData = true;
+ }
+
+ if (useDynamicBuffer)
+ {
+ // only create this if we don't create a per-actor dynamic buffer
+ if (submeshData.fallbackSkinningMemory == NULL && !submeshData.userSpecifiedData)
+ {
+ if (replaceDynamicBuffer)
+ {
+ submeshData.dynamicBufferReplacement = rrm->createVertexBuffer(dynamicBufDesc);
+ fillDynamicSharedVertexBuffer = true;
+ }
+ else if (mRenderMeshAsset->mRuntimeSubmeshData[submeshIndex].dynamicVertexBuffer == NULL)
+ {
+ mRenderMeshAsset->mRuntimeSubmeshData[submeshIndex].dynamicVertexBuffer = rrm->createVertexBuffer(dynamicBufDesc);
+ fillDynamicSharedVertexBuffer = true;
+ }
+ }
+ mRenderMeshAsset->mRuntimeSubmeshData[submeshIndex].needsDynamicData = true;
+ }
+
+ uint32_t bufferIndex = (uint32_t)vf.getBufferIndexFromID(vf.getSemanticID(RenderVertexSemantic::BONE_INDEX));
+ const uint32_t bonesPerVertex = vertexSemanticFormatElementCount(RenderVertexSemantic::BONE_INDEX, vf.getBufferFormat(bufferIndex));
+ if (vf.hasSeparateBoneBuffer() && bonesPerVertex > 0 && mTransforms.size() > 1 && useBones && mRenderMeshAsset->mRuntimeSubmeshData[submeshIndex].skinningVertexBuffer == NULL)
+ {
+ mRenderMeshAsset->mRuntimeSubmeshData[submeshIndex].skinningVertexBuffer = rrm->createVertexBuffer(boneBufDesc);
+ fillSkinningSharedVertexBuffer = true;
+ }
+ }
+
+ if ((submeshData.fallbackSkinningMemory != NULL || submeshData.userSpecifiedData) && submeshData.userDynamicVertexBuffer == NULL)
+ {
+ UserRenderVertexBufferDesc perActorDynamicBufDesc;
+ perActorDynamicBufDesc.moduleIdentifier = mRenderMeshAsset->mOwnerModuleID;
+ perActorDynamicBufDesc.maxVerts = srcVB.getVertexCount();
+ perActorDynamicBufDesc.uvOrigin = mRenderMeshAsset->getTextureUVOrigin();
+ perActorDynamicBufDesc.hint = RenderBufferHint::DYNAMIC;
+ perActorDynamicBufDesc.canBeShared = false;
+
+ if (submeshData.userPositions != NULL)
+ {
+ perActorDynamicBufDesc.buffersRequest[RenderVertexSemantic::POSITION] = RenderDataFormat::FLOAT3;
+ }
+
+ if (submeshData.userNormals != NULL)
+ {
+ perActorDynamicBufDesc.buffersRequest[RenderVertexSemantic::NORMAL] = RenderDataFormat::FLOAT3;
+ }
+
+ if (submeshData.userTangents4 != NULL)
+ {
+ perActorDynamicBufDesc.buffersRequest[RenderVertexSemantic::TANGENT] = RenderDataFormat::FLOAT4;
+ }
+
+ submeshData.userDynamicVertexBuffer = rrm->createVertexBuffer(perActorDynamicBufDesc);
+ }
+
+ if (fill2ndVertexBuffers)
+ {
+ UserRenderVertexBufferDesc bufDesc;
+ bufDesc.moduleIdentifier = mRenderMeshAsset->mOwnerModuleID;
+ bufDesc.maxVerts = srcVB.getVertexCount();
+ bufDesc.hint = RenderBufferHint::DYNAMIC;
+ if (mKeepVisibleBonesPacked)
+ {
+ bufDesc.buffersRequest[ RenderVertexSemantic::BONE_INDEX ] = RenderDataFormat::USHORT1;
+ }
+ if (submeshData.staticColorReplacement)
+ {
+ bufDesc.buffersRequest[ RenderVertexSemantic::COLOR ] = RenderDataFormat::R8G8B8A8;
+ }
+ bufDesc.uvOrigin = mRenderMeshAsset->getTextureUVOrigin();
+ bufDesc.canBeShared = false;
+ for (uint32_t i = 0; i < RenderVertexSemantic::NUM_SEMANTICS; i++)
+ {
+ PX_ASSERT(bufDesc.buffersRequest[i] == RenderDataFormat::UNSPECIFIED || vertexSemanticFormatValid((RenderVertexSemantic::Enum)i, bufDesc.buffersRequest[i]));
+ }
+ mPerActorVertexBuffers[submeshIndex] = rrm->createVertexBuffer(bufDesc);
+ }
+
+ // creates and/or fills index buffers
+ updatePartVisibility(submeshIndex, useBones, userRenderData);
+
+ if (fillStaticSharedVertexBuffer || fillDynamicSharedVertexBuffer || fillSkinningSharedVertexBuffer)
+ {
+ const VertexFormat& vf = srcVB.getFormat();
+
+ RenderVertexBufferData dynamicWriteData;
+ RenderVertexBufferData staticWriteData;
+ RenderVertexBufferData skinningWriteData;
+ RenderVertexBufferData& skinningWriteDataRef = vf.hasSeparateBoneBuffer() ? skinningWriteData : staticWriteData;
+
+ UserRenderVertexBuffer* staticVb = mRenderMeshAsset->mRuntimeSubmeshData[submeshIndex].staticVertexBuffer;
+ UserRenderVertexBuffer* dynamicVb = mRenderMeshAsset->mRuntimeSubmeshData[submeshIndex].dynamicVertexBuffer;
+ UserRenderVertexBuffer* skinningVb = mRenderMeshAsset->mRuntimeSubmeshData[submeshIndex].skinningVertexBuffer;
+
+ if (submeshData.staticBufferReplacement != NULL)
+ {
+ staticVb = submeshData.staticBufferReplacement;
+ }
+
+ if (submeshData.dynamicBufferReplacement != NULL)
+ {
+ dynamicVb = submeshData.dynamicBufferReplacement;
+ }
+
+ for (uint32_t semantic = RenderVertexSemantic::POSITION; semantic <= RenderVertexSemantic::COLOR; ++semantic)
+ {
+ if (semantic == RenderVertexSemantic::COLOR && submeshData.staticColorReplacement != NULL)
+ {
+ // Gets done in updatePartVisibility if submeshData.staticColorReplacement is used
+ continue;
+ }
+
+ RenderDataFormat::Enum format;
+ int32_t bufferIndex = vf.getBufferIndexFromID(vf.getSemanticID((RenderVertexSemantic::Enum)semantic));
+ if (bufferIndex < 0)
+ {
+ continue;
+ }
+ const void* src = srcVB.getBufferAndFormat(format, (uint32_t)bufferIndex);
+
+ if (semantic == RenderVertexSemantic::POSITION && submeshData.staticPositionReplacement != NULL)
+ {
+ src = submeshData.staticPositionReplacement;
+ }
+
+ if (format != RenderDataFormat::UNSPECIFIED)
+ {
+ if (srcVB.getFormat().getBufferAccess((uint32_t)bufferIndex) == RenderDataAccess::STATIC)
+ {
+ staticWriteData.setSemanticData((RenderVertexSemantic::Enum)semantic, src, RenderDataFormat::getFormatDataSize(format), format);
+ }
+ else
+ {
+ dynamicWriteData.setSemanticData((RenderVertexSemantic::Enum)semantic, src, RenderDataFormat::getFormatDataSize(format), format);
+ }
+ }
+ }
+
+ for (uint32_t semantic = RenderVertexSemantic::TEXCOORD0; semantic <= RenderVertexSemantic::TEXCOORD3; ++semantic)
+ {
+ RenderDataFormat::Enum format;
+ int32_t bufferIndex = vf.getBufferIndexFromID(vf.getSemanticID((RenderVertexSemantic::Enum)semantic));
+ if (bufferIndex < 0)
+ {
+ continue;
+ }
+ const void* src = srcVB.getBufferAndFormat(format, (uint32_t)bufferIndex);
+ if (format != RenderDataFormat::UNSPECIFIED)
+ {
+ staticWriteData.setSemanticData((RenderVertexSemantic::Enum)semantic, src, RenderDataFormat::getFormatDataSize(format), format);
+ }
+ }
+
+ for (uint32_t semantic = RenderVertexSemantic::DISPLACEMENT_TEXCOORD; semantic <= RenderVertexSemantic::DISPLACEMENT_FLAGS; ++semantic)
+ {
+ int32_t bufferIndex = vf.getBufferIndexFromID(vf.getSemanticID((RenderVertexSemantic::Enum)semantic));
+ if (bufferIndex >= 0)
+ {
+ RenderDataFormat::Enum format;
+ const void* src = srcVB.getBufferAndFormat(format, (uint32_t)bufferIndex);
+ if (format != RenderDataFormat::UNSPECIFIED)
+ {
+ staticWriteData.setSemanticData((RenderVertexSemantic::Enum)semantic, src, RenderDataFormat::getFormatDataSize(format), format);
+ }
+ }
+ }
+
+ nvidia::Array<RenderSemanticData> semanticData;
+
+ const uint32_t numCustom = vf.getCustomBufferCount();
+ if (numCustom)
+ {
+ // NvParameterized::Handle custom vertex buffer semantics
+ semanticData.resize(numCustom);
+
+ uint32_t writeIndex = 0;
+ for (uint32_t i = 0; i < vf.getBufferCount(); i++)
+ {
+ // Fill in a RenderSemanticData for each custom semantic
+ if (vf.getBufferSemantic(i) != RenderVertexSemantic::CUSTOM)
+ {
+ continue;
+ }
+ semanticData[writeIndex].data = srcVB.getBuffer(i);
+ RenderDataFormat::Enum fmt = mRenderMeshAsset->mRuntimeCustomSubmeshData[submeshIndex].customBufferFormats[writeIndex];
+ semanticData[writeIndex].stride = RenderDataFormat::getFormatDataSize(fmt);
+ semanticData[writeIndex].format = fmt;
+ semanticData[writeIndex].ident = mRenderMeshAsset->mRuntimeCustomSubmeshData[submeshIndex].customBufferVoidPtrs[writeIndex];
+
+ writeIndex++;
+ }
+ PX_ASSERT(writeIndex == numCustom);
+ staticWriteData.setCustomSemanticData(&semanticData[0], numCustom);
+ }
+
+ physx::Array<uint16_t> boneIndicesModuloMaxBoneCount;
+
+ if (mTransforms.size() > 1 || mForceBoneIndexChannel)
+ {
+ uint32_t bufferIndex = (uint32_t)vf.getBufferIndexFromID(vf.getSemanticID(RenderVertexSemantic::BONE_INDEX));
+ const uint32_t numBonesPerVertex = vertexSemanticFormatElementCount(RenderVertexSemantic::BONE_INDEX, vf.getBufferFormat(bufferIndex));
+ if (numBonesPerVertex == 1)
+ {
+ // Gets done in updatePartVisibility if keepVisibleBonesPacked is true
+ if (!mKeepVisibleBonesPacked)
+ {
+ RenderDataFormat::Enum format;
+ const VertexFormat& vf = srcVB.getFormat();
+ uint32_t bufferIndex = (uint32_t)vf.getBufferIndexFromID(vf.getSemanticID(RenderVertexSemantic::BONE_INDEX));
+ const void* src = srcVB.getBufferAndFormat(format, bufferIndex);
+ if (format != RenderDataFormat::UNSPECIFIED)
+ {
+ if (mForceBoneIndexChannel && format == RenderDataFormat::USHORT1 && submeshData.maxBonesPerMaterial > 0 && submeshData.maxBonesPerMaterial < mRenderMeshAsset->getBoneCount())
+ {
+ boneIndicesModuloMaxBoneCount.resize(srcVB.getVertexCount());
+ uint16_t* srcBuf = (uint16_t*)src;
+ for (uint32_t vertexNum = 0; vertexNum < srcVB.getVertexCount(); ++vertexNum)
+ {
+ boneIndicesModuloMaxBoneCount[vertexNum] = *(srcBuf++)%submeshData.maxBonesPerMaterial;
+ }
+ src = &boneIndicesModuloMaxBoneCount[0];
+ }
+ skinningWriteDataRef.setSemanticData(RenderVertexSemantic::BONE_INDEX, src, RenderDataFormat::getFormatDataSize(format), format);
+ }
+ }
+ }
+ else
+ {
+ RenderDataFormat::Enum format;
+ const void* src;
+ const VertexFormat& vf = srcVB.getFormat();
+ uint32_t bufferIndex = (uint32_t)vf.getBufferIndexFromID(vf.getSemanticID(nvidia::RenderVertexSemantic::BONE_INDEX));
+ src = srcVB.getBufferAndFormat(format, bufferIndex);
+ if (format != RenderDataFormat::UNSPECIFIED)
+ {
+ skinningWriteDataRef.setSemanticData(RenderVertexSemantic::BONE_INDEX, src, RenderDataFormat::getFormatDataSize(format), format);
+ }
+ bufferIndex = (uint32_t)vf.getBufferIndexFromID(vf.getSemanticID(nvidia::RenderVertexSemantic::BONE_WEIGHT));
+ src = srcVB.getBufferAndFormat(format, bufferIndex);
+ if (format != RenderDataFormat::UNSPECIFIED)
+ {
+ skinningWriteDataRef.setSemanticData(RenderVertexSemantic::BONE_WEIGHT, src, RenderDataFormat::getFormatDataSize(format), format);
+ }
+ }
+ }
+
+ if (staticVb != NULL && fillStaticSharedVertexBuffer)
+ {
+ staticVb->writeBuffer(staticWriteData, 0, srcVB.getVertexCount());
+ }
+ if (dynamicVb != NULL && fillDynamicSharedVertexBuffer)
+ {
+ dynamicVb->writeBuffer(dynamicWriteData, 0, srcVB.getVertexCount());
+ }
+ if (skinningVb != NULL && fillSkinningSharedVertexBuffer)
+ {
+ skinningVb->writeBuffer(skinningWriteData, 0, srcVB.getVertexCount());
+ }
+
+ // TODO - SJB - Beta2 - release submesh after updateRenderResources() returns. It requires acquiring the actor lock as game engine could delay these
+ // writes so long as it holds the render lock. Could be done in updateBounds(), which implictly has the lock. Perhaps we need to catch lock release
+ // so it happens immediately.
+ }
+
+ // Delete static vertex buffers after writing them
+ if (mRenderMeshAsset->mParams->deleteStaticBuffersAfterUse)
+ {
+ ApexVertexFormat dynamicFormats;
+ dynamicFormats.copy((const ApexVertexFormat&)vf);
+ for (uint32_t semantic = RenderVertexSemantic::POSITION; semantic < RenderVertexSemantic::NUM_SEMANTICS; ++semantic)
+ {
+ uint32_t bufferIndex = (uint32_t)vf.getBufferIndexFromID(vf.getSemanticID((RenderVertexSemantic::Enum)semantic));
+ if (dynamicFormats.getBufferAccess(bufferIndex) == RenderDataAccess::STATIC)
+ {
+ dynamicFormats.setBufferFormat(bufferIndex, RenderDataFormat::UNSPECIFIED);
+ }
+ }
+ srcVB.build(dynamicFormats, srcVB.getVertexCount());
+ }
+ }
+ mOneUserVertexBufferChanged = false;
+ mPartVisibilityChanged = false;
+ mBoneBufferInUse = useBones;
+}
+
+void ApexRenderMeshActor::updatePartVisibility(uint32_t submeshIndex, bool useBones, void* userRenderData)
+{
+#if VERBOSE
+ printf("updatePartVisibility(submeshIndex=%d, useBones=%s, userRenderData=0x%p)\n", submeshIndex, useBones ? "true" : "false", userRenderData);
+ printf(" mPartVisibilityChanged=%s\n", mPartVisibilityChanged ? "true" : "false");
+#endif
+
+ const ApexRenderSubmesh& submesh = *mRenderMeshAsset->mSubmeshes[submeshIndex];
+ SubmeshData& submeshData = mSubmeshData[ submeshIndex ];
+
+ UserRenderResourceManager* rrm = GetInternalApexSDK()->getUserRenderResourceManager();
+ PX_ASSERT(rrm != NULL);
+
+ // we end up with a division by 0 otherwise :(
+ PX_ASSERT(submeshData.maxBonesPerMaterial > 0);
+
+ const uint32_t partCount = mKeepVisibleBonesPacked ? getRenderVisiblePartCount() : mRenderMeshAsset->getPartCount();
+
+ // only use bones if there is no fallback skinning
+ useBones &= submeshData.fallbackSkinningMemory == NULL;
+
+ uint32_t resourceCount;
+ if (!useBones)
+ {
+ // If we're not skinning, we only need one resource
+ resourceCount = 1;
+ }
+ else
+ {
+ // LRR - poor workaround for http://nvbugs/534501
+ if (mKeepVisibleBonesPacked)
+ {
+ resourceCount = partCount == 0 ? 0 : (partCount + submeshData.maxBonesPerMaterial - 1) / submeshData.maxBonesPerMaterial;
+ }
+ else
+ {
+ resourceCount = partCount == 0 ? 0 : (mRenderMeshAsset->getBoneCount() + submeshData.maxBonesPerMaterial - 1) / submeshData.maxBonesPerMaterial;
+ }
+ }
+
+ // Eliminate unneeded resources:
+ const uint32_t start = (submeshData.userIndexBufferChanged || useBones != mBoneBufferInUse) ? 0 : resourceCount;
+ for (uint32_t i = start; i < submeshData.renderResources.size(); ++i)
+ {
+ if (submeshData.renderResources[i].resource != NULL)
+ {
+ UserRenderBoneBuffer* boneBuffer = submeshData.renderResources[i].resource->getBoneBuffer();
+ rrm->releaseResource(*submeshData.renderResources[i].resource);
+
+ if (boneBuffer)
+ {
+ rrm->releaseBoneBuffer(*boneBuffer);
+ }
+
+ submeshData.renderResources[i].resource = NULL;
+ }
+ }
+ submeshData.userIndexBufferChanged = false;
+
+ uint16_t resourceBoneCount = 0;
+ uint32_t resourceNum = 0;
+ uint32_t startIndex = 0;
+
+ if (mKeepVisibleBonesPacked)
+ {
+ if (mBoneIndexTempBuffer.size() < submesh.getVertexBuffer().getVertexCount())
+ {
+ mBoneIndexTempBuffer.resize(submesh.getVertexBuffer().getVertexCount()); // A new index buffer to remap the bone indices to the smaller buffer
+ }
+ }
+
+ uint32_t boneIndexStart = uint32_t(-1);
+ uint32_t boneIndexEnd = 0;
+
+ // Figure out how many indices we'll need
+ uint32_t totalIndexCount = submesh.getTotalIndexCount(); // Worst case
+
+ const uint32_t* visiblePartIndexPtr = getRenderVisibleParts();
+
+ if (mKeepVisibleBonesPacked)
+ {
+ // We can do better
+ totalIndexCount = 0;
+ for (uint32_t partNum = 0; partNum < partCount; ++partNum)
+ {
+ totalIndexCount += submesh.getIndexCount(visiblePartIndexPtr[partNum]);
+ }
+ }
+
+ uint32_t newIndexBufferRequestSize = submeshData.indexBufferRequestedSize;
+
+ // If there has not already been an index buffer request, set to exact size
+ if (newIndexBufferRequestSize == 0 || totalIndexCount >= 0x80000000) // special handling of potential overflow
+ {
+ newIndexBufferRequestSize = totalIndexCount;
+ }
+ else
+ {
+ // If the buffer has already been requested, see if we need to grow or shrink it
+ while (totalIndexCount > newIndexBufferRequestSize)
+ {
+ newIndexBufferRequestSize *= 2;
+ }
+ while (2*totalIndexCount < newIndexBufferRequestSize)
+ {
+ newIndexBufferRequestSize /= 2;
+ }
+ }
+
+ // In case our doubling schedule gave it a larger size than we'll ever need
+ if (newIndexBufferRequestSize > submesh.getTotalIndexCount())
+ {
+ newIndexBufferRequestSize = submesh.getTotalIndexCount();
+ }
+
+ if (submeshData.indexBuffer != NULL && newIndexBufferRequestSize != submeshData.indexBufferRequestedSize)
+ {
+ // Release the old buffer
+ rrm->releaseIndexBuffer(*submeshData.indexBuffer);
+ submeshData.indexBuffer = NULL;
+ releaseSubmeshRenderResources(submeshIndex);
+ }
+
+ // Create the index buffer now if needed
+ if (submeshData.indexBuffer == NULL && newIndexBufferRequestSize > 0)
+ {
+ UserRenderIndexBufferDesc indexDesc;
+ indexDesc.maxIndices = newIndexBufferRequestSize;
+ indexDesc.hint = mIndexBufferHint;
+ indexDesc.format = RenderDataFormat::UINT1;
+ submeshData.indexBuffer = rrm->createIndexBuffer(indexDesc);
+ submeshData.indexBufferRequestedSize = newIndexBufferRequestSize;
+ }
+
+ submeshData.renderResources.resize(resourceCount, ResourceData());
+
+ submeshData.visibleTriangleCount = 0;
+ // KHA - batch writes to temporary buffer so that index buffer is only locked once per frame
+ if(mPartIndexTempBuffer.size() < totalIndexCount)
+ {
+ mPartIndexTempBuffer.resize(totalIndexCount);
+ }
+ for (uint32_t partNum = 0; partNum < partCount;)
+ {
+ uint32_t partIndex;
+ bool partIsVisible;
+ if (mKeepVisibleBonesPacked)
+ {
+ partIndex = visiblePartIndexPtr[partNum++];
+ partIsVisible = true;
+
+ const uint32_t indexStart = submesh.getFirstVertexIndex(partIndex);
+ const uint32_t vertexCount = submesh.getVertexCount(partIndex);
+
+ uint16_t* boneIndex = mBoneIndexTempBuffer.begin() + indexStart;
+ const uint16_t* boneIndexStop = boneIndex + vertexCount;
+
+ boneIndexStart = PxMin(boneIndexStart, indexStart);
+ boneIndexEnd = PxMax(boneIndexEnd, indexStart + vertexCount);
+
+ while (boneIndex < boneIndexStop)
+ {
+ *boneIndex++ = resourceBoneCount;
+ }
+ }
+ else
+ {
+ partIndex = partNum++;
+ partIsVisible = isVisible((uint16_t)partIndex);
+ }
+
+ if (partIsVisible)
+ {
+ const uint32_t indexCount = submesh.getIndexCount(partIndex);
+ const uint32_t* indices = submesh.getIndexBuffer(partIndex);
+ const uint32_t currentIndexNum = submeshData.visibleTriangleCount * 3;
+ if (indexCount > 0 && mPartVisibilityChanged)
+ {
+ memcpy(mPartIndexTempBuffer.begin() + currentIndexNum, indices, indexCount * sizeof(uint32_t));
+ }
+ submeshData.visibleTriangleCount += indexCount / 3;
+ }
+
+ // LRR - poor workaround for http://nvbugs/534501
+ bool generateNewRenderResource = false;
+
+ const bool oneBonePerPart = mSkinningMode != RenderMeshActorSkinningMode::AllBonesPerPart;
+
+ const uint16_t bonesToAdd = oneBonePerPart ? 1u : (uint16_t)mRenderMeshAsset->getBoneCount();
+ resourceBoneCount = PxMin<uint16_t>((uint16_t)(resourceBoneCount + bonesToAdd), (uint16_t)submeshData.maxBonesPerMaterial);
+
+ // Check if we exceed max bones limit or if this is the last part
+ if ((useBones && resourceBoneCount == submeshData.maxBonesPerMaterial) || partNum == partCount)
+ {
+ generateNewRenderResource = true;
+ }
+ if (generateNewRenderResource)
+ {
+ submeshData.renderResources[resourceNum].boneCount = resourceBoneCount;
+ submeshData.renderResources[resourceNum].vertexCount = submeshData.visibleTriangleCount * 3 - startIndex;
+ UserRenderResource*& renderResource = submeshData.renderResources[resourceNum].resource; // Next resource
+ ++resourceNum;
+ if (renderResource == NULL) // Create if needed
+ {
+ UserRenderVertexBuffer* vertexBuffers[5] = { NULL };
+ uint32_t numVertexBuffers = 0;
+
+ if (submeshData.staticBufferReplacement != NULL)
+ {
+ vertexBuffers[numVertexBuffers++] = submeshData.staticBufferReplacement;
+ }
+ else if (mRenderMeshAsset->mRuntimeSubmeshData[submeshIndex].staticVertexBuffer != NULL)
+ {
+ vertexBuffers[numVertexBuffers++] = mRenderMeshAsset->mRuntimeSubmeshData[ submeshIndex ].staticVertexBuffer;
+ }
+
+ if (submeshData.userDynamicVertexBuffer != NULL && (submeshData.userSpecifiedData || submeshData.fallbackSkinningMemory != NULL))
+ {
+ vertexBuffers[numVertexBuffers++] = submeshData.userDynamicVertexBuffer;
+ }
+ else if (submeshData.dynamicBufferReplacement != NULL)
+ {
+ vertexBuffers[numVertexBuffers++] = submeshData.dynamicBufferReplacement;
+ }
+ else if (mRenderMeshAsset->mRuntimeSubmeshData[submeshIndex].dynamicVertexBuffer != NULL)
+ {
+ if (submeshData.userDynamicVertexBuffer != NULL && mReleaseResourcesIfNothingToRender)
+ {
+ rrm->releaseVertexBuffer(*submeshData.userDynamicVertexBuffer);
+ submeshData.userDynamicVertexBuffer = NULL;
+ }
+
+ vertexBuffers[numVertexBuffers++] = mRenderMeshAsset->mRuntimeSubmeshData[submeshIndex].dynamicVertexBuffer;
+ }
+
+ if (useBones && mRenderMeshAsset->mRuntimeSubmeshData[submeshIndex].skinningVertexBuffer != NULL)
+ {
+ vertexBuffers[numVertexBuffers++] = mRenderMeshAsset->mRuntimeSubmeshData[submeshIndex].skinningVertexBuffer;
+ }
+
+ // Separate (instanced) buffer for bone indices
+ if (mPerActorVertexBuffers.size())
+ {
+ vertexBuffers[numVertexBuffers++] = mPerActorVertexBuffers[ submeshIndex ];
+ }
+
+ PX_ASSERT(numVertexBuffers <= 5);
+
+ UserRenderResourceDesc resourceDesc;
+ resourceDesc.primitives = RenderPrimitiveType::TRIANGLES;
+
+ resourceDesc.vertexBuffers = vertexBuffers;
+ resourceDesc.numVertexBuffers = numVertexBuffers;
+
+ resourceDesc.numVerts = submesh.getVertexBuffer().getVertexCount();
+
+ resourceDesc.indexBuffer = submeshData.indexBuffer;
+ resourceDesc.firstIndex = startIndex;
+ resourceDesc.numIndices = submeshData.visibleTriangleCount * 3 - startIndex;
+
+ // not assuming partcount == bonecount anymore
+ //if (mRenderMeshAsset->getPartCount() > 1)
+ const uint32_t numBones = mRenderMeshAsset->getBoneCount();
+ if (numBones > 1 && useBones)
+ {
+ UserRenderBoneBufferDesc boneDesc;
+ // we don't need to use the minimum of numBones and max bones because the
+ // bone buffer update contains the proper range
+ boneDesc.maxBones = submeshData.maxBonesPerMaterial;
+ boneDesc.hint = RenderBufferHint::DYNAMIC;
+ boneDesc.buffersRequest[ RenderBoneSemantic::POSE ] = RenderDataFormat::FLOAT4x4;
+ if (mKeepPreviousFrameBoneBuffer)
+ {
+ boneDesc.buffersRequest[ RenderBoneSemantic::PREVIOUS_POSE ] = RenderDataFormat::FLOAT4x4;
+ }
+ resourceDesc.boneBuffer = rrm->createBoneBuffer(boneDesc);
+ PX_ASSERT(resourceDesc.boneBuffer);
+ if (resourceDesc.boneBuffer)
+ {
+ resourceDesc.numBones = numBones;
+ }
+ }
+
+ resourceDesc.instanceBuffer = submeshData.instanceBuffer;
+ resourceDesc.numInstances = 0;
+
+ if (!submeshData.isMaterialPointerValid)
+ {
+ // this should only be reached, when renderMeshActorLoadMaterialsLazily is true.
+ // URR may not be called asynchronously in that case (for example in a render thread)
+ ResourceProviderIntl* nrp = GetInternalApexSDK()->getInternalResourceProvider();
+ if (nrp != NULL)
+ {
+ submeshData.material = nrp->getResource(submeshData.materialID);
+ submeshData.isMaterialPointerValid = true;
+ }
+ }
+
+ resourceDesc.material = submeshData.material;
+
+ resourceDesc.submeshIndex = submeshIndex;
+
+ resourceDesc.userRenderData = userRenderData;
+
+ resourceDesc.cullMode = submesh.getVertexBuffer().getFormat().getWinding();
+
+ if (resourceDesc.isValid()) // TODO: should probably make this an if-statement... -jgd // I did, -poh
+ {
+ renderResource = rrm->createResource(resourceDesc);
+ }
+ }
+
+ if (renderResource != NULL)
+ {
+ renderResource->setIndexBufferRange(startIndex, submeshData.visibleTriangleCount * 3 - startIndex);
+ startIndex = submeshData.visibleTriangleCount * 3;
+
+ if (renderResource->getBoneBuffer() != NULL)
+ {
+ renderResource->setBoneBufferRange(0, resourceBoneCount);
+ // TODO - LRR - make useBoneVisibilitySemantic work with >1 bone/part
+ // if visible bone optimization enabled (as set in the actor desc)
+ // {
+ // if (renderMesh->getBoneBuffer() != NULL)
+ // if we have a 1:1 bone:part mapping, as determined when asset is loaded (or authored)
+ // renderMesh->getBoneBuffer()->writeBuffer(RenderBoneSemantic::VISIBLE_INDEX, visibleParts.usedIndices(), sizeof(uint32_t), 0, visibleParts.usedCount());
+ // else
+ // {
+ // // run through index buffer, and find all bones referenced by visible verts and store in visibleBones
+ // renderMesh->getBoneBuffer()->writeBuffer(RenderBoneSemantic::VISIBLE_INDEX, visibleBones.usedIndices(), sizeof(uint32_t), 0, visibleBones.usedCount());
+ // }
+ // }
+ }
+ }
+
+ resourceBoneCount = 0;
+ }
+ }
+
+ if (boneIndexStart == uint32_t(-1))
+ {
+ boneIndexStart = 0;
+ }
+
+ // KHA - Write temporary buffer to index buffer
+ if(submeshData.indexBuffer != NULL && mPartVisibilityChanged)
+ {
+ submeshData.indexBuffer->writeBuffer(mPartIndexTempBuffer.begin(), sizeof(uint32_t), 0, submeshData.visibleTriangleCount*3);
+ }
+
+ // Write re-mapped bone indices
+ if (mPerActorVertexBuffers.size())
+ {
+ if (mTransforms.size() > 1 && mKeepVisibleBonesPacked)
+ {
+ RenderVertexBufferData skinningWriteData;
+ skinningWriteData.setSemanticData(RenderVertexSemantic::BONE_INDEX, mBoneIndexTempBuffer.begin() + boneIndexStart, sizeof(uint16_t), RenderDataFormat::USHORT1);
+ if (submeshData.staticColorReplacement != NULL)
+ {
+ skinningWriteData.setSemanticData(RenderVertexSemantic::COLOR, submeshData.staticColorReplacement + boneIndexStart, sizeof(ColorRGBA), RenderDataFormat::R8G8B8A8);
+ }
+ mPerActorVertexBuffers[submeshIndex]->writeBuffer(skinningWriteData, boneIndexStart, boneIndexEnd - boneIndexStart);
+ }
+ else
+ if (submeshData.staticColorReplacement != NULL)
+ {
+ RenderVertexBufferData skinningWriteData;
+ skinningWriteData.setSemanticData(RenderVertexSemantic::COLOR, submeshData.staticColorReplacement, sizeof(ColorRGBA), RenderDataFormat::R8G8B8A8);
+ mPerActorVertexBuffers[submeshIndex]->writeBuffer(skinningWriteData, 0, submesh.getVertexBuffer().getVertexCount());
+ }
+ }
+
+ mBonePosesDirty = true;
+
+#if VERBOSE
+ printf("-updatePartVisibility(submeshIndex=%d, useBones=%s, userRenderData=0x%p)\n", submeshIndex, useBones ? "true" : "false", userRenderData);
+#endif
+}
+
+void ApexRenderMeshActor::updateBonePoses(uint32_t submeshIndex)
+{
+// There can now be >1 bones per part
+// if (mRenderMeshAsset->getPartCount() > 1)
+ if (mRenderMeshAsset->getBoneCount() > 1)
+ {
+ SubmeshData& submeshData = mSubmeshData[ submeshIndex ];
+ PxMat44* boneTMs = mTransforms.begin();
+ const uint32_t tmBufferSize = mKeepVisibleBonesPacked ? getRenderVisiblePartCount() : mTransforms.size();
+
+ // Set up the previous bone buffer, if requested and available
+ PxMat44* previousBoneTMs = NULL;
+ if (!mPreviousFrameBoneBufferValid || mTransformsLastFrame.size() != mTransforms.size())
+ {
+ previousBoneTMs = boneTMs;
+ }
+ else
+ if (!mKeepVisibleBonesPacked || mRemappedPreviousBoneTMs.size() == 0)
+ {
+ previousBoneTMs = mTransformsLastFrame.begin();
+ }
+ else
+ {
+ previousBoneTMs = mRemappedPreviousBoneTMs.begin();
+ }
+
+ uint32_t tmsRemaining = tmBufferSize;
+ for (uint32_t i = 0; i < submeshData.renderResources.size(); ++i)
+ {
+ UserRenderResource* renderResource = submeshData.renderResources[i].resource;
+ const uint32_t resourceBoneCount = submeshData.renderResources[i].boneCount;
+ if (renderResource && renderResource->getBoneBuffer() != NULL)
+ {
+ RenderBoneBufferData boneWriteData;
+ boneWriteData.setSemanticData(RenderBoneSemantic::POSE, boneTMs, sizeof(PxMat44), RenderDataFormat::FLOAT4x4);
+ if (mKeepPreviousFrameBoneBuffer)
+ {
+ boneWriteData.setSemanticData(RenderBoneSemantic::PREVIOUS_POSE, previousBoneTMs, sizeof(PxMat44), RenderDataFormat::FLOAT4x4);
+ }
+ renderResource->getBoneBuffer()->writeBuffer(boneWriteData, 0, PxMin(tmsRemaining, resourceBoneCount));
+ tmsRemaining -= resourceBoneCount;
+ boneTMs += resourceBoneCount;
+ previousBoneTMs += resourceBoneCount;
+ }
+ }
+ }
+}
+
+void ApexRenderMeshActor::releaseSubmeshRenderResources(uint32_t submeshIndex)
+{
+#if VERBOSE
+ printf("releaseSubmeshRenderResources()\n");
+#endif
+
+ if (submeshIndex >= mSubmeshData.size())
+ {
+ return;
+ }
+
+ UserRenderResourceManager* rrm = GetInternalApexSDK()->getUserRenderResourceManager();
+
+ SubmeshData& submeshData = mSubmeshData[submeshIndex];
+ for (uint32_t j = submeshData.renderResources.size(); j--;)
+ {
+ if (submeshData.renderResources[j].resource != NULL)
+ {
+ if (submeshData.renderResources[j].resource->getBoneBuffer() != NULL)
+ {
+ rrm->releaseBoneBuffer(*submeshData.renderResources[j].resource->getBoneBuffer());
+ }
+ rrm->releaseResource(*submeshData.renderResources[j].resource);
+ submeshData.renderResources[j].resource = NULL;
+ }
+ }
+ submeshData.renderResources.reset();
+}
+
+
+void ApexRenderMeshActor::releaseRenderResources()
+{
+#if VERBOSE
+ printf("releaseRenderResources()\n");
+#endif
+
+ UserRenderResourceManager* rrm = GetInternalApexSDK()->getUserRenderResourceManager();
+
+ for (uint32_t i = mSubmeshData.size(); i--;)
+ {
+ releaseSubmeshRenderResources(i);
+
+ SubmeshData& submeshData = mSubmeshData[i];
+
+ if (submeshData.indexBuffer != NULL)
+ {
+ rrm->releaseIndexBuffer(*submeshData.indexBuffer);
+ submeshData.indexBuffer = NULL;
+ }
+ submeshData.instanceBuffer = NULL;
+
+ if (submeshData.staticBufferReplacement != NULL)
+ {
+ rrm->releaseVertexBuffer(*submeshData.staticBufferReplacement);
+ submeshData.staticBufferReplacement = NULL;
+ }
+
+ if (submeshData.dynamicBufferReplacement != NULL)
+ {
+ rrm->releaseVertexBuffer(*submeshData.dynamicBufferReplacement);
+ submeshData.dynamicBufferReplacement = NULL;
+ }
+
+ if (submeshData.userDynamicVertexBuffer != NULL)
+ {
+ rrm->releaseVertexBuffer(*submeshData.userDynamicVertexBuffer);
+ submeshData.userDynamicVertexBuffer = NULL;
+ }
+ submeshData.userIndexBufferChanged = false;
+ }
+
+ for (uint32_t i = mPerActorVertexBuffers.size(); i--;)
+ {
+ if (mPerActorVertexBuffers[i] != NULL)
+ {
+ rrm->releaseVertexBuffer(*mPerActorVertexBuffers[i]);
+ mPerActorVertexBuffers[i] = NULL;
+ }
+ }
+ mPerActorVertexBuffers.reset();
+
+ if (mRenderResource)
+ {
+ rrm->releaseResource(*mRenderResource);
+ mRenderResource = NULL;
+ }
+
+ mBoneBufferInUse = false;
+}
+
+
+
+bool ApexRenderMeshActor::submeshHasVisibleTriangles(uint32_t submeshIndex) const
+{
+ const ApexRenderSubmesh& submesh = *mRenderMeshAsset->mSubmeshes[submeshIndex];
+
+ const uint32_t partCount = getRenderVisiblePartCount();
+ const uint32_t* visiblePartIndexPtr = getRenderVisibleParts();
+
+ for (uint32_t partNum = 0; partNum < partCount;)
+ {
+ const uint32_t partIndex = visiblePartIndexPtr[partNum++];
+ const uint32_t indexCount = submesh.getIndexCount(partIndex);
+
+ if (indexCount > 0)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+
+void ApexRenderMeshActor::createFallbackSkinning(uint32_t submeshIndex)
+{
+ if (mTransforms.size() == 1)
+ {
+ return;
+ }
+
+#if VERBOSE
+ printf("createFallbackSkinning(submeshIndex=%d)\n", submeshIndex);
+#endif
+ const VertexBuffer& vertexBuffer = mRenderMeshAsset->getSubmesh(submeshIndex).getVertexBuffer();
+ const VertexFormat& format = vertexBuffer.getFormat();
+
+ const uint32_t bufferCount = format.getBufferCount();
+
+ uint32_t bufferSize = 0;
+ for (uint32_t bufferIndex = 0; bufferIndex < bufferCount; bufferIndex++)
+ {
+ if (format.getBufferAccess(bufferIndex) == RenderDataAccess::DYNAMIC)
+ {
+ RenderDataFormat::Enum bufferFormat = format.getBufferFormat(bufferIndex);
+ RenderVertexSemantic::Enum bufferSemantic = format.getBufferSemantic(bufferIndex);
+
+ if (bufferSemantic == RenderVertexSemantic::POSITION ||
+ bufferSemantic == RenderVertexSemantic::NORMAL ||
+ bufferSemantic == RenderVertexSemantic::TANGENT)
+ {
+ if (bufferFormat == RenderDataFormat::FLOAT3)
+ {
+ bufferSize += sizeof(PxVec3);
+ }
+ else if (bufferFormat == RenderDataFormat::FLOAT4)
+ {
+ bufferSize += sizeof(PxVec4);
+ }
+ }
+ }
+ }
+
+ if (bufferSize > 0)
+ {
+ PX_ASSERT(mSubmeshData[submeshIndex].fallbackSkinningMemory == NULL);
+ mSubmeshData[submeshIndex].fallbackSkinningMemorySize = bufferSize * vertexBuffer.getVertexCount();
+ mSubmeshData[submeshIndex].fallbackSkinningMemory = PX_ALLOC(mSubmeshData[submeshIndex].fallbackSkinningMemorySize, "fallbackSkinnningMemory");
+
+ PX_ASSERT(mSubmeshData[submeshIndex].fallbackSkinningDirty == false);
+
+ if (!mSubmeshData[submeshIndex].userSpecifiedData)
+ {
+ distributeFallbackData(submeshIndex);
+ mOneUserVertexBufferChanged = true;
+ }
+ }
+}
+
+
+
+void ApexRenderMeshActor::distributeFallbackData(uint32_t submeshIndex)
+{
+ const VertexBuffer& vertexBuffer = mRenderMeshAsset->getSubmesh(submeshIndex).getVertexBuffer();
+ const VertexFormat& format = vertexBuffer.getFormat();
+ const uint32_t bufferCount = format.getBufferCount();
+ const uint32_t vertexCount = vertexBuffer.getVertexCount();
+
+ unsigned char* memoryIterator = (unsigned char*)mSubmeshData[submeshIndex].fallbackSkinningMemory;
+
+ uint32_t sizeUsed = 0;
+ for (uint32_t bufferIndex = 0; bufferIndex < bufferCount; bufferIndex++)
+ {
+ if (format.getBufferAccess(bufferIndex) == RenderDataAccess::DYNAMIC)
+ {
+ RenderDataFormat::Enum bufferFormat = format.getBufferFormat(bufferIndex);
+ RenderVertexSemantic::Enum bufferSemantic = format.getBufferSemantic(bufferIndex);
+
+ if (bufferSemantic == RenderVertexSemantic::POSITION && bufferFormat == RenderDataFormat::FLOAT3)
+ {
+ mSubmeshData[submeshIndex].userPositions = (PxVec3*)memoryIterator;
+ memoryIterator += sizeof(PxVec3) * vertexCount;
+ sizeUsed += sizeof(PxVec3);
+ }
+ else if (bufferSemantic == RenderVertexSemantic::NORMAL && bufferFormat == RenderDataFormat::FLOAT3)
+ {
+ mSubmeshData[submeshIndex].userNormals = (PxVec3*)memoryIterator;
+ memoryIterator += sizeof(PxVec3) * vertexCount;
+ sizeUsed += sizeof(PxVec3);
+ }
+ else if (bufferSemantic == RenderVertexSemantic::TANGENT && bufferFormat == RenderDataFormat::FLOAT4)
+ {
+ mSubmeshData[submeshIndex].userTangents4 = (PxVec4*)memoryIterator;
+ memoryIterator += sizeof(PxVec4) * vertexCount;
+ sizeUsed += sizeof(PxVec4);
+ }
+ }
+ }
+
+ PX_ASSERT(sizeUsed * vertexCount == mSubmeshData[submeshIndex].fallbackSkinningMemorySize);
+}
+
+
+
+void ApexRenderMeshActor::updateFallbackSkinning(uint32_t submeshIndex)
+{
+ if (mSubmeshData[submeshIndex].fallbackSkinningMemory == NULL || mSubmeshData[submeshIndex].userSpecifiedData)
+ {
+ return;
+ }
+
+#if VERBOSE
+ printf("updateFallbackSkinning(submeshIndex=%d)\n", submeshIndex);
+#endif
+ PX_PROFILE_ZONE("ApexRenderMesh::updateFallbackSkinning", GetInternalApexSDK()->getContextId());
+
+ const VertexBuffer& vertexBuffer = mRenderMeshAsset->getSubmesh(submeshIndex).getVertexBuffer();
+ const VertexFormat& format = vertexBuffer.getFormat();
+
+ PxVec3* outPositions = mSubmeshData[submeshIndex].userPositions;
+ PxVec3* outNormals = mSubmeshData[submeshIndex].userNormals;
+ PxVec4* outTangents = mSubmeshData[submeshIndex].userTangents4;
+
+ if (outPositions == NULL && outNormals == NULL && outTangents == NULL)
+ {
+ return;
+ }
+
+ RenderDataFormat::Enum inFormat;
+ const uint32_t positionIndex = (uint32_t)format.getBufferIndexFromID(format.getSemanticID(RenderVertexSemantic::POSITION));
+ const PxVec3* inPositions = (const PxVec3*)vertexBuffer.getBufferAndFormat(inFormat, positionIndex);
+ PX_ASSERT(inPositions == NULL || inFormat == RenderDataFormat::FLOAT3);
+
+ if (inPositions != NULL && mSubmeshData[submeshIndex].staticPositionReplacement != NULL)
+ {
+ inPositions = mSubmeshData[submeshIndex].staticPositionReplacement;
+ }
+
+ const uint32_t normalIndex = (uint32_t)format.getBufferIndexFromID(format.getSemanticID(RenderVertexSemantic::NORMAL));
+ const PxVec3* inNormals = (const PxVec3*)vertexBuffer.getBufferAndFormat(inFormat, normalIndex);
+ PX_ASSERT(inNormals == NULL || inFormat == RenderDataFormat::FLOAT3);
+
+ const uint32_t tangentIndex = (uint32_t)format.getBufferIndexFromID(format.getSemanticID(RenderVertexSemantic::TANGENT));
+ const PxVec4* inTangents = (const PxVec4*)vertexBuffer.getBufferAndFormat(inFormat, tangentIndex);
+ PX_ASSERT(inTangents == NULL || inFormat == RenderDataFormat::FLOAT4);
+
+ const uint32_t boneIndexIndex = (uint32_t)format.getBufferIndexFromID(format.getSemanticID(RenderVertexSemantic::BONE_INDEX));
+ const uint16_t* inBoneIndices = (const uint16_t*)vertexBuffer.getBufferAndFormat(inFormat, boneIndexIndex);
+ PX_ASSERT(inBoneIndices == NULL || inFormat == RenderDataFormat::USHORT1 || inFormat == RenderDataFormat::USHORT2 || inFormat == RenderDataFormat::USHORT3 || inFormat == RenderDataFormat::USHORT4);
+
+ const uint32_t boneWeightIndex = (uint32_t)format.getBufferIndexFromID(format.getSemanticID(RenderVertexSemantic::BONE_WEIGHT));
+ const float* inBoneWeights = (const float*)vertexBuffer.getBufferAndFormat(inFormat, boneWeightIndex);
+ PX_ASSERT(inBoneWeights == NULL || inFormat == RenderDataFormat::FLOAT1 || inFormat == RenderDataFormat::FLOAT2 || inFormat == RenderDataFormat::FLOAT3 || inFormat == RenderDataFormat::FLOAT4);
+
+ uint32_t numBonesPerVertex = 0;
+ switch (inFormat)
+ {
+ case RenderDataFormat::FLOAT1:
+ numBonesPerVertex = 1;
+ break;
+ case RenderDataFormat::FLOAT2:
+ numBonesPerVertex = 2;
+ break;
+ case RenderDataFormat::FLOAT3:
+ numBonesPerVertex = 3;
+ break;
+ case RenderDataFormat::FLOAT4:
+ numBonesPerVertex = 4;
+ break;
+ default:
+ break;
+ }
+
+ PX_ASSERT((inPositions != NULL) == (outPositions != NULL));
+ PX_ASSERT((inNormals != NULL) == (outNormals != NULL));
+ PX_ASSERT((inTangents != NULL) == (outTangents != NULL));
+
+ if (inBoneWeights == NULL || inBoneIndices == NULL || numBonesPerVertex == 0)
+ {
+ return;
+ }
+
+ // clear all data
+ nvidia::intrinsics::memSet(mSubmeshData[submeshIndex].fallbackSkinningMemory, 0, mSubmeshData[submeshIndex].fallbackSkinningMemorySize);
+
+ const uint32_t vertexCount = vertexBuffer.getVertexCount();
+ for (uint32_t i = 0; i < vertexCount; i++)
+ {
+ for (uint32_t k = 0; k < numBonesPerVertex; k++)
+ {
+ const uint32_t boneIndex = inBoneIndices[i * numBonesPerVertex + k];
+ PX_ASSERT(boneIndex < mTransforms.size());
+ PxMat44& transform = mTransforms[boneIndex];
+
+ const float boneWeight = inBoneWeights[i * numBonesPerVertex + k];
+ if (boneWeight > 0.0f)
+ {
+ if (outPositions != NULL)
+ {
+ outPositions[i] += transform.transform(inPositions[i]) * boneWeight;
+ }
+
+ if (outNormals != NULL)
+ {
+ outNormals[i] += transform.rotate(inNormals[i]) * boneWeight;
+ }
+
+ if (outTangents != NULL)
+ {
+ outTangents[i] += PxVec4(transform.rotate(inTangents[i].getXYZ()) * boneWeight, 0.0f);
+ }
+ }
+ }
+ if (outTangents != NULL)
+ {
+ outTangents[i].w = inTangents[i].w;
+ }
+ }
+
+ mSubmeshData[submeshIndex].fallbackSkinningDirty = true;
+}
+
+
+
+void ApexRenderMeshActor::writeUserBuffers(uint32_t submeshIndex)
+{
+ PxVec3* outPositions = mSubmeshData[submeshIndex].userPositions;
+ PxVec3* outNormals = mSubmeshData[submeshIndex].userNormals;
+ PxVec4* outTangents4 = mSubmeshData[submeshIndex].userTangents4;
+
+ if (outPositions == NULL && outNormals == NULL && outTangents4 == NULL)
+ {
+ return;
+ }
+
+ RenderVertexBufferData dynamicWriteData;
+ if (outPositions != NULL)
+ {
+ dynamicWriteData.setSemanticData(RenderVertexSemantic::POSITION, outPositions, sizeof(PxVec3), RenderDataFormat::FLOAT3);
+ }
+
+ if (outNormals != NULL)
+ {
+ dynamicWriteData.setSemanticData(RenderVertexSemantic::NORMAL, outNormals, sizeof(PxVec3), RenderDataFormat::FLOAT3);
+ }
+
+ if (outTangents4)
+ {
+ dynamicWriteData.setSemanticData(RenderVertexSemantic::TANGENT, outTangents4, sizeof(PxVec4), RenderDataFormat::FLOAT4);
+ }
+
+ const uint32_t vertexCount = mRenderMeshAsset->mSubmeshes[submeshIndex]->getVertexBuffer().getVertexCount();
+
+ mSubmeshData[submeshIndex].userDynamicVertexBuffer->writeBuffer(dynamicWriteData, 0, vertexCount);
+}
+
+
+
+void ApexRenderMeshActor::visualizeTangentSpace(RenderDebugInterface& batcher, float normalScale, float tangentScale, float bitangentScale, PxMat33* scaledRotations, PxVec3* translations, uint32_t stride, uint32_t numberOfTransforms) const
+{
+#ifdef WITHOUT_DEBUG_VISUALIZE
+ PX_UNUSED(batcher);
+ PX_UNUSED(normalScale);
+ PX_UNUSED(tangentScale);
+ PX_UNUSED(bitangentScale);
+ PX_UNUSED(scaledRotations);
+ PX_UNUSED(translations);
+ PX_UNUSED(stride);
+ PX_UNUSED(numberOfTransforms);
+#else
+
+ if (normalScale <= 0.0f && tangentScale <= 0.0f && bitangentScale <= 0.0f)
+ {
+ return;
+ }
+
+ using RENDER_DEBUG::DebugColors;
+ uint32_t debugColorRed = RENDER_DEBUG_IFACE(&batcher)->getDebugColor(DebugColors::Red);
+ uint32_t debugColorGreen = RENDER_DEBUG_IFACE(&batcher)->getDebugColor(DebugColors::Green);
+ uint32_t debugColorBlue = RENDER_DEBUG_IFACE(&batcher)->getDebugColor(DebugColors::Blue);
+
+ RENDER_DEBUG_IFACE(&batcher)->pushRenderState();
+
+ const uint32_t submeshCount = mRenderMeshAsset->getSubmeshCount();
+ PX_ASSERT(mSubmeshData.size() == submeshCount);
+ for (uint32_t submeshIndex = 0; submeshIndex < submeshCount; ++submeshIndex)
+ {
+ const PxVec3* positions = NULL;
+ const PxVec3* normals = NULL;
+ const PxVec3* tangents = NULL;
+ const PxVec4* tangents4 = NULL;
+
+ const uint16_t* boneIndices = NULL;
+ const float* boneWeights = NULL;
+
+ uint32_t numBonesPerVertex = 0;
+
+ if (mSubmeshData[submeshIndex].userSpecifiedData || mSubmeshData[submeshIndex].fallbackSkinningMemory != NULL)
+ {
+ positions = mSubmeshData[submeshIndex].userPositions;
+ normals = mSubmeshData[submeshIndex].userNormals;
+ tangents4 = mSubmeshData[submeshIndex].userTangents4;
+ }
+ else
+ {
+ const VertexBuffer& vertexBuffer = mRenderMeshAsset->getSubmesh(submeshIndex).getVertexBuffer();
+ const VertexFormat& format = vertexBuffer.getFormat();
+
+ RenderDataFormat::Enum inFormat;
+ const uint32_t positionIndex = (uint32_t)format.getBufferIndexFromID(format.getSemanticID(RenderVertexSemantic::POSITION));
+ positions = (const PxVec3*)vertexBuffer.getBufferAndFormat(inFormat, positionIndex);
+ PX_ASSERT(positions == NULL || inFormat == RenderDataFormat::FLOAT3);
+
+ if (positions != NULL && mSubmeshData[submeshIndex].staticPositionReplacement != NULL)
+ {
+ positions = mSubmeshData[submeshIndex].staticPositionReplacement;
+ }
+
+ const uint32_t normalIndex = (uint32_t)format.getBufferIndexFromID(format.getSemanticID(RenderVertexSemantic::NORMAL));
+ normals = (const PxVec3*)vertexBuffer.getBufferAndFormat(inFormat, normalIndex);
+ PX_ASSERT(normals == NULL || inFormat == RenderDataFormat::FLOAT3);
+
+ const uint32_t tangentIndex = (uint32_t)format.getBufferIndexFromID(format.getSemanticID(RenderVertexSemantic::TANGENT));
+ tangents = (const PxVec3*)vertexBuffer.getBufferAndFormat(inFormat, tangentIndex);
+ PX_ASSERT(tangents == NULL || inFormat == RenderDataFormat::FLOAT3 || inFormat == RenderDataFormat::FLOAT4);
+ if (inFormat == RenderDataFormat::FLOAT4)
+ {
+ tangents4 = (const PxVec4*)tangents;
+ tangents = NULL;
+ }
+
+ const uint32_t boneIndexIndex = (uint32_t)format.getBufferIndexFromID(format.getSemanticID(RenderVertexSemantic::BONE_INDEX));
+ boneIndices = (const uint16_t*)vertexBuffer.getBufferAndFormat(inFormat, boneIndexIndex);
+ PX_ASSERT(boneIndices == NULL || inFormat == RenderDataFormat::USHORT1 || inFormat == RenderDataFormat::USHORT2 || inFormat == RenderDataFormat::USHORT3 || inFormat == RenderDataFormat::USHORT4);
+
+ switch (inFormat)
+ {
+ case RenderDataFormat::USHORT1:
+ numBonesPerVertex = 1;
+ break;
+ case RenderDataFormat::USHORT2:
+ numBonesPerVertex = 2;
+ break;
+ case RenderDataFormat::USHORT3:
+ numBonesPerVertex = 3;
+ break;
+ case RenderDataFormat::USHORT4:
+ numBonesPerVertex = 4;
+ break;
+ default:
+ break;
+ }
+
+ const uint32_t boneWeightIndex = (uint32_t)format.getBufferIndexFromID(format.getSemanticID(RenderVertexSemantic::BONE_WEIGHT));
+ boneWeights = (const float*)vertexBuffer.getBufferAndFormat(inFormat, boneWeightIndex);
+ PX_ASSERT(boneWeights == NULL || inFormat == RenderDataFormat::FLOAT1 || inFormat == RenderDataFormat::FLOAT2 || inFormat == RenderDataFormat::FLOAT3 || inFormat == RenderDataFormat::FLOAT4);
+ }
+
+ const uint32_t partCount = visiblePartCount();
+ const uint32_t* visibleParts = getVisibleParts();
+ for (uint32_t visiblePartIndex = 0; visiblePartIndex < partCount; visiblePartIndex++)
+ {
+ const uint32_t partIndex = visibleParts[visiblePartIndex];
+
+ const RenderSubmesh& submesh = mRenderMeshAsset->getSubmesh(submeshIndex);
+ const uint32_t vertexStart = submesh.getFirstVertexIndex(partIndex);
+ const uint32_t vertexEnd = vertexStart + submesh.getVertexCount(partIndex);
+
+ for (uint32_t i = vertexStart; i < vertexEnd; i++)
+ {
+ PxVec3 position(0.0f), tangent(0.0f), bitangent(0.0f), normal(0.0f);
+ if (numBonesPerVertex == 0)
+ {
+ position = positions[i];
+ if (normals != NULL)
+ {
+ normal = normals[i].getNormalized();
+ }
+ if (tangents4 != NULL)
+ {
+ tangent = tangents4[i].getXYZ().getNormalized();
+ bitangent = normal.cross(tangent) * tangents4[i].w;
+ }
+ else if (tangents != NULL)
+ {
+ tangent = tangents[i].getNormalized();
+ bitangent = normal.cross(tangent);
+ }
+
+ }
+ else if (numBonesPerVertex == 1)
+ {
+ PX_ASSERT(boneIndices != NULL);
+ uint32_t boneIndex = 0;
+ if (mRenderWithoutSkinning)
+ {
+ boneIndex = 0;
+ }
+ else if (mKeepVisibleBonesPacked)
+ {
+ boneIndex = visiblePartIndex;
+ }
+ else
+ {
+ boneIndex = boneIndices[i];
+ }
+
+ const PxMat44& tm = mTransforms[boneIndex];
+ position = tm.transform(positions[i]);
+ if (normals != NULL)
+ {
+ normal = tm.rotate(normals[i].getNormalized());
+ }
+ if (tangents4 != NULL)
+ {
+ tangent = tm.rotate(tangents4[i].getXYZ().getNormalized());
+ bitangent = normal.cross(tangent) * tangents4[i].w;
+ }
+ else if (tangents != NULL)
+ {
+ tangent = tm.rotate(tangents[i].getNormalized());
+ bitangent = normal.cross(tangent);
+ }
+ }
+ else
+ {
+ position = tangent = bitangent = normal = PxVec3(0.0f);
+ for (uint32_t k = 0; k < numBonesPerVertex; k++)
+ {
+ const float weight = boneWeights[i * numBonesPerVertex + k];
+ if (weight > 0.0f)
+ {
+ const PxMat44& tm = mTransforms[boneIndices[i * numBonesPerVertex + k]];
+ position += tm.transform(positions[i]) * weight;
+ if (normals != NULL)
+ {
+ normal += tm.rotate(normals[i]) * weight;
+ }
+ if (tangents4 != NULL)
+ {
+ tangent += tm.rotate(tangents4[i].getXYZ()) * weight;
+ }
+ else if (tangents != NULL)
+ {
+ tangent += tm.rotate(tangents[i]) * weight;
+ }
+ }
+ }
+ normal.normalize();
+ tangent.normalize();
+ if (tangents4 != NULL)
+ {
+ bitangent = normal.cross(tangent) * tangents4[i].w;
+ }
+ else if (tangents != NULL)
+ {
+ bitangent = normal.cross(tangent);
+ }
+ }
+
+ if (numberOfTransforms == 0 || scaledRotations == NULL || translations == NULL)
+ {
+ if (!tangent.isZero() && tangentScale > 0.0f)
+ {
+ RENDER_DEBUG_IFACE(&batcher)->setCurrentColor(debugColorRed);
+ RENDER_DEBUG_IFACE(&batcher)->debugLine(position, position + tangent * tangentScale);
+ }
+
+ if (!bitangent.isZero() && bitangentScale > 0.0f)
+ {
+ RENDER_DEBUG_IFACE(&batcher)->setCurrentColor(debugColorGreen);
+ RENDER_DEBUG_IFACE(&batcher)->debugLine(position, position + bitangent * bitangentScale);
+ }
+
+ if (!normal.isZero() && normalScale > 0.0f)
+ {
+ RENDER_DEBUG_IFACE(&batcher)->setCurrentColor(debugColorBlue);
+ RENDER_DEBUG_IFACE(&batcher)->debugLine(position, position + normal * normalScale);
+ }
+ }
+ else //instancing
+ {
+ for (uint32_t k = 0; k < numberOfTransforms; k++)
+ {
+ PxMat33& scaledRotation = *(PxMat33*)((uint8_t*)scaledRotations + k*stride);
+ PxVec3& translation = *(PxVec3*)((uint8_t*)translations + k*stride);
+
+ PxVec3 newPos = scaledRotation.transform(position) + translation; //full transform
+
+ PxVec3 newTangent = scaledRotation.transform(tangent); //without translation
+ PxVec3 newBitangent = scaledRotation.transform(bitangent);
+
+ PxVec3 newNormal = (scaledRotation.getInverse()).getTranspose().transform(normal);
+
+ if (!tangent.isZero() && tangentScale > 0.0f)
+ {
+ RENDER_DEBUG_IFACE(&batcher)->setCurrentColor(debugColorRed);
+ RENDER_DEBUG_IFACE(&batcher)->debugLine(newPos, newPos + newTangent * tangentScale);
+ }
+
+ if (!bitangent.isZero() && bitangentScale > 0.0f)
+ {
+ RENDER_DEBUG_IFACE(&batcher)->setCurrentColor(debugColorGreen);
+ RENDER_DEBUG_IFACE(&batcher)->debugLine(newPos, newPos + newBitangent * bitangentScale);
+ }
+
+ if (!normal.isZero() && normalScale > 0.0f)
+ {
+ RENDER_DEBUG_IFACE(&batcher)->setCurrentColor(debugColorBlue);
+ RENDER_DEBUG_IFACE(&batcher)->debugLine(newPos, newPos + newNormal * normalScale);
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+ RENDER_DEBUG_IFACE(&batcher)->popRenderState();
+#endif
+}
+
+
+
+
+ApexRenderMeshActor::SubmeshData::SubmeshData() :
+ indexBuffer(NULL),
+ fallbackSkinningMemory(NULL),
+ userDynamicVertexBuffer(NULL),
+ instanceBuffer(NULL),
+ userPositions(NULL),
+ userNormals(NULL),
+ userTangents4(NULL),
+ staticColorReplacement(NULL),
+ staticColorReplacementDirty(false),
+ staticPositionReplacement(NULL),
+ staticBufferReplacement(NULL),
+ dynamicBufferReplacement(NULL),
+ fallbackSkinningMemorySize(0),
+ visibleTriangleCount(0),
+ materialID(INVALID_RESOURCE_ID),
+ material(NULL),
+ isMaterialPointerValid(false),
+ maxBonesPerMaterial(0),
+ indexBufferRequestedSize(0),
+ userSpecifiedData(false),
+ userVertexBufferAlwaysDirty(false),
+ userIndexBufferChanged(false),
+ fallbackSkinningDirty(false)
+{
+}
+
+
+
+ApexRenderMeshActor::SubmeshData::~SubmeshData()
+{
+ if (fallbackSkinningMemory != NULL)
+ {
+ PX_FREE(fallbackSkinningMemory);
+ fallbackSkinningMemory = NULL;
+ }
+ fallbackSkinningMemorySize = 0;
+}
+
+
+
+void ApexRenderMeshActor::setTM(const PxMat44& tm, uint32_t boneIndex /* = 0 */)
+{
+ WRITE_ZONE();
+ PX_ASSERT(boneIndex < mRenderMeshAsset->getBoneCount());
+ mBonePosesDirty = true;
+ PxMat44& boneTM = accessTM(boneIndex);
+ boneTM.column0 = tm.column0;
+ boneTM.column1 = tm.column1;
+ boneTM.column2 = tm.column2;
+ boneTM.column3 = tm.column3;
+}
+
+
+
+void ApexRenderMeshActor::setTM(const PxMat44& tm, const PxVec3& scale, uint32_t boneIndex /* = 0 */)
+{
+ // Assumes tm is pure rotation. This can allow some optimization.
+ WRITE_ZONE();
+ PX_ASSERT(boneIndex < mRenderMeshAsset->getBoneCount());
+ mBonePosesDirty = true;
+ PxMat44& boneTM = accessTM(boneIndex);
+ boneTM.column0 = tm.column0;
+ boneTM.column1 = tm.column1;
+ boneTM.column2 = tm.column2;
+ boneTM.column3 = tm.column3;
+ boneTM.scale(PxVec4(scale, 1.f));
+}
+
+
+void ApexRenderMeshActor::setLastFrameTM(const PxMat44& tm, uint32_t boneIndex /* = 0 */)
+{
+ if (!mPreviousFrameBoneBufferValid)
+ {
+ return;
+ }
+
+ PX_ASSERT(boneIndex < mRenderMeshAsset->getBoneCount());
+ PxMat44& boneTM = accessLastFrameTM(boneIndex);
+ boneTM.column0 = tm.column0;
+ boneTM.column1 = tm.column1;
+ boneTM.column2 = tm.column2;
+ boneTM.column3 = tm.column3;
+}
+
+
+
+void ApexRenderMeshActor::setLastFrameTM(const PxMat44& tm, const PxVec3& scale, uint32_t boneIndex /* = 0 */)
+{
+ if (!mPreviousFrameBoneBufferValid)
+ {
+ return;
+ }
+
+ // Assumes tm is pure rotation. This can allow some optimization.
+
+ PX_ASSERT(boneIndex < mRenderMeshAsset->getBoneCount());
+ PxMat44& boneTM = accessLastFrameTM(boneIndex);
+ boneTM.column0 = tm.column0;
+ boneTM.column1 = tm.column1;
+ boneTM.column2 = tm.column2;
+ boneTM.column3 = tm.column3;
+ boneTM.scale(PxVec4(scale, 1.f));
+}
+
+
+void ApexRenderMeshActor::setSkinningMode(RenderMeshActorSkinningMode::Enum mode)
+{
+ if (mode >= RenderMeshActorSkinningMode::Default && mode < RenderMeshActorSkinningMode::Count)
+ {
+ mSkinningMode = mode;
+ }
+}
+
+RenderMeshActorSkinningMode::Enum ApexRenderMeshActor::getSkinningMode() const
+{
+ return mSkinningMode;
+}
+
+
+void ApexRenderMeshActor::syncVisibility(bool useLock)
+{
+ WRITE_ZONE();
+ if (mApiVisibilityChanged && mBufferVisibility)
+ {
+ if (useLock)
+ {
+ lockRenderResources();
+ }
+ mVisiblePartsForRendering.resize(mVisiblePartsForAPI.usedCount());
+ memcpy(mVisiblePartsForRendering.begin(), mVisiblePartsForAPI.usedIndices(), mVisiblePartsForAPI.usedCount()*sizeof(uint32_t));
+ const uint32_t swapBufferSize = mTMSwapBuffer.size();
+ for (uint32_t i = 0; i < swapBufferSize; ++i)
+ {
+ const uint32_t swapIndices = mTMSwapBuffer[i];
+ nvidia::swap(mTransforms[swapIndices >> 16], mTransforms[swapIndices & 0xFFFF]);
+ }
+ mTMSwapBuffer.reset();
+ mPartVisibilityChanged = true;
+ mApiVisibilityChanged = false;
+ if (useLock)
+ {
+ unlockRenderResources();
+ }
+ }
+}
+
+// TODO - LRR - update part bounds actor bounds to work with >1 bones per part
+void ApexRenderMeshActor::updateBounds()
+{
+ mRenderBounds.setEmpty();
+ const uint32_t* visiblePartIndexPtr = mVisiblePartsForAPI.usedIndices();
+ const uint32_t* visiblePartIndexPtrStop = visiblePartIndexPtr + mVisiblePartsForAPI.usedCount();
+ if (mTransforms.size() < mRenderMeshAsset->getPartCount())
+ {
+ // BRG - for static meshes. We should create a mapping for more generality.
+ PX_ASSERT(mTransforms.size() == 1);
+ PxMat44& tm = accessTM();
+ while (visiblePartIndexPtr < visiblePartIndexPtrStop)
+ {
+ const uint32_t partIndex = *visiblePartIndexPtr++;
+ PxBounds3 partBounds = mRenderMeshAsset->getBounds(partIndex);
+ partBounds = PxBounds3::basisExtent(tm.transform(partBounds.getCenter()), PxMat33(tm.getBasis(0), tm.getBasis(1), tm.getBasis(2)), partBounds.getExtents());
+ mRenderBounds.include(partBounds);
+ }
+ }
+ else
+ {
+ while (visiblePartIndexPtr < visiblePartIndexPtrStop)
+ {
+ const uint32_t partIndex = *visiblePartIndexPtr++;
+ PxBounds3 partBounds = mRenderMeshAsset->getBounds(partIndex);
+ PxMat44& tm = accessTM(partIndex);
+ partBounds = PxBounds3::basisExtent(tm.transform(partBounds.getCenter()), PxMat33(tm.getBasis(0), tm.getBasis(1), tm.getBasis(2)), partBounds.getExtents());
+ mRenderBounds.include(partBounds);
+ }
+ }
+}
+
+void ApexRenderMeshActor::updateInstances(uint32_t submeshIndex)
+{
+ PX_PROFILE_ZONE("ApexRenderMesh::updateInstances", GetInternalApexSDK()->getContextId());
+
+ for (uint32_t i = 0; i < mSubmeshData[submeshIndex].renderResources.size(); ++i)
+ {
+ UserRenderResource* renderResource = mSubmeshData[submeshIndex].renderResources[i].resource;
+ renderResource->setInstanceBufferRange(mInstanceOffset, mInstanceCount);
+ }
+}
+
+void ApexRenderMeshActor::setReleaseResourcesIfNothingToRender(bool value)
+{
+ WRITE_ZONE();
+ mReleaseResourcesIfNothingToRender = value;
+}
+
+void ApexRenderMeshActor::setBufferVisibility(bool bufferVisibility)
+{
+ WRITE_ZONE();
+ mBufferVisibility = bufferVisibility;
+ mPartVisibilityChanged = true;
+}
+
+void ApexRenderMeshActor::setOverrideMaterial(uint32_t index, const char* overrideMaterialName)
+{
+ WRITE_ZONE();
+ ResourceProviderIntl* nrp = GetInternalApexSDK()->getInternalResourceProvider();
+ if (nrp != NULL && index < mSubmeshData.size())
+ {
+ // do create before release, so we don't release the resource if the newID is the same as the old
+ ResID materialNS = GetInternalApexSDK()->getMaterialNameSpace();
+
+ ResID newID = nrp->createResource(materialNS, overrideMaterialName);
+ nrp->releaseResource(mSubmeshData[index].materialID);
+
+ mSubmeshData[index].materialID = newID;
+ mSubmeshData[index].material = NULL;
+ mSubmeshData[index].isMaterialPointerValid = false;
+ mSubmeshData[index].maxBonesPerMaterial = 0;
+
+ if (!GetInternalApexSDK()->getRMALoadMaterialsLazily())
+ {
+ loadMaterial(mSubmeshData[index]);
+ }
+ }
+}
+
+// Need an inverse
+PX_INLINE PxMat44 inverse(const PxMat44& m)
+{
+ const PxMat33 invM33 = PxMat33(m.getBasis(0), m.getBasis(1), m.getBasis(2)).getInverse();
+ return PxMat44(invM33, -(invM33.transform(m.getPosition())));
+}
+
+bool ApexRenderMeshActor::rayCast(RenderMeshActorRaycastHitData& hitData,
+ const PxVec3& worldOrig, const PxVec3& worldDisp,
+ RenderMeshActorRaycastFlags::Enum flags,
+ RenderCullMode::Enum winding,
+ int32_t partIndex) const
+{
+ READ_ZONE();
+ PX_ASSERT(mRenderMeshAsset != NULL);
+ PX_ASSERT(worldOrig.isFinite() && worldDisp.isFinite() && !worldDisp.isZero());
+
+ // Come up with a part range which matches the flags, and if partIndex > 0, ensure it lies within the part range
+ uint32_t rankStart = (flags & RenderMeshActorRaycastFlags::VISIBLE_PARTS) != 0 ? 0 : mVisiblePartsForAPI.usedCount();
+ uint32_t rankStop = (flags & RenderMeshActorRaycastFlags::INVISIBLE_PARTS) != 0 ? mRenderMeshAsset->getPartCount() : mVisiblePartsForAPI.usedCount();
+ // We use the visibility index bank, since it holds visible and invisible parts contiguously
+ if (rankStart >= rankStop)
+ {
+ return false; // No parts selected for raycast
+ }
+ if (partIndex >= 0)
+ {
+ const uint32_t partRank = mVisiblePartsForAPI.getRank((uint32_t)partIndex);
+ if (partRank < rankStart || partRank >= rankStop)
+ {
+ return false;
+ }
+ rankStart = partRank;
+ rankStop = partRank + 1;
+ }
+ const uint32_t* partIndices = mVisiblePartsForAPI.usedIndices();
+
+ // Allocate an inverse transform and local ray for each part and calculate them
+ const uint32_t tmCount = mRenderWithoutSkinning ? 1 : rankStop - rankStart; // Only need one transform if not skinning
+
+ PX_ALLOCA(invTMs, PxMat44, tmCount);
+ PX_ALLOCA(localOrigs, PxVec3, tmCount);
+ PX_ALLOCA(localDisps, PxVec3, tmCount);
+
+ if (mRenderWithoutSkinning)
+ {
+ invTMs[0] = inverse(mTransforms[0]);
+ localOrigs[0] = invTMs[0].transform(worldOrig);
+ localDisps[0] = invTMs[0].rotate(worldDisp);
+ }
+ else
+ {
+ for (uint32_t partRank = rankStart; partRank < rankStop; ++partRank)
+ {
+ invTMs[partRank - rankStart] = inverse(mTransforms[partRank - rankStart]);
+ localOrigs[partRank - rankStart] = invTMs[partRank - rankStart].transform(worldOrig);
+ localDisps[partRank - rankStart] = invTMs[partRank - rankStart].rotate(worldDisp);
+ }
+ }
+
+ // Side "discriminant" - used to reduce branches in inner loops
+ const float disc = winding == RenderCullMode::CLOCKWISE ? 1.0f : (winding == RenderCullMode::COUNTER_CLOCKWISE ? -1.0f : 0.0f);
+
+ // Keeping hit time as a fraction
+ float tNum = -1.0f;
+ float tDen = 0.0f;
+
+ // To do: handle multiple-weighted vertices, and other cases where the number of parts does not equal the number of bones (besides non-skinned, which we do handle)
+// if (single-weighted vertices)
+ {
+ // Traverse the selected parts:
+ const uint32_t submeshCount = mRenderMeshAsset->getSubmeshCount();
+ for (uint32_t submeshIndex = 0; submeshIndex < submeshCount; ++submeshIndex)
+ {
+ const RenderSubmesh& submesh = mRenderMeshAsset->getSubmesh(submeshIndex);
+ const VertexBuffer& vertexBuffer = submesh.getVertexBuffer();
+ const VertexFormat& vertexFormat = vertexBuffer.getFormat();
+ RenderDataFormat::Enum positionFormat;
+ const PxVec3* vertexPositions = (const PxVec3*)vertexBuffer.getBufferAndFormat(positionFormat, (uint32_t)vertexFormat.getBufferIndexFromID(vertexFormat.getSemanticID(nvidia::RenderVertexSemantic::POSITION)));
+ if (positionFormat != RenderDataFormat::FLOAT3)
+ {
+ continue; // Not handling any position format other than FLOAT3
+ }
+ for (uint32_t partRank = rankStart; partRank < rankStop; ++partRank)
+ {
+ const uint32_t cachedLocalIndex = mRenderWithoutSkinning ? 0 : partRank - rankStart;
+ const PxVec3& localOrig = localOrigs[cachedLocalIndex];
+ const PxVec3& localDisp = localDisps[cachedLocalIndex];
+ const uint32_t partIndex = partIndices[partRank];
+ const uint32_t* ib = submesh.getIndexBuffer(partIndex);
+ const uint32_t* ibStop = ib + submesh.getIndexCount(partIndex);
+ PX_ASSERT(submesh.getIndexCount(partIndex) % 3 == 0);
+ for (; ib < ibStop; ib += 3)
+ {
+ const PxVec3 offsetVertices[3] = { vertexPositions[ib[0]] - localOrig, vertexPositions[ib[1]] - localOrig, vertexPositions[ib[2]] - localOrig };
+ const PxVec3 triangleNormal = (offsetVertices[1] - offsetVertices[0]).cross(offsetVertices[2] - offsetVertices[0]);
+ const float den = triangleNormal.dot(localDisp);
+ if (den > -PX_EPS_F32 * PX_EPS_F32)
+ {
+ // Ray misses plane (or is too near parallel)
+ continue;
+ }
+ const float sides[3] = { (offsetVertices[0].cross(offsetVertices[1])).dot(localDisp), (offsetVertices[1].cross(offsetVertices[2])).dot(localDisp), (offsetVertices[2].cross(offsetVertices[0])).dot(localDisp) };
+ if ((int)(sides[0]*disc > 0.0f) | (int)(sides[1]*disc > 0.0f) | (int)(sides[2]*disc > 0.0f))
+ {
+ // Ray misses triangle
+ continue;
+ }
+ // Ray has hit the triangle; calculate time of intersection
+ const float num = offsetVertices[0].dot(triangleNormal);
+ // Since den and tDen both have the same (negative) sign, this is equivalent to : if (num/den < tNum/tDen)
+ if (num * tDen < tNum * den)
+ {
+ // This intersection is earliest
+ tNum = num;
+ tDen = den;
+ hitData.partIndex = partIndex;
+ hitData.submeshIndex = submeshIndex;
+ hitData.vertexIndices[0] = ib[0];
+ hitData.vertexIndices[1] = ib[1];
+ hitData.vertexIndices[2] = ib[2];
+ }
+ }
+ }
+ }
+
+ if (tDen == 0.0f)
+ {
+ // No intersection found
+ return false;
+ }
+
+ // Found a triangle. Fill in hit data
+ hitData.time = tNum / tDen;
+
+ // See if normal, tangent, or binormal can be found
+ const RenderSubmesh& submesh = mRenderMeshAsset->getSubmesh(hitData.submeshIndex);
+ const VertexBuffer& vertexBuffer = submesh.getVertexBuffer();
+ const VertexFormat& vertexFormat = vertexBuffer.getFormat();
+
+ const int32_t normalBufferIndex = vertexFormat.getBufferIndexFromID(vertexFormat.getSemanticID(nvidia::RenderVertexSemantic::NORMAL));
+ const int32_t tangentBufferIndex = vertexFormat.getBufferIndexFromID(vertexFormat.getSemanticID(nvidia::RenderVertexSemantic::TANGENT));
+ const int32_t binormalBufferIndex = vertexFormat.getBufferIndexFromID(vertexFormat.getSemanticID(nvidia::RenderVertexSemantic::BINORMAL));
+
+ ExplicitRenderTriangle triangle;
+ const bool haveNormal = vertexBuffer.getBufferData(&triangle.vertices[0].normal, nvidia::RenderDataFormat::FLOAT3, 0, (uint32_t)normalBufferIndex, hitData.vertexIndices[0], 1);
+ const bool haveTangent = vertexBuffer.getBufferData(&triangle.vertices[0].tangent, nvidia::RenderDataFormat::FLOAT3, 0, (uint32_t)tangentBufferIndex, hitData.vertexIndices[0], 1);
+ const bool haveBinormal = vertexBuffer.getBufferData(&triangle.vertices[0].binormal, nvidia::RenderDataFormat::FLOAT3, 0, (uint32_t)binormalBufferIndex, hitData.vertexIndices[0], 1);
+
+ uint32_t fieldMask = 0;
+
+ if (haveNormal)
+ {
+ vertexBuffer.getBufferData(&triangle.vertices[1].normal, nvidia::RenderDataFormat::FLOAT3, 0, (uint32_t)normalBufferIndex, hitData.vertexIndices[1], 1);
+ vertexBuffer.getBufferData(&triangle.vertices[2].normal, nvidia::RenderDataFormat::FLOAT3, 0, (uint32_t)normalBufferIndex, hitData.vertexIndices[2], 1);
+ fieldMask |= 1 << TriangleFrame::Normal_x | 1 << TriangleFrame::Normal_y | 1 << TriangleFrame::Normal_z;
+ }
+ else
+ {
+ hitData.normal = PxVec3(0.0f);
+ }
+
+ if (haveTangent)
+ {
+ vertexBuffer.getBufferData(&triangle.vertices[1].tangent, nvidia::RenderDataFormat::FLOAT3, 0, (uint32_t)tangentBufferIndex, hitData.vertexIndices[1], 1);
+ vertexBuffer.getBufferData(&triangle.vertices[2].tangent, nvidia::RenderDataFormat::FLOAT3, 0, (uint32_t)tangentBufferIndex, hitData.vertexIndices[2], 1);
+ fieldMask |= 1 << TriangleFrame::Tangent_x | 1 << TriangleFrame::Tangent_y | 1 << TriangleFrame::Tangent_z;
+ }
+ else
+ {
+ hitData.tangent = PxVec3(0.0f);
+ }
+
+ if (haveBinormal)
+ {
+ vertexBuffer.getBufferData(&triangle.vertices[1].binormal, nvidia::RenderDataFormat::FLOAT3, 0, (uint32_t)binormalBufferIndex, hitData.vertexIndices[1], 1);
+ vertexBuffer.getBufferData(&triangle.vertices[2].binormal, nvidia::RenderDataFormat::FLOAT3, 0, (uint32_t)binormalBufferIndex, hitData.vertexIndices[2], 1);
+ fieldMask |= 1 << TriangleFrame::Binormal_x | 1 << TriangleFrame::Binormal_y | 1 << TriangleFrame::Binormal_z;
+ }
+ else
+ {
+ hitData.binormal = PxVec3(0.0f);
+ }
+
+ if (fieldMask != 0)
+ {
+ // We know the positions are in the correct format from the check in the raycast
+ const PxVec3* vertexPositions = (const PxVec3*)vertexBuffer.getBuffer(
+ (uint32_t)vertexFormat.getBufferIndexFromID(vertexFormat.getSemanticID(nvidia::RenderVertexSemantic::POSITION)));
+ triangle.vertices[0].position = vertexPositions[hitData.vertexIndices[0]];
+ triangle.vertices[1].position = vertexPositions[hitData.vertexIndices[1]];
+ triangle.vertices[2].position = vertexPositions[hitData.vertexIndices[2]];
+ TriangleFrame frame(triangle, fieldMask);
+
+ // Find the local hit position
+ const uint32_t partRank = mVisiblePartsForAPI.getRank(hitData.partIndex);
+ const uint32_t cachedLocalIndex = mRenderWithoutSkinning ? 0 : partRank - rankStart;
+ const PxMat44& tm = mTransforms[mRenderWithoutSkinning ? 0 : hitData.partIndex];
+
+ Vertex v;
+ v.position = localOrigs[cachedLocalIndex] + hitData.time * localDisps[cachedLocalIndex];
+ frame.interpolateVertexData(v);
+ if (haveNormal)
+ {
+ hitData.normal = invTMs[cachedLocalIndex].getTranspose().rotate(v.normal);
+ hitData.normal.normalize();
+ }
+
+ if (haveTangent)
+ {
+ hitData.tangent = tm.rotate(v.tangent);
+ hitData.tangent.normalize();
+ }
+
+ if (haveBinormal)
+ {
+ hitData.binormal = tm.rotate(v.binormal);
+ hitData.binormal.normalize();
+ }
+ else
+ {
+ if (haveNormal && haveTangent)
+ {
+ hitData.binormal = hitData.normal.cross(hitData.tangent);
+ hitData.binormal.normalize();
+ }
+ }
+ }
+
+ return true;
+ }
+}
+
+void ApexRenderMeshActor::visualize(RenderDebugInterface& batcher, nvidia::apex::DebugRenderParams* debugParams, PxMat33* scaledRotations, PxVec3* translations, uint32_t stride, uint32_t numberOfTransforms) const
+{
+#ifdef WITHOUT_DEBUG_VISUALIZE
+ PX_UNUSED(batcher);
+ PX_UNUSED(debugParams);
+ PX_UNUSED(scaledRotations);
+ PX_UNUSED(translations);
+ PX_UNUSED(stride);
+ PX_UNUSED(numberOfTransforms);
+#else
+ PX_ASSERT(&batcher != NULL);
+ if ( !mEnableDebugVisualization ) return;
+
+ // This implementation seems to work for destruction and clothing!
+ const float scale = debugParams->Scale;
+ visualizeTangentSpace(batcher, debugParams->RenderNormals * scale, debugParams->RenderTangents * scale, debugParams->RenderBitangents * scale, scaledRotations, translations, stride, numberOfTransforms);
+#endif
+}
+
+} // namespace apex
+} // namespace nvidia