tochie-kit/addons/Godoxel/Editor.gd

1578 lines
44 KiB
GDScript

tool
extends Control
enum Tools {
PAINT,
BRUSH,
BUCKET,
RAINBOW,
LINE,
RECT,
DARKEN,
BRIGHTEN
COLORPICKER,
CUT,
PASTECUT,
}
var shortcuts = {
KEY_CONTROL: {
KEY_Z: "undo_action",
KEY_Y: "redo_action",
},
KEY_SHIFT: {
KEY_Q: Tools.PAINT,
KEY_W: Tools.BUCKET,
KEY_E: Tools.RAINBOW,
KEY_A: Tools.LINE,
KEY_S: Tools.RECT,
KEY_D: Tools.COLORPICKER,
KEY_Z: Tools.BRIGHTEN,
KEY_X: Tools.DARKEN,
KEY_C: Tools.CUT,
},
KEY_Z: "undo_action",
KEY_Y: "redo_action",
}
const LayerButton = preload("res://addons/Godoxel/LayerButton.tscn")
onready var right_panel: Panel = find_node("RightPanel")
onready var left_panel: Panel = find_node("LeftPanel")
onready var layer_buttons: Control = find_node("LayerButtons")
onready var paint_canvas_container_node = find_node("PaintCanvasContainer")
onready var paint_canvas: GECanvas = find_node("Canvas")
onready var canvas_background: TextureRect = find_node("CanvasBackground")
onready var colors_grid = find_node("Colors")
onready var debug_text = find_node("DebugTextDisplay")
onready var preview_window: Control = find_node("PreviewWindow")
onready var preview_layer_textures = find_node("PreviewLayerTextures")
onready var shift_shortcut_window = find_node("ShortcutWindow")
onready var frame_selection_container = find_node("FrameSelectionContainer")
onready var anim_panel = find_node("AnimationPanel")
onready var color_picker_button = find_node("ColorPicker")
var allow_drawing = true
var selected_color = Color(1, 1, 1, 1) setget set_selected_color
var util = preload("res://addons/Godoxel/Util.gd")
var mouse_in_region = false
var mouse_on_top = false
var _preview_drag_start_pos = null
var _preview_drag_pos = null
var _middle_mouse_pressed_pos = null
var _middle_mouse_pressed_start_pos = null
var _previous_tool
var brush_mode
var _layer_button_ref = {}
var _total_added_layers = 0
var selected_brush_prefab = 0
var _last_drawn_pixel = Vector2.ZERO
var _last_preview_draw_cell_pos = Vector2.ZERO
var _selection_cells = []
var _selection_colors = []
var _cut_pos = Vector2.ZERO
var _cut_size = Vector2.ZERO
var _actions_history = [] # for undo
var _redo_history = []
var _current_action
var _last_mouse_pos_canvas_area = Vector2.ZERO
var _picked_color = false
var mouse_position = Vector2()
var canvas_position = Vector2()
var canvas_mouse_position = Vector2()
var cell_mouse_position = Vector2()
var cell_color = Color()
var last_mouse_position = Vector2()
var last_canvas_position = Vector2()
var last_canvas_mouse_position = Vector2()
var last_cell_mouse_position = Vector2()
var last_cell_color = Color()
const current_layer_highlight = Color(0.354706, 0.497302, 0.769531)
const other_layer_highlight = Color(0.180392, 0.176471, 0.176471)
const locked_layer_highlight = Color(0.098039, 0.094118, 0.094118)
var big_grid_pixels = 4 # 1 grid-box is big_grid_pixels big
var _preview_scale := 1.0
var animations = []
var current_animation_idx = 0
var current_animation: GEAnimation
var current_frame_idx = 0
var current_frame: GEFrame
var current_layer_idx = 0
var animation_looped := true
var animation_playing := false
var animation_fps = 5.0
var next_anim_time = 0.0
var animation_frame_duration: float = 1 / animation_fps
var preview_thread = Thread.new()
func _ready():
#--------------------
#Setup nodes
#--------------------
selected_color = color_picker_button.color
_center_element(paint_canvas)
#--------------------
#connect nodes
#--------------------
if not anim_panel.is_connected("on_add_animation", self, "_on_add_animation"):
anim_panel.connect("on_add_animation", self, "_on_add_animation")
anim_panel.connect("on_add_frame_pressed", self, "_on_add_frame_pressed")
anim_panel.connect("on_frame_pressed", self, "_on_frame_pressed")
anim_panel.connect("on_animation_pressed", self, "_on_animation_pressed")
anim_panel.connect("on_animation_move", self, "_on_animation_move")
anim_panel.connect("on_animation_duplicated", self, "_on_animation_duplicated")
anim_panel.connect("on_animation_selected", self, "_on_animation_selected")
anim_panel.connect("on_animation_deleted", self, "_on_animation_deleted")
if not colors_grid.is_connected("color_change_request", self, "change_color"):
colors_grid.connect("color_change_request", self, "change_color")
if not is_connected("visibility_changed", self, "_on_Editor_visibility_changed"):
connect("visibility_changed", self, "_on_Editor_visibility_changed")
canvas_background.material.set_shader_param(
"pixel_size", 8 * pow(0.5, big_grid_pixels)/paint_canvas.pixel_size)
# ready
set_tool(Tools.PAINT)
find_node("BrushSizeLabel").text = str(int(find_node("BrushSize").value))
paint_canvas.update()
shift_shortcut_window.setup(shortcuts[KEY_SHIFT])
#--------------------
# animation
#--------------------
current_animation = add_new_animation()
current_frame = add_new_frame(current_animation)
paint_canvas.set_frame(current_frame)
add_new_layer()
# var animation = GEAnimation.new()
# animations.append(animation)
#
# var frame = GEFrame.new()
# frame.resize(paint_canvas.canvas_width, paint_canvas.canvas_height)
# animation.add_frame(frame)
# var layer = frame.add_new_layer(layer_buttons.get_child(0).name)
#
# anim_panel.set_frame_preview(animations.size()-1, 0, frame)
#
# var anim = _on_add_animation(anim_panel.add_animation_stripe())
# data
# var frame = paint_canvas.frame
# var anim = GEAnimation.new()
# anim.add_frame(frame)
# animations.append(anim)
#
# # ui
# var anim_stripe = anim_panel.add_animation_stripe()
# anim_stripe.connect("on_frame_pressed", self, "_on_frame_pressed")
#
# # data -> ui
# anim_stripe.set_animation(anim)
# #anim_stripe.add_frame(frame)
# #anim_panel._on_add_frame_pressed(0, 0) #ugly
#--------------------
# animation
#--------------------
preview_window.update_preview(current_frame)
anim_panel.set_frame_preview(current_animation_idx, current_frame_idx, current_frame)
set_process(true)
# start thread
# preview_thread.start(self, "_update_frame_preview")
#func _update_frame_preview(data):
# while true:
# print("update preview")
# current_frame._update_preview()
# yield(get_tree().create_timer(2.0), "timeout")
func _exit_tree():
if preview_thread != null and preview_thread.is_active():
preview_thread.wait_to_finish()
func _input(event: InputEvent) -> void:
if is_any_menu_open():
return
if not is_visible_in_tree():
return
if paint_canvas_container_node == null or paint_canvas == null:
return
if event is InputEventMouseButton:
if event.pressed and not event.is_echo():
if is_mouse_in_preview_window():
_preview_drag_start_pos = preview_layer_textures.get_child(0).rect_position
_preview_drag_pos = get_global_mouse_position()
elif is_mouse_in_canvas():
_middle_mouse_pressed_start_pos = paint_canvas.rect_position
_middle_mouse_pressed_pos = get_global_mouse_position()
_handle_zoom(event)
if paint_canvas.is_active_layer_locked():
return
_handle_paint(event)
func _unhandled_input(event:InputEvent):
if is_any_menu_open():
return
if not is_visible_in_tree():
return
if paint_canvas_container_node == null or paint_canvas == null:
return
_handle_shortcuts(event)
func _handle_paint(event: InputEvent):
if brush_mode == Tools.CUT:
if event is InputEventMouseButton:
if event.button_index == BUTTON_LEFT:
if not event.pressed:
commit_action()
if is_mouse_in_canvas():
if event is InputEventMouseButton:
match brush_mode:
Tools.BUCKET:
if event.button_index == BUTTON_LEFT:
if event.pressed:
if _current_action == null:
_current_action = get_action()
do_action([cell_mouse_position, last_cell_mouse_position, selected_color])
Tools.COLORPICKER:
if event.button_index == BUTTON_LEFT:
if event.pressed:
if paint_canvas.get_pixel(cell_mouse_position.x, cell_mouse_position.y).a == 0:
return
selected_color = paint_canvas.get_pixel(cell_mouse_position.x, cell_mouse_position.y)
_picked_color = true
find_node("Colors").add_color_prefab(selected_color)
color_picker_button.color = selected_color
elif _picked_color:
set_tool(_previous_tool)
elif event.button_index == BUTTON_RIGHT:
if event.pressed:
set_tool(_previous_tool)
Tools.PASTECUT:
if event.button_index == BUTTON_RIGHT:
if event.pressed:
commit_action()
set_tool(Tools.PAINT)
func _process(delta):
if not is_visible_in_tree():
return
if paint_canvas_container_node == null or paint_canvas == null:
return
_handle_scroll()
if is_any_menu_open():
return
#Update commonly used variables
var grid_size = paint_canvas.pixel_size
mouse_position = get_global_mouse_position() #paint_canvas.get_local_mouse_position()
canvas_position = paint_canvas.rect_global_position
canvas_mouse_position = Vector2(mouse_position.x - canvas_position.x, mouse_position.y - canvas_position.y)
if is_mouse_in_canvas():
cell_mouse_position = Vector2(
floor(canvas_mouse_position.x / grid_size),
floor(canvas_mouse_position.y / grid_size))
cell_color = paint_canvas.get_pixel(cell_mouse_position.x, cell_mouse_position.y)
update_text_info()
if animation_playing:
next_anim_time += delta
if next_anim_time >= animation_frame_duration:
next_anim_time = 0
current_frame_idx += 1
if current_frame_idx >= current_animation.frames.size():
current_frame_idx = 0
if not animation_looped:
animation_playing = false
anim_panel.set_play_pause_button(animation_playing)
_on_frame_pressed(current_animation_idx, current_frame_idx)
else:
if is_mouse_in_canvas():
if not paint_canvas.is_active_layer_locked():
if is_position_in_canvas(get_global_mouse_position()) or \
is_position_in_canvas(_last_mouse_pos_canvas_area):
brush_process()
else:
print(cell_mouse_position, " not in ", paint_canvas_container_node.rect_size)
print("not in canvas")
_draw_tool_brush()
#Update last variables with the current variables
last_mouse_position = mouse_position
last_canvas_position = canvas_position
last_canvas_mouse_position = canvas_mouse_position
last_cell_mouse_position = cell_mouse_position
last_cell_color = cell_color
_last_mouse_pos_canvas_area = get_global_mouse_position()
#paint_canvas_container_node.get_local_mouse_position()
var ctrl_pressed = false
var shift_pressed = false
func _handle_shortcuts(event: InputEvent):
if not event is InputEventKey:
return
# check modifiers
if event.scancode == KEY_SHIFT:
shift_pressed = event.pressed
shift_shortcut_window.visible = shift_pressed
elif event.scancode == KEY_CONTROL:
ctrl_pressed = event.pressed
if not event.pressed:
return
# delegate shortcuts depending on modifiers
if shift_pressed:
if shortcuts[KEY_SHIFT].has(event.scancode):
if shift_shortcut_window.check_input_for_shorcut(event, event.shift):
if shortcuts[KEY_SHIFT].has(event.scancode):
_handle_shortcut(shortcuts[KEY_SHIFT][event.scancode])
accept_event()
return
elif ctrl_pressed:
if shortcuts[KEY_CONTROL].has(event.scancode):
_handle_shortcut(shortcuts[KEY_CONTROL][event.scancode])
accept_event()
return
elif shortcuts.has(event.scancode):
accept_event()
_handle_shortcut(shortcuts[event.scancode])
return
func _handle_shortcut(shortcut):
if typeof(shortcut) == TYPE_STRING:
call(shortcut)
else:
set_tool(shortcut)
func _draw_tool_brush():
paint_canvas.tool_layer.clear()
match brush_mode:
Tools.PASTECUT:
for idx in range(_selection_cells.size()):
var pixel = _selection_cells[idx]
# if pixel.x < 0 or pixel.y < 0:
# print(pixel)
var color = _selection_colors[idx]
pixel -= _cut_pos + _cut_size / 2
pixel += cell_mouse_position
paint_canvas._set_pixel_v(paint_canvas.tool_layer, pixel, color)
Tools.BRUSH:
var pixels = BrushPrefabs.get_brush(selected_brush_prefab, find_node("BrushSize").value)
for pixel in pixels:
paint_canvas._set_pixel(paint_canvas.tool_layer,
cell_mouse_position.x + pixel.x, cell_mouse_position.y + pixel.y, selected_color)
Tools.RAINBOW:
paint_canvas._set_pixel(paint_canvas.tool_layer,
cell_mouse_position.x, cell_mouse_position.y, Color(0.46875, 0.446777, 0.446777, 0.196078))
Tools.COLORPICKER:
paint_canvas._set_pixel(paint_canvas.tool_layer,
cell_mouse_position.x, cell_mouse_position.y, Color(0.866667, 0.847059, 0.847059, 0.196078))
_:
paint_canvas._set_pixel(paint_canvas.tool_layer,
cell_mouse_position.x, cell_mouse_position.y, selected_color)
paint_canvas.update()
#TODO add here brush prefab drawing
paint_canvas.tool_layer.update_texture()
############################################
# Animation Panel
############################################
var change_grid = true
func _on_AnimationPanel_on_play_pause_pressed():
animation_playing = not animation_playing
if animation_playing and not paint_canvas.grid.visible:
change_grid = false
if change_grid:
if animation_playing:
paint_canvas.hide_grid()
elif not paint_canvas.grid.visible:
paint_canvas.show_grid()
func _on_AnimationPanel_on_animation_loop_toggled():
animation_looped = not animation_looped
func _on_AnimationPanel_on_animation_frame_rate_changed(new_frame_rate):
animation_fps = new_frame_rate
animation_frame_duration = 1.0 / animation_fps
############################################
# Frames
############################################
func _create_new_frame():
var frame = GEFrame.new()
frame.resize(paint_canvas.canvas_width, paint_canvas.canvas_height)
for idx in range(get_layer_count()):
var layer = _create_layer(get_layer_name(idx), frame.width, frame.height)
frame.add_frame_layer(layer)
return frame
func add_new_frame(animation):
var frame = _create_new_frame()
animation.add_frame(frame)
anim_panel.set_frame_preview(current_animation_idx, current_frame_idx, frame)
_update_anim_and_frame_idx(current_animation_idx, current_frame_idx)
return frame
func duplicate_frame():
pass
func copy_current_frame_to_all_frames():
pass
func _on_frame_pressed(anim_idx, frame_idx):
_update_anim_and_frame_idx(anim_idx, frame_idx)
paint_canvas.set_frame(current_frame)
preview_window.update_preview(current_frame)
func _on_add_frame_pressed(anim_idx, frame_idx):
current_animation_idx = anim_idx
current_frame_idx = frame_idx
add_new_frame(animations[anim_idx])
func _update_anim_and_frame_idx(anim_idx, frame_idx):
current_animation_idx = anim_idx
current_animation = animations[current_animation_idx]
current_frame_idx = min(frame_idx, animations[current_animation_idx].frames.size() - 1)
current_frame = animations[current_animation_idx].frames[current_frame_idx]
############################################
# Animations
############################################
func _on_add_animation():
var animation = add_new_animation()
add_new_frame_to_animation(animation)
return animation
func add_new_frame_to_animation(animation: GEAnimation):
var frame = _create_new_frame()
animation.add_frame(frame)
anim_panel.set_frame_preview(animation.get_anim_index(), animation.frames.size()-1, frame)
return frame
func add_new_animation():
var animation = GEAnimation.new()
animation.set_anim_index(animations.size())
animations.append(animation)
anim_panel.add_animation_stripe()
return animation
func add_animation():
var animation = GEAnimation.new()
animations.append(animation)
var frame = GEFrame.new()
frame.resize(paint_canvas.canvas_width, paint_canvas.canvas_height)
animation.add_frame(frame)
for idx in range(preview_layer_textures.get_child_count()):
frame.add_new_layer(layer_buttons.get_child(idx).name)
anim_panel.set_frame_preview(animations.size()-1, 0, frame)
return animation
func _on_animation_pressed(anim_idx):
current_animation_idx = anim_idx
_update_anim_and_frame_idx(current_animation_idx, current_frame_idx)
paint_canvas.set_frame(current_frame)
preview_window.update_preview(current_frame)
func _on_animation_selected(anim_idx):
current_animation_idx = anim_idx
_update_anim_and_frame_idx(current_animation_idx, current_frame_idx)
paint_canvas.set_frame(current_frame)
preview_window.update_preview(current_frame)
func _on_animation_deleted(anim_idx):
if animations.size() <= 1:
return
animations.remove(anim_idx)
var anim_stripe = anim_panel.anim_button_container.get_child(anim_idx)
anim_panel.anim_button_container.remove_child(anim_stripe)
anim_stripe.queue_free()
if current_animation_idx >= anim_idx:
current_animation_idx -= 1
_update_anim_and_frame_idx(current_animation_idx, current_frame_idx)
func _on_animation_duplicated(anim_idx):
var anim = add_new_animation()
for i in range(animations[anim_idx].frames.size()):
if i > 0:
anim_panel.get_last_animation_stripe().add_new_frame_button()
var frame = add_new_frame_to_animation(anim)
frame.width = animations[anim_idx].frames[i].width
frame.height = animations[anim_idx].frames[i].height
for frame_idx in range(anim.frames.size()):
for layer_idx in range(anim.frames[frame_idx].layers.size()):
var layer = animations[anim_idx].frames[frame_idx].layers[layer_idx]
var dup_layer = anim.frames[frame_idx].layers[layer_idx]
dup_layer.image.copy_from(layer.image)
dup_layer.update_texture()
func _on_animation_move(from, to):
var anim = animations[to]
animations[to] = animations[from]
animations[from] = anim
if current_animation_idx == from:
current_animation_idx = to
_update_anim_and_frame_idx(current_animation_idx, current_frame_idx)
elif current_animation_idx == to:
current_animation_idx = from
_update_anim_and_frame_idx(current_animation_idx, current_frame_idx)
############################################
# Save/Load
############################################
func get_save_project_data():
var data = {}
data["canvas"] = {
"width": paint_canvas.canvas_width,
"height": paint_canvas.canvas_height,
}
data["animations"] = {}
var anim_idx = 0
for anim in animations:
data["animations"][anim_idx] = {
"name": anim_panel.get_animation_stripe(anim_idx).get_animation_name(),
"frames": {}
}
var frame_idx = 0
for frame in anim.frames:
data["animations"][anim_idx]["frames"][frame_idx] = {
"layers": {}
}
var layer_idx = 0
for layer in frame.layers:
data["animations"][anim_idx]["frames"][frame_idx]["layers"][layer_idx] = {}
data["animations"][anim_idx]["frames"][frame_idx]["layers"][layer_idx] = {
"name": str(anim_idx) + " - "+ str(frame_idx) + " - " + str(layer_idx),
"image_data": Array(layer.image.save_png_to_buffer())
}
layer_idx += 1
frame.layers
frame_idx += 1
anim_idx += 1
return data
func load_project(data):
resize(data.canvas.width, data.canvas.height)
var layer_amount = 0
var anim_amount = 0
var frame_amounts = []
for anim in data.animations:
var frame_amount = 0
for frame in data.animations[anim].frames:
layer_amount = 0
for layer in data.animations[anim].frames[frame].layers:
layer_amount += 1
frame_amount += 1
frame_amounts.append(frame_amount)
anim_amount += 1
print("Loading...")
print("Animations: ", anim_amount)
print("Frames: ", frame_amounts)
print("Layers: ", layer_amount)
reset_project()
current_animation_idx = 0
current_frame_idx = 0
for i in range(layer_amount):
add_new_layer()
for anim_key in data.animations:
var frames_data = data.animations[anim_key].frames
var anim = add_new_animation()
var anim_stripe = anim_panel.get_last_animation_stripe()
if "name" in data.animations[anim_key]:
anim_stripe.set_animation_name(data.animations[anim_key].name)
for frame_key in frames_data:
if current_frame_idx > 0:
anim_stripe.add_new_frame_button()
var frame = add_new_frame_to_animation(anim)
var layer_data = frames_data[frame_key].layers
for layer_key in layer_data:
var new_img = layer_data[layer_key]
var img_data = Array(new_img.image_data)
frame.layers[int(layer_key)].image.load_png_from_buffer(img_data)
frame.layers[int(layer_key)].update_texture()
current_frame_idx += 1
current_frame_idx = 0
current_animation_idx += 1
for anim_idx in range(animations.size()):
if current_animation == null and not animations[anim_idx].frames.empty():
current_animation = animations[anim_idx]
current_animation_idx = anim_idx
current_frame_idx = 0
current_frame = animations[anim_idx].frames[current_frame_idx]
if get_layer_count() <= 0:
add_new_layer()
current_layer_idx = 0
paint_canvas.set_frame(current_frame)
preview_window.update_preview(current_frame)
func reset_project():
anim_panel.clear_all()
for layer_button in layer_buttons.get_children():
layer_buttons.remove_child(layer_button)
layer_button.queue_free()
animations.clear()
_total_added_layers = 0
for preview_texture in preview_layer_textures.get_children():
preview_layer_textures.remove_child(preview_texture)
preview_texture.queue_free()
paint_canvas.frame = null
paint_canvas.update()
current_animation = null
current_frame = null
current_layer_idx = -1
current_frame_idx = -1
current_animation_idx = -1
func delete_current_animation():
delete_animation(current_animation_idx)
func delete_animation(anim_idx):
animations.remove(anim_idx)
func delete_current_frame():
delete_frame(current_animation_idx, current_frame_idx)
func delete_frame(anim_idx, frame_idx):
if animations[anim_idx].frames.size() <= 1:
return
animations[anim_idx].frames.remove(frame_idx)
anim_panel.get_last_animation_stripe().remove_frame(frame_idx)
if current_frame_idx >= frame_idx:
current_frame_idx -= 1
_update_anim_and_frame_idx(current_animation_idx, current_frame_idx)
paint_canvas.set_frame(current_frame)
preview_window.update_preview(current_frame)
#######################################################
# Zoom & Scroll
#######################################################
func _handle_scroll():
if is_mouse_in_preview_window():
_handle_preview_scroll()
elif control_has_point(anim_panel, get_global_mouse_position()):
pass
elif control_has_point(self, get_global_mouse_position()):
_handle_canvas_scroll()
func _handle_preview_scroll():
if is_preview_dragging():
if Input.is_mouse_button_pressed(BUTTON_MIDDLE):
if not is_preview_dragging():
return
for child in preview_layer_textures.get_children():
child.rect_position = _preview_drag_start_pos
child.rect_position += get_global_mouse_position() - _preview_drag_pos
elif is_preview_dragging():
_preview_drag_start_pos = null
func _handle_canvas_scroll():
if is_dragging():
if Input.is_mouse_button_pressed(BUTTON_MIDDLE):
if not is_dragging():
return
paint_canvas.rect_position = _middle_mouse_pressed_start_pos
paint_canvas.rect_position += get_global_mouse_position() - _middle_mouse_pressed_pos
if paint_canvas.rect_position.y < -paint_canvas.rect_size.y:
paint_canvas.rect_position.y = -paint_canvas.rect_size.y
if paint_canvas.rect_position.x < -paint_canvas.rect_size.x:
paint_canvas.rect_position.x = -paint_canvas.rect_size.x
if paint_canvas.rect_position.x > rect_global_position.x + rect_size.x:
paint_canvas.rect_position.x = rect_global_position.x + rect_size.x
if paint_canvas.rect_position.y > rect_global_position.y + rect_size.y:
paint_canvas.rect_position.y = rect_global_position.y + rect_size.y
elif is_dragging():
_middle_mouse_pressed_start_pos = null
func is_dragging() -> bool:
return _middle_mouse_pressed_start_pos != null
func is_preview_dragging() -> bool:
return _preview_drag_start_pos != null
const max_zoom_out = 0.25
const max_zoom_in = 50
func _handle_zoom(event):
if not event is InputEventMouseButton:
return
if is_mouse_in_preview_window():
_handle_preview_zoom(event)
elif control_has_point(anim_panel, get_global_mouse_position()):
pass
elif is_mouse_in_canvas():
_handle_canvas_zoom(event)
func _handle_preview_zoom(event: InputEvent):
if event.is_pressed():
if event.button_index == BUTTON_WHEEL_UP:
set_preview_scale(_preview_scale * 2)
_update_preview_layers_size()
for child in preview_layer_textures.get_children():
child.rect_position -= child.get_local_mouse_position()
child.rect_position.x = clamp(child.rect_position.x,
-child.rect_size.x * 0.8,
rect_size.x)
child.rect_position.y = clamp(child.rect_position.y,
-child.rect_size.y * 0.8,
rect_size.y)
elif event.button_index == BUTTON_WHEEL_DOWN:
set_preview_scale(_preview_scale / 2)
_update_preview_layers_size()
for child in preview_layer_textures.get_children():
child.rect_position += child.get_local_mouse_position()
child.rect_position.x = clamp(child.rect_position.x,
-child.rect_size.x * 0.8,
rect_size.x)
child.rect_position.y = clamp(child.rect_position.y,
-child.rect_size.y * 0.8,
rect_size.y)
func _handle_canvas_zoom(event: InputEvent):
if event.is_pressed():
if event.button_index == BUTTON_WHEEL_UP:
var px = min(paint_canvas.pixel_size * 2, max_zoom_in)
if px == paint_canvas.pixel_size:
return
paint_canvas.set_pixel_size(px)
find_node("CanvasBackground").material.set_shader_param(
"pixel_size", 8 * pow(0.5, big_grid_pixels)/paint_canvas.pixel_size)
paint_canvas.rect_position -= paint_canvas.get_local_mouse_position()
paint_canvas.rect_position.x = clamp(paint_canvas.rect_position.x,
-paint_canvas.rect_size.x * 0.8,
rect_size.x)
paint_canvas.rect_position.y = clamp(paint_canvas.rect_position.y,
-paint_canvas.rect_size.y * 0.8,
rect_size.y)
elif event.button_index == BUTTON_WHEEL_DOWN:
var px = max(paint_canvas.pixel_size / 2.0, max_zoom_out)
if px == paint_canvas.pixel_size:
return
paint_canvas.set_pixel_size(px)
find_node("CanvasBackground").material.set_shader_param(
# 4 2 1
"pixel_size", 8 * pow(0.5, big_grid_pixels)/paint_canvas.pixel_size)
paint_canvas.rect_position += paint_canvas.get_local_mouse_position() / 2
paint_canvas.rect_position.x = clamp(paint_canvas.rect_position.x,
-paint_canvas.rect_size.x * 0.8,
rect_size.x)
paint_canvas.rect_position.y = clamp(paint_canvas.rect_position.y,
-paint_canvas.rect_size.y * 0.8,
rect_size.y)
func _handle_cut():
if Input.is_mouse_button_pressed(BUTTON_RIGHT):
paint_canvas.clear_preview_layer()
set_tool(_previous_tool)
return
if Input.is_mouse_button_pressed(BUTTON_LEFT):
for pixel_pos in GEUtils.get_pixels_in_line(cell_mouse_position, last_cell_mouse_position):
for idx in range(_selection_cells.size()):
var pixel = _selection_cells[idx]
var color = _selection_colors[idx]
pixel -= _cut_pos + _cut_size / 2
pixel += pixel_pos
paint_canvas.set_pixel_v(pixel, color)
else:
if _last_preview_draw_cell_pos == cell_mouse_position:
return
paint_canvas.clear_preview_layer()
for idx in range(_selection_cells.size()):
var pixel = _selection_cells[idx]
var color = _selection_colors[idx]
pixel -= _cut_pos + _cut_size / 2
pixel += cell_mouse_position
paint_canvas.set_preview_pixel_v(pixel, color)
_last_preview_draw_cell_pos = cell_mouse_position
func brush_process():
if Input.is_mouse_button_pressed(BUTTON_LEFT):
if _current_action == null:
_current_action = get_action()
if brush_mode == Tools.COLORPICKER:
_current_action = null
#TODO only draw if cell != last cell pos AND! already drawn
# -> last_drawn_at ..
# if cell_mouse_position == last_cell_mouse_position:
# return
match brush_mode:
Tools.PAINT:
do_action([cell_mouse_position, last_cell_mouse_position, selected_color])
Tools.BRUSH:
do_action([cell_mouse_position, last_cell_mouse_position, selected_color,
selected_brush_prefab, find_node("BrushSize").value])
Tools.LINE:
do_action([cell_mouse_position, last_cell_mouse_position, selected_color])
Tools.RECT:
do_action([cell_mouse_position, last_cell_mouse_position, selected_color])
Tools.DARKEN:
do_action([cell_mouse_position, last_cell_mouse_position, selected_color])
Tools.BRIGHTEN:
do_action([cell_mouse_position, last_cell_mouse_position, selected_color])
Tools.COLORPICKER:
pass
Tools.CUT:
do_action([cell_mouse_position, last_cell_mouse_position, selected_color])
Tools.PASTECUT:
do_action([cell_mouse_position, last_cell_mouse_position,
_selection_cells, _selection_colors,
_cut_pos, _cut_size])
Tools.RAINBOW:
do_action([cell_mouse_position, last_cell_mouse_position])
paint_canvas.update()
if current_frame:
current_frame.preview_dirty = true
elif Input.is_mouse_button_pressed(BUTTON_RIGHT):
paint_canvas.update()
if _current_action == null:
_current_action = get_action()
match brush_mode:
Tools.PAINT:
do_action([cell_mouse_position, last_cell_mouse_position, Color.transparent])
Tools.BRUSH:
do_action([cell_mouse_position, last_cell_mouse_position, Color.transparent,
selected_brush_prefab, find_node("BrushSize").value])
current_frame.preview_dirty = true
else:
if _current_action and _current_action.can_commit():
commit_action()
paint_canvas.update()
if current_frame:
current_frame.preview_dirty = true
paint_canvas.active_layer.update_texture()
func update_text_info():
var text = ""
var cell_color_text = cell_color
cell_color_text = Color(0, 0, 0, 0)
text += \
str("FPS %s\t" + \
"Mouse Position %s\t" + \
"Canvas Mouse Position %s \t" + \
"Canvas Position %s\t\n" + \
"Cell Position %s \t" + \
"Cell Color %s\t" + \
"inside canvas %s\t") % [
str(Engine.get_frames_per_second()),
str(mouse_position),
str(canvas_mouse_position),
str(canvas_position),
str(cell_mouse_position),
str(cell_color_text),
str(is_mouse_in_canvas())
]
debug_text.display_text(text)
func _on_Save_pressed():
get_node("SaveFileDialog").show()
##################################################
# Actions
##################################################
func do_action(data: Array):
if _current_action == null:
#print("clear redo")
_redo_history.clear()
_current_action.do_action(paint_canvas, data)
func commit_action():
if not _current_action:
return
var commit_data = _current_action.commit_action(paint_canvas)
var action = get_action()
action.action_data = _current_action.action_data.duplicate(true)
_actions_history.push_back(action)
_redo_history.clear()
match brush_mode:
Tools.CUT:
_cut_pos = _current_action.mouse_start_pos
_cut_size = _current_action.mouse_end_pos - _current_action.mouse_start_pos
_selection_cells = _current_action.action_data.redo.cells.duplicate()
_selection_colors = _current_action.action_data.redo.colors.duplicate()
set_tool(Tools.PASTECUT)
_:
_current_action = null
func redo_action():
if _redo_history.empty():
print("Godoxel: Nothing to redo.")
return
var action = _redo_history.pop_back()
if not action:
return
_actions_history.append(action)
action.redo_action(paint_canvas)
paint_canvas.update()
print("Godoxel: redo.")
func undo_action():
var action = _actions_history.pop_back()
if not action:
print("Godoxel: Nothing to undo.")
return
_redo_history.append(action)
action.undo_action(paint_canvas)
update()
paint_canvas.update()
print("Godoxel: undo.")
func get_action():
match brush_mode:
Tools.PAINT:
return GEPencil.new()
Tools.BRUSH:
return GEBrush.new()
Tools.LINE:
return GELine.new()
Tools.RAINBOW:
return GERainbow.new()
Tools.BUCKET:
return GEBucket.new()
Tools.RECT:
return GERect.new()
Tools.DARKEN:
return GEDarken.new()
Tools.BRIGHTEN:
return GEBrighten.new()
Tools.CUT:
return GECut.new()
Tools.PASTECUT:
return GEPasteCut.new()
_:
#print("no tool!")
return null
############################################
# Brushes
############################################
func set_selected_color(color):
selected_color = color
func set_tool(new_mode):
if brush_mode == new_mode:
return
_previous_tool = brush_mode
brush_mode = new_mode
_current_action = get_action()
match _previous_tool:
Tools.CUT:
paint_canvas.clear_preview_layer()
Tools.PASTECUT:
_selection_cells.clear()
_selection_colors.clear()
Tools.BUCKET:
_current_action = null
#print("Selected: ", Tools.keys()[brush_mode])
func change_color(new_color):
if new_color.a == 0:
return
selected_color = new_color
color_picker_button.color = selected_color
func _on_ColorPicker_color_changed(color):
selected_color = color
func _on_PaintTool_pressed():
set_tool(Tools.PAINT)
func _on_BucketTool_pressed():
set_tool(Tools.BUCKET)
func _on_RainbowTool_pressed():
set_tool(Tools.RAINBOW)
func _on_BrushTool_pressed():
set_tool(Tools.BRUSH)
func _on_LineTool_pressed():
set_tool(Tools.LINE)
func _on_RectTool_pressed():
set_tool(Tools.RECT)
func _on_DarkenTool_pressed():
set_tool(Tools.DARKEN)
func _on_BrightenTool_pressed():
set_tool(Tools.BRIGHTEN)
func _on_ColorPickerTool_pressed():
set_tool(Tools.COLORPICKER)
func _on_CutTool_pressed():
set_tool(Tools.CUT)
func _on_Editor_visibility_changed():
pause_mode = not visible
############################################
# Preview
############################################
func _adjust_preview_layer_size():
_preview_scale = max_preview_scale
_on_canvas_resized()
var width = paint_canvas.canvas_width
var height = paint_canvas.canvas_height
while _preview_scale > min_preview_scale:
if width * _preview_scale < preview_layer_textures.rect_size.x:
break
set_preview_scale(_preview_scale / 2)
while _preview_scale > min_preview_scale:
if height * _preview_scale < preview_layer_textures.rect_size.y:
break
set_preview_scale(_preview_scale / 2)
_update_preview_layers_size()
const max_preview_scale = pow(2, 10)
const min_preview_scale = pow(0.5, 10)
func set_preview_scale(new_scale: float):
_preview_scale = clamp(new_scale, min_preview_scale, max_preview_scale)
func _update_preview_layers_size():
for child in preview_layer_textures.get_children():
child.rect_scale.x = _preview_scale
child.rect_scale.y = _preview_scale
_center_element(child)
preview_window.set_title(str("Preview (x", _preview_scale, ")"))
func _add_preview_layer():
var preview_texture = TextureRect.new()
preview_texture.expand = false
preview_texture.mouse_filter = Control.MOUSE_FILTER_IGNORE
preview_layer_textures.add_child(preview_texture)
preview_texture.owner = owner
preview_texture.set_anchors_preset(Control.PRESET_CENTER)
_adjust_preview_layer_size()
# only updates preview atm
func _on_canvas_resized():
if not paint_canvas.frame:
return
for idx in range(preview_layer_textures.get_child_count()):
preview_layer_textures.get_child(idx).texture = paint_canvas.frame.layers[idx].texture
_center_element(preview_layer_textures.get_child(idx))
func _center_element(element: Control):
var width = element.get_rect().size.x / 2
var height = element.get_rect().size.y / 2
element.margin_left = -width
element.margin_top = -height
element.margin_right = width
element.margin_bottom = height
############################################
# Layer
############################################
func get_layer_count():
return _total_added_layers
func get_layer_name(index: int):
return layer_buttons.get_child(index).name
func resize(width: int, height: int):
paint_canvas.resize(width, height)
for anim in animations:
for frame in anim.frames:
frame.resize(width, height)
_adjust_preview_layer_size()
func highlight_layer(layer_name: String):
if animations.empty() or animations[0].frames.empty():
return
for button in layer_buttons.get_children():
if paint_canvas.find_layer_by_name(button.name).locked:
button.get("custom_styles/panel").set("bg_color", locked_layer_highlight)
elif button.name == layer_name:
button.get("custom_styles/panel").set("bg_color", current_layer_highlight)
else:
button.get("custom_styles/panel").set("bg_color", other_layer_highlight)
func toggle_layer_visibility(button, layer_name: String):
var index = paint_canvas.get_layer_index(layer_name)
preview_layer_textures.get_child(index).visible = paint_canvas.toggle_layer_visibility(layer_name)
for anim_stripe in anim_panel.anim_button_container.get_children():
for frame_button in anim_stripe.frame_button_container.get_children():
frame_button.set_layer_visibility(index, preview_layer_textures.get_child(index).visible)
frame_button.update_preview()
_update_frame_button_previews()
func select_layer(layer_button):
current_layer_idx = layer_button.get_index()
var layer = paint_canvas.find_layer_by_name(layer_button.name)
paint_canvas.select_layer(layer)
highlight_layer(layer_button.name)
func lock_layer(button, layer_name: String):
paint_canvas.toggle_lock_layer(layer_name)
highlight_layer(paint_canvas.get_active_layer().name)
func _add_layer_button():
var layer_button = LayerButton.instance()
layer_buttons.add_child(layer_button, true)
_total_added_layers += 1
layer_button.find_node("Select").text = "Layer " + str(get_layer_count())
_layer_button_ref[layer_button.name] = layer_button
_connect_layer_buttons()
return layer_button
func _create_layer(layer_name: String, width: int, height: int):
var layer: GELayer = GELayer.new()
layer.name = layer_name
var texture_rect = TextureRect.new()
texture_rect.expand = true
texture_rect.anchor_right = 1
texture_rect.anchor_bottom = 1
texture_rect.margin_right = 0
texture_rect.margin_bottom = 0
texture_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE
layer.create(texture_rect, width, height)
return layer
func add_new_layer():
var layer_button = _add_layer_button()
for anim in animations:
for frame in anim.frames:
var layer: GELayer = _create_layer(layer_button.name,
paint_canvas.canvas_width, paint_canvas.canvas_height)
frame.add_frame_layer(layer)
_add_preview_layer()
if current_frame:
preview_window.update_preview(current_frame)
paint_canvas.set_frame(current_frame)
highlight_layer(paint_canvas.get_active_layer().name)
_update_frame_button_previews()
func duplicate_active_layer():
var layer_button = _add_layer_button()
for anim in animations:
for frame in anim.frames:
var new_layer: GELayer = _create_layer(layer_button.name,
paint_canvas.canvas_width, paint_canvas.canvas_height)
new_layer.copy_from(frame.layers[paint_canvas.get_active_layer_index()])
frame.add_frame_layer(new_layer)
new_layer.update_texture()
_add_preview_layer()
if current_frame:
preview_window.update_preview(current_frame)
paint_canvas.set_frame(current_frame)
# update highlight
highlight_layer(paint_canvas.get_active_layer().name)
_update_frame_button_previews()
func remove_active_layer():
if layer_buttons.get_child_count() <= 1:
return
var layer_name = paint_canvas.active_layer.name
var index = paint_canvas.get_layer_index(paint_canvas.active_layer.name)
paint_canvas.remove_layer(layer_name)
layer_buttons.remove_child(_layer_button_ref[layer_name])
_layer_button_ref[layer_name].queue_free()
_layer_button_ref.erase(layer_name)
var preview_layer = preview_layer_textures.get_child(index)
preview_layer.remove_child(preview_layer)
preview_layer.queue_free()
_update_frame_button_previews()
highlight_layer(paint_canvas.get_active_layer().name)
func move_up(layer_btn):
var index = layer_btn.get_index()
var new_idx = min(index + 1, layer_buttons.get_child_count() - 1)
if index == new_idx:
return
# layer buttons
layer_buttons.move_child(layer_btn, new_idx)
# canvas
paint_canvas.move_layer_forward(layer_btn.name)
# preview window
preview_layer_textures.move_child(preview_layer_textures.get_child(index), new_idx)
# Frame previews
for anim_idx in range(animations.size()):
var anim = animations[anim_idx]
for frame_idx in range(anim.frames.size()):
var frame = anim.frames[frame_idx]
var layer = frame.layers[index]
frame.layers.remove(index)
frame.layers.insert(new_idx, layer)
anim_panel.set_frame_preview(anim_idx, frame_idx, frame)
_update_frame_button_previews()
func move_down(layer_btn):
var index = layer_btn.get_index()
var new_idx = max(index - 1, 0)
if index == new_idx:
return
# layer buttons
layer_buttons.move_child(layer_btn, new_idx)
# canvas
paint_canvas.move_layer_forward(layer_btn.name)
# preview window
preview_layer_textures.move_child(preview_layer_textures.get_child(index), new_idx)
# Frame previews
for anim_idx in range(animations.size()):
var anim = animations[anim_idx]
for frame_idx in range(anim.frames.size()):
var frame = anim.frames[frame_idx]
var layer = frame.layers[index]
frame.layers.remove(index)
frame.layers.insert(new_idx, layer)
anim_panel.set_frame_preview(anim_idx, frame_idx, frame)
_update_frame_button_previews()
func _connect_layer_buttons():
for layer_btn in layer_buttons.get_children():
if layer_btn.find_node("Select").is_connected("pressed", self, "select_layer"):
continue
layer_btn.find_node("Select").connect("pressed", self, "select_layer", [layer_btn])
layer_btn.find_node("Visible").connect("pressed", self, "toggle_layer_visibility",
[layer_btn.find_node("Visible"), layer_btn.name])
layer_btn.find_node("Up").connect("pressed", self, "move_down", [layer_btn])
layer_btn.find_node("Down").connect("pressed", self, "move_up", [layer_btn])
layer_btn.find_node("Lock").connect("pressed", self, "lock_layer",
[layer_btn, layer_btn.name])
func _on_AddNewLayer_pressed():
add_new_layer()
#_update_frame_button_previews()
func _update_frame_button_previews():
for anim_stripe in anim_panel.anim_button_container.get_children():
for frame_button in anim_stripe.frame_button_container.get_children():
frame_button.update_preview()
func _on_PaintCanvasContainer_mouse_entered():
if mouse_on_top:
return
mouse_on_top = true
paint_canvas.tool_layer.clear()
paint_canvas.update()
paint_canvas.tool_layer.update_texture()
func _on_PaintCanvasContainer_mouse_exited():
if not mouse_on_top:
return
mouse_on_top = false
paint_canvas.tool_layer.clear()
paint_canvas.update()
paint_canvas.tool_layer.update_texture()
func _on_ColorPicker_popup_closed():
find_node("Colors").add_color_prefab(color_picker_button.color)
############################################
# MISC
############################################
func is_position_in_canvas(pos):
if control_has_point(left_panel, pos):
return false
if control_has_point(right_panel, pos):
return false
if control_has_point(preview_window, pos):
return false
return control_has_point(paint_canvas_container_node, pos)
func control_has_point(control, point) -> bool:
if not control.visible:
return false
return Rect2(control.rect_global_position, control.rect_size).has_point(point)
func is_mouse_in_canvas() -> bool:
return is_position_in_canvas(get_global_mouse_position())
func is_mouse_in_preview_window() -> bool:
return control_has_point(preview_window, get_global_mouse_position())
func is_any_menu_open() -> bool:
return $ChangeCanvasSize.visible or \
$ChangeGridSizeDialog.visible or \
$Settings.visible or \
$LoadFileDialog.visible or \
$SaveFileDialog.visible or \
find_node("Navbar").is_any_menu_open()
func _on_LockAlpha_pressed():
var checked = find_node("LockAlpha").pressed
paint_canvas.active_layer.toggle_alpha_locked()
for i in range(find_node("Layer").get_popup().get_item_count()):
if find_node("Layer").get_popup().get_item_text(i) == "Toggle Alpha Locked":
find_node("Layer").get_popup().set_item_checked(i,
not find_node("Layer").get_popup().is_item_checked(i))
func _on_BrushRect_pressed():
if brush_mode != Tools.BRUSH:
set_tool(Tools.BRUSH)
selected_brush_prefab = BrushPrefabs.Type.RECT
func _on_BrushCircle_pressed():
if brush_mode != Tools.BRUSH:
set_tool(Tools.BRUSH)
selected_brush_prefab = BrushPrefabs.Type.CIRCLE
func _on_BrushVLine_pressed():
if brush_mode != Tools.BRUSH:
set_tool(Tools.BRUSH)
selected_brush_prefab = BrushPrefabs.Type.V_LINE
func _on_BrushHLine_pressed():
if brush_mode != Tools.BRUSH:
set_tool(Tools.BRUSH)
selected_brush_prefab = BrushPrefabs.Type.H_LINE
func _on_BrushSize_value_changed(value: float):
find_node("BrushSizeLabel").text = str(int(value))
func _on_XSymmetry_pressed():
paint_canvas.symmetry_x = not paint_canvas.symmetry_x
func _on_YSymmetry_pressed():
paint_canvas.symmetry_y = not paint_canvas.symmetry_y