Skip to content

Commit 6feadd1

Browse files
committed
Add emission shape gizmos to Particles2D
1 parent a63a8b4 commit 6feadd1

6 files changed

+177
-19
lines changed

editor/plugins/particles_editor_plugin.cpp

+16-9
Original file line numberDiff line numberDiff line change
@@ -291,22 +291,29 @@ Particles2DEditorPlugin::Particles2DEditorPlugin() {
291291
emission_mask->connect(SceneStringName(confirmed), callable_mp(this, &Particles2DEditorPlugin::_generate_emission_mask));
292292
}
293293

294-
void GPUParticles2DEditorPlugin::_selection_changed() {
294+
void Particles2DEditorPlugin::_selection_changed() {
295295
List<Node *> selected_nodes = EditorNode::get_singleton()->get_editor_selection()->get_selected_node_list();
296296
if (selected_particles.is_empty() && selected_nodes.is_empty()) {
297297
return;
298298
}
299299

300-
for (GPUParticles2D *particles : selected_particles) {
301-
particles->set_show_visibility_rect(false);
300+
for (Node *particles : selected_particles) {
301+
if (GPUParticles2D *gpu_particles = Object::cast_to<GPUParticles2D>(particles)) {
302+
gpu_particles->set_show_gizmos(false);
303+
} else if (CPUParticles2D *cpu_particles = Object::cast_to<CPUParticles2D>(particles)) {
304+
cpu_particles->set_show_gizmos(false);
305+
}
302306
}
307+
303308
selected_particles.clear();
304309

305310
for (Node *node : selected_nodes) {
306-
GPUParticles2D *selected_particle = Object::cast_to<GPUParticles2D>(node);
307-
if (selected_particle) {
308-
selected_particle->set_show_visibility_rect(true);
309-
selected_particles.push_back(selected_particle);
311+
if (GPUParticles2D *selected_gpu_particle = Object::cast_to<GPUParticles2D>(node)) {
312+
selected_gpu_particle->set_show_gizmos(true);
313+
selected_particles.push_back(selected_gpu_particle);
314+
} else if (CPUParticles2D *selected_cpu_particle = Object::cast_to<CPUParticles2D>(node)) {
315+
selected_cpu_particle->set_show_gizmos(true);
316+
selected_particles.push_back(selected_cpu_particle);
310317
}
311318
}
312319
}
@@ -353,10 +360,10 @@ void GPUParticles2DEditorPlugin::_generate_visibility_rect() {
353360
undo_redo->commit_action();
354361
}
355362

356-
void GPUParticles2DEditorPlugin::_notification(int p_what) {
363+
void Particles2DEditorPlugin::_notification(int p_what) {
357364
switch (p_what) {
358365
case NOTIFICATION_ENTER_TREE: {
359-
EditorNode::get_singleton()->get_editor_selection()->connect("selection_changed", callable_mp(this, &GPUParticles2DEditorPlugin::_selection_changed));
366+
EditorNode::get_singleton()->get_editor_selection()->connect("selection_changed", callable_mp(this, &Particles2DEditorPlugin::_selection_changed));
360367
} break;
361368
}
362369
}

editor/plugins/particles_editor_plugin.h

+5-5
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class CheckBox;
3737
class ConfirmationDialog;
3838
class EditorFileDialog;
3939
class GPUParticles2D;
40+
class CPUParticles2D;
4041
class HBoxContainer;
4142
class MenuButton;
4243
class OptionButton;
@@ -87,6 +88,8 @@ class Particles2DEditorPlugin : public ParticlesEditorPlugin {
8788
MENU_LOAD_EMISSION_MASK = 100,
8889
};
8990

91+
List<Node *> selected_particles;
92+
9093
enum EmissionMode {
9194
EMISSION_MODE_SOLID,
9295
EMISSION_MODE_BORDER,
@@ -106,6 +109,8 @@ class Particles2DEditorPlugin : public ParticlesEditorPlugin {
106109
void _file_selected(const String &p_file);
107110
void _get_base_emission_mask(PackedVector2Array &r_valid_positions, PackedVector2Array &r_valid_normals, PackedByteArray &r_valid_colors, Vector2i &r_image_size);
108111
virtual void _generate_emission_mask() = 0;
112+
void _notification(int p_what);
113+
void _selection_changed();
109114

110115
public:
111116
Particles2DEditorPlugin();
@@ -118,17 +123,12 @@ class GPUParticles2DEditorPlugin : public Particles2DEditorPlugin {
118123
MENU_GENERATE_VISIBILITY_RECT = 200,
119124
};
120125

121-
List<GPUParticles2D *> selected_particles;
122-
123126
ConfirmationDialog *generate_visibility_rect = nullptr;
124127
SpinBox *generate_seconds = nullptr;
125128

126-
void _selection_changed();
127129
void _generate_visibility_rect();
128130

129131
protected:
130-
void _notification(int p_what);
131-
132132
void _menu_callback(int p_idx) override;
133133
void _add_menu_options(PopupMenu *p_menu) override;
134134

scene/2d/cpu_particles_2d.cpp

+69
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ void CPUParticles2D::set_use_local_coordinates(bool p_enable) {
105105
// We only need NOTIFICATION_TRANSFORM_CHANGED
106106
// when following an interpolated target.
107107
set_notify_transform(_interpolation_data.interpolated_follow);
108+
109+
#ifdef TOOLS_ENABLED
110+
if (Engine::get_singleton()->is_editor_hint()) {
111+
set_notify_transform(_interpolation_data.interpolated_follow || !local_coords);
112+
}
113+
#endif
114+
115+
queue_redraw();
108116
}
109117

110118
void CPUParticles2D::set_speed_scale(double p_scale) {
@@ -461,14 +469,35 @@ void CPUParticles2D::set_emission_shape(EmissionShape p_shape) {
461469
ERR_FAIL_INDEX(p_shape, EMISSION_SHAPE_MAX);
462470
emission_shape = p_shape;
463471
notify_property_list_changed();
472+
#ifdef TOOLS_ENABLED
473+
if (Engine::get_singleton()->is_editor_hint()) {
474+
queue_redraw();
475+
}
476+
#endif
464477
}
465478

466479
void CPUParticles2D::set_emission_sphere_radius(real_t p_radius) {
480+
if (p_radius == emission_sphere_radius) {
481+
return;
482+
}
467483
emission_sphere_radius = p_radius;
484+
#ifdef TOOLS_ENABLED
485+
if (Engine::get_singleton()->is_editor_hint()) {
486+
queue_redraw();
487+
}
488+
#endif
468489
}
469490

470491
void CPUParticles2D::set_emission_rect_extents(Vector2 p_extents) {
492+
if (p_extents == emission_rect_extents) {
493+
return;
494+
}
471495
emission_rect_extents = p_extents;
496+
#ifdef TOOLS_ENABLED
497+
if (Engine::get_singleton()->is_editor_hint()) {
498+
queue_redraw();
499+
}
500+
#endif
472501
}
473502

474503
void CPUParticles2D::set_emission_points(const Vector<Vector2> &p_points) {
@@ -556,6 +585,13 @@ void CPUParticles2D::set_seed(uint32_t p_seed) {
556585
seed = p_seed;
557586
}
558587

588+
#ifdef TOOLS_ENABLED
589+
void CPUParticles2D::set_show_gizmos(bool p_show_gizmos) {
590+
show_gizmos = p_show_gizmos;
591+
queue_redraw();
592+
}
593+
#endif
594+
559595
uint32_t CPUParticles2D::get_seed() const {
560596
return seed;
561597
}
@@ -1211,6 +1247,13 @@ void CPUParticles2D::_notification(int p_what) {
12111247
}
12121248

12131249
RS::get_singleton()->canvas_item_add_multimesh(get_canvas_item(), multimesh, texrid);
1250+
1251+
#ifdef TOOLS_ENABLED
1252+
if (show_gizmos) {
1253+
_draw_emission_gizmo();
1254+
}
1255+
#endif
1256+
12141257
} break;
12151258

12161259
case NOTIFICATION_INTERNAL_PROCESS: {
@@ -1232,6 +1275,11 @@ void CPUParticles2D::_notification(int p_what) {
12321275
_interpolation_data.global_xform_curr = get_global_transform();
12331276
}
12341277
}
1278+
#ifdef TOOLS_ENABLED
1279+
if (!local_coords) {
1280+
queue_redraw();
1281+
}
1282+
#endif
12351283
} break;
12361284

12371285
case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: {
@@ -1242,6 +1290,27 @@ void CPUParticles2D::_notification(int p_what) {
12421290
}
12431291
}
12441292

1293+
#ifdef TOOLS_ENABLED
1294+
void CPUParticles2D::_draw_emission_gizmo() {
1295+
Vector2 gizmo_position;
1296+
if (!local_coords) {
1297+
gizmo_position = get_position();
1298+
}
1299+
1300+
switch (emission_shape) {
1301+
case CPUParticles2D::EMISSION_SHAPE_RECTANGLE:
1302+
draw_rect(Rect2(gizmo_position - emission_rect_extents, emission_rect_extents * 2.0), Color(0.8, 0.7, 0.4, 0.4), false);
1303+
break;
1304+
case CPUParticles2D::EMISSION_SHAPE_SPHERE:
1305+
case CPUParticles2D::EMISSION_SHAPE_SPHERE_SURFACE:
1306+
draw_circle(gizmo_position, emission_sphere_radius, Color(0.8, 0.7, 0.4, 0.4), false);
1307+
break;
1308+
default:
1309+
break;
1310+
}
1311+
}
1312+
#endif
1313+
12451314
void CPUParticles2D::convert_from_particles(Node *p_particles) {
12461315
GPUParticles2D *gpu_particles = Object::cast_to<GPUParticles2D>(p_particles);
12471316
ERR_FAIL_NULL_MSG(gpu_particles, "Only GPUParticles2D nodes can be converted to CPUParticles2D.");

scene/2d/cpu_particles_2d.h

+10
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ class CPUParticles2D : public Node2D {
148148

149149
Transform2D inv_emission_transform;
150150

151+
#ifdef TOOLS_ENABLED
152+
bool show_gizmos = false;
153+
#endif
154+
151155
DrawOrder draw_order = DRAW_ORDER_INDEX;
152156

153157
Ref<Texture2D> texture;
@@ -211,6 +215,9 @@ class CPUParticles2D : public Node2D {
211215
protected:
212216
static void _bind_methods();
213217
void _notification(int p_what);
218+
#ifdef TOOLS_ENABLED
219+
void _draw_emission_gizmo();
220+
#endif
214221
void _validate_property(PropertyInfo &p_property) const;
215222

216223
#ifndef DISABLE_DEPRECATED
@@ -257,6 +264,9 @@ class CPUParticles2D : public Node2D {
257264
bool get_use_fixed_seed() const;
258265

259266
void set_seed(uint32_t p_seed);
267+
#ifdef TOOLS_ENABLED
268+
void set_show_gizmos(bool p_show_gizmos);
269+
#endif
260270
uint32_t get_seed() const;
261271

262272
void request_particles_process(real_t p_requested_process_time);

scene/2d/gpu_particles_2d.cpp

+72-3
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,22 @@ void GPUParticles2D::_update_particle_emission_transform() {
141141
}
142142

143143
void GPUParticles2D::set_process_material(const Ref<Material> &p_material) {
144+
if (process_material == p_material) {
145+
return;
146+
}
147+
148+
if (process_material.is_valid() && process_material->is_class("ParticleProcessMaterial")) {
149+
process_material->disconnect("emission_shape_changed", callable_mp((CanvasItem *)this, &GPUParticles2D::queue_redraw));
150+
}
151+
144152
process_material = p_material;
153+
154+
if (process_material.is_valid() && process_material->is_class("ParticleProcessMaterial")) {
155+
process_material->connect("emission_shape_changed", callable_mp((CanvasItem *)this, &GPUParticles2D::queue_redraw));
156+
}
157+
158+
queue_redraw();
159+
145160
Ref<ParticleProcessMaterial> pm = p_material;
146161
if (pm.is_valid() && !pm->get_particle_flag(ParticleProcessMaterial::PARTICLE_FLAG_DISABLE_Z) && pm->get_gravity() == Vector3(0, -9.8, 0)) {
147162
// Likely a new (3D) material, modify it to match 2D space
@@ -195,8 +210,8 @@ void GPUParticles2D::set_interp_to_end(float p_interp) {
195210
}
196211

197212
#ifdef TOOLS_ENABLED
198-
void GPUParticles2D::set_show_visibility_rect(bool p_show_visibility_rect) {
199-
show_visibility_rect = p_show_visibility_rect;
213+
void GPUParticles2D::set_show_gizmos(bool p_show_gizmos) {
214+
show_gizmos = p_show_gizmos;
200215
queue_redraw();
201216
}
202217
#endif
@@ -704,8 +719,9 @@ void GPUParticles2D::_notification(int p_what) {
704719
RS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid);
705720

706721
#ifdef TOOLS_ENABLED
707-
if (show_visibility_rect) {
722+
if (show_gizmos) {
708723
draw_rect(visibility_rect, Color(0, 0.7, 0.9, 0.4), false);
724+
_draw_emission_gizmo();
709725
}
710726
#endif
711727
} break;
@@ -781,6 +797,59 @@ void GPUParticles2D::_notification(int p_what) {
781797
}
782798
}
783799

800+
#ifdef TOOLS_ENABLED
801+
void GPUParticles2D::_draw_emission_gizmo() {
802+
Ref<ParticleProcessMaterial> pm = process_material;
803+
if (pm.is_null()) {
804+
return;
805+
}
806+
draw_set_transform(
807+
Vector2(pm->get_emission_shape_offset().x, pm->get_emission_shape_offset().y),
808+
0.0,
809+
Vector2(pm->get_emission_shape_scale().x, pm->get_emission_shape_scale().y));
810+
811+
switch (pm->get_emission_shape()) {
812+
case ParticleProcessMaterial::EmissionShape::EMISSION_SHAPE_BOX: {
813+
Vector2 extends2d = Vector2(pm->get_emission_box_extents().x, pm->get_emission_box_extents().y);
814+
draw_rect(Rect2(extends2d, extends2d * 2.0), Color(0.8, 0.7, 0.4, 0.4), false);
815+
break;
816+
}
817+
case ParticleProcessMaterial::EmissionShape::EMISSION_SHAPE_SPHERE:
818+
case ParticleProcessMaterial::EmissionShape::EMISSION_SHAPE_SPHERE_SURFACE: {
819+
draw_circle(Vector2(), pm->get_emission_sphere_radius(), Color(0.8, 0.7, 0.4, 0.4), false);
820+
break;
821+
}
822+
case ParticleProcessMaterial::EmissionShape::EMISSION_SHAPE_RING: {
823+
Vector3 ring_axis = pm->get_emission_ring_axis();
824+
if (ring_axis.is_equal_approx(Vector3(0.0, 0.0, 1.0))) {
825+
draw_circle(Vector2(), pm->get_emission_ring_inner_radius(), Color(0.8, 0.7, 0.4, 0.4), false);
826+
draw_circle(Vector2(), pm->get_emission_ring_radius(), Color(0.8, 0.7, 0.4, 0.4), false);
827+
} else {
828+
Vector2 a = Vector2(pm->get_emission_ring_height() / -2.0, pm->get_emission_ring_radius() / -1.0);
829+
Vector2 b = Vector2(-a.x, MIN(a.y + tan((90.0 - pm->get_emission_ring_cone_angle()) * 0.01745329) * pm->get_emission_ring_height(), 0.0));
830+
Vector2 c = Vector2(b.x, -b.y);
831+
Vector2 d = Vector2(a.x, -a.y);
832+
if (ring_axis.is_equal_approx(Vector3(1.0, 0.0, 0.0))) {
833+
Vector<Vector2> pos = { a, b, b, c, c, d, d, a };
834+
draw_multiline(pos, Color(0.8, 0.7, 0.4, 0.4));
835+
} else if (ring_axis.is_equal_approx(Vector3(0.0, 1.0, 0.0))) {
836+
a = Vector2(a.y, a.x);
837+
b = Vector2(b.y, b.x);
838+
c = Vector2(c.y, c.x);
839+
d = Vector2(d.y, d.x);
840+
Vector<Vector2> pos = { a, b, b, c, c, d, d, a };
841+
draw_multiline(pos, Color(0.8, 0.7, 0.4, 0.4));
842+
}
843+
}
844+
break;
845+
}
846+
default: {
847+
break;
848+
}
849+
}
850+
}
851+
#endif
852+
784853
void GPUParticles2D::_bind_methods() {
785854
ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &GPUParticles2D::set_emitting);
786855
ClassDB::bind_method(D_METHOD("set_amount", "amount"), &GPUParticles2D::set_amount);

scene/2d/gpu_particles_2d.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class GPUParticles2D : public Node2D {
6969
uint32_t seed = 0;
7070
bool use_fixed_seed = false;
7171
#ifdef TOOLS_ENABLED
72-
bool show_visibility_rect = false;
72+
bool show_gizmos = false;
7373
#endif
7474
Ref<Material> process_material;
7575

@@ -101,6 +101,9 @@ class GPUParticles2D : public Node2D {
101101
static void _bind_methods();
102102
void _validate_property(PropertyInfo &p_property) const;
103103
void _notification(int p_what);
104+
#ifdef TOOLS_ENABLED
105+
void _draw_emission_gizmo();
106+
#endif
104107
void _update_collision_size();
105108

106109
#ifndef DISABLE_DEPRECATED
@@ -129,7 +132,7 @@ class GPUParticles2D : public Node2D {
129132
void request_particles_process(real_t p_requested_process_time);
130133

131134
#ifdef TOOLS_ENABLED
132-
void set_show_visibility_rect(bool p_show_visibility_rect);
135+
void set_show_gizmos(bool p_show_gizmos);
133136
#endif
134137

135138
bool is_emitting() const;

0 commit comments

Comments
 (0)