// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "UObject/ObjectMacros.h" #include "Templates/SubclassOf.h" #include "Engine/World.h" #include "GameFramework/Actor.h" #include "FoliageType_InstancedStaticMesh.h" #include "FoliageInstanceBase.h" #include "InstancedFoliage.h" #include "InstancedFoliageCustomVersion.h" #include "ISMPartition/ISMPartitionActor.h" #include "InstancedFoliageActor.generated.h" #if WITH_EDITOR class UProceduralFoliageComponent; struct FActorPartitionIdentifier; #endif // Function for filtering out hit components during FoliageTrace typedef TFunction FFoliageTraceFilterFunc; UCLASS(notplaceable, hidecategories = (Object, Rendering, Mobility), MinimalAPI, NotBlueprintable) class AInstancedFoliageActor : public AISMPartitionActor { GENERATED_UCLASS_BODY() public: #if WITH_EDITORONLY_DATA // Cross level references cache for instances base FFoliageInstanceBaseCache InstanceBaseCache; UActorComponent* GetBaseComponentFromBaseId(const FFoliageInstanceBaseId& BaseId) const; #endif// WITH_EDITORONLY_DATA private: friend struct FFoliageInstanceBaseCache; TMap, TUniqueObj> FoliageInfos; public: FOLIAGE_API bool ForEachFoliageInfo(TFunctionRef InOperation); const TMap>& GetFoliageInfos() const { return ObjectPtrDecay(FoliageInfos); } FOLIAGE_API TUniqueObj& AddFoliageInfo(UFoliageType* FoliageType); FOLIAGE_API TUniqueObj& AddFoliageInfo(UFoliageType* FoliageType, TUniqueObj&& FoliageInfo); FOLIAGE_API bool RemoveFoliageInfoAndCopyValue(UFoliageType* FoliageType, TUniqueObj& OutFoliageInfo); //~ Begin UObject Interface. virtual void Serialize(FArchive& Ar) override; virtual void PostLoad() override; #if WITH_EDITORONLY_DATA FOLIAGE_API static void DeclareConstructClasses(TArray& OutConstructClasses, const UClass* SpecificSubclass); #endif static void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector); //~ End UObject Interface. //~ Begin AActor Interface. // we don't want to have our components automatically destroyed by the Blueprint code #if WITH_EDITOR virtual void RerunConstructionScripts() override {} #endif virtual bool IsLevelBoundsRelevant() const override { return false; } public: #if WITH_EDITOR bool CanDeleteSelectedActor(FText& OutReason) const override { return true; } bool CanEditFoliageInstance(const FFoliageInstanceId& InstanceId) const; bool CanMoveFoliageInstance(const FFoliageInstanceId& InstanceId, const ETypedElementWorldType WorldType) const; bool GetFoliageInstanceTransform(const FFoliageInstanceId& InstanceId, FTransform& OutInstanceTransform, bool bWorldSpace) const; bool SetFoliageInstanceTransform(const FFoliageInstanceId& InstanceId, const FTransform& InstanceTransform, bool bWorldSpace, bool bTeleport); void NotifyFoliageInstanceMovementStarted(const FFoliageInstanceId& InstanceId); void NotifyFoliageInstanceMovementOngoing(const FFoliageInstanceId& InstanceId); void NotifyFoliageInstanceMovementEnded(const FFoliageInstanceId& InstanceId); void NotifyFoliageInstanceSelectionChanged(const FFoliageInstanceId& InstanceId, const bool bIsSelected); bool DeleteFoliageInstances(TArrayView InstanceIds); bool DuplicateFoliageInstances(TArrayView InstanceIds, TArray& OutNewInstanceIds); UFoliageType* GetFoliageTypeForInfo(const FFoliageInfo* FoliageInfo) const; #endif protected: #if WITH_EDITOR void HandleFoliageInstancePreMove(const FFoliageInstanceId& InstanceId); void HandleFoliageInstancePostMove(const FFoliageInstanceId& InstanceId); #endif //~ ISMInstanceManagerProvider interface virtual ISMInstanceManager* GetSMInstanceManager(const FSMInstanceId& InstanceId) override; // Default InternalTakeRadialDamage behavior finds and scales damage for the closest component which isn't appropriate for foliage. virtual float InternalTakeRadialDamage(float Damage, struct FRadialDamageEvent const& RadialDamageEvent, class AController* EventInstigator, AActor* DamageCauser) override; public: #if WITH_EDITOR FOLIAGE_API void EnterEditMode(); FOLIAGE_API void ExitEditMode(); virtual void PostInitProperties() override; virtual void BeginDestroy() override; virtual void Destroyed() override; virtual bool IsListedInSceneOutliner() const override; FOLIAGE_API void CleanupDeletedFoliageType(); FOLIAGE_API void DetectFoliageTypeChangeAndUpdate(); virtual uint32 GetDefaultGridSize(UWorld* InWorld) const override; virtual bool ShouldIncludeGridSizeInName(UWorld* InWorld, const FActorPartitionIdentifier& InIdentifier) const override; // Delegate type for selection change events DECLARE_MULTICAST_DELEGATE_TwoParams(FOnSelectionChanged, bool, const TArray&); FOLIAGE_API static FOnSelectionChanged SelectionChanged; DECLARE_MULTICAST_DELEGATE_OneParam(FOnInstanceCoundChanged, const UFoliageType*); FOLIAGE_API static FOnInstanceCoundChanged InstanceCountChanged; #endif //~ End AActor Interface. // Performs a reverse lookup from a source object to a local foliage type (i.e. the foliage type owned exclusively by this IFA) FOLIAGE_API UFoliageType* GetLocalFoliageTypeForSource(const UObject* InSource, FFoliageInfo** OutMeshInfo = nullptr); // Performs a reverse lookup from a source object to all the foliage types that are currently using that object (includes assets and blueprint classes) FOLIAGE_API void GetAllFoliageTypesForSource(const UObject* InSource, TArray& OutFoliageTypes); FOLIAGE_API FFoliageInfo* FindFoliageTypeOfClass(TSubclassOf Class); // Finds the number of instances overlapping with the sphere. FOLIAGE_API int32 GetOverlappingSphereCount(const UFoliageType* FoliageType, const FSphere& Sphere) const; // Finds the number of instances overlapping with the box. FOLIAGE_API int32 GetOverlappingBoxCount(const UFoliageType* FoliageType, const FBox& Box) const; // Finds all instances in the provided box and get their transforms FOLIAGE_API void GetOverlappingBoxTransforms(const UFoliageType* FoliageType, const FBox& Box, TArray& OutTransforms) const; // Perf Warnin: potentially slow! Dev-only use recommended. // Returns list of meshes and counts for all nearby instances. OutCounts accumulates between runs. FOLIAGE_API void GetOverlappingMeshCounts(const FSphere& Sphere, TMap& OutCounts) const; // Finds a mesh entry FOLIAGE_API FFoliageInfo* FindInfo(const UFoliageType* InType); // Finds a mesh entry FOLIAGE_API const FFoliageInfo* FindInfo(const UFoliageType* InType) const; /** * Get the instanced foliage actor for the current streaming level. * * @param InWorld World to create the foliage instance in * @param bCreateIfNone Create if doesnt already exist * returns pointer to foliage object instance */ static FOLIAGE_API AInstancedFoliageActor* GetInstancedFoliageActorForCurrentLevel(const UWorld* InWorld, bool bCreateIfNone = false); /** * Get the instanced foliage actor for the specified streaming level. * @param InLevel Level to create the foliage instance in * @param bCreateIfNone Create if doesnt already exist * returns pointer to foliage object instance */ static FOLIAGE_API AInstancedFoliageActor* GetInstancedFoliageActorForLevel(ULevel* Level, bool bCreateIfNone = false); #if WITH_EDITOR /** * Get the instanced foliage actor for the specified params * @param InWorld World to create the foliage instance in * @param bCreateIfNone Create if doesnt already exist * @param InLevelHint Level hint for foliage instance creation * @param InLocationHint Location hint for foliage instance creation */ static FOLIAGE_API AInstancedFoliageActor* Get(UWorld* InWorld, bool bCreateIfNone, ULevel* InLevelHint = nullptr, const FVector& InLocationHint = FVector(ForceInitToZero)); static FOLIAGE_API AInstancedFoliageActor* GetDefault(UWorld* InWorld); static FOLIAGE_API void UpdateInstancePartitioning(UWorld* InWorld); static FOLIAGE_API void MoveSelectedInstancesToActorEditorContext(UWorld* InWorld); static FOLIAGE_API bool FoliageTrace(const UWorld* InWorld, FHitResult& OutHit, const FDesiredFoliageInstance& DesiredInstance, FName InTraceTag = NAME_None, bool InbReturnFaceIndex = false, const FFoliageTraceFilterFunc& FilterFunc = FFoliageTraceFilterFunc(), bool bAverageNormal = false); static FOLIAGE_API bool CheckCollisionWithWorld(const UWorld* InWorld, const UFoliageType* Settings, const FFoliageInstance& Inst, const FVector& HitNormal, const FVector& HitLocation, UPrimitiveComponent* HitComponent); virtual void PreEditUndo() override; virtual void PostEditUndo() override; virtual void PostDuplicate(bool bDuplicateForPIE) override; virtual bool ShouldExport() override; virtual bool ShouldImport(FStringView ActorPropString, bool IsMovingLevel) override; // Called in response to BSP rebuilds to migrate foliage from obsolete to new components. FOLIAGE_API void MapRebuild(); // Moves instances based on the specified component to the current streaming level static FOLIAGE_API void MoveInstancesForComponentToCurrentLevel(UActorComponent* InComponent); static FOLIAGE_API void MoveInstancesForComponentToLevel(UActorComponent* InComponent, ULevel* TargetLevel); // Change all instances based on one component to a new component (possible in another level). // The instances keep the same world locations FOLIAGE_API void MoveInstancesToNewComponent(UPrimitiveComponent* InOldComponent, UPrimitiveComponent* InNewComponent); FOLIAGE_API void MoveInstancesToNewComponent(UPrimitiveComponent* InOldComponent, const FBox& InBoxWithInstancesToMove, UPrimitiveComponent* InNewComponent); static FOLIAGE_API void MoveInstancesToNewComponent(UWorld* InWorld, UPrimitiveComponent* InOldComponent, const FBox& InBoxWithInstancesToMove, UPrimitiveComponent* InNewComponent); static FOLIAGE_API void MoveInstancesToNewComponent(UWorld* InWorld, UPrimitiveComponent* InOldComponent, UPrimitiveComponent* InNewComponent); // Move selected instances to a foliage actor in target level FOLIAGE_API void MoveSelectedInstancesToLevel(ULevel* InTargetLevel); // Move all instances to a foliage actor in target level FOLIAGE_API void MoveAllInstancesToLevel(ULevel* InTargetLevel); // Move instances to a foliage actor in target level FOLIAGE_API void MoveInstancesToLevel(ULevel* InTargetLevel, TSet& InInstanceList, FFoliageInfo* InCurrentMeshInfo, UFoliageType* InFoliageType, bool bSelect = false); // Returns a map of Static Meshes and their placed instances attached to a component. FOLIAGE_API TMap> GetInstancesForComponent(UActorComponent* InComponent); // Deletes the instances attached to a component FOLIAGE_API void DeleteInstancesForComponent(UActorComponent* InComponent); FOLIAGE_API void DeleteInstancesForComponent(UActorComponent* InComponent, const UFoliageType* InFoliageType); // Deletes the instances attached to a component, traverses all foliage actors in the world static FOLIAGE_API void DeleteInstancesForComponent(UWorld* InWorld, UActorComponent* InComponent); // Deletes the instances spawned by a procedural component FOLIAGE_API bool DeleteInstancesForProceduralFoliageComponent(const UProceduralFoliageComponent* InProceduralFoliageComponent, bool bInRebuildTree); FOLIAGE_API bool DeleteInstancesForProceduralFoliageComponent(const FGuid& InProceduralGuid, bool bInRebuildTree); FOLIAGE_API bool DeleteInstancesForAllProceduralFoliageComponents(bool bInRebuildTree); /** @return True if any instances exist that were spawned by the given procedural component */ FOLIAGE_API bool ContainsInstancesFromProceduralFoliageComponent(const UProceduralFoliageComponent* InProceduralFoliageComponent); FOLIAGE_API bool ContainsInstancesFromProceduralFoliageComponent(const FGuid& InProceduralGuid); // Finds a mesh entry or adds it if it doesn't already exist FOLIAGE_API FFoliageInfo* FindOrAddMesh(UFoliageType* InType); FOLIAGE_API UFoliageType* AddFoliageType(const UFoliageType* InType, FFoliageInfo** OutInfo = nullptr); // Add a new static mesh. FOLIAGE_API FFoliageInfo* AddMesh(UStaticMesh* InMesh, UFoliageType** OutSettings = nullptr, const UFoliageType_InstancedStaticMesh* DefaultSettings = nullptr); FOLIAGE_API FFoliageInfo* AddMesh(UFoliageType* InType); // Remove the FoliageType from the list, and all its instances. FOLIAGE_API void RemoveFoliageType(UFoliageType** InFoliageType, int32 Num); // Select an individual instance. FOLIAGE_API void SelectInstance(UInstancedStaticMeshComponent* InComponent, int32 InComponentInstanceIndex, bool bToggle); // Select an individual instance. FOLIAGE_API bool SelectInstance(AActor* InActor, bool bToggle); //Get the bounds of all selected instances FOLIAGE_API FBox GetSelectionBoundingBox() const; // Whether actor has selected instances FOLIAGE_API bool HasSelectedInstances() const; // Will return all the foliage type used by currently selected instances FOLIAGE_API TMap GetSelectedInstancesFoliageType(); // Returns FoliageType associated to this FoliageInfo FOLIAGE_API const UFoliageType* FindFoliageType(const FFoliageInfo* InFoliageInfo) const; // Will return all the foliage type used FOLIAGE_API TMap GetAllInstancesFoliageType(); // Propagate the selected instances to the actual foliage implementation FOLIAGE_API void ApplySelection(bool bApply); // Returns the location for the widget FOLIAGE_API bool GetSelectionLocation(FBox& OutLocation) const; /** Whether there any foliage instances painted on specified component */ static FOLIAGE_API bool HasFoliageAttached(UActorComponent* InComponent); /* Called to notify InstancedFoliageActor that a UFoliageType has been modified */ FOLIAGE_API void NotifyFoliageTypeChanged(UFoliageType* FoliageType, bool bSourceChanged); void NotifyFoliageTypeWillChange(UFoliageType* FoliageType); DECLARE_EVENT_OneParam(AInstancedFoliageActor, FOnFoliageTypeMeshChanged, UFoliageType*); FOnFoliageTypeMeshChanged& OnFoliageTypeMeshChanged() { return OnFoliageTypeMeshChangedEvent; } /* Fix up a duplicate IFA */ void RepairDuplicateIFA(AInstancedFoliageActor* InDuplicateIFA); void RemoveBaseComponentOnFoliageTypeInstances(UFoliageType* FoliageType); UFUNCTION(BlueprintCallable, Category="Foliage", meta = (WorldContext = "WorldContextObject")) static void AddInstances(UObject* WorldContextObject, UFoliageType* InFoliageType, const TArray& InTransforms); UFUNCTION(BlueprintCallable, Category="Foliage", meta = (WorldContext = "WorldContextObject")) static void RemoveAllInstances(UObject* WorldContextObject, UFoliageType* InFoliageType); #endif //WITH_EDITOR private: #if WITH_EDITORONLY_DATA // Deprecated data, will be converted and cleaned up in PostLoad TMap> FoliageMeshes_Deprecated; TMap> FoliageMeshes_Deprecated2; #endif//WITH_EDITORONLY_DATA #if WITH_EDITOR void ClearSelection(); void MoveInstancesToNewComponent(UPrimitiveComponent* InOldComponent, UPrimitiveComponent* InNewComponent, TFunctionRef(const FFoliageInfo&)> GetInstancesToMoveFunc); bool DeleteInstancesForProceduralFoliageComponentInternal(const FGuid& InProceduralGuid, bool bInRebuildTree, bool bInDeleteAll); friend class UFoliageEditorSubsystem; FOLIAGE_API bool MoveInstancesForMovedComponent(UActorComponent* InComponent); FOLIAGE_API void UpdateInstancePartitioningForMovedComponent(UActorComponent* InComponent); FOLIAGE_API void MoveInstancesForMovedOwnedActors(AActor* InActor); FOLIAGE_API void UpdateFoliageActorInstance(AActor* InActor); FOLIAGE_API void DeleteFoliageActorInstance(AActor* InActor); FOLIAGE_API void PostApplyLevelOffset(const FVector& InOffset, bool bWorldShift); FOLIAGE_API void PostApplyLevelTransform(const FTransform& InTransform); #endif private: #if WITH_EDITOR FOnFoliageTypeMeshChanged OnFoliageTypeMeshChangedEvent; #endif };