// Copyright Epic Games, Inc. All Rights Reserved. #include "Systems/MovieSceneComponentMaterialSystem.h" #include "Evaluation/PreAnimatedState/MovieScenePreAnimatedStorageID.inl" #include "EntitySystem/MovieSceneEntityMutations.h" #include "Evaluation/PreAnimatedState/MovieScenePreAnimatedStorageID.inl" #include "Systems/FloatChannelEvaluatorSystem.h" #include "Systems/MovieSceneHierarchicalBiasSystem.h" #include "Systems/MovieScenePiecewiseDoubleBlenderSystem.h" #include "Materials/MaterialInstanceDynamic.h" #include "Tracks/MovieSceneMaterialTrack.h" #include "Components/PrimitiveComponent.h" #include "Components/DecalComponent.h" #include "Components/MeshComponent.h" #include "Components/VolumetricCloudComponent.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(MovieSceneComponentMaterialSystem) namespace UE::MovieScene { FComponentMaterialAccessor::FComponentMaterialAccessor(const FComponentMaterialKey& InKey) : Object(InKey.Object.ResolveObjectPtr()) , MaterialInfo(InKey.MaterialInfo) {} FComponentMaterialAccessor::FComponentMaterialAccessor(UObject* InObject, const FComponentMaterialInfo& InMaterialInfo) : Object(InObject) , MaterialInfo(InMaterialInfo) {} FComponentMaterialAccessor::operator bool() const { return Object != nullptr; } FString FComponentMaterialAccessor::ToString() const { return FString::Printf(TEXT("object %s (material info: %s)"), *Object->GetPathName(), *MaterialInfo.ToString()); } UMaterialInterface* FComponentMaterialAccessor::GetMaterial() const { switch (MaterialInfo.MaterialType) { case EComponentMaterialType::Empty: break; case EComponentMaterialType::IndexedMaterial: if (UPrimitiveComponent* Component = Cast(Object)) { UMaterialInterface* Material = nullptr; if (!MaterialInfo.MaterialSlotName.IsNone()) { Material = Component->GetMaterialByName(MaterialInfo.MaterialSlotName); } if (!Material) { Material = Component->GetMaterial(MaterialInfo.MaterialSlotIndex); } return Material; } break; case EComponentMaterialType::OverlayMaterial: if (UMeshComponent* Component = Cast(Object)) { return Component->GetOverlayMaterial(); } break; case EComponentMaterialType::DecalMaterial: if (UDecalComponent* DecalComponent = Cast(Object)) { return DecalComponent->GetDecalMaterial(); } break; case EComponentMaterialType::VolumetricCloudMaterial: if (UVolumetricCloudComponent* CloudComponent = Cast(Object)) { return CloudComponent->GetMaterial(); } break; default: break; } return nullptr; } void FComponentMaterialAccessor::SetMaterial(UMaterialInterface* InMaterial) const { switch (MaterialInfo.MaterialType) { case EComponentMaterialType::Empty: break; case EComponentMaterialType::IndexedMaterial: if (UPrimitiveComponent* Component = Cast(Object)) { TArray MaterialSlots = Component->GetMaterialSlotNames(); if (!MaterialInfo.MaterialSlotName.IsNone() && MaterialSlots.Contains(MaterialInfo.MaterialSlotName)) { Component->SetMaterialByName(MaterialInfo.MaterialSlotName, InMaterial); } else { Component->SetMaterial(MaterialInfo.MaterialSlotIndex, InMaterial); } } break; case EComponentMaterialType::OverlayMaterial: if (UMeshComponent* Component = Cast(Object)) { Component->SetOverlayMaterial(InMaterial); } break; case EComponentMaterialType::DecalMaterial: if (UDecalComponent* DecalComponent = Cast(Object)) { DecalComponent->SetDecalMaterial(InMaterial); } break; case EComponentMaterialType::VolumetricCloudMaterial: if (UVolumetricCloudComponent* CloudComponent = Cast(Object)) { CloudComponent->SetMaterial(InMaterial); } break; default: break; } } UMaterialInstanceDynamic* FComponentMaterialAccessor::CreateDynamicMaterial(UMaterialInterface* InMaterial) { auto MakeDynamicMaterial = [this, InMaterial]() { TStringBuilder<128> DynamicName; InMaterial->GetFName().ToString(DynamicName); DynamicName.Append(TEXT("_Animated")); FName UniqueDynamicName = MakeUniqueObjectName(Object, UMaterialInstanceDynamic::StaticClass(), DynamicName.ToString()); return UMaterialInstanceDynamic::Create(InMaterial, Object, UniqueDynamicName); }; // Need to create a new MID, either because the parent has changed, or because one doesn't already exist switch (MaterialInfo.MaterialType) { case EComponentMaterialType::Empty: break; case EComponentMaterialType::IndexedMaterial: if (UPrimitiveComponent* Component = Cast(Object)) { TArray MaterialSlots = Component->GetMaterialSlotNames(); UMaterialInstanceDynamic* Result = MakeDynamicMaterial(); if (!MaterialInfo.MaterialSlotName.IsNone() && MaterialSlots.Contains(MaterialInfo.MaterialSlotName)) { Component->SetMaterialByName(MaterialInfo.MaterialSlotName, Result); } else { Component->SetMaterial(MaterialInfo.MaterialSlotIndex, Result); } return Result; } break; case EComponentMaterialType::OverlayMaterial: if (UMeshComponent* Component = Cast(Object)) { UMaterialInstanceDynamic* Result = MakeDynamicMaterial(); Component->SetOverlayMaterial(Result); return Result; } break; case EComponentMaterialType::DecalMaterial: if (UDecalComponent* DecalComponent = Cast(Object)) { return DecalComponent->CreateDynamicMaterialInstance(); } break; case EComponentMaterialType::VolumetricCloudMaterial: if (UVolumetricCloudComponent* CloudComponent = Cast(Object)) { UMaterialInstanceDynamic* Result = MakeDynamicMaterial(); CloudComponent->SetMaterial(Result); return Result; } break; default: break; } return nullptr; } TAutoRegisterPreAnimatedStorageID FPreAnimatedComponentMaterialSwitcherStorage::StorageID; TAutoRegisterPreAnimatedStorageID FPreAnimatedComponentMaterialParameterStorage::StorageID; } // namespace UE::MovieScene UMovieSceneComponentMaterialSystem::UMovieSceneComponentMaterialSystem(const FObjectInitializer& ObjInit) : Super(ObjInit) { using namespace UE::MovieScene; FBuiltInComponentTypes* BuiltInComponents = FBuiltInComponentTypes::Get(); FMovieSceneTracksComponentTypes* TracksComponents = FMovieSceneTracksComponentTypes::Get(); RelevantComponent = TracksComponents->ComponentMaterialInfo; Phase = ESystemPhase::Instantiation; if (HasAnyFlags(RF_ClassDefaultObject)) { DefineComponentConsumer(GetClass(), BuiltInComponents->ObjectResult); DefineComponentConsumer(GetClass(), BuiltInComponents->BoundObject); DefineComponentProducer(GetClass(), TracksComponents->BoundMaterial); DefineImplicitPrerequisite(UMovieSceneCachePreAnimatedStateSystem::StaticClass(), GetClass()); DefineImplicitPrerequisite(UMovieSceneHierarchicalBiasSystem::StaticClass(), GetClass()); } } void UMovieSceneComponentMaterialSystem::OnLink() { using namespace UE::MovieScene; SystemImpl.MaterialSwitcherStorage = Linker->PreAnimatedState.GetOrCreateStorage(); SystemImpl.MaterialParameterStorage = Linker->PreAnimatedState.GetOrCreateStorage(); SystemImpl.OnLink(Linker, FBuiltInComponentTypes::Get()->BoundObject, FMovieSceneTracksComponentTypes::Get()->ComponentMaterialInfo); } void UMovieSceneComponentMaterialSystem::OnUnlink() { SystemImpl.OnUnlink(Linker); } void UMovieSceneComponentMaterialSystem::OnRun(FSystemTaskPrerequisites& InPrerequisites, FSystemSubsequentTasks& Subsequents) { using namespace UE::MovieScene; SystemImpl.OnRun(Linker, FBuiltInComponentTypes::Get()->BoundObject, FMovieSceneTracksComponentTypes::Get()->ComponentMaterialInfo, InPrerequisites, Subsequents); } void UMovieSceneComponentMaterialSystem::SavePreAnimatedState(const FPreAnimationParameters& InParameters) { using namespace UE::MovieScene; SystemImpl.SavePreAnimatedState(Linker, FBuiltInComponentTypes::Get()->BoundObject, FMovieSceneTracksComponentTypes::Get()->ComponentMaterialInfo, InParameters); }