Files
OpenRA/OpenRA.Mods.Common/Traits/World/TerrainGeometryOverlay.cs
Ashley Newson 7a2e25af86 Fix editor grid overlay height coloring calculation
The change fixes two issues with the map editor's height-based grid
overlay color calculation:

- If the height exceeded the end of the color scale, the engine would
  crash due to attempting an out of bounds read. Now, the calculation
  caps out to using the last color in the scale.
- The calculation assumed the height step was always 512, even for
  RectangularIsometric maps. Now, the (halved) TileScale of the map's
  grid type is used.
2025-10-12 14:47:02 +03:00

108 lines
3.4 KiB
C#

#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Commands;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[TraitLocation(SystemActors.World | SystemActors.EditorWorld)]
[IncludeStaticFluentReferences(typeof(TerrainGeometryOverlay))]
[Desc("Renders a debug overlay showing the terrain cells. Attach this to the world actor.")]
public class TerrainGeometryOverlayInfo : TraitInfo<TerrainGeometryOverlay> { }
public class TerrainGeometryOverlay : IRenderAnnotations, IWorldLoaded, IChatCommand
{
const string CommandName = "terrain-geometry";
[FluentReference]
const string CommandDescription = "description-terrain-geometry-overlay";
public bool Enabled;
void IWorldLoaded.WorldLoaded(World w, WorldRenderer wr)
{
var console = w.WorldActor.TraitOrDefault<ChatCommands>();
var help = w.WorldActor.TraitOrDefault<HelpCommand>();
if (console == null || help == null)
return;
console.RegisterCommand(CommandName, this);
help.RegisterHelp(CommandName, CommandDescription);
}
void IChatCommand.InvokeCommand(string name, string arg)
{
if (name == CommandName)
Enabled ^= true;
}
IEnumerable<IRenderable> IRenderAnnotations.RenderAnnotations(Actor self, WorldRenderer wr)
{
if (!Enabled)
yield break;
var map = wr.World.Map;
var colors = wr.World.Map.Rules.TerrainInfo.HeightDebugColors;
var lastColor = colors.Length - 1;
var heightStep = map.Grid.TileScale / 2;
var mouseCell = wr.Viewport.ViewToWorld(Viewport.LastMousePos).ToMPos(wr.World.Map);
foreach (var uv in wr.Viewport.AllVisibleCells.CandidateMapCoords)
{
if (!map.Height.Contains(uv) || self.World.ShroudObscures(uv))
continue;
var height = (int)map.Height[uv];
var r = map.Grid.Ramps[map.Ramp[uv]];
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
foreach (var p in r.Polygons)
{
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[Math.Min(lastColor, height + p[i].Z / heightStep)];
var endColor = colors[Math.Min(lastColor, height + p[j].Z / heightStep)];
yield return new LineAnnotationRenderable(start, end, width, startColor, endColor);
}
}
}
// Projected cell coordinates for the current cell
var projectedCorners = map.Grid.Ramps[0].Corners;
foreach (var puv in map.ProjectedCellsCovering(mouseCell))
{
var pos = map.CenterOfCell(((MPos)puv).ToCPos(map));
for (var i = 0; i < 4; i++)
{
var j = (i + 1) % 4;
var start = pos + projectedCorners[i] - new WVec(0, 0, pos.Z);
var end = pos + projectedCorners[j] - new WVec(0, 0, pos.Z);
yield return new LineAnnotationRenderable(start, end, 3, Color.Navy);
}
}
}
bool IRenderAnnotations.SpatiallyPartitionable => false;
}
}