Files
Ben Marsh 93865ecfb8 More IncludeTool fixes.
#rb none
#jira
#rnx

[CL 8371085 by Ben Marsh in 4.23 branch]
2019-08-29 08:29:33 -04:00

834 lines
32 KiB
C++

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#include "GeometryCollection/GeometryCollectionSQAccelerator.h"
#include "Physics/Experimental/PhysScene_Chaos.h"
#include "Chaos/Box.h"
#include "Chaos/Capsule.h"
#include "Chaos/ImplicitObjectTransformed.h"
#include "Chaos/Sphere.h"
#include "Chaos/PBDRigidParticles.h"
#include "GeometryCollection/ManagedArray.h"
#include "GeometryCollection/GeometryCollectionComponent.h"
#include "GeometryCollection/GeometryCollectionActor.h"
#include "Components/BoxComponent.h"
#include "ChaosSolversModule.h"
#include "ChaosStats.h"
#include "PBDRigidsSolver.h"
#include "SolverObjects/GeometryCollectionPhysicsObject.h"
#if INCLUDE_CHAOS && !WITH_CHAOS_NEEDS_TO_BE_FIXED
DECLARE_CYCLE_STAT(TEXT("LowLevelSweep"), STAT_LowLevelSweep, STATGROUP_Chaos);
DECLARE_CYCLE_STAT(TEXT("LowLevelRaycast"), STAT_LowLevelRaycast, STATGROUP_Chaos);
DECLARE_CYCLE_STAT(TEXT("LowLevelOverlap"), STAT_LowLevelOverlap, STATGROUP_Chaos);
bool IsValidIndexAndTransform(const FGeometryCollectionResults& PhysResult, const Chaos::TPBDRigidParticles<float, 3>& Particles, const TManagedArray<FTransform>& TransformArray, const TArray<bool>& DisabledFlags, const int32 RigidBodyIdx, const bool bCanBeDisabled)
{
if(RigidBodyIdx == -1)
{
//todo(ocohen): managed to avoid this invalid index, but need to investigate a bit more into whether we can always assume it's valid
return false;
}
if (PhysResult.BaseIndex == -1)
{
//todo(mlentine): Why is this possible?
return false;
}
const int32 LocalBodyIndex = RigidBodyIdx - PhysResult.BaseIndex;
if(LocalBodyIndex < 0 || LocalBodyIndex >= PhysResult.NumParticlesAdded)
{
// Ignore collisions for other components - need to make this even faster (subset of potential intersections [maybe array view?])
return false;
}
const FTransform& CurrentTransform = TransformArray[LocalBodyIndex];
const FVector CurrentTranslation = CurrentTransform.GetTranslation();
//@todo(mlentine): In theory this is no longer needed
if (!bCanBeDisabled && DisabledFlags[LocalBodyIndex])
{
//disabled particles can actually have stale geometry in them and are clearly not useful anyway
return false;
}
if (static_cast<uint32>(RigidBodyIdx) >= Particles.Size())
{
// @todo(mlentine): Is this a possible situation?
return false;
}
if (!(ensure(!FMath::IsNaN(CurrentTranslation[0])) && ensure(!FMath::IsNaN(CurrentTranslation[1])) && ensure(!FMath::IsNaN(CurrentTranslation[2]))))
{
return false;
}
return true;
}
bool LowLevelRaycastSingleElement(int32 InParticleIndex, const Chaos::FPBDRigidsSolver* InSolver, const Chaos::TClusterBuffer<float, 3>& ClusterBuffer, const FGeometryCollectionPhysicsObject* InObject, const FVector& Start, const FVector& Dir, float DeltaMag, bool bCanBeDisabled, EHitFlags OutputFlags, FHitRaycast& OutHit)
{
using namespace Chaos;
// Preconditions from Raycast - shouldn't get in here without valid case
checkSlow(InSolver);
checkSlow(InObject);
const FGeometryCollectionResults& PhysResult = InObject->GetPhysicsResults().GetGameDataForRead();
const TManagedArray<int32>& RigidBodyIdArray = PhysResult.RigidBodyIds;
const TManagedArray<FTransform>& TransformArray = PhysResult.Transforms;
const TArray<bool>& DisabledFlags = PhysResult.DisabledStates;
{
const TPBDRigidParticles<float, 3>& Particles = InSolver->GetRigidParticles();
if(!IsValidIndexAndTransform(PhysResult, Particles, TransformArray, DisabledFlags, InParticleIndex, bCanBeDisabled))
{
return false;
}
const int32 LocalBodyIndex = InParticleIndex - PhysResult.BaseIndex;
const TRigidTransform<float, 3>& TM = PhysResult.ParticleToWorldTransforms[LocalBodyIndex];
if (!(ensure(!FMath::IsNaN(TM.GetTranslation().X)) && ensure(!FMath::IsNaN(TM.GetTranslation().Y)) && ensure(!FMath::IsNaN(TM.GetTranslation().Z))))
{
return false;
}
const TVector<float, 3> StartLocal = TM.InverseTransformPositionNoScale(Start);
const TVector<float, 3> DirLocal = TM.InverseTransformVectorNoScale(Dir);
const TVector<float, 3> EndLocal = StartLocal + DirLocal * DeltaMag;
const TImplicitObject<float, 3>* Object = ClusterBuffer.GeometryPtrs[InParticleIndex].Get(); //todo(ocohen): can this ever be null?
if (!Object)
{
return false;
}
Pair<TVector<float, 3>, bool> Result = Object->FindClosestIntersection(StartLocal, EndLocal, /*Thickness=*/0.f);
if(Result.Second) //todo(ocohen): once we do more than just a bool we need to get the closest point
{
#if WITH_PHYSX
//todo(ocohen): check output flags?
const float Distance = (Result.First - StartLocal).Size();
if(OutHit.distance == PX_MAX_REAL || Distance < OutHit.distance)
{
OutHit.distance = Distance; //todo(ocohen): assuming physx structs for now
OutHit.position = U2PVector(TM.TransformPositionNoScale(Result.First));
const TVector<float, 3> LocalNormal = Object->Normal(Result.First);
OutHit.normal = U2PVector(TM.TransformVectorNoScale(LocalNormal));
SetFlags(OutHit, EHitFlags::Distance | EHitFlags::Normal | EHitFlags::Position);
}
return true;
#endif
}
}
return false;
}
static TAutoConsoleVariable<int32> CVarMaxSweepSteps(
TEXT("p.MaxSweepSteps"),
3,
TEXT("Number of steps during a sweep"),
ECVF_Default);
bool LowLevelSweepSingleElement(int32 InParticleIndex, const Chaos::FPBDRigidsSolver* InSolver, const Chaos::TClusterBuffer<float, 3>& ClusterBuffer, const FGeometryCollectionPhysicsObject* InObject, const Chaos::TImplicitObject<float, 3>& QueryGeom, const Chaos::TParticles<float, 3>& CollisionParticles, const FTransform& StartPose, const FVector& Dir, float DeltaMag, const bool bCanBeDisabled, FHitSweep& OutHit)
{
using namespace Chaos;
checkSlow(InSolver);
checkSlow(InObject);
const FGeometryCollectionResults& PhysResult = InObject->GetPhysicsResults().GetGameDataForRead();
const TManagedArray<int32>& RigidBodyIdArray = PhysResult.RigidBodyIds;
const TManagedArray<FTransform>& TransformArray = PhysResult.Transforms;
const TArray<bool>& DisabledFlags = PhysResult.DisabledStates;
const TPBDRigidParticles<float, 3>& Particles = InSolver->GetRigidParticles();
if(!IsValidIndexAndTransform(PhysResult, Particles, TransformArray, DisabledFlags, InParticleIndex, bCanBeDisabled))
{
return false;
}
const int32 LocalBodyIndex = InParticleIndex - PhysResult.BaseIndex;
const TRigidTransform<float, 3>& TM = PhysResult.ParticleToWorldTransforms[LocalBodyIndex];
const TImplicitObject<float, 3>* Object = ClusterBuffer.GeometryPtrs[InParticleIndex].Get();
if (!Object)
{
return false;
}
const TVector<float, 3> DirLocal = TM.InverseTransformVectorNoScale(Dir);
bool bFound = false;
for(uint32 i = 0; i < CollisionParticles.Size(); ++i)
{
const TVector<float, 3> StartLocal = TM.InverseTransformPositionNoScale(StartPose.TransformPositionNoScale(CollisionParticles.X(i)));
const TVector<float, 3> EndLocal = StartLocal + DirLocal * DeltaMag;
Pair<TVector<float, 3>, bool> Result = Object->FindClosestIntersection(StartLocal, EndLocal, /*Thickness=*/0.f);
if(Result.Second)
{
#if WITH_PHYSX
const float Distance = (Result.First - StartLocal).Size();
if(!bFound || Distance < OutHit.distance)
{
OutHit.distance = Distance; //todo(ocohen): assuming physx structs for now
OutHit.position = U2PVector(TM.TransformPositionNoScale(Result.First));
const TVector<float, 3> LocalNormal = Object->Normal(Result.First);
OutHit.normal = U2PVector(TM.TransformVectorNoScale(LocalNormal));
SetFlags(OutHit, EHitFlags::Distance | EHitFlags::Normal | EHitFlags::Position);
}
bFound = true;
#endif
}
}
return bFound;
}
bool LowLevelOverlap(const UGeometryCollectionComponent& GeomCollectionComponent, const TArray<int32>& InPotentialIntersections, const Chaos::TClusterBuffer<float, 3>& ClusterBuffer, const Chaos::TImplicitObject<float, 3>& QueryGeom, const FTransform& GeomPose, FHitOverlap& OutHit)
{
using namespace Chaos;
SCOPE_CYCLE_COUNTER(STAT_LowLevelOverlap);
const FGeometryCollectionPhysicsObject* PhysObject = GeomCollectionComponent.GetPhysicsObject();
if (!ensure(PhysObject))
{
return false;
}
const FGeometryCollectionResults& PhysResult = PhysObject->GetPhysicsResults().GetGameDataForRead();
const TManagedArray<int32>& RigidBodyIdArray = PhysResult.RigidBodyIds;
const TManagedArray<FTransform>& TransformArray = PhysResult.Transforms;
const TArray<bool>& DisabledFlags = PhysResult.DisabledStates;
bool bFound = false;
if (Chaos::FPBDRigidsSolver* Solver = GeomCollectionComponent.ChaosSolverActor != nullptr ? GeomCollectionComponent.ChaosSolverActor->GetSolver() : GeomCollectionComponent.GetOwner()->GetWorld()->PhysicsScene_Chaos->GetSolver())
{
const TPBDRigidParticles<float, 3>& Particles = Solver->GetRigidParticles(); //todo(ocohen): should these just get passed in instead of hopping through scene?
check(QueryGeom.HasBoundingBox()); // We do not support unbounded query objects
PhysicsParallelFor(InPotentialIntersections.Num(), [&](int32 PotentialIdx)
{
int32 RigidBodyIdx = InPotentialIntersections[PotentialIdx];
if (!IsValidIndexAndTransform(PhysResult, Particles, TransformArray, DisabledFlags, RigidBodyIdx, false))
{
return;
}
const int32 LocalBodyIndex = RigidBodyIdx - PhysResult.BaseIndex;
const TRigidTransform<float, 3>& TM = PhysResult.ParticleToWorldTransforms[LocalBodyIndex];
const TImplicitObject<float, 3>* Object = ClusterBuffer.GeometryPtrs[RigidBodyIdx].Get();
if (!Object)
{
return;
}
// Need to do narrow phase
Pair<TVector<float, 3>, bool> Result = QueryGeom.FindDeepestIntersection(Object, Particles.CollisionParticles(RigidBodyIdx).Get(), TRigidTransform<float, 3>(TM) * TRigidTransform<float, 3>(GeomPose).Inverse(), 0);
if (Result.Second)
{
bFound = true;
}
});
}
return bFound;
}
int32 UseSlowSQ = 0;
FAutoConsoleVariableRef CVarUseSlowSQ(TEXT("p.UseSlowSQ"), UseSlowSQ, TEXT(""));
void FGeometryCollectionSQAccelerator::Raycast(const FVector& Start, const FVector& Dir, const float DeltaMagnitude, FPhysicsHitCallback<FHitRaycast>& HitBuffer, EHitFlags OutputFlags, FQueryFlags QueryFlags, const FCollisionFilterData& QueryFilter, const FQueryFilterData& QueryFilterData, ICollisionQueryFilterCallbackBase& QueryCallback) const
{
SCOPE_CYCLE_COUNTER(STAT_GCRaycast);
FChaosScopeSolverLock SolverScopeLock;
using namespace Chaos;
FChaosSolversModule* Module = FChaosSolversModule::GetModule();
TMap<const Chaos::FPBDRigidsSolver*, TArray<int32>> SolverIntersectionSets;
Chaos::TSpatialRay<float, 3> Ray(Start, Start + Dir * DeltaMagnitude);
#if WITH_PHYSX
const TArray<Chaos::FPBDRigidsSolver*>& Solvers = Module->GetSolvers();
for(const Chaos::FPBDRigidsSolver* Solver : Solvers)
{
if (!Solver)
{
continue;
}
TArray<int32> IntersectionSet = Solver->GetSpatialAcceleration()->FindAllIntersections(Ray);
Solver->ReleaseSpatialAcceleration();
const Chaos::TClusterBuffer<float, 3>& Buffer = Solver->GetRigidClustering().GetBufferedData();
const Chaos::FPBDRigidsSolver::FSolverObjectReverseMapping& ObjectMap = Solver->GetSolverObjectReverseMapping_GameThread();
FHitRaycast Hit;
int32 IntersectionSetSize = IntersectionSet.Num();
for(int32 i = 0; i < IntersectionSet.Num(); ++i)
{
const int32 IntersectParticleIndex = IntersectionSet[i];
const SolverObjectWrapper& ObjectWrapper = ObjectMap.SolverObjectReverseMappingArray[IntersectParticleIndex];
if (!ObjectWrapper.SolverObject)
{
const TImplicitObject<float, 3>* Object = Buffer.GeometryPtrs[IntersectParticleIndex].Get();
// Ignore the ground plane
if (IntersectParticleIndex == 0 && Object->GetType(true) == ImplicitObjectType::Plane)
{
continue;
}
if (Object && !UseSlowSQ && Object->IsUnderlyingUnion())
{
const TImplicitObjectUnion<float, 3>* Union = static_cast<const TImplicitObjectUnion<float, 3>*>(Object);
//hack: this is terrible because we have no buffered transform so could be off, but most of the time these things are static
{
const TRigidTransform<float, 3>* TMPtr = Buffer.ClusterParentTransforms.Find(IntersectParticleIndex);
if (ensure(TMPtr))
{
if (!(ensure(!FMath::IsNaN(TMPtr->GetTranslation().X)) && ensure(!FMath::IsNaN(TMPtr->GetTranslation().Y)) && ensure(!FMath::IsNaN(TMPtr->GetTranslation().Z))))
{
continue;
}
const TVector<float, 3> StartLocal = TMPtr->InverseTransformPositionNoScale(Start);
const TVector<float, 3> DirLocal = TMPtr->InverseTransformVectorNoScale(Dir);
const TVector<float, 3> EndLocal = StartLocal + DirLocal * DeltaMagnitude;
Chaos::TSpatialRay<float, 3> LocalRay(StartLocal, EndLocal);
const TArray<int32> IntersectingChildren = Union->FindAllIntersectingChildren(LocalRay);
IntersectionSet.Append(IntersectingChildren);
}
else
{
UE_LOG(LogChaos, Warning, TEXT("SQ: Could not find a valid transform for a cluster parent for faster child intersections."));
}
}
}
else
{
if (ensure(Buffer.MChildren.Contains(IntersectParticleIndex)))
{
const TArray<uint32>& Children = Buffer.MChildren[IntersectParticleIndex];
for (const uint32 Child : Children)
{
IntersectionSet.Add(Child);
}
}
}
continue;
}
if(ObjectWrapper.Type == ESolverObjectType::GeometryCollectionType && ensure(ObjectWrapper.SolverObject))
{
LowLevelRaycastSingleElement(IntersectParticleIndex, Solver, Buffer, static_cast<FGeometryCollectionPhysicsObject*>(ObjectWrapper.SolverObject), Start, Dir, DeltaMagnitude, i >= IntersectionSetSize, OutputFlags, Hit);
// If we registered a hit
if(Hit.distance != PX_MAX_REAL && ensure(ObjectWrapper.SolverObject))
{
#if !WITH_IMMEDIATE_PHYSX && PHYSICS_INTERFACE_PHYSX
//todo(ocohen):hack placeholder while we convert over to non physx API
UGeometryCollectionComponent* Component = Cast<UGeometryCollectionComponent>(ObjectWrapper.SolverObject->GetOwner());
check(Component);
if (Component->IsRegistered())
{
const FPhysicsActorHandle& ActorHandle = Component->DummyBodyInstance.GetPhysicsActorHandle();
PxRigidActor* PRigidActor = ActorHandle.SyncActor;
uint32 PNumShapes = PRigidActor->getNbShapes();
TArray<PxShape*> PShapes;
PShapes.AddZeroed(PNumShapes);
PRigidActor->getShapes(PShapes.GetData(), sizeof(PShapes[0]) * PNumShapes);
SetActor(Hit, ActorHandle.SyncActor);
SetShape(Hit, PShapes[0]);
}
#else
check(false); //this can't actually return nullptr since higher up API assumes both shape and actor exists in the low level
SetActor(Hit, nullptr);
SetShape(Hit, nullptr); //todo(ocohen): what do we return for apeiron?
#endif
Insert(HitBuffer, Hit, true); //for now assume all blocking hits
}
}
}
Solver->GetRigidClustering().ReleaseBufferedData();
Solver->ReleaseSolverObjectReverseMapping();
}
#endif
}
DECLARE_CYCLE_STAT(TEXT("Sweep Broadphase"), STAT_SQSweepBroadPhase, STATGROUP_Chaos);
DECLARE_CYCLE_STAT(TEXT("Sweep Narrowphase"), STAT_SQSweepNarrowPhase, STATGROUP_Chaos);
//@todo(mlentine): Avoid duplicated code between this and overlap
void FGeometryCollectionSQAccelerator::Sweep(const FPhysicsGeometry& QueryGeom, const FTransform& StartTM, const FVector& Dir, const float DeltaMag, FPhysicsHitCallback<FHitSweep>& HitBuffer, EHitFlags OutputFlags, FQueryFlags QueryFlags, const FCollisionFilterData& QueryFilter, const FQueryFilterData& QueryFilterData, ICollisionQueryFilterCallbackBase& QueryCallback) const
{
SCOPE_CYCLE_COUNTER(STAT_GCSweep);
FChaosScopeSolverLock SolverScopeLock;
using namespace Chaos;
#if WITH_PHYSX
TMap<const Chaos::FPBDRigidsSolver*, TArray<int32>> SolverIntersectionSets;
// Getter for intersections from the mapping above
auto GetIntersectionsFunc = [&](const Chaos::FPBDRigidsSolver* InSolver, const Chaos::TParticles<float, 3>& InCollisionParticles, float InDeltaMag, const FTransform& InPose) -> TArray<int32>
{
SCOPE_CYCLE_COUNTER(STAT_SQSweepBroadPhase)
TArray<int32> PotentialIntersections;
if(InSolver->Enabled())
{
const Chaos::ISpatialAcceleration<float, 3>* SpacialAcceleration = InSolver->GetSpatialAcceleration();
const int32 NumCollisionParticles = InCollisionParticles.Size();
for(int32 ParticleIndex = 0; ParticleIndex < NumCollisionParticles; ++ParticleIndex)
{
const Chaos::TVector<float, 3> RayStart = InPose.TransformPositionNoScale(InCollisionParticles.X(ParticleIndex));
const Chaos::TVector<float, 3> RayEnd = RayStart + Dir * InDeltaMag;
PotentialIntersections.Append(SpacialAcceleration->FindAllIntersections(Chaos::TSpatialRay<float, 3>(RayStart, RayEnd)));
}
InSolver->ReleaseSpatialAcceleration();
PotentialIntersections.Sort();
for(int32 i = PotentialIntersections.Num() - 1; i > 0; i--)
{
if(PotentialIntersections[i] == PotentialIntersections[i - 1])
{
PotentialIntersections.RemoveAtSwap(i, 1, false);
}
}
}
return PotentialIntersections;
};
// Need somewhere to store our translated shape, similar to the PhysX geom holder
struct FLocalImplicitStorage
{
FLocalImplicitStorage()
: Capsule(FVector::ZeroVector, FVector::ZeroVector, 0.0f)
, Sphere(FVector::ZeroVector, 0.0f)
, Box(FVector::ZeroVector, FVector::ZeroVector)
{}
Chaos::TCapsule<float> Capsule;
Chaos::TSphere<float, 3> Sphere;
Chaos::TBox<float, 3> Box;
};
FLocalImplicitStorage ImplicitStorage;
Chaos::TImplicitObject<float, 3>* Implicit = nullptr;
bool bHit = false;
FHitSweep Hit;
PxGeometryHolder Holder(QueryGeom);
Chaos::TParticles<float, 3> CollisionParticles;
if(Holder.getType() == PxGeometryType::eCAPSULE)
{
physx::PxCapsuleGeometry& PxCapsule = Holder.capsule();
float Radius = PxCapsule.radius;
float HalfHeight = PxCapsule.halfHeight;
Chaos::TVector<float, 3> x1(-HalfHeight, 0, 0);
Chaos::TVector<float, 3> x2(HalfHeight, 0, 0);
Chaos::TCapsule<float> Capsule(x1, x2, Radius);
ImplicitStorage.Capsule = MoveTemp(Capsule);
Implicit = &ImplicitStorage.Capsule;
{
CollisionParticles.AddParticles(14);
CollisionParticles.X(0) = Chaos::TVector<float, 3>(HalfHeight + Radius, 0, 0);
CollisionParticles.X(1) = Chaos::TVector<float, 3>(-HalfHeight - Radius, 0, 0);
CollisionParticles.X(2) = Chaos::TVector<float, 3>(HalfHeight, Radius, Radius);
CollisionParticles.X(3) = Chaos::TVector<float, 3>(HalfHeight, -Radius, Radius);
CollisionParticles.X(4) = Chaos::TVector<float, 3>(HalfHeight, -Radius, -Radius);
CollisionParticles.X(5) = Chaos::TVector<float, 3>(HalfHeight, Radius, -Radius);
CollisionParticles.X(6) = Chaos::TVector<float, 3>(0, Radius, Radius);
CollisionParticles.X(7) = Chaos::TVector<float, 3>(0, -Radius, Radius);
CollisionParticles.X(8) = Chaos::TVector<float, 3>(0, -Radius, -Radius);
CollisionParticles.X(9) = Chaos::TVector<float, 3>(0, Radius, -Radius);
CollisionParticles.X(10) = Chaos::TVector<float, 3>(-HalfHeight, Radius, Radius);
CollisionParticles.X(11) = Chaos::TVector<float, 3>(-HalfHeight, -Radius, Radius);
CollisionParticles.X(12) = Chaos::TVector<float, 3>(-HalfHeight, -Radius, -Radius);
CollisionParticles.X(13) = Chaos::TVector<float, 3>(-HalfHeight, Radius, -Radius);
}
}
else if(Holder.getType() == PxGeometryType::eSPHERE)
{
physx::PxSphereGeometry& PxSphere = Holder.sphere();
float Radius = PxSphere.radius;
Chaos::TSphere<float, 3> Sphere(Chaos::TVector<float, 3>(0), Radius);
ImplicitStorage.Sphere = MoveTemp(Sphere);
Implicit = &ImplicitStorage.Sphere;
{
CollisionParticles.AddParticles(6);
CollisionParticles.X(0) = Chaos::TVector<float, 3>(Radius, 0, 0);
CollisionParticles.X(1) = Chaos::TVector<float, 3>(-Radius, 0, 0);
CollisionParticles.X(2) = Chaos::TVector<float, 3>(0, Radius, Radius);
CollisionParticles.X(3) = Chaos::TVector<float, 3>(0, -Radius, Radius);
CollisionParticles.X(4) = Chaos::TVector<float, 3>(0, -Radius, -Radius);
CollisionParticles.X(5) = Chaos::TVector<float, 3>(0, Radius, -Radius);
}
}
else if(Holder.getType() == PxGeometryType::eBOX)
{
physx::PxBoxGeometry& PxBox = Holder.box();
Chaos::TVector<float, 3> x1(-P2UVector(PxBox.halfExtents));
Chaos::TVector<float, 3> x2(-x1);
Chaos::TBox<float, 3> Box(x1, x2);
ImplicitStorage.Box = MoveTemp(Box);
Implicit = &ImplicitStorage.Box;
{
CollisionParticles.AddParticles(8);
CollisionParticles.X(0) = Chaos::TVector<float, 3>(x1.X, x1.Y, x1.Z);
CollisionParticles.X(1) = Chaos::TVector<float, 3>(x1.X, x1.Y, x2.Z);
CollisionParticles.X(2) = Chaos::TVector<float, 3>(x1.X, x2.Y, x1.Z);
CollisionParticles.X(3) = Chaos::TVector<float, 3>(x2.X, x1.Y, x1.Z);
CollisionParticles.X(4) = Chaos::TVector<float, 3>(x2.X, x2.Y, x2.Z);
CollisionParticles.X(5) = Chaos::TVector<float, 3>(x2.X, x2.Y, x1.Z);
CollisionParticles.X(6) = Chaos::TVector<float, 3>(x2.X, x1.Y, x2.Z);
CollisionParticles.X(7) = Chaos::TVector<float, 3>(x1.X, x2.Y, x2.Z);
}
}
else
{
// We don't support anything else for sweeps
ensureMsgf(false, TEXT("Unsupported query type used for sweep"));
return;
}
const TArray<Chaos::FPBDRigidsSolver*>& Solvers = FChaosSolversModule::GetModule()->GetSolvers();
for(const Chaos::FPBDRigidsSolver* Solver : Solvers)
{
if (!Solver)
{
continue;
}
TArray<int32> IntersectionSet = GetIntersectionsFunc(Solver, CollisionParticles, DeltaMag, StartTM);
const Chaos::FPBDRigidsSolver::FSolverObjectReverseMapping& ObjectMap = Solver->GetSolverObjectReverseMapping_GameThread();
const Chaos::TClusterBuffer<float, 3>& Buffer = Solver->GetRigidClustering().GetBufferedData();
int32 IntersectionSetSize = IntersectionSet.Num();
for(int32 i = 0; i < IntersectionSet.Num(); ++i)
{
SCOPE_CYCLE_COUNTER(STAT_SQSweepNarrowPhase)
const int32 ParticleIndex = IntersectionSet[i];
const SolverObjectWrapper& ObjectWrapper = ObjectMap.SolverObjectReverseMappingArray[ParticleIndex];
if (!ObjectWrapper.SolverObject)
{
const TImplicitObject<float, 3>* Object = Buffer.GeometryPtrs[ParticleIndex].Get();
// Ignore ground plane
if (ParticleIndex == 0 && Object->GetType(true) == ImplicitObjectType::Plane)
{
continue;
}
if (Object && !UseSlowSQ && Object->IsUnderlyingUnion())
{
const TImplicitObjectUnion<float, 3>* Union = static_cast<const TImplicitObjectUnion<float, 3>*>(Object);
//hack: this is terrible because we have no buffered transform so could be off, but most of the time these things are static
{
const TRigidTransform<float, 3>* TMPtr = Buffer.ClusterParentTransforms.Find(ParticleIndex);
if (ensure(TMPtr))
{
if (!(ensure(!FMath::IsNaN(TMPtr->GetTranslation().X)) && ensure(!FMath::IsNaN(TMPtr->GetTranslation().Y)) && ensure(!FMath::IsNaN(TMPtr->GetTranslation().Z))))
{
continue;
}
const TVector<float, 3> StartLocal = TMPtr->InverseTransformPositionNoScale(StartTM.GetLocation());
const TVector<float, 3> DirLocal = TMPtr->InverseTransformVectorNoScale(Dir);
const TVector<float, 3> EndLocal = StartLocal + DirLocal * DeltaMag;
Chaos::TSpatialRay<float, 3> LocalRay(StartLocal, EndLocal);
const TArray<int32> IntersectingChildren = Union->FindAllIntersectingChildren(LocalRay);
IntersectionSet.Append(IntersectingChildren);
}
else
{
UE_LOG(LogChaos, Warning, TEXT("SQ: Could not find a valid transform for a cluster parent for faster child intersections."));
}
}
}
else
{
if (ensure(Buffer.MChildren.Contains(ParticleIndex)))
{
const TArray<uint32>& Children = Buffer.MChildren[ParticleIndex];
for (const uint32 Child : Children)
{
IntersectionSet.Add(Child);
}
}
}
continue;
}
if(ObjectWrapper.Type == ESolverObjectType::GeometryCollectionType && ensure(ObjectWrapper.SolverObject))
{
if(LowLevelSweepSingleElement(ParticleIndex, Solver, Buffer, static_cast<FGeometryCollectionPhysicsObject*>(ObjectWrapper.SolverObject), *Implicit, CollisionParticles, StartTM, Dir, DeltaMag, i >= IntersectionSetSize, Hit))
{
//todo(mlentine): This is duplicated from above and should be merged
//todo(ocohen):hack placeholder while we convert over to non physx API
UGeometryCollectionComponent* Component = Cast<UGeometryCollectionComponent>(ObjectWrapper.SolverObject->GetOwner());
check(Component);
if (Component->IsRegistered())
{
#if !WITH_IMMEDIATE_PHYSX && PHYSICS_INTERFACE_PHYSX
const FPhysicsActorHandle& ActorHandle = Component->DummyBodyInstance.GetPhysicsActorHandle();
PxRigidActor* PRigidActor = ActorHandle.SyncActor;
uint32 PNumShapes = PRigidActor->getNbShapes();
TArray<PxShape*> PShapes;
PShapes.AddZeroed(PNumShapes);
PRigidActor->getShapes(PShapes.GetData(), sizeof(PShapes[0]) * PNumShapes);
SetActor(Hit, ActorHandle.SyncActor);
SetShape(Hit, PShapes[0]);
#else
check(false); //this can't actually return nullptr since higher up API assumes both shape and actor exists in the low level
SetActor(Hit, nullptr);
SetShape(Hit, nullptr); //todo(ocohen): what do we return for apeiron?
#endif
Insert(HitBuffer, Hit, true); //for now assume all blocking hits
}
}
}
}
Solver->GetRigidClustering().ReleaseBufferedData();
Solver->ReleaseSolverObjectReverseMapping();
}
#endif
}
bool LowLevelOverlapSingleElement(int32 InParticleIndex, const Chaos::FPBDRigidsSolver* InSolver, const Chaos::TClusterBuffer<float, 3>& ClusterBuffer, const FGeometryCollectionPhysicsObject* InObject, const Chaos::TImplicitObject<float, 3>& QueryGeom, const FTransform& InPose, FHitOverlap& OutHit)
{
using namespace Chaos;
checkSlow(InSolver);
checkSlow(InObject);
const FGeometryCollectionResults& PhysResult = InObject->GetPhysicsResults().GetGameDataForRead();
const TManagedArray<int32>& RigidBodyIdArray = PhysResult.RigidBodyIds;
const TManagedArray<FTransform>& TransformArray = PhysResult.Transforms;
const TArray<bool>& DisabledFlags = PhysResult.DisabledStates;
const TPBDRigidParticles<float, 3>& Particles = InSolver->GetRigidParticles();
if(!IsValidIndexAndTransform(PhysResult, Particles, TransformArray, DisabledFlags, InParticleIndex, false))
{
return false;
}
const int32 LocalBodyIndex = InParticleIndex - PhysResult.BaseIndex;
const TRigidTransform<float, 3>& TM = PhysResult.ParticleToWorldTransforms[LocalBodyIndex];
const TImplicitObject<float, 3>* Object = ClusterBuffer.GeometryPtrs[InParticleIndex].Get();
if(!Object)
{
return false;
}
Pair<TVector<float, 3>, bool> Result = QueryGeom.FindDeepestIntersection(Object, Particles.CollisionParticles(InParticleIndex).Get(), TRigidTransform<float, 3>(TM) * TRigidTransform<float, 3>(InPose).Inverse(), 0);
return Result.Second;
}
void FGeometryCollectionSQAccelerator::Overlap(const FPhysicsGeometry& QueryGeom, const FTransform& GeomPose, FPhysicsHitCallback<FHitOverlap>& HitBuffer, FQueryFlags QueryFlags, const FCollisionFilterData& QueryFilter, const FQueryFilterData& QueryFilterData, ICollisionQueryFilterCallbackBase& QueryCallback) const
{
return; //todo: This is currently broken because it doesn'thandle cluster unions. Need to fix function - disabling entirely for now
SCOPE_CYCLE_COUNTER(STAT_GCOverlap);
FChaosScopeSolverLock SolverScopeLock;
#if WITH_PHYSX
TMap<const Chaos::FPBDRigidsSolver*, TArray<int32>> SolverIntersectionSets;
// Getter for intersections from the mapping above
auto GetIntersectionsFunc = [&](const Chaos::FPBDRigidsSolver* InSolver, Chaos::TImplicitObject<float, 3>* InGeometry, const FTransform& InPose) -> TArray<int32>*
{
TArray<int32>* CurrentIntersections = SolverIntersectionSets.Find(InSolver);
if(!CurrentIntersections)
{
// This is safe to access here, it's buffered from the physics thread
SolverIntersectionSets.Add(InSolver, InSolver->GetSpatialAcceleration()->FindAllIntersections(InGeometry->BoundingBox().TransformedBox(GeomPose)));
InSolver->ReleaseSpatialAcceleration();
}
return SolverIntersectionSets.Find(InSolver);
};
// Need somewhere to store our translated shape, similar to the PhysX geom holder
struct FLocalImplicitStorage
{
FLocalImplicitStorage()
: Capsule(FVector::ZeroVector, FVector::ZeroVector, 0.0f)
, Sphere(FVector::ZeroVector, 0.0f)
, Box(FVector::ZeroVector, FVector::ZeroVector)
{}
Chaos::TCapsule<float> Capsule;
Chaos::TSphere<float, 3> Sphere;
Chaos::TBox<float, 3> Box;
};
FLocalImplicitStorage ImplicitStorage;
bool bHit = false;
FHitOverlap Hit;
PxGeometryHolder Holder(QueryGeom);
Chaos::TImplicitObject<float, 3>* Implicit = nullptr;
if(Holder.getType() == PxGeometryType::eCAPSULE)
{
physx::PxCapsuleGeometry& PxCapsule = Holder.capsule();
float Radius = PxCapsule.radius;
float HalfHeight = PxCapsule.halfHeight - Radius;
Chaos::TVector<float, 3> x1(-HalfHeight, 0, 0);
Chaos::TVector<float, 3> x2(HalfHeight, 0, 0);
Chaos::TCapsule<float> Capsule(x1, x2, Radius);
ImplicitStorage.Capsule = MoveTemp(Capsule);
Implicit = &ImplicitStorage.Capsule;
}
else if(Holder.getType() == PxGeometryType::eSPHERE)
{
physx::PxSphereGeometry& PxSphere = Holder.sphere();
float Radius = PxSphere.radius;
Chaos::TSphere<float, 3> Sphere(Chaos::TVector<float, 3>(0), Radius);
ImplicitStorage.Sphere = MoveTemp(Sphere);
Implicit = &ImplicitStorage.Sphere;
}
else if(Holder.getType() == PxGeometryType::eBOX)
{
physx::PxBoxGeometry& PxBox = Holder.box();
Chaos::TVector<float, 3> x1(-P2UVector(PxBox.halfExtents));
Chaos::TVector<float, 3> x2(-x1);
Chaos::TBox<float, 3> Box(x1, x2);
ImplicitStorage.Box = MoveTemp(Box);
Implicit = &ImplicitStorage.Box;
}
else
{
ensureMsgf(false, TEXT("Unsupported query type used for overlap"));
return;
}
FChaosSolversModule* Module = FChaosSolversModule::GetModule();
const TArray<Chaos::FPBDRigidsSolver*> Solvers = Module->GetSolvers();
for(const Chaos::FPBDRigidsSolver* Solver : Solvers)
{
// Collect all intersections for this solver
TArray<int32> IntersectionSet = Solver->GetSpatialAcceleration()->FindAllIntersections(Implicit->BoundingBox().TransformedBox(GeomPose));
Solver->ReleaseSpatialAcceleration();
const Chaos::FPBDRigidsSolver::FSolverObjectReverseMapping& ObjectMap = Solver->GetSolverObjectReverseMapping_GameThread();
const Chaos::TClusterBuffer<float, 3>& ClusterBuffer = Solver->GetRigidClustering().GetBufferedData();
for(int32 i = 0; i < IntersectionSet.Num(); ++i)
{
const int32 IntersectParticleIndex = IntersectionSet[i];
const SolverObjectWrapper& ObjectWrapper = ObjectMap.SolverObjectReverseMappingArray[IntersectParticleIndex];
if(ObjectWrapper.Type == ESolverObjectType::GeometryCollectionType && ensure(ObjectWrapper.SolverObject))
{
if(LowLevelOverlapSingleElement(IntersectParticleIndex, Solver, ClusterBuffer, static_cast<FGeometryCollectionPhysicsObject*>(ObjectWrapper.SolverObject), *Implicit, GeomPose, Hit))
{
//todo(mlentine): This is duplicated from above and should be merged
//todo(ocohen):hack placeholder while we convert over to non physx API
UGeometryCollectionComponent* Component = Cast<UGeometryCollectionComponent>(ObjectWrapper.SolverObject->GetOwner());
check(Component);
if (Component->IsRegistered())
{
#if !WITH_IMMEDIATE_PHYSX && PHYSICS_INTERFACE_PHYSX
const FPhysicsActorHandle& ActorHandle = Component->DummyBodyInstance.GetPhysicsActorHandle();
PxRigidActor* PRigidActor = ActorHandle.SyncActor;
uint32 PNumShapes = PRigidActor->getNbShapes();
TArray<PxShape*> PShapes;
PShapes.AddZeroed(PNumShapes);
PRigidActor->getShapes(PShapes.GetData(), sizeof(PShapes[0]) * PNumShapes);
SetActor(Hit, ActorHandle.SyncActor);
SetShape(Hit, PShapes[0]);
#else
check(false); //this can't actually return nullptr since higher up API assumes both shape and actor exists in the low level
SetActor(Hit, nullptr);
SetShape(Hit, nullptr); //todo(ocohen): what do we return for apeiron?
#endif
InsertOverlap(HitBuffer, Hit);
}
}
}
}
Solver->GetRigidClustering().ReleaseBufferedData();
Solver->ReleaseSolverObjectReverseMapping();
}
#endif
}
void FGeometryCollectionSQAccelerator::AddComponent(UGeometryCollectionComponent* Component)
{
if (ensure(Component))
{
// UE_LOG(LogTemp, Log, TEXT("SQAccel: Adding %s (Outer %s)"), *GetNameSafe(Component), *GetNameSafe(Component->GetOuter()));
Components.Add(Component);
}
}
void FGeometryCollectionSQAccelerator::RemoveComponent(UGeometryCollectionComponent* Component)
{
if (ensure(Component))
{
// UE_LOG(LogTemp, Log, TEXT("SQAccel: Removing %s (Outer %s)"), *GetNameSafe(Component), *GetNameSafe(Component->GetOuter()));
Components.Remove(Component);
}
}
#endif