Add support for rendering tinted artwork.

This commit is contained in:
Paul Chote
2020-07-09 17:40:14 +01:00
committed by abcdefg30
parent baf58f53b3
commit ac7eda8ca2
29 changed files with 167 additions and 49 deletions

View File

@@ -54,12 +54,12 @@ namespace OpenRA.Graphics
public IRenderable[] Render(WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale) 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) if (CurrentSequence.ShadowStart >= 0)
{ {
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc()); 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 }; return new IRenderable[] { shadowRenderable, imageRenderable };
} }

View File

@@ -28,6 +28,11 @@ namespace OpenRA.Graphics
IFinalizedRenderable PrepareRender(WorldRenderer wr); IFinalizedRenderable PrepareRender(WorldRenderer wr);
} }
public interface ITintableRenderable
{
IRenderable WithTint(float3 newTint);
}
public interface IFinalizedRenderable public interface IFinalizedRenderable
{ {
void Render(WorldRenderer wr); void Render(WorldRenderer wr);

View File

@@ -45,5 +45,21 @@ namespace OpenRA.Graphics
parent.DrawSprite(s, a, b, c, d); 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);
}
} }
} }

View File

@@ -32,6 +32,7 @@ namespace OpenRA.Graphics
int ShadowZOffset { get; } int ShadowZOffset { get; }
int[] Frames { get; } int[] Frames { get; }
Rectangle Bounds { get; } Rectangle Bounds { get; }
bool IgnoreWorldTint { get; }
Sprite GetSprite(int frame); Sprite GetSprite(int frame);
Sprite GetSprite(int frame, WAngle facing); Sprite GetSprite(int frame, WAngle facing);

View File

@@ -14,7 +14,7 @@ using OpenRA.Primitives;
namespace OpenRA.Graphics namespace OpenRA.Graphics
{ {
public struct SpriteRenderable : IRenderable, IFinalizedRenderable public struct SpriteRenderable : IRenderable, ITintableRenderable, IFinalizedRenderable
{ {
public static readonly IEnumerable<IRenderable> None = new IRenderable[0]; public static readonly IEnumerable<IRenderable> None = new IRenderable[0];
@@ -24,9 +24,17 @@ namespace OpenRA.Graphics
readonly int zOffset; readonly int zOffset;
readonly PaletteReference palette; readonly PaletteReference palette;
readonly float scale; readonly float scale;
readonly float3 tint;
readonly bool isDecoration; readonly bool isDecoration;
readonly bool ignoreWorldTint;
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, bool isDecoration) 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.sprite = sprite;
this.pos = pos; this.pos = pos;
@@ -34,7 +42,9 @@ namespace OpenRA.Graphics
this.zOffset = zOffset; this.zOffset = zOffset;
this.palette = palette; this.palette = palette;
this.scale = scale; this.scale = scale;
this.tint = tint;
this.isDecoration = isDecoration; this.isDecoration = isDecoration;
this.ignoreWorldTint = ignoreWorldTint;
} }
public WPos Pos { get { return pos + offset; } } public WPos Pos { get { return pos + offset; } }
@@ -43,10 +53,12 @@ namespace OpenRA.Graphics
public int ZOffset { get { return zOffset; } } public int ZOffset { get { return zOffset; } }
public bool IsDecoration { get { return isDecoration; } } public bool IsDecoration { get { return isDecoration; } }
public IRenderable WithPalette(PaletteReference newPalette) { return new SpriteRenderable(sprite, pos, offset, zOffset, newPalette, scale, isDecoration); } 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, isDecoration); } 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, isDecoration); } 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, true); } 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) float3 ScreenPosition(WorldRenderer wr)
{ {
@@ -59,7 +71,11 @@ namespace OpenRA.Graphics
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; } public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
public void Render(WorldRenderer wr) 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) public void RenderDebugGeometry(WorldRenderer wr)

View File

@@ -107,7 +107,7 @@ namespace OpenRA.Graphics
internal void DrawSprite(Sprite s, float3 location, float paletteTextureIndex, float3 size) internal void DrawSprite(Sprite s, float3 location, float paletteTextureIndex, float3 size)
{ {
var samplers = SetRenderStateForSprite(s); 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; nv += 6;
} }
@@ -124,7 +124,26 @@ namespace OpenRA.Graphics
public void DrawSprite(Sprite s, float3 a, float3 b, float3 c, float3 d) public void DrawSprite(Sprite s, float3 a, float3 b, float3 c, float3 d)
{ {
var samplers = SetRenderStateForSprite(s); 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; nv += 6;
} }

