Example #1
0
void USetupDefinition::DoInstallSteps( FInstallPoll* Poll )
{
	guard(USetupDefinition::DoInstallSteps);

	// Handle all install steps.
	BeginSteps();
	TotalBytes = 0;
	InstallTree( TEXT("ProcessPreCopy"),  Poll, (INSTALL_STEP)ProcessPreCopy  );
	InstallTree( TEXT("ProcessCopy"),     Poll, (INSTALL_STEP)ProcessCopy     );
	InstallTree( TEXT("ProcessExtra"),    Poll, (INSTALL_STEP)ProcessExtra    );
	InstallTree( TEXT("ProcessPostCopy"), Poll, (INSTALL_STEP)ProcessPostCopy );
	Exists = FolderExists = 1;
	RegistryFolder = DestPath;
	if( IsMasterProduct )
		GConfig->SetString( TEXT("Setup"), TEXT("MasterProduct"), *Product, *(DestPath * TEXT("System") * SETUP_INI) );
	TMultiMap<FString,FString>* Map = GConfig->GetSectionPrivate( TEXT("Setup"), 1, 0, *(DestPath * TEXT("System") * SETUP_INI) );
		Map->AddUnique( TEXT("Group"), *Product );
	for( TArray<FSavedIni>::TIterator It(SavedIni); It; ++It )
		GConfig->SetString( *It->Section, *It->Key, *It->SavedValue, *It->File );
	UninstallLogAdd( TEXT("Caption"), *LocalProduct, 1, 0 );
	RootGroup->UninstallLog.Remove( TEXT("Version") );
	UninstallLogAdd( TEXT("Version"), *Version, 1, 0 );
	for( INT i=0; i<Requires.Num(); i++ )
	{
		USetupProduct* SetupProduct = new(GetOuter(),*Requires(i))USetupProduct;
		if( SetupProduct->Product!=Product )
			UninstallLogAdd( TEXT("Requires"), *SetupProduct->Product, 0, 0 );
	}
	EndSteps();

	unguard;
}
Example #2
0
//------------------------------------------------------------------------------
int32 FDeferredScriptTracker::ResolveDeferredScripts(ULinkerLoad* Linker)
{
	FArchive& Ar = *Linker;
	if (FStructScriptLoader::ShouldDeferScriptSerialization(Ar))
	{
		return 0;
	}

#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
	TGuardValue<ULinkerLoad*> ScopedResolvingLinker(ResolvingLinker, Linker);
#endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS

	TArray<FDeferredScriptLoader> DefferedLinkerScripts;
	DeferredScriptLoads.MultiFind(Linker, DefferedLinkerScripts);
	// remove before we resolve, because a failed call to 
	// FDeferredScriptLoader::Resolve() could insert back into this list 
	DeferredScriptLoads.Remove(Linker);

	int32 const SerializationPosToRestore = Ar.Tell();

	int32 ResolveCount = 0;
	for (FDeferredScriptLoader& DeferredScript : DefferedLinkerScripts)
	{
		if (DeferredScript.Resolve(Ar))
		{
			++ResolveCount;
		}
	}

	Ar.Seek(SerializationPosToRestore);
	return ResolveCount;
}
void USceneCaptureComponentCube::UpdateDeferredCaptures( FSceneInterface* Scene )
{
	UWorld* World = Scene->GetWorld();
	
	if( World && CubedSceneCapturesToUpdateMap.Num() > 0 )
	{
		World->SendAllEndOfFrameUpdates();
		// Only update the scene captures associated with the current scene.
		// Updating others not associated with the scene would cause invalid data to be rendered into the target
		TArray< TWeakObjectPtr<USceneCaptureComponentCube> > SceneCapturesToUpdate;
		CubedSceneCapturesToUpdateMap.MultiFind( World, SceneCapturesToUpdate );
		
		for( TWeakObjectPtr<USceneCaptureComponentCube> Component : SceneCapturesToUpdate )
		{
			if( Component.IsValid() )
			{
				Scene->UpdateSceneCaptureContents( Component.Get() );
			}
		}
		
		// All scene captures for this world have been updated
		CubedSceneCapturesToUpdateMap.Remove( World );
	}

}
Example #4
0
void USetupDefinition::DoUninstallSteps( FInstallPoll* Poll )
{
	guard(USetupDefinition::DoInstallSteps);

	// Handle all uninstall steps.
	BeginSteps();
	UninstallTotal=0, UninstallCount=0;
	UninstallTree( TEXT("ProcessUninstallCountTotal"), Poll, ProcessUninstallCountTotal );
	UninstallTree( TEXT("ProcessUninstallRemove"),     Poll, ProcessUninstallRemove );
	TMultiMap<FString,FString>* Map = GConfig->GetSectionPrivate( TEXT("Setup"), 0, 0, *(DestPath * TEXT("System") * SETUP_INI) );
	for( TArray<USetupGroup*>::TIterator GroupIt(UninstallComponents); GroupIt; ++GroupIt )
	{
		(*GroupIt)->UninstallLog = TMultiMap<FString,FString>();
		if( Map )
			Map->RemovePair( TEXT("Group"), (*GroupIt)->GetName() );
	}
	EndSteps();

	// If reference counts exausted, delete unnecessary setup file so full directory can be removed.
	INT Refs=0;
	for( TMap<FString,FString>::TIterator It(RefCounts); It; ++It )
		Refs += appAtoi( *It.Value() );
	if( Refs==0 )
	{
		GFileManager->Delete( *SetupIniFile );
		RemoveEmptyDirectory( *BasePath(SetupIniFile) );
	}

	unguard;
}
Example #5
0
void AShooterGameState::GetRankedMap(int32 TeamIndex, RankedPlayerMap& OutRankedMap) const
{
	OutRankedMap.Empty();

	//first, we need to go over all the PlayerStates, grab their score, and rank them
	TMultiMap<int32, AShooterPlayerState*> SortedMap;
	for(int32 i = 0; i < PlayerArray.Num(); ++i)
	{
		int32 Score = 0;
		AShooterPlayerState* CurPlayerState = Cast<AShooterPlayerState>(PlayerArray[i]);
		if (CurPlayerState && (CurPlayerState->GetTeamNum() == TeamIndex))
		{
			SortedMap.Add(FMath::TruncToInt(CurPlayerState->Score), CurPlayerState);
		}
	}

	//sort by the keys
	SortedMap.KeySort(TGreater<int32>());

	//now, add them back to the ranked map
	OutRankedMap.Empty();

	int32 Rank = 0;
	for(TMultiMap<int32, AShooterPlayerState*>::TIterator It(SortedMap); It; ++It)
	{
		OutRankedMap.Add(Rank++, It.Value());
	}
	
}
bool FChunkManifestGenerator::SaveManifests(FSandboxPlatformFile* InSandboxFile)
{
	// Always do package dependency work, is required to modify asset registry
	FixupPackageDependenciesForChunks(InSandboxFile);

	if (bGenerateChunks)
	{
		for (auto Platform : Platforms)
		{
			if (!GenerateStreamingInstallManifest(Platform->PlatformName()))
			{
				return false;
			}

			// Generate map for the platform abstraction
			TMultiMap<FString, int32> ChunkMap;	// asset -> ChunkIDs map
			TSet<int32> ChunkIDsInUse;
			const FString PlatformName = Platform->PlatformName();

			// Collect all unique chunk indices and map all files to their chunks
			for (int32 ChunkIndex = 0; ChunkIndex < FinalChunkManifests.Num(); ++ChunkIndex)
			{
				if (FinalChunkManifests[ChunkIndex] && FinalChunkManifests[ChunkIndex]->Num())
				{
					ChunkIDsInUse.Add(ChunkIndex);
					for (auto& Filename : *FinalChunkManifests[ChunkIndex])
					{
						FString PlatFilename = Filename.Value.Replace(TEXT("[Platform]"), *PlatformName);
						ChunkMap.Add(PlatFilename, ChunkIndex);
					}
				}
			}

			// Sort our chunk IDs and file paths
			ChunkMap.KeySort(TLess<FString>());
			ChunkIDsInUse.Sort(TLess<int32>());

			// Platform abstraction will generate any required platform-specific files for the chunks
			if (!Platform->GenerateStreamingInstallManifest(ChunkMap, ChunkIDsInUse))
			{
				return false;
			}
		}


		GenerateAssetChunkInformationCSV(FPaths::Combine(*FPaths::GameLogDir(), TEXT("ChunkLists")));
	}

	return true;
}
	virtual void AssociateSuppress(FLogCategoryBase* Destination)
	{
		FName NameFName(Destination->CategoryFName);
		check(Destination);
		check(!Associations.Find(Destination)); // should not have this address already registered
		Associations.Add(Destination, NameFName);
		bool bFoundExisting = false;
		for (TMultiMap<FName, FLogCategoryBase*>::TKeyIterator It(ReverseAssociations, NameFName); It; ++It)
		{
			if (It.Value() == Destination)
			{
				UE_LOG(LogHAL, Fatal,TEXT("Log suppression category %s was somehow declared twice with the same data."), *NameFName.ToString());
			}
			// if it is registered, it better be the same
			if (It.Value()->CompileTimeVerbosity != Destination->CompileTimeVerbosity)
			{
				UE_LOG(LogHAL, Fatal,TEXT("Log suppression category %s is defined multiple times with different compile time verbosity."), *NameFName.ToString());
			}
			// we take whatever the existing one has to keep them in sync always
			checkSlow(!(It.Value()->Verbosity & ELogVerbosity::BreakOnLog)); // this bit is factored out of this variable, always
			Destination->Verbosity = It.Value()->Verbosity;
			Destination->DebugBreakOnLog = It.Value()->DebugBreakOnLog;
			Destination->DefaultVerbosity = It.Value()->DefaultVerbosity;
			bFoundExisting = true;
		}
		ReverseAssociations.Add(NameFName, Destination);
		if (bFoundExisting)
		{
			return; // in no case is there anything more to do...we want to match the other ones
		}
		SetupSuppress(Destination, NameFName); // this might be done again later if this is being set up before appInit is called
	}
	virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) override
	{
		FString FullPath = FilenameOrDirectory;
	
		// for all packages
		if (!bIsDirectory && FPaths::GetExtension(FullPath, true) == FPackageName::GetMapPackageExtension())
		{
			FString TilePackageName = FPackageName::FilenameToLongPackageName(FullPath);
			FPackageNameAndLODIndex PackageNameLOD = BreakToNameAndLODIndex(TilePackageName);
						
			if (PackageNameLOD.LODIndex != INDEX_NONE)
			{
				if (PackageNameLOD.LODIndex == 0) 
				{
					// non-LOD tile
					TilesCollection.Add(TilePackageName);
				}
				else
				{
					// LOD tile
					FString TileShortName = FPackageName::GetShortName(PackageNameLOD.PackageName);
					TilesLODCollection.Add(TileShortName, PackageNameLOD);
				}
			}
		}
	
		return true;
	}
