diff --git a/OpenRA.Mods.Common/Scripting/Global/LightingGlobal.cs b/OpenRA.Mods.Common/Scripting/Global/LightingGlobal.cs index 29dafb1916..d9bc2da36f 100644 --- a/OpenRA.Mods.Common/Scripting/Global/LightingGlobal.cs +++ b/OpenRA.Mods.Common/Scripting/Global/LightingGlobal.cs @@ -19,15 +19,13 @@ namespace OpenRA.Mods.Common.Scripting public class LightingGlobal : ScriptGlobal { readonly IEnumerable flashEffects; - readonly GlobalLightingPaletteEffect lighting; - readonly bool hasLighting; + readonly TintPostProcessEffect tintEffect; public LightingGlobal(ScriptContext context) : base(context) { flashEffects = context.World.WorldActor.TraitsImplementing(); - lighting = context.World.WorldActor.TraitOrDefault(); - hasLighting = lighting != null; + tintEffect = context.World.WorldActor.TraitOrDefault(); } [Desc("Controls the `" + nameof(FlashPostProcessEffect) + "` trait.")] @@ -40,26 +38,26 @@ namespace OpenRA.Mods.Common.Scripting public double Red { - get => hasLighting ? lighting.Red : 1d; - set { if (hasLighting) lighting.Red = (float)value; } + get => tintEffect?.Red ?? 1; + set { if (tintEffect != null) tintEffect.Red = (float)value; } } public double Green { - get => hasLighting ? lighting.Green : 1d; - set { if (hasLighting) lighting.Green = (float)value; } + get => tintEffect?.Green ?? 1; + set { if (tintEffect != null) tintEffect.Green = (float)value; } } public double Blue { - get => hasLighting ? lighting.Blue : 1d; - set { if (hasLighting) lighting.Blue = (float)value; } + get => tintEffect?.Blue ?? 1; + set { if (tintEffect != null) tintEffect.Blue = (float)value; } } public double Ambient { - get => hasLighting ? lighting.Ambient : 1d; - set { if (hasLighting) lighting.Ambient = (float)value; } + get => tintEffect?.Ambient ?? 1; + set { if (tintEffect != null) tintEffect.Ambient = (float)value; } } } } diff --git a/OpenRA.Mods.Common/Traits/PaletteEffects/GlobalLightingPaletteEffect.cs b/OpenRA.Mods.Common/Traits/PaletteEffects/GlobalLightingPaletteEffect.cs deleted file mode 100644 index fc9831d038..0000000000 --- a/OpenRA.Mods.Common/Traits/PaletteEffects/GlobalLightingPaletteEffect.cs +++ /dev/null @@ -1,111 +0,0 @@ -#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; -using System.Linq; -using OpenRA.Graphics; -using OpenRA.Traits; - -namespace OpenRA.Mods.Common.Traits -{ - [Desc("Used for day/night effects.")] - [TraitLocation(SystemActors.World | SystemActors.EditorWorld)] - public class GlobalLightingPaletteEffectInfo : TraitInfo, ILobbyCustomRulesIgnore - { - [Desc("Do not modify graphics that use any palette in this list.")] - public readonly HashSet ExcludePalettes = new() { "cursor", "chrome", "colorpicker", "fog", "shroud", "alpha" }; - - [Desc("Do not modify graphics that start with these letters.")] - public readonly HashSet ExcludePalettePrefixes = new(); - - public readonly float Red = 1f; - public readonly float Green = 1f; - public readonly float Blue = 1f; - public readonly float Ambient = 1f; - - public override object Create(ActorInitializer init) { return new GlobalLightingPaletteEffect(this); } - } - - public class GlobalLightingPaletteEffect : IPaletteModifier - { - readonly GlobalLightingPaletteEffectInfo info; - - public float Red; - public float Green; - public float Blue; - public float Ambient; - - public GlobalLightingPaletteEffect(GlobalLightingPaletteEffectInfo info) - { - this.info = info; - - Red = info.Red; - Green = info.Green; - Blue = info.Blue; - Ambient = info.Ambient; - } - - public void AdjustPalette(IReadOnlyDictionary palettes) - { - // Calculate ambient color multipliers as integers for speed. To handle fractional ambiance, we'll increase - // the magnitude of the result by 8 bits. - var ar = (uint)((1 << 8) * Ambient * Red); - var ag = (uint)((1 << 8) * Ambient * Green); - var ab = (uint)((1 << 8) * Ambient * Blue); - - foreach (var kvp in palettes) - { - if (info.ExcludePalettes.Contains(kvp.Key)) - continue; - - if (info.ExcludePalettePrefixes.Any(kvp.Key.StartsWith)) - continue; - - var palette = kvp.Value; - - for (var x = 0; x < Palette.Size; x++) - { - /* Here is the reference code for the operation we are performing. - var from = palette.GetColor(x); - var r = (int)(from.R * Ambient * Red).Clamp(0, 255); - var g = (int)(from.G * Ambient * Green).Clamp(0, 255); - var b = (int)(from.B * Ambient * Blue).Clamp(0, 255); - palette.SetColor(x, Color.FromArgb(from.A, r, g, b)); - */ - - // PERF: Use integer arithmetic to avoid costly conversions to and from floating point values. - var from = palette[x]; - - // 1: Extract each color component and shift it to the lower bits, then multiply with ambiance. - // 2: Because the ambiance was increased by 8 bits, our result has been shifted 8 bits up. - // If the multiply overflowed we clamp the value, otherwise we mask out the fractional bits. - // 3: Finally, we shift the color component back to its correct place. We're already 8 bits higher - // than expected due to the multiply, so we don't have to shift as far to get back. - var r1 = ((from & 0x00FF0000) >> 16) * ar; - var r2 = r1 >= 0x0000FF00 ? 0x0000FF00 : r1 & 0x0000FF00; - var r3 = r2 << 8; - - var g1 = ((from & 0x0000FF00) >> 8) * ag; - var g2 = g1 >= 0x0000FF00 ? 0x0000FF00 : g1 & 0x0000FF00; - var g3 = g2 << 0; - - var b1 = ((from & 0x000000FF) >> 0) * ab; - var b2 = b1 >= 0x0000FF00 ? 0x0000FF00 : b1 & 0x0000FF00; - var b3 = b2 >> 8; - - // Combine all the adjusted components back together. - var a = from & 0xFF000000; - palette[x] = a | r3 | g3 | b3; - } - } - } - } -} diff --git a/OpenRA.Mods.Common/Traits/PaletteEffects/TintPostProcessEffect.cs b/OpenRA.Mods.Common/Traits/PaletteEffects/TintPostProcessEffect.cs new file mode 100644 index 0000000000..1b27d1f80d --- /dev/null +++ b/OpenRA.Mods.Common/Traits/PaletteEffects/TintPostProcessEffect.cs @@ -0,0 +1,51 @@ +#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 OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("Used for day/night effects.")] + [TraitLocation(SystemActors.World | SystemActors.EditorWorld)] + public class TintPostProcessEffectInfo : TraitInfo, ILobbyCustomRulesIgnore + { + public readonly float Red = 1f; + public readonly float Green = 1f; + public readonly float Blue = 1f; + public readonly float Ambient = 1f; + + public override object Create(ActorInitializer init) { return new TintPostProcessEffect(this); } + } + + public class TintPostProcessEffect : RenderPostProcessPassBase + { + public float Red; + public float Green; + public float Blue; + public float Ambient; + + public TintPostProcessEffect(TintPostProcessEffectInfo info) + : base("tint", PostProcessPassType.AfterActors) + { + Red = info.Red; + Green = info.Green; + Blue = info.Blue; + Ambient = info.Ambient; + } + + protected override bool Enabled => true; + protected override void PrepareRender(WorldRenderer wr, IShader shader) + { + shader.SetVec("Tint", Ambient * Red, Ambient * Green, Ambient * Blue); + } + } +} diff --git a/OpenRA.Mods.Common/UpdateRules/Rules/20230801/ReplacePaletteModifiers.cs b/OpenRA.Mods.Common/UpdateRules/Rules/20230801/ReplacePaletteModifiers.cs index f23c878e79..fba6cc6b3d 100644 --- a/OpenRA.Mods.Common/UpdateRules/Rules/20230801/ReplacePaletteModifiers.cs +++ b/OpenRA.Mods.Common/UpdateRules/Rules/20230801/ReplacePaletteModifiers.cs @@ -20,13 +20,15 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules public override string Description => "MenuPaletteEffect is renamed to MenuPostProcessEffect\n" + "ChronoshiftPaletteEffect is renamed to ChronoshiftPostProcessEffect\n" + - "FlashPaletteEffect is renamed to FlashPostProcessEffect"; + "FlashPaletteEffect is renamed to FlashPostProcessEffect\n" + + "GlobalLightingPaletteEffect is renamed to TintPostProcessEffect"; public override IEnumerable UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode) { actorNode.RenameChildrenMatching("MenuPaletteEffect", "MenuPostProcessEffect"); actorNode.RenameChildrenMatching("ChronoshiftPaletteEffect", "ChronoshiftPostProcessEffect"); actorNode.RenameChildrenMatching("FlashPaletteEffect", "FlashPostProcessEffect"); + actorNode.RenameChildrenMatching("GlobalLightingPaletteEffect", "TintPostProcessEffect"); yield break; } diff --git a/glsl/postprocess_tint.frag b/glsl/postprocess_tint.frag new file mode 100644 index 0000000000..0bc0d96656 --- /dev/null +++ b/glsl/postprocess_tint.frag @@ -0,0 +1,30 @@ +#version {VERSION} +#ifdef GL_ES +precision mediump float; +#endif + +uniform vec3 Tint; +uniform sampler2D WorldTexture; + +#if __VERSION__ == 120 +uniform vec2 WorldTextureSize; +#else +out vec4 fragColor; +#endif + +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 = vec4(min(c.r * Tint.r, 1.0), min(c.g * Tint.g, 1.0), min(c.b * Tint.b, 1.0), c.a); + + #if __VERSION__ == 120 + gl_FragColor = c; + #else + fragColor = c; + #endif +} diff --git a/mods/cnc/maps/desert-rats-cnc/rules.yaml b/mods/cnc/maps/desert-rats-cnc/rules.yaml index 90138dffcd..cc376d1fc2 100644 --- a/mods/cnc/maps/desert-rats-cnc/rules.yaml +++ b/mods/cnc/maps/desert-rats-cnc/rules.yaml @@ -1,5 +1,5 @@ World: - GlobalLightingPaletteEffect: + TintPostProcessEffect: Red: 1.1 Green: 0.95 Blue: 1.051 diff --git a/mods/cnc/maps/gdi06/rules.yaml b/mods/cnc/maps/gdi06/rules.yaml index 2ccae05d42..d4536b34a5 100644 --- a/mods/cnc/maps/gdi06/rules.yaml +++ b/mods/cnc/maps/gdi06/rules.yaml @@ -16,7 +16,7 @@ World: ParticleColors: 304074, 28386C, 202C60, 182C54 LineTailAlphaValue: 150 ParticleSize: 1, 1 - GlobalLightingPaletteEffect: + TintPostProcessEffect: Red: 0.75 Green: 0.85 Blue: 1.5 diff --git a/mods/ra/maps/a-nuclear-winter/rules.yaml b/mods/ra/maps/a-nuclear-winter/rules.yaml index 7eb2868642..2e2a22e74b 100644 --- a/mods/ra/maps/a-nuclear-winter/rules.yaml +++ b/mods/ra/maps/a-nuclear-winter/rules.yaml @@ -7,7 +7,7 @@ World: ScatterDirection: -1, 1 ParticleColors: ECECEC, E4E4E4, D0D0D0, BCBCBC LineTailAlphaValue: 0 - GlobalLightingPaletteEffect: + TintPostProcessEffect: Red: 0.88 Green: 0.93 Blue: 1.06 diff --git a/mods/ra/maps/chernobyl/rules.yaml b/mods/ra/maps/chernobyl/rules.yaml index f288694142..702d5c503d 100644 --- a/mods/ra/maps/chernobyl/rules.yaml +++ b/mods/ra/maps/chernobyl/rules.yaml @@ -1,5 +1,5 @@ World: - GlobalLightingPaletteEffect: + TintPostProcessEffect: Red: 1 Green: 0.90 Blue: 0.83 diff --git a/mods/ra/maps/desert-rats/rules.yaml b/mods/ra/maps/desert-rats/rules.yaml index 1e030c185e..777354a402 100644 --- a/mods/ra/maps/desert-rats/rules.yaml +++ b/mods/ra/maps/desert-rats/rules.yaml @@ -1,5 +1,5 @@ World: - GlobalLightingPaletteEffect: + TintPostProcessEffect: Red: 1.1 Green: 0.92 Blue: 1.051 diff --git a/mods/ra/maps/fort-lonestar/rules.yaml b/mods/ra/maps/fort-lonestar/rules.yaml index 304e04bc80..794fe6cc3d 100644 --- a/mods/ra/maps/fort-lonestar/rules.yaml +++ b/mods/ra/maps/fort-lonestar/rules.yaml @@ -17,7 +17,7 @@ World: ParticleColors: 304074, 28386C, 202C60, 182C54 LineTailAlphaValue: 150 ParticleSize: 1, 1 - GlobalLightingPaletteEffect: + TintPostProcessEffect: Red: 0.75 Green: 0.85 Blue: 1.5 diff --git a/mods/ra/maps/infiltration/rules.yaml b/mods/ra/maps/infiltration/rules.yaml index 3df3b68c6a..96cc63aba8 100644 --- a/mods/ra/maps/infiltration/rules.yaml +++ b/mods/ra/maps/infiltration/rules.yaml @@ -1,5 +1,5 @@ World: - GlobalLightingPaletteEffect@HAZE: + TintPostProcessEffect@HAZE: Red: 1 Green: 0.55 Blue: 0 diff --git a/mods/ra/maps/ritual-circle.oramap b/mods/ra/maps/ritual-circle.oramap index 9d7054e8dc..3112cdd55e 100644 Binary files a/mods/ra/maps/ritual-circle.oramap and b/mods/ra/maps/ritual-circle.oramap differ diff --git a/mods/ra/maps/shattered-mountain/rules.yaml b/mods/ra/maps/shattered-mountain/rules.yaml index fa53327a7f..f2f5e07787 100644 --- a/mods/ra/maps/shattered-mountain/rules.yaml +++ b/mods/ra/maps/shattered-mountain/rules.yaml @@ -7,7 +7,7 @@ World: ParticleDensityFactor: 8 ParticleColors: ECECEC, E4E4E4, D0D0D0, BCBCBC LineTailAlphaValue: 0 - GlobalLightingPaletteEffect: + TintPostProcessEffect: Red: 0.88 Green: 0.92 Blue: 1.06 diff --git a/mods/ra/maps/snow-town/rules.yaml b/mods/ra/maps/snow-town/rules.yaml index 0c568b5b2e..ce813387b1 100644 --- a/mods/ra/maps/snow-town/rules.yaml +++ b/mods/ra/maps/snow-town/rules.yaml @@ -5,7 +5,7 @@ World: UseSquares: true ParticleColors: ECECEC, E4E4E4, D0D0D0, BCBCBC LineTailAlphaValue: 0 - GlobalLightingPaletteEffect: + TintPostProcessEffect: Red: 0.9 Green: 0.9 Blue: 1.0