From 0a374e22648d3b55648918f6560ba6f99f52a08b Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Wed, 30 Dec 2020 18:42:25 +0000 Subject: [PATCH] Move ownership of tile sprites to the terrain renderer. --- OpenRA.Game/Graphics/Theater.cs | 26 ---------- OpenRA.Game/Graphics/WorldRenderer.cs | 3 -- OpenRA.Mods.Common/Traits/Buildings/Bridge.cs | 9 +++- .../Traits/World/EditorCursorLayer.cs | 6 ++- .../Traits/World/TerrainRenderer.cs | 48 ++++++++++++++++--- OpenRA.Mods.Common/TraitsInterfaces.cs | 11 +++++ .../Widgets/Logic/Editor/TileSelectorLogic.cs | 9 +++- .../Widgets/TerrainTemplatePreviewWidget.cs | 12 +++-- .../Traits/World/BuildableTerrainLayer.cs | 10 ++-- 9 files changed, 84 insertions(+), 50 deletions(-) diff --git a/OpenRA.Game/Graphics/Theater.cs b/OpenRA.Game/Graphics/Theater.cs index a796989d4e..3a6a6f6c96 100644 --- a/OpenRA.Game/Graphics/Theater.cs +++ b/OpenRA.Game/Graphics/Theater.cs @@ -162,32 +162,6 @@ namespace OpenRA.Graphics return template.Sprites[start * template.Stride + r.Index]; } - public Rectangle TemplateBounds(TerrainTemplateInfo template, Size tileSize, MapGridType mapGrid) - { - Rectangle? templateRect = null; - - var i = 0; - for (var y = 0; y < template.Size.Y; y++) - { - for (var x = 0; x < template.Size.X; x++) - { - var tile = new TerrainTile(template.Id, (byte)(i++)); - if (!tileset.TryGetTileInfo(tile, out var tileInfo)) - continue; - - var sprite = TileSprite(tile); - var u = mapGrid == MapGridType.Rectangular ? x : (x - y) / 2f; - var v = mapGrid == MapGridType.Rectangular ? y : (x + y) / 2f; - - var tl = new float2(u * tileSize.Width, (v - 0.5f * tileInfo.Height) * tileSize.Height) - 0.5f * sprite.Size; - var rect = new Rectangle((int)(tl.X + sprite.Offset.X), (int)(tl.Y + sprite.Offset.Y), (int)sprite.Size.X, (int)sprite.Size.Y); - templateRect = templateRect.HasValue ? Rectangle.Union(templateRect.Value, rect) : rect; - } - } - - return templateRect.HasValue ? templateRect.Value : Rectangle.Empty; - } - public Sheet Sheet { get { return sheetBuilder.Current; } } public void Dispose() diff --git a/OpenRA.Game/Graphics/WorldRenderer.cs b/OpenRA.Game/Graphics/WorldRenderer.cs index f63b22d0a5..97b87baa3b 100644 --- a/OpenRA.Game/Graphics/WorldRenderer.cs +++ b/OpenRA.Game/Graphics/WorldRenderer.cs @@ -26,7 +26,6 @@ namespace OpenRA.Graphics public readonly Size TileSize; public readonly int TileScale; public readonly World World; - public readonly Theater Theater; public Viewport Viewport { get; private set; } public readonly ITerrainLighting TerrainLighting; @@ -68,7 +67,6 @@ namespace OpenRA.Graphics palette.Initialize(); - Theater = new Theater(world.Map.Rules.TileSet); TerrainLighting = world.WorldActor.TraitOrDefault(); terrainRenderer = world.WorldActor.TraitOrDefault(); @@ -425,7 +423,6 @@ namespace OpenRA.Graphics World.Dispose(); palette.Dispose(); - Theater.Dispose(); } } } diff --git a/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs b/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs index 9263cdafde..6cfdc15dca 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs @@ -53,6 +53,9 @@ namespace OpenRA.Mods.Common.Traits public void RulesetLoaded(Ruleset rules, ActorInfo ai) { + if (!rules.Actors["world"].HasTraitInfo()) + throw new YamlException("Bridge requires a tile-based terrain renderer."); + if (string.IsNullOrEmpty(DemolishWeapon)) throw new YamlException("A value for DemolishWeapon of a Bridge trait is missing."); @@ -93,6 +96,7 @@ namespace OpenRA.Mods.Common.Traits readonly BuildingInfo buildingInfo; readonly Bridge[] neighbours = new Bridge[2]; readonly LegacyBridgeHut[] huts = new LegacyBridgeHut[2]; // Huts before this / first & after this / last + readonly ITiledTerrainRenderer terrainRenderer; readonly Health health; readonly Actor self; readonly BridgeInfo info; @@ -114,6 +118,7 @@ namespace OpenRA.Mods.Common.Traits type = self.Info.Name; isDangling = new Lazy(() => huts[0] == huts[1] && (neighbours[0] == null || neighbours[1] == null)); buildingInfo = self.Info.TraitInfo(); + terrainRenderer = self.World.WorldActor.Trait(); } public Bridge Neighbour(int direction) { return neighbours[direction]; } @@ -198,7 +203,7 @@ namespace OpenRA.Mods.Common.Traits var offset = buildingInfo.CenterOffset(self.World).Y + 1024; return footprint.Select(c => (IRenderable)(new SpriteRenderable( - wr.Theater.TileSprite(new TerrainTile(template, c.Value)), + terrainRenderer.TileSprite(new TerrainTile(template, c.Value)), wr.World.Map.CenterOfCell(c.Key), WVec.Zero, -offset, palette, 1f, true))).ToArray(); } @@ -224,7 +229,7 @@ namespace OpenRA.Mods.Common.Traits foreach (var kv in footprint) { var xy = wr.ScreenPxPosition(wr.World.Map.CenterOfCell(kv.Key)); - var size = wr.Theater.TileSprite(new TerrainTile(template, kv.Value)).Bounds.Size; + var size = terrainRenderer.TileSprite(new TerrainTile(template, kv.Value)).Bounds.Size; // Add an extra pixel padding to avoid issues with odd-sized sprites var halfWidth = size.Width / 2 + 1; diff --git a/OpenRA.Mods.Common/Traits/World/EditorCursorLayer.cs b/OpenRA.Mods.Common/Traits/World/EditorCursorLayer.cs index fc3d11cac3..9374faac08 100644 --- a/OpenRA.Mods.Common/Traits/World/EditorCursorLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/EditorCursorLayer.cs @@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.Traits public enum EditorCursorType { None, Actor, TerrainTemplate, Resource } [Desc("Required for the map editor to work. Attach this to the world actor.")] - public class EditorCursorLayerInfo : TraitInfo, Requires + public class EditorCursorLayerInfo : TraitInfo, Requires, Requires { public readonly WAngle PreviewFacing = new WAngle(384); @@ -30,6 +30,7 @@ namespace OpenRA.Mods.Common.Traits { readonly EditorCursorLayerInfo info; readonly EditorActorLayer editorLayer; + readonly ITiledTerrainRenderer terrainRenderer; readonly World world; public int CurrentToken { get; private set; } @@ -51,6 +52,7 @@ namespace OpenRA.Mods.Common.Traits this.info = info; world = self.World; editorLayer = self.Trait(); + terrainRenderer = self.Trait(); Type = EditorCursorType.None; } @@ -81,7 +83,7 @@ namespace OpenRA.Mods.Common.Traits if (!world.Map.Rules.TileSet.TryGetTileInfo(tile, out var tileInfo)) continue; - var sprite = wr.Theater.TileSprite(tile, 0); + var sprite = terrainRenderer.TileSprite(tile, 0); var offset = world.Map.Offset(new CVec(x, y), tileInfo.Height); var palette = wr.Palette(TerrainTemplate.Palette ?? TileSet.TerrainPaletteInternalName); diff --git a/OpenRA.Mods.Common/Traits/World/TerrainRenderer.cs b/OpenRA.Mods.Common/Traits/World/TerrainRenderer.cs index 64e0c4c607..8e3557b2a1 100644 --- a/OpenRA.Mods.Common/Traits/World/TerrainRenderer.cs +++ b/OpenRA.Mods.Common/Traits/World/TerrainRenderer.cs @@ -11,31 +11,31 @@ using System.Collections.Generic; using OpenRA.Graphics; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { - public class TerrainRendererInfo : TraitInfo + public class TerrainRendererInfo : TraitInfo, ITiledTerrainRendererInfo { public override object Create(ActorInitializer init) { return new TerrainRenderer(init.World); } } - public sealed class TerrainRenderer : IRenderTerrain, IWorldLoaded, INotifyActorDisposing + public sealed class TerrainRenderer : IRenderTerrain, IWorldLoaded, INotifyActorDisposing, ITiledTerrainRenderer { readonly Map map; readonly Dictionary spriteLayers = new Dictionary(); - Theater theater; + readonly Theater theater; bool disposed; public TerrainRenderer(World world) { map = world.Map; + theater = new Theater(world.Map.Rules.TileSet); } void IWorldLoaded.WorldLoaded(World world, WorldRenderer wr) { - theater = wr.Theater; - foreach (var template in map.Rules.TileSet.Templates) { var palette = template.Value.Palette ?? TileSet.TerrainPaletteInternalName; @@ -57,9 +57,8 @@ namespace OpenRA.Mods.Common.Traits if (map.Rules.TileSet.Templates.ContainsKey(tile.Type)) palette = map.Rules.TileSet.Templates[tile.Type].Palette ?? palette; - var sprite = theater.TileSprite(tile); foreach (var kv in spriteLayers) - kv.Value.Update(cell, palette == kv.Key ? sprite : null, false); + kv.Value.Update(cell, palette == kv.Key ? theater.TileSprite(tile) : null, false); } void IRenderTerrain.RenderTerrain(WorldRenderer wr, Viewport viewport) @@ -82,7 +81,42 @@ namespace OpenRA.Mods.Common.Traits foreach (var kv in spriteLayers.Values) kv.Dispose(); + theater.Dispose(); disposed = true; } + + Sheet ITiledTerrainRenderer.Sheet { get { return theater.Sheet; } } + + Sprite ITiledTerrainRenderer.TileSprite(TerrainTile r, int? variant) + { + return theater.TileSprite(r, variant); + } + + Rectangle ITiledTerrainRenderer.TemplateBounds(TerrainTemplateInfo template) + { + Rectangle? templateRect = null; + var tileSize = map.Grid.TileSize; + + var i = 0; + for (var y = 0; y < template.Size.Y; y++) + { + for (var x = 0; x < template.Size.X; x++) + { + var tile = new TerrainTile(template.Id, (byte)(i++)); + if (!map.Rules.TileSet.TryGetTileInfo(tile, out var tileInfo)) + continue; + + var sprite = theater.TileSprite(tile); + var u = map.Grid.Type == MapGridType.Rectangular ? x : (x - y) / 2f; + var v = map.Grid.Type == MapGridType.Rectangular ? y : (x + y) / 2f; + + var tl = new float2(u * tileSize.Width, (v - 0.5f * tileInfo.Height) * tileSize.Height) - 0.5f * sprite.Size; + var rect = new Rectangle((int)(tl.X + sprite.Offset.X), (int)(tl.Y + sprite.Offset.Y), (int)sprite.Size.X, (int)sprite.Size.Y); + templateRect = templateRect.HasValue ? Rectangle.Union(templateRect.Value, rect) : rect; + } + } + + return templateRect ?? Rectangle.Empty; + } } } diff --git a/OpenRA.Mods.Common/TraitsInterfaces.cs b/OpenRA.Mods.Common/TraitsInterfaces.cs index 009005d7ae..993a9823dd 100644 --- a/OpenRA.Mods.Common/TraitsInterfaces.cs +++ b/OpenRA.Mods.Common/TraitsInterfaces.cs @@ -643,4 +643,15 @@ namespace OpenRA.Mods.Common.Traits IEnumerable RenderDecoration(Actor self, WorldRenderer wr, ISelectionDecorations container); } + + [RequireExplicitImplementation] + public interface ITiledTerrainRendererInfo : ITraitInfoInterface { } + + [RequireExplicitImplementation] + public interface ITiledTerrainRenderer + { + Sheet Sheet { get; } + Sprite TileSprite(TerrainTile r, int? variant = null); + Rectangle TemplateBounds(TerrainTemplateInfo template); + } } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs index 4551f3062d..76f214179a 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs @@ -13,6 +13,7 @@ using System; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Traits; +using OpenRA.Traits; using OpenRA.Widgets; namespace OpenRA.Mods.Common.Widgets.Logic @@ -36,6 +37,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic } readonly TileSet tileset; + readonly ITiledTerrainRenderer terrainRenderer; readonly TileSelectorTemplate[] allTemplates; readonly EditorCursorLayer editorCursor; @@ -44,6 +46,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic : base(widget, world, worldRenderer, "TILETEMPLATE_LIST", "TILEPREVIEW_TEMPLATE") { tileset = world.Map.Rules.TileSet; + terrainRenderer = world.WorldActor.TraitOrDefault(); + if (terrainRenderer == null) + throw new YamlException("TileSelectorLogic requires a tile-based terrain renderer."); + allTemplates = tileset.Templates.Values.Select(t => new TileSelectorTemplate(t)).ToArray(); editorCursor = world.WorldActor.Trait(); @@ -106,8 +112,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic var preview = item.Get("TILE_PREVIEW"); var template = tileset.Templates[tileId]; - var grid = WorldRenderer.World.Map.Grid; - var bounds = WorldRenderer.Theater.TemplateBounds(template, grid.TileSize, grid.Type); + var bounds = terrainRenderer.TemplateBounds(template); // Scale templates to fit within the panel var scale = 1f; diff --git a/OpenRA.Mods.Common/Widgets/TerrainTemplatePreviewWidget.cs b/OpenRA.Mods.Common/Widgets/TerrainTemplatePreviewWidget.cs index a9f889f7cd..ce6d93d7d4 100644 --- a/OpenRA.Mods.Common/Widgets/TerrainTemplatePreviewWidget.cs +++ b/OpenRA.Mods.Common/Widgets/TerrainTemplatePreviewWidget.cs @@ -11,7 +11,9 @@ using System; using OpenRA.Graphics; +using OpenRA.Mods.Common.Traits; using OpenRA.Primitives; +using OpenRA.Traits; using OpenRA.Widgets; namespace OpenRA.Mods.Common.Widgets @@ -20,6 +22,7 @@ namespace OpenRA.Mods.Common.Widgets { public Func GetScale = () => 1f; + readonly ITiledTerrainRenderer terrainRenderer; readonly WorldRenderer worldRenderer; readonly TileSet tileset; @@ -39,8 +42,7 @@ namespace OpenRA.Mods.Common.Widgets if (template == null) return; - var grid = Game.ModData.Manifest.Get(); - bounds = worldRenderer.Theater.TemplateBounds(template, grid.TileSize, grid.Type); + bounds = terrainRenderer.TemplateBounds(template); } } @@ -49,6 +51,9 @@ namespace OpenRA.Mods.Common.Widgets { this.worldRenderer = worldRenderer; tileset = world.Map.Rules.TileSet; + terrainRenderer = world.WorldActor.TraitOrDefault(); + if (terrainRenderer == null) + throw new YamlException("TerrainTemplatePreviewWidget requires a tile-based terrain renderer."); } protected TerrainTemplatePreviewWidget(TerrainTemplatePreviewWidget other) @@ -56,6 +61,7 @@ namespace OpenRA.Mods.Common.Widgets { worldRenderer = other.worldRenderer; tileset = other.worldRenderer.World.Map.Rules.TileSet; + terrainRenderer = other.terrainRenderer; Template = other.Template; GetScale = other.GetScale; } @@ -84,7 +90,7 @@ namespace OpenRA.Mods.Common.Widgets if (!tileset.TryGetTileInfo(tile, out var tileInfo)) continue; - var sprite = worldRenderer.Theater.TileSprite(tile, 0); + var sprite = terrainRenderer.TileSprite(tile, 0); var size = new float2(sprite.Size.X * scale, sprite.Size.Y * scale); var u = gridType == MapGridType.Rectangular ? x : (x - y) / 2f; diff --git a/OpenRA.Mods.D2k/Traits/World/BuildableTerrainLayer.cs b/OpenRA.Mods.D2k/Traits/World/BuildableTerrainLayer.cs index eaa6c89ab5..095fce8894 100644 --- a/OpenRA.Mods.D2k/Traits/World/BuildableTerrainLayer.cs +++ b/OpenRA.Mods.D2k/Traits/World/BuildableTerrainLayer.cs @@ -18,7 +18,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.D2k.Traits { [Desc("Attach this to the world actor. Required for LaysTerrain to work.")] - public class BuildableTerrainLayerInfo : TraitInfo + public class BuildableTerrainLayerInfo : TraitInfo, Requires { [Desc("Palette to render the layer sprites in.")] public readonly string Palette = TileSet.TerrainPaletteInternalName; @@ -33,11 +33,11 @@ namespace OpenRA.Mods.D2k.Traits { readonly BuildableTerrainLayerInfo info; readonly Dictionary dirty = new Dictionary(); + readonly ITiledTerrainRenderer terrainRenderer; readonly World world; readonly CellLayer strength; TerrainSpriteLayer render; - Theater theater; bool disposed; public BuildableTerrainLayer(Actor self, BuildableTerrainLayerInfo info) @@ -45,12 +45,12 @@ namespace OpenRA.Mods.D2k.Traits this.info = info; world = self.World; strength = new CellLayer(world.Map); + terrainRenderer = self.Trait(); } public void WorldLoaded(World w, WorldRenderer wr) { - theater = wr.Theater; - render = new TerrainSpriteLayer(w, wr, theater.Sheet, BlendMode.Alpha, wr.Palette(info.Palette), wr.World.Type != WorldType.Editor); + render = new TerrainSpriteLayer(w, wr, terrainRenderer.Sheet, BlendMode.Alpha, wr.Palette(info.Palette), wr.World.Type != WorldType.Editor); } public void AddTile(CPos cell, TerrainTile tile) @@ -98,7 +98,7 @@ namespace OpenRA.Mods.D2k.Traits if (tile.HasValue) { // Terrain tiles define their origin at the topleft - var s = theater.TileSprite(tile.Value); + var s = terrainRenderer.TileSprite(tile.Value); var ss = new Sprite(s.Sheet, s.Bounds, s.ZRamp, float2.Zero, s.Channel, s.BlendMode); render.Update(kv.Key, ss, false); }