View File

@@ -59,7 +59,7 @@ namespace OpenRA.Graphics
for (var i = 0; i < vertices.Length; i++) for (var i = 0; i < vertices.Length; i++)
{ {
var v = vertices[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++) for (var row = 0; row < map.MapSize.Y; row++)
@@ -96,7 +96,7 @@ namespace OpenRA.Graphics
return; return;
var offset = rowStride * uv.V + 6 * uv.U; 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); dirtyRows.Add(uv.V);
} }

View File

@@ -20,15 +20,18 @@ namespace OpenRA.Graphics
// yes, our channel order is nuts. // yes, our channel order is nuts.
static readonly int[] ChannelMasks = { 2, 1, 0, 3 }; 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 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 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); 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 sl = 0;
float st = 0; float st = 0;
@@ -51,12 +54,12 @@ namespace OpenRA.Graphics
} }
var fAttribC = (float)attribC; var fAttribC = (float)attribC;
vertices[nv] = 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); 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); 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); 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); 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); vertices[nv + 5] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC, tint);
} }
public static void FastCopyIntoChannel(Sprite dest, byte[] src) public static void FastCopyIntoChannel(Sprite dest, byte[] src)

View File

@@ -16,17 +16,34 @@ namespace OpenRA.Graphics
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct Vertex 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) 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; X = x; Y = y; Z = z;
S = s; T = t; S = s; T = t;
U = u; V = v; U = u; V = v;
P = p; C = c; P = p; C = c;
R = r; G = g; B = b;
} }
} }
} }

View File

@@ -62,5 +62,6 @@ namespace OpenRA
public override string ToString() { return "{0},{1},{2}".F(X, Y, Z); } 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 Zero = new float3(0, 0, 0);
public static readonly float3 Ones = new float3(1, 1, 1);
} }
} }

View File

@@ -148,7 +148,7 @@ namespace OpenRA.Mods.Cnc.Graphics
.MinBy(t => Math.Abs(float2.Dot(z + new float2(t[0], t[1]), q) + c)); .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()); 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]); z += new float2(step[0], step[1]);
if (rs.Count >= 1000) if (rs.Count >= 1000)

View File

@@ -244,8 +244,7 @@ namespace OpenRA.Mods.Cnc.Traits
else if (!movement.CanEnterCell(c, null, BlockedByActor.Immovable) || (mobile != null && !mobile.CanStayInCell(c))) else if (!movement.CanEnterCell(c, null, BlockedByActor.Immovable) || (mobile != null && !mobile.CanStayInCell(c)))
tile = tileBlocked; tile = tileBlocked;
yield return new SpriteRenderable(tile, world.Map.CenterOfCell(c), yield return new SpriteRenderable(tile, world.Map.CenterOfCell(c), WVec.Zero, -511, pal, 1f, true, true);
WVec.Zero, -511, pal, 1f, true);
} }
} }

View File

@@ -205,7 +205,7 @@ namespace OpenRA.Mods.Cnc.Traits
var tiles = power.CellsMatching(xy, footprint, dimensions); var tiles = power.CellsMatching(xy, footprint, dimensions);
var palette = wr.Palette(((ChronoshiftPowerInfo)power.Info).TargetOverlayPalette); var palette = wr.Palette(((ChronoshiftPowerInfo)power.Info).TargetOverlayPalette);
foreach (var t in tiles) 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) 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)) foreach (var t in power.CellsMatching(sourceLocation, footprint, dimensions))
{ {
var tile = manager.Self.Owner.Shroud.IsExplored(t + delta) ? validTile : invalidTile; 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 // Unit previews
@@ -302,7 +302,7 @@ namespace OpenRA.Mods.Cnc.Traits
var canEnter = manager.Self.Owner.Shroud.IsExplored(targetCell) && var canEnter = manager.Self.Owner.Shroud.IsExplored(targetCell) &&
unit.Trait<Chronoshiftable>().CanChronoshiftTo(unit, targetCell); unit.Trait<Chronoshiftable>().CanChronoshiftTo(unit, targetCell);
var tile = canEnter ? validTile : invalidTile; 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); var offset = world.Map.CenterOfCell(xy) - world.Map.CenterOfCell(sourceLocation);
@@ -332,7 +332,7 @@ namespace OpenRA.Mods.Cnc.Traits
// Source tiles // Source tiles
foreach (var t in power.CellsMatching(sourceLocation, footprint, dimensions)) 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) bool IsValidTarget(CPos xy)

