ENGINE_API bool UpdateComponentStreamingSectionData(UWorld* InWorld, const FTexCoordScaleMap& InTexCoordScales, bool bIncremental, FSlowTask& BuildTextureStreamingTask)
{
#if WITH_EDITORONLY_DATA
    if (!InWorld) return false;
    const double StartTime = FPlatformTime::Seconds();

    // ====================================================
    // Build per primitive data
    // ====================================================
    const float NumPrimitiveComponents = (float)GetNumTextureStreamingPrimitives(InWorld);
    FScopedSlowTask SlowTask(NumPrimitiveComponents, (LOCTEXT("TextureStreamingBuild_ComponentDataUpdate", "Updating Component Data")));

    for (int32 LevelIndex = 0; LevelIndex < InWorld->GetNumLevels(); LevelIndex++)
    {
        TArray<UPrimitiveComponent*> Components;
        GetTextureStreamingPrimitives(InWorld->GetLevel(LevelIndex), Components);
        for (UPrimitiveComponent* Primitive : Components)
        {
            SlowTask.EnterProgressFrame();
            BuildTextureStreamingTask.EnterProgressFrame(1.f / NumPrimitiveComponents);
            if (GWarn->ReceivedUserCancel()) return false;

            if (bIncremental && Primitive->HasStreamingSectionData(InTexCoordScales.Num() > 0))
                continue;

            Primitive->UpdateStreamingSectionData(InTexCoordScales);
        }
    }
    UE_LOG(TextureStreamingBuild, Display, TEXT("Update Texture Streaming Data took %.3f seconds."), FPlatformTime::Seconds() - StartTime);
    return true;
#else
    return false;
#endif
}
bool WaitForShaderCompilation(const FText& Message, FSlowTask& BuildTextureStreamingTask)
{
    const int32 NumShadersToBeCompiled = GShaderCompilingManager->GetNumRemainingJobs();
    int32 RemainingShaders = NumShadersToBeCompiled;
    if (NumShadersToBeCompiled > 0)
    {
        FScopedSlowTask SlowTask((float)NumShadersToBeCompiled, Message);

        while (RemainingShaders > 0)
        {
            FPlatformProcess::Sleep(0.01f);
            GShaderCompilingManager->ProcessAsyncResults(false, true);

            const int32 RemainingShadersThisFrame = GShaderCompilingManager->GetNumRemainingJobs();
            const int32 NumberOfShadersCompiledThisFrame = RemainingShaders - RemainingShadersThisFrame;

            SlowTask.EnterProgressFrame((float)NumberOfShadersCompiledThisFrame);
            BuildTextureStreamingTask.EnterProgressFrame((float)NumberOfShadersCompiledThisFrame / (float)NumShadersToBeCompiled);
            if (GWarn->ReceivedUserCancel()) return false;

            RemainingShaders = RemainingShadersThisFrame;
        }
    }
    else
    {
        BuildTextureStreamingTask.EnterProgressFrame();
        if (GWarn->ReceivedUserCancel()) return false;
    }

    // Extra safety to make sure every shader map is updated
    GShaderCompilingManager->FinishAllCompilation();
    return true;
}
예제 #3
0
void FModuleDescriptor::LoadModulesForPhase(ELoadingPhase::Type LoadingPhase, const TArray<FModuleDescriptor>& Modules, TMap<FName, EModuleLoadResult>& ModuleLoadErrors)
{
	FScopedSlowTask SlowTask(Modules.Num());
	for(int Idx = 0; Idx < Modules.Num(); Idx++)
	{
		SlowTask.EnterProgressFrame(1);
		const FModuleDescriptor& Descriptor = Modules[Idx];

		// Don't need to do anything if this module is already loaded
		if( !FModuleManager::Get().IsModuleLoaded( Descriptor.Name ) )
		{
			if( LoadingPhase == Descriptor.LoadingPhase && Descriptor.IsLoadedInCurrentConfiguration() )
			{
				// @todo plugin: DLL search problems.  Plugins that statically depend on other modules within this plugin may not be found?  Need to test this.

				// NOTE: Loading this module may cause other modules to become loaded, both in the engine or game, or other modules 
				//       that are part of this project or plugin.  That's totally fine.
				EModuleLoadResult FailureReason;
				const TSharedPtr<IModuleInterface>& ModuleInterface = FModuleManager::Get().LoadModuleWithFailureReason( Descriptor.Name, FailureReason );
				if( ModuleInterface.IsValid() )
				{
					// Module loaded OK (or was already loaded.)
				}
				else 
				{
					// The module failed to load. Note this in the ModuleLoadErrors list.
					ModuleLoadErrors.Add(Descriptor.Name, FailureReason);
				}
			}
		}
	}
}
bool UActorRecording::StopRecording(ULevelSequence* CurrentSequence)
{
	FString ActorName;
	if(CurrentSequence)
	{
		UMovieScene* MovieScene = CurrentSequence->GetMovieScene();
		check(MovieScene);

		FMovieSceneSpawnable* Spawnable = MovieScene->FindSpawnable(Guid);
		if(Spawnable)
		{
			ActorName = Spawnable->GetName();
		}
	}

	FScopedSlowTask SlowTask((float)SectionRecorders.Num() + 1.0f, FText::Format(NSLOCTEXT("SequenceRecorder", "ProcessingActor", "Processing Actor {0}"), FText::FromString(ActorName)));

	// stop property recorders
	for(auto& SectionRecorder : SectionRecorders)
	{
		SlowTask.EnterProgressFrame();

		SectionRecorder->FinalizeSection();
	}

	SlowTask.EnterProgressFrame();

	SectionRecorders.Empty();

	return true;
}
bool FGameProjectGenerationModule::UpdateCodeProject(FText& OutFailReason, FText& OutFailLog)
{
	FScopedSlowTask SlowTask(0, LOCTEXT( "UpdatingCodeProject", "Updating code project..." ) );
	SlowTask.MakeDialog();

	return GameProjectUtils::GenerateCodeProjectFiles(FPaths::GetProjectFilePath(), OutFailReason, OutFailLog);
}
예제 #6
0
void FAssetFixUpRedirectors::LoadReferencingPackages(TArray<FRedirectorRefs>& RedirectorsToFix, TArray<UPackage*>& OutReferencingPackagesToSave) const
{
	FScopedSlowTask SlowTask( RedirectorsToFix.Num(), LOCTEXT( "LoadingReferencingPackages", "Loading Referencing Packages..." ) );
	SlowTask.MakeDialog();

	ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();

	// Load all packages that reference each redirector, if possible
	for ( auto RedirectorRefsIt = RedirectorsToFix.CreateIterator(); RedirectorRefsIt; ++RedirectorRefsIt )
	{
		SlowTask.EnterProgressFrame(1);

		FRedirectorRefs& RedirectorRefs = *RedirectorRefsIt;
		if ( ISourceControlModule::Get().IsEnabled() )
		{
			FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(RedirectorRefs.Redirector->GetOutermost(), EStateCacheUsage::Use);
			const bool bValidSCCState = !SourceControlState.IsValid() || SourceControlState->IsAdded() || SourceControlState->IsCheckedOut() || SourceControlState->CanCheckout() || !SourceControlState->IsSourceControlled() || SourceControlState->IsIgnored();

			if ( !bValidSCCState )
			{
				RedirectorRefs.bRedirectorValidForFixup = false;
				RedirectorRefs.FailureReason = LOCTEXT("RedirectorFixupFailed_BadSCC", "Redirector could not be checked out or marked for delete");
			}
		}

		// Load all referencers
		for ( auto PackageNameIt = RedirectorRefs.ReferencingPackageNames.CreateConstIterator(); PackageNameIt; ++PackageNameIt )
		{
			const FString PackageName = (*PackageNameIt).ToString();

			// Find the package in memory. If it is not in memory, try to load it
			UPackage* Package = FindPackage(NULL, *PackageName);
			if ( !Package )
			{
				Package = LoadPackage(NULL, *PackageName, LOAD_None);
			}

			if ( Package )
			{
				if ( Package->PackageFlags & PKG_CompiledIn )
				{
					// This is a script reference
					RedirectorRefs.bRedirectorValidForFixup = false;
					RedirectorRefs.FailureReason = FText::Format(LOCTEXT("RedirectorFixupFailed_CodeReference", "Redirector is referenced by code. Package: {0}"), FText::FromString(PackageName));
				}
				else
				{
					// If we found a valid package, mark it for save
					OutReferencingPackagesToSave.AddUnique(Package);
				}
			}
		}
	}
}
	void ApplyLODGroupToStaticMeshes(TArray<UStaticMesh*>& StaticMeshes)
	{
		FScopedSlowTask SlowTask(StaticMeshes.Num(), LOCTEXT("ApplyingLODGroups", "Applying LODGroups"));
		SlowTask.MakeDialog();

		for (UStaticMesh* StaticMesh : StaticMeshes)
		{
			FSimplygonLODGroupUtils::ApplyLODGroupToStaticMesh(StaticMesh, LODGroupIndex);

			SlowTask.EnterProgressFrame(1);
		}
	}
