Add path auto-tiling tool to map editor
Exposes the TilingPath code used by the map generator as a map editor tool to help automate the process of tiling sequences of templates together. No additional template stitching definitions are added as part of this commit. As such, the tool currently only supports the same auto tiling as the map generator does. This includes: - RA and CnC support. - Beaches, land cliffs, and roads. Support for additional mods and templates can be added in follow-ups by adding the necessary MultiBrush definitions. Related changes included in this commit to support this work include: - Add support for sparse EditorBlits. - Add support for MultiBrush to EditorBlitSource conversion. - Add support for EditorBlitSource previewing. - Adjust the "Road" types used in segmented MultiBrush definitions.
This commit is contained in:
committed by
Gustas Kažukauskas
parent
d3d949176d
commit
ad08045e5f
@@ -12,6 +12,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.EditorBrushes
|
||||
@@ -39,7 +40,7 @@ namespace OpenRA.Mods.Common.EditorBrushes
|
||||
readonly MapBlitFilters blitFilters;
|
||||
readonly IResourceLayer resourceLayer;
|
||||
readonly EditorActorLayer editorActorLayer;
|
||||
readonly EditorBlitSource blitSource;
|
||||
readonly EditorBlitSource commitBlitSource;
|
||||
readonly EditorBlitSource revertBlitSource;
|
||||
readonly CPos blitPosition;
|
||||
readonly Map map;
|
||||
@@ -56,30 +57,39 @@ namespace OpenRA.Mods.Common.EditorBrushes
|
||||
{
|
||||
this.blitFilters = blitFilters;
|
||||
this.resourceLayer = resourceLayer;
|
||||
this.blitSource = blitSource;
|
||||
this.blitPosition = blitPosition;
|
||||
this.editorActorLayer = editorActorLayer;
|
||||
this.map = map;
|
||||
this.respectBounds = respectBounds;
|
||||
|
||||
var blitSize = blitSource.CellRegion.BottomRight - blitSource.CellRegion.TopLeft;
|
||||
|
||||
// Only include into the revert blit stuff which would be modified by the main blit.
|
||||
var mask = GetBlitSourceMask(
|
||||
blitSource, blitPosition - blitSource.CellRegion.TopLeft);
|
||||
|
||||
commitBlitSource = blitSource;
|
||||
revertBlitSource = CopyRegionContents(
|
||||
map,
|
||||
editorActorLayer,
|
||||
resourceLayer,
|
||||
new CellRegion(map.Grid.Type, blitPosition, blitPosition + blitSize),
|
||||
blitFilters);
|
||||
blitFilters,
|
||||
mask);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an EditorBlitSource containing the map contents for a given region.
|
||||
/// If a mask is supplied, only tiles and actors (fully or partially) overlapping the mask
|
||||
/// are included in the EditorBlitSource.
|
||||
/// </summary>
|
||||
public static EditorBlitSource CopyRegionContents(
|
||||
Map map,
|
||||
EditorActorLayer editorActorLayer,
|
||||
IResourceLayer resourceLayer,
|
||||
CellRegion region,
|
||||
MapBlitFilters blitFilters)
|
||||
MapBlitFilters blitFilters,
|
||||
IReadOnlySet<CPos> mask = null)
|
||||
{
|
||||
var mapTiles = map.Tiles;
|
||||
var mapHeight = map.Height;
|
||||
@@ -90,7 +100,7 @@ namespace OpenRA.Mods.Common.EditorBrushes
|
||||
|
||||
foreach (var cell in region.CellCoords)
|
||||
{
|
||||
if (!mapTiles.Contains(cell))
|
||||
if (!mapTiles.Contains(cell) || (mask != null && !mask.Contains(cell)))
|
||||
continue;
|
||||
|
||||
tiles.Add(
|
||||
@@ -103,13 +113,15 @@ namespace OpenRA.Mods.Common.EditorBrushes
|
||||
|
||||
if (blitFilters.HasFlag(MapBlitFilters.Actors))
|
||||
foreach (var preview in editorActorLayer.PreviewsInCellRegion(region.CellCoords))
|
||||
previews.TryAdd(preview.ID, preview);
|
||||
if (mask == null || preview.Footprint.Keys.Any(mask.Contains))
|
||||
previews.TryAdd(preview.ID, preview);
|
||||
|
||||
return new EditorBlitSource(region, previews, tiles);
|
||||
}
|
||||
|
||||
void Blit(EditorBlitSource source, bool isRevert)
|
||||
void Blit(bool isRevert)
|
||||
{
|
||||
var source = isRevert ? revertBlitSource : commitBlitSource;
|
||||
var blitPos = isRevert ? source.CellRegion.TopLeft : blitPosition;
|
||||
var blitVec = blitPos - source.CellRegion.TopLeft;
|
||||
var blitSize = source.CellRegion.BottomRight - source.CellRegion.TopLeft;
|
||||
@@ -118,8 +130,25 @@ namespace OpenRA.Mods.Common.EditorBrushes
|
||||
if (blitFilters.HasFlag(MapBlitFilters.Actors))
|
||||
{
|
||||
// Clear any existing actors in the paste cells.
|
||||
foreach (var regionActor in editorActorLayer.PreviewsInCellRegion(blitRegion.CellCoords).ToList())
|
||||
editorActorLayer.Remove(regionActor);
|
||||
var regionActors = editorActorLayer.PreviewsInCellRegion(blitRegion.CellCoords).ToList();
|
||||
|
||||
// revertBlitSource's mask may be a superset of the commitBlitSource's mask if
|
||||
// - Its a sparse blit; and
|
||||
// - The revert actors removed by the commit are partially outside of the commit mask.
|
||||
// Otherwise, it's a (practically) equal set. (Subject to map bounds.)
|
||||
//
|
||||
// This implies that:
|
||||
// - commitBlitSource's mask will overlap all commit actors.
|
||||
// - revertBlitSource's mask will overlap all revert actors.
|
||||
// - commitBlitSource's mask will overlap all and no more than the revert actors.
|
||||
// - revertBlitSource's mask will overlap all revert actors BUT MAY OVERLAP MORE!
|
||||
//
|
||||
// This means we use the commit mask, not the revert one.
|
||||
var commitBlitVec = blitPosition - commitBlitSource.CellRegion.TopLeft;
|
||||
var mask = GetBlitSourceMask(commitBlitSource, commitBlitVec);
|
||||
foreach (var regionActor in regionActors)
|
||||
if (regionActor.Footprint.Any(kv => mask.Contains(kv.Key)))
|
||||
editorActorLayer.Remove(regionActor);
|
||||
}
|
||||
|
||||
foreach (var tileKeyValuePair in source.Tiles)
|
||||
@@ -178,12 +207,104 @@ namespace OpenRA.Mods.Common.EditorBrushes
|
||||
}
|
||||
}
|
||||
|
||||
public void Commit() => Blit(blitSource, false);
|
||||
public void Revert() => Blit(revertBlitSource, true);
|
||||
public static IEnumerable<IRenderable> PreviewBlitSource(
|
||||
EditorBlitSource blitSource,
|
||||
MapBlitFilters filters,
|
||||
CVec offset,
|
||||
WorldRenderer wr)
|
||||
{
|
||||
var world = wr.World;
|
||||
var map = world.Map;
|
||||
|
||||
var terrainRenderer = world.WorldActor.Trait<ITiledTerrainRenderer>();
|
||||
var resourceRenderers = world.WorldActor.TraitsImplementing<IResourceRenderer>().ToArray();
|
||||
|
||||
var wOffset = map.CenterOfCell(CPos.Zero + offset) - map.CenterOfCell(CPos.Zero);
|
||||
|
||||
if (filters.HasFlag(MapBlitFilters.Terrain))
|
||||
{
|
||||
foreach (var (cpos, tile) in blitSource.Tiles)
|
||||
{
|
||||
var preview =
|
||||
terrainRenderer.RenderPreview(
|
||||
wr,
|
||||
tile.TerrainTile,
|
||||
map.CenterOfCell(cpos + offset));
|
||||
foreach (var renderable in preview)
|
||||
yield return renderable;
|
||||
}
|
||||
}
|
||||
|
||||
if (filters.HasFlag(MapBlitFilters.Resources))
|
||||
{
|
||||
foreach (var (cpos, tile) in blitSource.Tiles)
|
||||
{
|
||||
if (tile.ResourceLayerContents == null || tile.ResourceLayerContents.Value.Type == null)
|
||||
continue;
|
||||
|
||||
var preview = resourceRenderers
|
||||
.SelectMany(r => r.RenderPreview(
|
||||
wr,
|
||||
tile.ResourceLayerContents.Value.Type,
|
||||
map.CenterOfCell(cpos + offset)));
|
||||
foreach (var renderable in preview)
|
||||
yield return renderable;
|
||||
}
|
||||
}
|
||||
|
||||
if (filters.HasFlag(MapBlitFilters.Actors))
|
||||
{
|
||||
foreach (var (_, editorActorPreview) in blitSource.Actors)
|
||||
{
|
||||
var preview = editorActorPreview.RenderWithOffset(wOffset);
|
||||
foreach (var renderable in preview)
|
||||
yield return renderable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the set of cells within an EditorBlitSource that are actually occupied by a
|
||||
/// BlitTile or actor. Note that all tiles must be inside the CellRegion, and actors must
|
||||
/// be at least partially inside the CellRegion. If an actor partially lies outside of the
|
||||
/// CellRegion, only cells within the CellRegion are included in the output set.
|
||||
/// </summary>
|
||||
static HashSet<CPos> GetBlitSourceMask(
|
||||
EditorBlitSource blitSource,
|
||||
CVec offset)
|
||||
{
|
||||
var mask = new HashSet<CPos>();
|
||||
|
||||
foreach (var (cpos, _) in blitSource.Tiles)
|
||||
{
|
||||
if (!blitSource.CellRegion.Contains(cpos))
|
||||
throw new ArgumentException("EditorBlitSource contains a BlitTile outside of its CellRegion");
|
||||
mask.Add(cpos + offset);
|
||||
}
|
||||
|
||||
foreach (var (_, editorActorPreview) in blitSource.Actors)
|
||||
{
|
||||
var anyContained = false;
|
||||
foreach (var cpos in editorActorPreview.Footprint.Keys)
|
||||
if (blitSource.CellRegion.Contains(cpos))
|
||||
{
|
||||
mask.Add(cpos + offset);
|
||||
anyContained = true;
|
||||
}
|
||||
|
||||
if (!anyContained)
|
||||
throw new ArgumentException("EditorBlitSource contains an actor entirely outside of its CellRegion");
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
public void Commit() => Blit(false);
|
||||
public void Revert() => Blit(true);
|
||||
|
||||
public int TileCount()
|
||||
{
|
||||
return blitSource.Tiles.Count;
|
||||
return commitBlitSource.Tiles.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user