// Copyright Epic Games, Inc. All Rights Reserved. #include "MovieSceneToolsModule.h" #include "Editor.h" #include "Modules/ModuleManager.h" #include "Curves/RichCurve.h" #include "ISequencerCoreModule.h" #include "ISequencerModule.h" #include "ICurveEditorModule.h" #include "MovieSceneToolsProjectSettingsCustomization.h" #include "MovieSceneCVarOverridesPropertyTypeCustomization.h" #include "Engine/Blueprint.h" #include "EdGraph/EdGraph.h" #include "K2Node_CustomEvent.h" #include "K2Node_FunctionEntry.h" #include "KismetCompiler.h" #include "Sections/MovieSceneEventSectionBase.h" #include "Sections/MovieSceneParticleSection.h" #include "TrackEditors/PropertyTrackEditors/BoolPropertyTrackEditor.h" #include "TrackEditors/PropertyTrackEditors/BytePropertyTrackEditor.h" #include "TrackEditors/PropertyTrackEditors/ColorPropertyTrackEditor.h" #include "TrackEditors/PropertyTrackEditors/FloatPropertyTrackEditor.h" #include "TrackEditors/PropertyTrackEditors/DoublePropertyTrackEditor.h" #include "TrackEditors/PropertyTrackEditors/IntegerPropertyTrackEditor.h" #include "TrackEditors/PropertyTrackEditors/VectorPropertyTrackEditor.h" #include "TrackEditors/PropertyTrackEditors/TransformPropertyTrackEditor.h" #include "TrackEditors/PropertyTrackEditors/EulerTransformPropertyTrackEditor.h" #include "TrackEditors/PropertyTrackEditors/RotatorPropertyTrackEditor.h" #include "TrackEditors/PropertyTrackEditors/VisibilityPropertyTrackEditor.h" #include "TrackEditors/PropertyTrackEditors/ActorReferencePropertyTrackEditor.h" #include "TrackEditors/PropertyTrackEditors/StringPropertyTrackEditor.h" #include "TrackEditors/TransformTrackEditor.h" #include "TrackEditors/CameraCutTrackEditor.h" #include "TrackEditors/CinematicShotTrackEditor.h" #include "TrackEditors/SlomoTrackEditor.h" #include "TrackEditors/SubTrackEditor.h" #include "TrackEditors/AudioTrackEditor.h" #include "TrackEditors/SkeletalAnimationTrackEditor.h" #include "TrackEditors/ParticleTrackEditor.h" #include "TrackEditors/ParticleParameterTrackEditor.h" #include "TrackEditors/AttachTrackEditor.h" #include "TrackEditors/EventTrackEditor.h" #include "TrackEditors/PathTrackEditor.h" #include "TrackEditors/MaterialTrackEditor.h" #include "TrackEditors/FadeTrackEditor.h" #include "TrackEditors/SpawnTrackEditor.h" #include "TrackEditors/LevelVisibilityTrackEditor.h" #include "TrackEditors/DataLayerTrackEditor.h" #include "TrackEditors/CameraShakeTrackEditor.h" #include "TrackEditors/MaterialParameterCollectionTrackEditor.h" #include "TrackEditors/ObjectPropertyTrackEditor.h" #include "TrackEditors/PrimitiveMaterialTrackEditor.h" #include "TrackEditors/CameraShakeSourceShakeTrackEditor.h" #include "TrackEditors/CVarTrackEditor.h" #include "TrackEditors/CustomPrimitiveDataTrackEditor.h" #include "TrackEditors/BindingLifetimeTrackEditor.h" #include "TrackEditors/TimeWarpTrackEditor.h" #include "Channels/PerlinNoiseChannelInterface.h" #include "MVVM/ViewModels/CameraCutTrackModel.h" #include "MVVM/ViewModels/CinematicShotTrackModel.h" #include "MVVM/ViewModels/BindingLifetimeTrackModel.h" #include "MVVM/ViewModels/ScalingAnchorsModel.h" #include "Decorations/MovieSceneScalingAnchors.h" #include "MovieSceneBuiltInEasingFunctionCustomization.h" #include "MovieSceneAlphaBlendOptionCustomization.h" #include "MovieSceneObjectBindingIDCustomization.h" #include "MovieSceneDynamicBindingCustomization.h" #include "MovieSceneEventCustomization.h" #include "MovieSceneTimeWarpVariantCustomization.h" #include "Conditions/MovieSceneConditionCustomization.h" #include "SequencerClipboardReconciler.h" #include "ClipboardTypes.h" #include "ISettingsModule.h" #include "PropertyEditorModule.h" #include "IMovieSceneTools.h" #include "IMovieSceneToolsTrackImporter.h" #include "MovieSceneToolsProjectSettings.h" #include "ISequencerChannelInterface.h" #include "SequencerChannelInterface.h" #include "Channels/BuiltInChannelEditors.h" #include "Channels/MovieSceneByteChannel.h" #include "Channels/MovieSceneChannel.h" #include "Channels/MovieSceneEventChannel.h" #include "Channels/MovieSceneTimeWarpChannel.h" #include "Channels/MovieSceneObjectPathChannel.h" #include "Channels/MovieSceneCameraShakeSourceTriggerChannel.h" #include "Channels/MovieSceneStringChannel.h" #include "Channels/MovieSceneFloatPerlinNoiseChannel.h" #include "Channels/MovieSceneDoublePerlinNoiseChannel.h" #include "Channels/EventChannelCurveModel.h" #include "Channels/SCurveEditorEventChannelView.h" #include "Channels/MovieSceneAudioTriggerChannel.h" #include "ConstraintChannel.h" #include "Channels/ConstraintChannelEditor.h" #include "Channels/ConstraintChannelCurveModel.h" #include "Channels/SCurveEditorKeyBarView.h" #include "Sections/MovieSceneEventSection.h" #include "Channels/MovieSceneDoublePerlinNoiseChannelContainer.h" #include "Channels/MovieSceneFloatPerlinNoiseChannelContainer.h" #include "Channels/PerlinNoiseChannelDetailsCustomization.h" #include "MovieSceneEventUtils.h" #include "EntitySystem/MovieSceneEntityManager.h" #include "EditorModeManager.h" #include "EditModes/SkeletalAnimationTrackEditMode.h" #include "ClassViewerFilter.h" #include "ClassViewerModule.h" #include "LevelSequence.h" #include "LevelSequenceAnimSequenceLink.h" #include "AnimSequenceLevelSequenceLink.h" #include "EditorAnimUtils.h" #include "Animation/AnimSequence.h" #include "Transform/TransformableHandle.h" #include "Constraints/ComponentConstraintChannelInterface.h" #include "Constraints/TransformConstraintChannelInterface.h" #include "Bindings/MovieSceneReplaceableDirectorBlueprintBinding.h" #include "Bindings/MovieSceneSpawnableDirectorBlueprintBinding.h" #include "EntitySystem/MovieSceneSharedPlaybackState.h" #include "MovieSceneDynamicBindingUtils.h" #include "EditModes/SubTrackEditorMode.h" #include "Kismet2/KismetEditorUtilities.h" #include "Conditions/MovieSceneDirectorBlueprintCondition.h" #include "Conditions/MovieSceneDirectorBlueprintConditionUtils.h" #include "Conditions/MovieSceneDirectorBlueprintConditionCustomization.h" #include "Conditions/MovieScenePlatformConditionCustomization.h" #define LOCTEXT_NAMESPACE "FMovieSceneToolsModule" TAutoConsoleVariable CVarDuplicateLinkedAnimSequence(TEXT("Sequencer.DuplicateLinkedAnimSequence"), false, TEXT("When true when we duplicate a level sequence that has a linked anim sequence it will duplicate and link the anim sequencel, if false we leave any link alone.")); void FMovieSceneToolsModule::StartupModule() { using namespace UE::Sequencer; if (GIsEditor) { if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) { SettingsModule->RegisterSettings("Project", "Editor", "Level Sequences", LOCTEXT("RuntimeSettingsName", "Level Sequences"), LOCTEXT("RuntimeSettingsDescription", "Configure project settings relating to Level Sequences"), GetMutableDefault() ); } ISequencerModule& SequencerModule = FModuleManager::Get().LoadModuleChecked( "Sequencer" ); // register property track editors BoolPropertyTrackCreateEditorHandle = SequencerModule.RegisterPropertyTrackEditor(); BytePropertyTrackCreateEditorHandle = SequencerModule.RegisterPropertyTrackEditor(); ColorPropertyTrackCreateEditorHandle = SequencerModule.RegisterPropertyTrackEditor(); FloatPropertyTrackCreateEditorHandle = SequencerModule.RegisterPropertyTrackEditor(); DoublePropertyTrackCreateEditorHandle = SequencerModule.RegisterPropertyTrackEditor(); IntegerPropertyTrackCreateEditorHandle = SequencerModule.RegisterPropertyTrackEditor(); FloatVectorPropertyTrackCreateEditorHandle = SequencerModule.RegisterPropertyTrackEditor(); DoubleVectorPropertyTrackCreateEditorHandle = SequencerModule.RegisterPropertyTrackEditor(); TransformPropertyTrackCreateEditorHandle = SequencerModule.RegisterPropertyTrackEditor(); EulerTransformPropertyTrackCreateEditorHandle = SequencerModule.RegisterPropertyTrackEditor(); RotatorPropertyTrackCreateEditorHandle = SequencerModule.RegisterPropertyTrackEditor(); VisibilityPropertyTrackCreateEditorHandle = SequencerModule.RegisterPropertyTrackEditor(); ActorReferencePropertyTrackCreateEditorHandle = SequencerModule.RegisterPropertyTrackEditor(); StringPropertyTrackCreateEditorHandle = SequencerModule.RegisterPropertyTrackEditor(); ObjectTrackCreateEditorHandle = SequencerModule.RegisterPropertyTrackEditor(); // register specialty track editors AnimationTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor( FOnCreateTrackEditor::CreateStatic( &FSkeletalAnimationTrackEditor::CreateTrackEditor ) ); AttachTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor( FOnCreateTrackEditor::CreateStatic( &F3DAttachTrackEditor::CreateTrackEditor ) ); AudioTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor( FOnCreateTrackEditor::CreateStatic( &FAudioTrackEditor::CreateTrackEditor ) ); EventTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor( FOnCreateTrackEditor::CreateStatic( &FEventTrackEditor::CreateTrackEditor ) ); ParticleTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor( FOnCreateTrackEditor::CreateStatic( &FParticleTrackEditor::CreateTrackEditor ) ); ParticleParameterTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor( FOnCreateTrackEditor::CreateStatic( &FParticleParameterTrackEditor::CreateTrackEditor ) ); PathTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor( FOnCreateTrackEditor::CreateStatic( &F3DPathTrackEditor::CreateTrackEditor ) ); CameraCutTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor( FOnCreateTrackEditor::CreateStatic( &FCameraCutTrackEditor::CreateTrackEditor ) ); CinematicShotTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor( FOnCreateTrackEditor::CreateStatic( &FCinematicShotTrackEditor::CreateTrackEditor ) ); SlomoTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor( FOnCreateTrackEditor::CreateStatic( &FSlomoTrackEditor::CreateTrackEditor ) ); SubTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor( FOnCreateTrackEditor::CreateStatic( &FSubTrackEditor::CreateTrackEditor ) ); TransformTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor( FOnCreateTrackEditor::CreateStatic( &F3DTransformTrackEditor::CreateTrackEditor ) ); ComponentMaterialTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor( FOnCreateTrackEditor::CreateStatic( &FComponentMaterialTrackEditor::CreateTrackEditor ) ); FadeTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor( FOnCreateTrackEditor::CreateStatic( &FFadeTrackEditor::CreateTrackEditor ) ); SpawnTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor( FOnCreateTrackEditor::CreateStatic( &FSpawnTrackEditor::CreateTrackEditor ) ); LevelVisibilityTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor( FOnCreateTrackEditor::CreateStatic( &FLevelVisibilityTrackEditor::CreateTrackEditor ) ); DataLayerTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor( FOnCreateTrackEditor::CreateStatic( &FDataLayerTrackEditor::CreateTrackEditor ) ); CameraShakeTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor(FOnCreateTrackEditor::CreateStatic(&FCameraShakeTrackEditor::CreateTrackEditor)); MPCTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor(FOnCreateTrackEditor::CreateStatic(&FMaterialParameterCollectionTrackEditor::CreateTrackEditor)); PrimitiveMaterialCreateEditorHandle = SequencerModule.RegisterTrackEditor(FOnCreateTrackEditor::CreateStatic(&FPrimitiveMaterialTrackEditor::CreateTrackEditor)); CameraShakeSourceShakeCreateEditorHandle = SequencerModule.RegisterTrackEditor(FOnCreateTrackEditor::CreateStatic(&FCameraShakeSourceShakeTrackEditor::CreateTrackEditor)); CVarTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor(FOnCreateTrackEditor::CreateStatic(&FCVarTrackEditor::CreateTrackEditor)); CustomPrimitiveDataTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor(FOnCreateTrackEditor::CreateStatic(&FCustomPrimitiveDataTrackEditor::CreateTrackEditor)); BindingLifetimeTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor(FOnCreateTrackEditor::CreateStatic(&FBindingLifetimeTrackEditor::CreateTrackEditor)); TimeWarpTrackCreateEditorHandle = SequencerModule.RegisterTrackEditor(FOnCreateTrackEditor::CreateStatic(&FTimeWarpTrackEditor::CreateTrackEditor)); // register track models CameraCutTrackModelHandle = SequencerModule.RegisterTrackModel(FOnCreateTrackModel::CreateStatic(&FCameraCutTrackModel::CreateTrackModel)); CinematicShotTrackModelHandle = SequencerModule.RegisterTrackModel(FOnCreateTrackModel::CreateStatic(&FCinematicShotTrackModel::CreateTrackModel)); BindingLifetimeTrackModelHandle = SequencerModule.RegisterTrackModel(FOnCreateTrackModel::CreateStatic(&FBindingLifetimeTrackModel::CreateTrackModel)); TimeWarpTrackModelHandle = SequencerModule.RegisterTrackModel(FOnCreateTrackModel::CreateStatic(&FTimeWarpTrackEditor::CreateTrackModel)); RegisterClipboardConversions(); // register details customization FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); PropertyModule.RegisterCustomClassLayout("MovieSceneToolsProjectSettings", FOnGetDetailCustomizationInstance::CreateStatic(&FMovieSceneToolsProjectSettingsCustomization::MakeInstance)); PropertyModule.RegisterCustomClassLayout("MovieSceneBuiltInEasingFunction", FOnGetDetailCustomizationInstance::CreateLambda(&MakeShared)); PropertyModule.RegisterCustomPropertyTypeLayout("EAlphaBlendOption", FOnGetPropertyTypeCustomizationInstance::CreateLambda(&MakeShared)); PropertyModule.RegisterCustomClassLayout("MovieSceneFloatPerlinNoiseChannelContainer", FOnGetDetailCustomizationInstance::CreateStatic(&FMovieSceneFloatPerlinNoiseChannelDetailsCustomization::MakeInstance)); PropertyModule.RegisterCustomClassLayout("MovieSceneDoublePerlinNoiseChannelContainer", FOnGetDetailCustomizationInstance::CreateStatic(&FMovieSceneDoublePerlinNoiseChannelDetailsCustomization::MakeInstance)); PropertyModule.RegisterCustomPropertyTypeLayout("MovieSceneObjectBindingID", FOnGetPropertyTypeCustomizationInstance::CreateLambda(&MakeShared)); PropertyModule.RegisterCustomPropertyTypeLayout("MovieSceneDynamicBinding", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FMovieSceneDynamicBindingCustomization::MakeInstance)); PropertyModule.RegisterCustomPropertyTypeLayout("MovieSceneDirectorBlueprintConditionData", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FMovieSceneDirectorBlueprintConditionCustomization::MakeInstance)); PropertyModule.RegisterCustomPropertyTypeLayout("MovieSceneEvent", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FMovieSceneEventCustomization::MakeInstance)); PropertyModule.RegisterCustomPropertyTypeLayout("MovieSceneCVarOverrides", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&UE::MovieScene::FCVarOverridesPropertyTypeCustomization::MakeInstance)); PropertyModule.RegisterCustomPropertyTypeLayout("MovieSceneTimeWarpVariant", FOnGetPropertyTypeCustomizationInstance::CreateLambda(&MakeShared)); PropertyModule.RegisterCustomPropertyTypeLayout("MovieSceneConditionContainer", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FMovieSceneConditionCustomization::MakeInstance)); PropertyModule.RegisterCustomClassLayout("MovieScenePlatformCondition", FOnGetDetailCustomizationInstance::CreateStatic(&FMovieScenePlatformConditionCustomization::MakeInstance)); SequencerModule.RegisterChannelInterface(); SequencerModule.RegisterChannelInterface(); SequencerModule.RegisterChannelInterface(); SequencerModule.RegisterChannelInterface(); SequencerModule.RegisterChannelInterface(); SequencerModule.RegisterChannelInterface(); SequencerModule.RegisterChannelInterface(); SequencerModule.RegisterChannelInterface(); SequencerModule.RegisterChannelInterface(); SequencerModule.RegisterChannelInterface(); SequencerModule.RegisterChannelInterface(); SequencerModule.RegisterChannelInterface(); SequencerModule.RegisterChannelInterface(); SequencerModule.RegisterChannelInterface(); SequencerModule.RegisterChannelInterface(); SequencerModule.RegisterChannelInterface(MakeUnique>()); SequencerModule.RegisterChannelInterface(MakeUnique>()); ICurveEditorModule& CurveEditorModule = FModuleManager::LoadModuleChecked("CurveEditor"); FEventChannelCurveModel::EventView = CurveEditorModule.RegisterView(FOnCreateCurveEditorView::CreateStatic( [](TWeakPtr WeakCurveEditor) -> TSharedRef { return SNew(SCurveEditorEventChannelView, WeakCurveEditor); } )); FConstraintChannelCurveModel::ViewID = CurveEditorModule.RegisterView(FOnCreateCurveEditorView::CreateStatic( [](TWeakPtr WeakCurveEditor) -> TSharedRef { return SNew(SCurveEditorKeyBarView, WeakCurveEditor); } )); ISequencerCoreModule& SequencerCoreModule = FModuleManager::Get().LoadModuleChecked("SequencerCore"); SequencerCoreModule.RegisterModelType(UMovieSceneScalingAnchors::StaticClass(), FScalingAnchorsModel::ID); } FixupDynamicBindingPayloadParameterNameHandle = UMovieScene::FixupDynamicBindingPayloadParameterNameEvent.AddStatic(FixupPayloadParameterNameForDynamicBinding); FixupEventSectionPayloadParameterNameHandle = UMovieSceneEventSectionBase::FixupPayloadParameterNameEvent.AddStatic(FixupPayloadParameterNameForSection); UMovieSceneEventSectionBase::UpgradeLegacyEventEndpoint.BindStatic(UpgradeLegacyEventEndpointForSection); UMovieSceneEventSectionBase::PostDuplicateSectionEvent.BindStatic(PostDuplicateEventSection); UMovieSceneEventSectionBase::RemoveForCookEvent.BindStatic(RemoveForCookEventSection); UMovieScene::IsTrackClassAllowedEvent.BindStatic(IsTrackClassAllowed); UMovieScene::IsCustomBindingClassAllowedEvent.BindStatic(IsCustomBindingClassAllowed); UMovieScene::IsConditionClassAllowedEvent.BindStatic(IsConditionClassAllowed); ULevelSequence::PostDuplicateEvent.BindStatic(PostDuplicateEvent); FixupDynamicBindingsHandle = ULevelSequence::FixupDynamicBindingsEvent.AddStatic(FixupDynamicBindingsEvent); FixupDirectorBlueprintConditionPayloadParameterNameHandle = UMovieScene::FixupDirectorBlueprintConditionPayloadParameterNameEvent.AddStatic(FixupPayloadParameterNameForDirectorBlueprintCondition); auto OnObjectsReplaced = [](const TMap& ReplacedObjects) { // If a movie scene signed object is reinstanced, it has to be marked as modified // so that the data gets recompiled properly. // @todo: this might cause cook non-determinism, but we need to verify that separately for (const TTuple& Pair : ReplacedObjects) { if (UMovieSceneSignedObject* SignedObject = Cast(Pair.Value)) { SignedObject->MarkAsChanged(); } } }; OnObjectsReplacedHandle = FCoreUObjectDelegates::OnObjectsReplaced.AddLambda(OnObjectsReplaced); FEditorModeRegistry::Get().RegisterMode( FSkeletalAnimationTrackEditMode::ModeName, NSLOCTEXT("SkeletalAnimationTrackEditorMode", "SkelAnimTrackEditMode", "Skeletal Anim Track Mode"), FSlateIcon(), false); FEditorModeRegistry::Get().RegisterMode( FSubTrackEditorMode::ModeName, NSLOCTEXT("SubTrackEditorMode", "SubTrackEditMode", "Sub Track Mode"), FSlateIcon(), false); // register UTransformableComponentHandle animatable interface FConstraintChannelInterfaceRegistry& ConstraintChannelInterfaceRegistry = FConstraintChannelInterfaceRegistry::Get(); ConstraintChannelInterfaceRegistry.RegisterConstraintChannelInterface(MakeUnique()); } void FMovieSceneToolsModule::ShutdownModule() { UMovieScene::FixupDynamicBindingPayloadParameterNameEvent.Remove(FixupDynamicBindingPayloadParameterNameHandle); UMovieSceneEventSectionBase::FixupPayloadParameterNameEvent.Remove(FixupEventSectionPayloadParameterNameHandle); UMovieSceneEventSectionBase::UpgradeLegacyEventEndpoint = UMovieSceneEventSectionBase::FUpgradeLegacyEventEndpoint(); UMovieSceneEventSectionBase::PostDuplicateSectionEvent = UMovieSceneEventSectionBase::FPostDuplicateEvent(); UMovieSceneEventSectionBase::RemoveForCookEvent = UMovieSceneEventSectionBase::FRemoveForCookEvent(); UMovieScene::IsTrackClassAllowedEvent = UMovieScene::FIsTrackClassAllowedEvent(); UMovieScene::IsCustomBindingClassAllowedEvent = UMovieScene::FIsCustomBindingClassAllowedEvent(); UMovieScene::IsConditionClassAllowedEvent = UMovieScene::FIsConditionClassAllowedEvent(); ULevelSequence::PostDuplicateEvent = ULevelSequence::FPostDuplicateEvent(); ULevelSequence::FixupDynamicBindingsEvent.Remove(FixupDynamicBindingsHandle); UMovieScene::FixupDirectorBlueprintConditionPayloadParameterNameEvent.Remove(FixupDirectorBlueprintConditionPayloadParameterNameHandle); if (ICurveEditorModule* CurveEditorModule = FModuleManager::GetModulePtr("CurveEditor")) { CurveEditorModule->UnregisterView(FEventChannelCurveModel::EventView); CurveEditorModule->UnregisterView(FConstraintChannelCurveModel::ViewID); } if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) { SettingsModule->UnregisterSettings("Project", "Editor", "Level Sequences"); } FCoreUObjectDelegates::OnObjectsReplaced.Remove(OnObjectsReplacedHandle); if (!FModuleManager::Get().IsModuleLoaded("Sequencer")) { return; } ISequencerModule& SequencerModule = FModuleManager::Get().GetModuleChecked( "Sequencer" ); // unregister property track editors SequencerModule.UnRegisterTrackEditor( BoolPropertyTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( BytePropertyTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( ColorPropertyTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( FloatPropertyTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( DoublePropertyTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( IntegerPropertyTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( FloatVectorPropertyTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( DoubleVectorPropertyTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( TransformPropertyTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( EulerTransformPropertyTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( RotatorPropertyTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( VisibilityPropertyTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( ActorReferencePropertyTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( StringPropertyTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( CameraShakeSourceShakeCreateEditorHandle ); // unregister specialty track editors SequencerModule.UnRegisterTrackEditor( AnimationTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( AttachTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( AudioTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( EventTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( ParticleTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( ParticleParameterTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( PathTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( CameraCutTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( CinematicShotTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( SlomoTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( SubTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( TransformTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( ComponentMaterialTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( FadeTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( SpawnTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( LevelVisibilityTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( DataLayerTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( CameraShakeTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( MPCTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( ObjectTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( PrimitiveMaterialCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( CVarTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( CustomPrimitiveDataTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( BindingLifetimeTrackCreateEditorHandle ); SequencerModule.UnRegisterTrackEditor( TimeWarpTrackCreateEditorHandle ); // unregister track models SequencerModule.UnregisterTrackModel( CameraCutTrackModelHandle ); SequencerModule.UnregisterTrackModel( CinematicShotTrackModelHandle ); SequencerModule.UnregisterTrackModel( BindingLifetimeTrackModelHandle ); SequencerModule.UnregisterTrackModel( TimeWarpTrackModelHandle ); if (FModuleManager::Get().IsModuleLoaded("PropertyEditor")) { FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); PropertyModule.UnregisterCustomClassLayout("MovieSceneToolsProjectSettings"); PropertyModule.UnregisterCustomClassLayout("MovieSceneBuiltInEasingFunction"); PropertyModule.UnregisterCustomPropertyTypeLayout("EAlphaBlendOption"); PropertyModule.UnregisterCustomPropertyTypeLayout("MovieSceneObjectBindingID"); PropertyModule.UnregisterCustomPropertyTypeLayout("MovieSceneEvent"); PropertyModule.UnregisterCustomPropertyTypeLayout("MovieSceneDynamicBinding"); PropertyModule.UnregisterCustomPropertyTypeLayout("MovieSceneDirectorBlueprintConditionData"); PropertyModule.UnregisterCustomPropertyTypeLayout("MovieSceneConditionContainer"); PropertyModule.UnregisterCustomClassLayout("MovieScenePlatformCondition"); } FEditorModeRegistry::Get().UnregisterMode(FSkeletalAnimationTrackEditMode::ModeName); FEditorModeRegistry::Get().UnregisterMode(FSubTrackEditorMode::ModeName); } void FMovieSceneToolsModule::PostDuplicateEventSection(UMovieSceneEventSectionBase* Section) { UMovieSceneSequence* Sequence = Section->GetTypedOuter(); FMovieSceneSequenceEditor* SequenceEditor = FMovieSceneSequenceEditor::Find(Sequence); UBlueprint* SequenceDirectorBP = SequenceEditor ? SequenceEditor->FindDirectorBlueprint(Sequence) : nullptr; if (SequenceDirectorBP) { // Always bind the event section onto the blueprint to ensure that we get another chance to upgrade when the BP compiles if this try wasn't successful FMovieSceneEventUtils::BindEventSectionToBlueprint(Section, SequenceDirectorBP); } } void FMovieSceneToolsModule::RemoveForCookEventSection(UMovieSceneEventSectionBase* Section) { UMovieSceneSequence* Sequence = Section->GetTypedOuter(); FMovieSceneSequenceEditor* SequenceEditor = FMovieSceneSequenceEditor::Find(Sequence); UBlueprint* SequenceDirectorBP = SequenceEditor ? SequenceEditor->FindDirectorBlueprint(Sequence) : nullptr; if (SequenceDirectorBP) { FMovieSceneEventUtils::RemoveEndpointsForEventSection(Section, SequenceDirectorBP); } } //When we duplicate a ULevelSequence we check to see if there are any linked UAnimSequences in the asset user data, //if so we either make a copy of the anim sequence, or leave it alone since a rename can also be a duplicate, the user will need to clean up this link later. void FMovieSceneToolsModule::PostDuplicateEvent(ULevelSequence* LevelSequence) { if (LevelSequence && LevelSequence->GetClass()->ImplementsInterface(UInterface_AssetUserData::StaticClass())) { if (IInterface_AssetUserData* AssetUserDataInterface = Cast< IInterface_AssetUserData >(LevelSequence)) { ULevelSequenceAnimSequenceLink* LevelAnimLink = AssetUserDataInterface->GetAssetUserData< ULevelSequenceAnimSequenceLink >(); if (LevelAnimLink) { const bool bDuplicateAnimSequence = CVarDuplicateLinkedAnimSequence.GetValueOnGameThread(); if (bDuplicateAnimSequence) { for (FLevelSequenceAnimSequenceLinkItem& Item : LevelAnimLink->AnimSequenceLinks) { if (UAnimSequence* AnimSequence = Item.ResolveAnimSequence()) { TArray AnimSequencesToDuplicate; AnimSequencesToDuplicate.Add(AnimSequence); UPackage* DestinationPackage = AnimSequence->GetPackage(); EditorAnimUtils::FNameDuplicationRule NameRule; NameRule.FolderPath = FPackageName::GetLongPackagePath(AnimSequence->GetPathName()) / TEXT(""); TMap DuplicatedAnimAssets = EditorAnimUtils::DuplicateAssets(AnimSequencesToDuplicate, DestinationPackage, &NameRule); for (TPair& Duplicates : DuplicatedAnimAssets) { if (UAnimSequence* NewAnimSequence = Duplicates.Value) { Item.PathToAnimSequence = FSoftObjectPath(NewAnimSequence); if (IInterface_AssetUserData* AnimAssetUserData = Cast< IInterface_AssetUserData >(NewAnimSequence)) { UAnimSequenceLevelSequenceLink* AnimLevelLink = AnimAssetUserData->GetAssetUserData< UAnimSequenceLevelSequenceLink >(); if (AnimLevelLink) { AnimLevelLink->SetLevelSequence(LevelSequence); } } } } } } } } } } } void FMovieSceneToolsModule::FixupDynamicBindingsEvent(ULevelSequence* LevelSequence) { if (LevelSequence) { if (UBlueprint* DirectorBlueprint = LevelSequence->GetDirectorBlueprint()) { FMovieSceneDynamicBindingUtils::EnsureBlueprintExtensionCreated(LevelSequence, DirectorBlueprint); FKismetEditorUtilities::CompileBlueprint(DirectorBlueprint, EBlueprintCompileOptions::SkipGarbageCollection); } } } bool FMovieSceneToolsModule::UpgradeLegacyEventEndpointForSection(UMovieSceneEventSectionBase* Section) { UMovieSceneSequence* Sequence = Section->GetTypedOuter(); FMovieSceneSequenceEditor* SequenceEditor = FMovieSceneSequenceEditor::Find(Sequence); UBlueprint* SequenceDirectorBP = SequenceEditor ? SequenceEditor->FindDirectorBlueprint(Sequence) : nullptr; if (!SequenceDirectorBP) { return true; } // Always bind the event section onto the blueprint to ensure that we get another chance to upgrade when the BP compiles if this try wasn't successful FMovieSceneEventUtils::BindEventSectionToBlueprint(Section, SequenceDirectorBP); // We can't do this upgrade if we any of the function graphs are RF_NeedLoad for (UEdGraph* EdGraph : SequenceDirectorBP->FunctionGraphs) { if (EdGraph->HasAnyFlags(RF_NeedLoad)) { return false; } } // All the function graphs have been loaded, which means this is a good time to perform legacy data upgrade for (FMovieSceneEvent& EntryPoint : Section->GetAllEntryPoints()) { UK2Node* Endpoint = CastChecked(EntryPoint.WeakEndpoint.Get(), ECastCheckedType::NullAllowed); if (!Endpoint) { if (UK2Node_FunctionEntry* LegacyFunctionEntry = Cast(EntryPoint.FunctionEntry_DEPRECATED.Get())) { EntryPoint.WeakEndpoint = Endpoint = LegacyFunctionEntry; } // If we don't have an endpoint but do have legacy graph or node guids, we do the manual upgrade if (!Endpoint && EntryPoint.GraphGuid_DEPRECATED.IsValid()) { if (EntryPoint.NodeGuid_DEPRECATED.IsValid()) { if (TObjectPtr const* GraphPtr = Algo::FindBy(SequenceDirectorBP->UbergraphPages, EntryPoint.GraphGuid_DEPRECATED, &UEdGraph::GraphGuid)) { TObjectPtr const* NodePtr = Algo::FindBy((*GraphPtr)->Nodes, EntryPoint.NodeGuid_DEPRECATED, &UEdGraphNode::NodeGuid); if (NodePtr) { UK2Node_CustomEvent* CustomEvent = Cast(*NodePtr); if (ensureMsgf(CustomEvent, TEXT("Encountered an event entry point node that is bound to something other than a custom event"))) { CustomEvent->OnUserDefinedPinRenamed().AddUObject(Section, &UMovieSceneEventSectionBase::OnUserDefinedPinRenamed); EntryPoint.WeakEndpoint = Endpoint = CustomEvent; } } } } // If the node guid is invalid, this must be a function graph on the BP else if (TObjectPtr const* GraphPtr = Algo::FindBy(SequenceDirectorBP->FunctionGraphs, EntryPoint.GraphGuid_DEPRECATED, &UEdGraph::GraphGuid)) { TObjectPtr const* NodePtr = Algo::FindByPredicate((*GraphPtr)->Nodes, [](UEdGraphNode* InNode){ return InNode && InNode->IsA(); }); if (NodePtr) { UK2Node_FunctionEntry* FunctionEntry = CastChecked(*NodePtr); FunctionEntry->OnUserDefinedPinRenamed().AddUObject(Section, &UMovieSceneEventSectionBase::OnUserDefinedPinRenamed); EntryPoint.WeakEndpoint = Endpoint = FunctionEntry; } } if (Endpoint) { // Discover its bound object pin name from the node for (UEdGraphPin* Pin : Endpoint->Pins) { if (Pin->Direction == EGPD_Output && (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Object || Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Interface) ) { EntryPoint.BoundObjectPinName = Pin->PinName; break; } } } } } // Set the compiled function name so that any immediate PostCompile steps find the correct function name if (Endpoint) { EntryPoint.CompiledFunctionName = Endpoint->GetGraph()->GetFName(); } } // If the BP has already been compiled (eg regenerate on load) we must perform PostCompile fixup immediately since // We will not have had a chance to generate function entries. In this case we just bind directly to the already compiled functions. if (SequenceDirectorBP->bHasBeenRegenerated) { Section->OnPostCompile(SequenceDirectorBP); } return true; } bool IsMovieSceneClassAllowed(const UClass* InClass) { if (!InClass) { return false; } FClassViewerModule& ClassViewerModule = FModuleManager::LoadModuleChecked("ClassViewer"); const TSharedPtr& GlobalClassFilter = ClassViewerModule.GetGlobalClassViewerFilter(); TSharedRef ClassFilterFuncs = ClassViewerModule.CreateFilterFuncs(); FClassViewerInitializationOptions ClassViewerOptions = {}; if (GlobalClassFilter.IsValid()) { return GlobalClassFilter->IsClassAllowed(ClassViewerOptions, InClass, ClassFilterFuncs); } return true; } bool FMovieSceneToolsModule::IsTrackClassAllowed(UClass* InClass) { return IsMovieSceneClassAllowed(InClass); } bool FMovieSceneToolsModule::IsCustomBindingClassAllowed(UClass* InClass) { // For now this has the same implementation as IsTrackClassAllowed, but has been kept a separate function and event in the case we want // different behavior here. return IsMovieSceneClassAllowed(InClass); } bool FMovieSceneToolsModule::IsConditionClassAllowed(const UClass* InClass) { // For now this has the same implementation as IsTrackClassAllowed, but has been kept a separate function and event in the case we want // different behavior here. return IsMovieSceneClassAllowed(InClass); } void FMovieSceneToolsModule::FixupPayloadParameterNameForSection(UMovieSceneEventSectionBase* Section, UK2Node* InNode, FName OldPinName, FName NewPinName) { check(Section && InNode); for (FMovieSceneEvent& EntryPoint : Section->GetAllEntryPoints()) { if (EntryPoint.WeakEndpoint.Get() != InNode) { continue; } if (EntryPoint.BoundObjectPinName == OldPinName) { EntryPoint.BoundObjectPinName = NewPinName; } if (FMovieSceneEventPayloadVariable* Variable = EntryPoint.PayloadVariables.Find(OldPinName)) { EntryPoint.PayloadVariables.Add(NewPinName, MoveTemp(*Variable)); EntryPoint.PayloadVariables.Remove(OldPinName); } } } void FMovieSceneToolsModule::FixupPayloadParameterNameForDynamicBinding(UMovieScene* MovieScene, UK2Node* InNode, FName OldPinName, FName NewPinName) { using namespace UE::MovieScene; check(MovieScene); auto FixupPayloadParameterName = [InNode, OldPinName, NewPinName](FMovieSceneDynamicBinding& DynamicBinding) { if (DynamicBinding.WeakEndpoint.Get() == InNode) { if (FMovieSceneDynamicBindingPayloadVariable* Variable = DynamicBinding.PayloadVariables.Find(OldPinName)) { DynamicBinding.PayloadVariables.Add(NewPinName, MoveTemp(*Variable)); DynamicBinding.PayloadVariables.Remove(OldPinName); } } }; UMovieSceneSequence* ThisSequence = MovieScene->GetTypedOuter(); TSharedRef TransientPlaybackState = MovieSceneHelpers::CreateTransientSharedPlaybackState(GEditor->GetEditorWorldContext().World(), ThisSequence); if (FMovieSceneBindingReferences* BindingReferences = MovieScene->GetTypedOuter()->GetBindingReferences()) { for (FMovieSceneBindingReference& BindingReference : BindingReferences->GetAllReferences()) { if (BindingReference.CustomBinding) { if (UMovieSceneReplaceableDirectorBlueprintBinding* ReplaceableDirectorBlueprintBinding = Cast(BindingReference.CustomBinding)) { FixupPayloadParameterName(ReplaceableDirectorBlueprintBinding->DynamicBinding); } if (UMovieSceneSpawnableDirectorBlueprintBinding* SpawnableDirectorBlueprintBinding = Cast(BindingReference.CustomBinding->AsSpawnable(TransientPlaybackState))) { FixupPayloadParameterName(SpawnableDirectorBlueprintBinding->DynamicBinding); } } } } } void FMovieSceneToolsModule::FixupPayloadParameterNameForDirectorBlueprintCondition(UMovieScene* MovieScene, UK2Node* InNode, FName OldPinName, FName NewPinName) { using namespace UE::MovieScene; check(MovieScene); auto FixupPayloadParameterName = [InNode, OldPinName, NewPinName](FMovieSceneDirectorBlueprintConditionData& DirectorBlueprintConditionData) { if (DirectorBlueprintConditionData.WeakEndpoint.Get() == InNode) { if (FMovieSceneDirectorBlueprintConditionPayloadVariable* Variable = DirectorBlueprintConditionData.PayloadVariables.Find(OldPinName)) { DirectorBlueprintConditionData.PayloadVariables.Add(NewPinName, MoveTemp(*Variable)); DirectorBlueprintConditionData.PayloadVariables.Remove(OldPinName); } } }; FMovieSceneDirectorBlueprintConditionUtils::IterateDirectorBlueprintConditions(MovieScene, FixupPayloadParameterName); } void FMovieSceneToolsModule::RegisterClipboardConversions() { using namespace MovieSceneClipboard; DefineImplicitConversion(); DefineImplicitConversion(); DefineImplicitConversion(); DefineImplicitConversion(); DefineExplicitConversion([](const int32& In) -> FMovieSceneFloatValue { return FMovieSceneFloatValue(In); }); DefineExplicitConversion([](const uint8& In) -> FMovieSceneFloatValue { return FMovieSceneFloatValue(In); }); DefineExplicitConversion([](const FMovieSceneFloatValue& In) -> int32 { return In.Value; }); DefineExplicitConversion([](const FMovieSceneFloatValue& In) -> uint8 { return In.Value; }); DefineExplicitConversion([](const FMovieSceneFloatValue& In) -> bool { return !!In.Value; }); DefineExplicitConversion([](const int32& In) -> FMovieSceneDoubleValue { return FMovieSceneDoubleValue(In); }); DefineExplicitConversion([](const uint8& In) -> FMovieSceneDoubleValue { return FMovieSceneDoubleValue(In); }); DefineExplicitConversion([](const FMovieSceneDoubleValue& In) -> int32 { return In.Value; }); DefineExplicitConversion([](const FMovieSceneDoubleValue& In) -> uint8 { return In.Value; }); DefineExplicitConversion([](const FMovieSceneDoubleValue& In) -> bool { return !!In.Value; }); FSequencerClipboardReconciler::AddTrackAlias("Location.X", "R"); FSequencerClipboardReconciler::AddTrackAlias("Location.Y", "G"); FSequencerClipboardReconciler::AddTrackAlias("Location.Z", "B"); FSequencerClipboardReconciler::AddTrackAlias("Rotation.X", "R"); FSequencerClipboardReconciler::AddTrackAlias("Rotation.Y", "G"); FSequencerClipboardReconciler::AddTrackAlias("Rotation.Z", "B"); FSequencerClipboardReconciler::AddTrackAlias("Scale.X", "R"); FSequencerClipboardReconciler::AddTrackAlias("Scale.Y", "G"); FSequencerClipboardReconciler::AddTrackAlias("Scale.Z", "B"); FSequencerClipboardReconciler::AddTrackAlias("X", "R"); FSequencerClipboardReconciler::AddTrackAlias("Y", "G"); FSequencerClipboardReconciler::AddTrackAlias("Z", "B"); FSequencerClipboardReconciler::AddTrackAlias("W", "A"); } void FMovieSceneToolsModule::RegisterAnimationBakeHelper(IMovieSceneToolsAnimationBakeHelper* InBakeHelper) { checkf(!BakeHelpers.Contains(InBakeHelper), TEXT("Bake Helper is already registered")); BakeHelpers.Add(InBakeHelper); } void FMovieSceneToolsModule::UnregisterAnimationBakeHelper(IMovieSceneToolsAnimationBakeHelper* InBakeHelper) { checkf(BakeHelpers.Contains(InBakeHelper), TEXT("Bake Helper is not registered")); BakeHelpers.Remove(InBakeHelper); } void FMovieSceneToolsModule::RegisterTakeData(IMovieSceneToolsTakeData* InTakeData) { checkf(!TakeDatas.Contains(InTakeData), TEXT("Take Data is already registered")); TakeDatas.Add(InTakeData); } void FMovieSceneToolsModule::UnregisterTakeData(IMovieSceneToolsTakeData* InTakeData) { checkf(TakeDatas.Contains(InTakeData), TEXT("Take Data is not registered")); TakeDatas.Remove(InTakeData); } void FMovieSceneToolsModule::RegisterTrackImporter(IMovieSceneToolsTrackImporter* InTrackImporter) { checkf(!TrackImporters.Contains(InTrackImporter), TEXT("Track Importer is already registered")); TrackImporters.Add(InTrackImporter); } void FMovieSceneToolsModule::UnregisterTrackImporter(IMovieSceneToolsTrackImporter* InTrackImporter) { checkf(TrackImporters.Contains(InTrackImporter), TEXT("Take Importer is not registered")); TrackImporters.Remove(InTrackImporter); } bool FMovieSceneToolsModule::GatherTakes(const UMovieSceneSection* Section, TArray& AssetData, uint32& OutCurrentTakeNumber) { for (IMovieSceneToolsTakeData* TakeData : TakeDatas) { if (TakeData->GatherTakes(Section, AssetData, OutCurrentTakeNumber)) { return true; } } return false; } bool FMovieSceneToolsModule::GetTakeNumber(const UMovieSceneSection* Section, FAssetData AssetData, uint32& OutTakeNumber) { for (IMovieSceneToolsTakeData* TakeData : TakeDatas) { if (TakeData->GetTakeNumber(Section, AssetData, OutTakeNumber)) { return true; } } return false; } bool FMovieSceneToolsModule::SetTakeNumber(const UMovieSceneSection* Section, uint32 InTakeNumber) { for (IMovieSceneToolsTakeData* TakeData : TakeDatas) { if (TakeData->SetTakeNumber(Section, InTakeNumber)) { return true; } } return false; } bool FMovieSceneToolsModule::ImportAnimatedProperty(const FString& InPropertyName, const FRichCurve& InCurve, FGuid InBinding, UMovieScene* InMovieScene) { for (IMovieSceneToolsTrackImporter* TrackImporter : TrackImporters) { if (TrackImporter->ImportAnimatedProperty(InPropertyName, InCurve, InBinding, InMovieScene)) { return true; } } return false; } bool FMovieSceneToolsModule::ImportStringProperty(const FString& InPropertyName, const FString& InStringValue, FGuid InBinding, UMovieScene* InMovieScene) { for (IMovieSceneToolsTrackImporter* TrackImporter : TrackImporters) { if (TrackImporter->ImportStringProperty(InPropertyName, InStringValue, InBinding, InMovieScene)) { return true; } } return false; } void FMovieSceneToolsModule::RegisterKeyStructInstancedPropertyTypeCustomizer(IMovieSceneToolsKeyStructInstancedPropertyTypeCustomizer* InCustomizer) { checkf(!KeyStructInstancedPropertyTypeCustomizers.Contains(InCustomizer), TEXT("Key Struct Instanced Property Type Customizer is already registered")); KeyStructInstancedPropertyTypeCustomizers.Add(InCustomizer); } void FMovieSceneToolsModule::UnregisterKeyStructInstancedPropertyTypeCustomizer(IMovieSceneToolsKeyStructInstancedPropertyTypeCustomizer* InCustomizer) { checkf(KeyStructInstancedPropertyTypeCustomizers.Contains(InCustomizer), TEXT("Key Struct Instanced Property Type Customizer is not registered")); KeyStructInstancedPropertyTypeCustomizers.Remove(InCustomizer); } void FMovieSceneToolsModule::CustomizeKeyStructInstancedPropertyTypes(TSharedRef StructureDetailsView, TWeakObjectPtr Section) { for (IMovieSceneToolsKeyStructInstancedPropertyTypeCustomizer* Customizer : KeyStructInstancedPropertyTypeCustomizers) { if (Customizer) { Customizer->RegisterKeyStructInstancedPropertyTypeCustomization(StructureDetailsView, Section); } } } IMPLEMENT_MODULE( FMovieSceneToolsModule, MovieSceneTools ); #undef LOCTEXT_NAMESPACE