Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Account for slopes in Map.DistanceAboveTerrain. #17990

Merged
merged 3 commits into from May 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 6 additions & 2 deletions OpenRA.Game/Graphics/TerrainSpriteLayer.cs
Expand Up @@ -68,8 +68,12 @@ void UpdatePaletteIndices()

public void Update(CPos cell, Sprite sprite)
{
var xyz = sprite == null ? float3.Zero :
worldRenderer.Screen3DPosition(map.CenterOfCell(cell)) + sprite.Offset - 0.5f * sprite.Size;
var xyz = float3.Zero;
if (sprite != null)
{
var cellOrigin = map.CenterOfCell(cell) - new WVec(0, 0, map.Grid.Ramps[map.Ramp[cell]].CenterHeightOffset);
xyz = worldRenderer.Screen3DPosition(cellOrigin) + sprite.Offset - 0.5f * sprite.Size;
}

Update(cell.ToMPos(map.Grid.Type), sprite, xyz);
}
Expand Down
16 changes: 3 additions & 13 deletions OpenRA.Game/Graphics/Viewport.cs
Expand Up @@ -254,7 +254,6 @@ public CPos ViewToWorld(int2 view)
var world = worldRenderer.Viewport.ViewToWorldPx(view);
var map = worldRenderer.World.Map;
var candidates = CandidateMouseoverCells(world).ToList();
var tileSet = worldRenderer.World.Map.Rules.TileSet;

