New UI insfrastructure, change texture ID tool

This commit is contained in:
Cory Petkovsek 2025-05-23 17:23:27 +07:00
parent 37fb986cad
commit ed040156ad
6 changed files with 450 additions and 0 deletions

View File

@ -0,0 +1,92 @@
# Copyright © 2025 Cory Petkovsek, Roope Palmroos, and Contributors.
# This is the start of the new UI for Terrain3D with all tools integrated.
@tool
extends Control
var terrain: Terrain3D
var show_pending: bool = true
func _ready() -> void:
%Close.pressed.connect(_on_close)
%TextureStartBtn.pressed.connect(_on_texture_start)
if not show_pending:
%Baking.queue_free()
%TexturePacking.queue_free()
%Import.queue_free()
%Export.queue_free()
func _on_close() -> void:
queue_free()
func _on_texture_start() -> void:
var process_arr: Array
var msg: String
%TextureStatus.visible = true
%TextureStatus.add_theme_color_override("font_color", Color.WHITE)
if %SrcID1.value != %DestID1.value:
process_arr.push_back([%SrcID1.value, %DestID1.value] as Array[int])
msg += "Changing texture ID %d to %d.\n" % [ %SrcID1.value, %DestID1.value ]
if %SrcID2.value != %DestID2.value:
process_arr.push_back([%SrcID2.value, %DestID2.value] as Array[int])
msg += "Changing texture ID %d to %d.\n" % [ %SrcID2.value, %DestID2.value ]
if %SrcID3.value != %DestID3.value:
process_arr.push_back([%SrcID3.value, %DestID3.value] as Array[int])
msg += "Changing texture ID %d to %d.\n" % [ %SrcID3.value, %DestID3.value ]
if process_arr.is_empty():
msg = "No changes specified. Nothing to do."
print(msg)
%TextureStatus.text = msg + "\n"
%TextureStatus.add_theme_color_override("font_color", Color.RED)
return
msg += "Starting..."
print(msg)
%TextureStatus.text = msg + "\n"
await RenderingServer.frame_post_draw
# Mask to clear base_id and over_id bits
var clear_mask: int = ~(0x1F << 27 | 0x1F << 22)
var util := Terrain3DUtil.new()
for region: Terrain3DRegion in terrain.data.get_regions_all().values():
var map: Image = region.control_map
var modified: bool = false
for x: int in map.get_width():
for y: int in map.get_height():
var control: int = util.as_uint(map.get_pixel(x, y).r)
var base_id: int = util.get_base(control)
var over_id: int = util.get_overlay(control)
for tuple: Array[int] in process_arr:
if base_id == tuple[0]:
base_id = tuple[1]
modified = true
if over_id == tuple[0]:
over_id = tuple[1]
modified = true
if modified:
# Preserve bits 1-22, clear bits 23-32, set new base_id and over_id
control = (control & clear_mask) | ((base_id & 0x1F) << 27) | ((over_id & 0x1F) << 22)
map.set_pixel(x, y, Color(util.as_float(control), 0., 0., 1.))
msg = "Processed region: %.0v" % [ region.get_location() ]
if modified:
region.set_modified(true)
msg += ", modified"
%TextureStatus.text += msg + "\n"
print(msg)
%Scroll.scroll_vertical += 50
await RenderingServer.frame_post_draw
terrain.data.update_maps(Terrain3DRegion.TYPE_CONTROL)
%TextureStatus.text += "Finished."
print("Finished.")
%Scroll.scroll_vertical += 50
%TextureStatus.add_theme_color_override("font_color", Color.GREEN)

View File

@ -0,0 +1 @@
uid://pkievxeb101w

View File

