void UActorRecording::StartRecordingNewComponents(ULevelSequence* CurrentSequence, float CurrentSequenceTime)
{
	if (GetActorToRecord() != nullptr)
	{
		// find the new component(s)
		TInlineComponentArray<USceneComponent*> NewComponents;
		TArray<USceneComponent*> SceneComponents;
		GetSceneComponents(SceneComponents);
		for(USceneComponent* SceneComponent : SceneComponents)
		{
			if(ValidComponent(SceneComponent))
			{
				TWeakObjectPtr<USceneComponent> WeakSceneComponent(SceneComponent);
				int32 FoundIndex = TrackedComponents.Find(WeakSceneComponent);
				if(FoundIndex == INDEX_NONE)
				{
					// new component!
					NewComponents.Add(SceneComponent);
				}
			}
		}

		ProcessNewComponentArray(NewComponents);

		UMovieScene* MovieScene = CurrentSequence->GetMovieScene();
		check(MovieScene);

		FAnimationRecordingSettings ComponentAnimationSettings = AnimationSettings;
		ComponentAnimationSettings.bRemoveRootAnimation = false;
		ComponentAnimationSettings.bRecordInWorldSpace = false;

		const USequenceRecorderSettings* Settings = GetDefault<USequenceRecorderSettings>();
		if (!bRecordToPossessable)
		{
			FMovieSceneSpawnable* Spawnable = MovieScene->FindSpawnable(Guid);
			check(Spawnable);

			AActor* ObjectTemplate = CastChecked<AActor>(Spawnable->GetObjectTemplate());

			for (USceneComponent* SceneComponent : NewComponents)
			{
				// new component, so we need to add this to our BP if it didn't come from SCS
				FName NewName;
				if (SceneComponent->CreationMethod != EComponentCreationMethod::SimpleConstructionScript)
				{
					// Give this component a unique name within its parent
					NewName = *FString::Printf(TEXT("Dynamic%s"), *SceneComponent->GetFName().GetPlainNameString());
					NewName.SetNumber(1);
					while (FindObjectFast<UObject>(ObjectTemplate, NewName))
					{
						NewName.SetNumber(NewName.GetNumber() + 1);
					}

					USceneComponent* TemplateRoot = ObjectTemplate->GetRootComponent();
					USceneComponent* AttachToComponent = nullptr;

					// look for a similar attach parent in the current structure
					USceneComponent* AttachParent = SceneComponent->GetAttachParent();
					if(AttachParent != nullptr)
					{
						// First off, check if we're attached to a component that has already been duplicated into this object
						// If so, the name lookup will fail, so we use a direct reference
						if (TWeakObjectPtr<USceneComponent>* DuplicatedComponent = DuplicatedDynamicComponents.Find(AttachParent))
						{
							AttachToComponent = DuplicatedComponent->Get();
						}
					
						// If we don't have an attachment parent duplicated already, perform a name lookup
						if (!AttachToComponent)
						{
							FName AttachName = SceneComponent->GetAttachParent()->GetFName();

							TInlineComponentArray<USceneComponent*> AllChildren;
							ObjectTemplate->GetComponents(AllChildren);

							for (USceneComponent* Child : AllChildren)
							{
								CA_SUPPRESS(28182); // Dereferencing NULL pointer. 'Child' contains the same NULL value as 'AttachToComponent' did.
								if (Child->GetFName() == AttachName)
								{
									AttachToComponent = Child;
									break;
								}
							}
						}
					}

					if (!AttachToComponent)
					{
						AttachToComponent = ObjectTemplate->GetRootComponent();
					}

					USceneComponent* NewTemplateComponent = Cast<USceneComponent>(StaticDuplicateObject(SceneComponent, ObjectTemplate, NewName, RF_AllFlags & ~RF_Transient));
					NewTemplateComponent->AttachToComponent(AttachToComponent, FAttachmentTransformRules::KeepRelativeTransform, SceneComponent->GetAttachSocketName());

					ObjectTemplate->AddInstanceComponent(NewTemplateComponent);

					DuplicatedDynamicComponents.Add(SceneComponent, NewTemplateComponent);
				}
				else
				{
					NewName = SceneComponent->GetFName();
				}

				StartRecordingComponentProperties(NewName, SceneComponent, GetActorToRecord(), CurrentSequence, CurrentSequenceTime, ComponentAnimationSettings);

				bNewComponentAddedWhileRecording = true;
			}

			SyncTrackedComponents();
		}
		else
		{
			for (USceneComponent* SceneComponent : NewComponents)
			{
				// new component, start recording
				StartRecordingComponentProperties(SceneComponent->GetFName(), SceneComponent, GetActorToRecord(), CurrentSequence, CurrentSequenceTime, ComponentAnimationSettings);
			}

			SyncTrackedComponents();
		}
	}
}
Esempio n. 2
0
bool UPawnAction_Repeat::PushSubAction()
{
	if (ActionToRepeat == NULL)
	{
		Finish(EPawnActionResult::Failed);
		return false;
	}
	else if (RepeatsLeft == 0)
	{
		Finish(EPawnActionResult::Success);
		return true;
	}

	if (RepeatsLeft > 0)
	{
		--RepeatsLeft;
	}

	UPawnAction* ActionCopy = SubActionTriggeringPolicy == EPawnSubActionTriggeringPolicy::CopyBeforeTriggering 
		? Cast<UPawnAction>(StaticDuplicateObject(ActionToRepeat, this, NULL))
		: ActionToRepeat;

	UE_VLOG(GetPawn(), LogPawnAction, Log, TEXT("%s> pushing repeted action %s %s, repeats left: %d")
		, *GetName(), SubActionTriggeringPolicy == EPawnSubActionTriggeringPolicy::CopyBeforeTriggering ? TEXT("copy") : TEXT("instance")
		, *GetNameSafe(ActionCopy), RepeatsLeft);
	check(ActionCopy);
	RecentActionCopy = ActionCopy;
	return PushChildAction(*ActionCopy); 
}
		/**
		* Duplicates the asset
		*/
		void DuplicateAsset()
		{
			if (AssetPackage && CreatedAsset)
			{
				const FString NewObjectName = FString::Printf(TEXT("%s_Copy"), *AssetName);
				const FString NewPackageName = FString::Printf(TEXT("%s/%s"), *GetGamePath(), *NewObjectName);

				// Make sure the referenced object is deselected before duplicating it.
				GEditor->GetSelectedObjects()->Deselect(CreatedAsset);

				// Duplicate the asset
				DuplicatedPackage = CreatePackage(NULL, *NewPackageName);
				DuplicatedAsset = StaticDuplicateObject(CreatedAsset, DuplicatedPackage, *NewObjectName);

				if (DuplicatedAsset)
				{
					DuplicatedAsset->MarkPackageDirty();

					// Notify the asset registry
					FAssetRegistryModule::AssetCreated(DuplicatedAsset);

					TestStats->NumDuplicated++;
					UE_LOG(LogEditorAssetAutomationTests, Display, TEXT("Duplicated asset %s to %s (%s)"), *AssetName, *NewObjectName, *Class->GetName());
				}
				else
				{
					UE_LOG(LogEditorAssetAutomationTests, Error, TEXT("Failed to duplicate asset %s (%s)"), *AssetName, *Class->GetName());
				}
			}
		}
	/**
	 * A helper that will take a blueprint and copy it into a new, temporary 
	 * package (intended for throwaway purposes).
	 * 
	 * @param  BlueprintToClone		The blueprint you wish to duplicate.
	 * @return A new temporary blueprint copy of what was passed in.
	 */
	static UBlueprint* DuplicateBlueprint(UBlueprint const* const BlueprintToClone)
	{
		UPackage* TempPackage = CreateTempPackage(BlueprintToClone->GetName());

		FString TempBlueprintName = MakeUniqueObjectName(TempPackage, UBlueprint::StaticClass(), BlueprintToClone->GetFName()).ToString();
		return Cast<UBlueprint>(StaticDuplicateObject(BlueprintToClone, TempPackage, *TempBlueprintName));
	}
	static void ReplaceStructWithTempDuplicate(
		UUserDefinedStruct* StructureToReinstance, 
		TSet<UBlueprint*>& BlueprintsToRecompile,
		TArray<UUserDefinedStruct*>& ChangedStructs)
	{
		if (StructureToReinstance)
		{
			UUserDefinedStruct* DuplicatedStruct = NULL;
			{
				const FString ReinstancedName = FString::Printf(TEXT("STRUCT_REINST_%s"), *StructureToReinstance->GetName());
				const FName UniqueName = MakeUniqueObjectName(GetTransientPackage(), UUserDefinedStruct::StaticClass(), FName(*ReinstancedName));

				TGuardValue<bool> IsDuplicatingClassForReinstancing(GIsDuplicatingClassForReinstancing, true);
				DuplicatedStruct = (UUserDefinedStruct*)StaticDuplicateObject(StructureToReinstance, GetTransientPackage(), *UniqueName.ToString(), ~RF_Transactional); 
			}

			DuplicatedStruct->Guid = StructureToReinstance->Guid;
			DuplicatedStruct->Bind();
			DuplicatedStruct->StaticLink(true);
			DuplicatedStruct->PrimaryStruct = StructureToReinstance;
			DuplicatedStruct->Status = EUserDefinedStructureStatus::UDSS_Duplicate;
			DuplicatedStruct->SetFlags(RF_Transient);
			DuplicatedStruct->AddToRoot();
			CastChecked<UUserDefinedStructEditorData>(DuplicatedStruct->EditorData)->RecreateDefaultInstance();

			for (auto StructProperty : TObjectRange<UStructProperty>(RF_ClassDefaultObject | RF_PendingKill))
			{
				if (StructProperty && (StructureToReinstance == StructProperty->Struct))
				{
					if (auto OwnerClass = Cast<UBlueprintGeneratedClass>(StructProperty->GetOwnerClass()))
					{
						if (UBlueprint* FoundBlueprint = Cast<UBlueprint>(OwnerClass->ClassGeneratedBy))
						{
							BlueprintsToRecompile.Add(FoundBlueprint);
							StructProperty->Struct = DuplicatedStruct;
						}
					}
					else if (auto OwnerStruct = Cast<UUserDefinedStruct>(StructProperty->GetOwnerStruct()))
					{
						check(OwnerStruct != DuplicatedStruct);
						const bool bValidStruct = (OwnerStruct->GetOutermost() != GetTransientPackage())
							&& !OwnerStruct->HasAnyFlags(RF_PendingKill)
							&& (EUserDefinedStructureStatus::UDSS_Duplicate != OwnerStruct->Status.GetValue());

						if (bValidStruct)
						{
							ChangedStructs.AddUnique(OwnerStruct);
							StructProperty->Struct = DuplicatedStruct;
						}
					}
					else
					{
						UE_LOG(LogK2Compiler, Error, TEXT("ReplaceStructWithTempDuplicate unknown owner"));
					}
				}
			}

			DuplicatedStruct->RemoveFromRoot();
		}
	}
