Agrarsense
InstancedRenderer.cpp
Go to the documentation of this file.
1// Copyright (c) 2023 FrostBit Software Lab at the Lapland University of Applied Sciences
2//
3// This work is licensed under the terms of the MIT license.
4// For a copy, see <https://opensource.org/licenses/MIT>.
5
6#include "InstancedRenderer.h"
7#include "InstancedActor.h"
8
12
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"
19
21
22AInstancedRenderer::AInstancedRenderer(const FObjectInitializer& ObjectInitializer)
23 : Super(ObjectInitializer)
24{
25 RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
26 RootComponent->SetMobility(EComponentMobility::Static);
27 PrimaryActorTick.bCanEverTick = false;
28
29 // Load a default cube mesh asset
30 static ConstructorHelpers::FObjectFinder<UStaticMesh> DefaultMesh(TEXT("/Engine/BasicShapes/Cube.Cube"));
31 if (DefaultMesh.Succeeded())
32 {
33 CubeMesh = DefaultMesh.Object;
34 }
35}
36
38{
39 Super::BeginPlay();
40
41 // SetActorEnableCollision(false);
42
43 if (Instance == nullptr)
44 {
45 Instance = this;
46 }
47 else
48 {
49#if WITH_EDITOR
50 UE_LOG(LogTemp, Warning, TEXT("InstancedRenderer.cpp: Instance already exists. Destroying this actor.."));
51#endif
52 Destroy();
53 }
54
55 UWorld* World = GetWorld();
56
58 if (Settings)
59 {
62 }
63
65 if (Weather)
66 {
69 }
70
71#if WITH_EDITOR
72 FTimerHandle Handle;
73 World->GetTimerManager().SetTimer(Handle, FTimerDelegate::CreateLambda([this] {
74 LogInfo();
75 }),
76 1.0f, false);
77
78 if (!InstanceEntries.IsEmpty() && InstanceEntries[0].UsingProxy)
79 {
81 }
82#endif
83}
84
86{
87#if WITH_EDITOR
88 UE_LOG(LogTemp, Warning, TEXT("InstancedRenderer.cpp: Unique mesh entries: %d | Total Instance Count: %d"), InstanceEntries.Num(), TotalInstanceCount);
89#endif
90}
91
92void AInstancedRenderer::EndPlay(const EEndPlayReason::Type EndPlayReason)
93{
94 Super::EndPlay(EndPlayReason);
95
96 if (Instance == this)
97 {
98 Instance = nullptr;
99 }
100 else
101 {
102 return;
103 }
104
105 UWorld* World = GetWorld();
106
108 if (Settings)
109 {
111 }
112
114 if (Weather)
115 {
117 }
118
119 // Destroy all Instanced entries
120 for (FInstanceEntry& Entry : InstanceEntries)
121 {
122 if (Entry.InstancedStaticMeshComponent)
123 {
124 Entry.InstancedStaticMeshComponent->ClearInstances();
125 Entry.InstancedStaticMeshComponent->UnregisterComponent();
126 Entry.InstancedStaticMeshComponent->DetachFromComponent(FDetachmentTransformRules::KeepRelativeTransform);
127 Entry.InstancedStaticMeshComponent->DestroyComponent();
128 Entry.InstancedStaticMeshComponent = nullptr;
129 }
130
131 Entry.Materials.Empty();
132 Entry.Mesh.Reset();
133 }
134
135 InstanceEntries.Empty();
136}
137
139{
140 if (!InstancedActor)
141 {
142#if WITH_EDITOR
143 UE_LOG(LogTemp, Warning, TEXT("InstancedRenderer.cpp: Actor is null!"));
144#endif
145 return false;
146 }
147
148 if (InstancedActor->UpdateTransformAutomatically())
149 {
150#if WITH_EDITOR
151 UE_LOG(LogTemp, Warning, TEXT("InstancedRenderer.cpp: AInstancedActor %s is set to update transform automatically."), *InstancedActor->GetName());
152#endif
153 return false;
154 }
155
156 UStaticMeshComponent* StaticMeshComponent = InstancedActor->GetStaticMeshComponent();
157 if (!StaticMeshComponent)
158 {
159#if WITH_EDITOR
160 UE_LOG(LogTemp, Warning, TEXT("InstancedRenderer.cpp: AInstancedActor %s UStaticMeshComponent is null!"), *InstancedActor->GetName());
161#endif
162 return false;
163 }
164
165 UStaticMesh* Mesh = StaticMeshComponent->GetStaticMesh();
166 if (!Mesh)
167 {
168#if WITH_EDITOR
169 UE_LOG(LogTemp, Warning, TEXT("InstancedRenderer.cpp: AInstancedActor %s UStaticMeshComponent mesh is null!"), *InstancedActor->GetName());
170#endif
171 return false;
172 }
173
174 int32 ComponentIndex = 9999;
175
176 // Find or create InstancedStaticMeshComponents array index for this instance
177 bool Found = FindOrAddUniqueMesh(StaticMeshComponent, InstancedActor, ComponentIndex);
178
179 if (!Found || !InstanceEntries.IsValidIndex(ComponentIndex))
180 {
181#if WITH_EDITOR
182 UE_LOG(LogTemp, Warning, TEXT("InstancedRenderer.cpp: ComponentIndex is not valid!"));
183#endif
184 return false;
185 }
186
187 UInstancedStaticMeshComponent* InstancedStaticMeshComponent = InstanceEntries[ComponentIndex].InstancedStaticMeshComponent;
188 if (!InstancedStaticMeshComponent)
189 {
190#if WITH_EDITOR
191 UE_LOG(LogTemp, Warning, TEXT("InstancedRenderer.cpp: UInstancedStaticMeshComponent is nullptr!"));
192#endif
193 return false;
194 }
195
196 // Get the StaticMeshComponent transform and add instance
197 FTransform ActorTransform = StaticMeshComponent->GetComponentTransform();
198 InstancedStaticMeshComponent->AddInstance(ActorTransform);
199
200 int32 InstanceIndex = InstancedStaticMeshComponent->GetNumRenderInstances() - 1;
201
202 // Notify the actor that instance has been added successfully.
203 InstancedActor->InstanceAdded(ComponentIndex, InstanceIndex);
204 // InstancedActor->Destroy();
205
207
208 return true;
209}
210
211bool AInstancedRenderer::FindOrAddUniqueMesh(UStaticMeshComponent* StaticMeshComponent, AInstancedActor* InstancedActor, int32& InstanceNum)
212{
213 if (InstancedActor == nullptr || StaticMeshComponent == nullptr)
214 {
215#if WITH_EDITOR
216 UE_LOG(LogTemp, Warning, TEXT("StaticInstancedRenderer.cpp: StaticMeshComponent or InstancedActor is null!"));
217#endif
218 return false;
219 }
220
221 UStaticMesh* Mesh = StaticMeshComponent->GetStaticMesh();
222
223 if (!InstancedActor->HasAlternativeMaterial())
224 {
225 const int32 Num = InstanceEntries.Num();
226 for (int32 Index = 0; Index < Num; ++Index)
227 {
228 if (InstanceEntries[Index].Mesh == Mesh)
229 {
230 InstanceNum = Index;
231 return true;
232 }
233 }
234 }
235
236 const int32 NumberOfMaterials = StaticMeshComponent->GetNumMaterials();
237
238 TArray<UMaterialInterface*> MeshMaterials;
239 MeshMaterials.Reserve(NumberOfMaterials);
240
241 for (int32 i = 0; i < NumberOfMaterials; i++)
242 {
243 MeshMaterials.Add(StaticMeshComponent->GetMaterial(i));
244 }
245
246 const int Size = InstanceEntries.Num();
247 for (int32 Index = 0; Index < Size; ++Index)
248 {
249 if (InstanceEntries[Index].Mesh == Mesh)
250 {
251 bool bMaterialsMatch = true;
252
253 for (int32 MaterialIndex = 0; MaterialIndex < InstanceEntries[Index].Materials.Num(); ++MaterialIndex)
254 {
255 UMaterialInterface* UniqueMeshMaterial = InstanceEntries[Index].Materials[MaterialIndex];
256 UMaterialInterface* MeshMaterial = MeshMaterials[MaterialIndex];
257 if (UniqueMeshMaterial != MeshMaterial)
258 {
259 bMaterialsMatch = false;
260 }
261 }
262
263 if (bMaterialsMatch)
264 {
265 InstanceNum = Index;
266 return true;
267 }
268 }
269 }
270
271 // Else this is completely new mesh and/or mesh with alternative material,
272 // we need to create new FInstanceEntry and UInstancedStaticMeshComponent for it
273
274 // Create and setup new UInstancedStaticMeshComponent
275 UInstancedStaticMeshComponent* InstancedStaticMeshComponent = NewObject<UInstancedStaticMeshComponent>(this);
276
277 UClass* ClassPtr = InstancedActor->GetClass();
278
279 FString Name = IInteractable::Execute_GetInteractableName(InstancedActor).ToString();
280
281 if (Name.IsEmpty())
282 {
283 Name = ClassPtr->GetAuthoredName();
284 if (Name.EndsWith(TEXT("_C")))
285 {
286 Name = Name.LeftChop(2);
287 }
288 if (Name.StartsWith(TEXT("BP_")))
289 {
290 Name = Name.RightChop(3);
291 }
292 Name.ReplaceInline(TEXT("_"), TEXT(" "));
293 Name.ReplaceInline(TEXT("Instanced"), TEXT(" "));
294 }
295
296 // Generate a 4-character GUID
297 FString Guid = FGuid::NewGuid().ToString().Left(4);
298
299 Name = FString::Printf(TEXT("%s(_%s_)"), *Name, *Guid);
300
301 InstancedStaticMeshComponent->Rename(*Name);
302
303 // Add Actor component list
304 InstancedStaticMeshComponent->AttachToComponent(GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
305 AddInstanceComponent(InstancedStaticMeshComponent);
306
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);
317 InstancedStaticMeshComponent->InstanceStartCullDistance = InstancedActor->GetInstanceStartCullDistance();
318 InstancedStaticMeshComponent->InstanceEndCullDistance = InstancedActor->GetInstanceEndCullDistance();
319
320 // Set WPO distance and evaluation
321 InstancedStaticMeshComponent->SetWorldPositionOffsetDisableDistance(WorldPositionOffsetDistance);
322 /*InstancedStaticMeshComponent->SetEvaluateWorldPositionOffset(true);
323 InstancedStaticMeshComponent->SetEvaluateWorldPositionOffsetInRayTracing(true);*/
324
325 bool AllowWorldPositionOffsetDisable = InstancedActor->GetAllowWorldPositionOffsetDisable();
326
327 // For whatever reason this only works when done a bit later..
328 FTimerHandle Handle;
329 GetWorld()->GetTimerManager().SetTimer(Handle, FTimerDelegate::CreateLambda([this, InstancedStaticMeshComponent, AllowWorldPositionOffsetDisable] {
330 if (InstancedStaticMeshComponent && AllowWorldPositionOffsetDisable && CurrentWeatherParameters.WindIntensity < 0.05f)
331 {
332 InstancedStaticMeshComponent->SetEvaluateWorldPositionOffset(false);
333 InstancedStaticMeshComponent->SetEvaluateWorldPositionOffsetInRayTracing(false);
334 }
335 }),
336 0.350f, false);
337
338 // Thermal and Semantic camera use custom depth.
339 // RenderCustomDepth is set to false, if no Thermal or Semantic camera exists in the level,
340 // and is automatically set to true/false for all instances when they are spawned/destroyed.
341 // Doing this saves a lot rendering performance.
342 InstancedStaticMeshComponent->SetRenderCustomDepth(true);
343 InstancedStaticMeshComponent->SetCustomDepthStencilValue(StaticMeshComponent->CustomDepthStencilValue);
344
345 // Huge performance implication, recommend is Rigid (especially for Foliage with WPO).
346 // https://docs.unrealengine.com/5.3/en-US/PythonAPI/class/ShadowCacheInvalidationBehavior.html
347 InstancedStaticMeshComponent->ShadowCacheInvalidationBehavior = EShadowCacheInvalidationBehavior::Rigid;
348
349 // Create new FInstanceEntry
350 FInstanceEntry InstanceEntry;
351 InstanceEntry.InstancedStaticMeshComponent = InstancedStaticMeshComponent;
352 InstanceEntry.ActorClass = ClassPtr;
353 InstanceEntry.Mesh = Mesh;
354 InstanceEntry.Materials = MeshMaterials;
355 InstanceEntry.InstanceEntryIndex = InstanceEntries.Num();
356 InstanceEntry.CustomDepthStencilValue = StaticMeshComponent->CustomDepthStencilValue;
357 InstanceEntry.AllowWorldPositionOffsetDisable = AllowWorldPositionOffsetDisable;
358 InstanceEntry.IsTree = InstancedActor->IsTreeActor();
359
360 // Set materials to newly created UInstancedStaticMeshComponent
361 const int32 MaterialCount = MeshMaterials.Num();
362 for (int32 MaterialIndex = 0; MaterialIndex < MaterialCount; ++MaterialIndex)
363 {
364 UMaterialInterface* MaterialInterface = InstanceEntry.Materials[MaterialIndex];
365
366 if (MaterialInterface)
367 {
368 InstancedStaticMeshComponent->SetMaterial(MaterialIndex, MaterialInterface);
369
370#if WITH_EDITOR
371 // Validate in Editor that material has bUsedWithInstancedStaticMeshes on
372 UMaterial* Material = Cast<UMaterial>(MaterialInterface);
373 if (Material)
374 {
375 if (!Material->bUsedWithInstancedStaticMeshes)
376 {
377 UE_LOG(LogTemp, Warning, TEXT("InstancedRenderer.cpp: Material '%s' bUsedWithInstancedStaticMeshes is not set to true!"), *Material->GetName());
378 }
379 }
380#endif
381 }
382 }
383
384 InstanceEntries.Add(InstanceEntry);
385
386 InstanceNum = InstanceEntries.Num() - 1;
387
388 return true;
389}
390
391void AInstancedRenderer::UpdateInstanceTransform(int32 ComponentIndex, int32 InstanceNumber, const FTransform& NewTransform)
392{
393 if (!InstanceEntries.IsValidIndex(ComponentIndex))
394 {
395 return;
396 }
397
398 FInstanceEntry& Entry = InstanceEntries[ComponentIndex];
399
401 {
402 Entry.InstancedStaticMeshComponent->UpdateInstanceTransform(InstanceNumber, NewTransform, true, true);
403 }
404}
405
406void AInstancedRenderer::RemoveInstance(int32 ComponentIndex, int32 InstanceNumber)
407{
408 if (!InstanceEntries.IsValidIndex(ComponentIndex))
409 {
410 return;
411 }
412
413 FInstanceEntry& Entry = InstanceEntries[ComponentIndex];
414
416 {
417 // Ensure InstanceNumber is valid before attempting to remove
418 if (InstanceNumber >= 0 && InstanceNumber < Entry.InstancedStaticMeshComponent->GetInstanceCount())
419 {
420 Entry.InstancedStaticMeshComponent->RemoveInstance(InstanceNumber);
422 }
423 }
424}
425
427{
428 Rendering = ShouldRender;
429 for (const FInstanceEntry& Entry : InstanceEntries)
430 {
431 if (Entry.InstancedStaticMeshComponent)
432 {
433 Entry.InstancedStaticMeshComponent->SetVisibility(Rendering);
434 }
435 }
436
437#if WITH_EDITOR
438 UE_LOG(LogTemp, Warning, TEXT("InstancedRenderer.cpp: Rendering: %s"), (Rendering ? TEXT("true") : TEXT("false")));
439#endif
440}
441
443{
444 if (!InstanceEntries.IsValidIndex(Index))
445 {
446#if WITH_EDITOR
447 UE_LOG(LogTemp, Warning, TEXT("InstancedRenderer.cpp: Index %d is not valid!"), Index);
448#endif
449 return;
450 }
451
452 FInstanceEntry& Entry = InstanceEntries[Index];
453
455 {
456 Entry.InstancedStaticMeshComponent->SetVisibility(Visible);
457 }
458}
459
461{
464}
465
467{
468 CurrentWeatherParameters = WeatherParameters;
469
470 bool IsWinter = WeatherParameters.IsWinterSeason();
471 bool IsWindy = WeatherParameters.WindIntensity > 0.05f;
472 bool ShouldRenderWPO = IsWinter || IsWindy;
473
474 // Only update if the rendering state has changed
475 if (RenderingWPO != ShouldRenderWPO)
476 {
477 RenderingWPO = ShouldRenderWPO;
478 SetRenderWorldPositionOffet(ShouldRenderWPO);
479 }
480}
481
483{
484 for (FInstanceEntry& Entry : InstanceEntries)
485 {
486 if (Entry.InstancedStaticMeshComponent)
487 {
488 Entry.InstancedStaticMeshComponent->SetRenderCustomDepth(Enabled);
489 }
490 }
491}
492
494{
495 if (WorldPositionOffsetDistance == NewWPODistance)
496 {
497 return;
498 }
499
500 WorldPositionOffsetDistance = NewWPODistance;
501
502 for (FInstanceEntry& Entry : InstanceEntries)
503 {
504 if (Entry.InstancedStaticMeshComponent)
505 {
506 Entry.InstancedStaticMeshComponent->SetWorldPositionOffsetDisableDistance(WorldPositionOffsetDistance);
507 }
508 }
509}
510
511void AInstancedRenderer::SetShadowCacheBehaviour(EShadowCacheInvalidationBehavior ShadowCacheInvalidationBehaviour)
512{
513 for (FInstanceEntry& Entry : InstanceEntries)
514 {
515 if (Entry.InstancedStaticMeshComponent)
516 {
517 Entry.InstancedStaticMeshComponent->ShadowCacheInvalidationBehavior = ShadowCacheInvalidationBehaviour;
518 }
519 }
520}
521
523{
524 for (FInstanceEntry& Entry : InstanceEntries)
525 {
526 if (Entry.AllowWorldPositionOffsetDisable && Entry.InstancedStaticMeshComponent)
527 {
528 Entry.InstancedStaticMeshComponent->SetEvaluateWorldPositionOffset(RenderWPO);
529 Entry.InstancedStaticMeshComponent->SetEvaluateWorldPositionOffsetInRayTracing(RenderWPO);
530 }
531 }
532}
533
534void AInstancedRenderer::DebugMaterialImpact(bool SetUnrealDefaultMaterial)
535{
536 UMaterialInterface* DefaultMaterial = nullptr;
537 if (SetUnrealDefaultMaterial)
538 {
539 DefaultMaterial = UMaterial::GetDefaultMaterial(EMaterialDomain(0));
540 }
541
542 for (FInstanceEntry& Entry : InstanceEntries)
543 {
544 if (Entry.AllowWorldPositionOffsetDisable && Entry.InstancedStaticMeshComponent)
545 {
546 if (SetUnrealDefaultMaterial)
547 {
548 for (int32 i = 0; i < Entry.InstancedStaticMeshComponent->GetNumMaterials(); ++i)
549 {
550 UMaterialInterface* CurrentMaterial = Entry.InstancedStaticMeshComponent->GetMaterial(i);
551 Entry.Materials.Add(CurrentMaterial);
552 }
553 for (int32 i = 0; i < Entry.InstancedStaticMeshComponent->GetNumMaterials(); ++i)
554 {
555 Entry.InstancedStaticMeshComponent->SetMaterial(i, DefaultMaterial);
556 }
557 }
558 else
559 {
560 if (Entry.Materials.Num() > 0)
561 {
562 for (int32 i = 0; i < Entry.Materials.Num(); ++i)
563 {
564 UMaterialInterface* OriginalMaterial = Entry.Materials[i];
565 Entry.InstancedStaticMeshComponent->SetMaterial(i, OriginalMaterial);
566 }
567 }
568 }
569 }
570 }
571}
572
574{
575#if WITH_EDITOR
576 for (FInstanceEntry& Entry : InstanceEntries)
577 {
578 auto& ISMComponent = Entry.InstancedStaticMeshComponent;
579
580 if (ISMComponent)
581 {
582 if (!Entry.UsingProxy)
583 {
584 ISMComponent->SetStaticMesh(CubeMesh.Get());
585 Entry.UsingProxy = true;
586 }
587 else
588 {
589 ISMComponent->SetStaticMesh(Entry.Mesh.Get());
590 Entry.UsingProxy = false;
591 }
592 }
593 }
594#endif
595}
596
598{
599 UWorld* World = GetWorld();
600
601 if (InstanceEntries.IsEmpty() || !World)
602 {
603 return;
604 }
605
606 for (FInstanceEntry& Entry : InstanceEntries)
607 {
608 UInstancedStaticMeshComponent* ISMComponent = Entry.InstancedStaticMeshComponent;
609 if (!ISMComponent)
610 {
611 continue;
612 }
613
614 ISMComponent->SetVisibility(false);
615
616 // Extract transforms from ISM component into TArray
617 TArray<FTransform> Transforms;
618
619 const int32 InstanceCount = ISMComponent->GetInstanceCount();
620
621 for (int32 i = 0; i < InstanceCount; i++)
622 {
623 FTransform Transform;
624 ISMComponent->GetInstanceTransform(i, Transform);
625 Transforms.Add(Transform);
626 }
627
628 // Spawn actors to level
629 UClass* Class = Entry.ActorClass;
630 for (const FTransform& Transform : Transforms)
631 {
632 AActor* SpawnedActor = World->SpawnActorDeferred<AActor>(Class, Transform);
633 if (SpawnedActor)
634 {
635 SpawnedActor->FinishSpawning(Transform);
636 }
637 }
638
639 ISMComponent->DestroyComponent();
640 }
641
642 InstanceEntries.Empty();
643}
644
646{
647#if WITH_EDITOR
648 UWorld* World = GetWorld();
649
650 if (InstanceEntries.IsEmpty() || !World)
651 {
652 return;
653 }
654
655 for (FInstanceEntry& Entry : InstanceEntries)
656 {
657 UInstancedStaticMeshComponent* ISMComponent = Entry.InstancedStaticMeshComponent;
658 if (!ISMComponent)
659 {
660 continue;
661 }
662
663 UClass* Class = Entry.ActorClass;
664 FTransform Transform;
665 AActor* SpawnedActor = World->SpawnActorDeferred<AActor>(Class, Transform);
666 if (SpawnedActor)
667 {
668 SpawnedActor->FinishSpawning(Transform);
669 }
670
671 AInstancedActor* Actor = Cast<AInstancedActor>(SpawnedActor);
672 if (Actor)
673 {
674
675 int32 Min = Actor->GetInstanceStartCullDistance();
676 int32 Max = Actor->GetInstanceEndCullDistance();
677 ISMComponent->SetCullDistances(Min, Max);
678
679 Actor->Destroy();
680 }
681 }
682#endif
683}
684
685bool AInstancedRenderer::SpawnInstanceBackToActor(UInstancedStaticMeshComponent* ISM, int32 Index, bool OnlyTree)
686{
687 if (!ISM || Index < 0 || Index >= ISM->GetInstanceCount())
688 {
689 return false;
690 }
691
692 UWorld* World = GetWorld();
693
694 // Get the transform for the specified instance
695 FTransform InstanceTransform;
696 ISM->GetInstanceTransform(Index, InstanceTransform);
697
698 // Get the actor class from the associated entry
699 FInstanceEntry* Entry = InstanceEntries.FindByPredicate([ISM](const FInstanceEntry& InEntry) {
700 return InEntry.InstancedStaticMeshComponent == ISM;
701 });
702
703 if (!Entry || !World)
704 {
705 return false;
706 }
707
708 if (OnlyTree && !Entry->IsTree)
709 {
710 return false;
711 }
712
713 UClass* ActorClass = Entry->ActorClass;
714 if (!ActorClass)
715 {
716 return false;
717 }
718
719 // Spawn the actor
720 AActor* SpawnedActor = World->SpawnActorDeferred<AActor>(ActorClass, InstanceTransform);
721 if (SpawnedActor)
722 {
723 AInstancedActor* InstancedActor = Cast<AInstancedActor>(SpawnedActor);
724 if (InstancedActor)
725 {
726 InstancedActor->AddToInstancedRenderer = false;
727 }
728
729 SpawnedActor->FinishSpawning(InstanceTransform);
730
731 // Remove this ISM instance.
732 return ISM->RemoveInstance(Index);
733 }
734
735 return false;
736}
737
738void AInstancedRenderer::DestroyOverlappingInstancesBox(UInstancedStaticMeshComponent* ISM, FBox AreaBox, bool OnlyTrees)
739{
740 // Check incoming ISM exists in InstanceEntries
741 FInstanceEntry* Entry = InstanceEntries.FindByPredicate([ISM](const FInstanceEntry& InEntry) {
742 return InEntry.InstancedStaticMeshComponent == ISM;
743 });
744
745 if (!Entry || (!Entry->IsTree && OnlyTrees))
746 {
747 return;
748 }
749
750 // Remove all instances that overlap incoming AreaBox bounds
751 if (UInstancedStaticMeshComponent* Comp = Entry->InstancedStaticMeshComponent)
752 {
753 TArray<int32> OverlappingInstances = Comp->GetInstancesOverlappingBox(AreaBox);
754 for (int32 InstanceIndex : OverlappingInstances)
755 {
756 Comp->RemoveInstance(InstanceIndex);
757 }
758 }
759}
int32 GetInstanceEndCullDistance() const
bool UpdateTransformAutomatically() const
void InstanceAdded(int32 CompIndex, int32 InstanceNum)
bool HasAlternativeMaterial() const
int32 GetInstanceStartCullDistance() const
UStaticMeshComponent * GetStaticMeshComponent() const
bool GetAllowWorldPositionOffsetDisable() const
bool IsTreeActor() const
bool AddActorToInstancedRendering(AInstancedActor *InstancedActor)
void RemoveInstance(int32 ComponentIndex, int32 InstanceNumber)
bool FindOrAddUniqueMesh(UStaticMeshComponent *StaticMeshComponent, AInstancedActor *InstancedActor, int32 &InstanceNum)
void OnWeatherParametersChanged(FWeatherParameters WeatherParameters)
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)
TArray< FInstanceEntry > InstanceEntries
void SetWorldPositionOffsetDistance(int32 NewWPODistance)
void SetRenderWorldPositionOffet(bool RenderWPO)
FWeatherParameters CurrentWeatherParameters
TWeakObjectPtr< UStaticMesh > CubeMesh
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
Definition: Weather.h:76
FLevelEventDelegate_WeatherChanged OnWeatherChanged
Definition: Weather.h:85
int32 GetWorldPositionOffsetRenderDistance() const
FGraphicsSettingsDelegate OnGraphicsSettingsChanged
static UAgrarsenseSettings * GetAgrarsenseSettings()
static AWeather * GetWeatherActor(const UObject *WorldContextObject)
EShadowCacheInvalidationBehavior FoliageShadowCacheInvalidationBehaviour
TWeakObjectPtr< UStaticMesh > Mesh
TArray< UMaterialInterface * > Materials
int32 CustomDepthStencilValue
bool AllowWorldPositionOffsetDisable
TSubclassOf< AActor > ActorClass
UInstancedStaticMeshComponent * InstancedStaticMeshComponent