237 lines
7.5 KiB
GDScript3
237 lines
7.5 KiB
GDScript3
|
tool
|
||
|
extends Control
|
||
|
|
||
|
const Brush = preload("./terrain_painter.gd")
|
||
|
const Errors = preload("../../util/errors.gd")
|
||
|
#const NativeFactory = preload("../../native/factory.gd")
|
||
|
const Logger = preload("../../util/logger.gd")
|
||
|
|
||
|
const SHAPES_DIR = "addons/zylann.hterrain/tools/brush/shapes"
|
||
|
const DEFAULT_BRUSH = "round2.exr"
|
||
|
|
||
|
onready var _size_slider := $GridContainer/BrushSizeControl/Slider as Slider
|
||
|
onready var _size_value_label := $GridContainer/BrushSizeControl/Label as Label
|
||
|
#onready var _size_label = _params_container.get_node("BrushSizeLabel")
|
||
|
|
||
|
onready var _opacity_slider = $GridContainer/BrushOpacityControl/Slider
|
||
|
onready var _opacity_value_label = $GridContainer/BrushOpacityControl/Label
|
||
|
onready var _opacity_control = $GridContainer/BrushOpacityControl
|
||
|
onready var _opacity_label = $GridContainer/BrushOpacityLabel
|
||
|
|
||
|
onready var _flatten_height_container = $GridContainer/HB
|
||
|
onready var _flatten_height_box = $GridContainer/HB/FlattenHeightControl
|
||
|
onready var _flatten_height_label = $GridContainer/FlattenHeightLabel
|
||
|
onready var _flatten_height_pick_button = $GridContainer/HB/FlattenHeightPickButton
|
||
|
|
||
|
onready var _color_picker = $GridContainer/ColorPickerButton
|
||
|
onready var _color_label = $GridContainer/ColorLabel
|
||
|
|
||
|
onready var _density_slider = $GridContainer/DensitySlider
|
||
|
onready var _density_label = $GridContainer/DensityLabel
|
||
|
|
||
|
onready var _holes_label = $GridContainer/HoleLabel
|
||
|
onready var _holes_checkbox = $GridContainer/HoleCheckbox
|
||
|
|
||
|
onready var _slope_limit_label = $GridContainer/SlopeLimitLabel
|
||
|
onready var _slope_limit_control = $GridContainer/SlopeLimit
|
||
|
|
||
|
onready var _shape_texture_rect = get_node("BrushShapeButton/TextureRect")
|
||
|
|
||
|
var _brush : Brush
|
||
|
var _load_image_dialog = null
|
||
|
var _logger = Logger.get_for(self)
|
||
|
|
||
|
# TODO This is an ugly workaround for https://github.com/godotengine/godot/issues/19479
|
||
|
onready var _temp_node = get_node("Temp")
|
||
|
onready var _grid_container = get_node("GridContainer")
|
||
|
func _set_visibility_of(node: Control, v: bool):
|
||
|
node.get_parent().remove_child(node)
|
||
|
if v:
|
||
|
_grid_container.add_child(node)
|
||
|
else:
|
||
|
_temp_node.add_child(node)
|
||
|
node.visible = v
|
||
|
|
||
|
|
||
|
func _ready():
|
||
|
_size_slider.connect("value_changed", self, "_on_size_slider_value_changed")
|
||
|
_opacity_slider.connect("value_changed", self, "_on_opacity_slider_value_changed")
|
||
|
_flatten_height_box.connect("value_changed", self, "_on_flatten_height_box_value_changed")
|
||
|
_color_picker.connect("color_changed", self, "_on_color_picker_color_changed")
|
||
|
_density_slider.connect("value_changed", self, "_on_density_slider_changed")
|
||
|
_holes_checkbox.connect("toggled", self, "_on_holes_checkbox_toggled")
|
||
|
_slope_limit_control.connect("changed", self, "_on_slope_limit_changed")
|
||
|
|
||
|
_size_slider.max_value = 200
|
||
|
#if NativeFactory.is_native_available():
|
||
|
# _size_slider.max_value = 200
|
||
|
#else:
|
||
|
# _size_slider.max_value = 50
|
||
|
|
||
|
|
||
|
func setup_dialogs(base_control: Control):
|
||
|
assert(_load_image_dialog == null)
|
||
|
_load_image_dialog = EditorFileDialog.new()
|
||
|
_load_image_dialog.mode = EditorFileDialog.MODE_OPEN_FILE
|
||
|
_load_image_dialog.add_filter("*.exr ; EXR files")
|
||
|
_load_image_dialog.resizable = true
|
||
|
_load_image_dialog.access = EditorFileDialog.ACCESS_FILESYSTEM
|
||
|
_load_image_dialog.current_dir = SHAPES_DIR
|
||
|
_load_image_dialog.connect("file_selected", self, "_on_LoadImageDialog_file_selected")
|
||
|
base_control.add_child(_load_image_dialog)
|
||
|
|
||
|
|
||
|
func _exit_tree():
|
||
|
if _load_image_dialog != null:
|
||
|
_load_image_dialog.queue_free()
|
||
|
_load_image_dialog = null
|
||
|
|
||
|
# Testing display modes
|
||
|
#var mode = 0
|
||
|
#func _input(event):
|
||
|
# if event is InputEventKey:
|
||
|
# if event.pressed:
|
||
|
# set_display_mode(mode)
|
||
|
# mode += 1
|
||
|
# if mode >= Brush.MODE_COUNT:
|
||
|
# mode = 0
|
||
|
|
||
|
func set_brush(brush: Brush):
|
||
|
if _brush != null:
|
||
|
_brush.disconnect("changed", self, "_on_brush_changed")
|
||
|
|
||
|
_brush = brush
|
||
|
|
||
|
if brush != null:
|
||
|
# TODO Had an issue in Godot 3.2.3 where mismatching type would silently cast to null...
|
||
|
# It happens if the argument went through a Variant (for example if call_deferred is used)
|
||
|
assert(_brush != null)
|
||
|
|
||
|
if _brush != null:
|
||
|
# Initial params
|
||
|
_size_slider.value = brush.get_brush_size()
|
||
|
_opacity_slider.ratio = brush.get_opacity()
|
||
|
_flatten_height_box.value = brush.get_flatten_height()
|
||
|
_color_picker.get_picker().color = brush.get_color()
|
||
|
_density_slider.value = brush.get_detail_density()
|
||
|
_holes_checkbox.pressed = not brush.get_mask_flag()
|
||
|
|
||
|
var low = rad2deg(brush.get_slope_limit_low_angle())
|
||
|
var high = rad2deg(brush.get_slope_limit_high_angle())
|
||
|
_slope_limit_control.set_values(low, high)
|
||
|
|
||
|
set_display_mode(brush.get_mode())
|
||
|
_set_brush_shape_from_file(SHAPES_DIR.plus_file(DEFAULT_BRUSH))
|
||
|
|
||
|
_brush.connect("changed", self, "_on_brush_properties_changed")
|
||
|
|
||
|
|
||
|
func _on_brush_properties_changed():
|
||
|
_flatten_height_box.value = _brush.get_flatten_height()
|
||
|
_flatten_height_pick_button.pressed = false
|
||
|
|
||
|
|
||
|
func set_display_mode(mode: int):
|
||
|
var show_flatten := mode == Brush.MODE_FLATTEN
|
||
|
var show_color := mode == Brush.MODE_COLOR
|
||
|
var show_density := mode == Brush.MODE_DETAIL
|
||
|
var show_opacity := mode != Brush.MODE_MASK
|
||
|
var show_holes := mode == Brush.MODE_MASK
|
||
|
var show_slope_limit := mode == Brush.MODE_SPLAT
|
||
|
|
||
|
_set_visibility_of(_opacity_label, show_opacity)
|
||
|
_set_visibility_of(_opacity_control, show_opacity)
|
||
|
|
||
|
_set_visibility_of(_color_label, show_color)
|
||
|
_set_visibility_of(_color_picker, show_color)
|
||
|
|
||
|
_set_visibility_of(_flatten_height_label, show_flatten)
|
||
|
_set_visibility_of(_flatten_height_container, show_flatten)
|
||
|
|
||
|
_set_visibility_of(_density_label, show_density)
|
||
|
_set_visibility_of(_density_slider, show_density)
|
||
|
|
||
|
_set_visibility_of(_holes_label, show_holes)
|
||
|
_set_visibility_of(_holes_checkbox, show_holes)
|
||
|
|
||
|
_set_visibility_of(_slope_limit_label, show_slope_limit)
|
||
|
_set_visibility_of(_slope_limit_control, show_slope_limit)
|
||
|
|
||
|
_flatten_height_pick_button.pressed = false
|
||
|
|
||
|
|
||
|
func _on_size_slider_value_changed(v: float):
|
||
|
if _brush != null:
|
||
|
_brush.set_brush_size(int(v))
|
||
|
_size_value_label.text = str(v)
|
||
|
|
||
|
|
||
|
func _on_opacity_slider_value_changed(v: float):
|
||
|
if _brush != null:
|
||
|
_brush.set_opacity(_opacity_slider.ratio)
|
||
|
_opacity_value_label.text = str(v)
|
||
|
|
||
|
|
||
|
func _on_flatten_height_box_value_changed(v: float):
|
||
|
if _brush != null:
|
||
|
_brush.set_flatten_height(v)
|
||
|
|
||
|
|
||
|
func _on_color_picker_color_changed(v: Color):
|
||
|
if _brush != null:
|
||
|
_brush.set_color(v)
|
||
|
|
||
|
|
||
|
func _on_density_slider_changed(v: float):
|
||
|
if _brush != null:
|
||
|
_brush.set_detail_density(v)
|
||
|
|
||
|
|
||
|
func _on_holes_checkbox_toggled(v: bool):
|
||
|
if _brush != null:
|
||
|
# When checked, we draw holes. When unchecked, we clear holes
|
||
|
_brush.set_mask_flag(not v)
|
||
|
|
||
|
|
||
|
func _on_BrushShapeButton_pressed():
|
||
|
_load_image_dialog.popup_centered_ratio(0.7)
|
||
|
|
||
|
|
||
|
func _on_LoadImageDialog_file_selected(path: String):
|
||
|
_set_brush_shape_from_file(path)
|
||
|
|
||
|
|
||
|
func _set_brush_shape_from_file(path: String):
|
||
|
var im := Image.new()
|
||
|
var err := im.load(path)
|
||
|
if err != OK:
|
||
|
_logger.error("Could not load image at '{0}', error {1}" \
|
||
|
.format([path, Errors.get_message(err)]))
|
||
|
return
|
||
|
|
||
|
var tex := ImageTexture.new()
|
||
|
tex.create_from_image(im, Texture.FLAG_FILTER)
|
||
|
|
||
|
if _brush != null:
|
||
|
var im2 := im
|
||
|
var v := Engine.get_version_info()
|
||
|
if v.major == 3 and v.minor < 1:
|
||
|
# Forcing image brushes would ruin resized ones,
|
||
|
# due to https://github.com/godotengine/godot/issues/24244
|
||
|
if path.find(SHAPES_DIR.plus_file(DEFAULT_BRUSH)) != -1:
|
||
|
im2 = null
|
||
|
|
||
|
_brush.set_brush_texture(tex)
|
||
|
|
||
|
_shape_texture_rect.texture = tex
|
||
|
|
||
|
|
||
|
func _on_FlattenHeightPickButton_pressed():
|
||
|
_brush.set_meta("pick_height", true)
|
||
|
|
||
|
|
||
|
func _on_slope_limit_changed():
|
||
|
var low = deg2rad(_slope_limit_control.get_low_value())
|
||
|
var high = deg2rad(_slope_limit_control.get_high_value())
|
||
|
_brush.set_slope_limit_angles(low, high)
|