How to get locations of foliage instances?

I’m trying to figure out how to get world locations of foliage instances that have been painted onto a StaticMeshActor using the foliage brush, but not having much luck.

I’ve created a C++ class derived from AStaticMeshActor. In the editor I can assign it a mesh (for example a plane) and then paint foliage instances on it in the editor. I then want to be able to get an array of all the instance types and locations. Here is what I am trying (this code is in the BeginPlay for the AStaticMeshActor-derived class, which now has the foliage instances painted on it:


for (TActorIterator<AInstancedFoliageActor> ActorItr(GetWorld()); ActorItr; ++ActorItr)
    {
        AInstancedFoliageActor* FoliageMesh = *ActorItr;

        UE_LOG(LogTemp, Warning, TEXT("InstancedFoliageActor found!"));

        UStaticMeshComponent* CurrentStaticMeshComponent = this->GetStaticMeshComponent();

        TMap < UFoliageType *, TArray < const FFoliageInstancePlacementInfo * > > FoliageInstances = FoliageMesh->GetInstancesForComponent(CurrentStaticMeshComponent);
    }

The code is finding the InstancedFoliageActor, but maybe I’m misunderstanding what I’m supposed to pass to the ‘GetInstancesForComponent’ function, because it is returning an empty TMap.

Or perhaps there is a different/better way to access the FFoliageInstancePlacementInfo data for all the instances?

Trying to dig a little deeper into this, it seems the ‘GetInstancesForComponent’ function is failing on this part (line 1979 of InstancedFoliage.cpp):


const auto* InstanceSet = MeshInfo.ComponentHash.Find(BaseId);

The BaseId always seems to be ‘1’ but ComponentHash is just empty, which means the InstanceSet is false so the arrays never get built beyond that.

What’s weird is that MeshInfo.Instances is also empty, even though there are definitely instances that have been placed, and MeshInfo.Component.SortedInstances shows Num=7 (which is the correct number of foliage instances that I have painted in the scene).

Still no closer to figuring out this works and why it’s going wrong though…

I figured out how to get the information I need. The code below will output the mesh name, translation, rotation, and scale for each foliage instance in the scene:


for (TActorIterator<AInstancedFoliageActor> ActorItr(GetWorld()); ActorItr; ++ActorItr)
    {
        AInstancedFoliageActor* FoliageMesh = *ActorItr;

        for (auto& MeshPair : FoliageMesh->FoliageMeshes)
        {
            const FFoliageMeshInfo& MeshInfo = *MeshPair.Value;
            UHierarchicalInstancedStaticMeshComponent* MeshComponent = MeshInfo.Component;
            TArray<FInstancedStaticMeshInstanceData> MeshDataArray = MeshComponent->PerInstanceSMData;
            FString MeshName = MeshComponent->GetStaticMesh()->GetName();

            for (auto& MeshMatrix : MeshDataArray)
            {
                FTransform MeshTransform = FTransform(MeshMatrix.Transform);

                UE_LOG(LogTemp, Warning, TEXT("%s, %f, %f, %f, %f, %f, %f, %f, %f, %f,"),
                    *MeshName,
                    MeshTransform.GetLocation().X,
                    MeshTransform.GetLocation().Y,
                    MeshTransform.GetLocation().Z,
                    MeshTransform.GetRotation().X,
                    MeshTransform.GetRotation().Y,
                    MeshTransform.GetRotation().Z,
                    MeshTransform.GetScale3D().X,
                    MeshTransform.GetScale3D().Y, 
                    MeshTransform.GetScale3D().Z);
            }
        }
    }

No idea if this is helpful to anyone else, but there it is!

Requires these includes:


#include "EngineUtils.h"
#include "Runtime/Foliage/Public/InstancedFoliageActor.h"

2 Likes

It works amazing, **localstarlight, **THANK YOU!

1 Like

This didn’t work for me in 4.27

FolliageMeshes was deprecated

Had to do something like this

for (TActorIterator<AInstancedFoliageActor> ActorItr(GetWorld()); ActorItr; ++ActorItr)
	{
		AInstancedFoliageActor* FoliageMesh = *ActorItr;
		auto FoliageComponents = FoliageMesh->GetComponentsByClass(UFoliageInstancedStaticMeshComponent::StaticClass());
		for (auto& Comp : FoliageComponents)
		{
			auto FoliageComponent = Cast<UFoliageInstancedStaticMeshComponent>(Comp);
			if (FoliageComponent != nullptr)
			{
				UStaticMesh* Mesh = FoliageComponent->GetStaticMesh();
				auto InstanceCount = FoliageComponent->GetInstanceCount();

				UE_LOG(LogTemp, Display, TEXT("FoliageComp Mesh:%s Instances:%d"),
					*Mesh->GetName(),
					FoliageComponent->GetInstanceCount());

				for (int i = 0; i < InstanceCount; ++i)
				{
					FTransform InstanceTransform;
					FoliageComponent->GetInstanceTransform(i, OUT InstanceTransform, true);

					UE_LOG(LogTemp, Display, TEXT("%s[%d] %s"),
						*Mesh->GetName(), 
						i,
						*InstanceTransform.ToString()
						);
				}
					
			}
		}

Need to include Foliage module in YourProject.Build.cs

and headers:
#include “Runtime/Foliage/Public/InstancedFoliageActor.h”
#include “Runtime/Foliage/Public/FoliageInstancedStaticMeshComponent.h”
#include “Engine/StaticMesh.h”