diff options
| author | Bryan Galdrikian <[email protected]> | 2017-02-21 12:07:59 -0800 |
|---|---|---|
| committer | Bryan Galdrikian <[email protected]> | 2017-02-21 12:07:59 -0800 |
| commit | 446ce137c6823ba9eff273bdafdaf266287c7c98 (patch) | |
| tree | d20aab3e2ed08d7b3ca71c2f40db6a93ea00c459 /NvBlast/sdk/extensions/physx/source | |
| download | blast-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')
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 |