Compare commits

...

2 Commits

  1. 1
      addons/terrain-editing/terrain_editing_plugin.gd
  2. 0
      assets/textures/terrain/editing/height_lower.png
  3. 6
      assets/textures/terrain/editing/height_lower.png.import
  4. 0
      assets/textures/terrain/editing/height_raise.png
  5. 6
      assets/textures/terrain/editing/height_raise.png.import
  6. 3
      terrain/Tile.cs
  7. 98
      terrain/editing/TerrainEditingControls+Editing.cs
  8. 6
      terrain/editing/TerrainEditingControls.cs
  9. 6
      terrain/editing/TerrainEditingControls.tscn

@ -20,6 +20,7 @@ func _make_visible(visible: bool) -> void:
if not controls:
var controls_scene = load("res://terrain/editing/TerrainEditingControls.tscn")
controls = controls_scene.instantiate()
controls.EditorUndoRedo = get_undo_redo()
add_control_to_container(CONTAINER, controls)
elif controls and controls.get_parent():
remove_control_from_container(CONTAINER, controls)

Before

Width:  |  Height:  |  Size: 135 B

After

Width:  |  Height:  |  Size: 135 B

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://covyafauwthij"
path="res://.godot/imported/height_down.png-47ae6eeb5feb0ddf7bd952befb4351fe.ctex"
path="res://.godot/imported/height_lower.png-d00d3c7062c0a355f5c8381ee7e664da.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/textures/terrain/editing/height_down.png"
dest_files=["res://.godot/imported/height_down.png-47ae6eeb5feb0ddf7bd952befb4351fe.ctex"]
source_file="res://assets/textures/terrain/editing/height_lower.png"
dest_files=["res://.godot/imported/height_lower.png-d00d3c7062c0a355f5c8381ee7e664da.ctex"]
[params]

Before

Width:  |  Height:  |  Size: 149 B

After

Width:  |  Height:  |  Size: 149 B

@ -3,15 +3,15 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://dxbfohim13ti1"
path="res://.godot/imported/height_up.png-d84180c0cff17a8285e5e5cb8f54e819.ctex"
path="res://.godot/imported/height_raise.png-a5718ceb2e90ce17597f6300a0a9a715.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/textures/terrain/editing/height_up.png"
dest_files=["res://.godot/imported/height_up.png-d84180c0cff17a8285e5e5cb8f54e819.ctex"]
source_file="res://assets/textures/terrain/editing/height_raise.png"
dest_files=["res://.godot/imported/height_raise.png-a5718ceb2e90ce17597f6300a0a9a715.ctex"]
[params]

