Add terrain editing controls

- Not functional yet
- Remove "input in editor" plugin
  (since it's no longer necessary)
- Move terrain editing code to plugin
main
copygirl 2 months ago
parent 4da1da73ef
commit a2cca8165b
  1. 49
      addons/receive-input-in-editor/ReceiveInputInEditorPlugin.cs
  2. 38
      addons/receive-input-in-editor/ReceiveInputInEditorPlugin.gd
  3. 7
      addons/receive-input-in-editor/plugin.cfg
  4. 113
      addons/terrain-editing/TerrainEditingControls+Editing.cs
  5. 101
      addons/terrain-editing/TerrainEditingControls.cs
  6. 120
      addons/terrain-editing/TerrainEditingControls.tscn
  7. 34
      addons/terrain-editing/TerrainEditingPlugin.cs
  8. BIN
      addons/terrain-editing/icons/circle.png
  9. 34
      addons/terrain-editing/icons/circle.png.import
  10. BIN
      addons/terrain-editing/icons/corner.png
  11. 34
      addons/terrain-editing/icons/corner.png.import
  12. BIN
      addons/terrain-editing/icons/height.png
  13. 34
      addons/terrain-editing/icons/height.png.import
  14. BIN
      addons/terrain-editing/icons/paint.png
  15. 34
      addons/terrain-editing/icons/paint.png.import
  16. BIN
      addons/terrain-editing/icons/square.png
  17. 34
      addons/terrain-editing/icons/square.png.import
  18. 7
      addons/terrain-editing/plugin.cfg
  19. 2
      project.godot
  20. 19
      terrain/Terrain.cs

@ -1,49 +0,0 @@
#if TOOLS
[Tool]
public partial class ReceiveInputInEditorPlugin
: EditorPlugin
{
public override bool _Handles(GodotObject obj)
=> obj is Node;
public override int _Forward3DGuiInput(Camera3D camera, InputEvent ev)
{
var root = (Node3D)EditorInterface.Singleton.GetEditedSceneRoot();
var viewport = root.GetViewport();
// Don't know about any negative effect of changing these and leaving them like that.
viewport.GuiDisableInput = false; // Re-enable input, required for `PushInput` to work at all.
viewport.HandleInputLocally = true; // Let sub-viewport handle its own input, makes `SetInputAsHandled` work as expected?
// This will propagate input events into the edited scene,
// including regular, GUI, shortcut, unhandled key and unhandled.
viewport.PushInput(ev);
// Enabling `PhysicsObjectPicking` causes `IsInputHandled()` to always
// return true, and object picking isn't immediate anyway, so let's just
// do it ourselves. This doesn't support touch input, though.
if (!viewport.IsInputHandled() && (ev is InputEventMouse { Position: var mouse })) {
// Ray is cast from the editor camera's view.
var from = camera.ProjectRayOrigin(mouse);
var to = from + camera.ProjectRayNormal(mouse) * camera.Far;
// Actual collision is done in the edited scene though.
var space = root.GetWorld3D().DirectSpaceState;
var query = PhysicsRayQueryParameters3D.Create(from, to);
var result = space.IntersectRay(query);
// The collider object isn't necessarily a `CollisionObject3D`.
var collider = (GodotObject)result.GetValueOrDefault("collider");
if (collider is CollisionObject3D { Visible: true } obj) {
var pos = (Vector3)result["position"];
var normal = (Vector3)result["normal"];
var shape = (int)result["shape"];
obj._InputEvent(camera, ev, pos, normal, shape);
}
}
// If any node calls `SetInputAsHandled()`, the event is not passed on to other editor gizmos / plugins.
return viewport.IsInputHandled() ? (int)AfterGuiInput.Stop : (int)AfterGuiInput.Pass;
}
}
#endif

@ -1,38 +0,0 @@
@tool
extends EditorPlugin
func _handles(obj: Object) -> bool:
return obj is Node
func _forward_3d_gui_input(camera: Camera3D, event: InputEvent) -> int:
var root := EditorInterface.get_edited_scene_root() as Node3D
var viewport := root.get_viewport()
# Don't know about any negative effect of changing these and leaving them like that.
viewport.gui_disable_input = false # Re-enable input, required for `push_input` to work at all.
viewport.handle_input_locally = true # Let sub-viewport handle its own input, makes `set_input_as_handled` work as expected?
# This will propagate input events into the edited scene,
# including regular, GUI, shortcut, unhandled key and unhandled.
viewport.push_input(event)
# Enabling `physics_object_picking` causes `is_input_handled()` to always
# return true, and object picking isn't immediate anyway, so let's just
# do it ourselves. This doesn't support touch input, though.
if !viewport.is_input_handled() and event is InputEventMouse:
# Ray is cast from the editor camera's view.
var from := camera.project_ray_origin(event.position)
var to := from + camera.project_ray_normal(event.position) * camera.far
# Actual collision is done in the edited scene though.
var space := root.get_world_3d().direct_space_state
var query := PhysicsRayQueryParameters3D.create(from, to)
var result := space.intersect_ray(query)
# The collider object isn't necessarily a `CollisionObject3D`.
var obj := result.get("collider") as CollisionObject3D
if obj and obj.visible:
obj._input_event(camera, event, result["position"], result["normal"], result["shape"])
# If any node calls `set_input_as_handled()`, the event is not passed on to other editor gizmos / plugins.
return AfterGUIInput.AFTER_GUI_INPUT_STOP if viewport.is_input_handled() else AfterGUIInput.AFTER_GUI_INPUT_PASS

@ -1,7 +0,0 @@
[plugin]
name="Receive Input in Editor"
description=""
author="copygirl"
version=""
script="ReceiveInputInEditorPlugin.cs"

@ -1,18 +1,37 @@
[Tool] public partial class TerrainEditingControls
public partial class Terrain
{ {
bool _unhandledMotion = false; // Used to detect when mouse moves off the terrain. bool _unhandledMotion = false; // Used to detect when mouse moves off the terrain.
TilePos? _tileHover = null; // Position of currently hovered tile. TilePos? _tileHover = null; // Position of currently hovered tile.
bool _isSelecting = false; // Whether left mouse is held down to select tiles. bool _isSelecting = false; // Whether left mouse is held down to select tiles.
(TilePos, TilePos)? _selection = null; (TilePos, TilePos)? _selection = null;
Material _editToolMaterial;
void OnEditingReady()
{
_editToolMaterial = new StandardMaterial3D {
VertexColorUseAsAlbedo = true,
ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded,
DepthDrawMode = BaseMaterial3D.DepthDrawModeEnum.Disabled,
NoDepthTest = true,
};
}
public override void _Input(InputEvent ev) public override void _Input(InputEvent ev)
{ {
if (!IsEditing()) return; if (GetTerrain() is not Terrain terrain) return;
if (_isSelecting && ev is InputEventMouseButton { ButtonIndex: MouseButton.Left, Pressed: false }) { if (Engine.IsEditorHint()) {
// Make sure to transform the input event to the 3D scene's viewport.
var viewport = EditorInterface.Singleton.GetEditorViewport3D();
var container = viewport.GetParent<SubViewportContainer>();
ev = ev.XformedBy(container.GetGlobalTransform().AffineInverse());
if (ev is InputEventMouse m) m.GlobalPosition = m.Position;
}
if (_isSelecting && (ev is InputEventMouseButton { ButtonIndex: MouseButton.Left, Pressed: false })) {
_isSelecting = false; _isSelecting = false;
GetViewport().SetInputAsHandled(); GetViewport().SetInputAsHandled();
return;
} }
if ((ev is InputEventMouseButton { ButtonIndex: var wheel, Pressed: var pressed, ShiftPressed: true }) if ((ev is InputEventMouseButton { ButtonIndex: var wheel, Pressed: var pressed, ShiftPressed: true })
@ -37,13 +56,13 @@ public partial class Terrain
var innerPos = selection.GetTileFor(innerCorner); var innerPos = selection.GetTileFor(innerCorner);
var outerPos = innerPos.GetNeighbor(innerCorner); var outerPos = innerPos.GetNeighbor(innerCorner);
var outerTile = GetTile(outerPos); var outerTile = terrain.GetTile(outerPos);
var innerHeight = GetTile(innerPos).Height[innerCorner]; var innerHeight = terrain.GetTile(innerPos).Height[innerCorner];
var outerHeight = outerTile.Height[outerCorner]; var outerHeight = outerTile.Height[outerCorner];
if (IsEqualApprox(outerHeight, innerHeight)) { if (IsEqualApprox(outerHeight, innerHeight)) {
outerTile.Height[outerCorner] = innerHeight + amount; outerTile.Height[outerCorner] = innerHeight + amount;
SetTile(outerPos, outerTile); terrain.SetTile(outerPos, outerTile);
} }
} }
@ -52,8 +71,8 @@ public partial class Terrain
foreach (var innerPos in selection.GetTilesFor(side)) { foreach (var innerPos in selection.GetTilesFor(side)) {
var outerPos = innerPos.GetNeighbor(side); var outerPos = innerPos.GetNeighbor(side);
var innerTile = GetTile(innerPos); var innerTile = terrain.GetTile(innerPos);
var outerTile = GetTile(outerPos); var outerTile = terrain.GetTile(outerPos);
var (innerCorner1, innerCorner2) = side.GetCorners(); var (innerCorner1, innerCorner2) = side.GetCorners();
var (outerCorner1, outerCorner2) = side.GetOpposite().GetCorners(); var (outerCorner1, outerCorner2) = side.GetOpposite().GetCorners();
@ -69,19 +88,20 @@ public partial class Terrain
changed = true; changed = true;
} }
} }
if (changed) SetTile(outerPos, outerTile); if (changed) terrain.SetTile(outerPos, outerTile);
} }
} }
// Raise selected tiles themselves. // Raise selected tiles themselves.
foreach (var pos in selection.GetAllTiles()) { foreach (var pos in selection.GetAllTiles()) {
var tile = GetTile(pos); var tile = terrain.GetTile(pos);
tile.Height.Adjust(amount); tile.Height.Adjust(amount);
SetTile(pos, tile); terrain.SetTile(pos, tile);
} }
UpdateMeshAndShape(); terrain.UpdateMeshAndShape();
NotifyPropertyListChanged(); terrain.NotifyPropertyListChanged();
return;
} }
if ((ev is InputEventMouseButton { ButtonIndex: var wheel2, Pressed: var pressed2, CtrlPressed: true }) if ((ev is InputEventMouseButton { ButtonIndex: var wheel2, Pressed: var pressed2, CtrlPressed: true })
@ -94,30 +114,59 @@ public partial class Terrain
var selection = TileRegion.From(_selection.Value); var selection = TileRegion.From(_selection.Value);
foreach (var pos in selection.GetAllTiles()) { foreach (var pos in selection.GetAllTiles()) {
var tile = GetTile(pos); var tile = terrain.GetTile(pos);
tile.TexturePrimary = PosMod(tile.TexturePrimary + amount, 4); tile.TexturePrimary = PosMod(tile.TexturePrimary + amount, 4);
SetTile(pos, tile); terrain.SetTile(pos, tile);
} }
UpdateMeshAndShape(); terrain.UpdateMeshAndShape();
NotifyPropertyListChanged(); terrain.NotifyPropertyListChanged();
return;
} }
if (ev is InputEventMouseMotion) if (ev is InputEventMouseMotion)
_unhandledMotion = true; _unhandledMotion = true;
if ((ev is InputEventMouse mouse) && (!Engine.IsEditorHint() ||
EditorInterface.Singleton.GetEditorViewport3D()
.GetVisibleRect().HasPoint(mouse.Position)))
OnInputRayCastTerrain(terrain, mouse);
} }
public override void _InputEvent(Camera3D camera, InputEvent ev, Vector3 position, Vector3 normal, int shapeIdx) void OnInputRayCastTerrain(Terrain terrain, InputEventMouse ev)
{ {
if (!IsEditing()) return; // Ray is cast from the editor camera's view.
var camera = EditorInterface.Singleton.GetEditorViewport3D().GetCamera3D();
var from = camera.ProjectRayOrigin(ev.Position);
var to = from + camera.ProjectRayNormal(ev.Position) * camera.Far;
// Actual collision is done in the edited scene though.
var root = (Node3D)EditorInterface.Singleton.GetEditedSceneRoot();
var space = root.GetWorld3D().DirectSpaceState;
var query = PhysicsRayQueryParameters3D.Create(from, to);
var result = space.IntersectRay(query);
var collider = (GodotObject)result.GetValueOrDefault("collider");
if (collider == terrain) {
var pos = (Vector3)result["position"];
// var normal = (Vector3)result["normal"];
// var shape = (int)result["shape"];
OnTerrainInput(terrain, ev, pos);
}
}
void OnTerrainInput(Terrain terrain, InputEvent ev, Vector3 position)
{
if (terrain == null) return;
var localPos = ToLocal(position); var localPos = terrain.ToLocal(position);
var tilePos = ToTilePos(localPos); var tilePos = terrain.ToTilePos(localPos);
if (ev is InputEventMouseButton { ButtonIndex: MouseButton.Left, Pressed: true }) { if (ev is InputEventMouseButton { ButtonIndex: MouseButton.Left, Pressed: true }) {
_selection = new(tilePos, tilePos); _selection = new(tilePos, tilePos);
_isSelecting = true; _isSelecting = true;
GetViewport().SetInputAsHandled(); GetViewport().SetInputAsHandled();
return;
} }
if (ev is InputEventMouseMotion) { if (ev is InputEventMouseMotion) {
@ -129,17 +178,18 @@ public partial class Terrain
public override void _Process(double delta) public override void _Process(double delta)
{ {
if (!IsEditing()) { if (GetTerrain() is not Terrain terrain) {
_tileHover = null; _tileHover = null;
_selection = null; _selection = null;
_isSelecting = false; _isSelecting = false;
terrain = null;
} }
if (_unhandledMotion) if (_unhandledMotion)
_tileHover = null; _tileHover = null;
if ((_tileHover != null) || (_selection != null)) { if ((_tileHover != null) || (_selection != null)) {
var mesh = GetOrCreateMesh("EditToolMesh"); var mesh = terrain.GetOrCreateMesh("EditToolMesh");
mesh.ClearSurfaces(); mesh.ClearSurfaces();
mesh.SurfaceBegin(Mesh.PrimitiveType.Lines); mesh.SurfaceBegin(Mesh.PrimitiveType.Lines);
@ -157,7 +207,7 @@ public partial class Terrain
} }
if (_tileHover is TilePos hover) { if (_tileHover is TilePos hover) {
var corners = GetTileCornerPositions(hover); var corners = terrain.GetTileCornerPositions(hover);
var margin = 0.1f; var margin = 0.1f;
mesh.SurfaceSetColor(Colors.Black); mesh.SurfaceSetColor(Colors.Black);
AddQuad(corners.TopLeft + new Vector3(-margin, 0, -margin), AddQuad(corners.TopLeft + new Vector3(-margin, 0, -margin),
@ -169,7 +219,7 @@ public partial class Terrain
mesh.SurfaceSetColor(Colors.Blue); mesh.SurfaceSetColor(Colors.Blue);
if (_selection is (TilePos, TilePos) selection) if (_selection is (TilePos, TilePos) selection)
foreach (var pos in TileRegion.From(selection).GetAllTiles()) { foreach (var pos in TileRegion.From(selection).GetAllTiles()) {
var corners = GetTileCornerPositions(pos); var corners = terrain.GetTileCornerPositions(pos);
AddQuad(corners.TopLeft, corners.TopRight, corners.BottomLeft, corners.BottomRight); AddQuad(corners.TopLeft, corners.TopRight, corners.BottomLeft, corners.BottomRight);
} }
@ -182,15 +232,8 @@ public partial class Terrain
} }
} }
bool IsEditing() public Terrain GetTerrain() => EditorInterface.Singleton.GetSelection()
{ .GetSelectedNodes().OfType<Terrain>().FirstOrDefault();
if (Engine.IsEditorHint()) {
var selection = EditorInterface.Singleton.GetSelection();
return selection.GetSelectedNodes().Contains(this);
} else {
return false;
}
}
readonly record struct TileRegion(int Left, int Top, int Right, int Bottom) readonly record struct TileRegion(int Left, int Top, int Right, int Bottom)

@ -0,0 +1,101 @@
[Tool]
public partial class TerrainEditingControls
: VBoxContainer
{
public (ToolMode , Button)[] ToolModeButtons { get; private set; }
public (ToolShape, Button)[] ToolShapeButtons { get; private set; }
public Button[] PaintTextureButtons { get; private set; }
public Slider DrawSizeSlider { get; private set; }
ToolMode _toolMode;
ToolShape _toolShape;
int _texture;
public ToolMode ToolMode { get => _toolMode ; set => SetToolMode (value); }
public ToolShape ToolShape { get => _toolShape; set => SetToolShape(value); }
public int Texture { get => _texture ; set => SetTexture (value); }
public int DrawSize {
get => RoundToInt(-DrawSizeSlider?.Value ?? 1);
set => DrawSizeSlider.Value = -value;
}
public override void _Ready()
{
ToolModeButtons = [
(ToolMode.Height, GetNode<Button>("Height")),
(ToolMode.Paint , GetNode<Button>("Paint" )),
];
ToolShapeButtons = [
(ToolShape.Corner, GetNode<Button>("Corner")),
(ToolShape.Circle, GetNode<Button>("Circle")),
(ToolShape.Square, GetNode<Button>("Square")),
];
PaintTextureButtons = [
GetNode<Button>("Grass"),
GetNode<Button>("Dirt"),
GetNode<Button>("Rock"),
GetNode<Button>("Sand"),
];
foreach (var (mode, button) in ToolModeButtons)
button.Pressed += () => SetToolMode(mode);
foreach (var (shape, button) in ToolShapeButtons)
button.Pressed += () => SetToolShape(shape);
foreach (var (i, button) in PaintTextureButtons.Select((b, i) => (i, b)))
button.Pressed += () => SetTexture(i + 1);
var drawSizeLabel = GetNode<Label>("SizeLabel");
DrawSizeSlider = GetNode<Slider>("SizeSlider");
DrawSizeSlider.ValueChanged += (_) => drawSizeLabel.Text = $"{DrawSize}";
SetToolMode(ToolMode.Height);
SetToolShape(ToolShape.Circle);
SetTexture(1);
OnEditingReady();
}
void SetToolMode(ToolMode value)
{
foreach (var (mode, button) in ToolModeButtons)
button.Flat = button.ButtonPressed = value != mode;
foreach (var button in PaintTextureButtons)
button.Disabled = value != ToolMode.Paint;
_toolMode = value;
}
void SetToolShape(ToolShape value)
{
foreach (var (shape, button) in ToolShapeButtons)
button.Flat = button.ButtonPressed = value != shape;
DrawSizeSlider.Editable = value != ToolShape.Corner;
_toolShape = value;
}
void SetTexture(int value)
{
if ((value < 1) || (value > PaintTextureButtons.Length))
throw new ArgumentOutOfRangeException(nameof(value));
foreach (var (i, button) in PaintTextureButtons.Select((b, i) => (i, b)))
button.Flat = button.ButtonPressed = value != i + 1;
_texture = value;
}
}
public enum ToolMode
{
Height,
Paint,
}
public enum ToolShape
{
Corner,
Circle,
Square,
}

@ -0,0 +1,120 @@
[gd_scene load_steps=15 format=3 uid="uid://bmljchm3fj42"]
[ext_resource type="Script" path="res://addons/terrain-editing/TerrainEditingControls.cs" id="1_fklx3"]
[ext_resource type="Texture2D" uid="uid://btl3jsqeldix2" path="res://addons/terrain-editing/icons/corner.png" id="1_w5qr7"]
[ext_resource type="Texture2D" uid="uid://dqbtbf8pe05qv" path="res://addons/terrain-editing/icons/height.png" id="2_hrmm4"]
[ext_resource type="Texture2D" uid="uid://2u1ldmh0osbx" path="res://addons/terrain-editing/icons/circle.png" id="2_yvc34"]
[ext_resource type="Texture2D" uid="uid://btdpyu4n3pgkx" path="res://addons/terrain-editing/icons/paint.png" id="3_5x55r"]
[ext_resource type="Texture2D" uid="uid://btjd1704xtdjv" path="res://addons/terrain-editing/icons/square.png" id="3_aaaoe"]
[ext_resource type="Image" uid="uid://b0jp1dyxugbr7" path="res://assets/textures/terrain/grass.png" id="Image_d41co"]
[ext_resource type="Image" uid="uid://bpo7mkr6sctqr" path="res://assets/textures/terrain/dirt.png" id="Image_y3rra"]
[ext_resource type="Image" uid="uid://dqyqg6yt7yk3k" path="res://assets/textures/terrain/rock.png" id="Image_x8cdn"]
[ext_resource type="Image" uid="uid://bkwjxg6g2itag" path="res://assets/textures/terrain/sand.png" id="Image_sb66e"]
[sub_resource type="ImageTexture" id="ImageTexture_btyvd"]
image = ExtResource("Image_d41co")
[sub_resource type="ImageTexture" id="ImageTexture_wk5vj"]
image = ExtResource("Image_y3rra")
[sub_resource type="ImageTexture" id="ImageTexture_mfste"]
image = ExtResource("Image_x8cdn")
[sub_resource type="ImageTexture" id="ImageTexture_2hv1j"]
image = ExtResource("Image_sb66e")
[node name="TerrainEditingControls" type="VBoxContainer"]
anchors_preset = 11
anchor_left = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = -20.0
grow_horizontal = 0
grow_vertical = 2
script = ExtResource("1_fklx3")
[node name="Height" type="Button" parent="."]
layout_mode = 2
toggle_mode = true
icon = ExtResource("2_hrmm4")
[node name="Paint" type="Button" parent="."]
layout_mode = 2
toggle_mode = true
button_pressed = true
icon = ExtResource("3_5x55r")
flat = true
[node name="HSeparator" type="HSeparator" parent="."]
layout_mode = 2
[node name="Corner" type="Button" parent="."]
layout_mode = 2
toggle_mode = true
button_pressed = true
icon = ExtResource("1_w5qr7")
flat = true
[node name="Circle" type="Button" parent="."]
layout_mode = 2
toggle_mode = true
icon = ExtResource("2_yvc34")
[node name="Square" type="Button" parent="."]
layout_mode = 2
toggle_mode = true
button_pressed = true
icon = ExtResource("3_aaaoe")
flat = true
[node name="SizeLabel" type="Label" parent="."]
layout_mode = 2
text = "1
"
horizontal_alignment = 1
[node name="SizeSlider" type="VSlider" parent="."]
custom_minimum_size = Vector2(0, 80)
layout_mode = 2
size_flags_horizontal = 1
min_value = -16.0
max_value = -1.0
value = -1.0
[node name="HSeparator2" type="HSeparator" parent="."]
layout_mode = 2
[node name="Grass" type="Button" parent="."]
layout_mode = 2
theme_override_constants/icon_max_width = 16
disabled = true
toggle_mode = true
icon = SubResource("ImageTexture_btyvd")
[node name="Dirt" type="Button" parent="."]
layout_mode = 2
theme_override_constants/icon_max_width = 16
disabled = true
toggle_mode = true
button_pressed = true
icon = SubResource("ImageTexture_wk5vj")
flat = true
[node name="Rock" type="Button" parent="."]
layout_mode = 2
theme_override_constants/icon_max_width = 16
disabled = true
toggle_mode = true
button_pressed = true
icon = SubResource("ImageTexture_mfste")
flat = true
[node name="Sand" type="Button" parent="."]
layout_mode = 2
theme_override_constants/icon_max_width = 16
disabled = true
toggle_mode = true
button_pressed = true
icon = SubResource("ImageTexture_2hv1j")
flat = true

@ -0,0 +1,34 @@
#if TOOLS
[Tool]
public partial class TerrainEditingPlugin
: EditorPlugin
{
TerrainEditingControls _controls;
public override bool _Handles(GodotObject obj)
=> obj is Terrain;
public override void _EnterTree()
{
var scene = GD.Load<PackedScene>("res://addons/terrain-editing/TerrainEditingControls.tscn");
_controls = scene.Instantiate<TerrainEditingControls>();
}
public override void _ExitTree()
{
if (_controls == null) return;
_controls.QueueFree();
_controls = null;
}
public override void _MakeVisible(bool visible)
{
var container = CustomControlContainer.SpatialEditorSideRight;
if (visible)
AddControlToContainer(container, _controls);
else if (_controls != null)
RemoveControlFromContainer(container, _controls);
}
}
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://2u1ldmh0osbx"
path="res://.godot/imported/circle.png-7ef635c7ab3f0bfc0f9202fc8a8f1dd4.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/terrain-editing/icons/circle.png"
dest_files=["res://.godot/imported/circle.png-7ef635c7ab3f0bfc0f9202fc8a8f1dd4.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://btl3jsqeldix2"
path="res://.godot/imported/corner.png-c9f5b33637b5a9c1ee2a2ae62a163672.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/terrain-editing/icons/corner.png"
dest_files=["res://.godot/imported/corner.png-c9f5b33637b5a9c1ee2a2ae62a163672.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dqbtbf8pe05qv"
path="res://.godot/imported/height.png-a11588cb8bcd4e93c189a14ad0809338.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/terrain-editing/icons/height.png"
dest_files=["res://.godot/imported/height.png-a11588cb8bcd4e93c189a14ad0809338.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 B

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://btdpyu4n3pgkx"
path="res://.godot/imported/paint.png-7f2ce8187160ce862842aed3c583971f.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/terrain-editing/icons/paint.png"
dest_files=["res://.godot/imported/paint.png-7f2ce8187160ce862842aed3c583971f.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://btjd1704xtdjv"
path="res://.godot/imported/square.png-ee9f4ef6bc1f78130e7829ecfc4538ca.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/terrain-editing/icons/square.png"
dest_files=["res://.godot/imported/square.png-ee9f4ef6bc1f78130e7829ecfc4538ca.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

@ -0,0 +1,7 @@
[plugin]
name="Terrain Editing"
description=""
author="copygirl"
version="1.0.0"
script="TerrainEditingPlugin.cs"

@ -21,7 +21,7 @@ project/assembly_name="SlimeDream"
[editor_plugins] [editor_plugins]
enabled=PackedStringArray("res://addons/receive-input-in-editor/plugin.cfg") enabled=PackedStringArray("res://addons/terrain-editing/plugin.cfg")
[input] [input]

@ -1,3 +1,4 @@
[Tool]
public partial class Terrain public partial class Terrain
: StaticBody3D : StaticBody3D
{ {
@ -12,18 +13,8 @@ public partial class Terrain
[Export] public Godot.Collections.Dictionary<Vector2I, Variant> Tiles { get; set; } [Export] public Godot.Collections.Dictionary<Vector2I, Variant> Tiles { get; set; }
Material _editToolMaterial;
public override void _Ready() public override void _Ready()
{ => UpdateMeshAndShape();
_editToolMaterial = new StandardMaterial3D {
VertexColorUseAsAlbedo = true,
ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded,
DepthDrawMode = BaseMaterial3D.DepthDrawModeEnum.Disabled,
NoDepthTest = true,
};
UpdateMeshAndShape();
}
public Tile GetTile(TilePos pos) public Tile GetTile(TilePos pos)
@ -48,7 +39,7 @@ public partial class Terrain
RoundToInt(localPos.Z / TileSize + Size.Y / 2.0f)); RoundToInt(localPos.Z / TileSize + Size.Y / 2.0f));
void UpdateMeshAndShape() public void UpdateMeshAndShape()
{ {
var mesh = GetOrCreateMesh("MeshInstance"); var mesh = GetOrCreateMesh("MeshInstance");
var shape = GetOrCreateShape("CollisionShape"); var shape = GetOrCreateShape("CollisionShape");
@ -142,7 +133,7 @@ public partial class Terrain
} }
ImmediateMesh GetOrCreateMesh(string name) public ImmediateMesh GetOrCreateMesh(string name)
{ {
var meshInstance = (MeshInstance3D)GetNodeOrNull(name); var meshInstance = (MeshInstance3D)GetNodeOrNull(name);
if (meshInstance == null) { if (meshInstance == null) {
@ -153,7 +144,7 @@ public partial class Terrain
return (ImmediateMesh)meshInstance.Mesh; return (ImmediateMesh)meshInstance.Mesh;
} }
ConcavePolygonShape3D GetOrCreateShape(string name) public ConcavePolygonShape3D GetOrCreateShape(string name)
{ {
var collisionShape = (CollisionShape3D)GetNodeOrNull(name); var collisionShape = (CollisionShape3D)GetNodeOrNull(name);
if (collisionShape == null) { if (collisionShape == null) {

Loading…
Cancel
Save