Foliage

Foliage

#Overview

name: Foliage

The value of this variable can be defined or overridden in .ini config files. 1 .ini config file referencing this setting variable.

It is referenced in 24 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of the “Foliage” variable in Unreal Engine 5 is to manage and categorize foliage-related assets and components within the engine. It is primarily used for organizing and identifying assets related to vegetation and landscape elements in the game development process.

This variable is utilized by several Unreal Engine subsystems and modules, including:

  1. Asset Definition system
  2. Foliage system
  3. Landscape system
  4. Rendering system
  5. Nanite system

The value of this variable is typically set as a constant or enum value in various parts of the engine, such as in the EAssetCategoryPaths and EHISMViewRelevanceType enumerations.

Other variables that interact with the Foliage variable include:

Developers should be aware of the following when using this variable:

  1. It is used to categorize assets in the content browser and asset management systems.
  2. It affects the visibility and rendering of foliage elements in the game.
  3. It interacts with the Hierarchical Instanced Static Mesh (HISM) system for efficient rendering of multiple instances.

Best practices when using this variable include:

  1. Properly categorize foliage-related assets using the appropriate asset categories.
  2. Consider performance implications when working with large amounts of foliage, especially in relation to the Nanite and HISM systems.
  3. Be aware of how foliage interacts with other systems like landscapes and lighting.
  4. Use the appropriate flags and settings when creating or modifying foliage components to ensure proper rendering and performance.

By understanding and correctly utilizing the Foliage variable and related systems, developers can create more efficient and visually appealing vegetation in their Unreal Engine 5 projects.

#Setting Variables

#References In INI files

Location: <Workspace>/Engine/Config/BaseEngine.ini:2416, section: [StaticMeshLODSettings]

#References in C++ code

#Callsites

This variable is referenced in the following C++ source code:

#Loc: <Workspace>/Engine/Plugins/Editor/EngineAssetDefinitions/Source/Private/AssetDefinition_ActorFoliageSettings.h:19

Scope (from outer to inner):

file
class        class UAssetDefinition_ActorFoliageSettings : public UAssetDefinitionDefault
function     virtual TConstArrayView<FAssetCategoryPath> GetAssetCategories

Source code excerpt:

	virtual TConstArrayView<FAssetCategoryPath> GetAssetCategories() const override
	{
		static const auto Categories = { EAssetCategoryPaths::Foliage };
		return Categories;
	}
	// UAssetDefinition End
};

#Loc: <Workspace>/Engine/Plugins/Editor/EngineAssetDefinitions/Source/Private/AssetDefinition_InstancedFoliageSettings.h:19

Scope (from outer to inner):

file
class        class UAssetDefinition_InstancedFoliageSettings : public UAssetDefinitionDefault
function     virtual TConstArrayView<FAssetCategoryPath> GetAssetCategories

Source code excerpt:

	virtual TConstArrayView<FAssetCategoryPath> GetAssetCategories() const override
	{
		static const auto Categories = { EAssetCategoryPaths::Foliage };
		return Categories;
	}
	// UAssetDefinition End
};

#Loc: <Workspace>/Engine/Plugins/Editor/EngineAssetDefinitions/Source/Private/AssetDefinition_LandscapeGrassType.h:19

Scope (from outer to inner):

file
class        class UAssetDefinition_LandscapeGrassType : public UAssetDefinitionDefault
function     virtual TConstArrayView<FAssetCategoryPath> GetAssetCategories

Source code excerpt:

	virtual TConstArrayView<FAssetCategoryPath> GetAssetCategories() const override
	{
		static const auto Categories = { EAssetCategoryPaths::Foliage };
		return Categories;
	}
	// UAssetDefinition End
};

#Loc: <Workspace>/Engine/Plugins/Editor/EngineAssetDefinitions/Source/Private/AssetDefinition_ProceduralFoliageSpawner.h:19

Scope (from outer to inner):

file
class        class UAssetDefinition_ProceduralFoliageSpawner : public UAssetDefinitionDefault
function     virtual TConstArrayView<FAssetCategoryPath> GetAssetCategories

Source code excerpt:

	virtual TConstArrayView<FAssetCategoryPath> GetAssetCategories() const override
	{
		static const auto Categories = { EAssetCategoryPaths::Foliage };
		return Categories;
	}
	//bool FAssetTypeActions_ProceduralFoliageSpawner::CanFilter()
	//{
	//	return GetDefault<UEditorExperimentalSettings>()->bProceduralFoliage;
	//}

