Implement heightmap-aware view -> cell conversion.
This commit is contained in:
@@ -87,6 +87,29 @@ namespace OpenRA
|
||||
return r.Contains(p.ToPointF());
|
||||
}
|
||||
|
||||
static int WindingDirectionTest(int2 v0, int2 v1, int2 p)
|
||||
{
|
||||
return (v1.X - v0.X) * (p.Y - v0.Y) - (p.X - v0.X) * (v1.Y - v0.Y);
|
||||
}
|
||||
|
||||
public static bool PolygonContains(this int2[] polygon, int2 p)
|
||||
{
|
||||
var windingNumber = 0;
|
||||
|
||||
for (var i = 0; i < polygon.Length; i++)
|
||||
{
|
||||
var tv = polygon[i];
|
||||
var nv = polygon[(i + 1) % polygon.Length];
|
||||
|
||||
if (tv.Y <= p.Y && nv.Y > p.Y && WindingDirectionTest(tv, nv, p) > 0)
|
||||
windingNumber++;
|
||||
else if (tv.Y > p.Y && nv.Y <= p.Y && WindingDirectionTest(tv, nv, p) < 0)
|
||||
windingNumber--;
|
||||
}
|
||||
|
||||
return windingNumber != 0;
|
||||
}
|
||||
|
||||
public static bool HasModifier(this Modifiers k, Modifiers mod)
|
||||
{
|
||||
return (k & mod) == mod;
|
||||
|
||||
@@ -105,9 +105,66 @@ namespace OpenRA.Graphics
|
||||
|
||||
public CPos ViewToWorld(int2 view)
|
||||
{
|
||||
var world = worldRenderer.Viewport.ViewToWorldPx(view);
|
||||
var map = worldRenderer.World.Map;
|
||||
var ts = Game.ModData.Manifest.TileSize;
|
||||
var candidates = CandidateMouseoverCells(world);
|
||||
var tileSet = worldRenderer.World.TileSet;
|
||||
|
||||
foreach (var uv in candidates)
|
||||
{
|
||||
// Coarse filter to nearby cells
|
||||
var p = map.CenterOfCell(uv.ToCPos(map.TileShape));
|
||||
var s = worldRenderer.ScreenPxPosition(p);
|
||||
if (Math.Abs(s.X - world.X) <= ts.Width && Math.Abs(s.Y - world.Y) <= ts.Height)
|
||||
{
|
||||
var tile = map.MapTiles.Value[uv];
|
||||
var ti = tileSet.GetTileInfo(tile);
|
||||
var ramp = ti != null ? ti.RampType : 0;
|
||||
|
||||
var corners = map.CellCorners[ramp];
|
||||
var pos = map.CenterOfCell(uv.ToCPos(map));
|
||||
var screen = corners.Select(c => worldRenderer.ScreenPxPosition(pos + c)).ToArray();
|
||||
|
||||
if (screen.PolygonContains(world))
|
||||
return uv.ToCPos(map);
|
||||
}
|
||||
}
|
||||
|
||||
// Mouse is not directly over a cell (perhaps on a cliff)
|
||||
// Try and find the closest cell
|
||||
if (candidates.Any())
|
||||
{
|
||||
return candidates.OrderBy(uv =>
|
||||
{
|
||||
var p = map.CenterOfCell(uv.ToCPos(map.TileShape));
|
||||
var s = worldRenderer.ScreenPxPosition(p);
|
||||
var dx = Math.Abs(s.X - world.X);
|
||||
var dy = Math.Abs(s.Y - world.Y);
|
||||
|
||||
return dx * dx + dy * dy;
|
||||
}).First().ToCPos(map);
|
||||
}
|
||||
|
||||
// Something is very wrong, but lets return something that isn't completely bogus and hope the caller can recover
|
||||
return worldRenderer.World.Map.CellContaining(worldRenderer.Position(ViewToWorldPx(view)));
|
||||
}
|
||||
|
||||
/// <summary> Returns an unfiltered list of all cells that could potentially contain the mouse cursor</summary>
|
||||
IEnumerable<MPos> CandidateMouseoverCells(int2 world)
|
||||
{
|
||||
var map = worldRenderer.World.Map;
|
||||
var minPos = worldRenderer.Position(world);
|
||||
|
||||
// Find all the cells that could potentially have been clicked
|
||||
var a = map.CellContaining(minPos - new WVec(1024, 0, 0)).ToMPos(map.TileShape);
|
||||
var b = map.CellContaining(minPos + new WVec(512, 512 * maxGroundHeight, 0)).ToMPos(map.TileShape);
|
||||
|
||||
for (var v = b.V; v >= a.V; v--)
|
||||
for (var u = b.U; u >= a.U; u--)
|
||||
yield return new MPos(u, v);
|
||||
}
|
||||
|
||||
public int2 ViewToWorldPx(int2 view) { return (1f / Zoom * view.ToFloat2()).ToInt2() + TopLeft; }
|
||||
public int2 WorldToViewPx(int2 world) { return (Zoom * (world - TopLeft).ToFloat2()).ToInt2(); }
|
||||
|
||||
|
||||
@@ -37,19 +37,21 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (devMode.Value == null || !devMode.Value.ShowTerrainGeometry)
|
||||
return;
|
||||
|
||||
var map = wr.World.Map;
|
||||
var tileSet = wr.World.TileSet;
|
||||
var lr = Game.Renderer.WorldLineRenderer;
|
||||
var colors = wr.World.TileSet.HeightDebugColors;
|
||||
|
||||
foreach (var uv in wr.Viewport.VisibleCells.MapCoords)
|
||||
{
|
||||
var height = (int)wr.World.Map.MapHeight.Value[uv];
|
||||
var tile = wr.World.Map.MapTiles.Value[uv];
|
||||
var ti = wr.World.TileSet.GetTileInfo(tile);
|
||||
var height = (int)map.MapHeight.Value[uv];
|
||||
var tile = map.MapTiles.Value[uv];
|
||||
var ti = tileSet.GetTileInfo(tile);
|
||||
var ramp = ti != null ? ti.RampType : 0;
|
||||
|
||||
var corners = wr.World.Map.CellCorners[ramp];
|
||||
var corners = map.CellCorners[ramp];
|
||||
var color = corners.Select(c => colors[height + c.Z / 512]).ToArray();
|
||||
var pos = wr.World.Map.CenterOfCell(uv.ToCPos(wr.World.Map));
|
||||
var pos = map.CenterOfCell(uv.ToCPos(map));
|
||||
var screen = corners.Select(c => wr.ScreenPxPosition(pos + c).ToFloat2()).ToArray();
|
||||
|
||||
for (var i = 0; i < 4; i++)
|
||||
|
||||
Reference in New Issue
Block a user