void UGameplayCueManager::InitObjectLibraries(TArray<FString> Paths, UObjectLibrary* ActorObjectLibrary, UObjectLibrary* StaticObjectLibrary, FOnGameplayCueNotifySetLoaded OnLoadDelegate, FShouldLoadGCNotifyDelegate ShouldLoadDelegate)
{
	DECLARE_SCOPE_CYCLE_COUNTER(TEXT("Loading Library"), STAT_ObjectLibrary, STATGROUP_LoadTime);

#if WITH_EDITOR
	bAccelerationMapOutdated = false;
	FFormatNamedArguments Args;
	FScopedSlowTask SlowTask(0, FText::Format(NSLOCTEXT("AbilitySystemEditor", "BeginLoadingGameplayCueNotify", "Loading GameplayCue Library"), Args));
	SlowTask.MakeDialog();
#endif

	FScopeCycleCounterUObject PreloadScopeActor(ActorObjectLibrary);
	ActorObjectLibrary->LoadBlueprintAssetDataFromPaths(Paths);
	StaticObjectLibrary->LoadBlueprintAssetDataFromPaths(Paths);

	// ---------------------------------------------------------
	// Determine loading scheme.
	// Sync at startup in commandlets like cook.
	// Async at startup in all other cases
	// ---------------------------------------------------------

	const bool bSyncFullyLoad = IsRunningCommandlet();
	const bool bAsyncLoadAtStartup = !bSyncFullyLoad && ShouldAsyncLoadAtStartup();
	if (bSyncFullyLoad)
	{
#if STATS
		FString PerfMessage = FString::Printf(TEXT("Fully Loaded GameplayCueNotify object library"));
		SCOPE_LOG_TIME_IN_SECONDS(*PerfMessage, nullptr)
#endif
		ActorObjectLibrary->LoadAssetsFromAssetData();
		StaticObjectLibrary->LoadAssetsFromAssetData();
	}

	// ---------------------------------------------------------
	// Look for GameplayCueNotifies that handle events
	// ---------------------------------------------------------
	
	TArray<FAssetData> ActorAssetDatas;
	ActorObjectLibrary->GetAssetDataList(ActorAssetDatas);

	TArray<FAssetData> StaticAssetDatas;
	StaticObjectLibrary->GetAssetDataList(StaticAssetDatas);

	TArray<FGameplayCueReferencePair> CuesToAdd;
	BuildCuesToAddToGlobalSet(ActorAssetDatas, GET_MEMBER_NAME_CHECKED(AGameplayCueNotify_Actor, GameplayCueName), bAsyncLoadAtStartup, CuesToAdd, OnLoadDelegate, ShouldLoadDelegate);
	BuildCuesToAddToGlobalSet(StaticAssetDatas, GET_MEMBER_NAME_CHECKED(UGameplayCueNotify_Static, GameplayCueName), bAsyncLoadAtStartup, CuesToAdd, OnLoadDelegate, ShouldLoadDelegate);

	check(GlobalCueSet);
	GlobalCueSet->AddCues(CuesToAdd);
}
	static void ApplyLODGroup(int32 LODGroupIndex)
	{
		bool bHasActors = false;
		bool bHasValidActors = true;
		TArray<AActor*> SelectedActors;

		//Gather Actors
		USelection* ActorSelection = GEditor->GetSelectedActors();
		for (FSelectionIterator Iter(*ActorSelection); Iter; ++Iter)
		{
			AActor* Actor = Cast<AActor>(*Iter);

			if (Actor)
			{
				SelectedActors.Add(Actor);
			}
		}

		bHasActors = SelectedActors.Num() > 0;
		if (!bHasActors)
		{
			return;
		}

		{
			FScopedSlowTask SlowTask(SelectedActors.Num(), LOCTEXT("ApplyingLODGroups", "Applying LODGroups"));
			SlowTask.MakeDialog();
			for (AActor* Actor : SelectedActors)
			{
				if (!Actor)
				{
					continue;
				}

				TArray<UStaticMeshComponent*> Components;
				Actor->GetComponents<UStaticMeshComponent>(Components);

				for (UStaticMeshComponent* StaticMeshComponent : Components)
				{
					FSimplygonLODGroupUtils::ApplyLODGroupToStaticMesh(StaticMeshComponent->StaticMesh, LODGroupIndex);
				}

				SlowTask.EnterProgressFrame(1);
			}
		}
		
	}
