diff --git a/OpenRA.Game/Graphics/ContrailRenderable.cs b/OpenRA.Game/Graphics/ContrailRenderable.cs
new file mode 100644
index 0000000000..9ef43b1813
--- /dev/null
+++ b/OpenRA.Game/Graphics/ContrailRenderable.cs
@@ -0,0 +1,105 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
+ * 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. For more information,
+ * see COPYING.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+
+namespace OpenRA.Graphics
+{
+ public struct ContrailRenderable : IRenderable
+ {
+ readonly World world;
+
+ // Store trail positions in a circular buffer
+ readonly WPos[] trail;
+ int next;
+ int length;
+ int skip;
+
+ readonly Color color;
+ readonly int zOffset;
+
+ public ContrailRenderable(World world, Color color, int length, int skip, int zOffset)
+ : this(world, new WPos[length], 0, 0, skip, color, zOffset) {}
+
+ ContrailRenderable(World world, WPos[] trail, int next, int length, int skip, Color color, int zOffset)
+ {
+ this.world = world;
+ this.trail = trail;
+ this.next = next;
+ this.length = length;
+ this.skip = skip;
+ this.color = color;
+ this.zOffset = zOffset;
+ }
+
+ public WPos Pos { get { return trail[idx(next-1)]; } }
+ public float Scale { get { return 1f; } }
+ public PaletteReference Palette { get { return null; } }
+ public int ZOffset { get { return zOffset; } }
+
+ public IRenderable WithScale(float newScale) { return new ContrailRenderable(world, (WPos[])trail.Clone(), next, length, skip, color, zOffset); }
+ public IRenderable WithPalette(PaletteReference newPalette) { return new ContrailRenderable(world, (WPos[])trail.Clone(), next, length, skip, color, zOffset); }
+ public IRenderable WithZOffset(int newOffset) { return new ContrailRenderable(world, (WPos[])trail.Clone(), next, length, skip, color, newOffset); }
+ public IRenderable WithPos(WPos pos) { return new ContrailRenderable(world, (WPos[])trail.Clone(), next, length, skip, color, zOffset); }
+
+ public void BeforeRender(WorldRenderer wr) {}
+ public void Render(WorldRenderer wr)
+ {
+ // Need at least 4 points to smooth the contrail over
+ if (length - skip < 4 )
+ return;
+
+ // Start of the first line segment is the tail of the list - don't smooth it.
+ var curPos = trail[idx(next - skip - 1)];
+ var curCell = new CPos(curPos);
+ var curColor = color;
+ for (var i = 0; i < length - skip - 4; i++)
+ {
+ var j = next - skip - i - 2;
+ var nextPos = WPos.Average(trail[idx(j)], trail[idx(j-1)], trail[idx(j-2)], trail[idx(j-3)]);
+ var nextCell = new CPos(nextPos);
+ var nextColor = Exts.ColorLerp(i * 1f / (length - 4), color, Color.Transparent);
+
+ if (!world.FogObscures(curCell) && !world.FogObscures(nextCell))
+ Game.Renderer.WorldLineRenderer.DrawLine(wr.ScreenPosition(curPos), wr.ScreenPosition(nextPos), curColor, nextColor);
+
+ curPos = nextPos;
+ curCell = nextCell;
+ curColor = nextColor;
+ }
+ }
+
+ public void RenderDebugGeometry(WorldRenderer wr) {}
+
+ // Array index modulo length
+ int idx(int i)
+ {
+ var j = i % trail.Length;
+ return j < 0 ? j + trail.Length : j;
+ }
+
+ public void Update(WPos pos)
+ {
+ trail[next] = pos;
+ next = idx(next+1);
+
+ if (length < trail.Length)
+ length++;
+ }
+
+ public static Color ChooseColor(Actor self)
+ {
+ var ownerColor = Color.FromArgb(255, self.Owner.Color.RGB);
+ return Exts.ColorLerp(0.5f, ownerColor, Color.White);
+ }
+ }
+}
diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj
index d2f7dea76d..0aca544852 100644
--- a/OpenRA.Game/OpenRA.Game.csproj
+++ b/OpenRA.Game/OpenRA.Game.csproj
@@ -235,6 +235,7 @@
+
diff --git a/OpenRA.Mods.RA/Effects/Bullet.cs b/OpenRA.Mods.RA/Effects/Bullet.cs
index 5193c3bd4f..678aec409b 100755
--- a/OpenRA.Mods.RA/Effects/Bullet.cs
+++ b/OpenRA.Mods.RA/Effects/Bullet.cs
@@ -51,7 +51,7 @@ namespace OpenRA.Mods.RA.Effects
Animation anim;
const int BaseBulletSpeed = 100; /* pixels / 40ms frame */
- ContrailHistory Trail;
+ ContrailRenderable Trail;
public Bullet(BulletInfo info, ProjectileArgs args)
{
@@ -73,9 +73,8 @@ namespace OpenRA.Mods.RA.Effects
if (Info.ContrailLength > 0)
{
- Trail = new ContrailHistory(Info.ContrailLength,
- Info.ContrailUsePlayerColor ? ContrailHistory.ChooseColor(args.firedBy) : Info.ContrailColor,
- Info.ContrailDelay);
+ var color = Info.ContrailUsePlayerColor ? ContrailRenderable.ChooseColor(args.firedBy) : Info.ContrailColor;
+ Trail = new ContrailRenderable(args.firedBy.World, color, Info.ContrailLength, Info.ContrailDelay, 0);
}
}
@@ -127,10 +126,10 @@ namespace OpenRA.Mods.RA.Effects
ticksToNextSmoke = Info.TrailInterval;
}
- if (Trail != null)
+ if (Info.ContrailLength > 0)
{
var alt = (Info.High || Info.Angle > 0) ? GetAltitude() : 0;
- Trail.Tick(new PPos((int)pos.X, (int)pos.Y).ToWPos((int)alt));
+ Trail.Update(new PPos((int)pos.X, (int)pos.Y).ToWPos((int)alt));
}
}
@@ -153,6 +152,9 @@ namespace OpenRA.Mods.RA.Effects
public IEnumerable Render(WorldRenderer wr)
{
+ if (Info.ContrailLength > 0)
+ yield return Trail;
+
if (anim != null)
{
var at = (float)t / TotalTime();
@@ -177,9 +179,6 @@ namespace OpenRA.Mods.RA.Effects
wr.Palette(Args.weapon.Underwater ? "shadow" : "effect"), (int)pos.Y);
}
}
-
- if (Trail != null)
- Trail.Render(wr, Args.firedBy);
}
void Explode( World world )
diff --git a/OpenRA.Mods.RA/Effects/Contrail.cs b/OpenRA.Mods.RA/Effects/Contrail.cs
index 17f15b49e2..8f4ee7cc06 100755
--- a/OpenRA.Mods.RA/Effects/Contrail.cs
+++ b/OpenRA.Mods.RA/Effects/Contrail.cs
@@ -28,17 +28,18 @@ namespace OpenRA.Mods.RA
public object Create(ActorInitializer init) { return new Contrail(init.self, this); }
}
- class Contrail : ITick, IPostRender
+ class Contrail : ITick, IRender
{
ContrailInfo info;
- ContrailHistory history;
+ ContrailRenderable trail;
IBodyOrientation body;
public Contrail(Actor self, ContrailInfo info)
{
this.info = info;
- history = new ContrailHistory(info.TrailLength,
- info.UsePlayerColor ? ContrailHistory.ChooseColor(self) : info.Color);
+
+ var color = info.UsePlayerColor ? ContrailRenderable.ChooseColor(self) : info.Color;
+ trail = new ContrailRenderable(self.World, color, info.TrailLength, 0, 0);
body = self.Trait();
}
@@ -46,62 +47,12 @@ namespace OpenRA.Mods.RA
public void Tick(Actor self)
{
var local = info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation));
- history.Tick(self.CenterPosition + body.LocalToWorld(local));
+ trail.Update(self.CenterPosition + body.LocalToWorld(local));
}
- public void RenderAfterWorld(WorldRenderer wr, Actor self) { history.Render(wr, self); }
- }
-
- class ContrailHistory
- {
- List positions = new List();
- readonly int TrailLength;
- readonly Color Color;
- readonly int StartSkip;
-
- public static Color ChooseColor(Actor self)
+ public IEnumerable Render(Actor self, WorldRenderer wr)
{
- var ownerColor = Color.FromArgb(255, self.Owner.Color.RGB);
- return Exts.ColorLerp(0.5f, ownerColor, Color.White);
- }
-
- public ContrailHistory(int trailLength, Color color)
- : this(trailLength, color, 0) { }
-
- public ContrailHistory(int trailLength, Color color, int startSkip)
- {
- this.TrailLength = trailLength;
- this.Color = color;
- this.StartSkip = startSkip;
- }
-
- public void Tick(WPos currentPos)
- {
- positions.Add(currentPos);
- if (positions.Count >= TrailLength)
- positions.RemoveAt(0);
- }
-
- public void Render(WorldRenderer wr, Actor self)
- {
- Color trailStart = Color;
- Color trailEnd = Color.FromArgb(trailStart.A - 255 / TrailLength, trailStart.R, trailStart.G, trailStart.B);
-
- for (int i = positions.Count - 1 - StartSkip; i >= 4; --i)
- {
- // World positions
- var conPos = WPos.Average(positions[i], positions[i-1], positions[i-2], positions[i-3]);
- var nextPos = WPos.Average(positions[i-1], positions[i-2], positions[i-3], positions[i-4]);
-
- if (!self.World.FogObscures(new CPos(conPos)) &&
- !self.World.FogObscures(new CPos(nextPos)))
- {
- Game.Renderer.WorldLineRenderer.DrawLine(wr.ScreenPosition(conPos), wr.ScreenPosition(nextPos), trailStart, trailEnd);
-
- trailStart = trailEnd;
- trailEnd = Color.FromArgb(trailStart.A - 255 / positions.Count, trailStart.R, trailStart.G, trailStart.B);
- }
- }
+ yield return trail;
}
}
}
diff --git a/OpenRA.Mods.RA/Effects/Missile.cs b/OpenRA.Mods.RA/Effects/Missile.cs
index e429148b9b..6b3aea3198 100755
--- a/OpenRA.Mods.RA/Effects/Missile.cs
+++ b/OpenRA.Mods.RA/Effects/Missile.cs
@@ -59,7 +59,7 @@ namespace OpenRA.Mods.RA.Effects
int Facing;
int t;
int Altitude;
- ContrailHistory Trail;
+ ContrailRenderable Trail;
public Missile(MissileInfo info, ProjectileArgs args)
{
@@ -81,9 +81,8 @@ namespace OpenRA.Mods.RA.Effects
if (Info.ContrailLength > 0)
{
- Trail = new ContrailHistory(Info.ContrailLength,
- Info.ContrailUsePlayerColor ? ContrailHistory.ChooseColor(args.firedBy) : Info.ContrailColor,
- Info.ContrailDelay);
+ var color = Info.ContrailUsePlayerColor ? ContrailRenderable.ChooseColor(args.firedBy) : Info.ContrailColor;
+ Trail = new ContrailRenderable(args.firedBy.World, color, Info.ContrailLength, Info.ContrailDelay, 0);
}
}
@@ -162,8 +161,8 @@ namespace OpenRA.Mods.RA.Effects
Explode(world);
}
- if (Trail != null)
- Trail.Tick(PxPosition.ToWPos(Altitude));
+ if (Info.ContrailLength > 0)
+ Trail.Update(PxPosition.ToWPos(Altitude));
}
void Explode(World world)
@@ -176,12 +175,12 @@ namespace OpenRA.Mods.RA.Effects
public IEnumerable Render(WorldRenderer wr)
{
+ if (Info.ContrailLength > 0)
+ yield return Trail;
+
if (!Args.firedBy.World.FogObscures(PxPosition.ToCPos()))
yield return new SpriteRenderable(anim.Image, PxPosition.ToFloat2() - new float2(0, Altitude),
wr.Palette(Args.weapon.Underwater ? "shadow" : "effect"), PxPosition.Y);
-
- if (Trail != null)
- Trail.Render(wr, Args.firedBy);
}
}
}