Files
2025-05-18 13:04:45 +08:00

547 lines
17 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ChaosBlueprint.h"
#include "PhysicsSolver.h"
#include "Async/Async.h"
#include "Engine/World.h"
#include "PhysicsProxy/GeometryCollectionPhysicsProxy.h"
#include "GeometryCollection/GeometryCollectionActor.h"
#include "GeometryCollection/GeometryCollectionComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(ChaosBlueprint)
#define DISPATCH_BLUEPRINTS_IMMEDIATE 1
UChaosDestructionListener::UChaosDestructionListener(FObjectInitializer const& ObjectInitializer)
: Super(ObjectInitializer), LastCollisionDataTimeStamp(-1.f), LastBreakingDataTimeStamp(-1.f), LastTrailingDataTimeStamp(-1.f), LastRemovalDataTimeStamp(-1.f)
{
bUseAttachParentBound = true;
bAutoActivate = true;
bNeverNeedsRenderUpdate = true;
#ifdef DISPATCH_BLUEPRINTS_IMMEDIATE
PrimaryComponentTick.bCanEverTick = false;
#else
PrimaryComponentTick.bCanEverTick = true;
#endif
SetCollisionFilter(MakeShareable(new FChaosCollisionEventFilter(&CollisionEventRequestSettings)));
SetBreakingFilter(MakeShareable(new FChaosBreakingEventFilter(&BreakingEventRequestSettings)));
SetTrailingFilter(MakeShareable(new FChaosTrailingEventFilter(&TrailingEventRequestSettings)));
SetRemovalFilter(MakeShareable(new FChaosRemovalEventFilter(&RemovalEventRequestSettings)));
}
void UChaosDestructionListener::ClearEvents()
{
#if 0 // solver actors no longer functional, using GetWorld()->GetPhysicsScene() instead
for (AChaosSolverActor* ChaosSolverActorObject : ChaosSolverActors)
{
UnregisterChaosEvents(ChaosSolverActorObject->GetPhysicsScene());
}
#else
UnregisterChaosEvents(GetWorld()->GetPhysicsScene());
#endif
}
void UChaosDestructionListener::UpdateEvents()
{
if (!ChaosSolverActors.Num() && !GeometryCollectionActors.Num())
{
if (FPhysScene* PhysScene = GetWorld()->GetPhysicsScene())
{
RegisterChaosEvents(PhysScene);
}
}
else
{
#if 0 // solver actors no longer functional, using GetWorld()->GetPhysicsScene() instead
for (AChaosSolverActor* ChaosSolverActorObject : ChaosSolverActors)
{
if (ChaosSolverActorObject)
{
RegisterChaosEvents(ChaosSolverActorObject->GetPhysicsScene());
}
}
#else
if (FPhysScene* PhysScene = GetWorld()->GetPhysicsScene())
{
RegisterChaosEvents(PhysScene);
}
#endif
for (AGeometryCollectionActor* GeometryCollectionActor : GeometryCollectionActors)
{
if (GeometryCollectionActor)
{
if (const UGeometryCollectionComponent* GeometryCollectionComponent = GeometryCollectionActor->GetGeometryCollectionComponent())
{
if (const FGeometryCollectionPhysicsProxy* GeometryCollectionPhysicsObject = GeometryCollectionComponent->GetPhysicsProxy())
{
RegisterChaosEvents(GeometryCollectionComponent->GetWorld()->GetPhysicsScene());
}
}
}
}
}
}
#if 0 // #todo: No longer required?
void UChaosDestructionListener::UpdateGeometryCollectionPhysicsProxies()
{
GeometryCollectionPhysicsProxies.Reset();
if (GeometryCollectionActors.Num() > 0)
{
for (AGeometryCollectionActor* GeometryCollectionActorObject : GeometryCollectionActors)
{
if (GeometryCollectionActorObject)
{
// Get GeometryCollectionComponent
if (const UGeometryCollectionComponent* GeometryCollectionComponent = GeometryCollectionActorObject->GetGeometryCollectionComponent())
{
// Get GeometryCollectionPhysicsProxies
if (const FGeometryCollectionPhysicsProxy* GeometryCollectionPhysicsProxy = GeometryCollectionComponent->GetPhysicsProxy())
{
if (Chaos::FPhysicsSolver* Solver = GeometryCollectionPhysicsProxy->GetSolver())
{
if (!Solvers.Contains(Solver))
{
GeometryCollectionPhysicsProxies.Add(GeometryCollectionPhysicsProxy);
}
}
}
}
}
}
}
}
#endif
#if WITH_EDITOR
void UChaosDestructionListener::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
}
#endif
bool UChaosDestructionListener::IsEventListening() const
{
return bIsCollisionEventListeningEnabled ||
bIsBreakingEventListeningEnabled ||
bIsTrailingEventListeningEnabled ||
bIsRemovalEventListeningEnabled;
}
void UChaosDestructionListener::UpdateTransformSettings()
{
// Only need to update the transform if anybody is listening at all and if any of the settings are sorting by nearest, otherwise, no need to get updates
if (IsEventListening())
{
bWantsOnUpdateTransform = CollisionEventRequestSettings.SortMethod == EChaosCollisionSortMethod::SortByNearestFirst ||
BreakingEventRequestSettings.SortMethod == EChaosBreakingSortMethod::SortByNearestFirst ||
TrailingEventRequestSettings.SortMethod == EChaosTrailingSortMethod::SortByNearestFirst ||
RemovalEventRequestSettings.SortMethod == EChaosRemovalSortMethod::SortByNearestFirst;
}
else
{
bWantsOnUpdateTransform = false;
}
bChanged = true;
}
void UChaosDestructionListener::BeginPlay()
{
Super::BeginPlay();
UpdateEvents();
}
void UChaosDestructionListener::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
ClearEvents();
Super::EndPlay(EndPlayReason);
}
void UChaosDestructionListener::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
#if 0
bool bIsListening = IsEventListening();
// if owning actor is disabled, don't listen
AActor* Owner = GetOwner();
if (Owner && !Owner->IsActorTickEnabled())
{
bIsListening = false;
}
// If we have a task and it isn't finished, let it do it's thing
int32 TaskStateValue = TaskState.GetValue();
if (TaskStateValue == (int32)ETaskState::Processing)
{
return;
}
// Note this could be "NoTask" if this is the first tick or if the event listener has been stopped
else if (TaskStateValue == (int32)ETaskState::Finished)
{
// Notify the callbacks with the filtered destruction data results if they're being listened to
// If the data was changed during the task, then bChanged will be true and we will avoid broadcasting this frame since it won't be valid.
if (bIsListening && !bChanged)
{
if (ChaosCollisionFilter.IsValid())
{
if (bIsCollisionEventListeningEnabled && ChaosCollisionFilter->GetNumEvents() > 0 && OnCollisionEvents.IsBound())
{
OnCollisionEvents.Broadcast(ChaosCollisionFilter->GetFilteredResults());
}
}
if (ChaosBreakingFilter.IsValid())
{
if (bIsBreakingEventListeningEnabled && ChaosBreakingFilter->GetNumEvents() > 0 && OnBreakingEvents.IsBound())
{
OnBreakingEvents.Broadcast(ChaosBreakingFilter->GetFilteredResults());
}
}
if (ChaosTrailingFilter.IsValid())
{
if (bIsTrailingEventListeningEnabled && ChaosTrailingFilter->GetNumEvents() > 0 && OnTrailingEvents.IsBound())
{
OnTrailingEvents.Broadcast(ChaosTrailingFilter->GetFilteredResults());
}
}
}
else
{
TaskState.Set((int32)ETaskState::NoTask);
}
// Reset the changed bool so we can broadcast next tick if the settings haven't changed
bChanged = false;
}
// Early exit if we're not listening anymore
if (!bIsListening)
{
return;
}
// If we don't have solvers, call update to make sure we have built our solver array
if (!Solvers.Num())
{
UpdateSolvers();
}
if (!GeometryCollectionPhysicsProxies.Num())
{
UpdateGeometryCollectionPhysicsProxies();
}
// Reset our cached data arrays for various destruction types
RawCollisionDataArray.Reset();
RawBreakingDataArray.Reset();
RawTrailingDataArray.Reset();
// Retrieve the raw data arrays from the solvers
GetDataFromSolvers();
// Retrieve the raw data arrays from the GeometryCollectionPhysicsProxy
GetDataFromGeometryCollectionPhysicsProxies();
TaskState.Set((int32)ETaskState::Processing);
// Retreive a copy of the transform before kicking off the task
ChaosComponentTransform = GetComponentTransform();
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask,
[this]()
{
if (bIsCollisionEventListeningEnabled)
{
if (ChaosCollisionFilter.IsValid())
{
ChaosCollisionFilter->FilterEvents(ChaosComponentTransform, RawCollisionDataArray);
}
}
if (ChaosBreakingFilter.IsValid())
{
if (bIsBreakingEventListeningEnabled)
{
ChaosBreakingFilter->FilterEvents(ChaosComponentTransform, RawBreakingDataArray);
}
}
if (ChaosTrailingFilter.IsValid())
{
if (bIsTrailingEventListeningEnabled)
{
ChaosTrailingFilter->FilterEvents(ChaosComponentTransform, RawTrailingDataArray);
}
}
TaskState.Set((int32)ETaskState::Finished);
});
#endif // if 0
}
void UChaosDestructionListener::AddChaosSolverActor(AChaosSolverActor* ChaosSolverActor)
{
#if 0 // solver actors no longer functional, using GetWorld()->GetPhysicsScene() instead
if (ChaosSolverActor && !ChaosSolverActors.Contains(ChaosSolverActor))
{
ChaosSolverActors.Add(ChaosSolverActor);
RegisterChaosEvents(ChaosSolverActor->GetPhysicsScene());
}
#endif
}
void UChaosDestructionListener::RemoveChaosSolverActor(AChaosSolverActor* ChaosSolverActor)
{
#if 0 // solver actors no longer functional, using GetWorld()->GetPhysicsScene() instead
if (ChaosSolverActor)
{
ClearEvents();
ChaosSolverActors.Remove(ChaosSolverActor);
UpdateEvents();
}
#endif
}
void UChaosDestructionListener::AddGeometryCollectionActor(AGeometryCollectionActor* GeometryCollectionActor)
{
if (GeometryCollectionActor && !GeometryCollectionActors.Contains(GeometryCollectionActor))
{
GeometryCollectionActors.Add(GeometryCollectionActor);
if (const UGeometryCollectionComponent* GeometryCollectionComponent = GeometryCollectionActor->GetGeometryCollectionComponent())
{
if (const FGeometryCollectionPhysicsProxy* GeometryCollectionPhysicsObject = GeometryCollectionComponent->GetPhysicsProxy())
{
RegisterChaosEvents(GeometryCollectionComponent->GetWorld()->GetPhysicsScene());
}
}
}
}
void UChaosDestructionListener::RemoveGeometryCollectionActor(AGeometryCollectionActor* GeometryCollectionActor)
{
if (GeometryCollectionActor)
{
ClearEvents();
GeometryCollectionActors.Remove(GeometryCollectionActor);
UpdateEvents();
}
}
void UChaosDestructionListener::SetCollisionEventRequestSettings(const FChaosCollisionEventRequestSettings& InSettings)
{
CollisionEventRequestSettings = InSettings;
UpdateTransformSettings();
}
void UChaosDestructionListener::SetBreakingEventRequestSettings(const FChaosBreakingEventRequestSettings& InSettings)
{
BreakingEventRequestSettings = InSettings;
UpdateTransformSettings();
}
void UChaosDestructionListener::SetTrailingEventRequestSettings(const FChaosTrailingEventRequestSettings& InSettings)
{
TrailingEventRequestSettings = InSettings;
UpdateTransformSettings();
}
void UChaosDestructionListener::SetRemovalEventRequestSettings(const FChaosRemovalEventRequestSettings& InSettings)
{
RemovalEventRequestSettings = InSettings;
UpdateTransformSettings();
}
void UChaosDestructionListener::SetCollisionEventEnabled(bool bIsEnabled)
{
bIsCollisionEventListeningEnabled = bIsEnabled;
UpdateTransformSettings();
}
void UChaosDestructionListener::SetBreakingEventEnabled(bool bIsEnabled)
{
bIsBreakingEventListeningEnabled = bIsEnabled;
UpdateTransformSettings();
}
void UChaosDestructionListener::SetTrailingEventEnabled(bool bIsEnabled)
{
bIsTrailingEventListeningEnabled = bIsEnabled;
UpdateTransformSettings();
}
void UChaosDestructionListener::SetRemovalEventEnabled(bool bIsEnabled)
{
bIsRemovalEventListeningEnabled = bIsEnabled;
UpdateTransformSettings();
}
void UChaosDestructionListener::SortCollisionEvents(TArray<FChaosCollisionEventData>& CollisionEvents, EChaosCollisionSortMethod SortMethod)
{
if (ChaosCollisionFilter.IsValid())
{
ChaosCollisionFilter->SortEvents(CollisionEvents, SortMethod, GetComponentTransform());
}
}
void UChaosDestructionListener::SortBreakingEvents(TArray<FChaosBreakingEventData>& BreakingEvents, EChaosBreakingSortMethod SortMethod)
{
if (ChaosBreakingFilter.IsValid())
{
ChaosBreakingFilter->SortEvents(BreakingEvents, SortMethod, GetComponentTransform());
}
}
void UChaosDestructionListener::SortTrailingEvents(TArray<FChaosTrailingEventData>& TrailingEvents, EChaosTrailingSortMethod SortMethod)
{
if (ChaosTrailingFilter.IsValid())
{
ChaosTrailingFilter->SortEvents(TrailingEvents, SortMethod, GetComponentTransform());
}
}
void UChaosDestructionListener::SortRemovalEvents(TArray<FChaosRemovalEventData>& RemovalEvents, EChaosRemovalSortMethod SortMethod)
{
if (ChaosRemovalFilter.IsValid())
{
ChaosRemovalFilter->SortEvents(RemovalEvents, SortMethod, GetComponentTransform());
}
}
void UChaosDestructionListener::RegisterChaosEvents(FPhysScene* Scene)
{
Chaos::FPhysicsSolver* Solver = Scene->GetSolver();
Chaos::FEventManager* EventManager = Solver->GetEventManager();
EventManager->RegisterHandler<Chaos::FCollisionEventData>(Chaos::EEventType::Collision, this, &UChaosDestructionListener::HandleCollisionEvents);
EventManager->RegisterHandler<Chaos::FBreakingEventData>(Chaos::EEventType::Breaking, this, &UChaosDestructionListener::HandleBreakingEvents);
EventManager->RegisterHandler<Chaos::FTrailingEventData>(Chaos::EEventType::Trailing, this, &UChaosDestructionListener::HandleTrailingEvents);
EventManager->RegisterHandler<Chaos::FRemovalEventData>(Chaos::EEventType::Removal, this, &UChaosDestructionListener::HandleRemovalEvents);
}
void UChaosDestructionListener::UnregisterChaosEvents(FPhysScene* Scene)
{
Chaos::FPhysicsSolver* Solver = Scene->GetSolver();
Chaos::FEventManager* EventManager = Solver->GetEventManager();
EventManager->UnregisterHandler(Chaos::EEventType::Collision, this);
EventManager->UnregisterHandler(Chaos::EEventType::Breaking, this);
EventManager->UnregisterHandler(Chaos::EEventType::Trailing, this);
EventManager->UnregisterHandler(Chaos::EEventType::Removal, this);
}
void UChaosDestructionListener::RegisterChaosEvents(TSharedPtr<FPhysScene_Chaos> Scene)
{
Chaos::FPhysicsSolver* Solver = Scene->GetSolver();
Chaos::FEventManager* EventManager = Solver->GetEventManager();
EventManager->RegisterHandler<Chaos::FCollisionEventData>(Chaos::EEventType::Collision, this, &UChaosDestructionListener::HandleCollisionEvents);
EventManager->RegisterHandler<Chaos::FBreakingEventData>(Chaos::EEventType::Breaking, this, &UChaosDestructionListener::HandleBreakingEvents);
EventManager->RegisterHandler<Chaos::FTrailingEventData>(Chaos::EEventType::Trailing, this, &UChaosDestructionListener::HandleTrailingEvents);
EventManager->RegisterHandler<Chaos::FRemovalEventData>(Chaos::EEventType::Removal, this, &UChaosDestructionListener::HandleRemovalEvents);
}
void UChaosDestructionListener::UnregisterChaosEvents(TSharedPtr<FPhysScene_Chaos> Scene)
{
Chaos::FPhysicsSolver* Solver = Scene->GetSolver();
Chaos::FEventManager* EventManager = Solver->GetEventManager();
EventManager->UnregisterHandler(Chaos::EEventType::Collision, this);
EventManager->UnregisterHandler(Chaos::EEventType::Breaking, this);
EventManager->UnregisterHandler(Chaos::EEventType::Trailing, this);
EventManager->UnregisterHandler(Chaos::EEventType::Removal, this);
}
void UChaosDestructionListener::HandleCollisionEvents(const Chaos::FCollisionEventData& Event)
{
if (bIsCollisionEventListeningEnabled)
{
int NumCollisions = Event.CollisionData.AllCollisionsArray.Num();
RawCollisionDataArray.Append(Event.CollisionData.AllCollisionsArray.GetData(), NumCollisions);
#if DISPATCH_BLUEPRINTS_IMMEDIATE
if (ChaosCollisionFilter.IsValid())
{
ChaosCollisionFilter->FilterEvents(ChaosComponentTransform, RawCollisionDataArray);
if (ChaosCollisionFilter->GetNumEvents() > 0 && OnCollisionEvents.IsBound())
{
OnCollisionEvents.Broadcast(ChaosCollisionFilter->GetFilteredResults());
}
}
RawCollisionDataArray.Reset();
#endif
}
}
void UChaosDestructionListener::HandleBreakingEvents(const Chaos::FBreakingEventData& Event)
{
if (bIsBreakingEventListeningEnabled)
{
int NumBreakings = Event.BreakingData.AllBreakingsArray.Num();
RawBreakingDataArray.Append(Event.BreakingData.AllBreakingsArray.GetData(), NumBreakings);
#if DISPATCH_BLUEPRINTS_IMMEDIATE
if (ChaosBreakingFilter.IsValid())
{
ChaosBreakingFilter->FilterEvents(ChaosComponentTransform, RawBreakingDataArray);
if (ChaosBreakingFilter->GetNumEvents() > 0 && OnBreakingEvents.IsBound())
{
OnBreakingEvents.Broadcast(ChaosBreakingFilter->GetFilteredResults());
}
}
RawBreakingDataArray.Reset();
#endif
}
}
void UChaosDestructionListener::HandleTrailingEvents(const Chaos::FTrailingEventData& Event)
{
if (bIsTrailingEventListeningEnabled)
{
int NumTrailings = Event.TrailingData.AllTrailingsArray.Num();
RawTrailingDataArray.Append(Event.TrailingData.AllTrailingsArray.GetData(), NumTrailings);
#if DISPATCH_BLUEPRINTS_IMMEDIATE
if (ChaosTrailingFilter.IsValid())
{
ChaosTrailingFilter->FilterEvents(ChaosComponentTransform, RawTrailingDataArray);
if (ChaosTrailingFilter->GetNumEvents() > 0 && OnTrailingEvents.IsBound())
{
OnTrailingEvents.Broadcast(ChaosTrailingFilter->GetFilteredResults());
}
}
RawTrailingDataArray.Reset();
#endif
}
}
void UChaosDestructionListener::HandleRemovalEvents(const Chaos::FRemovalEventData& Event)
{
if (bIsRemovalEventListeningEnabled)
{
int NumRemovals = Event.RemovalData.AllRemovalArray.Num();
RawRemovalDataArray.Append(Event.RemovalData.AllRemovalArray.GetData(), NumRemovals);
#if DISPATCH_BLUEPRINTS_IMMEDIATE
if (ChaosRemovalFilter.IsValid())
{
ChaosRemovalFilter->FilterEvents(ChaosComponentTransform, RawRemovalDataArray);
if (ChaosRemovalFilter->GetNumEvents() > 0 && OnRemovalEvents.IsBound())
{
OnRemovalEvents.Broadcast(ChaosRemovalFilter->GetFilteredResults());
}
}
RawRemovalDataArray.Reset();
#endif
}
}