diff options
Diffstat (limited to 'scene/3d/cpu_particles.cpp')
-rw-r--r-- | scene/3d/cpu_particles.cpp | 222 |
1 files changed, 125 insertions, 97 deletions
diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp index b07848e02e..cff147ba74 100644 --- a/scene/3d/cpu_particles.cpp +++ b/scene/3d/cpu_particles.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -47,20 +47,8 @@ PoolVector<Face3> CPUParticles::get_faces(uint32_t p_usage_flags) const { void CPUParticles::set_emitting(bool p_emitting) { emitting = p_emitting; - if (!is_processing_internal()) { + if (emitting) set_process_internal(true); - if (is_inside_tree()) { -#ifndef NO_THREADS - update_mutex->lock(); -#endif - VS::get_singleton()->connect("frame_pre_draw", this, "_update_render_thread"); - VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, true); - -#ifndef NO_THREADS - update_mutex->unlock(); -#endif - } - } } void CPUParticles::set_amount(int p_amount) { @@ -224,7 +212,7 @@ String CPUParticles::get_configuration_warning() const { get_param_curve(PARAM_ANIM_SPEED).is_valid() || get_param_curve(PARAM_ANIM_OFFSET).is_valid())) { if (warnings != String()) warnings += "\n"; - warnings += "- " + TTR("CPUParticles animation requires the usage of a SpatialMaterial with \"Billboard Particles\" enabled."); + warnings += "- " + TTR("CPUParticles animation requires the usage of a SpatialMaterial whose Billboard Mode is set to \"Particle Billboard\"."); } return warnings; @@ -314,9 +302,9 @@ void CPUParticles::set_param_curve(Parameter p_param, const Ref<Curve> &p_curve) case PARAM_ANGULAR_VELOCITY: { _adjust_curve_range(p_curve, -360, 360); } break; - /*case PARAM_ORBIT_VELOCITY: { + case PARAM_ORBIT_VELOCITY: { _adjust_curve_range(p_curve, -500, 500); - } break;*/ + } break; case PARAM_LINEAR_ACCEL: { _adjust_curve_range(p_curve, -200, 200); } break; @@ -343,7 +331,8 @@ void CPUParticles::set_param_curve(Parameter p_param, const Ref<Curve> &p_curve) } break; case PARAM_ANIM_OFFSET: { } break; - default: {} + default: { + } } } Ref<Curve> CPUParticles::get_param_curve(Parameter p_param) const { @@ -472,11 +461,10 @@ void CPUParticles::_validate_property(PropertyInfo &property) const { if (property.name == "emission_normals" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { property.usage = 0; } - /* + if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) { property.usage = 0; } - */ } static uint32_t idhash(uint32_t x) { @@ -526,6 +514,8 @@ void CPUParticles::_particles_process(float p_delta) { velocity_xform = emission_xform.basis; } + float system_phase = time / lifetime; + for (int i = 0; i < pcount; i++) { Particle &p = parray[i]; @@ -533,21 +523,26 @@ void CPUParticles::_particles_process(float p_delta) { if (!emitting && !p.active) continue; - float restart_time = (float(i) / float(pcount)) * lifetime; float local_delta = p_delta; + // The phase is a ratio between 0 (birth) and 1 (end of life) for each particle. + // While we use time in tests later on, for randomness we use the phase as done in the + // original shader code, and we later multiply by lifetime to get the time. + float restart_phase = float(i) / float(pcount); + if (randomness_ratio > 0.0) { uint32_t seed = cycle; - if (restart_time >= time) { + if (restart_phase >= system_phase) { seed -= uint32_t(1); } seed *= uint32_t(pcount); seed += uint32_t(i); float random = float(idhash(seed) % uint32_t(65536)) / 65536.0; - restart_time += randomness_ratio * random * 1.0 / float(pcount); + restart_phase += randomness_ratio * random * 1.0 / float(pcount); } - restart_time *= (1.0 - explosiveness_ratio); + restart_phase *= (1.0 - explosiveness_ratio); + float restart_time = restart_phase * lifetime; bool restart = false; if (time > prev_time) { @@ -556,7 +551,7 @@ void CPUParticles::_particles_process(float p_delta) { if (restart_time >= prev_time && restart_time < time) { restart = true; if (fractional_delta) { - local_delta = (time - restart_time) * lifetime; + local_delta = time - restart_time; } } @@ -564,13 +559,13 @@ void CPUParticles::_particles_process(float p_delta) { if (restart_time >= prev_time) { restart = true; if (fractional_delta) { - local_delta = (1.0 - restart_time + time) * lifetime; + local_delta = lifetime - restart_time + time; } } else if (restart_time < time) { restart = true; if (fractional_delta) { - local_delta = (time - restart_time) * lifetime; + local_delta = time - restart_time; } } } @@ -605,19 +600,14 @@ void CPUParticles::_particles_process(float p_delta) { p.hue_rot_rand = Math::randf(); p.anim_offset_rand = Math::randf(); - float angle1_rad; - float angle2_rad; - if (flags[FLAG_DISABLE_Z]) { - - angle1_rad = (Math::randf() * 2.0 - 1.0) * Math_PI * spread / 180.0; + float angle1_rad = (Math::randf() * 2.0 - 1.0) * Math_PI * spread / 180.0; Vector3 rot = Vector3(Math::cos(angle1_rad), Math::sin(angle1_rad), 0.0); p.velocity = rot * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp(1.0f, float(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]); - } else { //initiate velocity spread in 3D - angle1_rad = (Math::randf() * 2.0 - 1.0) * Math_PI * spread / 180.0; - angle2_rad = (Math::randf() * 2.0 - 1.0) * (1.0 - flatness) * Math_PI * spread / 180.0; + float angle1_rad = (Math::randf() * 2.0 - 1.0) * Math_PI * spread / 180.0; + float angle2_rad = (Math::randf() * 2.0 - 1.0) * (1.0 - flatness) * Math_PI * spread / 180.0; Vector3 direction_xz = Vector3(Math::sin(angle1_rad), 0, Math::cos(angle1_rad)); Vector3 direction_yz = Vector3(0, Math::sin(angle2_rad), Math::cos(angle2_rad)); @@ -707,16 +697,14 @@ void CPUParticles::_particles_process(float p_delta) { if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(p.custom[1]); } - /* - float tex_orbit_velocity = 0.0; + float tex_orbit_velocity = 0.0; if (flags[FLAG_DISABLE_Z]) { - - if (curve_parameters[PARAM_INITIAL_ORBIT_VELOCITY].is_valid()) { - tex_orbit_velocity = curve_parameters[PARAM_INITIAL_ORBIT_VELOCITY]->interpolate(p.custom[1]); + if (curve_parameters[PARAM_ORBIT_VELOCITY].is_valid()) { + tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->interpolate(p.custom[1]); } } -*/ + float tex_angular_velocity = 0.0; if (curve_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) { tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(p.custom[1]); @@ -770,8 +758,9 @@ void CPUParticles::_particles_process(float p_delta) { //apply tangential acceleration; if (flags[FLAG_DISABLE_Z]) { - Vector3 yx = Vector3(diff.y, 0, diff.x); - force += yx.length() > 0.0 ? (yx * Vector3(-1.0, 0, 1.0)) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3(); + Vector2 yx = Vector2(diff.y, diff.x); + Vector2 yx2 = (yx * Vector2(-1.0, 1.0)).normalized(); + force += yx.length() > 0.0 ? Vector3(yx2.x, yx2.y, 0.0) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3(); } else { Vector3 crossDiff = diff.normalized().cross(gravity.normalized()); @@ -780,18 +769,18 @@ void CPUParticles::_particles_process(float p_delta) { //apply attractor forces p.velocity += force * local_delta; //orbit velocity -#if 0 if (flags[FLAG_DISABLE_Z]) { - - float orbit_amount = (orbit_velocity + tex_orbit_velocity) * mix(1.0, rand_from_seed(alt_seed), orbit_velocity_random); + float orbit_amount = (parameters[PARAM_ORBIT_VELOCITY] + tex_orbit_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ORBIT_VELOCITY]); if (orbit_amount != 0.0) { - float ang = orbit_amount * DELTA * pi * 2.0; - mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang))); - TRANSFORM[3].xy -= diff.xy; - TRANSFORM[3].xy += rot * diff.xy; + float ang = orbit_amount * local_delta * Math_PI * 2.0; + // Not sure why the ParticlesMaterial code uses a clockwise rotation matrix, + // but we use -ang here to reproduce its behavior. + Transform2D rot = Transform2D(-ang, Vector2()); + Vector2 rotv = rot.basis_xform(Vector2(diff.x, diff.y)); + p.transform.origin -= Vector3(diff.x, diff.y, 0); + p.transform.origin += Vector3(rotv.x, rotv.y, 0); } } -#endif if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { p.velocity = p.velocity.normalized() * tex_linear_velocity; } @@ -926,11 +915,6 @@ void CPUParticles::_update_particle_data_buffer() { PoolVector<Particle>::Read r = particles.read(); float *ptr = w.ptr(); - Transform un_transform; - if (!local_coords) { - un_transform = get_global_transform().affine_inverse(); - } - if (draw_order != DRAW_ORDER_INDEX) { ow = particle_order.write(); order = ow.ptr(); @@ -948,7 +932,12 @@ void CPUParticles::_update_particle_data_buffer() { Vector3 dir = c->get_global_transform().basis.get_axis(2); //far away to close if (local_coords) { - dir = un_transform.basis.xform(dir).normalized(); + + // will look different from Particles in editor as this is based on the camera in the scenetree + // and not the editor camera + dir = inv_emission_transform.xform(dir).normalized(); + } else { + dir = dir.normalized(); } SortArray<int, SortAxis> sorter; @@ -966,7 +955,7 @@ void CPUParticles::_update_particle_data_buffer() { Transform t = r[idx].transform; if (!local_coords) { - t = un_transform * t; + t = inv_emission_transform * t; } if (r[idx].active) { @@ -1009,6 +998,27 @@ void CPUParticles::_update_particle_data_buffer() { #endif } +void CPUParticles::_set_redraw(bool p_redraw) { + if (redraw == p_redraw) + return; + redraw = p_redraw; +#ifndef NO_THREADS + update_mutex->lock(); +#endif + if (redraw) { + VS::get_singleton()->connect("frame_pre_draw", this, "_update_render_thread"); + VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, true); + VS::get_singleton()->multimesh_set_visible_instances(multimesh, -1); + } else { + VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread"); + VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, false); + VS::get_singleton()->multimesh_set_visible_instances(multimesh, 0); + } +#ifndef NO_THREADS + update_mutex->unlock(); +#endif +} + void CPUParticles::_update_render_thread() { #ifndef NO_THREADS @@ -1027,31 +1037,11 @@ void CPUParticles::_update_render_thread() { void CPUParticles::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { - if (is_processing_internal()) { - -#ifndef NO_THREADS - update_mutex->lock(); -#endif - VS::get_singleton()->connect("frame_pre_draw", this, "_update_render_thread"); - VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, true); -#ifndef NO_THREADS - update_mutex->unlock(); -#endif - } + set_process_internal(emitting); } if (p_what == NOTIFICATION_EXIT_TREE) { - if (is_processing_internal()) { - -#ifndef NO_THREADS - update_mutex->lock(); -#endif - VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread"); - VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, false); -#ifndef NO_THREADS - update_mutex->unlock(); -#endif - } + _set_redraw(false); } if (p_what == NOTIFICATION_PAUSED || p_what == NOTIFICATION_UNPAUSED) { @@ -1059,26 +1049,20 @@ void CPUParticles::_notification(int p_what) { if (p_what == NOTIFICATION_INTERNAL_PROCESS) { - if (particles.size() == 0 || !is_visible_in_tree()) + if (particles.size() == 0 || !is_visible_in_tree()) { + _set_redraw(false); return; + } float delta = get_process_delta_time(); if (emitting) { - inactive_time = 0; } else { inactive_time += delta; if (inactive_time > lifetime * 1.2) { set_process_internal(false); -#ifndef NO_THREADS - update_mutex->lock(); -#endif - VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread"); - VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, false); + _set_redraw(false); -#ifndef NO_THREADS - update_mutex->unlock(); -#endif //reset variables time = 0; inactive_time = 0; @@ -1087,6 +1071,7 @@ void CPUParticles::_notification(int p_what) { return; } } + _set_redraw(true); bool processed = false; @@ -1136,6 +1121,46 @@ void CPUParticles::_notification(int p_what) { _update_particle_data_buffer(); } } + + if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { + + inv_emission_transform = get_global_transform().affine_inverse(); + + if (!local_coords) { + + int pc = particles.size(); + + PoolVector<float>::Write w = particle_data.write(); + PoolVector<Particle>::Read r = particles.read(); + float *ptr = w.ptr(); + + for (int i = 0; i < pc; i++) { + + Transform t = inv_emission_transform * r[i].transform; + + if (r[i].active) { + ptr[0] = t.basis.elements[0][0]; + ptr[1] = t.basis.elements[0][1]; + ptr[2] = t.basis.elements[0][2]; + ptr[3] = t.origin.x; + ptr[4] = t.basis.elements[1][0]; + ptr[5] = t.basis.elements[1][1]; + ptr[6] = t.basis.elements[1][2]; + ptr[7] = t.origin.y; + ptr[8] = t.basis.elements[2][0]; + ptr[9] = t.basis.elements[2][1]; + ptr[10] = t.basis.elements[2][2]; + ptr[11] = t.origin.z; + } else { + zeromem(ptr, sizeof(float) * 12); + } + + ptr += 17; + } + + can_update = true; + } + } } void CPUParticles::convert_from_particles(Node *p_particles) { @@ -1191,7 +1216,7 @@ void CPUParticles::convert_from_particles(Node *p_particles) { CONVERT_PARAM(PARAM_INITIAL_LINEAR_VELOCITY); CONVERT_PARAM(PARAM_ANGULAR_VELOCITY); - // CONVERT_PARAM(PARAM_ORBIT_VELOCITY); + CONVERT_PARAM(PARAM_ORBIT_VELOCITY); CONVERT_PARAM(PARAM_LINEAR_ACCEL); CONVERT_PARAM(PARAM_RADIAL_ACCEL); CONVERT_PARAM(PARAM_TANGENTIAL_ACCEL); @@ -1243,7 +1268,7 @@ void CPUParticles::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount"); ADD_GROUP("Time", ""); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_EXP_RANGE, "0.01,600.0,0.01"), "set_lifetime", "get_lifetime"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_EXP_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"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_EXP_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale"); @@ -1334,12 +1359,10 @@ void CPUParticles::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGULAR_VELOCITY); - /* ADD_GROUP("Orbit Velocity", "orbit_"); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ORBIT_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ORBIT_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ORBIT_VELOCITY); -*/ ADD_GROUP("Linear Accel", "linear_"); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_LINEAR_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_LINEAR_ACCEL); @@ -1382,7 +1405,7 @@ void CPUParticles::_bind_methods() { BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY); BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY); - //BIND_ENUM_CONSTANT(PARAM_ORBIT_VELOCITY); + BIND_ENUM_CONSTANT(PARAM_ORBIT_VELOCITY); BIND_ENUM_CONSTANT(PARAM_LINEAR_ACCEL); BIND_ENUM_CONSTANT(PARAM_RADIAL_ACCEL); BIND_ENUM_CONSTANT(PARAM_TANGENTIAL_ACCEL); @@ -1396,6 +1419,7 @@ void CPUParticles::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY); BIND_ENUM_CONSTANT(FLAG_ROTATE_Y); + BIND_ENUM_CONSTANT(FLAG_DISABLE_Z); BIND_ENUM_CONSTANT(FLAG_MAX); BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT); @@ -1411,8 +1435,12 @@ CPUParticles::CPUParticles() { inactive_time = 0; frame_remainder = 0; cycle = 0; + redraw = false; + + set_notify_transform(true); multimesh = VisualServer::get_singleton()->multimesh_create(); + VisualServer::get_singleton()->multimesh_set_visible_instances(multimesh, 0); set_base(multimesh); set_emitting(true); @@ -1432,7 +1460,7 @@ CPUParticles::CPUParticles() { set_spread(45); set_flatness(0); set_param(PARAM_INITIAL_LINEAR_VELOCITY, 1); - //set_param(PARAM_ORBIT_VELOCITY, 0); + set_param(PARAM_ORBIT_VELOCITY, 0); set_param(PARAM_LINEAR_ACCEL, 0); set_param(PARAM_RADIAL_ACCEL, 0); set_param(PARAM_TANGENTIAL_ACCEL, 0); |