HAxis sos
This commit is contained in:
		| @@ -0,0 +1,23 @@ | ||||
| // Copyright (c), Firelight Technologies Pty, Ltd. 2012-2016. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "FMODAmbientSoundActorFactory.generated.h" | ||||
|  | ||||
| /** FMOD Ambient Sound Actor Factory. | ||||
| */ | ||||
| UCLASS(MinimalAPI, config=Editor, collapsecategories, hidecategories=Object) | ||||
| class UFMODAmbientSoundActorFactory : public UActorFactory | ||||
| { | ||||
| 	GENERATED_UCLASS_BODY() | ||||
|  | ||||
| 	// Begin UActorFactory Interface | ||||
| 	virtual void PostSpawnActor( UObject* Asset, AActor* NewActor ) override; | ||||
| 	virtual void PostCreateBlueprint( UObject* Asset, AActor* CDO ) override; | ||||
| 	virtual bool CanCreateActorFrom( const FAssetData& AssetData, FText& OutErrorMsg ) override; | ||||
| 	virtual UObject* GetAssetFromActorInstance(AActor* ActorInstance) override; | ||||
| 	// End UActorFactory Interface | ||||
| }; | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -0,0 +1,58 @@ | ||||
| // Copyright (c), Firelight Technologies Pty, Ltd. 2012-2016. | ||||
|  | ||||
| namespace UnrealBuildTool.Rules | ||||
| { | ||||
| 	public class FMODStudioEditor : ModuleRules | ||||
| 	{ | ||||
| 		public FMODStudioEditor(TargetInfo Target) | ||||
| 		{ | ||||
| 			bFasterWithoutUnity = true; | ||||
|  | ||||
| 			PublicIncludePaths.AddRange( | ||||
| 				new string[] { | ||||
| 				} | ||||
| 				); | ||||
|  | ||||
| 			PrivateIncludePaths.AddRange( | ||||
| 				new string[] { | ||||
| 					"FMODStudioEditor/Private", | ||||
| 					"FMODStudio/Private", | ||||
| 				} | ||||
| 				); | ||||
|  | ||||
| 			PublicDependencyModuleNames.AddRange( | ||||
| 				new string[] | ||||
| 				{ | ||||
| 					"Core", | ||||
| 					"CoreUObject", | ||||
| 					"Engine", | ||||
| 					"FMODStudio" | ||||
| 				} | ||||
| 				); | ||||
|  | ||||
| 			PrivateDependencyModuleNames.AddRange( | ||||
| 				new string[] | ||||
| 				{ | ||||
| 					"UnrealEd", | ||||
| 					"Slate", | ||||
| 					"SlateCore", | ||||
| 					"InputCore", | ||||
| 					"Settings", | ||||
| 					"EditorStyle", | ||||
| 					"LevelEditor", | ||||
| 					"AssetTools", | ||||
| 					"AssetRegistry", | ||||
| 					"PropertyEditor", | ||||
| 					"WorkspaceMenuStructure", | ||||
| 					"Sockets" | ||||
| 				} | ||||
| 				); | ||||
|  | ||||
| 			DynamicallyLoadedModuleNames.AddRange( | ||||
| 				new string[] | ||||
| 				{ | ||||
| 				} | ||||
| 				); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,152 @@ | ||||
| // Copyright (c), Firelight Technologies Pty, Ltd. 2012-2016. | ||||
|  | ||||
| #include "FMODStudioEditorPrivatePCH.h" | ||||
| #include "AssetTypeActions_Base.h" | ||||
| #include "AssetTypeActions_FMODEvent.h" | ||||
| #include "FMODEventEditor.h" | ||||
| #include "FMODEvent.h" | ||||
| #include "FMODUtils.h" | ||||
| #include "FMODStudioModule.h" | ||||
| #include "FMODStudioEditorModule.h" | ||||
|  | ||||
| #define LOCTEXT_NAMESPACE "AssetTypeActions" | ||||
|  | ||||
| FAssetTypeActions_FMODEvent::FAssetTypeActions_FMODEvent() | ||||
| 	: CurrentPreviewEventInstance(nullptr) | ||||
| { | ||||
| 	BeginPIEDelegateHandle = FEditorDelegates::BeginPIE.AddRaw(this, &FAssetTypeActions_FMODEvent::HandleBeginPIE); | ||||
| 	IFMODStudioModule::Get().BanksReloadedEvent().AddRaw(this, &FAssetTypeActions_FMODEvent::HandleBanksReloaded); | ||||
| } | ||||
|  | ||||
| FAssetTypeActions_FMODEvent::~FAssetTypeActions_FMODEvent() | ||||
| { | ||||
| 	FEditorDelegates::BeginPIE.Remove(BeginPIEDelegateHandle); | ||||
| 	IFMODStudioModule::Get().BanksReloadedEvent().RemoveAll(this); | ||||
| 	IFMODStudioModule::Get().StopAuditioningInstance(); | ||||
| } | ||||
|  | ||||
| UClass* FAssetTypeActions_FMODEvent::GetSupportedClass() const | ||||
| { | ||||
| 	return UFMODEvent::StaticClass(); | ||||
| } | ||||
|  | ||||
| void FAssetTypeActions_FMODEvent::GetActions(const TArray<UObject*>& InObjects, FMenuBuilder& MenuBuilder) | ||||
| { | ||||
| 	auto Events = GetTypedWeakObjectPtrs<UFMODEvent>(InObjects); | ||||
|  | ||||
|  | ||||
| 	MenuBuilder.AddMenuEntry( | ||||
| 		LOCTEXT("FMODEvent_Play", "Play"), | ||||
| 		LOCTEXT("FMODEvent_PlayTooltip", "Plays the selected FMOD event."), | ||||
| 		FSlateIcon(FEditorStyle::GetStyleSetName(), "MediaAsset.AssetActions.Play"), | ||||
| 		FUIAction( | ||||
| 			FExecuteAction::CreateSP(this, &FAssetTypeActions_FMODEvent::ExecutePlay, Events), | ||||
| 			FCanExecuteAction::CreateSP(this, &FAssetTypeActions_FMODEvent::CanExecutePlayCommand, Events) | ||||
| 			) | ||||
| 		); | ||||
|  | ||||
| 	MenuBuilder.AddMenuEntry( | ||||
| 		LOCTEXT("FMODEvent_Stop", "Stop"), | ||||
| 		LOCTEXT("FMODEvent_StopTooltip", "Stops the currently playing FMOD event."), | ||||
| 		FSlateIcon(FEditorStyle::GetStyleSetName(), "MediaAsset.AssetActions.Stop"), | ||||
| 		FUIAction( | ||||
| 			FExecuteAction::CreateSP(this, &FAssetTypeActions_FMODEvent::ExecuteStop, Events), | ||||
| 			FCanExecuteAction() | ||||
| 			) | ||||
| 		); | ||||
| } | ||||
|  | ||||
| void FAssetTypeActions_FMODEvent::OpenAssetEditor(const TArray<UObject*>& InObjects, TSharedPtr<IToolkitHost> EditWithinLevelEditor) | ||||
| { | ||||
| 	EToolkitMode::Type Mode = EditWithinLevelEditor.IsValid() ? EToolkitMode::WorldCentric : EToolkitMode::Standalone; | ||||
|  | ||||
| 	for (auto ObjIt = InObjects.CreateConstIterator(); ObjIt; ++ObjIt) | ||||
| 	{ | ||||
| 		auto Event = Cast<UFMODEvent>(*ObjIt); | ||||
| 		if (Event != nullptr) | ||||
| 		{ | ||||
| 			TSharedRef<FFMODEventEditor> NewFMODEventEditor(new FFMODEventEditor()); | ||||
| 			NewFMODEventEditor->InitFMODEventEditor(Mode, EditWithinLevelEditor, Event); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool FAssetTypeActions_FMODEvent::CanExecutePlayCommand(TArray<TWeakObjectPtr<UFMODEvent>> Objects) const | ||||
| { | ||||
| 	return Objects.Num() == 1; | ||||
| } | ||||
|  | ||||
| void FAssetTypeActions_FMODEvent::AssetsActivated(const TArray<UObject*>& InObjects, EAssetTypeActivationMethod::Type ActivationType) | ||||
| { | ||||
| 	if (ActivationType == EAssetTypeActivationMethod::Previewed) | ||||
| 	{ | ||||
| 		for (auto ObjIt = InObjects.CreateConstIterator(); ObjIt; ++ObjIt) | ||||
| 		{ | ||||
| 			UFMODEvent* Event = Cast<UFMODEvent>(*ObjIt); | ||||
| 			if (Event != nullptr) | ||||
| 			{ | ||||
| 				// Only play the first valid event | ||||
| 				PlayEvent(Event); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		FAssetTypeActions_Base::AssetsActivated(InObjects, ActivationType); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void FAssetTypeActions_FMODEvent::ExecuteEdit(TArray<TWeakObjectPtr<UFMODEvent>> Objects) | ||||
| { | ||||
| 	for (auto ObjIt = Objects.CreateConstIterator(); ObjIt; ++ObjIt) | ||||
| 	{ | ||||
| 		auto Object = (*ObjIt).Get(); | ||||
| 		if (Object != nullptr) | ||||
| 		{ | ||||
| 			FAssetEditorManager::Get().OpenEditorForAsset(Object); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void FAssetTypeActions_FMODEvent::ExecutePlay(TArray<TWeakObjectPtr<UFMODEvent>> Objects) | ||||
| { | ||||
| 	for (auto ObjIt = Objects.CreateConstIterator(); ObjIt; ++ObjIt) | ||||
| 	{ | ||||
| 		UFMODEvent* Event = (*ObjIt).Get(); | ||||
| 		if (Event != nullptr) | ||||
| 		{ | ||||
| 			// Only play the first valid event | ||||
| 			PlayEvent(Event); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void FAssetTypeActions_FMODEvent::ExecuteStop(TArray<TWeakObjectPtr<UFMODEvent>> Objects) | ||||
| { | ||||
| 	IFMODStudioModule::Get().StopAuditioningInstance(); | ||||
| } | ||||
|  | ||||
| void FAssetTypeActions_FMODEvent::PlayEvent(UFMODEvent* Event) | ||||
| { | ||||
| 	CurrentPreviewEventInstance = IFMODStudioModule::Get().CreateAuditioningInstance(Event); | ||||
| 	if (CurrentPreviewEventInstance != nullptr) | ||||
| 	{ | ||||
| 		CurrentPreviewEventInstance->start(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void FAssetTypeActions_FMODEvent::HandleBeginPIE(bool bSimulating) | ||||
| { | ||||
| 	// Studio module will handle its own auditioning, just clear the handle | ||||
| 	CurrentPreviewEventInstance = nullptr; | ||||
| } | ||||
|  | ||||
| void FAssetTypeActions_FMODEvent::HandleBanksReloaded() | ||||
| { | ||||
| 	// Studio module will handle its own auditioning, just clear the handle | ||||
| 	CurrentPreviewEventInstance = nullptr; | ||||
| } | ||||
|  | ||||
| #undef LOCTEXT_NAMESPACE | ||||
| @@ -0,0 +1,55 @@ | ||||
| // Copyright (c), Firelight Technologies Pty, Ltd. 2012-2016. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "AssetTypeActions_Base.h" | ||||
|  | ||||
| namespace FMOD | ||||
| { | ||||
| 	namespace Studio | ||||
| 	{ | ||||
| 		class EventInstance; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| class UFMODEvent; | ||||
|  | ||||
| class FAssetTypeActions_FMODEvent : public FAssetTypeActions_Base | ||||
| { | ||||
| public: | ||||
| 	FAssetTypeActions_FMODEvent(); | ||||
| 	~FAssetTypeActions_FMODEvent(); | ||||
|  | ||||
| 	// IAssetTypeActions Implementation | ||||
| 	virtual FText GetName() const override { return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_FMODEvent", "FMOD Event"); } | ||||
| 	virtual FColor GetTypeColor() const override { return FColor(0, 175, 255); } | ||||
| 	virtual UClass* GetSupportedClass() const override; | ||||
| 	virtual bool HasActions(const TArray<UObject*>& InObjects) const override { return true; } | ||||
| 	virtual void GetActions(const TArray<UObject*>& InObjects, FMenuBuilder& MenuBuilder) override; | ||||
| 	virtual void AssetsActivated(const TArray<UObject*>& InObjects, EAssetTypeActivationMethod::Type ActivationType) override; | ||||
| 	virtual void OpenAssetEditor(const TArray<UObject*>& InObjects, TSharedPtr<class IToolkitHost> EditWithinLevelEditor = TSharedPtr<IToolkitHost>()) override; | ||||
| 	virtual bool CanFilter() override { return false; } | ||||
| 	virtual uint32 GetCategories() override { return EAssetTypeCategories::Sounds; } | ||||
|  | ||||
| private: | ||||
| 	/** Returns true if only one event is selected to play */ | ||||
| 	bool CanExecutePlayCommand(TArray<TWeakObjectPtr<UFMODEvent>> Objects) const; | ||||
|  | ||||
| 	/** Handler for when Edit is selected */ | ||||
| 	void ExecuteEdit(TArray<TWeakObjectPtr<UFMODEvent>> Objects); | ||||
|  | ||||
| 	/** Handler for when Play is selected */ | ||||
| 	void ExecutePlay(TArray<TWeakObjectPtr<UFMODEvent>> Objects); | ||||
|  | ||||
| 	/** Handler for when Stop is selected */ | ||||
| 	void ExecuteStop(TArray<TWeakObjectPtr<UFMODEvent>> Objects); | ||||
|  | ||||
| 	/** Plays the event */ | ||||
| 	void PlayEvent(UFMODEvent* Event); | ||||
|  | ||||
| 	void HandleBeginPIE(bool bSimulating); | ||||
| 	void HandleBanksReloaded(); | ||||
|  | ||||
| 	FMOD::Studio::EventInstance* CurrentPreviewEventInstance; | ||||
| 	FDelegateHandle BeginPIEDelegateHandle; | ||||
| }; | ||||
| @@ -0,0 +1,66 @@ | ||||
| // Copyright (c), Firelight Technologies Pty, Ltd. 2012-2016. | ||||
|  | ||||
| #include "FMODStudioEditorPrivatePCH.h" | ||||
| #include "AssetData.h" | ||||
| #include "FMODAmbientSoundActorFactory.h" | ||||
| #include "FMODAmbientSound.h" | ||||
| #include "FMODEvent.h" | ||||
|  | ||||
| UFMODAmbientSoundActorFactory::UFMODAmbientSoundActorFactory(const FObjectInitializer& ObjectInitializer) | ||||
| 	: Super(ObjectInitializer) | ||||
| { | ||||
| 	DisplayName = NSLOCTEXT("FMOD", "FMODAmbientSoundDisplayName", "FMOD Ambient Sound"); | ||||
| 	NewActorClass = AFMODAmbientSound::StaticClass(); | ||||
| } | ||||
|  | ||||
| bool UFMODAmbientSoundActorFactory::CanCreateActorFrom( const FAssetData& AssetData, FText& OutErrorMsg ) | ||||
| { | ||||
| 	//We allow creating AAmbientSounds without an existing sound asset | ||||
| 	if ( UActorFactory::CanCreateActorFrom( AssetData, OutErrorMsg ) ) | ||||
| 	{ | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	if ( AssetData.IsValid() && !AssetData.GetClass()->IsChildOf( UFMODEvent::StaticClass() ) ) | ||||
| 	{ | ||||
| 		OutErrorMsg = NSLOCTEXT("FMOD", "CanCreateActorFrom_NoFMODEventAsset", "A valid FMOD Event asset must be specified."); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void UFMODAmbientSoundActorFactory::PostSpawnActor( UObject* Asset, AActor* NewActor) | ||||
| { | ||||
| 	UFMODEvent* Event = Cast<UFMODEvent>( Asset ); | ||||
|  | ||||
| 	if ( Event != NULL ) | ||||
| 	{ | ||||
| 		AFMODAmbientSound* NewSound = CastChecked<AFMODAmbientSound>( NewActor ); | ||||
| 		FActorLabelUtilities::SetActorLabelUnique(NewSound, Event->GetName()); | ||||
| 		NewSound->AudioComponent->Event = Event; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| UObject* UFMODAmbientSoundActorFactory::GetAssetFromActorInstance(AActor* Instance) | ||||
| { | ||||
| 	check(Instance->IsA(NewActorClass)); | ||||
| 	AFMODAmbientSound* SoundActor = CastChecked<AFMODAmbientSound>(Instance); | ||||
|  | ||||
| 	check(SoundActor->AudioComponent); | ||||
| 	return SoundActor->AudioComponent->Event.Get(); | ||||
| } | ||||
|  | ||||
| void UFMODAmbientSoundActorFactory::PostCreateBlueprint( UObject* Asset, AActor* CDO ) | ||||
| { | ||||
| 	if (Asset != NULL && CDO != NULL) | ||||
| 	{ | ||||
| 		UFMODEvent* Event = Cast<UFMODEvent>(Asset); | ||||
|  | ||||
| 		if (Event != NULL) | ||||
| 		{ | ||||
| 			AFMODAmbientSound* NewSound = CastChecked<AFMODAmbientSound>(CDO); | ||||
| 			NewSound->AudioComponent->Event = Event; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,119 @@ | ||||
| // Copyright (c), Firelight Technologies Pty, Ltd. 2012-2016. | ||||
|  | ||||
| #include "FMODStudioEditorPrivatePCH.h" | ||||
| #include "FMODAmbientSoundDetails.h" | ||||
| #include "Toolkits/AssetEditorManager.h" | ||||
| #include "FMODAmbientSound.h" | ||||
| #include "FMODStudioModule.h" | ||||
| #include "FMODEvent.h" | ||||
| #include "fmod_studio.hpp" | ||||
|  | ||||
| #define LOCTEXT_NAMESPACE "FMODStudio" | ||||
|  | ||||
| TSharedRef<IDetailCustomization> FFMODAmbientSoundDetails::MakeInstance() | ||||
| { | ||||
| 	return MakeShareable( new FFMODAmbientSoundDetails ); | ||||
| } | ||||
|  | ||||
| void FFMODAmbientSoundDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) | ||||
| { | ||||
| 	const TArray< TWeakObjectPtr<UObject> >& SelectedObjects = DetailBuilder.GetDetailsView().GetSelectedObjects(); | ||||
|  | ||||
| 	for( int32 ObjectIndex = 0; !AmbientSound.IsValid() && ObjectIndex < SelectedObjects.Num(); ++ObjectIndex ) | ||||
| 	{ | ||||
| 		const TWeakObjectPtr<UObject>& CurrentObject = SelectedObjects[ObjectIndex]; | ||||
| 		if ( CurrentObject.IsValid() ) | ||||
| 		{ | ||||
| 			AmbientSound = Cast<AFMODAmbientSound>(CurrentObject.Get()); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	DetailBuilder.EditCategory(TEXT("Sound")) | ||||
| 		.AddCustomRow(FText::GetEmpty()) | ||||
| 		[ | ||||
| 			SNew(SVerticalBox) | ||||
| 			+ SVerticalBox::Slot() | ||||
| 			.Padding( 0, 2.0f, 0, 0 ) | ||||
| 			.FillHeight(1.0f) | ||||
| 			.VAlign( VAlign_Center ) | ||||
| 			[ | ||||
| 				SNew(SHorizontalBox) | ||||
| 				+SHorizontalBox::Slot() | ||||
| 					.AutoWidth() | ||||
| 					.Padding( 2.0f, 0.0f ) | ||||
| 					.VAlign(VAlign_Center) | ||||
| 					.HAlign(HAlign_Left) | ||||
| 					[ | ||||
| 						SNew(SButton) | ||||
| 						.VAlign(VAlign_Center) | ||||
| 						.OnClicked( this, &FFMODAmbientSoundDetails::OnEditSoundClicked ) | ||||
| 						.Text( LOCTEXT("EditAsset", "Edit") ) | ||||
| 						.ToolTipText( LOCTEXT("EditAssetToolTip", "Edit this sound cue") ) | ||||
| 					] | ||||
| 				+SHorizontalBox::Slot() | ||||
| 					.AutoWidth() | ||||
| 					.Padding( 2.0f, 0.0f ) | ||||
| 					.VAlign(VAlign_Center) | ||||
| 					.HAlign(HAlign_Left) | ||||
| 					[ | ||||
| 						SNew(SButton) | ||||
| 						.VAlign(VAlign_Center) | ||||
| 						.OnClicked( this, &FFMODAmbientSoundDetails::OnPlaySoundClicked ) | ||||
| 						.Text( LOCTEXT("PlaySoundCue", "Play") ) | ||||
| 					] | ||||
| 				+SHorizontalBox::Slot() | ||||
| 					.AutoWidth() | ||||
| 					.Padding( 2.0f, 0.0f ) | ||||
| 					.VAlign(VAlign_Center) | ||||
| 					.HAlign(HAlign_Left) | ||||
| 					[ | ||||
| 						SNew(SButton) | ||||
| 						.VAlign(VAlign_Center) | ||||
| 						.OnClicked( this, &FFMODAmbientSoundDetails::OnStopSoundClicked ) | ||||
| 						.Text( LOCTEXT("StopSoundCue", "Stop") ) | ||||
| 					] | ||||
| 			] | ||||
| 		]; | ||||
| } | ||||
|  | ||||
|  | ||||
| FReply FFMODAmbientSoundDetails::OnEditSoundClicked() | ||||
| { | ||||
| 	if( AmbientSound.IsValid() ) | ||||
| 	{ | ||||
| 		UFMODEvent* Event = AmbientSound.Get()->AudioComponent->Event.Get(); | ||||
| 		if (Event) | ||||
| 		{ | ||||
| 			FAssetEditorManager::Get().OpenEditorForAsset(Event); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return FReply::Handled(); | ||||
| } | ||||
|  | ||||
| FReply FFMODAmbientSoundDetails::OnPlaySoundClicked() | ||||
| { | ||||
| 	if( AmbientSound.IsValid() ) | ||||
| 	{ | ||||
| 		UFMODEvent* Event = AmbientSound.Get()->AudioComponent->Event.Get(); | ||||
| 		if (Event) | ||||
| 		{ | ||||
| 			FMOD::Studio::EventInstance* Instance = IFMODStudioModule::Get().CreateAuditioningInstance(Event); | ||||
| 			if (Instance) | ||||
| 			{ | ||||
| 				Instance->start(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return FReply::Handled(); | ||||
| } | ||||
|  | ||||
| FReply FFMODAmbientSoundDetails::OnStopSoundClicked() | ||||
| { | ||||
| 	IFMODStudioModule::Get().StopAuditioningInstance(); | ||||
|  | ||||
| 	return FReply::Handled(); | ||||
| } | ||||
|  | ||||
| #undef LOCTEXT_NAMESPACE | ||||
| @@ -0,0 +1,23 @@ | ||||
| // Copyright (c), Firelight Technologies Pty, Ltd. 2012-2016. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "PropertyEditing.h" | ||||
| #include "PropertyCustomizationHelpers.h" | ||||
|  | ||||
| class FFMODAmbientSoundDetails : public IDetailCustomization | ||||
| { | ||||
| public: | ||||
| 	/** Makes a new instance of this detail layout class for a specific detail view requesting it */ | ||||
| 	static TSharedRef<IDetailCustomization> MakeInstance(); | ||||
|  | ||||
| private: | ||||
| 	/** IDetailCustomization interface */ | ||||
| 	virtual void CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) override; | ||||
|  | ||||
| 	FReply OnEditSoundClicked(); | ||||
| 	FReply OnPlaySoundClicked(); | ||||
| 	FReply OnStopSoundClicked(); | ||||
|  | ||||
| 	TWeakObjectPtr<class AFMODAmbientSound> AmbientSound; | ||||
| }; | ||||
| @@ -0,0 +1,43 @@ | ||||
| // Copyright (c), Firelight Technologies Pty, Ltd. 2012-2016. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "ComponentAssetBroker.h" | ||||
| #include "FMODEvent.h" | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////////////// | ||||
| // FFMODAssetBroker | ||||
|  | ||||
| class FFMODAssetBroker : public IComponentAssetBroker | ||||
| { | ||||
| public: | ||||
| 	UClass* GetSupportedAssetClass() override | ||||
| 	{ | ||||
| 		return UFMODEvent::StaticClass(); | ||||
| 	} | ||||
|  | ||||
| 	virtual bool AssignAssetToComponent(UActorComponent* InComponent, UObject* InAsset) override | ||||
| 	{ | ||||
| 		if (UFMODAudioComponent* AudioComp = Cast<UFMODAudioComponent>(InComponent)) | ||||
| 		{ | ||||
| 			UFMODEvent* Event = Cast<UFMODEvent>(InAsset); | ||||
|  | ||||
| 			if ((Event != NULL) || (InAsset == NULL)) | ||||
| 			{ | ||||
| 				AudioComp->Event = Event; | ||||
| 				return true; | ||||
| 			} | ||||
| 		} | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	virtual UObject* GetAssetFromComponent(UActorComponent* InComponent) override | ||||
| 	{ | ||||
| 		if (UFMODAudioComponent* AudioComp = Cast<UFMODAudioComponent>(InComponent)) | ||||
| 		{ | ||||
| 			return AudioComp->Event.Get(); | ||||
| 		} | ||||
| 		return NULL; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| @@ -0,0 +1,45 @@ | ||||
| // Copyright (c), Firelight Technologies Pty, Ltd. 2012-2016. | ||||
|  | ||||
| #include "FMODStudioEditorPrivatePCH.h" | ||||
| #include "FMODAudioComponentVisualizer.h" | ||||
| #include "FMODAudioComponent.h" | ||||
| #include "FMODUtils.h" | ||||
| #include "FMODEvent.h" | ||||
| #include "fmod_studio.hpp" | ||||
|  | ||||
| void FFMODAudioComponentVisualizer::DrawVisualization(const UActorComponent* Component, const FSceneView* View, FPrimitiveDrawInterface* PDI) | ||||
| { | ||||
| 	if (View->Family->EngineShowFlags.AudioRadius) | ||||
| 	{ | ||||
| 		const UFMODAudioComponent* AudioComp = Cast<const UFMODAudioComponent>(Component); | ||||
| 		if (AudioComp != nullptr && AudioComp->Event.IsValid()) | ||||
| 		{ | ||||
| 			FMOD::Studio::EventDescription* EventDesc = IFMODStudioModule::Get().GetEventDescription(AudioComp->Event.Get(), EFMODSystemContext::Auditioning); | ||||
| 			if (EventDesc != nullptr) | ||||
| 			{ | ||||
| 				bool bIs3D = false; | ||||
| 				EventDesc->is3D(&bIs3D); | ||||
| 				if (bIs3D) | ||||
| 				{ | ||||
| 					const FColor AudioOuterRadiusColor(255, 153, 0); | ||||
| 					const FColor AudioInnerRadiusColor(216, 130, 0); | ||||
|  | ||||
| 					const FTransform& Transform = AudioComp->ComponentToWorld; | ||||
|  | ||||
| 					float MinDistance = 0.0f; | ||||
| 					float MaxDistance = 0.0f; | ||||
| 					EventDesc->getMinimumDistance(&MinDistance); | ||||
| 					EventDesc->getMaximumDistance(&MaxDistance); | ||||
| 					MinDistance = FMODUtils::DistanceToUEScale(MinDistance); | ||||
| 					MaxDistance = FMODUtils::DistanceToUEScale(MaxDistance); | ||||
|  | ||||
| 					DrawWireSphereAutoSides(PDI, Transform.GetTranslation(), AudioOuterRadiusColor, MinDistance, SDPG_World); | ||||
| 					if (MaxDistance != MinDistance) | ||||
| 					{ | ||||
| 						DrawWireSphereAutoSides(PDI, Transform.GetTranslation(), AudioInnerRadiusColor, MaxDistance, SDPG_World); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,13 @@ | ||||
| // Copyright (c), Firelight Technologies Pty, Ltd. 2012-2016. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "ComponentVisualizer.h" | ||||
|  | ||||
| class FFMODAudioComponentVisualizer : public FComponentVisualizer | ||||
| { | ||||
| public: | ||||
| 	// Begin FComponentVisualizer interface | ||||
| 	virtual void DrawVisualization(const UActorComponent* Component, const FSceneView* View, FPrimitiveDrawInterface* PDI) override; | ||||
| 	// End FComponentVisualizer interface | ||||
| }; | ||||
| @@ -0,0 +1,187 @@ | ||||
| // Copyright (c), Firelight Technologies Pty, Ltd. 2012-2016. | ||||
|  | ||||
| #include "FMODStudioEditorPrivatePCH.h" | ||||
| #include "FMODEventEditor.h" | ||||
| #include "FMODEvent.h" | ||||
| #include "FMODStudioModule.h" | ||||
| #include "FMODUtils.h" | ||||
| #include "SFMODEventEditorPanel.h" | ||||
| #include "SDockTab.h" | ||||
| //#include "WorkspaceMenuStructureModule.h" | ||||
| #include "fmod_studio.hpp" | ||||
|  | ||||
| #define LOCTEXT_NAMESPACE "FMODEventEditor" | ||||
|  | ||||
| DEFINE_LOG_CATEGORY_STATIC(LogFMODEventEditor, Log, All); | ||||
|  | ||||
| const FName FFMODEventEditor::EventEditorTabId(TEXT("FFMODEventEditor_EventView")); | ||||
| const FName FFMODEventEditor::FMODEventEditorAppIdentifier(TEXT("FMODEventEditorApp")); | ||||
|  | ||||
| void FFMODEventEditor::RegisterTabSpawners(const TSharedRef<class FTabManager>& TabManager) | ||||
| { | ||||
| 	WorkspaceMenuCategory = TabManager->AddLocalWorkspaceMenuCategory(LOCTEXT("WorkspaceMenu_FMODEventEditor", "FMOD Event Editor")); | ||||
| 	auto WorkspaceMenuCategoryRef = WorkspaceMenuCategory.ToSharedRef(); | ||||
|  | ||||
| 	FAssetEditorToolkit::RegisterTabSpawners(TabManager); | ||||
|  | ||||
| 	TabManager->RegisterTabSpawner( | ||||
| 		EventEditorTabId, | ||||
| 		FOnSpawnTab::CreateSP(this, &FFMODEventEditor::SpawnTab_EventEditor)) | ||||
| 		.SetDisplayName(LOCTEXT("EventTab", "FMOD Event")) | ||||
| 		.SetGroup(WorkspaceMenuCategoryRef); | ||||
| } | ||||
|  | ||||
| void FFMODEventEditor::UnregisterTabSpawners(const TSharedRef<class FTabManager>& TabManager) | ||||
| { | ||||
| 	FAssetEditorToolkit::UnregisterTabSpawners(TabManager); | ||||
|  | ||||
| 	TabManager->UnregisterTabSpawner(EventEditorTabId); | ||||
| } | ||||
|  | ||||
| FFMODEventEditor::FFMODEventEditor() | ||||
| 	: CurrentPreviewEventInstance(nullptr) | ||||
| { | ||||
| 	IFMODStudioModule::Get().BanksReloadedEvent().AddRaw(this, &FFMODEventEditor::HandleBanksReloaded); | ||||
| 	BeginPIEDelegateHandle = FEditorDelegates::BeginPIE.AddRaw(this, &FFMODEventEditor::HandleBeginPIE); | ||||
| } | ||||
|  | ||||
| FFMODEventEditor::~FFMODEventEditor() | ||||
| { | ||||
| 	IFMODStudioModule::Get().BanksReloadedEvent().RemoveAll(this); | ||||
| 	FEditorDelegates::BeginPIE.Remove(BeginPIEDelegateHandle); | ||||
|  | ||||
| 	CurrentPreviewEventInstance = nullptr; | ||||
| } | ||||
|  | ||||
| UFMODEvent* FFMODEventEditor::GetEditedEvent() const | ||||
| { | ||||
| 	return EditedEvent; | ||||
| } | ||||
|  | ||||
| FMOD::Studio::EventDescription* FFMODEventEditor::GetEventDescription() const | ||||
| { | ||||
| 	return IFMODStudioModule::Get().GetEventDescription(EditedEvent, EFMODSystemContext::Auditioning); | ||||
| } | ||||
|  | ||||
| void FFMODEventEditor::PlayEvent() | ||||
| { | ||||
| 	CurrentPreviewEventInstance = IFMODStudioModule::Get().CreateAuditioningInstance(EditedEvent); | ||||
| 	if (CurrentPreviewEventInstance != nullptr) | ||||
| 	{ | ||||
| 		for (int32 ParamIdx = 0; ParamIdx < ParameterValues.Num(); ParamIdx++) | ||||
| 		{ | ||||
| 			CurrentPreviewEventInstance->setParameterValueByIndex(ParamIdx, ParameterValues[ParamIdx]); | ||||
| 		} | ||||
|  | ||||
| 		CurrentPreviewEventInstance->start(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void FFMODEventEditor::PauseEvent() | ||||
| { | ||||
| 	if (CurrentPreviewEventInstance != nullptr) | ||||
| 	{ | ||||
| 		bool bIsPaused = false; | ||||
| 		CurrentPreviewEventInstance->getPaused(&bIsPaused); | ||||
| 		CurrentPreviewEventInstance->setPaused(!bIsPaused); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void FFMODEventEditor::StopEvent() | ||||
| { | ||||
| 	IFMODStudioModule::Get().StopAuditioningInstance(); | ||||
| } | ||||
|  | ||||
| void FFMODEventEditor::SetParameterValue(int32 ParameterIdx, float Value) | ||||
| { | ||||
| 	ParameterValues[ParameterIdx] = Value; | ||||
|  | ||||
| 	if (CurrentPreviewEventInstance != nullptr) | ||||
| 	{ | ||||
| 		CurrentPreviewEventInstance->setParameterValueByIndex(ParameterIdx, Value); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| TArray<float>& FFMODEventEditor::GetParameterValues() | ||||
| { | ||||
| 	return ParameterValues; | ||||
| } | ||||
|  | ||||
| void FFMODEventEditor::InitFMODEventEditor(const EToolkitMode::Type Mode, const TSharedPtr< class IToolkitHost >& InitToolkitHost, UFMODEvent* Event) | ||||
| { | ||||
| 	EditedEvent = Event; | ||||
|  | ||||
| 	TSharedRef<FTabManager::FLayout> StandaloneDefaultLayout = FTabManager::NewLayout("Standalone_FMODEventEditor_Layout") | ||||
| 	->AddArea | ||||
| 	( | ||||
| 		FTabManager::NewPrimaryArea()->SetOrientation(Orient_Vertical) | ||||
| 		->Split | ||||
| 		( | ||||
| 			FTabManager::NewStack() | ||||
| 			->AddTab(EventEditorTabId, ETabState::OpenedTab)->SetHideTabWell(true) | ||||
| 		) | ||||
| 	); | ||||
|  | ||||
| 	const bool bCreateDefaultStandaloneMenu = true; | ||||
| 	const bool bCreateDefaultToolbar = false; | ||||
| 	FAssetEditorToolkit::InitAssetEditor(Mode, InitToolkitHost, FFMODEventEditor::FMODEventEditorAppIdentifier, StandaloneDefaultLayout, bCreateDefaultStandaloneMenu, bCreateDefaultToolbar, Event); | ||||
| } | ||||
|  | ||||
| FName FFMODEventEditor::GetToolkitFName() const | ||||
| { | ||||
| 	return FName("FMODEventEditor"); | ||||
| } | ||||
|  | ||||
| FText FFMODEventEditor::GetBaseToolkitName() const | ||||
| { | ||||
| 	return LOCTEXT("ToolkitName", "FMOD Event Editor"); | ||||
| } | ||||
|  | ||||
| FString FFMODEventEditor::GetWorldCentricTabPrefix() const | ||||
| { | ||||
| 	return LOCTEXT("WorldCentricTabPrefix", "FMOD Event ").ToString(); | ||||
| } | ||||
|  | ||||
| FLinearColor FFMODEventEditor::GetWorldCentricTabColorScale() const | ||||
| { | ||||
| 	return FLinearColor(0.0f, 0.0f, 0.5f, 0.5f); | ||||
| } | ||||
|  | ||||
| void FFMODEventEditor::CreateInternalWidgets() | ||||
| { | ||||
| 	FMODEventEditorPanel = SNew(SFMODEventEditorPanel) | ||||
| 		.FMODEventEditor(SharedThis(this)); | ||||
| } | ||||
|  | ||||
| TSharedRef<SDockTab> FFMODEventEditor::SpawnTab_EventEditor(const FSpawnTabArgs& Args) | ||||
| { | ||||
| 	check(Args.GetTabId().TabType == EventEditorTabId); | ||||
|  | ||||
| 	CreateInternalWidgets(); | ||||
|  | ||||
| 	return SAssignNew(OwnerTab, SDockTab) | ||||
| 		.Label(LOCTEXT("EventEditorTitle", "FMOD Event")) | ||||
| 		.TabColorScale(GetTabColorScale()) | ||||
| 		[ | ||||
| 			FMODEventEditorPanel.ToSharedRef() | ||||
| 		]; | ||||
| } | ||||
|  | ||||
| void FFMODEventEditor::HandleBanksReloaded() | ||||
| { | ||||
| 	CurrentPreviewEventInstance = nullptr; | ||||
|  | ||||
| 	CreateInternalWidgets(); | ||||
|  | ||||
| 	if (OwnerTab.IsValid()) | ||||
| 	{ | ||||
| 		OwnerTab->SetContent(FMODEventEditorPanel.ToSharedRef()); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void FFMODEventEditor::HandleBeginPIE(bool bSimulating) | ||||
| { | ||||
| 	CurrentPreviewEventInstance = nullptr; | ||||
| } | ||||
|  | ||||
| #undef LOCTEXT_NAMESPACE | ||||
| @@ -0,0 +1,80 @@ | ||||
| // Copyright (c), Firelight Technologies Pty, Ltd. 2012-2016. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "Toolkits/AssetEditorToolkit.h" | ||||
| #include "fmod_studio_common.h" | ||||
|  | ||||
| namespace FMOD | ||||
| { | ||||
| 	namespace Studio | ||||
| 	{ | ||||
| 		class EventDescription; | ||||
| 		class EventInstance; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| class FFMODEventEditor : public FAssetEditorToolkit | ||||
| { | ||||
| public: | ||||
| 	virtual void RegisterTabSpawners(const TSharedRef<class FTabManager>& TabManager) override; | ||||
| 	virtual void UnregisterTabSpawners(const TSharedRef<class FTabManager>& TabManager) override; | ||||
|  | ||||
| 	/** | ||||
| 	* Edits the specified event | ||||
| 	* | ||||
| 	* @param	Mode					Asset editing mode for this editor (standalone or world-centric) | ||||
| 	* @param	InitToolkitHost			When Mode is WorldCentric, this is the level editor instance to spawn this editor within | ||||
| 	* @param	Event					The event to edit | ||||
| 	*/ | ||||
| 	void InitFMODEventEditor(const EToolkitMode::Type Mode, const TSharedPtr< class IToolkitHost >& InitToolkitHost, class UFMODEvent* Event); | ||||
|  | ||||
| 	/** Constructor */ | ||||
| 	FFMODEventEditor(); | ||||
|  | ||||
| 	/** Destructor */ | ||||
| 	virtual ~FFMODEventEditor(); | ||||
|  | ||||
| 	UFMODEvent* GetEditedEvent() const; | ||||
| 	FMOD::Studio::EventDescription* GetEventDescription() const; | ||||
| 	void PlayEvent(); | ||||
| 	void PauseEvent(); | ||||
| 	void StopEvent(); | ||||
| 	void SetParameterValue(int32 ParameterIdx, float Value); | ||||
| 	TArray<float>& GetParameterValues(); | ||||
|  | ||||
| 	/** IToolkit interface */ | ||||
| 	virtual FName GetToolkitFName() const override; | ||||
| 	virtual FText GetBaseToolkitName() const override; | ||||
| 	virtual FString GetWorldCentricTabPrefix() const override; | ||||
| 	virtual FLinearColor GetWorldCentricTabColorScale() const override; | ||||
|  | ||||
| 	TArray<float> ParameterValues; | ||||
|  | ||||
| private: | ||||
|  | ||||
| 	FMOD::Studio::EventInstance* CurrentPreviewEventInstance; | ||||
|  | ||||
| 	void HandlePreBanksReloaded(); | ||||
| 	void HandleBanksReloaded(); | ||||
| 	void HandleBeginPIE(bool bSimulating); | ||||
|  | ||||
| 	/** Creates all internal widgets for the tabs to point at */ | ||||
| 	void CreateInternalWidgets(); | ||||
|  | ||||
| 	/**	Spawns the tab with the FMOD event inside */ | ||||
| 	TSharedRef<SDockTab> SpawnTab_EventEditor(const FSpawnTabArgs& Args); | ||||
|  | ||||
| 	TSharedPtr<class SFMODEventEditorPanel> FMODEventEditorPanel; | ||||
| 	TSharedPtr<SDockTab> OwnerTab; | ||||
|  | ||||
| 	/**	The tab id for the event editor tab */ | ||||
| 	static const FName EventEditorTabId; | ||||
|  | ||||
| 	/** FMOD event editor app identifier string */ | ||||
| 	static const FName FMODEventEditorAppIdentifier; | ||||
|  | ||||
| 	class UFMODEvent* EditedEvent; | ||||
|  | ||||
| 	FDelegateHandle BeginPIEDelegateHandle; | ||||
| }; | ||||
| @@ -0,0 +1,872 @@ | ||||
| // Copyright (c), Firelight Technologies Pty, Ltd. 2012-2016. | ||||
|  | ||||
| #include "FMODStudioEditorPrivatePCH.h" | ||||
|  | ||||
| #include "FMODStudioEditorModule.h" | ||||
| #include "FMODStudioModule.h" | ||||
| #include "FMODStudioStyle.h" | ||||
| #include "FMODAudioComponent.h" | ||||
| #include "FMODAssetBroker.h" | ||||
| #include "FMODSettings.h" | ||||
| #include "FMODUtils.h" | ||||
|  | ||||
| #include "FMODEventEditor.h" | ||||
| #include "FMODAudioComponentVisualizer.h" | ||||
| #include "FMODAmbientSoundDetails.h" | ||||
|  | ||||
| #include "SlateBasics.h" | ||||
| #include "AssetTypeActions_FMODEvent.h" | ||||
| #include "NotificationManager.h" | ||||
| #include "SNotificationList.h" | ||||
| #include "ISettingsModule.h" | ||||
| #include "ISettingsSection.h" | ||||
| #include "Editor.h" | ||||
| #include "SceneViewport.h" | ||||
| #include "LevelEditor.h" | ||||
| #include "SocketSubsystem.h" | ||||
| #include "Sockets.h" | ||||
| #include "IPAddress.h" | ||||
|  | ||||
| #include "fmod_studio.hpp" | ||||
|  | ||||
| #define LOCTEXT_NAMESPACE "FMODStudio" | ||||
|  | ||||
| DEFINE_LOG_CATEGORY(LogFMOD); | ||||
|  | ||||
| class FFMODStudioLink | ||||
| { | ||||
| public: | ||||
| 	FFMODStudioLink()  | ||||
| 	:	SocketSubsystem(nullptr), | ||||
| 		Socket(nullptr) | ||||
| 	{  | ||||
| 		SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM); | ||||
| 	} | ||||
|  | ||||
| 	~FFMODStudioLink() | ||||
| 	{ | ||||
| 		Disconnect(); | ||||
| 	} | ||||
|  | ||||
| 	bool Connect() | ||||
| 	{ | ||||
| 		if (!SocketSubsystem) return false; | ||||
|  | ||||
| 		Disconnect(); | ||||
| 		Socket = SocketSubsystem->CreateSocket(NAME_Stream, TEXT("FMOD Studio Connection"), false); | ||||
|  | ||||
| 		TSharedRef<FInternetAddr> Addr = SocketSubsystem->CreateInternetAddr(); | ||||
| 		bool Valid = false; | ||||
| 		Addr->SetIp(TEXT("127.0.0.1"), Valid); | ||||
| 		if (!Valid) return false; | ||||
|  | ||||
| 		Addr->SetPort(3663); | ||||
| 		return Socket->Connect(*Addr); | ||||
| 	} | ||||
|  | ||||
| 	void Disconnect() | ||||
| 	{ | ||||
| 		if (SocketSubsystem && Socket) | ||||
| 		{ | ||||
| 			SocketSubsystem->DestroySocket(Socket); | ||||
| 			Socket = nullptr; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	bool Execute(const TCHAR* Message, FString& OutMessage) | ||||
| 	{ | ||||
| 		OutMessage = TEXT(""); | ||||
| 		if (!Socket) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		UE_LOG(LogFMOD, Log, TEXT("Sent studio message: %s"), Message); | ||||
|  | ||||
| 		FTCHARToUTF8 MessageChars(Message); | ||||
| 		int32 BytesSent = 0; | ||||
| 		if (!Socket->Send((const uint8*)MessageChars.Get(), MessageChars.Length(), BytesSent)) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		while (1) | ||||
| 		{ | ||||
| 			FString BackMessage; | ||||
| 			if (!ReadMessage(BackMessage)) | ||||
| 			{ | ||||
| 				return false; | ||||
| 			} | ||||
| 			UE_LOG(LogFMOD, Log, TEXT("Received studio message: %s"), *BackMessage); | ||||
| 			if (BackMessage.StartsWith(TEXT("out(): "))) | ||||
| 			{ | ||||
| 				OutMessage = BackMessage.Mid(7).TrimTrailing(); | ||||
| 				break; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				// Keep going  | ||||
| 			} | ||||
| 		} | ||||
| 		return true; | ||||
| 	} | ||||
| private: | ||||
| 	bool ReadMessage(FString& OutMessage) | ||||
| 	{ | ||||
| 		while (1) | ||||
| 		{ | ||||
| 			for (int32 i=0; i<ReceivedMessage.Num(); ++i) | ||||
| 			{ | ||||
| 				if (ReceivedMessage[i] == '\0') | ||||
| 				{ | ||||
| 					OutMessage = FString(UTF8_TO_TCHAR(ReceivedMessage.GetData())); | ||||
| 					ReceivedMessage.RemoveAt(0, i + 1); | ||||
| 					return true; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			int32 ExtraSpace = 64; | ||||
| 			int32 CurrentSize = ReceivedMessage.Num(); | ||||
| 			ReceivedMessage.SetNum(CurrentSize + ExtraSpace); | ||||
| 			int32 ActualRead = 0; | ||||
| 			if (!Socket->Wait(ESocketWaitConditions::WaitForRead, FTimespan::FromSeconds(10))) | ||||
| 			{ | ||||
| 				return false; | ||||
| 			} | ||||
| 			else if (!Socket->Recv((uint8*)ReceivedMessage.GetData() + CurrentSize, ExtraSpace, ActualRead)) | ||||
| 			{ | ||||
| 				return false; | ||||
| 			} | ||||
| 			ReceivedMessage.SetNum(CurrentSize + ActualRead); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	ISocketSubsystem* SocketSubsystem; | ||||
| 	FSocket* Socket; | ||||
| 	TArray<char> ReceivedMessage; | ||||
| }; | ||||
|  | ||||
| class FFMODStudioEditorModule : public IFMODStudioEditorModule | ||||
| { | ||||
| public: | ||||
| 	/** IModuleInterface implementation */ | ||||
| 	FFMODStudioEditorModule() : | ||||
| 		bSimulating(false), | ||||
| 		bIsInPIE(false), | ||||
| 		bRegisteredComponentVisualizers(false) | ||||
| 	{ | ||||
| 	} | ||||
|  | ||||
| 	virtual void StartupModule() override; | ||||
| 	virtual void PostLoadCallback() override; | ||||
| 	virtual void ShutdownModule() override; | ||||
|  | ||||
| 	bool HandleSettingsSaved(); | ||||
|  | ||||
| 	/** Called after all banks were reloaded by the studio module */ | ||||
| 	void HandleBanksReloaded(); | ||||
|  | ||||
| 	/** Show notification */ | ||||
| 	void ShowNotification(const FText& Text, SNotificationItem::ECompletionState State); | ||||
|  | ||||
| 	void BeginPIE(bool simulating); | ||||
| 	void EndPIE(bool simulating); | ||||
| 	void PausePIE(bool simulating); | ||||
| 	void ResumePIE(bool simulating); | ||||
|  | ||||
| 	void ViewportDraw(UCanvas* Canvas, APlayerController*); | ||||
|  | ||||
| 	bool Tick( float DeltaTime ); | ||||
|  | ||||
| 	/** Add extensions to menu */ | ||||
| 	void AddHelpMenuExtension(FMenuBuilder& MenuBuilder); | ||||
| 	void AddFileMenuExtension(FMenuBuilder& MenuBuilder); | ||||
|  | ||||
| 	/** Show FMOD version */ | ||||
| 	void ShowVersion(); | ||||
| 	/** Open CHM */ | ||||
| 	void OpenCHM(); | ||||
| 	/** Open web page to online docs */ | ||||
| 	void OpenOnlineDocs(); | ||||
| 	/** Open Video tutorials page */ | ||||
| 	void OpenVideoTutorials(); | ||||
| 	/** Set Studio build path */ | ||||
| 	void ValidateFMOD(); | ||||
|  | ||||
| 	/** Reload banks */ | ||||
| 	void ReloadBanks(); | ||||
|  | ||||
| 	TArray<FName> RegisteredComponentClassNames; | ||||
| 	void RegisterComponentVisualizer(FName ComponentClassName, TSharedPtr<FComponentVisualizer> Visualizer); | ||||
|  | ||||
| 	/** The delegate to be invoked when this profiler manager ticks. */ | ||||
| 	FTickerDelegate OnTick; | ||||
|  | ||||
| 	/** Handle for registered delegates. */ | ||||
| 	FDelegateHandle TickDelegateHandle; | ||||
| 	FDelegateHandle BeginPIEDelegateHandle; | ||||
| 	FDelegateHandle EndPIEDelegateHandle; | ||||
| 	FDelegateHandle PausePIEDelegateHandle; | ||||
| 	FDelegateHandle ResumePIEDelegateHandle; | ||||
| 	FDelegateHandle HandleBanksReloadedDelegateHandle; | ||||
|  | ||||
| 	/** Hook for drawing viewport */ | ||||
| 	FDebugDrawDelegate ViewportDrawingDelegate; | ||||
| 	FDelegateHandle ViewportDrawingDelegateHandle; | ||||
|  | ||||
| 	TSharedPtr<IComponentAssetBroker> AssetBroker; | ||||
|  | ||||
| 	/** The extender to pass to the level editor to extend it's window menu */ | ||||
| 	TSharedPtr<FExtender> MainMenuExtender; | ||||
|  | ||||
| 	/** Asset type actions for events (edit, play, stop) */ | ||||
| 	TSharedPtr<FAssetTypeActions_FMODEvent> FMODEventAssetTypeActions; | ||||
|  | ||||
| 	bool bSimulating; | ||||
| 	bool bIsInPIE; | ||||
| 	bool bRegisteredComponentVisualizers; | ||||
| }; | ||||
|  | ||||
| IMPLEMENT_MODULE( FFMODStudioEditorModule, FMODStudioEditor ) | ||||
|  | ||||
| void FFMODStudioEditorModule::StartupModule() | ||||
| { | ||||
| 	UE_LOG(LogFMOD, Log, TEXT("FFMODStudioEditorModule startup")); | ||||
|  | ||||
| 	AssetBroker = MakeShareable(new FFMODAssetBroker); | ||||
| 	FComponentAssetBrokerage::RegisterBroker(AssetBroker, UFMODAudioComponent::StaticClass(), true, true); | ||||
|  | ||||
| 	if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings")) | ||||
| 	{ | ||||
| 		ISettingsSectionPtr SettingsSection = SettingsModule->RegisterSettings("Project", "Plugins", "FMODStudio", | ||||
| 			LOCTEXT("FMODStudioSettingsName", "FMOD Studio"), | ||||
| 			LOCTEXT("FMODStudioDescription", "Configure the FMOD Studio plugin"), | ||||
| 			GetMutableDefault<UFMODSettings>() | ||||
| 		); | ||||
|  | ||||
| 		if (SettingsSection.IsValid()) | ||||
| 		{ | ||||
| 			SettingsSection->OnModified().BindRaw(this, &FFMODStudioEditorModule::HandleSettingsSaved); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Register the details customizations | ||||
| 	{ | ||||
| 		FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor"); | ||||
| 		PropertyModule.RegisterCustomClassLayout("FMODAmbientSound", FOnGetDetailCustomizationInstance::CreateStatic(&FFMODAmbientSoundDetails::MakeInstance)); | ||||
| 		PropertyModule.NotifyCustomizationModuleChanged(); | ||||
| 	} | ||||
|  | ||||
| 	// Need to load the editor module since it gets created after us, and we can't re-order ourselves otherwise our asset registration stops working! | ||||
| 	// It only works if we are running the editor, not a commandlet | ||||
| 	if (!IsRunningCommandlet()) | ||||
| 	{ | ||||
| 		MainMenuExtender = MakeShareable(new FExtender); | ||||
| 		MainMenuExtender->AddMenuExtension("HelpBrowse", EExtensionHook::After, NULL, FMenuExtensionDelegate::CreateRaw(this, &FFMODStudioEditorModule::AddHelpMenuExtension)); | ||||
| 		MainMenuExtender->AddMenuExtension("FileLoadAndSave", EExtensionHook::After, NULL, FMenuExtensionDelegate::CreateRaw(this, &FFMODStudioEditorModule::AddFileMenuExtension)); | ||||
|  | ||||
| 		FLevelEditorModule* LevelEditor = FModuleManager::LoadModulePtr<FLevelEditorModule>(TEXT("LevelEditor")); | ||||
| 		if (LevelEditor) | ||||
| 		{ | ||||
| 			LevelEditor->GetMenuExtensibilityManager()->AddExtender(MainMenuExtender); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Register AssetTypeActions | ||||
| 	IAssetTools& AssetTools = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools").Get(); | ||||
|  | ||||
| 	FMODEventAssetTypeActions = MakeShareable(new FAssetTypeActions_FMODEvent); | ||||
| 	AssetTools.RegisterAssetTypeActions(FMODEventAssetTypeActions.ToSharedRef()); | ||||
|  | ||||
| 	// Register slate style overrides | ||||
| 	FFMODStudioStyle::Initialize(); | ||||
|  | ||||
| 	BeginPIEDelegateHandle = FEditorDelegates::BeginPIE.AddRaw(this, &FFMODStudioEditorModule::BeginPIE); | ||||
| 	EndPIEDelegateHandle = FEditorDelegates::EndPIE.AddRaw(this, &FFMODStudioEditorModule::EndPIE); | ||||
| 	PausePIEDelegateHandle = FEditorDelegates::PausePIE.AddRaw(this, &FFMODStudioEditorModule::PausePIE); | ||||
| 	ResumePIEDelegateHandle = FEditorDelegates::ResumePIE.AddRaw(this, &FFMODStudioEditorModule::ResumePIE); | ||||
|  | ||||
| 	ViewportDrawingDelegate = FDebugDrawDelegate::CreateRaw(this, &FFMODStudioEditorModule::ViewportDraw); | ||||
| 	ViewportDrawingDelegateHandle = UDebugDrawService::Register(TEXT("Editor"), ViewportDrawingDelegate); | ||||
|  | ||||
| 	OnTick = FTickerDelegate::CreateRaw( this, &FFMODStudioEditorModule::Tick ); | ||||
| 	TickDelegateHandle = FTicker::GetCoreTicker().AddTicker( OnTick ); | ||||
|  | ||||
| 	// This module is loaded after FMODStudioModule | ||||
| 	HandleBanksReloadedDelegateHandle = IFMODStudioModule::Get().BanksReloadedEvent().AddRaw(this, &FFMODStudioEditorModule::HandleBanksReloaded); | ||||
|  | ||||
| } | ||||
|  | ||||
| void FFMODStudioEditorModule::AddHelpMenuExtension(FMenuBuilder& MenuBuilder) | ||||
| { | ||||
| 	MenuBuilder.BeginSection("FMODHelp", LOCTEXT("FMODHelpLabel", "FMOD Help")); | ||||
| 	MenuBuilder.AddMenuEntry( | ||||
| 		LOCTEXT("FMODVersionMenuEntryTitle", "About FMOD Studio"), | ||||
| 		LOCTEXT("FMODVersionMenuEntryToolTip", "Shows the informationa about FMOD Studio."), | ||||
| 		FSlateIcon(), | ||||
| 		FUIAction(FExecuteAction::CreateRaw(this, &FFMODStudioEditorModule::ShowVersion))); | ||||
|  | ||||
| #if PLATFORM_WINDOWS | ||||
| 	MenuBuilder.AddMenuEntry( | ||||
| 		LOCTEXT("FMODHelpCHMTitle", "FMOD Documentation..."), | ||||
| 		LOCTEXT("FMODHelpCHMToolTip", "Opens the local FMOD documentation."), | ||||
| 		FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.BrowseAPIReference"), | ||||
| 		FUIAction(FExecuteAction::CreateRaw(this, &FFMODStudioEditorModule::OpenCHM))); | ||||
| #endif | ||||
|  | ||||
| 	MenuBuilder.AddMenuEntry( | ||||
| 		LOCTEXT("FMODHelpOnlineTitle", "FMOD Online Documentation..."), | ||||
| 		LOCTEXT("FMODHelpOnlineToolTip", "Go to the online FMOD documentation."), | ||||
| 		FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.BrowseDocumentation"), | ||||
| 		FUIAction(FExecuteAction::CreateRaw(this, &FFMODStudioEditorModule::OpenOnlineDocs))); | ||||
|  | ||||
| 	MenuBuilder.AddMenuEntry( | ||||
| 		LOCTEXT("FMODHelpVideosTitle", "FMOD Tutorial Videos..."), | ||||
| 		LOCTEXT("FMODHelpVideosToolTip", "Go to the online FMOD tutorial videos."), | ||||
| 		FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tutorials"), | ||||
| 		FUIAction(FExecuteAction::CreateRaw(this, &FFMODStudioEditorModule::OpenVideoTutorials))); | ||||
|  | ||||
| 	MenuBuilder.AddMenuEntry( | ||||
| 		LOCTEXT("FMODSetStudioBuildTitle", "Validate FMOD"), | ||||
| 		LOCTEXT("FMODSetStudioBuildToolTip", "Verifies that FMOD and FMOD Studio are working as expected."), | ||||
| 		FSlateIcon(), | ||||
| 		FUIAction(FExecuteAction::CreateRaw(this, &FFMODStudioEditorModule::ValidateFMOD))); | ||||
|  | ||||
| 	MenuBuilder.EndSection(); | ||||
| } | ||||
|  | ||||
| void FFMODStudioEditorModule::AddFileMenuExtension(FMenuBuilder& MenuBuilder) | ||||
| { | ||||
| 	MenuBuilder.BeginSection("FMODFile", LOCTEXT("FMODFileLabel", "FMOD")); | ||||
| 	MenuBuilder.AddMenuEntry( | ||||
| 		LOCTEXT("FMODFileMenuEntryTitle", "Reload Banks"), | ||||
| 		LOCTEXT("FMODFileMenuEntryToolTip", "Force a manual reload of all FMOD Studio banks."), | ||||
| 		FSlateIcon(), | ||||
| 		FUIAction(FExecuteAction::CreateRaw(this, &FFMODStudioEditorModule::ReloadBanks))); | ||||
| 	MenuBuilder.EndSection(); | ||||
| } | ||||
|  | ||||
| unsigned int GetDLLVersion() | ||||
| { | ||||
| 	// Just grab it from the audition context which is always valid | ||||
| 	unsigned int DLLVersion = 0; | ||||
| 	FMOD::Studio::System* StudioSystem = IFMODStudioModule::Get().GetStudioSystem(EFMODSystemContext::Auditioning); | ||||
| 	if (StudioSystem) | ||||
| 	{ | ||||
| 		FMOD::System* LowLevelSystem = nullptr; | ||||
| 		if (StudioSystem->getLowLevelSystem(&LowLevelSystem) == FMOD_OK) | ||||
| 		{ | ||||
| 			LowLevelSystem->getVersion(&DLLVersion); | ||||
| 		} | ||||
| 	} | ||||
| 	return DLLVersion; | ||||
| } | ||||
| unsigned int StripPatch(unsigned int FullVersion) | ||||
| { | ||||
| 	return FullVersion & ~0xFF; | ||||
| } | ||||
| FString VersionToString(unsigned int FullVersion) | ||||
| { | ||||
| 	return FString::Printf(TEXT("%x.%02x.%02x"), (FullVersion>>16), (FullVersion>>8) & 0xFF, FullVersion & 0xFF); | ||||
| } | ||||
|  | ||||
| void FFMODStudioEditorModule::ShowVersion() | ||||
| { | ||||
| 	unsigned int HeaderVersion = FMOD_VERSION; | ||||
| 	unsigned int DLLVersion = GetDLLVersion(); | ||||
|  | ||||
| 	FText VersionMessage = FText::Format( | ||||
| 		LOCTEXT("FMODStudio_About", "FMOD Studio\n\nBuilt Version: {0}\nDLL Version: {1}\n\nCopyright Firelight Technologies Pty Ltd"), | ||||
| 			FText::FromString(VersionToString(HeaderVersion)), | ||||
| 			FText::FromString(VersionToString(DLLVersion))); | ||||
| 	FMessageDialog::Open(EAppMsgType::Ok, VersionMessage); | ||||
| } | ||||
|  | ||||
| void FFMODStudioEditorModule::OpenCHM() | ||||
| { | ||||
| 	FString APIPath = FPaths::Combine(*FPaths::EngineDir(), TEXT("Plugins/FMODStudio/Docs/FMOD UE4 Integration.chm")); | ||||
| 	if( IFileManager::Get().FileSize( *APIPath ) != INDEX_NONE ) | ||||
| 	{ | ||||
| 		FString AbsoluteAPIPath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*APIPath); | ||||
| 		FPlatformProcess::LaunchFileInDefaultExternalApplication(*AbsoluteAPIPath); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("Documentation", "CannotFindFMODIntegration", "Cannot open FMOD Studio Integration CHM reference; help file not found.")); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void FFMODStudioEditorModule::OpenOnlineDocs() | ||||
| { | ||||
| 	FPlatformProcess::LaunchFileInDefaultExternalApplication(TEXT("http://www.fmod.org/documentation")); | ||||
| } | ||||
|  | ||||
| void FFMODStudioEditorModule::OpenVideoTutorials() | ||||
| { | ||||
| 	FPlatformProcess::LaunchFileInDefaultExternalApplication(TEXT("http://www.youtube.com/user/FMODTV")); | ||||
| } | ||||
|  | ||||
| void FFMODStudioEditorModule::ValidateFMOD() | ||||
| { | ||||
| 	int ProblemsFound = 0; | ||||
|  | ||||
| 	FFMODStudioLink StudioLink; | ||||
| 	bool Connected = StudioLink.Connect(); | ||||
| 	if (!Connected) | ||||
| 	{ | ||||
| 		if (EAppReturnType::No == FMessageDialog::Open(EAppMsgType::YesNo, LOCTEXT("SetStudioBuildStudioNotRunning", "FMODStudio does not appear to be running.  Only some validation will occur.  Do you want to continue anyway?"))) | ||||
| 		{ | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| 	unsigned int HeaderVersion = FMOD_VERSION; | ||||
| 	unsigned int DLLVersion = GetDLLVersion(); | ||||
| 	unsigned int StudioVersion = 0; | ||||
| 	if (Connected) | ||||
| 	{ | ||||
| 		FString StudioVersionString; | ||||
| 		if (StudioLink.Execute(TEXT("studio.version"), StudioVersionString)) | ||||
| 		{ | ||||
| 			int Super = 0; | ||||
| 			int Major = 0; | ||||
| 			int Minor = 0; | ||||
| 			sscanf(TCHAR_TO_UTF8(*StudioVersionString), "Version %x.%x.%x", &Super, &Major, &Minor); | ||||
| 			StudioVersion = (Super<<16) | (Major<<8) | Minor; | ||||
| 		} | ||||
| 	} | ||||
| 	if (StripPatch(HeaderVersion) != StripPatch(DLLVersion)) | ||||
| 	{ | ||||
| 		FText VersionMessage = FText::Format( | ||||
| 			LOCTEXT("SetStudioBuildStudio_Status", "The FMOD DLL version is different to the version the integration was built against.  This may cause problems running the game.\nBuilt Version: {0}\nDLL Version: {1}\n"), | ||||
| 				FText::FromString(VersionToString(HeaderVersion)), | ||||
| 				FText::FromString(VersionToString(DLLVersion))); | ||||
| 		FMessageDialog::Open(EAppMsgType::Ok, VersionMessage); | ||||
| 		ProblemsFound++; | ||||
| 	} | ||||
| 	if (StudioVersion > DLLVersion) | ||||
| 	{ | ||||
| 		FText VersionMessage = FText::Format( | ||||
| 			LOCTEXT("SetStudioBuildStudio_Version", "The Studio tool is newer than the version the integration was built against.  The integration may not be able to load the banks that the tool builds.\n\nBuilt Version: {0}\nDLL Version: {1}\nStudio Version: {2}\n\nWe recommend using the Studio tool that matches the integration.\n\nDo you want to continue with the validation?"), | ||||
| 				FText::FromString(VersionToString(HeaderVersion)), | ||||
| 				FText::FromString(VersionToString(DLLVersion)), | ||||
| 				FText::FromString(VersionToString(StudioVersion))); | ||||
| 		if (EAppReturnType::No == FMessageDialog::Open(EAppMsgType::YesNo, VersionMessage)) | ||||
| 		{ | ||||
| 			return; | ||||
| 		} | ||||
| 		ProblemsFound++; | ||||
| 	} | ||||
|  | ||||
| 	const UFMODSettings& Settings = *GetDefault<UFMODSettings>(); | ||||
| 	FString FullBankPath = Settings.BankOutputDirectory.Path; | ||||
| 	if (FPaths::IsRelative(FullBankPath)) | ||||
| 	{ | ||||
| 		FullBankPath = FPaths::GameContentDir() / FullBankPath; | ||||
| 	} | ||||
| 	FString PlatformBankPath = Settings.GetFullBankPath(); | ||||
| 	FullBankPath = FPaths::ConvertRelativePathToFull(FullBankPath); | ||||
| 	PlatformBankPath = FPaths::ConvertRelativePathToFull(PlatformBankPath); | ||||
|  | ||||
| 	if (Connected) | ||||
| 	{ | ||||
| 		// File path was added in FMOD Studio 1.07.00 | ||||
| 		FString StudioProjectPath; | ||||
| 		FString StudioProjectDir; | ||||
| 		if (StudioVersion >= 0x00010700) | ||||
| 		{ | ||||
| 			StudioLink.Execute(TEXT("studio.project.filePath"), StudioProjectPath); | ||||
| 			if (StudioProjectPath.IsEmpty() || StudioProjectPath == TEXT("undefined")) | ||||
| 			{ | ||||
| 				FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("SetStudioBuildStudio_NewProject", "FMOD Studio has an empty project.  Please go to FMOD Studio, and press Save to create your new project.")); | ||||
| 				// Just try to save anyway | ||||
| 				FString Result; | ||||
| 				StudioLink.Execute(TEXT("studio.project.save()"), Result); | ||||
| 			} | ||||
| 			StudioLink.Execute(TEXT("studio.project.filePath"), StudioProjectPath); | ||||
| 			if (StudioProjectPath != TEXT("undefined")) | ||||
| 			{ | ||||
| 				StudioProjectDir = FPaths::GetPath(StudioProjectPath); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		FString StudioPathString; | ||||
| 		StudioLink.Execute(TEXT("studio.project.workspace.builtBanksOutputDirectory"), StudioPathString); | ||||
| 		if (StudioPathString == TEXT("undefined")) | ||||
| 		{ | ||||
| 			StudioPathString = TEXT(""); | ||||
| 		} | ||||
|  | ||||
| 		FString CanonicalBankPath = FullBankPath; | ||||
| 		FPaths::CollapseRelativeDirectories(CanonicalBankPath); | ||||
| 		FPaths::NormalizeDirectoryName(CanonicalBankPath); | ||||
| 		FPaths::RemoveDuplicateSlashes(CanonicalBankPath); | ||||
| 		FPaths::NormalizeDirectoryName(CanonicalBankPath); | ||||
|  | ||||
| 		FString CanonicalStudioPath = StudioPathString; | ||||
| 		if (FPaths::IsRelative(CanonicalStudioPath) && !StudioProjectDir.IsEmpty() && !StudioPathString.IsEmpty()) | ||||
| 		{ | ||||
| 			CanonicalStudioPath = FPaths::Combine(*StudioProjectDir, *CanonicalStudioPath); | ||||
| 		} | ||||
| 		FPaths::CollapseRelativeDirectories(CanonicalStudioPath); | ||||
| 		FPaths::NormalizeDirectoryName(CanonicalStudioPath); | ||||
| 		FPaths::RemoveDuplicateSlashes(CanonicalStudioPath); | ||||
| 		FPaths::NormalizeDirectoryName(CanonicalStudioPath); | ||||
| 		if (!FPaths::IsSamePath(CanonicalBankPath, CanonicalStudioPath)) | ||||
| 		{ | ||||
| 			FString BankPathToSet = FullBankPath; | ||||
| 			// Extra logic - if we have put the studio project inside the game project, then make it relative | ||||
| 			if (!StudioProjectDir.IsEmpty()) | ||||
| 			{ | ||||
| 				FString GameBaseDir = FPaths::ConvertRelativePathToFull(FPaths::GameDir()); | ||||
| 				FString BankPathFromGameProject = FullBankPath; | ||||
| 				FString StudioProjectFromGameProject = StudioProjectDir; | ||||
| 				if (FPaths::MakePathRelativeTo(BankPathFromGameProject, *GameBaseDir) && !BankPathFromGameProject.Contains(TEXT("..")) && | ||||
| 					FPaths::MakePathRelativeTo(StudioProjectFromGameProject, *GameBaseDir) && !StudioProjectFromGameProject.Contains(TEXT(".."))) | ||||
| 				{ | ||||
| 					FPaths::MakePathRelativeTo(BankPathToSet, *(StudioProjectDir + TEXT("/"))); | ||||
| 				} | ||||
| 			} | ||||
| 			ProblemsFound++; | ||||
|  | ||||
| 			FText AskMessage = FText::Format( | ||||
| 					LOCTEXT("SetStudioBuildStudio_Ask", "FMOD Studio build path should be set up.\n\nCurrent Studio build path: {0}\nNew build path: {1}\n\nDo you want to fix up the project now?"), | ||||
| 					FText::FromString(StudioPathString), | ||||
| 					FText::FromString(BankPathToSet)); | ||||
| 			if (EAppReturnType::Yes == FMessageDialog::Open(EAppMsgType::YesNo, AskMessage)) | ||||
| 			{ | ||||
| 				FString Result; | ||||
| 				StudioLink.Execute(*FString::Printf(TEXT("studio.project.workspace.builtBanksOutputDirectory = \"%s\";"), *BankPathToSet), Result); | ||||
| 				StudioLink.Execute(TEXT("studio.project.workspace.builtBanksOutputDirectory"), Result); | ||||
| 				if (Result != BankPathToSet) | ||||
| 				{ | ||||
| 					FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("SetStudioBuildStudio_Save", "Failed to set bank directory.  Please go to FMOD Studio, and set the bank path in FMOD Studio project settings.")); | ||||
| 				} | ||||
| 				FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("SetStudioBuildStudio_Save", "Please go to FMOD Studio, save your project and build banks.")); | ||||
| 				// Just try to do it again anyway | ||||
| 				StudioLink.Execute(TEXT("studio.project.save()"), Result); | ||||
| 				StudioLink.Execute(TEXT("studio.project.build()"), Result); | ||||
| 				// Pretend settings have changed which will force a reload | ||||
| 				IFMODStudioModule::Get().RefreshSettings(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	bool AnyBankFiles = false; | ||||
|  | ||||
| 	if (!FPaths::DirectoryExists(FullBankPath) || !FPaths::DirectoryExists(PlatformBankPath)) | ||||
| 	{ | ||||
| 		FText DirMessage = FText::Format( | ||||
| 				LOCTEXT("SetStudioBuildStudio_Dir", "The FMOD Content directory does not exist.  Please make sure FMOD Studio is exporting banks into the correct location.\n\nBanks should be exported to: {0}\nBanks files should exist in: {1}\n"), | ||||
| 				FText::FromString(FullBankPath), | ||||
| 				FText::FromString(PlatformBankPath)); | ||||
| 		FMessageDialog::Open(EAppMsgType::Ok, DirMessage); | ||||
| 		ProblemsFound++; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		TArray<FString> BankFiles; | ||||
| 		Settings.GetAllBankPaths(BankFiles, true); | ||||
| 		if (BankFiles.Num() != 0) | ||||
| 		{ | ||||
| 			AnyBankFiles = true; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			FText EmptyBankDirMessage = FText::Format( | ||||
| 					LOCTEXT("SetStudioBuildStudio_EmptyBankDir", "The FMOD Content directory does not have any bank files in them.  Please make sure FMOD Studio is exporting banks into the correct location.\n\nBanks should be exported to: {0}\nBanks files should exist in: {1}\n"), | ||||
| 					FText::FromString(FullBankPath), | ||||
| 					FText::FromString(PlatformBankPath)); | ||||
| 			FMessageDialog::Open(EAppMsgType::Ok, EmptyBankDirMessage); | ||||
| 			ProblemsFound++; | ||||
| 		} | ||||
| 	} | ||||
| 	if (AnyBankFiles) | ||||
| 	{ | ||||
| 		FMOD::Studio::System* StudioSystem = IFMODStudioModule::Get().GetStudioSystem(EFMODSystemContext::Auditioning); | ||||
| 		int BankCount = 0; | ||||
| 		StudioSystem->getBankCount(&BankCount); | ||||
| 		TArray<FString> FailedBanks = IFMODStudioModule::Get().GetFailedBankLoads(EFMODSystemContext::Auditioning); | ||||
| 		if (BankCount == 0 || FailedBanks.Num() != 0) | ||||
| 		{ | ||||
| 			FString CombinedBanks; | ||||
| 			for (auto Bank : FailedBanks) | ||||
| 			{ | ||||
| 				CombinedBanks += Bank; | ||||
| 				CombinedBanks += TEXT("\n"); | ||||
| 			} | ||||
| 			FText BankLoadMessage; | ||||
| 			if (BankCount == 0 && FailedBanks.Num() == 0) | ||||
| 			{ | ||||
| 				BankLoadMessage = LOCTEXT("SetStudioBuildStudio_BankLoad", "Failed to load banks\n"); | ||||
| 			} | ||||
| 			else if (BankCount == 0) | ||||
| 			{ | ||||
| 				BankLoadMessage = FText::Format( | ||||
| 						LOCTEXT("SetStudioBuildStudio_BankLoad", "Failed to load banks:\n{0}\n"), | ||||
| 						FText::FromString(CombinedBanks)); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				BankLoadMessage = FText::Format( | ||||
| 						LOCTEXT("SetStudioBuildStudio_BankLoad", "Some banks failed to load:\n{0}\n"), | ||||
| 						FText::FromString(CombinedBanks)); | ||||
| 			} | ||||
| 			FMessageDialog::Open(EAppMsgType::Ok, BankLoadMessage); | ||||
| 			ProblemsFound++; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			int TotalEventCount = 0; | ||||
| 			TArray<FMOD::Studio::Bank*> Banks; | ||||
| 			Banks.SetNum(BankCount); | ||||
| 			StudioSystem->getBankList(Banks.GetData(), BankCount, &BankCount); | ||||
| 			for (FMOD::Studio::Bank* Bank : Banks) | ||||
| 			{ | ||||
| 				int EventCount = 0; | ||||
| 				Bank->getEventCount(&EventCount); | ||||
| 				TotalEventCount += EventCount; | ||||
| 			} | ||||
| 			if (TotalEventCount == 0) | ||||
| 			{ | ||||
| 				FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("SetStudioBuildStudio_NoEvents", "Banks have been loaded but they didn't have any events in them.  Please make sure you have added some events to banks.")); | ||||
| 				ProblemsFound++; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	TArray<FString> RequiredPlugins = IFMODStudioModule::Get().GetRequiredPlugins(); | ||||
| 	if (RequiredPlugins.Num() != 0 && Settings.PluginFiles.Num() == 0) | ||||
| 	{ | ||||
| 		FString CombinedPlugins; | ||||
| 		for (auto Name : RequiredPlugins) | ||||
| 		{ | ||||
| 			CombinedPlugins += Name; | ||||
| 			CombinedPlugins += TEXT("\n"); | ||||
| 		} | ||||
| 		FText PluginMessage = FText::Format( | ||||
| 				LOCTEXT("SetStudioBuildStudio_Plugins", "The banks require the following plugins, but no plugin filenames are listed in the settings:\n{0}\n"), | ||||
| 				FText::FromString(CombinedPlugins)); | ||||
| 		FMessageDialog::Open(EAppMsgType::Ok, PluginMessage); | ||||
| 		ProblemsFound++; | ||||
| 	} | ||||
|  | ||||
| 	if (ProblemsFound) | ||||
| 	{ | ||||
| 		FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("SetStudioBuildStudio_FinishedBad", "Finished validation.  Problems were detected.\n")); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("SetStudioBuildStudio_FinishedGood", "Finished validation.  No problems detected.\n")); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void FFMODStudioEditorModule::ReloadBanks() | ||||
| { | ||||
| 	// Pretend settings have changed which will force a reload | ||||
| 	IFMODStudioModule::Get().RefreshSettings(); | ||||
| } | ||||
|  | ||||
| bool FFMODStudioEditorModule::Tick( float DeltaTime ) | ||||
| { | ||||
| 	if (!bRegisteredComponentVisualizers && GUnrealEd != nullptr) | ||||
| 	{ | ||||
| 		// Register component visualizers (GUnrealED is required for this, but not initialized before this module loads, so we have to wait until GUnrealEd is available) | ||||
| 		RegisterComponentVisualizer(UFMODAudioComponent::StaticClass()->GetFName(), MakeShareable(new FFMODAudioComponentVisualizer)); | ||||
|  | ||||
| 		bRegisteredComponentVisualizers = true; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void FFMODStudioEditorModule::BeginPIE(bool simulating) | ||||
| { | ||||
| 	UE_LOG(LogFMOD, Verbose, TEXT("FFMODStudioEditorModule BeginPIE: %d"), simulating); | ||||
| 	bSimulating = simulating; | ||||
| 	bIsInPIE = true; | ||||
| 	IFMODStudioModule::Get().SetInPIE(true, simulating); | ||||
| } | ||||
|  | ||||
| void FFMODStudioEditorModule::EndPIE(bool simulating) | ||||
| { | ||||
| 	UE_LOG(LogFMOD, Verbose, TEXT("FFMODStudioEditorModule EndPIE: %d"), simulating); | ||||
| 	bSimulating = false; | ||||
| 	bIsInPIE = false; | ||||
| 	IFMODStudioModule::Get().SetInPIE(false, simulating); | ||||
| } | ||||
|  | ||||
| void FFMODStudioEditorModule::PausePIE(bool simulating) | ||||
| { | ||||
| 	UE_LOG(LogFMOD, Verbose, TEXT("FFMODStudioEditorModule PausePIE%d")); | ||||
| 	IFMODStudioModule::Get().SetSystemPaused(true); | ||||
| } | ||||
|  | ||||
| void FFMODStudioEditorModule::ResumePIE(bool simulating) | ||||
| { | ||||
| 	UE_LOG(LogFMOD, Verbose, TEXT("FFMODStudioEditorModule ResumePIE")); | ||||
| 	IFMODStudioModule::Get().SetSystemPaused(false); | ||||
| } | ||||
|  | ||||
| void FFMODStudioEditorModule::PostLoadCallback() | ||||
| { | ||||
| 	UE_LOG(LogFMOD, Verbose, TEXT("FFMODStudioEditorModule PostLoadCallback")); | ||||
| } | ||||
|  | ||||
| void FFMODStudioEditorModule::ViewportDraw(UCanvas* Canvas, APlayerController*) | ||||
| { | ||||
| 	// Only want to update based on viewport in simulate mode. | ||||
| 	// In PIE/game, we update from the player controller instead. | ||||
| 	if (!bSimulating) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	const FSceneView* View = Canvas->SceneView; | ||||
| 	 | ||||
| 	if (View->Drawer == GCurrentLevelEditingViewportClient) | ||||
| 	{ | ||||
| 		UWorld* World = GCurrentLevelEditingViewportClient->GetWorld(); | ||||
| 		const FVector& ViewLocation = GCurrentLevelEditingViewportClient->GetViewLocation(); | ||||
|  | ||||
| 		FMatrix CameraToWorld = View->ViewMatrices.ViewMatrix.InverseFast(); | ||||
| 		FVector ProjUp = CameraToWorld.TransformVector(FVector(0, 1000, 0)); | ||||
| 		FVector ProjRight = CameraToWorld.TransformVector(FVector(1000, 0, 0)); | ||||
|  | ||||
| 		FTransform ListenerTransform(FRotationMatrix::MakeFromZY(ProjUp, ProjRight)); | ||||
| 		ListenerTransform.SetTranslation(ViewLocation); | ||||
| 		ListenerTransform.NormalizeRotation(); | ||||
|  | ||||
| 		IFMODStudioModule::Get().SetListenerPosition(0, World, ListenerTransform, 0.0f); | ||||
| 		IFMODStudioModule::Get().FinishSetListenerPosition(1, 0.0f); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void FFMODStudioEditorModule::ShutdownModule() | ||||
| { | ||||
| 	UE_LOG(LogFMOD, Verbose, TEXT("FFMODStudioEditorModule shutdown")); | ||||
|  | ||||
| 	if (UObjectInitialized()) | ||||
| 	{ | ||||
| 		// Unregister tick function. | ||||
| 		FTicker::GetCoreTicker().RemoveTicker(TickDelegateHandle); | ||||
|  | ||||
| 		FEditorDelegates::BeginPIE.Remove(BeginPIEDelegateHandle); | ||||
| 		FEditorDelegates::EndPIE.Remove(EndPIEDelegateHandle); | ||||
| 		FEditorDelegates::PausePIE.Remove(PausePIEDelegateHandle); | ||||
| 		FEditorDelegates::ResumePIE.Remove(ResumePIEDelegateHandle); | ||||
|  | ||||
| 		if (ViewportDrawingDelegate.IsBound()) | ||||
| 		{ | ||||
| 			UDebugDrawService::Unregister(ViewportDrawingDelegateHandle); | ||||
| 		} | ||||
|  | ||||
| 		FComponentAssetBrokerage::UnregisterBroker(AssetBroker); | ||||
|  | ||||
| 		if (MainMenuExtender.IsValid()) | ||||
| 		{ | ||||
| 			FLevelEditorModule* LevelEditorModule = FModuleManager::GetModulePtr<FLevelEditorModule>( "LevelEditor" ); | ||||
| 			if (LevelEditorModule) | ||||
| 			{ | ||||
| 				LevelEditorModule->GetMenuExtensibilityManager()->RemoveExtender(MainMenuExtender); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings")) | ||||
| 	{ | ||||
| 		SettingsModule->UnregisterSettings("Project", "Plugins", "FMODStudio"); | ||||
| 	} | ||||
|  | ||||
| 	// Unregister AssetTypeActions | ||||
| 	if (FModuleManager::Get().IsModuleLoaded("AssetTools")) | ||||
| 	{ | ||||
| 		IAssetTools& AssetTools = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools").Get(); | ||||
|  | ||||
| 		AssetTools.UnregisterAssetTypeActions(FMODEventAssetTypeActions.ToSharedRef()); | ||||
| 	} | ||||
|  | ||||
| 	// Unregister component visualizers | ||||
| 	if (GUnrealEd != nullptr) | ||||
| 	{ | ||||
| 		// Iterate over all class names we registered for | ||||
| 		for (FName ClassName : RegisteredComponentClassNames) | ||||
| 		{ | ||||
| 			GUnrealEd->UnregisterComponentVisualizer(ClassName); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	IFMODStudioModule::Get().BanksReloadedEvent().Remove(HandleBanksReloadedDelegateHandle); | ||||
| } | ||||
|  | ||||
| bool FFMODStudioEditorModule::HandleSettingsSaved() | ||||
| { | ||||
| 	IFMODStudioModule::Get().RefreshSettings(); | ||||
| 	 | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void FFMODStudioEditorModule::HandleBanksReloaded() | ||||
| { | ||||
| 	// Show a reload notification | ||||
| 	TArray<FString> FailedBanks = IFMODStudioModule::Get().GetFailedBankLoads(EFMODSystemContext::Auditioning); | ||||
| 	FText Message; | ||||
| 	SNotificationItem::ECompletionState State; | ||||
| 	if (FailedBanks.Num() == 0) | ||||
| 	{ | ||||
| 		Message = LOCTEXT("FMODBanksReloaded", "Reloaded FMOD Banks\n"); | ||||
| 		State = SNotificationItem::CS_Success; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		FString CombinedMessage = "Problem loading FMOD Banks:"; | ||||
| 		for (auto Entry : FailedBanks) | ||||
| 		{ | ||||
| 			CombinedMessage += TEXT("\n"); | ||||
| 			CombinedMessage += Entry; | ||||
|  | ||||
| 			UE_LOG(LogFMOD, Warning, TEXT("Problem loading FMOD Bank: %s"), *Entry); | ||||
| 		} | ||||
|  | ||||
| 		Message = FText::Format( | ||||
| 				LOCTEXT("FMODBanksReloaded", "{0}"), | ||||
| 				FText::FromString(CombinedMessage)); | ||||
| 		State = SNotificationItem::CS_Fail; | ||||
| 	} | ||||
| 	ShowNotification(Message, State); | ||||
| } | ||||
|  | ||||
| void FFMODStudioEditorModule::ShowNotification(const FText& Text, SNotificationItem::ECompletionState State) | ||||
| { | ||||
| 	FNotificationInfo Info(Text); | ||||
| 	Info.Image = FEditorStyle::GetBrush(TEXT("NoBrush")); | ||||
| 	Info.FadeInDuration = 0.1f; | ||||
| 	Info.FadeOutDuration = 0.5f; | ||||
| 	Info.ExpireDuration = State == SNotificationItem::CS_Fail ? 6.0f : 1.5f; | ||||
| 	Info.bUseThrobber = false; | ||||
| 	Info.bUseSuccessFailIcons = true; | ||||
| 	Info.bUseLargeFont = true; | ||||
| 	Info.bFireAndForget = false; | ||||
| 	Info.bAllowThrottleWhenFrameRateIsLow = false; | ||||
| 	auto NotificationItem = FSlateNotificationManager::Get().AddNotification(Info); | ||||
| 	NotificationItem->SetCompletionState(State); | ||||
| 	NotificationItem->ExpireAndFadeout(); | ||||
|  | ||||
| 	if (GCurrentLevelEditingViewportClient) | ||||
| 	{ | ||||
| 		// Refresh any 3d event visualization | ||||
| 		GCurrentLevelEditingViewportClient->bNeedsRedraw = true; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void FFMODStudioEditorModule::RegisterComponentVisualizer(FName ComponentClassName, TSharedPtr<FComponentVisualizer> Visualizer) | ||||
| { | ||||
| 	if (GUnrealEd != nullptr) | ||||
| 	{ | ||||
| 		GUnrealEd->RegisterComponentVisualizer(ComponentClassName, Visualizer); | ||||
| 	} | ||||
|  | ||||
| 	RegisteredComponentClassNames.Add(ComponentClassName); | ||||
|  | ||||
| 	if (Visualizer.IsValid()) | ||||
| 	{ | ||||
| 		Visualizer->OnRegister(); | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,9 @@ | ||||
| // Copyright (c), Firelight Technologies Pty, Ltd. 2012-2016. | ||||
| #pragma once | ||||
|  | ||||
| #include "Engine.h" | ||||
| #include "UnrealEd.h" | ||||
| #include "Components/SceneComponent.h" | ||||
|  | ||||
| DECLARE_LOG_CATEGORY_EXTERN(LogFMOD, Log, All); | ||||
|  | ||||
| @@ -0,0 +1,56 @@ | ||||
| // Copyright (c), Firelight Technologies Pty, Ltd. 2012-2016. | ||||
|  | ||||
| #include "FMODStudioEditorPrivatePCH.h" | ||||
| #include "FMODStudioStyle.h" | ||||
| #include "SlateStyle.h" | ||||
| #include "EditorStyle.h" | ||||
|  | ||||
| #define IMAGE_BRUSH(RelativePath, ...) FSlateImageBrush(Style.RootToContentDir(RelativePath, TEXT(".png")), __VA_ARGS__) | ||||
| #define BOX_BRUSH(RelativePath, ...) FSlateBoxBrush(Style.RootToContentDir(RelativePath, TEXT(".png")), __VA_ARGS__) | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////////////// | ||||
| // FFMODStudioStyle | ||||
|  | ||||
| TSharedPtr<FSlateStyleSet> FFMODStudioStyle::StyleInstance = NULL; | ||||
|  | ||||
| void FFMODStudioStyle::Initialize() | ||||
| { | ||||
| 	if (!StyleInstance.IsValid()) | ||||
| 	{ | ||||
| 		StyleInstance = Create(); | ||||
| 	} | ||||
|  | ||||
| 	SetStyle(StyleInstance.ToSharedRef()); | ||||
| } | ||||
|  | ||||
| void FFMODStudioStyle::Shutdown() | ||||
| { | ||||
| 	ResetToDefault(); | ||||
| 	ensure(StyleInstance.IsUnique()); | ||||
| 	StyleInstance.Reset(); | ||||
| } | ||||
|  | ||||
| TSharedRef<FSlateStyleSet> FFMODStudioStyle::Create() | ||||
| { | ||||
| 	IEditorStyleModule& EditorStyle = FModuleManager::LoadModuleChecked<IEditorStyleModule>(TEXT("EditorStyle")); | ||||
| 	TSharedRef< FSlateStyleSet > StyleRef = EditorStyle.CreateEditorStyleInstance(); | ||||
| 	FSlateStyleSet& Style = StyleRef.Get(); | ||||
|  | ||||
| 	const FVector2D Icon20x20(20.0f, 20.0f); | ||||
| 	const FVector2D Icon40x40(40.0f, 40.0f); | ||||
| 	 | ||||
| 	Style.Set( "ClassIcon.FMODAmbientSound", new IMAGE_BRUSH( "Icons/AssetIcons/AmbientSound_16x",  FVector2D(16.0f, 16.0f) ) ); | ||||
| 	Style.Set( "ClassThumbnail.FMODAmbientSound", new IMAGE_BRUSH( "Icons/AssetIcons/AmbientSound_64x", FVector2D(64.0f, 64.0f)  ) ); | ||||
|  | ||||
| 	Style.Set( "ClassIcon.FMODAudioComponent", new IMAGE_BRUSH( "Icons/ActorIcons/SoundActor_16x",  FVector2D(16.0f, 16.0f) ) ); | ||||
| 	//Style.Set( "ClassThumbnail.FMODAudioComponent", new IMAGE_BRUSH( "Icons/ActorIcons/SoundActor_64x",  FVector2D(64.0f, 64.0f) ) ); | ||||
|  | ||||
| 	Style.Set( "ClassIcon.FMODAsset", new IMAGE_BRUSH( "Icons/ActorIcons/SoundActor_16x", FVector2D(16.0f,16.0f)  ) ); | ||||
| 	//Style.Set( "ClassThumbnail.FMODAsset", new IMAGE_BRUSH( "Icons/ActorIcons/SoundActor_64x", FVector2D(64.0f, 64.0f)  ) ); | ||||
|  | ||||
| 	return StyleRef; | ||||
| } | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
| #undef IMAGE_BRUSH | ||||
| @@ -0,0 +1,20 @@ | ||||
| // Copyright (c), Firelight Technologies Pty, Ltd. 2012-2016. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| class FFMODStudioStyle : public FEditorStyle | ||||
| { | ||||
| public: | ||||
| 	static void Initialize(); | ||||
|  | ||||
| 	static void Shutdown(); | ||||
|  | ||||
| private: | ||||
| 	static TSharedRef<class FSlateStyleSet> Create(); | ||||
|  | ||||
| private: | ||||
| 	static TSharedPtr<class FSlateStyleSet> StyleInstance; | ||||
|  | ||||
| private: | ||||
| 	FFMODStudioStyle() {} | ||||
| }; | ||||
| @@ -0,0 +1,375 @@ | ||||
| // Copyright (c), Firelight Technologies Pty, Ltd. 2012-2016. | ||||
|  | ||||
| #include "FMODStudioEditorPrivatePCH.h" | ||||
| #include "SFMODEventEditorPanel.h" | ||||
| #include "FMODStudioModule.h" | ||||
| #include "FMODUtils.h" | ||||
| #include "Input/Reply.h" | ||||
| #include "SNumericEntryBox.h" | ||||
| #include "SExpandableArea.h" | ||||
| #include "fmod_studio.hpp" | ||||
|  | ||||
| #define LOCTEXT_NAMESPACE "FMODEventEditor" | ||||
|  | ||||
| SFMODEventEditorPanel::~SFMODEventEditorPanel() | ||||
| { | ||||
| } | ||||
|  | ||||
| void SFMODEventEditorPanel::Construct(const FArguments& InArgs) | ||||
| { | ||||
| 	FMODEventEditorPtr = InArgs._FMODEventEditor; | ||||
|  | ||||
| 	FMOD::Studio::EventDescription* EventDescription = FMODEventEditorPtr.Pin()->GetEventDescription(); | ||||
| 	 | ||||
| 	TSharedRef<SBorder> ToolbarBorder = ConstructToolbar(EventDescription); | ||||
| 	TSharedRef<SExpandableArea> InfoArea = ConstructInfo(EventDescription); | ||||
| 	TSharedRef<SExpandableArea> ParametersArea = ConstructParameters(EventDescription); | ||||
| 	TSharedRef<SExpandableArea> UserPropertiesArea = ConstructUserProperties(EventDescription); | ||||
|  | ||||
| 	TSharedRef<SVerticalBox> ChildWidget = | ||||
| 		SNew(SVerticalBox) | ||||
| 		+ SVerticalBox::Slot() | ||||
| 		.AutoHeight() | ||||
| 		.Padding(0.0f, 3.0f) | ||||
| 		[ | ||||
| 			InfoArea | ||||
| 		] | ||||
| 		+ SVerticalBox::Slot() | ||||
| 		.AutoHeight() | ||||
| 		.Padding(0.0f, 3.0f) | ||||
| 		[ | ||||
| 			ParametersArea | ||||
| 		] | ||||
| 		+ SVerticalBox::Slot() | ||||
| 		.AutoHeight() | ||||
| 		.Padding(0.0f, 3.0f) | ||||
| 		[ | ||||
| 			UserPropertiesArea | ||||
| 		]; | ||||
|  | ||||
| 	ChildSlot | ||||
| 	[ | ||||
| 		SNew(SVerticalBox) | ||||
| 		+ SVerticalBox::Slot() | ||||
| 		.AutoHeight() | ||||
| 		.Padding(0.0f, 3.0f) | ||||
| 		[ | ||||
| 			ToolbarBorder | ||||
| 		] | ||||
| 		+ SVerticalBox::Slot() | ||||
| 		.FillHeight(1.0f) | ||||
| 		[ | ||||
| 			SNew(SScrollBox) | ||||
| 			+ SScrollBox::Slot() | ||||
| 			.Padding(0.0f) | ||||
| 			[ | ||||
| 				SNew(SVerticalBox) | ||||
| 				+ SVerticalBox::Slot() | ||||
| 				.AutoHeight() | ||||
| 				.Padding(0.0f) | ||||
| 				[ | ||||
| 					ChildWidget | ||||
| 				] | ||||
| 			] | ||||
| 		] | ||||
| 	]; | ||||
| } | ||||
|  | ||||
| TSharedRef<SBorder> SFMODEventEditorPanel::ConstructToolbar(FMOD::Studio::EventDescription* EventDescription) | ||||
| { | ||||
| 	float MinDistance = 0.0f; | ||||
| 	float MaxDistance = 0.0f; | ||||
| 	int32 EventLengthMS = 0; | ||||
| 	bool bIsOneshot = false, bIsStream = false, bIs3D = false; | ||||
| 	if (EventDescription != nullptr) | ||||
| 	{ | ||||
| 		EventDescription->getMinimumDistance(&MinDistance); | ||||
| 		EventDescription->getMaximumDistance(&MaxDistance); | ||||
| 		EventDescription->getLength(&EventLengthMS); | ||||
| 		EventDescription->isOneshot(&bIsOneshot); | ||||
| 		EventDescription->isStream(&bIsStream); | ||||
| 		EventDescription->is3D(&bIs3D); | ||||
| 	} | ||||
|  | ||||
| 	const FTimespan EventLength = FTimespan::FromMilliseconds((double)EventLengthMS); | ||||
| 	const FString EventLengthString = EventLength.GetHours() <= 0 ? EventLength.ToString(TEXT("%m:%s.%f")) : EventLength.ToString(TEXT("%h:%m:%s.%f")); | ||||
|  | ||||
| 	const FText RadiusText = FText::Format(LOCTEXT("RadiusFormat", "Distance Attenuation: {0}m to {1}m"), FText::AsNumber(MinDistance), FText::AsNumber(MaxDistance)); | ||||
| 	const FText LengthText = FText::Format(LOCTEXT("LengthFormat", "Length: {0}"), FText::FromString(EventLengthString)); | ||||
| 	 | ||||
| 	FText EventInfoText; | ||||
| 	if (bIs3D && bIsOneshot) | ||||
| 	{ | ||||
| 		EventInfoText = FText::Format(LOCTEXT("RadiusLengthFormat", "{0}  -  {1}"), RadiusText, LengthText); | ||||
| 	} | ||||
| 	else if (!bIs3D && bIsOneshot) | ||||
| 	{ | ||||
| 		EventInfoText = LengthText; | ||||
| 	} | ||||
| 	else if (bIs3D && !bIsOneshot) | ||||
| 	{ | ||||
| 		EventInfoText = RadiusText; | ||||
| 	} | ||||
|  | ||||
| 	return SNew(SBorder) | ||||
| 		.BorderImage(FEditorStyle::Get().GetBrush("ToolPanel.GroupBorder")) | ||||
| 		.Padding(6.0f) | ||||
| 		.Content() | ||||
| 		[ | ||||
| 			SNew(SHorizontalBox) | ||||
| 			+ SHorizontalBox::Slot() | ||||
| 			.AutoWidth() | ||||
| 			.Padding(0.0f, 0.0f, 2.0f, 0.0f) | ||||
| 			.VAlign(VAlign_Center) | ||||
| 			.HAlign(HAlign_Left) | ||||
| 			[ | ||||
| 				SNew(SButton) | ||||
| 				.VAlign(VAlign_Center) | ||||
| 				.Text(LOCTEXT("Play", "Play")) | ||||
| 				.ContentPadding(4) | ||||
| 				.OnClicked(this, &SFMODEventEditorPanel::OnClickedPlay) | ||||
| 			] | ||||
| 			+ SHorizontalBox::Slot() | ||||
| 			.AutoWidth() | ||||
| 			.Padding(2.0f, 0.0f) | ||||
| 			.VAlign(VAlign_Center) | ||||
| 			.HAlign(HAlign_Left) | ||||
| 			[ | ||||
| 				SNew(SButton) | ||||
| 				.Text(LOCTEXT("Pause", "Pause")) | ||||
| 				.ContentPadding(4) | ||||
| 				.OnClicked(this, &SFMODEventEditorPanel::OnClickedPause) | ||||
| 			] | ||||
| 			+ SHorizontalBox::Slot() | ||||
| 			.AutoWidth() | ||||
| 			.Padding(2.0f, 0.0f) | ||||
| 			.VAlign(VAlign_Center) | ||||
| 			.HAlign(HAlign_Left) | ||||
| 			[ | ||||
| 				SNew(SButton) | ||||
| 				.VAlign(VAlign_Center) | ||||
| 				.Text(LOCTEXT("Stop", "Stop")) | ||||
| 				.ContentPadding(4) | ||||
| 				.OnClicked(this, &SFMODEventEditorPanel::OnClickedStop) | ||||
| 			] | ||||
| 			+ SHorizontalBox::Slot() | ||||
| 			.FillWidth(1.0f) | ||||
| 			.Padding(2.0f, 0.0f) | ||||
| 			.VAlign(VAlign_Center) | ||||
| 			.HAlign(HAlign_Right) | ||||
| 			[ | ||||
| 				SNew(STextBlock) | ||||
| 				.Text(EventInfoText) | ||||
| 			] | ||||
| 		]; | ||||
| } | ||||
|  | ||||
| void AddTextField(TSharedRef<SVerticalBox>& InfoBox, const TCHAR* Name, const FText& Value) | ||||
| { | ||||
| 	InfoBox->AddSlot() | ||||
| 	.Padding(4.0f, 3.0f) | ||||
| 	[ | ||||
| 		SNew(SHorizontalBox) | ||||
| 		+ SHorizontalBox::Slot() | ||||
| 		.FillWidth(0.3f) | ||||
| 		[ | ||||
| 			SNew(STextBlock) | ||||
|             .Text(FText::FromString(Name)) | ||||
| 		] | ||||
| 		+ SHorizontalBox::Slot() | ||||
| 		[ | ||||
| 			SNew(SEditableText) | ||||
| 			.Text(Value) | ||||
| 			.IsReadOnly(true) | ||||
| 		] | ||||
| 	]; | ||||
| } | ||||
|  | ||||
| void AddBoolField(TSharedRef<SVerticalBox>& InfoBox, const TCHAR* Name, bool bValue) | ||||
| { | ||||
| 	AddTextField(InfoBox, Name, bValue ? LOCTEXT("True","True") : LOCTEXT("False","False")); | ||||
| } | ||||
|  | ||||
| void AddFloatField(TSharedRef<SVerticalBox>& InfoBox, const TCHAR* Name, float Value) | ||||
| { | ||||
| 	AddTextField(InfoBox, Name, FText::AsNumber(Value)); | ||||
| } | ||||
|  | ||||
| TSharedRef<SExpandableArea> MakeBox(TSharedRef<SVerticalBox>& InfoBox, const FText& Value) | ||||
| { | ||||
| 	return SNew(SExpandableArea) | ||||
| 		.AreaTitle(Value) | ||||
| 		.InitiallyCollapsed(false) | ||||
| 		.BodyContent() | ||||
| 		[ | ||||
| 			SNew(SBorder) | ||||
| 			.BorderImage(FCoreStyle::Get().GetBrush("NoBorder")) | ||||
| 			.Padding(4.0f) | ||||
| 			.Content() | ||||
| 			[ | ||||
| 				InfoBox | ||||
| 			] | ||||
| 		]; | ||||
| } | ||||
|  | ||||
| TSharedRef<SExpandableArea> SFMODEventEditorPanel::ConstructInfo(FMOD::Studio::EventDescription* EventDescription) | ||||
| { | ||||
| 	TSharedRef<SVerticalBox> InfoBox = SNew(SVerticalBox); | ||||
|  | ||||
| 	if (EventDescription != nullptr) | ||||
| 	{ | ||||
| 		FString EventPath = FMODUtils::GetPath(EventDescription); | ||||
| 		FGuid Guid = FMODUtils::GetID(EventDescription); | ||||
|  | ||||
| 		int Length = 0.0f; | ||||
| 		float MinDist = 0.0f; | ||||
| 		float MaxDist = 0.0f; | ||||
| 		EventDescription->getLength(&Length); | ||||
| 		EventDescription->getMinimumDistance(&MinDist); | ||||
| 		EventDescription->getMaximumDistance(&MaxDist); | ||||
|  | ||||
| 		bool bOneShot = false; | ||||
| 		bool bStream = false; | ||||
| 		bool b3D = false; | ||||
| 		EventDescription->isOneshot(&bOneShot); | ||||
| 		EventDescription->isStream(&bStream); | ||||
| 		EventDescription->is3D(&b3D); | ||||
|  | ||||
| 		AddTextField(InfoBox, TEXT("Path"), FText::FromString(EventPath)); | ||||
| 		AddTextField(InfoBox, TEXT("Guid"), FText::FromString(Guid.ToString(EGuidFormats::DigitsWithHyphensInBraces))); | ||||
| 		AddBoolField(InfoBox, TEXT("OneShot"), bOneShot); | ||||
| 		AddBoolField(InfoBox, TEXT("Streaming"), bStream); | ||||
| 		AddBoolField(InfoBox, TEXT("3D"), b3D); | ||||
|  | ||||
| 		AddFloatField(InfoBox, TEXT("Length"), static_cast<float>(Length)); | ||||
| 		if (b3D) | ||||
| 		{ | ||||
| 			AddFloatField(InfoBox, TEXT("Min Dist"), MinDist); | ||||
| 			AddFloatField(InfoBox, TEXT("Max Dist"), MaxDist); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return MakeBox(InfoBox, LOCTEXT("EventInfo", "Event Info")); | ||||
| } | ||||
|  | ||||
| TSharedRef<SExpandableArea> SFMODEventEditorPanel::ConstructParameters(FMOD::Studio::EventDescription* EventDescription) | ||||
| { | ||||
| 	auto EventEditor = FMODEventEditorPtr.Pin(); | ||||
| 	TSharedRef<SVerticalBox> ParametersBox = SNew(SVerticalBox); | ||||
|  | ||||
| 	FNumberFormattingOptions Options; | ||||
| 	Options.MinimumFractionalDigits = 1; | ||||
|  | ||||
| 	if (EventDescription != nullptr) | ||||
| 	{ | ||||
| 		int32 ParameterCount; | ||||
| 		EventDescription->getParameterCount(&ParameterCount); | ||||
| 		for (int32 ParamIdx = 0; ParamIdx < ParameterCount; ParamIdx++) | ||||
| 		{ | ||||
| 			FMOD_STUDIO_PARAMETER_DESCRIPTION Parameter; | ||||
| 			EventDescription->getParameterByIndex(ParamIdx, &Parameter); | ||||
|  | ||||
| 			EventEditor->GetParameterValues().Add(Parameter.minimum); | ||||
|  | ||||
| 			const FString ParameterName = Parameter.type == FMOD_STUDIO_PARAMETER_GAME_CONTROLLED ? FString(UTF8_TO_TCHAR(Parameter.name)) : FMODUtils::ParameterTypeToString(Parameter.type); | ||||
| 			const FText ToolTipText = FText::Format(LOCTEXT("ParameterTooltipFormat", "{0} (Min Value: {1} - Max Value: {2})"), | ||||
| 				FText::FromString(ParameterName), FText::AsNumber(Parameter.minimum, &Options), FText::AsNumber(Parameter.maximum, &Options)); | ||||
|  | ||||
| 			ParametersBox->AddSlot() | ||||
| 			.Padding(4.0f, 2.0f) | ||||
| 			[ | ||||
| 				SNew(SHorizontalBox) | ||||
| 				.ToolTipText(ToolTipText) | ||||
| 				+ SHorizontalBox::Slot() | ||||
| 				.FillWidth(0.3f) | ||||
| 				[ | ||||
| 					SNew(STextBlock) | ||||
| 					.Text(FText::FromString(ParameterName)) | ||||
| 				] | ||||
| 				+ SHorizontalBox::Slot() | ||||
| 				.MaxWidth(200.0f) | ||||
| 				[ | ||||
| 					SNew(SNumericEntryBox<float>) | ||||
| 					.Value(this, &SFMODEventEditorPanel::GetParameterValue, ParamIdx) | ||||
| 					.OnValueChanged(this, &SFMODEventEditorPanel::OnParameterValueChanged, ParamIdx) | ||||
| 					.AllowSpin(true) | ||||
| 					.MinValue(Parameter.minimum) | ||||
| 					.MaxValue(Parameter.maximum) | ||||
| 					.MinSliderValue(Parameter.minimum) | ||||
| 					.MaxSliderValue(Parameter.maximum) | ||||
| 					.Delta(0.01f) | ||||
| 				] | ||||
| 			]; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return MakeBox(ParametersBox, LOCTEXT("EventParameters", "Event Parameters")); | ||||
| } | ||||
|  | ||||
| TSharedRef<SExpandableArea> SFMODEventEditorPanel::ConstructUserProperties(FMOD::Studio::EventDescription* EventDescription) | ||||
| { | ||||
| 	TSharedRef<SVerticalBox> UserPropertiesBox = SNew(SVerticalBox); | ||||
|  | ||||
| 	if (EventDescription != nullptr) | ||||
| 	{ | ||||
| 		int32 UserPropertyCount; | ||||
| 		EventDescription->getUserPropertyCount(&UserPropertyCount); | ||||
| 		for (int32 PropertyIdx = 0; PropertyIdx < UserPropertyCount; PropertyIdx++) | ||||
| 		{ | ||||
| 			FMOD_STUDIO_USER_PROPERTY UserProperty; | ||||
| 			EventDescription->getUserPropertyByIndex(PropertyIdx, &UserProperty); | ||||
|  | ||||
| 			FText PropertyText; | ||||
| 			switch (UserProperty.type) | ||||
| 			{ | ||||
| 			case FMOD_STUDIO_USER_PROPERTY_TYPE_INTEGER: | ||||
| 				PropertyText = FText::AsNumber(UserProperty.intValue); | ||||
| 				break; | ||||
| 			case FMOD_STUDIO_USER_PROPERTY_TYPE_BOOLEAN: | ||||
| 				PropertyText = UserProperty.boolValue ? LOCTEXT("True", "True") : LOCTEXT("False", "False"); | ||||
| 				break; | ||||
| 			case FMOD_STUDIO_USER_PROPERTY_TYPE_FLOAT: | ||||
| 				PropertyText = FText::AsNumber(UserProperty.floatValue); | ||||
| 				break; | ||||
| 			case FMOD_STUDIO_USER_PROPERTY_TYPE_STRING: | ||||
| 				PropertyText = FText::FromString(UTF8_TO_TCHAR(UserProperty.stringValue)); | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| 			FString UserName(UTF8_TO_TCHAR(UserProperty.name)); | ||||
| 			AddTextField(UserPropertiesBox, *UserName, PropertyText); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return MakeBox(UserPropertiesBox, LOCTEXT("EventUserProperties", "Event User Properties")); | ||||
| } | ||||
|  | ||||
| FReply SFMODEventEditorPanel::OnClickedPlay() | ||||
| { | ||||
| 	FMODEventEditorPtr.Pin()->PlayEvent(); | ||||
| 	return FReply::Handled(); | ||||
| } | ||||
|  | ||||
| FReply SFMODEventEditorPanel::OnClickedStop() | ||||
| { | ||||
| 	FMODEventEditorPtr.Pin()->StopEvent(); | ||||
| 	return FReply::Handled(); | ||||
| } | ||||
|  | ||||
| FReply SFMODEventEditorPanel::OnClickedPause() | ||||
| { | ||||
| 	FMODEventEditorPtr.Pin()->PauseEvent(); | ||||
| 	return FReply::Handled(); | ||||
| } | ||||
|  | ||||
| void SFMODEventEditorPanel::OnParameterValueChanged(float NewValue, int32 ParameterIdx) | ||||
| { | ||||
| 	FMODEventEditorPtr.Pin()->SetParameterValue(ParameterIdx, NewValue); | ||||
| } | ||||
|  | ||||
| TOptional<float> SFMODEventEditorPanel::GetParameterValue(int32 ParameterIdx) const | ||||
| { | ||||
| 	return FMODEventEditorPtr.Pin()->GetParameterValues()[ParameterIdx]; | ||||
| } | ||||
|  | ||||
| #undef LOC_NAMESPACE | ||||
| @@ -0,0 +1,45 @@ | ||||
| // Copyright (c), Firelight Technologies Pty, Ltd. 2012-2016. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "FMODEventEditor.h" | ||||
|  | ||||
| namespace FMOD | ||||
| { | ||||
| 	namespace Studio | ||||
| 	{ | ||||
| 		class EventDescription; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| class SFMODEventEditorPanel : public SCompoundWidget | ||||
| { | ||||
| public: | ||||
|  | ||||
| 	SLATE_BEGIN_ARGS(SFMODEventEditorPanel) | ||||
| 	{} | ||||
| 		SLATE_ARGUMENT(TWeakPtr<FFMODEventEditor>, FMODEventEditor) | ||||
| 	SLATE_END_ARGS() | ||||
|  | ||||
| 	~SFMODEventEditorPanel(); | ||||
|  | ||||
| 	/** SCompoundWidget interface */ | ||||
| 	void Construct(const FArguments& InArgs); | ||||
|  | ||||
| private: | ||||
|  | ||||
| 	TSharedRef<SBorder> ConstructToolbar(FMOD::Studio::EventDescription* EventDescription); | ||||
| 	TSharedRef<SExpandableArea> ConstructInfo(FMOD::Studio::EventDescription* EventDescription); | ||||
| 	TSharedRef<SExpandableArea> ConstructParameters(FMOD::Studio::EventDescription* EventDescription); | ||||
| 	TSharedRef<SExpandableArea> ConstructUserProperties(FMOD::Studio::EventDescription* EventDescription); | ||||
|  | ||||
| 	/** Editor that owns this panel */ | ||||
| 	TWeakPtr<FFMODEventEditor> FMODEventEditorPtr; | ||||
|  | ||||
| 	FReply OnClickedPlay(); | ||||
| 	FReply OnClickedStop(); | ||||
| 	FReply OnClickedPause(); | ||||
|  | ||||
| 	TOptional<float> GetParameterValue(int32 ParameterIdx) const; | ||||
| 	void OnParameterValueChanged(float NewValue, int32 ParameterIdx); | ||||
| }; | ||||
| @@ -0,0 +1,35 @@ | ||||
| // Copyright (c), Firelight Technologies Pty, Ltd. 2012-2016. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "ModuleManager.h" | ||||
|  | ||||
| /** | ||||
|  * The public interface to this module | ||||
|  */ | ||||
| class IFMODStudioEditorModule : public IModuleInterface | ||||
| { | ||||
|  | ||||
| public: | ||||
|  | ||||
| 	/** | ||||
| 	 * Singleton-like access to this module's interface.  This is just for convenience! | ||||
| 	 * Beware of calling this during the shutdown phase, though.  Your module might have been unloaded already. | ||||
| 	 * | ||||
| 	 * @return Returns singleton instance, loading the module on demand if needed | ||||
| 	 */ | ||||
| 	static inline IFMODStudioEditorModule& Get() | ||||
| 	{ | ||||
| 		return FModuleManager::LoadModuleChecked< IFMODStudioEditorModule >( "FMODStudioEditor" ); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Checks to see if this module is loaded and ready.  It is only valid to call Get() if IsAvailable() returns true. | ||||
| 	 * | ||||
| 	 * @return True if the module is loaded and ready to use | ||||
| 	 */ | ||||
| 	static inline bool IsAvailable() | ||||
| 	{ | ||||
| 		return FModuleManager::Get().IsModuleLoaded( "FMODStudioEditor" ); | ||||
| 	} | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user