void USceneCaptureComponentCube::UpdateContent()
{
	if (World && World->Scene && IsVisible())
	{
		// Defer until after updates finish
		CubedSceneCapturesToUpdateMap.AddUnique( World, this );
	}	
}
Example #10
0
//------------------------------------------------------------------------------
void FDeferredScriptTracker::AddDeferredScriptObject(ULinkerLoad* Linker, UStruct* TargetScriptContainer, const FStructScriptLoader& ScriptLoader)
{
#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
	check(ResolvingLinker == nullptr);
#endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS

	DeferredScriptLoads.Add(Linker, FDeferredScriptLoader(ScriptLoader, TargetScriptContainer));
}
	virtual void DisassociateSuppress(FLogCategoryBase* Destination)
	{
		FName* Name = Associations.Find(Destination);
		if (Name)
		{
			verify(ReverseAssociations.Remove(*Name, Destination)==1);
			verify(Associations.Remove(Destination) == 1);
		}
	}
void USceneCaptureComponent2D::UpdateDeferredCaptures( FSceneInterface* Scene )
{
	UWorld* World = Scene->GetWorld();
	if( World && SceneCapturesToUpdateMap.Num() > 0 )
	{
		// Only update the scene captures assoicated with the current scene.
		// Updating others not associated with the scene would cause invalid data to be rendered into the target
		TArray<USceneCaptureComponent2D*> SceneCapturesToUpdate;
		SceneCapturesToUpdateMap.MultiFind( World, SceneCapturesToUpdate );
		
		for( USceneCaptureComponent2D* Component : SceneCapturesToUpdate )
		{
			Scene->UpdateSceneCaptureContents( Component );
		}
		
		// All scene captures for this world have been updated
		SceneCapturesToUpdateMap.Remove( World) ;
	}
}
// Creates child/parent relationship between the nodes
static void LinkToParents(TMultiMap<UObject*, FRefGraphItem*>& InputGraphNodeList, FRefGraphItem* NodeToLink)
{
	for (auto It = InputGraphNodeList.CreateConstIterator(); It; ++It)
	{
		if (It.Value()->Link.ReferencedObj == NodeToLink->Link.ReferencedBy)
		{
			It.Value()->Children.Push(NodeToLink);
			NodeToLink->Parents.Push(It.Value());
		}
	}
}
void FBlueprintNativeCodeGenModule::CollectBoundFunctions(UBlueprint* BP)
{
	TArray<UFunction*> Functions = IBlueprintCompilerCppBackendModule::CollectBoundFunctions(BP);
	for (UFunction* Func : Functions)
	{
		if (Func)
		{
			FunctionsBoundToADelegate.AddUnique(Func->GetFName(), Func->GetOwnerClass());
		}
	}
}
void FAttenuationSettings::CollectAttenuationShapesForVisualization(TMultiMap<EAttenuationShape::Type, AttenuationShapeDetails>& ShapeDetailsMap) const
{
    if (bAttenuate)
    {
        AttenuationShapeDetails ShapeDetails;
        ShapeDetails.Extents = AttenuationShapeExtents;
        ShapeDetails.Falloff = FalloffDistance;
        ShapeDetails.ConeOffset = ConeOffset;

        ShapeDetailsMap.Add(AttenuationShape, ShapeDetails);
    }
}
Example #16
0
void USetupDefinition::ProcessPreCopy( FString Key, FString Value, UBOOL Selected, FInstallPoll* Poll )
{
	guard(USetupDefinition::ProcessPreCopy);
	if( Selected && Key==TEXT("File") )
	{
		// Verify that file exists and is copyable.
		FFileInfo Info(*Value);
		if( Info.Lang==TEXT("") || Info.Lang==UObject::GetLanguage() )
		{
			if( !LocateSourceFile(Info.Src) )
				LocalizedFileError( TEXT("MissingInstallerFile"), Patch ? TEXT("AdviseBadDownload") : TEXT("AdviseBadMedia"), *Info.Src );
			if( Info.Ref!=TEXT("") )
			{
				FString FullRef = GetFullRef(*Info.Ref);
				if( GFileManager->FileSize(*FullRef)<=0 )
					LocalizedFileError( TEXT("MissingReferenceFile"), TEXT("AdviseBadMedia"), *FullRef );
				TotalBytes += GFileManager->FileSize(*FullRef);
				TotalBytes += Info.Size;
			}
			else TotalBytes += Info.Size;
		}
	}
	else if( Selected && Key==TEXT("SaveIni") )
	{
		Value           = Format(Value,NULL);
		INT Pos         = Value.InStr(TEXT(","));
		check(Pos>=0);
		FString File    = DestPath * Value.Left(Pos);
		Value           = Value.Mid(Pos+1);
		Pos             = Value.InStr(TEXT("."),1);
		check(Pos>=0);
		FString Section = Value.Left(Pos);
		FString Key     = Value.Mid(Pos+1);
		TMultiMap<FString,FString>* Map = GConfig->GetSectionPrivate( *Section, 0, 0, *File );
		if( Map )
			Map->AddUnique( *Key, *Value );
	}
	unguard;
}
/**
 * Loads all of the UClass-es used by code so we can search for commandlets
 */
