Files
UnrealEngineUWP/Engine/Source/Editor/SubobjectDataInterface/Private/SubobjectData.cpp
ben hoffman 1eb5090127 Do not allow copying or duplicating subobjects with the name of "DefaultSceneRoot". This will prevent confusing renaming situations where the DefaultSceneRoot could be deleted, but still be preventing other subobjects from being named that
#jira UE-119104
#rb todo
#rnx
#preflight 60ff101a47c93a00010d9d2e

#ROBOMERGE-SOURCE: CL 16961027 in //UE5/Main/...
#ROBOMERGE-BOT: STARSHIP (Main -> Release-Engine-Test) (v838-16927207)

[CL 16961064 by ben hoffman in ue5-release-engine-test branch]
2021-07-26 16:58:12 -04:00

1097 lines
30 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SubobjectData.h"
#include "Engine/Blueprint.h" // Casting to UBlueprint
#include "Components/ChildActorComponent.h"
#include "GameFramework/Actor.h"
#include "Kismet2/ComponentEditorUtils.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "ComponentAssetBroker.h" // FComponentAssetBrokerage
#include "Engine/World.h" // Finding the instance of a component on an actor
#include "Engine/InheritableComponentHandler.h"
#include "Engine/SCS_Node.h" // #TODO_BH We need to remove this when the actual subobject refactor happens
#define LOCTEXT_NAMESPACE "SubobjectData"
FSubobjectData::FSubobjectData()
: WeakObjectPtr(nullptr)
, ParentObjectHandle(FSubobjectDataHandle::InvalidHandle)
, SCSNodePtr(nullptr)
{
// By Default the handle will be invalid, it will only
// be generated if we are given an object.
}
FSubobjectData::FSubobjectData(UObject* ContextObject, const FSubobjectDataHandle& InParentHandle)
: WeakObjectPtr(ContextObject)
, ParentObjectHandle(InParentHandle)
, SCSNodePtr(nullptr)
{
AttemptToSetSCSNode();
}
FSubobjectData::~FSubobjectData()
{
ChildrenHandles.Empty();
WeakObjectPtr = nullptr;
}
bool FSubobjectData::CanEdit() const
{
// Actors are editable by default
if(const AActor* ActorContext = GetObject<AActor>())
{
return true;
}
// If this is an instance-added component, then we can edit it. We know this isn't inherted because it would
// be a FInheritedSubobjectData
else if(const UActorComponent* Component = GetComponentTemplate())
{
if(Component->CreationMethod == EComponentCreationMethod::Instance)
{
return true;
}
}
return false;
}
bool FSubobjectData::CanDelete() const
{
// Components can be deleted if they are not inherited or the scene root
if (const UActorComponent* ComponentTemplate = GetComponentTemplate())
{
return !IsInheritedComponent() && !IsDefaultSceneRoot() && !IsChildActor();
}
// Otherwise, it can't be deleted
return false;
}
bool FSubobjectData::CanDuplicate() const
{
return CanCopy();
}
bool FSubobjectData::CanCopy() const
{
if (IsInstancedInheritedComponent())
{
return FComponentEditorUtils::CanCopyComponent(GetComponentTemplate()) && !SceneRootHasDefaultName();
}
return FComponentEditorUtils::CanCopyComponent(GetComponentTemplate());
}
bool FSubobjectData::CanReparent() const
{
if(IsComponent())
{
if(GetSCSNode() != nullptr)
{
return !IsInstancedInheritedComponent() && !IsInheritedComponent();
}
return !IsInheritedComponent() && !IsDefaultSceneRoot() && IsSceneComponent();
}
return false;
}
bool FSubobjectData::CanRename() const
{
if(!IsValid())
{
return false;
}
// You can rename instance actors in the level editor, but you cannot rename
// the default actors in blueprints.
if(IsActor())
{
return IsInstancedActor() && !IsChildActor();
}
// You can rename within the BP editor, but not if if this subobject
// is on an instance in the level
if(GetSCSNode() != nullptr && !IsInheritedSCSNode())
{
return !IsInstancedInheritedComponent() && !SceneRootHasDefaultName();
}
return !IsInheritedComponent() && !IsDefaultSceneRoot() && !IsChildActorSubtreeObject();
}
const UObject* FSubobjectData::GetObjectForBlueprint(UBlueprint* Blueprint) const
{
const bool bCanEdit = CanEdit();
// We have to deal with the ICH (Inherited Component Handler) for components
if(IsComponent() && bCanEdit && !IsNativeComponent() && IsInheritedSCSNode())
{
UActorComponent* OverriddenComponent = nullptr;
FComponentKey Key(GetSCSNode());
const bool bBlueprintCanOverrideComponentFromKey = Key.IsValid()
&& Blueprint
&& Blueprint->ParentClass
&& Blueprint->ParentClass->IsChildOf(Key.GetComponentOwner());
if (bBlueprintCanOverrideComponentFromKey)
{
const bool bCreateIfNecessary = true;
UInheritableComponentHandler* InheritableComponentHandler = Blueprint->GetInheritableComponentHandler(bCreateIfNecessary);
if (InheritableComponentHandler)
{
OverriddenComponent = InheritableComponentHandler->GetOverridenComponentTemplate(Key);
if (!OverriddenComponent && bCreateIfNecessary)
{
OverriddenComponent = InheritableComponentHandler->CreateOverridenComponentTemplate(Key);
}
}
}
return OverriddenComponent;
}
// If this not a component, then we can simply return the current object as long as it's editable
return bCanEdit ? WeakObjectPtr.Get() : nullptr;
}
bool FSubobjectData::IsInstancedComponent() const
{
// Check the flags on the component (RF_ClassDefaultObject | RF_ArchetypeObject)
const UActorComponent* Component = GetComponentTemplate();
return Component && !Component->IsTemplate();
}
UActorComponent* FSubobjectData::FindMutableComponentInstanceInActor(const AActor* InActor) const
{
const USCS_Node* SCS_Node = GetSCSNode();
const UActorComponent* ComponentTemplate = GetComponentTemplate();
UActorComponent* ComponentInstance = nullptr;
if (InActor)
{
if (SCS_Node)
{
FName VariableName = SCS_Node->GetVariableName();
if (VariableName != NAME_None)
{
UWorld* World = InActor->GetWorld();
FObjectPropertyBase* Property = FindFProperty<FObjectPropertyBase>(InActor->GetClass(), VariableName);
if (Property != nullptr)
{
// Return the component instance that's stored in the property with the given variable name
ComponentInstance = Cast<UActorComponent>(Property->GetObjectPropertyValue_InContainer(InActor));
}
else if (World != nullptr && World->WorldType == EWorldType::EditorPreview)
{
// If this is the preview actor, return the cached component instance that's being used for the preview actor prior to recompiling the Blueprint
ComponentInstance = SCS_Node->EditorComponentInstance.Get();
}
}
}
else if (ComponentTemplate)
{
TInlineComponentArray<UActorComponent*> Components;
InActor->GetComponents(Components);
ComponentInstance = FComponentEditorUtils::FindMatchingComponent(ComponentTemplate, Components);
}
if (!ComponentInstance && SCS_Node)
{
TInlineComponentArray<UActorComponent*> Components;
InActor->GetComponents(Components);
UActorComponent** MatchingArchetype = Components.FindByPredicate([SCS_Node](const UActorComponent* A)
{
return A && A->GetArchetype() == SCS_Node->ComponentTemplate;
});
ComponentInstance = MatchingArchetype ? *MatchingArchetype : nullptr;
}
}
return ComponentInstance;
}
UBlueprint* FSubobjectData::GetBlueprint() const
{
// If this object is a BP, we can just return that
if (UBlueprint* BP = Cast<UBlueprint>(WeakObjectPtr.Get()))
{
return BP;
}
// If it is an actor, then we can get the BP from the UClass
else if (const AActor* DefaultActor = GetObject<AActor>())
{
return UBlueprint::GetBlueprintFromClass(DefaultActor->GetClass());
}
// For components, we need to get the blueprint from their owning actor or the SCS
else if(IsComponent())
{
if (const USCS_Node* SCS_Node = GetSCSNode())
{
if (const USimpleConstructionScript* SCS = SCS_Node->GetSCS())
{
return SCS->GetBlueprint();
}
}
else if(const UActorComponent* ActorComponent = GetComponentTemplate())
{
if (const AActor* Actor = ActorComponent->GetOwner())
{
return UBlueprint::GetBlueprintFromClass(Actor->GetClass());
}
}
}
return nullptr;
}
FString FSubobjectData::GetDisplayString(bool bShowNativeComponentNames /* = true */) const
{
FName VariableName = GetVariableName();
const UActorComponent* ComponentTemplate = GetComponentTemplate();
UBlueprint* Blueprint = GetBlueprint();
UClass* VariableOwner = (Blueprint != nullptr) ? Blueprint->SkeletonGeneratedClass : nullptr;
FProperty* VariableProperty = FindFProperty<FProperty>(VariableOwner, VariableName);
bool const bHasValidVarName = (VariableName != NAME_None);
bool const bIsArrayVariable = bHasValidVarName && (VariableOwner != nullptr) &&
VariableProperty && VariableProperty->IsA<FArrayProperty>();
// Only display SCS node variable names in the tree if they have not been autogenerated
if (ComponentTemplate && bHasValidVarName && !bIsArrayVariable)
{
const bool bIsNative = IsNativeComponent();
const bool bIsInherited = IsInheritedComponent();
const bool bIsInstance = IsInstancedComponent();
const bool bIsBlueprintInstanceInherited = bIsInherited && bIsInstance;
// Inherited/Native components will have "Name (Inherited)" as their display
if ((bIsNative || bIsInherited) && bShowNativeComponentNames)
{
FStringFormatNamedArguments Args;
Args.Add(TEXT("VarName"), VariableProperty && VariableProperty->IsNative() ? VariableProperty->GetDisplayNameText().ToString() : VariableName.ToString());
FString CompName = TEXT(" (") + ComponentTemplate->GetName() + TEXT(")");
Args.Add(TEXT("CompName"), bIsNative ? CompName : TEXT(""));
return FString::Format(TEXT("{VarName}{CompName}"), Args);
}
else
{
return VariableName.ToString();
}
}
else if (ComponentTemplate != nullptr)
{
return ComponentTemplate->GetFName().ToString();
}
else if (const AActor* DefaultActor = GetObject<AActor>())
{
FString Name;
if (Blueprint != nullptr && !IsInstancedActor())
{
Blueprint->GetName(Name);
}
else
{
Name = DefaultActor->GetActorLabel();
}
FStringFormatNamedArguments Args;
Args.Add(TEXT("ActorName"), Name);
return FString::Format(TEXT("{ActorName}"), Args);
}
// If the context is a simple UObject, then we can get it's name
else if (const UObject* Context = GetObject())
{
FString Name;
Context->GetName(Name);
return Name;
}
// Anything else will be unknown!
else
{
FString UnnamedString = LOCTEXT("UnnamedToolTip", "Unnamed").ToString();
FString NativeString = IsNativeComponent() ? LOCTEXT("NativeToolTip", "Native ").ToString() : TEXT("");
if (ComponentTemplate != nullptr)
{
return FString::Printf(TEXT("[%s %s%s]"), *UnnamedString, *NativeString, *ComponentTemplate->GetClass()->GetName());
}
else
{
return FString::Printf(TEXT("[%s %s]"), *UnnamedString, *NativeString);
}
}
}
FText FSubobjectData::GetDragDropDisplayText() const
{
return FText::FromString(GetDisplayString());
}
FText FSubobjectData::GetDisplayNameContextModifiers(bool bShowNativeComponentNames) const
{
if(IsActor())
{
if(IsChildActor())
{
return LOCTEXT("ActorContext_ChildActor", "(Child Actor)");
}
else
{
if (const AActor* DefaultActor = GetObject<AActor>())
{
if (const UBlueprint* Blueprint = UBlueprint::GetBlueprintFromClass(DefaultActor->GetClass()))
{
return LOCTEXT("ActorContext_self", "(Self)");
}
else
{
return LOCTEXT("ActorContext_Instance", "(Instance)");
}
}
}
}
else if(const UActorComponent* Template = GetComponentTemplate())
{
FName VariableName = GetVariableName();
UBlueprint* Blueprint = GetBlueprint();
UClass* VariableOwner = (Blueprint != nullptr) ? Blueprint->SkeletonGeneratedClass : nullptr;
FProperty* VariableProperty = FindFProperty<FProperty>(VariableOwner, VariableName);
bool const bHasValidVarName = (VariableName != NAME_None);
bool const bIsArrayVariable = bHasValidVarName && (VariableOwner != nullptr) && VariableProperty && VariableProperty->IsA<FArrayProperty>();
const bool bIsBlueprintInstanceInherited = GetSCSNode() != nullptr && IsInstancedInheritedComponent();
if(bIsBlueprintInstanceInherited)
{
FStringFormatNamedArguments Args;
Args.Add(TEXT("InheritedText"), TEXT("(Inherited)"));
return FText::FromString(FString::Format(TEXT("{InheritedText}"), Args));
}
// Only display node variable names in the tree if they have not been autogenerated
if (bHasValidVarName && !bIsArrayVariable)
{
const bool bIsNative = IsNativeComponent();
const bool bIsInherited = IsInheritedSCSNode();
if ((bIsNative || bIsInherited || bIsBlueprintInstanceInherited) && bShowNativeComponentNames)
{
FStringFormatNamedArguments Args;
Args.Add(TEXT("InheritedText"), TEXT("(Inherited)"));
return FText::FromString(FString::Format(TEXT("{InheritedText}"), Args));
}
}
}
return FText::GetEmpty();
}
FText FSubobjectData::GetDisplayName() const
{
if (const AActor* DefaultActor = GetObject<AActor>())
{
FString Name;
UBlueprint* Blueprint = UBlueprint::GetBlueprintFromClass(DefaultActor->GetClass());
if (Blueprint != nullptr && !IsInstancedActor())
{
Blueprint->GetName(Name);
}
else
{
Name = DefaultActor->GetActorLabel();
}
return FText::FromString(Name);
}
else if (const UObject* Context = GetObject())
{
FString Name;
Context->GetName(Name);
return FText::FromString(Name);
}
return LOCTEXT("GetDisplayNameNotOverridden", "Unknown Subobject");
}
FName FSubobjectData::GetVariableName() const
{
FName VariableName = NAME_None;
const USCS_Node* SCS_Node = GetSCSNode();
const UActorComponent* ComponentTemplate = GetComponentTemplate();
if (SCS_Node != nullptr)
{
// Use the same variable name as is obtained by the compiler
VariableName = SCS_Node->GetVariableName();
}
else if (ComponentTemplate != nullptr)
{
// Try to find the component anchor variable name (first looks for an exact match then scans for any matching variable that points to the archetype in the CDO)
VariableName = FComponentEditorUtils::FindVariableNameGivenComponentInstance(ComponentTemplate);
}
return VariableName;
}
FText FSubobjectData::GetSocketName() const
{
if (USCS_Node* SCSNode = GetSCSNode())
{
return FText::FromName(SCSNode->AttachToName);
}
return FText::GetEmpty();
}
FName FSubobjectData::GetSocketFName() const
{
if (USCS_Node* SCSNode = GetSCSNode())
{
return SCSNode->AttachToName;
}
return NAME_None;
}
bool FSubobjectData::HasValidSocket() const
{
return GetSCSNode() != nullptr;
}
void FSubobjectData::SetSocketName(FName InNewName)
{
if(USCS_Node* SCS = GetSCSNode())
{
SCS->AttachToName = InNewName;
}
}
void FSubobjectData::SetupAttachment(FName SocketName, const FSubobjectDataHandle& AttachParentHandle)
{
USceneComponent* SceneComponentTemplate = Cast<USceneComponent>(GetMutableComponentTemplate());
FSubobjectData* AttachParentData = AttachParentHandle.GetData();
USceneComponent* AttachParent = AttachParentData ?
Cast<USceneComponent>(AttachParentData->GetMutableComponentTemplate()) :
SceneComponentTemplate->GetAttachParent();
SceneComponentTemplate->SetupAttachment(AttachParent, NAME_None);
if(USCS_Node* SCS_Node = GetSCSNode())
{
SCS_Node->Modify();
SCS_Node->AttachToName = NAME_None;
}
}
FSubobjectDataHandle FSubobjectData::GetRootSubobject() const
{
FSubobjectDataHandle Current = ParentObjectHandle;
while(Current.IsValid() && Current.GetSharedDataPtr()->HasParent())
{
Current = Current.GetSharedDataPtr()->GetParentHandle();
}
return Current;
}
bool FSubobjectData::HasChild(const FSubobjectDataHandle& InChildHandle) const
{
for(const FSubobjectDataHandle& MyChildHandle : ChildrenHandles)
{
if(MyChildHandle == InChildHandle)
{
return true;
}
}
return false;
}
FSubobjectDataHandle FSubobjectData::FindChild(const FSubobjectDataHandle& InChildHandle) const
{
for(const FSubobjectDataHandle& MyChildHandle : ChildrenHandles)
{
if(MyChildHandle == InChildHandle)
{
return MyChildHandle;
}
}
return FSubobjectDataHandle::InvalidHandle;
}
FSubobjectDataHandle FSubobjectData::FindChildByObject(UObject* ContextObject) const
{
if (!ContextObject)
{
return FSubobjectDataHandle::InvalidHandle;
}
for (const FSubobjectDataHandle& CurrentChild : ChildrenHandles)
{
if (FSubobjectData* ChildData = CurrentChild.GetData())
{
if (ChildData->GetObject() == ContextObject)
{
return CurrentChild;
}
}
}
return FSubobjectDataHandle::InvalidHandle;
}
bool FSubobjectData::AddChildHandleOnly(const FSubobjectDataHandle& InHandle)
{
// If we already have this child, then don't both with adding it
if (HasChild(InHandle) || !InHandle.IsValid() || InHandle == Handle)
{
return false;
}
ChildrenHandles.Add(InHandle);
if(FSubobjectData* NewChildData = InHandle.GetData())
{
NewChildData->SetParentHandle(Handle);
}
return true;
}
bool FSubobjectData::RemoveChildHandleOnly(const FSubobjectDataHandle& InHandle)
{
if (HasChild(InHandle))
{
ChildrenHandles.Remove(InHandle);
return true;
}
return false;
}
FText FSubobjectData::GetAssetName() const
{
UActorComponent* Template = GetMutableComponentTemplate();
FText AssetName = LOCTEXT("None", "None");
if (Template)
{
UObject* Asset = FComponentAssetBrokerage::GetAssetFromComponent(Template);
if (Asset != nullptr)
{
AssetName = FText::FromString(Asset->GetName());
}
}
return AssetName;
}
FText FSubobjectData::GetAssetPath() const
{
FText AssetName = LOCTEXT("None", "None");
UActorComponent* Template = GetMutableComponentTemplate();
if (Template)
{
UObject* Asset = FComponentAssetBrokerage::GetAssetFromComponent(Template);
if (Asset != nullptr)
{
AssetName = FText::FromString(Asset->GetPathName());
}
}
return AssetName;
}
bool FSubobjectData::IsAssetVisible() const
{
UActorComponent* Template = GetMutableComponentTemplate();
if (Template && FComponentAssetBrokerage::SupportsAssets(Template))
{
return true;
}
return false;
}
FText FSubobjectData::GetMobilityToolTipText() const
{
FText MobilityToolTip;
if (const USceneComponent* SceneComponentTemplate = Cast<USceneComponent>(GetComponentTemplate()))
{
if (SceneComponentTemplate->Mobility == EComponentMobility::Movable)
{
MobilityToolTip = LOCTEXT("MovableMobilityTooltip", "Movable");
}
else if (SceneComponentTemplate->Mobility == EComponentMobility::Stationary)
{
MobilityToolTip = LOCTEXT("StationaryMobilityTooltip", "Stationary");
}
else if (SceneComponentTemplate->Mobility == EComponentMobility::Static)
{
MobilityToolTip = LOCTEXT("StaticMobilityTooltip", "Static");
}
else
{
// make sure we're the mobility type we're expecting (we've handled Movable & Stationary)
ensureMsgf(false, TEXT("Unhandled mobility type [%d], is this a new type that we don't handle here?"), SceneComponentTemplate->Mobility.GetValue());
MobilityToolTip = LOCTEXT("UnknownMobilityTooltip", "Component with unknown mobility");
}
}
else
{
MobilityToolTip = LOCTEXT("NoMobilityTooltip", "Non-scene component");
}
return MobilityToolTip;
}
FText FSubobjectData::GetComponentEditorOnlyTooltipText() const
{
FText ComponentType = LOCTEXT("ComponentEditorOnlyFalse", "False");
if (IsComponent())
{
if (const UActorComponent* Template = GetComponentTemplate())
{
UBlueprint* Blueprint = GetBlueprint();
FObjectProperty* Prop = Blueprint ? FindFProperty<FObjectProperty>(Blueprint->SkeletonGeneratedClass, GetVariableName()) : nullptr;
if(Template->bIsEditorOnly || (Prop && Prop->HasAnyPropertyFlags(CPF_EditorOnly)))
{
ComponentType = LOCTEXT("ComponentEditorOnlyTrue", "True");
}
}
}
return ComponentType;
}
FText FSubobjectData::GetIntroducedInToolTipText() const
{
FText IntroducedInTooltip = LOCTEXT("IntroducedInThisBPTooltip", "this class");
if (IsInheritedComponent())
{
if (const UActorComponent* ComponentTemplate = GetComponentTemplate())
{
UClass* BestClass = nullptr;
AActor* OwningActor = ComponentTemplate->GetOwner();
if (IsNativeComponent() && (OwningActor != nullptr))
{
for (UClass* TestClass = OwningActor->GetClass(); TestClass != AActor::StaticClass(); TestClass = TestClass->GetSuperClass())
{
if (FindComponentInstanceInActor(Cast<AActor>(TestClass->GetDefaultObject())))
{
BestClass = TestClass;
}
else
{
break;
}
}
}
else if (!IsNativeComponent())
{
USCS_Node* SCSNode = GetSCSNode();
if ((SCSNode == nullptr) && (OwningActor != nullptr))
{
SCSNode = FindSCSNodeForInstance(ComponentTemplate, OwningActor->GetClass());
}
if (SCSNode != nullptr)
{
if (UBlueprint* OwningBP = SCSNode->GetSCS()->GetBlueprint())
{
BestClass = OwningBP->GeneratedClass;
}
}
else if (OwningActor != nullptr)
{
if (UBlueprint* OwningBP = UBlueprint::GetBlueprintFromClass(OwningActor->GetClass()))
{
BestClass = OwningBP->GeneratedClass;
}
}
}
if (BestClass == nullptr)
{
if (ComponentTemplate->IsCreatedByConstructionScript())
{
IntroducedInTooltip = LOCTEXT("IntroducedInUnknownError", "Unknown Blueprint Class (via an Add Component call)");
}
else
{
IntroducedInTooltip = LOCTEXT("IntroducedInNativeError", "Unknown native source (via C++ code)");
}
}
else if (IsInstancedComponent() && ComponentTemplate->CreationMethod == EComponentCreationMethod::Native && !ComponentTemplate->HasAnyFlags(RF_DefaultSubObject))
{
IntroducedInTooltip = FText::Format(LOCTEXT("IntroducedInCPPErrorFmt", "{0} (via C++ code)"), FBlueprintEditorUtils::GetFriendlyClassDisplayName(BestClass));
}
else if (IsInstancedComponent() && ComponentTemplate->CreationMethod == EComponentCreationMethod::UserConstructionScript)
{
IntroducedInTooltip = FText::Format(LOCTEXT("IntroducedInUCSErrorFmt", "{0} (via an Add Component call)"), FBlueprintEditorUtils::GetFriendlyClassDisplayName(BestClass));
}
else
{
IntroducedInTooltip = FBlueprintEditorUtils::GetFriendlyClassDisplayName(BestClass);
}
}
else
{
IntroducedInTooltip = LOCTEXT("IntroducedInNoTemplateError", "[no component template found]");
}
}
else if (IsInstancedComponent())
{
IntroducedInTooltip = LOCTEXT("IntroducedInThisActorInstanceTooltip", "this actor instance");
}
return IntroducedInTooltip;
}
FText FSubobjectData::GetActorDisplayText() const
{
if (const AActor* DefaultActor = GetObject<AActor>())
{
FString Name;
UBlueprint* Blueprint = UBlueprint::GetBlueprintFromClass(DefaultActor->GetClass());
if (Blueprint != nullptr && !IsInstancedActor())
{
Blueprint->GetName(Name);
}
else
{
Name = DefaultActor->GetActorLabel();
}
return FText::FromString(Name);
}
return FText::GetEmpty();
}
FLinearColor FSubobjectData::GetColorTintForIcon() const
{
// A blue-ish tint
static const FLinearColor InheritedBlueprintComponentColor(0.08f, 0.35f, 0.6f);
static const FLinearColor InstancedInheritedBlueprintComponentColor(0.08f, 0.35f, 0.6f);
// A green-ish tint
static const FLinearColor InheritedNativeComponentColor(0.7f, 0.9f, 0.7f);
static const FLinearColor IntroducedHereColor(FLinearColor::White);
if (IsInheritedComponent())
{
// Native C++ components will be tinted green
if (IsNativeComponent())
{
return InheritedNativeComponentColor;
}
else if (IsInstancedComponent())
{
return InstancedInheritedBlueprintComponentColor;
}
else if(IsInheritedSCSNode())
{
return InheritedBlueprintComponentColor;
}
}
// If we have an SCS but are not Inherited, then this is just a regular blueprint and we should be blue
else if(GetSCSNode() != nullptr)
{
// If it's inherited BP color then it should be blue (i.e. this is a BP component that came from a BP generated class)
if(IsInheritedSCSNode() || IsInstancedComponent())
{
return InheritedBlueprintComponentColor;
}
// Otherwise this is just a regular SCS node inside of the BP editor
else
{
return IntroducedHereColor;
}
}
// By default, this node will appear white to represent being introduced here
return IntroducedHereColor;
}
bool FSubobjectData::IsInstancedActor() const
{
if (const AActor* Actor = GetObject<AActor>())
{
return !Actor->IsTemplate();
}
return false;
}
bool FSubobjectData::IsNativeComponent() const
{
if (const UActorComponent* Template = GetComponentTemplate())
{
return Template->CreationMethod == EComponentCreationMethod::Native && GetSCSNode() == nullptr;
}
return false;
}
bool FSubobjectData::IsInheritedComponent() const
{
// This covers a component that is added via blueprints
if(GetSCSNode() != nullptr)
{
return IsInheritedSCSNode();
}
else if (const UActorComponent* ComponentTemplate = GetComponentTemplate())
{
return IsNativeComponent() || ComponentTemplate->CreationMethod != EComponentCreationMethod::Instance;
}
return false;
}
bool FSubobjectData::IsSceneComponent() const
{
return Cast<USceneComponent>(GetComponentTemplate()) != nullptr;
}
bool FSubobjectData::IsRootComponent() const
{
const UActorComponent* ComponentTemplate = GetComponentTemplate();
if(!ComponentTemplate)
{
return false;
}
const AActor* CDO = ComponentTemplate ? ComponentTemplate->GetOwner() : nullptr;
if(CDO && (IsInstancedComponent() || IsInheritedComponent()))
{
return CDO->GetRootComponent() == ComponentTemplate;
}
bool bIsRoot = true;
if(USCS_Node* SCS_Node = GetSCSNode())
{
if(USimpleConstructionScript* SCS = SCS_Node->GetSCS())
{
// Evaluate to TRUE if we have an SCS node reference, it is contained in the SCS root set and does not have an external parent
bIsRoot = SCS->GetRootNodes().Contains(SCS_Node) && SCS_Node->ParentComponentOrVariableName == NAME_None;
}
}
else if(ComponentTemplate && CDO)
{
// Evaluate to TRUE if we have a valid component reference that matches the native root component
bIsRoot = (ComponentTemplate == CDO->GetRootComponent());
}
return bIsRoot;
}
bool FSubobjectData::IsDefaultSceneRoot() const
{
// If this is a scene component and is instanced, then it will have a specific name
const USceneComponent* SceneComponent = Cast<USceneComponent>(GetComponentTemplate());
if (SceneComponent && !SceneComponent->IsTemplate())
{
const AActor* OwningActor = SceneComponent->GetAttachmentRootActor();
return
SceneComponent->GetFName() == USceneComponent::GetDefaultSceneRootVariableName() ||
(OwningActor && SceneComponent == OwningActor->GetRootComponent());
}
// If this isn't a scene component, then we can check an SCS node for a flag.
else if (const USCS_Node* SCS_Node = GetSCSNode())
{
if (USimpleConstructionScript* SCS = SCS_Node->GetSCS())
{
const USceneComponent* SCS_Root = SCS->GetSceneRootComponentTemplate();
return (SCS_Node == SCS->GetDefaultSceneRootNode()) || (SceneComponent && (SCS_Root == SceneComponent));
}
}
// As a last resort check the owning actor to see if the root component matches up with this
// This will be the case for native subobjects
else if(SceneComponent)
{
const AActor* Owner = SceneComponent->GetOwner();
return Owner && Owner->GetRootComponent() == SceneComponent;
}
// Nothing else can be a DefaultSceneRoot
return false;
}
bool FSubobjectData::SceneRootHasDefaultName() const
{
const UActorComponent* Template = GetComponentTemplate();
const FName TemplateName = Template ? Template->GetFName() : NAME_None;
// Only the first default scene root can be attached to an actor, so this will
// rule out false positives of other subobjects that may have been named the same thing
// or if the first default scene root was duplicated
const FSubobjectData* ParentData = GetParentHandle().GetData();
const bool bIsAttachedToActor = ParentData ? ParentData->IsActor() : false;
return bIsAttachedToActor && TemplateName.ToString().StartsWith(USceneComponent::GetDefaultSceneRootVariableName().ToString());
}
bool FSubobjectData::IsComponent() const
{
// Check if we are pointing to a component
return GetComponentTemplate() != nullptr;
}
bool FSubobjectData::IsChildActor() const
{
return false;
}
bool FSubobjectData::IsChildActorSubtreeObject() const
{
const FSubobjectDataHandle& RootActor = GetRootSubobject();
if (const FSubobjectData* RootData = RootActor.GetData())
{
return RootData->IsChildActor();
}
return false;
}
bool FSubobjectData::IsRootActor() const
{
// This is the root actor if it points to an AActor and has no parent
if(const AActor* Actor = GetObject<AActor>())
{
return !ParentObjectHandle.IsValid();
}
return false;
}
bool FSubobjectData::IsActor() const
{
return GetObject<AActor>() != nullptr;
}
bool FSubobjectData::IsInstancedInheritedComponent() const
{
if(!IsComponent())
{
return false;
}
FSubobjectDataHandle CurrentHandle = ParentObjectHandle;
FSubobjectData* CurrentData = CurrentHandle.GetData();
while(CurrentHandle.IsValid() && CurrentData && !CurrentData->IsActor())
{
CurrentHandle = CurrentData->GetParentHandle();
CurrentData = CurrentHandle.GetData();
}
return CurrentData && CurrentData->IsInstancedActor();
}
bool FSubobjectData::IsAttachedTo(const FSubobjectDataHandle& InHandle) const
{
FSubobjectDataHandle TestParentHandle = ParentObjectHandle;
while (TestParentHandle.IsValid())
{
if (TestParentHandle == InHandle)
{
return true;
}
const FSubobjectData* TestParentData = TestParentHandle.GetData();
TestParentHandle = TestParentData ? TestParentData->GetParentHandle() : FSubobjectDataHandle::InvalidHandle;
}
return false;
}
AActor* FSubobjectData::GetMutableActorContext()
{
if(AActor* Actor = GetMutableObject<AActor>())
{
return Actor;
}
else if(UActorComponent* Component = GetMutableComponentTemplate())
{
return Component->GetOwner();
}
else if(UBlueprint* BP = GetBlueprint())
{
return BP->GeneratedClass ? BP->GeneratedClass->GetDefaultObject<AActor>() : nullptr;
}
return nullptr;
}
USCS_Node* FSubobjectData::FindSCSNodeForInstance(const UActorComponent* InstanceComponent, UClass* ClassToSearch)
{
if ((ClassToSearch != nullptr) && InstanceComponent->IsCreatedByConstructionScript())
{
for (UClass* TestClass = ClassToSearch; TestClass->ClassGeneratedBy != nullptr; TestClass = TestClass->GetSuperClass())
{
if (UBlueprint* TestBP = Cast<UBlueprint>(TestClass->ClassGeneratedBy))
{
if (TestBP->SimpleConstructionScript != nullptr)
{
if (USCS_Node* Result = TestBP->SimpleConstructionScript->FindSCSNode(InstanceComponent->GetFName()))
{
return Result;
}
}
}
}
}
return nullptr;
}
bool FSubobjectData::AttemptToSetSCSNode()
{
if (USCS_Node* PossibleSCS = Cast<USCS_Node>(WeakObjectPtr.Get()))
{
WeakObjectPtr = PossibleSCS->ComponentTemplate;
SCSNodePtr = PossibleSCS;
return true;
}
// If this is an instanced component, then we can find it's SCS node
else if (IsInstancedComponent())
{
const UActorComponent* Template = GetComponentTemplate();
if (Template->GetOwner())
{
SCSNodePtr = FindSCSNodeForInstance(Template, Template->GetOwner()->GetClass());
if (SCSNodePtr.IsValid())
{
WeakObjectPtr = SCSNodePtr->ComponentTemplate;
return true;
}
}
}
return false;
}
USCS_Node* FSubobjectData::GetSCSNode(bool bEvenIfPendingKill) const
{
// @todo Deprecate everything related to SCS nodes that could possibly be public facing.
return SCSNodePtr.IsValid() ? SCSNodePtr.Get() : Cast<USCS_Node>(WeakObjectPtr.Get(bEvenIfPendingKill));
}
bool FSubobjectData::IsInheritedSCSNode() const
{
return false;
}
#undef LOCTEXT_NAMESPACE