// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Containers/Array.h" #include "Containers/ArrayView.h" #include "Containers/Map.h" #include "Containers/SortedMap.h" #include "EntitySystem/MovieSceneEntityIDs.h" #include "EntitySystem/MovieSceneEntityRange.h" #include "EntitySystem/MovieSceneEntityFactory.h" #include "EntitySystem/MovieSceneEntitySystemTask.h" #include "EntitySystem/MovieSceneComponentRegistry.h" #include "EntitySystem/MovieSceneEntitySystemTypes.h" #include "EntitySystem/MovieSceneComponentAccessors.h" #include "EntitySystem/MovieSceneSequenceInstanceHandle.h" #include "EntitySystem/MovieSceneEntitySystemDirectedGraph.h" #include namespace UE { namespace MovieScene { struct FInstanceRegistry; using FBoundObjectResolver = UObject* (*)(UObject*); template struct TChildEntityInitializer : FChildEntityInitializer { explicit TChildEntityInitializer(TComponentTypeID InParentComponent, TComponentTypeID InChildComponent) : FChildEntityInitializer(InParentComponent, InChildComponent) {} TComponentTypeID GetParentComponent() const { return ParentComponent.ReinterpretCast(); } TComponentTypeID GetChildComponent() const { return ChildComponent.ReinterpretCast(); } TComponentLock> GetParentComponents(const FEntityAllocation* Allocation) const { return Allocation->ReadComponents(GetParentComponent()); } TComponentLock> GetChildComponents(const FEntityAllocation* Allocation) const { return Allocation->WriteComponents(GetChildComponent(), FEntityAllocationWriteContext::NewAllocation()); } }; // Callback must be compatible with form void(const ParentComponentType, ChildComponentType&); template struct TStaticChildEntityInitializer : TChildEntityInitializer { InitializerType Callback; explicit TStaticChildEntityInitializer(TComponentTypeID InParentComponent, TComponentTypeID InChildComponent, InitializerType InCallback) : TChildEntityInitializer(InParentComponent, InChildComponent) , Callback(InCallback) {} virtual void Run(const FEntityRange& ChildRange, const FEntityAllocation* ParentAllocation, TArrayView ParentAllocationOffsets) { TComponentLock> ParentComponents = ParentAllocation->ReadComponents(this->GetParentComponent()); TComponentLock> ChildComponents = ChildRange.Allocation->WriteComponents(this->GetChildComponent(), FEntityAllocationWriteContext::NewAllocation()); for (int32 Index = 0; Index < ChildRange.Num; ++Index) { const int32 ParentIndex = ParentAllocationOffsets[Index]; const int32 ChildIndex = ChildRange.ComponentStartOffset + Index; Callback(ParentComponents[ParentIndex], ChildComponents[ChildIndex]); } } }; template struct TDuplicateChildEntityInitializer : FChildEntityInitializer { explicit TDuplicateChildEntityInitializer(TComponentTypeID InComponent) : FChildEntityInitializer(InComponent, InComponent) {} TComponentTypeID GetComponent() const { return ParentComponent.ReinterpretCast(); } virtual void Run(const FEntityRange& ChildRange, const FEntityAllocation* ParentAllocation, TArrayView ParentAllocationOffsets) { TComponentLock> ParentComponents = ParentAllocation->ReadComponents(GetComponent()); TComponentLock> ChildComponents = ChildRange.Allocation->WriteComponents(GetComponent(), FEntityAllocationWriteContext::NewAllocation()); TArrayView ChildComponentSlice = ChildComponents.Slice(ChildRange.ComponentStartOffset, ChildRange.Num); for (int32 Index = 0; Index < ParentAllocationOffsets.Num(); ++Index) { ChildComponentSlice[Index] = ParentComponents[ParentAllocationOffsets[Index]]; } } }; /* Duplicates child components, but only if the parent entity passes the given component mask*/ template struct TConditionalDuplicateChildEntityInitializer : TDuplicateChildEntityInitializer { explicit TConditionalDuplicateChildEntityInitializer(TComponentTypeID InComponent, FComponentMask InParentComponentMask) : TDuplicateChildEntityInitializer(InComponent) , ParentComponentMask(InParentComponentMask) {} virtual bool IsRelevant(const FComponentMask& InParentType, const FComponentMask& InChildType) const override { return TDuplicateChildEntityInitializer::IsRelevant(InParentType, InChildType) && InParentType.ContainsAll(ParentComponentMask); } FComponentMask ParentComponentMask; }; struct FObjectFactoryBatch : FChildEntityFactory { void Add(int32 EntityIndex, UObject* BoundObject); virtual void GenerateDerivedType(FComponentMask& OutNewEntityType) override; virtual void InitializeAllocation(UMovieSceneEntitySystemLinker* Linker, const FComponentMask& ParentType, const FComponentMask& ChildType, const FEntityAllocation* ParentAllocation, TArrayView ParentAllocationOffsets, const FEntityRange& InChildEntityRange) override; virtual void PostInitialize(UMovieSceneEntitySystemLinker* InLinker) override; TMap, FMovieSceneEntityID>* StaleEntitiesToPreserve; private: TSortedMap PreservedEntities; TArray ObjectsToAssign; }; struct FBoundObjectTask { FBoundObjectTask(UMovieSceneEntitySystemLinker* InLinker); void Apply(); void ForEachAllocation(FEntityAllocationProxy AllocationProxy, FReadEntityIDs EntityIDs, TRead Instances, TRead ObjectBindings, TReadOptional Resolvers); void PostTask(); private: struct FEntityMutationData { FMovieSceneEntityID EntityID; FComponentTypeID ComponentTypeID; bool bAddComponent; }; TMap, FMovieSceneEntityID> StaleEntitiesToPreserve; TMap Batches; TArray EntitiesToDiscard; TArray EntityMutations; protected: UMovieSceneEntitySystemLinker* Linker; }; template inline void FEntityFactories::DuplicateChildComponent(TComponentTypeID InComponent) { DefineChildComponent(TDuplicateChildEntityInitializer(InComponent)); } template inline void FEntityFactories::ConditionallyDuplicateChildComponent(TComponentTypeID InComponent, FComponentMask InParentComponentMask) { DefineChildComponent(TConditionalDuplicateChildEntityInitializer(InComponent, InParentComponentMask)); } template inline void FEntityFactories::DefineChildComponent(TComponentTypeID InParentType, TComponentTypeID InChildType, InitializerCallback&& InInitializer) { using FInitializer = TStaticChildEntityInitializer; DefineChildComponent(InParentType, InChildType); ChildInitializers.Add(FInitializer(InParentType, InChildType, Forward(InInitializer))); } template FComponentTypeInfo FComponentRegistry::MakeComponentTypeInfoWithoutComponentOps(const TCHAR* const DebugName, const FNewComponentTypeParams& Params) { static const uint32 ComponentTypeSize = sizeof(T); static_assert(ComponentTypeSize < TNumericLimits::Max(), "Type too large to be used as component data"); static const uint32 Alignment = alignof(T); static_assert(Alignment < TNumericLimits::Max(), "Type alignment too large to be used as component data"); FComponentTypeInfo NewTypeInfo; NewTypeInfo.Sizeof = ComponentTypeSize; NewTypeInfo.Alignment = Alignment; NewTypeInfo.bIsZeroConstructType = TIsZeroConstructType::Value; NewTypeInfo.bIsTriviallyDestructable = std::is_trivially_destructible_v; NewTypeInfo.bIsTriviallyCopyAssignable = TIsTriviallyCopyAssignable::Value; NewTypeInfo.bIsPreserved = EnumHasAnyFlags(Params.Flags, EComponentTypeFlags::Preserved); NewTypeInfo.bIsCopiedToOutput = EnumHasAnyFlags(Params.Flags, EComponentTypeFlags::CopyToOutput); NewTypeInfo.bIsMigratedToOutput = EnumHasAnyFlags(Params.Flags, EComponentTypeFlags::MigrateToOutput); NewTypeInfo.bHasReferencedObjects = false; #if UE_MOVIESCENE_ENTITY_DEBUG NewTypeInfo.DebugInfo = MakeUnique>(); NewTypeInfo.DebugInfo->DebugName = DebugName; NewTypeInfo.DebugInfo->DebugTypeName = GetGeneratedTypeName(); #endif return NewTypeInfo; } template TComponentTypeID FComponentRegistry::NewComponentType(const TCHAR* const DebugName, const FNewComponentTypeParams& Params) { FComponentTypeInfo NewTypeInfo = FComponentRegistry::MakeComponentTypeInfoWithoutComponentOps(DebugName, Params); NewTypeInfo.bHasReferencedObjects = Params.ReferenceCollectionCallback != nullptr || THasAddReferencedObjectForComponent::Value; if (Params.ReferenceCollectionCallback) { NewTypeInfo.MakeComplexComponentOps(Params.ReferenceCollectionCallback); } else if (!NewTypeInfo.bIsZeroConstructType || !NewTypeInfo.bIsTriviallyDestructable || !NewTypeInfo.bIsTriviallyCopyAssignable || NewTypeInfo.bHasReferencedObjects) { NewTypeInfo.MakeComplexComponentOps(); } FComponentTypeID NewTypeID = NewComponentTypeInternal(MoveTemp(NewTypeInfo)); TComponentTypeID TypedTypeID = NewTypeID.ReinterpretCast(); if (EnumHasAnyFlags(Params.Flags, EComponentTypeFlags::CopyToChildren)) { Factories.DefineChildComponent(TDuplicateChildEntityInitializer(TypedTypeID)); } return TypedTypeID; } template TComponentTypeID FComponentRegistry::NewComponentTypeNoAddReferencedObjects(const TCHAR* const DebugName, const FNewComponentTypeParams& Params) { FComponentTypeInfo NewTypeInfo = FComponentRegistry::MakeComponentTypeInfoWithoutComponentOps(DebugName, Params); NewTypeInfo.bHasReferencedObjects = false; if (!NewTypeInfo.bIsZeroConstructType || !NewTypeInfo.bIsTriviallyDestructable || !NewTypeInfo.bIsTriviallyCopyAssignable) { NewTypeInfo.MakeComplexComponentOpsNoAddReferencedObjects(); } FComponentTypeID NewTypeID = NewComponentTypeInternal(MoveTemp(NewTypeInfo)); TComponentTypeID TypedTypeID = NewTypeID.ReinterpretCast(); if (EnumHasAnyFlags(Params.Flags, EComponentTypeFlags::CopyToChildren)) { Factories.DefineChildComponent(TDuplicateChildEntityInitializer(TypedTypeID)); } return TypedTypeID; } } // using namespace MovieScene } // using namespace UE