1 /*************************************************************************/
2 /*  particles_2d_editor_plugin.cpp                                       */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
10 /*                                                                       */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the       */
13 /* "Software"), to deal in the Software without restriction, including   */
14 /* without limitation the rights to use, copy, modify, merge, publish,   */
15 /* distribute, sublicense, and/or sell copies of the Software, and to    */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions:                                             */
18 /*                                                                       */
19 /* The above copyright notice and this permission notice shall be        */
20 /* included in all copies or substantial portions of the Software.       */
21 /*                                                                       */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
29 /*************************************************************************/
30 
31 #include "particles_2d_editor_plugin.h"
32 #include "canvas_item_editor_plugin.h"
33 #include "io/image_loader.h"
34 #include "scene/gui/separator.h"
35 
edit(Object * p_object)36 void Particles2DEditorPlugin::edit(Object *p_object) {
37 
38 	if (p_object) {
39 		particles = p_object->cast_to<Particles2D>();
40 	} else {
41 		particles = NULL;
42 	}
43 }
44 
handles(Object * p_object) const45 bool Particles2DEditorPlugin::handles(Object *p_object) const {
46 
47 	return p_object->is_type("Particles2D");
48 }
49 
make_visible(bool p_visible)50 void Particles2DEditorPlugin::make_visible(bool p_visible) {
51 
52 	if (p_visible) {
53 
54 		toolbar->show();
55 	} else {
56 
57 		toolbar->hide();
58 	}
59 }
60 
_file_selected(const String & p_file)61 void Particles2DEditorPlugin::_file_selected(const String &p_file) {
62 
63 	print_line("file: " + p_file);
64 
65 	int epc = epoints->get_val();
66 
67 	Image img;
68 	Error err = ImageLoader::load_image(p_file, &img);
69 	ERR_EXPLAIN(TTR("Error loading image:") + " " + p_file);
70 	ERR_FAIL_COND(err != OK);
71 
72 	img.convert(Image::FORMAT_GRAYSCALE_ALPHA);
73 	ERR_FAIL_COND(img.get_format() != Image::FORMAT_GRAYSCALE_ALPHA);
74 	Size2i s = Size2(img.get_width(), img.get_height());
75 	ERR_FAIL_COND(s.width == 0 || s.height == 0);
76 
77 	DVector<uint8_t> data = img.get_data();
78 	DVector<uint8_t>::Read r = data.read();
79 
80 	Vector<Point2i> valid_positions;
81 	valid_positions.resize(s.width * s.height);
82 	int vpc = 0;
83 
84 	for (int i = 0; i < s.width * s.height; i++) {
85 
86 		uint8_t a = r[i * 2 + 1];
87 		if (a > 128) {
88 			valid_positions[vpc++] = Point2i(i % s.width, i / s.width);
89 		}
90 	}
91 
92 	valid_positions.resize(vpc);
93 
94 	ERR_EXPLAIN(TTR("No pixels with transparency > 128 in image.."));
95 	ERR_FAIL_COND(valid_positions.size() == 0);
96 
97 	DVector<Point2> epoints;
98 	epoints.resize(epc);
99 	DVector<Point2>::Write w = epoints.write();
100 
101 	Size2 extents = Size2(img.get_width() * 0.5, img.get_height() * 0.5);
102 
103 	for (int i = 0; i < epc; i++) {
104 
105 		Point2 p = valid_positions[Math::rand() % vpc];
106 		p -= s / 2;
107 		w[i] = p / extents;
108 	}
109 
110 	w = DVector<Point2>::Write();
111 
112 	undo_redo->create_action(TTR("Set Emission Mask"));
113 	undo_redo->add_do_method(particles, "set_emission_points", epoints);
114 	undo_redo->add_do_method(particles, "set_emission_half_extents", extents);
115 	undo_redo->add_undo_method(particles, "set_emission_points", particles->get_emission_points());
116 	undo_redo->add_undo_method(particles, "set_emission_half_extents", particles->get_emission_half_extents());
117 	undo_redo->commit_action();
118 }
119 
_menu_callback(int p_idx)120 void Particles2DEditorPlugin::_menu_callback(int p_idx) {
121 
122 	switch (p_idx) {
123 		case MENU_LOAD_EMISSION_MASK: {
124 
125 			file->popup_centered_ratio();
126 
127 		} break;
128 		case MENU_CLEAR_EMISSION_MASK: {
129 
130 			undo_redo->create_action(TTR("Clear Emission Mask"));
131 			undo_redo->add_do_method(particles, "set_emission_points", DVector<Vector2>());
132 			undo_redo->add_undo_method(particles, "set_emission_points", particles->get_emission_points());
133 			undo_redo->commit_action();
134 		} break;
135 	}
136 }
137 
_notification(int p_what)138 void Particles2DEditorPlugin::_notification(int p_what) {
139 
140 	if (p_what == NOTIFICATION_ENTER_TREE) {
141 
142 		menu->get_popup()->connect("item_pressed", this, "_menu_callback");
143 		menu->set_icon(menu->get_popup()->get_icon("Particles2D", "EditorIcons"));
144 		file->connect("file_selected", this, "_file_selected");
145 	}
146 }
147 
_bind_methods()148 void Particles2DEditorPlugin::_bind_methods() {
149 
150 	ObjectTypeDB::bind_method(_MD("_menu_callback"), &Particles2DEditorPlugin::_menu_callback);
151 	ObjectTypeDB::bind_method(_MD("_file_selected"), &Particles2DEditorPlugin::_file_selected);
152 }
153 
Particles2DEditorPlugin(EditorNode * p_node)154 Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) {
155 
156 	particles = NULL;
157 	editor = p_node;
158 	undo_redo = editor->get_undo_redo();
159 
160 	toolbar = memnew(HBoxContainer);
161 	add_control_to_container(CONTAINER_CANVAS_EDITOR_MENU, toolbar);
162 	toolbar->hide();
163 
164 	toolbar->add_child(memnew(VSeparator));
165 
166 	menu = memnew(MenuButton);
167 	menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK);
168 	menu->get_popup()->add_item(TTR("Clear Emission Mask"), MENU_CLEAR_EMISSION_MASK);
169 	menu->set_text("Particles");
170 	toolbar->add_child(menu);
171 
172 	file = memnew(EditorFileDialog);
173 	List<String> ext;
174 	ImageLoader::get_recognized_extensions(&ext);
175 	for (List<String>::Element *E = ext.front(); E; E = E->next()) {
176 		file->add_filter("*." + E->get() + "; " + E->get().to_upper());
177 	}
178 	file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
179 	toolbar->add_child(file);
180 
181 	epoints = memnew(SpinBox);
182 	epoints->set_min(1);
183 	epoints->set_max(8192);
184 	epoints->set_step(1);
185 	epoints->set_val(512);
186 	file->get_vbox()->add_margin_child(TTR("Generated Point Count:"), epoints);
187 }
188 
~Particles2DEditorPlugin()189 Particles2DEditorPlugin::~Particles2DEditorPlugin() {
190 }
191