Files
UnrealEngine/Engine/Source/Runtime/MovieScene/Public/EntitySystem/MovieSceneMutualComponentInclusivity.h
2025-05-18 13:04:45 +08:00

307 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreTypes.h"
#include "Containers/Array.h"
#include "EntitySystem/MovieSceneEntityIDs.h"
#include "EntitySystem/MovieSceneEntitySystemTypes.h"
#include "EntitySystem/MovieSceneMutualComponentInitializer.h"
#include "EntitySystem/MovieSceneEntitySystemDirectedGraph.h"
#include <initializer_list>
namespace UE::MovieScene
{
struct FMutualInclusivityGraph;
struct IMutualComponentInitializer;
/** Enumeration specifying how to apply a complex filter for mutual inclusion */
enum class EComplexInclusivityFilterMode
{
/** Specifies that all the components must be present for a mutal component to be included */
AllOf,
/** Specifies that the mutal component should be included if any of the specified components are present */
AnyOf
};
struct FMutuallyInclusiveComponentParams
{
FMutuallyInclusiveComponentParams()
{}
/** Custom initializer for this included component */
TUniquePtr<IMutualComponentInitializer> CustomInitializer;
/** Optional by default because generally we do not want mutual components on Imported entities */
EMutuallyInclusiveComponentType Type = EMutuallyInclusiveComponentType::Optional;
};
/** Filter used for specifying more complex mutual inclusivity rules that depend on more than one component type */
struct FComplexInclusivityFilter
{
/** The component mask defining the set of components to test for */
FComponentMask Mask;
/** The filter mode used for matching */
EComplexInclusivityFilterMode Mode;
FComplexInclusivityFilter(const FComponentMask& InMask, EComplexInclusivityFilterMode InMode)
: Mask(InMask), Mode(InMode)
{}
friend uint32 GetTypeHash(const FComplexInclusivityFilter& Filter)
{
return HashCombine(GetTypeHash(Filter.Mask), GetTypeHash(Filter.Mode));
}
friend bool operator==(const FComplexInclusivityFilter& A, const FComplexInclusivityFilter& B)
{
return A.Mask == B.Mask && A.Mode == B.Mode;
}
static FComplexInclusivityFilter All(std::initializer_list<FComponentTypeID> InComponentTypes)
{
return FComplexInclusivityFilter(FComponentMask(InComponentTypes), EComplexInclusivityFilterMode::AllOf);
}
static FComplexInclusivityFilter Any(std::initializer_list<FComponentTypeID> InComponentTypes)
{
return FComplexInclusivityFilter(FComponentMask(InComponentTypes), EComplexInclusivityFilterMode::AnyOf);
}
UE_DEPRECATED(5.2, "This function will no longer be called")
bool Match(FComponentMask Input) const
{
switch (Mode)
{
case EComplexInclusivityFilterMode::AllOf:
{
FComponentMask Temp = Mask;
Temp.CombineWithBitwiseAND(Input, EBitwiseOperatorFlags::MaintainSize);
return Temp == Mask;
}
break;
case EComplexInclusivityFilterMode::AnyOf:
{
FComponentMask Temp = Mask;
Temp.CombineWithBitwiseAND(Input, EBitwiseOperatorFlags::MaintainSize);
return Temp.Find(true) != INDEX_NONE;
}
break;
default:
checkf(false, TEXT("Not implemented"));
return false;
}
}
};
/**
* Container for keeping track of which initializers need to be run for a given call to
* FMutualInclusivityGraphCommandBuffer::ComputeMutuallyInclusiveComponents
*/
struct FMutualComponentInitializers
{
/**
* Run the mutual initializers over the specified entity range
*/
MOVIESCENE_API void Execute(const FEntityRange& Range, const FEntityAllocationWriteContext& WriteContext) const;
/**
* Add a new initializer to this cache
*/
void Add(IMutualComponentInitializer* InitializerToAdd);
/**
* Reset this container. Will not free memory.
*/
void Reset();
private:
TArray<IMutualComponentInitializer*, TInlineAllocator<2>> Initializers;
};
/**
* Command buffer used for computing mutually inclusive components based on a complex set of rules and dependencies
*/
struct FMutualInclusivityGraphCommandBuffer
{
/**
* Populate a component mask with all the necessary mutually inclusive components given an input type.
* Mutual components that already existed in InMask will not be added or counted.
* Can be used to populate a new mask with only the mutual components, or to add the components to an existing mask.
*
* @param InMask The input component type. Rules that are relevant to this type (directly or indirectly) will be applied to the output.
* @param TypesToCompute Allows computation of mandatory, optional or all rules
* @param OutMask Component mask to receieve the result. May point to the same mask as InMask in order to add the mutual components directly.
* @param OutInitializers Array to receieve initializers that must be called on the resulting entity range(s)
* @return The number of new components added to OutMask. Only components that did not exist before are considered.
*/
int32 ComputeMutuallyInclusiveComponents(EMutuallyInclusiveComponentType TypesToCompute, const FComponentMask& InMask, FComponentMask& OutMask, FMutualComponentInitializers& OutInitializers) const;
/**
* Check this buffer's invariants are satisfied
*/
void CheckInvariants() const;
private:
// Only FMutualInclusivityGraph can populate this command buffer
friend FMutualInclusivityGraph;
enum class ECommandType
{
Simple,
Type,
MatchAny,
MatchAll,
ShortCircuit,
Include,
Initialize,
};
struct FSimpleCommand
{
FComponentTypeID MatchID;
FComponentTypeID IncludeID;
};
struct FTypeCommand
{
/** Array index within FMutualInclusivityGraphCommandBuffer::Commands to jump to if this command fails */
uint16 ShortCircuitIndex;
/** The type to match */
EMutuallyInclusiveComponentType Type;
};
struct FMatchCommand
{
/** The ID of the component we are to match against, or include (depending on the value of CommandType) */
FComponentTypeID ComponentTypeID;
/** Array index within FMutualInclusivityGraphCommandBuffer::Commands to jump to if this command fails */
uint16 ShortCircuitIndex;
};
struct FShortCircuitCommand
{
/** Array index within FMutualInclusivityGraphCommandBuffer::Commands to jump to */
uint16 ShortCircuitIndex;
};
struct FIncludeCommand
{
/** The ID of the component we are to match against, or include (depending on the value of CommandType) */
FComponentTypeID ComponentTypeID;
};
struct FInitializeCommand
{
/** Pointer to an initializer to use */
IMutualComponentInitializer* Initializer;
};
/** A single command within a command buffer (FMutualInclusivityGraphCommandBuffer) that either matches or includes a component type ID */
struct FCommand
{
FCommand(FSimpleCommand InSimple) : Simple(InSimple), CommandType(ECommandType::Simple) {}
FCommand(FTypeCommand InType) : Type(InType), CommandType(ECommandType::Type) {}
FCommand(FMatchCommand InMatch, EComplexInclusivityFilterMode Mode) : Match(InMatch), CommandType(Mode == EComplexInclusivityFilterMode::AllOf ? ECommandType::MatchAll : ECommandType::MatchAny) {}
FCommand(FShortCircuitCommand InShortCircuit) : ShortCircuit(InShortCircuit), CommandType(ECommandType::ShortCircuit) {}
FCommand(FIncludeCommand InInclude) : Include(InInclude), CommandType(ECommandType::Include) {}
FCommand(FInitializeCommand InInitialize) : Initialize(InInitialize), CommandType(ECommandType::Initialize) {}
union
{
FTypeCommand Type;
FSimpleCommand Simple;
FMatchCommand Match;
FShortCircuitCommand ShortCircuit;
FIncludeCommand Include;
FInitializeCommand Initialize;
};
/** The type of the command: either a match or include */
ECommandType CommandType;
};
/** Ordered command buffer specifying matches and includes */
TArray<FCommand> Commands;
};
/**
* A mutual inclusion graph for adding components to an entity based on the presence of other components
*/
struct FMutualInclusivityGraph
{
/**
* Define a new rule specifying that all of Dependents should always exist if Predicate exists
*/
void DefineMutualInclusionRule(FComponentTypeID Predicate, std::initializer_list<FComponentTypeID> Dependents);
void DefineMutualInclusionRule(FComponentTypeID Predicate, std::initializer_list<FComponentTypeID> Dependents, FMutuallyInclusiveComponentParams&& InParams);
/**
* Define a new complex rule specifying that all of Dependents should exist if InFilter is matched
*/
void DefineComplexInclusionRule(const FComplexInclusivityFilter& InFilter, std::initializer_list<FComponentTypeID> Dependents);
void DefineComplexInclusionRule(const FComplexInclusivityFilter& InFilter, std::initializer_list<FComponentTypeID> Dependents, FMutuallyInclusiveComponentParams&& InParams);
public:
/**
* Populate a component mask with all the necessary mutually inclusive components given an input type.
* Mutual components that already existed in InMask will not be added or counted.
* Can be used to populate a new mask with only the mutual components, or to add the components to an existing mask.
*
* @param InMask The input component type. Rules that are relevant to this type (directly or indirectly) will be applied to the output.
* @param OutMask Component mask to receieve the result. May point to the same mask as InMask in order to add the mutual components directly.
* @param OutInitializers Initializers that must be run for any new entities created from OutMask
* @return The number of new components added to OutMask. Only components that did not exist before are considered.
*/
int32 ComputeMutuallyInclusiveComponents(EMutuallyInclusiveComponentType TypesToCompute, const FComponentMask& InMask, FComponentMask& OutMask, FMutualComponentInitializers& OutInitializers) const;
private:
/**
* Follows the longest chain in the graph starting from Component
*/
static int32 FindPrerequisiteChainLength(const FDirectedGraph& Graph, FComponentTypeID Component, TMap<FComponentTypeID, int32>& InOutCache);
/**
* Reconstructs the command buffer
*/
void ReconstructCommandBuffer() const;
private:
struct FIncludes
{
void Add(FComponentTypeID Component)
{
Includes.AddUnique(Component);
}
TArray<FComponentTypeID, TInlineAllocator<1>> Includes;
TArray<TUniquePtr<IMutualComponentInitializer>> Initializers;
};
struct FIncludePair
{
FIncludes MandatoryIncludes;
FIncludes OptionalIncludes;
};
/** 1->many simple include relationships */
TMap<FComponentTypeID, FIncludePair> SimpleIncludes;
/** many->many complex include relationships */
TMap<FComplexInclusivityFilter, FIncludePair> ComplexIncludes;
/** Mutable command buffer that is repopulated by ReconstructCommandBuffer */
mutable FMutualInclusivityGraphCommandBuffer CommandBuffer;
/** Flag indicating whether our command buffer needs re-generating or not */
mutable bool bCommandBufferInvalidated = false;
};
} // namespace UE::MovieScene