@ -90,6 +90,9 @@ public struct Corners<T>(T topLeft, T topRight, T bottomRight, T bottomLeft)
public T BottomRight = bottomRight;
public T BottomLeft = bottomLeft;
public Corners(T value)
: this(value, value, value, value) { }
public T this[Corner corner] {
readonly get => corner switch {
Corner.TopLeft => TopLeft,

@ -1,5 +1,10 @@
using System.IO;
public partial class TerrainEditingControls
{
// Set by the terrain editing plugin.
public EditorUndoRedoManager EditorUndoRedo { get; set; }
Terrain _currentTerrain = null;
Material _editToolMaterial;
@ -76,7 +81,6 @@ public partial class TerrainEditingControls
// TODO: Handle different tool modes, such as flatten and painting.
// TODO: Allow click-dragging which doesn't affect already changed tiles / corners.
// TODO: Support undo and redo.
// TODO: Make mesh generation generate vertical walls between disconnected corners.
// TODO: Use ArrayMesh instead of ImmediateMesh.
@ -84,13 +88,16 @@ public partial class TerrainEditingControls
if (mouse is InputEventMouseButton { ButtonIndex: MouseButton.Left, Pressed: true }) {
GetViewport().SetInputAsHandled();
const float AdjustHeight = 0.5f;
var amount = UpDownToggle.ButtonPressed ? AdjustHeight : -AdjustHeight;
var cornersToChange = new HashSet<(TilePos Position, Corner Corner)>();
// Raise selected tiles themselves.
foreach (var pos in tiles)
foreach (var corner2 in Enum.GetValues<Corner>())
cornersToChange.Add((pos, corner2));
// 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 (ConnectedToggle.ButtonPressed) {
var corners = new HashSet<(TilePos Position, Corner Corner)>();
foreach (var pos in tiles) {
var tile2 = terrain.GetTile(pos);
foreach (var corner2 in Enum.GetValues<Corner>()) {
@ -98,30 +105,48 @@ public partial class TerrainEditingControls
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));
if (neighborHeight == height) cornersToChange.Add((neighborPos, neighborCorner));
}
}
}
}
// Raise connected corners.
foreach (var group in corners.GroupBy(e => e.Position, e => e.Corner)) {
var isFlatten = ToolMode == ToolMode.Flatten;
var isRaise = RaiseLowerToggle.ButtonPressed;
const float AdjustHeight = 0.5f;
var amount = isFlatten ? terrain.GetTile(tile).Height[corner]
: isRaise ? AdjustHeight : -AdjustHeight;
var tilesPrevious = new List<(TilePos, Corners<float>)>();
var tilesChanged = new List<(TilePos, Corners<float>)>();
foreach (var group in cornersToChange.GroupBy(c => c.Position, c => c.Corner)) {
var pos = group.Key;
var tile2 = terrain.GetTile(pos);
foreach (var corner2 in group)
tile2.Height[corner2] += amount;
terrain.SetTile(pos, tile2);
tilesPrevious.Add((pos, tile2.Height));
var newHeight = tile2.Height;
foreach (var corner2 in group) {
if (isFlatten) newHeight[corner2] = amount;
else newHeight[corner2] += amount;
}
tilesChanged.Add((pos, newHeight));
}
// Raise selected tiles themselves.
foreach (var pos in tiles) {
var tile2 = terrain.GetTile(pos);
tile2.Height.Adjust(amount);
terrain.SetTile(pos, tile2);
}
if (EditorUndoRedo is EditorUndoRedoManager undo) {
var name = "Modify terrain"; // TODO: Change name depending on tool mode.
undo.CreateAction(name, customContext: terrain, backwardUndoOps: false);
terrain.UpdateMeshAndShape();
terrain.NotifyPropertyListChanged();
undo.AddDoMethod(this, nameof(TerrainModifyHeight), terrain, Pack(tilesChanged));
undo.AddDoMethod(terrain, GodotObject.MethodName.NotifyPropertyListChanged);
undo.AddDoMethod(terrain, nameof(Terrain.UpdateMeshAndShape));
undo.AddUndoMethod(this, nameof(TerrainModifyHeight), terrain, Pack(tilesPrevious));
undo.AddUndoMethod(terrain, GodotObject.MethodName.NotifyPropertyListChanged);
undo.AddUndoMethod(terrain, nameof(Terrain.UpdateMeshAndShape));
undo.CommitAction(true);
}
}
UpdateEditToolMesh(terrain, tiles);
@ -131,6 +156,43 @@ public partial class TerrainEditingControls
}
}
public void TerrainModifyHeight(Terrain terrain, byte[] data)
{
foreach (var (pos, corners) in Unpack(data)) {
var tile = terrain.GetTile(pos);
tile.Height = corners;
terrain.SetTile(pos, tile);
}
}
static byte[] Pack(IEnumerable<(TilePos Position, Corners<float> Corners)> data) {
using var stream = new MemoryStream();
using var writer = new BinaryWriter(stream);
foreach (var (pos, corners) in data) {
writer.Write(pos.X);
writer.Write(pos.Y);
writer.Write(corners.TopLeft);
writer.Write(corners.TopRight);
writer.Write(corners.BottomRight);
writer.Write(corners.BottomLeft);
}
return stream.ToArray();
}
static IEnumerable<(TilePos Position, Corners<float> Corners)> Unpack(byte[] data)
{
using var stream = new MemoryStream(data);
using var reader = new BinaryReader(stream);
while (stream.Position < stream.Length) {
var x = reader.ReadInt32();
var y = reader.ReadInt32();
var corners = new Corners<float>(
reader.ReadSingle(), reader.ReadSingle(),
reader.ReadSingle(), reader.ReadSingle());
yield return (new(x, y), corners);
}
}
void UpdateEditToolMesh(Terrain terrain, IEnumerable<TilePos> tiles)
{
if (terrain != _currentTerrain) ClearEditToolMesh();

@ -7,7 +7,7 @@ public partial class TerrainEditingControls
public Slider DrawSizeSlider { get; private set; }
public Button[] PaintTextureButtons { get; private set; }
public Button UpDownToggle { get; private set; }
public Button RaiseLowerToggle { get; private set; }
public Button ConnectedToggle { get; private set; }
public ToolMode ToolMode { get => GetToolMode (); set => SetToolMode (value); }
@ -48,7 +48,7 @@ public partial class TerrainEditingControls
DrawSizeSlider = GetNode<Slider>("SizeSlider");
DrawSizeSlider.ValueChanged += (_) => drawSizeLabel.Text = $"{DrawSize}";
UpDownToggle = GetNode<Button>("UpDown");
RaiseLowerToggle = GetNode<Button>("RaiseLower");
ConnectedToggle = GetNode<Button>("Connected");
}
@ -67,7 +67,7 @@ public partial class TerrainEditingControls
ToolShapeButtons[0].Item2.Icon = (value != ToolMode.Paint)
? CornerTextureNormal : CornerTexturePaint;
UpDownToggle.Disabled = value is ToolMode.Flatten or ToolMode.Paint;
RaiseLowerToggle.Disabled = value is ToolMode.Flatten or ToolMode.Paint;
ConnectedToggle.Disabled = value is ToolMode.Paint;
}

@ -9,8 +9,8 @@
[ext_resource type="Texture2D" uid="uid://dc0q2xn2cgcjw" path="res://assets/textures/terrain/editing/corner_paint.png" id="3_e00xo"]
[ext_resource type="Texture2D" uid="uid://2u1ldmh0osbx" path="res://assets/textures/terrain/editing/circle.png" id="2_yvc34"]
[ext_resource type="Texture2D" uid="uid://btjd1704xtdjv" path="res://assets/textures/terrain/editing/square.png" id="3_aaaoe"]
[ext_resource type="Texture2D" uid="uid://dxbfohim13ti1" path="res://assets/textures/terrain/editing/height_up.png" id="9_u4loi"]
[ext_resource type="Texture2D" uid="uid://covyafauwthij" path="res://assets/textures/terrain/editing/height_down.png" id="10_owj33"]
[ext_resource type="Texture2D" uid="uid://dxbfohim13ti1" path="res://assets/textures/terrain/editing/height_raise.png" id="9_u4loi"]
[ext_resource type="Texture2D" uid="uid://covyafauwthij" path="res://assets/textures/terrain/editing/height_lower.png" id="10_owj33"]
[ext_resource type="Texture2D" uid="uid://dsbovbbrtuv8f" path="res://assets/textures/terrain/editing/connected_on.png" id="8_4qifu"]
[ext_resource type="Texture2D" uid="uid://c5j5v62f7p6qt" path="res://assets/textures/terrain/editing/connected_off.png" id="12_5hs2d"]
[ext_resource type="Image" uid="uid://b0jp1dyxugbr7" path="res://assets/textures/terrain/grass.png" id="Image_d41co"]
@ -91,7 +91,7 @@ value = -1.0
[node name="HSeparator2" type="HSeparator" parent="."]
layout_mode = 2
[node name="UpDown" type="Button" parent="."]
[node name="RaiseLower" type="Button" parent="."]
layout_mode = 2
toggle_mode = true
button_pressed = true

Loading…
Cancel
Save