void FMovieScene3DTransformSectionRecorder::FinalizeSection()
{
	FScopedSlowTask SlowTask(4.0f, NSLOCTEXT("SequenceRecorder", "ProcessingTransforms", "Processing Transforms"));

	bRecording = false;

	// if we have a valid animation recorder, we need to build our transforms from the animation
	// so we properly synchronize our keyframes
	if(AnimRecorder.IsValid())
	{
		check(BufferedTransforms.Num() == 0);

		UAnimSequence* AnimSequence = AnimRecorder->GetAnimSequence();
		USkeletalMeshComponent* SkeletalMeshComponent = AnimRecorder->GetSkeletalMeshComponent();
		if (SkeletalMeshComponent)
		{
			USkeletalMesh* SkeletalMesh = SkeletalMeshComponent->MasterPoseComponent != nullptr ? SkeletalMeshComponent->MasterPoseComponent->SkeletalMesh : SkeletalMeshComponent->SkeletalMesh;
			if (AnimSequence && SkeletalMesh)
			{
				// find the root bone
				int32 RootIndex = INDEX_NONE;
				USkeleton* AnimSkeleton = AnimSequence->GetSkeleton();
				for (int32 TrackIndex = 0; TrackIndex < AnimSequence->RawAnimationData.Num(); ++TrackIndex)
				{
					// verify if this bone exists in skeleton
					int32 BoneTreeIndex = AnimSequence->GetSkeletonIndexFromRawDataTrackIndex(TrackIndex);
					if (BoneTreeIndex != INDEX_NONE)
					{
						int32 BoneIndex = AnimSkeleton->GetMeshBoneIndexFromSkeletonBoneIndex(SkeletalMesh, BoneTreeIndex);
						int32 ParentIndex = SkeletalMesh->RefSkeleton.GetParentIndex(BoneIndex);
						if (ParentIndex == INDEX_NONE)
						{
							// found root
							RootIndex = BoneIndex;
							break;
						}
					}
				}

				check(RootIndex != INDEX_NONE);

				const float StartTime = MovieSceneSection->GetStartTime();

				// we may need to offset the transform here if the animation was not recorded on the root component
				FTransform InvComponentTransform = AnimRecorder->GetComponentTransform().Inverse();

				FRawAnimSequenceTrack& RawTrack = AnimSequence->RawAnimationData[RootIndex];
				const int32 KeyCount = FMath::Max(FMath::Max(RawTrack.PosKeys.Num(), RawTrack.RotKeys.Num()), RawTrack.ScaleKeys.Num());
				for (int32 KeyIndex = 0; KeyIndex < KeyCount; KeyIndex++)
				{
					FTransform Transform;
					if (RawTrack.PosKeys.IsValidIndex(KeyIndex))
					{
						Transform.SetTranslation(RawTrack.PosKeys[KeyIndex]);
					}
					else if (RawTrack.PosKeys.Num() > 0)
					{
						Transform.SetTranslation(RawTrack.PosKeys[0]);
					}

					if (RawTrack.RotKeys.IsValidIndex(KeyIndex))
					{
						Transform.SetRotation(RawTrack.RotKeys[KeyIndex]);
					}
					else if (RawTrack.RotKeys.Num() > 0)
					{
						Transform.SetRotation(RawTrack.RotKeys[0]);
					}

					if (RawTrack.ScaleKeys.IsValidIndex(KeyIndex))
					{
						Transform.SetScale3D(RawTrack.ScaleKeys[KeyIndex]);
					}
					else if (RawTrack.ScaleKeys.Num() > 0)
					{
						Transform.SetScale3D(RawTrack.ScaleKeys[0]);
					}

					BufferedTransforms.Add(FBufferedTransformKey(InvComponentTransform * Transform, StartTime + AnimSequence->GetTimeAtFrame(KeyIndex)));
				}
			}
		}
	}

	SlowTask.EnterProgressFrame();

	// Try to 're-wind' rotations that look like axis flips
	// We need to do this as a post-process because the recorder cant reliably access 'wound' rotations:
	// - Net quantize may use quaternions.
	// - Scene components cache transforms as quaternions.
	// - Gameplay is free to clamp/fmod rotations as it sees fit.
	int32 TransformCount = BufferedTransforms.Num();
	for(int32 TransformIndex = 0; TransformIndex < TransformCount - 1; TransformIndex++)
	{
		FRotator& Rotator = BufferedTransforms[TransformIndex].WoundRotation;
		FRotator& NextRotator = BufferedTransforms[TransformIndex + 1].WoundRotation;

		FMath::WindRelativeAnglesDegrees(Rotator.Pitch, NextRotator.Pitch);
		FMath::WindRelativeAnglesDegrees(Rotator.Yaw, NextRotator.Yaw);
		FMath::WindRelativeAnglesDegrees(Rotator.Roll, NextRotator.Roll);
	}

	SlowTask.EnterProgressFrame();

	// never unwind rotations
	const bool bUnwindRotation = false;
	// If we are syncing to an animation, use linear interpolation to avoid foot sliding etc. 
	// Otherwise use cubic for better quality (much better for projectiles etc.)
	const EMovieSceneKeyInterpolation Interpolation = AnimRecorder.IsValid() ? EMovieSceneKeyInterpolation::Linear : EMovieSceneKeyInterpolation::Auto;

	// add buffered transforms
	for(const FBufferedTransformKey& BufferedTransform : BufferedTransforms)
	{
		const FVector Translation = BufferedTransform.Transform.GetTranslation();
		const FVector Rotation = BufferedTransform.WoundRotation.Euler();
		const FVector Scale = BufferedTransform.Transform.GetScale3D();

		MovieSceneSection->AddKey(BufferedTransform.KeyTime, FTransformKey(EKey3DTransformChannel::Translation, EAxis::X, Translation.X, bUnwindRotation), Interpolation);
		MovieSceneSection->AddKey(BufferedTransform.KeyTime, FTransformKey(EKey3DTransformChannel::Translation, EAxis::Y, Translation.Y, bUnwindRotation), Interpolation);
		MovieSceneSection->AddKey(BufferedTransform.KeyTime, FTransformKey(EKey3DTransformChannel::Translation, EAxis::Z, Translation.Z, bUnwindRotation), Interpolation);

		MovieSceneSection->AddKey(BufferedTransform.KeyTime, FTransformKey(EKey3DTransformChannel::Rotation, EAxis::X, Rotation.X, bUnwindRotation), Interpolation);
		MovieSceneSection->AddKey(BufferedTransform.KeyTime, FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Y, Rotation.Y, bUnwindRotation), Interpolation);
		MovieSceneSection->AddKey(BufferedTransform.KeyTime, FTransformKey(EKey3DTransformChannel::Rotation, EAxis::Z, Rotation.Z, bUnwindRotation), Interpolation);

		MovieSceneSection->AddKey(BufferedTransform.KeyTime, FTransformKey(EKey3DTransformChannel::Scale, EAxis::X, Scale.X, bUnwindRotation), Interpolation);
		MovieSceneSection->AddKey(BufferedTransform.KeyTime, FTransformKey(EKey3DTransformChannel::Scale, EAxis::Y, Scale.Y, bUnwindRotation), Interpolation);
		MovieSceneSection->AddKey(BufferedTransform.KeyTime, FTransformKey(EKey3DTransformChannel::Scale, EAxis::Z, Scale.Z, bUnwindRotation), Interpolation);
	}

	BufferedTransforms.Empty();

	SlowTask.EnterProgressFrame();

	// now remove linear keys
	TPair<FRichCurve*, float> CurvesAndTolerances[] =
	{
		TPairInitializer<FRichCurve*, float>(&MovieSceneSection->GetTranslationCurve(EAxis::X), KINDA_SMALL_NUMBER),
		TPairInitializer<FRichCurve*, float>(&MovieSceneSection->GetTranslationCurve(EAxis::Y), KINDA_SMALL_NUMBER),
		TPairInitializer<FRichCurve*, float>(&MovieSceneSection->GetTranslationCurve(EAxis::Z), KINDA_SMALL_NUMBER),
		TPairInitializer<FRichCurve*, float>(&MovieSceneSection->GetRotationCurve(EAxis::X), KINDA_SMALL_NUMBER),
		TPairInitializer<FRichCurve*, float>(&MovieSceneSection->GetRotationCurve(EAxis::Y), KINDA_SMALL_NUMBER),
		TPairInitializer<FRichCurve*, float>(&MovieSceneSection->GetRotationCurve(EAxis::Z), KINDA_SMALL_NUMBER),
		TPairInitializer<FRichCurve*, float>(&MovieSceneSection->GetScaleCurve(EAxis::X), KINDA_SMALL_NUMBER),
		TPairInitializer<FRichCurve*, float>(&MovieSceneSection->GetScaleCurve(EAxis::Y), KINDA_SMALL_NUMBER),
		TPairInitializer<FRichCurve*, float>(&MovieSceneSection->GetScaleCurve(EAxis::Z), KINDA_SMALL_NUMBER),
	};

	for(TPair<FRichCurve*, float>& CurveAndTolerance : CurvesAndTolerances)
	{
		CurveAndTolerance.Key->RemoveRedundantKeys(CurveAndTolerance.Value);
	}

	// we cant remove redundant tracks if we were attached as the playback relies on update order of
	// transform tracks. Without this track, relative transforms would accumulate.
	if(!bWasAttached)
	{
		// now we have reduced our keys, if we dont have any, remove the section as it is redundant
		if( MovieSceneSection->GetTranslationCurve(EAxis::X).Keys.Num() == 0 && 
			MovieSceneSection->GetTranslationCurve(EAxis::Y).Keys.Num() == 0 && 
			MovieSceneSection->GetTranslationCurve(EAxis::Z).Keys.Num() == 0 && 
			MovieSceneSection->GetRotationCurve(EAxis::X).Keys.Num() == 0 && 
			MovieSceneSection->GetRotationCurve(EAxis::Y).Keys.Num() == 0 && 
			MovieSceneSection->GetRotationCurve(EAxis::Z).Keys.Num() == 0 && 
			MovieSceneSection->GetScaleCurve(EAxis::X).Keys.Num() == 0 && 
			MovieSceneSection->GetScaleCurve(EAxis::Y).Keys.Num() == 0 && 
			MovieSceneSection->GetScaleCurve(EAxis::Z).Keys.Num() == 0)
		{
			if(DefaultTransform.Equals(FTransform::Identity))
			{
				MovieScene->RemoveTrack(*MovieSceneTrack.Get());
			}
		}
	}

	SlowTask.EnterProgressFrame();
}
예제 #11
0
TShaderMap<FGlobalShaderType>* GetGlobalShaderMap(EShaderPlatform Platform, bool bRefreshShaderMap)
{
	// No global shaders needed on dedicated server
	if (FPlatformProperties::IsServerOnly())
	{
		if (!GGlobalShaderMap[Platform])
		{
			GGlobalShaderMap[Platform] = new TShaderMap<FGlobalShaderType>();
			return GGlobalShaderMap[Platform];
		}
		return NULL;
	}

	if (bRefreshShaderMap)
	{
		// delete the current global shader map
		delete GGlobalShaderMap[Platform];
		GGlobalShaderMap[Platform] = NULL;

		// make sure we look for updated shader source files
		FlushShaderFileCache();
	}

	// If the global shader map hasn't been created yet, create it.
	if(!GGlobalShaderMap[Platform])
	{
		// GetGlobalShaderMap is called the first time during startup in the main thread.
		check(IsInGameThread());

		FScopedSlowTask SlowTask(70);

		// verify that all shader source files are intact
		SlowTask.EnterProgressFrame(20);
		VerifyShaderSourceFiles();

		GGlobalShaderMap[Platform] = new TShaderMap<FGlobalShaderType>();

		bool bLoadedFromCacheFile = false;

		// Try to load the global shaders from a local cache file if it exists
		// This method is used exclusively with cooked content, since the DDC is not present
		if (FPlatformProperties::RequiresCookedData())
		{
			SlowTask.EnterProgressFrame(50);

			TArray<uint8> GlobalShaderData;
			FString GlobalShaderCacheFilename = FPaths::GetRelativePathToRoot() / GetGlobalShaderCacheFilename(Platform);
			FPaths::MakeStandardFilename(GlobalShaderCacheFilename);
			bLoadedFromCacheFile = FFileHelper::LoadFileToArray(GlobalShaderData, *GlobalShaderCacheFilename, FILEREAD_Silent);

			if (!bLoadedFromCacheFile)
			{
				// Handle this gracefully and exit.
				FString SandboxPath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite(*GlobalShaderCacheFilename);				
				// This can be too early to localize in some situations.
				const FText Message = FText::Format( NSLOCTEXT("Engine", "GlobalShaderCacheFileMissing", "The global shader cache file '{0}' is missing.\n\nYou're running a version of the application built to load COOKED content only, however no COOKED content was found. Consider cooking content for this build, or build and run the UNCOOKED version of the application instead."), FText::FromString( SandboxPath ) );
				if (FPlatformProperties::SupportsWindowedMode())
				{
					UE_LOG(LogMaterial, Error, TEXT("%s"), *Message.ToString());
					FMessageDialog::Open(EAppMsgType::Ok, Message);
					FPlatformMisc::RequestExit(false);
					return NULL;
				}
				else
				{
					UE_LOG(LogMaterial, Fatal, TEXT("%s"), *Message.ToString());
				}
			}

			FMemoryReader MemoryReader(GlobalShaderData);
			SerializeGlobalShaders(MemoryReader, GGlobalShaderMap[Platform]);
		}
		// Uncooked platform
		else
		{
			FGlobalShaderMapId ShaderMapId(Platform);

			TArray<uint8> CachedData;
			SlowTask.EnterProgressFrame(40);
			const FString DataKey = GetGlobalShaderMapKeyString(ShaderMapId, Platform);

			// Find the shader map in the derived data cache
			SlowTask.EnterProgressFrame(10);
			if (GetDerivedDataCacheRef().GetSynchronous(*DataKey, CachedData))
			{
				FMemoryReader Ar(CachedData, true);

				// Deserialize from the cached data
				SerializeGlobalShaders(Ar, GGlobalShaderMap[Platform]);
			}
		}

		// If any shaders weren't loaded, compile them now.
		VerifyGlobalShaders(Platform, bLoadedFromCacheFile);

		extern int32 GCreateShadersOnLoad;
		if (GCreateShadersOnLoad && Platform == GMaxRHIShaderPlatform)
		{
			for (TMap<FShaderType*, TRefCountPtr<FShader> >::TConstIterator ShaderIt(GGlobalShaderMap[Platform]->GetShaders()); ShaderIt; ++ShaderIt)
			{
				FShader* Shader = ShaderIt.Value();

				if (Shader)
				{
					Shader->InitializeResource();
				}
			}
		}
	}
	return GGlobalShaderMap[Platform];
}
예제 #12
0
void UEditorEngine::csgRebuild( UWorld* InWorld )
{
	GWarn->BeginSlowTask( NSLOCTEXT("UnrealEd", "RebuildingGeometry", "Rebuilding geometry"), false );
	FBSPOps::GFastRebuild = 1;

	FinishAllSnaps();

	// Empty the model out.
	InWorld->GetModel()->Modify();
	InWorld->GetModel()->EmptyModel(1, 1);

	// Count brushes.
	int32 BrushTotal=0, BrushCount=0;
	for( FStaticBrushIterator It(InWorld); It; ++It )
	{
		ABrush* Brush = CastChecked<ABrush>(*It);
		if( !FActorEditorUtils::IsABuilderBrush(Brush) )
		{
			BrushTotal++;
		}
	}

	// Check for the giant cube brush that is created for subtractive levels.
	// If it's found, apply the RemoveSurfaceMaterial to its polygons so they aren't lit or drawn.
	for(FStaticBrushIterator GiantCubeBrushIt(InWorld);GiantCubeBrushIt;++GiantCubeBrushIt)
	{
		ABrush* GiantCubeBrush = CastChecked<ABrush>(*GiantCubeBrushIt);
		if(GiantCubeBrush->Brush && GiantCubeBrush->Brush->Bounds.SphereRadius > HALF_WORLD_MAX)
		{
			check(GiantCubeBrush->Brush->Polys);
			for(int32 PolyIndex = 0;PolyIndex < GiantCubeBrush->Brush->Polys->Element.Num();PolyIndex++)
			{
				FPoly& Polygon = GiantCubeBrush->Brush->Polys->Element[PolyIndex];
				const float PolygonArea = Polygon.Area();
				if(PolygonArea > WORLD_MAX * WORLD_MAX * 0.99f && PolygonArea < WORLD_MAX * WORLD_MAX * 1.01f)
				{
					Polygon.Material = GEngine->RemoveSurfaceMaterial;
				}
			}
		}
	}

	// Compose all structural brushes and portals.
	for( FStaticBrushIterator It(InWorld); It; ++It )
	{	
		ABrush* Brush = CastChecked<ABrush>(*It);
		if( !FActorEditorUtils::IsABuilderBrush(Brush) )
		{
			if
			(  !(Brush->PolyFlags&PF_Semisolid)
			||	(Brush->BrushType!=Brush_Add)
			||	(Brush->PolyFlags&PF_Portal) )
			{
				// Treat portals as solids for cutting.
				if( Brush->PolyFlags & PF_Portal )
				{
					Brush->PolyFlags = (Brush->PolyFlags & ~PF_Semisolid) | PF_NotSolid;
				}
				BrushCount++;

				FFormatNamedArguments Args;
				Args.Add( TEXT("BrushCount"), BrushCount );
				Args.Add( TEXT("BrushTotal"), BrushTotal );
				GWarn->StatusUpdate( BrushCount, BrushTotal, FText::Format( NSLOCTEXT("UnrealEd", "ApplyingStructuralBrushF", "Applying structural brush {BrushCount} of {BrushTotal}"), Args ) );

				Brush->Modify();
				bspBrushCSG( Brush, InWorld->GetModel(), Brush->PolyFlags, (EBrushType)Brush->BrushType, CSG_None, false, true, false );
			}
		}
	}

	// Repartition the structural BSP.
	{
		GWarn->StatusUpdate( 0, 4, NSLOCTEXT("UnrealEd", "RebuildBSPBuildingPolygons", "Rebuild BSP: Building polygons") );
		bspBuildFPolys( InWorld->GetModel(), 1, 0 );

		GWarn->StatusUpdate( 1, 4, NSLOCTEXT("UnrealEd", "RebuildBSPMergingPlanars", "Rebuild BSP: Merging planars") );
		bspMergeCoplanars( InWorld->GetModel(), 0, 0 );

		GWarn->StatusUpdate( 2, 4, NSLOCTEXT("UnrealEd", "RebuildBSPPartitioning", "Rebuild BSP: Partitioning") );
		FBSPOps::bspBuild( InWorld->GetModel(), FBSPOps::BSP_Optimal, 15, 70, 0, 0 );

		GWarn->UpdateProgress( 4, 4 );
	}

	// Remember leaves.
	TArray<int32> iFronts, iBacks;
	if( InWorld->GetModel()->Nodes.Num() )
	{
		EnlistLeaves( InWorld->GetModel(), iFronts, iBacks, 0 );
	}

	// Compose all detail brushes.
	for( FStaticBrushIterator It(InWorld); It; ++It )
	{
		ABrush* Brush = CastChecked<ABrush>(*It);
		if
		(	!FActorEditorUtils::IsABuilderBrush(Brush)
		&&	(Brush->PolyFlags&PF_Semisolid)
		&& !(Brush->PolyFlags&PF_Portal)
		&&	Brush->BrushType==Brush_Add )
		{
			BrushCount++;

			FFormatNamedArguments Args;
			Args.Add( TEXT("BrushCount"), BrushCount );
			Args.Add( TEXT("BrushTotal"), BrushTotal );
			GWarn->StatusUpdate( BrushCount, BrushTotal, FText::Format( NSLOCTEXT("UnrealEd", "ApplyingDetailBrushF", "Applying detail brush {BrushCount} of {BrushTotal}"), Args ) );

			Brush->Modify();
			bspBrushCSG( Brush, InWorld->GetModel(), Brush->PolyFlags, (EBrushType)Brush->BrushType, CSG_None, false, true, false );
		}
	}

	// Optimize the sub-bsp's.
	GWarn->StatusUpdate( 0, 4, NSLOCTEXT("UnrealEd", "RebuildCSGOptimizingSubBSPs", "Rebuild CSG: Optimizing Sub-BSPs") );
	int32 iNode;
	for( TArray<int32>::TIterator ItF(iFronts); ItF; ++ItF )
	{
		if( (iNode=InWorld->GetModel()->Nodes[*ItF].iFront)!=INDEX_NONE )
		{
			bspRepartition( InWorld, iNode );
		}
	}
	for( TArray<int32>::TIterator ItB(iBacks); ItB; ++ItB )
	{
		if( (iNode=InWorld->GetModel()->Nodes[*ItB].iBack)!=INDEX_NONE )
		{
			bspRepartition( InWorld, iNode );
		}
	}

	GWarn->StatusUpdate( 1, 4, NSLOCTEXT("UnrealEd", "RebuildBSPOptimizingGeometry", "Rebuild BSP: Optimizing geometry") );
	bspOptGeom( InWorld->GetModel() );

	// Build bounding volumes.
	GWarn->StatusUpdate( 2, 4,  NSLOCTEXT("UnrealEd", "RebuildCSGBuildingBoundingVolumes", "Rebuild CSG: Building Bounding Volumes") );
	FBSPOps::bspBuildBounds( InWorld->GetModel() );

	// Rebuild dynamic brush BSP's.
	GWarn->StatusUpdate( 3, 4,  NSLOCTEXT("UnrealEd", "RebuildCSGRebuildingDynamicBrushBSPs", "Rebuild CSG: Rebuilding Dynamic Brush BSPs") );

	TArray<ABrush*> DynamicBrushes;
	DynamicBrushes.Empty();
	for( TActorIterator<ABrush> It(InWorld); It; ++It )
	{
		ABrush* B=*It;
		if ( B->Brush && !B->IsStaticBrush() )
		{
			DynamicBrushes.Add(B);
		}
	}

	{
		FScopedSlowTask SlowTask(DynamicBrushes.Num(), NSLOCTEXT("UnrealEd", "RebuildCSGRebuildingDynamicBrushBSPs", "Rebuild CSG: Rebuilding Dynamic Brush BSPs") );
		for ( int32 BrushIndex = 0; BrushIndex < DynamicBrushes.Num(); BrushIndex++ )
		{
			SlowTask.EnterProgressFrame();

			ABrush* B = DynamicBrushes[BrushIndex];
			FBSPOps::csgPrepMovingBrush(B);
			
			if ( GEngine->GetMapBuildCancelled() )
			{
				break;
			}
		}
	}

	GWarn->UpdateProgress( 4, 4 );

	// update static navigable geometry in current level
	RebuildStaticNavigableGeometry(InWorld->GetCurrentLevel());

	// Empty EdPolys.
	InWorld->GetModel()->Polys->Element.Empty();

	// Done.
	FBSPOps::GFastRebuild = 0;
	InWorld->GetCurrentLevel()->MarkPackageDirty();
	GWarn->EndSlowTask();
}
bool FPluginManager::LoadModulesForEnabledPlugins( const ELoadingPhase::Type LoadingPhase )
{
	// Figure out which plugins are enabled
	if(!ConfigureEnabledPlugins())
	{
		return false;
	}

	FScopedSlowTask SlowTask(AllPlugins.Num());

	// Load plugins!
	for( const TSharedRef< FPlugin > Plugin : AllPlugins )
	{
		SlowTask.EnterProgressFrame(1);

		if ( Plugin->bEnabled )
		{
			TMap<FName, EModuleLoadResult> ModuleLoadFailures;
			FModuleDescriptor::LoadModulesForPhase(LoadingPhase, Plugin->Descriptor.Modules, ModuleLoadFailures);

			FText FailureMessage;
			for( auto FailureIt( ModuleLoadFailures.CreateConstIterator() ); FailureIt; ++FailureIt )
			{
				const auto ModuleNameThatFailedToLoad = FailureIt.Key();
				const auto FailureReason = FailureIt.Value();

				if( FailureReason != EModuleLoadResult::Success )
				{
					const FText PluginNameText = FText::FromString(Plugin->Name);
					const FText TextModuleName = FText::FromName(FailureIt.Key());

					if ( FailureReason == EModuleLoadResult::FileNotFound )
					{
						FailureMessage = FText::Format( LOCTEXT("PluginModuleNotFound", "Plugin '{0}' failed to load because module '{1}' could not be found.  Please ensure the plugin is properly installed, otherwise consider disabling the plugin for this project."), PluginNameText, TextModuleName );
					}
					else if ( FailureReason == EModuleLoadResult::FileIncompatible )
					{
						FailureMessage = FText::Format( LOCTEXT("PluginModuleIncompatible", "Plugin '{0}' failed to load because module '{1}' does not appear to be compatible with the current version of the engine.  The plugin may need to be recompiled."), PluginNameText, TextModuleName );
					}
					else if ( FailureReason == EModuleLoadResult::CouldNotBeLoadedByOS )
					{
						FailureMessage = FText::Format( LOCTEXT("PluginModuleCouldntBeLoaded", "Plugin '{0}' failed to load because module '{1}' could not be loaded.  There may be an operating system error or the module may not be properly set up."), PluginNameText, TextModuleName );
					}
					else if ( FailureReason == EModuleLoadResult::FailedToInitialize )
					{
						FailureMessage = FText::Format( LOCTEXT("PluginModuleFailedToInitialize", "Plugin '{0}' failed to load because module '{1}' could be initialized successfully after it was loaded."), PluginNameText, TextModuleName );
					}
					else 
					{
						ensure(0);	// If this goes off, the error handling code should be updated for the new enum values!
						FailureMessage = FText::Format( LOCTEXT("PluginGenericLoadFailure", "Plugin '{0}' failed to load because module '{1}' could not be loaded for an unspecified reason.  This plugin's functionality will not be available.  Please report this error."), PluginNameText, TextModuleName );
					}

					// Don't need to display more than one module load error per plugin that failed to load
					break;
				}
			}

			if( !FailureMessage.IsEmpty() )
			{
				FMessageDialog::Open(EAppMsgType::Ok, FailureMessage);
				return false;
			}
		}
	}
	return true;
}
/**
 * Build Shaders to compute tex coord scale per texture.
 *
 * @param InWorld			The world to build streaming for.
 * @param QualityLevel		The quality level for the shaders.
 * @param FeatureLevel		The feature level for the shaders.
 * @param TexCoordScales	The map to store material interfaces used by the current primitive world.
 * @param MaterialToLevels	The map to bind a material to all levels where primitives are refering it.
 * @param bIncremental		true if the build should only update components that have no data.
 * @return true if the operation is a success, false if it was canceled.
 */