static void LoadAllClasses(void)
{
	TArray<FString> ScriptPackageNames;
	TMultiMap<FString,FString>* Sec = GConfig->GetSectionPrivate( TEXT("UnrealEd.EditorEngine"), 0, 1, GEngineIni );
	if (Sec != NULL)
	{
		// Get the list of all code packages
		Sec->MultiFind( FString(TEXT("EditPackages")), ScriptPackageNames );
		// Iterate through loading each package
		for (INT Index = 0; Index < ScriptPackageNames.Num(); Index++)
		{
			// Loading without LOAD_Verify should load all objects
			UPackage* Package = UObject::LoadPackage(NULL,*ScriptPackageNames(Index),LOAD_NoWarn | LOAD_Quiet);
			if (Package == NULL)
			{
				warnf(TEXT("Error loading package %s"),*ScriptPackageNames(Index));
			}
		}
	}
	else
	{
		warnf(TEXT("Error finding the code packages"));
	}
}
void URuntimeMeshLibrary::CalculateTangentsForMesh(TFunction<int32(int32 Index)> IndexAccessor, TFunction<FVector(int32 Index)> VertexAccessor, TFunction<FVector2D(int32 Index)> UVAccessor,
	TFunction<void(int32 Index, FVector TangentX, FVector TangentY, FVector TangentZ)> TangentSetter, int32 NumVertices, int32 NumUVs, int32 NumIndices, bool bCreateSmoothNormals)
{
	SCOPE_CYCLE_COUNTER(STAT_RuntimeMeshLibrary_CalculateTangentsForMesh);

	if (NumVertices == 0 || NumIndices == 0)
	{
		return;
	}

	// Calculate the duplicate vertices map if we're wanting smooth normals.  Don't find duplicates if we don't want smooth normals
	// that will cause it to only smooth across faces sharing a common vertex, not across faces with vertices of common position
	const TMultiMap<uint32, uint32> DuplicateVertexMap = bCreateSmoothNormals ? FRuntimeMeshInternalUtilities::FindDuplicateVerticesMap(VertexAccessor, NumVertices) : TMultiMap<uint32, uint32>();


	// Number of triangles
	const int32 NumTris = NumIndices / 3;

	// Map of vertex to triangles in Triangles array
	TMultiMap<uint32, uint32> VertToTriMap;
	// Map of vertex to triangles to consider for normal calculation
	TMultiMap<uint32, uint32> VertToTriSmoothMap;

	// Normal/tangents for each face
	TArray<FVector> FaceTangentX, FaceTangentY, FaceTangentZ;
	FaceTangentX.AddUninitialized(NumTris);
	FaceTangentY.AddUninitialized(NumTris);
	FaceTangentZ.AddUninitialized(NumTris);

	// Iterate over triangles
	for (int TriIdx = 0; TriIdx < NumTris; TriIdx++)
	{
		uint32 CornerIndex[3];
		FVector P[3];

		for (int32 CornerIdx = 0; CornerIdx < 3; CornerIdx++)
		{
			// Find vert index (clamped within range)
			uint32 VertIndex = FMath::Min(IndexAccessor((TriIdx * 3) + CornerIdx), NumVertices - 1);

			CornerIndex[CornerIdx] = VertIndex;
			P[CornerIdx] = VertexAccessor(VertIndex);

			// Find/add this vert to index buffer
			TArray<uint32> VertOverlaps;
			DuplicateVertexMap.MultiFind(VertIndex, VertOverlaps);

			// Remember which triangles map to this vert
			VertToTriMap.AddUnique(VertIndex, TriIdx);
			VertToTriSmoothMap.AddUnique(VertIndex, TriIdx);

			// Also update map of triangles that 'overlap' this vert (ie don't match UV, but do match smoothing) and should be considered when calculating normal
			for (int32 OverlapIdx = 0; OverlapIdx < VertOverlaps.Num(); OverlapIdx++)
			{
				// For each vert we overlap..
				int32 OverlapVertIdx = VertOverlaps[OverlapIdx];

				// Add this triangle to that vert
				VertToTriSmoothMap.AddUnique(OverlapVertIdx, TriIdx);

				// And add all of its triangles to us
				TArray<uint32> OverlapTris;
				VertToTriMap.MultiFind(OverlapVertIdx, OverlapTris);
				for (int32 OverlapTriIdx = 0; OverlapTriIdx < OverlapTris.Num(); OverlapTriIdx++)
				{
					VertToTriSmoothMap.AddUnique(VertIndex, OverlapTris[OverlapTriIdx]);
				}
			}
		}

		// Calculate triangle edge vectors and normal
		const FVector Edge21 = P[1] - P[2];
		const FVector Edge20 = P[0] - P[2];
		const FVector TriNormal = (Edge21 ^ Edge20).GetSafeNormal();

		// If we have UVs, use those to calculate
		if (NumUVs == NumVertices)
		{
			const FVector2D T1 = UVAccessor(CornerIndex[0]);
			const FVector2D T2 = UVAccessor(CornerIndex[1]);
			const FVector2D T3 = UVAccessor(CornerIndex[2]);

// 			float X1 = P[1].X - P[0].X;
// 			float X2 = P[2].X - P[0].X;
// 			float Y1 = P[1].Y - P[0].Y;
// 			float Y2 = P[2].Y - P[0].Y;
// 			float Z1 = P[1].Z - P[0].Z;
// 			float Z2 = P[2].Z - P[0].Z;
// 
// 			float S1 = U1.X - U0.X;
// 			float S2 = U2.X - U0.X;
// 			float T1 = U1.Y - U0.Y;
// 			float T2 = U2.Y - U0.Y;
// 
// 			float R = 1.0f / (S1 * T2 - S2 * T1);
// 			FaceTangentX[TriIdx] = FVector((T2 * X1 - T1 * X2) * R, (T2 * Y1 - T1 * Y2) * R,
// 				(T2 * Z1 - T1 * Z2) * R);
// 			FaceTangentY[TriIdx] = FVector((S1 * X2 - S2 * X1) * R, (S1 * Y2 - S2 * Y1) * R,
// 				(S1 * Z2 - S2 * Z1) * R);




			FMatrix	ParameterToLocal(
				FPlane(P[1].X - P[0].X, P[1].Y - P[0].Y, P[1].Z - P[0].Z, 0),
				FPlane(P[2].X - P[0].X, P[2].Y - P[0].Y, P[2].Z - P[0].Z, 0),
				FPlane(P[0].X, P[0].Y, P[0].Z, 0),
				FPlane(0, 0, 0, 1)
			);

			FMatrix ParameterToTexture(
				FPlane(T2.X - T1.X, T2.Y - T1.Y, 0, 0),
				FPlane(T3.X - T1.X, T3.Y - T1.Y, 0, 0),
				FPlane(T1.X, T1.Y, 1, 0),
				FPlane(0, 0, 0, 1)
			);

			// Use InverseSlow to catch singular matrices.  Inverse can miss this sometimes.
			const FMatrix TextureToLocal = ParameterToTexture.Inverse() * ParameterToLocal;

			FaceTangentX[TriIdx] = TextureToLocal.TransformVector(FVector(1, 0, 0)).GetSafeNormal();
			FaceTangentY[TriIdx] = TextureToLocal.TransformVector(FVector(0, 1, 0)).GetSafeNormal();
		}
		else
		{
			FaceTangentX[TriIdx] = Edge20.GetSafeNormal();
			FaceTangentY[TriIdx] = (FaceTangentX[TriIdx] ^ TriNormal).GetSafeNormal();
		}

		FaceTangentZ[TriIdx] = TriNormal;
	}


	// Arrays to accumulate tangents into
	TArray<FVector> VertexTangentXSum, VertexTangentYSum, VertexTangentZSum;
	VertexTangentXSum.AddZeroed(NumVertices);
	VertexTangentYSum.AddZeroed(NumVertices);
	VertexTangentZSum.AddZeroed(NumVertices);

	// For each vertex..
	for (int VertxIdx = 0; VertxIdx < NumVertices; VertxIdx++)
	{
		// Find relevant triangles for normal
		TArray<uint32> SmoothTris;
		VertToTriSmoothMap.MultiFind(VertxIdx, SmoothTris);

		for (int i = 0; i < SmoothTris.Num(); i++)
		{
			uint32 TriIdx = SmoothTris[i];
			VertexTangentZSum[VertxIdx] += FaceTangentZ[TriIdx];
		}

		// Find relevant triangles for tangents
		TArray<uint32> TangentTris;
		VertToTriMap.MultiFind(VertxIdx, TangentTris);

		for (int i = 0; i < TangentTris.Num(); i++)
		{
			uint32 TriIdx = TangentTris[i];
			VertexTangentXSum[VertxIdx] += FaceTangentX[TriIdx];
			VertexTangentYSum[VertxIdx] += FaceTangentY[TriIdx];
		}
	}

	// Finally, normalize tangents and build output arrays	
	for (int VertxIdx = 0; VertxIdx < NumVertices; VertxIdx++)
	{
		FVector& TangentX = VertexTangentXSum[VertxIdx];
		FVector& TangentY = VertexTangentYSum[VertxIdx];
		FVector& TangentZ = VertexTangentZSum[VertxIdx];

		TangentX.Normalize();
		//TangentY.Normalize();
		TangentZ.Normalize();

		// Use Gram-Schmidt orthogonalization to make sure X is orthonormal with Z
		TangentX -= TangentZ * (TangentZ | TangentX);
		TangentX.Normalize();
		TangentY.Normalize();


		TangentSetter(VertxIdx, TangentX, TangentY, TangentZ);
	}
}
Example #19
0
AItem* AMech_RPGCharacter::CalucluateItemDrop(UGroup* inGroup, ItemEnumns::ItemType type) {
	TMultiMap<int32, int32> gradeMap;
	int32 outputGrade;
	int32 outputQuality;
	bool upgradeGrade = FMath::RandHelper(100) <= 70;// upgradeGradeChance;
	bool upgradeQuality = FMath::RandHelper(100) <= 70; //upgradeQualityChance;

	float totalItems = 0;
	float lowestGrade = AItem::HighestItemLevel;
	float totalGrade = 0;
	float meanGrade = 0;
	int32 modeGrade = 0;

	float lowestQuality = 20;
	float totalQuality = 0;
	float meanQuality = 0;
	int32 modeQuality = 0;

	for (AMech_RPGCharacter* member : inGroup->GetMembers()) {
		//for (AItem* item : member->GetInventory()->GetItems()) {
		AItem* item = member->GetCurrentWeapon();
		if (item != nullptr && item->GetType() == type) {
			totalItems++;
			totalGrade += item->GetGrade();
			totalQuality += item->GetQuality();

			if (!gradeMap.Contains(item->GetGrade())) {
				gradeMap.Add(item->GetGrade(), 1);
			}
			else {
				gradeMap.Add(item->GetGrade(), (int32)(*gradeMap.Find(item->GetGrade()) + 1));
			}

			if (item->GetQuality() < lowestQuality) {
				lowestQuality = item->GetQuality();
			}

			if (item->GetGrade() < lowestGrade) {
				lowestGrade = item->GetGrade();
			}
		}
		//}
	}

	meanGrade = totalGrade / totalItems;
	meanQuality = totalQuality / totalItems;

	TPair<int32, int32> heighestValue;
	heighestValue.Key = 1;
	heighestValue.Value = 0;

	for (auto& map : gradeMap) {
		// Found a higher quantity
		if (map.Value > heighestValue.Value) {
			heighestValue = map;
		}
		// Found the same quantity, only set if the grade is higher
		else if (map.Value == heighestValue.Value && map.Key > heighestValue.Key) {
			heighestValue = map;
		}
	}

	//if (upgradeGrade) 
	meanGrade++;
	//if (upgradeQuality)
	meanQuality++;

	outputGrade = FMath::RoundHalfToEven(MAX(meanGrade, heighestValue.Key));
	outputQuality = FMath::RoundHalfToEven(meanQuality);

	AItem* newItem = AItem::CreateItemByType(type, GetWorld(), outputGrade, outputQuality);

	if (newItem != nullptr) {
		newItem->SetItemOwner(this);
		return newItem;
	}

	return nullptr;
}
Example #20
0
void FStatsMemoryDumpCommand::ProcessMemoryOperations( const TMap<int64, FStatPacketArray>& CombinedHistory )
{
	// This is only example code, no fully implemented, may sometimes crash.
	// This code is not optimized. 
	double PreviousSeconds = FPlatformTime::Seconds();
	uint64 NumMemoryOperations = 0;

	// Generate frames
	TArray<int64> Frames;
	CombinedHistory.GenerateKeyArray( Frames );
	Frames.Sort();

	// Raw stats callstack for this stat packet array.
	TMap<FName, FStackState> StackStates;

	// All allocation ordered by the sequence tag.
	// There is an assumption that the sequence tag will not turn-around.
	//TMap<uint32, FAllocationInfo> SequenceAllocationMap;
	TArray<FAllocationInfo> SequenceAllocationArray;

	// Pass 1.
	// Read all stats messages, parse all memory operations and decode callstacks.
	const int64 FirstFrame = 0;
	PreviousSeconds -= NumSecondsBetweenLogs;
	for( int32 FrameIndex = 0; FrameIndex < Frames.Num(); ++FrameIndex )
	{
        {
            const double CurrentSeconds = FPlatformTime::Seconds();
            if( CurrentSeconds > PreviousSeconds + NumSecondsBetweenLogs )
            {
                UE_LOG( LogStats, Warning, TEXT( "Processing frame %i/%i" ), FrameIndex+1, Frames.Num() );
                PreviousSeconds = CurrentSeconds;
            }
        }

		const int64 TargetFrame = Frames[FrameIndex];
		const int64 Diff = TargetFrame - FirstFrame;
		const FStatPacketArray& Frame = CombinedHistory.FindChecked( TargetFrame );

		bool bAtLeastOnePacket = false;
		for( int32 PacketIndex = 0; PacketIndex < Frame.Packets.Num(); PacketIndex++ )
		{
            {
                const double CurrentSeconds = FPlatformTime::Seconds();
                if( CurrentSeconds > PreviousSeconds + NumSecondsBetweenLogs )
                {
                    UE_LOG( LogStats, Log, TEXT( "Processing packet %i/%i" ), PacketIndex, Frame.Packets.Num() );
                    PreviousSeconds = CurrentSeconds;
                    bAtLeastOnePacket = true;
                }
            }

			const FStatPacket& StatPacket = *Frame.Packets[PacketIndex];
			const FName& ThreadFName = StatsThreadStats.Threads.FindChecked( StatPacket.ThreadId );
			const uint32 NewThreadID = ThreadIDtoStatID.FindChecked( StatPacket.ThreadId );

			FStackState* StackState = StackStates.Find( ThreadFName );
			if( !StackState )
			{
				StackState = &StackStates.Add( ThreadFName );
				StackState->Stack.Add( ThreadFName );
				StackState->Current = ThreadFName;
			}

			const FStatMessagesArray& Data = StatPacket.StatMessages;

			int32 LastPct = 0;
			const int32 NumDataElements = Data.Num();
			const int32 OnerPercent = FMath::Max( NumDataElements / 100, 1024 );
			bool bAtLeastOneMessage = false;
			for( int32 Index = 0; Index < NumDataElements; Index++ )
			{
				if( Index % OnerPercent )
				{
					const double CurrentSeconds = FPlatformTime::Seconds();
					if( CurrentSeconds > PreviousSeconds + NumSecondsBetweenLogs )
					{
						const int32 CurrentPct = int32( 100.0*(Index + 1) / NumDataElements );
						UE_LOG( LogStats, Log, TEXT( "Processing %3i%% (%i/%i) stat messages" ), CurrentPct, Index, NumDataElements );
						PreviousSeconds = CurrentSeconds;
						bAtLeastOneMessage = true;
					}
				}

				const FStatMessage& Item = Data[Index];

				const EStatOperation::Type Op = Item.NameAndInfo.GetField<EStatOperation>();
				const FName RawName = Item.NameAndInfo.GetRawName();

				if( Op == EStatOperation::CycleScopeStart || Op == EStatOperation::CycleScopeEnd || Op == EStatOperation::Memory )
				{
					if( Op == EStatOperation::CycleScopeStart )
					{
						StackState->Stack.Add( RawName );
						StackState->Current = RawName;
					}
					else if( Op == EStatOperation::Memory )
					{
						// Experimental code used only to test the implementation.
						// First memory operation is Alloc or Free
						const uint64 EncodedPtr = Item.GetValue_Ptr();
						const bool bIsAlloc = (EncodedPtr & (uint64)EMemoryOperation::Alloc) != 0;
						const bool bIsFree = (EncodedPtr & (uint64)EMemoryOperation::Free) != 0;
						const uint64 Ptr = EncodedPtr & ~(uint64)EMemoryOperation::Mask;
						if( bIsAlloc )
						{
							NumMemoryOperations++;
							// @see FStatsMallocProfilerProxy::TrackAlloc
							// After alloc ptr message there is always alloc size message and the sequence tag.
							Index++;
							const FStatMessage& AllocSizeMessage = Data[Index];
							const int64 AllocSize = AllocSizeMessage.GetValue_int64();

							// Read operation sequence tag.
							Index++;
							const FStatMessage& SequenceTagMessage = Data[Index];
							const uint32 SequenceTag = SequenceTagMessage.GetValue_int64();

							// Create a callstack.
							TArray<FName> StatsBasedCallstack;
							for( const auto& StackName : StackState->Stack )
							{
								StatsBasedCallstack.Add( StackName );
							}

							// Add a new allocation.
							SequenceAllocationArray.Add(
								FAllocationInfo(
								Ptr,
								AllocSize,
								StatsBasedCallstack,
								SequenceTag,
								EMemoryOperation::Alloc,
								StackState->bIsBrokenCallstack
								) );
						}
						else if( bIsFree )
						{
							NumMemoryOperations++;
							// Read operation sequence tag.
							Index++;
							const FStatMessage& SequenceTagMessage = Data[Index];
							const uint32 SequenceTag = SequenceTagMessage.GetValue_int64();

							// Create a callstack.
							/*
							TArray<FName> StatsBasedCallstack;
							for( const auto& RawName : StackState->Stack )
							{
								StatsBasedCallstack.Add( RawName );
							}
							*/

							// Add a new free.
							SequenceAllocationArray.Add(
								FAllocationInfo(
								Ptr,		
								0,
								TArray<FName>()/*StatsBasedCallstack*/,					
								SequenceTag,
								EMemoryOperation::Free,
								StackState->bIsBrokenCallstack
								) );
						}
						else
						{
							UE_LOG( LogStats, Warning, TEXT( "Pointer from a memory operation is invalid" ) );
						}
					}
					else if( Op == EStatOperation::CycleScopeEnd )
					{
						if( StackState->Stack.Num() > 1 )
						{
							const FName ScopeStart = StackState->Stack.Pop();
							const FName ScopeEnd = Item.NameAndInfo.GetRawName();

							check( ScopeStart == ScopeEnd );

							StackState->Current = StackState->Stack.Last();

							// The stack should be ok, but it may be partially broken.
							// This will happen if memory profiling starts in the middle of executing a background thread.
							StackState->bIsBrokenCallstack = false;
						}
						else
						{
							const FName ShortName = Item.NameAndInfo.GetShortName();

							UE_LOG( LogStats, Warning, TEXT( "Broken cycle scope end %s/%s, current %s" ),
									*ThreadFName.ToString(),
									*ShortName.ToString(),
									*StackState->Current.ToString() );

							// The stack is completely broken, only has the thread name and the last cycle scope.
							// Rollback to the thread node.
							StackState->bIsBrokenCallstack = true;
							StackState->Stack.Empty();
							StackState->Stack.Add( ThreadFName );
							StackState->Current = ThreadFName;
						}
					}
				}
			}
			if( bAtLeastOneMessage )
			{
				PreviousSeconds -= NumSecondsBetweenLogs;
			}
		}
		if( bAtLeastOnePacket )
		{
			PreviousSeconds -= NumSecondsBetweenLogs;
		}
	}

	UE_LOG( LogStats, Warning, TEXT( "NumMemoryOperations:   %llu" ), NumMemoryOperations );
	UE_LOG( LogStats, Warning, TEXT( "SequenceAllocationNum: %i" ), SequenceAllocationArray.Num() );

	// Pass 2.
	/*
	TMap<uint32,FAllocationInfo> UniqueSeq;
	TMultiMap<uint32,FAllocationInfo> OriginalAllocs;
	TMultiMap<uint32,FAllocationInfo> BrokenAllocs;
	for( const FAllocationInfo& Alloc : SequenceAllocationArray )
	{
		const FAllocationInfo* Found = UniqueSeq.Find(Alloc.SequenceTag);
		if( !Found )
		{
			UniqueSeq.Add(Alloc.SequenceTag,Alloc);
		}
		else
		{
			OriginalAllocs.Add(Alloc.SequenceTag, *Found);
			BrokenAllocs.Add(Alloc.SequenceTag, Alloc);
		}
	}
	*/

	// Sort all memory operation by the sequence tag, iterate through all operation and generate memory usage.
	SequenceAllocationArray.Sort( TLess<FAllocationInfo>() );

	// Alive allocations.
	TMap<uint64, FAllocationInfo> AllocationMap;
	TMultiMap<uint64, FAllocationInfo> FreeWithoutAllocMap;
	TMultiMap<uint64, FAllocationInfo> DuplicatedAllocMap;
	int32 NumDuplicatedMemoryOperations = 0;
	int32 NumFWAMemoryOperations = 0; // FreeWithoutAlloc

	UE_LOG( LogStats, Warning, TEXT( "Generating memory operations map" ) );
	const int32 NumSequenceAllocations = SequenceAllocationArray.Num();
	const int32 OnePercent = FMath::Max( NumSequenceAllocations / 100, 1024 );
	for( int32 Index = 0; Index < NumSequenceAllocations; Index++ )
	{
		if( Index % OnePercent )
		{
			const double CurrentSeconds = FPlatformTime::Seconds();
			if( CurrentSeconds > PreviousSeconds + NumSecondsBetweenLogs )
			{
				const int32 CurrentPct = int32( 100.0*(Index + 1) / NumSequenceAllocations );
				UE_LOG( LogStats, Log, TEXT( "Processing allocations %3i%% (%10i/%10i)" ), CurrentPct, Index + 1, NumSequenceAllocations );
				PreviousSeconds = CurrentSeconds;
			}
		}

		const FAllocationInfo& Alloc = SequenceAllocationArray[Index];
		const EMemoryOperation MemOp = Alloc.Op;
		const uint64 Ptr = Alloc.Ptr;
		const int64 Size = Alloc.Size;
		const uint32 SequenceTag = Alloc.SequenceTag;

		if( MemOp == EMemoryOperation::Alloc )
		{
			const FAllocationInfo* Found = AllocationMap.Find( Ptr );

			if( !Found )
			{
				AllocationMap.Add( Ptr, Alloc );
			}
			else
			{
				const FAllocationInfo* FoundAndFreed = FreeWithoutAllocMap.Find( Found->Ptr );
				const FAllocationInfo* FoundAndAllocated = FreeWithoutAllocMap.Find( Alloc.Ptr );

#if	_DEBUG
				if( FoundAndFreed )
				{
					const FString FoundAndFreedCallstack = GetCallstack( FoundAndFreed->EncodedCallstack );
				}

				if( FoundAndAllocated )
				{
					const FString FoundAndAllocatedCallstack = GetCallstack( FoundAndAllocated->EncodedCallstack );
				}

				NumDuplicatedMemoryOperations++;


				const FString FoundCallstack = GetCallstack( Found->EncodedCallstack );
				const FString AllocCallstack = GetCallstack( Alloc.EncodedCallstack );
#endif // _DEBUG

				// Replace pointer.
				AllocationMap.Add( Ptr, Alloc );
				// Store the old pointer.
				DuplicatedAllocMap.Add( Ptr, *Found );
			}
		}
		else if( MemOp == EMemoryOperation::Free )
		{
			const FAllocationInfo* Found = AllocationMap.Find( Ptr );
			if( Found )
			{
				const bool bIsValid = Alloc.SequenceTag > Found->SequenceTag;
				if( !bIsValid )
				{
					UE_LOG( LogStats, Warning, TEXT( "InvalidFree Ptr: %llu, Seq: %i/%i" ), Ptr, SequenceTag, Found->SequenceTag );
				}
				AllocationMap.Remove( Ptr );
			}
			else
			{
				FreeWithoutAllocMap.Add( Ptr, Alloc );
				NumFWAMemoryOperations++;
			}
		}
	}

	UE_LOG( LogStats, Warning, TEXT( "NumDuplicatedMemoryOperations: %i" ), NumDuplicatedMemoryOperations );
	UE_LOG( LogStats, Warning, TEXT( "NumFWAMemoryOperations:        %i" ), NumFWAMemoryOperations );

	// Dump problematic allocations
	DuplicatedAllocMap.ValueSort( FAllocationInfoGreater() );
	//FreeWithoutAllocMap

	uint64 TotalDuplicatedMemory = 0;
	for( const auto& It : DuplicatedAllocMap )
	{
		const FAllocationInfo& Alloc = It.Value;
		TotalDuplicatedMemory += Alloc.Size;
	}

	UE_LOG( LogStats, Warning, TEXT( "Dumping duplicated alloc map" ) );
	const float MaxPctDisplayed = 0.80f;
	uint64 DisplayedSoFar = 0;
	for( const auto& It : DuplicatedAllocMap )
	{
		const FAllocationInfo& Alloc = It.Value;
		const FString AllocCallstack = GetCallstack( Alloc.EncodedCallstack );
		UE_LOG( LogStats, Log, TEXT( "%lli (%.2f MB) %s" ), Alloc.Size, Alloc.Size / 1024.0f / 1024.0f, *AllocCallstack );

		DisplayedSoFar += Alloc.Size;

		const float CurrentPct = (float)DisplayedSoFar / (float)TotalDuplicatedMemory;
		if( CurrentPct > MaxPctDisplayed )
		{
			break;
		}
	}

	GenerateMemoryUsageReport( AllocationMap );
}
void FRCPassPostProcessSelectionOutlineColor::Process(FRenderingCompositePassContext& Context)
{
	SCOPED_DRAW_EVENT(Context.RHICmdList, PostProcessSelectionOutlineBuffer, DEC_SCENE_ITEMS);

	const FPooledRenderTargetDesc* SceneColorInputDesc = GetInputDesc(ePId_Input0);

	if(!SceneColorInputDesc)
	{
		// input is not hooked up correctly
		return;
	}

	const FViewInfo& View = Context.View;
	FIntRect ViewRect = View.ViewRect;
	FIntPoint SrcSize = SceneColorInputDesc->Extent;

	// Get the output render target
	const FSceneRenderTargetItem& DestRenderTarget = PassOutputs[0].RequestSurface(Context);
	
	// Set the render target/viewport.
	SetRenderTarget(Context.RHICmdList, FTextureRHIParamRef(), DestRenderTarget.TargetableTexture);
	// This is a reversed Z depth surface, so 0.0f is the far plane.
	Context.RHICmdList.Clear(false, FLinearColor(0, 0, 0, 0), true, 0.0f, true, 0, FIntRect());
	Context.SetViewportAndCallRHI(ViewRect);

	if (View.Family->EngineShowFlags.Selection)
	{
		const bool bUseGetMeshElements = ShouldUseGetDynamicMeshElements();

		if (bUseGetMeshElements)
		{
			FHitProxyDrawingPolicyFactory::ContextType FactoryContext;

			//@todo - use memstack
			TMap<FName, int32> ActorNameToStencilIndex;
			ActorNameToStencilIndex.Add(NAME_BSP, 1);

			Context.RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI());
			Context.RHICmdList.SetBlendState(TStaticBlendStateWriteMask<CW_NONE, CW_NONE, CW_NONE, CW_NONE>::GetRHI());

			for (int32 MeshBatchIndex = 0; MeshBatchIndex < View.DynamicMeshElements.Num(); MeshBatchIndex++)
			{
				const FMeshBatchAndRelevance& MeshBatchAndRelevance = View.DynamicMeshElements[MeshBatchIndex];
				const FPrimitiveSceneProxy* PrimitiveSceneProxy = MeshBatchAndRelevance.PrimitiveSceneProxy;

				if (PrimitiveSceneProxy->IsSelected() && MeshBatchAndRelevance.Mesh->bUseSelectionOutline)
				{
					const int32* AssignedStencilIndexPtr = ActorNameToStencilIndex.Find(PrimitiveSceneProxy->GetOwnerName());

					if (!AssignedStencilIndexPtr)
					{
						AssignedStencilIndexPtr = &ActorNameToStencilIndex.Add(PrimitiveSceneProxy->GetOwnerName(), ActorNameToStencilIndex.Num() + 1);
					}

					// This is a reversed Z depth surface, using CF_GreaterEqual.
					// Note that the stencil value will overflow with enough selected objects
					Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState<true, CF_GreaterEqual, true, CF_Always, SO_Keep, SO_Keep, SO_Replace>::GetRHI(), *AssignedStencilIndexPtr);

					const FMeshBatch& MeshBatch = *MeshBatchAndRelevance.Mesh;
					FHitProxyDrawingPolicyFactory::DrawDynamicMesh(Context.RHICmdList, View, FactoryContext, MeshBatch, false, true, MeshBatchAndRelevance.PrimitiveSceneProxy, MeshBatch.BatchHitProxyId);
				}
			}
		}
		else if (View.VisibleDynamicPrimitives.Num() > 0)
		{
			TDynamicPrimitiveDrawer<FHitProxyDrawingPolicyFactory> Drawer(Context.RHICmdList, &View, FHitProxyDrawingPolicyFactory::ContextType(), true, false, false, true);
			TMultiMap<FName, const FPrimitiveSceneInfo*> PrimitivesByActor;

			for (int32 PrimitiveIndex = 0; PrimitiveIndex < View.VisibleDynamicPrimitives.Num();PrimitiveIndex++)
			{
				const FPrimitiveSceneInfo* PrimitiveSceneInfo = View.VisibleDynamicPrimitives[PrimitiveIndex];

				// Only draw the primitive if relevant
				if(PrimitiveSceneInfo->Proxy->IsSelected())
				{
					PrimitivesByActor.Add(PrimitiveSceneInfo->Proxy->GetOwnerName(), PrimitiveSceneInfo);
				}
			}

			if (PrimitivesByActor.Num())
			{
				// 0 means no object, 1 means BSP so we start with 2
				uint32 StencilValue = 2;

				Context.RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI());
				Context.RHICmdList.SetBlendState(TStaticBlendStateWriteMask<CW_NONE, CW_NONE, CW_NONE, CW_NONE>::GetRHI());

				// Sort by actor
				TArray<FName> Actors;
				PrimitivesByActor.GetKeys(Actors);
				for( TArray<FName>::TConstIterator ActorIt(Actors); ActorIt; ++ActorIt )
				{
					bool bBSP = *ActorIt == NAME_BSP;
					if (bBSP)
					{
						// This is a reversed Z depth surface, using CF_GreaterEqual.
						Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState<true, CF_GreaterEqual, true, CF_Always, SO_Keep, SO_Keep, SO_Replace>::GetRHI(), 1);
					}
					else
					{
						// This is a reversed Z depth surface, using CF_GreaterEqual.
						Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState<true, CF_GreaterEqual, true, CF_Always, SO_Keep, SO_Keep, SO_Replace>::GetRHI(), StencilValue);

						// we want to use 1..255 for all objects, not correct silhouettes after that is acceptable
						StencilValue = (StencilValue == 255) ? 2 : (StencilValue + 1);
					}

					TArray<const FPrimitiveSceneInfo*> Primitives;
					PrimitivesByActor.MultiFind(*ActorIt, Primitives);

					for( TArray<const FPrimitiveSceneInfo*>::TConstIterator PrimIt(Primitives); PrimIt; ++PrimIt )
					{
						const FPrimitiveSceneInfo* PrimitiveSceneInfo = *PrimIt;
						// Render the object to the stencil/depth buffer
						Drawer.SetPrimitive(PrimitiveSceneInfo->Proxy);
						PrimitiveSceneInfo->Proxy->DrawDynamicElements(&Drawer, &View);
					}
				}
			}
		}

		// to get an outline around the objects if it's partly outside of the screen
		{
			FIntRect InnerRect = ViewRect;

			// 1 as we have an outline that is that thick
			InnerRect.InflateRect(-1);

			// We could use Clear with InnerRect but this is just an optimization - on some hardware it might do a full clear (and we cannot disable yet)
			//			RHICmdList.Clear(false, FLinearColor(0, 0, 0, 0), true, 0.0f, true, 0, InnerRect);
			// so we to 4 clears - one for each border.

			// top
			Context.RHICmdList.SetScissorRect(true, ViewRect.Min.X, ViewRect.Min.Y, ViewRect.Max.X, InnerRect.Min.Y);
			Context.RHICmdList.Clear(false, FLinearColor(0, 0, 0, 0), true, 0.0f, true, 0, FIntRect());
			// bottom
			Context.RHICmdList.SetScissorRect(true, ViewRect.Min.X, InnerRect.Max.Y, ViewRect.Max.X, ViewRect.Max.Y);
			Context.RHICmdList.Clear(false, FLinearColor(0, 0, 0, 0), true, 0.0f, true, 0, FIntRect());
			// left
			Context.RHICmdList.SetScissorRect(true, ViewRect.Min.X, ViewRect.Min.Y, InnerRect.Min.X, ViewRect.Max.Y);
			Context.RHICmdList.Clear(false, FLinearColor(0, 0, 0, 0), true, 0.0f, true, 0, FIntRect());
			// right
			Context.RHICmdList.SetScissorRect(true, InnerRect.Max.X, ViewRect.Min.Y, ViewRect.Max.X, ViewRect.Max.Y);
			Context.RHICmdList.Clear(false, FLinearColor(0, 0, 0, 0), true, 0.0f, true, 0, FIntRect());

			Context.RHICmdList.SetScissorRect(false, 0, 0, 0, 0);
		}
	}
	
	// Resolve to the output
	Context.RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams());
}
void FPropertyTable::UpdateRows()
{
	if( Orientation == EPropertyTableOrientation::AlignPropertiesInColumns )
	{
		TMultiMap< UObject*, TSharedRef< IPropertyTableRow > > RowsMap;

		for (int RowIdx = 0; RowIdx < Rows.Num(); ++RowIdx)
		{
			RowsMap.Add(Rows[RowIdx]->GetDataSource()->AsUObject().Get(), Rows[RowIdx]);
		}

		Rows.Empty();
		for( auto ObjectIter = SourceObjects.CreateConstIterator(); ObjectIter; ++ObjectIter )
		{
			const TWeakObjectPtr< UObject >& Object = *ObjectIter;

			if( Object.IsValid() )
			{
				const TSharedRef< FObjectPropertyNode > ObjectNode = GetObjectPropertyNode( Object );
				const TSharedPtr< FPropertyNode > PropertyNode = FPropertyNode::FindPropertyNodeByPath( RootPath, ObjectNode );

				//@todo This system will need to change in order to properly support arrays [11/30/2012 Justin.Sargent]
				if ( PropertyNode.IsValid() )
				{
					UProperty* Property = PropertyNode->GetProperty();

					if ( Property != NULL && Property->IsA( UArrayProperty::StaticClass() ) )
					{
						for (int ChildIdx = 0; ChildIdx < PropertyNode->GetNumChildNodes(); ChildIdx++)
						{
							TSharedPtr< FPropertyNode > ChildNode = PropertyNode->GetChildNode( ChildIdx );

							FPropertyInfo Extension;
							Extension.Property = ChildNode->GetProperty();
							Extension.ArrayIndex = ChildNode->GetArrayIndex();
							TSharedRef< FPropertyPath > Path = FPropertyPath::CreateEmpty()->ExtendPath( Extension );
							TArray< TSharedRef< IPropertyTableRow > > MapResults;
							bool Found = false;

							RowsMap.MultiFind(Object.Get(), MapResults);

							for (int MapIdx = 0; MapIdx < MapResults.Num(); ++MapIdx)
							{
								if (FPropertyPath::AreEqual(Path, MapResults[MapIdx]->GetPartialPath()))
								{
									Found = true;
									Rows.Add(MapResults[MapIdx]);
									break;
								}
							}

							if (!Found)
							{
								Rows.Add( MakeShareable( new FPropertyTableRow( SharedThis( this ), Object, Path ) ) );
							}
						}
					}
					else
					{
						TArray< TSharedRef< IPropertyTableRow > > MapResults;
						bool Found = false;

						RowsMap.MultiFind(Object.Get(), MapResults);

						for (int MapIdx = 0; MapIdx < MapResults.Num(); ++MapIdx)
						{
							if (MapResults[MapIdx]->GetPartialPath()->GetNumProperties() == 0)
							{
								Found = true;
								Rows.Add(MapResults[MapIdx]);
								break;
							}
						}

						if (!Found)
						{
							Rows.Add( MakeShareable( new FPropertyTableRow( SharedThis( this ), Object ) ) );
						}
					}
				}
			}
		}
	}

	const TSharedPtr< IPropertyTableColumn > Column = SortedByColumn.Pin();
	if ( Column.IsValid() && SortedColumnMode != EColumnSortMode::None )
	{
		Column->Sort( Rows, SortedColumnMode );
	}

	RowsChanged.Broadcast();
}
void FRedirectCollector::ResolveStringAssetReference(FString FilterPackage)
{
    SCOPE_REDIRECT_TIMER(ResolveTimeTotal);


    FName FilterPackageFName = NAME_None;
    if (!FilterPackage.IsEmpty())
    {
        FilterPackageFName = FName(*FilterPackage);
    }

    TMultiMap<FName, FPackagePropertyPair> SkippedReferences;
    SkippedReferences.Empty(StringAssetReferences.Num());
    while ( StringAssetReferences.Num())
    {

        TMultiMap<FName, FPackagePropertyPair> CurrentReferences;
        Swap(StringAssetReferences, CurrentReferences);

        for (const auto& CurrentReference : CurrentReferences)
        {
            const FName& ToLoadFName = CurrentReference.Key;
            const FPackagePropertyPair& RefFilenameAndProperty = CurrentReference.Value;

            if ((FilterPackageFName != NAME_None) && // not using a filter
                    (FilterPackageFName != RefFilenameAndProperty.GetCachedPackageName()) && // this is the package we are looking for
                    (RefFilenameAndProperty.GetCachedPackageName() != NAME_None) // if we have an empty package name then process it straight away
               )
            {
                // If we have a valid filter and it doesn't match, skip this reference
                SkippedReferences.Add(ToLoadFName, RefFilenameAndProperty);
                continue;
            }

            const FString ToLoad = ToLoadFName.ToString();

            if (FCoreDelegates::LoadStringAssetReferenceInCook.IsBound())
            {
                SCOPE_REDIRECT_TIMER(ResolveTimeDelegate);
                if (FCoreDelegates::LoadStringAssetReferenceInCook.Execute(ToLoad) == false)
                {
                    // Skip this reference
                    continue;
                }
            }

            if (ToLoad.Len() > 0 )
            {
                SCOPE_REDIRECT_TIMER(ResolveTimeLoad);

                UE_LOG(LogRedirectors, Verbose, TEXT("String Asset Reference '%s'"), *ToLoad);
                UE_CLOG(RefFilenameAndProperty.GetProperty().ToString().Len(), LogRedirectors, Verbose, TEXT("    Referenced by '%s'"), *RefFilenameAndProperty.GetProperty().ToString());

                StringAssetRefFilenameStack.Push(RefFilenameAndProperty.GetPackage().ToString());

                UObject *Loaded = LoadObject<UObject>(NULL, *ToLoad, NULL, RefFilenameAndProperty.GetReferencedByEditorOnlyProperty() ? LOAD_EditorOnly : LOAD_None, NULL);
                StringAssetRefFilenameStack.Pop();

                UObjectRedirector* Redirector = dynamic_cast<UObjectRedirector*>(Loaded);
                if (Redirector)
                {
                    UE_LOG(LogRedirectors, Verbose, TEXT("    Found redir '%s'"), *Redirector->GetFullName());
                    FRedirection Redir;
                    Redir.PackageFilename = RefFilenameAndProperty.GetPackage().ToString();
                    Redir.RedirectorName = Redirector->GetFullName();
                    Redir.RedirectorPackageFilename = Redirector->GetLinker()->Filename;
                    CA_SUPPRESS(28182)
                    Redir.DestinationObjectName = Redirector->DestinationObject->GetFullName();
                    Redirections.AddUnique(Redir);
                    Loaded = Redirector->DestinationObject;
                }
                if (Loaded)
                {
                    if (FCoreUObjectDelegates::PackageLoadedFromStringAssetReference.IsBound())
                    {
                        FCoreUObjectDelegates::PackageLoadedFromStringAssetReference.Broadcast(Loaded->GetOutermost()->GetFName());
                    }
                    FString Dest = Loaded->GetPathName();
                    UE_LOG(LogRedirectors, Verbose, TEXT("    Resolved to '%s'"), *Dest);
                    if (Dest != ToLoad)
                    {
                        StringAssetRemap.Add(ToLoad, Dest);
                    }
                }
                else
                {
                    const FString Referencer = RefFilenameAndProperty.GetProperty().ToString().Len() ? RefFilenameAndProperty.GetProperty().ToString() : TEXT("Unknown");

                    int32 DotIndex = ToLoad.Find(TEXT("."));

                    FString PackageName = DotIndex != INDEX_NONE ? ToLoad.Left(DotIndex) : ToLoad;

                    if (FLinkerLoad::IsKnownMissingPackage(FName(*PackageName)) == false)
                    {
                        UE_LOG(LogRedirectors, Warning, TEXT("String Asset Reference '%s' was not found! (Referencer '%s')"), *ToLoad, *Referencer);
                    }
                }
            }

        }
    }

    check(StringAssetReferences.Num() == 0);
    // Add any skipped references back into the map for the next time this is called
    Swap(StringAssetReferences, SkippedReferences);
    // we shouldn't have any references left if we decided to resolve them all
    check((StringAssetReferences.Num() == 0) || (FilterPackageFName != NAME_None));
}
void FRedirectCollector::ResolveStringAssetReference(FString FilterPackage)
{
	TMultiMap<FString, FPackagePropertyPair> SkippedReferences;
	while (StringAssetReferences.Num())
	{
		TMultiMap<FString, FPackagePropertyPair>::TIterator First(StringAssetReferences);
		FString ToLoad = First.Key();
		FPackagePropertyPair RefFilenameAndProperty = First.Value();
		First.RemoveCurrent();
		
		if (FCoreDelegates::LoadStringAssetReferenceInCook.IsBound())
		{
			if (FCoreDelegates::LoadStringAssetReferenceInCook.Execute(ToLoad) == false)
			{
				// Skip this reference
				continue;
			}
		}

		if (!FilterPackage.IsEmpty() && FilterPackage != RefFilenameAndProperty.Package)
		{
			// If we have a valid filter and it doesn't match, skip this reference
			SkippedReferences.Add(ToLoad, RefFilenameAndProperty);
			continue;
		}

		if (ToLoad.Len() > 0)
		{
			UE_LOG(LogRedirectors, Verbose, TEXT("String Asset Reference '%s'"), *ToLoad);
			UE_CLOG(RefFilenameAndProperty.Property.Len(), LogRedirectors, Verbose, TEXT("    Referenced by '%s'"), *RefFilenameAndProperty.Property);

			StringAssetRefFilenameStack.Push(RefFilenameAndProperty.Package);

			UObject *Loaded = LoadObject<UObject>(NULL, *ToLoad, NULL, RefFilenameAndProperty.bReferencedByEditorOnlyProperty ? LOAD_EditorOnly : LOAD_None, NULL);
			StringAssetRefFilenameStack.Pop();

			UObjectRedirector* Redirector = dynamic_cast<UObjectRedirector*>(Loaded);
			if (Redirector)
			{
				UE_LOG(LogRedirectors, Verbose, TEXT("    Found redir '%s'"), *Redirector->GetFullName());
				FRedirection Redir;
				Redir.PackageFilename = RefFilenameAndProperty.Package;
				Redir.RedirectorName = Redirector->GetFullName();
				Redir.RedirectorPackageFilename = Redirector->GetLinker()->Filename;
				CA_SUPPRESS(28182)
				Redir.DestinationObjectName = Redirector->DestinationObject->GetFullName();
				Redirections.AddUnique(Redir);
				Loaded = Redirector->DestinationObject;
			}
			if (Loaded)
			{
				FString Dest = Loaded->GetPathName();
				UE_LOG(LogRedirectors, Verbose, TEXT("    Resolved to '%s'"), *Dest);
				if (Dest != ToLoad)
				{
					StringAssetRemap.Add(ToLoad, Dest);
				}
			}
			else
			{
				const FString Referencer = RefFilenameAndProperty.Property.Len() ? RefFilenameAndProperty.Property : TEXT("Unknown");
				UE_LOG(LogRedirectors, Warning, TEXT("String Asset Reference '%s' was not found! (Referencer '%s')"), *ToLoad, *Referencer);
			}
		}
	}

	// Add any skipped references back into the map for the next time this is called
	StringAssetReferences = SkippedReferences;
}
void FAudioComponentVisualizer::DrawVisualization( const UActorComponent* Component, const FSceneView* View, FPrimitiveDrawInterface* PDI )
{
	if(View->Family->EngineShowFlags.AudioRadius)
	{
		const UAudioComponent* AudioComp = Cast<const UAudioComponent>(Component);
		if(AudioComp != NULL)
		{
			const FTransform& Transform = AudioComp->ComponentToWorld;

			TMultiMap<EAttenuationShape::Type, FAttenuationSettings::AttenuationShapeDetails> ShapeDetailsMap;
			AudioComp->CollectAttenuationShapesForVisualization(ShapeDetailsMap);

			FVector Translation = Transform.GetTranslation();
			FVector UnitXAxis   = Transform.GetUnitAxis( EAxis::X );
			FVector UnitYAxis   = Transform.GetUnitAxis( EAxis::Y );
			FVector UnitZAxis   = Transform.GetUnitAxis( EAxis::Z );

			for ( auto It = ShapeDetailsMap.CreateConstIterator(); It; ++It )
			{
				FColor AudioOuterRadiusColor(255, 153, 0);
				FColor AudioInnerRadiusColor(216, 130, 0);

				const FAttenuationSettings::AttenuationShapeDetails& ShapeDetails = It.Value();
				switch(It.Key())
				{
				case EAttenuationShape::Box:

					if (ShapeDetails.Falloff > 0.f)
					{
						DrawOrientedWireBox( PDI, Translation, UnitXAxis, UnitYAxis, UnitZAxis, ShapeDetails.Extents + FVector(ShapeDetails.Falloff), AudioOuterRadiusColor, SDPG_World);
						DrawOrientedWireBox( PDI, Translation, UnitXAxis, UnitYAxis, UnitZAxis, ShapeDetails.Extents, AudioInnerRadiusColor, SDPG_World);
					}
					else
					{
						DrawOrientedWireBox( PDI, Translation, UnitXAxis, UnitYAxis, UnitZAxis, ShapeDetails.Extents, AudioOuterRadiusColor, SDPG_World);
					}
					break;

				case EAttenuationShape::Capsule:

					if (ShapeDetails.Falloff > 0.f)
					{
						DrawWireCapsule( PDI, Translation, UnitXAxis, UnitYAxis, UnitZAxis, AudioOuterRadiusColor, ShapeDetails.Extents.Y + ShapeDetails.Falloff, ShapeDetails.Extents.X + ShapeDetails.Falloff, 25, SDPG_World);
						DrawWireCapsule( PDI, Translation, UnitXAxis, UnitYAxis, UnitZAxis, AudioInnerRadiusColor, ShapeDetails.Extents.Y, ShapeDetails.Extents.X, 25, SDPG_World);
					}
					else
					{
						DrawWireCapsule( PDI, Translation, UnitXAxis, UnitYAxis, UnitZAxis, AudioOuterRadiusColor, ShapeDetails.Extents.Y, ShapeDetails.Extents.X, 25, SDPG_World);
					}
					break;

				case EAttenuationShape::Cone:
					{
						FTransform Origin = Transform;
						Origin.SetScale3D(FVector(1.f));
						Origin.SetTranslation(Translation - (UnitXAxis * ShapeDetails.ConeOffset));

						if (ShapeDetails.Falloff > 0.f || ShapeDetails.Extents.Z > 0.f)
						{
							float ConeRadius = ShapeDetails.Extents.X + ShapeDetails.Falloff + ShapeDetails.ConeOffset;
							float ConeAngle = ShapeDetails.Extents.Y + ShapeDetails.Extents.Z;
							DrawWireSphereCappedCone(PDI, Origin, ConeRadius, ConeAngle, 16, 4, 10, AudioOuterRadiusColor, SDPG_World);

							ConeRadius = ShapeDetails.Extents.X + ShapeDetails.ConeOffset;
							ConeAngle = ShapeDetails.Extents.Y;
							DrawWireSphereCappedCone(PDI, Origin, ConeRadius, ConeAngle, 16, 4, 10, AudioInnerRadiusColor, SDPG_World );
						}
						else
						{
							const float ConeRadius = ShapeDetails.Extents.X + ShapeDetails.ConeOffset;
							const float ConeAngle = ShapeDetails.Extents.Y;
							DrawWireSphereCappedCone(PDI, Origin, ConeRadius, ConeAngle, 16, 4, 10, AudioOuterRadiusColor, SDPG_World );
						}
					}
					break;

				case EAttenuationShape::Sphere:

					if (ShapeDetails.Falloff > 0.f)
					{
						DrawWireSphereAutoSides(PDI, Translation, AudioOuterRadiusColor, ShapeDetails.Extents.X + ShapeDetails.Falloff, SDPG_World);
						DrawWireSphereAutoSides(PDI, Translation, AudioInnerRadiusColor, ShapeDetails.Extents.X, SDPG_World);
					}
					else
					{
						DrawWireSphereAutoSides(PDI, Translation, AudioOuterRadiusColor, ShapeDetails.Extents.X, SDPG_World);
					}
					break;

				default:
					check(false);
				}
			}
		}
	}
}
// Internal helper function to find all graph nodes that reference the specified object
static int32 FindReferencedGraphNodes(TMultiMap<UObject*, FRefGraphItem*>& InputGraphNodeList, UObject* ReferencedObj, TArray<FRefGraphItem*>& FoundNodes)
{
	InputGraphNodeList.MultiFind(ReferencedObj, FoundNodes);
	return FoundNodes.Num();
}
void FPaperAtlasGenerator::HandleAssetChangedEvent(UPaperSpriteAtlas* Atlas)
{
	Atlas->MaxWidth = FMath::Clamp(Atlas->MaxWidth, 16, 4096);
	Atlas->MaxHeight = FMath::Clamp(Atlas->MaxHeight, 16, 4096);

	// Find all sprites that reference the atlas and force them loaded
	FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));

	TArray<FAssetData> AssetList;
	TMultiMap<FName, FString> TagsAndValues;
	TagsAndValues.Add(TEXT("AtlasGroupGUID"), Atlas->AtlasGUID.ToString(EGuidFormats::Digits));
	AssetRegistryModule.Get().GetAssetsByTagValues(TagsAndValues, AssetList);

	TIndirectArray<FSingleTexturePaperAtlas> AtlasGenerators;

	// Start off with one page
	new (AtlasGenerators) FSingleTexturePaperAtlas(Atlas->MaxWidth, Atlas->MaxHeight);

	for (const FAssetData& SpriteRef : AssetList)
	{
		if (UPaperSprite* Sprite = Cast<UPaperSprite>(SpriteRef.GetAsset()))
		{
			//@TODO: Use the tight bounds instead of the source bounds
			const FVector2D SpriteSizeFloat = Sprite->GetSourceSize();
			const FIntPoint SpriteSize(FMath::TruncToInt(SpriteSizeFloat.X), FMath::TruncToInt(SpriteSizeFloat.Y));

			if (Sprite->GetSourceTexture() == nullptr)
			{
				UE_LOG(LogPaper2DEditor, Error, TEXT("Sprite %s has no source texture and cannot be packed"), *(Sprite->GetPathName()));
				continue;
			}

			//@TODO: Take padding into account (push this into the generator)
			if ((SpriteSize.X > Atlas->MaxWidth) || (SpriteSize.Y >= Atlas->MaxHeight))
			{
				// This sprite cannot ever fit into an atlas page
				UE_LOG(LogPaper2DEditor, Error, TEXT("Sprite %s (%d x %d) can never fit into atlas %s (%d x %d) due to maximum page size restrictions"),
					*(Sprite->GetPathName()),
					SpriteSize.X,
					SpriteSize.Y,
					*(Atlas->GetPathName()),
					Atlas->MaxWidth,
					Atlas->MaxHeight);
			}
			else
			{
				const FAtlasedTextureSlot* Slot = nullptr;

				// Does it fit in any existing pages?
				for (auto& Generator : AtlasGenerators)
				{
					Slot = Generator.AddSprite(Sprite);

					if (Slot != nullptr)
					{
						break;
					}
				}

				if (Slot == nullptr)
				{
					// Doesn't fit in any current pages, make a new one
					FSingleTexturePaperAtlas* NewGenerator = new (AtlasGenerators) FSingleTexturePaperAtlas(Atlas->MaxWidth, Atlas->MaxHeight);
					Slot = NewGenerator->AddSprite(Sprite);
				}

				if (Slot != nullptr)
				{
					UE_LOG(LogPaper2DEditor, Warning, TEXT("Allocated %s to (%d,%d)"), *(Sprite->GetPathName()), Slot->X, Slot->Y);
				}
				else
				{
					// ERROR: Didn't fit into a brand new page, maybe padding was wrong
					UE_LOG(LogPaper2DEditor, Error, TEXT("Failed to allocate %s into a brand new page"), *(Sprite->GetPathName()));
				}
			}
		}
	}

	// Turn the generators back into textures
	Atlas->GeneratedTextures.Empty(AtlasGenerators.Num());
	for (int32 GeneratorIndex = 0; GeneratorIndex < AtlasGenerators.Num(); ++GeneratorIndex)
	{
		FSingleTexturePaperAtlas& AtlasPage = AtlasGenerators[GeneratorIndex];

		UTexture2D* Texture = NewNamedObject<UTexture2D>(Atlas, NAME_None, RF_Public);
		Atlas->GeneratedTextures.Add(Texture);

		AtlasPage.CopyToTexture(Texture);

		// Now update the baked data for all the sprites to point there
		for (auto& SpriteToSlot : AtlasPage.SpriteToSlotMap)
		{
			UPaperSprite* Sprite = SpriteToSlot.Key;
			Sprite->Modify();
			const FAtlasedTextureSlot* Slot = SpriteToSlot.Value;
			Sprite->BakedSourceTexture = Texture;
			Sprite->BakedSourceUV = FVector2D(Slot->X, Slot->Y);
			Sprite->RebuildRenderData();
		}
	}

	//@TODO: Adjust the atlas rebuild code so that it tries to preserve existing sprites (optimize for the 'just added one to the end' case, preventing dirtying lots of assets unnecessarily)
	//@TODO: invoke this code when a sprite has the atlas group property modified
}
void FPropertyTable::UpdateColumns()
{
	if( Orientation == EPropertyTableOrientation::AlignPropertiesInColumns)
	{
		TMultiMap< UProperty*, TSharedRef< IPropertyTableColumn > > ColumnsMap;
		for (int ColumnIdx = 0; ColumnIdx < Columns.Num(); ++ColumnIdx)
		{
			TSharedRef< IDataSource > DataSource = Columns[ColumnIdx]->GetDataSource();
			TSharedPtr< FPropertyPath > PropertyPath = DataSource->AsPropertyPath();
			if( PropertyPath.IsValid() && PropertyPath->GetNumProperties() > 0 )
			{
				ColumnsMap.Add(PropertyPath->GetRootProperty().Property.Get(), Columns[ColumnIdx]);
			}
		}

		Columns.Empty();

		if ( ShowRowHeader )
		{
			TSharedRef< IPropertyTableColumn > NewColumn = MakeShareable( new FPropertyTableRowHeaderColumn( SharedThis( this ) ) );
			Columns.Add( NewColumn );
		}

		if ( ShowObjectName )
		{
			TSharedRef< IPropertyTableColumn > NewColumn = MakeShareable( new FPropertyTableObjectNameColumn( SharedThis( this ) ) );
			NewColumn->SetFrozen( true );
			Columns.Add( NewColumn );
		}

		TArray< TWeakObjectPtr< UStruct > > UniqueTypes;
		TArray< int > TypeCounter;

		for( auto ObjectIter = SourceObjects.CreateConstIterator(); ObjectIter; ++ObjectIter )
		{
			auto Object = *ObjectIter;

			if( !Object.IsValid() )
			{
				continue;
			}

			const TSharedRef< FObjectPropertyNode > RootObjectNode = GetObjectPropertyNode( Object );

			TWeakObjectPtr< UStruct > Type;
			if ( RootPath->GetNumProperties() == 0 )
			{
				Type = RootObjectNode->GetObjectBaseClass();
			}
			else
			{
				const TSharedPtr< FPropertyNode > PropertyNode = FPropertyNode::FindPropertyNodeByPath( RootPath, RootObjectNode );

				if ( !PropertyNode.IsValid() )
				{
					continue;
				}

				const TWeakObjectPtr< UProperty > Property = PropertyNode->GetProperty();

				if ( !Property.IsValid() || !Property->IsA( UStructProperty::StaticClass() ) )
				{
					continue;
				}

				UStructProperty* StructProperty = Cast< UStructProperty >( Property.Get() );
				Type = StructProperty->Struct;
			}

			if ( !Type.IsValid() )
			{
				continue;
			}

			int FoundIndex = -1;
			if ( UniqueTypes.Find( Type, /*OUT*/FoundIndex ) )
			{
				TypeCounter[ FoundIndex ] = TypeCounter[ FoundIndex ] + 1;
				continue;
			}

			UniqueTypes.Add( Type );
			TypeCounter.Add( 1 );
		}

		if ( UniqueTypes.Num() > 0 )
		{
			int HighestCountIndex = 0;
			int HighestCount = 0;
			for (int Index = 0; Index < TypeCounter.Num(); Index++)
			{
				if ( TypeCounter[Index] > HighestCount )
				{
					HighestCountIndex = Index;
					HighestCount = TypeCounter[Index];
				}
			}

			TWeakObjectPtr< UStruct > PrimaryType = UniqueTypes[ HighestCountIndex ];

			for (TFieldIterator<UProperty> PropertyIter( PrimaryType.Get(), EFieldIteratorFlags::IncludeSuper); PropertyIter; ++PropertyIter)
			{
				TWeakObjectPtr< UProperty > Property = *PropertyIter;

				if ( PropertyIter->HasAnyPropertyFlags(CPF_AssetRegistrySearchable) )
				{
					TArray< TSharedRef< IPropertyTableColumn > > MapResults;

					ColumnsMap.MultiFind(Property.Get(), MapResults);
					if(MapResults.Num() > 0)
					{
						for (int MapIdx = 0; MapIdx < MapResults.Num(); ++MapIdx)
						{
							Columns.Add(MapResults[MapIdx]);
						}
					}
					else
					{
						TSharedRef< IPropertyTableColumn > NewColumn = CreateColumn( Property );
						Columns.Add( NewColumn );
					}
				}
			}
		}
	}
	else
	{
		Columns.Empty();

		if( SourceObjects.Num() > 0 )
		{
			UClass* ObjectClass = SourceObjects[0]->GetClass();
			TSharedRef< IPropertyTableColumn > HeadingColumn = MakeShareable( new FPropertyTablePropertyNameColumn( SharedThis( this ) ) );

			Columns.Add( HeadingColumn );

			for( auto ObjectIter = SourceObjects.CreateConstIterator(); ObjectIter; ++ObjectIter )
			{
				auto Object = *ObjectIter;

				if( Object.IsValid() )
				{
					const TSharedRef< FObjectPropertyNode > ObjectNode = GetObjectPropertyNode( Object );
					const TSharedPtr< FPropertyNode > PropertyNode = FPropertyNode::FindPropertyNodeByPath( RootPath, ObjectNode );

					UProperty* Property = PropertyNode->GetProperty();
					if ( Property != NULL && Property->IsA( UArrayProperty::StaticClass() ) )
					{
						for (int ChildIdx = 0; ChildIdx < PropertyNode->GetNumChildNodes(); ChildIdx++)
						{
							TSharedPtr< FPropertyNode > ChildNode = PropertyNode->GetChildNode( ChildIdx );
							FPropertyInfo Extension;
							Extension.Property = ChildNode->GetProperty();
							Extension.ArrayIndex = ChildNode->GetArrayIndex();
							TSharedRef< FPropertyPath > Path = FPropertyPath::CreateEmpty()->ExtendPath( Extension );
							TSharedRef< IPropertyTableColumn > NewColumn = MakeShareable( new FPropertyTableColumn( SharedThis( this ), Object, Path ) );
							Columns.Add( NewColumn );
						}
					}
					else if (  Property != NULL )
					{
						TSharedRef< IPropertyTableColumn > NewColumn = MakeShareable( new FPropertyTableColumn( SharedThis( this ), Object ) );
						Columns.Add( NewColumn );
					}
				}

			}
		}
	}

	ColumnsChanged.Broadcast();
}
	/**
	* Nulls out references to a given object
	*
	* @param InObject - Object to null references to
	*/
	void NullReferencesToObject(UObject* InObject)
	{
		TArray<UObject*> ReplaceableObjects;
		TMap<UObject*, UObject*> ReplacementMap;
		ReplacementMap.Add(InObject, NULL);
		ReplacementMap.GenerateKeyArray(ReplaceableObjects);

		// Find all the properties (and their corresponding objects) that refer to any of the objects to be replaced
		TMap< UObject*, TArray<UProperty*> > ReferencingPropertiesMap;
		for (FObjectIterator ObjIter; ObjIter; ++ObjIter)
		{
			UObject* CurObject = *ObjIter;

			// Find the referencers of the objects to be replaced
			FFindReferencersArchive FindRefsArchive(CurObject, ReplaceableObjects);

			// Inform the object referencing any of the objects to be replaced about the properties that are being forcefully
			// changed, and store both the object doing the referencing as well as the properties that were changed in a map (so that
			// we can correctly call PostEditChange later)
			TMap<UObject*, int32> CurNumReferencesMap;
			TMultiMap<UObject*, UProperty*> CurReferencingPropertiesMMap;
			if (FindRefsArchive.GetReferenceCounts(CurNumReferencesMap, CurReferencingPropertiesMMap) > 0)
			{
				TArray<UProperty*> CurReferencedProperties;
				CurReferencingPropertiesMMap.GenerateValueArray(CurReferencedProperties);
				ReferencingPropertiesMap.Add(CurObject, CurReferencedProperties);
				for (TArray<UProperty*>::TConstIterator RefPropIter(CurReferencedProperties); RefPropIter; ++RefPropIter)
				{
					CurObject->PreEditChange(*RefPropIter);
				}
			}

		}

		// Iterate over the map of referencing objects/changed properties, forcefully replacing the references and then
		// alerting the referencing objects the change has completed via PostEditChange
		int32 NumObjsReplaced = 0;
		for (TMap< UObject*, TArray<UProperty*> >::TConstIterator MapIter(ReferencingPropertiesMap); MapIter; ++MapIter)
		{
			++NumObjsReplaced;

			UObject* CurReplaceObj = MapIter.Key();
			const TArray<UProperty*>& RefPropArray = MapIter.Value();

			FArchiveReplaceObjectRef<UObject> ReplaceAr(CurReplaceObj, ReplacementMap, false, true, false);

			for (TArray<UProperty*>::TConstIterator RefPropIter(RefPropArray); RefPropIter; ++RefPropIter)
			{
				FPropertyChangedEvent PropertyEvent(*RefPropIter);
				CurReplaceObj->PostEditChangeProperty(PropertyEvent);
			}

			if (!CurReplaceObj->HasAnyFlags(RF_Transient) && CurReplaceObj->GetOutermost() != GetTransientPackage())
			{
				if (!CurReplaceObj->RootPackageHasAnyFlags(PKG_CompiledIn))
				{
					CurReplaceObj->MarkPackageDirty();
				}
			}
		}
	}
