Introduce initial PPos plumbing.
PPos is best thought of as a cell grid applied in screen space. Multiple cells with different terrain heights may be projected to the same PPos, or to multiple PPos if they do not align with the screen grid. PPos coordinates are used primarily for map edge checks and shroud / visibility queries.
This commit is contained in:
@@ -98,8 +98,8 @@ namespace OpenRA.Graphics
|
||||
var cells = restrictToBounds ? viewport.VisibleCellsInsideBounds : viewport.AllVisibleCells;
|
||||
|
||||
// Only draw the rows that are visible.
|
||||
var firstRow = cells.MapCoords.TopLeft.V;
|
||||
var lastRow = Math.Min(cells.MapCoords.BottomRight.V + 1, map.MapSize.Y);
|
||||
var firstRow = cells.CandidateMapCoords.TopLeft.V.Clamp(0, map.MapSize.Y);
|
||||
var lastRow = (cells.CandidateMapCoords.BottomRight.V + 1).Clamp(firstRow, map.MapSize.Y);
|
||||
|
||||
Game.Renderer.Flush();
|
||||
|
||||
|
||||
@@ -48,10 +48,10 @@ namespace OpenRA.Graphics
|
||||
public int2 TopLeft { get { return CenterLocation - viewportSize / 2; } }
|
||||
public int2 BottomRight { get { return CenterLocation + viewportSize / 2; } }
|
||||
int2 viewportSize;
|
||||
CellRegion cells;
|
||||
ProjectedCellRegion cells;
|
||||
bool cellsDirty = true;
|
||||
|
||||
CellRegion allCells;
|
||||
ProjectedCellRegion allCells;
|
||||
bool allCellsDirty = true;
|
||||
|
||||
float zoom = 1f;
|
||||
@@ -93,15 +93,27 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
worldRenderer = wr;
|
||||
|
||||
var cells = wr.World.Type == WorldType.Editor ?
|
||||
map.AllCells : map.CellsInsideBounds;
|
||||
|
||||
// Calculate map bounds in world-px
|
||||
var tl = wr.ScreenPxPosition(map.CenterOfCell(cells.TopLeft) - new WVec(512, 512, 0));
|
||||
var br = wr.ScreenPxPosition(map.CenterOfCell(cells.BottomRight) + new WVec(511, 511, 0));
|
||||
mapBounds = Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y);
|
||||
if (wr.World.Type == WorldType.Editor)
|
||||
{
|
||||
// The full map is visible in the editor
|
||||
var ts = Game.ModData.Manifest.TileSize;
|
||||
var width = map.MapSize.X * ts.Width;
|
||||
var height = map.MapSize.Y * ts.Height;
|
||||
if (wr.World.Map.TileShape == TileShape.Diamond)
|
||||
height /= 2;
|
||||
|
||||
mapBounds = new Rectangle(0, 0, width, height);
|
||||
CenterLocation = new int2(width / 2, height / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
var tl = wr.ScreenPxPosition(map.ProjectedTopLeft);
|
||||
var br = wr.ScreenPxPosition(map.ProjectedBottomRight);
|
||||
mapBounds = Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y);
|
||||
CenterLocation = (tl + br) / 2;
|
||||
}
|
||||
|
||||
CenterLocation = (tl + br) / 2;
|
||||
Zoom = Game.Settings.Graphics.PixelDouble ? 2 : 1;
|
||||
tileSize = Game.ModData.Manifest.TileSize;
|
||||
}
|
||||
@@ -209,8 +221,8 @@ namespace OpenRA.Graphics
|
||||
// Visible rectangle in world coordinates (expanded to the corners of the cells)
|
||||
var bounds = insideBounds ? VisibleCellsInsideBounds : AllVisibleCells;
|
||||
var map = worldRenderer.World.Map;
|
||||
var ctl = map.CenterOfCell(bounds.TopLeft) - new WVec(512, 512, 0);
|
||||
var cbr = map.CenterOfCell(bounds.BottomRight) + new WVec(512, 512, 0);
|
||||
var ctl = map.CenterOfCell(((MPos)bounds.TopLeft).ToCPos(map)) - new WVec(512, 512, 0);
|
||||
var cbr = map.CenterOfCell(((MPos)bounds.BottomRight).ToCPos(map)) + new WVec(512, 512, 0);
|
||||
|
||||
// Convert to screen coordinates
|
||||
var tl = WorldToViewPx(worldRenderer.ScreenPxPosition(ctl - new WVec(0, 0, ctl.Z))).Clamp(ScreenClip);
|
||||
@@ -221,22 +233,21 @@ namespace OpenRA.Graphics
|
||||
br.X + tileSize.Width, br.Y + tileSize.Height);
|
||||
}
|
||||
|
||||
CellRegion CalculateVisibleCells(bool insideBounds)
|
||||
ProjectedCellRegion CalculateVisibleCells(bool insideBounds)
|
||||
{
|
||||
var map = worldRenderer.World.Map;
|
||||
|
||||
// Calculate the viewport corners in "projected wpos" (at ground level), and
|
||||
// this to an equivalent projected cell for the two corners
|
||||
var tl = map.CellContaining(worldRenderer.ProjectedPosition(TopLeft)).ToMPos(map);
|
||||
var br = map.CellContaining(worldRenderer.ProjectedPosition(BottomRight)).ToMPos(map);
|
||||
// Calculate the projected cell position at the corners of the visible area
|
||||
var tl = (PPos)map.CellContaining(worldRenderer.ProjectedPosition(TopLeft)).ToMPos(map);
|
||||
var br = (PPos)map.CellContaining(worldRenderer.ProjectedPosition(BottomRight)).ToMPos(map);
|
||||
|
||||
// Diamond tile shapes don't have straight edges, and so we need
|
||||
// an additional cell margin to include the cells that are half
|
||||
// visible on each edge.
|
||||
if (map.TileShape == TileShape.Diamond)
|
||||
{
|
||||
tl = new MPos(tl.U - 1, tl.V - 1);
|
||||
br = new MPos(br.U + 1, br.V + 1);
|
||||
tl = new PPos(tl.U - 1, tl.V - 1);
|
||||
br = new PPos(br.U + 1, br.V + 1);
|
||||
}
|
||||
|
||||
// Clamp to the visible map bounds, if requested
|
||||
@@ -246,21 +257,10 @@ namespace OpenRA.Graphics
|
||||
br = map.Clamp(br);
|
||||
}
|
||||
|
||||
// Cells can be pushed up from below if they have non-zero height.
|
||||
// Each height step is equivalent to 512 WDist units, which is
|
||||
// one MPos step for diamond cells, but only half a MPos step
|
||||
// for classic cells. Doh!
|
||||
var heightOffset = map.TileShape == TileShape.Diamond ? map.MaximumTerrainHeight : map.MaximumTerrainHeight / 2;
|
||||
br = new MPos(br.U, br.V + heightOffset);
|
||||
|
||||
// Finally, make sure that this region doesn't extend outside the map area.
|
||||
tl = map.MapHeight.Value.Clamp(tl);
|
||||
br = map.MapHeight.Value.Clamp(br);
|
||||
|
||||
return new CellRegion(map.TileShape, tl.ToCPos(map), br.ToCPos(map));
|
||||
return new ProjectedCellRegion(map, tl, br);
|
||||
}
|
||||
|
||||
public CellRegion VisibleCellsInsideBounds
|
||||
public ProjectedCellRegion VisibleCellsInsideBounds
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -274,7 +274,7 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
public CellRegion AllVisibleCells
|
||||
public ProjectedCellRegion AllVisibleCells
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
@@ -61,4 +61,34 @@ namespace OpenRA
|
||||
return new CPos(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Projected map position
|
||||
/// </summary>
|
||||
public struct PPos : IEquatable<PPos>
|
||||
{
|
||||
public readonly int U, V;
|
||||
|
||||
public PPos(int u, int v) { U = u; V = v; }
|
||||
public static readonly PPos Zero = new PPos(0, 0);
|
||||
|
||||
public static bool operator ==(PPos me, PPos other) { return me.U == other.U && me.V == other.V; }
|
||||
public static bool operator !=(PPos me, PPos other) { return !(me == other); }
|
||||
|
||||
public static explicit operator MPos(PPos puv) { return new MPos(puv.U, puv.V); }
|
||||
public static explicit operator PPos(MPos uv) { return new PPos(uv.U, uv.V); }
|
||||
|
||||
public PPos Clamp(Rectangle r)
|
||||
{
|
||||
return new PPos(Math.Min(r.Right, Math.Max(U, r.Left)),
|
||||
Math.Min(r.Bottom, Math.Max(V, r.Top)));
|
||||
}
|
||||
|
||||
public override int GetHashCode() { return U.GetHashCode() ^ V.GetHashCode(); }
|
||||
|
||||
public bool Equals(PPos other) { return other == this; }
|
||||
public override bool Equals(object obj) { return obj is PPos && Equals((PPos)obj); }
|
||||
|
||||
public override string ToString() { return U + "," + V; }
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
@@ -245,6 +246,8 @@ namespace OpenRA
|
||||
[FieldLoader.Ignore] public Lazy<CellLayer<byte>> MapHeight;
|
||||
|
||||
[FieldLoader.Ignore] public CellLayer<byte> CustomTerrain;
|
||||
[FieldLoader.Ignore] CellLayer<PPos[]> cellProjection;
|
||||
[FieldLoader.Ignore] CellLayer<List<MPos>> inverseCellProjection;
|
||||
|
||||
[FieldLoader.Ignore] Lazy<TileSet> cachedTileSet;
|
||||
[FieldLoader.Ignore] Lazy<Ruleset> rules;
|
||||
@@ -252,9 +255,11 @@ namespace OpenRA
|
||||
public SequenceProvider SequenceProvider { get { return Rules.Sequences[Tileset]; } }
|
||||
|
||||
public WVec[][] CellCorners { get; private set; }
|
||||
[FieldLoader.Ignore] public CellRegion CellsInsideBounds;
|
||||
[FieldLoader.Ignore] public ProjectedCellRegion ProjectedCellBounds;
|
||||
[FieldLoader.Ignore] public CellRegion AllCells;
|
||||
|
||||
readonly Func<PPos, bool> containsTest;
|
||||
|
||||
void AssertExists(string filename)
|
||||
{
|
||||
using (var s = Container.GetContent(filename))
|
||||
@@ -268,6 +273,8 @@ namespace OpenRA
|
||||
/// </summary>
|
||||
public Map(TileSet tileset, int width, int height)
|
||||
{
|
||||
containsTest = Contains;
|
||||
|
||||
var size = new Size(width, height);
|
||||
var tileShape = Game.ModData.Manifest.TileShape;
|
||||
var tileRef = new TerrainTile(tileset.Templates.First().Key, (byte)0);
|
||||
@@ -307,6 +314,8 @@ namespace OpenRA
|
||||
/// <summary>Initializes a map loaded from disk.</summary>
|
||||
public Map(string path)
|
||||
{
|
||||
containsTest = Contains;
|
||||
|
||||
Path = path;
|
||||
Container = GlobalFileSystem.OpenPackage(path, null, int.MaxValue);
|
||||
|
||||
@@ -409,8 +418,8 @@ namespace OpenRA
|
||||
var br = new MPos(MapSize.X - 1, MapSize.Y - 1).ToCPos(this);
|
||||
AllCells = new CellRegion(TileShape, tl, br);
|
||||
|
||||
var btl = new MPos(Bounds.Left, Bounds.Top);
|
||||
var bbr = new MPos(Bounds.Right - 1, Bounds.Bottom - 1);
|
||||
var btl = new PPos(Bounds.Left, Bounds.Top);
|
||||
var bbr = new PPos(Bounds.Right - 1, Bounds.Bottom - 1);
|
||||
SetBounds(btl, bbr);
|
||||
|
||||
CustomTerrain = new CellLayer<byte>(this);
|
||||
@@ -428,6 +437,80 @@ namespace OpenRA
|
||||
rightDelta + new WVec(0, 0, 512 * ramp[2]),
|
||||
bottomDelta + new WVec(0, 0, 512 * ramp[3])
|
||||
}).ToArray();
|
||||
|
||||
if (MaximumTerrainHeight != 0)
|
||||
{
|
||||
cellProjection = new CellLayer<PPos[]>(this);
|
||||
inverseCellProjection = new CellLayer<List<MPos>>(this);
|
||||
|
||||
// Initialize collections
|
||||
foreach (var cell in AllCells)
|
||||
{
|
||||
var uv = cell.ToMPos(TileShape);
|
||||
cellProjection[uv] = new PPos[0];
|
||||
inverseCellProjection[uv] = new List<MPos>();
|
||||
}
|
||||
|
||||
// Initialize projections
|
||||
foreach (var cell in AllCells)
|
||||
UpdateProjection(cell);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateProjection(CPos cell)
|
||||
{
|
||||
if (MaximumTerrainHeight == 0)
|
||||
return;
|
||||
|
||||
var uv = cell.ToMPos(TileShape);
|
||||
|
||||
// Remove old reverse projection
|
||||
foreach (var puv in cellProjection[uv])
|
||||
inverseCellProjection[(MPos)puv].Remove(uv);
|
||||
|
||||
var projected = ProjectCellInner(uv);
|
||||
cellProjection[uv] = projected;
|
||||
|
||||
foreach (var puv in projected)
|
||||
inverseCellProjection[(MPos)puv].Add(uv);
|
||||
}
|
||||
|
||||
PPos[] ProjectCellInner(MPos uv)
|
||||
{
|
||||
var mapHeight = MapHeight.Value;
|
||||
if (!mapHeight.Contains(uv))
|
||||
return NoProjectedCells;
|
||||
|
||||
var height = mapHeight[uv];
|
||||
if (height == 0)
|
||||
return new[] { (PPos)uv };
|
||||
|
||||
// Odd-height ramps get bumped up a level to the next even height layer
|
||||
if ((height & 1) == 1)
|
||||
{
|
||||
var ti = cachedTileSet.Value.GetTileInfo(MapTiles.Value[uv]);
|
||||
if (ti != null && ti.RampType != 0)
|
||||
height += 1;
|
||||
}
|
||||
|
||||
var candidates = new List<PPos>();
|
||||
|
||||
// Odd-height level tiles are equally covered by four projected tiles
|
||||
if ((height & 1) == 1)
|
||||
{
|
||||
if ((uv.V & 1) == 1)
|
||||
candidates.Add(new PPos(uv.U + 1, uv.V - height));
|
||||
else
|
||||
candidates.Add(new PPos(uv.U - 1, uv.V - height));
|
||||
|
||||
candidates.Add(new PPos(uv.U, uv.V - height));
|
||||
candidates.Add(new PPos(uv.U, uv.V - height + 1));
|
||||
candidates.Add(new PPos(uv.U, uv.V - height - 1));
|
||||
}
|
||||
else
|
||||
candidates.Add(new PPos(uv.U, uv.V - height));
|
||||
|
||||
return candidates.Where(c => mapHeight.Contains((MPos)c)).ToArray();
|
||||
}
|
||||
|
||||
public Ruleset PreloadRules()
|
||||
@@ -534,6 +617,8 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
tiles.CellEntryChanged += UpdateProjection;
|
||||
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@@ -552,6 +637,8 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
tiles.CellEntryChanged += UpdateProjection;
|
||||
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@@ -652,7 +739,15 @@ namespace OpenRA
|
||||
|
||||
public bool Contains(MPos uv)
|
||||
{
|
||||
return Bounds.Contains(uv.U, uv.V);
|
||||
// The first check ensures that the cell is within the valid map region, avoiding
|
||||
// potential crashes in deeper code. All CellLayers have the same geometry, and
|
||||
// CustomTerrain is convenient (cellProjection may be null and others are Lazy).
|
||||
return CustomTerrain.Contains(uv) && ProjectedCellsCovering(uv).All(containsTest);
|
||||
}
|
||||
|
||||
public bool Contains(PPos puv)
|
||||
{
|
||||
return Bounds.Contains(puv.U, puv.V);
|
||||
}
|
||||
|
||||
public WPos CenterOfCell(CPos cell)
|
||||
@@ -695,6 +790,39 @@ namespace OpenRA
|
||||
return new CPos(u, v);
|
||||
}
|
||||
|
||||
public PPos ProjectedCellCovering(WPos pos)
|
||||
{
|
||||
var projectedPos = pos - new WVec(0, pos.Z, pos.Z);
|
||||
return (PPos)CellContaining(projectedPos).ToMPos(TileShape);
|
||||
}
|
||||
|
||||
static readonly PPos[] NoProjectedCells = { };
|
||||
public PPos[] ProjectedCellsCovering(MPos uv)
|
||||
{
|
||||
// Shortcut for mods that don't use heightmaps
|
||||
if (MaximumTerrainHeight == 0)
|
||||
return new[] { (PPos)uv };
|
||||
|
||||
if (!cellProjection.Contains(uv))
|
||||
return NoProjectedCells;
|
||||
|
||||
return cellProjection[uv];
|
||||
}
|
||||
|
||||
public MPos[] Unproject(PPos puv)
|
||||
{
|
||||
var uv = (MPos)puv;
|
||||
|
||||
// Shortcut for mods that don't use heightmaps
|
||||
if (MaximumTerrainHeight == 0)
|
||||
return new[] { uv };
|
||||
|
||||
if (!inverseCellProjection.Contains(uv))
|
||||
return new MPos[0];
|
||||
|
||||
return inverseCellProjection[uv].ToArray();
|
||||
}
|
||||
|
||||
public int FacingBetween(CPos cell, CPos towards, int fallbackfacing)
|
||||
{
|
||||
return Traits.Util.GetFacing(CenterOfCell(towards) - CenterOfCell(cell), fallbackfacing);
|
||||
@@ -717,12 +845,11 @@ namespace OpenRA
|
||||
AllCells = new CellRegion(TileShape, tl, br);
|
||||
}
|
||||
|
||||
public void SetBounds(MPos tl, MPos br)
|
||||
public void SetBounds(PPos tl, PPos br)
|
||||
{
|
||||
// The tl and br coordinates are inclusive, but the Rectangle
|
||||
// is exclusive. Pad the right and bottom edges to match.
|
||||
Bounds = Rectangle.FromLTRB(tl.U, tl.V, br.U + 1, br.V + 1);
|
||||
CellsInsideBounds = new CellRegion(TileShape, tl.ToCPos(this), br.ToCPos(this));
|
||||
|
||||
// Directly calculate the projected map corners in world units avoiding unnecessary
|
||||
// conversions. This abuses the definition that the width of the cell is always
|
||||
@@ -738,6 +865,8 @@ namespace OpenRA
|
||||
|
||||
ProjectedTopLeft = new WPos(tl.U * 1024, wtop, 0);
|
||||
ProjectedBottomRight = new WPos(br.U * 1024 - 1, wbottom - 1, 0);
|
||||
|
||||
ProjectedCellBounds = new ProjectedCellRegion(this, tl, br);
|
||||
}
|
||||
|
||||
string ComputeHash()
|
||||
@@ -799,18 +928,28 @@ namespace OpenRA
|
||||
|
||||
public CPos Clamp(CPos cell)
|
||||
{
|
||||
var bounds = new Rectangle(Bounds.X, Bounds.Y, Bounds.Width - 1, Bounds.Height - 1);
|
||||
return cell.ToMPos(this).Clamp(bounds).ToCPos(this);
|
||||
return Clamp(cell.ToMPos(this)).ToCPos(this);
|
||||
}
|
||||
|
||||
public MPos Clamp(MPos uv)
|
||||
{
|
||||
// Already in bounds, so don't need to do anything.
|
||||
if (Contains(uv))
|
||||
return uv;
|
||||
|
||||
// TODO: Account for terrain height
|
||||
return (MPos)Clamp((PPos)uv);
|
||||
}
|
||||
|
||||
public PPos Clamp(PPos puv)
|
||||
{
|
||||
var bounds = new Rectangle(Bounds.X, Bounds.Y, Bounds.Width - 1, Bounds.Height - 1);
|
||||
return uv.Clamp(bounds);
|
||||
return puv.Clamp(bounds);
|
||||
}
|
||||
|
||||
public CPos ChooseRandomCell(MersenneTwister rand)
|
||||
{
|
||||
// TODO: Account for terrain height
|
||||
var x = rand.Next(Bounds.Left, Bounds.Right);
|
||||
var y = rand.Next(Bounds.Top, Bounds.Bottom);
|
||||
|
||||
@@ -819,6 +958,7 @@ namespace OpenRA
|
||||
|
||||
public CPos ChooseClosestEdgeCell(CPos pos)
|
||||
{
|
||||
// TODO: Account for terrain height
|
||||
var mpos = pos.ToMPos(this);
|
||||
|
||||
var horizontalBound = ((mpos.U - Bounds.Left) < Bounds.Width / 2) ? Bounds.Left : Bounds.Right;
|
||||
@@ -832,6 +972,7 @@ namespace OpenRA
|
||||
|
||||
public CPos ChooseRandomEdgeCell(MersenneTwister rand)
|
||||
{
|
||||
// TODO: Account for terrain height
|
||||
var isX = rand.Next(2) == 0;
|
||||
var edge = rand.Next(2) == 0;
|
||||
|
||||
@@ -843,8 +984,10 @@ namespace OpenRA
|
||||
|
||||
public WDist DistanceToEdge(WPos pos, WVec dir)
|
||||
{
|
||||
var tl = CenterOfCell(CellsInsideBounds.TopLeft) - new WVec(512, 512, 0);
|
||||
var br = CenterOfCell(CellsInsideBounds.BottomRight) + new WVec(511, 511, 0);
|
||||
// TODO: Account for terrain height
|
||||
// Project into the screen plane and then compare against ProjectedWorldBounds.
|
||||
var tl = CenterOfCell(((MPos)ProjectedCellBounds.TopLeft).ToCPos(this)) - new WVec(512, 512, 0);
|
||||
var br = CenterOfCell(((MPos)ProjectedCellBounds.BottomRight).ToCPos(this)) + new WVec(511, 511, 0);
|
||||
var x = dir.X == 0 ? int.MaxValue : ((dir.X < 0 ? tl.X : br.X) - pos.X) / dir.X;
|
||||
var y = dir.Y == 0 ? int.MaxValue : ((dir.Y < 0 ? tl.Y : br.Y) - pos.Y) / dir.Y;
|
||||
return new WDist(Math.Min(x, y) * dir.Length);
|
||||
|
||||
125
OpenRA.Game/Map/ProjectedCellRegion.cs
Normal file
125
OpenRA.Game/Map/ProjectedCellRegion.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 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. For more information,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
// Represents a (on-screen) rectangular collection of tiles.
|
||||
// TopLeft and BottomRight are inclusive
|
||||
public class ProjectedCellRegion : IEnumerable<PPos>
|
||||
{
|
||||
// Corners of the region
|
||||
public readonly PPos TopLeft;
|
||||
public readonly PPos BottomRight;
|
||||
|
||||
// Corners of the bounding map region that contains all the cells that
|
||||
// may be projected within this region.
|
||||
readonly MPos mapTopLeft;
|
||||
readonly MPos mapBottomRight;
|
||||
|
||||
public ProjectedCellRegion(Map map, PPos topLeft, PPos bottomRight)
|
||||
{
|
||||
TopLeft = topLeft;
|
||||
BottomRight = bottomRight;
|
||||
|
||||
// The projection from MPos -> PPos cannot produce a larger V coordinate
|
||||
// so the top edge of the MPos region is the same as the PPos region.
|
||||
// (in fact the cells are identical if height == 0)
|
||||
mapTopLeft = (MPos)topLeft;
|
||||
|
||||
// The bottom edge is trickier: cells at MPos.V > bottomRight.V may have
|
||||
// been projected into this region if they have height > 0.
|
||||
// Each height step is equivalent to 512 WRange units, which is one MPos
|
||||
// step for diamond cells, but only half a MPos step for classic cells. Doh!
|
||||
var heightOffset = map.TileShape == TileShape.Diamond ? map.MaximumTerrainHeight : map.MaximumTerrainHeight / 2;
|
||||
|
||||
// Use the MapHeight data array to clamp the bottom coordinate so it doesn't overflow the map
|
||||
mapBottomRight = map.MapHeight.Value.Clamp(new MPos(bottomRight.U, bottomRight.V + heightOffset));
|
||||
}
|
||||
|
||||
public bool Contains(PPos puv)
|
||||
{
|
||||
return puv.U >= TopLeft.U && puv.U <= BottomRight.U && puv.V >= TopLeft.V && puv.V <= BottomRight.V;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The region in map coordinates that contains all the cells that
|
||||
/// may be projected inside this region. For increased performance,
|
||||
/// this does not validate whether individual map cells are actually
|
||||
/// projected inside the region.
|
||||
/// </summary>
|
||||
public MapCoordsRegion CandidateMapCoords { get { return new MapCoordsRegion(mapTopLeft, mapBottomRight); } }
|
||||
|
||||
public ProjectedCellRegionEnumerator GetEnumerator()
|
||||
{
|
||||
return new ProjectedCellRegionEnumerator(this);
|
||||
}
|
||||
|
||||
IEnumerator<PPos> IEnumerable<PPos>.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public sealed class ProjectedCellRegionEnumerator : IEnumerator<PPos>
|
||||
{
|
||||
readonly ProjectedCellRegion r;
|
||||
|
||||
// Current position, in projected map coordinates
|
||||
int u, v;
|
||||
|
||||
PPos current;
|
||||
|
||||
public ProjectedCellRegionEnumerator(ProjectedCellRegion region)
|
||||
{
|
||||
r = region;
|
||||
Reset();
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
u += 1;
|
||||
|
||||
// Check for column overflow
|
||||
if (u > r.BottomRight.U)
|
||||
{
|
||||
v += 1;
|
||||
u = r.TopLeft.U;
|
||||
|
||||
// Check for row overflow
|
||||
if (v > r.BottomRight.V)
|
||||
return false;
|
||||
}
|
||||
|
||||
current = new PPos(u, v);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
// Enumerator starts *before* the first element in the sequence.
|
||||
u = r.TopLeft.U - 1;
|
||||
v = r.TopLeft.V;
|
||||
}
|
||||
|
||||
public PPos Current { get { return current; } }
|
||||
object IEnumerator.Current { get { return Current; } }
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -248,6 +248,7 @@
|
||||
<Compile Include="Widgets\WorldInteractionControllerWidget.cs" />
|
||||
<Compile Include="Graphics\PaletteReference.cs" />
|
||||
<Compile Include="Graphics\TerrainSpriteLayer.cs" />
|
||||
<Compile Include="Map\ProjectedCellRegion.cs" />
|
||||
<Compile Include="Map\MapCoordsRegion.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -204,7 +204,7 @@ namespace OpenRA.Traits
|
||||
throw new ArgumentException("The map bounds of these shrouds do not match.", "s");
|
||||
|
||||
var changed = new List<CPos>();
|
||||
foreach (var uv in map.CellsInsideBounds.MapCoords)
|
||||
foreach (var uv in map.ProjectedCellBounds.CandidateMapCoords)
|
||||
{
|
||||
if (!explored[uv] && s.explored[uv])
|
||||
{
|
||||
@@ -219,7 +219,7 @@ namespace OpenRA.Traits
|
||||
public void ExploreAll(World world)
|
||||
{
|
||||
var changed = new List<CPos>();
|
||||
foreach (var uv in map.CellsInsideBounds.MapCoords)
|
||||
foreach (var uv in map.ProjectedCellBounds.CandidateMapCoords)
|
||||
{
|
||||
if (!explored[uv])
|
||||
{
|
||||
@@ -234,7 +234,7 @@ namespace OpenRA.Traits
|
||||
public void ResetExploration()
|
||||
{
|
||||
var changed = new List<CPos>();
|
||||
foreach (var uv in map.CellsInsideBounds.MapCoords)
|
||||
foreach (var uv in map.ProjectedCellBounds.CandidateMapCoords)
|
||||
{
|
||||
var visible = visibleCount[uv] > 0;
|
||||
if (explored[uv] != visible)
|
||||
|
||||
@@ -60,23 +60,24 @@ namespace OpenRA.Mods.Common.Traits
|
||||
var doDim = refreshTick - world.WorldTick <= 0;
|
||||
if (doDim) refreshTick = world.WorldTick + 20;
|
||||
|
||||
var map = wr.World.Map;
|
||||
foreach (var pair in layers)
|
||||
{
|
||||
var c = (pair.Key != null) ? pair.Key.Color.RGB : Color.PaleTurquoise;
|
||||
var layer = pair.Value;
|
||||
|
||||
// Only render quads in viewing range:
|
||||
foreach (var cell in wr.Viewport.VisibleCellsInsideBounds)
|
||||
foreach (var uv in wr.Viewport.VisibleCellsInsideBounds.CandidateMapCoords)
|
||||
{
|
||||
if (layer[cell] <= 0)
|
||||
if (layer[uv] <= 0)
|
||||
continue;
|
||||
|
||||
var w = Math.Max(0, Math.Min(layer[cell], 128));
|
||||
var w = Math.Max(0, Math.Min(layer[uv], 128));
|
||||
if (doDim)
|
||||
layer[cell] = layer[cell] * 5 / 6;
|
||||
layer[uv] = layer[uv] * 5 / 6;
|
||||
|
||||
// TODO: This doesn't make sense for isometric terrain
|
||||
var pos = wr.World.Map.CenterOfCell(cell);
|
||||
var pos = wr.World.Map.CenterOfCell(uv.ToCPos(map));
|
||||
var tl = wr.ScreenPxPosition(pos - new WVec(512, 512, 0));
|
||||
var br = wr.ScreenPxPosition(pos + new WVec(511, 511, 0));
|
||||
qr.FillRect(RectangleF.FromLTRB(tl.X, tl.Y, br.X, br.Y), Color.FromArgb(w, c));
|
||||
|
||||
@@ -249,7 +249,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
|
||||
currentShroud = shroud;
|
||||
DirtyCells(map.CellsInsideBounds);
|
||||
var dirty = map.ProjectedCellBounds
|
||||
.SelectMany(puv => map.Unproject(puv).Select(uv => uv.ToCPos(map)));
|
||||
DirtyCells(dirty);
|
||||
}
|
||||
|
||||
// We need to update newly dirtied areas of the shroud.
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
var colors = wr.World.TileSet.HeightDebugColors;
|
||||
var mouseCell = wr.Viewport.ViewToWorld(Viewport.LastMousePos).ToMPos(wr.World.Map);
|
||||
|
||||
foreach (var uv in wr.Viewport.AllVisibleCells.MapCoords)
|
||||
foreach (var uv in wr.Viewport.AllVisibleCells.CandidateMapCoords)
|
||||
{
|
||||
var height = (int)map.MapHeight.Value[uv];
|
||||
var tile = map.MapTiles.Value[uv];
|
||||
@@ -80,6 +80,22 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
lr.LineWidth = 1;
|
||||
}
|
||||
|
||||
// Projected cell coordinates for the current cell
|
||||
var projectedCorners = map.CellCorners[0];
|
||||
lr.LineWidth = 3;
|
||||
foreach (var puv in map.ProjectedCellsCovering(mouseCell))
|
||||
{
|
||||
var pos = map.CenterOfCell(((MPos)puv).ToCPos(map));
|
||||
var screen = projectedCorners.Select(c => wr.ScreenPxPosition(pos + c - new WVec(0, 0, pos.Z)).ToFloat2()).ToArray();
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
var j = (i + 1) % 4;
|
||||
lr.DrawLine(screen[i], screen[j], Color.Navy);
|
||||
}
|
||||
}
|
||||
|
||||
lr.LineWidth = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,8 +151,8 @@ namespace OpenRA.Mods.Common.UtilityCommands
|
||||
Author = "Westwood Studios"
|
||||
};
|
||||
|
||||
var tl = new MPos(offsetX, offsetY);
|
||||
var br = new MPos(offsetX + width - 1, offsetY + height - 1);
|
||||
var tl = new PPos(offsetX, offsetY);
|
||||
var br = new PPos(offsetX + width - 1, offsetY + height - 1);
|
||||
map.SetBounds(tl, br);
|
||||
|
||||
if (legacyMapFormat == IniMapFormat.RedAlert)
|
||||
|
||||
@@ -64,8 +64,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
var tileset = modRules.TileSets[tilesetDropDown.Text];
|
||||
var map = new Map(tileset, width + 2, height + maxTerrainHeight + 2);
|
||||
|
||||
var tl = new MPos(1, 1);
|
||||
var br = new MPos(width, height + maxTerrainHeight);
|
||||
var tl = new PPos(1, 1);
|
||||
var br = new PPos(width, height + maxTerrainHeight);
|
||||
map.SetBounds(tl, br);
|
||||
|
||||
map.PlayerDefinitions = new MapPlayers(map.Rules, map.SpawnPoints.Value.Length).ToMiniYaml();
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
actorSprite = new Sprite(radarSheet, new Rectangle(0, height, width, height), TextureChannel.Alpha);
|
||||
|
||||
// Set initial terrain data
|
||||
foreach (var cell in world.Map.CellsInsideBounds)
|
||||
foreach (var cell in world.Map.AllCells)
|
||||
UpdateTerrainCell(cell);
|
||||
|
||||
world.Map.MapTiles.Value.CellEntryChanged += UpdateTerrainCell;
|
||||
@@ -290,7 +290,7 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
if (newRenderShroud != null)
|
||||
{
|
||||
// Redraw the full shroud sprite
|
||||
MarkShroudDirty(world.Map.CellsInsideBounds);
|
||||
MarkShroudDirty(world.Map.AllCells);
|
||||
|
||||
// Update the notification binding
|
||||
newRenderShroud.CellsChanged += MarkShroudDirty;
|
||||
|
||||
@@ -315,8 +315,8 @@ namespace OpenRA.Mods.D2k.UtilityCommands
|
||||
Author = "Westwood Studios"
|
||||
};
|
||||
|
||||
var tl = new MPos(MapCordonWidth, MapCordonWidth);
|
||||
var br = new MPos(MapCordonWidth + mapSize.Width - 1, MapCordonWidth + mapSize.Height - 1);
|
||||
var tl = new PPos(MapCordonWidth, MapCordonWidth);
|
||||
var br = new PPos(MapCordonWidth + mapSize.Width - 1, MapCordonWidth + mapSize.Height - 1);
|
||||
map.SetBounds(tl, br);
|
||||
|
||||
// Get all templates from the tileset YAML file that have at least one frame and an Image property corresponding to the requested tileset
|
||||
|
||||
Reference in New Issue
Block a user