diff --git a/OpenRA.Game/Graphics/TerrainSpriteLayer.cs b/OpenRA.Game/Graphics/TerrainSpriteLayer.cs index 1b22f4006b..78e54f8917 100644 --- a/OpenRA.Game/Graphics/TerrainSpriteLayer.cs +++ b/OpenRA.Game/Graphics/TerrainSpriteLayer.cs @@ -35,20 +35,20 @@ namespace OpenRA.Graphics readonly WorldRenderer worldRenderer; readonly Map map; - readonly PaletteReference palette; + readonly PaletteReference[] palettes; - public TerrainSpriteLayer(World world, WorldRenderer wr, Sheet sheet, BlendMode blendMode, PaletteReference palette, bool restrictToBounds) + public TerrainSpriteLayer(World world, WorldRenderer wr, Sheet sheet, BlendMode blendMode, bool restrictToBounds) { worldRenderer = wr; this.restrictToBounds = restrictToBounds; Sheet = sheet; BlendMode = blendMode; - this.palette = palette; map = world.Map; rowStride = 6 * map.MapSize.X; vertices = new Vertex[rowStride * map.MapSize.Y]; + palettes = new PaletteReference[map.MapSize.X * map.MapSize.Y]; vertexBuffer = Game.Renderer.Context.CreateVertexBuffer(vertices.Length); emptySprite = new Sprite(sheet, Rectangle.Empty, TextureChannel.Alpha); @@ -63,12 +63,11 @@ namespace OpenRA.Graphics void UpdatePaletteIndices() { - // Everything in the layer uses the same palette, - // so we can fix the indices in one pass for (var i = 0; i < vertices.Length; i++) { var v = vertices[i]; - vertices[i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C, v.R, v.G, v.B, v.A); + var p = palettes[i / 6]?.TextureIndex ?? 0; + vertices[i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, p, v.C, v.R, v.G, v.B, v.A); } for (var row = 0; row < map.MapSize.Y; row++) @@ -77,15 +76,15 @@ namespace OpenRA.Graphics public void Clear(CPos cell) { - Update(cell, null, true); + Update(cell, null, null, true); } - public void Update(CPos cell, ISpriteSequence sequence, int frame) + public void Update(CPos cell, ISpriteSequence sequence, PaletteReference palette, int frame) { - Update(cell, sequence.GetSprite(frame), sequence.IgnoreWorldTint); + Update(cell, sequence.GetSprite(frame), palette, sequence.IgnoreWorldTint); } - public void Update(CPos cell, Sprite sprite, bool ignoreTint) + public void Update(CPos cell, Sprite sprite, PaletteReference palette, bool ignoreTint) { var xyz = float3.Zero; if (sprite != null) @@ -94,7 +93,7 @@ namespace OpenRA.Graphics xyz = worldRenderer.Screen3DPosition(cellOrigin) + sprite.Offset - 0.5f * sprite.Size; } - Update(cell.ToMPos(map.Grid.Type), sprite, xyz, ignoreTint); + Update(cell.ToMPos(map.Grid.Type), sprite, palette, xyz, ignoreTint); } void UpdateTint(MPos uv) @@ -106,7 +105,7 @@ namespace OpenRA.Graphics for (var i = 0; i < 6; i++) { var v = vertices[offset + i]; - vertices[offset + i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C, noTint, 1f); + vertices[offset + i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, v.P, v.C, noTint, 1f); } return; @@ -131,13 +130,13 @@ namespace OpenRA.Graphics for (var i = 0; i < 6; i++) { var v = vertices[offset + i]; - vertices[offset + i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C, weights[CornerVertexMap[i]], 1f); + vertices[offset + i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, v.P, v.C, weights[CornerVertexMap[i]], 1f); } dirtyRows.Add(uv.V); } - public void Update(MPos uv, Sprite sprite, in float3 pos, bool ignoreTint) + public void Update(MPos uv, Sprite sprite, PaletteReference palette, in float3 pos, bool ignoreTint) { if (sprite != null) { @@ -155,7 +154,8 @@ namespace OpenRA.Graphics return; var offset = rowStride * uv.V + 6 * uv.U; - Util.FastCreateQuad(vertices, pos, sprite, int2.Zero, palette.TextureIndex, offset, sprite.Size, float3.Ones, 1f); + Util.FastCreateQuad(vertices, pos, sprite, int2.Zero, palette?.TextureIndex ?? 0, offset, sprite.Size, float3.Ones, 1f); + palettes[uv.V * map.MapSize.X + uv.U] = palette; if (worldRenderer.TerrainLighting != null) { diff --git a/OpenRA.Mods.Common/Traits/World/ResourceRenderer.cs b/OpenRA.Mods.Common/Traits/World/ResourceRenderer.cs index 2955e8a447..21020a0877 100644 --- a/OpenRA.Mods.Common/Traits/World/ResourceRenderer.cs +++ b/OpenRA.Mods.Common/Traits/World/ResourceRenderer.cs @@ -35,7 +35,7 @@ namespace OpenRA.Mods.Common.Traits readonly HashSet dirty = new HashSet(); readonly Queue cleanDirty = new Queue(); - readonly Dictionary spriteLayers = new Dictionary(); + TerrainSpriteLayer spriteLayer; public ResourceRenderer(Actor self, ResourceRendererInfo info) { @@ -58,26 +58,22 @@ namespace OpenRA.Mods.Common.Traits var resources = w.WorldActor.TraitsImplementing() .ToDictionary(r => r.Info.ResourceType, r => r); - // 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 layer = spriteLayers.GetOrAdd(r.Value.Palette, pal => + if (spriteLayer == null) { var first = r.Value.Variants.First().Value.GetSprite(0); - return new TerrainSpriteLayer(w, wr, first.Sheet, first.BlendMode, pal, wr.World.Type != WorldType.Editor); - }); + spriteLayer = new TerrainSpriteLayer(w, wr, first.Sheet, first.BlendMode, wr.World.Type != WorldType.Editor); + } - // Validate that sprites are compatible with this layer - var sheet = layer.Sheet; + // All resources must share a sheet and blend mode var sprites = r.Value.Variants.Values.SelectMany(v => Exts.MakeArray(v.Length, x => v.GetSprite(x))); - if (sprites.Any(s => s.Sheet != sheet)) + if (sprites.Any(s => s.Sheet != spriteLayer.Sheet)) throw new InvalidDataException("Resource sprites span multiple sheets. Try loading their sequences earlier."); - var blendMode = layer.BlendMode; - if (sprites.Any(s => s.BlendMode != blendMode)) + if (sprites.Any(s => s.BlendMode != spriteLayer.BlendMode)) throw new InvalidDataException("Resource sprites specify different blend modes. " - + "Try using different palettes for resource types that use different blend modes."); + + "Try using different ResourceRenderer traits for resource types that use different blend modes."); } // Initialize the RenderContent with the initial map state @@ -97,20 +93,16 @@ namespace OpenRA.Mods.Common.Traits protected void UpdateSpriteLayers(CPos cell, ISpriteSequence sequence, int frame, PaletteReference palette) { - foreach (var kv in spriteLayers) - { - // resource.Type is meaningless (and may be null) if resource.Sequence is null - if (sequence != null && palette == kv.Key) - kv.Value.Update(cell, sequence, frame); - else - kv.Value.Clear(cell); - } + // resource.Type is meaningless (and may be null) if resource.Sequence is null + if (sequence != null) + spriteLayer.Update(cell, sequence, palette, frame); + else + spriteLayer.Clear(cell); } void IRenderOverlay.Render(WorldRenderer wr) { - foreach (var kv in spriteLayers.Values) - kv.Draw(wr.Viewport); + spriteLayer.Draw(wr.Viewport); } void ITickRender.TickRender(WorldRenderer wr, Actor self) @@ -175,8 +167,7 @@ namespace OpenRA.Mods.Common.Traits if (disposed) return; - foreach (var kv in spriteLayers.Values) - kv.Dispose(); + spriteLayer.Dispose(); ResourceLayer.CellChanged -= AddDirtyCell; diff --git a/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs b/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs index fc5b5f6186..ad24ded801 100644 --- a/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs +++ b/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs @@ -106,6 +106,7 @@ namespace OpenRA.Mods.Common.Traits Shroud shroud; Func visibleUnderShroud, visibleUnderFog; TerrainSpriteLayer shroudLayer, fogLayer; + PaletteReference shroudPaletteReference, fogPaletteReference; bool disposed; public ShroudRenderer(World world, ShroudRendererInfo info) @@ -205,8 +206,10 @@ namespace OpenRA.Mods.Common.Traits if (fogSprites.Any(s => s.BlendMode != fogBlend)) throw new InvalidDataException("Fog sprites must all use the same blend mode."); - shroudLayer = new TerrainSpriteLayer(w, wr, shroudSheet, shroudBlend, wr.Palette(info.ShroudPalette), false); - fogLayer = new TerrainSpriteLayer(w, wr, fogSheet, fogBlend, wr.Palette(info.FogPalette), false); + shroudPaletteReference = wr.Palette(info.ShroudPalette); + fogPaletteReference = wr.Palette(info.FogPalette); + shroudLayer = new TerrainSpriteLayer(w, wr, shroudSheet, shroudBlend, false); + fogLayer = new TerrainSpriteLayer(w, wr, fogSheet, fogBlend, false); WorldOnRenderPlayerChanged(world.RenderPlayer); } @@ -297,8 +300,8 @@ namespace OpenRA.Mods.Common.Traits if (fogSprite != null) fogPos += fogSprite.Offset - 0.5f * fogSprite.Size; - shroudLayer.Update(uv, shroudSprite, shroudPos, true); - fogLayer.Update(uv, fogSprite, fogPos, true); + shroudLayer.Update(uv, shroudSprite, shroudPaletteReference, shroudPos, true); + fogLayer.Update(uv, fogSprite, fogPaletteReference, fogPos, true); } anyCellDirty = false; diff --git a/OpenRA.Mods.Common/Traits/World/SmudgeLayer.cs b/OpenRA.Mods.Common/Traits/World/SmudgeLayer.cs index 8c938e70ea..6ed9178c0f 100644 --- a/OpenRA.Mods.Common/Traits/World/SmudgeLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/SmudgeLayer.cs @@ -94,6 +94,7 @@ namespace OpenRA.Mods.Common.Traits readonly bool hasSmoke; TerrainSpriteLayer render; + PaletteReference paletteReference; bool disposed; public SmudgeLayer(Actor self, SmudgeLayerInfo info) @@ -121,7 +122,8 @@ namespace OpenRA.Mods.Common.Traits 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), w.Type != WorldType.Editor); + paletteReference = wr.Palette(Info.Palette); + render = new TerrainSpriteLayer(w, wr, sheet, blendMode, w.Type != WorldType.Editor); // Add map smudges foreach (var kv in Info.InitialSmudges) @@ -139,7 +141,7 @@ namespace OpenRA.Mods.Common.Traits }; tiles.Add(kv.Key, smudge); - render.Update(kv.Key, seq, s.Depth); + render.Update(kv.Key, seq, paletteReference, s.Depth); } } @@ -201,7 +203,7 @@ namespace OpenRA.Mods.Common.Traits { var smudge = kv.Value; tiles[kv.Key] = smudge; - render.Update(kv.Key, smudge.Sequence, smudge.Depth); + render.Update(kv.Key, smudge.Sequence, paletteReference, smudge.Depth); } remove.Add(kv.Key); diff --git a/OpenRA.Mods.Common/Traits/World/TerrainRenderer.cs b/OpenRA.Mods.Common/Traits/World/TerrainRenderer.cs index 2e9ec17eb4..8ec562205e 100644 --- a/OpenRA.Mods.Common/Traits/World/TerrainRenderer.cs +++ b/OpenRA.Mods.Common/Traits/World/TerrainRenderer.cs @@ -61,9 +61,10 @@ namespace OpenRA.Mods.Common.Traits public sealed class TerrainRenderer : IRenderTerrain, IWorldLoaded, INotifyActorDisposing, ITiledTerrainRenderer { readonly Map map; - readonly Dictionary spriteLayers = new Dictionary(); + TerrainSpriteLayer spriteLayer; readonly DefaultTerrain terrainInfo; readonly DefaultTileCache tileCache; + WorldRenderer worldRenderer; bool disposed; public TerrainRenderer(World world) @@ -78,14 +79,8 @@ namespace OpenRA.Mods.Common.Traits void IWorldLoaded.WorldLoaded(World world, WorldRenderer wr) { - foreach (var template in terrainInfo.Templates) - { - var templateInfo = (DefaultTerrainTemplateInfo)template.Value; - var palette = templateInfo.Palette ?? TileSet.TerrainPaletteInternalName; - spriteLayers.GetOrAdd(palette, pal => - new TerrainSpriteLayer(world, wr, tileCache.Sheet, BlendMode.Alpha, wr.Palette(palette), world.Type != WorldType.Editor)); - } - + worldRenderer = wr; + spriteLayer = new TerrainSpriteLayer(world, wr, tileCache.Sheet, BlendMode.Alpha, world.Type != WorldType.Editor); foreach (var cell in map.AllCells) UpdateCell(cell); @@ -100,14 +95,14 @@ namespace OpenRA.Mods.Common.Traits if (terrainInfo.Templates.TryGetValue(tile.Type, out var template)) palette = ((DefaultTerrainTemplateInfo)template).Palette ?? palette; - foreach (var kv in spriteLayers) - kv.Value.Update(cell, palette == kv.Key ? tileCache.TileSprite(tile) : null, false); + var sprite = tileCache.TileSprite(tile); + var paletteReference = worldRenderer.Palette(palette); + spriteLayer.Update(cell, sprite, paletteReference, false); } void IRenderTerrain.RenderTerrain(WorldRenderer wr, Viewport viewport) { - foreach (var kv in spriteLayers.Values) - kv.Draw(wr.Viewport); + spriteLayer.Draw(wr.Viewport); foreach (var r in wr.World.WorldActor.TraitsImplementing()) r.Render(wr); @@ -121,8 +116,7 @@ namespace OpenRA.Mods.Common.Traits map.Tiles.CellEntryChanged -= UpdateCell; map.Height.CellEntryChanged -= UpdateCell; - foreach (var kv in spriteLayers.Values) - kv.Dispose(); + spriteLayer.Dispose(); tileCache.Dispose(); disposed = true; diff --git a/OpenRA.Mods.D2k/Traits/World/BuildableTerrainLayer.cs b/OpenRA.Mods.D2k/Traits/World/BuildableTerrainLayer.cs index 14d502ed8c..5a30704b6f 100644 --- a/OpenRA.Mods.D2k/Traits/World/BuildableTerrainLayer.cs +++ b/OpenRA.Mods.D2k/Traits/World/BuildableTerrainLayer.cs @@ -38,6 +38,7 @@ namespace OpenRA.Mods.D2k.Traits readonly CellLayer strength; TerrainSpriteLayer render; + PaletteReference paletteReference; bool disposed; public BuildableTerrainLayer(Actor self, BuildableTerrainLayerInfo info) @@ -48,9 +49,10 @@ namespace OpenRA.Mods.D2k.Traits terrainRenderer = self.Trait(); } - public void WorldLoaded(World w, WorldRenderer wr) + void IWorldLoaded.WorldLoaded(World w, WorldRenderer wr) { - render = new TerrainSpriteLayer(w, wr, terrainRenderer.Sheet, BlendMode.Alpha, wr.Palette(info.Palette), wr.World.Type != WorldType.Editor); + render = new TerrainSpriteLayer(w, wr, terrainRenderer.Sheet, BlendMode.Alpha, wr.World.Type != WorldType.Editor); + paletteReference = wr.Palette(info.Palette); } public void AddTile(CPos cell, TerrainTile tile) @@ -100,7 +102,7 @@ namespace OpenRA.Mods.D2k.Traits // Terrain tiles define their origin at the topleft 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); + render.Update(kv.Key, ss, paletteReference, false); } else render.Clear(kv.Key);