#Loc: <Workspace>/Engine/Plugins/VirtualProduction/LevelSnapshots/Source/FoliageSupport/Private/FoliageSupport/FoliageSupport.cpp:63

Scope (from outer to inner):

file
namespace    UE::LevelSnapshots::Foliage::Private
function     static UFoliageType* FindFoliageInfoFor

Source code excerpt:

	static UFoliageType* FindFoliageInfoFor(UHierarchicalInstancedStaticMeshComponent* Component)
	{
		AInstancedFoliageActor* Foliage = Cast<AInstancedFoliageActor>(Component->GetOwner());
		if (!LIKELY(Foliage))
		{
			return nullptr;
		}

		for (auto FoliageIt = Foliage->GetFoliageInfos().CreateConstIterator(); FoliageIt; ++FoliageIt)
		{
			if (FoliageIt->Value->Implementation->IsOwnedComponent(Component))
			{
				return FoliageIt->Key;
			}
		}

#Loc: <Workspace>/Engine/Source/Editor/AssetDefinition/Private/AssetDefinition.cpp:18

Scope: file

Source code excerpt:

FAssetCategoryPath EAssetCategoryPaths::Blueprint(LOCTEXT("Blueprint", "Blueprint"));
FAssetCategoryPath EAssetCategoryPaths::Texture(LOCTEXT("Texture", "Texture"));
FAssetCategoryPath EAssetCategoryPaths::Foliage(LOCTEXT("Foliage", "Foliage"));
FAssetCategoryPath EAssetCategoryPaths::Input(LOCTEXT("Input", "Input"));
FAssetCategoryPath EAssetCategoryPaths::FX(LOCTEXT("FX", "FX"));
FAssetCategoryPath EAssetCategoryPaths::Cinematics(LOCTEXT("Cinematics", "Cinematics"));
FAssetCategoryPath EAssetCategoryPaths::Media(LOCTEXT("Media", "Media"));
FAssetCategoryPath EAssetCategoryPaths::World(LOCTEXT("World", "World"));

#Loc: <Workspace>/Engine/Source/Editor/AssetDefinition/Public/AssetDefinition.h:267

Scope: file

Source code excerpt:

	static FAssetCategoryPath Blueprint;
	static FAssetCategoryPath Cinematics;
	static FAssetCategoryPath Foliage;
	static FAssetCategoryPath FX;
	static FAssetCategoryPath Gameplay;
	static FAssetCategoryPath AI;
	static FAssetCategoryPath Input;
	static FAssetCategoryPath Material;
	static FAssetCategoryPath Media;

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Classes/Components/HierarchicalInstancedStaticMeshComponent.h:18

Scope: file

Source code excerpt:

{
	Grass,
	Foliage,
	HISM
};

// Due to BulkSerialize we can't edit the struct, so we must deprecated this one and create a new one
USTRUCT()
struct FClusterNode_DEPRECATED

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/HierarchicalInstancedStaticMesh.cpp:873

Scope (from outer to inner):

file
function     FPrimitiveViewRelevance FHierarchicalStaticMeshSceneProxy::GetViewRelevance

Source code excerpt:

		bShowInstancedMesh = View->Family->EngineShowFlags.InstancedGrass;
		break;
	case EHISMViewRelevanceType::Foliage:
		bShowInstancedMesh = View->Family->EngineShowFlags.InstancedFoliage;
		break;
	case EHISMViewRelevanceType::HISM:
		bShowInstancedMesh = View->Family->EngineShowFlags.InstancedStaticMeshes;
		break;
	default:

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Rendering/NaniteResources.cpp:948

Scope (from outer to inner):

file
namespace    Nanite
function     FSceneProxy::FSceneProxy

Source code excerpt:

		bIsLandscapeGrass = true;
		break;
	case EHISMViewRelevanceType::Foliage:
		FilterFlags = EFilterFlags::Foliage;
		break;
	default:
		FilterFlags = EFilterFlags::InstancedStaticMesh;
		break;
	}
	FilterFlags |= Component->Mobility == EComponentMobility::Static ? EFilterFlags::StaticMobility : EFilterFlags::NonStaticMobility;

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Public/NaniteSceneProxy.h:151

Scope (from outer to inner):

file
namespace    Nanite

