From e986e47ac32bc720ccb1caeca8301dfa60ade288 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Mon, 4 May 2020 00:20:48 +0100 Subject: [PATCH 1/3] Cache cell ramps to avoid repeated tileset lookups. --- OpenRA.Game/Graphics/Viewport.cs | 10 +------ OpenRA.Game/Map/Map.cs | 29 +++++++++++++++---- .../Traits/Buildings/BuildingUtils.cs | 10 ++----- .../Conditions/GrantConditionOnDeploy.cs | 12 ++------ .../Traits/World/JumpjetActorLayer.cs | 4 +-- .../Traits/World/LegacyBridgeLayer.cs | 8 +++-- .../Traits/World/ResourceLayer.cs | 10 +------ .../Traits/World/SubterraneanActorLayer.cs | 4 +-- .../Traits/World/TerrainGeometryOverlay.cs | 6 +--- 9 files changed, 37 insertions(+), 56 deletions(-) diff --git a/OpenRA.Game/Graphics/Viewport.cs b/OpenRA.Game/Graphics/Viewport.cs index ee6cd61b6560..4affc0156c3a 100644 --- a/OpenRA.Game/Graphics/Viewport.cs +++ b/OpenRA.Game/Graphics/Viewport.cs @@ -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) { @@ -263,14 +262,7 @@ 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 ramp = map.Ramp.Contains(uv) ? map.Ramp[uv] : 0; var corners = map.Grid.CellCorners[ramp]; var pos = map.CenterOfCell(uv.ToCPos(map)); var screen = corners.Select(c => worldRenderer.ScreenPxPosition(pos + c)).ToArray(); diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs index bc9912addb4b..3b66521130f4 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -230,6 +230,7 @@ public class Map : IReadOnlyFileSystem public CellLayer Tiles { get; private set; } public CellLayer Resources { get; private set; } public CellLayer Height { get; private set; } + public CellLayer Ramp { get; private set; } public CellLayer CustomTerrain { get; private set; } public ProjectedCellRegion ProjectedCellBounds { get; private set; } @@ -301,10 +302,12 @@ public Map(ModData modData, TileSet tileset, int width, int height) Tiles = new CellLayer(Grid.Type, size); Resources = new CellLayer(Grid.Type, size); Height = new CellLayer(Grid.Type, size); + Ramp = new CellLayer(Grid.Type, size); if (Grid.MaximumTerrainHeight > 0) { Height.CellEntryChanged += UpdateProjection; Tiles.CellEntryChanged += UpdateProjection; + Tiles.CellEntryChanged += UpdateRamp; } Tiles.Clear(tileRef); @@ -336,6 +339,7 @@ public Map(ModData modData, IReadOnlyPackage package) Tiles = new CellLayer(Grid.Type, size); Resources = new CellLayer(Grid.Type, size); Height = new CellLayer(Grid.Type, size); + Ramp = new CellLayer(Grid.Type, size); using (var s = Package.GetStream("map.bin")) { @@ -384,6 +388,7 @@ public Map(ModData modData, IReadOnlyPackage package) if (Grid.MaximumTerrainHeight > 0) { + Tiles.CellEntryChanged += UpdateRamp; Tiles.CellEntryChanged += UpdateProjection; Height.CellEntryChanged += UpdateProjection; } @@ -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) @@ -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(); @@ -913,11 +928,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); diff --git a/OpenRA.Mods.Common/Traits/Buildings/BuildingUtils.cs b/OpenRA.Mods.Common/Traits/Buildings/BuildingUtils.cs index b48ff39e3d85..80479c33a6b8 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/BuildingUtils.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/BuildingUtils.cs @@ -39,14 +39,8 @@ public static bool IsCellBuildable(this World world, CPos cell, ActorInfo ai, Bu else if (!bi.AllowInvalidPlacement && world.ActorMap.GetActorsAt(cell).Any(a => a != toIgnore)) return false; - var tile = world.Map.Tiles[cell]; - var tileInfo = world.Map.Rules.TileSet.GetTileInfo(tile); - - // TODO: This is bandaiding over bogus tilesets. - if (tileInfo != null && tileInfo.RampType > 0) - return false; - - return bi.TerrainTypes.Contains(world.Map.GetTerrainInfo(cell).Type); + // Buildings can never be placed on ramps + return world.Map.Ramp[cell] == 0 && bi.TerrainTypes.Contains(world.Map.GetTerrainInfo(cell).Type); } public static bool CanPlaceBuilding(this World world, CPos cell, ActorInfo ai, BuildingInfo bi, Actor toIgnore) diff --git a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDeploy.cs b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDeploy.cs index 0b3a237e9b36..0e5b964e091a 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDeploy.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/GrantConditionOnDeploy.cs @@ -241,16 +241,8 @@ bool IsValidRampType(CPos location) if (Info.CanDeployOnRamps) return true; - var ramp = 0; - if (self.World.Map.Contains(location)) - { - var tile = self.World.Map.Tiles[location]; - var ti = self.World.Map.Rules.TileSet.GetTileInfo(tile); - if (ti != null) - ramp = ti.RampType; - } - - return ramp == 0; + var map = self.World.Map; + return !map.Ramp.Contains(location) || map.Ramp[location] == 0; } void INotifyDeployComplete.FinishedDeploy(Actor self) diff --git a/OpenRA.Mods.Common/Traits/World/JumpjetActorLayer.cs b/OpenRA.Mods.Common/Traits/World/JumpjetActorLayer.cs index 64fe1c2cce61..45357b94d5ed 100644 --- a/OpenRA.Mods.Common/Traits/World/JumpjetActorLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/JumpjetActorLayer.cs @@ -83,9 +83,7 @@ bool ValidTransitionCell(CPos cell, LocomotorInfo li) if (jli.JumpjetTransitionOnRamps) return true; - var tile = map.Tiles[cell]; - var ti = map.Rules.TileSet.GetTileInfo(tile); - return ti == null || ti.RampType == 0; + return map.Ramp[cell] == 0; } int ICustomMovementLayer.EntryMovementCost(ActorInfo a, LocomotorInfo li, CPos cell) diff --git a/OpenRA.Mods.Common/Traits/World/LegacyBridgeLayer.cs b/OpenRA.Mods.Common/Traits/World/LegacyBridgeLayer.cs index b32d3f99e009..890fad8f4729 100644 --- a/OpenRA.Mods.Common/Traits/World/LegacyBridgeLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/LegacyBridgeLayer.cs @@ -65,8 +65,9 @@ void ConvertBridgeToActor(World w, CPos cell) return; // Correlate the tile "image" aka subtile with its position to find the template origin - var tile = w.Map.Tiles[cell].Type; - var index = w.Map.Tiles[cell].Index; + var ti = w.Map.Tiles[cell]; + var tile = ti.Type; + var index = ti.Index; var template = w.Map.Rules.TileSet.Templates[tile]; var ni = cell.X - index % template.Size.X; var nj = cell.Y - index / template.Size.X; @@ -89,7 +90,8 @@ void ConvertBridgeToActor(World w, CPos cell) var subtile = new CPos(ni + ind % template.Size.X, nj + ind / template.Size.X); // This isn't the bridge you're looking for - if (!mapTiles.Contains(subtile) || mapTiles[subtile].Type != tile || mapTiles[subtile].Index != ind) + var subti = mapTiles[subtile]; + if (!mapTiles.Contains(subtile) || subti.Type != tile || subti.Index != ind) continue; subTiles.Add(subtile, ind); diff --git a/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs b/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs index 89156f0e6e46..28398ed0ef0b 100644 --- a/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/ResourceLayer.cs @@ -109,15 +109,7 @@ public bool AllowResourceAt(ResourceType rt, CPos cell) if (!rt.Info.AllowUnderBuildings && buildingInfluence.GetBuildingAt(cell) != null) return false; - if (!rt.Info.AllowOnRamps) - { - var tile = world.Map.Tiles[cell]; - var tileInfo = world.Map.Rules.TileSet.GetTileInfo(tile); - if (tileInfo != null && tileInfo.RampType > 0) - return false; - } - - return true; + return rt.Info.AllowOnRamps || world.Map.Ramp[cell] == 0; } public bool CanSpawnResourceAt(ResourceType newResourceType, CPos cell) diff --git a/OpenRA.Mods.Common/Traits/World/SubterraneanActorLayer.cs b/OpenRA.Mods.Common/Traits/World/SubterraneanActorLayer.cs index c9c5314cc3d7..316c986c5bac 100644 --- a/OpenRA.Mods.Common/Traits/World/SubterraneanActorLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/SubterraneanActorLayer.cs @@ -82,9 +82,7 @@ bool ValidTransitionCell(CPos cell, LocomotorInfo li) if (sli.SubterraneanTransitionOnRamps) return true; - var tile = map.Tiles[cell]; - var ti = map.Rules.TileSet.GetTileInfo(tile); - return ti == null || ti.RampType == 0; + return map.Ramp[cell] == 0; } int ICustomMovementLayer.EntryMovementCost(ActorInfo a, LocomotorInfo li, CPos cell) diff --git a/OpenRA.Mods.Common/Traits/World/TerrainGeometryOverlay.cs b/OpenRA.Mods.Common/Traits/World/TerrainGeometryOverlay.cs index e286cfcccfb5..f539700eb99c 100644 --- a/OpenRA.Mods.Common/Traits/World/TerrainGeometryOverlay.cs +++ b/OpenRA.Mods.Common/Traits/World/TerrainGeometryOverlay.cs @@ -63,11 +63,7 @@ IEnumerable IRenderAnnotations.RenderAnnotations(Actor self, WorldR continue; var height = (int)map.Height[uv]; - var tile = map.Tiles[uv]; - var ti = tileSet.GetTileInfo(tile); - var ramp = ti != null ? ti.RampType : 0; - - var corners = map.Grid.CellCorners[ramp]; + var corners = map.Grid.CellCorners[map.Ramp[uv]]; var pos = map.CenterOfCell(uv.ToCPos(map)); var width = uv == mouseCell ? 3 : 1; From 25bbb9b4b29741c3c4e12f73ecdc08a681e237d3 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Tue, 28 Apr 2020 00:52:40 +0100 Subject: [PATCH 2/3] Replace MapGrid.CellCorners with a new CellRamp struct. --- OpenRA.Game/Graphics/Viewport.cs | 2 +- OpenRA.Game/Map/MapGrid.cs | 172 +++++++++++------- .../Traits/World/TerrainGeometryOverlay.cs | 21 ++- 3 files changed, 124 insertions(+), 71 deletions(-) diff --git a/OpenRA.Game/Graphics/Viewport.cs b/OpenRA.Game/Graphics/Viewport.cs index 4affc0156c3a..85aa1b8bf8b0 100644 --- a/OpenRA.Game/Graphics/Viewport.cs +++ b/OpenRA.Game/Graphics/Viewport.cs @@ -263,7 +263,7 @@ public CPos ViewToWorld(int2 view) if (Math.Abs(s.X - world.X) <= tileSize.Width && Math.Abs(s.Y - world.Y) <= tileSize.Height) { var ramp = map.Ramp.Contains(uv) ? map.Ramp[uv] : 0; - var corners = map.Grid.CellCorners[ramp]; + var corners = map.Grid.Ramps[ramp].Corners; var pos = map.CenterOfCell(uv.ToCPos(map)); var screen = corners.Select(c => worldRenderer.ScreenPxPosition(pos + c)).ToArray(); diff --git a/OpenRA.Game/Map/MapGrid.cs b/OpenRA.Game/Map/MapGrid.cs index e6ddb4ee564d..d52fc6a78213 100644 --- a/OpenRA.Game/Map/MapGrid.cs +++ b/OpenRA.Game/Map/MapGrid.cs @@ -9,7 +9,6 @@ */ #endregion -using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -20,6 +19,81 @@ 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 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 }; + } + + public WDist DistanceAboveTerrain(WVec delta) + { + // 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) * (delta.X - p[2].X) - (p[1].X - p[2].X) * (delta.Y - p[2].Y)) / 1024; + v = ((p[0].X - p[2].X) * (delta.Y - p[2].Y) - (p[0].Y - p[2].Y) * (delta.X - 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 + var dz = (u * p[0].Z + v * p[1].Z + (1024 - u - v) * p[2].Z) / 1024; + return new WDist(delta.Z - dz); + } + } + public class MapGrid : IGlobalModData { public readonly MapGridType Type = MapGridType.Rectangular; @@ -41,43 +115,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; @@ -96,32 +134,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)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), + 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() diff --git a/OpenRA.Mods.Common/Traits/World/TerrainGeometryOverlay.cs b/OpenRA.Mods.Common/Traits/World/TerrainGeometryOverlay.cs index f539700eb99c..0396b55d1094 100644 --- a/OpenRA.Mods.Common/Traits/World/TerrainGeometryOverlay.cs +++ b/OpenRA.Mods.Common/Traits/World/TerrainGeometryOverlay.cs @@ -63,24 +63,27 @@ IEnumerable IRenderAnnotations.RenderAnnotations(Actor self, WorldR continue; var height = (int)map.Height[uv]; - var corners = map.Grid.CellCorners[map.Ramp[uv]]; + var r = map.Grid.Ramps[map.Ramp[uv]]; var pos = map.CenterOfCell(uv.ToCPos(map)); var width = uv == mouseCell ? 3 : 1; // Colors change between points, so render separately - for (var i = 0; i < 4; i++) + foreach (var p in r.Polygons) { - var j = (i + 1) % 4; - var start = pos + corners[i]; - var end = pos + corners[j]; - var startColor = colors[height + corners[i].Z / 512]; - var endColor = colors[height + corners[j].Z / 512]; - yield return new LineAnnotationRenderable(start, end, width, startColor, endColor); + for (var i = 0; i < p.Length; i++) + { + var j = (i + 1) % p.Length; + var start = pos + p[i]; + var end = pos + p[j]; + var startColor = colors[height + p[i].Z / 512]; + var endColor = colors[height + p[j].Z / 512]; + yield return new LineAnnotationRenderable(start, end, width, startColor, endColor); + } } } // Projected cell coordinates for the current cell - var projectedCorners = map.Grid.CellCorners[0]; + var projectedCorners = map.Grid.Ramps[0].Corners; foreach (var puv in map.ProjectedCellsCovering(mouseCell)) { var pos = map.CenterOfCell(((MPos)puv).ToCPos(map)); From baf32d69e44039e69a6a900c59f007275118c15e Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Mon, 4 May 2020 00:25:14 +0100 Subject: [PATCH 3/3] Account for ramps in terrain height calculations. --- OpenRA.Game/Graphics/TerrainSpriteLayer.cs | 8 +++-- OpenRA.Game/Graphics/Viewport.cs | 8 ++--- OpenRA.Game/Map/Map.cs | 32 ++++++++++++++++--- OpenRA.Game/Map/MapGrid.cs | 14 +++++--- OpenRA.Mods.Common/Activities/Move/Move.cs | 1 + .../Traits/World/TerrainGeometryOverlay.cs | 2 +- 6 files changed, 48 insertions(+), 17 deletions(-) diff --git a/OpenRA.Game/Graphics/TerrainSpriteLayer.cs b/OpenRA.Game/Graphics/TerrainSpriteLayer.cs index 49381985f4bc..c6b6b3637c40 100644 --- a/OpenRA.Game/Graphics/TerrainSpriteLayer.cs +++ b/OpenRA.Game/Graphics/TerrainSpriteLayer.cs @@ -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); } diff --git a/OpenRA.Game/Graphics/Viewport.cs b/OpenRA.Game/Graphics/Viewport.cs index 85aa1b8bf8b0..2ee520d341d0 100644 --- a/OpenRA.Game/Graphics/Viewport.cs +++ b/OpenRA.Game/Graphics/Viewport.cs @@ -262,11 +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 = map.Ramp.Contains(uv) ? map.Ramp[uv] : 0; - var corners = map.Grid.Ramps[ramp].Corners; - 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); } diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs index 3b66521130f4..0302819fad09 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -820,7 +820,7 @@ 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); } @@ -828,15 +828,39 @@ 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) diff --git a/OpenRA.Game/Map/MapGrid.cs b/OpenRA.Game/Map/MapGrid.cs index d52fc6a78213..2e8483f1530e 100644 --- a/OpenRA.Game/Map/MapGrid.cs +++ b/OpenRA.Game/Map/MapGrid.cs @@ -24,6 +24,7 @@ public enum RampCornerHeight { Low = 0, Half = 1, Full = 2 } public struct CellRamp { + public readonly int CenterHeightOffset; public readonly WVec[] Corners; public readonly WVec[][] Polygons; @@ -68,9 +69,13 @@ public CellRamp(MapGridType type, RampCornerHeight tl = RampCornerHeight.Low, Ra } else Polygons = new[] { Corners }; + + // Initial value must be asigned before HeightOffset can be called + CenterHeightOffset = 0; + CenterHeightOffset = HeightOffset(0, 0); } - public WDist DistanceAboveTerrain(WVec delta) + 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 @@ -80,8 +85,8 @@ public WDist DistanceAboveTerrain(WVec delta) for (var i = 0; i < Polygons.Length; i++) { p = Polygons[i]; - u = ((p[1].Y - p[2].Y) * (delta.X - p[2].X) - (p[1].X - p[2].X) * (delta.Y - p[2].Y)) / 1024; - v = ((p[0].X - p[2].X) * (delta.Y - p[2].Y) - (p[0].Y - p[2].Y) * (delta.X - p[2].X)) / 1024; + 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) @@ -89,8 +94,7 @@ public WDist DistanceAboveTerrain(WVec delta) } // Calculate w from u,v and interpolate height - var dz = (u * p[0].Z + v * p[1].Z + (1024 - u - v) * p[2].Z) / 1024; - return new WDist(delta.Z - dz); + return (u * p[0].Z + v * p[1].Z + (1024 - u - v) * p[2].Z) / 1024; } } diff --git a/OpenRA.Mods.Common/Activities/Move/Move.cs b/OpenRA.Mods.Common/Activities/Move/Move.cs index 5fb74f87add0..00757f93cc74 100644 --- a/OpenRA.Mods.Common/Activities/Move/Move.cs +++ b/OpenRA.Mods.Common/Activities/Move/Move.cs @@ -464,6 +464,7 @@ void UpdateCenterLocation(Actor self, Mobile mobile) else pos = WPos.Lerp(From, To, moveFraction, MoveFractionTotal); + pos -= new WVec(WDist.Zero, WDist.Zero, self.World.Map.DistanceAboveTerrain(pos)); mobile.SetVisualPosition(self, pos); } else diff --git a/OpenRA.Mods.Common/Traits/World/TerrainGeometryOverlay.cs b/OpenRA.Mods.Common/Traits/World/TerrainGeometryOverlay.cs index 0396b55d1094..370ee15c51cd 100644 --- a/OpenRA.Mods.Common/Traits/World/TerrainGeometryOverlay.cs +++ b/OpenRA.Mods.Common/Traits/World/TerrainGeometryOverlay.cs @@ -64,7 +64,7 @@ IEnumerable IRenderAnnotations.RenderAnnotations(Actor self, WorldR var height = (int)map.Height[uv]; var r = map.Grid.Ramps[map.Ramp[uv]]; - var pos = map.CenterOfCell(uv.ToCPos(map)); + var pos = map.CenterOfCell(uv.ToCPos(map)) - new WVec(0, 0, r.CenterHeightOffset); var width = uv == mouseCell ? 3 : 1; // Colors change between points, so render separately