diff --git a/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs b/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs index 5f5f2c2e5c..c9e1618d1f 100644 --- a/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/EditorResourceLayer.cs @@ -28,7 +28,7 @@ namespace OpenRA.Mods.Common.Traits public virtual object Create(ActorInitializer init) { return new EditorResourceLayer(init.Self); } } - public class EditorResourceLayer : IWorldLoaded, IRenderOverlay + public class EditorResourceLayer : IWorldLoaded, IRenderOverlay, INotifyActorDisposing { protected readonly Map Map; protected readonly TileSet Tileset; @@ -36,6 +36,8 @@ namespace OpenRA.Mods.Common.Traits protected readonly CellLayer Tiles; protected readonly HashSet Dirty = new HashSet(); + readonly Dictionary spriteLayers = new Dictionary(); + public EditorResourceLayer(Actor self) { if (self.World.Type != WorldType.Editor) @@ -58,6 +60,28 @@ namespace OpenRA.Mods.Common.Traits foreach (var cell in Map.AllCells) UpdateCell(cell); + + // Build the sprite layer dictionary for rendering resources + // All resources that have the same palette must also share a sheet and blend mode + foreach (var r in Resources) + { + var res = r; + var layer = spriteLayers.GetOrAdd(r.Value.Palette, pal => + { + var first = res.Value.Variants.First().Value.First(); + return new TerrainSpriteLayer(w, wr, first.Sheet, first.BlendMode, pal, false); + }); + + // Validate that sprites are compatible with this layer + var sheet = layer.Sheet; + if (res.Value.Variants.Any(kv => kv.Value.Any(s => s.Sheet != sheet))) + throw new InvalidDataException("Resource sprites span multiple sheets. Try loading their sequences earlier."); + + var blendMode = layer.BlendMode; + if (res.Value.Variants.Any(kv => kv.Value.Any(s => s.BlendMode != blendMode))) + throw new InvalidDataException("Resource sprites specify different blend modes. " + + "Try using different palettes for resource types that use different blend modes."); + } } public void UpdateCell(CPos cell) @@ -141,18 +165,39 @@ namespace OpenRA.Mods.Common.Traits return; foreach (var c in Dirty) + { if (Tiles.Contains(c)) - Tiles[c] = UpdateDirtyTile(c); + { + var resource = UpdateDirtyTile(c); + Tiles[c] = resource; + + foreach (var kv in spriteLayers) + { + // resource.Type is meaningless (and may be null) if resource.Sprite is null + if (resource.Sprite != null && resource.Type.Palette == kv.Key) + kv.Value.Update(c, resource.Sprite); + else + kv.Value.Update(c, null); + } + } + } Dirty.Clear(); - foreach (var uv in wr.Viewport.AllVisibleCells.MapCoords) - { - var t = Tiles[uv]; - if (t.Sprite != null) - new SpriteRenderable(t.Sprite, wr.World.Map.CenterOfCell(uv.ToCPos(Map)), - WVec.Zero, -511, t.Type.Palette, 1f, true).Render(wr); - } + foreach (var l in spriteLayers.Values) + l.Draw(wr.Viewport); + } + + bool disposed; + public void Disposing(Actor self) + { + if (disposed) + return; + + foreach (var kv in spriteLayers.Values) + kv.Dispose(); + + disposed = true; } } } diff --git a/OpenRA.Mods.Common/Traits/World/SmudgeLayer.cs b/OpenRA.Mods.Common/Traits/World/SmudgeLayer.cs index 7edf0ad3c5..1dacf21c94 100644 --- a/OpenRA.Mods.Common/Traits/World/SmudgeLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/SmudgeLayer.cs @@ -9,6 +9,7 @@ #endregion using System.Collections.Generic; +using System.IO; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Effects; @@ -34,10 +35,10 @@ namespace OpenRA.Mods.Common.Traits public readonly string Palette = "terrain"; - public object Create(ActorInitializer init) { return new SmudgeLayer(this); } + public object Create(ActorInitializer init) { return new SmudgeLayer(init.Self, this); } } - public class SmudgeLayer : IRenderOverlay, IWorldLoaded, ITickRender + public class SmudgeLayer : IRenderOverlay, IWorldLoaded, ITickRender, INotifyActorDisposing { struct Smudge { @@ -46,23 +47,18 @@ namespace OpenRA.Mods.Common.Traits public Sprite Sprite; } - public SmudgeLayerInfo Info; - Dictionary tiles; - Dictionary dirty; - Dictionary smudges; - World world; + public readonly SmudgeLayerInfo Info; + readonly Dictionary tiles = new Dictionary(); + readonly Dictionary dirty = new Dictionary(); + readonly Dictionary smudges = new Dictionary(); + readonly World world; - public SmudgeLayer(SmudgeLayerInfo info) - { - this.Info = info; - } + TerrainSpriteLayer render; - public void WorldLoaded(World w, WorldRenderer wr) + public SmudgeLayer(Actor self, SmudgeLayerInfo info) { - world = w; - tiles = new Dictionary(); - dirty = new Dictionary(); - smudges = new Dictionary(); + Info = info; + world = self.World; var types = world.Map.SequenceProvider.Sequences(Info.Sequence); foreach (var t in types) @@ -71,6 +67,21 @@ namespace OpenRA.Mods.Common.Traits var sprites = Exts.MakeArray(seq.Length, x => seq.GetSprite(x)); smudges.Add(t, sprites); } + } + + public void WorldLoaded(World w, WorldRenderer wr) + { + var first = smudges.First().Value.First(); + var sheet = first.Sheet; + if (smudges.Values.Any(sprites => sprites.Any(s => s.Sheet != sheet))) + throw new InvalidDataException("Resource sprites span multiple sheets. Try loading their sequences earlier."); + + var blendMode = first.BlendMode; + if (smudges.Values.Any(sprites => sprites.Any(s => s.BlendMode != blendMode))) + throw new InvalidDataException("Smudges specify different blend modes. " + + "Try using different smudge types for smudges that use different blend modes."); + + render = new TerrainSpriteLayer(w, wr, sheet, blendMode, wr.Palette(Info.Palette), wr.World.Type != WorldType.Editor); // Add map smudges foreach (var s in w.Map.SmudgeDefinitions) @@ -94,6 +105,7 @@ namespace OpenRA.Mods.Common.Traits }; tiles.Add(cell, smudge); + render.Update(cell, smudge.Sprite); } } @@ -131,6 +143,8 @@ namespace OpenRA.Mods.Common.Traits if (!self.World.FogObscures(kv.Key)) { tiles[kv.Key] = kv.Value; + render.Update(kv.Key, kv.Value.Sprite); + remove.Add(kv.Key); } } @@ -141,19 +155,17 @@ namespace OpenRA.Mods.Common.Traits public void Render(WorldRenderer wr) { - var pal = wr.Palette(Info.Palette); + render.Draw(wr.Viewport); + } - foreach (var kv in tiles) - { - if (!wr.Viewport.VisibleCellsInsideBounds.Contains(kv.Key)) - continue; + bool disposed; + public void Disposing(Actor self) + { + if (disposed) + return; - if (world.ShroudObscures(kv.Key)) - continue; - - new SpriteRenderable(kv.Value.Sprite, world.Map.CenterOfCell(kv.Key), - WVec.Zero, -511, pal, 1f, true).Render(wr); // TODO ZOffset is ignored - } + render.Dispose(); + disposed = true; } } } diff --git a/OpenRA.Mods.D2k/Traits/World/BuildableTerrainLayer.cs b/OpenRA.Mods.D2k/Traits/World/BuildableTerrainLayer.cs index 39f4481a93..3555380ba4 100644 --- a/OpenRA.Mods.D2k/Traits/World/BuildableTerrainLayer.cs +++ b/OpenRA.Mods.D2k/Traits/World/BuildableTerrainLayer.cs @@ -15,22 +15,35 @@ 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 BuildableTerrainLayer : IRenderOverlay, IWorldLoaded, ITickRender + public class BuildableTerrainLayerInfo : ITraitInfo { - Dictionary tiles; - Dictionary dirty; + [Desc("Palette to render the layer sprites in.")] + public readonly string Palette = "terrain"; + + public object Create(ActorInitializer init) { return new BuildableTerrainLayer(init.Self, this); } + } + + public class BuildableTerrainLayer : IRenderOverlay, IWorldLoaded, ITickRender, INotifyActorDisposing + { + readonly BuildableTerrainLayerInfo info; + readonly Dictionary dirty = new Dictionary(); + readonly TileSet tileset; + readonly Map map; + + TerrainSpriteLayer render; Theater theater; - TileSet tileset; - Map map; + + public BuildableTerrainLayer(Actor self, BuildableTerrainLayerInfo info) + { + this.info = info; + tileset = self.World.TileSet; + map = self.World.Map; + } public void WorldLoaded(World w, WorldRenderer wr) { theater = wr.Theater; - tileset = w.TileSet; - map = w.Map; - tiles = new Dictionary(); - dirty = new Dictionary(); + render = new TerrainSpriteLayer(w, wr, theater.Sheet, BlendMode.Alpha, wr.Palette(info.Palette), wr.World.Type != WorldType.Editor); } public void AddTile(CPos cell, TerrainTile tile) @@ -49,7 +62,7 @@ namespace OpenRA.Mods.D2k.Traits { if (!self.World.FogObscures(kv.Key)) { - tiles[kv.Key] = kv.Value; + render.Update(kv.Key, kv.Value); remove.Add(kv.Key); } } @@ -60,19 +73,17 @@ namespace OpenRA.Mods.D2k.Traits public void Render(WorldRenderer wr) { - var pal = wr.Palette("terrain"); + render.Draw(wr.Viewport); + } - foreach (var kv in tiles) - { - if (!wr.Viewport.VisibleCellsInsideBounds.Contains(kv.Key)) - continue; + bool disposed; + public void Disposing(Actor self) + { + if (disposed) + return; - if (wr.World.ShroudObscures(kv.Key)) - continue; - - new SpriteRenderable(kv.Value, wr.World.Map.CenterOfCell(kv.Key), - WVec.Zero, -511, pal, 1f, true).Render(wr); - } + render.Dispose(); + disposed = true; } } }