ENGINE_API bool BuildTextureStreamingShaders(UWorld* InWorld, EMaterialQualityLevel::Type QualityLevel, ERHIFeatureLevel::Type FeatureLevel, OUT FTexCoordScaleMap& TexCoordScales, OUT FMaterialToLevelsMap& MaterialToLevels, bool bIncremental, FSlowTask& BuildTextureStreamingTask)
{
#if WITH_EDITORONLY_DATA

    if (!InWorld || !GShaderCompilingManager) return false;

    const bool bUseNewMetrics = CVarStreamingUseNewMetrics.GetValueOnGameThread() != 0;
    const double StartTime = FPlatformTime::Seconds();

    FlushRenderingCommands();

    if (!WaitForShaderCompilation(LOCTEXT("TextureStreamingBuild_FinishPendingShadersCompilation", "Waiting For Pending Shaders Compilation"), BuildTextureStreamingTask))
    {
        return false;
    }

    if (!bIncremental)
    {
        FDebugViewModeMaterialProxy::ClearAllShaders();
    }
    else // Otherwise, if there are no shaders, then we need to rebuild them all.
    {
        bIncremental = FDebugViewModeMaterialProxy::HasAnyShaders();
    }

    // Without the new metrics, the shaders will are not compiled, making the texcoord scale viewmode non functional.
    if (bUseNewMetrics && AllowDebugViewPS(DVSM_MaterialTexCoordScalesAnalysis, GetFeatureLevelShaderPlatform(FeatureLevel)))
    {
        const float NumPrimitiveComponents = (float)GetNumTextureStreamingPrimitives(InWorld);

        {
            FScopedSlowTask SlowTask(NumPrimitiveComponents, (LOCTEXT("TextureStreamingBuild_ParsingPrimitiveMaterials ", "Parsing Primitive Materials")));
            for (int32 LevelIndex = 0; LevelIndex < InWorld->GetNumLevels(); LevelIndex++)
            {
                ULevel* Level = InWorld->GetLevel(LevelIndex);

                TArray<UPrimitiveComponent*> Components;
                GetTextureStreamingPrimitives(Level, Components);
                for (UPrimitiveComponent* Primitive : Components)
                {
                    SlowTask.EnterProgressFrame();
                    BuildTextureStreamingTask.EnterProgressFrame(1.f / NumPrimitiveComponents);
                    if (GWarn->ReceivedUserCancel())
                    {
                        FDebugViewModeMaterialProxy::ClearAllShaders();
                        return false;
                    }

                    // When only updating (viewmode path), skip primitives that already have data.
                    if (bIncremental && Primitive->HasStreamingSectionData(true))
                        continue;

                    TArray<UMaterialInterface*> Materials;
                    Primitive->GetUsedMaterials(Materials);

                    for (UMaterialInterface* MaterialInterface : Materials)
                    {
                        if (!MaterialInterface) continue;

                        // Landscape material resources can not be used. See logic in FLandscapeMaterialResource::ShouldCache().
                        const FMaterial* Material = MaterialInterface->GetMaterialResource(FeatureLevel);
                        if (!Material || Material->IsUsedWithLandscape())
                        {
                            UE_LOG(TextureStreamingBuild, Verbose, TEXT("Landscape material %s not supported, skipping shader"), *MaterialInterface->GetName());
                            continue;
                        }

                        if (!TexCoordScales.Contains(MaterialInterface))
                        {
                            TexCoordScales.Add(MaterialInterface);
                            FDebugViewModeMaterialProxy::AddShader(MaterialInterface, QualityLevel, FeatureLevel, EMaterialShaderMapUsage::DebugViewModeTexCoordScale);
                        }
                        MaterialToLevels.FindOrAdd(MaterialInterface).AddUnique(Level);
                    }
                }
            }
        }

        if (WaitForShaderCompilation(LOCTEXT("TextureStreamingBuild_CompTexCoordScaleShaders", "Compiling Shaders For TexCoord Scale Analysis "), BuildTextureStreamingTask))
        {
            // Check The validity of all shaders, removing invalid entries
            FDebugViewModeMaterialProxy::ValidateAllShaders(TexCoordScales);
            return true;
        }
        else
        {
            FDebugViewModeMaterialProxy::ClearAllShaders();
            return false;
        }
    }
    else
    {
        return true; // Nothing to build but this is not an error.
    }

#else
    UE_LOG(TextureStreamingBuild, Fatal,TEXT("Build Texture Streaming Shaders should not be called on a console"));
    return false;
#endif
}
예제 #15
0
void FHierarchicalLODBuilder::MergeClustersAndBuildActors(ULevel* InLevel, const int32 LODIdx, float HighestCost, int32 MinNumActors, const bool bCreateMeshes)
{	
	if (Clusters.Num() > 0)
	{
		FString LevelName = FPackageName::GetShortName(InLevel->GetOutermost()->GetName());
		FFormatNamedArguments Arguments;
		Arguments.Add(TEXT("LODIndex"), FText::AsNumber(LODIdx+1));
		Arguments.Add(TEXT("LevelName"), FText::FromString(LevelName));
		// merge clusters first
		{
			SCOPE_LOG_TIME(TEXT("HLOD_MergeClusters"), nullptr);
			static int32 TotalIteration=3;
			const int32 TotalCluster = Clusters.Num();

			FScopedSlowTask SlowTask(TotalIteration*TotalCluster, FText::Format( LOCTEXT("HierarchicalLOD_BuildClusters", "Building Clusters for LOD {LODIndex} of {LevelName}..."), Arguments) );
			SlowTask.MakeDialog();

			for(int32 Iteration=0; Iteration<TotalIteration; ++Iteration)
			{
				// now we have minimum Clusters
				for(int32 ClusterId=0; ClusterId < TotalCluster; ++ClusterId)
				{
					auto& Cluster = Clusters[ClusterId];
					UE_LOG(LogLODGenerator, Verbose, TEXT("%d. %0.2f {%s}"), ClusterId+1, Cluster.GetCost(), *Cluster.ToString());

					// progress bar update
					SlowTask.EnterProgressFrame();

					if(Cluster.IsValid())
					{
						for(int32 MergedClusterId=0; MergedClusterId < ClusterId; ++MergedClusterId)
						{
							// compare with previous clusters
							auto& MergedCluster = Clusters[MergedClusterId];
							// see if it's valid, if it contains, check the cost
							if(MergedCluster.IsValid())
							{
								if(MergedCluster.Contains(Cluster))
								{
									// if valid, see if it contains any of this actors
									// merge whole clusters
									FLODCluster NewCluster = Cluster + MergedCluster;
									float MergeCost = NewCluster.GetCost();

									// merge two clusters
									if(MergeCost <= HighestCost)
									{
										UE_LOG(LogLODGenerator, Log, TEXT("Merging of Cluster (%d) and (%d) with merge cost (%0.2f) "), ClusterId+1, MergedClusterId+1, MergeCost);

										MergedCluster = NewCluster;
										// now this cluster is invalid
										Cluster.Invalidate();
										break;
									}
									else
									{
										Cluster -= MergedCluster;
									}
								}
							}
						}

						UE_LOG(LogLODGenerator, Verbose, TEXT("Processed(%s): %0.2f {%s}"), Cluster.IsValid()? TEXT("Valid"):TEXT("Invalid"), Cluster.GetCost(), *Cluster.ToString());
					}
				}
			}
		}


		if (LODIdx == 0)
		{
			for (auto& Cluster : HLODVolumeClusters)
			{
				Clusters.Add(Cluster.Value);
			}
		}
		

		// debug flag, so that I can just see clustered data since no visualization in the editor yet
		//static bool bBuildActor=true;

		//if (bBuildActors)
		{
			SCOPE_LOG_TIME(TEXT("HLOD_BuildActors"), nullptr);
			// print data
			int32 TotalValidCluster=0;
			for(auto& Cluster: Clusters)
			{
				if(Cluster.IsValid())
				{
					++TotalValidCluster;
				}
			}

			FScopedSlowTask SlowTask(TotalValidCluster, FText::Format( LOCTEXT("HierarchicalLOD_MergeActors", "Merging Actors for LOD {LODIndex} of {LevelName}..."), Arguments) );
			SlowTask.MakeDialog();

			for(auto& Cluster: Clusters)
			{
				if(Cluster.IsValid())
				{
					SlowTask.EnterProgressFrame();

					if (Cluster.Actors.Num() >= MinNumActors)
					{
						Cluster.BuildActor(InLevel, LODIdx, bCreateMeshes);
					}
				}
			}
		}
	}
}
예제 #16
0
void FHierarchicalLODBuilder::BuildClusters(ULevel* InLevel, const bool bCreateMeshes)
{	
	SCOPE_LOG_TIME(TEXT("STAT_HLOD_BuildClusters"), nullptr);

	// I'm using stack mem within this scope of the function
	// so we need this
	FMemMark Mark(FMemStack::Get());

	// you still have to delete all objects just in case they had it and didn't want it anymore
	TArray<UObject*> AssetsToDelete;
	for (int32 ActorId=InLevel->Actors.Num()-1; ActorId >= 0; --ActorId)
	{
		ALODActor* LodActor = Cast<ALODActor>(InLevel->Actors[ActorId]);
		if (LodActor)
		{
			for (auto& Asset: LodActor->SubObjects)
			{
				// @TOOD: This is not permanent fix
				if (Asset)
				{
					AssetsToDelete.Add(Asset);
				}
			}
			World->DestroyActor(LodActor);
		}
	}

	ULevel::BuildStreamingData(InLevel->OwningWorld, InLevel);

	for (auto& Asset : AssetsToDelete)
	{
		Asset->MarkPendingKill();
		ObjectTools::DeleteSingleObject(Asset, false);
	}
	
	// garbage collect
	CollectGarbage( GARBAGE_COLLECTION_KEEPFLAGS, true );

	// only build if it's enabled
	if(InLevel->GetWorld()->GetWorldSettings()->bEnableHierarchicalLODSystem && InLevel->GetWorld()->GetWorldSettings()->HierarchicalLODSetup.Num() != 0)
	{
		// Handle HierachicalLOD volumes first
		HandleHLODVolumes(InLevel);

		AWorldSettings* WorldSetting = InLevel->GetWorld()->GetWorldSettings();		
		const int32 TotalNumLOD = InLevel->GetWorld()->GetWorldSettings()->HierarchicalLODSetup.Num();
		for(int32 LODId=0; LODId<TotalNumLOD; ++LODId)
		{
			
			// we use meter for bound. Otherwise it's very easy to get to overflow and have problem with filling ratio because
			// bound is too huge
			const float DesiredBoundRadius = WorldSetting->HierarchicalLODSetup[LODId].DesiredBoundRadius * CM_TO_METER;
			const float DesiredFillingRatio = WorldSetting->HierarchicalLODSetup[LODId].DesiredFillingPercentage * 0.01f;
			ensure(DesiredFillingRatio!=0.f);
			const float HighestCost = FMath::Pow(DesiredBoundRadius, 3) / (DesiredFillingRatio);
			const int32 MinNumActors = WorldSetting->HierarchicalLODSetup[LODId].MinNumberOfActorsToBuild;
			check (MinNumActors > 0);
			// test parameter I was playing with to cull adding to the array
			// intialization can have too many elements, decided to cull
			// the problem can be that we can create disconnected tree
			// my assumption is that if the merge cost is too high, then it's not worth merge anyway
			static int32 CullMultiplier=1;

			// since to show progress of initialization, I'm scoping it
			{
				FString LevelName = FPackageName::GetShortName(InLevel->GetOutermost()->GetName());
				FFormatNamedArguments Arguments;
				Arguments.Add(TEXT("LODIndex"), FText::AsNumber(LODId+1));
				Arguments.Add(TEXT("LevelName"), FText::FromString(LevelName));

				FScopedSlowTask SlowTask(100, FText::Format(LOCTEXT("HierarchicalLOD_InitializeCluster", "Initializing Clusters for LOD {LODIndex} of {LevelName}..."), Arguments));
				SlowTask.MakeDialog();

				// initialize Clusters
				InitializeClusters(InLevel, LODId, HighestCost*CullMultiplier);

				// move a half way - I know we can do this better but as of now this is small progress
				SlowTask.EnterProgressFrame(50);

				// now we have all pair of nodes
				FindMST();
			}

			// now we have to calculate merge clusters and build actors
			MergeClustersAndBuildActors(InLevel, LODId, HighestCost, MinNumActors, bCreateMeshes);
		}
	}
	else
	{
		// Fire map check warnings if HLOD System is not enabled
		FMessageLog MapCheck("MapCheck");
		MapCheck.Warning()
			->AddToken(FUObjectToken::Create(InLevel->GetWorld()->GetWorldSettings()))
			->AddToken(FTextToken::Create(LOCTEXT("MapCheck_Message_HLODSystemNotEnabled", "Hierarchical LOD System is disabled or no HLOD level settings available, unable to build LOD actors.")))
			->AddToken(FMapErrorToken::Create(FMapErrors::HLODSystemNotEnabled));
	}

	// Clear Clusters. It is using stack mem, so it won't be good after this
	Clusters.Empty();
	Clusters.Shrink();
}
예제 #17
0
bool FMeshMergingTool::RunMerge(const FString& PackageName)
{
	IMeshUtilities& MeshUtilities = FModuleManager::Get().LoadModuleChecked<IMeshUtilities>("MeshUtilities");
	USelection* SelectedActors = GEditor->GetSelectedActors();
	TArray<AActor*> Actors;
	TArray<ULevel*> UniqueLevels;
	for (FSelectionIterator Iter(*SelectedActors); Iter; ++Iter)
	{
		AActor* Actor = Cast<AActor>(*Iter);
		if (Actor)
		{
			Actors.Add(Actor);
			UniqueLevels.AddUnique(Actor->GetLevel());
		}
	}

	// This restriction is only for replacement of selected actors with merged mesh actor
	if (UniqueLevels.Num() > 1 && bReplaceSourceActors)
	{
		FText Message = NSLOCTEXT("UnrealEd", "FailedToMergeActorsSublevels_Msg", "The selected actors should be in the same level");
		OpenMsgDlgInt(EAppMsgType::Ok, Message, NSLOCTEXT("UnrealEd", "FailedToMergeActors_Title", "Unable to merge actors"));
		return false;
	}

	int32 TargetMeshLOD = bExportSpecificLOD ? ExportLODIndex : INDEX_NONE;
	FVector MergedActorLocation;
	TArray<UObject*> AssetsToSync;
	// Merge...
	{
		FScopedSlowTask SlowTask(0, LOCTEXT("MergingActorsSlowTask", "Merging actors..."));
		SlowTask.MakeDialog();

		MeshUtilities.MergeActors(Actors, MergingSettings, NULL, PackageName, TargetMeshLOD, AssetsToSync, MergedActorLocation);
	}

	if (AssetsToSync.Num())
	{
		FAssetRegistryModule& AssetRegistry = FModuleManager::Get().LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
		int32 AssetCount = AssetsToSync.Num();
		for (int32 AssetIndex = 0; AssetIndex < AssetCount; AssetIndex++)
		{
			AssetRegistry.AssetCreated(AssetsToSync[AssetIndex]);
			GEditor->BroadcastObjectReimported(AssetsToSync[AssetIndex]);
		}

		//Also notify the content browser that the new assets exists
		FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
		ContentBrowserModule.Get().SyncBrowserToAssets(AssetsToSync, true);

		// Place new mesh in the world
		if (bReplaceSourceActors)
		{
			UStaticMesh* MergedMesh = nullptr;
			if (AssetsToSync.FindItemByClass(&MergedMesh))
			{
				const FScopedTransaction Transaction(LOCTEXT("PlaceMergedActor", "Place Merged Actor"));
				UniqueLevels[0]->Modify();

				UWorld* World = UniqueLevels[0]->OwningWorld;
				FActorSpawnParameters Params;
				Params.OverrideLevel = UniqueLevels[0];
				FRotator MergedActorRotation(ForceInit);
								
				AStaticMeshActor* MergedActor = World->SpawnActor<AStaticMeshActor>(MergedActorLocation, MergedActorRotation, Params);
				MergedActor->GetStaticMeshComponent()->StaticMesh = MergedMesh;
				MergedActor->SetActorLabel(AssetsToSync[0]->GetName());

				// Remove source actors
				for (AActor* Actor : Actors)
				{
					Actor->Destroy();
				}
			}
		}
	}

	return true;
}
ENGINE_API bool BuildTextureStreamingData(UWorld* InWorld, const FTexCoordScaleMap& InTexCoordScales, EMaterialQualityLevel::Type QualityLevel, ERHIFeatureLevel::Type FeatureLevel, FSlowTask& BuildTextureStreamingTask)
{
#if WITH_EDITORONLY_DATA
    if (!InWorld) return false;
    const double StartTime = FPlatformTime::Seconds();

    // ====================================================
    // Build per primitive data
    // ====================================================
    const float NumPrimitiveComponents = (float)GetNumTextureStreamingPrimitives(InWorld);
    FScopedSlowTask SlowTask(NumPrimitiveComponents, (LOCTEXT("TextureStreamingBuild_ComponentDataUpdate", "Updating Component Data")));

    for (int32 LevelIndex = 0; LevelIndex < InWorld->GetNumLevels(); LevelIndex++)
    {
        ULevel* Level = InWorld->GetLevel(LevelIndex);
        if (!Level) continue;

        TArray<UTexture2D*> LevelTextures;

        TArray<UPrimitiveComponent*> Components;
        GetTextureStreamingPrimitives(Level, Components, false);
        for (UPrimitiveComponent* Primitive : Components)
        {
            // Update all primitives as this could clear unwanted data.
            Primitive->UpdateStreamingTextureData(LevelTextures, InTexCoordScales, QualityLevel, FeatureLevel);

            if (Primitive->RequiresStreamingTextureData())
            {
                // Slow task only accounts for primitive having streaming data.
                SlowTask.EnterProgressFrame();
                BuildTextureStreamingTask.EnterProgressFrame(1.f / NumPrimitiveComponents);

                if (GWarn->ReceivedUserCancel())
                    return false;

                UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(Primitive);
                if (StaticMeshComponent && StaticMeshComponent->StaticMesh && StaticMeshComponent->StaticMesh->GetLightingGuid().IsValid())
                {
                    // Add to the build Guids all used static meshes.
                    Level->TextureStreamingBuildGuids.Add(StaticMeshComponent->StaticMesh->GetLightingGuid());
                }
            }
        }

        // If a component was updated, or  the level data needs reset.
        if (Level->bTextureStreamingRotationChanged || Level->StreamingTextureGuids.Num() || LevelTextures.Num())
        {
            Level->bTextureStreamingRotationChanged = false;
            Level->StreamingTextureGuids.Empty(LevelTextures.Num());
            Level->MarkPackageDirty();

            // Reset LevelIndex to default for next use and build the level Guid array.
            for (int32 TextureIndex = 0; TextureIndex < LevelTextures.Num(); ++TextureIndex)
            {
                UTexture2D* Texture2D = LevelTextures[TextureIndex];
                Level->StreamingTextureGuids.Push(Texture2D->GetLightingGuid());
                Texture2D->LevelIndex = INDEX_NONE;
            }
        }
    }

    // ====================================================
    // Update texture streaming data
    // ====================================================

    ULevel::BuildStreamingData(InWorld);

    // ====================================================
    // Reregister everything for debug view modes to reflect changes
    // ====================================================

    for (int32 LevelIndex = 0; LevelIndex < InWorld->GetNumLevels(); LevelIndex++)
    {
        ULevel* Level = InWorld->GetLevel(LevelIndex);
        if (!Level) continue;
        for (AActor* Actor : Level->Actors)
        {
            if (!Actor) continue;
            Actor->MarkComponentsRenderStateDirty();
        }
    }
    UE_LOG(TextureStreamingBuild, Display, TEXT("Build Texture Streaming took %.3f seconds."), FPlatformTime::Seconds() - StartTime);
    return true;
#else
    UE_LOG(TextureStreamingBuild, Fatal,TEXT("Build Texture Streaming should not be called on a console"));
    return false;
#endif
}
	bool Save(const FString& HeaderDirPath, const FString& CppDirPath)
	{
		if (!Blueprint.IsValid())
		{
			ErrorString += LOCTEXT("InvalidBlueprint", "Invalid Blueprint\n").ToString();
			return false;
		}

		const int WorkParts = 3 + (4 * DependentObjects.Num()) + (2 * UnconvertedNeededClasses.Num());
		FScopedSlowTask SlowTask(WorkParts, LOCTEXT("GeneratingCppFiles", "Generating C++ files.."));
		SlowTask.MakeDialog();

		IBlueprintCompilerCppBackendModule& CodeGenBackend = (IBlueprintCompilerCppBackendModule&)IBlueprintCompilerCppBackendModule::Get();

		TArray<FString> CreatedFiles;
		for(auto Obj : DependentObjects)
		{
			SlowTask.EnterProgressFrame();

			TSharedPtr<FString> HeaderSource(new FString());
			TSharedPtr<FString> CppSource(new FString());
			FBlueprintNativeCodeGenUtils::GenerateCppCode(Obj, HeaderSource, CppSource);
			SlowTask.EnterProgressFrame();

			const FString BackendBaseFilename = CodeGenBackend.ConstructBaseFilename(Obj);

			const FString FullHeaderFilename = FPaths::Combine(*HeaderDirPath, *(BackendBaseFilename + TEXT(".h")));
			const bool bHeaderSaved = FFileHelper::SaveStringToFile(*HeaderSource, *FullHeaderFilename);
			if (!bHeaderSaved)
			{
				ErrorString += FString::Printf(*LOCTEXT("HeaderNotSaved", "Header file wasn't saved. Check log for details. %s\n").ToString(), *Obj->GetPathName());
			}
			else
			{
				CreatedFiles.Add(FullHeaderFilename);
			}

			SlowTask.EnterProgressFrame();
			if (!CppSource->IsEmpty())
			{
				const FString NewCppFilename = FPaths::Combine(*CppDirPath, *(BackendBaseFilename + TEXT(".cpp")));
				const bool bCppSaved = FFileHelper::SaveStringToFile(*CppSource, *NewCppFilename);
				if (!bCppSaved)
				{
					ErrorString += FString::Printf(*LOCTEXT("CppNotSaved", "Cpp file wasn't saved. Check log for details. %s\n").ToString(), *Obj->GetPathName());
				}
				else
				{
					CreatedFiles.Add(NewCppFilename);
				}
			}
		}

		for (auto BPGC : UnconvertedNeededClasses)
		{
			SlowTask.EnterProgressFrame();

			IKismetCompilerInterface& Compiler = FModuleManager::LoadModuleChecked<IKismetCompilerInterface>(KISMET_COMPILER_MODULENAME);
			const FString HeaderSource = Compiler.GenerateCppWrapper(BPGC);

			SlowTask.EnterProgressFrame();

			const FString BackendBaseFilename = CodeGenBackend.ConstructBaseFilename(BPGC);

			const FString FullHeaderFilename = FPaths::Combine(*HeaderDirPath, *(BackendBaseFilename + TEXT(".h")));
			const bool bHeaderSaved = FFileHelper::SaveStringToFile(HeaderSource, *FullHeaderFilename);
			if (!bHeaderSaved)
			{
				ErrorString += FString::Printf(*LOCTEXT("HeaderNotSaved", "Header file wasn't saved. Check log for details. %s\n").ToString(), *BPGC->GetPathName());
			}
			else
			{
				CreatedFiles.Add(FullHeaderFilename);
			}
		}

		SlowTask.EnterProgressFrame();

		bool bGenerateProjectFiles = true;
		{
			bool bProjectHadCodeFiles = false;
			{
				TArray<FString> OutProjectCodeFilenames;
				IFileManager::Get().FindFilesRecursive(OutProjectCodeFilenames, *FPaths::GameSourceDir(), TEXT("*.h"), true, false, false);
				IFileManager::Get().FindFilesRecursive(OutProjectCodeFilenames, *FPaths::GameSourceDir(), TEXT("*.cpp"), true, false, false);
				bProjectHadCodeFiles = OutProjectCodeFilenames.Num() > 0;
			}

			TArray<FString> CreatedFilesForExternalAppRead;
			CreatedFilesForExternalAppRead.Reserve(CreatedFiles.Num());
			for (const FString& CreatedFile : CreatedFiles)
			{
				CreatedFilesForExternalAppRead.Add(IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*CreatedFile));
			}

			// First see if we can avoid a full generation by adding the new files to an already open project
			if (bProjectHadCodeFiles && FSourceCodeNavigation::AddSourceFiles(CreatedFilesForExternalAppRead))
			{
				// We successfully added the new files to the solution, but we still need to run UBT with -gather to update any UBT makefiles
				if (FDesktopPlatformModule::Get()->InvalidateMakefiles(FPaths::RootDir(), FPaths::GetProjectFilePath(), GWarn))
				{
					// We managed the gather, so we can skip running the full generate
					bGenerateProjectFiles = false;
				}
			}
		}

		SlowTask.EnterProgressFrame();

		bool bProjectFileUpdated = true;
		if (bGenerateProjectFiles)
		{
			// Generate project files if we happen to be using a project file.
			if (!FDesktopPlatformModule::Get()->GenerateProjectFiles(FPaths::RootDir(), FPaths::GetProjectFilePath(), GWarn))
			{
				ErrorString += LOCTEXT("FailedToGenerateProjectFiles", "Failed to generate project files.").ToString();
				bProjectFileUpdated = false;
			}
		}

		return ErrorString.IsEmpty();
	}
