Explorar o código

Ability to create sprite frames in AnimatedSprite from sprite sheet.

Juan Linietsky %!s(int64=6) %!d(string=hai) anos
pai
achega
3f76d2c2f3

+ 61 - 0
editor/icons/icon_sprite_sheet.svg

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   version="1.1"
+   viewBox="0 0 16 16"
+   id="svg6"
+   sodipodi:docname="icon_sprite_sheet.svg"
+   inkscape:version="0.92.3 (2405546, 2018-03-11)">
+  <metadata
+     id="metadata12">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs10" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="773"
+     inkscape:window-height="480"
+     id="namedview8"
+     showgrid="false"
+     inkscape:zoom="14.75"
+     inkscape:cx="8"
+     inkscape:cy="8"
+     inkscape:window-x="551"
+     inkscape:window-y="278"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="g4" />
+  <g
+     transform="translate(0 -1036.4)"
+     id="g4">
+    <path
+       transform="translate(0 1036.4)"
+       d="m3 1c-1.1046 0-2 0.89543-2 2v10c0 1.1046 0.89543 2 2 2h10c1.1046 0 2-0.89543 2-2v-10c0-1.1046-0.89543-2-2-2h-10zm0 2h2v2h-2v-2zm4 0h2v2h-2v-2zm4 0h2v2h-2v-2zm-8 4h2v2h-2v-2zm4 0h2v2h-2v-2zm4 0h2v2h-2v-2zm-8 4h2v2h-2v-2zm4 0h2v2h-2v-2zm4 0h2v2h-2v-2z"
+       fill="#a5efac"
+       id="path2"
+       style="fill:#e0e0e0;fill-opacity:1" />
+  </g>
+</svg>

+ 228 - 2
editor/plugins/sprite_frames_editor_plugin.cpp

@@ -38,11 +38,167 @@
 void SpriteFramesEditor::_gui_input(Ref<InputEvent> p_event) {
 }
 