foreach (var uv in candidates)
{
Expand All @@ -263,18 +262,9 @@ public CPos ViewToWorld(int2 view)
var s = worldRenderer.ScreenPxPosition(p);
if (Math.Abs(s.X - world.X) <= tileSize.Width && Math.Abs(s.Y - world.Y) <= tileSize.Height)
{
var ramp = 0;
if (map.Contains(uv))
{
var ti = tileSet.GetTileInfo(map.Tiles[uv]);
if (ti != null)
ramp = ti.RampType;
}

var corners = map.Grid.CellCorners[ramp];
var pos = map.CenterOfCell(uv.ToCPos(map));
var screen = corners.Select(c => worldRenderer.ScreenPxPosition(pos + c)).ToArray();

var ramp = map.Grid.Ramps[map.Ramp.Contains(uv) ? map.Ramp[uv] : 0];
var pos = map.CenterOfCell(uv.ToCPos(map)) - new WVec(0, 0, ramp.CenterHeightOffset);
var screen = ramp.Corners.Select(c => worldRenderer.ScreenPxPosition(pos + c)).ToArray();
if (screen.PolygonContains(world))
return uv.ToCPos(map);
}
Expand Down
61 changes: 51 additions & 10 deletions OpenRA.Game/Map/Map.cs
Expand Up @@ -230,6 +230,7 @@ public class Map : IReadOnlyFileSystem
public CellLayer<TerrainTile> Tiles { get; private set; }
public CellLayer<ResourceTile> Resources { get; private set; }
public CellLayer<byte> Height { get; private set; }
public CellLayer<byte> Ramp { get; private set; }
public CellLayer<byte> CustomTerrain { get; private set; }

public ProjectedCellRegion ProjectedCellBounds { get; private set; }
Expand Down Expand Up @@ -301,10 +302,12 @@ public Map(ModData modData, TileSet tileset, int width, int height)
Tiles = new CellLayer<TerrainTile>(Grid.Type, size);
Resources = new CellLayer<ResourceTile>(Grid.Type, size);
Height = new CellLayer<byte>(Grid.Type, size);
Ramp = new CellLayer<byte>(Grid.Type, size);
if (Grid.MaximumTerrainHeight > 0)
{
Height.CellEntryChanged += UpdateProjection;
Tiles.CellEntryChanged += UpdateProjection;
Tiles.CellEntryChanged += UpdateRamp;
}

Tiles.Clear(tileRef);
Expand Down Expand Up @@ -336,6 +339,7 @@ public Map(ModData modData, IReadOnlyPackage package)
Tiles = new CellLayer<TerrainTile>(Grid.Type, size);
Resources = new CellLayer<ResourceTile>(Grid.Type, size);
Height = new CellLayer<byte>(Grid.Type, size);
Ramp = new CellLayer<byte>(Grid.Type, size);

using (var s = Package.GetStream("map.bin"))
{
Expand Down Expand Up @@ -384,6 +388,7 @@ public Map(ModData modData, IReadOnlyPackage package)

if (Grid.MaximumTerrainHeight > 0)
{
Tiles.CellEntryChanged += UpdateRamp;
Tiles.CellEntryChanged += UpdateProjection;
Height.CellEntryChanged += UpdateProjection;
}
Expand Down Expand Up @@ -422,9 +427,23 @@ void PostInit()
foreach (var uv in AllCells.MapCoords)
CustomTerrain[uv] = byte.MaxValue;

// Cache initial ramp state
var tileset = Rules.TileSet;
foreach (var uv in AllCells)
{
var tile = tileset.GetTileInfo(Tiles[uv]);
Ramp[uv] = tile != null ? tile.RampType : (byte)0;
}

AllEdgeCells = UpdateEdgeCells();
}

void UpdateRamp(CPos cell)
{
var tile = Rules.TileSet.GetTileInfo(Tiles[cell]);
Ramp[cell] = tile != null ? tile.RampType : (byte)0;
}

void InitializeCellProjection()
{
if (initializedCellProjection)
Expand Down Expand Up @@ -531,12 +550,8 @@ PPos[] ProjectCellInner(MPos uv)
return new[] { (PPos)uv };

// Odd-height ramps get bumped up a level to the next even height layer
if ((height & 1) == 1)
{
var ti = Rules.TileSet.GetTileInfo(Tiles[uv]);
if (ti != null && ti.RampType != 0)
height += 1;
}
if ((height & 1) == 1 && Ramp[uv] != 0)
height += 1;

var candidates = new List<PPos>();

Expand Down Expand Up @@ -805,23 +820,47 @@ public WPos CenterOfCell(CPos cell)
// (c) u, v coordinates run diagonally to the cell axes, and we define
// 1024 as the length projected onto the primary cell axis
// - 512 * sqrt(2) = 724
var z = Height.Contains(cell) ? 724 * Height[cell] : 0;
var z = Height.Contains(cell) ? 724 * Height[cell] + Grid.Ramps[Ramp[cell]].CenterHeightOffset : 0;
return new WPos(724 * (cell.X - cell.Y + 1), 724 * (cell.X + cell.Y + 1), z);
}

public WPos CenterOfSubCell(CPos cell, SubCell subCell)
{
var index = (int)subCell;
if (index >= 0 && index < Grid.SubCellOffsets.Length)
return CenterOfCell(cell) + Grid.SubCellOffsets[index];
{
var center = CenterOfCell(cell);
var offset = Grid.SubCellOffsets[index];
var ramp = Ramp.Contains(cell) ? Ramp[cell] : 0;
if (ramp != 0)
{
var r = Grid.Ramps[ramp];
offset += new WVec(0, 0, r.HeightOffset(offset.X, offset.Y) - r.CenterHeightOffset);
}

return center + offset;
}

return CenterOfCell(cell);
}

public WDist DistanceAboveTerrain(WPos pos)
{
if (Grid.Type == MapGridType.Rectangular)
return new WDist(pos.Z);

// Apply ramp offset
var cell = CellContaining(pos);
var delta = pos - CenterOfCell(cell);
return new WDist(delta.Z);
var offset = pos - CenterOfCell(cell);

var ramp = Ramp[cell];
if (ramp != 0)
{
var r = Grid.Ramps[ramp];
return new WDist(offset.Z + r.CenterHeightOffset - r.HeightOffset(offset.X, offset.Y));
}

return new WDist(offset.Z);
}

public WVec Offset(CVec delta, int dz)
Expand Down Expand Up @@ -913,11 +952,13 @@ public void Resize(int width, int height)
var oldMapTiles = Tiles;
var oldMapResources = Resources;
var oldMapHeight = Height;
var oldMapRamp = Ramp;
var newSize = new Size(width, height);

Tiles = CellLayer.Resize(oldMapTiles, newSize, oldMapTiles[MPos.Zero]);
Resources = CellLayer.Resize(oldMapResources, newSize, oldMapResources[MPos.Zero]);
Height = CellLayer.Resize(oldMapHeight, newSize, oldMapHeight[MPos.Zero]);
Ramp = CellLayer.Resize(oldMapRamp, newSize, oldMapHeight[MPos.Zero]);
MapSize = new int2(newSize);

var tl = new MPos(0, 0);
Expand Down
176 changes: 115 additions & 61 deletions OpenRA.Game/Map/MapGrid.cs
Expand Up @@ -9,7 +9,6 @@
*/
#endregion

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
Expand All @@ -20,6 +19,85 @@ namespace OpenRA
{
public enum MapGridType { Rectangular, RectangularIsometric }

public enum RampSplit { Flat, X, Y }
public enum RampCornerHeight { Low = 0, Half = 1, Full = 2 }

public struct CellRamp
{
public readonly int CenterHeightOffset;
public readonly WVec[] Corners;
public readonly WVec[][] Polygons;

public CellRamp(MapGridType type, RampCornerHeight tl = RampCornerHeight.Low, RampCornerHeight tr = RampCornerHeight.Low, RampCornerHeight br = RampCornerHeight.Low, RampCornerHeight bl = RampCornerHeight.Low, RampSplit split = RampSplit.Flat)
{
if (type == MapGridType.RectangularIsometric)
{
Corners = new[]
{
new WVec(0, -724, 724 * (int)tl),
new WVec(724, 0, 724 * (int)tr),
new WVec(0, 724, 724 * (int)br),
new WVec(-724, 0, 724 * (int)bl),
};
}
else
{
Corners = new[]
{
new WVec(-512, -512, 512 * (int)tl),
new WVec(512, -512, 512 * (int)tr),
new WVec(512, 512, 512 * (int)br),
new WVec(-512, 512, 512 * (int)bl)
};
}

if (split == RampSplit.X)
{
Polygons = new[]
{
new[] { Corners[0], Corners[1], Corners[3] },
new[] { Corners[1], Corners[2], Corners[3] }
};
}
else if (split == RampSplit.Y)
{
Polygons = new[]
{
new[] { Corners[0], Corners[1], Corners[2] },
new[] { Corners[0], Corners[2], Corners[3] }
};
}
else
Polygons = new[] { Corners };

// Initial value must be asigned before HeightOffset can be called
CenterHeightOffset = 0;
CenterHeightOffset = HeightOffset(0, 0);
}

public int HeightOffset(int dX, int dY)
{
// Enumerate over the polygons, assuming that they are triangles
// If the ramp is not split we will take the first three vertices of the corners as a valid triangle
WVec[] p = null;
var u = 0;
var v = 0;
for (var i = 0; i < Polygons.Length; i++)
{
p = Polygons[i];
u = ((p[1].Y - p[2].Y) * (dX - p[2].X) - (p[1].X - p[2].X) * (dY - p[2].Y)) / 1024;
v = ((p[0].X - p[2].X) * (dY - p[2].Y) - (p[0].Y - p[2].Y) * (dX - p[2].X)) / 1024;

// Point is within the triangle if 0 <= u,v <= 1024
if (u >= 0 && u <= 1024 && v >= 0 && v <= 1024)
break;
}

// Calculate w from u,v and interpolate height
return (u * p[0].Z + v * p[1].Z + (1024 - u - v) * p[2].Z) / 1024;
}
}

public class MapGrid : IGlobalModData
{
public readonly MapGridType Type = MapGridType.Rectangular;
Expand All @@ -41,43 +119,7 @@ public class MapGrid : IGlobalModData
new WVec(256, 256, 0), // bottom right - index 5
};

public WVec[][] CellCorners { get; private set; }

readonly int[][] cellCornerHalfHeights = new int[][]
{
// Flat
new[] { 0, 0, 0, 0 },

// Slopes (two corners high)
new[] { 0, 0, 1, 1 },
new[] { 1, 0, 0, 1 },
new[] { 1, 1, 0, 0 },
new[] { 0, 1, 1, 0 },

// Slopes (one corner high)
new[] { 0, 0, 0, 1 },
new[] { 1, 0, 0, 0 },
new[] { 0, 1, 0, 0 },
new[] { 0, 0, 1, 0 },

// Slopes (three corners high)
new[] { 1, 0, 1, 1 },
new[] { 1, 1, 0, 1 },
new[] { 1, 1, 1, 0 },
new[] { 0, 1, 1, 1 },

// Slopes (two corners high, one corner double high)
new[] { 1, 0, 1, 2 },
new[] { 2, 1, 0, 1 },
new[] { 1, 2, 1, 0 },
new[] { 0, 1, 2, 1 },

// Slopes (two corners high, alternating)
new[] { 1, 0, 1, 0 },
new[] { 0, 1, 0, 1 },
new[] { 1, 0, 1, 0 },
new[] { 0, 1, 0, 1 }
};
public CellRamp[] Ramps { get; private set; }

internal readonly CVec[][] TilesByDistance;

Expand All @@ -96,32 +138,44 @@ public MapGrid(MiniYaml yaml)
throw new InvalidDataException("Subcell default index must be a valid index into the offset triples and must be greater than 0 for mods with subcells");
}

var makeCorners = Type == MapGridType.RectangularIsometric ?
(Func<int[], WVec[]>)IsometricCellCorners : RectangularCellCorners;
CellCorners = cellCornerHalfHeights.Select(makeCorners).ToArray();
TilesByDistance = CreateTilesByDistance();
}

