LODGroups
LODGroups
#Overview
name: LODGroups
The value of this variable can be defined or overridden in .ini config files. 4
.ini config files referencing this setting variable.
This variable is created as a Console Variable (cvar).
- type:
Exec
- help:
Sorry: Exec commands have no help
It is referenced in 17
C++ source files.
#Summary
#Usage in the C++ source code
The purpose of LODGroups is to manage Level of Detail (LOD) settings for various assets in Unreal Engine 5, particularly for skeletal meshes and static meshes. LODGroups allow developers to define and organize different levels of detail for 3D models, which can be used to optimize rendering performance by displaying simpler versions of objects at greater distances or under specific conditions.
This setting variable is primarily used in the rendering system and asset management subsystems of Unreal Engine. Based on the callsites, the following modules and subsystems rely on LODGroups:
- Skeletal Mesh System
- Static Mesh System
- FBX Import System
- Device Profile Management
- Landscape Rendering System
The value of this variable is typically set in various places:
- In the USkeletalMeshLODSettings class, where it’s defined as a TArray of FSkeletalMeshLODGroupSettings.
- During asset import, particularly when importing FBX files.
- In device profiles, where LOD settings can be customized for different hardware configurations.
- Through editor tools and customizations, such as the Skeletal Mesh Editor and Static Mesh Editor.
LODGroups interact with several other variables and settings, including:
- ReductionSettings for mesh simplification
- ScreenSize for determining when to switch LOD levels
- LODHysteresis for smoothing LOD transitions
- Various import and export settings in the FBX import system
Developers should be aware of the following when using LODGroups:
- LODGroups settings can significantly impact performance and visual quality, so they should be carefully tuned.
- Different asset types (skeletal meshes, static meshes, landscapes) may have slightly different LODGroups implementations.
- LODGroups settings may need to be adjusted when targeting different platforms or device profiles.
- Older assets or HLOD setups may need to be rebuilt to fully utilize LODGroups functionality.
Best practices for using LODGroups include:
- Regularly review and update LODGroups settings as project requirements evolve.
- Use appropriate tools and visualizations to verify LOD transitions and performance impacts.
- Consider platform-specific optimizations when setting up LODGroups.
- Ensure that LODGroups settings are consistent across related assets for visual coherence.
- Collaborate with both artists and performance optimization specialists when defining LODGroups settings.
#Setting Variables
#References In INI files
Location: <Workspace>/Engine/Config/BaseEngine.ini:3319, section: [/Script/Engine.SkeletalMeshLODSettings]
- INI Section:
/Script/Engine.SkeletalMeshLODSettings
- Raw value:
(ScreenSize=(Default=1.0,PerPlatform=()),ReductionSettings=(NumOfTrianglesPercentage=.5))
- Is Array:
True
Location: <Workspace>/Engine/Config/BaseEngine.ini:3320, section: [/Script/Engine.SkeletalMeshLODSettings]
- INI Section:
/Script/Engine.SkeletalMeshLODSettings
- Raw value:
(ScreenSize=(Default=.3,PerPlatform=()),ReductionSettings=(NumOfTrianglesPercentage=.25))
- Is Array:
True
Location: <Workspace>/Engine/Config/BaseEngine.ini:3321, section: [/Script/Engine.SkeletalMeshLODSettings]
- INI Section:
/Script/Engine.SkeletalMeshLODSettings
- Raw value:
(ScreenSize=(Default=.15,PerPlatform=()),ReductionSettings=(NumOfTrianglesPercentage=.125))
- Is Array:
True
Location: <Workspace>/Engine/Config/BaseEngine.ini:3322, section: [/Script/Engine.SkeletalMeshLODSettings]
- INI Section:
/Script/Engine.SkeletalMeshLODSettings
- Raw value:
(ScreenSize=(Default=.1,PerPlatform=()),ReductionSettings=(NumOfTrianglesPercentage=.06))
- Is Array:
True
#References in C++ code
#Callsites
This variable is referenced in the following C++ source code:
#Loc: <Workspace>/Engine/Source/Editor/DetailCustomizations/Private/SkeletalMeshReductionSettingsDetails.cpp:94
Scope (from outer to inner):
file
function void FSkeletalMeshReductionSettingsDetails::CustomizeChildren
lambda-function
Source code excerpt:
{
if (StructPropertyHandle->GetParentHandle()->GetProperty()->GetFName() == GET_MEMBER_NAME_CHECKED(FSkeletalMeshObject, LODInfo) ||
StructPropertyHandle->GetParentHandle()->GetProperty()->GetFName() == GET_MEMBER_NAME_CHECKED(USkeletalMeshLODSettings, LODGroups))
{
return StructPropertyHandle->GetParentHandle()->GetIndexInArray();
}
}
return INDEX_NONE;
#Loc: <Workspace>/Engine/Source/Editor/StaticMeshEditor/Private/StaticMeshEditorSubsystem.cpp:599
Scope (from outer to inner):
file
function bool UStaticMeshEditorSubsystem::SetLODGroup
Source code excerpt:
{
TArray<FName> LODGroups;
UStaticMesh::GetLODGroups(LODGroups);
if(LODGroups.Contains(LODGroup))
{
GWarn->BeginSlowTask(FText::Format(FText::FromString("SetLODGroup: Applying changes to %s"), FText::FromString(StaticMesh->GetName())), true, false);
// Close the mesh editor to prevent crashing. If changes are applied, reopen it after the mesh has been built.
bool bStaticMeshIsEdited = false;
UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Fbx/FbxFactory.cpp:690
Scope: file
Source code excerpt:
FbxImporter->FindAllLODGroupNode(NodeInLod, Node, LODIndex);
}
else // in less some LODGroups have less level, use the last level
{
FbxImporter->FindAllLODGroupNode(NodeInLod, Node, Node->GetChildCount() - 1);
}
for (FbxNode *MeshNode : NodeInLod)
{
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Fbx/FbxSceneImportFactory.cpp:1911
Scope (from outer to inner):
file
function UObject* UFbxSceneImportFactory::ImportOneSkeletalMesh
Source code excerpt:
FbxImporter->FindAllLODGroupNode(NodeInLod, Node, LODIndex);
}
else // in less some LODGroups have less level, use the last level
{
FbxImporter->FindAllLODGroupNode(NodeInLod, Node, Node->GetChildCount() - 1);
}
for (FbxNode *MeshNode : NodeInLod)
{
SkelMeshNodeArray.Add(MeshNode);
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Fbx/FbxSkeletalMeshImport.cpp:2620
Scope (from outer to inner):
file
function USkeletalMesh* UnFbx::FFbxImporter::ReimportSkeletalMesh
Source code excerpt:
FindAllLODGroupNode(NodeInLod, Node, LODIndex);
}
else // in less some LODGroups have less level, use the last level
{
FindAllLODGroupNode(NodeInLod, Node, Node->GetChildCount() - 1);
}
for (FbxNode *MeshNode : NodeInLod)
{
SkelMeshNodeArray.Add(MeshNode);
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/FbxMeshUtils.cpp:720
Scope (from outer to inner):
file
namespace FbxMeshUtils
function bool ImportSkeletalMeshLOD
Source code excerpt:
FFbxImporter->FindAllLODGroupNode(NodeInLod, Node, SelectedLOD);
}
else // in less some LODGroups have less level, use the last level
{
FFbxImporter->FindAllLODGroupNode(NodeInLod, Node, Node->GetChildCount() - 1);
}
for (FbxNode *MeshNode : NodeInLod)
{
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Classes/Engine/SkeletalMeshLODSettings.h:153
Scope (from outer to inner):
file
class class USkeletalMeshLODSettings : public UDataAsset
Source code excerpt:
UPROPERTY(globalconfig, EditAnywhere, Category=LODGroups)
TArray<FSkeletalMeshLODGroupSettings> LODGroups;
friend class FSkeletalMeshReductionSettingsDetails;
public:
/** Retrieves the Skeletal mesh LOD group settings for the given name */
ENGINE_API const FSkeletalMeshLODGroupSettings& GetSettingsForLODLevel(const int32 LODIndex) const;
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Classes/Engine/SkeletalMeshLODSettings.h:163
Scope (from outer to inner):
file
class class USkeletalMeshLODSettings : public UDataAsset
function const bool HasValidSettings
Source code excerpt:
const bool HasValidSettings() const
{
return LODGroups.Num() > 0;
}
/** Returns the number of settings parsed from the ini file */
ENGINE_API int32 GetNumberOfSettings() const;
/*
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/DeviceProfiles/DeviceProfileManager.cpp:754
Scope (from outer to inner):
file
function UDeviceProfile* UDeviceProfileManager::CreateProfile
Source code excerpt:
}
// make sure the DP has all the LODGroups it needs
DeviceProfile->ValidateProfile();
// if the config didn't specify a DeviceType, use the passed in one
if (DeviceProfile->DeviceType.IsEmpty())
{
DeviceProfile->DeviceType = ProfileType;
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/DeviceProfiles/DeviceProfileManager.cpp:1456
Scope (from outer to inner):
file
class class FPlatformCVarExec : public FSelfRegisteringExec
function virtual bool Exec_Runtime
Source code excerpt:
}
// log out the LODGroups fully
FArrayProperty* LODGroupsProperty = FindFProperty<FArrayProperty>(UDeviceProfile::StaticClass(), GET_MEMBER_NAME_CHECKED(UTextureLODSettings, TextureLODGroups));
FScriptArrayHelper_InContainer ArrayHelper(LODGroupsProperty, DeviceProfile);
for (int32 Index = 0; Index < ArrayHelper.Num(); Index++)
{
FString Buffer;
LODGroupsProperty->Inner->ExportTextItem_Direct(Buffer, ArrayHelper.GetRawPtr(Index), ArrayHelper.GetRawPtr(Index), DeviceProfile, 0);
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/SkeletalMeshLODSettings.cpp:29
Scope (from outer to inner):
file
function const FSkeletalMeshLODGroupSettings& USkeletalMeshLODSettings::GetSettingsForLODLevel
Source code excerpt:
const FSkeletalMeshLODGroupSettings& USkeletalMeshLODSettings::GetSettingsForLODLevel(const int32 LODIndex) const
{
if (LODIndex < LODGroups.Num())
{
return LODGroups[LODIndex];
}
// This should not happen as of right now, since the function is only called with 'Default' as name
ensureMsgf(false, TEXT("Invalid Skeletal mesh default settings LOD Level"));
// Default return value to prevent compile issue
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/SkeletalMeshLODSettings.cpp:45
Scope (from outer to inner):
file
function int32 USkeletalMeshLODSettings::GetNumberOfSettings
Source code excerpt:
int32 USkeletalMeshLODSettings::GetNumberOfSettings() const
{
return LODGroups.Num();
}
bool USkeletalMeshLODSettings::SetLODSettingsToMesh(USkeletalMesh* InMesh, int32 LODIndex) const
{
if (InMesh->IsValidLODIndex(LODIndex) && LODGroups.IsValidIndex(LODIndex))
{
FSkeletalMeshLODInfo* LODInfo = InMesh->GetLODInfo(LODIndex);
const FSkeletalMeshLODGroupSettings& Setting = LODGroups[LODIndex];
LODInfo->ReductionSettings = Setting.ReductionSettings;
LODInfo->ScreenSize = Setting.ScreenSize;
LODInfo->LODHysteresis = Setting.LODHysteresis;
LODInfo->WeightOfPrioritization = Setting.WeightOfPrioritization;
LODInfo->bAllowMeshDeformer = Setting.bAllowMeshDeformer;
// if we have available bake pose
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/SkeletalMeshLODSettings.cpp:213
Scope (from outer to inner):
file
function int32 USkeletalMeshLODSettings::SetLODSettingsToMesh
Source code excerpt:
#endif
// we only fill up until we have enough LODs
const int32 NumSettings = FMath::Min(LODGroups.Num(), InMesh->GetLODNum());
for (int32 Index = 0; Index < NumSettings; ++Index)
{
SetLODSettingsToMesh(InMesh, Index);
}
return NumSettings;
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/SkeletalMeshLODSettings.cpp:241
Scope (from outer to inner):
file
function int32 USkeletalMeshLODSettings::SetLODSettingsFromMesh
Source code excerpt:
// we only fill up until we have enough LODs
const int32 NumSettings = InMesh->GetLODNum();
LODGroups.Reset(NumSettings);
LODGroups.AddDefaulted(NumSettings);
for (int32 Index = 0; Index < NumSettings; ++Index)
{
FSkeletalMeshLODInfo* LODInfo = InMesh->GetLODInfo(Index);
FSkeletalMeshLODGroupSettings& Setting = LODGroups[Index];
Setting.ReductionSettings = LODInfo->ReductionSettings;
Setting.ScreenSize = LODInfo->ScreenSize;
Setting.LODHysteresis = LODInfo->LODHysteresis;
// copy mesh setting to shared setting
Setting.BonesToPrioritize.Reset();
for (auto& Bone : LODInfo->BonesToPrioritize)
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/SkeletalMeshLODSettings.cpp:330
Scope (from outer to inner):
file
function void USkeletalMeshLODSettings::Serialize
Source code excerpt:
if (Ar.CustomVer(FFortniteMainBranchObjectVersion::GUID) < FFortniteMainBranchObjectVersion::ConvertReductionSettingOptions)
{
for (int32 Index = 0; Index < LODGroups.Num(); ++Index)
{
FSkeletalMeshOptimizationSettings& ReductionSettings = LODGroups[Index].ReductionSettings;
// prior to this version, both of them were used
ReductionSettings.ReductionMethod = SMOT_TriangleOrDeviation;
if (ReductionSettings.MaxDeviationPercentage == 0.f)
{
// 0.f and 1.f should produce same result. However, it is bad to display 0.f in the slider
// as 0.01 and 0.f causes extreme confusion.
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/StreamingManagerTexture.cpp:2967
Scope: file
Source code excerpt:
}
else if (FParse::Command(&Cmd,TEXT("TextureGroups"))
|| FParse::Command(&Cmd, TEXT("LODGroups")))
{
return HandleLODGroupsCommand( Cmd, Ar );
}
else if (FParse::Command(&Cmd,TEXT("InvestigateTexture"))
|| FParse::Command(&Cmd, TEXT("InvestigateRenderAsset")))
{
#Loc: <Workspace>/Engine/Source/Runtime/Landscape/Private/LandscapeRender.cpp:4773
Scope (from outer to inner):
file
function FPrimitiveSceneProxy* ULandscapeMeshProxyComponent::CreateSceneProxy
Source code excerpt:
if ((LODGroupKey != 0) && (ComponentResolution == 0))
{
// this is an OLD HLOD not built with the new resolution/x/y/center information, which we need if LODGroups are used.
UE_LOG(LogLandscape, Warning, TEXT("HLOD for Landscape needs to be rebuilt! Landscape Mesh Proxy Component '%s' is using LODGroups but does not have the position and orientation information necessary to register it with the Landscape renderer. This HLOD will not render until it is fixed."),
*GetName());
return nullptr;
}
return new FLandscapeMeshProxySceneProxy(this, LandscapeGuid, ProxyComponentBases, ProxyComponentCentersObjectSpace, ComponentXVectorObjectSpace, ComponentYVectorObjectSpace, GetComponentTransform(), ComponentResolution, ProxyLOD, LODGroupKey, ALandscapeProxy::ComputeLandscapeKey(GetWorld(), LODGroupKey, LandscapeGuid));