diff --git a/OpenRA.Game/Graphics/Animation.cs b/OpenRA.Game/Graphics/Animation.cs index a78fa3426f..b029903369 100644 --- a/OpenRA.Game/Graphics/Animation.cs +++ b/OpenRA.Game/Graphics/Animation.cs @@ -54,12 +54,12 @@ namespace OpenRA.Graphics public IRenderable[] Render(WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale) { - var imageRenderable = new SpriteRenderable(Image, pos, offset, CurrentSequence.ZOffset + zOffset, palette, scale, IsDecoration); + var imageRenderable = new SpriteRenderable(Image, pos, offset, CurrentSequence.ZOffset + zOffset, palette, scale, IsDecoration, CurrentSequence.IgnoreWorldTint); if (CurrentSequence.ShadowStart >= 0) { var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc()); - var shadowRenderable = new SpriteRenderable(shadow, pos, offset, CurrentSequence.ShadowZOffset + zOffset, palette, scale, true); + var shadowRenderable = new SpriteRenderable(shadow, pos, offset, CurrentSequence.ShadowZOffset + zOffset, palette, scale, true, CurrentSequence.IgnoreWorldTint); return new IRenderable[] { shadowRenderable, imageRenderable }; } diff --git a/OpenRA.Game/Graphics/Renderable.cs b/OpenRA.Game/Graphics/Renderable.cs index b18328123d..819602581b 100644 --- a/OpenRA.Game/Graphics/Renderable.cs +++ b/OpenRA.Game/Graphics/Renderable.cs @@ -28,6 +28,11 @@ namespace OpenRA.Graphics IFinalizedRenderable PrepareRender(WorldRenderer wr); } + public interface ITintableRenderable + { + IRenderable WithTint(float3 newTint); + } + public interface IFinalizedRenderable { void Render(WorldRenderer wr); diff --git a/OpenRA.Game/Graphics/RgbaSpriteRenderer.cs b/OpenRA.Game/Graphics/RgbaSpriteRenderer.cs index eb0d1d03ae..21f1b980c9 100644 --- a/OpenRA.Game/Graphics/RgbaSpriteRenderer.cs +++ b/OpenRA.Game/Graphics/RgbaSpriteRenderer.cs @@ -45,5 +45,21 @@ namespace OpenRA.Graphics parent.DrawSprite(s, a, b, c, d); } + + public void DrawSpriteWithTint(Sprite s, float3 location, float3 size, float3 tint) + { + if (s.Channel != TextureChannel.RGBA) + throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite."); + + parent.DrawSpriteWithTint(s, location, 0, size, tint); + } + + public void DrawSpriteWithTint(Sprite s, float3 a, float3 b, float3 c, float3 d, float3 tint) + { + if (s.Channel != TextureChannel.RGBA) + throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite."); + + parent.DrawSpriteWithTint(s, a, b, c, d, tint); + } } } diff --git a/OpenRA.Game/Graphics/SequenceProvider.cs b/OpenRA.Game/Graphics/SequenceProvider.cs index 3a8f872ea7..0ee4698eac 100644 --- a/OpenRA.Game/Graphics/SequenceProvider.cs +++ b/OpenRA.Game/Graphics/SequenceProvider.cs @@ -32,6 +32,7 @@ namespace OpenRA.Graphics int ShadowZOffset { get; } int[] Frames { get; } Rectangle Bounds { get; } + bool IgnoreWorldTint { get; } Sprite GetSprite(int frame); Sprite GetSprite(int frame, WAngle facing); diff --git a/OpenRA.Game/Graphics/SpriteRenderable.cs b/OpenRA.Game/Graphics/SpriteRenderable.cs index 3a72b4ad27..6f228860bd 100644 --- a/OpenRA.Game/Graphics/SpriteRenderable.cs +++ b/OpenRA.Game/Graphics/SpriteRenderable.cs @@ -14,7 +14,7 @@ using OpenRA.Primitives; namespace OpenRA.Graphics { - public struct SpriteRenderable : IRenderable, IFinalizedRenderable + public struct SpriteRenderable : IRenderable, ITintableRenderable, IFinalizedRenderable { public static readonly IEnumerable None = new IRenderable[0]; @@ -24,9 +24,17 @@ namespace OpenRA.Graphics readonly int zOffset; readonly PaletteReference palette; readonly float scale; + readonly float3 tint; readonly bool isDecoration; + readonly bool ignoreWorldTint; public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, bool isDecoration) + : this(sprite, pos, offset, zOffset, palette, scale, float3.Ones, isDecoration, false) { } + + public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, bool isDecoration, bool ignoreWorldTint) + : this(sprite, pos, offset, zOffset, palette, scale, float3.Ones, isDecoration, ignoreWorldTint) { } + + public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, float3 tint, bool isDecoration, bool ignoreWorldTint) { this.sprite = sprite; this.pos = pos; @@ -34,7 +42,9 @@ namespace OpenRA.Graphics this.zOffset = zOffset; this.palette = palette; this.scale = scale; + this.tint = tint; this.isDecoration = isDecoration; + this.ignoreWorldTint = ignoreWorldTint; } public WPos Pos { get { return pos + offset; } } @@ -43,10 +53,12 @@ namespace OpenRA.Graphics public int ZOffset { get { return zOffset; } } public bool IsDecoration { get { return isDecoration; } } - public IRenderable WithPalette(PaletteReference newPalette) { return new SpriteRenderable(sprite, pos, offset, zOffset, newPalette, scale, isDecoration); } - public IRenderable WithZOffset(int newOffset) { return new SpriteRenderable(sprite, pos, offset, newOffset, palette, scale, isDecoration); } - public IRenderable OffsetBy(WVec vec) { return new SpriteRenderable(sprite, pos + vec, offset, zOffset, palette, scale, isDecoration); } - public IRenderable AsDecoration() { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, true); } + public IRenderable WithPalette(PaletteReference newPalette) { return new SpriteRenderable(sprite, pos, offset, zOffset, newPalette, scale, tint, isDecoration, ignoreWorldTint); } + public IRenderable WithZOffset(int newOffset) { return new SpriteRenderable(sprite, pos, offset, newOffset, palette, scale, tint, isDecoration, ignoreWorldTint); } + public IRenderable OffsetBy(WVec vec) { return new SpriteRenderable(sprite, pos + vec, offset, zOffset, palette, scale, tint, isDecoration, ignoreWorldTint); } + public IRenderable AsDecoration() { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, tint, true, ignoreWorldTint); } + + public IRenderable WithTint(float3 newTint) { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, newTint, isDecoration, ignoreWorldTint); } float3 ScreenPosition(WorldRenderer wr) { @@ -59,7 +71,11 @@ namespace OpenRA.Graphics public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; } public void Render(WorldRenderer wr) { - Game.Renderer.WorldSpriteRenderer.DrawSprite(sprite, ScreenPosition(wr), palette, scale * sprite.Size); + var wsr = Game.Renderer.WorldSpriteRenderer; + if (ignoreWorldTint) + wsr.DrawSprite(sprite, ScreenPosition(wr), palette, scale * sprite.Size); + else + wsr.DrawSpriteWithTint(sprite, ScreenPosition(wr), palette, scale * sprite.Size, tint); } public void RenderDebugGeometry(WorldRenderer wr) diff --git a/OpenRA.Game/Graphics/SpriteRenderer.cs b/OpenRA.Game/Graphics/SpriteRenderer.cs index 5ff9c5fe25..659df9244a 100644 --- a/OpenRA.Game/Graphics/SpriteRenderer.cs +++ b/OpenRA.Game/Graphics/SpriteRenderer.cs @@ -107,7 +107,7 @@ namespace OpenRA.Graphics internal void DrawSprite(Sprite s, float3 location, float paletteTextureIndex, float3 size) { var samplers = SetRenderStateForSprite(s); - Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, samplers, paletteTextureIndex, nv, size); + Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, samplers, paletteTextureIndex, nv, size, float3.Ones); nv += 6; } @@ -124,7 +124,26 @@ namespace OpenRA.Graphics public void DrawSprite(Sprite s, float3 a, float3 b, float3 c, float3 d) { var samplers = SetRenderStateForSprite(s); - Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, nv); + Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, float3.Ones, nv); + nv += 6; + } + + internal void DrawSpriteWithTint(Sprite s, float3 location, float paletteTextureIndex, float3 size, float3 tint) + { + var samplers = SetRenderStateForSprite(s); + Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, samplers, paletteTextureIndex, nv, size, tint); + nv += 6; + } + + public void DrawSpriteWithTint(Sprite s, float3 location, PaletteReference pal, float3 size, float3 tint) + { + DrawSpriteWithTint(s, location, pal.TextureIndex, size, tint); + } + + public void DrawSpriteWithTint(Sprite s, float3 a, float3 b, float3 c, float3 d, float3 tint) + { + var samplers = SetRenderStateForSprite(s); + Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, tint, nv); nv += 6; } diff --git a/OpenRA.Game/Graphics/TerrainSpriteLayer.cs b/OpenRA.Game/Graphics/TerrainSpriteLayer.cs index c6b6b3637c..e158321704 100644 --- a/OpenRA.Game/Graphics/TerrainSpriteLayer.cs +++ b/OpenRA.Game/Graphics/TerrainSpriteLayer.cs @@ -59,7 +59,7 @@ namespace OpenRA.Graphics for (var i = 0; i < vertices.Length; i++) { var v = vertices[i]; - vertices[i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C); + vertices[i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C, new float3(v.R, v.G, v.B)); } for (var row = 0; row < map.MapSize.Y; row++) @@ -96,7 +96,7 @@ namespace OpenRA.Graphics return; var offset = rowStride * uv.V + 6 * uv.U; - Util.FastCreateQuad(vertices, pos, sprite, int2.Zero, palette.TextureIndex, offset, sprite.Size); + Util.FastCreateQuad(vertices, pos, sprite, int2.Zero, palette.TextureIndex, offset, sprite.Size, float3.Ones); dirtyRows.Add(uv.V); } diff --git a/OpenRA.Game/Graphics/Util.cs b/OpenRA.Game/Graphics/Util.cs index 123b2a4eab..eba67ad7d3 100644 --- a/OpenRA.Game/Graphics/Util.cs +++ b/OpenRA.Game/Graphics/Util.cs @@ -20,15 +20,18 @@ namespace OpenRA.Graphics // yes, our channel order is nuts. static readonly int[] ChannelMasks = { 2, 1, 0, 3 }; - public static void FastCreateQuad(Vertex[] vertices, float3 o, Sprite r, int2 samplers, float paletteTextureIndex, int nv, float3 size) + public static void FastCreateQuad(Vertex[] vertices, float3 o, Sprite r, int2 samplers, float paletteTextureIndex, int nv, float3 size, float3 tint) { var b = new float3(o.X + size.X, o.Y, o.Z); var c = new float3(o.X + size.X, o.Y + size.Y, o.Z + size.Z); var d = new float3(o.X, o.Y + size.Y, o.Z + size.Z); - FastCreateQuad(vertices, o, b, c, d, r, samplers, paletteTextureIndex, nv); + FastCreateQuad(vertices, o, b, c, d, r, samplers, paletteTextureIndex, tint, nv); } - public static void FastCreateQuad(Vertex[] vertices, float3 a, float3 b, float3 c, float3 d, Sprite r, int2 samplers, float paletteTextureIndex, int nv) + public static void FastCreateQuad(Vertex[] vertices, + float3 a, float3 b, float3 c, float3 d, + Sprite r, int2 samplers, float paletteTextureIndex, + float3 tint, int nv) { float sl = 0; float st = 0; @@ -51,12 +54,12 @@ namespace OpenRA.Graphics } var fAttribC = (float)attribC; - vertices[nv] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC); - vertices[nv + 1] = new Vertex(b, r.Right, r.Top, sr, st, paletteTextureIndex, fAttribC); - vertices[nv + 2] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC); - vertices[nv + 3] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC); - vertices[nv + 4] = new Vertex(d, r.Left, r.Bottom, sl, sb, paletteTextureIndex, fAttribC); - vertices[nv + 5] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC); + vertices[nv] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC, tint); + vertices[nv + 1] = new Vertex(b, r.Right, r.Top, sr, st, paletteTextureIndex, fAttribC, tint); + vertices[nv + 2] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC, tint); + vertices[nv + 3] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC, tint); + vertices[nv + 4] = new Vertex(d, r.Left, r.Bottom, sl, sb, paletteTextureIndex, fAttribC, tint); + vertices[nv + 5] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC, tint); } public static void FastCopyIntoChannel(Sprite dest, byte[] src) diff --git a/OpenRA.Game/Graphics/Vertex.cs b/OpenRA.Game/Graphics/Vertex.cs index 004f892012..a434a13872 100644 --- a/OpenRA.Game/Graphics/Vertex.cs +++ b/OpenRA.Game/Graphics/Vertex.cs @@ -16,17 +16,34 @@ namespace OpenRA.Graphics [StructLayout(LayoutKind.Sequential)] public struct Vertex { - public readonly float X, Y, Z, S, T, U, V, P, C; + // 3d position + public readonly float X, Y, Z; + + // Primary and secondary texture coordinates or RGBA color + public readonly float S, T, U, V; + + // Palette and channel flags + public readonly float P, C; + + // Color tint + public readonly float R, G, B; public Vertex(float3 xyz, float s, float t, float u, float v, float p, float c) - : this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c) { } + : this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c, float3.Ones) { } - public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c) + public Vertex(float3 xyz, float s, float t, float u, float v, float p, float c, float3 tint) + : this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c, tint.X, tint.Y, tint.Z) { } + + public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c, float3 tint) + : this(x, y, z, s, t, u, v, p, c, tint.X, tint.Y, tint.Z) { } + + public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c, float r, float g, float b) { X = x; Y = y; Z = z; S = s; T = t; U = u; V = v; P = p; C = c; + R = r; G = g; B = b; } } } diff --git a/OpenRA.Game/Primitives/float3.cs b/OpenRA.Game/Primitives/float3.cs index be05134b58..9e20a35735 100644 --- a/OpenRA.Game/Primitives/float3.cs +++ b/OpenRA.Game/Primitives/float3.cs @@ -62,5 +62,6 @@ namespace OpenRA public override string ToString() { return "{0},{1},{2}".F(X, Y, Z); } public static readonly float3 Zero = new float3(0, 0, 0); + public static readonly float3 Ones = new float3(1, 1, 1); } } diff --git a/OpenRA.Mods.Cnc/Graphics/TeslaZapRenderable.cs b/OpenRA.Mods.Cnc/Graphics/TeslaZapRenderable.cs index 7096f85b2b..b21e1973c4 100644 --- a/OpenRA.Mods.Cnc/Graphics/TeslaZapRenderable.cs +++ b/OpenRA.Mods.Cnc/Graphics/TeslaZapRenderable.cs @@ -148,7 +148,7 @@ namespace OpenRA.Mods.Cnc.Graphics .MinBy(t => Math.Abs(float2.Dot(z + new float2(t[0], t[1]), q) + c)); var pos = wr.ProjectedPosition((z + new float2(step[2], step[3])).ToInt2()); - rs.Add(new SpriteRenderable(s.GetSprite(step[4]), pos, WVec.Zero, 0, pal, 1f, true).PrepareRender(wr)); + rs.Add(new SpriteRenderable(s.GetSprite(step[4]), pos, WVec.Zero, 0, pal, 1f, true, s.IgnoreWorldTint).PrepareRender(wr)); z += new float2(step[0], step[1]); if (rs.Count >= 1000) diff --git a/OpenRA.Mods.Cnc/Traits/Minelayer.cs b/OpenRA.Mods.Cnc/Traits/Minelayer.cs index 1c7d7911c4..266754a0e9 100644 --- a/OpenRA.Mods.Cnc/Traits/Minelayer.cs +++ b/OpenRA.Mods.Cnc/Traits/Minelayer.cs @@ -244,8 +244,7 @@ namespace OpenRA.Mods.Cnc.Traits else if (!movement.CanEnterCell(c, null, BlockedByActor.Immovable) || (mobile != null && !mobile.CanStayInCell(c))) tile = tileBlocked; - yield return new SpriteRenderable(tile, world.Map.CenterOfCell(c), - WVec.Zero, -511, pal, 1f, true); + yield return new SpriteRenderable(tile, world.Map.CenterOfCell(c), WVec.Zero, -511, pal, 1f, true, true); } } diff --git a/OpenRA.Mods.Cnc/Traits/SupportPowers/ChronoshiftPower.cs b/OpenRA.Mods.Cnc/Traits/SupportPowers/ChronoshiftPower.cs index 3dabafa4d6..78db83a5bd 100644 --- a/OpenRA.Mods.Cnc/Traits/SupportPowers/ChronoshiftPower.cs +++ b/OpenRA.Mods.Cnc/Traits/SupportPowers/ChronoshiftPower.cs @@ -205,7 +205,7 @@ namespace OpenRA.Mods.Cnc.Traits var tiles = power.CellsMatching(xy, footprint, dimensions); var palette = wr.Palette(((ChronoshiftPowerInfo)power.Info).TargetOverlayPalette); foreach (var t in tiles) - yield return new SpriteRenderable(tile, wr.World.Map.CenterOfCell(t), WVec.Zero, -511, palette, 1f, true); + yield return new SpriteRenderable(tile, wr.World.Map.CenterOfCell(t), WVec.Zero, -511, palette, 1f, true, true); } protected override string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi) @@ -290,7 +290,7 @@ namespace OpenRA.Mods.Cnc.Traits foreach (var t in power.CellsMatching(sourceLocation, footprint, dimensions)) { var tile = manager.Self.Owner.Shroud.IsExplored(t + delta) ? validTile : invalidTile; - yield return new SpriteRenderable(tile, wr.World.Map.CenterOfCell(t + delta), WVec.Zero, -511, palette, 1f, true); + yield return new SpriteRenderable(tile, wr.World.Map.CenterOfCell(t + delta), WVec.Zero, -511, palette, 1f, true, true); } // Unit previews @@ -302,7 +302,7 @@ namespace OpenRA.Mods.Cnc.Traits var canEnter = manager.Self.Owner.Shroud.IsExplored(targetCell) && unit.Trait().CanChronoshiftTo(unit, targetCell); var tile = canEnter ? validTile : invalidTile; - yield return new SpriteRenderable(tile, wr.World.Map.CenterOfCell(targetCell), WVec.Zero, -511, palette, 1f, true); + yield return new SpriteRenderable(tile, wr.World.Map.CenterOfCell(targetCell), WVec.Zero, -511, palette, 1f, true, true); } var offset = world.Map.CenterOfCell(xy) - world.Map.CenterOfCell(sourceLocation); @@ -332,7 +332,7 @@ namespace OpenRA.Mods.Cnc.Traits // Source tiles foreach (var t in power.CellsMatching(sourceLocation, footprint, dimensions)) - yield return new SpriteRenderable(sourceTile, wr.World.Map.CenterOfCell(t), WVec.Zero, -511, palette, 1f, true); + yield return new SpriteRenderable(sourceTile, wr.World.Map.CenterOfCell(t), WVec.Zero, -511, palette, 1f, true, true); } bool IsValidTarget(CPos xy) diff --git a/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs b/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs index 8449772299..988854c2bb 100644 --- a/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs +++ b/OpenRA.Mods.Common/Graphics/DefaultSpriteSequence.cs @@ -112,6 +112,7 @@ namespace OpenRA.Mods.Common.Graphics public int ShadowZOffset { get; private set; } public int[] Frames { get; private set; } public Rectangle Bounds { get; private set; } + public bool IgnoreWorldTint { get; private set; } public readonly uint[] EmbeddedPalette; @@ -156,6 +157,7 @@ namespace OpenRA.Mods.Common.Graphics Tick = LoadField(d, "Tick", 40); transpose = LoadField(d, "Transpose", false); Frames = LoadField(d, "Frames", null); + IgnoreWorldTint = LoadField(d, "IgnoreWorldTint", false); var flipX = LoadField(d, "FlipX", false); var flipY = LoadField(d, "FlipY", false); diff --git a/OpenRA.Mods.Common/Graphics/ModelRenderable.cs b/OpenRA.Mods.Common/Graphics/ModelRenderable.cs index df94473b8a..9a4eaf56ab 100644 --- a/OpenRA.Mods.Common/Graphics/ModelRenderable.cs +++ b/OpenRA.Mods.Common/Graphics/ModelRenderable.cs @@ -17,7 +17,7 @@ using OpenRA.Primitives; namespace OpenRA.Mods.Common.Graphics { - public struct ModelRenderable : IRenderable + public struct ModelRenderable : IRenderable, ITintableRenderable { readonly IEnumerable models; readonly WPos pos; @@ -30,11 +30,22 @@ namespace OpenRA.Mods.Common.Graphics readonly PaletteReference normalsPalette; readonly PaletteReference shadowPalette; readonly float scale; + readonly float3 tint; public ModelRenderable( IEnumerable models, WPos pos, int zOffset, WRot camera, float scale, WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor, PaletteReference color, PaletteReference normals, PaletteReference shadow) + : this(models, pos, zOffset, camera, scale, + lightSource, lightAmbientColor, lightDiffuseColor, + color, normals, shadow, + float3.Ones) { } + + public ModelRenderable( + IEnumerable models, WPos pos, int zOffset, WRot camera, float scale, + WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor, + PaletteReference color, PaletteReference normals, PaletteReference shadow, + float3 tint) { this.models = models; this.pos = pos; @@ -47,6 +58,7 @@ namespace OpenRA.Mods.Common.Graphics palette = color; normalsPalette = normals; shadowPalette = shadow; + this.tint = tint; } public WPos Pos { get { return pos; } } @@ -59,7 +71,7 @@ namespace OpenRA.Mods.Common.Graphics return new ModelRenderable( models, pos, zOffset, camera, scale, lightSource, lightAmbientColor, lightDiffuseColor, - newPalette, normalsPalette, shadowPalette); + newPalette, normalsPalette, shadowPalette, tint); } public IRenderable WithZOffset(int newOffset) @@ -67,7 +79,7 @@ namespace OpenRA.Mods.Common.Graphics return new ModelRenderable( models, pos, newOffset, camera, scale, lightSource, lightAmbientColor, lightDiffuseColor, - palette, normalsPalette, shadowPalette); + palette, normalsPalette, shadowPalette, tint); } public IRenderable OffsetBy(WVec vec) @@ -75,11 +87,19 @@ namespace OpenRA.Mods.Common.Graphics return new ModelRenderable( models, pos + vec, zOffset, camera, scale, lightSource, lightAmbientColor, lightDiffuseColor, - palette, normalsPalette, shadowPalette); + palette, normalsPalette, shadowPalette, tint); } public IRenderable AsDecoration() { return this; } + public IRenderable WithTint(float3 newTint) + { + return new ModelRenderable( + models, pos, zOffset, camera, scale, + lightSource, lightAmbientColor, lightDiffuseColor, + palette, normalsPalette, shadowPalette, newTint); + } + // This will need generalizing once we support TS/RA2 terrain static readonly float[] GroundNormal = new float[] { 0, 0, 1, 1 }; public IFinalizedRenderable PrepareRender(WorldRenderer wr) @@ -122,8 +142,11 @@ namespace OpenRA.Mods.Common.Graphics var sb = shadowOrigin + psb[2]; var sc = shadowOrigin + psb[1]; var sd = shadowOrigin + psb[3]; - Game.Renderer.WorldRgbaSpriteRenderer.DrawSprite(renderProxy.ShadowSprite, sa, sb, sc, sd); - Game.Renderer.WorldRgbaSpriteRenderer.DrawSprite(renderProxy.Sprite, pxOrigin - 0.5f * renderProxy.Sprite.Size); + + var wrsr = Game.Renderer.WorldRgbaSpriteRenderer; + var ti = model.tint; + wrsr.DrawSpriteWithTint(renderProxy.ShadowSprite, sa, sb, sc, sd, ti); + wrsr.DrawSpriteWithTint(renderProxy.Sprite, pxOrigin - 0.5f * renderProxy.Sprite.Size, renderProxy.Sprite.Size, ti); } public void RenderDebugGeometry(WorldRenderer wr) diff --git a/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs b/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs index 35af96e428..7f844c12f3 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs @@ -200,7 +200,7 @@ namespace OpenRA.Mods.Common.Traits return footprint.Select(c => (IRenderable)(new SpriteRenderable( wr.Theater.TileSprite(new TerrainTile(template, c.Value)), - wr.World.Map.CenterOfCell(c.Key), WVec.Zero, -offset, palette, 1f, true))).ToArray(); + wr.World.Map.CenterOfCell(c.Key), WVec.Zero, -offset, palette, 1f, true, false))).ToArray(); } bool initialized; diff --git a/OpenRA.Mods.Common/Traits/Buildings/FootprintPlaceBuildingPreview.cs b/OpenRA.Mods.Common/Traits/Buildings/FootprintPlaceBuildingPreview.cs index 302f1dfcb0..6a83edd085 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/FootprintPlaceBuildingPreview.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/FootprintPlaceBuildingPreview.cs @@ -93,7 +93,7 @@ namespace OpenRA.Mods.Common.Traits var pal = HasFlag(c.Value, PlaceBuildingCellType.LineBuild) ? linePalette : cellPalette; var pos = wr.World.Map.CenterOfCell(c.Key); var offset = new WVec(0, 0, topLeftPos.Z - pos.Z); - yield return new SpriteRenderable(tile, pos, offset, -511, pal, 1f, true); + yield return new SpriteRenderable(tile, pos, offset, -511, pal, 1f, true, true); } } diff --git a/OpenRA.Mods.Common/Traits/Render/DrawLineToTarget.cs b/OpenRA.Mods.Common/Traits/Render/DrawLineToTarget.cs index 22151b7a7c..7535d7a498 100644 --- a/OpenRA.Mods.Common/Traits/Render/DrawLineToTarget.cs +++ b/OpenRA.Mods.Common/Traits/Render/DrawLineToTarget.cs @@ -80,7 +80,7 @@ namespace OpenRA.Mods.Common.Traits if (!a.IsCanceling) foreach (var n in a.TargetLineNodes(self)) if (n.Tile != null && n.Target.Type != TargetType.Invalid) - yield return new SpriteRenderable(n.Tile, n.Target.CenterPosition, WVec.Zero, -511, pal, 1f, true); + yield return new SpriteRenderable(n.Tile, n.Target.CenterPosition, WVec.Zero, -511, pal, 1f, true, true); } bool IRenderAboveShroud.SpatiallyPartitionable { get { return false; } } diff --git a/OpenRA.Mods.Common/Traits/Render/WithParachute.cs b/OpenRA.Mods.Common/Traits/Render/WithParachute.cs index c76d9edc84..079ca15e90 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithParachute.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithParachute.cs @@ -177,7 +177,7 @@ namespace OpenRA.Mods.Common.Traits.Render var dat = self.World.Map.DistanceAboveTerrain(self.CenterPosition); var pos = self.CenterPosition - new WVec(0, 0, dat.Length); var palette = wr.Palette(info.ShadowPalette); - return new IRenderable[] { new SpriteRenderable(shadow.Image, pos, info.ShadowOffset, info.ShadowZOffset, palette, 1, true) }; + return new IRenderable[] { new SpriteRenderable(shadow.Image, pos, info.ShadowOffset, info.ShadowZOffset, palette, 1, true, shadow.CurrentSequence.IgnoreWorldTint) }; } IEnumerable IRender.ScreenBounds(Actor self, WorldRenderer wr) diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/GrantExternalConditionPower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/GrantExternalConditionPower.cs index 8540234a09..f737366377 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/GrantExternalConditionPower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/GrantExternalConditionPower.cs @@ -167,7 +167,7 @@ namespace OpenRA.Mods.Common.Traits var pal = wr.Palette(TileSet.TerrainPaletteInternalName); foreach (var t in power.CellsMatching(xy, footprint, dimensions)) - yield return new SpriteRenderable(tile, wr.World.Map.CenterOfCell(t), WVec.Zero, -511, pal, 1f, true); + yield return new SpriteRenderable(tile, wr.World.Map.CenterOfCell(t), WVec.Zero, -511, pal, 1f, true, true); } protected override string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi) diff --git a/OpenRA.Mods.Common/Traits/World/EditorCursorLayer.cs b/OpenRA.Mods.Common/Traits/World/EditorCursorLayer.cs index 9b113fdce1..f0d4b4241d 100644 --- a/OpenRA.Mods.Common/Traits/World/EditorCursorLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/EditorCursorLayer.cs @@ -88,7 +88,7 @@ namespace OpenRA.Mods.Common.Traits var offset = world.Map.Offset(new CVec(x, y), tileInfo.Height); var palette = wr.Palette(TerrainTemplate.Palette ?? TileSet.TerrainPaletteInternalName); - terrainOrResourcePreview.Add(new SpriteRenderable(sprite, pos, offset, 0, palette, 1, false)); + terrainOrResourcePreview.Add(new SpriteRenderable(sprite, pos, offset, 0, palette, 1, false, false)); } } } @@ -99,7 +99,7 @@ namespace OpenRA.Mods.Common.Traits var sprite = sequence.GetSprite(Resource.MaxDensity - 1); var palette = wr.Palette(Resource.Palette); - terrainOrResourcePreview.Add(new SpriteRenderable(sprite, pos, WVec.Zero, 0, palette, 1, false)); + terrainOrResourcePreview.Add(new SpriteRenderable(sprite, pos, WVec.Zero, 0, palette, 1, false, sequence.IgnoreWorldTint)); } } } diff --git a/OpenRA.Mods.Common/Traits/World/EditorSelectionLayer.cs b/OpenRA.Mods.Common/Traits/World/EditorSelectionLayer.cs index a0f118e3c9..71418061d1 100644 --- a/OpenRA.Mods.Common/Traits/World/EditorSelectionLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/EditorSelectionLayer.cs @@ -89,12 +89,12 @@ namespace OpenRA.Mods.Common.Traits if (CopyRegion != null) foreach (var c in CopyRegion) yield return new SpriteRenderable(copySprite, wr.World.Map.CenterOfCell(c), - WVec.Zero, -511, palette, 1f, true); + WVec.Zero, -511, palette, 1f, true, true); if (PasteRegion != null) foreach (var c in PasteRegion) yield return new SpriteRenderable(pasteSprite, wr.World.Map.CenterOfCell(c), - WVec.Zero, -511, palette, 1f, true); + WVec.Zero, -511, palette, 1f, true, true); } bool IRenderAboveShroud.SpatiallyPartitionable { get { return false; } } diff --git a/OpenRA.Mods.D2k/Traits/Buildings/D2kActorPreviewPlaceBuildingPreview.cs b/OpenRA.Mods.D2k/Traits/Buildings/D2kActorPreviewPlaceBuildingPreview.cs index 4d9275713f..858ce441db 100644 --- a/OpenRA.Mods.D2k/Traits/Buildings/D2kActorPreviewPlaceBuildingPreview.cs +++ b/OpenRA.Mods.D2k/Traits/Buildings/D2kActorPreviewPlaceBuildingPreview.cs @@ -82,7 +82,7 @@ namespace OpenRA.Mods.D2k.Traits var pal = HasFlag(c.Value, PlaceBuildingCellType.LineBuild) ? linePalette : cellPalette; var pos = wr.World.Map.CenterOfCell(c.Key); var offset = new WVec(0, 0, topLeftPos.Z - pos.Z); - yield return new SpriteRenderable(tile, pos, offset, -511, pal, 1f, true); + yield return new SpriteRenderable(tile, pos, offset, -511, pal, 1f, true, true); } } } diff --git a/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs b/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs index 2bc18f7211..c855583d3d 100644 --- a/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs +++ b/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs @@ -53,6 +53,8 @@ namespace OpenRA.Platforms.Default OpenGL.CheckGLError(); OpenGL.glEnableVertexAttribArray(Shader.TexMetadataAttributeIndex); OpenGL.CheckGLError(); + OpenGL.glEnableVertexAttribArray(Shader.TintAttributeIndex); + OpenGL.CheckGLError(); } public IVertexBuffer CreateVertexBuffer(int size) diff --git a/OpenRA.Platforms.Default/Shader.cs b/OpenRA.Platforms.Default/Shader.cs index 8e46d5f3f0..6cb1ea804c 100644 --- a/OpenRA.Platforms.Default/Shader.cs +++ b/OpenRA.Platforms.Default/Shader.cs @@ -21,6 +21,7 @@ namespace OpenRA.Platforms.Default public const int VertexPosAttributeIndex = 0; public const int TexCoordAttributeIndex = 1; public const int TexMetadataAttributeIndex = 2; + public const int TintAttributeIndex = 3; readonly Dictionary samplers = new Dictionary(); readonly Dictionary legacySizeUniforms = new Dictionary(); @@ -83,6 +84,8 @@ namespace OpenRA.Platforms.Default OpenGL.CheckGLError(); OpenGL.glBindAttribLocation(program, TexMetadataAttributeIndex, "aVertexTexMetadata"); OpenGL.CheckGLError(); + OpenGL.glBindAttribLocation(program, TintAttributeIndex, "aVertexTint"); + OpenGL.CheckGLError(); if (OpenGL.Profile != GLProfile.Legacy) { diff --git a/OpenRA.Platforms.Default/VertexBuffer.cs b/OpenRA.Platforms.Default/VertexBuffer.cs index 1d624bb3a8..2b54f2ffb3 100644 --- a/OpenRA.Platforms.Default/VertexBuffer.cs +++ b/OpenRA.Platforms.Default/VertexBuffer.cs @@ -101,6 +101,8 @@ namespace OpenRA.Platforms.Default OpenGL.CheckGLError(); OpenGL.glVertexAttribPointer(Shader.TexMetadataAttributeIndex, 2, OpenGL.GL_FLOAT, false, VertexSize, new IntPtr(28)); OpenGL.CheckGLError(); + OpenGL.glVertexAttribPointer(Shader.TintAttributeIndex, 3, OpenGL.GL_FLOAT, false, VertexSize, new IntPtr(36)); + OpenGL.CheckGLError(); } public void Dispose() diff --git a/glsl/combined.frag b/glsl/combined.frag index 4234195965..9c868845de 100644 --- a/glsl/combined.frag +++ b/glsl/combined.frag @@ -26,6 +26,7 @@ varying vec2 vTexSampler; varying vec4 vColorFraction; varying vec4 vRGBAFraction; varying vec4 vPalettedFraction; +varying vec4 vTint; uniform vec2 Texture0Size; uniform vec2 Texture1Size; @@ -46,6 +47,7 @@ in vec2 vTexSampler; in vec4 vColorFraction; in vec4 vRGBAFraction; in vec4 vPalettedFraction; +in vec4 vTint; out vec4 fragColor; #endif @@ -227,8 +229,8 @@ void main() } else #if __VERSION__ == 120 - gl_FragColor = c; + gl_FragColor = c * vTint; #else - fragColor = c; + fragColor = c * vTint; #endif } diff --git a/glsl/combined.vert b/glsl/combined.vert index 374f44f079..d1b30e746a 100644 --- a/glsl/combined.vert +++ b/glsl/combined.vert @@ -7,6 +7,7 @@ uniform vec3 r1, r2; attribute vec4 aVertexPosition; attribute vec4 aVertexTexCoord; attribute vec2 aVertexTexMetadata; +attribute vec3 aVertexTint; varying vec4 vTexCoord; varying vec2 vTexMetadata; @@ -17,10 +18,12 @@ varying vec2 vTexSampler; varying vec4 vColorFraction; varying vec4 vRGBAFraction; varying vec4 vPalettedFraction; +varying vec4 vTint; #else in vec4 aVertexPosition; in vec4 aVertexTexCoord; in vec2 aVertexTexMetadata; +in vec3 aVertexTint; out vec4 vTexCoord; out vec2 vTexMetadata; @@ -31,6 +34,7 @@ out vec2 vTexSampler; out vec4 vColorFraction; out vec4 vRGBAFraction; out vec4 vPalettedFraction; +out vec4 vTint; #endif vec4 UnpackChannelAttributes(float x) @@ -123,4 +127,5 @@ void main() vPalettedFraction = SelectPalettedFraction(attrib.s); vDepthMask = SelectChannelMask(attrib.t); vTexSampler = attrib.pq; + vTint = vec4(aVertexTint, 1.0); } diff --git a/glsl/model.vert b/glsl/model.vert index 16aa02496f..ab111cd9f0 100644 --- a/glsl/model.vert +++ b/glsl/model.vert @@ -7,6 +7,7 @@ uniform mat4 TransformMatrix; attribute vec4 aVertexPosition; attribute vec4 aVertexTexCoord; attribute vec2 aVertexTexMetadata; +attribute vec3 aVertexTint; varying vec4 vTexCoord; varying vec4 vChannelMask; varying vec4 vNormalsMask; @@ -14,6 +15,7 @@ varying vec4 vNormalsMask; in vec4 aVertexPosition; in vec4 aVertexTexCoord; in vec2 aVertexTexMetadata; +in vec3 aVertexTint; out vec4 vTexCoord; out vec4 vChannelMask; out vec4 vNormalsMask;