2 min read

[18/50] The ground beneath your feet - Why it is hard to get collision UV for Landscape (Part 3)

Stable Diffusion result for: "An art work for collisions in unreal."
Stable Diffusion result for: "An art work for collisions in unreal."

In this post, we look at a very handy function available in UGameplayStatics and try to figure out why there is no convenient way to get UV of the collision with the Landscape in Unreal Engine.

💡
This post is a part of our series covering Landscapes in Unreal Engine. Previous posts in the series: Part 1 and Part 2.

From the official documentation, UGameplayStatics is:

Static class with useful gameplay utility functions that can be called from both Blueprint and C++

Essentially, it implements a bunch of functions which we routinely use withing Blueprints and C++, for example, GetPlayerController().

Let's start with looking at the function FindCollisionUV from this neat little static class. This function accepts FHitResult returned by a collision event, the UV channel and the UV itself and returns the UV coordinates for the collision impact location.

But it does so only if it is able to infer the coordinates - which is not always the case - Landscape being one of the actors for which this function fails.

Screenshot of FindCollisionUV function defintion from UE 5.1 © Epic Games, Inc.
Screenshot of FindCollisionUV function defintion from UE 5.1 © Epic Games, Inc.

Above is a screenshot from the Unreal Engine 5.1 codebase of the function. Disclaimer: The function is a copyright of Epic Games, Inc. and is used here for educational purposes only.

The very first if clause checks if the feature flag is enabled from the project settings. The feature flag can be found under the "Physics Settings" section of the project settings menu in the editor and is called Support UV From Hit Results. The flag is disabled by default and enabling it leads to a small performance penalty.

If the flag is set, the function checks for the hit component and if the hit component overrides the GetBodySetup() function. The GetBodySetup() function is defined as a virtual function in the UPrimitiveComponent and returns NULL by default. The overriden implementions are expected to return the object, UBodySetup, which contains the collision data at the asset level (rather than instances of the assets deployed in the level) for efficiency.

If the component does return a UBodySetup, then the function proceeds to do the math in order to arrive at the UV coordinates at the local hit position computed from the collision location.

So why this does not work for Landscape?

Collisions with the Landscape return ULandscapeHeightfieldCollisionComponent which too is a sub-class of UPrimitiveComponent but does not override the GetBodySetup() function which is required by FindCollisionUV.

Now why it does not override is not documented anywhere but our naive guess is that UBodySetup works at an asset level (think an imported FBX or an OBJ) unlike Landscape where the geo is defined within the level with either brushes or heightmaps or a combination of both.

Again, this is our estimate. Someone at Epic Games, Inc. will definitely know better.