+void SpriteFramesEditor::_open_sprite_sheet() {
+
+	file_split_sheet->clear_filters();
+	List<String> extensions;
+	ResourceLoader::get_recognized_extensions_for_type("Texture", &extensions);
+	for (int i = 0; i < extensions.size(); i++) {
+		file_split_sheet->add_filter("*." + extensions[i]);
+	}
+
+	file_split_sheet->popup_centered_ratio();
+}
+
+void SpriteFramesEditor::_sheet_preview_draw() {
+	Size2i size = split_sheet_preview->get_size();
+	int h = split_sheet_h->get_value();
+	int v = split_sheet_v->get_value();
+	const float a = 0.3;
+	for (int i = 1; i < h; i++) {
+		for (int j = 1; j < v; j++) {
+
+			int x = i * size.width / h;
+			int y = i * size.height / v;
+
+			split_sheet_preview->draw_line(Point2(x, 0), Point2(x, size.height), Color(1, 1, 1, a));
+			split_sheet_preview->draw_line(Point2(x + 1, 0), Point2(x + 1, size.height), Color(0, 0, 0, a));
+
+			split_sheet_preview->draw_line(Point2(0, y), Point2(size.width, y), Color(1, 1, 1, a));
+			split_sheet_preview->draw_line(Point2(0, y + 1), Point2(size.width, y + 1), Color(0, 0, 0, a));
+		}
+	}
+
+	Color accent = get_color("accent_color", "Editor");
+
+	for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) {
+		int idx = E->get();
+		int x = (idx % h) * size.width / h;
+		int y = (idx / v) * size.height / v;
+		int width = size.width / h;
+		int height = size.height / v;
+
+		split_sheet_preview->draw_rect(Rect2(x + 5, y + 5, width - 10, height - 10), Color(0, 0, 0, 0.35), true);
+		split_sheet_preview->draw_rect(Rect2(x + 0, y + 0, width - 0, height - 0), Color(0, 0, 0, 1), false);
+		split_sheet_preview->draw_rect(Rect2(x + 1, y + 1, width - 2, height - 2), Color(0, 0, 0, 1), false);
+		split_sheet_preview->draw_rect(Rect2(x + 2, y + 2, width - 4, height - 4), accent, false);
+		split_sheet_preview->draw_rect(Rect2(x + 3, y + 3, width - 6, height - 6), accent, false);
+		split_sheet_preview->draw_rect(Rect2(x + 4, y + 4, width - 8, height - 8), Color(0, 0, 0, 1), false);
+		split_sheet_preview->draw_rect(Rect2(x + 5, y + 5, width - 10, height - 10), Color(0, 0, 0, 1), false);
+	}
+
+	if (frames_selected.size() == 0) {
+		split_sheet_dialog->get_ok()->set_disabled(true);
+		split_sheet_dialog->get_ok()->set_text(TTR("No frames selected"));
+	} else {
+		split_sheet_dialog->get_ok()->set_disabled(false);
+		split_sheet_dialog->get_ok()->set_text(vformat(TTR("Add %d frame(s)"), frames_selected.size()));
+	}
+}
+void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) {
+
+	Ref<InputEventMouseButton> mb = p_event;
+
+	if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+		Size2i size = split_sheet_preview->get_size();
+		int h = split_sheet_h->get_value();
+		int v = split_sheet_v->get_value();
+
+		int x = CLAMP(int(mb->get_position().x) * h / size.width, 0, h - 1);
+		int y = CLAMP(int(mb->get_position().y) * v / size.height, 0, v - 1);
+
+		int idx = h * y + x;
+
+		if (mb->get_shift() && last_frame_selected >= 0) {
+			//select multiple
+			int from = idx;
+			int to = last_frame_selected;
+			if (from > to) {
+				SWAP(from, to);
+			}
+
+			for (int i = from; i <= to; i++) {
+				if (mb->get_control()) {
+					frames_selected.erase(i);
+				} else {
+					frames_selected.insert(i);
+				}
+			}
+		} else {
+			if (frames_selected.has(idx)) {
+				frames_selected.erase(idx);
+			} else {
+				frames_selected.insert(idx);
+			}
+		}
+
+		last_frame_selected = idx;
+		split_sheet_preview->update();
+	}
+}
+
+void SpriteFramesEditor::_sheet_add_frames() {
+
+	Size2i size = split_sheet_preview->get_size();
+	int h = split_sheet_h->get_value();
+	int v = split_sheet_v->get_value();
+
+	undo_redo->create_action(TTR("Add Frame"));
+
+	int fc = frames->get_frame_count(edited_anim);
+
+	for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) {
+		int idx = E->get();
+		int x = (idx % h) * size.width / h;
+		int y = (idx / v) * size.height / v;
+		int width = size.width / h;
+		int height = size.height / v;
+
+		Ref<AtlasTexture> at;
+		at.instance();
+		at->set_atlas(split_sheet_preview->get_texture());
+		at->set_region(Rect2(x, y, width, height));
+
+		undo_redo->add_do_method(frames, "add_frame", edited_anim, at, -1);
+		undo_redo->add_undo_method(frames, "remove_frame", edited_anim, fc);
+	}
+
+	undo_redo->add_do_method(this, "_update_library");
+	undo_redo->add_undo_method(this, "_update_library");
+	undo_redo->commit_action();
+}
+
+void SpriteFramesEditor::_sheet_spin_changed(double) {
+	frames_selected.clear();
+	last_frame_selected = -1;
+	split_sheet_preview->update();
+}
+
+void SpriteFramesEditor::_prepare_sprite_sheet(const String &p_file) {
+
+	Ref<Resource> texture = ResourceLoader::load(p_file);
+	if (!texture.is_valid()) {
+		EditorNode::get_singleton()->show_warning("Unable to load images");
+		ERR_FAIL_COND(!texture.is_valid());
+	}
+	if (texture != split_sheet_preview->get_texture()) {
+		//different texture, reset to 4x4
+		split_sheet_h->set_value(4);
+		split_sheet_v->set_value(4);
+	}
+	frames_selected.clear();
+	last_frame_selected = -1;
+
+	split_sheet_preview->set_texture(texture);
+	split_sheet_dialog->popup_centered_ratio(0.65);
+}
+
 void SpriteFramesEditor::_notification(int p_what) {
 
 	if (p_what == NOTIFICATION_ENTER_TREE) {
 
 		load->set_icon(get_icon("Load", "EditorIcons"));
+		load_sheet->set_icon(get_icon("SpriteSheet", "EditorIcons"));
 		copy->set_icon(get_icon("ActionCopy", "EditorIcons"));
 		paste->set_icon(get_icon("ActionPaste", "EditorIcons"));
 		empty->set_icon(get_icon("InsertBefore", "EditorIcons"));
@@ -72,6 +228,7 @@ void SpriteFramesEditor::_file_load_request(const PoolVector<String> &p_path, in
 		if (resource.is_null()) {
 			dialog->set_text(TTR("ERROR: Couldn't load frame resource!"));
 			dialog->set_title(TTR("Error!"));
+
 			//dialog->get_cancel()->set_text("Close");
 			dialog->get_ok()->set_text(TTR("Close"));
 			dialog->popup_centered_minsize();
@@ -655,6 +812,12 @@ void SpriteFramesEditor::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &SpriteFramesEditor::get_drag_data_fw);
 	ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &SpriteFramesEditor::can_drop_data_fw);
 	ClassDB::bind_method(D_METHOD("drop_data_fw"), &SpriteFramesEditor::drop_data_fw);
+	ClassDB::bind_method(D_METHOD("_prepare_sprite_sheet"), &SpriteFramesEditor::_prepare_sprite_sheet);
+	ClassDB::bind_method(D_METHOD("_open_sprite_sheet"), &SpriteFramesEditor::_open_sprite_sheet);
+	ClassDB::bind_method(D_METHOD("_sheet_preview_draw"), &SpriteFramesEditor::_sheet_preview_draw);
+	ClassDB::bind_method(D_METHOD("_sheet_preview_input"), &SpriteFramesEditor::_sheet_preview_input);
+	ClassDB::bind_method(D_METHOD("_sheet_spin_changed"), &SpriteFramesEditor::_sheet_spin_changed);
+	ClassDB::bind_method(D_METHOD("_sheet_add_frames"), &SpriteFramesEditor::_sheet_add_frames);
 }
 
 SpriteFramesEditor::SpriteFramesEditor() {
@@ -712,9 +875,15 @@ SpriteFramesEditor::SpriteFramesEditor() {
 	sub_vb->add_child(hbc);
 
 	load = memnew(ToolButton);
-	load->set_tooltip(TTR("Load Resource"));
+	load->set_tooltip(TTR("Add a Texture from File"));
 	hbc->add_child(load);
 
+	load_sheet = memnew(ToolButton);
+	load_sheet->set_tooltip(TTR("Add frames from a Sprite Sheet"));
+	hbc->add_child(load_sheet);
+
+	hbc->add_child(memnew(VSeparator));
+
 	copy = memnew(ToolButton);
 	copy->set_tooltip(TTR("Copy"));
 	hbc->add_child(copy);
@@ -723,6 +892,8 @@ SpriteFramesEditor::SpriteFramesEditor() {
 	paste->set_tooltip(TTR("Paste"));
 	hbc->add_child(paste);
 
+	hbc->add_spacer(false);
+
 	empty = memnew(ToolButton);
 	empty->set_tooltip(TTR("Insert Empty (Before)"));
 	hbc->add_child(empty);
@@ -731,7 +902,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
 	empty2->set_tooltip(TTR("Insert Empty (After)"));
 	hbc->add_child(empty2);
 
-	hbc->add_spacer(false);
+	hbc->add_child(memnew(VSeparator));
 
 	move_up = memnew(ToolButton);
 	move_up->set_tooltip(TTR("Move (Before)"));
@@ -766,6 +937,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
 	add_child(dialog);
 
 	load->connect("pressed", this, "_load_pressed");
+	load_sheet->connect("pressed", this, "_open_sprite_sheet");
 	_delete->connect("pressed", this, "_delete_pressed");
 	copy->connect("pressed", this, "_copy_pressed");
 	paste->connect("pressed", this, "_paste_pressed");
@@ -780,6 +952,60 @@ SpriteFramesEditor::SpriteFramesEditor() {
 	updating = false;
 
 	edited_anim = "default";
+
+	split_sheet_dialog = memnew(ConfirmationDialog);
+	add_child(split_sheet_dialog);
+	VBoxContainer *split_sheet_vb = memnew(VBoxContainer);
+	split_sheet_dialog->add_child(split_sheet_vb);
+	split_sheet_dialog->set_title(TTR("Select Frames"));
+	split_sheet_dialog->connect("confirmed", this, "_sheet_add_frames");
+
+	ScrollContainer *scroll = memnew(ScrollContainer);
+	split_sheet_preview = memnew(TextureRect);
+	split_sheet_preview->set_expand(false);
+	split_sheet_preview->set_mouse_filter(MOUSE_FILTER_PASS);
+	split_sheet_preview->connect("draw", this, "_sheet_preview_draw");
+	split_sheet_preview->connect("gui_input", this, "_sheet_preview_input");
+
+	scroll->set_enable_h_scroll(true);
+	scroll->set_enable_v_scroll(true);
+	CenterContainer *cc = memnew(CenterContainer);
+	cc->add_child(split_sheet_preview);
+	cc->set_h_size_flags(SIZE_EXPAND_FILL);
+	cc->set_v_size_flags(SIZE_EXPAND_FILL);
+	scroll->add_child(cc);
+
+	split_sheet_vb->add_margin_child(TTR("Base Image:"), scroll, true);
+
+	HBoxContainer *split_sheet_hb = memnew(HBoxContainer);
+	split_sheet_hb->add_spacer();
+	Label *ss_label = memnew(Label(TTR("Horizontal:")));
+	split_sheet_hb->add_child(ss_label);
+	split_sheet_h = memnew(SpinBox);
+	split_sheet_h->set_min(1);
+	split_sheet_h->set_max(128);
+	split_sheet_h->set_step(1);
+	split_sheet_hb->add_child(split_sheet_h);
+	split_sheet_hb->add_spacer();
+	split_sheet_h->connect("value_changed", this, "_sheet_spin_changed");
+
+	ss_label = memnew(Label(TTR("Vertical:")));
+	split_sheet_hb->add_child(ss_label);
+	split_sheet_v = memnew(SpinBox);
+	split_sheet_v->set_min(1);
+	split_sheet_v->set_max(128);
+	split_sheet_v->set_step(1);
+	split_sheet_hb->add_child(split_sheet_v);
+	split_sheet_hb->add_spacer();
+	split_sheet_v->connect("value_changed", this, "_sheet_spin_changed");
+
+	split_sheet_vb->add_margin_child(TTR("Split Settings:"), split_sheet_hb);
+
+	file_split_sheet = memnew(EditorFileDialog);
+	file_split_sheet->set_title(TTR("Create frames from Sprite Sheet"));
+	file_split_sheet->set_mode(EditorFileDialog::MODE_OPEN_FILE);
+	add_child(file_split_sheet);
+	file_split_sheet->connect("file_selected", this, "_prepare_sprite_sheet");
 }
 
 void SpriteFramesEditorPlugin::edit(Object *p_object) {

+ 17 - 0
editor/plugins/sprite_frames_editor_plugin.h

@@ -37,6 +37,7 @@
 #include "scene/gui/dialogs.h"
 #include "scene/gui/file_dialog.h"
 #include "scene/gui/split_container.h"
+#include "scene/gui/texture_rect.h"
 #include "scene/gui/tree.h"
 
 class SpriteFramesEditor : public HSplitContainer {
@@ -44,6 +45,7 @@ class SpriteFramesEditor : public HSplitContainer {
 	GDCLASS(SpriteFramesEditor, HSplitContainer);
 
 	ToolButton *load;
+	ToolButton *load_sheet;
 	ToolButton *_delete;
 	ToolButton *copy;
 	ToolButton *paste;
@@ -71,6 +73,14 @@ class SpriteFramesEditor : public HSplitContainer {
 
 	StringName edited_anim;
 
+	ConfirmationDialog *split_sheet_dialog;
+	TextureRect *split_sheet_preview;
+	SpinBox *split_sheet_h;
+	SpinBox *split_sheet_v;
+	EditorFileDialog *file_split_sheet;
+	Set<int> frames_selected;
+	int last_frame_selected;
+
 	void _load_pressed();
 	void _load_scene_pressed();
 	void _file_load_request(const PoolVector<String> &p_path, int p_at_pos = -1);
@@ -99,6 +109,13 @@ class SpriteFramesEditor : public HSplitContainer {
 	bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
 	void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
 
+	void _open_sprite_sheet();
+	void _prepare_sprite_sheet(const String &p_file);
+	void _sheet_preview_draw();
+	void _sheet_spin_changed(double);
+	void _sheet_preview_input(const Ref<InputEvent> &p_event);
+	void _sheet_add_frames();
+
 protected:
 	void _notification(int p_what);
 	void _gui_input(Ref<InputEvent> p_event);