2021-03-15 14:00:26 -04:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
|
|
|
|
#include "MetasoundArrayRandomNode.h"
|
2021-03-29 23:58:06 -04:00
|
|
|
#include "CoreMinimal.h"
|
|
|
|
|
#include "Internationalization/Text.h"
|
|
|
|
|
#include "MetasoundBuilderInterface.h"
|
|
|
|
|
#include "MetasoundDataFactory.h"
|
|
|
|
|
#include "MetasoundExecutableOperator.h"
|
|
|
|
|
#include "MetasoundFacade.h"
|
|
|
|
|
#include "MetasoundLog.h"
|
|
|
|
|
#include "MetasoundNodeInterface.h"
|
|
|
|
|
#include "MetasoundNodeRegistrationMacro.h"
|
|
|
|
|
#include "MetasoundOperatorInterface.h"
|
|
|
|
|
#include "MetasoundPrimitives.h"
|
|
|
|
|
#include "MetasoundTrigger.h"
|
|
|
|
|
#include "Containers/CircularQueue.h"
|
|
|
|
|
#include "MetasoundArrayNodes.h"
|
2021-03-15 14:00:26 -04:00
|
|
|
|
2021-03-29 23:58:06 -04:00
|
|
|
#include <type_traits>
|
|
|
|
|
|
|
|
|
|
#define LOCTEXT_NAMESPACE "MetasoundFrontend_RandomArrayGet"
|
2021-03-15 14:00:26 -04:00
|
|
|
|
|
|
|
|
namespace Metasound
|
|
|
|
|
{
|
2021-03-29 23:58:06 -04:00
|
|
|
FArrayRandomGet::FArrayRandomGet(int32 InSeed, int32 InMaxIndex, const TArray<float>& InWeights, int32 InNoRepeatOrder)
|
2021-03-15 14:00:26 -04:00
|
|
|
{
|
2021-03-29 23:58:06 -04:00
|
|
|
Init(InSeed, InMaxIndex, InWeights, InNoRepeatOrder);
|
2021-03-15 14:00:26 -04:00
|
|
|
}
|
|
|
|
|
|
2021-03-29 23:58:06 -04:00
|
|
|
void FArrayRandomGet::Init(int32 InSeed, int32 InMaxIndex, const TArray<float>& InWeights, int32 InNoRepeatOrder)
|
2021-03-15 14:00:26 -04:00
|
|
|
{
|
|
|
|
|
SetSeed(InSeed);
|
|
|
|
|
MaxIndex = InMaxIndex;
|
|
|
|
|
SetNoRepeatOrder(InNoRepeatOrder);
|
2021-03-22 08:31:34 -04:00
|
|
|
check(!InNoRepeatOrder || !PreviousIndicesQueue->IsFull());
|
2021-03-29 23:58:06 -04:00
|
|
|
RandomWeights = InWeights;
|
2021-03-15 14:00:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FArrayRandomGet::SetSeed(int32 InSeed)
|
|
|
|
|
{
|
|
|
|
|
if (InSeed == INDEX_NONE)
|
|
|
|
|
{
|
|
|
|
|
RandomStream.Initialize(FPlatformTime::Cycles());
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
RandomStream.Initialize(InSeed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ResetSeed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FArrayRandomGet::SetNoRepeatOrder(int32 InNoRepeatOrder)
|
|
|
|
|
{
|
|
|
|
|
// Make sure the no repeat order is between 0 and one less than our max index
|
|
|
|
|
InNoRepeatOrder = FMath::Clamp(InNoRepeatOrder, 0, MaxIndex - 1);
|
|
|
|
|
|
|
|
|
|
if (InNoRepeatOrder != NoRepeatOrder)
|
|
|
|
|
{
|
2021-03-22 11:03:56 -04:00
|
|
|
PreviousIndicesQueue = MakeUnique<TCircularQueue<int32>>(InNoRepeatOrder + 2);
|
2021-03-15 14:00:26 -04:00
|
|
|
PreviousIndices.Reset();
|
|
|
|
|
NoRepeatOrder = InNoRepeatOrder;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 23:58:06 -04:00
|
|
|
void FArrayRandomGet::SetRandomWeights(const TArray<float>& InRandomWeights)
|
|
|
|
|
{
|
|
|
|
|
RandomWeights = InRandomWeights;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-15 14:00:26 -04:00
|
|
|
void FArrayRandomGet::ResetSeed()
|
|
|
|
|
{
|
|
|
|
|
RandomStream.Reset();
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 23:58:06 -04:00
|
|
|
float FArrayRandomGet::ComputeTotalWeight()
|
2021-03-15 14:00:26 -04:00
|
|
|
{
|
|
|
|
|
float TotalWeight = 0.0f;
|
|
|
|
|
if (RandomWeights.Num() > 0)
|
|
|
|
|
{
|
|
|
|
|
for (int32 i = 0; i < MaxIndex; ++i)
|
|
|
|
|
{
|
|
|
|
|
// If the index exists in previous indices, continue
|
|
|
|
|
if (PreviousIndices.Contains(i))
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We modulus on the weight array to determine weights for the input array
|
|
|
|
|
// I.e. if weights is 2 elements, the weights will alternate in application to the input array
|
|
|
|
|
TotalWeight += RandomWeights[i % RandomWeights.Num()];
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-29 23:58:06 -04:00
|
|
|
return TotalWeight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns the next random weighted value in the array indices.
|
|
|
|
|
int32 FArrayRandomGet::NextValue()
|
|
|
|
|
{
|
|
|
|
|
// First compute the total size of the weights
|
2021-03-30 00:26:25 -04:00
|
|
|
bool bHasWeights = RandomWeights.Num() > 0;
|
|
|
|
|
float TotalWeight = 0.0f;
|
|
|
|
|
if (bHasWeights)
|
2021-03-15 14:00:26 -04:00
|
|
|
{
|
2021-03-30 00:26:25 -04:00
|
|
|
TotalWeight = ComputeTotalWeight();
|
|
|
|
|
if (TotalWeight == 0.0f && PreviousIndices.Num() > 0)
|
2021-03-29 23:58:06 -04:00
|
|
|
{
|
|
|
|
|
PreviousIndices.Reset();
|
|
|
|
|
PreviousIndicesQueue->Empty();
|
|
|
|
|
TotalWeight = ComputeTotalWeight();
|
|
|
|
|
}
|
2021-03-30 00:26:25 -04:00
|
|
|
|
|
|
|
|
// Weights might have been set with all 0.0s. If that's the case, we treat as if there were no weights set.
|
|
|
|
|
bHasWeights = (TotalWeight > 0.0f);
|
2021-03-15 14:00:26 -04:00
|
|
|
}
|
|
|
|
|
|
2021-03-30 00:26:25 -04:00
|
|
|
if (!bHasWeights)
|
|
|
|
|
{
|
|
|
|
|
TotalWeight = (float)(FMath::Max(MaxIndex - PreviousIndices.Num(), 1));
|
|
|
|
|
}
|
|
|
|
|
check(TotalWeight > 0.0f);
|
|
|
|
|
|
2021-03-29 23:58:06 -04:00
|
|
|
|
2021-03-15 14:00:26 -04:00
|
|
|
// Make a random choice based on the total weight
|
|
|
|
|
float Choice = RandomStream.FRandRange(0.0f, TotalWeight);
|
|
|
|
|
// Now find the index this choice matches up to
|
|
|
|
|
TotalWeight = 0.0f;
|
|
|
|
|
int32 ChosenIndex = INDEX_NONE;
|
|
|
|
|
for (int32 i = 0; i < MaxIndex; ++i)
|
|
|
|
|
{
|
|
|
|
|
if (PreviousIndices.Contains(i))
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float NextTotalWeight = TotalWeight;
|
2021-03-30 00:26:25 -04:00
|
|
|
if (bHasWeights)
|
2021-03-15 14:00:26 -04:00
|
|
|
{
|
2021-03-30 00:26:25 -04:00
|
|
|
check(RandomWeights.Num() > 0);
|
|
|
|
|
NextTotalWeight += RandomWeights[i % RandomWeights.Num()];
|
2021-03-15 14:00:26 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-03-30 00:26:25 -04:00
|
|
|
NextTotalWeight += 1.0f;
|
2021-03-15 14:00:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Choice >= TotalWeight && Choice < NextTotalWeight)
|
|
|
|
|
{
|
|
|
|
|
ChosenIndex = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
TotalWeight = NextTotalWeight;
|
|
|
|
|
}
|
|
|
|
|
check(ChosenIndex != INDEX_NONE);
|
|
|
|
|
|
|
|
|
|
// Dequeue and remove the oldest previous index
|
2021-03-22 08:31:34 -04:00
|
|
|
if (NoRepeatOrder > 0)
|
2021-03-15 14:00:26 -04:00
|
|
|
{
|
2021-03-22 08:31:34 -04:00
|
|
|
if (PreviousIndices.Num() == NoRepeatOrder)
|
|
|
|
|
{
|
|
|
|
|
check(PreviousIndicesQueue->Count() == PreviousIndices.Num());
|
|
|
|
|
int32 OldPrevIndex;
|
|
|
|
|
PreviousIndicesQueue->Dequeue(OldPrevIndex);
|
|
|
|
|
PreviousIndices.Remove(OldPrevIndex);
|
|
|
|
|
check(PreviousIndices.Num() == NoRepeatOrder - 1);
|
|
|
|
|
}
|
2021-03-15 14:00:26 -04:00
|
|
|
|
2021-03-22 08:31:34 -04:00
|
|
|
check(PreviousIndices.Num() < NoRepeatOrder);
|
|
|
|
|
check(!PreviousIndicesQueue->IsFull());
|
|
|
|
|
|
|
|
|
|
bool bSuccess = PreviousIndicesQueue->Enqueue(ChosenIndex);
|
|
|
|
|
check(bSuccess);
|
|
|
|
|
check(!PreviousIndices.Contains(ChosenIndex));
|
|
|
|
|
PreviousIndices.Add(ChosenIndex);
|
|
|
|
|
check(PreviousIndicesQueue->Count() == PreviousIndices.Num());
|
|
|
|
|
}
|
2021-03-15 14:00:26 -04:00
|
|
|
|
|
|
|
|
return ChosenIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FSharedStateRandomGetManager& FSharedStateRandomGetManager::Get()
|
|
|
|
|
{
|
|
|
|
|
static FSharedStateRandomGetManager RGM;
|
|
|
|
|
return RGM;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 23:58:06 -04:00
|
|
|
void FSharedStateRandomGetManager::InitSharedState(uint32 InSharedStateId, int32 InSeed, int32 InNumElements, const TArray<float>& InWeights, int32 InNoRepeatOrder)
|
2021-03-15 14:00:26 -04:00
|
|
|
{
|
|
|
|
|
FScopeLock Lock(&CritSect);
|
|
|
|
|
|
|
|
|
|
if (!RandomGets.Contains(InSharedStateId))
|
|
|
|
|
{
|
2021-03-29 23:58:06 -04:00
|
|
|
RandomGets.Add(InSharedStateId, MakeUnique<FArrayRandomGet>(InSeed, InNumElements, InWeights, InNoRepeatOrder));
|
2021-03-15 14:00:26 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32 FSharedStateRandomGetManager::NextValue(uint32 InSharedStateId)
|
|
|
|
|
{
|
|
|
|
|
FScopeLock Lock(&CritSect);
|
|
|
|
|
TUniquePtr<FArrayRandomGet>* RG = RandomGets.Find(InSharedStateId);
|
|
|
|
|
return (*RG)->NextValue();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FSharedStateRandomGetManager::SetSeed(uint32 InSharedStateId, int32 InSeed)
|
|
|
|
|
{
|
|
|
|
|
FScopeLock Lock(&CritSect);
|
|
|
|
|
TUniquePtr<FArrayRandomGet>* RG = RandomGets.Find(InSharedStateId);
|
|
|
|
|
(*RG)->SetSeed(InSeed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FSharedStateRandomGetManager::SetNoRepeatOrder(uint32 InSharedStateId, int32 InNoRepeatOrder)
|
|
|
|
|
{
|
|
|
|
|
FScopeLock Lock(&CritSect);
|
|
|
|
|
TUniquePtr<FArrayRandomGet>* RG = RandomGets.Find(InSharedStateId);
|
|
|
|
|
(*RG)->SetNoRepeatOrder(InNoRepeatOrder);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FSharedStateRandomGetManager::ResetSeed(uint32 InSharedStateId)
|
|
|
|
|
{
|
|
|
|
|
FScopeLock Lock(&CritSect);
|
|
|
|
|
TUniquePtr<FArrayRandomGet>* RG = RandomGets.Find(InSharedStateId);
|
|
|
|
|
(*RG)->ResetSeed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef LOCTEXT_NAMESPACE
|