// Fill out your copyright notice in the Description page of Project Settings. #include "SoundSource/VAAbstractSourceComponent.h" #include "SoundSource/VASoundSource.h" #include "VAReceiverActor.h" #include "VAPlugin.h" #include "VAUtils.h" #include "ImageSourceModel/VAImageSourceModel.h" #include "SignalSources/VAAudiofileSignalSource.h" #include "SignalSources/VAJetEngineSignalSource.h" #include "Directivity/VADirectivityManager.h" #include "Components/SkeletalMeshComponent.h" // Skeletons #include "Kismet/GameplayStatics.h" // Get Actors of Class #include "Utility/VirtualRealityUtilities.h" // Sets default values for this component's properties UVAAbstractSourceComponent::UVAAbstractSourceComponent() { PrimaryComponentTick.bCanEverTick = true; //Default objects for subcomponents does not work //SignalSource = CreateDefaultSubobject<UVAAudiofileSignalSource>(TEXT("SignalSource"), false); } // Called when the game starts void UVAAbstractSourceComponent::BeginPlay() { Super::BeginPlay(); // If the receiver Actor is initialized but this sound Component not, this Component is spawned at runtime and has to be initialized // Otherwise it will be later on initialized from the Receiver Actor TArray<AActor*> ReceiverActors; UGameplayStatics::GetAllActorsOfClass(this->GetWorld(), AVAReceiverActor::StaticClass(), ReceiverActors); if (ReceiverActors.Num() >= 1 && Cast<AVAReceiverActor>(ReceiverActors[0])->IsInitialized() && !bInitialized) { Initialize(); } } void UVAAbstractSourceComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) { SoundSource.Reset(); Super::EndPlay(EndPlayReason); } // Called every frame void UVAAbstractSourceComponent::TickComponent(const float DeltaTime, const ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); if (!FVAPlugin::GetUseVA() || !UVirtualRealityUtilities::IsMaster()) { return; } if (!bInitialized) { if(!bInformedNotInitialized) { FVAUtils::OpenMessageBox("[UVASourceComponent::TickComponent()]: Sound source is not initialized", true); bInformedNotInitialized = true; } return; } if (bFirstTick && UVirtualRealityUtilities::IsMaster()) { TimeSinceUpdate = 1.0f; } bFirstTick = false; TimeSinceUpdate += DeltaTime; if ((MovementSetting == EMovement::AttachToBone || MovementSetting == EMovement::MoveWithObject) && TimeSinceUpdate > (1.0f / float(UpdateRate))) { UpdatePose(); TimeSinceUpdate = 0.0f; } } void UVAAbstractSourceComponent::UpdateSkeletalMeshIfAttachToBone() { if (MovementSetting == EMovement::AttachToBone) { AActor* Owner = GetOwner(); bool bFoundSkelMesh = false; while(!bFoundSkelMesh && Owner != nullptr) { TArray<UActorComponent*> MeshComponents = Owner->GetComponentsByClass(USkeletalMeshComponent::StaticClass()); for (UActorComponent* Component : MeshComponents) { USkeletalMeshComponent* MeshComp = Cast<USkeletalMeshComponent>(Component); if (MeshComp && MeshComp->DoesSocketExist(FName(*BoneName))) { //found the right mesh component SkeletalMeshComponent = MeshComp; bFoundSkelMesh = true; break; } } Owner = Owner->GetAttachParentActor(); } if (SkeletalMeshComponent == nullptr) { FVAUtils::OpenMessageBox("[UVASourceComponent::Initialize()]: Could not find bone " + BoneName + ", using MoveWithObject instead.", true); MovementSetting = EMovement::MoveWithObject; } } } void UVAAbstractSourceComponent::Initialize() { if (!FVAPlugin::GetUseVA() || bInitialized) { return; } bFirstTick = true; TArray<AActor*> ReceiverActors; UGameplayStatics::GetAllActorsOfClass(this->GetWorld(), AVAReceiverActor::StaticClass(), ReceiverActors); AVAReceiverActor* ReceiverActorTmp = nullptr; if (ReceiverActors.Num() == 0) { // If no Rec Actor found spawn one with default parameters if (ReceiverActorTmp == nullptr) { FVAUtils::LogStuff("[UVASourceComponent::BeginPlay()]: No AVAReceiver found! Spawning one with default values", false); ReceiverActorTmp = this->GetWorld()->SpawnActor<AVAReceiverActor>(AVAReceiverActor::StaticClass()); } } else if (ReceiverActors.Num() == 1) { ReceiverActorTmp = dynamic_cast<AVAReceiverActor*>(ReceiverActors[0]); FVAUtils::LogStuff("[UVASourceComponent::BeginPlay()]: Receiver found!", false); } else if (ReceiverActors.Num() >= 2) { FVAPlugin::SetUseVA(false); FVAUtils::OpenMessageBox("There are more than 1 Receiver Actors in the world. Stopping VAPlugin. Make sure to remove the wrong one.", true); FVAUtils::LogStuff("[UVASourceComponent::BeginPlay()]: More than 1 Receiver found! Stopping VAPlugin. Make sure to remove the wrong one", true); return; } CurrentReceiverActor = ReceiverActorTmp; UpdateRate = ReceiverActorTmp->GetUpdateRate(); UpdateSkeletalMeshIfAttachToBone(); SpawnPosition = GetOwner()->GetTransform().GetLocation(); SpawnRotation = GetOwner()->GetTransform().GetRotation().Rotator(); const std::string ExplicitRendererID = bRendererExplicit ? std::string( TCHAR_TO_UTF8(*RendererID) ): ""; // Empty ID => general source const std::string SoundSourceName = std::string( TCHAR_TO_UTF8(*GetName()) ); SoundSource = MakeShared<FVASoundSource>(GetWorld(), GetPosition(), GetRotation(), SoundPower, SoundSourceName, ExplicitRendererID); if (UVirtualRealityUtilities::IsMaster()) { switch (DirectivitySetting) { case EDirectivitySetting::DefaultDirectivity: SoundSource->SetDirectivity(FVADirectivityManager::GetDefaultDirectivity()); break; case EDirectivitySetting::ManualFile: SoundSource->SetDirectivity( CurrentReceiverActor->GetDirectivityByFileName(DirectivityByFileName)); break; case EDirectivitySetting::Phoneme: SoundSource->SetDirectivity( CurrentReceiverActor->GetDirectivityByMapping(DirectivityByMapping)); break; default: break; } } if (bHandleReflections) { TArray<AVAReflectionWall*> ReflWalls = CurrentReceiverActor->GetReflectionWalls(); ImageSourceModel = MakeShared<FVAImageSourceModel>(GetWorld(), SoundSource.ToSharedRef(), ReflWalls); } if (SignalSource) { SignalSource->Initialize(); SetSignalSourceID(SignalSource->GetID()); } FVAUtils::LogStuff("[UVASourceComponent::Initialize()]: SoundSourceComponent initialized successfully", false); bInitialized = true; } void UVAAbstractSourceComponent::UpdatePose() { if (!FVAPlugin::GetUseVA() || !SoundSource.IsValid()) { return; } SoundSource->SetPosition(GetPosition()); SoundSource->SetRotation(GetRotation()); if (bHandleReflections && ImageSourceModel.IsValid()) { ImageSourceModel->UpdateISPositions(); ImageSourceModel->UpdateISRotations(); } } bool UVAAbstractSourceComponent::SetSignalSourceID(const std::string& SignalSourceID) { if (!SoundSource.IsValid()) { FVAUtils::OpenMessageBox(FString("[UVASourceComponent::SetSignalSourceID]: VA sound source not initialized"), true); return false; } if (!SoundSource->SetSignalSource(SignalSourceID)) { return false; } if (bHandleReflections && ImageSourceModel.IsValid()) { return ImageSourceModel->UpdateISSignalSource(); } return true; } bool UVAAbstractSourceComponent::ShouldSendCommand() const { return (bInitialized && FVAPlugin::GetUseVA() && UVirtualRealityUtilities::IsMaster()); } // ****************************************************************** // // ******* Sound Source Settings ************************************ // // ****************************************************************** // bool UVAAbstractSourceComponent::MuteSound(const bool bMute) { if (!ShouldSendCommand()) { return false; } if (!SoundSource->MuteSound(bMute)) { return false; } if (bHandleReflections && ImageSourceModel.IsValid()) { return ImageSourceModel->MuteIS(bMute); } return true; } bool UVAAbstractSourceComponent::SetSoundPower(const float Power) { if (!ShouldSendCommand()) { return false; } if (!SoundSource->SetPower(Power)) { return false; } SoundPower = Power; if (bHandleReflections && ImageSourceModel.IsValid()) { return ImageSourceModel->UpdateISPower(); } return true; } float UVAAbstractSourceComponent::GetSoundPower() const { return SoundPower; } // ****************************************************************** // // ******* Image Sources / Reflections ****************************** // // ****************************************************************** // bool UVAAbstractSourceComponent::GetHandleReflections() const { return bHandleReflections; } // ****************************************************************** // // ******* Sound Pose *********************************************** // // ****************************************************************** // FVector UVAAbstractSourceComponent::GetPosition() const { FVector Pos; switch (MovementSetting) { case EMovement::MoveWithObject: Pos = GetOwner()->GetTransform().GetLocation(); break; case EMovement::ObjectSpawnPoint: Pos = SpawnPosition; break; case EMovement::AttachToBone: Pos = SkeletalMeshComponent->GetSocketLocation(FName(*BoneName)); break; default: Pos = FVector::ZeroVector; FVAUtils::LogStuff(FString("[UVASourceComponent::GetPosition()]: In default, Unreachable Error", true)); break; } if (bUsePoseOffset) { Pos = Pos + OffsetPosition; } return Pos; } FRotator UVAAbstractSourceComponent::GetRotation() const { FRotator Rot; switch (MovementSetting) { case EMovement::MoveWithObject: Rot = GetOwner()->GetTransform().GetRotation().Rotator(); break; case EMovement::ObjectSpawnPoint: Rot = SpawnRotation; break; case EMovement::AttachToBone: Rot = SkeletalMeshComponent->GetSocketRotation(FName(*BoneName)); break; default: FVAUtils::LogStuff(FString("[UVASourceComponent::GetRotation()]: In default, Unreachable Error", true)); Rot = FRotator::ZeroRotator; break; } if (bUsePoseOffset) { Rot = FRotator(FQuat(Rot) * FQuat(OffsetRotation)); } return Rot; } bool UVAAbstractSourceComponent::SetMovementSetting(const EMovement::Type NewMovementSetting) { if (!FVAPlugin::GetUseVA() || !SoundSource.IsValid()) { return false; } if (MovementSetting == NewMovementSetting) { return true; } MovementSetting = NewMovementSetting; UpdateSkeletalMeshIfAttachToBone(); UpdatePose(); return true; } bool UVAAbstractSourceComponent::SetUsePoseOffset(const bool bNewUsePoseOffset) { if (!FVAPlugin::GetUseVA() || !SoundSource.IsValid()) { return false; } if (bUsePoseOffset == bNewUsePoseOffset) { return true; } bUsePoseOffset = bNewUsePoseOffset; UpdatePose(); return true; } bool UVAAbstractSourceComponent::SetOffsetPosition(const FVector Pos) { if (!FVAPlugin::GetUseVA() || !SoundSource.IsValid()) { return false; } if (OffsetPosition == Pos && bUsePoseOffset) { return true; } bUsePoseOffset = true; OffsetPosition = Pos; UpdatePose(); return true; } bool UVAAbstractSourceComponent::SetOffsetRotation(const FRotator Rot) { if (!FVAPlugin::GetUseVA() || !SoundSource.IsValid()) { return false; } if (OffsetRotation == Rot && bUsePoseOffset) { return true; } bUsePoseOffset = true; OffsetRotation = Rot; UpdatePose(); return true; } // ****************************************************************** // // ******* Directivity stuff **************************************** // // ****************************************************************** // bool UVAAbstractSourceComponent::SetDirectivityByMapping(const FString Phoneme) { if (!ShouldSendCommand()) { return false; } DirectivitySetting = EDirectivitySetting::Phoneme; DirectivityByMapping = Phoneme; return SoundSource->SetDirectivity(CurrentReceiverActor->GetDirectivityByMapping(Phoneme)); } bool UVAAbstractSourceComponent::SetDirectivityByFileName(const FString FileName) { if (!ShouldSendCommand()) { return false; } DirectivitySetting = EDirectivitySetting::ManualFile; DirectivityByFileName = FileName; if (FileName == "") { return SoundSource->RemoveDirectivity(); } return SoundSource->SetDirectivity(CurrentReceiverActor->GetDirectivityByFileName(FileName)); } FString UVAAbstractSourceComponent::GetDirectivityFileName() const { if (SoundSource.IsValid()) { return SoundSource->GetDirectivityFilename(); } return ""; } // ****************************************************************** // // ******* Graphical Representation ********************************* // // ****************************************************************** // bool UVAAbstractSourceComponent::SetVisibility(const bool bVis) { if (!FVAPlugin::GetUseVA() || !SoundSource.IsValid()) { return false; } SoundSource->SetVisibility(bVis); if (bHandleReflections && ImageSourceModel.IsValid()) { ImageSourceModel->SetISVisibility(bVis); } return true; } bool UVAAbstractSourceComponent::GetVisibility() const { return SoundSource->GetVisibility(); } int UVAAbstractSourceComponent::GetSoundSourceID() const { if (!SoundSource) { return -1; } return SoundSource->GetSoundSourceID(); } bool UVAAbstractSourceComponent::SetBoneName(const FString NewBoneName) { if (!FVAPlugin::GetUseVA()) { return false; } if (BoneName == NewBoneName) { return true; } // Check if the bone exists if (SkeletalMeshComponent != nullptr && SkeletalMeshComponent->DoesSocketExist(FName(*NewBoneName))) { BoneName = NewBoneName; MovementSetting = EMovement::AttachToBone; FVAUtils::LogStuff("[UVASourceComponent::SetBoneName()]: Successfully found bone with name" + BoneName + "and set Movement Setting to follow the bone", false); return true; } FVAUtils::OpenMessageBox( "[UVASourceComponent::SetBoneName()]: Could not find the bone, using old settings instead."); return false; } FString UVAAbstractSourceComponent::GetBoneName() const { if (MovementSetting != EMovement::AttachToBone) { FVAUtils::LogStuff("[UVASourceComponent::GetBoneName()]: Movement is not set to AttachToBone..", true); return "SoundSource is not attached to bone, but current bone is: " + BoneName; } return BoneName; } // ****************************************************************** // // ******* Blueprint Settings *************************************** // // ****************************************************************** // #if WITH_EDITOR bool UVAAbstractSourceComponent::CanEditChange(const FProperty* InProperty) const { const bool ParentVal = Super::CanEditChange(InProperty); // Check Bone Name if (InProperty->GetFName() == GET_MEMBER_NAME_CHECKED(UVAAbstractSourceComponent, BoneName)) { return MovementSetting == EMovement::AttachToBone; } // Check Directivity Config if (InProperty->GetFName() == GET_MEMBER_NAME_CHECKED(UVAAbstractSourceComponent, DirectivityByFileName)) { return DirectivitySetting == EDirectivitySetting::ManualFile; } // Check Bone Name if (InProperty->GetFName() == GET_MEMBER_NAME_CHECKED(UVAAbstractSourceComponent, DirectivityByMapping)) { return DirectivitySetting == EDirectivitySetting::Phoneme; } // Check Bone Name if (InProperty->GetFName() == GET_MEMBER_NAME_CHECKED(UVAAbstractSourceComponent, OffsetPosition) || InProperty->GetFName() == GET_MEMBER_NAME_CHECKED(UVAAbstractSourceComponent, OffsetRotation)) { return bUsePoseOffset; } return ParentVal; } #endif