From 5c1b66d8a61555534440993144b7284a9c7c7df2 Mon Sep 17 00:00:00 2001 From: copygirl Date: Mon, 30 Sep 2024 21:36:10 +0200 Subject: [PATCH] Redo some of the terrain editing logic - Introduce a tileToChange variable Holds all of the tiles and which corners to modify Is used to calculate tilesPrevious & tilesChanged - Support editing a single corner - Edit tool mesh shows edited corners --- terrain/Terrain+Editing.cs | 121 +++++++++++++++++++++---------------- 1 file changed, 69 insertions(+), 52 deletions(-) diff --git a/terrain/Terrain+Editing.cs b/terrain/Terrain+Editing.cs index 2dc6c47..dcd76b8 100644 --- a/terrain/Terrain+Editing.cs +++ b/terrain/Terrain+Editing.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Runtime.InteropServices; public partial class Terrain { @@ -8,11 +9,15 @@ public partial class Terrain // Set by the terrain editing plugin. public EditorUndoRedoManager EditorUndoRedo { get; set; } + // Dummy value to satisfy the overly careful compiler. + static bool _dummy = false; + Material _editToolMaterial; public override void _EnterTree() { _editToolMaterial = new StandardMaterial3D { - AlbedoColor = Colors.Blue, + VertexColorUseAsAlbedo = true, + BlendMode = BaseMaterial3D.BlendModeEnum.Mix, ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded, DepthDrawMode = BaseMaterial3D.DepthDrawModeEnum.Disabled, NoDepthTest = true, @@ -64,46 +69,60 @@ public partial class Terrain center.DistanceSquaredTo(tile.ToCenter()) < distanceSqr); } - var tiles = (toolShape switch { - ToolShape.Corner => [ hover.Position ], - ToolShape.Circle => GetTilesInRadius(), - ToolShape.Square => GetTilesInSquare(), - _ => throw new InvalidOperationException(), - }).ToHashSet(); - // TODO: Handle different tool modes, such as painting. - // TODO: Finally allow editing single corners. // TODO: Allow click-dragging which doesn't affect already changed tiles / corners. // TODO: Make mesh generation generate vertical walls between disconnected corners. // TODO: Use ArrayMesh instead of ImmediateMesh. // TODO: Dynamically expand terrain instead of having it be a set size. - // Raise / lower the terrain when left mouse button is pressed. - if (ev is InputEventMouseButton { ButtonIndex: MouseButton.Left, Pressed: true }) { - prevent_default = true; + // Holds onto all the tiles and which of their corners corners will be affected by this edit operation. + var tilesToChange = new Dictionary>(); + ref Corners GetTileToChange(TilePos position) + // Don't look at this black magic. The Dictionary type should have this by default I swear! + => ref CollectionsMarshal.GetValueRefOrAddDefault(tilesToChange, position, out _dummy); - var cornersToChange = new HashSet<(TilePos Position, Corner Corner)>(); + if (toolShape == ToolShape.Corner) { + // Modify selected corner itself. + GetTileToChange(hover.Position)[hover.Corner] = true; - // Raise selected tiles themselves. + if (isConnected) { + var height = GetTile(hover.Position).Height[hover.Corner]; + foreach (var neighbor in GetNeighbors(hover.Position, hover.Corner)) { + var neighborHeight = GetTile(neighbor.Position).Height[neighbor.Corner]; + if (neighborHeight != height) continue; + GetTileToChange(neighbor.Position)[neighbor.Corner] = true; + } + } + } else { + var tiles = (toolShape switch { + ToolShape.Circle => GetTilesInRadius(), + ToolShape.Square => GetTilesInSquare(), + _ => throw new InvalidOperationException(), + }).ToHashSet(); + + // Modify selected tiles themselves. foreach (var pos in tiles) - foreach (var corner2 in Enum.GetValues()) - cornersToChange.Add((pos, corner2)); + GetTileToChange(pos) = new(true); - if (isConnected) { - // If the 'connected_toggle' button is active, move "connected" corners. - // Connected corners are the ones that are at the same height as ones already being moved. - foreach (var pos in tiles) { - var tile = GetTile(pos); - foreach (var corner in Enum.GetValues()) { - var height = tile.Height[corner]; - foreach (var (neighborPos, neighborCorner) in GetNeighbors(pos, corner)) { - if (tiles.Contains(neighborPos)) continue; - var neighborHeight = GetTile(neighborPos).Height[neighborCorner]; - if (neighborHeight == height) cornersToChange.Add((neighborPos, neighborCorner)); - } + // If the 'connected_toggle' button is active, move "connected" corners. + // Connected corners are the ones that are at the same height as ones already being moved. + if (isConnected) foreach (var pos in tiles) { + var tile = GetTile(pos); + foreach (var corner in Enum.GetValues()) { + var height = tile.Height[corner]; + foreach (var neighbor in GetNeighbors(pos, corner)) { + if (tiles.Contains(neighbor.Position)) continue; + var neighborHeight = GetTile(neighbor.Position).Height[neighbor.Corner]; + if (neighborHeight != height) continue; + GetTileToChange(neighbor.Position)[neighbor.Corner] = true; } } } + } + + // Raise / lower the terrain when left mouse button is pressed. + if (ev is InputEventMouseButton { ButtonIndex: MouseButton.Left, Pressed: true }) { + prevent_default = true; const float AdjustHeight = 0.5f; var amount = isFlatten ? GetTile(hover.Position).Height[hover.Corner] @@ -111,16 +130,15 @@ public partial class Terrain var tilesPrevious = new List<(TilePos, Corners)>(); var tilesChanged = new List<(TilePos, Corners)>(); - foreach (var group in cornersToChange.GroupBy(c => c.Position, c => c.Corner)) { - var pos = group.Key; + foreach (var (pos, corners) in tilesToChange) { var tile = GetTile(pos); tilesPrevious.Add((pos, tile.Height)); var newHeight = tile.Height; - foreach (var corner in group) { - if (isFlatten) newHeight[corner] = amount; - else newHeight[corner] += amount; - } + if (corners.TopLeft ) newHeight.TopLeft = isFlatten ? amount : newHeight.TopLeft + amount; + if (corners.TopRight ) newHeight.TopRight = isFlatten ? amount : newHeight.TopRight + amount; + if (corners.BottomRight) newHeight.BottomRight = isFlatten ? amount : newHeight.BottomRight + amount; + if (corners.BottomLeft ) newHeight.BottomLeft = isFlatten ? amount : newHeight.BottomLeft + amount; tilesChanged.Add((pos, newHeight)); } @@ -140,7 +158,7 @@ public partial class Terrain } } - UpdateEditToolMesh(tiles); + UpdateEditToolMesh(tilesToChange); return prevent_default; } @@ -157,29 +175,28 @@ public partial class Terrain } - void UpdateEditToolMesh(IEnumerable tiles) + void UpdateEditToolMesh(Dictionary> tiles) { var mesh = GetOrCreateMesh("EditToolMesh"); mesh.ClearSurfaces(); mesh.SurfaceBegin(Mesh.PrimitiveType.Lines); - void AddLine(Vector3 start, Vector3 end) { - mesh.SurfaceAddVertex(start); - mesh.SurfaceAddVertex(end); + void AddLine((Vector3 Position, bool Visible) start, + (Vector3 Position, bool Visible) end) { + mesh.SurfaceSetColor(start.Visible ? Colors.Blue : Colors.Transparent); + mesh.SurfaceAddVertex(start.Position); + mesh.SurfaceSetColor(end.Visible ? Colors.Blue : Colors.Transparent); + mesh.SurfaceAddVertex(end.Position); } - void AddQuad(Vector3 topLeft , Vector3 topRight , - Vector3 bottomRight, Vector3 bottomLeft) { - AddLine(topLeft , topRight ); - AddLine(topRight , bottomRight); - AddLine(bottomRight, bottomLeft ); - AddLine(bottomLeft , topLeft ); - } - - foreach (var tile in tiles) { - var (topLeft, topRight, bottomRight, bottomLeft) - = GetTileCornerPositions(tile); - AddQuad(topLeft, topRight, bottomRight, bottomLeft); + foreach (var (tile, visible) in tiles) { + var positions = GetTileCornerPositions(tile); + foreach (var side in Enum.GetValues()) { + var (corner1, corner2) = side.GetCorners(); + if (!visible[corner1] && !visible[corner2]) continue; + AddLine((positions[corner1], visible[corner1]), + (positions[corner2], visible[corner2])); + } } mesh.SurfaceEnd(); @@ -211,7 +228,7 @@ public partial class Terrain [Corner.BottomRight] = [(+1, +1, Corner.TopLeft ), (+1, 0, Corner.BottomLeft ), (0, +1, Corner.TopRight )], [Corner.BottomLeft ] = [(-1, +1, Corner.TopRight ), (-1, 0, Corner.BottomRight), (0, +1, Corner.TopLeft )], }; - static IEnumerable<(TilePos, Corner)> GetNeighbors(TilePos pos, Corner corner) + static IEnumerable<(TilePos Position, Corner Corner)> GetNeighbors(TilePos pos, Corner corner) => _offsetLookup[corner].Select(e => (new TilePos(pos.X + e.X, pos.Y + e.Y), e.Opposite)); static byte[] Pack(IEnumerable<(TilePos Position, Corners Corners)> data)