void UAnimCompress_BitwiseCompressOnly::DoReduction(UAnimSequence* AnimSeq, const TArray<FBoneData>& BoneData)
{
#if WITH_EDITORONLY_DATA
	// split the raw data into tracks
	TArray<FTranslationTrack> TranslationData;
	TArray<FRotationTrack> RotationData;
	TArray<FScaleTrack> ScaleData;
	SeparateRawDataIntoTracks( AnimSeq->RawAnimationData, AnimSeq->SequenceLength, TranslationData, RotationData, ScaleData );

	// Remove Translation Keys from tracks marked bAnimRotationOnly
	FilterAnimRotationOnlyKeys(TranslationData, AnimSeq);

	// remove obviously redundant keys from the source data
	FilterTrivialKeys(TranslationData, RotationData, ScaleData, TRANSLATION_ZEROING_THRESHOLD, QUATERNION_ZEROING_THRESHOLD, SCALE_ZEROING_THRESHOLD);

	// bitwise compress the tracks into the anim sequence buffers
	BitwiseCompressAnimationTracks(
		AnimSeq,
		static_cast<AnimationCompressionFormat>(TranslationCompressionFormat),
		static_cast<AnimationCompressionFormat>(RotationCompressionFormat),
		static_cast<AnimationCompressionFormat>(ScaleCompressionFormat),
		TranslationData,
		RotationData, 
		ScaleData);

	// record the proper runtime decompressor to use
	AnimSeq->KeyEncodingFormat = AKF_ConstantKeyLerp;
	AnimationFormat_SetInterfaceLinks(*AnimSeq);
	AnimSeq->CompressionScheme = static_cast<UAnimCompress*>( StaticDuplicateObject( this, AnimSeq, TEXT("None")) );
#endif // WITH_EDITORONLY_DATA
}
void FAssetTypeActions_CameraAnim::OpenAssetEditor( const TArray<UObject*>& InObjects, TSharedPtr<class IToolkitHost> EditWithinLevelEditor )
{
	if(InObjects.Num() > 0)
	{
		UCameraAnim* CameraAnim = Cast<UCameraAnim>(InObjects[0]);
		if (CameraAnim != NULL)
		{
			// construct a temporary matinee actor
			CreateMatineeActorForCameraAnim(CameraAnim);

			if (GEditor->ShouldOpenMatinee(PreviewMatineeActor.Get()))
			{
				// changed the actor type, but don't want to lose any properties from previous
				// so duplicate from old, but with new class
				check(CameraAnim->CameraInterpGroup);
				if (!CameraAnim->CameraInterpGroup->IsA(UInterpGroupCamera::StaticClass()))
				{
					CameraAnim->CameraInterpGroup = CastChecked<UInterpGroupCamera>(StaticDuplicateObject(CameraAnim->CameraInterpGroup, CameraAnim, TEXT("CameraAnimation"), RF_NoFlags, UInterpGroupCamera::StaticClass()));
				}

				UInterpGroupCamera* NewInterpGroup = CastChecked<UInterpGroupCamera>(CameraAnim->CameraInterpGroup);
				check(NewInterpGroup);

				if (PreviewMatineeActor.Get()->MatineeData)
				{
					PreviewMatineeActor.Get()->MatineeData->SetFlags(RF_Transient);
					PreviewMatineeActor.Get()->MatineeData->InterpLength = CameraAnim->AnimLength;

					if (NewInterpGroup)
					{
						PreviewMatineeActor.Get()->MatineeData->InterpGroups.Add(NewInterpGroup);
					}
				}

				// create a CameraActor and connect it to the Interp. will create this at the perspective viewport's location and rotation
				CreateCameraActorForCameraAnim(CameraAnim);

				// set up the group actor
				PreviewMatineeActor.Get()->InitGroupActorForGroup(NewInterpGroup, PreviewCamera.Get());

				// Create preview pawn
				CreatePreviewPawnForCameraAnim(CameraAnim);

				// this will create the instances for everything
				PreviewMatineeActor.Get()->InitInterp();

				// open matinee for this actor
				GEditor->OpenMatinee(PreviewMatineeActor.Get());

				// install our delegate so we can clean up when finished
				OnMatineeEditorClosedDelegateHandle = FEditorDelegates::EditorModeExit.AddSP(this, &FAssetTypeActions_CameraAnim::OnMatineeEditorClosed);
			}
			else
			{
				GEditor->GetEditorWorldContext().World()->DestroyActor(PreviewMatineeActor.Get());
			}
		}
	}
}
Esempio n. 8
0
/**
 * Called after duplication & serialization and before PostLoad. Used to make sure UModel's FPolys
 * get duplicated as well.
 */
