diff --git a/OpenRA.Mods.Common/Traits/PaletteEffects/MenuPaletteEffect.cs b/OpenRA.Mods.Common/Traits/PaletteEffects/MenuPostProcessEffect.cs similarity index 51% rename from OpenRA.Mods.Common/Traits/PaletteEffects/MenuPaletteEffect.cs rename to OpenRA.Mods.Common/Traits/PaletteEffects/MenuPostProcessEffect.cs index 8693e4630c..1aedffde07 100644 --- a/OpenRA.Mods.Common/Traits/PaletteEffects/MenuPaletteEffect.cs +++ b/OpenRA.Mods.Common/Traits/PaletteEffects/MenuPostProcessEffect.cs @@ -9,9 +9,7 @@ */ #endregion -using System.Collections.Generic; using OpenRA.Graphics; -using OpenRA.Primitives; using OpenRA.Traits; using OpenRA.Widgets; @@ -19,97 +17,56 @@ namespace OpenRA.Mods.Common.Traits { [TraitLocation(SystemActors.World | SystemActors.EditorWorld)] [Desc("Fades the world from/to black at the start/end of the game, and can (optionally) desaturate the world")] - public class MenuPaletteEffectInfo : TraitInfo + public class MenuPostProcessEffectInfo : TraitInfo { [Desc("Time (in ticks) to fade between states")] public readonly int FadeLength = 10; [Desc("Effect style to fade to during gameplay. Accepts values of None or Desaturated.")] - public readonly MenuPaletteEffect.EffectType Effect = MenuPaletteEffect.EffectType.None; + public readonly MenuPostProcessEffect.EffectType Effect = MenuPostProcessEffect.EffectType.None; [Desc("Effect style to fade to when opening the in-game menu. Accepts values of None, Black or Desaturated.")] - public readonly MenuPaletteEffect.EffectType MenuEffect = MenuPaletteEffect.EffectType.None; + public readonly MenuPostProcessEffect.EffectType MenuEffect = MenuPostProcessEffect.EffectType.None; - public override object Create(ActorInitializer init) { return new MenuPaletteEffect(this); } + public override object Create(ActorInitializer init) { return new MenuPostProcessEffect(this); } } - public class MenuPaletteEffect : IPaletteModifier, IRender, IWorldLoaded, INotifyGameLoaded + public class MenuPostProcessEffect : RenderPostProcessPassBase, IWorldLoaded, INotifyGameLoaded { public enum EffectType { None, Black, Desaturated } - public readonly MenuPaletteEffectInfo Info; + public readonly MenuPostProcessEffectInfo Info; EffectType from = EffectType.Black; EffectType to = EffectType.Black; - float frac = 0; long startTime; long endTime; - public MenuPaletteEffect(MenuPaletteEffectInfo info) { Info = info; } + public MenuPostProcessEffect(MenuPostProcessEffectInfo info) + : base("menufade", PostProcessPassType.AfterShroud) + { + Info = info; + } public void Fade(EffectType type) { startTime = Game.RunTime; endTime = startTime + Ui.Timestep * Info.FadeLength; - frac = 1; from = to; to = type; } - IEnumerable IRender.Render(Actor self, WorldRenderer wr) + protected override bool Enabled => to != EffectType.None || endTime != 0; + protected override void PrepareRender(WorldRenderer wr, IShader shader) { - if (endTime == 0) - yield break; + var blend = (endTime - Game.RunTime) * 1f / (endTime - startTime); + if (blend < 0) + blend = startTime = endTime = 0; - frac = (endTime - Game.RunTime) * 1f / (endTime - startTime); - if (frac < 0) - frac = startTime = endTime = 0; - - yield break; - } - - IEnumerable IRender.ScreenBounds(Actor self, WorldRenderer wr) - { - yield break; - } - - static Color ColorForEffect(EffectType t, Color orig) - { - switch (t) - { - case EffectType.Black: - return Color.FromArgb(orig.A, Color.Black); - case EffectType.Desaturated: - var lum = (int)(255 * orig.GetBrightness()); - return Color.FromArgb(orig.A, lum, lum, lum); - default: - case EffectType.None: - return orig; - } - } - - public void AdjustPalette(IReadOnlyDictionary palettes) - { - if (to == EffectType.None && endTime == 0) - return; - - foreach (var pal in palettes.Values) - { - for (var x = 0; x < Palette.Size; x++) - { - var orig = pal.GetColor(x); - var t = ColorForEffect(to, orig); - - if (endTime == 0) - pal.SetColor(x, t); - else - { - var f = ColorForEffect(from, orig); - pal.SetColor(x, Exts.ColorLerp(frac, t, f)); - } - } - } + shader.SetVec("From", (int)from); + shader.SetVec("To", (int)to); + shader.SetVec("Blend", blend); } void IWorldLoaded.WorldLoaded(World w, WorldRenderer wr) diff --git a/OpenRA.Mods.Common/UpdateRules/Rules/20230801/ReplacePaletteModifiers.cs b/OpenRA.Mods.Common/UpdateRules/Rules/20230801/ReplacePaletteModifiers.cs new file mode 100644 index 0000000000..bc43165c6f --- /dev/null +++ b/OpenRA.Mods.Common/UpdateRules/Rules/20230801/ReplacePaletteModifiers.cs @@ -0,0 +1,30 @@ +#region Copyright & License Information +/* + * Copyright (c) The OpenRA Developers and Contributors + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System.Collections.Generic; + +namespace OpenRA.Mods.Common.UpdateRules.Rules +{ + public class ReplacePaletteModifiers : UpdateRule + { + public override string Name => "Replace palette modifiers with post-processing shaders."; + + public override string Description => + "MenuPaletteEffect is renamed to MenuPostProcessEffect."; + + public override IEnumerable UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode) + { + actorNode.RenameChildrenMatching("MenuPaletteEffect", "MenuPostProcessEffect"); + + yield break; + } + } +} diff --git a/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs b/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs index 7c8970e450..b2ff95bd19 100644 --- a/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs +++ b/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs @@ -110,6 +110,7 @@ namespace OpenRA.Mods.Common.UpdateRules // bleed only changes here. new RemoveValidRelationsFromCapturable(), new ExtractResourceStorageFromHarvester(), + new ReplacePaletteModifiers(), // Execute these rules last to avoid premature yaml merge crashes. new AbstractDocking(), diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/IngameMenuLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/IngameMenuLogic.cs index 44b863de00..7f4886ff7d 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/IngameMenuLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/IngameMenuLogic.cs @@ -148,7 +148,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic readonly Action onExit; readonly World world; readonly WorldRenderer worldRenderer; - readonly MenuPaletteEffect mpe; + readonly MenuPostProcessEffect mpe; readonly bool isSinglePlayer; readonly bool hasError; bool leaving; @@ -184,7 +184,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic isSinglePlayer = !world.LobbyInfo.GlobalSettings.Dedicated && world.LobbyInfo.NonBotClients.Count() == 1; menu = widget.Get("INGAME_MENU"); - mpe = world.WorldActor.TraitOrDefault(); + mpe = world.WorldActor.TraitOrDefault(); mpe?.Fade(mpe.Info.MenuEffect); menu.Get("VERSION_LABEL").Text = modData.Manifest.Metadata.Version; @@ -249,13 +249,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic var iop = world.WorldActor.TraitsImplementing().FirstOrDefault(); var exitDelay = iop?.ExitDelay ?? 0; - var mpe = world.WorldActor.TraitOrDefault(); + var mpe = world.WorldActor.TraitOrDefault(); if (mpe != null) { Game.RunAfterDelay(exitDelay, () => { if (Game.IsCurrentWorld(world)) - mpe.Fade(MenuPaletteEffect.EffectType.Black); + mpe.Fade(MenuPostProcessEffect.EffectType.Black); }); exitDelay += 40 * mpe.Info.FadeLength; } @@ -280,7 +280,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic void CloseMenu() { Ui.CloseWindow(); - mpe?.Fade(MenuPaletteEffect.EffectType.None); + mpe?.Fade(MenuPostProcessEffect.EffectType.None); onExit(); Ui.ResetTooltips(); } @@ -342,7 +342,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (mpe != null) { if (Game.IsCurrentWorld(world)) - mpe.Fade(MenuPaletteEffect.EffectType.Black); + mpe.Fade(MenuPostProcessEffect.EffectType.Black); exitDelay += 40 * mpe.Info.FadeLength; } @@ -536,7 +536,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic Ui.ResetTooltips(); void CloseMenu() { - mpe?.Fade(MenuPaletteEffect.EffectType.None); + mpe?.Fade(MenuPostProcessEffect.EffectType.None); onExit(); } diff --git a/glsl/postprocess_menufade.frag b/glsl/postprocess_menufade.frag new file mode 100644 index 0000000000..dc5575dc75 --- /dev/null +++ b/glsl/postprocess_menufade.frag @@ -0,0 +1,47 @@ +#version {VERSION} +#ifdef GL_ES +precision mediump float; +#endif + +uniform float From; +uniform float To; +uniform float Blend; +uniform sampler2D WorldTexture; + +#if __VERSION__ == 120 +uniform vec2 WorldTextureSize; +#else +out vec4 fragColor; +#endif + +vec4 ColorForEffect(float effect, vec4 c) +{ + if (effect > 1.5) + { + float lum = 0.5 * (min(c.r, min(c.g, c.b)) + max(c.r, max(c.g, c.b))); + return vec4(lum, lum, lum, c.a); + } + + if (effect > 0.5) + { + return vec4(0, 0, 0, c.a); + } + + return c; +} + +void main() +{ +#if __VERSION__ == 120 + vec4 c = texture2D(WorldTexture, gl_FragCoord.xy / WorldTextureSize); +#else + vec4 c = texture(WorldTexture, gl_FragCoord.xy / textureSize(WorldTexture, 0)); +#endif + c = ColorForEffect(From, c) * Blend + ColorForEffect(To, c) * (1.0 - Blend); + + #if __VERSION__ == 120 + gl_FragColor = c; + #else + fragColor = c; + #endif +} diff --git a/mods/cnc/rules/palettes.yaml b/mods/cnc/rules/palettes.yaml index 103bb91e91..c9ff698aab 100644 --- a/mods/cnc/rules/palettes.yaml +++ b/mods/cnc/rules/palettes.yaml @@ -97,7 +97,7 @@ PlayerColorPalette: BasePalette: terrain RemapIndex: 176, 178, 180, 182, 184, 186, 189, 191, 177, 179, 181, 183, 185, 187, 188, 190 - MenuPaletteEffect: + MenuPostProcessEffect: MenuEffect: Desaturated CloakPaletteEffect: FlashPaletteEffect: diff --git a/mods/ra/rules/palettes.yaml b/mods/ra/rules/palettes.yaml index 19d21dff4d..a8405ba29e 100644 --- a/mods/ra/rules/palettes.yaml +++ b/mods/ra/rules/palettes.yaml @@ -84,7 +84,7 @@ BaseName: cloak BasePalette: player Alpha: 0.55 - MenuPaletteEffect: + MenuPostProcessEffect: RotationPaletteEffect@defaultwater: Palettes: terrain ExcludeTilesets: DESERT diff --git a/mods/ts/rules/palettes.yaml b/mods/ts/rules/palettes.yaml index aa424dadf4..5c644a8753 100644 --- a/mods/ts/rules/palettes.yaml +++ b/mods/ts/rules/palettes.yaml @@ -160,4 +160,4 @@ BasePalette: terraindecoration Name: terrainalpha Alpha: 0.55 - MenuPaletteEffect: + MenuPostProcessEffect: