Finding exit location on a mesh Problem

I’ve been stuck on this problem for a week or 2 now. I am trying to make an algorithm to calculate penetration exit location, before I used backwards (reverse start and end, so the reported hit is in the other side of the wall) UKismetSystemLibrary::SphereTraceMultiForObjects and that works until you try it on a U shape (so it hits the same object twice) since it just counts both hits as just one and the reported hit is the one furthest away. I can only think of 2 ways to solve this problem and both I couldn’t figure out, either make UKismetSystemLibrary::SphereTraceMultiForObjects or find another function that will report everytime it hits/interacts with an object even if it’s the same object or make my own function which will get every intersection (so entering and leaving an object) in a ray or sweep, so then I can later process it myself which is what I’ve been trying to do, but the problem with that is that I’ve been looking at the source code of what already exists, and that is a never ending rabbit hole of functions which aren’t exposed or symbol errors.

Here are some pictures to better show what I mean:
The current results/outcome (Unwanted)
CurrentResults

The wanted results/outcome
WantedResults

SphereTraceMultiForObjects returns an array of hits sorted from start of the ray to the end of the ray, so couldn’t you just take the 2nd hit in the array (if it exists) and ignore anything else?

I should have said, but I do go through the array in reverse, so the last hit (closest and most likely the exit location) will come first. I have checked the array count and if I have 2 separate objects (walls), one in front of the other one, then it does report 2 separate hits and it works fine but if I have the U shape, then it will only report 1 hit (as in only 1 hit result in the array) and its always the furthest away point

Ah, sorry, I thought that the array would return hits for each time a ray hit a mesh. It looks like it only returns a hit for every actor it hits, and only on the outside of a mesh. Bummer.

This is a pretty tough problem!

There are a couple ways I can think of doing this:

  1. Create an inner mesh with normals facing inward and fire two line traces. The first one hits the mesh normally and the second starts from the impact point (with a slight offset to not hit the same point) and tries to hit the inner mesh as the line trace is exiting the shape
  2. If you don’t want to use an inner mesh, you can fire three line traces. The first line trace will hit the outside of the mesh, the second will hit the third point on your mesh (in your second diagram), then fire a third line trace from the second impact point in the opposite direction to get the second point (the actual exit).

I personally don’t like the first approach, so I tried coding up a function for the second one.

void ATestBullet::TraceBullet(FVector Start, FVector End)
{
	// Fire first line trace
	FHitResult HitResult;
	FCollisionQueryParams CollisionParams;
	CollisionParams.bTraceComplex = true;
	AActor* LastHitActor = nullptr;

	GetWorld()->LineTraceSingleByChannel(HitResult, Start, End, ECC_Visibility, CollisionParams);
	AActor* HitActor = HitResult.GetActor();
	if (HitActor != nullptr && LastHitActor == nullptr)
	{
		LastHitActor = HitActor;
	}

	// Show it
	DrawDebugLine(GetWorld(),Start,HitResult.ImpactPoint, FColor::Red, true, 5.0, 0, 2.0);
	UE_LOG(LogTemp, Warning, TEXT("Hit point: %s"), *HitResult.ImpactPoint.ToString());

	// If we hit something, make another line trace
	if (HitResult.bBlockingHit)
	{
		FVector newStart = HitResult.ImpactPoint - HitResult.ImpactNormal;
		GetWorld()->LineTraceSingleByChannel(HitResult, newStart, End, ECC_Visibility, CollisionParams);

		// Show it
		DrawDebugLine(GetWorld(), newStart, HitResult.ImpactPoint, FColor::Green, true, 5.0, 0, 2.0);  
		UE_LOG(LogTemp, Warning, TEXT("Second hit point: %s"), *HitResult.ImpactPoint.ToString());

		// If we have hit the same mesh, fire a third line trace backwards
		if (HitResult.bBlockingHit)
		{
			auto secondHitActor = HitResult.GetActor();
			if (secondHitActor != nullptr && secondHitActor == LastHitActor)
			{
				FVector finalStart = HitResult.ImpactPoint + HitResult.ImpactNormal;
				GetWorld()->LineTraceSingleByChannel(HitResult, finalStart, Start, ECC_Visibility, CollisionParams);

				// Show it, but render it a bit lower so we can see it
				
				DrawDebugLine(GetWorld(), finalStart + FVector(0, 0, -2.f), HitResult.ImpactPoint + FVector(0, 0, -2.f), FColor::Blue, true, 5.0, 0, 2.0);  
				UE_LOG(LogTemp, Warning, TEXT("Third hit point: %s"), *HitResult.ImpactPoint.ToString());
			}
			
		}
	}
	
}

I tested this with a torus, so it should work.

Ah, thank you very much. That is a smart solution, kinda of sucks how trace only reports 1 hit per object, but oh well

1 Like

Using your algorithm as a basis, I have managed to develop a recursive function, so (in theory at least) it should work on weird ■■■ shapes, hopefully.

Here is a GitHub gist link to it