Compare commits

...

2 Commits

  1. 8
      GodotExtensions.cs
  2. 15
      addons/terrain-editing/terrain_editing_plugin.gd
  3. 172
      level.tscn
  4. 7
      terrain/Terrain+Editing.cs
  5. 61
      terrain/Terrain.cs

@ -5,10 +5,10 @@ public static class GodotExtensions
public static (Corner, Corner) GetCorners(this Side side)
=> side switch {
Side.Left => (Corner.TopLeft, Corner.BottomLeft),
Side.Top => (Corner.TopLeft, Corner.TopRight),
Side.Right => (Corner.TopRight, Corner.BottomRight),
Side.Bottom => (Corner.BottomLeft, Corner.BottomRight),
Side.Left => (Corner.BottomLeft , Corner.TopLeft ),
Side.Top => (Corner.TopLeft , Corner.TopRight ),
Side.Right => (Corner.TopRight , Corner.BottomRight),
Side.Bottom => (Corner.BottomRight, Corner.BottomLeft ),
_ => throw new ArgumentException($"Invalid Side value '{side}'", nameof(side)),
};

@ -17,6 +17,8 @@ func _make_visible(visible: bool) -> void:
remove_control_from_container(CONTAINER, controls)
func _forward_3d_gui_input(camera: Camera3D, event: InputEvent) -> int:
var position := Vector3.ZERO
if event is InputEventMouse:
var previous_terrain := current_terrain
current_terrain = null
@ -33,17 +35,18 @@ func _forward_3d_gui_input(camera: Camera3D, event: InputEvent) -> int:
var result := space.intersect_ray(query)
var terrain := result.get("collider") as StaticBody3D
if is_terrain(terrain):
# Allow terrain access to editor undo redo functionality.
terrain.EditorUndoRedo = get_undo_redo()
if terrain.EditorInput(event, result["position"], controls):
return AFTER_GUI_INPUT_STOP
current_terrain = terrain
return AFTER_GUI_INPUT_PASS
position = result["position"]
if previous_terrain and previous_terrain != current_terrain:
previous_terrain.EditorUnfocus()
if current_terrain:
# Allow terrain access to editor undo redo functionality.
current_terrain.EditorUndoRedo = get_undo_redo()
if current_terrain.EditorInput(event, controls, position):
return AFTER_GUI_INPUT_STOP
return AFTER_GUI_INPUT_PASS
func is_terrain(object: Object) -> bool:

