// Voir LPColorGradeSubsystem.h. #include "LPColorGradeSubsystem.h" #include "Blueprint/UserWidget.h" #include "Components/HorizontalBox.h" #include "Components/HorizontalBoxSlot.h" #include "Components/PanelWidget.h" #include "Components/ScrollBox.h" #include "Components/ScrollBoxSlot.h" #include "Components/SizeBox.h" #include "Components/Slider.h" #include "Components/TextBlock.h" #include "Engine/GameInstance.h" #include "Engine/PostProcessVolume.h" #include "Engine/World.h" #include "LifePlay.h" #include "Misc/ConfigCacheIni.h" #include "Misc/Paths.h" #include "Styling/CoreStyle.h" #include "Styling/SlateTypes.h" namespace LPColorGrade { FString IniPath() { return FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("Config/Windows/LPColorGrade.ini")); } FString FormatValue(float Value, int32 Decimals) { return FString::Printf(TEXT("%.*f"), Decimals, Value); } FSlateFontInfo MenuFont() { FSlateFontInfo Font = FCoreStyle::GetDefaultFontStyle("Regular", 20); Font.OutlineSettings.OutlineSize = 2; Font.OutlineSettings.bApplyOutlineToDropShadows = true; Font.OutlineSettings.OutlineColor = FLinearColor(0.f, 0.f, 0.f, 0.8f); return Font; } void FindLook(UWidget* Widget, UTextBlock*& OutText, USlider*& OutSlider) { if (!Widget || (OutText && OutSlider)) { return; } if (!OutText) { OutText = Cast(Widget); } if (!OutSlider) { OutSlider = Cast(Widget); } if (UUserWidget* User = Cast(Widget)) { FindLook(User->GetRootWidget(), OutText, OutSlider); } if (UPanelWidget* Panel = Cast(Widget)) { for (int32 Index = 0; Index < Panel->GetChildrenCount() && !(OutText && OutSlider); ++Index) { FindLook(Panel->GetChildAt(Index), OutText, OutSlider); } } } } void ULPColorGradeSubsystem::Initialize(FSubsystemCollectionBase& Collection) { Super::Initialize(Collection); Load(); TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &ULPColorGradeSubsystem::Tick), 0.5f); } void ULPColorGradeSubsystem::Deinitialize() { FTSTicker::GetCoreTicker().RemoveTicker(TickHandle); Super::Deinitialize(); } bool ULPColorGradeSubsystem::Tick(float DeltaTime) { (void)DeltaTime; EnsureVolume(); EnsureControls(); return true; } void ULPColorGradeSubsystem::Load() { const FString Ini = LPColorGrade::IniPath(); FConfigFile File; File.Read(Ini); auto Read = [&File](const TCHAR* Key, float& Out) { FString Text; if (File.GetString(TEXT("LPColorGrade"), Key, Text)) { Out = FCString::Atof(*Text); } }; Read(TEXT("Brightness"), Brightness); Read(TEXT("Saturation"), Saturation); Read(TEXT("Contrast"), Contrast); Read(TEXT("WhiteBalance"), WhiteBalance); Read(TEXT("Tint"), Tint); Brightness = FMath::Clamp(Brightness, -2.f, 2.f); Saturation = FMath::Clamp(Saturation, 0.f, 2.f); Contrast = FMath::Clamp(Contrast, 0.f, 2.f); WhiteBalance = FMath::Clamp(WhiteBalance, 1500.f, 15000.f); Tint = FMath::Clamp(Tint, -1.f, 1.f); UE_LOG(LogLifePlay, Display, TEXT("Couleur : relu Brightness %.2f, Saturation %.2f, Contrast %.2f, White Balance %.0f, Tint %.2f depuis %s"), Brightness, Saturation, Contrast, WhiteBalance, Tint, *Ini); } void ULPColorGradeSubsystem::Save() const { const FString Ini = LPColorGrade::IniPath(); FConfigFile File; File.Read(Ini); auto Write = [&File](const TCHAR* Key, float Value) { File.SetString(TEXT("LPColorGrade"), Key, *FString::SanitizeFloat(Value)); }; Write(TEXT("Brightness"), Brightness); Write(TEXT("Saturation"), Saturation); Write(TEXT("Contrast"), Contrast); Write(TEXT("WhiteBalance"), WhiteBalance); Write(TEXT("Tint"), Tint); File.Write(Ini); UE_LOG(LogLifePlay, Display, TEXT("Couleur : Brightness %.2f, Saturation %.2f, Contrast %.2f, White Balance %.0f, Tint %.2f enregistres dans %s"), Brightness, Saturation, Contrast, WhiteBalance, Tint, *Ini); } void ULPColorGradeSubsystem::ApplyGrade() { APostProcessVolume* Volume = GradeVolume.Get(); if (!Volume) { return; } FPostProcessSettings& Settings = Volume->Settings; Settings.bOverride_AutoExposureBias = true; Settings.AutoExposureBias = Brightness; Settings.bOverride_ColorSaturation = true; Settings.ColorSaturation = FVector4(Saturation, Saturation, Saturation, 1.f); Settings.bOverride_ColorContrast = true; Settings.ColorContrast = FVector4(Contrast, Contrast, Contrast, 1.f); Settings.bOverride_TemperatureType = true; Settings.TemperatureType = TEMP_WhiteBalance; Settings.bOverride_WhiteTemp = true; Settings.WhiteTemp = WhiteBalance; Settings.bOverride_WhiteTint = true; Settings.WhiteTint = Tint; } void ULPColorGradeSubsystem::EnsureVolume() { UWorld* World = GetWorld(); if (!World || !World->IsGameWorld()) { return; } if (GradeVolume.IsValid() && GradeVolume->GetWorld() == World) { ApplyGrade(); return; } FActorSpawnParameters Params; Params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; Params.ObjectFlags |= RF_Transient; APostProcessVolume* Volume = World->SpawnActor(APostProcessVolume::StaticClass(), Params); if (!Volume) { return; } Volume->bUnbound = true; Volume->bEnabled = true; Volume->BlendWeight = 1.f; Volume->Priority = 100.f; GradeVolume = Volume; ApplyGrade(); UE_LOG(LogLifePlay, Display, TEXT("Couleur : exposition, saturation, contraste et balance des blancs")); } USlider* ULPColorGradeSubsystem::AddRow(UScrollBox* Scroll, const TCHAR* Name, const TCHAR* Label, float Min, float Max, float Step, float Value, FName Handler, const TCHAR* Tip, const FSlateFontInfo& Font, const FSlateColor& Color, const FSliderStyle* SliderStyle) { UHorizontalBox* Row = NewObject(Scroll, Name); UTextBlock* Caption = NewObject(Row); Caption->SetText(FText::FromString(Label)); Caption->SetFont(Font); Caption->SetColorAndOpacity(Color); USizeBox* CaptionBox = NewObject(Row); CaptionBox->SetWidthOverride(220.f); CaptionBox->AddChild(Caption); USlider* Slider = NewObject(Row); if (SliderStyle) { Slider->SetWidgetStyle(*SliderStyle); } Slider->SetMinValue(Min); Slider->SetMaxValue(Max); Slider->SetStepSize(Step); Slider->SetValue(Value); UTextBlock* Readout = NewObject(Row); Readout->SetFont(Font); Readout->SetColorAndOpacity(Color); if (UHorizontalBoxSlot* Slot = Row->AddChildToHorizontalBox(CaptionBox)) { Slot->SetVerticalAlignment(VAlign_Center); Slot->SetPadding(FMargin(10.f, 0.f, 20.f, 0.f)); } if (UHorizontalBoxSlot* Slot = Row->AddChildToHorizontalBox(Slider)) { Slot->SetSize(FSlateChildSize(ESlateSizeRule::Fill)); Slot->SetVerticalAlignment(VAlign_Center); Slot->SetPadding(FMargin(0.f, 0.f, 20.f, 0.f)); } if (UHorizontalBoxSlot* Slot = Row->AddChildToHorizontalBox(Readout)) { Slot->SetVerticalAlignment(VAlign_Center); Slot->SetPadding(FMargin(0.f, 0.f, 10.f, 0.f)); } if (UScrollBoxSlot* Slot = Cast(Scroll->AddChild(Row))) { Slot->SetPadding(FMargin(0.f, 0.f, 0.f, 10.f)); } Row->SetToolTipText(FText::FromString(Tip)); FScriptDelegate Delegate; Delegate.BindUFunction(this, Handler); Slider->OnValueChanged.Add(Delegate); if (Name == FString(TEXT("LPColorBrightness"))) { BrightnessValue = Readout; } else if (Name == FString(TEXT("LPColorSaturation"))) { SaturationValue = Readout; } else if (Name == FString(TEXT("LPColorContrast"))) { ContrastValue = Readout; } else if (Name == FString(TEXT("LPColorWhiteBalance"))) { WhiteBalanceValue = Readout; } else if (Name == FString(TEXT("LPColorTint"))) { TintValue = Readout; } return Slider; } void ULPColorGradeSubsystem::EnsureControls() { UScrollBox* Scroll = nullptr; for (TObjectIterator It; It; ++It) { UScrollBox* Candidate = *It; if (!Candidate || Candidate->GetFName() != TEXT("SettingsScrollBox") || Candidate->HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject)) { continue; } const UUserWidget* Owner = Candidate->GetTypedOuter(); if (!Owner || Owner->HasAnyFlags(RF_ClassDefaultObject) || Owner->GetGameInstance() != GetGameInstance()) { continue; } Scroll = Candidate; break; } if (!Scroll) { return; } for (int32 Index = 0; Index < Scroll->GetChildrenCount(); ++Index) { if (UWidget* Child = Scroll->GetChildAt(Index)) { if (Child->GetFName() == TEXT("LPColorBrightness")) { return; } } } UTextBlock* SampleText = nullptr; USlider* SampleSlider = nullptr; for (int32 Index = 0; Index < Scroll->GetChildrenCount(); ++Index) { LPColorGrade::FindLook(Scroll->GetChildAt(Index), SampleText, SampleSlider); } const FSlateFontInfo Font = SampleText ? SampleText->GetFont() : LPColorGrade::MenuFont(); const FSlateColor Color = SampleText ? SampleText->GetColorAndOpacity() : FSlateColor(FLinearColor(1.f, 1.f, 1.f, 0.95f)); const FSliderStyle* SliderStyle = SampleSlider ? &SampleSlider->GetWidgetStyle() : nullptr; AddRow(Scroll, TEXT("LPColorBrightness"), TEXT("Brightness"), -2.f, 2.f, 0.05f, Brightness, GET_FUNCTION_NAME_CHECKED(ULPColorGradeSubsystem, OnBrightnessChanged), TEXT("Exposure compensation. -1 is twice as dark, +1 is twice as bright."), Font, Color, SliderStyle); AddRow(Scroll, TEXT("LPColorSaturation"), TEXT("Saturation"), 0.f, 2.f, 0.05f, Saturation, GET_FUNCTION_NAME_CHECKED(ULPColorGradeSubsystem, OnSaturationChanged), TEXT("Color intensity. 1 is unchanged, 0 is gray."), Font, Color, SliderStyle); AddRow(Scroll, TEXT("LPColorContrast"), TEXT("Contrast"), 0.f, 2.f, 0.05f, Contrast, GET_FUNCTION_NAME_CHECKED(ULPColorGradeSubsystem, OnContrastChanged), TEXT("Range between light and dark. 1 is unchanged."), Font, Color, SliderStyle); AddRow(Scroll, TEXT("LPColorWhiteBalance"), TEXT("White Balance"), 1500.f, 15000.f, 50.f, WhiteBalance, GET_FUNCTION_NAME_CHECKED(ULPColorGradeSubsystem, OnWhiteBalanceChanged), TEXT("Color temperature in kelvin. 6500 is daylight."), Font, Color, SliderStyle); AddRow(Scroll, TEXT("LPColorTint"), TEXT("Tint"), -1.f, 1.f, 0.01f, Tint, GET_FUNCTION_NAME_CHECKED(ULPColorGradeSubsystem, OnTintChanged), TEXT("Magenta to green. 0 is unchanged."), Font, Color, SliderStyle); if (UTextBlock* Readout = BrightnessValue.Get()) { Readout->SetText(FText::FromString(LPColorGrade::FormatValue(Brightness, 2))); } if (UTextBlock* Readout = SaturationValue.Get()) { Readout->SetText(FText::FromString(LPColorGrade::FormatValue(Saturation, 2))); } if (UTextBlock* Readout = ContrastValue.Get()) { Readout->SetText(FText::FromString(LPColorGrade::FormatValue(Contrast, 2))); } if (UTextBlock* Readout = WhiteBalanceValue.Get()) { Readout->SetText(FText::FromString(LPColorGrade::FormatValue(WhiteBalance, 0))); } if (UTextBlock* Readout = TintValue.Get()) { Readout->SetText(FText::FromString(LPColorGrade::FormatValue(Tint, 2))); } } void ULPColorGradeSubsystem::OnBrightnessChanged(float Value) { Brightness = Value; if (UTextBlock* Readout = BrightnessValue.Get()) { Readout->SetText(FText::FromString(LPColorGrade::FormatValue(Value, 2))); } ApplyGrade(); Save(); } void ULPColorGradeSubsystem::OnSaturationChanged(float Value) { Saturation = Value; if (UTextBlock* Readout = SaturationValue.Get()) { Readout->SetText(FText::FromString(LPColorGrade::FormatValue(Value, 2))); } ApplyGrade(); Save(); } void ULPColorGradeSubsystem::OnContrastChanged(float Value) { Contrast = Value; if (UTextBlock* Readout = ContrastValue.Get()) { Readout->SetText(FText::FromString(LPColorGrade::FormatValue(Value, 2))); } ApplyGrade(); Save(); } void ULPColorGradeSubsystem::OnWhiteBalanceChanged(float Value) { WhiteBalance = Value; if (UTextBlock* Readout = WhiteBalanceValue.Get()) { Readout->SetText(FText::FromString(LPColorGrade::FormatValue(Value, 0))); } ApplyGrade(); Save(); } void ULPColorGradeSubsystem::OnTintChanged(float Value) { Tint = Value; if (UTextBlock* Readout = TintValue.Get()) { Readout->SetText(FText::FromString(LPColorGrade::FormatValue(Value, 2))); } ApplyGrade(); Save(); }