Files
UnrealEngine/Engine/Source/Editor/LevelEditor/Private/LightmapResRatioAdjust.cpp
2025-05-18 13:04:45 +08:00

317 lines
9.6 KiB
C++

// 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<UWorld*> Worlds;
TArray<UStaticMeshComponent*> StaticMeshComponents;
TArray<ULevel*> MeshLevels;
TArray<ULevel*> BrushLevels;
// Gather the relevant levels, static meshes and BSP components.
for (TObjectIterator<UObject> ObjIt; ObjIt; ++ObjIt)
{
UStaticMeshComponent* StaticMeshComp = Cast<UStaticMeshComponent>(*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<AActor>(*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<AActor*> AttachedActors;
Actor->GetAttachedActors( AttachedActors );
const bool bExactClass = true;
TArray<AActor*> 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<ABrush>( 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<FCurrentLevelSurfaceLevelFilter> 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<ULevel*>& OutLevels )
{
switch (InLevelOptions)
{
case AdjustLevels::Current:
{
check(InWorld);
OutLevels.AddUnique(InWorld->GetCurrentLevel());
}
break;
case AdjustLevels::Selected:
{
TArray<TObjectPtr<class ULevel>>& 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