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