aboutsummaryrefslogtreecommitdiff
path: root/NvBlast/sdk/extensions/physx/source
diff options
context:
space:
mode:
authorBryan Galdrikian <[email protected]>2017-02-21 12:07:59 -0800
committerBryan Galdrikian <[email protected]>2017-02-21 12:07:59 -0800
commit446ce137c6823ba9eff273bdafdaf266287c7c98 (patch)
treed20aab3e2ed08d7b3ca71c2f40db6a93ea00c459 /NvBlast/sdk/extensions/physx/source
downloadblast-1.0.0-beta.tar.xz
blast-1.0.0-beta.zip
first commitv1.0.0-beta
Diffstat (limited to 'NvBlast/sdk/extensions/physx/source')
-rw-r--r--NvBlast/sdk/extensions/physx/source/physics/NvBlastExtImpactDamageManager.cpp448
-rw-r--r--NvBlast/sdk/extensions/physx/source/physics/NvBlastExtImpulseStressSolver.cpp1312
-rw-r--r--NvBlast/sdk/extensions/physx/source/physics/NvBlastExtImpulseStressSolver.h164
-rw-r--r--NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxActorImpl.cpp180
-rw-r--r--NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxActorImpl.h94
-rw-r--r--NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxAssetImpl.cpp315
-rw-r--r--NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxAssetImpl.h126
-rw-r--r--NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxFamilyImpl.cpp294
-rw-r--r--NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxFamilyImpl.h168
-rw-r--r--NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxManagerImpl.cpp127
-rw-r--r--NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxManagerImpl.h202
-rw-r--r--NvBlast/sdk/extensions/physx/source/sync/NvBlastExtSync.cpp235
12 files changed, 3665 insertions, 0 deletions
diff --git a/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtImpactDamageManager.cpp b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtImpactDamageManager.cpp
new file mode 100644
index 0000000..54d2696
--- /dev/null
+++ b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtImpactDamageManager.cpp
@@ -0,0 +1,448 @@
+/*
+* Copyright (c) 2016-2017, 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 "NvBlastExtImpactDamageManager.h"
+#include "NvBlastExtPxManager.h"
+#include "NvBlastExtPxFamily.h"
+#include "NvBlastExtPxActor.h"
+#include "NvBlastExtPxListener.h"
+
+#include "NvBlastAssert.h"
+
+#include "NvBlastExtDamageShaders.h"
+#include "NvBlastExtArray.h"
+#include "NvBlastExtDefs.h"
+
+#include "PxRigidDynamic.h"
+#include "PxSimulationEventCallback.h"
+#include "PxRigidBodyExt.h"
+
+#include "NvBlastTkFramework.h"
+#include "NvBlastTkActor.h"
+#include "NvBlastTkFamily.h"
+#include "NvBlastTkAsset.h"
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+using namespace physx;
+
+const float MIN_IMPACT_VELOCITY_SQUARED = 1.0f;
+
+
+class ExtImpactDamageManagerImpl final : public ExtImpactDamageManager
+{
+public:
+ ExtImpactDamageManagerImpl(ExtPxManager* pxManager, ExtImpactSettings settings)
+ : m_pxManager(pxManager), m_settings(settings), m_listener(this), m_usePxUserData(m_pxManager->isPxUserDataUsed())
+ {
+ NVBLAST_ASSERT_WITH_MESSAGE(pxManager != nullptr, "ExtImpactDamageManager creation: input ExtPxManager is nullptr.");
+ m_pxManager->subscribe(m_listener);
+
+ m_impactDamageBuffer.reserve(32);
+ }
+
+ ~ExtImpactDamageManagerImpl()
+ {
+ m_pxManager->unsubscribe(m_listener);
+ }
+
+ virtual void release() override
+ {
+ NVBLASTEXT_DELETE(this, ExtImpactDamageManagerImpl);
+ }
+
+
+ //////// interface ////////
+
+ virtual void setSettings(const ExtImpactSettings& settings) override
+ {
+ m_settings = settings;
+ }
+
+ virtual void onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, uint32_t nbPairs) override;
+
+ virtual void applyDamage() override;
+
+
+ //////// public methods ////////
+
+ void queueImpactDamage(ExtPxActor* actor, PxVec3 force, PxVec3 position, PxShape* shape)
+ {
+ ImpactDamageData data = { actor, force, position, shape };
+ m_impactDamageBuffer.pushBack(data);
+ }
+
+
+private:
+ //////// physics manager listener ////////
+
+ class PxManagerListener : public ExtPxListener
+ {
+ public:
+ PxManagerListener(ExtImpactDamageManagerImpl* manager) : m_manager(manager) {}
+
+ virtual void onActorCreated(ExtPxFamily&, ExtPxActor&) override {}
+ virtual void onActorDestroyed(ExtPxFamily& family, ExtPxActor& actor) override
+ {
+ NV_UNUSED(family);
+
+ // filter out actor from queued buffer
+ auto& buffer = m_manager->m_impactDamageBuffer;
+ for (int32_t i = 0; i < (int32_t)buffer.size(); ++i)
+ {
+ if (buffer[i].actor == &actor)
+ {
+ buffer.replaceWithLast(i);
+ i--;
+ }
+ }
+ }
+ private:
+ ExtImpactDamageManagerImpl* m_manager;
+ };
+
+
+ //////// private methods ////////
+
+ void ensureBuffersSize(ExtPxActor* actor);
+ void damageActor(ExtPxActor* actor, PxShape* shape, PxVec3 position, PxVec3 force);
+
+
+ //////// data ////////
+
+ ExtPxManager* m_pxManager;
+ ExtImpactSettings m_settings;
+ PxManagerListener m_listener;
+ ExtArray<PxContactPairPoint>::type m_pairPointBuffer;
+ bool m_usePxUserData;
+
+ struct ImpactDamageData
+ {
+ ExtPxActor* actor;
+ PxVec3 force;
+ PxVec3 position;
+ PxShape* shape;
+ };
+
+ ExtArray<ImpactDamageData>::type m_impactDamageBuffer;
+
+ NvBlastFractureBuffers m_fractureBuffers;
+ ExtArray<uint8_t>::type m_fractureData;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// ExtImpactDamageManagerImpl
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ExtImpactDamageManager* ExtImpactDamageManager::create(ExtPxManager* pxManager, ExtImpactSettings settings)
+{
+ return NVBLASTEXT_NEW(ExtImpactDamageManagerImpl) (pxManager, settings);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// onContact callback call
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void ExtImpactDamageManagerImpl::onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, uint32_t nbPairs)
+{
+ if (pairHeader.flags & physx::PxContactPairHeaderFlag::eREMOVED_ACTOR_0 ||
+ pairHeader.flags & physx::PxContactPairHeaderFlag::eREMOVED_ACTOR_1 ||
+ pairHeader.actors[0] == nullptr ||
+ pairHeader.actors[1] == nullptr)
+ {
+ return;
+ }
+
+ PxRigidActor* rigidActor0 = pairHeader.actors[0];
+ PxRigidActor* rigidActor1 = pairHeader.actors[1];
+
+ ExtPxActor* actors[2];
+
+ if (m_usePxUserData)
+ {
+ actors[0] = (ExtPxActor*)rigidActor0->userData;
+ actors[1] = (ExtPxActor*)rigidActor1->userData;
+ }
+ else
+ {
+ PxRigidDynamic* rigidDynamic0 = rigidActor0->is<PxRigidDynamic>();
+ PxRigidDynamic* rigidDynamic1 = rigidActor1->is<PxRigidDynamic>();
+ actors[0] = rigidDynamic0 ? m_pxManager->getActorFromPhysXActor(*rigidDynamic0) : nullptr;
+ actors[1] = rigidDynamic1 ? m_pxManager->getActorFromPhysXActor(*rigidDynamic1) : nullptr;
+ }
+
+
+ // check one of them is blast actor
+ if (actors[0] == nullptr && actors[1] == nullptr)
+ {
+ return;
+ }
+
+ // self-collision check
+ if (actors[0] != nullptr && actors[1] != nullptr)
+ {
+ if (&actors[0]->getFamily() == &actors[1]->getFamily() && !m_settings.isSelfCollissionEnabled)
+ return;
+ }
+
+ for (uint32_t pairIdx = 0; pairIdx < nbPairs; pairIdx++)
+ {
+ const PxContactPair& currentPair = pairs[pairIdx];
+
+ if (currentPair.flags & physx::PxContactPairFlag::eREMOVED_SHAPE_0 ||
+ currentPair.flags & physx::PxContactPairFlag::eREMOVED_SHAPE_1 ||
+ currentPair.shapes[0] == nullptr ||
+ currentPair.shapes[1] == nullptr)
+ {
+ continue;
+ }
+
+ float masses[2] = { 0, 0 };
+ {
+ for (int i = 0; i < 2; ++i)
+ {
+ PxRigidDynamic* rigidDynamic = pairHeader.actors[i]->is<physx::PxRigidDynamic>();
+ if (rigidDynamic)
+ {
+ if (!(rigidDynamic->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC))
+ {
+ masses[i] = rigidDynamic->getMass();
+ }
+ }
+ }
+ };
+
+ float reducedMass;
+ if (masses[0] == 0.0f)
+ {
+ reducedMass = masses[1];
+ }
+ else if (masses[1] == 0.0f)
+ {
+ reducedMass = masses[0];
+ }
+ else
+ {
+ reducedMass = masses[0] * masses[1] / (masses[0] + masses[1]);
+ }
+
+
+ PxVec3 destructibleForces[2] = { PxVec3(0.0f), PxVec3(0.0f) };
+ PxVec3 avgContactPosition = PxVec3(0.0f);
+ PxVec3 avgContactNormal = PxVec3(0.0f);
+ uint32_t numContacts = 0;
+
+ m_pairPointBuffer.resize(currentPair.contactCount);
+ uint32_t numContactsInStream = currentPair.contactCount > 0 ? currentPair.extractContacts(m_pairPointBuffer.begin(), currentPair.contactCount) : 0;
+
+ for (uint32_t contactIdx = 0; contactIdx < numContactsInStream; contactIdx++)
+ {
+ PxContactPairPoint& currentPoint = m_pairPointBuffer[contactIdx];
+
+ const PxVec3& patchNormal = currentPoint.normal;
+ const PxVec3& position = currentPoint.position;
+ PxVec3 velocities[2] = { PxVec3(0.0f), PxVec3(0.0f) };
+ for (int i = 0; i < 2; ++i)
+ {
+ PxRigidBody* rigidBody = pairHeader.actors[i]->is<physx::PxRigidBody>();
+ if (rigidBody)
+ {
+ velocities[i] = physx::PxRigidBodyExt::getVelocityAtPos(*rigidBody, position);
+ }
+ }
+
+ const PxVec3 velocityDelta = velocities[0] - velocities[1];
+ if (velocityDelta.magnitudeSquared() >= MIN_IMPACT_VELOCITY_SQUARED || reducedMass == 0.0f) // If reduced mass == 0, this is kineamtic vs. kinematic. Generate damage.
+ {
+ for (int i = 0; i < 2; ++i)
+ {
+ if (actors[i])
+ {
+ // this is not really physically correct, but at least its deterministic...
+ destructibleForces[i] += (patchNormal * patchNormal.dot(velocityDelta)) * reducedMass * (i ? 1.0f : -1.0f);
+ }
+ }
+ avgContactPosition += position;
+ avgContactNormal += patchNormal;
+ numContacts++;
+ }
+ }
+
+ if (numContacts)
+ {
+ avgContactPosition /= (float)numContacts;
+ avgContactNormal.normalize();
+ for (uint32_t i = 0; i < 2; i++)
+ {
+ const PxVec3 force = destructibleForces[i] / (float)numContacts;
+ ExtPxActor* actor = actors[i];
+ if (actor != nullptr)
+ {
+ if (!force.isZero())
+ {
+ queueImpactDamage(actor, force, avgContactPosition, currentPair.shapes[i]);
+ }
+ else if (reducedMass == 0.0f) // Handle kinematic vs. kinematic
+ {
+ // holy molly
+ }
+ }
+ }
+ }
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// ExtImpactDamageManager damage processing
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+float clampedLerp(float from, float to, float t)
+{
+ t = PxClamp(t, 0.0f, 1.0f);
+ return (1 - t) * from + to * t;
+}
+
+void ExtImpactDamageManagerImpl::applyDamage()
+{
+ const auto damageFn = m_settings.damageFunction;
+ const auto damageFnData = m_settings.damageFunctionData;
+
+ for (const ImpactDamageData& data : m_impactDamageBuffer)
+ {
+ float forceMag = data.force.magnitude();
+ float acceleration = forceMag / data.actor->getPhysXActor().getMass();
+ float factor = acceleration * m_settings.fragility * 0.001f;
+ if (factor > 0.05f)
+ {
+ PxTransform t(data.actor->getPhysXActor().getGlobalPose().getInverse());
+ PxVec3 force = t.rotate(data.force);
+ PxVec3 position = t.transform(data.position);
+
+ if (!damageFn || !damageFn(damageFnData, data.actor, data.shape, position, force))
+ {
+ damageActor(data.actor, data.shape, position, force*.00001f);
+ }
+ }
+ }
+ m_impactDamageBuffer.clear();
+}
+
+void ExtImpactDamageManagerImpl::ensureBuffersSize(ExtPxActor* actor)
+{
+ const TkAsset* tkAsset = actor->getTkActor().getAsset();
+ const uint32_t chunkCount = tkAsset->getChunkCount();
+ const uint32_t bondCount = tkAsset->getBondCount();
+
+ m_fractureBuffers.bondFractureCount = bondCount;
+ m_fractureBuffers.chunkFractureCount = chunkCount;
+ m_fractureData.resize((uint32_t)(m_fractureBuffers.bondFractureCount*sizeof(NvBlastBondFractureData) + m_fractureBuffers.chunkFractureCount*sizeof(NvBlastChunkFractureData))); // chunk count + bond count
+ m_fractureBuffers.chunkFractures = reinterpret_cast<NvBlastChunkFractureData*>(m_fractureData.begin());
+ m_fractureBuffers.bondFractures = reinterpret_cast<NvBlastBondFractureData*>(&m_fractureData.begin()[m_fractureBuffers.chunkFractureCount*sizeof(NvBlastChunkFractureData)]);
+}
+
+void ExtImpactDamageManagerImpl::damageActor(ExtPxActor* actor, PxShape* /*shape*/, PxVec3 position, PxVec3 force)
+{
+ ensureBuffersSize(actor);
+
+ NvBlastExtShearDamageDesc damage[] = {
+ {
+ { force[0], force[1], force[2] }, // shear
+ { position[0], position[1], position[2] } // position
+ }
+ };
+
+ const void* familyMaterial = actor->getTkActor().getFamily().getMaterial();
+
+ // default material params settings
+ const NvBlastExtMaterial defaultMaterial = { 3.0f, 0.1f, 0.2f, 1.5f + 1e-5f, 0.95f };
+
+ NvBlastProgramParams programParams;
+ programParams.damageDescCount = 1;
+ programParams.damageDescBuffer = &damage;
+ programParams.material = familyMaterial == nullptr ? &defaultMaterial : familyMaterial;
+
+ NvBlastDamageProgram program = {
+ NvBlastExtShearGraphShader,
+ NvBlastExtShearSubgraphShader
+ };
+
+ NvBlastFractureBuffers fractureEvents = m_fractureBuffers;
+
+ actor->getTkActor().generateFracture(&fractureEvents, program, &programParams);
+ actor->getTkActor().applyFracture(nullptr, &fractureEvents);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Filter Shader
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+PxFilterFlags ExtImpactDamageManager::FilterShader(
+ PxFilterObjectAttributes attributes0,
+ PxFilterData filterData0,
+ PxFilterObjectAttributes attributes1,
+ PxFilterData filterData1,
+ PxPairFlags& pairFlags,
+ const void* constantBlock,
+ uint32_t constantBlockSize)
+{
+ PX_UNUSED(constantBlock);
+ PX_UNUSED(constantBlockSize);
+ // let triggers through
+ if (PxFilterObjectIsTrigger(attributes0) || PxFilterObjectIsTrigger(attributes1))
+ {
+ pairFlags = PxPairFlag::eTRIGGER_DEFAULT;
+ return PxFilterFlags();
+ }
+
+ if ((PxFilterObjectIsKinematic(attributes0) || PxFilterObjectIsKinematic(attributes1)) &&
+ (PxGetFilterObjectType(attributes0) == PxFilterObjectType::eRIGID_STATIC || PxGetFilterObjectType(attributes1) == PxFilterObjectType::eRIGID_STATIC))
+ {
+ return PxFilterFlag::eSUPPRESS;
+ }
+
+ // use a group-based mechanism if the first two filter data words are not 0
+ uint32_t f0 = filterData0.word0 | filterData0.word1;
+ uint32_t f1 = filterData1.word0 | filterData1.word1;
+ if (f0 && f1 && !(filterData0.word0&filterData1.word1 || filterData1.word0&filterData0.word1))
+ return PxFilterFlag::eSUPPRESS;
+
+ // determine if we should suppress notification
+ const bool suppressNotify = ((filterData0.word3 | filterData1.word3) & ExtPxManager::LEAF_CHUNK) != 0;
+
+ pairFlags = PxPairFlag::eCONTACT_DEFAULT;
+ if (!suppressNotify)
+ {
+ pairFlags = pairFlags
+ | PxPairFlag::eNOTIFY_CONTACT_POINTS
+ | PxPairFlag::eNOTIFY_THRESHOLD_FORCE_PERSISTS
+ | PxPairFlag::eNOTIFY_THRESHOLD_FORCE_FOUND
+ | PxPairFlag::eNOTIFY_TOUCH_FOUND
+ | PxPairFlag::eNOTIFY_TOUCH_PERSISTS;
+ }
+
+ // eSOLVE_CONTACT is invalid with kinematic pairs
+ if (PxFilterObjectIsKinematic(attributes0) && PxFilterObjectIsKinematic(attributes1))
+ {
+ pairFlags &= ~PxPairFlag::eSOLVE_CONTACT;
+ }
+
+ return PxFilterFlags();
+}
+
+} // namespace Blast
+} // namespace Nv
diff --git a/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtImpulseStressSolver.cpp b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtImpulseStressSolver.cpp
new file mode 100644
index 0000000..8329de5
--- /dev/null
+++ b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtImpulseStressSolver.cpp
@@ -0,0 +1,1312 @@
+/*
+* Copyright (c) 2016-2017, 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 "NvBlastExtImpulseStressSolver.h"
+#include "NvBlastExtPxAsset.h"
+#include "NvBlastExtPxFamily.h"
+#include "NvBlastExtPxActor.h"
+#include "NvBlastAssert.h"
+#include "NvBlastIndexFns.h"
+#include "NvBlastExtDefs.h"
+
+#include "NvBlastTkAsset.h"
+#include "NvBlastTkActor.h"
+#include "NvBlastTkFamily.h"
+
+#include "PxScene.h"
+#include "PxRigidDynamic.h"
+
+#include <PsVecMath.h>
+#include "PsFPU.h"
+
+#include <algorithm>
+#include <set>
+
+#define USE_SCALAR_IMPL 0
+#define WARM_START 1
+#define USE_PHYSX_CONVEX_DATA 1
+#define GRAPH_INTERGRIRY_CHECK 0
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+using namespace physx;
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Solver
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+class SequentialImpulseSolver
+{
+public:
+ PX_ALIGN_PREFIX(16)
+ struct BondData
+ {
+ physx::PxVec3 impulseLinear;
+ uint32_t node0;
+ physx::PxVec3 impulseAngular;
+ uint32_t node1;
+ physx::PxVec3 offset0;
+ float invOffsetSqrLength;
+
+ float getStressHealth(const ExtStressSolverSettings& settings) const
+ {
+ return (impulseLinear.magnitude() * settings.stressLinearFactor + impulseAngular.magnitude() * settings.stressAngularFactor);
+ }
+ }
+ PX_ALIGN_SUFFIX(16);
+
+ PX_ALIGN_PREFIX(16)
+ struct NodeData
+ {
+ physx::PxVec3 velocityLinear;
+ float invI;
+ physx::PxVec3 velocityAngular;
+ float invMass;
+ }
+ PX_ALIGN_SUFFIX(16);
+
+ SequentialImpulseSolver(uint32_t nodeCount, uint32_t maxBondCount)
+ {
+ m_nodesData.resize(nodeCount);
+ m_bondsData.reserve(maxBondCount);
+ }
+
+ NV_INLINE const NodeData& getNodeData(uint32_t node) const
+ {
+ return m_nodesData[node];
+ }
+
+ NV_INLINE const BondData& getBondData(uint32_t bond) const
+ {
+ return m_bondsData[bond];
+ }
+
+ NV_INLINE uint32_t getBondCount() const
+ {
+ return m_bondsData.size();
+ }
+
+ NV_INLINE uint32_t getNodeCount() const
+ {
+ return m_nodesData.size();;
+ }
+
+ NV_INLINE void setNodeMassInfo(uint32_t node, float invMass, float invI)
+ {
+ m_nodesData[node].invMass = invMass;
+ m_nodesData[node].invI = invI;
+ }
+
+ NV_INLINE void initialize()
+ {
+ for (auto& node : m_nodesData)
+ {
+ node.velocityLinear = PxVec3(PxZero);
+ node.velocityAngular = PxVec3(PxZero);
+ }
+ }
+
+ NV_INLINE void setNodeVelocities(uint32_t node, const PxVec3& velocityLinear, const PxVec3& velocityAngular)
+ {
+ m_nodesData[node].velocityLinear = velocityLinear;
+ m_nodesData[node].velocityAngular = velocityAngular;
+ }
+
+ NV_INLINE uint32_t addBond(uint32_t node0, uint32_t node1, const PxVec3& offset)
+ {
+ const BondData data = {
+ PxVec3(PxZero),
+ node0,
+ PxVec3(PxZero),
+ node1,
+ offset,
+ 1.0f / offset.magnitudeSquared()
+ };
+ m_bondsData.pushBack(data);
+ return m_bondsData.size() - 1;
+ }
+
+ NV_INLINE void replaceWithLast(uint32_t bondIndex)
+ {
+ m_bondsData.replaceWithLast(bondIndex);
+ }
+
+ NV_INLINE void reset(uint32_t nodeCount)
+ {
+ m_bondsData.clear();
+ m_nodesData.resize(nodeCount);
+ }
+
+ NV_INLINE void clearBonds()
+ {
+ m_bondsData.clear();
+ }
+
+ void solve(uint32_t iterationCount, bool warmStart = false)
+ {
+ solveInit(warmStart);
+
+ for (uint32_t i = 0; i < iterationCount; ++i)
+ {
+ iterate();
+ }
+ }
+
+ void calcError(float& linear, float& angular)
+ {
+ linear = 0.0f;
+ angular = 0.0f;
+ for (BondData& bond : m_bondsData)
+ {
+ NodeData* node0 = &m_nodesData[bond.node0];
+ NodeData* node1 = &m_nodesData[bond.node1];
+
+ const PxVec3 vA = node0->velocityLinear - node0->velocityAngular.cross(bond.offset0);
+ const PxVec3 vB = node1->velocityLinear + node1->velocityAngular.cross(bond.offset0);
+
+ const PxVec3 vErrorLinear = vA - vB;
+ const PxVec3 vErrorAngular = node0->velocityAngular - node1->velocityAngular;
+
+ linear += vErrorLinear.magnitude();
+ angular += vErrorAngular.magnitude();
+ }
+ }
+
+private:
+ void solveInit(bool warmStart = false)
+ {
+ if (warmStart)
+ {
+ for (BondData& bond : m_bondsData)
+ {
+ NodeData* node0 = &m_nodesData[bond.node0];
+ NodeData* node1 = &m_nodesData[bond.node1];
+
+ const PxVec3 velocityLinearCorr0 = bond.impulseLinear * node0->invMass;
+ const PxVec3 velocityLinearCorr1 = bond.impulseLinear * node1->invMass;
+
+ const PxVec3 velocityAngularCorr0 = bond.impulseAngular * node0->invI - bond.offset0.cross(velocityLinearCorr0) * bond.invOffsetSqrLength;
+ const PxVec3 velocityAngularCorr1 = bond.impulseAngular * node1->invI + bond.offset0.cross(velocityLinearCorr1) * bond.invOffsetSqrLength;
+
+ node0->velocityLinear += velocityLinearCorr0;
+ node1->velocityLinear -= velocityLinearCorr1;
+
+ node0->velocityAngular += velocityAngularCorr0;
+ node1->velocityAngular -= velocityAngularCorr1;
+ }
+ }
+ else
+ {
+ for (BondData& bond : m_bondsData)
+ {
+ bond.impulseLinear = PxVec3(PxZero);
+ bond.impulseAngular = PxVec3(PxZero);
+ }
+ }
+ }
+
+
+ NV_INLINE void iterate()
+ {
+ using namespace physx::shdfnd::aos;
+
+ for (BondData& bond : m_bondsData)
+ {
+ NodeData* node0 = &m_nodesData[bond.node0];
+ NodeData* node1 = &m_nodesData[bond.node1];
+
+#if USE_SCALAR_IMPL
+ const PxVec3 vA = node0->velocityLinear - node0->velocityAngular.cross(bond.offset0);
+ const PxVec3 vB = node1->velocityLinear + node1->velocityAngular.cross(bond.offset0);
+
+ const PxVec3 vErrorLinear = vA - vB;
+ const PxVec3 vErrorAngular = node0->velocityAngular - node1->velocityAngular;
+
+ const float weightedMass = 1.0f / (node0->invMass + node1->invMass);
+ const float weightedInertia = 1.0f / (node0->invI + node1->invI);
+
+ const PxVec3 outImpulseLinear = -vErrorLinear * weightedMass * 0.5f;
+ const PxVec3 outImpulseAngular = -vErrorAngular * weightedInertia * 0.5f;
+
+ bond.impulseLinear += outImpulseLinear;
+ bond.impulseAngular += outImpulseAngular;
+
+ const PxVec3 velocityLinearCorr0 = outImpulseLinear * node0->invMass;
+ const PxVec3 velocityLinearCorr1 = outImpulseLinear * node1->invMass;
+
+ const PxVec3 velocityAngularCorr0 = outImpulseAngular * node0->invI - bond.offset0.cross(velocityLinearCorr0) * bond.invOffsetSqrLength;
+ const PxVec3 velocityAngularCorr1 = outImpulseAngular * node1->invI + bond.offset0.cross(velocityLinearCorr1) * bond.invOffsetSqrLength;
+
+ node0->velocityLinear += velocityLinearCorr0;
+ node1->velocityLinear -= velocityLinearCorr1;
+
+ node0->velocityAngular += velocityAngularCorr0;
+ node1->velocityAngular -= velocityAngularCorr1;
+#else
+ const Vec3V velocityLinear0 = V3LoadUnsafeA(node0->velocityLinear);
+ const Vec3V velocityLinear1 = V3LoadUnsafeA(node1->velocityLinear);
+ const Vec3V velocityAngular0 = V3LoadUnsafeA(node0->velocityAngular);
+ const Vec3V velocityAngular1 = V3LoadUnsafeA(node1->velocityAngular);
+
+ const Vec3V offset = V3LoadUnsafeA(bond.offset0);
+ const Vec3V vA = V3Add(velocityLinear0, V3Neg(V3Cross(velocityAngular0, offset)));
+ const Vec3V vB = V3Add(velocityLinear1, V3Cross(velocityAngular1, offset));
+
+ const Vec3V vErrorLinear = V3Sub(vA, vB);
+ const Vec3V vErrorAngular = V3Sub(velocityAngular0, velocityAngular1);
+
+ const FloatV invM0 = FLoad(node0->invMass);
+ const FloatV invM1 = FLoad(node1->invMass);
+ const FloatV invI0 = FLoad(node0->invI);
+ const FloatV invI1 = FLoad(node1->invI);
+ const FloatV invOffsetSqrLength = FLoad(bond.invOffsetSqrLength);
+
+ const FloatV weightedMass = FLoad(-0.5f / (node0->invMass + node1->invMass));
+ const FloatV weightedInertia = FLoad(-0.5f / (node0->invI + node1->invI));
+
+ const Vec3V outImpulseLinear = V3Scale(vErrorLinear, weightedMass);
+ const Vec3V outImpulseAngular = V3Scale(vErrorAngular, weightedInertia);
+
+ V3StoreA(V3Add(V3LoadUnsafeA(bond.impulseLinear), outImpulseLinear), bond.impulseLinear);
+ V3StoreA(V3Add(V3LoadUnsafeA(bond.impulseAngular), outImpulseAngular), bond.impulseAngular);
+
+ const Vec3V velocityLinearCorr0 = V3Scale(outImpulseLinear, invM0);
+ const Vec3V velocityLinearCorr1 = V3Scale(outImpulseLinear, invM1);
+
+ const Vec3V velocityAngularCorr0 = V3Sub(V3Scale(outImpulseAngular, invI0), V3Scale(V3Cross(offset, velocityLinearCorr0), invOffsetSqrLength));
+ const Vec3V velocityAngularCorr1 = V3Add(V3Scale(outImpulseAngular, invI1), V3Scale(V3Cross(offset, velocityLinearCorr1), invOffsetSqrLength));
+
+ V3StoreA(V3Add(velocityLinear0, velocityLinearCorr0), node0->velocityLinear);
+ V3StoreA(V3Sub(velocityLinear1, velocityLinearCorr1), node1->velocityLinear);
+
+ V3StoreA(V3Add(velocityAngular0, velocityAngularCorr0), node0->velocityAngular);
+ V3StoreA(V3Sub(velocityAngular1, velocityAngularCorr1), node1->velocityAngular);
+#endif
+ }
+ }
+
+ shdfnd::Array<BondData, ExtAlignedAllocator<16>> m_bondsData;
+ shdfnd::Array<NodeData, ExtAlignedAllocator<16>> m_nodesData;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Graph Processor
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if GRAPH_INTERGRIRY_CHECK
+#define CHECK_GRAPH_INTEGRITY checkGraphIntegrity()
+#else
+#define CHECK_GRAPH_INTEGRITY ((void)0)
+#endif
+
+class SupportGraphProcessor
+{
+
+public:
+ struct BondData
+ {
+ uint32_t node0;
+ uint32_t node1;
+ uint32_t blastBondIndex;
+ };
+
+ struct NodeData
+ {
+ float mass;
+ float volume;
+ PxVec3 localPos;
+ bool isStatic;
+ uint32_t solverNode;
+ uint32_t neighborsCount;
+ PxVec3 impulse;
+ };
+
+ struct SolverNodeData
+ {
+ uint32_t supportNodesCount;
+ PxVec3 localPos;
+ union
+ {
+ float mass;
+ int32_t indexShift;
+ };
+ float volume;
+ bool isStatic;
+ };
+
+ struct SolverBondData
+ {
+ ExtInlineArray<uint32_t, 8>::type blastBondIndices;
+ };
+
+ SupportGraphProcessor(uint32_t nodeCount, uint32_t maxBondCount) : m_solver(nodeCount, maxBondCount), m_nodesDirty(true)
+ {
+ m_nodesData.resize(nodeCount);
+ m_bondsData.reserve(maxBondCount);
+
+ m_solverNodesData.resize(nodeCount);
+ m_solverBondsData.reserve(maxBondCount);
+
+ m_solverBondsMap.reserve(maxBondCount);
+
+ m_blastBondIndexMap.resize(maxBondCount);
+ memset(m_blastBondIndexMap.begin(), 0xFF, m_blastBondIndexMap.size() * sizeof(uint32_t));
+ }
+
+ NV_INLINE const NodeData& getNodeData(uint32_t node) const
+ {
+ return m_nodesData[node];
+ }
+
+ NV_INLINE const BondData& getBondData(uint32_t bond) const
+ {
+ return m_bondsData[bond];
+ }
+
+ NV_INLINE const SolverNodeData& getSolverNodeData(uint32_t node) const
+ {
+ return m_solverNodesData[node];
+ }
+
+ NV_INLINE const SolverBondData& getSolverBondData(uint32_t bond) const
+ {
+ return m_solverBondsData[bond];
+ }
+
+ NV_INLINE const SequentialImpulseSolver::BondData& getSolverInternalBondData(uint32_t bond) const
+ {
+ return m_solver.getBondData(bond);
+ }
+
+ NV_INLINE const SequentialImpulseSolver::NodeData& getSolverInternalNodeData(uint32_t node) const
+ {
+ return m_solver.getNodeData(node);
+ }
+
+ NV_INLINE uint32_t getBondCount() const
+ {
+ return m_bondsData.size();
+ }
+
+ NV_INLINE uint32_t getNodeCount() const
+ {
+ return m_nodesData.size();;
+ }
+
+ NV_INLINE uint32_t getSolverBondCount() const
+ {
+ return m_solverBondsData.size();
+ }
+
+ NV_INLINE uint32_t getSolverNodeCount() const
+ {
+ return m_solverNodesData.size();;
+ }
+
+ NV_INLINE void setNodeInfo(uint32_t node, float mass, float volume, PxVec3 localPos, bool isStatic)
+ {
+ m_nodesData[node].mass = mass;
+ m_nodesData[node].volume = volume;
+ m_nodesData[node].localPos = localPos;
+ m_nodesData[node].isStatic = isStatic;
+ }
+
+ NV_INLINE void setNodeNeighborsCount(uint32_t node, uint32_t neighborsCount)
+ {
+ // neighbors count is expected to be the number of nodes on 1 island/actor.
+ m_nodesData[node].neighborsCount = neighborsCount;
+
+ // check for too huge aggregates (happens after island's split)
+ if (!m_nodesDirty)
+ {
+ m_nodesDirty |= (m_solverNodesData[m_nodesData[node].solverNode].supportNodesCount > neighborsCount / 2);
+ }
+ }
+
+ NV_INLINE void initialize()
+ {
+ sync();
+
+ m_solver.initialize();
+
+ for (auto& node : m_nodesData)
+ {
+ node.impulse = PxVec3(PxZero);
+ }
+ }
+
+ NV_INLINE void addNodeImpulse(uint32_t node, const PxVec3& impulse)
+ {
+ m_nodesData[node].impulse += impulse;
+ }
+
+ NV_INLINE void addNodeVelocity(uint32_t node, const PxVec3& velocity)
+ {
+ PxVec3 impulse = velocity * m_nodesData[node].mass;
+ addNodeImpulse(node, impulse);
+ }
+
+ NV_INLINE void addBond(uint32_t node0, uint32_t node1, uint32_t blastBondIndex)
+ {
+ if (isInvalidIndex(m_blastBondIndexMap[blastBondIndex]))
+ {
+ const BondData data = {
+ node0,
+ node1,
+ blastBondIndex
+ };
+ m_bondsData.pushBack(data);
+ m_blastBondIndexMap[blastBondIndex] = m_bondsData.size() - 1;
+ }
+ }
+
+ NV_INLINE void removeBondIfExists(uint32_t blastBondIndex)
+ {
+ const uint32_t bondIndex = m_blastBondIndexMap[blastBondIndex];
+
+ if (!isInvalidIndex(bondIndex))
+ {
+ const BondData& bond = m_bondsData[bondIndex];
+ const uint32_t solverNode0 = m_nodesData[bond.node0].solverNode;
+ const uint32_t solverNode1 = m_nodesData[bond.node1].solverNode;
+ bool isBondInternal = (solverNode0 == solverNode1);
+
+ if (isBondInternal)
+ {
+ // internal bond sadly requires graph resync (it never happens on reduction level '0')
+ m_nodesDirty = true;
+ }
+ else if (!m_nodesDirty)
+ {
+ // otherwise it's external bond, we can remove it manually and keep graph synced
+ // we don't need to spend time there if (m_nodesDirty == true), graph will be resynced anyways
+
+ BondKey solverBondKey(solverNode0, solverNode1);
+ auto entry = m_solverBondsMap.find(solverBondKey);
+ if (entry)
+ {
+ const uint32_t solverBondIndex = entry->second;
+ auto& blastBondIndices = m_solverBondsData[solverBondIndex].blastBondIndices;
+ blastBondIndices.findAndReplaceWithLast(blastBondIndex);
+ if (blastBondIndices.empty())
+ {
+ // all bonds associated with this solver bond were removed, so let's remove solver bond
+
+ m_solverBondsData.replaceWithLast(solverBondIndex);
+ m_solver.replaceWithLast(solverBondIndex);
+ if (m_solver.getBondCount() > 0)
+ {
+ // update 'previously last' solver bond mapping
+ const auto& solverBond = m_solver.getBondData(solverBondIndex);
+ m_solverBondsMap[BondKey(solverBond.node0, solverBond.node1)] = solverBondIndex;
+ }
+
+ m_solverBondsMap.erase(solverBondKey);
+ }
+ }
+
+ CHECK_GRAPH_INTEGRITY;
+ }
+
+ // remove bond from graph processor's list
+ m_blastBondIndexMap[blastBondIndex] = invalidIndex<uint32_t>();
+ m_bondsData.replaceWithLast(bondIndex);
+ m_blastBondIndexMap[m_bondsData[bondIndex].blastBondIndex] = m_bondsData.size() > bondIndex ? bondIndex : invalidIndex<uint32_t>();
+ }
+ }
+
+ NV_INLINE void setGraphReductionLevel(uint32_t level)
+ {
+ m_graphReductionLevel = level;
+ m_nodesDirty = true;
+ }
+
+ uint32_t getGraphReductionLevel() const
+ {
+ return m_graphReductionLevel;
+ }
+
+ void solve(uint32_t iterationCount, bool warmStart = false)
+ {
+ CHECK_GRAPH_INTEGRITY;
+
+ for (const NodeData& node : m_nodesData)
+ {
+ const SequentialImpulseSolver::NodeData& solverNode = m_solver.getNodeData(node.solverNode);
+ m_solver.setNodeVelocities(node.solverNode, solverNode.velocityLinear + node.impulse * solverNode.invMass, PxVec3(PxZero));
+ }
+
+ m_solver.solve(iterationCount, warmStart);
+ }
+
+ void calcError(float& linear, float& angular)
+ {
+ m_solver.calcError(linear, angular);
+ }
+
+ void generateFracture(ExtArray<NvBlastBondFractureData>::type& bondFractureBuffer, const ExtStressSolverSettings& settings, const float* blastBondHealths)
+ {
+ CHECK_GRAPH_INTEGRITY;
+
+ for (uint32_t i = 0; i < m_solverBondsData.size(); ++i)
+ {
+ const SequentialImpulseSolver::BondData& solverInternalBond = m_solver.getBondData(i);
+ if (solverInternalBond.getStressHealth(settings) > 1.0f)
+ {
+ const auto& blastBondIndices = m_solverBondsData[i].blastBondIndices;
+ for (auto blastBondIndex : blastBondIndices)
+ {
+ const uint32_t bondIndex = m_blastBondIndexMap[blastBondIndex];
+ if (!isInvalidIndex(bondIndex))
+ {
+ const BondData& bond = m_bondsData[bondIndex];
+
+ NVBLAST_ASSERT(getNodeData(bond.node0).solverNode != getNodeData(bond.node1).solverNode);
+ NVBLAST_ASSERT(bond.blastBondIndex == blastBondIndex);
+
+ NvBlastBondFractureData data;
+ data.health = blastBondHealths[blastBondIndex];
+ data.nodeIndex0 = bond.node0;
+ data.nodeIndex1 = bond.node1;
+ bondFractureBuffer.pushBack(data);
+ }
+ }
+ }
+ }
+ }
+
+private:
+
+ NV_INLINE void sync()
+ {
+ if (m_nodesDirty)
+ {
+ syncNodes();
+ }
+ if (m_bondsDirty)
+ {
+ syncBonds();
+ }
+
+ CHECK_GRAPH_INTEGRITY;
+ }
+
+ void syncNodes()
+ {
+ // init with 1<->1 blast nodes to solver nodes mapping
+ m_solverNodesData.resize(m_nodesData.size());
+ for (uint32_t i = 0; i < m_nodesData.size(); ++i)
+ {
+ m_nodesData[i].solverNode = i;
+ m_solverNodesData[i].supportNodesCount = 1;
+ m_solverNodesData[i].indexShift = 0;
+ }
+
+ // for static nodes aggregate size per graph reduction level is lower, it
+ // falls behind on few levels. (can be made as parameter)
+ const uint32_t STATIC_NODES_COUNT_PENALTY = 2 << 2;
+
+ // reducing graph by aggregating nodes level by level
+ for (uint32_t k = 0; k < m_graphReductionLevel; k++)
+ {
+ const uint32_t maxAggregateSize = 1 << (k + 1);
+
+ for (const BondData& bond : m_bondsData)
+ {
+ NodeData& node0 = m_nodesData[bond.node0];
+ NodeData& node1 = m_nodesData[bond.node1];
+
+ if (node0.isStatic != node1.isStatic)
+ continue;
+
+ if (node0.solverNode == node1.solverNode)
+ continue;
+
+ SolverNodeData& solverNode0 = m_solverNodesData[node0.solverNode];
+ SolverNodeData& solverNode1 = m_solverNodesData[node1.solverNode];
+
+ const int countPenalty = node0.isStatic ? STATIC_NODES_COUNT_PENALTY : 1;
+ const uint32_t aggregateSize = std::min<uint32_t>(maxAggregateSize, node0.neighborsCount / 2);
+
+ if (solverNode0.supportNodesCount * countPenalty >= aggregateSize)
+ continue;
+ if (solverNode1.supportNodesCount * countPenalty >= aggregateSize)
+ continue;
+
+ if (solverNode0.supportNodesCount >= solverNode1.supportNodesCount)
+ {
+ solverNode1.supportNodesCount--;
+ solverNode0.supportNodesCount++;
+ node1.solverNode = node0.solverNode;
+ }
+ else if (solverNode1.supportNodesCount >= solverNode0.supportNodesCount)
+ {
+ solverNode1.supportNodesCount++;
+ solverNode0.supportNodesCount--;
+ node0.solverNode = node1.solverNode;
+ }
+ }
+ }
+
+ // Solver Nodes now sparse, a lot of empty ones. Rearrange them by moving all non-empty to the front
+ // 2 passes used for that
+ {
+ uint32_t currentNode = 0;
+ for (; currentNode < m_solverNodesData.size(); ++currentNode)
+ {
+ if (m_solverNodesData[currentNode].supportNodesCount > 0)
+ continue;
+
+ // 'currentNode' is free
+
+ // search next occupied node
+ uint32_t k = currentNode + 1;
+ for (; k < m_solverNodesData.size(); ++k)
+ {
+ if (m_solverNodesData[k].supportNodesCount > 0)
+ {
+ // replace currentNode and keep indexShift
+ m_solverNodesData[currentNode].supportNodesCount = m_solverNodesData[k].supportNodesCount;
+ m_solverNodesData[k].indexShift = k - currentNode;
+ m_solverNodesData[k].supportNodesCount = 0;
+ break;
+ }
+ }
+
+ if (k == m_solverNodesData.size())
+ {
+ break;
+ }
+ }
+ for (auto& node : m_nodesData)
+ {
+ node.solverNode -= m_solverNodesData[node.solverNode].indexShift;
+ }
+
+ // now, we know total solver nodes count and which nodes are aggregated into them
+ m_solverNodesData.resize(currentNode);
+ }
+
+
+ // calculate all needed data
+ for (SolverNodeData& solverNode : m_solverNodesData)
+ {
+ solverNode.supportNodesCount = 0;
+ solverNode.localPos = PxVec3(PxZero);
+ solverNode.mass = 0.0f;
+ solverNode.volume = 0.0f;
+ solverNode.isStatic = false;
+ }
+
+ for (NodeData& node : m_nodesData)
+ {
+ SolverNodeData& solverNode = m_solverNodesData[node.solverNode];
+ solverNode.supportNodesCount++;
+ solverNode.localPos += node.localPos;
+ solverNode.mass += node.mass;
+ solverNode.volume += node.volume;
+ solverNode.isStatic |= node.isStatic;
+ }
+
+ for (SolverNodeData& solverNode : m_solverNodesData)
+ {
+ solverNode.localPos /= (float)solverNode.supportNodesCount;
+ }
+
+ m_solver.reset(m_solverNodesData.size());
+ for (uint32_t nodeIndex = 0; nodeIndex < m_solverNodesData.size(); ++nodeIndex)
+ {
+ const SolverNodeData& solverNode = m_solverNodesData[nodeIndex];
+
+ const float invMass = solverNode.isStatic ? 0.0f : 1.0f / solverNode.mass;
+ const float R = PxPow(solverNode.volume * 3.0f * PxInvPi / 4.0f, 1.0f / 3.0f); // sphere volume approximation
+ const float invI = invMass / (R * R * 0.4f); // sphere inertia tensor approximation: I = 2/5 * M * R^2 ; invI = 1 / I;
+ m_solver.setNodeMassInfo(nodeIndex, invMass, invI);
+ }
+
+ m_nodesDirty = false;
+
+ syncBonds();
+ }
+
+ void syncBonds()
+ {
+ // traverse all blast bonds and aggregate
+ m_solver.clearBonds();
+ m_solverBondsMap.clear();
+ m_solverBondsData.clear();
+ for (const BondData& bond : m_bondsData)
+ {
+ const NodeData& node0 = m_nodesData[bond.node0];
+ const NodeData& node1 = m_nodesData[bond.node1];
+
+ if (node0.solverNode == node1.solverNode)
+ continue; // skip (internal)
+
+ if (node0.isStatic && node1.isStatic)
+ continue;
+
+ BondKey key(node0.solverNode, node1.solverNode);
+ auto entry = m_solverBondsMap.find(key);
+ SolverBondData* data;
+ if (!entry)
+ {
+ m_solverBondsData.pushBack(SolverBondData());
+ data = &m_solverBondsData.back();
+ m_solverBondsMap[key] = m_solverBondsData.size() - 1;
+
+ SolverNodeData& solverNode0 = m_solverNodesData[node0.solverNode];
+ SolverNodeData& solverNode1 = m_solverNodesData[node1.solverNode];
+ m_solver.addBond(node0.solverNode, node1.solverNode, (solverNode1.localPos - solverNode0.localPos) * 0.5f);
+ }
+ else
+ {
+ data = &m_solverBondsData[entry->second];
+ }
+ data->blastBondIndices.pushBack(bond.blastBondIndex);
+ }
+
+ m_bondsDirty = false;
+ }
+
+#if GRAPH_INTERGRIRY_CHECK
+ void checkGraphIntegrity()
+ {
+ NVBLAST_ASSERT(m_solver.getBondCount() == m_solverBondsData.size());
+ NVBLAST_ASSERT(m_solver.getNodeCount() == m_solverNodesData.size());
+
+ std::set<uint64_t> solverBonds;
+ for (uint32_t i = 0; i < m_solverBondsData.size(); ++i)
+ {
+ const auto& bondData = m_solver.getBondData(i);
+ BondKey key(bondData.node0, bondData.node1);
+ NVBLAST_ASSERT(solverBonds.find(key) == solverBonds.end());
+ solverBonds.emplace(key);
+ auto entry = m_solverBondsMap.find(key);
+ NVBLAST_ASSERT(entry != nullptr);
+ const auto& solverBond = m_solverBondsData[entry->second];
+ for (auto& blastBondIndex : solverBond.blastBondIndices)
+ {
+ if (!isInvalidIndex(m_blastBondIndexMap[blastBondIndex]))
+ {
+ auto& b = m_bondsData[m_blastBondIndexMap[blastBondIndex]];
+ BondKey key2(m_nodesData[b.node0].solverNode, m_nodesData[b.node1].solverNode);
+ NVBLAST_ASSERT(key2 == key);
+ }
+ }
+ }
+
+ for (auto& solverBond : m_solverBondsData)
+ {
+ for (auto& blastBondIndex : solverBond.blastBondIndices)
+ {
+ if (!isInvalidIndex(m_blastBondIndexMap[blastBondIndex]))
+ {
+ auto& b = m_bondsData[m_blastBondIndexMap[blastBondIndex]];
+ NVBLAST_ASSERT(m_nodesData[b.node0].solverNode != m_nodesData[b.node1].solverNode);
+ }
+ }
+ }
+ uint32_t mappedBondCount = 0;
+ for (uint32_t i = 0; i < m_blastBondIndexMap.size(); i++)
+ {
+ const auto& bondIndex = m_blastBondIndexMap[i];
+ if (!isInvalidIndex(bondIndex))
+ {
+ mappedBondCount++;
+ NVBLAST_ASSERT(m_bondsData[bondIndex].blastBondIndex == i);
+ }
+ }
+ NVBLAST_ASSERT(m_bondsData.size() == mappedBondCount);
+ }
+#endif
+
+ struct BondKey
+ {
+ uint32_t node0;
+ uint32_t node1;
+
+ BondKey(uint32_t n0, uint32_t n1)
+ {
+ node0 = n0 < n1 ? n0 : n1;
+ node1 = n0 < n1 ? n1 : n0;
+ }
+
+ operator uint64_t() const
+ {
+ return static_cast<uint64_t>(node0) + (static_cast<uint64_t>(node1) << 32);
+ }
+ };
+
+ SequentialImpulseSolver m_solver;
+ ExtArray<SolverNodeData>::type m_solverNodesData;
+ ExtArray<SolverBondData>::type m_solverBondsData;
+
+ uint32_t m_graphReductionLevel;
+
+ bool m_nodesDirty;
+ bool m_bondsDirty;
+
+ ExtHashMap<BondKey, uint32_t>::type m_solverBondsMap;
+ ExtArray<uint32_t>::type m_blastBondIndexMap;
+
+ ExtArray<BondData>::type m_bondsData;
+ ExtArray<NodeData>::type m_nodesData;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// ExtImpulseStressSolver
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Creation
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ExtImpulseStressSolver::ExtImpulseStressSolver(ExtPxFamily& family, ExtStressSolverSettings settings)
+ : m_family(family), m_settings(settings), m_isDirty(false), m_reset(false),
+ m_errorAngular(std::numeric_limits<float>::max()), m_errorLinear(std::numeric_limits<float>::max()), m_framesCount(0)
+{
+
+ const TkAsset* tkAsset = m_family.getTkFamily().getAsset();
+ const ExtPxAsset& asset = m_family.getPxAsset();
+ const ExtPxChunk* chunks = asset.getChunks();
+ const ExtPxSubchunk* subChunks = asset.getSubchunks();
+ m_graph = tkAsset->getGraph();
+ const uint32_t bondCount = tkAsset->getBondCount();
+
+ TkActor* tkActor;
+ m_family.getTkFamily().getActors(&tkActor, 1);
+ m_bondHealths = tkActor->getBondHealths();
+
+ m_graphProcessor = NVBLASTEXT_NEW(SupportGraphProcessor)(m_graph.nodeCount, bondCount);
+
+ // traverse graph and fill node info
+ for (uint32_t i = 0; i < m_graph.nodeCount; ++i)
+ {
+ uint32_t node0 = i;
+ uint32_t chunkIndex0 = m_graph.chunkIndices[node0];
+ const ExtPxChunk& chunk0 = chunks[chunkIndex0];
+
+ bool isChunkStatic = chunk0.isStatic;
+
+ for (uint32_t adjacencyIndex = m_graph.adjacencyPartition[node0]; adjacencyIndex < m_graph.adjacencyPartition[node0 + 1]; adjacencyIndex++)
+ {
+ uint32_t bondIndex = m_graph.adjacentBondIndices[adjacencyIndex];
+ if (m_bondHealths[bondIndex] <= 0.0f)
+ continue;
+ uint32_t node1 = m_graph.adjacentNodeIndices[adjacencyIndex];
+ uint32_t chunkIndex1 = m_graph.chunkIndices[node1];
+ const ExtPxChunk& chunk1 = chunks[chunkIndex1];
+
+ if (chunk1.subchunkCount == 0 || chunk1.isStatic)
+ {
+ isChunkStatic |= chunk1.isStatic;
+ continue;
+ }
+ }
+
+ // fill node info
+
+ float mass;
+ float volume;
+ PxVec3 localPos;
+ if (chunk0.subchunkCount > 0)
+ {
+#if USE_PHYSX_CONVEX_DATA
+ const ExtPxSubchunk& subChunk = subChunks[chunk0.firstSubchunkIndex];
+ PxVec3 localCenterOfMass;
+ PxMat33 intertia;
+ PxVec3 scale = subChunk.geometry.scale.scale;
+ subChunk.geometry.convexMesh->getMassInformation(mass, intertia, localCenterOfMass);
+ mass *= scale.x * scale.y * scale.z;
+ const PxTransform& chunk0LocalTransform = subChunk.transform;
+ localPos = chunk0LocalTransform.transform(localCenterOfMass);
+ volume = mass / 1.0f; // unit density
+#else
+ volume = solverChunk0.volume;
+ mass = volume * 1.0f; // density
+ localPos = *reinterpret_cast<const PxVec3*>(solverChunk0.centroid);
+#endif
+ }
+ else
+ {
+ mass = 0.0f;
+ volume = 0.0f;
+ localPos = PxVec3(PxZero);
+ isChunkStatic = true;
+ }
+ m_graphProcessor->setNodeInfo(node0, mass, volume, localPos, isChunkStatic);
+ }
+
+ // traverse graph and fill bond info
+ for (uint32_t node0 = 0; node0 < m_graph.nodeCount; ++node0)
+ {
+ for (uint32_t adjacencyIndex = m_graph.adjacencyPartition[node0]; adjacencyIndex < m_graph.adjacencyPartition[node0 + 1]; adjacencyIndex++)
+ {
+ uint32_t bondIndex = m_graph.adjacentBondIndices[adjacencyIndex];
+ if (m_bondHealths[bondIndex] <= 0.0f)
+ continue;
+ uint32_t node1 = m_graph.adjacentNodeIndices[adjacencyIndex];
+
+ if (node0 < node1)
+ {
+ m_graphProcessor->addBond(node0, node1, bondIndex);
+ }
+ }
+ }
+
+ // fire initial actor's created
+ ExtInlineArray<ExtPxActor*, 4>::type actors;;
+ actors.resize((uint32_t)m_family.getActorCount());
+ m_family.getActors(actors.begin(), actors.size());
+ for (const auto actor : actors)
+ {
+ onActorCreated(m_family, *actor);
+ }
+
+ m_family.subscribe(*this);
+}
+
+ExtImpulseStressSolver::~ExtImpulseStressSolver()
+{
+ NVBLASTEXT_DELETE(m_graphProcessor, SupportGraphProcessor);
+ m_family.unsubscribe(*this);
+}
+
+ExtStressSolver* ExtStressSolver::create(ExtPxFamily& family, ExtStressSolverSettings settings)
+{
+ return NVBLASTEXT_NEW(ExtImpulseStressSolver) (family, settings);
+}
+
+void ExtImpulseStressSolver::release()
+{
+ NVBLASTEXT_DELETE(this, ExtImpulseStressSolver);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Actors
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void ExtImpulseStressSolver::onActorCreated(ExtPxFamily& /*family*/, ExtPxActor& actor)
+{
+ if (actor.getTkActor().getGraphNodeCount() > 1)
+ {
+ // update neighbors
+ {
+ const uint32_t graphNodeCount = actor.getTkActor().getGraphNodeCount();
+ uint32_t* graphNodeIndices = getScratchArray<uint32_t>(graphNodeCount);
+ actor.getTkActor().getGraphNodeIndices(graphNodeIndices, graphNodeCount);
+ for (uint32_t i = 0; i < graphNodeCount; ++i)
+ {
+ m_graphProcessor->setNodeNeighborsCount(graphNodeIndices[i], graphNodeCount);
+ }
+ }
+
+ m_actors.insert(&actor);
+ m_isDirty = true;
+ }
+}
+
+void ExtImpulseStressSolver::onActorDestroyed(ExtPxFamily& /*family*/, ExtPxActor& actor)
+{
+ if (m_actors.erase(&actor))
+ {
+ m_isDirty = true;
+ }
+}
+
+void ExtImpulseStressSolver::syncSolver()
+{
+ // traverse graph and remove dead bonds
+ for (uint32_t node0 = 0; node0 < m_graph.nodeCount; ++node0)
+ {
+ for (uint32_t adjacencyIndex = m_graph.adjacencyPartition[node0]; adjacencyIndex < m_graph.adjacencyPartition[node0 + 1]; adjacencyIndex++)
+ {
+ uint32_t node1 = m_graph.adjacentNodeIndices[adjacencyIndex];
+ if (node0 < node1)
+ {
+ uint32_t bondIndex = m_graph.adjacentBondIndices[adjacencyIndex];
+
+ if (m_bondHealths[bondIndex] <= 0.0f)
+ {
+ m_graphProcessor->removeBondIfExists(bondIndex);
+ }
+ }
+ }
+ }
+
+ m_isDirty = false;
+}
+
+
+void ExtImpulseStressSolver::initialize()
+{
+ if (m_reset)
+ {
+ m_framesCount = 0;
+ }
+
+ if (m_isDirty)
+ {
+ syncSolver();
+ }
+
+ if (m_settings.graphReductionLevel != m_graphProcessor->getGraphReductionLevel())
+ {
+ m_graphProcessor->setGraphReductionLevel(m_settings.graphReductionLevel);
+ }
+
+ m_graphProcessor->initialize();
+
+ for (auto it = m_actors.getIterator(); !it.done(); ++it)
+ {
+ const ExtPxActor* actor = *it;
+ const uint32_t graphNodeCount = actor->getTkActor().getGraphNodeCount();
+ uint32_t* graphNodeIndices = getScratchArray<uint32_t>(graphNodeCount);
+ actor->getTkActor().getGraphNodeIndices(graphNodeIndices, graphNodeCount);
+
+ PxRigidDynamic& rigidDynamic = actor->getPhysXActor();
+ const bool isStatic = rigidDynamic.getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC;
+ if (isStatic)
+ {
+ PxVec3 gravity = rigidDynamic.getScene()->getGravity();
+ gravity = rigidDynamic.getGlobalPose().rotateInv(gravity);
+
+ for (uint32_t i = 0; i < graphNodeCount; ++i)
+ {
+ const uint32_t node = graphNodeIndices[i];
+ m_graphProcessor->addNodeVelocity(node, gravity);
+ }
+ }
+ else
+ {
+ PxVec3 cMassPose = rigidDynamic.getCMassLocalPose().p;
+
+ PxVec3 angularVelocity = rigidDynamic.getGlobalPose().rotateInv(rigidDynamic.getAngularVelocity());
+ //PxVec3 linearVelocity = rigidDynamic.getGlobalPose().rotateInv(rigidDynamic.getLinearVelocity());
+
+ // Apply centrifugal force
+ for (uint32_t i = 0; i < graphNodeCount; ++i)
+ {
+ const uint32_t node = graphNodeIndices[i];
+ const auto& localPos = m_graphProcessor->getNodeData(node).localPos;
+ // a = w x (w x r)
+ const PxVec3 centrifugalAcceleration = angularVelocity.cross(angularVelocity.cross(localPos - cMassPose));
+ m_graphProcessor->addNodeVelocity(node, centrifugalAcceleration);
+ }
+ }
+
+ const auto entry = m_impulseBuffer.find(actor);
+ if (entry)
+ {
+ for (const ImpulseData& data : entry->second)
+ {
+ float bestDist = FLT_MAX;
+ uint32_t bestNode = invalidIndex<uint32_t>();
+
+ for (uint32_t i = 0; i < graphNodeCount; ++i)
+ {
+ const uint32_t node = graphNodeIndices[i];
+ const float sqrDist = (data.position - m_graphProcessor->getNodeData(node).localPos).magnitudeSquared();
+ if (sqrDist < bestDist)
+ {
+ bestDist = sqrDist;
+ bestNode = node;
+ }
+ }
+
+ if (!isInvalidIndex(bestNode))
+ {
+ m_graphProcessor->addNodeImpulse(bestNode, data.impulse);
+ }
+ }
+ m_impulseBuffer[actor].clear();
+ }
+ }
+}
+
+void ExtImpulseStressSolver::applyImpulse(ExtPxActor& actor, physx::PxVec3 position, physx::PxVec3 force)
+{
+ ImpulseData data = { position, force };
+
+ m_impulseBuffer[&actor].pushBack(data);
+}
+
+uint32_t ExtImpulseStressSolver::getBondCount() const
+{
+ return m_graphProcessor->getSolverBondCount();
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Update
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void ExtImpulseStressSolver::update(bool doDamage)
+{
+ initialize();
+
+ solve();
+
+ if (doDamage)
+ {
+ applyDamage();
+ }
+
+ m_framesCount++;
+}
+
+void ExtImpulseStressSolver::solve()
+{
+ PX_SIMD_GUARD;
+
+ const uint32_t iterations = getIterationsPerFrame();
+ m_graphProcessor->solve(iterations, WARM_START && !m_reset);
+ m_reset = false;
+
+ m_graphProcessor->calcError(m_errorLinear, m_errorAngular);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Damage
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void ExtImpulseStressSolver::applyDamage()
+{
+ m_bondFractureBuffer.clear();
+ m_graphProcessor->generateFracture(m_bondFractureBuffer, m_settings, m_bondHealths);
+
+ if (m_bondFractureBuffer.size() > 0)
+ {
+ NvBlastFractureBuffers fractureCommands;
+ fractureCommands.chunkFractureCount = 0;
+ fractureCommands.bondFractureCount = m_bondFractureBuffer.size();
+ fractureCommands.bondFractures = m_bondFractureBuffer.begin();
+
+ m_family.getTkFamily().applyFracture(&fractureCommands);
+ }
+}
+
+uint32_t ExtImpulseStressSolver::getIterationCount() const
+{
+ return getFrameCount() * getIterationsPerFrame();
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Debug Render
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static PxU32 PxVec4ToU32Color(const PxVec4& color)
+{
+ PxU32 c = 0;
+ c |= (int)(color.w * 255); c <<= 8;
+ c |= (int)(color.z * 255); c <<= 8;
+ c |= (int)(color.y * 255); c <<= 8;
+ c |= (int)(color.x * 255);
+ return c;
+}
+
+static PxVec4 PxVec4Lerp(const PxVec4 v0, const PxVec4 v1, float val)
+{
+ PxVec4 v(
+ v0.x * (1 - val) + v1.x * val,
+ v0.y * (1 - val) + v1.y * val,
+ v0.z * (1 - val) + v1.z * val,
+ v0.w * (1 - val) + v1.w * val
+ );
+ return v;
+}
+
+inline float clamp01(float v)
+{
+ return v < 0.0f ? 0.0f : (v > 1.0f ? 1.0f : v);
+}
+
+inline PxVec4 bondHealthColor(float healthFraction)
+{
+ healthFraction = clamp01(healthFraction);
+
+ const PxVec4 BOND_HEALTHY_COLOR(0.0f, 1.0f, 1.0f, 1.0f);
+ const PxVec4 BOND_MID_COLOR(1.0f, 1.0f, 0.0f, 1.0f);
+ const PxVec4 BOND_BROKEN_COLOR(1.0f, 0.0f, 0.0f, 1.0f);
+
+ return healthFraction < 0.5 ? PxVec4Lerp(BOND_BROKEN_COLOR, BOND_MID_COLOR, 2.0f * healthFraction) : PxVec4Lerp(BOND_MID_COLOR, BOND_HEALTHY_COLOR, 2.0f * healthFraction - 1.0f);
+}
+
+void ExtImpulseStressSolver::fillDebugRender(const std::vector<uint32_t>& nodes, std::vector<PxDebugLine>& lines, DebugRenderMode mode, float scale)
+{
+ const PxVec4 BOND_IMPULSE_LINEAR_COLOR(0.0f, 1.0f, 0.0f, 1.0f);
+ const PxVec4 BOND_IMPULSE_ANGULAR_COLOR(1.0f, 0.0f, 0.0f, 1.0f);
+
+ if (m_isDirty)
+ return;
+
+ ExtArray<uint8_t>::type& nodesSet = m_scratch;
+
+ nodesSet.resize(m_graphProcessor->getSolverNodeCount());
+ memset(nodesSet.begin(), 0, nodesSet.size() * sizeof(uint8_t));
+ for (auto& nodeIndex : nodes)
+ {
+ nodesSet[m_graphProcessor->getNodeData(nodeIndex).solverNode] = 1;
+ }
+
+ const uint32_t bondCount = m_graphProcessor->getSolverBondCount();
+ for (uint32_t i = 0; i < bondCount; ++i)
+ {
+ const auto& solverInternalBondData = m_graphProcessor->getSolverInternalBondData(i);
+ if (nodesSet[solverInternalBondData.node0] != 0)
+ {
+ NVBLAST_ASSERT(nodesSet[solverInternalBondData.node1] != 0);
+
+ const auto& solverInternalNode0 = m_graphProcessor->getSolverInternalNodeData(solverInternalBondData.node0);
+ const auto& solverInternalNode1 = m_graphProcessor->getSolverInternalNodeData(solverInternalBondData.node1);
+ const auto& solverNode0 = m_graphProcessor->getSolverNodeData(solverInternalBondData.node0);
+ const auto& solverNode1 = m_graphProcessor->getSolverNodeData(solverInternalBondData.node1);
+
+ PxVec3 p0 = solverNode0.localPos;
+ PxVec3 p1 = solverNode1.localPos;
+ PxVec3 center = (p0 + p1) * 0.5f;
+
+ const float stress = std::min<float>(solverInternalBondData.getStressHealth(m_settings), 1.0f);
+ PxVec4 color = bondHealthColor(1.0f - stress);
+
+ lines.push_back(PxDebugLine(p0, p1, PxVec4ToU32Color(color)));
+
+ float impulseScale = scale;
+
+ if (mode == DebugRenderMode::STRESS_GRAPH_NODES_IMPULSES)
+ {
+ lines.push_back(PxDebugLine(p0, p0 + solverInternalNode0.velocityLinear * impulseScale, PxVec4ToU32Color(BOND_IMPULSE_LINEAR_COLOR)));
+ lines.push_back(PxDebugLine(p0, p0 + solverInternalNode0.velocityAngular * impulseScale, PxVec4ToU32Color(BOND_IMPULSE_ANGULAR_COLOR)));
+ lines.push_back(PxDebugLine(p1, p1 + solverInternalNode1.velocityLinear * impulseScale, PxVec4ToU32Color(BOND_IMPULSE_LINEAR_COLOR)));
+ lines.push_back(PxDebugLine(p1, p1 + solverInternalNode1.velocityAngular * impulseScale, PxVec4ToU32Color(BOND_IMPULSE_ANGULAR_COLOR)));
+ }
+ else if (mode == DebugRenderMode::STRESS_GRAPH_BONDS_IMPULSES)
+ {
+ lines.push_back(PxDebugLine(center, center + solverInternalBondData.impulseLinear * impulseScale, PxVec4ToU32Color(BOND_IMPULSE_LINEAR_COLOR)));
+ lines.push_back(PxDebugLine(center, center + solverInternalBondData.impulseAngular * impulseScale, PxVec4ToU32Color(BOND_IMPULSE_ANGULAR_COLOR)));
+ }
+ }
+ }
+}
+
+
+} // namespace Blast
+} // namespace Nv
diff --git a/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtImpulseStressSolver.h b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtImpulseStressSolver.h
new file mode 100644
index 0000000..d274789
--- /dev/null
+++ b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtImpulseStressSolver.h
@@ -0,0 +1,164 @@
+/*
+* Copyright (c) 2016-2017, 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.
+*/
+
+#ifndef NVBLASTEXTIMPULSESTRESSSOLVER_H
+#define NVBLASTEXTIMPULSESTRESSSOLVER_H
+
+#include "NvBlastExtStressSolver.h"
+#include "NvBlastExtPxManager.h"
+#include "NvBlastExtPxListener.h"
+#include "NvBlastTypes.h"
+#include <NvBlastExtArray.h>
+#include <NvBlastExtHashSet.h>
+#include <NvBlastExtHashMap.h>
+
+namespace Nv
+{
+namespace Blast
+{
+
+
+struct ExtStressNodeCachedData
+{
+ physx::PxVec3 localPos;
+ bool isStatic;
+};
+
+
+struct ExtStressBondCachedData
+{
+ uint32_t bondIndex;
+};
+
+class SupportGraphProcessor;
+
+/**
+*/
+class ExtImpulseStressSolver : public ExtStressSolver, ExtPxListener
+{
+ NV_NOCOPY(ExtImpulseStressSolver)
+
+public:
+ ExtImpulseStressSolver(ExtPxFamily& family, ExtStressSolverSettings settings);
+ virtual void release() override;
+
+
+ //////// ExtStressSolver interface ////////
+
+ virtual void setSettings(const ExtStressSolverSettings& settings) override
+ {
+ m_settings = settings;
+ }
+
+ virtual const ExtStressSolverSettings& getSettings() const override
+ {
+ return m_settings;
+ }
+
+ virtual void applyImpulse(ExtPxActor& actor, physx::PxVec3 position, physx::PxVec3 force) override;
+
+ virtual void update(bool doDamage) override;
+
+ void reset() override
+ {
+ m_reset = true;
+ }
+
+ virtual float getStressErrorLinear() const override
+ {
+ return m_errorLinear;
+ }
+
+ virtual float getStressErrorAngular() const override
+ {
+ return m_errorAngular;
+ }
+
+ virtual uint32_t getIterationCount() const override;
+
+ virtual uint32_t getFrameCount() const override
+ {
+ return m_framesCount;
+ }
+
+ virtual uint32_t getBondCount() const override;
+
+ virtual void fillDebugRender(const std::vector<uint32_t>& nodes, std::vector<physx::PxDebugLine>& lines, DebugRenderMode mode, float scale) override;
+
+
+ //////// ExtPxListener interface ////////
+
+ virtual void onActorCreated(ExtPxFamily& family, ExtPxActor& actor) final;
+
+ virtual void onActorDestroyed(ExtPxFamily& family, ExtPxActor& actor) final;
+
+
+private:
+ ~ExtImpulseStressSolver();
+
+
+ //////// private methods ////////
+
+ void solve();
+
+ void applyDamage();
+
+ void initialize();
+
+ NV_INLINE void iterate();
+
+ void syncSolver();
+
+ template<class T>
+ NV_INLINE T* getScratchArray(uint32_t size);
+
+
+ //////// data ////////
+
+ struct ImpulseData
+ {
+ physx::PxVec3 position;
+ physx::PxVec3 impulse;
+ };
+
+ ExtPxFamily& m_family;
+ ExtHashSet<ExtPxActor*>::type m_actors;
+ ExtStressSolverSettings m_settings;
+ NvBlastSupportGraph m_graph;
+ bool m_isDirty;
+ bool m_reset;
+ const float* m_bondHealths;
+ SupportGraphProcessor* m_graphProcessor;
+ float m_errorAngular;
+ float m_errorLinear;
+ uint32_t m_framesCount;
+ ExtArray<NvBlastBondFractureData>::type m_bondFractureBuffer;
+ ExtHashMap<const ExtPxActor*, ExtArray<ImpulseData>::type>::type m_impulseBuffer;
+ ExtArray<uint8_t>::type m_scratch;
+};
+
+
+template<class T>
+NV_INLINE T* ExtImpulseStressSolver::getScratchArray(uint32_t size)
+{
+ const uint32_t scratchSize = sizeof(T) * size;
+ if (m_scratch.size() < scratchSize)
+ {
+ m_scratch.resize(scratchSize);
+ }
+ return reinterpret_cast<T*>(m_scratch.begin());
+}
+
+
+} // namespace Blast
+} // namespace Nv
+
+
+#endif // ifndef NVBLASTEXTIMPULSESTRESSSOLVER_H
diff --git a/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxActorImpl.cpp b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxActorImpl.cpp
new file mode 100644
index 0000000..7732d18
--- /dev/null
+++ b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxActorImpl.cpp
@@ -0,0 +1,180 @@
+/*
+* Copyright (c) 2016-2017, 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 "NvBlastExtPxActorImpl.h"
+#include "NvBlastExtPxAsset.h"
+#include "NvBlastExtPxManagerImpl.h"
+#include "NvBlastExtPxFamilyImpl.h"
+
+#include "PxRigidDynamic.h"
+#include "PxPhysics.h"
+
+#include "NvBlastTkActor.h"
+#include "NvBlastTkAsset.h"
+
+#include "PxRigidBodyExt.h"
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+
+ExtPxActorImpl::ExtPxActorImpl(ExtPxFamilyImpl* family, TkActor* tkActor, const PxActorCreateInfo& pxActorInfo)
+ : m_family(family), m_tkActor(tkActor)
+{
+ const ExtPxChunk* pxChunks = m_family->m_pxAsset.getChunks();
+ const ExtPxSubchunk* pxSubchunks = m_family->m_pxAsset.getSubchunks();
+ const NvBlastChunk* chunks = m_tkActor->getAsset()->getChunks();
+ uint32_t nodeCount = m_tkActor->getGraphNodeCount();
+
+ PxFilterData simulationFilterData; // Default constructor = {0,0,0,0}
+
+ // get visible chunk indices list
+ {
+ auto& chunkIndices = m_family->m_indicesScratch;
+ chunkIndices.resize(m_tkActor->getVisibleChunkCount());
+ m_tkActor->getVisibleChunkIndices(chunkIndices.begin(), static_cast<uint32_t>(chunkIndices.size()));
+
+ // fill visible chunk indices list with mapped to our asset indices
+ m_chunkIndices.reserve(chunkIndices.size());
+ for (const uint32_t chunkIndex : chunkIndices)
+ {
+ const ExtPxChunk& chunk = pxChunks[chunkIndex];
+ if (chunk.subchunkCount == 0)
+ continue;
+ m_chunkIndices.pushBack(chunkIndex);
+ }
+
+ // Single lower-support chunk actors might be leaf actors, check for this and disable contact callbacks if so
+ if (nodeCount <= 1)
+ {
+ PX_ASSERT(chunkIndices.size() == 1);
+ if (chunkIndices.size() > 0)
+ {
+ const NvBlastChunk& chunk = chunks[chunkIndices[0]];
+ if (chunk.firstChildIndex == chunk.childIndexStop)
+ {
+ simulationFilterData.word3 = ExtPxManager::LEAF_CHUNK; // mark as leaf chunk if chunk has no children
+ }
+ }
+ }
+ }
+
+ // create rigidDynamic and setup
+ PxPhysics& physics = m_family->m_manager.m_physics;
+ m_rigidDynamic = physics.createRigidDynamic(pxActorInfo.m_transform);
+ if (m_family->m_pxActorDescTemplate != nullptr)
+ {
+ m_rigidDynamic->setActorFlags(static_cast<physx::PxActorFlags>(m_family->m_pxActorDescTemplate->flags));
+ }
+
+ // fill rigidDynamic with shapes
+ PxMaterial* material = m_family->m_spawnSettings.material;
+ for (uint32_t i = 0; i < m_chunkIndices.size(); ++i)
+ {
+ uint32_t chunkID = m_chunkIndices[i];
+ const ExtPxChunk& chunk = pxChunks[chunkID];
+ for (uint32_t c = 0; c < chunk.subchunkCount; c++)
+ {
+ const uint32_t subchunkIndex = chunk.firstSubchunkIndex + c;
+ auto& subchunk = pxSubchunks[subchunkIndex];
+ PxShape* shape = physics.createShape(subchunk.geometry, *material);
+ shape->setLocalPose(subchunk.transform);
+
+ const ExtPxShapeDescTemplate* pxShapeDesc = m_family->m_pxShapeDescTemplate;
+ if (pxShapeDesc != nullptr)
+ {
+ shape->setFlags(static_cast<PxShapeFlags>(pxShapeDesc->flags));
+ shape->setSimulationFilterData(pxShapeDesc->simulationFilterData);
+ shape->setQueryFilterData(pxShapeDesc->queryFilterData);
+ shape->setContactOffset(pxShapeDesc->contactOffset);
+ shape->setRestOffset(pxShapeDesc->restOffset);
+ }
+ else
+ {
+ shape->setSimulationFilterData(simulationFilterData);
+ }
+
+ m_rigidDynamic->attachShape(*shape);
+
+ PX_ASSERT_WITH_MESSAGE(m_family->m_subchunkShapes[subchunkIndex] == nullptr, "Chunk has some shapes(live).");
+ m_family->m_subchunkShapes[subchunkIndex] = shape;
+ }
+ }
+
+ // search for static chunk in actor's graph (make actor static if it contains static chunk)
+ bool staticFound = false;
+ if (nodeCount > 0)
+ {
+ auto& graphChunkIndices = m_family->m_indicesScratch;
+ graphChunkIndices.resize(nodeCount);
+ m_tkActor->getGraphNodeIndices(graphChunkIndices.begin(), static_cast<uint32_t>(graphChunkIndices.size()));
+ const NvBlastSupportGraph graph = m_tkActor->getAsset()->getGraph();
+
+ for (uint32_t i = 0; !staticFound && i < graphChunkIndices.size(); ++i)
+ {
+ uint32_t chunkIndex = graph.chunkIndices[graphChunkIndices[i]];
+ const ExtPxChunk& chunk = pxChunks[chunkIndex];
+ staticFound = chunk.isStatic;
+ }
+ }
+ m_rigidDynamic->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, staticFound);
+
+ // store pointer to actor in px userData
+ m_family->m_manager.registerActor(m_rigidDynamic, this);
+
+ // store pointer to actor in blast userData
+ m_tkActor->userData = this;
+
+ // update mass properties
+ PxRigidBodyExt::updateMassAndInertia(*m_rigidDynamic, m_family->m_spawnSettings.density);
+
+ // set initial velocities
+ if (!(m_rigidDynamic->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC))
+ {
+ m_rigidDynamic->setLinearVelocity(pxActorInfo.m_linearVelocity);
+ m_rigidDynamic->setAngularVelocity(pxActorInfo.m_angularVelocity);
+ }
+}
+
+void ExtPxActorImpl::release()
+{
+ if (m_rigidDynamic != nullptr)
+ {
+ m_family->m_manager.unregisterActor(m_rigidDynamic);
+ m_rigidDynamic->release();
+ m_rigidDynamic = nullptr;
+ }
+
+ const ExtPxChunk* pxChunks = m_family->m_pxAsset.getChunks();
+ for (uint32_t chunkID : m_chunkIndices)
+ {
+ const ExtPxChunk& chunk = pxChunks[chunkID];
+ for (uint32_t c = 0; c < chunk.subchunkCount; c++)
+ {
+ const uint32_t subchunkIndex = chunk.firstSubchunkIndex + c;
+ m_family->m_subchunkShapes[subchunkIndex] = nullptr;
+ }
+ }
+ m_chunkIndices.clear();
+
+ m_tkActor->userData = nullptr;
+}
+
+ExtPxFamily& ExtPxActorImpl::getFamily() const
+{
+ return *m_family;
+}
+
+
+} // namespace Blast
+} // namespace Nv
diff --git a/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxActorImpl.h b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxActorImpl.h
new file mode 100644
index 0000000..a592293
--- /dev/null
+++ b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxActorImpl.h
@@ -0,0 +1,94 @@
+/*
+* Copyright (c) 2016-2017, 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.
+*/
+
+#ifndef NVBLASTEXTPXACTORIMPL_H
+#define NVBLASTEXTPXACTORIMPL_H
+
+#include "NvBlastExtPxActor.h"
+#include "NvBlastExtArray.h"
+#include "PxTransform.h"
+
+
+using namespace physx;
+
+namespace Nv
+{
+namespace Blast
+{
+
+
+// Forward declarations
+class ExtPxFamilyImpl;
+
+struct PxActorCreateInfo
+{
+ PxTransform m_transform;
+ PxVec3 m_scale;
+ PxVec3 m_linearVelocity;
+ PxVec3 m_angularVelocity;
+};
+
+
+class ExtPxActorImpl final : public ExtPxActor
+{
+public:
+ //////// ctor ////////
+
+ ExtPxActorImpl(ExtPxFamilyImpl* family, TkActor* tkActor, const PxActorCreateInfo& pxActorInfo);
+
+ ~ExtPxActorImpl()
+ {
+ release();
+ }
+
+ void release();
+
+
+ //////// interface ////////
+
+ virtual uint32_t getChunkCount() const override
+ {
+ return static_cast<uint32_t>(m_chunkIndices.size());
+ }
+
+ virtual const uint32_t* getChunkIndices() const override
+ {
+ return m_chunkIndices.begin();
+ }
+
+ virtual PxRigidDynamic& getPhysXActor() const override
+ {
+ return *m_rigidDynamic;
+ }
+
+ virtual TkActor& getTkActor() const override
+ {
+ return *m_tkActor;
+ }
+
+ virtual ExtPxFamily& getFamily() const override;
+
+
+private:
+ //////// data ////////
+
+ ExtPxFamilyImpl* m_family;
+ TkActor* m_tkActor;
+ PxRigidDynamic* m_rigidDynamic;
+ ExtInlineArray<uint32_t, 4>::type m_chunkIndices;
+};
+
+
+
+} // namespace Blast
+} // namespace Nv
+
+
+#endif // ifndef NVBLASTEXTPXACTORIMPL_H
diff --git a/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxAssetImpl.cpp b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxAssetImpl.cpp
new file mode 100644
index 0000000..a0f75fc
--- /dev/null
+++ b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxAssetImpl.cpp
@@ -0,0 +1,315 @@
+/*
+* Copyright (c) 2016-2017, 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 "NvBlastExtPxAssetImpl.h"
+#include "NvBlastExtHashMap.h"
+
+#include "NvBlastAssert.h"
+#include "NvBlastIndexFns.h"
+
+#include "NvBlastTkAsset.h"
+
+#include "PxIO.h"
+#include "PxPhysics.h"
+#include "PxFileBuf.h"
+#include "cooking/PxCooking.h"
+
+#include <algorithm>
+
+namespace Nv
+{
+namespace Blast
+{
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Helpers/Wrappers
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+class FileBufToPxInputStream final : public PxInputStream
+{
+public:
+ FileBufToPxInputStream(PxFileBuf& filebuf) : m_filebuf(filebuf) {}
+
+ virtual uint32_t read(void* dest, uint32_t count)
+ {
+ return m_filebuf.read(dest, count);
+ }
+
+private:
+ FileBufToPxInputStream& operator=(const FileBufToPxInputStream&);
+
+ PxFileBuf& m_filebuf;
+};
+
+
+class FileBufToPxOutputStream final : public PxOutputStream
+{
+public:
+ FileBufToPxOutputStream(PxFileBuf& filebuf) : m_filebuf(filebuf) {}
+
+ virtual uint32_t write(const void* src, uint32_t count) override
+ {
+ return m_filebuf.write(src, count);
+ }
+
+private:
+ FileBufToPxOutputStream& operator=(const FileBufToPxOutputStream&);
+
+ PxFileBuf& m_filebuf;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// ExtPxAssetImpl Implementation
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ExtPxAssetImpl::ExtPxAssetImpl(const ExtPxAssetDesc& desc, TkFramework& framework)
+{
+ m_tkAsset = framework.createAsset(desc);
+
+ // count subchunks and reserve memory
+ uint32_t subchunkCount = 0;
+ for (uint32_t i = 0; i < desc.chunkCount; ++i)
+ {
+ const auto& chunk = desc.pxChunks[i];
+ subchunkCount += static_cast<uint32_t>(chunk.subchunkCount);
+ }
+ m_subchunks.reserve(subchunkCount);
+
+ // fill chunks and subchunks
+ m_chunks.resize(desc.chunkCount);
+ for (uint32_t i = 0; i < desc.chunkCount; ++i)
+ {
+ const auto& chunk = desc.pxChunks[i];
+ m_chunks[i].isStatic = chunk.isStatic;
+ m_chunks[i].firstSubchunkIndex = m_subchunks.size();
+ m_chunks[i].subchunkCount = chunk.subchunkCount;
+ for (uint32_t k = 0; k < chunk.subchunkCount; ++k)
+ {
+ ExtPxSubchunk subchunk =
+ {
+ chunk.subchunks[k].transform,
+ chunk.subchunks[k].geometry
+ };
+ m_subchunks.pushBack(subchunk);
+ }
+ }
+}
+
+ExtPxAssetImpl::ExtPxAssetImpl(TkAsset* tkAsset):
+ m_tkAsset(tkAsset)
+{
+
+}
+
+ExtPxAssetImpl::~ExtPxAssetImpl()
+{
+ if (m_tkAsset)
+ {
+ m_tkAsset->release();
+ }
+}
+
+void ExtPxAssetImpl::release()
+{
+ NVBLASTEXT_DELETE(this, ExtPxAssetImpl);
+}
+
+NV_INLINE bool serializeConvexMesh(const PxConvexMesh& convexMesh, PxCooking& cooking, ExtArray<uint32_t>::type& indicesScratch,
+ ExtArray<PxHullPolygon>::type hullPolygonsScratch, PxOutputStream& stream)
+{
+ PxConvexMeshDesc desc;
+ desc.points.data = convexMesh.getVertices();
+ desc.points.count = convexMesh.getNbVertices();
+ desc.points.stride = sizeof(PxVec3);
+
+ hullPolygonsScratch.resize(convexMesh.getNbPolygons());
+
+ uint32_t indexCount = 0;
+ for (uint32_t i = 0; i < convexMesh.getNbPolygons(); i++)
+ {
+ PxHullPolygon polygon;
+ convexMesh.getPolygonData(i, polygon);
+ if (polygon.mNbVerts)
+ {
+ indexCount = std::max<uint32_t>(indexCount, polygon.mIndexBase + polygon.mNbVerts);
+ }
+ }
+ indicesScratch.resize(indexCount);
+
+ for (uint32_t i = 0; i < convexMesh.getNbPolygons(); i++)
+ {
+ PxHullPolygon polygon;
+ convexMesh.getPolygonData(i, polygon);
+ for (uint32_t j = 0; j < polygon.mNbVerts; j++)
+ {
+ indicesScratch[polygon.mIndexBase + j] = convexMesh.getIndexBuffer()[polygon.mIndexBase + j];
+ }
+
+ hullPolygonsScratch[i] = polygon;
+ }
+
+ desc.indices.count = indexCount;
+ desc.indices.data = indicesScratch.begin();
+ desc.indices.stride = sizeof(uint32_t);
+
+ desc.polygons.count = convexMesh.getNbPolygons();
+ desc.polygons.data = hullPolygonsScratch.begin();
+ desc.polygons.stride = sizeof(PxHullPolygon);
+
+ return cooking.cookConvexMesh(desc, stream);
+}
+
+bool ExtPxAssetImpl::serialize(PxFileBuf& stream, PxCooking& cooking) const
+{
+ // Header data
+ stream.storeDword(ClassID);
+ stream.storeDword(Version::Current);
+
+ m_tkAsset->serialize(stream);
+
+ // Chunks
+ const uint32_t chunkCount = m_tkAsset->getChunkCount();
+ for (uint32_t i = 0; i < chunkCount; ++i)
+ {
+ const ExtPxChunk& chunk = m_chunks[i];
+ stream.storeDword(chunk.firstSubchunkIndex);
+ stream.storeDword(chunk.subchunkCount);
+ stream.storeDword(chunk.isStatic ? 1 : 0);
+ }
+
+ stream.storeDword(m_subchunks.size());
+
+ ExtArray<uint32_t>::type indicesScratch(512);
+ ExtArray<PxHullPolygon>::type hullPolygonsScratch(512);
+ ExtHashMap<PxConvexMesh*, uint32_t>::type convexReuseMap;
+
+ FileBufToPxOutputStream outputStream(stream);
+ for (uint32_t i = 0; i < m_subchunks.size(); ++i)
+ {
+ auto& subchunk = m_subchunks[i];
+
+ // Subchunk transform
+ stream.storeFloat(subchunk.transform.q.x); stream.storeFloat(subchunk.transform.q.y); stream.storeFloat(subchunk.transform.q.z); stream.storeFloat(subchunk.transform.q.w);
+ stream.storeFloat(subchunk.transform.p.x); stream.storeFloat(subchunk.transform.p.y); stream.storeFloat(subchunk.transform.p.z);
+
+ // Subchunk scale
+ stream.storeFloat(subchunk.geometry.scale.scale.x); stream.storeFloat(subchunk.geometry.scale.scale.y); stream.storeFloat(subchunk.geometry.scale.scale.z);
+ stream.storeFloat(subchunk.geometry.scale.rotation.x); stream.storeFloat(subchunk.geometry.scale.rotation.y); stream.storeFloat(subchunk.geometry.scale.rotation.z); stream.storeFloat(subchunk.geometry.scale.rotation.w);
+
+ auto convexMesh = subchunk.geometry.convexMesh;
+ NVBLASTEXT_CHECK_ERROR(convexMesh != nullptr, "ExtPxAssetImpl::serialize: subchunk convexMesh is nullptr.", return false);
+
+ auto entry = convexReuseMap.find(convexMesh);
+ if (entry)
+ {
+ stream.storeDword(entry->second);
+ }
+ else
+ {
+ stream.storeDword(invalidIndex<uint32_t>());
+ if (!serializeConvexMesh(*convexMesh, cooking, indicesScratch, hullPolygonsScratch, outputStream))
+ {
+ NVBLASTEXT_LOG_ERROR("ExtPxAssetImpl::serialize: subchunk convexMesh cooking/serialization failed.");
+ return false;
+ }
+ convexReuseMap[convexMesh] = i;
+ }
+ }
+
+ return true;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// ExtPxAsset Static
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ExtPxAsset* ExtPxAsset::create(const ExtPxAssetDesc& desc, TkFramework& framework)
+{
+ ExtPxAssetImpl* asset = NVBLASTEXT_NEW(ExtPxAssetImpl)(desc, framework);
+ return asset;
+}
+
+
+Nv::Blast::ExtPxAsset* ExtPxAsset::create(TkAsset* tkAsset)
+{
+ ExtPxAssetImpl* asset = NVBLASTEXT_NEW(ExtPxAssetImpl)(tkAsset);
+
+ // Don't populate the chunks or subchunks!
+
+ return asset;
+}
+
+ExtPxAsset* ExtPxAsset::deserialize(PxFileBuf& stream, TkFramework& framework, PxPhysics& physics)
+{
+ ExtPxAssetImpl::DataHeader header;
+ header.dataType = stream.readDword();
+ header.version = stream.readDword();
+ NVBLASTEXT_CHECK_ERROR(header.dataType == ExtPxAssetImpl::ClassID, "ExtPxAsset::deserialize: wrong data type in filebuf stream.", return nullptr);
+ NVBLASTEXT_CHECK_ERROR(header.version == ExtPxAssetImpl::Version::Current, "ExtPxAsset::deserialize: wrong data version in filebuf stream.", return nullptr);
+
+ TkAsset* tkAsset = static_cast<TkAsset*>(framework.deserialize(stream));
+ NVBLASTEXT_CHECK_ERROR(tkAsset != nullptr, "ExtPxAsset::deserialize: failed to deserialize TkAsset.", return nullptr);
+
+ ExtPxAssetImpl* asset = NVBLASTEXT_NEW(ExtPxAssetImpl)(tkAsset);
+
+ asset->m_chunks.resize(asset->m_tkAsset->getChunkCount());
+
+ const uint32_t chunkCount = asset->m_chunks.size();
+ for (uint32_t i = 0; i < chunkCount; ++i)
+ {
+ ExtPxChunk& chunk = asset->m_chunks[i];
+ chunk.firstSubchunkIndex = stream.readDword();
+ chunk.subchunkCount = stream.readDword();
+ chunk.isStatic = 0 != stream.readDword();
+ }
+
+ const uint32_t subchunkCount = stream.readDword();
+ asset->m_subchunks.resize(subchunkCount);
+
+ FileBufToPxInputStream inputStream(stream);
+ for (uint32_t i = 0; i < asset->m_subchunks.size(); ++i)
+ {
+ ExtPxSubchunk& subChunk = asset->m_subchunks[i];
+
+ // Subchunk transform
+ subChunk.transform.q.x = stream.readFloat(); subChunk.transform.q.y = stream.readFloat(); subChunk.transform.q.z = stream.readFloat(); subChunk.transform.q.w = stream.readFloat();
+ subChunk.transform.p.x = stream.readFloat(); subChunk.transform.p.y = stream.readFloat(); subChunk.transform.p.z = stream.readFloat();
+
+ // Subchunk scale
+ subChunk.geometry.scale.scale.x = stream.readFloat(); subChunk.geometry.scale.scale.y = stream.readFloat(); subChunk.geometry.scale.scale.z = stream.readFloat();
+ subChunk.geometry.scale.rotation.x = stream.readFloat(); subChunk.geometry.scale.rotation.y = stream.readFloat(); subChunk.geometry.scale.rotation.z = stream.readFloat(); subChunk.geometry.scale.rotation.w = stream.readFloat();
+
+ const uint32_t convexReuseIndex = stream.readDword();
+ if (isInvalidIndex(convexReuseIndex))
+ {
+ subChunk.geometry.convexMesh = physics.createConvexMesh(inputStream);
+ }
+ else
+ {
+ NVBLAST_ASSERT_WITH_MESSAGE(convexReuseIndex < i, "ExtPxAsset::deserialize: wrong convexReuseIndex.");
+ subChunk.geometry.convexMesh = asset->m_subchunks[convexReuseIndex].geometry.convexMesh;
+ }
+ if (!subChunk.geometry.convexMesh)
+ {
+ NVBLASTEXT_LOG_ERROR("ExtPxAsset::deserialize: failed to deserialize convex mesh.");
+ asset->release();
+ return nullptr;
+ }
+ }
+
+ return asset;
+}
+
+
+} // namespace Blast
+} // namespace Nv
diff --git a/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxAssetImpl.h b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxAssetImpl.h
new file mode 100644
index 0000000..fd95293
--- /dev/null
+++ b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxAssetImpl.h
@@ -0,0 +1,126 @@
+/*
+* Copyright (c) 2016-2017, 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.
+*/
+
+#ifndef NVBLASTEXTPXASSETIMPL_H
+#define NVBLASTEXTPXASSETIMPL_H
+
+#include "NvBlastExtPxAsset.h"
+#include "NvBlastExtArray.h"
+#include "NvBlastExtDefs.h"
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+
+using namespace physx;
+using namespace general_PxIOStream2;
+
+
+class ExtPxAssetImpl final : public ExtPxAsset
+{
+ NV_NOCOPY(ExtPxAssetImpl)
+
+public:
+ friend class ExtPxAsset;
+
+ /**
+ Enum which keeps track of the serialized data format.
+ */
+ enum Version
+ {
+ /** Initial version */
+ Initial,
+
+ // New formats must come before Count. They should be given descriptive names with more information in comments.
+
+ /** The number of serialized formats. */
+ Count,
+
+ /** The current version. This should always be Count-1 */
+ Current = Count - 1
+ };
+
+ //////// ctor ////////
+
+ ExtPxAssetImpl(const ExtPxAssetDesc& desc, TkFramework& framework);
+ ExtPxAssetImpl(TkAsset* tkAsset);
+
+ ~ExtPxAssetImpl();
+
+
+ //////// interface ////////
+
+ virtual void release() override;
+
+ virtual const TkAsset& getTkAsset() const override
+ {
+ return *m_tkAsset;
+ }
+
+ virtual uint32_t getChunkCount() const override
+ {
+ return m_chunks.size();
+ }
+
+ virtual const ExtPxChunk* getChunks() const override
+ {
+ return m_chunks.begin();
+ }
+
+ virtual uint32_t getSubchunkCount() const override
+ {
+ return m_subchunks.size();
+ }
+
+ virtual const ExtPxSubchunk* getSubchunks() const override
+ {
+ return m_subchunks.begin();
+ }
+
+ virtual bool serialize(PxFileBuf& stream, PxCooking& cooking) const override;
+
+
+ /*
+ Get the underlying array for the chunks. Used for serialization.
+ */
+ ExtArray<ExtPxChunk>::type& getChunksArray() { return m_chunks; }
+
+ /*
+ Get the underlying array for the subchunks. Used for serialization.
+ */
+ ExtArray<ExtPxSubchunk>::type& getSubchunksArray() { return m_subchunks; }
+
+private:
+ //////// serialization data ////////
+
+ struct DataHeader
+ {
+ uint32_t dataType;
+ uint32_t version;
+ };
+
+ enum { ClassID = NVBLASTEXT_FOURCC('B', 'P', 'X', 'A') }; // Blast PhysX Asset
+
+
+ //////// data ////////
+
+ TkAsset* m_tkAsset;
+ ExtArray<ExtPxChunk>::type m_chunks;
+ ExtArray<ExtPxSubchunk>::type m_subchunks;
+};
+
+} // namespace Blast
+} // namespace Nv
+
+
+#endif // ifndef NVBLASTEXTPXASSETIMPL_H
diff --git a/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxFamilyImpl.cpp b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxFamilyImpl.cpp
new file mode 100644
index 0000000..b2d3a47
--- /dev/null
+++ b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxFamilyImpl.cpp
@@ -0,0 +1,294 @@
+/*
+* Copyright (c) 2016-2017, 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 "NvBlastExtPxFamilyImpl.h"
+#include "NvBlastExtPxActorImpl.h"
+#include "NvBlastExtPxAssetImpl.h"
+#include "NvBlastExtPxListener.h"
+#include "NvBlastExtPxManagerImpl.h"
+
+#include "NvBlastTkFamily.h"
+#include "NvBlastTkActor.h"
+#include "NvBlastTkJoint.h"
+
+#include "NvBlastAssert.h"
+
+#include "PxRigidDynamic.h"
+#include "PxScene.h"
+
+#include <algorithm>
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+
+ExtPxFamilyImpl::ExtPxFamilyImpl(ExtPxManagerImpl& manager, TkFamily& tkFamily, const ExtPxAsset& pxAsset)
+ : m_manager(manager), m_tkFamily(tkFamily), m_pxAsset(pxAsset), m_pxShapeDescTemplate(nullptr), m_pxActorDescTemplate(nullptr), m_isSpawned(false)
+{
+ m_subchunkShapes.resize(static_cast<uint32_t>(m_pxAsset.getSubchunkCount()));
+
+ userData = nullptr;
+
+ m_manager.registerFamily(*this);
+}
+
+ExtPxFamilyImpl::~ExtPxFamilyImpl()
+{
+ m_manager.unregisterFamily(*this);
+
+ if (m_isSpawned)
+ {
+ m_tkFamily.removeListener(*this);
+
+ auto& actors = m_actorsBuffer;
+ actors.resize(m_actors.size());
+ uint32_t i = 0;
+ for (auto it = m_actors.getIterator(); !it.done(); ++it)
+ {
+ actors[i++] = *it;
+ }
+ destroyActors(actors.begin(), actors.size());
+ }
+
+ m_tkFamily.release();
+}
+
+void ExtPxFamilyImpl::release()
+{
+ NVBLASTEXT_DELETE(this, ExtPxFamilyImpl);
+}
+
+bool ExtPxFamilyImpl::spawn(const physx::PxTransform& pose, const physx::PxVec3& scale, const ExtPxSpawnSettings& settings)
+{
+ NVBLASTEXT_CHECK_ERROR(!m_isSpawned, "Family spawn: family already spawned. Was spawn() called twice?", return false);
+ NVBLASTEXT_CHECK_ERROR(settings.scene != nullptr, "Family creation: desc.scene is nullptr", return false);
+ NVBLASTEXT_CHECK_ERROR(settings.material != nullptr, "Family creation: desc.material is nullptr", return false);
+
+ m_initialTransform = pose;
+ m_spawnSettings = settings;
+
+ // get current tkActors (usually it's only 1, but it can be already in split state)
+ const uint32_t actorCount = (uint32_t)m_tkFamily.getActorCount();
+ m_newActorsBuffer.resize(actorCount);
+ m_tkFamily.getActors(m_newActorsBuffer.begin(), actorCount);
+
+ // calc max split count
+ uint32_t splitMaxActorCount = 0;
+ for (TkActor* actor : m_newActorsBuffer)
+ {
+ splitMaxActorCount = std::max<uint32_t>(splitMaxActorCount, actor->getSplitMaxActorCount());
+ }
+
+ // preallocate memory
+ m_newActorsBuffer.resize(splitMaxActorCount);
+ m_newActorCreateInfo.resize(splitMaxActorCount);
+ m_physXActorsBuffer.resize(splitMaxActorCount);
+ m_physXActorsBuffer.resize(splitMaxActorCount);
+ m_indicesScratch.reserve(splitMaxActorCount);
+
+ // fill initial actor create info
+ for (uint32_t i = 0; i < actorCount; ++i)
+ {
+ PxActorCreateInfo& pxActorInfo = m_newActorCreateInfo[i];
+ pxActorInfo.m_angularVelocity = PxVec3(PxZero);
+ pxActorInfo.m_linearVelocity = PxVec3(PxZero);
+ pxActorInfo.m_transform = pose;
+ pxActorInfo.m_scale = scale;
+ }
+
+ // create first actors in family
+ createActors(m_newActorsBuffer.begin(), m_newActorCreateInfo.begin(), actorCount);
+
+ // listen family for new actors
+ m_tkFamily.addListener(*this);
+
+ m_isSpawned = true;
+
+ return true;
+}
+
+bool ExtPxFamilyImpl::despawn()
+{
+ NVBLASTEXT_CHECK_ERROR(m_spawnSettings.scene != nullptr, "Family despawn: desc.scene is nullptr", return false);
+
+ auto& actors = m_actorsBuffer;
+ actors.resize(m_actors.size());
+ uint32_t i = 0;
+ for (auto it = m_actors.getIterator(); !it.done(); ++it)
+ {
+ actors[i++] = *it;
+ }
+ destroyActors(actors.begin(), actors.size());
+
+ return true;
+}
+
+void ExtPxFamilyImpl::receive(const TkEvent* events, uint32_t eventCount)
+{
+ auto& actorsToDelete = m_actorsBuffer;
+ actorsToDelete.clear();
+ uint32_t totalNewActorsCount = 0;
+
+ for (uint32_t i = 0; i < eventCount; ++i)
+ {
+ const TkEvent& e = events[i];
+ if (e.type == TkEvent::Split)
+ {
+ const TkSplitEvent* splitEvent = e.getPayload<TkSplitEvent>();
+
+ uint32_t newActorsCount = splitEvent->numChildren;
+
+ ExtPxActorImpl* parentActor = nullptr;
+ PxRigidDynamic* parentPxActor = nullptr;
+ if (splitEvent->parentData.userData)
+ {
+ parentActor = reinterpret_cast<ExtPxActorImpl*>(splitEvent->parentData.userData);
+ parentPxActor = &parentActor->getPhysXActor();
+ }
+
+ for (uint32_t j = totalNewActorsCount; j < totalNewActorsCount + newActorsCount; ++j)
+ {
+ m_newActorCreateInfo[j].m_transform = parentPxActor ? parentPxActor->getGlobalPose() : m_initialTransform;
+
+ //TODO: Get the current scale of the actor!
+ m_newActorCreateInfo[j].m_scale = m_initialScale;
+
+ m_newActorCreateInfo[j].m_linearVelocity = parentPxActor ? parentPxActor->getLinearVelocity() : PxVec3(PxZero);
+ m_newActorCreateInfo[j].m_angularVelocity = parentPxActor ? parentPxActor->getAngularVelocity() : PxVec3(PxZero);
+
+ m_newActorsBuffer[j] = splitEvent->children[j - totalNewActorsCount];
+ }
+
+ totalNewActorsCount += newActorsCount;
+
+ if (parentActor)
+ {
+ actorsToDelete.pushBack(parentActor);
+ }
+ }
+ }
+
+ destroyActors(actorsToDelete.begin(), actorsToDelete.size());
+ if (totalNewActorsCount > 0)
+ {
+ uint32_t cappedNewActorsCount = totalNewActorsCount;
+ const uint32_t actorCountLimit = m_manager.getActorCountLimit();
+ const uint32_t totalActorCount = m_manager.getPxActorCount();
+ if (actorCountLimit > 0 && cappedNewActorsCount + totalActorCount > actorCountLimit)
+ {
+ cappedNewActorsCount = actorCountLimit > totalActorCount ? actorCountLimit - totalActorCount : 0;
+ }
+ createActors(m_newActorsBuffer.begin(), m_newActorCreateInfo.begin(), cappedNewActorsCount);
+ m_culledActors.reserve(m_culledActors.size() + totalNewActorsCount - cappedNewActorsCount);
+ for (uint32_t i = cappedNewActorsCount; i < totalNewActorsCount; ++i)
+ {
+ m_culledActors.pushBack(m_newActorsBuffer[i]);
+ }
+ totalNewActorsCount = cappedNewActorsCount; // In case it's used below
+ }
+
+ for (uint32_t i = 0; i < eventCount; ++i)
+ {
+ const TkEvent& e = events[i];
+ if (e.type == TkEvent::JointUpdate)
+ {
+ const TkJointUpdateEvent* jointEvent = e.getPayload<TkJointUpdateEvent>();
+ NVBLAST_ASSERT(jointEvent->joint);
+ TkJoint& joint = *jointEvent->joint;
+
+ switch (jointEvent->subtype)
+ {
+ case TkJointUpdateEvent::External:
+ m_manager.createJoint(joint);
+ break;
+ case TkJointUpdateEvent::Changed:
+ m_manager.updateJoint(joint);
+ break;
+ case TkJointUpdateEvent::Unreferenced:
+ m_manager.destroyJoint(joint);
+ joint.release();
+ break;
+ }
+ }
+ }
+}
+
+void ExtPxFamilyImpl::createActors(TkActor** tkActors, const PxActorCreateInfo* pxActorInfos, uint32_t count)
+{
+ auto actorsToAdd = m_physXActorsBuffer.begin();
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ ExtPxActorImpl* actor = NVBLASTEXT_NEW(ExtPxActorImpl)(this, tkActors[i], pxActorInfos[i]);
+ m_actors.insert(actor);
+ actorsToAdd[i] = &actor->getPhysXActor();
+ dispatchActorCreated(*actor);
+
+ // Handle incomplete joints
+ auto e = m_manager.m_incompleteJointMultiMap.find(tkActors[i]);
+ if (e != nullptr)
+ {
+ ExtArray<TkJoint*>::type joints = e->second; // Copying the array
+ m_manager.m_incompleteJointMultiMap.erase(tkActors[i]);
+ for (uint32_t j = 0; j < joints.size(); ++j)
+ {
+ m_manager.updateJoint(*joints[j]);
+ }
+ }
+ }
+ m_spawnSettings.scene->addActors(actorsToAdd, static_cast<uint32_t>(count));
+}
+
+void ExtPxFamilyImpl::destroyActors(ExtPxActor** actors, uint32_t count)
+{
+ auto pxActorsToRemove = m_physXActorsBuffer.begin();
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ pxActorsToRemove[i] = &actors[i]->getPhysXActor();
+ }
+ m_spawnSettings.scene->removeActors(pxActorsToRemove, static_cast<uint32_t>(count));
+
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ ExtPxActorImpl* actor = (ExtPxActorImpl*)actors[i];
+ m_actors.erase(actor);
+ dispatchActorDestroyed(*actor);
+ NVBLASTEXT_DELETE(actor, ExtPxActorImpl);
+ }
+}
+
+void ExtPxFamilyImpl::dispatchActorCreated(ExtPxActor& actor)
+{
+ for (ExtPxListener* listener : m_listeners)
+ listener->onActorCreated(*this, actor);
+ m_manager.dispatchActorCreated(*this, actor);
+}
+
+void ExtPxFamilyImpl::dispatchActorDestroyed(ExtPxActor& actor)
+{
+ for (ExtPxListener* listener : m_listeners)
+ listener->onActorDestroyed(*this, actor);
+ m_manager.dispatchActorDestroyed(*this, actor);
+}
+
+void ExtPxFamilyImpl::postSplitUpdate()
+{
+ for (auto actor : m_culledActors)
+ {
+ actor->release();
+ }
+ m_culledActors.resize(0);
+}
+
+
+} // namespace Blast
+} // namespace Nv
diff --git a/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxFamilyImpl.h b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxFamilyImpl.h
new file mode 100644
index 0000000..5c90346
--- /dev/null
+++ b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxFamilyImpl.h
@@ -0,0 +1,168 @@
+/*
+* Copyright (c) 2016-2017, 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.
+*/
+
+#ifndef NVBLASTEXTPXFAMILYIMPL_H
+#define NVBLASTEXTPXFAMILYIMPL_H
+
+#include "NvBlastExtPxFamily.h"
+#include "NvBlastExtArray.h"
+#include "NvBlastExtHashSet.h"
+#include "PxTransform.h"
+#include "NvBlastTkEvent.h"
+
+
+using namespace physx;
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+// Forward declarations
+class ExtPxManagerImpl;
+class ExtPxActorImpl;
+struct PxActorCreateInfo;
+
+
+class ExtPxFamilyImpl final : public ExtPxFamily, TkEventListener
+{
+ NV_NOCOPY(ExtPxFamilyImpl)
+
+public:
+ friend ExtPxActorImpl;
+ friend ExtPxManagerImpl;
+
+ //////// ctor ////////
+
+ ExtPxFamilyImpl(ExtPxManagerImpl& manager, TkFamily& tkFamily, const ExtPxAsset& pxAsset);
+ ~ExtPxFamilyImpl();
+
+ virtual void release() override;
+
+
+ //////// ExtPxFamily interface ////////
+
+// virtual bool spawn(const PxTransform& pose, const ExtPxSpawnSettings& settings) override;
+ virtual bool spawn(const physx::PxTransform& pose, const physx::PxVec3& scale, const ExtPxSpawnSettings& settings) override;
+ virtual bool despawn() override;
+
+
+ virtual uint32_t getActorCount() const override
+ {
+ return m_actors.size();
+ }
+
+ virtual uint32_t getActors(ExtPxActor** buffer, uint32_t bufferSize) const override
+ {
+ uint32_t index = 0;
+ for (auto it = const_cast<ExtPxFamilyImpl*>(this)->m_actors.getIterator(); !it.done() && index < bufferSize; ++it)
+ {
+ buffer[index++] = *it;
+ }
+ return index;
+ }
+
+ virtual TkFamily& getTkFamily() const override
+ {
+ return m_tkFamily;
+ }
+
+ virtual const physx::PxShape* const* getSubchunkShapes() const override
+ {
+ return m_subchunkShapes.begin();
+ }
+
+ virtual const ExtPxAsset& getPxAsset() const override
+ {
+ return m_pxAsset;
+ }
+
+ virtual void setMaterial(PxMaterial& material) override
+ {
+ m_spawnSettings.material = &material;
+ }
+
+ virtual void setPxShapeDescTemplate(const ExtPxShapeDescTemplate* pxShapeDesc) override
+ {
+ m_pxShapeDescTemplate = pxShapeDesc;
+ }
+
+ virtual const ExtPxShapeDescTemplate* getPxShapeDescTemplate() const override
+ {
+ return m_pxShapeDescTemplate;
+ }
+
+ virtual void setPxActorDesc(const ExtPxActorDescTemplate* pxActorDesc) override
+ {
+ m_pxActorDescTemplate = pxActorDesc;
+ }
+
+ virtual const ExtPxActorDescTemplate* getPxActorDesc() const override
+ {
+ return m_pxActorDescTemplate;
+ }
+
+ virtual void subscribe(ExtPxListener& listener) override
+ {
+ m_listeners.pushBack(&listener);
+ }
+
+ virtual void unsubscribe(ExtPxListener& listener) override
+ {
+ m_listeners.findAndReplaceWithLast(&listener);
+ }
+
+ virtual void postSplitUpdate() override;
+
+ //////// TkEventListener interface ////////
+
+ virtual void receive(const TkEvent* events, uint32_t eventCount) override;
+
+
+ //////// events dispatch ////////
+
+ void dispatchActorCreated(ExtPxActor& actor);
+ void dispatchActorDestroyed(ExtPxActor& actor);
+
+
+private:
+ //////// private methods ////////
+
+ void createActors(TkActor** tkActors, const PxActorCreateInfo* pxActorInfos, uint32_t count);
+ void destroyActors(ExtPxActor** actors, uint32_t count);
+
+ //////// data ////////
+
+ ExtPxManagerImpl& m_manager;
+ TkFamily& m_tkFamily;
+ const ExtPxAsset& m_pxAsset;
+ ExtPxSpawnSettings m_spawnSettings;
+ const ExtPxShapeDescTemplate* m_pxShapeDescTemplate;
+ const ExtPxActorDescTemplate* m_pxActorDescTemplate;
+ bool m_isSpawned;
+ PxTransform m_initialTransform;
+ PxVec3 m_initialScale;
+ ExtHashSet<ExtPxActor*>::type m_actors;
+ ExtArray<TkActor*>::type m_culledActors;
+ ExtInlineArray<ExtPxListener*, 4>::type m_listeners;
+ ExtArray<PxShape*>::type m_subchunkShapes;
+ ExtArray<TkActor*>::type m_newActorsBuffer;
+ ExtArray<PxActorCreateInfo>::type m_newActorCreateInfo;
+ ExtArray<PxActor*>::type m_physXActorsBuffer;
+ ExtArray<ExtPxActor*>::type m_actorsBuffer;
+ ExtArray<uint32_t>::type m_indicesScratch;
+};
+
+} // namespace Blast
+} // namespace Nv
+
+
+#endif // ifndef NVBLASTEXTPXFAMILYIMPL_H
diff --git a/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxManagerImpl.cpp b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxManagerImpl.cpp
new file mode 100644
index 0000000..42266ee
--- /dev/null
+++ b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxManagerImpl.cpp
@@ -0,0 +1,127 @@
+/*
+* Copyright (c) 2016-2017, 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 "NvBlastExtPxManagerImpl.h"
+#include "NvBlastExtPxAssetImpl.h"
+#include "NvBlastExtPxActorImpl.h"
+#include "NvBlastExtPxFamilyImpl.h"
+
+#include "NvBlastAssert.h"
+
+#include "NvBlastTkActor.h"
+#include "NvBlastTkFamily.h"
+#include "NvBlastTkGroup.h"
+#include "NvBlastTkJoint.h"
+
+#include "PxRigidDynamic.h"
+#include "PxJoint.h"
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+
+ExtPxManager* ExtPxManager::create(PxPhysics& physics, TkFramework& framework, ExtPxCreateJointFunction createFn, bool useUserData)
+{
+ return NVBLASTEXT_NEW(ExtPxManagerImpl)(physics, framework, createFn, useUserData);
+}
+
+void ExtPxManagerImpl::release()
+{
+ NVBLASTEXT_DELETE(this, ExtPxManagerImpl);
+}
+
+ExtPxFamily* ExtPxManagerImpl::createFamily(const ExtPxFamilyDesc& desc)
+{
+ NVBLASTEXT_CHECK_ERROR(desc.pxAsset != nullptr, "Family creation: pxAsset is nullptr.", return nullptr);
+
+ // create tk family
+ TkActorDesc tkActorDesc;
+ (&tkActorDesc)->NvBlastActorDesc::operator=(desc.actorDesc);
+ tkActorDesc.asset = &desc.pxAsset->getTkAsset();
+ TkActor* actor = m_framework.createActor(tkActorDesc);
+ NVBLASTEXT_CHECK_ERROR(actor != nullptr, "Family creation: tk actor creation failed.", return nullptr);
+
+ ExtPxFamilyImpl* family = NVBLASTEXT_NEW(ExtPxFamilyImpl)(*this, actor->getFamily(), *desc.pxAsset);
+
+ if (desc.group)
+ {
+ desc.group->addActor(*actor);
+ }
+
+ return family;
+}
+
+bool ExtPxManagerImpl::createJoint(TkJoint& joint)
+{
+ if (!joint.userData && m_createJointFn)
+ {
+ const TkJointData data = joint.getData();
+ ExtPxActorImpl* pxActor0 = data.actors[0] != nullptr ? reinterpret_cast<ExtPxActorImpl*>(data.actors[0]->userData) : nullptr;
+ ExtPxActorImpl* pxActor1 = data.actors[1] != nullptr ? reinterpret_cast<ExtPxActorImpl*>(data.actors[1]->userData) : nullptr;
+ NVBLAST_ASSERT(pxActor0 || pxActor1);
+ PxTransform lf0(data.attachPositions[0]);
+ PxTransform lf1(data.attachPositions[1]);
+ PxJoint* pxJoint = m_createJointFn(pxActor0, lf0, pxActor1, lf1, m_physics, joint);
+ if (pxJoint)
+ {
+ joint.userData = pxJoint;
+ return true;
+ }
+ }
+ return false;
+}
+
+void ExtPxManagerImpl::updateJoint(TkJoint& joint)
+{
+ if (joint.userData)
+ {
+ const TkJointData& data = joint.getData();
+ ExtPxActorImpl* pxActors[2];
+ for (int i = 0; i < 2; ++i)
+ {
+ if (data.actors[i] != nullptr)
+ {
+ pxActors[i] = reinterpret_cast<ExtPxActorImpl*>(data.actors[i]->userData);
+ if (pxActors[i] == nullptr)
+ {
+ ExtArray<TkJoint*>::type& joints = m_incompleteJointMultiMap[data.actors[i]];
+ NVBLAST_ASSERT(joints.find(&joint) == joints.end());
+ joints.pushBack(&joint);
+ return; // Wait until the TkActor is received to create this joint
+ }
+ }
+ else
+ {
+ pxActors[i] = nullptr;
+ }
+ }
+ NVBLAST_ASSERT(pxActors[0] || pxActors[1]);
+ PxJoint* pxJoint = reinterpret_cast<PxJoint*>(joint.userData);
+ pxJoint->setActors(pxActors[0] ? &pxActors[0]->getPhysXActor() : nullptr, pxActors[1] ? &pxActors[1]->getPhysXActor() : nullptr);
+ }
+}
+
+void ExtPxManagerImpl::destroyJoint(TkJoint& joint)
+{
+ if (joint.userData)
+ {
+ PxJoint* pxJoint = reinterpret_cast<PxJoint*>(joint.userData);
+ pxJoint->release();
+ joint.userData = nullptr;
+ }
+}
+
+
+
+} // namespace Blast
+} // namespace Nv
diff --git a/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxManagerImpl.h b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxManagerImpl.h
new file mode 100644
index 0000000..1f5e510
--- /dev/null
+++ b/NvBlast/sdk/extensions/physx/source/physics/NvBlastExtPxManagerImpl.h
@@ -0,0 +1,202 @@
+/*
+* Copyright (c) 2016-2017, 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.
+*/
+
+#ifndef NVBLASTEXTPXMANAGERIMPL_H
+#define NVBLASTEXTPXMANAGERIMPL_H
+
+#include "NvBlastExtPxManager.h"
+#include "NvBlastExtArray.h"
+#include "NvBlastExtHashMap.h"
+#include "NvBlastExtPxListener.h"
+#include "NvBlastExtPxFamily.h"
+
+#include "PxRigidDynamic.h"
+
+
+using namespace physx;
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+// Forward declarations
+class TkActor;
+
+class ExtPxManagerImpl final : public ExtPxManager
+{
+ NV_NOCOPY(ExtPxManagerImpl)
+
+public:
+ friend class ExtPxActorImpl;
+ friend class ExtPxFamilyImpl;
+
+ ExtPxManagerImpl(PxPhysics& physics, TkFramework&framework, ExtPxCreateJointFunction createFn, bool usePxUserData)
+ : m_physics(physics), m_framework(framework), m_createJointFn(createFn), m_usePxUserData(usePxUserData), m_actorCountLimit(0)
+ {
+ }
+
+ ~ExtPxManagerImpl()
+ {
+ }
+
+ virtual void release() override;
+
+
+ //////// interface ////////
+
+ virtual ExtPxFamily* createFamily(const ExtPxFamilyDesc& desc) override;
+
+ virtual bool createJoint(TkJoint& joint) override;
+
+ virtual void destroyJoint(TkJoint& joint) override;
+
+ virtual void setCreateJointFunction(ExtPxCreateJointFunction createFn) override
+ {
+ m_createJointFn = createFn;
+ }
+
+ virtual uint32_t getFamilyCount() const override
+ {
+ return m_tkFamiliesMap.size();
+ }
+
+ virtual uint32_t getFamilies(ExtPxFamily** buffer, uint32_t bufferSize) const override
+ {
+ uint32_t index = 0;
+ for (auto it = const_cast<ExtPxManagerImpl*>(this)->m_tkFamiliesMap.getIterator(); !it.done() && index < bufferSize; ++it)
+ {
+ buffer[index++] = it->second;
+ }
+ return index;
+ }
+
+ virtual ExtPxFamily* getFamilyFromTkFamily(TkFamily& family) const override
+ {
+ auto entry = m_tkFamiliesMap.find(&family);
+ return entry != nullptr ? entry->second : nullptr;
+ }
+
+ virtual ExtPxActor* getActorFromPhysXActor(const PxRigidDynamic& pxActor) const override
+ {
+ auto it = m_physXActorsMap.find(&pxActor);
+ return it != nullptr ? it->second : nullptr;
+ }
+
+ virtual PxPhysics& getPhysics() const override
+ {
+ return m_physics;
+ }
+
+ virtual TkFramework& getFramework() const override
+ {
+ return m_framework;
+ }
+
+ virtual bool isPxUserDataUsed() const override
+ {
+ return m_usePxUserData;
+ }
+
+ virtual void subscribe(ExtPxListener& listener) override
+ {
+ m_listeners.pushBack(&listener);
+ }
+
+ virtual void unsubscribe(ExtPxListener& listener) override
+ {
+ m_listeners.findAndReplaceWithLast(&listener);
+ }
+
+ virtual void setActorCountLimit(uint32_t limit) override
+ {
+ m_actorCountLimit = limit;
+ }
+
+ virtual uint32_t getActorCountLimit() override
+ {
+ return m_actorCountLimit;
+ }
+
+ virtual uint32_t getPxActorCount() const override
+ {
+ return m_physXActorsMap.size();
+ }
+
+
+ //////// internal public methods ////////
+
+ void registerActor(PxRigidDynamic* pxActor, ExtPxActor* actor)
+ {
+ if (m_usePxUserData)
+ {
+ pxActor->userData = actor;
+ }
+ m_physXActorsMap[pxActor] = actor;
+ }
+
+ void unregisterActor(PxRigidDynamic* pxActor)
+ {
+ if (m_usePxUserData)
+ {
+ pxActor->userData = nullptr;
+ }
+ m_physXActorsMap.erase(pxActor);
+ }
+
+ void registerFamily(ExtPxFamily& family)
+ {
+ m_tkFamiliesMap[&family.getTkFamily()] = &family;
+ }
+
+ void unregisterFamily(ExtPxFamily& family)
+ {
+ m_tkFamiliesMap.erase(&family.getTkFamily());
+ }
+
+ void updateJoint(TkJoint& joint);
+
+
+ //////// events dispatch ////////
+
+ void dispatchActorCreated(ExtPxFamily& family, ExtPxActor& actor)
+ {
+ for (ExtPxListener* listener : m_listeners)
+ listener->onActorCreated(family, actor);
+ }
+
+ void dispatchActorDestroyed(ExtPxFamily& family, ExtPxActor&actor)
+ {
+ for (ExtPxListener* listener : m_listeners)
+ listener->onActorDestroyed(family, actor);
+ }
+
+
+private:
+
+ //////// data ////////
+
+ PxPhysics& m_physics;
+ TkFramework& m_framework;
+ ExtPxCreateJointFunction m_createJointFn;
+ bool m_usePxUserData;
+ ExtInlineArray<ExtPxListener*, 8>::type m_listeners;
+ ExtHashMap<const PxRigidDynamic*, ExtPxActor*>::type m_physXActorsMap;
+ ExtHashMap<TkFamily*, ExtPxFamily*>::type m_tkFamiliesMap;
+ ExtHashMap<TkActor*, ExtArray<TkJoint*>::type >::type m_incompleteJointMultiMap;
+ uint32_t m_actorCountLimit;
+};
+
+} // namespace Blast
+} // namespace Nv
+
+
+#endif // ifndef NVBLASTEXTPXMANAGERIMPL_H
diff --git a/NvBlast/sdk/extensions/physx/source/sync/NvBlastExtSync.cpp b/NvBlast/sdk/extensions/physx/source/sync/NvBlastExtSync.cpp
new file mode 100644
index 0000000..5f018d9
--- /dev/null
+++ b/NvBlast/sdk/extensions/physx/source/sync/NvBlastExtSync.cpp
@@ -0,0 +1,235 @@
+/*
+* Copyright (c) 2016-2017, 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 "NvBlastExtSync.h"
+#include "NvBlastAssert.h"
+#include "NvBlast.h"
+#include "NvBlastExtDefs.h"
+#include "NvBlastExtPxManager.h"
+#include "NvBlastExtPxFamily.h"
+#include "NvBlastExtPxActor.h"
+#include "PxRigidDynamic.h"
+
+#include <chrono>
+using namespace std::chrono;
+
+namespace Nv
+{
+namespace Blast
+{
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// ExtSyncImpl Definition
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+class ExtSyncImpl : public ExtSync
+{
+ NV_NOCOPY(ExtSyncImpl)
+
+public:
+ //////// ctor ////////
+
+ ExtSyncImpl();
+
+ ~ExtSyncImpl();
+
+
+ //////// TkEventListener interface ////////
+
+ virtual void receive(const TkEvent* events, uint32_t eventCount) override;
+
+
+ //////// ExtSync interface ////////
+
+ virtual void release() override;
+
+ virtual void syncFamily(const TkFamily& family) override;
+ virtual void syncFamily(const ExtPxFamily& family) override;
+
+ virtual uint32_t getSyncBufferSize() const override;
+ virtual void acquireSyncBuffer(const ExtSyncEvent*const*& buffer, uint32_t& size) const override;
+ virtual void releaseSyncBuffer() override;
+
+ virtual void applySyncBuffer(TkFramework& framework, const ExtSyncEvent** buffer, uint32_t size, TkGroup* groupForNewActors, ExtPxManager* manager) override;
+
+
+private:
+ //////// data ////////
+
+ std::vector<ExtSyncEvent*> m_syncEvents;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// ExtSyncEvent Implementation
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void ExtSyncEvent::release()
+{
+ NVBLASTEXT_DELETE(this, ExtSyncEvent);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// ExtSyncImpl Implementation
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ExtSync* ExtSync::create()
+{
+ return NVBLASTEXT_NEW(ExtSyncImpl) ();
+}
+
+void ExtSyncImpl::release()
+{
+ NVBLASTEXT_DELETE(this, ExtSyncImpl);
+}
+
+ExtSyncImpl::ExtSyncImpl()
+{
+}
+
+ExtSyncImpl::~ExtSyncImpl()
+{
+ releaseSyncBuffer();
+}
+
+void ExtSyncImpl::receive(const TkEvent* events, uint32_t eventCount)
+{
+ for (uint32_t i = 0; i < eventCount; ++i)
+ {
+ const TkEvent& tkEvent = events[i];
+ if (tkEvent.type == TkEvent::FractureCommand)
+ {
+ const TkFractureCommands* fracEvent = tkEvent.getPayload<TkFractureCommands>();
+ ExtSyncEventFracture* e = NVBLASTEXT_NEW(ExtSyncEventFracture) ();
+ e->timestamp = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
+ e->familyID = fracEvent->tkActorData.family->getID();
+ e->bondFractures.resize(fracEvent->buffers.bondFractureCount);
+ e->chunkFractures.resize(fracEvent->buffers.chunkFractureCount);
+ memcpy(e->bondFractures.data(), fracEvent->buffers.bondFractures, e->bondFractures.size() * sizeof(NvBlastBondFractureData));
+ memcpy(e->chunkFractures.data(), fracEvent->buffers.chunkFractures, e->chunkFractures.size() * sizeof(NvBlastChunkFractureData));
+ m_syncEvents.push_back(e);
+ }
+ }
+}
+
+void ExtSyncImpl::syncFamily(const TkFamily& family)
+{
+ ExtSyncEventFamilySync* e = NVBLASTEXT_NEW(ExtSyncEventFamilySync) ();
+ e->timestamp = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
+ e->familyID = family.getID();
+ const NvBlastFamily* familyLL = family.getFamilyLL();
+ const uint32_t size = NvBlastFamilyGetSize(familyLL, NvBlastTkFrameworkGet()->getLogFn());
+ e->family = std::vector<char>((char*)familyLL, (char*)familyLL + size);
+ m_syncEvents.push_back(e);
+}
+
+void ExtSyncImpl::syncFamily(const ExtPxFamily& family)
+{
+ const TkFamily& tkFamily = family.getTkFamily();
+
+ syncFamily(tkFamily);
+
+ ExtSyncEventPhysicsSync* e = NVBLASTEXT_NEW(ExtSyncEventPhysicsSync) ();
+ e->timestamp = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
+ e->familyID = tkFamily.getID();
+ std::vector<ExtPxActor*> actors(family.getActorCount());
+ family.getActors(actors.data(), static_cast<uint32_t>(actors.size()));
+ e->data.reserve(actors.size());
+ for (ExtPxActor* actor : actors)
+ {
+ ExtSyncEventPhysicsSync::ActorData data;
+ data.transform = actor->getPhysXActor().getGlobalPose();
+ data.actorIndex = actor->getTkActor().getIndex();
+ e->data.push_back(data);
+ }
+
+ m_syncEvents.push_back(e);
+}
+
+uint32_t ExtSyncImpl::getSyncBufferSize() const
+{
+ return static_cast<uint32_t>(m_syncEvents.size());
+}
+
+void ExtSyncImpl::acquireSyncBuffer(const ExtSyncEvent* const*& buffer, uint32_t& size) const
+{
+ buffer = m_syncEvents.data();
+ size = static_cast<uint32_t>(m_syncEvents.size());
+}
+
+void ExtSyncImpl::releaseSyncBuffer()
+{
+ for (uint32_t i = 0; i < m_syncEvents.size(); ++i)
+ {
+ NVBLASTEXT_DELETE(m_syncEvents[i], ExtSyncEvent);
+ }
+ m_syncEvents.clear();
+}
+
+void ExtSyncImpl::applySyncBuffer(TkFramework& framework, const ExtSyncEvent** buffer, uint32_t size, TkGroup* groupForNewActors, ExtPxManager* manager)
+{
+ const TkType* familyType = framework.getType(TkTypeIndex::Family);
+ NVBLAST_ASSERT(familyType);
+
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ const ExtSyncEvent* e = buffer[i];
+ const NvBlastID& id = e->familyID;
+ TkIdentifiable* object = framework.findObjectByID(id);
+ if (object && object->getType() == *familyType)
+ {
+ TkFamily* family = static_cast<TkFamily*>(object);
+
+ if (e->type == ExtSyncEventFracture::EVENT_TYPE)
+ {
+ const ExtSyncEventFracture* fractureEvent = e->getEvent<ExtSyncEventFracture>();
+ const NvBlastFractureBuffers commands =
+ {
+ static_cast<uint32_t>(fractureEvent->bondFractures.size()),
+ static_cast<uint32_t>(fractureEvent->chunkFractures.size()),
+ const_cast<NvBlastBondFractureData*>(fractureEvent->bondFractures.data()),
+ const_cast<NvBlastChunkFractureData*>(fractureEvent->chunkFractures.data())
+ };
+ family->applyFracture(&commands);
+ }
+ else if (e->type == ExtSyncEventFamilySync::EVENT_TYPE)
+ {
+ const ExtSyncEventFamilySync* familyEvent = e->getEvent<ExtSyncEventFamilySync>();
+ family->reinitialize((NvBlastFamily*)familyEvent->family.data(), groupForNewActors);
+ }
+ else if (e->type == ExtSyncEventPhysicsSync::EVENT_TYPE && manager)
+ {
+ const ExtSyncEventPhysicsSync* physicsEvent = e->getEvent<ExtSyncEventPhysicsSync>();
+ ExtPxFamily* pxFamily = manager->getFamilyFromTkFamily(*family);
+ if (pxFamily)
+ {
+ std::vector<ExtPxActor*> actors(pxFamily->getActorCount());
+ pxFamily->getActors(actors.data(), static_cast<uint32_t>(actors.size()));
+
+ for (auto data : physicsEvent->data)
+ {
+ for (ExtPxActor* physicsaActor : actors)
+ {
+ if (data.actorIndex == physicsaActor->getTkActor().getIndex())
+ {
+ physicsaActor->getPhysXActor().setGlobalPose(data.transform);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+} // namespace Blast
+} // namespace Nv