571 lines
24 KiB
C++
571 lines
24 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "EventDefaults.h"
|
|
#include "EventsData.h"
|
|
#include "PhysicsProxy/SingleParticlePhysicsProxy.h"
|
|
#include "Chaos/PBDRigidsEvolution.h"
|
|
#include "Chaos/PBDRigidClustering.h"
|
|
#include "Chaos/PBDCollisionConstraints.h"
|
|
#include "Chaos/CollisionResolutionTypes.h"
|
|
#include "PBDRigidsSolver.h"
|
|
#include "PhysicsProxy/SkeletalMeshPhysicsProxy.h"
|
|
#include "PhysicsProxy/StaticMeshPhysicsProxy.h"
|
|
#include "PhysicsProxy/GeometryCollectionPhysicsProxy.h"
|
|
#include "PhysicsProxy/PerSolverFieldSystem.h"
|
|
#include "ChaosSolversModule.h"
|
|
#include "Chaos/CollisionFilterData.h"
|
|
|
|
namespace Chaos
|
|
{
|
|
DECLARE_CYCLE_STAT(TEXT("EventDefaults::RegisterCollisionEvent_Filter"), STAT_Events_RegisterCollisionEvent_Filter, STATGROUP_Chaos);
|
|
DECLARE_CYCLE_STAT(TEXT("EventDefaults::RegisterCollisionEvent_Notify"), STAT_Events_RegisterCollisionEvent_Notify, STATGROUP_Chaos);
|
|
|
|
/**
|
|
* Resolves a material from a shape and a collision.
|
|
* Either both shapes are simple primitives, or one is simple and the other is a heightfield or trimesh.
|
|
* In the case of the trimesh/heightfield the contact points should have the face index of the hit face
|
|
* which the geometry can translate into a material index.
|
|
* @param InShape - The shape to resolve a material for
|
|
* @param InConstraint - The collision that the shape is a part of
|
|
* @see FTriangleMeshImplicitObject::ContactManifoldImp
|
|
* @see FHeightField::ContactManifoldImp
|
|
*/
|
|
FMaterialHandle ResolveMaterial(const FPerShapeData* InShape, const FPBDCollisionConstraint& InConstraint)
|
|
{
|
|
// @todo(chaos): this does not handle PerParticleMaterials so may return the wrong value when that is used
|
|
|
|
if (InShape && (InShape->NumMaterials() > 0))
|
|
{
|
|
// Simple case, one material. All primitives (Box, convex, sphere etc.) should only have one material.
|
|
// Heightfield and Trimesh can have one or more and will require data from the contacts to resolve.
|
|
if (InShape->NumMaterials() == 1)
|
|
{
|
|
return InShape->GetMaterial(0);
|
|
}
|
|
else if (InConstraint.NumManifoldPoints() > 0)
|
|
{
|
|
// We only support one material per manifold (just use the first manifold point)
|
|
const int32 ShapeFaceIndex = InConstraint.GetManifoldPoint(0).ContactPoint.FaceIndex;
|
|
const int32 ShapeMaterialIndex = InShape->GetGeometry()->GetMaterialIndex(ShapeFaceIndex);
|
|
if (ShapeMaterialIndex < InShape->NumMaterials())
|
|
{
|
|
return InShape->GetMaterial(ShapeMaterialIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
// No valid material
|
|
return {};
|
|
}
|
|
|
|
void FEventDefaults::RegisterSystemEvents(FEventManager& EventManager)
|
|
{
|
|
RegisterCollisionEvent(EventManager);
|
|
RegisterBreakingEvent(EventManager);
|
|
RegisterTrailingEvent(EventManager);
|
|
RegisterSleepingEvent(EventManager);
|
|
RegisterRemovalEvent(EventManager);
|
|
RegisterCrumblingEvent(EventManager);
|
|
}
|
|
|
|
void FEventDefaults::RegisterCollisionEvent(FEventManager& EventManager)
|
|
{
|
|
EventManager.template RegisterEvent<FCollisionEventData>(EEventType::Collision, []
|
|
(const Chaos::FPBDRigidsSolver* Solver, FCollisionEventData& CollisionEventData, bool bResetData)
|
|
{
|
|
check(Solver);
|
|
|
|
EnsureIsInPhysicsThreadContext();
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_GatherCollisionEvent);
|
|
|
|
// #todo: This isn't working - SolverActor parameters are set on a solver but it is currently a different solver that is simulating!!
|
|
//if (!Solver->GetEventFilters()->IsCollisionEventEnabled())
|
|
// return;
|
|
|
|
if (bResetData)
|
|
{
|
|
CollisionEventData.Reset();
|
|
}
|
|
CollisionEventData.CollisionData.TimeCreated = Solver->MTime;
|
|
CollisionEventData.PhysicsProxyToCollisionIndices.TimeCreated = Solver->MTime;
|
|
|
|
const auto* Evolution = Solver->GetEvolution();
|
|
|
|
const FPBDCollisionConstraints& CollisionRule = Evolution->GetCollisionConstraints();
|
|
|
|
|
|
const FPBDRigidParticles& Particles = Evolution->GetParticles().GetDynamicParticles();
|
|
const TArrayCollectionArray<ClusterId>& ClusterIdsArray = Evolution->GetRigidClustering().GetClusterIdsArray();
|
|
#if TODO_REIMPLEMENT_RIGID_CLUSTERING
|
|
const Chaos::FPBDRigidsSolver::FClusteringType::FClusterMap& ParentToChildrenMap = Evolution->GetRigidClustering().GetChildrenMap();
|
|
#endif
|
|
const typename Chaos::FRigidClustering::FClusterMap& ParentToChildrenMap = Evolution->GetRigidClustering().GetChildrenMap();
|
|
|
|
if(CollisionRule.NumConstraints() > 0)
|
|
{
|
|
// Get the number of valid constraints (AccumulatedImpulse != 0.f and Phi < 0.f) from AllConstraintsArray
|
|
TArray<const Chaos::FPBDCollisionConstraintHandle*> ValidCollisionHandles;
|
|
ValidCollisionHandles.SetNumUninitialized(CollisionRule.NumConstraints());
|
|
int32 NumValidCollisions = 0;
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_Events_RegisterCollisionEvent_Filter);
|
|
const FReal MinDeltaVelocityForHitEvents = FChaosSolversModule::GetModule()->GetSettingsProvider().GetMinDeltaVelocityForHitEvents();
|
|
const FPBDCollisionConstraints::FConstHandles& CollisionHandles = CollisionRule.GetConstConstraintHandles();
|
|
TArray<bool> ValidArray;
|
|
ValidArray.SetNum(CollisionHandles.Num());
|
|
InnerPhysicsParallelForRange(CollisionHandles.Num(), [&](int32 StartRangeIndex, int32 EndRangeIndex)
|
|
{
|
|
for (int32 Index = StartRangeIndex; Index < EndRangeIndex; ++Index)
|
|
{
|
|
ValidArray[Index] = false;
|
|
|
|
const Chaos::FPBDCollisionConstraintHandle* ContactHandle = CollisionHandles[Index];
|
|
if (ContactHandle == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (NumValidCollisions >= CollisionRule.NumConstraints())
|
|
{
|
|
break;
|
|
}
|
|
|
|
const FPBDCollisionConstraint& Constraint = ContactHandle->GetContact();
|
|
if (ensure(!Constraint.AccumulatedImpulse.ContainsNaN() && FMath::IsFinite(Constraint.GetPhi())))
|
|
{
|
|
|
|
const FPerShapeData* Shape0 = Constraint.GetShape0();
|
|
const FPerShapeData* Shape1 = Constraint.GetShape1();
|
|
|
|
// If we don't have a filter - allow the notify, otherwise obey the filter flag
|
|
const bool bFilter0Notify = Shape0 ? Shape0->GetSimData().HasFlag(EFilterFlags::ContactNotify) : true;
|
|
const bool bFilter1Notify = Shape1 ? Shape1->GetSimData().HasFlag(EFilterFlags::ContactNotify) : true;
|
|
|
|
if(!bFilter0Notify && !bFilter1Notify)
|
|
{
|
|
// No need to notify - engine didn't request notifications for either shape.
|
|
continue;
|
|
}
|
|
|
|
const FGeometryParticleHandle* Particle0 = Constraint.GetParticle0();
|
|
const FGeometryParticleHandle* Particle1 = Constraint.GetParticle1();
|
|
const FKinematicGeometryParticleHandle* Body0 = Particle0->CastToKinematicParticle();
|
|
|
|
// presently when a rigidbody or kinematic hits static geometry then Body1 is null
|
|
const FKinematicGeometryParticleHandle* Body1 = Particle1->CastToKinematicParticle();
|
|
|
|
const FKinematicGeometryParticleHandle* Primary = Body0 ? Body0 : Body1;
|
|
const FKinematicGeometryParticleHandle* Secondary = Body0 ? Body1 : Body0;
|
|
|
|
//
|
|
const int32 NumManifoldPoints = Constraint.GetManifoldPoints().Num();
|
|
|
|
if (((Constraint.IsProbe() && NumManifoldPoints > 0) || !Constraint.AccumulatedImpulse.IsZero()) && Primary)
|
|
{
|
|
if (ensure(!Constraint.CalculateWorldContactLocation().ContainsNaN() &&
|
|
!Constraint.CalculateWorldContactNormal().ContainsNaN()) &&
|
|
!Primary->GetV().ContainsNaN() &&
|
|
!Primary->GetW().ContainsNaN() &&
|
|
(Secondary == nullptr || ((!Secondary->GetV().ContainsNaN()) && !Secondary->GetW().ContainsNaN())))
|
|
{
|
|
ValidArray[Index] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}, Chaos::LargeBatchSize);
|
|
for (int32 Index = 0; Index < CollisionHandles.Num(); ++Index)
|
|
{
|
|
if (ValidArray[Index])
|
|
{
|
|
const Chaos::FPBDCollisionConstraintHandle* ContactHandle = CollisionHandles[Index];
|
|
ValidCollisionHandles[NumValidCollisions] = ContactHandle;
|
|
NumValidCollisions++;
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_Events_RegisterCollisionEvent_Notify);
|
|
ValidCollisionHandles.SetNum(NumValidCollisions);
|
|
if (ValidCollisionHandles.Num() > 0)
|
|
{
|
|
FCollisionDataArray DupAllCollisionsDataArray;
|
|
DupAllCollisionsDataArray.SetNum(NumValidCollisions);
|
|
InnerPhysicsParallelForRange(ValidCollisionHandles.Num(), [&](int32 StartRangeIndex, int32 EndRangeIndex)
|
|
{
|
|
for (int32 IdxCollision = StartRangeIndex; IdxCollision < EndRangeIndex; ++IdxCollision)
|
|
{
|
|
const FPBDCollisionConstraint& Constraint = ValidCollisionHandles[IdxCollision]->GetContact();
|
|
|
|
const FGeometryParticleHandle* Particle0 = Constraint.GetParticle0();
|
|
const FGeometryParticleHandle* Particle1 = Constraint.GetParticle1();
|
|
|
|
FCollidingData Data;
|
|
Data.SolverTime = Solver->MTime;
|
|
Data.Location = Constraint.CalculateWorldContactLocation();
|
|
Data.AccumulatedImpulse = Constraint.AccumulatedImpulse;
|
|
Data.Normal = Constraint.CalculateWorldContactNormal();
|
|
Data.PenetrationDepth = Constraint.GetPhi();
|
|
Data.bProbe = Constraint.GetIsProbe();
|
|
|
|
// @todo(chaos): fix this casting
|
|
Data.Proxy1 = Particle0 ? const_cast<IPhysicsProxyBase*>(Particle0->PhysicsProxy()) : nullptr;
|
|
Data.Proxy2 = Particle1 ? const_cast<IPhysicsProxyBase*>(Particle1->PhysicsProxy()) : nullptr;
|
|
|
|
const FPerShapeData* Shape0 = Constraint.GetShape0();
|
|
const FPerShapeData* Shape1 = Constraint.GetShape1();
|
|
|
|
Data.ShapeIndex1 = Shape0 ? Shape0->GetShapeIndex() : INDEX_NONE;
|
|
Data.ShapeIndex2 = Shape1 ? Shape1->GetShapeIndex() : INDEX_NONE;
|
|
Data.Mat1 = ResolveMaterial(Shape0, Constraint);
|
|
Data.Mat2 = ResolveMaterial(Shape1, Constraint);
|
|
|
|
// Collision constraints require both proxies are valid. If either is not, we needn't record the collision event.
|
|
if (Data.Proxy1 == nullptr || Data.Proxy2 == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (const FPBDRigidParticleHandle* Rigid0 = Particle0->CastToRigidParticle())
|
|
{
|
|
Data.DeltaVelocity1 = Rigid0->GetV() - Rigid0->GetPreV();
|
|
}
|
|
if (const FPBDRigidParticleHandle* Rigid1 = Particle1->CastToRigidParticle())
|
|
{
|
|
Data.DeltaVelocity2 = Rigid1->GetV() - Rigid1->GetPreV();
|
|
}
|
|
|
|
// todo: do we need these anymore now we are storing the particles you can access all of this stuff from there
|
|
// do we still need these now we have pointers to particles returned?
|
|
const FPBDRigidParticleHandle* PBDRigid0 = Particle0->CastToRigidParticle();
|
|
if (PBDRigid0 && PBDRigid0->ObjectState() == EObjectStateType::Dynamic)
|
|
{
|
|
Data.Velocity1 = PBDRigid0->GetV();
|
|
Data.AngularVelocity1 = PBDRigid0->GetW();
|
|
Data.Mass1 = PBDRigid0->M();
|
|
}
|
|
|
|
const FPBDRigidParticleHandle* PBDRigid1 = Particle1->CastToRigidParticle();
|
|
if (PBDRigid1 && PBDRigid1->ObjectState() == EObjectStateType::Dynamic)
|
|
{
|
|
Data.Velocity2 = PBDRigid1->GetV();
|
|
Data.AngularVelocity2 = PBDRigid1->GetW();
|
|
Data.Mass2 = PBDRigid1->M();
|
|
}
|
|
|
|
IPhysicsProxyBase* const PhysicsProxy = const_cast<IPhysicsProxyBase*>(Particle0->PhysicsProxy());
|
|
IPhysicsProxyBase* const OtherPhysicsProxy = const_cast<IPhysicsProxyBase*>(Particle1->PhysicsProxy());
|
|
|
|
const FSolverCollisionEventFilter* SolverCollisionEventFilter = Solver->GetEventFilters()->GetCollisionFilter();
|
|
if (!SolverCollisionEventFilter->Enabled() || SolverCollisionEventFilter->Pass(Data))
|
|
|
|
{
|
|
DupAllCollisionsDataArray[IdxCollision] = Data;
|
|
#if TODO_REIMPLEMENT_RIGID_CLUSTERING
|
|
// If Constraint.ParticleIndex is a cluster store an index for a mesh in this cluster
|
|
if (ClusterIdsArray[Constraint.ParticleIndex].NumChildren > 0)
|
|
{
|
|
int32 ParticleIndexMesh = GetParticleIndexMesh(ParentToChildrenMap, Constraint.ParticleIndex);
|
|
ensure(ParticleIndexMesh != INDEX_NONE);
|
|
CollisionDataArrayItem.ParticleIndexMesh = ParticleIndexMesh;
|
|
}
|
|
// If Constraint.LevelsetIndex is a cluster store an index for a mesh in this cluster
|
|
if (ClusterIdsArray[Constraint.LevelsetIndex].NumChildren > 0)
|
|
{
|
|
int32 LevelsetIndexMesh = GetParticleIndexMesh(ParentToChildrenMap, Constraint.LevelsetIndex);
|
|
ensure(LevelsetIndexMesh != INDEX_NONE);
|
|
CollisionDataArrayItem.LevelsetIndexMesh = LevelsetIndexMesh;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}, Chaos::SmallBatchSize);
|
|
|
|
FCollisionDataArray& AllCollisionsDataArray = CollisionEventData.CollisionData.AllCollisionsArray;
|
|
TMap<IPhysicsProxyBase*, TArray<int32>>& AllCollisionsIndicesByPhysicsProxy = CollisionEventData.PhysicsProxyToCollisionIndices.PhysicsProxyToIndicesMap;
|
|
for (int32 IdxCollision = 0; IdxCollision < NumValidCollisions; ++IdxCollision)
|
|
{
|
|
if (DupAllCollisionsDataArray[IdxCollision].Proxy1 != nullptr && !DupAllCollisionsDataArray[IdxCollision].Proxy1->GetMarkedDeleted())
|
|
{
|
|
int32 NewIdx = AllCollisionsDataArray.Add(DupAllCollisionsDataArray[IdxCollision]);
|
|
AllCollisionsIndicesByPhysicsProxy.FindOrAdd(AllCollisionsDataArray[NewIdx].Proxy1).Add(FEventManager::EncodeCollisionIndex(NewIdx, false));
|
|
|
|
if (AllCollisionsDataArray[NewIdx].Proxy2 && AllCollisionsDataArray[NewIdx].Proxy2 != AllCollisionsDataArray[NewIdx].Proxy1
|
|
&& !AllCollisionsDataArray[NewIdx].Proxy2->GetMarkedDeleted())
|
|
{
|
|
AllCollisionsIndicesByPhysicsProxy.FindOrAdd(AllCollisionsDataArray[NewIdx].Proxy2).Add(FEventManager::EncodeCollisionIndex(NewIdx, true));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
void FEventDefaults::RegisterBreakingEvent(FEventManager& EventManager)
|
|
{
|
|
EventManager.template RegisterEvent<FBreakingEventData>(EEventType::Breaking, []
|
|
(const Chaos::FPBDRigidsSolver* Solver, FBreakingEventData& BreakingEventData, bool bResetData)
|
|
{
|
|
check(Solver);
|
|
|
|
EnsureIsInPhysicsThreadContext();
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_GatherBreakingEvent);
|
|
|
|
// #todo: This isn't working - SolverActor parameters are set on a solver but it is currently a different solver that is simulating!!
|
|
if (!Solver->GetEventFilters()->IsBreakingEventEnabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (bResetData)
|
|
{
|
|
BreakingEventData.Reset();
|
|
}
|
|
BreakingEventData.BreakingData.TimeCreated = Solver->MTime;
|
|
|
|
const auto* Evolution = Solver->GetEvolution();
|
|
const FPBDRigidParticles& Particles = Evolution->GetParticles().GetDynamicParticles();
|
|
const TArray<FBreakingData>& AllClusterBreakings = Evolution->GetRigidClustering().GetAllClusterBreakings();
|
|
FBreakingDataArray& FilteredBreakingDataArray = BreakingEventData.BreakingData.AllBreakingsArray;
|
|
TMap<IPhysicsProxyBase*, TArray<int32>>& FilteredBreakingIndicesByPhysicsProxy = BreakingEventData.PhysicsProxyToBreakingIndices.PhysicsProxyToIndicesMap;
|
|
|
|
if (AllClusterBreakings.Num() > 0)
|
|
{
|
|
const FSolverBreakingEventFilter* SolverBreakingEventFilter = Solver->GetEventFilters()->GetBreakingFilter();
|
|
for (int32 Idx = 0; Idx < AllClusterBreakings.Num(); ++Idx)
|
|
{
|
|
const FBreakingData& ClusterBreaking = AllClusterBreakings[Idx];
|
|
IPhysicsProxyBase* Proxy = AllClusterBreakings[Idx].Proxy;
|
|
if (!Proxy->GetMarkedDeleted() && (!SolverBreakingEventFilter->Enabled() || SolverBreakingEventFilter->Pass(ClusterBreaking)))
|
|
{
|
|
const int32 NewIndex = FilteredBreakingDataArray.Emplace(ClusterBreaking);
|
|
FilteredBreakingIndicesByPhysicsProxy.FindOrAdd(Proxy).Add(FEventManager::EncodeCollisionIndex(NewIndex, false));
|
|
BreakingEventData.BreakingData.bHasGlobalEvent |= (ClusterBreaking.EmitterFlag & EventEmitterFlag::GlobalDispatcher) != 0;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
void FEventDefaults::RegisterTrailingEvent(FEventManager& EventManager)
|
|
{
|
|
EventManager.template RegisterEvent<FTrailingEventData>(EEventType::Trailing, []
|
|
(const Chaos::FPBDRigidsSolver* Solver, FTrailingEventData& TrailingEventData, bool bResetData)
|
|
{
|
|
check(Solver);
|
|
|
|
EnsureIsInPhysicsThreadContext();
|
|
|
|
// #todo: This isn't working - SolverActor parameters are set on a solver but it is currently a different solver that is simulating!!
|
|
if (!Solver->GetEventFilters()->IsTrailingEventEnabled())
|
|
return;
|
|
|
|
const auto* Evolution = Solver->GetEvolution();
|
|
|
|
const TArrayCollectionArray<ClusterId>& ClusterIdsArray = Evolution->GetRigidClustering().GetClusterIdsArray();
|
|
#if TODO_REIMPLEMENT_RIGID_CLUSTERING
|
|
const TMap<uint32, TUniquePtr<TArray<uint32>>>& ParentToChildrenMap = Evolution->GetRigidClustering().GetChildrenMap();
|
|
#endif
|
|
|
|
|
|
if (bResetData)
|
|
{
|
|
TrailingEventData.Reset();
|
|
}
|
|
TrailingEventData.TrailingData.TimeCreated = Solver->MTime;
|
|
TrailingEventData.PhysicsProxyToTrailingIndices.TimeCreated = Solver->MTime;
|
|
|
|
const TArray<TPBDRigidParticleHandle<Chaos::FReal, 3>*>& ActiveParticlesArray = Evolution->GetParticles().GetActiveParticlesArray();
|
|
FTrailingDataArray& AllTrailingsDataArray = TrailingEventData.TrailingData.AllTrailingsArray;
|
|
TMap<IPhysicsProxyBase*, TArray<int32>>& AllTrailingIndicesByPhysicsProxy = TrailingEventData.PhysicsProxyToTrailingIndices.PhysicsProxyToIndicesMap;
|
|
|
|
for (TPBDRigidParticleHandle<Chaos::FReal, 3>* ActiveParticle : ActiveParticlesArray)
|
|
{
|
|
|
|
if (ensure(FMath::IsFinite(ActiveParticle->InvM())))
|
|
{
|
|
if (ActiveParticle->InvM() != 0.f &&
|
|
ActiveParticle->GetGeometry() &&
|
|
ActiveParticle->GetGeometry()->HasBoundingBox())
|
|
{
|
|
if (ensure(!ActiveParticle->GetX().ContainsNaN() &&
|
|
!ActiveParticle->GetV().ContainsNaN() &&
|
|
!ActiveParticle->GetW().ContainsNaN() &&
|
|
FMath::IsFinite(ActiveParticle->M())))
|
|
{
|
|
FTrailingData TrailingData;
|
|
TrailingData.Location = ActiveParticle->GetX();
|
|
TrailingData.Orientation = ActiveParticle->GetR();
|
|
TrailingData.Velocity = ActiveParticle->GetV();
|
|
TrailingData.AngularVelocity = ActiveParticle->GetW();
|
|
TrailingData.Mass = ActiveParticle->M();
|
|
TrailingData.Proxy = ActiveParticle->PhysicsProxy();
|
|
|
|
if (ActiveParticle->GetGeometry()->HasBoundingBox())
|
|
{
|
|
TrailingData.BoundingBox = ActiveParticle->GetGeometry()->BoundingBox();
|
|
}
|
|
|
|
if (TrailingData.Proxy->GetType() == EPhysicsProxyType::GeometryCollectionType)
|
|
{
|
|
FGeometryCollectionPhysicsProxy* ConcreteProxy = static_cast<FGeometryCollectionPhysicsProxy*>(TrailingData.Proxy);
|
|
TrailingData.TransformGroupIndex = ConcreteProxy->GetTransformGroupIndexFromHandle(ActiveParticle);
|
|
}
|
|
else
|
|
{
|
|
TrailingData.TransformGroupIndex = INDEX_NONE;
|
|
}
|
|
|
|
const FSolverTrailingEventFilter* SolverTrailingEventFilter = Solver->GetEventFilters()->GetTrailingFilter();
|
|
if (!SolverTrailingEventFilter->Enabled() || SolverTrailingEventFilter->Pass(TrailingData))
|
|
{
|
|
int32 NewIdx = AllTrailingsDataArray.Add(FTrailingData());
|
|
FTrailingData& TrailingDataArrayItem = AllTrailingsDataArray[NewIdx];
|
|
TrailingDataArrayItem = TrailingData;
|
|
|
|
// Add to AllTrailingIndicesByPhysicsProxy
|
|
AllTrailingIndicesByPhysicsProxy.FindOrAdd(TrailingData.Proxy).Add(FEventManager::EncodeCollisionIndex(NewIdx, false));
|
|
|
|
// If IdxParticle is a cluster store an index for a mesh in this cluster
|
|
#if 0
|
|
if (ClusterIdsArray[IdxParticle].NumChildren > 0)
|
|
{
|
|
int32 ParticleIndexMesh = GetParticleIndexMesh(ParentToChildrenMap, IdxParticle);
|
|
ensure(ParticleIndexMesh != INDEX_NONE);
|
|
TrailingDataArrayItem.ParticleIndexMesh = ParticleIndexMesh;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
void FEventDefaults::RegisterSleepingEvent(FEventManager& EventManager)
|
|
{
|
|
EventManager.template RegisterEvent<FSleepingEventData>(EEventType::Sleeping, []
|
|
(const Chaos::FPBDRigidsSolver* Solver, FSleepingEventData& SleepingEventData, bool bResetData)
|
|
{
|
|
check(Solver);
|
|
|
|
EnsureIsInPhysicsThreadContext();
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_GatherSleepingEvent);
|
|
|
|
const auto* Evolution = Solver->GetEvolution();
|
|
|
|
SleepingEventData.Reset();
|
|
|
|
Chaos::FPBDRigidsSolver* NonConstSolver = const_cast<Chaos::FPBDRigidsSolver*>(Solver);
|
|
|
|
const TArray<FPBDRigidParticles*> RelevantParticleArrays = {
|
|
&NonConstSolver->Particles.GetDynamicParticles(),
|
|
&NonConstSolver->Particles.GetClusteredParticles(),
|
|
&NonConstSolver->Particles.GetGeometryCollectionParticles()
|
|
};
|
|
|
|
FSleepingDataArray& EventSleepDataArray = SleepingEventData.SleepingData;
|
|
for (FPBDRigidParticles* ParticleArray : RelevantParticleArrays)
|
|
{
|
|
check(ParticleArray != nullptr);
|
|
ParticleArray->GetSleepDataLock().ReadLock();
|
|
const TArray<TSleepData<FReal, 3>>& SolverSleepingData = ParticleArray->GetSleepData();
|
|
for (const TSleepData<FReal, 3>& SleepData : SolverSleepingData)
|
|
{
|
|
if (SleepData.Particle)
|
|
{
|
|
FGeometryParticle* Particle = SleepData.Particle->GTGeometryParticle();
|
|
if (Particle && SleepData.Particle->PhysicsProxy())
|
|
{
|
|
int32 NewIdx = EventSleepDataArray.Add(FSleepingData());
|
|
FSleepingData& SleepingDataArrayItem = EventSleepDataArray[NewIdx];
|
|
SleepingDataArrayItem.Proxy = SleepData.Particle->PhysicsProxy();
|
|
SleepingDataArrayItem.Sleeping = SleepData.Sleeping;
|
|
}
|
|
}
|
|
}
|
|
ParticleArray->GetSleepDataLock().ReadUnlock();
|
|
ParticleArray->ClearSleepData();
|
|
}
|
|
|
|
// We don't care about sleep data added to these
|
|
NonConstSolver->Particles.GetDynamicKinematicParticles().ClearSleepData();
|
|
NonConstSolver->Particles.GetDynamicDisabledParticles().ClearSleepData();
|
|
});
|
|
}
|
|
|
|
void FEventDefaults::RegisterRemovalEvent(FEventManager& EventManager)
|
|
{
|
|
EventManager.template RegisterEvent<FRemovalEventData>(EEventType::Removal, []
|
|
(const Chaos::FPBDRigidsSolver* Solver, FRemovalEventData& RemovalEventData, bool bResetData)
|
|
{
|
|
check(Solver);
|
|
EnsureIsInPhysicsThreadContext();
|
|
|
|
if (bResetData)
|
|
{
|
|
RemovalEventData.Reset();
|
|
}
|
|
RemovalEventData.RemovalData.TimeCreated = Solver->MTime;
|
|
|
|
const TArray<FRemovalData>& AllRemovalsArray = Solver->GetEvolution()->GetAllRemovals();
|
|
FRemovalDataArray& AllRemovalDataArray = RemovalEventData.RemovalData.AllRemovalArray;
|
|
TMap<IPhysicsProxyBase*, TArray<int32>>& AllRemovalIndicesByPhysicsProxy = RemovalEventData.PhysicsProxyToRemovalIndices.PhysicsProxyToIndicesMap;
|
|
|
|
for (int32 Idx = 0; Idx < AllRemovalsArray.Num(); ++Idx)
|
|
{
|
|
IPhysicsProxyBase* Proxy = AllRemovalsArray[Idx].Proxy;
|
|
if (!Proxy->GetMarkedDeleted())
|
|
{
|
|
FRemovalData RemovalData;
|
|
RemovalData.Location = AllRemovalsArray[Idx].Location;
|
|
RemovalData.Mass = AllRemovalsArray[Idx].Mass;
|
|
RemovalData.Proxy = Proxy;
|
|
RemovalData.BoundingBox = AllRemovalsArray[Idx].BoundingBox;
|
|
|
|
const int32 NewIdx = AllRemovalDataArray.Add(RemovalData);
|
|
AllRemovalIndicesByPhysicsProxy.FindOrAdd(Proxy).Add(FEventManager::EncodeCollisionIndex(NewIdx, false));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
void FEventDefaults::RegisterCrumblingEvent(FEventManager& EventManager)
|
|
{
|
|
EventManager.template RegisterEvent<FCrumblingEventData>(EEventType::Crumbling, []
|
|
(const Chaos::FPBDRigidsSolver* Solver, FCrumblingEventData& CrumblingEventData, bool bResetData)
|
|
{
|
|
check(Solver);
|
|
|
|
EnsureIsInPhysicsThreadContext();
|
|
|
|
SCOPE_CYCLE_COUNTER(STAT_GatherBreakingEvent);
|
|
|
|
// @todo(chaos) : should we check for a way to globally stop this from being processed ?
|
|
|
|
if (bResetData)
|
|
{
|
|
CrumblingEventData.Reset();
|
|
}
|
|
CrumblingEventData.SetTimeCreated(Solver->MTime);
|
|
|
|
const TArray<FCrumblingData>& AllCrumblingsArray = Solver->GetEvolution()->GetRigidClustering().GetAllClusterCrumblings();
|
|
|
|
CrumblingEventData.Reserve(AllCrumblingsArray.Num());
|
|
for (const FCrumblingData& Crumbling: AllCrumblingsArray)
|
|
{
|
|
// todo(chaos) : implement filtering only if necessary as crumbling event should be sparser than breaking ones
|
|
CrumblingEventData.AddCrumbling(Crumbling);
|
|
CrumblingEventData.CrumblingData.bHasGlobalEvent |= (Crumbling.EmitterFlag & EventEmitterFlag::GlobalDispatcher) != 0;
|
|
}
|
|
});
|
|
}
|
|
}
|