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

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.
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.

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.