View File

@@ -112,6 +112,7 @@ namespace OpenRA.Mods.Common.Graphics
public int ShadowZOffset { get; private set; } public int ShadowZOffset { get; private set; }
public int[] Frames { get; private set; } public int[] Frames { get; private set; }
public Rectangle Bounds { get; private set; } public Rectangle Bounds { get; private set; }
public bool IgnoreWorldTint { get; private set; }
public readonly uint[] EmbeddedPalette; public readonly uint[] EmbeddedPalette;
@@ -156,6 +157,7 @@ namespace OpenRA.Mods.Common.Graphics
Tick = LoadField(d, "Tick", 40); Tick = LoadField(d, "Tick", 40);
transpose = LoadField(d, "Transpose", false); transpose = LoadField(d, "Transpose", false);
Frames = LoadField<int[]>(d, "Frames", null); Frames = LoadField<int[]>(d, "Frames", null);
IgnoreWorldTint = LoadField(d, "IgnoreWorldTint", false);
var flipX = LoadField(d, "FlipX", false); var flipX = LoadField(d, "FlipX", false);
var flipY = LoadField(d, "FlipY", false); var flipY = LoadField(d, "FlipY", false);

View File

@@ -17,7 +17,7 @@ using OpenRA.Primitives;
namespace OpenRA.Mods.Common.Graphics namespace OpenRA.Mods.Common.Graphics
{ {
public struct ModelRenderable : IRenderable public struct ModelRenderable : IRenderable, ITintableRenderable
{ {
readonly IEnumerable<ModelAnimation> models; readonly IEnumerable<ModelAnimation> models;
readonly WPos pos; readonly WPos pos;
@@ -30,11 +30,22 @@ namespace OpenRA.Mods.Common.Graphics
readonly PaletteReference normalsPalette; readonly PaletteReference normalsPalette;
readonly PaletteReference shadowPalette; readonly PaletteReference shadowPalette;
readonly float scale; readonly float scale;
readonly float3 tint;
public ModelRenderable( public ModelRenderable(
IEnumerable<ModelAnimation> models, WPos pos, int zOffset, WRot camera, float scale, IEnumerable<ModelAnimation> models, WPos pos, int zOffset, WRot camera, float scale,
WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor, WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
PaletteReference color, PaletteReference normals, PaletteReference shadow) PaletteReference color, PaletteReference normals, PaletteReference shadow)
: this(models, pos, zOffset, camera, scale,
lightSource, lightAmbientColor, lightDiffuseColor,
color, normals, shadow,
float3.Ones) { }
public ModelRenderable(
IEnumerable<ModelAnimation> 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.models = models;
this.pos = pos; this.pos = pos;
@@ -47,6 +58,7 @@ namespace OpenRA.Mods.Common.Graphics
palette = color; palette = color;
normalsPalette = normals; normalsPalette = normals;
shadowPalette = shadow; shadowPalette = shadow;
this.tint = tint;
} }
public WPos Pos { get { return pos; } } public WPos Pos { get { return pos; } }
@@ -59,7 +71,7 @@ namespace OpenRA.Mods.Common.Graphics
return new ModelRenderable( return new ModelRenderable(
models, pos, zOffset, camera, scale, models, pos, zOffset, camera, scale,
lightSource, lightAmbientColor, lightDiffuseColor, lightSource, lightAmbientColor, lightDiffuseColor,
newPalette, normalsPalette, shadowPalette); newPalette, normalsPalette, shadowPalette, tint);
} }
public IRenderable WithZOffset(int newOffset) public IRenderable WithZOffset(int newOffset)
@@ -67,7 +79,7 @@ namespace OpenRA.Mods.Common.Graphics
return new ModelRenderable( return new ModelRenderable(
models, pos, newOffset, camera, scale, models, pos, newOffset, camera, scale,
lightSource, lightAmbientColor, lightDiffuseColor, lightSource, lightAmbientColor, lightDiffuseColor,
palette, normalsPalette, shadowPalette); palette, normalsPalette, shadowPalette, tint);
} }
public IRenderable OffsetBy(WVec vec) public IRenderable OffsetBy(WVec vec)
@@ -75,11 +87,19 @@ namespace OpenRA.Mods.Common.Graphics
return new ModelRenderable( return new ModelRenderable(
models, pos + vec, zOffset, camera, scale, models, pos + vec, zOffset, camera, scale,
lightSource, lightAmbientColor, lightDiffuseColor, lightSource, lightAmbientColor, lightDiffuseColor,
palette, normalsPalette, shadowPalette); palette, normalsPalette, shadowPalette, tint);
} }
public IRenderable AsDecoration() { return this; } 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 // This will need generalizing once we support TS/RA2 terrain
static readonly float[] GroundNormal = new float[] { 0, 0, 1, 1 }; static readonly float[] GroundNormal = new float[] { 0, 0, 1, 1 };
public IFinalizedRenderable PrepareRender(WorldRenderer wr) public IFinalizedRenderable PrepareRender(WorldRenderer wr)
@@ -122,8 +142,11 @@ namespace OpenRA.Mods.Common.Graphics
var sb = shadowOrigin + psb[2]; var sb = shadowOrigin + psb[2];
var sc = shadowOrigin + psb[1]; var sc = shadowOrigin + psb[1];
var sd = shadowOrigin + psb[3]; 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) public void RenderDebugGeometry(WorldRenderer wr)

