summaryrefslogtreecommitdiff
path: root/scene/2d/gpu_particles_2d.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/2d/gpu_particles_2d.cpp')
-rw-r--r--scene/2d/gpu_particles_2d.cpp308
1 files changed, 183 insertions, 125 deletions
diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp
index 8c8f794298..8b0840e7c8 100644
--- a/scene/2d/gpu_particles_2d.cpp
+++ b/scene/2d/gpu_particles_2d.cpp
@@ -335,153 +335,199 @@ Ref<Texture2D> GPUParticles2D::get_texture() const {
void GPUParticles2D::_validate_property(PropertyInfo &property) const {
}
-void GPUParticles2D::restart() {
- RS::get_singleton()->particles_restart(particles);
- RS::get_singleton()->particles_set_emitting(particles, true);
+void GPUParticles2D::emit_particle(const Transform2D &p_transform2d, const Vector2 &p_velocity2d, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) {
+ Transform3D transform;
+ transform.basis.set_axis(0, Vector3(p_transform2d.get_axis(0).x, p_transform2d.get_axis(0).y, 0));
+ transform.basis.set_axis(1, Vector3(p_transform2d.get_axis(1).x, p_transform2d.get_axis(1).y, 0));
+ transform.set_origin(Vector3(p_transform2d.get_origin().x, p_transform2d.get_origin().y, 0));
+ Vector3 velocity = Vector3(p_velocity2d.x, p_velocity2d.y, 0);
+
+ RS::get_singleton()->particles_emit(particles, transform, velocity, p_color, p_custom, p_emit_flags);
+}
+
+void GPUParticles2D::_attach_sub_emitter() {
+ Node *n = get_node_or_null(sub_emitter);
+ if (n) {
+ GPUParticles2D *sen = Object::cast_to<GPUParticles2D>(n);
+ if (sen && sen != this) {
+ RS::get_singleton()->particles_set_subemitter(particles, sen->particles);
+ }
+ }
}
-void GPUParticles2D::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- RID texture_rid;
- Size2 size;
- if (texture.is_valid()) {
- texture_rid = texture->get_rid();
- size = texture->get_size();
- } else {
- size = Size2(1, 1);
- }
+void GPUParticles2D::set_sub_emitter(const NodePath &p_path) {
+ if (is_inside_tree()) {
+ RS::get_singleton()->particles_set_subemitter(particles, RID());
+ }
- if (trail_enabled) {
- RS::get_singleton()->mesh_clear(mesh);
- PackedVector2Array points;
- PackedVector2Array uvs;
- PackedInt32Array bone_indices;
- PackedFloat32Array bone_weights;
- PackedInt32Array indices;
+ sub_emitter = p_path;
- int total_segments = trail_sections * trail_section_subdivisions;
- real_t depth = size.height * trail_sections;
+ if (is_inside_tree() && sub_emitter != NodePath()) {
+ _attach_sub_emitter();
+ }
+}
- for (int j = 0; j <= total_segments; j++) {
- real_t v = j;
- v /= total_segments;
+NodePath GPUParticles2D::get_sub_emitter() const {
+ return sub_emitter;
+}
- real_t y = depth * v;
- y = (depth * 0.5) - y;
+void GPUParticles2D::restart() {
+ RS::get_singleton()->particles_restart(particles);
+ RS::get_singleton()->particles_set_emitting(particles, true);
+}
- int bone = j / trail_section_subdivisions;
- real_t blend = 1.0 - real_t(j % trail_section_subdivisions) / real_t(trail_section_subdivisions);
+void GPUParticles2D::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ RID texture_rid;
+ Size2 size;
+ if (texture.is_valid()) {
+ texture_rid = texture->get_rid();
+ size = texture->get_size();
+ } else {
+ size = Size2(1, 1);
+ }
- real_t s = size.width;
+ if (trail_enabled) {
+ RS::get_singleton()->mesh_clear(mesh);
+ PackedVector2Array points;
+ PackedVector2Array uvs;
+ PackedInt32Array bone_indices;
+ PackedFloat32Array bone_weights;
+ PackedInt32Array indices;
+
+ int total_segments = trail_sections * trail_section_subdivisions;
+ real_t depth = size.height * trail_sections;
+
+ for (int j = 0; j <= total_segments; j++) {
+ real_t v = j;
+ v /= total_segments;
+
+ real_t y = depth * v;
+ y = (depth * 0.5) - y;
+
+ int bone = j / trail_section_subdivisions;
+ real_t blend = 1.0 - real_t(j % trail_section_subdivisions) / real_t(trail_section_subdivisions);
+
+ real_t s = size.width;
+
+ points.push_back(Vector2(-s * 0.5, 0));
+ points.push_back(Vector2(+s * 0.5, 0));
+
+ uvs.push_back(Vector2(0, v));
+ uvs.push_back(Vector2(1, v));
+
+ for (int i = 0; i < 2; i++) {
+ bone_indices.push_back(bone);
+ bone_indices.push_back(MIN(trail_sections, bone + 1));
+ bone_indices.push_back(0);
+ bone_indices.push_back(0);
+
+ bone_weights.push_back(blend);
+ bone_weights.push_back(1.0 - blend);
+ bone_weights.push_back(0);
+ bone_weights.push_back(0);
+ }
+
+ if (j > 0) {
+ int base = j * 2 - 2;
+ indices.push_back(base + 0);
+ indices.push_back(base + 1);
+ indices.push_back(base + 2);
+
+ indices.push_back(base + 1);
+ indices.push_back(base + 3);
+ indices.push_back(base + 2);
+ }
+ }
- points.push_back(Vector2(-s * 0.5, 0));
- points.push_back(Vector2(+s * 0.5, 0));
+ Array arr;
+ arr.resize(RS::ARRAY_MAX);
+ arr[RS::ARRAY_VERTEX] = points;
+ arr[RS::ARRAY_TEX_UV] = uvs;
+ arr[RS::ARRAY_BONES] = bone_indices;
+ arr[RS::ARRAY_WEIGHTS] = bone_weights;
+ arr[RS::ARRAY_INDEX] = indices;
+
+ RS::get_singleton()->mesh_add_surface_from_arrays(mesh, RS::PRIMITIVE_TRIANGLES, arr, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES);
+
+ Vector<Transform3D> xforms;
+ for (int i = 0; i <= trail_sections; i++) {
+ Transform3D xform;
+ /*
+ xform.origin.y = depth / 2.0 - size.height * real_t(i);
+ xform.origin.y = -xform.origin.y; //bind is an inverse transform, so negate y */
+ xforms.push_back(xform);
+ }
- uvs.push_back(Vector2(0, v));
- uvs.push_back(Vector2(1, v));
+ RS::get_singleton()->particles_set_trail_bind_poses(particles, xforms);
- for (int i = 0; i < 2; i++) {
- bone_indices.push_back(bone);
- bone_indices.push_back(MIN(trail_sections, bone + 1));
- bone_indices.push_back(0);
- bone_indices.push_back(0);
+ } else {
+ RS::get_singleton()->mesh_clear(mesh);
- bone_weights.push_back(blend);
- bone_weights.push_back(1.0 - blend);
- bone_weights.push_back(0);
- bone_weights.push_back(0);
- }
+ Vector<Vector2> points = {
+ Vector2(-size.x / 2.0, -size.y / 2.0),
+ Vector2(size.x / 2.0, -size.y / 2.0),
+ Vector2(size.x / 2.0, size.y / 2.0),
+ Vector2(-size.x / 2.0, size.y / 2.0)
+ };
- if (j > 0) {
- int base = j * 2 - 2;
- indices.push_back(base + 0);
- indices.push_back(base + 1);
- indices.push_back(base + 2);
+ Vector<Vector2> uvs = {
+ Vector2(0, 0),
+ Vector2(1, 0),
+ Vector2(1, 1),
+ Vector2(0, 1)
+ };
- indices.push_back(base + 1);
- indices.push_back(base + 3);
- indices.push_back(base + 2);
- }
- }
+ Vector<int> indices = { 0, 1, 2, 0, 2, 3 };
- Array arr;
- arr.resize(RS::ARRAY_MAX);
- arr[RS::ARRAY_VERTEX] = points;
- arr[RS::ARRAY_TEX_UV] = uvs;
- arr[RS::ARRAY_BONES] = bone_indices;
- arr[RS::ARRAY_WEIGHTS] = bone_weights;
- arr[RS::ARRAY_INDEX] = indices;
-
- RS::get_singleton()->mesh_add_surface_from_arrays(mesh, RS::PRIMITIVE_TRIANGLES, arr, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES);
-
- Vector<Transform3D> xforms;
- for (int i = 0; i <= trail_sections; i++) {
- Transform3D xform;
- /*
- xform.origin.y = depth / 2.0 - size.height * real_t(i);
- xform.origin.y = -xform.origin.y; //bind is an inverse transform, so negate y */
- xforms.push_back(xform);
- }
+ Array arr;
+ arr.resize(RS::ARRAY_MAX);
+ arr[RS::ARRAY_VERTEX] = points;
+ arr[RS::ARRAY_TEX_UV] = uvs;
+ arr[RS::ARRAY_INDEX] = indices;
- RS::get_singleton()->particles_set_trail_bind_poses(particles, xforms);
-
- } else {
- RS::get_singleton()->mesh_clear(mesh);
- Vector<Vector2> points;
- points.resize(4);
- points.write[0] = Vector2(-size.x / 2.0, -size.y / 2.0);
- points.write[1] = Vector2(size.x / 2.0, -size.y / 2.0);
- points.write[2] = Vector2(size.x / 2.0, size.y / 2.0);
- points.write[3] = Vector2(-size.x / 2.0, size.y / 2.0);
- Vector<Vector2> uvs;
- uvs.resize(4);
- uvs.write[0] = Vector2(0, 0);
- uvs.write[1] = Vector2(1, 0);
- uvs.write[2] = Vector2(1, 1);
- uvs.write[3] = Vector2(0, 1);
- Vector<int> indices;
- indices.resize(6);
- indices.write[0] = 0;
- indices.write[1] = 1;
- indices.write[2] = 2;
- indices.write[3] = 0;
- indices.write[4] = 2;
- indices.write[5] = 3;
- Array arr;
- arr.resize(RS::ARRAY_MAX);
- arr[RS::ARRAY_VERTEX] = points;
- arr[RS::ARRAY_TEX_UV] = uvs;
- arr[RS::ARRAY_INDEX] = indices;
-
- RS::get_singleton()->mesh_add_surface_from_arrays(mesh, RS::PRIMITIVE_TRIANGLES, arr, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES);
- RS::get_singleton()->particles_set_trail_bind_poses(particles, Vector<Transform3D>());
- }
- RS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid);
+ RS::get_singleton()->mesh_add_surface_from_arrays(mesh, RS::PRIMITIVE_TRIANGLES, arr, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES);
+ RS::get_singleton()->particles_set_trail_bind_poses(particles, Vector<Transform3D>());
+ }
+ RS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid);
#ifdef TOOLS_ENABLED
- if (show_visibility_rect) {
- draw_rect(visibility_rect, Color(0, 0.7, 0.9, 0.4), false);
- }
+ if (show_visibility_rect) {
+ draw_rect(visibility_rect, Color(0, 0.7, 0.9, 0.4), false);
+ }
#endif
- }
+ } break;
- if (p_what == NOTIFICATION_PAUSED || p_what == NOTIFICATION_UNPAUSED) {
- if (can_process()) {
- RS::get_singleton()->particles_set_speed_scale(particles, speed_scale);
- } else {
- RS::get_singleton()->particles_set_speed_scale(particles, 0);
- }
- }
+ case NOTIFICATION_ENTER_TREE: {
+ if (sub_emitter != NodePath()) {
+ _attach_sub_emitter();
+ }
+ } break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ RS::get_singleton()->particles_set_subemitter(particles, RID());
+ } break;
+
+ case NOTIFICATION_PAUSED:
+ case NOTIFICATION_UNPAUSED: {
+ if (can_process()) {
+ RS::get_singleton()->particles_set_speed_scale(particles, speed_scale);
+ } else {
+ RS::get_singleton()->particles_set_speed_scale(particles, 0);
+ }
+ } break;
- if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
- _update_particle_emission_transform();
- }
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+ _update_particle_emission_transform();
+ } break;
- if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
- if (one_shot && !is_emitting()) {
- notify_property_list_changed();
- set_process_internal(false);
- }
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ if (one_shot && !is_emitting()) {
+ notify_property_list_changed();
+ set_process_internal(false);
+ }
+ } break;
}
}
@@ -526,6 +572,11 @@ void GPUParticles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("restart"), &GPUParticles2D::restart);
+ ClassDB::bind_method(D_METHOD("set_sub_emitter", "path"), &GPUParticles2D::set_sub_emitter);
+ ClassDB::bind_method(D_METHOD("get_sub_emitter"), &GPUParticles2D::get_sub_emitter);
+
+ ClassDB::bind_method(D_METHOD("emit_particle", "xform", "velocity", "color", "custom", "flags"), &GPUParticles2D::emit_particle);
+
ClassDB::bind_method(D_METHOD("set_trail_enabled", "enabled"), &GPUParticles2D::set_trail_enabled);
ClassDB::bind_method(D_METHOD("set_trail_length", "secs"), &GPUParticles2D::set_trail_length);
@@ -541,6 +592,7 @@ void GPUParticles2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
ADD_PROPERTY_DEFAULT("emitting", true); // Workaround for doctool in headless mode, as dummy rasterizer always returns false.
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "sub_emitter", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GPUParticles2D"), "set_sub_emitter", "get_sub_emitter");
ADD_GROUP("Time", "");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
@@ -569,6 +621,12 @@ void GPUParticles2D::_bind_methods() {
BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX);
BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME);
BIND_ENUM_CONSTANT(DRAW_ORDER_REVERSE_LIFETIME);
+
+ BIND_ENUM_CONSTANT(EMIT_FLAG_POSITION);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_ROTATION_SCALE);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_VELOCITY);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_COLOR);
+ BIND_ENUM_CONSTANT(EMIT_FLAG_CUSTOM);
}
GPUParticles2D::GPUParticles2D() {