From 60420340fbc45550b68133a119a559bcec7f58cf Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Thu, 30 Oct 2014 18:33:59 +1300 Subject: [PATCH 1/5] Add height map support. --- OpenRA.Game/Map/Map.cs | 191 +++++++++++++++++++++++---------- OpenRA.Game/Map/TileSet.cs | 8 +- mods/ts/tilesets/temperat.yaml | 1 + 3 files changed, 141 insertions(+), 59 deletions(-) diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs index 24089e2ff0..d2ac405aaa 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -24,6 +24,38 @@ using OpenRA.Traits; namespace OpenRA { + struct BinaryDataHeader + { + public readonly byte Format; + public readonly uint TilesOffset; + public readonly uint HeightsOffset; + public readonly uint ResourcesOffset; + + public BinaryDataHeader(Stream s, int2 expectedSize) + { + Format = s.ReadUInt8(); + var width = s.ReadUInt16(); + var height = s.ReadUInt16(); + if (width != expectedSize.X || height != expectedSize.Y) + throw new InvalidDataException("Invalid tile data"); + + if (Format == 1) + { + TilesOffset = 5; + HeightsOffset = 0; + ResourcesOffset = (uint)(3 * width * height + 5); + } + else if (Format == 2) + { + TilesOffset = s.ReadUInt32(); + HeightsOffset = s.ReadUInt32(); + ResourcesOffset = s.ReadUInt32(); + } + else + throw new InvalidDataException("Unknown binary map format '{0}'".F(Format)); + } + } + public class MapOptions { public bool? Cheats; @@ -118,11 +150,14 @@ namespace OpenRA [FieldLoader.Ignore] public List TranslationDefinitions = new List(); // Binary map data - [FieldLoader.Ignore] public byte TileFormat = 1; + [FieldLoader.Ignore] public byte TileFormat = 2; + public int2 MapSize; [FieldLoader.Ignore] public Lazy> MapTiles; [FieldLoader.Ignore] public Lazy> MapResources; + [FieldLoader.Ignore] public Lazy> MapHeight; + [FieldLoader.Ignore] public CellLayer CustomTerrain; [FieldLoader.Ignore] Lazy cachedTileSet; @@ -145,6 +180,13 @@ namespace OpenRA return ret; }); + var makeMapHeight = Exts.Lazy(() => + { + var ret = new CellLayer(tileShape, size); + ret.Clear(0); + return ret; + }); + var map = new Map() { Title = "Name your map here", @@ -155,6 +197,7 @@ namespace OpenRA Options = new MapOptions(), MapResources = Exts.Lazy(() => new CellLayer(tileShape, size)), MapTiles = makeMapTiles, + MapHeight = makeMapHeight, Actors = Exts.Lazy(() => new Dictionary()), Smudges = Exts.Lazy(() => new List()) }; @@ -253,6 +296,8 @@ namespace OpenRA MapTiles = Exts.Lazy(() => LoadMapTiles()); MapResources = Exts.Lazy(() => LoadResourceTiles()); + MapHeight = Exts.Lazy(() => LoadMapHeight()); + TileShape = Game.modData.Manifest.TileShape; SubCellOffsets = Game.modData.Manifest.SubCellOffsets; LastSubCell = (SubCell)(SubCellOffsets.Length - 1); @@ -395,33 +440,25 @@ namespace OpenRA public CellLayer LoadMapTiles() { var tiles = new CellLayer(this); - using (var dataStream = Container.GetContent("map.bin")) + using (var s = Container.GetContent("map.bin")) { - if (dataStream.ReadUInt8() != 1) - throw new InvalidDataException("Unknown binary map format"); - - // Load header info - var width = dataStream.ReadUInt16(); - var height = dataStream.ReadUInt16(); - - if (width != MapSize.X || height != MapSize.Y) - throw new InvalidDataException("Invalid tile data"); - - // Load tile data - var data = dataStream.ReadBytes(MapSize.X * MapSize.Y * 3); - var d = 0; - for (var i = 0; i < MapSize.X; i++) + var header = new BinaryDataHeader(s, MapSize); + if (header.TilesOffset > 0) { - for (var j = 0; j < MapSize.Y; j++) + s.Position = header.TilesOffset; + for (var i = 0; i < MapSize.X; i++) { - var tile = BitConverter.ToUInt16(data, d); - d += 2; + for (var j = 0; j < MapSize.Y; j++) + { + var tile = s.ReadUInt16(); + var index = s.ReadUInt8(); - var index = data[d++]; - if (index == byte.MaxValue) - index = (byte)(i % 4 + (j % 4) * 4); + // TODO: Remember to remove this when rewriting tile variants / PickAny + if (index == byte.MaxValue) + index = (byte)(i % 4 + (j % 4) * 4); - tiles[i, j] = new TerrainTile(tile, index); + tiles[i, j] = new TerrainTile(tile, index); + } } } } @@ -429,32 +466,45 @@ namespace OpenRA return tiles; } + public CellLayer LoadMapHeight() + { + var maxHeight = cachedTileSet.Value.MaxGroundHeight; + var tiles = new CellLayer(this); + using (var s = Container.GetContent("map.bin")) + { + var header = new BinaryDataHeader(s, MapSize); + if (header.HeightsOffset > 0) + { + s.Position = header.HeightsOffset; + for (var i = 0; i < MapSize.X; i++) + for (var j = 0; j < MapSize.Y; j++) + tiles[i, j] = s.ReadUInt8().Clamp((byte)0, maxHeight); + } + } + + return tiles; + } + public CellLayer LoadResourceTiles() { var resources = new CellLayer(this); - using (var dataStream = Container.GetContent("map.bin")) + using (var s = Container.GetContent("map.bin")) { - if (dataStream.ReadUInt8() != 1) - throw new InvalidDataException("Unknown binary map format"); - - // Load header info - var width = dataStream.ReadUInt16(); - var height = dataStream.ReadUInt16(); - - if (width != MapSize.X || height != MapSize.Y) - throw new InvalidDataException("Invalid tile data"); - - // Skip past tile data - dataStream.Seek(3 * MapSize.X * MapSize.Y, SeekOrigin.Current); - - var data = dataStream.ReadBytes(MapSize.X * MapSize.Y * 2); - var d = 0; - - // Load resource data - for (var i = 0; i < MapSize.X; i++) - for (var j = 0; j < MapSize.Y; j++) - resources[i, j] = new ResourceTile(data[d++], data[d++]); + var header = new BinaryDataHeader(s, MapSize); + if (header.ResourcesOffset > 0) + { + s.Position = header.ResourcesOffset; + for (var i = 0; i < MapSize.X; i++) + { + for (var j = 0; j < MapSize.Y; j++) + { + var type = s.ReadUInt8(); + var density = s.ReadUInt8(); + resources[i, j] = new ResourceTile(type, density); + } + } + } } return resources; @@ -465,28 +515,53 @@ namespace OpenRA var dataStream = new MemoryStream(); using (var writer = new BinaryWriter(dataStream)) { - // File header consists of a version byte, followed by 2 ushorts for width and height + // Binary data version writer.Write(TileFormat); + + // Size writer.Write((ushort)MapSize.X); writer.Write((ushort)MapSize.Y); + // Data offsets + var tilesOffset = 17; + var heightsOffset = cachedTileSet.Value.MaxGroundHeight > 0 ? 3 * MapSize.X * MapSize.Y + 17 : 0; + var resourcesOffset = (cachedTileSet.Value.MaxGroundHeight > 0 ? 4 : 3) * MapSize.X * MapSize.Y + 17; + + writer.Write((uint)tilesOffset); + writer.Write((uint)heightsOffset); + writer.Write((uint)resourcesOffset); + // Tile data - for (var i = 0; i < MapSize.X; i++) - for (var j = 0; j < MapSize.Y; j++) + if (tilesOffset != 0) + { + for (var i = 0; i < MapSize.X; i++) { - var tile = MapTiles.Value[i, j]; - writer.Write(tile.Type); - writer.Write(tile.Index); + for (var j = 0; j < MapSize.Y; j++) + { + var tile = MapTiles.Value[i, j]; + writer.Write(tile.Type); + writer.Write(tile.Index); + } } + } + + // Height data + if (heightsOffset != 0) + for (var i = 0; i < MapSize.X; i++) + for (var j = 0; j < MapSize.Y; j++) + writer.Write(MapHeight.Value[i, j]); // Resource data - for (var i = 0; i < MapSize.X; i++) + if (resourcesOffset != 0) { - for (var j = 0; j < MapSize.Y; j++) + for (var i = 0; i < MapSize.X; i++) { - var tile = MapResources.Value[i, j]; - writer.Write(tile.Type); - writer.Write(tile.Index); + for (var j = 0; j < MapSize.Y; j++) + { + var tile = MapResources.Value[i, j]; + writer.Write(tile.Type); + writer.Write(tile.Index); + } } } } @@ -513,7 +588,9 @@ namespace OpenRA // (b) Therefore: // - ax + by adds (a - b) * 512 + 512 to u // - ax + by adds (a + b) * 512 + 512 to v - return new WPos(512 * (cell.X - cell.Y + 1), 512 * (cell.X + cell.Y + 1), 0); + + var z = Contains(cell) ? 512 * MapHeight.Value[cell] : 0; + return new WPos(512 * (cell.X - cell.Y + 1), 512 * (cell.X + cell.Y + 1), z); } public WPos CenterOfSubCell(CPos cell, SubCell subCell) @@ -588,10 +665,12 @@ namespace OpenRA { var oldMapTiles = MapTiles.Value; var oldMapResources = MapResources.Value; + var oldMapHeight = MapHeight.Value; var newSize = new Size(width, height); MapTiles = Exts.Lazy(() => CellLayer.Resize(oldMapTiles, newSize, oldMapTiles[0, 0])); MapResources = Exts.Lazy(() => CellLayer.Resize(oldMapResources, newSize, oldMapResources[0, 0])); + MapHeight = Exts.Lazy(() => CellLayer.Resize(oldMapHeight, newSize, oldMapHeight[0, 0])); MapSize = new int2(newSize); } diff --git a/OpenRA.Game/Map/TileSet.cs b/OpenRA.Game/Map/TileSet.cs index f26da12774..b1934a64b7 100644 --- a/OpenRA.Game/Map/TileSet.cs +++ b/OpenRA.Game/Map/TileSet.cs @@ -148,7 +148,7 @@ namespace OpenRA public class TileSet { - static readonly string[] Fields = { "Name", "Id", "SheetSize", "Palette", "PlayerPalette", "Extensions", "WaterPaletteRotationBase", "EditorTemplateOrder", "IgnoreTileSpriteOffsets" }; + static readonly string[] Fields = { "Name", "Id", "SheetSize", "Palette", "PlayerPalette", "Extensions", "WaterPaletteRotationBase", "EditorTemplateOrder", "IgnoreTileSpriteOffsets", "MaximumHeight" }; public readonly string Name; public readonly string Id; @@ -156,11 +156,13 @@ namespace OpenRA public readonly string Palette; public readonly string PlayerPalette; public readonly string[] Extensions; - public readonly int WaterPaletteRotationBase = 0x60; - public readonly Dictionary Templates = new Dictionary(); + public readonly int WaterPaletteRotationBase = 0x60; + public readonly byte MaxGroundHeight = 0; public readonly string[] EditorTemplateOrder; public readonly bool IgnoreTileSpriteOffsets; + public readonly Dictionary Templates = new Dictionary(); + public readonly TerrainTypeInfo[] TerrainInfo; readonly Dictionary terrainIndexByType = new Dictionary(); readonly byte defaultWalkableTerrainIndex; diff --git a/mods/ts/tilesets/temperat.yaml b/mods/ts/tilesets/temperat.yaml index b82a5c1e99..9048ae413d 100644 --- a/mods/ts/tilesets/temperat.yaml +++ b/mods/ts/tilesets/temperat.yaml @@ -3,6 +3,7 @@ General: Id: TEMPERAT Extensions: .tem, .shp Palette: isotem.pal + MaxGroundHeight: 16 EditorTemplateOrder: Misc Buildings, Clear, Cliff Pieces, Ice Flow, House, Blank, Ice Ramps, Cliff Set, Civilian Buildings, Shore Pieces, Rough LAT tile, Clear/Rough LAT, Cliff/Water pieces, Bendy Dirt Roads, Dirt Road Junctions, Straight Dirt Roads, Bridges, Paved Roads, Water, Dirt Road Slopes, Slope Set Pieces, Dead Oil Tanker, Ruins, Waterfalls, Ground 01, Ground 02, Sand, Sand/Clear LAT, Rough ground, Paved Road Ends, TrainBridges, Pavement, Pavement/Clear LAT, Paved road bits, Green, Green/Clear LAT, Ramp edge fixup, Water slopes, Pavement (Use for LAT), Paved Road Slopes, Monorail Slopes, Waterfalls-B, Waterfalls-C, Waterfalls-D, Tunnel Floor, Tunnel Side, TrackTunnel Floor, Destroyable Cliffs, Water Caves, Scrin Wreckage, DirtTrackTunnel Floor, DirtTunnel Floor SheetSize: 2048 From 6527ab21d7f62ede715b1bba2a872fc020c8fdfe Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 1 Nov 2014 11:56:34 +1300 Subject: [PATCH 2/5] Improve voxel shadow rendering. --- OpenRA.Mods.Common/Graphics/VoxelRenderable.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs b/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs index 24c0b76655..259fb1c4d3 100644 --- a/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs +++ b/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs @@ -100,8 +100,13 @@ namespace OpenRA.Mods.Common.Graphics public void Render(WorldRenderer wr) { + // TODO: This is a temporary workaround until we have a proper ramp-aware height calculation + var groundPos = wr.world.Map.CenterOfCell(wr.world.Map.CellContaining(pos)); + + var ts = Game.modData.Manifest.TileSize; + var groundZ = ts.Height * (groundPos.Z - pos.Z) / 1024f; + var pxOrigin = wr.ScreenPosition(pos); - var groundZ = 0.5f*(pxOrigin.Y - wr.ScreenZPosition(pos, 0)); var shadowOrigin = pxOrigin - groundZ*(new float2(renderProxy.ShadowDirection, 1)); var psb = renderProxy.ProjectedShadowBounds; From 18f65552a1cc58e342d9fbbd4ab9648a67a143e1 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 1 Nov 2014 18:58:16 +1300 Subject: [PATCH 3/5] Account for terrain height in Viewport. --- OpenRA.Game/Graphics/Viewport.cs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/OpenRA.Game/Graphics/Viewport.cs b/OpenRA.Game/Graphics/Viewport.cs index efa1a53b8b..d9a8513a88 100644 --- a/OpenRA.Game/Graphics/Viewport.cs +++ b/OpenRA.Game/Graphics/Viewport.cs @@ -39,6 +39,8 @@ namespace OpenRA.Graphics // Map bounds (world-px) readonly Rectangle mapBounds; + readonly int maxGroundHeight; + // Viewport geometry (world-px) public int2 CenterLocation { get; private set; } @@ -96,6 +98,7 @@ namespace OpenRA.Graphics var br = wr.ScreenPxPosition(map.CenterOfCell(Map.MapToCell(map.TileShape, new CPos(b.Right, b.Bottom))) + new WVec(511, 511, 0)); mapBounds = Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y); + maxGroundHeight = wr.world.TileSet.MaxGroundHeight; CenterLocation = (tl + br) / 2; Zoom = Game.Settings.Graphics.PixelDouble ? 2 : 1; } @@ -144,8 +147,8 @@ namespace OpenRA.Graphics var cbr = map.CenterOfCell(VisibleCells.BottomRight) + new WVec(512, 512, 0); // Convert to screen coordinates - var tl = WorldToViewPx(worldRenderer.ScreenPxPosition(ctl)).Clamp(ScreenClip); - var br = WorldToViewPx(worldRenderer.ScreenPxPosition(cbr)).Clamp(ScreenClip); + var tl = WorldToViewPx(worldRenderer.ScreenPxPosition(ctl - new WVec(0, 0, ctl.Z))).Clamp(ScreenClip); + var br = WorldToViewPx(worldRenderer.ScreenPxPosition(cbr - new WVec(0, 0, cbr.Z))).Clamp(ScreenClip); return Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y); } } @@ -156,14 +159,20 @@ namespace OpenRA.Graphics { if (cellsDirty) { - // Visible rectangle in map coordinates var map = worldRenderer.world.Map; - var ctl = Map.CellToMap(map.TileShape, map.CellContaining(worldRenderer.Position(TopLeft))); - var cbr = Map.CellToMap(map.TileShape, map.CellContaining(worldRenderer.Position(BottomRight))); + var wtl = worldRenderer.Position(TopLeft); + var wbr = worldRenderer.Position(BottomRight); - // Add a 2 cell cordon to prevent holes, then convert back to cell coordinates - var tl = map.Clamp(Map.MapToCell(map.TileShape, ctl - new CVec(2, 2))); - var br = map.Clamp(Map.MapToCell(map.TileShape, cbr + new CVec(2, 2))); + // Visible rectangle in map coordinates + var ctl = new CPos(wtl.X / 1024, wtl.Y / 1024); + var dy = map.TileShape == TileShape.Diamond ? 512 : 1024; + var cbr = new CPos((wbr.X + 1023) / 1024, (wbr.Y + dy - 1) / dy); + + // Add a 1 cell cordon to prevent holes, then convert back to cell coordinates + var tl = map.Clamp(Map.MapToCell(map.TileShape, ctl - new CVec(1, 1))); + + // Also need to account for height of cells in rows below the bottom + var br = map.Clamp(Map.MapToCell(map.TileShape, cbr + new CVec(1, 2 + maxGroundHeight / 2))); cells = new CellRegion(map.TileShape, tl, br); cellsDirty = false; From 118275b3769aacd29c63e6652d18567cfe820624 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 1 Nov 2014 18:02:09 +1300 Subject: [PATCH 4/5] Add terrain geometry visualisation. --- OpenRA.Game/FieldLoader.cs | 21 +++ OpenRA.Game/Map/TileSet.cs | 1 + OpenRA.Game/Traits/Player/DeveloperMode.cs | 3 + OpenRA.Mods.Common/OpenRA.Mods.Common.csproj | 1 + .../World/TerrainGeometryOverlay.cs | 127 ++++++++++++++++++ .../Widgets/Logic/DebugMenuLogic.cs | 7 + mods/cnc/chrome/ingame-debug.yaml | 16 ++- mods/cnc/rules/world.yaml | 1 + mods/d2k/rules/world.yaml | 1 + mods/ra/chrome/ingame-debug.yaml | 18 ++- mods/ra/rules/world.yaml | 1 + mods/ts/rules/world.yaml | 1 + mods/ts/tilesets/temperat.yaml | 1 + 13 files changed, 188 insertions(+), 11 deletions(-) create mode 100644 OpenRA.Mods.Common/World/TerrainGeometryOverlay.cs diff --git a/OpenRA.Game/FieldLoader.cs b/OpenRA.Game/FieldLoader.cs index 9b2dcb920c..616bc1af34 100644 --- a/OpenRA.Game/FieldLoader.cs +++ b/OpenRA.Game/FieldLoader.cs @@ -188,6 +188,27 @@ namespace OpenRA return InvalidValueAction(value, fieldType, fieldName); } + else if (fieldType == typeof(Color[])) + { + var parts = value.Split(','); + + if (parts.Length % 4 != 0) + return InvalidValueAction(value, fieldType, fieldName); + + var colors = new Color[parts.Length / 4]; + + for (var i = 0; i < colors.Length; i++) + { + colors[i] = Color.FromArgb( + Exts.ParseIntegerInvariant(parts[4 * i]).Clamp(0, 255), + Exts.ParseIntegerInvariant(parts[4 * i + 1]).Clamp(0, 255), + Exts.ParseIntegerInvariant(parts[4 * i + 2]).Clamp(0, 255), + Exts.ParseIntegerInvariant(parts[4 * i + 3]).Clamp(0, 255)); + } + + return colors; + } + else if (fieldType == typeof(HSLColor)) { var parts = value.Split(','); diff --git a/OpenRA.Game/Map/TileSet.cs b/OpenRA.Game/Map/TileSet.cs index b1934a64b7..2c1769808f 100644 --- a/OpenRA.Game/Map/TileSet.cs +++ b/OpenRA.Game/Map/TileSet.cs @@ -158,6 +158,7 @@ namespace OpenRA public readonly string[] Extensions; public readonly int WaterPaletteRotationBase = 0x60; public readonly byte MaxGroundHeight = 0; + public readonly Color[] HeightDebugColors = new[] { Color.Red }; public readonly string[] EditorTemplateOrder; public readonly bool IgnoreTileSpriteOffsets; diff --git a/OpenRA.Game/Traits/Player/DeveloperMode.cs b/OpenRA.Game/Traits/Player/DeveloperMode.cs index 66b9656ca7..af18664c41 100644 --- a/OpenRA.Game/Traits/Player/DeveloperMode.cs +++ b/OpenRA.Game/Traits/Player/DeveloperMode.cs @@ -23,6 +23,7 @@ namespace OpenRA.Traits public bool BuildAnywhere; public bool ShowCombatGeometry; public bool ShowDebugGeometry; + public bool ShowTerrainGeometry; public object Create(ActorInitializer init) { return new DeveloperMode(this); } } @@ -41,6 +42,7 @@ namespace OpenRA.Traits // Client side only public bool ShowCombatGeometry; public bool ShowDebugGeometry; + public bool ShowTerrainGeometry; public DeveloperMode(DeveloperModeInfo info) { @@ -53,6 +55,7 @@ namespace OpenRA.Traits BuildAnywhere = info.BuildAnywhere; ShowCombatGeometry = info.ShowCombatGeometry; ShowDebugGeometry = info.ShowDebugGeometry; + ShowTerrainGeometry = info.ShowTerrainGeometry; } public void ResolveOrder(Actor self, Order order) diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index cde0c6f705..da17f71218 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -147,6 +147,7 @@ + diff --git a/OpenRA.Mods.Common/World/TerrainGeometryOverlay.cs b/OpenRA.Mods.Common/World/TerrainGeometryOverlay.cs new file mode 100644 index 0000000000..f68b7d22c3 --- /dev/null +++ b/OpenRA.Mods.Common/World/TerrainGeometryOverlay.cs @@ -0,0 +1,127 @@ +#region Copyright & License Information + /* + * Copyright 2007-2014 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.Drawing; +using System.Collections.Generic; +using System.Linq; +using OpenRA.Traits; +using OpenRA.Graphics; + +namespace OpenRA.Mods.Common +{ + [Desc("Renders a debug overlay showing the terrain cells. Attach this to the world actor.")] + public class TerrainGeometryOverlayInfo : ITraitInfo + { + public object Create(ActorInitializer init) { return new TerrainGeometryOverlay(init.self); } + } + + public class TerrainGeometryOverlay : IRenderOverlay + { + readonly int[][] vertices = 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 } + }; + + readonly Lazy devMode; + + public TerrainGeometryOverlay(Actor self) + { + devMode = Exts.Lazy(() => self.World.LocalPlayer != null ? self.World.LocalPlayer.PlayerActor.Trait() : null); + } + + public void Render(WorldRenderer wr) + { + if (devMode.Value == null || !devMode.Value.ShowTerrainGeometry) + return; + + var ts = wr.world.Map.TileShape; + var colors = wr.world.TileSet.HeightDebugColors; + + var leftDelta = ts == TileShape.Diamond ? new WVec(-512, 0, 0) : new WVec(-512, -512, 0); + var topDelta = ts == TileShape.Diamond ? new WVec(0, -512, 0) : new WVec(512, -512, 0); + var rightDelta = ts == TileShape.Diamond ? new WVec(512, 0, 0) : new WVec(512, 512, 0); + var bottomDelta = ts == TileShape.Diamond ? new WVec(0, 512, 0) : new WVec(-512, 512, 0); + + foreach (var cell in wr.Viewport.VisibleCells) + { + var lr = Game.Renderer.WorldLineRenderer; + var pos = wr.world.Map.CenterOfCell(cell); + + var height = (int)wr.world.Map.MapHeight.Value[cell]; + var tile = wr.world.Map.MapTiles.Value[cell]; + + TerrainTileInfo tileInfo = null; + + // TODO: This is a temporary workaround for our sloppy tileset definitions + // (ra/td templates omit Clear tiles from templates) + try + { + tileInfo = wr.world.TileSet.Templates[tile.Type][tile.Index]; + } + catch (Exception) { } + + if (tileInfo == null) + continue; + + var leftHeight = vertices[tileInfo.RampType][0]; + var topHeight = vertices[tileInfo.RampType][1]; + var rightHeight = vertices[tileInfo.RampType][2]; + var bottomHeight = vertices[tileInfo.RampType][3]; + + var leftColor = colors[height + leftHeight]; + var topColor = colors[height + topHeight]; + var rightColor = colors[height + rightHeight]; + var bottomColor = colors[height + bottomHeight]; + + var left = wr.ScreenPxPosition(pos + leftDelta + new WVec(0, 0, 512 * leftHeight)).ToFloat2(); + var top = wr.ScreenPxPosition(pos + topDelta + new WVec(0, 0, 512 * topHeight)).ToFloat2(); + var right = wr.ScreenPxPosition(pos + rightDelta + new WVec(0, 0, 512 * rightHeight)).ToFloat2(); + var bottom = wr.ScreenPxPosition(pos + bottomDelta + new WVec(0, 0, 512 * bottomHeight)).ToFloat2(); + + lr.DrawLine(left, top, leftColor, topColor); + lr.DrawLine(top, right, topColor, rightColor); + lr.DrawLine(right, bottom, rightColor, bottomColor); + lr.DrawLine(bottom, left, bottomColor, leftColor); + } + } + } +} diff --git a/OpenRA.Mods.RA/Widgets/Logic/DebugMenuLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/DebugMenuLogic.cs index 1011d75816..2e6cd763ff 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/DebugMenuLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/DebugMenuLogic.cs @@ -74,6 +74,13 @@ namespace OpenRA.Mods.RA.Widgets.Logic showGeometryCheckbox.OnClick = () => devTrait.ShowDebugGeometry ^= true; } + var showTerrainGeometryCheckbox = widget.GetOrNull("SHOW_TERRAIN_OVERLAY"); + if (showTerrainGeometryCheckbox != null) + { + showTerrainGeometryCheckbox.IsChecked = () => devTrait.ShowTerrainGeometry; + showTerrainGeometryCheckbox.OnClick = () => devTrait.ShowTerrainGeometry ^= true; + } + var allTechCheckbox = widget.GetOrNull("ENABLE_TECH"); if (allTechCheckbox != null) { diff --git a/mods/cnc/chrome/ingame-debug.yaml b/mods/cnc/chrome/ingame-debug.yaml index 148e0ec559..9e1dc992bc 100644 --- a/mods/cnc/chrome/ingame-debug.yaml +++ b/mods/cnc/chrome/ingame-debug.yaml @@ -83,30 +83,36 @@ Container@DEBUG_PANEL: Width: PARENT_RIGHT Checkbox@SHOW_UNIT_PATHS: X: 45 - Y: 285 + Y: 275 Width: 200 Height: 20 Font: Regular Text: Show Unit Paths Checkbox@SHOW_ASTAR: X: 45 - Y: 315 + Y: 305 Height: 20 Width: 200 Font: Regular Text: Show A* Cost Checkbox@SHOW_COMBATOVERLAY: X: 290 - Y: 285 + Y: 275 Height: 20 Width: 200 Font: Regular Text: Show Combat Geometry Checkbox@SHOW_GEOMETRY: X: 290 - Y: 315 + Y: 305 Height: 20 Width: 200 Font: Regular Text: Show Render Geometry - + Checkbox@SHOW_TERRAIN_OVERLAY: + X: 290 + Y: 335 + Height: 20 + Width: 200 + Font: Regular + Text: Show Terrain Geometry diff --git a/mods/cnc/rules/world.yaml b/mods/cnc/rules/world.yaml index 9410aca729..a26240673b 100644 --- a/mods/cnc/rules/world.yaml +++ b/mods/cnc/rules/world.yaml @@ -125,6 +125,7 @@ World: AllowedTerrainTypes: Clear,Road AllowUnderActors: false PathfinderDebugOverlay: + TerrainGeometryOverlay: SpawnMapActors: MPStartLocations: CreateMPPlayers: diff --git a/mods/d2k/rules/world.yaml b/mods/d2k/rules/world.yaml index e3d091e8b1..26dd5e7d75 100644 --- a/mods/d2k/rules/world.yaml +++ b/mods/d2k/rules/world.yaml @@ -104,6 +104,7 @@ World: Race: ordos DomainIndex: PathfinderDebugOverlay: + TerrainGeometryOverlay: BuildableTerrainLayer: D2kResourceLayer: ResourceClaimLayer: diff --git a/mods/ra/chrome/ingame-debug.yaml b/mods/ra/chrome/ingame-debug.yaml index b2e20db205..e6cf64c0ae 100644 --- a/mods/ra/chrome/ingame-debug.yaml +++ b/mods/ra/chrome/ingame-debug.yaml @@ -1,6 +1,6 @@ Container@DEBUG_PANEL: Logic: DebugMenuLogic - Y: 20 + Y: 10 Width: PARENT_RIGHT Height: PARENT_BOTTOM Children: @@ -88,30 +88,36 @@ Container@DEBUG_PANEL: Width: PARENT_RIGHT Checkbox@SHOW_UNIT_PATHS: X: 45 - Y: 285 + Y: 275 Width: 200 Height: 20 Font: Regular Text: Show Unit Paths Checkbox@SHOW_ASTAR: X: 45 - Y: 315 + Y: 305 Height: 20 Width: 200 Font: Regular Text: Show A* Cost Checkbox@SHOW_COMBATOVERLAY: X: 290 - Y: 285 + Y: 275 Height: 20 Width: 200 Font: Regular Text: Show Combat Geometry Checkbox@SHOW_GEOMETRY: X: 290 - Y: 315 + Y: 305 Height: 20 Width: 200 Font: Regular Text: Show Render Geometry - + Checkbox@SHOW_TERRAIN_OVERLAY: + X: 290 + Y: 335 + Height: 20 + Width: 200 + Font: Regular + Text: Show Terrain Geometry diff --git a/mods/ra/rules/world.yaml b/mods/ra/rules/world.yaml index 7e6fda39aa..a5094986a2 100644 --- a/mods/ra/rules/world.yaml +++ b/mods/ra/rules/world.yaml @@ -133,6 +133,7 @@ World: AllowUnderActors: false TerrainType: Gems PathfinderDebugOverlay: + TerrainGeometryOverlay: SpawnMapActors: CreateMPPlayers: MPStartUnits@mcvonly: diff --git a/mods/ts/rules/world.yaml b/mods/ts/rules/world.yaml index 4c7f48d9cf..4badd21f11 100644 --- a/mods/ts/rules/world.yaml +++ b/mods/ts/rules/world.yaml @@ -148,6 +148,7 @@ World: AllowUnderActors: false TerrainType: BlueTiberium PathfinderDebugOverlay: + TerrainGeometryOverlay: SpawnMapActors: CreateMPPlayers: MPStartUnits@MCV: diff --git a/mods/ts/tilesets/temperat.yaml b/mods/ts/tilesets/temperat.yaml index 9048ae413d..024ce8afc4 100644 --- a/mods/ts/tilesets/temperat.yaml +++ b/mods/ts/tilesets/temperat.yaml @@ -4,6 +4,7 @@ General: Extensions: .tem, .shp Palette: isotem.pal MaxGroundHeight: 16 + HeightDebugColors: 128,0,0,0, 128,0,0,68, 128,0,0,136, 128,0,0,204, 128,0,0,255, 128,68,0,204, 128,136,0,136, 128,204,0,68, 128,255,17,0, 128,255,85,0, 128,255,153,0, 128,255,221,0, 128,221,255,0, 128,153,255,0, 128,85,255,0, 128,17,255,0 EditorTemplateOrder: Misc Buildings, Clear, Cliff Pieces, Ice Flow, House, Blank, Ice Ramps, Cliff Set, Civilian Buildings, Shore Pieces, Rough LAT tile, Clear/Rough LAT, Cliff/Water pieces, Bendy Dirt Roads, Dirt Road Junctions, Straight Dirt Roads, Bridges, Paved Roads, Water, Dirt Road Slopes, Slope Set Pieces, Dead Oil Tanker, Ruins, Waterfalls, Ground 01, Ground 02, Sand, Sand/Clear LAT, Rough ground, Paved Road Ends, TrainBridges, Pavement, Pavement/Clear LAT, Paved road bits, Green, Green/Clear LAT, Ramp edge fixup, Water slopes, Pavement (Use for LAT), Paved Road Slopes, Monorail Slopes, Waterfalls-B, Waterfalls-C, Waterfalls-D, Tunnel Floor, Tunnel Side, TrackTunnel Floor, Destroyable Cliffs, Water Caves, Scrin Wreckage, DirtTrackTunnel Floor, DirtTunnel Floor SheetSize: 2048 From cfddd478640c39cf11caaa1cc8a1b39a1a669dcf Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 2 Nov 2014 19:32:23 +1300 Subject: [PATCH 5/5] Add a new test map. --- mods/ts/maps/arivruns/map.bin | Bin 0 -> 205841 bytes mods/ts/maps/arivruns/map.yaml | 188 +++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 mods/ts/maps/arivruns/map.bin create mode 100644 mods/ts/maps/arivruns/map.yaml diff --git a/mods/ts/maps/arivruns/map.bin b/mods/ts/maps/arivruns/map.bin new file mode 100644 index 0000000000000000000000000000000000000000..3ba4a80df183b035e3eb341a664a5213f0405cc3 GIT binary patch literal 205841 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