@ -0,0 +1,212 @@
[gd_scene load_steps=4 format=3 uid="uid://b5wxp6lton0et"]
[ext_resource type="Theme" uid="uid://bb2773v41h8r" path="res://addons/terrain_3d/menu/terrain3d_theme.tres" id="1_xbcvl"]
[ext_resource type="Script" uid="uid://pkievxeb101w" path="res://addons/terrain_3d/menu/menu_ui.gd" id="2_xbcvl"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_uxh0t"]
[node name="MenuUI" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("1_xbcvl")
script = ExtResource("2_xbcvl")
[node name="Scroll" type="ScrollContainer" parent="."]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="Tabs" type="TabContainer" parent="Scroll"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
current_tab = 0
clip_tabs = false
[node name="TextureIDs" type="MarginContainer" parent="Scroll/Tabs"]
unique_name_in_owner = true
layout_mode = 2
metadata/_tab_index = 0
[node name="VBox" type="VBoxContainer" parent="Scroll/Tabs/TextureIDs"]
layout_mode = 2
size_flags_horizontal = 0
[node name="Header" type="Label" parent="Scroll/Tabs/TextureIDs/VBox"]
layout_mode = 2
theme_override_font_sizes/font_size = 36
text = "Change Texture IDs"
[node name="SubHeader" type="Label" parent="Scroll/Tabs/TextureIDs/VBox"]
visible = false
layout_mode = 2
text = "This tool changes texture data on the currently selected terrain."
[node name="HSeparator" type="HSeparator" parent="Scroll/Tabs/TextureIDs/VBox"]
layout_mode = 2
[node name="Instructions" type="Label" parent="Scroll/Tabs/TextureIDs/VBox"]
layout_mode = 2
text = "This tool will look at every pixel in every region and all Base or Overlay IDs that match the source ID will be changed to the destination ID.
There is no undo, however you can reload your project without saving. Data is not written until you save.
1. Specify the source and destination texture IDs 0-31 from your Asset list.
2. Enter up to 2 more pairs if desired. Swap IDs in 3 steps with any unused ID (use 32 if all textures are used).
e.g. To swap 0 and 1, set 0 -> 32, 1 -> 0, 32 -> 1.
3. Click Process.
4. Close this panel and Save."
[node name="HSeparator2" type="HSeparator" parent="Scroll/Tabs/TextureIDs/VBox"]
layout_mode = 2
theme_override_constants/separation = 30
theme_override_styles/separator = SubResource("StyleBoxEmpty_uxh0t")
[node name="HBox1" type="HBoxContainer" parent="Scroll/Tabs/TextureIDs/VBox"]
layout_mode = 2
[node name="SrcLbl" type="Label" parent="Scroll/Tabs/TextureIDs/VBox/HBox1"]
layout_mode = 2
text = "1. Source ID: "
[node name="SrcID1" type="SpinBox" parent="Scroll/Tabs/TextureIDs/VBox/HBox1"]
unique_name_in_owner = true
layout_mode = 2
max_value = 32.0
[node name="VSeparator" type="VSeparator" parent="Scroll/Tabs/TextureIDs/VBox/HBox1"]
layout_mode = 2
theme_override_constants/separation = 20
theme_override_styles/separator = SubResource("StyleBoxEmpty_uxh0t")
[node name="DestLbl" type="Label" parent="Scroll/Tabs/TextureIDs/VBox/HBox1"]
layout_mode = 2
text = "Destination ID: "
[node name="DestID1" type="SpinBox" parent="Scroll/Tabs/TextureIDs/VBox/HBox1"]
unique_name_in_owner = true
layout_mode = 2
max_value = 32.0
[node name="HBox2" type="HBoxContainer" parent="Scroll/Tabs/TextureIDs/VBox"]
layout_mode = 2
[node name="SrcLbl" type="Label" parent="Scroll/Tabs/TextureIDs/VBox/HBox2"]
layout_mode = 2
text = "2. Source ID: "
[node name="SrcID2" type="SpinBox" parent="Scroll/Tabs/TextureIDs/VBox/HBox2"]
unique_name_in_owner = true
layout_mode = 2
max_value = 32.0
[node name="VSeparator" type="VSeparator" parent="Scroll/Tabs/TextureIDs/VBox/HBox2"]
layout_mode = 2
theme_override_constants/separation = 20
theme_override_styles/separator = SubResource("StyleBoxEmpty_uxh0t")
[node name="DestLbl" type="Label" parent="Scroll/Tabs/TextureIDs/VBox/HBox2"]
layout_mode = 2
text = "Destination ID: "
[node name="DestID2" type="SpinBox" parent="Scroll/Tabs/TextureIDs/VBox/HBox2"]
unique_name_in_owner = true
layout_mode = 2
max_value = 32.0
[node name="HBox3" type="HBoxContainer" parent="Scroll/Tabs/TextureIDs/VBox"]
layout_mode = 2
[node name="SrcLbl" type="Label" parent="Scroll/Tabs/TextureIDs/VBox/HBox3"]
layout_mode = 2
text = "3. Source ID: "
[node name="SrcID3" type="SpinBox" parent="Scroll/Tabs/TextureIDs/VBox/HBox3"]
unique_name_in_owner = true
layout_mode = 2
max_value = 32.0
[node name="VSeparator" type="VSeparator" parent="Scroll/Tabs/TextureIDs/VBox/HBox3"]
layout_mode = 2
theme_override_constants/separation = 20
theme_override_styles/separator = SubResource("StyleBoxEmpty_uxh0t")
[node name="DestLbl" type="Label" parent="Scroll/Tabs/TextureIDs/VBox/HBox3"]
layout_mode = 2
text = "Destination ID: "
[node name="DestID3" type="SpinBox" parent="Scroll/Tabs/TextureIDs/VBox/HBox3"]
unique_name_in_owner = true
layout_mode = 2
max_value = 32.0
[node name="HSeparator3" type="HSeparator" parent="Scroll/Tabs/TextureIDs/VBox"]
layout_mode = 2
theme_override_constants/separation = 30
theme_override_styles/separator = SubResource("StyleBoxEmpty_uxh0t")
[node name="TextureStartBtn" type="Button" parent="Scroll/Tabs/TextureIDs/VBox"]
unique_name_in_owner = true
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
size_flags_horizontal = 0
text = "Process"
[node name="HSeparator4" type="HSeparator" parent="Scroll/Tabs/TextureIDs/VBox"]
layout_mode = 2
theme_override_constants/separation = 30
theme_override_styles/separator = SubResource("StyleBoxEmpty_uxh0t")
[node name="TextureStatus" type="Label" parent="Scroll/Tabs/TextureIDs/VBox"]
unique_name_in_owner = true
visible = false
layout_mode = 2
text = "Finished"
[node name="Baking" type="MarginContainer" parent="Scroll/Tabs"]
unique_name_in_owner = true
visible = false
layout_mode = 2
metadata/_tab_index = 1
[node name="Texture Packing" type="MarginContainer" parent="Scroll/Tabs"]
unique_name_in_owner = true
visible = false
layout_mode = 2
metadata/_tab_index = 2
[node name="Import" type="MarginContainer" parent="Scroll/Tabs"]
unique_name_in_owner = true
visible = false
layout_mode = 2
metadata/_tab_index = 3
[node name="Export" type="MarginContainer" parent="Scroll/Tabs"]
unique_name_in_owner = true
visible = false
layout_mode = 2
metadata/_tab_index = 4
[node name="Margin" type="MarginContainer" parent="."]
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -50.0
offset_bottom = 31.0
grow_horizontal = 0
theme_override_constants/margin_left = 0
theme_override_constants/margin_bottom = 0
[node name="Close" type="Button" parent="Margin"]
unique_name_in_owner = true
layout_mode = 2
text = "Close"

View File

@ -0,0 +1,133 @@
[gd_resource type="Theme" load_steps=10 format=3 uid="uid://bb2773v41h8r"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_0qu63"]
content_margin_left = 4.0
content_margin_top = 4.0
content_margin_right = 4.0
content_margin_bottom = 4.0
bg_color = Color(0.101961, 0.101961, 0.101961, 1)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
corner_detail = 5
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3l01n"]
content_margin_left = 4.0
content_margin_top = 4.0
content_margin_right = 4.0
content_margin_bottom = 4.0
bg_color = Color(1, 1, 1, 0.75)
draw_center = false
border_width_left = 2
border_width_top = 2
border_width_right = 2
border_width_bottom = 2
border_color = Color(0.52, 0.52, 0.52, 1)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
corner_detail = 5
expand_margin_left = 2.0
expand_margin_top = 2.0
expand_margin_right = 2.0
expand_margin_bottom = 2.0
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_1v4h1"]
content_margin_left = 4.0
content_margin_top = 4.0
content_margin_right = 4.0
content_margin_bottom = 4.0
bg_color = Color(0.223529, 0.223529, 0.223529, 1)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
corner_detail = 5
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_kl51k"]
content_margin_left = 4.0
content_margin_top = 4.0
content_margin_right = 4.0
content_margin_bottom = 4.0
bg_color = Color(0.101961, 0.101961, 0.101961, 1)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
corner_detail = 5
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3c1e3"]
content_margin_left = 4.0
content_margin_top = 4.0
content_margin_right = 4.0
content_margin_bottom = 4.0
bg_color = Color(0, 0, 0, 1)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
corner_detail = 5
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_c01fe"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_wm6q5"]
bg_color = Color(0.145098, 0.145098, 0.145098, 1)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_gxfll"]
bg_color = Color(0.145098, 0.145098, 0.145098, 1)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_0jsb3"]
content_margin_left = 0.0
content_margin_top = 0.0
content_margin_right = 0.0
content_margin_bottom = 0.0
bg_color = Color(0.1, 0.1, 0.1, 0.6)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
corner_detail = 5
[resource]
Button/colors/font_color = Color(0.875, 0.875, 0.875, 1)
Button/colors/font_disabled_color = Color(0.875, 0.875, 0.875, 0.5)
Button/colors/font_focus_color = Color(0.95, 0.95, 0.95, 1)
Button/colors/font_hover_color = Color(0.95, 0.95, 0.95, 1)
Button/colors/font_hover_pressed_color = Color(1, 1, 1, 1)
Button/colors/font_outline_color = Color(0, 0, 0, 1)
Button/colors/font_pressed_color = Color(1, 1, 1, 1)
Button/colors/icon_disabled_color = Color(1, 1, 1, 0.4)
Button/colors/icon_focus_color = Color(1, 1, 1, 1)
Button/colors/icon_hover_color = Color(1, 1, 1, 1)
Button/colors/icon_hover_pressed_color = Color(1, 1, 1, 1)
Button/colors/icon_normal_color = Color(1, 1, 1, 1)
Button/colors/icon_pressed_color = Color(1, 1, 1, 1)
Button/constants/align_to_largest_stylebox = 0
Button/constants/h_separation = 4
Button/constants/icon_max_width = 0
Button/constants/outline_size = 0
Button/font_sizes/font_size = 16
Button/styles/disabled = SubResource("StyleBoxFlat_0qu63")
Button/styles/focus = SubResource("StyleBoxFlat_3l01n")
Button/styles/hover = SubResource("StyleBoxFlat_1v4h1")
Button/styles/normal = SubResource("StyleBoxFlat_kl51k")
Button/styles/pressed = SubResource("StyleBoxFlat_3c1e3")
Label/colors/font_color = Color(1, 1, 1, 1)
Label/colors/font_outline_color = Color(0, 0, 0, 1)
Label/colors/font_shadow_color = Color(0, 0, 0, 0)
Label/constants/line_spacing = 3
Label/constants/outline_size = 0
Label/constants/shadow_offset_x = 1
Label/constants/shadow_offset_y = 1
Label/constants/shadow_outline_size = 1
Label/font_sizes/font_size = 16
Label/styles/normal = SubResource("StyleBoxEmpty_c01fe")
MarginContainer/constants/margin_bottom = 20
MarginContainer/constants/margin_left = 20
MarginContainer/constants/margin_right = 20
MarginContainer/constants/margin_top = 20
Panel/styles/panel = SubResource("StyleBoxFlat_wm6q5")
ScrollContainer/styles/panel = SubResource("StyleBoxFlat_gxfll")
TabContainer/styles/panel = SubResource("StyleBoxFlat_0jsb3")

View File

@ -6,6 +6,7 @@ extends HBoxContainer
const DirectoryWizard: Script = preload("res://addons/terrain_3d/menu/directory_setup.gd")
const Packer: Script = preload("res://addons/terrain_3d/menu/channel_packer.gd")
const Baker: Script = preload("res://addons/terrain_3d/menu/baker.gd")
const NewUIScn: String = "res://addons/terrain_3d/menu/menu_ui.tscn"
var plugin: EditorPlugin
var menu_button: MenuButton = MenuButton.new()
@ -17,6 +18,7 @@ var baker: Baker = Baker.new()
enum {
MENU_DIRECTORY_SETUP,
MENU_PACK_TEXTURES,
MENU_NEW_UI,
MENU_SEPARATOR,
MENU_BAKE_ARRAY_MESH,
MENU_BAKE_OCCLUDER,
@ -36,6 +38,7 @@ func _enter_tree() -> void:
menu_button.text = "Terrain3D"
menu_button.get_popup().add_item("Directory Setup...", MENU_DIRECTORY_SETUP)
menu_button.get_popup().add_item("Pack Textures...", MENU_PACK_TEXTURES)
menu_button.get_popup().add_item("Change Texture IDs...", MENU_NEW_UI)
menu_button.get_popup().add_separator("", MENU_SEPARATOR)
menu_button.get_popup().add_item("Bake ArrayMesh...", MENU_BAKE_ARRAY_MESH)
menu_button.get_popup().add_item("Bake Occluder3D...", MENU_BAKE_OCCLUDER)
@ -54,6 +57,10 @@ func _on_menu_pressed(p_id: int) -> void:
directory_setup.directory_setup_popup()
MENU_PACK_TEXTURES:
packer.pack_textures_popup()
MENU_NEW_UI:
var newui: Control = load(NewUIScn).instantiate()
newui.terrain = plugin.terrain
plugin.godot_editor_vp_container.add_child(newui)
MENU_BAKE_ARRAY_MESH:
baker.bake_mesh_popup()
MENU_BAKE_OCCLUDER:
@ -67,6 +74,7 @@ func _on_menu_pressed(p_id: int) -> void:
func _on_menu_about_to_popup() -> void:
menu_button.get_popup().set_item_disabled(MENU_DIRECTORY_SETUP, not plugin.terrain)
menu_button.get_popup().set_item_disabled(MENU_PACK_TEXTURES, not plugin.terrain)
menu_button.get_popup().set_item_disabled(MENU_NEW_UI, not plugin.terrain)
menu_button.get_popup().set_item_disabled(MENU_BAKE_ARRAY_MESH, not plugin.terrain)
menu_button.get_popup().set_item_disabled(MENU_BAKE_OCCLUDER, not plugin.terrain)

View File

@ -28,6 +28,7 @@ var region_gizmo: RegionGizmo
var current_region_position: Vector2
var mouse_global_position: Vector3 = Vector3.ZERO
var godot_editor_window: Window # The Godot Editor window
var godot_editor_vp_container: Node # Houses all editor 3D viewports
func _init() -> void:
@ -37,6 +38,9 @@ func _init() -> void:
# Get the Godot Editor window. Structure is root:Window/EditorNode/Base Control
godot_editor_window = EditorInterface.get_base_control().get_parent().get_parent()
godot_editor_window.focus_entered.connect(_on_godot_focus_entered)
# Container of all editor 3D viewports
# Node3DEditorViewportContainer/Node3DEditorViewport/SubViewportContainer/SubViewport/Camera3D
godot_editor_vp_container = EditorInterface.get_editor_viewport_3d(0).get_parent().get_parent().get_parent()
func _enter_tree() -> void: