2025-07-22 11:59:08 +00:00
|
|
|
@tool
|
2025-06-13 08:03:19 +00:00
|
|
|
extends TextureRect
|
2025-02-07 12:52:15 +00:00
|
|
|
|
2025-06-13 08:03:19 +00:00
|
|
|
signal shaven(progress: float)
|
2025-07-22 11:59:08 +00:00
|
|
|
signal key_area_shaven(progress: float)
|
2025-06-13 08:03:19 +00:00
|
|
|
|
2025-07-22 11:59:08 +00:00
|
|
|
@export var brush_width := 9
|
|
|
|
@export var key_area := Rect2i():
|
|
|
|
set(val):
|
|
|
|
key_area = val
|
|
|
|
queue_redraw()
|
2025-07-09 10:22:38 +00:00
|
|
|
|
2025-06-17 03:39:55 +00:00
|
|
|
# points inside radius 9 circle(必须>5)
|
2025-07-22 11:59:08 +00:00
|
|
|
var brush_points: PackedVector2Array
|
2025-06-13 08:03:19 +00:00
|
|
|
var area_size := Vector2.ZERO
|
|
|
|
var image: Image
|
2025-06-17 03:39:55 +00:00
|
|
|
# bit_mask is the bottom
|
2025-07-22 11:59:08 +00:00
|
|
|
var bit_mask_key_area: BitMap
|
2025-06-13 08:03:19 +00:00
|
|
|
var bit_mask := BitMap.new()
|
2025-07-22 11:59:08 +00:00
|
|
|
var key_area_total_pixels := 0
|
|
|
|
var total_pixels := 0
|
|
|
|
|
|
|
|
# 性能优化相关变量
|
|
|
|
var update_timer := 0.0
|
|
|
|
var update_interval := 0.02 # 约50fps
|
|
|
|
var pending_update := false
|
2025-02-07 12:52:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
func _ready() -> void:
|
2025-07-22 11:59:08 +00:00
|
|
|
if Engine.is_editor_hint():
|
|
|
|
return
|
2025-07-09 10:22:38 +00:00
|
|
|
brush_points = generate_brush_points(brush_width)
|
2025-06-13 08:03:19 +00:00
|
|
|
if not texture:
|
|
|
|
printerr("texture not found")
|
|
|
|
return
|
|
|
|
area_size = texture.get_size()
|
|
|
|
image = texture.get_image()
|
2025-07-22 11:59:08 +00:00
|
|
|
# 确保图像具有alpha通道
|
|
|
|
if image.get_format() != Image.FORMAT_RGBA8:
|
|
|
|
image.convert(Image.FORMAT_RGBA8)
|
2025-06-13 08:03:19 +00:00
|
|
|
texture = ImageTexture.create_from_image(image)
|
2025-07-22 11:59:08 +00:00
|
|
|
# 创建 bit_mask
|
2025-06-13 08:03:19 +00:00
|
|
|
bit_mask.create_from_image_alpha(image)
|
|
|
|
total_pixels = bit_mask.get_true_bit_count()
|
2025-07-22 11:59:08 +00:00
|
|
|
# 创建 bit_mask_key_area
|
|
|
|
if key_area.size.x > 0 and key_area.size.y > 0:
|
|
|
|
bit_mask_key_area = BitMap.new()
|
|
|
|
bit_mask_key_area.create(area_size)
|
|
|
|
# clamp key_area
|
|
|
|
key_area.position = key_area.position.clamp(Vector2.ZERO, area_size - Vector2.ONE)
|
|
|
|
key_area.end = key_area.end.clamp(key_area.position, area_size - Vector2.ONE)
|
|
|
|
# 只保留 key_area 区域中的 bit_mask_key_area
|
|
|
|
for y in range(key_area.position.y, key_area.end.y):
|
|
|
|
for x in range(key_area.position.x, key_area.end.x):
|
|
|
|
var pos = Vector2i(x, y)
|
|
|
|
if bit_mask.get_bitv(pos):
|
|
|
|
bit_mask_key_area.set_bitv(pos, true)
|
|
|
|
key_area_total_pixels = bit_mask_key_area.get_true_bit_count()
|
|
|
|
if GlobalConfig.DEBUG:
|
|
|
|
prints("key_area after clamp:", key_area, "area_size:", area_size, "pixels:", key_area_total_pixels)
|
2025-06-13 08:03:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
# a solid circle brush, center = (0, 0)
|
|
|
|
func generate_brush_points(radius: int) -> PackedVector2Array:
|
|
|
|
var points := PackedVector2Array()
|
2025-06-17 03:39:55 +00:00
|
|
|
# Top points
|
2025-06-13 08:03:19 +00:00
|
|
|
for x in range(-radius, radius + 1):
|
|
|
|
for y in range(-radius, radius + 1):
|
|
|
|
# If point is within circle radius
|
|
|
|
if x * x + y * y <= radius * radius:
|
|
|
|
points.append(Vector2(x, y))
|
2025-06-17 03:39:55 +00:00
|
|
|
# mid points
|
|
|
|
radius = int(radius / 3.0)
|
2025-07-22 11:59:08 +00:00
|
|
|
for x in range(-radius, radius + 1):
|
2025-06-17 03:39:55 +00:00
|
|
|
for y in range(-radius, radius + 1):
|
|
|
|
# If point is within circle radius
|
|
|
|
if x * x + y * y <= radius * radius:
|
|
|
|
points.append(Vector2(x, y))
|
|
|
|
# bottom points
|
|
|
|
points.append(Vector2(0, 0))
|
|
|
|
points.append(Vector2(1, 0))
|
|
|
|
points.append(Vector2(0, 1))
|
|
|
|
points.append(Vector2(1, 1))
|
2025-06-13 08:03:19 +00:00
|
|
|
return points
|
|
|
|
|
|
|
|
|
2025-06-17 03:39:55 +00:00
|
|
|
var last_pos := Vector2(-1, -1)
|
|
|
|
|
|
|
|
|
2025-07-22 11:59:08 +00:00
|
|
|
func _process(delta: float) -> void:
|
|
|
|
if pending_update:
|
|
|
|
update_timer += delta
|
|
|
|
if update_timer >= update_interval:
|
|
|
|
_perform_texture_update()
|
|
|
|
update_timer = 0.0
|
|
|
|
|
|
|
|
|
2025-06-13 08:03:19 +00:00
|
|
|
func _shave() -> void:
|
|
|
|
if not mouse_pressing:
|
|
|
|
return
|
|
|
|
var mouse_pos := get_local_mouse_position()
|
2025-07-22 11:59:08 +00:00
|
|
|
# 添加距离检查,避免重复处理相同位置
|
|
|
|
if last_pos.distance_to(mouse_pos) < 1.0:
|
2025-06-17 03:39:55 +00:00
|
|
|
return
|
|
|
|
last_pos = mouse_pos
|
2025-06-13 08:03:19 +00:00
|
|
|
var updated = false
|
2025-07-22 11:59:08 +00:00
|
|
|
# 批量处理像素
|
2025-06-13 08:03:19 +00:00
|
|
|
for point in brush_points:
|
|
|
|
var pos = mouse_pos + point
|
|
|
|
if pos.x < 0 or pos.x >= area_size.x or pos.y < 0 or pos.y >= area_size.y:
|
|
|
|
continue
|
|
|
|
if bit_mask.get_bitv(pos):
|
|
|
|
updated = true
|
2025-07-22 11:59:08 +00:00
|
|
|
var color = image.get_pixelv(pos)
|
2025-06-17 03:39:55 +00:00
|
|
|
color.a = clampf(color.a - 0.15, 0.0, 1.0)
|
2025-07-22 11:59:08 +00:00
|
|
|
if color.a == 0.0:
|
2025-06-17 03:39:55 +00:00
|
|
|
bit_mask.set_bitv(pos, false)
|
2025-07-22 11:59:08 +00:00
|
|
|
if bit_mask_key_area:
|
|
|
|
bit_mask_key_area.set_bitv(pos, false)
|
2025-06-17 03:39:55 +00:00
|
|
|
image.set_pixelv(pos, color)
|
2025-06-13 08:03:19 +00:00
|
|
|
if updated:
|
2025-07-22 11:59:08 +00:00
|
|
|
pending_update = true
|
|
|
|
|
|
|
|
|
|
|
|
func _perform_texture_update() -> void:
|
|
|
|
if pending_update:
|
2025-06-13 08:03:19 +00:00
|
|
|
(texture as ImageTexture).update(image)
|
|
|
|
_send_signal()
|
2025-07-22 11:59:08 +00:00
|
|
|
pending_update = false
|
2025-06-13 08:03:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
func _send_signal() -> void:
|
2025-07-22 11:59:08 +00:00
|
|
|
if total_pixels > 0:
|
|
|
|
var true_bit_count = bit_mask.get_true_bit_count()
|
|
|
|
var progress = 1.0 - float(true_bit_count) / total_pixels
|
|
|
|
shaven.emit(progress)
|
|
|
|
if key_area_total_pixels > 0:
|
|
|
|
var key_area_true_bit_count = bit_mask_key_area.get_true_bit_count()
|
|
|
|
var key_area_progress = 1.0 - float(key_area_true_bit_count) / key_area_total_pixels
|
|
|
|
key_area_shaven.emit(key_area_progress)
|
2025-06-13 08:03:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
var mouse_pressing = false
|
|
|
|
|
2025-02-07 12:52:15 +00:00
|
|
|
|
2025-06-13 08:03:19 +00:00
|
|
|
# 可以刮出区域
|
|
|
|
func _gui_input(event: InputEvent) -> void:
|
2025-07-22 11:59:08 +00:00
|
|
|
if Engine.is_editor_hint():
|
|
|
|
return
|
2025-06-13 08:03:19 +00:00
|
|
|
if not is_visible_in_tree():
|
|
|
|
return
|
|
|
|
if event is InputEventMouseButton:
|
|
|
|
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
|
|
|
mouse_pressing = true
|
2025-07-22 11:59:08 +00:00
|
|
|
# 重置位置以确保首次点击生效
|
|
|
|
last_pos = Vector2(-1, -1)
|
2025-06-13 08:03:19 +00:00
|
|
|
_shave()
|
|
|
|
elif event.button_index == MOUSE_BUTTON_LEFT and not event.pressed:
|
|
|
|
mouse_pressing = false
|
2025-07-22 11:59:08 +00:00
|
|
|
# 立即执行最后的更新
|
|
|
|
if pending_update:
|
|
|
|
_perform_texture_update()
|
2025-06-13 08:03:19 +00:00
|
|
|
accept_event()
|
|
|
|
elif event is InputEventMouseMotion and mouse_pressing:
|
|
|
|
_shave()
|
|
|
|
accept_event()
|
2025-07-22 11:59:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
func _draw() -> void:
|
|
|
|
# draw key area if exists
|
|
|
|
if Engine.is_editor_hint() and key_area.size.x > 0 and key_area.size.y > 0:
|
|
|
|
draw_rect(key_area, Color.RED, false, 2.0)
|