#region Copyright & License Information /* * Copyright 2007-2020 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, either version 3 of * the License, or (at your option) any later version. For more * information, see COPYING. */ #endregion using System.Collections.Generic; using OpenRA.Graphics; using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { public class TerrainRendererInfo : TraitInfo, ITiledTerrainRendererInfo { public override object Create(ActorInitializer init) { return new TerrainRenderer(init.World); } } public sealed class TerrainRenderer : IRenderTerrain, IWorldLoaded, INotifyActorDisposing, ITiledTerrainRenderer { readonly Map map; readonly Dictionary spriteLayers = new Dictionary(); 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) { foreach (var template in map.Rules.TileSet.Templates) { var palette = template.Value.Palette ?? TileSet.TerrainPaletteInternalName; spriteLayers.GetOrAdd(palette, pal => new TerrainSpriteLayer(world, wr, theater.Sheet, BlendMode.Alpha, wr.Palette(palette), world.Type != WorldType.Editor)); } foreach (var cell in map.AllCells) UpdateCell(cell); map.Tiles.CellEntryChanged += UpdateCell; map.Height.CellEntryChanged += UpdateCell; } public void UpdateCell(CPos cell) { var tile = map.Tiles[cell]; var palette = TileSet.TerrainPaletteInternalName; if (map.Rules.TileSet.Templates.ContainsKey(tile.Type)) palette = map.Rules.TileSet.Templates[tile.Type].Palette ?? palette; foreach (var kv in spriteLayers) kv.Value.Update(cell, palette == kv.Key ? theater.TileSprite(tile) : null, false); } void IRenderTerrain.RenderTerrain(WorldRenderer wr, Viewport viewport) { foreach (var kv in spriteLayers.Values) kv.Draw(wr.Viewport); foreach (var r in wr.World.WorldActor.TraitsImplementing()) r.Render(wr); } void INotifyActorDisposing.Disposing(Actor self) { if (disposed) return; map.Tiles.CellEntryChanged -= UpdateCell; map.Height.CellEntryChanged -= UpdateCell; 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; } } }