// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "ViewModels/Stack/NiagaraParameterHandle.h" #include "NiagaraEditorCommon.h" #include "NiagaraModule.h" #include "INiagaraMergeManager.h" #include "NiagaraTypes.h" #include "NiagaraCommon.h" #include "NiagaraEmitter.h" #include "NiagaraMessages.h" #include "NiagaraEmitterEditorData.h" #include "Templates/SharedPointer.h" #include "UObject/WeakObjectPtrTemplates.h" #include "UObject/ObjectKey.h" class UNiagaraEmitter; class UNiagaraEmitterEditorData; class UNiagaraScript; class UNiagaraNodeOutput; class UNiagaraNodeInput; class UNiagaraNodeFunctionCall; class UNiagaraNodeParameterMapSet; class UEdGraphNode; class UEdGraphPin; class UNiagaraDataInterface; struct FNiagaraEventScriptProperties; class UNiagaraSimulationStageBase; class UNiagaraRendererProperties; class FNiagaraStackFunctionMergeAdapter; struct FNiagaraStackLinkedValueData { FNiagaraVariableBase LinkedParameterValue; FGuid LinkedFunctionNodeId; bool operator==(const FNiagaraStackLinkedValueData& Other) const { return LinkedParameterValue == Other.LinkedParameterValue && LinkedFunctionNodeId == Other.LinkedFunctionNodeId; } }; class FNiagaraStackFunctionInputOverrideMergeAdapter { public: FNiagaraStackFunctionInputOverrideMergeAdapter( const FVersionedNiagaraEmitter& InOwningEmitter, UNiagaraScript& InOwningScript, UNiagaraNodeFunctionCall& InOwningFunctionCallNode, UEdGraphPin& InOverridePin); FNiagaraStackFunctionInputOverrideMergeAdapter( UNiagaraScript& InOwningScript, UNiagaraNodeFunctionCall& InOwningFunctionCallNode, FStringView InInputName, FNiagaraVariable InRapidIterationParameter); FNiagaraStackFunctionInputOverrideMergeAdapter(UEdGraphPin* InStaticSwitchPin); UNiagaraScript* GetOwningScript() const; UNiagaraNodeFunctionCall* GetOwningFunctionCall() const; FString GetInputName() const; UNiagaraNodeParameterMapSet* GetOverrideNode() const; UEdGraphPin* GetOverridePin() const; const FGuid& GetOverrideNodeId() const; const FNiagaraTypeDefinition& GetType() const; TOptional GetLocalValueString() const; TOptional GetLocalValueRapidIterationParameter() const; TOptional GetLinkedValueData() const; TOptional GetDataInterfaceValueInputName() const; UNiagaraDataInterface* GetDataInterfaceValue() const; TOptional GetObjectAssetInputVariable() const; UObject* GetObjectAssetValue() const; TSharedPtr GetDynamicValueFunction() const; TOptional GetStaticSwitchValue() const; private: TWeakObjectPtr OwningScript; TWeakObjectPtr OwningFunctionCallNode; FString InputName; FNiagaraTypeDefinition Type; TWeakObjectPtr OverrideNode; UEdGraphPin* OverridePin = nullptr; TOptional LocalValueString; TOptional LocalValueRapidIterationParameter; TOptional LinkedValueData; TOptional DataInterfaceValueInputName; UNiagaraDataInterface* DataInterfaceValue = nullptr; TOptional ObjectAssetInputVariable; UObject* ObjectAssetValue = nullptr; TSharedPtr DynamicValueFunction; TOptional StaticSwitchValue; FGuid OverrideValueNodePersistentId; }; class FNiagaraStackFunctionMergeAdapter { public: FNiagaraStackFunctionMergeAdapter(const FVersionedNiagaraEmitter& InOwningEmitter, UNiagaraScript& InOwningScript, UNiagaraNodeFunctionCall& InFunctionCallNode, int32 InStackIndex); UNiagaraNodeFunctionCall* GetFunctionCallNode() const; int32 GetStackIndex() const; bool GetUsesScratchPadScript() const; const TArray>& GetInputOverrides() const; TSharedPtr GetInputOverrideByInputNameAndType(const FString& InputName, const FNiagaraTypeDefinition& InputType) const; void GatherFunctionCallNodes(TArray& OutFunctionCallNodes) const; private: TWeakObjectPtr OwningScript; TWeakObjectPtr FunctionCallNode; int32 StackIndex; bool bUsesScratchPadScript; TArray> InputOverrides; }; class FNiagaraScriptStackMergeAdapter { public: FNiagaraScriptStackMergeAdapter(const FVersionedNiagaraEmitter& InOwningEmitter, UNiagaraNodeOutput& InOutputNode, UNiagaraScript& InScript); UNiagaraNodeOutput* GetOutputNode() const; UNiagaraNodeInput* GetInputNode() const; UNiagaraScript* GetScript() const; FString GetUniqueEmitterName() const; const TArray>& GetModuleFunctions() const; TSharedPtr GetModuleFunctionById(FGuid FunctionCallNodeId) const; void GatherFunctionCallNodes(TArray& OutFunctionCallNodes) const; private: TWeakObjectPtr InputNode; TWeakObjectPtr OutputNode; TWeakObjectPtr Script; FString UniqueEmitterName; TArray> ModuleFunctions; }; class FNiagaraEventHandlerMergeAdapter { public: FNiagaraEventHandlerMergeAdapter(const FVersionedNiagaraEmitter& InEmitter, const FNiagaraEventScriptProperties* InEventScriptProperties, UNiagaraNodeOutput* InOutputNode); FNiagaraEventHandlerMergeAdapter(const FVersionedNiagaraEmitter& InEmitter, FNiagaraEventScriptProperties* InEventScriptProperties, UNiagaraNodeOutput* InOutputNode); FNiagaraEventHandlerMergeAdapter(const FVersionedNiagaraEmitter& InEmitter, UNiagaraNodeOutput* InOutputNode); FGuid GetUsageId() const; FVersionedNiagaraEmitter GetEmitter() const; const FNiagaraEventScriptProperties* GetEventScriptProperties() const; FNiagaraEventScriptProperties* GetEditableEventScriptProperties() const; UNiagaraNodeOutput* GetOutputNode() const; UNiagaraNodeInput* GetInputNode() const; TSharedPtr GetEventStack() const; private: void Initialize(const FVersionedNiagaraEmitter& InEmitter, const FNiagaraEventScriptProperties* InEventScriptProperties, FNiagaraEventScriptProperties* InEditableEventScriptProperties, UNiagaraNodeOutput* InOutputNode); private: FVersionedNiagaraEmitterWeakPtr Emitter; const FNiagaraEventScriptProperties* EventScriptProperties; FNiagaraEventScriptProperties* EditableEventScriptProperties; TWeakObjectPtr OutputNode; TWeakObjectPtr InputNode; TSharedPtr EventStack; }; class FNiagaraSimulationStageMergeAdapter { public: FNiagaraSimulationStageMergeAdapter(const FVersionedNiagaraEmitter& InEmitter, const UNiagaraSimulationStageBase* InSimulationStage, int32 InSimulationStageIndex, UNiagaraNodeOutput* InOutputNode); FNiagaraSimulationStageMergeAdapter(const FVersionedNiagaraEmitter& InEmitter, UNiagaraSimulationStageBase* InSimulationStage, int32 InSimulationStageIndex, UNiagaraNodeOutput* InOutputNode); FNiagaraSimulationStageMergeAdapter(const FVersionedNiagaraEmitter& InEmitter, UNiagaraNodeOutput* InOutputNode); FGuid GetUsageId() const; FVersionedNiagaraEmitter GetEmitter() const; const UNiagaraSimulationStageBase* GetSimulationStage() const; UNiagaraSimulationStageBase* GetEditableSimulationStage() const; UNiagaraNodeOutput* GetOutputNode() const; UNiagaraNodeInput* GetInputNode() const; int32 GetSimulationStageIndex() const; TSharedPtr GetSimulationStageStack() const; private: void Initialize(const FVersionedNiagaraEmitter& InEmitter, const UNiagaraSimulationStageBase* InSimulationStage, UNiagaraSimulationStageBase* InEditableSimulationStage, int32 InSimulationStageIndex, UNiagaraNodeOutput* InOutputNode); private: FVersionedNiagaraEmitterWeakPtr Emitter; const UNiagaraSimulationStageBase* SimulationStage; UNiagaraSimulationStageBase* EditableSimulationStage; TWeakObjectPtr OutputNode; TWeakObjectPtr InputNode; int32 SimulationStageIndex; TSharedPtr SimulationStageStack; }; class FNiagaraRendererMergeAdapter { public: FNiagaraRendererMergeAdapter(UNiagaraRendererProperties& InRenderer); UNiagaraRendererProperties* GetRenderer(); private: TWeakObjectPtr Renderer; }; class FNiagaraInputSummaryMergeAdapter { public: FNiagaraInputSummaryMergeAdapter(const FFunctionInputSummaryViewKey& Key, const FFunctionInputSummaryViewMetadata& Value); const FFunctionInputSummaryViewKey& GetKey() const; const FFunctionInputSummaryViewMetadata& GetValue() const; private: const FFunctionInputSummaryViewKey Key; const FFunctionInputSummaryViewMetadata Value; }; class FNiagaraScratchPadMergeAdapter { public: FNiagaraScratchPadMergeAdapter(); FNiagaraScratchPadMergeAdapter(const FVersionedNiagaraEmitter InTargetEmitter, const FVersionedNiagaraEmitter& InInstanceEmitter, const FVersionedNiagaraEmitter& InParentEmitter); UNiagaraScript* GetScratchPadScriptForFunctionId(FGuid FunctionId); // Struct to aid in book-keeping around scratch pads and merge inheritance. struct FMergeRecord { FMergeRecord(FVersionedNiagaraEmitter& InOriginalEmitter, UNiagaraScript* InOriginalScript, const FGuid& InOriginalScriptVersion) : OriginalEmitter(InOriginalEmitter), OriginalScript(InOriginalScript), OriginalScriptVersion(InOriginalScriptVersion){} FVersionedNiagaraEmitter OriginalEmitter; UNiagaraScript* OriginalScript; FGuid OriginalScriptVersion; }; // Get the collapsed merge records for this merge session TArray< FMergeRecord > GetMergedEmitterRecords() const; private: void Initialize(); private: FVersionedNiagaraEmitter TargetEmitter; FVersionedNiagaraEmitter InstanceEmitter; FVersionedNiagaraEmitter ParentEmitter; bool bIsInitialized; TMap FunctionIdToScratchPadScript; }; class FNiagaraEmitterMergeAdapter { public: FNiagaraEmitterMergeAdapter(const FVersionedNiagaraEmitter& InEmitter); FVersionedNiagaraEmitter GetEditableEmitter() const; TSharedPtr GetEmitterSpawnStack() const; TSharedPtr GetEmitterUpdateStack() const; TSharedPtr GetParticleSpawnStack() const; TSharedPtr GetParticleUpdateStack() const; const TArray> GetEventHandlers() const; const TArray> GetSimulationStages() const; const TArray> GetRenderers() const; const UNiagaraEmitterEditorData* GetEditorData() const; TSharedPtr GetScriptStack(ENiagaraScriptUsage Usage, FGuid UsageId); TSharedPtr GetEventHandler(FGuid EventScriptUsageId); TSharedPtr GetSimulationStage(FGuid SimulationStageUsageId); TSharedPtr GetRenderer(FGuid RendererMergeId); void GatherFunctionCallNodes(TArray& OutFunctionCallNodes) const; private: void Initialize(const FVersionedNiagaraEmitter& InEmitter, const FVersionedNiagaraEmitter& InEditableEmitter); private: FVersionedNiagaraEmitterWeakPtr Emitter; FVersionedNiagaraEmitterWeakPtr EditableEmitter; TSharedPtr EmitterSpawnStack; TSharedPtr EmitterUpdateStack; TSharedPtr ParticleSpawnStack; TSharedPtr ParticleUpdateStack; TArray> EventHandlers; TArray> SimulationStages; TArray> Renderers; TWeakObjectPtr EditorData; }; struct FNiagaraStackFunctionMessageData { TSharedPtr Function; FNiagaraStackMessage StackMessage; }; struct FNiagaraScriptStackDiffResults { FNiagaraScriptStackDiffResults(); bool IsEmpty() const; bool IsValid() const; void AddError(FText ErrorMessage); const TArray& GetErrorMessages() const; TArray> RemovedBaseModules; TArray> AddedOtherModules; TArray> MovedBaseModules; TArray> MovedOtherModules; TArray> ChangedVersionBaseModules; TArray> ChangedVersionOtherModules; TArray> EnabledChangedBaseModules; TArray> EnabledChangedOtherModules; TArray> RemovedBaseInputOverrides; TArray> AddedOtherInputOverrides; TArray> ModifiedBaseInputOverrides; TArray> ModifiedOtherInputOverrides; TOptional ChangedBaseUsage; TOptional ChangedOtherUsage; private: bool bIsValid; TArray ErrorMessages; }; struct FNiagaraModifiedEventHandlerDiffResults { TSharedPtr BaseAdapter; TSharedPtr OtherAdapter; TArray ChangedProperties; FNiagaraScriptStackDiffResults ScriptDiffResults; }; struct FNiagaraModifiedSimulationStageDiffResults { TSharedPtr BaseAdapter; TSharedPtr OtherAdapter; TArray ChangedProperties; FNiagaraScriptStackDiffResults ScriptDiffResults; }; struct FNiagaraEmitterDiffResults { FNiagaraEmitterDiffResults(); bool IsValid() const; bool IsEmpty() const; void AddError(FText ErrorMessage); const TArray& GetErrorMessages() const; FString GetErrorMessagesString() const; bool HasVersionChanges() const; TArray DifferentEmitterProperties; TSharedPtr BaseEmitterAdapter; TSharedPtr OtherEmitterAdapter; FNiagaraScriptStackDiffResults EmitterSpawnDiffResults; FNiagaraScriptStackDiffResults EmitterUpdateDiffResults; FNiagaraScriptStackDiffResults ParticleSpawnDiffResults; FNiagaraScriptStackDiffResults ParticleUpdateDiffResults; TArray> RemovedBaseEventHandlers; TArray> AddedOtherEventHandlers; TArray ModifiedEventHandlers; TArray> RemovedBaseSimulationStages; TArray> AddedOtherSimulationStages; TArray ModifiedSimulationStages; TArray> RemovedBaseRenderers; TArray> AddedOtherRenderers; TArray> ModifiedBaseRenderers; TArray> ModifiedOtherRenderers; TArray AddedSummaryEntriesInOther; TArray AddedSummarySectionsInOther; TOptional NewShouldShowSummaryViewValue; bool bScratchPadModified; TMap ModifiedStackEntryDisplayNames; TMap AddedOrModifiedStackNotesInOther; private: bool bIsValid; TArray ErrorMessages; }; class FNiagaraScriptMergeManager : public INiagaraMergeManager { public: struct FApplyDiffResults { FApplyDiffResults() : bSucceeded(true) , bModifiedGraph(false) { } bool bSucceeded; bool bModifiedGraph; TArray ErrorMessages; }; public: void UpdateModuleVersions(const FVersionedNiagaraEmitter& Instance, const FNiagaraEmitterDiffResults& EmitterDiffResults) const; virtual INiagaraMergeManager::FMergeEmitterResults MergeEmitter(const FVersionedNiagaraEmitter& Parent, const FVersionedNiagaraEmitter& ParentAtLastMerge, const FVersionedNiagaraEmitter& Instance) const override; static TSharedRef Get(); FNiagaraEmitterDiffResults DiffEmitters(const FVersionedNiagaraEmitter& BaseEmitter, const FVersionedNiagaraEmitter& OtherEmitter) const; bool IsMergeableScriptUsage(ENiagaraScriptUsage ScriptUsage) const; bool HasBaseModule(const FVersionedNiagaraEmitter& BaseEmitter, ENiagaraScriptUsage ScriptUsage, FGuid ScriptUsageId, FGuid ModuleId); // Find the module originating this module id in the scratch lists bool FindBaseModule(const FVersionedNiagaraEmitter& BaseEmitter, ENiagaraScriptUsage ScriptUsage, FGuid ScriptUsageId, FGuid ModuleId, class UNiagaraScript*& OutActualScript, FGuid& OutScriptVersionGuid, FVersionedNiagaraEmitter& OutBaseEmitter); bool IsModuleInputDifferentFromBase(const FVersionedNiagaraEmitter& Emitter, const FVersionedNiagaraEmitter& BaseEmitter, ENiagaraScriptUsage ScriptUsage, FGuid ScriptUsageId, FGuid ModuleId, FNiagaraVariableBase Variable); /** Check whether a given summary item identity exists in the parent of a given emitter. */ bool DoesSummaryItemExistInBase(const FVersionedNiagaraEmitter& Emitter, FHierarchyElementIdentity Identity); FApplyDiffResults ResetModuleInputToBase(const FVersionedNiagaraEmitter& VersionedEmitter, const FVersionedNiagaraEmitter& VersionedBaseEmitter, ENiagaraScriptUsage ScriptUsage, FGuid ScriptUsageId, FGuid ModuleId, FString InputName); bool HasBaseEventHandler(const FVersionedNiagaraEmitter& BaseEmitter, FGuid EventScriptUsageId); bool IsEventHandlerPropertySetDifferentFromBase(const FVersionedNiagaraEmitter& Emitter, const FVersionedNiagaraEmitter& BaseEmitter, FGuid EventScriptUsageId); void ResetEventHandlerPropertySetToBase(const FVersionedNiagaraEmitter& VersionedEmitter, const FVersionedNiagaraEmitter& BaseEmitter, FGuid EventScriptUsageId); bool HasBaseSimulationStage(const FVersionedNiagaraEmitter& BaseEmitter, FGuid SimulationStageScriptUsageId); bool IsSimulationStagePropertySetDifferentFromBase(const FVersionedNiagaraEmitter& Emitter, const FVersionedNiagaraEmitter& BaseEmitter, FGuid SimulationStageScriptUsageId); void ResetSimulationStagePropertySetToBase(const FVersionedNiagaraEmitter& VersionedEmitter, const FVersionedNiagaraEmitter& BaseEmitter, FGuid SimulationStageScriptUsageId); bool HasBaseRenderer(const FVersionedNiagaraEmitter& BaseEmitter, FGuid RendererMergeId); bool IsRendererDifferentFromBase(const FVersionedNiagaraEmitter& Emitter, const FVersionedNiagaraEmitter& BaseEmitter, FGuid RendererMergeId); void ResetRendererToBase(FVersionedNiagaraEmitter Emitter, const FVersionedNiagaraEmitter& BaseEmitter, FGuid RendererMergeId); bool IsEmitterEditablePropertySetDifferentFromBase(const FVersionedNiagaraEmitter& Emitter, const FVersionedNiagaraEmitter& BaseEmitter); void ResetEmitterEditablePropertySetToBase(const FVersionedNiagaraEmitter& VersionedEmitter, const FVersionedNiagaraEmitter& BaseEmitter); void DiffEventHandlers(const TArray>& BaseEventHandlers, const TArray>& OtherEventHandlers, FNiagaraEmitterDiffResults& DiffResults) const; void DiffSimulationStages(const TArray>& BaseSimulationStages, const TArray>& OtherSimulationStages, FNiagaraEmitterDiffResults& DiffResults) const; void DiffRenderers(const TArray>& BaseRenderers, const TArray>& OtherRenderers, FNiagaraEmitterDiffResults& DiffResults) const; void DiffEmitterSummary(const UNiagaraEmitterEditorData* BaseEditorData, const UNiagaraEmitterEditorData* OtherEditorData, FNiagaraEmitterDiffResults& DiffResults) const; void DiffScriptStacks(TSharedRef BaseScriptStackAdapter, TSharedRef OtherScriptStackAdapter, FNiagaraScriptStackDiffResults& DiffResults) const; void DiffFunctionInputs(TSharedRef BaseFunctionAdapter, TSharedRef OtherFunctionAdapter, FNiagaraScriptStackDiffResults& DiffResults) const; virtual void DiffEditableProperties(const void* BaseDataAddress, const void* OtherDataAddress, UStruct& Struct, TArray& OutDifferentProperties) const override; void DiffStackEntryDisplayNames(const UNiagaraEmitterEditorData* BaseEditorData, const UNiagaraEmitterEditorData* OtherEditorData, TMap& OutModifiedStackEntryDisplayNames) const; void DiffStackNotes(const UNiagaraEmitterEditorData* BaseEditorData, const UNiagaraEmitterEditorData* OtherEditorData, FNiagaraEmitterDiffResults& DiffResults) const; void DiffScratchPadScripts(const TArray& BaseScratchPadScripts, const TArray& OtherEmitterScratchPadScripts, FNiagaraEmitterDiffResults& DiffResults) const; virtual void CopyPropertiesToBase(void* BaseDataAddress, const void* OtherDataAddress, TArray PropertiesToCopy) const override; void ClearMergeAdapterCache(); private: struct FCachedMergeAdapter { FGuid ChangeId; TSharedPtr EmitterMergeAdapter; TSharedPtr ScratchPadMergeAdapter; }; enum class EmitterMergeState { Failed, Unchanged, DuplicateParent, CopyProperties }; EmitterMergeState GetEmitterMergeState(const FNiagaraEmitterDiffResults& DiffResults, const FVersionedNiagaraEmitter& Parent, const FVersionedNiagaraEmitter& Instance) const; TOptional DoFunctionInputOverridesMatch(TSharedRef BaseFunctionInputAdapter, TSharedRef OtherFunctionInputAdapter) const; void CopyInstanceScratchPadScripts(const FVersionedNiagaraEmitter& MergedInstance, FVersionedNiagaraEmitterData* SourceInstance) const; FApplyDiffResults ApplyScriptStackDiff( TSharedRef BaseEmitterAdapter, TSharedRef BaseScriptStackAdapter, TSharedRef ScratchPadAdapter, const FNiagaraScriptStackDiffResults& DiffResults, const bool bNoParentAtLastMerge) const; FApplyDiffResults ApplyEventHandlerDiff( TSharedRef BaseEmitterAdapter, TSharedRef ScratchPadAdapter, const FNiagaraEmitterDiffResults& DiffResults, const bool bNoParentAtLastMerge) const; FApplyDiffResults ApplySimulationStageDiff( TSharedRef BaseEmitterAdapter, TSharedRef ScratchPadAdapter, const FNiagaraEmitterDiffResults& DiffResults, const bool bNoParentAtLastMerge) const; FApplyDiffResults ApplyRendererDiff(const FVersionedNiagaraEmitter& BaseEmitter, const FNiagaraEmitterDiffResults& DiffResults, const bool bNoParentAtLastMerge) const; FApplyDiffResults ApplyEmitterSummaryDiff(const FVersionedNiagaraEmitter& BaseEmitter, const FNiagaraEmitterDiffResults& DiffResults) const; FApplyDiffResults ApplyStackEntryDisplayNameDiffs(FVersionedNiagaraEmitter BaseEmitter, const FNiagaraEmitterDiffResults& DiffResults) const; FApplyDiffResults ApplyStackNoteDiffs(FVersionedNiagaraEmitter BaseEmitter, const FNiagaraEmitterDiffResults& DiffResults) const; FApplyDiffResults AddModule( TSharedRef BaseEmitterAdapter, TSharedRef ScratchPadAdapter, UNiagaraScript& OwningScript, UNiagaraNodeOutput& TargetOutputNode, TSharedRef AddModule) const; FApplyDiffResults RemoveInputOverride(UNiagaraScript& OwningScript, TSharedRef OverrideToRemove) const; FApplyDiffResults AddInputOverride( TSharedRef BaseEmitterAdapter, TSharedRef ScratchPadAdapter, UNiagaraScript& OwningScript, UNiagaraNodeFunctionCall& TargetFunctionCall, TSharedRef OverrideToAdd) const; FCachedMergeAdapter* FindOrAddMergeAdapterCacheForEmitter(const FVersionedNiagaraEmitter& VersionedEmitter); TSharedRef GetEmitterMergeAdapterUsingCache(const FVersionedNiagaraEmitter& Emitter); TSharedRef GetScratchPadMergeAdapterUsingCache(const FVersionedNiagaraEmitter& VersionedEmitter); void GetForcedChangeIds( const TMap& InParentFunctionIdToNodeMap, const TMap& InParentAtLastMergeFunctionIdToNodeMap, const TMap& InInstanceFunctionIdToNodeMap, TMap& OutFunctionIdToForcedChangeId) const; FApplyDiffResults ForceInstanceChangeIds(TSharedRef MergedInstanceAdapter, const FVersionedNiagaraEmitter& OriginalEmitterInstance, const TMap& ChangeIdsThatNeedToBeReset) const; TMap CachedMergeAdapters; };