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; }
//------------------------------------------------------------------------------ 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 ); } }
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; }
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 ); } }
//------------------------------------------------------------------------------ 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); } }
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); } }
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; }
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]); } }