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); } } }