@ -307,7 +307,7 @@ Vector2i(23, 23): {
"texture": 2
},
Vector2i(23, 24): {
"heights": PackedFloat32Array(6.5, 3, 1.5, 4),
"heights": PackedFloat32Array(6.5, 3, 3, 4),
"texture": 2
},
Vector2i(23, 25): {
@ -371,11 +371,11 @@ Vector2i(24, 22): {
"texture": 2
},
Vector2i(24, 23): {
"heights": PackedFloat32Array(5, 3.5, 0, 3),
"heights": PackedFloat32Array(5, 3.5, 2, 3),
"texture": 2
},
Vector2i(24, 24): {
"heights": PackedFloat32Array(3, 0, 0, 1.5),
"heights": PackedFloat32Array(3, 1, 0, 3),
"texture": 2
},
Vector2i(24, 25): {
@ -435,15 +435,15 @@ Vector2i(25, 22): {
"texture": 2
},
Vector2i(25, 23): {
"heights": PackedFloat32Array(3.5, 3, -0.5, 0),
"heights": PackedFloat32Array(3.5, 3, 0.5, 2),
"texture": 2
},
Vector2i(25, 24): {
"heights": PackedFloat32Array(0, -0.5, -0.5, 0),
"heights": -0.5,
"texture": 0
},
Vector2i(25, 25): {
"heights": PackedFloat32Array(0, -0.5, -1.5, 0),
"heights": PackedFloat32Array(-0.5, -0.5, -1.5, 0),
"texture": 0
},
Vector2i(25, 26): {
@ -515,7 +515,7 @@ Vector2i(26, 22): {
"texture": 2
},
Vector2i(26, 23): {
"heights": PackedFloat32Array(3, 3.5, -0.5, -0.5),
"heights": PackedFloat32Array(3, 2.5, 0.5, 0.5),
"texture": 2
},
Vector2i(26, 24): {
@ -575,11 +575,11 @@ Vector2i(27, 17): {
"texture": 2
},
Vector2i(27, 18): {
"heights": PackedFloat32Array(1, 3, 6, 1),
"heights": PackedFloat32Array(1, 3, 5.5, 1),
"texture": 2
},
Vector2i(27, 19): {
"heights": PackedFloat32Array(1, 6, 5.5, 2.5),
"heights": PackedFloat32Array(1, 5.5, 5.5, 2.5),
"texture": 2
},
Vector2i(27, 20): {
@ -587,15 +587,15 @@ Vector2i(27, 20): {
"texture": 2
},
Vector2i(27, 21): {
"heights": PackedFloat32Array(1.5, 4, 5.5, 3.5),
"heights": PackedFloat32Array(1.5, 4, 4.5, 3.5),
"texture": 2
},
Vector2i(27, 22): {
"heights": PackedFloat32Array(3.5, 5.5, 3, 3.5),
"heights": PackedFloat32Array(3.5, 4.5, 3, 3.5),
"texture": 2
},
Vector2i(27, 23): {
"heights": PackedFloat32Array(3.5, 3, -0.5, -0.5),
"heights": PackedFloat32Array(2.5, 3, -0.5, 0.5),
"texture": 2
},
Vector2i(27, 24): {
@ -659,11 +659,11 @@ Vector2i(28, 17): {
"texture": 2
},
Vector2i(28, 18): {
"heights": PackedFloat32Array(3, 3, 6, 6),
"heights": PackedFloat32Array(3, 3, 6, 5.5),
"texture": 2
},
Vector2i(28, 19): {
"heights": PackedFloat32Array(6, 6, 7, 5.5),
"heights": PackedFloat32Array(5.5, 6, 7, 5.5),
"texture": 2
},
Vector2i(28, 20): {
@ -671,11 +671,11 @@ Vector2i(28, 20): {
"texture": 2
},
Vector2i(28, 21): {
"heights": PackedFloat32Array(4, 5.5, 4, 5.5),
"heights": PackedFloat32Array(4, 5.5, 4, 4.5),
"texture": 2
},
Vector2i(28, 22): {
"heights": PackedFloat32Array(5.5, 4, 1.5, 3),
"heights": PackedFloat32Array(4.5, 4, 1.5, 3),
"texture": 2
},
Vector2i(28, 23): {
@ -751,11 +751,11 @@ Vector2i(29, 17): {
"texture": 2
},
Vector2i(29, 18): {
"heights": PackedFloat32Array(3, 2, 5, 6),
"heights": PackedFloat32Array(3, 2, 6, 6),
"texture": 2
},
Vector2i(29, 19): {
"heights": PackedFloat32Array(6, 5, 6, 7),
"heights": PackedFloat32Array(6, 6, 6, 7),
"texture": 2
},
Vector2i(29, 20): {
@ -827,15 +827,15 @@ Vector2i(30, 17): {
"texture": 2
},
Vector2i(30, 18): {
"heights": PackedFloat32Array(2, 3.5, 6, 5),
"heights": PackedFloat32Array(2, 3.5, 6, 6),
"texture": 2
},
Vector2i(30, 19): {
"heights": PackedFloat32Array(5, 6, 5, 6),
"heights": PackedFloat32Array(6, 6, 5, 6),
"texture": 2
},
Vector2i(30, 20): {
"heights": PackedFloat32Array(6, 5, 0, 2),
"heights": PackedFloat32Array(5, 4, 0, 2),
"texture": 2
},
Vector2i(30, 21): {
@ -903,11 +903,11 @@ Vector2i(31, 18): {
"texture": 2
},
Vector2i(31, 19): {
"heights": PackedFloat32Array(6, 5, 4, 5),
"heights": PackedFloat32Array(6, 5, 4.5, 4.5),
"texture": 2
},
Vector2i(31, 20): {
"heights": PackedFloat32Array(5, 4, 0, 0),
"heights": PackedFloat32Array(2.5, 3.5, 0.5, 0),
"texture": 2
},
Vector2i(31, 21): {
@ -971,11 +971,11 @@ Vector2i(31, 37): {
"texture": 0
},
Vector2i(32, 16): {
"heights": PackedFloat32Array(0, 0, 5.5, 2.5),
"heights": PackedFloat32Array(0, 1, 4.5, 2.5),
"texture": 2
},
Vector2i(32, 17): {
"heights": PackedFloat32Array(2.5, 5.5, 5.5, 2.5),
"heights": PackedFloat32Array(2.5, 4.5, 5.5, 2.5),
"texture": 2
},
Vector2i(32, 18): {
@ -987,7 +987,7 @@ Vector2i(32, 19): {
"texture": 2
},
Vector2i(32, 20): {
"heights": PackedFloat32Array(4, 5.5, 0.5, 0),
"heights": PackedFloat32Array(3, 5, 1.5, 0.5),
"texture": 2
},
Vector2i(32, 21): {
@ -1051,11 +1051,11 @@ Vector2i(32, 37): {
"texture": 0
},
Vector2i(33, 16): {
"heights": PackedFloat32Array(0, 0, 4.5, 5.5),
"heights": PackedFloat32Array(1, 2, 4.5, 4.5),
"texture": 2
},
Vector2i(33, 17): {
"heights": PackedFloat32Array(5.5, 4.5, 4.5, 5.5),
"heights": PackedFloat32Array(4.5, 4.5, 4.5, 5.5),
"texture": 2
},
Vector2i(33, 18): {
@ -1067,7 +1067,7 @@ Vector2i(33, 19): {
"texture": 2
},
Vector2i(33, 20): {
"heights": PackedFloat32Array(5.5, 3.5, 0.5, 0.5),
"heights": PackedFloat32Array(5, 3.5, 1, 1.5),
"texture": 2
},
Vector2i(33, 21): {
@ -1123,7 +1123,7 @@ Vector2i(33, 36): {
"texture": 0
},
Vector2i(34, 16): {
"heights": PackedFloat32Array(0, 0, 4, 4.5),
"heights": PackedFloat32Array(2, 2, 4, 4.5),
"texture": 2
},
Vector2i(34, 17): {
@ -1139,15 +1139,15 @@ Vector2i(34, 19): {
"texture": 2
},
Vector2i(34, 20): {
"heights": PackedFloat32Array(3.5, 4, 0, 0.5),
"heights": PackedFloat32Array(3.5, 4, 2, 1),
"texture": 2
},
Vector2i(34, 21): {
"heights": PackedFloat32Array(0.5, 0, 0, 0.5),
"heights": PackedFloat32Array(0.5, 1, 0.5, 0.5),
"texture": 0
},
Vector2i(34, 22): {
"heights": PackedFloat32Array(0.5, 0, -0.5, 0.5),
"heights": PackedFloat32Array(0.5, 0.5, -0.5, 0.5),
"texture": 1
},
Vector2i(34, 23): {
@ -1195,7 +1195,7 @@ Vector2i(34, 36): {
"texture": 0
},
Vector2i(35, 16): {
"heights": PackedFloat32Array(0, 0, 1, 4),
"heights": PackedFloat32Array(2, 0, 1, 4),
"texture": 2
},
Vector2i(35, 17): {
@ -1203,23 +1203,23 @@ Vector2i(35, 17): {
"texture": 2
},
Vector2i(35, 18): {
"heights": PackedFloat32Array(4, 2.5, 6.5, 5),
"heights": PackedFloat32Array(4, 2.5, 6, 5),
"texture": 2
},
Vector2i(35, 19): {
"heights": PackedFloat32Array(5, 6.5, 5.5, 4),
"heights": PackedFloat32Array(5, 6, 5.5, 4),
"texture": 2
},
Vector2i(35, 20): {
"heights": PackedFloat32Array(4, 5.5, 1.5, 0),
"heights": PackedFloat32Array(4, 5.5, 2.5, 2.5),
"texture": 2
},
Vector2i(35, 21): {
"heights": PackedFloat32Array(0, 1.5, 0, 0),
"heights": PackedFloat32Array(2.5, 2.5, 2, 2),
"texture": 2
},
Vector2i(35, 22): {
"heights": PackedFloat32Array(0, 0, -0.5, -0.5),
"heights": PackedFloat32Array(0.5, 0.5, -0.5, -0.5),
"texture": 1
},
Vector2i(35, 23): {
@ -1259,23 +1259,23 @@ Vector2i(36, 17): {
"texture": 2
},
Vector2i(36, 18): {
"heights": PackedFloat32Array(2.5, 4, 4, 6.5),
"heights": PackedFloat32Array(2.5, 4, 4, 6),
"texture": 2
},
Vector2i(36, 19): {
"heights": PackedFloat32Array(6.5, 4, 4.5, 5.5),
"heights": PackedFloat32Array(6, 4, 4.5, 5.5),
"texture": 2
},
Vector2i(36, 20): {
"heights": PackedFloat32Array(5.5, 4.5, 4.5, 1.5),
"heights": PackedFloat32Array(5.5, 4.5, 4.5, 2.5),
"texture": 2
},
Vector2i(36, 21): {
"heights": PackedFloat32Array(1.5, 4.5, 3, 0),
"heights": PackedFloat32Array(2.5, 4.5, 3, 2),
"texture": 2
},
Vector2i(36, 22): {
"heights": PackedFloat32Array(0, 3, 0, -0.5),
"heights": PackedFloat32Array(1, 3, 0, -0.5),
"texture": 2
},
Vector2i(36, 23): {
@ -1518,6 +1518,18 @@ Vector2i(41, 24): {
"heights": PackedFloat32Array(-0.5, 0, 0, 0),
"texture": 0
},
Vector2i(41, 25): {
"heights": PackedFloat32Array(0, 0, 0.5, 0),
"texture": 0
},
Vector2i(41, 26): {
"heights": PackedFloat32Array(0, -0.5, -0.5, 0),
"texture": 0
},
Vector2i(41, 27): {
"heights": PackedFloat32Array(0, -1, 0, 0),
"texture": 0
},
Vector2i(42, 19): {
"heights": PackedFloat32Array(0, 0, 4.5, 3),
"texture": 2
@ -1534,6 +1546,18 @@ Vector2i(42, 22): {
"heights": PackedFloat32Array(4, 5.5, 0, 0),
"texture": 2
},
Vector2i(42, 25): {
"heights": PackedFloat32Array(0, 0, -0.5, -0.5),
"texture": 0
},
Vector2i(42, 26): {
"heights": -0.5,
"texture": 0
},
Vector2i(42, 27): {
"heights": PackedFloat32Array(-0.5, -0.5, 0, 0),
"texture": 0
},
Vector2i(43, 19): {
"heights": PackedFloat32Array(0, 0, 4.5, 4.5),
"texture": 2
@ -1550,6 +1574,26 @@ Vector2i(43, 22): {
"heights": PackedFloat32Array(5.5, 4.5, 0, 0),
"texture": 2
},
Vector2i(43, 25): {
"heights": PackedFloat32Array(0, 0, 0, 1),
"texture": 0
},
Vector2i(43, 26): {
"heights": PackedFloat32Array(-0.5, 0, 0, -0.5),
"texture": 0
},
Vector2i(43, 27): {
"heights": PackedFloat32Array(1.5, 0, 1, 0),
"texture": 0
},
Vector2i(43, 28): {
"heights": PackedFloat32Array(0, 1, 1, 0),
"texture": 0
},
Vector2i(43, 29): {
"heights": PackedFloat32Array(0, 1, 0, 0),
"texture": 0
},
Vector2i(44, 19): {
"heights": PackedFloat32Array(0, 0, 3, 4.5),
"texture": 2
@ -1566,6 +1610,18 @@ Vector2i(44, 22): {
"heights": PackedFloat32Array(4.5, 3, 0, 0),
"texture": 2
},
Vector2i(44, 27): {
"heights": PackedFloat32Array(0, 0, 2, 1),
"texture": 0
},
Vector2i(44, 28): {
"heights": PackedFloat32Array(2, 4, 5.5, 4.5),
"texture": 0
},
Vector2i(44, 29): {
"heights": PackedFloat32Array(1, 2, 0, 0),
"texture": 0
},
Vector2i(45, 19): {
"heights": PackedFloat32Array(0, 0, 0, 3),
"texture": 2
@ -1581,6 +1637,34 @@ Vector2i(45, 21): {
Vector2i(45, 22): {
"heights": PackedFloat32Array(3, 0, 0, 0),
"texture": 2
},
Vector2i(45, 25): {
"heights": PackedFloat32Array(0.5, 1, 1.5, 2),
"texture": 0
},
Vector2i(45, 27): {
"heights": 2.0,
"texture": 0
},
Vector2i(45, 28): {
"heights": PackedFloat32Array(3.5, 3.5, 4.5, 4.5),
"texture": 0
},
Vector2i(45, 29): {
"heights": PackedFloat32Array(2, 1, 0, 0),
"texture": 0
},
Vector2i(46, 27): {
"heights": PackedFloat32Array(0, 0, 0, 1),
"texture": 0
},
Vector2i(46, 28): {
"heights": PackedFloat32Array(1, 0, 0, 1),
"texture": 0
},
Vector2i(46, 29): {
"heights": PackedFloat32Array(1, 0, 0, 0),
"texture": 0
}
}

@ -24,10 +24,14 @@ public partial class Terrain
};
}
public bool EditorInput(InputEventMouse ev, Vector3 position, Control controls)
Vector3 _lastPosition;
public bool EditorInput(InputEvent ev, Control controls, Vector3 maybePos)
{
var prevent_default = false;
// If 'maybePosition' is Vector3.Zero, use previous know position, otherwise update.
var position = (maybePos == Vector3.Zero) ? _lastPosition : (_lastPosition = maybePos);
var toolMode = (ToolMode)(int)controls.Get("tool_mode");
var toolShape = (ToolShape)(int)controls.Get("tool_shape");
var texture = (int)controls.Get("texture");
@ -71,7 +75,6 @@ public partial class Terrain
// TODO: Handle different tool modes, such as painting.
// 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.

@ -63,13 +63,12 @@ public partial class Terrain
var num_textures = 4;
var num_blend_textures = 7;
var rnd = new Random();
var rnd = new Random();
for (var x = 0; x < Size.X; x++)
for (var z = 0; z < Size.Y; z++) {
var tile = GetTile(new(x, z));
var corners = GetTileCornerPositions(new(x, z), tile);
// Randomly select two different textures and one blend texture.
mesh.SurfaceSetColor(new(
(float)tile.TexturePrimary / num_textures,
(float)tile.TextureSecondary / num_textures,
@ -77,10 +76,10 @@ public partial class Terrain
));
var sorted = new (Corner Corner, float Height)[] {
(Corner.TopLeft , tile.Height.TopLeft ),
(Corner.TopRight , tile.Height.TopRight ),
(Corner.BottomRight , tile.Height.BottomRight),
(Corner.BottomLeft , tile.Height.BottomLeft ),
(Corner.TopLeft , tile.Height.TopLeft ),
(Corner.TopRight , tile.Height.TopRight ),
(Corner.BottomRight, tile.Height.BottomRight),
(Corner.BottomLeft , tile.Height.BottomLeft ),
};
Array.Sort(sorted, (a, b) => a.Height.CompareTo(b.Height));
@ -105,6 +104,56 @@ public partial class Terrain
corners.BottomLeft , new(0.0f, 1.0f),
corners.TopLeft , new(0.0f, 0.0f));
}
// Set stone texture for walls.
mesh.SurfaceSetColor(new(
(float)2 / num_textures,
(float)2 / num_textures,
(float)0 / num_blend_textures
));
void DrawWall(TilePos nbrPos, Side side) {
var nbrTile = GetTile(nbrPos);
var nbrCorners = GetTileCornerPositions(nbrPos, nbrTile);
var (c1, c2) = side.GetCorners();
var (c3, c4) = side.GetOpposite().GetCorners();
var corner1 = corners[c1]; // "TopRight"
var corner2 = corners[c2]; // "TopLeft"
var corner3 = nbrCorners[c3]; // "BottomLeft"
var corner4 = nbrCorners[c4]; // "BottomRight"
var equal1 = IsEqualApprox(corner1.Y, corner4.Y);
var equal2 = IsEqualApprox(corner2.Y, corner3.Y);
switch (equal1, equal2) {
case (true, true):
// Both corners are connected, no wall needed.
break;
case (true, false):
AddTriangle(corner1, new(1.0f, corner1.Y / TileSize),
corner3, new(0.0f, corner3.Y / TileSize),
corner2, new(0.0f, corner2.Y / TileSize));
break;
case (false, true):
AddTriangle(corner1, new(1.0f, corner1.Y / TileSize),
corner4, new(1.0f, corner4.Y / TileSize),
corner2, new(0.0f, corner2.Y / TileSize));
break;
case (false, false):
AddTriangle(corner1, new(1.0f, corner1.Y / TileSize),
corner4, new(1.0f, corner4.Y / TileSize),
corner2, new(0.0f, corner2.Y / TileSize));
AddTriangle(corner2, new(0.0f, corner2.Y / TileSize),
corner4, new(1.0f, corner4.Y / TileSize),
corner3, new(0.0f, corner3.Y / TileSize));
break;
}
}
if (x < Size.X - 1) DrawWall(new(x + 1, z), Side.Right);
if (z < Size.Y - 1) DrawWall(new(x, z + 1), Side.Bottom);
}
mesh.SurfaceEnd();

Loading…
Cancel
Save