13#include "Components/StaticMeshComponent.h"
14#include "UObject/ConstructorHelpers.h"
15#include "Kismet/GameplayStatics.h"
16#include "Materials/Material.h"
17#include "TimerManager.h"
18#include "Engine/World.h"
23 : Super(ObjectInitializer)
25 RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT(
"SceneRoot"));
26 RootComponent->SetMobility(EComponentMobility::Static);
27 PrimaryActorTick.bCanEverTick =
false;
30 static ConstructorHelpers::FObjectFinder<UStaticMesh> DefaultMesh(TEXT(
"/Engine/BasicShapes/Cube.Cube"));
31 if (DefaultMesh.Succeeded())
50 UE_LOG(LogTemp, Warning, TEXT(
"InstancedRenderer.cpp: Instance already exists. Destroying this actor.."));
55 UWorld* World = GetWorld();
73 World->GetTimerManager().SetTimer(Handle, FTimerDelegate::CreateLambda([
this] {
94 Super::EndPlay(EndPlayReason);
105 UWorld* World = GetWorld();
122 if (Entry.InstancedStaticMeshComponent)
124 Entry.InstancedStaticMeshComponent->ClearInstances();
125 Entry.InstancedStaticMeshComponent->UnregisterComponent();
126 Entry.InstancedStaticMeshComponent->DetachFromComponent(FDetachmentTransformRules::KeepRelativeTransform);
127 Entry.InstancedStaticMeshComponent->DestroyComponent();
128 Entry.InstancedStaticMeshComponent =
nullptr;
131 Entry.Materials.Empty();
143 UE_LOG(LogTemp, Warning, TEXT(
"InstancedRenderer.cpp: Actor is null!"));
151 UE_LOG(LogTemp, Warning, TEXT(
"InstancedRenderer.cpp: AInstancedActor %s is set to update transform automatically."), *InstancedActor->GetName());
157 if (!StaticMeshComponent)
160 UE_LOG(LogTemp, Warning, TEXT(
"InstancedRenderer.cpp: AInstancedActor %s UStaticMeshComponent is null!"), *InstancedActor->GetName());
165 UStaticMesh* Mesh = StaticMeshComponent->GetStaticMesh();
169 UE_LOG(LogTemp, Warning, TEXT(
"InstancedRenderer.cpp: AInstancedActor %s UStaticMeshComponent mesh is null!"), *InstancedActor->GetName());
174 int32 ComponentIndex = 9999;
182 UE_LOG(LogTemp, Warning, TEXT(
"InstancedRenderer.cpp: ComponentIndex is not valid!"));
187 UInstancedStaticMeshComponent* InstancedStaticMeshComponent =
InstanceEntries[ComponentIndex].InstancedStaticMeshComponent;
188 if (!InstancedStaticMeshComponent)
191 UE_LOG(LogTemp, Warning, TEXT(
"InstancedRenderer.cpp: UInstancedStaticMeshComponent is nullptr!"));
197 FTransform ActorTransform = StaticMeshComponent->GetComponentTransform();
198 InstancedStaticMeshComponent->AddInstance(ActorTransform);
200 int32 InstanceIndex = InstancedStaticMeshComponent->GetNumRenderInstances() - 1;
203 InstancedActor->
InstanceAdded(ComponentIndex, InstanceIndex);
213 if (InstancedActor ==
nullptr || StaticMeshComponent ==
nullptr)
216 UE_LOG(LogTemp, Warning, TEXT(
"StaticInstancedRenderer.cpp: StaticMeshComponent or InstancedActor is null!"));
221 UStaticMesh* Mesh = StaticMeshComponent->GetStaticMesh();
226 for (int32 Index = 0; Index < Num; ++Index)
236 const int32 NumberOfMaterials = StaticMeshComponent->GetNumMaterials();
238 TArray<UMaterialInterface*> MeshMaterials;
239 MeshMaterials.Reserve(NumberOfMaterials);
241 for (int32 i = 0; i < NumberOfMaterials; i++)
243 MeshMaterials.Add(StaticMeshComponent->GetMaterial(i));
247 for (int32 Index = 0; Index < Size; ++Index)
251 bool bMaterialsMatch =
true;
253 for (int32 MaterialIndex = 0; MaterialIndex <
InstanceEntries[Index].Materials.Num(); ++MaterialIndex)
255 UMaterialInterface* UniqueMeshMaterial =
InstanceEntries[Index].Materials[MaterialIndex];
256 UMaterialInterface* MeshMaterial = MeshMaterials[MaterialIndex];
257 if (UniqueMeshMaterial != MeshMaterial)
259 bMaterialsMatch =
false;
275 UInstancedStaticMeshComponent* InstancedStaticMeshComponent = NewObject<UInstancedStaticMeshComponent>(
this);
277 UClass* ClassPtr = InstancedActor->GetClass();
279 FString Name = IInteractable::Execute_GetInteractableName(InstancedActor).ToString();
283 Name = ClassPtr->GetAuthoredName();
284 if (Name.EndsWith(TEXT(
"_C")))
286 Name = Name.LeftChop(2);
288 if (Name.StartsWith(TEXT(
"BP_")))
290 Name = Name.RightChop(3);
292 Name.ReplaceInline(TEXT(
"_"), TEXT(
" "));
293 Name.ReplaceInline(TEXT(
"Instanced"), TEXT(
" "));
297 FString Guid = FGuid::NewGuid().ToString().Left(4);
299 Name = FString::Printf(TEXT(
"%s(_%s_)"), *Name, *Guid);
301 InstancedStaticMeshComponent->Rename(*Name);
304 InstancedStaticMeshComponent->AttachToComponent(GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
305 AddInstanceComponent(InstancedStaticMeshComponent);
307 InstancedStaticMeshComponent->bEditableWhenInherited =
false;
308 InstancedStaticMeshComponent->RegisterComponent();
309 InstancedStaticMeshComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
310 InstancedStaticMeshComponent->CreatePhysicsState(
false);
311 InstancedStaticMeshComponent->SetSimulatePhysics(
false);
312 InstancedStaticMeshComponent->SetStaticMesh(Mesh);
313 InstancedStaticMeshComponent->SetMobility(EComponentMobility::Static);
314 InstancedStaticMeshComponent->SetHiddenInGame(
false);
315 InstancedStaticMeshComponent->SetVisibility(
true);
316 InstancedStaticMeshComponent->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepWorldTransform);
329 GetWorld()->GetTimerManager().SetTimer(Handle, FTimerDelegate::CreateLambda([
this, InstancedStaticMeshComponent, AllowWorldPositionOffsetDisable] {
332 InstancedStaticMeshComponent->SetEvaluateWorldPositionOffset(false);
333 InstancedStaticMeshComponent->SetEvaluateWorldPositionOffsetInRayTracing(false);
342 InstancedStaticMeshComponent->SetRenderCustomDepth(
true);
343 InstancedStaticMeshComponent->SetCustomDepthStencilValue(StaticMeshComponent->CustomDepthStencilValue);
347 InstancedStaticMeshComponent->ShadowCacheInvalidationBehavior = EShadowCacheInvalidationBehavior::Rigid;
353 InstanceEntry.
Mesh = Mesh;
361 const int32 MaterialCount = MeshMaterials.Num();
362 for (int32 MaterialIndex = 0; MaterialIndex < MaterialCount; ++MaterialIndex)
364 UMaterialInterface* MaterialInterface = InstanceEntry.
Materials[MaterialIndex];
366 if (MaterialInterface)
368 InstancedStaticMeshComponent->SetMaterial(MaterialIndex, MaterialInterface);
372 UMaterial* Material = Cast<UMaterial>(MaterialInterface);
375 if (!Material->bUsedWithInstancedStaticMeshes)
377 UE_LOG(LogTemp, Warning, TEXT(
"InstancedRenderer.cpp: Material '%s' bUsedWithInstancedStaticMeshes is not set to true!"), *Material->GetName());
418 if (InstanceNumber >= 0 && InstanceNumber < Entry.InstancedStaticMeshComponent->GetInstanceCount())
431 if (Entry.InstancedStaticMeshComponent)
433 Entry.InstancedStaticMeshComponent->SetVisibility(
Rendering);
438 UE_LOG(LogTemp, Warning, TEXT(
"InstancedRenderer.cpp: Rendering: %s"), (
Rendering ? TEXT(
"true") : TEXT(
"false")));
447 UE_LOG(LogTemp, Warning, TEXT(
"InstancedRenderer.cpp: Index %d is not valid!"), Index);
472 bool ShouldRenderWPO = IsWinter || IsWindy;
486 if (Entry.InstancedStaticMeshComponent)
488 Entry.InstancedStaticMeshComponent->SetRenderCustomDepth(Enabled);
504 if (Entry.InstancedStaticMeshComponent)
515 if (Entry.InstancedStaticMeshComponent)
517 Entry.InstancedStaticMeshComponent->ShadowCacheInvalidationBehavior = ShadowCacheInvalidationBehaviour;
526 if (Entry.AllowWorldPositionOffsetDisable && Entry.InstancedStaticMeshComponent)
528 Entry.InstancedStaticMeshComponent->SetEvaluateWorldPositionOffset(RenderWPO);
529 Entry.InstancedStaticMeshComponent->SetEvaluateWorldPositionOffsetInRayTracing(RenderWPO);
536 UMaterialInterface* DefaultMaterial =
nullptr;
537 if (SetUnrealDefaultMaterial)
539 DefaultMaterial = UMaterial::GetDefaultMaterial(EMaterialDomain(0));
544 if (Entry.AllowWorldPositionOffsetDisable && Entry.InstancedStaticMeshComponent)
546 if (SetUnrealDefaultMaterial)
548 for (int32 i = 0; i < Entry.InstancedStaticMeshComponent->GetNumMaterials(); ++i)
550 UMaterialInterface* CurrentMaterial = Entry.InstancedStaticMeshComponent->GetMaterial(i);
551 Entry.Materials.Add(CurrentMaterial);
553 for (int32 i = 0; i < Entry.InstancedStaticMeshComponent->GetNumMaterials(); ++i)
555 Entry.InstancedStaticMeshComponent->SetMaterial(i, DefaultMaterial);
560 if (Entry.Materials.Num() > 0)
562 for (int32 i = 0; i < Entry.Materials.Num(); ++i)
564 UMaterialInterface* OriginalMaterial = Entry.Materials[i];
565 Entry.InstancedStaticMeshComponent->SetMaterial(i, OriginalMaterial);
578 auto& ISMComponent = Entry.InstancedStaticMeshComponent;
582 if (!Entry.UsingProxy)
584 ISMComponent->SetStaticMesh(
CubeMesh.Get());
585 Entry.UsingProxy =
true;
589 ISMComponent->SetStaticMesh(Entry.Mesh.Get());
590 Entry.UsingProxy =
false;
599 UWorld* World = GetWorld();
608 UInstancedStaticMeshComponent* ISMComponent = Entry.InstancedStaticMeshComponent;
614 ISMComponent->SetVisibility(
false);
617 TArray<FTransform> Transforms;
619 const int32 InstanceCount = ISMComponent->GetInstanceCount();
621 for (int32 i = 0; i < InstanceCount; i++)
624 ISMComponent->GetInstanceTransform(i,
Transform);
629 UClass* Class = Entry.ActorClass;
630 for (
const FTransform&
Transform : Transforms)
632 AActor* SpawnedActor = World->SpawnActorDeferred<AActor>(Class,
Transform);
639 ISMComponent->DestroyComponent();
648 UWorld* World = GetWorld();
657 UInstancedStaticMeshComponent* ISMComponent = Entry.InstancedStaticMeshComponent;
663 UClass* Class = Entry.ActorClass;
665 AActor* SpawnedActor = World->SpawnActorDeferred<AActor>(Class,
Transform);
677 ISMComponent->SetCullDistances(Min, Max);
687 if (!ISM || Index < 0 || Index >= ISM->GetInstanceCount())
692 UWorld* World = GetWorld();
695 FTransform InstanceTransform;
696 ISM->GetInstanceTransform(Index, InstanceTransform);
703 if (!Entry || !World)
708 if (OnlyTree && !Entry->
IsTree)
720 AActor* SpawnedActor = World->SpawnActorDeferred<AActor>(ActorClass, InstanceTransform);
729 SpawnedActor->FinishSpawning(InstanceTransform);
732 return ISM->RemoveInstance(Index);
745 if (!Entry || (!Entry->
IsTree && OnlyTrees))
753 TArray<int32> OverlappingInstances = Comp->GetInstancesOverlappingBox(AreaBox);
754 for (int32 InstanceIndex : OverlappingInstances)
756 Comp->RemoveInstance(InstanceIndex);
int32 GetInstanceEndCullDistance() const
bool UpdateTransformAutomatically() const
bool AddToInstancedRenderer
void InstanceAdded(int32 CompIndex, int32 InstanceNum)
bool HasAlternativeMaterial() const
int32 GetInstanceStartCullDistance() const
UStaticMeshComponent * GetStaticMeshComponent() const
bool GetAllowWorldPositionOffsetDisable() const
bool AddActorToInstancedRendering(AInstancedActor *InstancedActor)
void RemoveInstance(int32 ComponentIndex, int32 InstanceNumber)
bool FindOrAddUniqueMesh(UStaticMeshComponent *StaticMeshComponent, AInstancedActor *InstancedActor, int32 &InstanceNum)
void OnWeatherParametersChanged(FWeatherParameters WeatherParameters)
void SpawnAllInstancesBackActors()
void SetShadowCacheBehaviour(EShadowCacheInvalidationBehavior ShadowCacheInvalidationBehaviour)
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override
void UpdateInstanceTransform(int32 ComponentIndex, int32 InstanceNumber, const FTransform &NewTransform)
void SetInstancedRendering(bool ShouldRender)
virtual void BeginPlay() override
void OnGraphicsSettingsChanged(FGlobalGraphicsSettings GraphicsSettings)
bool SpawnInstanceBackToActor(UInstancedStaticMeshComponent *ISM, int32 Index, bool OnlyTree)
void UpdateRenderDistances()
TArray< FInstanceEntry > InstanceEntries
void SetWorldPositionOffsetDistance(int32 NewWPODistance)
void SetRenderWorldPositionOffet(bool RenderWPO)
FWeatherParameters CurrentWeatherParameters
TWeakObjectPtr< UStaticMesh > CubeMesh
int32 WorldPositionOffsetDistance
void DestroyOverlappingInstancesBox(UInstancedStaticMeshComponent *ISM, FBox AreaBox, bool OnlyTrees)
void SetRenderCustomDepth(bool Enabled)
AInstancedRenderer(const FObjectInitializer &ObjectInitializer)
void DebugMaterialImpact(bool SetUnrealDefaultMaterial)
void SetComponentIndexVisibility(int32 Index, bool Visible)
static AInstancedRenderer * Instance
const FWeatherParameters & GetCurrentWeather() const
FLevelEventDelegate_WeatherChanged OnWeatherChanged
int32 GetWorldPositionOffsetRenderDistance() const
FGraphicsSettingsDelegate OnGraphicsSettingsChanged
static UAgrarsenseSettings * GetAgrarsenseSettings()
static AWeather * GetWeatherActor(const UObject *WorldContextObject)
int32 WorldPositionOffsetDistance
EShadowCacheInvalidationBehavior FoliageShadowCacheInvalidationBehaviour
TWeakObjectPtr< UStaticMesh > Mesh
TArray< UMaterialInterface * > Materials
int32 CustomDepthStencilValue
bool AllowWorldPositionOffsetDisable
TSubclassOf< AActor > ActorClass
UInstancedStaticMeshComponent * InstancedStaticMeshComponent