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

1021 lines
28 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SSurfaceProperties.h"
#include "Widgets/Text/STextBlock.h"
#include "Misc/ConfigCacheIni.h"
#include "Modules/ModuleManager.h"
#include "SlateOptMacros.h"
#include "Widgets/Images/SImage.h"
#include "Widgets/Layout/SUniformGridPanel.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Input/SComboButton.h"
#include "Widgets/Views/SListView.h"
#include "Widgets/Input/SCheckBox.h"
#include "Styling/AppStyle.h"
#include "Editor/UnrealEdEngine.h"
#include "Lightmass/LightmassPrimitiveSettingsObject.h"
#include "UnrealEdGlobals.h"
#include "PropertyEditorModule.h"
#include "IDetailsView.h"
#include "SurfaceIterators.h"
#include "Widgets/Input/SNumericEntryBox.h"
#include "Widgets/Input/SHyperlink.h"
#include "Engine/Polys.h"
#define LOCTEXT_NAMESPACE "SSurfaceProperties"
void SSurfaceProperties::Construct( const FArguments& InArgs )
{
bUseNegativePanningU = false;
bUseNegativePanningV = false;
bUseNegativeRotation = false;
CachedScalingValueU = 1.0f;
CachedScalingValueV = 1.0f;
// Initialize scale fields according to the scale of the first selected surface
if (TSelectedSurfaceIterator<> It(GetWorld()); It)
{
FBspSurf* Surf = *It;
UModel* Model = It.GetModel();
const FVector3f TextureU(Model->Vectors[Surf->vTextureU]);
const FVector3f TextureV(Model->Vectors[Surf->vTextureV]);
const float TextureUSize = TextureU.Size();
const float TextureVSize = TextureV.Size();
if (!FMath::IsNearlyZero(TextureUSize))
{
CachedScalingValueU = 1.0f / TextureUSize;
}
if (!FMath::IsNearlyZero(TextureVSize))
{
CachedScalingValueV = 1.0f / TextureVSize;
}
}
bPreserveScaleRatio = false;
bUseRelativeScaling = false;
GConfig->GetBool(TEXT("SelectionDetails"), TEXT("PreserveScaleRatio"), bPreserveScaleRatio, GEditorPerProjectIni);
GConfig->GetBool(TEXT("SelectionDetails"), TEXT("UseRelativeScaling"), bUseRelativeScaling, GEditorPerProjectIni);
static const float ScalingValues[] = { 1.0f / 16, 1.0f / 8, 1.0f / 4, 1.0f / 2, 1, 2, 4, 8, 16 };
for(int Idx = 0; Idx < UE_ARRAY_COUNT(ScalingValues); Idx++)
{
ScalingFactors.Add(MakeShareable(new FString(FString::Printf(TEXT("%f"),ScalingValues[Idx]))));
}
static const FSlateBrush* BorderStyle = FAppStyle::GetBrush( "DetailsView.GroupSection" );
static const FLinearColor BorderColor = FLinearColor(.2f,.2f,.2f,.2f);
ChildSlot
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
.Padding(0.f,0.f,0.f,5.f)
[
SNew(SBorder)
.BorderBackgroundColor( BorderColor )
.BorderImage( BorderStyle )
.Padding(10.f)
.AddMetaData<FTagMetaData>(TEXT("DetailsView.TexturePan"))
[
ConstructTexturePan()
]
]
+SVerticalBox::Slot()
.AutoHeight()
.Padding(0.f,0.f,0.f,5.f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(3)
.Padding(0.f,0.f,5.f,0.f)
[
SNew(SBorder)
.BorderBackgroundColor( BorderColor )
.BorderImage( BorderStyle )
.Padding(10.f)
.AddMetaData<FTagMetaData>(TEXT("DetailsView.TextureRotate"))
[
ConstructTextureRotate()
]
]
+ SHorizontalBox::Slot()
.FillWidth(2.f)
[
SNew(SBorder)
.BorderBackgroundColor( BorderColor )
.BorderImage( BorderStyle )
.Padding(10.f)
.AddMetaData<FTagMetaData>(TEXT("DetailsView.TextureFlip"))
[
ConstructTextureFlip()
]
]
]
+SVerticalBox::Slot()
.AutoHeight()
.Padding(0.f,0.f,0.f,5.f)
[
SNew(SBorder)
.BorderBackgroundColor( BorderColor )
.BorderImage( BorderStyle )
.Padding(10.f)
.AddMetaData<FTagMetaData>(TEXT("DetailsView.TextureScale"))
[
ConstructTextureScale()
]
]
+SVerticalBox::Slot()
.AutoHeight()
.Padding(0.f,0.f,0.f,5.f)
[
SNew(SBorder)
.BorderBackgroundColor( BorderColor )
.BorderImage( BorderStyle )
.Padding(10.f)
.AddMetaData<FTagMetaData>(TEXT("DetailsView.ConstructLighting"))
[
ConstructLighting()
]
]
];
}
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
TSharedRef<SWidget> SSurfaceProperties::ConstructTexturePan()
{
TSharedRef<SVerticalBox> ParentBox = SNew(SVerticalBox);
ParentBox->AddSlot()
.AutoHeight()
.Padding(0.f,0.f,0.f,5.f)
[
SNew(STextBlock)
.Text( LOCTEXT("Pan", "Pan:") )
];
TSharedPtr<SHorizontalBox> HorizontalBox;
ParentBox->AddSlot()
.AutoHeight()
[
SAssignNew(HorizontalBox,SHorizontalBox)
];
TSharedPtr<SVerticalBox> VerticalBox;
HorizontalBox->AddSlot()
.AutoWidth()
[
SAssignNew(VerticalBox,SVerticalBox)
];
static const TextureCoordChannel Channels[] = {UChannel,VChannel};
for (int i = 0; i < 2; i++)
{
VerticalBox->AddSlot()
.FillHeight(1)
.VAlign(VAlign_Center)
.HAlign(HAlign_Fill)
.Padding(5.f)
[
SNew( SCheckBox )
.IsChecked( this, &SSurfaceProperties::IsUsingNegativePanning, Channels[i] )
.OnCheckStateChanged( this, &SSurfaceProperties::OnTogglePanningDirection, Channels[i])
.Style( FAppStyle::Get(), "TransparentCheckBox" )
.ToolTipText( LOCTEXT("InvertPanningDirection", "Toggle panning direction.") )
[
SNew( SImage )
.Image( this, &SSurfaceProperties::GetTogglePanDirectionImage, Channels[i] )
.ColorAndOpacity( FSlateColor::UseForeground() )
]
];
}
TSharedPtr<SUniformGridPanel> GridBox;
HorizontalBox->AddSlot()
.FillWidth(1.f)
[
SAssignNew(GridBox,SUniformGridPanel)
];
const static FText ButtonFields[] = {FText::FromString("1/256"), FText::FromString("1/64"), FText::FromString("1/16"), FText::FromString("1/4")};
const static int32 ButtonIncriments[] = {1,4,16,64};
for (int32 i = 0; i < UE_ARRAY_COUNT(ButtonFields); i++)
{
GridBox->AddSlot(i, 0)
.VAlign(VAlign_Fill)
.HAlign(HAlign_Fill)
[
SNew(SButton)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.Text(ButtonFields[i])
.ToolTipText(LOCTEXT("PanUTooltip", "Pans U texture coordinate"))
.OnClicked(this,&SSurfaceProperties::OnPanTexture,ButtonIncriments[i],SSurfaceProperties::UChannel)
];
GridBox->AddSlot(i, 1)
.VAlign(VAlign_Fill)
.HAlign(HAlign_Fill)
[
SNew(SButton)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.Text(ButtonFields[i])
.ToolTipText(LOCTEXT("PanVTooltip", "Pans V texture coordinate"))
.OnClicked(this,&SSurfaceProperties::OnPanTexture,ButtonIncriments[i],SSurfaceProperties::VChannel)
];
}
// Create the last two custom buttons on the end (there will always be two)
for (int i = 0; i < 2; i++)
{
TSharedPtr<SComboButton> ComboButton;
TSharedPtr<SWidget> NumberBox;
GridBox->AddSlot(UE_ARRAY_COUNT(ButtonFields),i)
.VAlign(VAlign_Fill)
.HAlign(HAlign_Fill)
[
SAssignNew(ComboButton,SComboButton)
.VAlign(VAlign_Fill)
.ButtonContent()
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
.HAlign(HAlign_Center)
[
SNew(STextBlock)
.Text(LOCTEXT("PanToolCustomPan", "---"))
.ToolTipText(LOCTEXT("PanToolCustomPanToolTip", "Set Custom pan amount"))
]
]
.MenuContent()
[
SNew(SBorder)
.BorderImage( FAppStyle::GetBrush( "ToolPanel.GroupBorder" ) )
[
SAssignNew(NumberBox, SNumericEntryBox<int32>)
.OnValueCommitted( this, &SSurfaceProperties::OnCustomPanValueCommitted,(TextureCoordChannel)i)
]
]
];
ComboButton->SetMenuContentWidgetToFocus(NumberBox);
CustomPanButtoms.Add(TWeakPtr<SComboButton>(ComboButton));
}
return ParentBox;
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
TSharedRef<SWidget> SSurfaceProperties::ConstructTextureRotate()
{
TSharedRef<SVerticalBox> Parent = SNew(SVerticalBox);
Parent->AddSlot()
.AutoHeight()
.Padding(0.f,0.f,5.f,5.f)
[
SNew (STextBlock)
.Text(LOCTEXT("RotateTitle", "Rotate:"))
];
TSharedPtr<SHorizontalBox> RotateBox;
Parent->AddSlot()
.AutoHeight()
[
SAssignNew(RotateBox,SHorizontalBox)
];
RotateBox->AddSlot()
.AutoWidth()
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.Padding(5.f)
[
SNew( SCheckBox )
.IsChecked( this, &SSurfaceProperties::IsUsingNegativeRotation )
.OnCheckStateChanged( this, &SSurfaceProperties::OnToggleRotationDirection )
.Style( FAppStyle::Get(), "TransparentCheckBox" )
.ToolTipText( LOCTEXT("InvertRotation", "Toggle Rotation direction.") )
[
SNew( SImage )
.Image( this, &SSurfaceProperties::GetToggleRotationDirectionImage )
.ColorAndOpacity( FSlateColor::UseForeground() )
]
];
// Rotation button fields:
const static FText ButtonFields[] = { LOCTEXT("Rotate45Degrees", "45"), LOCTEXT("Rotate90Degrees", "90"), LOCTEXT("RotateCustom", "---") };
const static int32 RotationValues[] = {45, 90, -1};
const static RotationAction RotationActions[] = {Rotate,Rotate,RotateCustom};
// create rotation controls
for (int Idx = 0; Idx < UE_ARRAY_COUNT(RotationValues); Idx++)
{
if (RotationActions[Idx] == RotateCustom)
{
TSharedPtr<SComboButton> CurrentButton;
TSharedPtr<SWidget> NumberBox;
RotateBox->AddSlot()
.FillWidth(1.f)
.VAlign(VAlign_Fill)
.HAlign(HAlign_Fill)
[
SAssignNew(CurrentButton,SComboButton)
.VAlign(VAlign_Center)
.ButtonContent()
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
.HAlign(HAlign_Center)
[
SNew(STextBlock)
.Text(ButtonFields[Idx])
.ToolTipText(LOCTEXT("RotateToolTip_Custom", "Sets a custom rotate amount"))
]
]
.MenuContent()
[
SNew(SBorder)
.BorderImage( FAppStyle::GetBrush( "ToolPanel.GroupBorder" ) )
[
SAssignNew(NumberBox, SNumericEntryBox<int32>)
.OnValueCommitted( this, &SSurfaceProperties::OnCustomRotateValueCommitted)
]
]
];
CurrentButton->SetMenuContentWidgetToFocus(NumberBox);
CustomRotationButton = TWeakPtr<SComboButton>(CurrentButton);
}
else
{
RotateBox->AddSlot()
.FillWidth(1.f)
.VAlign(VAlign_Fill)
.HAlign(HAlign_Fill)
[
SNew(SButton)
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
.Text(ButtonFields[Idx])
.ToolTipText(LOCTEXT("RotateToolTip", "Rotate texture"))
.OnClicked(this,&SSurfaceProperties::OnRotateTexture,RotationValues[Idx],RotationActions[Idx])
];
}
}
return Parent;
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
TSharedRef<SWidget> SSurfaceProperties::ConstructTextureFlip()
{
TSharedPtr<SHorizontalBox> FlipBox;
TSharedRef<SVerticalBox> Parent = SNew(SVerticalBox);
Parent->AddSlot()
.AutoHeight()
.Padding(0.f,0.f,5.f,5.f)
[
SNew (STextBlock)
.Text(LOCTEXT("FlipTitle", "Flip:"))
];
Parent->AddSlot()
.AutoHeight()
[
SAssignNew(FlipBox,SHorizontalBox)
];
// Flip button fields:
const static FText ButtonFields[] = {LOCTEXT("RotateFlipU", "Flip U"), LOCTEXT("RotateFlipV", "Flip V")};
const static TextureCoordChannel TextureCoordinateChannels[] = {UChannel,VChannel};
// create flip controls
for (int Idx = 0; Idx < UE_ARRAY_COUNT(ButtonFields); Idx++)
{
FlipBox->AddSlot()
.FillWidth(1)
.VAlign(VAlign_Fill)
.HAlign(HAlign_Fill)
[
SNew(SButton)
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
.ContentPadding(FMargin(0.f, 5.f))
.Text(ButtonFields[Idx])
.ToolTipText(LOCTEXT("FlipToolTip", "Flip texture"))
.OnClicked(this,&SSurfaceProperties::OnFlipTexture,TextureCoordinateChannels[Idx])
];
}
return Parent;
}
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
TSharedRef<SWidget> SSurfaceProperties::ConstructTextureScale()
{
TSharedRef<SVerticalBox> Parent = SNew(SVerticalBox);
Parent->AddSlot()
.AutoHeight()
.Padding(0.f,0.f,0.f,5.f)
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SHyperlink)
.Text(this, &SSurfaceProperties::GetScalingLabel)
.ToolTipText(LOCTEXT("ScalingRelativeToggle", "Toggle between Absolute and Relative scaling"))
.OnNavigate( this, &SSurfaceProperties::OnScaleLabelClicked )
.TextStyle(FAppStyle::Get(), "DetailsView.HyperlinkStyle")
]
];
TSharedPtr<SHorizontalBox> Controls;
Parent->AddSlot()
.AutoHeight()
.HAlign(HAlign_Fill)
[
SAssignNew(Controls,SHorizontalBox)
];
TSharedPtr<SComboButton> NewComboButton;
TSharedPtr<SListView<TSharedPtr<FString>>> NewListView;
FString ControlLabels[] = {"U", "V"};
TextureCoordChannel Channels[] = {UChannel,VChannel};
for(int idx = 0; idx < UE_ARRAY_COUNT(ControlLabels); idx++)
{
Controls->AddSlot()
.HAlign(HAlign_Fill)
.FillWidth(1.f)
[
SAssignNew(NewComboButton, SComboButton)
.ContentPadding(0.f)
.HAlign(HAlign_Fill)
.ButtonContent()
[
SNew(SNumericEntryBox<float>)
.OnValueCommitted( this,&SSurfaceProperties::OnScaleValueCommitted,Channels[idx])
.Value( this, &SSurfaceProperties::OnGetScalingValue,Channels[idx] )
.LabelVAlign(VAlign_Center)
.Label()
[
SNew(STextBlock)
.Text(FText::FromString(ControlLabels[idx]))
]
]
.MenuContent()
[
SAssignNew(NewListView, SListView<TSharedPtr<FString>>)
.ListItemsSource(&ScalingFactors)
.OnGenerateRow(this, &SSurfaceProperties::OnGenerateScaleTableRow)
.OnSelectionChanged(this, &SSurfaceProperties::OnScaleSelectionChanged,Channels[idx])
]
];
ScalingComboButton.Add(NewComboButton);
ScalingListViews.Add(NewListView);
}
Controls->AddSlot()
.AutoWidth()
.VAlign(VAlign_Center)
.HAlign(HAlign_Left)
[
SNew( SCheckBox )
.IsChecked( this, &SSurfaceProperties::IsPreserveScaleRatioChecked )
.OnCheckStateChanged( this, &SSurfaceProperties::OnPreserveScaleRatioToggled )
.Style( FAppStyle::Get(), "TransparentCheckBox" )
.ToolTipText( LOCTEXT("PreserveScaleSurfaceToolTip", "When locked changes to either scaling value will be applied to the other.") )
[
SNew( SImage )
.Image( this, &SSurfaceProperties::GetPreserveScaleRatioImage )
.ColorAndOpacity( FSlateColor::UseForeground() )
]
];
Controls->AddSlot()
.FillWidth(1.f);
Controls->AddSlot()
.AutoWidth()
.HAlign(HAlign_Right)
[
SNew(SButton)
.Text(LOCTEXT("ApplyScaling", "Apply"))
.ToolTipText(LOCTEXT("ApplyScalingToolTip", "Apply scaling to selected surfaces"))
.OnClicked(this, &SSurfaceProperties::OnApplyScaling)
];
return Parent;
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
TSharedRef<SWidget> SSurfaceProperties::ConstructLighting()
{
LevelLightmassSettingsObjects.Empty();
SelectedLightmassSettingsObjects.Empty();
if ( GetWorld() )
{
for ( int32 LevelIndex = 0 ; LevelIndex < GetWorld()->GetNumLevels() ; ++LevelIndex )
{
const ULevel* Level = GetWorld()->GetLevel(LevelIndex);
const UModel* Model = Level->Model;
TLightmassSettingsObjectArray ObjArray;
for(int32 SurfaceIndex = 0; SurfaceIndex < Model->Surfs.Num();SurfaceIndex++)
{
const FBspSurf& Surf = Model->Surfs[SurfaceIndex];
if(Surf.PolyFlags & PF_Selected)
{
FLightmassPrimitiveSettings TempSettings = Model->LightmassSettings[Surf.iLightmassIndex];
int32 FoundIndex = INDEX_NONE;
for (int32 CheckIndex = 0; CheckIndex < ObjArray.Num(); CheckIndex++)
{
if (ObjArray[CheckIndex]->LightmassSettings == TempSettings)
{
FoundIndex = CheckIndex;
break;
}
}
if (FoundIndex == INDEX_NONE)
{
ULightmassPrimitiveSettingsObject* LightmassSettingsObject = NewObject<ULightmassPrimitiveSettingsObject>();
LightmassSettingsObject->LightmassSettings = TempSettings;
ObjArray.Add(LightmassSettingsObject);
SelectedLightmassSettingsObjects.Add(LightmassSettingsObject);
}
}
}
LevelLightmassSettingsObjects.Add(ObjArray);
}
}
// setup UI
TSharedRef<SVerticalBox> Parent = SNew(SVerticalBox);
Parent->AddSlot()
.AutoHeight()
.Padding(0.f,0.f,0.f,5.f)
[
SNew(STextBlock)
.Text(LOCTEXT("LightingTitle", "Lighting:"))
];
Parent->AddSlot()
.AutoHeight()
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(0.f,0.f,10.f,5.f)
[
SNew(STextBlock)
.Text(LOCTEXT("LightingLightMapResolution", "Lightmap Resolution:"))
]
+SHorizontalBox::Slot()
.HAlign(HAlign_Fill)
.FillWidth(1.f)
.Padding(0.f,0.f,0.f,5.f)
[
SNew(SNumericEntryBox<float>)
.OnValueCommitted(this, &SSurfaceProperties::OnLightmapResolutionCommitted)
.UndeterminedString( LOCTEXT("MultipleValues", "Multiple Values") )
.Value(this, &SSurfaceProperties::GetLightmapResolutionValue)
]
+SHorizontalBox::Slot()
.HAlign(HAlign_Fill)
.FillWidth(1.f)
];
FDetailsViewArgs Args;
Args.bHideSelectionTip = true;
Args.bAllowSearch = false;
Args.NotifyHook = this;
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyView = PropertyModule.CreateDetailView(Args);
PropertyView->SetObjects( ObjectPtrDecay(SelectedLightmassSettingsObjects) );
Parent->AddSlot()
.AutoHeight()
[
PropertyView.ToSharedRef()
];
return Parent;
}
FReply SSurfaceProperties::OnPanTexture(int32 PanAmount, TextureCoordChannel Channel)
{
int32 PanV = 0;
int32 PanU = 0;
bool InvertPanDirection = false;
if (Channel == UChannel)
{
PanU = PanAmount;
InvertPanDirection = bUseNegativePanningU;
}
else
{
PanV = PanAmount;
InvertPanDirection = bUseNegativePanningV;
}
const float Mod = InvertPanDirection? -1.f : 1.f;
GUnrealEd->Exec( GetWorld(), *FString::Printf( TEXT("POLY TEXPAN U=%f V=%f"), static_cast<float>(PanU) * Mod, static_cast<float>(PanV) * Mod ) );
return FReply::Handled();
}
FReply SSurfaceProperties::OnRotateTexture(int32 RotationAmount, RotationAction Action)
{
const float Mod = bUseNegativeRotation ? -1.f : 1.f;
const float RotateRadians = static_cast<float>(RotationAmount) / 180.0f * PI;
const float UU = cosf(RotateRadians);
const float VV = UU;
const float UV = -sinf(RotateRadians) * Mod;
const float VU = sinf(RotateRadians) * Mod;
GUnrealEd->Exec( GetWorld(), *FString::Printf( TEXT("POLY TEXMULT UU=%f VV=%f UV=%f VU=%f"), UU, VV, UV, VU ) );
return FReply::Handled();
}
FReply SSurfaceProperties::OnFlipTexture(TextureCoordChannel Channel)
{
if (Channel == UChannel)
{
GUnrealEd->Exec( GetWorld(), TEXT("POLY TEXMULT UU=-1 VV=1") );
}
else
{
GUnrealEd->Exec( GetWorld(), TEXT("POLY TEXMULT UU=1 VV=-1") );
}
return FReply::Handled();
}
void SSurfaceProperties::OnScaleTexture(float InScaleU, float InScaleV, bool InRelative)
{
if( InScaleU == 0.f )
{
InScaleU = 1.f;
}
if( InScaleV == 0.f )
{
InScaleV = 1.f;
}
InScaleU = 1.0f / InScaleU;
InScaleV = 1.0f / InScaleV;
GUnrealEd->Exec( GetWorld(), *FString::Printf( TEXT("POLY TEXSCALE %s UU=%f VV=%f"), InRelative?TEXT("RELATIVE"):TEXT(""), InScaleU, InScaleV ) );
}
void SSurfaceProperties::AddReferencedObjects(FReferenceCollector& Collector)
{
// we need to serialize all the UObjects in case of a GC
for( int32 Index = 0; Index < LevelLightmassSettingsObjects.Num(); Index++ )
{
TLightmassSettingsObjectArray& SettingsArray = LevelLightmassSettingsObjects[ Index ];
for( int32 SettingIndex = 0; SettingIndex < SettingsArray.Num(); SettingIndex++ )
{
Collector.AddReferencedObject( SettingsArray[ SettingIndex ] );
}
}
for( int32 Index = 0; Index < SelectedLightmassSettingsObjects.Num(); Index++ )
{
Collector.AddReferencedObject( SelectedLightmassSettingsObjects[ Index ] );
}
}
FString SSurfaceProperties::GetReferencerName() const
{
return TEXT("SSurfaceProperties");
}
TOptional<float> SSurfaceProperties::GetLightmapResolutionValue() const
{
float LightMapScale = 0.0f;
int32 SelectedSurfaceCount = 0;
bool bMultipleValues = false;
if ( GetWorld() )
{
for ( int32 LevelIndex = 0 ; LevelIndex < GetWorld()->GetNumLevels() ; ++LevelIndex )
{
const ULevel* Level = GetWorld()->GetLevel(LevelIndex);
const UModel* Model = Level->Model;
TLightmassSettingsObjectArray ObjArray;
for(int32 SurfaceIndex = 0; SurfaceIndex < Model->Surfs.Num();SurfaceIndex++)
{
const FBspSurf& Surf = Model->Surfs[SurfaceIndex];
if(Surf.PolyFlags & PF_Selected)
{
if(SelectedSurfaceCount == 0)
{
LightMapScale = Surf.LightMapScale;
}
else if(SelectedSurfaceCount > 0 && LightMapScale != Surf.LightMapScale)
{
bMultipleValues = true;
}
SelectedSurfaceCount++;
}
}
}
}
if(bMultipleValues)
{
// Return an unset value so it displays the undetermined indicator
return TOptional<float>();
}
else
{
return LightMapScale;
}
}
void SSurfaceProperties::OnLightmapResolutionCommitted(float NewValue, ETextCommit::Type CommitInfo)
{
const float LightMapScale = FMath::Clamp<float>(NewValue, 0.1f, 65536.0f);
bool bSurfacesDirty = false;
for ( int32 LevelIndex = 0 ; LevelIndex < GetWorld()->GetNumLevels(); ++LevelIndex )
{
ULevel* Level = GetWorld()->GetLevel(LevelIndex);
UModel* Model = Level->Model;
for(int32 SurfaceIndex = 0;SurfaceIndex < Model->Surfs.Num();SurfaceIndex++)
{
FBspSurf& Surf = Model->Surfs[SurfaceIndex];
if ( (Surf.PolyFlags&PF_Selected) != 0 && Surf.Actor != NULL )
{
Surf.Actor->Brush->Polys->Element[Surf.iBrushPoly].LightMapScale = LightMapScale;
Surf.LightMapScale = LightMapScale;
bSurfacesDirty = true;
}
}
}
if ( bSurfacesDirty )
{
GetWorld()->MarkPackageDirty();
ULevel::LevelDirtiedEvent.Broadcast();
}
}
void SSurfaceProperties::NotifyPostChange( const FPropertyChangedEvent& PropertyChangedEvent, FProperty* PropertyThatChanged )
{
// update any selected lightmass settings with the new information
if (SelectedLightmassSettingsObjects.Num() > 0)
{
SetLightmassSettingsForSelectedSurfaces(Cast<ULightmassPrimitiveSettingsObject>(SelectedLightmassSettingsObjects[0])->LightmassSettings);
}
}
void SSurfaceProperties::SetLightmassSettingsForSelectedSurfaces(FLightmassPrimitiveSettings& InSettings)
{
bool bSawLightmassSettingsChange = false;
for (int32 LevelIndex = 0 ; LevelIndex < GetWorld()->GetNumLevels() ; ++LevelIndex)
{
ULevel* Level = GetWorld()->GetLevel(LevelIndex);
UModel* Model = Level->Model;
for (int32 SurfaceIndex = 0 ; SurfaceIndex < Model->Surfs.Num() ; ++SurfaceIndex)
{
FBspSurf& Surf = Model->Surfs[SurfaceIndex];
if (((Surf.PolyFlags&PF_Selected) != 0) && (Surf.Actor != NULL))
{
int32 LookupIndex = FMath::Clamp<int32>(Surf.iLightmassIndex, 0, Model->LightmassSettings.Num());
FLightmassPrimitiveSettings& Settings = Model->LightmassSettings[LookupIndex];
if (!(Settings == InSettings))
{
// See if we can find the one of interest...
int32 FoundLightmassIndex = INDEX_NONE;
if (Model->LightmassSettings.Find(InSettings, FoundLightmassIndex) == false)
{
FoundLightmassIndex = Model->LightmassSettings.Add(InSettings);
}
Surf.iLightmassIndex = FoundLightmassIndex;
bSawLightmassSettingsChange = true;
Surf.Actor->Brush->Polys->Element[Surf.iBrushPoly].LightmassSettings = InSettings;
}
}
}
// Clean out unused Lightmass settings from the model...
if (bSawLightmassSettingsChange)
{
TArray<bool> UsedIndices;
UsedIndices.Empty(Model->LightmassSettings.Num());
UsedIndices.AddZeroed(Model->LightmassSettings.Num());
for (int32 SurfaceIndex = 0 ; SurfaceIndex < Model->Surfs.Num() ; ++SurfaceIndex)
{
FBspSurf& Surf = Model->Surfs[SurfaceIndex];
if (Surf.Actor != NULL)
{
if ((Surf.iLightmassIndex >= 0) && (Surf.iLightmassIndex < Model->LightmassSettings.Num()))
{
UsedIndices[Surf.iLightmassIndex] = true;
}
}
}
for (int32 UsedIndex = UsedIndices.Num() - 1; UsedIndex >= 0; UsedIndex--)
{
if (UsedIndices[UsedIndex] == false)
{
Model->LightmassSettings.RemoveAt(UsedIndex);
for (int32 SurfaceIndex = 0 ; SurfaceIndex < Model->Surfs.Num() ; ++SurfaceIndex)
{
FBspSurf& Surf = Model->Surfs[SurfaceIndex];
if (Surf.Actor != NULL)
{
check (Surf.iLightmassIndex != UsedIndex);
if (Surf.iLightmassIndex > UsedIndex)
{
Surf.iLightmassIndex--;
check(Surf.iLightmassIndex >= 0);
}
}
}
}
}
}
}
if (bSawLightmassSettingsChange)
{
GetWorld()->MarkPackageDirty();
ULevel::LevelDirtiedEvent.Broadcast();
}
}
void SSurfaceProperties::OnCustomPanValueCommitted( int32 NewValue, ETextCommit::Type CommitInfo, TextureCoordChannel Channel )
{
if (CommitInfo == ETextCommit::OnEnter)
{
OnPanTexture( NewValue, Channel);
}
CustomPanButtoms[Channel].Pin()->SetIsOpen(false);
}
void SSurfaceProperties::OnScaleLabelClicked( )
{
bUseRelativeScaling = !bUseRelativeScaling;
GConfig->SetBool(TEXT("SurfaceSelection"), TEXT("UseRelativeScaling"), bUseRelativeScaling, GEditorPerProjectIni);
}
FText SSurfaceProperties::GetScalingLabel() const
{
return (bUseRelativeScaling) ? LOCTEXT("ScaleRelativeTitle", "Scale Relative:") : LOCTEXT("ScaleTitle", "Scale:");
}
TSharedRef< ITableRow > SSurfaceProperties::OnGenerateScaleTableRow( TSharedPtr<FString> Item, const TSharedRef< STableViewBase >& OwnerTable )
{
return SNew(STableRow<TSharedPtr<FString>>, OwnerTable)
[
SNew(STextBlock) .Text(FText::FromString(*Item.Get()))
];
}
void SSurfaceProperties::OnScaleSelectionChanged(TSharedPtr<FString> ProposedSelection, ESelectInfo::Type SelectInfo, TextureCoordChannel Channel)
{
if (ProposedSelection.IsValid())
{
float Scaling = FCString::Atof(*(*ProposedSelection.Get()));
OnScaleValueCommitted(Scaling, ETextCommit::OnEnter, Channel);
ScalingListViews[Channel].Pin()->ClearSelection();
ScalingComboButton[Channel].Pin()->SetIsOpen(false);
}
}
void SSurfaceProperties::OnScaleValueCommitted(float Value, ETextCommit::Type CommitInfo,TextureCoordChannel Channel)
{
if (bPreserveScaleRatio)
{
CachedScalingValueU = CachedScalingValueV = Value;
}
else if (Channel == UChannel)
{
CachedScalingValueU = Value;
}
else
{
CachedScalingValueV = Value;
}
}
TOptional<float> SSurfaceProperties::OnGetScalingValue(TextureCoordChannel Channel) const
{
return (Channel == UChannel) ? CachedScalingValueU : CachedScalingValueV;
}
FReply SSurfaceProperties::OnApplyScaling()
{
OnScaleTexture(CachedScalingValueU,CachedScalingValueV,bUseRelativeScaling);
return FReply::Handled();
}
const FSlateBrush* SSurfaceProperties::GetPreserveScaleRatioImage() const
{
return bPreserveScaleRatio ? FAppStyle::GetBrush( TEXT("Icons.Lock") ) : FAppStyle::GetBrush( TEXT("Icons.Unlock") ) ;
}
ECheckBoxState SSurfaceProperties::IsPreserveScaleRatioChecked() const
{
return bPreserveScaleRatio ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
void SSurfaceProperties::OnPreserveScaleRatioToggled( ECheckBoxState NewState )
{
bPreserveScaleRatio = (NewState == ECheckBoxState::Checked) ? true : false;
CachedScalingValueV = CachedScalingValueU;
GConfig->SetBool(TEXT("SurfaceSelection"), TEXT("PreserveScaleRatio"), bPreserveScaleRatio, GEditorPerProjectIni);
}
void SSurfaceProperties::OnCustomRotateValueCommitted(int32 NewValue, ETextCommit::Type CommitInfo)
{
if (CommitInfo == ETextCommit::OnEnter)
{
OnRotateTexture(NewValue,Rotate);
}
CustomRotationButton.Pin()->SetIsOpen(false);
}
const FSlateBrush* SSurfaceProperties::GetTogglePanDirectionImage( TextureCoordChannel Channel ) const
{
if (Channel == UChannel)
{
return bUseNegativePanningU ? FAppStyle::GetBrush( TEXT("SurfaceDetails.PanUNegative") ) : FAppStyle::GetBrush( TEXT("SurfaceDetails.PanUPositive") ) ;
}
return bUseNegativePanningV ? FAppStyle::GetBrush( TEXT("SurfaceDetails.PanVNegative") ) : FAppStyle::GetBrush( TEXT("SurfaceDetails.PanVPositive") ) ;
}
ECheckBoxState SSurfaceProperties::IsUsingNegativePanning( TextureCoordChannel Channel ) const
{
if (Channel == UChannel)
{
return bUseNegativePanningU ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
return bUseNegativePanningV ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
void SSurfaceProperties::OnTogglePanningDirection( ECheckBoxState NewState, TextureCoordChannel Channel )
{
bool CheckBoxState = (NewState == ECheckBoxState::Checked) ? true : false;
(Channel == UChannel) ? bUseNegativePanningU = CheckBoxState : bUseNegativePanningV = CheckBoxState;
}
const FSlateBrush* SSurfaceProperties::GetToggleRotationDirectionImage() const
{
return bUseNegativeRotation ? FAppStyle::GetBrush( TEXT("SurfaceDetails.ClockwiseRotation") ) : FAppStyle::GetBrush( TEXT("SurfaceDetails.AntiClockwiseRotation") ) ;
}
ECheckBoxState SSurfaceProperties::IsUsingNegativeRotation() const
{
return bUseNegativeRotation ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
void SSurfaceProperties::OnToggleRotationDirection( ECheckBoxState NewState )
{
bUseNegativeRotation = (NewState == ECheckBoxState::Checked) ? true : false;
}
#undef LOCTEXT_NAMESPACE