View File

@@ -200,7 +200,7 @@ namespace OpenRA.Mods.Common.Traits
return footprint.Select(c => (IRenderable)(new SpriteRenderable( return footprint.Select(c => (IRenderable)(new SpriteRenderable(
wr.Theater.TileSprite(new TerrainTile(template, c.Value)), 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; bool initialized;

View File

@@ -93,7 +93,7 @@ namespace OpenRA.Mods.Common.Traits
var pal = HasFlag(c.Value, PlaceBuildingCellType.LineBuild) ? linePalette : cellPalette; var pal = HasFlag(c.Value, PlaceBuildingCellType.LineBuild) ? linePalette : cellPalette;
var pos = wr.World.Map.CenterOfCell(c.Key); var pos = wr.World.Map.CenterOfCell(c.Key);
var offset = new WVec(0, 0, topLeftPos.Z - pos.Z); 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);
} }
} }

View File

@@ -80,7 +80,7 @@ namespace OpenRA.Mods.Common.Traits
if (!a.IsCanceling) if (!a.IsCanceling)
foreach (var n in a.TargetLineNodes(self)) foreach (var n in a.TargetLineNodes(self))
if (n.Tile != null && n.Target.Type != TargetType.Invalid) 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; } } bool IRenderAboveShroud.SpatiallyPartitionable { get { return false; } }

View File

@@ -177,7 +177,7 @@ namespace OpenRA.Mods.Common.Traits.Render
var dat = self.World.Map.DistanceAboveTerrain(self.CenterPosition); var dat = self.World.Map.DistanceAboveTerrain(self.CenterPosition);
var pos = self.CenterPosition - new WVec(0, 0, dat.Length); var pos = self.CenterPosition - new WVec(0, 0, dat.Length);
var palette = wr.Palette(info.ShadowPalette); 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<Rectangle> IRender.ScreenBounds(Actor self, WorldRenderer wr) IEnumerable<Rectangle> IRender.ScreenBounds(Actor self, WorldRenderer wr)

View File

@@ -167,7 +167,7 @@ namespace OpenRA.Mods.Common.Traits
var pal = wr.Palette(TileSet.TerrainPaletteInternalName); var pal = wr.Palette(TileSet.TerrainPaletteInternalName);
foreach (var t in power.CellsMatching(xy, footprint, dimensions)) 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) protected override string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi)

View File

@@ -88,7 +88,7 @@ namespace OpenRA.Mods.Common.Traits
var offset = world.Map.Offset(new CVec(x, y), tileInfo.Height); var offset = world.Map.Offset(new CVec(x, y), tileInfo.Height);
var palette = wr.Palette(TerrainTemplate.Palette ?? TileSet.TerrainPaletteInternalName); 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 sprite = sequence.GetSprite(Resource.MaxDensity - 1);
var palette = wr.Palette(Resource.Palette); 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));
} }
} }
} }