void FReferenceChainSearch::BuildRefGraph()
{
	UE_LOG(LogReferenceChain, Log, TEXT("Generating reference graph ..."));

	bool bContinue = true;

	TMultiMap<UObject*, FRefGraphItem*> GraphNodes;

	// Create the first graph-nodes referencing the target object
	for (FRawObjectIterator It;It;++It)
	{
		UObject* Obj = *It;

		TArray<FReferenceChainLink>& RefList = ReferenceMap.FindChecked(Obj);

		for (int32 i=0; i < RefList.Num(); ++i)
		{
			if (RefList[i].ReferencedObj == ObjectToFind)
			{
				FRefGraphItem* Node = new FRefGraphItem();
				Node->Link = RefList[i];
				GraphNodes.Add(Node->Link.ReferencedBy, Node);

				RefList[i].ReferenceType = EReferenceType::Invalid;
			}
		}
	}

	int32 Level = 0;
	UE_LOG(LogReferenceChain, Log, TEXT("Level 0 has %d nodes ..."), GraphNodes.Num());

	while(bContinue)
	{
		int32 AddedNodes = 0;

		TArray<FRefGraphItem*> NewGraphNodes;


		for (FRawObjectIterator It;It;++It)
		{
			UObject* Obj = *It;

			TArray<FReferenceChainLink>& RefList = ReferenceMap.FindChecked(Obj);

			for (int32 i=0; i < RefList.Num(); ++i)
			{
				if (RefList[i].ReferenceType == EReferenceType::Invalid ||
					RefList[i].ReferencedObj->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS | RF_RootSet)) // references to rooted objects are not important
				{ 
					continue; 
				}

				TArray<FRefGraphItem*> RefNodes;

				if (FindReferencedGraphNodes(GraphNodes, RefList[i].ReferencedObj, RefNodes) > 0)
				{
					FRefGraphItem* Node = FindNode(GraphNodes, RefList[i].ReferencedBy, RefList[i].ReferencedObj);
					if (Node == NULL)
					{
						Node = new FRefGraphItem();
						Node->Link = RefList[i];

						NewGraphNodes.Push(Node);
					}
				
					for (int32 j=0; j < RefNodes.Num(); ++j)
					{
						Node->Children.Push(RefNodes[j]);
						RefNodes[j]->Parents.Push(Node);
					}

					++AddedNodes;
					
					RefList[i].ReferenceType = EReferenceType::Invalid;
				}
			}
		}
		Level++;
		UE_LOG(LogReferenceChain, Log, TEXT("Level %d added %d nodes ..."), Level, NewGraphNodes.Num());

		for (int32 i = 0; i < NewGraphNodes.Num(); ++i)
		{
			GraphNodes.Add(NewGraphNodes[i]->Link.ReferencedBy, NewGraphNodes[i]);
		}


		NewGraphNodes.Empty(NewGraphNodes.Num());

		bContinue = AddedNodes > 0;
	}

	TArray<FReferenceChain> Chains;

	UE_LOG(LogReferenceChain, Log, TEXT("Generating reference chains ..."));
	for (auto It = GraphNodes.CreateConstIterator(); It; ++It)
	{
		FRefGraphItem* Node = It.Value();

		if (Node->Link.ReferencedBy->HasAnyFlags(GARBAGE_COLLECTION_KEEPFLAGS | RF_RootSet))
		{
			FReferenceChain CurChain;
			CreateReferenceChain(Node, CurChain, Chains, ObjectToFind, Level);
		}
	}

	for (int32 i=0; i < Chains.Num(); ++i)
	{
		InsertReferenceChain(Chains[i]);
	}
}