Source code excerpt:

	StaticMesh				= (1u << 0u),
	InstancedStaticMesh		= (1u << 1u),
	Foliage					= (1u << 2u),
	Grass					= (1u << 3u),
	Landscape				= (1u << 4u),
	StaticMobility			= (1u << 5u),
	NonStaticMobility		= (1u << 6u),
	All						= 0xFF
};

#Loc: <Workspace>/Engine/Source/Runtime/Foliage/Private/FoliageModule.cpp:3

Scope: file

Source code excerpt:

#include "Modules/ModuleManager.h"

IMPLEMENT_MODULE( IFoliageModule, Foliage );

#Loc: <Workspace>/Engine/Source/Runtime/Foliage/Private/InstancedFoliage.cpp:4338

Scope (from outer to inner):

file
function     void AInstancedFoliageActor::Serialize

Source code excerpt:

void AInstancedFoliageActor::Serialize(FArchive& Ar)
{
	LLM_SCOPE_BYNAME(TEXT("Foliage"));
	Super::Serialize(Ar);

	Ar.UsingCustomVersion(FFoliageCustomVersion::GUID);

#if WITH_EDITORONLY_DATA
	if (!Ar.ArIsFilterEditorOnly && Ar.CustomVer(FFoliageCustomVersion::GUID) >= FFoliageCustomVersion::CrossLevelBase)

#Loc: <Workspace>/Engine/Source/Runtime/Foliage/Private/InstancedFoliage.cpp:5784

Scope (from outer to inner):

file
function     UFoliageInstancedStaticMeshComponent::UFoliageInstancedStaticMeshComponent

Source code excerpt:

	bEnableAutoLODGeneration = false;

	ViewRelevanceType = EHISMViewRelevanceType::Foliage;
}

void UFoliageInstancedStaticMeshComponent::ReceiveComponentDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
	Super::ReceiveComponentDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);

#Loc: <Workspace>/Engine/Source/Runtime/Landscape/Classes/LandscapeProxy.h:263

Scope: file

