diff --git a/OpenRA.Game/Graphics/WorldRenderer.cs b/OpenRA.Game/Graphics/WorldRenderer.cs index 273016e3c8..6c6399536b 100644 --- a/OpenRA.Game/Graphics/WorldRenderer.cs +++ b/OpenRA.Game/Graphics/WorldRenderer.cs @@ -24,6 +24,7 @@ namespace OpenRA.Graphics r => ZPosition(r.Pos, r.ZOffset); public readonly Size TileSize; + public readonly int TileScale; public readonly World World; public readonly Theater Theater; public Viewport Viewport { get; private set; } @@ -41,6 +42,7 @@ namespace OpenRA.Graphics { World = world; TileSize = World.Map.Grid.TileSize; + TileScale = World.Map.Grid.Type == MapGridType.RectangularIsometric ? 1448 : 1024; Viewport = new Viewport(this, world.Map); createPaletteReference = CreatePaletteReference; @@ -216,13 +218,13 @@ namespace OpenRA.Graphics // Conversion between world and screen coordinates public float2 ScreenPosition(WPos pos) { - return new float2(TileSize.Width * pos.X / 1024f, TileSize.Height * (pos.Y - pos.Z) / 1024f); + return new float2((float)TileSize.Width * pos.X / TileScale, (float)TileSize.Height * (pos.Y - pos.Z) / TileScale); } public float3 Screen3DPosition(WPos pos) { - var z = ZPosition(pos, 0) * TileSize.Height / 1024f; - return new float3(TileSize.Width * pos.X / 1024f, TileSize.Height * (pos.Y - pos.Z) / 1024f, z); + var z = ZPosition(pos, 0) * (float)TileSize.Height / TileScale; + return new float3((float)TileSize.Width * pos.X / TileScale, (float)TileSize.Height * (pos.Y - pos.Z) / TileScale, z); } public int2 ScreenPxPosition(WPos pos) @@ -243,9 +245,9 @@ namespace OpenRA.Graphics public float3 ScreenVectorComponents(WVec vec) { return new float3( - TileSize.Width * vec.X / 1024f, - TileSize.Height * (vec.Y - vec.Z) / 1024f, - TileSize.Height * vec.Z / 1024f); + (float)TileSize.Width * vec.X / TileScale, + (float)TileSize.Height * (vec.Y - vec.Z) / TileScale, + (float)TileSize.Height * vec.Z / TileScale); } // For scaling vectors to pixel sizes in the voxel renderer @@ -264,7 +266,7 @@ namespace OpenRA.Graphics public float ScreenZPosition(WPos pos, int offset) { - return ZPosition(pos, offset) * TileSize.Height / 1024f; + return ZPosition(pos, offset) * (float)TileSize.Height / TileScale; } static int ZPosition(WPos pos, int offset) @@ -278,7 +280,7 @@ namespace OpenRA.Graphics /// public WPos ProjectedPosition(int2 screenPx) { - return new WPos(1024 * screenPx.X / TileSize.Width, 1024 * screenPx.Y / TileSize.Height, 0); + return new WPos(TileScale * screenPx.X / TileSize.Width, TileScale * screenPx.Y / TileSize.Height, 0); } public void Dispose() diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs index 9a1b0fea4b..adf35dba2f 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -761,8 +761,11 @@ namespace OpenRA // (b) Therefore: // - ax + by adds (a - b) * 512 + 512 to u // - ax + by adds (a + b) * 512 + 512 to v - var z = Height.Contains(cell) ? 512 * Height[cell] : 0; - return new WPos(512 * (cell.X - cell.Y + 1), 512 * (cell.X + cell.Y + 1), z); + // (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; + return new WPos(724 * (cell.X - cell.Y + 1), 724 * (cell.X + cell.Y + 1), z); } public WPos CenterOfSubCell(CPos cell, SubCell subCell) @@ -786,15 +789,15 @@ namespace OpenRA return new CPos(pos.X / 1024, pos.Y / 1024); // Convert from world position to isometric cell position: - // (a) Subtract (512, 512) to move the rotation center to the middle of the corner cell - // (b) Rotate axes by -pi/4 - // (c) Divide through by sqrt(2) to bring us to an equivalent world pos aligned with u,v axes - // (d) Apply an offset so that the integer division by 1024 rounds in the right direction: - // (i) u is always positive, so add 512 (which then partially cancels the -1024 term from the rotation) - // (ii) v can be negative, so we need to be careful about rounding directions. We add 512 *away from 0* (negative if y > x). - // (e) Divide by 1024 to bring into cell coords. - var u = (pos.Y + pos.X - 512) / 1024; - var v = (pos.Y - pos.X + (pos.Y > pos.X ? 512 : -512)) / 1024; + // (a) Subtract ([1/2 cell], [1/2 cell]) to move the rotation center to the middle of the corner cell + // (b) Rotate axes by -pi/4 to align the world axes with the cell axes + // (c) Apply an offset so that the integer division by [1 cell] rounds in the right direction: + // (i) u is always positive, so add [1/2 cell] (which then partially cancels the -[1 cell] term from the rotation) + // (ii) v can be negative, so we need to be careful about rounding directions. We add [1/2 cell] *away from 0* (negative if y > x). + // (e) Divide by [1 cell] to bring into cell coords. + // The world axes are rotated relative to the cell axes, so the standard cell size (1024) is increased by a factor of sqrt(2) + var u = (pos.Y + pos.X - 724) / 1448; + var v = (pos.Y - pos.X + (pos.Y > pos.X ? 724 : -724)) / 1448; return new CPos(u, v); } @@ -868,19 +871,19 @@ namespace OpenRA Bounds = Rectangle.FromLTRB(tl.U, tl.V, br.U + 1, br.V + 1); // Directly calculate the projected map corners in world units avoiding unnecessary - // conversions. This abuses the definition that the width of the cell is always - // 1024 units, and that the height of two rows is 2048 for classic cells and 1024 + // conversions. This abuses the definition that the width of the cell along the x world axis + // is always 1024 or 1448 units, and that the height of two rows is 2048 for classic cells and 724 // for isometric cells. - var wtop = tl.V * 1024; - var wbottom = (br.V + 1) * 1024; if (Grid.Type == MapGridType.RectangularIsometric) { - wtop /= 2; - wbottom /= 2; + ProjectedTopLeft = new WPos(tl.U * 1448, tl.V * 724, 0); + ProjectedBottomRight = new WPos(br.U * 1448 - 1, (br.V + 1) * 724 - 1, 0); + } + else + { + ProjectedTopLeft = new WPos(tl.U * 1024, tl.V * 1024, 0); + ProjectedBottomRight = new WPos(br.U * 1024 - 1, (br.V + 1) * 1024 - 1, 0); } - - ProjectedTopLeft = new WPos(tl.U * 1024, wtop, 0); - ProjectedBottomRight = new WPos(br.U * 1024 - 1, wbottom - 1, 0); ProjectedCellBounds = new ProjectedCellRegion(this, tl, br); } diff --git a/OpenRA.Game/Map/MapGrid.cs b/OpenRA.Game/Map/MapGrid.cs index 676efcf506..0d60d19ba1 100644 --- a/OpenRA.Game/Map/MapGrid.cs +++ b/OpenRA.Game/Map/MapGrid.cs @@ -9,6 +9,7 @@ */ #endregion +using System; using System.Collections.Generic; using System.Drawing; using System.IO; @@ -91,21 +92,34 @@ namespace OpenRA else if (defaultSubCellIndex < (SubCellOffsets.Length > 1 ? 1 : 0) || defaultSubCellIndex >= SubCellOffsets.Length) 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 leftDelta = Type == MapGridType.RectangularIsometric ? new WVec(-512, 0, 0) : new WVec(-512, -512, 0); - var topDelta = Type == MapGridType.RectangularIsometric ? new WVec(0, -512, 0) : new WVec(512, -512, 0); - var rightDelta = Type == MapGridType.RectangularIsometric ? new WVec(512, 0, 0) : new WVec(512, 512, 0); - var bottomDelta = Type == MapGridType.RectangularIsometric ? new WVec(0, 512, 0) : new WVec(-512, 512, 0); - CellCorners = cellCornerHalfHeights.Select(ramp => new WVec[] - { - leftDelta + new WVec(0, 0, 512 * ramp[0]), - topDelta + new WVec(0, 0, 512 * ramp[1]), - rightDelta + new WVec(0, 0, 512 * ramp[2]), - bottomDelta + new WVec(0, 0, 512 * ramp[3]) - }).ToArray(); - + var makeCorners = Type == MapGridType.RectangularIsometric ? + (Func)IsometricCellCorners : RectangularCellCorners; + CellCorners = cellCornerHalfHeights.Select(makeCorners).ToArray(); TilesByDistance = CreateTilesByDistance(); } + static WVec[] IsometricCellCorners(int[] cornerHeight) + { + return new WVec[] + { + 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]) + }; + } + + 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]) + }; + } + CVec[][] CreateTilesByDistance() { var ts = new List[MaximumTileSearchRange + 1]; diff --git a/OpenRA.Mods.Cnc/Traits/Minelayer.cs b/OpenRA.Mods.Cnc/Traits/Minelayer.cs index 0c7320dac8..45c616184d 100644 --- a/OpenRA.Mods.Cnc/Traits/Minelayer.cs +++ b/OpenRA.Mods.Cnc/Traits/Minelayer.cs @@ -103,15 +103,14 @@ namespace OpenRA.Mods.Cnc.Traits var mins = new CPos(Math.Min(start.X, end.X), Math.Min(start.Y, end.Y)); var maxs = new CPos(Math.Max(start.X, end.X), Math.Max(start.Y, end.Y)); - /* TODO: proper endcaps, if anyone cares (which won't happen unless depth is large) */ - + // TODO: proper endcaps, if anyone cares (which won't happen unless depth is large) var p = end - start; var q = new float2(p.Y, -p.X); q = (start != end) ? (1 / q.Length) * q : new float2(1, 0); var c = -float2.Dot(q, new float2(start.X, start.Y)); - /* return all points such that |ax + by + c| < depth */ - + // return all points such that |ax + by + c| < depth + // HACK: This will return the wrong results for isometric cells for (var i = mins.X; i <= maxs.X; i++) for (var j = mins.Y; j <= maxs.Y; j++) if (Math.Abs(q.X * i + q.Y * j + c) * 1024 < depth.Length) diff --git a/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs b/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs index 0f8a516594..ada39287a8 100644 --- a/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs +++ b/OpenRA.Mods.Common/Graphics/VoxelRenderable.cs @@ -107,7 +107,9 @@ namespace OpenRA.Mods.Common.Graphics public void Render(WorldRenderer wr) { var groundPos = voxel.pos - new WVec(0, 0, wr.World.Map.DistanceAboveTerrain(voxel.pos).Length); - var groundZ = wr.World.Map.Grid.TileSize.Height * (groundPos.Z - voxel.pos.Z) / 1024f; + var tileScale = wr.World.Map.Grid.Type == MapGridType.RectangularIsometric ? 1448f : 1024f; + + var groundZ = wr.World.Map.Grid.TileSize.Height * (groundPos.Z - voxel.pos.Z) / tileScale; var pxOrigin = wr.Screen3DPosition(voxel.pos); // HACK: We don't have enough texture channels to pass the depth data to the shader