static WVec[] IsometricCellCorners(int[] cornerHeight)
{
return new WVec[]
// Slope types are hardcoded following the convention from the TS and RA2 map format
Ramps = new[]
{
new WVec(-724, 0, 724 * cornerHeight[0]),
new WVec(0, -724, 724 * cornerHeight[1]),
new WVec(724, 0, 724 * cornerHeight[2]),
new WVec(0, 724, 724 * cornerHeight[3])
// Flat
new CellRamp(Type),

// Two adjacent corners raised by half a cell
new CellRamp(Type, tr: RampCornerHeight.Half, br: RampCornerHeight.Half),
new CellRamp(Type, br: RampCornerHeight.Half, bl: RampCornerHeight.Half),
new CellRamp(Type, tl: RampCornerHeight.Half, bl: RampCornerHeight.Half),
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Half),

// One corner raised by half a cell
new CellRamp(Type, br: RampCornerHeight.Half, split: RampSplit.X),
new CellRamp(Type, bl: RampCornerHeight.Half, split: RampSplit.Y),
new CellRamp(Type, tl: RampCornerHeight.Half, split: RampSplit.X),
new CellRamp(Type, tr: RampCornerHeight.Half, split: RampSplit.Y),

// Three corners raised by half a cell
new CellRamp(Type, tr: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X),
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.Y),
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X),
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.Y),

// Full tile sloped (mid corners raised by half cell, far corner by full cell)
new CellRamp(Type, tr: RampCornerHeight.Half, br: RampCornerHeight.Full, bl: RampCornerHeight.Half),
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Full),
new CellRamp(Type, tl: RampCornerHeight.Full, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half),
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Full, br: RampCornerHeight.Half),

// Two opposite corners raised by half a cell
new CellRamp(Type, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.Y),
pchote marked this conversation as resolved.
Show resolved Hide resolved
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.Y),
new CellRamp(Type, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X),
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.X),
};
}

static WVec[] RectangularCellCorners(int[] cornerHeight)
{
return new WVec[]
{
new WVec(-512, -512, 512 * cornerHeight[0]),
new WVec(512, -512, 512 * cornerHeight[1]),
new WVec(512, 512, 512 * cornerHeight[2]),
new WVec(-512, 512, 512 * cornerHeight[3])
};
TilesByDistance = CreateTilesByDistance();
}

CVec[][] CreateTilesByDistance()
Expand Down