// Extract the sprites based on SpriteExtractSettings->Mode
void SPaperExtractSpritesDialog::PreviewExtractedSprites()
{
	FScopedSlowTask SlowTask(2, NSLOCTEXT("Paper2D", "Paper2D_ExtractingSprites", "Extracting Sprites"));
	SlowTask.MakeDialog(false);
	SlowTask.EnterProgressFrame();

	const FString DefaultSuffix = TEXT("Sprite");
	if (ExtractSpriteSettings->Mode == ESpriteExtractMode::Auto)
	{
		ExtractedSprites.Empty();

		// First extract the rects from the texture
		TArray<FIntRect> ExtractedRects;
		UPaperSprite::ExtractRectsFromTexture(SourceTexture, /*out*/ ExtractedRects);

		// Sort the rectangles by approximate row
		struct FRectangleSortHelper
		{
			FRectangleSortHelper(TArray<FIntRect>& InOutSprites)
			{
				// Sort by Y, then by X (top left corner), descending order (so we can use it as a stack from the top row down)
				TArray<FIntRect> SpritesLeft = InOutSprites;
				SpritesLeft.Sort([](const FIntRect& A, const FIntRect& B) { return (A.Min.Y == B.Min.Y) ? (A.Min.X > B.Min.X) : (A.Min.Y > B.Min.Y); });
				InOutSprites.Reset();

				// Start pulling sprites out, the first one in each row will dominate remaining ones and cause them to get labeled
				TArray<FIntRect> DominatedSprites;
				DominatedSprites.Empty(SpritesLeft.Num());
				while (SpritesLeft.Num())
				{
					FIntRect DominatingSprite = SpritesLeft.Pop();
					DominatedSprites.Add(DominatingSprite);

					// Find the sprites that are dominated (intersect the infinite horizontal band described by the dominating sprite)
					for (int32 Index = 0; Index < SpritesLeft.Num();)
					{
						const FIntRect& CurElement = SpritesLeft[Index];
						if ((CurElement.Min.Y <= DominatingSprite.Max.Y) && (CurElement.Max.Y >= DominatingSprite.Min.Y))
						{
							DominatedSprites.Add(CurElement);
							SpritesLeft.RemoveAt(Index, /*Count=*/ 1, /*bAllowShrinking=*/ false);
						}
						else
						{
							++Index;
						}
					}

					// Sort the sprites in the band by X and add them to the result
					DominatedSprites.Sort([](const FIntRect& A, const FIntRect& B) { return (A.Min.X < B.Min.X); });
					InOutSprites.Append(DominatedSprites);
					DominatedSprites.Reset();
				}
			}
		};
		FRectangleSortHelper RectSorter(ExtractedRects);

		int32 ExtractedRectIndex = 0;
		for (FIntRect Rect : ExtractedRects)
		{
			FPaperExtractedSprite* Sprite = new(ExtractedSprites)FPaperExtractedSprite();
			Sprite->Rect = Rect;
			Sprite->Name = FString::Printf(TEXT("%s_%d"), *DefaultSuffix, ExtractedRectIndex);
			ExtractedRectIndex++;
		}
	}
	else
	{
		// Calculate rects
		ExtractedSprites.Empty();
		if (SourceTexture != nullptr)
		{
			int32 ExtractedRectIndex = 0;
			int32 TextureWidth = SourceTexture->GetSizeX();
			int32 TextureHeight = SourceTexture->GetSizeY();
			for (int32 Y = ExtractSpriteSettings->Margin; Y + ExtractSpriteSettings->GridHeight <= TextureHeight; Y += ExtractSpriteSettings->GridHeight + ExtractSpriteSettings->Spacing)
			{
				for (int32 X = ExtractSpriteSettings->Margin; X + ExtractSpriteSettings->GridWidth <= TextureWidth; X += ExtractSpriteSettings->GridWidth + ExtractSpriteSettings->Spacing)
				{
					FPaperExtractedSprite* Sprite = new(ExtractedSprites)FPaperExtractedSprite();
					Sprite->Rect = FIntRect(X, Y, X + ExtractSpriteSettings->GridWidth, Y + ExtractSpriteSettings->GridHeight);
					Sprite->Name = FString::Printf(TEXT("%s_%d"), *DefaultSuffix, ExtractedRectIndex);
					ExtractedRectIndex++;
				}
			}
		}
	}
}
bool FAutomationTestFramework::RunSmokeTests()
{
	bool bAllSuccessful = true;

	uint32 PreviousRequestedTestFilter = RequestedTestFilter;
	//so extra log spam isn't generated
	RequestedTestFilter = EAutomationTestFlags::SmokeFilter;
	
	// Skip running on cooked platforms like mobile
	//@todo - better determination of whether to run than requires cooked data
	// Ensure there isn't another slow task in progress when trying to run unit tests
	const bool bRequiresCookedData = FPlatformProperties::RequiresCookedData();
	if ((!bRequiresCookedData && !GIsSlowTask && !GIsPlayInEditorWorld && !FPlatformProperties::IsProgram()) || bForceSmokeTests)
	{
		TArray<FAutomationTestInfo> TestInfo;

		GetValidTestNames( TestInfo );

		if ( TestInfo.Num() > 0 )
		{
			double SmokeTestStartTime = FPlatformTime::Seconds();

			// Output the results of running the automation tests
			TMap<FString, FAutomationTestExecutionInfo> OutExecutionInfoMap;

			// Run each valid test
			FScopedSlowTask SlowTask(TestInfo.Num());

			for ( int TestIndex = 0; TestIndex < TestInfo.Num(); ++TestIndex )
			{
				SlowTask.EnterProgressFrame(1);
				if (TestInfo[TestIndex].GetTestFlags() & EAutomationTestFlags::SmokeFilter )
				{
					FString TestCommand = TestInfo[TestIndex].GetTestName();
					FAutomationTestExecutionInfo& CurExecutionInfo = OutExecutionInfoMap.Add( TestCommand, FAutomationTestExecutionInfo() );

					int32 RoleIndex = 0;  //always default to "local" role index.  Only used for multi-participant tests
					StartTestByName( TestCommand, RoleIndex );
					const bool CurTestSuccessful = StopTest(CurExecutionInfo);

					bAllSuccessful = bAllSuccessful && CurTestSuccessful;
				}
			}

			double EndTime = FPlatformTime::Seconds();
			double TimeForTest = static_cast<float>(EndTime - SmokeTestStartTime);
			if (TimeForTest > 2.0f)
			{
				//force a failure if a smoke test takes too long
				UE_LOG(LogAutomationTest, Warning, TEXT("Smoke tests took > 2s to run: %.2fs"), (float)TimeForTest);
			}

			FAutomationTestFramework::DumpAutomationTestExecutionInfo( OutExecutionInfoMap );
		}
	}
	else if( bRequiresCookedData )
	{
		UE_LOG( LogAutomationTest, Log, TEXT( "Skipping unit tests for the cooked build." ) );
	}
	else if (!FPlatformProperties::IsProgram())
	{
		UE_LOG(LogAutomationTest, Error, TEXT("Skipping unit tests.") );
		bAllSuccessful = false;
	}

	//revert to allowing all logs
	RequestedTestFilter = PreviousRequestedTestFilter;

	return bAllSuccessful;
}
예제 #22
0
int32 EditorInit( IEngineLoop& EngineLoop )
{
	// Create debug exec.	
	GDebugToolExec = new FDebugToolExec;

	DECLARE_SCOPE_CYCLE_COUNTER(TEXT("Editor Initialized"), STAT_EditorStartup, STATGROUP_LoadTime);
	
	FScopedSlowTask SlowTask(100, NSLOCTEXT("EngineLoop", "EngineLoop_Loading", "Loading..."));
	
	SlowTask.EnterProgressFrame(50);

	int32 ErrorLevel = EngineLoop.Init();
	if( ErrorLevel != 0 )
	{
		FPlatformSplash::Hide();
		return 0;
	}

	// Let the analytics know that the editor has started
	if ( FEngineAnalytics::IsAvailable() )
	{
		TArray<FAnalyticsEventAttribute> EventAttributes;
		EventAttributes.Add(FAnalyticsEventAttribute(TEXT("MachineID"), FPlatformMisc::GetMachineId().ToString(EGuidFormats::Digits).ToLower()));
		EventAttributes.Add(FAnalyticsEventAttribute(TEXT("AccountID"), FPlatformMisc::GetEpicAccountId()));
		EventAttributes.Add(FAnalyticsEventAttribute(TEXT("GameName"), FApp::GetGameName()));
		EventAttributes.Add(FAnalyticsEventAttribute(TEXT("CommandLine"), FCommandLine::Get()));

		FEngineAnalytics::GetProvider().RecordEvent(TEXT("Editor.ProgramStarted"), EventAttributes);
	}

	SlowTask.EnterProgressFrame(40);

	// Initialize the misc editor
	FUnrealEdMisc::Get().OnInit();
	
	SlowTask.EnterProgressFrame(10);

	// Prime our array of default directories for loading and saving content files to
	FEditorDirectories::Get().LoadLastDirectories();

	// Set up the actor folders singleton
	FActorFolders::Init();

	// =================== CORE EDITOR INIT FINISHED ===================

	// Hide the splash screen now that everything is ready to go
	FPlatformSplash::Hide();

	// Are we in immersive mode?
	const bool bIsImmersive = FPaths::IsProjectFilePathSet() && FParse::Param( FCommandLine::Get(), TEXT( "immersive" ) );

	// Do final set up on the editor frame and show it
	{
		// Tear down rendering thread once instead of doing it for every window being resized.
		SCOPED_SUSPEND_RENDERING_THREAD(true);

		// Startup Slate main frame and other editor windows
		{
			const bool bStartImmersive = bIsImmersive;
			const bool bStartPIE = bIsImmersive;

			IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>(TEXT("MainFrame"));
			MainFrameModule.CreateDefaultMainFrame( bStartImmersive, bStartPIE );
		}
	}


	// Go straight to VR mode if we were asked to
	{
		if( !bIsImmersive && FParse::Param( FCommandLine::Get(), TEXT( "VREditor" ) ) )
		{
			IVREditorModule& VREditorModule = IVREditorModule::Get();
			VREditorModule.EnableVREditor( true );
		}
		else if( FParse::Param( FCommandLine::Get(), TEXT( "ForceVREditor" ) ) )
		{
			GEngine->DeferredCommands.Add( TEXT( "VREd.ForceVRMode" ) );
		}
	}

	// Check for automated build/submit option
	const bool bDoAutomatedMapBuild = FParse::Param( FCommandLine::Get(), TEXT("AutomatedMapBuild") );

	// Prompt to update the game project file to the current version, if necessary
	if ( FPaths::IsProjectFilePathSet() )
	{
		FGameProjectGenerationModule::Get().CheckForOutOfDateGameProjectFile();
		FGameProjectGenerationModule::Get().CheckAndWarnProjectFilenameValid();
	}

	// =================== EDITOR STARTUP FINISHED ===================
	
	// Stat tracking
	{
		const float StartupTime = (float)( FPlatformTime::Seconds() - GStartTime );

		if( FEngineAnalytics::IsAvailable() )
		{
			FEngineAnalytics::GetProvider().RecordEvent( 
				TEXT( "Editor.Performance.Startup" ), 
				TEXT( "Duration" ), FString::Printf( TEXT( "%.3f" ), StartupTime ) );
		}
	}

	FModuleManager::LoadModuleChecked<IModuleInterface>(TEXT("HierarchicalLODOutliner"));

	// this will be ultimately returned from main(), so no error should be 0.
	return 0;
}
bool FHierarchicalLODUtilities::BuildStaticMeshForLODActor(ALODActor* LODActor, UPackage* AssetsOuter, const FHierarchicalSimplification& LODSetup, const uint32 LODIndex)
{
	if (AssetsOuter && LODActor)
	{
		if (!LODActor->IsDirty())
		{
			return false;
		}

		const FScopedTransaction Transaction(LOCTEXT("UndoAction_BuildProxyMesh", "Building Proxy Mesh for Cluster"));
		LODActor->Modify();		

		// Delete actor assets before generating new ones
		FHierarchicalLODUtilities::DestroyClusterData(LODActor);	
		
		TArray<UStaticMeshComponent*> AllComponents;
		{
			FScopedSlowTask SlowTask(LODActor->SubActors.Num(), (LOCTEXT("HierarchicalLODUtils_CollectStaticMeshes", "Collecting Static Meshes for Cluster")));
			SlowTask.MakeDialog();
			for (auto& Actor : LODActor->SubActors)
			{
				TArray<UStaticMeshComponent*> Components;

				if (Actor->IsA<ALODActor>())
				{
					ExtractStaticMeshComponentsFromLODActor(Actor, Components);
				}
				else
				{
					Actor->GetComponents<UStaticMeshComponent>(Components);
				}

				// TODO: support instanced static meshes
				Components.RemoveAll([](UStaticMeshComponent* Val){ return Val->IsA(UInstancedStaticMeshComponent::StaticClass()); });

				AllComponents.Append(Components);
				SlowTask.EnterProgressFrame(1);
			}
		}

		// it shouldn't even have come here if it didn't have any staticmesh
		if (ensure(AllComponents.Num() > 0))
		{
			FScopedSlowTask SlowTask(LODActor->SubActors.Num(), (LOCTEXT("HierarchicalLODUtils_MergingMeshes", "Merging Static Meshes and creating LODActor")));
			SlowTask.MakeDialog();

			// In case we don't have outer generated assets should have same path as LOD level
			const FString AssetsPath = AssetsOuter->GetName() + TEXT("/");
			AActor* FirstActor = LODActor->SubActors[0];

			TArray<UObject*> OutAssets;
			FVector OutProxyLocation = FVector::ZeroVector;
			UStaticMesh* MainMesh = nullptr;

			// Generate proxy mesh and proxy material assets
			IMeshUtilities& MeshUtilities = FModuleManager::Get().LoadModuleChecked<IMeshUtilities>("MeshUtilities");
			// should give unique name, so use level + actor name
			const FString PackageName = FString::Printf(TEXT("LOD_%s"), *FirstActor->GetName());
			if (MeshUtilities.GetMeshMergingInterface() && LODSetup.bSimplifyMesh)
			{
				TArray<AActor*> Actors;
				ExtractSubActorsFromLODActor(LODActor, Actors);

				FHierarchicalLODUtilities& Module = FModuleManager::LoadModuleChecked<FHierarchicalLODUtilities>("HierarchicalLODUtilities");
				FHierarchicalLODProxyProcessor* Processor = Module.GetProxyProcessor();

				FHierarchicalSimplification OverrideLODSetup = LODSetup;

				FMeshProxySettings ProxySettings = LODSetup.ProxySetting;
				if (LODActor->bOverrideMaterialMergeSettings)
				{
					ProxySettings.MaterialSettings = LODActor->MaterialSettings;
				}

				if (LODActor->bOverrideScreenSize)
				{
					ProxySettings.ScreenSize = LODActor->ScreenSize;
				}

				if (LODActor->bOverrideTransitionScreenSize)
				{
					OverrideLODSetup.TransitionScreenSize = LODActor->TransitionScreenSize;
				}

				FGuid JobID = Processor->AddProxyJob(LODActor, OverrideLODSetup);
				MeshUtilities.CreateProxyMesh(Actors, ProxySettings, AssetsOuter, PackageName, JobID, Processor->GetCallbackDelegate(), true, OverrideLODSetup.TransitionScreenSize);
				return true;
			}
			else
			{
				FMeshMergingSettings MergeSettings = LODSetup.MergeSetting;
				if (LODActor->bOverrideMaterialMergeSettings)
				{
					MergeSettings.MaterialSettings = LODActor->MaterialSettings;
				}

				MeshUtilities.MergeStaticMeshComponents(AllComponents, FirstActor->GetWorld(), MergeSettings, AssetsOuter, PackageName, LODIndex, OutAssets, OutProxyLocation, LODSetup.TransitionScreenSize, true);
				
				// set staticmesh
				for (auto& Asset : OutAssets)
				{
					UStaticMesh* StaticMesh = Cast<UStaticMesh>(Asset);

					if (StaticMesh)
					{
						MainMesh = StaticMesh;
					}
				}

				LODActor->SetStaticMesh(MainMesh);
				LODActor->SetActorLocation(OutProxyLocation);
				LODActor->SubObjects = OutAssets;

				// At the moment this assumes a fixed field of view of 90 degrees (horizontal and vertical axi)
				static const float FOVRad = 90.0f * (float)PI / 360.0f;
				static const FMatrix ProjectionMatrix = FPerspectiveMatrix(FOVRad, 1920, 1080, 0.01f);
				FBoxSphereBounds Bounds = LODActor->GetStaticMeshComponent()->CalcBounds(FTransform());
				LODActor->LODDrawDistance = FHierarchicalLODUtilities::CalculateDrawDistanceFromScreenSize(Bounds.SphereRadius, LODSetup.TransitionScreenSize, ProjectionMatrix);
				LODActor->UpdateSubActorLODParents();

				// Freshly build so mark not dirty
				LODActor->SetIsDirty(false);

				return true;
			}
		}
	}
	return false;
}