// Copyright Epic Games, Inc. All Rights Reserved. #include "PreAnimatedState/MovieScenePreAnimatedComponentTransformStorage.h" #include "Evaluation/PreAnimatedState/MovieScenePreAnimatedStorageID.inl" #include "Components/SceneComponent.h" namespace UE { namespace MovieScene { TAutoRegisterPreAnimatedStorageID FPreAnimatedComponentTransformStorage::StorageID; int32 GResetComponentVelocity = 1; static FAutoConsoleVariableRef CVarResetComponentVelocity( TEXT("MovieScene.ResetComponentVelocity"), GResetComponentVelocity, TEXT("If 1 set component velocity to 0 on restore state, otherwise don't do anything (previous behavior)."), ECVF_Default); struct FTemporaryMobilityScope { // Ideally we would not be temporarily changing mobility here, but there are some very specific // edge cases where mobility can be legitimately restored whilst pre-animated transforms are still // maintained. One example is where an attach track has previously been run and since restored - // thus detatching and resetting the transform. If nothing else animates the mobility, this will also // be reset, but the object's global transform may have been captured. FTemporaryMobilityScope(UObject* Object) : SceneComponent(Cast(Object)) , PreviousMobility(SceneComponent ? SceneComponent->Mobility.GetValue() : EComponentMobility::Movable) { if (PreviousMobility != EComponentMobility::Movable) { SceneComponent->SetMobility(EComponentMobility::Movable); } } ~FTemporaryMobilityScope() { if (PreviousMobility != EComponentMobility::Movable) { SceneComponent->SetMobility(PreviousMobility); } // Forcibly reset the component velocity which is set in FIntermediate3DTransform::ApplyTransformTo if (CVarResetComponentVelocity->GetInt() > 0) { SceneComponent->ComponentVelocity = FVector(0.f); } } USceneComponent* SceneComponent; const EComponentMobility::Type PreviousMobility; }; void FComponentTransformPreAnimatedTraits::SetObjectPropertyValue(UObject* InObject, const FCustomPropertyAccessor& BaseCustomAccessor, const FIntermediate3DTransform& CachedTransform) { FTemporaryMobilityScope TemporaryMobilityScope(InObject); const TCustomPropertyAccessor& CustomAccessor = static_cast&>(BaseCustomAccessor); (*CustomAccessor.Functions.Setter)(InObject, CachedTransform); } void FComponentTransformPreAnimatedTraits::SetObjectPropertyValue(UObject* InObject, uint16 PropertyOffset, const FIntermediate3DTransform& CachedTransform) { FTemporaryMobilityScope TemporaryMobilityScope(InObject); StorageType* PropertyAddress = reinterpret_cast( reinterpret_cast(InObject) + PropertyOffset ); *PropertyAddress = CachedTransform; } void FComponentTransformPreAnimatedTraits::SetObjectPropertyValue(UObject* InObject, FTrackInstancePropertyBindings* PropertyBindings, const FIntermediate3DTransform& CachedTransform) { FTemporaryMobilityScope TemporaryMobilityScope(InObject); PropertyBindings->CallFunction(*InObject, CachedTransform); } FPreAnimatedComponentTransformStorage::FPreAnimatedComponentTransformStorage() : TPreAnimatedPropertyStorage(FBuiltInComponentTypes::Get()->PropertyRegistry.GetDefinition(FMovieSceneTracksComponentTypes::Get()->ComponentTransform.CompositeID)) {} void FPreAnimatedComponentTransformStorage::CachePreAnimatedTransforms(const FCachePreAnimatedValueParams& Params, TArrayView BoundObjects, TOptional> Predicate) { for (int32 Index = 0; Index < BoundObjects.Num(); ++Index) { UObject* BoundObject = BoundObjects[Index]; if (BoundObject && (!Predicate || (*Predicate)(Index))) { CachePreAnimatedTransform(Params, BoundObject); } } } void FPreAnimatedComponentTransformStorage::CachePreAnimatedTransform(const FCachePreAnimatedValueParams& Params, UObject* BoundObject) { check(BoundObject); static FMovieScenePropertyBinding PropertyBinding("Transform", TEXT("Transform")); TOptional Property = FPropertyRegistry::ResolveProperty(BoundObject, PropertyBinding, CustomAccessors); if (!Property) { return; } TTuple Key{ BoundObject, PropertyBinding.PropertyPath }; FPreAnimatedStorageGroupHandle GroupHandle = this->Traits.MakeGroup(BoundObject); FPreAnimatedStorageIndex StorageIndex = this->GetOrCreateStorageIndex(Key); FPreAnimatedStateEntry Entry{ GroupHandle, FPreAnimatedStateCachedValueHandle{ StorageID, StorageIndex } }; ParentExtension->EnsureMetaData(Entry); EPreAnimatedStorageRequirement StorageRequirement = ParentExtension->GetStorageRequirement(Entry); if (!this->IsStorageRequirementSatisfied(StorageIndex, StorageRequirement)) { StorageType NewValue; if (const uint16* Fast = Property->TryGet()) { NewValue.Binding.template Set(*Fast); FComponentTransformPropertyTraits::GetObjectPropertyValue(BoundObject, *Fast, NewValue.Data); } else if (const FCustomPropertyIndex* CustomIndex = Property->TryGet()) { const FCustomPropertyAccessor& Accessor = CustomAccessors[CustomIndex->Value]; NewValue.Binding.template Set(&Accessor); FComponentTransformPropertyTraits::GetObjectPropertyValue(BoundObject, Accessor, NewValue.Data); } else { const TSharedPtr& Bindings = Property->Get>(); NewValue.Binding.template Set>(Bindings); FComponentTransformPropertyTraits::GetObjectPropertyValue(BoundObject, Bindings.Get(), NewValue.Data); } this->AssignPreAnimatedValue(StorageIndex, StorageRequirement, MoveTemp(NewValue)); } if (Params.bForcePersist) { this->ForciblyPersistStorage(StorageIndex); } } } // namespace MovieScene } // namespace UE