diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index 9da6da7d97..b0e86e3d11 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -502,6 +502,10 @@ namespace OpenRA using (new PerfSample("render_widgets")) { + Game.Renderer.WorldVoxelRenderer.BeginFrame(); + Ui.PrepareRenderables(); + Game.Renderer.WorldVoxelRenderer.EndFrame(); + Ui.Draw(); if (ModData != null && ModData.CursorProvider != null) diff --git a/OpenRA.Game/Graphics/Renderable.cs b/OpenRA.Game/Graphics/Renderable.cs index 2dc089f326..14ad932990 100644 --- a/OpenRA.Game/Graphics/Renderable.cs +++ b/OpenRA.Game/Graphics/Renderable.cs @@ -17,73 +17,22 @@ namespace OpenRA.Graphics public interface IRenderable { WPos Pos { get; } - float Scale { get; } PaletteReference Palette { get; } int ZOffset { get; } bool IsDecoration { get; } - IRenderable WithScale(float newScale); IRenderable WithPalette(PaletteReference newPalette); IRenderable WithZOffset(int newOffset); IRenderable OffsetBy(WVec offset); IRenderable AsDecoration(); - void BeforeRender(WorldRenderer wr); + IFinalizedRenderable PrepareRender(WorldRenderer wr); + } + + public interface IFinalizedRenderable + { void Render(WorldRenderer wr); void RenderDebugGeometry(WorldRenderer wr); - } - - public struct SpriteRenderable : IRenderable - { - public static readonly IEnumerable None = new IRenderable[0].AsEnumerable(); - - readonly Sprite sprite; - readonly WPos pos; - readonly WVec offset; - readonly int zOffset; - readonly PaletteReference palette; - readonly float scale; - readonly bool isDecoration; - - public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, bool isDecoration) - { - this.sprite = sprite; - this.pos = pos; - this.offset = offset; - this.zOffset = zOffset; - this.palette = palette; - this.scale = scale; - this.isDecoration = isDecoration; - } - - public WPos Pos { get { return pos + offset; } } - public WVec Offset { get { return offset; } } - public float Scale { get { return scale; } } - public PaletteReference Palette { get { return palette; } } - public int ZOffset { get { return zOffset; } } - public bool IsDecoration { get { return isDecoration; } } - - public IRenderable WithScale(float newScale) { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, newScale, 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); } - - float2 ScreenPosition(WorldRenderer wr) - { - return wr.ScreenPxPosition(pos) + wr.ScreenPxOffset(offset) - (0.5f * scale * sprite.Size).ToInt2(); - } - - public void BeforeRender(WorldRenderer wr) { } - public void Render(WorldRenderer wr) - { - Game.Renderer.WorldSpriteRenderer.DrawSprite(sprite, ScreenPosition(wr), palette, sprite.Size * scale); - } - - public void RenderDebugGeometry(WorldRenderer wr) - { - var offset = ScreenPosition(wr) + sprite.Offset; - Game.Renderer.WorldLineRenderer.DrawRect(offset, offset + sprite.Size, Color.Red); - } + Rectangle ScreenBounds(WorldRenderer wr); } } diff --git a/OpenRA.Game/Graphics/Renderer.cs b/OpenRA.Game/Graphics/Renderer.cs index 6880fa30df..19211ff5be 100644 --- a/OpenRA.Game/Graphics/Renderer.cs +++ b/OpenRA.Game/Graphics/Renderer.cs @@ -108,7 +108,11 @@ namespace OpenRA.Graphics public void BeginFrame(int2 scroll, float zoom) { Device.Clear(); + SetViewportParams(scroll, zoom); + } + public void SetViewportParams(int2 scroll, float zoom) + { var resolutionChanged = lastResolution != Resolution; if (resolutionChanged) { diff --git a/OpenRA.Game/Graphics/SelectionBarsRenderable.cs b/OpenRA.Game/Graphics/SelectionBarsRenderable.cs index 2c4711ecf7..9127ea3bae 100644 --- a/OpenRA.Game/Graphics/SelectionBarsRenderable.cs +++ b/OpenRA.Game/Graphics/SelectionBarsRenderable.cs @@ -14,7 +14,7 @@ using OpenRA.Traits; namespace OpenRA.Graphics { - public struct SelectionBarsRenderable : IRenderable + public struct SelectionBarsRenderable : IRenderable, IFinalizedRenderable { readonly WPos pos; readonly Actor actor; @@ -30,12 +30,10 @@ namespace OpenRA.Graphics public WPos Pos { get { return pos; } } - public float Scale { get { return 1f; } } public PaletteReference Palette { get { return null; } } public int ZOffset { get { return 0; } } public bool IsDecoration { get { return true; } } - public IRenderable WithScale(float newScale) { return this; } public IRenderable WithPalette(PaletteReference newPalette) { return this; } public IRenderable WithZOffset(int newOffset) { return this; } public IRenderable OffsetBy(WVec vec) { return new SelectionBarsRenderable(pos + vec, actor); } @@ -135,7 +133,7 @@ namespace OpenRA.Graphics } } - public void BeforeRender(WorldRenderer wr) { } + public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; } public void Render(WorldRenderer wr) { if (!actor.IsInWorld || actor.IsDead) @@ -155,5 +153,6 @@ namespace OpenRA.Graphics } public void RenderDebugGeometry(WorldRenderer wr) { } + public Rectangle ScreenBounds(WorldRenderer wr) { return Rectangle.Empty; } } } diff --git a/OpenRA.Game/Graphics/SelectionBoxRenderable.cs b/OpenRA.Game/Graphics/SelectionBoxRenderable.cs index 2f44fe1041..1b677553f8 100644 --- a/OpenRA.Game/Graphics/SelectionBoxRenderable.cs +++ b/OpenRA.Game/Graphics/SelectionBoxRenderable.cs @@ -14,7 +14,7 @@ using OpenRA.Traits; namespace OpenRA.Graphics { - public struct SelectionBoxRenderable : IRenderable + public struct SelectionBoxRenderable : IRenderable, IFinalizedRenderable { readonly WPos pos; readonly float scale; @@ -34,18 +34,16 @@ namespace OpenRA.Graphics public WPos Pos { get { return pos; } } - public float Scale { get { return scale; } } public PaletteReference Palette { get { return null; } } public int ZOffset { get { return 0; } } public bool IsDecoration { get { return true; } } - public IRenderable WithScale(float newScale) { return new SelectionBoxRenderable(pos, bounds, newScale, color); } public IRenderable WithPalette(PaletteReference newPalette) { return this; } public IRenderable WithZOffset(int newOffset) { return this; } public IRenderable OffsetBy(WVec vec) { return new SelectionBoxRenderable(pos + vec, bounds, scale, color); } public IRenderable AsDecoration() { return this; } - public void BeforeRender(WorldRenderer wr) { } + public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; } public void Render(WorldRenderer wr) { var screenPos = wr.ScreenPxPosition(pos); @@ -69,5 +67,6 @@ namespace OpenRA.Graphics } public void RenderDebugGeometry(WorldRenderer wr) { } + public Rectangle ScreenBounds(WorldRenderer wr) { return Rectangle.Empty; } } } diff --git a/OpenRA.Game/Graphics/SpriteRenderable.cs b/OpenRA.Game/Graphics/SpriteRenderable.cs new file mode 100644 index 0000000000..793942d2ee --- /dev/null +++ b/OpenRA.Game/Graphics/SpriteRenderable.cs @@ -0,0 +1,74 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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.Collections.Generic; +using System.Drawing; +using System.Linq; + +namespace OpenRA.Graphics +{ + public struct SpriteRenderable : IRenderable, IFinalizedRenderable + { + public static readonly IEnumerable None = new IRenderable[0].AsEnumerable(); + + readonly Sprite sprite; + readonly WPos pos; + readonly WVec offset; + readonly int zOffset; + readonly PaletteReference palette; + readonly float scale; + readonly bool isDecoration; + + public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, bool isDecoration) + { + this.sprite = sprite; + this.pos = pos; + this.offset = offset; + this.zOffset = zOffset; + this.palette = palette; + this.scale = scale; + this.isDecoration = isDecoration; + } + + public WPos Pos { get { return pos + offset; } } + public WVec Offset { get { return offset; } } + public PaletteReference Palette { get { return palette; } } + 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); } + + float2 ScreenPosition(WorldRenderer wr) + { + return wr.ScreenPxPosition(pos) + wr.ScreenPxOffset(offset) - (0.5f * scale * sprite.Size).ToInt2(); + } + + public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; } + public void Render(WorldRenderer wr) + { + Game.Renderer.WorldSpriteRenderer.DrawSprite(sprite, ScreenPosition(wr), palette, sprite.Size * scale); + } + + public void RenderDebugGeometry(WorldRenderer wr) + { + var offset = ScreenPosition(wr) + sprite.Offset; + Game.Renderer.WorldLineRenderer.DrawRect(offset, offset + sprite.Size, Color.Red); + } + + public Rectangle ScreenBounds(WorldRenderer wr) + { + var offset = ScreenPosition(wr) + sprite.Offset; + return new Rectangle((int)offset.X, (int)offset.Y, (int)sprite.Size.X, (int)sprite.Size.Y); + } + } +} diff --git a/OpenRA.Game/Graphics/TargetLineRenderable.cs b/OpenRA.Game/Graphics/TargetLineRenderable.cs index 0231d390e4..ad8adba6b6 100644 --- a/OpenRA.Game/Graphics/TargetLineRenderable.cs +++ b/OpenRA.Game/Graphics/TargetLineRenderable.cs @@ -16,7 +16,7 @@ using OpenRA.Traits; namespace OpenRA.Graphics { - public struct TargetLineRenderable : IRenderable + public struct TargetLineRenderable : IRenderable, IFinalizedRenderable { readonly IEnumerable waypoints; readonly Color color; @@ -28,18 +28,16 @@ namespace OpenRA.Graphics } public WPos Pos { get { return waypoints.First(); } } - public float Scale { get { return 1f; } } public PaletteReference Palette { get { return null; } } public int ZOffset { get { return 0; } } public bool IsDecoration { get { return true; } } - public IRenderable WithScale(float newScale) { return new TargetLineRenderable(waypoints, color); } public IRenderable WithPalette(PaletteReference newPalette) { return new TargetLineRenderable(waypoints, color); } public IRenderable WithZOffset(int newOffset) { return new TargetLineRenderable(waypoints, color); } public IRenderable OffsetBy(WVec vec) { return new TargetLineRenderable(waypoints.Select(w => w + vec), color); } public IRenderable AsDecoration() { return this; } - public void BeforeRender(WorldRenderer wr) { } + public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; } public void Render(WorldRenderer wr) { if (!waypoints.Any()) @@ -58,5 +56,6 @@ namespace OpenRA.Graphics } public void RenderDebugGeometry(WorldRenderer wr) { } + public Rectangle ScreenBounds(WorldRenderer wr) { return Rectangle.Empty; } } } diff --git a/OpenRA.Game/Graphics/UISpriteRenderable.cs b/OpenRA.Game/Graphics/UISpriteRenderable.cs index 3bd66d967d..757fdc6485 100644 --- a/OpenRA.Game/Graphics/UISpriteRenderable.cs +++ b/OpenRA.Game/Graphics/UISpriteRenderable.cs @@ -14,7 +14,7 @@ using System.Linq; namespace OpenRA.Graphics { - public struct UISpriteRenderable : IRenderable + public struct UISpriteRenderable : IRenderable, IFinalizedRenderable { readonly Sprite sprite; readonly int2 screenPos; @@ -36,17 +36,15 @@ namespace OpenRA.Graphics public WVec Offset { get { return WVec.Zero; } } public bool IsDecoration { get { return true; } } - public float Scale { get { return scale; } } public PaletteReference Palette { get { return palette; } } public int ZOffset { get { return zOffset; } } - public IRenderable WithScale(float newScale) { return new UISpriteRenderable(sprite, screenPos, zOffset, palette, newScale); } public IRenderable WithPalette(PaletteReference newPalette) { return new UISpriteRenderable(sprite, screenPos, zOffset, newPalette, scale); } public IRenderable WithZOffset(int newOffset) { return this; } public IRenderable OffsetBy(WVec vec) { return this; } public IRenderable AsDecoration() { return this; } - public void BeforeRender(WorldRenderer wr) { } + public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; } public void Render(WorldRenderer wr) { Game.Renderer.SpriteRenderer.DrawSprite(sprite, screenPos, palette, sprite.Size * scale); @@ -57,5 +55,11 @@ namespace OpenRA.Graphics var offset = screenPos + sprite.Offset; Game.Renderer.LineRenderer.DrawRect(offset, offset + sprite.Size, Color.Red); } + + public Rectangle ScreenBounds(WorldRenderer wr) + { + var offset = screenPos + sprite.Offset; + return new Rectangle((int)offset.X, (int)offset.Y, (int)sprite.Size.X, (int)sprite.Size.Y); + } } } diff --git a/OpenRA.Game/Graphics/WorldRenderer.cs b/OpenRA.Game/Graphics/WorldRenderer.cs index e79e551af9..4f3ba875d4 100644 --- a/OpenRA.Game/Graphics/WorldRenderer.cs +++ b/OpenRA.Game/Graphics/WorldRenderer.cs @@ -77,7 +77,7 @@ namespace OpenRA.Graphics public void AddPalette(string name, ImmutablePalette pal, bool allowModifiers) { palette.AddPalette(name, pal, allowModifiers); } public void ReplacePalette(string name, IPalette pal) { palette.ReplacePalette(name, pal); palettes[name].Palette = pal; } - List GenerateRenderables() + List GenerateRenderables() { var actors = World.ScreenMap.ActorsInBox(Viewport.TopLeft, Viewport.BottomRight) .Append(World.WorldActor); @@ -100,12 +100,9 @@ namespace OpenRA.Graphics if (World.OrderGenerator != null) effectRenderables = effectRenderables.Concat(World.OrderGenerator.RenderAfterWorld(this, World)); - // Iterating via foreach copies the structs, so enumerate by index - var renderables = worldRenderables.Concat(effectRenderables).ToList(); - Game.Renderer.WorldVoxelRenderer.BeginFrame(); - for (var i = 0; i < renderables.Count; i++) - renderables[i].BeforeRender(this); + var renderables = worldRenderables.Concat(effectRenderables) + .Select(r => r.PrepareRender(this)).ToList(); Game.Renderer.WorldVoxelRenderer.EndFrame(); return renderables; @@ -146,21 +143,19 @@ namespace OpenRA.Graphics var overlayRenderables = World.Selection.Actors.Where(a => !a.Destroyed) .SelectMany(a => a.TraitsImplementing()) - .SelectMany(t => t.RenderAfterWorld(this)) - .ToList(); + .SelectMany(t => t.RenderAfterWorld(this)); Game.Renderer.WorldVoxelRenderer.BeginFrame(); - for (var i = 0; i < overlayRenderables.Count; i++) - overlayRenderables[i].BeforeRender(this); + var finalOverlayRenderables = overlayRenderables.Select(r => r.PrepareRender(this)); Game.Renderer.WorldVoxelRenderer.EndFrame(); // HACK: Keep old grouping behaviour - foreach (var g in overlayRenderables.GroupBy(prs => prs.GetType())) + foreach (var g in finalOverlayRenderables.GroupBy(prs => prs.GetType())) foreach (var r in g) r.Render(this); if (devTrait.Value != null && devTrait.Value.ShowDebugGeometry) - foreach (var g in overlayRenderables.GroupBy(prs => prs.GetType())) + foreach (var g in finalOverlayRenderables.GroupBy(prs => prs.GetType())) foreach (var r in g) r.RenderDebugGeometry(this); diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index 9473294e05..db244831f3 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -219,7 +219,6 @@ - @@ -258,6 +257,7 @@ + diff --git a/OpenRA.Game/Widgets/SpriteSequenceWidget.cs b/OpenRA.Game/Widgets/SpriteSequenceWidget.cs deleted file mode 100644 index 7990c69d3f..0000000000 --- a/OpenRA.Game/Widgets/SpriteSequenceWidget.cs +++ /dev/null @@ -1,65 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2015 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 OpenRA.Graphics; - -namespace OpenRA.Widgets -{ - public class SpriteSequenceWidget : SpriteWidget - { - public string Unit = null; - public string Sequence = null; - public int Frame = 0; - public int Facing = 0; - - public Func GetAnimation; - public Func GetFacing; - - [ObjectCreator.UseCtor] - public SpriteSequenceWidget(WorldRenderer worldRenderer) - : base(worldRenderer) - { - GetAnimation = () => null; - } - - public override void Initialize(WidgetArgs args) - { - base.Initialize(args); - - if (Unit != null && Sequence != null) - { - var anim = new Animation(WorldRenderer.World, Unit, () => Facing); - anim.PlayFetchIndex(Sequence, () => Frame); - GetAnimation = () => anim; - } - - GetSprite = () => - { - var anim = GetAnimation(); - return anim != null ? anim.Image : null; - }; - } - - protected SpriteSequenceWidget(SpriteSequenceWidget other) - : base(other) - { - Unit = other.Unit; - Sequence = other.Sequence; - Frame = other.Frame; - Facing = other.Facing; - - GetAnimation = other.GetAnimation; - GetFacing = other.GetFacing; - } - - public override Widget Clone() { return new SpriteSequenceWidget(this); } - } -} diff --git a/OpenRA.Game/Widgets/Widget.cs b/OpenRA.Game/Widgets/Widget.cs index 081df1efaa..8723bae336 100644 --- a/OpenRA.Game/Widgets/Widget.cs +++ b/OpenRA.Game/Widgets/Widget.cs @@ -72,6 +72,8 @@ namespace OpenRA.Widgets public static void Tick() { Root.TickOuter(); } + public static void PrepareRenderables() { Root.PrepareRenderablesOuter(); } + public static void Draw() { Root.DrawOuter(); } public static bool HandleInput(MouseInput mi) @@ -387,6 +389,18 @@ namespace OpenRA.Widgets return handled; } + public virtual void PrepareRenderables() { } + + public virtual void PrepareRenderablesOuter() + { + if (IsVisible()) + { + PrepareRenderables(); + foreach (var child in Children) + child.PrepareRenderablesOuter(); + } + } + public virtual void Draw() { } public virtual void DrawOuter() diff --git a/OpenRA.Mods.Common/Graphics/BeamRenderable.cs b/OpenRA.Mods.Common/Graphics/BeamRenderable.cs index fcb9113cc8..ac024c4187 100644 --- a/OpenRA.Mods.Common/Graphics/BeamRenderable.cs +++ b/OpenRA.Mods.Common/Graphics/BeamRenderable.cs @@ -13,7 +13,7 @@ using OpenRA.Graphics; namespace OpenRA.Mods.Common.Graphics { - public struct BeamRenderable : IRenderable + public struct BeamRenderable : IRenderable, IFinalizedRenderable { readonly WPos pos; readonly int zOffset; @@ -31,18 +31,16 @@ namespace OpenRA.Mods.Common.Graphics } public WPos Pos { get { return pos; } } - public float Scale { get { return 1f; } } public PaletteReference Palette { get { return null; } } public int ZOffset { get { return zOffset; } } public bool IsDecoration { get { return true; } } - public IRenderable WithScale(float newScale) { return new BeamRenderable(pos, zOffset, length, width, color); } public IRenderable WithPalette(PaletteReference newPalette) { return new BeamRenderable(pos, zOffset, length, width, color); } public IRenderable WithZOffset(int newOffset) { return new BeamRenderable(pos, zOffset, length, width, color); } public IRenderable OffsetBy(WVec vec) { return new BeamRenderable(pos + vec, zOffset, length, width, color); } public IRenderable AsDecoration() { return this; } - public void BeforeRender(WorldRenderer wr) { } + public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; } public void Render(WorldRenderer wr) { var wlr = Game.Renderer.WorldLineRenderer; @@ -56,5 +54,6 @@ namespace OpenRA.Mods.Common.Graphics } public void RenderDebugGeometry(WorldRenderer wr) { } + public Rectangle ScreenBounds(WorldRenderer wr) { return Rectangle.Empty; } } } diff --git a/OpenRA.Mods.Common/Graphics/ContrailRenderable.cs b/OpenRA.Mods.Common/Graphics/ContrailRenderable.cs index c4f315aa64..d1075be2cb 100644 --- a/OpenRA.Mods.Common/Graphics/ContrailRenderable.cs +++ b/OpenRA.Mods.Common/Graphics/ContrailRenderable.cs @@ -14,7 +14,7 @@ using OpenRA.Graphics; namespace OpenRA.Mods.Common.Graphics { - public struct ContrailRenderable : IRenderable + public struct ContrailRenderable : IRenderable, IFinalizedRenderable { public int Length { get { return trail.Length; } } @@ -43,18 +43,16 @@ namespace OpenRA.Mods.Common.Graphics } public WPos Pos { get { return trail[Index(next - 1)]; } } - public float Scale { get { return 1f; } } public PaletteReference Palette { get { return null; } } public int ZOffset { get { return zOffset; } } public bool IsDecoration { get { return true; } } - 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 OffsetBy(WVec vec) { return new ContrailRenderable(world, trail.Select(pos => pos + vec).ToArray(), next, length, skip, color, zOffset); } public IRenderable AsDecoration() { return this; } - public void BeforeRender(WorldRenderer wr) { } + public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; } public void Render(WorldRenderer wr) { // Need at least 4 points to smooth the contrail over @@ -88,6 +86,7 @@ namespace OpenRA.Mods.Common.Graphics } public void RenderDebugGeometry(WorldRenderer wr) { } + public Rectangle ScreenBounds(WorldRenderer wr) { return Rectangle.Empty; } // Array index modulo length int Index(int i) diff --git a/OpenRA.Mods.Common/Graphics/RangeCircleRenderable.cs b/OpenRA.Mods.Common/Graphics/RangeCircleRenderable.cs index 6961899bfc..82cb05e07f 100644 --- a/OpenRA.Mods.Common/Graphics/RangeCircleRenderable.cs +++ b/OpenRA.Mods.Common/Graphics/RangeCircleRenderable.cs @@ -13,7 +13,7 @@ using OpenRA.Graphics; namespace OpenRA.Mods.Common.Graphics { - public struct RangeCircleRenderable : IRenderable + public struct RangeCircleRenderable : IRenderable, IFinalizedRenderable { readonly WPos centerPosition; readonly WRange radius; @@ -31,18 +31,16 @@ namespace OpenRA.Mods.Common.Graphics } public WPos Pos { get { return centerPosition; } } - public float Scale { get { return 1f; } } public PaletteReference Palette { get { return null; } } public int ZOffset { get { return zOffset; } } public bool IsDecoration { get { return true; } } - public IRenderable WithScale(float newScale) { return new RangeCircleRenderable(centerPosition, radius, zOffset, color, contrastColor); } public IRenderable WithPalette(PaletteReference newPalette) { return new RangeCircleRenderable(centerPosition, radius, zOffset, color, contrastColor); } public IRenderable WithZOffset(int newOffset) { return new RangeCircleRenderable(centerPosition, radius, newOffset, color, contrastColor); } public IRenderable OffsetBy(WVec vec) { return new RangeCircleRenderable(centerPosition + vec, radius, zOffset, color, contrastColor); } public IRenderable AsDecoration() { return this; } - public void BeforeRender(WorldRenderer wr) { } + public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; } public void Render(WorldRenderer wr) { var wlr = Game.Renderer.WorldLineRenderer; @@ -55,5 +53,6 @@ namespace OpenRA.Mods.Common.Graphics } public void RenderDebugGeometry(WorldRenderer wr) { } + public Rectangle ScreenBounds(WorldRenderer wr) { return Rectangle.Empty; } } } diff --git a/OpenRA.Mods.Common/Graphics/TextRenderable.cs b/OpenRA.Mods.Common/Graphics/TextRenderable.cs index 03e09e26f7..1fe6ee36bc 100644 --- a/OpenRA.Mods.Common/Graphics/TextRenderable.cs +++ b/OpenRA.Mods.Common/Graphics/TextRenderable.cs @@ -14,7 +14,7 @@ using OpenRA.Graphics; namespace OpenRA.Mods.Common.Graphics { - public struct TextRenderable : IRenderable + public struct TextRenderable : IRenderable, IFinalizedRenderable { readonly SpriteFont font; readonly WPos pos; @@ -32,18 +32,16 @@ namespace OpenRA.Mods.Common.Graphics } public WPos Pos { get { return pos; } } - public float Scale { get { return 1f; } } public PaletteReference Palette { get { return null; } } public int ZOffset { get { return zOffset; } } public bool IsDecoration { get { return true; } } - public IRenderable WithScale(float newScale) { return new TextRenderable(font, pos, zOffset, color, text); } public IRenderable WithPalette(PaletteReference newPalette) { return new TextRenderable(font, pos, zOffset, color, text); } public IRenderable WithZOffset(int newOffset) { return new TextRenderable(font, pos, zOffset, color, text); } public IRenderable OffsetBy(WVec vec) { return new TextRenderable(font, pos + vec, zOffset, color, text); } public IRenderable AsDecoration() { return this; } - public void BeforeRender(WorldRenderer wr) { } + public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; } public void Render(WorldRenderer wr) { var screenPos = wr.Viewport.Zoom * (wr.ScreenPosition(pos) - wr.Viewport.TopLeft.ToFloat2()) - 0.5f * font.Measure(text).ToFloat2(); @@ -57,5 +55,7 @@ namespace OpenRA.Mods.Common.Graphics var offset = wr.ScreenPxPosition(pos) - 0.5f * size; Game.Renderer.WorldLineRenderer.DrawRect(offset, offset + size, Color.Red); } + + public Rectangle ScreenBounds(WorldRenderer wr) { return Rectangle.Empty; } } } diff --git a/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs b/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs index 2ff1381906..9898862bf2 100644 --- a/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs +++ b/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs @@ -8,6 +8,7 @@ */ #endregion +using System; using System.Collections.Generic; using System.Drawing; using System.Linq; @@ -29,9 +30,6 @@ namespace OpenRA.Mods.Common.Graphics readonly PaletteReference shadowPalette; readonly float scale; - // Generated at render-time - VoxelRenderProxy renderProxy; - public VoxelRenderable( IEnumerable voxels, WPos pos, int zOffset, WRot camera, float scale, WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor, @@ -48,23 +46,13 @@ namespace OpenRA.Mods.Common.Graphics this.palette = color; this.normalsPalette = normals; this.shadowPalette = shadow; - this.renderProxy = null; } public WPos Pos { get { return pos; } } - public float Scale { get { return scale; } } public PaletteReference Palette { get { return palette; } } public int ZOffset { get { return zOffset; } } public bool IsDecoration { get { return false; } } - public IRenderable WithScale(float newScale) - { - return new VoxelRenderable( - voxels, pos, zOffset, camera, newScale, - lightSource, lightAmbientColor, lightDiffuseColor, - palette, normalsPalette, shadowPalette); - } - public IRenderable WithPalette(PaletteReference newPalette) { return new VoxelRenderable( @@ -93,99 +81,146 @@ namespace OpenRA.Mods.Common.Graphics // This will need generalizing once we support TS/RA2 terrain static readonly float[] GroundNormal = new float[] { 0, 0, 1, 1 }; - public void BeforeRender(WorldRenderer wr) + public IFinalizedRenderable PrepareRender(WorldRenderer wr) { - var draw = voxels.Where(v => v.DisableFunc == null || !v.DisableFunc()); - - renderProxy = Game.Renderer.WorldVoxelRenderer.RenderAsync( - wr, draw, camera, scale, GroundNormal, lightSource, - lightAmbientColor, lightDiffuseColor, - palette, normalsPalette, shadowPalette); + return new FinalizedVoxelRenderable(wr, this); } - public void Render(WorldRenderer wr) + struct FinalizedVoxelRenderable : IFinalizedRenderable { - // TODO: This is a temporary workaround until we have a proper ramp-aware height calculation - var groundPos = wr.World.Map.CenterOfCell(wr.World.Map.CellContaining(pos)); + readonly VoxelRenderable voxel; + readonly VoxelRenderProxy renderProxy; - var ts = Game.ModData.Manifest.TileSize; - var groundZ = ts.Height * (groundPos.Z - pos.Z) / 1024f; - - var pxOrigin = wr.ScreenPosition(pos); - var shadowOrigin = pxOrigin - groundZ * (new float2(renderProxy.ShadowDirection, 1)); - - var psb = renderProxy.ProjectedShadowBounds; - var sa = shadowOrigin + psb[0]; - 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); - } - - public void RenderDebugGeometry(WorldRenderer wr) - { - var pxOrigin = wr.ScreenPosition(pos); - var groundZ = 0.5f * (pxOrigin.Y - wr.ScreenZPosition(pos, 0)); - var shadowOrigin = pxOrigin - groundZ * (new float2(renderProxy.ShadowDirection, 1)); - - // Draw sprite rect - var offset = pxOrigin + renderProxy.Sprite.Offset - 0.5f * renderProxy.Sprite.Size; - Game.Renderer.WorldLineRenderer.DrawRect(offset, offset + renderProxy.Sprite.Size, Color.Red); - - // Draw transformed shadow sprite rect - var c = Color.Purple; - var psb = renderProxy.ProjectedShadowBounds; - Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[1], shadowOrigin + psb[3], c, c); - Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[3], shadowOrigin + psb[0], c, c); - Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[0], shadowOrigin + psb[2], c, c); - Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[2], shadowOrigin + psb[1], c, c); - - // Draw voxel bounding box - var draw = voxels.Where(v => v.DisableFunc == null || !v.DisableFunc()); - var scaleTransform = Util.ScaleMatrix(scale, scale, scale); - var cameraTransform = Util.MakeFloatMatrix(camera.AsMatrix()); - - foreach (var v in draw) + public FinalizedVoxelRenderable(WorldRenderer wr, VoxelRenderable voxel) { - var bounds = v.Voxel.Bounds(v.FrameFunc()); - var worldTransform = v.RotationFunc().Reverse().Aggregate(scaleTransform, - (x, y) => Util.MatrixMultiply(x, Util.MakeFloatMatrix(y.AsMatrix()))); + this.voxel = voxel; + var draw = voxel.voxels.Where(v => v.DisableFunc == null || !v.DisableFunc()); - var pxOffset = wr.ScreenVector(v.OffsetFunc()); - var pxPos = pxOrigin + new float2(pxOffset[0], pxOffset[1]); - var screenTransform = Util.MatrixMultiply(cameraTransform, worldTransform); - DrawBoundsBox(pxPos, screenTransform, bounds, Color.Yellow); - } - } - - static readonly uint[] CornerXIndex = new uint[] { 0, 0, 0, 0, 3, 3, 3, 3 }; - static readonly uint[] CornerYIndex = new uint[] { 1, 1, 4, 4, 1, 1, 4, 4 }; - static readonly uint[] CornerZIndex = new uint[] { 2, 5, 2, 5, 2, 5, 2, 5 }; - static void DrawBoundsBox(float2 pxPos, float[] transform, float[] bounds, Color c) - { - var corners = new float2[8]; - for (var i = 0; i < 8; i++) - { - var vec = new float[] { bounds[CornerXIndex[i]], bounds[CornerYIndex[i]], bounds[CornerZIndex[i]], 1 }; - var screen = Util.MatrixVectorMultiply(transform, vec); - corners[i] = pxPos + new float2(screen[0], screen[1]); + renderProxy = Game.Renderer.WorldVoxelRenderer.RenderAsync( + wr, draw, voxel.camera, voxel.scale, VoxelRenderable.GroundNormal, voxel.lightSource, + voxel.lightAmbientColor, voxel.lightDiffuseColor, + voxel.palette, voxel.normalsPalette, voxel.shadowPalette); } - Game.Renderer.WorldLineRenderer.DrawLine(corners[0], corners[1], c, c); - Game.Renderer.WorldLineRenderer.DrawLine(corners[1], corners[3], c, c); - Game.Renderer.WorldLineRenderer.DrawLine(corners[3], corners[2], c, c); - Game.Renderer.WorldLineRenderer.DrawLine(corners[2], corners[0], c, c); + public void Render(WorldRenderer wr) + { + // TODO: This is a temporary workaround until we have a proper ramp-aware height calculation + var groundPos = wr.World.Map.CenterOfCell(wr.World.Map.CellContaining(voxel.pos)); - Game.Renderer.WorldLineRenderer.DrawLine(corners[4], corners[5], c, c); - Game.Renderer.WorldLineRenderer.DrawLine(corners[5], corners[7], c, c); - Game.Renderer.WorldLineRenderer.DrawLine(corners[7], corners[6], c, c); - Game.Renderer.WorldLineRenderer.DrawLine(corners[6], corners[4], c, c); + var ts = Game.ModData.Manifest.TileSize; + var groundZ = ts.Height * (groundPos.Z - voxel.pos.Z) / 1024f; - Game.Renderer.WorldLineRenderer.DrawLine(corners[0], corners[4], c, c); - Game.Renderer.WorldLineRenderer.DrawLine(corners[1], corners[5], c, c); - Game.Renderer.WorldLineRenderer.DrawLine(corners[2], corners[6], c, c); - Game.Renderer.WorldLineRenderer.DrawLine(corners[3], corners[7], c, c); + var pxOrigin = wr.ScreenPosition(voxel.pos); + var shadowOrigin = pxOrigin - groundZ * (new float2(renderProxy.ShadowDirection, 1)); + + var psb = renderProxy.ProjectedShadowBounds; + var sa = shadowOrigin + psb[0]; + 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); + } + + public void RenderDebugGeometry(WorldRenderer wr) + { + var pxOrigin = wr.ScreenPosition(voxel.pos); + var groundZ = 0.5f * (pxOrigin.Y - wr.ScreenZPosition(voxel.pos, 0)); + var shadowOrigin = pxOrigin - groundZ * (new float2(renderProxy.ShadowDirection, 1)); + + // Draw sprite rect + var offset = pxOrigin + renderProxy.Sprite.Offset - 0.5f * renderProxy.Sprite.Size; + Game.Renderer.WorldLineRenderer.DrawRect(offset, offset + renderProxy.Sprite.Size, Color.Red); + + // Draw transformed shadow sprite rect + var c = Color.Purple; + var psb = renderProxy.ProjectedShadowBounds; + Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[1], shadowOrigin + psb[3], c, c); + Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[3], shadowOrigin + psb[0], c, c); + Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[0], shadowOrigin + psb[2], c, c); + Game.Renderer.WorldLineRenderer.DrawLine(shadowOrigin + psb[2], shadowOrigin + psb[1], c, c); + + // Draw voxel bounding box + var draw = voxel.voxels.Where(v => v.DisableFunc == null || !v.DisableFunc()); + var scaleTransform = Util.ScaleMatrix(voxel.scale, voxel.scale, voxel.scale); + var cameraTransform = Util.MakeFloatMatrix(voxel.camera.AsMatrix()); + + foreach (var v in draw) + { + var bounds = v.Voxel.Bounds(v.FrameFunc()); + var worldTransform = v.RotationFunc().Reverse().Aggregate(scaleTransform, + (x, y) => Util.MatrixMultiply(x, Util.MakeFloatMatrix(y.AsMatrix()))); + + var pxOffset = wr.ScreenVector(v.OffsetFunc()); + var pxPos = pxOrigin + new float2(pxOffset[0], pxOffset[1]); + var screenTransform = Util.MatrixMultiply(cameraTransform, worldTransform); + DrawBoundsBox(pxPos, screenTransform, bounds, Color.Yellow); + } + } + + static readonly uint[] CornerXIndex = new uint[] { 0, 0, 0, 0, 3, 3, 3, 3 }; + static readonly uint[] CornerYIndex = new uint[] { 1, 1, 4, 4, 1, 1, 4, 4 }; + static readonly uint[] CornerZIndex = new uint[] { 2, 5, 2, 5, 2, 5, 2, 5 }; + static void DrawBoundsBox(float2 pxPos, float[] transform, float[] bounds, Color c) + { + var corners = new float2[8]; + for (var i = 0; i < 8; i++) + { + var vec = new float[] { bounds[CornerXIndex[i]], bounds[CornerYIndex[i]], bounds[CornerZIndex[i]], 1 }; + var screen = Util.MatrixVectorMultiply(transform, vec); + corners[i] = pxPos + new float2(screen[0], screen[1]); + } + + Game.Renderer.WorldLineRenderer.DrawLine(corners[0], corners[1], c, c); + Game.Renderer.WorldLineRenderer.DrawLine(corners[1], corners[3], c, c); + Game.Renderer.WorldLineRenderer.DrawLine(corners[3], corners[2], c, c); + Game.Renderer.WorldLineRenderer.DrawLine(corners[2], corners[0], c, c); + + Game.Renderer.WorldLineRenderer.DrawLine(corners[4], corners[5], c, c); + Game.Renderer.WorldLineRenderer.DrawLine(corners[5], corners[7], c, c); + Game.Renderer.WorldLineRenderer.DrawLine(corners[7], corners[6], c, c); + Game.Renderer.WorldLineRenderer.DrawLine(corners[6], corners[4], c, c); + + Game.Renderer.WorldLineRenderer.DrawLine(corners[0], corners[4], c, c); + Game.Renderer.WorldLineRenderer.DrawLine(corners[1], corners[5], c, c); + Game.Renderer.WorldLineRenderer.DrawLine(corners[2], corners[6], c, c); + Game.Renderer.WorldLineRenderer.DrawLine(corners[3], corners[7], c, c); + } + + public Rectangle ScreenBounds(WorldRenderer wr) + { + var pxOrigin = wr.ScreenPosition(voxel.pos); + var draw = voxel.voxels.Where(v => v.DisableFunc == null || !v.DisableFunc()); + var scaleTransform = Util.ScaleMatrix(voxel.scale, voxel.scale, voxel.scale); + var cameraTransform = Util.MakeFloatMatrix(voxel.camera.AsMatrix()); + + var minX = float.MaxValue; + var minY = float.MaxValue; + var maxX = float.MinValue; + var maxY = float.MinValue; + foreach (var v in draw) + { + var bounds = v.Voxel.Bounds(v.FrameFunc()); + var worldTransform = v.RotationFunc().Reverse().Aggregate(scaleTransform, + (x, y) => Util.MatrixMultiply(x, Util.MakeFloatMatrix(y.AsMatrix()))); + + var pxOffset = wr.ScreenVector(v.OffsetFunc()); + var pxPos = pxOrigin + new float2(pxOffset[0], pxOffset[1]); + var screenTransform = Util.MatrixMultiply(cameraTransform, worldTransform); + + for (var i = 0; i < 8; i++) + { + var vec = new float[] { bounds[CornerXIndex[i]], bounds[CornerYIndex[i]], bounds[CornerZIndex[i]], 1 }; + var screen = Util.MatrixVectorMultiply(screenTransform, vec); + minX = Math.Min(minX, pxPos.X + screen[0]); + minY = Math.Min(minY, pxPos.Y + screen[1]); + maxX = Math.Max(maxX, pxPos.X + screen[0]); + maxY = Math.Max(maxY, pxPos.Y + screen[1]); + } + } + + return Rectangle.FromLTRB((int)minX, (int)minY, (int)maxX, (int)maxY); + } } } } diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index a9ea82b64c..9f94976bdb 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -525,6 +525,7 @@ + diff --git a/OpenRA.Mods.Common/Traits/Render/WithTurret.cs b/OpenRA.Mods.Common/Traits/Render/WithTurret.cs index 719b820e7b..bd1138950a 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithTurret.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithTurret.cs @@ -39,10 +39,14 @@ namespace OpenRA.Mods.Common.Traits var t = init.Actor.Traits.WithInterface() .First(tt => tt.Turret == Turret); - var anim = new Animation(init.World, image, () => t.InitialFacing); + var ifacing = init.Actor.Traits.GetOrDefault(); + var bodyFacing = ifacing != null ? init.Contains() ? init.Get() : ifacing.GetInitialFacing() : 0; + var turretFacing = init.Contains() ? init.Get() : t.InitialFacing; + + var anim = new Animation(init.World, image, () => turretFacing); anim.Play(Sequence); - var orientation = body.QuantizeOrientation(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(t.InitialFacing)), facings); + var orientation = body.QuantizeOrientation(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(bodyFacing)), facings); var offset = body.LocalToWorld(t.Offset.Rotate(orientation)); yield return new SpriteActorPreview(anim, offset, offset.Y + offset.Z + 1, p, rs.Scale); } diff --git a/OpenRA.Mods.Common/Widgets/ActorPreviewWidget.cs b/OpenRA.Mods.Common/Widgets/ActorPreviewWidget.cs new file mode 100644 index 0000000000..08eb24e567 --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/ActorPreviewWidget.cs @@ -0,0 +1,116 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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; +using System.Linq; +using OpenRA.Graphics; +using OpenRA.Mods.Common.Graphics; +using OpenRA.Mods.Common.Traits; +using OpenRA.Primitives; +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets +{ + public class ActorPreviewWidget : Widget + { + public bool Animate = false; + public Func GetScale = () => 1f; + + readonly WorldRenderer worldRenderer; + + IActorPreview[] preview = new IActorPreview[0]; + public int2 PreviewOffset { get; private set; } + public int2 IdealPreviewSize { get; private set; } + + [ObjectCreator.UseCtor] + public ActorPreviewWidget(WorldRenderer worldRenderer) + { + this.worldRenderer = worldRenderer; + } + + protected ActorPreviewWidget(ActorPreviewWidget other) + : base(other) + { + preview = other.preview; + worldRenderer = other.worldRenderer; + } + + public override Widget Clone() { return new ActorPreviewWidget(this); } + + public void SetPreview(ActorInfo actor, Player owner, TypeDictionary td) + { + var init = new ActorPreviewInitializer(actor, owner, worldRenderer, td); + preview = actor.Traits.WithInterface() + .SelectMany(rpi => rpi.RenderPreview(init)) + .ToArray(); + + // Calculate the preview bounds + PreviewOffset = int2.Zero; + IdealPreviewSize = int2.Zero; + + var r = preview + .SelectMany(p => p.Render(worldRenderer, WPos.Zero)) + .OrderBy(WorldRenderer.RenderableScreenZPositionComparisonKey) + .Select(rr => rr.PrepareRender(worldRenderer)); + + if (r.Any()) + { + var b = r.First().ScreenBounds(worldRenderer); + foreach (var rr in r.Skip(1)) + b = Rectangle.Union(b, rr.ScreenBounds(worldRenderer)); + + IdealPreviewSize = new int2(b.Width, b.Height); + PreviewOffset = -new int2(b.Left, b.Top) - IdealPreviewSize / 2; + } + } + + IFinalizedRenderable[] renderables; + public override void PrepareRenderables() + { + renderables = preview + .SelectMany(p => p.Render(worldRenderer, WPos.Zero)) + .OrderBy(WorldRenderer.RenderableScreenZPositionComparisonKey) + .Select(r => r.PrepareRender(worldRenderer)) + .ToArray(); + } + + public override void Draw() + { + // HACK: The split between world and UI shaders is a giant PITA because it isn't + // feasible to maintain two parallel sets of renderables for the two cases. + // Instead, we temporarily hijack the world rendering context and set the position + // and zoom values to give the desired screen position and size. + var scale = GetScale(); + var origin = RenderOrigin + new int2(RenderBounds.Size.Width / 2, RenderBounds.Size.Height / 2); + + // The scale affects world -> screen transform, which we don't want when drawing the (fixed) UI. + if (scale != 1f) + origin = (1f / scale * origin.ToFloat2()).ToInt2(); + + Game.Renderer.Flush(); + Game.Renderer.SetViewportParams(-origin - PreviewOffset, scale); + + foreach (var r in renderables) + r.Render(worldRenderer); + + Game.Renderer.Flush(); + Game.Renderer.SetViewportParams(worldRenderer.Viewport.TopLeft, worldRenderer.Viewport.Zoom); + } + + public override void Tick() + { + if (Animate) + foreach (var p in preview) + p.Tick(); + } + } +} diff --git a/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs index 2c4f119c14..a44ee3e553 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs @@ -10,6 +10,7 @@ using System; using OpenRA.Graphics; +using OpenRA.Primitives; using OpenRA.Widgets; namespace OpenRA.Mods.Common.Widgets.Logic @@ -17,16 +18,15 @@ namespace OpenRA.Mods.Common.Widgets.Logic public class ColorPickerLogic { [ObjectCreator.UseCtor] - public ColorPickerLogic(Widget widget, HSLColor initialColor, Action onChange, WorldRenderer worldRenderer) + public ColorPickerLogic(Widget widget, World world, HSLColor initialColor, Action onChange, WorldRenderer worldRenderer) { - var ticker = widget.GetOrNull("ANIMATE_PREVIEW"); - if (ticker != null) - { - var preview = widget.Get("PREVIEW"); - var anim = preview.GetAnimation(); - anim.PlayRepeating(anim.CurrentSequence.Name); - ticker.OnTick = anim.Tick; - } + string actorType; + if (!ChromeMetrics.TryGet("ColorPickerActorType", out actorType)) + actorType = "mcv"; + + var preview = widget.GetOrNull("PREVIEW"); + var actor = world.Map.Rules.Actors[actorType]; + preview.SetPreview(actor, world.WorldActor.Owner, new TypeDictionary()); var hueSlider = widget.Get("HUE"); var mixer = widget.Get("MIXER"); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs index 72f92d53bd..39ddcde156 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs @@ -32,6 +32,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic readonly OrderManager orderManager; readonly bool skirmishMode; readonly Ruleset modRules; + readonly World shellmapWorld; enum PanelType { Players, Options, Kick, ForceStart } PanelType panel = PanelType.Players; @@ -108,6 +109,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic this.onExit = onExit; this.skirmishMode = skirmishMode; this.modRules = modRules; + shellmapWorld = worldRenderer.World; orderManager.AddChatLine += AddChatLine; Game.LobbyInfoChanged += UpdateCurrentMap; @@ -699,7 +701,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic else LobbyUtils.SetupEditableNameWidget(template, slot, client, orderManager); - LobbyUtils.SetupEditableColorWidget(template, slot, client, orderManager, colorPreview); + LobbyUtils.SetupEditableColorWidget(template, slot, client, orderManager, shellmapWorld, colorPreview); LobbyUtils.SetupEditableFactionWidget(template, slot, client, orderManager, countries); LobbyUtils.SetupEditableTeamWidget(template, slot, client, orderManager, Map); LobbyUtils.SetupEditableSpawnWidget(template, slot, client, orderManager, Map); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs index 4b3dd4bf5a..446bcb014c 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs @@ -127,7 +127,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic } public static void ShowColorDropDown(DropDownButtonWidget color, Session.Client client, - OrderManager orderManager, ColorPreviewManagerWidget preview) + OrderManager orderManager, World world, ColorPreviewManagerWidget preview) { Action onExit = () => { @@ -143,7 +143,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic Action onChange = c => preview.Color = c; - var colorChooser = Game.LoadWidget(orderManager.World, "COLOR_CHOOSER", null, new WidgetArgs() + var colorChooser = Game.LoadWidget(world, "COLOR_CHOOSER", null, new WidgetArgs() { { "onChange", onChange }, { "initialColor", client.Color } @@ -377,11 +377,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic }; } - public static void SetupEditableColorWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, ColorPreviewManagerWidget colorPreview) + public static void SetupEditableColorWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, World world, ColorPreviewManagerWidget colorPreview) { var color = parent.Get("COLOR"); color.IsDisabled = () => (s != null && s.LockColor) || orderManager.LocalClient.IsReady; - color.OnMouseDown = _ => ShowColorDropDown(color, c, orderManager, colorPreview); + color.OnMouseDown = _ => ShowColorDropDown(color, c, orderManager, world, colorPreview); SetupColorWidget(color, s, c); } diff --git a/OpenRA.Mods.RA/Graphics/TeslaZapRenderable.cs b/OpenRA.Mods.RA/Graphics/TeslaZapRenderable.cs index 8afabe99b2..28d25da0ef 100644 --- a/OpenRA.Mods.RA/Graphics/TeslaZapRenderable.cs +++ b/OpenRA.Mods.RA/Graphics/TeslaZapRenderable.cs @@ -10,12 +10,13 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.Linq; using OpenRA.Graphics; namespace OpenRA.Mods.RA.Graphics { - struct TeslaZapRenderable : IRenderable + struct TeslaZapRenderable : IRenderable, IFinalizedRenderable { static int[][] steps = new[] { @@ -38,7 +39,7 @@ namespace OpenRA.Mods.RA.Graphics WPos cachedPos; WVec cachedLength; - IEnumerable cache; + IEnumerable cache; public TeslaZapRenderable(WPos pos, int zOffset, WVec length, string image, int brightZaps, int dimZaps, string palette) { @@ -52,22 +53,20 @@ namespace OpenRA.Mods.RA.Graphics cachedPos = WPos.Zero; cachedLength = WVec.Zero; - cache = new IRenderable[] { }; + cache = new IFinalizedRenderable[] { }; } public WPos Pos { get { return pos; } } - public float Scale { get { return 1f; } } public PaletteReference Palette { get { return null; } } public int ZOffset { get { return zOffset; } } public bool IsDecoration { get { return true; } } - public IRenderable WithScale(float newScale) { return new TeslaZapRenderable(pos, zOffset, length, image, brightZaps, dimZaps, palette); } public IRenderable WithPalette(PaletteReference newPalette) { return new TeslaZapRenderable(pos, zOffset, length, image, brightZaps, dimZaps, palette); } public IRenderable WithZOffset(int newOffset) { return new TeslaZapRenderable(pos, zOffset, length, image, brightZaps, dimZaps, palette); } public IRenderable OffsetBy(WVec vec) { return new TeslaZapRenderable(pos + vec, zOffset, length, image, brightZaps, dimZaps, palette); } public IRenderable AsDecoration() { return this; } - public void BeforeRender(WorldRenderer wr) { } + public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; } public void RenderDebugGeometry(WorldRenderer wr) { } public void Render(WorldRenderer wr) { @@ -77,7 +76,9 @@ namespace OpenRA.Mods.RA.Graphics cache.Do(c => c.Render(wr)); } - public IEnumerable GenerateRenderables(WorldRenderer wr) + public Rectangle ScreenBounds(WorldRenderer wr) { return Rectangle.Empty; } + + public IEnumerable GenerateRenderables(WorldRenderer wr) { var bright = wr.World.Map.SequenceProvider.GetSequence(image, "bright"); var dim = wr.World.Map.SequenceProvider.GetSequence(image, "dim"); @@ -93,13 +94,13 @@ namespace OpenRA.Mods.RA.Graphics yield return z; } - static IEnumerable DrawZapWandering(WorldRenderer wr, float2 from, float2 to, Sequence s, string pal) + static IEnumerable DrawZapWandering(WorldRenderer wr, float2 from, float2 to, Sequence s, string pal) { var z = float2.Zero; /* hack */ var dist = to - from; var norm = (1f / dist.Length) * new float2(-dist.Y, dist.X); - var renderables = new List(); + var renderables = new List(); if (Game.CosmeticRandom.Next(2) != 0) { var p1 = from + (1 / 3f) * dist + WRange.FromPDF(Game.CosmeticRandom, 2).Range * dist.Length / 4096 * norm; @@ -120,12 +121,12 @@ namespace OpenRA.Mods.RA.Graphics return renderables; } - static IEnumerable DrawZap(WorldRenderer wr, float2 from, float2 to, Sequence s, out float2 p, string palette) + static IEnumerable DrawZap(WorldRenderer wr, float2 from, float2 to, Sequence s, out float2 p, string palette) { var dist = to - from; var q = new float2(-dist.Y, dist.X); var c = -float2.Dot(from, q); - var rs = new List(); + var rs = new List(); var z = from; var pal = wr.Palette(palette); @@ -135,7 +136,7 @@ namespace OpenRA.Mods.RA.Graphics .MinBy(t => Math.Abs(float2.Dot(z + new float2(t[0], t[1]), q) + c)); var pos = wr.Position((z + new float2(step[2], step[3])).ToInt2()); - rs.Add(new SpriteRenderable(s.GetSprite(step[4]), pos, WVec.Zero, 0, pal, 1f, true)); + rs.Add(new SpriteRenderable(s.GetSprite(step[4]), pos, WVec.Zero, 0, pal, 1f, true).PrepareRender(wr)); z += new float2(step[0], step[1]); if (rs.Count >= 1000) diff --git a/OpenRA.Mods.TS/Traits/Render/RenderVoxels.cs b/OpenRA.Mods.TS/Traits/Render/RenderVoxels.cs index 7b7423f634..d60c9b6909 100644 --- a/OpenRA.Mods.TS/Traits/Render/RenderVoxels.cs +++ b/OpenRA.Mods.TS/Traits/Render/RenderVoxels.cs @@ -49,7 +49,8 @@ namespace OpenRA.Mods.TS.Traits var facings = body.QuantizedFacings == -1 ? init.Actor.Traits.Get().QuantizedBodyFacings(sequenceProvider, init.Actor) : body.QuantizedFacings; var palette = init.WorldRenderer.Palette(Palette ?? (init.Owner != null ? PlayerPalette + init.Owner.InternalName : null)); - var facing = init.Contains() ? init.Get() : 0; + var ifacing = init.Actor.Traits.GetOrDefault(); + var facing = ifacing != null ? init.Contains() ? init.Get() : ifacing.GetInitialFacing() : 0; var orientation = WRot.FromFacing(facing); var components = init.Actor.Traits.WithInterface() .SelectMany(rvpi => rvpi.RenderPreviewVoxels(init, this, image, orientation, facings, palette)) diff --git a/OpenRA.Mods.TS/Traits/Render/WithVoxelBarrel.cs b/OpenRA.Mods.TS/Traits/Render/WithVoxelBarrel.cs index c511f1cf5d..e0ce270ab3 100644 --- a/OpenRA.Mods.TS/Traits/Render/WithVoxelBarrel.cs +++ b/OpenRA.Mods.TS/Traits/Render/WithVoxelBarrel.cs @@ -37,7 +37,9 @@ namespace OpenRA.Mods.TS.Traits .First(tt => tt.Turret == armament.Turret); var voxel = VoxelProvider.GetVoxel(image, Sequence); - var turretOrientation = body.QuantizeOrientation(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(t.InitialFacing) - orientation.Yaw), facings); + + var turretFacing = init.Contains() ? init.Get() : t.InitialFacing; + var turretOrientation = body.QuantizeOrientation(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(turretFacing) - orientation.Yaw), facings); var turretOffset = body.LocalToWorld(t.Offset.Rotate(orientation)); yield return new VoxelAnimation(voxel, () => turretOffset, () => new[] { turretOrientation, orientation }, diff --git a/OpenRA.Mods.TS/Traits/Render/WithVoxelTurret.cs b/OpenRA.Mods.TS/Traits/Render/WithVoxelTurret.cs index 4fadd909a2..2aacc0e0f8 100644 --- a/OpenRA.Mods.TS/Traits/Render/WithVoxelTurret.cs +++ b/OpenRA.Mods.TS/Traits/Render/WithVoxelTurret.cs @@ -36,7 +36,8 @@ namespace OpenRA.Mods.TS.Traits var voxel = VoxelProvider.GetVoxel(image, Sequence); var turretOffset = body.LocalToWorld(t.Offset.Rotate(orientation)); - var turretBodyOrientation = new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(t.InitialFacing) - orientation.Yaw); + var turretFacing = init.Contains() ? init.Get() : t.InitialFacing; + var turretBodyOrientation = new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(turretFacing) - orientation.Yaw); var turretOrientation = new[] { turretBodyOrientation, body.QuantizeOrientation(orientation, facings) }; yield return new VoxelAnimation(voxel, () => turretOffset, () => turretOrientation, () => false, () => 0); } diff --git a/mods/cnc/chrome/color-picker.yaml b/mods/cnc/chrome/color-picker.yaml index a659b56bc7..8e03d21587 100644 --- a/mods/cnc/chrome/color-picker.yaml +++ b/mods/cnc/chrome/color-picker.yaml @@ -30,14 +30,12 @@ Background@COLOR_CHOOSER: Y: 2 Width: 144 Height: 72 - SpriteSequence@PREVIEW: + ActorPreview@PREVIEW: X: 153 Y: 1 Width: 80 Height: 73 - Palette: colorpicker - Unit: fact - Sequence: idle + Animate: true Button@RANDOM_BUTTON: Key: tab X: 158 diff --git a/mods/cnc/metrics.yaml b/mods/cnc/metrics.yaml index 13c4b0da05..81e18610c7 100644 --- a/mods/cnc/metrics.yaml +++ b/mods/cnc/metrics.yaml @@ -20,6 +20,7 @@ Metrics: TextContrast: false TextContrastColor: 0,0,0 ColorPickerRemapIndices: 176, 178, 180, 182, 184, 186, 189, 191, 177, 179, 181, 183, 185, 187, 188, 190 + ColorPickerActorType: ^fact.colorpicker SpawnFont: TinyBold SpawnColor: 255,255,255 SpawnContrastColor: 0,0,0 diff --git a/mods/cnc/rules/misc.yaml b/mods/cnc/rules/misc.yaml index 6982a8be0d..384137e931 100644 --- a/mods/cnc/rules/misc.yaml +++ b/mods/cnc/rules/misc.yaml @@ -42,6 +42,12 @@ waypoint: RenderEditorOnly: BodyOrientation: +^fact.colorpicker: + Inherits: FACT + RenderBuilding: + Image: fact + Palette: colorpicker + CAMERA: Immobile: OccupiesSpace: false diff --git a/mods/d2k/chrome/color-picker.yaml b/mods/d2k/chrome/color-picker.yaml index d2d4a2a792..5427cd376f 100644 --- a/mods/d2k/chrome/color-picker.yaml +++ b/mods/d2k/chrome/color-picker.yaml @@ -29,15 +29,11 @@ Background@COLOR_CHOOSER: Y: 2 Width: 144 Height: 72 - SpriteSequence@FACT: + ActorPreview@PREVIEW: X: 153 Y: 1 Width: 80 Height: 73 - Palette: colorpicker - Unit: carryall - Sequence: idle - Facing: 104 Button@RANDOM_BUTTON: Key: tab X: 158 diff --git a/mods/d2k/metrics.yaml b/mods/d2k/metrics.yaml index 0d1147ea49..b0cd208e75 100644 --- a/mods/d2k/metrics.yaml +++ b/mods/d2k/metrics.yaml @@ -20,6 +20,7 @@ Metrics: TextContrast: false TextContrastColor: 0,0,0 ColorPickerRemapIndices: 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240 + ColorPickerActorType: ^carryall.colorpicker SpawnFont: TinyBold SpawnColor: 255,255,255 SpawnContrastColor: 0,0,0 diff --git a/mods/d2k/rules/misc.yaml b/mods/d2k/rules/misc.yaml index 57084a2a1c..d878294ffa 100644 --- a/mods/d2k/rules/misc.yaml +++ b/mods/d2k/rules/misc.yaml @@ -140,6 +140,14 @@ waypoint: RenderEditorOnly: BodyOrientation: +^carryall.colorpicker: + Inherits: ^CARRYALL + RenderUnit: + Image: carryall + Palette: colorpicker + Helicopter: + InitialFacing: 104 + CAMERA: Immobile: OccupiesSpace: false diff --git a/mods/ra/chrome/color-picker.yaml b/mods/ra/chrome/color-picker.yaml index e5b790203c..5427cd376f 100644 --- a/mods/ra/chrome/color-picker.yaml +++ b/mods/ra/chrome/color-picker.yaml @@ -29,14 +29,11 @@ Background@COLOR_CHOOSER: Y: 2 Width: 144 Height: 72 - SpriteSequence@FACT: + ActorPreview@PREVIEW: X: 153 Y: 1 Width: 80 Height: 73 - Palette: colorpicker - Unit: fact - Sequence: idle Button@RANDOM_BUTTON: Key: tab X: 158 diff --git a/mods/ra/metrics.yaml b/mods/ra/metrics.yaml index ce65490323..7e05014dc7 100644 --- a/mods/ra/metrics.yaml +++ b/mods/ra/metrics.yaml @@ -20,6 +20,7 @@ Metrics: TextContrast: false TextContrastColor: 0,0,0 ColorPickerRemapIndices: 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95 + ColorPickerActorType: ^fact.colorpicker SpawnFont: TinyBold SpawnColor: 255,255,255 SpawnContrastColor: 0,0,0 diff --git a/mods/ra/rules/misc.yaml b/mods/ra/rules/misc.yaml index 5cdfc30393..b9bf3cbf1e 100644 --- a/mods/ra/rules/misc.yaml +++ b/mods/ra/rules/misc.yaml @@ -368,6 +368,12 @@ waypoint: RenderEditorOnly: BodyOrientation: +^fact.colorpicker: + Inherits: FACT + RenderBuilding: + Image: fact + Palette: colorpicker + CTFLAG: Inherits: ^TechBuilding Building: diff --git a/mods/ts/chrome/color-picker.yaml b/mods/ts/chrome/color-picker.yaml index 61ddd97e38..f027657764 100644 --- a/mods/ts/chrome/color-picker.yaml +++ b/mods/ts/chrome/color-picker.yaml @@ -29,14 +29,11 @@ Background@COLOR_CHOOSER: Y: 2 Width: 144 Height: 72 - SpriteSequence@PREVIEW: - X: 153 - Y: 1-40 + ActorPreview@PREVIEW: + X: 163 Width: 80 - Height: 73 - Palette: colorpicker - Unit: gacnst - Sequence: make + Height: 74 + Animate: true Button@RANDOM_BUTTON: Key: tab X: 158 diff --git a/mods/ts/metrics.yaml b/mods/ts/metrics.yaml index b6222a2053..ee97b3c6f5 100644 --- a/mods/ts/metrics.yaml +++ b/mods/ts/metrics.yaml @@ -20,6 +20,7 @@ Metrics: TextContrast: false TextContrastColor: 0,0,0 ColorPickerRemapIndices: 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 + ColorPickerActorType: ^mmch.colorpicker SpawnFont: TinyBold SpawnColor: 255,255,255 SpawnContrastColor: 0,0,0 diff --git a/mods/ts/rules/misc.yaml b/mods/ts/rules/misc.yaml index 92f58e10b5..5c43c50251 100644 --- a/mods/ts/rules/misc.yaml +++ b/mods/ts/rules/misc.yaml @@ -10,6 +10,20 @@ waypoint: RenderEditorOnly: BodyOrientation: +^mmch.colorpicker: + Inherits: MMCH + Mobile: + InitialFacing: 160 + Turreted: + InitialFacing: 160 + RenderInfantry: + Image: mmch + StandAnimations: run + Palette: colorpicker + RenderVoxels: + Image: mmch + Palette: colorpicker + CAMERA: Immobile: OccupiesSpace: false diff --git a/mods/ts/rules/world.yaml b/mods/ts/rules/world.yaml index 9675140a65..6cadda1463 100644 --- a/mods/ts/rules/world.yaml +++ b/mods/ts/rules/world.yaml @@ -37,6 +37,7 @@ World: Name: colorpicker Filename: unittem.pal AllowModifiers: false + ShadowIndex: 1 PaletteFromRGBA@shadow: Name: shadow R: 0