It also can be used as another way to apply impact damage, which can give the visually pleasant result of an actor breaking in a weak place instead of the place of contact.
Debug render can help a lot for tuning, consider using stressSolver->fillDebugRender(...) for that.
ExtStressSolver* stressSolver = ExtStressSolver::create(family, settings);
ExtStressSolverSettings are passed in create function, but also can be changed at any time with stressSolver->setSettings(...).
It fully utilizes the fact that it knows the initial support graph structure and does a maximum of processing in the create(...) method call. After that, all actor split calls are synchronized internally and efficiently so only the actual stress propagation takes most of computational time.
You need to provide physics specific information (mass, volume, position, static) for every node in support graph since Blast™ itself is physics agnostic. There are two ways to do it. One way is to call stressSolver->setNodeInfo(...) for every graph node. The other way is to call stressSolver->setAllNodesInfoFromLL() once: all the data will be populated using NvBlastAsset chunk's data, in particular volume and centroid. All nodes connected to 'world' chunk are marked as static.
stressSolver->setAllNodesInfoFromLL();
Stress solver needs to keep track for actor create/destroy events in order to update its internal stress graph accordingly. So you need to call stressSolver->notifyActorCreated(actor) and stressSolver->notifyActorDestroyed(actor) every time an actor is created or destroyed, including the initial actor the family had when the stress solver was created. There is no need to track actors which contain only one or less graph nodes. In that case notifyActorCreated(actor) returns 'false' as a hint. It means that the stress solver will ignore them, as for those actors applying forces does not make any sense.
A typical update loop looks like this:
Example code from ExtPxStressSolverImpl:
void ExtPxStressSolverImpl::onActorCreated(ExtPxFamily& /*family*/, ExtPxActor& actor) { if (m_solver->notifyActorCreated(*actor.getTkActor().getActorLL())) { m_actors.insert(&actor); } } void ExtPxStressSolverImpl::onActorDestroyed(ExtPxFamily& /*family*/, ExtPxActor& actor) { m_solver->notifyActorDestroyed(*actor.getTkActor().getActorLL()); m_actors.erase(&actor); } void ExtPxStressSolverImpl::update(bool doDamage) { for (auto it = m_actors.getIterator(); !it.done(); ++it) { const ExtPxActor* actor = *it; PxRigidDynamic& rigidDynamic = actor->getPhysXActor(); const bool isStatic = rigidDynamic.getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC; if (isStatic) { PxVec3 gravity = rigidDynamic.getScene()->getGravity(); PxVec3 localGravity = rigidDynamic.getGlobalPose().rotateInv(gravity); m_solver->addGravityForce(*actor->getTkActor().getActorLL(), localGravity); } else { PxVec3 localCenterMass = rigidDynamic.getCMassLocalPose().p; PxVec3 localAngularVelocity = rigidDynamic.getGlobalPose().rotateInv(rigidDynamic.getAngularVelocity()); m_solver->addAngularVelocity(*actor->getTkActor().getActorLL(), localCenterMass, localAngularVelocity); } } m_solver->update(); if (doDamage && m_solver->getOverstressedBondCount() > 0) { NvBlastFractureBuffers commands; m_solver->generateFractureCommands(commands); if (commands.bondFractureCount > 0) { m_family.getTkFamily().applyFracture(&commands); } } }
Have a look at ExtPxStressSolver implementation code, which is basically a high level wrapper on NvBlastExtStress to couple it with PhysX™ and NvBlastExtPx extension (see extpxstresssolver).