From 5541872d2089bb33b68a1d8fa5c4f40044d078ed Mon Sep 17 00:00:00 2001 From: copygirl Date: Sat, 28 Sep 2024 23:38:28 +0200 Subject: [PATCH] Re-introduce simple terrain editing --- .../editing/TerrainEditingControls+Editing.cs | 167 +++++++----------- 1 file changed, 63 insertions(+), 104 deletions(-) diff --git a/terrain/editing/TerrainEditingControls+Editing.cs b/terrain/editing/TerrainEditingControls+Editing.cs index 5873ceb..e5c42ad 100644 --- a/terrain/editing/TerrainEditingControls+Editing.cs +++ b/terrain/editing/TerrainEditingControls+Editing.cs @@ -66,119 +66,69 @@ public partial class TerrainEditingControls new Vector2(tile.X + 0.5f, tile.Y + 0.5f)) < distanceSqr); } - UpdateEditToolMesh(terrain, ToolShape switch { + var tiles = (ToolShape switch { // TODO: Edit corner, not full tile. ToolShape.Corner => [tile], ToolShape.Circle => GetTilesInRadius(), ToolShape.Square => GetTilesInSquare(), _ => throw new InvalidOperationException(), - }); + }).ToHashSet(); + + // TODO: Handle different tool modes, such as painting. + // TODO: Add a flatten tool mode: Flattens everything selected to nearest corner height. + // TODO: Allow click-dragging which doesn't affect already changed tiles / corners. + // TODO: Support undo and redo. + // TODO: Support "disconnected" mode which can create vertical cliffs. + + // Raise / lower the terrain if left / right mouse button is pressed. + if ((mouse is InputEventMouseButton { ButtonIndex: var button, Pressed: true }) + && (button is MouseButton.Left or MouseButton.Right)) + { + GetViewport().SetInputAsHandled(); + + const float AdjustHeight = 0.5f; + var amount = (button == MouseButton.Left) + ? AdjustHeight : -AdjustHeight; + + // Find corners that are "connected" and should be raised. + var corners = new HashSet<(TilePos Position, Corner Corner)>(); + foreach (var pos in tiles) { + var tile2 = terrain.GetTile(pos); + foreach (var corner2 in Enum.GetValues()) { + var height = tile2.Height[corner2]; + foreach (var (neighborPos, neighborCorner) in GetNeighbors(pos, corner2)) { + if (tiles.Contains(neighborPos)) continue; + var neighborHeight = terrain.GetTile(neighborPos).Height[neighborCorner]; + if (neighborHeight == height) corners.Add((neighborPos, neighborCorner)); + } + } + } + + // Raise connected corners. + foreach (var group in corners.GroupBy(e => e.Position, e => e.Corner)) { + var pos = group.Key; + var tile2 = terrain.GetTile(pos); + foreach (var corner2 in group) + tile2.Height[corner2] += amount; + terrain.SetTile(pos, tile2); + } + + // Raise selected tiles themselves. + foreach (var pos in tiles) { + var tile2 = terrain.GetTile(pos); + tile2.Height.Adjust(amount); + terrain.SetTile(pos, tile2); + } + + terrain.UpdateMeshAndShape(); + terrain.NotifyPropertyListChanged(); + } + + UpdateEditToolMesh(terrain, tiles); } else { ClearEditToolMesh(); } } - - // if (_isSelecting && (ev is InputEventMouseButton { ButtonIndex: MouseButton.Left, Pressed: false })) { - // _isSelecting = false; - // GetViewport().SetInputAsHandled(); - // return; - // } - - // if ((ev is InputEventMouseButton { ButtonIndex: var wheel, Pressed: var pressed, ShiftPressed: true }) - // && (wheel is MouseButton.WheelUp or MouseButton.WheelDown) && (_selection != null)) - // { - // // NOTE: Potential bug in the Godot editor? - // // Does it zoom both when mouse wheel is "pressed" and "released"? - // // Because just cancelling one of them still causes zooming to occur. - // GetViewport().SetInputAsHandled(); - // if (!pressed) return; - - // const float AdjustHeight = 0.5f; - // var amount = (wheel == MouseButton.WheelUp) - // ? AdjustHeight : -AdjustHeight; - - // var selection = TileRegion.From(_selection.Value); - - // // Raise connected corners. - // foreach (var innerCorner in Enum.GetValues()) { - // var outerCorner = innerCorner.GetOpposite(); - - // var innerPos = selection.GetTileFor(innerCorner); - // var outerPos = innerPos.GetNeighbor(innerCorner); - - // var outerTile = terrain.GetTile(outerPos); - // var innerHeight = terrain.GetTile(innerPos).Height[innerCorner]; - // var outerHeight = outerTile.Height[outerCorner]; - - // if (IsEqualApprox(outerHeight, innerHeight)) { - // outerTile.Height[outerCorner] = innerHeight + amount; - // terrain.SetTile(outerPos, outerTile); - // } - // } - - // // Raise connected sides. - // foreach (var side in Enum.GetValues()) { - // foreach (var innerPos in selection.GetTilesFor(side)) { - // var outerPos = innerPos.GetNeighbor(side); - - // var innerTile = terrain.GetTile(innerPos); - // var outerTile = terrain.GetTile(outerPos); - - // var (innerCorner1, innerCorner2) = side.GetCorners(); - // var (outerCorner1, outerCorner2) = side.GetOpposite().GetCorners(); - - // var changed = false; - // var matchingCorners = new[]{ (innerCorner1, outerCorner1), (innerCorner2, outerCorner2) }; - // foreach (var (innerCorner, outerCorner) in matchingCorners) { - // var innerHeight = innerTile.Height[innerCorner]; - // var outerHeight = outerTile.Height[outerCorner]; - - // if (IsEqualApprox(outerHeight, innerHeight)) { - // outerTile.Height[outerCorner] = innerHeight + amount; - // changed = true; - // } - // } - // if (changed) terrain.SetTile(outerPos, outerTile); - // } - // } - - // // Raise selected tiles themselves. - // foreach (var pos in selection.GetAllTiles()) { - // var tile = terrain.GetTile(pos); - // tile.Height.Adjust(amount); - // terrain.SetTile(pos, tile); - // } - - // terrain.UpdateMeshAndShape(); - // terrain.NotifyPropertyListChanged(); - // return; - // } - - // if ((ev is InputEventMouseButton { ButtonIndex: var wheel2, Pressed: var pressed2, CtrlPressed: true }) - // && (wheel2 is MouseButton.WheelUp or MouseButton.WheelDown) && (_selection != null)) - // { - // GetViewport().SetInputAsHandled(); - // if (!pressed2) return; - - // var amount = (wheel2 == MouseButton.WheelUp) ? 1 : -1; - // var selection = TileRegion.From(_selection.Value); - - // foreach (var pos in selection.GetAllTiles()) { - // var tile = terrain.GetTile(pos); - // tile.TexturePrimary = PosMod(tile.TexturePrimary + amount, 4); - // terrain.SetTile(pos, tile); - // } - - // terrain.UpdateMeshAndShape(); - // terrain.NotifyPropertyListChanged(); - // return; - // } - - // if (ev is InputEventMouseMotion) - // _unhandledMotion = true; - - // if ((ev is InputEventMouse mouse) && viewport.GetVisibleRect().HasPoint(mouse.Position)) - // OnInputRayCastTerrain(terrain, mouse); } void UpdateEditToolMesh(Terrain terrain, IEnumerable tiles) @@ -255,4 +205,13 @@ public partial class TerrainEditingControls return (tile, corner); } + + static readonly Dictionary _offsetLookup = new(){ + [Corner.TopLeft ] = [(-1, -1, Corner.BottomRight), (-1, 0, Corner.TopRight ), (0, -1, Corner.BottomLeft )], + [Corner.TopRight ] = [(+1, -1, Corner.BottomLeft ), (+1, 0, Corner.TopLeft ), (0, -1, Corner.BottomRight)], + [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) + => _offsetLookup[corner].Select(e => (new TilePos(pos.X + e.X, pos.Y + e.Y), e.Opposite)); }