View File

@@ -89,12 +89,12 @@ namespace OpenRA.Mods.Common.Traits
if (CopyRegion != null) if (CopyRegion != null)
foreach (var c in CopyRegion) foreach (var c in CopyRegion)
yield return new SpriteRenderable(copySprite, wr.World.Map.CenterOfCell(c), 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) if (PasteRegion != null)
foreach (var c in PasteRegion) foreach (var c in PasteRegion)
yield return new SpriteRenderable(pasteSprite, wr.World.Map.CenterOfCell(c), 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; } } bool IRenderAboveShroud.SpatiallyPartitionable { get { return false; } }

View File

@@ -82,7 +82,7 @@ namespace OpenRA.Mods.D2k.Traits
var pal = HasFlag(c.Value, PlaceBuildingCellType.LineBuild) ? linePalette : cellPalette; var pal = HasFlag(c.Value, PlaceBuildingCellType.LineBuild) ? linePalette : cellPalette;
var pos = wr.World.Map.CenterOfCell(c.Key); var pos = wr.World.Map.CenterOfCell(c.Key);
var offset = new WVec(0, 0, topLeftPos.Z - pos.Z); 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);
} }
} }
} }

View File

@@ -53,6 +53,8 @@ namespace OpenRA.Platforms.Default
OpenGL.CheckGLError(); OpenGL.CheckGLError();
OpenGL.glEnableVertexAttribArray(Shader.TexMetadataAttributeIndex); OpenGL.glEnableVertexAttribArray(Shader.TexMetadataAttributeIndex);
OpenGL.CheckGLError(); OpenGL.CheckGLError();
OpenGL.glEnableVertexAttribArray(Shader.TintAttributeIndex);
OpenGL.CheckGLError();
} }
public IVertexBuffer<Vertex> CreateVertexBuffer(int size) public IVertexBuffer<Vertex> CreateVertexBuffer(int size)

View File

@@ -21,6 +21,7 @@ namespace OpenRA.Platforms.Default
public const int VertexPosAttributeIndex = 0; public const int VertexPosAttributeIndex = 0;
public const int TexCoordAttributeIndex = 1; public const int TexCoordAttributeIndex = 1;
public const int TexMetadataAttributeIndex = 2; public const int TexMetadataAttributeIndex = 2;
public const int TintAttributeIndex = 3;
readonly Dictionary<string, int> samplers = new Dictionary<string, int>(); readonly Dictionary<string, int> samplers = new Dictionary<string, int>();
readonly Dictionary<int, int> legacySizeUniforms = new Dictionary<int, int>(); readonly Dictionary<int, int> legacySizeUniforms = new Dictionary<int, int>();
@@ -83,6 +84,8 @@ namespace OpenRA.Platforms.Default
OpenGL.CheckGLError(); OpenGL.CheckGLError();
OpenGL.glBindAttribLocation(program, TexMetadataAttributeIndex, "aVertexTexMetadata"); OpenGL.glBindAttribLocation(program, TexMetadataAttributeIndex, "aVertexTexMetadata");
OpenGL.CheckGLError(); OpenGL.CheckGLError();
OpenGL.glBindAttribLocation(program, TintAttributeIndex, "aVertexTint");
OpenGL.CheckGLError();
if (OpenGL.Profile != GLProfile.Legacy) if (OpenGL.Profile != GLProfile.Legacy)
{ {

View File

@@ -101,6 +101,8 @@ namespace OpenRA.Platforms.Default
OpenGL.CheckGLError(); OpenGL.CheckGLError();
OpenGL.glVertexAttribPointer(Shader.TexMetadataAttributeIndex, 2, OpenGL.GL_FLOAT, false, VertexSize, new IntPtr(28)); OpenGL.glVertexAttribPointer(Shader.TexMetadataAttributeIndex, 2, OpenGL.GL_FLOAT, false, VertexSize, new IntPtr(28));
OpenGL.CheckGLError(); OpenGL.CheckGLError();
OpenGL.glVertexAttribPointer(Shader.TintAttributeIndex, 3, OpenGL.GL_FLOAT, false, VertexSize, new IntPtr(36));
OpenGL.CheckGLError();
} }
public void Dispose() public void Dispose()

View File