void UModel::PostDuplicate(bool bDuplicateForPIE)
{
	Super::PostDuplicate(bDuplicateForPIE);
#if WITH_EDITOR
	if( Polys )
	{
		Polys = CastChecked<UPolys>(StaticDuplicateObject( Polys, this, NULL ));
	}
#endif // WITH_EDITOR
}
Esempio n. 9
0
bool UBehaviorTreeManager::LoadTree(UBehaviorTree& Asset, UBTCompositeNode*& Root, uint16& InstanceMemorySize)
{
	SCOPE_CYCLE_COUNTER(STAT_AI_BehaviorTree_LoadTime);

	for (int32 TemplateIndex = 0; TemplateIndex < LoadedTemplates.Num(); TemplateIndex++)
	{
		FBehaviorTreeTemplateInfo& TemplateInfo = LoadedTemplates[TemplateIndex];
		if (TemplateInfo.Asset == &Asset)
		{
			Root = TemplateInfo.Template;
			InstanceMemorySize = TemplateInfo.InstanceMemorySize;
			return true;
		}
	}

	if (Asset.RootNode)
	{
		FBehaviorTreeTemplateInfo TemplateInfo;
		TemplateInfo.Asset = &Asset;
		TemplateInfo.Template = Cast<UBTCompositeNode>(StaticDuplicateObject(Asset.RootNode, this, TEXT("None")));

		TArray<FNodeInitializationData> InitList;
		uint16 ExecutionIndex = 0;
		InitializeNodeHelper(NULL, TemplateInfo.Template, 0, ExecutionIndex, InitList, Asset, this);

#if USE_BEHAVIORTREE_DEBUGGER
		// fill in information about next nodes in execution index, before sorting memory offsets
		for (int32 Index = 0; Index < InitList.Num() - 1; Index++)
		{
			InitList[Index].Node->InitializeExecutionOrder(InitList[Index + 1].Node);
		}
#endif

		// sort nodes by memory size, so they can be packed better
		// it still won't protect against structures, that are internally misaligned (-> uint8, uint32)
		// but since all Engine level nodes are good... 
		InitList.Sort(FNodeInitializationData::FMemorySort());
		uint16 MemoryOffset = 0;
		for (int32 Index = 0; Index < InitList.Num(); Index++)
		{
			InitList[Index].Node->InitializeNode(InitList[Index].ParentNode, InitList[Index].ExecutionIndex, InitList[Index].SpecialDataSize + MemoryOffset, InitList[Index].TreeDepth);
			MemoryOffset += InitList[Index].DataSize;
		}
		
		TemplateInfo.InstanceMemorySize = MemoryOffset;

		INC_DWORD_STAT(STAT_AI_BehaviorTree_NumTemplates);
		LoadedTemplates.Add(TemplateInfo);
		Root = TemplateInfo.Template;
		InstanceMemorySize = TemplateInfo.InstanceMemorySize;
		return true;
	}

	return false;
}
Esempio n. 10
0
UEnvQueryContext* UEnvQueryManager::PrepareLocalContext(TSubclassOf<UEnvQueryContext> ContextClass)
{
	UEnvQueryContext* LocalContext = LocalContextMap.FindRef(ContextClass->GetFName());
	if (LocalContext == NULL)
	{
		LocalContext = (UEnvQueryContext*)StaticDuplicateObject(ContextClass.GetDefaultObject(), this, TEXT("None"));
		LocalContexts.Add(LocalContext);
		LocalContextMap.Add(ContextClass->GetFName(), LocalContext);
	}

	return LocalContext;
}
bool UCameraAnim::CreateFromInterpGroup(class UInterpGroup* SrcGroup, class AMatineeActor* InMatineeActor)
{
	// assert we're controlling a camera actor
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
	{
		UInterpGroupInst* GroupInst = InMatineeActor ? InMatineeActor->FindFirstGroupInst(SrcGroup) : NULL;
		if (GroupInst)
		{
			check( GroupInst->GetGroupActor()->IsA(ACameraActor::StaticClass()) );
		}
	}
#endif
	
	// copy length information
	AnimLength = (InMatineeActor && InMatineeActor->MatineeData) ? InMatineeActor->MatineeData->InterpLength : 0.f;

	UInterpGroup* OldGroup = CameraInterpGroup;

	if (CameraInterpGroup != SrcGroup)
	{
		// copy the source interp group for use in the CameraAnim
		// @fixme jf: fixed this potentially creating an object of UInterpGroup and raw casting it to InterpGroupCamera.  No source data in UE4 to test though.
		CameraInterpGroup = Cast<UInterpGroupCamera>(StaticDuplicateObject(SrcGroup, this, NAME_None, RF_AllFlags, UInterpGroupCamera::StaticClass()));

		if (CameraInterpGroup)
		{
			// delete the old one, if it exists
			if (OldGroup)
			{
				OldGroup->MarkPendingKill();
			}

			// success!
			return true;
		}
		else
		{
			// creation of new one failed somehow, restore the old one
			CameraInterpGroup = OldGroup;
		}
	}
	else
	{
		// no need to perform work above, but still a "success" case
		return true;
	}

	// failed creation
	return false;
}
UActorComponent* AActor::CreateComponentFromTemplate(UActorComponent* Template, const FString & InName)
{
	UActorComponent* NewActorComp = NULL;
	if(Template != NULL)
	{
		// Note we aren't copying the the RF_ArchetypeObject flag. Also note the result is non-transactional by default.
		NewActorComp = (UActorComponent*)StaticDuplicateObject(Template, this, *InName, RF_AllFlags & ~(RF_ArchetypeObject|RF_Transactional|RF_WasLoaded) );
		//NewActorComp = ConstructObject<UActorComponent>(Template->GetClass(), this, *InName, RF_NoFlags, Template);

		NewActorComp->bCreatedByConstructionScript = true;

		// Need to do this so component gets saved - Components array is not serialized
		SerializedComponents.Add(NewActorComp);
	}
	return NewActorComp;
}
Esempio n. 13
0
void UAnimCompress_Automatic::DoReduction(UAnimSequence* AnimSeq, const TArray<FBoneData>& BoneData)
{
#if WITH_EDITORONLY_DATA
	FAnimationUtils::CompressAnimSequenceExplicit(
		AnimSeq,
		MaxEndEffectorError,
		false, // bOutput
		bRunCurrentDefaultCompressor,
		bAutoReplaceIfExistingErrorTooGreat,
		bRaiseMaxErrorToExisting,
		bTryFixedBitwiseCompression,
		bTryPerTrackBitwiseCompression,
		bTryLinearKeyRemovalCompression,
		bTryIntervalKeyRemoval);
	AnimSeq->CompressionScheme = static_cast<UAnimCompress*>( StaticDuplicateObject( AnimSeq->CompressionScheme, AnimSeq, TEXT("None")) );
#endif // WITH_EDITORONLY_DATA
}
void UChildActorComponent::Serialize(FArchive& Ar)
{
	Super::Serialize(Ar);

	if (Ar.HasAllPortFlags(PPF_DuplicateForPIE))
	{
		// PIE duplication should just work normally
		Ar << ChildActorTemplate;
	}
	else if (Ar.HasAllPortFlags(PPF_Duplicate))
	{
		if (GIsEditor && Ar.IsLoading() && !IsTemplate())
		{
			// If we're not a template then we do not want the duplicate so serialize manually and destroy the template that was created for us
			Ar.Serialize(&ChildActorTemplate, sizeof(UObject*));

			if (AActor* UnwantedDuplicate = static_cast<AActor*>(FindObjectWithOuter(this, AActor::StaticClass())))
			{
				UnwantedDuplicate->MarkPendingKill();
			}
		}
		else if (!GIsEditor && !Ar.IsLoading() && !GIsDuplicatingClassForReinstancing)
		{
			// Avoid the archiver in the duplicate writer case because we want to avoid the duplicate being created
			Ar.Serialize(&ChildActorTemplate, sizeof(UObject*));
		}
		else
		{
			// When we're loading outside of the editor we won't have created the duplicate, so its fine to just use the normal path
			// When we're loading a template then we want the duplicate, so it is fine to use normal archiver
			// When we're saving in the editor we'll create the duplicate, but on loading decide whether to take it or not
			Ar << ChildActorTemplate;
		}
	}

#if WITH_EDITOR
	// Since we sometimes serialize properties in instead of using duplication if we are a template
	// and are not pointing at a component we own we'll need to fix that
	if (ChildActorTemplate && ChildActorTemplate->GetOuter() != this && IsTemplate())
	{
		const FString TemplateName = FString::Printf(TEXT("%s_%s_CAT"), *GetName(), *ChildActorClass->GetName());
		ChildActorTemplate = CastChecked<AActor>(StaticDuplicateObject(ChildActorTemplate, this, *TemplateName));
	}
#endif
}
Esempio n. 15
0
UActorComponent* AActor::CreateComponentFromTemplate(UActorComponent* Template, const FString& InName)
{
	UActorComponent* NewActorComp = NULL;
	if(Template != NULL)
	{
		// If there is a Component with this name already (almost certainly because it is an Instance component), we need to rename it out of the way
		if (!InName.IsEmpty())
		{
			UObject* ConflictingObject = FindObjectFast<UObject>(this, *InName);
			if (ConflictingObject && ConflictingObject->IsA<UActorComponent>() && CastChecked<UActorComponent>(ConflictingObject)->CreationMethod == EComponentCreationMethod::Instance)
			{		
				// Try and pick a good name
				FString ConflictingObjectName = ConflictingObject->GetName();
				int32 CharIndex = ConflictingObjectName.Len()-1;
				while (FChar::IsDigit(ConflictingObjectName[CharIndex]))
				{
					--CharIndex;
				}
				int32 Counter = 0;
				if (CharIndex < ConflictingObjectName.Len()-1)
				{
					Counter = FCString::Atoi(*ConflictingObjectName.RightChop(CharIndex+1));
					ConflictingObjectName = ConflictingObjectName.Left(CharIndex+1);
				}
				FString NewObjectName;
				do
				{
					NewObjectName = ConflictingObjectName + FString::FromInt(++Counter);
					
				} while (FindObjectFast<UObject>(this, *NewObjectName) != nullptr);

				ConflictingObject->Rename(*NewObjectName, this);
			}
		}

		// Note we aren't copying the the RF_ArchetypeObject flag. Also note the result is non-transactional by default.
		NewActorComp = (UActorComponent*)StaticDuplicateObject(Template, this, *InName, RF_AllFlags & ~(RF_ArchetypeObject|RF_Transactional|RF_WasLoaded|RF_Public|RF_InheritableComponentTemplate) );

		NewActorComp->CreationMethod = EComponentCreationMethod::UserConstructionScript;

		// Need to do this so component gets saved - Components array is not serialized
		BlueprintCreatedComponents.Add(NewActorComp);
	}
	return NewActorComp;
}
Esempio n. 16
0
FReply FNiagaraEffectEditor::OnDuplicateEmitterClicked(TSharedPtr<FNiagaraSimulation> Emitter)
{
	FNiagaraEditorModule& NiagaraEditorModule = FModuleManager::LoadModuleChecked<FNiagaraEditorModule>("NiagaraEditor");

	if (UNiagaraEmitterProperties* ToDupe = Emitter->GetProperties().Get())
	{
		UNiagaraEmitterProperties *Props = CastChecked<UNiagaraEmitterProperties>(StaticDuplicateObject(ToDupe,Effect,NULL));
		Effect->AddEmitterProperties(Props);
		TSharedPtr<FNiagaraSimulation> NewEmitter = EffectInstance->AddEmitter(Props);
		Effect->CreateEffectRendererProps(NewEmitter);
		Viewport->SetPreviewEffect(EffectInstance);
		EmitterEditorWidget->UpdateList();
		DevEmitterEditorWidget->UpdateList();

		Effect->MarkPackageDirty();
	}
	return FReply::Handled();
}
bool UPawnAction_Sequence::PushNextActionCopy()
{
	if (CurrentActionIndex >= uint32(ActionSequence.Num()))
	{
		Finish(EPawnActionResult::Success);
		return true;
	}

	UPawnAction* ActionCopy = SubActionTriggeringPolicy == EPawnSubActionTriggeringPolicy::CopyBeforeTriggering
		? Cast<UPawnAction>(StaticDuplicateObject(ActionSequence[CurrentActionIndex], this, NULL))
		: ActionSequence[CurrentActionIndex];

	UE_VLOG(GetPawn(), LogPawnAction, Log, TEXT("%s> pushing action %s")
		, *GetName(), *GetNameSafe(ActionCopy));
	++CurrentActionIndex;	
	check(ActionCopy);
	RecentActionCopy = ActionCopy;
	return PushChildAction(*ActionCopy);
}
Esempio n. 18
0
void UBehaviorTreeGraph::UpdateDeprecatedNodes()
{
	for (int32 Index = 0; Index < Nodes.Num(); ++Index)
	{
		UBehaviorTreeGraphNode* Node = Cast<UBehaviorTreeGraphNode>(Nodes[Index]);
		if (Node)
		{
			// UBTTask_RunBehavior is now handled by dedicated graph node
			if (Node->NodeInstance && Node->NodeInstance->IsA(UBTTask_RunBehavior::StaticClass()))
			{
				UBehaviorTreeGraphNode* NewNode = Cast<UBehaviorTreeGraphNode>(StaticDuplicateObject(Node, this, TEXT(""), RF_AllFlags, UBehaviorTreeGraphNode_SubtreeTask::StaticClass()));
				check(NewNode);

				ReplaceNodeConnections(Node, NewNode);
				Nodes[Index] = NewNode;
				Node = NewNode;
			}

			if (Node->NodeInstance)
			{
				Node->ErrorMessage = FClassBrowseHelper::GetDeprecationMessage(Node->NodeInstance->GetClass());
			}

			for (int32 i = 0; i < Node->Decorators.Num(); i++)
			{
				if (Node->Decorators[i] && Node->Decorators[i]->NodeInstance)
				{
					Node->Decorators[i]->ErrorMessage = FClassBrowseHelper::GetDeprecationMessage(Node->Decorators[i]->NodeInstance->GetClass());
				}
			}

			for (int32 i = 0; i < Node->Services.Num(); i++)
			{
				if (Node->Services[i] && Node->Services[i]->NodeInstance)
				{
					Node->Services[i]->ErrorMessage = FClassBrowseHelper::GetDeprecationMessage(Node->Services[i]->NodeInstance->GetClass());
				}
			}
		}
	}
}
void FConfigPropertyHelperDetails::AddEditablePropertyForConfig(IDetailLayoutBuilder& DetailBuilder, const UPropertyConfigFileDisplayRow* ConfigFilePropertyRowObj)
{
	AssociatedConfigFileAndObjectPairings.Add(ConfigFilePropertyRowObj->ConfigFileName, (UObject*)ConfigFilePropertyRowObj);

	// Add the properties to a property table so we can edit these.
	IDetailCategoryBuilder& TempCategory = DetailBuilder.EditCategory("TempCategory");

	UObject* ConfigEntryObject = StaticDuplicateObject(ConfigEditorPropertyViewCDO, GetTransientPackage(), *(ConfigFilePropertyRowObj->ConfigFileName + TEXT("_cdoDupe")));
	ConfigEntryObject->AddToRoot();

	FString ExistingConfigEntryValue;
	static FString SectionName = OriginalProperty->GetOwnerClass()->GetPathName();
	static FString PropertyName = ConfigEditorCopyOfEditProperty->GetName();
	if (GConfig->GetString(*SectionName, *PropertyName, ExistingConfigEntryValue, ConfigFilePropertyRowObj->ConfigFileName))
	{
		ConfigEditorCopyOfEditProperty->ImportText(*ExistingConfigEntryValue, ConfigEditorCopyOfEditProperty->ContainerPtrToValuePtr<uint8>(ConfigEntryObject), 0, nullptr);
	}

	// Cache a reference for future usage.
	ConfigFileAndPropertySourcePairings.Add(ConfigFilePropertyRowObj->ConfigFileName, (UObject*)ConfigEntryObject);

	// We need to add a property row for each config file entry. 
	// This allows us to have an editable widget for each config file.
	TArray<UObject*> ConfigPropertyDisplayObjects;
	ConfigPropertyDisplayObjects.Add(ConfigEntryObject);
	if (IDetailPropertyRow* ExternalRow = TempCategory.AddExternalProperty(ConfigPropertyDisplayObjects, ConfigEditorCopyOfEditProperty->GetFName()))
	{
		TSharedPtr<SWidget> NameWidget;
		TSharedPtr<SWidget> ValueWidget;
		ExternalRow->GetDefaultWidgets(NameWidget, ValueWidget);

		// Register the Value widget and config file pairing with the config editor.
		// The config editor needs this to determine what a cell presenter shows.
		IConfigEditorModule& ConfigEditor = FModuleManager::Get().LoadModuleChecked<IConfigEditorModule>("ConfigEditor");
		ConfigEditor.AddExternalPropertyValueWidgetAndConfigPairing(ConfigFilePropertyRowObj->ConfigFileName, ValueWidget);
		
		// now hide the property so it is not added to the property display view
		ExternalRow->Visibility(EVisibility::Hidden);
	}
}
Esempio n. 20
0
void UBTNode::InitializeInSubtree(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, int32& NextInstancedIndex, EBTMemoryInit::Type InitType) const
{
	FBTInstancedNodeMemory* SpecialMemory = GetSpecialNodeMemory<FBTInstancedNodeMemory>(NodeMemory);
	if (SpecialMemory)
	{
		SpecialMemory->NodeIdx = INDEX_NONE;
	}

	if (bCreateNodeInstance)
	{
		// composite nodes can't be instanced!
		check(IsA(UBTCompositeNode::StaticClass()) == false);

		UBTNode* NodeInstance = OwnerComp.NodeInstances.IsValidIndex(NextInstancedIndex) ? OwnerComp.NodeInstances[NextInstancedIndex] : NULL;
		if (NodeInstance == NULL)
		{
			NodeInstance = (UBTNode*)StaticDuplicateObject(this, &OwnerComp);
			NodeInstance->InitializeNode(GetParentNode(), GetExecutionIndex(), GetMemoryOffset(), GetTreeDepth());
			NodeInstance->bIsInstanced = true;

			OwnerComp.NodeInstances.Add(NodeInstance);
		}

		check(NodeInstance);
		check(SpecialMemory);

		SpecialMemory->NodeIdx = NextInstancedIndex;

		NodeInstance->SetOwner(OwnerComp.GetOwner());
		NodeInstance->InitializeMemory(OwnerComp, NodeMemory, InitType);
		check(TreeAsset);
		NodeInstance->InitializeFromAsset(*TreeAsset);
		NodeInstance->OnInstanceCreated(OwnerComp);
		NextInstancedIndex++;
	}
	else
	{
		InitializeMemory(OwnerComp, NodeMemory, InitType);
	}
}
void UAnimCompress_RemoveLinearKeys::DoReduction(UAnimSequence* AnimSeq, const TArray<FBoneData>& BoneData)
{
#if WITH_EDITORONLY_DATA
	// Only need to do the heavy lifting if it will have some impact
	// One of these will always be true for the base class, but derived classes may choose to turn both off (e.g., in PerTrackCompression)
	const bool bRunningProcessor = bRetarget || bActuallyFilterLinearKeys;

	if (GIsEditor && bRunningProcessor)
	{
		GWarn->BeginSlowTask( NSLOCTEXT("UAnimCompress_RemoveLinearKeys", "BeginReductionTaskMessage", "Compressing animation with a RemoveLinearKeys scheme."), false);
	}

	// If the processor is to be run, then additive animations need to be converted from relative to absolute
	const bool bNeedToConvertBackToAdditive = bRunningProcessor ? ConvertFromRelativeSpace(AnimSeq) : false;

	// Separate the raw data into tracks and remove trivial tracks (all the same value)
	TArray<FTranslationTrack> TranslationData;
	TArray<FRotationTrack> RotationData;
	TArray<FScaleTrack> ScaleData;
	SeparateRawDataIntoTracks(AnimSeq->RawAnimationData, AnimSeq->SequenceLength, TranslationData, RotationData, ScaleData);
	FilterBeforeMainKeyRemoval(AnimSeq, BoneData, TranslationData, RotationData, ScaleData);

	if (bRunningProcessor)
	{
#if TIME_LINEAR_KEY_REMOVAL
		double TimeStart = FPlatformTime::Seconds();
#endif
		// compress this animation without any key-reduction to prime the codec
		CompressUsingUnderlyingCompressor(
			AnimSeq,
			BoneData, 
			TranslationData,
			RotationData,
			ScaleData,
			false);

		// now remove the keys which can be approximated with linear interpolation
		ProcessAnimationTracks(
			AnimSeq,
			BoneData,
			TranslationData,
			RotationData, 
			ScaleData);
#if TIME_LINEAR_KEY_REMOVAL
		double ElapsedTime = FPlatformTime::Seconds() - TimeStart;
		UE_LOG(LogAnimationCompression, Log, TEXT("ProcessAnimationTracks time is (%f) seconds"),ElapsedTime);
#endif

		// if previously additive, convert back to relative-space
		if( bNeedToConvertBackToAdditive )
		{
			ConvertToRelativeSpace(AnimSeq, TranslationData, RotationData);
		}
	}

	// Remove Translation Keys from tracks marked bAnimRotationOnly
	FilterAnimRotationOnlyKeys(TranslationData, AnimSeq);

	// compress the final (possibly key-reduced) tracks into the anim sequence buffers
	CompressUsingUnderlyingCompressor(
		AnimSeq,
		BoneData,
		TranslationData,
		RotationData,
		ScaleData,
		true);

	if (GIsEditor && bRunningProcessor)
	{
		GWarn->EndSlowTask();
	}
	AnimSeq->CompressionScheme = static_cast<UAnimCompress*>( StaticDuplicateObject( this, AnimSeq, TEXT("None")) );

#endif // WITH_EDITORONLY_DATA
}
Esempio n. 22
0
TSharedPtr<FEnvQueryInstance> UEnvQueryManager::CreateQueryInstance(const UEnvQuery* Template, EEnvQueryRunMode::Type RunMode)
{
	if (Template == nullptr || Template->Options.Num() == 0)
	{
		UE_CLOG(Template != nullptr && Template->Options.Num() == 0, LogEQS, Warning, TEXT("Query [%s] doesn't have any valid options!"), *Template->GetName());
		return nullptr;
	}

	// try to find entry in cache
	FEnvQueryInstance* InstanceTemplate = NULL;
	for (int32 InstanceIndex = 0; InstanceIndex < InstanceCache.Num(); InstanceIndex++)
	{
		if (InstanceCache[InstanceIndex].Template->GetFName() == Template->GetFName() &&
			InstanceCache[InstanceIndex].Instance.Mode == RunMode)
		{
			InstanceTemplate = &InstanceCache[InstanceIndex].Instance;
			break;
		}
	}

	// and create one if can't be found
	if (InstanceTemplate == NULL)
	{
		SCOPE_CYCLE_COUNTER(STAT_AI_EQS_LoadTime);
		
		// duplicate template in manager's world for BP based nodes
		UEnvQuery* LocalTemplate = (UEnvQuery*)StaticDuplicateObject(Template, this, *Template->GetName());

		{
			// memory stat tracking: temporary variable will exist only inside this section
			FEnvQueryInstanceCache NewCacheEntry;
			NewCacheEntry.Template = LocalTemplate;
			NewCacheEntry.Instance.QueryName = LocalTemplate->GetName();
			NewCacheEntry.Instance.Mode = RunMode;

			const int32 Idx = InstanceCache.Add(NewCacheEntry);
			InstanceTemplate = &InstanceCache[Idx].Instance;
		}

		// NOTE: We must iterate over this from 0->Num because we are copying the options from the template into the
		// instance, and order matters!  Since we also may need to remove invalid or null options, we must decrement
		// the iteration pointer when doing so to avoid problems.
		for (int32 OptionIndex = 0; OptionIndex < LocalTemplate->Options.Num(); ++OptionIndex)
		{
			UEnvQueryOption* MyOption = LocalTemplate->Options[OptionIndex];
			if (MyOption == nullptr ||
				MyOption->Generator == nullptr ||
				MyOption->Generator->ItemType == nullptr)
			{
				UE_LOG(LogEQS, Error, TEXT("Trying to spawn a query with broken Template (generator:%s itemType:%s): %s, option %d"),
					MyOption ? (MyOption->Generator ? TEXT("ok") : TEXT("MISSING")) : TEXT("N/A"),
					(MyOption && MyOption->Generator) ? (MyOption->Generator->ItemType ? TEXT("ok") : TEXT("MISSING")) : TEXT("N/A"),
					*GetNameSafe(LocalTemplate), OptionIndex);

				LocalTemplate->Options.RemoveAt(OptionIndex, 1, false);
				--OptionIndex; // See note at top of for loop.  We cannot iterate backwards here.
				continue;
			}

			UEnvQueryOption* LocalOption = (UEnvQueryOption*)StaticDuplicateObject(MyOption, this, TEXT("None"));
			UEnvQueryGenerator* LocalGenerator = (UEnvQueryGenerator*)StaticDuplicateObject(MyOption->Generator, this, TEXT("None"));
			LocalTemplate->Options[OptionIndex] = LocalOption;
			LocalOption->Generator = LocalGenerator;

			EEnvTestCost::Type HighestCost(EEnvTestCost::Low);
			TArray<UEnvQueryTest*> SortedTests = MyOption->Tests;
			TSubclassOf<UEnvQueryItemType> GeneratedType = MyOption->Generator->ItemType;
			for (int32 TestIndex = SortedTests.Num() - 1; TestIndex >= 0; TestIndex--)
			{
				UEnvQueryTest* TestOb = SortedTests[TestIndex];
				if (TestOb == NULL || !TestOb->IsSupportedItem(GeneratedType))
				{
					UE_LOG(LogEQS, Warning, TEXT("Query [%s] can't use test [%s] in option %d [%s], removing it"),
						*GetNameSafe(LocalTemplate), *GetNameSafe(TestOb), OptionIndex, *MyOption->Generator->OptionName);

					SortedTests.RemoveAt(TestIndex, 1, false);
				}
				else if (HighestCost < TestOb->Cost)
				{
					HighestCost = TestOb->Cost;
				}
			}

			if (SortedTests.Num() == 0)
			{
				UE_LOG(LogEQS, Warning, TEXT("Query [%s] doesn't have any tests in option %d [%s]"),
					*GetNameSafe(LocalTemplate), OptionIndex, *MyOption->Generator->OptionName);

				LocalTemplate->Options.RemoveAt(OptionIndex, 1, false);
				--OptionIndex; // See note at top of for loop.  We cannot iterate backwards here.
				continue;
			}

			LocalOption->Tests.Reset(SortedTests.Num());
			for (int32 TestIdx = 0; TestIdx < SortedTests.Num(); TestIdx++)
			{
				UEnvQueryTest* LocalTest = (UEnvQueryTest*)StaticDuplicateObject(SortedTests[TestIdx], this, TEXT("None"));
				LocalOption->Tests.Add(LocalTest);
			}

			// use locally referenced duplicates
			SortedTests = LocalOption->Tests;

			if (SortedTests.Num() && LocalGenerator->bAutoSortTests)
			{
				switch (RunMode)
				{
				case EEnvQueryRunMode::SingleResult:
					SortedTests.Sort(EnvQueryTestSort::FSingleResult(HighestCost));
					break;

				case EEnvQueryRunMode::RandomBest5Pct:
				case EEnvQueryRunMode::RandomBest25Pct:
				case EEnvQueryRunMode::AllMatching:
					SortedTests.Sort(EnvQueryTestSort::FAllMatching());
					break;

				default:
					{
						UEnum* RunModeEnum = FindObject<UEnum>(ANY_PACKAGE, TEXT("EEnvQueryRunMode"));
						UE_LOG(LogEQS, Warning, TEXT("Query [%s] can't be sorted for RunMode: %d [%s]"),
							*GetNameSafe(LocalTemplate), (int32)RunMode, RunModeEnum ? *RunModeEnum->GetEnumName(RunMode) : TEXT("??"));
					}
				}
			}

			CreateOptionInstance(LocalOption, SortedTests, *InstanceTemplate);
		}
	}

	if (InstanceTemplate->Options.Num() == 0)
	{
		return nullptr;
	}

	// create new instance
	TSharedPtr<FEnvQueryInstance> NewInstance(new FEnvQueryInstance(*InstanceTemplate));
	return NewInstance;
}
FBlueprintCompileReinstancer::FBlueprintCompileReinstancer(UClass* InClassToReinstance, bool bIsBytecodeOnly, bool bSkipGC)
	: ClassToReinstance(InClassToReinstance)
	, DuplicatedClass(NULL)
	, OriginalCDO(NULL)
	, bHasReinstanced(false)
	, bSkipGarbageCollection(bSkipGC)
	, ClassToReinstanceDefaultValuesCRC(0)
{
	if( InClassToReinstance != NULL )
	{
		bIsReinstancingSkeleton = FKismetEditorUtilities::IsClassABlueprintSkeleton(ClassToReinstance);

		SaveClassFieldMapping(InClassToReinstance);

		// Remember the initial CDO for the class being resinstanced
		OriginalCDO = ClassToReinstance->GetDefaultObject();

		// Duplicate the class we're reinstancing into the transient package.  We'll re-class all objects we find to point to this new class
		GIsDuplicatingClassForReinstancing = true;
		ClassToReinstance->ClassFlags |= CLASS_NewerVersionExists;
		const FName RenistanceName = MakeUniqueObjectName(GetTransientPackage(), ClassToReinstance->GetClass(), *FString::Printf(TEXT("REINST_%s"), *ClassToReinstance->GetName()));
		DuplicatedClass = (UClass*)StaticDuplicateObject(ClassToReinstance, GetTransientPackage(), *RenistanceName.ToString(), ~RF_Transactional); 

		ClassToReinstance->ClassFlags &= ~CLASS_NewerVersionExists;
		GIsDuplicatingClassForReinstancing = false;

		auto BPGDuplicatedClass = Cast<UBlueprintGeneratedClass>(DuplicatedClass);
		auto DuplicatedClassUberGraphFunction = BPGDuplicatedClass ? BPGDuplicatedClass->UberGraphFunction : nullptr;
		if (DuplicatedClassUberGraphFunction)
		{
			DuplicatedClassUberGraphFunction->Bind();
			DuplicatedClassUberGraphFunction->StaticLink(true);
		}

		// Bind and link the duplicate class, so that it has the proper duplicate property offsets
		DuplicatedClass->Bind();
		DuplicatedClass->StaticLink(true);

		// Copy over the ComponentNametoDefaultObjectMap, which tells CopyPropertiesForUnrelatedObjects which components are instanced and which aren't

		GIsDuplicatingClassForReinstancing = true;
		UObject* OldCDO = ClassToReinstance->GetDefaultObject();
		DuplicatedClass->ClassDefaultObject = (UObject*)StaticDuplicateObject(OldCDO, GetTransientPackage(), *DuplicatedClass->GetDefaultObjectName().ToString());
		GIsDuplicatingClassForReinstancing = false;

		DuplicatedClass->ClassDefaultObject->SetFlags(RF_ClassDefaultObject);
		DuplicatedClass->ClassDefaultObject->SetClass(DuplicatedClass);

		OldCDO->SetClass(DuplicatedClass);
		ObjectsThatShouldUseOldStuff.Add(DuplicatedClass); //CDO of REINST_ class can be used as archetype

		if( !bIsBytecodeOnly )
		{
			TArray<UObject*> ObjectsToChange;
			const bool bIncludeDerivedClasses = false;
			GetObjectsOfClass(ClassToReinstance, ObjectsToChange, bIncludeDerivedClasses);
			for (auto ObjIt = ObjectsToChange.CreateConstIterator(); ObjIt; ++ObjIt)
			{
				(*ObjIt)->SetClass(DuplicatedClass);
			}

			TArray<UClass*> ChildrenOfClass;
			GetDerivedClasses(ClassToReinstance, ChildrenOfClass);
			for ( auto ClassIt = ChildrenOfClass.CreateConstIterator(); ClassIt; ++ClassIt )
			{
				UClass* ChildClass = *ClassIt;
				UBlueprint* ChildBP = Cast<UBlueprint>(ChildClass->ClassGeneratedBy);
				if (ChildBP)
				{
					const bool bClassIsDirectlyGeneratedByTheBlueprint = (ChildBP->GeneratedClass == ChildClass)
						|| (ChildBP->SkeletonGeneratedClass == ChildClass);

					if (ChildBP->HasAnyFlags(RF_BeingRegenerated) || !bClassIsDirectlyGeneratedByTheBlueprint)
					{
						if (ChildClass->GetSuperClass() == ClassToReinstance)
						{
							ReparentChild(ChildClass);
						}

						//TODO: some stronger condition would be nice
						if (!bClassIsDirectlyGeneratedByTheBlueprint)
						{
							ObjectsThatShouldUseOldStuff.Add(ChildClass);
						}
					}
					// If this is a direct child, change the parent and relink so the property chain is valid for reinstancing
					else if( !ChildBP->HasAnyFlags(RF_NeedLoad) )
					{
						if( ChildClass->GetSuperClass() == ClassToReinstance )
						{
							ReparentChild(ChildBP);
						}

						Children.AddUnique(ChildBP);
					}
					else
					{
						// If this is a child that caused the load of their parent, relink to the REINST class so that we can still serialize in the CDO, but do not add to later processing
						ReparentChild(ChildClass);
					}
				}
			}
		}

		// Pull the blueprint that generated this reinstance target, and gather the blueprints that are dependent on it
		UBlueprint* GeneratingBP = Cast<UBlueprint>(ClassToReinstance->ClassGeneratedBy);
		check(GeneratingBP || GIsAutomationTesting);
		if(GeneratingBP)
		{
			ClassToReinstanceDefaultValuesCRC = GeneratingBP->CrcPreviousCompiledCDO;
			FBlueprintEditorUtils::GetDependentBlueprints(GeneratingBP, Dependencies);
		}
	}
}
EReimportResult::Type UReimportFbxSceneFactory::ReimportStaticMesh(void* VoidFbxImporter, TSharedPtr<FFbxMeshInfo> MeshInfo)
{
	UnFbx::FFbxImporter* FbxImporter = (UnFbx::FFbxImporter*)VoidFbxImporter;
	//Find the UObject associate with this MeshInfo
	UPackage* PkgExist = LoadPackage(nullptr, *(MeshInfo->GetImportPath()), LOAD_Verify | LOAD_NoWarn);
	if (PkgExist != nullptr)
	{
		PkgExist->FullyLoad();
	}

	FString AssetName = MeshInfo->GetFullImportName();
	UStaticMesh* Mesh = FindObjectSafe<UStaticMesh>(ANY_PACKAGE, *AssetName);
	if (Mesh == nullptr)
	{
		//We reimport only static mesh here
		FbxImporter->AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(FText::FromString("Reimport Mesh {0} fail, the original staicmesh in the content browser cannot be load."), FText::FromString(MeshInfo->GetImportPath()))), FName(TEXT("Reimport Fbx Scene")));
		return EReimportResult::Failed;
	}
	
	//Copy default options to StaticMeshImportData
	SFbxSceneOptionWindow::CopyFbxOptionsToStaticMeshOptions(GlobalImportSettingsReference, SceneImportOptionsStaticMesh);
	SceneImportOptionsStaticMesh->FillStaticMeshInmportData(StaticMeshImportData, SceneImportOptions);

	UnFbx::FBXImportOptions* OverrideImportSettings = GetOptionsFromName(MeshInfo->OptionName);
	if (OverrideImportSettings != nullptr)
	{
		SFbxSceneOptionWindow::CopyFbxOptionsToFbxOptions(OverrideImportSettings, GlobalImportSettings);
		SFbxSceneOptionWindow::CopyFbxOptionsToStaticMeshOptions(OverrideImportSettings, SceneImportOptionsStaticMesh);
	}
	else
	{
		SFbxSceneOptionWindow::CopyFbxOptionsToFbxOptions(GlobalImportSettingsReference, GlobalImportSettings);
		SFbxSceneOptionWindow::CopyFbxOptionsToStaticMeshOptions(GlobalImportSettingsReference, SceneImportOptionsStaticMesh);
	}
	SceneImportOptionsStaticMesh->FillStaticMeshInmportData(StaticMeshImportData, SceneImportOptions);

	FbxImporter->ApplyTransformSettingsToFbxNode(FbxImporter->Scene->GetRootNode(), StaticMeshImportData);
	const TArray<UAssetUserData*>* UserData = Mesh->GetAssetUserDataArray();
	TArray<UAssetUserData*> UserDataCopy;
	if (UserData)
	{
		for (int32 Idx = 0; Idx < UserData->Num(); Idx++)
		{
			UserDataCopy.Add((UAssetUserData*)StaticDuplicateObject((*UserData)[Idx], GetTransientPackage()));
		}
	}

	// preserve settings in navcollision subobject
	UNavCollision* NavCollision = Mesh->NavCollision ?
		(UNavCollision*)StaticDuplicateObject(Mesh->NavCollision, GetTransientPackage()) :
		nullptr;

	// preserve extended bound settings
	const FVector PositiveBoundsExtension = Mesh->PositiveBoundsExtension;
	const FVector NegativeBoundsExtension = Mesh->NegativeBoundsExtension;
	Mesh = FbxImporter->ReimportSceneStaticMesh(MeshInfo->UniqueId, Mesh, StaticMeshImportData);
	if (Mesh != nullptr)
	{
		//Put back the new mesh data since the reimport is putting back the original import data
		SceneImportOptionsStaticMesh->FillStaticMeshInmportData(StaticMeshImportData, SceneImportOptions);
		Mesh->AssetImportData = StaticMeshImportData;

		// Copy user data to newly created mesh
		for (int32 Idx = 0; Idx < UserDataCopy.Num(); Idx++)
		{
			UserDataCopy[Idx]->Rename(nullptr, Mesh, REN_DontCreateRedirectors | REN_DoNotDirty);
			Mesh->AddAssetUserData(UserDataCopy[Idx]);
		}

		if (NavCollision)
		{
			Mesh->NavCollision = NavCollision;
			NavCollision->Rename(nullptr, Mesh, REN_DontCreateRedirectors | REN_DoNotDirty);
		}

		// Restore bounds extension settings
		Mesh->PositiveBoundsExtension = PositiveBoundsExtension;
		Mesh->NegativeBoundsExtension = NegativeBoundsExtension;

		Mesh->AssetImportData->Update(FbxImportFileName);

		// Try to find the outer package so we can dirty it up
		if (Mesh->GetOutermost())
		{
			Mesh->GetOutermost()->MarkPackageDirty();
		}
		else
		{
			Mesh->MarkPackageDirty();
		}
		AllNewAssets.Add(MeshInfo, Mesh);
		AssetToSyncContentBrowser.Add(Mesh);
	}
	else
	{
		return EReimportResult::Failed;
	}
	return EReimportResult::Succeeded;
}
Esempio n. 25
0
UPaperTileMap* UPaperTileMap::CloneTileMap(UObject* OuterForClone)
{
	return CastChecked<UPaperTileMap>(StaticDuplicateObject(this, OuterForClone, nullptr));
}
Esempio n. 26
0
static void InitializeNodeHelper(UBTCompositeNode* ParentNode, UBTNode* NodeOb,
	uint8 TreeDepth, uint16& ExecutionIndex, TArray<FNodeInitializationData>& InitList,
	UBehaviorTree& TreeAsset, UObject* NodeOuter)
{
	// special case: subtrees
	UBTTask_RunBehavior* SubtreeTask = Cast<UBTTask_RunBehavior>(NodeOb);
	if (SubtreeTask)
	{
		ExecutionIndex += SubtreeTask->GetInjectedNodesCount();
	}

	InitList.Add(FNodeInitializationData(NodeOb, ParentNode, ExecutionIndex, TreeDepth, NodeOb->GetInstanceMemorySize(), NodeOb->GetSpecialMemorySize()));
	NodeOb->InitializeFromAsset(TreeAsset);
	ExecutionIndex++;

	UBTCompositeNode* CompositeOb = Cast<UBTCompositeNode>(NodeOb);
	if (CompositeOb)
	{
		for (int32 ServiceIndex = 0; ServiceIndex < CompositeOb->Services.Num(); ServiceIndex++)
		{
			if (CompositeOb->Services[ServiceIndex] == NULL)
			{
				UE_LOG(LogBehaviorTree, Warning, TEXT("%s has missing service node! (parent: %s)"),
					*TreeAsset.GetName(), *UBehaviorTreeTypes::DescribeNodeHelper(CompositeOb));

				CompositeOb->Services.RemoveAt(ServiceIndex, 1, false);
				ServiceIndex--;
				continue;
			}

			UBTService* Service = Cast<UBTService>(StaticDuplicateObject(CompositeOb->Services[ServiceIndex], NodeOuter, TEXT("None")));;
			CompositeOb->Services[ServiceIndex] = Service;

			InitList.Add(FNodeInitializationData(Service, CompositeOb, ExecutionIndex, TreeDepth,
				Service->GetInstanceMemorySize(), Service->GetSpecialMemorySize()));

			Service->InitializeFromAsset(TreeAsset);
			ExecutionIndex++;
		}

		for (int32 ChildIndex = 0; ChildIndex < CompositeOb->Children.Num(); ChildIndex++)
		{
			FBTCompositeChild& ChildInfo = CompositeOb->Children[ChildIndex];
			for (int32 DecoratorIndex = 0; DecoratorIndex < ChildInfo.Decorators.Num(); DecoratorIndex++)
			{
				if (ChildInfo.Decorators[DecoratorIndex] == NULL)
				{
					UE_LOG(LogBehaviorTree, Warning, TEXT("%s has missing decorator node! (parent: %s, branch: %d)"),
						*TreeAsset.GetName(), *UBehaviorTreeTypes::DescribeNodeHelper(CompositeOb), ChildIndex);

					ChildInfo.Decorators.RemoveAt(DecoratorIndex, 1, false);
					DecoratorIndex--;
					continue;
				}

				UBTDecorator* Decorator = Cast<UBTDecorator>(StaticDuplicateObject(ChildInfo.Decorators[DecoratorIndex], NodeOuter, TEXT("None")));
				ChildInfo.Decorators[DecoratorIndex] = Decorator;

				InitList.Add(FNodeInitializationData(Decorator, CompositeOb, ExecutionIndex, TreeDepth,
					Decorator->GetInstanceMemorySize(), Decorator->GetSpecialMemorySize()));

				Decorator->InitializeFromAsset(TreeAsset);
				Decorator->InitializeDecorator(ChildIndex);
				ExecutionIndex++;
			}

			UBTNode* ChildNode = NULL;
			
			if (ChildInfo.ChildComposite)
			{
				ChildInfo.ChildComposite = Cast<UBTCompositeNode>(StaticDuplicateObject(ChildInfo.ChildComposite, NodeOuter, TEXT("None")));
				ChildNode = ChildInfo.ChildComposite;
			}
			else if (ChildInfo.ChildTask)
			{
				ChildInfo.ChildTask = Cast<UBTTaskNode>(StaticDuplicateObject(ChildInfo.ChildTask, NodeOuter, TEXT("None")));
				ChildNode = ChildInfo.ChildTask;
			}

			if (ChildNode)
			{
				InitializeNodeHelper(CompositeOb, ChildNode, TreeDepth + 1, ExecutionIndex, InitList, TreeAsset, NodeOuter);
			}
		}

		CompositeOb->InitializeComposite(ExecutionIndex - 1);
	}
}
bool UBehaviorTreeGraphNode_SubtreeTask::UpdateInjectedNodes()
{
	bool bUpdated = false;

	// check if cached data needs to be updated
	UBTTask_RunBehavior* MyNode = Cast<UBTTask_RunBehavior>(NodeInstance);
	if (MyNode == NULL)
	{
		return bUpdated;
	}

	UBehaviorTreeGraph* MyGraph = MyNode->GetSubtreeAsset() ? Cast<UBehaviorTreeGraph>(MyNode->GetSubtreeAsset()->BTGraph) : NULL;
	int32 MyVersion = MyGraph ? MyGraph->GraphVersion : 0;
	FString MyPath = MyNode->GetSubtreeAsset() ? MyNode->GetSubtreeAsset()->GetName() : FString();

	if (MyPath == SubtreePath && MyVersion == SubtreeVersion)
	{
		return bUpdated;
	}

	SubtreePath = MyPath;
	SubtreeVersion = MyVersion;
	bUpdated = true;

	// remove existing injected nodes
	for (int32 Index = Decorators.Num() - 1; Index >= 0; Index--)
	{
		if (Decorators[Index] && Decorators[Index]->bInjectedNode)
		{
			Decorators.RemoveAt(Index, 1, false);
		}
	}

	// find root graph node of subtree
	UBehaviorTreeGraphNode* SubRoot = NULL;
	if (MyGraph && MyNode->GetSubtreeAsset()->RootDecorators.Num())
	{
		for (int32 Index = 0; Index < MyGraph->Nodes.Num(); Index++)
		{
			UBehaviorTreeGraphNode_Root* BTNode = Cast<UBehaviorTreeGraphNode_Root>(MyGraph->Nodes[Index]);
			if (BTNode && BTNode->Pins.IsValidIndex(0) && BTNode->Pins[0]->LinkedTo.IsValidIndex(0))
			{
				SubRoot = Cast<UBehaviorTreeGraphNode>(BTNode->Pins[0]->LinkedTo[0]->GetOwningNode());
				break;
			}
		}
	}

	// add root level subnodes as injected nodes
	if (SubRoot)
	{
		UBehaviorTree* BTAsset = Cast<UBehaviorTree>(GetBehaviorTreeGraph()->GetOuter());

		if(BTAsset)
		{
			for (int32 Index = 0; Index < SubRoot->Decorators.Num(); Index++)
			{
				UBehaviorTreeGraphNode* SubNode = SubRoot->Decorators[Index];
				if (SubNode)
				{
					SubNode->PrepareForCopying();

					UBehaviorTreeGraphNode* InjectedNode = Cast<UBehaviorTreeGraphNode>(StaticDuplicateObject(SubNode, GetOuter(), TEXT("")));

					SubNode->PostCopyNode();
					InjectedNode->PostCopyNode();

					InjectedNode->ParentNode = this;
					InjectedNode->bInjectedNode = true;

					UBTDecorator* InjectedInstance = Cast<UBTDecorator>(InjectedNode->NodeInstance);
					if (InjectedInstance)
					{
						InjectedInstance->InitializeFromAsset(*BTAsset);
					}

					UBehaviorTreeGraphNode_CompositeDecorator* CompNode = Cast<UBehaviorTreeGraphNode_CompositeDecorator>(InjectedNode);
					if (CompNode)
					{
						UEdGraph* SubGraph = CompNode->GetBoundGraph();
						if (SubGraph)
						{
							SubGraph->bEditable = false;

							for (int32 SubIndex = 0; SubIndex < SubGraph->Nodes.Num(); SubIndex++)
							{
								UBehaviorTreeDecoratorGraphNode_Decorator* InjectedDecorator = Cast<UBehaviorTreeDecoratorGraphNode_Decorator>(SubGraph->Nodes[SubIndex]);
								if (InjectedDecorator)
								{
									InjectedInstance = Cast<UBTDecorator>(InjectedDecorator->NodeInstance);
									if (InjectedInstance)
									{
										InjectedInstance->InitializeFromAsset(*BTAsset);
									}
								}
							}
						}
					}

					Decorators.Add(InjectedNode);
				}
			}
		}
	}

	UEdGraph* ParentGraph = GetGraph();
	if (ParentGraph && bUpdated)
	{
		ParentGraph->NotifyGraphChanged();
	}

	return bUpdated;
}
void USimpleConstructionScript::PostLoad()
{
	Super::PostLoad();

#if WITH_EDITOR
	// Get the Blueprint that owns the SCS
	UBlueprint* Blueprint = GetBlueprint();
	if (!Blueprint)
	{
		// sometimes the PostLoad can be called, after the object was trashed, we dont want this
		UE_LOG(LogBlueprint, Warning, TEXT("USimpleConstructionScript::PostLoad() '%s' cannot find its owner blueprint"), *GetPathName());
		return;
	}

	for (USCS_Node* Node : GetAllNodes())
	{
		// Fix up any uninitialized category names
		if(Node->CategoryName.IsEmpty())
		{
			Node->CategoryName = NSLOCTEXT("SCS", "Default", "Default");
		}

		// Fix up components that may have switched from scene to non-scene type and vice-versa
		if(Node->ComponentTemplate != nullptr)
		{
			// Fix up any component template objects whose name doesn't match the current variable name; this ensures that there is always one unique template per node.
			FString VariableName = Node->GetVariableName().ToString();
			FString ComponentTemplateName = Node->ComponentTemplate->GetName();
			if(ComponentTemplateName.EndsWith(UActorComponent::ComponentTemplateNameSuffix) && !ComponentTemplateName.StartsWith(VariableName) && !GIsDuplicatingClassForReinstancing)
			{
				Node->ComponentTemplate->ConditionalPostLoad();
				Node->ComponentTemplate = static_cast<UActorComponent*>(StaticDuplicateObject(Node->ComponentTemplate, Node->ComponentTemplate->GetOuter(), *(VariableName + UActorComponent::ComponentTemplateNameSuffix)));
			}

			// Check to see if switched from scene to a non-scene component type
			if (!Node->ComponentTemplate->IsA<USceneComponent>())
			{
				// Otherwise, check to see if switched from scene to non-scene component type
				int32 RootNodeIndex = INDEX_NONE;
				if(!RootNodes.Find(Node, RootNodeIndex))
				{
					// Move the node into the root set if it's currently in the scene hierarchy
					USCS_Node* ParentNode = FindParentNode(Node);
					if(ParentNode != nullptr)
					{
						ParentNode->RemoveChildNode(Node);
					}

					RootNodes.Add(Node);
				}
				else
				{
					// Otherwise, if it's a root node, promote one of its children (if any) to take its place
					int32 PromoteIndex = FindPromotableChildNodeIndex(Node);
					if(PromoteIndex != INDEX_NONE)
					{
						// Remove it as a child node
						USCS_Node* ChildToPromote = Node->GetChildNodes()[PromoteIndex];
						Node->RemoveChildNodeAt(PromoteIndex, false);

						// Insert it as a root node just before its prior parent node; this way if it switches back to a scene type it won't supplant the new root we've just created
						RootNodes.Insert(ChildToPromote, RootNodeIndex);

						// Append previous root node's children to the new root
						ChildToPromote->MoveChildNodes(Node);

						// Copy any previous external attachment info from the previous root node
						ChildToPromote->bIsParentComponentNative = Node->bIsParentComponentNative;
						ChildToPromote->ParentComponentOrVariableName = Node->ParentComponentOrVariableName;
						ChildToPromote->ParentComponentOwnerClassName = Node->ParentComponentOwnerClassName;
					}

					// Clear info for any previous external attachment if set
					if(Node->ParentComponentOrVariableName != NAME_None)
					{
						Node->bIsParentComponentNative = false;
						Node->ParentComponentOrVariableName = NAME_None;
						Node->ParentComponentOwnerClassName = NAME_None;
					}
				}
			}
		}
	}
#endif // WITH_EDITOR

	// Fix up native/inherited parent attachments, in case anything has changed
	FixupRootNodeParentReferences();

	// Ensure that we have a valid scene root
	ValidateSceneRootNodes();

	// Reset non-native "root" scene component scale values, prior to the change in which
	// we began applying custom scale values to root components at construction time. This
	// way older, existing Blueprint actor instances won't start unexpectedly getting scaled.
	if(GetLinkerUE4Version() < VER_UE4_BLUEPRINT_USE_SCS_ROOTCOMPONENT_SCALE)
	{
		// Get the BlueprintGeneratedClass that owns the SCS
		UClass* BPGeneratedClass = GetOwnerClass();
		if(BPGeneratedClass != nullptr)
		{
			// Get the Blueprint class default object
			AActor* CDO = Cast<AActor>(BPGeneratedClass->GetDefaultObject(false));
			if(CDO != NULL)
			{
				// Check for a native root component
				USceneComponent* NativeRootComponent = CDO->GetRootComponent();
				if(NativeRootComponent == nullptr)
				{
					// If no native root component exists, find the first non-native, non-parented SCS node with a
					// scene component template. This will be designated as the root component at construction time.
					for (USCS_Node* Node : RootNodes)
					{
						if(Node->ParentComponentOrVariableName == NAME_None)
						{
							// Note that we have to check for nullptr here, because it may be an ActorComponent type
							USceneComponent* SceneComponentTemplate = Cast<USceneComponent>(Node->ComponentTemplate);
							if(SceneComponentTemplate != nullptr
								&& SceneComponentTemplate->RelativeScale3D != FVector(1.0f, 1.0f, 1.0f))
							{
								UE_LOG(LogBlueprint, Warning, TEXT("%s: Found non-native root component custom scale for %s (%s) saved prior to being usable; reverting to default scale."), *BPGeneratedClass->GetName(), *Node->GetVariableName().ToString(), *SceneComponentTemplate->RelativeScale3D.ToString());
								SceneComponentTemplate->RelativeScale3D = FVector(1.0f, 1.0f, 1.0f);
							}

							// Done - no need to fix up any other nodes.
							break;
						}
					}
				}
			}
		}
	}

	if (GetLinkerUE4Version() < VER_UE4_SCS_STORES_ALLNODES_ARRAY)
	{
		// Fill out AllNodes if this is an older object
		if (RootNodes.Num() > 0)
		{
			AllNodes.Reset();
			for (USCS_Node* RootNode : RootNodes)
			{
				if (RootNode != nullptr)
				{
					AllNodes.Append(RootNode->GetAllNodes());
				}
			}
		}
	}
}
void FComponentEditorUtils::CopyComponents(const TArray<UActorComponent*>& ComponentsToCopy)
{
	FStringOutputDevice Archive;
	const FExportObjectInnerContext Context;

	// Clear the mark state for saving.
	UnMarkAllObjects(EObjectMark(OBJECTMARK_TagExp | OBJECTMARK_TagImp));

	// Duplicate the selected component templates into temporary objects that we can modify
	TMap<FName, FName> ParentMap;
	TMap<FName, UActorComponent*> ObjectMap;
	for (UActorComponent* Component : ComponentsToCopy)
	{
		// Duplicate the component into a temporary object
		UObject* DuplicatedComponent = StaticDuplicateObject(Component, GetTransientPackage(), Component->GetFName(), RF_AllFlags & ~RF_ArchetypeObject);
		if (DuplicatedComponent)
		{
			// If the duplicated component is a scene component, wipe its attach parent (to prevent log warnings for referencing a private object in an external package)
			if (auto DuplicatedCompAsSceneComp = Cast<USceneComponent>(DuplicatedComponent))
			{
				DuplicatedCompAsSceneComp->AttachParent = nullptr;
			}

			// Find the closest parent component of the current component within the list of components to copy
			USceneComponent* ClosestSelectedParent = FindClosestParentInList(Component, ComponentsToCopy);
			if (ClosestSelectedParent)
			{
				// If the parent is included in the list, record it into the node->parent map
				ParentMap.Add(Component->GetFName(), ClosestSelectedParent->GetFName());
			}

			// Record the temporary object into the name->object map
			ObjectMap.Add(Component->GetFName(), CastChecked<UActorComponent>(DuplicatedComponent));
		}
	}

	// Export the component object(s) to text for copying
	for (auto ObjectIt = ObjectMap.CreateIterator(); ObjectIt; ++ObjectIt)
	{
		// Get the component object to be copied
		UActorComponent* ComponentToCopy = ObjectIt->Value;
		check(ComponentToCopy);

		// If this component object had a parent within the selected set
		if (ParentMap.Contains(ComponentToCopy->GetFName()))
		{
			// Get the name of the parent component
			FName ParentName = ParentMap[ComponentToCopy->GetFName()];
			if (ObjectMap.Contains(ParentName))
			{
				// Ensure that this component is a scene component
				USceneComponent* SceneComponent = Cast<USceneComponent>(ComponentToCopy);
				if (SceneComponent)
				{
					// Set the attach parent to the matching parent object in the temporary set. This allows us to preserve hierarchy in the copied set.
					SceneComponent->AttachParent = Cast<USceneComponent>(ObjectMap[ParentName]);
				}
			}
		}

		// Export the component object to the given string
		UExporter::ExportToOutputDevice(&Context, ComponentToCopy, NULL, Archive, TEXT("copy"), 0, PPF_ExportsNotFullyQualified | PPF_Copy | PPF_Delimited, false, ComponentToCopy->GetOuter());
	}

	// Copy text to clipboard
	FString ExportedText = Archive;
	FPlatformMisc::ClipboardCopy(*ExportedText);
}
void SAnimationCompressionPanel::Construct(const FArguments& InArgs)
{
	ParentWindow = InArgs._ParentWindow.Get();

	AnimSequences = InArgs._AnimSequences;
	CompressionHolder = NewObject<UCompressionHolder>();
	CompressionHolder->AddToRoot();

	if (AnimSequences.Num() == 1)
	{
		UAnimSequence* Seq = AnimSequences[0].Get();

		CompressionHolder->Compression = static_cast<UAnimCompress*>(StaticDuplicateObject(Seq->CompressionScheme, CompressionHolder));
	}

	FPropertyEditorModule& EditModule = FModuleManager::Get().GetModuleChecked<FPropertyEditorModule>("PropertyEditor");

	const bool bAllowSearch = true;
	FDetailsViewArgs DetailsViewArgs(/*bUpdateFromSelection=*/ false, /*bLockable=*/ false, bAllowSearch, FDetailsViewArgs::HideNameArea, /*bHideSelectionTip=*/ true);

	TSharedPtr<class IDetailsView> PropertyView = EditModule.CreateDetailView(DetailsViewArgs);

	TArray<UObject*> SelectedObjects;
	SelectedObjects.Add(CompressionHolder);
	PropertyView->SetObjects(SelectedObjects);

	TSharedRef<SVerticalBox> Box =
		SNew(SVerticalBox)
		+ SVerticalBox::Slot()
		.FillHeight(1.f)
		.Padding(8.0f, 4.0f, 8.0f, 4.0f)
		[
			PropertyView.ToSharedRef()
		]
		+SVerticalBox::Slot()
		.AutoHeight()
		.Padding(8.0f, 4.0f, 4.0f, 8.0f)
		[
			SNew(SSeparator)
		]
		+SVerticalBox::Slot()
		.Padding(4.0f)
		.HAlign(HAlign_Right)
		.VAlign(VAlign_Bottom)
		.AutoHeight()
		[
			SNew(SUniformGridPanel)
			.SlotPadding(FEditorStyle::GetMargin("StandardDialog.SlotPadding"))
			.MinDesiredSlotWidth(FEditorStyle::GetFloat("StandardDialog.MinDesiredSlotWidth"))
			.MinDesiredSlotHeight(FEditorStyle::GetFloat("StandardDialog.MinDesiredSlotHeight"))
			+ SUniformGridPanel::Slot(0, 0)
			[
				SNew(SButton)
				.Text(LOCTEXT("AnimCompressionApply", "Apply"))
			.HAlign(HAlign_Center)
			.ContentPadding(FEditorStyle::GetMargin("StandardDialog.ContentPadding"))
			.OnClicked(this, &SAnimationCompressionPanel::ApplyClicked)
			]
		];
	
	this->ChildSlot[Box];
}