184 lines
4.1 KiB
GDScript
184 lines
4.1 KiB
GDScript
tool
|
|
|
|
# Brush properties (shape, transform, timing and opacity).
|
|
# Other attributes like color, height or texture index are tool-specific,
|
|
# while brush properties apply to all of them.
|
|
# This is separate from Painter because it could apply to multiple Painters at once.
|
|
|
|
const Errors = preload("../../util/errors.gd")
|
|
|
|
const SHAPES_DIR = "addons/zylann.hterrain/tools/brush/shapes"
|
|
const DEFAULT_BRUSH = "round2.exr"
|
|
# Reasonable size for sliders to be usable
|
|
const MAX_SIZE_FOR_SLIDERS = 500
|
|
# Absolute size limit. Terrains can't be larger than that, and it will be very slow to paint
|
|
const MAX_SIZE = 4000
|
|
|
|
signal size_changed(new_size)
|
|
signal shapes_changed
|
|
|
|
var _size := 32
|
|
var _opacity := 1.0
|
|
var _random_rotation := false
|
|
var _pressure_enabled := false
|
|
var _pressure_over_scale := 0.5
|
|
var _pressure_over_opacity := 0.5
|
|
# TODO Rename stamp_*?
|
|
var _frequency_distance := 0.0
|
|
var _frequency_time_ms := 0
|
|
# Array of greyscale Textures
|
|
var _shapes := []
|
|
|
|
var _shape_index := 0
|
|
var _prev_position := Vector2(-999, -999)
|
|
var _prev_time_ms := 0
|
|
|
|
|
|
func set_size(size: int):
|
|
if size < 1:
|
|
size = 1
|
|
if size != _size:
|
|
_size = size
|
|
emit_signal("size_changed", _size)
|
|
|
|
|
|
func get_size() -> int:
|
|
return _size
|
|
|
|
|
|
func set_opacity(opacity: float):
|
|
_opacity = clamp(opacity, 0.0, 1.0)
|
|
|
|
|
|
func get_opacity() -> float:
|
|
return _opacity
|
|
|
|
|
|
func set_random_rotation_enabled(enabled: bool):
|
|
_random_rotation = enabled
|
|
|
|
|
|
func is_random_rotation_enabled() -> bool:
|
|
return _random_rotation
|
|
|
|
|
|
func set_pressure_enabled(enabled: bool):
|
|
_pressure_enabled = enabled
|
|
|
|
|
|
func is_pressure_enabled() -> bool:
|
|
return _pressure_enabled
|
|
|
|
|
|
func set_pressure_over_scale(amount: float):
|
|
_pressure_over_scale = clamp(amount, 0.0, 1.0)
|
|
|
|
|
|
func get_pressure_over_scale() -> float:
|
|
return _pressure_over_scale
|
|
|
|
|
|
func set_pressure_over_opacity(amount: float):
|
|
_pressure_over_opacity = clamp(amount, 0.0, 1.0)
|
|
|
|
|
|
func get_pressure_over_opacity() -> float:
|
|
return _pressure_over_opacity
|
|
|
|
|
|
func set_frequency_distance(d: float):
|
|
_frequency_distance = max(d, 0.0)
|
|
|
|
|
|
func get_frequency_distance() -> float:
|
|
return _frequency_distance
|
|
|
|
|
|
func set_frequency_time_ms(t: int):
|
|
if t < 0:
|
|
t = 0
|
|
_frequency_time_ms = t
|
|
|
|
|
|
func get_frequency_time_ms() -> int:
|
|
return _frequency_time_ms
|
|
|
|
|
|
func set_shapes(shapes: Array):
|
|
assert(len(shapes) >= 1)
|
|
for s in shapes:
|
|
assert(s != null)
|
|
assert(s is Texture)
|
|
_shapes = shapes.duplicate(false)
|
|
if _shape_index >= len(_shapes):
|
|
_shape_index = len(_shapes) - 1
|
|
emit_signal("shapes_changed")
|
|
|
|
|
|
func get_shapes() -> Array:
|
|
return _shapes.duplicate(false)
|
|
|
|
|
|
func get_shape(i: int) -> Texture:
|
|
return _shapes[i]
|
|
|
|
|
|
static func load_shape_from_image_file(fpath: String, logger) -> Texture:
|
|
var im := Image.new()
|
|
var err := im.load(fpath)
|
|
if err != OK:
|
|
logger.error("Could not load image at '{0}', error {1}" \
|
|
.format([fpath, Errors.get_message(err)]))
|
|
return null
|
|
var tex := ImageTexture.new()
|
|
tex.create_from_image(im, Texture.FLAG_FILTER)
|
|
return tex
|
|
|
|
|
|
# Call this while handling mouse or pen input.
|
|
# If it returns false, painting should not run.
|
|
func configure_paint_input(painters: Array, position: Vector2, pressure: float) -> bool:
|
|
assert(len(_shapes) != 0)
|
|
|
|
# DEBUG
|
|
#pressure = 0.5 + 0.5 * sin(OS.get_ticks_msec() / 200.0)
|
|
|
|
if position.distance_to(_prev_position) < _frequency_distance:
|
|
return false
|
|
var now = OS.get_ticks_msec()
|
|
if (now - _prev_time_ms) < _frequency_time_ms:
|
|
return false
|
|
_prev_position = position
|
|
_prev_time_ms = now
|
|
|
|
for painter in painters:
|
|
if _random_rotation:
|
|
painter.set_brush_rotation(rand_range(-PI, PI))
|
|
else:
|
|
painter.set_brush_rotation(0.0)
|
|
|
|
painter.set_brush_texture(_shapes[_shape_index])
|
|
painter.set_brush_size(_size)
|
|
|
|
if _pressure_enabled:
|
|
painter.set_brush_scale(lerp(1.0, pressure, _pressure_over_scale))
|
|
painter.set_brush_opacity(_opacity * lerp(1.0, pressure, _pressure_over_opacity))
|
|
else:
|
|
painter.set_brush_scale(1.0)
|
|
painter.set_brush_opacity(_opacity)
|
|
|
|
#painter.paint_input(position)
|
|
|
|
_shape_index += 1
|
|
if _shape_index >= len(_shapes):
|
|
_shape_index = 0
|
|
|
|
return true
|
|
|
|
|
|
# Call this when the user releases the pen or mouse button
|
|
func on_paint_end():
|
|
_prev_position = Vector2(-999, -999)
|
|
_prev_time_ms = 0
|
|
|
|
|