(UE4) Is there a method of getting all actors in camera view?

djs415 picture djs415 · Aug 18, 2015 · Viewed 8.8k times · Source

Like the title says, i want to be able to get all actors that are within the camera view in Unreal Engine 4.

I have thought of two ways i could do this: 1) using a shape trace in the form of a "boxtracebyobject" which works but seems to be glitchy at times and has trouble recognizing multiple overlapping actors. 2) using a "BoxOverlappingActors", though i havent quite figured out how to use it yet.

If anyone knows of a proper method to getting actors in cameria view, my ears are open!

Answer

T. Kiley picture T. Kiley · Aug 27, 2015

Update

A much better answer has been posted on Unreal Answers by Rama:

Use the AActor::GetLastRenderTime() to find out for each actor when last drawn or UPrimitiveComponent::LastRenderTime for the primitive itself. His answer explains how you can use this.

Original answer:

As you suggested, it seems weird that you would have to do something to do with collision volumes when Unreal must be checking to do culling so I found the following method to query that information:

First we have to create a FSceneView from the camera. This code is taken from the UE Answers question.

APlayerCameraManager* Manager = World->GetFirstPlayerController()->PlayerCameraManager;
ULocalPlayer* LocalPlayer = World->GetFirstLocalPlayerFromController();

FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(
    LocalPlayer->ViewportClient->Viewport,
    World->Scene,
    LocalPlayer->ViewportClient->EngineShowFlags)
    .SetRealtimeUpdate(true));

FVector ViewLocation;
FRotator ViewRotation;
FSceneView* SceneView = LocalPlayer->CalcSceneView(&ViewFamily, /*out*/ ViewLocation, /*out*/ ViewRotation, LocalPlayer->ViewportClient->Viewport);

Once we've created the view we can iterate through all primitive components (includes meshes) and then we can use the bounds from the SceneProxy of the Static Mesh and the bounds of the view to do our own culling.

for (TObjectIterator<UPrimitiveComponent> ScenePrimitsItr; ScenePrimitsItr; ++ScenePrimitsItr)
{
    // Using the Object iterator so we have to skip objects not in the 
    // relevant world
    if (ScenePrimitsItr->GetWorld() == World)
    {
        FPrimitiveSceneProxy* Proxy = ScenePrimitsItr->SceneProxy;
        if (Proxy)
        {
            bool bIsShown = 
                SceneView->ViewFrustum.IntersectSphere(Proxy->GetBounds().Origin, Proxy->GetBounds().SphereRadius)||
                SceneView->ViewFrustum.IntersectBox(Proxy->GetBounds().Origin, Proxy->GetBounds().BoxExtent);
        }
    }
}

The frustrum check came from SceneVisibility.cpp in the method FrustumCull This method isn't ideal as a) involves duplicating that check and b) performing the test twice. However, I couldn't find a way to actually query the result. It appears to be stored in a bit array in the FViewInfo. Unfortunately this info is not exposed outside of the renderer module.