/** Tests shape components more efficiently than the with-adjustment case, but does less-efficient ppr-poly collision for meshes. */ static bool ComponentEncroachesBlockingGeometry_NoAdjustment(UWorld const* World, AActor const* TestActor, UPrimitiveComponent const* PrimComp, FTransform const& TestWorldTransform) { float const Epsilon = CVarEncroachEpsilon.GetValueOnGameThread(); if (World && PrimComp) { bool bFoundBlockingHit = false; static FName NAME_ComponentEncroachesBlockingGeometry_NoAdjustment = FName(TEXT("ComponentEncroachesBlockingGeometry_NoAdjustment")); ECollisionChannel const BlockingChannel = PrimComp->GetCollisionObjectType(); FCollisionShape const CollisionShape = PrimComp->GetCollisionShape(-Epsilon); if (CollisionShape.IsBox() && (Cast<UBoxComponent>(PrimComp) == nullptr)) { // we have a bounding box not for a box component, which means this was the fallback aabb // since we don't need the penetration info, go ahead and test the component itself for overlaps, which is more accurate if (PrimComp->IsRegistered()) { // must be registered TArray<FOverlapResult> Overlaps; FComponentQueryParams Params(NAME_ComponentEncroachesBlockingGeometry_NoAdjustment, TestActor); return World->ComponentOverlapMultiByChannel(Overlaps, PrimComp, TestWorldTransform.GetLocation(), TestWorldTransform.GetRotation(), BlockingChannel, Params); } else { UE_LOG(LogPhysics, Log, TEXT("Components must be registered in order to be used in a ComponentOverlapMulti call. PriComp: %s TestActor: %s"), *PrimComp->GetName(), *TestActor->GetName()); return false; } } else { FCollisionQueryParams Params(NAME_ComponentEncroachesBlockingGeometry_NoAdjustment, false, TestActor); return World->OverlapAnyTestByChannel(TestWorldTransform.GetLocation(), TestWorldTransform.GetRotation(), BlockingChannel, CollisionShape, Params); } } return false; }
/** Tests shape components less efficiently than the no-adjustment case, but does quicker aabb collision for meshes. */ static bool ComponentEncroachesBlockingGeometry_WithAdjustment(UWorld const* World, AActor const* TestActor, UPrimitiveComponent const* PrimComp, FTransform const& TestWorldTransform, FVector& OutProposedAdjustment) { // init our output OutProposedAdjustment = FVector::ZeroVector; float const Epsilon = CVarEncroachEpsilon.GetValueOnGameThread(); if (World && PrimComp) { bool bFoundBlockingHit = false; bool bComputePenetrationAdjustment = true; TArray<FOverlapResult> Overlaps; static FName NAME_ComponentEncroachesBlockingGeometry_WithAdjustment = FName(TEXT("ComponentEncroachesBlockingGeometry_WithAdjustment")); ECollisionChannel const BlockingChannel = PrimComp->GetCollisionObjectType(); FCollisionShape const CollisionShape = PrimComp->GetCollisionShape(-Epsilon); if (CollisionShape.IsBox() && (Cast<UBoxComponent>(PrimComp) == nullptr)) { // we have a bounding box not for a box component, which means this was the fallback aabb // so lets test the actual component instead of it's aabb // note we won't get penetration adjustment but that's ok if (PrimComp->IsRegistered()) { // must be registered FComponentQueryParams Params(NAME_ComponentEncroachesBlockingGeometry_WithAdjustment, TestActor); bFoundBlockingHit = World->ComponentOverlapMultiByChannel(Overlaps, PrimComp, TestWorldTransform.GetLocation(), TestWorldTransform.GetRotation(), BlockingChannel, Params); bComputePenetrationAdjustment = false; } else { UE_LOG(LogPhysics, Log, TEXT("Components must be registered in order to be used in a ComponentOverlapMulti call. PriComp: %s TestActor: %s"), *PrimComp->GetName(), *TestActor->GetName()); } } else { // overlap our shape FCollisionQueryParams Params(NAME_ComponentEncroachesBlockingGeometry_WithAdjustment, false, TestActor); bFoundBlockingHit = World->OverlapMultiByChannel(Overlaps, TestWorldTransform.GetLocation(), TestWorldTransform.GetRotation(), BlockingChannel, CollisionShape, Params); } // compute adjustment if (bFoundBlockingHit && bComputePenetrationAdjustment) { // if encroaching, add up all the MTDs of overlapping shapes FMTDResult MTDResult; for (int32 HitIdx = 0; HitIdx < Overlaps.Num(); HitIdx++) { UPrimitiveComponent* const OverlapComponent = Overlaps[HitIdx].Component.Get(); // first determine closest impact point along each axis if (OverlapComponent && OverlapComponent->GetCollisionResponseToChannel(BlockingChannel) == ECR_Block) { FCollisionShape const NonShrunkenCollisionShape = PrimComp->GetCollisionShape(); bool bSuccess = OverlapComponent->ComputePenetration(MTDResult, NonShrunkenCollisionShape, TestWorldTransform.GetLocation(), TestWorldTransform.GetRotation()); if (bSuccess) { OutProposedAdjustment += MTDResult.Direction * MTDResult.Distance; } else { UE_LOG(LogPhysics, Log, TEXT("OverlapTest says we are overlapping, yet MTD says we're not. Something is wrong")); } // #hack: sometimes for boxes, physx returns a 0 MTD even though it reports a contact (returns true) // to get around this, let's go ahead and test again with the epsilon-shrunken collision shape to see if we're really in // the clear. if so, we'll say we have no contact (despite what OverlapMultiByChannel said -- it uses a different algorithm) if (FMath::IsNearlyZero(MTDResult.Distance)) { FCollisionShape const ShrunkenCollisionShape = PrimComp->GetCollisionShape(-Epsilon); bSuccess = OverlapComponent->ComputePenetration(MTDResult, ShrunkenCollisionShape, TestWorldTransform.GetLocation(), TestWorldTransform.GetRotation()); if (bSuccess) { OutProposedAdjustment += MTDResult.Direction * MTDResult.Distance; } else { // let's call this "no contact" and be done return false; } } } } } return bFoundBlockingHit; } return false; }