@@ -26,6 +26,7 @@ varying vec2 vTexSampler;
varying vec4 vColorFraction; varying vec4 vColorFraction;
varying vec4 vRGBAFraction; varying vec4 vRGBAFraction;
varying vec4 vPalettedFraction; varying vec4 vPalettedFraction;
varying vec4 vTint;
uniform vec2 Texture0Size; uniform vec2 Texture0Size;
uniform vec2 Texture1Size; uniform vec2 Texture1Size;
@@ -46,6 +47,7 @@ in vec2 vTexSampler;
in vec4 vColorFraction; in vec4 vColorFraction;
in vec4 vRGBAFraction; in vec4 vRGBAFraction;
in vec4 vPalettedFraction; in vec4 vPalettedFraction;
in vec4 vTint;
out vec4 fragColor; out vec4 fragColor;
#endif #endif
@@ -227,8 +229,8 @@ void main()
} }
else else
#if __VERSION__ == 120 #if __VERSION__ == 120
gl_FragColor = c; gl_FragColor = c * vTint;
#else #else
fragColor = c; fragColor = c * vTint;
#endif #endif
} }

View File

@@ -7,6 +7,7 @@ uniform vec3 r1, r2;
attribute vec4 aVertexPosition; attribute vec4 aVertexPosition;
attribute vec4 aVertexTexCoord; attribute vec4 aVertexTexCoord;
attribute vec2 aVertexTexMetadata; attribute vec2 aVertexTexMetadata;
attribute vec3 aVertexTint;
varying vec4 vTexCoord; varying vec4 vTexCoord;
varying vec2 vTexMetadata; varying vec2 vTexMetadata;
@@ -17,10 +18,12 @@ varying vec2 vTexSampler;
varying vec4 vColorFraction; varying vec4 vColorFraction;
varying vec4 vRGBAFraction; varying vec4 vRGBAFraction;
varying vec4 vPalettedFraction; varying vec4 vPalettedFraction;
varying vec4 vTint;
#else #else
in vec4 aVertexPosition; in vec4 aVertexPosition;
in vec4 aVertexTexCoord; in vec4 aVertexTexCoord;
in vec2 aVertexTexMetadata; in vec2 aVertexTexMetadata;
in vec3 aVertexTint;
out vec4 vTexCoord; out vec4 vTexCoord;
out vec2 vTexMetadata; out vec2 vTexMetadata;
@@ -31,6 +34,7 @@ out vec2 vTexSampler;
out vec4 vColorFraction; out vec4 vColorFraction;
out vec4 vRGBAFraction; out vec4 vRGBAFraction;
out vec4 vPalettedFraction; out vec4 vPalettedFraction;
out vec4 vTint;
#endif #endif
vec4 UnpackChannelAttributes(float x) vec4 UnpackChannelAttributes(float x)
@@ -123,4 +127,5 @@ void main()
vPalettedFraction = SelectPalettedFraction(attrib.s); vPalettedFraction = SelectPalettedFraction(attrib.s);
vDepthMask = SelectChannelMask(attrib.t); vDepthMask = SelectChannelMask(attrib.t);
vTexSampler = attrib.pq; vTexSampler = attrib.pq;
vTint = vec4(aVertexTint, 1.0);
} }

View File

@@ -7,6 +7,7 @@ uniform mat4 TransformMatrix;
attribute vec4 aVertexPosition; attribute vec4 aVertexPosition;
attribute vec4 aVertexTexCoord; attribute vec4 aVertexTexCoord;
attribute vec2 aVertexTexMetadata; attribute vec2 aVertexTexMetadata;
attribute vec3 aVertexTint;
varying vec4 vTexCoord; varying vec4 vTexCoord;
varying vec4 vChannelMask; varying vec4 vChannelMask;
varying vec4 vNormalsMask; varying vec4 vNormalsMask;
@@ -14,6 +15,7 @@ varying vec4 vNormalsMask;
in vec4 aVertexPosition; in vec4 aVertexPosition;
in vec4 aVertexTexCoord; in vec4 aVertexTexCoord;
in vec2 aVertexTexMetadata; in vec2 aVertexTexMetadata;
in vec3 aVertexTint;
out vec4 vTexCoord; out vec4 vTexCoord;
out vec4 vChannelMask; out vec4 vChannelMask;
out vec4 vNormalsMask; out vec4 vNormalsMask;