// Copyright Epic Games, Inc. All Rights Reserved. /*================================================================================ LightmapResRatioAdjust.cpp: Lightmap Resolution Ratio Adjustment helper ================================================================================*/ #include "LightmapResRatioAdjust.h" #include "UObject/Object.h" #include "UObject/UObjectIterator.h" #include "GameFramework/Actor.h" #include "Components/StaticMeshComponent.h" #include "Engine/Brush.h" #include "Editor/UnrealEdEngine.h" #include "UnrealEdGlobals.h" #include "EditorSupportDelegates.h" #include "SurfaceIterators.h" #include "Logging/MessageLog.h" #include "ActorEditorUtils.h" #include "Engine/StaticMesh.h" #include "Engine/Polys.h" #include "Engine/LevelStreaming.h" #define LOCTEXT_NAMESPACE "LightmapResRatioAdjustSettings" /** * LightmapResRatioAdjust settings */ /** Static: Global lightmap resolution ratio adjust settings */ FLightmapResRatioAdjustSettings FLightmapResRatioAdjustSettings::LightmapResRatioAdjustSettings; bool FLightmapResRatioAdjustSettings::ApplyRatioAdjustment() { FLightmapResRatioAdjustSettings& Settings = Get(); FMessageLog EditorErrors("EditorErrors"); if ((Settings.bStaticMeshes == false) && (Settings.bBSPSurfaces == false)) { // No primitive type is selected... EditorErrors.Warning(LOCTEXT("LMRatioAdjust_NoPrimitivesSelected", "No primitive type selected.")); EditorErrors.Notify(); return false; } bool bRebuildGeometry = false; bool bRefreshViewport = false; // Collect the levels to process //@todo. This code is done in a few places for lighting related changes... // It should be moved to a centralized place to remove duplication. TArray Worlds; TArray StaticMeshComponents; TArray MeshLevels; TArray BrushLevels; // Gather the relevant levels, static meshes and BSP components. for (TObjectIterator ObjIt; ObjIt; ++ObjIt) { UStaticMeshComponent* StaticMeshComp = Cast(*ObjIt); if (StaticMeshComp && (Settings.bStaticMeshes == true)) { bool AddThisItem = false; if (Settings.bSelectedObjectsOnly == true) { AActor* OwnerActor = StaticMeshComp->GetOwner(); if (OwnerActor && OwnerActor->IsSelected() == true) { AddThisItem = true; } } else { AddThisItem = true; } if( AddThisItem ) { StaticMeshComponents.AddUnique(StaticMeshComp); // Store the required levels relating to this object based on the level option settings UWorld* MeshWorld = StaticMeshComp->GetWorld(); // Exclude editor preview worlds if( MeshWorld && (MeshWorld->WorldType != EWorldType::EditorPreview && MeshWorld->WorldType != EWorldType::Inactive) ) { AddRequiredLevels( Settings.LevelOptions, MeshWorld , MeshLevels ); Worlds.AddUnique( MeshWorld ); } } } // Check if this object is a brush or has any brushes attached AActor* Actor = Cast(*ObjIt); if ( Actor && Settings.bBSPSurfaces == true) { ABrush* Brush = Cast< ABrush >( Actor ); if (Brush && !FActorEditorUtils::IsABuilderBrush(Brush)) { UWorld* BrushWorld = Brush->GetWorld(); // Exclude editor preview worlds if( BrushWorld->WorldType != EWorldType::EditorPreview && BrushWorld->WorldType != EWorldType::Inactive ) { AddRequiredLevels( Settings.LevelOptions, BrushWorld, BrushLevels ); Worlds.AddUnique( BrushWorld ); } } else { // Search for any brushes attached to an actor TArray AttachedActors; Actor->GetAttachedActors( AttachedActors ); const bool bExactClass = true; TArray AttachedBrushes; // Get any brush actors attached to this one if( ContainsObjectOfClass( AttachedActors, ABrush::StaticClass(), bExactClass, &AttachedBrushes ) ) { for( int32 BrushIndex = 0; BrushIndex < AttachedBrushes.Num(); ++BrushIndex ) { ABrush* AttachedBrush = CastChecked( AttachedBrushes[BrushIndex] ); UWorld* BrushWorld = AttachedBrush->GetWorld(); // Exclude editor preview worlds if( BrushWorld->WorldType != EWorldType::EditorPreview && BrushWorld->WorldType != EWorldType::Inactive ) { AddRequiredLevels( Settings.LevelOptions, BrushWorld, BrushLevels ); Worlds.AddUnique( BrushWorld ); } } } } } } if ( (MeshLevels.Num() == 0) && ( BrushLevels.Num() == 0 ) ) { // No levels are selected... EditorErrors.Warning( LOCTEXT("LMRatioAdjust_NoLevelsToProcess", "No levels to process.") ); EditorErrors.Notify(); return false; } // There should only be one world - exit if there isnt ! if ( Worlds.Num() != 1 ) { // No levels are selected... EditorErrors.Warning( LOCTEXT("LMRatioAdjust_TooManyWorldsToProcess", "you can only process one world at a time.") ); EditorErrors.Notify(); return false; } UWorld* World = Worlds[0]; // Attempt to apply the modification to the static meshes in the relevant levels for (int32 LevelIdx = 0; LevelIdx < MeshLevels.Num(); LevelIdx++) { ULevel* EachLevel = MeshLevels[ LevelIdx ]; for (int32 StaticMeshIdx = 0; StaticMeshIdx < StaticMeshComponents.Num(); StaticMeshIdx++) { UStaticMeshComponent* SMComp = StaticMeshComponents[StaticMeshIdx]; if (SMComp && SMComp->IsIn(EachLevel) == true) { // Process it! int32 CurrentResolution = SMComp->GetStaticLightMapResolution(); bool bConvertIt = true; if (((SMComp->bOverrideLightMapRes == true) && (CurrentResolution == 0)) || ((SMComp->bOverrideLightMapRes == false) && (SMComp->GetStaticMesh() != nullptr) && (SMComp->GetStaticMesh()->GetLightMapResolution() == 0))) { // Don't convert vertex mapped objects! bConvertIt = false; } else if ((Settings.Ratio >= 1.0f) && (CurrentResolution >= Settings.Max_StaticMeshes)) { bConvertIt = false; } else if ((Settings.Ratio < 1.0f) && (CurrentResolution <= Settings.Min_StaticMeshes)) { bConvertIt = false; } else if( SMComp->GetWorld() && (SMComp->GetWorld()->WorldType == EWorldType::EditorPreview || SMComp->GetWorld()->WorldType == EWorldType::Inactive) ) { // Don't do objects with an editor preview or inactive world bConvertIt = false; } if (bConvertIt) { SMComp->Modify(); float AdjustedResolution = (float)CurrentResolution * (Settings.Ratio); int32 NewResolution = FMath::TruncToInt(AdjustedResolution); NewResolution = FMath::Max(NewResolution + 3 & ~3,4); SMComp->SetStaticLightingMapping(true, NewResolution); SMComp->InvalidateLightingCache(); SMComp->MarkRenderStateDirty(); bRefreshViewport = true; } } } } // Try to update all surfaces in this level... if (Settings.bBSPSurfaces == true) { ULevel* OrigCurrentLevel = World->GetCurrentLevel(); for (int32 LevelIdx = 0; LevelIdx < BrushLevels.Num(); LevelIdx++) { ULevel* EachLevel = BrushLevels[ LevelIdx ]; World->SetCurrentLevel( EachLevel ); for (TSurfaceIterator It(World); It; ++It) { UModel* Model = It.GetModel(); bool bConvertIt = true; const int32 SurfaceIndex = It.GetSurfaceIndex(); FBspSurf& Surf = Model->Surfs[SurfaceIndex]; if (Settings.bSelectedObjectsOnly == true) { if ((Surf.PolyFlags & PF_Selected) == 0) { bConvertIt = false; } } if (bConvertIt == true) { // Process it! int32 CurrentResolution = FMath::TruncToInt32(Surf.LightMapScale); float Scalar = 1.0f / Settings.Ratio; if ((Scalar < 1.0f) && (CurrentResolution <= Settings.Min_BSPSurfaces)) { bConvertIt = false; } else if ((Scalar >= 1.0f) && (CurrentResolution >= Settings.Max_BSPSurfaces)) { bConvertIt = false; } if (bConvertIt == true) { Model->ModifySurf( SurfaceIndex, true ); int32 NewResolution = FMath::TruncToInt(Surf.LightMapScale * Scalar); NewResolution = FMath::Max(NewResolution + 3 & ~3,4); Surf.LightMapScale = (float)NewResolution; if (Surf.Actor != NULL) { Surf.Actor->Brush->Polys->Element[Surf.iBrushPoly].LightMapScale = Surf.LightMapScale; } bRefreshViewport = true; bRebuildGeometry = true; } } } } World->SetCurrentLevel( OrigCurrentLevel ); } if (bRebuildGeometry == true) { GUnrealEd->Exec( World, TEXT("MAP REBUILD") ); } if (bRefreshViewport == true) { FEditorSupportDelegates::RedrawAllViewports.Broadcast(); FEditorSupportDelegates::RefreshPropertyWindows.Broadcast(); } return true; } void FLightmapResRatioAdjustSettings::AddRequiredLevels( AdjustLevels InLevelOptions, UWorld* InWorld, TArray& OutLevels ) { switch (InLevelOptions) { case AdjustLevels::Current: { check(InWorld); OutLevels.AddUnique(InWorld->GetCurrentLevel()); } break; case AdjustLevels::Selected: { TArray>& SelectedLevels = InWorld->GetSelectedLevels(); for(auto It = SelectedLevels.CreateIterator(); It; ++It) { ULevel* Level = *It; if (Level) { OutLevels.AddUnique(Level); } } if (OutLevels.Num() == 0) { // Fall to the current level... check(InWorld); OutLevels.AddUnique(InWorld->GetCurrentLevel()); } } break; case AdjustLevels::AllLoaded: { if (InWorld != NULL) { // Add main level. OutLevels.AddUnique(InWorld->PersistentLevel); // Add secondary levels. for (ULevelStreaming* StreamingLevel : InWorld->GetStreamingLevels()) { if (StreamingLevel) { if (ULevel* Level = StreamingLevel->GetLoadedLevel()) { OutLevels.AddUnique(Level); } } } } } break; } } #undef LOCTEXT_NAMESPACE