Source code excerpt:

	{
		FGrassCompKey Key;
		TWeakObjectPtr<UHierarchicalInstancedStaticMeshComponent> Foliage;
		TWeakObjectPtr<UHierarchicalInstancedStaticMeshComponent> PreviousFoliage;
		TArray<FBox> ExcludedBoxes;
		uint32 LastUsedFrameNumber;
		uint32 ExclusionChangeTag;
		double LastUsedTime;
		bool Pending;

#Loc: <Workspace>/Engine/Source/Runtime/Landscape/Classes/LandscapeProxy.h:318

Scope (from outer to inner):

file
class        class FAsyncGrassTask : public FNonAbandonableTask

Source code excerpt:

	FAsyncGrassBuilder* Builder;
	FCachedLandscapeFoliage::FGrassCompKey Key;
	TWeakObjectPtr<UHierarchicalInstancedStaticMeshComponent> Foliage;

	FAsyncGrassTask(FAsyncGrassBuilder* InBuilder, const FCachedLandscapeFoliage::FGrassCompKey& InKey, UHierarchicalInstancedStaticMeshComponent* InFoliage);
	void DoWork();

	FORCEINLINE TStatId GetStatId() const
	{

#Loc: <Workspace>/Engine/Source/Runtime/Landscape/Private/LandscapeGrass.cpp:2341

Scope (from outer to inner):

file
function     void ALandscapeProxy::FlushGrassComponents

Source code excerpt:

			if (Component == nullptr || OnlyForComponents->Contains(Component))
			{
				UHierarchicalInstancedStaticMeshComponent *Used = (*Iter).Foliage.Get();
				if (Used)
				{
					SCOPE_CYCLE_COUNTER(STAT_FoliageGrassDestoryComp);
					Used->ClearInstances();
					Used->DetachFromComponent(FDetachmentTransformRules(EDetachmentRule::KeepRelative, false));
					Used->DestroyComponent();

#Loc: <Workspace>/Engine/Source/Runtime/Landscape/Private/LandscapeGrass.cpp:2767

Scope: file

Source code excerpt:

												{
													bRebuildForBoxes = true;
													NewComp.PreviousFoliage = Existing->Foliage;
													Existing->PendingRemovalRebuild = true;
												}
												else
												{
													Existing->ExclusionChangeTag = GGrassExclusionChangeTag;
												}

#Loc: <Workspace>/Engine/Source/Runtime/Landscape/Private/LandscapeGrass.cpp:2828

Scope: file

Source code excerpt:

											GrassInstancedStaticMeshComponent = NewObject<UGrassInstancedStaticMeshComponent>(this, NAME_None, RF_Transient);
										}
										NewComp.Foliage = GrassInstancedStaticMeshComponent;
										FoliageCache.CachedGrassComps.Add(NewComp);

										GrassInstancedStaticMeshComponent->Mobility = EComponentMobility::Static;
										GrassInstancedStaticMeshComponent->SetStaticMesh(GrassVariety.GrassMesh);
										GrassInstancedStaticMeshComponent->MinLOD = GrassVariety.MinLOD;
										GrassInstancedStaticMeshComponent->bSelectable = false;

#Loc: <Workspace>/Engine/Source/Runtime/Landscape/Private/LandscapeGrass.cpp:2954

Scope (from outer to inner):

file
function     void ALandscapeProxy::UpdateGrass

Source code excerpt:

		{
			const FCachedLandscapeFoliage::FGrassComp& GrassItem = *Iter;
			UHierarchicalInstancedStaticMeshComponent *Used = GrassItem.Foliage.Get();
			UHierarchicalInstancedStaticMeshComponent *UsedPrev = GrassItem.PreviousFoliage.Get();
			bool bOld =
				!GrassItem.Pending &&
				(
					!GrassItem.Key.BasedOn.Get() ||
					!GrassItem.Key.GrassType.Get() ||

#Loc: <Workspace>/Engine/Source/Runtime/Landscape/Private/LandscapeGrass.cpp:3036

Scope (from outer to inner):

file
function     void ALandscapeProxy::ProcessAsyncGrassInstanceTasks

Source code excerpt:

			// We need to preserve the order here, otherwise we'll have new jobs that are added to the end jumping up in front of the queue and an original second job would be updated the last
			AsyncFoliageTasks.RemoveAt(Index--);
			UGrassInstancedStaticMeshComponent* GrassISMComponent = Cast<UGrassInstancedStaticMeshComponent>(Inner.Foliage.Get());
			int32 NumBuiltRenderInstances = Inner.Builder->InstanceBuffer.GetNumInstances();
			//UE_LOG(LogCore, Display, TEXT("%d instances in %4.0fms     %6.0f instances / sec"), NumBuiltRenderInstances, 1000.0f * float(Inner.Builder->BuildTime), float(NumBuiltRenderInstances) / float(Inner.Builder->BuildTime));

			FCachedLandscapeFoliage::FGrassCompKey& InnerKey = Inner.Key;

			UE_LOG(LogGrass, Verbose, TEXT("ASYNC GRASS INSTANCES COMPLETE %s %s %d %d (%d)"),

#Loc: <Workspace>/Engine/Source/Runtime/Landscape/Private/LandscapeGrass.cpp:3094

Scope (from outer to inner):

file
function     FAsyncGrassTask::FAsyncGrassTask

Source code excerpt:

	: Builder(InBuilder)
	, Key(InKey)
	, Foliage(InFoliage)
{
}

void FAsyncGrassTask::DoWork()
{
	Builder->Build();

#Loc: <Workspace>/Engine/Source/Runtime/Landscape/Private/LandscapeLight.cpp:766

Scope (from outer to inner):

file
function     void ULandscapeComponent::InvalidateLightingCacheDetailed

Source code excerpt:

		const ULandscapeGrassType* GrassType = GrassKey.GrassType.Get();
		const ULandscapeComponent* BasedOn = GrassKey.BasedOn.Get();
		UHierarchicalInstancedStaticMeshComponent* GrassComponent = Iter->Foliage.Get();

		if (BasedOn == this && GrassType && GrassComponent &&
			GrassType->GrassVarieties.IsValidIndex(GrassKey.VarietyIndex) &&
			GrassType->GrassVarieties[GrassKey.VarietyIndex].bUseLandscapeLightmap)
		{
			// Remove this grass component from the cache, which will cause it to be replaced

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/Nanite/NaniteCullRaster.cpp:3030

Scope (from outer to inner):

file
namespace    Nanite
function     void FRenderer::AddPass_PrimitiveFilter

Source code excerpt:

	if (!SceneView.Family->EngineShowFlags.InstancedFoliage)
	{
		HiddenFilterFlags |= EFilterFlags::Foliage;
	}

	if (!SceneView.Family->EngineShowFlags.InstancedGrass)
	{
		HiddenFilterFlags |= EFilterFlags::Grass;
	}